@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
@@ -1,5 +1,5 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
- import React__default, { useCallback, useState, useMemo } from 'react';
2
+ import React__default, { useCallback, useState, useRef, useMemo } from 'react';
3
3
  import { motion, AnimatePresence, useReducedMotion } from 'motion/react';
4
4
  import { cn } from '../../../utils/cn.js';
5
5
  import { treeTheme } from './Tree.theme.js';
@@ -7,6 +7,32 @@ import { Checkbox } from '../../forms/Checkbox/Checkbox.js';
7
7
  import { ChevronRightIcon, DocumentIcon } from '@heroicons/react/24/outline';
8
8
  import { getReducedMotionVariants, treeNodeVariants, expandIconVariants, childNodeVariants, childrenStagger } from './Tree.animations.js';
9
9
 
10
+ // Build a flat ordered list of visible (non-collapsed) node ids via depth-first traversal.
11
+ // Disabled nodes are included in the list so that ArrowDown/Up skip over them in the
12
+ // keyboard handler rather than here — keeping the traversal pure.
13
+ const buildVisibleNodeList = (nodes, expandedSet) => {
14
+ const result = [];
15
+ const traverse = (currentNodes) => {
16
+ for (const node of currentNodes) {
17
+ result.push(node.id);
18
+ if (node.children && node.children.length > 0 && expandedSet.has(node.id)) {
19
+ traverse(node.children);
20
+ }
21
+ }
22
+ };
23
+ traverse(nodes);
24
+ return result;
25
+ };
26
+ // Build a map from node id → parent node id for ArrowLeft "move to parent" navigation.
27
+ const buildParentMap = (nodes, parentId = null, acc = new Map()) => {
28
+ for (const node of nodes) {
29
+ acc.set(node.id, parentId);
30
+ if (node.children && node.children.length > 0) {
31
+ buildParentMap(node.children, node.id, acc);
32
+ }
33
+ }
34
+ return acc;
35
+ };
10
36
  // Simplified selection utilities (merged from Tree.utils.ts)
11
37
  const getDescendantIds = (node) => {
12
38
  const descendants = [];
@@ -72,7 +98,7 @@ const calculateSelection = (nodeId, selectedKeys, data) => {
72
98
  indeterminateKeys
73
99
  };
74
100
  };
