@flikk/ui 1.0.0-beta.23 → 1.0.0-beta.25

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 (157) hide show
  1. package/dist/components/charts/AreaChart/AreaChart.types.d.ts +2 -2
  2. package/dist/components/charts/BarChart/BarChart.types.d.ts +2 -2
  3. package/dist/components/charts/ComboChart/ComboChart.types.d.ts +2 -2
  4. package/dist/components/charts/LineChart/LineChart.types.d.ts +2 -2
  5. package/dist/components/charts/ScatterPlot/ScatterPlot.types.d.ts +2 -2
  6. package/dist/components/charts/StackedBarChart/StackedBarChart.types.d.ts +2 -2
  7. package/dist/components/core/Accordion/Accordion.theme.js +2 -2
  8. package/dist/components/core/Accordion/AccordionTrigger.js +28 -1
  9. package/dist/components/core/Alert/Alert.js +2 -1
  10. package/dist/components/core/AlertDialog/AlertDialog.js +1 -1
  11. package/dist/components/core/Badge/Badge.js +6 -17
  12. package/dist/components/core/Breadcrumbs/Breadcrumbs.js +17 -28
  13. package/dist/components/core/Breadcrumbs/Breadcrumbs.theme.js +2 -2
  14. package/dist/components/core/Calendar/Calendar.js +1 -1
  15. package/dist/components/core/Calendar/CalendarMini/CalendarMini.js +4 -2
  16. package/dist/components/core/Calendar/CalendarMini/CalendarMini.theme.js +1 -1
  17. package/dist/components/core/Carousel/CarouselBody.js +6 -1
  18. package/dist/components/core/ContextMenu/ContextMenuBody.js +6 -3
  19. package/dist/components/core/ContextMenu/ContextMenuSubBody.js +6 -3
  20. package/dist/components/core/DragDrop/DragDrop.js +170 -17
  21. package/dist/components/core/DragDrop/DragDrop.types.d.ts +6 -0
  22. package/dist/components/core/Drawer/Drawer.d.ts +1 -1
  23. package/dist/components/core/Drawer/Drawer.js +2 -0
  24. package/dist/components/core/Drawer/Drawer.types.d.ts +2 -0
  25. package/dist/components/core/Drawer/DrawerTitle.d.ts +1 -1
  26. package/dist/components/core/Drawer/DrawerTitle.js +2 -2
  27. package/dist/components/core/Dropdown/DropdownMenu.js +2 -30
  28. package/dist/components/core/Empty/Empty.theme.js +1 -1
  29. package/dist/components/core/Link/Link.js +1 -1
  30. package/dist/components/core/Masonry/Masonry.js +7 -2
  31. package/dist/components/core/Modal/Modal.d.ts +1 -1
  32. package/dist/components/core/Modal/Modal.js +3 -3
  33. package/dist/components/core/Modal/Modal.types.d.ts +2 -0
  34. package/dist/components/core/Modal/ModalTitle.d.ts +1 -1
  35. package/dist/components/core/Modal/ModalTitle.js +2 -2
  36. package/dist/components/core/NavItem/NavItem.js +13 -8
  37. package/dist/components/core/Pagination/Pagination.js +1 -1
  38. package/dist/components/core/Popover/PopoverBody.js +3 -27
  39. package/dist/components/core/Popover/PopoverTrigger.js +1 -1
  40. package/dist/components/core/Progress/Progress.js +4 -4
  41. package/dist/components/core/Rating/Rating.js +1 -1
  42. package/dist/components/core/ScrollArea/ScrollArea.js +3 -2
  43. package/dist/components/core/Segmented/Segmented.js +6 -6
  44. package/dist/components/core/Segmented/SegmentedItem.js +28 -2
  45. package/dist/components/core/Skeleton/Skeleton.js +2 -2
  46. package/dist/components/core/Sortable/Sortable.d.ts +1 -1
  47. package/dist/components/core/Sortable/Sortable.js +20 -4
  48. package/dist/components/core/Sortable/Sortable.types.d.ts +2 -0
  49. package/dist/components/core/Tabs/Tabs.js +6 -4
  50. package/dist/components/core/Tabs/Tabs.types.d.ts +2 -0
  51. package/dist/components/core/Tabs/TabsBody.js +3 -3
  52. package/dist/components/core/Tabs/TabsList.js +19 -21
  53. package/dist/components/core/Tabs/TabsTrigger.js +47 -28
  54. package/dist/components/core/Toast/Toast.js +1 -1
  55. package/dist/components/core/Toast/ToastProvider.js +3 -2
  56. package/dist/components/core/Tooltip/Tooltip.js +25 -34
  57. package/dist/components/core/Tree/Tree.js +178 -8
  58. package/dist/components/data-display/GanttChart/GanttSplitter.js +15 -1
  59. package/dist/components/data-display/Table/Table.js +2 -2
  60. package/dist/components/data-display/Table/Table.types.d.ts +2 -0
  61. package/dist/components/data-display/Table/TableFilter.js +21 -18
  62. package/dist/components/data-display/Timeline/Timeline.animations.d.ts +0 -7
  63. package/dist/components/data-display/Timeline/Timeline.animations.js +1 -27
  64. package/dist/components/data-display/Timeline/Timeline.js +3 -4
  65. package/dist/components/data-display/Timeline/Timeline.types.d.ts +0 -13
  66. package/dist/components/data-display/Timeline/TimelineContent.js +3 -7
  67. package/dist/components/data-display/Timeline/TimelineItem.js +4 -17
  68. package/dist/components/data-display/Timeline/index.d.ts +2 -2
  69. package/dist/components/effects/CustomCursor/CustomCursor.js +3 -7
  70. package/dist/components/effects/Overlay/Overlay.js +4 -9
  71. package/dist/components/effects/PageTransition/PageTransition.js +17 -6
  72. package/dist/components/effects/SpotlightBorder/SpotlightBorder.js +3 -4
  73. package/dist/components/effects/index.d.ts +7 -0
  74. package/dist/components/effects/index.js +3 -0
  75. package/dist/components/forms/Checkbox/Checkbox.js +11 -1
  76. package/dist/components/forms/Checkbox/Checkbox.theme.js +4 -1
  77. package/dist/components/forms/Checkbox/Checkbox.types.d.ts +7 -5
  78. package/dist/components/forms/Checkbox/CheckboxGroup.js +9 -4
  79. package/dist/components/forms/ColorPicker/ColorPicker.js +7 -4
  80. package/dist/components/forms/ColorPicker/ColorPicker.types.d.ts +2 -2
  81. package/dist/components/forms/ColorPicker/ColorPickerBody.js +5 -3
  82. package/dist/components/forms/ColorPicker/ColorPickerEyeDropper.js +3 -3
  83. package/dist/components/forms/ColorPicker/ColorPickerGradient.js +8 -8
  84. package/dist/components/forms/ColorPicker/ColorPickerInput.js +1 -1
  85. package/dist/components/forms/ColorPicker/index.d.ts +1 -1
  86. package/dist/components/forms/Combobox/Combobox.js +20 -45
  87. package/dist/components/forms/Combobox/Combobox.types.d.ts +4 -2
  88. package/dist/components/forms/CronInput/CronInput.js +10 -4
  89. package/dist/components/forms/CronInput/CronInput.types.d.ts +12 -1
  90. package/dist/components/forms/DatePicker/DatePicker.js +2 -2
  91. package/dist/components/forms/DatePicker/DatePicker.types.d.ts +4 -2
  92. package/dist/components/forms/DatePicker/DatePickerBody.js +1 -29
  93. package/dist/components/forms/DatePicker/DatePickerTrigger.js +2 -2
  94. package/dist/components/forms/DateRangePicker/DateRangePicker.js +3 -3
  95. package/dist/components/forms/DateRangePicker/DateRangePicker.types.d.ts +4 -2
  96. package/dist/components/forms/DateRangePicker/DateRangePickerBody.js +1 -28
  97. package/dist/components/forms/DateRangePicker/DateRangePickerTrigger.js +2 -2
  98. package/dist/components/forms/FileUpload/FileUpload.js +14 -10
  99. package/dist/components/forms/FileUpload/FileUpload.types.d.ts +15 -0
  100. package/dist/components/forms/FormLabel/FormLabel.js +2 -2
  101. package/dist/components/forms/Input/Input.js +5 -3
  102. package/dist/components/forms/Input/Input.types.d.ts +2 -0
  103. package/dist/components/forms/InputAddress/InputAddress.d.ts +1 -1
  104. package/dist/components/forms/InputAddress/InputAddress.js +8 -9
  105. package/dist/components/forms/InputCounter/InputCounter.js +25 -18
  106. package/dist/components/forms/InputCounter/InputCounter.types.d.ts +8 -1
  107. package/dist/components/forms/InputCreditCard/InputCreditCard.d.ts +1 -1
  108. package/dist/components/forms/InputCreditCard/InputCreditCard.js +27 -9
  109. package/dist/components/forms/InputCreditCard/InputCreditCard.types.d.ts +5 -0
  110. package/dist/components/forms/InputOTP/InputOTP.js +26 -17
  111. package/dist/components/forms/InputOTP/InputOTP.types.d.ts +21 -4
  112. package/dist/components/forms/InputTag/InputTag.js +12 -39
  113. package/dist/components/forms/InputTag/InputTag.types.d.ts +2 -2
  114. package/dist/components/forms/Mention/Mention.js +13 -6
  115. package/dist/components/forms/Mention/Mention.types.d.ts +9 -0
  116. package/dist/components/forms/Radio/Radio.js +12 -2
  117. package/dist/components/forms/Radio/Radio.theme.js +5 -1
  118. package/dist/components/forms/Radio/Radio.types.d.ts +7 -5
  119. package/dist/components/forms/Radio/RadioGroup.js +8 -3
  120. package/dist/components/forms/RichTextEditor/RichTextEditor.js +21 -21
  121. package/dist/components/forms/RichTextEditor/RichTextEditor.types.d.ts +2 -2
  122. package/dist/components/forms/Select/Select.d.ts +2 -0
  123. package/dist/components/forms/Select/Select.js +27 -57
  124. package/dist/components/forms/Select/Select.types.d.ts +14 -0
  125. package/dist/components/forms/Signature/Signature.js +1 -1
  126. package/dist/components/forms/Signature/Signature.types.d.ts +3 -3
  127. package/dist/components/forms/Slider/Slider.js +18 -4
  128. package/dist/components/forms/Slider/Slider.types.d.ts +2 -2
  129. package/dist/components/forms/Switch/Switch.js +12 -19
  130. package/dist/components/forms/Switch/Switch.theme.js +12 -2
  131. package/dist/components/forms/Switch/Switch.types.d.ts +20 -2
  132. package/dist/components/forms/Textarea/Textarea.js +8 -3
  133. package/dist/components/forms/Textarea/Textarea.types.d.ts +2 -0
  134. package/dist/components/forms/TimePicker/TimePicker.js +1 -1
  135. package/dist/components/forms/TimePicker/TimePicker.types.d.ts +6 -2
  136. package/dist/components/forms/TimePicker/TimePickerContent.js +2 -29
  137. package/dist/components/forms/TimePicker/TimePickerTrigger.js +2 -2
  138. package/dist/components/forms/index.js +1 -1
  139. package/dist/components/layout/index.js +4 -0
  140. package/dist/hooks/index.d.ts +1 -0
  141. package/dist/hooks/useClickOutside.d.ts +10 -4
  142. package/dist/hooks/useClickOutside.js +44 -24
  143. package/dist/hooks/useDarkMode.d.ts +1 -0
  144. package/dist/hooks/useDarkMode.js +42 -0
  145. package/dist/hooks/useFormStateMachine.d.ts +1 -1
  146. package/dist/hooks/useNetworkStatus.js +1 -1
  147. package/dist/hooks/useOverlay.js +4 -0
  148. package/dist/hooks/useScaleBackground.js +5 -2
  149. package/dist/hooks/useSelectPortal.js +14 -5
  150. package/dist/index.d.ts +0 -2
  151. package/dist/index.js +1 -57
  152. package/dist/shadcn-compat.css +80 -0
  153. package/dist/styles.css +1 -1
  154. package/dist/utils/stateMachine.d.ts +4 -4
  155. package/package.json +11 -10
  156. package/tailwind.preset.cjs +32 -3
  157. /package/{src/theme-plugin.css → dist/theme.css} +0 -0
