@devalok/shilp-sutra 0.55.1 → 0.57.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 (270) hide show
  1. package/BREAKING.json +13 -0
  2. package/MIGRATION.md +163 -0
  3. package/README.md +16 -1
  4. package/dist/_chunks/badge-group.js +92 -78
  5. package/dist/_chunks/badge-group.js.map +1 -1
  6. package/dist/_chunks/chat.js +91 -75
  7. package/dist/_chunks/chat.js.map +1 -1
  8. package/dist/_chunks/document-preview.js +1 -1
  9. package/dist/_chunks/document-preview.js.map +1 -1
  10. package/dist/_chunks/emoji-suggestion.js +32 -30
  11. package/dist/_chunks/emoji-suggestion.js.map +1 -1
  12. package/dist/_chunks/mention-suggestion.js +36 -35
  13. package/dist/_chunks/mention-suggestion.js.map +1 -1
  14. package/dist/_chunks/oauth-button.js +1 -1
  15. package/dist/_chunks/oauth-button.js.map +1 -1
  16. package/dist/_chunks/primitives-internal.js +437 -537
  17. package/dist/_chunks/primitives-internal.js.map +1 -1
  18. package/dist/_chunks/success.js +2 -2
  19. package/dist/_chunks/success.js.map +1 -1
  20. package/dist/_chunks/tree-view.js +1 -1
  21. package/dist/_chunks/tree-view.js.map +1 -1
  22. package/dist/_chunks/use-calendar.js +4 -4
  23. package/dist/_chunks/use-calendar.js.map +1 -1
  24. package/dist/ai/blocks/text.js +1 -1
  25. package/dist/ai/blocks/text.js.map +1 -1
  26. package/dist/ai/command-bar.js +9 -9
  27. package/dist/ai/command-bar.js.map +1 -1
  28. package/dist/ai/conversation.js +2 -2
  29. package/dist/ai/conversation.js.map +1 -1
  30. package/dist/composed/activity-feed.js +1 -1
  31. package/dist/composed/activity-feed.js.map +1 -1
  32. package/dist/composed/avatar-group.d.ts +7 -2
  33. package/dist/composed/avatar-group.d.ts.map +1 -1
  34. package/dist/composed/avatar-group.js +3 -3
  35. package/dist/composed/avatar-group.js.map +1 -1
  36. package/dist/composed/command-palette.js +7 -7
  37. package/dist/composed/command-palette.js.map +1 -1
  38. package/dist/composed/content-card.js +20 -20
  39. package/dist/composed/content-card.js.map +1 -1
  40. package/dist/composed/date-picker/index.js +4 -4
  41. package/dist/composed/date-picker/index.js.map +1 -1
  42. package/dist/composed/diff.d.ts +23 -2
  43. package/dist/composed/diff.d.ts.map +1 -1
  44. package/dist/composed/diff.js +341 -260
  45. package/dist/composed/diff.js.map +1 -1
  46. package/dist/composed/emoji-picker.js +3 -3
  47. package/dist/composed/emoji-picker.js.map +1 -1
  48. package/dist/composed/error-boundary.js +2 -2
  49. package/dist/composed/error-boundary.js.map +1 -1
  50. package/dist/composed/extensions/emoji-node.js +8 -7
  51. package/dist/composed/extensions/emoji-node.js.map +1 -1
  52. package/dist/composed/file-preview.js +1 -1
  53. package/dist/composed/file-preview.js.map +1 -1
  54. package/dist/composed/filter-bar.js +1 -1
  55. package/dist/composed/filter-bar.js.map +1 -1
  56. package/dist/composed/inline-edit.js +1 -1
  57. package/dist/composed/inline-edit.js.map +1 -1
  58. package/dist/composed/loading-skeleton.js +3 -3
  59. package/dist/composed/loading-skeleton.js.map +1 -1
  60. package/dist/composed/master-detail.js +1 -1
  61. package/dist/composed/master-detail.js.map +1 -1
  62. package/dist/composed/member-picker.js +1 -1
  63. package/dist/composed/member-picker.js.map +1 -1
  64. package/dist/composed/multi-select-popover.js +1 -1
  65. package/dist/composed/multi-select-popover.js.map +1 -1
  66. package/dist/composed/page-skeletons.js +4 -4
  67. package/dist/composed/page-skeletons.js.map +1 -1
  68. package/dist/composed/responsive-modal.js +1 -1
  69. package/dist/composed/responsive-modal.js.map +1 -1
  70. package/dist/composed/rich-chat-input.js +549 -540
  71. package/dist/composed/rich-chat-input.js.map +1 -1
  72. package/dist/composed/rich-text-editor.js +206 -198
  73. package/dist/composed/rich-text-editor.js.map +1 -1
  74. package/dist/composed/schedule-view.js +5 -5
  75. package/dist/composed/schedule-view.js.map +1 -1
  76. package/dist/hooks/index.d.ts +1 -0
  77. package/dist/hooks/index.d.ts.map +1 -1
  78. package/dist/hooks/index.js +4 -3
  79. package/dist/hooks/use-container-size.d.ts +18 -0
  80. package/dist/hooks/use-container-size.d.ts.map +1 -0
  81. package/dist/hooks/use-container-size.js +28 -0
  82. package/dist/hooks/use-container-size.js.map +1 -0
  83. package/dist/shell/app-shell.d.ts +101 -0
  84. package/dist/shell/app-shell.d.ts.map +1 -0
  85. package/dist/shell/app-shell.js +76 -0
  86. package/dist/shell/app-shell.js.map +1 -0
  87. package/dist/shell/bottom-navbar.js +2 -2
  88. package/dist/shell/bottom-navbar.js.map +1 -1
  89. package/dist/shell/index.d.ts +1 -0
  90. package/dist/shell/index.d.ts.map +1 -1
  91. package/dist/shell/index.js +6 -5
  92. package/dist/shell/notification-center.js +4 -4
  93. package/dist/shell/notification-center.js.map +1 -1
  94. package/dist/shell/notification-preferences.js +1 -1
  95. package/dist/shell/notification-preferences.js.map +1 -1
  96. package/dist/shell/top-bar.js +7 -7
  97. package/dist/shell/top-bar.js.map +1 -1
  98. package/dist/tokens/generate-scale.d.ts +49 -0
  99. package/dist/tokens/generate-scale.d.ts.map +1 -1
  100. package/dist/tokens/primitives.css +29 -26
  101. package/dist/tokens/semantic.css +98 -39
  102. package/dist/tokens/variants.css +1 -1
  103. package/dist/ui/accordion.js +1 -1
  104. package/dist/ui/accordion.js.map +1 -1
  105. package/dist/ui/alert-dialog.js +1 -1
  106. package/dist/ui/alert-dialog.js.map +1 -1
  107. package/dist/ui/alert.d.ts.map +1 -1
  108. package/dist/ui/alert.js +36 -36
  109. package/dist/ui/alert.js.map +1 -1
  110. package/dist/ui/avatar.js +44 -44
  111. package/dist/ui/avatar.js.map +1 -1
  112. package/dist/ui/badge-indicator.js +1 -1
  113. package/dist/ui/badge-indicator.js.map +1 -1
  114. package/dist/ui/badge.d.ts +47 -33
  115. package/dist/ui/badge.d.ts.map +1 -1
  116. package/dist/ui/banner.js +26 -26
  117. package/dist/ui/banner.js.map +1 -1
  118. package/dist/ui/button.js +35 -35
  119. package/dist/ui/button.js.map +1 -1
  120. package/dist/ui/card.js +42 -42
  121. package/dist/ui/card.js.map +1 -1
  122. package/dist/ui/chat/date-separator.d.ts +8 -1
  123. package/dist/ui/chat/date-separator.d.ts.map +1 -1
  124. package/dist/ui/checkbox.js +1 -1
  125. package/dist/ui/checkbox.js.map +1 -1
  126. package/dist/ui/code.js +2 -2
  127. package/dist/ui/code.js.map +1 -1
  128. package/dist/ui/color-input.js +3 -3
  129. package/dist/ui/color-input.js.map +1 -1
  130. package/dist/ui/combobox.js +139 -139
  131. package/dist/ui/combobox.js.map +1 -1
  132. package/dist/ui/context-menu.js +4 -4
  133. package/dist/ui/context-menu.js.map +1 -1
  134. package/dist/ui/data-table-body.d.ts +3 -1
  135. package/dist/ui/data-table-body.d.ts.map +1 -1
  136. package/dist/ui/data-table-body.js +93 -92
  137. package/dist/ui/data-table-body.js.map +1 -1
  138. package/dist/ui/data-table-bulk-actions.d.ts +7 -1
  139. package/dist/ui/data-table-bulk-actions.d.ts.map +1 -1
  140. package/dist/ui/data-table-bulk-actions.js +14 -8
  141. package/dist/ui/data-table-bulk-actions.js.map +1 -1
  142. package/dist/ui/data-table-card.d.ts.map +1 -1
  143. package/dist/ui/data-table-card.js +66 -47
  144. package/dist/ui/data-table-card.js.map +1 -1
  145. package/dist/ui/data-table-context.d.ts +4 -0
  146. package/dist/ui/data-table-context.d.ts.map +1 -1
  147. package/dist/ui/data-table-context.js.map +1 -1
  148. package/dist/ui/data-table-header.d.ts.map +1 -1
  149. package/dist/ui/data-table-header.js +18 -15
  150. package/dist/ui/data-table-header.js.map +1 -1
  151. package/dist/ui/data-table-pagination.js +3 -3
  152. package/dist/ui/data-table-pagination.js.map +1 -1
  153. package/dist/ui/data-table-toolbar.d.ts +11 -1
  154. package/dist/ui/data-table-toolbar.d.ts.map +1 -1
  155. package/dist/ui/data-table-toolbar.js +7 -7
  156. package/dist/ui/data-table-toolbar.js.map +1 -1
  157. package/dist/ui/data-table.d.ts +52 -5
  158. package/dist/ui/data-table.d.ts.map +1 -1
  159. package/dist/ui/data-table.js +181 -169
  160. package/dist/ui/data-table.js.map +1 -1
  161. package/dist/ui/dialog.js +1 -1
  162. package/dist/ui/dialog.js.map +1 -1
  163. package/dist/ui/dot.js +13 -13
  164. package/dist/ui/dot.js.map +1 -1
  165. package/dist/ui/dropdown-menu.js +4 -4
  166. package/dist/ui/dropdown-menu.js.map +1 -1
  167. package/dist/ui/file-upload.js +3 -3
  168. package/dist/ui/file-upload.js.map +1 -1
  169. package/dist/ui/input-otp.js +1 -1
  170. package/dist/ui/input-otp.js.map +1 -1
  171. package/dist/ui/input.js +30 -30
  172. package/dist/ui/input.js.map +1 -1
  173. package/dist/ui/lib/utils.js +2 -1
  174. package/dist/ui/lib/utils.js.map +1 -1
  175. package/dist/ui/menubar.js +5 -5
  176. package/dist/ui/menubar.js.map +1 -1
  177. package/dist/ui/navigation-menu.js +1 -1
  178. package/dist/ui/navigation-menu.js.map +1 -1
  179. package/dist/ui/number-input.js +36 -36
  180. package/dist/ui/number-input.js.map +1 -1
  181. package/dist/ui/pagination.js +1 -1
  182. package/dist/ui/pagination.js.map +1 -1
  183. package/dist/ui/progress-ring.js +2 -2
  184. package/dist/ui/progress-ring.js.map +1 -1
  185. package/dist/ui/progress.d.ts.map +1 -1
  186. package/dist/ui/progress.js +66 -62
  187. package/dist/ui/progress.js.map +1 -1
  188. package/dist/ui/radio.js +1 -1
  189. package/dist/ui/radio.js.map +1 -1
  190. package/dist/ui/select.js +61 -61
  191. package/dist/ui/select.js.map +1 -1
  192. package/dist/ui/sheet.js +47 -47
  193. package/dist/ui/sheet.js.map +1 -1
  194. package/dist/ui/sidebar.js +132 -132
  195. package/dist/ui/sidebar.js.map +1 -1
  196. package/dist/ui/skeleton.js +39 -39
  197. package/dist/ui/slider.js +18 -18
  198. package/dist/ui/slider.js.map +1 -1
  199. package/dist/ui/split-button.js +3 -3
  200. package/dist/ui/split-button.js.map +1 -1
  201. package/dist/ui/stat-card.js +1 -1
  202. package/dist/ui/stat-card.js.map +1 -1
  203. package/dist/ui/stepper.js +2 -2
  204. package/dist/ui/stepper.js.map +1 -1
  205. package/dist/ui/surface.d.ts.map +1 -1
  206. package/dist/ui/surface.js +11 -11
  207. package/dist/ui/surface.js.map +1 -1
  208. package/dist/ui/switch.js +1 -1
  209. package/dist/ui/switch.js.map +1 -1
  210. package/dist/ui/table.js +1 -1
  211. package/dist/ui/table.js.map +1 -1
  212. package/dist/ui/tabs.d.ts.map +1 -1
  213. package/dist/ui/tabs.js +48 -48
  214. package/dist/ui/tabs.js.map +1 -1
  215. package/dist/ui/text.js +8 -8
  216. package/dist/ui/textarea.js +17 -17
  217. package/dist/ui/textarea.js.map +1 -1
  218. package/dist/ui/toast.js +1 -1
  219. package/dist/ui/toast.js.map +1 -1
  220. package/dist/ui/toggle.js +15 -15
  221. package/dist/ui/toggle.js.map +1 -1
  222. package/docs/components/composed/avatar-group.md +4 -4
  223. package/docs/components/composed/diff.md +10 -2
  224. package/docs/components/composed/schedule-view.md +1 -1
  225. package/docs/components/shell/app-shell.md +132 -0
  226. package/docs/components/ui/avatar.md +1 -1
  227. package/docs/components/ui/button.md +1 -1
  228. package/docs/components/ui/chat.md +7 -1
  229. package/docs/components/ui/color-swatch.md +1 -1
  230. package/docs/components/ui/data-table-toolbar.md +6 -2
  231. package/docs/components/ui/data-table.md +41 -7
  232. package/docs/components/ui/dot.md +1 -1
  233. package/docs/components/ui/progress.md +1 -0
  234. package/docs/components/ui/surface.md +3 -3
  235. package/docs/components/ui/table.md +2 -2
  236. package/docs/recipes/install-astro.md +3 -2
  237. package/docs/recipes/install-next-app-router.md +3 -2
  238. package/docs/recipes/install-remix.md +3 -2
  239. package/docs/recipes/install-tanstack-start.md +3 -2
  240. package/docs/recipes/install-vite.md +3 -2
  241. package/docs/recipes/troubleshoot.md +26 -5
  242. package/llms.txt +2 -1
  243. package/make-kit/Guidelines.md +2 -2
  244. package/make-kit/components/button.md +1 -1
  245. package/make-kit/components/card.md +6 -6
  246. package/make-kit/components/surface.md +4 -4
  247. package/make-kit/foundations/color.md +4 -4
  248. package/make-kit/foundations/dark-mode.md +1 -1
  249. package/make-kit/foundations/radius.md +1 -1
  250. package/make-kit/foundations/surfaces.md +9 -9
  251. package/mcp-manifest.json +373 -34
  252. package/package.json +43 -13
  253. package/skill/SKILL.md +12 -6
  254. package/skill/references/components.md +2 -1
  255. package/skill/references/setup-astro.md +3 -2
  256. package/skill/references/setup-next-app-router.md +3 -2
  257. package/skill/references/setup-remix.md +3 -2
  258. package/skill/references/setup-tanstack-start.md +3 -2
  259. package/skill/references/setup-vite.md +3 -2
  260. package/skill/references/troubleshoot.md +26 -5
  261. package/dist/_chunks/tiptap.js +0 -18809
  262. package/dist/_chunks/tiptap.js.map +0 -1
  263. package/dist/_chunks/vendor-utils.js +0 -1766
  264. package/dist/_chunks/vendor-utils.js.map +0 -1
  265. package/dist/primitives/react-toast.d.ts +0 -120
  266. package/dist/ui/charts/_internal/scales.d.ts +0 -5
  267. package/dist/ui/charts/_internal/scales.d.ts.map +0 -1
  268. package/dist/ui/lib/slot.d.ts +0 -8
  269. package/dist/ui/lib/slot.d.ts.map +0 -1
  270. package/scripts/welcome.mjs +0 -312