75
- const InternalTreeNode = React__default.memo(({ node, level, expanded, selected, isIndeterminate, selectable, checkable, hierarchicalSelection, expandable, expandedKeysSet, selectedKeysSet, indeterminateKeysSet, onNodeClick, onToggleExpand, theme, className, shouldReduceMotion, }) => {
101
+ const InternalTreeNode = React__default.memo(({ node, level, expanded, selected, isIndeterminate, selectable, checkable, hierarchicalSelection, expandable, expandedKeysSet, selectedKeysSet, indeterminateKeysSet, onNodeClick, onToggleExpand, onKeyboardNavigate, nodeRefs, focusedNodeId, onFocusNode, theme, className, shouldReduceMotion, }) => {
76
102
  var _a;
77
103
  const hasChildren = node.children && node.children.length > 0;
78
104
  const isExpandable = expandable && hasChildren;
@@ -80,20 +106,40 @@ const InternalTreeNode = React__default.memo(({ node, level, expanded, selected,
80
106
  const nodeVariants = shouldReduceMotion ? getReducedMotionVariants(treeNodeVariants) : treeNodeVariants;
81
107
  const iconVariants = shouldReduceMotion ? getReducedMotionVariants(expandIconVariants) : expandIconVariants;
82
108
  const childVariants = shouldReduceMotion ? getReducedMotionVariants(childNodeVariants) : childNodeVariants;
109
+ // Register/unregister this node's DOM element in the shared refs map.
110
+ const itemRef = useCallback((el) => {
111
+ if (el) {
112
+ nodeRefs.current.set(node.id, el);
113
+ }
114
+ else {
115
+ nodeRefs.current.delete(node.id);
116
+ }
117
+ }, [node.id, nodeRefs]);
118
+ // Roving tabIndex: only the focused node is reachable via Tab.
119
+ // If no node is focused yet, the first root node (level 0) gets tabIndex 0 as
120
+ // a sensible initial entry point — this is handled at the Tree root.
121
+ const isFocused = focusedNodeId === node.id;
122
+ const tabIndexValue = node.disabled ? -1 : (isFocused ? 0 : -1);
83
123
  const handleNodeClick = useCallback((e) => {
84
124
  e.stopPropagation();
85
125
  if (node.disabled)
86
126
  return;
127
+ onFocusNode(node.id);
87
128
  if (selectable) {
88
129
  onNodeClick(node);
89
130
  }
90
- }, [node, selectable, onNodeClick]);
131
+ }, [node, selectable, onNodeClick, onFocusNode]);
91
132
  const handleExpandClick = useCallback((e) => {
92
133
  e.stopPropagation();
93
134
  if (isExpandable && !node.disabled) {
94
135
  onToggleExpand(node);
95
136
  }
96
137
  }, [isExpandable, node, onToggleExpand]);
138
+ const handleFocus = useCallback(() => {
139
+ if (!node.disabled) {
140
+ onFocusNode(node.id);
141
+ }
142
+ }, [node.id, node.disabled, onFocusNode]);
97
143
  const handleKeyDown = (e) => {
98
144
  if (node.disabled)
99
145
  return;
@@ -106,16 +152,43 @@ const InternalTreeNode = React__default.memo(({ node, level, expanded, selected,
106
152
  }
107
153
  break;
108
154
  case 'ArrowRight':
155
+ e.preventDefault();
109
156
  if (isExpandable && !expanded) {
110
- e.preventDefault();
157
+ // Collapsed node with children: expand it
111
158
  onToggleExpand(node);
112
159
  }
160
+ else if (isExpandable && expanded) {
161
+ // Expanded node with children: move focus to first child
162
+ onKeyboardNavigate(node.id, 'ArrowRight');
163
+ }
164
+ // Leaf node: do nothing (APG spec)
113
165
  break;
114
166
  case 'ArrowLeft':
167
+ e.preventDefault();
115
168
  if (isExpandable && expanded) {
116
- e.preventDefault();
169
+ // Expanded node: collapse it
117
170
  onToggleExpand(node);
118
171
  }
172
+ else {
173
+ // Collapsed node or leaf: move to parent
174
+ onKeyboardNavigate(node.id, 'ArrowLeft');
175
+ }
176
+ break;
177
+ case 'ArrowDown':
178
+ e.preventDefault();
179
+ onKeyboardNavigate(node.id, 'ArrowDown');
180
+ break;
181
+ case 'ArrowUp':
182
+ e.preventDefault();
183
+ onKeyboardNavigate(node.id, 'ArrowUp');
184
+ break;
185
+ case 'Home':
186
+ e.preventDefault();
187
+ onKeyboardNavigate(node.id, 'Home');
188
+ break;
189
+ case 'End':
190
+ e.preventDefault();
191
+ onKeyboardNavigate(node.id, 'End');
119
192
  break;
120
193
  }
121
194
  };
@@ -126,10 +199,10 @@ const InternalTreeNode = React__default.memo(({ node, level, expanded, selected,
126
199
  const labelStyle = cn(theme.labelStyle, {
127
200
  'text-[var(--color-text-muted)]': node.disabled,
128
201
  });
129
- return (jsxs("div", { children: [jsxs("div", { className: nodeStyle, style: { paddingLeft: `calc(${level} * ${theme.indentSize} + 0.25rem)` }, onClick: handleNodeClick, onKeyDown: handleKeyDown, tabIndex: node.disabled ? -1 : 0, role: "treeitem", "aria-expanded": isExpandable ? expanded : false, "aria-selected": selectable ? selected : undefined, "aria-disabled": node.disabled, children: [isExpandable ? (jsx(motion.span, { className: cn(theme.expandIconStyle), variants: iconVariants, initial: "collapsed", animate: expanded ? "expanded" : "collapsed", onClick: handleExpandClick, role: "button", "aria-label": expanded ? 'Collapse' : 'Expand', tabIndex: -1, children: jsx(ChevronRightIcon, { className: "size-4", strokeWidth: 2 }) })) : (jsx("span", { className: "w-4 mr-1" })), checkable && (jsx(Checkbox, { size: 'sm', id: `tree-checkbox-${node.id}`, name: `tree-checkbox-${node.id}`, value: node.id, checked: selected, indeterminate: hierarchicalSelection && isIndeterminate, onChange: () => onNodeClick(node), state: node.disabled ? "disabled" : "default", className: "flex-shrink-0", onClick: (e) => e.stopPropagation() })), (node.icon || !isExpandable) && (jsx("span", { className: cn(theme.iconStyle), children: node.icon || jsx(DocumentIcon, { className: "w-4 h-4" }) })), jsx("span", { className: labelStyle, title: node.label, children: node.label })] }), hasChildren && (jsx(AnimatePresence, { initial: false, children: expanded && (jsx(motion.div, { variants: nodeVariants, initial: "collapsed", animate: "expanded", exit: "collapsed", style: {
202
+ return (jsxs("div", { children: [jsxs("div", { ref: itemRef, className: nodeStyle, style: { paddingLeft: `calc(${level} * ${theme.indentSize} + 0.25rem)` }, onClick: handleNodeClick, onKeyDown: handleKeyDown, onFocus: handleFocus, tabIndex: tabIndexValue, role: "treeitem", "aria-expanded": isExpandable ? expanded : undefined, "aria-selected": selectable ? selected : undefined, "aria-disabled": node.disabled, children: [isExpandable ? (jsx(motion.span, { className: cn(theme.expandIconStyle), variants: iconVariants, initial: "collapsed", animate: expanded ? "expanded" : "collapsed", onClick: handleExpandClick, "aria-label": expanded ? 'Collapse' : 'Expand', tabIndex: -1, children: jsx(ChevronRightIcon, { className: "size-4", strokeWidth: 2 }) })) : (jsx("span", { className: "w-4 mr-1" })), checkable && (jsx(Checkbox, { size: 'sm', id: `tree-checkbox-${node.id}`, name: `tree-checkbox-${node.id}`, value: node.id, checked: selected, indeterminate: hierarchicalSelection && isIndeterminate, onChange: () => onNodeClick(node), state: node.disabled ? "disabled" : "default", className: "flex-shrink-0", onClick: (e) => e.stopPropagation() })), (node.icon || !isExpandable) && (jsx("span", { className: cn(theme.iconStyle), children: node.icon || jsx(DocumentIcon, { className: "w-4 h-4" }) })), jsx("span", { className: labelStyle, title: node.label, children: node.label })] }), hasChildren && (jsx(AnimatePresence, { initial: false, children: expanded && (jsx(motion.div, { variants: nodeVariants, initial: "collapsed", animate: "expanded", exit: "collapsed", style: {
130
203
  overflow: 'hidden',
131
204
  transformOrigin: 'top'
132
- }, transition: shouldReduceMotion ? { duration: 0.01 } : childrenStagger, children: (_a = node.children) === null || _a === void 0 ? void 0 : _a.map((childNode, index) => (jsx(motion.div, { variants: childVariants, initial: "hidden", animate: "visible", exit: "hidden", custom: index, children: jsx(InternalTreeNode, { node: childNode, level: level + 1, expanded: expandedKeysSet.has(childNode.id), selected: selectedKeysSet.has(childNode.id), isIndeterminate: indeterminateKeysSet.has(childNode.id), selectable: selectable, checkable: checkable, hierarchicalSelection: hierarchicalSelection, expandable: expandable, expandedKeysSet: expandedKeysSet, selectedKeysSet: selectedKeysSet, indeterminateKeysSet: indeterminateKeysSet, onNodeClick: onNodeClick, onToggleExpand: onToggleExpand, theme: theme, shouldReduceMotion: shouldReduceMotion }) }, childNode.id))) }, `${node.id}-children`)) }))] }));
205
+ }, transition: shouldReduceMotion ? { duration: 0.01 } : childrenStagger, children: (_a = node.children) === null || _a === void 0 ? void 0 : _a.map((childNode, index) => (jsx(motion.div, { variants: childVariants, initial: "hidden", animate: "visible", exit: "hidden", custom: index, children: jsx(InternalTreeNode, { node: childNode, level: level + 1, expanded: expandedKeysSet.has(childNode.id), selected: selectedKeysSet.has(childNode.id), isIndeterminate: indeterminateKeysSet.has(childNode.id), selectable: selectable, checkable: checkable, hierarchicalSelection: hierarchicalSelection, expandable: expandable, expandedKeysSet: expandedKeysSet, selectedKeysSet: selectedKeysSet, indeterminateKeysSet: indeterminateKeysSet, onNodeClick: onNodeClick, onToggleExpand: onToggleExpand, onKeyboardNavigate: onKeyboardNavigate, nodeRefs: nodeRefs, focusedNodeId: focusedNodeId, onFocusNode: onFocusNode, theme: theme, shouldReduceMotion: shouldReduceMotion }) }, childNode.id))) }, `${node.id}-children`)) }))] }));
133
206
  });
134
207
  const TreeRoot = React__default.forwardRef(({ data, selectable = false, checkable = false, hierarchicalSelection, expandable = true, defaultExpandedKeys = [], defaultSelectedKeys = [], onSelectionChange, onExpand, className, theme: customTheme = {}, ...htmlProps }, ref) => {
135
208
  var _a;
@@ -144,9 +217,106 @@ const TreeRoot = React__default.forwardRef(({ data, selectable = false, checkabl
144
217
  const [expandedKeys, setExpandedKeys] = useState(defaultExpandedKeys);
145
218
  const [selectedKeys, setSelectedKeys] = useState(defaultSelectedKeys);
146
219
  const [indeterminateKeys, setIndeterminateKeys] = useState([]);
220
+ // Roving tabIndex: track which node currently owns tabIndex=0.
221
+ // Initialise to the first root node (if any) so the tree is keyboard-enterable.
222
+ const [focusedNodeId, setFocusedNodeId] = useState(data.length > 0 ? data[0].id : null);
223
+ // Map from node id → DOM element, populated by each InternalTreeNode on mount.
224
+ const nodeRefs = useRef(new Map());
147
225
  const expandedKeysSet = useMemo(() => new Set(expandedKeys), [expandedKeys]);
148
226
  const selectedKeysSet = useMemo(() => new Set(selectedKeys), [selectedKeys]);
149
227
  const indeterminateKeysSet = useMemo(() => new Set(indeterminateKeys), [indeterminateKeys]);
228
+ // Parent map for ArrowLeft navigation (move to parent node).
229
+ const parentMap = useMemo(() => buildParentMap(data), [data]);
230
+ // Called by InternalTreeNode when it receives focus (mouse or keyboard).
231
+ const handleFocusNode = useCallback((nodeId) => {
232
+ setFocusedNodeId(nodeId);
233
+ }, []);
234
+ // Central keyboard navigation handler: moves focus within the tree.
235
+ const handleKeyboardNavigate = useCallback((nodeId, key) => {
236
+ const visibleIds = buildVisibleNodeList(data, expandedKeysSet);
237
+ const focusById = (targetId) => {
238
+ const el = nodeRefs.current.get(targetId);
239
+ if (el) {
240
+ setFocusedNodeId(targetId);
241
+ el.focus();
242
+ }
243
+ };
244
+ const currentIndex = visibleIds.indexOf(nodeId);
245
+ switch (key) {
246
+ case 'ArrowDown': {
247
+ // Move to next visible node, skipping disabled ones.
248
+ for (let i = currentIndex + 1; i < visibleIds.length; i++) {
249
+ const candidateId = visibleIds[i];
250
+ const candidateEl = nodeRefs.current.get(candidateId);
251
+ if (candidateEl && candidateEl.getAttribute('aria-disabled') !== 'true') {
252
+ focusById(candidateId);
253
+ break;
254
+ }
255
+ }
256
+ break;
257
+ }
258
+ case 'ArrowUp': {
259
+ // Move to previous visible node, skipping disabled ones.
260
+ for (let i = currentIndex - 1; i >= 0; i--) {
261
+ const candidateId = visibleIds[i];
262
+ const candidateEl = nodeRefs.current.get(candidateId);
263
+ if (candidateEl && candidateEl.getAttribute('aria-disabled') !== 'true') {
264
+ focusById(candidateId);
265
+ break;
266
+ }
267
+ }
268
+ break;
269
+ }
270
+ case 'Home': {
271
+ // Move to the first non-disabled visible node.
272
+ for (let i = 0; i < visibleIds.length; i++) {
273
+ const candidateId = visibleIds[i];
274
+ const candidateEl = nodeRefs.current.get(candidateId);
275
+ if (candidateEl && candidateEl.getAttribute('aria-disabled') !== 'true') {
276
+ focusById(candidateId);
277
+ break;
278
+ }
279
+ }
280
+ break;
281
+ }
282
+ case 'End': {
283
+ // Move to the last non-disabled visible node.
284
+ for (let i = visibleIds.length - 1; i >= 0; i--) {
285
+ const candidateId = visibleIds[i];
286
+ const candidateEl = nodeRefs.current.get(candidateId);
287
+ if (candidateEl && candidateEl.getAttribute('aria-disabled') !== 'true') {
288
+ focusById(candidateId);
289
+ break;
290
+ }
291
+ }
292
+ break;
293
+ }
294
+ case 'ArrowRight': {
295
+ // Move focus to the first child of an already-expanded node.
296
+ // (Expand-when-collapsed is handled directly in InternalTreeNode.)
297
+ const firstChildIndex = currentIndex + 1;
298
+ if (firstChildIndex < visibleIds.length) {
299
+ const firstChildId = visibleIds[firstChildIndex];
300
+ const firstChildEl = nodeRefs.current.get(firstChildId);
301
+ if (firstChildEl && firstChildEl.getAttribute('aria-disabled') !== 'true') {
302
+ focusById(firstChildId);
303
+ }
304
+ }
305
+ break;
306
+ }
307
+ case 'ArrowLeft': {
308
+ // Move focus to the parent node (when current node is collapsed or a leaf).
309
+ const parentId = parentMap.get(nodeId);
310
+ if (parentId) {
311
+ const parentEl = nodeRefs.current.get(parentId);
312
+ if (parentEl && parentEl.getAttribute('aria-disabled') !== 'true') {
313
+ focusById(parentId);
314
+ }
315
+ }
316
+ break;
317
+ }
318
+ }
319
+ }, [data, expandedKeysSet, parentMap]);
150
320
  // Handle node selection
151
321
  const handleNodeSelect = useCallback((node) => {
152
322
  if (!selectable || node.disabled)
@@ -194,7 +364,7 @@ const TreeRoot = React__default.forwardRef(({ data, selectable = false, checkabl
194
364
  setExpandedKeys(newExpandedKeys);
195
365
  onExpand === null || onExpand === void 0 ? void 0 : onExpand(newExpandedKeys, node, !isExpanded);
196
366
  }, [expandedKeys, expandable, onExpand]);
197
- return (jsx("div", { ref: ref, className: cn(theme.baseStyle, 'tree-container', className), role: "tree", "aria-multiselectable": checkable, ...htmlProps, children: data.map((node) => (jsx(InternalTreeNode, { node: node, level: 0, expanded: expandedKeysSet.has(node.id), selected: selectedKeysSet.has(node.id), isIndeterminate: indeterminateKeysSet.has(node.id), selectable: selectable, checkable: checkable, hierarchicalSelection: enableHierarchicalSelection, expandable: expandable, expandedKeysSet: expandedKeysSet, selectedKeysSet: selectedKeysSet, indeterminateKeysSet: indeterminateKeysSet, onNodeClick: handleNodeSelect, onToggleExpand: handleNodeToggleExpand, theme: theme, shouldReduceMotion: shouldReduceMotion }, node.id))) }));
367
+ return (jsx("div", { ref: ref, className: cn(theme.baseStyle, 'tree-container', className), role: "tree", "aria-multiselectable": checkable, ...htmlProps, children: data.map((node) => (jsx(InternalTreeNode, { node: node, level: 0, expanded: expandedKeysSet.has(node.id), selected: selectedKeysSet.has(node.id), isIndeterminate: indeterminateKeysSet.has(node.id), selectable: selectable, checkable: checkable, hierarchicalSelection: enableHierarchicalSelection, expandable: expandable, expandedKeysSet: expandedKeysSet, selectedKeysSet: selectedKeysSet, indeterminateKeysSet: indeterminateKeysSet, onNodeClick: handleNodeSelect, onToggleExpand: handleNodeToggleExpand, onKeyboardNavigate: handleKeyboardNavigate, nodeRefs: nodeRefs, focusedNodeId: focusedNodeId, onFocusNode: handleFocusNode, theme: theme, shouldReduceMotion: shouldReduceMotion }, node.id))) }));
198
368
  });
199
369
  TreeRoot.displayName = 'Tree';
200
370
  // Simplified compound component
@@ -1,5 +1,5 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { useRef, useCallback } from 'react';
2
+ import { useRef, useEffect, useCallback } from 'react';
3
3
  import { cn } from '../../../utils/cn.js';
4
4
  import { useGanttContext } from './GanttChart.js';
5
5
 
@@ -10,6 +10,18 @@ const GanttSplitter = () => {
10
10
  const { theme, tablePanelWidth, setTablePanelWidth } = useGanttContext();
11
11
  const startX = useRef(0);
12
12
  const startWidth = useRef(0);
13
+ // Track active pointer listeners so they can be cleaned up on unmount during a drag
14
+ const activeListenersRef = useRef({ move: null, up: null });
15
+ // Cleanup on unmount — removes any lingering listeners if component unmounts during a drag
16
+ useEffect(() => {
17
+ return () => {
18
+ const { move, up } = activeListenersRef.current;
19
+ if (move)
20
+ document.removeEventListener("pointermove", move);
21
+ if (up)
22
+ document.removeEventListener("pointerup", up);
23
+ };
24
+ }, []);
13
25
  const onPointerDown = useCallback((e) => {
14
26
  e.preventDefault();
15
27
  startX.current = e.clientX;
@@ -20,9 +32,11 @@ const GanttSplitter = () => {
20
32
  setTablePanelWidth(next);
21
33
  };
22
34
  const onPointerUp = () => {
35
+ activeListenersRef.current = { move: null, up: null };
23
36
  document.removeEventListener("pointermove", onPointerMove);
24
37
  document.removeEventListener("pointerup", onPointerUp);
25
38
  };
39
+ activeListenersRef.current = { move: onPointerMove, up: onPointerUp };
26
40
  document.addEventListener("pointermove", onPointerMove);
27
41
  document.addEventListener("pointerup", onPointerUp);
28
42
  }, [tablePanelWidth, setTablePanelWidth]);
@@ -38,7 +38,7 @@ const TableContext = createContext({
38
38
  */
39
39
  function TableComponent(props) {
40
40
  var _a;
41
- const { data, columns, rowKey, variant = "default", theme: themeOverrides, className = "", freezeHeader = false, freezeFirstColumn = false, freezeLastColumn = false, sortable = false, sortConfig = [], onSortChange, filterable = false, filterConfig = [], onFilterChange, filterLogic: controlledFilterLogic, enableBuiltInFilter = false, expandable = false, expandedRowRender, expandedRows = [], onExpandedRowsChange, selectable = false, selectionType = "checkbox", selectedRows = [], onSelectionChange, bulkActions = [], showColumnManager = false, visibleColumns: initialVisibleColumns, onVisibleColumnsChange, onColumnReorder, pagination, children, } = props;
41
+ const { data, columns, rowKey, variant = "default", theme: themeOverrides, className = "", freezeHeader = false, freezeFirstColumn = false, freezeLastColumn = false, sortable = false, sortConfig = [], onSortChange, filterable = false, filterConfig = [], onFilterChange, filterLogic: controlledFilterLogic, enableBuiltInFilter = false, expandable = false, expandedRowRender, expandedRows = [], onExpandedRowsChange, selectable = false, selectionType = "checkbox", selectedRows = [], onSelectionChange, bulkActions = [], showColumnManager = false, visibleColumns: initialVisibleColumns, onVisibleColumnsChange, onColumnReorder, pagination, children, caption, } = props;
42
42
  // Column management hook
43
43
  const { visibleColumns, columnOrder, visibleColumnsList, handleToggleColumn, handleColumnReorder, handleResetToDefault, } = useTableColumns({
44
44
  columns,
@@ -148,7 +148,7 @@ function TableComponent(props) {
148
148
  selectedRows,
149
149
  onSelectionChange,
150
150
  ]);
151
- return (jsx(TableContext.Provider, { value: contextValue, children: jsxs("div", { className: "relative", children: [!isDeclarativeAPI && columns && ((filterable && onFilterChange) || showColumnManager) && (jsxs("div", { className: theme.columnManagerContainer, children: [filterable && onFilterChange && (jsx(TableFilter, { columns: columns, filterConfig: filterConfig, onFilterChange: onFilterChange, filterLogic: filterLogic, onFilterLogicChange: handleFilterLogicChange, data: data })), showColumnManager && (jsx(TableColumnManager, { columns: columns, visibleColumns: visibleColumns, columnOrder: columnOrder, onToggleColumn: handleToggleColumn, onResetToDefault: handleResetToDefault, onColumnReorder: onColumnReorder ? handleColumnReorder : undefined }))] })), !isDeclarativeAPI && selectable && selectedRows.length > 0 && (jsx(TableSelectionHeader, { selectedCount: selectedRows.length, totalCount: paginatedData.length, selectedRows: selectedRows, bulkActions: bulkActions, onClearSelection: handleClearSelection })), jsx("div", { className: "overflow-x-auto", ref: scrollContainerRef, children: jsx("table", { className: cn(baseClasses, variantClasses, className), role: "table", "aria-label": isDeclarativeAPI ? "Data table" : `Data table with ${paginatedData.length} rows`, "aria-rowcount": pagination ? pagination.totalItems : data === null || data === void 0 ? void 0 : data.length, "aria-colcount": visibleColumnsList.length, children: isDeclarativeAPI ? (children) : (jsxs(Fragment, { children: [jsx(TableHeader, { columns: visibleColumnsList, sortConfig: sortConfig, onSortChange: onSortChange, filterConfig: filterConfig, onFilterChange: onFilterChange, freezeHeader: freezeHeader, freezeFirstColumn: freezeFirstColumn, freezeLastColumn: freezeLastColumn, expandable: expandable, expandedRowRender: expandedRowRender, selectable: selectable, selectionType: selectionType, selectedRows: selectedRows, onSelectionChange: onSelectionChange, totalRowCount: paginatedData.length, theme: theme, data: paginatedData, rowKey: rowKey }), jsx(TableBody, { data: paginatedData, columns: visibleColumnsList, rowKey: rowKey, expandedRowRender: expandedRowRender, expandedRows: expandedRows, onExpandedRowsChange: onExpandedRowsChange, selectable: selectable, selectionType: selectionType, selectedRows: selectedRows, onSelectionChange: onSelectionChange, freezeFirstColumn: freezeFirstColumn, freezeLastColumn: freezeLastColumn, theme: theme })] })) }) })] }) }));
151
+ return (jsx(TableContext.Provider, { value: contextValue, children: jsxs("div", { className: "relative", children: [!isDeclarativeAPI && columns && ((filterable && onFilterChange) || showColumnManager) && (jsxs("div", { className: theme.columnManagerContainer, children: [filterable && onFilterChange && (jsx(TableFilter, { columns: columns, filterConfig: filterConfig, onFilterChange: onFilterChange, filterLogic: filterLogic, onFilterLogicChange: handleFilterLogicChange, data: data })), showColumnManager && (jsx(TableColumnManager, { columns: columns, visibleColumns: visibleColumns, columnOrder: columnOrder, onToggleColumn: handleToggleColumn, onResetToDefault: handleResetToDefault, onColumnReorder: onColumnReorder ? handleColumnReorder : undefined }))] })), !isDeclarativeAPI && selectable && selectedRows.length > 0 && (jsx(TableSelectionHeader, { selectedCount: selectedRows.length, totalCount: paginatedData.length, selectedRows: selectedRows, bulkActions: bulkActions, onClearSelection: handleClearSelection })), jsx("div", { className: "overflow-x-auto", ref: scrollContainerRef, children: jsxs("table", { className: cn(baseClasses, variantClasses, className), role: "table", "aria-label": isDeclarativeAPI ? "Data table" : `Data table with ${paginatedData.length} rows`, "aria-rowcount": pagination ? pagination.totalItems : data === null || data === void 0 ? void 0 : data.length, "aria-colcount": visibleColumnsList.length, children: [caption && (jsx("caption", { className: "sr-only", children: caption })), isDeclarativeAPI ? (children) : (jsxs(Fragment, { children: [jsx(TableHeader, { columns: visibleColumnsList, sortConfig: sortConfig, onSortChange: onSortChange, filterConfig: filterConfig, onFilterChange: onFilterChange, freezeHeader: freezeHeader, freezeFirstColumn: freezeFirstColumn, freezeLastColumn: freezeLastColumn, expandable: expandable, expandedRowRender: expandedRowRender, selectable: selectable, selectionType: selectionType, selectedRows: selectedRows, onSelectionChange: onSelectionChange, totalRowCount: paginatedData.length, theme: theme, data: paginatedData, rowKey: rowKey }), jsx(TableBody, { data: paginatedData, columns: visibleColumnsList, rowKey: rowKey, expandedRowRender: expandedRowRender, expandedRows: expandedRows, onExpandedRowsChange: onExpandedRowsChange, selectable: selectable, selectionType: selectionType, selectedRows: selectedRows, onSelectionChange: onSelectionChange, freezeFirstColumn: freezeFirstColumn, freezeLastColumn: freezeLastColumn, theme: theme })] }))] }) })] }) }));
152
152
  }
153
153
  TableComponent.displayName = "Table";
154
154
  const Table = TableComponent;
@@ -193,6 +193,8 @@ export interface TableProps<T = unknown> {
193
193
  totalItems: number;
194
194
  };
195
195
  children?: ReactNode;
196
+ /** Optional accessible caption for the table, rendered as a visually-hidden <caption> element */
197
+ caption?: string;
196
198
  }
197
199
  export interface TableHeaderProps {
198
200
  children?: ReactNode;
@@ -1,6 +1,6 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { useState, useCallback } from 'react';
3
- import { inferFilterType, getDefaultOperator, isUnaryOperator, getOperatorsForFilterType, isBetweenOperator } from './Table.filterUtils.js';
2
+ import { useRef, useState, useCallback } from 'react';
3
+ import { inferFilterType, getDefaultOperator, isUnaryOperator, isBetweenOperator, getOperatorsForFilterType } from './Table.filterUtils.js';
4
4
  import { Button } from '../../core/Button/Button.js';
5
5
  import { Input } from '../../forms/Input/Input.js';
6
6
  import '../../forms/Input/Input.theme.js';
@@ -57,6 +57,7 @@ function FilterValueInput({ filter, column, filterType, onValueChange, }) {
57
57
  }
58
58
  function TableFilter({ columns, filterConfig, onFilterChange, filterLogic: controlledLogic, onFilterLogicChange, data, className, }) {
59
59
  const filterableColumns = columns.filter((col) => col.filterable);
60
+ const filterIdRef = useRef(0);
60
61
  // Internal filter logic state
61
62
  const [internalLogic, setInternalLogic] = useState("and");
62
63
  const filterLogic = controlledLogic !== null && controlledLogic !== void 0 ? controlledLogic : internalLogic;
@@ -87,7 +88,7 @@ function TableFilter({ columns, filterConfig, onFilterChange, filterLogic: contr
87
88
  const resolvedType = inferFilterType(defaultColumn, data);
88
89
  return [
89
90
  {
90
- id: `filter-${Date.now()}`,
91
+ id: `filter-${++filterIdRef.current}`,
91
92
  columnId: defaultColumn.id,
92
93
  operator: getDefaultOperator(resolvedType),
93
94
  value: "",
@@ -107,7 +108,7 @@ function TableFilter({ columns, filterConfig, onFilterChange, filterLogic: contr
107
108
  return "text";
108
109
  return inferFilterType(column, data);
109
110
  };
110
- const syncFilters = (filters) => {
111
+ const syncFilters = useCallback((filters) => {
111
112
  const newFilterConfig = filters
112
113
  .filter((f) => {
113
114
  if (!f.columnId)
@@ -131,8 +132,8 @@ function TableFilter({ columns, filterConfig, onFilterChange, filterLogic: contr
131
132
  operator: f.operator,
132
133
  }));
133
134
  onFilterChange(newFilterConfig);
134
- };
135
- const handleAddFilter = () => {
135
+ }, [onFilterChange]);
136
+ const handleAddFilter = useCallback(() => {
136
137
  const defaultColumn = filterableColumns[0];
137
138
  if (!defaultColumn)
138
139
  return;
@@ -141,21 +142,22 @@ function TableFilter({ columns, filterConfig, onFilterChange, filterLogic: contr
141
142
  setActiveFilters((prev) => [
142
143
  ...prev,
143
144
  {
144
- id: `filter-${Date.now()}`,
145
+ id: `filter-${++filterIdRef.current}`,
145
146
  columnId: defaultColumn.id,
146
147
  operator: defaultOp,
147
148
  value: "",
148
149
  },
149
150
  ]);
150
- };
151
- const handleRemoveFilter = (filterId) => {
151
+ // eslint-disable-next-line react-hooks/exhaustive-deps
152
+ }, [filterableColumns, onFilterChange]);
153
+ const handleRemoveFilter = useCallback((filterId) => {
152
154
  setActiveFilters((prev) => {
153
155
  const updated = prev.filter((f) => f.id !== filterId);
154
156
  syncFilters(updated);
155
157
  return updated;
156
158
  });
157
- };
158
- const handleFilterColumnChange = (filterId, columnId) => {
159
+ }, [syncFilters]);
160
+ const handleFilterColumnChange = useCallback((filterId, columnId) => {
159
161
  setActiveFilters((prev) => {
160
162
  const updated = prev.map((f) => {
161
163
  if (f.id !== filterId)
@@ -168,8 +170,9 @@ function TableFilter({ columns, filterConfig, onFilterChange, filterLogic: contr
168
170
  syncFilters(updated);
169
171
  return updated;
170
172
  });
171
- };
172
- const handleFilterOperatorChange = (filterId, operator) => {
173
+ // eslint-disable-next-line react-hooks/exhaustive-deps
174
+ }, [filterableColumns, data, syncFilters]);
175
+ const handleFilterOperatorChange = useCallback((filterId, operator) => {
173
176
  setActiveFilters((prev) => {
174
177
  const updated = prev.map((f) => {
175
178
  if (f.id !== filterId)
@@ -188,18 +191,18 @@ function TableFilter({ columns, filterConfig, onFilterChange, filterLogic: contr
188
191
  syncFilters(updated);
189
192
  return updated;
190
193
  });
191
- };
192
- const handleFilterValueChange = (filterId, value) => {
194
+ }, [syncFilters]);
195
+ const handleFilterValueChange = useCallback((filterId, value) => {
193
196
  setActiveFilters((prev) => {
194
197
  const updated = prev.map((f) => f.id === filterId ? { ...f, value } : f);
195
198
  syncFilters(updated);
196
199
  return updated;
197
200
  });
198
- };
199
- const handleClearAll = () => {
201
+ }, [syncFilters]);
202
+ const handleClearAll = useCallback(() => {
200
203
  setActiveFilters([]);
201
204
  onFilterChange([]);
202
- };
205
+ }, [onFilterChange]);
203
206
  const appliedFilterCount = activeFilters.filter((f) => {
204
207
  if (!f.columnId)
205
208
  return false;
@@ -4,13 +4,6 @@ import { Variants } from "motion/react";
4
4
  * Provides a fade + slide effect when items enter the viewport
5
5
  */
6
6
  export declare const timelineItemVariants: Variants;
7
- /**
8
- * Get directional slide variants based on alignment side
9
- * Items slide in from the opposite direction of their placement
10
- *
11
- * @param side - 'left' slides from left, 'right' slides from right
12
- */
13
- export declare const getDirectionalVariants: (side: "left" | "right") => Variants;
14
7
  /**
15
8
  * Horizontal layout item entry animation
16
9
  * Items fade in from below
@@ -20,32 +20,6 @@ const timelineItemVariants = {
20
20
  },
21
21
  },
22
22
  };
23
- /**
24
- * Get directional slide variants based on alignment side
25
- * Items slide in from the opposite direction of their placement
26
- *
27
- * @param side - 'left' slides from left, 'right' slides from right
28
- */
29
- const getDirectionalVariants = (side) => ({
30
- hidden: {
31
- opacity: 0,
32
- x: side === "left" ? -30 : 30,
33
- y: 10,
34
- transition: {
35
- duration: 0.3,
36
- ease: "easeOut",
37
- },
38
- },
39
- visible: {
40
- opacity: 1,
41
- x: 0,
42
- y: 0,
43
- transition: {
44
- duration: 0.5,
45
- ease: [0.04, 0.62, 0.23, 0.98],
46
- },
47
- },
48
- });
49
23
  /**
50
24
  * Horizontal layout item entry animation
51
25
  * Items fade in from below
@@ -89,4 +63,4 @@ const timelineMarkerVariants = {
89
63
  },
90
64
  };
91
65
 
92
- export { getDirectionalVariants, timelineHorizontalItemVariants, timelineItemVariants, timelineMarkerVariants };
66
+ export { timelineHorizontalItemVariants, timelineItemVariants, timelineMarkerVariants };
@@ -14,7 +14,7 @@ const useTimelineContext = () => {
14
14
  throw new Error("Timeline compound components must be used within <Timeline>");
15
15
  return ctx;
16
16
  };
17
- const TimelineRoot = React__default.forwardRef(({ children, className, layout = "vertical", align = "left", theme: themeOverrides, ...props }, ref) => {
17
+ const TimelineRoot = React__default.forwardRef(({ children, className, layout = "vertical", theme: themeOverrides, ...props }, ref) => {
18
18
  // Merge theme with any overrides
19
19
  const theme = useMemo(() => ({
20
20
  ...timelineTheme,
@@ -29,12 +29,11 @@ const TimelineRoot = React__default.forwardRef(({ children, className, layout =
29
29
  // Context value to be provided to children
30
30
  const contextValue = useMemo(() => ({
31
31
  layout,
32
- align,
33
32
  theme,
34
33
  totalItems,
35
- }), [layout, align, theme, totalItems]);
34
+ }), [layout, theme, totalItems]);
36
35
  const isHorizontal = layout === "horizontal";
37
- return (jsx(TimelineContext.Provider, { value: contextValue, children: jsx("div", { ref: ref, className: cn(isHorizontal ? theme.baseStyleHorizontal : theme.baseStyle, className), "data-layout": layout, "data-align": align, role: "list", "aria-label": "Timeline", ...props, children: children }) }));
36
+ return (jsx(TimelineContext.Provider, { value: contextValue, children: jsx("div", { ref: ref, className: cn(isHorizontal ? theme.baseStyleHorizontal : theme.baseStyle, className), "data-layout": layout, role: "list", "aria-label": "Timeline", ...props, children: children }) }));
38
37
  });
39
38
  TimelineRoot.displayName = "Timeline";
40
39
  // Attach compound subcomponents
@@ -3,13 +3,6 @@ import React from "react";
3
3
  * Layout orientation for the Timeline
4
4
  */
5
5
  export type TimelineLayout = "vertical" | "horizontal";
6
- /**
7
- * Alignment mode for vertical timeline items
8
- * - left: all items on the left side of the line
9
- * - right: all items on the right side of the line
10
- * - alternate: items alternate between left and right
11
- */
12
- export type TimelineAlign = "left" | "right" | "alternate";
13
6
  /**
14
7
  * Status of an individual timeline item
15
8
  * - completed: item is finished (success color)
@@ -41,11 +34,6 @@ export interface TimelineProps extends React.HTMLAttributes<HTMLDivElement> {
41
34
  * @default 'vertical'
42
35
  */
43
36
  layout?: TimelineLayout;
44
- /**
45
- * Alignment of items relative to the timeline line (vertical only)
46
- * @default 'left'
47
- */
48
- align?: TimelineAlign;
49
37
  /**
50
38
  * Theme overrides for customizing styles
51
39
  */
@@ -60,7 +48,6 @@ export interface TimelineProps extends React.HTMLAttributes<HTMLDivElement> {
60
48
  */
61
49
  export interface TimelineContextValue {
62
50
  layout: TimelineLayout;
63
- align: TimelineAlign;
64
51
  theme: TimelineThemeOverrides & {
65
52
  baseStyle: string;
66
53
  baseStyleHorizontal: string;
@@ -5,16 +5,12 @@ import { useTimelineContext } from './Timeline.js';
5
5
  import { useTimelineItemContext } from './TimelineItem.js';
6
6
 
7
7
  const TimelineContent = React__default.forwardRef(({ children, className, ...props }, ref) => {
8
- const { theme, layout, align } = useTimelineContext();
9
- const { isLast, index } = useTimelineItemContext();
8
+ const { theme, layout } = useTimelineContext();
9
+ const { isLast } = useTimelineItemContext();
10
10
  const isHorizontal = layout === "horizontal";
11
- // For alternate layout, determine text alignment
12
- const isAlternateRight = align === "alternate" && index % 2 !== 0;
13
11
  return (jsx("div", { ref: ref, className: cn(isHorizontal ? theme.contentStyleHorizontal : theme.contentStyle,
14
12
  // Remove bottom padding on last item
15
- !isHorizontal && isLast && "pb-0",
16
- // Align text based on side in alternate mode
17
- !isHorizontal && isAlternateRight && "text-right", !isHorizontal && align === "right" && "text-right", className), ...props, children: children }));
13
+ !isHorizontal && isLast && "pb-0", className), ...props, children: children }));
18
14
  });
19
15
  TimelineContent.displayName = "TimelineContent";
20
16
 
@@ -3,7 +3,7 @@ import React__default, { createContext, useRef, useState, useEffect, useContext
3
3
  import { useReducedMotion, motion } from 'motion/react';
4
4
  import { cn } from '../../../utils/cn.js';
5
5
  import { useTimelineContext } from './Timeline.js';
6
- import { timelineHorizontalItemVariants, getDirectionalVariants, timelineItemVariants } from './Timeline.animations.js';
6
+ import { timelineHorizontalItemVariants, timelineItemVariants } from './Timeline.animations.js';
7
7
 
8
8
  // Create context for TimelineItem
9
9
  const TimelineItemContext = createContext(undefined);
@@ -15,7 +15,7 @@ const useTimelineItemContext = () => {
15
15
  return ctx;
16
16
  };
17
17
  const TimelineItem = React__default.forwardRef(({ children, className, status = "pending", ...props }, ref) => {
18
- const { layout, align, theme, totalItems } = useTimelineContext();
18
+ const { layout, theme, totalItems } = useTimelineContext();
19
19
  const shouldReduceMotion = useReducedMotion();
20
20
  const itemRef = useRef(null);
21
21
  const [isVisible, setIsVisible] = useState(false);
@@ -48,21 +48,12 @@ const TimelineItem = React__default.forwardRef(({ children, className, status =
48
48
  const isFirst = index === 0;
49
49
  const isLast = index === totalItems - 1;
50
50
  const isHorizontal = layout === "horizontal";
51
- // Determine which side this item is on for alternate layout
52
- const itemSide = align === "alternate"
53
- ? index % 2 === 0
54
- ? "left"
55
- : "right"
56
- : align;
57
- // Select animation variants based on layout and alignment
51
+ // Select animation variants based on layout
58
52
  const getVariants = () => {
59
53
  if (shouldReduceMotion)
60
54
  return undefined;
61
55
  if (isHorizontal)
62
56
  return timelineHorizontalItemVariants;
63
- if (align === "alternate") {
64
- return getDirectionalVariants(itemSide);
65
- }
66
57
  return timelineItemVariants;
67
58
  };
68
59
  // Context value for Marker and Content
@@ -72,14 +63,10 @@ const TimelineItem = React__default.forwardRef(({ children, className, status =
72
63
  isFirst,
73
64
  isLast,
74
65
  };
75
- // Build className based on layout
76
- const isAlternateRight = align === "alternate" && index % 2 !== 0;
77
66
  // Filter out conflicting motion props from rest
78
67
  const { onDrag, onDragEnd, onDragStart, onAnimationStart, onAnimationEnd, ...safeProps } = props;
79
68
  const variants = getVariants();
80
- return (jsx(TimelineItemContext.Provider, { value: contextValue, children: jsx(motion.div, { ref: ref || itemRef, className: cn(isHorizontal ? theme.itemStyleHorizontal : theme.itemStyle,
81
- // For right-aligned or alternate-right, reverse the flex direction
82
- !isHorizontal && align === "right" && "flex-row-reverse", !isHorizontal && isAlternateRight && "flex-row-reverse", className), "data-status": status, "data-side": itemSide, "data-layout": layout, role: "listitem", initial: variants ? "hidden" : undefined, animate: variants && isVisible ? "visible" : variants ? "hidden" : undefined, variants: variants, ...safeProps, children: children }) }));
69
+ return (jsx(TimelineItemContext.Provider, { value: contextValue, children: jsx(motion.div, { ref: ref || itemRef, className: cn(isHorizontal ? theme.itemStyleHorizontal : theme.itemStyle, className), "data-status": status, "data-layout": layout, role: "listitem", initial: variants ? "hidden" : undefined, animate: variants && isVisible ? "visible" : variants ? "hidden" : undefined, variants: variants, ...safeProps, children: children }) }));
83
70
  });
84
71
  TimelineItem.displayName = "TimelineItem";
85
72