@neasg/design-system 0.4.8 → 0.4.9

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 (115) hide show
  1. package/README.md +4 -4
  2. package/dist/alert.d.ts +21 -2
  3. package/dist/alert.js +10 -3
  4. package/dist/avatar.d.ts +5 -2
  5. package/dist/avatar.js +76 -8
  6. package/dist/back-button.d.ts +2 -0
  7. package/dist/back-button.js +1 -1
  8. package/dist/badge.d.ts +3 -2
  9. package/dist/badge.js +9 -3
  10. package/dist/breadcrumb.js +2 -1
  11. package/dist/button.d.ts +1 -1
  12. package/dist/button.js +12 -9
  13. package/dist/calendar.js +10 -9
  14. package/dist/card.d.ts +29 -1
  15. package/dist/card.js +61 -7
  16. package/dist/checkbox.d.ts +2 -0
  17. package/dist/checkbox.js +2 -2
  18. package/dist/close-button.d.ts +6 -0
  19. package/dist/close-button.js +8 -0
  20. package/dist/collapsible.d.ts +7 -1
  21. package/dist/collapsible.js +2 -2
  22. package/dist/command-search.d.ts +16 -1
  23. package/dist/command-search.js +42 -16
  24. package/dist/command.js +3 -3
  25. package/dist/copy-button.d.ts +3 -1
  26. package/dist/copy-button.js +4 -2
  27. package/dist/count-badge.d.ts +3 -1
  28. package/dist/count-badge.js +10 -5
  29. package/dist/date-input.d.ts +18 -3
  30. package/dist/date-input.js +94 -21
  31. package/dist/dialog-primitive.js +4 -3
  32. package/dist/draggable-tabs.d.ts +7 -0
  33. package/dist/draggable-tabs.js +6 -5
  34. package/dist/drawer.js +2 -2
  35. package/dist/dropdown-button.d.ts +18 -0
  36. package/dist/dropdown-button.js +31 -0
  37. package/dist/edit-button.d.ts +10 -0
  38. package/dist/edit-button.js +7 -0
  39. package/dist/editable-table.d.ts +3 -1
  40. package/dist/editable-table.js +25 -12
  41. package/dist/field.d.ts +2 -2
  42. package/dist/field.js +4 -3
  43. package/dist/file-upload-status-toast.d.ts +28 -0
  44. package/dist/file-upload-status-toast.js +136 -0
  45. package/dist/file-upload.js +8 -7
  46. package/dist/filter-popover.d.ts +37 -0
  47. package/dist/filter-popover.js +73 -0
  48. package/dist/filter-toggle.js +2 -2
  49. package/dist/index.d.ts +45 -14
  50. package/dist/index.js +19 -4
  51. package/dist/input-control.d.ts +3 -2
  52. package/dist/input-control.js +3 -2
  53. package/dist/label-value-grid.d.ts +4 -2
  54. package/dist/label-value-grid.js +7 -6
  55. package/dist/label-value.js +3 -1
  56. package/dist/layout-right-panel-edge.d.ts +6 -0
  57. package/dist/layout-right-panel-edge.js +6 -0
  58. package/dist/layout.d.ts +77 -5
  59. package/dist/layout.js +439 -35
  60. package/dist/lib/date-utils.d.ts +10 -2
  61. package/dist/lib/date-utils.js +91 -11
  62. package/dist/lib/utils.js +8 -1
  63. package/dist/link.js +2 -2
  64. package/dist/message-item.d.ts +62 -0
  65. package/dist/message-item.js +148 -0
  66. package/dist/multi-select.js +31 -5
  67. package/dist/notification.d.ts +82 -0
  68. package/dist/notification.js +121 -0
  69. package/dist/otp-input.js +1 -1
  70. package/dist/page-header.js +1 -1
  71. package/dist/page-section.d.ts +2 -1
  72. package/dist/page-section.js +4 -3
  73. package/dist/pagination.js +2 -2
  74. package/dist/popover-menu.d.ts +23 -1
  75. package/dist/popover-menu.js +75 -5
  76. package/dist/popover-primitive.js +1 -1
  77. package/dist/progress.d.ts +5 -2
  78. package/dist/progress.js +6 -3
  79. package/dist/radio-group.d.ts +21 -0
  80. package/dist/radio-group.js +41 -0
  81. package/dist/rich-text-editor.d.ts +56 -0
  82. package/dist/rich-text-editor.js +394 -0
  83. package/dist/routing-timeline.d.ts +22 -0
  84. package/dist/routing-timeline.js +58 -0
  85. package/dist/scroll-hint.d.ts +8 -0
  86. package/dist/scroll-hint.js +7 -0
  87. package/dist/search-input-shell.js +1 -1
  88. package/dist/search-input.js +2 -2
  89. package/dist/section-nav.js +4 -4
  90. package/dist/select-primitive.js +3 -3
  91. package/dist/select.js +1 -1
  92. package/dist/skeleton.js +1 -1
  93. package/dist/styles.css +235 -57
  94. package/dist/table-column-visibility.d.ts +21 -0
  95. package/dist/table-column-visibility.js +74 -0
  96. package/dist/table-toolbar.js +1 -1
  97. package/dist/table.d.ts +12 -1
  98. package/dist/table.js +67 -33
  99. package/dist/tabs.d.ts +14 -3
  100. package/dist/tabs.js +60 -6
  101. package/dist/tailwind-preset.js +46 -0
  102. package/dist/task-mode-shell.d.ts +24 -0
  103. package/dist/task-mode-shell.js +110 -0
  104. package/dist/textarea.js +2 -1
  105. package/dist/theme-switcher.d.ts +28 -0
  106. package/dist/theme-switcher.js +27 -0
  107. package/dist/theme.d.ts +167 -0
  108. package/dist/theme.js +112 -0
  109. package/dist/toaster.js +5 -4
  110. package/dist/tooltip.js +1 -1
  111. package/dist/typography.d.ts +1 -1
  112. package/dist/typography.js +5 -5
  113. package/dist/workspace-header.d.ts +13 -0
  114. package/dist/workspace-header.js +11 -0
  115. package/package.json +15 -1