@@ -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 */
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
- import { useContext, useCallback, useState, useEffect } from 'react';
2
+ import { useContext, useCallback } from 'react';
3
3
  import { AnimatePresence, motion } from 'motion/react';
4
4
  import { createPortal } from 'react-dom';
5
5
  import { cn } from '../../../utils/cn.js';
@@ -93,35 +93,8 @@ import '../../core/ContextMenu/ContextMenu.theme.js';
93
93
  import '../../core/Pill/Pill.js';
94
94
  import '../../core/Icon/Icon.js';
95
95
  import { useIsClient } from '../../../hooks/useIsClient.js';
96
+ import { useDarkMode } from '../../../hooks/useDarkMode.js';
96
97
 
97
- /**
98
- * Hook to detect dark mode state from document
99
- */
100
- const useDarkMode = () => {
101
- const [isDark, setIsDark] = useState(false);
102
- useEffect(() => {
103
- if (typeof document === "undefined")
104
- return;
105
- const checkDarkMode = () => {
106
- const hasDarkClass = document.documentElement.classList.contains("dark") ||
107
- document.body.classList.contains("dark") ||
108
- document.querySelector(".dark") !== null;
109
- setIsDark(hasDarkClass);
110
- };
111
- checkDarkMode();
112
- const observer = new MutationObserver(checkDarkMode);
113
- observer.observe(document.documentElement, {
114
- attributes: true,
115
- attributeFilter: ["class"],
116
- });
117
- observer.observe(document.body, {
118
- attributes: true,
119
- attributeFilter: ["class"],
120
- });
121
- return () => observer.disconnect();
122
- }, []);
123
- return isDark;
124
- };
125
98
  /**
126
99
  * TimePickerContent Component - contains the time selector dropdown
127
100
  * Uses portal positioning like Select and DatePicker components
@@ -9,7 +9,7 @@ import { TimePickerContext } from './TimePicker.js';
9
9
  * Displays a masked time input that opens the wheel picker on focus
10
10
  * Follows the same visual pattern as DatePickerTrigger and SelectButton
11
11
  */
