@exxatdesignux/ui 0.2.19 → 0.4.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.
- package/CHANGELOG.md +662 -7
- package/bin/sync-extras.mjs +116 -29
- package/consumer-extras/README.md +42 -7
- package/consumer-extras/cursor-rules/exxat-accessibility.mdc +39 -0
- package/consumer-extras/cursor-rules/exxat-board-cards.mdc +26 -0
- package/consumer-extras/cursor-rules/exxat-breadcrumbs-no-back.mdc +21 -0
- package/consumer-extras/cursor-rules/exxat-card-vs-list-rows.mdc +21 -0
- package/consumer-extras/cursor-rules/exxat-centralized-list-dataset.mdc +44 -0
- package/consumer-extras/cursor-rules/exxat-collaboration-access.mdc +32 -0
- package/consumer-extras/cursor-rules/exxat-command-menu.mdc +22 -0
- package/consumer-extras/cursor-rules/exxat-dashboard-view-charts.mdc +53 -0
- package/consumer-extras/cursor-rules/exxat-data-tables.mdc +43 -0
- package/consumer-extras/cursor-rules/exxat-dedicated-search-surfaces.mdc +25 -0
- package/consumer-extras/cursor-rules/exxat-drawer-vs-dialog.mdc +22 -0
- package/consumer-extras/cursor-rules/exxat-ds-agents.mdc +56 -0
- package/consumer-extras/cursor-rules/exxat-fontawesome-icons.mdc +31 -0
- package/consumer-extras/cursor-rules/exxat-kbd-shortcuts.mdc +100 -0
- package/consumer-extras/cursor-rules/exxat-kpi-flat-band.mdc +28 -0
- package/consumer-extras/cursor-rules/exxat-kpi-max-four.mdc +21 -0
- package/consumer-extras/cursor-rules/exxat-kpi-trends.mdc +31 -0
- package/consumer-extras/cursor-rules/exxat-library-hub-header.mdc +28 -0
- package/consumer-extras/cursor-rules/exxat-list-page-connected-views.mdc +24 -0
- package/consumer-extras/cursor-rules/exxat-list-page-view-shells.mdc +31 -0
- package/consumer-extras/cursor-rules/exxat-mono-ids.mdc +30 -0
- package/consumer-extras/cursor-rules/exxat-no-slds-leakage.mdc +78 -0
- package/consumer-extras/cursor-rules/exxat-no-toast.mdc +25 -0
- package/consumer-extras/cursor-rules/exxat-page-vs-drawer.mdc +23 -0
- package/consumer-extras/cursor-rules/exxat-person-identity-display.mdc +47 -0
- package/consumer-extras/cursor-rules/exxat-primary-nav-secondary-panel.mdc +52 -0
- package/consumer-extras/cursor-rules/exxat-reuse-before-custom.mdc +34 -0
- package/consumer-extras/cursor-rules/exxat-table-properties-drawer.mdc +77 -0
- package/consumer-extras/cursor-rules/exxat-token-discipline.mdc +103 -0
- package/consumer-extras/cursor-skills/exxat-accessibility/SKILL.md +1 -1
- package/consumer-extras/cursor-skills/exxat-board-cards/SKILL.md +3 -3
- package/consumer-extras/cursor-skills/exxat-centralized-list-dataset/SKILL.md +5 -16
- package/consumer-extras/cursor-skills/exxat-collaboration-access/SKILL.md +3 -3
- package/consumer-extras/cursor-skills/exxat-dedicated-search-surfaces/SKILL.md +2 -2
- package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +19 -34
- package/consumer-extras/cursor-skills/exxat-ds-skill/references/data-table-pattern.md +1 -1
- package/consumer-extras/cursor-skills/exxat-kpi-flat-band/SKILL.md +1 -1
- package/consumer-extras/cursor-skills/exxat-list-page-view-shells/SKILL.md +1 -1
- package/consumer-extras/cursor-skills/exxat-mono-ids/SKILL.md +4 -4
- package/consumer-extras/cursor-skills/exxat-primary-nav-secondary-panel/SKILL.md +10 -12
- package/consumer-extras/cursor-skills/exxat-token-economy/SKILL.md +277 -0
- package/consumer-extras/handbook/HANDBOOK.md +187 -0
- package/consumer-extras/handbook/glossary.md +58 -0
- package/consumer-extras/handbook/reference-implementations.md +153 -0
- package/consumer-extras/handbook/voice-and-tone.md +262 -0
- package/consumer-extras/patterns/collaboration-access-pattern.md +7 -7
- package/consumer-extras/patterns/command-menu-pattern.md +1 -1
- package/consumer-extras/patterns/consumer-upgrade-checklist.md +0 -20
- package/consumer-extras/patterns/data-views-pattern.md +31 -66
- package/consumer-extras/patterns/kpi-flat-band-pattern.md +2 -2
- package/consumer-extras/patterns/shell-surface-elevation-pattern.md +3 -5
- package/dist/components/data-table/filter-date-calendar.d.ts +10 -0
- package/dist/components/data-table/filter-date-calendar.js +280 -0
- package/dist/components/data-table/filter-date-calendar.js.map +1 -0
- package/dist/components/data-table/filter-text-value-input.d.ts +15 -0
- package/dist/components/data-table/filter-text-value-input.js +561 -0
- package/dist/components/data-table/filter-text-value-input.js.map +1 -0
- package/dist/components/data-table/index.d.ts +45 -0
- package/dist/components/data-table/index.js +3085 -0
- package/dist/components/data-table/index.js.map +1 -0
- package/dist/components/data-table/pagination.d.ts +28 -0
- package/dist/components/data-table/pagination.js +3264 -0
- package/dist/components/data-table/pagination.js.map +1 -0
- package/dist/components/data-table/types.d.ts +84 -0
- package/dist/components/data-table/types.js +3 -0
- package/dist/components/data-table/types.js.map +1 -0
- package/dist/components/data-table/use-table-state.d.ts +116 -0
- package/dist/components/data-table/use-table-state.js +670 -0
- package/dist/components/data-table/use-table-state.js.map +1 -0
- package/dist/components/data-views/board-card-primitives.d.ts +22 -0
- package/dist/components/data-views/board-card-primitives.js +84 -0
- package/dist/components/data-views/board-card-primitives.js.map +1 -0
- package/dist/components/data-views/data-row-list.d.ts +33 -0
- package/dist/components/data-views/data-row-list.js +106 -0
- package/dist/components/data-views/data-row-list.js.map +1 -0
- package/dist/components/data-views/finder-panel-view.d.ts +54 -0
- package/dist/components/data-views/finder-panel-view.js +388 -0
- package/dist/components/data-views/finder-panel-view.js.map +1 -0
- package/dist/components/data-views/folder-grid-view.d.ts +22 -0
- package/dist/components/data-views/folder-grid-view.js +58 -0
- package/dist/components/data-views/folder-grid-view.js.map +1 -0
- package/dist/components/data-views/hub-table.d.ts +173 -0
- package/dist/components/data-views/hub-table.js +5783 -0
- package/dist/components/data-views/hub-table.js.map +1 -0
- package/dist/components/data-views/index.d.ts +27 -0
- package/dist/components/data-views/index.js +6797 -0
- package/dist/components/data-views/index.js.map +1 -0
- package/dist/components/data-views/list-page-board-card.d.ts +72 -0
- package/dist/components/data-views/list-page-board-card.js +264 -0
- package/dist/components/data-views/list-page-board-card.js.map +1 -0
- package/dist/components/data-views/list-page-board-template.d.ts +24 -0
- package/dist/components/data-views/list-page-board-template.js +137 -0
- package/dist/components/data-views/list-page-board-template.js.map +1 -0
- package/dist/components/data-views/list-page-connected-view-body.d.ts +19 -0
- package/dist/components/data-views/list-page-connected-view-body.js +116 -0
- package/dist/components/data-views/list-page-connected-view-body.js.map +1 -0
- package/dist/components/data-views/list-page-split-details-placeholder.d.ts +14 -0
- package/dist/components/data-views/list-page-split-details-placeholder.js +38 -0
- package/dist/components/data-views/list-page-split-details-placeholder.js.map +1 -0
- package/dist/components/data-views/list-page-split-hub-chrome.d.ts +17 -0
- package/dist/components/data-views/list-page-split-hub-chrome.js +54 -0
- package/dist/components/data-views/list-page-split-hub-chrome.js.map +1 -0
- package/dist/components/data-views/list-page-split-hub-tokens.d.ts +12 -0
- package/dist/components/data-views/list-page-split-hub-tokens.js +8 -0
- package/dist/components/data-views/list-page-split-hub-tokens.js.map +1 -0
- package/dist/components/data-views/list-page-tree-column-header.d.ts +15 -0
- package/dist/components/data-views/list-page-tree-column-header.js +22 -0
- package/dist/components/data-views/list-page-tree-column-header.js.map +1 -0
- package/dist/components/data-views/list-page-tree-panel-shell.d.ts +25 -0
- package/dist/components/data-views/list-page-tree-panel-shell.js +146 -0
- package/dist/components/data-views/list-page-tree-panel-shell.js.map +1 -0
- package/dist/components/data-views/os-folder-glyph.d.ts +35 -0
- package/dist/components/data-views/os-folder-glyph.js +104 -0
- package/dist/components/data-views/os-folder-glyph.js.map +1 -0
- package/dist/components/data-views/outline-tree-menu.d.ts +36 -0
- package/dist/components/data-views/outline-tree-menu.js +131 -0
- package/dist/components/data-views/outline-tree-menu.js.map +1 -0
- package/dist/components/table-properties/column-row.d.ts +22 -0
- package/dist/components/table-properties/column-row.js +153 -0
- package/dist/components/table-properties/column-row.js.map +1 -0
- package/dist/components/table-properties/draggable-list.d.ts +24 -0
- package/dist/components/table-properties/draggable-list.js +53 -0
- package/dist/components/table-properties/draggable-list.js.map +1 -0
- package/dist/components/table-properties/drawer-button.d.ts +110 -0
- package/dist/components/table-properties/drawer-button.js +2748 -0
- package/dist/components/table-properties/drawer-button.js.map +1 -0
- package/dist/components/table-properties/drawer.d.ts +100 -0
- package/dist/components/table-properties/drawer.js +2595 -0
- package/dist/components/table-properties/drawer.js.map +1 -0
- package/dist/components/table-properties/filter-card.d.ts +24 -0
- package/dist/components/table-properties/filter-card.js +854 -0
- package/dist/components/table-properties/filter-card.js.map +1 -0
- package/dist/components/table-properties/index.d.ts +14 -0
- package/dist/components/table-properties/index.js +2768 -0
- package/dist/components/table-properties/index.js.map +1 -0
- package/dist/components/table-properties/sort-card.d.ts +20 -0
- package/dist/components/table-properties/sort-card.js +102 -0
- package/dist/components/table-properties/sort-card.js.map +1 -0
- package/dist/components/templates/dedicated-search-landing-template.d.ts +21 -0
- package/dist/components/templates/dedicated-search-landing-template.js +254 -0
- package/dist/components/templates/dedicated-search-landing-template.js.map +1 -0
- package/dist/components/templates/dedicated-search-results-template.d.ts +15 -0
- package/dist/components/templates/dedicated-search-results-template.js +16 -0
- package/dist/components/templates/dedicated-search-results-template.js.map +1 -0
- package/dist/components/templates/index.d.ts +9 -0
- package/dist/components/templates/index.js +2720 -0
- package/dist/components/templates/index.js.map +1 -0
- package/dist/components/templates/list-page.d.ts +83 -0
- package/dist/components/templates/list-page.js +2433 -0
- package/dist/components/templates/list-page.js.map +1 -0
- package/dist/components/templates/nested-secondary-panel-shell.d.ts +20 -0
- package/dist/components/templates/nested-secondary-panel-shell.js +54 -0
- package/dist/components/templates/nested-secondary-panel-shell.js.map +1 -0
- package/dist/components/ui/accordion.d.ts +10 -0
- package/dist/components/ui/accordion.js +74 -0
- package/dist/components/ui/accordion.js.map +1 -0
- package/dist/components/ui/alert-dialog.d.ts +37 -0
- package/dist/components/ui/alert-dialog.js +201 -0
- package/dist/components/ui/alert-dialog.js.map +1 -0
- package/dist/components/ui/avatar.d.ts +84 -0
- package/dist/components/ui/avatar.js +328 -0
- package/dist/components/ui/avatar.js.map +1 -0
- package/dist/components/ui/badge.d.ts +13 -0
- package/dist/components/ui/badge.js +49 -0
- package/dist/components/ui/badge.js.map +1 -0
- package/dist/components/ui/banner.d.ts +62 -0
- package/dist/components/ui/banner.js +364 -0
- package/dist/components/ui/banner.js.map +1 -0
- package/dist/components/ui/breadcrumb.d.ts +14 -0
- package/dist/components/ui/breadcrumb.js +114 -0
- package/dist/components/ui/breadcrumb.js.map +1 -0
- package/dist/components/ui/button.d.ts +16 -0
- package/dist/components/ui/button.js +59 -0
- package/dist/components/ui/button.js.map +1 -0
- package/dist/components/ui/calendar.d.ts +13 -0
- package/dist/components/ui/calendar.js +238 -0
- package/dist/components/ui/calendar.js.map +1 -0
- package/dist/components/ui/card.d.ts +14 -0
- package/dist/components/ui/card.js +102 -0
- package/dist/components/ui/card.js.map +1 -0
- package/dist/components/ui/chart.d.ts +58 -0
- package/dist/components/ui/chart.js +292 -0
- package/dist/components/ui/chart.js.map +1 -0
- package/dist/components/ui/checkbox.d.ts +23 -0
- package/dist/components/ui/checkbox.js +155 -0
- package/dist/components/ui/checkbox.js.map +1 -0
- package/dist/components/ui/coach-mark.d.ts +27 -0
- package/dist/components/ui/coach-mark.js +306 -0
- package/dist/components/ui/coach-mark.js.map +1 -0
- package/dist/components/ui/collapsible.d.ts +8 -0
- package/dist/components/ui/collapsible.js +35 -0
- package/dist/components/ui/collapsible.js.map +1 -0
- package/dist/components/ui/command.d.ts +36 -0
- package/dist/components/ui/command.js +274 -0
- package/dist/components/ui/command.js.map +1 -0
- package/dist/components/ui/context-menu.d.ts +32 -0
- package/dist/components/ui/context-menu.js +245 -0
- package/dist/components/ui/context-menu.js.map +1 -0
- package/dist/components/ui/date-picker-field.d.ts +38 -0
- package/dist/components/ui/date-picker-field.js +550 -0
- package/dist/components/ui/date-picker-field.js.map +1 -0
- package/dist/components/ui/dialog.d.ts +22 -0
- package/dist/components/ui/dialog.js +200 -0
- package/dist/components/ui/dialog.js.map +1 -0
- package/dist/components/ui/dot-pattern.d.ts +21 -0
- package/dist/components/ui/dot-pattern.js +139 -0
- package/dist/components/ui/dot-pattern.js.map +1 -0
- package/dist/components/ui/drag-handle-grip.d.ts +10 -0
- package/dist/components/ui/drag-handle-grip.js +15 -0
- package/dist/components/ui/drag-handle-grip.js.map +1 -0
- package/dist/components/ui/drawer.d.ts +16 -0
- package/dist/components/ui/drawer.js +125 -0
- package/dist/components/ui/drawer.js.map +1 -0
- package/dist/components/ui/dropdown-menu.d.ts +45 -0
- package/dist/components/ui/dropdown-menu.js +353 -0
- package/dist/components/ui/dropdown-menu.js.map +1 -0
- package/dist/components/ui/export-drawer.d.ts +11 -0
- package/dist/components/ui/export-drawer.js +1658 -0
- package/dist/components/ui/export-drawer.js.map +1 -0
- package/dist/components/ui/field.d.ts +30 -0
- package/dist/components/ui/field.js +249 -0
- package/dist/components/ui/field.js.map +1 -0
- package/dist/components/ui/form.d.ts +28 -0
- package/dist/components/ui/form.js +110 -0
- package/dist/components/ui/form.js.map +1 -0
- package/dist/components/ui/hover-card.d.ts +9 -0
- package/dist/components/ui/hover-card.js +43 -0
- package/dist/components/ui/hover-card.js.map +1 -0
- package/dist/components/ui/input-group.d.ts +20 -0
- package/dist/components/ui/input-group.js +219 -0
- package/dist/components/ui/input-group.js.map +1 -0
- package/dist/components/ui/input-mask.d.ts +39 -0
- package/dist/components/ui/input-mask.js +118 -0
- package/dist/components/ui/input-mask.js.map +1 -0
- package/dist/components/ui/input.d.ts +5 -0
- package/dist/components/ui/input.js +30 -0
- package/dist/components/ui/input.js.map +1 -0
- package/dist/components/ui/kbd.d.ts +20 -0
- package/dist/components/ui/kbd.js +45 -0
- package/dist/components/ui/kbd.js.map +1 -0
- package/dist/components/ui/key-metrics-context.d.ts +19 -0
- package/dist/components/ui/key-metrics-context.js +26 -0
- package/dist/components/ui/key-metrics-context.js.map +1 -0
- package/dist/components/ui/key-metrics.d.ts +131 -0
- package/dist/components/ui/key-metrics.js +1015 -0
- package/dist/components/ui/key-metrics.js.map +1 -0
- package/dist/components/ui/label.d.ts +6 -0
- package/dist/components/ui/label.js +28 -0
- package/dist/components/ui/label.js.map +1 -0
- package/dist/components/ui/list-page-view-frame.d.ts +22 -0
- package/dist/components/ui/list-page-view-frame.js +24 -0
- package/dist/components/ui/list-page-view-frame.js.map +1 -0
- package/dist/components/ui/page-header.d.ts +51 -0
- package/dist/components/ui/page-header.js +372 -0
- package/dist/components/ui/page-header.js.map +1 -0
- package/dist/components/ui/payment-card-fields.d.ts +10 -0
- package/dist/components/ui/payment-card-fields.js +80 -0
- package/dist/components/ui/payment-card-fields.js.map +1 -0
- package/dist/components/ui/popover.d.ts +10 -0
- package/dist/components/ui/popover.js +47 -0
- package/dist/components/ui/popover.js.map +1 -0
- package/dist/components/ui/radio-group.d.ts +29 -0
- package/dist/components/ui/radio-group.js +190 -0
- package/dist/components/ui/radio-group.js.map +1 -0
- package/dist/components/ui/resizable.d.ts +16 -0
- package/dist/components/ui/resizable.js +51 -0
- package/dist/components/ui/resizable.js.map +1 -0
- package/dist/components/ui/scroll-area.d.ts +8 -0
- package/dist/components/ui/scroll-area.js +66 -0
- package/dist/components/ui/scroll-area.js.map +1 -0
- package/dist/components/ui/select.d.ts +18 -0
- package/dist/components/ui/select.js +186 -0
- package/dist/components/ui/select.js.map +1 -0
- package/dist/components/ui/selection-tile-grid.d.ts +52 -0
- package/dist/components/ui/selection-tile-grid.js +347 -0
- package/dist/components/ui/selection-tile-grid.js.map +1 -0
- package/dist/components/ui/separator.d.ts +7 -0
- package/dist/components/ui/separator.js +33 -0
- package/dist/components/ui/separator.js.map +1 -0
- package/dist/components/ui/sheet.d.ts +18 -0
- package/dist/components/ui/sheet.js +181 -0
- package/dist/components/ui/sheet.js.map +1 -0
- package/dist/components/ui/sidebar.d.ts +94 -0
- package/dist/components/ui/sidebar.js +805 -0
- package/dist/components/ui/sidebar.js.map +1 -0
- package/dist/components/ui/skeleton.d.ts +5 -0
- package/dist/components/ui/skeleton.js +22 -0
- package/dist/components/ui/skeleton.js.map +1 -0
- package/dist/components/ui/slider.d.ts +7 -0
- package/dist/components/ui/slider.js +66 -0
- package/dist/components/ui/slider.js.map +1 -0
- package/dist/components/ui/sonner.d.ts +6 -0
- package/dist/components/ui/sonner.js +38 -0
- package/dist/components/ui/sonner.js.map +1 -0
- package/dist/components/ui/status-badge.d.ts +38 -0
- package/dist/components/ui/status-badge.js +77 -0
- package/dist/components/ui/status-badge.js.map +1 -0
- package/dist/components/ui/table.d.ts +13 -0
- package/dist/components/ui/table.js +115 -0
- package/dist/components/ui/table.js.map +1 -0
- package/dist/components/ui/tabs.d.ts +15 -0
- package/dist/components/ui/tabs.js +93 -0
- package/dist/components/ui/tabs.js.map +1 -0
- package/dist/components/ui/textarea.d.ts +6 -0
- package/dist/components/ui/textarea.js +25 -0
- package/dist/components/ui/textarea.js.map +1 -0
- package/dist/components/ui/tip.d.ts +12 -0
- package/dist/components/ui/tip.js +61 -0
- package/dist/components/ui/tip.js.map +1 -0
- package/dist/components/ui/toggle-group.d.ts +14 -0
- package/dist/components/ui/toggle-group.js +104 -0
- package/dist/components/ui/toggle-group.js.map +1 -0
- package/dist/components/ui/toggle-switch.d.ts +10 -0
- package/dist/components/ui/toggle-switch.js +33 -0
- package/dist/components/ui/toggle-switch.js.map +1 -0
- package/dist/components/ui/toggle.d.ts +13 -0
- package/dist/components/ui/toggle.js +51 -0
- package/dist/components/ui/toggle.js.map +1 -0
- package/dist/components/ui/tooltip.d.ts +10 -0
- package/dist/components/ui/tooltip.js +68 -0
- package/dist/components/ui/tooltip.js.map +1 -0
- package/dist/components/ui/view-segmented-control.d.ts +31 -0
- package/dist/components/ui/view-segmented-control.js +167 -0
- package/dist/components/ui/view-segmented-control.js.map +1 -0
- package/dist/data-list-view-registry-CyBoBML4.d.ts +73 -0
- package/dist/hooks/use-app-theme.d.ts +24 -0
- package/dist/hooks/use-app-theme.js +286 -0
- package/dist/hooks/use-app-theme.js.map +1 -0
- package/dist/hooks/use-coach-mark.d.ts +86 -0
- package/dist/hooks/use-coach-mark.js +218 -0
- package/dist/hooks/use-coach-mark.js.map +1 -0
- package/dist/hooks/use-mobile.d.ts +3 -0
- package/dist/hooks/use-mobile.js +29 -0
- package/dist/hooks/use-mobile.js.map +1 -0
- package/dist/hooks/use-mod-key-label.d.ts +6 -0
- package/dist/hooks/use-mod-key-label.js +25 -0
- package/dist/hooks/use-mod-key-label.js.map +1 -0
- package/dist/index.d.ts +120 -0
- package/dist/index.js +13421 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/compose-refs.d.ts +6 -0
- package/dist/lib/compose-refs.js +17 -0
- package/dist/lib/compose-refs.js.map +1 -0
- package/dist/lib/conditional-rule-match.d.ts +30 -0
- package/dist/lib/conditional-rule-match.js +66 -0
- package/dist/lib/conditional-rule-match.js.map +1 -0
- package/dist/lib/data-list-display-options.d.ts +26 -0
- package/dist/lib/data-list-display-options.js +14 -0
- package/dist/lib/data-list-display-options.js.map +1 -0
- package/dist/lib/data-list-view-registry.d.ts +2 -0
- package/dist/lib/data-list-view-registry.js +102 -0
- package/dist/lib/data-list-view-registry.js.map +1 -0
- package/dist/lib/data-list-view-surface.d.ts +2 -0
- package/dist/lib/data-list-view-surface.js +80 -0
- package/dist/lib/data-list-view-surface.js.map +1 -0
- package/dist/lib/data-list-view.d.ts +21 -0
- package/dist/lib/data-list-view.js +25 -0
- package/dist/lib/data-list-view.js.map +1 -0
- package/dist/lib/date-filter.d.ts +22 -0
- package/dist/lib/date-filter.js +61 -0
- package/dist/lib/date-filter.js.map +1 -0
- package/dist/lib/dev-log.d.ts +8 -0
- package/dist/lib/dev-log.js +10 -0
- package/dist/lib/dev-log.js.map +1 -0
- package/dist/lib/dropdown-menu-surface.d.ts +14 -0
- package/dist/lib/dropdown-menu-surface.js +6 -0
- package/dist/lib/dropdown-menu-surface.js.map +1 -0
- package/dist/lib/editable-target.d.ts +12 -0
- package/dist/lib/editable-target.js +12 -0
- package/dist/lib/editable-target.js.map +1 -0
- package/dist/lib/list-page-table-properties.d.ts +35 -0
- package/dist/lib/list-page-table-properties.js +81 -0
- package/dist/lib/list-page-table-properties.js.map +1 -0
- package/dist/lib/raf-throttle.d.ts +23 -0
- package/dist/lib/raf-throttle.js +27 -0
- package/dist/lib/raf-throttle.js.map +1 -0
- package/dist/lib/row-height.d.ts +16 -0
- package/dist/lib/row-height.js +10 -0
- package/dist/lib/row-height.js.map +1 -0
- package/dist/lib/table-properties-types.d.ts +83 -0
- package/dist/lib/table-properties-types.js +19 -0
- package/dist/lib/table-properties-types.js.map +1 -0
- package/dist/lib/utils.d.ts +5 -0
- package/dist/lib/utils.js +11 -0
- package/dist/lib/utils.js.map +1 -0
- package/package.json +83 -19
- package/src/components/data-table/filter-date-calendar.tsx +38 -0
- package/src/components/data-table/filter-text-value-input.tsx +77 -0
- package/src/components/data-table/index.tsx +1678 -0
- package/src/components/data-table/pagination.tsx +259 -0
- package/src/components/data-table/types.ts +96 -0
- package/src/components/data-table/use-table-state.ts +767 -0
- package/src/components/data-views/board-card-primitives.tsx +93 -0
- package/src/components/data-views/data-row-list.tsx +183 -0
- package/src/components/data-views/finder-panel-view.tsx +405 -0
- package/src/components/data-views/folder-grid-view.tsx +86 -0
- package/src/components/data-views/hub-table.tsx +606 -0
- package/src/components/data-views/index.ts +28 -0
- package/src/components/data-views/list-page-board-card.tsx +192 -0
- package/src/components/data-views/list-page-board-template.tsx +122 -0
- package/src/components/data-views/list-page-connected-view-body.tsx +66 -0
- package/src/components/data-views/list-page-split-details-placeholder.tsx +39 -0
- package/src/components/data-views/list-page-split-hub-chrome.tsx +60 -0
- package/src/components/data-views/list-page-split-hub-tokens.ts +16 -0
- package/src/components/data-views/list-page-tree-column-header.tsx +31 -0
- package/src/components/data-views/list-page-tree-panel-shell.tsx +91 -0
- package/src/components/data-views/os-folder-glyph.tsx +141 -0
- package/src/components/data-views/outline-tree-menu.tsx +157 -0
- package/src/components/table-properties/column-row.tsx +90 -0
- package/src/components/table-properties/draggable-list.ts +54 -0
- package/src/components/table-properties/drawer-button.tsx +300 -0
- package/src/components/table-properties/drawer.tsx +1148 -0
- package/src/components/table-properties/filter-card.tsx +251 -0
- package/src/components/table-properties/index.ts +36 -0
- package/src/components/table-properties/sort-card.tsx +63 -0
- package/src/components/templates/dedicated-search-landing-template.tsx +124 -0
- package/src/components/templates/dedicated-search-results-template.tsx +19 -0
- package/src/components/templates/index.ts +33 -0
- package/src/components/templates/list-page.tsx +602 -0
- package/src/components/templates/nested-secondary-panel-shell.tsx +70 -0
- package/src/components/ui/accordion.tsx +92 -0
- package/src/components/ui/alert-dialog.tsx +221 -0
- package/src/components/ui/avatar.tsx +13 -2
- package/src/components/ui/banner.tsx +2 -2
- package/src/components/ui/button.tsx +4 -4
- package/src/components/ui/calendar.tsx +1 -1
- package/src/components/ui/coach-mark.tsx +1 -1
- package/src/components/ui/context-menu.tsx +291 -0
- package/src/components/ui/date-picker-field.tsx +2 -2
- package/src/components/ui/dot-pattern.tsx +183 -0
- package/src/components/ui/export-drawer.tsx +375 -0
- package/src/components/ui/hover-card.tsx +66 -0
- package/src/components/ui/key-metrics-context.tsx +78 -0
- package/src/components/ui/key-metrics.tsx +1133 -0
- package/src/components/ui/list-page-view-frame.tsx +64 -0
- package/src/components/ui/page-header.tsx +244 -0
- package/src/components/ui/payment-card-fields.tsx +2 -2
- package/src/components/ui/resizable.tsx +68 -0
- package/src/components/ui/scroll-area.tsx +72 -0
- package/src/components/ui/selection-tile-grid.tsx +9 -2
- package/src/components/ui/sidebar.tsx +84 -12
- package/src/components/ui/slider.tsx +83 -0
- package/src/globals.css +2201 -7
- package/src/globals.d.ts +20 -0
- package/src/index.ts +68 -1
- package/src/lib/conditional-rule-match.ts +119 -0
- package/src/lib/data-list-display-options.ts +35 -0
- package/src/lib/data-list-view-registry.ts +104 -0
- package/src/lib/data-list-view-surface.ts +83 -0
- package/src/lib/data-list-view.ts +47 -0
- package/src/lib/dev-log.ts +10 -0
- package/src/lib/editable-target.ts +20 -0
- package/src/lib/list-page-table-properties.ts +48 -0
- package/src/lib/raf-throttle.ts +45 -0
- package/src/lib/row-height.ts +19 -0
- package/src/lib/table-properties-types.ts +98 -0
- package/template/.claude/skills/exxat-ds-skill/SKILL.md +8 -7
- package/template/.cursor/rules/exxat-accessibility.mdc +1 -1
- package/template/.cursor/rules/exxat-command-menu.mdc +2 -2
- package/template/.cursor/rules/exxat-dashboard-view-charts.mdc +7 -7
- package/template/.cursor/rules/exxat-data-tables.mdc +3 -3
- package/template/.cursor/rules/exxat-ds-agents.mdc +2 -2
- package/template/.cursor/rules/exxat-kbd-shortcuts.mdc +7 -7
- package/template/.cursor/rules/exxat-mono-ids.mdc +1 -1
- package/template/.cursor/rules/exxat-page-vs-drawer.mdc +1 -1
- package/template/.cursor/rules/exxat-table-properties-drawer.mdc +1 -1
- package/template/AGENTS.md +135 -103
- package/template/app/(app)/columns/page.tsx +11 -0
- package/template/app/(app)/dashboard/loading.tsx +15 -3
- package/template/app/(app)/dashboard/page.tsx +14 -2
- package/template/app/(app)/layout.tsx +17 -4
- package/template/app/(app)/library/all/page.tsx +11 -0
- package/template/app/(app)/library/find/page.tsx +12 -0
- package/template/app/(app)/{question-bank → library}/layout.tsx +17 -17
- package/template/app/(app)/library/list/page.tsx +12 -0
- package/template/app/(app)/library/new/page.tsx +45 -0
- package/template/app/(app)/library/page.tsx +11 -0
- package/template/app/(app)/loading.tsx +18 -1
- package/template/app/(app)/settings/page.tsx +5 -4
- package/template/app/(app)/tokens-themes/page.tsx +11 -0
- package/template/app/globals.css +14 -16
- package/template/components/ask-leo-composer.tsx +2 -2
- package/template/components/ask-leo-sidebar.tsx +5 -1
- package/template/components/brand-color-picker.tsx +2 -2
- package/template/components/charts-overview.tsx +1 -1
- package/template/components/columns-client.tsx +158 -0
- package/template/components/columns-showcase.tsx +541 -0
- package/template/components/dashboard-report-charts.tsx +1 -1
- package/template/components/dashboard-tabs.tsx +1 -1
- package/template/components/data-table/filter-date-calendar.tsx +1 -38
- package/template/components/data-table/filter-text-value-input.tsx +1 -77
- package/template/components/data-table/index.tsx +1 -1634
- package/template/components/data-table/pagination.tsx +1 -255
- package/template/components/data-table/types.ts +1 -94
- package/template/components/data-table/use-table-state.test.ts +420 -0
- package/template/components/data-table/use-table-state.ts +1 -758
- package/template/components/data-views/board-card-primitives.tsx +1 -93
- package/template/components/data-views/data-row-list.tsx +1 -183
- package/template/components/data-views/finder-panel-view.tsx +1 -405
- package/template/components/data-views/folder-grid-view.tsx +1 -86
- package/template/components/data-views/hub-table.tsx +1 -0
- package/template/components/data-views/index.ts +77 -38
- package/template/components/data-views/{question-bank-folder-tree-branch.tsx → library-folder-tree-branch.tsx} +19 -19
- package/template/components/data-views/list-page-board-card.tsx +1 -192
- package/template/components/data-views/list-page-board-template.tsx +1 -122
- package/template/components/data-views/list-page-connected-view-body.tsx +1 -66
- package/template/components/data-views/list-page-split-details-placeholder.tsx +1 -39
- package/template/components/data-views/list-page-split-hub-chrome.tsx +1 -68
- package/template/components/data-views/list-page-split-hub-tokens.ts +1 -16
- package/template/components/data-views/list-page-tree-column-header.tsx +1 -31
- package/template/components/data-views/list-page-tree-panel-shell.tsx +1 -91
- package/template/components/data-views/list-page-view-frame.tsx +5 -53
- package/template/components/data-views/os-folder-glyph.tsx +1 -129
- package/template/components/data-views/outline-tree-menu.tsx +1 -157
- package/template/components/data-views/table-cells.tsx +673 -0
- package/template/components/export-drawer.test.tsx +71 -0
- package/template/components/export-drawer.tsx +1 -375
- package/template/components/exxat-product-logo.tsx +5 -5
- package/template/components/folder-details-shell.tsx +11 -11
- package/template/components/hub-tree-panel-view.tsx +26 -26
- package/template/components/invite-collaborators-drawer.tsx +3 -3
- package/template/components/key-metrics-ask-leo-bridge.tsx +40 -0
- package/template/components/key-metrics.tsx +1 -1063
- package/template/components/leo-insight-indicator.tsx +2 -2
- package/template/components/{question-bank-board-view.tsx → library-board-view.tsx} +44 -44
- package/template/components/{question-bank-client.tsx → library-client.tsx} +83 -83
- package/template/components/{question-bank-dashboard-charts.tsx → library-dashboard-charts.tsx} +14 -14
- package/template/components/{question-bank-favorite-button.tsx → library-favorite-button.tsx} +7 -7
- package/template/components/{question-bank-hub-client.tsx → library-hub-client.tsx} +44 -44
- package/template/components/{question-bank-new-folder-sheet.tsx → library-new-folder-sheet.tsx} +16 -16
- package/template/components/{question-bank-os-folder-view.tsx → library-os-folder-view.tsx} +31 -31
- package/template/components/{question-bank-page-header.tsx → library-page-header.tsx} +6 -6
- package/template/components/library-panel-activator.tsx +8 -0
- package/template/components/{question-bank-secondary-nav.tsx → library-secondary-nav.tsx} +63 -63
- package/template/components/library-table.tsx +839 -0
- package/template/components/list-hub-status-badge.tsx +2 -2
- package/template/components/{new-question-composer.tsx → new-library-item-form.tsx} +489 -441
- package/template/components/onboarding/index.ts +9 -0
- package/template/components/onboarding/onboarding-01.tsx +1 -1
- package/template/components/onboarding/onboarding-02.tsx +1 -1
- package/template/components/onboarding/onboarding-03.tsx +1 -1
- package/template/components/onboarding/onboarding-04.tsx +1 -1
- package/template/components/page-header.tsx +8 -226
- package/template/components/product-switcher.tsx +3 -4
- package/template/components/product-wordmark.tsx +2 -1
- package/template/components/settings-appearance-card.tsx +3 -4
- package/template/components/settings-client.tsx +15 -59
- package/template/components/settings-form-row.tsx +4 -9
- package/template/components/{app-sidebar-dynamic.tsx → sidebar/app-sidebar-dynamic.tsx} +1 -1
- package/template/components/{app-sidebar.tsx → sidebar/app-sidebar.tsx} +114 -73
- package/template/components/sidebar/index.ts +16 -0
- package/template/components/{secondary-nav.tsx → sidebar/secondary-nav.tsx} +2 -2
- package/template/components/sidebar/secondary-panel.tsx +316 -0
- package/template/components/sidebar/sidebar-auto-collapse.tsx +27 -0
- package/template/components/{sidebar-auto-open.tsx → sidebar/sidebar-auto-open.tsx} +2 -1
- package/template/components/{sidebar-shell.tsx → sidebar/sidebar-shell.tsx} +1 -1
- package/template/components/site-header.tsx +1 -1
- package/template/components/table-properties/column-row.tsx +1 -90
- package/template/components/table-properties/draggable-list.ts +1 -49
- package/template/components/table-properties/drawer-button.tsx +1 -262
- package/template/components/table-properties/drawer.tsx +1 -1166
- package/template/components/table-properties/filter-card.tsx +1 -251
- package/template/components/table-properties/sort-card.tsx +1 -59
- package/template/components/table-properties/types.ts +28 -71
- package/template/components/templates/dedicated-search-landing-template.tsx +1 -124
- package/template/components/templates/dedicated-search-results-template.tsx +1 -19
- package/template/components/templates/discovery-hub-template.tsx +1 -1
- package/template/components/templates/list-page.tsx +1 -608
- package/template/components/templates/nested-secondary-panel-shell.tsx +1 -63
- package/template/components/templates/new-focus-template.tsx +659 -0
- package/template/components/templates/secondary-panel-hub-template.tsx +2 -2
- package/template/components/tokens-secondary-nav.tsx +192 -0
- package/template/components/tokens-themes-client.tsx +476 -0
- package/template/components/tokens-themes-section.tsx +386 -0
- package/template/components/ui/accordion.tsx +1 -0
- package/template/components/ui/alert-dialog.tsx +1 -0
- package/template/components/ui/context-menu.tsx +1 -0
- package/template/components/ui/dot-pattern.tsx +1 -183
- package/template/components/ui/hover-card.tsx +1 -0
- package/template/components/ui/resizable.tsx +1 -68
- package/template/components/ui/scroll-area.tsx +1 -0
- package/template/components/ui/slider.tsx +1 -0
- package/template/docs/HANDBOOK.md +187 -0
- package/template/docs/blueprints/README.md +86 -0
- package/template/docs/blueprints/_template.md +91 -0
- package/template/docs/blueprints/board-card.md +123 -0
- package/template/docs/blueprints/data-table.md +139 -0
- package/template/docs/blueprints/key-metrics.md +128 -0
- package/template/docs/blueprints/list-page-template.md +123 -0
- package/template/docs/blueprints/page-header.md +130 -0
- package/template/docs/collaboration-access-pattern.md +7 -7
- package/template/docs/command-menu-pattern.md +1 -1
- package/template/docs/component-selection-guide.md +224 -0
- package/template/docs/components-audit-2026-05.md +158 -0
- package/template/docs/data-views-pattern.md +31 -66
- package/template/docs/drawer-vs-dialog-pattern.md +1 -3
- package/template/docs/glossary.md +58 -0
- package/template/docs/kpi-flat-band-pattern.md +3 -3
- package/template/docs/kpi-trend-pattern.md +18 -3
- package/template/docs/large-dataset-strategy.md +155 -0
- package/template/docs/library-hub-header-pattern.md +25 -0
- package/template/docs/migrations/0001-brand-deep-alias-stabilization.md +95 -0
- package/template/docs/migrations/0002-exxat-token-namespace.md +154 -0
- package/template/docs/migrations/0003-globals-css-canonical.md +110 -0
- package/template/docs/migrations/README.md +100 -0
- package/template/docs/migrations/_template.md +64 -0
- package/template/docs/reference-implementations.md +151 -0
- package/template/docs/shell-surface-elevation-pattern.md +3 -5
- package/template/docs/token-taxonomy.md +416 -0
- package/template/docs/voice-and-tone.md +262 -0
- package/template/eslint.config.mjs +27 -0
- package/template/hooks/use-secondary-panel-hub-nav.ts +11 -11
- package/template/lib/ask-leo-route-context.ts +6 -18
- package/template/lib/coach-mark-registry.ts +0 -16
- package/template/lib/command-menu-config.ts +5 -13
- package/template/lib/command-menu-search-data.ts +8 -23
- package/template/lib/conditional-rule-match.ts +6 -97
- package/template/lib/data-list-display-options.ts +1 -49
- package/template/lib/data-list-view-registry.ts +1 -104
- package/template/lib/data-list-view-surface.ts +1 -83
- package/template/lib/data-list-view.ts +1 -47
- package/template/lib/data-view-dashboard-storage.ts +35 -38
- package/template/lib/dev-log.ts +1 -8
- package/template/lib/editable-target.ts +1 -10
- package/template/lib/{question-bank-authoring.ts → library-authoring.ts} +89 -88
- package/template/lib/library-dedicated-search.ts +19 -0
- package/template/lib/library-hub-search.ts +90 -0
- package/template/lib/library-nav.ts +477 -0
- package/template/lib/library-recent-searches.ts +22 -0
- package/template/lib/{question-bank-supported-views.ts → library-supported-views.ts} +2 -3
- package/template/lib/list-page-table-properties.ts +1 -48
- package/template/lib/list-status-badges.ts +16 -11
- package/template/lib/mock/dashboard.ts +1 -1
- package/template/lib/mock/{question-bank-folders.ts → library-folders.ts} +30 -30
- package/template/lib/mock/library-header-collaborators.ts +54 -0
- package/template/lib/mock/{question-bank-inspector.ts → library-inspector.ts} +29 -29
- package/template/lib/mock/{question-bank-kpi.ts → library-kpi.ts} +20 -20
- package/template/lib/mock/library.ts +249 -0
- package/template/lib/mock/navigation.tsx +32 -35
- package/template/lib/raf-throttle.ts +1 -45
- package/template/lib/row-height.ts +4 -10
- package/template/lib/sidebar-state-cookie.ts +11 -2
- package/template/lib/table-state-lifecycle.ts +3 -3
- package/template/next.config.mjs +7 -4
- package/template/package.json +1 -0
- package/template/tests/setup.ts +25 -0
- package/consumer-extras/AGENTS.md +0 -76
- package/consumer-extras/cursor-skills/exxat-consumer-app/SKILL.md +0 -37
- package/consumer-extras/cursor-skills/exxat-focused-workflow-page/SKILL.md +0 -57
- package/consumer-extras/patterns/consumer-app-pattern.md +0 -39
- package/consumer-extras/patterns/focused-workflow-page-pattern.md +0 -84
- package/src/components/ui/button-group.tsx +0 -81
- package/src/theme.css +0 -16
- package/src/tokens/README.md +0 -15
- package/src/tokens/base.css +0 -337
- package/src/tokens/high-contrast.css +0 -1195
- package/src/tokens/layers.css +0 -224
- package/src/tokens/tailwind-bridge.css +0 -118
- package/src/tokens/themes.css +0 -201
- package/template/app/(app)/data-list/layout.tsx +0 -43
- package/template/app/(app)/data-list/page.tsx +0 -10
- package/template/app/(app)/examples/focused-workflow/page.tsx +0 -5
- package/template/app/(app)/examples/page.tsx +0 -43
- package/template/app/(app)/question-bank/find/page.tsx +0 -13
- package/template/app/(app)/question-bank/library/page.tsx +0 -12
- package/template/app/(app)/question-bank/list/page.tsx +0 -13
- package/template/app/(app)/question-bank/new/page.tsx +0 -50
- package/template/app/(app)/question-bank/page.tsx +0 -12
- package/template/components/app-route-loading.tsx +0 -14
- package/template/components/dashboard-onboarding-gallery.tsx +0 -13
- package/template/components/dashboard-onboarding.tsx +0 -21
- package/template/components/data-views/list-page-calendar-view.tsx +0 -593
- package/template/components/data-views/list-page-folder-columns-panel.tsx +0 -345
- package/template/components/examples/focused-workflow-showcase.tsx +0 -183
- package/template/components/list-hub-board-view.tsx +0 -68
- package/template/components/list-hub-client.tsx +0 -186
- package/template/components/list-hub-list-view.tsx +0 -36
- package/template/components/list-hub-panel-activator.tsx +0 -8
- package/template/components/list-hub-secondary-nav.tsx +0 -121
- package/template/components/list-hub-table.tsx +0 -336
- package/template/components/question-bank-folder-columns-panel.tsx +0 -104
- package/template/components/question-bank-list-view.tsx +0 -53
- package/template/components/question-bank-panel-activator.tsx +0 -8
- package/template/components/question-bank-table.tsx +0 -729
- package/template/components/secondary-panel/nav-link-rows.tsx +0 -83
- package/template/components/secondary-panel.tsx +0 -220
- package/template/components/secondary-panels/list-hub-panel.tsx +0 -39
- package/template/components/secondary-panels/question-bank-panel.tsx +0 -39
- package/template/components/secondary-panels/registry.tsx +0 -15
- package/template/components/section-cards.tsx +0 -106
- package/template/components/sidebar-auto-collapse.tsx +0 -23
- package/template/components/templates/focused-workflow-layouts.tsx +0 -448
- package/template/components/templates/focused-workflow-page-template.tsx +0 -69
- package/template/components/templates/page-loading-shell.tsx +0 -262
- package/template/components/ui/button-group.tsx +0 -1
- package/template/docs/consumer-app-pattern.md +0 -39
- package/template/docs/focused-workflow-page-pattern.md +0 -84
- package/template/docs/question-bank-hub-header-pattern.md +0 -25
- package/template/lib/list-hub-nav.ts +0 -121
- package/template/lib/mock/list-hub-directory.ts +0 -27
- package/template/lib/mock/list-hub-kpi.ts +0 -27
- package/template/lib/mock/question-bank-header-collaborators.ts +0 -54
- package/template/lib/mock/question-bank.ts +0 -249
- package/template/lib/page-loading-variant.ts +0 -40
- package/template/lib/question-bank-dedicated-search.ts +0 -19
- package/template/lib/question-bank-hub-search.ts +0 -90
- package/template/lib/question-bank-nav.ts +0 -477
- package/template/lib/question-bank-recent-searches.ts +0 -22
- /package/template/components/{getting-started.tsx → onboarding/getting-started.tsx} +0 -0
- /package/template/components/{nav-documents.tsx → sidebar/nav-documents.tsx} +0 -0
- /package/template/components/{nav-main.tsx → sidebar/nav-main.tsx} +0 -0
- /package/template/components/{nav-secondary.tsx → sidebar/nav-secondary.tsx} +0 -0
- /package/template/components/{nav-user.tsx → sidebar/nav-user.tsx} +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* FolderGridView — generic icon-grid layout for any list-page hub.
|
|
5
|
+
*
|
|
6
|
+
* Handles the responsive CSS grid shell, empty state, and accessibility list role.
|
|
7
|
+
* Pass a `renderTile` render-prop for domain-specific tile content
|
|
8
|
+
* (Placements, Team, Rotations, etc.).
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```tsx
|
|
12
|
+
* <FolderGridView
|
|
13
|
+
* rows={placements}
|
|
14
|
+
* getRowId={r => r.id}
|
|
15
|
+
* renderTile={row => <PlacementFolderTile row={row} onClick={…} />}
|
|
16
|
+
* ariaLabel="Placements folder view"
|
|
17
|
+
* />
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import * as React from "react"
|
|
22
|
+
import { cn } from "../../lib/utils"
|
|
23
|
+
import {
|
|
24
|
+
ListPageViewFrame,
|
|
25
|
+
LIST_PAGE_VIEW_FRAME_MAX_ICON_GRID,
|
|
26
|
+
} from "../ui/list-page-view-frame"
|
|
27
|
+
|
|
28
|
+
export interface FolderGridViewProps<T> {
|
|
29
|
+
rows: T[]
|
|
30
|
+
getRowId: (row: T) => string | number
|
|
31
|
+
/** Render one tile — receives the row, returns a React node (typically a `<button>`). */
|
|
32
|
+
renderTile: (row: T) => React.ReactNode
|
|
33
|
+
/** Shown when `rows` is empty. */
|
|
34
|
+
emptyContent?: React.ReactNode
|
|
35
|
+
/** `aria-label` on the grid list. */
|
|
36
|
+
ariaLabel?: string
|
|
37
|
+
className?: string
|
|
38
|
+
/**
|
|
39
|
+
* When true, constrains the grid to a centered max width so folder tiles don’t stretch
|
|
40
|
+
* edge-to-edge on very wide viewports.
|
|
41
|
+
*/
|
|
42
|
+
constrainWidth?: boolean
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function FolderGridView<T>({
|
|
46
|
+
rows,
|
|
47
|
+
getRowId,
|
|
48
|
+
renderTile,
|
|
49
|
+
emptyContent,
|
|
50
|
+
ariaLabel = "Folder view",
|
|
51
|
+
className,
|
|
52
|
+
constrainWidth = false,
|
|
53
|
+
}: FolderGridViewProps<T>) {
|
|
54
|
+
if (rows.length === 0) {
|
|
55
|
+
return (
|
|
56
|
+
<ListPageViewFrame
|
|
57
|
+
maxWidthClassName={constrainWidth ? LIST_PAGE_VIEW_FRAME_MAX_ICON_GRID : undefined}
|
|
58
|
+
className={cn(className)}
|
|
59
|
+
>
|
|
60
|
+
<div className="flex flex-col items-center justify-center rounded-xl border border-dashed border-border py-16 text-sm text-muted-foreground">
|
|
61
|
+
<i className="fa-solid fa-grid-2 mb-3 text-3xl opacity-40" aria-hidden="true" />
|
|
62
|
+
{emptyContent ?? <p>No records found.</p>}
|
|
63
|
+
</div>
|
|
64
|
+
</ListPageViewFrame>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<ListPageViewFrame className={cn(className)}>
|
|
70
|
+
<div
|
|
71
|
+
className={cn(
|
|
72
|
+
"grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:grid-cols-8",
|
|
73
|
+
constrainWidth && "mx-auto max-w-6xl",
|
|
74
|
+
)}
|
|
75
|
+
role="list"
|
|
76
|
+
aria-label={ariaLabel}
|
|
77
|
+
>
|
|
78
|
+
{rows.map(row => (
|
|
79
|
+
<div key={getRowId(row)} role="listitem">
|
|
80
|
+
{renderTile(row)}
|
|
81
|
+
</div>
|
|
82
|
+
))}
|
|
83
|
+
</div>
|
|
84
|
+
</ListPageViewFrame>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
@@ -0,0 +1,606 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `<HubTable<TRow>>` — single centralized table surface used by every list-page hub.
|
|
5
|
+
*
|
|
6
|
+
* Owns all the per-hub scaffolding that was previously duplicated in `placements-table.tsx`,
|
|
7
|
+
* `team-table.tsx`, `compliance-table.tsx`, `library-table.tsx`, and `sites-table.tsx`:
|
|
8
|
+
*
|
|
9
|
+
* • `useTableState` setup tied to the centralized row dataset
|
|
10
|
+
* • `displayOptions` state + `patchDisplay`
|
|
11
|
+
* • `conditionalRules` state + add/remove/update handlers
|
|
12
|
+
* • `filterFields` derived from column `filter` defs
|
|
13
|
+
* • `fieldDefinitions` + `resolveColumnLabel` for the Properties drawer
|
|
14
|
+
* • `TablePropertiesDrawerButton` wiring with `currentView` + `supportedViewTypes`
|
|
15
|
+
* • `DataTable` for `view === "table"`; `ListPageConnectedViewBody` for every other view
|
|
16
|
+
* • Imperative `openPropertiesDrawer()` exposed via `handleRef`
|
|
17
|
+
*
|
|
18
|
+
* Each hub now only declares **what's actually different**: its column defs and a typed
|
|
19
|
+
* `renderers` map for the views it implements. The renderers receive `state`, `toolbar`,
|
|
20
|
+
* `toolbarShell`, `drawerToolbarProps`, `displayOptions`, and `patchDisplay` so they can
|
|
21
|
+
* either reuse the pre-composed toolbar (`toolbarShell(<MyView rows={state.rows} />)`) or
|
|
22
|
+
* build their own (e.g. dashboard view with extra layout-edit actions).
|
|
23
|
+
*
|
|
24
|
+
* **Single dataset rule:** rows flow through `useTableState`; `tableState.rows` is the
|
|
25
|
+
* filtered/sorted row bag every non-table renderer reads. There is **no** parallel mock
|
|
26
|
+
* array per view. See `.cursor/rules/exxat-centralized-list-dataset.mdc`.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import * as React from "react"
|
|
30
|
+
import { cn } from "../../lib/utils"
|
|
31
|
+
import { DataTable, DataTableToolbar } from "../data-table"
|
|
32
|
+
import { CountSyncer, PaginationBar } from "../data-table/pagination"
|
|
33
|
+
import type { ColumnDef } from "../data-table/types"
|
|
34
|
+
import { useTableState } from "../data-table/use-table-state"
|
|
35
|
+
import type { DataListViewType } from "../../lib/data-list-view"
|
|
36
|
+
import {
|
|
37
|
+
getDataListViewRenderKind,
|
|
38
|
+
type DataListViewRenderKind,
|
|
39
|
+
} from "../../lib/data-list-view-registry"
|
|
40
|
+
import {
|
|
41
|
+
DEFAULT_DATA_LIST_DISPLAY_OPTIONS,
|
|
42
|
+
type DataListDisplayOptions,
|
|
43
|
+
} from "../../lib/data-list-display-options"
|
|
44
|
+
import { TablePropertiesDrawerButton } from "../table-properties"
|
|
45
|
+
import type {
|
|
46
|
+
ConditionalRule,
|
|
47
|
+
FilterFieldDef,
|
|
48
|
+
FilterOperator,
|
|
49
|
+
} from "../../lib/table-properties-types"
|
|
50
|
+
import {
|
|
51
|
+
ListPageConnectedViewBody,
|
|
52
|
+
type ListPageConnectedViewRenderers,
|
|
53
|
+
} from "./list-page-connected-view-body"
|
|
54
|
+
import { DataRowList } from "./data-row-list"
|
|
55
|
+
import {
|
|
56
|
+
ListPageBoardTemplate,
|
|
57
|
+
type ListPageBoardColumnDef,
|
|
58
|
+
} from "./list-page-board-template"
|
|
59
|
+
import type { OpenTablePropertiesHandle } from "../../lib/list-page-table-properties"
|
|
60
|
+
|
|
61
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
62
|
+
// Helpers
|
|
63
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
function columnToFilterFieldDef<TRow>(c: ColumnDef<TRow>): FilterFieldDef | null {
|
|
66
|
+
if (!c.filter) return null
|
|
67
|
+
const f = c.filter
|
|
68
|
+
const defaultOps: FilterOperator[] =
|
|
69
|
+
f.type === "select" || f.type === "date" ? ["is", "is_not"] : ["contains", "not_contains"]
|
|
70
|
+
return {
|
|
71
|
+
key: c.key,
|
|
72
|
+
label: c.label,
|
|
73
|
+
icon: f.icon ?? "fa-filter",
|
|
74
|
+
type: f.type,
|
|
75
|
+
operators: (f.operators ?? defaultOps) as FilterOperator[],
|
|
76
|
+
options: f.options,
|
|
77
|
+
...(f.textMask ? { textMask: f.textMask } : {}),
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Derive `FilterFieldDef[]` from a hub's column defs — preferred to hand-writing both. */
|
|
82
|
+
export function columnsToFilterFields<TRow>(cols: ColumnDef<TRow>[]): FilterFieldDef[] {
|
|
83
|
+
return cols
|
|
84
|
+
.map(c => columnToFilterFieldDef<TRow>(c))
|
|
85
|
+
.filter((x): x is FilterFieldDef => x !== null)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Field definitions for the drawer's Sort / Group / Columns panels — excludes utility columns. */
|
|
89
|
+
export function columnsToFieldDefinitions<TRow>(
|
|
90
|
+
cols: ColumnDef<TRow>[],
|
|
91
|
+
): { key: string; label: string; sortable: boolean }[] {
|
|
92
|
+
return cols
|
|
93
|
+
.filter(c => c.key !== "select" && c.key !== "actions")
|
|
94
|
+
.map(c => ({
|
|
95
|
+
key: c.key,
|
|
96
|
+
label: c.label,
|
|
97
|
+
sortable: !!(c.sortable && (c.sortKey ?? c.key)),
|
|
98
|
+
}))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
102
|
+
// Public types
|
|
103
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
/** Subset of `TablePropertiesDrawerButton` props that `HubTable` owns. Exposed so renderers
|
|
106
|
+
* that build a custom toolbar (e.g. dashboard with an extra Edit-layout button) can splat
|
|
107
|
+
* this back into their own `<TablePropertiesDrawerButton {...drawerToolbarProps} state={s} extraActions={…} />`. */
|
|
108
|
+
export interface HubDrawerToolbarProps {
|
|
109
|
+
totalRows: number
|
|
110
|
+
filterFields: FilterFieldDef[]
|
|
111
|
+
fieldDefinitions: { key: string; label: string; sortable: boolean }[]
|
|
112
|
+
resolveColumnLabel: (key: string) => string
|
|
113
|
+
displayOptions: DataListDisplayOptions
|
|
114
|
+
onDisplayOptionsChange: (patch: Partial<DataListDisplayOptions>) => void
|
|
115
|
+
conditionalRules: ConditionalRule[]
|
|
116
|
+
onAddConditionalRule: (rule: Omit<ConditionalRule, "id">) => void
|
|
117
|
+
onRemoveConditionalRule: (id: string) => void
|
|
118
|
+
onUpdateConditionalRule: (id: string, patch: Partial<ConditionalRule>) => void
|
|
119
|
+
currentView: DataListViewType
|
|
120
|
+
onViewChange?: (v: DataListViewType) => void
|
|
121
|
+
supportedViewTypes: readonly DataListViewType[]
|
|
122
|
+
lifecycleTabLabel: string
|
|
123
|
+
boardGroupByColumnOptions?: { key: string; label: string }[]
|
|
124
|
+
renderFilterOptionValue?: (fieldKey: string, value: string) => React.ReactNode
|
|
125
|
+
pagination?: boolean
|
|
126
|
+
onPaginationChange?: (v: boolean) => void
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Everything a non-table renderer needs. The pre-composed `toolbar` includes search +
|
|
130
|
+
* filter chips + the Properties button; the renderer just wraps it around its body with
|
|
131
|
+
* `toolbarShell`. For dashboards or other layouts that own their own toolbar, use
|
|
132
|
+
* `drawerToolbarProps` to assemble a custom `<TablePropertiesDrawerButton>`. */
|
|
133
|
+
export interface HubTableRendererArgs<TRow extends Record<string, unknown>> {
|
|
134
|
+
state: ReturnType<typeof useTableState<TRow>>
|
|
135
|
+
toolbar: React.ReactNode
|
|
136
|
+
toolbarShell: (body: React.ReactNode) => React.ReactNode
|
|
137
|
+
drawerToolbarProps: HubDrawerToolbarProps
|
|
138
|
+
displayOptions: DataListDisplayOptions
|
|
139
|
+
patchDisplay: (patch: Partial<DataListDisplayOptions>) => void
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Hubs provide one renderer per `DataListViewRenderKind` they implement. The `data-table`
|
|
143
|
+
* kind is handled internally by `HubTable` (do not override unless you genuinely need a
|
|
144
|
+
* different table surface). Missing kinds render `<ListPageViewNotConfigured>` — clear empty
|
|
145
|
+
* state, never a silent dashboard fallback. */
|
|
146
|
+
export type HubTableRenderers<TRow extends Record<string, unknown>> = Partial<
|
|
147
|
+
Record<DataListViewRenderKind, (args: HubTableRendererArgs<TRow>) => React.ReactNode>
|
|
148
|
+
>
|
|
149
|
+
|
|
150
|
+
export type HubTableHandle = OpenTablePropertiesHandle
|
|
151
|
+
|
|
152
|
+
export interface HubTableProps<TRow extends Record<string, unknown>> {
|
|
153
|
+
/** Full row dataset (already scoped/filtered by the hub client, e.g. by URL nav). */
|
|
154
|
+
rows: TRow[]
|
|
155
|
+
/** Column defs — `filter` blocks here become Properties drawer filter fields automatically. */
|
|
156
|
+
columns: ColumnDef<TRow>[]
|
|
157
|
+
/** Active view from the `ListPageTemplate` tab. */
|
|
158
|
+
view: DataListViewType
|
|
159
|
+
onViewChange?: (v: DataListViewType) => void
|
|
160
|
+
/** Allowlist passed to `TablePropertiesDrawerButton` so Properties cannot offer unsupported views. */
|
|
161
|
+
supportedViewTypes: readonly DataListViewType[]
|
|
162
|
+
/** Used by `ListPageViewNotConfigured` when a supported view has no renderer. */
|
|
163
|
+
hubLabel: string
|
|
164
|
+
/** Shown below "Properties" in the drawer header. */
|
|
165
|
+
lifecycleTabLabel: string
|
|
166
|
+
/** Toolbar search input aria-label. */
|
|
167
|
+
searchAriaLabel: string
|
|
168
|
+
getRowId: (row: TRow) => string | number
|
|
169
|
+
getRowSelectionLabel: (row: TRow) => string
|
|
170
|
+
defaultSort: { key: string; dir: "asc" | "desc" }
|
|
171
|
+
/** DataTable empty state. Defaults to a muted "No records match your filters." */
|
|
172
|
+
emptyState?: React.ReactNode
|
|
173
|
+
/** Per-view renderers (everything other than `view === "table"`). */
|
|
174
|
+
renderers: HubTableRenderers<TRow>
|
|
175
|
+
/** Bulk-actions slot rendered when one or more rows are selected. */
|
|
176
|
+
bulkActionsSlot?: (selected: Set<string | number>) => React.ReactNode
|
|
177
|
+
/** Board-view group-by column options for the Properties drawer. */
|
|
178
|
+
boardGroupByColumnOptions?: { key: string; label: string }[]
|
|
179
|
+
/** Initial display options (toolbar search visibility, calendar panel, etc.). */
|
|
180
|
+
displayOptionsInit?: DataListDisplayOptions
|
|
181
|
+
/** Custom renderer for filter option values (e.g. status chips). */
|
|
182
|
+
renderFilterOptionValue?: (fieldKey: string, value: string) => React.ReactNode
|
|
183
|
+
/** DataTable: enable "Group by" feature. Default `true`. */
|
|
184
|
+
groupable?: boolean
|
|
185
|
+
/** DataTable: enable row selection checkboxes. Default `true`. */
|
|
186
|
+
selectable?: boolean
|
|
187
|
+
/** DataTable: row click handler (e.g. navigate to detail route). */
|
|
188
|
+
onRowClick?: (row: TRow) => void
|
|
189
|
+
/**
|
|
190
|
+
* Controlled `displayOptions` — when provided (with `onDisplayOptionsChange`), the hub client
|
|
191
|
+
* owns persistence (e.g. Placements page persists across tab switches). Otherwise `HubTable`
|
|
192
|
+
* owns internal state from `displayOptionsInit`.
|
|
193
|
+
*/
|
|
194
|
+
displayOptions?: DataListDisplayOptions
|
|
195
|
+
onDisplayOptionsChange?: (patch: Partial<DataListDisplayOptions>) => void
|
|
196
|
+
/**
|
|
197
|
+
* Pagination toggle forwarded to `TablePropertiesDrawerButton` so the drawer can offer
|
|
198
|
+
* "Show pagination" on the Display panel. The hub still implements its own pagination chrome
|
|
199
|
+
* around the table body via `tableRenderer`. Defaults to `false` / no toggle.
|
|
200
|
+
*/
|
|
201
|
+
pagination?: boolean
|
|
202
|
+
onPaginationChange?: (v: boolean) => void
|
|
203
|
+
/** Imperative handle (`openPropertiesDrawer`) for hub clients that pass `tablePropertiesRef`. */
|
|
204
|
+
handleRef?: React.Ref<HubTableHandle>
|
|
205
|
+
/** Optional override for the `data-table` view. Default mounts `<DataTable {…}>`. */
|
|
206
|
+
tableRenderer?: (args: HubTableRendererArgs<TRow>) => React.ReactNode
|
|
207
|
+
/**
|
|
208
|
+
* Forwarded to `useTableState` so the hub can switch on server-style pagination
|
|
209
|
+
* with externally-controlled page/pageSize (advanced; most hubs should leave
|
|
210
|
+
* this undefined and let `HubTable` own the internal page state — see
|
|
211
|
+
* `pagination` + `paginationPageSizeOptions`).
|
|
212
|
+
*/
|
|
213
|
+
paginationOverride?: { page: number; pageSize: number }
|
|
214
|
+
/** Page size options shown in the toolbar `<PaginationBar>`. Default `[10, 25, 50, 100]`. */
|
|
215
|
+
paginationPageSizeOptions?: number[]
|
|
216
|
+
/** Initial page size when `HubTable` owns pagination internally. Default `10`. */
|
|
217
|
+
paginationInitialPageSize?: number
|
|
218
|
+
/**
|
|
219
|
+
* Forwarded to `useTableState` to sync toolbar search from `?q=` (Library search routes).
|
|
220
|
+
* Defining as `""` enables sync without an initial query.
|
|
221
|
+
*/
|
|
222
|
+
syncedSearchFromUrl?: string
|
|
223
|
+
|
|
224
|
+
// ─── Centralized "list" and "board" defaults ───────────────────────────────
|
|
225
|
+
// When a hub does NOT provide a renderer for `list-with-toolbar`, but DOES provide
|
|
226
|
+
// `renderListRow`, `HubTable` synthesises a default that wires `DataRowList` through
|
|
227
|
+
// the shared toolbar. Same for `board-with-toolbar` + `renderBoardCard`. This is the
|
|
228
|
+
// happy path for most hubs; the explicit renderer escape hatch is only needed for
|
|
229
|
+
// exotic surfaces (e.g. Placements board with per-phase column search).
|
|
230
|
+
|
|
231
|
+
/** Default `list-with-toolbar` renderer body — `HubTable` wraps with toolbar + `DataRowList`. */
|
|
232
|
+
renderListRow?: (row: TRow) => React.ReactNode
|
|
233
|
+
/** Override the `aria-label` on the `<ul>` from `renderListRow`. Defaults to `hubLabel`. */
|
|
234
|
+
listAriaLabel?: string
|
|
235
|
+
/** Empty-state for the default list renderer. Defaults to "No records match your filters." */
|
|
236
|
+
listEmptyState?: React.ReactNode
|
|
237
|
+
/** Virtualise after N rows in the default list renderer. Default 100; pass `0` to disable. */
|
|
238
|
+
listVirtualizeThreshold?: number
|
|
239
|
+
/** Estimated row height (px) for the default list virtualiser. Default 96. */
|
|
240
|
+
listEstimatedRowHeight?: number
|
|
241
|
+
|
|
242
|
+
/** Default `board-with-toolbar` renderer body — `HubTable` wraps with toolbar + `ListPageBoardTemplate`. */
|
|
243
|
+
renderBoardCard?: (row: TRow) => React.ReactNode
|
|
244
|
+
/** Required if `renderBoardCard` is set: how rows group into columns. */
|
|
245
|
+
boardGroups?: ListPageBoardColumnDef<TRow>[]
|
|
246
|
+
/** Per-column count badge tint classes for the default board renderer. */
|
|
247
|
+
boardColumnCountBadgeClassName?: Record<string, string>
|
|
248
|
+
/** Empty column copy for the default board renderer. Defaults to "No items". */
|
|
249
|
+
boardEmptyColumnLabel?: string
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
253
|
+
// Component
|
|
254
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
255
|
+
|
|
256
|
+
export function HubTable<TRow extends Record<string, unknown>>({
|
|
257
|
+
rows,
|
|
258
|
+
columns,
|
|
259
|
+
view,
|
|
260
|
+
onViewChange,
|
|
261
|
+
supportedViewTypes,
|
|
262
|
+
hubLabel,
|
|
263
|
+
lifecycleTabLabel,
|
|
264
|
+
searchAriaLabel,
|
|
265
|
+
getRowId,
|
|
266
|
+
getRowSelectionLabel,
|
|
267
|
+
defaultSort,
|
|
268
|
+
emptyState,
|
|
269
|
+
renderers,
|
|
270
|
+
bulkActionsSlot,
|
|
271
|
+
boardGroupByColumnOptions,
|
|
272
|
+
displayOptionsInit,
|
|
273
|
+
renderFilterOptionValue,
|
|
274
|
+
groupable = true,
|
|
275
|
+
selectable = true,
|
|
276
|
+
onRowClick,
|
|
277
|
+
displayOptions: displayOptionsControlled,
|
|
278
|
+
onDisplayOptionsChange: onDisplayOptionsChangeControlled,
|
|
279
|
+
pagination,
|
|
280
|
+
onPaginationChange,
|
|
281
|
+
handleRef,
|
|
282
|
+
tableRenderer,
|
|
283
|
+
paginationOverride,
|
|
284
|
+
paginationPageSizeOptions = [10, 25, 50, 100],
|
|
285
|
+
paginationInitialPageSize = 10,
|
|
286
|
+
syncedSearchFromUrl,
|
|
287
|
+
renderListRow,
|
|
288
|
+
listAriaLabel,
|
|
289
|
+
listEmptyState,
|
|
290
|
+
listVirtualizeThreshold,
|
|
291
|
+
listEstimatedRowHeight,
|
|
292
|
+
renderBoardCard,
|
|
293
|
+
boardGroups,
|
|
294
|
+
boardColumnCountBadgeClassName,
|
|
295
|
+
boardEmptyColumnLabel,
|
|
296
|
+
}: HubTableProps<TRow>) {
|
|
297
|
+
const filterFields = React.useMemo(() => columnsToFilterFields(columns), [columns])
|
|
298
|
+
const fieldDefinitions = React.useMemo(() => columnsToFieldDefinitions(columns), [columns])
|
|
299
|
+
const resolveColumnLabel = React.useCallback(
|
|
300
|
+
(key: string) => columns.find(c => c.key === key)?.label ?? key,
|
|
301
|
+
[columns],
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
// displayOptions: controlled (parent owns state via prop pair) OR uncontrolled (HubTable owns it).
|
|
305
|
+
// Most hubs keep display options ephemeral; Placements persists them across tabs at the page level.
|
|
306
|
+
const [internalDisplayOptions, setInternalDisplayOptions] = React.useState<DataListDisplayOptions>(
|
|
307
|
+
displayOptionsInit ?? DEFAULT_DATA_LIST_DISPLAY_OPTIONS,
|
|
308
|
+
)
|
|
309
|
+
const isControlled =
|
|
310
|
+
displayOptionsControlled !== undefined && onDisplayOptionsChangeControlled !== undefined
|
|
311
|
+
const displayOptions = isControlled ? displayOptionsControlled : internalDisplayOptions
|
|
312
|
+
const patchDisplay = React.useCallback(
|
|
313
|
+
(patch: Partial<DataListDisplayOptions>) => {
|
|
314
|
+
if (isControlled) {
|
|
315
|
+
onDisplayOptionsChangeControlled!(patch)
|
|
316
|
+
} else {
|
|
317
|
+
setInternalDisplayOptions(prev => ({ ...prev, ...patch }))
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
[isControlled, onDisplayOptionsChangeControlled],
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
const [conditionalRules, setConditionalRules] = React.useState<ConditionalRule[]>([])
|
|
324
|
+
const addConditionalRule = React.useCallback((rule: Omit<ConditionalRule, "id">) => {
|
|
325
|
+
setConditionalRules(prev => [...prev, { ...rule, id: `cr-${Date.now()}` }])
|
|
326
|
+
}, [])
|
|
327
|
+
const removeConditionalRule = React.useCallback((id: string) => {
|
|
328
|
+
setConditionalRules(prev => prev.filter(r => r.id !== id))
|
|
329
|
+
}, [])
|
|
330
|
+
const updateConditionalRule = React.useCallback(
|
|
331
|
+
(id: string, patch: Partial<ConditionalRule>) => {
|
|
332
|
+
setConditionalRules(prev => prev.map(r => (r.id === id ? { ...r, ...patch } : r)))
|
|
333
|
+
},
|
|
334
|
+
[],
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
// ─── Pagination chrome (centralized) ─────────────────────────────────────
|
|
338
|
+
// When `pagination === true` and the parent did NOT supply `paginationOverride`,
|
|
339
|
+
// `HubTable` owns the page/pageSize internally and wraps the default table +
|
|
340
|
+
// list renderers with `<CountSyncer>` + `<PaginationBar>`. Hubs that need full
|
|
341
|
+
// control (e.g. server-side pagination) keep using `paginationOverride`.
|
|
342
|
+
const [internalPage, setInternalPage] = React.useState(1)
|
|
343
|
+
const [internalPageSize, setInternalPageSize] = React.useState(paginationInitialPageSize)
|
|
344
|
+
const chromeOwnedPagination = pagination === true && paginationOverride === undefined
|
|
345
|
+
const effectivePaginationOverride =
|
|
346
|
+
paginationOverride ??
|
|
347
|
+
(chromeOwnedPagination ? { page: internalPage, pageSize: internalPageSize } : undefined)
|
|
348
|
+
|
|
349
|
+
const tableState = useTableState<TRow>(
|
|
350
|
+
rows,
|
|
351
|
+
columns,
|
|
352
|
+
defaultSort,
|
|
353
|
+
effectivePaginationOverride,
|
|
354
|
+
syncedSearchFromUrl,
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
const handlePageChange = React.useCallback((p: number) => setInternalPage(p), [])
|
|
358
|
+
const handlePageSizeChange = React.useCallback((n: number) => {
|
|
359
|
+
setInternalPageSize(n)
|
|
360
|
+
setInternalPage(1)
|
|
361
|
+
}, [])
|
|
362
|
+
const resetPage = React.useCallback(() => setInternalPage(1), [])
|
|
363
|
+
|
|
364
|
+
// Extract the stable setter from `useTableState` first so the
|
|
365
|
+
// `useImperativeHandle` deps array sees the exact value the hook reads.
|
|
366
|
+
// `setSheetOpen` is referentially stable, so the handle is created once.
|
|
367
|
+
const { setSheetOpen: openPropertiesSheet } = tableState
|
|
368
|
+
React.useImperativeHandle(
|
|
369
|
+
handleRef ?? null,
|
|
370
|
+
() => ({ openPropertiesDrawer: () => openPropertiesSheet(true) }),
|
|
371
|
+
[openPropertiesSheet],
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
const drawerToolbarProps: HubDrawerToolbarProps = {
|
|
375
|
+
totalRows: rows.length,
|
|
376
|
+
filterFields,
|
|
377
|
+
fieldDefinitions,
|
|
378
|
+
resolveColumnLabel,
|
|
379
|
+
displayOptions,
|
|
380
|
+
onDisplayOptionsChange: patchDisplay,
|
|
381
|
+
conditionalRules,
|
|
382
|
+
onAddConditionalRule: addConditionalRule,
|
|
383
|
+
onRemoveConditionalRule: removeConditionalRule,
|
|
384
|
+
onUpdateConditionalRule: updateConditionalRule,
|
|
385
|
+
currentView: view,
|
|
386
|
+
onViewChange,
|
|
387
|
+
supportedViewTypes,
|
|
388
|
+
lifecycleTabLabel,
|
|
389
|
+
boardGroupByColumnOptions,
|
|
390
|
+
renderFilterOptionValue,
|
|
391
|
+
...(pagination !== undefined ? { pagination } : {}),
|
|
392
|
+
...(onPaginationChange !== undefined ? { onPaginationChange } : {}),
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const toolbar = (
|
|
396
|
+
<DataTableToolbar
|
|
397
|
+
state={tableState}
|
|
398
|
+
columns={columns}
|
|
399
|
+
searchable={displayOptions.showToolbarSearch}
|
|
400
|
+
searchAriaLabel={searchAriaLabel}
|
|
401
|
+
renderFilterOptionValue={renderFilterOptionValue}
|
|
402
|
+
toolbarSlot={s => <TablePropertiesDrawerButton {...drawerToolbarProps} state={s} />}
|
|
403
|
+
/>
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
const toolbarShell = React.useCallback(
|
|
407
|
+
(body: React.ReactNode) => (
|
|
408
|
+
<div className="flex min-h-0 flex-1 flex-col">
|
|
409
|
+
{toolbar}
|
|
410
|
+
{body}
|
|
411
|
+
</div>
|
|
412
|
+
),
|
|
413
|
+
// `toolbar` is recreated each render; including it would defeat memoization but
|
|
414
|
+
// also gives renderers a fresh closure — acceptable because renderers are called
|
|
415
|
+
// on render anyway. Keep deps stable so the function identity is stable.
|
|
416
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
417
|
+
[
|
|
418
|
+
tableState,
|
|
419
|
+
columns,
|
|
420
|
+
displayOptions.showToolbarSearch,
|
|
421
|
+
searchAriaLabel,
|
|
422
|
+
renderFilterOptionValue,
|
|
423
|
+
drawerToolbarProps,
|
|
424
|
+
],
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
// Default `data-table` renderer — full DataTable with toolbar + bulk actions. When
|
|
428
|
+
// `pagination === true` and the parent did not provide `paginationOverride`, the chrome
|
|
429
|
+
// (CountSyncer + PaginationBar) is wrapped automatically so the drawer toggle "Show
|
|
430
|
+
// pagination" works out of the box. Hubs that need finer control (custom chrome,
|
|
431
|
+
// server-side paging) can still override via `tableRenderer`.
|
|
432
|
+
const defaultTableRenderer = (args: HubTableRendererArgs<TRow>) => {
|
|
433
|
+
const filteredCount = (args.state.rows as TRow[]).length
|
|
434
|
+
const totalPages = Math.max(1, Math.ceil(filteredCount / Math.max(1, internalPageSize)))
|
|
435
|
+
const safePage = Math.min(internalPage, totalPages)
|
|
436
|
+
return (
|
|
437
|
+
<div className="pb-6">
|
|
438
|
+
{chromeOwnedPagination ? (
|
|
439
|
+
<CountSyncer
|
|
440
|
+
count={filteredCount}
|
|
441
|
+
onSync={(n) => {
|
|
442
|
+
const next = Math.max(1, Math.ceil(n / Math.max(1, internalPageSize)))
|
|
443
|
+
if (safePage > next) setInternalPage(next)
|
|
444
|
+
}}
|
|
445
|
+
onReset={resetPage}
|
|
446
|
+
/>
|
|
447
|
+
) : null}
|
|
448
|
+
<DataTable<TRow>
|
|
449
|
+
data={rows}
|
|
450
|
+
columns={columns}
|
|
451
|
+
getRowId={getRowId}
|
|
452
|
+
getRowSelectionLabel={getRowSelectionLabel}
|
|
453
|
+
selectable={selectable}
|
|
454
|
+
searchable={displayOptions.showToolbarSearch}
|
|
455
|
+
showColumnHeaders={displayOptions.showColumnLabels}
|
|
456
|
+
groupable={groupable}
|
|
457
|
+
defaultSort={defaultSort}
|
|
458
|
+
emptyState={emptyState ?? <p className="text-sm text-muted-foreground">No records match your filters.</p>}
|
|
459
|
+
conditionalRules={conditionalRules}
|
|
460
|
+
state={args.state}
|
|
461
|
+
renderFilterOptionValue={renderFilterOptionValue}
|
|
462
|
+
toolbarSlot={s => <TablePropertiesDrawerButton {...drawerToolbarProps} state={s} />}
|
|
463
|
+
bulkActionsSlot={bulkActionsSlot}
|
|
464
|
+
onRowClick={onRowClick}
|
|
465
|
+
hasFooter={chromeOwnedPagination}
|
|
466
|
+
/>
|
|
467
|
+
{chromeOwnedPagination ? (
|
|
468
|
+
<div
|
|
469
|
+
className={cn(
|
|
470
|
+
"mx-4 lg:mx-6 border-x border-b border-border rounded-b-lg overflow-hidden bg-background",
|
|
471
|
+
// z-40 sits above pinned cells (z-20), group headers (z-25), and column headers
|
|
472
|
+
// (z-30 / z-40) so the sticky footer paints over any table content that scrolls
|
|
473
|
+
// behind it. Pinned-left cells ship with their own `bg-dt-row-bg`, which
|
|
474
|
+
// otherwise wins because of z-20 > z-10.
|
|
475
|
+
"sticky bottom-0 z-40",
|
|
476
|
+
)}
|
|
477
|
+
>
|
|
478
|
+
<PaginationBar
|
|
479
|
+
page={safePage}
|
|
480
|
+
pageSize={internalPageSize}
|
|
481
|
+
total={filteredCount}
|
|
482
|
+
pageSizeOptions={paginationPageSizeOptions}
|
|
483
|
+
onPageChange={handlePageChange}
|
|
484
|
+
onPageSizeChange={handlePageSizeChange}
|
|
485
|
+
/>
|
|
486
|
+
</div>
|
|
487
|
+
) : null}
|
|
488
|
+
</div>
|
|
489
|
+
)
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const args: HubTableRendererArgs<TRow> = {
|
|
493
|
+
state: tableState,
|
|
494
|
+
toolbar,
|
|
495
|
+
toolbarShell,
|
|
496
|
+
drawerToolbarProps,
|
|
497
|
+
displayOptions,
|
|
498
|
+
patchDisplay,
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Dev-time warning when a `supportedViewType` has no renderer (other than `table`,
|
|
502
|
+
// which has the built-in default). Mirrors `defineHubViewRenderers` from
|
|
503
|
+
// `lib/hub-connected-view-renderers.ts` but inline so we don't double-wrap.
|
|
504
|
+
if (process.env.NODE_ENV !== "production") {
|
|
505
|
+
for (const v of supportedViewTypes) {
|
|
506
|
+
const kind = getDataListViewRenderKind(v)
|
|
507
|
+
if (kind === "data-table") continue
|
|
508
|
+
if (renderers[kind] == null) {
|
|
509
|
+
console.warn(
|
|
510
|
+
`[Exxat DS][HubTable: ${hubLabel}] Missing renderer for supported view "${v}" (${kind}). ` +
|
|
511
|
+
"Add a renderer entry, or remove the view from supportedViewTypes.",
|
|
512
|
+
)
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Compose `ListPageConnectedViewBody` renderers: the built-in `data-table` (or hub override)
|
|
518
|
+
// plus each hub-provided non-table renderer. Wrapping in `() => …` defers execution until
|
|
519
|
+
// the active view actually selects that kind — so heavy bodies (dashboard charts) don't
|
|
520
|
+
// pay render cost on a table tab.
|
|
521
|
+
const composed: ListPageConnectedViewRenderers = {
|
|
522
|
+
"data-table": () => (tableRenderer ?? defaultTableRenderer)(args),
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Default centralized list renderer: same DataRowList shell every hub used to roll
|
|
526
|
+
// by hand. Hub provides only the per-row body via `renderListRow`. When `pagination`
|
|
527
|
+
// is on (and `paginationOverride` is not externally supplied), the list view reads
|
|
528
|
+
// `state.pagedRows` and adds the same `CountSyncer` + `PaginationBar` chrome as the
|
|
529
|
+
// table view.
|
|
530
|
+
if (renderers["list-with-toolbar"] == null && renderListRow != null) {
|
|
531
|
+
composed["list-with-toolbar"] = () => {
|
|
532
|
+
const fullRows = args.state.rows as TRow[]
|
|
533
|
+
const pagedRows = chromeOwnedPagination ? (args.state.pagedRows as TRow[]) : fullRows
|
|
534
|
+
const filteredCount = fullRows.length
|
|
535
|
+
const totalPages = Math.max(1, Math.ceil(filteredCount / Math.max(1, internalPageSize)))
|
|
536
|
+
const safePage = Math.min(internalPage, totalPages)
|
|
537
|
+
return args.toolbarShell(
|
|
538
|
+
<>
|
|
539
|
+
{chromeOwnedPagination ? (
|
|
540
|
+
<CountSyncer
|
|
541
|
+
count={filteredCount}
|
|
542
|
+
onSync={(n) => {
|
|
543
|
+
const next = Math.max(1, Math.ceil(n / Math.max(1, internalPageSize)))
|
|
544
|
+
if (safePage > next) setInternalPage(next)
|
|
545
|
+
}}
|
|
546
|
+
onReset={resetPage}
|
|
547
|
+
/>
|
|
548
|
+
) : null}
|
|
549
|
+
<DataRowList<TRow>
|
|
550
|
+
rows={pagedRows}
|
|
551
|
+
getRowId={row => getRowId(row)}
|
|
552
|
+
ariaLabel={listAriaLabel ?? hubLabel}
|
|
553
|
+
emptyState={listEmptyState ?? "No records match your filters."}
|
|
554
|
+
{...(listVirtualizeThreshold !== undefined ? { virtualizeThreshold: listVirtualizeThreshold } : {})}
|
|
555
|
+
{...(listEstimatedRowHeight !== undefined ? { estimatedRowHeight: listEstimatedRowHeight } : {})}
|
|
556
|
+
renderRow={renderListRow}
|
|
557
|
+
/>
|
|
558
|
+
{chromeOwnedPagination ? (
|
|
559
|
+
<div
|
|
560
|
+
className={cn(
|
|
561
|
+
"mx-4 lg:mx-6 border-x border-b border-border rounded-b-lg overflow-hidden bg-background",
|
|
562
|
+
// Match the table-view footer — above pinned cells (z-20) and column
|
|
563
|
+
// headers (z-30 / z-40) so the sticky chrome paints over scrolling rows.
|
|
564
|
+
"sticky bottom-0 z-40",
|
|
565
|
+
)}
|
|
566
|
+
>
|
|
567
|
+
<PaginationBar
|
|
568
|
+
page={safePage}
|
|
569
|
+
pageSize={internalPageSize}
|
|
570
|
+
total={filteredCount}
|
|
571
|
+
pageSizeOptions={paginationPageSizeOptions}
|
|
572
|
+
onPageChange={handlePageChange}
|
|
573
|
+
onPageSizeChange={handlePageSizeChange}
|
|
574
|
+
/>
|
|
575
|
+
</div>
|
|
576
|
+
) : null}
|
|
577
|
+
</>,
|
|
578
|
+
)
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Default centralized board renderer: same ListPageBoardTemplate every hub used to wrap.
|
|
583
|
+
// Hub provides only the per-card body via `renderBoardCard` and the column predicate set.
|
|
584
|
+
if (renderers["board-with-toolbar"] == null && renderBoardCard != null && boardGroups != null) {
|
|
585
|
+
composed["board-with-toolbar"] = () =>
|
|
586
|
+
args.toolbarShell(
|
|
587
|
+
<ListPageBoardTemplate<TRow>
|
|
588
|
+
columns={boardGroups}
|
|
589
|
+
rows={args.state.rows as TRow[]}
|
|
590
|
+
getRowKey={getRowId}
|
|
591
|
+
renderCard={renderBoardCard}
|
|
592
|
+
columnCountBadgeClassName={boardColumnCountBadgeClassName ?? {}}
|
|
593
|
+
emptyColumnLabel={boardEmptyColumnLabel ?? "No items"}
|
|
594
|
+
/>,
|
|
595
|
+
)
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
for (const kind of Object.keys(renderers) as DataListViewRenderKind[]) {
|
|
599
|
+
const r = renderers[kind]
|
|
600
|
+
if (r) composed[kind] = () => r(args)
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return <ListPageConnectedViewBody view={view} hubLabel={hubLabel} renderers={composed} />
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
HubTable.displayName = "HubTable"
|