@salt-ds/lab 1.0.0-alpha.60 → 1.0.0-alpha.62
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 +93 -0
- package/css/salt-lab.css +185 -1
- package/dist-cjs/date-picker/DatePickerRangeInput.js +1 -0
- package/dist-cjs/date-picker/DatePickerRangeInput.js.map +1 -1
- package/dist-cjs/date-picker/DatePickerSingleInput.js +1 -0
- package/dist-cjs/date-picker/DatePickerSingleInput.js.map +1 -1
- package/dist-cjs/index.js +6 -0
- package/dist-cjs/index.js.map +1 -1
- package/dist-cjs/splitter/SplitHandle.css.js +6 -0
- package/dist-cjs/splitter/SplitHandle.css.js.map +1 -0
- package/dist-cjs/splitter/SplitHandle.js +60 -0
- package/dist-cjs/splitter/SplitHandle.js.map +1 -0
- package/dist-cjs/splitter/SplitPanel.css.js +6 -0
- package/dist-cjs/splitter/SplitPanel.css.js.map +1 -0
- package/dist-cjs/splitter/SplitPanel.js +37 -0
- package/dist-cjs/splitter/SplitPanel.js.map +1 -0
- package/dist-cjs/splitter/Splitter.js +31 -0
- package/dist-cjs/splitter/Splitter.js.map +1 -0
- package/dist-cjs/splitter/utils.js +18 -0
- package/dist-cjs/splitter/utils.js.map +1 -0
- package/dist-cjs/stepped-tracker/stepReducer.js +127 -81
- package/dist-cjs/stepped-tracker/stepReducer.js.map +1 -1
- package/dist-cjs/stepped-tracker/useStepReducer.js +6 -4
- package/dist-cjs/stepped-tracker/useStepReducer.js.map +1 -1
- package/dist-cjs/stepped-tracker/utils.js +44 -9
- package/dist-cjs/stepped-tracker/utils.js.map +1 -1
- package/dist-cjs/stepper-input/StepperInput.js +6 -5
- package/dist-cjs/stepper-input/StepperInput.js.map +1 -1
- package/dist-cjs/tabs-next/TabListNext.css.js +1 -1
- package/dist-cjs/tabs-next/TabListNext.js +10 -16
- package/dist-cjs/tabs-next/TabListNext.js.map +1 -1
- package/dist-cjs/tabs-next/TabOverflowList.js +1 -1
- package/dist-cjs/tabs-next/TabOverflowList.js.map +1 -1
- package/dist-cjs/tabs-next/TabsNext.js +4 -51
- package/dist-cjs/tabs-next/TabsNext.js.map +1 -1
- package/dist-cjs/tabs-next/TabsNextContext.js +1 -1
- package/dist-cjs/tabs-next/TabsNextContext.js.map +1 -1
- package/dist-cjs/tabs-next/hooks/useCollection.js.map +1 -1
- package/dist-cjs/tabs-next/hooks/useOverflow.js +48 -5
- package/dist-cjs/tabs-next/hooks/useOverflow.js.map +1 -1
- package/dist-cjs/tabs-next/hooks/useRestoreActiveTab.js +93 -0
- package/dist-cjs/tabs-next/hooks/useRestoreActiveTab.js.map +1 -0
- package/dist-es/date-picker/DatePickerRangeInput.js +1 -0
- package/dist-es/date-picker/DatePickerRangeInput.js.map +1 -1
- package/dist-es/date-picker/DatePickerSingleInput.js +1 -0
- package/dist-es/date-picker/DatePickerSingleInput.js.map +1 -1
- package/dist-es/index.js +3 -0
- package/dist-es/index.js.map +1 -1
- package/dist-es/splitter/SplitHandle.css.js +4 -0
- package/dist-es/splitter/SplitHandle.css.js.map +1 -0
- package/dist-es/splitter/SplitHandle.js +58 -0
- package/dist-es/splitter/SplitHandle.js.map +1 -0
- package/dist-es/splitter/SplitPanel.css.js +4 -0
- package/dist-es/splitter/SplitPanel.css.js.map +1 -0
- package/dist-es/splitter/SplitPanel.js +35 -0
- package/dist-es/splitter/SplitPanel.js.map +1 -0
- package/dist-es/splitter/Splitter.js +27 -0
- package/dist-es/splitter/Splitter.js.map +1 -0
- package/dist-es/splitter/utils.js +15 -0
- package/dist-es/splitter/utils.js.map +1 -0
- package/dist-es/stepped-tracker/stepReducer.js +128 -82
- package/dist-es/stepped-tracker/stepReducer.js.map +1 -1
- package/dist-es/stepped-tracker/useStepReducer.js +7 -5
- package/dist-es/stepped-tracker/useStepReducer.js.map +1 -1
- package/dist-es/stepped-tracker/utils.js +43 -9
- package/dist-es/stepped-tracker/utils.js.map +1 -1
- package/dist-es/stepper-input/StepperInput.js +6 -5
- package/dist-es/stepper-input/StepperInput.js.map +1 -1
- package/dist-es/tabs-next/TabListNext.css.js +1 -1
- package/dist-es/tabs-next/TabListNext.js +11 -17
- package/dist-es/tabs-next/TabListNext.js.map +1 -1
- package/dist-es/tabs-next/TabOverflowList.js +1 -1
- package/dist-es/tabs-next/TabOverflowList.js.map +1 -1
- package/dist-es/tabs-next/TabsNext.js +5 -52
- package/dist-es/tabs-next/TabsNext.js.map +1 -1
- package/dist-es/tabs-next/TabsNextContext.js +1 -1
- package/dist-es/tabs-next/TabsNextContext.js.map +1 -1
- package/dist-es/tabs-next/hooks/useCollection.js.map +1 -1
- package/dist-es/tabs-next/hooks/useOverflow.js +49 -6
- package/dist-es/tabs-next/hooks/useOverflow.js.map +1 -1
- package/dist-es/tabs-next/hooks/useRestoreActiveTab.js +91 -0
- package/dist-es/tabs-next/hooks/useRestoreActiveTab.js.map +1 -0
- package/dist-types/index.d.ts +1 -0
- package/dist-types/splitter/SplitHandle.d.ts +21 -0
- package/dist-types/splitter/SplitPanel.d.ts +10 -0
- package/dist-types/splitter/Splitter.d.ts +22 -0
- package/dist-types/splitter/index.d.ts +4 -0
- package/dist-types/splitter/utils.d.ts +4 -0
- package/dist-types/stepped-tracker/Step.types.d.ts +2 -4
- package/dist-types/stepped-tracker/stepReducer.types.d.ts +6 -4
- package/dist-types/stepped-tracker/utils.d.ts +6 -3
- package/dist-types/tabs/drag-drop/drag-utils.d.ts +6 -6
- package/dist-types/tabs-next/TabsNextContext.d.ts +1 -1
- package/dist-types/tabs-next/hooks/useOverflow.d.ts +1 -1
- package/dist-types/tabs-next/hooks/useRestoreActiveTab.d.ts +10 -0
- package/package.json +5 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { forwardRef, useState, useRef, useCallback, useMemo } from 'react';
|
|
3
|
-
import { makePrefixer, useControlled,
|
|
3
|
+
import { makePrefixer, useControlled, useEventCallback } from '@salt-ds/core';
|
|
4
4
|
import { clsx } from 'clsx';
|
|
5
5
|
import { TabsNextContext } from './TabsNextContext.js';
|
|
6
6
|
import { useCollection } from './hooks/useCollection.js';
|
|
@@ -25,7 +25,7 @@ const TabsNext = forwardRef(
|
|
|
25
25
|
items
|
|
26
26
|
} = useCollection({ wrap: true });
|
|
27
27
|
const activeTab = useRef();
|
|
28
|
-
const
|
|
28
|
+
const removedActiveTabRef = useRef(void 0);
|
|
29
29
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
30
30
|
const [selected, setSelectedState] = useControlled({
|
|
31
31
|
controlled: value,
|
|
@@ -33,10 +33,6 @@ const TabsNext = forwardRef(
|
|
|
33
33
|
name: "TabListNext",
|
|
34
34
|
state: "selected"
|
|
35
35
|
});
|
|
36
|
-
const selectedRef = useRef(void 0);
|
|
37
|
-
useIsomorphicLayoutEffect(() => {
|
|
38
|
-
selectedRef.current = selected;
|
|
39
|
-
}, [selected]);
|
|
40
36
|
const setSelected = useCallback(
|
|
41
37
|
(event, value2) => {
|
|
42
38
|
setMenuOpen(false);
|
|
@@ -53,7 +49,7 @@ const TabsNext = forwardRef(
|
|
|
53
49
|
});
|
|
54
50
|
return () => {
|
|
55
51
|
var _a;
|
|
56
|
-
|
|
52
|
+
cleanup();
|
|
57
53
|
setValueToIdMap(({ map }) => {
|
|
58
54
|
map.delete(item2.value);
|
|
59
55
|
return { map };
|
|
@@ -61,50 +57,7 @@ const TabsNext = forwardRef(
|
|
|
61
57
|
if (((_a = activeTab.current) == null ? void 0 : _a.value) !== item2.value) {
|
|
62
58
|
return;
|
|
63
59
|
}
|
|
64
|
-
|
|
65
|
-
const containFocus = () => {
|
|
66
|
-
var _a2;
|
|
67
|
-
const activeIndex = items2.current.findIndex(
|
|
68
|
-
(i) => item2.value === i.value
|
|
69
|
-
);
|
|
70
|
-
const nextIndex = activeIndex === items2.current.length - 1 ? items2.current.length - 2 : activeIndex + 1;
|
|
71
|
-
const nextActive = items2.current[nextIndex];
|
|
72
|
-
returnFocus.current = nextActive.value;
|
|
73
|
-
if (selectedRef.current === item2.value) {
|
|
74
|
-
setSelected(null, nextActive.value);
|
|
75
|
-
}
|
|
76
|
-
(_a2 = nextActive == null ? void 0 : nextActive.element) == null ? void 0 : _a2.focus();
|
|
77
|
-
};
|
|
78
|
-
if (document.activeElement === document.body) {
|
|
79
|
-
requestAnimationFrame(() => {
|
|
80
|
-
if (document.activeElement === document.body) {
|
|
81
|
-
containFocus();
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
} else {
|
|
85
|
-
const handleFocusOut = (event) => {
|
|
86
|
-
if (!event.relatedTarget) {
|
|
87
|
-
requestAnimationFrame(() => {
|
|
88
|
-
if (document.activeElement === document.body) {
|
|
89
|
-
containFocus();
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
item2.element.ownerDocument.addEventListener(
|
|
95
|
-
"focusout",
|
|
96
|
-
handleFocusOut,
|
|
97
|
-
{
|
|
98
|
-
once: true
|
|
99
|
-
}
|
|
100
|
-
);
|
|
101
|
-
setTimeout(() => {
|
|
102
|
-
item2.element.ownerDocument.removeEventListener(
|
|
103
|
-
"focusout",
|
|
104
|
-
handleFocusOut
|
|
105
|
-
);
|
|
106
|
-
}, 1e3);
|
|
107
|
-
}
|
|
60
|
+
removedActiveTabRef.current = item2.value;
|
|
108
61
|
};
|
|
109
62
|
});
|
|
110
63
|
const registerPanel = useCallback((id, value2) => {
|
|
@@ -148,7 +101,7 @@ const TabsNext = forwardRef(
|
|
|
148
101
|
activeTab,
|
|
149
102
|
menuOpen,
|
|
150
103
|
setMenuOpen,
|
|
151
|
-
|
|
104
|
+
removedActiveTabRef
|
|
152
105
|
}),
|
|
153
106
|
[
|
|
154
107
|
registerPanel,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabsNext.js","sources":["../src/tabs-next/TabsNext.tsx"],"sourcesContent":["import {\n type ComponentPropsWithoutRef,\n type ReactNode,\n type SyntheticEvent,\n forwardRef,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport {\n makePrefixer,\n useControlled,\n useEventCallback,\n useIsomorphicLayoutEffect,\n} from \"@salt-ds/core\";\nimport { clsx } from \"clsx\";\nimport { type Item, TabsNextContext } from \"./TabsNextContext\";\nimport { useCollection } from \"./hooks/useCollection\";\n\nexport interface TabsNextProps\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"onChange\"> {\n children?: ReactNode;\n /**\n * The default value. Use when the component is not controlled.\n */\n defaultValue?: string;\n /**\n * The value. Use when the component is controlled.\n */\n value?: string;\n /**\n * Callback fired when the selection changes. The event will be null when selection is moved automatically.\n */\n onChange?: (event: SyntheticEvent | null, value: string) => void;\n}\n\nconst withBaseName = makePrefixer(\"saltTabsNext\");\n\nexport const TabsNext = forwardRef<HTMLDivElement, TabsNextProps>(\n function TabsNext(props, ref) {\n const { className, children, value, defaultValue, onChange, ...rest } =\n props;\n\n const [valueToTabIdMap, setValueToIdMap] = useState({\n map: new Map<string, string>(),\n });\n const [valueToPanelIdMap, setValueToPanelIdMap] = useState({\n map: new Map<string, string>(),\n });\n\n const {\n registerItem,\n item,\n getNext,\n getPrevious,\n getFirst,\n getLast,\n items,\n } = useCollection({ wrap: true });\n\n const activeTab = useRef<Pick<Item, \"id\" | \"value\">>();\n const returnFocus = useRef<string | undefined>(undefined);\n\n const [menuOpen, setMenuOpen] = useState(false);\n\n const [selected, setSelectedState] = useControlled({\n controlled: value,\n default: defaultValue,\n name: \"TabListNext\",\n state: \"selected\",\n });\n\n // This ref is needed so we can read the current selected item in the containFocus() function.\n const selectedRef = useRef<string | undefined>(undefined);\n useIsomorphicLayoutEffect(() => {\n selectedRef.current = selected;\n }, [selected]);\n\n const setSelected = useCallback(\n (event: SyntheticEvent | null, value: string) => {\n setMenuOpen(false);\n setSelectedState(value);\n onChange?.(event, value);\n },\n [onChange],\n );\n\n const registerTab = useEventCallback((item: Item) => {\n const cleanup = registerItem(item);\n setValueToIdMap(({ map }) => {\n map.set(item.value, item.id);\n return { map };\n });\n\n return () => {\n const items = cleanup();\n setValueToIdMap(({ map }) => {\n map.delete(item.value);\n return { map };\n });\n\n if (activeTab.current?.value !== item.value) {\n return;\n }\n\n returnFocus.current = item.value;\n\n const containFocus = () => {\n const activeIndex = items.current.findIndex(\n (i) => item.value === i.value,\n );\n\n const nextIndex =\n activeIndex === items.current.length - 1\n ? items.current.length - 2\n : activeIndex + 1;\n\n const nextActive = items.current[nextIndex];\n\n returnFocus.current = nextActive.value;\n\n if (selectedRef.current === item.value) {\n setSelected(null, nextActive.value);\n }\n\n nextActive?.element?.focus();\n };\n\n if (document.activeElement === document.body) {\n requestAnimationFrame(() => {\n if (document.activeElement === document.body) {\n containFocus();\n }\n });\n } else {\n const handleFocusOut = (event: FocusEvent) => {\n if (!event.relatedTarget) {\n requestAnimationFrame(() => {\n if (document.activeElement === document.body) {\n containFocus();\n }\n });\n }\n };\n\n item.element.ownerDocument.addEventListener(\n \"focusout\",\n handleFocusOut,\n {\n once: true,\n },\n );\n\n setTimeout(() => {\n item.element.ownerDocument.removeEventListener(\n \"focusout\",\n handleFocusOut,\n );\n }, 1000);\n }\n };\n });\n\n const registerPanel = useCallback((id: string, value: string) => {\n setValueToPanelIdMap(({ map }) => {\n map.set(value, id);\n return { map };\n });\n return () => {\n setValueToIdMap(({ map }) => {\n map.delete(value);\n return { map };\n });\n };\n }, []);\n\n const getPanelId = useCallback(\n (value: string) => {\n return valueToPanelIdMap.map.get(value);\n },\n [valueToPanelIdMap],\n );\n\n const getTabId = useCallback(\n (value: string) => {\n return valueToTabIdMap.map.get(value);\n },\n [valueToTabIdMap],\n );\n\n const context = useMemo(\n () => ({\n registerTab,\n registerPanel,\n getPanelId,\n getTabId,\n selected,\n setSelected,\n item,\n getNext,\n getPrevious,\n getFirst,\n getLast,\n items,\n activeTab,\n menuOpen,\n setMenuOpen,\n returnFocus,\n }),\n [\n registerPanel,\n registerTab,\n getPanelId,\n getTabId,\n selected,\n setSelected,\n item,\n getNext,\n getPrevious,\n getFirst,\n getLast,\n items,\n menuOpen,\n ],\n );\n\n return (\n <TabsNextContext.Provider value={context}>\n <div className={clsx(withBaseName(), className)} ref={ref} {...rest}>\n {children}\n </div>\n </TabsNextContext.Provider>\n );\n },\n);\n"],"names":["TabsNext","value","item","items","_a"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TabsNext.js","sources":["../src/tabs-next/TabsNext.tsx"],"sourcesContent":["import {\n type ComponentPropsWithoutRef,\n type ReactNode,\n type SyntheticEvent,\n forwardRef,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport { makePrefixer, useControlled, useEventCallback } from \"@salt-ds/core\";\nimport { clsx } from \"clsx\";\nimport { type Item, TabsNextContext } from \"./TabsNextContext\";\nimport { useCollection } from \"./hooks/useCollection\";\n\nexport interface TabsNextProps\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"onChange\"> {\n children?: ReactNode;\n /**\n * The default value. Use when the component is not controlled.\n */\n defaultValue?: string;\n /**\n * The value. Use when the component is controlled.\n */\n value?: string;\n /**\n * Callback fired when the selection changes. The event will be null when selection is moved automatically.\n */\n onChange?: (event: SyntheticEvent | null, value: string) => void;\n}\n\nconst withBaseName = makePrefixer(\"saltTabsNext\");\n\nexport const TabsNext = forwardRef<HTMLDivElement, TabsNextProps>(\n function TabsNext(props, ref) {\n const { className, children, value, defaultValue, onChange, ...rest } =\n props;\n\n const [valueToTabIdMap, setValueToIdMap] = useState({\n map: new Map<string, string>(),\n });\n const [valueToPanelIdMap, setValueToPanelIdMap] = useState({\n map: new Map<string, string>(),\n });\n\n const {\n registerItem,\n item,\n getNext,\n getPrevious,\n getFirst,\n getLast,\n items,\n } = useCollection({ wrap: true });\n\n const activeTab = useRef<Pick<Item, \"id\" | \"value\">>();\n const removedActiveTabRef = useRef<string | undefined>(undefined);\n\n const [menuOpen, setMenuOpen] = useState(false);\n\n const [selected, setSelectedState] = useControlled({\n controlled: value,\n default: defaultValue,\n name: \"TabListNext\",\n state: \"selected\",\n });\n\n const setSelected = useCallback(\n (event: SyntheticEvent | null, value: string) => {\n setMenuOpen(false);\n setSelectedState(value);\n onChange?.(event, value);\n },\n [onChange],\n );\n\n const registerTab = useEventCallback((item: Item) => {\n const cleanup = registerItem(item);\n setValueToIdMap(({ map }) => {\n map.set(item.value, item.id);\n return { map };\n });\n\n return () => {\n cleanup();\n setValueToIdMap(({ map }) => {\n map.delete(item.value);\n return { map };\n });\n\n if (activeTab.current?.value !== item.value) {\n return;\n }\n\n removedActiveTabRef.current = item.value;\n };\n });\n\n const registerPanel = useCallback((id: string, value: string) => {\n setValueToPanelIdMap(({ map }) => {\n map.set(value, id);\n return { map };\n });\n return () => {\n setValueToIdMap(({ map }) => {\n map.delete(value);\n return { map };\n });\n };\n }, []);\n\n const getPanelId = useCallback(\n (value: string) => {\n return valueToPanelIdMap.map.get(value);\n },\n [valueToPanelIdMap],\n );\n\n const getTabId = useCallback(\n (value: string) => {\n return valueToTabIdMap.map.get(value);\n },\n [valueToTabIdMap],\n );\n\n const context = useMemo(\n () => ({\n registerTab,\n registerPanel,\n getPanelId,\n getTabId,\n selected,\n setSelected,\n item,\n getNext,\n getPrevious,\n getFirst,\n getLast,\n items,\n activeTab,\n menuOpen,\n setMenuOpen,\n removedActiveTabRef,\n }),\n [\n registerPanel,\n registerTab,\n getPanelId,\n getTabId,\n selected,\n setSelected,\n item,\n getNext,\n getPrevious,\n getFirst,\n getLast,\n items,\n menuOpen,\n ],\n );\n\n return (\n <TabsNextContext.Provider value={context}>\n <div className={clsx(withBaseName(), className)} ref={ref} {...rest}>\n {children}\n </div>\n </TabsNextContext.Provider>\n );\n },\n);\n"],"names":["TabsNext","value","item"],"mappings":"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"}
|
|
@@ -17,7 +17,7 @@ const TabsNextContext = createContext(
|
|
|
17
17
|
getTabId: () => void 0,
|
|
18
18
|
setSelected: () => void 0,
|
|
19
19
|
activeTab: { current: void 0 },
|
|
20
|
-
|
|
20
|
+
removedActiveTabRef: { current: void 0 },
|
|
21
21
|
menuOpen: false,
|
|
22
22
|
setMenuOpen: () => void 0
|
|
23
23
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabsNextContext.js","sources":["../src/tabs-next/TabsNextContext.tsx"],"sourcesContent":["import { createContext } from \"@salt-ds/core\";\nimport {\n type Dispatch,\n type MutableRefObject,\n type SetStateAction,\n type SyntheticEvent,\n useContext,\n} from \"react\";\nimport type { useCollection } from \"./hooks/useCollection\";\n\nexport interface Item {\n id: string;\n value: string;\n element: HTMLElement;\n}\n\nexport interface TabsNextContextValue\n extends Omit<ReturnType<typeof useCollection>, \"registerItem\"> {\n registerTab: (item: Item) => () => void;\n registerPanel: (id: string, value: string) => () => void;\n getPanelId: (value: string) => string | undefined;\n getTabId: (value: string) => string | undefined;\n selected?: string;\n setSelected: (event: SyntheticEvent, value: string) => void;\n activeTab: MutableRefObject<Pick<Item, \"id\" | \"value\"> | undefined>;\n
|
|
1
|
+
{"version":3,"file":"TabsNextContext.js","sources":["../src/tabs-next/TabsNextContext.tsx"],"sourcesContent":["import { createContext } from \"@salt-ds/core\";\nimport {\n type Dispatch,\n type MutableRefObject,\n type SetStateAction,\n type SyntheticEvent,\n useContext,\n} from \"react\";\nimport type { useCollection } from \"./hooks/useCollection\";\n\nexport interface Item {\n id: string;\n value: string;\n element: HTMLElement;\n}\n\nexport interface TabsNextContextValue\n extends Omit<ReturnType<typeof useCollection>, \"registerItem\"> {\n registerTab: (item: Item) => () => void;\n registerPanel: (id: string, value: string) => () => void;\n getPanelId: (value: string) => string | undefined;\n getTabId: (value: string) => string | undefined;\n selected?: string;\n setSelected: (event: SyntheticEvent, value: string) => void;\n activeTab: MutableRefObject<Pick<Item, \"id\" | \"value\"> | undefined>;\n removedActiveTabRef: MutableRefObject<string | undefined>;\n menuOpen: boolean;\n setMenuOpen: Dispatch<SetStateAction<boolean>>;\n}\n\nexport const TabsNextContext = createContext<TabsNextContextValue>(\n \"TabsNextContext\",\n {\n getFirst: () => null,\n getLast: () => null,\n getNext: () => null,\n getPrevious: () => null,\n item: () => null,\n items: [],\n selected: undefined,\n registerTab: () => () => undefined,\n registerPanel: () => () => undefined,\n getPanelId: () => undefined,\n getTabId: () => undefined,\n setSelected: () => undefined,\n activeTab: { current: undefined },\n removedActiveTabRef: { current: undefined },\n menuOpen: false,\n setMenuOpen: () => undefined,\n },\n);\n\nexport function useTabsNext() {\n return useContext(TabsNextContext);\n}\n"],"names":[],"mappings":";;;AA8BO,MAAM,eAAkB,GAAA,aAAA;AAAA,EAC7B,iBAAA;AAAA,EACA;AAAA,IACE,UAAU,MAAM,IAAA;AAAA,IAChB,SAAS,MAAM,IAAA;AAAA,IACf,SAAS,MAAM,IAAA;AAAA,IACf,aAAa,MAAM,IAAA;AAAA,IACnB,MAAM,MAAM,IAAA;AAAA,IACZ,OAAO,EAAC;AAAA,IACR,QAAU,EAAA,KAAA,CAAA;AAAA,IACV,WAAA,EAAa,MAAM,MAAM,KAAA,CAAA;AAAA,IACzB,aAAA,EAAe,MAAM,MAAM,KAAA,CAAA;AAAA,IAC3B,YAAY,MAAM,KAAA,CAAA;AAAA,IAClB,UAAU,MAAM,KAAA,CAAA;AAAA,IAChB,aAAa,MAAM,KAAA,CAAA;AAAA,IACnB,SAAA,EAAW,EAAE,OAAA,EAAS,KAAU,CAAA,EAAA;AAAA,IAChC,mBAAA,EAAqB,EAAE,OAAA,EAAS,KAAU,CAAA,EAAA;AAAA,IAC1C,QAAU,EAAA,KAAA;AAAA,IACV,aAAa,MAAM,KAAA;AAAA;AAEvB;AAEO,SAAS,WAAc,GAAA;AAC5B,EAAA,OAAO,WAAW,eAAe,CAAA;AACnC;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCollection.js","sources":["../src/tabs-next/hooks/useCollection.
|
|
1
|
+
{"version":3,"file":"useCollection.js","sources":["../src/tabs-next/hooks/useCollection.ts"],"sourcesContent":["import { useCallback, useRef, useState } from \"react\";\n\nexport interface Item {\n id: string;\n element?: HTMLElement | null;\n value: string;\n}\n\nfunction sortBasedOnDOMPosition(items: Item[]): Item[] {\n const indexedItems = items.map((item, index) => [index, item] as const);\n let orderChanged = false;\n indexedItems.sort(([itemAIndex, itemA], [itemBIndex, itemB]) => {\n const itemAElement = itemA.element;\n const itemBElement = itemB.element;\n if (itemAElement === itemBElement) return 0;\n if (!itemAElement || !itemBElement) return 0;\n\n if (\n itemAElement.compareDocumentPosition(itemBElement) &\n Node.DOCUMENT_POSITION_FOLLOWING\n ) {\n if (itemAIndex > itemBIndex) {\n orderChanged = true;\n }\n return -1;\n }\n\n if (itemAIndex < itemBIndex) {\n orderChanged = true;\n }\n return 1;\n });\n\n if (orderChanged) {\n return indexedItems.map(([_, item]) => item);\n }\n return items;\n}\n\ninterface UseCollectionProps {\n wrap: boolean;\n}\n\nexport function useCollection({ wrap }: UseCollectionProps) {\n const [items, setItems] = useState<Item[]>([]);\n const itemsRef = useRef<Item[]>([]);\n const itemMap = useRef<Map<string, Item>>(new Map());\n\n const registerItem = useCallback((item: Item) => {\n setItems((old) => {\n const newItems = old.slice();\n const index = newItems.findIndex(({ id }) => id === item.id);\n if (index !== -1) {\n const newItem = { ...newItems[index], ...item };\n newItems[index] = newItem;\n itemMap.current.set(item.id, newItem);\n } else {\n newItems.push(item);\n itemMap.current.set(item.id, item);\n }\n const value = sortBasedOnDOMPosition(newItems);\n itemsRef.current = value;\n return value;\n });\n\n return () => {\n setItems((old) => {\n itemMap.current.delete(item.id);\n return old.filter(({ id }) => id !== item.id);\n });\n return itemsRef;\n };\n }, []);\n\n return {\n registerItem,\n item: (id?: string | null): Item | null => {\n if (!id) return null;\n let item = itemMap.current.get(id);\n if (!item) {\n item = items.find((item) => item.id === id);\n if (item) {\n itemMap.current.set(item.id, item);\n }\n }\n return item ?? null;\n },\n getNext: (current: string): Item | null => {\n const index = items.findIndex(({ id }) => id === current);\n\n const newIndex = wrap\n ? (index + 1) % items.length\n : Math.min(index + 1, items.length - 1);\n\n return items[newIndex] ?? null;\n },\n getPrevious: (current: string): Item | null => {\n const index = items.findIndex(({ id }) => id === current);\n\n const newIndex = wrap\n ? (index - 1 + items.length) % items.length\n : Math.max(index - 1, 0);\n\n return items[newIndex] ?? null;\n },\n getFirst: (): Item | null => {\n return items[0] ?? null;\n },\n getLast: (): Item | null => {\n return items[items.length - 1] ?? null;\n },\n items,\n };\n}\n"],"names":["item"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useValueEffect, useEventCallback, useIsomorphicLayoutEffect, ownerWindow } from '@salt-ds/core';
|
|
2
2
|
import { useWindow } from '@salt-ds/window';
|
|
3
|
-
import { useEffect, useMemo, Children } from 'react';
|
|
3
|
+
import { useRef, useEffect, useMemo, Children } from 'react';
|
|
4
4
|
|
|
5
5
|
function getTabWidth(element) {
|
|
6
6
|
const { width } = element.getBoundingClientRect();
|
|
@@ -18,6 +18,7 @@ function useOverflow({
|
|
|
18
18
|
isMeasuring: false
|
|
19
19
|
});
|
|
20
20
|
const targetWindow = useWindow();
|
|
21
|
+
const realSelectedIndex = useRef(-1);
|
|
21
22
|
const updateOverflow = useEventCallback(() => {
|
|
22
23
|
const computeVisible = (visibleCount2) => {
|
|
23
24
|
var _a;
|
|
@@ -69,7 +70,7 @@ function useOverflow({
|
|
|
69
70
|
newVisibleCount--;
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
|
-
return Math.max(
|
|
73
|
+
return Math.max(0, newVisibleCount);
|
|
73
74
|
}
|
|
74
75
|
return visibleCount2;
|
|
75
76
|
};
|
|
@@ -95,6 +96,15 @@ function useOverflow({
|
|
|
95
96
|
useIsomorphicLayoutEffect(() => {
|
|
96
97
|
updateOverflow();
|
|
97
98
|
}, [selected]);
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
const handleWindowResize = () => {
|
|
101
|
+
updateOverflow();
|
|
102
|
+
};
|
|
103
|
+
targetWindow == null ? void 0 : targetWindow.addEventListener("resize", handleWindowResize);
|
|
104
|
+
return () => {
|
|
105
|
+
targetWindow == null ? void 0 : targetWindow.removeEventListener("resize", handleWindowResize);
|
|
106
|
+
};
|
|
107
|
+
}, [updateOverflow, targetWindow]);
|
|
98
108
|
useEffect(() => {
|
|
99
109
|
const element = container == null ? void 0 : container.current;
|
|
100
110
|
if (!element) return;
|
|
@@ -115,9 +125,31 @@ function useOverflow({
|
|
|
115
125
|
}
|
|
116
126
|
};
|
|
117
127
|
}, [container, updateOverflow]);
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
const element = container == null ? void 0 : container.current;
|
|
130
|
+
if (!element || isMeasuring) return;
|
|
131
|
+
const win = ownerWindow(element);
|
|
132
|
+
const mutationObserver = new win.MutationObserver(() => {
|
|
133
|
+
requestAnimationFrame(() => {
|
|
134
|
+
updateOverflow();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
mutationObserver.observe(element, {
|
|
138
|
+
childList: true
|
|
139
|
+
});
|
|
140
|
+
return () => {
|
|
141
|
+
mutationObserver.disconnect();
|
|
142
|
+
};
|
|
143
|
+
}, [container, updateOverflow, isMeasuring]);
|
|
118
144
|
const childArray = useMemo(() => Children.toArray(children), [children]);
|
|
119
|
-
const visible =
|
|
120
|
-
|
|
145
|
+
const visible = useMemo(
|
|
146
|
+
() => childArray.slice(0, visibleCount),
|
|
147
|
+
[visibleCount, childArray]
|
|
148
|
+
);
|
|
149
|
+
const hidden = useMemo(
|
|
150
|
+
() => childArray.slice(visibleCount),
|
|
151
|
+
[childArray, visibleCount]
|
|
152
|
+
);
|
|
121
153
|
const hiddenSelectedIndex = hidden.findIndex(
|
|
122
154
|
// @ts-ignore
|
|
123
155
|
(child) => {
|
|
@@ -125,14 +157,25 @@ function useOverflow({
|
|
|
125
157
|
return ((_a = child == null ? void 0 : child.props) == null ? void 0 : _a.value) === selected;
|
|
126
158
|
}
|
|
127
159
|
);
|
|
160
|
+
useIsomorphicLayoutEffect(() => {
|
|
161
|
+
if (visibleCount === childArray.length) {
|
|
162
|
+
realSelectedIndex.current = childArray.findIndex(
|
|
163
|
+
// @ts-ignore
|
|
164
|
+
(child) => {
|
|
165
|
+
var _a;
|
|
166
|
+
return ((_a = child == null ? void 0 : child.props) == null ? void 0 : _a.value) === selected;
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}, [visibleCount, childArray, selected]);
|
|
128
171
|
if (selected && hiddenSelectedIndex !== -1) {
|
|
129
172
|
const removed = hidden.splice(hiddenSelectedIndex, 1);
|
|
130
173
|
visible.push(removed[0]);
|
|
131
174
|
}
|
|
132
175
|
if (isMeasuring) {
|
|
133
|
-
return [childArray, [], isMeasuring];
|
|
176
|
+
return [childArray, [], isMeasuring, realSelectedIndex];
|
|
134
177
|
}
|
|
135
|
-
return [visible, hidden, isMeasuring];
|
|
178
|
+
return [visible, hidden, isMeasuring, realSelectedIndex];
|
|
136
179
|
}
|
|
137
180
|
|
|
138
181
|
export { useOverflow };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useOverflow.js","sources":["../src/tabs-next/hooks/useOverflow.ts"],"sourcesContent":["import {\n ownerWindow,\n useEventCallback,\n useIsomorphicLayoutEffect,\n useValueEffect,\n} from \"@salt-ds/core\";\nimport { useWindow } from \"@salt-ds/window\";\nimport {\n Children,\n type ReactNode,\n type RefObject,\n useEffect,\n useMemo,\n} from \"react\";\nimport type { Item } from \"./useCollection\";\n\ninterface UseOverflowProps {\n container: RefObject<HTMLElement>;\n selected?: string;\n children: ReactNode;\n tabs: Item[];\n overflowButton: RefObject<HTMLButtonElement>;\n}\n\nfunction getTabWidth(element: HTMLElement) {\n const { width } = element.getBoundingClientRect();\n return Math.ceil(width);\n}\n\nexport function useOverflow({\n tabs,\n container,\n overflowButton,\n children,\n selected,\n}: UseOverflowProps) {\n const [{ visibleCount, isMeasuring }, setVisibleItems] = useValueEffect({\n visibleCount: tabs.length,\n isMeasuring: false,\n });\n const targetWindow = useWindow();\n\n const updateOverflow = useEventCallback(() => {\n const computeVisible = (visibleCount: number) => {\n if (container.current && targetWindow) {\n const items = Array.from(\n container.current.querySelectorAll<HTMLElement>(\n \"[data-overflowitem]\",\n ),\n );\n const selectedTab = container.current.querySelector<HTMLElement>(\n \"[role=tab][aria-selected=true]\",\n )?.parentElement;\n\n let maxWidth = container.current.clientWidth ?? 0;\n\n const containerStyles = targetWindow.getComputedStyle(\n container.current,\n );\n const gap = Number.parseInt(containerStyles.gap || \"0\");\n\n let currentWidth = 0;\n let newVisibleCount = 0;\n\n const visible = [];\n\n while (newVisibleCount < items.length) {\n const element = items[newVisibleCount];\n if (element) {\n if (currentWidth + getTabWidth(element) + gap > maxWidth) {\n break;\n }\n currentWidth += getTabWidth(element) + gap;\n visible.push(element);\n }\n newVisibleCount++;\n }\n\n if (newVisibleCount >= items.length) {\n return newVisibleCount;\n }\n\n const overflowButtonWidth = overflowButton.current\n ? overflowButton.current.offsetWidth + gap\n : 0;\n maxWidth -= overflowButtonWidth;\n\n while (currentWidth > maxWidth) {\n const removed = visible.pop();\n if (!removed) break;\n currentWidth -= getTabWidth(removed) + gap;\n newVisibleCount--;\n }\n\n if (selectedTab && !visible.includes(selectedTab)) {\n const selectedTabWidth = getTabWidth(selectedTab) + gap;\n while (currentWidth + selectedTabWidth > maxWidth) {\n const removed = visible.pop();\n if (!removed) break;\n currentWidth -= getTabWidth(selectedTab) + gap;\n newVisibleCount--;\n }\n }\n\n return Math.max(1, newVisibleCount);\n }\n return visibleCount;\n };\n\n setVisibleItems(function* () {\n // Show all\n yield {\n visibleCount: tabs.length,\n isMeasuring: true,\n };\n\n // Measure the visible count\n const newVisibleCount = computeVisible(tabs.length);\n const isMeasuring = newVisibleCount < tabs.length && newVisibleCount > 0;\n yield {\n visibleCount: newVisibleCount,\n isMeasuring,\n };\n\n // ensure the visible count is correct\n if (isMeasuring) {\n yield {\n visibleCount: computeVisible(newVisibleCount),\n isMeasuring: false,\n };\n }\n });\n });\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: we want to update when selected changes.\n useIsomorphicLayoutEffect(() => {\n updateOverflow();\n }, [selected]);\n\n useEffect(() => {\n const element = container?.current;\n if (!element) return;\n\n const win = ownerWindow(element);\n\n const resizeObserver = new win.ResizeObserver((entries) => {\n requestAnimationFrame(() => {\n if (entries.length === 0) return;\n\n updateOverflow();\n });\n });\n resizeObserver.observe(element);\n if (element.parentElement) {\n resizeObserver.observe(element.parentElement);\n }\n\n return () => {\n if (element) {\n resizeObserver.unobserve(element);\n }\n };\n }, [container, updateOverflow]);\n\n const childArray = useMemo(() => Children.toArray(children), [children]);\n const visible = childArray.slice(0, visibleCount || 1);\n const hidden = childArray.slice(visibleCount || 1);\n\n const hiddenSelectedIndex = hidden.findIndex(\n // @ts-ignore\n (child) => child?.props?.value === selected,\n );\n\n if (selected && hiddenSelectedIndex !== -1) {\n const removed = hidden.splice(hiddenSelectedIndex, 1);\n visible.push(removed[0]);\n }\n\n if (isMeasuring) {\n return [childArray, [], isMeasuring] as const;\n }\n\n return [visible, hidden, isMeasuring] as const;\n}\n"],"names":["visibleCount","visible","isMeasuring"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useOverflow.js","sources":["../src/tabs-next/hooks/useOverflow.ts"],"sourcesContent":["import {\n ownerWindow,\n useEventCallback,\n useIsomorphicLayoutEffect,\n useValueEffect,\n} from \"@salt-ds/core\";\nimport { useWindow } from \"@salt-ds/window\";\nimport {\n Children,\n type ReactNode,\n type RefObject,\n useEffect,\n useMemo,\n useRef,\n} from \"react\";\nimport type { Item } from \"./useCollection\";\n\ninterface UseOverflowProps {\n container: RefObject<HTMLElement>;\n selected?: string;\n children: ReactNode;\n tabs: Item[];\n overflowButton: RefObject<HTMLButtonElement>;\n}\n\nfunction getTabWidth(element: HTMLElement) {\n const { width } = element.getBoundingClientRect();\n return Math.ceil(width);\n}\n\nexport function useOverflow({\n tabs,\n container,\n overflowButton,\n children,\n selected,\n}: UseOverflowProps) {\n /**\n * `visibleCount` doesn't include newly selected tab from overflow menu, which is removed in `computeVisible`\n */\n const [{ visibleCount, isMeasuring }, setVisibleItems] = useValueEffect({\n visibleCount: tabs.length,\n isMeasuring: false,\n });\n const targetWindow = useWindow();\n const realSelectedIndex = useRef<number>(-1);\n\n const updateOverflow = useEventCallback(() => {\n const computeVisible = (visibleCount: number) => {\n if (container.current && targetWindow) {\n const items = Array.from(\n container.current.querySelectorAll<HTMLElement>(\n \"[data-overflowitem]\",\n ),\n );\n const selectedTab = container.current.querySelector<HTMLElement>(\n \"[role=tab][aria-selected=true]\",\n )?.parentElement;\n\n let maxWidth = container.current.clientWidth ?? 0;\n\n const containerStyles = targetWindow.getComputedStyle(\n container.current,\n );\n const gap = Number.parseInt(containerStyles.gap || \"0\");\n\n let currentWidth = 0;\n let newVisibleCount = 0;\n\n const visible = [];\n\n while (newVisibleCount < items.length) {\n const element = items[newVisibleCount];\n if (element) {\n if (currentWidth + getTabWidth(element) + gap > maxWidth) {\n break;\n }\n currentWidth += getTabWidth(element) + gap;\n visible.push(element);\n }\n newVisibleCount++;\n }\n\n if (newVisibleCount >= items.length) {\n return newVisibleCount;\n }\n\n const overflowButtonWidth = overflowButton.current\n ? overflowButton.current.offsetWidth + gap\n : 0;\n maxWidth -= overflowButtonWidth;\n\n while (currentWidth > maxWidth) {\n const removed = visible.pop();\n if (!removed) break;\n currentWidth -= getTabWidth(removed) + gap;\n newVisibleCount--;\n }\n\n if (selectedTab && !visible.includes(selectedTab)) {\n const selectedTabWidth = getTabWidth(selectedTab) + gap;\n while (currentWidth + selectedTabWidth > maxWidth) {\n const removed = visible.pop();\n if (!removed) break;\n currentWidth -= getTabWidth(selectedTab) + gap;\n newVisibleCount--;\n }\n }\n\n // minimal count should be 0, if there is no space for any tab apart from selected tab from the overflow menu\n return Math.max(0, newVisibleCount);\n }\n return visibleCount;\n };\n\n setVisibleItems(function* () {\n // Show all\n yield {\n visibleCount: tabs.length,\n isMeasuring: true,\n };\n\n // Measure the visible count\n const newVisibleCount = computeVisible(tabs.length);\n const isMeasuring = newVisibleCount < tabs.length && newVisibleCount > 0;\n yield {\n visibleCount: newVisibleCount,\n isMeasuring,\n };\n\n // ensure the visible count is correct\n if (isMeasuring) {\n yield {\n visibleCount: computeVisible(newVisibleCount),\n isMeasuring: false,\n };\n }\n });\n });\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: we want to update when selected changes.\n useIsomorphicLayoutEffect(() => {\n updateOverflow();\n }, [selected]);\n\n useEffect(() => {\n const handleWindowResize = () => {\n updateOverflow();\n };\n\n targetWindow?.addEventListener(\"resize\", handleWindowResize);\n\n return () => {\n targetWindow?.removeEventListener(\"resize\", handleWindowResize);\n };\n }, [updateOverflow, targetWindow]);\n\n useEffect(() => {\n const element = container?.current;\n if (!element) return;\n\n const win = ownerWindow(element);\n\n const resizeObserver = new win.ResizeObserver((entries) => {\n requestAnimationFrame(() => {\n if (entries.length === 0) return;\n\n updateOverflow();\n });\n });\n resizeObserver.observe(element);\n if (element.parentElement) {\n resizeObserver.observe(element.parentElement);\n }\n\n return () => {\n if (element) {\n resizeObserver.unobserve(element);\n }\n };\n }, [container, updateOverflow]);\n\n useEffect(() => {\n const element = container?.current;\n if (!element || isMeasuring) return;\n\n const win = ownerWindow(element);\n\n const mutationObserver = new win.MutationObserver(() => {\n requestAnimationFrame(() => {\n updateOverflow();\n });\n });\n\n mutationObserver.observe(element, {\n childList: true,\n });\n\n return () => {\n mutationObserver.disconnect();\n };\n }, [container, updateOverflow, isMeasuring]);\n\n const childArray = useMemo(() => Children.toArray(children), [children]);\n const visible = useMemo(\n () => childArray.slice(0, visibleCount),\n [visibleCount, childArray],\n );\n const hidden = useMemo(\n () => childArray.slice(visibleCount),\n [childArray, visibleCount],\n );\n\n const hiddenSelectedIndex = hidden.findIndex(\n // @ts-ignore\n (child) => child?.props?.value === selected,\n );\n\n useIsomorphicLayoutEffect(() => {\n if (visibleCount === childArray.length) {\n realSelectedIndex.current = childArray.findIndex(\n // @ts-ignore\n (child) => child?.props?.value === selected,\n );\n }\n }, [visibleCount, childArray, selected]);\n\n if (selected && hiddenSelectedIndex !== -1) {\n const removed = hidden.splice(hiddenSelectedIndex, 1);\n visible.push(removed[0]);\n }\n\n if (isMeasuring) {\n return [childArray, [], isMeasuring, realSelectedIndex] as const;\n }\n\n return [visible, hidden, isMeasuring, realSelectedIndex] as const;\n}\n"],"names":["visibleCount","visible","isMeasuring"],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { useIsomorphicLayoutEffect, ownerWindow } from '@salt-ds/core';
|
|
2
|
+
import { useRef, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
function useRestoreActiveTab({
|
|
5
|
+
container,
|
|
6
|
+
tabs,
|
|
7
|
+
realSelectedIndex,
|
|
8
|
+
removedActiveTabRef
|
|
9
|
+
}) {
|
|
10
|
+
const tabsRef = useRef(tabs);
|
|
11
|
+
const previousTabsRef = useRef(tabs);
|
|
12
|
+
useIsomorphicLayoutEffect(() => {
|
|
13
|
+
tabsRef.current = tabs;
|
|
14
|
+
return () => {
|
|
15
|
+
previousTabsRef.current = tabs;
|
|
16
|
+
};
|
|
17
|
+
}, [tabs]);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!container.current) return;
|
|
20
|
+
const win = ownerWindow(container.current);
|
|
21
|
+
const mutationObserver = new win.MutationObserver((mutations) => {
|
|
22
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
23
|
+
for (const mutation of mutations) {
|
|
24
|
+
const removedItem = mutation.removedNodes[0] instanceof HTMLElement ? mutation.removedNodes[0] : null;
|
|
25
|
+
const removedItemWasTab = removedActiveTabRef.current && ((_a = removedItem == null ? void 0 : removedItem.dataset) == null ? void 0 : _a.overflowitem);
|
|
26
|
+
const activeTabWasRemoved = removedItemWasTab && !tabsRef.current.find(
|
|
27
|
+
({ value }) => value === removedActiveTabRef.current
|
|
28
|
+
);
|
|
29
|
+
if (activeTabWasRemoved) {
|
|
30
|
+
const removedTab = removedItem == null ? void 0 : removedItem.querySelector('[role="tab"]');
|
|
31
|
+
let nextTab = null;
|
|
32
|
+
if ((removedTab == null ? void 0 : removedTab.ariaSelected) === "true" && realSelectedIndex.current != null && realSelectedIndex.current >= 0) {
|
|
33
|
+
nextTab = (_b = tabsRef.current[Math.min(realSelectedIndex.current, tabsRef.current.length - 1)]) == null ? void 0 : _b.element;
|
|
34
|
+
}
|
|
35
|
+
if (!nextTab) {
|
|
36
|
+
const previousTab = mutation.previousSibling instanceof HTMLElement ? mutation.previousSibling.querySelector(
|
|
37
|
+
'[role="tab"]'
|
|
38
|
+
) : null;
|
|
39
|
+
if (!previousTab) {
|
|
40
|
+
nextTab = mutation.nextSibling instanceof HTMLElement ? (_c = mutation.nextSibling) == null ? void 0 : _c.querySelector(
|
|
41
|
+
'[role="tab"]'
|
|
42
|
+
) : null;
|
|
43
|
+
} else {
|
|
44
|
+
const nextTabIndex = previousTab ? tabsRef.current.findIndex(
|
|
45
|
+
({ element }) => element === previousTab
|
|
46
|
+
) + 1 : -1;
|
|
47
|
+
nextTab = (_d = tabsRef.current[Math.min(nextTabIndex, tabsRef.current.length - 1)]) == null ? void 0 : _d.element;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (!((_e = container.current) == null ? void 0 : _e.querySelector(
|
|
51
|
+
'[role="tab"][aria-selected="true"]'
|
|
52
|
+
))) {
|
|
53
|
+
nextTab == null ? void 0 : nextTab.click();
|
|
54
|
+
}
|
|
55
|
+
if (!((_f = container.current) == null ? void 0 : _f.contains(win.document.activeElement))) {
|
|
56
|
+
if (nextTab == null ? void 0 : nextTab.isConnected) {
|
|
57
|
+
nextTab == null ? void 0 : nextTab.focus({ preventScroll: true });
|
|
58
|
+
} else {
|
|
59
|
+
(_h = (_g = container.current) == null ? void 0 : _g.querySelector(
|
|
60
|
+
'[role="tab"][aria-selected="true"]'
|
|
61
|
+
)) == null ? void 0 : _h.focus({ preventScroll: true });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
removedActiveTabRef.current = void 0;
|
|
65
|
+
}
|
|
66
|
+
if (removedActiveTabRef.current) {
|
|
67
|
+
if (removedItemWasTab) {
|
|
68
|
+
const tabElement = (_i = tabsRef.current.find(
|
|
69
|
+
({ value }) => value === removedActiveTabRef.current
|
|
70
|
+
)) == null ? void 0 : _i.element;
|
|
71
|
+
if (win.document.activeElement === win.document.body) {
|
|
72
|
+
tabElement == null ? void 0 : tabElement.focus();
|
|
73
|
+
}
|
|
74
|
+
} else if (removedItem == null ? void 0 : removedItem.dataset.overflow) {
|
|
75
|
+
(_k = (_j = tabsRef.current[tabsRef.current.length - 1]) == null ? void 0 : _j.element) == null ? void 0 : _k.focus();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
mutationObserver.observe(container.current, {
|
|
81
|
+
childList: true,
|
|
82
|
+
subtree: true
|
|
83
|
+
});
|
|
84
|
+
return () => {
|
|
85
|
+
mutationObserver.disconnect();
|
|
86
|
+
};
|
|
87
|
+
}, [container, realSelectedIndex, removedActiveTabRef]);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export { useRestoreActiveTab };
|
|
91
|
+
//# sourceMappingURL=useRestoreActiveTab.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRestoreActiveTab.js","sources":["../src/tabs-next/hooks/useRestoreActiveTab.ts"],"sourcesContent":["import { ownerWindow, useIsomorphicLayoutEffect } from \"@salt-ds/core\";\nimport {\n type MutableRefObject,\n type RefObject,\n useEffect,\n useRef,\n} from \"react\";\nimport type { Item } from \"./useCollection\";\n\ninterface UseHandleRemovalProps {\n container: RefObject<HTMLElement>;\n tabs: Item[];\n realSelectedIndex: RefObject<number>;\n removedActiveTabRef: MutableRefObject<string | undefined>;\n}\n\nexport function useRestoreActiveTab({\n container,\n tabs,\n realSelectedIndex,\n removedActiveTabRef,\n}: UseHandleRemovalProps) {\n const tabsRef = useRef(tabs);\n const previousTabsRef = useRef(tabs);\n\n useIsomorphicLayoutEffect(() => {\n tabsRef.current = tabs;\n\n return () => {\n previousTabsRef.current = tabs;\n };\n }, [tabs]);\n\n useEffect(() => {\n if (!container.current) return;\n\n const win = ownerWindow(container.current);\n\n const mutationObserver = new win.MutationObserver((mutations) => {\n for (const mutation of mutations) {\n const removedItem =\n mutation.removedNodes[0] instanceof HTMLElement\n ? mutation.removedNodes[0]\n : null;\n\n const removedItemWasTab =\n removedActiveTabRef.current && removedItem?.dataset?.overflowitem;\n\n const activeTabWasRemoved =\n removedItemWasTab &&\n !tabsRef.current.find(\n ({ value }) => value === removedActiveTabRef.current,\n );\n\n if (activeTabWasRemoved) {\n const removedTab =\n removedItem?.querySelector<HTMLElement>('[role=\"tab\"]');\n\n let nextTab: HTMLElement | null | undefined = null;\n\n if (\n removedTab?.ariaSelected === \"true\" &&\n realSelectedIndex.current != null &&\n realSelectedIndex.current >= 0\n ) {\n nextTab =\n tabsRef.current[\n Math.min(realSelectedIndex.current, tabsRef.current.length - 1)\n ]?.element;\n }\n\n if (!nextTab) {\n const previousTab =\n mutation.previousSibling instanceof HTMLElement\n ? mutation.previousSibling.querySelector<HTMLElement>(\n '[role=\"tab\"]',\n )\n : null;\n\n if (!previousTab) {\n nextTab =\n mutation.nextSibling instanceof HTMLElement\n ? mutation.nextSibling?.querySelector<HTMLElement>(\n '[role=\"tab\"]',\n )\n : null;\n } else {\n const nextTabIndex = previousTab\n ? tabsRef.current.findIndex(\n ({ element }) => element === previousTab,\n ) + 1\n : -1;\n\n nextTab =\n tabsRef.current[\n Math.min(nextTabIndex, tabsRef.current.length - 1)\n ]?.element;\n }\n }\n\n if (\n !container.current?.querySelector<HTMLElement>(\n '[role=\"tab\"][aria-selected=\"true\"]',\n )\n ) {\n nextTab?.click();\n }\n\n if (!container.current?.contains(win.document.activeElement)) {\n if (nextTab?.isConnected) {\n nextTab?.focus({ preventScroll: true });\n } else {\n container.current\n ?.querySelector<HTMLElement>(\n '[role=\"tab\"][aria-selected=\"true\"]',\n )\n ?.focus({ preventScroll: true });\n }\n }\n\n removedActiveTabRef.current = undefined;\n }\n\n // Focus the tab if it was moved from the overflow menu into the visible tabs\n if (removedActiveTabRef.current) {\n if (removedItemWasTab) {\n const tabElement = tabsRef.current.find(\n ({ value }) => value === removedActiveTabRef.current,\n )?.element;\n\n if (win.document.activeElement === win.document.body) {\n tabElement?.focus();\n }\n } else if (removedItem?.dataset.overflow) {\n tabsRef.current[tabsRef.current.length - 1]?.element?.focus();\n }\n }\n }\n });\n\n mutationObserver.observe(container.current, {\n childList: true,\n subtree: true,\n });\n\n return () => {\n mutationObserver.disconnect();\n };\n }, [container, realSelectedIndex, removedActiveTabRef]);\n}\n"],"names":[],"mappings":"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"}
|
package/dist-types/index.d.ts
CHANGED
|
@@ -37,6 +37,7 @@ export * from "./query-input";
|
|
|
37
37
|
export * from "./responsive";
|
|
38
38
|
export * from "./search-input";
|
|
39
39
|
export * from "./slider";
|
|
40
|
+
export * from "./splitter";
|
|
40
41
|
export * from "./static-list";
|
|
41
42
|
export * from "./stepped-tracker";
|
|
42
43
|
export * from "./stepper-input";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type PanelResizeHandleProps } from "react-resizable-panels";
|
|
2
|
+
export declare type SplitHandleVariant = "primary" | "secondary" | "tertiary" | "transparent";
|
|
3
|
+
export declare type SplitHandleBorder = "top" | "bottom" | "right" | "left" | "top-bottom" | "left-right" | "none";
|
|
4
|
+
export interface SplitHandleProps extends PanelResizeHandleProps {
|
|
5
|
+
/**
|
|
6
|
+
* Styling variant
|
|
7
|
+
* @default "primary"
|
|
8
|
+
*/
|
|
9
|
+
variant?: SplitHandleVariant;
|
|
10
|
+
/**
|
|
11
|
+
* Change which sides get a border displayed
|
|
12
|
+
*
|
|
13
|
+
* Default is based on the orientation and appearance
|
|
14
|
+
* set on the parent Stepper components, ex.
|
|
15
|
+
* bordered + vertical = left-right
|
|
16
|
+
* bordered + horizontal = top-bottom
|
|
17
|
+
* transparent = none
|
|
18
|
+
*/
|
|
19
|
+
border?: SplitHandleBorder;
|
|
20
|
+
}
|
|
21
|
+
export declare function SplitHandle({ variant: variantProp, border: borderProp, hitAreaMargins, className, ...props }: SplitHandleProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ImperativePanelHandle, type PanelProps } from "react-resizable-panels";
|
|
2
|
+
export declare type SplitPanelVariant = "primary" | "secondary" | "tertiary";
|
|
3
|
+
export interface SplitPanelProps extends PanelProps {
|
|
4
|
+
/**
|
|
5
|
+
* Styling variant
|
|
6
|
+
* @default "primary"
|
|
7
|
+
*/
|
|
8
|
+
variant?: SplitPanelVariant;
|
|
9
|
+
}
|
|
10
|
+
export declare const SplitPanel: import("react").ForwardRefExoticComponent<SplitPanelProps & import("react").RefAttributes<ImperativePanelHandle>>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type PanelGroupProps } from "react-resizable-panels";
|
|
3
|
+
export declare type SplitterAppearance = "bordered" | "transparent";
|
|
4
|
+
export declare type SplitterOrientation = "horizontal" | "vertical";
|
|
5
|
+
export declare const OrientationContext: import("react").Context<SplitterOrientation>;
|
|
6
|
+
export declare const AppearanceContext: import("react").Context<SplitterAppearance>;
|
|
7
|
+
export interface SplitterProps extends Omit<PanelGroupProps, "direction"> {
|
|
8
|
+
/**
|
|
9
|
+
* The orientation of the splitter.
|
|
10
|
+
* Replaces `PanelGroupProps["direction"]`
|
|
11
|
+
*/
|
|
12
|
+
orientation: SplitterOrientation;
|
|
13
|
+
/**
|
|
14
|
+
* The appearance of the splitter.
|
|
15
|
+
* If set to "transparent", the splitter handle will
|
|
16
|
+
* be transparent, hence the background will be visible.
|
|
17
|
+
* @default "bordered"
|
|
18
|
+
*/
|
|
19
|
+
appearance?: SplitterAppearance;
|
|
20
|
+
children: ReactNode;
|
|
21
|
+
}
|
|
22
|
+
export declare function Splitter({ orientation, appearance: appearanceProp, ...props }: SplitterProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SplitHandleBorder } from "./SplitHandle";
|
|
2
|
+
import type { SplitterAppearance, SplitterOrientation } from "./Splitter";
|
|
3
|
+
export declare function computeAccent(appearance: SplitterAppearance, orientation: SplitterOrientation): SplitHandleBorder;
|
|
4
|
+
export declare function computeVariant(appearance: SplitterAppearance): "primary" | "transparent";
|
|
@@ -11,11 +11,9 @@ export interface StepProps extends Omit<ComponentPropsWithoutRef<"li">, "onToggl
|
|
|
11
11
|
substeps?: StepRecord[];
|
|
12
12
|
children?: ReactNode;
|
|
13
13
|
}
|
|
14
|
-
export declare type StepRecord =
|
|
14
|
+
export declare type StepRecord = Omit<StepProps, "children"> & {
|
|
15
15
|
id: string;
|
|
16
|
-
}
|
|
17
|
-
key: string;
|
|
18
|
-
});
|
|
16
|
+
};
|
|
19
17
|
export declare type StepStatus = "warning" | "error";
|
|
20
18
|
export declare type StepStage = "pending" | "locked" | "completed" | "inprogress" | "active";
|
|
21
19
|
export declare type StepDepth = number;
|
|
@@ -2,10 +2,10 @@ import type { StepRecord } from ".";
|
|
|
2
2
|
export interface StepReducerState {
|
|
3
3
|
steps: StepRecord[];
|
|
4
4
|
flatSteps: StepRecord[];
|
|
5
|
+
activeStepIndex: number;
|
|
5
6
|
activeStep: StepRecord | null;
|
|
6
7
|
previousStep: StepRecord | null;
|
|
7
8
|
nextStep: StepRecord | null;
|
|
8
|
-
activeStepIndex: number;
|
|
9
9
|
started: boolean;
|
|
10
10
|
ended: boolean;
|
|
11
11
|
}
|
|
@@ -14,11 +14,13 @@ export declare type StepReducerAction = {
|
|
|
14
14
|
} | {
|
|
15
15
|
type: "previous";
|
|
16
16
|
} | {
|
|
17
|
-
type: "
|
|
17
|
+
type: "reset";
|
|
18
|
+
} | {
|
|
19
|
+
type: "status/error";
|
|
18
20
|
} | {
|
|
19
|
-
type: "warning";
|
|
21
|
+
type: "status/warning";
|
|
20
22
|
} | {
|
|
21
|
-
type: "clear";
|
|
23
|
+
type: "status/clear";
|
|
22
24
|
};
|
|
23
25
|
export interface StepReducerOptions {
|
|
24
26
|
activeStepId?: string;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import type { StepRecord, StepStage } from "./Step.types";
|
|
1
|
+
import type { StepRecord, StepStage, StepStatus } from "./Step.types";
|
|
2
2
|
import type { StepReducerOptions, StepReducerState } from "./stepReducer.types";
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function
|
|
3
|
+
export declare function assignStepsStage(steps: StepRecord[], stage?: StepStage): StepRecord[];
|
|
4
|
+
export declare function assignStepStatus(steps: StepRecord[], stepId: string, status: StepStatus | undefined): StepRecord[];
|
|
5
|
+
export declare function resetSteps(steps: StepRecord[], options?: {
|
|
6
|
+
resetStatus: boolean;
|
|
7
|
+
}): StepRecord[];
|
|
5
8
|
export declare function autoStageSteps(steps: StepRecord[], options?: StepReducerOptions): StepRecord[];
|
|
6
9
|
export declare function flattenSteps(steps: StepRecord[]): StepRecord[];
|
|
7
10
|
export declare function initStepReducerState(initialSteps: StepRecord[], options?: StepReducerOptions): StepReducerState;
|