@basic-ui/core 0.0.60 → 0.0.61

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 (214) hide show
  1. package/build/cjs/index.js.map +1 -1
  2. package/build/esm/Accordion/AccordionBody.d.ts.map +1 -1
  3. package/build/esm/Accordion/AccordionBody.js +6 -26
  4. package/build/esm/Accordion/AccordionBody.js.map +1 -1
  5. package/build/esm/Accordion/AccordionHeader.d.ts.map +1 -1
  6. package/build/esm/Accordion/AccordionHeader.js +21 -69
  7. package/build/esm/Accordion/AccordionHeader.js.map +1 -1
  8. package/build/esm/Accordion/AccordionItem.d.ts.map +1 -1
  9. package/build/esm/Accordion/AccordionItem.js +31 -18
  10. package/build/esm/Accordion/AccordionItem.js.map +1 -1
  11. package/build/esm/Accordion/context.d.ts +0 -8
  12. package/build/esm/Accordion/context.d.ts.map +1 -1
  13. package/build/esm/Accordion/context.js +0 -11
  14. package/build/esm/Accordion/context.js.map +1 -1
  15. package/build/esm/Accordion/scopeQuery.d.ts +1 -0
  16. package/build/esm/Accordion/scopeQuery.d.ts.map +1 -1
  17. package/build/esm/Accordion/scopeQuery.js +3 -0
  18. package/build/esm/Accordion/scopeQuery.js.map +1 -1
  19. package/build/esm/Collapsible/Collapsible.d.ts +13 -0
  20. package/build/esm/Collapsible/Collapsible.d.ts.map +1 -0
  21. package/build/esm/Collapsible/Collapsible.js +53 -0
  22. package/build/esm/Collapsible/Collapsible.js.map +1 -0
  23. package/build/esm/Collapsible/CollapsiblePanel.d.ts +10 -0
  24. package/build/esm/Collapsible/CollapsiblePanel.d.ts.map +1 -0
  25. package/build/esm/Collapsible/CollapsiblePanel.js +85 -0
  26. package/build/esm/Collapsible/CollapsiblePanel.js.map +1 -0
  27. package/build/esm/Collapsible/CollapsibleTrigger.d.ts +11 -0
  28. package/build/esm/Collapsible/CollapsibleTrigger.d.ts.map +1 -0
  29. package/build/esm/Collapsible/CollapsibleTrigger.js +51 -0
  30. package/build/esm/Collapsible/CollapsibleTrigger.js.map +1 -0
  31. package/build/esm/Collapsible/context.d.ts +16 -0
  32. package/build/esm/Collapsible/context.d.ts.map +1 -0
  33. package/build/esm/Collapsible/context.js +11 -0
  34. package/build/esm/Collapsible/context.js.map +1 -0
  35. package/build/esm/Collapsible/index.d.ts +4 -0
  36. package/build/esm/Collapsible/index.d.ts.map +1 -0
  37. package/build/esm/Collapsible/index.js +4 -0
  38. package/build/esm/Collapsible/index.js.map +1 -0
  39. package/build/esm/Menu/Menu.d.ts +3 -2
  40. package/build/esm/Menu/Menu.d.ts.map +1 -1
  41. package/build/esm/Menu/Menu.js +64 -4
  42. package/build/esm/Menu/Menu.js.map +1 -1
  43. package/build/esm/Menu/MenuButton.d.ts.map +1 -1
  44. package/build/esm/Menu/MenuButton.js +85 -8
  45. package/build/esm/Menu/MenuButton.js.map +1 -1
  46. package/build/esm/Menu/MenuItem.d.ts.map +1 -1
  47. package/build/esm/Menu/MenuItem.js +16 -4
  48. package/build/esm/Menu/MenuItem.js.map +1 -1
  49. package/build/esm/Menu/MenuList.d.ts.map +1 -1
  50. package/build/esm/Menu/MenuList.js +47 -12
  51. package/build/esm/Menu/MenuList.js.map +1 -1
  52. package/build/esm/Menu/MenuPopover.d.ts.map +1 -1
  53. package/build/esm/Menu/MenuPopover.js +12 -1
  54. package/build/esm/Menu/MenuPopover.js.map +1 -1
  55. package/build/esm/Menu/MenuSubmenuTrigger.d.ts +8 -0
  56. package/build/esm/Menu/MenuSubmenuTrigger.d.ts.map +1 -0
  57. package/build/esm/Menu/MenuSubmenuTrigger.js +131 -0
  58. package/build/esm/Menu/MenuSubmenuTrigger.js.map +1 -0
  59. package/build/esm/Menu/context.d.ts +13 -3
  60. package/build/esm/Menu/context.d.ts.map +1 -1
  61. package/build/esm/Menu/context.js +1 -0
  62. package/build/esm/Menu/context.js.map +1 -1
  63. package/build/esm/Menu/index.d.ts +3 -0
  64. package/build/esm/Menu/index.d.ts.map +1 -1
  65. package/build/esm/Menu/index.js +2 -0
  66. package/build/esm/Menu/index.js.map +1 -1
  67. package/build/esm/Menu/scope.d.ts +1 -0
  68. package/build/esm/Menu/scope.d.ts.map +1 -1
  69. package/build/esm/Menu/scope.js +2 -1
  70. package/build/esm/Menu/scope.js.map +1 -1
  71. package/build/esm/MenuBar/MenuBar.d.ts +11 -0
  72. package/build/esm/MenuBar/MenuBar.d.ts.map +1 -0
  73. package/build/esm/MenuBar/MenuBar.js +153 -0
  74. package/build/esm/MenuBar/MenuBar.js.map +1 -0
  75. package/build/esm/MenuBar/context.d.ts +29 -0
  76. package/build/esm/MenuBar/context.d.ts.map +1 -0
  77. package/build/esm/MenuBar/context.js +7 -0
  78. package/build/esm/MenuBar/context.js.map +1 -0
  79. package/build/esm/MenuBar/index.d.ts +2 -0
  80. package/build/esm/MenuBar/index.d.ts.map +1 -0
  81. package/build/esm/MenuBar/index.js +2 -0
  82. package/build/esm/MenuBar/index.js.map +1 -0
  83. package/build/esm/Slider/Slider.d.ts +47 -1
  84. package/build/esm/Slider/Slider.d.ts.map +1 -1
  85. package/build/esm/Slider/Slider.js +91 -5
  86. package/build/esm/Slider/Slider.js.map +1 -1
  87. package/build/esm/ToggleGroup/ToggleGroup.d.ts +40 -0
  88. package/build/esm/ToggleGroup/ToggleGroup.d.ts.map +1 -0
  89. package/build/esm/ToggleGroup/ToggleGroup.js +113 -0
  90. package/build/esm/ToggleGroup/ToggleGroup.js.map +1 -0
  91. package/build/esm/ToggleGroup/ToggleGroupContext.d.ts +10 -0
  92. package/build/esm/ToggleGroup/ToggleGroupContext.d.ts.map +1 -0
  93. package/build/esm/ToggleGroup/ToggleGroupContext.js +6 -0
  94. package/build/esm/ToggleGroup/ToggleGroupContext.js.map +1 -0
  95. package/build/esm/ToggleGroup/index.d.ts +3 -0
  96. package/build/esm/ToggleGroup/index.d.ts.map +1 -0
  97. package/build/esm/ToggleGroup/index.js +3 -0
  98. package/build/esm/ToggleGroup/index.js.map +1 -0
  99. package/build/esm/Tree/Tree.d.ts +3 -0
  100. package/build/esm/Tree/Tree.d.ts.map +1 -0
  101. package/build/esm/Tree/Tree.js +730 -0
  102. package/build/esm/Tree/Tree.js.map +1 -0
  103. package/build/esm/Tree/TreeHeader.d.ts +3 -0
  104. package/build/esm/Tree/TreeHeader.d.ts.map +1 -0
  105. package/build/esm/Tree/TreeHeader.js +5 -0
  106. package/build/esm/Tree/TreeHeader.js.map +1 -0
  107. package/build/esm/Tree/TreeItem.d.ts +3 -0
  108. package/build/esm/Tree/TreeItem.d.ts.map +1 -0
  109. package/build/esm/Tree/TreeItem.js +5 -0
  110. package/build/esm/Tree/TreeItem.js.map +1 -0
  111. package/build/esm/Tree/TreeItemContent.d.ts +3 -0
  112. package/build/esm/Tree/TreeItemContent.d.ts.map +1 -0
  113. package/build/esm/Tree/TreeItemContent.js +69 -0
  114. package/build/esm/Tree/TreeItemContent.js.map +1 -0
  115. package/build/esm/Tree/TreeSection.d.ts +3 -0
  116. package/build/esm/Tree/TreeSection.d.ts.map +1 -0
  117. package/build/esm/Tree/TreeSection.js +5 -0
  118. package/build/esm/Tree/TreeSection.js.map +1 -0
  119. package/build/esm/Tree/collection.d.ts +18 -0
  120. package/build/esm/Tree/collection.d.ts.map +1 -0
  121. package/build/esm/Tree/collection.js +252 -0
  122. package/build/esm/Tree/collection.js.map +1 -0
  123. package/build/esm/Tree/context.d.ts +3 -0
  124. package/build/esm/Tree/context.d.ts.map +1 -0
  125. package/build/esm/Tree/context.js +3 -0
  126. package/build/esm/Tree/context.js.map +1 -0
  127. package/build/esm/Tree/index.d.ts +8 -0
  128. package/build/esm/Tree/index.d.ts.map +1 -0
  129. package/build/esm/Tree/index.js +7 -0
  130. package/build/esm/Tree/index.js.map +1 -0
  131. package/build/esm/Tree/types.d.ts +128 -0
  132. package/build/esm/Tree/types.d.ts.map +1 -0
  133. package/build/esm/Tree/types.js +2 -0
  134. package/build/esm/Tree/types.js.map +1 -0
  135. package/build/esm/hooks/index.d.ts +1 -0
  136. package/build/esm/hooks/index.d.ts.map +1 -1
  137. package/build/esm/hooks/index.js +1 -0
  138. package/build/esm/hooks/index.js.map +1 -1
  139. package/build/esm/hooks/useTransitionStatus.d.ts +7 -0
  140. package/build/esm/hooks/useTransitionStatus.d.ts.map +1 -0
  141. package/build/esm/hooks/useTransitionStatus.js +48 -0
  142. package/build/esm/hooks/useTransitionStatus.js.map +1 -0
  143. package/build/esm/index.d.ts +5 -0
  144. package/build/esm/index.d.ts.map +1 -1
  145. package/build/esm/index.js +5 -0
  146. package/build/esm/index.js.map +1 -1
  147. package/build/esm/toggle/Toggle.d.ts +28 -0
  148. package/build/esm/toggle/Toggle.d.ts.map +1 -0
  149. package/build/esm/toggle/Toggle.js +55 -0
  150. package/build/esm/toggle/Toggle.js.map +1 -0
  151. package/build/esm/toggle/index.d.ts +2 -0
  152. package/build/esm/toggle/index.d.ts.map +1 -0
  153. package/build/esm/toggle/index.js +2 -0
  154. package/build/esm/toggle/index.js.map +1 -0
  155. package/build/esm/utils/assign-ref.d.ts +3 -3
  156. package/build/esm/utils/assign-ref.d.ts.map +1 -1
  157. package/build/esm/utils/assign-ref.js +1 -1
  158. package/build/esm/utils/assign-ref.js.map +1 -1
  159. package/build/tsconfig-build.tsbuildinfo +1 -1
  160. package/build/tsconfig.tsbuildinfo +1 -1
  161. package/package.json +7 -4
  162. package/src/Accordion/AccordionBody.tsx +6 -35
  163. package/src/Accordion/AccordionHeader.tsx +29 -103
  164. package/src/Accordion/AccordionItem.tsx +40 -29
  165. package/src/Accordion/context.ts +0 -18
  166. package/src/Accordion/scopeQuery.ts +4 -0
  167. package/src/Collapsible/Collapsible.story.tsx +153 -0
  168. package/src/Collapsible/Collapsible.tsx +79 -0
  169. package/src/Collapsible/CollapsiblePanel.tsx +103 -0
  170. package/src/Collapsible/CollapsibleTrigger.tsx +60 -0
  171. package/src/Collapsible/context.ts +28 -0
  172. package/src/Collapsible/index.ts +3 -0
  173. package/src/Menu/Menu.story.tsx +70 -1
  174. package/src/Menu/Menu.tsx +141 -65
  175. package/src/Menu/MenuButton.tsx +115 -9
  176. package/src/Menu/MenuItem.tsx +20 -3
  177. package/src/Menu/MenuList.tsx +50 -13
  178. package/src/Menu/MenuPopover.tsx +12 -2
  179. package/src/Menu/MenuSubmenuTrigger.tsx +167 -0
  180. package/src/Menu/context.ts +20 -10
  181. package/src/Menu/index.ts +3 -0
  182. package/src/Menu/scope.ts +4 -1
  183. package/src/Menu/styles.css +57 -22
  184. package/src/MenuBar/MenuBar.story.tsx +92 -0
  185. package/src/MenuBar/MenuBar.tsx +236 -0
  186. package/src/MenuBar/context.ts +46 -0
  187. package/src/MenuBar/index.ts +1 -0
  188. package/src/MenuBar/styles.css +78 -0
  189. package/src/Slider/Slider.story.tsx +1 -1
  190. package/src/Slider/Slider.tsx +145 -8
  191. package/src/Toggle/Toggle.story.tsx +42 -0
  192. package/src/Toggle/Toggle.tsx +95 -0
  193. package/src/Toggle/index.ts +1 -0
  194. package/src/Toggle/styles.css +39 -0
  195. package/src/ToggleGroup/ToggleGroup.story.tsx +86 -0
  196. package/src/ToggleGroup/ToggleGroup.tsx +185 -0
  197. package/src/ToggleGroup/ToggleGroupContext.ts +17 -0
  198. package/src/ToggleGroup/index.ts +2 -0
  199. package/src/ToggleGroup/styles.css +66 -0
  200. package/src/Tree/Tree.story.tsx +221 -0
  201. package/src/Tree/Tree.tsx +1081 -0
  202. package/src/Tree/TreeHeader.tsx +9 -0
  203. package/src/Tree/TreeItem.tsx +9 -0
  204. package/src/Tree/TreeItemContent.tsx +91 -0
  205. package/src/Tree/TreeSection.tsx +9 -0
  206. package/src/Tree/collection.tsx +371 -0
  207. package/src/Tree/context.ts +6 -0
  208. package/src/Tree/index.ts +7 -0
  209. package/src/Tree/styles.css +135 -0
  210. package/src/Tree/types.ts +161 -0
  211. package/src/hooks/index.ts +1 -0
  212. package/src/hooks/useTransitionStatus.ts +65 -0
  213. package/src/index.ts +5 -0
  214. package/src/utils/assign-ref.ts +4 -4