@@ -9,8 +9,8 @@ import { Fragment as o, jsx as s, jsxs as c } from "react/jsx-runtime";
9
9
  import { flexRender as l } from "@tanstack/react-table";
10
10
  //#region src/ui/data-table-card.tsx
11
11
  function u({ loading: u, skeletonRowCount: d, noResultsText: f, emptyState: p }) {
12
- let { table: m, selectable: h } = a(), g = m.getRowModel().rows;
13
- return u ? /* @__PURE__ */ s("div", {
12
+ let { table: m, selectable: h, filterable: g, filterableColumns: _, rowClassName: v } = a(), y = m.getRowModel().rows;
13
+ if (u) return /* @__PURE__ */ s("div", {
14
14
  className: "flex flex-col gap-ds-03",
15
15
  children: Array.from({ length: d }, (e, i) => /* @__PURE__ */ s(n, {
16
16
  size: "sm",
@@ -40,54 +40,73 @@ function u({ loading: u, skeletonRowCount: d, noResultsText: f, emptyState: p })
40
40
  ]
41
41
  })] })
42
42
  }, `card-skeleton-${i}`))
43
- }) : g.length ? /* @__PURE__ */ s("div", {
44
- className: "flex flex-col gap-ds-03",
45
- role: "list",
46
- children: g.map((t) => {
47
- let a = t.getVisibleCells().filter((e) => e.column.id !== "_select" && e.column.id !== "_expand"), u = a[0], d = a.slice(1), f = t.getIsSelected();
48
- return /* @__PURE__ */ c(n, {
49
- role: "listitem",
50
- size: "sm",
51
- variant: "outline",
52
- className: e(f && "ring-2 ring-accent-9"),
53
- children: [/* @__PURE__ */ c(r, {
54
- className: "flex items-start justify-between gap-ds-03",
55
- children: [/* @__PURE__ */ s("div", {
56
- className: "min-w-0 flex-1 font-medium text-surface-fg",
57
- children: u && l(u.column.columnDef.cell, u.getContext())
58
- }), h && /* @__PURE__ */ s(i, {
59
- size: "sm",
60
- checked: f,
61
- disabled: !t.getCanSelect(),
62
- onCheckedChange: (e) => t.toggleSelected(!!e),
63
- "aria-label": "Select row",
64
- className: "shrink-0"
65
- })]
66
- }), d.length > 0 && /* @__PURE__ */ c(o, { children: [/* @__PURE__ */ s("div", {
67
- "aria-hidden": "true",
68
- className: "h-px w-full bg-surface-border-subtle"
69
- }), /* @__PURE__ */ s(r, {
70
- className: "flex flex-col gap-ds-01",
71
- children: d.map((e) => {
72
- let t = e.column.columnDef.meta;
73
- return t?.hideBelow === "md" || t?.hideBelow, /* @__PURE__ */ c("div", {
74
- className: "flex justify-between gap-ds-03 text-body-sm",
75
- children: [/* @__PURE__ */ s("span", {
76
- className: "shrink-0 text-surface-fg-muted",
77
- children: typeof e.column.columnDef.header == "string" ? e.column.columnDef.header : e.column.id
78
- }), /* @__PURE__ */ s("span", {
79
- className: "min-w-0 text-right text-surface-fg",
80
- children: l(e.column.columnDef.cell, e.getContext())
81
- })]
82
- }, e.id);
83
- })
84
- })] })]
85
- }, t.id);
86
- })
87
- }) : /* @__PURE__ */ s("div", {
43
+ });
44
+ if (!y.length) return /* @__PURE__ */ s("div", {
88
45
  className: e("py-ds-07 text-center", !p && "text-surface-fg-subtle"),
89
46
  children: p || f || "No results."
90
47
  });
48
+ let b = g ? m.getAllLeafColumns().filter((e) => e.id === "_select" || e.id === "_expand" ? !1 : _ ? _.includes(e.id) : e.columnDef.enableColumnFilter !== !1) : [];
49
+ return /* @__PURE__ */ c("div", {
50
+ className: "flex flex-col gap-ds-03",
51
+ children: [b.length > 0 && /* @__PURE__ */ s("div", {
52
+ className: "flex flex-col gap-ds-02",
53
+ children: b.map((t) => {
54
+ let n = typeof t.columnDef.header == "string" ? t.columnDef.header : t.id;
55
+ return /* @__PURE__ */ s("input", {
56
+ type: "text",
57
+ value: t.getFilterValue() ?? "",
58
+ onChange: (e) => t.setFilterValue(e.target.value),
59
+ placeholder: `Filter ${n}...`,
60
+ "aria-label": `Filter ${n}`,
61
+ className: e("h-ds-xs-plus w-full rounded-control", "border border-card-strong bg-surface-panel-hover", "px-ds-02 text-body-sm", "text-surface-fg placeholder:text-surface-fg-subtle", "outline-hidden focus:border-accent-7")
62
+ }, t.id);
63
+ })
64
+ }), /* @__PURE__ */ s("div", {
65
+ className: "flex flex-col gap-ds-03",
66
+ role: "list",
67
+ children: y.map((t) => {
68
+ let a = t.getVisibleCells().filter((e) => e.column.id !== "_select" && e.column.id !== "_expand"), u = a[0], d = a.slice(1), f = t.getIsSelected();
69
+ return /* @__PURE__ */ c(n, {
70
+ role: "listitem",
71
+ size: "sm",
72
+ variant: "outline",
73
+ className: e(f && "ring-2 ring-accent-9", v?.(t.original)),
74
+ children: [/* @__PURE__ */ c(r, {
75
+ className: "flex items-start justify-between gap-ds-03",
76
+ children: [/* @__PURE__ */ s("div", {
77
+ className: "min-w-0 flex-1 font-medium text-surface-fg",
78
+ children: u && l(u.column.columnDef.cell, u.getContext())
79
+ }), h && /* @__PURE__ */ s(i, {
80
+ size: "sm",
81
+ checked: f,
82
+ disabled: !t.getCanSelect(),
83
+ onCheckedChange: (e) => t.toggleSelected(!!e),
84
+ "aria-label": "Select row",
85
+ className: "shrink-0"
86
+ })]
87
+ }), d.length > 0 && /* @__PURE__ */ c(o, { children: [/* @__PURE__ */ s("div", {
88
+ "aria-hidden": "true",
89
+ className: "h-px w-full bg-surface-border-subtle"
90
+ }), /* @__PURE__ */ s(r, {
91
+ className: "flex flex-col gap-ds-01",
92
+ children: d.map((e) => {
93
+ let t = e.column.columnDef.meta;
94
+ return t?.hideBelow === "md" || t?.hideBelow, /* @__PURE__ */ c("div", {
95
+ className: "flex justify-between gap-ds-03 text-body-sm",
96
+ children: [/* @__PURE__ */ s("span", {
97
+ className: "shrink-0 text-surface-fg-muted",
98
+ children: typeof e.column.columnDef.header == "string" ? e.column.columnDef.header : e.column.id
99
+ }), /* @__PURE__ */ s("span", {
100
+ className: "min-w-0 text-right text-surface-fg",
101
+ children: l(e.column.columnDef.cell, e.getContext())
102
+ })]
103
+ }, e.id);
104
+ })
105
+ })] })]
106
+ }, t.id);
107
+ })
108
+ })]
109
+ });
91
110
  }
92
111
  //#endregion
93
112
  export { u as DataTableCards };
