111 lines
2.9 KiB
TypeScript
Executable File
111 lines
2.9 KiB
TypeScript
Executable File
"use client";
|
|
|
|
import { Button } from "@/shared/components/ui/button"
|
|
import { Checkbox } from "@/shared/components/ui/checkbox"
|
|
import {
|
|
Popover,
|
|
PopoverContent,
|
|
PopoverTrigger,
|
|
} from "@/shared/components/ui/popover"
|
|
import { useRef, useState } from "react";
|
|
|
|
interface MultiSelectProps {
|
|
name: string;
|
|
options?: any[];
|
|
value?: any[];
|
|
onChange: (e: { target: { name: string; value: string[] } }) => void;
|
|
placeholder?: string;
|
|
}
|
|
|
|
export default function FormMultiSelect({
|
|
name,
|
|
options = [],
|
|
value = [],
|
|
onChange,
|
|
placeholder = "Pilih data",
|
|
}: MultiSelectProps) {
|
|
const toggle = (val:string) => {
|
|
let newValue:string[];
|
|
|
|
if (value.includes(val)) {
|
|
newValue = value.filter((v) => v !== val)
|
|
} else {
|
|
newValue = [...value, val]
|
|
}
|
|
|
|
if (onChange) {
|
|
onChange({
|
|
target: {
|
|
name,
|
|
value: newValue,
|
|
},
|
|
})
|
|
}
|
|
}
|
|
|
|
const selectedLabels = options
|
|
.filter((o) => value.includes(o.value))
|
|
.map((o) => o.label)
|
|
.join(", ")
|
|
|
|
|
|
const scrollRef = useRef<HTMLDivElement | null>(null);
|
|
const [isAtBottom, setIsAtBottom] = useState(false);
|
|
const handleScroll = () => {
|
|
console.log('gtw');
|
|
|
|
const el = scrollRef.current;
|
|
if (!el) return;
|
|
|
|
const atBottom =
|
|
el.scrollTop + el.clientHeight >= el.scrollHeight - 2;
|
|
|
|
setIsAtBottom(atBottom);
|
|
};
|
|
|
|
|
|
return (
|
|
<Popover>
|
|
<PopoverTrigger asChild>
|
|
<Button
|
|
variant="outline"
|
|
className="w-full justify-start text-left font-normal border-gray-300 rounded-md px-2 shadow-none"
|
|
>
|
|
<div className="flex-1 overflow-x-auto whitespace-nowrap scrollbar-hide">
|
|
{value.length > 0 ? selectedLabels : placeholder}
|
|
</div>
|
|
</Button>
|
|
</PopoverTrigger>
|
|
|
|
<PopoverContent
|
|
ref={scrollRef}
|
|
onScroll={handleScroll}
|
|
side="bottom"
|
|
align="start"
|
|
sideOffset={4}
|
|
className="w-[var(--radix-popover-trigger-width)] max-h-60 overflow-y-auto z-50"
|
|
>
|
|
<div
|
|
className="space-y-2"
|
|
>
|
|
{options.map((opt) => (
|
|
<label
|
|
key={opt.value}
|
|
className="flex items-center space-x-2"
|
|
>
|
|
<Checkbox
|
|
checked={value.includes(opt.value)}
|
|
onCheckedChange={() => toggle(opt.value)}
|
|
/>
|
|
<span>{opt.label}</span>
|
|
</label>
|
|
))}
|
|
{!isAtBottom && (
|
|
<div className="arrow-symbol pt-0 pb-2 bg-white w-full text-center fixed bottom-0">⌄</div>
|
|
)}
|
|
</div>
|
|
</PopoverContent>
|
|
</Popover>
|
|
)
|
|
}
|