diff --git a/src/app/globals.css b/src/app/globals.css index fddb75d..12781e7 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -386,3 +386,13 @@ html[data-theme="dark"] { .game-explanation { display: none; } + +.icon-delivery { + display: inline-block; + width: 16px; + height: 16px; + flex-shrink: 0; + background-color: var(--primary); + -webkit-mask: url("/assets/images/fast-delivery.svg") no-repeat center / contain; + mask: url("/assets/images/fast-delivery.svg") no-repeat center / contain; +} \ No newline at end of file diff --git a/src/components/combobox/Combobox.tsx b/src/components/combobox/Combobox.tsx index e157d20..9ce8086 100644 --- a/src/components/combobox/Combobox.tsx +++ b/src/components/combobox/Combobox.tsx @@ -1,214 +1,234 @@ -'use client'; +"use client"; -import React, { useState, useEffect, useRef } from 'react'; -import { motion, Variants } from 'framer-motion'; -import { Icon, SearchNormal } from 'iconsax-react'; -import clsx from 'clsx'; -import { ChevronDown } from 'lucide-react'; -import Image from 'next/image'; +import clsx from "clsx"; +import { motion, Variants } from "framer-motion"; +import { Icon, SearchNormal } from "iconsax-react"; +import { ChevronDown } from "lucide-react"; +import Image from "next/image"; +import React, { useEffect, useRef, useState } from "react"; export interface ComboboxOption { - id: string; - title: string; - icon?: Icon; - imagePath?: string; - label?: string; + id: string; + title: string; + icon?: Icon; + imagePath?: string; + label?: string; } type Props = { - title: string; - options: Array; - expanded?: boolean; - selectedId: string; - searchable?: boolean; - placeholder?: string; - icon?: React.ElementType; - onSelectionChange: (e: React.MouseEvent, index: number) => void, -} & React.HTMLAttributes + title: string; + options: Array; + expanded?: boolean; + selectedId: string; + searchable?: boolean; + placeholder?: string; + icon?: React.ElementType; + onSelectionChange: ( + e: React.MouseEvent, + index: number, + ) => void; +} & React.HTMLAttributes; -function Combobox({ title, options, placeholder, icon: Icon, expanded, selectedId, searchable = true, onSelectionChange, ...props }: Props) { - const [expand, setExpand] = useState(expanded); - const [searchValue, setSearchValue] = useState(''); - const boxRef = useRef(null); +function Combobox({ + title, + options, + placeholder, + icon: Icon, + expanded, + selectedId, + searchable = true, + onSelectionChange, + ...props +}: Props) { + const [expand, setExpand] = useState(expanded); + const [searchValue, setSearchValue] = useState(""); + const boxRef = useRef(null); - useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - if (boxRef.current && !boxRef.current.contains(event.target as Node)) { - setExpand(false); - } - }; - - if (expand) { - document.addEventListener('mousedown', handleClickOutside); - } - - return () => { - document.removeEventListener('mousedown', handleClickOutside); - }; - }, [expand]); - - const toggleExpand = () => { - setExpand((prev) => !prev); - setSearchValue(''); - }; - - const searchChanged = (e: React.ChangeEvent) => { - setSearchValue(e.target.value); - }; - - const setSelection = (e: React.MouseEvent, index: number) => { - e.stopPropagation(); // prevent toggle - onSelectionChange(e, index); + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (boxRef.current && !boxRef.current.contains(event.target as Node)) { setExpand(false); + } }; - const selectedOption = options.find((x) => x.id === selectedId); - const activeIcon = selectedOption?.icon && React.createElement(selectedOption.icon, { - size: 16, - color: "currentColor", - className: "inline-block mr-1 text-primary dark:text-foreground" + if (expand) { + document.addEventListener("mousedown", handleClickOutside); + } + + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [expand]); + + const toggleExpand = () => { + setExpand((prev) => !prev); + setSearchValue(""); + }; + + const searchChanged = (e: React.ChangeEvent) => { + setSearchValue(e.target.value); + }; + + const setSelection = ( + e: React.MouseEvent, + index: number, + ) => { + e.stopPropagation(); // prevent toggle + onSelectionChange(e, index); + setExpand(false); + }; + + const selectedOption = options.find((x) => x.id === selectedId); + const activeIcon = + selectedOption?.icon && + React.createElement(selectedOption.icon, { + size: 16, + color: "currentColor", + className: "inline-block mr-1 text-primary dark:text-foreground", }); - const activeImage = selectedOption?.imagePath && ( - - ); + const activeImage = selectedOption?.imagePath && ( + + ); + const variants: Variants = { + collapse: { + opacity: 0, + scale: 0.95, + pointerEvents: "none", // disable clicks + transition: { + duration: 0.1, + ease: "easeInOut", + }, + }, + expand: { + opacity: 1, + scale: 1, + pointerEvents: "auto", // re-enable clicks + transition: { + duration: 0.1, + ease: "easeInOut", + }, + }, + }; - const variants: Variants = { - collapse: { - opacity: 0, - scale: 0.95, - pointerEvents: 'none', // disable clicks - transition: { - duration: 0.1, - ease: 'easeInOut' - } - }, - expand: { - opacity: 1, - scale: 1, - pointerEvents: 'auto', // re-enable clicks - transition: { - duration: 0.1, - ease: 'easeInOut' - } - } - }; - - - return ( -
+
+
+ + e.stopPropagation()} + initial="collapse" + animate={expand ? "expand" : "collapse"} + variants={variants} + > + {searchable && ( +
+ + +
+ )} +
+ {options + .filter((v) => v.title?.includes(searchValue)) + .map((v, i) => ( +
setSelection(e, i)} + key={v.id} + data-selected={v.id === selectedId} + className="flex gap-3 items-center justify-end px-2 py-1 cursor-pointer hover:bg-current/10 rounded-md last:rounded-b-xl" + tabIndex={0} + role="option" + aria-selected + > + {v?.imagePath ? ( + + ) : ( + v?.icon && + React.createElement(v.icon, { + size: 16, + color: "currentColor", + className: + "inline-block mr-1 text-primary dark:text-foreground", + }) + )} + + {v.title} + +
+ ))} +
+
+
+ ); } - -export default Combobox \ No newline at end of file +export default Combobox;