package/dist/styles.css CHANGED
@@ -121,6 +121,16 @@
121
121
  --nea-neutral-800: 222 36% 11%;
122
122
  --nea-neutral-900: 222 84% 5%;
123
123
 
124
+ --nea-dark-neutral-50: 214 18% 92%;
125
+ --nea-dark-neutral-100: 214 18% 90%;
126
+ --nea-dark-neutral-200: 215 14% 68%;
127
+ --nea-dark-neutral-300: 220 10% 16%;
128
+ --nea-dark-neutral-400: 220 12% 13.5%;
129
+ --nea-dark-neutral-500: 220 14% 11.5%;
130
+ --nea-dark-neutral-600: 220 16% 9.5%;
131
+ --nea-dark-neutral-700: 220 18% 8%;
132
+ --nea-dark-neutral-800: 220 22% 6.5%;
133
+
124
134
  --font-sans: "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
125
135
  "Segoe UI", sans-serif;
126
136
  --font-mono: "SFMono-Regular", "SF Mono", ui-monospace, Menlo, Monaco, Consolas,
@@ -136,24 +146,27 @@
136
146
 
137
147
  --card: var(--nea-neutral-0);
138
148
  --card-foreground: var(--nea-neutral-900);
149
+ --card-border: var(--nea-neutral-200);
139
150
 
140
151
  --popover: var(--nea-neutral-0);
141
152
  --popover-foreground: var(--nea-neutral-900);
153
+ --tooltip-background: var(--nea-neutral-800);
154
+ --tooltip-foreground: var(--nea-neutral-0);
142
155
 
143
156
  --primary: var(--nea-green-700);
144
157
  --primary-foreground: var(--nea-neutral-0);
145
158
 
146
159
  --secondary: var(--nea-neutral-100);
147
- --secondary-foreground: 222.2 47.4% 11.2%;
160
+ --secondary-foreground: var(--nea-neutral-900);
148
161
 
149
162
  --muted: var(--nea-neutral-100);
150
163
  --muted-foreground: var(--nea-neutral-500);
151
164
 
152
165
  --accent: var(--nea-neutral-100);
153
- --accent-foreground: 222.2 47.4% 11.2%;
166
+ --accent-foreground: var(--nea-neutral-900);
154
167
 
155
- --destructive: 0 84.2% 60.2%;
156
- --destructive-foreground: 210 40% 98%;
168
+ --destructive: var(--nea-red-500);
169
+ --destructive-foreground: var(--nea-neutral-0);
157
170
 
158
171
  --status-success: var(--nea-green-100);
159
172
  --status-success-foreground: var(--nea-green-800);
@@ -169,69 +182,173 @@
169
182
  --border: var(--nea-neutral-200);
170
183
  --input: var(--nea-neutral-200);
171
184
  --ring: var(--nea-green-700);
