Files
medusa-store/www/api-reference/components/Select/Dropdown/index.tsx
Shahed Nasser f07dc0384f docs,api-ref: added search filters (#4830)
* initial implementation of search modal

* added hit and search suggestions

* added support for multiple indices

* updated sample env

* added close when click outside dropdown

* test for mobile

* added mobile design

* added shortcut

* dark mode fixes

* added search to docs

* added plugins filter

* added React import

* moved filters to configurations

* handled error on page load

* change suggestion text

* removed hits limit

* handle select all

* open link in current tab

* change highlight colors

* added support for shortcuts + auto focus

* change header and footer

* redesigned search ui
2023-08-24 18:36:06 +03:00

152 lines
4.2 KiB
TypeScript

import clsx from "clsx"
import IconCheckMini from "../../Icons/CheckMini"
import IconEllipseMiniSolid from "../../Icons/EllipseMiniSolid"
import { OptionType } from "../../../hooks/use-select"
import { forwardRef, useCallback, useEffect, useRef } from "react"
type SelectDropdownProps = {
options: OptionType[]
open: boolean
setOpen: React.Dispatch<React.SetStateAction<boolean>>
addAll?: boolean
multiple?: boolean
isAllSelected: boolean
isValueSelected: (val: string) => boolean
handleSelectAll: () => void
handleChange?: (selectedValue: string, wasSelected: boolean) => void
parentRef?: React.RefObject<HTMLDivElement>
className?: string
}
const SelectDropdown = forwardRef<HTMLDivElement, SelectDropdownProps>(
function SelectDropdown(
{
open,
setOpen,
options,
addAll,
multiple = false,
isAllSelected,
isValueSelected,
handleSelectAll,
handleChange: handleSelectChange,
parentRef,
className,
},
passedRef
) {
const ref = useRef<HTMLDivElement | null>(null)
const setRefs = useCallback(
(node: HTMLDivElement) => {
// Ref's from useRef needs to have the node assigned to `current`
ref.current = node
if (typeof passedRef === "function") {
passedRef(node)
} else if (passedRef && "current" in passedRef) {
passedRef.current = node
}
},
[passedRef]
)
const handleChange = (clickedValue: string, wasSelected: boolean) => {
handleSelectChange?.(clickedValue, wasSelected)
if (!multiple) {
setOpen(false)
}
}
const handleOutsideClick = useCallback(
(e: MouseEvent) => {
if (
open &&
!ref.current?.contains(e.target as Element) &&
!parentRef?.current?.contains(e.target as Element)
) {
setOpen(false)
}
},
[open, parentRef, setOpen]
)
useEffect(() => {
document.body.addEventListener("click", handleOutsideClick)
return () => {
document.body.removeEventListener("click", handleOutsideClick)
}
}, [handleOutsideClick])
const getSelectOption = (option: OptionType, index: number) => {
const isSelected = option.isAllOption
? isAllSelected
: isValueSelected(option.value)
return (
<li
key={index}
className={clsx(
"pr-0.75 relative rounded-sm py-0.5 pl-2.5",
"hover:bg-medusa-bg-base-hover dark:hover:bg-medusa-bg-base-hover-dark",
"[&>svg]:left-0.75 cursor-pointer [&>svg]:absolute [&>svg]:top-0.5",
!isSelected && "text-compact-small",
isSelected && "text-compact-small-plus"
)}
onClick={() => {
if (option.isAllOption) {
handleSelectAll()
} else {
handleChange(option.value, isSelected)
}
}}
>
{isSelected && (
<>
{multiple && (
<IconCheckMini className="stroke-medusa-fg-base dark:stroke-medusa-fg-base-dark" />
)}
{!multiple && (
<IconEllipseMiniSolid className="fill-medusa-fg-base dark:fill-medusa-fg-base-dark" />
)}
</>
)}
{option.label}
</li>
)
}
return (
<div
className={clsx(
"absolute top-full left-0 w-full",
"z-10 h-0 translate-y-0 overflow-hidden transition-transform",
open && "h-auto translate-y-0.5 !overflow-visible",
className
)}
ref={setRefs}
>
<ul
className={clsx(
"p-0.25 mb-0 w-full overflow-auto rounded",
"bg-medusa-bg-base dark:bg-medusa-bg-base-dark text-medusa-fg-base dark:text-medusa-fg-base-dark",
"shadow-flyout dark:shadow-flyout-dark list-none"
)}
>
{addAll &&
getSelectOption(
{
value: "all",
label: "All Areas",
isAllOption: true,
},
-1
)}
{options.map(getSelectOption)}
</ul>
</div>
)
}
)
export default SelectDropdown