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