185
+ --component-border: var(--card-border);
186
+ --button-outline-background: var(--nea-neutral-100);
187
+ --button-outline-hover-background: var(--nea-neutral-200);
188
+ --button-outline-border: var(--component-border);
172
189
 
173
190
  --radius: 0.5rem;
174
191
  --radius-sm: calc(var(--radius) - 4px);
175
192
  --radius-md: calc(var(--radius) - 2px);
176
193
  --radius-lg: var(--radius);
177
194
  --radius-xl: calc(var(--radius) + 4px);
178
-
179
- --layout-shell-background: 223 30% 95.5%;
180
- --layout-sidebar-width: 18rem;
195
+ --radius-control: var(--radius-md);
196
+ --radius-surface: var(--radius-lg);
197
+ --radius-overlay: var(--radius-lg);
198
+
199
+ --layout-chrome-background: var(--nea-neutral-100);
200
+ --layout-shell-background: var(--layout-chrome-background);
201
+ --layout-sidebar-background: var(--layout-chrome-background);
202
+ --layout-sidebar-logo-background: transparent;
203
+ --layout-sidebar-border: var(--nea-neutral-200);
204
+ --layout-surface-background: var(--nea-neutral-50);
205
+ --layout-workspace-header-background: var(--nea-neutral-0);
206
+ --layout-surface-border: var(--nea-neutral-200);
207
+ --layout-surface-shadow:
208
+ 0 22px 48px -32px rgb(15 23 42 / 0.46),
209
+ 0 2px 8px rgb(15 23 42 / 0.08);
210
+ --overlay-shadow:
211
+ 0 18px 36px -22px rgb(15 23 42 / 0.26),
212
+ 0 8px 18px -14px rgb(15 23 42 / 0.16);
213
+ --overlay-shadow-strong:
214
+ 0 8px 34px -20px rgb(15 23 42 / 0.26),
215
+ 0 3px 14px -10px rgb(15 23 42 / 0.14);
216
+ --tab-list-background: var(--layout-chrome-background);
217
+ --tab-list-border: var(--component-border);
218
+ --tab-trigger-foreground: var(--nea-neutral-500);
219
+ --tab-trigger-hover-background: var(--nea-neutral-200);
220
+ --tab-trigger-active-background: var(--nea-neutral-0);
221
+ --tab-trigger-active-foreground: var(--nea-neutral-900);
222
+ --tab-indicator: var(--primary);
223
+ --card-gradient-from: var(--nea-green-700);
224
+ --card-gradient-to: var(--nea-green-500);
225
+ --card-gradient-border: var(--nea-green-500);
226
+ --card-gradient-foreground: var(--nea-neutral-0);
227
+ --layout-sidebar-width: 19.25rem;
181
228
  --layout-sidebar-collapsed-width: 4rem;
182
- --layout-sidebar-padding-inline: var(--space-2);
183
- --layout-sidebar-padding-top: var(--space-3);
184
- --layout-content-gap-start: var(--space-2);
185
- --layout-content-gap-start-md: var(--space-3);
186
- --layout-content-gap-start-lg: var(--space-4);
187
- --layout-content-padding: var(--space-4);
188
- --layout-content-padding-md: var(--space-6);
189
- --layout-content-padding-lg: var(--space-8);
190
- --layout-surface-padding: var(--space-4);
191
- --layout-surface-padding-md: var(--space-6);
192
- --layout-surface-padding-lg: var(--space-8);
193
- --layout-surface-gap: var(--space-6);
229
+ --layout-right-panel-width: 27rem;
230
+ --layout-sidebar-padding-inline: var(--space-1);
231
+ --layout-sidebar-padding-top: var(--space-2);
232
+ --layout-sidebar-content-padding: var(--space-2);
233
+ --layout-sidebar-section-gap: var(--space-5);
234
+ --layout-content-gap-start: var(--space-0);
235
+ --layout-content-gap-start-md: var(--space-0);
236
+ --layout-content-gap-start-lg: var(--space-0);
237
+ --layout-content-padding: var(--space-2);
238
+ --layout-content-padding-md: var(--space-3);
239
+ --layout-content-padding-lg: var(--space-3);
240
+ --layout-surface-padding: var(--space-3);
241
+ --layout-surface-padding-md: var(--space-4);
242
+ --layout-surface-padding-lg: var(--space-4);
243
+ --layout-surface-gap: var(--space-5);
244
+ --layout-page-gap: var(--space-8);
245
+ --layout-page-header-gap: var(--space-5);
246
+ --layout-page-section-gap: var(--space-6);
247
+ --layout-page-control-gap: var(--space-4);
248
+ --layout-page-context-gap: var(--space-3);
249
+ --layout-page-alert-margin: var(--space-1);
250
+ --layout-page-title-offset: var(--space-1);
251
+ --layout-right-panel-padding: var(--space-4);
252
+ --layout-right-panel-gap: var(--space-6);
253
+ --layout-right-panel-tabs-gap: var(--space-4);
254
+ --layout-right-panel-footer-gap: var(--space-3);
255
+ --layout-right-panel-section-gap: var(--space-2);
256
+ --layout-right-panel-heading-gap: var(--space-1);
257
+ --layout-right-panel-background: var(--layout-surface-background);
258
+ --layout-right-panel-border: var(--nea-neutral-300);
259
+ --layout-right-panel-shadow:
260
+ -18px 0 34px -30px rgb(15 23 42 / 0.32),
261
+ -2px 0 10px -8px rgb(15 23 42 / 0.18);
262
+ --table-border: var(--component-border);
263
+ --table-header-background: var(--tab-list-background);
264
+ --table-row-striped-background: var(--nea-neutral-100);
265
+ --table-row-warning-background: var(--nea-neutral-0);
194
266
  }
