entangle-ui 0.8.2 → 0.9.0

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 (203) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/dist/esm/assets/src/components/controls/Combobox/Combobox.css.ts.vanilla-B7B5ttkq.css +210 -0
  3. package/dist/esm/assets/src/components/controls/FileUploader/FileUploader.css.ts.vanilla-T4nRiI7s.css +194 -0
  4. package/dist/esm/assets/src/components/controls/MultiSelect/MultiSelect.css.ts.vanilla-CdYayqaF.css +311 -0
  5. package/dist/esm/assets/src/components/controls/TagInput/TagInput.css.ts.vanilla-hnkMOPp1.css +141 -0
  6. package/dist/esm/assets/src/components/data/DataTable/DataTable.css.ts.vanilla-CmRgtjIW.css +231 -0
  7. package/dist/esm/assets/src/components/feedback/Alert/{Alert.css.ts.vanilla-CRAI-xHx.css → Alert.css.ts.vanilla-CfCDsIEg.css} +2 -0
  8. package/dist/esm/assets/src/components/feedback/CommandPalette/CommandPalette.css.ts.vanilla-DGdrLKYZ.css +160 -0
  9. package/dist/esm/assets/src/components/feedback/Drawer/Drawer.css.ts.vanilla-CLPTOUrA.css +247 -0
  10. package/dist/esm/assets/src/components/feedback/Skeleton/SkeletonLayout.css.ts.vanilla-Db7bpqiI.css +75 -0
  11. package/dist/esm/assets/src/components/feedback/Stat/Stat.css.ts.vanilla-GBk3JAMB.css +69 -0
  12. package/dist/esm/assets/src/components/layout/Card/Card.css.ts.vanilla-Ducn1gUX.css +124 -0
  13. package/dist/esm/assets/src/components/navigation/Pagination/Pagination.css.ts.vanilla-CmlFyyjh.css +103 -0
  14. package/dist/esm/assets/src/components/primitives/HoverCard/HoverCard.css.ts.vanilla-BYT0qbLp.css +41 -0
  15. package/dist/esm/components/Icons/CloudUploadIcon.js +24 -0
  16. package/dist/esm/components/Icons/CloudUploadIcon.js.map +1 -0
  17. package/dist/esm/components/Icons/ExternalLinkIcon.js +26 -0
  18. package/dist/esm/components/Icons/ExternalLinkIcon.js.map +1 -0
  19. package/dist/esm/components/Icons/FirstIcon.js +23 -0
  20. package/dist/esm/components/Icons/FirstIcon.js.map +1 -0
  21. package/dist/esm/components/Icons/LastIcon.js +23 -0
  22. package/dist/esm/components/Icons/LastIcon.js.map +1 -0
  23. package/dist/esm/components/Icons/UnlinkIcon.js +26 -0
  24. package/dist/esm/components/Icons/UnlinkIcon.js.map +1 -0
  25. package/dist/esm/components/controls/Combobox/Combobox.css.js +20 -0
  26. package/dist/esm/components/controls/Combobox/Combobox.css.js.map +1 -0
  27. package/dist/esm/components/controls/Combobox/Combobox.js +354 -0
  28. package/dist/esm/components/controls/Combobox/Combobox.js.map +1 -0
  29. package/dist/esm/components/controls/FileUploader/FileUploader.css.js +20 -0
  30. package/dist/esm/components/controls/FileUploader/FileUploader.css.js.map +1 -0
  31. package/dist/esm/components/controls/FileUploader/FileUploader.js +264 -0
  32. package/dist/esm/components/controls/FileUploader/FileUploader.js.map +1 -0
  33. package/dist/esm/components/controls/MultiSelect/MultiSelect.css.js +23 -0
  34. package/dist/esm/components/controls/MultiSelect/MultiSelect.css.js.map +1 -0
  35. package/dist/esm/components/controls/MultiSelect/MultiSelect.js +269 -0
  36. package/dist/esm/components/controls/MultiSelect/MultiSelect.js.map +1 -0
  37. package/dist/esm/components/controls/Select/Select.js +5 -4
  38. package/dist/esm/components/controls/Select/Select.js.map +1 -1
  39. package/dist/esm/components/controls/TagInput/TagInput.css.js +12 -0
  40. package/dist/esm/components/controls/TagInput/TagInput.css.js.map +1 -0
  41. package/dist/esm/components/controls/TagInput/TagInput.js +189 -0
  42. package/dist/esm/components/controls/TagInput/TagInput.js.map +1 -0
  43. package/dist/esm/components/controls/TreeView/TreeNode.js +87 -1
  44. package/dist/esm/components/controls/TreeView/TreeNode.js.map +1 -1
  45. package/dist/esm/components/controls/VectorInput/VectorInput.js +87 -4
  46. package/dist/esm/components/controls/VectorInput/VectorInput.js.map +1 -1
  47. package/dist/esm/components/data/DataTable/DataTable.css.js +25 -0
  48. package/dist/esm/components/data/DataTable/DataTable.css.js.map +1 -0
  49. package/dist/esm/components/data/DataTable/DataTable.js +502 -0
  50. package/dist/esm/components/data/DataTable/DataTable.js.map +1 -0
  51. package/dist/esm/components/editor/ChatPanel/ChatCodeBlock.js +87 -5
  52. package/dist/esm/components/editor/ChatPanel/ChatCodeBlock.js.map +1 -1
  53. package/dist/esm/components/editor/ChatPanel/ChatInput.js +87 -5
  54. package/dist/esm/components/editor/ChatPanel/ChatInput.js.map +1 -1
  55. package/dist/esm/components/editor/ChatPanel/ChatMessage.js +87 -2
  56. package/dist/esm/components/editor/ChatPanel/ChatMessage.js.map +1 -1
  57. package/dist/esm/components/editor/PropertyInspector/PropertyRow.js +87 -3
  58. package/dist/esm/components/editor/PropertyInspector/PropertyRow.js.map +1 -1
  59. package/dist/esm/components/editor/PropertyInspector/PropertySection.js +87 -3
  60. package/dist/esm/components/editor/PropertyInspector/PropertySection.js.map +1 -1
  61. package/dist/esm/components/feedback/Alert/Alert.css.js +1 -1
  62. package/dist/esm/components/feedback/Alert/Alert.js +3 -2
  63. package/dist/esm/components/feedback/Alert/Alert.js.map +1 -1
  64. package/dist/esm/components/feedback/CommandPalette/CommandPalette.css.js +20 -0
  65. package/dist/esm/components/feedback/CommandPalette/CommandPalette.css.js.map +1 -0
  66. package/dist/esm/components/feedback/CommandPalette/CommandPalette.js +261 -0
  67. package/dist/esm/components/feedback/CommandPalette/CommandPalette.js.map +1 -0
  68. package/dist/esm/components/feedback/CommandPalette/fuzzySearch.js +86 -0
  69. package/dist/esm/components/feedback/CommandPalette/fuzzySearch.js.map +1 -0
  70. package/dist/esm/components/feedback/CommandPalette/useRecentItems.js +63 -0
  71. package/dist/esm/components/feedback/CommandPalette/useRecentItems.js.map +1 -0
  72. package/dist/esm/components/feedback/Dialog/DialogHeader.js +2 -1
  73. package/dist/esm/components/feedback/Dialog/DialogHeader.js.map +1 -1
  74. package/dist/esm/components/feedback/Drawer/Drawer.css.js +17 -0
  75. package/dist/esm/components/feedback/Drawer/Drawer.css.js.map +1 -0
  76. package/dist/esm/components/feedback/Drawer/Drawer.js +120 -0
  77. package/dist/esm/components/feedback/Drawer/Drawer.js.map +1 -0
  78. package/dist/esm/components/feedback/Drawer/useDrawerAnimation.js +74 -0
  79. package/dist/esm/components/feedback/Drawer/useDrawerAnimation.js.map +1 -0
  80. package/dist/esm/components/feedback/Skeleton/SkeletonLayout.css.js +18 -0
  81. package/dist/esm/components/feedback/Skeleton/SkeletonLayout.css.js.map +1 -0
  82. package/dist/esm/components/feedback/Skeleton/SkeletonLayout.js +95 -0
  83. package/dist/esm/components/feedback/Skeleton/SkeletonLayout.js.map +1 -0
  84. package/dist/esm/components/feedback/Stat/Stat.css.js +15 -0
  85. package/dist/esm/components/feedback/Stat/Stat.css.js.map +1 -0
  86. package/dist/esm/components/feedback/Stat/Stat.js +55 -0
  87. package/dist/esm/components/feedback/Stat/Stat.js.map +1 -0
  88. package/dist/esm/components/feedback/Toast/ToastItem.js +12 -15
  89. package/dist/esm/components/feedback/Toast/ToastItem.js.map +1 -1
  90. package/dist/esm/components/layout/Accordion/Accordion.js +2 -1
  91. package/dist/esm/components/layout/Accordion/Accordion.js.map +1 -1
  92. package/dist/esm/components/layout/Accordion/AccordionTrigger.js +2 -3
  93. package/dist/esm/components/layout/Accordion/AccordionTrigger.js.map +1 -1
  94. package/dist/esm/components/layout/Card/Card.css.js +18 -0
  95. package/dist/esm/components/layout/Card/Card.css.js.map +1 -0
  96. package/dist/esm/components/layout/Card/Card.js +66 -0
  97. package/dist/esm/components/layout/Card/Card.js.map +1 -0
  98. package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbEllipsis.js +1 -0
  99. package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbEllipsis.js.map +1 -1
  100. package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbItem.js +1 -0
  101. package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbItem.js.map +1 -1
  102. package/dist/esm/components/navigation/Breadcrumbs/Breadcrumbs.js +5 -0
  103. package/dist/esm/components/navigation/Breadcrumbs/Breadcrumbs.js.map +1 -1
  104. package/dist/esm/components/navigation/Pagination/Pagination.css.js +12 -0
  105. package/dist/esm/components/navigation/Pagination/Pagination.css.js.map +1 -0
  106. package/dist/esm/components/navigation/Pagination/Pagination.js +107 -0
  107. package/dist/esm/components/navigation/Pagination/Pagination.js.map +1 -0
  108. package/dist/esm/components/navigation/Pagination/usePagination.js +143 -0
  109. package/dist/esm/components/navigation/Pagination/usePagination.js.map +1 -0
  110. package/dist/esm/components/primitives/Avatar/Avatar.js +87 -1
  111. package/dist/esm/components/primitives/Avatar/Avatar.js.map +1 -1
  112. package/dist/esm/components/primitives/Badge/Badge.js +87 -1
  113. package/dist/esm/components/primitives/Badge/Badge.js.map +1 -1
  114. package/dist/esm/components/primitives/Checkbox/Checkbox.js +5 -2
  115. package/dist/esm/components/primitives/Checkbox/Checkbox.js.map +1 -1
  116. package/dist/esm/components/primitives/Collapsible/Collapsible.js +2 -3
  117. package/dist/esm/components/primitives/Collapsible/Collapsible.js.map +1 -1
  118. package/dist/esm/components/primitives/HoverCard/HoverCard.css.js +7 -0
  119. package/dist/esm/components/primitives/HoverCard/HoverCard.css.js.map +1 -0
  120. package/dist/esm/components/primitives/HoverCard/HoverCard.js +169 -0
  121. package/dist/esm/components/primitives/HoverCard/HoverCard.js.map +1 -0
  122. package/dist/esm/components/primitives/Icon/Icon.js +16 -2
  123. package/dist/esm/components/primitives/Icon/Icon.js.map +1 -1
  124. package/dist/esm/components/primitives/Link/Link.js +3 -3
  125. package/dist/esm/components/primitives/Link/Link.js.map +1 -1
  126. package/dist/esm/components/primitives/Popover/PopoverClose.js +2 -3
  127. package/dist/esm/components/primitives/Popover/PopoverClose.js.map +1 -1
  128. package/dist/esm/components/primitives/Radio/Radio.js +1 -1
  129. package/dist/esm/hooks/useBreakpoint/useBreakpoint.js +44 -0
  130. package/dist/esm/hooks/useBreakpoint/useBreakpoint.js.map +1 -0
  131. package/dist/esm/hooks/useDebounced/useDebouncedCallback.js +97 -0
  132. package/dist/esm/hooks/useDebounced/useDebouncedCallback.js.map +1 -0
  133. package/dist/esm/hooks/useDebounced/useDebouncedValue.js +35 -0
  134. package/dist/esm/hooks/useDebounced/useDebouncedValue.js.map +1 -0
  135. package/dist/esm/hooks/useIntersectionObserver/useIntersectionObserver.js +73 -0
  136. package/dist/esm/hooks/useIntersectionObserver/useIntersectionObserver.js.map +1 -0
  137. package/dist/esm/hooks/useListboxNav/useListboxNav.js +181 -0
  138. package/dist/esm/hooks/useListboxNav/useListboxNav.js.map +1 -0
  139. package/dist/esm/hooks/useMediaQuery/useMediaQuery.js +54 -0
  140. package/dist/esm/hooks/useMediaQuery/useMediaQuery.js.map +1 -0
  141. package/dist/esm/hooks/useThrottledCallback/useThrottledCallback.js +78 -0
  142. package/dist/esm/hooks/useThrottledCallback/useThrottledCallback.js.map +1 -0
  143. package/dist/esm/index.js +25 -0
  144. package/dist/esm/index.js.map +1 -1
  145. package/dist/esm/theme/breakpoints.js +27 -0
  146. package/dist/esm/theme/breakpoints.js.map +1 -0
  147. package/dist/esm/theme/index.js +1 -0
  148. package/dist/esm/theme/index.js.map +1 -1
  149. package/dist/tokens/tokens.dark.css +1 -1
  150. package/dist/tokens/tokens.json +1 -1
  151. package/dist/tokens/tokens.light.css +1 -1
  152. package/dist/types/components/Icons/CloudUploadIcon.d.ts +27 -0
  153. package/dist/types/components/Icons/ExternalLinkIcon.d.ts +29 -0
  154. package/dist/types/components/Icons/FirstIcon.d.ts +26 -0
  155. package/dist/types/components/Icons/LastIcon.d.ts +26 -0
  156. package/dist/types/components/Icons/UnlinkIcon.d.ts +29 -0
  157. package/dist/types/components/controls/Combobox/Combobox.d.ts +29 -0
  158. package/dist/types/components/controls/Combobox/Combobox.types.d.ts +109 -0
  159. package/dist/types/components/controls/FileUploader/FileUploader.d.ts +34 -0
  160. package/dist/types/components/controls/FileUploader/FileUploader.types.d.ts +94 -0
  161. package/dist/types/components/controls/MultiSelect/MultiSelect.d.ts +31 -0
  162. package/dist/types/components/controls/MultiSelect/MultiSelect.types.d.ts +85 -0
  163. package/dist/types/components/controls/TagInput/TagInput.d.ts +24 -0
  164. package/dist/types/components/controls/TagInput/TagInput.types.d.ts +100 -0
  165. package/dist/types/components/data/DataTable/DataTable.d.ts +8 -0
  166. package/dist/types/components/data/DataTable/DataTable.types.d.ts +159 -0
  167. package/dist/types/components/feedback/Alert/Alert.d.ts +1 -0
  168. package/dist/types/components/feedback/Alert/Alert.types.d.ts +7 -0
  169. package/dist/types/components/feedback/CommandPalette/CommandPalette.d.ts +29 -0
  170. package/dist/types/components/feedback/CommandPalette/CommandPalette.types.d.ts +61 -0
  171. package/dist/types/components/feedback/CommandPalette/fuzzySearch.d.ts +6 -0
  172. package/dist/types/components/feedback/Drawer/Drawer.d.ts +12 -0
  173. package/dist/types/components/feedback/Drawer/Drawer.types.d.ts +70 -0
  174. package/dist/types/components/feedback/Skeleton/Skeleton.types.d.ts +44 -1
  175. package/dist/types/components/feedback/Skeleton/SkeletonLayout.d.ts +314 -0
  176. package/dist/types/components/feedback/Stat/Stat.d.ts +23 -0
  177. package/dist/types/components/feedback/Stat/Stat.types.d.ts +38 -0
  178. package/dist/types/components/layout/Accordion/Accordion.types.d.ts +7 -0
  179. package/dist/types/components/layout/Card/Card.d.ts +12 -0
  180. package/dist/types/components/layout/Card/Card.types.d.ts +54 -0
  181. package/dist/types/components/navigation/Pagination/Pagination.d.ts +22 -0
  182. package/dist/types/components/navigation/Pagination/Pagination.types.d.ts +49 -0
  183. package/dist/types/components/primitives/Button/Button.d.ts +1 -1
  184. package/dist/types/components/primitives/HoverCard/HoverCard.d.ts +10 -0
  185. package/dist/types/components/primitives/HoverCard/HoverCard.types.d.ts +64 -0
  186. package/dist/types/components/primitives/Icon/Icon.d.ts +14 -1
  187. package/dist/types/components/primitives/IconButton/IconButton.d.ts +1 -1
  188. package/dist/types/hooks/useBreakpoint/useBreakpoint.d.ts +19 -0
  189. package/dist/types/hooks/useBreakpoint/useBreakpoint.types.d.ts +20 -0
  190. package/dist/types/hooks/useDebounced/useDebounced.types.d.ts +15 -0
  191. package/dist/types/hooks/useDebounced/useDebouncedCallback.d.ts +22 -0
  192. package/dist/types/hooks/useDebounced/useDebouncedValue.d.ts +16 -0
  193. package/dist/types/hooks/useIntersectionObserver/useIntersectionObserver.d.ts +22 -0
  194. package/dist/types/hooks/useIntersectionObserver/useIntersectionObserver.types.d.ts +22 -0
  195. package/dist/types/hooks/useListboxNav/useListboxNav.d.ts +75 -0
  196. package/dist/types/hooks/useMediaQuery/useMediaQuery.d.ts +19 -0
  197. package/dist/types/hooks/useMediaQuery/useMediaQuery.types.d.ts +6 -0
  198. package/dist/types/hooks/useThrottledCallback/useThrottledCallback.d.ts +23 -0
  199. package/dist/types/hooks/useThrottledCallback/useThrottledCallback.types.d.ts +13 -0
  200. package/dist/types/index.d.ts +43 -1
  201. package/dist/types/theme/breakpoints.d.ts +22 -0
  202. package/dist/types/theme/index.d.ts +1 -0
  203. package/package.json +3 -1
