@flikk/ui 1.0.0-beta.22 → 1.0.0-beta.24

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.
Files changed (161) hide show
  1. package/dist/components/ai/PromptInput/PromptInput.js +139 -7
  2. package/dist/components/ai/PromptInput/VoiceRecorder.js +84 -0
  3. package/dist/components/charts/AreaChart/AreaChart.types.d.ts +2 -2
  4. package/dist/components/charts/BarChart/BarChart.types.d.ts +2 -2
  5. package/dist/components/charts/ComboChart/ComboChart.types.d.ts +2 -2
  6. package/dist/components/charts/LineChart/LineChart.types.d.ts +2 -2
  7. package/dist/components/charts/ScatterPlot/ScatterPlot.types.d.ts +2 -2
  8. package/dist/components/charts/StackedBarChart/StackedBarChart.types.d.ts +2 -2
  9. package/dist/components/core/Accordion/AccordionTrigger.js +28 -1
  10. package/dist/components/core/Alert/Alert.js +2 -1
  11. package/dist/components/core/AlertDialog/AlertDialog.js +1 -1
  12. package/dist/components/core/Badge/Badge.js +6 -17
  13. package/dist/components/core/Breadcrumbs/Breadcrumbs.js +17 -28
  14. package/dist/components/core/Calendar/Calendar.js +1 -1
  15. package/dist/components/core/Carousel/CarouselBody.js +6 -1
  16. package/dist/components/core/ContextMenu/ContextMenuBody.js +6 -3
  17. package/dist/components/core/ContextMenu/ContextMenuSubBody.js +6 -3
  18. package/dist/components/core/DragDrop/DragDrop.js +170 -17
  19. package/dist/components/core/DragDrop/DragDrop.types.d.ts +6 -0
  20. package/dist/components/core/Drawer/Drawer.d.ts +1 -1
  21. package/dist/components/core/Drawer/Drawer.js +2 -0
  22. package/dist/components/core/Drawer/Drawer.types.d.ts +2 -0
  23. package/dist/components/core/Drawer/DrawerTitle.d.ts +1 -1
  24. package/dist/components/core/Drawer/DrawerTitle.js +2 -2
  25. package/dist/components/core/Dropdown/DropdownMenu.js +2 -30
  26. package/dist/components/core/Masonry/Masonry.js +7 -2
  27. package/dist/components/core/Modal/Modal.d.ts +1 -1
  28. package/dist/components/core/Modal/Modal.js +3 -3
  29. package/dist/components/core/Modal/Modal.types.d.ts +2 -0
  30. package/dist/components/core/Modal/ModalTitle.d.ts +1 -1
  31. package/dist/components/core/Modal/ModalTitle.js +2 -2
  32. package/dist/components/core/NavItem/NavItem.js +21 -6
  33. package/dist/components/core/NavItem/NavItem.theme.js +2 -2
  34. package/dist/components/core/Pagination/Pagination.js +1 -1
  35. package/dist/components/core/Popover/PopoverBody.js +3 -27
  36. package/dist/components/core/Popover/PopoverTrigger.js +1 -1
  37. package/dist/components/core/Progress/Progress.js +4 -4
  38. package/dist/components/core/Rating/Rating.js +1 -1
  39. package/dist/components/core/ScrollArea/ScrollArea.js +3 -2
  40. package/dist/components/core/Segmented/Segmented.js +6 -6
  41. package/dist/components/core/Segmented/SegmentedItem.js +28 -2
  42. package/dist/components/core/Sidebar/Sidebar.d.ts +1 -0
  43. package/dist/components/core/Sidebar/Sidebar.js +5 -3
  44. package/dist/components/core/Sidebar/Sidebar.types.d.ts +7 -1
  45. package/dist/components/core/Sidebar/SidebarContent.js +1 -1
  46. package/dist/components/core/Sidebar/SidebarFooter.js +1 -1
  47. package/dist/components/core/Sidebar/SidebarHeader.js +5 -2
  48. package/dist/components/core/Sidebar/SidebarLogo.d.ts +3 -0
  49. package/dist/components/core/Sidebar/SidebarLogo.js +17 -0
  50. package/dist/components/core/Sidebar/SidebarNavGroup.js +2 -1
  51. package/dist/components/core/Sidebar/SidebarToggle.js +2 -4
  52. package/dist/components/core/Sidebar/SidebarUserProfile.js +3 -6
  53. package/dist/components/core/Sidebar/index.d.ts +2 -1
  54. package/dist/components/core/Skeleton/Skeleton.js +2 -2
  55. package/dist/components/core/Sortable/Sortable.d.ts +1 -1
  56. package/dist/components/core/Sortable/Sortable.js +20 -4
  57. package/dist/components/core/Sortable/Sortable.types.d.ts +2 -0
  58. package/dist/components/core/Tabs/Tabs.js +6 -4
  59. package/dist/components/core/Tabs/Tabs.types.d.ts +2 -0
  60. package/dist/components/core/Tabs/TabsBody.js +3 -3
  61. package/dist/components/core/Tabs/TabsList.js +19 -21
  62. package/dist/components/core/Tabs/TabsTrigger.js +47 -28
  63. package/dist/components/core/Toast/Toast.js +1 -1
  64. package/dist/components/core/Toast/ToastProvider.js +3 -2
  65. package/dist/components/core/Tooltip/Tooltip.d.ts +1 -1
  66. package/dist/components/core/Tooltip/Tooltip.js +26 -35
  67. package/dist/components/core/Tooltip/Tooltip.types.d.ts +5 -1
  68. package/dist/components/core/Tree/Tree.js +178 -8
  69. package/dist/components/core/index.js +1 -0
  70. package/dist/components/data-display/GanttChart/GanttSplitter.js +15 -1
  71. package/dist/components/data-display/Table/Table.js +2 -2
  72. package/dist/components/data-display/Table/Table.types.d.ts +2 -0
  73. package/dist/components/data-display/Table/TableFilter.js +21 -18
  74. package/dist/components/effects/CustomCursor/CustomCursor.js +3 -7
  75. package/dist/components/effects/Overlay/Overlay.js +4 -9
  76. package/dist/components/effects/PageTransition/PageTransition.js +17 -6
  77. package/dist/components/effects/SpotlightBorder/SpotlightBorder.js +3 -4
  78. package/dist/components/forms/Checkbox/Checkbox.js +11 -1
  79. package/dist/components/forms/Checkbox/Checkbox.theme.js +4 -1
  80. package/dist/components/forms/Checkbox/Checkbox.types.d.ts +7 -5
  81. package/dist/components/forms/Checkbox/CheckboxGroup.js +9 -4
  82. package/dist/components/forms/ColorPicker/ColorPicker.js +7 -4
  83. package/dist/components/forms/ColorPicker/ColorPicker.types.d.ts +2 -2
  84. package/dist/components/forms/ColorPicker/ColorPickerBody.js +5 -3
  85. package/dist/components/forms/ColorPicker/ColorPickerEyeDropper.js +3 -3
  86. package/dist/components/forms/ColorPicker/ColorPickerGradient.js +8 -8
  87. package/dist/components/forms/ColorPicker/ColorPickerInput.js +1 -1
  88. package/dist/components/forms/ColorPicker/index.d.ts +1 -1
  89. package/dist/components/forms/Combobox/Combobox.js +20 -45
  90. package/dist/components/forms/Combobox/Combobox.types.d.ts +4 -2
  91. package/dist/components/forms/CronInput/CronInput.js +10 -4
  92. package/dist/components/forms/CronInput/CronInput.types.d.ts +12 -1
  93. package/dist/components/forms/DatePicker/DatePicker.js +2 -2
  94. package/dist/components/forms/DatePicker/DatePicker.types.d.ts +4 -2
  95. package/dist/components/forms/DatePicker/DatePickerBody.js +1 -29
  96. package/dist/components/forms/DatePicker/DatePickerTrigger.js +2 -2
  97. package/dist/components/forms/DateRangePicker/DateRangePicker.js +3 -3
  98. package/dist/components/forms/DateRangePicker/DateRangePicker.types.d.ts +4 -2
  99. package/dist/components/forms/DateRangePicker/DateRangePickerBody.js +1 -28
  100. package/dist/components/forms/DateRangePicker/DateRangePickerTrigger.js +2 -2
  101. package/dist/components/forms/FileUpload/FileUpload.js +99 -10
  102. package/dist/components/forms/FileUpload/FileUpload.types.d.ts +15 -0
  103. package/dist/components/forms/FormLabel/FormLabel.js +2 -2
  104. package/dist/components/forms/Input/Input.js +5 -3
  105. package/dist/components/forms/Input/Input.types.d.ts +2 -0
  106. package/dist/components/forms/InputAddress/InputAddress.d.ts +1 -1
  107. package/dist/components/forms/InputAddress/InputAddress.js +8 -9
  108. package/dist/components/forms/InputCounter/InputCounter.js +25 -18
  109. package/dist/components/forms/InputCounter/InputCounter.types.d.ts +8 -1
  110. package/dist/components/forms/InputCreditCard/InputCreditCard.d.ts +1 -1
  111. package/dist/components/forms/InputCreditCard/InputCreditCard.js +27 -9
  112. package/dist/components/forms/InputCreditCard/InputCreditCard.types.d.ts +5 -0
  113. package/dist/components/forms/InputOTP/InputOTP.js +26 -17
  114. package/dist/components/forms/InputOTP/InputOTP.types.d.ts +21 -4
  115. package/dist/components/forms/InputTag/InputTag.js +12 -39
  116. package/dist/components/forms/InputTag/InputTag.types.d.ts +2 -2
  117. package/dist/components/forms/Mention/Mention.js +13 -6
  118. package/dist/components/forms/Mention/Mention.types.d.ts +9 -0
  119. package/dist/components/forms/Radio/Radio.js +12 -2
  120. package/dist/components/forms/Radio/Radio.theme.js +5 -1
  121. package/dist/components/forms/Radio/Radio.types.d.ts +7 -5
  122. package/dist/components/forms/Radio/RadioGroup.js +8 -3
  123. package/dist/components/forms/RichTextEditor/RichTextEditor.js +21 -21
  124. package/dist/components/forms/RichTextEditor/RichTextEditor.types.d.ts +2 -2
  125. package/dist/components/forms/Select/Select.d.ts +2 -0
  126. package/dist/components/forms/Select/Select.js +26 -56
  127. package/dist/components/forms/Select/Select.types.d.ts +14 -0
  128. package/dist/components/forms/Signature/Signature.js +1 -1
  129. package/dist/components/forms/Signature/Signature.types.d.ts +3 -3
  130. package/dist/components/forms/Slider/Slider.js +18 -4
  131. package/dist/components/forms/Slider/Slider.types.d.ts +2 -2
  132. package/dist/components/forms/Switch/Switch.js +12 -19
  133. package/dist/components/forms/Switch/Switch.theme.js +12 -2
  134. package/dist/components/forms/Switch/Switch.types.d.ts +20 -2
  135. package/dist/components/forms/Textarea/Textarea.js +8 -3
  136. package/dist/components/forms/Textarea/Textarea.types.d.ts +2 -0
  137. package/dist/components/forms/TimePicker/TimePicker.js +1 -1
  138. package/dist/components/forms/TimePicker/TimePicker.types.d.ts +6 -2
  139. package/dist/components/forms/TimePicker/TimePickerContent.js +88 -30
  140. package/dist/components/forms/TimePicker/TimePickerTrigger.js +2 -2
  141. package/dist/components/forms/index.js +86 -1
  142. package/dist/components/layout/FormLayout/FormLayout.js +87 -1
  143. package/dist/components/layout/index.js +4 -0
  144. package/dist/hooks/index.d.ts +1 -0
  145. package/dist/hooks/useClickOutside.d.ts +10 -4
  146. package/dist/hooks/useClickOutside.js +44 -24
  147. package/dist/hooks/useDarkMode.d.ts +1 -0
  148. package/dist/hooks/useDarkMode.js +42 -0
  149. package/dist/hooks/useFormStateMachine.d.ts +1 -1
  150. package/dist/hooks/useNetworkStatus.js +1 -1
  151. package/dist/hooks/useOverlay.js +4 -0
  152. package/dist/hooks/useScaleBackground.js +5 -2
  153. package/dist/hooks/useSelectPortal.js +14 -5
  154. package/dist/index.d.ts +0 -2
  155. package/dist/index.js +174 -223
  156. package/dist/shadcn-compat.css +80 -0
  157. package/dist/styles.css +1 -1
  158. package/dist/utils/stateMachine.d.ts +4 -4
  159. package/package.json +12 -10
  160. package/tailwind.preset.cjs +32 -3
  161. /package/{src/theme-plugin.css → dist/theme.css} +0 -0