195
267
 
196
268
  .dark {
197
- --background: 222.2 84% 4.9%;
198
- --foreground: 210 40% 98%;
199
-
200
- --card: 222.2 84% 4.9%;
201
- --card-foreground: 210 40% 98%;
202
-
203
- --popover: 222.2 84% 4.9%;
204
- --popover-foreground: 210 40% 98%;
205
-
206
- --primary: 210 40% 98%;
207
- --primary-foreground: 222.2 47.4% 11.2%;
208
-
209
- --secondary: 217.2 32.6% 17.5%;
210
- --secondary-foreground: 210 40% 98%;
211
-
212
- --muted: 217.2 32.6% 17.5%;
213
- --muted-foreground: 215 20.2% 65.1%;
214
-
215
- --accent: 217.2 32.6% 17.5%;
216
- --accent-foreground: 210 40% 98%;
217
-
218
- --destructive: 0 62.8% 30.6%;
219
- --destructive-foreground: 210 40% 98%;
220
-
221
- --status-success: var(--nea-green-900);
222
- --status-success-foreground: var(--nea-green-300);
223
- --status-warning: var(--nea-amber-900);
224
- --status-warning-foreground: var(--nea-amber-300);
225
- --status-info: var(--nea-blue-900);
226
- --status-info-foreground: var(--nea-blue-300);
227
- --status-accent: var(--nea-violet-900);
228
- --status-accent-foreground: var(--nea-violet-300);
229
- --status-destructive: var(--nea-red-900);
230
- --status-destructive-foreground: var(--nea-red-300);
231
-
232
- --border: 217.2 32.6% 17.5%;
233
- --input: 217.2 32.6% 17.5%;
234
- --ring: 212.7 26.8% 83.9%;
269
+ --background: var(--nea-dark-neutral-300);
270
+ --foreground: var(--nea-dark-neutral-50);
271
+
272
+ --card: var(--nea-dark-neutral-300);
273
+ --card-foreground: var(--nea-dark-neutral-50);
274
+ --card-border: 220 10% 28%;
275
+
276
+ --popover: var(--nea-dark-neutral-300);
277
+ --popover-foreground: var(--nea-dark-neutral-50);
278
+ --tooltip-background: var(--nea-dark-neutral-800);
279
+ --tooltip-foreground: var(--nea-dark-neutral-50);
280
+
281
+ --primary: 148 30% 38%;
282
+ --primary-foreground: var(--nea-dark-neutral-50);
283
+
284
+ --secondary: var(--nea-dark-neutral-500);
285
+ --secondary-foreground: var(--nea-dark-neutral-100);
286
+
287
+ --muted: var(--nea-dark-neutral-500);
288
+ --muted-foreground: var(--nea-dark-neutral-200);
289
+
290
+ --accent: var(--nea-dark-neutral-500);
291
+ --accent-foreground: var(--nea-dark-neutral-50);
292
+
293
+ --destructive: 0 72% 58%;
294
+ --destructive-foreground: var(--nea-dark-neutral-50);
295
+
296
+ --status-success: 148 20% 18%;
297
+ --status-success-foreground: 148 28% 62%;
298
+ --status-warning: 38 22% 18%;
299
+ --status-warning-foreground: 42 42% 64%;
300
+ --status-info: 214 22% 19%;
301
+ --status-info-foreground: 214 36% 66%;
302
+ --status-accent: 263 18% 20%;
303
+ --status-accent-foreground: 255 34% 68%;
304
+ --status-destructive: 0 20% 19%;
305
+ --status-destructive-foreground: 0 42% 66%;
306
+
307
+ --tab-list-background: var(--table-header-background);
308
+ --tab-trigger-foreground: var(--nea-dark-neutral-200);
309
+ --tab-trigger-hover-background: var(--nea-dark-neutral-400);
310
+ --tab-trigger-active-background: var(--nea-dark-neutral-300);
311
+ --tab-trigger-active-foreground: var(--nea-dark-neutral-50);
312
+ --tab-indicator: var(--primary);
313
+ --card-gradient-from: var(--nea-green-700);
314
+ --card-gradient-to: var(--nea-green-500);
315
+ --card-gradient-border: var(--nea-green-500);
316
+ --card-gradient-foreground: var(--nea-neutral-0);
317
+ --layout-chrome-background: var(--nea-dark-neutral-500);
318
+ --layout-shell-background: var(--layout-chrome-background);
319
+ --layout-sidebar-background: var(--layout-chrome-background);
320
+ --layout-sidebar-logo-background: hsl(var(--nea-neutral-0));
321
+ --layout-sidebar-border: var(--card-border);
322
+ --layout-surface-background: var(--nea-dark-neutral-400);
323
+ --layout-workspace-header-background: var(--nea-dark-neutral-300);
324
+ --layout-surface-border: var(--card-border);
325
+ --layout-surface-shadow:
326
+ 0 24px 54px -34px rgb(0 0 0 / 0.64),
327
+ 0 2px 10px rgb(0 0 0 / 0.26);
328
+ --overlay-shadow:
329
+ 0 18px 36px -22px rgb(0 0 0 / 0.58),
330
+ 0 8px 18px -14px rgb(0 0 0 / 0.34);
331
+ --overlay-shadow-strong:
332
+ 0 10px 42px -24px rgb(0 0 0 / 0.68),
333
+ 0 4px 16px -12px rgb(0 0 0 / 0.42);
334
+ --layout-right-panel-background: var(--layout-surface-background);
335
+ --layout-right-panel-border: 220 10% 32%;
336
+ --layout-right-panel-shadow:
337
+ -18px 0 36px -28px rgb(0 0 0 / 0.58),
338
+ -2px 0 12px -8px rgb(0 0 0 / 0.46);
339
+ --table-header-background: var(--nea-dark-neutral-500);
340
+ --table-row-striped-background: var(--nea-dark-neutral-500);
341
+ --table-row-warning-background: var(--nea-dark-neutral-300);
342
+
343
+ --border: var(--card-border);
344
+ --input: var(--card-border);
345
+ --ring: 148 30% 38%;
346
+ --component-border: var(--card-border);
347
+ --button-outline-background: var(--nea-dark-neutral-500);
348
+ --button-outline-hover-background: var(--nea-dark-neutral-400);
349
+ --button-outline-border: var(--component-border);
350
+ --tab-list-border: var(--component-border);
351
+ --table-border: var(--component-border);
235
352
  }
