@funstack/ui 0.0.0-stage → 0.1.0-alpha.1

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 (299) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +42 -2
  3. package/dist/accordion/accordion-context.d.mts +5 -0
  4. package/dist/accordion/accordion-context.d.mts.map +1 -0
  5. package/dist/accordion/accordion-context.mjs +9 -0
  6. package/dist/accordion/accordion-context.mjs.map +1 -0
  7. package/dist/accordion/accordion-item.d.mts +53 -0
  8. package/dist/accordion/accordion-item.d.mts.map +1 -0
  9. package/dist/accordion/accordion-item.mjs +85 -0
  10. package/dist/accordion/accordion-item.mjs.map +1 -0
  11. package/dist/accordion/accordion-panel.d.mts +40 -0
  12. package/dist/accordion/accordion-panel.d.mts.map +1 -0
  13. package/dist/accordion/accordion-panel.mjs +40 -0
  14. package/dist/accordion/accordion-panel.mjs.map +1 -0
  15. package/dist/accordion/accordion-root.d.mts +90 -0
  16. package/dist/accordion/accordion-root.d.mts.map +1 -0
  17. package/dist/accordion/accordion-root.mjs +229 -0
  18. package/dist/accordion/accordion-root.mjs.map +1 -0
  19. package/dist/accordion/accordion-trigger.d.mts +37 -0
  20. package/dist/accordion/accordion-trigger.d.mts.map +1 -0
  21. package/dist/accordion/accordion-trigger.mjs +81 -0
  22. package/dist/accordion/accordion-trigger.mjs.map +1 -0
  23. package/dist/accordion/index.d.mts +20 -0
  24. package/dist/accordion/index.d.mts.map +1 -0
  25. package/dist/accordion/index.mjs +20 -0
  26. package/dist/accordion/index.mjs.map +1 -0
  27. package/dist/checkbox/checkbox.d.mts +92 -0
  28. package/dist/checkbox/checkbox.d.mts.map +1 -0
  29. package/dist/checkbox/checkbox.mjs +82 -0
  30. package/dist/checkbox/checkbox.mjs.map +1 -0
  31. package/dist/checkbox/index.d.mts +3 -0
  32. package/dist/checkbox/index.mjs +1 -0
  33. package/dist/checkbox-group/checkbox-group-context.mjs +24 -0
  34. package/dist/checkbox-group/checkbox-group-context.mjs.map +1 -0
  35. package/dist/checkbox-group/checkbox-group-item.d.mts +55 -0
  36. package/dist/checkbox-group/checkbox-group-item.d.mts.map +1 -0
  37. package/dist/checkbox-group/checkbox-group-item.mjs +90 -0
  38. package/dist/checkbox-group/checkbox-group-item.mjs.map +1 -0
  39. package/dist/checkbox-group/checkbox-group-label.d.mts +21 -0
  40. package/dist/checkbox-group/checkbox-group-label.d.mts.map +1 -0
  41. package/dist/checkbox-group/checkbox-group-label.mjs +30 -0
  42. package/dist/checkbox-group/checkbox-group-label.mjs.map +1 -0
  43. package/dist/checkbox-group/checkbox-group-parent.d.mts +68 -0
  44. package/dist/checkbox-group/checkbox-group-parent.d.mts.map +1 -0
  45. package/dist/checkbox-group/checkbox-group-parent.mjs +145 -0
  46. package/dist/checkbox-group/checkbox-group-parent.mjs.map +1 -0
  47. package/dist/checkbox-group/checkbox-group-root.d.mts +100 -0
  48. package/dist/checkbox-group/checkbox-group-root.d.mts.map +1 -0
  49. package/dist/checkbox-group/checkbox-group-root.mjs +291 -0
  50. package/dist/checkbox-group/checkbox-group-root.mjs.map +1 -0
  51. package/dist/checkbox-group/index.d.mts +23 -0
  52. package/dist/checkbox-group/index.d.mts.map +1 -0
  53. package/dist/checkbox-group/index.mjs +23 -0
  54. package/dist/checkbox-group/index.mjs.map +1 -0
  55. package/dist/dialog/dialog-close.d.mts +44 -0
  56. package/dist/dialog/dialog-close.d.mts.map +1 -0
  57. package/dist/dialog/dialog-close.mjs +76 -0
  58. package/dist/dialog/dialog-close.mjs.map +1 -0
  59. package/dist/dialog/dialog-context.mjs +13 -0
  60. package/dist/dialog/dialog-context.mjs.map +1 -0
  61. package/dist/dialog/dialog-description.d.mts +22 -0
  62. package/dist/dialog/dialog-description.d.mts.map +1 -0
  63. package/dist/dialog/dialog-description.mjs +31 -0
  64. package/dist/dialog/dialog-description.mjs.map +1 -0
  65. package/dist/dialog/dialog-form.d.mts +58 -0
  66. package/dist/dialog/dialog-form.d.mts.map +1 -0
  67. package/dist/dialog/dialog-form.mjs +108 -0
  68. package/dist/dialog/dialog-form.mjs.map +1 -0
  69. package/dist/dialog/dialog-popup.d.mts +81 -0
  70. package/dist/dialog/dialog-popup.d.mts.map +1 -0
  71. package/dist/dialog/dialog-popup.mjs +367 -0
  72. package/dist/dialog/dialog-popup.mjs.map +1 -0
  73. package/dist/dialog/dialog-root.d.mts +70 -0
  74. package/dist/dialog/dialog-root.d.mts.map +1 -0
  75. package/dist/dialog/dialog-root.mjs +113 -0
  76. package/dist/dialog/dialog-root.mjs.map +1 -0
  77. package/dist/dialog/dialog-title.d.mts +23 -0
  78. package/dist/dialog/dialog-title.d.mts.map +1 -0
  79. package/dist/dialog/dialog-title.mjs +31 -0
  80. package/dist/dialog/dialog-title.mjs.map +1 -0
  81. package/dist/dialog/dialog-trigger.d.mts +39 -0
  82. package/dist/dialog/dialog-trigger.d.mts.map +1 -0
  83. package/dist/dialog/dialog-trigger.mjs +80 -0
  84. package/dist/dialog/dialog-trigger.mjs.map +1 -0
  85. package/dist/dialog/index.d.mts +28 -0
  86. package/dist/dialog/index.d.mts.map +1 -0
  87. package/dist/dialog/index.mjs +28 -0
  88. package/dist/dialog/index.mjs.map +1 -0
  89. package/dist/index.d.mts +60 -0
  90. package/dist/index.mjs +12 -0
  91. package/dist/internal/action-details.mjs +47 -0
  92. package/dist/internal/action-details.mjs.map +1 -0
  93. package/dist/internal/anchor-position.d.mts +7 -0
  94. package/dist/internal/anchor-position.d.mts.map +1 -0
  95. package/dist/internal/anchor-position.mjs +16 -0
  96. package/dist/internal/anchor-position.mjs.map +1 -0
  97. package/dist/internal/data-attr.mjs +13 -0
  98. package/dist/internal/data-attr.mjs.map +1 -0
  99. package/dist/internal/merge-props.mjs +119 -0
  100. package/dist/internal/merge-props.mjs.map +1 -0
  101. package/dist/internal/platform-attributes.d.mts +55 -0
  102. package/dist/internal/platform-attributes.d.mts.map +1 -0
  103. package/dist/internal/sync-presence.mjs +19 -0
  104. package/dist/internal/sync-presence.mjs.map +1 -0
  105. package/dist/internal/transition-boundary.mjs +52 -0
  106. package/dist/internal/transition-boundary.mjs.map +1 -0
  107. package/dist/internal/transition-types.mjs +15 -0
  108. package/dist/internal/transition-types.mjs.map +1 -0
  109. package/dist/internal/types.d.mts +38 -0
  110. package/dist/internal/types.d.mts.map +1 -0
  111. package/dist/internal/use-action-trigger.mjs +58 -0
  112. package/dist/internal/use-action-trigger.mjs.map +1 -0
  113. package/dist/internal/use-controllable-action.mjs +115 -0
  114. package/dist/internal/use-controllable-action.mjs.map +1 -0
  115. package/dist/internal/use-delayed-pair.mjs +40 -0
  116. package/dist/internal/use-delayed-pair.mjs.map +1 -0
  117. package/dist/internal/use-dev-warning.mjs +21 -0
  118. package/dist/internal/use-dev-warning.mjs.map +1 -0
  119. package/dist/internal/use-exit-animation.mjs +55 -0
  120. package/dist/internal/use-exit-animation.mjs.map +1 -0
  121. package/dist/internal/use-form-reset.mjs +50 -0
  122. package/dist/internal/use-form-reset.mjs.map +1 -0
  123. package/dist/internal/use-native-checked.mjs +113 -0
  124. package/dist/internal/use-native-checked.mjs.map +1 -0
  125. package/dist/internal/use-native-group-reconciliation.mjs +64 -0
  126. package/dist/internal/use-native-group-reconciliation.mjs.map +1 -0
  127. package/dist/internal/use-popover-popup.mjs +274 -0
  128. package/dist/internal/use-popover-popup.mjs.map +1 -0
  129. package/dist/internal/use-popover-root.d.mts +2 -0
  130. package/dist/internal/use-popover-root.mjs +112 -0
  131. package/dist/internal/use-popover-root.mjs.map +1 -0
  132. package/dist/internal/use-popover-trigger.mjs +86 -0
  133. package/dist/internal/use-popover-trigger.mjs.map +1 -0
  134. package/dist/internal/use-render.mjs +51 -0
  135. package/dist/internal/use-render.mjs.map +1 -0
  136. package/dist/internal/use-until-found.mjs +40 -0
  137. package/dist/internal/use-until-found.mjs.map +1 -0
  138. package/dist/menu/index.d.mts +34 -0
  139. package/dist/menu/index.d.mts.map +1 -0
  140. package/dist/menu/index.mjs +34 -0
  141. package/dist/menu/index.mjs.map +1 -0
  142. package/dist/menu/menu-checkbox-item.d.mts +42 -0
  143. package/dist/menu/menu-checkbox-item.d.mts.map +1 -0
  144. package/dist/menu/menu-checkbox-item.mjs +71 -0
  145. package/dist/menu/menu-checkbox-item.mjs.map +1 -0
  146. package/dist/menu/menu-context.d.mts +1 -0
  147. package/dist/menu/menu-context.mjs +28 -0
  148. package/dist/menu/menu-context.mjs.map +1 -0
  149. package/dist/menu/menu-dom.mjs +37 -0
  150. package/dist/menu/menu-dom.mjs.map +1 -0
  151. package/dist/menu/menu-group.d.mts +48 -0
  152. package/dist/menu/menu-group.d.mts.map +1 -0
  153. package/dist/menu/menu-group.mjs +69 -0
  154. package/dist/menu/menu-group.mjs.map +1 -0
  155. package/dist/menu/menu-hover.mjs +29 -0
  156. package/dist/menu/menu-hover.mjs.map +1 -0
  157. package/dist/menu/menu-item.d.mts +45 -0
  158. package/dist/menu/menu-item.d.mts.map +1 -0
  159. package/dist/menu/menu-item.mjs +54 -0
  160. package/dist/menu/menu-item.mjs.map +1 -0
  161. package/dist/menu/menu-popup.d.mts +69 -0
  162. package/dist/menu/menu-popup.d.mts.map +1 -0
  163. package/dist/menu/menu-popup.mjs +207 -0
  164. package/dist/menu/menu-popup.mjs.map +1 -0
  165. package/dist/menu/menu-radio-group.d.mts +37 -0
  166. package/dist/menu/menu-radio-group.d.mts.map +1 -0
  167. package/dist/menu/menu-radio-group.mjs +72 -0
  168. package/dist/menu/menu-radio-group.mjs.map +1 -0
  169. package/dist/menu/menu-radio-item.d.mts +33 -0
  170. package/dist/menu/menu-radio-item.d.mts.map +1 -0
  171. package/dist/menu/menu-radio-item.mjs +62 -0
  172. package/dist/menu/menu-radio-item.mjs.map +1 -0
  173. package/dist/menu/menu-root.d.mts +54 -0
  174. package/dist/menu/menu-root.d.mts.map +1 -0
  175. package/dist/menu/menu-root.mjs +102 -0
  176. package/dist/menu/menu-root.mjs.map +1 -0
  177. package/dist/menu/menu-submenu-trigger.d.mts +31 -0
  178. package/dist/menu/menu-submenu-trigger.d.mts.map +1 -0
  179. package/dist/menu/menu-submenu-trigger.mjs +92 -0
  180. package/dist/menu/menu-submenu-trigger.mjs.map +1 -0
  181. package/dist/menu/menu-trigger.d.mts +36 -0
  182. package/dist/menu/menu-trigger.d.mts.map +1 -0
  183. package/dist/menu/menu-trigger.mjs +81 -0
  184. package/dist/menu/menu-trigger.mjs.map +1 -0
  185. package/dist/menu/use-menu-item.mjs +54 -0
  186. package/dist/menu/use-menu-item.mjs.map +1 -0
  187. package/dist/popover/index.d.mts +28 -0
  188. package/dist/popover/index.d.mts.map +1 -0
  189. package/dist/popover/index.mjs +28 -0
  190. package/dist/popover/index.mjs.map +1 -0
  191. package/dist/popover/popover-close.d.mts +41 -0
  192. package/dist/popover/popover-close.d.mts.map +1 -0
  193. package/dist/popover/popover-close.mjs +90 -0
  194. package/dist/popover/popover-close.mjs.map +1 -0
  195. package/dist/popover/popover-context.mjs +26 -0
  196. package/dist/popover/popover-context.mjs.map +1 -0
  197. package/dist/popover/popover-description.d.mts +22 -0
  198. package/dist/popover/popover-description.d.mts.map +1 -0
  199. package/dist/popover/popover-description.mjs +28 -0
  200. package/dist/popover/popover-description.mjs.map +1 -0
  201. package/dist/popover/popover-popup.d.mts +82 -0
  202. package/dist/popover/popover-popup.d.mts.map +1 -0
  203. package/dist/popover/popover-popup.mjs +93 -0
  204. package/dist/popover/popover-popup.mjs.map +1 -0
  205. package/dist/popover/popover-root.d.mts +64 -0
  206. package/dist/popover/popover-root.d.mts.map +1 -0
  207. package/dist/popover/popover-root.mjs +39 -0
  208. package/dist/popover/popover-root.mjs.map +1 -0
  209. package/dist/popover/popover-title.d.mts +23 -0
  210. package/dist/popover/popover-title.d.mts.map +1 -0
  211. package/dist/popover/popover-title.mjs +28 -0
  212. package/dist/popover/popover-title.mjs.map +1 -0
  213. package/dist/popover/popover-trigger.d.mts +37 -0
  214. package/dist/popover/popover-trigger.d.mts.map +1 -0
  215. package/dist/popover/popover-trigger.mjs +40 -0
  216. package/dist/popover/popover-trigger.mjs.map +1 -0
  217. package/dist/radio-group/index.d.mts +20 -0
  218. package/dist/radio-group/index.d.mts.map +1 -0
  219. package/dist/radio-group/index.mjs +20 -0
  220. package/dist/radio-group/index.mjs.map +1 -0
  221. package/dist/radio-group/radio-group-context.mjs +13 -0
  222. package/dist/radio-group/radio-group-context.mjs.map +1 -0
  223. package/dist/radio-group/radio-group-item.d.mts +54 -0
  224. package/dist/radio-group/radio-group-item.d.mts.map +1 -0
  225. package/dist/radio-group/radio-group-item.mjs +83 -0
  226. package/dist/radio-group/radio-group-item.mjs.map +1 -0
  227. package/dist/radio-group/radio-group-label.d.mts +21 -0
  228. package/dist/radio-group/radio-group-label.d.mts.map +1 -0
  229. package/dist/radio-group/radio-group-label.mjs +30 -0
  230. package/dist/radio-group/radio-group-label.mjs.map +1 -0
  231. package/dist/radio-group/radio-group-root.d.mts +92 -0
  232. package/dist/radio-group/radio-group-root.d.mts.map +1 -0
  233. package/dist/radio-group/radio-group-root.mjs +137 -0
  234. package/dist/radio-group/radio-group-root.mjs.map +1 -0
  235. package/dist/switch/index.d.mts +3 -0
  236. package/dist/switch/index.mjs +1 -0
  237. package/dist/switch/switch.d.mts +78 -0
  238. package/dist/switch/switch.d.mts.map +1 -0
  239. package/dist/switch/switch.mjs +77 -0
  240. package/dist/switch/switch.mjs.map +1 -0
  241. package/dist/tabs/index.d.mts +24 -0
  242. package/dist/tabs/index.d.mts.map +1 -0
  243. package/dist/tabs/index.mjs +23 -0
  244. package/dist/tabs/index.mjs.map +1 -0
  245. package/dist/tabs/tabs-context.d.mts +7 -0
  246. package/dist/tabs/tabs-context.d.mts.map +1 -0
  247. package/dist/tabs/tabs-context.mjs +14 -0
  248. package/dist/tabs/tabs-context.mjs.map +1 -0
  249. package/dist/tabs/tabs-ids.mjs +39 -0
  250. package/dist/tabs/tabs-ids.mjs.map +1 -0
  251. package/dist/tabs/tabs-indicator.d.mts +32 -0
  252. package/dist/tabs/tabs-indicator.d.mts.map +1 -0
  253. package/dist/tabs/tabs-indicator.mjs +49 -0
  254. package/dist/tabs/tabs-indicator.mjs.map +1 -0
  255. package/dist/tabs/tabs-list.d.mts +34 -0
  256. package/dist/tabs/tabs-list.d.mts.map +1 -0
  257. package/dist/tabs/tabs-list.mjs +130 -0
  258. package/dist/tabs/tabs-list.mjs.map +1 -0
  259. package/dist/tabs/tabs-panel.d.mts +50 -0
  260. package/dist/tabs/tabs-panel.d.mts.map +1 -0
  261. package/dist/tabs/tabs-panel.mjs +81 -0
  262. package/dist/tabs/tabs-panel.mjs.map +1 -0
  263. package/dist/tabs/tabs-root.d.mts +64 -0
  264. package/dist/tabs/tabs-root.d.mts.map +1 -0
  265. package/dist/tabs/tabs-root.mjs +90 -0
  266. package/dist/tabs/tabs-root.mjs.map +1 -0
  267. package/dist/tabs/tabs-tab.d.mts +49 -0
  268. package/dist/tabs/tabs-tab.d.mts.map +1 -0
  269. package/dist/tabs/tabs-tab.mjs +89 -0
  270. package/dist/tabs/tabs-tab.mjs.map +1 -0
  271. package/dist/toggle/index.d.mts +3 -0
  272. package/dist/toggle/index.mjs +1 -0
  273. package/dist/toggle/toggle.d.mts +57 -0
  274. package/dist/toggle/toggle.d.mts.map +1 -0
  275. package/dist/toggle/toggle.mjs +59 -0
  276. package/dist/toggle/toggle.mjs.map +1 -0
  277. package/dist/tooltip/index.d.mts +20 -0
  278. package/dist/tooltip/index.d.mts.map +1 -0
  279. package/dist/tooltip/index.mjs +19 -0
  280. package/dist/tooltip/index.mjs.map +1 -0
  281. package/dist/tooltip/tooltip-context.d.mts +5 -0
  282. package/dist/tooltip/tooltip-context.d.mts.map +1 -0
  283. package/dist/tooltip/tooltip-context.mjs +13 -0
  284. package/dist/tooltip/tooltip-context.mjs.map +1 -0
  285. package/dist/tooltip/tooltip-popup.d.mts +49 -0
  286. package/dist/tooltip/tooltip-popup.d.mts.map +1 -0
  287. package/dist/tooltip/tooltip-popup.mjs +52 -0
  288. package/dist/tooltip/tooltip-popup.mjs.map +1 -0
  289. package/dist/tooltip/tooltip-root.d.mts +41 -0
  290. package/dist/tooltip/tooltip-root.d.mts.map +1 -0
  291. package/dist/tooltip/tooltip-root.mjs +86 -0
  292. package/dist/tooltip/tooltip-root.mjs.map +1 -0
  293. package/dist/tooltip/tooltip-trigger.d.mts +38 -0
  294. package/dist/tooltip/tooltip-trigger.d.mts.map +1 -0
  295. package/dist/tooltip/tooltip-trigger.mjs +55 -0
  296. package/dist/tooltip/tooltip-trigger.mjs.map +1 -0
  297. package/dist/tooltip/use-tooltip-interaction.mjs +119 -0
  298. package/dist/tooltip/use-tooltip-interaction.mjs.map +1 -0
  299. package/package.json +73 -3
