@salt-ds/core 1.64.0 → 1.65.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 (88) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/css/salt-core.css +243 -3
  3. package/dist-cjs/avatar/Avatar.css.js +1 -1
  4. package/dist-cjs/avatar/Avatar.js +12 -2
  5. package/dist-cjs/avatar/Avatar.js.map +1 -1
  6. package/dist-cjs/index.js +6 -0
  7. package/dist-cjs/index.js.map +1 -1
  8. package/dist-cjs/pagination/PageButton.css.js +1 -1
  9. package/dist-cjs/salt-provider/ThemeApplicator.js +1 -1
  10. package/dist-cjs/salt-provider/ThemeApplicator.js.map +1 -1
  11. package/dist-cjs/toolbar/Toolbar.css.js +6 -0
  12. package/dist-cjs/toolbar/Toolbar.css.js.map +1 -0
  13. package/dist-cjs/toolbar/Toolbar.js +401 -0
  14. package/dist-cjs/toolbar/Toolbar.js.map +1 -0
  15. package/dist-cjs/toolbar/ToolbarContent.css.js +6 -0
  16. package/dist-cjs/toolbar/ToolbarContent.css.js.map +1 -0
  17. package/dist-cjs/toolbar/ToolbarContent.js +38 -0
  18. package/dist-cjs/toolbar/ToolbarContent.js.map +1 -0
  19. package/dist-cjs/toolbar/ToolbarOverflow.css.js +6 -0
  20. package/dist-cjs/toolbar/ToolbarOverflow.css.js.map +1 -0
  21. package/dist-cjs/toolbar/ToolbarOverflow.js +712 -0
  22. package/dist-cjs/toolbar/ToolbarOverflow.js.map +1 -0
  23. package/dist-cjs/toolbar/ToolbarOverflowFloatingBoundary.js +160 -0
  24. package/dist-cjs/toolbar/ToolbarOverflowFloatingBoundary.js.map +1 -0
  25. package/dist-cjs/toolbar/Tooltray.css.js +6 -0
  26. package/dist-cjs/toolbar/Tooltray.css.js.map +1 -0
  27. package/dist-cjs/toolbar/Tooltray.js +59 -0
  28. package/dist-cjs/toolbar/Tooltray.js.map +1 -0
  29. package/dist-cjs/toolbar/toolbarKeyboardUtils.js +400 -0
  30. package/dist-cjs/toolbar/toolbarKeyboardUtils.js.map +1 -0
  31. package/dist-cjs/toolbar/toolbarUtils.js +220 -0
  32. package/dist-cjs/toolbar/toolbarUtils.js.map +1 -0
  33. package/dist-cjs/toolbar/useToolbarKeyboardNavigation.js +309 -0
  34. package/dist-cjs/toolbar/useToolbarKeyboardNavigation.js.map +1 -0
  35. package/dist-cjs/toolbar/useToolbarOverflow.js +750 -0
  36. package/dist-cjs/toolbar/useToolbarOverflow.js.map +1 -0
  37. package/dist-cjs/utils/mergeProps.js +2 -0
  38. package/dist-cjs/utils/mergeProps.js.map +1 -1
  39. package/dist-es/avatar/Avatar.css.js +1 -1
  40. package/dist-es/avatar/Avatar.js +12 -2
  41. package/dist-es/avatar/Avatar.js.map +1 -1
  42. package/dist-es/index.js +3 -0
  43. package/dist-es/index.js.map +1 -1
  44. package/dist-es/pagination/PageButton.css.js +1 -1
  45. package/dist-es/salt-provider/ThemeApplicator.js +1 -1
  46. package/dist-es/salt-provider/ThemeApplicator.js.map +1 -1
  47. package/dist-es/toolbar/Toolbar.css.js +4 -0
  48. package/dist-es/toolbar/Toolbar.css.js.map +1 -0
  49. package/dist-es/toolbar/Toolbar.js +399 -0
  50. package/dist-es/toolbar/Toolbar.js.map +1 -0
  51. package/dist-es/toolbar/ToolbarContent.css.js +4 -0
  52. package/dist-es/toolbar/ToolbarContent.css.js.map +1 -0
  53. package/dist-es/toolbar/ToolbarContent.js +36 -0
  54. package/dist-es/toolbar/ToolbarContent.js.map +1 -0
  55. package/dist-es/toolbar/ToolbarOverflow.css.js +4 -0
  56. package/dist-es/toolbar/ToolbarOverflow.css.js.map +1 -0
  57. package/dist-es/toolbar/ToolbarOverflow.js +707 -0
  58. package/dist-es/toolbar/ToolbarOverflow.js.map +1 -0
  59. package/dist-es/toolbar/ToolbarOverflowFloatingBoundary.js +154 -0
  60. package/dist-es/toolbar/ToolbarOverflowFloatingBoundary.js.map +1 -0
  61. package/dist-es/toolbar/Tooltray.css.js +4 -0
  62. package/dist-es/toolbar/Tooltray.css.js.map +1 -0
  63. package/dist-es/toolbar/Tooltray.js +57 -0
  64. package/dist-es/toolbar/Tooltray.js.map +1 -0
  65. package/dist-es/toolbar/toolbarKeyboardUtils.js +381 -0
  66. package/dist-es/toolbar/toolbarKeyboardUtils.js.map +1 -0
  67. package/dist-es/toolbar/toolbarUtils.js +216 -0
  68. package/dist-es/toolbar/toolbarUtils.js.map +1 -0
  69. package/dist-es/toolbar/useToolbarKeyboardNavigation.js +307 -0
  70. package/dist-es/toolbar/useToolbarKeyboardNavigation.js.map +1 -0
  71. package/dist-es/toolbar/useToolbarOverflow.js +748 -0
  72. package/dist-es/toolbar/useToolbarOverflow.js.map +1 -0
  73. package/dist-es/utils/mergeProps.js +2 -0
  74. package/dist-es/utils/mergeProps.js.map +1 -1
  75. package/dist-types/avatar/Avatar.d.ts +5 -0
  76. package/dist-types/index.d.ts +1 -0
  77. package/dist-types/toolbar/Toolbar.d.ts +12 -0
  78. package/dist-types/toolbar/ToolbarContent.d.ts +10 -0
  79. package/dist-types/toolbar/ToolbarOverflow.d.ts +34 -0
  80. package/dist-types/toolbar/ToolbarOverflowFloatingBoundary.d.ts +16 -0
  81. package/dist-types/toolbar/Tooltray.d.ts +36 -0
  82. package/dist-types/toolbar/index.d.ts +3 -0
  83. package/dist-types/toolbar/toolbarKeyboardUtils.d.ts +39 -0
  84. package/dist-types/toolbar/toolbarUtils.d.ts +44 -0
  85. package/dist-types/toolbar/useToolbarKeyboardNavigation.d.ts +42 -0
  86. package/dist-types/toolbar/useToolbarOverflow.d.ts +38 -0
  87. package/dist-types/utils/mergeProps.d.ts +4 -0
  88. package/package.json +1 -1