236
353
 
237
354
  * {
@@ -252,3 +369,64 @@
252
369
  font-smooth: never;
253
370
  }
254
371
  }
372
+
373
+ @layer components {
374
+ [data-slot="routing-timeline"] {
375
+ --routing-timeline-background: var(--layout-right-panel-background);
376
+ }
377
+
378
+ [data-slot="routing-timeline-list"]::before {
379
+ position: absolute;
380
+ top: var(--space-2);
381
+ bottom: var(--space-0);
382
+ left: calc(var(--space-3) / 2);
383
+ width: 1px;
384
+ content: "";
385
+ transform: translateX(-50%);
386
+ background: hsl(var(--border));
387
+ }
388
+
389
+ [data-slot="routing-timeline-marker"] {
390
+ box-shadow: 0 0 0 var(--space-1) hsl(var(--routing-timeline-background));
391
+ }
392
+
393
+ [data-slot="routing-timeline-event-card"] {
394
+ container-type: inline-size;
395
+ }
396
+
397
+ [data-slot="routing-timeline-endpoints"][data-complete-route="true"] {
398
+ grid-template-columns: minmax(0, 1fr);
399
+ }
400
+
401
+ [data-slot="routing-timeline-endpoint-arrow"] {
402
+ justify-self: start;
403
+ transform: rotate(90deg);
404
+ }
405
+
406
+ @container (min-width: 22rem) {
407
+ [data-slot="routing-timeline-endpoints"][data-complete-route="true"] {
408
+ grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
409
+ }
410
+
411
+ [data-slot="routing-timeline-endpoint-arrow"] {
412
+ align-self: center;
413
+ justify-self: center;
414
+ transform: none;
415
+ }
416
+ }
417
+ }
418
+
419
+ @layer utilities {
420
+ .shadow-overlay-strong {
421
+ box-shadow: var(--overlay-shadow-strong);
422
+ }
423
+
424
+ .scrollbar-none {
425
+ -ms-overflow-style: none;
426
+ scrollbar-width: none;
427
+ }
428
+
429
+ .scrollbar-none::-webkit-scrollbar {
430
+ display: none;
431
+ }
432
+ }
@@ -0,0 +1,21 @@
1
+ import * as React from "react";
2
+ import { type PopoverProps } from "./popover";
3
+ import type { TableColumn } from "./table";
4
+ type PopoverPlacementProps = Pick<PopoverProps, "align" | "side" | "sideOffset">;
5
+ export interface TableColumnVisibilityMenuProps<Row> extends PopoverPlacementProps {
6
+ columns: TableColumn<Row>[];
7
+ visibleColumnKeys: readonly string[];
8
+ onVisibleColumnKeysChange: (visibleColumnKeys: string[]) => void;
9
+ defaultVisibleColumnKeys?: readonly string[];
10
+ trigger?: React.ReactElement;
11
+ triggerLabel?: string;
12
+ triggerClassName?: string;
13
+ contentClassName?: string;
14
+ className?: string;
15
+ searchPlaceholder?: string;
16
+ restoreDefaultsLabel?: string;
17
+ emptyMessage?: React.ReactNode;
18
+ minVisibleColumns?: number;
19
+ }
20
+ declare function TableColumnVisibilityMenu<Row>({ columns, visibleColumnKeys, onVisibleColumnKeysChange, defaultVisibleColumnKeys, trigger, triggerLabel, triggerClassName, contentClassName, className, searchPlaceholder, restoreDefaultsLabel, emptyMessage, minVisibleColumns, align, sideOffset, side, }: TableColumnVisibilityMenuProps<Row>): import("react/jsx-runtime").JSX.Element;
21
+ export { TableColumnVisibilityMenu };
@@ -0,0 +1,74 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Columns3 } from "lucide-react";
5
+ import { Button } from "./button";
6
+ import { Checkbox } from "./checkbox";
7
+ import { InputControl } from "./input-control";
8
+ import { cn } from "./lib/utils";
9
+ import { Popover } from "./popover";
10
+ import { PopoverMenuFooter, PopoverMenuFooterAction, PopoverMenuFooterMeta, } from "./popover-menu";
11
+ import { SearchInputShell } from "./search-input-shell";
12
+ function getColumnVisibilityLabel(column) {
13
+ if (column.visibilityLabel) {
14
+ return column.visibilityLabel;
15
+ }
16
+ if (typeof column.header === "string" ||
17
+ typeof column.header === "number") {
18
+ return String(column.header);
19
+ }
20
+ return column.key;
21
+ }
22
+ function orderVisibleColumnKeys(columns, visibleColumnKeys) {
23
+ const visibleColumnKeySet = new Set(visibleColumnKeys);
24
+ return columns
25
+ .map((column) => column.key)
26
+ .filter((columnKey) => visibleColumnKeySet.has(columnKey));
27
+ }
28
+ function arraysMatch(left, right) {
29
+ if (left.length !== right.length) {
30
+ return false;
31
+ }
32
+ return left.every((value, index) => value === right[index]);
33
+ }
34
+ function TableColumnVisibilityMenu({ columns, visibleColumnKeys, onVisibleColumnKeysChange, defaultVisibleColumnKeys, trigger, triggerLabel = "Customize columns", triggerClassName, contentClassName, className, searchPlaceholder = "Search columns", restoreDefaultsLabel = "Restore defaults", emptyMessage = "No columns found", minVisibleColumns = 1, align = "end", sideOffset = 4, side, }) {
35
+ const [query, setQuery] = React.useState("");
36
+ const searchInputId = React.useId();
37
+ const requiredVisibleColumns = Math.max(0, minVisibleColumns);
38
+ const visibleColumnKeySet = React.useMemo(() => new Set(visibleColumnKeys), [visibleColumnKeys]);
39
+ const visibleColumnCount = columns.filter((column) => visibleColumnKeySet.has(column.key)).length;
40
+ const selectableColumns = React.useMemo(() => columns.filter((column) => column.hideable !== false), [columns]);
41
+ const defaultKeys = React.useMemo(() => orderVisibleColumnKeys(columns, defaultVisibleColumnKeys !== null && defaultVisibleColumnKeys !== void 0 ? defaultVisibleColumnKeys : columns
42
+ .filter((column) => column.defaultVisible !== false)
43
+ .map((column) => column.key)), [columns, defaultVisibleColumnKeys]);
44
+ const currentKeys = React.useMemo(() => orderVisibleColumnKeys(columns, visibleColumnKeys), [columns, visibleColumnKeys]);
45
+ const canRestoreDefaults = defaultKeys.length >= requiredVisibleColumns &&
46
+ !arraysMatch(currentKeys, defaultKeys);
47
+ const normalizedQuery = query.trim().toLowerCase();
48
+ const filteredColumns = selectableColumns.filter((column) => getColumnVisibilityLabel(column).toLowerCase().includes(normalizedQuery));
49
+ const commitVisibleColumnKeys = React.useCallback((nextVisibleColumnKeys) => {
50
+ const orderedKeys = orderVisibleColumnKeys(columns, nextVisibleColumnKeys);
51
+ if (orderedKeys.length < requiredVisibleColumns) {
52
+ return;
53
+ }
54
+ onVisibleColumnKeysChange(orderedKeys);
55
+ }, [columns, onVisibleColumnKeysChange, requiredVisibleColumns]);
56
+ const toggleColumn = React.useCallback((columnKey) => {
57
+ const nextVisibleColumnKeys = new Set(visibleColumnKeys);
58
+ if (nextVisibleColumnKeys.has(columnKey)) {
59
+ nextVisibleColumnKeys.delete(columnKey);
60
+ }
61
+ else {
62
+ nextVisibleColumnKeys.add(columnKey);
63
+ }
64
+ commitVisibleColumnKeys(nextVisibleColumnKeys);
65
+ }, [commitVisibleColumnKeys, visibleColumnKeys]);
66
+ const defaultTrigger = (_jsx(Button, { type: "button", variant: "outline", size: "icon", "aria-label": triggerLabel, className: triggerClassName, children: _jsx(Columns3, { "aria-hidden": "true" }) }));
67
+ return (_jsx(Popover, { trigger: trigger !== null && trigger !== void 0 ? trigger : defaultTrigger, align: align, side: side, sideOffset: sideOffset, contentClassName: cn("w-80 p-0", contentClassName), children: _jsxs("div", { className: cn("flex max-h-96 flex-col", className), children: [_jsxs("div", { className: "border-b p-2", children: [_jsx("label", { htmlFor: searchInputId, className: "sr-only", children: searchPlaceholder }), _jsx(SearchInputShell, { children: _jsx(InputControl, { id: searchInputId, value: query, onChange: (event) => setQuery(event.target.value), placeholder: searchPlaceholder, className: "h-8 bg-background pl-8 pr-2" }) })] }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto p-1", children: filteredColumns.length ? (filteredColumns.map((column) => {
68
+ const label = getColumnVisibilityLabel(column);
69
+ const checked = visibleColumnKeySet.has(column.key);
70
+ const disabled = checked && visibleColumnCount <= requiredVisibleColumns;
71
+ return (_jsxs("label", { className: cn("flex h-9 cursor-pointer items-center gap-2 rounded-sm px-2 text-sm transition-colors hover:bg-accent hover:text-accent-foreground", disabled && "cursor-not-allowed opacity-50 hover:bg-transparent hover:text-inherit"), children: [_jsx(Checkbox, { checked: checked, disabled: disabled, onChange: () => toggleColumn(column.key), "aria-label": label }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: label })] }, column.key));
72
+ })) : (_jsx("div", { className: "px-3 py-6 text-center text-sm text-muted-foreground", children: emptyMessage })) }), _jsxs(PopoverMenuFooter, { inset: "flush", className: "justify-between", children: [_jsx(PopoverMenuFooterAction, { onClick: () => commitVisibleColumnKeys(defaultKeys), disabled: !canRestoreDefaults, className: "w-auto min-w-0 flex-1", children: restoreDefaultsLabel }), _jsxs(PopoverMenuFooterMeta, { children: [visibleColumnCount, " of ", columns.length] })] })] }) }));
73
+ }
74
+ export { TableColumnVisibilityMenu };
@@ -2,6 +2,6 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { SearchInput } from "./search-input";
3
3
  import { cn } from "./lib/utils";