12
- const TimePickerTrigger = ({ id, disabled, isOpen, state = 'default', size = 'md', clearable, onClear, iconStart, iconEnd, className, inputValue, onInputChange, onInputFocus, onInputKeyDown, format, period, onPeriodToggle, placeholder, }) => {
12
+ const TimePickerTrigger = ({ id, disabled, isOpen, state = 'default', size = 'md', clearable, onClear, iconStart, iconEnd, className, inputValue, onInputChange, onInputFocus, onInputKeyDown, format, period, onPeriodToggle, placeholder, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedby, }) => {
13
13
  var _a, _b, _c, _d;
14
14
  const context = useContext(TimePickerContext);
15
15
  if (!context) {
@@ -31,7 +31,7 @@ const TimePickerTrigger = ({ id, disabled, isOpen, state = 'default', size = 'md
31
31
  const hasStartIcon = iconStart !== undefined ? Boolean(iconStart) : true;
32
32
  const iconStartPadding = theme.iconStartPadding || "";
33
33
  return (jsx("div", { ref: triggerRef, className: "relative w-full", children: jsxs("div", { className: cn(baseStyle, stateStyle, hoverStateStyle, activeFocusState, 'w-full items-center', className // User overrides take highest precedence
34
- ), children: [jsxs("div", { className: "relative flex-1", children: [jsx("input", { ref: inputRef, id: id, type: "text", inputMode: "numeric", value: inputValue, onChange: (e) => onInputChange(e.target.value), onFocus: onInputFocus, onKeyDown: onInputKeyDown, placeholder: placeholder, maxLength: 5, disabled: disabled, className: cn(inputStyle, sizeStyle, hasStartIcon && iconStartPadding), "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-label": "Enter time", autoComplete: "off" }), iconStart !== undefined ? (iconStart && jsx("div", { className: iconStartStyle, children: iconStart })) : (jsx("div", { className: iconStartStyle, children: jsx(ClockIcon, { className: "h-4 w-4" }) }))] }), format === '12h' && (jsx("button", { type: "button", onClick: onPeriodToggle, disabled: disabled, className: cn(periodToggleStyle, "shrink-0"), "aria-label": `Toggle AM/PM, current: ${period}`, tabIndex: -1, children: period || 'AM' })), clearable && hasValue && !disabled && (jsx("button", { type: "button", onClick: (e) => {
34
+ ), children: [jsxs("div", { className: "relative flex-1", children: [jsx("input", { ref: inputRef, id: id, type: "text", inputMode: "numeric", value: inputValue, onChange: (e) => onInputChange(e.target.value), onFocus: onInputFocus, onKeyDown: onInputKeyDown, placeholder: placeholder, maxLength: 5, disabled: disabled, className: cn(inputStyle, sizeStyle, hasStartIcon && iconStartPadding), "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-label": "Enter time", "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, autoComplete: "off" }), iconStart !== undefined ? (iconStart && jsx("div", { className: iconStartStyle, children: iconStart })) : (jsx("div", { className: iconStartStyle, children: jsx(ClockIcon, { className: "h-4 w-4" }) }))] }), format === '12h' && (jsx("button", { type: "button", onClick: onPeriodToggle, disabled: disabled, className: cn(periodToggleStyle, "shrink-0"), "aria-label": `Toggle AM/PM, current: ${period}`, tabIndex: -1, children: period || 'AM' })), clearable && hasValue && !disabled && (jsx("button", { type: "button", onClick: (e) => {
35
35
  e.stopPropagation();
36
36
  onClear === null || onClear === void 0 ? void 0 : onClear();
37
37
  }, className: "shrink-0 p-0.5 rounded text-[var(--color-text-muted)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-background-secondary)] transition-colors", "aria-label": "Clear time", tabIndex: -1, children: jsx(XMarkIcon, { className: "w-4 h-4" }) })), iconEnd !== undefined ? (iconEnd && (jsx("span", { className: "shrink-0 flex items-center pr-3 pointer-events-none text-[var(--color-text-muted)] dark:text-[var(--color-neutral-500)]", children: iconEnd }))) : (jsx("span", { className: "shrink-0 flex items-center pr-3 pointer-events-none text-[var(--color-text-muted)] dark:text-[var(--color-neutral-500)]", children: jsx(ChevronUpDownIcon, { className: "h-4 w-4" }) }))] }) }));
@@ -138,7 +138,7 @@ export { inputTagTheme } from './InputTag/InputTag.theme.js';
138
138
  export { Combobox } from './Combobox/Combobox.js';
139
139
  export { comboboxTheme } from './Combobox/Combobox.theme.js';
140
140
  export { ColorPicker } from './ColorPicker/ColorPicker.js';
141
- export { ColorPickerContext, useColorPicker } from './ColorPicker/ColorPickerContext.js';
141
+ export { useColorPicker } from './ColorPicker/ColorPickerContext.js';
142
142
  export { ColorPickerTrigger } from './ColorPicker/ColorPickerTrigger.js';
143
143
  export { ColorPickerBody } from './ColorPicker/ColorPickerBody.js';
144
144
  export { ColorPicker2DCanvas } from './ColorPicker/ColorPicker2DCanvas.js';
@@ -0,0 +1,4 @@
1
+ export { FormLayout } from './FormLayout/FormLayout.js';
2
+ export { formLayoutTheme } from './FormLayout/FormLayout.theme.js';
3
+ export { PageLayout } from './PageLayout/PageLayout.js';
4
+ export { pageLayoutTheme } from './PageLayout/PageLayout.theme.js';
@@ -14,3 +14,4 @@ export { useClickOutside } from "./useClickOutside";
14
14
  export { useScaleBackground } from "./useScaleBackground";
15
15
  export type { UseScaleBackgroundOptions } from "./useScaleBackground";
16
16
  export { useMediaQuery } from "./useMediaQuery";
17
+ export { useDarkMode } from './useDarkMode';
@@ -1,7 +1,13 @@
1
1
  import { RefObject } from 'react';
2
2
  /**
3
- * Custom hook that handles clicks outside of the specified element
4
- * @param ref The reference to the element to detect clicks outside of
5
- * @param handler The callback function to execute when a click outside is detected
3
+ * Overload 1 legacy API: useClickOutside(ref, handler)
4
+ * Preserves backward compatibility with existing callers.
6
5
  */
7
- export declare const useClickOutside: <T extends HTMLElement | null>(ref: RefObject<T>, handler: (event: MouseEvent | TouchEvent) => void) => void;
6
+ export declare function useClickOutside<T extends HTMLElement | null>(ref: RefObject<T>, handler: (event: MouseEvent | TouchEvent) => void): void;
7
+ /**
8
+ * Overload 2 — new API: useClickOutside(callback, isActive?, additionalRefs?)
9
+ * Returns a ref to attach to the primary element.
10
+ * Accepts an optional array of additional refs whose contents count as "inside"
11
+ * (useful for trigger+content patterns).
12
+ */
13
+ export declare function useClickOutside(callback: () => void, isActive?: boolean, additionalRefs?: RefObject<HTMLElement | null>[]): RefObject<HTMLElement | null>;
@@ -1,28 +1,48 @@
1
- import { useRef, useEffect } from 'react';
1
+ import { useRef, useCallback, useEffect } from 'react';
2
2
 
3
- /**
4
- * Custom hook that handles clicks outside of the specified element
5
- * @param ref The reference to the element to detect clicks outside of
6
- * @param handler The callback function to execute when a click outside is detected
7
- */
8
- const useClickOutside = (ref, handler) => {
9
- const handlerRef = useRef(handler);
10
- handlerRef.current = handler;
11
- useEffect(() => {
12
- const listener = (event) => {
13
- const el = ref.current;
14
- if (!el || el.contains(event.target)) {
3
+ // Implementation
4
+ function useClickOutside(refOrCallback, handlerOrIsActive, additionalRefs) {
5
+ const internalRef = useRef(null);
6
+ const legacyHandlerRef = useRef(null);
7
+ // Keep legacy handler ref current
8
+ if (typeof refOrCallback !== 'function' && typeof handlerOrIsActive === 'function') {
9
+ legacyHandlerRef.current = handlerOrIsActive;
10
+ }
11
+ const isLegacy = typeof refOrCallback !== 'function';
12
+ const isActive = isLegacy
13
+ ? true
14
+ : typeof handlerOrIsActive === 'boolean'
15
+ ? handlerOrIsActive
16
+ : true;
17
+ const handleClickOutside = useCallback((event) => {
18
+ var _a;
19
+ const target = event.target;
20
+ if (isLegacy) {
21
+ const legacyRef = refOrCallback;
22
+ const el = legacyRef.current;
23
+ if (!el || el.contains(target))
24
+ return;
25
+ (_a = legacyHandlerRef.current) === null || _a === void 0 ? void 0 : _a.call(legacyHandlerRef, event);
26
+ }
27
+ else {
28
+ if (internalRef.current && internalRef.current.contains(target))
15
29
  return;
16
- }
17
- handlerRef.current(event);
18
- };
19
- document.addEventListener('mousedown', listener);
20
- document.addEventListener('touchstart', listener);
21
- return () => {
22
- document.removeEventListener('mousedown', listener);
23
- document.removeEventListener('touchstart', listener);
24
- };
25
- }, [ref]);
26
- };
30
+ if (additionalRefs === null || additionalRefs === void 0 ? void 0 : additionalRefs.some(r => { var _a; return (_a = r.current) === null || _a === void 0 ? void 0 : _a.contains(target); }))
31
+ return;
32
+ refOrCallback();
33
+ }
34
+ // eslint-disable-next-line react-hooks/exhaustive-deps
35
+ }, [isLegacy, additionalRefs]);
36
+ useEffect(() => {
37
+ if (!isActive)
38
+ return;
39
+ document.addEventListener('mousedown', handleClickOutside);
40
+ return () => document.removeEventListener('mousedown', handleClickOutside);
41
+ }, [handleClickOutside, isActive]);
42
+ if (isLegacy) {
43
+ return;
44
+ }
45
+ return internalRef;
46
+ }
27
47
 
28
48
  export { useClickOutside };
@@ -0,0 +1 @@
1
+ export declare function useDarkMode(): boolean;
@@ -0,0 +1,42 @@
1
+ import { useState, useEffect } from 'react';
2
+
3
+ const _subscribers = new Set();
4
+ let _observer = null;
5
+ function _getIsDark() {
6
+ if (typeof document === 'undefined')
7
+ return false;
8
+ return (document.documentElement.classList.contains('dark') ||
9
+ document.body.classList.contains('dark') ||
10
+ document.querySelector('.dark') !== null);
11
+ }
12
+ function _ensureObserver() {
13
+ if (_observer || typeof document === 'undefined')
14
+ return;
15
+ _getIsDark();
16
+ _observer = new MutationObserver(() => {
17
+ _getIsDark();
18
+ _subscribers.forEach((fn) => fn());
19
+ });
20
+ _observer.observe(document.documentElement, {
21
+ attributes: true,
22
+ attributeFilter: ['class'],
23
+ });
24
+ _observer.observe(document.body, {
25
+ attributes: true,
26
+ attributeFilter: ['class'],
27
+ });
28
+ }
29
+ function useDarkMode() {
30
+ const [, rerender] = useState(0);
31
+ useEffect(() => {
32
+ _ensureObserver();
33
+ const notify = () => rerender((n) => n + 1);
34
+ _subscribers.add(notify);
35
+ return () => {
36
+ _subscribers.delete(notify);
37
+ };
38
+ }, []);
39
+ return typeof document === 'undefined' ? false : _getIsDark();
40
+ }
41
+
42
+ export { useDarkMode };
@@ -1,5 +1,5 @@
1
1
  import { StateMachineConfig, StateIdentifier } from "../utils/stateMachine";
2
- export interface UseFormStateMachineOptions<TData = any> {
2
+ export interface UseFormStateMachineOptions<TData = unknown> {
3
3
  /** Initial form data */
4
4
  initialData?: TData;
5
5
  /** Callback when state changes */
@@ -32,7 +32,7 @@ const getConnection = () => {
32
32
  * }
33
33
  */
34
34
  const useNetworkStatus = () => {
35
- const [isOnline, setIsOnline] = useState(() => typeof navigator !== "undefined" ? navigator.onLine : true);
35
+ const [isOnline, setIsOnline] = useState(true);
36
36
  const [wasOffline, setWasOffline] = useState(false);
37
37
  const [networkInfo, setNetworkInfo] = useState({});
38
38
  const updateNetworkInfo = useCallback(() => {
@@ -7,6 +7,8 @@ let lockCount = 0;
7
7
  let savedOverflow = '';
8
8
  let savedPaddingRight = '';
9
9
  function lockScroll() {
10
+ if (typeof window === 'undefined')
11
+ return;
10
12
  lockCount++;
11
13
  if (lockCount === 1) {
12
14
  const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
@@ -23,6 +25,8 @@ function lockScroll() {
23
25
  }
24
26
  }
25
27
  function unlockScroll() {
28
+ if (typeof window === 'undefined')
29
+ return;
26
30
  lockCount = Math.max(0, lockCount - 1);
27
31
  if (lockCount === 0) {
28
32
  document.body.style.overflow = savedOverflow;
@@ -1,6 +1,7 @@
1
- import { useRef, useLayoutEffect, useEffect } from 'react';
1
+ import { useRef, useEffect, useLayoutEffect } from 'react';
2
2
  import { animate } from 'motion/react';
3
3
 
4
+ const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
4
5
  const WRAPPER_SELECTOR = "[data-flik-drawer-wrapper]";
5
6
  /**
6
7
  * useScaleBackground
@@ -22,7 +23,9 @@ function useScaleBackground({ isOpen, scale = 0.94, borderRadius = 24, position
22
23
  const controlsRef = useRef(null);
23
24
  // Track current animation progress so rapid toggling picks up where it left off
24
25
  const progressRef = useRef(0);
25
- useLayoutEffect(() => {
26
+ useIsomorphicLayoutEffect(() => {
27
+ if (typeof window === 'undefined')
28
+ return;
26
29
  // Skip if no scaling needed
27
30
  if (scale >= 1)
28
31
  return;
@@ -1,5 +1,6 @@
1
- import { useRef, useState, useCallback, useLayoutEffect } from 'react';
1
+ import { useRef, useState, useCallback, useLayoutEffect, useEffect } from 'react';
2
2
 
3
+ const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
3
4
  /**
4
5
  * Fixed dropdown positioning hook
5
6
  * Uses position: fixed with pure viewport coordinates to properly follow scroll
@@ -288,7 +289,9 @@ const useSimpleDropdown = ({ triggerRef, isOpen, placement = "bottom-start", off
288
289
  contentRef.current.style.left = `${left}px`;
289
290
  }, [triggerRef, measureDropdownHeight, measureDropdownWidth, offset]);
290
291
  // Reset initial render flag when dropdown opens
291
- useLayoutEffect(() => {
292
+ useIsomorphicLayoutEffect(() => {
293
+ if (typeof window === 'undefined')
294
+ return;
292
295
  if (!isOpen) {
293
296
  if (rafIdRef.current !== null) {
294
297
  cancelAnimationFrame(rafIdRef.current);
@@ -319,7 +322,9 @@ const useSimpleDropdown = ({ triggerRef, isOpen, placement = "bottom-start", off
319
322
  };
320
323
  }, [isOpen, calculatePosition]);
321
324
  // Initial position calculation
322
- useLayoutEffect(() => {
325
+ useIsomorphicLayoutEffect(() => {
326
+ if (typeof window === 'undefined')
327
+ return;
323
328
  if (!isOpen)
324
329
  return;
325
330
  calculatePosition();
@@ -363,7 +368,9 @@ const useSimpleDropdown = ({ triggerRef, isOpen, placement = "bottom-start", off
363
368
  });
364
369
  }, [calculatePosition]);
365
370
  // Update position on scroll and resize - rAF-synced for smooth tracking
366
- useLayoutEffect(() => {
371
+ useIsomorphicLayoutEffect(() => {
372
+ if (typeof window === 'undefined')
373
+ return;
367
374
  if (!isOpen)
368
375
  return;
369
376
  window.addEventListener("scroll", handleScroll, { passive: true, capture: true });
@@ -378,7 +385,9 @@ const useSimpleDropdown = ({ triggerRef, isOpen, placement = "bottom-start", off
378
385
  };
379
386
  }, [isOpen, handleScroll, handleResize]);
380
387
  // Recalculate when dropdown content resizes (e.g. search filtering)
381
- useLayoutEffect(() => {
388
+ useIsomorphicLayoutEffect(() => {
389
+ if (typeof window === 'undefined')
390
+ return;
382
391
  if (!isOpen)
383
392
  return;
384
393
  if (!contentRef.current)
package/dist/index.d.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  export * from "./components/core";
2
2
  export * from "./components/data-display";
3
3
  export * from "./components/forms";
4
- export * from "./components/charts";
5
4
  export * from "./components/layout";
6
- export * from "./components/effects";
7
5
  export { cn } from "./utils/cn";
8
6
  export { debounce } from "./utils/debounce";
9
7
  export { useClickOutside } from "./hooks/useClickOutside";