@@ -0,0 +1,261 @@
1
+ "use client";
2
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
3
+ import { useId, useRef, useState, useEffect, useMemo, useCallback } from 'react';
4
+ import { createPortal } from 'react-dom';
5
+ import { SearchIcon } from '../../Icons/SearchIcon.js';
6
+ import { Kbd } from '../../primitives/Kbd/Kbd.js';
7
+ import { useDebouncedValue } from '../../../hooks/useDebounced/useDebouncedValue.js';
8
+ import { cx } from '../../../utils/cx.js';
9
+ import { overlayStyle, inputWrapperStyle, searchIconStyle, inputStyle, emptyStyle, listStyle, groupHeaderStyle, itemSelectedStyle, itemStyle, panelStyle, itemIconStyle, itemTextStyle, itemLabelStyle, itemDescriptionStyle, itemShortcutStyle } from './CommandPalette.css.js';
10
+ import { fuzzyFilter } from './fuzzySearch.js';
11
+ import { useRecentItems } from './useRecentItems.js';
12
+
13
+ const DEFAULT_GROUP = 'default';
14
+ const RECENT_GROUP = '__recent__';
15
+ function getItemSearchStrings(item) {
16
+ const out = [item.label];
17
+ if (item.description)
18
+ out.push(item.description);
19
+ if (item.keywords)
20
+ out.push(...item.keywords);
21
+ return out;
22
+ }
23
+ /**
24
+ * Search-driven command list shown as a centred floating dialog. Type to
25
+ * fuzzy-filter, ArrowUp/ArrowDown to navigate, Enter to select, Escape to
26
+ * close. Recent selections are tracked in localStorage when `recentKey` is
27
+ * set. The component does not bind a global hotkey — wire `useHotkey` in
28
+ * the consumer.
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * useHotkey('Mod+K', () => { setOpen(true); });
33
+ *
34
+ * <CommandPalette
35
+ * open={open}
36
+ * onClose={() => setOpen(false)}
37
+ * items={[
38
+ * { id: 'open', label: 'Open File', shortcut: 'Cmd+O', group: 'File' },
39
+ * { id: 'save', label: 'Save', shortcut: 'Cmd+S', group: 'File' },
40
+ * ]}
41
+ * onSelect={(item) => runCommand(item.id)}
42
+ * recentKey="myapp:command-palette"
43
+ * />
44
+ * ```
45
+ */
46
+ const CommandPalette = ({ open, onClose, items, onSelect, placeholder = 'Type a command or search...', emptyState, recentLabel = 'Recent', recentKey, maxRecent = 5, debounceMs = 150, renderItem, portal = true, maxHeight = 400, width = 640, className, style, testId, id, ...rest }) => {
47
+ const autoId = useId();
48
+ const inputId = id ?? `command-palette-${autoId}`;
49
+ const listboxId = `${inputId}-listbox`;
50
+ const inputRef = useRef(null);
51
+ const listRef = useRef(null);
52
+ const [query, setQuery] = useState('');
53
+ const [activeIndex, setActiveIndex] = useState(0);
54
+ const debouncedQuery = useDebouncedValue(query, debounceMs);
55
+ const { recentIds, pushRecent } = useRecentItems({
56
+ storageKey: recentKey,
57
+ maxRecent,
58
+ });
59
+ // Reset state when the palette opens.
60
+ useEffect(() => {
61
+ if (open) {
62
+ setQuery('');
63
+ setActiveIndex(0);
64
+ }
65
+ }, [open]);
66
+ // Focus the input on open.
67
+ useEffect(() => {
68
+ if (open) {
69
+ const t = setTimeout(() => {
70
+ inputRef.current?.focus();
71
+ }, 0);
72
+ return () => {
73
+ clearTimeout(t);
74
+ };
75
+ }
76
+ return undefined;
77
+ }, [open]);
78
+ // Resolve groups + filtering.
79
+ const { rows, selectableItems } = useMemo(() => {
80
+ const groupOrder = [];
81
+ const groupMap = new Map();
82
+ if (debouncedQuery === '' && recentIds.length > 0) {
83
+ const byId = new Map(items.map(it => [it.id, it]));
84
+ const recent = [];
85
+ for (const id of recentIds) {
86
+ const it = byId.get(id);
87
+ if (it && !it.disabled)
88
+ recent.push(it);
89
+ }
90
+ if (recent.length > 0) {
91
+ groupOrder.push(RECENT_GROUP);
92
+ groupMap.set(RECENT_GROUP, recent);
93
+ }
94
+ }
95
+ const filtered = fuzzyFilter(debouncedQuery, items, getItemSearchStrings);
96
+ const filteredItems = filtered.map(r => r.item);
97
+ for (const item of filteredItems) {
98
+ const g = item.group ?? DEFAULT_GROUP;
99
+ if (!groupMap.has(g)) {
100
+ groupOrder.push(g);
101
+ groupMap.set(g, []);
102
+ }
103
+ groupMap.get(g)?.push(item);
104
+ }
105
+ const rows = [];
106
+ const flat = [];
107
+ let nextIndex = 0;
108
+ for (const groupKey of groupOrder) {
109
+ const list = groupMap.get(groupKey);
110
+ if (!list || list.length === 0)
111
+ continue;
112
+ const groupLabel = groupKey === RECENT_GROUP
113
+ ? recentLabel
114
+ : groupKey === DEFAULT_GROUP
115
+ ? ''
116
+ : groupKey;
117
+ if (groupLabel) {
118
+ rows.push({
119
+ type: 'header',
120
+ group: groupKey,
121
+ groupLabel,
122
+ });
123
+ }
124
+ for (const item of list) {
125
+ const index = nextIndex++;
126
+ rows.push({
127
+ type: 'item',
128
+ group: groupKey,
129
+ groupLabel,
130
+ item,
131
+ index,
132
+ });
133
+ flat.push(item);
134
+ }
135
+ }
136
+ return { rows, selectableItems: flat };
137
+ }, [items, debouncedQuery, recentIds, recentLabel]);
138
+ // Clamp activeIndex when the result list shrinks.
139
+ useEffect(() => {
140
+ setActiveIndex(prev => {
141
+ if (selectableItems.length === 0)
142
+ return 0;
143
+ if (prev >= selectableItems.length)
144
+ return selectableItems.length - 1;
145
+ return prev;
146
+ });
147
+ }, [selectableItems.length]);
148
+ const moveActive = useCallback((delta) => {
149
+ setActiveIndex(prev => {
150
+ const len = selectableItems.length;
151
+ if (len === 0)
152
+ return 0;
153
+ let next = prev + delta;
154
+ if (next < 0)
155
+ next = len - 1;
156
+ if (next >= len)
157
+ next = 0;
158
+ return next;
159
+ });
160
+ }, [selectableItems.length]);
161
+ const runSelect = useCallback((item) => {
162
+ if (item.disabled)
163
+ return;
164
+ pushRecent(item.id);
165
+ if (item.onSelect) {
166
+ item.onSelect();
167
+ }
168
+ else {
169
+ onSelect?.(item);
170
+ }
171
+ onClose();
172
+ }, [onClose, onSelect, pushRecent]);
173
+ const handleKeyDown = useCallback((e) => {
174
+ if (e.key === 'Escape') {
175
+ e.preventDefault();
176
+ onClose();
177
+ return;
178
+ }
179
+ if (e.key === 'ArrowDown') {
180
+ e.preventDefault();
181
+ moveActive(1);
182
+ return;
183
+ }
184
+ if (e.key === 'ArrowUp') {
185
+ e.preventDefault();
186
+ moveActive(-1);
187
+ return;
188
+ }
189
+ if (e.key === 'Home') {
190
+ e.preventDefault();
191
+ setActiveIndex(0);
192
+ return;
193
+ }
194
+ if (e.key === 'End') {
195
+ e.preventDefault();
196
+ setActiveIndex(Math.max(0, selectableItems.length - 1));
197
+ return;
198
+ }
199
+ if (e.key === 'Enter') {
200
+ e.preventDefault();
201
+ const item = selectableItems[activeIndex];
202
+ if (item)
203
+ runSelect(item);
204
+ }
205
+ }, [activeIndex, moveActive, onClose, runSelect, selectableItems]);
206
+ // Scroll active item into view when it changes.
207
+ useEffect(() => {
208
+ if (!open)
209
+ return;
210
+ const list = listRef.current;
211
+ if (!list)
212
+ return;
213
+ const el = list.querySelector(`[data-row-index="${String(activeIndex)}"]`);
214
+ if (el && typeof el.scrollIntoView === 'function') {
215
+ el.scrollIntoView({ block: 'nearest' });
216
+ }
217
+ }, [activeIndex, open]);
218
+ const handleOverlayClick = useCallback((e) => {
219
+ if (e.target === e.currentTarget) {
220
+ onClose();
221
+ }
222
+ }, [onClose]);
223
+ if (!open)
224
+ return null;
225
+ const widthCss = typeof width === 'number' ? `${String(width)}px` : width;
226
+ const activeId = activeIndex < selectableItems.length
227
+ ? `${listboxId}-item-${String(activeIndex)}`
228
+ : undefined;
229
+ const content = (jsxs(Fragment, { children: [jsx("div", { className: overlayStyle, "data-testid": testId ? `${testId}-overlay` : undefined, onClick: handleOverlayClick }), jsxs("div", { role: "dialog", "aria-modal": "true", "aria-label": "Command palette", className: cx(panelStyle, className), style: { width: widthCss, ...style }, "data-testid": testId, onKeyDown: handleKeyDown, ...rest, children: [jsxs("div", { className: inputWrapperStyle, children: [jsx("span", { className: searchIconStyle, "aria-hidden": "true", children: jsx(SearchIcon, {}) }), jsx("input", { ref: inputRef, id: inputId, type: "text", role: "combobox", "aria-expanded": "true", "aria-controls": listboxId, "aria-activedescendant": activeId, "aria-autocomplete": "list", className: inputStyle, placeholder: placeholder, value: query, onChange: e => {
230
+ setQuery(e.target.value);
231
+ setActiveIndex(0);
232
+ }, "data-testid": testId ? `${testId}-input` : undefined })] }), selectableItems.length === 0 ? (jsx("div", { className: emptyStyle, "data-testid": testId ? `${testId}-empty` : undefined, children: emptyState ?? `No matches for "${debouncedQuery}"` })) : (jsx("ul", { ref: listRef, id: listboxId, role: "listbox", className: listStyle, style: { maxHeight }, children: rows.map((row, rowIndex) => {
233
+ if (row.type === 'header') {
234
+ return (jsx("li", { role: "presentation", className: groupHeaderStyle, children: row.groupLabel }, `hdr-${row.group}-${String(rowIndex)}`));
235
+ }
236
+ const item = row.item;
237
+ const itemIndex = row.index;
238
+ if (!item || itemIndex === undefined)
239
+ return null;
240
+ const isSelected = itemIndex === activeIndex;
241
+ const itemDomId = `${listboxId}-item-${String(itemIndex)}`;
242
+ return (jsx("li", { id: itemDomId, role: "option", "aria-selected": isSelected, "aria-disabled": item.disabled ? true : undefined, className: cx(itemStyle, isSelected && itemSelectedStyle), "data-disabled": item.disabled ? 'true' : undefined, "data-row-index": itemIndex, "data-testid": testId ? `${testId}-item-${item.id}` : undefined, onMouseEnter: () => {
243
+ if (!item.disabled)
244
+ setActiveIndex(itemIndex);
245
+ }, onClick: () => {
246
+ runSelect(item);
247
+ }, children: renderItem ? (renderItem(item, {
248
+ selected: isSelected,
249
+ query: debouncedQuery,
250
+ })) : (jsx(DefaultItemContent, { item: item })) }, `${row.group}-${item.id}`));
251
+ }) }))] })] }));
252
+ if (portal && typeof document !== 'undefined') {
253
+ return createPortal(content, document.body);
254
+ }
255
+ return content;
256
+ };
257
+ CommandPalette.displayName = 'CommandPalette';
258
+ const DefaultItemContent = ({ item }) => (jsxs(Fragment, { children: [item.icon && jsx("span", { className: itemIconStyle, children: item.icon }), jsxs("span", { className: itemTextStyle, children: [jsx("span", { className: itemLabelStyle, children: item.label }), item.description && (jsx("span", { className: itemDescriptionStyle, children: item.description }))] }), item.shortcut && (jsx("span", { className: itemShortcutStyle, children: jsx(Kbd, { size: "sm", children: item.shortcut }) }))] }));
259
+
260
+ export { CommandPalette };
261
+ //# sourceMappingURL=CommandPalette.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette.js","sources":["../../../../../../src/components/feedback/CommandPalette/CommandPalette.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;;AAoCA;AACA;AAUA;AACE;;AACsB;;;AAEtB;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACI;AAqBL;AACA;AACA;AAEA;AACA;;;;AAMA;AACE;;AAED;;;;;;;AAQD;;;;AAKI;AACE;;AAEF;;AAEA;;AAEF;AACF;;;;AAKE;;;;AAKE;;AAEE;AAAwB;;AAE1B;AACE;AACA;;;;AAKJ;AAEA;AACE;;AAEE;AACA;;;;;;;AASJ;;AAEE;;AACA;AAEI;;AAEE;;;;AAIF;AACA;;AAED;;AAEH;AACE;;AAEE;AACA;;;;AAID;AACD;;;AAIJ;;;;;AAME;AAAkC;AAClC;AAAoC;AACpC;AACF;AACF;AAEA;;AAGM;;AACe;AACf;;AACc;;;AAEd;AACF;AACF;AAIF;;;AAGI;AACA;;;;AAGE;;AAEF;;AAKJ;AAEI;;AAEE;;;AAGF;;;;;AAKA;;AAEE;;;AAGF;;;;;AAKA;;AAEE;;;AAGF;;AAEE;AACA;;;AAEJ;;;AAMA;;AACA;AACA;;AACA;;;;AAMF;AAEA;;AAGM;;AAEJ;AAIF;AAAW;AAEX;AACA;;;;AAuCY;;AAEF;AAqBE;;;AAWA;AACA;AACA;AAAsC;AACtC;;AAEA;;;AAaI;;;AAOI;AACA;;AAOV;AAOV;;;AAGA;AACF;AAEA;AAEA;;"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Score how well `query` matches `target`. Higher = better. Zero = no match.
3
+ *
4
+ * Heuristics, in priority order:
5
+ * 1. Exact case-insensitive match (1000).
6
+ * 2. Target starts with the query (800 + length bonus).
7
+ * 3. Target contains the query as a contiguous substring (500 + position bonus).
8
+ * 4. Subsequence match: every query char appears in target in order; bonuses
9
+ * for consecutive matches, word-boundary matches, and earlier positions.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * fuzzyScore('opn', 'open file') // > 0, subsequence match
14
+ * fuzzyScore('zzz', 'open file') // 0
15
+ * ```
16
+ */
17
+ function fuzzyScore(query, target) {
18
+ if (query === '')
19
+ return 1;
20
+ if (target === '')
21
+ return 0;
22
+ const q = query.toLowerCase();
23
+ const t = target.toLowerCase();
24
+ if (q === t)
25
+ return 1000;
26
+ if (t.startsWith(q))
27
+ return 800 + Math.max(0, 50 - target.length);
28
+ const containsAt = t.indexOf(q);
29
+ if (containsAt !== -1)
30
+ return 500 - containsAt + Math.max(0, 30 - target.length);
31
+ let score = 0;
32
+ let qi = 0;
33
+ let lastMatch = -2;
34
+ for (let ti = 0; ti < t.length && qi < q.length; ti++) {
35
+ const ch = t[ti];
36
+ if (ch === q[qi]) {
37
+ // Earlier matches contribute more.
38
+ score += Math.max(1, 30 - ti);
39
+ if (lastMatch === ti - 1) {
40
+ score += 25;
41
+ }
42
+ // Bonus when the match begins a "word" (after space/-/_/.)
43
+ const prev = ti > 0 ? t[ti - 1] : '';
44
+ if (ti === 0 ||
45
+ prev === ' ' ||
46
+ prev === '-' ||
47
+ prev === '_' ||
48
+ prev === '.') {
49
+ score += 20;
50
+ }
51
+ lastMatch = ti;
52
+ qi++;
53
+ }
54
+ }
55
+ if (qi !== q.length)
56
+ return 0;
57
+ return score;
58
+ }
59
+ /**
60
+ * Score a list of items against a query. Items with score 0 are dropped, the
61
+ * rest are sorted by descending score. The `getStrings` accessor returns one
62
+ * or more strings per item to score against; the highest-scoring string wins.
63
+ */
64
+ function fuzzyFilter(query, items, getStrings) {
65
+ if (query === '') {
66
+ return items.map(item => ({ item, score: 1 }));
67
+ }
68
+ const result = [];
69
+ for (const item of items) {
70
+ const strings = getStrings(item);
71
+ let best = 0;
72
+ for (const s of strings) {
73
+ const score = fuzzyScore(query, s);
74
+ if (score > best)
75
+ best = score;
76
+ }
77
+ if (best > 0) {
78
+ result.push({ item, score: best });
79
+ }
80
+ }
81
+ result.sort((a, b) => b.score - a.score);
82
+ return result;
83
+ }
84
+
85
+ export { fuzzyFilter, fuzzyScore };
86
+ //# sourceMappingURL=fuzzySearch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fuzzySearch.js","sources":["../../../../../../src/components/feedback/CommandPalette/fuzzySearch.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ "use client";
2
+ import { useState, useEffect, useCallback } from 'react';
3
+
4
+ function readStorage(key) {
5
+ if (typeof window === 'undefined')
6
+ return [];
7
+ try {
8
+ const raw = window.localStorage.getItem(key);
9
+ if (!raw)
10
+ return [];
11
+ const parsed = JSON.parse(raw);
12
+ if (Array.isArray(parsed)) {
13
+ return parsed.filter((v) => typeof v === 'string');
14
+ }
15
+ return [];
16
+ }
17
+ catch {
18
+ return [];
19
+ }
20
+ }
21
+ function writeStorage(key, ids) {
22
+ if (typeof window === 'undefined')
23
+ return;
24
+ try {
25
+ window.localStorage.setItem(key, JSON.stringify(ids));
26
+ }
27
+ catch {
28
+ // localStorage may be unavailable (private mode, full quota); silently ignore.
29
+ }
30
+ }
31
+ /**
32
+ * Track recently selected command ids. Backed by localStorage when
33
+ * `storageKey` is provided; otherwise the list is in-memory only.
34
+ */
35
+ function useRecentItems({ storageKey, maxRecent = 5, }) {
36
+ const [recentIds, setRecentIds] = useState(() => storageKey ? readStorage(storageKey).slice(0, maxRecent) : []);
37
+ // Re-read storage if the key changes after mount.
38
+ useEffect(() => {
39
+ if (storageKey) {
40
+ setRecentIds(readStorage(storageKey).slice(0, maxRecent));
41
+ }
42
+ else {
43
+ setRecentIds([]);
44
+ }
45
+ }, [storageKey, maxRecent]);
46
+ const pushRecent = useCallback((id) => {
47
+ setRecentIds(prev => {
48
+ const next = [id, ...prev.filter(v => v !== id)].slice(0, maxRecent);
49
+ if (storageKey)
50
+ writeStorage(storageKey, next);
51
+ return next;
52
+ });
53
+ }, [storageKey, maxRecent]);
54
+ const clearRecent = useCallback(() => {
55
+ setRecentIds([]);
56
+ if (storageKey)
57
+ writeStorage(storageKey, []);
58
+ }, [storageKey]);
59
+ return { recentIds, pushRecent, clearRecent };
60
+ }
61
+
62
+ export { useRecentItems };
63
+ //# sourceMappingURL=useRecentItems.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRecentItems.js","sources":["../../../../../../src/components/feedback/CommandPalette/useRecentItems.ts"],"sourcesContent":[null],"names":[],"mappings":";;;AAiBA;;AACqC;AACnC;;AAEE;AAAU;;AAEV;AACE;;AAEF;;AACA;AACA;;AAEJ;AAEA;;;AAEE;AACE;;AACA;;;AAGJ;AAEA;;;AAGG;AACG;AAIJ;;;;AAOI;;;;;AAIJ;AAEA;;;AAIM;AAAgB;AAChB;AACF;AACF;AAIF;;AAEE;AAAgB;AAClB;AAEA;AACF;;"}
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { CloseIcon } from '../../Icons/CloseIcon.js';
3
4
  import { useDialogContext } from './Dialog.js';
4
5
  import { cx } from '../../../utils/cx.js';
5
6
  import { dialogHeaderContentStyle, dialogTitleStyle, dialogDescriptionStyle, dialogCloseButtonStyle, dialogHeaderStyle } from './Dialog.css.js';
@@ -18,7 +19,7 @@ import { dialogHeaderContentStyle, dialogTitleStyle, dialogDescriptionStyle, dia
18
19
  */
19
20
  const DialogHeader = ({ children, showClose = true, description, className, style, testId, ref, ...rest }) => {
20
21
  const { onClose, titleId, descriptionId } = useDialogContext();
21
- return (jsxs("div", { ref: ref, className: cx(dialogHeaderStyle, className), style: style, "data-testid": testId, ...rest, children: [jsxs("div", { className: dialogHeaderContentStyle, children: [jsx("div", { id: titleId, className: dialogTitleStyle, children: children }), description && (jsx("div", { id: descriptionId, className: dialogDescriptionStyle, children: description }))] }), showClose && (jsx("button", { type: "button", onClick: onClose, "aria-label": "Close dialog", className: dialogCloseButtonStyle, "data-testid": testId ? `${testId}-close` : undefined, children: jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsx("path", { d: "M1 1L11 11M11 1L1 11", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) }))] }));
22
+ return (jsxs("div", { ref: ref, className: cx(dialogHeaderStyle, className), style: style, "data-testid": testId, ...rest, children: [jsxs("div", { className: dialogHeaderContentStyle, children: [jsx("div", { id: titleId, className: dialogTitleStyle, children: children }), description && (jsx("div", { id: descriptionId, className: dialogDescriptionStyle, children: description }))] }), showClose && (jsx("button", { type: "button", onClick: onClose, "aria-label": "Close dialog", className: dialogCloseButtonStyle, "data-testid": testId ? `${testId}-close` : undefined, children: jsx(CloseIcon, { size: "sm", decorative: true }) }))] }));
22
23
  };
23
24
  DialogHeader.displayName = 'DialogHeader';
24
25
 
@@ -1 +1 @@
1
- {"version":3,"file":"DialogHeader.js","sources":["../../../../../../src/components/feedback/Dialog/DialogHeader.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;AAcA;AAEA;;;;;;;;;;AAUG;AACI;;AAYL;AA4CF;AAEA;;"}
1
+ {"version":3,"file":"DialogHeader.js","sources":["../../../../../../src/components/feedback/Dialog/DialogHeader.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;AAeA;AAEA;;;;;;;;;;AAUG;AACI;;;AA2CP;AAEA;;"}
@@ -0,0 +1,17 @@
1
+ import './../../../assets/src/components/feedback/Drawer/Drawer.css.ts.vanilla-CLPTOUrA.css';
2
+ import { createRuntimeFn } from '@vanilla-extract/recipes/createRuntimeFn';
3
+
4
+ var DRAWER_ANIMATION_MS = 220;
5
+ var DRAWER_SIZE_MAP = {sm:'280px',md:'360px',lg:'480px',xl:'640px'};
6
+ var drawerBodyStyle = 'Drawer_drawerBodyStyle__x57sw1x';
7
+ var drawerCloseButtonStyle = 'Drawer_drawerCloseButtonStyle__x57sw113';
8
+ var drawerDescriptionStyle = 'Drawer_drawerDescriptionStyle__x57sw1w';
9
+ var drawerFooterRecipe = createRuntimeFn({defaultClassName:'Drawer_drawerFooterRecipe__x57sw1y',variantClassNames:{align:{left:'Drawer_drawerFooterRecipe_align_left__x57sw1z',center:'Drawer_drawerFooterRecipe_align_center__x57sw110',right:'Drawer_drawerFooterRecipe_align_right__x57sw111','space-between':'Drawer_drawerFooterRecipe_align_space-between__x57sw112'}},defaultVariants:{align:'right'},compoundVariants:[]});
10
+ var drawerHeaderContentStyle = 'Drawer_drawerHeaderContentStyle__x57sw1u';
11
+ var drawerHeaderStyle = 'Drawer_drawerHeaderStyle__x57sw1t';
12
+ var drawerPanelRecipe = createRuntimeFn({defaultClassName:'Drawer_drawerPanelBase__x57sw1d',variantClassNames:{anchor:{left:'Drawer_drawerPanelRecipe_anchor_left__x57sw1f',right:'Drawer_drawerPanelRecipe_anchor_right__x57sw1g',top:'Drawer_drawerPanelRecipe_anchor_top__x57sw1h',bottom:'Drawer_drawerPanelRecipe_anchor_bottom__x57sw1i'},state:{open:'Drawer_drawerPanelRecipe_state_open__x57sw1j',closing:'Drawer_drawerPanelRecipe_state_closing__x57sw1k'}},defaultVariants:{anchor:'right',state:'open'},compoundVariants:[[{anchor:'left',state:'open'},'Drawer_drawerPanelRecipe_compound_0__x57sw1l'],[{anchor:'left',state:'closing'},'Drawer_drawerPanelRecipe_compound_1__x57sw1m'],[{anchor:'right',state:'open'},'Drawer_drawerPanelRecipe_compound_2__x57sw1n'],[{anchor:'right',state:'closing'},'Drawer_drawerPanelRecipe_compound_3__x57sw1o'],[{anchor:'top',state:'open'},'Drawer_drawerPanelRecipe_compound_4__x57sw1p'],[{anchor:'top',state:'closing'},'Drawer_drawerPanelRecipe_compound_5__x57sw1q'],[{anchor:'bottom',state:'open'},'Drawer_drawerPanelRecipe_compound_6__x57sw1r'],[{anchor:'bottom',state:'closing'},'Drawer_drawerPanelRecipe_compound_7__x57sw1s']]});
13
+ var drawerTitleStyle = 'Drawer_drawerTitleStyle__x57sw1v';
14
+ var overlayRecipe = createRuntimeFn({defaultClassName:'Drawer_overlayRecipe__x57sw1a',variantClassNames:{closing:{true:'Drawer_overlayRecipe_closing_true__x57sw1b',false:'Drawer_overlayRecipe_closing_false__x57sw1c'}},defaultVariants:{closing:false},compoundVariants:[]});
15
+
16
+ export { DRAWER_ANIMATION_MS, DRAWER_SIZE_MAP, drawerBodyStyle, drawerCloseButtonStyle, drawerDescriptionStyle, drawerFooterRecipe, drawerHeaderContentStyle, drawerHeaderStyle, drawerPanelRecipe, drawerTitleStyle, overlayRecipe };
17
+ //# sourceMappingURL=Drawer.css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Drawer.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,120 @@
1
+ "use client";
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { useId, useRef, useCallback, createContext, useContext } from 'react';
4
+ import { createPortal } from 'react-dom';
5
+ import { CloseIcon } from '../../Icons/CloseIcon.js';
6
+ import { useFocusTrap } from '../../../hooks/useFocusTrap/useFocusTrap.js';
7
+ import { useMergedRef } from '../../../hooks/useMergedRef/useMergedRef.js';
8
+ import { cx } from '../../../utils/cx.js';
9
+ import { overlayRecipe, drawerPanelRecipe, drawerHeaderContentStyle, drawerTitleStyle, drawerDescriptionStyle, drawerCloseButtonStyle, drawerHeaderStyle, drawerBodyStyle, drawerFooterRecipe, DRAWER_SIZE_MAP } from './Drawer.css.js';
10
+ import { useDrawerAnimation } from './useDrawerAnimation.js';
11
+
12
+ const DrawerContext = /*#__PURE__*/ createContext(null);
13
+ function useDrawerContext() {
14
+ const ctx = useContext(DrawerContext);
15
+ if (!ctx) {
16
+ throw new Error('Drawer sub-components must be used within <Drawer>');
17
+ }
18
+ return ctx;
19
+ }
20
+ function resolveSize(size, anchor) {
21
+ const isHorizontal = anchor === 'left' || anchor === 'right';
22
+ const dimension = isHorizontal ? 'width' : 'height';
23
+ let value;
24
+ if (typeof size === 'number') {
25
+ value = `${String(size)}px`;
26
+ }
27
+ else {
28
+ value = DRAWER_SIZE_MAP[size] ?? size;
29
+ }
30
+ return {
31
+ [dimension]: value,
32
+ maxWidth: '100vw',
33
+ maxHeight: '100vh',
34
+ };
35
+ }
36
+ /**
37
+ * Anchored sliding panel for secondary navigation, filters, or detail views.
38
+ * Modal mode renders an overlay backdrop and traps focus; non-modal mode
39
+ * leaves the rest of the page interactive.
40
+ *
41
+ * @example
42
+ * ```tsx
43
+ * <Drawer open={isOpen} onClose={close} anchor="right" size="md">
44
+ * <Drawer.Header>Filters</Drawer.Header>
45
+ * <Drawer.Body>...</Drawer.Body>
46
+ * <Drawer.Footer>
47
+ * <Button onClick={close}>Cancel</Button>
48
+ * <Button variant="filled">Apply</Button>
49
+ * </Drawer.Footer>
50
+ * </Drawer>
51
+ * ```
52
+ */
53
+ const DrawerRoot = ({ open, onClose, anchor = 'right', size = 'md', modal = true, closeOnOverlayClick = true, closeOnEscape = true, trapFocus, initialFocusRef, portal = true, title, description, className, style, testId, id, children, ref, ...rest }) => {
54
+ const autoId = useId();
55
+ const titleId = `drawer-title-${autoId}`;
56
+ const descriptionId = `drawer-desc-${autoId}`;
57
+ const panelRef = useRef(null);
58
+ const shouldTrapFocus = trapFocus ?? modal;
59
+ const { mounted, closing } = useDrawerAnimation({
60
+ open,
61
+ panelRef,
62
+ initialFocusRef,
63
+ shouldFocus: shouldTrapFocus,
64
+ });
65
+ const handleFocusTrap = useFocusTrap({
66
+ containerRef: panelRef,
67
+ enabled: shouldTrapFocus,
68
+ });
69
+ const setPanelRef = useMergedRef(panelRef, ref);
70
+ const handleKeyDown = useCallback((e) => {
71
+ if (closeOnEscape && e.key === 'Escape') {
72
+ e.stopPropagation();
73
+ onClose();
74
+ return;
75
+ }
76
+ handleFocusTrap(e);
77
+ }, [closeOnEscape, onClose, handleFocusTrap]);
78
+ const handleOverlayClick = useCallback((e) => {
79
+ if (closeOnOverlayClick && e.target === e.currentTarget) {
80
+ onClose();
81
+ }
82
+ }, [closeOnOverlayClick, onClose]);
83
+ if (!mounted)
84
+ return null;
85
+ const contextValue = {
86
+ onClose,
87
+ titleId,
88
+ descriptionId,
89
+ };
90
+ const sizeStyle = resolveSize(size, anchor);
91
+ const drawerContent = (jsxs(DrawerContext.Provider, { value: contextValue, children: [modal && (jsx("div", { className: overlayRecipe({ closing }), onClick: handleOverlayClick, "data-testid": testId ? `${testId}-overlay` : undefined })), jsxs("div", { ref: setPanelRef, role: "dialog", "aria-modal": modal ? 'true' : undefined, "aria-labelledby": title ? titleId : undefined, "aria-describedby": description ? descriptionId : undefined, tabIndex: -1, className: cx(drawerPanelRecipe({ anchor, state: closing ? 'closing' : 'open' }), className), style: { ...sizeStyle, ...style }, id: id, "data-testid": testId, "data-anchor": anchor, onKeyDown: handleKeyDown, ...rest, children: [title && (jsx("span", { id: titleId, style: { display: 'none' }, children: title })), description && (jsx("span", { id: descriptionId, style: { display: 'none' }, children: description })), children] })] }));
92
+ if (portal && typeof document !== 'undefined') {
93
+ return createPortal(drawerContent, document.body);
94
+ }
95
+ return drawerContent;
96
+ };
97
+ DrawerRoot.displayName = 'Drawer';
98
+ const DrawerHeader = ({ children, showClose = true, description, className, style, testId, ref, ...rest }) => {
99
+ const { onClose, titleId, descriptionId } = useDrawerContext();
100
+ return (jsxs("div", { ref: ref, className: cx(drawerHeaderStyle, className), style: style, "data-testid": testId, ...rest, children: [jsxs("div", { className: drawerHeaderContentStyle, children: [jsx("div", { id: titleId, className: drawerTitleStyle, children: children }), description && (jsx("div", { id: descriptionId, className: drawerDescriptionStyle, children: description }))] }), showClose && (jsx("button", { type: "button", onClick: onClose, "aria-label": "Close drawer", className: drawerCloseButtonStyle, "data-testid": testId ? `${testId}-close` : undefined, children: jsx(CloseIcon, { size: "sm", decorative: true }) }))] }));
101
+ };
102
+ DrawerHeader.displayName = 'Drawer.Header';
103
+ const DrawerBody = ({ children, className, style, testId, ref, ...rest }) => (jsx("div", { ref: ref, className: cx(drawerBodyStyle, className), style: style, "data-testid": testId, ...rest, children: children }));
104
+ DrawerBody.displayName = 'Drawer.Body';
105
+ const DrawerFooter = ({ children, align = 'right', className, style, testId, ref, ...rest }) => (jsx("div", { ref: ref, className: cx(drawerFooterRecipe({ align }), className), style: style, "data-testid": testId, ...rest, children: children }));
106
+ DrawerFooter.displayName = 'Drawer.Footer';
107
+ const DrawerCloseButton = ({ children, className, style, testId, 'aria-label': ariaLabel = 'Close drawer', ref, ...rest }) => {
108
+ const { onClose } = useDrawerContext();
109
+ return (jsx("button", { ref: ref, type: "button", onClick: onClose, "aria-label": ariaLabel, className: cx(drawerCloseButtonStyle, className), style: style, "data-testid": testId, ...rest, children: children ?? jsx(CloseIcon, { size: "sm", decorative: true }) }));
110
+ };
111
+ DrawerCloseButton.displayName = 'Drawer.CloseButton';
112
+ const Drawer = Object.assign(DrawerRoot, {
113
+ Header: DrawerHeader,
114
+ Body: DrawerBody,
115
+ Footer: DrawerFooter,
116
+ CloseButton: DrawerCloseButton,
117
+ });
118
+
119
+ export { Drawer };
120
+ //# sourceMappingURL=Drawer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Drawer.js","sources":["../../../../../../src/components/feedback/Drawer/Drawer.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;AAqCA;AAIA;AACE;;AAEE;;AAEF;AACF;AAEA;;;AAME;AACA;AACE;;;AAEA;;;;AAIA;AACA;;AAEJ;AAEA;;;;;;;;;;;;;;;;AAgBG;AACH;AAqBE;AACA;AACA;AACA;AAEA;AAEA;;;;AAIE;AACD;;AAGC;AACA;AACD;;AAID;;;AAIM;;;;;AAQN;;AAGM;;AAEJ;AAIF;AAAc;AAEd;;;;;;AAQA;AA0CA;;;AAGA;AACF;AAEA;AAEA;;;AA0CA;AAEA;AAEA;AAmBA;AAEA;AAoBA;AAEA;AASE;;AAeF;AAEA;;AAUE;AACA;AACA;AACA;AACD;;"}