@@ -0,0 +1,730 @@
1
+ import { forwardRef, useCallback, useEffect, useId, useMemo, useRef, useState, createElement as _createElement } from 'react';
2
+ import { flattenAllItems, parseTreeChildren, shouldRenderLoadingRow, useTreeCollection } from './collection';
3
+ import { TreeItemRenderContext } from './context';
4
+ import { assignMultipleRefs, wrapEvent } from '../utils';
5
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
6
+ const TYPEAHEAD_TIMEOUT = 500;
7
+ export const Tree = /*#__PURE__*/forwardRef(function Tree(props, forwardedRef) {
8
+ const {
9
+ as: Comp = 'div',
10
+ innerAs,
11
+ children,
12
+ selectionMode = 'none',
13
+ selectionBehavior = 'toggle',
14
+ selectedKeys: selectedKeysProp,
15
+ defaultSelectedKeys,
16
+ onSelectionChange,
17
+ disallowEmptySelection = false,
18
+ disabledKeys: disabledKeysProp,
19
+ disabledBehavior = 'all',
20
+ expandedKeys: expandedKeysProp,
21
+ defaultExpandedKeys,
22
+ onExpandedChange,
23
+ autoFocus = false,
24
+ onAction,
25
+ keyboardNavigationBehavior = 'arrow',
26
+ escapeKeyBehavior = 'clearSelection',
27
+ shouldFocusWrap = false,
28
+ disallowTypeAhead = false,
29
+ disallowSelectAll = false,
30
+ direction: directionProp,
31
+ renderEmptyState,
32
+ renderLoadingState,
33
+ renderedRowIndices,
34
+ onVisibleRowsChange,
35
+ onFocusedKeyChange,
36
+ renderRow,
37
+ onKeyDown,
38
+ onFocus,
39
+ onBlur,
40
+ onMouseDown,
41
+ id: idProp,
42
+ tabIndex,
43
+ ...otherProps
44
+ } = props;
45
+ const generatedId = useId();
46
+ const treeId = idProp ?? generatedId;
47
+ const rootRef = useRef(null);
48
+ const itemRefs = useRef(new Map());
49
+ const typeaheadStringRef = useRef('');
50
+ const typeaheadTimeoutRef = useRef(undefined);
51
+ const [isFocused, setFocused] = useState(false);
52
+ const [isFocusVisible, setFocusVisible] = useState(false);
53
+ const [focusedKey, setFocusedKey] = useState(null);
54
+ const [pressedKey, setPressedKey] = useState(null);
55
+ const lastSelectedKeyRef = useRef(null);
56
+ const disabledKeys = useMemo(() => new Set(disabledKeysProp ?? []), [disabledKeysProp]);
57
+ const parsedTree = useMemo(() => parseTreeChildren(children), [children]);
58
+ const allItems = useMemo(() => flattenAllItems(parsedTree), [parsedTree]);
59
+ const allItemKeys = useMemo(() => allItems.map(item => item.key), [allItems]);
60
+ const expandableKeys = useMemo(() => allItems.filter(item => item.hasChildItems || item.childItems.length > 0).map(item => item.key), [allItems]);
61
+ const [uncontrolledExpandedKeys, setUncontrolledExpandedKeys] = useState(() => makeKeySet(defaultExpandedKeys, expandableKeys));
62
+ const expandedKeys = expandedKeysProp !== undefined ? makeKeySet(expandedKeysProp, expandableKeys) : uncontrolledExpandedKeys;
63
+ const [uncontrolledSelectedKeys, setUncontrolledSelectedKeys] = useState(() => makeKeySet(defaultSelectedKeys, allItemKeys));
64
+ const selectedKeys = selectedKeysProp !== undefined ? makeKeySet(selectedKeysProp, allItemKeys) : uncontrolledSelectedKeys;
65
+ const treeCollection = useTreeCollection(children, {
66
+ expandedKeys,
67
+ renderLoadingState
68
+ });
69
+ const visibleItems = treeCollection.visibleItems;
70
+ const visibleRows = treeCollection.visibleRows;
71
+ const focusableItems = useMemo(() => visibleItems.filter(item => !(disabledBehavior === 'all' && isItemDisabled(item, disabledKeys))), [disabledBehavior, disabledKeys, visibleItems]);
72
+ const direction = directionProp ?? (typeof document !== 'undefined' && document.dir === 'rtl' ? 'rtl' : 'ltr');
73
+ const expandKey = direction === 'rtl' ? 'ArrowLeft' : 'ArrowRight';
74
+ const collapseKey = direction === 'rtl' ? 'ArrowRight' : 'ArrowLeft';
75
+ const setExpandedKeys = useCallback(keys => {
76
+ if (expandedKeysProp === undefined) {
77
+ setUncontrolledExpandedKeys(keys);
78
+ }
79
+ onExpandedChange?.(new Set(keys));
80
+ }, [expandedKeysProp, onExpandedChange]);
81
+ const setSelectedKeys = useCallback(keys => {
82
+ if (selectedKeysProp === undefined) {
83
+ setUncontrolledSelectedKeys(keys);
84
+ }
85
+ onSelectionChange?.(new Set(keys));
86
+ }, [onSelectionChange, selectedKeysProp]);
87
+ const getItemByKey = useCallback(key => key == null ? undefined : allItems.find(item => item.key === key), [allItems]);
88
+ const canSelectItem = useCallback(item => selectionMode !== 'none' && !isItemDisabled(item, disabledKeys), [disabledKeys, selectionMode]);
89
+ const focusItem = useCallback(key => {
90
+ setFocusedKey(key);
91
+ if (key == null) {
92
+ onFocusedKeyChange?.(null, null);
93
+ return;
94
+ }
95
+ const rowIndex = visibleRows.findIndex(row => row.type === 'item' && row.item.key === key);
96
+ onFocusedKeyChange?.(key, rowIndex === -1 ? null : rowIndex);
97
+ requestAnimationFrame(() => {
98
+ itemRefs.current.get(key)?.focus({
99
+ preventScroll: true
100
+ });
101
+ });
102
+ }, [onFocusedKeyChange, visibleRows]);
103
+ const updateSelection = useCallback((item, event, options) => {
104
+ if (!canSelectItem(item)) {
105
+ return;
106
+ }
107
+ let next = new Set(selectedKeys);
108
+ const isSelected = next.has(item.key);
109
+ if (selectionMode === 'single') {
110
+ if (isSelected && !disallowEmptySelection) {
111
+ next.delete(item.key);
112
+ } else {
113
+ next = new Set([item.key]);
114
+ }
115
+ } else if (selectionMode === 'multiple') {
116
+ if (options?.range && lastSelectedKeyRef.current != null && visibleItems.some(visibleItem => visibleItem.key === lastSelectedKeyRef.current)) {
117
+ next = addRangeToSelection(visibleItems, lastSelectedKeyRef.current, item.key, selectedKeys, candidate => canSelectItem(candidate));
118
+ } else if (options?.forceToggle || selectionBehavior === 'toggle' || event?.ctrlKey || event?.metaKey) {
119
+ if (isSelected) {
120
+ if (!disallowEmptySelection || next.size > 1) {
121
+ next.delete(item.key);
122
+ }
123
+ } else {
124
+ next.add(item.key);
125
+ }
126
+ } else {
127
+ next = new Set([item.key]);
128
+ }
129
+ }
130
+ lastSelectedKeyRef.current = item.key;
131
+ setSelectedKeys(next);
132
+ }, [canSelectItem, disallowEmptySelection, selectedKeys, selectionBehavior, selectionMode, setSelectedKeys, visibleItems]);
133
+ const toggleExpanded = useCallback(item => {
134
+ if (!item.hasChildItems && item.childItems.length === 0) {
135
+ return;
136
+ }
137
+ const next = new Set(expandedKeys);
138
+ if (next.has(item.key)) {
139
+ next.delete(item.key);
140
+ } else {
141
+ next.add(item.key);
142
+ }
143
+ setExpandedKeys(next);
144
+ }, [expandedKeys, setExpandedKeys]);
145
+ const navigateByIndex = useCallback((currentKey, delta) => {
146
+ if (focusableItems.length === 0) {
147
+ return;
148
+ }
149
+ const currentIndex = focusableItems.findIndex(item => item.key === currentKey);
150
+ let nextIndex = currentIndex === -1 ? delta > 0 ? 0 : -1 : currentIndex + delta;
151
+ if (nextIndex < 0 || nextIndex >= focusableItems.length) {
152
+ if (!shouldFocusWrap) {
153
+ return;
154
+ }
155
+ nextIndex = (nextIndex % focusableItems.length + focusableItems.length) % focusableItems.length;
156
+ }
157
+ focusItem(focusableItems[nextIndex].key);
158
+ }, [focusItem, focusableItems, shouldFocusWrap]);
159
+ const navigateToEdge = useCallback(edge => {
160
+ const item = edge === 'first' ? focusableItems[0] : focusableItems[focusableItems.length - 1];
161
+ if (item) {
162
+ focusItem(item.key);
163
+ }
164
+ }, [focusItem, focusableItems]);
165
+ const handleTypeahead = useCallback(event => {
166
+ if (disallowTypeAhead || event.key.length !== 1 || event.ctrlKey || event.metaKey || event.altKey) {
167
+ return false;
168
+ }
169
+ event.preventDefault();
170
+ const key = event.key.toLocaleLowerCase();
171
+ if (typeaheadStringRef.current.length === 0 || typeaheadStringRef.current.slice(-1) !== key) {
172
+ typeaheadStringRef.current += key;
173
+ }
174
+ clearTimeout(typeaheadTimeoutRef.current);
175
+ typeaheadTimeoutRef.current = setTimeout(() => {
176
+ typeaheadStringRef.current = '';
177
+ }, TYPEAHEAD_TIMEOUT);
178
+ const search = typeaheadStringRef.current;
179
+ const currentIndex = focusableItems.findIndex(item => item.key === focusedKey);
180
+ for (let i = search.length === 1 ? 1 : 0; i <= focusableItems.length; i++) {
181
+ const index = ((currentIndex + i) % focusableItems.length + focusableItems.length) % focusableItems.length;
182
+ const item = focusableItems[index];
183
+ if (item?.textValue.toLocaleLowerCase().startsWith(search)) {
184
+ focusItem(item.key);
185
+ return true;
186
+ }
187
+ }
188
+ return true;
189
+ }, [disallowTypeAhead, focusItem, focusableItems, focusedKey]);
190
+ const handleKeyDown = event => {
191
+ const target = event.target;
192
+ if (!(target instanceof Node) || !rootRef.current?.contains(target)) {
193
+ return;
194
+ }
195
+ setFocusVisible(true);
196
+ switch (event.key) {
197
+ case 'ArrowDown':
198
+ event.preventDefault();
199
+ navigateByIndex(focusedKey, 1);
200
+ break;
201
+ case 'ArrowUp':
202
+ event.preventDefault();
203
+ navigateByIndex(focusedKey, -1);
204
+ break;
205
+ case 'Home':
206
+ event.preventDefault();
207
+ navigateToEdge('first');
208
+ break;
209
+ case 'End':
210
+ event.preventDefault();
211
+ navigateToEdge('last');
212
+ break;
213
+ case 'a':
214
+ if ((event.ctrlKey || event.metaKey) && selectionMode === 'multiple' && !disallowSelectAll) {
215
+ event.preventDefault();
216
+ setSelectedKeys(new Set(allItems.filter(item => canSelectItem(item)).map(item => item.key)));
217
+ } else {
218
+ handleTypeahead(event);
219
+ }
220
+ break;
221
+ case 'Escape':
222
+ if (escapeKeyBehavior === 'clearSelection' && !disallowEmptySelection && selectedKeys.size > 0) {
223
+ event.preventDefault();
224
+ event.stopPropagation();
225
+ setSelectedKeys(new Set());
226
+ }
227
+ break;
228
+ case ' ':
229
+ if (focusedKey != null && event.target === itemRefs.current.get(focusedKey)) {
230
+ const item = getItemByKey(focusedKey);
231
+ if (item) {
232
+ event.preventDefault();
233
+ updateSelection(item, event, {
234
+ forceToggle: true,
235
+ range: event.shiftKey
236
+ });
237
+ }
238
+ }
239
+ break;
240
+ case 'Enter':
241
+ if (focusedKey != null && event.target === itemRefs.current.get(focusedKey)) {
242
+ const item = getItemByKey(focusedKey);
243
+ if (item && !isInteractionDisabled(item, disabledKeys, disabledBehavior)) {
244
+ event.preventDefault();
245
+ if (item.props.onAction) {
246
+ item.props.onAction();
247
+ } else if (onAction) {
248
+ onAction(item.key);
249
+ } else if (selectionMode === 'none' && item.hasChildItems) {
250
+ toggleExpanded(item);
251
+ }
252
+ }
253
+ }
254
+ break;
255
+ default:
256
+ handleTypeahead(event);
257
+ break;
258
+ }
259
+ };
260
+ const handleFocus = event => {
261
+ if (!rootRef.current?.contains(event.target)) {
262
+ return;
263
+ }
264
+ setFocused(true);
265
+ if (event.target === rootRef.current) {
266
+ const key = focusedKey ?? firstVisibleSelectedKey(visibleItems, selectedKeys) ?? focusableItems[0]?.key ?? null;
267
+ focusItem(key);
268
+ }
269
+ };
270
+ const handleBlur = event => {
271
+ if (event.relatedTarget instanceof Node && rootRef.current?.contains(event.relatedTarget)) {
272
+ return;
273
+ }
274
+ setFocused(false);
275
+ };
276
+ const handleMouseDown = () => {
277
+ setFocusVisible(false);
278
+ };
279
+ useEffect(() => {
280
+ if (focusedKey != null && !focusableItems.some(item => item.key === focusedKey)) {
281
+ setFocusedKey(focusableItems[0]?.key ?? null);
282
+ }
283
+ }, [focusableItems, focusedKey]);
284
+ useEffect(() => {
285
+ onVisibleRowsChange?.(visibleRows);
286
+ }, [onVisibleRowsChange, visibleRows]);
287
+ useEffect(() => {
288
+ if (!autoFocus) {
289
+ return;
290
+ }
291
+ const key = autoFocus === 'last' ? focusableItems[focusableItems.length - 1]?.key : firstVisibleSelectedKey(visibleItems, selectedKeys) ?? focusableItems[0]?.key;
292
+ if (key != null) {
293
+ setFocused(true);
294
+ focusItem(key);
295
+ } else {
296
+ rootRef.current?.focus();
297
+ }
298
+ // eslint-disable-next-line react-hooks/exhaustive-deps
299
+ }, []);
300
+ useEffect(() => {
301
+ return () => {
302
+ clearTimeout(typeaheadTimeoutRef.current);
303
+ };
304
+ }, []);
305
+ if (process.env.NODE_ENV !== 'production') {
306
+ if (!otherProps['aria-label'] && !otherProps['aria-labelledby']) {
307
+ console.warn('An aria-label or aria-labelledby prop is required for <Tree> accessibility.');
308
+ }
309
+ }
310
+ const renderState = {
311
+ allItems,
312
+ visibleItems,
313
+ visibleRows,
314
+ focusedKey,
315
+ expandedKeys,
316
+ selectedKeys,
317
+ selectionMode,
318
+ selectionBehavior
319
+ };
320
+ const renderItem = function (item) {
321
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
322
+ const isDisabled = isInteractionDisabled(item, disabledKeys, disabledBehavior);
323
+ const isExpanded = expandedKeys.has(item.key);
324
+ const isSelected = selectedKeys.has(item.key);
325
+ const rowId = `${treeId}-row-${sanitizeId(item.keyString)}`;
326
+ const rowProps = getItemDOMProps(item.props);
327
+ const canSelect = canSelectItem(item);
328
+ const itemOnClick = rowProps.onClick;
329
+ const itemOnDoubleClick = rowProps.onDoubleClick;
330
+ const itemOnFocus = rowProps.onFocus;
331
+ const itemOnKeyDownCapture = rowProps.onKeyDownCapture;
332
+ const itemOnMouseDown = rowProps.onMouseDown;
333
+ const itemOnMouseUp = rowProps.onMouseUp;
334
+ const itemOnMouseLeave = rowProps.onMouseLeave;
335
+ const itemOnKeyDown = rowProps.onKeyDown;
336
+ const contextValue = {
337
+ item,
338
+ rowId,
339
+ isExpanded,
340
+ isSelected,
341
+ isDisabled,
342
+ canSelect,
343
+ selectionMode,
344
+ selectionBehavior,
345
+ toggleExpanded: event => {
346
+ event.preventDefault();
347
+ event.stopPropagation();
348
+ if (!isDisabled) {
349
+ toggleExpanded(item);
350
+ focusItem(item.key);
351
+ }
352
+ },
353
+ toggleSelection: event => {
354
+ event.stopPropagation();
355
+ updateSelection(item, event, {
356
+ forceToggle: true
357
+ });
358
+ focusItem(item.key);
359
+ }
360
+ };
361
+ const handleRowKeyDownCapture = event => {
362
+ if (!rootRef.current || !itemRefs.current.get(item.key)) {
363
+ return;
364
+ }
365
+ const row = itemRefs.current.get(item.key);
366
+ const activeElement = getActiveElement(row);
367
+ if (activeElement === row) {
368
+ if (event.key === expandKey && item.hasChildItems && !isExpanded && !isDisabled) {
369
+ event.preventDefault();
370
+ event.stopPropagation();
371
+ toggleExpanded(item);
372
+ } else if (event.key === collapseKey) {
373
+ if (item.hasChildItems && isExpanded && !isDisabled) {
374
+ event.preventDefault();
375
+ event.stopPropagation();
376
+ toggleExpanded(item);
377
+ } else if (item.parentKey != null) {
378
+ event.preventDefault();
379
+ event.stopPropagation();
380
+ focusItem(item.parentKey);
381
+ }
382
+ }
383
+ return;
384
+ }
385
+ if (keyboardNavigationBehavior === 'arrow' && activeElement instanceof HTMLElement && row.contains(activeElement) && (event.key === 'ArrowLeft' || event.key === 'ArrowRight')) {
386
+ const focusables = getFocusableElements(row);
387
+ const currentIndex = focusables.indexOf(activeElement);
388
+ const delta = event.key === 'ArrowRight' && direction === 'ltr' || event.key === 'ArrowLeft' && direction === 'rtl' ? 1 : -1;
389
+ const next = focusables[currentIndex + delta] ?? row;
390
+ event.preventDefault();
391
+ event.stopPropagation();
392
+ next.focus({
393
+ preventScroll: true
394
+ });
395
+ }
396
+ };
397
+ const handleRowClick = event => {
398
+ if (isDisabled || isInteractiveEventTarget(event, itemRefs.current.get(item.key))) {
399
+ return;
400
+ }
401
+ focusItem(item.key);
402
+ if (selectionMode === 'none') {
403
+ if (item.props.onAction) {
404
+ item.props.onAction();
405
+ } else if (onAction) {
406
+ onAction(item.key);
407
+ } else if (item.hasChildItems) {
408
+ toggleExpanded(item);
409
+ }
410
+ return;
411
+ }
412
+ updateSelection(item, event, {
413
+ range: event.shiftKey
414
+ });
415
+ };
416
+ const handleRowDoubleClick = () => {
417
+ if (selectionBehavior === 'replace' && !isDisabled && (item.props.onAction || onAction)) {
418
+ item.props.onAction?.();
419
+ if (!item.props.onAction) {
420
+ onAction?.(item.key);
421
+ }
422
+ }
423
+ };
424
+ const handleRowFocus = () => {
425
+ setFocusedKey(item.key);
426
+ };
427
+ return /*#__PURE__*/_jsx(TreeItemRenderContext.Provider, {
428
+ value: contextValue,
429
+ children: /*#__PURE__*/_jsx("div", {
430
+ ...rowProps,
431
+ ref: node => {
432
+ options.measureElement?.(node);
433
+ if (node) {
434
+ itemRefs.current.set(item.key, node);
435
+ } else {
436
+ itemRefs.current.delete(item.key);
437
+ }
438
+ },
439
+ id: rowId,
440
+ role: "row",
441
+ "data-index": options.virtualIndex,
442
+ "aria-rowindex": options.rowIndex,
443
+ tabIndex: focusedKey === item.key ? 0 : -1,
444
+ "aria-label": (item.props['aria-label'] ?? item.textValue) || undefined,
445
+ style: {
446
+ ...rowProps.style,
447
+ '--tree-item-level': item.level
448
+ },
449
+ "aria-expanded": item.hasChildItems ? isExpanded : undefined,
450
+ "aria-level": item.level,
451
+ "aria-posinset": item.posInSet,
452
+ "aria-setsize": item.setSize,
453
+ "aria-selected": canSelect ? isSelected : undefined,
454
+ "aria-disabled": isDisabled || undefined,
455
+ "data-tree-item": "",
456
+ "data-key": item.keyString,
457
+ "data-expanded": item.hasChildItems && isExpanded ? '' : undefined,
458
+ "data-has-child-items": item.hasChildItems ? '' : undefined,
459
+ "data-level": item.level,
460
+ "data-selected": isSelected ? '' : undefined,
461
+ "data-disabled": isDisabled ? '' : undefined,
462
+ "data-focused": focusedKey === item.key && isFocused ? '' : undefined,
463
+ "data-focus-visible": focusedKey === item.key && isFocusVisible ? '' : undefined,
464
+ "data-pressed": pressedKey === item.key ? '' : undefined,
465
+ "data-selection-mode": selectionMode === 'none' ? undefined : selectionMode,
466
+ onClick: wrapEvent(itemOnClick, handleRowClick),
467
+ onDoubleClick: wrapEvent(itemOnDoubleClick, handleRowDoubleClick),
468
+ onFocus: wrapEvent(itemOnFocus, handleRowFocus),
469
+ onKeyDownCapture: wrapEvent(itemOnKeyDownCapture, handleRowKeyDownCapture),
470
+ onKeyDown: itemOnKeyDown,
471
+ onMouseDown: wrapEvent(itemOnMouseDown, () => {
472
+ if (!isDisabled) {
473
+ setPressedKey(item.key);
474
+ }
475
+ }),
476
+ onMouseUp: wrapEvent(itemOnMouseUp, () => {
477
+ setPressedKey(null);
478
+ }),
479
+ onMouseLeave: wrapEvent(itemOnMouseLeave, () => {
480
+ setPressedKey(null);
481
+ }),
482
+ children: /*#__PURE__*/_jsx("div", {
483
+ role: "gridcell",
484
+ "aria-colindex": 1,
485
+ children: renderTreeItemContent(item, contextValue)
486
+ })
487
+ })
488
+ }, item.keyString);
489
+ };
490
+ const renderItemBranch = item => {
491
+ const rows = [renderItem(item)];
492
+ if (expandedKeys.has(item.key)) {
493
+ item.childItems.forEach(childItem => {
494
+ rows.push(...renderItemBranch(childItem));
495
+ });
496
+ if (shouldRenderLoadingRow(item, renderLoadingState)) {
497
+ rows.push(renderLoadingRow(item));
498
+ }
499
+ }
500
+ return rows;
501
+ };
502
+ const renderItemBranches = items => {
503
+ const rows = [];
504
+ items.forEach(item => {
505
+ rows.push(...renderItemBranch(item));
506
+ });
507
+ return rows;
508
+ };
509
+ const renderNode = node => {
510
+ if (node.type === 'item') {
511
+ return renderItemBranch(node);
512
+ }
513
+ const sectionDOMProps = getSectionDOMProps(node.sectionProps);
514
+ const headerId = `${treeId}-section-${sanitizeId(node.key)}-header`;
515
+ return /*#__PURE__*/_createElement("div", {
516
+ ...sectionDOMProps,
517
+ key: node.key,
518
+ role: "rowgroup",
519
+ "aria-labelledby": node.header ? headerId : undefined,
520
+ "aria-label": node.header ? undefined : node.sectionProps['aria-label'] ?? undefined
521
+ }, node.header ? /*#__PURE__*/_jsx("div", {
522
+ role: "row",
523
+ children: /*#__PURE__*/_jsx("div", {
524
+ ...getHeaderDOMProps(node.headerProps),
525
+ id: headerId,
526
+ role: "rowheader",
527
+ children: node.header
528
+ })
529
+ }) : null, renderItemBranches(node.items));
530
+ };
531
+ const renderSectionRow = function (node) {
532
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
533
+ const headerId = `${treeId}-section-${sanitizeId(node.key)}-header`;
534
+ return /*#__PURE__*/_jsx("div", {
535
+ ref: options.measureElement,
536
+ role: "row",
537
+ "data-index": options.virtualIndex,
538
+ "aria-rowindex": options.rowIndex,
539
+ children: /*#__PURE__*/_jsx("div", {
540
+ ...getHeaderDOMProps(node.headerProps),
541
+ id: headerId,
542
+ role: "rowheader",
543
+ children: node.header
544
+ })
545
+ }, node.key);
546
+ };
547
+ const renderLoadingRow = function (item) {
548
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
549
+ return /*#__PURE__*/_jsx("div", {
550
+ ref: options.measureElement,
551
+ role: "row",
552
+ "data-index": options.virtualIndex,
553
+ "aria-level": item.level + 1,
554
+ "aria-rowindex": options.rowIndex,
555
+ "data-tree-loading-row": "",
556
+ "data-level": item.level + 1,
557
+ style: {
558
+ '--tree-item-level': item.level + 1
559
+ },
560
+ children: /*#__PURE__*/_jsx("div", {
561
+ role: "gridcell",
562
+ "aria-colindex": 1,
563
+ children: renderLoadingState?.(item)
564
+ })
565
+ }, `${item.keyString}-loading`);
566
+ };
567
+ const renderVisibleRow = (row, index, measureElement) => {
568
+ let defaultRow;
569
+ if (row.type === 'item') {
570
+ defaultRow = renderItem(row.item, {
571
+ rowIndex: index + 1,
572
+ virtualIndex: index,
573
+ measureElement
574
+ });
575
+ } else if (row.type === 'section') {
576
+ defaultRow = renderSectionRow(row.section, {
577
+ rowIndex: index + 1,
578
+ virtualIndex: index,
579
+ measureElement
580
+ });
581
+ } else {
582
+ defaultRow = renderLoadingRow(row.item, {
583
+ rowIndex: index + 1,
584
+ virtualIndex: index,
585
+ measureElement
586
+ });
587
+ }
588
+ return renderRow?.(row, defaultRow, renderState) ?? defaultRow;
589
+ };
590
+ const emptyState = allItems.length === 0 && renderEmptyState ? /*#__PURE__*/_jsx("div", {
591
+ role: "row",
592
+ "aria-level": 1,
593
+ children: /*#__PURE__*/_jsx("div", {
594
+ role: "gridcell",
595
+ "aria-colindex": 1,
596
+ children: renderEmptyState()
597
+ })
598
+ }) : null;
599
+ return /*#__PURE__*/_jsx(Comp, {
600
+ ...otherProps,
601
+ as: innerAs,
602
+ ref: assignMultipleRefs(forwardedRef, rootRef),
603
+ id: treeId,
604
+ role: "treegrid",
605
+ "aria-rowcount": renderedRowIndices ? visibleRows.length : undefined,
606
+ tabIndex: focusedKey == null ? tabIndex ?? 0 : tabIndex ?? -1,
607
+ "aria-multiselectable": selectionMode === 'multiple' ? true : undefined,
608
+ "data-tree": "",
609
+ "data-empty": allItems.length === 0 ? '' : undefined,
610
+ "data-focused": isFocused ? '' : undefined,
611
+ "data-focus-visible": isFocusVisible ? '' : undefined,
612
+ "data-selection-mode": selectionMode,
613
+ onKeyDown: wrapEvent(onKeyDown, handleKeyDown),
614
+ onFocus: wrapEvent(onFocus, handleFocus),
615
+ onBlur: wrapEvent(onBlur, handleBlur),
616
+ onMouseDown: wrapEvent(onMouseDown, handleMouseDown),
617
+ children: emptyState ?? (renderedRowIndices ? renderFlatRows(visibleRows, renderedRowIndices, renderVisibleRow) : parsedTree.map(renderNode))
618
+ });
619
+ });
620
+ function renderFlatRows(visibleRows, renderedRowIndices, renderVisibleRow) {
621
+ return Array.from(renderedRowIndices, index => {
622
+ const row = visibleRows[index];
623
+ return row ? renderVisibleRow(row, index) : null;
624
+ });
625
+ }
626
+ function renderTreeItemContent(item, context) {
627
+ if (item.props.title === undefined || !item.hasChildItems) {
628
+ return item.content;
629
+ }
630
+ return /*#__PURE__*/_jsxs(_Fragment, {
631
+ children: [/*#__PURE__*/_jsx("button", {
632
+ type: "button",
633
+ slot: "chevron",
634
+ "aria-expanded": context.isExpanded,
635
+ "aria-label": context.isExpanded ? 'Collapse' : 'Expand',
636
+ "aria-labelledby": context.rowId,
637
+ disabled: context.isDisabled,
638
+ tabIndex: -1,
639
+ onClick: context.toggleExpanded,
640
+ children: /*#__PURE__*/_jsx("span", {
641
+ "aria-hidden": "true",
642
+ children: ">"
643
+ })
644
+ }), item.content]
645
+ });
646
+ }
647
+ function makeKeySet(keys, allKeys) {
648
+ if (!keys) {
649
+ return new Set();
650
+ }
651
+ if (keys === 'all') {
652
+ return new Set(allKeys);
653
+ }
654
+ return new Set(keys);
655
+ }
656
+ function addRangeToSelection(visibleItems, anchorKey, key, selectedKeys, canSelect) {
657
+ const anchorIndex = visibleItems.findIndex(item => item.key === anchorKey);
658
+ const keyIndex = visibleItems.findIndex(item => item.key === key);
659
+ if (anchorIndex === -1 || keyIndex === -1) {
660
+ return new Set(selectedKeys);
661
+ }
662
+ const [start, end] = anchorIndex < keyIndex ? [anchorIndex, keyIndex] : [keyIndex, anchorIndex];
663
+ const next = new Set(selectedKeys);
664
+ for (let index = start; index <= end; index++) {
665
+ const item = visibleItems[index];
666
+ if (canSelect(item)) {
667
+ next.add(item.key);
668
+ }
669
+ }
670
+ return next;
671
+ }
672
+ function firstVisibleSelectedKey(visibleItems, selectedKeys) {
673
+ return visibleItems.find(item => selectedKeys.has(item.key))?.key ?? null;
674
+ }
675
+ function isItemDisabled(item, disabledKeys) {
676
+ return item.disabled || disabledKeys.has(item.key);
677
+ }
678
+ function isInteractionDisabled(item, disabledKeys, disabledBehavior) {
679
+ return disabledBehavior === 'all' && isItemDisabled(item, disabledKeys);
680
+ }
681
+ function getItemDOMProps(props) {
682
+ const {
683
+ id,
684
+ title,
685
+ textValue,
686
+ isDisabled,
687
+ disabled,
688
+ hasChildItems,
689
+ isLoading,
690
+ onAction,
691
+ children,
692
+ ...domProps
693
+ } = props;
694
+ return domProps;
695
+ }
696
+ function getSectionDOMProps(props) {
697
+ const {
698
+ as,
699
+ innerAs,
700
+ children,
701
+ ...domProps
702
+ } = props;
703
+ return domProps;
704
+ }
705
+ function getHeaderDOMProps(props) {
706
+ const {
707
+ as,
708
+ innerAs,
709
+ children,
710
+ ...domProps
711
+ } = props;
712
+ return domProps;
713
+ }
714
+ function sanitizeId(value) {
715
+ return value.replace(/[^A-Za-z0-9_-]/g, '-');
716
+ }
717
+ function getActiveElement(root) {
718
+ return root.ownerDocument.activeElement;
719
+ }
720
+ function getFocusableElements(root) {
721
+ return Array.from(root.querySelectorAll('a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])')).filter(element => !element.hasAttribute('disabled'));
722
+ }
723
+ function isInteractiveEventTarget(event, row) {
724
+ if (!row || !(event.target instanceof HTMLElement) || event.target === row) {
725
+ return false;
726
+ }
727
+ const interactiveElement = event.target.closest('a[href], button, input, select, textarea, [role="button"], [role="checkbox"], [tabindex]');
728
+ return Boolean(interactiveElement && interactiveElement !== row);
729
+ }
730
+ //# sourceMappingURL=Tree.js.map