@zyncat/ui 0.8.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 (334) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +133 -0
  3. package/dist/alert.css +132 -0
  4. package/dist/alert.d.ts +46 -0
  5. package/dist/alert.js +80 -0
  6. package/dist/alert.js.map +1 -0
  7. package/dist/avatar-group.d.ts +24 -0
  8. package/dist/avatar-group.js +23 -0
  9. package/dist/avatar-group.js.map +1 -0
  10. package/dist/avatar.css +200 -0
  11. package/dist/avatar.d.ts +35 -0
  12. package/dist/avatar.js +67 -0
  13. package/dist/avatar.js.map +1 -0
  14. package/dist/badge.css +182 -0
  15. package/dist/badge.d.ts +34 -0
  16. package/dist/badge.js +4 -0
  17. package/dist/badge.js.map +1 -0
  18. package/dist/button.css +269 -0
  19. package/dist/button.d.ts +44 -0
  20. package/dist/button.js +4 -0
  21. package/dist/button.js.map +1 -0
  22. package/dist/checkbox.css +177 -0
  23. package/dist/checkbox.d.ts +35 -0
  24. package/dist/checkbox.js +5 -0
  25. package/dist/checkbox.js.map +1 -0
  26. package/dist/chunk-2ERBBFZS.js +12 -0
  27. package/dist/chunk-2ERBBFZS.js.map +1 -0
  28. package/dist/chunk-2HORSLXJ.js +191 -0
  29. package/dist/chunk-2HORSLXJ.js.map +1 -0
  30. package/dist/chunk-36WVAQKL.js +40 -0
  31. package/dist/chunk-36WVAQKL.js.map +1 -0
  32. package/dist/chunk-3SBCBYB7.js +575 -0
  33. package/dist/chunk-3SBCBYB7.js.map +1 -0
  34. package/dist/chunk-4R2GQO43.js +125 -0
  35. package/dist/chunk-4R2GQO43.js.map +1 -0
  36. package/dist/chunk-64UU4LGU.js +258 -0
  37. package/dist/chunk-64UU4LGU.js.map +1 -0
  38. package/dist/chunk-7SZCJ7WI.js +35 -0
  39. package/dist/chunk-7SZCJ7WI.js.map +1 -0
  40. package/dist/chunk-BIDVNLJO.js +38 -0
  41. package/dist/chunk-BIDVNLJO.js.map +1 -0
  42. package/dist/chunk-CONSWNIV.js +17 -0
  43. package/dist/chunk-CONSWNIV.js.map +1 -0
  44. package/dist/chunk-EQ32F2QJ.js +75 -0
  45. package/dist/chunk-EQ32F2QJ.js.map +1 -0
  46. package/dist/chunk-GY7JOPUQ.js +111 -0
  47. package/dist/chunk-GY7JOPUQ.js.map +1 -0
  48. package/dist/chunk-I4XQ4PV2.js +30 -0
  49. package/dist/chunk-I4XQ4PV2.js.map +1 -0
  50. package/dist/chunk-JSKXZMLH.js +67 -0
  51. package/dist/chunk-JSKXZMLH.js.map +1 -0
  52. package/dist/chunk-L3Q3VDIW.js +295 -0
  53. package/dist/chunk-L3Q3VDIW.js.map +1 -0
  54. package/dist/chunk-LDC3RIYA.js +62 -0
  55. package/dist/chunk-LDC3RIYA.js.map +1 -0
  56. package/dist/chunk-MKIQFK7S.js +43 -0
  57. package/dist/chunk-MKIQFK7S.js.map +1 -0
  58. package/dist/chunk-MMRIQNEN.js +20 -0
  59. package/dist/chunk-MMRIQNEN.js.map +1 -0
  60. package/dist/chunk-MXOPCFWR.js +59 -0
  61. package/dist/chunk-MXOPCFWR.js.map +1 -0
  62. package/dist/chunk-NEF5XAHG.js +17 -0
  63. package/dist/chunk-NEF5XAHG.js.map +1 -0
  64. package/dist/chunk-NNXJCLTV.js +22 -0
  65. package/dist/chunk-NNXJCLTV.js.map +1 -0
  66. package/dist/chunk-O4N2OBTR.js +94 -0
  67. package/dist/chunk-O4N2OBTR.js.map +1 -0
  68. package/dist/chunk-PMECHR3A.js +38 -0
  69. package/dist/chunk-PMECHR3A.js.map +1 -0
  70. package/dist/chunk-QDK66S4G.js +44 -0
  71. package/dist/chunk-QDK66S4G.js.map +1 -0
  72. package/dist/chunk-QGNC7LH5.js +8 -0
  73. package/dist/chunk-QGNC7LH5.js.map +1 -0
  74. package/dist/chunk-QMSJZCK3.js +398 -0
  75. package/dist/chunk-QMSJZCK3.js.map +1 -0
  76. package/dist/chunk-S5ZTZANS.js +57 -0
  77. package/dist/chunk-S5ZTZANS.js.map +1 -0
  78. package/dist/chunk-S7B34VGN.js +14 -0
  79. package/dist/chunk-S7B34VGN.js.map +1 -0
  80. package/dist/chunk-TCUCYFT7.js +25 -0
  81. package/dist/chunk-TCUCYFT7.js.map +1 -0
  82. package/dist/chunk-TYHR4VNM.js +193 -0
  83. package/dist/chunk-TYHR4VNM.js.map +1 -0
  84. package/dist/chunk-U5QQZC3U.js +56 -0
  85. package/dist/chunk-U5QQZC3U.js.map +1 -0
  86. package/dist/chunk-UE63OOVG.js +13 -0
  87. package/dist/chunk-UE63OOVG.js.map +1 -0
  88. package/dist/chunk-VTJTGVZZ.js +22 -0
  89. package/dist/chunk-VTJTGVZZ.js.map +1 -0
  90. package/dist/chunk-W3SHPKSB.js +49 -0
  91. package/dist/chunk-W3SHPKSB.js.map +1 -0
  92. package/dist/chunk-XZAEIQDR.js +188 -0
  93. package/dist/chunk-XZAEIQDR.js.map +1 -0
  94. package/dist/chunk-YGGCQNNU.js +92 -0
  95. package/dist/chunk-YGGCQNNU.js.map +1 -0
  96. package/dist/collapse.css +88 -0
  97. package/dist/collapse.d.ts +46 -0
  98. package/dist/collapse.js +5 -0
  99. package/dist/collapse.js.map +1 -0
  100. package/dist/count-badge.d.ts +13 -0
  101. package/dist/count-badge.js +13 -0
  102. package/dist/count-badge.js.map +1 -0
  103. package/dist/date-field.d.ts +25 -0
  104. package/dist/date-field.js +87 -0
  105. package/dist/date-field.js.map +1 -0
  106. package/dist/date-picker.css +595 -0
  107. package/dist/date-range-field.d.ts +33 -0
  108. package/dist/date-range-field.js +404 -0
  109. package/dist/date-range-field.js.map +1 -0
  110. package/dist/datetime-field.d.ts +29 -0
  111. package/dist/datetime-field.js +155 -0
  112. package/dist/datetime-field.js.map +1 -0
  113. package/dist/dialog.css +180 -0
  114. package/dist/dialog.d.ts +42 -0
  115. package/dist/dialog.js +128 -0
  116. package/dist/dialog.js.map +1 -0
  117. package/dist/dom-props-C0jrllBw.d.ts +5 -0
  118. package/dist/field-chrome-CMPOQSLs.d.ts +28 -0
  119. package/dist/field-shell-Cuda-feL.d.ts +24 -0
  120. package/dist/glide.d.ts +15 -0
  121. package/dist/glide.js +5 -0
  122. package/dist/glide.js.map +1 -0
  123. package/dist/icon.css +33 -0
  124. package/dist/input.css +333 -0
  125. package/dist/mcp.js +359 -0
  126. package/dist/motion-devtools.css +265 -0
  127. package/dist/motion-devtools.d.ts +51 -0
  128. package/dist/motion-devtools.js +354 -0
  129. package/dist/motion-devtools.js.map +1 -0
  130. package/dist/motion-scale-Canq4stZ.d.ts +18 -0
  131. package/dist/motion-tokens.d.ts +28 -0
  132. package/dist/motion-tokens.js +3 -0
  133. package/dist/motion-tokens.js.map +1 -0
  134. package/dist/multi-select.d.ts +44 -0
  135. package/dist/multi-select.js +109 -0
  136. package/dist/multi-select.js.map +1 -0
  137. package/dist/number-field.d.ts +36 -0
  138. package/dist/number-field.js +112 -0
  139. package/dist/number-field.js.map +1 -0
  140. package/dist/odometer.css +62 -0
  141. package/dist/otp-field.d.ts +31 -0
  142. package/dist/otp-field.js +98 -0
  143. package/dist/otp-field.js.map +1 -0
  144. package/dist/overlay.css +23 -0
  145. package/dist/pagination.css +43 -0
  146. package/dist/pagination.d.ts +29 -0
  147. package/dist/pagination.js +107 -0
  148. package/dist/pagination.js.map +1 -0
  149. package/dist/popover.css +69 -0
  150. package/dist/popover.d.ts +35 -0
  151. package/dist/popover.js +15 -0
  152. package/dist/popover.js.map +1 -0
  153. package/dist/radio-group.css +300 -0
  154. package/dist/radio-group.d.ts +52 -0
  155. package/dist/radio-group.js +115 -0
  156. package/dist/radio-group.js.map +1 -0
  157. package/dist/select.css +416 -0
  158. package/dist/select.d.ts +49 -0
  159. package/dist/select.js +99 -0
  160. package/dist/select.js.map +1 -0
  161. package/dist/sheet.css +26 -0
  162. package/dist/sheet.d.ts +31 -0
  163. package/dist/sheet.js +14 -0
  164. package/dist/sheet.js.map +1 -0
  165. package/dist/status-badge.d.ts +19 -0
  166. package/dist/status-badge.js +72 -0
  167. package/dist/status-badge.js.map +1 -0
  168. package/dist/table.css +348 -0
  169. package/dist/table.d.ts +75 -0
  170. package/dist/table.js +262 -0
  171. package/dist/table.js.map +1 -0
  172. package/dist/tabs.css +208 -0
  173. package/dist/tabs.d.ts +71 -0
  174. package/dist/tabs.js +181 -0
  175. package/dist/tabs.js.map +1 -0
  176. package/dist/tag.css +248 -0
  177. package/dist/tag.d.ts +52 -0
  178. package/dist/tag.js +72 -0
  179. package/dist/tag.js.map +1 -0
  180. package/dist/text-field.d.ts +28 -0
  181. package/dist/text-field.js +69 -0
  182. package/dist/text-field.js.map +1 -0
  183. package/dist/textarea.css +219 -0
  184. package/dist/textarea.d.ts +38 -0
  185. package/dist/textarea.js +144 -0
  186. package/dist/textarea.js.map +1 -0
  187. package/dist/time-field.d.ts +28 -0
  188. package/dist/time-field.js +66 -0
  189. package/dist/time-field.js.map +1 -0
  190. package/dist/timing-7JA_zQHP.d.ts +27 -0
  191. package/dist/toast-store.d.ts +86 -0
  192. package/dist/toast-store.js +3 -0
  193. package/dist/toast-store.js.map +1 -0
  194. package/dist/toast.css +257 -0
  195. package/dist/toast.d.ts +12 -0
  196. package/dist/toast.js +310 -0
  197. package/dist/toast.js.map +1 -0
  198. package/dist/toggle-tag.d.ts +34 -0
  199. package/dist/toggle-tag.js +57 -0
  200. package/dist/toggle-tag.js.map +1 -0
  201. package/dist/toggle.css +152 -0
  202. package/dist/toggle.d.ts +31 -0
  203. package/dist/toggle.js +75 -0
  204. package/dist/toggle.js.map +1 -0
  205. package/dist/tooltip.css +86 -0
  206. package/dist/tooltip.d.ts +30 -0
  207. package/dist/tooltip.js +268 -0
  208. package/dist/tooltip.js.map +1 -0
  209. package/dist/types-SGMShK7R.d.ts +22 -0
  210. package/llms.txt +196 -0
  211. package/next.d.mts +7 -0
  212. package/next.mjs +59 -0
  213. package/package.json +257 -0
  214. package/src/components/composites/alert/Alert.tsx +141 -0
  215. package/src/components/composites/alert/alert.css +132 -0
  216. package/src/components/composites/date-picker/DateField.tsx +80 -0
  217. package/src/components/composites/date-picker/DateRangeField.tsx +110 -0
  218. package/src/components/composites/date-picker/DateTimeField.tsx +162 -0
  219. package/src/components/composites/date-picker/TimeField.tsx +78 -0
  220. package/src/components/composites/date-picker/calendar-panel.tsx +216 -0
  221. package/src/components/composites/date-picker/date-picker.css +595 -0
  222. package/src/components/composites/date-picker/date-utils.ts +56 -0
  223. package/src/components/composites/date-picker/field-shell.tsx +85 -0
  224. package/src/components/composites/date-picker/range-panel.tsx +363 -0
  225. package/src/components/composites/date-picker/time-core.tsx +290 -0
  226. package/src/components/composites/date-picker/use-day-focus.ts +35 -0
  227. package/src/components/composites/dialog/Dialog.tsx +198 -0
  228. package/src/components/composites/dialog/dialog.css +180 -0
  229. package/src/components/composites/pagination/Pagination.tsx +131 -0
  230. package/src/components/composites/pagination/pagination.css +43 -0
  231. package/src/components/composites/popover/Popover.tsx +141 -0
  232. package/src/components/composites/popover/popover.css +69 -0
  233. package/src/components/composites/select/MultiSelect.tsx +133 -0
  234. package/src/components/composites/select/Select.tsx +122 -0
  235. package/src/components/composites/select/core/index.ts +4 -0
  236. package/src/components/composites/select/core/menu.tsx +100 -0
  237. package/src/components/composites/select/core/panel.tsx +154 -0
  238. package/src/components/composites/select/core/trigger.tsx +74 -0
  239. package/src/components/composites/select/core/types.ts +39 -0
  240. package/src/components/composites/select/core/use-listbox.ts +182 -0
  241. package/src/components/composites/select/select.css +416 -0
  242. package/src/components/composites/sheet/Sheet.tsx +135 -0
  243. package/src/components/composites/sheet/sheet.css +26 -0
  244. package/src/components/composites/sheet/use-sheet-drag.ts +126 -0
  245. package/src/components/composites/table/Table.tsx +377 -0
  246. package/src/components/composites/table/table.css +348 -0
  247. package/src/components/composites/tabs/Tabs.tsx +278 -0
  248. package/src/components/composites/tabs/tabs.css +208 -0
  249. package/src/components/composites/toast/Toast.tsx +380 -0
  250. package/src/components/composites/toast/toast-store.ts +295 -0
  251. package/src/components/composites/toast/toast.css +257 -0
  252. package/src/components/composites/tooltip/Tooltip.tsx +110 -0
  253. package/src/components/composites/tooltip/tooltip-host.tsx +184 -0
  254. package/src/components/composites/tooltip/tooltip-store.ts +81 -0
  255. package/src/components/composites/tooltip/tooltip.css +86 -0
  256. package/src/components/dev/MotionDevtools.tsx +200 -0
  257. package/src/components/dev/motion-devtools.css +265 -0
  258. package/src/components/dev/slowmo-engine.ts +230 -0
  259. package/src/components/internal/glass/glass.css +100 -0
  260. package/src/components/internal/glass/glint.ts +9 -0
  261. package/src/components/internal/hooks/use-controllable.ts +21 -0
  262. package/src/components/internal/hooks/use-scroll-edges.ts +46 -0
  263. package/src/components/internal/icon/Icon.tsx +66 -0
  264. package/src/components/internal/icon/IconSlot.tsx +10 -0
  265. package/src/components/internal/icon/icon.css +33 -0
  266. package/src/components/internal/overlay/focus.ts +68 -0
  267. package/src/components/internal/overlay/layer.tsx +149 -0
  268. package/src/components/internal/overlay/modal.tsx +141 -0
  269. package/src/components/internal/overlay/overlay.css +23 -0
  270. package/src/components/internal/overlay/position.ts +103 -0
  271. package/src/components/internal/utils/cx.ts +3 -0
  272. package/src/components/internal/utils/token-px.ts +8 -0
  273. package/src/components/primitives/avatar/Avatar.tsx +120 -0
  274. package/src/components/primitives/avatar/AvatarGroup.tsx +56 -0
  275. package/src/components/primitives/avatar/avatar.css +200 -0
  276. package/src/components/primitives/badge/Badge.tsx +75 -0
  277. package/src/components/primitives/badge/CountBadge.tsx +20 -0
  278. package/src/components/primitives/badge/StatusBadge.tsx +111 -0
  279. package/src/components/primitives/badge/badge.css +182 -0
  280. package/src/components/primitives/badge/odometer.css +62 -0
  281. package/src/components/primitives/badge/odometer.tsx +36 -0
  282. package/src/components/primitives/button/Button.tsx +102 -0
  283. package/src/components/primitives/button/button.css +269 -0
  284. package/src/components/primitives/checkbox/Checkbox.tsx +76 -0
  285. package/src/components/primitives/checkbox/check-glyph.tsx +28 -0
  286. package/src/components/primitives/checkbox/checkbox.css +177 -0
  287. package/src/components/primitives/collapse/Collapse.tsx +86 -0
  288. package/src/components/primitives/collapse/collapse.css +88 -0
  289. package/src/components/primitives/collapse/collapse.typecheck.tsx +29 -0
  290. package/src/components/primitives/input/NumberField.tsx +139 -0
  291. package/src/components/primitives/input/OtpField.tsx +134 -0
  292. package/src/components/primitives/input/TextField.tsx +110 -0
  293. package/src/components/primitives/input/field-chrome.tsx +83 -0
  294. package/src/components/primitives/input/input.css +333 -0
  295. package/src/components/primitives/radio-group/RadioGroup.tsx +183 -0
  296. package/src/components/primitives/radio-group/radio-group.css +300 -0
  297. package/src/components/primitives/tag/Tag.tsx +143 -0
  298. package/src/components/primitives/tag/ToggleTag.tsx +97 -0
  299. package/src/components/primitives/tag/tag.css +248 -0
  300. package/src/components/primitives/textarea/Textarea.tsx +190 -0
  301. package/src/components/primitives/textarea/textarea.css +219 -0
  302. package/src/components/primitives/toggle/Toggle.tsx +105 -0
  303. package/src/components/primitives/toggle/toggle.css +152 -0
  304. package/src/css.d.ts +1 -0
  305. package/src/dom-props.ts +1 -0
  306. package/src/engine/animate.ts +141 -0
  307. package/src/engine/drag.ts +53 -0
  308. package/src/engine/ease.ts +155 -0
  309. package/src/engine/flip.ts +59 -0
  310. package/src/engine/index.ts +13 -0
  311. package/src/mcp/server.ts +420 -0
  312. package/src/motion/element.tsx +53 -0
  313. package/src/motion/flip.tsx +87 -0
  314. package/src/motion/glide.tsx +47 -0
  315. package/src/motion/motion-timing.ts +33 -0
  316. package/src/motion/presence-context.ts +18 -0
  317. package/src/motion/presence.tsx +91 -0
  318. package/src/motion/timing.ts +49 -0
  319. package/src/motion/use-motion.ts +64 -0
  320. package/src/styles.css +26 -0
  321. package/src/tokens/avatar.css +20 -0
  322. package/src/tokens/color.css +58 -0
  323. package/src/tokens/elevation.css +22 -0
  324. package/src/tokens/fonts.css +3 -0
  325. package/src/tokens/glass.css +20 -0
  326. package/src/tokens/icons.css +7 -0
  327. package/src/tokens/layers.css +6 -0
  328. package/src/tokens/motion-scale.ts +17 -0
  329. package/src/tokens/motion-tokens.ts +92 -0
  330. package/src/tokens/motion.css +49 -0
  331. package/src/tokens/radius.css +11 -0
  332. package/src/tokens/semantic.css +65 -0
  333. package/src/tokens/spacing.css +26 -0
  334. package/src/tokens/typography.css +52 -0