@@ -13,40 +13,9 @@ import { cn } from '../../../utils/cn.js';
13
13
  import { isValidTypeaheadChar, findNextMatchingOption, TYPEAHEAD_TIMEOUT_MS } from '../../../utils/typeaheadUtils.js';
14
14
  import { Tag } from '../../core/Tag/Tag.js';
15
15
  import { useIsClient } from '../../../hooks/useIsClient.js';
16
+ import { useDarkMode } from '../../../hooks/useDarkMode.js';
16
17
  import { useSelectState } from './useSelectState.js';
17
18
 
18
- // ============================================================================
19
- // Dark Mode Hook
20
- // ============================================================================
21
- /**
22
- * Hook to detect dark mode state from document
23
- */
24
- const useDarkMode = () => {
25
- const [isDark, setIsDark] = React__default.useState(false);
26
- useEffect(() => {
27
- if (typeof document === "undefined")
28
- return;
29
- const checkDarkMode = () => {
30
- const hasDarkClass = document.documentElement.classList.contains("dark") ||
31
- document.body.classList.contains("dark") ||
32
- document.querySelector(".dark") !== null;
33
- setIsDark(hasDarkClass);
34
- };
35
- checkDarkMode();
36
- // Observe class changes on html and body
37
- const observer = new MutationObserver(checkDarkMode);
38
- observer.observe(document.documentElement, {
39
- attributes: true,
40
- attributeFilter: ["class"],
41
- });
42
- observer.observe(document.body, {
43
- attributes: true,
44
- attributeFilter: ["class"],
45
- });
46
- return () => observer.disconnect();
47
- }, []);
48
- return isDark;
49
- };
50
19
  /**
51
20
  * SelectContext - exported for external consumption
52
21
  */