@@ -0,0 +1,712 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var react = require('@floating-ui/react');
5
+ var styles = require('@salt-ds/styles');
6
+ var window = require('@salt-ds/window');
7
+ var clsx = require('clsx');
8
+ var computeScrollIntoView = require('compute-scroll-into-view');
9
+ var React = require('react');
10
+ var reactDom = require('react-dom');
11
+ var Button = require('../button/Button.js');
12
+ var SemanticIconProvider = require('../semantic-icon-provider/SemanticIconProvider.js');
13
+ var makePrefixer = require('../utils/makePrefixer.js');
14
+ var useIsomorphicLayoutEffect = require('../utils/useIsomorphicLayoutEffect.js');
15
+ var useFloatingUI = require('../utils/useFloatingUI/useFloatingUI.js');
16
+ var useForkRef = require('../utils/useForkRef.js');
17
+ var useId = require('../utils/useId.js');
18
+ require('../salt-provider/SaltProvider.js');
19
+ require('../viewport/ViewportProvider.js');
20
+ var ToolbarContent = require('./ToolbarContent.js');
21
+ var ToolbarOverflow = require('./ToolbarOverflow.css.js');
22
+ var ToolbarOverflowFloatingBoundary = require('./ToolbarOverflowFloatingBoundary.js');
23
+ var toolbarKeyboardUtils = require('./toolbarKeyboardUtils.js');
24
+ var toolbarUtils = require('./toolbarUtils.js');
25
+ var useToolbarKeyboardNavigation = require('./useToolbarKeyboardNavigation.js');
26
+
27
+ const withBaseName = makePrefixer.makePrefixer("saltToolbarOverflow");
28
+ const toolbarStatefulFocusRootSelector = [
29
+ ".saltComboBox-focused",
30
+ ".saltDateInput-focused",
31
+ ".saltInput-focused"
32
+ ].join(", ");
33
+ const toolbarOverflowFocusScrollRootSelector = [
34
+ ".saltComboBox",
35
+ ".saltDateInput",
36
+ ".saltDropdown",
37
+ ".saltInput",
38
+ ".saltSwitch"
39
+ ].join(", ");
40
+ function createToolbarFocusEvent(eventName, target, relatedTarget) {
41
+ var _a;
42
+ const FocusEventCtor = (_a = target.ownerDocument.defaultView) == null ? void 0 : _a.FocusEvent;
43
+ const eventInit = {
44
+ bubbles: eventName === "focusout",
45
+ relatedTarget
46
+ };
47
+ return FocusEventCtor ? new FocusEventCtor(eventName, eventInit) : new Event(eventName, eventInit);
48
+ }
49
+ function notifyToolbarReparentedFocusLoss(mountNode) {
50
+ const activeElement = mountNode.ownerDocument.activeElement;
51
+ if (mountNode.contains(activeElement)) {
52
+ return;
53
+ }
54
+ const staleFocusTargets = Array.from(
55
+ mountNode.querySelectorAll(toolbarStatefulFocusRootSelector)
56
+ ).map(
57
+ (root) => root.querySelector("input") ?? root.querySelector(toolbarKeyboardUtils.toolbarFocusableSelector)
58
+ ).filter((target) => target != null);
59
+ for (const target of staleFocusTargets) {
60
+ const relatedTarget = activeElement instanceof Element ? activeElement : null;
61
+ target.dispatchEvent(
62
+ createToolbarFocusEvent("blur", target, relatedTarget)
63
+ );
64
+ target.dispatchEvent(
65
+ createToolbarFocusEvent("focusout", target, relatedTarget)
66
+ );
67
+ }
68
+ }
69
+ function getToolbarOverflowFocusScrollTarget(target) {
70
+ const itemRoot = target.closest(`[${toolbarKeyboardUtils.TOOLBAR_ITEM_ATTR}]`);
71
+ const controlRoot = target.closest(
72
+ toolbarOverflowFocusScrollRootSelector
73
+ );
74
+ return controlRoot && (itemRoot == null ? void 0 : itemRoot.contains(controlRoot)) ? controlRoot : target;
75
+ }
76
+ function getToolbarOverflowPanelInlinePadding(target) {
77
+ var _a;
78
+ const panelContent = target.closest(
79
+ `[${toolbarKeyboardUtils.TOOLBAR_SCOPE_ROOT_ATTR}]`
80
+ );
81
+ const styles = (_a = panelContent == null ? void 0 : panelContent.ownerDocument.defaultView) == null ? void 0 : _a.getComputedStyle(panelContent);
82
+ return {
83
+ left: Number.parseFloat((styles == null ? void 0 : styles.paddingLeft) ?? "0") || 0,
84
+ right: Number.parseFloat((styles == null ? void 0 : styles.paddingRight) ?? "0") || 0
85
+ };
86
+ }
87
+ function scrollToolbarOverflowTargetIntoView(panel, target) {
88
+ if (!panel || !panel.contains(target)) {
89
+ return;
90
+ }
91
+ const scrollTarget = getToolbarOverflowFocusScrollTarget(target);
92
+ const actions = computeScrollIntoView.compute(scrollTarget, {
93
+ block: "nearest",
94
+ boundary: panel,
95
+ inline: "nearest",
96
+ scrollMode: "if-needed"
97
+ });
98
+ for (const { el, left, top } of actions) {
99
+ if (el === panel) {
100
+ const targetRect = scrollTarget.getBoundingClientRect();
101
+ const panelRect = panel.getBoundingClientRect();
102
+ const viewportLeft = panelRect.left + panel.clientLeft;
103
+ const viewportRight = viewportLeft + panel.clientWidth;
104
+ const padding = getToolbarOverflowPanelInlinePadding(scrollTarget);
105
+ const nextLeft = targetRect.left < viewportLeft + padding.left ? left - padding.left : targetRect.right > viewportRight - padding.right ? left + padding.right : left;
106
+ el.scrollLeft = Math.max(0, nextLeft);
107
+ } else {
108
+ el.scrollLeft = left;
109
+ }
110
+ el.scrollTop = top;
111
+ }
112
+ }
113
+ function canSeedOverflowFocusMemory(focusMemory, group) {
114
+ return (focusMemory == null ? void 0 : focusMemory.type) === "item" && group.items.some((item) => item.id === focusMemory.itemId);
115
+ }
116
+ function ToolbarOverflowItemOwner({
117
+ host,
118
+ item
119
+ }) {
120
+ const targetWindow = window.useWindow();
121
+ const [mountNode, setMountNode] = React.useState(null);
122
+ const mainToolbarTabIndexMemoryRef = React.useRef(
123
+ /* @__PURE__ */ new WeakMap()
124
+ );
125
+ const lastOverflowBoundaryKeyRef = React.useRef(null);
126
+ const currentOverflowBoundaryKey = ToolbarOverflowFloatingBoundary.getToolbarOverflowBoundaryKey(host);
127
+ if (host) {
128
+ lastOverflowBoundaryKeyRef.current = currentOverflowBoundaryKey;
129
+ }
130
+ const boundaryKey = host ? currentOverflowBoundaryKey : lastOverflowBoundaryKeyRef.current;
131
+ useIsomorphicLayoutEffect.useIsomorphicLayoutEffect(() => {
132
+ const nextMountNode = targetWindow == null ? void 0 : targetWindow.document.createElement("div");
133
+ if (!nextMountNode) {
134
+ return;
135
+ }
136
+ nextMountNode.className = withBaseName("contentHost");
137
+ setMountNode(nextMountNode);
138
+ return () => {
139
+ var _a;
140
+ (_a = nextMountNode.parentElement) == null ? void 0 : _a.removeChild(nextMountNode);
141
+ };
142
+ }, [targetWindow]);
143
+ useIsomorphicLayoutEffect.useIsomorphicLayoutEffect(() => {
144
+ var _a, _b;
145
+ if (!mountNode) {
146
+ return;
147
+ }
148
+ if (host) {
149
+ if (mountNode.parentElement !== host) {
150
+ host.appendChild(mountNode);
151
+ notifyToolbarReparentedFocusLoss(mountNode);
152
+ }
153
+ const isMainToolbarHost = ((_a = host.closest(`[${toolbarKeyboardUtils.TOOLBAR_SCOPE_ROOT_ATTR}]`)) == null ? void 0 : _a.getAttribute(toolbarKeyboardUtils.TOOLBAR_SCOPE_ROOT_ATTR)) === "main";
154
+ if (isMainToolbarHost) {
155
+ const focusableElements = Array.from(
156
+ mountNode.querySelectorAll(toolbarKeyboardUtils.toolbarFocusableSelector)
157
+ );
158
+ for (const element of focusableElements) {
159
+ const rememberedTabIndex = mainToolbarTabIndexMemoryRef.current.get(element);
160
+ if (rememberedTabIndex !== void 0) {
161
+ if (rememberedTabIndex == null) {
162
+ element.removeAttribute("tabindex");
163
+ } else {
164
+ element.setAttribute("tabindex", rememberedTabIndex);
165
+ }
166
+ }
167
+ }
168
+ for (const element of focusableElements) {
169
+ mainToolbarTabIndexMemoryRef.current.set(
170
+ element,
171
+ element.getAttribute("tabindex")
172
+ );
173
+ }
174
+ }
175
+ return;
176
+ }
177
+ notifyToolbarReparentedFocusLoss(mountNode);
178
+ (_b = mountNode.parentElement) == null ? void 0 : _b.removeChild(mountNode);
179
+ }, [host, mountNode]);
180
+ if (!mountNode) {
181
+ return null;
182
+ }
183
+ const clonedItem = React.cloneElement(
184
+ item.element,
185
+ {
186
+ [toolbarKeyboardUtils.TOOLBAR_GROUP_KEY_ATTR]: item.overflowGroupKey,
187
+ [toolbarKeyboardUtils.TOOLBAR_ITEM_ATTR]: item.id
188
+ }
189
+ );
190
+ const itemContent = /* @__PURE__ */ jsxRuntime.jsx(ToolbarOverflowFloatingBoundary.ToolbarOverflowFloatingComponentProvider, { boundaryKey, children: clonedItem });
191
+ return reactDom.createPortal(itemContent, mountNode);
192
+ }
193
+ function ToolbarOverflowOwners({
194
+ hostNodes,
195
+ items
196
+ }) {
197
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
198
+ ToolbarOverflowItemOwner,
199
+ {
200
+ host: hostNodes[item.id] ?? null,
201
+ item
202
+ },
203
+ item.id
204
+ )) });
205
+ }
206
+ function cloneDecorations(itemId, decorations, slot) {
207
+ return React.Children.toArray(decorations).flatMap((decoration) => {
208
+ if (!React.isValidElement(decoration)) {
209
+ return [];
210
+ }
211
+ return [
212
+ React.cloneElement(decoration, {
213
+ key: `${itemId}-${slot}-${String(
214
+ decoration.key ?? (typeof decoration.type === "string" ? decoration.type : "decoration")
215
+ )}`
216
+ })
217
+ ];
218
+ });
219
+ }
220
+ function ToolbarOverflowTriggerContent({
221
+ label,
222
+ named
223
+ }) {
224
+ const { OverflowIcon } = SemanticIconProvider.useIcon();
225
+ if (named) {
226
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: label });
227
+ }
228
+ return /* @__PURE__ */ jsxRuntime.jsx(OverflowIcon, { "aria-hidden": true });
229
+ }
230
+ function getOverflowTriggerLabel(group) {
231
+ return group.named ? `${group.label} overflow. Hidden controls.` : "Overflow. Hidden controls.";
232
+ }
233
+ function ToolbarOverflowMenu({
234
+ focusMemoryRef,
235
+ getItemHostRef,
236
+ group,
237
+ onItemFocus
238
+ }) {
239
+ var _a, _b;
240
+ const panelId = useId.useId();
241
+ const [open, setOpen] = React.useState(false);
242
+ const triggerRef = React.useRef(null);
243
+ const panelRef = React.useRef(null);
244
+ const panelContentRef = React.useRef(null);
245
+ const [panelContentNode, setPanelContentNode] = React.useState(null);
246
+ const wasOpenRef = React.useRef(false);
247
+ const focusedOpenPanelRef = React.useRef(false);
248
+ const openModalityRef = React.useRef(null);
249
+ const floatingBoundary = ToolbarOverflowFloatingBoundary.useToolbarOverflowFloatingBoundary();
250
+ const {
251
+ focusEntryTarget,
252
+ handleScopeBlur,
253
+ handleScopeFocus,
254
+ handleScopeKeyDown,
255
+ handleScopePointerDown,
256
+ rememberedFocusRef
257
+ } = useToolbarKeyboardNavigation.useToolbarKeyboardNavigation({
258
+ includeTabIndexMinusOne: true,
259
+ items: group.items,
260
+ scopeRef: panelContentRef
261
+ });
262
+ const { refs, x, y, strategy, context, elements } = useFloatingUI.useFloatingUI({
263
+ open,
264
+ onOpenChange(nextOpen, _, reason) {
265
+ setOpen(nextOpen);
266
+ if (!nextOpen) {
267
+ openModalityRef.current = null;
268
+ }
269
+ if (!nextOpen && reason === "escape-key") {
270
+ toolbarKeyboardUtils.scheduleToolbarFocus(triggerRef.current);
271
+ }
272
+ },
273
+ placement: "bottom-end",
274
+ middleware: [
275
+ react.offset(1),
276
+ react.size({
277
+ apply({ elements: elements2, availableHeight }) {
278
+ Object.assign(elements2.floating.style, {
279
+ maxHeight: `${availableHeight}px`
280
+ });
281
+ }
282
+ }),
283
+ react.flip(),
284
+ react.shift({
285
+ padding: 8
286
+ })
287
+ ]
288
+ });
289
+ const { getFloatingProps, getReferenceProps } = react.useInteractions([
290
+ react.useClick(context),
291
+ react.useDismiss(context, {
292
+ escapeKey: false,
293
+ outsidePress(event) {
294
+ return !ToolbarOverflowFloatingBoundary.isTargetInsideOverflowBoundary(
295
+ panelContentRef.current,
296
+ floatingBoundary,
297
+ group.key,
298
+ event.target
299
+ );
300
+ }
301
+ })
302
+ ]);
303
+ const handleTriggerKeyDown = React.useCallback(
304
+ (event) => {
305
+ if (!open && ["Enter", " "].includes(event.key)) {
306
+ event.preventDefault();
307
+ openModalityRef.current = "keyboard";
308
+ setOpen(true);
309
+ return;
310
+ }
311
+ if (open && event.key === "Escape") {
312
+ event.preventDefault();
313
+ setOpen(false);
314
+ toolbarKeyboardUtils.scheduleToolbarFocus(triggerRef.current);
315
+ }
316
+ },
317
+ [open]
318
+ );
319
+ const handleTriggerPointerDown = React.useCallback(
320
+ (event) => {
321
+ if (!open && event.button === 0) {
322
+ openModalityRef.current = "pointer";
323
+ }
324
+ },
325
+ [open]
326
+ );
327
+ const handleTriggerMouseDown = React.useCallback(
328
+ (event) => {
329
+ if (!open && event.button === 0) {
330
+ openModalityRef.current = "pointer";
331
+ }
332
+ },
333
+ [open]
334
+ );
335
+ const handlePanelKeyDown = React.useCallback((event) => {
336
+ var _a2;
337
+ if (event.key === "Tab") {
338
+ const panelContent = panelContentRef.current;
339
+ const target = event.target;
340
+ if (panelContent && target instanceof HTMLElement && toolbarKeyboardUtils.shouldToolbarPreserveNativeTab(target)) {
341
+ const focusableElements = toolbarKeyboardUtils.getDocumentFocusableElements(
342
+ panelContent.ownerDocument
343
+ );
344
+ const currentIndex = focusableElements.indexOf(target);
345
+ const nextFocusTarget = focusableElements[currentIndex + (event.shiftKey ? -1 : 1)];
346
+ if (currentIndex !== -1 && nextFocusTarget && panelContent.contains(nextFocusTarget)) {
347
+ return;
348
+ }
349
+ }
350
+ }
351
+ if (event.key === "Tab" && !event.shiftKey) {
352
+ const toolbarRoot = (_a2 = triggerRef.current) == null ? void 0 : _a2.closest(
353
+ `[${toolbarKeyboardUtils.TOOLBAR_SCOPE_ROOT_ATTR}]`
354
+ );
355
+ const nextFocusTarget = toolbarRoot ? toolbarKeyboardUtils.getToolbarTabMoveTarget(toolbarRoot, false) : null;
356
+ if (nextFocusTarget) {
357
+ event.preventDefault();
358
+ event.stopPropagation();
359
+ setOpen(false);
360
+ toolbarKeyboardUtils.scheduleToolbarFocus(nextFocusTarget);
361
+ }
362
+ return;
363
+ }
364
+ if (event.key === "Tab" && event.shiftKey) {
365
+ event.preventDefault();
366
+ event.stopPropagation();
367
+ setOpen(false);
368
+ toolbarKeyboardUtils.scheduleToolbarFocus(triggerRef.current);
369
+ return;
370
+ }
371
+ if (event.key === "Escape") {
372
+ event.preventDefault();
373
+ event.stopPropagation();
374
+ setOpen(false);
375
+ toolbarKeyboardUtils.scheduleToolbarFocus(triggerRef.current);
376
+ }
377
+ }, []);
378
+ const handleTriggerRef = useForkRef.useForkRef(
379
+ triggerRef,
380
+ refs.setReference
381
+ );
382
+ const handlePanelRef = useForkRef.useForkRef(panelRef, refs.setFloating);
383
+ const handlePanelContentRef = useForkRef.useForkRef(
384
+ panelContentRef,
385
+ setPanelContentNode
386
+ );
387
+ const handlePanelFocus = React.useCallback(
388
+ (event) => {
389
+ handleScopeFocus(event);
390
+ const panelContent = panelContentRef.current;
391
+ const target = event.target;
392
+ if (!panelContent || !(target instanceof HTMLElement)) {
393
+ return;
394
+ }
395
+ const focusMemory = toolbarKeyboardUtils.getToolbarFocusMemory(panelContent, target, {
396
+ includeTabIndexMinusOne: true
397
+ });
398
+ if ((focusMemory == null ? void 0 : focusMemory.type) !== "item") {
399
+ return;
400
+ }
401
+ scrollToolbarOverflowTargetIntoView(panelRef.current, target);
402
+ onItemFocus == null ? void 0 : onItemFocus(focusMemory.itemId, focusMemory.controlIndex);
403
+ },
404
+ [handleScopeFocus, onItemFocus]
405
+ );
406
+ const { Component: FloatingComponent } = useFloatingUI.useFloatingComponent();
407
+ React.useEffect(() => {
408
+ if (open && !wasOpenRef.current) {
409
+ const externalFocusMemory = focusMemoryRef == null ? void 0 : focusMemoryRef.current;
410
+ if (canSeedOverflowFocusMemory(externalFocusMemory, group)) {
411
+ rememberedFocusRef.current = externalFocusMemory;
412
+ }
413
+ if (openModalityRef.current !== "pointer") {
414
+ focusEntryTarget();
415
+ }
416
+ }
417
+ wasOpenRef.current = open;
418
+ }, [focusEntryTarget, focusMemoryRef, group, open, rememberedFocusRef]);
419
+ React.useEffect(() => {
420
+ if (!open || !panelContentNode) {
421
+ return;
422
+ }
423
+ const handleFocusIn = (event) => {
424
+ handlePanelFocus(event);
425
+ };
426
+ const handleFocusOut = (event) => {
427
+ if (ToolbarOverflowFloatingBoundary.isTargetInsideOverflowBoundary(
428
+ panelContentRef.current,
429
+ floatingBoundary,
430
+ group.key,
431
+ event.relatedTarget
432
+ )) {
433
+ return;
434
+ }
435
+ handleScopeBlur(event);
436
+ };
437
+ const handleKeyDownCapture = (event) => {
438
+ handleScopeKeyDown(event);
439
+ };
440
+ const handlePointerDownCapture = (event) => {
441
+ handleScopePointerDown(event);
442
+ };
443
+ const handleKeyDown = (event) => {
444
+ if (!event.defaultPrevented) {
445
+ handlePanelKeyDown(event);
446
+ }
447
+ };
448
+ panelContentNode.addEventListener("focusin", handleFocusIn);
449
+ panelContentNode.addEventListener("focusout", handleFocusOut);
450
+ panelContentNode.addEventListener("keydown", handleKeyDownCapture, true);
451
+ panelContentNode.addEventListener(
452
+ "pointerdown",
453
+ handlePointerDownCapture,
454
+ true
455
+ );
456
+ panelContentNode.addEventListener("keydown", handleKeyDown);
457
+ return () => {
458
+ panelContentNode.removeEventListener("focusin", handleFocusIn);
459
+ panelContentNode.removeEventListener("focusout", handleFocusOut);
460
+ panelContentNode.removeEventListener(
461
+ "keydown",
462
+ handleKeyDownCapture,
463
+ true
464
+ );
465
+ panelContentNode.removeEventListener(
466
+ "pointerdown",
467
+ handlePointerDownCapture,
468
+ true
469
+ );
470
+ panelContentNode.removeEventListener("keydown", handleKeyDown);
471
+ };
472
+ }, [
473
+ handlePanelFocus,
474
+ handlePanelKeyDown,
475
+ handleScopeBlur,
476
+ handleScopeKeyDown,
477
+ handleScopePointerDown,
478
+ floatingBoundary,
479
+ group.key,
480
+ open,
481
+ panelContentNode
482
+ ]);
483
+ useIsomorphicLayoutEffect.useIsomorphicLayoutEffect(() => {
484
+ if (!open) {
485
+ focusedOpenPanelRef.current = false;
486
+ return;
487
+ }
488
+ if (!panelContentNode || focusedOpenPanelRef.current || openModalityRef.current === "pointer") {
489
+ return;
490
+ }
491
+ const getPanelFocusables = () => toolbarKeyboardUtils.getToolbarScopeFocusableElements(panelContentNode, {
492
+ includeTabIndexMinusOne: true
493
+ });
494
+ const focusEntryWhenReady = () => {
495
+ const panelFocusables = getPanelFocusables();
496
+ if (panelFocusables.length === 0) {
497
+ return false;
498
+ }
499
+ focusedOpenPanelRef.current = true;
500
+ focusEntryTarget();
501
+ return true;
502
+ };
503
+ if (focusEntryWhenReady()) {
504
+ return;
505
+ }
506
+ const panelWindow = panelContentNode.ownerDocument.defaultView;
507
+ const mutationObserverCtor = panelWindow == null ? void 0 : panelWindow.MutationObserver;
508
+ const resizeObserverCtor = panelWindow == null ? void 0 : panelWindow.ResizeObserver;
509
+ let mutationObserver = null;
510
+ let resizeObserver = null;
511
+ mutationObserver = mutationObserverCtor != null ? new mutationObserverCtor(() => {
512
+ if (focusEntryWhenReady()) {
513
+ mutationObserver == null ? void 0 : mutationObserver.disconnect();
514
+ resizeObserver == null ? void 0 : resizeObserver.disconnect();
515
+ }
516
+ }) : null;
517
+ resizeObserver = resizeObserverCtor != null ? new resizeObserverCtor(() => {
518
+ if (focusEntryWhenReady()) {
519
+ mutationObserver == null ? void 0 : mutationObserver.disconnect();
520
+ resizeObserver == null ? void 0 : resizeObserver.disconnect();
521
+ }
522
+ }) : null;
523
+ mutationObserver == null ? void 0 : mutationObserver.observe(panelContentNode, {
524
+ childList: true,
525
+ subtree: true
526
+ });
527
+ resizeObserver == null ? void 0 : resizeObserver.observe(panelContentNode);
528
+ return () => {
529
+ mutationObserver == null ? void 0 : mutationObserver.disconnect();
530
+ resizeObserver == null ? void 0 : resizeObserver.disconnect();
531
+ };
532
+ }, [focusEntryTarget, open, panelContentNode]);
533
+ return /* @__PURE__ */ jsxRuntime.jsxs(
534
+ "div",
535
+ {
536
+ className: withBaseName("menu"),
537
+ "data-overflowgroup": group.overflowGroup,
538
+ children: [
539
+ /* @__PURE__ */ jsxRuntime.jsx(
540
+ Button.Button,
541
+ {
542
+ appearance: "transparent",
543
+ "aria-controls": panelId,
544
+ "aria-expanded": open,
545
+ "aria-label": getOverflowTriggerLabel(group),
546
+ className: withBaseName("trigger"),
547
+ ...{
548
+ [toolbarKeyboardUtils.TOOLBAR_GROUP_KEY_ATTR]: group.key,
549
+ [toolbarKeyboardUtils.TOOLBAR_OVERFLOW_TRIGGER_ATTR]: ""
550
+ },
551
+ ...getReferenceProps({
552
+ onKeyDown: handleTriggerKeyDown,
553
+ onMouseDown: handleTriggerMouseDown,
554
+ onPointerDown: handleTriggerPointerDown
555
+ }),
556
+ ref: handleTriggerRef,
557
+ sentiment: "neutral",
558
+ children: /* @__PURE__ */ jsxRuntime.jsx(
559
+ ToolbarOverflowTriggerContent,
560
+ {
561
+ label: group.label,
562
+ named: group.named
563
+ }
564
+ )
565
+ }
566
+ ),
567
+ /* @__PURE__ */ jsxRuntime.jsx(
568
+ FloatingComponent,
569
+ {
570
+ ...getFloatingProps({
571
+ role: "presentation"
572
+ }),
573
+ className: withBaseName("panel"),
574
+ focusManagerProps: context ? {
575
+ context,
576
+ initialFocus: -1,
577
+ returnFocus: false,
578
+ modal: false,
579
+ closeOnFocusOut: false
580
+ } : void 0,
581
+ id: panelId,
582
+ left: x ?? 0,
583
+ open,
584
+ position: strategy,
585
+ ref: handlePanelRef,
586
+ top: y ?? 0,
587
+ width: (_a = elements.floating) == null ? void 0 : _a.offsetWidth,
588
+ height: (_b = elements.floating) == null ? void 0 : _b.offsetHeight,
589
+ children: /* @__PURE__ */ jsxRuntime.jsx(
590
+ "div",
591
+ {
592
+ "aria-label": `${group.label} overflow`,
593
+ "aria-orientation": "horizontal",
594
+ className: withBaseName("panelContent"),
595
+ ...{ [toolbarKeyboardUtils.TOOLBAR_SCOPE_ROOT_ATTR]: group.key },
596
+ role: "toolbar",
597
+ ref: handlePanelContentRef,
598
+ children: group.items.map((item, index) => /* @__PURE__ */ jsxRuntime.jsxs(
599
+ "div",
600
+ {
601
+ className: withBaseName("panelItem"),
602
+ children: [
603
+ index > 0 && item.leadingDecorations.length > 0 && cloneDecorations(item.id, item.leadingDecorations, "leading"),
604
+ /* @__PURE__ */ jsxRuntime.jsx(
605
+ "div",
606
+ {
607
+ className: withBaseName("itemHost"),
608
+ ref: open ? getItemHostRef(item.id, "overflow") : null
609
+ }
610
+ ),
611
+ cloneDecorations(item.id, item.trailingDecorations, "trailing")
612
+ ]
613
+ },
614
+ `${group.id}-${item.id}`
615
+ ))
616
+ }
617
+ )
618
+ }
619
+ )
620
+ ]
621
+ }
622
+ );
623
+ }
624
+ function ToolbarOverflowContent({
625
+ content,
626
+ focusMemoryRef,
627
+ getItemHostRef,
628
+ getItemRef,
629
+ getNamedTriggerRef,
630
+ getContentRef,
631
+ onItemFocus,
632
+ overflowGroups,
633
+ overflowedIds
634
+ }) {
635
+ const targetWindow = window.useWindow();
636
+ styles.useComponentCssInjection({
637
+ testId: "salt-toolbar-overflow",
638
+ css: ToolbarOverflow,
639
+ window: targetWindow
640
+ });
641
+ const { className, ...contentProps } = content.props;
642
+ const handleContentRef = useForkRef.useForkRef(content.ref, getContentRef(content.key));
643
+ const overflowGroupByKey = new Map(
644
+ overflowGroups.map((group) => [group.key, group])
645
+ );
646
+ const renderSlots = toolbarUtils.buildContentOverflowRenderSlots(
647
+ content.items,
648
+ overflowedIds,
649
+ new Set(overflowGroups.map((group) => group.key))
650
+ );
651
+ return /* @__PURE__ */ jsxRuntime.jsx(
652
+ ToolbarContent.ToolbarContent,
653
+ {
654
+ ...contentProps,
655
+ "data-implicit": content.implicit || void 0,
656
+ className: clsx.clsx(className, withBaseName("content")),
657
+ position: content.position,
658
+ ref: handleContentRef,
659
+ children: renderSlots.map(
660
+ ({
661
+ item,
662
+ overflowed,
663
+ showLeadingDecorations,
664
+ showTrailingDecorations,
665
+ triggerGroupKey
666
+ }) => {
667
+ const triggerGroup = triggerGroupKey != null ? overflowGroupByKey.get(triggerGroupKey) : void 0;
668
+ return /* @__PURE__ */ jsxRuntime.jsxs(
669
+ "div",
670
+ {
671
+ className: withBaseName("slot"),
672
+ "data-align": item.align,
673
+ "data-priority": item.overflowPriority,
674
+ ref: triggerGroup ? getNamedTriggerRef(item.id) : getItemRef(item.id),
675
+ children: [
676
+ showLeadingDecorations ? cloneDecorations(item.id, item.leadingDecorations, "leading") : null,
677
+ !overflowed ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: withBaseName("item"), children: /* @__PURE__ */ jsxRuntime.jsx(
678
+ "div",
679
+ {
680
+ className: withBaseName("itemHost"),
681
+ ref: getItemHostRef(item.id, "main")
682
+ }
683
+ ) }) : null,
684
+ triggerGroup ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: withBaseName("item"), children: /* @__PURE__ */ jsxRuntime.jsx(
685
+ ToolbarOverflowMenu,
686
+ {
687
+ focusMemoryRef,
688
+ getItemHostRef,
689
+ group: triggerGroup,
690
+ onItemFocus
691
+ }
692
+ ) }) : null,
693
+ showTrailingDecorations ? cloneDecorations(
694
+ item.id,
695
+ item.trailingDecorations,
696
+ "trailing"
697
+ ) : null
698
+ ]
699
+ },
700
+ triggerGroup ? `${content.key}-${triggerGroup.id}-anchor-${item.id}` : item.id
701
+ );
702
+ }
703
+ )
704
+ }
705
+ );
706
+ }
707
+
708
+ exports.ToolbarOverflowContent = ToolbarOverflowContent;
709
+ exports.ToolbarOverflowMenu = ToolbarOverflowMenu;
710
+ exports.ToolbarOverflowOwners = ToolbarOverflowOwners;
711
+ exports.ToolbarOverflowTriggerContent = ToolbarOverflowTriggerContent;
712
+ //# sourceMappingURL=ToolbarOverflow.js.map