4
4
  function TableToolbar({ search, className, children, ...props }) {
5
- return (_jsxs("div", { className: cn("flex flex-wrap items-center gap-2", className), ...props, children: [search ? (_jsx(SearchInput, { ...search, containerClassName: cn("min-w-[200px] flex-1 max-w-md", search.containerClassName) })) : null, children] }));
5
+ return (_jsxs("div", { className: cn("flex flex-wrap items-center gap-1.5", className), ...props, children: [search ? (_jsx(SearchInput, { ...search, containerClassName: cn("min-w-[200px] flex-1 max-w-md", search.containerClassName) })) : null, children] }));
6
6
  }
7
7
  export { TableToolbar };
package/dist/table.d.ts CHANGED
@@ -25,6 +25,7 @@ declare const TableCell: React.ForwardRefExoticComponent<React.TdHTMLAttributes<
25
25
  } & React.RefAttributes<HTMLTableCellElement>>;
26
26
  declare const TableCaption: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLTableCaptionElement> & React.RefAttributes<HTMLTableCaptionElement>>;
27
27
  type TableAlign = "left" | "center" | "right";
28
+ export type TableRowTone = "default" | "muted" | "warning";
28
29
  export type TableSortDirection = "asc" | "desc";
29
30
  export type SortDirection = TableSortDirection;
