115 lines
3.3 KiB
TypeScript
115 lines
3.3 KiB
TypeScript
import Icon from '@/Components/Icon/Icon'
|
|
import { DropdownItem } from './DropdownItem'
|
|
import { classNames } from '@standardnotes/snjs'
|
|
import {
|
|
Select,
|
|
SelectItem,
|
|
SelectLabel,
|
|
SelectPopover,
|
|
SelectStoreProps,
|
|
useSelectStore,
|
|
VisuallyHidden,
|
|
} from '@ariakit/react'
|
|
import { KeyboardKey } from '@standardnotes/ui-services'
|
|
|
|
type DropdownProps = {
|
|
label: string
|
|
items: DropdownItem[]
|
|
value: string
|
|
onChange: (value: string) => void
|
|
disabled?: boolean
|
|
classNameOverride?: {
|
|
wrapper?: string
|
|
button?: string
|
|
popover?: string
|
|
}
|
|
fullWidth?: boolean
|
|
popoverPlacement?: SelectStoreProps['placement']
|
|
}
|
|
|
|
const Dropdown = ({
|
|
label,
|
|
value,
|
|
onChange,
|
|
items,
|
|
disabled,
|
|
fullWidth,
|
|
classNameOverride = {},
|
|
popoverPlacement,
|
|
}: DropdownProps) => {
|
|
const select = useSelectStore({
|
|
value,
|
|
setValue: onChange,
|
|
placement: popoverPlacement || 'top',
|
|
})
|
|
|
|
const isExpanded = select.useState('open')
|
|
|
|
const currentItem = items.find((item) => item.value === value)
|
|
|
|
return (
|
|
<div
|
|
className={classNameOverride.wrapper}
|
|
onKeyDown={(event) => {
|
|
if (event.key === KeyboardKey.Escape) {
|
|
event.stopPropagation()
|
|
select.toggle()
|
|
}
|
|
}}
|
|
>
|
|
<VisuallyHidden>
|
|
<SelectLabel store={select}>{label}</SelectLabel>
|
|
</VisuallyHidden>
|
|
<Select
|
|
className={classNames(
|
|
'flex w-full min-w-55 items-center justify-between rounded border border-passive-3 bg-default md:translucent-ui:bg-transparent px-3.5 py-1.5 text-sm text-foreground',
|
|
disabled && 'opacity-50',
|
|
classNameOverride.button,
|
|
!fullWidth && 'md:w-fit',
|
|
)}
|
|
store={select}
|
|
disabled={disabled}
|
|
>
|
|
<div className="flex items-center">
|
|
{currentItem?.icon ? (
|
|
<div className="mr-2 flex">
|
|
<Icon type={currentItem.icon} className={currentItem.iconClassName ?? ''} size="small" />
|
|
</div>
|
|
) : null}
|
|
<div className="text-base lg:text-sm">{currentItem?.label}</div>
|
|
</div>
|
|
<Icon type="chevron-down" size="normal" className={isExpanded ? 'rotate-180' : ''} />
|
|
</Select>
|
|
<SelectPopover
|
|
store={select}
|
|
className={classNames(
|
|
'z-dropdown-menu max-h-[var(--popover-available-height)] w-[var(--popover-anchor-width)] overflow-y-auto rounded border border-passive-3 bg-default py-1',
|
|
classNameOverride.popover,
|
|
)}
|
|
style={{
|
|
backdropFilter: 'var(--popover-backdrop-filter)',
|
|
}}
|
|
portal={false}
|
|
>
|
|
{items.map((item) => (
|
|
<SelectItem
|
|
className="flex cursor-pointer items-center bg-transparent px-3 py-1.5 text-sm text-text hover:bg-contrast hover:text-foreground [&[data-active-item]]:bg-info [&[data-active-item]]:text-info-contrast"
|
|
key={item.value}
|
|
value={item.value}
|
|
disabled={item.disabled}
|
|
>
|
|
{item.icon ? (
|
|
<div className="mr-3 flex">
|
|
<Icon type={item.icon} className={item.iconClassName ?? ''} size="small" />
|
|
</div>
|
|
) : null}
|
|
<div className="text-base lg:text-sm">{item.label}</div>
|
|
</SelectItem>
|
|
))}
|
|
</SelectPopover>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default Dropdown
|