From 64fd9a5dcdc4995611a74503157482d2524f7589 Mon Sep 17 00:00:00 2001 From: Bram Suurd <78373894+BramSuurdje@users.noreply.github.com> Date: Wed, 13 Nov 2024 19:04:39 +0100 Subject: [PATCH] Remove MultiSelect component --- frontend/src/components/ui/multi-select.tsx | 387 -------------------- 1 file changed, 387 deletions(-) delete mode 100644 frontend/src/components/ui/multi-select.tsx diff --git a/frontend/src/components/ui/multi-select.tsx b/frontend/src/components/ui/multi-select.tsx deleted file mode 100644 index d554f7d1..00000000 --- a/frontend/src/components/ui/multi-select.tsx +++ /dev/null @@ -1,387 +0,0 @@ -"use client"; - -import { Badge } from "@/components/ui/badge"; -import { - Command, - CommandEmpty, - CommandItem, - CommandList, -} from "@/components/ui/command"; -import { cn } from "@/lib/utils"; -import { Command as CommandPrimitive } from "cmdk"; -import { Check, X as RemoveIcon } from "lucide-react"; -import React, { - KeyboardEvent, - createContext, - forwardRef, - useCallback, - useContext, - useState, -} from "react"; - -interface MultiSelectorProps - extends React.ComponentPropsWithoutRef { - values: string[]; - onValuesChange: (value: string[]) => void; - loop?: boolean; -} - -interface MultiSelectContextProps { - value: string[]; - onValueChange: (value: any) => void; - open: boolean; - setOpen: (value: boolean) => void; - inputValue: string; - setInputValue: React.Dispatch>; - activeIndex: number; - setActiveIndex: React.Dispatch>; - ref: React.RefObject; - handleSelect: (e: React.SyntheticEvent) => void; -} - -const MultiSelectContext = createContext(null); - -const useMultiSelect = () => { - const context = useContext(MultiSelectContext); - if (!context) { - throw new Error("useMultiSelect must be used within MultiSelectProvider"); - } - return context; -}; - -/** - * MultiSelect Docs: {@link: https://shadcn-extension.vercel.app/docs/multi-select} - */ - -// TODO : expose the visibility of the popup - -const MultiSelector = ({ - values: value, - onValuesChange: onValueChange, - loop = false, - className, - children, - dir, - ...props -}: MultiSelectorProps) => { - const [inputValue, setInputValue] = useState(""); - const [open, setOpen] = useState(false); - const [activeIndex, setActiveIndex] = useState(-1); - const inputRef = React.useRef(null); - const [isValueSelected, setIsValueSelected] = React.useState(false); - const [selectedValue, setSelectedValue] = React.useState(""); - - const onValueChangeHandler = useCallback( - (val: string) => { - if (value.includes(val)) { - onValueChange(value.filter((item) => item !== val)); - } else { - onValueChange([...value, val]); - } - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [value], - ); - - const handleSelect = React.useCallback( - (e: React.SyntheticEvent) => { - e.preventDefault(); - const target = e.currentTarget; - const selection = target.value.substring( - target.selectionStart ?? 0, - target.selectionEnd ?? 0, - ); - - setSelectedValue(selection); - setIsValueSelected(selection === inputValue); - }, - [inputValue], - ); - - const handleKeyDown = useCallback( - (e: KeyboardEvent) => { - e.stopPropagation(); - const target = inputRef.current; - - if (!target) return; - - const moveNext = () => { - const nextIndex = activeIndex + 1; - setActiveIndex( - nextIndex > value.length - 1 ? (loop ? 0 : -1) : nextIndex, - ); - }; - - const movePrev = () => { - const prevIndex = activeIndex - 1; - setActiveIndex(prevIndex < 0 ? value.length - 1 : prevIndex); - }; - - const moveCurrent = () => { - const newIndex = - activeIndex - 1 <= 0 - ? value.length - 1 === 0 - ? -1 - : 0 - : activeIndex - 1; - setActiveIndex(newIndex); - }; - - switch (e.key) { - case "ArrowLeft": - if (dir === "rtl") { - if (value.length > 0 && (activeIndex !== -1 || loop)) { - moveNext(); - } - } else { - if (value.length > 0 && target.selectionStart === 0) { - movePrev(); - } - } - break; - - case "ArrowRight": - if (dir === "rtl") { - if (value.length > 0 && target.selectionStart === 0) { - movePrev(); - } - } else { - if (value.length > 0 && (activeIndex !== -1 || loop)) { - moveNext(); - } - } - break; - - case "Backspace": - case "Delete": - if (value.length > 0) { - if (activeIndex !== -1 && activeIndex < value.length) { - onValueChangeHandler(value[activeIndex]); - moveCurrent(); - } else { - if (target.selectionStart === 0) { - if (selectedValue === inputValue || isValueSelected) { - onValueChangeHandler(value[value.length - 1]); - } - } - } - } - break; - - case "Enter": - setOpen(true); - break; - - case "Escape": - if (activeIndex !== -1) { - setActiveIndex(-1); - } else if (open) { - setOpen(false); - } - break; - } - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [value, inputValue, activeIndex, loop], - ); - - return ( - - - {children} - - - ); -}; - -const MultiSelectorTrigger = forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, children, ...props }, ref) => { - const { value, onValueChange, activeIndex } = useMultiSelect(); - - const mousePreventDefault = useCallback((e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - }, []); - - return ( -
- {value.map((item, index) => ( - - {item} - - - ))} - {children} -
- ); -}); - -MultiSelectorTrigger.displayName = "MultiSelectorTrigger"; - -const MultiSelectorInput = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => { - const { - setOpen, - inputValue, - setInputValue, - activeIndex, - setActiveIndex, - handleSelect, - ref: inputRef, - } = useMultiSelect(); - - return ( - setOpen(false)} - onFocus={() => setOpen(true)} - onClick={() => setActiveIndex(-1)} - className={cn( - "ml-2 bg-transparent outline-none placeholder:text-muted-foreground flex-1", - className, - activeIndex !== -1 && "caret-transparent", - )} - /> - ); -}); - -MultiSelectorInput.displayName = "MultiSelectorInput"; - -const MultiSelectorContent = forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ children }, ref) => { - const { open } = useMultiSelect(); - return ( -
- {open && children} -
- ); -}); - -MultiSelectorContent.displayName = "MultiSelectorContent"; - -const MultiSelectorList = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children }, ref) => { - return ( - - {children} - - No results found - - - ); -}); - -MultiSelectorList.displayName = "MultiSelectorList"; - -const MultiSelectorItem = forwardRef< - React.ElementRef, - { value: string } & React.ComponentPropsWithoutRef< - typeof CommandPrimitive.Item - > ->(({ className, value, children, ...props }, ref) => { - const { value: Options, onValueChange, setInputValue } = useMultiSelect(); - - const mousePreventDefault = useCallback((e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - }, []); - - const isIncluded = Options.includes(value); - return ( - { - onValueChange(value); - setInputValue(""); - }} - className={cn( - "rounded-md cursor-pointer px-2 py-1 transition-colors flex justify-between ", - className, - isIncluded && "opacity-50 cursor-default", - props.disabled && "opacity-50 cursor-not-allowed", - )} - onMouseDown={mousePreventDefault} - > - {children} - {isIncluded && } - - ); -}); - -MultiSelectorItem.displayName = "MultiSelectorItem"; - -export { - MultiSelector, - MultiSelectorContent, - MultiSelectorInput, - MultiSelectorItem, - MultiSelectorList, - MultiSelectorTrigger, -};