@salt-ds/core 1.65.1 → 1.67.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 (161) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/css/salt-core.css +145 -3
  3. package/dist-cjs/aria-announcer/AriaAnnouncerProvider.js +28 -11
  4. package/dist-cjs/aria-announcer/AriaAnnouncerProvider.js.map +1 -1
  5. package/dist-cjs/border-layout/BorderLayout.js +28 -7
  6. package/dist-cjs/border-layout/BorderLayout.js.map +1 -1
  7. package/dist-cjs/dialog/Dialog.js +3 -2
  8. package/dist-cjs/dialog/Dialog.js.map +1 -1
  9. package/dist-cjs/drawer/Drawer.js +2 -1
  10. package/dist-cjs/drawer/Drawer.js.map +1 -1
  11. package/dist-cjs/file-drop-zone/FileDropZone.js +19 -4
  12. package/dist-cjs/file-drop-zone/FileDropZone.js.map +1 -1
  13. package/dist-cjs/file-drop-zone/FileDropZoneTrigger.js +3 -2
  14. package/dist-cjs/file-drop-zone/FileDropZoneTrigger.js.map +1 -1
  15. package/dist-cjs/grid-layout/GridLayout.css.js +1 -1
  16. package/dist-cjs/index.js +22 -0
  17. package/dist-cjs/index.js.map +1 -1
  18. package/dist-cjs/mega-menu/MegaMenu.js +81 -0
  19. package/dist-cjs/mega-menu/MegaMenu.js.map +1 -0
  20. package/dist-cjs/mega-menu/MegaMenuActions.css.js +6 -0
  21. package/dist-cjs/mega-menu/MegaMenuActions.css.js.map +1 -0
  22. package/dist-cjs/mega-menu/MegaMenuActions.js +37 -0
  23. package/dist-cjs/mega-menu/MegaMenuActions.js.map +1 -0
  24. package/dist-cjs/mega-menu/MegaMenuAside.css.js +6 -0
  25. package/dist-cjs/mega-menu/MegaMenuAside.css.js.map +1 -0
  26. package/dist-cjs/mega-menu/MegaMenuAside.js +37 -0
  27. package/dist-cjs/mega-menu/MegaMenuAside.js.map +1 -0
  28. package/dist-cjs/mega-menu/MegaMenuContent.css.js +6 -0
  29. package/dist-cjs/mega-menu/MegaMenuContent.css.js.map +1 -0
  30. package/dist-cjs/mega-menu/MegaMenuContent.js +29 -0
  31. package/dist-cjs/mega-menu/MegaMenuContent.js.map +1 -0
  32. package/dist-cjs/mega-menu/MegaMenuContext.js +18 -0
  33. package/dist-cjs/mega-menu/MegaMenuContext.js.map +1 -0
  34. package/dist-cjs/mega-menu/MegaMenuGroup.css.js +6 -0
  35. package/dist-cjs/mega-menu/MegaMenuGroup.css.js.map +1 -0
  36. package/dist-cjs/mega-menu/MegaMenuGroup.js +44 -0
  37. package/dist-cjs/mega-menu/MegaMenuGroup.js.map +1 -0
  38. package/dist-cjs/mega-menu/MegaMenuGroupContext.js +23 -0
  39. package/dist-cjs/mega-menu/MegaMenuGroupContext.js.map +1 -0
  40. package/dist-cjs/mega-menu/MegaMenuGroupHeading.css.js +6 -0
  41. package/dist-cjs/mega-menu/MegaMenuGroupHeading.css.js.map +1 -0
  42. package/dist-cjs/mega-menu/MegaMenuGroupHeading.js +44 -0
  43. package/dist-cjs/mega-menu/MegaMenuGroupHeading.js.map +1 -0
  44. package/dist-cjs/mega-menu/MegaMenuGroups.css.js +6 -0
  45. package/dist-cjs/mega-menu/MegaMenuGroups.css.js.map +1 -0
  46. package/dist-cjs/mega-menu/MegaMenuGroups.js +29 -0
  47. package/dist-cjs/mega-menu/MegaMenuGroups.js.map +1 -0
  48. package/dist-cjs/mega-menu/MegaMenuList.css.js +6 -0
  49. package/dist-cjs/mega-menu/MegaMenuList.css.js.map +1 -0
  50. package/dist-cjs/mega-menu/MegaMenuList.js +47 -0
  51. package/dist-cjs/mega-menu/MegaMenuList.js.map +1 -0
  52. package/dist-cjs/mega-menu/MegaMenuListItem.css.js +6 -0
  53. package/dist-cjs/mega-menu/MegaMenuListItem.css.js.map +1 -0
  54. package/dist-cjs/mega-menu/MegaMenuListItem.js +50 -0
  55. package/dist-cjs/mega-menu/MegaMenuListItem.js.map +1 -0
  56. package/dist-cjs/mega-menu/MegaMenuPanel.css.js +6 -0
  57. package/dist-cjs/mega-menu/MegaMenuPanel.css.js.map +1 -0
  58. package/dist-cjs/mega-menu/MegaMenuPanel.js +94 -0
  59. package/dist-cjs/mega-menu/MegaMenuPanel.js.map +1 -0
  60. package/dist-cjs/mega-menu/MegaMenuTrigger.js +97 -0
  61. package/dist-cjs/mega-menu/MegaMenuTrigger.js.map +1 -0
  62. package/dist-cjs/mega-menu/useMegaMenu.js +15 -0
  63. package/dist-cjs/mega-menu/useMegaMenu.js.map +1 -0
  64. package/dist-cjs/mega-menu/useMegaMenuNavigation.js +282 -0
  65. package/dist-cjs/mega-menu/useMegaMenuNavigation.js.map +1 -0
  66. package/dist-cjs/mega-menu/usePageMargin.js +28 -0
  67. package/dist-cjs/mega-menu/usePageMargin.js.map +1 -0
  68. package/dist-cjs/overlay/OverlayPanel.js +2 -1
  69. package/dist-cjs/overlay/OverlayPanel.js.map +1 -1
  70. package/dist-cjs/semantic-icon-provider/SemanticIconProvider.js +1 -0
  71. package/dist-cjs/semantic-icon-provider/SemanticIconProvider.js.map +1 -1
  72. package/dist-es/aria-announcer/AriaAnnouncerProvider.js +29 -12
  73. package/dist-es/aria-announcer/AriaAnnouncerProvider.js.map +1 -1
  74. package/dist-es/border-layout/BorderLayout.js +28 -7
  75. package/dist-es/border-layout/BorderLayout.js.map +1 -1
  76. package/dist-es/dialog/Dialog.js +3 -2
  77. package/dist-es/dialog/Dialog.js.map +1 -1
  78. package/dist-es/drawer/Drawer.js +2 -1
  79. package/dist-es/drawer/Drawer.js.map +1 -1
  80. package/dist-es/file-drop-zone/FileDropZone.js +20 -5
  81. package/dist-es/file-drop-zone/FileDropZone.js.map +1 -1
  82. package/dist-es/file-drop-zone/FileDropZoneTrigger.js +3 -2
  83. package/dist-es/file-drop-zone/FileDropZoneTrigger.js.map +1 -1
  84. package/dist-es/grid-layout/GridLayout.css.js +1 -1
  85. package/dist-es/index.js +11 -0
  86. package/dist-es/index.js.map +1 -1
  87. package/dist-es/mega-menu/MegaMenu.js +79 -0
  88. package/dist-es/mega-menu/MegaMenu.js.map +1 -0
  89. package/dist-es/mega-menu/MegaMenuActions.css.js +4 -0
  90. package/dist-es/mega-menu/MegaMenuActions.css.js.map +1 -0
  91. package/dist-es/mega-menu/MegaMenuActions.js +35 -0
  92. package/dist-es/mega-menu/MegaMenuActions.js.map +1 -0
  93. package/dist-es/mega-menu/MegaMenuAside.css.js +4 -0
  94. package/dist-es/mega-menu/MegaMenuAside.css.js.map +1 -0
  95. package/dist-es/mega-menu/MegaMenuAside.js +35 -0
  96. package/dist-es/mega-menu/MegaMenuAside.js.map +1 -0
  97. package/dist-es/mega-menu/MegaMenuContent.css.js +4 -0
  98. package/dist-es/mega-menu/MegaMenuContent.css.js.map +1 -0
  99. package/dist-es/mega-menu/MegaMenuContent.js +27 -0
  100. package/dist-es/mega-menu/MegaMenuContent.js.map +1 -0
  101. package/dist-es/mega-menu/MegaMenuContext.js +16 -0
  102. package/dist-es/mega-menu/MegaMenuContext.js.map +1 -0
  103. package/dist-es/mega-menu/MegaMenuGroup.css.js +4 -0
  104. package/dist-es/mega-menu/MegaMenuGroup.css.js.map +1 -0
  105. package/dist-es/mega-menu/MegaMenuGroup.js +42 -0
  106. package/dist-es/mega-menu/MegaMenuGroup.js.map +1 -0
  107. package/dist-es/mega-menu/MegaMenuGroupContext.js +20 -0
  108. package/dist-es/mega-menu/MegaMenuGroupContext.js.map +1 -0
  109. package/dist-es/mega-menu/MegaMenuGroupHeading.css.js +4 -0
  110. package/dist-es/mega-menu/MegaMenuGroupHeading.css.js.map +1 -0
  111. package/dist-es/mega-menu/MegaMenuGroupHeading.js +42 -0
  112. package/dist-es/mega-menu/MegaMenuGroupHeading.js.map +1 -0
  113. package/dist-es/mega-menu/MegaMenuGroups.css.js +4 -0
  114. package/dist-es/mega-menu/MegaMenuGroups.css.js.map +1 -0
  115. package/dist-es/mega-menu/MegaMenuGroups.js +27 -0
  116. package/dist-es/mega-menu/MegaMenuGroups.js.map +1 -0
  117. package/dist-es/mega-menu/MegaMenuList.css.js +4 -0
  118. package/dist-es/mega-menu/MegaMenuList.css.js.map +1 -0
  119. package/dist-es/mega-menu/MegaMenuList.js +45 -0
  120. package/dist-es/mega-menu/MegaMenuList.js.map +1 -0
  121. package/dist-es/mega-menu/MegaMenuListItem.css.js +4 -0
  122. package/dist-es/mega-menu/MegaMenuListItem.css.js.map +1 -0
  123. package/dist-es/mega-menu/MegaMenuListItem.js +48 -0
  124. package/dist-es/mega-menu/MegaMenuListItem.js.map +1 -0
  125. package/dist-es/mega-menu/MegaMenuPanel.css.js +4 -0
  126. package/dist-es/mega-menu/MegaMenuPanel.css.js.map +1 -0
  127. package/dist-es/mega-menu/MegaMenuPanel.js +92 -0
  128. package/dist-es/mega-menu/MegaMenuPanel.js.map +1 -0
  129. package/dist-es/mega-menu/MegaMenuTrigger.js +95 -0
  130. package/dist-es/mega-menu/MegaMenuTrigger.js.map +1 -0
  131. package/dist-es/mega-menu/useMegaMenu.js +13 -0
  132. package/dist-es/mega-menu/useMegaMenu.js.map +1 -0
  133. package/dist-es/mega-menu/useMegaMenuNavigation.js +278 -0
  134. package/dist-es/mega-menu/useMegaMenuNavigation.js.map +1 -0
  135. package/dist-es/mega-menu/usePageMargin.js +26 -0
  136. package/dist-es/mega-menu/usePageMargin.js.map +1 -0
  137. package/dist-es/overlay/OverlayPanel.js +2 -1
  138. package/dist-es/overlay/OverlayPanel.js.map +1 -1
  139. package/dist-es/semantic-icon-provider/SemanticIconProvider.js +1 -0
  140. package/dist-es/semantic-icon-provider/SemanticIconProvider.js.map +1 -1
  141. package/dist-types/index.d.ts +1 -0
  142. package/dist-types/mega-menu/MegaMenu.d.ts +36 -0
  143. package/dist-types/mega-menu/MegaMenuActions.d.ts +10 -0
  144. package/dist-types/mega-menu/MegaMenuAside.d.ts +9 -0
  145. package/dist-types/mega-menu/MegaMenuContent.d.ts +9 -0
  146. package/dist-types/mega-menu/MegaMenuContext.d.ts +28 -0
  147. package/dist-types/mega-menu/MegaMenuGroup.d.ts +9 -0
  148. package/dist-types/mega-menu/MegaMenuGroupContext.d.ts +12 -0
  149. package/dist-types/mega-menu/MegaMenuGroupHeading.d.ts +13 -0
  150. package/dist-types/mega-menu/MegaMenuGroups.d.ts +8 -0
  151. package/dist-types/mega-menu/MegaMenuList.d.ts +16 -0
  152. package/dist-types/mega-menu/MegaMenuListItem.d.ts +25 -0
  153. package/dist-types/mega-menu/MegaMenuPanel.d.ts +8 -0
  154. package/dist-types/mega-menu/MegaMenuTrigger.d.ts +9 -0
  155. package/dist-types/mega-menu/index.d.ts +11 -0
  156. package/dist-types/mega-menu/useMegaMenu.d.ts +6 -0
  157. package/dist-types/mega-menu/useMegaMenuNavigation.d.ts +16 -0
  158. package/dist-types/mega-menu/usePageMargin.d.ts +6 -0
  159. package/dist-types/semantic-icon-provider/SemanticIconProvider.d.ts +1 -0
  160. package/dist-types/tooltip/useTooltip.d.ts +1 -1
  161. package/package.json +2 -2
