111 lines
2.7 KiB
TypeScript
111 lines
2.7 KiB
TypeScript
import { useMemo, useState } from "react";
|
|
import { View, TouchableOpacity } from "react-native";
|
|
import { Menu, Text, IconButton, Divider } from "react-native-paper";
|
|
|
|
import { MultiSelectGlyph } from "./filters/MultiSelectGlyph";
|
|
|
|
export type FilterOption = {
|
|
value: string;
|
|
label: string;
|
|
icon?: React.ReactNode;
|
|
};
|
|
|
|
interface FilterMultiSelectProps {
|
|
label: string;
|
|
options: FilterOption[];
|
|
selected: string[];
|
|
onChange: (values: string[]) => void;
|
|
iconName?: string;
|
|
className?: string;
|
|
menuWidth?: number;
|
|
showClearAll?: boolean;
|
|
}
|
|
|
|
export const FilterMultiSelect = ({
|
|
label,
|
|
options,
|
|
selected,
|
|
onChange,
|
|
iconName = undefined,
|
|
className,
|
|
menuWidth,
|
|
showClearAll = true,
|
|
}: FilterMultiSelectProps) => {
|
|
const [visible, setVisible] = useState(false);
|
|
|
|
const selectedSet = useMemo(() => new Set(selected), [selected]);
|
|
|
|
const toggle = (value: string) => {
|
|
const next = new Set(selectedSet);
|
|
if (next.has(value)) {
|
|
next.delete(value);
|
|
} else {
|
|
next.add(value);
|
|
}
|
|
onChange(Array.from(next));
|
|
};
|
|
|
|
const anchor = (
|
|
<TouchableOpacity
|
|
onPress={() => setVisible(true)}
|
|
className={`flex-row items-center px-2 h-8 rounded ${className || ""}`}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Text style={{ fontSize: 12, color: "#000" }}>{label}</Text>
|
|
{iconName ? (
|
|
<IconButton
|
|
icon={iconName}
|
|
size={18}
|
|
onPress={() => setVisible(true)}
|
|
/>
|
|
) : (
|
|
<MultiSelectGlyph />
|
|
)}
|
|
</TouchableOpacity>
|
|
);
|
|
|
|
return (
|
|
<Menu
|
|
visible={visible}
|
|
onDismiss={() => setVisible(false)}
|
|
anchor={anchor}
|
|
contentStyle={menuWidth ? { width: menuWidth } : undefined}
|
|
>
|
|
{options.map((opt) => {
|
|
const isSelected = selectedSet.has(opt.value);
|
|
return (
|
|
<Menu.Item
|
|
key={opt.value}
|
|
onPress={() => toggle(opt.value)}
|
|
leadingIcon={
|
|
isSelected ? "checkbox-marked" : "checkbox-blank-outline"
|
|
}
|
|
title={
|
|
<View className="flex-row items-center">
|
|
{opt.icon ? <View className="mr-2">{opt.icon}</View> : null}
|
|
<Text>{opt.label}</Text>
|
|
</View>
|
|
}
|
|
/>
|
|
);
|
|
})}
|
|
{showClearAll && (
|
|
<>
|
|
<Divider />
|
|
<Menu.Item
|
|
onPress={() => onChange([])}
|
|
leadingIcon="close-circle-outline"
|
|
disabled={selectedSet.size === 0}
|
|
title="Clear all"
|
|
/>
|
|
</>
|
|
)}
|
|
<Menu.Item
|
|
onPress={() => setVisible(false)}
|
|
leadingIcon="check"
|
|
title="Done"
|
|
/>
|
|
</Menu>
|
|
);
|
|
};
|