@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
package/template/AGENTS.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
**Purpose:** One place for product patterns so tools (Cursor, Codex, etc.) and contributors apply the same rules. **Imperative sections** use MUST / MUST NOT / SHOULD so they are easy to parse.
|
|
4
4
|
|
|
5
|
-
**Scope:** The Next.js app in this directory. **Path:** If your workspace root is only this folder, use **`./AGENTS.md`**. If the workspace is the parent
|
|
5
|
+
**Scope:** The Next.js app in this directory. **Path:** If your workspace root is only this folder, use **`./AGENTS.md`**. If the workspace is the parent monorepo, use **`apps/web/AGENTS.md`** (this file).
|
|
6
|
+
|
|
7
|
+
> **First-time reader?** Open **`docs/HANDBOOK.md`** first — it's a 10-minute orientation that links into the right section here. This file is the **authoritative §-numbered manual**; the handbook is the map. Other quick entry points: **`docs/glossary.md`** (shared vocabulary), **`docs/voice-and-tone.md`** (copy rules), **`docs/reference-implementations.md`** (canonical reference pages).
|
|
6
8
|
|
|
7
9
|
Cross-cutting Cursor rules also live in the repo root `.cursor/rules/` (data tables, keyboard hints, accessibility) when the parent repo is open.
|
|
8
10
|
|
|
@@ -14,31 +16,32 @@ Cross-cutting Cursor rules also live in the repo root `.cursor/rules/` (data tab
|
|
|
14
16
|
2. **Before** changing **keyboard hints or shortcuts**, read **§7** and root `.cursor/rules/exxat-kbd-shortcuts.mdc`.
|
|
15
17
|
3. **Before** changing **table behavior**, read **§3** and root `.cursor/rules/exxat-data-tables.mdc`. **Before** wiring **`TablePropertiesDrawer`** on **`ListPageTemplate`** (view tabs), read **§4.2** and **`.cursor/rules/exxat-table-properties-drawer.mdc`**.
|
|
16
18
|
4. **Before** building or changing **tabs, nav, dialogs, icon-only controls, or color/contrast**, read **§8** and **`.cursor/skills/exxat-accessibility/SKILL.md`** (from monorepo root when the parent repo is open).
|
|
17
|
-
5. **Before** adding or changing **Data view charts** (dashboard tab on list hubs) or **graph keyboard styling**, read **§4.3** and **`
|
|
19
|
+
5. **Before** adding or changing **Data view charts** (dashboard tab on list hubs) or **graph keyboard styling**, read **§4.3** and **`apps/web/.cursor/rules/exxat-dashboard-view-charts.mdc`**.
|
|
18
20
|
6. **Before** adding or changing **board (kanban) cards** on list hubs, read **§4.4** and the **`exxat-board-cards`** skill (**`.cursor/skills/`** or **`.claude/skills/`** at repo root — same content).
|
|
19
21
|
7. **Before** adding **folder, panel, or other non-table view bodies** (centered grids, reusable shells), read **§4.5** and **`.cursor/rules/exxat-list-page-view-shells.mdc`** / **`.cursor/skills/exxat-list-page-view-shells/SKILL.md`**.
|
|
20
22
|
8. **Before** adding or changing **Font Awesome** icons in app UI, read **`.cursor/rules/exxat-fontawesome-icons.mdc`** (Kit subsetting, weights, **`aria-hidden`** on **`<i>`**).
|
|
21
23
|
9. **Before** rendering **record IDs, question IDs, or other system identifiers**, read **`.cursor/rules/exxat-mono-ids.mdc`** and **`.cursor/skills/exxat-mono-ids/SKILL.md`** (**`font-mono tabular-nums`**).
|
|
22
|
-
10. **Before** adding a **primary nav row** that opens a **nested secondary nav panel** (
|
|
24
|
+
10. **Before** adding a **primary nav row** that opens a **nested secondary nav panel** (Library style), read **§4.6** and **`.cursor/rules/exxat-primary-nav-secondary-panel.mdc`**.
|
|
23
25
|
11. **Before** adding **shared access / invite collaborators** on a hub (face stack + invite sheet), read **§4.7** and **`.cursor/rules/exxat-collaboration-access.mdc`** / **`.cursor/skills/exxat-collaboration-access/SKILL.md`**.
|
|
24
26
|
12. **Before** adding **onboarding tours, feature walkthroughs, or coach marks**, read **§11** and `references/coach-marks.md`.
|
|
25
27
|
13. **Before** changing the **global command palette (⌘K)** or search/AI entry UX, read **§7.1** and **`docs/command-menu-pattern.md`**.
|
|
26
28
|
14. **Before** choosing **drawer vs dialog vs new page** for a task flow, read **§6.4**, **`docs/data-views-pattern.md`** (Page vs drawer), and **`docs/drawer-vs-dialog-pattern.md`** (modal vs side panel on the same route).
|
|
27
|
-
15. **Before** adding
|
|
28
|
-
16. **
|
|
29
|
-
17. Prefer **composing existing components** over new one-off UI. If something is missing, **extend** shared components under `components/`, not a single page file.
|
|
29
|
+
15. **Before** adding **success/error/confirmation feedback**, read **§6.5** and **`.cursor/rules/exxat-no-toast.mdc`** (no toast or snackbars).
|
|
30
|
+
16. Prefer **composing existing components** over new one-off UI. If something is missing, **extend** shared components under `components/`, not a single page file.
|
|
30
31
|
- **MUST** scan `components/` (especially `components/ui/`, `components/data-views/`, `components/templates/`, `components/key-metrics.tsx`, `components/page-header.tsx`, and the charts/banner/dot-pattern surfaces) **before** writing any new UI. If a primitive or composition already exists, **use it** — don't build a parallel one.
|
|
31
|
-
- **Examples of existing surfaces to reuse:** card grid → `ListPageBoardCard` + `BoardCardIconRow` / `BoardCardTwoLineBlock`; AI / dot animation → `AiThinkingOverlay` + `DotPattern`; search input → `InputGroup` + `InputGroupAddon` + `InputGroupInput`; page title → `PageHeader` (serif via `font-heading`); list hub shell → `ListPageTemplate` (`metrics`, `defaultTabs`, `renderContent`);
|
|
32
|
+
- **Examples of existing surfaces to reuse:** card grid → `ListPageBoardCard` + `BoardCardIconRow` / `BoardCardTwoLineBlock`; AI / dot animation → `AiThinkingOverlay` + `DotPattern`; search input → `InputGroup` + `InputGroupAddon` + `InputGroupInput`; page title → `PageHeader` (serif via `font-heading`); list hub shell → `ListPageTemplate` (`metrics`, `defaultTabs`, `renderContent`); metrics strip → `KeyMetrics`; **view body gutter + centered max-width** → **`ListPageViewFrame`** (**§4.5**); **shared access / invite** → **`PageHeader` `variant="collaboration"`** + **`InviteCollaboratorsDrawer`** (**§4.7**).
|
|
32
33
|
- **If** nothing fits and you would add a **new shared primitive or large bespoke widget**: **ask the user** for direction first — **`.cursor/rules/exxat-reuse-before-custom.mdc`** (unless the task already explicitly approved a greenfield build).
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
**
|
|
34
|
+
17. **Match** naming, imports, and patterns of the nearest reference implementation (usually Placements).
|
|
35
|
+
18. **Before** adding entity **mock data**, a **new view tab**, or **detail/inspector** panels on a list hub, read **`.cursor/rules/exxat-centralized-list-dataset.mdc`** and **`.cursor/skills/exxat-centralized-list-dataset/SKILL.md`** (single **`useTableState`** row bag for every view; **no** parallel mock arrays per view).
|
|
36
|
+
19. **Before** choosing **cards vs table rows vs simple lists** for a hub, read **`docs/card-vs-rows-pattern.md`** and **`.cursor/rules/exxat-card-vs-list-rows.mdc`**.
|
|
37
|
+
20. **Before** adding **`KeyMetrics`** strips on list hubs or dashboard key-metrics cards, read **`docs/kpi-strip-max-four-pattern.md`** and **`.cursor/rules/exxat-kpi-max-four.mdc`** (at most **four** tiles).
|
|
38
|
+
21. **Before** styling **`KeyMetrics variant="flat"`** (list hub metrics strip, dashboard mix KPI band), read **`docs/kpi-flat-band-pattern.md`** and **`.cursor/rules/exxat-kpi-flat-band.mdc`** / **`.cursor/skills/exxat-kpi-flat-band/SKILL.md`** (transparent band, OKLCH glow, border hairlines only).
|
|
39
|
+
22. **Before** changing **secondary panel** or **sidebar** brand chrome, read **`docs/shell-surface-elevation-pattern.md`** and **§4.6** ( **`--secondary-panel-bg`**, active product theme).
|
|
40
|
+
23. **Before** adding **new shared UI primitives** or bespoke widgets when nothing in **`components/`** fits after scanning, follow **`.cursor/rules/exxat-reuse-before-custom.mdc`** — **ask the user** for direction unless the task already approved a greenfield build.
|
|
41
|
+
24. **Before** introducing a **color, spacing, radius, or shadow value** in JSX `className`, inline `style`, CSS, or a `cn()` / `clsx()` argument, read **`docs/token-taxonomy.md`** and **`.cursor/rules/exxat-token-discipline.mdc`**. **New code SHOULD reach for the L0 Exxat namespace first** (`var(--exxat-color-surface-1)`, `bg-surface-1`, `bg-brand-1`, `rounded-2`, …) — the older shadcn-style names (`--background`, `bg-brand`, `rounded-md`, …) remain as L1 aliases and continue to work. No hex literals in app code; check `packages/ui/tokens/hooks-index.json` for available tokens + deprecations (163 tokens · 36 namespaces). ESLint catches violations via `exxat-ds/no-hex-color` + `exxat-ds/no-deprecated-tokens` (plugin: **`@exxatdesignux/eslint-plugin`**).
|
|
42
|
+
25. **Before** adopting any pattern from a **sibling Salesforce / SLDS / LWC** repo (e.g. `design-system-2-starter-kit`), read **`.cursor/rules/exxat-no-slds-leakage.mdc`**. **No** `slds-*` classes, **no** `<lightning-*>` markup, **no** synthetic-shadow assumptions, **no** SLDS token names (`--slds-g-color-*`). Document architectural ideas as Exxat blueprints (`docs/blueprints/`) instead of copying SLDS code.
|
|
43
|
+
|
|
44
|
+
**Longer narrative and architecture:** `docs/data-views-pattern.md`, `docs/drawer-vs-dialog-pattern.md`, `docs/card-vs-rows-pattern.md`, `docs/kpi-strip-max-four-pattern.md`, **`docs/kpi-flat-band-pattern.md`**, **`docs/shell-surface-elevation-pattern.md`**, `docs/command-menu-pattern.md`, `docs/collaboration-access-pattern.md` (keep in sync with this handbook for big refactors).
|
|
42
45
|
|
|
43
46
|
---
|
|
44
47
|
|
|
@@ -46,7 +49,7 @@ Cross-cutting Cursor rules also live in the repo root `.cursor/rules/` (data tab
|
|
|
46
49
|
|
|
47
50
|
1. **User / task instructions** in the current session (highest).
|
|
48
51
|
2. This **`AGENTS.md`** for Exxat DS product patterns.
|
|
49
|
-
3. **`.cursor/rules/*.mdc`** at repo root (`exxat-data-tables`, `exxat-list-page-connected-views`, `exxat-centralized-list-dataset`, `exxat-list-page-view-shells`, `exxat-table-properties-drawer`, `exxat-board-cards`, `exxat-page-vs-drawer`, `exxat-drawer-vs-dialog`, `exxat-card-vs-list-rows`, `exxat-kpi-max-four`, `exxat-reuse-before-custom`, `exxat-no-toast`, `exxat-kbd-shortcuts`, `exxat-accessibility`, `exxat-fontawesome-icons`, **`exxat-mono-ids`**, `exxat-primary-nav-secondary-panel`, `exxat-collaboration-access`, `exxat-ds-agents`) and any rules under **`
|
|
52
|
+
3. **`.cursor/rules/*.mdc`** at repo root (`exxat-data-tables`, `exxat-list-page-connected-views`, `exxat-centralized-list-dataset`, `exxat-list-page-view-shells`, `exxat-table-properties-drawer`, `exxat-board-cards`, `exxat-page-vs-drawer`, `exxat-drawer-vs-dialog`, `exxat-card-vs-list-rows`, `exxat-kpi-max-four`, `exxat-reuse-before-custom`, `exxat-no-toast`, `exxat-kbd-shortcuts`, `exxat-accessibility`, `exxat-fontawesome-icons`, **`exxat-mono-ids`**, `exxat-primary-nav-secondary-panel`, `exxat-collaboration-access`, `exxat-ds-agents`) and any rules under **`apps/web/.cursor/rules/`** (including **`exxat-dashboard-view-charts`** for Data view charts).
|
|
50
53
|
4. Project **skills** under `.cursor/skills/` when relevant — e.g. **shadcn**, **exxat-accessibility** (WCAG / ARIA / touch / contrast), **exxat-board-cards** (kanban card shell, status badges, primitives), **exxat-list-page-view-shells** (centered view bodies, **`ListPageViewFrame`**), **exxat-centralized-list-dataset** (one **`useTableState`** row bag + shared maps across all list-hub views and **`TablePropertiesDrawer`**), **exxat-collaboration-access** (face rail + invite sheet + library access), **exxat-dedicated-search-surfaces** (landing vs results split, **`DedicatedSearch*`** templates + recents without hydration drift), **exxat-drawer-vs-dialog**, **exxat-card-vs-list-rows**, **exxat-kpi-max-four**, **`exxat-mono-ids`** (monospace system identifiers).
|
|
51
54
|
|
|
52
55
|
If two documents conflict, prefer the **more specific** rule for the file type, then **newer** team decisions captured in `AGENTS.md`.
|
|
@@ -55,7 +58,7 @@ If two documents conflict, prefer the **more specific** rule for the file type,
|
|
|
55
58
|
|
|
56
59
|
## 3. Data tables (product lists)
|
|
57
60
|
|
|
58
|
-
**MUST** for any screen that is a **browsable, filterable grid of records** (
|
|
61
|
+
**MUST** for any screen that is a **browsable, filterable grid of records** (directories, tokens, library items, columns showcase, etc.):
|
|
59
62
|
|
|
60
63
|
| Requirement | Action |
|
|
61
64
|
|-------------|--------|
|
|
@@ -64,7 +67,9 @@ If two documents conflict, prefer the **more specific** rule for the file type,
|
|
|
64
67
|
| Filters | Use the **shared filter model** (`FilterFieldDef`, operators, chips) consistent with existing list pages. |
|
|
65
68
|
| Table properties | Expose **Table properties** via **`TablePropertiesDrawer`** (`@/components/table-properties`) — columns, density, related options — same pattern as Placements / data list. |
|
|
66
69
|
|
|
67
|
-
**Reference:** `components/
|
|
70
|
+
**Reference:** `components/library-table.tsx`, `components/columns-showcase.tsx`, `components/data-table/`.
|
|
71
|
+
|
|
72
|
+
**Scaling past ~2K rows:** Turn on **`pagination={true}`** on **`HubTable`** and pick a page size; lists + boards already auto-virtualize at 100 rows. Beyond ~50K, lift filter / sort / page state via **`paginationOverride`** and fetch one page at a time — full upgrade path in **`docs/large-dataset-strategy.md`**.
|
|
68
73
|
|
|
69
74
|
**MUST NOT:** Build product list pages with only `@/components/ui/table`, raw `<table>`, or a third-party grid that bypasses this stack.
|
|
70
75
|
|
|
@@ -76,7 +81,7 @@ If two documents conflict, prefer the **more specific** rule for the file type,
|
|
|
76
81
|
|
|
77
82
|
**MUST:** If the main surface is a **`DataTable`** (or equivalent data grid), wrap it in **`ListPageTemplate`** so the **views toolbar** exists (tabs, add view, per-tab settings). Do **not** place `DataTable` only under `PageHeader` without the tab shell.
|
|
78
83
|
|
|
79
|
-
**Reference implementations:** `components/
|
|
84
|
+
**Reference implementations:** `components/library-hub-client.tsx` (full hub), `components/columns-showcase.tsx` (single-view catalog), `components/tokens-themes-client.tsx` (hub + secondary panel).
|
|
80
85
|
|
|
81
86
|
**Rationale:** Consistent navigation, saved views, per-tab view type (table / list / board / dashboard), export at template level.
|
|
82
87
|
|
|
@@ -86,7 +91,7 @@ If two documents conflict, prefer the **more specific** rule for the file type,
|
|
|
86
91
|
|
|
87
92
|
**MUST NOT** ship a **new primary nav hub** as an **empty or placeholder-only page** (e.g. a paragraph saying “replace this later” with no **`DataTable`**, mock data, or connected views). When a route is linked from **`lib/mock/navigation.tsx`**, land users on the same **hub stack** as Team / Placements: **`ListPageTemplate`** + typed mock rows (typically **≥ ~12**), search, filters, **`TablePropertiesDrawer`**, and all view tabs the template supports (**§4.1**), unless the product explicitly scopes a route as a non-data shell (rare).
|
|
88
93
|
|
|
89
|
-
**Mock data:** Put typed row arrays in **`lib/mock/<entity>.ts`**. Add **`lib/mock/<entity>-kpi.ts`** (or colocated helpers) with pure functions **`entityKpiMetrics(rows)`** / **`entityKpiInsight(rows)`** returning **`MetricItem[]`** / **`MetricInsight`** for **`KeyMetrics`**. Each **`MetricItem`** must set **`trend`** to match the signed change; use **`trendPolarity`** when an increase is **not** favorable (defects, review flags, overdue — see **`docs/kpi-trend-pattern.md`** and **`.cursor/rules/exxat-kpi-trends.mdc`**). **`entityKpiMetrics`** for **`ListPageTemplate`** metrics and Data-tab key-metrics cards: return **at most four** **`MetricItem`** — **`docs/kpi-strip-max-four-pattern.md`**, **`lib/dashboard-layout-merge.ts`** (`KEY_METRICS_KPI_COUNT_MAX`), **`.cursor/rules/exxat-kpi-max-four.mdc`**. The page client passes full mock rows into one table component; KPI helpers receive **`tableState.rows`** inside that component so search/filters apply to list, board, dashboard, and table together.
|
|
94
|
+
**Mock data:** Put typed row arrays in **`lib/mock/<entity>.ts`**. Add **`lib/mock/<entity>-kpi.ts`** (or colocated helpers) with pure functions **`entityKpiMetrics(rows)`** / **`entityKpiInsight(rows)`** returning **`MetricItem[]`** / **`MetricInsight`** for **`KeyMetrics`**. Each **`MetricItem`** must set **`trend`** to match the signed change; use **`trendPolarity`** when an increase is **not** favorable (defects, review flags, overdue — see **`docs/kpi-trend-pattern.md`** and **`.cursor/rules/exxat-kpi-trends.mdc`**). **`delta`** is the **count change** that renders next to the arrow (e.g. `"+5"`, `"-3"`); contextual prose like `"left + right"` / `"vs last week"` goes in **`MetricItem.description`** (caption below the value). When there is no direction *and* no count, leave **`delta: ""`** + **`trend: "neutral"`** — **`KeyMetrics`** hides the chip; **MUST NOT** hand-roll a `—` placeholder. **`entityKpiMetrics`** for **`ListPageTemplate`** metrics and Data-tab key-metrics cards: return **at most four** **`MetricItem`** — **`docs/kpi-strip-max-four-pattern.md`**, **`lib/dashboard-layout-merge.ts`** (`KEY_METRICS_KPI_COUNT_MAX`), **`.cursor/rules/exxat-kpi-max-four.mdc`**. The page client passes full mock rows into one table component; KPI helpers receive **`tableState.rows`** inside that component so search/filters apply to list, board, dashboard, and table together.
|
|
90
95
|
|
|
91
96
|
**Centralized dataset (rows + table properties + alternate views):** **MUST** use one **`useTableState`** row bag for the **`DataTable`**, **`TablePropertiesDrawer`** (columns/density on **that** table), and **every** record-bearing **`DataListViewType`** — **folder**, **panel**, **tree**, etc. — via **`tableState.rows`**. **MUST NOT** import a second **`lib/mock/<entity>`** array into a view-only module while the grid filters state; **MUST NOT** fork a duplicate row type for inspectors. Shared **properties**: tab labels **`DATA_LIST_VIEW_TILES`** (`lib/data-list-view.ts`), status **`lib/list-status-badges.ts`**, KPI helpers from **`tableState.rows`**. **Presentation:** non-table bodies use **`ListPageViewFrame`** and **`components/data-views/`** primitives fed by the **same** **`tableState.rows`** (**§4.5**). **Rule + skill:** **`.cursor/rules/exxat-centralized-list-dataset.mdc`**, **`.cursor/skills/exxat-centralized-list-dataset/SKILL.md`**.
|
|
92
97
|
|
|
@@ -94,19 +99,6 @@ If two documents conflict, prefer the **more specific** rule for the file type,
|
|
|
94
99
|
|
|
95
100
|
**Details:** `docs/data-views-pattern.md` (mock data, connected views, dashboard view).
|
|
96
101
|
|
|
97
|
-
### 4.1.1 View registry + `ListPageConnectedViewBody` (required for new hubs)
|
|
98
|
-
|
|
99
|
-
**MUST** route view bodies through the shared registry — **MUST NOT** use `if (view === "board")` chains with a dashboard fallback.
|
|
100
|
-
|
|
101
|
-
| Step | Action |
|
|
102
|
-
|------|--------|
|
|
103
|
-
| **Register** | Add the tile in **`lib/data-list-view.ts`**; capabilities derive in **`lib/data-list-view-registry.ts`** (`renderKind`, `showsListPageHubMetricsStrip`). |
|
|
104
|
-
| **Declare hub** | **`lib/<hub>-supported-views.ts`** — pass the same array to **`ListPageTemplate`** (`supportedViewTypes`) and **`TablePropertiesDrawerButton`** (`supportedViewTypes`). |
|
|
105
|
-
| **Render** | Hub **`*-table.tsx`** returns **`ListPageConnectedViewBody`** with **`defineHubViewRenderers(supportedViewTypes, { … })`** — one entry per **`DataListViewRenderKind`**; dev warns on gaps; missing kinds show **`ListPageViewNotConfigured`** (never silent dashboard). |
|
|
106
|
-
| **Bodies** | Generic UI in **`components/data-views/`** (`ListPageCalendarView`, `ListPageBoardTemplate`, `ListPageFolderColumnsPanel`, `FolderGridView`, …); entity wiring in thin hub wrappers (e.g. **`QuestionBankFolderColumnsPanel`**). **MUST NOT** export QB-only tree branches from the generic **`data-views`** barrel. |
|
|
107
|
-
|
|
108
|
-
**Golden references (this app):** `components/list-hub-table.tsx`, `components/question-bank-table.tsx`. **Template** (`packages/ui/template/`): same list-hub + question-bank stack — keep in sync when publishing **`@exxatdesignux/ui`**.
|
|
109
|
-
|
|
110
102
|
### 4.2 `TablePropertiesDrawer` and the active view
|
|
111
103
|
|
|
112
104
|
**MUST:** Any page that uses **`ListPageTemplate`** with **`tab.viewType`** (table / list / board / dashboard) and renders **`TablePropertiesDrawer`** **MUST** pass:
|
|
@@ -115,11 +107,41 @@ If two documents conflict, prefer the **more specific** rule for the file type,
|
|
|
115
107
|
|------|--------|
|
|
116
108
|
| **`currentView`** | The same **`DataListViewType`** as the tab’s active view (e.g. **`view={tab.viewType}`** on the table component). |
|
|
117
109
|
| **`onViewChange`** | From **`renderContent={(tab, updateTab) => ...}`**: **`(v) => updateTab({ viewType: v, icon: dataListViewIcon(v) })`** — import **`dataListViewIcon`** from **`@/lib/data-list-view`**. |
|
|
118
|
-
| **`supportedViewTypes`** | Same **`lib/<hub>-supported-views.ts`** array as **`ListPageTemplate`** — limits Properties view tiles and **⌘1–9** Add-view shortcuts to views the hub implements. |
|
|
119
110
|
|
|
120
|
-
Thread **`view
|
|
111
|
+
Thread **`view`** and **`onViewChange`** from the **client** → **table / toolbar wrapper** → **`TablePropertiesDrawer`**. If **`currentView`** is omitted, the drawer defaults to **table** labels and controls even on **Board**, which is incorrect.
|
|
112
|
+
|
|
113
|
+
**Reference:** `components/library-table.tsx`, `components/columns-showcase.tsx`, `components/tokens-themes-client.tsx`. Root **`.cursor/rules/exxat-table-properties-drawer.mdc`**.
|
|
114
|
+
|
|
115
|
+
#### Deep-linking the drawer to a specific panel
|
|
116
|
+
|
|
117
|
+
`TablePropertiesDrawer` accepts an optional **`initialPanel`** prop so callsites can open the drawer focused on a named panel — `"main"` (default), `"table-display"`, `"filter"`, `"sort"`, `"group"`, `"columns"`, or `"conditional-rules"`. The current built-in use is the **Add Conditional Rule** item in every column header menu, which deep-links to the **Conditional rules** panel.
|
|
118
|
+
|
|
119
|
+
**MUST**:
|
|
121
120
|
|
|
122
|
-
|
|
121
|
+
1. `useTableState` exposes **`sheetInitialPanel`** + **`setSheetInitialPanel`** alongside `sheetOpen` / `setSheetOpen`. Pass **`initialPanel={sheetInitialPanel}`** to `TablePropertiesDrawer` from the toolbar / button wrapper. Reference: `packages/ui/src/components/table-properties/drawer-button.tsx`.
|
|
122
|
+
2. The toolbar **Properties** button MUST clear the deep-link before opening so a plain "open Properties" click always lands on the main index panel:
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
onClick={() => {
|
|
126
|
+
setSheetInitialPanel?.(null)
|
|
127
|
+
setSheetOpen(true)
|
|
128
|
+
}}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
3. Deep-link callsites MUST set panel + open in **the same batched setState call** so the drawer mounts with the right panel in one render:
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
setSheetInitialPanel("conditional-rules")
|
|
135
|
+
setSheetOpen(true)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
4. From inside a Radix `DropdownMenu`, queue the work into `onCloseAutoFocus` (see `columnMenuPendingActionRef` in `packages/ui/src/components/data-table/index.tsx`) — opening the non-modal Sheet synchronously from `onSelect` races with the menu close cycle.
|
|
139
|
+
|
|
140
|
+
**MUST NOT**: introduce a second source of truth for "which panel" — `sheetInitialPanel` is the only deep-link channel; the drawer's internal `sheetPanel` stays internal.
|
|
141
|
+
|
|
142
|
+
#### View-type tile grid
|
|
143
|
+
|
|
144
|
+
The drawer's "View type" tile grid (and the Export drawer's "File format" grid) renders through `SelectionTileGrid` with `interaction="button"` + `labelPlacement="inside"`. The shared `selectionTileClassNames` utility applies **`aspect-square`** + `leading-tight` so every tile is the same uniform square regardless of how many tiles populate the last row of the `grid-cols-N` track. **Prefer** the shared `SelectionTileGrid` (or `selectionTileClassNames` directly) instead of inventing flex/grid wrappers for tile-style pickers — that's the only way to keep the squares uniform across the system.
|
|
123
145
|
|
|
124
146
|
### 4.3 Data view dashboard — charts, customisation, and parity with the gallery
|
|
125
147
|
|
|
@@ -128,15 +150,15 @@ Thread **`view`**, **`onViewChange`**, and **`supportedViewTypes`** from the **c
|
|
|
128
150
|
| Topic | Rule |
|
|
129
151
|
|-------|------|
|
|
130
152
|
| **Accessibility** | Each chart uses **`ChartFigure`** (keyboard + live region) and **`ChartDataTable`** (`sr-only` table fallback), inside **`ChartCard`** — same stack as **`charts-overview.tsx`**. **MUST NOT** ship bare Recharts-only charts on these surfaces. |
|
|
131
|
-
| **Two “dashboard” surfaces** | The **`/dashboard`** route uses **`DashboardTabs`** + **`ChartsOverview`** (gallery / demos). The **Data** tab on
|
|
153
|
+
| **Two “dashboard” surfaces** | The **`/dashboard`** route uses **`DashboardTabs`** + **`ChartsOverview`** (gallery / demos). The **Data** tab on a hub uses a hub-specific `*DashboardChartsSection` (reference: **`LibraryDashboardChartsSection`** in **`components/library-dashboard-charts.tsx`**, wired from **`library-table.tsx`**). Both share **`ChartFigure`**, **`ChartCard`**, and **`useChartVariant()`**; they are **not** duplicate chart engines — product charts belong in the shared components above. |
|
|
132
154
|
| **Keyboard selection (bars & pies)** | Match the **`/dashboard` gallery**: use **`CHART_KBD_ACTIVE_BAR`** and **`CHART_KBD_ACTIVE_PIE_SHAPE`** from **`@/lib/chart-keyboard-selection`** with Recharts **`activeBar` + `activeIndex`** on **`Bar`** and **`activeShape` + `activeIndex`** on **`Pie`**. **MUST NOT** rely on **`fillOpacity` dimming alone** on **`Cell`** as the only keyboard-selected state — it diverges from the gallery and from WCAG-aligned focus feedback. |
|
|
133
|
-
| **Customise UI** | Toggle **Edit layout** on the hub dashboard toolbar
|
|
155
|
+
| **Customise UI** | Toggle **Edit layout** on the hub dashboard toolbar inside **`PlacementsTable`** / **`TeamTable`** / **`ComplianceTable`** (the dashboard tab body renders `*DashboardChartsSection` directly — there is no shared `DashboardShell` wrapper). **`layoutEditMode`** shows on-canvas drag reorder, remove, width (half / full width), chart type, add chart, reset — **no** separate Sheet for layout. Target for coach marks: **`[aria-label='Edit dashboard layout']`**. |
|
|
134
156
|
| **Toolbar in edit mode** | Do **not** render **`DataTableToolbar`** while **`layoutEditMode`** — hides search, filters, **Properties**, and the edit affordance in one row. Canvas **Done** / **Cancel** / **Reset** stay on the charts section. |
|
|
135
157
|
| **Key metrics card** | Dashboard **`key-metrics`** uses **`KeyMetrics`** **`variant="card"`** (not **`flat`**). Users choose how many KPIs to show (**1–4**) via the canvas control in edit mode; persist **`keyMetricsKpiCount`** in the same layout object. Half-width (**span 1**) sets **`metricsHalfWidthLayout`**. |
|
|
136
158
|
| **Data wiring** | **`PlacementsDashboardChartsSection`** (and Team / Compliance equivalents) **MUST** receive **`cardSpans`** and **`cardChartTypes`** (or rely on defaults **inside** the component). **MUST NOT** omit them without defaults — runtime crash (`undefined[id]`). |
|
|
137
|
-
| **Persistence (centralized)** |
|
|
159
|
+
| **Persistence (centralized)** | Hub dashboard layouts share one bundle: **`lib/data-view-dashboard-storage.ts`** (key **`exxat-ds:data-view-dashboards:v1`**). Each hub registers under a string scope (e.g. **`library`**); the generic API is **`loadDataViewLayout`** / **`saveDataViewLayout`** + **`mergeDashboardLayoutGeneric`** (`lib/dashboard-layout-merge.ts`) for default-layout safety. **MUST NOT** add a sibling `localStorage` key for the same layout shape without extending this module. |
|
|
138
160
|
|
|
139
|
-
**Reference:** `components/
|
|
161
|
+
**Reference:** `components/library-dashboard-charts.tsx` (`LibraryDashboardChartsSection`), `components/library-table.tsx` (dashboard tab body wires the section + edit toolbar inline), `components/charts-overview.tsx` (full-page gallery), `lib/chart-keyboard-selection.ts`, `lib/data-view-dashboard-storage.ts`, **`apps/web/.cursor/rules/exxat-dashboard-view-charts.mdc`**.
|
|
140
162
|
|
|
141
163
|
### 4.4 Board cards (kanban)
|
|
142
164
|
|
|
@@ -148,10 +170,9 @@ Thread **`view`**, **`onViewChange`**, and **`supportedViewTypes`** from the **c
|
|
|
148
170
|
| **Information hierarchy** | **(1)** **`ListPageBoardCardTitleRow`** — title + optional **`ListPageBoardCardAvatar`** (`trailing`). **(2)** **`ListPageBoardCardBadgeRow`** — status / tags as **`Badge`** chips when the entity has a status (not raw body text for status). **(3)** **`ListPageBoardCardBody`** — facts via **`BoardCardTwoLineBlock`** and/or **`BoardCardIconRow`** from **`board-card-primitives.tsx`**. **(4)** Optional **`ListPageBoardCardSecondary`** for empty-state hints. |
|
|
149
171
|
| **Facts rows** | Prefer **`BoardCardTwoLineBlock`** (icon + primary line + optional secondary line) so rows match Placements. **`line2`** may be omitted for a single-line fact. Use **`BoardCardIconRow`** when the cell mirrors **`ColumnDef` cell renderers** (e.g. Placements). |
|
|
150
172
|
| **Avatar** | Use **`ListPageBoardCardAvatar`** with entity **`initials`** when present; otherwise derive with **`initialsFromDisplayName`** from **`lib/initials-from-name.ts`** (e.g. compliance owner name). |
|
|
151
|
-
| **Status labels + colors** | **All list hubs**
|
|
152
|
-
| **Placements-specific** | **`BoardPlacementCard`** may keep domain logic (lifecycle tabs, conditional row background, **`TablePropertiesDrawer`** column wiring); it still composes **`ListPageBoardCard`** parts and primitives. **Placements** status uses **`StatusBadge`** in **`components/data-list-table-cells.tsx`**, which wraps **`ListHubStatusBadge`** with **`PLACEMENT_STATUS_*`** maps in **`lib/list-status-badges.ts`** (same system as other hubs). |
|
|
173
|
+
| **Status labels + colors** | **All list hubs** **MUST** render status chips with **`ListHubStatusBadge`** and compose per-domain label / tint / icon maps next to the entity's mock data (reference: `lib/mock/library.ts` + `components/library-board-view.tsx`). Generic semantic tints live in **`lib/list-status-badges.ts`** (**`LIST_HUB_STATUS_TINT_SUCCESS / WARNING / INFO / NEUTRAL / DANGER`**). **`surface="table"`** for **`DataTable`** / **list** rows; **`surface="board"`** in **`ListPageBoardCardBadgeRow`**. **SHOULD** map domain statuses onto **`LIST_HUB_STATUS_TINT_*`** before inventing new palettes. **MUST NOT** duplicate the same tint table across feature files or add **`uppercase`** / **`tracking-wide`**. |
|
|
153
174
|
|
|
154
|
-
**Reference:** **`components/
|
|
175
|
+
**Reference:** **`components/library-board-view.tsx`**, **`components/list-hub-status-badge.tsx`**, **`lib/list-status-badges.ts`** (generic tints), **`components/data-views/list-page-board-template.tsx`**, **`@exxatdesignux/ui/components/list-page-board-card`** (primitive). **Skill (Cursor + Claude):** **`.cursor/skills/exxat-board-cards/SKILL.md`** and **`.claude/skills/exxat-board-cards/SKILL.md`**.
|
|
155
176
|
|
|
156
177
|
### 4.5 View layout shells (centered, reusable, not page-tied)
|
|
157
178
|
|
|
@@ -160,7 +181,7 @@ Thread **`view`**, **`onViewChange`**, and **`supportedViewTypes`** from the **c
|
|
|
160
181
|
| Topic | Rule |
|
|
161
182
|
|-------|------|
|
|
162
183
|
| **Folder / icon / panel-style views** | Compose **`FolderGridView`**, **`FinderPanelView`**, or another **`data-views/`** primitive; those shells **should** use **`ListPageViewFrame`** internally or as a direct wrapper so every hub gets the same rhythm. |
|
|
163
|
-
| **New view types** | Implement the **generic** grid/shell under **`components/data-views/`** with render props; **`TeamTable`** / **`
|
|
184
|
+
| **New view types** | Implement the **generic** grid/shell under **`components/data-views/`** with render props; **`TeamTable`** / **`LibraryTable`** **MUST** only wire data + callbacks — **MUST NOT** own one-off full-page grid markup that another entity would duplicate. |
|
|
164
185
|
| **`DataTable` branch** | **MUST NOT** wrap **`DataTable`** in **`ListPageViewFrame`** when it would **stack** horizontal inset with the table toolbar (**§5**). |
|
|
165
186
|
|
|
166
187
|
**Cursor rule:** **`.cursor/rules/exxat-list-page-view-shells.mdc`**. **Skill:** **`.cursor/skills/exxat-list-page-view-shells/SKILL.md`**.
|
|
@@ -173,14 +194,14 @@ Thread **`view`**, **`onViewChange`**, and **`supportedViewTypes`** from the **c
|
|
|
173
194
|
|
|
174
195
|
| Step | Action |
|
|
175
196
|
|------|--------|
|
|
176
|
-
| **Nav** | Set **`secondaryPanel`** on the **`NavLinkItem`** in **`lib/mock/navigation.tsx`** to a **stable id** (e.g. **`"
|
|
197
|
+
| **Nav** | Set **`secondaryPanel`** on the **`NavLinkItem`** in **`lib/mock/navigation.tsx`** to a **stable id** (e.g. **`"library"`**). **`url`** stays the **hub route**. |
|
|
177
198
|
| **Registry** | Map that id in **`PANELS`** inside **`components/secondary-panel.tsx`** to a component that renders **panel header** + **secondary nav** UI. |
|
|
178
|
-
| **Route** | Mount **`*PanelActivator`** on the hub that calls **`useAutoPanel(id)`** with the **same id** so the panel opens while the route is mounted (see **`
|
|
179
|
-
| **Data** | Keep using **one** **`useTableState`** row bag; **scope** filters via **URL** + shared helpers (**`lib/
|
|
199
|
+
| **Route** | Mount **`*PanelActivator`** on the hub that calls **`useAutoPanel(id)`** with the **same id** so the panel opens while the route is mounted (see **`LibraryPanelActivator`** on **`LibraryClient`**). |
|
|
200
|
+
| **Data** | Keep using **one** **`useTableState`** row bag; **scope** filters via **URL** + shared helpers (**`lib/library-nav.ts`**) so **refresh** and **links** match the panel — **`.cursor/rules/exxat-centralized-list-dataset.mdc`**. |
|
|
180
201
|
|
|
181
202
|
**MUST NOT:** Set **`secondaryPanel`** without **`PANELS[id]`** and **`useAutoPanel`** — users see **collapsed** main nav with **no** panel body.
|
|
182
203
|
|
|
183
|
-
**Folder-scoped library (
|
|
204
|
+
**Folder-scoped library (Library):** When the URL is scoped to a folder (**`scope === "folder"`** + **`folderId`** via **`lib/library-nav.ts`**), the hub **`LibraryPageHeader`** **⋯ More** menu **MUST** include **Customize folder** and open **`LibraryNewFolderSheet`** from the **hub client** so the action works on **every** **`ListPageTemplate`** view tab — not only inside **`LibraryTable`** branches that mount their own sheet. **Pattern:** **`docs/library-hub-header-pattern.md`**. **Cursor rule:** **`.cursor/rules/exxat-library-hub-header.mdc`**.
|
|
184
205
|
|
|
185
206
|
**Surface elevation:** Secondary panel = **level 1** between primary sidebar (**`--sidebar`**, level 0) and page canvas (**`--background`**, level 2). **`NestedSecondaryPanelShell`** uses **`bg-[var(--secondary-panel-bg)]`** — OKLCH mix from **`--brand-tint*`** per active product (**One** indigo, **Prism** rose, **`theme-custom`** when accent differs from default). **MUST NOT** set panel to **`bg-sidebar`** or a fixed rose fill for all products. **`docs/shell-surface-elevation-pattern.md`**.
|
|
186
207
|
|
|
@@ -203,7 +224,7 @@ Thread **`view`**, **`onViewChange`**, and **`supportedViewTypes`** from the **c
|
|
|
203
224
|
|
|
204
225
|
**MUST NOT:** **`Select`** in **`InputGroupAddon`** without **`InputGroupInput`** / **`SelectGroup`**; per-person cards in the roster; a second invite control **beside** an existing face rail.
|
|
205
226
|
|
|
206
|
-
**Narrative:** **`docs/collaboration-access-pattern.md`**. **Cursor rule:** **`.cursor/rules/exxat-collaboration-access.mdc`**. **Skill:** **`.cursor/skills/exxat-collaboration-access/SKILL.md`**. **Reference:**
|
|
227
|
+
**Narrative:** **`docs/collaboration-access-pattern.md`**. **Cursor rule:** **`.cursor/rules/exxat-collaboration-access.mdc`**. **Skill:** **`.cursor/skills/exxat-collaboration-access/SKILL.md`**. **Reference:** Library header + client + **`InviteCollaboratorsDrawer`**.
|
|
207
228
|
|
|
208
229
|
### 4.8 Dedicated search (landing vs results)
|
|
209
230
|
|
|
@@ -242,7 +263,7 @@ Below the threshold, these MAY be omitted unless the page is a **primary hub** (
|
|
|
242
263
|
|
|
243
264
|
Match **Placements**:
|
|
244
265
|
|
|
245
|
-
- **Primary CTA:** one **default (filled)** `Button`, often `size="lg"` — e.g. New
|
|
266
|
+
- **Primary CTA:** one **default (filled)** `Button`, often `size="lg"` — e.g. "New question", "Invite collaborator", "Add token". **MUST NOT** use `variant="outline"` for that primary action.
|
|
246
267
|
- **More (⋯):** outline **icon** button → menu including **Export** → **`ExportDrawer`** (or same pattern).
|
|
247
268
|
|
|
248
269
|
**Subtitle:** Short line with **count + freshness** (e.g. `24 records · Last updated now`) when useful — see `PlacementsPageHeader` / `TeamPageHeader`.
|
|
@@ -251,7 +272,7 @@ Match **Placements**:
|
|
|
251
272
|
|
|
252
273
|
**Primary nav destinations** that show **large or highly interactive** datasets **MUST** use the **primary page template**:
|
|
253
274
|
|
|
254
|
-
- **`ListPageTemplate`** + **`KeyMetrics`** (when metrics apply) + export wiring + the same **client composition** as **`
|
|
275
|
+
- **`ListPageTemplate`** + **`KeyMetrics`** (when metrics apply) + export wiring + the same **client composition** as **`PlacementsClient`** / **`TeamClient`** — not a minimal `PageHeader`-only layout for that hub.
|
|
255
276
|
|
|
256
277
|
**MUST NOT** treat a main hub table page as a “light” sub-section: use the same shell as Placements (tabs, optional metrics strip, template-level export).
|
|
257
278
|
|
|
@@ -266,9 +287,7 @@ Match **Placements**:
|
|
|
266
287
|
|
|
267
288
|
**Rationale:** Drawers preserve **spatial context**; dialogs enforce **focus**; full pages avoid cramming complex work into overlays.
|
|
268
289
|
|
|
269
|
-
**Details:** `docs/data-views-pattern.md` (Page vs drawer), **`docs/drawer-vs-dialog-pattern.md`** (drawer vs modal on the same route)
|
|
270
|
-
|
|
271
|
-
**Focused workflow routes:** Primary / long / multi-step / sectioned settings on an **own URL** → **`FocusedWorkflowPageTemplate`** (§14 checklist) — **not** **`ListPageTemplate`**, **not** Miller-column explorers.
|
|
290
|
+
**Details:** `docs/data-views-pattern.md` (Page vs drawer), **`docs/drawer-vs-dialog-pattern.md`** (drawer vs modal on the same route). Root **`.cursor/rules/exxat-page-vs-drawer.mdc`**, **`.cursor/rules/exxat-drawer-vs-dialog.mdc`**.
|
|
272
291
|
|
|
273
292
|
### 6.5 Messaging — no toast
|
|
274
293
|
|
|
@@ -291,7 +310,7 @@ Follow root **`.cursor/rules/exxat-kbd-shortcuts.mdc`**. Summary:
|
|
|
291
310
|
|
|
292
311
|
### 7.1 Global command palette (⌘K)
|
|
293
312
|
|
|
294
|
-
**Product intent:** **`CommandMenu`** is **global search** and the primary **AI entry**—not a second nav tree. Config: **`buildCommandMenuConfig()`** in **`lib/command-menu-config.ts`**, provider in **`app/(app)/layout.tsx`**. Optional searchable rows (e.g.
|
|
313
|
+
**Product intent:** **`CommandMenu`** is **global search** and the primary **AI entry**—not a second nav tree. Config: **`buildCommandMenuConfig()`** in **`lib/command-menu-config.ts`**, provider in **`app/(app)/layout.tsx`**. Optional searchable rows (e.g. student names, question stems, record IDs) come from **`dataGroups`**, typically via **`getCommandMenuSearchDataGroups()`** in **`lib/command-menu-search-data.ts`**.
|
|
295
314
|
|
|
296
315
|
| SHOULD | Rationale |
|
|
297
316
|
|--------|-----------|
|
|
@@ -310,7 +329,7 @@ Follow root **`.cursor/rules/exxat-kbd-shortcuts.mdc`**. Summary:
|
|
|
310
329
|
|
|
311
330
|
**Standard:** **WCAG 2.1 Level AA** (and **2.2** where noted, e.g. target size).
|
|
312
331
|
|
|
313
|
-
**Authoritative detail (badges,
|
|
332
|
+
**Authoritative detail (badges, sidebar count colors, audit table):** **`.cursor/skills/exxat-accessibility/SKILL.md`** at the monorepo root (when the parent repo is open). If the skill path differs in your checkout, search for **`exxat-accessibility`**.
|
|
314
333
|
|
|
315
334
|
### 8.1 ARIA roles & structure (SC 1.3.1)
|
|
316
335
|
|
|
@@ -434,7 +453,7 @@ Keyboard shortcut hints rendered **inline inside a `Button`** (primary, secondar
|
|
|
434
453
|
<TooltipContent><span>Close</span><Kbd>Esc</Kbd></TooltipContent>
|
|
435
454
|
```
|
|
436
455
|
|
|
437
|
-
Reference: `components/new-
|
|
456
|
+
Reference: `components/new-library-item-form.tsx` (Next/Back buttons); full shortcut table in **`.cursor/rules/exxat-kbd-shortcuts.mdc`**.
|
|
438
457
|
|
|
439
458
|
---
|
|
440
459
|
|
|
@@ -442,24 +461,21 @@ Reference: `components/new-placement-form.tsx` (Next/Back buttons); full shortcu
|
|
|
442
461
|
|
|
443
462
|
| Need | Reuse | Where |
|
|
444
463
|
|------|--------|--------|
|
|
445
|
-
| View tabs + shell | `ListPageTemplate`
|
|
446
|
-
| Focused form / wizard / settings route | `FocusedWorkflowPageTemplate` + layouts in `focused-workflow-layouts.tsx` | `components/templates/focused-workflow-page-template.tsx` — **`docs/focused-workflow-page-pattern.md`** |
|
|
447
|
-
| View router | `ListPageConnectedViewBody`, `defineHubViewRenderers`, `data-list-view-registry` | `components/data-views/list-page-connected-view-body.tsx`, `lib/hub-connected-view-renderers.ts`, `lib/data-list-view-registry.ts` |
|
|
464
|
+
| View tabs + shell | `ListPageTemplate` | `components/templates/list-page.tsx` |
|
|
448
465
|
| Table + toolbar | `DataTable`, `DataTableToolbar`, `useTableState` | `components/data-table/` |
|
|
449
|
-
| Properties | `TablePropertiesDrawer` (+ **`currentView`** / **`onViewChange`**
|
|
450
|
-
|
|
|
451
|
-
|
|
|
452
|
-
|
|
|
453
|
-
| Dashboard view tab (KPIs + charts) | **`DashboardReportCharts`**; default **`ChartsOverview`** (placement demo). **Team** passes **`chartsSection`** (`TeamDashboardChartsSection`) so graphs match roster rows. KPIs from **`tableState.rows`** | `components/dashboard-report-charts.tsx`, `data-view-dashboard-charts-team.tsx`, `data-list-table.tsx` |
|
|
466
|
+
| Properties | `TablePropertiesDrawer` (+ **`currentView`** / **`onViewChange`** when using view tabs — §4.2) | `@/components/table-properties` |
|
|
467
|
+
| Full hub (table / board / dashboard) | `LibraryHubClient`, `LibraryTable` | `components/library-hub-client.tsx`, `components/library-table.tsx` |
|
|
468
|
+
| Single-view catalog hub | `ColumnsClient`, `ColumnsShowcase` | `components/columns-client.tsx`, `components/columns-showcase.tsx` |
|
|
469
|
+
| Dashboard view tab (KPIs + charts) | **`DashboardReportCharts`**; default **`ChartsOverview`** (full-page gallery). Hubs pass **`chartsSection`** with their own `*DashboardChartsSection` so graphs match the row set. KPIs from **`tableState.rows`**. | `components/dashboard-report-charts.tsx`, `components/library-dashboard-charts.tsx`, `components/library-table.tsx` |
|
|
454
470
|
| Data view layout + graph keyboard tokens | **`loadDataViewLayout` / `saveDataViewLayout`**, **`CHART_KBD_ACTIVE_BAR`**, **`CHART_KBD_ACTIVE_PIE_SHAPE`** | `lib/data-view-dashboard-storage.ts`, `lib/chart-keyboard-selection.ts` |
|
|
455
|
-
| Customize dashboard coach marks | Shared steps in **`lib/dashboard-customize-coach-mark.ts`**;
|
|
456
|
-
| Board columns (simple hubs) | **`ListPageBoardTemplate`** + **`ListPageBoardCard`** + primitives + **`lib/list-status-badges`** + **`ListHubStatusBadge`** (when applicable) | `components/data-views/list-page-board-template.tsx`, `list-hub-status-badge.tsx`, `
|
|
471
|
+
| Customize dashboard coach marks | Shared steps in **`lib/dashboard-customize-coach-mark.ts`**; one flow per hub scope (e.g. **`library-dashboard-customize`**) | `hooks/use-coach-mark.ts` (`enabled`, `dependsOnDismissedFlowId`), `library-table.tsx` |
|
|
472
|
+
| Board columns (simple hubs) | **`ListPageBoardTemplate`** + **`ListPageBoardCard`** + primitives + **`lib/list-status-badges`** + **`ListHubStatusBadge`** (when applicable) | `components/data-views/list-page-board-template.tsx`, `list-hub-status-badge.tsx`, `library-board-view.tsx`, **`§4.4`** |
|
|
457
473
|
| Full dashboard route | `DashboardTabs`, `KeyMetrics`, `ChartsOverview` | `app/(app)/dashboard/page.tsx`, `components/dashboard-tabs.tsx` |
|
|
458
|
-
| Board cards | **`ListPageBoardCard`** + primitives + entity card (**§4.4**) | `components/data-views/list-page-board-card.tsx`, `board-card-primitives.tsx`, `
|
|
474
|
+
| Board cards | **`ListPageBoardCard`** + primitives + entity card (**§4.4**) | `components/data-views/list-page-board-card.tsx`, `board-card-primitives.tsx`, `library-board-view.tsx` |
|
|
459
475
|
| **Application sidebar** (school/program, product, profile, child nav) | **`AppSidebar`**, **`TeamSwitcher`**, **`NavUser`**, collapsible + **popover** (icon rail) | `components/app-sidebar.tsx`, `nav-user.tsx`, `product-switcher.tsx`, `lib/mock/navigation.tsx`, `lib/logo-dev.ts`, `lib/stock-portrait.ts` — patterns in **exxat-ds-skill §3.1** |
|
|
460
|
-
| **Collaboration & access** (face rail + invite sheet) | **`PageHeader` `variant="collaboration"`**, **`InviteCollaboratorsDrawer`**, **`lib/collaborator-access.ts`** | `components/page-header.tsx`, `components/invite-collaborators-drawer.tsx`, `components/
|
|
476
|
+
| **Collaboration & access** (face rail + invite sheet) | **`PageHeader` `variant="collaboration"`**, **`InviteCollaboratorsDrawer`**, **`lib/collaborator-access.ts`** | `components/page-header.tsx`, `components/invite-collaborators-drawer.tsx`, `components/library-page-header.tsx`, `components/library-client.tsx`, **`§4.7`**, **`docs/collaboration-access-pattern.md`** |
|
|
461
477
|
| **Dedicated search** (empty `?q=` landing vs results) | **`DedicatedSearchLandingTemplate`**, **`DedicatedSearchUrlComposer`**, **`DedicatedSearchRecents`**, **`DedicatedSearchResultsHeaderChrome`**, **`lib/dedicated-search-recents.ts`** | **`§4.8`**, **`components/templates/dedicated-search-*`**, **`components/dedicated-search-*.tsx`** |
|
|
462
|
-
| Persistence (example) | Page + lifecycle keys | `lib/data-list-persistence.ts`, `
|
|
478
|
+
| Persistence (example) | Page + lifecycle keys | `lib/data-list-persistence.ts`, `PlacementsClient` / `PlacementsTable` |
|
|
463
479
|
| Coach marks / tours | `CoachMark`, `useCoachMark`, coach mark registry | `components/ui/coach-mark.tsx`, `hooks/use-coach-mark.ts`, `lib/coach-mark-registry.ts` |
|
|
464
480
|
| Settings page | Coach mark management | `app/(app)/settings/page.tsx`, `components/settings-client.tsx` |
|
|
465
481
|
|
|
@@ -484,8 +500,8 @@ Reference: `components/new-placement-form.tsx` (Next/Back buttons); full shortcu
|
|
|
484
500
|
|
|
485
501
|
## 10. Persistence (when copying Placements behavior)
|
|
486
502
|
|
|
487
|
-
- **Page-level:** tabs, `showMetrics`, `displayOptions`, `activeTabId` — see `lib/data-list-persistence.ts` and `
|
|
488
|
-
- **Per-lifecycle / tab:** sort, filters, columns, etc. — see `
|
|
503
|
+
- **Page-level:** tabs, `showMetrics`, `displayOptions`, `activeTabId` — see `lib/data-list-persistence.ts` and `PlacementsClient`.
|
|
504
|
+
- **Per-lifecycle / tab:** sort, filters, columns, etc. — see `PlacementsTable` and `scheduleLifecycleSave`.
|
|
489
505
|
|
|
490
506
|
New pages **SHOULD** namespace keys and version JSON (`v: 1`) for future migrations.
|
|
491
507
|
|
|
@@ -514,7 +530,7 @@ New pages **SHOULD** namespace keys and version JSON (`v: 1`) for future migrati
|
|
|
514
530
|
- **Brand-colored:** popover background is `bg-brand-deep text-white` — not `bg-popover`.
|
|
515
531
|
- **Persistent:** once completed/skipped, the flow is dismissed via `localStorage` and won't reshow until reset from Settings.
|
|
516
532
|
- **Settings page:** `/settings` lists all registered flows with reset/preview controls.
|
|
517
|
-
- **Sequencing / gating:** `useCoachMark` supports **`enabled`** (e.g. only when **`view === "dashboard"`**) and **`dependsOnDismissedFlowId`** (e.g. customize-dashboard after
|
|
533
|
+
- **Sequencing / gating:** `useCoachMark` supports **`enabled`** (e.g. only when **`view === "dashboard"`**) and **`dependsOnDismissedFlowId`** (e.g. customize-dashboard after a "views" tour completes). Completed flows dispatch **`COACH_MARK_FLOW_COMPLETED_EVENT`** on `window` so follow-up tours can open in the same tab.
|
|
518
534
|
- **Customize Data dashboard:** registered flows target **`[aria-label='Edit dashboard layout']`**; shared step copy lives in **`lib/dashboard-customize-coach-mark.ts`**.
|
|
519
535
|
|
|
520
536
|
### Variants
|
|
@@ -524,16 +540,15 @@ New pages **SHOULD** namespace keys and version JSON (`v: 1`) for future migrati
|
|
|
524
540
|
- **With image** — set `image` + `imageAlt` on the step
|
|
525
541
|
- **Without image** — text-only
|
|
526
542
|
|
|
527
|
-
**Reference:** `references/coach-marks.md` in the skill, `components/dashboard-tabs.tsx` (dashboard tour), `components/
|
|
543
|
+
**Reference:** `references/coach-marks.md` in the skill, `components/dashboard-tabs.tsx` (dashboard tour), `components/library-hub-client.tsx` (views tour).
|
|
528
544
|
|
|
529
545
|
---
|
|
530
546
|
|
|
531
547
|
## 12. Documentation
|
|
532
548
|
|
|
533
549
|
- **Deep dive:** `docs/data-views-pattern.md` (includes **Page vs drawer** with **§6.4**)
|
|
550
|
+
- **Scaling to 5K+ rows:** `docs/large-dataset-strategy.md` (client mode, pagination, server mode, virtualization follow-up)
|
|
534
551
|
- **Drawer vs dialog (same route):** `docs/drawer-vs-dialog-pattern.md` — **`.cursor/rules/exxat-drawer-vs-dialog.mdc`**
|
|
535
|
-
- **Focused workflow (form / wizard / settings route):** `docs/focused-workflow-page-pattern.md` — **`.cursor/rules/exxat-focused-workflow-page.mdc`**, **`.cursor/skills/exxat-focused-workflow-page/SKILL.md`**
|
|
536
|
-
- **Consumer app (npm):** `docs/consumer-app-pattern.md` — **`packages/ui/consumer-extras/AGENTS.md`**, **`.cursor/rules/exxat-consumer-app.mdc`**, **`.cursor/skills/exxat-consumer-app/SKILL.md`**
|
|
537
552
|
- **Cards vs table rows:** `docs/card-vs-rows-pattern.md` — **`.cursor/rules/exxat-card-vs-list-rows.mdc`**
|
|
538
553
|
- **KPI strip (max four tiles):** `docs/kpi-strip-max-four-pattern.md` — **`.cursor/rules/exxat-kpi-max-four.mdc`**
|
|
539
554
|
- **KPI flat band (list hubs):** `docs/kpi-flat-band-pattern.md` — **`.cursor/rules/exxat-kpi-flat-band.mdc`**
|
|
@@ -541,7 +556,38 @@ New pages **SHOULD** namespace keys and version JSON (`v: 1`) for future migrati
|
|
|
541
556
|
- **KPI deltas & trend arrows:** `docs/kpi-trend-pattern.md` (`MetricItem.trendPolarity`, `KeyMetrics`, chart mini-metrics)
|
|
542
557
|
- **Global command palette (⌘K):** `docs/command-menu-pattern.md`
|
|
543
558
|
- **No toast / snackbars:** **§6.5**, root **`.cursor/rules/exxat-no-toast.mdc`**
|
|
544
|
-
- **
|
|
559
|
+
- **Token taxonomy:** `docs/token-taxonomy.md` — namespaces, layering (L0 / L1 / L2 / L3), naming, deprecation policy. Machine-readable index at `packages/ui/tokens/hooks-index.json` (163 tokens · 36 namespaces · regenerate via `pnpm --filter @exxatdesignux/ui tokens:index`).
|
|
560
|
+
- **Exxat L0 canonical namespace:** `--exxat-color-surface-*`, `--exxat-color-ink-*`, `--exxat-color-brand-*`, `--exxat-radius-*`, `--exxat-spacing-*`, … (taxonomy §2.0; rollout: `docs/migrations/0002-exxat-token-namespace.md`).
|
|
561
|
+
- **Component selection guide:** `docs/component-selection-guide.md` — decision tree across the whole DS.
|
|
562
|
+
- **Blueprints:** `docs/blueprints/` — framework-agnostic specs. Start with `page-header.md` and `data-table.md`; add new ones via `_template.md`.
|
|
563
|
+
- **Migrations:** `docs/migrations/` — token rename + removal history. Open a new entry alongside every breaking change.
|
|
564
|
+
- **Components audit:** `docs/components-audit-2026-05.md` — observation log; §2.1 (form-layout / section-cards) and §2.2 (onboarding consolidation) both resolved 2026-05-19.
|
|
565
|
+
- **This handbook:** `apps/web/AGENTS.md` (keep checklist sections updated when patterns change)
|
|
566
|
+
|
|
567
|
+
### 12.1 Design-system meta-rules
|
|
568
|
+
|
|
569
|
+
| Concern | Rule | Enforcement |
|
|
570
|
+
|---|---|---|
|
|
571
|
+
| Salesforce / SLDS / LWC cross-contamination | **`.cursor/rules/exxat-no-slds-leakage.mdc`** | ESLint: `exxat-ds/no-slds-classes`, `exxat-ds/no-lightning-elements` |
|
|
572
|
+
| Hex literals, deprecated tokens, wrong token family | **`.cursor/rules/exxat-token-discipline.mdc`** | ESLint: `exxat-ds/no-hex-color`, `exxat-ds/no-deprecated-tokens` |
|
|
573
|
+
| No-toast feedback policy | **`.cursor/rules/exxat-no-toast.mdc`** (§6.5) | ESLint: `exxat-ds/no-sonner-toast` |
|
|
574
|
+
|
|
575
|
+
The ESLint rules ship as a **workspace package**:
|
|
576
|
+
**`packages/eslint-plugin-exxat-ds/`** (published as `@exxatdesignux/eslint-plugin`).
|
|
577
|
+
`apps/web/eslint.config.mjs` imports it via `import exxatDs from "@exxatdesignux/eslint-plugin"`
|
|
578
|
+
and consumers of `@exxatdesignux/ui` can install the plugin from npm with the
|
|
579
|
+
same wiring. See [`packages/eslint-plugin-exxat-ds/README.md`](../../packages/eslint-plugin-exxat-ds/README.md)
|
|
580
|
+
for rule docs and authoring guidance.
|
|
581
|
+
|
|
582
|
+
When you **introduce** a new token in `packages/ui/src/globals.css` (the canonical CSS — app/template `globals.css` are thin `@import` shells):
|
|
583
|
+
|
|
584
|
+
1. **Prefer L0** — declare the canonical name in the `Exxat L0 — canonical namespace`
|
|
585
|
+
block (`--exxat-color-<bucket>-<slot>` / `--exxat-radius-N` / `--exxat-spacing-N`).
|
|
586
|
+
2. Add a row to `docs/token-taxonomy.md` in the right § (§2.0 if L0; §2.1–§2.17 for legacy L1 surfaces).
|
|
587
|
+
3. Run `pnpm --filter @exxatdesignux/ui tokens:index` and commit the
|
|
588
|
+
regenerated `packages/ui/tokens/hooks-index.json`.
|
|
589
|
+
4. If you are **deprecating** a token, also open a numbered entry under
|
|
590
|
+
`docs/migrations/` (see `docs/migrations/README.md` template).
|
|
545
591
|
|
|
546
592
|
---
|
|
547
593
|
|
|
@@ -549,13 +595,13 @@ New pages **SHOULD** namespace keys and version JSON (`v: 1`) for future migrati
|
|
|
549
595
|
|
|
550
596
|
| MUST | MUST NOT |
|
|
551
597
|
|------|----------|
|
|
552
|
-
| Use `
|
|
598
|
+
| Use **`HubTable`** (from **`@/components/data-views`**) inside **`ListPageTemplate.renderContent`** — it wires `useTableState`, the **toolbar** (search + filter chips + filter dropdown + sort), and **`TablePropertiesDrawerButton`** in one place. Pass **`view`** / **`onViewChange`** through (§4.2); raw **`DataTable`** is fine **only** outside hubs (drawer/dialog mini-grids) | Mount raw **`<DataTable>`** in a hub or showcase — users lose filter chips and Properties; introduce a second table stack for the same surfaces; omit **`currentView`** on multi-view pages |
|
|
553
599
|
| Wrap main `DataTable` in `ListPageTemplate` | `DataTable` only under `PageHeader` without view tabs |
|
|
554
600
|
| Use primary template (`ListPageTemplate` + metrics + export pattern) for primary hubs with large data | Hub pages that look like “nested cards” with staggered margins |
|
|
555
601
|
| Match Placements for export + primary CTA + More menu | Outline button as the single primary CTA on exportable pages |
|
|
556
602
|
| Pair `Kbd` hints with real shortcuts | Browser-reserved chords for app actions |
|
|
557
603
|
| Global palette: **§7.1** — search + quick in-menu AI vs **Ask Leo**; **`dataGroups`** + **`searchOnly`** for bulky indexes | Palette as link-only dump; AI that belongs in **Ask Leo** forced into the palette; mounting full **`dataGroups`** on open when **`searchOnly`** should hide them |
|
|
558
|
-
| **§6.4** — **drawer** when **page context + quick** view/actions; **dialog** for **blocking** confirm/alert/short choice; **
|
|
604
|
+
| **§6.4** — **drawer** when **page context + quick** view/actions; **dialog** for **blocking** confirm/alert/short choice; **new page** for primary / long / own-URL flows | Forcing **full workflows** into a drawer when a route fits; using a **dialog** when users must **reference** the grid (prefer drawer); **routing** for tasks that are only quick glances over a hub |
|
|
559
605
|
| **KPI strips** — **≤ 4** `MetricItem` per **`KeyMetrics`** on template metrics + Data-tab key-metrics cards (**`KEY_METRICS_KPI_COUNT_MAX`**) | Fifth+ headline tile in the same strip; duplicate tiles to pad count |
|
|
560
606
|
| **Cards vs rows** — **DataTable** for dense comparable hubs; **`ListPageBoardCard`** / **`ListPageViewFrame`** when visual/kanban/folder — **`docs/card-vs-rows-pattern.md`** | Card walls for **50+** homogeneous records where the product expects **sort/filter/compare** without a deliberate UX exception |
|
|
561
607
|
| **Reuse before custom** — scan **`components/`** + **§9**; **ask the user** before new shared primitives or large bespoke widgets — **`exxat-reuse-before-custom.mdc`** | Parallel stacks; silent new “table” or metric systems when **`DataTable`** / **`KeyMetrics`** already apply |
|
|
@@ -565,9 +611,9 @@ New pages **SHOULD** namespace keys and version JSON (`v: 1`) for future migrati
|
|
|
565
611
|
| Data view charts: **`ChartFigure`** + **`ChartDataTable`**; keyboard highlight via **`chart-keyboard-selection`** (§4.3); layout via **`data-view-dashboard-storage`** | Ad-hoc `localStorage` keys for dashboard layout; opacity-only “selection” without `activeBar`/`activeShape` |
|
|
566
612
|
| Board cards: **`ListPageBoardCard`** shell; status via **`ListHubStatusBadge`** + **`lib/list-status-badges`**; no **`uppercase`** on status chips (§4.4) | One-off board card markup; status as plain body text; duplicated status maps outside **`list-status-badges`**; **empty placeholder** primary hubs (§4.1) |
|
|
567
613
|
| **§4.5** — Non-table view bodies use **`ListPageViewFrame`** (+ **`data-views/`** shells); new grids are generic components, not route-only markup | Duplicated `mx-4` / `max-w-*` per hub; wrapping **`DataTable`** so inset **doubles** (**§5**) |
|
|
568
|
-
| **§4.6** — **`secondaryPanel`** + **`PANELS`** + **`useAutoPanel`** together for nested scope nav; **folder URL scope** → header **⋯** **Customize folder** + client-mounted **`
|
|
614
|
+
| **§4.6** — **`secondaryPanel`** + **`PANELS`** + **`useAutoPanel`** together for nested scope nav; **folder URL scope** → header **⋯** **Customize folder** + client-mounted **`LibraryNewFolderSheet`** (**`exxat-library-hub-header.mdc`**) | **`secondaryPanel`** id with no panel component or activator; folder scope with customize **only** inside a single view tab’s subtree |
|
|
569
615
|
| **§4.7** — **`PageHeader` `variant="collaboration"`** + **`CollaborationAccessFlow`** / **`InviteCollaboratorsDrawer`**; empty **Add collaborator** + non-empty face rail; roster + invite from **`collaborator-access.ts`** | Extra invite beside a populated face rail; per-person roster cards; forked access enums; toast on invite |
|
|
570
|
-
| **§4.8** — **`DedicatedSearch*`** templates + composer + recents; **no** `localStorage` in **`useState`** initial paint; hub-specific **`patchSearchParams`** only | Forked `*
|
|
616
|
+
| **§4.8** — **`DedicatedSearch*`** templates + composer + recents; **no** `localStorage` in **`useState`** initial paint; hub-specific **`patchSearchParams`** only | Forked `*Library*SearchLanding*` shells for another entity; hydration mismatch on recents |
|
|
571
617
|
| **Font Awesome** — Kit in **`app/layout.tsx`**; **`fa-light` / `fa-solid`** conventions; **`aria-hidden`** on decorative **`<i>`**; run **`fa:subset-audit`** when adding glyphs (**`exxat-fontawesome-icons.mdc`**) | Parallel icon libraries for the same product chrome |
|
|
572
618
|
| **System IDs** — **`font-mono tabular-nums`** on question/record keys; mono **only** the ID token in mixed subtitles (**`exxat-mono-ids.mdc`**) | Mono on names, statuses, dates, or whole subtitle lines |
|
|
573
619
|
|
|
@@ -578,20 +624,20 @@ New pages **SHOULD** namespace keys and version JSON (`v: 1`) for future migrati
|
|
|
578
624
|
Copy and complete when implementing or reviewing:
|
|
579
625
|
|
|
580
626
|
- [ ] **Centralized dataset:** One **`useTableState`** / **`tableState.rows`** for **all** view tabs and inspectors; **TablePropertiesDrawer** on the **same** `DataTable`; **no** parallel mock arrays per view — **`.cursor/rules/exxat-centralized-list-dataset.mdc`**.
|
|
581
|
-
- [ ] **
|
|
627
|
+
- [ ] **Hub primitive (`HubTable`):** Every page that mounts a hub grid inside **`ListPageTemplate`** uses **`HubTable`** (from **`@/components/data-views`**), not raw **`<DataTable>`**. **`HubTable`** wires `useTableState`, the toolbar (search + filter chips + filter dropdown + sort), and **`TablePropertiesDrawerButton`** in one place; raw **`<DataTable>`** is reserved for tiny embedded grids (drawer/dialog body). When a column ships **`filter:`**, the chips appear automatically — **MUST NOT** add a parallel search/filter UI above the table — **`.cursor/rules/exxat-data-tables.mdc`**.
|
|
582
628
|
- [ ] **Reuse:** `ListPageTemplate`, `DataTable` / `useTableState`, `TablePropertiesDrawer` — no parallel bespoke tabs/filters. **New shared primitives:** **ask the user** after scanning **`components/`** + **§9** — **`.cursor/rules/exxat-reuse-before-custom.mdc`**.
|
|
583
629
|
- [ ] **Tabs:** Any main `DataTable` sits under `ListPageTemplate` with appropriate view tabs.
|
|
584
630
|
- [ ] **Inset:** No double horizontal padding around `DataTable`.
|
|
585
631
|
- [ ] **§4.5 View shells:** Folder / panel / icon views use **`ListPageViewFrame`** (or a **`data-views/`** component that uses it); no page-tied-only grid wrappers; **`DataTable`** not double-wrapped (**§5**).
|
|
586
632
|
- [ ] **> ~10 items:** Search, filter, sort, properties (per surface type in §6.1).
|
|
587
633
|
- [ ] **Exportable data:** Filled primary CTA; **⋯** menu with Export → `ExportDrawer`.
|
|
588
|
-
- [ ] **Primary hub + large data:** Same composition as `
|
|
634
|
+
- [ ] **Primary hub + large data:** Same composition as `PlacementsClient` / `TeamClient` (template + metrics when applicable).
|
|
589
635
|
- [ ] **All view tabs:** List/board/dashboard use **`tableState.rows`**; dashboard view uses **`KeyMetrics`** + shared KPI helpers — no “not wired” placeholders or duplicate metric cards.
|
|
590
636
|
- [ ] **Properties drawer:** **`TablePropertiesDrawer`** receives **`currentView`** and **`onViewChange`** from **`renderContent`** / **`updateTab`** + **`dataListViewIcon`** (§4.2) — not table-only copy on Board/List/Dashboard.
|
|
591
637
|
- [ ] **Data view dashboard (Placements / Team / Compliance):** Charts use **`ChartFigure`** + **`ChartDataTable`**; **Edit layout** on toolbar; **`activeBar` / `activeShape`** keyboard styling from **`lib/chart-keyboard-selection`** — not opacity-only **`Cell`** hacks (§4.3).
|
|
592
638
|
- [ ] **Dashboard layout persistence:** **`lib/data-view-dashboard-storage`** (or **`saveDashboardLayout`** / **`loadDashboardLayout`** on Placements); **`mergeDashboardLayout`** on load — no new ad-hoc storage keys for the same layout (§4.3).
|
|
593
639
|
- [ ] **⌘K palette (§7.1):** If adding or changing **`dataGroups`**, map rows in **`lib/command-menu-search-data.ts`** (not `command-menu.tsx`); use **`searchOnly`** on bulky groups; keep **`docs/command-menu-pattern.md`** aligned.
|
|
594
|
-
- [ ] **Page vs drawer vs dialog (§6.4):** Quick auxiliary with **parent context** and interactable hub → **drawer/sheet**; **blocking** short confirm → **dialog**; primary or long flows → **
|
|
640
|
+
- [ ] **Page vs drawer vs dialog (§6.4):** Quick auxiliary with **parent context** and interactable hub → **drawer/sheet**; **blocking** short confirm → **dialog**; primary or long flows → **new route** — **`docs/data-views-pattern.md`**, **`docs/drawer-vs-dialog-pattern.md`**.
|
|
595
641
|
- [ ] **Cards vs rows:** Primary sortable hub with many homogeneous records → **`DataTable`**; kanban / visual tiles → **`ListPageBoardCard`** — **`docs/card-vs-rows-pattern.md`**, **`.cursor/rules/exxat-card-vs-list-rows.mdc`**.
|
|
596
642
|
- [ ] **KPI count (max four):** **`entityKpiMetrics`** (and any static **`MetricItem[]`** for the same strip) has **≤ 4** tiles for template metrics + Data-tab key-metrics — **`docs/kpi-strip-max-four-pattern.md`**, **`.cursor/rules/exxat-kpi-max-four.mdc`**.
|
|
597
643
|
- [ ] **No toast (§6.5):** No **`toast()`** / Sonner / snackbars — use banners, inline status, or dialogs.
|
|
@@ -603,29 +649,15 @@ Copy and complete when implementing or reviewing:
|
|
|
603
649
|
- [ ] **Accessibility:** §8 — tablist/toolbar patterns, **≥24px** targets for icon-only controls, contrast on tinted surfaces, dialog/sheet/drawer **titles**; **every icon that communicates info has a text alternative** — adjacent label (preferred) OR `aria-label` + `Tooltip` (§8.6 Case A/B/C, covers informational icons like calendar-for-date, status dots, AND icon-only buttons); **kbd inside a button uses `<Kbd variant="bare">`** (§8.7); re-run **axe** on Placements when changing views toolbar.
|
|
604
650
|
- [ ] **Coach marks (§11):** `CoachMark` + `useCoachMark`; register in **`coach-mark-registry`**; use **`enabled`** / **`dependsOnDismissedFlowId`** when a tour must wait for another flow or a specific view (e.g. **dashboard**); customize-dashboard flows use **`lib/dashboard-customize-coach-mark.ts`**.
|
|
605
651
|
- [ ] **Application sidebar (§9.1):** **`ExxatProductLogo`** for product; **`logoDevUrl`** for schools; team switcher **`DropdownMenuContent`** keeps the explicit wide school/program surface (**`!w-max`** + min/max width); expanded switcher **`h-auto min-h-12`**; no **`CollapsibleTrigger` → `SidebarMenuButton` with `tooltip` prop**; child links **popover** on icon rail; profile **`stockPortraitUrl`** + **`referrerPolicy="no-referrer"`** on **`AvatarImage`**.
|
|
606
|
-
- [ ] **Secondary panel (§4.6):** If **`NavLinkItem.secondaryPanel`** is set — **`PANELS[id]`** in **`secondary-panel.tsx`**, hub mounts **`useAutoPanel(id)`**, scope syncs to URL + **`tableState.rows`** — **`.cursor/rules/exxat-primary-nav-secondary-panel.mdc`**. Panel shell uses **`--secondary-panel-bg`** (brand OKLCH, not **`bg-sidebar`**) — **`docs/shell-surface-elevation-pattern.md`**. **
|
|
652
|
+
- [ ] **Secondary panel (§4.6):** If **`NavLinkItem.secondaryPanel`** is set — **`PANELS[id]`** in **`secondary-panel.tsx`**, hub mounts **`useAutoPanel(id)`**, scope syncs to URL + **`tableState.rows`** — **`.cursor/rules/exxat-primary-nav-secondary-panel.mdc`**. Panel shell uses **`--secondary-panel-bg`** (brand OKLCH, not **`bg-sidebar`**) — **`docs/shell-surface-elevation-pattern.md`**. **Library folder scope:** header **⋯** → **Customize folder** + **`LibraryNewFolderSheet`** on **`LibraryClient`** — **`docs/library-hub-header-pattern.md`**, **`.cursor/rules/exxat-library-hub-header.mdc`**.
|
|
607
653
|
- [ ] **Flat KPI strip:** **`KeyMetrics variant="flat"`** — transparent cells, radial glow only, **`flatMetricsHairlineClass`** borders — **`docs/kpi-flat-band-pattern.md`**, **`.cursor/rules/exxat-kpi-flat-band.mdc`**.
|
|
608
654
|
- [ ] **Collaboration & access (§4.7):** Shared hubs use **`variant="collaboration"`**, empty **Add collaborator** / non-empty face rail, **⋯ → Invite people**, **`CollaborationAccessFlow`** or **`InviteCollaboratorsDrawer`**, **`lib/collaborator-access.ts`**, roster **name → email → role tags** — **`.cursor/rules/exxat-collaboration-access.mdc`**.
|
|
609
655
|
- [ ] **Dedicated search (§4.8):** Landing uses **`DedicatedSearchLandingTemplate`**; results use **`DedicatedSearchResultsHeaderChrome`** + outer **`DEDICATED_SEARCH_RESULTS_OUTER_CONTENT_CLASSNAME`**; **`DedicatedSearchUrlComposer`** + **`DedicatedSearchRecents`** with **`createDedicatedSearchRecentsController`** — **`.cursor/rules/exxat-dedicated-search-surfaces.mdc`**.
|
|
610
|
-
- [ ] **KPI trends:** **`MetricItem.trend`** matches the delta direction; **`trendPolarity`** set for “more is worse” metrics (flags, defects, overdue) — **`docs/kpi-trend-pattern.md`**, **`.cursor/rules/exxat-kpi-trends.mdc`**.
|
|
656
|
+
- [ ] **KPI trends:** **`MetricItem.trend`** matches the delta direction; **`trendPolarity`** set for “more is worse” metrics (flags, defects, overdue); **`delta`** is a count (not prose) and is **left empty** when there is no comparison (chip is suppressed — no `—` placeholder); supporting prose lives in **`MetricItem.description`** (renders below the value) — **`docs/kpi-trend-pattern.md`**, **`.cursor/rules/exxat-kpi-trends.mdc`**.
|
|
657
|
+
- [ ] **Person rails — no overlap:** Face rails / reviewer piles / collaborator stacks render avatars **side-by-side** with **`gap-1`** / **`gap-1.5`** (use **`AvatarGroup`** + **`AvatarGroupCount`** for **`+N`**); **MUST NOT** restore negative-margin overlap (`-space-x-*` + `*:ring-*`) — **`.cursor/rules/exxat-person-identity-display.mdc`**.
|
|
611
658
|
- [ ] **Font Awesome:** New glyphs covered by **`fa:subset-audit`** / Kit subset; decorative **`<i>`** has **`aria-hidden`**; icon-only controls follow **§8.6** — **`.cursor/rules/exxat-fontawesome-icons.mdc`**.
|
|
612
659
|
- [ ] **System IDs:** Visible **`questionId`**, record keys, and copy-pasteable identifiers use **`font-mono tabular-nums`**; mixed lines mono-wrap **only** the ID — **`.cursor/rules/exxat-mono-ids.mdc`**, **`.cursor/skills/exxat-mono-ids/SKILL.md`**.
|
|
613
660
|
|
|
614
661
|
---
|
|
615
662
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
Copy and complete when implementing or reviewing a **dedicated form, wizard, or settings** page (not a list hub):
|
|
619
|
-
|
|
620
|
-
- [ ] **`FocusedWorkflowPageTemplate`** on **`app/(app)/…/page.tsx`** — **`siteHeader`** with **`back`** or **`breadcrumbs`** + **`title`**.
|
|
621
|
-
- [ ] **`maxWidth`**: **`md`** | **`lg`** | **`xl`** — not list-hub **`max-w-[1440px]`**.
|
|
622
|
-
- [ ] **One body layout:** **`FocusedWorkflowSingleColumn`** | **`FocusedWorkflowStepForm`** | **`FocusedWorkflowSidebarSections`** | **`FocusedWorkflowEmptyState`**.
|
|
623
|
-
- [ ] **Wizard / actions:** **`FocusedWorkflowWizardFooter`** or **`FocusedWorkflowActionFooter`** + **`Shortcut`** + **`<Kbd variant="bare">`** (**`exxat-kbd-shortcuts.mdc`**).
|
|
624
|
-
- [ ] **Domain UI** in **`*-composer.tsx`** / **`*-client.tsx`**; **`FocusedWorkflow*`** names stay generic.
|
|
625
|
-
- [ ] **No** **`ListPageTemplate`**, **no** **`ListPageFolderColumnsPanel`**, **no** Miller-column split explorer in this shell.
|
|
626
|
-
- [ ] **No toast (§6.5)** for product feedback on this route.
|
|
627
|
-
- [ ] **Reference:** **`/examples/focused-workflow`**, **`docs/focused-workflow-page-pattern.md`**, **`.cursor/skills/exxat-focused-workflow-page/SKILL.md`**.
|
|
628
|
-
|
|
629
|
-
---
|
|
630
|
-
|
|
631
|
-
*Last updated: focused workflow page pattern + §14 checklist + rule/skill; KPI flat band + shell surface elevation; §4.6 secondary panel OKLCH; monospace system IDs; question bank folder header; drawer vs dialog / card vs rows / KPI max-four; §4.8 dedicated search; §4.7 collaboration; §4.1 centralized dataset; §4.5 view shells; Font Awesome; §9.1 sidebar; §4.4 board cards; §6.5 no toast; §7.1 command palette; §13 checklist.*
|
|
663
|
+
*Last updated: HubTable is the canonical hub primitive (raw DataTable only outside hubs); filter button tooltip nesting fix; KPI delta vs description (chip suppressed when empty) + no overlapping avatars (`AvatarGroup` is gapped, not negative-margin); KPI flat band + shell surface elevation pattern docs/rules/skills; §4.6 secondary panel OKLCH; monospace system IDs; library folder header; drawer vs dialog / card vs rows / KPI max-four; §4.8 dedicated search; §4.7 collaboration; §4.1 centralized dataset; §4.5 view shells; Font Awesome; §9.1 sidebar; §4.4 board cards; §6.5 no toast; §7.1 command palette; §13 checklist.*
|