@@ -0,0 +1,282 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+
5
+ const COLUMN_SELECTOR = "[data-mega-menu-column]";
6
+ const ACTIONS_SELECTOR = "[data-mega-menu-actions]";
7
+ const FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])';
8
+ const SELF_CONSUMING_SELECTOR = 'input, textarea, select, [contenteditable], [role="combobox"], [role="listbox"], [role="slider"], [role="spinbutton"], [role="textbox"]';
9
+ const HIDDEN_ANCESTOR_SELECTOR = '[aria-hidden="true"], [inert]';
10
+ function queryFocusables(root) {
11
+ return Array.from(
12
+ root.querySelectorAll(FOCUSABLE_SELECTOR)
13
+ ).filter((el) => !el.closest(HIDDEN_ANCESTOR_SELECTOR));
14
+ }
15
+ function firstFocusable(root) {
16
+ return root ? queryFocusables(root)[0] ?? null : null;
17
+ }
18
+ function buildModel(panel) {
19
+ const columns = Array.from(
20
+ panel.querySelectorAll(COLUMN_SELECTOR)
21
+ ).filter((el) => queryFocusables(el).length > 0);
22
+ const actions = Array.from(
23
+ panel.querySelectorAll(ACTIONS_SELECTOR)
24
+ ).filter((el) => queryFocusables(el).length > 0);
25
+ return { columns, actions };
26
+ }
27
+ function focusTrigger(context) {
28
+ var _a;
29
+ const reference = context.elements.reference;
30
+ (_a = firstFocusable(reference) ?? reference) == null ? void 0 : _a.focus();
31
+ }
32
+ function focusNextTriggerAndClose(context) {
33
+ const reference = context.elements.reference;
34
+ const trigger = firstFocusable(reference) ?? reference;
35
+ const li = trigger == null ? void 0 : trigger.closest("li");
36
+ const next = (li == null ? void 0 : li.nextElementSibling) instanceof HTMLElement ? firstFocusable(li.nextElementSibling) : null;
37
+ if (next) {
38
+ context.onOpenChange(false);
39
+ next.focus();
40
+ return true;
41
+ }
42
+ return false;
43
+ }
44
+ function columnsStacked(columns) {
45
+ if (columns.length < 2) return false;
46
+ const first = columns[0].getBoundingClientRect();
47
+ const second = columns[1].getBoundingClientRect();
48
+ return second.top >= first.bottom - 1;
49
+ }
50
+ function handleLinear(key, cell, panel, context) {
51
+ var _a, _b;
52
+ const cells = queryFocusables(panel);
53
+ const index = cells.indexOf(cell);
54
+ if (index === -1) return false;
55
+ switch (key) {
56
+ case "ArrowDown":
57
+ case "ArrowRight": {
58
+ if (index < cells.length - 1) {
59
+ cells[index + 1].focus();
60
+ }
61
+ return true;
62
+ }
63
+ case "ArrowUp":
64
+ case "ArrowLeft": {
65
+ if (index > 0) {
66
+ cells[index - 1].focus();
67
+ } else {
68
+ focusTrigger(context);
69
+ }
70
+ return true;
71
+ }
72
+ case "Home": {
73
+ (_a = cells[0]) == null ? void 0 : _a.focus();
74
+ return true;
75
+ }
76
+ case "End": {
77
+ (_b = cells[cells.length - 1]) == null ? void 0 : _b.focus();
78
+ return true;
79
+ }
80
+ default:
81
+ return false;
82
+ }
83
+ }
84
+ function handleArrow(key, cell, panel, context) {
85
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
86
+ const { columns, actions } = buildModel(panel);
87
+ if (columnsStacked(columns)) {
88
+ return handleLinear(key, cell, panel, context);
89
+ }
90
+ const column = cell.closest(COLUMN_SELECTOR);
91
+ const actionsEl = cell.closest(ACTIONS_SELECTOR);
92
+ if (column) {
93
+ const colIndex = columns.indexOf(column);
94
+ const cells = queryFocusables(column);
95
+ const rowIndex = cells.indexOf(cell);
96
+ switch (key) {
97
+ case "ArrowDown": {
98
+ if (rowIndex < cells.length - 1) {
99
+ cells[rowIndex + 1].focus();
100
+ } else if (colIndex < columns.length - 1) {
101
+ (_a = firstFocusable(columns[colIndex + 1])) == null ? void 0 : _a.focus();
102
+ } else if (actions.length > 0) {
103
+ (_b = firstFocusable(actions[0])) == null ? void 0 : _b.focus();
104
+ } else {
105
+ focusNextTriggerAndClose(context);
106
+ }
107
+ return true;
108
+ }
109
+ case "ArrowUp": {
110
+ if (rowIndex > 0) {
111
+ cells[rowIndex - 1].focus();
112
+ } else {
113
+ focusTrigger(context);
114
+ }
115
+ return true;
116
+ }
117
+ case "ArrowRight": {
118
+ if (colIndex < columns.length - 1) {
119
+ (_c = firstFocusable(columns[colIndex + 1])) == null ? void 0 : _c.focus();
120
+ return true;
121
+ }
122
+ if (rowIndex === cells.length - 1 && focusNextTriggerAndClose(context)) {
123
+ return true;
124
+ }
125
+ focusTrigger(context);
126
+ return true;
127
+ }
128
+ case "ArrowLeft": {
129
+ if (colIndex > 0) {
130
+ (_d = firstFocusable(columns[colIndex - 1])) == null ? void 0 : _d.focus();
131
+ } else {
132
+ focusTrigger(context);
133
+ }
134
+ return true;
135
+ }
136
+ case "Home": {
137
+ (_e = cells[0]) == null ? void 0 : _e.focus();
138
+ return true;
139
+ }
140
+ case "End": {
141
+ (_f = cells[cells.length - 1]) == null ? void 0 : _f.focus();
142
+ return true;
143
+ }
144
+ default:
145
+ return false;
146
+ }
147
+ }
148
+ if (actionsEl) {
149
+ const cells = queryFocusables(actionsEl);
150
+ const index = cells.indexOf(cell);
151
+ switch (key) {
152
+ case "ArrowRight": {
153
+ if (index < cells.length - 1) {
154
+ cells[index + 1].focus();
155
+ } else if (!focusNextTriggerAndClose(context)) {
156
+ focusTrigger(context);
157
+ }
158
+ return true;
159
+ }
160
+ case "ArrowLeft": {
161
+ if (index > 0) {
162
+ cells[index - 1].focus();
163
+ } else if (cell === firstFocusable(panel)) {
164
+ focusTrigger(context);
165
+ }
166
+ return true;
167
+ }
168
+ case "ArrowUp": {
169
+ if (columns.length > 0) {
170
+ const lastColumnCells = queryFocusables(columns[columns.length - 1]);
171
+ (_g = lastColumnCells[lastColumnCells.length - 1]) == null ? void 0 : _g.focus();
172
+ } else {
173
+ focusTrigger(context);
174
+ }
175
+ return true;
176
+ }
177
+ case "ArrowDown": {
178
+ if (index === cells.length - 1) {
179
+ focusNextTriggerAndClose(context);
180
+ }
181
+ return true;
182
+ }
183
+ case "Home": {
184
+ (_h = cells[0]) == null ? void 0 : _h.focus();
185
+ return true;
186
+ }
187
+ case "End": {
188
+ (_i = cells[cells.length - 1]) == null ? void 0 : _i.focus();
189
+ return true;
190
+ }
191
+ default:
192
+ return false;
193
+ }
194
+ }
195
+ return false;
196
+ }
197
+ function focusNextAfterPanel(context, panel) {
198
+ var _a;
199
+ const reference = context.elements.reference;
200
+ const refFocusable = firstFocusable(reference) ?? reference;
201
+ const nextLi = (_a = refFocusable == null ? void 0 : refFocusable.closest("li")) == null ? void 0 : _a.nextElementSibling;
202
+ const nextSibling = nextLi instanceof HTMLElement ? firstFocusable(nextLi) : null;
203
+ const nextOutside = nextSibling || (() => {
204
+ const allFocusable = queryFocusables(
205
+ panel.ownerDocument.documentElement
206
+ ).filter((el) => !panel.contains(el));
207
+ const idx = refFocusable ? allFocusable.indexOf(refFocusable) : -1;
208
+ return idx >= 0 ? allFocusable[idx + 1] : void 0;
209
+ })();
210
+ if (nextOutside) {
211
+ const view = panel.ownerDocument.defaultView;
212
+ view == null ? void 0 : view.requestAnimationFrame(() => {
213
+ view == null ? void 0 : view.requestAnimationFrame(() => {
214
+ nextOutside.focus();
215
+ });
216
+ });
217
+ }
218
+ }
219
+ function useMegaMenuNavigation(context, props = {}) {
220
+ const { enabled = true } = props;
221
+ const { open, onOpenChange } = context;
222
+ return React.useMemo(() => {
223
+ if (!enabled) {
224
+ return {};
225
+ }
226
+ return {
227
+ floating: {
228
+ onKeyDown(event) {
229
+ var _a, _b;
230
+ if (!open) return;
231
+ const panel = event.currentTarget;
232
+ const target = event.target;
233
+ const { key } = event;
234
+ const isArrowOrEdge = key === "ArrowUp" || key === "ArrowDown" || key === "ArrowLeft" || key === "ArrowRight" || key === "Home" || key === "End";
235
+ if (isArrowOrEdge && target.closest(SELF_CONSUMING_SELECTOR)) {
236
+ return;
237
+ }
238
+ const cell = target.closest(FOCUSABLE_SELECTOR);
239
+ if (key === "Tab") {
240
+ const allCells = queryFocusables(panel);
241
+ const linearIndex = cell ? allCells.indexOf(cell) : -1;
242
+ if (linearIndex === -1) return;
243
+ event.preventDefault();
244
+ if (event.shiftKey) {
245
+ if (linearIndex === 0) {
246
+ focusTrigger(context);
247
+ } else {
248
+ (_a = allCells[linearIndex - 1]) == null ? void 0 : _a.focus();
249
+ }
250
+ } else if (linearIndex === allCells.length - 1) {
251
+ onOpenChange(false);
252
+ focusNextAfterPanel(context, panel);
253
+ } else {
254
+ (_b = allCells[linearIndex + 1]) == null ? void 0 : _b.focus();
255
+ }
256
+ return;
257
+ }
258
+ if (!cell || !isArrowOrEdge) return;
259
+ if (handleArrow(key, cell, panel, context)) {
260
+ event.preventDefault();
261
+ }
262
+ }
263
+ }
264
+ };
265
+ }, [enabled, open, context, onOpenChange]);
266
+ }
267
+ function focusFirstItem(panel, attempt = 0) {
268
+ const firstItem = firstFocusable(panel);
269
+ if (firstItem) {
270
+ firstItem.focus();
271
+ return;
272
+ }
273
+ const view = panel.ownerDocument.defaultView;
274
+ if (attempt < 3 && view) {
275
+ view.requestAnimationFrame(() => focusFirstItem(panel, attempt + 1));
276
+ }
277
+ }
278
+
279
+ exports.FOCUSABLE_SELECTOR = FOCUSABLE_SELECTOR;
280
+ exports.focusFirstItem = focusFirstItem;
281
+ exports.useMegaMenuNavigation = useMegaMenuNavigation;
282
+ //# sourceMappingURL=useMegaMenuNavigation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMegaMenuNavigation.js","sources":["../src/mega-menu/useMegaMenuNavigation.ts"],"sourcesContent":["import type { ElementProps, FloatingRootContext } from \"@floating-ui/react\";\nimport { useMemo } from \"react\";\n\nconst COLUMN_SELECTOR = \"[data-mega-menu-column]\";\nconst ACTIONS_SELECTOR = \"[data-mega-menu-actions]\";\n\nexport const FOCUSABLE_SELECTOR =\n 'a[href], button:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n// Controls that own the arrow keys (caret, option lists, steppers); the engine\n// leaves their keys alone when focus is inside one.\nconst SELF_CONSUMING_SELECTOR =\n 'input, textarea, select, [contenteditable], [role=\"combobox\"], [role=\"listbox\"], [role=\"slider\"], [role=\"spinbutton\"], [role=\"textbox\"]';\n\n// aria-hidden / inert subtrees are out of the focus order; filtered by ancestor\n// lookup since `:not()` cannot match on an ancestor.\nconst HIDDEN_ANCESTOR_SELECTOR = '[aria-hidden=\"true\"], [inert]';\n\nfunction queryFocusables(root: HTMLElement): HTMLElement[] {\n return Array.from(\n root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR),\n ).filter((el) => !el.closest(HIDDEN_ANCESTOR_SELECTOR));\n}\n\nfunction firstFocusable(root: HTMLElement | null): HTMLElement | null {\n return root ? (queryFocusables(root)[0] ?? null) : null;\n}\n\ninterface NavModel {\n /** Columns (`data-mega-menu-column`) in DOM order, each with ≥1 cell. */\n columns: HTMLElement[];\n /** Full-width actions row; the bottom of the center area. */\n actions: HTMLElement[];\n}\n\n/** Build the navigation model from the panel DOM at keypress time. */\nfunction buildModel(panel: HTMLElement): NavModel {\n const columns = Array.from(\n panel.querySelectorAll<HTMLElement>(COLUMN_SELECTOR),\n ).filter((el) => queryFocusables(el).length > 0);\n\n const actions = Array.from(\n panel.querySelectorAll<HTMLElement>(ACTIONS_SELECTOR),\n ).filter((el) => queryFocusables(el).length > 0);\n\n return { columns, actions };\n}\n\nfunction focusTrigger(context: FloatingRootContext) {\n const reference = context.elements.reference as HTMLElement | null;\n (firstFocusable(reference) ?? reference)?.focus();\n}\n\n/** Exit to the next nav-bar trigger and close. Returns false when there is none. */\nfunction focusNextTriggerAndClose(context: FloatingRootContext): boolean {\n const reference = context.elements.reference as HTMLElement | null;\n const trigger = firstFocusable(reference) ?? reference;\n const li = trigger?.closest(\"li\");\n const next =\n li?.nextElementSibling instanceof HTMLElement\n ? firstFocusable(li.nextElementSibling)\n : null;\n if (next) {\n context.onOpenChange(false);\n next.focus();\n return true;\n }\n return false;\n}\n\n/** True when the grid has collapsed to one stacked column (small viewport). */\nfunction columnsStacked(columns: HTMLElement[]): boolean {\n if (columns.length < 2) return false;\n const first = columns[0].getBoundingClientRect();\n const second = columns[1].getBoundingClientRect();\n return second.top >= first.bottom - 1;\n}\n\n/** Stacked layout: all arrows degrade to prev/next over every cell. */\nfunction handleLinear(\n key: string,\n cell: HTMLElement,\n panel: HTMLElement,\n context: FloatingRootContext,\n): boolean {\n const cells = queryFocusables(panel);\n const index = cells.indexOf(cell);\n if (index === -1) return false;\n\n switch (key) {\n case \"ArrowDown\":\n case \"ArrowRight\": {\n if (index < cells.length - 1) {\n cells[index + 1].focus();\n }\n return true;\n }\n case \"ArrowUp\":\n case \"ArrowLeft\": {\n if (index > 0) {\n cells[index - 1].focus();\n } else {\n focusTrigger(context);\n }\n return true;\n }\n case \"Home\": {\n cells[0]?.focus();\n return true;\n }\n case \"End\": {\n cells[cells.length - 1]?.focus();\n return true;\n }\n default:\n return false;\n }\n}\n\n/** Arrow/Home/End movement from `cell`. Returns `true` when the key was consumed. */\nfunction handleArrow(\n key: string,\n cell: HTMLElement,\n panel: HTMLElement,\n context: FloatingRootContext,\n): boolean {\n const { columns, actions } = buildModel(panel);\n\n if (columnsStacked(columns)) {\n return handleLinear(key, cell, panel, context);\n }\n\n const column = cell.closest<HTMLElement>(COLUMN_SELECTOR);\n const actionsEl = cell.closest<HTMLElement>(ACTIONS_SELECTOR);\n\n if (column) {\n const colIndex = columns.indexOf(column);\n const cells = queryFocusables(column);\n const rowIndex = cells.indexOf(cell);\n\n switch (key) {\n case \"ArrowDown\": {\n if (rowIndex < cells.length - 1) {\n cells[rowIndex + 1].focus();\n } else if (colIndex < columns.length - 1) {\n firstFocusable(columns[colIndex + 1])?.focus();\n } else if (actions.length > 0) {\n firstFocusable(actions[0])?.focus();\n } else {\n focusNextTriggerAndClose(context);\n }\n return true;\n }\n case \"ArrowUp\": {\n if (rowIndex > 0) {\n cells[rowIndex - 1].focus();\n } else {\n focusTrigger(context);\n }\n return true;\n }\n case \"ArrowRight\": {\n if (colIndex < columns.length - 1) {\n firstFocusable(columns[colIndex + 1])?.focus();\n return true;\n }\n // Right border column: exit to the next trigger and close if available\n if (\n rowIndex === cells.length - 1 &&\n focusNextTriggerAndClose(context)\n ) {\n return true;\n }\n // Wrap to the current trigger and keep the menu open,\n // mirroring Left in the first column.\n focusTrigger(context);\n return true;\n }\n case \"ArrowLeft\": {\n if (colIndex > 0) {\n firstFocusable(columns[colIndex - 1])?.focus();\n } else {\n focusTrigger(context);\n }\n return true;\n }\n case \"Home\": {\n cells[0]?.focus();\n return true;\n }\n case \"End\": {\n cells[cells.length - 1]?.focus();\n return true;\n }\n default:\n return false;\n }\n }\n\n if (actionsEl) {\n const cells = queryFocusables(actionsEl);\n const index = cells.indexOf(cell);\n\n switch (key) {\n case \"ArrowRight\": {\n if (index < cells.length - 1) {\n cells[index + 1].focus();\n } else if (!focusNextTriggerAndClose(context)) {\n // Last action with no next trigger: wrap to the current trigger.\n focusTrigger(context);\n }\n return true;\n }\n case \"ArrowLeft\": {\n if (index > 0) {\n cells[index - 1].focus();\n } else if (cell === firstFocusable(panel)) {\n focusTrigger(context);\n }\n return true;\n }\n case \"ArrowUp\": {\n if (columns.length > 0) {\n const lastColumnCells = queryFocusables(columns[columns.length - 1]);\n lastColumnCells[lastColumnCells.length - 1]?.focus();\n } else {\n focusTrigger(context);\n }\n return true;\n }\n case \"ArrowDown\": {\n if (index === cells.length - 1) {\n focusNextTriggerAndClose(context);\n }\n return true;\n }\n case \"Home\": {\n cells[0]?.focus();\n return true;\n }\n case \"End\": {\n cells[cells.length - 1]?.focus();\n return true;\n }\n default:\n return false;\n }\n }\n\n return false;\n}\n\n/** Focus the first document-focusable after the panel, once it has unmounted. */\nfunction focusNextAfterPanel(context: FloatingRootContext, panel: HTMLElement) {\n const reference = context.elements.reference as HTMLElement | null;\n const refFocusable = firstFocusable(reference) ?? reference;\n\n const nextLi = refFocusable?.closest(\"li\")?.nextElementSibling;\n const nextSibling =\n nextLi instanceof HTMLElement ? firstFocusable(nextLi) : null;\n\n const nextOutside =\n nextSibling ||\n (() => {\n const allFocusable = queryFocusables(\n panel.ownerDocument.documentElement,\n ).filter((el) => !panel.contains(el));\n const idx = refFocusable ? allFocusable.indexOf(refFocusable) : -1;\n return idx >= 0 ? allFocusable[idx + 1] : undefined;\n })();\n\n if (nextOutside) {\n const view = panel.ownerDocument.defaultView;\n view?.requestAnimationFrame(() => {\n view?.requestAnimationFrame(() => {\n nextOutside.focus();\n });\n });\n }\n}\n\nexport interface UseMegaMenuNavigationProps {\n /**\n * Whether the interaction is enabled.\n * @default true\n */\n enabled?: boolean;\n}\n\n/**\n * Floating-ui interaction hook for mega menu keyboard navigation. Handles keys\n * on the panel only; the trigger keys are owned by `MegaMenuTrigger`.\n */\nexport function useMegaMenuNavigation(\n context: FloatingRootContext,\n props: UseMegaMenuNavigationProps = {},\n): ElementProps {\n const { enabled = true } = props;\n const { open, onOpenChange } = context;\n\n return useMemo(() => {\n if (!enabled) {\n return {};\n }\n\n return {\n floating: {\n onKeyDown(event: React.KeyboardEvent) {\n if (!open) return;\n\n const panel = event.currentTarget as HTMLElement;\n const target = event.target as HTMLElement;\n const { key } = event;\n\n const isArrowOrEdge =\n key === \"ArrowUp\" ||\n key === \"ArrowDown\" ||\n key === \"ArrowLeft\" ||\n key === \"ArrowRight\" ||\n key === \"Home\" ||\n key === \"End\";\n\n // A self-consuming control keeps its own navigation keys.\n if (isArrowOrEdge && target.closest(SELF_CONSUMING_SELECTOR)) {\n return;\n }\n\n const cell = target.closest<HTMLElement>(FOCUSABLE_SELECTOR);\n\n // Tab walks every cell in layout order; the panel is portaled, so the\n // boundary transitions are handled manually.\n if (key === \"Tab\") {\n const allCells = queryFocusables(panel);\n const linearIndex = cell ? allCells.indexOf(cell) : -1;\n if (linearIndex === -1) return;\n event.preventDefault();\n if (event.shiftKey) {\n if (linearIndex === 0) {\n focusTrigger(context);\n } else {\n allCells[linearIndex - 1]?.focus();\n }\n } else if (linearIndex === allCells.length - 1) {\n onOpenChange(false);\n focusNextAfterPanel(context, panel);\n } else {\n allCells[linearIndex + 1]?.focus();\n }\n return;\n }\n\n if (!cell || !isArrowOrEdge) return;\n\n if (handleArrow(key, cell, panel, context)) {\n event.preventDefault();\n }\n },\n },\n };\n }, [enabled, open, context, onOpenChange]);\n}\n\n/** Focus the first navigable item inside a mega menu panel, retrying until rendered. */\nexport function focusFirstItem(panel: HTMLElement, attempt = 0): void {\n const firstItem = firstFocusable(panel);\n\n if (firstItem) {\n firstItem.focus();\n return;\n }\n\n const view = panel.ownerDocument.defaultView;\n if (attempt < 3 && view) {\n view.requestAnimationFrame(() => focusFirstItem(panel, attempt + 1));\n }\n}\n"],"names":["useMemo"],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var SaltProvider = require('../salt-provider/SaltProvider.js');
5
+ require('clsx');
6
+ require('react/jsx-runtime');
7
+ var useIsomorphicLayoutEffect = require('../utils/useIsomorphicLayoutEffect.js');
8
+ require('../utils/useFloatingUI/useFloatingUI.js');
9
+ require('../utils/useId.js');
10
+ require('../viewport/ViewportProvider.js');
11
+
12
+ function usePageMargin(referenceEl) {
13
+ const density = SaltProvider.useDensity();
14
+ const [pageMargin, setPageMargin] = React.useState(0);
15
+ useIsomorphicLayoutEffect.useIsomorphicLayoutEffect(() => {
16
+ if (!(referenceEl == null ? void 0 : referenceEl.parentNode)) return;
17
+ const probe = referenceEl.ownerDocument.createElement("div");
18
+ probe.style.cssText = "position:absolute;visibility:hidden;pointer-events:none;width:var(--saltMegaMenuPanel-pageMargin, var(--salt-layout-page-margin));";
19
+ referenceEl.insertAdjacentElement("afterend", probe);
20
+ const width = probe.getBoundingClientRect().width;
21
+ probe.remove();
22
+ setPageMargin((prev) => prev === width ? prev : width);
23
+ }, [referenceEl, density]);
24
+ return pageMargin;
25
+ }
26
+
27
+ exports.usePageMargin = usePageMargin;
28
+ //# sourceMappingURL=usePageMargin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePageMargin.js","sources":["../src/mega-menu/usePageMargin.ts"],"sourcesContent":["import { useState } from \"react\";\nimport { useDensity } from \"../salt-provider\";\nimport { useIsomorphicLayoutEffect } from \"../utils\";\n\n/**\n * Resolves the panel's page-margin token to pixels for floating-ui padding, to\n * handle token (`--saltMegaMenuPanel-pageMargin`) override by consumers\n * (edge to edge example).\n */\nexport function usePageMargin(\n referenceEl: HTMLElement | null | undefined,\n): number {\n const density = useDensity();\n const [pageMargin, setPageMargin] = useState(0);\n // biome-ignore lint/correctness/useExhaustiveDependencies: density isn't read here — signal that the resolved token changed.\n useIsomorphicLayoutEffect(() => {\n if (!referenceEl?.parentNode) return;\n const probe = referenceEl.ownerDocument.createElement(\"div\");\n probe.style.cssText =\n \"position:absolute;visibility:hidden;pointer-events:none;width:var(--saltMegaMenuPanel-pageMargin, var(--salt-layout-page-margin));\";\n referenceEl.insertAdjacentElement(\"afterend\", probe);\n const width = probe.getBoundingClientRect().width;\n probe.remove();\n setPageMargin((prev) => (prev === width ? prev : width));\n }, [referenceEl, density]);\n return pageMargin;\n}\n"],"names":["useDensity","useState","useIsomorphicLayoutEffect"],"mappings":";;;;;;;;;;;AASO,SAAS,cACd,WAAA,EACQ;AACR,EAAA,MAAM,UAAUA,uBAAA,EAAW;AAC3B,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIC,eAAS,CAAC,CAAA;AAE9C,EAAAC,mDAAA,CAA0B,MAAM;AAC9B,IAAA,IAAI,EAAC,2CAAa,UAAA,CAAA,EAAY;AAC9B,IAAA,MAAM,KAAA,GAAQ,WAAA,CAAY,aAAA,CAAc,aAAA,CAAc,KAAK,CAAA;AAC3D,IAAA,KAAA,CAAM,MAAM,OAAA,GACV,oIAAA;AACF,IAAA,WAAA,CAAY,qBAAA,CAAsB,YAAY,KAAK,CAAA;AACnD,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,qBAAA,EAAsB,CAAE,KAAA;AAC5C,IAAA,KAAA,CAAM,MAAA,EAAO;AACb,IAAA,aAAA,CAAc,CAAC,IAAA,KAAU,IAAA,KAAS,KAAA,GAAQ,OAAO,KAAM,CAAA;AAAA,EACzD,CAAA,EAAG,CAAC,WAAA,EAAa,OAAO,CAAC,CAAA;AACzB,EAAA,OAAO,UAAA;AACT;;;;"}
@@ -55,7 +55,8 @@ const OverlayPanel = React.forwardRef(
55
55
  left,
56
56
  position,
57
57
  focusManagerProps: {
58
- context
58
+ context,
59
+ outsideElementsInert: true
59
60
  },
60
61
  "aria-labelledby": ariaLabelledby,
61
62
  children: [
@@ -1 +1 @@
1
- {"version":3,"file":"OverlayPanel.js","sources":["../src/overlay/OverlayPanel.tsx"],"sourcesContent":["import { FloatingArrow } from \"@floating-ui/react\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport { clsx } from \"clsx\";\nimport {\n type ComponentPropsWithoutRef,\n forwardRef,\n type ReactNode,\n} from \"react\";\nimport { makePrefixer, useFloatingComponent, useForkRef } from \"../utils\";\nimport { useOverlayContext } from \"./OverlayContext\";\nimport overlayPanelCss from \"./OverlayPanel.css\";\n\nconst withBaseName = makePrefixer(\"saltOverlayPanel\");\nexport interface OverlayPanelProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The content of Overlay Panel\n */\n children?: ReactNode;\n}\n\nexport const OverlayPanel = forwardRef<HTMLDivElement, OverlayPanelProps>(\n function OverlayPanel(props, ref) {\n const {\n className,\n \"aria-labelledby\": ariaLabelledby,\n children,\n ...rest\n } = props;\n\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-overlay-panel\",\n css: overlayPanelCss,\n window: targetWindow,\n });\n\n const { Component: FloatingComponent } = useFloatingComponent();\n\n const {\n openState,\n floatingStyles,\n context,\n getFloatingProps,\n floating,\n arrowProps,\n } = useOverlayContext();\n\n const handleRef = useForkRef<HTMLDivElement>(floating, ref);\n\n const { top, left, width, height, position } = floatingStyles;\n\n return (\n <FloatingComponent\n open={openState}\n className={clsx(withBaseName(), className)}\n aria-modal=\"true\"\n {...getFloatingProps()}\n ref={handleRef}\n width={width}\n height={height}\n top={top}\n left={left}\n position={position}\n focusManagerProps={{\n context: context,\n }}\n aria-labelledby={ariaLabelledby}\n >\n <div {...rest}> {children} </div>\n <FloatingArrow\n {...arrowProps}\n strokeWidth={1}\n fill=\"var(--overlay-background)\"\n stroke=\"var(--overlay-borderColor)\"\n height={6}\n width={12}\n />\n </FloatingComponent>\n );\n },\n);\n"],"names":["makePrefixer","forwardRef","OverlayPanel","useWindow","useComponentCssInjection","overlayPanelCss","useFloatingComponent","useOverlayContext","useForkRef","jsxs","clsx","jsx","FloatingArrow"],"mappings":";;;;;;;;;;;;;;;;;AAaA,MAAM,YAAA,GAAeA,0BAAa,kBAAkB,CAAA;AAQ7C,MAAM,YAAA,GAAeC,gBAAA;AAAA,EAC1B,SAASC,aAAAA,CAAa,KAAA,EAAO,GAAA,EAAK;AAChC,IAAA,MAAM;AAAA,MACJ,SAAA;AAAA,MACA,iBAAA,EAAmB,cAAA;AAAA,MACnB,QAAA;AAAA,MACA,GAAG;AAAA,KACL,GAAI,KAAA;AAEJ,IAAA,MAAM,eAAeC,gBAAA,EAAU;AAC/B,IAAAC,+BAAA,CAAyB;AAAA,MACvB,MAAA,EAAQ,oBAAA;AAAA,MACR,GAAA,EAAKC,cAAA;AAAA,MACL,MAAA,EAAQ;AAAA,KACT,CAAA;AAED,IAAA,MAAM,EAAE,SAAA,EAAW,iBAAA,EAAkB,GAAIC,kCAAA,EAAqB;AAE9D,IAAA,MAAM;AAAA,MACJ,SAAA;AAAA,MACA,cAAA;AAAA,MACA,OAAA;AAAA,MACA,gBAAA;AAAA,MACA,QAAA;AAAA,MACA;AAAA,QACEC,gCAAA,EAAkB;AAEtB,IAAA,MAAM,SAAA,GAAYC,qBAAA,CAA2B,QAAA,EAAU,GAAG,CAAA;AAE1D,IAAA,MAAM,EAAE,GAAA,EAAK,IAAA,EAAM,KAAA,EAAO,MAAA,EAAQ,UAAS,GAAI,cAAA;AAE/C,IAAA,uBACEC,eAAA;AAAA,MAAC,iBAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAM,SAAA;AAAA,QACN,SAAA,EAAWC,SAAA,CAAK,YAAA,EAAa,EAAG,SAAS,CAAA;AAAA,QACzC,YAAA,EAAW,MAAA;AAAA,QACV,GAAG,gBAAA,EAAiB;AAAA,QACrB,GAAA,EAAK,SAAA;AAAA,QACL,KAAA;AAAA,QACA,MAAA;AAAA,QACA,GAAA;AAAA,QACA,IAAA;AAAA,QACA,QAAA;AAAA,QACA,iBAAA,EAAmB;AAAA,UACjB;AAAA,SACF;AAAA,QACA,iBAAA,EAAiB,cAAA;AAAA,QAEjB,QAAA,EAAA;AAAA,0BAAAD,eAAA,CAAC,KAAA,EAAA,EAAK,GAAG,IAAA,EAAM,QAAA,EAAA;AAAA,YAAA,GAAA;AAAA,YAAE,QAAA;AAAA,YAAS;AAAA,WAAA,EAAC,CAAA;AAAA,0BAC3BE,cAAA;AAAA,YAACC,mBAAA;AAAA,YAAA;AAAA,cACE,GAAG,UAAA;AAAA,cACJ,WAAA,EAAa,CAAA;AAAA,cACb,IAAA,EAAK,2BAAA;AAAA,cACL,MAAA,EAAO,4BAAA;AAAA,cACP,MAAA,EAAQ,CAAA;AAAA,cACR,KAAA,EAAO;AAAA;AAAA;AACT;AAAA;AAAA,KACF;AAAA,EAEJ;AACF;;;;"}
1
+ {"version":3,"file":"OverlayPanel.js","sources":["../src/overlay/OverlayPanel.tsx"],"sourcesContent":["import { FloatingArrow } from \"@floating-ui/react\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport { clsx } from \"clsx\";\nimport {\n type ComponentPropsWithoutRef,\n forwardRef,\n type ReactNode,\n} from \"react\";\nimport { makePrefixer, useFloatingComponent, useForkRef } from \"../utils\";\nimport { useOverlayContext } from \"./OverlayContext\";\nimport overlayPanelCss from \"./OverlayPanel.css\";\n\nconst withBaseName = makePrefixer(\"saltOverlayPanel\");\nexport interface OverlayPanelProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The content of Overlay Panel\n */\n children?: ReactNode;\n}\n\nexport const OverlayPanel = forwardRef<HTMLDivElement, OverlayPanelProps>(\n function OverlayPanel(props, ref) {\n const {\n className,\n \"aria-labelledby\": ariaLabelledby,\n children,\n ...rest\n } = props;\n\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-overlay-panel\",\n css: overlayPanelCss,\n window: targetWindow,\n });\n\n const { Component: FloatingComponent } = useFloatingComponent();\n\n const {\n openState,\n floatingStyles,\n context,\n getFloatingProps,\n floating,\n arrowProps,\n } = useOverlayContext();\n\n const handleRef = useForkRef<HTMLDivElement>(floating, ref);\n\n const { top, left, width, height, position } = floatingStyles;\n\n return (\n <FloatingComponent\n open={openState}\n className={clsx(withBaseName(), className)}\n aria-modal=\"true\"\n {...getFloatingProps()}\n ref={handleRef}\n width={width}\n height={height}\n top={top}\n left={left}\n position={position}\n focusManagerProps={{\n context: context,\n outsideElementsInert: true,\n }}\n aria-labelledby={ariaLabelledby}\n >\n <div {...rest}> {children} </div>\n <FloatingArrow\n {...arrowProps}\n strokeWidth={1}\n fill=\"var(--overlay-background)\"\n stroke=\"var(--overlay-borderColor)\"\n height={6}\n width={12}\n />\n </FloatingComponent>\n );\n },\n);\n"],"names":["makePrefixer","forwardRef","OverlayPanel","useWindow","useComponentCssInjection","overlayPanelCss","useFloatingComponent","useOverlayContext","useForkRef","jsxs","clsx","jsx","FloatingArrow"],"mappings":";;;;;;;;;;;;;;;;;AAaA,MAAM,YAAA,GAAeA,0BAAa,kBAAkB,CAAA;AAQ7C,MAAM,YAAA,GAAeC,gBAAA;AAAA,EAC1B,SAASC,aAAAA,CAAa,KAAA,EAAO,GAAA,EAAK;AAChC,IAAA,MAAM;AAAA,MACJ,SAAA;AAAA,MACA,iBAAA,EAAmB,cAAA;AAAA,MACnB,QAAA;AAAA,MACA,GAAG;AAAA,KACL,GAAI,KAAA;AAEJ,IAAA,MAAM,eAAeC,gBAAA,EAAU;AAC/B,IAAAC,+BAAA,CAAyB;AAAA,MACvB,MAAA,EAAQ,oBAAA;AAAA,MACR,GAAA,EAAKC,cAAA;AAAA,MACL,MAAA,EAAQ;AAAA,KACT,CAAA;AAED,IAAA,MAAM,EAAE,SAAA,EAAW,iBAAA,EAAkB,GAAIC,kCAAA,EAAqB;AAE9D,IAAA,MAAM;AAAA,MACJ,SAAA;AAAA,MACA,cAAA;AAAA,MACA,OAAA;AAAA,MACA,gBAAA;AAAA,MACA,QAAA;AAAA,MACA;AAAA,QACEC,gCAAA,EAAkB;AAEtB,IAAA,MAAM,SAAA,GAAYC,qBAAA,CAA2B,QAAA,EAAU,GAAG,CAAA;AAE1D,IAAA,MAAM,EAAE,GAAA,EAAK,IAAA,EAAM,KAAA,EAAO,MAAA,EAAQ,UAAS,GAAI,cAAA;AAE/C,IAAA,uBACEC,eAAA;AAAA,MAAC,iBAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAM,SAAA;AAAA,QACN,SAAA,EAAWC,SAAA,CAAK,YAAA,EAAa,EAAG,SAAS,CAAA;AAAA,QACzC,YAAA,EAAW,MAAA;AAAA,QACV,GAAG,gBAAA,EAAiB;AAAA,QACrB,GAAA,EAAK,SAAA;AAAA,QACL,KAAA;AAAA,QACA,MAAA;AAAA,QACA,GAAA;AAAA,QACA,IAAA;AAAA,QACA,QAAA;AAAA,QACA,iBAAA,EAAmB;AAAA,UACjB,OAAA;AAAA,UACA,oBAAA,EAAsB;AAAA,SACxB;AAAA,QACA,iBAAA,EAAiB,cAAA;AAAA,QAEjB,QAAA,EAAA;AAAA,0BAAAD,eAAA,CAAC,KAAA,EAAA,EAAK,GAAG,IAAA,EAAM,QAAA,EAAA;AAAA,YAAA,GAAA;AAAA,YAAE,QAAA;AAAA,YAAS;AAAA,WAAA,EAAC,CAAA;AAAA,0BAC3BE,cAAA;AAAA,YAACC,mBAAA;AAAA,YAAA;AAAA,cACE,GAAG,UAAA;AAAA,cACJ,WAAA,EAAa,CAAA;AAAA,cACb,IAAA,EAAK,2BAAA;AAAA,cACL,MAAA,EAAO,4BAAA;AAAA,cACP,MAAA,EAAQ,CAAA;AAAA,cACR,KAAA,EAAO;AAAA;AAAA;AACT;AAAA;AAAA,KACF;AAAA,EAEJ;AACF;;;;"}
@@ -26,6 +26,7 @@ const defaultIconMap = {
26
26
  InfoIcon: icons.InfoSolidIcon,
27
27
  InProgressIcon: icons.ProgressInprogressIcon,
28
28
  LockedIcon: icons.LockedIcon,
29
+ LevelSeparatorIcon: icons.ChevronRightIcon,
29
30
  NextIcon: icons.ChevronRightIcon,
30
31
  OverflowIcon: icons.OverflowMenuIcon,
31
32
  PendingIcon: icons.StepDefaultIcon,
@@ -1 +1 @@
1
- {"version":3,"file":"SemanticIconProvider.js","sources":["../src/semantic-icon-provider/SemanticIconProvider.tsx"],"sourcesContent":["import {\n CalendarIcon,\n ChevronDownIcon,\n ChevronLeftIcon,\n ChevronRightIcon,\n ChevronUpIcon,\n CloseIcon,\n DoubleChevronLeftIcon,\n DoubleChevronRightIcon,\n ErrorSolidIcon,\n FavoriteIcon,\n FavoriteSolidIcon,\n FavoriteStrongIcon,\n InfoSolidIcon,\n LockedIcon,\n OverflowMenuIcon,\n ProgressInprogressIcon,\n StepActiveIcon,\n StepDefaultIcon,\n SuccessCircleSolidIcon,\n TearOutIcon,\n TriangleDownIcon,\n TriangleUpIcon,\n UploadIcon,\n UserSolidIcon,\n WarningSolidIcon,\n} from \"@salt-ds/icons\";\nimport {\n createContext,\n type ElementType,\n type ReactNode,\n useContext,\n} from \"react\";\nimport { ErrorAdornmentIcon } from \"../status-adornment/ErrorAdornment\";\nimport { SuccessAdornmentIcon } from \"../status-adornment/SuccessAdornment\";\nimport { WarningAdornmentIcon } from \"../status-adornment/WarningAdornment\";\n\nexport interface SemanticIconMap {\n ActiveIcon: ElementType;\n CalendarIcon: ElementType;\n CloseIcon: ElementType;\n CollapseGroupIcon: ElementType;\n CollapseIcon: ElementType;\n CollapseLeftIcon: ElementType;\n CollapseRightIcon: ElementType;\n CompletedIcon: ElementType;\n DecreaseIcon: ElementType;\n ErrorIcon: ElementType;\n ErrorStatusAdornment: ElementType;\n ExpandGroupIcon: ElementType;\n ExpandIcon: ElementType;\n ExternalIcon: ElementType;\n IncreaseIcon: ElementType;\n InfoIcon: ElementType;\n InProgressIcon: ElementType;\n LockedIcon: ElementType;\n NextIcon: ElementType;\n OverflowIcon: ElementType;\n PendingIcon: ElementType;\n PreviousIcon: ElementType;\n RatingIcon: ElementType;\n RatingSelectedIcon: ElementType;\n RatingUnselectingIcon: ElementType;\n SuccessIcon: ElementType;\n SuccessStatusAdornment: ElementType;\n UploadIcon: ElementType;\n UserIcon: ElementType;\n WarningIcon: ElementType;\n WarningStatusAdornment: ElementType;\n}\n\nexport interface SemanticIconProviderProps {\n /**\n * Custom mapping of icon names to components. Overrides default icons if provided.\n */\n iconMap?: Partial<SemanticIconMap>;\n\n /**\n * Child elements that will use the provided icons.\n */\n children: ReactNode;\n}\n\nconst defaultIconMap: SemanticIconMap = {\n ActiveIcon: StepActiveIcon,\n CalendarIcon: CalendarIcon,\n CloseIcon: CloseIcon,\n CollapseGroupIcon: ChevronDownIcon,\n CollapseIcon: ChevronUpIcon,\n CollapseLeftIcon: DoubleChevronLeftIcon,\n CollapseRightIcon: DoubleChevronRightIcon,\n CompletedIcon: SuccessCircleSolidIcon,\n DecreaseIcon: TriangleDownIcon,\n ErrorIcon: ErrorSolidIcon,\n ErrorStatusAdornment: ErrorAdornmentIcon,\n ExpandGroupIcon: ChevronRightIcon,\n ExpandIcon: ChevronDownIcon,\n ExternalIcon: TearOutIcon,\n IncreaseIcon: TriangleUpIcon,\n InfoIcon: InfoSolidIcon,\n InProgressIcon: ProgressInprogressIcon,\n LockedIcon: LockedIcon,\n NextIcon: ChevronRightIcon,\n OverflowIcon: OverflowMenuIcon,\n PendingIcon: StepDefaultIcon,\n PreviousIcon: ChevronLeftIcon,\n RatingIcon: FavoriteIcon,\n RatingSelectedIcon: FavoriteSolidIcon,\n RatingUnselectingIcon: FavoriteStrongIcon,\n SuccessIcon: SuccessCircleSolidIcon,\n SuccessStatusAdornment: SuccessAdornmentIcon,\n UploadIcon,\n UserIcon: UserSolidIcon,\n WarningIcon: WarningSolidIcon,\n WarningStatusAdornment: WarningAdornmentIcon,\n};\n\nconst SemanticIconContext = createContext<SemanticIconMap>(defaultIconMap);\n\nexport const SemanticIconProvider = ({\n iconMap = {},\n children,\n}: SemanticIconProviderProps) => (\n <SemanticIconContext.Provider value={{ ...defaultIconMap, ...iconMap }}>\n {children}\n </SemanticIconContext.Provider>\n);\n\nexport const useIcon = () => {\n const context = useContext(SemanticIconContext);\n return context || defaultIconMap;\n};\n"],"names":["StepActiveIcon","CalendarIcon","CloseIcon","ChevronDownIcon","ChevronUpIcon","DoubleChevronLeftIcon","DoubleChevronRightIcon","SuccessCircleSolidIcon","TriangleDownIcon","ErrorSolidIcon","ErrorAdornmentIcon","ChevronRightIcon","TearOutIcon","TriangleUpIcon","InfoSolidIcon","ProgressInprogressIcon","LockedIcon","OverflowMenuIcon","StepDefaultIcon","ChevronLeftIcon","FavoriteIcon","FavoriteSolidIcon","FavoriteStrongIcon","SuccessAdornmentIcon","UploadIcon","UserSolidIcon","WarningSolidIcon","WarningAdornmentIcon","createContext","jsx","useContext"],"mappings":";;;;;;;;;AAmFA,MAAM,cAAA,GAAkC;AAAA,EACtC,UAAA,EAAYA,oBAAA;AAAA,gBACZC,kBAAA;AAAA,aACAC,eAAA;AAAA,EACA,iBAAA,EAAmBC,qBAAA;AAAA,EACnB,YAAA,EAAcC,mBAAA;AAAA,EACd,gBAAA,EAAkBC,2BAAA;AAAA,EAClB,iBAAA,EAAmBC,4BAAA;AAAA,EACnB,aAAA,EAAeC,4BAAA;AAAA,EACf,YAAA,EAAcC,sBAAA;AAAA,EACd,SAAA,EAAWC,oBAAA;AAAA,EACX,oBAAA,EAAsBC,iCAAA;AAAA,EACtB,eAAA,EAAiBC,sBAAA;AAAA,EACjB,UAAA,EAAYR,qBAAA;AAAA,EACZ,YAAA,EAAcS,iBAAA;AAAA,EACd,YAAA,EAAcC,oBAAA;AAAA,EACd,QAAA,EAAUC,mBAAA;AAAA,EACV,cAAA,EAAgBC,4BAAA;AAAA,cAChBC,gBAAA;AAAA,EACA,QAAA,EAAUL,sBAAA;AAAA,EACV,YAAA,EAAcM,sBAAA;AAAA,EACd,WAAA,EAAaC,qBAAA;AAAA,EACb,YAAA,EAAcC,qBAAA;AAAA,EACd,UAAA,EAAYC,kBAAA;AAAA,EACZ,kBAAA,EAAoBC,uBAAA;AAAA,EACpB,qBAAA,EAAuBC,wBAAA;AAAA,EACvB,WAAA,EAAaf,4BAAA;AAAA,EACb,sBAAA,EAAwBgB,qCAAA;AAAA,cACxBC,gBAAA;AAAA,EACA,QAAA,EAAUC,mBAAA;AAAA,EACV,WAAA,EAAaC,sBAAA;AAAA,EACb,sBAAA,EAAwBC;AAC1B,CAAA;AAEA,MAAM,mBAAA,GAAsBC,oBAA+B,cAAc,CAAA;AAElE,MAAM,uBAAuB,CAAC;AAAA,EACnC,UAAU,EAAC;AAAA,EACX;AACF,CAAA,qBACEC,cAAA,CAAC,mBAAA,CAAoB,QAAA,EAApB,EAA6B,KAAA,EAAO,EAAE,GAAG,cAAA,EAAgB,GAAG,OAAA,EAAQ,EAClE,QAAA,EACH;AAGK,MAAM,UAAU,MAAM;AAC3B,EAAA,MAAM,OAAA,GAAUC,iBAAW,mBAAmB,CAAA;AAC9C,EAAA,OAAO,OAAA,IAAW,cAAA;AACpB;;;;;"}
1
+ {"version":3,"file":"SemanticIconProvider.js","sources":["../src/semantic-icon-provider/SemanticIconProvider.tsx"],"sourcesContent":["import {\n CalendarIcon,\n ChevronDownIcon,\n ChevronLeftIcon,\n ChevronRightIcon,\n ChevronUpIcon,\n CloseIcon,\n DoubleChevronLeftIcon,\n DoubleChevronRightIcon,\n ErrorSolidIcon,\n FavoriteIcon,\n FavoriteSolidIcon,\n FavoriteStrongIcon,\n InfoSolidIcon,\n LockedIcon,\n OverflowMenuIcon,\n ProgressInprogressIcon,\n StepActiveIcon,\n StepDefaultIcon,\n SuccessCircleSolidIcon,\n TearOutIcon,\n TriangleDownIcon,\n TriangleUpIcon,\n UploadIcon,\n UserSolidIcon,\n WarningSolidIcon,\n} from \"@salt-ds/icons\";\nimport {\n createContext,\n type ElementType,\n type ReactNode,\n useContext,\n} from \"react\";\nimport { ErrorAdornmentIcon } from \"../status-adornment/ErrorAdornment\";\nimport { SuccessAdornmentIcon } from \"../status-adornment/SuccessAdornment\";\nimport { WarningAdornmentIcon } from \"../status-adornment/WarningAdornment\";\n\nexport interface SemanticIconMap {\n ActiveIcon: ElementType;\n CalendarIcon: ElementType;\n CloseIcon: ElementType;\n CollapseGroupIcon: ElementType;\n CollapseIcon: ElementType;\n CollapseLeftIcon: ElementType;\n CollapseRightIcon: ElementType;\n CompletedIcon: ElementType;\n DecreaseIcon: ElementType;\n ErrorIcon: ElementType;\n ErrorStatusAdornment: ElementType;\n ExpandGroupIcon: ElementType;\n ExpandIcon: ElementType;\n ExternalIcon: ElementType;\n IncreaseIcon: ElementType;\n InfoIcon: ElementType;\n InProgressIcon: ElementType;\n LockedIcon: ElementType;\n LevelSeparatorIcon: ElementType;\n NextIcon: ElementType;\n OverflowIcon: ElementType;\n PendingIcon: ElementType;\n PreviousIcon: ElementType;\n RatingIcon: ElementType;\n RatingSelectedIcon: ElementType;\n RatingUnselectingIcon: ElementType;\n SuccessIcon: ElementType;\n SuccessStatusAdornment: ElementType;\n UploadIcon: ElementType;\n UserIcon: ElementType;\n WarningIcon: ElementType;\n WarningStatusAdornment: ElementType;\n}\n\nexport interface SemanticIconProviderProps {\n /**\n * Custom mapping of icon names to components. Overrides default icons if provided.\n */\n iconMap?: Partial<SemanticIconMap>;\n\n /**\n * Child elements that will use the provided icons.\n */\n children: ReactNode;\n}\n\nconst defaultIconMap: SemanticIconMap = {\n ActiveIcon: StepActiveIcon,\n CalendarIcon: CalendarIcon,\n CloseIcon: CloseIcon,\n CollapseGroupIcon: ChevronDownIcon,\n CollapseIcon: ChevronUpIcon,\n CollapseLeftIcon: DoubleChevronLeftIcon,\n CollapseRightIcon: DoubleChevronRightIcon,\n CompletedIcon: SuccessCircleSolidIcon,\n DecreaseIcon: TriangleDownIcon,\n ErrorIcon: ErrorSolidIcon,\n ErrorStatusAdornment: ErrorAdornmentIcon,\n ExpandGroupIcon: ChevronRightIcon,\n ExpandIcon: ChevronDownIcon,\n ExternalIcon: TearOutIcon,\n IncreaseIcon: TriangleUpIcon,\n InfoIcon: InfoSolidIcon,\n InProgressIcon: ProgressInprogressIcon,\n LockedIcon: LockedIcon,\n LevelSeparatorIcon: ChevronRightIcon,\n NextIcon: ChevronRightIcon,\n OverflowIcon: OverflowMenuIcon,\n PendingIcon: StepDefaultIcon,\n PreviousIcon: ChevronLeftIcon,\n RatingIcon: FavoriteIcon,\n RatingSelectedIcon: FavoriteSolidIcon,\n RatingUnselectingIcon: FavoriteStrongIcon,\n SuccessIcon: SuccessCircleSolidIcon,\n SuccessStatusAdornment: SuccessAdornmentIcon,\n UploadIcon,\n UserIcon: UserSolidIcon,\n WarningIcon: WarningSolidIcon,\n WarningStatusAdornment: WarningAdornmentIcon,\n};\n\nconst SemanticIconContext = createContext<SemanticIconMap>(defaultIconMap);\n\nexport const SemanticIconProvider = ({\n iconMap = {},\n children,\n}: SemanticIconProviderProps) => (\n <SemanticIconContext.Provider value={{ ...defaultIconMap, ...iconMap }}>\n {children}\n </SemanticIconContext.Provider>\n);\n\nexport const useIcon = () => {\n const context = useContext(SemanticIconContext);\n return context || defaultIconMap;\n};\n"],"names":["StepActiveIcon","CalendarIcon","CloseIcon","ChevronDownIcon","ChevronUpIcon","DoubleChevronLeftIcon","DoubleChevronRightIcon","SuccessCircleSolidIcon","TriangleDownIcon","ErrorSolidIcon","ErrorAdornmentIcon","ChevronRightIcon","TearOutIcon","TriangleUpIcon","InfoSolidIcon","ProgressInprogressIcon","LockedIcon","OverflowMenuIcon","StepDefaultIcon","ChevronLeftIcon","FavoriteIcon","FavoriteSolidIcon","FavoriteStrongIcon","SuccessAdornmentIcon","UploadIcon","UserSolidIcon","WarningSolidIcon","WarningAdornmentIcon","createContext","jsx","useContext"],"mappings":";;;;;;;;;AAoFA,MAAM,cAAA,GAAkC;AAAA,EACtC,UAAA,EAAYA,oBAAA;AAAA,gBACZC,kBAAA;AAAA,aACAC,eAAA;AAAA,EACA,iBAAA,EAAmBC,qBAAA;AAAA,EACnB,YAAA,EAAcC,mBAAA;AAAA,EACd,gBAAA,EAAkBC,2BAAA;AAAA,EAClB,iBAAA,EAAmBC,4BAAA;AAAA,EACnB,aAAA,EAAeC,4BAAA;AAAA,EACf,YAAA,EAAcC,sBAAA;AAAA,EACd,SAAA,EAAWC,oBAAA;AAAA,EACX,oBAAA,EAAsBC,iCAAA;AAAA,EACtB,eAAA,EAAiBC,sBAAA;AAAA,EACjB,UAAA,EAAYR,qBAAA;AAAA,EACZ,YAAA,EAAcS,iBAAA;AAAA,EACd,YAAA,EAAcC,oBAAA;AAAA,EACd,QAAA,EAAUC,mBAAA;AAAA,EACV,cAAA,EAAgBC,4BAAA;AAAA,cAChBC,gBAAA;AAAA,EACA,kBAAA,EAAoBL,sBAAA;AAAA,EACpB,QAAA,EAAUA,sBAAA;AAAA,EACV,YAAA,EAAcM,sBAAA;AAAA,EACd,WAAA,EAAaC,qBAAA;AAAA,EACb,YAAA,EAAcC,qBAAA;AAAA,EACd,UAAA,EAAYC,kBAAA;AAAA,EACZ,kBAAA,EAAoBC,uBAAA;AAAA,EACpB,qBAAA,EAAuBC,wBAAA;AAAA,EACvB,WAAA,EAAaf,4BAAA;AAAA,EACb,sBAAA,EAAwBgB,qCAAA;AAAA,cACxBC,gBAAA;AAAA,EACA,QAAA,EAAUC,mBAAA;AAAA,EACV,WAAA,EAAaC,sBAAA;AAAA,EACb,sBAAA,EAAwBC;AAC1B,CAAA;AAEA,MAAM,mBAAA,GAAsBC,oBAA+B,cAAc,CAAA;AAElE,MAAM,uBAAuB,CAAC;AAAA,EACnC,UAAU,EAAC;AAAA,EACX;AACF,CAAA,qBACEC,cAAA,CAAC,mBAAA,CAAoB,QAAA,EAApB,EAA6B,KAAA,EAAO,EAAE,GAAG,cAAA,EAAgB,GAAG,OAAA,EAAQ,EAClE,QAAA,EACH;AAGK,MAAM,UAAU,MAAM;AAC3B,EAAA,MAAM,OAAA,GAAUC,iBAAW,mBAAmB,CAAA;AAC9C,EAAA,OAAO,OAAA,IAAW,cAAA;AACpB;;;;;"}
@@ -1,5 +1,6 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
- import { forwardRef, useState, useRef, useCallback, useMemo } from 'react';
2
+ import { useWindow } from '@salt-ds/window';
3
+ import { forwardRef, useState, useRef, useCallback, useEffect, useMemo } from 'react';
3
4
  import { useIsomorphicLayoutEffect } from '../utils/useIsomorphicLayoutEffect.js';
4
5
  import { AriaAnnouncerContext } from './AriaAnnouncerContext.js';
5
6
  import { registerAnnouncementTarget } from './announcementRegistry.js';
@@ -16,9 +17,25 @@ const AnnouncementRegion = forwardRef(function AnnouncementRegion2(props, ref) {
16
17
  );
17
18
  });
18
19
  const AriaAnnouncerProvider = forwardRef(function AriaAnnouncerProvider2({ children, style, target, ...rest }, ref) {
20
+ const targetWindow = useWindow();
19
21
  const [politeAnnouncements, setPoliteAnnouncements] = useState([]);
20
22
  const [assertiveAnnouncements, setAssertiveAnnouncements] = useState([]);
21
23
  const idCounterRef = useRef(0);
24
+ const timeoutsRef = useRef(/* @__PURE__ */ new Set());
25
+ const scheduleRemoval = useCallback(
26
+ (id, assertiveness, duration) => {
27
+ if (!targetWindow) return;
28
+ const setter = assertiveness === "polite" ? setPoliteAnnouncements : setAssertiveAnnouncements;
29
+ const handle = targetWindow.setTimeout(() => {
30
+ timeoutsRef.current.delete(handle);
31
+ setter(
32
+ (previous) => previous.filter((announcement) => announcement.id !== id)
33
+ );
34
+ }, duration);
35
+ timeoutsRef.current.add(handle);
36
+ },
37
+ [targetWindow]
38
+ );
22
39
  const makeAnnouncement = useCallback(
23
40
  (message, assertiveness = "polite", duration = ANNOUNCEMENT_TIME_IN_DOM) => {
24
41
  idCounterRef.current += 1;
@@ -27,24 +44,24 @@ const AriaAnnouncerProvider = forwardRef(function AriaAnnouncerProvider2({ child
27
44
  setPoliteAnnouncements((previous) => {
28
45
  return previous.concat({ id, message });
29
46
  });
30
- setTimeout(() => {
31
- setPoliteAnnouncements(
32
- (previous) => previous.filter((announcement) => announcement.id !== id)
33
- );
34
- }, duration);
35
47
  } else {
36
48
  setAssertiveAnnouncements((previous) => {
37
49
  return previous.concat({ id, message });
38
50
  });
39
- setTimeout(() => {
40
- setAssertiveAnnouncements(
41
- (previous) => previous.filter((announcement) => announcement.id !== id)
42
- );
43
- }, duration);
44
51
  }
52
+ scheduleRemoval(id, assertiveness, duration);
45
53
  },
46
- []
54
+ [scheduleRemoval]
47
55
  );
56
+ useEffect(() => {
57
+ const timeouts = timeoutsRef.current;
58
+ return () => {
59
+ for (const handle of timeouts) {
60
+ targetWindow == null ? void 0 : targetWindow.clearTimeout(handle);
61
+ }
62
+ timeouts.clear();
63
+ };
64
+ }, [targetWindow]);
48
65
  const announce = useCallback(
49
66
  (announcement, legacyDelayOrOptions = {}) => {
50
67
  const options = typeof legacyDelayOrOptions === "object" && legacyDelayOrOptions ? legacyDelayOrOptions : {};
@@ -1 +1 @@
1
- {"version":3,"file":"AriaAnnouncerProvider.js","sources":["../src/aria-announcer/AriaAnnouncerProvider.tsx"],"sourcesContent":["import {\n type ComponentPropsWithRef,\n forwardRef,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { useIsomorphicLayoutEffect } from \"../utils/useIsomorphicLayoutEffect\";\nimport {\n type AnnounceFnOptions,\n AriaAnnouncerContext,\n} from \"./AriaAnnouncerContext\";\nimport { registerAnnouncementTarget } from \"./announcementRegistry\";\n\nexport const ANNOUNCEMENT_TIME_IN_DOM = 300; // time between DOM updates\n\nexport interface AriaAnnouncerProviderProps\n extends ComponentPropsWithRef<\"div\"> {\n /**\n * Optional target key used to route announcements to this provider.\n */\n target?: string;\n}\n\nconst AnnouncementRegion = forwardRef<\n HTMLDivElement,\n ComponentPropsWithRef<\"div\">\n>(function AnnouncementRegion(props, ref) {\n return (\n <div\n // Keep the region simple for maximum assistive-tech compatibility.\n // aria-live is applied by the caller (polite/assertive).\n aria-atomic={false}\n ref={ref}\n {...props}\n />\n );\n});\n\ntype AnnouncementMessage = {\n id: string;\n message: string;\n};\n\nexport const AriaAnnouncerProvider = forwardRef<\n HTMLDivElement,\n AriaAnnouncerProviderProps\n>(function AriaAnnouncerProvider({ children, style, target, ...rest }, ref) {\n const [politeAnnouncements, setPoliteAnnouncements] = useState<\n AnnouncementMessage[]\n >([]);\n const [assertiveAnnouncements, setAssertiveAnnouncements] = useState<\n AnnouncementMessage[]\n >([]);\n\n const idCounterRef = useRef(0);\n\n const makeAnnouncement = useCallback(\n (\n message: string,\n assertiveness: \"polite\" | \"assertive\" = \"polite\",\n duration: number = ANNOUNCEMENT_TIME_IN_DOM,\n ) => {\n idCounterRef.current += 1;\n // Date.now() can collide when multiple announcements are created in the same millisecond\n // (e.g. tests with cy.clock, batching, or multiple announces during one tick).\n // Add a monotonic counter suffix to guarantee uniqueness.\n const id = `announce-${assertiveness}-${Date.now()}-${idCounterRef.current}`;\n if (assertiveness === \"polite\") {\n setPoliteAnnouncements((previous) => {\n return previous.concat({ id, message });\n });\n\n setTimeout(() => {\n setPoliteAnnouncements((previous) =>\n previous.filter((announcement) => announcement.id !== id),\n );\n }, duration);\n } else {\n setAssertiveAnnouncements((previous) => {\n return previous.concat({ id, message });\n });\n\n setTimeout(() => {\n setAssertiveAnnouncements((previous) =>\n previous.filter((announcement) => announcement.id !== id),\n );\n }, duration);\n }\n },\n [],\n );\n\n const announce = useCallback(\n (\n announcement: string,\n legacyDelayOrOptions: number | AnnounceFnOptions | undefined = {},\n ) => {\n // Legacy delay (number arg) is handled by useAriaAnnouncer; if we also delayed\n // here we'd apply it twice. Keep supporting the signature but ignore the delay.\n const options: AnnounceFnOptions =\n typeof legacyDelayOrOptions === \"object\" && legacyDelayOrOptions\n ? legacyDelayOrOptions\n : {};\n\n makeAnnouncement(\n announcement,\n options.ariaLive,\n options.duration ?? ANNOUNCEMENT_TIME_IN_DOM,\n );\n },\n [makeAnnouncement],\n );\n\n const value = useMemo(() => ({ announce }), [announce]);\n\n useIsomorphicLayoutEffect(() => {\n if (!target) {\n return;\n }\n return registerAnnouncementTarget(target, announce);\n }, [announce, target]);\n\n return (\n <AriaAnnouncerContext.Provider value={value}>\n {children}\n <div\n // hidden styling based on https://tailwindcss.com/docs/screen-readers\n style={{\n position: \"absolute\",\n height: 1,\n width: 1,\n padding: 0,\n margin: -1,\n overflow: \"hidden\",\n clip: \"rect(0, 0, 0, 0)\",\n whiteSpace: \"nowrap\",\n borderWidth: 0,\n ...style,\n }}\n {...rest}\n ref={ref}\n >\n <AnnouncementRegion aria-live=\"polite\">\n {politeAnnouncements.map((announcement) => (\n <div key={`polite-${announcement.id}`}>{announcement.message}</div>\n ))}\n </AnnouncementRegion>\n <AnnouncementRegion aria-live=\"assertive\">\n {assertiveAnnouncements.map((announcement) => (\n <div key={`assertive-${announcement.id}`}>\n {announcement.message}\n </div>\n ))}\n </AnnouncementRegion>\n </div>\n </AriaAnnouncerContext.Provider>\n );\n});\n"],"names":["AnnouncementRegion","AriaAnnouncerProvider"],"mappings":"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"}
1
+ {"version":3,"file":"AriaAnnouncerProvider.js","sources":["../src/aria-announcer/AriaAnnouncerProvider.tsx"],"sourcesContent":["import { useWindow } from \"@salt-ds/window\";\nimport {\n type ComponentPropsWithRef,\n forwardRef,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { useIsomorphicLayoutEffect } from \"../utils/useIsomorphicLayoutEffect\";\nimport {\n type AnnounceFnOptions,\n AriaAnnouncerContext,\n} from \"./AriaAnnouncerContext\";\nimport { registerAnnouncementTarget } from \"./announcementRegistry\";\n\nexport const ANNOUNCEMENT_TIME_IN_DOM = 300; // time between DOM updates\n\nexport interface AriaAnnouncerProviderProps\n extends ComponentPropsWithRef<\"div\"> {\n /**\n * Optional target key used to route announcements to this provider.\n */\n target?: string;\n}\n\nconst AnnouncementRegion = forwardRef<\n HTMLDivElement,\n ComponentPropsWithRef<\"div\">\n>(function AnnouncementRegion(props, ref) {\n return (\n <div\n // Keep the region simple for maximum assistive-tech compatibility.\n // aria-live is applied by the caller (polite/assertive).\n aria-atomic={false}\n ref={ref}\n {...props}\n />\n );\n});\n\ntype AnnouncementMessage = {\n id: string;\n message: string;\n};\n\nexport const AriaAnnouncerProvider = forwardRef<\n HTMLDivElement,\n AriaAnnouncerProviderProps\n>(function AriaAnnouncerProvider({ children, style, target, ...rest }, ref) {\n const targetWindow = useWindow();\n\n const [politeAnnouncements, setPoliteAnnouncements] = useState<\n AnnouncementMessage[]\n >([]);\n const [assertiveAnnouncements, setAssertiveAnnouncements] = useState<\n AnnouncementMessage[]\n >([]);\n\n const idCounterRef = useRef(0);\n const timeoutsRef = useRef<Set<number>>(new Set());\n\n const scheduleRemoval = useCallback(\n (id: string, assertiveness: \"polite\" | \"assertive\", duration: number) => {\n if (!targetWindow) return;\n\n const setter =\n assertiveness === \"polite\"\n ? setPoliteAnnouncements\n : setAssertiveAnnouncements;\n const handle = targetWindow.setTimeout(() => {\n timeoutsRef.current.delete(handle);\n setter((previous) =>\n previous.filter((announcement) => announcement.id !== id),\n );\n }, duration);\n timeoutsRef.current.add(handle);\n },\n [targetWindow],\n );\n\n const makeAnnouncement = useCallback(\n (\n message: string,\n assertiveness: \"polite\" | \"assertive\" = \"polite\",\n duration: number = ANNOUNCEMENT_TIME_IN_DOM,\n ) => {\n idCounterRef.current += 1;\n // Date.now() can collide when multiple announcements are created in the same millisecond\n // (e.g. tests with cy.clock, batching, or multiple announces during one tick).\n // Add a monotonic counter suffix to guarantee uniqueness.\n const id = `announce-${assertiveness}-${Date.now()}-${idCounterRef.current}`;\n if (assertiveness === \"polite\") {\n setPoliteAnnouncements((previous) => {\n return previous.concat({ id, message });\n });\n } else {\n setAssertiveAnnouncements((previous) => {\n return previous.concat({ id, message });\n });\n }\n scheduleRemoval(id, assertiveness, duration);\n },\n [scheduleRemoval],\n );\n\n useEffect(() => {\n const timeouts = timeoutsRef.current;\n return () => {\n for (const handle of timeouts) {\n targetWindow?.clearTimeout(handle);\n }\n timeouts.clear();\n };\n }, [targetWindow]);\n\n const announce = useCallback(\n (\n announcement: string,\n legacyDelayOrOptions: number | AnnounceFnOptions | undefined = {},\n ) => {\n // Legacy delay (number arg) is handled by useAriaAnnouncer; if we also delayed\n // here we'd apply it twice. Keep supporting the signature but ignore the delay.\n const options: AnnounceFnOptions =\n typeof legacyDelayOrOptions === \"object\" && legacyDelayOrOptions\n ? legacyDelayOrOptions\n : {};\n\n makeAnnouncement(\n announcement,\n options.ariaLive,\n options.duration ?? ANNOUNCEMENT_TIME_IN_DOM,\n );\n },\n [makeAnnouncement],\n );\n\n const value = useMemo(() => ({ announce }), [announce]);\n\n useIsomorphicLayoutEffect(() => {\n if (!target) {\n return;\n }\n return registerAnnouncementTarget(target, announce);\n }, [announce, target]);\n\n return (\n <AriaAnnouncerContext.Provider value={value}>\n {children}\n <div\n // hidden styling based on https://tailwindcss.com/docs/screen-readers\n style={{\n position: \"absolute\",\n height: 1,\n width: 1,\n padding: 0,\n margin: -1,\n overflow: \"hidden\",\n clip: \"rect(0, 0, 0, 0)\",\n whiteSpace: \"nowrap\",\n borderWidth: 0,\n ...style,\n }}\n {...rest}\n ref={ref}\n >\n <AnnouncementRegion aria-live=\"polite\">\n {politeAnnouncements.map((announcement) => (\n <div key={`polite-${announcement.id}`}>{announcement.message}</div>\n ))}\n </AnnouncementRegion>\n <AnnouncementRegion aria-live=\"assertive\">\n {assertiveAnnouncements.map((announcement) => (\n <div key={`assertive-${announcement.id}`}>\n {announcement.message}\n </div>\n ))}\n </AnnouncementRegion>\n </div>\n </AriaAnnouncerContext.Provider>\n );\n});\n"],"names":["AnnouncementRegion","AriaAnnouncerProvider"],"mappings":"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"}
@@ -17,12 +17,31 @@ const BorderLayout = forwardRef(
17
17
  children,
18
18
  (child) => isValidElement(child) && child.props.position
19
19
  );
20
- const topSection = borderAreas.includes("north") ? "north ".repeat(numberOfColumns) : "none ".repeat(numberOfColumns);
21
- const leftSection = borderAreas.includes("west") ? "west" : "center";
22
- const rightSection = borderAreas.includes("east") ? "east" : "center";
23
- const midSection = `${leftSection} center ${rightSection}`;
24
- const bottomSection = borderAreas.includes("south") ? "south ".repeat(numberOfColumns) : "none ".repeat(numberOfColumns);
25
- const gridTemplateAreas = `"${topSection}" "${midSection}" "${bottomSection}"`;
20
+ const hasNorth = borderAreas.includes("north");
21
+ const hasWest = borderAreas.includes("west");
22
+ const hasEast = borderAreas.includes("east");
23
+ const hasSouth = borderAreas.includes("south");
24
+ const middleColumns = [
25
+ hasWest ? "west" : void 0,
26
+ "center",
27
+ hasEast ? "east" : void 0
28
+ ].filter(Boolean);
29
+ const gridTemplateColumns = [
30
+ hasWest ? "min-content" : void 0,
31
+ "1fr",
32
+ hasEast ? "min-content" : void 0
33
+ ].filter(Boolean).join(" ");
34
+ const gridTemplateRows = [
35
+ hasNorth ? "min-content" : void 0,
36
+ "1fr",
37
+ hasSouth ? "min-content" : void 0
38
+ ].filter(Boolean).join(" ");
39
+ const gridTemplateSections = [
40
+ hasNorth ? `"${"north ".repeat(middleColumns.length).trimEnd()}"` : void 0,
41
+ `"${middleColumns.join(" ")}"`,
42
+ hasSouth ? `"${"south ".repeat(middleColumns.length).trimEnd()}"` : void 0
43
+ ].filter(Boolean);
44
+ const gridTemplateAreas = gridTemplateSections.join(" ");
26
45
  const hasMainSection = borderAreas.includes("center");
27
46
  useEffect(() => {
28
47
  if (process.env.NODE_ENV !== "production") {
@@ -35,7 +54,9 @@ const BorderLayout = forwardRef(
35
54
  }, [hasMainSection]);
36
55
  const borderLayoutStyles = {
37
56
  ...style,
38
- "--gridLayout-gridTemplate": gridTemplateAreas
57
+ "--gridLayout-gridTemplate": gridTemplateAreas,
58
+ "--gridLayout-areaColumns": gridTemplateColumns,
59
+ "--gridLayout-areaRows": gridTemplateRows
39
60
  };
40
61
  return /* @__PURE__ */ jsx(
41
62
  GridLayout,