@devalok/shilp-sutra 0.49.4 → 0.49.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"combobox.js","names":[],"sources":["../../src/ui/combobox.tsx"],"sourcesContent":["'use client'\n\nimport * as PopoverPrimitive from '@primitives/react-popover'\nimport { IconCheck, IconChevronDown, IconSearch, IconX } from '@tabler/icons-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useFormField } from './form'\nimport { Icon } from './icon'\nimport { IconProvider, type IconSize } from './icon-context'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport type { IconInput } from './lib/icon-input'\nimport { springs, tweens } from './lib/motion'\nimport { normalizeIcon } from './lib/normalize-icon'\nimport { cn } from './lib/utils'\n\n/** Border tint per validation state, applied to the trigger. */\nconst stateBorderClasses: Record<Exclude<FieldState, 'default'>, string> = {\n error: 'border-error-7',\n warning: 'border-warning-7',\n success: 'border-success-7',\n}\n\nexport const comboboxTriggerVariants = cva(\n [\n 'flex w-full items-center justify-between whitespace-nowrap rounded-control',\n 'border border-surface-border-strong bg-surface-raised-hover',\n 'transition-colors duration-fast-01 ease-productive-standard',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 focus-visible:border-accent-7',\n 'disabled:cursor-not-allowed disabled:opacity-action-disabled',\n ],\n {\n variants: {\n size: {\n xs: 'h-ds-xs-plus text-ds-sm px-ds-02',\n sm: 'h-ds-sm text-ds-sm px-ds-03',\n md: 'h-ds-md text-ds-md px-ds-04',\n lg: 'h-ds-lg text-ds-md px-ds-05',\n },\n },\n defaultVariants: { size: 'md' },\n },\n)\n\nexport type ComboboxSize = NonNullable<VariantProps<typeof comboboxTriggerVariants>['size']>\n\n/** Maps combobox size to Icon component size for chevron / check icons */\nconst iconSizeMap: Record<NonNullable<ComboboxSize>, IconSize> = {\n xs: 'xs',\n sm: 'sm',\n md: 'sm',\n lg: 'md',\n}\n\n/** Maps combobox size to pill text + padding classes */\nconst pillSizeMap: Record<NonNullable<ComboboxSize>, string> = {\n xs: 'px-ds-02 py-0 text-ds-xs',\n sm: 'px-ds-02 py-0 text-ds-xs',\n md: 'px-ds-03 py-[1px] text-ds-sm',\n lg: 'px-ds-03 py-[2px] text-ds-sm',\n}\n\n/** Maps combobox size to overflow text classes */\nconst overflowTextMap: Record<NonNullable<ComboboxSize>, string> = {\n xs: 'text-ds-xs',\n sm: 'text-ds-xs',\n md: 'text-ds-sm',\n lg: 'text-ds-sm',\n}\n\n/**\n * Option shape for a Combobox dropdown item.\n * `value` must be unique across all options — it is the key used in selection state.\n */\nexport interface ComboboxOption {\n value: string\n label: string\n description?: string\n icon?: IconInput\n disabled?: boolean\n}\n\n/**\n * Props for Combobox — a searchable single or multi-select dropdown with built-in keyboard\n * navigation, pill overflow (\"+ N more\"), and an optional custom option renderer.\n *\n * **Single vs multi:** `multiple={false}` (default) — `value` is a `string` and `onValueChange`\n * receives a `string`. When `multiple={true}`, `value` is `string[]`, `onValueChange` receives\n * `string[]`, and selected items appear as dismissible pills in the trigger.\n *\n * The props form a **discriminated union** on `multiple` — TypeScript will narrow `value` and\n * `onValueChange` automatically, so no manual casts are needed.\n *\n * **Custom rendering:** Use `renderOption` to return custom JSX per option (e.g. avatars, badges).\n *\n * @example\n * // Single-select country picker:\n * <Combobox\n * options={[{ value: 'in', label: 'India' }, { value: 'us', label: 'United States' }]}\n * value={country}\n * onValueChange={(v) => setCountry(v)}\n * placeholder=\"Select country\"\n * />\n *\n * @example\n * // Multi-select tag picker with pill display:\n * <Combobox\n * multiple\n * options={tagOptions}\n * value={selectedTags}\n * onValueChange={(v) => setSelectedTags(v)}\n * placeholder=\"Select tags...\"\n * />\n *\n * @example\n * // Custom option renderer (user avatars in assignee picker):\n * <Combobox\n * options={users.map(u => ({ value: u.id, label: u.name }))}\n * value={assigneeId}\n * onValueChange={(v) => setAssigneeId(v)}\n * renderOption={(option, selected) => (\n * <span className=\"flex items-center gap-ds-03\">\n * <Avatar size=\"xs\"><AvatarFallback>{option.label[0]}</AvatarFallback></Avatar>\n * {option.label}\n * </span>\n * )}\n * />\n * // These are just a few ways — feel free to combine props creatively!\n */\ninterface ComboboxBaseProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {\n /** Available options shown in the dropdown. */\n options: ComboboxOption[]\n /** Placeholder shown in the trigger when no value is selected. */\n placeholder?: string\n /** Placeholder for the search input inside the dropdown. */\n searchPlaceholder?: string\n /** Message shown when the search yields no results. */\n emptyMessage?: string\n disabled?: boolean\n triggerClassName?: string\n /** Max visible items in the dropdown before scroll (default 6). */\n maxVisible?: number\n /** Custom renderer for each option row. Receives the option and whether it's currently selected. */\n renderOption?: (option: ComboboxOption, selected: boolean) => React.ReactNode\n /** Accessible label for the trigger button. Falls back to `placeholder` if not provided. */\n accessibleLabel?: string\n /** Size of the trigger. Controls height, text size, padding, and pill sizing. */\n size?: ComboboxSize\n /** Validation/feedback state. `'error'` also sets `aria-invalid`. Inherited from `FormField` when omitted. */\n state?: FieldState\n}\n\ninterface ComboboxSingleProps extends ComboboxBaseProps {\n multiple?: false\n value?: string\n onValueChange: (value: string) => void\n}\n\ninterface ComboboxMultipleProps extends ComboboxBaseProps {\n multiple: true\n value?: string[]\n onValueChange: (value: string[]) => void\n}\n\nexport type ComboboxProps = ComboboxSingleProps | ComboboxMultipleProps\n\n/** Max pills shown in the trigger before \"+N more\" overflow */\nconst MAX_VISIBLE_PILLS = 2\n\n/** Approximate height of a single option item in px */\nconst ITEM_HEIGHT_PX = 36\n\nconst Combobox = React.forwardRef<HTMLButtonElement, ComboboxProps>(\n (\n {\n options,\n value,\n onValueChange,\n placeholder = 'Select...',\n searchPlaceholder = 'Search...',\n emptyMessage = 'No results found',\n multiple = false,\n disabled = false,\n className,\n triggerClassName,\n maxVisible = 6,\n renderOption,\n accessibleLabel,\n size: sizeProp = 'md',\n state: stateProp,\n ...rest\n },\n ref,\n ) => {\n const size = sizeProp ?? 'md'\n const [open, setOpen] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [highlightedIndex, setHighlightedIndex] = React.useState(-1)\n const searchInputRef = React.useRef<HTMLInputElement>(null)\n const listRef = React.useRef<HTMLUListElement>(null)\n const optionIdPrefix = React.useId()\n const listboxId = React.useId()\n\n const selectedValues = React.useMemo<string[]>(() => {\n if (value === undefined || value === null) return []\n if (Array.isArray(value)) return value\n return [value]\n }, [value])\n\n const filteredOptions = React.useMemo(\n () =>\n search\n ? options.filter((o) =>\n o.label.toLowerCase().includes(search.toLowerCase()),\n )\n : options,\n [options, search],\n )\n\n const isSelected = React.useCallback(\n (optionValue: string) => selectedValues.includes(optionValue),\n [selectedValues],\n )\n\n const handleSelect = React.useCallback(\n (optionValue: string) => {\n if (multiple) {\n const newValue = selectedValues.includes(optionValue)\n ? selectedValues.filter((v) => v !== optionValue)\n : [...selectedValues, optionValue]\n ;(onValueChange as (value: string[]) => void)(newValue)\n } else {\n ;(onValueChange as (value: string) => void)(optionValue)\n setOpen(false)\n }\n },\n [multiple, selectedValues, onValueChange],\n )\n\n const handleRemovePill = React.useCallback(\n (e: React.SyntheticEvent, optionValue: string) => {\n e.stopPropagation()\n e.preventDefault()\n const newValue = selectedValues.filter((v) => v !== optionValue)\n ;(onValueChange as (value: string[]) => void)(newValue)\n },\n [selectedValues, onValueChange],\n )\n\n const handleOpenChange = React.useCallback(\n (nextOpen: boolean) => {\n if (disabled) return\n setOpen(nextOpen)\n if (!nextOpen) {\n setSearch('')\n setHighlightedIndex(-1)\n }\n },\n [disabled],\n )\n\n // Auto-focus search input when popover opens\n React.useEffect(() => {\n if (open) {\n // Use a small timeout to allow the popover to render\n const timer = setTimeout(() => {\n searchInputRef.current?.focus()\n }, 0)\n return () => clearTimeout(timer)\n }\n }, [open])\n\n const findNextEnabledIndex = React.useCallback(\n (currentIndex: number, direction: 1 | -1): number => {\n const len = filteredOptions.length\n if (len === 0) return -1\n\n let nextIndex = currentIndex + direction\n while (nextIndex >= 0 && nextIndex < len) {\n if (!filteredOptions[nextIndex].disabled) return nextIndex\n nextIndex += direction\n }\n return currentIndex\n },\n [filteredOptions],\n )\n\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent) => {\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault()\n const nextIdx = findNextEnabledIndex(highlightedIndex, 1)\n setHighlightedIndex(nextIdx)\n break\n }\n case 'ArrowUp': {\n e.preventDefault()\n const prevIdx = findNextEnabledIndex(highlightedIndex, -1)\n setHighlightedIndex(prevIdx)\n break\n }\n case 'Home': {\n e.preventDefault()\n const firstEnabled = filteredOptions.findIndex((o) => !o.disabled)\n setHighlightedIndex(firstEnabled)\n break\n }\n case 'End': {\n e.preventDefault()\n let lastEnabled = -1\n for (let i = filteredOptions.length - 1; i >= 0; i--) {\n if (!filteredOptions[i].disabled) {\n lastEnabled = i\n break\n }\n }\n setHighlightedIndex(lastEnabled)\n break\n }\n case 'Enter': {\n e.preventDefault()\n if (\n highlightedIndex >= 0 &&\n highlightedIndex < filteredOptions.length &&\n !filteredOptions[highlightedIndex].disabled\n ) {\n handleSelect(filteredOptions[highlightedIndex].value)\n }\n break\n }\n case 'Escape': {\n e.preventDefault()\n setOpen(false)\n setSearch('')\n setHighlightedIndex(-1)\n break\n }\n }\n },\n [highlightedIndex, filteredOptions, findNextEnabledIndex, handleSelect],\n )\n\n // Scroll highlighted option into view\n React.useEffect(() => {\n if (highlightedIndex >= 0 && listRef.current) {\n const optionEl = listRef.current.children[highlightedIndex] as HTMLElement\n if (optionEl) {\n optionEl.scrollIntoView?.({ block: 'nearest' })\n }\n }\n }, [highlightedIndex])\n\n const getSelectedLabel = React.useCallback(() => {\n if (selectedValues.length === 0) return null\n const option = options.find((o) => o.value === selectedValues[0])\n return option?.label ?? null\n }, [selectedValues, options])\n\n const resolvedIconSize = iconSizeMap[size]\n const resolvedPillClasses = pillSizeMap[size]\n const resolvedOverflowText = overflowTextMap[size]\n\n const renderTriggerContent = () => {\n if (multiple && selectedValues.length > 0) {\n const visiblePills = selectedValues.slice(0, MAX_VISIBLE_PILLS)\n const remaining = selectedValues.length - MAX_VISIBLE_PILLS\n\n return (\n <span className=\"flex flex-1 flex-wrap items-center gap-ds-02 overflow-hidden\">\n {visiblePills.map((val) => {\n const option = options.find((o) => o.value === val)\n if (!option) return null\n return (\n <span\n key={val}\n className={cn('inline-flex items-center gap-ds-01 rounded-control-inner bg-accent-2', resolvedPillClasses)}\n >\n {option.label}\n <button\n type=\"button\"\n className=\"inline-flex items-center justify-center rounded-pill outline-hidden hover:bg-surface-raised-hover transition-colors duration-fast-01 ease-productive-standard\"\n onClick={(e) => handleRemovePill(e, val)}\n aria-label={`Remove ${option.label}`}\n tabIndex={-1}\n >\n <Icon icon={IconX} size={resolvedIconSize} />\n </button>\n </span>\n )\n })}\n {remaining > 0 && (\n <span className={cn('text-surface-fg-muted', resolvedOverflowText)}>\n +{remaining} more\n </span>\n )}\n </span>\n )\n }\n\n if (!multiple && selectedValues.length === 1) {\n const label = getSelectedLabel()\n if (label) {\n return <span className=\"min-w-0 flex-1 truncate text-left\">{label}</span>\n }\n }\n\n return (\n <span className=\"min-w-0 flex-1 truncate text-left text-surface-fg-subtle\">\n {placeholder}\n </span>\n )\n }\n\n const fieldCtx = useFormField()\n const state = resolveFieldState(stateProp, fieldCtx.state)\n const isError = state === 'error'\n const ariaDescribedBy = fieldCtx.helperTextId\n const ariaRequired = fieldCtx.required\n\n return (\n <PopoverPrimitive.Root open={open} onOpenChange={handleOpenChange}>\n <div className={cn('relative', className)} {...rest}>\n <PopoverPrimitive.Trigger asChild disabled={disabled}>\n <button\n ref={ref}\n type=\"button\"\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={listboxId}\n aria-haspopup=\"listbox\"\n aria-label={accessibleLabel ?? placeholder}\n aria-invalid={isError || undefined}\n aria-describedby={ariaDescribedBy}\n aria-required={ariaRequired || undefined}\n disabled={disabled}\n className={cn(\n comboboxTriggerVariants({ size }),\n open && 'border-accent-7',\n state && stateBorderClasses[state],\n triggerClassName,\n )}\n >\n {renderTriggerContent()}\n <Icon icon={IconChevronDown} size={resolvedIconSize} className={cn(\"ml-ds-02 shrink-0 opacity-50 transition-transform duration-fast-01 ease-productive-standard\", open && 'rotate-180')} />\n </button>\n </PopoverPrimitive.Trigger>\n\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n asChild\n sideOffset={4}\n align=\"start\"\n onOpenAutoFocus={(e) => {\n e.preventDefault()\n searchInputRef.current?.focus()\n }}\n >\n <motion.div\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ ...springs.snappy, opacity: tweens.fade }}\n className={cn(\n 'z-popover w-[var(--radix-popover-trigger-width)] overflow-hidden rounded-overlay bg-surface-overlay shadow-floating',\n )}\n >\n {/* Search input */}\n <div className=\"flex items-center gap-ds-02 border-b border-surface-border px-ds-04\">\n <Icon icon={IconSearch} size=\"sm\" className=\"shrink-0 text-surface-fg-subtle\" />\n <input\n ref={searchInputRef}\n type=\"text\"\n className=\"flex-1 bg-transparent py-ds-03 text-ds-md outline-hidden placeholder:text-surface-fg-subtle\"\n placeholder={searchPlaceholder}\n value={search}\n onChange={(e) => {\n setSearch(e.target.value)\n setHighlightedIndex(-1)\n }}\n onKeyDown={handleKeyDown}\n aria-autocomplete=\"list\"\n aria-controls={listboxId}\n aria-activedescendant={\n highlightedIndex >= 0\n ? `${optionIdPrefix}-option-${highlightedIndex}`\n : undefined\n }\n aria-label=\"Search options\"\n />\n </div>\n\n {/* Options list */}\n {filteredOptions.length === 0 ? (\n <div className=\"px-ds-04 py-ds-05 text-center text-ds-md text-surface-fg-subtle\">\n {emptyMessage}\n </div>\n ) : (\n <ul\n ref={listRef}\n id={listboxId}\n role=\"listbox\"\n aria-multiselectable={multiple || undefined}\n className=\"overflow-auto p-ds-02\"\n style={{ maxHeight: `${maxVisible * ITEM_HEIGHT_PX}px` }}\n >\n {filteredOptions.map((option, index) => {\n const selected = isSelected(option.value)\n return (\n <li\n key={option.value}\n id={`${optionIdPrefix}-option-${index}`}\n role=\"option\"\n aria-selected={selected}\n aria-disabled={option.disabled || undefined}\n className={cn(\n 'relative flex cursor-pointer select-none items-center gap-ds-03 rounded-control px-ds-04 py-ds-03 text-ds-md outline-hidden',\n 'transition-colors duration-fast-01 ease-productive-standard',\n highlightedIndex === index &&\n 'bg-accent-2',\n selected && 'text-accent-11',\n option.disabled &&\n 'pointer-events-none opacity-action-disabled',\n )}\n onClick={() => {\n if (!option.disabled) {\n handleSelect(option.value)\n }\n }}\n onKeyDown={(e) => {\n if (!option.disabled && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault()\n handleSelect(option.value)\n }\n }}\n onMouseEnter={() => {\n if (!option.disabled) {\n setHighlightedIndex(index)\n }\n }}\n >\n {option.icon && (\n <span className=\"flex h-ico-sm w-ico-sm items-center justify-center shrink-0\">\n <IconProvider size={resolvedIconSize}>\n {normalizeIcon(option.icon)}\n </IconProvider>\n </span>\n )}\n <span className=\"flex flex-1 flex-col\">\n {renderOption ? (\n renderOption(option, selected)\n ) : (\n <>\n <span>{option.label}</span>\n {option.description && (\n <span className=\"text-ds-sm text-surface-fg-muted\">\n {option.description}\n </span>\n )}\n </>\n )}\n </span>\n {selected && (\n <Icon icon={IconCheck} size=\"sm\" className=\"shrink-0\" />\n )}\n </li>\n )\n })}\n </ul>\n )}\n </motion.div>\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n </div>\n </PopoverPrimitive.Root>\n )\n },\n)\nCombobox.displayName = 'Combobox'\n\nexport { Combobox }\n"],"mappings":"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"}
1
+ {"version":3,"file":"combobox.js","names":[],"sources":["../../src/ui/combobox.tsx"],"sourcesContent":["'use client'\n\nimport * as PopoverPrimitive from '@primitives/react-popover'\nimport { IconCheck, IconChevronDown, IconSearch, IconX } from '@tabler/icons-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useFormField } from './form'\nimport { Icon } from './icon'\nimport { IconProvider, type IconSize } from './icon-context'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport type { IconInput } from './lib/icon-input'\nimport { springs, tweens } from './lib/motion'\nimport { normalizeIcon } from './lib/normalize-icon'\nimport { cn } from './lib/utils'\n\n/** Border tint per validation state, applied to the trigger. */\nconst stateBorderClasses: Record<Exclude<FieldState, 'default'>, string> = {\n error: 'border-error-7',\n warning: 'border-warning-7',\n success: 'border-success-7',\n}\n\nexport const comboboxTriggerVariants = cva(\n [\n 'flex w-full items-center justify-between whitespace-nowrap rounded-control',\n 'border border-surface-border-strong bg-surface-raised-hover',\n 'transition-colors duration-fast-01 ease-productive-standard',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 focus-visible:border-accent-7',\n 'disabled:cursor-not-allowed disabled:opacity-action-disabled',\n ],\n {\n variants: {\n size: {\n xs: 'h-ds-xs-plus text-ds-sm px-ds-02',\n sm: 'h-ds-sm text-ds-sm px-ds-03',\n md: 'h-ds-md text-ds-md px-ds-04',\n lg: 'h-ds-lg text-ds-md px-ds-05',\n },\n },\n defaultVariants: { size: 'md' },\n },\n)\n\nexport type ComboboxSize = NonNullable<VariantProps<typeof comboboxTriggerVariants>['size']>\n\n/** Maps combobox size to Icon component size for chevron / check icons */\nconst iconSizeMap: Record<NonNullable<ComboboxSize>, IconSize> = {\n xs: 'xs',\n sm: 'sm',\n md: 'sm',\n lg: 'md',\n}\n\n/** Maps combobox size to pill text + padding classes */\nconst pillSizeMap: Record<NonNullable<ComboboxSize>, string> = {\n xs: 'px-ds-02 py-0 text-ds-xs',\n sm: 'px-ds-02 py-0 text-ds-xs',\n md: 'px-ds-03 py-[1px] text-ds-sm',\n lg: 'px-ds-03 py-[2px] text-ds-sm',\n}\n\n/** Maps combobox size to overflow text classes */\nconst overflowTextMap: Record<NonNullable<ComboboxSize>, string> = {\n xs: 'text-ds-xs',\n sm: 'text-ds-xs',\n md: 'text-ds-sm',\n lg: 'text-ds-sm',\n}\n\n/**\n * Option shape for a Combobox dropdown item.\n * `value` must be unique across all options — it is the key used in selection state.\n */\nexport interface ComboboxOption {\n value: string\n label: string\n description?: string\n icon?: IconInput\n disabled?: boolean\n}\n\n/**\n * Props for Combobox — a searchable single or multi-select dropdown with built-in keyboard\n * navigation, pill overflow (\"+ N more\"), and an optional custom option renderer.\n *\n * **Single vs multi:** `multiple={false}` (default) — `value` is a `string` and `onValueChange`\n * receives a `string`. When `multiple={true}`, `value` is `string[]`, `onValueChange` receives\n * `string[]`, and selected items appear as dismissible pills in the trigger.\n *\n * The props form a **discriminated union** on `multiple` — TypeScript will narrow `value` and\n * `onValueChange` automatically, so no manual casts are needed.\n *\n * **Custom rendering:** Use `renderOption` to return custom JSX per option (e.g. avatars, badges).\n *\n * @example\n * // Single-select country picker:\n * <Combobox\n * options={[{ value: 'in', label: 'India' }, { value: 'us', label: 'United States' }]}\n * value={country}\n * onValueChange={(v) => setCountry(v)}\n * placeholder=\"Select country\"\n * />\n *\n * @example\n * // Multi-select tag picker with pill display:\n * <Combobox\n * multiple\n * options={tagOptions}\n * value={selectedTags}\n * onValueChange={(v) => setSelectedTags(v)}\n * placeholder=\"Select tags...\"\n * />\n *\n * @example\n * // Custom option renderer (user avatars in assignee picker):\n * <Combobox\n * options={users.map(u => ({ value: u.id, label: u.name }))}\n * value={assigneeId}\n * onValueChange={(v) => setAssigneeId(v)}\n * renderOption={(option, selected) => (\n * <span className=\"flex items-center gap-ds-03\">\n * <Avatar size=\"xs\"><AvatarFallback>{option.label[0]}</AvatarFallback></Avatar>\n * {option.label}\n * </span>\n * )}\n * />\n * // These are just a few ways — feel free to combine props creatively!\n */\ninterface ComboboxBaseProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {\n /** Available options shown in the dropdown. */\n options: ComboboxOption[]\n /** Placeholder shown in the trigger when no value is selected. */\n placeholder?: string\n /** Placeholder for the search input inside the dropdown. */\n searchPlaceholder?: string\n /** Message shown when the search yields no results. */\n emptyMessage?: string\n disabled?: boolean\n triggerClassName?: string\n /** Max visible items in the dropdown before scroll (default 6). */\n maxVisible?: number\n /** Custom renderer for each option row. Receives the option and whether it's currently selected. */\n renderOption?: (option: ComboboxOption, selected: boolean) => React.ReactNode\n /** Accessible label for the trigger button. Falls back to `placeholder` if not provided. */\n accessibleLabel?: string\n /** Size of the trigger. Controls height, text size, padding, and pill sizing. */\n size?: ComboboxSize\n /** Validation/feedback state. `'error'` also sets `aria-invalid`. Inherited from `FormField` when omitted. */\n state?: FieldState\n}\n\ninterface ComboboxSingleProps extends ComboboxBaseProps {\n multiple?: false\n value?: string\n onValueChange: (value: string) => void\n}\n\ninterface ComboboxMultipleProps extends ComboboxBaseProps {\n multiple: true\n value?: string[]\n onValueChange: (value: string[]) => void\n}\n\nexport type ComboboxProps = ComboboxSingleProps | ComboboxMultipleProps\n\n/** Max pills shown in the trigger before \"+N more\" overflow */\nconst MAX_VISIBLE_PILLS = 2\n\n/** Approximate height of a single option item in px */\nconst ITEM_HEIGHT_PX = 36\n\nconst Combobox = React.forwardRef<HTMLButtonElement, ComboboxProps>(\n (\n {\n options,\n value,\n onValueChange,\n placeholder = 'Select...',\n searchPlaceholder = 'Search...',\n emptyMessage = 'No results found',\n multiple = false,\n disabled = false,\n className,\n triggerClassName,\n maxVisible = 6,\n renderOption,\n accessibleLabel,\n id: externalId,\n size: sizeProp = 'md',\n state: stateProp,\n ...rest\n },\n ref,\n ) => {\n const size = sizeProp ?? 'md'\n const [open, setOpen] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [highlightedIndex, setHighlightedIndex] = React.useState(-1)\n const searchInputRef = React.useRef<HTMLInputElement>(null)\n const listRef = React.useRef<HTMLUListElement>(null)\n const optionIdPrefix = React.useId()\n const listboxId = React.useId()\n\n const selectedValues = React.useMemo<string[]>(() => {\n if (value === undefined || value === null) return []\n if (Array.isArray(value)) return value\n return [value]\n }, [value])\n\n const filteredOptions = React.useMemo(\n () =>\n search\n ? options.filter((o) =>\n o.label.toLowerCase().includes(search.toLowerCase()),\n )\n : options,\n [options, search],\n )\n\n const isSelected = React.useCallback(\n (optionValue: string) => selectedValues.includes(optionValue),\n [selectedValues],\n )\n\n const handleSelect = React.useCallback(\n (optionValue: string) => {\n if (multiple) {\n const newValue = selectedValues.includes(optionValue)\n ? selectedValues.filter((v) => v !== optionValue)\n : [...selectedValues, optionValue]\n ;(onValueChange as (value: string[]) => void)(newValue)\n } else {\n ;(onValueChange as (value: string) => void)(optionValue)\n setOpen(false)\n }\n },\n [multiple, selectedValues, onValueChange],\n )\n\n const handleRemovePill = React.useCallback(\n (e: React.SyntheticEvent, optionValue: string) => {\n e.stopPropagation()\n e.preventDefault()\n const newValue = selectedValues.filter((v) => v !== optionValue)\n ;(onValueChange as (value: string[]) => void)(newValue)\n },\n [selectedValues, onValueChange],\n )\n\n const handleOpenChange = React.useCallback(\n (nextOpen: boolean) => {\n if (disabled) return\n setOpen(nextOpen)\n if (!nextOpen) {\n setSearch('')\n setHighlightedIndex(-1)\n }\n },\n [disabled],\n )\n\n // Auto-focus search input when popover opens\n React.useEffect(() => {\n if (open) {\n // Use a small timeout to allow the popover to render\n const timer = setTimeout(() => {\n searchInputRef.current?.focus()\n }, 0)\n return () => clearTimeout(timer)\n }\n }, [open])\n\n const findNextEnabledIndex = React.useCallback(\n (currentIndex: number, direction: 1 | -1): number => {\n const len = filteredOptions.length\n if (len === 0) return -1\n\n let nextIndex = currentIndex + direction\n while (nextIndex >= 0 && nextIndex < len) {\n if (!filteredOptions[nextIndex].disabled) return nextIndex\n nextIndex += direction\n }\n return currentIndex\n },\n [filteredOptions],\n )\n\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent) => {\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault()\n const nextIdx = findNextEnabledIndex(highlightedIndex, 1)\n setHighlightedIndex(nextIdx)\n break\n }\n case 'ArrowUp': {\n e.preventDefault()\n const prevIdx = findNextEnabledIndex(highlightedIndex, -1)\n setHighlightedIndex(prevIdx)\n break\n }\n case 'Home': {\n e.preventDefault()\n const firstEnabled = filteredOptions.findIndex((o) => !o.disabled)\n setHighlightedIndex(firstEnabled)\n break\n }\n case 'End': {\n e.preventDefault()\n let lastEnabled = -1\n for (let i = filteredOptions.length - 1; i >= 0; i--) {\n if (!filteredOptions[i].disabled) {\n lastEnabled = i\n break\n }\n }\n setHighlightedIndex(lastEnabled)\n break\n }\n case 'Enter': {\n e.preventDefault()\n if (\n highlightedIndex >= 0 &&\n highlightedIndex < filteredOptions.length &&\n !filteredOptions[highlightedIndex].disabled\n ) {\n handleSelect(filteredOptions[highlightedIndex].value)\n }\n break\n }\n case 'Escape': {\n e.preventDefault()\n setOpen(false)\n setSearch('')\n setHighlightedIndex(-1)\n break\n }\n }\n },\n [highlightedIndex, filteredOptions, findNextEnabledIndex, handleSelect],\n )\n\n // Scroll highlighted option into view\n React.useEffect(() => {\n if (highlightedIndex >= 0 && listRef.current) {\n const optionEl = listRef.current.children[highlightedIndex] as HTMLElement\n if (optionEl) {\n optionEl.scrollIntoView?.({ block: 'nearest' })\n }\n }\n }, [highlightedIndex])\n\n const getSelectedLabel = React.useCallback(() => {\n if (selectedValues.length === 0) return null\n const option = options.find((o) => o.value === selectedValues[0])\n return option?.label ?? null\n }, [selectedValues, options])\n\n const resolvedIconSize = iconSizeMap[size]\n const resolvedPillClasses = pillSizeMap[size]\n const resolvedOverflowText = overflowTextMap[size]\n\n const renderTriggerContent = () => {\n if (multiple && selectedValues.length > 0) {\n const visiblePills = selectedValues.slice(0, MAX_VISIBLE_PILLS)\n const remaining = selectedValues.length - MAX_VISIBLE_PILLS\n\n return (\n <span className=\"flex flex-1 flex-wrap items-center gap-ds-02 overflow-hidden\">\n {visiblePills.map((val) => {\n const option = options.find((o) => o.value === val)\n if (!option) return null\n return (\n <span\n key={val}\n className={cn('inline-flex items-center gap-ds-01 rounded-control-inner bg-accent-2', resolvedPillClasses)}\n >\n {option.label}\n <button\n type=\"button\"\n className=\"inline-flex items-center justify-center rounded-pill outline-hidden hover:bg-surface-raised-hover transition-colors duration-fast-01 ease-productive-standard\"\n onClick={(e) => handleRemovePill(e, val)}\n aria-label={`Remove ${option.label}`}\n tabIndex={-1}\n >\n <Icon icon={IconX} size={resolvedIconSize} />\n </button>\n </span>\n )\n })}\n {remaining > 0 && (\n <span className={cn('text-surface-fg-muted', resolvedOverflowText)}>\n +{remaining} more\n </span>\n )}\n </span>\n )\n }\n\n if (!multiple && selectedValues.length === 1) {\n const label = getSelectedLabel()\n if (label) {\n return <span className=\"min-w-0 flex-1 truncate text-left\">{label}</span>\n }\n }\n\n return (\n <span className=\"min-w-0 flex-1 truncate text-left text-surface-fg-subtle\">\n {placeholder}\n </span>\n )\n }\n\n const fieldCtx = useFormField()\n const state = resolveFieldState(stateProp, fieldCtx.state)\n const isError = state === 'error'\n const ariaDescribedBy = fieldCtx.helperTextId\n const ariaRequired = fieldCtx.required\n\n return (\n <PopoverPrimitive.Root open={open} onOpenChange={handleOpenChange}>\n <div className={cn('relative', className)} {...rest}>\n <PopoverPrimitive.Trigger asChild disabled={disabled}>\n <button\n ref={ref}\n type=\"button\"\n role=\"combobox\"\n // Explicit id wins; otherwise adopt FormField's inputId so <Label htmlFor> resolves.\n id={externalId ?? fieldCtx.inputId}\n aria-expanded={open}\n aria-controls={listboxId}\n aria-haspopup=\"listbox\"\n // Explicit accessibleLabel wins. Inside a FormField, let the visible <Label>\n // provide the name (via htmlFor); only fall back to placeholder when standalone.\n aria-label={accessibleLabel ?? (fieldCtx.inputId ? undefined : placeholder)}\n aria-invalid={isError || undefined}\n aria-describedby={ariaDescribedBy}\n aria-required={ariaRequired || undefined}\n disabled={disabled}\n className={cn(\n comboboxTriggerVariants({ size }),\n open && 'border-accent-7',\n state && stateBorderClasses[state],\n triggerClassName,\n )}\n >\n {renderTriggerContent()}\n <Icon icon={IconChevronDown} size={resolvedIconSize} className={cn(\"ml-ds-02 shrink-0 opacity-50 transition-transform duration-fast-01 ease-productive-standard\", open && 'rotate-180')} />\n </button>\n </PopoverPrimitive.Trigger>\n\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n asChild\n sideOffset={4}\n align=\"start\"\n onOpenAutoFocus={(e) => {\n e.preventDefault()\n searchInputRef.current?.focus()\n }}\n >\n <motion.div\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ ...springs.snappy, opacity: tweens.fade }}\n className={cn(\n 'z-popover w-[var(--radix-popover-trigger-width)] overflow-hidden rounded-overlay bg-surface-overlay shadow-floating',\n )}\n >\n {/* Search input */}\n <div className=\"flex items-center gap-ds-02 border-b border-surface-border px-ds-04\">\n <Icon icon={IconSearch} size=\"sm\" className=\"shrink-0 text-surface-fg-subtle\" />\n <input\n ref={searchInputRef}\n type=\"text\"\n className=\"flex-1 bg-transparent py-ds-03 text-ds-md outline-hidden placeholder:text-surface-fg-subtle\"\n placeholder={searchPlaceholder}\n value={search}\n onChange={(e) => {\n setSearch(e.target.value)\n setHighlightedIndex(-1)\n }}\n onKeyDown={handleKeyDown}\n aria-autocomplete=\"list\"\n aria-controls={listboxId}\n aria-activedescendant={\n highlightedIndex >= 0\n ? `${optionIdPrefix}-option-${highlightedIndex}`\n : undefined\n }\n aria-label=\"Search options\"\n />\n </div>\n\n {/* Options list */}\n {filteredOptions.length === 0 ? (\n <div className=\"px-ds-04 py-ds-05 text-center text-ds-md text-surface-fg-subtle\">\n {emptyMessage}\n </div>\n ) : (\n <ul\n ref={listRef}\n id={listboxId}\n role=\"listbox\"\n aria-multiselectable={multiple || undefined}\n className=\"overflow-auto p-ds-02\"\n style={{ maxHeight: `${maxVisible * ITEM_HEIGHT_PX}px` }}\n >\n {filteredOptions.map((option, index) => {\n const selected = isSelected(option.value)\n return (\n <li\n key={option.value}\n id={`${optionIdPrefix}-option-${index}`}\n role=\"option\"\n aria-selected={selected}\n aria-disabled={option.disabled || undefined}\n className={cn(\n 'relative flex cursor-pointer select-none items-center gap-ds-03 rounded-control px-ds-04 py-ds-03 text-ds-md outline-hidden',\n 'transition-colors duration-fast-01 ease-productive-standard',\n highlightedIndex === index &&\n 'bg-accent-2',\n selected && 'text-accent-11',\n option.disabled &&\n 'pointer-events-none opacity-action-disabled',\n )}\n onClick={() => {\n if (!option.disabled) {\n handleSelect(option.value)\n }\n }}\n onKeyDown={(e) => {\n if (!option.disabled && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault()\n handleSelect(option.value)\n }\n }}\n onMouseEnter={() => {\n if (!option.disabled) {\n setHighlightedIndex(index)\n }\n }}\n >\n {option.icon && (\n <span className=\"flex h-ico-sm w-ico-sm items-center justify-center shrink-0\">\n <IconProvider size={resolvedIconSize}>\n {normalizeIcon(option.icon)}\n </IconProvider>\n </span>\n )}\n <span className=\"flex flex-1 flex-col\">\n {renderOption ? (\n renderOption(option, selected)\n ) : (\n <>\n <span>{option.label}</span>\n {option.description && (\n <span className=\"text-ds-sm text-surface-fg-muted\">\n {option.description}\n </span>\n )}\n </>\n )}\n </span>\n {selected && (\n <Icon icon={IconCheck} size=\"sm\" className=\"shrink-0\" />\n )}\n </li>\n )\n })}\n </ul>\n )}\n </motion.div>\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n </div>\n </PopoverPrimitive.Root>\n )\n },\n)\nCombobox.displayName = 'Combobox'\n\nexport { Combobox }\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"number-input.d.ts","sourceRoot":"","sources":["../../src/ui/number-input.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,mBAAmB,CAAA;AAGtE,sFAAsF;AACtF,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAA;AAEzC,QAAA,MAAM,0BAA0B;;8EAa/B,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,WAAW,CAAC,YAAY,CAAC,OAAO,0BAA0B,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;AAkClG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;IACjI,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0EAA0E;IAC1E,IAAI,CAAC,EAAE,eAAe,CAAA;IACtB,iDAAiD;IACjD,KAAK,CAAC,EAAE,UAAU,CAAA;CACnB;AAED,QAAA,MAAM,WAAW,2FAmHhB,CAAA;AAGD,OAAO,EAAE,WAAW,EAAE,0BAA0B,EAAE,CAAA"}
1
+ {"version":3,"file":"number-input.d.ts","sourceRoot":"","sources":["../../src/ui/number-input.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,mBAAmB,CAAA;AAGtE,sFAAsF;AACtF,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAA;AAEzC,QAAA,MAAM,0BAA0B;;8EAa/B,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,WAAW,CAAC,YAAY,CAAC,OAAO,0BAA0B,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;AAkClG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;IACjI,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0EAA0E;IAC1E,IAAI,CAAC,EAAE,eAAe,CAAA;IACtB,iDAAiD;IACjD,KAAK,CAAC,EAAE,UAAU,CAAA;CACnB;AAED,QAAA,MAAM,WAAW,2FAqHhB,CAAA;AAGD,OAAO,EAAE,WAAW,EAAE,0BAA0B,EAAE,CAAA"}
@@ -82,7 +82,8 @@ var u = e("flex items-center justify-between rounded-control border border-surfa
82
82
  "aria-invalid": O === "error" || void 0,
83
83
  "aria-describedby": C["aria-describedby"] ?? E.helperTextId,
84
84
  className: t("bg-transparent font-semibold border-0 text-center text-surface-fg-muted focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none", P),
85
- ...C
85
+ ...C,
86
+ id: C.id ?? E.inputId
86
87
  }),
87
88
  /* @__PURE__ */ o("button", {
88
89
  type: "button",
@@ -1 +1 @@
1
- {"version":3,"file":"number-input.js","names":[],"sources":["../../src/ui/number-input.tsx"],"sourcesContent":["'use client'\n\nimport { IconMinus, IconPlus } from '@tabler/icons-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport * as React from 'react'\n\nimport { useFormField } from './form'\nimport { Icon } from './icon'\nimport type { IconSize } from './icon-context'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport { cn } from './lib/utils'\n\n/** @deprecated Use `FieldState` — the shared control-state type. Kept as an alias. */\nexport type NumberInputState = FieldState\n\nconst numberInputWrapperVariants = cva(\n 'flex items-center justify-between rounded-control border border-surface-border-strong',\n {\n variants: {\n size: {\n xs: 'h-ds-xs-plus',\n sm: 'h-ds-sm',\n md: 'h-ds-md',\n lg: 'h-ds-lg',\n },\n },\n defaultVariants: { size: 'md' },\n },\n)\n\nexport type NumberInputSize = NonNullable<VariantProps<typeof numberInputWrapperVariants>['size']>\n\n/** Maps size to stepper button dimensions */\nconst buttonSizeMap: Record<NonNullable<NumberInputSize>, string> = {\n xs: 'h-[22px] w-[22px]',\n sm: 'h-ds-sm w-ds-sm',\n md: 'h-ds-sm w-ds-sm',\n lg: 'h-ds-md w-ds-md',\n}\n\n/** Maps size to Icon component size */\nconst iconSizeMap: Record<NonNullable<NumberInputSize>, IconSize> = {\n xs: 'xs',\n sm: 'sm',\n md: 'sm',\n lg: 'md',\n}\n\n/** Maps size to input text and width classes */\nconst inputSizeMap: Record<NonNullable<NumberInputSize>, string> = {\n xs: 'text-ds-sm w-ds-06b',\n sm: 'text-ds-sm w-ds-sm-plus',\n md: 'text-ds-md w-ds-sm-plus',\n lg: 'text-ds-md w-ds-md',\n}\n\n/** Maps state to border color classes */\nconst stateColorMap: Record<NonNullable<NumberInputState>, string> = {\n default: '',\n error: 'border-error-7',\n warning: 'border-warning-7',\n success: 'border-success-7',\n}\n\n/**\n * Props for NumberInput — a stepper control with \"−\" and \"+\" buttons flanking a numeric input,\n * clamped between `min` and `max`. The decrement/increment buttons are disabled when bounds are reached.\n *\n * **Controlled only:** Pass `value` + `onValueChange` for controlled usage. Uncontrolled usage is\n * possible but the buttons won't update the displayed value without `onValueChange`.\n *\n * **Step:** The `step` prop controls how much each button press increments/decrements (default 1).\n * Direct text input is also clamped to `[min, max]` on change.\n *\n * **Sizes:** `xs` (28px) | `sm` (32px) | `md` (40px, default) | `lg` (48px)\n *\n * **Validation states:** `state=\"error\"` colors the border red.\n * Use with `<FormField>` to show helper text below the input.\n *\n * @example\n * // Quantity selector with 1–99 range:\n * <NumberInput value={qty} onValueChange={setQty} min={1} max={99} />\n *\n * @example\n * // Rating input (1–10, step 1):\n * <NumberInput value={rating} onValueChange={setRating} min={1} max={10} />\n *\n * @example\n * // Fine-grained opacity control (0–100, step 5):\n * <NumberInput value={opacity} onValueChange={setOpacity} min={0} max={100} step={5} />\n *\n * @example\n * // Disabled number display (read-only-like):\n * <NumberInput value={autoCalcValue} onValueChange={() => {}} disabled />\n * // These are just a few ways — feel free to combine props creatively!\n */\nexport interface NumberInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'type' | 'size'> {\n value?: number\n onValueChange?: (value: number) => void\n min?: number\n max?: number\n step?: number\n /** Size of the number input. Controls height, button sizes, text size. */\n size?: NumberInputSize\n /** Validation state controlling border color. */\n state?: FieldState\n}\n\nconst NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(\n (\n {\n value = 0,\n onValueChange,\n min = Number.MIN_SAFE_INTEGER,\n max = Number.MAX_SAFE_INTEGER,\n step = 1,\n disabled = false,\n className,\n size: sizeProp = 'md',\n state: stateProp,\n 'aria-label': ariaLabelProp,\n ...rest\n },\n ref,\n ) => {\n const size = sizeProp ?? 'md'\n const fieldCtx = useFormField()\n // If no explicit aria-label and not inside a FormField (no id to associate with Label),\n // provide a sensible default\n const ariaLabel = ariaLabelProp ?? (rest.id || fieldCtx.helperTextId ? undefined : 'Numeric value')\n\n // Merge FormField context — explicit props always win (shared precedence)\n const state = resolveFieldState(stateProp, fieldCtx.state) ?? 'default'\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const raw = e.target.value.trim()\n if (raw === '' || raw === '-') {\n onValueChange?.(min >= 0 ? min : 0)\n return\n }\n const parsed = Number(raw)\n if (Number.isNaN(parsed)) return\n const clamped = Math.min(Math.max(parsed, min), max)\n onValueChange?.(clamped)\n }\n\n const handleIncrement = (e: React.MouseEvent<HTMLButtonElement>) => {\n e.preventDefault() // Prevent form submission\n const newValue = value + step\n if (newValue <= max) {\n onValueChange?.(newValue)\n }\n }\n\n const handleDecrement = (e: React.MouseEvent<HTMLButtonElement>) => {\n e.preventDefault() // Prevent form submission\n const newValue = value - step\n if (newValue >= min) {\n onValueChange?.(newValue)\n }\n }\n\n const resolvedButtonSize = buttonSizeMap[size]\n const resolvedIconSize = iconSizeMap[size]\n const resolvedInputSize = inputSizeMap[size]\n\n return (\n <div\n className={cn(\n numberInputWrapperVariants({ size }),\n stateColorMap[state],\n className,\n )}\n >\n <button\n type=\"button\"\n onClick={handleDecrement}\n disabled={disabled || value <= min}\n aria-label=\"Decrease value\"\n title=\"Decrease\"\n className={cn(\n 'flex items-center justify-center border-0 rounded-control-inner text-surface-fg-subtle hover:bg-surface-raised-hover hover:text-surface-fg-muted active:scale-90 transition-[color,background-color,transform] duration-fast-01 ease-productive-standard disabled:opacity-action-disabled disabled:pointer-events-none',\n resolvedButtonSize,\n )}\n >\n <Icon icon={IconMinus} size={resolvedIconSize} />\n </button>\n\n <input\n ref={ref}\n type=\"number\"\n value={value}\n onChange={handleInputChange}\n min={min}\n max={max}\n step={step}\n disabled={disabled}\n aria-label={ariaLabel}\n aria-invalid={state === 'error' || undefined}\n aria-describedby={rest['aria-describedby'] ?? fieldCtx.helperTextId}\n className={cn(\n 'bg-transparent font-semibold border-0 text-center text-surface-fg-muted focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none',\n resolvedInputSize,\n )}\n {...rest}\n />\n\n <button\n type=\"button\"\n onClick={handleIncrement}\n disabled={disabled || value >= max}\n aria-label=\"Increase value\"\n title=\"Increase\"\n className={cn(\n 'flex items-center justify-center border-0 rounded-control-inner text-surface-fg-subtle hover:bg-surface-raised-hover hover:text-surface-fg-muted active:scale-90 transition-[color,background-color,transform] duration-fast-01 ease-productive-standard disabled:opacity-action-disabled disabled:pointer-events-none',\n resolvedButtonSize,\n )}\n >\n <Icon icon={IconPlus} size={resolvedIconSize} />\n </button>\n </div>\n )\n },\n)\nNumberInput.displayName = 'NumberInput'\n\nexport { NumberInput, numberInputWrapperVariants }\n"],"mappings":";;;;;;;;;;AAeA,IAAM,IAA6B,EACjC,yFACA;CACE,UAAU,EACR,MAAM;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN,EACF;CACA,iBAAiB,EAAE,MAAM,KAAK;AAChC,CACF,GAKM,IAA8D;CAClE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN,GAGM,IAA8D;CAClE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN,GAGM,IAA6D;CACjE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN,GAGM,IAA+D;CACnE,SAAS;CACT,OAAO;CACP,SAAS;CACT,SAAS;AACX,GA8CM,IAAc,EAAM,YAEtB,EACE,WAAQ,GACR,kBACA,SAAM,gBACN,sBACA,UAAO,GACP,cAAW,IACX,cACA,MAAM,IAAW,MACjB,OAAO,GACP,cAAc,GACd,GAAG,KAEL,MACG;CACH,IAAM,IAAO,KAAY,MACnB,IAAW,EAAa,GAGxB,IAAY,MAAkB,EAAK,MAAM,EAAS,eAAe,KAAA,IAAY,kBAG7E,IAAQ,EAAkB,GAAW,EAAS,KAAK,KAAK,WAExD,KAAqB,MAA2C;EACpE,IAAM,IAAM,EAAE,OAAO,MAAM,KAAK;EAChC,IAAI,MAAQ,MAAM,MAAQ,KAAK;GAC7B,IAAgB,KAAO,IAAI,IAAM,CAAC;GAClC;EACF;EACA,IAAM,IAAS,OAAO,CAAG;EACrB,OAAO,MAAM,CAAM,KAEvB,IADgB,KAAK,IAAI,KAAK,IAAI,GAAQ,CAAG,GAAG,CAChC,CAAO;CACzB,GAEM,KAAmB,MAA2C;EAClE,EAAE,eAAe;EACjB,IAAM,IAAW,IAAQ;EACzB,AAAI,KAAY,KACd,IAAgB,CAAQ;CAE5B,GAEM,KAAmB,MAA2C;EAClE,EAAE,eAAe;EACjB,IAAM,IAAW,IAAQ;EACzB,AAAI,KAAY,KACd,IAAgB,CAAQ;CAE5B,GAEM,IAAqB,EAAc,IACnC,IAAmB,EAAY,IAC/B,IAAoB,EAAa;CAEvC,OACE,kBAAC,OAAD;EACE,WAAW,EACT,EAA2B,EAAE,QAAK,CAAC,GACnC,EAAc,IACd,CACF;YALF;GAOE,kBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,UAAU,KAAY,KAAS;IAC/B,cAAW;IACX,OAAM;IACN,WAAW,EACT,0TACA,CACF;cAEA,kBAAC,GAAD;KAAM,MAAM;KAAW,MAAM;IAAmB,CAAA;GAC1C,CAAA;GAER,kBAAC,SAAD;IACO;IACL,MAAK;IACE;IACP,UAAU;IACL;IACA;IACC;IACI;IACV,cAAY;IACZ,gBAAc,MAAU,WAAW,KAAA;IACnC,oBAAkB,EAAK,uBAAuB,EAAS;IACvD,WAAW,EACT,8QACA,CACF;IACA,GAAI;GACL,CAAA;GAED,kBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,UAAU,KAAY,KAAS;IAC/B,cAAW;IACX,OAAM;IACN,WAAW,EACT,0TACA,CACF;cAEA,kBAAC,GAAD;KAAM,MAAM;KAAU,MAAM;IAAmB,CAAA;GACzC,CAAA;EACL;;AAET,CACF;AACA,EAAY,cAAc"}
1
+ {"version":3,"file":"number-input.js","names":[],"sources":["../../src/ui/number-input.tsx"],"sourcesContent":["'use client'\n\nimport { IconMinus, IconPlus } from '@tabler/icons-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport * as React from 'react'\n\nimport { useFormField } from './form'\nimport { Icon } from './icon'\nimport type { IconSize } from './icon-context'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport { cn } from './lib/utils'\n\n/** @deprecated Use `FieldState` — the shared control-state type. Kept as an alias. */\nexport type NumberInputState = FieldState\n\nconst numberInputWrapperVariants = cva(\n 'flex items-center justify-between rounded-control border border-surface-border-strong',\n {\n variants: {\n size: {\n xs: 'h-ds-xs-plus',\n sm: 'h-ds-sm',\n md: 'h-ds-md',\n lg: 'h-ds-lg',\n },\n },\n defaultVariants: { size: 'md' },\n },\n)\n\nexport type NumberInputSize = NonNullable<VariantProps<typeof numberInputWrapperVariants>['size']>\n\n/** Maps size to stepper button dimensions */\nconst buttonSizeMap: Record<NonNullable<NumberInputSize>, string> = {\n xs: 'h-[22px] w-[22px]',\n sm: 'h-ds-sm w-ds-sm',\n md: 'h-ds-sm w-ds-sm',\n lg: 'h-ds-md w-ds-md',\n}\n\n/** Maps size to Icon component size */\nconst iconSizeMap: Record<NonNullable<NumberInputSize>, IconSize> = {\n xs: 'xs',\n sm: 'sm',\n md: 'sm',\n lg: 'md',\n}\n\n/** Maps size to input text and width classes */\nconst inputSizeMap: Record<NonNullable<NumberInputSize>, string> = {\n xs: 'text-ds-sm w-ds-06b',\n sm: 'text-ds-sm w-ds-sm-plus',\n md: 'text-ds-md w-ds-sm-plus',\n lg: 'text-ds-md w-ds-md',\n}\n\n/** Maps state to border color classes */\nconst stateColorMap: Record<NonNullable<NumberInputState>, string> = {\n default: '',\n error: 'border-error-7',\n warning: 'border-warning-7',\n success: 'border-success-7',\n}\n\n/**\n * Props for NumberInput — a stepper control with \"−\" and \"+\" buttons flanking a numeric input,\n * clamped between `min` and `max`. The decrement/increment buttons are disabled when bounds are reached.\n *\n * **Controlled only:** Pass `value` + `onValueChange` for controlled usage. Uncontrolled usage is\n * possible but the buttons won't update the displayed value without `onValueChange`.\n *\n * **Step:** The `step` prop controls how much each button press increments/decrements (default 1).\n * Direct text input is also clamped to `[min, max]` on change.\n *\n * **Sizes:** `xs` (28px) | `sm` (32px) | `md` (40px, default) | `lg` (48px)\n *\n * **Validation states:** `state=\"error\"` colors the border red.\n * Use with `<FormField>` to show helper text below the input.\n *\n * @example\n * // Quantity selector with 1–99 range:\n * <NumberInput value={qty} onValueChange={setQty} min={1} max={99} />\n *\n * @example\n * // Rating input (1–10, step 1):\n * <NumberInput value={rating} onValueChange={setRating} min={1} max={10} />\n *\n * @example\n * // Fine-grained opacity control (0–100, step 5):\n * <NumberInput value={opacity} onValueChange={setOpacity} min={0} max={100} step={5} />\n *\n * @example\n * // Disabled number display (read-only-like):\n * <NumberInput value={autoCalcValue} onValueChange={() => {}} disabled />\n * // These are just a few ways — feel free to combine props creatively!\n */\nexport interface NumberInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'type' | 'size'> {\n value?: number\n onValueChange?: (value: number) => void\n min?: number\n max?: number\n step?: number\n /** Size of the number input. Controls height, button sizes, text size. */\n size?: NumberInputSize\n /** Validation state controlling border color. */\n state?: FieldState\n}\n\nconst NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(\n (\n {\n value = 0,\n onValueChange,\n min = Number.MIN_SAFE_INTEGER,\n max = Number.MAX_SAFE_INTEGER,\n step = 1,\n disabled = false,\n className,\n size: sizeProp = 'md',\n state: stateProp,\n 'aria-label': ariaLabelProp,\n ...rest\n },\n ref,\n ) => {\n const size = sizeProp ?? 'md'\n const fieldCtx = useFormField()\n // If no explicit aria-label and not inside a FormField (no id to associate with Label),\n // provide a sensible default\n const ariaLabel = ariaLabelProp ?? (rest.id || fieldCtx.helperTextId ? undefined : 'Numeric value')\n\n // Merge FormField context — explicit props always win (shared precedence)\n const state = resolveFieldState(stateProp, fieldCtx.state) ?? 'default'\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const raw = e.target.value.trim()\n if (raw === '' || raw === '-') {\n onValueChange?.(min >= 0 ? min : 0)\n return\n }\n const parsed = Number(raw)\n if (Number.isNaN(parsed)) return\n const clamped = Math.min(Math.max(parsed, min), max)\n onValueChange?.(clamped)\n }\n\n const handleIncrement = (e: React.MouseEvent<HTMLButtonElement>) => {\n e.preventDefault() // Prevent form submission\n const newValue = value + step\n if (newValue <= max) {\n onValueChange?.(newValue)\n }\n }\n\n const handleDecrement = (e: React.MouseEvent<HTMLButtonElement>) => {\n e.preventDefault() // Prevent form submission\n const newValue = value - step\n if (newValue >= min) {\n onValueChange?.(newValue)\n }\n }\n\n const resolvedButtonSize = buttonSizeMap[size]\n const resolvedIconSize = iconSizeMap[size]\n const resolvedInputSize = inputSizeMap[size]\n\n return (\n <div\n className={cn(\n numberInputWrapperVariants({ size }),\n stateColorMap[state],\n className,\n )}\n >\n <button\n type=\"button\"\n onClick={handleDecrement}\n disabled={disabled || value <= min}\n aria-label=\"Decrease value\"\n title=\"Decrease\"\n className={cn(\n 'flex items-center justify-center border-0 rounded-control-inner text-surface-fg-subtle hover:bg-surface-raised-hover hover:text-surface-fg-muted active:scale-90 transition-[color,background-color,transform] duration-fast-01 ease-productive-standard disabled:opacity-action-disabled disabled:pointer-events-none',\n resolvedButtonSize,\n )}\n >\n <Icon icon={IconMinus} size={resolvedIconSize} />\n </button>\n\n <input\n ref={ref}\n type=\"number\"\n value={value}\n onChange={handleInputChange}\n min={min}\n max={max}\n step={step}\n disabled={disabled}\n aria-label={ariaLabel}\n aria-invalid={state === 'error' || undefined}\n aria-describedby={rest['aria-describedby'] ?? fieldCtx.helperTextId}\n className={cn(\n 'bg-transparent font-semibold border-0 text-center text-surface-fg-muted focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none',\n resolvedInputSize,\n )}\n {...rest}\n // Explicit id wins; otherwise adopt FormField's inputId so <Label htmlFor> resolves.\n id={rest.id ?? fieldCtx.inputId}\n />\n\n <button\n type=\"button\"\n onClick={handleIncrement}\n disabled={disabled || value >= max}\n aria-label=\"Increase value\"\n title=\"Increase\"\n className={cn(\n 'flex items-center justify-center border-0 rounded-control-inner text-surface-fg-subtle hover:bg-surface-raised-hover hover:text-surface-fg-muted active:scale-90 transition-[color,background-color,transform] duration-fast-01 ease-productive-standard disabled:opacity-action-disabled disabled:pointer-events-none',\n resolvedButtonSize,\n )}\n >\n <Icon icon={IconPlus} size={resolvedIconSize} />\n </button>\n </div>\n )\n },\n)\nNumberInput.displayName = 'NumberInput'\n\nexport { NumberInput, numberInputWrapperVariants }\n"],"mappings":";;;;;;;;;;AAeA,IAAM,IAA6B,EACjC,yFACA;CACE,UAAU,EACR,MAAM;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN,EACF;CACA,iBAAiB,EAAE,MAAM,KAAK;AAChC,CACF,GAKM,IAA8D;CAClE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN,GAGM,IAA8D;CAClE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN,GAGM,IAA6D;CACjE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN,GAGM,IAA+D;CACnE,SAAS;CACT,OAAO;CACP,SAAS;CACT,SAAS;AACX,GA8CM,IAAc,EAAM,YAEtB,EACE,WAAQ,GACR,kBACA,SAAM,gBACN,sBACA,UAAO,GACP,cAAW,IACX,cACA,MAAM,IAAW,MACjB,OAAO,GACP,cAAc,GACd,GAAG,KAEL,MACG;CACH,IAAM,IAAO,KAAY,MACnB,IAAW,EAAa,GAGxB,IAAY,MAAkB,EAAK,MAAM,EAAS,eAAe,KAAA,IAAY,kBAG7E,IAAQ,EAAkB,GAAW,EAAS,KAAK,KAAK,WAExD,KAAqB,MAA2C;EACpE,IAAM,IAAM,EAAE,OAAO,MAAM,KAAK;EAChC,IAAI,MAAQ,MAAM,MAAQ,KAAK;GAC7B,IAAgB,KAAO,IAAI,IAAM,CAAC;GAClC;EACF;EACA,IAAM,IAAS,OAAO,CAAG;EACrB,OAAO,MAAM,CAAM,KAEvB,IADgB,KAAK,IAAI,KAAK,IAAI,GAAQ,CAAG,GAAG,CAChC,CAAO;CACzB,GAEM,KAAmB,MAA2C;EAClE,EAAE,eAAe;EACjB,IAAM,IAAW,IAAQ;EACzB,AAAI,KAAY,KACd,IAAgB,CAAQ;CAE5B,GAEM,KAAmB,MAA2C;EAClE,EAAE,eAAe;EACjB,IAAM,IAAW,IAAQ;EACzB,AAAI,KAAY,KACd,IAAgB,CAAQ;CAE5B,GAEM,IAAqB,EAAc,IACnC,IAAmB,EAAY,IAC/B,IAAoB,EAAa;CAEvC,OACE,kBAAC,OAAD;EACE,WAAW,EACT,EAA2B,EAAE,QAAK,CAAC,GACnC,EAAc,IACd,CACF;YALF;GAOE,kBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,UAAU,KAAY,KAAS;IAC/B,cAAW;IACX,OAAM;IACN,WAAW,EACT,0TACA,CACF;cAEA,kBAAC,GAAD;KAAM,MAAM;KAAW,MAAM;IAAmB,CAAA;GAC1C,CAAA;GAER,kBAAC,SAAD;IACO;IACL,MAAK;IACE;IACP,UAAU;IACL;IACA;IACC;IACI;IACV,cAAY;IACZ,gBAAc,MAAU,WAAW,KAAA;IACnC,oBAAkB,EAAK,uBAAuB,EAAS;IACvD,WAAW,EACT,8QACA,CACF;IACA,GAAI;IAEJ,IAAI,EAAK,MAAM,EAAS;GACzB,CAAA;GAED,kBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,UAAU,KAAY,KAAS;IAC/B,cAAW;IACX,OAAM;IACN,WAAW,EACT,0TACA,CACF;cAEA,kBAAC,GAAD;KAAM,MAAM;KAAU,MAAM;IAAmB,CAAA;GACzC,CAAA;EACL;;AAET,CACF;AACA,EAAY,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../src/ui/select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,eAAe,MAAM,0BAA0B,CAAA;AAE3D,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,QAAA,MAAM,MAAM,uCAAuB,CAAA;AAEnC,QAAA,MAAM,WAAW,yGAAwB,CAAA;AAEzC,QAAA,MAAM,WAAW,0GAAwB,CAAA;AAEzC,eAAO,MAAM,qBAAqB;;;;8EA2BjC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,kBACf,SAAQ,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,OAAO,eAAe,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,EACnF,YAAY,CAAC,OAAO,qBAAqB,CAAC;CAAG;AAEjD,QAAA,MAAM,aAAa,8FAyBjB,CAAA;AAGF,QAAA,MAAM,oBAAoB,qKAcxB,CAAA;AAGF,QAAA,MAAM,sBAAsB,uKAc1B,CAAA;AAIF,QAAA,MAAM,aAAa,8JAoCjB,CAAA;AAGF,QAAA,MAAM,WAAW,4JASf,CAAA;AAGF,QAAA,MAAM,UAAU,2JAmBd,CAAA;AAGF,QAAA,MAAM,eAAe,gKASnB,CAAA;AAGF,OAAO,EACL,MAAM,EACN,aAAa,EACb,WAAW,EACX,UAAU,EACV,WAAW,EACX,sBAAsB,EACtB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,WAAW,GACZ,CAAA"}
1
+ {"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../src/ui/select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,eAAe,MAAM,0BAA0B,CAAA;AAE3D,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,QAAA,MAAM,MAAM,uCAAuB,CAAA;AAEnC,QAAA,MAAM,WAAW,yGAAwB,CAAA;AAEzC,QAAA,MAAM,WAAW,0GAAwB,CAAA;AAEzC,eAAO,MAAM,qBAAqB;;;;8EA2BjC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,kBACf,SAAQ,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,OAAO,eAAe,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,EACnF,YAAY,CAAC,OAAO,qBAAqB,CAAC;CAAG;AAEjD,QAAA,MAAM,aAAa,8FA2BjB,CAAA;AAGF,QAAA,MAAM,oBAAoB,qKAcxB,CAAA;AAGF,QAAA,MAAM,sBAAsB,uKAc1B,CAAA;AAIF,QAAA,MAAM,aAAa,8JAoCjB,CAAA;AAGF,QAAA,MAAM,WAAW,4JASf,CAAA;AAGF,QAAA,MAAM,UAAU,2JAmBd,CAAA;AAGF,QAAA,MAAM,eAAe,gKASnB,CAAA;AAGF,OAAO,EACL,MAAM,EACN,aAAa,EACb,WAAW,EACX,UAAU,EACV,WAAW,EACX,sBAAsB,EACtB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,WAAW,GACZ,CAAA"}
package/dist/ui/select.js CHANGED
@@ -49,6 +49,7 @@ var k = c, A = t, j = p, M = _("flex w-full items-center justify-between whitesp
49
49
  "aria-describedby": u,
50
50
  "aria-required": d || void 0,
51
51
  ...o,
52
+ id: o.id ?? c.inputId,
52
53
  children: [t, /* @__PURE__ */ C(n, {
53
54
  asChild: !0,
54
55
  children: /* @__PURE__ */ C(y, {
@@ -1 +1 @@
1
- {"version":3,"file":"select.js","names":[],"sources":["../../src/ui/select.tsx"],"sourcesContent":["'use client'\n\nimport * as SelectPrimitive from '@primitives/react-select'\nimport { IconCheck, IconChevronDown, IconChevronUp } from '@tabler/icons-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useFormField } from './form'\nimport { Icon } from './icon'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\n\n/**\n * Select root — manages open/close state and selected value.\n *\n * **Important:** `size` is NOT a prop on `Select`. Set it on `SelectTrigger` instead.\n * Passing `size` directly to `Select` produces no TypeScript error but has no effect.\n *\n * @example\n * // CORRECT — size goes on SelectTrigger:\n * <Select onValueChange={setValue}>\n * <SelectTrigger size=\"lg\">\n * <SelectValue placeholder=\"Choose...\" />\n * </SelectTrigger>\n * <SelectContent>\n * <SelectItem value=\"a\">Option A</SelectItem>\n * <SelectItem value=\"b\">Option B</SelectItem>\n * </SelectContent>\n * </Select>\n *\n * // WRONG — size on Select root is silently ignored (no TypeScript error):\n * // <Select size=\"lg\">...</Select>\n */\nconst Select = SelectPrimitive.Root\n\nconst SelectGroup = SelectPrimitive.Group\n\nconst SelectValue = SelectPrimitive.Value\n\nexport const selectTriggerVariants = cva(\n 'flex w-full items-center justify-between whitespace-nowrap rounded-control placeholder:text-surface-fg-subtle focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-action-disabled [&>span]:line-clamp-1 [&>span]:min-w-0',\n {\n variants: {\n variant: {\n default:\n 'border border-surface-border-strong bg-surface-raised-hover focus-visible:border-accent-7',\n outline:\n 'border border-surface-border-strong bg-transparent focus-visible:border-accent-7',\n ghost:\n 'border border-transparent bg-transparent hover:bg-surface-raised-hover focus-visible:border-accent-7',\n },\n state: {\n default: '',\n error: 'border-error-7 text-error-11 focus-visible:ring-error-9',\n success: 'border-success-7',\n warning: 'border-warning-7',\n },\n size: {\n xs: 'h-ds-xs-plus text-ds-sm px-ds-02',\n sm: 'h-ds-sm text-ds-sm px-ds-03',\n md: 'h-ds-md text-ds-md px-ds-04',\n lg: 'h-ds-lg text-ds-md px-ds-05',\n },\n },\n defaultVariants: { variant: 'default', state: 'default', size: 'md' },\n },\n)\n\n/**\n * Props for SelectTrigger. Use `size` here (not on the `Select` root).\n *\n * @example\n * <SelectTrigger size=\"lg\" className=\"w-[200px]\">\n * <SelectValue placeholder=\"Select an option\" />\n * </SelectTrigger>\n */\nexport interface SelectTriggerProps\n extends Omit<React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>, 'color'>,\n VariantProps<typeof selectTriggerVariants> {}\n\nconst SelectTrigger = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Trigger>,\n SelectTriggerProps\n>(({ className, children, variant, state: stateProp, size, ...props }, ref) => {\n const fieldCtx = useFormField()\n // Explicit `state` prop wins over FormField context (shared precedence).\n const state = resolveFieldState((stateProp ?? undefined) as FieldState | undefined, fieldCtx.state)\n const ariaDescribedBy = props['aria-describedby'] ?? fieldCtx.helperTextId\n const ariaRequired = props['aria-required'] ?? fieldCtx.required\n\n return (\n <SelectPrimitive.Trigger\n ref={ref}\n className={cn(selectTriggerVariants({ variant, state: state ?? 'default', size }), className)}\n aria-invalid={state === 'error' || undefined}\n aria-describedby={ariaDescribedBy}\n aria-required={ariaRequired || undefined}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <Icon icon={IconChevronDown} size=\"sm\" className=\"opacity-50\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n )\n})\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName\n\nconst SelectScrollUpButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollUpButton\n ref={ref}\n className={cn(\n 'flex cursor-default items-center justify-center py-ds-02',\n className,\n )}\n {...props}\n >\n <Icon icon={IconChevronUp} size=\"sm\" />\n </SelectPrimitive.ScrollUpButton>\n))\nSelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName\n\nconst SelectScrollDownButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollDownButton\n ref={ref}\n className={cn(\n 'flex cursor-default items-center justify-center py-ds-02',\n className,\n )}\n {...props}\n >\n <Icon icon={IconChevronDown} size=\"sm\" />\n </SelectPrimitive.ScrollDownButton>\n))\nSelectScrollDownButton.displayName =\n SelectPrimitive.ScrollDownButton.displayName\n\nconst SelectContent = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>\n>(({ className, children, position = 'popper', ...props }, ref) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n position={position}\n asChild\n {...props}\n >\n <motion.div\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ ...springs.snappy, opacity: tweens.fade }}\n className={cn(\n 'relative z-popover max-h-96 min-w-[8rem] overflow-hidden rounded-overlay bg-surface-overlay text-surface-fg shadow-floating',\n position === 'popper' &&\n 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',\n className,\n )}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n 'p-ds-02',\n position === 'popper' &&\n 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]',\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </motion.div>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n))\nSelectContent.displayName = SelectPrimitive.Content.displayName\n\nconst SelectLabel = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Label\n ref={ref}\n className={cn('px-ds-03 py-ds-02b text-ds-md font-semibold', className)}\n {...props}\n />\n))\nSelectLabel.displayName = SelectPrimitive.Label.displayName\n\nconst SelectItem = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n 'relative flex w-full cursor-default select-none items-center rounded-control py-ds-02b pl-ds-03 pr-ds-07 text-ds-md outline-hidden transition-colors duration-fast-01 ease-productive-standard hover:bg-surface-raised focus:bg-surface-raised focus:text-surface-fg data-[disabled]:pointer-events-none data-[disabled]:opacity-action-disabled',\n className,\n )}\n {...props}\n >\n <span className=\"absolute right-ds-03 flex h-ico-sm w-ico-sm items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <Icon icon={IconCheck} size=\"sm\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText className=\"min-w-0 line-clamp-1\">{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n))\nSelectItem.displayName = SelectPrimitive.Item.displayName\n\nconst SelectSeparator = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Separator\n ref={ref}\n className={cn('-mx-ds-01 my-ds-02 h-px bg-surface-border', className)}\n {...props}\n />\n))\nSelectSeparator.displayName = SelectPrimitive.Separator.displayName\n\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"select.js","names":[],"sources":["../../src/ui/select.tsx"],"sourcesContent":["'use client'\n\nimport * as SelectPrimitive from '@primitives/react-select'\nimport { IconCheck, IconChevronDown, IconChevronUp } from '@tabler/icons-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useFormField } from './form'\nimport { Icon } from './icon'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\n\n/**\n * Select root — manages open/close state and selected value.\n *\n * **Important:** `size` is NOT a prop on `Select`. Set it on `SelectTrigger` instead.\n * Passing `size` directly to `Select` produces no TypeScript error but has no effect.\n *\n * @example\n * // CORRECT — size goes on SelectTrigger:\n * <Select onValueChange={setValue}>\n * <SelectTrigger size=\"lg\">\n * <SelectValue placeholder=\"Choose...\" />\n * </SelectTrigger>\n * <SelectContent>\n * <SelectItem value=\"a\">Option A</SelectItem>\n * <SelectItem value=\"b\">Option B</SelectItem>\n * </SelectContent>\n * </Select>\n *\n * // WRONG — size on Select root is silently ignored (no TypeScript error):\n * // <Select size=\"lg\">...</Select>\n */\nconst Select = SelectPrimitive.Root\n\nconst SelectGroup = SelectPrimitive.Group\n\nconst SelectValue = SelectPrimitive.Value\n\nexport const selectTriggerVariants = cva(\n 'flex w-full items-center justify-between whitespace-nowrap rounded-control placeholder:text-surface-fg-subtle focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-action-disabled [&>span]:line-clamp-1 [&>span]:min-w-0',\n {\n variants: {\n variant: {\n default:\n 'border border-surface-border-strong bg-surface-raised-hover focus-visible:border-accent-7',\n outline:\n 'border border-surface-border-strong bg-transparent focus-visible:border-accent-7',\n ghost:\n 'border border-transparent bg-transparent hover:bg-surface-raised-hover focus-visible:border-accent-7',\n },\n state: {\n default: '',\n error: 'border-error-7 text-error-11 focus-visible:ring-error-9',\n success: 'border-success-7',\n warning: 'border-warning-7',\n },\n size: {\n xs: 'h-ds-xs-plus text-ds-sm px-ds-02',\n sm: 'h-ds-sm text-ds-sm px-ds-03',\n md: 'h-ds-md text-ds-md px-ds-04',\n lg: 'h-ds-lg text-ds-md px-ds-05',\n },\n },\n defaultVariants: { variant: 'default', state: 'default', size: 'md' },\n },\n)\n\n/**\n * Props for SelectTrigger. Use `size` here (not on the `Select` root).\n *\n * @example\n * <SelectTrigger size=\"lg\" className=\"w-[200px]\">\n * <SelectValue placeholder=\"Select an option\" />\n * </SelectTrigger>\n */\nexport interface SelectTriggerProps\n extends Omit<React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>, 'color'>,\n VariantProps<typeof selectTriggerVariants> {}\n\nconst SelectTrigger = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Trigger>,\n SelectTriggerProps\n>(({ className, children, variant, state: stateProp, size, ...props }, ref) => {\n const fieldCtx = useFormField()\n // Explicit `state` prop wins over FormField context (shared precedence).\n const state = resolveFieldState((stateProp ?? undefined) as FieldState | undefined, fieldCtx.state)\n const ariaDescribedBy = props['aria-describedby'] ?? fieldCtx.helperTextId\n const ariaRequired = props['aria-required'] ?? fieldCtx.required\n\n return (\n <SelectPrimitive.Trigger\n ref={ref}\n className={cn(selectTriggerVariants({ variant, state: state ?? 'default', size }), className)}\n aria-invalid={state === 'error' || undefined}\n aria-describedby={ariaDescribedBy}\n aria-required={ariaRequired || undefined}\n {...props}\n // Explicit id wins; otherwise adopt FormField's inputId so <Label htmlFor> resolves.\n id={props.id ?? fieldCtx.inputId}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <Icon icon={IconChevronDown} size=\"sm\" className=\"opacity-50\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n )\n})\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName\n\nconst SelectScrollUpButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollUpButton\n ref={ref}\n className={cn(\n 'flex cursor-default items-center justify-center py-ds-02',\n className,\n )}\n {...props}\n >\n <Icon icon={IconChevronUp} size=\"sm\" />\n </SelectPrimitive.ScrollUpButton>\n))\nSelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName\n\nconst SelectScrollDownButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollDownButton\n ref={ref}\n className={cn(\n 'flex cursor-default items-center justify-center py-ds-02',\n className,\n )}\n {...props}\n >\n <Icon icon={IconChevronDown} size=\"sm\" />\n </SelectPrimitive.ScrollDownButton>\n))\nSelectScrollDownButton.displayName =\n SelectPrimitive.ScrollDownButton.displayName\n\nconst SelectContent = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>\n>(({ className, children, position = 'popper', ...props }, ref) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n position={position}\n asChild\n {...props}\n >\n <motion.div\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ ...springs.snappy, opacity: tweens.fade }}\n className={cn(\n 'relative z-popover max-h-96 min-w-[8rem] overflow-hidden rounded-overlay bg-surface-overlay text-surface-fg shadow-floating',\n position === 'popper' &&\n 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',\n className,\n )}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n 'p-ds-02',\n position === 'popper' &&\n 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]',\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </motion.div>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n))\nSelectContent.displayName = SelectPrimitive.Content.displayName\n\nconst SelectLabel = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Label\n ref={ref}\n className={cn('px-ds-03 py-ds-02b text-ds-md font-semibold', className)}\n {...props}\n />\n))\nSelectLabel.displayName = SelectPrimitive.Label.displayName\n\nconst SelectItem = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n 'relative flex w-full cursor-default select-none items-center rounded-control py-ds-02b pl-ds-03 pr-ds-07 text-ds-md outline-hidden transition-colors duration-fast-01 ease-productive-standard hover:bg-surface-raised focus:bg-surface-raised focus:text-surface-fg data-[disabled]:pointer-events-none data-[disabled]:opacity-action-disabled',\n className,\n )}\n {...props}\n >\n <span className=\"absolute right-ds-03 flex h-ico-sm w-ico-sm items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <Icon icon={IconCheck} size=\"sm\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText className=\"min-w-0 line-clamp-1\">{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n))\nSelectItem.displayName = SelectPrimitive.Item.displayName\n\nconst SelectSeparator = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Separator\n ref={ref}\n className={cn('-mx-ds-01 my-ds-02 h-px bg-surface-border', className)}\n {...props}\n />\n))\nSelectSeparator.displayName = SelectPrimitive.Separator.displayName\n\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"textarea.d.ts","sourceRoot":"","sources":["../../src/ui/textarea.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,mBAAmB,CAAA;AAItE,QAAA,MAAM,gBAAgB;;8EAyBrB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,aACf,SAAQ,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC,EACrE,YAAY,CAAC,OAAO,gBAAgB,CAAC;IACvC,KAAK,CAAC,EAAE,UAAU,CAAA;CACnB;AAED,QAAA,MAAM,QAAQ,2FAyBb,CAAA;AAGD,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAA"}
1
+ {"version":3,"file":"textarea.d.ts","sourceRoot":"","sources":["../../src/ui/textarea.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,mBAAmB,CAAA;AAItE,QAAA,MAAM,gBAAgB;;8EAyBrB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,aACf,SAAQ,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC,EACrE,YAAY,CAAC,OAAO,gBAAgB,CAAC;IACvC,KAAK,CAAC,EAAE,UAAU,CAAA;CACnB;AAED,QAAA,MAAM,QAAQ,2FA4Bb,CAAA;AAGD,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAA"}
@@ -27,14 +27,15 @@ var c = t([
27
27
  } },
28
28
  defaultVariants: { size: "md" }
29
29
  }), l = a.forwardRef(({ className: t, state: a, size: l, ...u }, d) => {
30
- let f = r(), p = i(a, f.state), m = u["aria-describedby"] ?? f.helperTextId, h = u["aria-required"] ?? f.required;
30
+ let f = r(), p = i(a, f.state), m = u["aria-describedby"] ?? f.helperTextId, h = u["aria-required"] ?? f.required, g = u.id ?? f.inputId;
31
31
  return /* @__PURE__ */ o(s.textarea, {
32
32
  "aria-invalid": p === "error" || void 0,
33
33
  "aria-describedby": m,
34
34
  "aria-required": h || void 0,
35
35
  className: n(c({ size: l }), p === "error" && "border-error-7 focus-visible:ring-error-7", p === "warning" && "border-warning-7 focus-visible:ring-warning-7", p === "success" && "border-success-7 focus-visible:ring-success-7", t),
36
36
  ref: d,
37
- ...e(u)
37
+ ...e(u),
38
+ id: g
38
39
  });
39
40
  });
40
41
  l.displayName = "Textarea";
@@ -1 +1 @@
1
- {"version":3,"file":"textarea.js","names":[],"sources":["../../src/ui/textarea.tsx"],"sourcesContent":["'use client'\n\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useFormField } from './form'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport { motionProps } from './lib/motion'\nimport { cn } from './lib/utils'\n\nconst textareaVariants = cva(\n [\n 'flex w-full font-sans resize-y',\n 'bg-surface-raised-hover text-surface-fg',\n 'border border-surface-border-strong rounded-control',\n 'placeholder:text-surface-fg-subtle',\n 'hover:bg-surface-raised-active',\n 'transition-colors duration-fast-01 ease-productive-standard',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 focus-visible:border-accent-7',\n 'disabled:cursor-not-allowed disabled:opacity-action-disabled',\n 'read-only:bg-surface-raised read-only:cursor-default',\n ],\n {\n variants: {\n // Min-height ramp 48/60/80/120: 48/80 map to spacing tokens (ds-09/ds-11);\n // 60/120 have no exact token so stay arbitrary (allowed). Rendered sizes unchanged.\n size: {\n xs: 'min-h-ds-09 text-ds-sm px-ds-02 py-ds-02',\n sm: 'min-h-[60px] text-ds-sm px-ds-03 py-ds-02',\n md: 'min-h-ds-11 text-ds-md px-ds-04 py-ds-03',\n lg: 'min-h-[120px] text-ds-md px-ds-05 py-ds-04',\n },\n },\n defaultVariants: { size: 'md' },\n },\n)\n\n/**\n * Props for Textarea — a resizable multi-line text input with size variants and validation state coloring,\n * sharing the same `FieldState` type as every form control.\n *\n * **Sizes:** `sm` (min 60px) | `md` (min 80px, default) | `lg` (min 120px) — all are vertically resizable.\n *\n * **Validation states:** `state=\"error\"` | `\"warning\"` | `\"success\"` — changes border color and focus ring.\n * HTML's native `size` attribute is omitted; use `rows` for initial height or CSS `min-height`.\n *\n * **Pair with FormField:** Use inside `<FormField>` to get a label, helper text, and associated `state`.\n *\n * @example\n * // Basic description textarea in a form:\n * <Textarea placeholder=\"Describe the issue...\" rows={4} />\n *\n * @example\n * // Error state when validation fails:\n * <Textarea state=\"error\" value={bio} onChange={(e) => setBio(e.target.value)} />\n *\n * @example\n * // Large textarea for a full email draft composer:\n * <Textarea size=\"lg\" placeholder=\"Write your message...\" className=\"min-h-[200px]\" />\n *\n * @example\n * // Read-only view of a previously submitted note:\n * <Textarea readOnly value={submission.notes} size=\"md\" />\n * // These are just a few ways — feel free to combine props creatively!\n */\nexport interface TextareaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'>,\n VariantProps<typeof textareaVariants> {\n state?: FieldState\n}\n\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n ({ className, state: stateProp, size, ...props }, ref) => {\n const fieldCtx = useFormField()\n // Merge FormField context — explicit props always win (shared precedence)\n const state = resolveFieldState(stateProp, fieldCtx.state)\n const ariaDescribedBy = props['aria-describedby'] ?? fieldCtx.helperTextId\n const ariaRequired = props['aria-required'] ?? fieldCtx.required\n\n return (\n <motion.textarea\n aria-invalid={state === 'error' || undefined}\n aria-describedby={ariaDescribedBy}\n aria-required={ariaRequired || undefined}\n className={cn(\n textareaVariants({ size }),\n state === 'error' && 'border-error-7 focus-visible:ring-error-7',\n state === 'warning' && 'border-warning-7 focus-visible:ring-warning-7',\n state === 'success' && 'border-success-7 focus-visible:ring-success-7',\n className,\n )}\n ref={ref}\n {...motionProps(props)}\n />\n )\n },\n)\nTextarea.displayName = 'Textarea'\n\nexport { Textarea, textareaVariants }\n"],"mappings":";;;;;;;;;;AAWA,IAAM,IAAmB,EACvB;CACE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,GACA;CACE,UAAU,EAGR,MAAM;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN,EACF;CACA,iBAAiB,EAAE,MAAM,KAAK;AAChC,CACF,GAoCM,IAAW,EAAM,YACpB,EAAE,cAAW,OAAO,GAAW,SAAM,GAAG,KAAS,MAAQ;CACxD,IAAM,IAAW,EAAa,GAExB,IAAQ,EAAkB,GAAW,EAAS,KAAK,GACnD,IAAkB,EAAM,uBAAuB,EAAS,cACxD,IAAe,EAAM,oBAAoB,EAAS;CAExD,OACE,kBAAC,EAAO,UAAR;EACE,gBAAc,MAAU,WAAW,KAAA;EACnC,oBAAkB;EAClB,iBAAe,KAAgB,KAAA;EAC/B,WAAW,EACT,EAAiB,EAAE,QAAK,CAAC,GACzB,MAAU,WAAW,6CACrB,MAAU,aAAa,iDACvB,MAAU,aAAa,iDACvB,CACF;EACK;EACL,GAAI,EAAY,CAAK;CACtB,CAAA;AAEL,CACF;AACA,EAAS,cAAc"}
1
+ {"version":3,"file":"textarea.js","names":[],"sources":["../../src/ui/textarea.tsx"],"sourcesContent":["'use client'\n\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useFormField } from './form'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport { motionProps } from './lib/motion'\nimport { cn } from './lib/utils'\n\nconst textareaVariants = cva(\n [\n 'flex w-full font-sans resize-y',\n 'bg-surface-raised-hover text-surface-fg',\n 'border border-surface-border-strong rounded-control',\n 'placeholder:text-surface-fg-subtle',\n 'hover:bg-surface-raised-active',\n 'transition-colors duration-fast-01 ease-productive-standard',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 focus-visible:border-accent-7',\n 'disabled:cursor-not-allowed disabled:opacity-action-disabled',\n 'read-only:bg-surface-raised read-only:cursor-default',\n ],\n {\n variants: {\n // Min-height ramp 48/60/80/120: 48/80 map to spacing tokens (ds-09/ds-11);\n // 60/120 have no exact token so stay arbitrary (allowed). Rendered sizes unchanged.\n size: {\n xs: 'min-h-ds-09 text-ds-sm px-ds-02 py-ds-02',\n sm: 'min-h-[60px] text-ds-sm px-ds-03 py-ds-02',\n md: 'min-h-ds-11 text-ds-md px-ds-04 py-ds-03',\n lg: 'min-h-[120px] text-ds-md px-ds-05 py-ds-04',\n },\n },\n defaultVariants: { size: 'md' },\n },\n)\n\n/**\n * Props for Textarea — a resizable multi-line text input with size variants and validation state coloring,\n * sharing the same `FieldState` type as every form control.\n *\n * **Sizes:** `sm` (min 60px) | `md` (min 80px, default) | `lg` (min 120px) — all are vertically resizable.\n *\n * **Validation states:** `state=\"error\"` | `\"warning\"` | `\"success\"` — changes border color and focus ring.\n * HTML's native `size` attribute is omitted; use `rows` for initial height or CSS `min-height`.\n *\n * **Pair with FormField:** Use inside `<FormField>` to get a label, helper text, and associated `state`.\n *\n * @example\n * // Basic description textarea in a form:\n * <Textarea placeholder=\"Describe the issue...\" rows={4} />\n *\n * @example\n * // Error state when validation fails:\n * <Textarea state=\"error\" value={bio} onChange={(e) => setBio(e.target.value)} />\n *\n * @example\n * // Large textarea for a full email draft composer:\n * <Textarea size=\"lg\" placeholder=\"Write your message...\" className=\"min-h-[200px]\" />\n *\n * @example\n * // Read-only view of a previously submitted note:\n * <Textarea readOnly value={submission.notes} size=\"md\" />\n * // These are just a few ways — feel free to combine props creatively!\n */\nexport interface TextareaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'>,\n VariantProps<typeof textareaVariants> {\n state?: FieldState\n}\n\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n ({ className, state: stateProp, size, ...props }, ref) => {\n const fieldCtx = useFormField()\n // Merge FormField context — explicit props always win (shared precedence)\n const state = resolveFieldState(stateProp, fieldCtx.state)\n const ariaDescribedBy = props['aria-describedby'] ?? fieldCtx.helperTextId\n const ariaRequired = props['aria-required'] ?? fieldCtx.required\n // Explicit id wins; otherwise adopt FormField's inputId so <Label htmlFor> resolves.\n const textareaId = props.id ?? fieldCtx.inputId\n\n return (\n <motion.textarea\n aria-invalid={state === 'error' || undefined}\n aria-describedby={ariaDescribedBy}\n aria-required={ariaRequired || undefined}\n className={cn(\n textareaVariants({ size }),\n state === 'error' && 'border-error-7 focus-visible:ring-error-7',\n state === 'warning' && 'border-warning-7 focus-visible:ring-warning-7',\n state === 'success' && 'border-success-7 focus-visible:ring-success-7',\n className,\n )}\n ref={ref}\n {...motionProps(props)}\n id={textareaId}\n />\n )\n },\n)\nTextarea.displayName = 'Textarea'\n\nexport { Textarea, textareaVariants }\n"],"mappings":";;;;;;;;;;AAWA,IAAM,IAAmB,EACvB;CACE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,GACA;CACE,UAAU,EAGR,MAAM;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN,EACF;CACA,iBAAiB,EAAE,MAAM,KAAK;AAChC,CACF,GAoCM,IAAW,EAAM,YACpB,EAAE,cAAW,OAAO,GAAW,SAAM,GAAG,KAAS,MAAQ;CACxD,IAAM,IAAW,EAAa,GAExB,IAAQ,EAAkB,GAAW,EAAS,KAAK,GACnD,IAAkB,EAAM,uBAAuB,EAAS,cACxD,IAAe,EAAM,oBAAoB,EAAS,UAElD,IAAa,EAAM,MAAM,EAAS;CAExC,OACE,kBAAC,EAAO,UAAR;EACE,gBAAc,MAAU,WAAW,KAAA;EACnC,oBAAkB;EAClB,iBAAe,KAAgB,KAAA;EAC/B,WAAW,EACT,EAAiB,EAAE,QAAK,CAAC,GACzB,MAAU,WAAW,6CACrB,MAAU,aAAa,iDACvB,MAAU,aAAa,iDACvB,CACF;EACK;EACL,GAAI,EAAY,CAAK;EACrB,IAAI;CACL,CAAA;AAEL,CACF;AACA,EAAS,cAAc"}
@@ -32,7 +32,7 @@
32
32
  editable: boolean — enable double-click cell editing
33
33
  virtualRows: boolean — virtualize rows for large datasets
34
34
  columnPinning: { left?: string[], right?: string[] }
35
- defaultDensity: 'compact' | 'standard' | 'comfortable'
35
+ density: 'compact' | 'standard' | 'comfortable'
36
36
 
37
37
  ## Defaults
38
38
  pageSize=10, noResultsText="No results."
@@ -86,7 +86,7 @@ import { DataTable } from '@devalok/shilp-sutra/ui/data-table'
86
86
  - When pagination prop is provided, pagination is manual — pass total count
87
87
  - selectedIds syncs via useEffect — provide getRowId for custom row IDs
88
88
  - onRowClick does NOT fire when clicking checkboxes, buttons, links, or inputs
89
- - Use defaultDensity="compact" for Karm-style h-9 rows
89
+ - Use density="compact" for Karm-style h-9 rows
90
90
  - `virtualRows={true}` requires a bounded scroll container — unbounded height silently disables virtualization
91
91
 
92
92
  ## Changes
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # @devalok/shilp-sutra
2
2
 
3
- > Radix UI + Tailwind 4 (CSS-first) + CVA design system for Devalok apps, v0.49.4.
3
+ > Radix UI + Tailwind 4 (CSS-first) + CVA design system for Devalok apps, v0.49.5.
4
4
  > Built on the same primitives as shadcn/ui but with DIFFERENT prop APIs — never guess from shadcn knowledge; verify every prop.
5
5
  > This file is a ROUTER: it tells you what exists and where to get details. Do not look for prop tables here — fetch them per component (MCP tool or per-component doc file below).
6
6
 
package/mcp-manifest.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$schema": "./mcp-manifest.schema.json",
3
3
  "manifestVersion": "1.2.0",
4
4
  "package": "@devalok/shilp-sutra",
5
- "packageVersion": "0.49.4",
5
+ "packageVersion": "0.49.5",
6
6
  "components": {
7
7
  "accordion": {
8
8
  "displayName": "Accordion",
@@ -2756,7 +2756,7 @@
2756
2756
  },
2757
2757
  "required": false
2758
2758
  },
2759
- "defaultDensity": {
2759
+ "density": {
2760
2760
  "type": {
2761
2761
  "name": "enum",
2762
2762
  "value": [
@@ -2798,7 +2798,7 @@
2798
2798
  "When pagination prop is provided, pagination is manual — pass total count",
2799
2799
  "selectedIds syncs via useEffect — provide getRowId for custom row IDs",
2800
2800
  "onRowClick does NOT fire when clicking checkboxes, buttons, links, or inputs",
2801
- "Use defaultDensity=\"compact\" for Karm-style h-9 rows",
2801
+ "Use density=\"compact\" for Karm-style h-9 rows",
2802
2802
  "`virtualRows={true}` requires a bounded scroll container — unbounded height silently disables virtualization"
2803
2803
  ],
2804
2804
  "changes": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devalok/shilp-sutra",
3
- "version": "0.49.4",
3
+ "version": "0.49.5",
4
4
  "description": "Devalok Design System — accessible React components, OKLCH design tokens, and Tailwind 4 CSS-first setup. Ships with AI-agent setup recipes.",
5
5
  "license": "MIT",
6
6
  "author": "Devalok Design & Strategy Studios <shilp-sutra@devalok.in>",
package/skill/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: shilp-sutra
3
3
  description: Add, configure, and use components from Devalok's shilp-sutra design system (@devalok/shilp-sutra) — a Tailwind 4 + React 19 + CVA library with 110+ accessible components, OKLCH design tokens, framer-motion animations, and per-component RSC-safe entry points. Use this skill whenever the user mentions shilp-sutra, Devalok, the @devalok npm scope, or asks to install/add/style/theme UI in any React project that already depends on the package — even if they don't name it explicitly. Use it instead of generic shadcn/ui, MUI, or Chakra knowledge when shilp-sutra is in the project. Covers Next.js (App + Pages), Vite, Astro, Remix, TanStack Start setup playbooks; component API and variant reference; brand token customization; Server Component import patterns; and a troubleshoot tree for the thirteen most common breakages.
4
4
  license: MIT
5
5
  metadata:
6
- version: "0.49.4"
6
+ version: "0.49.5"
7
7
  author: Devalok Design & Strategy Studios
8
8
  homepage: https://github.com/devalok-design/shilp-sutra
9
9
  npm: https://www.npmjs.com/package/@devalok/shilp-sutra
@@ -2,7 +2,7 @@
2
2
 
3
3
  # @devalok/shilp-sutra
4
4
 
5
- > Radix UI + Tailwind 4 (CSS-first) + CVA design system for Devalok apps, v0.49.4.
5
+ > Radix UI + Tailwind 4 (CSS-first) + CVA design system for Devalok apps, v0.49.5.
6
6
  > Built on the same primitives as shadcn/ui but with DIFFERENT prop APIs — never guess from shadcn knowledge; verify every prop.
7
7
  > This file is a ROUTER: it tells you what exists and where to get details. Do not look for prop tables here — fetch them per component (MCP tool or per-component doc file below).
8
8