@salt-ds/core 1.59.1 → 1.61.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/css/salt-core.css +366 -1
  3. package/dist-cjs/aria-announcer/AriaAnnounce.js +15 -3
  4. package/dist-cjs/aria-announcer/AriaAnnounce.js.map +1 -1
  5. package/dist-cjs/aria-announcer/AriaAnnouncerContext.js.map +1 -1
  6. package/dist-cjs/aria-announcer/AriaAnnouncerProvider.js +65 -43
  7. package/dist-cjs/aria-announcer/AriaAnnouncerProvider.js.map +1 -1
  8. package/dist-cjs/aria-announcer/announcementRegistry.js +31 -0
  9. package/dist-cjs/aria-announcer/announcementRegistry.js.map +1 -0
  10. package/dist-cjs/aria-announcer/useAriaAnnouncer.js +44 -16
  11. package/dist-cjs/aria-announcer/useAriaAnnouncer.js.map +1 -1
  12. package/dist-cjs/index.js +17 -1
  13. package/dist-cjs/index.js.map +1 -1
  14. package/dist-cjs/navigation-item/NavigationItem.js +2 -0
  15. package/dist-cjs/navigation-item/NavigationItem.js.map +1 -1
  16. package/dist-cjs/pagination/Pagination.js +1 -0
  17. package/dist-cjs/pagination/Pagination.js.map +1 -1
  18. package/dist-cjs/rating/Rating.css.js +6 -0
  19. package/dist-cjs/rating/Rating.css.js.map +1 -0
  20. package/dist-cjs/rating/Rating.js +140 -0
  21. package/dist-cjs/rating/Rating.js.map +1 -0
  22. package/dist-cjs/rating/RatingItem.css.js +6 -0
  23. package/dist-cjs/rating/RatingItem.css.js.map +1 -0
  24. package/dist-cjs/rating/RatingItem.js +75 -0
  25. package/dist-cjs/rating/RatingItem.js.map +1 -0
  26. package/dist-cjs/salt-provider/SaltProvider.js +3 -1
  27. package/dist-cjs/salt-provider/SaltProvider.js.map +1 -1
  28. package/dist-cjs/semantic-icon-provider/SemanticIconProvider.js +22 -20
  29. package/dist-cjs/semantic-icon-provider/SemanticIconProvider.js.map +1 -1
  30. package/dist-cjs/spinner/Spinner.js +1 -0
  31. package/dist-cjs/spinner/Spinner.js.map +1 -1
  32. package/dist-cjs/tabs/Tab.css.js +6 -0
  33. package/dist-cjs/tabs/Tab.css.js.map +1 -0
  34. package/dist-cjs/tabs/Tab.js +211 -0
  35. package/dist-cjs/tabs/Tab.js.map +1 -0
  36. package/dist-cjs/tabs/TabAction.js +63 -0
  37. package/dist-cjs/tabs/TabAction.js.map +1 -0
  38. package/dist-cjs/tabs/TabBar.css.js +6 -0
  39. package/dist-cjs/tabs/TabBar.css.js.map +1 -0
  40. package/dist-cjs/tabs/TabBar.js +45 -0
  41. package/dist-cjs/tabs/TabBar.js.map +1 -0
  42. package/dist-cjs/tabs/TabList.css.js +6 -0
  43. package/dist-cjs/tabs/TabList.css.js.map +1 -0
  44. package/dist-cjs/tabs/TabList.js +281 -0
  45. package/dist-cjs/tabs/TabList.js.map +1 -0
  46. package/dist-cjs/tabs/TabPanel.css.js +6 -0
  47. package/dist-cjs/tabs/TabPanel.css.js.map +1 -0
  48. package/dist-cjs/tabs/TabPanel.js +98 -0
  49. package/dist-cjs/tabs/TabPanel.js.map +1 -0
  50. package/dist-cjs/tabs/TabTrigger.css.js +6 -0
  51. package/dist-cjs/tabs/TabTrigger.css.js.map +1 -0
  52. package/dist-cjs/tabs/TabTrigger.js +188 -0
  53. package/dist-cjs/tabs/TabTrigger.js.map +1 -0
  54. package/dist-cjs/tabs/Tabs.css.js +6 -0
  55. package/dist-cjs/tabs/Tabs.css.js.map +1 -0
  56. package/dist-cjs/tabs/Tabs.js +200 -0
  57. package/dist-cjs/tabs/Tabs.js.map +1 -0
  58. package/dist-cjs/tabs/internal/contexts/TabContext.js +26 -0
  59. package/dist-cjs/tabs/internal/contexts/TabContext.js.map +1 -0
  60. package/dist-cjs/tabs/internal/contexts/TabListLayoutContext.js +19 -0
  61. package/dist-cjs/tabs/internal/contexts/TabListLayoutContext.js.map +1 -0
  62. package/dist-cjs/tabs/internal/contexts/TabSlotRegistryContext.js +22 -0
  63. package/dist-cjs/tabs/internal/contexts/TabSlotRegistryContext.js.map +1 -0
  64. package/dist-cjs/tabs/internal/contexts/TabsContext.js +50 -0
  65. package/dist-cjs/tabs/internal/contexts/TabsContext.js.map +1 -0
  66. package/dist-cjs/tabs/internal/hooks/useFocusWithRetry.js +64 -0
  67. package/dist-cjs/tabs/internal/hooks/useFocusWithRetry.js.map +1 -0
  68. package/dist-cjs/tabs/internal/hooks/useTabListRecovery.js +76 -0
  69. package/dist-cjs/tabs/internal/hooks/useTabListRecovery.js.map +1 -0
  70. package/dist-cjs/tabs/internal/hooks/useTabRemovalHandler.js +165 -0
  71. package/dist-cjs/tabs/internal/hooks/useTabRemovalHandler.js.map +1 -0
  72. package/dist-cjs/tabs/internal/hooks/useTabSelectionFocus.js +87 -0
  73. package/dist-cjs/tabs/internal/hooks/useTabSelectionFocus.js.map +1 -0
  74. package/dist-cjs/tabs/internal/overflow/TabOverflowList.css.js +6 -0
  75. package/dist-cjs/tabs/internal/overflow/TabOverflowList.css.js.map +1 -0
  76. package/dist-cjs/tabs/internal/overflow/TabOverflowList.js +245 -0
  77. package/dist-cjs/tabs/internal/overflow/TabOverflowList.js.map +1 -0
  78. package/dist-cjs/tabs/internal/overflow/TabSlot.js +30 -0
  79. package/dist-cjs/tabs/internal/overflow/TabSlot.js.map +1 -0
  80. package/dist-cjs/tabs/internal/overflow/overflowMath.js +86 -0
  81. package/dist-cjs/tabs/internal/overflow/overflowMath.js.map +1 -0
  82. package/dist-cjs/tabs/internal/overflow/useOverflow.js +273 -0
  83. package/dist-cjs/tabs/internal/overflow/useOverflow.js.map +1 -0
  84. package/dist-cjs/tabs/internal/overflow/useOverflowLayoutState.js +99 -0
  85. package/dist-cjs/tabs/internal/overflow/useOverflowLayoutState.js.map +1 -0
  86. package/dist-cjs/tabs/internal/overflow/useOverflowSelectionState.js +68 -0
  87. package/dist-cjs/tabs/internal/overflow/useOverflowSelectionState.js.map +1 -0
  88. package/dist-cjs/tabs/internal/overflow/useRenderedTabWidth.js +92 -0
  89. package/dist-cjs/tabs/internal/overflow/useRenderedTabWidth.js.map +1 -0
  90. package/dist-cjs/tabs/internal/overflow/widthMeasurement.js +42 -0
  91. package/dist-cjs/tabs/internal/overflow/widthMeasurement.js.map +1 -0
  92. package/dist-cjs/tabs/internal/registry/useCollection.js +197 -0
  93. package/dist-cjs/tabs/internal/registry/useCollection.js.map +1 -0
  94. package/dist-cjs/tabs/internal/registry/useRenderedTabsRegistry.js +206 -0
  95. package/dist-cjs/tabs/internal/registry/useRenderedTabsRegistry.js.map +1 -0
  96. package/dist-cjs/tabs/internal/utils/domUtils.js +13 -0
  97. package/dist-cjs/tabs/internal/utils/domUtils.js.map +1 -0
  98. package/dist-cjs/tooltip/useAriaAnnounce.js +1 -0
  99. package/dist-cjs/tooltip/useAriaAnnounce.js.map +1 -1
  100. package/dist-es/aria-announcer/AriaAnnounce.js +15 -3
  101. package/dist-es/aria-announcer/AriaAnnounce.js.map +1 -1
  102. package/dist-es/aria-announcer/AriaAnnouncerContext.js.map +1 -1
  103. package/dist-es/aria-announcer/AriaAnnouncerProvider.js +67 -45
  104. package/dist-es/aria-announcer/AriaAnnouncerProvider.js.map +1 -1
  105. package/dist-es/aria-announcer/announcementRegistry.js +28 -0
  106. package/dist-es/aria-announcer/announcementRegistry.js.map +1 -0
  107. package/dist-es/aria-announcer/useAriaAnnouncer.js +45 -17
  108. package/dist-es/aria-announcer/useAriaAnnouncer.js.map +1 -1
  109. package/dist-es/index.js +9 -1
  110. package/dist-es/index.js.map +1 -1
  111. package/dist-es/navigation-item/NavigationItem.js +2 -0
  112. package/dist-es/navigation-item/NavigationItem.js.map +1 -1
  113. package/dist-es/pagination/Pagination.js +1 -0
  114. package/dist-es/pagination/Pagination.js.map +1 -1
  115. package/dist-es/rating/Rating.css.js +4 -0
  116. package/dist-es/rating/Rating.css.js.map +1 -0
  117. package/dist-es/rating/Rating.js +138 -0
  118. package/dist-es/rating/Rating.js.map +1 -0
  119. package/dist-es/rating/RatingItem.css.js +4 -0
  120. package/dist-es/rating/RatingItem.css.js.map +1 -0
  121. package/dist-es/rating/RatingItem.js +73 -0
  122. package/dist-es/rating/RatingItem.js.map +1 -0
  123. package/dist-es/salt-provider/SaltProvider.js +3 -1
  124. package/dist-es/salt-provider/SaltProvider.js.map +1 -1
  125. package/dist-es/semantic-icon-provider/SemanticIconProvider.js +23 -21
  126. package/dist-es/semantic-icon-provider/SemanticIconProvider.js.map +1 -1
  127. package/dist-es/spinner/Spinner.js +1 -0
  128. package/dist-es/spinner/Spinner.js.map +1 -1
  129. package/dist-es/tabs/Tab.css.js +4 -0
  130. package/dist-es/tabs/Tab.css.js.map +1 -0
  131. package/dist-es/tabs/Tab.js +209 -0
  132. package/dist-es/tabs/Tab.js.map +1 -0
  133. package/dist-es/tabs/TabAction.js +61 -0
  134. package/dist-es/tabs/TabAction.js.map +1 -0
  135. package/dist-es/tabs/TabBar.css.js +4 -0
  136. package/dist-es/tabs/TabBar.css.js.map +1 -0
  137. package/dist-es/tabs/TabBar.js +43 -0
  138. package/dist-es/tabs/TabBar.js.map +1 -0
  139. package/dist-es/tabs/TabList.css.js +4 -0
  140. package/dist-es/tabs/TabList.css.js.map +1 -0
  141. package/dist-es/tabs/TabList.js +279 -0
  142. package/dist-es/tabs/TabList.js.map +1 -0
  143. package/dist-es/tabs/TabPanel.css.js +4 -0
  144. package/dist-es/tabs/TabPanel.css.js.map +1 -0
  145. package/dist-es/tabs/TabPanel.js +96 -0
  146. package/dist-es/tabs/TabPanel.js.map +1 -0
  147. package/dist-es/tabs/TabTrigger.css.js +4 -0
  148. package/dist-es/tabs/TabTrigger.css.js.map +1 -0
  149. package/dist-es/tabs/TabTrigger.js +186 -0
  150. package/dist-es/tabs/TabTrigger.js.map +1 -0
  151. package/dist-es/tabs/Tabs.css.js +4 -0
  152. package/dist-es/tabs/Tabs.css.js.map +1 -0
  153. package/dist-es/tabs/Tabs.js +198 -0
  154. package/dist-es/tabs/Tabs.js.map +1 -0
  155. package/dist-es/tabs/internal/contexts/TabContext.js +23 -0
  156. package/dist-es/tabs/internal/contexts/TabContext.js.map +1 -0
  157. package/dist-es/tabs/internal/contexts/TabListLayoutContext.js +16 -0
  158. package/dist-es/tabs/internal/contexts/TabListLayoutContext.js.map +1 -0
  159. package/dist-es/tabs/internal/contexts/TabSlotRegistryContext.js +19 -0
  160. package/dist-es/tabs/internal/contexts/TabSlotRegistryContext.js.map +1 -0
  161. package/dist-es/tabs/internal/contexts/TabsContext.js +47 -0
  162. package/dist-es/tabs/internal/contexts/TabsContext.js.map +1 -0
  163. package/dist-es/tabs/internal/hooks/useFocusWithRetry.js +62 -0
  164. package/dist-es/tabs/internal/hooks/useFocusWithRetry.js.map +1 -0
  165. package/dist-es/tabs/internal/hooks/useTabListRecovery.js +74 -0
  166. package/dist-es/tabs/internal/hooks/useTabListRecovery.js.map +1 -0
  167. package/dist-es/tabs/internal/hooks/useTabRemovalHandler.js +163 -0
  168. package/dist-es/tabs/internal/hooks/useTabRemovalHandler.js.map +1 -0
  169. package/dist-es/tabs/internal/hooks/useTabSelectionFocus.js +85 -0
  170. package/dist-es/tabs/internal/hooks/useTabSelectionFocus.js.map +1 -0
  171. package/dist-es/tabs/internal/overflow/TabOverflowList.css.js +4 -0
  172. package/dist-es/tabs/internal/overflow/TabOverflowList.css.js.map +1 -0
  173. package/dist-es/tabs/internal/overflow/TabOverflowList.js +243 -0
  174. package/dist-es/tabs/internal/overflow/TabOverflowList.js.map +1 -0
  175. package/dist-es/tabs/internal/overflow/TabSlot.js +28 -0
  176. package/dist-es/tabs/internal/overflow/TabSlot.js.map +1 -0
  177. package/dist-es/tabs/internal/overflow/overflowMath.js +82 -0
  178. package/dist-es/tabs/internal/overflow/overflowMath.js.map +1 -0
  179. package/dist-es/tabs/internal/overflow/useOverflow.js +271 -0
  180. package/dist-es/tabs/internal/overflow/useOverflow.js.map +1 -0
  181. package/dist-es/tabs/internal/overflow/useOverflowLayoutState.js +97 -0
  182. package/dist-es/tabs/internal/overflow/useOverflowLayoutState.js.map +1 -0
  183. package/dist-es/tabs/internal/overflow/useOverflowSelectionState.js +66 -0
  184. package/dist-es/tabs/internal/overflow/useOverflowSelectionState.js.map +1 -0
  185. package/dist-es/tabs/internal/overflow/useRenderedTabWidth.js +90 -0
  186. package/dist-es/tabs/internal/overflow/useRenderedTabWidth.js.map +1 -0
  187. package/dist-es/tabs/internal/overflow/widthMeasurement.js +36 -0
  188. package/dist-es/tabs/internal/overflow/widthMeasurement.js.map +1 -0
  189. package/dist-es/tabs/internal/registry/useCollection.js +195 -0
  190. package/dist-es/tabs/internal/registry/useCollection.js.map +1 -0
  191. package/dist-es/tabs/internal/registry/useRenderedTabsRegistry.js +204 -0
  192. package/dist-es/tabs/internal/registry/useRenderedTabsRegistry.js.map +1 -0
  193. package/dist-es/tabs/internal/utils/domUtils.js +11 -0
  194. package/dist-es/tabs/internal/utils/domUtils.js.map +1 -0
  195. package/dist-es/tooltip/useAriaAnnounce.js +1 -0
  196. package/dist-es/tooltip/useAriaAnnounce.js.map +1 -1
  197. package/dist-types/aria-announcer/AriaAnnounce.d.ts +9 -2
  198. package/dist-types/aria-announcer/AriaAnnouncerContext.d.ts +26 -2
  199. package/dist-types/aria-announcer/AriaAnnouncerProvider.d.ts +6 -7
  200. package/dist-types/aria-announcer/announcementRegistry.d.ts +5 -0
  201. package/dist-types/index.d.ts +2 -0
  202. package/dist-types/rating/Rating.d.ts +48 -0
  203. package/dist-types/rating/RatingItem.d.ts +47 -0
  204. package/dist-types/rating/index.d.ts +1 -0
  205. package/dist-types/semantic-icon-provider/SemanticIconProvider.d.ts +21 -19
  206. package/dist-types/tabs/Tab.d.ts +12 -0
  207. package/dist-types/tabs/TabAction.d.ts +4 -0
  208. package/dist-types/tabs/TabBar.d.ts +12 -0
  209. package/dist-types/tabs/TabList.d.ts +12 -0
  210. package/dist-types/tabs/TabPanel.d.ts +9 -0
  211. package/dist-types/tabs/TabTrigger.d.ts +4 -0
  212. package/dist-types/tabs/Tabs.d.ts +20 -0
  213. package/dist-types/tabs/index.d.ts +7 -0
  214. package/dist-types/tabs/internal/contexts/TabContext.d.ts +12 -0
  215. package/dist-types/tabs/internal/contexts/TabListLayoutContext.d.ts +9 -0
  216. package/dist-types/tabs/internal/contexts/TabSlotRegistryContext.d.ts +5 -0
  217. package/dist-types/tabs/internal/contexts/TabsContext.d.ts +43 -0
  218. package/dist-types/tabs/internal/hooks/useFocusWithRetry.d.ts +9 -0
  219. package/dist-types/tabs/internal/hooks/useTabListRecovery.d.ts +12 -0
  220. package/dist-types/tabs/internal/hooks/useTabRemovalHandler.d.ts +32 -0
  221. package/dist-types/tabs/internal/hooks/useTabSelectionFocus.d.ts +15 -0
  222. package/dist-types/tabs/internal/overflow/TabOverflowList.d.ts +10 -0
  223. package/dist-types/tabs/internal/overflow/TabSlot.d.ts +6 -0
  224. package/dist-types/tabs/internal/overflow/overflowMath.d.ts +18 -0
  225. package/dist-types/tabs/internal/overflow/useOverflow.d.ts +11 -0
  226. package/dist-types/tabs/internal/overflow/useOverflowLayoutState.d.ts +13 -0
  227. package/dist-types/tabs/internal/overflow/useOverflowSelectionState.d.ts +13 -0
  228. package/dist-types/tabs/internal/overflow/useRenderedTabWidth.d.ts +12 -0
  229. package/dist-types/tabs/internal/overflow/widthMeasurement.d.ts +5 -0
  230. package/dist-types/tabs/internal/registry/useCollection.d.ts +30 -0
  231. package/dist-types/tabs/internal/registry/useRenderedTabsRegistry.d.ts +12 -0
  232. package/dist-types/tabs/internal/utils/domUtils.d.ts +1 -0
  233. package/package.json +3 -1
