fix(dashboard,ui): Fixes to Combobox and CategoryCombobox (#9537)

**What**
- Fixes the Combobox to keep the width of the content constant.
- Brings CategoryCombobox inline with the other Combobox component
- Adds keyboard navigation to the CategoryCombobox: You can now navigate options using ArrowUp and ArrowDown, and if an option has children you can use ArrowRight to see the children options.
- Add "outline-none" to the Drawer component to stop it from flashing whenever focus is dropped.
- Removes a dependency that was added to the UI package by mistake

Resolves CC-155
This commit is contained in:
Kasper Fabricius Kristensen
2024-10-12 16:46:32 +02:00
committed by GitHub
parent 93b38bf47b
commit 1f682daf5c
8 changed files with 1323 additions and 1765 deletions

View File

@@ -18,6 +18,7 @@ import { clx, Text } from "@medusajs/ui"
import { matchSorter } from "match-sorter"
import {
ComponentPropsWithoutRef,
CSSProperties,
ForwardedRef,
Fragment,
ReactNode,
@@ -41,6 +42,9 @@ type ComboboxOption = {
type Value = string[] | string
const TABLUAR_NUM_WIDTH = 8
const TAG_BASE_WIDTH = 28
interface ComboboxProps<T extends Value = Value>
extends Omit<ComponentPropsWithoutRef<"input">, "onChange" | "value"> {
value?: T
@@ -195,6 +199,17 @@ const ComboboxImpl = <T extends Value = string>(
const hidePlaceholder = showSelected || open
const tagWidth = useMemo(() => {
if (!Array.isArray(selectedValues)) {
return TAG_BASE_WIDTH + TABLUAR_NUM_WIDTH // There can only be a single digit
}
const count = selectedValues.length
const digits = count.toString().length
return TAG_BASE_WIDTH + digits * TABLUAR_NUM_WIDTH
}, [selectedValues])
const results = useMemo(() => {
return isSearchControlled ? options : matches
}, [matches, options, isSearchControlled])
@@ -213,41 +228,42 @@ const ComboboxImpl = <T extends Value = string>(
<div
className={clx(
"relative flex cursor-pointer items-center gap-x-2 overflow-hidden",
"h-8 w-full rounded-md px-2 py-0.5",
"h-8 w-full rounded-md",
"bg-ui-bg-field transition-fg shadow-borders-base",
"hover:bg-ui-bg-field-hover",
"has-[input:focus]:shadow-borders-interactive-with-active",
"has-[:invalid]:shadow-borders-error has-[[aria-invalid=true]]:shadow-borders-error",
"has-[:disabled]:bg-ui-bg-disabled has-[:disabled]:text-ui-fg-disabled has-[:disabled]:cursor-not-allowed",
{
"pl-0.5": hasValue && isArrayValue,
},
className
)}
style={
{
"--tag-width": `${tagWidth}px`,
} as CSSProperties
}
>
{showTag && (
<div className="bg-ui-bg-base txt-compact-small-plus text-ui-fg-subtle focus-within:border-ui-fg-interactive relative flex h-[28px] items-center rounded-[4px] border py-[3px] pl-1.5 pr-1">
<span>{selectedValues.length}</span>
<button
type="button"
className="size-fit outline-none"
onClick={(e) => {
e.preventDefault()
handleValueChange(undefined)
}}
>
<XMarkMini className="text-ui-fg-muted" />
</button>
</div>
<button
type="button"
onClick={(e) => {
e.preventDefault()
handleValueChange(undefined)
}}
className="bg-ui-bg-base hover:bg-ui-bg-base-hover txt-compact-small-plus text-ui-fg-subtle focus-within:border-ui-fg-interactive transition-fg absolute left-0.5 top-0.5 z-[1] flex h-[28px] items-center rounded-[4px] border py-[3px] pl-1.5 pr-1 outline-none"
>
<span className="tabular-nums">{selectedValues.length}</span>
<XMarkMini className="text-ui-fg-muted" />
</button>
)}
<div className="relative flex size-full items-center">
{showSelected && (
<Text size="small" leading="compact">
{t("general.selected")}
</Text>
<div className="pointer-events-none absolute inset-y-0 left-[calc(var(--tag-width)+8px)] flex size-full items-center">
<Text size="small" leading="compact">
{t("general.selected")}
</Text>
</div>
)}
{hideInput && (
<div className="absolute inset-y-0 left-0 flex size-full items-center overflow-hidden">
<div className="pointer-events-none absolute inset-y-0 left-[calc(var(--tag-width)+8px)] flex size-full items-center overflow-hidden">
<Text size="small" leading="compact" className="truncate">
{selectedLabel}
</Text>
@@ -256,10 +272,14 @@ const ComboboxImpl = <T extends Value = string>(
<PrimitiveCombobox
autoSelect
ref={comboboxRef}
onFocus={() => setOpen(true)}
className={clx(
"txt-compact-small text-ui-fg-base placeholder:text-ui-fg-subtle size-full cursor-pointer bg-transparent pr-7 outline-none focus:cursor-text",
"txt-compact-small text-ui-fg-base placeholder:text-ui-fg-subtle transition-fg size-full cursor-pointer bg-transparent pl-2 pr-8 outline-none focus:cursor-text",
"hover:bg-ui-bg-field-hover",
{
"opacity-0": hideInput,
"pl-2": !showTag,
"pl-[calc(var(--tag-width)+8px)]": showTag,
}
)}
placeholder={hidePlaceholder ? undefined : placeholder}
@@ -267,11 +287,12 @@ const ComboboxImpl = <T extends Value = string>(
/>
</div>
<PrimitiveComboboxDisclosure
render={() => {
render={(props) => {
return (
<button
{...props}
type="button"
className="text-ui-fg-muted pointer-events-none absolute right-2 size-fit outline-none"
className="text-ui-fg-muted transition-fg hover:bg-ui-bg-field-hover absolute right-0 flex size-8 items-center justify-center rounded-r outline-none"
>
<TrianglesMini />
</button>
@@ -281,10 +302,11 @@ const ComboboxImpl = <T extends Value = string>(
</div>
<PrimitiveComboboxPopover
gutter={4}
sameWidth
ref={listboxRef}
role="listbox"
className={clx(
"shadow-elevation-flyout bg-ui-bg-base -left-2 z-50 w-[calc(var(--popover-anchor-width)+16px)] rounded-[8px] p-1",
"shadow-elevation-flyout bg-ui-bg-base z-50 rounded-[8px] p-1",
"max-h-[200px] overflow-y-auto",
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
@@ -303,7 +325,7 @@ const ComboboxImpl = <T extends Value = string>(
setValueOnClick={false}
disabled={disabled}
className={clx(
"transition-fg bg-ui-bg-base data-[active-item=true]:bg-ui-bg-base-hover group flex cursor-pointer items-center gap-x-2 rounded-[4px] px-2 py-1.5",
"transition-fg bg-ui-bg-base data-[active-item=true]:bg-ui-bg-base-hover group flex cursor-pointer items-center gap-x-2 rounded-[4px] px-2 py-1",
{
"text-ui-fg-disabled": disabled,
"bg-ui-bg-component": disabled,