@@ -0,0 +1,39 @@
1
+ //#region src/tabs/tabs-ids.ts
2
+ /**
3
+ * Id derivation for Tabs (design section 6). Every part derives its ids from the Root's `useId`
4
+ * and the tab `value`, so there is no registration pass and no extra render.
5
+ *
6
+ * No `"use client"` directive: pure functions, importable from anywhere (conventions 7.1).
7
+ */
8
+ function escapeChar(char) {
9
+ return `_${(char.codePointAt(0) ?? 0).toString(16)}_`;
10
+ }
11
+ /**
12
+ * Makes `value` safe as both an HTML id fragment and a CSS dashed-ident suffix. Every character
13
+ * outside `[A-Za-z0-9-]` becomes `_<hex code point>_`. `_` itself is escaped too (`_5f_`), so the
14
+ * mapping is injective: `"a b"` and `"a_20_"` never share an id.
15
+ */
16
+ function slug(value) {
17
+ return value.replace(/[^A-Za-z0-9-]/gu, escapeChar);
18
+ }
19
+ /**
20
+ * The Root id from `useId` (`_r_1_` on React 19.3) is already a valid id and CSS ident; anything
21
+ * else (a custom `identifierPrefix`) is escaped defensively for the anchor name, keeping `_`.
22
+ */
23
+ function cssSafeRootId(rootId) {
24
+ return rootId.replace(/[^A-Za-z0-9_-]/gu, escapeChar);
25
+ }
26
+ function tabId(rootId, value) {
27
+ return `${rootId}-tab-${slug(value)}`;
28
+ }
29
+ function panelId(rootId, value) {
30
+ return `${rootId}-panel-${slug(value)}`;
31
+ }
32
+ /** `anchor-name` of a Tab, and `position-anchor` of the Indicator that points at it. */
33
+ function anchorName(rootId, value) {
34
+ return `--fsui-tab-${cssSafeRootId(rootId)}-tab-${slug(value)}`;
35
+ }
36
+ //#endregion
37
+ export { anchorName, panelId, slug, tabId };
38
+
39
+ //# sourceMappingURL=tabs-ids.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs-ids.mjs","names":[],"sources":["../../src/tabs/tabs-ids.ts"],"sourcesContent":["/**\n * Id derivation for Tabs (design section 6). Every part derives its ids from the Root's `useId`\n * and the tab `value`, so there is no registration pass and no extra render.\n *\n * No `\"use client\"` directive: pure functions, importable from anywhere (conventions 7.1).\n */\n\nfunction escapeChar(char: string): string {\n return `_${(char.codePointAt(0) ?? 0).toString(16)}_`;\n}\n\n/**\n * Makes `value` safe as both an HTML id fragment and a CSS dashed-ident suffix. Every character\n * outside `[A-Za-z0-9-]` becomes `_<hex code point>_`. `_` itself is escaped too (`_5f_`), so the\n * mapping is injective: `\"a b\"` and `\"a_20_\"` never share an id.\n */\nexport function slug(value: string): string {\n return value.replace(/[^A-Za-z0-9-]/gu, escapeChar);\n}\n\n/**\n * The Root id from `useId` (`_r_1_` on React 19.3) is already a valid id and CSS ident; anything\n * else (a custom `identifierPrefix`) is escaped defensively for the anchor name, keeping `_`.\n */\nfunction cssSafeRootId(rootId: string): string {\n return rootId.replace(/[^A-Za-z0-9_-]/gu, escapeChar);\n}\n\nexport function tabId(rootId: string, value: string): string {\n return `${rootId}-tab-${slug(value)}`;\n}\n\nexport function panelId(rootId: string, value: string): string {\n return `${rootId}-panel-${slug(value)}`;\n}\n\n/** `anchor-name` of a Tab, and `position-anchor` of the Indicator that points at it. */\nexport function anchorName(rootId: string, value: string): `--${string}` {\n return `--fsui-tab-${cssSafeRootId(rootId)}-tab-${slug(value)}`;\n}\n"],"mappings":";;;;;;;AAOA,SAAS,WAAW,MAAsB;CACxC,OAAO,KAAK,KAAK,YAAY,CAAC,KAAK,EAAA,CAAG,SAAS,EAAE,EAAE;AACrD;;;;;;AAOA,SAAgB,KAAK,OAAuB;CAC1C,OAAO,MAAM,QAAQ,mBAAmB,UAAU;AACpD;;;;;AAMA,SAAS,cAAc,QAAwB;CAC7C,OAAO,OAAO,QAAQ,oBAAoB,UAAU;AACtD;AAEA,SAAgB,MAAM,QAAgB,OAAuB;CAC3D,OAAO,GAAG,OAAO,OAAO,KAAK,KAAK;AACpC;AAEA,SAAgB,QAAQ,QAAgB,OAAuB;CAC7D,OAAO,GAAG,OAAO,SAAS,KAAK,KAAK;AACtC;;AAGA,SAAgB,WAAW,QAAgB,OAA8B;CACvE,OAAO,cAAc,cAAc,MAAM,EAAE,OAAO,KAAK,KAAK;AAC9D"}
@@ -0,0 +1,32 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { TabsOrientation } from "./tabs-context.mjs";
3
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
4
+ //#region src/tabs/tabs-indicator.d.ts
5
+ export interface TabsIndicatorState {
6
+ /** Optimistic selected value, or `null` (the indicator is then `hidden`). */
7
+ readonly value: string | null;
8
+ readonly pending: boolean;
9
+ readonly orientation: TabsOrientation;
10
+ }
11
+ export interface TabsIndicatorProps extends Omit<ComponentPropsWithRef<"span">, "hidden" | "className" | "style" | "children"> {
12
+ className?: StateValue<string | undefined, TabsIndicatorState>;
13
+ style?: StateValue<CSSProperties | undefined, TabsIndicatorState>;
14
+ children?: StateValue<ReactNode, TabsIndicatorState>;
15
+ render?: RenderProp<ComponentPropsWithRef<"span">, TabsIndicatorState>;
16
+ }
17
+ /**
18
+ * A decorative marker that follows the optimistic selection. Positioned by CSS Anchor
19
+ * Positioning: it sets `position-anchor` to the selected Tab's `anchor-name`; the consumer's CSS
20
+ * gives it `position: absolute` and `anchor()` insets, and a CSS `transition` on those insets
21
+ * slides it. It must come after the Tabs inside `Tabs.List` (an anchor must precede the
22
+ * positioned element in tree order). No `<ViewTransition>`: it changes only in optimistic (sync)
23
+ * commits, which never start a view transition (design 10.1).
24
+ *
25
+ * Owned keys (conventions 1.2): `aria-hidden`, `hidden`, `data-selected`, `data-pending`.
26
+ */
27
+ export declare function TabsIndicator({ className, style, children, render, ...rest }: TabsIndicatorProps): ReactNode;
28
+ export declare namespace TabsIndicator {
29
+ var displayName: string;
30
+ }
31
+ //#endregion
32
+ //# sourceMappingURL=tabs-indicator.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs-indicator.d.mts","names":[],"sources":["../../src/tabs/tabs-indicator.tsx"],"mappings":";;;;iBASiB;;WAEN;WACA;WACA,aAAa;;iBAGP,2BAA2B,KAC1C;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,+BAA+B;;;;;;;;;;;;wBAarC,gBAAgB,WAAW,OAAO,UAAU,WAAW,QAAQ,qBAAkB;yBAAjF"}
@@ -0,0 +1,49 @@
1
+ "use client";
2
+ import { dataAttr } from "../internal/data-attr.mjs";
3
+ import { resolveStateValue, useRender } from "../internal/use-render.mjs";
4
+ import { useTabsContext } from "./tabs-context.mjs";
5
+ import { anchorName } from "./tabs-ids.mjs";
6
+ //#region src/tabs/tabs-indicator.tsx
7
+ /**
8
+ * A decorative marker that follows the optimistic selection. Positioned by CSS Anchor
9
+ * Positioning: it sets `position-anchor` to the selected Tab's `anchor-name`; the consumer's CSS
10
+ * gives it `position: absolute` and `anchor()` insets, and a CSS `transition` on those insets
11
+ * slides it. It must come after the Tabs inside `Tabs.List` (an anchor must precede the
12
+ * positioned element in tree order). No `<ViewTransition>`: it changes only in optimistic (sync)
13
+ * commits, which never start a view transition (design 10.1).
14
+ *
15
+ * Owned keys (conventions 1.2): `aria-hidden`, `hidden`, `data-selected`, `data-pending`.
16
+ */
17
+ function TabsIndicator({ className, style, children, render, ...rest }) {
18
+ const ctx = useTabsContext("Tabs.Indicator");
19
+ const value = ctx.optimistic;
20
+ const state = {
21
+ value,
22
+ pending: ctx.pending,
23
+ orientation: ctx.orientation
24
+ };
25
+ const anchor = value === null ? void 0 : anchorName(ctx.rootId, value);
26
+ return useRender({
27
+ defaultElement: "span",
28
+ render,
29
+ state,
30
+ className,
31
+ style: (s) => ({
32
+ positionAnchor: anchor,
33
+ ...resolveStateValue(style, s)
34
+ }),
35
+ children,
36
+ props: rest,
37
+ libraryProps: {
38
+ "aria-hidden": true,
39
+ hidden: value === null,
40
+ "data-selected": dataAttr(value !== null),
41
+ "data-pending": dataAttr(ctx.pending)
42
+ }
43
+ });
44
+ }
45
+ TabsIndicator.displayName = "TabsIndicator";
46
+ //#endregion
47
+ export { TabsIndicator };
48
+
49
+ //# sourceMappingURL=tabs-indicator.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs-indicator.mjs","names":[],"sources":["../../src/tabs/tabs-indicator.tsx"],"sourcesContent":["\"use client\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { resolveStateValue, useRender } from \"../internal/use-render.js\";\nimport { useTabsContext } from \"./tabs-context.js\";\nimport type { TabsOrientation } from \"./tabs-context.js\";\nimport { anchorName } from \"./tabs-ids.js\";\n\nexport interface TabsIndicatorState {\n /** Optimistic selected value, or `null` (the indicator is then `hidden`). */\n readonly value: string | null;\n readonly pending: boolean;\n readonly orientation: TabsOrientation;\n}\n\nexport interface TabsIndicatorProps extends Omit<\n ComponentPropsWithRef<\"span\">,\n \"hidden\" | \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, TabsIndicatorState>;\n style?: StateValue<CSSProperties | undefined, TabsIndicatorState>;\n children?: StateValue<ReactNode, TabsIndicatorState>;\n render?: RenderProp<ComponentPropsWithRef<\"span\">, TabsIndicatorState>;\n}\n\n/**\n * A decorative marker that follows the optimistic selection. Positioned by CSS Anchor\n * Positioning: it sets `position-anchor` to the selected Tab's `anchor-name`; the consumer's CSS\n * gives it `position: absolute` and `anchor()` insets, and a CSS `transition` on those insets\n * slides it. It must come after the Tabs inside `Tabs.List` (an anchor must precede the\n * positioned element in tree order). No `<ViewTransition>`: it changes only in optimistic (sync)\n * commits, which never start a view transition (design 10.1).\n *\n * Owned keys (conventions 1.2): `aria-hidden`, `hidden`, `data-selected`, `data-pending`.\n */\nexport function TabsIndicator({ className, style, children, render, ...rest }: TabsIndicatorProps) {\n const ctx = useTabsContext(\"Tabs.Indicator\");\n const value = ctx.optimistic;\n const state: TabsIndicatorState = {\n value,\n pending: ctx.pending,\n orientation: ctx.orientation,\n };\n const anchor = value === null ? undefined : anchorName(ctx.rootId, value);\n\n return useRender<ComponentPropsWithRef<\"span\">, TabsIndicatorState>({\n defaultElement: \"span\",\n render,\n state,\n className,\n // `position-anchor` sits under the consumer's style: the consumer wins.\n style: (s) => ({ positionAnchor: anchor, ...resolveStateValue(style, s) }),\n children,\n props: rest,\n libraryProps: {\n \"aria-hidden\": true,\n hidden: value === null,\n \"data-selected\": dataAttr(value !== null),\n \"data-pending\": dataAttr(ctx.pending),\n },\n });\n}\n\nTabsIndicator.displayName = \"TabsIndicator\";\n"],"mappings":";;;;;;;;;;;;;;;;AAoCA,SAAgB,cAAc,EAAE,WAAW,OAAO,UAAU,QAAQ,GAAG,QAA4B;CACjG,MAAM,MAAM,eAAe,gBAAgB;CAC3C,MAAM,QAAQ,IAAI;CAClB,MAAM,QAA4B;EAChC;EACA,SAAS,IAAI;EACb,aAAa,IAAI;CACnB;CACA,MAAM,SAAS,UAAU,OAAO,KAAA,IAAY,WAAW,IAAI,QAAQ,KAAK;CAExE,OAAO,UAA6D;EAClE,gBAAgB;EAChB;EACA;EACA;EAEA,QAAQ,OAAO;GAAE,gBAAgB;GAAQ,GAAG,kBAAkB,OAAO,CAAC;EAAE;EACxE;EACA,OAAO;EACP,cAAc;GACZ,eAAe;GACf,QAAQ,UAAU;GAClB,iBAAiB,SAAS,UAAU,IAAI;GACxC,gBAAgB,SAAS,IAAI,OAAO;EACtC;CACF,CAAC;AACH;AAEA,cAAc,cAAc"}
@@ -0,0 +1,34 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { TabsOrientation } from "./tabs-context.mjs";
3
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
4
+ //#region src/tabs/tabs-list.d.ts
5
+ export interface TabsListState {
6
+ readonly orientation: TabsOrientation;
7
+ readonly disabled: boolean;
8
+ }
9
+ export interface TabsListProps extends Omit<ComponentPropsWithRef<"div">, "onKeyDown" | "onFocus" | "className" | "style" | "children"> {
10
+ /**
11
+ * Render `focusgroup="tablist …"` and, where the browser implements it, let the browser handle
12
+ * arrow keys, Home and End. Chromium 150+ only; elsewhere the JS path stays active.
13
+ * Default `false`.
14
+ */
15
+ experimental_focusgroup?: boolean;
16
+ className?: StateValue<string | undefined, TabsListState>;
17
+ style?: StateValue<CSSProperties | undefined, TabsListState>;
18
+ children?: StateValue<ReactNode, TabsListState>;
19
+ render?: RenderProp<ComponentPropsWithRef<"div">, TabsListState>;
20
+ }
21
+ /**
22
+ * The `role="tablist"` container. Owns arrow-key / Home / End navigation between Tabs: it only
23
+ * moves focus; automatic activation happens in the Tab's focus handler.
24
+ *
25
+ * Owned keys (a `render` element cannot override them, conventions 1.2): `role`,
26
+ * `aria-orientation`, `data-orientation`, `data-disabled`, `tabIndex`, `onKeyDown`, `onFocus`,
27
+ * and `focusgroup` when `experimental_focusgroup` is on.
28
+ */
29
+ export declare function TabsList({ experimental_focusgroup, className, style, children, render, ...rest }: TabsListProps): import("react").JSX.Element;
30
+ export declare namespace TabsList {
31
+ var displayName: string;
32
+ }
33
+ //#endregion
34
+ //# sourceMappingURL=tabs-list.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs-list.d.mts","names":[],"sources":["../../src/tabs/tabs-list.tsx"],"mappings":";;;;iBAgBiB;WACN,aAAa;WACb;;iBAGM,sBAAsB,KACrC;;;;;;EAQA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,8BAA8B;;;;;;;;;;wBA+CpC,WACd,yBACA,WACA,OACA,UACA,WACG,QACF,gCAAa,IAAA;yBAPA"}
@@ -0,0 +1,130 @@
1
+ "use client";
2
+ import { dataAttr } from "../internal/data-attr.mjs";
3
+ import { useRender } from "../internal/use-render.mjs";
4
+ import { useDevWarning } from "../internal/use-dev-warning.mjs";
5
+ import { TabsListContext, useTabsContext } from "./tabs-context.mjs";
6
+ import { useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
7
+ import { jsx } from "react/jsx-runtime";
8
+ //#region src/tabs/tabs-list.tsx
9
+ function resolveKey(key, orientation, rtl) {
10
+ switch (key) {
11
+ case "Home": return "first";
12
+ case "End": return "last";
13
+ case "ArrowRight": return orientation === "horizontal" ? rtl ? "prev" : "next" : null;
14
+ case "ArrowLeft": return orientation === "horizontal" ? rtl ? "next" : "prev" : null;
15
+ case "ArrowDown": return orientation === "vertical" ? "next" : null;
16
+ case "ArrowUp": return orientation === "vertical" ? "prev" : null;
17
+ default: return null;
18
+ }
19
+ }
20
+ /** The tabs that belong to `list` (not to a nested tablist), in DOM order. Disabled included. */
21
+ function tabsOf(list) {
22
+ return Array.from(list.querySelectorAll("[role=\"tab\"]")).filter((tab) => tab.closest("[role=\"tablist\"]") === list);
23
+ }
24
+ function focusTab(tab) {
25
+ tab.focus({ focusVisible: true });
26
+ }
27
+ /** Engine capabilities never change while the page lives. */
28
+ const subscribeNever = () => () => {};
29
+ /**
30
+ * The `role="tablist"` container. Owns arrow-key / Home / End navigation between Tabs: it only
31
+ * moves focus; automatic activation happens in the Tab's focus handler.
32
+ *
33
+ * Owned keys (a `render` element cannot override them, conventions 1.2): `role`,
34
+ * `aria-orientation`, `data-orientation`, `data-disabled`, `tabIndex`, `onKeyDown`, `onFocus`,
35
+ * and `focusgroup` when `experimental_focusgroup` is on.
36
+ */
37
+ function TabsList({ experimental_focusgroup = false, className, style, children, render, ...rest }) {
38
+ const { orientation, loop, disabled, optimistic } = useTabsContext("Tabs.List");
39
+ const nativeFocusgroup = useSyncExternalStore(subscribeNever, () => experimental_focusgroup && "focusGroup" in HTMLElement.prototype, () => false);
40
+ const listContext = useMemo(() => ({
41
+ focusgroup: experimental_focusgroup,
42
+ nativeFocusgroup
43
+ }), [experimental_focusgroup, nativeFocusgroup]);
44
+ const listRef = useRef(null);
45
+ const [unmatched, setUnmatched] = useState(false);
46
+ useLayoutEffect(() => {
47
+ const list = listRef.current;
48
+ const next = optimistic !== null && list !== null && !tabsOf(list).some((tab) => tab.getAttribute("tabindex") === "0");
49
+ if (next !== unmatched) setUnmatched(next);
50
+ }, [
51
+ optimistic,
52
+ children,
53
+ unmatched
54
+ ]);
55
+ useDevWarning(unmatched, `Tabs: the value ${JSON.stringify(optimistic)} matches no Tabs.Tab. No tab is selected; the tablist itself is the tab stop. Pass null to select nothing on purpose.`);
56
+ const listIsTabStop = optimistic === null || unmatched;
57
+ const state = {
58
+ orientation,
59
+ disabled
60
+ };
61
+ const onKeyDown = (event) => {
62
+ if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey) return;
63
+ const list = event.currentTarget;
64
+ const target = event.target;
65
+ if (target.getAttribute("role") !== "tab" || target.closest("[role=\"tablist\"]") !== list) return;
66
+ const rtl = orientation === "horizontal" && getComputedStyle(list).direction === "rtl";
67
+ const move = resolveKey(event.key, orientation, rtl);
68
+ if (move === null) return;
69
+ if (nativeFocusgroup) return;
70
+ event.preventDefault();
71
+ const tabs = tabsOf(list);
72
+ if (tabs.length === 0) return;
73
+ const index = tabs.indexOf(target);
74
+ const last = tabs.length - 1;
75
+ let nextIndex;
76
+ switch (move) {
77
+ case "first":
78
+ nextIndex = 0;
79
+ break;
80
+ case "last":
81
+ nextIndex = last;
82
+ break;
83
+ case "next":
84
+ nextIndex = index >= last ? loop ? 0 : last : index + 1;
85
+ break;
86
+ case "prev": nextIndex = index <= 0 ? loop ? last : 0 : index - 1;
87
+ }
88
+ const next = tabs[nextIndex];
89
+ if (next !== void 0 && next !== target) focusTab(next);
90
+ };
91
+ const onFocus = (event) => {
92
+ const list = event.currentTarget;
93
+ if (event.target !== list || !listIsTabStop) return;
94
+ const from = event.relatedTarget;
95
+ if (from instanceof Node && list.contains(from)) return;
96
+ const tabs = tabsOf(list);
97
+ const first = tabs.find((tab) => tab.getAttribute("aria-disabled") !== "true") ?? tabs[0];
98
+ if (first !== void 0) focusTab(first);
99
+ };
100
+ const focusgroup = experimental_focusgroup ? `tablist ${orientation === "vertical" ? "block" : "inline"} ${loop ? "wrap" : "nowrap"} nomemory` : void 0;
101
+ const element = useRender({
102
+ defaultElement: "div",
103
+ render,
104
+ state,
105
+ className,
106
+ style,
107
+ children,
108
+ props: rest,
109
+ libraryProps: {
110
+ role: "tablist",
111
+ "aria-orientation": orientation,
112
+ "data-orientation": orientation,
113
+ "data-disabled": dataAttr(disabled),
114
+ tabIndex: listIsTabStop ? 0 : void 0,
115
+ ...focusgroup !== void 0 && { focusgroup },
116
+ onKeyDown,
117
+ onFocus
118
+ },
119
+ ref: listRef
120
+ });
121
+ return /* @__PURE__ */ jsx(TabsListContext, {
122
+ value: listContext,
123
+ children: element
124
+ });
125
+ }
126
+ TabsList.displayName = "TabsList";
127
+ //#endregion
128
+ export { TabsList };
129
+
130
+ //# sourceMappingURL=tabs-list.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs-list.mjs","names":[],"sources":["../../src/tabs/tabs-list.tsx"],"sourcesContent":["\"use client\";\nimport { useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from \"react\";\nimport type {\n ComponentPropsWithRef,\n CSSProperties,\n FocusEvent,\n KeyboardEvent,\n ReactNode,\n} from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { TabsListContext, useTabsContext } from \"./tabs-context.js\";\nimport type { TabsListContextValue, TabsOrientation } from \"./tabs-context.js\";\n\nexport interface TabsListState {\n readonly orientation: TabsOrientation;\n readonly disabled: boolean;\n}\n\nexport interface TabsListProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n \"onKeyDown\" | \"onFocus\" | \"className\" | \"style\" | \"children\"\n> {\n /**\n * Render `focusgroup=\"tablist …\"` and, where the browser implements it, let the browser handle\n * arrow keys, Home and End. Chromium 150+ only; elsewhere the JS path stays active.\n * Default `false`.\n */\n experimental_focusgroup?: boolean;\n className?: StateValue<string | undefined, TabsListState>;\n style?: StateValue<CSSProperties | undefined, TabsListState>;\n children?: StateValue<ReactNode, TabsListState>;\n render?: RenderProp<ComponentPropsWithRef<\"div\">, TabsListState>;\n}\n\ntype Move = \"next\" | \"prev\" | \"first\" | \"last\";\n\nfunction resolveKey(key: string, orientation: TabsOrientation, rtl: boolean): Move | null {\n switch (key) {\n case \"Home\":\n return \"first\";\n case \"End\":\n return \"last\";\n case \"ArrowRight\":\n return orientation === \"horizontal\" ? (rtl ? \"prev\" : \"next\") : null;\n case \"ArrowLeft\":\n return orientation === \"horizontal\" ? (rtl ? \"next\" : \"prev\") : null;\n case \"ArrowDown\":\n return orientation === \"vertical\" ? \"next\" : null;\n case \"ArrowUp\":\n return orientation === \"vertical\" ? \"prev\" : null;\n default:\n return null;\n }\n}\n\n/** The tabs that belong to `list` (not to a nested tablist), in DOM order. Disabled included. */\nfunction tabsOf(list: Element): HTMLElement[] {\n return Array.from(list.querySelectorAll<HTMLElement>('[role=\"tab\"]')).filter(\n (tab) => tab.closest('[role=\"tablist\"]') === list,\n );\n}\n\nfunction focusTab(tab: HTMLElement): void {\n // `focusVisible` shows the ring even where the engine's heuristic would not (survey: adopt).\n tab.focus({ focusVisible: true } as FocusOptions);\n}\n\n/** Engine capabilities never change while the page lives. */\nconst subscribeNever = () => () => {};\n\n/**\n * The `role=\"tablist\"` container. Owns arrow-key / Home / End navigation between Tabs: it only\n * moves focus; automatic activation happens in the Tab's focus handler.\n *\n * Owned keys (a `render` element cannot override them, conventions 1.2): `role`,\n * `aria-orientation`, `data-orientation`, `data-disabled`, `tabIndex`, `onKeyDown`, `onFocus`,\n * and `focusgroup` when `experimental_focusgroup` is on.\n */\nexport function TabsList({\n experimental_focusgroup = false,\n className,\n style,\n children,\n render,\n ...rest\n}: TabsListProps) {\n const ctx = useTabsContext(\"Tabs.List\");\n const { orientation, loop, disabled, optimistic } = ctx;\n\n // Feature detection only when the flag is on, and never during SSR or hydration (conventions\n // 8): the server snapshot is `false`, so hydration markup matches the server and React re-renders\n // with the client answer afterwards. The IDL attribute is camel-cased `focusGroup`\n // (`\"focusgroup\" in HTMLElement.prototype` is always false, issue #43). The Tabs read it too:\n // they drop `tabindex=\"-1\"` on the native path (TabsListContextValue).\n const nativeFocusgroup = useSyncExternalStore(\n subscribeNever,\n () => experimental_focusgroup && \"focusGroup\" in HTMLElement.prototype,\n () => false,\n );\n const listContext = useMemo<TabsListContextValue>(\n () => ({ focusgroup: experimental_focusgroup, nativeFocusgroup }),\n [experimental_focusgroup, nativeFocusgroup],\n );\n\n // A value that matches no Tab (a controlled `value` for a route without a tab, design 11.5)\n // leaves no tab with tabindex=0, so the List takes over as the tab stop exactly as for `null`.\n // Read from the DOM (Tabs register nowhere) whenever the value or the List's children change; a\n // Tab added or removed by a component below that re-renders on its own is picked up on the\n // List's next render. The state only changes when the answer does.\n const listRef = useRef<HTMLElement | null>(null);\n const [unmatched, setUnmatched] = useState(false);\n useLayoutEffect(() => {\n const list = listRef.current;\n const next =\n optimistic !== null &&\n list !== null &&\n !tabsOf(list).some((tab) => tab.getAttribute(\"tabindex\") === \"0\");\n if (next !== unmatched) setUnmatched(next);\n }, [optimistic, children, unmatched]);\n useDevWarning(\n unmatched,\n `Tabs: the value ${JSON.stringify(optimistic)} matches no Tabs.Tab. No tab is selected; the ` +\n \"tablist itself is the tab stop. Pass null to select nothing on purpose.\",\n );\n const listIsTabStop = optimistic === null || unmatched;\n\n const state: TabsListState = { orientation, disabled };\n\n const onKeyDown = (event: KeyboardEvent<HTMLElement>) => {\n if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey) return;\n const list = event.currentTarget;\n const target = event.target as Element;\n if (target.getAttribute(\"role\") !== \"tab\" || target.closest('[role=\"tablist\"]') !== list) {\n return;\n }\n const rtl = orientation === \"horizontal\" && getComputedStyle(list).direction === \"rtl\";\n const move = resolveKey(event.key, orientation, rtl);\n if (move === null) return;\n // The browser moves focus itself where `focusgroup` is implemented.\n if (nativeFocusgroup) return;\n\n event.preventDefault();\n const tabs = tabsOf(list);\n if (tabs.length === 0) return;\n const index = tabs.indexOf(target as HTMLElement);\n const last = tabs.length - 1;\n let nextIndex: number;\n switch (move) {\n case \"first\":\n nextIndex = 0;\n break;\n case \"last\":\n nextIndex = last;\n break;\n case \"next\":\n nextIndex = index >= last ? (loop ? 0 : last) : index + 1;\n break;\n case \"prev\":\n nextIndex = index <= 0 ? (loop ? last : 0) : index - 1;\n break;\n }\n const next = tabs[nextIndex];\n if (next !== undefined && next !== target) focusTab(next);\n };\n\n // With no selected tab (value null or matching no Tab) no Tab has tabindex=0, so the list\n // itself is the tab stop and hands focus to the first enabled tab (design section 8). Only when\n // focus arrives from outside the list: Shift+Tab from a tab inside would otherwise bounce\n // straight back. The redirect never selects: the Tab only dispatches on focus that moves\n // between tabs.\n const onFocus = (event: FocusEvent<HTMLElement>) => {\n const list = event.currentTarget;\n if (event.target !== list || !listIsTabStop) return;\n const from = event.relatedTarget;\n if (from instanceof Node && list.contains(from)) return;\n const tabs = tabsOf(list);\n const first = tabs.find((tab) => tab.getAttribute(\"aria-disabled\") !== \"true\") ?? tabs[0];\n if (first !== undefined) focusTab(first);\n };\n\n const focusgroup = experimental_focusgroup\n ? // `nomemory` + `focusgroupstart` on the selected Tab: Tab always enters on the selected tab\n // (APG), not on the last focused one, which differs in manual activation.\n `tablist ${orientation === \"vertical\" ? \"block\" : \"inline\"} ${loop ? \"wrap\" : \"nowrap\"} nomemory`\n : undefined;\n\n const element = useRender<ComponentPropsWithRef<\"div\">, TabsListState>({\n defaultElement: \"div\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps: {\n role: \"tablist\",\n \"aria-orientation\": orientation,\n \"data-orientation\": orientation,\n \"data-disabled\": dataAttr(disabled),\n tabIndex: listIsTabStop ? 0 : undefined,\n // Owned only while the flag is on; otherwise the key is omitted, not `undefined`, so it is not\n // owned (conventions 1.2).\n ...(focusgroup !== undefined && { focusgroup }),\n onKeyDown,\n onFocus,\n },\n ref: listRef,\n });\n\n return <TabsListContext value={listContext}>{element}</TabsListContext>;\n}\n\nTabsList.displayName = \"TabsList\";\n"],"mappings":";;;;;;;;AAuCA,SAAS,WAAW,KAAa,aAA8B,KAA2B;CACxF,QAAQ,KAAR;EACE,KAAK,QACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,KAAK,cACH,OAAO,gBAAgB,eAAgB,MAAM,SAAS,SAAU;EAClE,KAAK,aACH,OAAO,gBAAgB,eAAgB,MAAM,SAAS,SAAU;EAClE,KAAK,aACH,OAAO,gBAAgB,aAAa,SAAS;EAC/C,KAAK,WACH,OAAO,gBAAgB,aAAa,SAAS;EAC/C,SACE,OAAO;CACX;AACF;;AAGA,SAAS,OAAO,MAA8B;CAC5C,OAAO,MAAM,KAAK,KAAK,iBAA8B,gBAAc,CAAC,CAAC,CAAC,QACnE,QAAQ,IAAI,QAAQ,oBAAkB,MAAM,IAC/C;AACF;AAEA,SAAS,SAAS,KAAwB;CAExC,IAAI,MAAM,EAAE,cAAc,KAAK,CAAiB;AAClD;;AAGA,MAAM,6BAA6B,CAAC;;;;;;;;;AAUpC,SAAgB,SAAS,EACvB,0BAA0B,OAC1B,WACA,OACA,UACA,QACA,GAAG,QACa;CAEhB,MAAM,EAAE,aAAa,MAAM,UAAU,eADzB,eAAe,WAC2B;CAOtD,MAAM,mBAAmB,qBACvB,sBACM,2BAA2B,gBAAgB,YAAY,iBACvD,KACR;CACA,MAAM,cAAc,eACX;EAAE,YAAY;EAAyB;CAAiB,IAC/D,CAAC,yBAAyB,gBAAgB,CAC5C;CAOA,MAAM,UAAU,OAA2B,IAAI;CAC/C,MAAM,CAAC,WAAW,gBAAgB,SAAS,KAAK;CAChD,sBAAsB;EACpB,MAAM,OAAO,QAAQ;EACrB,MAAM,OACJ,eAAe,QACf,SAAS,QACT,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,QAAQ,IAAI,aAAa,UAAU,MAAM,GAAG;EAClE,IAAI,SAAS,WAAW,aAAa,IAAI;CAC3C,GAAG;EAAC;EAAY;EAAU;CAAS,CAAC;CACpC,cACE,WACA,mBAAmB,KAAK,UAAU,UAAU,EAAE,sHAEhD;CACA,MAAM,gBAAgB,eAAe,QAAQ;CAE7C,MAAM,QAAuB;EAAE;EAAa;CAAS;CAErD,MAAM,aAAa,UAAsC;EACvD,IAAI,MAAM,oBAAoB,MAAM,UAAU,MAAM,WAAW,MAAM,SAAS;EAC9E,MAAM,OAAO,MAAM;EACnB,MAAM,SAAS,MAAM;EACrB,IAAI,OAAO,aAAa,MAAM,MAAM,SAAS,OAAO,QAAQ,oBAAkB,MAAM,MAClF;EAEF,MAAM,MAAM,gBAAgB,gBAAgB,iBAAiB,IAAI,CAAC,CAAC,cAAc;EACjF,MAAM,OAAO,WAAW,MAAM,KAAK,aAAa,GAAG;EACnD,IAAI,SAAS,MAAM;EAEnB,IAAI,kBAAkB;EAEtB,MAAM,eAAe;EACrB,MAAM,OAAO,OAAO,IAAI;EACxB,IAAI,KAAK,WAAW,GAAG;EACvB,MAAM,QAAQ,KAAK,QAAQ,MAAqB;EAChD,MAAM,OAAO,KAAK,SAAS;EAC3B,IAAI;EACJ,QAAQ,MAAR;GACE,KAAK;IACH,YAAY;IACZ;GACF,KAAK;IACH,YAAY;IACZ;GACF,KAAK;IACH,YAAY,SAAS,OAAQ,OAAO,IAAI,OAAQ,QAAQ;IACxD;GACF,KAAK,QACH,YAAY,SAAS,IAAK,OAAO,OAAO,IAAK,QAAQ;EAEzD;EACA,MAAM,OAAO,KAAK;EAClB,IAAI,SAAS,KAAA,KAAa,SAAS,QAAQ,SAAS,IAAI;CAC1D;CAOA,MAAM,WAAW,UAAmC;EAClD,MAAM,OAAO,MAAM;EACnB,IAAI,MAAM,WAAW,QAAQ,CAAC,eAAe;EAC7C,MAAM,OAAO,MAAM;EACnB,IAAI,gBAAgB,QAAQ,KAAK,SAAS,IAAI,GAAG;EACjD,MAAM,OAAO,OAAO,IAAI;EACxB,MAAM,QAAQ,KAAK,MAAM,QAAQ,IAAI,aAAa,eAAe,MAAM,MAAM,KAAK,KAAK;EACvF,IAAI,UAAU,KAAA,GAAW,SAAS,KAAK;CACzC;CAEA,MAAM,aAAa,0BAGf,WAAW,gBAAgB,aAAa,UAAU,SAAS,GAAG,OAAO,SAAS,SAAS,aACvF,KAAA;CAEJ,MAAM,UAAU,UAAuD;EACrE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP,cAAc;GACZ,MAAM;GACN,oBAAoB;GACpB,oBAAoB;GACpB,iBAAiB,SAAS,QAAQ;GAClC,UAAU,gBAAgB,IAAI,KAAA;GAG9B,GAAI,eAAe,KAAA,KAAa,EAAE,WAAW;GAC7C;GACA;EACF;EACA,KAAK;CACP,CAAC;CAED,OAAO,oBAAC,iBAAD;EAAiB,OAAO;EAAc,UAAA;CAAyB,CAAA;AACxE;AAEA,SAAS,cAAc"}
@@ -0,0 +1,50 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { TabsOrientation } from "./tabs-context.mjs";
3
+ import { CSSProperties, ComponentPropsWithRef, ReactNode, ViewTransitionClass } from "react";
4
+ //#region src/tabs/tabs-panel.d.ts
5
+ export interface TabsPanelState {
6
+ readonly value: string;
7
+ /** Committed: true only once the action has resolved and the render has committed. */
8
+ readonly selected: boolean;
9
+ readonly orientation: TabsOrientation;
10
+ }
11
+ export interface TabsPanelProps extends Omit<ComponentPropsWithRef<"div">, "id" | "hidden" | "className" | "style" | "children"> {
12
+ /** The tab value this panel belongs to. */
13
+ value: string;
14
+ /**
15
+ * What happens while this panel is not the committed selection.
16
+ * - `false` (default): the panel is not rendered.
17
+ * - `true`: rendered inside `<Activity mode="hidden">`: DOM and state are preserved, effects
18
+ * are stopped, content is pre-rendered at low priority. Not part of server-rendered HTML.
19
+ * - `"until-found"`: rendered with `hidden="until-found"`: DOM, state and effects stay live,
20
+ * the content is in server-rendered HTML, and find-in-page or a text fragment selects the
21
+ * tab (`details.reason === "platform"`).
22
+ */
23
+ keepMounted?: boolean | "until-found";
24
+ /**
25
+ * Forwarded to the Panel's `<ViewTransition default>`. Default: no boundary.
26
+ *
27
+ * Static: without `transition` the part renders no `<ViewTransition>`; changing it between
28
+ * `undefined` and a value remounts the element. Any value, `"none"` included, makes a mount or
29
+ * `<Activity>` reveal of this part inside a transition start a document view transition: the
30
+ * page cross-fades for about 250 ms and the next transition commit waits for it (issue #35).
31
+ */
32
+ transition?: ViewTransitionClass;
33
+ className?: StateValue<string | undefined, TabsPanelState>;
34
+ style?: StateValue<CSSProperties | undefined, TabsPanelState>;
35
+ children?: StateValue<ReactNode, TabsPanelState>;
36
+ render?: RenderProp<ComponentPropsWithRef<"div">, TabsPanelState>;
37
+ }
38
+ /**
39
+ * A `div[role=tabpanel]`. Renders the **committed** selection, so a newly selected panel that
40
+ * suspends keeps the current panel on screen while the Root shows `data-pending`.
41
+ *
42
+ * Owned keys (conventions 1.2): `role`, `id`, `aria-labelledby` (token-merged), `data-selected`,
43
+ * `hidden`.
44
+ */
45
+ export declare function TabsPanel(props: TabsPanelProps): import("react").JSX.Element | null;
46
+ export declare namespace TabsPanel {
47
+ var displayName: string;
48
+ }
49
+ //#endregion
50
+ //# sourceMappingURL=tabs-panel.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs-panel.d.mts","names":[],"sources":["../../src/tabs/tabs-panel.tsx"],"mappings":";;;;iBAYiB;WACN;;WAEA;WACA,aAAa;;iBAGP,uBAAuB,KACtC;;EAIA;;;;;;;;;;EAUA;;;;;;;;;EASA,aAAa;EACb,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,8BAA8B;;;;;;;;;wBAUpC,UAAU,OAAO,iCAAc,IAAA;yBAA/B"}
@@ -0,0 +1,81 @@
1
+ "use client";
2
+ import { useTransitionBoundary } from "../internal/transition-boundary.mjs";
3
+ import { dataAttr } from "../internal/data-attr.mjs";
4
+ import { useRender } from "../internal/use-render.mjs";
5
+ import { useTabsContext } from "./tabs-context.mjs";
6
+ import { panelId, tabId } from "./tabs-ids.mjs";
7
+ import { useUntilFound } from "../internal/use-until-found.mjs";
8
+ import { Activity } from "react";
9
+ import { jsx } from "react/jsx-runtime";
10
+ //#region src/tabs/tabs-panel.tsx
11
+ /**
12
+ * A `div[role=tabpanel]`. Renders the **committed** selection, so a newly selected panel that
13
+ * suspends keeps the current panel on screen while the Root shows `data-pending`.
14
+ *
15
+ * Owned keys (conventions 1.2): `role`, `id`, `aria-labelledby` (token-merged), `data-selected`,
16
+ * `hidden`.
17
+ */
18
+ function TabsPanel(props) {
19
+ const ctx = useTabsContext("Tabs.Panel");
20
+ const { value, keepMounted = false } = props;
21
+ const selected = ctx.committed === value;
22
+ if (keepMounted === false) return selected ? /* @__PURE__ */ jsx(PanelElement, {
23
+ ...props,
24
+ ctx,
25
+ selected: true,
26
+ untilFound: false
27
+ }) : null;
28
+ if (keepMounted === true) return /* @__PURE__ */ jsx(Activity, {
29
+ mode: selected ? "visible" : "hidden",
30
+ children: /* @__PURE__ */ jsx(PanelElement, {
31
+ ...props,
32
+ ctx,
33
+ selected,
34
+ untilFound: false
35
+ })
36
+ });
37
+ return /* @__PURE__ */ jsx(PanelElement, {
38
+ ...props,
39
+ ctx,
40
+ selected,
41
+ untilFound: true
42
+ });
43
+ }
44
+ TabsPanel.displayName = "TabsPanel";
45
+ /** Split out so that an unrendered panel resolves none of its function props. */
46
+ function PanelElement({ ctx, selected, untilFound, value, keepMounted: _keepMounted, transition, className, style, children, render, ...rest }) {
47
+ const untilFoundRef = useUntilFound({
48
+ active: untilFound && !selected && ctx.optimistic !== value,
49
+ onMatch: (event) => ctx.select(value, event, "platform")
50
+ });
51
+ const state = {
52
+ value,
53
+ selected,
54
+ orientation: ctx.orientation
55
+ };
56
+ const element = useRender({
57
+ defaultElement: "div",
58
+ render,
59
+ state,
60
+ className,
61
+ style,
62
+ children,
63
+ props: {
64
+ tabIndex: 0,
65
+ ...rest
66
+ },
67
+ libraryProps: {
68
+ role: "tabpanel",
69
+ id: panelId(ctx.rootId, value),
70
+ "aria-labelledby": tabId(ctx.rootId, value),
71
+ "data-selected": dataAttr(selected),
72
+ hidden: untilFound ? !selected : void 0
73
+ },
74
+ ref: untilFound ? untilFoundRef : void 0
75
+ });
76
+ return useTransitionBoundary("Tabs.Panel", transition, element);
77
+ }
78
+ //#endregion
79
+ export { TabsPanel };
80
+
81
+ //# sourceMappingURL=tabs-panel.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs-panel.mjs","names":[],"sources":["../../src/tabs/tabs-panel.tsx"],"sourcesContent":["\"use client\";\nimport { Activity } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode, ViewTransitionClass } from \"react\";\nimport { useTransitionBoundary } from \"../internal/transition-boundary.js\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { useUntilFound } from \"../internal/use-until-found.js\";\nimport { useTabsContext } from \"./tabs-context.js\";\nimport type { TabsContextValue, TabsOrientation } from \"./tabs-context.js\";\nimport { panelId, tabId } from \"./tabs-ids.js\";\n\nexport interface TabsPanelState {\n readonly value: string;\n /** Committed: true only once the action has resolved and the render has committed. */\n readonly selected: boolean;\n readonly orientation: TabsOrientation;\n}\n\nexport interface TabsPanelProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n \"id\" | \"hidden\" | \"className\" | \"style\" | \"children\"\n> {\n /** The tab value this panel belongs to. */\n value: string;\n /**\n * What happens while this panel is not the committed selection.\n * - `false` (default): the panel is not rendered.\n * - `true`: rendered inside `<Activity mode=\"hidden\">`: DOM and state are preserved, effects\n * are stopped, content is pre-rendered at low priority. Not part of server-rendered HTML.\n * - `\"until-found\"`: rendered with `hidden=\"until-found\"`: DOM, state and effects stay live,\n * the content is in server-rendered HTML, and find-in-page or a text fragment selects the\n * tab (`details.reason === \"platform\"`).\n */\n keepMounted?: boolean | \"until-found\";\n /**\n * Forwarded to the Panel's `<ViewTransition default>`. Default: no boundary.\n *\n * Static: without `transition` the part renders no `<ViewTransition>`; changing it between\n * `undefined` and a value remounts the element. Any value, `\"none\"` included, makes a mount or\n * `<Activity>` reveal of this part inside a transition start a document view transition: the\n * page cross-fades for about 250 ms and the next transition commit waits for it (issue #35).\n */\n transition?: ViewTransitionClass;\n className?: StateValue<string | undefined, TabsPanelState>;\n style?: StateValue<CSSProperties | undefined, TabsPanelState>;\n children?: StateValue<ReactNode, TabsPanelState>;\n render?: RenderProp<ComponentPropsWithRef<\"div\">, TabsPanelState>;\n}\n\n/**\n * A `div[role=tabpanel]`. Renders the **committed** selection, so a newly selected panel that\n * suspends keeps the current panel on screen while the Root shows `data-pending`.\n *\n * Owned keys (conventions 1.2): `role`, `id`, `aria-labelledby` (token-merged), `data-selected`,\n * `hidden`.\n */\nexport function TabsPanel(props: TabsPanelProps) {\n const ctx = useTabsContext(\"Tabs.Panel\");\n const { value, keepMounted = false } = props;\n const selected = ctx.committed === value;\n\n if (keepMounted === false) {\n return selected ? <PanelElement {...props} ctx={ctx} selected untilFound={false} /> : null;\n }\n if (keepMounted === true) {\n // The mode only changes when `committed` does, i.e. inside the Root's transition, so the\n // nested `<ViewTransition>` gets enter / exit (conventions 5.5).\n return (\n <Activity mode={selected ? \"visible\" : \"hidden\"}>\n <PanelElement {...props} ctx={ctx} selected={selected} untilFound={false} />\n </Activity>\n );\n }\n return <PanelElement {...props} ctx={ctx} selected={selected} untilFound />;\n}\n\nTabsPanel.displayName = \"TabsPanel\";\n\ninterface PanelElementProps extends TabsPanelProps {\n ctx: TabsContextValue;\n selected: boolean;\n untilFound: boolean;\n}\n\n/** Split out so that an unrendered panel resolves none of its function props. */\nfunction PanelElement({\n ctx,\n selected,\n untilFound,\n value,\n keepMounted: _keepMounted,\n transition,\n className,\n style,\n children,\n render,\n ...rest\n}: PanelElementProps) {\n const untilFoundRef = useUntilFound<HTMLDivElement>({\n // Not while this panel is the optimistic target: the browser has revealed it (or the user\n // selected its tab) and the action is pending; re-hiding it would undo the reveal. A\n // rejection or veto reverts the optimistic value, which re-activates the hook.\n active: untilFound && !selected && ctx.optimistic !== value,\n onMatch: (event) => ctx.select(value, event, \"platform\"),\n });\n\n const state: TabsPanelState = { value, selected, orientation: ctx.orientation };\n\n const element = useRender<ComponentPropsWithRef<\"div\">, TabsPanelState>({\n defaultElement: \"div\",\n render,\n state,\n className,\n style,\n children,\n // `tabIndex` defaults to 0 (APG: the panel is a tab stop) and passes through.\n props: { tabIndex: 0, ...rest },\n libraryProps: {\n role: \"tabpanel\",\n id: panelId(ctx.rootId, value),\n \"aria-labelledby\": tabId(ctx.rootId, value),\n \"data-selected\": dataAttr(selected),\n // Boolean form for SSR; `useUntilFound` upgrades it to \"until-found\" (conventions 7.4).\n hidden: untilFound ? !selected : undefined,\n },\n ref: untilFound ? untilFoundRef : undefined,\n });\n\n return useTransitionBoundary(\"Tabs.Panel\", transition, element);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAyDA,SAAgB,UAAU,OAAuB;CAC/C,MAAM,MAAM,eAAe,YAAY;CACvC,MAAM,EAAE,OAAO,cAAc,UAAU;CACvC,MAAM,WAAW,IAAI,cAAc;CAEnC,IAAI,gBAAgB,OAClB,OAAO,WAAW,oBAAC,cAAD;EAAc,GAAI;EAAY;EAAK,UAAA;EAAS,YAAY;CAAQ,CAAA,IAAI;CAExF,IAAI,gBAAgB,MAGlB,OACE,oBAAC,UAAD;EAAU,MAAM,WAAW,YAAY;EACrC,UAAA,oBAAC,cAAD;GAAc,GAAI;GAAY;GAAe;GAAU,YAAY;EAAQ,CAAA;CACnE,CAAA;CAGd,OAAO,oBAAC,cAAD;EAAc,GAAI;EAAY;EAAe;EAAU,YAAA;CAAY,CAAA;AAC5E;AAEA,UAAU,cAAc;;AASxB,SAAS,aAAa,EACpB,KACA,UACA,YACA,OACA,aAAa,cACb,YACA,WACA,OACA,UACA,QACA,GAAG,QACiB;CACpB,MAAM,gBAAgB,cAA8B;EAIlD,QAAQ,cAAc,CAAC,YAAY,IAAI,eAAe;EACtD,UAAU,UAAU,IAAI,OAAO,OAAO,OAAO,UAAU;CACzD,CAAC;CAED,MAAM,QAAwB;EAAE;EAAO;EAAU,aAAa,IAAI;CAAY;CAE9E,MAAM,UAAU,UAAwD;EACtE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EAEA,OAAO;GAAE,UAAU;GAAG,GAAG;EAAK;EAC9B,cAAc;GACZ,MAAM;GACN,IAAI,QAAQ,IAAI,QAAQ,KAAK;GAC7B,mBAAmB,MAAM,IAAI,QAAQ,KAAK;GAC1C,iBAAiB,SAAS,QAAQ;GAElC,QAAQ,aAAa,CAAC,WAAW,KAAA;EACnC;EACA,KAAK,aAAa,gBAAgB,KAAA;CACpC,CAAC;CAED,OAAO,sBAAsB,cAAc,YAAY,OAAO;AAChE"}
@@ -0,0 +1,64 @@
1
+ import { ActionDetails, ChangeAction, RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { TabsActivationMode, TabsChangeReason, TabsOrientation } from "./tabs-context.mjs";
3
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
4
+ //#region src/tabs/tabs-root.d.ts
5
+ /** Details passed as the second argument of `valueChangeAction`. */
6
+ export interface TabsValueChangeDetails extends ActionDetails {
7
+ /**
8
+ * `"activate"`: the user selected the tab (click, Enter/Space, or focus in automatic mode).
9
+ * `"platform"`: the browser revealed a `keepMounted="until-found"` panel through find-in-page or a
10
+ * text fragment (`details.event` is the `beforematch` event).
11
+ */
12
+ readonly reason: TabsChangeReason;
13
+ }
14
+ export interface TabsRootState {
15
+ /** Optimistic selected value, or `null`. Content rendered from it must not suspend. */
16
+ readonly value: string | null;
17
+ /** An action dispatched through this Tabs is in flight. */
18
+ readonly pending: boolean;
19
+ readonly orientation: TabsOrientation;
20
+ readonly disabled: boolean;
21
+ }
22
+ export interface TabsRootProps extends Omit<ComponentPropsWithRef<"div">, "className" | "style" | "children" | "defaultValue"> {
23
+ /** Controlled selected value. `null` selects nothing; `undefined` means uncontrolled. */
24
+ value?: string | null;
25
+ /** Initial value in uncontrolled mode. Defaults to `null` (no panel shown). */
26
+ defaultValue?: string | null;
27
+ /**
28
+ * Runs inside the Root's transition whenever the user selects a tab. May be sync or async;
29
+ * rejections reach the nearest Error Boundary and roll the selection back.
30
+ *
31
+ * With `activationMode="automatic"` every arrow key press calls it; prefer `"manual"` when the
32
+ * action awaits a server round trip. A `setState` placed after your own `await` is not a
33
+ * Transition update; wrap it in `startTransition` (React limitation).
34
+ */
35
+ valueChangeAction?: ChangeAction<string, TabsValueChangeDetails>;
36
+ /** Layout and arrow-key axis. Default `"horizontal"`. */
37
+ orientation?: TabsOrientation;
38
+ /**
39
+ * `"automatic"`: selection follows focus. `"manual"`: Enter, Space or click selects.
40
+ * Default `"automatic"`.
41
+ */
42
+ activationMode?: TabsActivationMode;
43
+ /** Whether arrow keys wrap from the last tab to the first. Default `true`. */
44
+ loop?: boolean;
45
+ /** Disables every Tab (focusable, never dispatches). */
46
+ disabled?: boolean;
47
+ className?: StateValue<string | undefined, TabsRootState>;
48
+ style?: StateValue<CSSProperties | undefined, TabsRootState>;
49
+ children?: StateValue<ReactNode, TabsRootState>;
50
+ render?: RenderProp<ComponentPropsWithRef<"div">, TabsRootState>;
51
+ }
52
+ /**
53
+ * Owns the Tabs state (`value`), the transition, the ids and the context. Renders a `div` with
54
+ * `data-pending` while a selection is in flight (including a suspending panel's wait).
55
+ *
56
+ * Owned keys (conventions 1.2): `data-pending`, `data-orientation`, `data-disabled`.
57
+ */
58
+ export declare function TabsRoot({ value, defaultValue, valueChangeAction, orientation, activationMode, loop, disabled, className, style, children, render, ...rest }: TabsRootProps): import("react").JSX.Element;
59
+ export declare namespace TabsRoot {
60
+ var displayName: string;
61
+ }
62
+ //#endregion
63
+ export type { TabsActivationMode, TabsOrientation };
64
+ //# sourceMappingURL=tabs-root.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs-root.d.mts","names":[],"sources":["../../src/tabs/tabs-root.tsx"],"mappings":";;;;;iBAsBiB,+BAA+B;;;;;;WAMrC,QAAQ;;iBAGF;;WAEN;;WAEA;WACA,aAAa;WACb;;iBAGM,sBAAsB,KACrC;;EAIA;;EAEA;;;;;;;;;EASA,oBAAoB,qBAAqB;;EAEzC,cAAc;;;;;EAKd,iBAAiB;;EAEjB;;EAEA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,8BAA8B;;;;;;;;wBAkBpC,WACd,OACA,cACA,mBACA,aACA,gBACA,MACA,UACA,WACA,OACA,UACA,WACG,QACF,gCAAa,IAAA;yBAbA"}