30
31
  export type TableSkeletonLine = {
@@ -54,6 +55,8 @@ export declare const TABLE_SKELETON_PRESETS: {
54
55
  export interface TableColumn<Row> {
55
56
  key: string;
56
57
  header: React.ReactNode;
58
+ /** Text label used by column visibility controls when the header is not plain text. */
59
+ visibilityLabel?: string;
57
60
  accessorKey?: keyof Row;
58
61
  cell?: (row: Row, rowIndex: number) => React.ReactNode;
59
62
  width?: string;
@@ -64,6 +67,10 @@ export interface TableColumn<Row> {
64
67
  sortable?: boolean;
65
68
  sortKey?: string;
66
69
  sortLabel?: string;
70
+ /** When false, restore-defaults helpers can omit the column from the visible set. */
71
+ defaultVisible?: boolean;
72
+ /** When false, the column visibility menu keeps the column out of its checklist. */
73
+ hideable?: boolean;
67
74
  skeleton?: TableSkeletonConfig;
68
75
  headerClassName?: string;
69
76
  cellClassName?: string;
@@ -100,16 +107,20 @@ export interface TableProps<Row> extends Omit<React.HTMLAttributes<HTMLDivElemen
100
107
  containerClassName?: string;
101
108
  tableClassName?: string;
102
109
  rowClassName?: string | ((row: Row, index: number) => string | undefined);
110
+ /** Semantic row tone. Use this for conditional formatting instead of raw colour classes. */
111
+ rowTone?: TableRowTone | ((row: Row, index: number) => TableRowTone | undefined);
103
112
  /** Constrains the table container height so the table body scrolls instead of the surrounding surface. */
104
113
  maxHeight?: React.CSSProperties["maxHeight"];
105
114
  sorting?: TableSortingProps;
106
115
  pagination?: Omit<PaginationProps, "className">;
107
116
  loading?: boolean;
108
117
  loadingRows?: number;
118
+ /** Optional controlled set of visible column keys. Omit to render every column. */
119
+ visibleColumnKeys?: readonly string[];
109
120
  /** When true, applies alternating row backgrounds for easier scanning. */
110
121
  striped?: boolean;
111
122
  /** When set, rows become interactive: cursor-pointer, hover highlight, and click handling. */
112
123
  onRowClick?: (row: Row, index: number) => void;
113
124
  }
114
- declare function Table<Row>({ columns, rows, getRowKey, emptyState, toolbar, caption, className, containerClassName, tableClassName, rowClassName, maxHeight, sorting, pagination, loading, loadingRows, striped, onRowClick, style, ...props }: TableProps<Row>): import("react/jsx-runtime").JSX.Element;
125
+ declare function Table<Row>({ columns, rows, getRowKey, emptyState, toolbar, caption, className, containerClassName, tableClassName, rowClassName, rowTone, maxHeight, sorting, pagination, loading, loadingRows, visibleColumnKeys, striped, onRowClick, style, ...props }: TableProps<Row>): import("react/jsx-runtime").JSX.Element;
115
126
  export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, SortableTableHead, TableRowSkeleton, };