package/dist/table.js ADDED
@@ -0,0 +1,262 @@
1
+ 'use client';import { useScrollEdges } from './chunk-BIDVNLJO.js';
2
+ import { Motion } from './chunk-36WVAQKL.js';
3
+ import './chunk-W3SHPKSB.js';
4
+ import './chunk-JSKXZMLH.js';
5
+ import './chunk-L3Q3VDIW.js';
6
+ import { Icon } from './chunk-3SBCBYB7.js';
7
+ import { Odometer } from './chunk-CONSWNIV.js';
8
+ import { UIMotion } from './chunk-S5ZTZANS.js';
9
+ import { Button } from './chunk-U5QQZC3U.js';
10
+ import { Checkbox } from './chunk-QDK66S4G.js';
11
+ import './chunk-NNXJCLTV.js';
12
+ import { cx } from './chunk-QGNC7LH5.js';
13
+ import './table.css';
14
+ import { useState, useRef, useMemo } from 'react';
15
+ import { jsxs, jsx } from 'react/jsx-runtime';
16
+
17
+ function tblCompare(a, b) {
18
+ if (a == null && b == null) return 0;
19
+ if (a == null) return 1;
20
+ if (b == null) return -1;
21
+ if (typeof a === "number" && typeof b === "number") return a - b;
22
+ return String(a).localeCompare(String(b), void 0, { numeric: true, sensitivity: "base" });
23
+ }
24
+ function Table({
25
+ columns = [],
26
+ rows = [],
27
+ rowKey = "id",
28
+ ariaLabel,
29
+ selectable = false,
30
+ onSelectionChange,
31
+ bulkActions,
32
+ selectionLabel,
33
+ defaultSort = null,
34
+ onSortChange,
35
+ density = "cozy",
36
+ pinFirst = true,
37
+ loading = false,
38
+ empty,
39
+ footer,
40
+ onRowClick,
41
+ className = "",
42
+ htmlProps
43
+ }) {
44
+ const [sort, setSort] = useState(defaultSort);
45
+ const [selected, setSelected] = useState(() => /* @__PURE__ */ new Set());
46
+ const wrapRef = useRef(null);
47
+ const scrollRef = useRef(null);
48
+ const lastIdxRef = useRef(-1);
49
+ const shiftRef = useRef(false);
50
+ const lastCountRef = useRef(0);
51
+ const keyOf = (row) => row[rowKey];
52
+ const sortedRows = useMemo(() => {
53
+ if (!sort) return rows;
54
+ const col = columns.find((c) => c.key === sort.key);
55
+ if (!col) return rows;
56
+ const get = typeof col.sortBy === "function" ? col.sortBy : col.sortBy ? (r) => r[col.sortBy] : (r) => r[col.key];
57
+ const dir = sort.dir === "desc" ? -1 : 1;
58
+ return rows.slice().sort((a, b) => dir * tblCompare(get(a), get(b)));
59
+ }, [rows, columns, sort]);
60
+ function cycleSort(col) {
61
+ const next = sort && sort.key === col.key ? { key: col.key, dir: sort.dir === "asc" ? "desc" : "asc" } : { key: col.key, dir: "asc" };
62
+ setSort(next);
63
+ if (onSortChange) onSortChange(next);
64
+ }
65
+ const allSelected = sortedRows.length > 0 && sortedRows.every((r) => selected.has(keyOf(r)));
66
+ const someSelected = selected.size > 0 && !allSelected;
67
+ function commitSelection(next) {
68
+ setSelected(next);
69
+ if (onSelectionChange) onSelectionChange(Array.from(next));
70
+ }
71
+ function clearSelection() {
72
+ lastIdxRef.current = -1;
73
+ commitSelection(/* @__PURE__ */ new Set());
74
+ }
75
+ function toggleAll() {
76
+ const next = /* @__PURE__ */ new Set();
77
+ if (!allSelected) sortedRows.forEach((r) => next.add(keyOf(r)));
78
+ commitSelection(next);
79
+ }
80
+ function toggleRow(idx) {
81
+ const next = new Set(selected);
82
+ const on = !next.has(keyOf(sortedRows[idx]));
83
+ if (shiftRef.current && lastIdxRef.current >= 0 && lastIdxRef.current !== idx) {
84
+ const lo = Math.min(lastIdxRef.current, idx);
85
+ const hi = Math.max(lastIdxRef.current, idx);
86
+ for (let i = lo; i <= hi; i++) {
87
+ const k = keyOf(sortedRows[i]);
88
+ if (on) next.add(k);
89
+ else next.delete(k);
90
+ }
91
+ } else if (on) {
92
+ next.add(keyOf(sortedRows[idx]));
93
+ } else {
94
+ next.delete(keyOf(sortedRows[idx]));
95
+ }
96
+ lastIdxRef.current = idx;
97
+ shiftRef.current = false;
98
+ commitSelection(next);
99
+ }
100
+ useScrollEdges(
101
+ scrollRef,
102
+ (edges, el) => {
103
+ const w = wrapRef.current;
104
+ if (!w) return;
105
+ const set = (attr, on) => {
106
+ if (on) w.setAttribute(attr, "");
107
+ else w.removeAttribute(attr);
108
+ };
109
+ set("data-scrolled", el.scrollTop > 0);
110
+ set("data-x-back", edges.left);
111
+ set("data-x-more", edges.right);
112
+ },
113
+ { content: true }
114
+ );
115
+ function cellMods(col, i) {
116
+ const a = [];
117
+ if (col.align === "end") a.push("tbl__cell--end");
118
+ if (col.align === "center") a.push("tbl__cell--center");
119
+ if (col.grow) a.push("tbl__cell--grow");
120
+ if (col.hideBelow) a.push("tbl__cell--hide-" + col.hideBelow);
121
+ if (pinFirst && i === 0) {
122
+ a.push("tbl__cell--pin", "tbl__cell--pinEdge");
123
+ if (selectable) a.push("tbl__cell--pinOff");
124
+ }
125
+ return a;
126
+ }
127
+ const t = UIMotion.t;
128
+ const colCount = columns.length + (selectable ? 1 : 0);
129
+ const showEmpty = !loading && sortedRows.length === 0;
130
+ const checkCellCls = "tbl__cell--check" + (pinFirst ? " tbl__cell--pin" : "");
131
+ const checkSize = density === "compact" ? "sm" : "md";
132
+ const bulkOpen = selectable && selected.size > 0;
133
+ if (selected.size > 0) lastCountRef.current = selected.size;
134
+ const rootCls = cx(
135
+ "tbl",
136
+ density === "compact" && "tbl--compact",
137
+ pinFirst && "tbl--pin",
138
+ loading && "tbl--loading",
139
+ onRowClick && "tbl--clickable",
140
+ className
141
+ );
142
+ return /* @__PURE__ */ jsxs("div", { ref: wrapRef, className: rootCls, ...htmlProps, children: [
143
+ selectable ? /* @__PURE__ */ jsxs(
144
+ "div",
145
+ {
146
+ className: "tbl__bulk",
147
+ "data-open": bulkOpen ? "" : void 0,
148
+ "aria-hidden": bulkOpen ? void 0 : true,
149
+ role: "toolbar",
150
+ "aria-label": "Selection actions",
151
+ children: [
152
+ /* @__PURE__ */ jsx(
153
+ Checkbox,
154
+ {
155
+ className: "tbl__cbx",
156
+ size: checkSize,
157
+ checked: allSelected,
158
+ indeterminate: someSelected,
159
+ htmlProps: { "aria-label": "Select all rows" },
160
+ onChange: toggleAll
161
+ }
162
+ ),
163
+ /* @__PURE__ */ jsxs("span", { className: "tbl__bulkCount", "aria-live": "polite", "aria-atomic": "true", children: [
164
+ /* @__PURE__ */ jsx(Odometer, { value: bulkOpen ? selected.size : lastCountRef.current }),
165
+ /* @__PURE__ */ jsx("span", { children: " selected" })
166
+ ] }),
167
+ /* @__PURE__ */ jsx("span", { className: "tbl__bulkSpacer" }),
168
+ bulkActions ? bulkActions(Array.from(selected), clearSelection) : null,
169
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: clearSelection, children: "Clear" })
170
+ ]
171
+ }
172
+ ) : null,
173
+ /* @__PURE__ */ jsxs("div", { className: "tbl__viewport", children: [
174
+ /* @__PURE__ */ jsx("div", { className: "tbl__scroll", ref: scrollRef, children: /* @__PURE__ */ jsxs("table", { className: "tbl__table", "aria-label": ariaLabel, "aria-busy": loading || void 0, children: [
175
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
176
+ selectable ? /* @__PURE__ */ jsx("th", { scope: "col", className: checkCellCls, children: /* @__PURE__ */ jsx(
177
+ Checkbox,
178
+ {
179
+ className: "tbl__cbx",
180
+ size: checkSize,
181
+ checked: allSelected,
182
+ indeterminate: someSelected,
183
+ htmlProps: { "aria-label": "Select all rows" },
184
+ onChange: toggleAll
185
+ }
186
+ ) }) : null,
187
+ columns.map((c, i) => {
188
+ const active = sort && sort.key === c.key;
189
+ const cls = (c.sortable ? ["tbl__cell--sortable"] : []).concat(cellMods(c, i));
190
+ return /* @__PURE__ */ jsx(
191
+ "th",
192
+ {
193
+ scope: "col",
194
+ className: cls.join(" "),
195
+ "aria-sort": active ? sort.dir === "asc" ? "ascending" : "descending" : void 0,
196
+ children: c.sortable ? /* @__PURE__ */ jsxs("button", { type: "button", className: "tbl__sort", onClick: () => cycleSort(c), children: [
197
+ /* @__PURE__ */ jsx("span", { className: "tbl__sortLbl", children: c.label }),
198
+ /* @__PURE__ */ jsx(
199
+ "span",
200
+ {
201
+ className: "tbl__sortIcon" + (active ? " is-on" : "") + (active && sort.dir === "desc" ? " is-desc" : ""),
202
+ "aria-hidden": "true",
203
+ children: /* @__PURE__ */ jsx(Icon, { name: "arrow-up", size: "sm" })
204
+ }
205
+ )
206
+ ] }) : c.label != null ? /* @__PURE__ */ jsx("span", { className: "tbl__hLbl", children: c.label }) : null
207
+ },
208
+ c.key
209
+ );
210
+ })
211
+ ] }) }),
212
+ /* @__PURE__ */ jsx("tbody", { children: showEmpty ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: colCount, className: "tbl__emptyCell", children: empty != null ? empty : "Nothing to show" }) }) : sortedRows.map((row, idx) => {
213
+ const k = keyOf(row);
214
+ const isSel = selected.has(k);
215
+ return /* @__PURE__ */ jsxs(
216
+ Motion,
217
+ {
218
+ as: "tr",
219
+ layout: { timing: t.layout },
220
+ className: "tbl__row",
221
+ "data-selected": isSel ? "" : void 0,
222
+ onClick: onRowClick ? () => onRowClick(row) : void 0,
223
+ children: [
224
+ selectable ? /* @__PURE__ */ jsx("td", { className: checkCellCls, onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx(
225
+ Checkbox,
226
+ {
227
+ className: "tbl__cbx",
228
+ size: checkSize,
229
+ checked: isSel,
230
+ htmlProps: {
231
+ "aria-label": selectionLabel ? selectionLabel(row) : "Select row",
232
+ onClick: (e) => {
233
+ shiftRef.current = e.shiftKey;
234
+ }
235
+ },
236
+ onChange: () => toggleRow(idx)
237
+ }
238
+ ) }) : null,
239
+ columns.map((c, i) => /* @__PURE__ */ jsx(
240
+ "td",
241
+ {
242
+ className: cellMods(c, i).concat(c.mono ? ["tbl__cell--mono"] : []).concat(c.strong ? ["tbl__cell--strong"] : []).join(" "),
243
+ children: c.render ? c.render(row) : row[c.key]
244
+ },
245
+ c.key
246
+ ))
247
+ ]
248
+ },
249
+ k
250
+ );
251
+ }) })
252
+ ] }) }),
253
+ /* @__PURE__ */ jsx("div", { className: "tbl__edge tbl__edge--l", "aria-hidden": "true" }),
254
+ /* @__PURE__ */ jsx("div", { className: "tbl__edge tbl__edge--r", "aria-hidden": "true" })
255
+ ] }),
256
+ footer ? /* @__PURE__ */ jsx("div", { className: "tbl__foot", children: footer }) : null
257
+ ] });
258
+ }
259
+
260
+ export { Table };
261
+ //# sourceMappingURL=table.js.map
262
+ //# sourceMappingURL=table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/composites/table/Table.tsx"],"names":[],"mappings":"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","file":"table.js","sourcesContent":["'use client';\n\nimport './table.css';\nimport { useMemo, useRef, useState, type HTMLAttributes, type ReactNode } from 'react';\nimport { Motion } from '../../../motion/element';\nimport { UIMotion } from '../../../tokens/motion-tokens';\nimport { Icon } from '../../internal/icon/Icon';\nimport type { DataAttributes } from '../../../dom-props';\nimport { Button } from '../../primitives/button/Button';\nimport { Checkbox } from '../../primitives/checkbox/Checkbox';\nimport { Odometer } from '../../primitives/badge/odometer';\nimport { useScrollEdges } from '../../internal/hooks/use-scroll-edges';\nimport { cx } from '../../internal/utils/cx';\n\nexport type TableAlign = 'start' | 'end' | 'center';\nexport type TableHideBelow = 'sm' | 'md';\n\nexport interface TableColumn<Row = any> {\n /** Unique column id; also the default data accessor (row[key]). */\n key: string;\n /** Header cell content; omit for a header-less column (e.g. an actions column). */\n label?: ReactNode;\n /** Cell + header alignment. Default 'start'. */\n align?: TableAlign;\n /** Mono + tabular numerals (timestamps, counts, IDs - section E). */\n mono?: boolean;\n /** Identity emphasis: strong text, medium weight. */\n strong?: boolean;\n /** The one greedy column - absorbs slack width. Others size to content. */\n grow?: boolean;\n /** Header becomes a sort control. Sort is local; asc - desc, no third state. */\n sortable?: boolean;\n /** Sort accessor: a row property name, or a function. Defaults to row[key]. */\n sortBy?: string | ((row: Row) => unknown);\n /** Collapse this column below a container breakpoint (30rem / 42rem). */\n hideBelow?: TableHideBelow;\n /** Custom cell renderer. Defaults to row[key]. */\n render?: (row: Row) => ReactNode;\n}\n\nexport interface TableSort {\n /** `key` of the column being sorted by. */\n key: string;\n /** Sort direction - `asc` or `desc`. */\n dir: 'asc' | 'desc';\n}\n\nexport interface TableProps<Row = any> {\n /** Column defs, rendered left to right. */\n columns: TableColumn<Row>[];\n /** The row data; sorted locally per the active sort before render. */\n rows: Row[];\n /** Row identity property. Default 'id'. Must be unique and stable. */\n rowKey?: string;\n /** Accessible name for the <table>. */\n ariaLabel?: string;\n\n /** Checkbox column + bulk bar. Selection is a Set of row keys, owned here. */\n selectable?: boolean;\n /** Fires with the selected row keys after every selection change. */\n onSelectionChange?: (keys: Array<string | number>) => void;\n /** Rendered in the bulk bar between the count and the built-in Clear. */\n bulkActions?: (keys: Array<string | number>, clear: () => void) => ReactNode;\n /** Per-row checkbox aria-label. Default 'Select row'. */\n selectionLabel?: (row: Row) => string;\n\n /** Initial sort; sorting stays local and uncontrolled after that. */\n defaultSort?: TableSort | null;\n /** Notification only - fires after the local sort updates. */\n onSortChange?: (sort: TableSort) => void;\n\n /** Row rhythm. Default 'cozy' (46px rows); 'compact' is 38px. */\n density?: 'cozy' | 'compact';\n /** Pin checkbox + first column under horizontal overflow. Default true. */\n pinFirst?: boolean;\n\n /** Rows recede and go inert (list-fetch convention). */\n loading?: boolean;\n /** Shown when rows is empty and not loading. Default 'Nothing to show'. */\n empty?: ReactNode;\n /** Footer strip - pagination, summaries. */\n footer?: ReactNode;\n\n /** Makes rows clickable (checkbox cell excluded). */\n onRowClick?: (row: Row) => void;\n /** Size the table here - the internal scroller absorbs the constraint. */\n className?: string;\n /** Standard <div> attributes (style, data-*, aria-*, ...) forwarded to the table wrapper. */\n htmlProps?: Omit<HTMLAttributes<HTMLDivElement>, 'className'> & DataAttributes;\n}\n\nfunction tblCompare(a: unknown, b: unknown) {\n if (a == null && b == null) return 0;\n if (a == null) return 1;\n if (b == null) return -1;\n if (typeof a === 'number' && typeof b === 'number') return a - b;\n return String(a).localeCompare(String(b), undefined, { numeric: true, sensitivity: 'base' });\n}\n\nexport function Table<Row = any>({\n columns = [],\n rows = [],\n rowKey = 'id',\n ariaLabel,\n selectable = false,\n onSelectionChange,\n bulkActions,\n selectionLabel,\n defaultSort = null,\n onSortChange,\n density = 'cozy',\n pinFirst = true,\n loading = false,\n empty,\n footer,\n onRowClick,\n className = '',\n htmlProps,\n}: TableProps<Row>) {\n const [sort, setSort] = useState<TableSort | null>(defaultSort);\n const [selected, setSelected] = useState<Set<string | number>>(() => new Set());\n const wrapRef = useRef<HTMLDivElement>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n const lastIdxRef = useRef(-1);\n const shiftRef = useRef(false);\n const lastCountRef = useRef(0);\n\n const keyOf = (row: Row): string | number => (row as Record<string, string | number>)[rowKey];\n\n const sortedRows = useMemo(() => {\n if (!sort) return rows;\n const col = columns.find((c) => c.key === sort.key);\n if (!col) return rows;\n const get =\n typeof col.sortBy === 'function'\n ? col.sortBy\n : col.sortBy\n ? (r: Row) => (r as Record<string, unknown>)[col.sortBy as string]\n : (r: Row) => (r as Record<string, unknown>)[col.key];\n const dir = sort.dir === 'desc' ? -1 : 1;\n return rows.slice().sort((a, b) => dir * tblCompare(get(a), get(b)));\n }, [rows, columns, sort]);\n\n function cycleSort(col: TableColumn<Row>) {\n const next: TableSort =\n sort && sort.key === col.key\n ? { key: col.key, dir: sort.dir === 'asc' ? 'desc' : 'asc' }\n : { key: col.key, dir: 'asc' };\n setSort(next);\n if (onSortChange) onSortChange(next);\n }\n\n const allSelected = sortedRows.length > 0 && sortedRows.every((r) => selected.has(keyOf(r)));\n const someSelected = selected.size > 0 && !allSelected;\n\n function commitSelection(next: Set<string | number>) {\n setSelected(next);\n if (onSelectionChange) onSelectionChange(Array.from(next));\n }\n function clearSelection() {\n lastIdxRef.current = -1;\n commitSelection(new Set());\n }\n function toggleAll() {\n const next = new Set<string | number>();\n if (!allSelected) sortedRows.forEach((r) => next.add(keyOf(r)));\n commitSelection(next);\n }\n function toggleRow(idx: number) {\n const next = new Set(selected);\n const on = !next.has(keyOf(sortedRows[idx]));\n if (shiftRef.current && lastIdxRef.current >= 0 && lastIdxRef.current !== idx) {\n const lo = Math.min(lastIdxRef.current, idx);\n const hi = Math.max(lastIdxRef.current, idx);\n for (let i = lo; i <= hi; i++) {\n const k = keyOf(sortedRows[i]);\n if (on) next.add(k);\n else next.delete(k);\n }\n } else if (on) {\n next.add(keyOf(sortedRows[idx]));\n } else {\n next.delete(keyOf(sortedRows[idx]));\n }\n lastIdxRef.current = idx;\n shiftRef.current = false;\n commitSelection(next);\n }\n\n useScrollEdges(\n scrollRef,\n (edges, el) => {\n const w = wrapRef.current;\n if (!w) return;\n const set = (attr: string, on: boolean) => {\n if (on) w.setAttribute(attr, '');\n else w.removeAttribute(attr);\n };\n set('data-scrolled', el.scrollTop > 0);\n set('data-x-back', edges.left);\n set('data-x-more', edges.right);\n },\n { content: true },\n );\n\n function cellMods(col: TableColumn<Row>, i: number) {\n const a: string[] = [];\n if (col.align === 'end') a.push('tbl__cell--end');\n if (col.align === 'center') a.push('tbl__cell--center');\n if (col.grow) a.push('tbl__cell--grow');\n if (col.hideBelow) a.push('tbl__cell--hide-' + col.hideBelow);\n if (pinFirst && i === 0) {\n a.push('tbl__cell--pin', 'tbl__cell--pinEdge');\n if (selectable) a.push('tbl__cell--pinOff');\n }\n return a;\n }\n\n const t = UIMotion.t;\n const colCount = columns.length + (selectable ? 1 : 0);\n const showEmpty = !loading && sortedRows.length === 0;\n const checkCellCls = 'tbl__cell--check' + (pinFirst ? ' tbl__cell--pin' : '');\n\n const checkSize = density === 'compact' ? 'sm' : 'md';\n const bulkOpen = selectable && selected.size > 0;\n if (selected.size > 0) lastCountRef.current = selected.size;\n\n const rootCls = cx(\n 'tbl',\n density === 'compact' && 'tbl--compact',\n pinFirst && 'tbl--pin',\n loading && 'tbl--loading',\n onRowClick && 'tbl--clickable',\n className,\n );\n\n return (\n <div ref={wrapRef} className={rootCls} {...htmlProps}>\n {selectable ? (\n <div\n className=\"tbl__bulk\"\n data-open={bulkOpen ? '' : undefined}\n aria-hidden={bulkOpen ? undefined : true}\n role=\"toolbar\"\n aria-label=\"Selection actions\"\n >\n <Checkbox\n className=\"tbl__cbx\"\n size={checkSize}\n checked={allSelected}\n indeterminate={someSelected}\n htmlProps={{ 'aria-label': 'Select all rows' }}\n onChange={toggleAll}\n />\n <span className=\"tbl__bulkCount\" aria-live=\"polite\" aria-atomic=\"true\">\n <Odometer value={bulkOpen ? selected.size : lastCountRef.current} />\n <span> selected</span>\n </span>\n <span className=\"tbl__bulkSpacer\"></span>\n {bulkActions ? bulkActions(Array.from(selected), clearSelection) : null}\n <Button variant=\"ghost\" size=\"sm\" onClick={clearSelection}>\n Clear\n </Button>\n </div>\n ) : null}\n\n <div className=\"tbl__viewport\">\n <div className=\"tbl__scroll\" ref={scrollRef}>\n <table className=\"tbl__table\" aria-label={ariaLabel} aria-busy={loading || undefined}>\n <thead>\n <tr>\n {selectable ? (\n <th scope=\"col\" className={checkCellCls}>\n <Checkbox\n className=\"tbl__cbx\"\n size={checkSize}\n checked={allSelected}\n indeterminate={someSelected}\n htmlProps={{ 'aria-label': 'Select all rows' }}\n onChange={toggleAll}\n />\n </th>\n ) : null}\n {columns.map((c, i) => {\n const active = sort && sort.key === c.key;\n const cls = (c.sortable ? ['tbl__cell--sortable'] : []).concat(cellMods(c, i));\n return (\n <th\n key={c.key}\n scope=\"col\"\n className={cls.join(' ')}\n aria-sort={active ? (sort.dir === 'asc' ? 'ascending' : 'descending') : undefined}\n >\n {c.sortable ? (\n <button type=\"button\" className=\"tbl__sort\" onClick={() => cycleSort(c)}>\n <span className=\"tbl__sortLbl\">{c.label}</span>\n <span\n className={\n 'tbl__sortIcon' +\n (active ? ' is-on' : '') +\n (active && sort.dir === 'desc' ? ' is-desc' : '')\n }\n aria-hidden=\"true\"\n >\n <Icon name=\"arrow-up\" size=\"sm\" />\n </span>\n </button>\n ) : c.label != null ? (\n <span className=\"tbl__hLbl\">{c.label}</span>\n ) : null}\n </th>\n );\n })}\n </tr>\n </thead>\n <tbody>\n {showEmpty ? (\n <tr>\n <td colSpan={colCount} className=\"tbl__emptyCell\">\n {empty != null ? empty : 'Nothing to show'}\n </td>\n </tr>\n ) : (\n sortedRows.map((row, idx) => {\n const k = keyOf(row);\n const isSel = selected.has(k);\n return (\n <Motion\n as=\"tr\"\n key={k}\n layout={{ timing: t.layout }}\n className=\"tbl__row\"\n data-selected={isSel ? '' : undefined}\n onClick={onRowClick ? () => onRowClick(row) : undefined}\n >\n {selectable ? (\n <td className={checkCellCls} onClick={(e) => e.stopPropagation()}>\n <Checkbox\n className=\"tbl__cbx\"\n size={checkSize}\n checked={isSel}\n htmlProps={{\n 'aria-label': selectionLabel ? selectionLabel(row) : 'Select row',\n onClick: (e) => {\n shiftRef.current = e.shiftKey;\n },\n }}\n onChange={() => toggleRow(idx)}\n />\n </td>\n ) : null}\n {columns.map((c, i) => (\n <td\n key={c.key}\n className={cellMods(c, i)\n .concat(c.mono ? ['tbl__cell--mono'] : [])\n .concat(c.strong ? ['tbl__cell--strong'] : [])\n .join(' ')}\n >\n {c.render ? c.render(row) : (row as Record<string, ReactNode>)[c.key]}\n </td>\n ))}\n </Motion>\n );\n })\n )}\n </tbody>\n </table>\n </div>\n <div className=\"tbl__edge tbl__edge--l\" aria-hidden=\"true\"></div>\n <div className=\"tbl__edge tbl__edge--r\" aria-hidden=\"true\"></div>\n </div>\n\n {footer ? <div className=\"tbl__foot\">{footer}</div> : null}\n </div>\n );\n}\n"]}
package/dist/tabs.css ADDED
@@ -0,0 +1,208 @@
1
+ /* tabs.css - line tabs for VIEW SWITCHING (overview / activity / settings), sitting on a
2
+ hairline baseline. NOT a form control: pick Segmented control for 2-3 filter options.
3
+
4
+ Anatomy: .tabs > .tabs__list[role=tablist] > .tab__hover? + .tab* + .tabs__ink
5
+ .tab > .tab__pad ( Icon? + .tab__label + .tab__count? )
6
+ States: :hover - :active - :focus-visible - :disabled - .is-selected
7
+
8
+ Two motions, both driven from Tabs.tsx - nothing here transitions them:
9
+ - the ink ("reaches, then releases"): one persistent node; the edge facing the
10
+ destination travels first, then the trailing edge settles. animate() keyframes on
11
+ x + measured width (destination width is unknowable to transforms; scaleX would
12
+ distort the radius).
13
+ - the gliding hover: one persistent pill (../motion/glide) that fades in under the
14
+ first tab touched, springs between pads, and fades out where it rests on leave.
15
+ Label color, icon weight and press dip remain trivial-tier CSS. */
16
+
17
+ /* -- Shell - owns the full-width baseline ----------------------------------
18
+ The hairline is a ::after on the shell (not a border on the scroller) so it
19
+ spans edge-to-edge while the scroller above it clips, masks and scrolls
20
+ freely. The scroller gets z-index:1 so the ink paints OVER the hairline. */
21
+ .tabs {
22
+ position: relative;
23
+ }
24
+ .tabs::after {
25
+ content: '';
26
+ position: absolute;
27
+ inset-inline: 0;
28
+ bottom: 0;
29
+ height: var(--border-hairline);
30
+ background: var(--border-default);
31
+ }
32
+
33
+ /* -- Scroller - the tablist ------------------------------------------------
34
+ Overflow is honest: the row scrolls (scrollbar hidden) and the clipped edge
35
+ fades out via a mask. The fade lengths are two custom props flipped by
36
+ data-fade (set from scroll geometry in Tabs.jsx) - at 0px the mask is fully
37
+ opaque, i.e. a no-op. Negative margin + matching padding align the first
38
+ label flush with the container edge while the hover pill still has room. */
39
+ .tabs__list {
40
+ position: relative;
41
+ z-index: 1;
42
+ display: flex;
43
+ gap: var(--space-1);
44
+ overflow-x: auto;
45
+ scrollbar-width: none;
46
+ margin-inline: calc(var(--space-2) * -1);
47
+ padding-inline: var(--space-2);
48
+
49
+ --tabs-fade-start: 0px;
50
+ --tabs-fade-end: 0px;
51
+ mask-image: linear-gradient(
52
+ to right,
53
+ transparent,
54
+ black var(--tabs-fade-start),
55
+ black calc(100% - var(--tabs-fade-end)),
56
+ transparent
57
+ );
58
+ }
59
+ .tabs__list::-webkit-scrollbar {
60
+ display: none;
61
+ }
62
+ .tabs__list[data-fade~='start'] {
63
+ --tabs-fade-start: var(--space-8);
64
+ }
65
+ .tabs__list[data-fade~='end'] {
66
+ --tabs-fade-end: var(--space-8);
67
+ }
68
+
69
+ /* -- Tab - the hit target --------------------------------------------------
70
+ The button is pill + a bottom gap down to the baseline (total height 36px).
71
+ All visual feedback lives on the inner pill so hover/press/focus read as a
72
+ soft inset shape, while the ink below stays anchored to the baseline. */
73
+ .tab {
74
+ appearance: none;
75
+ border: 0;
76
+ background: none;
77
+ margin: 0;
78
+ padding: 0 0 var(--space-2);
79
+ flex: none;
80
+ display: block;
81
+ font-family: var(--font-sans);
82
+ cursor: pointer;
83
+ -webkit-tap-highlight-color: transparent;
84
+ }
85
+ .tab:focus-visible {
86
+ outline: none;
87
+ }
88
+
89
+ .tab__pad {
90
+ position: relative; /* positioned so every pad paints above the shared glide
91
+ pill (z-index 0, first child of the list) */
92
+ display: flex;
93
+ align-items: center;
94
+ gap: var(--space-2);
95
+ height: var(--control-height-sm); /* 28 */
96
+ padding-inline: var(--space-2);
97
+ border-radius: var(--radius-md);
98
+ font: var(--type-micro);
99
+ line-height: 1;
100
+ white-space: nowrap;
101
+ color: var(--text-subtle);
102
+ transition:
103
+ var(--transition-control),
104
+ transform var(--duration-fast) var(--ease-standard);
105
+ }
106
+ .tab__pad .icon {
107
+ display: inline-flex;
108
+ }
109
+
110
+ /* States - hover strengthens the label (the shared glide pill supplies the
111
+ background), press dips 1px on a darker fill (tactile, like Button), focus
112
+ is the composed accent ring, selection is text-strong + the ink. */
113
+ .tab:hover:not(:disabled) .tab__pad {
114
+ color: var(--text-body);
115
+ }
116
+ .tab:active:not(:disabled) .tab__pad {
117
+ background: var(--neutral-wash-press);
118
+ transform: translateY(var(--space-px));
119
+ }
120
+ .tab.is-selected .tab__pad,
121
+ .tab.is-selected:hover:not(:disabled) .tab__pad {
122
+ color: var(--text-strong);
123
+ }
124
+ .tab:focus-visible .tab__pad {
125
+ box-shadow: var(--ring-accent);
126
+ }
127
+ .tab:disabled {
128
+ cursor: not-allowed;
129
+ }
130
+ .tab:disabled .tab__pad {
131
+ color: var(--text-disabled);
132
+ }
133
+
134
+ /* -- Count - always mono, always tabular -----------------------------------*/
135
+ .tab__count {
136
+ font-family: var(--font-mono);
137
+ font-size: var(--size-micro);
138
+ line-height: 1;
139
+ font-variant-numeric: tabular-nums;
140
+ color: var(--text-subtle);
141
+ background: var(--bg-muted);
142
+ padding: var(--space-1);
143
+ border-radius: var(--radius-sm);
144
+ transition: var(--transition-colors);
145
+ }
146
+ .tab.is-selected .tab__count {
147
+ color: var(--text-accent);
148
+ background: var(--accent-wash);
149
+ }
150
+ .tab:disabled .tab__count {
151
+ color: var(--text-disabled);
152
+ background: var(--bg-subtle);
153
+ }
154
+
155
+ /* -- The gliding hover ------------------------------------------------------
156
+ One persistent pill, first child of the scroller (../motion/glide writes
157
+ x/y/width/height/opacity inline; coordinates include scrollLeft, so it
158
+ scrolls with its tab). z-index 0 + DOM order keep it under every tab's
159
+ positioned pad in both travel directions. Nothing transitions here. */
160
+ .tab__hover {
161
+ opacity: 0;
162
+ position: absolute;
163
+ top: 0;
164
+ left: 0;
165
+ z-index: 0;
166
+ border-radius: var(--radius-md);
167
+ background: var(--neutral-wash);
168
+ pointer-events: none;
169
+ will-change: transform, opacity;
170
+ }
171
+
172
+ /* -- The ink ---------------------------------------------------------------
173
+ Positioned in the scroller's layout coordinates (offsetLeft space), so it
174
+ scrolls WITH its tab. Motion writes transform + width at runtime; resting
175
+ geometry is set the same way, so there is nothing to transition here.
176
+ EXCEPTION (intentional, mirrors RadioGroup's dot dims): 2px ink height is a
177
+ bespoke control dim - elevation.css reserves 2px for selected emphasis, but
178
+ ships no width token for it (--border-emphasis is 1.5px, which renders as
179
+ sub-pixel fuzz on 1x screens). Do not "fix" it to a token. */
180
+ .tabs__ink {
181
+ --tabs-ink-h: 2px;
182
+ position: absolute;
183
+ left: 0;
184
+ bottom: 0;
185
+ height: var(--tabs-ink-h);
186
+ border-radius: var(--radius-full);
187
+ background: var(--accent);
188
+ pointer-events: none;
189
+ will-change: transform, width;
190
+ }
191
+
192
+ /* -- Panel -----------------------------------------------------------------
193
+ The panel ROOT is static chrome - borders, padding, background put on it
194
+ by the consumer never move. Only .tab-panel__inner (the content) enters
195
+ from the direction of travel (Motion, TabPanel). The root is focusable per
196
+ the APG (tabindex=0) so keyboard users land on content right after the
197
+ tablist; the ring shows only for keyboard focus. */
198
+ .tab-panel {
199
+ outline: none;
200
+ overflow: hidden;
201
+ }
202
+ .tab-panel:focus-visible {
203
+ box-shadow: var(--ring-accent);
204
+ border-radius: var(--radius-md);
205
+ }
206
+ .tab-panel__inner {
207
+ will-change: transform, opacity;
208
+ }
package/dist/tabs.d.ts ADDED
@@ -0,0 +1,71 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, CSSProperties, HTMLAttributes } from 'react';
3
+ import { D as DisableableAnimation } from './timing-7JA_zQHP.js';
4
+ import { D as DataAttributes } from './dom-props-C0jrllBw.js';
5
+ import './motion-scale-Canq4stZ.js';
6
+
7
+ /** One tab in the row. */
8
+ interface TabItem {
9
+ /** Stable identity - also used in the tab/panel id pair. */
10
+ value: string;
11
+ /** Visible tab text. */
12
+ label: ReactNode;
13
+ /** Leading icon - your own node. */
14
+ icon?: ReactNode;
15
+ /** Rendered mono + tabular. Pass a number or a preformatted string. */
16
+ count?: number | string;
17
+ /** Dims the tab, blocks selection, and skips it during arrow-key travel. */
18
+ disabled?: boolean;
19
+ }
20
+ interface TabsOwnProps {
21
+ /** The tab row, in order. @default [] */
22
+ items: TabItem[];
23
+ /** Controlled - the value of the active tab (null/undefined hides the ink). */
24
+ value: string | null | undefined;
25
+ /**
26
+ * Fires on click and on arrow-key travel (automatic activation).
27
+ * `dir` is the direction of travel (+1 right / -1 left) - hand it to
28
+ * TabPanel so content enters from the side the user moved toward.
29
+ */
30
+ onChange?: (value: string, dir: 1 | -1) => void;
31
+ /**
32
+ * Shared id prefix wiring tab and panel aria. Give Tabs and its TabPanel the
33
+ * same `name`; omit it when the tabs have no managed panel.
34
+ */
35
+ name?: string;
36
+ /** Accessible name for the tablist (e.g. "Section views"). */
37
+ ariaLabel?: string;
38
+ /** Extra class(es) merged onto the root. */
39
+ className?: string;
40
+ /** Inline styles merged onto the root. */
41
+ style?: CSSProperties;
42
+ }
43
+ interface TabsProps extends TabsOwnProps {
44
+ /** Standard <div> attributes (data-*, aria-*, ...) forwarded to the root. */
45
+ htmlProps?: Omit<HTMLAttributes<HTMLDivElement>, keyof TabsOwnProps> & DataAttributes;
46
+ }
47
+ declare function Tabs({ items, value, onChange, name, ariaLabel, className, style, htmlProps }: TabsProps): react.JSX.Element;
48
+ interface TabPanelOwnProps {
49
+ /** The active tab's value - changing it cuts to the new content. */
50
+ tab: string;
51
+ /** Same `name` as the paired Tabs - wires role/id/aria-labelledby. */
52
+ name?: string;
53
+ /** Direction of travel from Tabs' onChange; 0 = plain fade. */
54
+ dir?: -1 | 0 | 1;
55
+ /** Panel content - only this inner node animates in on `tab` change; the root chrome stays static. */
56
+ children?: ReactNode;
57
+ /** Entrance timing for the inner content - motion tokens only, or `null` to disable.
58
+ * @default duration 'base' + ease 'entrance' */
59
+ animation?: DisableableAnimation;
60
+ /** Extra class(es) merged onto the root. */
61
+ className?: string;
62
+ /** Inline styles merged onto the root. */
63
+ style?: CSSProperties;
64
+ }
65
+ interface TabPanelProps extends TabPanelOwnProps {
66
+ /** Standard <div> attributes (data-*, aria-*, ...) forwarded to the root. */
67
+ htmlProps?: Omit<HTMLAttributes<HTMLDivElement>, keyof TabPanelOwnProps> & DataAttributes;
68
+ }
69
+ declare function TabPanel({ tab, name, dir, className, style, children, animation, htmlProps }: TabPanelProps): react.JSX.Element;
70
+
71
+ export { type TabItem, TabPanel, type TabPanelProps, Tabs, type TabsProps };