@@ -0,0 +1,209 @@
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import { useComponentCssInjection } from '@salt-ds/styles';
3
+ import { useWindow } from '@salt-ds/window';
4
+ import { clsx } from 'clsx';
5
+ import { forwardRef, useRef, useState, useCallback, useMemo } from 'react';
6
+ import { createPortal } from 'react-dom';
7
+ import { makePrefixer } from '../utils/makePrefixer.js';
8
+ import { useIsomorphicLayoutEffect } from '../utils/useIsomorphicLayoutEffect.js';
9
+ import '../utils/useFloatingUI/useFloatingUI.js';
10
+ import { useForkRef } from '../utils/useForkRef.js';
11
+ import { useId } from '../utils/useId.js';
12
+ import '../salt-provider/SaltProvider.js';
13
+ import '../viewport/ViewportProvider.js';
14
+ import { TabContext } from './internal/contexts/TabContext.js';
15
+ import { useTabs } from './internal/contexts/TabsContext.js';
16
+ import { useRenderedTabWidth } from './internal/overflow/useRenderedTabWidth.js';
17
+ import { getIntrinsicMeasuredWidth } from './internal/overflow/widthMeasurement.js';
18
+ import css_248z from './Tab.css.js';
19
+
20
+ const withBaseName = makePrefixer("saltTab");
21
+ const Tab = forwardRef(
22
+ function Tab2(props, ref) {
23
+ const {
24
+ children,
25
+ className,
26
+ disabled: disabledProp,
27
+ onBlur,
28
+ onMouseDown,
29
+ onFocus,
30
+ onFocusCapture,
31
+ value,
32
+ id: idProp,
33
+ ...rest
34
+ } = props;
35
+ const targetWindow = useWindow();
36
+ useComponentCssInjection({
37
+ testId: "salt-tab",
38
+ css: css_248z,
39
+ window: targetWindow
40
+ });
41
+ const {
42
+ selected,
43
+ activeTab,
44
+ renderMode,
45
+ registerBootstrapTab,
46
+ setBootstrapTabReady,
47
+ registerRenderedTab,
48
+ updateRenderedTab
49
+ } = useTabs();
50
+ const disabled = !!disabledProp;
51
+ const id = useId(idProp);
52
+ const wasMouseDown = useRef(false);
53
+ const [focusVisible, setFocusVisible] = useState(false);
54
+ const [focused, setFocused] = useState(false);
55
+ const [hostElement, setHostElement] = useState(null);
56
+ const markerRef = useRef(null);
57
+ const tabRootRef = useRef(null);
58
+ const handleFocusCapture = (event) => {
59
+ onFocusCapture == null ? void 0 : onFocusCapture(event);
60
+ if (id) {
61
+ activeTab.current = { value, id };
62
+ }
63
+ };
64
+ const handleFocus = (event) => {
65
+ onFocus == null ? void 0 : onFocus(event);
66
+ setFocused(true);
67
+ if (!wasMouseDown.current && event.target.getAttribute("role") === "tab") {
68
+ setFocusVisible(true);
69
+ }
70
+ wasMouseDown.current = false;
71
+ };
72
+ const handleBlur = (event) => {
73
+ onBlur == null ? void 0 : onBlur(event);
74
+ setFocused(false);
75
+ setFocusVisible(false);
76
+ };
77
+ const handleMouseDown = (event) => {
78
+ onMouseDown == null ? void 0 : onMouseDown(event);
79
+ if (id) {
80
+ activeTab.current = { value, id };
81
+ }
82
+ wasMouseDown.current = true;
83
+ };
84
+ const [actionIds, setActionIds] = useState(() => /* @__PURE__ */ new Set());
85
+ const registerAction = useCallback((id2) => {
86
+ setActionIds((old) => {
87
+ if (old.has(id2)) {
88
+ return old;
89
+ }
90
+ const next = new Set(old);
91
+ next.add(id2);
92
+ return next;
93
+ });
94
+ return () => {
95
+ setActionIds((old) => {
96
+ if (!old.has(id2)) {
97
+ return old;
98
+ }
99
+ const next = new Set(old);
100
+ next.delete(id2);
101
+ return next;
102
+ });
103
+ };
104
+ }, []);
105
+ const actions = useMemo(() => Array.from(actionIds), [actionIds]);
106
+ const context = useMemo(
107
+ () => ({
108
+ tabId: id,
109
+ selected: selected === value,
110
+ focused,
111
+ value,
112
+ disabled,
113
+ actions,
114
+ registerAction
115
+ }),
116
+ [id, selected, value, focused, disabled, actions, registerAction]
117
+ );
118
+ useIsomorphicLayoutEffect(() => {
119
+ const doc = targetWindow == null ? void 0 : targetWindow.document;
120
+ if (!doc) {
121
+ return;
122
+ }
123
+ const host = doc.createElement("div");
124
+ host.dataset.tabHost = value;
125
+ host.role = "presentation";
126
+ host.style.display = "contents";
127
+ setHostElement(host);
128
+ return () => {
129
+ host.remove();
130
+ };
131
+ }, [targetWindow, value]);
132
+ useIsomorphicLayoutEffect(() => {
133
+ if (renderMode !== "inline") {
134
+ return;
135
+ }
136
+ return registerBootstrapTab(value);
137
+ }, [registerBootstrapTab, renderMode, value]);
138
+ useIsomorphicLayoutEffect(() => {
139
+ setBootstrapTabReady(value, hostElement != null);
140
+ return () => {
141
+ setBootstrapTabReady(value, false);
142
+ };
143
+ }, [hostElement, setBootstrapTabReady, value]);
144
+ useIsomorphicLayoutEffect(() => {
145
+ if (!hostElement || !id) {
146
+ return;
147
+ }
148
+ return registerRenderedTab({
149
+ host: hostElement,
150
+ id,
151
+ marker: markerRef.current,
152
+ root: tabRootRef.current,
153
+ trigger: null,
154
+ value,
155
+ width: getIntrinsicMeasuredWidth(tabRootRef.current)
156
+ });
157
+ }, [hostElement, id, registerRenderedTab, value]);
158
+ useIsomorphicLayoutEffect(() => {
159
+ const updates = {
160
+ marker: markerRef.current,
161
+ root: tabRootRef.current
162
+ };
163
+ if (renderMode === "inline") {
164
+ updates.width = getIntrinsicMeasuredWidth(tabRootRef.current);
165
+ }
166
+ updateRenderedTab(value, updates);
167
+ }, [renderMode, updateRenderedTab, value]);
168
+ useRenderedTabWidth({
169
+ hostElement,
170
+ renderMode,
171
+ tabRootRef,
172
+ targetWindow,
173
+ updateRenderedTab,
174
+ value
175
+ });
176
+ const handleTabRootRef = useForkRef(tabRootRef, ref);
177
+ const tabMarkup = /* @__PURE__ */ jsx(TabContext.Provider, { value: context, children: /* @__PURE__ */ jsx(
178
+ "div",
179
+ {
180
+ className: clsx(
181
+ withBaseName(),
182
+ {
183
+ [withBaseName("selected")]: selected === value,
184
+ [withBaseName("disabled")]: disabled,
185
+ [withBaseName("focusVisible")]: focusVisible
186
+ },
187
+ className
188
+ ),
189
+ "data-overflowitem": "true",
190
+ "data-value": value,
191
+ ref: handleTabRootRef,
192
+ onMouseDown: handleMouseDown,
193
+ onFocusCapture: handleFocusCapture,
194
+ onFocus: handleFocus,
195
+ onBlur: handleBlur,
196
+ role: "presentation",
197
+ ...rest,
198
+ children
199
+ }
200
+ ) });
201
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
202
+ /* @__PURE__ */ jsx("span", { role: "presentation", "data-tab-marker": "", hidden: true, ref: markerRef }),
203
+ renderMode === "inline" ? tabMarkup : hostElement ? createPortal(tabMarkup, hostElement) : null
204
+ ] });
205
+ }
206
+ );
207
+
208
+ export { Tab };
209
+ //# sourceMappingURL=Tab.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Tab.js","sources":["../src/tabs/Tab.tsx"],"sourcesContent":["import { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport { clsx } from \"clsx\";\nimport {\n type ComponentPropsWithoutRef,\n type FocusEvent,\n forwardRef,\n type MouseEvent,\n type ReactElement,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport {\n makePrefixer,\n useForkRef,\n useId,\n useIsomorphicLayoutEffect,\n} from \"../utils\";\nimport { TabContext } from \"./internal/contexts/TabContext\";\nimport { useTabs } from \"./internal/contexts/TabsContext\";\nimport { useRenderedTabWidth } from \"./internal/overflow/useRenderedTabWidth\";\nimport { getIntrinsicMeasuredWidth } from \"./internal/overflow/widthMeasurement\";\nimport tabCss from \"./Tab.css\";\n\nconst withBaseName = makePrefixer(\"saltTab\");\n\nexport interface TabProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * If `true`, the tab will be disabled.\n */\n disabled?: boolean;\n /**\n * The value of the tab. This must be unique within a `Tabs` instance.\n */\n value: string;\n}\n\nexport const Tab = forwardRef<HTMLDivElement, TabProps>(\n function Tab(props, ref): ReactElement<TabProps> {\n const {\n children,\n className,\n disabled: disabledProp,\n onBlur,\n onMouseDown,\n onFocus,\n onFocusCapture,\n value,\n id: idProp,\n ...rest\n } = props;\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-tab\",\n css: tabCss,\n window: targetWindow,\n });\n\n const {\n selected,\n activeTab,\n renderMode,\n registerBootstrapTab,\n setBootstrapTabReady,\n registerRenderedTab,\n updateRenderedTab,\n } = useTabs();\n\n const disabled = !!disabledProp;\n\n const id = useId(idProp);\n\n const wasMouseDown = useRef(false);\n const [focusVisible, setFocusVisible] = useState(false);\n const [focused, setFocused] = useState(false);\n const [hostElement, setHostElement] = useState<HTMLDivElement | null>(null);\n const markerRef = useRef<HTMLSpanElement>(null);\n const tabRootRef = useRef<HTMLDivElement>(null);\n\n const handleFocusCapture = (event: FocusEvent<HTMLDivElement>) => {\n onFocusCapture?.(event);\n if (id) {\n activeTab.current = { value, id };\n }\n };\n\n const handleFocus = (event: FocusEvent<HTMLDivElement>) => {\n onFocus?.(event);\n\n setFocused(true);\n\n if (\n !wasMouseDown.current &&\n event.target.getAttribute(\"role\") === \"tab\"\n ) {\n setFocusVisible(true);\n }\n\n wasMouseDown.current = false;\n };\n\n const handleBlur = (event: FocusEvent<HTMLDivElement>) => {\n onBlur?.(event);\n setFocused(false);\n setFocusVisible(false);\n };\n\n const handleMouseDown = (event: MouseEvent<HTMLDivElement>) => {\n onMouseDown?.(event);\n if (id) {\n activeTab.current = { value, id };\n }\n wasMouseDown.current = true;\n };\n\n const [actionIds, setActionIds] = useState(() => new Set<string>());\n\n const registerAction = useCallback((id: string) => {\n setActionIds((old) => {\n if (old.has(id)) {\n return old;\n }\n\n const next = new Set(old);\n next.add(id);\n return next;\n });\n\n return () => {\n setActionIds((old) => {\n if (!old.has(id)) {\n return old;\n }\n\n const next = new Set(old);\n next.delete(id);\n return next;\n });\n };\n }, []);\n\n const actions = useMemo(() => Array.from(actionIds), [actionIds]);\n\n const context = useMemo(\n () => ({\n tabId: id,\n selected: selected === value,\n focused,\n value,\n disabled,\n actions,\n registerAction,\n }),\n [id, selected, value, focused, disabled, actions, registerAction],\n );\n\n useIsomorphicLayoutEffect(() => {\n const doc = targetWindow?.document;\n if (!doc) {\n return;\n }\n\n const host = doc.createElement(\"div\");\n host.dataset.tabHost = value;\n host.role = \"presentation\";\n host.style.display = \"contents\";\n setHostElement(host);\n\n return () => {\n host.remove();\n };\n }, [targetWindow, value]);\n\n useIsomorphicLayoutEffect(() => {\n if (renderMode !== \"inline\") {\n return;\n }\n\n return registerBootstrapTab(value);\n }, [registerBootstrapTab, renderMode, value]);\n\n useIsomorphicLayoutEffect(() => {\n setBootstrapTabReady(value, hostElement != null);\n\n return () => {\n setBootstrapTabReady(value, false);\n };\n }, [hostElement, setBootstrapTabReady, value]);\n\n useIsomorphicLayoutEffect(() => {\n if (!hostElement || !id) {\n return;\n }\n\n return registerRenderedTab({\n host: hostElement,\n id,\n marker: markerRef.current,\n root: tabRootRef.current,\n trigger: null,\n value,\n width: getIntrinsicMeasuredWidth(tabRootRef.current),\n });\n }, [hostElement, id, registerRenderedTab, value]);\n\n useIsomorphicLayoutEffect(() => {\n const updates = {\n marker: markerRef.current,\n root: tabRootRef.current,\n } as Partial<{\n host: HTMLDivElement;\n id: string;\n marker: HTMLElement | null;\n root: HTMLElement | null;\n trigger: HTMLButtonElement | null;\n width: number;\n }>;\n\n if (renderMode === \"inline\") {\n updates.width = getIntrinsicMeasuredWidth(tabRootRef.current);\n }\n\n updateRenderedTab(value, updates);\n }, [renderMode, updateRenderedTab, value]);\n\n useRenderedTabWidth({\n hostElement,\n renderMode,\n tabRootRef,\n targetWindow,\n updateRenderedTab,\n value,\n });\n\n const handleTabRootRef = useForkRef(tabRootRef, ref);\n\n const tabMarkup = (\n <TabContext.Provider value={context}>\n <div\n className={clsx(\n withBaseName(),\n {\n [withBaseName(\"selected\")]: selected === value,\n [withBaseName(\"disabled\")]: disabled,\n [withBaseName(\"focusVisible\")]: focusVisible,\n },\n className,\n )}\n data-overflowitem=\"true\"\n data-value={value}\n ref={handleTabRootRef}\n onMouseDown={handleMouseDown}\n onFocusCapture={handleFocusCapture}\n onFocus={handleFocus}\n onBlur={handleBlur}\n role=\"presentation\"\n {...rest}\n >\n {children}\n </div>\n </TabContext.Provider>\n );\n\n return (\n <>\n <span role=\"presentation\" data-tab-marker=\"\" hidden ref={markerRef} />\n {renderMode === \"inline\"\n ? tabMarkup\n : hostElement\n ? createPortal(tabMarkup, hostElement)\n : null}\n </>\n );\n },\n);\n"],"names":["Tab","tabCss","id"],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { clsx } from 'clsx';
3
+ import { forwardRef } from 'react';
4
+ import { Button } from '../button/Button.js';
5
+ import { useIsomorphicLayoutEffect } from '../utils/useIsomorphicLayoutEffect.js';
6
+ import '../utils/useFloatingUI/useFloatingUI.js';
7
+ import { useId } from '../utils/useId.js';
8
+ import '../salt-provider/SaltProvider.js';
9
+ import '../viewport/ViewportProvider.js';
10
+ import { useTab } from './internal/contexts/TabContext.js';
11
+ import { useTabs } from './internal/contexts/TabsContext.js';
12
+
13
+ const TabAction = forwardRef(
14
+ function TabAction2(props, ref) {
15
+ const {
16
+ "aria-labelledby": ariaLabelledBy,
17
+ id: idProp,
18
+ onFocus,
19
+ onMouseDown,
20
+ ...rest
21
+ } = props;
22
+ const id = useId(idProp);
23
+ const { focused, selected, tabId, registerAction, value } = useTab();
24
+ const { activeTab } = useTabs();
25
+ useIsomorphicLayoutEffect(() => {
26
+ if (id) {
27
+ return registerAction(id);
28
+ }
29
+ }, [registerAction, id]);
30
+ const setActiveTab = () => {
31
+ if (tabId) {
32
+ activeTab.current = { id: tabId, value };
33
+ }
34
+ };
35
+ const handleFocus = (event) => {
36
+ onFocus == null ? void 0 : onFocus(event);
37
+ setActiveTab();
38
+ };
39
+ const handleMouseDown = (event) => {
40
+ onMouseDown == null ? void 0 : onMouseDown(event);
41
+ setActiveTab();
42
+ };
43
+ return /* @__PURE__ */ jsx(
44
+ Button,
45
+ {
46
+ id,
47
+ "aria-labelledby": clsx(ariaLabelledBy, tabId, id),
48
+ tabIndex: focused || selected ? void 0 : -1,
49
+ appearance: "transparent",
50
+ sentiment: "neutral",
51
+ ref,
52
+ onFocus: handleFocus,
53
+ onMouseDown: handleMouseDown,
54
+ ...rest
55
+ }
56
+ );
57
+ }
58
+ );
59
+
60
+ export { TabAction };
61
+ //# sourceMappingURL=TabAction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TabAction.js","sources":["../src/tabs/TabAction.tsx"],"sourcesContent":["import { clsx } from \"clsx\";\nimport { type FocusEvent, forwardRef, type MouseEvent } from \"react\";\nimport { Button, type ButtonProps } from \"../button\";\nimport { useId, useIsomorphicLayoutEffect } from \"../utils\";\nimport { useTab } from \"./internal/contexts/TabContext\";\nimport { useTabs } from \"./internal/contexts/TabsContext\";\n\nexport interface TabActionProps extends ButtonProps {}\n\nexport const TabAction = forwardRef<HTMLButtonElement, TabActionProps>(\n function TabAction(props, ref) {\n const {\n \"aria-labelledby\": ariaLabelledBy,\n id: idProp,\n onFocus,\n onMouseDown,\n ...rest\n } = props;\n\n const id = useId(idProp);\n const { focused, selected, tabId, registerAction, value } = useTab();\n const { activeTab } = useTabs();\n\n useIsomorphicLayoutEffect(() => {\n if (id) {\n return registerAction(id);\n }\n }, [registerAction, id]);\n\n const setActiveTab = () => {\n if (tabId) {\n activeTab.current = { id: tabId, value };\n }\n };\n\n const handleFocus = (event: FocusEvent<HTMLButtonElement>) => {\n onFocus?.(event);\n setActiveTab();\n };\n\n const handleMouseDown = (event: MouseEvent<HTMLButtonElement>) => {\n onMouseDown?.(event);\n setActiveTab();\n };\n\n return (\n <Button\n id={id}\n aria-labelledby={clsx(ariaLabelledBy, tabId, id)}\n tabIndex={focused || selected ? undefined : -1}\n appearance=\"transparent\"\n sentiment=\"neutral\"\n ref={ref}\n onFocus={handleFocus}\n onMouseDown={handleMouseDown}\n {...rest}\n />\n );\n },\n);\n"],"names":["TabAction"],"mappings":";;;;;;;;;;;;AASO,MAAM,SAAA,GAAY,UAAA;AAAA,EACvB,SAASA,UAAAA,CAAU,KAAA,EAAO,GAAA,EAAK;AAC7B,IAAA,MAAM;AAAA,MACJ,iBAAA,EAAmB,cAAA;AAAA,MACnB,EAAA,EAAI,MAAA;AAAA,MACJ,OAAA;AAAA,MACA,WAAA;AAAA,MACA,GAAG;AAAA,KACL,GAAI,KAAA;AAEJ,IAAA,MAAM,EAAA,GAAK,MAAM,MAAM,CAAA;AACvB,IAAA,MAAM,EAAE,OAAA,EAAS,QAAA,EAAU,OAAO,cAAA,EAAgB,KAAA,KAAU,MAAA,EAAO;AACnE,IAAA,MAAM,EAAE,SAAA,EAAU,GAAI,OAAA,EAAQ;AAE9B,IAAA,yBAAA,CAA0B,MAAM;AAC9B,MAAA,IAAI,EAAA,EAAI;AACN,QAAA,OAAO,eAAe,EAAE,CAAA;AAAA,MAC1B;AAAA,IACF,CAAA,EAAG,CAAC,cAAA,EAAgB,EAAE,CAAC,CAAA;AAEvB,IAAA,MAAM,eAAe,MAAM;AACzB,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,SAAA,CAAU,OAAA,GAAU,EAAE,EAAA,EAAI,KAAA,EAAO,KAAA,EAAM;AAAA,MACzC;AAAA,IACF,CAAA;AAEA,IAAA,MAAM,WAAA,GAAc,CAAC,KAAA,KAAyC;AAC5D,MAAA,OAAA,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAU,KAAA,CAAA;AACV,MAAA,YAAA,EAAa;AAAA,IACf,CAAA;AAEA,IAAA,MAAM,eAAA,GAAkB,CAAC,KAAA,KAAyC;AAChE,MAAA,WAAA,IAAA,IAAA,GAAA,MAAA,GAAA,WAAA,CAAc,KAAA,CAAA;AACd,MAAA,YAAA,EAAa;AAAA,IACf,CAAA;AAEA,IAAA,uBACE,GAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,EAAA;AAAA,QACA,iBAAA,EAAiB,IAAA,CAAK,cAAA,EAAgB,KAAA,EAAO,EAAE,CAAA;AAAA,QAC/C,QAAA,EAAU,OAAA,IAAW,QAAA,GAAW,MAAA,GAAY,EAAA;AAAA,QAC5C,UAAA,EAAW,aAAA;AAAA,QACX,SAAA,EAAU,SAAA;AAAA,QACV,GAAA;AAAA,QACA,OAAA,EAAS,WAAA;AAAA,QACT,WAAA,EAAa,eAAA;AAAA,QACZ,GAAG;AAAA;AAAA,KACN;AAAA,EAEJ;AACF;;;;"}
@@ -0,0 +1,4 @@
1
+ var css_248z = ".saltTabBar {\n display: flex;\n align-items: center;\n flex-direction: row;\n position: relative;\n box-sizing: border-box;\n min-width: 0;\n max-width: 100%;\n}\n\n.saltTabBar-strip {\n display: flex;\n align-items: center;\n flex-direction: row;\n flex: 1 1 auto;\n gap: var(--salt-spacing-100);\n box-sizing: border-box;\n min-width: 0;\n max-width: 100%;\n}\n\n.saltTabBar-divider::before {\n content: \"\";\n position: absolute;\n inset: auto 0 0 0;\n height: var(--salt-size-fixed-100);\n border-bottom: var(--salt-size-fixed-100) var(--salt-borderStyle-solid) var(--salt-separable-secondary-borderColor);\n}\n\n.saltTabBar-inset {\n padding-left: var(--salt-spacing-300);\n padding-right: var(--salt-spacing-300);\n}\n";
2
+
3
+ export { css_248z as default };
4
+ //# sourceMappingURL=TabBar.css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TabBar.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
@@ -0,0 +1,43 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useComponentCssInjection } from '@salt-ds/styles';
3
+ import { useWindow } from '@salt-ds/window';
4
+ import { clsx } from 'clsx';
5
+ import { forwardRef } from 'react';
6
+ import { makePrefixer } from '../utils/makePrefixer.js';
7
+ import '../utils/useFloatingUI/useFloatingUI.js';
8
+ import '../utils/useId.js';
9
+ import '../salt-provider/SaltProvider.js';
10
+ import '../viewport/ViewportProvider.js';
11
+ import css_248z from './TabBar.css.js';
12
+
13
+ const withBaseName = makePrefixer("saltTabBar");
14
+ const TabBar = forwardRef(
15
+ function TabBar2(props, ref) {
16
+ const { className, children, divider, inset, ...rest } = props;
17
+ const targetWindow = useWindow();
18
+ useComponentCssInjection({
19
+ testId: "salt-tab-bar",
20
+ css: css_248z,
21
+ window: targetWindow
22
+ });
23
+ return /* @__PURE__ */ jsx(
24
+ "div",
25
+ {
26
+ className: clsx(
27
+ withBaseName(),
28
+ {
29
+ [withBaseName("divider")]: divider,
30
+ [withBaseName("inset")]: inset
31
+ },
32
+ className
33
+ ),
34
+ ...rest,
35
+ ref,
36
+ children: /* @__PURE__ */ jsx("div", { className: withBaseName("strip"), children })
37
+ }
38
+ );
39
+ }
40
+ );
41
+
42
+ export { TabBar };
43
+ //# sourceMappingURL=TabBar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TabBar.js","sources":["../src/tabs/TabBar.tsx"],"sourcesContent":["import { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport { clsx } from \"clsx\";\nimport { type ComponentPropsWithRef, forwardRef } from \"react\";\nimport { makePrefixer } from \"../utils\";\nimport tabBarCss from \"./TabBar.css\";\n\nexport interface TabBarProps extends ComponentPropsWithRef<\"div\"> {\n /**\n * Styling variant with a bottom separator. Defaults to false\n */\n divider?: boolean;\n /**\n * Styling variant with left and right padding. Defaults to false\n */\n inset?: boolean;\n}\n\nconst withBaseName = makePrefixer(\"saltTabBar\");\n\nexport const TabBar = forwardRef<HTMLDivElement, TabBarProps>(\n function TabBar(props, ref) {\n const { className, children, divider, inset, ...rest } = props;\n\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-tab-bar\",\n css: tabBarCss,\n window: targetWindow,\n });\n\n return (\n <div\n className={clsx(\n withBaseName(),\n {\n [withBaseName(\"divider\")]: divider,\n [withBaseName(\"inset\")]: inset,\n },\n className,\n )}\n {...rest}\n ref={ref}\n >\n <div className={withBaseName(\"strip\")}>{children}</div>\n </div>\n );\n },\n);\n"],"names":["TabBar","tabBarCss"],"mappings":";;;;;;;;;;;;AAkBA,MAAM,YAAA,GAAe,aAAa,YAAY,CAAA;AAEvC,MAAM,MAAA,GAAS,UAAA;AAAA,EACpB,SAASA,OAAAA,CAAO,KAAA,EAAO,GAAA,EAAK;AAC1B,IAAA,MAAM,EAAE,SAAA,EAAW,QAAA,EAAU,SAAS,KAAA,EAAO,GAAG,MAAK,GAAI,KAAA;AAEzD,IAAA,MAAM,eAAe,SAAA,EAAU;AAC/B,IAAA,wBAAA,CAAyB;AAAA,MACvB,MAAA,EAAQ,cAAA;AAAA,MACR,GAAA,EAAKC,QAAA;AAAA,MACL,MAAA,EAAQ;AAAA,KACT,CAAA;AAED,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAW,IAAA;AAAA,UACT,YAAA,EAAa;AAAA,UACb;AAAA,YACE,CAAC,YAAA,CAAa,SAAS,CAAC,GAAG,OAAA;AAAA,YAC3B,CAAC,YAAA,CAAa,OAAO,CAAC,GAAG;AAAA,WAC3B;AAAA,UACA;AAAA,SACF;AAAA,QACC,GAAG,IAAA;AAAA,QACJ,GAAA;AAAA,QAEA,8BAAC,KAAA,EAAA,EAAI,SAAA,EAAW,YAAA,CAAa,OAAO,GAAI,QAAA,EAAS;AAAA;AAAA,KACnD;AAAA,EAEJ;AACF;;;;"}
@@ -0,0 +1,4 @@
1
+ var css_248z = "/* Component class applied to the root element */\n.saltTabList {\n display: flex;\n flex-wrap: nowrap;\n justify-content: flex-start;\n align-items: center;\n position: relative;\n background: transparent;\n min-height: calc(var(--salt-size-base) + var(--salt-spacing-100));\n gap: var(--salt-spacing-100);\n max-width: 100%;\n min-width: 0;\n flex: 0 1 auto;\n}\n\n.saltTabList-center {\n justify-content: center;\n}\n\n.saltTabList-right {\n justify-content: flex-end;\n}\n\n.saltTabList-activeColorPrimary {\n --saltTabList-activeColor: var(--salt-container-primary-background);\n}\n\n.saltTabList-activeColorSecondary {\n --saltTabList-activeColor: var(--salt-container-secondary-background);\n}\n\n.saltTabList-activeColorTertiary {\n --saltTabList-activeColor: var(--salt-container-tertiary-background);\n}\n\n.saltTabList-measureContainer {\n position: absolute;\n top: 0;\n left: 0;\n height: 0;\n overflow: hidden;\n pointer-events: none;\n visibility: hidden;\n white-space: nowrap;\n}\n";
2
+
3
+ export { css_248z as default };
4
+ //# sourceMappingURL=TabList.css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TabList.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
@@ -0,0 +1,279 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { useComponentCssInjection } from '@salt-ds/styles';
3
+ import { useWindow } from '@salt-ds/window';
4
+ import { clsx } from 'clsx';
5
+ import { forwardRef, useRef, useState, useEffect, useCallback, useMemo } from 'react';
6
+ import { useAriaAnnouncer } from '../aria-announcer/useAriaAnnouncer.js';
7
+ import '../aria-announcer/AriaAnnouncerContext.js';
8
+ import '../aria-announcer/AriaAnnouncerProvider.js';
9
+ import { capitalize } from '../utils/capitalize.js';
10
+ import { makePrefixer } from '../utils/makePrefixer.js';
11
+ import { useIsomorphicLayoutEffect } from '../utils/useIsomorphicLayoutEffect.js';
12
+ import '../utils/useFloatingUI/useFloatingUI.js';
13
+ import { useForkRef } from '../utils/useForkRef.js';
14
+ import '../utils/useId.js';
15
+ import '../salt-provider/SaltProvider.js';
16
+ import '../viewport/ViewportProvider.js';
17
+ import { TabListLayoutContext } from './internal/contexts/TabListLayoutContext.js';
18
+ import { TabSlotRegistryContext } from './internal/contexts/TabSlotRegistryContext.js';
19
+ import { useTabs } from './internal/contexts/TabsContext.js';
20
+ import { useFocusWithRetry } from './internal/hooks/useFocusWithRetry.js';
21
+ import { useTabListRecovery } from './internal/hooks/useTabListRecovery.js';
22
+ import { useTabRemovalHandler } from './internal/hooks/useTabRemovalHandler.js';
23
+ import { useTabSelectionFocus } from './internal/hooks/useTabSelectionFocus.js';
24
+ import { TabOverflowList } from './internal/overflow/TabOverflowList.js';
25
+ import { TabSlot } from './internal/overflow/TabSlot.js';
26
+ import { useOverflow } from './internal/overflow/useOverflow.js';
27
+ import { useOverflowLayoutState } from './internal/overflow/useOverflowLayoutState.js';
28
+ import css_248z from './TabList.css.js';
29
+
30
+ const withBaseName = makePrefixer("saltTabList");
31
+ const MAX_FOCUS_RETRY_ATTEMPTS = 120;
32
+ const TabList = forwardRef(
33
+ function TabList2(props, ref) {
34
+ const {
35
+ appearance = "bordered",
36
+ activeColor = "primary",
37
+ children,
38
+ className,
39
+ onKeyDown,
40
+ ...rest
41
+ } = props;
42
+ const targetWindow = useWindow();
43
+ useComponentCssInjection({
44
+ testId: "salt-tab-list",
45
+ css: css_248z,
46
+ window: targetWindow
47
+ });
48
+ const {
49
+ renderMode,
50
+ selected,
51
+ setSelected,
52
+ setBootstrapOverflowReady,
53
+ getNext,
54
+ getPrevious,
55
+ getFirst,
56
+ getLast,
57
+ getIndex,
58
+ item,
59
+ itemAt,
60
+ activeTab,
61
+ selectionFromOverflowValueRef,
62
+ menuOpen,
63
+ setMenuOpen,
64
+ sortItems,
65
+ getRemovedItems,
66
+ getRenderedTab,
67
+ renderedTabs,
68
+ removalVersion
69
+ } = useTabs();
70
+ const tabstripRef = useRef(null);
71
+ const overflowListRef = useRef(null);
72
+ const slotMapRef = useRef(/* @__PURE__ */ new Map());
73
+ const removalRecoveryRafRef = useRef(null);
74
+ const pendingRemovalRecoveryRef = useRef(false);
75
+ const pendingRemovalRecoveryRetriesRef = useRef(0);
76
+ const [slotVersion, setSlotVersion] = useState(0);
77
+ const handleRef = useForkRef(tabstripRef, ref);
78
+ const overflowButtonRef = useRef(null);
79
+ const { announce } = useAriaAnnouncer();
80
+ const overflowMenuOpen = renderMode === "portal" ? menuOpen : false;
81
+ const [visibleValues, hiddenValues, isMeasuring] = useOverflow({
82
+ container: tabstripRef,
83
+ menuOpen: overflowMenuOpen,
84
+ selected,
85
+ tabs: renderedTabs,
86
+ overflowButton: overflowButtonRef
87
+ });
88
+ useEffect(() => {
89
+ setBootstrapOverflowReady(
90
+ renderMode === "inline" && renderedTabs.length > 0 && !isMeasuring
91
+ );
92
+ }, [
93
+ isMeasuring,
94
+ renderMode,
95
+ renderedTabs.length,
96
+ setBootstrapOverflowReady
97
+ ]);
98
+ const { resolvedOverflowActiveValue, tabListLayoutContext } = useOverflowLayoutState({
99
+ hiddenValues,
100
+ menuOpen,
101
+ overflowMenuOpen,
102
+ visibleValues
103
+ });
104
+ const registerSlot = useCallback(
105
+ (slotId, element) => {
106
+ const currentElement = slotMapRef.current.get(slotId) ?? null;
107
+ if (currentElement === element) {
108
+ return;
109
+ }
110
+ if (element) {
111
+ slotMapRef.current.set(slotId, element);
112
+ } else {
113
+ slotMapRef.current.delete(slotId);
114
+ }
115
+ setSlotVersion((currentVersion) => currentVersion + 1);
116
+ },
117
+ []
118
+ );
119
+ const slotRegistryContext = useMemo(
120
+ () => ({ registerSlot }),
121
+ [registerSlot]
122
+ );
123
+ const slotAssignments = useMemo(() => {
124
+ const nextAssignments = /* @__PURE__ */ new Map();
125
+ for (const value of visibleValues) {
126
+ nextAssignments.set(value, `main:${value}`);
127
+ }
128
+ for (const value of hiddenValues) {
129
+ nextAssignments.set(
130
+ value,
131
+ menuOpen ? `overflow:${value}` : `measure:${value}`
132
+ );
133
+ }
134
+ return {
135
+ map: nextAssignments,
136
+ version: slotVersion
137
+ };
138
+ }, [hiddenValues, menuOpen, slotVersion, visibleValues]);
139
+ useIsomorphicLayoutEffect(() => {
140
+ var _a;
141
+ if (renderMode !== "portal") {
142
+ return;
143
+ }
144
+ for (const [value, slotId] of slotAssignments.map) {
145
+ const host = (_a = getRenderedTab(value)) == null ? void 0 : _a.host;
146
+ const slot = slotMapRef.current.get(slotId);
147
+ if (host && slot && host.parentElement !== slot) {
148
+ slot.appendChild(host);
149
+ }
150
+ }
151
+ }, [getRenderedTab, renderMode, slotAssignments]);
152
+ const handleKeyDown = (event) => {
153
+ var _a, _b;
154
+ onKeyDown == null ? void 0 : onKeyDown(event);
155
+ if (menuOpen) return;
156
+ const actionMap = {
157
+ ArrowRight: getNext,
158
+ ArrowLeft: getPrevious,
159
+ Home: getFirst,
160
+ End: getLast
161
+ };
162
+ const action = actionMap[event.key];
163
+ if (action) {
164
+ event.preventDefault();
165
+ sortItems();
166
+ const activeTabId = (_a = activeTab.current) == null ? void 0 : _a.id;
167
+ if (!activeTabId) return;
168
+ const nextItem = action(activeTabId);
169
+ if (nextItem) {
170
+ (_b = nextItem.element) == null ? void 0 : _b.focus({ preventScroll: true });
171
+ }
172
+ }
173
+ };
174
+ const getSelectedTabElement = useCallback(() => {
175
+ var _a, _b, _c;
176
+ return ((_a = tabstripRef.current) == null ? void 0 : _a.querySelector(
177
+ '[role="tab"][aria-selected="true"]'
178
+ )) ?? ((_c = item((_b = activeTab.current) == null ? void 0 : _b.id)) == null ? void 0 : _c.element);
179
+ }, [item, activeTab]);
180
+ const { focusElementWithRetry } = useFocusWithRetry({
181
+ maxAttempts: MAX_FOCUS_RETRY_ATTEMPTS,
182
+ targetWindow
183
+ });
184
+ useTabSelectionFocus({
185
+ announce,
186
+ focusElementWithRetry,
187
+ getRenderedTab,
188
+ getSelectedTabElement,
189
+ menuOpen,
190
+ resolvedOverflowActiveValue,
191
+ selected,
192
+ selectionFromOverflowValueRef,
193
+ targetWindow
194
+ });
195
+ const handleTabRemoval = useTabRemovalHandler({
196
+ activeTab,
197
+ focusElementWithRetry,
198
+ getFirst,
199
+ getIndex,
200
+ getLast,
201
+ getRemovedItems,
202
+ getRenderedTab,
203
+ getSelectedTabElement,
204
+ item,
205
+ itemAt,
206
+ maxRetryAttempts: MAX_FOCUS_RETRY_ATTEMPTS,
207
+ menuOpen,
208
+ overflowButtonRef,
209
+ overflowListRef,
210
+ pendingRemovalRecoveryRef,
211
+ pendingRemovalRecoveryRetriesRef,
212
+ removalRecoveryRafRef,
213
+ selected,
214
+ setSelected,
215
+ tabstripRef,
216
+ targetWindow
217
+ });
218
+ useTabListRecovery({
219
+ removalVersion,
220
+ targetWindow,
221
+ tabstripRef,
222
+ overflowListRef,
223
+ handleTabRemoval,
224
+ pendingRemovalRecoveryRef,
225
+ pendingRemovalRecoveryRetriesRef
226
+ });
227
+ return /* @__PURE__ */ jsx(
228
+ "div",
229
+ {
230
+ role: "tablist",
231
+ className: clsx(
232
+ withBaseName(),
233
+ withBaseName(appearance),
234
+ withBaseName("horizontal"),
235
+ withBaseName(`activeColor${capitalize(activeColor)}`),
236
+ className
237
+ ),
238
+ "data-ismeasuring": renderMode === "portal" && isMeasuring ? true : void 0,
239
+ ref: handleRef,
240
+ onKeyDown: handleKeyDown,
241
+ ...rest,
242
+ children: renderMode === "inline" ? children : /* @__PURE__ */ jsx(TabSlotRegistryContext.Provider, { value: slotRegistryContext, children: /* @__PURE__ */ jsxs(TabListLayoutContext.Provider, { value: tabListLayoutContext, children: [
243
+ children,
244
+ visibleValues.map((value) => /* @__PURE__ */ jsx(TabSlot, { slotId: `main:${value}`, value }, value)),
245
+ !menuOpen && hiddenValues.length > 0 ? /* @__PURE__ */ jsx(
246
+ "div",
247
+ {
248
+ "aria-hidden": "true",
249
+ role: "presentation",
250
+ className: withBaseName("measureContainer"),
251
+ children: hiddenValues.map((value) => /* @__PURE__ */ jsx(
252
+ TabSlot,
253
+ {
254
+ slotId: `measure:${value}`,
255
+ value
256
+ },
257
+ `measure-${value}`
258
+ ))
259
+ }
260
+ ) : null,
261
+ /* @__PURE__ */ jsx(
262
+ TabOverflowList,
263
+ {
264
+ buttonRef: overflowButtonRef,
265
+ hiddenValues,
266
+ open: menuOpen,
267
+ order: renderedTabs.length,
268
+ setOpen: setMenuOpen,
269
+ ref: overflowListRef
270
+ }
271
+ )
272
+ ] }) })
273
+ }
274
+ );
275
+ }
276
+ );
277
+
278
+ export { TabList };
279
+ //# sourceMappingURL=TabList.js.map