feat: integrate latest HMDesign design system updates
This commit is contained in:
1 parent
99faec189e
commit
1c4e0bb9a1
209 files changed
+8845
-552
No files matched your search
@@ -5,7 +5,7 @@ import { CheckIcon, MinusIcon } from "lucide-react"
|
||||
|
||||
// Radix Checkbox structure, data-state and ARIA are kept (ui-viz has no Checkbox spec; reference only); the look comes from
|
||||
// src/theme/checkbox.css. Indeterminate is `checked="indeterminate"` (Radix); the shadcn check icon is kept and a minus icon is shown for it.
|
||||
function Checkbox({ className, form, asChild, ...props }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
|
||||
function Checkbox({ className, form, asChild, children, ...props }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
|
||||
// Radix's reset listener uses the trigger's native form, while Root only passes `form` to its input.
|
||||
const useFormButton = !!form && !asChild
|
||||
const indicator = (
|
||||
@@ -26,7 +26,10 @@ function Checkbox({ className, form, asChild, ...props }: React.ComponentProps<t
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{useFormButton ? <button type="button" form={form}>{indicator}</button> : indicator}
|
||||
{asChild
|
||||
? React.cloneElement(React.Children.only(children) as React.ReactElement<{ children?: React.ReactNode }>, undefined,
|
||||
(children as React.ReactElement<{ children?: React.ReactNode }>).props.children, indicator)
|
||||
: useFormButton ? <button type="button" form={form}>{children}{indicator}</button> : <>{children}{indicator}</>}
|
||||
</CheckboxPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -63,6 +63,9 @@ function Combobox({
|
||||
const value = controlled ? valueProp : inner
|
||||
const selected = options.find((option) => option.value === value)
|
||||
const popupRef = useConnectedPopupRef()
|
||||
const generatedId = React.useId()
|
||||
const triggerId = props.id ?? `combobox-${generatedId}`
|
||||
const popupId = `combobox-popup-${generatedId}`
|
||||
|
||||
const select = (next: string) => {
|
||||
if (!controlled) setInner(next)
|
||||
@@ -76,7 +79,7 @@ function Combobox({
|
||||
<button
|
||||
type="button"
|
||||
role="combobox"
|
||||
aria-haspopup="listbox"
|
||||
aria-haspopup="dialog"
|
||||
aria-expanded={open}
|
||||
data-slot="combobox-trigger"
|
||||
data-size={size}
|
||||
@@ -84,6 +87,8 @@ function Combobox({
|
||||
disabled={disabled}
|
||||
className={cn("flex w-full items-center justify-between text-left outline-none disabled:cursor-not-allowed", className)}
|
||||
{...props}
|
||||
id={triggerId}
|
||||
aria-controls={open ? popupId : undefined}
|
||||
>
|
||||
<span data-slot="combobox-value">{selected ? selected.label : placeholder}</span>
|
||||
<ChevronDownIcon data-slot="combobox-icon" aria-hidden="true" />
|
||||
@@ -91,6 +96,9 @@ function Combobox({
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
ref={popupRef}
|
||||
id={popupId}
|
||||
aria-label={props["aria-label"]}
|
||||
aria-labelledby={props["aria-labelledby"] ?? (props["aria-label"] ? undefined : triggerId)}
|
||||
data-combobox-popup=""
|
||||
data-size={size}
|
||||
align="start"
|
||||
@@ -102,9 +110,9 @@ function Combobox({
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
<Command>
|
||||
<CommandInput placeholder={searchPlaceholder} />
|
||||
<CommandList>
|
||||
<Command label={props["aria-label"] ?? placeholder}>
|
||||
<CommandInput aria-label={searchPlaceholder} placeholder={searchPlaceholder} />
|
||||
<CommandList label={props["aria-label"] ?? placeholder} aria-labelledby={props["aria-labelledby"]}>
|
||||
<CommandEmpty>{emptyText}</CommandEmpty>
|
||||
{options.map((option) => (
|
||||
<CommandItem
|
||||
|
||||
@@ -54,6 +54,9 @@ function DatePicker({
|
||||
const [inner, setInner] = React.useState<Date | undefined>(defaultValue)
|
||||
const controlled = controlledProp ?? (valueProp !== undefined)
|
||||
const value = controlled ? valueProp : inner
|
||||
const generatedId = React.useId()
|
||||
const triggerId = props.id ?? `date-picker-${generatedId}`
|
||||
const popupId = `date-picker-popup-${generatedId}`
|
||||
|
||||
const select = (date: Date | undefined) => {
|
||||
if (!controlled) setInner(date)
|
||||
@@ -72,12 +75,17 @@ function DatePicker({
|
||||
disabled={disabled}
|
||||
className={cn("flex w-full items-center justify-between text-left outline-none disabled:cursor-not-allowed", className)}
|
||||
{...props}
|
||||
id={triggerId}
|
||||
aria-controls={open ? popupId : undefined}
|
||||
>
|
||||
<span data-slot="date-picker-value">{value ? format(value) : placeholder}</span>
|
||||
<CalendarIcon data-slot="date-picker-icon" aria-hidden="true" />
|
||||
</button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
id={popupId}
|
||||
aria-label={props["aria-label"]}
|
||||
aria-labelledby={props["aria-labelledby"] ?? (props["aria-label"] ? undefined : triggerId)}
|
||||
align="start"
|
||||
style={{ "--hm-popover-padding": "0px", "--hm-popover-width": "auto" } as React.CSSProperties}
|
||||
// Radix focuses the first tabbable (the "previous month" icon button). Land on the day the Calendar marks as its tab stop
|
||||
|
||||
@@ -73,7 +73,10 @@ function MenubarContent({
|
||||
alignOffset={alignOffset}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 min-w-36 origin-(--radix-menubar-content-transform-origin) overflow-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
||||
// Unmount the previous menu immediately on switching. Keeping its
|
||||
// dismissable layer alive for an exit animation closes the next menu
|
||||
// when that menu receives focus (Radix non-modal Menubar).
|
||||
"z-50 min-w-36 origin-(--radix-menubar-content-transform-origin) overflow-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -10,6 +10,8 @@ function Progress({
|
||||
className,
|
||||
value,
|
||||
max,
|
||||
asChild,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ProgressPrimitive.Root>) {
|
||||
// Use the same valid range/default as Radix so the visual fill agrees with its ARIA value.
|
||||
@@ -17,19 +19,24 @@ function Progress({
|
||||
const percentage = typeof value === "number" && value >= 0 && value <= effectiveMax
|
||||
? (value / effectiveMax) * 100
|
||||
: 0
|
||||
const indicator = <ProgressPrimitive.Indicator
|
||||
data-slot="progress-indicator"
|
||||
className="size-full flex-1 transition-all"
|
||||
style={{ transform: `translateX(${percentage - 100}%)` }}
|
||||
/>
|
||||
return (
|
||||
<ProgressPrimitive.Root
|
||||
data-slot="progress"
|
||||
className={cn("relative w-full overflow-hidden", className)}
|
||||
value={value}
|
||||
max={max}
|
||||
asChild={asChild}
|
||||
{...props}
|
||||
>
|
||||
<ProgressPrimitive.Indicator
|
||||
data-slot="progress-indicator"
|
||||
className="size-full flex-1 transition-all"
|
||||
style={{ transform: `translateX(${percentage - 100}%)` }}
|
||||
/>
|
||||
{asChild
|
||||
? React.cloneElement(React.Children.only(children) as React.ReactElement<{ children?: React.ReactNode }>, undefined,
|
||||
(children as React.ReactElement<{ children?: React.ReactNode }>).props.children, indicator)
|
||||
: <>{children}{indicator}</>}
|
||||
</ProgressPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ function ToggleGroup({
|
||||
size,
|
||||
spacing = 0,
|
||||
children,
|
||||
style,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &
|
||||
VariantProps<typeof toggleVariants> & {
|
||||
@@ -38,7 +39,7 @@ function ToggleGroup({
|
||||
data-variant={variant}
|
||||
data-size={size}
|
||||
data-spacing={spacing}
|
||||
style={{ "--hm-toggle-group-gap": `${spacing * 4}px` } as React.CSSProperties}
|
||||
style={{ "--hm-toggle-group-gap": `${spacing * 4}px`, ...style } as React.CSSProperties}
|
||||
className={cn("group/toggle-group flex w-fit flex-row items-center data-vertical:flex-col data-vertical:items-stretch", className)}
|
||||
{...props}
|
||||
>
|
||||
|
||||
Reference in new issue
Block a user