@@ -1 +1 @@
1
- {"version":3,"file":"data-table-card.js","names":[],"sources":["../../src/ui/data-table-card.tsx"],"sourcesContent":["'use client'\n\nimport { flexRender } from '@tanstack/react-table'\nimport React from 'react'\n\nimport { Card, CardContent } from './card'\nimport { Checkbox } from './checkbox'\nimport { useDataTableContext } from './data-table-context'\nimport { cn } from './lib/utils'\nimport { Skeleton } from './skeleton'\n\n// ── DataTableCards ─────────────────────────────────────────────\n// Renders rows as vertically stacked cards for small (< sm) viewports.\n// Consumes the same TanStack Table instance from context — sorting,\n// filtering, pagination, and selection all work identically.\n\nexport function DataTableCards<TData>({\n loading,\n skeletonRowCount,\n noResultsText,\n emptyState,\n}: {\n loading?: boolean\n skeletonRowCount: number\n noResultsText?: string\n emptyState?: React.ReactNode\n}) {\n const { table, selectable } = useDataTableContext<TData>()\n const rows = table.getRowModel().rows\n\n // Loading skeleton\n if (loading) {\n return (\n <div className=\"flex flex-col gap-ds-03\">\n {Array.from({ length: skeletonRowCount }, (_, i) => (\n <Card key={`card-skeleton-${i}`} size=\"sm\" variant=\"outline\">\n <CardContent>\n <Skeleton variant=\"text\" className=\"mb-ds-03 h-5 w-2/3\" animation=\"pulse\" />\n <div className=\"flex flex-col gap-ds-02\">\n <Skeleton variant=\"text\" className=\"h-4 w-full\" animation=\"pulse\" />\n <Skeleton variant=\"text\" className=\"h-4 w-3/4\" animation=\"pulse\" />\n <Skeleton variant=\"text\" className=\"h-4 w-1/2\" animation=\"pulse\" />\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n )\n }\n\n // Empty state\n if (!rows.length) {\n return (\n <div className={cn('py-ds-07 text-center', !emptyState && 'text-surface-fg-subtle')}>\n {emptyState || noResultsText || 'No results.'}\n </div>\n )\n }\n\n return (\n <div className=\"flex flex-col gap-ds-03\" role=\"list\">\n {rows.map((row) => {\n const cells = row.getVisibleCells()\n // Skip internal columns (_select, _expand) for card content\n const contentCells = cells.filter(\n (cell) => cell.column.id !== '_select' && cell.column.id !== '_expand',\n )\n const primaryCell = contentCells[0]\n const restCells = contentCells.slice(1)\n const isSelected = row.getIsSelected()\n\n return (\n <Card\n key={row.id}\n role=\"listitem\"\n size=\"sm\"\n // outline, not default — a phone screen of stacked shadow cards\n // accumulates lift (make-kit dense-list rule).\n variant=\"outline\"\n className={cn(isSelected && 'ring-2 ring-accent-9')}\n >\n {/* Header row: primary field + optional selection checkbox */}\n <CardContent className=\"flex items-start justify-between gap-ds-03\">\n <div className=\"min-w-0 flex-1 font-medium text-surface-fg\">\n {primaryCell &&\n flexRender(primaryCell.column.columnDef.cell, primaryCell.getContext())}\n </div>\n {selectable && (\n <Checkbox\n size=\"sm\"\n checked={isSelected}\n disabled={!row.getCanSelect()}\n onCheckedChange={(v) => row.toggleSelected(!!v)}\n aria-label=\"Select row\"\n className=\"shrink-0\"\n />\n )}\n </CardContent>\n\n {/* Label-value pairs for remaining columns, behind a full-width rule */}\n {restCells.length > 0 && (\n <>\n <div aria-hidden=\"true\" className=\"h-px w-full bg-surface-border-subtle\" />\n <CardContent className=\"flex flex-col gap-ds-01\">\n {restCells.map((cell) => {\n // Respect hideBelow column meta — skip hidden columns\n const meta = cell.column.columnDef.meta as\n | { hideBelow?: string }\n | undefined\n // In card mode on mobile, hideBelow='sm' columns are still useful\n // since we're showing all data in a card layout. Only skip if\n // the column is explicitly hidden via visibility toggle.\n if (meta?.hideBelow === 'md' || meta?.hideBelow === 'lg') {\n // These would normally be hidden at this breakpoint in table\n // mode too, so skip them in cards for consistency\n }\n\n // Derive header label text\n const header =\n typeof cell.column.columnDef.header === 'string'\n ? cell.column.columnDef.header\n : cell.column.id\n\n return (\n <div key={cell.id} className=\"flex justify-between gap-ds-03 text-body-sm\">\n <span className=\"shrink-0 text-surface-fg-muted\">{header}</span>\n <span className=\"min-w-0 text-right text-surface-fg\">\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </span>\n </div>\n )\n })}\n </CardContent>\n </>\n )}\n </Card>\n )\n })}\n </div>\n )\n}\n"],"mappings":";;;;;;;;;;AAgBA,SAAgB,EAAsB,EACpC,YACA,qBACA,kBACA,iBAMC;CACD,IAAM,EAAE,UAAO,kBAAe,EAA2B,GACnD,IAAO,EAAM,YAAY,CAAC,CAAC;CA+BjC,OA5BI,IAEA,kBAAC,OAAD;EAAK,WAAU;YACZ,MAAM,KAAK,EAAE,QAAQ,EAAiB,IAAI,GAAG,MAC5C,kBAAC,GAAD;GAAiC,MAAK;GAAK,SAAQ;aACjD,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;IAAU,SAAQ;IAAO,WAAU;IAAqB,WAAU;GAAS,CAAA,GAC3E,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAU,SAAQ;MAAO,WAAU;MAAa,WAAU;KAAS,CAAA;KACnE,kBAAC,GAAD;MAAU,SAAQ;MAAO,WAAU;MAAY,WAAU;KAAS,CAAA;KAClE,kBAAC,GAAD;MAAU,SAAQ;MAAO,WAAU;MAAY,WAAU;KAAS,CAAA;IAC/D;KACM,EAAA,CAAA;EACT,GATK,iBAAiB,GAStB,CACP;CACE,CAAA,IAKJ,EAAK,SASR,kBAAC,OAAD;EAAK,WAAU;EAA0B,MAAK;YAC3C,EAAK,KAAK,MAAQ;GAGjB,IAAM,IAFQ,EAAI,gBAEG,CAAA,CAAM,QACxB,MAAS,EAAK,OAAO,OAAO,aAAa,EAAK,OAAO,OAAO,SAC/D,GACM,IAAc,EAAa,IAC3B,IAAY,EAAa,MAAM,CAAC,GAChC,IAAa,EAAI,cAAc;GAErC,OACE,kBAAC,GAAD;IAEE,MAAK;IACL,MAAK;IAGL,SAAQ;IACR,WAAW,EAAG,KAAc,sBAAsB;cAPpD,CAUE,kBAAC,GAAD;KAAa,WAAU;eAAvB,CACE,kBAAC,OAAD;MAAK,WAAU;gBACZ,KACC,EAAW,EAAY,OAAO,UAAU,MAAM,EAAY,WAAW,CAAC;KACrE,CAAA,GACJ,KACC,kBAAC,GAAD;MACE,MAAK;MACL,SAAS;MACT,UAAU,CAAC,EAAI,aAAa;MAC5B,kBAAkB,MAAM,EAAI,eAAe,CAAC,CAAC,CAAC;MAC9C,cAAW;MACX,WAAU;KACX,CAAA,CAEQ;QAGZ,EAAU,SAAS,KAClB,kBAAA,GAAA,EAAA,UAAA,CACA,kBAAC,OAAD;KAAK,eAAY;KAAO,WAAU;IAAwC,CAAA,GAC1E,kBAAC,GAAD;KAAa,WAAU;eACpB,EAAU,KAAK,MAAS;MAEvB,IAAM,IAAO,EAAK,OAAO,UAAU;MAiBnC,OAXI,GAAM,cAAc,QAAQ,GAAM,WAYpC,kBAAC,OAAD;OAAmB,WAAU;iBAA7B,CACE,kBAAC,QAAD;QAAM,WAAU;kBANlB,OAAO,EAAK,OAAO,UAAU,UAAW,WACpC,EAAK,OAAO,UAAU,SACtB,EAAK,OAAO;OAIiD,CAAA,GAC/D,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAW,EAAK,OAAO,UAAU,MAAM,EAAK,WAAW,CAAC;OACrD,CAAA,CACH;SALK,EAAK,EAKV;KAET,CAAC;IACU,CAAA,CACX,EAAA,CAAA,CAEA;MA9DC,EAAI,EA8DL;EAEV,CAAC;CACE,CAAA,IArFH,kBAAC,OAAD;EAAK,WAAW,EAAG,wBAAwB,CAAC,KAAc,wBAAwB;YAC/E,KAAc,KAAiB;CAC7B,CAAA;AAqFX"}
1
+ {"version":3,"file":"data-table-card.js","names":[],"sources":["../../src/ui/data-table-card.tsx"],"sourcesContent":["'use client'\n\nimport { flexRender } from '@tanstack/react-table'\nimport React from 'react'\n\nimport { Card, CardContent } from './card'\nimport { Checkbox } from './checkbox'\nimport { useDataTableContext } from './data-table-context'\nimport { cn } from './lib/utils'\nimport { Skeleton } from './skeleton'\n\n// ── DataTableCards ─────────────────────────────────────────────\n// Renders rows as vertically stacked cards for small (< sm) viewports.\n// Consumes the same TanStack Table instance from context — sorting,\n// filtering, pagination, and selection all work identically.\n\nexport function DataTableCards<TData>({\n loading,\n skeletonRowCount,\n noResultsText,\n emptyState,\n}: {\n loading?: boolean\n skeletonRowCount: number\n noResultsText?: string\n emptyState?: React.ReactNode\n}) {\n const { table, selectable, filterable, filterableColumns, rowClassName } = useDataTableContext<TData>()\n const rows = table.getRowModel().rows\n\n // Loading skeleton\n if (loading) {\n return (\n <div className=\"flex flex-col gap-ds-03\">\n {Array.from({ length: skeletonRowCount }, (_, i) => (\n <Card key={`card-skeleton-${i}`} size=\"sm\" variant=\"outline\">\n <CardContent>\n <Skeleton variant=\"text\" className=\"mb-ds-03 h-5 w-2/3\" animation=\"pulse\" />\n <div className=\"flex flex-col gap-ds-02\">\n <Skeleton variant=\"text\" className=\"h-4 w-full\" animation=\"pulse\" />\n <Skeleton variant=\"text\" className=\"h-4 w-3/4\" animation=\"pulse\" />\n <Skeleton variant=\"text\" className=\"h-4 w-1/2\" animation=\"pulse\" />\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n )\n }\n\n // Empty state\n if (!rows.length) {\n return (\n <div className={cn('py-ds-07 text-center', !emptyState && 'text-surface-fg-subtle')}>\n {emptyState || noResultsText || 'No results.'}\n </div>\n )\n }\n\n // Filterable columns that should show an input in card mode.\n // Lives above the card list since there's no <thead> in card layout.\n const filterableCols = filterable\n ? table.getAllLeafColumns().filter((col) => {\n if (col.id === '_select' || col.id === '_expand') return false\n if (filterableColumns) return filterableColumns.includes(col.id)\n return col.columnDef.enableColumnFilter !== false\n })\n : []\n\n return (\n <div className=\"flex flex-col gap-ds-03\">\n {/* Filter inputs — rendered above the card list, one per filterable column */}\n {filterableCols.length > 0 && (\n <div className=\"flex flex-col gap-ds-02\">\n {filterableCols.map((col) => {\n const headerLabel =\n typeof col.columnDef.header === 'string' ? col.columnDef.header : col.id\n return (\n <input\n key={col.id}\n type=\"text\"\n value={(col.getFilterValue() as string) ?? ''}\n onChange={(e) => col.setFilterValue(e.target.value)}\n placeholder={`Filter ${headerLabel}...`}\n aria-label={`Filter ${headerLabel}`}\n className={cn(\n 'h-ds-xs-plus w-full rounded-control',\n 'border border-card-strong bg-surface-panel-hover',\n 'px-ds-02 text-body-sm',\n 'text-surface-fg placeholder:text-surface-fg-subtle',\n 'outline-hidden focus:border-accent-7',\n )}\n />\n )\n })}\n </div>\n )}\n\n <div className=\"flex flex-col gap-ds-03\" role=\"list\">\n {rows.map((row) => {\n const cells = row.getVisibleCells()\n // Skip internal columns (_select, _expand) for card content\n const contentCells = cells.filter(\n (cell) => cell.column.id !== '_select' && cell.column.id !== '_expand',\n )\n const primaryCell = contentCells[0]\n const restCells = contentCells.slice(1)\n const isSelected = row.getIsSelected()\n\n return (\n <Card\n key={row.id}\n role=\"listitem\"\n size=\"sm\"\n // outline, not default — a phone screen of stacked shadow cards\n // accumulates lift (make-kit dense-list rule).\n variant=\"outline\"\n className={cn(isSelected && 'ring-2 ring-accent-9', rowClassName?.(row.original))}\n >\n {/* Header row: primary field + optional selection checkbox */}\n <CardContent className=\"flex items-start justify-between gap-ds-03\">\n <div className=\"min-w-0 flex-1 font-medium text-surface-fg\">\n {primaryCell &&\n flexRender(primaryCell.column.columnDef.cell, primaryCell.getContext())}\n </div>\n {selectable && (\n <Checkbox\n size=\"sm\"\n checked={isSelected}\n disabled={!row.getCanSelect()}\n onCheckedChange={(v) => row.toggleSelected(!!v)}\n aria-label=\"Select row\"\n className=\"shrink-0\"\n />\n )}\n </CardContent>\n\n {/* Label-value pairs for remaining columns, behind a full-width rule */}\n {restCells.length > 0 && (\n <>\n <div aria-hidden=\"true\" className=\"h-px w-full bg-surface-border-subtle\" />\n <CardContent className=\"flex flex-col gap-ds-01\">\n {restCells.map((cell) => {\n // Respect hideBelow column meta — skip hidden columns\n const meta = cell.column.columnDef.meta as\n | { hideBelow?: string }\n | undefined\n // In card mode on mobile, hideBelow='sm' columns are still useful\n // since we're showing all data in a card layout. Only skip if\n // the column is explicitly hidden via visibility toggle.\n if (meta?.hideBelow === 'md' || meta?.hideBelow === 'lg') {\n // These would normally be hidden at this breakpoint in table\n // mode too, so skip them in cards for consistency\n }\n\n // Derive header label text\n const header =\n typeof cell.column.columnDef.header === 'string'\n ? cell.column.columnDef.header\n : cell.column.id\n\n return (\n <div key={cell.id} className=\"flex justify-between gap-ds-03 text-body-sm\">\n <span className=\"shrink-0 text-surface-fg-muted\">{header}</span>\n <span className=\"min-w-0 text-right text-surface-fg\">\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </span>\n </div>\n )\n })}\n </CardContent>\n </>\n )}\n </Card>\n )\n })}\n </div>\n </div>\n )\n}\n"],"mappings":"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"}
@@ -21,6 +21,8 @@ export interface DataTableContextValue<TData = unknown> {
21
21
  sortable: boolean;
22
22
  /** Whether per-column filters are enabled */
23
23
  filterable: boolean;
24
+ /** When set, only these column IDs get filter inputs (subset of filterable columns) */
25
+ filterableColumns?: string[];
24
26
  /** Whether inline editing is enabled */
25
27
  editable: boolean;
26
28
  /** Whether row expansion is enabled */
@@ -41,6 +43,8 @@ export interface DataTableContextValue<TData = unknown> {
41
43
  renderExpanded?: (row: TData) => React.ReactNode;
42
44
  /** Row click handler */
43
45
  onRowClick?: (row: TData) => void;
46
+ /** Return a className for a given row — applied to the tr in table and card layouts */
47
+ rowClassName?: (row: TData) => string | undefined;
44
48
  }
45
49
  export declare function DataTableProvider<TData>({ children, value, }: {
46
50
  children: React.ReactNode;
@@ -1 +1 @@
1
- {"version":3,"file":"data-table-context.d.ts","sourceRoot":"","sources":["../../src/ui/data-table-context.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,kBAAkB,EAAE,KAAK,KAAK,EAAE,MAAM,uBAAuB,CAAA;AAC3E,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,0DAA0D;AAC1D,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAA;AAIvE,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAS3E;AAED,4DAA4D;AAC5D,eAAO,MAAM,oBAAoB,4DACwB,CAAA;AAIzD,MAAM,WAAW,qBAAqB,CAAC,KAAK,GAAG,OAAO;IACpD,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IACnB,yDAAyD;IACzD,UAAU,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAA;IAC/C,kDAAkD;IAClD,kBAAkB,EAAE,kBAAkB,CAAA;IACtC,iCAAiC;IACjC,QAAQ,EAAE,OAAO,CAAA;IACjB,6CAA6C;IAC7C,UAAU,EAAE,OAAO,CAAA;IACnB,wCAAwC;IACxC,QAAQ,EAAE,OAAO,CAAA;IACjB,uCAAuC;IACvC,UAAU,EAAE,OAAO,CAAA;IACnB,uCAAuC;IACvC,WAAW,EAAE,OAAO,CAAA;IACpB,uCAAuC;IACvC,UAAU,EAAE,OAAO,CAAA;IACnB,uBAAuB;IACvB,UAAU,EAAE,MAAM,GAAG,OAAO,CAAA;IAC5B,2BAA2B;IAC3B,WAAW,EAAE,WAAW,CAAA;IACxB,uBAAuB;IACvB,cAAc,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAA;IAC3C,2CAA2C;IAC3C,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACzE,+CAA+C;IAC/C,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,KAAK,CAAC,SAAS,CAAA;IAChD,wBAAwB;IACxB,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAA;CAClC;AAID,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,EACvC,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,KAAK,EAAE,qBAAqB,CAAC,KAAK,CAAC,CAAA;CACpC,qBAMA;AAED,wBAAgB,mBAAmB,CAAC,KAAK,GAAG,OAAO,KAGnC,qBAAqB,CAAC,KAAK,CAAC,CAC3C;AAID,2DAA2D;AAC3D,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,kBAAkB,EAAE,kBAAkB;;WASZ,KAAK,CAAC,aAAa;EAU9C;AAED,uCAAuC;AACvC,wBAAgB,gBAAgB,CAAC,KAAK,EACpC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,OAAO,EACjB,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,GAClB,OAAO,CAYT"}
1
+ {"version":3,"file":"data-table-context.d.ts","sourceRoot":"","sources":["../../src/ui/data-table-context.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,kBAAkB,EAAE,KAAK,KAAK,EAAE,MAAM,uBAAuB,CAAA;AAC3E,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,0DAA0D;AAC1D,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAA;AAIvE,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAS3E;AAED,4DAA4D;AAC5D,eAAO,MAAM,oBAAoB,4DACwB,CAAA;AAIzD,MAAM,WAAW,qBAAqB,CAAC,KAAK,GAAG,OAAO;IACpD,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IACnB,yDAAyD;IACzD,UAAU,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAA;IAC/C,kDAAkD;IAClD,kBAAkB,EAAE,kBAAkB,CAAA;IACtC,iCAAiC;IACjC,QAAQ,EAAE,OAAO,CAAA;IACjB,6CAA6C;IAC7C,UAAU,EAAE,OAAO,CAAA;IACnB,uFAAuF;IACvF,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B,wCAAwC;IACxC,QAAQ,EAAE,OAAO,CAAA;IACjB,uCAAuC;IACvC,UAAU,EAAE,OAAO,CAAA;IACnB,uCAAuC;IACvC,WAAW,EAAE,OAAO,CAAA;IACpB,uCAAuC;IACvC,UAAU,EAAE,OAAO,CAAA;IACnB,uBAAuB;IACvB,UAAU,EAAE,MAAM,GAAG,OAAO,CAAA;IAC5B,2BAA2B;IAC3B,WAAW,EAAE,WAAW,CAAA;IACxB,uBAAuB;IACvB,cAAc,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAA;IAC3C,2CAA2C;IAC3C,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACzE,+CAA+C;IAC/C,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,KAAK,CAAC,SAAS,CAAA;IAChD,wBAAwB;IACxB,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAA;IACjC,uFAAuF;IACvF,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,MAAM,GAAG,SAAS,CAAA;CAClD;AAID,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,EACvC,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,KAAK,EAAE,qBAAqB,CAAC,KAAK,CAAC,CAAA;CACpC,qBAMA;AAED,wBAAgB,mBAAmB,CAAC,KAAK,GAAG,OAAO,KAGnC,qBAAqB,CAAC,KAAK,CAAC,CAC3C;AAID,2DAA2D;AAC3D,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,kBAAkB,EAAE,kBAAkB;;WASZ,KAAK,CAAC,aAAa;EAU9C;AAED,uCAAuC;AACvC,wBAAgB,gBAAgB,CAAC,KAAK,EACpC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,OAAO,EACjB,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,GAClB,OAAO,CAYT"}
@@ -1 +1 @@
1
- {"version":3,"file":"data-table-context.js","names":[],"sources":["../../src/ui/data-table-context.tsx"],"sourcesContent":["'use client'\n\nimport { type ColumnPinningState, type Table } from '@tanstack/react-table'\nimport React from 'react'\n\n// ── Shared types ────────────────────────────────────────────────\n\n/** Editing state: which cell is currently in edit mode */\nexport type EditingCell = { rowIndex: number; columnId: string } | null\n\n// ── Utility functions ───────────────────────────────────────────\n\nexport function getColumnMetaClasses(meta?: Record<string, unknown>): string {\n if (!meta) return ''\n const classes: string[] = []\n if (meta.align === 'right') classes.push('text-right tabular-nums')\n if (meta.align === 'center') classes.push('text-center')\n if (meta.hideBelow === 'sm') classes.push('hidden sm:table-cell')\n if (meta.hideBelow === 'md') classes.push('hidden md:table-cell')\n if (meta.hideBelow === 'lg') classes.push('hidden lg:table-cell')\n return classes.join(' ')\n}\n\n/** Interactive element selectors for row click filtering */\nexport const INTERACTIVE_SELECTOR =\n 'button, a, input, select, textarea, [role=\"checkbox\"]'\n\n// ── Context ─────────────────────────────────────────────────────\n\nexport interface DataTableContextValue<TData = unknown> {\n table: Table<TData>\n /** All assembled columns (including _select, _expand) */\n allColumns: { id?: string; header?: unknown }[]\n /** Column pinning state for sticky positioning */\n columnPinningState: ColumnPinningState\n /** Whether sorting is enabled */\n sortable: boolean\n /** Whether per-column filters are enabled */\n filterable: boolean\n /** Whether inline editing is enabled */\n editable: boolean\n /** Whether row expansion is enabled */\n expandable: boolean\n /** Whether virtual rows are enabled */\n virtualRows: boolean\n /** Whether row selection is enabled */\n selectable: boolean\n /** Mobile view mode */\n mobileView: 'card' | 'table'\n /** Current editing cell */\n editingCell: EditingCell\n /** Set editing cell */\n setEditingCell: (cell: EditingCell) => void\n /** Callback when a cell value is edited */\n onCellEdit?: (rowIndex: number, columnId: string, value: unknown) => void\n /** Render function for expanded row content */\n renderExpanded?: (row: TData) => React.ReactNode\n /** Row click handler */\n onRowClick?: (row: TData) => void\n}\n\nconst DataTableContext = React.createContext<DataTableContextValue | null>(null)\n\nexport function DataTableProvider<TData>({\n children,\n value,\n}: {\n children: React.ReactNode\n value: DataTableContextValue<TData>\n}) {\n return (\n <DataTableContext.Provider value={value as DataTableContextValue}>\n {children}\n </DataTableContext.Provider>\n )\n}\n\nexport function useDataTableContext<TData = unknown>() {\n const ctx = React.useContext(DataTableContext)\n if (!ctx) throw new Error('useDataTableContext must be used within DataTableProvider')\n return ctx as DataTableContextValue<TData>\n}\n\n// ── Shared helpers ──────────────────────────────────────────────\n\n/** Compute sticky positioning styles for pinned columns */\nexport function getPinnedCellStyle(\n columnId: string,\n columnPinningState: ColumnPinningState,\n) {\n const { left = [], right = [] } = columnPinningState\n const leftIndex = left.indexOf(columnId)\n const rightIndex = right.indexOf(columnId)\n\n if (leftIndex !== -1) {\n return {\n className: 'sticky bg-surface-base z-raised',\n style: { left: 0 } as React.CSSProperties,\n }\n }\n if (rightIndex !== -1) {\n return {\n className: 'sticky bg-surface-base z-raised',\n style: { right: 0 } as React.CSSProperties,\n }\n }\n return { className: '', style: {} as React.CSSProperties }\n}\n\n/** Check if a column allows editing */\nexport function isColumnEditable<TData>(\n columnId: string,\n editable: boolean,\n table: Table<TData>,\n): boolean {\n if (!editable) return false\n // Internal columns are never editable\n if (columnId === '_select' || columnId === '_expand') return false\n const col = table.getColumn(columnId)\n if (!col) return false\n const meta = col.columnDef.meta as\n | { enableEditing?: boolean }\n | undefined\n // Editable by default unless explicitly disabled\n if (meta?.enableEditing === false) return false\n return true\n}\n"],"mappings":";;;;AAYA,SAAgB,EAAqB,GAAwC;CAC3E,IAAI,CAAC,GAAM,OAAO;CAClB,IAAM,IAAoB,CAAC;CAM3B,OALI,EAAK,UAAU,WAAS,EAAQ,KAAK,yBAAyB,GAC9D,EAAK,UAAU,YAAU,EAAQ,KAAK,aAAa,GACnD,EAAK,cAAc,QAAM,EAAQ,KAAK,sBAAsB,GAC5D,EAAK,cAAc,QAAM,EAAQ,KAAK,sBAAsB,GAC5D,EAAK,cAAc,QAAM,EAAQ,KAAK,sBAAsB,GACzD,EAAQ,KAAK,GAAG;AACzB;AAGA,IAAa,IACX,2DAoCI,IAAmB,EAAM,cAA4C,IAAI;AAE/E,SAAgB,EAAyB,EACvC,aACA,YAIC;CACD,OACE,kBAAC,EAAiB,UAAlB;EAAkC;EAC/B;CACwB,CAAA;AAE/B;AAEA,SAAgB,IAAuC;CACrD,IAAM,IAAM,EAAM,WAAW,CAAgB;CAC7C,IAAI,CAAC,GAAK,MAAU,MAAM,2DAA2D;CACrF,OAAO;AACT;AAKA,SAAgB,EACd,GACA,GACA;CACA,IAAM,EAAE,UAAO,CAAC,GAAG,WAAQ,CAAC,MAAM,GAC5B,IAAY,EAAK,QAAQ,CAAQ,GACjC,IAAa,EAAM,QAAQ,CAAQ;CAczC,OAZI,MAAc,KAMd,MAAe,KAMZ;EAAE,WAAW;EAAI,OAAO,CAAC;CAAyB,IALhD;EACL,WAAW;EACX,OAAO,EAAE,OAAO,EAAE;CACpB,IATO;EACL,WAAW;EACX,OAAO,EAAE,MAAM,EAAE;CACnB;AASJ;AAGA,SAAgB,EACd,GACA,GACA,GACS;CAGT,IAFI,CAAC,KAED,MAAa,aAAa,MAAa,WAAW,OAAO;CAC7D,IAAM,IAAM,EAAM,UAAU,CAAQ;CAOpC,OADA,EALI,CAAC,KACQ,EAAI,UAAU,MAIjB,kBAAkB;AAE9B"}
1
+ {"version":3,"file":"data-table-context.js","names":[],"sources":["../../src/ui/data-table-context.tsx"],"sourcesContent":["'use client'\n\nimport { type ColumnPinningState, type Table } from '@tanstack/react-table'\nimport React from 'react'\n\n// ── Shared types ────────────────────────────────────────────────\n\n/** Editing state: which cell is currently in edit mode */\nexport type EditingCell = { rowIndex: number; columnId: string } | null\n\n// ── Utility functions ───────────────────────────────────────────\n\nexport function getColumnMetaClasses(meta?: Record<string, unknown>): string {\n if (!meta) return ''\n const classes: string[] = []\n if (meta.align === 'right') classes.push('text-right tabular-nums')\n if (meta.align === 'center') classes.push('text-center')\n if (meta.hideBelow === 'sm') classes.push('hidden sm:table-cell')\n if (meta.hideBelow === 'md') classes.push('hidden md:table-cell')\n if (meta.hideBelow === 'lg') classes.push('hidden lg:table-cell')\n return classes.join(' ')\n}\n\n/** Interactive element selectors for row click filtering */\nexport const INTERACTIVE_SELECTOR =\n 'button, a, input, select, textarea, [role=\"checkbox\"]'\n\n// ── Context ─────────────────────────────────────────────────────\n\nexport interface DataTableContextValue<TData = unknown> {\n table: Table<TData>\n /** All assembled columns (including _select, _expand) */\n allColumns: { id?: string; header?: unknown }[]\n /** Column pinning state for sticky positioning */\n columnPinningState: ColumnPinningState\n /** Whether sorting is enabled */\n sortable: boolean\n /** Whether per-column filters are enabled */\n filterable: boolean\n /** When set, only these column IDs get filter inputs (subset of filterable columns) */\n filterableColumns?: string[]\n /** Whether inline editing is enabled */\n editable: boolean\n /** Whether row expansion is enabled */\n expandable: boolean\n /** Whether virtual rows are enabled */\n virtualRows: boolean\n /** Whether row selection is enabled */\n selectable: boolean\n /** Mobile view mode */\n mobileView: 'card' | 'table'\n /** Current editing cell */\n editingCell: EditingCell\n /** Set editing cell */\n setEditingCell: (cell: EditingCell) => void\n /** Callback when a cell value is edited */\n onCellEdit?: (rowIndex: number, columnId: string, value: unknown) => void\n /** Render function for expanded row content */\n renderExpanded?: (row: TData) => React.ReactNode\n /** Row click handler */\n onRowClick?: (row: TData) => void\n /** Return a className for a given row — applied to the tr in table and card layouts */\n rowClassName?: (row: TData) => string | undefined\n}\n\nconst DataTableContext = React.createContext<DataTableContextValue | null>(null)\n\nexport function DataTableProvider<TData>({\n children,\n value,\n}: {\n children: React.ReactNode\n value: DataTableContextValue<TData>\n}) {\n return (\n <DataTableContext.Provider value={value as DataTableContextValue}>\n {children}\n </DataTableContext.Provider>\n )\n}\n\nexport function useDataTableContext<TData = unknown>() {\n const ctx = React.useContext(DataTableContext)\n if (!ctx) throw new Error('useDataTableContext must be used within DataTableProvider')\n return ctx as DataTableContextValue<TData>\n}\n\n// ── Shared helpers ──────────────────────────────────────────────\n\n/** Compute sticky positioning styles for pinned columns */\nexport function getPinnedCellStyle(\n columnId: string,\n columnPinningState: ColumnPinningState,\n) {\n const { left = [], right = [] } = columnPinningState\n const leftIndex = left.indexOf(columnId)\n const rightIndex = right.indexOf(columnId)\n\n if (leftIndex !== -1) {\n return {\n className: 'sticky bg-surface-base z-raised',\n style: { left: 0 } as React.CSSProperties,\n }\n }\n if (rightIndex !== -1) {\n return {\n className: 'sticky bg-surface-base z-raised',\n style: { right: 0 } as React.CSSProperties,\n }\n }\n return { className: '', style: {} as React.CSSProperties }\n}\n\n/** Check if a column allows editing */\nexport function isColumnEditable<TData>(\n columnId: string,\n editable: boolean,\n table: Table<TData>,\n): boolean {\n if (!editable) return false\n // Internal columns are never editable\n if (columnId === '_select' || columnId === '_expand') return false\n const col = table.getColumn(columnId)\n if (!col) return false\n const meta = col.columnDef.meta as\n | { enableEditing?: boolean }\n | undefined\n // Editable by default unless explicitly disabled\n if (meta?.enableEditing === false) return false\n return true\n}\n"],"mappings":";;;;AAYA,SAAgB,EAAqB,GAAwC;CAC3E,IAAI,CAAC,GAAM,OAAO;CAClB,IAAM,IAAoB,CAAC;CAM3B,OALI,EAAK,UAAU,WAAS,EAAQ,KAAK,yBAAyB,GAC9D,EAAK,UAAU,YAAU,EAAQ,KAAK,aAAa,GACnD,EAAK,cAAc,QAAM,EAAQ,KAAK,sBAAsB,GAC5D,EAAK,cAAc,QAAM,EAAQ,KAAK,sBAAsB,GAC5D,EAAK,cAAc,QAAM,EAAQ,KAAK,sBAAsB,GACzD,EAAQ,KAAK,GAAG;AACzB;AAGA,IAAa,IACX,2DAwCI,IAAmB,EAAM,cAA4C,IAAI;AAE/E,SAAgB,EAAyB,EACvC,aACA,YAIC;CACD,OACE,kBAAC,EAAiB,UAAlB;EAAkC;EAC/B;CACwB,CAAA;AAE/B;AAEA,SAAgB,IAAuC;CACrD,IAAM,IAAM,EAAM,WAAW,CAAgB;CAC7C,IAAI,CAAC,GAAK,MAAU,MAAM,2DAA2D;CACrF,OAAO;AACT;AAKA,SAAgB,EACd,GACA,GACA;CACA,IAAM,EAAE,UAAO,CAAC,GAAG,WAAQ,CAAC,MAAM,GAC5B,IAAY,EAAK,QAAQ,CAAQ,GACjC,IAAa,EAAM,QAAQ,CAAQ;CAczC,OAZI,MAAc,KAMd,MAAe,KAMZ;EAAE,WAAW;EAAI,OAAO,CAAC;CAAyB,IALhD;EACL,WAAW;EACX,OAAO,EAAE,OAAO,EAAE;CACpB,IATO;EACL,WAAW;EACX,OAAO,EAAE,MAAM,EAAE;CACnB;AASJ;AAGA,SAAgB,EACd,GACA,GACA,GACS;CAGT,IAFI,CAAC,KAED,MAAa,aAAa,MAAa,WAAW,OAAO;CAC7D,IAAM,IAAM,EAAM,UAAU,CAAQ;CAOpC,OADA,EALI,CAAC,KACQ,EAAI,UAAU,MAIjB,kBAAkB;AAE9B"}
@@ -1 +1 @@
1
- {"version":3,"file":"data-table-header.d.ts","sourceRoot":"","sources":["../../src/ui/data-table-header.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,MAAM,OAAO,CAAA;AAYzB;;;GAGG;AACH,iBAAS,mBAAmB,CAAC,EAAE,YAAY,EAAE,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,qBAwJxE;AAED,OAAO,EAAE,mBAAmB,IAAI,eAAe,EAAE,CAAA"}
1
+ {"version":3,"file":"data-table-header.d.ts","sourceRoot":"","sources":["../../src/ui/data-table-header.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,MAAM,OAAO,CAAA;AAYzB;;;GAGG;AACH,iBAAS,mBAAmB,CAAC,EAAE,YAAY,EAAE,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,qBA8JxE;AAED,OAAO,EAAE,mBAAmB,IAAI,eAAe,EAAE,CAAA"}
@@ -11,18 +11,18 @@ import { AnimatePresence as m, motion as h } from "framer-motion";
11
11
  import { flexRender as g } from "@tanstack/react-table";
12
12
  //#region src/ui/data-table-header.tsx
13
13
  function _({ stickyHeader: _ }) {
14
- let { table: v, sortable: y, filterable: b, columnPinningState: x } = a();
14
+ let { table: v, sortable: y, filterable: b, filterableColumns: x, columnPinningState: S } = a();
15
15
  return /* @__PURE__ */ u(s, {
16
- className: t(_ && "sticky top-0 z-10 bg-surface-raised"),
16
+ className: t(_ && "sticky top-0 z-10 bg-surface-panel"),
17
17
  children: [v.getHeaderGroups().map((a) => /* @__PURE__ */ l(c, { children: a.headers.map((a) => {
18
- let s = y && a.column.getCanSort(), c = a.column.getIsSorted(), _ = i(a.column.id, x);
18
+ let s = y && a.column.getCanSort(), c = a.column.getIsSorted(), _ = i(a.column.id, S);
19
19
  return /* @__PURE__ */ l(o, {
20
20
  className: t(_.className, r(a.column.columnDef.meta)),
21
21
  style: _.style,
22
22
  "aria-sort": s ? c === "asc" ? "ascending" : c === "desc" ? "descending" : "none" : void 0,
23
23
  children: a.isPlaceholder ? null : s ? /* @__PURE__ */ u("button", {
24
24
  type: "button",
25
- className: t("flex items-center gap-ds-01 font-medium", "cursor-pointer select-none", "-ml-ds-01 rounded-control-inner px-ds-01 py-ds-01", "hover:bg-surface-raised-hover transition-colors"),
25
+ className: t("flex items-center gap-ds-01 font-medium", "cursor-pointer select-none", "-ml-ds-01 rounded-control-inner px-ds-01 py-ds-01", "hover:bg-surface-panel-hover transition-colors"),
26
26
  onClick: a.column.getToggleSortingHandler(),
27
27
  "aria-label": `Sort by ${typeof a.column.columnDef.header == "string" ? a.column.columnDef.header : a.column.id}`,
28
28
  children: [g(a.column.columnDef.header, a.getContext()), /* @__PURE__ */ l(m, {
@@ -83,17 +83,20 @@ function _({ stickyHeader: _ }) {
83
83
  })]
84
84
  }) : g(a.column.columnDef.header, a.getContext())
85
85
  }, a.id);
86
- }) }, a.id)), b && v.getHeaderGroups().map((e) => /* @__PURE__ */ l(c, { children: e.headers.map((e) => /* @__PURE__ */ l(o, {
87
- className: "py-ds-01",
88
- children: e.isPlaceholder || e.column.columnDef.enableColumnFilter === !1 ? null : /* @__PURE__ */ l("input", {
89
- type: "text",
90
- value: e.column.getFilterValue() ?? "",
91
- onChange: (t) => e.column.setFilterValue(t.target.value),
92
- placeholder: `Filter ${typeof e.column.columnDef.header == "string" ? e.column.columnDef.header : ""}...`,
93
- "aria-label": `Filter ${typeof e.column.columnDef.header == "string" ? e.column.columnDef.header : e.column.id}`,
94
- className: t("h-ds-xs-plus w-full rounded-control", "border border-card-strong bg-surface-raised-hover", "px-ds-02 text-body-sm", "text-surface-fg placeholder:text-surface-fg-subtle", "outline-hidden focus:border-accent-7")
95
- })
96
- }, `${e.id}-filter`)) }, `${e.id}-filters`))]
86
+ }) }, a.id)), b && v.getHeaderGroups().map((e) => /* @__PURE__ */ l(c, { children: e.headers.map((e) => {
87
+ let n = x ? x.includes(e.column.id) : e.column.columnDef.enableColumnFilter !== !1;
88
+ return /* @__PURE__ */ l(o, {
89
+ className: "py-ds-01",
90
+ children: e.isPlaceholder || !n ? null : /* @__PURE__ */ l("input", {
91
+ type: "text",
92
+ value: e.column.getFilterValue() ?? "",
93
+ onChange: (t) => e.column.setFilterValue(t.target.value),
94
+ placeholder: `Filter ${typeof e.column.columnDef.header == "string" ? e.column.columnDef.header : ""}...`,
95
+ "aria-label": `Filter ${typeof e.column.columnDef.header == "string" ? e.column.columnDef.header : e.column.id}`,
96
+ className: t("h-ds-xs-plus w-full rounded-control", "border border-card-strong bg-surface-panel-hover", "px-ds-02 text-body-sm", "text-surface-fg placeholder:text-surface-fg-subtle", "outline-hidden focus:border-accent-7")
97
+ })
98
+ }, `${e.id}-filter`);
99
+ }) }, `${e.id}-filters`))]
97
100
  });
98
101
  }
99
102
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"data-table-header.js","names":[],"sources":["../../src/ui/data-table-header.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconArrowDown,\n IconArrowsSort,\n IconArrowUp,\n} from '@tabler/icons-react'\nimport { flexRender } from '@tanstack/react-table'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport React from 'react'\n\nimport {\n getColumnMetaClasses,\n getPinnedCellStyle,\n useDataTableContext,\n} from './data-table-context'\nimport { Icon } from './icon'\nimport { springs } from './lib/motion'\nimport { cn } from './lib/utils'\nimport { TableHead, TableHeader, TableRow } from './table'\n\n/**\n * Renders the `<TableHeader>` block including sortable column headers\n * and optional per-column filter inputs. Internal — not exported to consumers.\n */\nfunction DataTableHeaderImpl({ stickyHeader }: { stickyHeader?: boolean }) {\n const { table, sortable, filterable, columnPinningState } =\n useDataTableContext()\n\n return (\n <TableHeader\n className={cn(\n // surface-raised, not base — the table lives on a card; a base-colored\n // sticky bar would read as a grey stripe over the white surface.\n stickyHeader && 'sticky top-0 z-10 bg-surface-raised',\n )}\n >\n {table.getHeaderGroups().map((headerGroup) => (\n <TableRow key={headerGroup.id}>\n {headerGroup.headers.map((header) => {\n const canSort = sortable && header.column.getCanSort()\n const sorted = header.column.getIsSorted()\n const pinned = getPinnedCellStyle(\n header.column.id,\n columnPinningState,\n )\n\n return (\n <TableHead\n key={header.id}\n className={cn(\n pinned.className,\n getColumnMetaClasses(\n header.column.columnDef.meta as Record<string, unknown>,\n ),\n )}\n style={pinned.style}\n aria-sort={\n canSort\n ? sorted === 'asc'\n ? 'ascending'\n : sorted === 'desc'\n ? 'descending'\n : 'none'\n : undefined\n }\n >\n {header.isPlaceholder ? null : canSort ? (\n <button\n type=\"button\"\n className={cn(\n 'flex items-center gap-ds-01 font-medium',\n 'cursor-pointer select-none',\n '-ml-ds-01 rounded-control-inner px-ds-01 py-ds-01',\n 'hover:bg-surface-raised-hover transition-colors',\n )}\n onClick={header.column.getToggleSortingHandler()}\n aria-label={`Sort by ${typeof header.column.columnDef.header === 'string' ? header.column.columnDef.header : header.column.id}`}\n >\n {flexRender(\n header.column.columnDef.header,\n header.getContext(),\n )}\n <AnimatePresence mode=\"wait\" initial={false}>\n {sorted === 'asc' ? (\n <motion.span\n key=\"asc\"\n initial={{ opacity: 0, rotate: 90 }}\n animate={{ opacity: 1, rotate: 0 }}\n exit={{ opacity: 0, rotate: -90 }}\n transition={springs.snappy}\n className=\"inline-flex\"\n >\n <Icon\n icon={IconArrowUp}\n size=\"sm\"\n className=\"text-surface-fg-muted\"\n />\n </motion.span>\n ) : sorted === 'desc' ? (\n <motion.span\n key=\"desc\"\n initial={{ opacity: 0, rotate: -90 }}\n animate={{ opacity: 1, rotate: 0 }}\n exit={{ opacity: 0, rotate: 90 }}\n transition={springs.snappy}\n className=\"inline-flex\"\n >\n <Icon\n icon={IconArrowDown}\n size=\"sm\"\n className=\"text-surface-fg-muted\"\n />\n </motion.span>\n ) : (\n <motion.span\n key=\"unsorted\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={springs.snappy}\n className=\"inline-flex\"\n >\n <Icon\n icon={IconArrowsSort}\n size=\"sm\"\n className=\"text-surface-fg-subtle\"\n />\n </motion.span>\n )}\n </AnimatePresence>\n </button>\n ) : (\n flexRender(\n header.column.columnDef.header,\n header.getContext(),\n )\n )}\n </TableHead>\n )\n })}\n </TableRow>\n ))}\n\n {/* Column filter row */}\n {filterable &&\n table.getHeaderGroups().map((headerGroup) => (\n <TableRow key={`${headerGroup.id}-filters`}>\n {headerGroup.headers.map((header) => (\n <TableHead key={`${header.id}-filter`} className=\"py-ds-01\">\n {header.isPlaceholder ||\n header.column.columnDef.enableColumnFilter === false ? null : (\n <input\n type=\"text\"\n value={\n (header.column.getFilterValue() as string) ?? ''\n }\n onChange={(e) =>\n header.column.setFilterValue(e.target.value)\n }\n placeholder={`Filter ${typeof header.column.columnDef.header === 'string' ? header.column.columnDef.header : ''}...`}\n aria-label={`Filter ${typeof header.column.columnDef.header === 'string' ? header.column.columnDef.header : header.column.id}`}\n className={cn(\n 'h-ds-xs-plus w-full rounded-control',\n 'border border-card-strong bg-surface-raised-hover',\n 'px-ds-02 text-body-sm',\n 'text-surface-fg placeholder:text-surface-fg-subtle',\n 'outline-hidden focus:border-accent-7',\n )}\n />\n )}\n </TableHead>\n ))}\n </TableRow>\n ))}\n </TableHeader>\n )\n}\n\nexport { DataTableHeaderImpl as DataTableHeader }\n"],"mappings":"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"}
1
+ {"version":3,"file":"data-table-header.js","names":[],"sources":["../../src/ui/data-table-header.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconArrowDown,\n IconArrowsSort,\n IconArrowUp,\n} from '@tabler/icons-react'\nimport { flexRender } from '@tanstack/react-table'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport React from 'react'\n\nimport {\n getColumnMetaClasses,\n getPinnedCellStyle,\n useDataTableContext,\n} from './data-table-context'\nimport { Icon } from './icon'\nimport { springs } from './lib/motion'\nimport { cn } from './lib/utils'\nimport { TableHead, TableHeader, TableRow } from './table'\n\n/**\n * Renders the `<TableHeader>` block including sortable column headers\n * and optional per-column filter inputs. Internal — not exported to consumers.\n */\nfunction DataTableHeaderImpl({ stickyHeader }: { stickyHeader?: boolean }) {\n const { table, sortable, filterable, filterableColumns, columnPinningState } =\n useDataTableContext()\n\n return (\n <TableHeader\n className={cn(\n // surface-panel, not base — the table lives on a card; a base-colored\n // sticky bar would read as a grey stripe over the white surface.\n stickyHeader && 'sticky top-0 z-10 bg-surface-panel',\n )}\n >\n {table.getHeaderGroups().map((headerGroup) => (\n <TableRow key={headerGroup.id}>\n {headerGroup.headers.map((header) => {\n const canSort = sortable && header.column.getCanSort()\n const sorted = header.column.getIsSorted()\n const pinned = getPinnedCellStyle(\n header.column.id,\n columnPinningState,\n )\n\n return (\n <TableHead\n key={header.id}\n className={cn(\n pinned.className,\n getColumnMetaClasses(\n header.column.columnDef.meta as Record<string, unknown>,\n ),\n )}\n style={pinned.style}\n aria-sort={\n canSort\n ? sorted === 'asc'\n ? 'ascending'\n : sorted === 'desc'\n ? 'descending'\n : 'none'\n : undefined\n }\n >\n {header.isPlaceholder ? null : canSort ? (\n <button\n type=\"button\"\n className={cn(\n 'flex items-center gap-ds-01 font-medium',\n 'cursor-pointer select-none',\n '-ml-ds-01 rounded-control-inner px-ds-01 py-ds-01',\n 'hover:bg-surface-panel-hover transition-colors',\n )}\n onClick={header.column.getToggleSortingHandler()}\n aria-label={`Sort by ${typeof header.column.columnDef.header === 'string' ? header.column.columnDef.header : header.column.id}`}\n >\n {flexRender(\n header.column.columnDef.header,\n header.getContext(),\n )}\n <AnimatePresence mode=\"wait\" initial={false}>\n {sorted === 'asc' ? (\n <motion.span\n key=\"asc\"\n initial={{ opacity: 0, rotate: 90 }}\n animate={{ opacity: 1, rotate: 0 }}\n exit={{ opacity: 0, rotate: -90 }}\n transition={springs.snappy}\n className=\"inline-flex\"\n >\n <Icon\n icon={IconArrowUp}\n size=\"sm\"\n className=\"text-surface-fg-muted\"\n />\n </motion.span>\n ) : sorted === 'desc' ? (\n <motion.span\n key=\"desc\"\n initial={{ opacity: 0, rotate: -90 }}\n animate={{ opacity: 1, rotate: 0 }}\n exit={{ opacity: 0, rotate: 90 }}\n transition={springs.snappy}\n className=\"inline-flex\"\n >\n <Icon\n icon={IconArrowDown}\n size=\"sm\"\n className=\"text-surface-fg-muted\"\n />\n </motion.span>\n ) : (\n <motion.span\n key=\"unsorted\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={springs.snappy}\n className=\"inline-flex\"\n >\n <Icon\n icon={IconArrowsSort}\n size=\"sm\"\n className=\"text-surface-fg-subtle\"\n />\n </motion.span>\n )}\n </AnimatePresence>\n </button>\n ) : (\n flexRender(\n header.column.columnDef.header,\n header.getContext(),\n )\n )}\n </TableHead>\n )\n })}\n </TableRow>\n ))}\n\n {/* Column filter row */}\n {filterable &&\n table.getHeaderGroups().map((headerGroup) => (\n <TableRow key={`${headerGroup.id}-filters`}>\n {headerGroup.headers.map((header) => {\n // When filterableColumns is set, only render inputs for listed IDs.\n // Falls back to the existing enableColumnFilter: false column-level opt-out.\n const isFiltered = filterableColumns\n ? filterableColumns.includes(header.column.id)\n : header.column.columnDef.enableColumnFilter !== false\n return (\n <TableHead key={`${header.id}-filter`} className=\"py-ds-01\">\n {header.isPlaceholder || !isFiltered ? null : (\n <input\n type=\"text\"\n value={\n (header.column.getFilterValue() as string) ?? ''\n }\n onChange={(e) =>\n header.column.setFilterValue(e.target.value)\n }\n placeholder={`Filter ${typeof header.column.columnDef.header === 'string' ? header.column.columnDef.header : ''}...`}\n aria-label={`Filter ${typeof header.column.columnDef.header === 'string' ? header.column.columnDef.header : header.column.id}`}\n className={cn(\n 'h-ds-xs-plus w-full rounded-control',\n 'border border-card-strong bg-surface-panel-hover',\n 'px-ds-02 text-body-sm',\n 'text-surface-fg placeholder:text-surface-fg-subtle',\n 'outline-hidden focus:border-accent-7',\n )}\n />\n )}\n </TableHead>\n )\n })}\n </TableRow>\n ))}\n </TableHeader>\n )\n}\n\nexport { DataTableHeaderImpl as DataTableHeader }\n"],"mappings":"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"}
@@ -20,7 +20,7 @@ function o({ table: o, totalRowCount: s, useServerPagination: c, pageSizeOptions
20
20
  o.setPageSize(Number(e.target.value));
21
21
  },
22
22
  "aria-label": "Rows per page",
23
- className: e("h-ds-sm rounded-control", "border border-card-strong bg-surface-raised-hover", "px-ds-03 text-body-sm", "text-surface-fg"),
23
+ className: e("h-ds-sm rounded-control", "border border-card-strong bg-surface-panel-hover", "px-ds-03 text-body-sm", "text-surface-fg"),
24
24
  children: (l ?? [
25
25
  10,
26
26
  20,
@@ -36,7 +36,7 @@ function o({ table: o, totalRowCount: s, useServerPagination: c, pageSizeOptions
36
36
  disabled: !o.getCanPreviousPage(),
37
37
  onClick: () => o.previousPage(),
38
38
  "aria-label": "Previous page",
39
- className: e("h-ds-sm w-ds-sm flex items-center justify-center", "rounded-control border border-surface-border-strong", "enabled:hover:bg-surface-raised", "disabled:opacity-action-disabled disabled:cursor-not-allowed", "transition-colors"),
39
+ className: e("h-ds-sm w-ds-sm flex items-center justify-center", "rounded-control border border-surface-border-strong", "enabled:hover:bg-surface-panel-hover", "disabled:opacity-action-disabled disabled:cursor-not-allowed", "transition-colors"),
40
40
  children: /* @__PURE__ */ n(t, {
41
41
  icon: i,
42
42
  size: "sm"
@@ -57,7 +57,7 @@ function o({ table: o, totalRowCount: s, useServerPagination: c, pageSizeOptions
57
57
  disabled: !o.getCanNextPage(),
58
58
  onClick: () => o.nextPage(),
59
59
  "aria-label": "Next page",
60
- className: e("h-ds-sm w-ds-sm flex items-center justify-center", "rounded-control border border-surface-border-strong", "enabled:hover:bg-surface-raised", "disabled:opacity-action-disabled disabled:cursor-not-allowed", "transition-colors"),
60
+ className: e("h-ds-sm w-ds-sm flex items-center justify-center", "rounded-control border border-surface-border-strong", "enabled:hover:bg-surface-panel-hover", "disabled:opacity-action-disabled disabled:cursor-not-allowed", "transition-colors"),
61
61
  children: /* @__PURE__ */ n(t, {
62
62
  icon: a,
63
63
  size: "sm"
@@ -1 +1 @@
1
- {"version":3,"file":"data-table-pagination.js","names":[],"sources":["../../src/ui/data-table-pagination.tsx"],"sourcesContent":["'use client'\n\nimport { IconChevronLeft, IconChevronRight } from '@tabler/icons-react'\nimport { type Table } from '@tanstack/react-table'\nimport React from 'react'\n\nimport { Icon } from './icon'\nimport { cn } from './lib/utils'\n\n/**\n * Pagination controls: page size selector, prev/next buttons, page info.\n * Internal — not exported to consumers.\n */\nexport function DataTablePagination<TData>({\n table,\n totalRowCount,\n useServerPagination,\n pageSizeOptions,\n}: {\n table: Table<TData>\n totalRowCount: number\n useServerPagination: boolean\n pageSizeOptions?: number[]\n}) {\n return (\n <div className=\"flex items-center justify-between px-ds-03 py-ds-04 border-t border-surface-border\">\n <span className=\"text-body-sm text-surface-fg-muted\">\n {totalRowCount} total rows\n </span>\n <div className=\"flex items-center gap-ds-03\">\n {/* Page size selector */}\n {!useServerPagination && (\n <select\n value={table.getState().pagination.pageSize}\n onChange={(e) => {\n table.setPageSize(Number(e.target.value))\n }}\n aria-label=\"Rows per page\"\n className={cn(\n 'h-ds-sm rounded-control',\n 'border border-card-strong bg-surface-raised-hover',\n 'px-ds-03 text-body-sm',\n 'text-surface-fg',\n )}\n >\n {(pageSizeOptions ?? [10, 20, 50, 100]).map((size) => (\n <option key={size} value={size}>\n {size} rows\n </option>\n ))}\n </select>\n )}\n\n {/* Previous page button */}\n <button\n type=\"button\"\n disabled={!table.getCanPreviousPage()}\n onClick={() => table.previousPage()}\n aria-label=\"Previous page\"\n className={cn(\n 'h-ds-sm w-ds-sm flex items-center justify-center',\n 'rounded-control border border-surface-border-strong',\n 'enabled:hover:bg-surface-raised',\n 'disabled:opacity-action-disabled disabled:cursor-not-allowed',\n 'transition-colors',\n )}\n >\n <Icon icon={IconChevronLeft} size=\"sm\" />\n </button>\n\n {/* Page info */}\n <span className=\"text-body-sm text-surface-fg-muted\">\n Page {table.getState().pagination.pageIndex + 1} of{' '}\n {table.getPageCount()}\n </span>\n\n {/* Next page button */}\n <button\n type=\"button\"\n disabled={!table.getCanNextPage()}\n onClick={() => table.nextPage()}\n aria-label=\"Next page\"\n className={cn(\n 'h-ds-sm w-ds-sm flex items-center justify-center',\n 'rounded-control border border-surface-border-strong',\n 'enabled:hover:bg-surface-raised',\n 'disabled:opacity-action-disabled disabled:cursor-not-allowed',\n 'transition-colors',\n )}\n >\n <Icon icon={IconChevronRight} size=\"sm\" />\n </button>\n </div>\n </div>\n )\n}\n"],"mappings":";;;;;;;AAaA,SAAgB,EAA2B,EACzC,UACA,kBACA,wBACA,sBAMC;CACD,OACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,QAAD;GAAM,WAAU;aAAhB,CACG,GAAc,aACX;MACN,kBAAC,OAAD;GAAK,WAAU;aAAf;IAEG,CAAC,KACA,kBAAC,UAAD;KACE,OAAO,EAAM,SAAS,CAAC,CAAC,WAAW;KACnC,WAAW,MAAM;MACf,EAAM,YAAY,OAAO,EAAE,OAAO,KAAK,CAAC;KAC1C;KACA,cAAW;KACX,WAAW,EACT,2BACA,qDACA,yBACA,iBACF;gBAEE,KAAmB;MAAC;MAAI;MAAI;MAAI;KAAG,EAAA,CAAG,KAAK,MAC3C,kBAAC,UAAD;MAAmB,OAAO;gBAA1B,CACG,GAAK,OACA;QAFK,CAEL,CACT;IACK,CAAA;IAIV,kBAAC,UAAD;KACE,MAAK;KACL,UAAU,CAAC,EAAM,mBAAmB;KACpC,eAAe,EAAM,aAAa;KAClC,cAAW;KACX,WAAW,EACT,oDACA,uDACA,mCACA,gEACA,mBACF;eAEA,kBAAC,GAAD;MAAM,MAAM;MAAiB,MAAK;KAAM,CAAA;IAClC,CAAA;IAGR,kBAAC,QAAD;KAAM,WAAU;eAAhB;MAAqD;MAC7C,EAAM,SAAS,CAAC,CAAC,WAAW,YAAY;MAAE;MAAI;MACnD,EAAM,aAAa;KAChB;;IAGN,kBAAC,UAAD;KACE,MAAK;KACL,UAAU,CAAC,EAAM,eAAe;KAChC,eAAe,EAAM,SAAS;KAC9B,cAAW;KACX,WAAW,EACT,oDACA,uDACA,mCACA,gEACA,mBACF;eAEA,kBAAC,GAAD;MAAM,MAAM;MAAkB,MAAK;KAAM,CAAA;IACnC,CAAA;GACL;IACF;;AAET"}
1
+ {"version":3,"file":"data-table-pagination.js","names":[],"sources":["../../src/ui/data-table-pagination.tsx"],"sourcesContent":["'use client'\n\nimport { IconChevronLeft, IconChevronRight } from '@tabler/icons-react'\nimport { type Table } from '@tanstack/react-table'\nimport React from 'react'\n\nimport { Icon } from './icon'\nimport { cn } from './lib/utils'\n\n/**\n * Pagination controls: page size selector, prev/next buttons, page info.\n * Internal — not exported to consumers.\n */\nexport function DataTablePagination<TData>({\n table,\n totalRowCount,\n useServerPagination,\n pageSizeOptions,\n}: {\n table: Table<TData>\n totalRowCount: number\n useServerPagination: boolean\n pageSizeOptions?: number[]\n}) {\n return (\n <div className=\"flex items-center justify-between px-ds-03 py-ds-04 border-t border-surface-border\">\n <span className=\"text-body-sm text-surface-fg-muted\">\n {totalRowCount} total rows\n </span>\n <div className=\"flex items-center gap-ds-03\">\n {/* Page size selector */}\n {!useServerPagination && (\n <select\n value={table.getState().pagination.pageSize}\n onChange={(e) => {\n table.setPageSize(Number(e.target.value))\n }}\n aria-label=\"Rows per page\"\n className={cn(\n 'h-ds-sm rounded-control',\n 'border border-card-strong bg-surface-panel-hover',\n 'px-ds-03 text-body-sm',\n 'text-surface-fg',\n )}\n >\n {(pageSizeOptions ?? [10, 20, 50, 100]).map((size) => (\n <option key={size} value={size}>\n {size} rows\n </option>\n ))}\n </select>\n )}\n\n {/* Previous page button */}\n <button\n type=\"button\"\n disabled={!table.getCanPreviousPage()}\n onClick={() => table.previousPage()}\n aria-label=\"Previous page\"\n className={cn(\n 'h-ds-sm w-ds-sm flex items-center justify-center',\n 'rounded-control border border-surface-border-strong',\n 'enabled:hover:bg-surface-panel-hover',\n 'disabled:opacity-action-disabled disabled:cursor-not-allowed',\n 'transition-colors',\n )}\n >\n <Icon icon={IconChevronLeft} size=\"sm\" />\n </button>\n\n {/* Page info */}\n <span className=\"text-body-sm text-surface-fg-muted\">\n Page {table.getState().pagination.pageIndex + 1} of{' '}\n {table.getPageCount()}\n </span>\n\n {/* Next page button */}\n <button\n type=\"button\"\n disabled={!table.getCanNextPage()}\n onClick={() => table.nextPage()}\n aria-label=\"Next page\"\n className={cn(\n 'h-ds-sm w-ds-sm flex items-center justify-center',\n 'rounded-control border border-surface-border-strong',\n 'enabled:hover:bg-surface-panel-hover',\n 'disabled:opacity-action-disabled disabled:cursor-not-allowed',\n 'transition-colors',\n )}\n >\n <Icon icon={IconChevronRight} size=\"sm\" />\n </button>\n </div>\n </div>\n )\n}\n"],"mappings":";;;;;;;AAaA,SAAgB,EAA2B,EACzC,UACA,kBACA,wBACA,sBAMC;CACD,OACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,QAAD;GAAM,WAAU;aAAhB,CACG,GAAc,aACX;MACN,kBAAC,OAAD;GAAK,WAAU;aAAf;IAEG,CAAC,KACA,kBAAC,UAAD;KACE,OAAO,EAAM,SAAS,CAAC,CAAC,WAAW;KACnC,WAAW,MAAM;MACf,EAAM,YAAY,OAAO,EAAE,OAAO,KAAK,CAAC;KAC1C;KACA,cAAW;KACX,WAAW,EACT,2BACA,oDACA,yBACA,iBACF;gBAEE,KAAmB;MAAC;MAAI;MAAI;MAAI;KAAG,EAAA,CAAG,KAAK,MAC3C,kBAAC,UAAD;MAAmB,OAAO;gBAA1B,CACG,GAAK,OACA;QAFK,CAEL,CACT;IACK,CAAA;IAIV,kBAAC,UAAD;KACE,MAAK;KACL,UAAU,CAAC,EAAM,mBAAmB;KACpC,eAAe,EAAM,aAAa;KAClC,cAAW;KACX,WAAW,EACT,oDACA,uDACA,wCACA,gEACA,mBACF;eAEA,kBAAC,GAAD;MAAM,MAAM;MAAiB,MAAK;KAAM,CAAA;IAClC,CAAA;IAGR,kBAAC,QAAD;KAAM,WAAU;eAAhB;MAAqD;MAC7C,EAAM,SAAS,CAAC,CAAC,WAAW,YAAY;MAAE;MAAI;MACnD,EAAM,aAAa;KAChB;;IAGN,kBAAC,UAAD;KACE,MAAK;KACL,UAAU,CAAC,EAAM,eAAe;KAChC,eAAe,EAAM,SAAS;KAC9B,cAAW;KACX,WAAW,EACT,oDACA,uDACA,wCACA,gEACA,mBACF;eAEA,kBAAC,GAAD;MAAM,MAAM;MAAkB,MAAK;KAAM,CAAA;IACnC,CAAA;GACL;IACF;;AAET"}
@@ -8,9 +8,19 @@ export interface DataTableToolbarProps<TData> extends Omit<React.HTMLAttributes<
8
8
  onGlobalFilterChange: (value: string) => void;
9
9
  density: Density;
10
10
  onDensityChange: (density: Density) => void;
11
+ /** Show the Export CSV button. @default true */
11
12
  enableExport?: boolean;
13
+ /**
14
+ * Override the default CSV export. When provided, clicking Export calls this
15
+ * instead of the built-in client-side CSV logic. Receives the currently visible
16
+ * filtered rows so you can trigger a server fetch or apply custom formatting.
17
+ *
18
+ * Same signature as `DataTable`'s `onExport`, so the prop can be forwarded
19
+ * straight through.
20
+ */
21
+ onExport?: (visibleRows: TData[]) => void;
12
22
  }
13
- export declare function DataTableToolbar<TData>({ table, globalFilter, globalFilterValue, onGlobalFilterChange, density, onDensityChange, enableExport, className, ...props }: DataTableToolbarProps<TData>): React.JSX.Element;
23
+ export declare function DataTableToolbar<TData>({ table, globalFilter, globalFilterValue, onGlobalFilterChange, density, onDensityChange, enableExport, onExport, className, ...props }: DataTableToolbarProps<TData>): React.JSX.Element;
14
24
  export declare namespace DataTableToolbar {
15
25
  var displayName: string;
16
26
  }
@@ -1 +1 @@
1
- {"version":3,"file":"data-table-toolbar.d.ts","sourceRoot":"","sources":["../../src/ui/data-table-toolbar.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAc9B,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,aAAa,CAAA;AAc5D,MAAM,WAAW,qBAAqB,CAAC,KAAK,CAAE,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC;IAC1G,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IACnB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,iBAAiB,EAAE,MAAM,CAAA;IACzB,oBAAoB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7C,OAAO,EAAE,OAAO,CAAA;IAChB,eAAe,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAC3C,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAyCD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,EACtC,KAAK,EACL,YAAoB,EACpB,iBAAiB,EACjB,oBAAoB,EACpB,OAAO,EACP,eAAe,EACf,YAAmB,EACnB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,qBAAqB,CAAC,KAAK,CAAC,qBA8F9B;yBAxGe,gBAAgB"}
1
+ {"version":3,"file":"data-table-toolbar.d.ts","sourceRoot":"","sources":["../../src/ui/data-table-toolbar.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAc9B,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,aAAa,CAAA;AAc5D,MAAM,WAAW,qBAAqB,CAAC,KAAK,CAAE,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC;IAC1G,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IACnB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,iBAAiB,EAAE,MAAM,CAAA;IACzB,oBAAoB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7C,OAAO,EAAE,OAAO,CAAA;IAChB,eAAe,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAC3C,gDAAgD;IAChD,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,IAAI,CAAA;CAC1C;AAyCD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,EACtC,KAAK,EACL,YAAoB,EACpB,iBAAiB,EACjB,oBAAoB,EACpB,OAAO,EACP,eAAe,EACf,YAAmB,EACnB,QAAQ,EACR,SAAS,EACT,GAAG,KAAK,EACT,EAAE,qBAAqB,CAAC,KAAK,CAAC,qBAoG9B;yBA/Ge,gBAAgB"}
@@ -26,11 +26,11 @@ function v(e) {
26
26
  })), r = [t.join(","), ...n.map((e) => e.join(","))].join("\n"), i = new Blob([r], { type: "text/csv;charset=utf-8;" }), a = URL.createObjectURL(i), o = document.createElement("a");
27
27
  o.href = a, o.download = "table-export.csv", o.click(), URL.revokeObjectURL(a);
28
28
  }
29
- function y({ table: y, globalFilter: b = !1, globalFilterValue: x, onGlobalFilterChange: S, density: C, onDensityChange: w, enableExport: T = !0, className: E, ...D }) {
30
- let O = y.getAllColumns().filter((e) => e.getCanHide() && e.id !== "_select");
29
+ function y({ table: y, globalFilter: b = !1, globalFilterValue: x, onGlobalFilterChange: S, density: C, onDensityChange: w, enableExport: T = !0, onExport: E, className: D, ...O }) {
30
+ let k = y.getAllColumns().filter((e) => e.getCanHide() && e.id !== "_select");
31
31
  return /* @__PURE__ */ u("div", {
32
- className: e("flex items-center gap-ds-03 pb-ds-04", E),
33
- ...D,
32
+ className: e("flex items-center gap-ds-03 pb-ds-04", D),
33
+ ...O,
34
34
  children: [b && /* @__PURE__ */ u("div", {
35
35
  className: "flex flex-1 items-center gap-ds-02",
36
36
  children: [/* @__PURE__ */ l(t, {
@@ -48,7 +48,7 @@ function y({ table: y, globalFilter: b = !1, globalFilterValue: x, onGlobalFilte
48
48
  }), /* @__PURE__ */ u("div", {
49
49
  className: "ml-auto flex items-center gap-ds-03",
50
50
  children: [
51
- O.length > 0 && /* @__PURE__ */ u(r, { children: [/* @__PURE__ */ l(c, {
51
+ k.length > 0 && /* @__PURE__ */ u(r, { children: [/* @__PURE__ */ l(c, {
52
52
  asChild: !0,
53
53
  children: /* @__PURE__ */ u(n, {
54
54
  variant: "outline",
@@ -67,7 +67,7 @@ function y({ table: y, globalFilter: b = !1, globalFilterValue: x, onGlobalFilte
67
67
  children: [
68
68
  /* @__PURE__ */ l(o, { children: "Toggle columns" }),
69
69
  /* @__PURE__ */ l(s, {}),
70
- O.map((e) => {
70
+ k.map((e) => {
71
71
  let t = e.columnDef.header, n = typeof t == "string" ? t : e.id;
72
72
  return /* @__PURE__ */ l(i, {
73
73
  checked: e.getIsVisible(),
@@ -94,7 +94,7 @@ function y({ table: y, globalFilter: b = !1, globalFilterValue: x, onGlobalFilte
94
94
  variant: "outline",
95
95
  color: "neutral",
96
96
  size: "sm",
97
- onClick: () => v(y),
97
+ onClick: () => E ? E(y.getFilteredRowModel().rows.map((e) => e.original)) : v(y),
98
98
  "aria-label": "Export table as CSV",
99
99
  children: [/* @__PURE__ */ l(t, {
100
100
  icon: f,
@@ -1 +1 @@
1
- {"version":3,"file":"data-table-toolbar.js","names":[],"sources":["../../src/ui/data-table-toolbar.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconColumns3,\n IconDownload,\n IconSearch,\n IconTextResize,\n} from '@tabler/icons-react'\nimport { type Table } from '@tanstack/react-table'\nimport * as React from 'react'\n\nimport { Button } from './button'\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from './dropdown-menu'\nimport { Icon } from './icon'\nimport { cn } from './lib/utils'\n\nexport type Density = 'compact' | 'standard' | 'comfortable'\n\nconst densityLabels: Record<Density, string> = {\n compact: 'Compact',\n standard: 'Standard',\n comfortable: 'Comfortable',\n}\n\nconst densityCycle: Record<Density, Density> = {\n compact: 'standard',\n standard: 'comfortable',\n comfortable: 'compact',\n}\n\nexport interface DataTableToolbarProps<TData> extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {\n table: Table<TData>\n globalFilter?: boolean\n globalFilterValue: string\n onGlobalFilterChange: (value: string) => void\n density: Density\n onDensityChange: (density: Density) => void\n enableExport?: boolean\n}\n\nconst toolbarIconClass = 'text-surface-fg-subtle'\n\nfunction exportToCsv<TData>(table: Table<TData>) {\n const headers = table\n .getAllLeafColumns()\n .filter((col) => col.getIsVisible() && col.id !== '_select')\n .map((col) => {\n const header = col.columnDef.header\n return typeof header === 'string' ? header : col.id\n })\n\n const rows = table.getFilteredRowModel().rows.map((row) =>\n table\n .getAllLeafColumns()\n .filter((col) => col.getIsVisible() && col.id !== '_select')\n .map((col) => {\n const value = row.getValue(col.id)\n const str = String(value ?? '')\n // Escape CSV: wrap in quotes if it contains comma, quote, or newline\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`\n }\n return str\n }),\n )\n\n const csvContent = [headers.join(','), ...rows.map((r) => r.join(','))].join(\n '\\n',\n )\n\n const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' })\n const url = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.href = url\n link.download = 'table-export.csv'\n link.click()\n URL.revokeObjectURL(url)\n}\n\nexport function DataTableToolbar<TData>({\n table,\n globalFilter = false,\n globalFilterValue,\n onGlobalFilterChange,\n density,\n onDensityChange,\n enableExport = true,\n className,\n ...props\n}: DataTableToolbarProps<TData>) {\n const toggleableColumns = table\n .getAllColumns()\n .filter((col) => col.getCanHide() && col.id !== '_select')\n\n return (\n <div className={cn('flex items-center gap-ds-03 pb-ds-04', className)} {...props}>\n {/* Global search */}\n {globalFilter && (\n <div className=\"flex flex-1 items-center gap-ds-02\">\n <Icon icon={IconSearch} size=\"sm\" className={toolbarIconClass} />\n <input\n type=\"text\"\n value={globalFilterValue}\n onChange={(e) => onGlobalFilterChange(e.target.value)}\n placeholder=\"Search all columns...\"\n aria-label=\"Search all columns\"\n className={cn(\n 'flex-1 bg-transparent text-body-md',\n 'text-surface-fg placeholder:text-surface-fg-subtle',\n 'outline-hidden',\n 'h-ds-sm',\n )}\n />\n </div>\n )}\n\n <div className=\"ml-auto flex items-center gap-ds-03\">\n {/* Column visibility dropdown */}\n {toggleableColumns.length > 0 && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n color=\"neutral\"\n size=\"sm\"\n aria-label=\"Toggle column visibility\"\n >\n <Icon icon={IconColumns3} size=\"sm\" className={toolbarIconClass} />\n Columns\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-40\">\n <DropdownMenuLabel>Toggle columns</DropdownMenuLabel>\n <DropdownMenuSeparator />\n {toggleableColumns.map((column) => {\n const headerDef = column.columnDef.header\n const label =\n typeof headerDef === 'string' ? headerDef : column.id\n return (\n <DropdownMenuCheckboxItem\n key={column.id}\n checked={column.getIsVisible()}\n onCheckedChange={(value) =>\n column.toggleVisibility(!!value)\n }\n >\n {label}\n </DropdownMenuCheckboxItem>\n )\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n\n {/* Density toggle */}\n <Button\n variant=\"outline\"\n color=\"neutral\"\n size=\"sm\"\n onClick={() => onDensityChange(densityCycle[density])}\n aria-label={`Table density: ${densityLabels[density]}. Click to change.`}\n title={`Density: ${densityLabels[density]}`}\n >\n <Icon icon={IconTextResize} size=\"sm\" className={toolbarIconClass} />\n {densityLabels[density]}\n </Button>\n\n {/* CSV Export */}\n {enableExport && (\n <Button\n variant=\"outline\"\n color=\"neutral\"\n size=\"sm\"\n onClick={() => exportToCsv(table)}\n aria-label=\"Export table as CSV\"\n >\n <Icon icon={IconDownload} size=\"sm\" className={toolbarIconClass} />\n Export\n </Button>\n )}\n </div>\n </div>\n )\n}\nDataTableToolbar.displayName = 'DataTableToolbar'\n"],"mappings":"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"}
1
+ {"version":3,"file":"data-table-toolbar.js","names":[],"sources":["../../src/ui/data-table-toolbar.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconColumns3,\n IconDownload,\n IconSearch,\n IconTextResize,\n} from '@tabler/icons-react'\nimport { type Table } from '@tanstack/react-table'\nimport * as React from 'react'\n\nimport { Button } from './button'\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from './dropdown-menu'\nimport { Icon } from './icon'\nimport { cn } from './lib/utils'\n\nexport type Density = 'compact' | 'standard' | 'comfortable'\n\nconst densityLabels: Record<Density, string> = {\n compact: 'Compact',\n standard: 'Standard',\n comfortable: 'Comfortable',\n}\n\nconst densityCycle: Record<Density, Density> = {\n compact: 'standard',\n standard: 'comfortable',\n comfortable: 'compact',\n}\n\nexport interface DataTableToolbarProps<TData> extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {\n table: Table<TData>\n globalFilter?: boolean\n globalFilterValue: string\n onGlobalFilterChange: (value: string) => void\n density: Density\n onDensityChange: (density: Density) => void\n /** Show the Export CSV button. @default true */\n enableExport?: boolean\n /**\n * Override the default CSV export. When provided, clicking Export calls this\n * instead of the built-in client-side CSV logic. Receives the currently visible\n * filtered rows so you can trigger a server fetch or apply custom formatting.\n *\n * Same signature as `DataTable`'s `onExport`, so the prop can be forwarded\n * straight through.\n */\n onExport?: (visibleRows: TData[]) => void\n}\n\nconst toolbarIconClass = 'text-surface-fg-subtle'\n\nfunction exportToCsv<TData>(table: Table<TData>) {\n const headers = table\n .getAllLeafColumns()\n .filter((col) => col.getIsVisible() && col.id !== '_select')\n .map((col) => {\n const header = col.columnDef.header\n return typeof header === 'string' ? header : col.id\n })\n\n const rows = table.getFilteredRowModel().rows.map((row) =>\n table\n .getAllLeafColumns()\n .filter((col) => col.getIsVisible() && col.id !== '_select')\n .map((col) => {\n const value = row.getValue(col.id)\n const str = String(value ?? '')\n // Escape CSV: wrap in quotes if it contains comma, quote, or newline\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`\n }\n return str\n }),\n )\n\n const csvContent = [headers.join(','), ...rows.map((r) => r.join(','))].join(\n '\\n',\n )\n\n const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' })\n const url = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.href = url\n link.download = 'table-export.csv'\n link.click()\n URL.revokeObjectURL(url)\n}\n\nexport function DataTableToolbar<TData>({\n table,\n globalFilter = false,\n globalFilterValue,\n onGlobalFilterChange,\n density,\n onDensityChange,\n enableExport = true,\n onExport,\n className,\n ...props\n}: DataTableToolbarProps<TData>) {\n const toggleableColumns = table\n .getAllColumns()\n .filter((col) => col.getCanHide() && col.id !== '_select')\n\n return (\n <div className={cn('flex items-center gap-ds-03 pb-ds-04', className)} {...props}>\n {/* Global search */}\n {globalFilter && (\n <div className=\"flex flex-1 items-center gap-ds-02\">\n <Icon icon={IconSearch} size=\"sm\" className={toolbarIconClass} />\n <input\n type=\"text\"\n value={globalFilterValue}\n onChange={(e) => onGlobalFilterChange(e.target.value)}\n placeholder=\"Search all columns...\"\n aria-label=\"Search all columns\"\n className={cn(\n 'flex-1 bg-transparent text-body-md',\n 'text-surface-fg placeholder:text-surface-fg-subtle',\n 'outline-hidden',\n 'h-ds-sm',\n )}\n />\n </div>\n )}\n\n <div className=\"ml-auto flex items-center gap-ds-03\">\n {/* Column visibility dropdown */}\n {toggleableColumns.length > 0 && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n color=\"neutral\"\n size=\"sm\"\n aria-label=\"Toggle column visibility\"\n >\n <Icon icon={IconColumns3} size=\"sm\" className={toolbarIconClass} />\n Columns\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-40\">\n <DropdownMenuLabel>Toggle columns</DropdownMenuLabel>\n <DropdownMenuSeparator />\n {toggleableColumns.map((column) => {\n const headerDef = column.columnDef.header\n const label =\n typeof headerDef === 'string' ? headerDef : column.id\n return (\n <DropdownMenuCheckboxItem\n key={column.id}\n checked={column.getIsVisible()}\n onCheckedChange={(value) =>\n column.toggleVisibility(!!value)\n }\n >\n {label}\n </DropdownMenuCheckboxItem>\n )\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n\n {/* Density toggle */}\n <Button\n variant=\"outline\"\n color=\"neutral\"\n size=\"sm\"\n onClick={() => onDensityChange(densityCycle[density])}\n aria-label={`Table density: ${densityLabels[density]}. Click to change.`}\n title={`Density: ${densityLabels[density]}`}\n >\n <Icon icon={IconTextResize} size=\"sm\" className={toolbarIconClass} />\n {densityLabels[density]}\n </Button>\n\n {/* CSV Export */}\n {enableExport && (\n <Button\n variant=\"outline\"\n color=\"neutral\"\n size=\"sm\"\n onClick={() =>\n onExport\n ? onExport(\n table.getFilteredRowModel().rows.map((r) => r.original),\n )\n : exportToCsv(table)\n }\n aria-label=\"Export table as CSV\"\n >\n <Icon icon={IconDownload} size=\"sm\" className={toolbarIconClass} />\n Export\n </Button>\n )}\n </div>\n </div>\n )\n}\nDataTableToolbar.displayName = 'DataTableToolbar'\n"],"mappings":"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"}