@@ -104,7 +73,7 @@ function SelectProvider({ children, selectState, size = "md", componentState = "
104
73
  /**
105
74
  * Select Button Component
106
75
  */
107
- const SelectButton = ({ id, disabled, isOpen, state = "default", size = "md", iconStart, iconEnd, className = "", onClick, onFocus, onBlur, children, }) => {
76
+ const SelectButton = ({ id, disabled, isOpen, state = "default", size = "md", iconStart, iconEnd, contentStart, contentEnd, className = "", onClick, onFocus, onBlur, children, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, "aria-labelledby": ariaLabelledby, }) => {
108
77
  var _a, _b, _c, _d, _e, _f;
109
78
  const { theme, buttonRef } = useSelectContext();
110
79
  const baseStyle = theme.baseStyle || "";
@@ -118,17 +87,23 @@ const SelectButton = ({ id, disabled, isOpen, state = "default", size = "md", ic
118
87
  const iconEndStyle = theme.iconEndStyle || "";
119
88
  const iconStartPadding = iconStart ? ((_e = theme.iconPadding) === null || _e === void 0 ? void 0 : _e.left) || "" : "";
120
89
  const iconEndPadding = iconEnd ? ((_f = theme.iconPadding) === null || _f === void 0 ? void 0 : _f.right) || "" : "";
90
+ // Size-aware content slot style (mirrors Input.theme contentStyle)
91
+ const contentSlotStyle = `flex items-center px-3 ${size === "sm"
92
+ ? "text-sm"
93
+ : size === "lg"
94
+ ? "text-base"
95
+ : "text-sm"} text-[var(--color-text-muted)] shrink-0 dark:text-[var(--color-neutral-500)]`;
121
96
  // Use either the themed focus state or directly apply the focus classes if the theme is not working
122
97
  const activeFocusState = isOpen
123
98
  ? focusStateStyle ||
124
99
  "outline-2 outline-[var(--color-primary)] -outline-offset-1 ring-4 ring-[var(--color-primary)]/10"
125
100
  : "";
126
- return (jsx("div", { className: cn(buttonWrapperStyle), children: jsxs("button", { ref: buttonRef, id: id, type: "button", className: cn(baseStyle, sizeStyle, stateStyle, hoverStateStyle, activeFocusState, iconStartPadding, iconEndPadding, buttonStyle, className), onClick: onClick, onFocus: onFocus, onBlur: onBlur, disabled: disabled, "aria-expanded": isOpen, "aria-haspopup": "listbox", "aria-controls": isOpen && id ? `${id}-listbox` : undefined, children: [iconStart && jsx("span", { className: iconStartStyle, children: iconStart }), children, iconEnd && jsx("span", { className: iconEndStyle, children: iconEnd })] }) }));
101
+ return (jsxs("div", { className: cn(buttonWrapperStyle, "flex items-stretch"), children: [contentStart && (jsx("div", { "data-slot": "content-start", className: cn(contentSlotStyle, "pr-0 border-r border-[var(--color-border)]"), children: contentStart })), jsxs("button", { ref: buttonRef, id: id, type: "button", className: cn(baseStyle, sizeStyle, stateStyle, hoverStateStyle, activeFocusState, iconStartPadding, iconEndPadding, buttonStyle, "flex-1 min-w-0", className), onClick: onClick, onFocus: onFocus, onBlur: onBlur, disabled: disabled, "aria-expanded": isOpen, "aria-haspopup": "listbox", "aria-controls": isOpen && id ? `${id}-listbox` : undefined, "aria-labelledby": ariaLabelledby, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, children: [iconStart && jsx("span", { className: iconStartStyle, children: iconStart }), children, iconEnd && jsx("span", { className: iconEndStyle, children: iconEnd })] }), contentEnd && (jsx("div", { "data-slot": "content-end", className: cn(contentSlotStyle, "pl-0 border-l border-[var(--color-border)]"), children: contentEnd }))] }));
127
102
  };
128
103
  /**
129
104
  * Select Options Component
130
105
  */
131
- const SelectOptions = ({ isOpen, maxHeight = "15rem", className = "", onClose, children, portal = true, placement = "bottom-start", offset = 4, triggerRef: customTriggerRef, }) => {
106
+ const SelectOptions = ({ isOpen, maxHeight = "15rem", className = "", onClose, children, portal = true, placement = "bottom-start", offset = 4, triggerRef: customTriggerRef, listboxId, }) => {
132
107
  var _a;
133
108
  const context = useSelectContext();
134
109
  const { optionsRef, buttonRef, highlightedIndex, setHighlightedIndex, filteredOptions, searchable, searchValue, setSearchValue, enableTypeahead, typeaheadValue, setTypeaheadValue, typeaheadTimeoutRef, theme, selectOption, } = context;
@@ -285,22 +260,12 @@ const SelectOptions = ({ isOpen, maxHeight = "15rem", className = "", onClose, c
285
260
  searchInputRef.current.select();
286
261
  }
287
262
  }, [isOpen, searchable]);
288
- // Add effect to measure content after render for improved positioning
289
- useEffect(() => {
290
- if (isOpen && contentRef.current) {
291
- // Trigger recalculation after content renders
292
- const timer = setTimeout(() => {
293
- // Hook will remeasure automatically due to contentRef dependency
294
- }, 0);
295
- return () => clearTimeout(timer);
296
- }
297
- }, [isOpen, children, filteredOptions.length]);
298
263
  // Handle search input change
299
264
  const handleSearchChange = (e) => {
300
265
  setSearchValue(e.target.value);
301
266
  setHighlightedIndex(-1); // Reset highlighted index when search changes
302
267
  };
303
- const optionsContent = (jsx(AnimatePresence, { mode: "wait", children: isOpen && (jsxs(motion.div, { ref: combinedRef, className: cn(optionsContainerStyle, className), style: {
268
+ const optionsContent = (jsx(AnimatePresence, { mode: "wait", children: isOpen && (jsxs(motion.div, { ref: combinedRef, id: listboxId, className: cn(optionsContainerStyle, className), style: {
304
269
  ...cssVariables,
305
270
  overscrollBehavior: "contain",
306
271
  ...(portal
@@ -386,7 +351,7 @@ const SelectOption = ({ value, isSelected, disabled, className = "", onSelect, c
386
351
  * />
387
352
  * ```
388
353
  */
389
- const SelectInner = ({ id, name, size = "md", state = "default", value, displayValue, placeholder = "Select an option", disabled = false, required = false, options = [], label, helperText, onChange, onFocus, onBlur, iconStart, iconEnd, theme: themeOverrides, className = "", wrapperClassName = "", searchable = false, searchPlaceholder = "Search options...", portal = true, placement = "bottom-start", offset = 8, enableTypeahead = true, creatable = false, onCreateOption, createLabel, multiple = false, maxDisplayedChips = 3, children, }, ref) => {
354
+ const SelectInner = ({ id, name, size = "md", state = "default", value, displayValue, placeholder = "Select an option", disabled = false, required = false, options = [], label, helperText, onChange, onFocus, onBlur, iconStart, iconEnd, contentStart, contentEnd, theme: themeOverrides, className = "", wrapperClassName = "", searchable = false, searchPlaceholder = "Search options...", portal = true, placement = "bottom-start", offset = 8, enableTypeahead = true, creatable = false, onCreateOption, createLabel, multiple = false, maxDisplayedChips = 3, children, }, ref) => {
390
355
  var _a;
391
356
  // Merge theme with overrides
392
357
  const theme = useMemo(() => ({ ...selectTheme, ...(themeOverrides || {}) }), [themeOverrides]);
@@ -438,18 +403,23 @@ const SelectInner = ({ id, name, size = "md", state = "default", value, displayV
438
403
  // Generate a unique ID if not provided
439
404
  const generatedId = React__default.useId();
440
405
  const selectId = id || generatedId;
441
- return (jsx(SelectProvider, { selectState: selectState, size: size, componentState: state, theme: themeOverrides, searchable: searchable, enableTypeahead: enableTypeahead, children: jsxs("div", { ref: ref, className: cn("relative", containerStyle, wrapperClassName), children: [label && (typeof label === 'string' ? (jsx(FormLabel, { htmlFor: selectId, required: required, children: label })) : (label)), jsx(SelectButton, { id: selectId, disabled: disabled, isOpen: isOpen, state: state, size: size, iconStart: iconStart, iconEnd: iconEnd || defaultChevronIcon, onClick: handleButtonClick, onFocus: onFocus, onBlur: onBlur, className: className, children: jsx("span", { className: cn(multiple && selectedValues.length > 0
406
+ return (jsx(SelectProvider, { selectState: selectState, size: size, componentState: state, theme: themeOverrides, searchable: searchable, enableTypeahead: enableTypeahead, children: jsxs("div", { ref: ref, className: cn("relative", containerStyle, wrapperClassName), children: [label && (typeof label === 'string' ? (jsx(FormLabel, { id: `${selectId}-label`, htmlFor: selectId, required: required, state: state, children: label })) : (label)), jsx(SelectButton, { id: selectId, disabled: disabled, isOpen: isOpen, state: state, size: size, iconStart: iconStart, iconEnd: iconEnd || defaultChevronIcon, contentStart: contentStart, contentEnd: contentEnd, onClick: handleButtonClick, onFocus: onFocus, onBlur: onBlur, className: className, "aria-labelledby": label && typeof label === 'string' ? `${selectId}-label` : undefined, "aria-invalid": state === "invalid" || undefined, "aria-describedby": helperText ? `${selectId}-helper` : undefined, children: jsx("span", { className: cn(multiple && selectedValues.length > 0
442
407
  ? "flex min-w-0"
443
408
  : "block truncate", (multiple ? selectedValues.length > 0 : selectedOption)
444
409
  ? theme.valueTextStyle
445
- : theme.placeholderTextStyle), children: computedDisplayValue }) }), jsx(SelectOptions, { isOpen: isOpen, onClose: () => setIsOpen(false), portal: portal, placement: placement, offset: offset, children: children ||
446
- filteredOptions.map((option) => {
447
- const isCreateOption = option.id === "__create__";
448
- const isSelected = multiple
449
- ? isValueSelected(option.value)
450
- : option.value === selectedValue;
451
- return (jsx(SelectOption, { value: option.value, isSelected: isSelected, disabled: option.disabled || false, children: isCreateOption ? (jsxs("span", { className: "flex items-center gap-2", children: [jsx(PlusIcon, { className: "size-4 shrink-0" }), jsx("span", { className: "block truncate", children: option.label })] })) : multiple ? (jsxs(Fragment, { children: [jsx("span", { className: "block truncate", children: option.label }), isSelected && (jsx("span", { className: theme.checkIconStyle, children: jsx(CheckIcon, { className: "size-4", strokeWidth: 2 }) }))] })) : (jsx("span", { className: "block truncate", children: option.label })) }, option.id));
452
- }) }), helperText && (typeof helperText === 'string' ? (jsx("p", { className: cn(helperTextStyle, helperTextStateStyle), children: helperText })) : (helperText)), name && (jsx("input", { type: "hidden", name: name, value: selectedValue !== undefined ? String(selectedValue) : "" }))] }) }));
410
+ : theme.placeholderTextStyle), children: computedDisplayValue }) }), jsx(SelectOptions, { isOpen: isOpen, onClose: () => setIsOpen(false), portal: portal, placement: placement, offset: offset, listboxId: `${selectId}-listbox`, children: children ||
411
+ (() => {
412
+ const displayedOptions = filteredOptions.length > 100
413
+ ? filteredOptions.slice(0, 100)
414
+ : filteredOptions;
415
+ return (jsxs(Fragment, { children: [displayedOptions.map((option) => {
416
+ const isCreateOption = option.id === "__create__";
417
+ const isSelected = multiple
418
+ ? isValueSelected(option.value)
419
+ : option.value === selectedValue;
420
+ return (jsx(SelectOption, { value: option.value, isSelected: isSelected, disabled: option.disabled || false, children: isCreateOption ? (jsxs("span", { className: "flex items-center gap-2", children: [jsx(PlusIcon, { className: "size-4 shrink-0" }), jsx("span", { className: "block truncate", children: option.label })] })) : multiple ? (jsxs(Fragment, { children: [jsx("span", { className: "block truncate", children: option.label }), isSelected && (jsx("span", { className: theme.checkIconStyle, children: jsx(CheckIcon, { className: "size-4", strokeWidth: 2 }) }))] })) : (jsx("span", { className: "block truncate", children: option.label })) }, option.id));
421
+ }), filteredOptions.length > 100 && (jsxs("div", { className: "px-3 py-2 text-xs text-[var(--color-text-muted)] text-center border-t border-[var(--color-border)]", children: ["Showing 100 of ", filteredOptions.length, " results \u2014 refine your search"] }))] }));
422
+ })() }), helperText && (typeof helperText === 'string' ? (jsx("p", { id: `${selectId}-helper`, className: cn(helperTextStyle, helperTextStateStyle), children: helperText })) : (helperText)), name && (jsx("input", { type: "hidden", name: name, value: selectedValue !== undefined ? String(selectedValue) : "" }))] }) }));
453
423
  };
454
424
  const SelectForwarded = React__default.forwardRef(SelectInner);
455
425
  SelectForwarded.displayName = "Select";
@@ -64,6 +64,10 @@ export interface SelectProps<T = unknown> extends SelectBaseProps {
64
64
  iconStart?: React.ReactNode;
65
65
  /** Right icon to display in the select button (default is chevron) */
66
66
  iconEnd?: React.ReactNode;
67
+ /** Structural content rendered before iconStart (e.g. a prefix label or nested Select) */
68
+ contentStart?: React.ReactNode;
69
+ /** Structural content rendered after iconEnd (e.g. a suffix label or nested Select) */
70
+ contentEnd?: React.ReactNode;
67
71
  /** Children to render instead of default options */
68
72
  children?: React.ReactNode;
69
73
  /** Component-level theme overrides */
@@ -115,6 +119,16 @@ export interface SelectButtonProps extends SelectBaseProps {
115
119
  onBlur?: (event: React.FocusEvent<HTMLButtonElement>) => void;
116
120
  /** Children to render inside the button */
117
121
  children?: React.ReactNode;
122
+ /** Marks the button as invalid for assistive technologies */
123
+ "aria-invalid"?: boolean;
124
+ /** ID of the element that describes this button (e.g. helper text) */
125
+ "aria-describedby"?: string;
126
+ /** ID of the element that labels this button (e.g. form label) */
127
+ "aria-labelledby"?: string;
128
+ /** Structural content rendered before iconStart */
129
+ contentStart?: React.ReactNode;
130
+ /** Structural content rendered after iconEnd */
131
+ contentEnd?: React.ReactNode;
118
132
  }
119
133
  /**
120
134
  * Select options container props
@@ -145,7 +145,7 @@ const Signature = React__default.forwardRef(({ id, label, labelClassName, helper
145
145
  const sizeClasses = ((_a = theme.sizes) === null || _a === void 0 ? void 0 : _a[size]) || '';
146
146
  const stateClasses = ((_b = theme.states) === null || _b === void 0 ? void 0 : _b[state]) || '';
147
147
  const helperTextClasses = theme.helperText || '';
148
- return (jsxs("div", { className: cn(wrapperClasses, stateClass, wrapperClassName), children: [label && (jsx("div", { className: "mb-1", children: jsx(FormLabel, { htmlFor: signatureId, className: labelClassName, state: state, required: required, children: label }) })), jsx("div", { className: cn(inputGroupClasses, sizeClasses, stateClasses, className), style: { width, height }, children: jsxs("div", { ref: ref, className: baseClasses, ...props, children: [jsx("canvas", { ref: canvasRef, id: signatureId, className: cn(theme.canvasStyle, customTheme.canvasStyle), tabIndex: isDisabled ? -1 : 0, onMouseDown: startDrawing, onMouseMove: draw, onMouseUp: stopDrawing, onMouseLeave: stopDrawing, onTouchStart: startDrawing, onTouchMove: draw, onTouchEnd: stopDrawing, "aria-label": label || 'Signature canvas', "aria-required": required, "aria-invalid": isInvalid, "aria-disabled": isDisabled }), isEmpty && placeholder && !isDisabled && (jsx("div", { className: cn(theme.placeholderStyle, customTheme.placeholderStyle), children: placeholder })), showControls && !isDisabled && (jsxs("div", { className: cn(theme.controlsStyle, customTheme.controlsStyle), children: [jsx(Button, { size: "sm", variant: "outline", color: 'neutral', onClick: clear, type: "button", children: "Clear" }), jsx(Button, { size: "sm", variant: "filled", onClick: save, disabled: isEmpty, type: "button", children: "Save" })] }))] }) }), helperText && (jsx("div", { className: cn(helperTextClasses, helperTextClassName), children: helperText }))] }));
148
+ return (jsxs("div", { className: cn(wrapperClasses, stateClass, wrapperClassName), children: [label && (typeof label === 'string' ? (jsx("div", { className: "mb-1", children: jsx(FormLabel, { htmlFor: signatureId, className: labelClassName, state: state, required: required, children: label }) })) : (label)), jsx("div", { className: cn(inputGroupClasses, sizeClasses, stateClasses, className), style: { width, height }, children: jsxs("div", { ref: ref, className: baseClasses, ...props, children: [jsx("canvas", { ref: canvasRef, id: signatureId, className: cn(theme.canvasStyle, customTheme.canvasStyle), tabIndex: isDisabled ? -1 : 0, onMouseDown: startDrawing, onMouseMove: draw, onMouseUp: stopDrawing, onMouseLeave: stopDrawing, onTouchStart: startDrawing, onTouchMove: draw, onTouchEnd: stopDrawing, "aria-label": typeof label === 'string' ? label : 'Signature canvas', "aria-required": required, "aria-invalid": isInvalid, "aria-disabled": isDisabled }), isEmpty && placeholder && !isDisabled && (jsx("div", { className: cn(theme.placeholderStyle, customTheme.placeholderStyle), children: placeholder })), showControls && !isDisabled && (jsxs("div", { className: cn(theme.controlsStyle, customTheme.controlsStyle), children: [jsx(Button, { size: "sm", variant: "outline", color: 'neutral', onClick: clear, type: "button", children: "Clear" }), jsx(Button, { size: "sm", variant: "filled", onClick: save, disabled: isEmpty, type: "button", children: "Save" })] }))] }) }), helperText && (typeof helperText === 'string' ? (jsx("div", { className: cn(helperTextClasses, helperTextClassName), children: helperText })) : (helperText))] }));
149
149
  });
150
150
  Signature.displayName = 'Signature';
151
151
 
@@ -1,4 +1,4 @@
1
- import { HTMLAttributes } from 'react';
1
+ import { HTMLAttributes, ReactNode } from 'react';
2
2
  /**
3
3
  * Signature component size variants
4
4
  */
@@ -51,11 +51,11 @@ export interface SignatureProps extends Omit<HTMLAttributes<HTMLDivElement>, 'on
51
51
  /** Unique identifier for the signature field */
52
52
  id?: string;
53
53
  /** Label text for the signature */
54
- label?: string;
54
+ label?: ReactNode;
55
55
  /** Additional class name for the label */
56
56
  labelClassName?: string;
57
57
  /** Helper text displayed below the signature */
58
- helperText?: string;
58
+ helperText?: ReactNode;
59
59
  /** Additional class name for the helper text */
60
60
  helperTextClassName?: string;
61
61
  /** Additional class name for the wrapper */
@@ -1,5 +1,5 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
- import React__default, { useRef, useState, useCallback, useMemo } from 'react';
2
+ import React__default, { useRef, useEffect, useState, useCallback, useMemo } from 'react';
3
3
  import { FormLabel } from '../FormLabel/FormLabel.js';
4
4
  import { sliderTheme } from './Slider.theme.js';
5
5
  import { cn } from '../../../utils/cn.js';
@@ -7,9 +7,21 @@ import { cn } from '../../../utils/cn.js';
7
7
  const Slider = React__default.forwardRef(({ state = "default", className = "", label, labelClassName = "", helperText, helperTextClassName = "", wrapperClassName = "", min = 0, max = 100, step = 1, value, defaultValue, onChange, onChangeCommitted, showValue = false, unit = "", showMinMax = false, showTicks = false, tickStep, range = false, iconStart, iconEnd, theme = {}, required = false, name, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, "aria-describedby": ariaDescribedby, ...props }, ref) => {
8
8
  var _a;
9
9
  const trackRef = useRef(null);
10
+ // Track active pointer listeners so they can be cleaned up on unmount during a drag
11
+ const activeListenersRef = useRef({ move: null, up: null });
12
+ // Cleanup on unmount — removes any lingering listeners if component unmounts mid-drag
13
+ useEffect(() => {
14
+ return () => {
15
+ const { move, up } = activeListenersRef.current;
16
+ if (move)
17
+ document.removeEventListener("pointermove", move);
18
+ if (up)
19
+ document.removeEventListener("pointerup", up);
20
+ };
21
+ }, []);
10
22
  const generatedId = React__default.useId();
11
23
  const sliderId = id || generatedId;
12
- const helperTextId = helperText ? `${sliderId}-helper` : undefined;
24
+ const helperTextId = typeof helperText === 'string' && helperText ? `${sliderId}-helper` : undefined;
13
25
  // Handle controlled/uncontrolled value
14
26
  const [internalValue, setInternalValue] = useState((_a = value !== null && value !== void 0 ? value : defaultValue) !== null && _a !== void 0 ? _a : (range ? [min, min] : min));
15
27
  const currentValue = value !== null && value !== void 0 ? value : internalValue;
@@ -68,11 +80,13 @@ const Slider = React__default.forwardRef(({ state = "default", className = "", l
68
80
  }
69
81
  };
70
82
  const handleEnd = () => {
83
+ activeListenersRef.current = { move: null, up: null };
71
84
  setDragIndex(null);
72
85
  onChangeCommitted === null || onChangeCommitted === void 0 ? void 0 : onChangeCommitted(currentValue);
73
86
  document.removeEventListener("pointermove", handleMove);
74
87
  document.removeEventListener("pointerup", handleEnd);
75
88
  };
89
+ activeListenersRef.current = { move: handleMove, up: handleEnd };
76
90
  document.addEventListener("pointermove", handleMove);
77
91
  document.addEventListener("pointerup", handleEnd);
78
92
  }, [
@@ -217,7 +231,7 @@ const Slider = React__default.forwardRef(({ state = "default", className = "", l
217
231
  return `${currentValue}${unit}`;
218
232
  };
219
233
  const filterId = `slider-liquid-lens-${sliderId}`;
220
- return (jsxs("div", { className: cn(mergedTheme.wrapperStyle, wrapperClassName), ref: ref, ...props, children: [jsx("svg", { width: "0", height: "0", className: "absolute", "aria-hidden": "true", children: jsx("defs", { children: jsxs("filter", { id: filterId, children: [jsx("feGaussianBlur", { in: "SourceGraphic", stdDeviation: "1", result: "blur" }), jsx("feColorMatrix", { in: "blur", type: "matrix", values: "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7", result: "goo" }), jsx("feComposite", { in: "SourceGraphic", in2: "goo", operator: "atop" })] }) }) }), label && (jsx(FormLabel, { htmlFor: sliderId, required: required, className: cn(mergedTheme.labelStyle, labelClassName), children: label })), jsxs("div", { className: "flex items-center gap-4", children: [iconStart && (jsx("div", { className: "flex-shrink-0 text-[var(--color-text-muted)]", children: iconStart })), showMinMax && (jsx("span", { className: cn(mergedTheme.minMaxLabelStyle), children: min })), jsxs("div", { className: cn(mergedTheme.rootStyle, className), "data-disabled": isDisabled || undefined, "data-invalid": isInvalid || undefined, "data-orientation": "horizontal", children: [jsxs("div", { ref: trackRef, className: mergedTheme.trackStyle, onPointerDown: handleTrackClick, role: "presentation", children: [jsx("div", { className: mergedTheme.rangeStyle, style: getFilledTrackStyle() }), showTicks &&
234
+ return (jsxs("div", { className: cn(mergedTheme.wrapperStyle, wrapperClassName), ref: ref, ...props, children: [jsx("svg", { width: "0", height: "0", className: "absolute", "aria-hidden": "true", children: jsx("defs", { children: jsxs("filter", { id: filterId, children: [jsx("feGaussianBlur", { in: "SourceGraphic", stdDeviation: "1", result: "blur" }), jsx("feColorMatrix", { in: "blur", type: "matrix", values: "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7", result: "goo" }), jsx("feComposite", { in: "SourceGraphic", in2: "goo", operator: "atop" })] }) }) }), label && (typeof label === 'string' ? (jsx(FormLabel, { htmlFor: sliderId, state: state, required: required, className: cn(mergedTheme.labelStyle, labelClassName), children: label })) : (label)), jsxs("div", { className: "flex items-center gap-4", children: [iconStart && (jsx("div", { className: "flex-shrink-0 text-[var(--color-text-muted)]", children: iconStart })), showMinMax && (jsx("span", { className: cn(mergedTheme.minMaxLabelStyle), children: min })), jsxs("div", { className: cn(mergedTheme.rootStyle, className), "data-disabled": isDisabled || undefined, "data-invalid": isInvalid || undefined, "data-orientation": "horizontal", children: [jsxs("div", { ref: trackRef, className: mergedTheme.trackStyle, onPointerDown: handleTrackClick, role: "presentation", children: [jsx("div", { className: mergedTheme.rangeStyle, style: getFilledTrackStyle() }), showTicks &&
221
235
  ticks.map((tick) => {
222
236
  const percentage = getPercentage(tick);
223
237
  return (jsx("div", { className: mergedTheme.tickStyle, style: { left: `${percentage}%` } }, tick));
@@ -237,7 +251,7 @@ const Slider = React__default.forwardRef(({ state = "default", className = "", l
237
251
  }, "aria-hidden": "true" })] }, index));
238
252
  }), name && (jsx("input", { type: "hidden", name: name, value: Array.isArray(currentValue)
239
253
  ? currentValue.join(",")
240
- : currentValue }))] }), showMinMax && (jsx("span", { className: cn(mergedTheme.minMaxLabelStyle), children: max })), showValue && (jsx("div", { className: cn(mergedTheme.valueDisplayStyle), children: formatValueDisplay() })), iconEnd && (jsx("div", { className: "flex-shrink-0 text-[var(--color-text-muted)]", children: iconEnd }))] }), helperText && (jsx("div", { id: helperTextId, className: cn(mergedTheme.helperTextStyle, helperTextClassName), children: helperText }))] }));
254
+ : currentValue }))] }), showMinMax && (jsx("span", { className: cn(mergedTheme.minMaxLabelStyle), children: max })), showValue && (jsx("div", { className: cn(mergedTheme.valueDisplayStyle), children: formatValueDisplay() })), iconEnd && (jsx("div", { className: "flex-shrink-0 text-[var(--color-text-muted)]", children: iconEnd }))] }), helperText && (typeof helperText === 'string' ? (jsx("div", { id: helperTextId, className: cn(mergedTheme.helperTextStyle, helperTextClassName), children: helperText })) : (helperText))] }));
241
255
  });
242
256
  Slider.displayName = "Slider";
243
257
 
@@ -26,9 +26,9 @@ export interface SliderProps extends Omit<React.HTMLAttributes<HTMLDivElement>,
26
26
  /** The state of the slider (default, disabled, invalid) */
27
27
  state?: SliderState;
28
28
  /** Optional label text for the slider */
29
- label?: string;
29
+ label?: React.ReactNode;
30
30
  /** Optional helper text displayed below the slider */
31
- helperText?: string;
31
+ helperText?: React.ReactNode;
32
32
  /** Minimum value for the slider */
33
33
  min?: number;
34
34
  /** Maximum value for the slider */
@@ -8,8 +8,10 @@ import { cn } from '../../../utils/cn.js';
8
8
  /**
9
9
  * Switch component for toggling between two states
10
10
  */
11
- const Switch = React__default.forwardRef(({ id, checked: controlledChecked, onChange, label, description, state = "default", labelPosition = "end", theme, className, ...rest }, ref) => {
12
- const [internalChecked, setInternalChecked] = useState(false);
11
+ const Switch = React__default.forwardRef(({ id, checked: controlledChecked, defaultChecked, onChange, label, description, state = "default", labelPosition = "end", size = "md", theme, className, name, value, required, form, ...rest }, ref) => {
12
+ const generatedId = React__default.useId();
13
+ const switchId = id !== null && id !== void 0 ? id : generatedId;
14
+ const [internalChecked, setInternalChecked] = useState(defaultChecked !== null && defaultChecked !== void 0 ? defaultChecked : false);
13
15
  // Determine if component is controlled or uncontrolled
14
16
  const isControlled = controlledChecked !== undefined;
15
17
  const isChecked = isControlled ? controlledChecked : internalChecked;
@@ -24,18 +26,9 @@ const Switch = React__default.forwardRef(({ id, checked: controlledChecked, onCh
24
26
  }
25
27
  onChange === null || onChange === void 0 ? void 0 : onChange(e.target.checked);
26
28
  };
27
- const handleTrackClick = () => {
28
- if (!isDisabled) {
29
- const newValue = !isChecked;
30
- if (!isControlled) {
31
- setInternalChecked(newValue);
32
- }
33
- onChange === null || onChange === void 0 ? void 0 : onChange(newValue);
34
- }
35
- };
36
29
  // Label and description content
37
30
  const labelContent = label || description ? (jsxs("div", { className: "flex grow flex-col", children: [label &&
38
- (typeof label === "string" ? (jsx(FormLabel, { htmlFor: id, state: state, children: label })) : (label)), description &&
31
+ (typeof label === "string" ? (jsx(FormLabel, { htmlFor: switchId, state: state, children: label })) : (label)), description &&
39
32
  (typeof description === "string" ? (jsx("div", { className: cn(switchTheme.description, theme === null || theme === void 0 ? void 0 : theme.description), children: description })) : (description))] })) : null;
40
33
  // Build data attributes for state-based styling
41
34
  const trackDataState = isDisabled
@@ -45,14 +38,14 @@ const Switch = React__default.forwardRef(({ id, checked: controlledChecked, onCh
45
38
  : isChecked
46
39
  ? "checked"
47
40
  : "unchecked";
48
- const filterId = `switch-liquid-lens-${id}`;
41
+ const filterId = `switch-liquid-lens-${switchId}`;
49
42
  // Switch toggle component
50
- const switchToggle = (jsxs("div", { className: "relative flex items-center h-6", children: [jsx("input", { ref: ref, type: "checkbox", id: id, checked: isChecked, onChange: handleChange, disabled: isDisabled, className: "sr-only", "aria-checked": isChecked, "aria-invalid": isInvalid }), jsx("svg", { width: "0", height: "0", className: "absolute", "aria-hidden": "true", children: jsx("defs", { children: jsxs("filter", { id: filterId, children: [jsx("feGaussianBlur", { in: "SourceGraphic", stdDeviation: "1", result: "blur" }), jsx("feColorMatrix", { in: "blur", type: "matrix", values: "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7", result: "goo" }), jsx("feComposite", { in: "SourceGraphic", in2: "goo", operator: "atop" })] }) }) }), jsx("div", { className: cn(switchTheme.track, theme === null || theme === void 0 ? void 0 : theme.track), "data-state": trackDataState, onClick: handleTrackClick, children: jsxs(motion.div, { className: cn(switchTheme.thumb, theme === null || theme === void 0 ? void 0 : theme.thumb, ""), initial: false, animate: isChecked ? thumbPositions.checked : thumbPositions.unchecked, transition: { type: "spring", stiffness: 500, damping: 30 }, whileTap: isDisabled ? undefined : { scale: 1.15 }, style: { filter: `url(#${filterId})` }, children: [jsx("div", { className: "absolute inset-0 z-0 rounded-full", style: {
51
- backdropFilter: "blur(0.6px)",
52
- WebkitBackdropFilter: "blur(0.6px)",
53
- }, "aria-hidden": "true" }), jsx("div", { className: "absolute inset-0 z-[1] rounded-full bg-[var(--color-surface)]/80", "aria-hidden": "true" }), jsx("div", { className: "absolute inset-0 z-[2] rounded-full", style: {
54
- boxShadow: "inset 1px 1px 0 rgba(255,255,255,0.35), inset 1px 3px 0 rgba(0,0,0,0.03), inset 0 0 14px rgba(255,255,255,0.25), inset -1px -1px 0 rgba(0,0,0,0.06)",
55
- }, "aria-hidden": "true" })] }) })] }));
43
+ const switchToggle = (jsxs("div", { className: "relative flex items-center h-6", children: [jsx("svg", { width: "0", height: "0", className: "absolute", "aria-hidden": "true", children: jsx("defs", { children: jsxs("filter", { id: filterId, children: [jsx("feGaussianBlur", { in: "SourceGraphic", stdDeviation: "1", result: "blur" }), jsx("feColorMatrix", { in: "blur", type: "matrix", values: "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7", result: "goo" }), jsx("feComposite", { in: "SourceGraphic", in2: "goo", operator: "atop" })] }) }) }), jsxs("label", { htmlFor: switchId, className: "contents", children: [jsx("input", { ref: ref, type: "checkbox", id: switchId, checked: isChecked, onChange: handleChange, disabled: isDisabled, className: "sr-only", name: name, value: value, required: required, form: form }), jsx("div", { className: cn(switchTheme.track, switchTheme.trackSizes[size], theme === null || theme === void 0 ? void 0 : theme.track), "data-state": trackDataState, "aria-hidden": "true", children: jsxs(motion.div, { className: cn(switchTheme.thumb, switchTheme.thumbSizes[size], theme === null || theme === void 0 ? void 0 : theme.thumb), initial: false, animate: isChecked ? thumbPositions.checked : thumbPositions.unchecked, transition: { type: "spring", stiffness: 500, damping: 30 }, whileTap: isDisabled ? undefined : { scale: 1.15 }, style: { filter: `url(#${filterId})` }, children: [jsx("div", { className: "absolute inset-0 z-0 rounded-full", style: {
44
+ backdropFilter: "blur(0.6px)",
45
+ WebkitBackdropFilter: "blur(0.6px)",
46
+ }, "aria-hidden": "true" }), jsx("div", { className: "absolute inset-0 z-[1] rounded-full bg-[var(--color-surface)]/80", "aria-hidden": "true" }), jsx("div", { className: "absolute inset-0 z-[2] rounded-full", style: {
47
+ boxShadow: "inset 1px 1px 0 rgba(255,255,255,0.35), inset 1px 3px 0 rgba(0,0,0,0.03), inset 0 0 14px rgba(255,255,255,0.25), inset -1px -1px 0 rgba(0,0,0,0.06)",
48
+ }, "aria-hidden": "true" })] }) })] })] }));
56
49
  return (jsxs("div", { className: cn(switchTheme.container, labelPosition === "start" ? "flex-row-reverse" : "flex-row", theme === null || theme === void 0 ? void 0 : theme.container, className), ...rest, children: [labelContent, switchToggle] }));
57
50
  });
58
51
  Switch.displayName = "Switch";
@@ -3,9 +3,19 @@
3
3
  */
4
4
  const switchTheme = {
5
5
  container: "flex w-full justify-between items-center gap-4",
6
- track: "w-12 h-6 rounded-full cursor-pointer shadow-inner bg-[var(--color-background-tertiary)] data-[state=checked]:bg-[var(--color-primary)] data-[state=disabled]:bg-[var(--color-background-disabled)] data-[state=disabled]:cursor-not-allowed data-[state=invalid]:border-2 data-[state=invalid]:border-[var(--color-danger)] outline outline-[var(--color-border)] transition-colors duration-300",
7
- thumb: "w-7 h-5 rounded-full bg-transparent overflow-hidden border border-[var(--color-border)]/40 shadow-lg",
6
+ track: "rounded-full cursor-pointer shadow-inner bg-[var(--color-background-tertiary)] data-[state=checked]:bg-[var(--color-primary)] data-[state=disabled]:bg-[var(--color-background-disabled)] data-[state=disabled]:cursor-not-allowed data-[state=invalid]:border-2 data-[state=invalid]:border-[var(--color-danger)] outline outline-[var(--color-border)] transition-colors duration-300",
7
+ thumb: "rounded-full bg-transparent overflow-hidden border border-[var(--color-border)]/40 shadow-lg",
8
8
  description: "text-sm text-[var(--color-text-muted)] -mt-1.5",
9
+ trackSizes: {
10
+ sm: "w-9 h-5",
11
+ md: "w-12 h-6",
12
+ lg: "w-14 h-7",
13
+ },
14
+ thumbSizes: {
15
+ sm: "w-5 h-4",
16
+ md: "w-7 h-5",
17
+ lg: "w-8 h-6",
18
+ },
9
19
  };
10
20
  /**
11
21
  * Thumb position values for animation
@@ -6,14 +6,20 @@ export type SwitchLabelPosition = "start" | "end";
6
6
  * State variants for the switch component
7
7
  */
8
8
  export type SwitchState = 'default' | 'disabled' | 'invalid';
9
+ /**
10
+ * Size variants for the switch component
11
+ */
12
+ export type SwitchSize = 'sm' | 'md' | 'lg';
9
13
  /**
10
14
  * Switch component props
11
15
  */
12
16
  export interface SwitchProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {
13
- /** Unique identifier for the switch input */
14
- id: string;
17
+ /** Unique identifier for the switch input — if omitted, one is generated via React.useId() */
18
+ id?: string;
15
19
  /** Whether the switch is checked (controlled mode) */
16
20
  checked?: boolean;
21
+ /** Initial checked state for uncontrolled mode */
22
+ defaultChecked?: boolean;
17
23
  /** Function called when the switch state changes */
18
24
  onChange?: (checked: boolean) => void;
19
25
  /** Label for the switch — pass a string for default styling, or a ReactNode for full control */
@@ -24,8 +30,18 @@ export interface SwitchProps extends Omit<React.HTMLAttributes<HTMLDivElement>,
24
30
  state?: SwitchState;
25
31
  /** Position of the label relative to the switch */
26
32
  labelPosition?: SwitchLabelPosition;
33
+ /** Size of the switch track and thumb */
34
+ size?: SwitchSize;
27
35
  /** Optional theme overrides */
28
36
  theme?: SwitchThemeOverrides;
37
+ /** HTML name attribute for the hidden input */
38
+ name?: string;
39
+ /** HTML value attribute for the hidden input */
40
+ value?: string;
41
+ /** Whether the hidden input is required */
42
+ required?: boolean;
43
+ /** The id of the form this input belongs to */
44
+ form?: string;
29
45
  }
30
46
  /**
31
47
  * Switch theme configuration
@@ -35,6 +51,8 @@ export interface SwitchTheme {
35
51
  track: string;
36
52
  thumb: string;
37
53
  description: string;
54
+ trackSizes: Record<SwitchSize, string>;
55
+ thumbSizes: Record<SwitchSize, string>;
38
56
  }
39
57
  /**
40
58
  * Switch theme overrides
@@ -4,7 +4,7 @@ import { FormLabel } from '../FormLabel/FormLabel.js';
4
4
  import { textareaTheme } from './Textarea.theme.js';
5
5
  import { cn } from '../../../utils/cn.js';
6
6
 
7
- const Textarea = React__default.forwardRef(({ size = "md", state = "default", className = "", label, labelClassName = "", helperText, helperTextClassName = "", wrapperClassName = "", inputGroupClassName = "", id, theme = {}, required, rows = 3, cols, unstyled = false, ...props }, ref) => {
7
+ const Textarea = React__default.forwardRef(({ size = "md", state = "default", className = "", label, labelClassName = "", helperText, helperTextClassName = "", wrapperClassName = "", inputGroupClassName = "", id, theme = {}, required, rows = 3, cols, unstyled = false, onValueChange, ...props }, ref) => {
8
8
  var _a, _b, _c, _d;
9
9
  const [isFocused, setIsFocused] = useState(false);
10
10
  const generatedId = React__default.useId();
@@ -30,6 +30,8 @@ const Textarea = React__default.forwardRef(({ size = "md", state = "default", cl
30
30
  : isDisabled
31
31
  ? "state-disabled"
32
32
  : "state-default";
33
+ // Pull onChange out of props so the explicit handler below isn't overwritten by the spread
34
+ const { onChange: propsOnChange, ...restProps } = props;
33
35
  // Get theme classes
34
36
  const wrapperClasses = theme.wrapperStyle || textareaTheme.wrapperStyle || "";
35
37
  const inputGroupClasses = theme.inputGroupStyle || textareaTheme.inputGroupStyle || "";
@@ -37,7 +39,7 @@ const Textarea = React__default.forwardRef(({ size = "md", state = "default", cl
37
39
  const sizeClasses = ((_a = theme.sizes) === null || _a === void 0 ? void 0 : _a[size]) || ((_b = textareaTheme.sizes) === null || _b === void 0 ? void 0 : _b[size]) || "";
38
40
  const stateClasses = ((_c = theme.states) === null || _c === void 0 ? void 0 : _c[state]) || ((_d = textareaTheme.states) === null || _d === void 0 ? void 0 : _d[state]) || "";
39
41
  const helperTextClasses = theme.helperText || textareaTheme.helperText || "";
40
- return (jsxs("div", { className: cn(wrapperClasses, stateClass, wrapperClassName), children: [label && (typeof label === 'string' ? (jsx("div", { className: "mb-1", children: jsx(FormLabel, { htmlFor: inputId, className: labelClassName, state: state, required: required, children: label }) })) : (label)), jsx("div", { children: jsx("div", { className: cn(inputGroupClasses, stateClasses, inputGroupClassName), children: jsx("div", { className: "relative flex-1", children: jsx("textarea", { ref: ref, id: inputId, className: cn(baseClasses, sizeClasses, className), disabled: isDisabled, "aria-invalid": isInvalid, required: required, rows: rows, cols: cols, onFocus: (e) => {
42
+ return (jsxs("div", { className: cn(wrapperClasses, stateClass, wrapperClassName), children: [label && (typeof label === 'string' ? (jsx("div", { className: "mb-1", children: jsx(FormLabel, { htmlFor: inputId, className: labelClassName, state: state, required: required, children: label }) })) : (label)), jsx("div", { children: jsx("div", { className: cn(inputGroupClasses, stateClasses, inputGroupClassName), children: jsx("div", { className: "relative flex-1", children: jsx("textarea", { ref: ref, id: inputId, className: cn(baseClasses, sizeClasses, className), disabled: isDisabled, "aria-invalid": isInvalid, "aria-describedby": helperText ? `${inputId}-helper` : undefined, required: required, rows: rows, cols: cols, onFocus: (e) => {
41
43
  var _a;
42
44
  setIsFocused(true);
43
45
  (_a = props.onFocus) === null || _a === void 0 ? void 0 : _a.call(props, e);
@@ -45,7 +47,10 @@ const Textarea = React__default.forwardRef(({ size = "md", state = "default", cl
45
47
  var _a;
46
48
  setIsFocused(false);
47
49
  (_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, e);
48
- }, ...props }) }) }) }), helperText && (typeof helperText === 'string' ? (jsx("div", { className: cn(helperTextClasses, helperTextClassName), children: helperText })) : (helperText))] }));
50
+ }, onChange: (e) => {
51
+ propsOnChange === null || propsOnChange === void 0 ? void 0 : propsOnChange(e);
52
+ onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(e.target.value);
53
+ }, ...restProps }) }) }) }), helperText && (typeof helperText === 'string' ? (jsx("div", { id: `${inputId}-helper`, className: cn(helperTextClasses, helperTextClassName), children: helperText })) : (helperText))] }));
49
54
  });
50
55
  Textarea.displayName = "Textarea";
51
56
 
@@ -35,6 +35,8 @@ export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextArea
35
35
  cols?: number;
36
36
  /** Render only the textarea element without wrapper, label, or helper text */
37
37
  unstyled?: boolean;
38
+ /** Callback fired with the raw string value on every change — convenience alternative to reading e.target.value from onChange */
39
+ onValueChange?: (value: string) => void;
38
40
  }
39
41
  export type TextareaSize = 'sm' | 'md' | 'lg';
40
42
  export type TextareaState = 'default' | 'disabled' | 'invalid';
@@ -447,7 +447,7 @@ const TimePicker = React__default.forwardRef(({ value, defaultValue, onChange, f
447
447
  else if (ref) {
448
448
  ref.current = el;
449
449
  }
450
- }, className: cn(mergedTheme.containerStyle, className), children: [label && (jsx(FormLabel, { htmlFor: inputId, className: labelClassName, state: finalState, required: props.required, children: label })), jsx(TimePickerTrigger, { id: inputId, disabled: disabled, isOpen: isOpen, state: finalState, size: size, clearable: clearable, onClear: handleClear, iconStart: iconStart, iconEnd: iconEnd, inputValue: inputDisplayValue, onInputChange: handleInputChange, onInputFocus: handleInputFocus, onInputKeyDown: handleInputKeyDown, format: format, period: currentPeriod, onPeriodToggle: handlePeriodToggle, placeholder: placeholder }), jsxs(TimePickerContent, { isOpen: isOpen, onCancel: handleCancel, onSave: handleSave, portal: true, placement: "bottom-start", offset: 4, dropdownRef: dropdownRef, children: [jsx(WheelColumn, { label: "Hour", options: hourOptions, value: getDisplayHour(wheelTime.hour, format), onChange: handleHourChange, theme: mergedTheme }), jsx(WheelColumn, { label: "Min", options: minuteOptions, value: wheelTime.minute, onChange: handleMinuteChange, theme: mergedTheme }), format === "12h" && (jsx(WheelColumn, { label: "", options: periodOptions, value: wheelTime.period || "AM", onChange: handlePeriodChange, theme: mergedTheme }))] }), (helperText || validationError) && (jsx("p", { className: cn(mergedTheme.helperTextStyle, (_a = mergedTheme.helperTextStates) === null || _a === void 0 ? void 0 : _a[finalState], helperTextClassName), children: validationError || helperText }))] }) }));
450
+ }, className: cn(mergedTheme.containerStyle, className), children: [label && (typeof label === 'string' ? (jsx(FormLabel, { htmlFor: inputId, className: labelClassName, state: finalState, required: props.required, children: label })) : (label)), jsx(TimePickerTrigger, { id: inputId, disabled: disabled, isOpen: isOpen, state: finalState, size: size, clearable: clearable, onClear: handleClear, iconStart: iconStart, iconEnd: iconEnd, inputValue: inputDisplayValue, onInputChange: handleInputChange, onInputFocus: handleInputFocus, onInputKeyDown: handleInputKeyDown, format: format, period: currentPeriod, onPeriodToggle: handlePeriodToggle, placeholder: placeholder, "aria-invalid": finalState === 'invalid' || undefined, "aria-describedby": helperText ? `${inputId}-helper` : undefined }), jsxs(TimePickerContent, { isOpen: isOpen, onCancel: handleCancel, onSave: handleSave, portal: true, placement: "bottom-start", offset: 4, dropdownRef: dropdownRef, children: [jsx(WheelColumn, { label: "Hour", options: hourOptions, value: getDisplayHour(wheelTime.hour, format), onChange: handleHourChange, theme: mergedTheme }), jsx(WheelColumn, { label: "Min", options: minuteOptions, value: wheelTime.minute, onChange: handleMinuteChange, theme: mergedTheme }), format === "12h" && (jsx(WheelColumn, { label: "", options: periodOptions, value: wheelTime.period || "AM", onChange: handlePeriodChange, theme: mergedTheme }))] }), (helperText || validationError) && (typeof helperText === 'string' || validationError ? (jsx("p", { id: `${inputId}-helper`, className: cn(mergedTheme.helperTextStyle, (_a = mergedTheme.helperTextStates) === null || _a === void 0 ? void 0 : _a[finalState], helperTextClassName), children: validationError || helperText })) : (helperText))] }) }));
451
451
  });
452
452
  TimePicker.displayName = "TimePicker";
453
453
 
@@ -44,9 +44,9 @@ export interface TimePickerProps extends Omit<React.InputHTMLAttributes<HTMLInpu
44
44
  /** Whether to show a clear button */
45
45
  clearable?: boolean;
46
46
  /** Optional label text */
47
- label?: string;
47
+ label?: React.ReactNode;
48
48
  /** Optional helper text */
49
- helperText?: string;
49
+ helperText?: React.ReactNode;
50
50
  /** Icon to display at the start (left side) */
51
51
  iconStart?: React.ReactNode;
52
52
  /** Icon to display at the end (right side) */
@@ -175,6 +175,10 @@ export interface TimePickerTriggerProps {
175
175
  id?: string;
176
176
  /** Whether the trigger is disabled */
177
177
  disabled?: boolean;
178
+ /** Whether the trigger has an invalid value */
179
+ "aria-invalid"?: boolean;
180
+ /** ID of the element that describes the trigger */
181
+ "aria-describedby"?: string;
178
182
  /** Whether the dropdown is open */
179
183
  isOpen?: boolean;
180
184
  /** Visual state of the trigger */