@salt-ds/lab 1.0.0-alpha.87 → 1.0.0-alpha.89
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.
- package/CHANGELOG.md +44 -0
- package/css/salt-lab.css +64 -45
- package/dist-cjs/calendar/internal/CalendarDay.css.js +1 -1
- package/dist-cjs/contact-details/ContactDetails.css.js +1 -1
- package/dist-cjs/list-deprecated/ListStateContext.js +1 -1
- package/dist-cjs/list-deprecated/ListStateContext.js.map +1 -1
- package/dist-cjs/tabs-next/TabBar.css.js +1 -1
- package/dist-cjs/tabs-next/TabBar.js +1 -1
- package/dist-cjs/tabs-next/TabBar.js.map +1 -1
- package/dist-cjs/tabs-next/TabListLayoutContext.js +13 -0
- package/dist-cjs/tabs-next/TabListLayoutContext.js.map +1 -0
- package/dist-cjs/tabs-next/TabListNext.css.js +1 -1
- package/dist-cjs/tabs-next/TabListNext.js +179 -33
- package/dist-cjs/tabs-next/TabListNext.js.map +1 -1
- package/dist-cjs/tabs-next/TabNext.js +111 -7
- package/dist-cjs/tabs-next/TabNext.js.map +1 -1
- package/dist-cjs/tabs-next/TabNextAction.js +25 -2
- package/dist-cjs/tabs-next/TabNextAction.js.map +1 -1
- package/dist-cjs/tabs-next/TabNextPanel.js +31 -16
- package/dist-cjs/tabs-next/TabNextPanel.js.map +1 -1
- package/dist-cjs/tabs-next/TabNextTrigger.js +110 -9
- package/dist-cjs/tabs-next/TabNextTrigger.js.map +1 -1
- package/dist-cjs/tabs-next/TabOverflowList.css.js +1 -1
- package/dist-cjs/tabs-next/TabOverflowList.js +168 -64
- package/dist-cjs/tabs-next/TabOverflowList.js.map +1 -1
- package/dist-cjs/tabs-next/TabSlot.js +30 -0
- package/dist-cjs/tabs-next/TabSlot.js.map +1 -0
- package/dist-cjs/tabs-next/TabSlotRegistryContext.js +16 -0
- package/dist-cjs/tabs-next/TabSlotRegistryContext.js.map +1 -0
- package/dist-cjs/tabs-next/TabsNext.css.js +6 -0
- package/dist-cjs/tabs-next/TabsNext.css.js.map +1 -0
- package/dist-cjs/tabs-next/TabsNext.js +113 -47
- package/dist-cjs/tabs-next/TabsNext.js.map +1 -1
- package/dist-cjs/tabs-next/TabsNextContext.js +17 -3
- package/dist-cjs/tabs-next/TabsNextContext.js.map +1 -1
- package/dist-cjs/tabs-next/domUtils.js +13 -0
- package/dist-cjs/tabs-next/domUtils.js.map +1 -0
- package/dist-cjs/tabs-next/hooks/overflowMath.js +86 -0
- package/dist-cjs/tabs-next/hooks/overflowMath.js.map +1 -0
- package/dist-cjs/tabs-next/hooks/useCollection.js +147 -41
- package/dist-cjs/tabs-next/hooks/useCollection.js.map +1 -1
- package/dist-cjs/tabs-next/hooks/useFocusWithRetry.js +64 -0
- package/dist-cjs/tabs-next/hooks/useFocusWithRetry.js.map +1 -0
- package/dist-cjs/tabs-next/hooks/useOverflow.js +240 -156
- package/dist-cjs/tabs-next/hooks/useOverflow.js.map +1 -1
- package/dist-cjs/tabs-next/hooks/useOverflowLayoutState.js +99 -0
- package/dist-cjs/tabs-next/hooks/useOverflowLayoutState.js.map +1 -0
- package/dist-cjs/tabs-next/hooks/useOverflowSelectionState.js +60 -0
- package/dist-cjs/tabs-next/hooks/useOverflowSelectionState.js.map +1 -0
- package/dist-cjs/tabs-next/hooks/useRenderedTabWidth.js +92 -0
- package/dist-cjs/tabs-next/hooks/useRenderedTabWidth.js.map +1 -0
- package/dist-cjs/tabs-next/hooks/useRenderedTabsRegistry.js +200 -0
- package/dist-cjs/tabs-next/hooks/useRenderedTabsRegistry.js.map +1 -0
- package/dist-cjs/tabs-next/hooks/useTabListRecovery.js +76 -0
- package/dist-cjs/tabs-next/hooks/useTabListRecovery.js.map +1 -0
- package/dist-cjs/tabs-next/hooks/useTabRemovalHandler.js +165 -0
- package/dist-cjs/tabs-next/hooks/useTabRemovalHandler.js.map +1 -0
- package/dist-cjs/tabs-next/hooks/useTabSelectionFocus.js +80 -0
- package/dist-cjs/tabs-next/hooks/useTabSelectionFocus.js.map +1 -0
- package/dist-cjs/tabs-next/widthMeasurement.js +42 -0
- package/dist-cjs/tabs-next/widthMeasurement.js.map +1 -0
- package/dist-cjs/tree/Tree.css.js +1 -1
- package/dist-cjs/tree/TreeNode.css.js +1 -1
- package/dist-cjs/tree/TreeNode.js +1 -1
- package/dist-cjs/tree/TreeNode.js.map +1 -1
- package/dist-cjs/tree/TreeNodeExpansionIcon.css.js +1 -1
- package/dist-cjs/tree/TreeNodeTrigger.css.js +1 -1
- package/dist-cjs/tree/TreeNodeTrigger.js +2 -2
- package/dist-cjs/tree/TreeNodeTrigger.js.map +1 -1
- package/dist-cjs/utils/useEventCallback.js +5 -5
- package/dist-cjs/utils/useEventCallback.js.map +1 -1
- package/dist-es/calendar/internal/CalendarDay.css.js +1 -1
- package/dist-es/contact-details/ContactDetails.css.js +1 -1
- package/dist-es/list-deprecated/ListStateContext.js +1 -1
- package/dist-es/list-deprecated/ListStateContext.js.map +1 -1
- package/dist-es/tabs-next/TabBar.css.js +1 -1
- package/dist-es/tabs-next/TabBar.js +1 -1
- package/dist-es/tabs-next/TabBar.js.map +1 -1
- package/dist-es/tabs-next/TabListLayoutContext.js +10 -0
- package/dist-es/tabs-next/TabListLayoutContext.js.map +1 -0
- package/dist-es/tabs-next/TabListNext.css.js +1 -1
- package/dist-es/tabs-next/TabListNext.js +182 -36
- package/dist-es/tabs-next/TabListNext.js.map +1 -1
- package/dist-es/tabs-next/TabNext.js +113 -9
- package/dist-es/tabs-next/TabNext.js.map +1 -1
- package/dist-es/tabs-next/TabNextAction.js +25 -2
- package/dist-es/tabs-next/TabNextAction.js.map +1 -1
- package/dist-es/tabs-next/TabNextPanel.js +31 -16
- package/dist-es/tabs-next/TabNextPanel.js.map +1 -1
- package/dist-es/tabs-next/TabNextTrigger.js +110 -9
- package/dist-es/tabs-next/TabNextTrigger.js.map +1 -1
- package/dist-es/tabs-next/TabOverflowList.css.js +1 -1
- package/dist-es/tabs-next/TabOverflowList.js +172 -68
- package/dist-es/tabs-next/TabOverflowList.js.map +1 -1
- package/dist-es/tabs-next/TabSlot.js +28 -0
- package/dist-es/tabs-next/TabSlot.js.map +1 -0
- package/dist-es/tabs-next/TabSlotRegistryContext.js +13 -0
- package/dist-es/tabs-next/TabSlotRegistryContext.js.map +1 -0
- package/dist-es/tabs-next/TabsNext.css.js +4 -0
- package/dist-es/tabs-next/TabsNext.css.js.map +1 -0
- package/dist-es/tabs-next/TabsNext.js +114 -48
- package/dist-es/tabs-next/TabsNext.js.map +1 -1
- package/dist-es/tabs-next/TabsNextContext.js +17 -3
- package/dist-es/tabs-next/TabsNextContext.js.map +1 -1
- package/dist-es/tabs-next/domUtils.js +11 -0
- package/dist-es/tabs-next/domUtils.js.map +1 -0
- package/dist-es/tabs-next/hooks/overflowMath.js +82 -0
- package/dist-es/tabs-next/hooks/overflowMath.js.map +1 -0
- package/dist-es/tabs-next/hooks/useCollection.js +148 -42
- package/dist-es/tabs-next/hooks/useCollection.js.map +1 -1
- package/dist-es/tabs-next/hooks/useFocusWithRetry.js +62 -0
- package/dist-es/tabs-next/hooks/useFocusWithRetry.js.map +1 -0
- package/dist-es/tabs-next/hooks/useOverflow.js +242 -158
- package/dist-es/tabs-next/hooks/useOverflow.js.map +1 -1
- package/dist-es/tabs-next/hooks/useOverflowLayoutState.js +97 -0
- package/dist-es/tabs-next/hooks/useOverflowLayoutState.js.map +1 -0
- package/dist-es/tabs-next/hooks/useOverflowSelectionState.js +58 -0
- package/dist-es/tabs-next/hooks/useOverflowSelectionState.js.map +1 -0
- package/dist-es/tabs-next/hooks/useRenderedTabWidth.js +90 -0
- package/dist-es/tabs-next/hooks/useRenderedTabWidth.js.map +1 -0
- package/dist-es/tabs-next/hooks/useRenderedTabsRegistry.js +198 -0
- package/dist-es/tabs-next/hooks/useRenderedTabsRegistry.js.map +1 -0
- package/dist-es/tabs-next/hooks/useTabListRecovery.js +74 -0
- package/dist-es/tabs-next/hooks/useTabListRecovery.js.map +1 -0
- package/dist-es/tabs-next/hooks/useTabRemovalHandler.js +163 -0
- package/dist-es/tabs-next/hooks/useTabRemovalHandler.js.map +1 -0
- package/dist-es/tabs-next/hooks/useTabSelectionFocus.js +78 -0
- package/dist-es/tabs-next/hooks/useTabSelectionFocus.js.map +1 -0
- package/dist-es/tabs-next/widthMeasurement.js +36 -0
- package/dist-es/tabs-next/widthMeasurement.js.map +1 -0
- package/dist-es/tree/Tree.css.js +1 -1
- package/dist-es/tree/TreeNode.css.js +1 -1
- package/dist-es/tree/TreeNode.js +1 -1
- package/dist-es/tree/TreeNode.js.map +1 -1
- package/dist-es/tree/TreeNodeExpansionIcon.css.js +1 -1
- package/dist-es/tree/TreeNodeTrigger.css.js +1 -1
- package/dist-es/tree/TreeNodeTrigger.js +2 -2
- package/dist-es/tree/TreeNodeTrigger.js.map +1 -1
- package/dist-es/utils/useEventCallback.js +5 -5
- package/dist-es/utils/useEventCallback.js.map +1 -1
- package/dist-types/cascading-menu/internal/useMenuTriggerHandlers.d.ts +1 -1
- package/dist-types/list-deprecated/ListStateContext.d.ts +7 -2
- package/dist-types/tabs-next/TabListLayoutContext.d.ts +9 -0
- package/dist-types/tabs-next/TabNext.d.ts +1 -1
- package/dist-types/tabs-next/TabNextPanel.d.ts +2 -1
- package/dist-types/tabs-next/TabOverflowList.d.ts +3 -4
- package/dist-types/tabs-next/TabSlot.d.ts +6 -0
- package/dist-types/tabs-next/TabSlotRegistryContext.d.ts +5 -0
- package/dist-types/tabs-next/TabsNext.d.ts +2 -1
- package/dist-types/tabs-next/TabsNextContext.d.ts +26 -4
- package/dist-types/tabs-next/domUtils.d.ts +1 -0
- package/dist-types/tabs-next/hooks/overflowMath.d.ts +18 -0
- package/dist-types/tabs-next/hooks/useCollection.d.ts +15 -3
- package/dist-types/tabs-next/hooks/useFocusWithRetry.d.ts +9 -0
- package/dist-types/tabs-next/hooks/useOverflow.d.ts +5 -5
- package/dist-types/tabs-next/hooks/useOverflowLayoutState.d.ts +13 -0
- package/dist-types/tabs-next/hooks/useOverflowSelectionState.d.ts +13 -0
- package/dist-types/tabs-next/hooks/useRenderedTabWidth.d.ts +12 -0
- package/dist-types/tabs-next/hooks/useRenderedTabsRegistry.d.ts +12 -0
- package/dist-types/tabs-next/hooks/useTabListRecovery.d.ts +12 -0
- package/dist-types/tabs-next/hooks/useTabRemovalHandler.d.ts +32 -0
- package/dist-types/tabs-next/hooks/useTabSelectionFocus.d.ts +15 -0
- package/dist-types/tabs-next/widthMeasurement.d.ts +5 -0
- package/dist-types/tokenized-input/internal/InputPill.d.ts +1 -1
- package/dist-types/tokenized-input-next/internal/InputPill.d.ts +1 -1
- package/dist-types/utils/useEventCallback.d.ts +1 -1
- package/package.json +3 -3
- package/dist-cjs/tabs-next/hooks/useFocusOutside.js +0 -25
- package/dist-cjs/tabs-next/hooks/useFocusOutside.js.map +0 -1
- package/dist-cjs/tabs-next/hooks/useRestoreActiveTab.js +0 -93
- package/dist-cjs/tabs-next/hooks/useRestoreActiveTab.js.map +0 -1
- package/dist-es/tabs-next/hooks/useFocusOutside.js +0 -23
- package/dist-es/tabs-next/hooks/useFocusOutside.js.map +0 -1
- package/dist-es/tabs-next/hooks/useRestoreActiveTab.js +0 -91
- package/dist-es/tabs-next/hooks/useRestoreActiveTab.js.map +0 -1
- package/dist-types/tabs-next/hooks/useFocusOutside.d.ts +0 -2
- package/dist-types/tabs-next/hooks/useRestoreActiveTab.d.ts +0 -10
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@salt-ds/core');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
|
|
6
|
+
function useOverflowSelectionState({
|
|
7
|
+
commitSelection,
|
|
8
|
+
menuOpen,
|
|
9
|
+
selected,
|
|
10
|
+
setMenuOpen
|
|
11
|
+
}) {
|
|
12
|
+
const previousSelected = core.usePrevious(selected, [selected]);
|
|
13
|
+
const selectionFromOverflowValueRef = react.useRef(null);
|
|
14
|
+
const pendingOverflowSelectionRef = react.useRef(
|
|
15
|
+
null
|
|
16
|
+
);
|
|
17
|
+
const setSelected = react.useCallback(
|
|
18
|
+
(event, value, source = "main") => {
|
|
19
|
+
const selectedFromOverflow = source === "overflow";
|
|
20
|
+
selectionFromOverflowValueRef.current = selectedFromOverflow ? value : null;
|
|
21
|
+
if (selectedFromOverflow) {
|
|
22
|
+
pendingOverflowSelectionRef.current = { event, value };
|
|
23
|
+
setMenuOpen(false);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
pendingOverflowSelectionRef.current = null;
|
|
27
|
+
setMenuOpen(false);
|
|
28
|
+
commitSelection(event, value);
|
|
29
|
+
},
|
|
30
|
+
[commitSelection, setMenuOpen]
|
|
31
|
+
);
|
|
32
|
+
core.useIsomorphicLayoutEffect(() => {
|
|
33
|
+
if (menuOpen) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const pendingSelection = pendingOverflowSelectionRef.current;
|
|
37
|
+
if (!pendingSelection) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
pendingOverflowSelectionRef.current = null;
|
|
41
|
+
commitSelection(pendingSelection.event, pendingSelection.value);
|
|
42
|
+
}, [commitSelection, menuOpen]);
|
|
43
|
+
react.useEffect(() => {
|
|
44
|
+
const selectedFromOverflow = selectionFromOverflowValueRef.current;
|
|
45
|
+
if (selectedFromOverflow == null || pendingOverflowSelectionRef.current) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (selected === selectedFromOverflow && selected !== previousSelected) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
selectionFromOverflowValueRef.current = null;
|
|
52
|
+
}, [previousSelected, selected]);
|
|
53
|
+
return {
|
|
54
|
+
selectionFromOverflowValueRef,
|
|
55
|
+
setSelected
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
exports.useOverflowSelectionState = useOverflowSelectionState;
|
|
60
|
+
//# sourceMappingURL=useOverflowSelectionState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOverflowSelectionState.js","sources":["../src/tabs-next/hooks/useOverflowSelectionState.ts"],"sourcesContent":["import { useIsomorphicLayoutEffect, usePrevious } from \"@salt-ds/core\";\nimport {\n type Dispatch,\n type MutableRefObject,\n type SetStateAction,\n type SyntheticEvent,\n useCallback,\n useEffect,\n useRef,\n} from \"react\";\n\ninterface PendingOverflowSelection {\n event: SyntheticEvent | null;\n value: string;\n}\n\ninterface UseOverflowSelectionStateArgs {\n commitSelection: (event: SyntheticEvent | null, value: string) => void;\n menuOpen: boolean;\n selected?: string;\n setMenuOpen: Dispatch<SetStateAction<boolean>>;\n}\n\ninterface UseOverflowSelectionStateResult {\n selectionFromOverflowValueRef: MutableRefObject<string | null>;\n setSelected: (\n event: SyntheticEvent | null,\n value: string,\n source?: \"main\" | \"overflow\",\n ) => void;\n}\n\nexport function useOverflowSelectionState({\n commitSelection,\n menuOpen,\n selected,\n setMenuOpen,\n}: UseOverflowSelectionStateArgs): UseOverflowSelectionStateResult {\n const previousSelected = usePrevious(selected, [selected]);\n const selectionFromOverflowValueRef = useRef<string | null>(null);\n const pendingOverflowSelectionRef = useRef<PendingOverflowSelection | null>(\n null,\n );\n\n const setSelected = useCallback(\n (\n event: SyntheticEvent | null,\n value: string,\n source: \"main\" | \"overflow\" = \"main\",\n ) => {\n const selectedFromOverflow = source === \"overflow\";\n selectionFromOverflowValueRef.current = selectedFromOverflow\n ? value\n : null;\n\n if (selectedFromOverflow) {\n pendingOverflowSelectionRef.current = { event, value };\n setMenuOpen(false);\n return;\n }\n\n pendingOverflowSelectionRef.current = null;\n setMenuOpen(false);\n commitSelection(event, value);\n },\n [commitSelection, setMenuOpen],\n );\n\n useIsomorphicLayoutEffect(() => {\n if (menuOpen) {\n return;\n }\n\n const pendingSelection = pendingOverflowSelectionRef.current;\n if (!pendingSelection) {\n return;\n }\n\n pendingOverflowSelectionRef.current = null;\n commitSelection(pendingSelection.event, pendingSelection.value);\n }, [commitSelection, menuOpen]);\n\n useEffect(() => {\n const selectedFromOverflow = selectionFromOverflowValueRef.current;\n if (selectedFromOverflow == null || pendingOverflowSelectionRef.current) {\n return;\n }\n\n if (selected === selectedFromOverflow && selected !== previousSelected) {\n return;\n }\n\n selectionFromOverflowValueRef.current = null;\n }, [previousSelected, selected]);\n\n return {\n selectionFromOverflowValueRef,\n setSelected,\n };\n}\n"],"names":["usePrevious","useRef","useCallback","useIsomorphicLayoutEffect","useEffect"],"mappings":";;;;;AAgCO,SAAS,yBAAA,CAA0B;AAAA,EACxC,eAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA,EAAmE;AACjE,EAAA,MAAM,gBAAA,GAAmBA,gBAAA,CAAY,QAAA,EAAU,CAAC,QAAQ,CAAC,CAAA;AACzD,EAAA,MAAM,6BAAA,GAAgCC,aAAsB,IAAI,CAAA;AAChE,EAAA,MAAM,2BAAA,GAA8BA,YAAA;AAAA,IAClC;AAAA,GACF;AAEA,EAAA,MAAM,WAAA,GAAcC,iBAAA;AAAA,IAClB,CACE,KAAA,EACA,KAAA,EACA,MAAA,GAA8B,MAAA,KAC3B;AACH,MAAA,MAAM,uBAAuB,MAAA,KAAW,UAAA;AACxC,MAAA,6BAAA,CAA8B,OAAA,GAAU,uBACpC,KAAA,GACA,IAAA;AAEJ,MAAA,IAAI,oBAAA,EAAsB;AACxB,QAAA,2BAAA,CAA4B,OAAA,GAAU,EAAE,KAAA,EAAO,KAAA,EAAM;AACrD,QAAA,WAAA,CAAY,KAAK,CAAA;AACjB,QAAA;AAAA,MACF;AAEA,MAAA,2BAAA,CAA4B,OAAA,GAAU,IAAA;AACtC,MAAA,WAAA,CAAY,KAAK,CAAA;AACjB,MAAA,eAAA,CAAgB,OAAO,KAAK,CAAA;AAAA,IAC9B,CAAA;AAAA,IACA,CAAC,iBAAiB,WAAW;AAAA,GAC/B;AAEA,EAAAC,8BAAA,CAA0B,MAAM;AAC9B,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,mBAAmB,2BAAA,CAA4B,OAAA;AACrD,IAAA,IAAI,CAAC,gBAAA,EAAkB;AACrB,MAAA;AAAA,IACF;AAEA,IAAA,2BAAA,CAA4B,OAAA,GAAU,IAAA;AACtC,IAAA,eAAA,CAAgB,gBAAA,CAAiB,KAAA,EAAO,gBAAA,CAAiB,KAAK,CAAA;AAAA,EAChE,CAAA,EAAG,CAAC,eAAA,EAAiB,QAAQ,CAAC,CAAA;AAE9B,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,MAAM,uBAAuB,6BAAA,CAA8B,OAAA;AAC3D,IAAA,IAAI,oBAAA,IAAwB,IAAA,IAAQ,2BAAA,CAA4B,OAAA,EAAS;AACvE,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,QAAA,KAAa,oBAAA,IAAwB,QAAA,KAAa,gBAAA,EAAkB;AACtE,MAAA;AAAA,IACF;AAEA,IAAA,6BAAA,CAA8B,OAAA,GAAU,IAAA;AAAA,EAC1C,CAAA,EAAG,CAAC,gBAAA,EAAkB,QAAQ,CAAC,CAAA;AAE/B,EAAA,OAAO;AAAA,IACL,6BAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var widthMeasurement = require('../widthMeasurement.js');
|
|
5
|
+
|
|
6
|
+
const MIN_TRUSTED_RENDERED_TAB_WIDTH = 0.5;
|
|
7
|
+
function isSecondaryMeasurementContext(element) {
|
|
8
|
+
return element.closest(".saltTabOverflow-list") || element.closest(".saltTabListNext-measureContainer");
|
|
9
|
+
}
|
|
10
|
+
function useRenderedTabWidth({
|
|
11
|
+
hostElement,
|
|
12
|
+
renderMode,
|
|
13
|
+
tabRootRef,
|
|
14
|
+
targetWindow,
|
|
15
|
+
updateRenderedTab,
|
|
16
|
+
value
|
|
17
|
+
}) {
|
|
18
|
+
react.useEffect(() => {
|
|
19
|
+
if (!hostElement) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const element = tabRootRef.current;
|
|
23
|
+
const resizeObserverCtor = targetWindow == null ? void 0 : targetWindow.ResizeObserver;
|
|
24
|
+
const mutationObserverCtor = targetWindow == null ? void 0 : targetWindow.MutationObserver;
|
|
25
|
+
if (!element || !resizeObserverCtor) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const updateWidth = (allowSecondaryMeasurementContext = false) => {
|
|
29
|
+
if (!element.isConnected) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (!allowSecondaryMeasurementContext && isSecondaryMeasurementContext(element)) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const width = widthMeasurement.getIntrinsicMeasuredWidth(element);
|
|
36
|
+
if (width <= MIN_TRUSTED_RENDERED_TAB_WIDTH) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
updateRenderedTab(value, {
|
|
40
|
+
width
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
let animationFrameId = null;
|
|
44
|
+
const scheduleWidthUpdate = (allowSecondaryMeasurementContext = false) => {
|
|
45
|
+
if (!(targetWindow == null ? void 0 : targetWindow.requestAnimationFrame)) {
|
|
46
|
+
updateWidth(allowSecondaryMeasurementContext);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (animationFrameId != null) {
|
|
50
|
+
targetWindow.cancelAnimationFrame(animationFrameId);
|
|
51
|
+
}
|
|
52
|
+
animationFrameId = targetWindow.requestAnimationFrame(() => {
|
|
53
|
+
animationFrameId = null;
|
|
54
|
+
updateWidth(allowSecondaryMeasurementContext);
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
if (renderMode === "portal") {
|
|
58
|
+
scheduleWidthUpdate(true);
|
|
59
|
+
} else {
|
|
60
|
+
updateWidth(true);
|
|
61
|
+
}
|
|
62
|
+
const resizeObserver = new resizeObserverCtor(() => {
|
|
63
|
+
updateWidth();
|
|
64
|
+
});
|
|
65
|
+
resizeObserver.observe(element);
|
|
66
|
+
const mutationObserver = mutationObserverCtor ? new mutationObserverCtor(() => {
|
|
67
|
+
scheduleWidthUpdate();
|
|
68
|
+
}) : null;
|
|
69
|
+
mutationObserver == null ? void 0 : mutationObserver.observe(element, {
|
|
70
|
+
childList: true,
|
|
71
|
+
characterData: true,
|
|
72
|
+
subtree: true
|
|
73
|
+
});
|
|
74
|
+
return () => {
|
|
75
|
+
if (animationFrameId != null && targetWindow) {
|
|
76
|
+
targetWindow.cancelAnimationFrame(animationFrameId);
|
|
77
|
+
}
|
|
78
|
+
mutationObserver == null ? void 0 : mutationObserver.disconnect();
|
|
79
|
+
resizeObserver.disconnect();
|
|
80
|
+
};
|
|
81
|
+
}, [
|
|
82
|
+
hostElement,
|
|
83
|
+
renderMode,
|
|
84
|
+
tabRootRef,
|
|
85
|
+
targetWindow,
|
|
86
|
+
updateRenderedTab,
|
|
87
|
+
value
|
|
88
|
+
]);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
exports.useRenderedTabWidth = useRenderedTabWidth;
|
|
92
|
+
//# sourceMappingURL=useRenderedTabWidth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRenderedTabWidth.js","sources":["../src/tabs-next/hooks/useRenderedTabWidth.ts"],"sourcesContent":["import { type RefObject, useEffect } from \"react\";\nimport type {\n TabsNextContextValue,\n TabsNextRenderMode,\n} from \"../TabsNextContext\";\nimport { getIntrinsicMeasuredWidth } from \"../widthMeasurement\";\n\nconst MIN_TRUSTED_RENDERED_TAB_WIDTH = 0.5;\n\ninterface UseRenderedTabWidthProps {\n hostElement: HTMLDivElement | null;\n renderMode: TabsNextRenderMode;\n tabRootRef: RefObject<HTMLDivElement>;\n targetWindow: Window | null | undefined;\n updateRenderedTab: TabsNextContextValue[\"updateRenderedTab\"];\n value: string;\n}\n\nfunction isSecondaryMeasurementContext(element: HTMLElement) {\n return (\n element.closest(\".saltTabOverflow-list\") ||\n element.closest(\".saltTabListNext-measureContainer\")\n );\n}\n\nexport function useRenderedTabWidth({\n hostElement,\n renderMode,\n tabRootRef,\n targetWindow,\n updateRenderedTab,\n value,\n}: UseRenderedTabWidthProps) {\n useEffect(() => {\n if (!hostElement) {\n return;\n }\n\n const element = tabRootRef.current;\n const resizeObserverCtor = (\n targetWindow as\n | (Window & { ResizeObserver?: typeof ResizeObserver })\n | undefined\n )?.ResizeObserver;\n const mutationObserverCtor = (\n targetWindow as\n | (Window & { MutationObserver?: typeof MutationObserver })\n | undefined\n )?.MutationObserver;\n if (!element || !resizeObserverCtor) {\n return;\n }\n\n const updateWidth = (allowSecondaryMeasurementContext = false) => {\n if (!element.isConnected) {\n return;\n }\n\n // Preserve the strip width while a tab is rendered in the overflow menu.\n // Overflow items stretch to the menu width, and hidden measurement tabs\n // can collapse to a different intrinsic size. Neither width is suitable\n // for deciding whether the tab fits back in the main strip once the tab\n // is already established. A one-time seeded width is still useful for\n // newly mounted tabs before they have ever appeared in the main strip.\n if (\n !allowSecondaryMeasurementContext &&\n isSecondaryMeasurementContext(element)\n ) {\n return;\n }\n\n const width = getIntrinsicMeasuredWidth(element);\n if (width <= MIN_TRUSTED_RENDERED_TAB_WIDTH) {\n return;\n }\n\n updateRenderedTab(value, {\n width,\n });\n };\n\n let animationFrameId: number | null = null;\n const scheduleWidthUpdate = (allowSecondaryMeasurementContext = false) => {\n if (!targetWindow?.requestAnimationFrame) {\n updateWidth(allowSecondaryMeasurementContext);\n return;\n }\n\n if (animationFrameId != null) {\n targetWindow.cancelAnimationFrame(animationFrameId);\n }\n\n animationFrameId = targetWindow.requestAnimationFrame(() => {\n animationFrameId = null;\n updateWidth(allowSecondaryMeasurementContext);\n });\n };\n\n if (renderMode === \"portal\") {\n scheduleWidthUpdate(true);\n } else {\n updateWidth(true);\n }\n\n const resizeObserver = new resizeObserverCtor(() => {\n updateWidth();\n });\n\n resizeObserver.observe(element);\n const mutationObserver = mutationObserverCtor\n ? new mutationObserverCtor(() => {\n scheduleWidthUpdate();\n })\n : null;\n\n mutationObserver?.observe(element, {\n childList: true,\n characterData: true,\n subtree: true,\n });\n\n return () => {\n if (animationFrameId != null && targetWindow) {\n targetWindow.cancelAnimationFrame(animationFrameId);\n }\n mutationObserver?.disconnect();\n resizeObserver.disconnect();\n };\n }, [\n hostElement,\n renderMode,\n tabRootRef,\n targetWindow,\n updateRenderedTab,\n value,\n ]);\n}\n"],"names":["useEffect","getIntrinsicMeasuredWidth"],"mappings":";;;;;AAOA,MAAM,8BAAA,GAAiC,GAAA;AAWvC,SAAS,8BAA8B,OAAA,EAAsB;AAC3D,EAAA,OACE,QAAQ,OAAA,CAAQ,uBAAuB,CAAA,IACvC,OAAA,CAAQ,QAAQ,mCAAmC,CAAA;AAEvD;AAEO,SAAS,mBAAA,CAAoB;AAAA,EAClC,WAAA;AAAA,EACA,UAAA;AAAA,EACA,UAAA;AAAA,EACA,YAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACF,CAAA,EAA6B;AAC3B,EAAAA,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,WAAA,EAAa;AAChB,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,UAAU,UAAA,CAAW,OAAA;AAC3B,IAAA,MAAM,qBACJ,YAAA,IAAA,IAAA,GAAA,MAAA,GAAA,YAAA,CAGC,cAAA;AACH,IAAA,MAAM,uBACJ,YAAA,IAAA,IAAA,GAAA,MAAA,GAAA,YAAA,CAGC,gBAAA;AACH,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,kBAAA,EAAoB;AACnC,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,WAAA,GAAc,CAAC,gCAAA,GAAmC,KAAA,KAAU;AAChE,MAAA,IAAI,CAAC,QAAQ,WAAA,EAAa;AACxB,QAAA;AAAA,MACF;AAQA,MAAA,IACE,CAAC,gCAAA,IACD,6BAAA,CAA8B,OAAO,CAAA,EACrC;AACA,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,KAAA,GAAQC,2CAA0B,OAAO,CAAA;AAC/C,MAAA,IAAI,SAAS,8BAAA,EAAgC;AAC3C,QAAA;AAAA,MACF;AAEA,MAAA,iBAAA,CAAkB,KAAA,EAAO;AAAA,QACvB;AAAA,OACD,CAAA;AAAA,IACH,CAAA;AAEA,IAAA,IAAI,gBAAA,GAAkC,IAAA;AACtC,IAAA,MAAM,mBAAA,GAAsB,CAAC,gCAAA,GAAmC,KAAA,KAAU;AACxE,MAAA,IAAI,EAAC,6CAAc,qBAAA,CAAA,EAAuB;AACxC,QAAA,WAAA,CAAY,gCAAgC,CAAA;AAC5C,QAAA;AAAA,MACF;AAEA,MAAA,IAAI,oBAAoB,IAAA,EAAM;AAC5B,QAAA,YAAA,CAAa,qBAAqB,gBAAgB,CAAA;AAAA,MACpD;AAEA,MAAA,gBAAA,GAAmB,YAAA,CAAa,sBAAsB,MAAM;AAC1D,QAAA,gBAAA,GAAmB,IAAA;AACnB,QAAA,WAAA,CAAY,gCAAgC,CAAA;AAAA,MAC9C,CAAC,CAAA;AAAA,IACH,CAAA;AAEA,IAAA,IAAI,eAAe,QAAA,EAAU;AAC3B,MAAA,mBAAA,CAAoB,IAAI,CAAA;AAAA,IAC1B,CAAA,MAAO;AACL,MAAA,WAAA,CAAY,IAAI,CAAA;AAAA,IAClB;AAEA,IAAA,MAAM,cAAA,GAAiB,IAAI,kBAAA,CAAmB,MAAM;AAClD,MAAA,WAAA,EAAY;AAAA,IACd,CAAC,CAAA;AAED,IAAA,cAAA,CAAe,QAAQ,OAAO,CAAA;AAC9B,IAAA,MAAM,gBAAA,GAAmB,oBAAA,GACrB,IAAI,oBAAA,CAAqB,MAAM;AAC7B,MAAA,mBAAA,EAAoB;AAAA,IACtB,CAAC,CAAA,GACD,IAAA;AAEJ,IAAA,gBAAA,IAAA,IAAA,GAAA,MAAA,GAAA,gBAAA,CAAkB,QAAQ,OAAA,EAAS;AAAA,MACjC,SAAA,EAAW,IAAA;AAAA,MACX,aAAA,EAAe,IAAA;AAAA,MACf,OAAA,EAAS;AAAA,KACX,CAAA;AAEA,IAAA,OAAO,MAAM;AACX,MAAA,IAAI,gBAAA,IAAoB,QAAQ,YAAA,EAAc;AAC5C,QAAA,YAAA,CAAa,qBAAqB,gBAAgB,CAAA;AAAA,MACpD;AACA,MAAA,gBAAA,IAAA,IAAA,GAAA,MAAA,GAAA,gBAAA,CAAkB,UAAA,EAAA;AAClB,MAAA,cAAA,CAAe,UAAA,EAAW;AAAA,IAC5B,CAAA;AAAA,EACF,CAAA,EAAG;AAAA,IACD,WAAA;AAAA,IACA,UAAA;AAAA,IACA,UAAA;AAAA,IACA,YAAA;AAAA,IACA,iBAAA;AAAA,IACA;AAAA,GACD,CAAA;AACH;;;;"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@salt-ds/core');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var widthMeasurement = require('../widthMeasurement.js');
|
|
6
|
+
|
|
7
|
+
function sortRenderedTabs(tabs) {
|
|
8
|
+
return [...tabs].sort((tabA, tabB) => {
|
|
9
|
+
if (tabA.marker === tabB.marker) {
|
|
10
|
+
return 0;
|
|
11
|
+
}
|
|
12
|
+
if (!tabA.marker || !tabB.marker) {
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
const position = tabA.marker.compareDocumentPosition(tabB.marker);
|
|
16
|
+
if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
|
|
17
|
+
return -1;
|
|
18
|
+
}
|
|
19
|
+
if (position & Node.DOCUMENT_POSITION_PRECEDING) {
|
|
20
|
+
return 1;
|
|
21
|
+
}
|
|
22
|
+
return 0;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function useRenderedTabsRegistry() {
|
|
26
|
+
const [renderedTabMap, setRenderedTabMap] = react.useState(
|
|
27
|
+
() => /* @__PURE__ */ new Map()
|
|
28
|
+
);
|
|
29
|
+
const [renderMode, setRenderMode] = react.useState("inline");
|
|
30
|
+
const [bootstrapTabs, setBootstrapTabs] = react.useState(() => /* @__PURE__ */ new Set());
|
|
31
|
+
const [readyBootstrapTabs, setReadyBootstrapTabs] = react.useState(
|
|
32
|
+
() => /* @__PURE__ */ new Set()
|
|
33
|
+
);
|
|
34
|
+
const [bootstrapOverflowReady, setBootstrapOverflowReadyState] = react.useState(false);
|
|
35
|
+
const registerBootstrapTab = react.useCallback((tabValue) => {
|
|
36
|
+
setBootstrapTabs((currentTabs) => {
|
|
37
|
+
if (currentTabs.has(tabValue)) {
|
|
38
|
+
return currentTabs;
|
|
39
|
+
}
|
|
40
|
+
const nextTabs = new Set(currentTabs);
|
|
41
|
+
nextTabs.add(tabValue);
|
|
42
|
+
return nextTabs;
|
|
43
|
+
});
|
|
44
|
+
return () => {
|
|
45
|
+
setBootstrapTabs((currentTabs) => {
|
|
46
|
+
if (!currentTabs.has(tabValue)) {
|
|
47
|
+
return currentTabs;
|
|
48
|
+
}
|
|
49
|
+
const nextTabs = new Set(currentTabs);
|
|
50
|
+
nextTabs.delete(tabValue);
|
|
51
|
+
return nextTabs;
|
|
52
|
+
});
|
|
53
|
+
setReadyBootstrapTabs((currentTabs) => {
|
|
54
|
+
if (!currentTabs.has(tabValue)) {
|
|
55
|
+
return currentTabs;
|
|
56
|
+
}
|
|
57
|
+
const nextTabs = new Set(currentTabs);
|
|
58
|
+
nextTabs.delete(tabValue);
|
|
59
|
+
return nextTabs;
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
}, []);
|
|
63
|
+
const setBootstrapTabReady = react.useCallback(
|
|
64
|
+
(tabValue, ready) => {
|
|
65
|
+
setReadyBootstrapTabs((currentTabs) => {
|
|
66
|
+
const hasTab = currentTabs.has(tabValue);
|
|
67
|
+
if (ready === hasTab) {
|
|
68
|
+
return currentTabs;
|
|
69
|
+
}
|
|
70
|
+
const nextTabs = new Set(currentTabs);
|
|
71
|
+
if (ready) {
|
|
72
|
+
nextTabs.add(tabValue);
|
|
73
|
+
} else {
|
|
74
|
+
nextTabs.delete(tabValue);
|
|
75
|
+
}
|
|
76
|
+
return nextTabs;
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
[]
|
|
80
|
+
);
|
|
81
|
+
const setBootstrapOverflowReady = react.useCallback((ready) => {
|
|
82
|
+
setBootstrapOverflowReadyState((currentReady) => {
|
|
83
|
+
if (currentReady === ready) {
|
|
84
|
+
return currentReady;
|
|
85
|
+
}
|
|
86
|
+
return ready;
|
|
87
|
+
});
|
|
88
|
+
}, []);
|
|
89
|
+
core.useIsomorphicLayoutEffect(() => {
|
|
90
|
+
if (renderMode === "portal" || bootstrapTabs.size < 1) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (!bootstrapOverflowReady) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
for (const tabValue of bootstrapTabs) {
|
|
97
|
+
if (!readyBootstrapTabs.has(tabValue)) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const renderedTab = renderedTabMap.get(tabValue);
|
|
101
|
+
if (!renderedTab || widthMeasurement.getMeasuredWidth(renderedTab.root) <= 0) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
setRenderMode("portal");
|
|
106
|
+
}, [
|
|
107
|
+
bootstrapOverflowReady,
|
|
108
|
+
bootstrapTabs,
|
|
109
|
+
readyBootstrapTabs,
|
|
110
|
+
renderMode,
|
|
111
|
+
renderedTabMap
|
|
112
|
+
]);
|
|
113
|
+
const registerRenderedTab = react.useCallback((tab) => {
|
|
114
|
+
setRenderedTabMap((map) => {
|
|
115
|
+
const existing = map.get(tab.value);
|
|
116
|
+
if (process.env.NODE_ENV !== "production" && existing && existing.id !== tab.id) {
|
|
117
|
+
console.warn(
|
|
118
|
+
`TabsNext received duplicate tab value "${tab.value}". Tab values must be unique within a TabsNext instance.`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
if (existing === tab) {
|
|
122
|
+
return map;
|
|
123
|
+
}
|
|
124
|
+
const next = new Map(map);
|
|
125
|
+
next.set(tab.value, tab);
|
|
126
|
+
return next;
|
|
127
|
+
});
|
|
128
|
+
return () => {
|
|
129
|
+
setRenderedTabMap((map) => {
|
|
130
|
+
const existing = map.get(tab.value);
|
|
131
|
+
if (!existing || existing.id !== tab.id) {
|
|
132
|
+
return map;
|
|
133
|
+
}
|
|
134
|
+
const next = new Map(map);
|
|
135
|
+
next.delete(tab.value);
|
|
136
|
+
return next;
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
}, []);
|
|
140
|
+
const updateRenderedTab = react.useCallback(
|
|
141
|
+
(value, updates) => {
|
|
142
|
+
setRenderedTabMap((map) => {
|
|
143
|
+
const existing = map.get(value);
|
|
144
|
+
if (!existing) {
|
|
145
|
+
return map;
|
|
146
|
+
}
|
|
147
|
+
let changed = false;
|
|
148
|
+
const nextRecord = { ...existing };
|
|
149
|
+
for (const [key, nextValue] of Object.entries(updates)) {
|
|
150
|
+
const typedKey = key;
|
|
151
|
+
if (nextRecord[typedKey] !== nextValue) {
|
|
152
|
+
changed = true;
|
|
153
|
+
nextRecord[typedKey] = nextValue;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (!changed) {
|
|
157
|
+
return map;
|
|
158
|
+
}
|
|
159
|
+
const next = new Map(map);
|
|
160
|
+
next.set(value, nextRecord);
|
|
161
|
+
return next;
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
[]
|
|
165
|
+
);
|
|
166
|
+
const getRenderedTab = react.useCallback(
|
|
167
|
+
(value) => {
|
|
168
|
+
return renderedTabMap.get(value);
|
|
169
|
+
},
|
|
170
|
+
[renderedTabMap]
|
|
171
|
+
);
|
|
172
|
+
const renderedTabs = react.useMemo(() => {
|
|
173
|
+
return sortRenderedTabs(Array.from(renderedTabMap.values()));
|
|
174
|
+
}, [renderedTabMap]);
|
|
175
|
+
const renderedTabOrderMap = react.useMemo(() => {
|
|
176
|
+
return new Map(
|
|
177
|
+
renderedTabs.map((tab, index) => [tab.value, index])
|
|
178
|
+
);
|
|
179
|
+
}, [renderedTabs]);
|
|
180
|
+
const getRenderedTabOrder = react.useCallback(
|
|
181
|
+
(value) => {
|
|
182
|
+
return renderedTabOrderMap.get(value) ?? -1;
|
|
183
|
+
},
|
|
184
|
+
[renderedTabOrderMap]
|
|
185
|
+
);
|
|
186
|
+
return {
|
|
187
|
+
renderMode,
|
|
188
|
+
registerBootstrapTab,
|
|
189
|
+
setBootstrapTabReady,
|
|
190
|
+
setBootstrapOverflowReady,
|
|
191
|
+
registerRenderedTab,
|
|
192
|
+
updateRenderedTab,
|
|
193
|
+
getRenderedTab,
|
|
194
|
+
getRenderedTabOrder,
|
|
195
|
+
renderedTabs
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
exports.useRenderedTabsRegistry = useRenderedTabsRegistry;
|
|
200
|
+
//# sourceMappingURL=useRenderedTabsRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRenderedTabsRegistry.js","sources":["../src/tabs-next/hooks/useRenderedTabsRegistry.ts"],"sourcesContent":["import { useIsomorphicLayoutEffect } from \"@salt-ds/core\";\nimport { useCallback, useMemo, useState } from \"react\";\nimport type { RenderedTab, TabsNextRenderMode } from \"../TabsNextContext\";\nimport { getMeasuredWidth } from \"../widthMeasurement\";\n\nfunction sortRenderedTabs(tabs: RenderedTab[]) {\n return [...tabs].sort((tabA, tabB) => {\n if (tabA.marker === tabB.marker) {\n return 0;\n }\n if (!tabA.marker || !tabB.marker) {\n return 0;\n }\n\n const position = tabA.marker.compareDocumentPosition(tabB.marker);\n if (position & Node.DOCUMENT_POSITION_FOLLOWING) {\n return -1;\n }\n if (position & Node.DOCUMENT_POSITION_PRECEDING) {\n return 1;\n }\n return 0;\n });\n}\n\nexport function useRenderedTabsRegistry() {\n const [renderedTabMap, setRenderedTabMap] = useState(\n () => new Map<string, RenderedTab>(),\n );\n const [renderMode, setRenderMode] = useState<TabsNextRenderMode>(\"inline\");\n const [bootstrapTabs, setBootstrapTabs] = useState(() => new Set<string>());\n const [readyBootstrapTabs, setReadyBootstrapTabs] = useState(\n () => new Set<string>(),\n );\n const [bootstrapOverflowReady, setBootstrapOverflowReadyState] =\n useState(false);\n\n const registerBootstrapTab = useCallback((tabValue: string) => {\n setBootstrapTabs((currentTabs) => {\n if (currentTabs.has(tabValue)) {\n return currentTabs;\n }\n\n const nextTabs = new Set(currentTabs);\n nextTabs.add(tabValue);\n return nextTabs;\n });\n\n return () => {\n setBootstrapTabs((currentTabs) => {\n if (!currentTabs.has(tabValue)) {\n return currentTabs;\n }\n\n const nextTabs = new Set(currentTabs);\n nextTabs.delete(tabValue);\n return nextTabs;\n });\n setReadyBootstrapTabs((currentTabs) => {\n if (!currentTabs.has(tabValue)) {\n return currentTabs;\n }\n\n const nextTabs = new Set(currentTabs);\n nextTabs.delete(tabValue);\n return nextTabs;\n });\n };\n }, []);\n\n const setBootstrapTabReady = useCallback(\n (tabValue: string, ready: boolean) => {\n setReadyBootstrapTabs((currentTabs) => {\n const hasTab = currentTabs.has(tabValue);\n if (ready === hasTab) {\n return currentTabs;\n }\n\n const nextTabs = new Set(currentTabs);\n if (ready) {\n nextTabs.add(tabValue);\n } else {\n nextTabs.delete(tabValue);\n }\n return nextTabs;\n });\n },\n [],\n );\n\n const setBootstrapOverflowReady = useCallback((ready: boolean) => {\n setBootstrapOverflowReadyState((currentReady) => {\n if (currentReady === ready) {\n return currentReady;\n }\n\n return ready;\n });\n }, []);\n\n useIsomorphicLayoutEffect(() => {\n if (renderMode === \"portal\" || bootstrapTabs.size < 1) {\n return;\n }\n\n if (!bootstrapOverflowReady) {\n return;\n }\n\n for (const tabValue of bootstrapTabs) {\n if (!readyBootstrapTabs.has(tabValue)) {\n return;\n }\n\n const renderedTab = renderedTabMap.get(tabValue);\n if (!renderedTab || getMeasuredWidth(renderedTab.root) <= 0) {\n return;\n }\n }\n\n setRenderMode(\"portal\");\n }, [\n bootstrapOverflowReady,\n bootstrapTabs,\n readyBootstrapTabs,\n renderMode,\n renderedTabMap,\n ]);\n\n const registerRenderedTab = useCallback((tab: RenderedTab) => {\n setRenderedTabMap((map) => {\n const existing = map.get(tab.value);\n if (\n process.env.NODE_ENV !== \"production\" &&\n existing &&\n existing.id !== tab.id\n ) {\n console.warn(\n `TabsNext received duplicate tab value \"${tab.value}\". Tab values must be unique within a TabsNext instance.`,\n );\n }\n\n if (existing === tab) {\n return map;\n }\n\n const next = new Map(map);\n next.set(tab.value, tab);\n return next;\n });\n\n return () => {\n setRenderedTabMap((map) => {\n const existing = map.get(tab.value);\n if (!existing || existing.id !== tab.id) {\n return map;\n }\n\n const next = new Map(map);\n next.delete(tab.value);\n return next;\n });\n };\n }, []);\n\n const updateRenderedTab = useCallback(\n (value: string, updates: Partial<Omit<RenderedTab, \"value\">>) => {\n setRenderedTabMap((map) => {\n const existing = map.get(value);\n if (!existing) {\n return map;\n }\n\n let changed = false;\n const nextRecord = { ...existing };\n for (const [key, nextValue] of Object.entries(updates)) {\n const typedKey = key as keyof Omit<RenderedTab, \"value\">;\n if (nextRecord[typedKey] !== nextValue) {\n changed = true;\n nextRecord[typedKey] = nextValue as never;\n }\n }\n\n if (!changed) {\n return map;\n }\n\n const next = new Map(map);\n next.set(value, nextRecord);\n return next;\n });\n },\n [],\n );\n\n const getRenderedTab = useCallback(\n (value: string) => {\n return renderedTabMap.get(value);\n },\n [renderedTabMap],\n );\n\n const renderedTabs = useMemo(() => {\n return sortRenderedTabs(Array.from(renderedTabMap.values()));\n }, [renderedTabMap]);\n\n const renderedTabOrderMap = useMemo(() => {\n return new Map(\n renderedTabs.map((tab, index) => [tab.value, index] as const),\n );\n }, [renderedTabs]);\n\n const getRenderedTabOrder = useCallback(\n (value: string) => {\n return renderedTabOrderMap.get(value) ?? -1;\n },\n [renderedTabOrderMap],\n );\n\n return {\n renderMode,\n registerBootstrapTab,\n setBootstrapTabReady,\n setBootstrapOverflowReady,\n registerRenderedTab,\n updateRenderedTab,\n getRenderedTab,\n getRenderedTabOrder,\n renderedTabs,\n };\n}\n"],"names":["useState","useCallback","useIsomorphicLayoutEffect","getMeasuredWidth","useMemo"],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var domUtils = require('../domUtils.js');
|
|
5
|
+
|
|
6
|
+
function useTabListRecovery({
|
|
7
|
+
removalVersion,
|
|
8
|
+
targetWindow,
|
|
9
|
+
tabstripRef,
|
|
10
|
+
overflowListRef,
|
|
11
|
+
handleTabRemoval,
|
|
12
|
+
pendingRemovalRecoveryRef,
|
|
13
|
+
pendingRemovalRecoveryRetriesRef
|
|
14
|
+
}) {
|
|
15
|
+
react.useEffect(() => {
|
|
16
|
+
if (removalVersion < 1) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
pendingRemovalRecoveryRef.current = true;
|
|
20
|
+
pendingRemovalRecoveryRetriesRef.current = 0;
|
|
21
|
+
const raf = targetWindow == null ? void 0 : targetWindow.requestAnimationFrame(() => {
|
|
22
|
+
handleTabRemoval();
|
|
23
|
+
});
|
|
24
|
+
return () => {
|
|
25
|
+
if (raf != null) {
|
|
26
|
+
targetWindow == null ? void 0 : targetWindow.cancelAnimationFrame(raf);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}, [
|
|
30
|
+
handleTabRemoval,
|
|
31
|
+
pendingRemovalRecoveryRef,
|
|
32
|
+
pendingRemovalRecoveryRetriesRef,
|
|
33
|
+
removalVersion,
|
|
34
|
+
targetWindow
|
|
35
|
+
]);
|
|
36
|
+
react.useEffect(() => {
|
|
37
|
+
const isInTabList = (node) => {
|
|
38
|
+
var _a, _b;
|
|
39
|
+
if (!node) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
return (((_a = tabstripRef.current) == null ? void 0 : _a.contains(node)) ?? false) || (((_b = overflowListRef.current) == null ? void 0 : _b.contains(node)) ?? false);
|
|
43
|
+
};
|
|
44
|
+
const handleFocusOut = (event) => {
|
|
45
|
+
if (!pendingRemovalRecoveryRef.current) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const wasInTabList = domUtils.isHTMLElement(event.target) && isInTabList(event.target);
|
|
49
|
+
const stillInTabList = domUtils.isHTMLElement(event.relatedTarget) && isInTabList(event.relatedTarget);
|
|
50
|
+
if (wasInTabList && !stillInTabList) {
|
|
51
|
+
if (targetWindow == null ? void 0 : targetWindow.requestAnimationFrame) {
|
|
52
|
+
targetWindow.requestAnimationFrame(() => handleTabRemoval());
|
|
53
|
+
} else {
|
|
54
|
+
queueMicrotask(() => handleTabRemoval());
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
targetWindow == null ? void 0 : targetWindow.document.addEventListener("focusout", handleFocusOut, true);
|
|
59
|
+
return () => {
|
|
60
|
+
targetWindow == null ? void 0 : targetWindow.document.removeEventListener(
|
|
61
|
+
"focusout",
|
|
62
|
+
handleFocusOut,
|
|
63
|
+
true
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
}, [
|
|
67
|
+
handleTabRemoval,
|
|
68
|
+
overflowListRef,
|
|
69
|
+
pendingRemovalRecoveryRef,
|
|
70
|
+
tabstripRef,
|
|
71
|
+
targetWindow
|
|
72
|
+
]);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
exports.useTabListRecovery = useTabListRecovery;
|
|
76
|
+
//# sourceMappingURL=useTabListRecovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTabListRecovery.js","sources":["../src/tabs-next/hooks/useTabListRecovery.ts"],"sourcesContent":["import { type MutableRefObject, type RefObject, useEffect } from \"react\";\nimport { isHTMLElement } from \"../domUtils\";\n\ninterface UseTabListRecoveryArgs {\n removalVersion: number;\n targetWindow: Window | null | undefined;\n tabstripRef: RefObject<HTMLDivElement | null>;\n overflowListRef: RefObject<HTMLDivElement | null>;\n handleTabRemoval: () => void;\n pendingRemovalRecoveryRef: MutableRefObject<boolean>;\n pendingRemovalRecoveryRetriesRef: MutableRefObject<number>;\n}\n\nexport function useTabListRecovery({\n removalVersion,\n targetWindow,\n tabstripRef,\n overflowListRef,\n handleTabRemoval,\n pendingRemovalRecoveryRef,\n pendingRemovalRecoveryRetriesRef,\n}: UseTabListRecoveryArgs) {\n useEffect(() => {\n if (removalVersion < 1) {\n return;\n }\n\n pendingRemovalRecoveryRef.current = true;\n pendingRemovalRecoveryRetriesRef.current = 0;\n\n const raf = targetWindow?.requestAnimationFrame(() => {\n handleTabRemoval();\n });\n\n return () => {\n if (raf != null) {\n targetWindow?.cancelAnimationFrame(raf);\n }\n };\n }, [\n handleTabRemoval,\n pendingRemovalRecoveryRef,\n pendingRemovalRecoveryRetriesRef,\n removalVersion,\n targetWindow,\n ]);\n\n useEffect(() => {\n const isInTabList = (node: HTMLElement | null) => {\n if (!node) {\n return false;\n }\n\n return (\n (tabstripRef.current?.contains(node) ?? false) ||\n (overflowListRef.current?.contains(node) ?? false)\n );\n };\n\n const handleFocusOut = (event: FocusEvent) => {\n if (!pendingRemovalRecoveryRef.current) {\n return;\n }\n\n const wasInTabList =\n isHTMLElement(event.target) && isInTabList(event.target);\n const stillInTabList =\n isHTMLElement(event.relatedTarget) && isInTabList(event.relatedTarget);\n\n if (wasInTabList && !stillInTabList) {\n if (targetWindow?.requestAnimationFrame) {\n targetWindow.requestAnimationFrame(() => handleTabRemoval());\n } else {\n queueMicrotask(() => handleTabRemoval());\n }\n }\n };\n\n targetWindow?.document.addEventListener(\"focusout\", handleFocusOut, true);\n\n return () => {\n targetWindow?.document.removeEventListener(\n \"focusout\",\n handleFocusOut,\n true,\n );\n };\n }, [\n handleTabRemoval,\n overflowListRef,\n pendingRemovalRecoveryRef,\n tabstripRef,\n targetWindow,\n ]);\n}\n"],"names":["useEffect","isHTMLElement"],"mappings":";;;;;AAaO,SAAS,kBAAA,CAAmB;AAAA,EACjC,cAAA;AAAA,EACA,YAAA;AAAA,EACA,WAAA;AAAA,EACA,eAAA;AAAA,EACA,gBAAA;AAAA,EACA,yBAAA;AAAA,EACA;AACF,CAAA,EAA2B;AACzB,EAAAA,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,iBAAiB,CAAA,EAAG;AACtB,MAAA;AAAA,IACF;AAEA,IAAA,yBAAA,CAA0B,OAAA,GAAU,IAAA;AACpC,IAAA,gCAAA,CAAiC,OAAA,GAAU,CAAA;AAE3C,IAAA,MAAM,GAAA,GAAM,YAAA,IAAA,IAAA,GAAA,MAAA,GAAA,YAAA,CAAc,qBAAA,CAAsB,MAAM;AACpD,MAAA,gBAAA,EAAiB;AAAA,IACnB,CAAA,CAAA;AAEA,IAAA,OAAO,MAAM;AACX,MAAA,IAAI,OAAO,IAAA,EAAM;AACf,QAAA,YAAA,IAAA,IAAA,GAAA,MAAA,GAAA,YAAA,CAAc,oBAAA,CAAqB,GAAA,CAAA;AAAA,MACrC;AAAA,IACF,CAAA;AAAA,EACF,CAAA,EAAG;AAAA,IACD,gBAAA;AAAA,IACA,yBAAA;AAAA,IACA,gCAAA;AAAA,IACA,cAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAAA,eAAA,CAAU,MAAM;AACd,IAAA,MAAM,WAAA,GAAc,CAAC,IAAA,KAA6B;AAhDtD,MAAA,IAAA,EAAA,EAAA,EAAA;AAiDM,MAAA,IAAI,CAAC,IAAA,EAAM;AACT,QAAA,OAAO,KAAA;AAAA,MACT;AAEA,MAAA,OAAA,CAAA,CAAA,CACG,EAAA,GAAA,WAAA,CAAY,OAAA,KAAZ,IAAA,GAAA,MAAA,GAAA,EAAA,CAAqB,QAAA,CAAS,IAAA,CAAA,KAAS,aACvC,EAAA,GAAA,eAAA,CAAgB,OAAA,KAAhB,IAAA,GAAA,MAAA,GAAA,EAAA,CAAyB,QAAA,CAAS,IAAA,CAAA,KAAS,KAAA,CAAA;AAAA,IAEhD,CAAA;AAEA,IAAA,MAAM,cAAA,GAAiB,CAAC,KAAA,KAAsB;AAC5C,MAAA,IAAI,CAAC,0BAA0B,OAAA,EAAS;AACtC,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,eACJC,sBAAA,CAAc,KAAA,CAAM,MAAM,CAAA,IAAK,WAAA,CAAY,MAAM,MAAM,CAAA;AACzD,MAAA,MAAM,iBACJA,sBAAA,CAAc,KAAA,CAAM,aAAa,CAAA,IAAK,WAAA,CAAY,MAAM,aAAa,CAAA;AAEvE,MAAA,IAAI,YAAA,IAAgB,CAAC,cAAA,EAAgB;AACnC,QAAA,IAAI,6CAAc,qBAAA,EAAuB;AACvC,UAAA,YAAA,CAAa,qBAAA,CAAsB,MAAM,gBAAA,EAAkB,CAAA;AAAA,QAC7D,CAAA,MAAO;AACL,UAAA,cAAA,CAAe,MAAM,kBAAkB,CAAA;AAAA,QACzC;AAAA,MACF;AAAA,IACF,CAAA;AAEA,IAAA,YAAA,IAAA,IAAA,GAAA,MAAA,GAAA,YAAA,CAAc,QAAA,CAAS,gBAAA,CAAiB,UAAA,EAAY,cAAA,EAAgB,IAAA,CAAA;AAEpE,IAAA,OAAO,MAAM;AACX,MAAA,YAAA,IAAA,IAAA,GAAA,MAAA,GAAA,YAAA,CAAc,QAAA,CAAS,mBAAA;AAAA,QACrB,UAAA;AAAA,QACA,cAAA;AAAA,QACA;AAAA,OAAA;AAAA,IAEJ,CAAA;AAAA,EACF,CAAA,EAAG;AAAA,IACD,gBAAA;AAAA,IACA,eAAA;AAAA,IACA,yBAAA;AAAA,IACA,WAAA;AAAA,IACA;AAAA,GACD,CAAA;AACH;;;;"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var useEventCallback = require('../../utils/useEventCallback.js');
|
|
5
|
+
var domUtils = require('../domUtils.js');
|
|
6
|
+
|
|
7
|
+
function hasLostDocumentFocus(doc) {
|
|
8
|
+
const activeElement = doc.activeElement;
|
|
9
|
+
return !activeElement || activeElement === doc.body || activeElement === doc.documentElement || !activeElement.isConnected;
|
|
10
|
+
}
|
|
11
|
+
function getVisibleSelectedTab(tabstrip, excludedId) {
|
|
12
|
+
if (!tabstrip) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const tabs = tabstrip.querySelectorAll(
|
|
16
|
+
':scope > [data-tabslot] [role="tab"][aria-selected="true"]'
|
|
17
|
+
);
|
|
18
|
+
return Array.from(tabs).find((tab) => {
|
|
19
|
+
if (excludedId && tab.id === excludedId) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return tab.isConnected;
|
|
23
|
+
}) ?? null;
|
|
24
|
+
}
|
|
25
|
+
function useTabRemovalHandler({
|
|
26
|
+
activeTab,
|
|
27
|
+
focusElementWithRetry,
|
|
28
|
+
getFirst,
|
|
29
|
+
getIndex,
|
|
30
|
+
getLast,
|
|
31
|
+
getRemovedItems,
|
|
32
|
+
getRenderedTab,
|
|
33
|
+
getSelectedTabElement,
|
|
34
|
+
item,
|
|
35
|
+
itemAt,
|
|
36
|
+
maxRetryAttempts,
|
|
37
|
+
menuOpen,
|
|
38
|
+
overflowButtonRef,
|
|
39
|
+
overflowListRef,
|
|
40
|
+
pendingRemovalRecoveryRef,
|
|
41
|
+
pendingRemovalRecoveryRetriesRef,
|
|
42
|
+
removalRecoveryRafRef,
|
|
43
|
+
selected,
|
|
44
|
+
setSelected,
|
|
45
|
+
tabstripRef,
|
|
46
|
+
targetWindow
|
|
47
|
+
}) {
|
|
48
|
+
const clearPendingRemovalRecovery = react.useCallback(() => {
|
|
49
|
+
pendingRemovalRecoveryRef.current = false;
|
|
50
|
+
pendingRemovalRecoveryRetriesRef.current = 0;
|
|
51
|
+
}, [pendingRemovalRecoveryRef, pendingRemovalRecoveryRetriesRef]);
|
|
52
|
+
const handleTabRemoval = useEventCallback.useEventCallback(() => {
|
|
53
|
+
var _a;
|
|
54
|
+
const doc = targetWindow == null ? void 0 : targetWindow.document;
|
|
55
|
+
if (!doc) return;
|
|
56
|
+
const activeTabWasSelected = ((_a = activeTab.current) == null ? void 0 : _a.value) === selected;
|
|
57
|
+
const shouldRecoverFocus = () => {
|
|
58
|
+
var _a2, _b;
|
|
59
|
+
if (hasLostDocumentFocus(doc)) return true;
|
|
60
|
+
const activeElement = doc.activeElement;
|
|
61
|
+
if (!domUtils.isHTMLElement(activeElement)) return false;
|
|
62
|
+
if (!menuOpen) {
|
|
63
|
+
if (activeElement === overflowButtonRef.current) {
|
|
64
|
+
return activeTabWasSelected;
|
|
65
|
+
}
|
|
66
|
+
return ((_a2 = tabstripRef.current) == null ? void 0 : _a2.contains(activeElement)) && activeElement.getAttribute("role") !== "tab";
|
|
67
|
+
}
|
|
68
|
+
if (activeElement === overflowButtonRef.current) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
if ((_b = overflowListRef.current) == null ? void 0 : _b.contains(activeElement)) {
|
|
72
|
+
return activeElement.getAttribute("role") !== "tab";
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
};
|
|
76
|
+
if (!shouldRecoverFocus()) {
|
|
77
|
+
if (pendingRemovalRecoveryRef.current && removalRecoveryRafRef.current == null) {
|
|
78
|
+
if (pendingRemovalRecoveryRetriesRef.current < maxRetryAttempts) {
|
|
79
|
+
pendingRemovalRecoveryRetriesRef.current += 1;
|
|
80
|
+
if (!(targetWindow == null ? void 0 : targetWindow.requestAnimationFrame)) {
|
|
81
|
+
handleTabRemoval();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
removalRecoveryRafRef.current = targetWindow.requestAnimationFrame(
|
|
85
|
+
() => {
|
|
86
|
+
removalRecoveryRafRef.current = null;
|
|
87
|
+
handleTabRemoval();
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
} else {
|
|
91
|
+
clearPendingRemovalRecovery();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (!activeTab.current) {
|
|
97
|
+
clearPendingRemovalRecovery();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const removedItems = getRemovedItems();
|
|
101
|
+
const removed = removedItems.get(activeTab.current.id);
|
|
102
|
+
if (!removed) {
|
|
103
|
+
clearPendingRemovalRecovery();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
clearPendingRemovalRecovery();
|
|
107
|
+
const removedWasSelected = removed.value === selected;
|
|
108
|
+
const baseIndex = removed.staleIndex ?? -1;
|
|
109
|
+
const removedId = removed.id;
|
|
110
|
+
const restoreFocus = () => {
|
|
111
|
+
var _a2;
|
|
112
|
+
const restoredTab = item(removedId);
|
|
113
|
+
const restoredRenderedTab = getRenderedTab(removed.value);
|
|
114
|
+
const focusMovedToOverflowButton = doc.activeElement === overflowButtonRef.current;
|
|
115
|
+
if (((_a2 = restoredTab == null ? void 0 : restoredTab.element) == null ? void 0 : _a2.isConnected) && (restoredRenderedTab == null ? void 0 : restoredRenderedTab.trigger) === restoredTab.element && !(removedWasSelected && focusMovedToOverflowButton)) {
|
|
116
|
+
if (shouldRecoverFocus()) {
|
|
117
|
+
focusElementWithRetry(() => {
|
|
118
|
+
var _a3;
|
|
119
|
+
return (_a3 = item(removedId)) == null ? void 0 : _a3.element;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (!shouldRecoverFocus()) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
let nextTab = (baseIndex >= 0 ? itemAt(baseIndex) : null) ?? (baseIndex > 0 ? itemAt(baseIndex - 1) : null) ?? getLast() ?? getFirst();
|
|
128
|
+
if ((nextTab == null ? void 0 : nextTab.element) === overflowButtonRef.current) {
|
|
129
|
+
const overflowIndex = getIndex(nextTab.id);
|
|
130
|
+
nextTab = (overflowIndex > 0 ? itemAt(overflowIndex - 1) : null) ?? (baseIndex > 0 ? itemAt(baseIndex - 1) : null) ?? getFirst();
|
|
131
|
+
}
|
|
132
|
+
if (!(nextTab == null ? void 0 : nextTab.element)) return;
|
|
133
|
+
if (removedWasSelected && !menuOpen && !getVisibleSelectedTab(tabstripRef.current, removedId)) {
|
|
134
|
+
activeTab.current = { id: nextTab.id, value: nextTab.value };
|
|
135
|
+
setSelected(null, nextTab.value);
|
|
136
|
+
}
|
|
137
|
+
focusElementWithRetry(() => {
|
|
138
|
+
if (removedWasSelected) {
|
|
139
|
+
return getSelectedTabElement() ?? (nextTab == null ? void 0 : nextTab.element);
|
|
140
|
+
}
|
|
141
|
+
return nextTab == null ? void 0 : nextTab.element;
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
if (!(targetWindow == null ? void 0 : targetWindow.requestAnimationFrame)) {
|
|
145
|
+
restoreFocus();
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
targetWindow.requestAnimationFrame(() => {
|
|
149
|
+
targetWindow.requestAnimationFrame(() => {
|
|
150
|
+
restoreFocus();
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
react.useEffect(() => {
|
|
155
|
+
return () => {
|
|
156
|
+
if (removalRecoveryRafRef.current != null && targetWindow) {
|
|
157
|
+
targetWindow.cancelAnimationFrame(removalRecoveryRafRef.current);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
}, [removalRecoveryRafRef, targetWindow]);
|
|
161
|
+
return handleTabRemoval;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
exports.useTabRemovalHandler = useTabRemovalHandler;
|
|
165
|
+
//# sourceMappingURL=useTabRemovalHandler.js.map
|