@exxatdesignux/ui 0.2.18 → 0.3.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 +69 -1
- package/bin/sync-extras.mjs +116 -29
- package/consumer-extras/README.md +43 -4
- 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 +41 -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-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-question-bank-hub-header.mdc +28 -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 +2 -2
- package/consumer-extras/cursor-skills/exxat-centralized-list-dataset/SKILL.md +1 -1
- package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +9 -9
- package/consumer-extras/cursor-skills/exxat-ds-skill/references/data-table-pattern.md +1 -1
- package/consumer-extras/handbook/HANDBOOK.md +185 -0
- package/consumer-extras/handbook/glossary.md +57 -0
- package/consumer-extras/handbook/reference-implementations.md +126 -0
- package/consumer-extras/handbook/voice-and-tone.md +262 -0
- package/consumer-extras/patterns/command-menu-pattern.md +1 -1
- package/consumer-extras/patterns/data-views-pattern.md +14 -14
- 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 +167 -0
- package/dist/components/data-views/hub-table.js +5561 -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 +6575 -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 +13324 -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 -18
- 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 +255 -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 +498 -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/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 +494 -151
- 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/.cursor/rules/exxat-command-menu.mdc +1 -1
- package/template/.cursor/rules/exxat-dashboard-view-charts.mdc +3 -3
- package/template/.cursor/rules/exxat-data-tables.mdc +1 -1
- package/template/.cursor/rules/exxat-ds-agents.mdc +2 -2
- package/template/.cursor/rules/exxat-kbd-shortcuts.mdc +2 -2
- package/template/.cursor/rules/exxat-table-properties-drawer.mdc +1 -1
- package/template/AGENTS.md +84 -20
- package/template/app/(app)/examples/page.tsx +0 -1
- package/template/app/(app)/layout.tsx +17 -4
- package/template/app/(app)/question-bank/layout.tsx +1 -1
- package/template/app/(app)/question-bank/new/page.tsx +11 -24
- package/template/app/globals.css +13 -1972
- 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/compliance-table.tsx +240 -384
- 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-view-dashboard-charts-compliance.tsx +2 -2
- package/template/components/data-view-dashboard-charts-team.tsx +2 -2
- package/template/components/data-view-dashboard-charts.tsx +2 -2
- 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 +42 -1
- 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 -0
- 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 -60
- 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/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/hub-tree-panel-view.tsx +2 -2
- 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/new-placement-back-btn.tsx +1 -1
- package/template/components/new-placement-form.tsx +63 -189
- package/template/components/new-question-composer.tsx +432 -402
- 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/placement-board-card.tsx +71 -83
- package/template/components/placements-board-view.tsx +3 -10
- package/template/components/placements-client.tsx +10 -42
- package/template/components/placements-list-view.tsx +22 -69
- package/template/components/placements-table-columns.tsx +8 -438
- package/template/components/placements-table.tsx +588 -1296
- package/template/components/product-switcher.tsx +1 -1
- package/template/components/product-wordmark.tsx +2 -1
- package/template/components/question-bank-client.tsx +4 -1
- package/template/components/question-bank-hub-client.tsx +1 -1
- package/template/components/question-bank-new-folder-sheet.tsx +2 -2
- package/template/components/question-bank-secondary-nav.tsx +3 -3
- package/template/components/question-bank-table.tsx +294 -526
- package/template/components/rotations-empty-state.tsx +1 -1
- package/template/components/rotations-panel-activator.tsx +1 -1
- package/template/components/settings-appearance-card.tsx +1 -1
- package/template/components/{app-sidebar-dynamic.tsx → sidebar/app-sidebar-dynamic.tsx} +1 -1
- package/template/components/{app-sidebar.tsx → sidebar/app-sidebar.tsx} +4 -4
- package/template/components/sidebar/index.ts +16 -0
- package/template/components/{secondary-nav.tsx → sidebar/secondary-nav.tsx} +2 -2
- package/template/components/{secondary-panel.tsx → sidebar/secondary-panel.tsx} +6 -3
- package/template/components/{sidebar-auto-collapse.tsx → sidebar/sidebar-auto-collapse.tsx} +6 -2
- package/template/components/{sidebar-shell.tsx → sidebar/sidebar-shell.tsx} +1 -1
- package/template/components/site-header.tsx +1 -1
- package/template/components/{sites-all-client.tsx → sites-client.tsx} +1 -1
- package/template/components/sites-table.tsx +124 -257
- 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 -249
- package/template/components/table-properties/drawer.tsx +1 -1105
- 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/team-table.tsx +242 -382
- 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/list-page.tsx +1 -584
- package/template/components/templates/nested-secondary-panel-shell.tsx +1 -62
- package/template/components/templates/new-focus-template.tsx +659 -0
- package/template/components/templates/secondary-panel-hub-template.tsx +1 -1
- 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/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/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 +14 -14
- 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/token-taxonomy.md +416 -0
- package/template/eslint.config.mjs +27 -0
- package/template/hooks/use-secondary-panel-hub-nav.ts +1 -1
- package/template/lib/command-menu-config.ts +0 -1
- package/template/lib/compliance-supported-views.ts +10 -0
- package/template/lib/conditional-rule-match.ts +6 -97
- package/template/lib/data-list-display-options.ts +1 -35
- package/template/lib/data-list-view-registry.ts +1 -0
- package/template/lib/data-list-view-surface.ts +1 -69
- package/template/lib/data-list-view.ts +1 -38
- package/template/lib/dev-log.ts +1 -8
- package/template/lib/editable-target.ts +1 -10
- package/template/lib/hub-connected-view-renderers.ts +58 -0
- package/template/lib/list-hub-supported-views.ts +10 -0
- package/template/lib/list-page-table-properties.ts +1 -52
- package/template/lib/mock/navigation.tsx +0 -8
- package/template/lib/mock/placements.ts +0 -7
- package/template/lib/placement-board-card-layout.ts +41 -41
- package/template/lib/placements-supported-views.ts +12 -0
- package/template/lib/question-bank-supported-views.ts +12 -0
- 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/sites-supported-views.ts +10 -0
- package/template/lib/team-supported-views.ts +10 -0
- package/template/package.json +1 -0
- package/template/tests/setup.ts +25 -0
- package/src/theme.css +0 -1132
- package/template/app/(app)/data-list/[id]/page.tsx +0 -44
- package/template/app/(app)/data-list/new/page.tsx +0 -34
- package/template/app/(app)/data-list/page.tsx +0 -10
- package/template/components/compliance-list-view.tsx +0 -54
- package/template/components/dashboard-onboarding-gallery.tsx +0 -13
- package/template/components/dashboard-onboarding.tsx +0 -21
- package/template/components/question-bank-list-view.tsx +0 -53
- package/template/components/section-cards.tsx +0 -106
- package/template/components/sites-list-view.tsx +0 -42
- package/template/components/team-list-view.tsx +0 -59
- package/template/lib/placement-lifecycle.ts +0 -5
- /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/components/{sidebar-auto-open.tsx → sidebar/sidebar-auto-open.tsx} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -15,10 +15,78 @@ After the user bumps `@exxatdesignux/ui`, do this in order:
|
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
+
## [Unreleased]
|
|
19
|
+
|
|
20
|
+
## [0.3.0] – 2026-05-21
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- **`exxat-ui sync-extras` now installs the full AI bundle in one command.** Previously the CLI shipped only Cursor skills + pattern docs; consumers had to `degit` Cursor rules and handbook tier docs separately. The CLI now writes, in one pass:
|
|
25
|
+
1. **Skills to both clients** — `.cursor/skills/exxat-*/` **and** `.claude/skills/exxat-*/` (same `SKILL.md` shape, two readers, one source of truth — no more `ln -s` step).
|
|
26
|
+
2. **Cursor rules** — `.cursor/rules/exxat-*.mdc` (29 binding `MUST` / `MUST NOT` files: data-tables, accessibility, kbd shortcuts, KPI trends/flat-band/max-four, board cards, list-page-view-shells, centralized-list-dataset, reuse-before-custom, no-toast, drawer-vs-dialog, page-vs-drawer, no-slds-leakage, token-discipline, mono-ids, fontawesome-icons, dashboard-view-charts, …).
|
|
27
|
+
3. **Handbook tier** — `docs/exxat-ds/handbook/{HANDBOOK,glossary,voice-and-tone,reference-implementations}.md`. These are stage-rewritten copies of `apps/web/docs/*.md` — links to shipped patterns use `../`, links to unshipped neighbours (blueprints, token-taxonomy, root `AGENTS.md`, `lib/*`) are rewritten to absolute GitHub URLs at bundle time so every link in the consumer's repo resolves.
|
|
28
|
+
4. **Patterns + checklist** — unchanged from prior releases (`docs/exxat-ds/*-pattern.md`, `consumer-upgrade-checklist.md`).
|
|
29
|
+
|
|
30
|
+
The CLI is **namespaced** — it only writes files starting with `exxat-` (skills + rules), so a consumer's own non-Exxat rules / skills survive an upgrade untouched. Product code under `app/`, `pages/`, `src/`, plus the consumer's own `AGENTS.md`, are never modified. Bundle source lives at `packages/ui/consumer-extras/{cursor-skills,cursor-rules,patterns,handbook}/`.
|
|
31
|
+
|
|
32
|
+
- **Single centralized `<HubTable<TRow>>`** — `components/data-views/hub-table.tsx` is now the one table surface every list-page hub renders. It owns the previously per-hub scaffolding that drifted across five files: `useTableState`, `displayOptions` (controlled or internal), `conditionalRules` add/remove/update, `filterFields` + `fieldDefinitions` + `resolveColumnLabel` memos, `TablePropertiesDrawerButton` wiring with `supportedViewTypes`, the imperative `openPropertiesDrawer()` handle (`HubTableHandle`), the default `DataTable` for `view === "table"`, and `ListPageConnectedViewBody` routing for every other view. Hubs now pass only **what's different**: column defs, a typed `renderers: HubTableRenderers<TRow>` map for non-table views, and entity-specific labels/sorts/empties. The renderer args (`state`, `toolbar`, `toolbarShell`, `drawerToolbarProps`, `displayOptions`, `patchDisplay`) cover both the common "wrap a body in the shared toolbar" case (`toolbarShell(<MyView rows={state.rows} />)`) and the escape-hatch case (dashboards that own their own toolbar with extra layout-edit actions splat `drawerToolbarProps` back into a custom `<TablePropertiesDrawerButton>`). Hub-specific helpers (`columnsToFilterFields`, `columnsToFieldDefinitions`) are exported from the same module so column defs become the single source of truth for filters + drawer field options. Mirrored to `packages/ui/template/components/data-views/hub-table.tsx`.
|
|
33
|
+
- **Per-hub `supportedViewTypes` allowlists** — `lib/team-supported-views.ts`, `lib/compliance-supported-views.ts`, `lib/placements-supported-views.ts`, `lib/sites-supported-views.ts` (matching the existing `question-bank-supported-views.ts` and `list-hub-supported-views.ts`). Each hub passes its allowlist into `<HubTable>` so `TablePropertiesDrawerButton` filters the view-type tile grid to the hub's implemented views — Sites cannot pick Calendar, Placements cannot pick Calendar, Team cannot pick Folder/Tree, etc. Pair these with `ListPageTemplate.supportedViewTypes` on the hub client to filter the Add-view menu + ⌘1–9 shortcuts in tandem.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- **Every hub table migrated to `<HubTable>` + `ListPageConnectedViewBody`** — the previous "single centralized list dataset" rule is now structurally enforced (one `useTableState` per hub, one row bag every view body reads). Net code: hub `*-table.tsx` files dropped from **4,402** → **2,984** lines (-32%) by removing duplicated scaffolding; the new `hub-table.tsx` adds ~350 lines once. Per-hub deltas:
|
|
38
|
+
1. **`placements-table.tsx`** 1,642 → 955 (-42%). Six bespoke `DataList*Shell` sub-components (board / list / dashboard / folder / tree-panel / panel) deleted — each one was a separate copy of `<DataTableToolbar … toolbarSlot={s => <TablePropertiesDrawerButton …/>}>` wrapped around a different view body. Replaced by per-view renderer entries that call `toolbarShell(<body />)`. Pagination chrome (`CountSyncer` + `PaginationBar`) moves into a custom `tableRenderer` + the `list-with-toolbar` renderer; `HubTable.paginationOverride` is forwarded into `useTableState`. The dashboard's layout-edit + coach-mark + persistence state lives in an internal `<PlacementsDashboardBody>` component (renderers can't host hooks — `ListPageConnectedViewBody` calls them inline).
|
|
39
|
+
2. **`question-bank-table.tsx`** 1,073 → 823 (-23%). The custom `<HubFolderColumnsPanel>` miller-view + `<HubTreePanelView>` stay (genuinely entity-specific); every shell/toolbar wrapper around them is gone. The `QuestionBankNewFolderSheet` modal state (shared by panel + tree-panel) moves to the hub's top scope, so both renderers share one sheet instance instead of duplicating it. URL search sync is now `HubTable.syncedSearchFromUrl={searchLanding ? undefined : urlListSearch}` (was a 5th positional argument to `useTableState`).
|
|
40
|
+
3. **`team-table.tsx`** 693 → 534 (-23%). Dashboard layout + coach-mark + persistence state extracted into a `<TeamDashboardBody>` internal component, same pattern as Placements. `TeamFinderListRow` + `TeamFinderDetail` panel helpers unchanged.
|
|
41
|
+
4. **`compliance-table.tsx`** 612 → 448 (-27%). Same dashboard-body extraction; panel groups builder unchanged.
|
|
42
|
+
5. **`sites-table.tsx`** 382 → 224 (-41%). Inline panel + dashboard renderers; sites has no custom dashboard-layout state.
|
|
43
|
+
- **`HubTable` API additions** (back-compat — all new props optional) — `displayOptions` + `onDisplayOptionsChange` (controlled mode for hubs that persist display state at the page level, e.g. Placements; otherwise `HubTable` owns the state internally), `pagination` + `onPaginationChange` (forwarded to `TablePropertiesDrawerButton` so the drawer's Display panel can offer "Show pagination"), `paginationOverride` (forwarded to `useTableState` for server-style paged tables), `syncedSearchFromUrl` (forwarded to `useTableState` for `?q=`-bound toolbar search), `onRowClick`, and `tableRenderer` (override for the `data-table` view when the hub needs pagination chrome or a custom wrapper around `<DataTable>`).
|
|
44
|
+
- **`OpenTablePropertiesHandle` aliases** — each `*-table.tsx` now exports `*TableHandle = HubTableHandle` instead of `= OpenTablePropertiesHandle` directly. Both resolve to the same shape (`{ openPropertiesDrawer(): void }`), so existing `tablePropertiesRef` consumers continue to work without changes.
|
|
45
|
+
|
|
46
|
+
### Added (continued, from earlier in this Unreleased section)
|
|
47
|
+
|
|
48
|
+
- **View registry + per-hub `supportedViewTypes` plumbing** — single source of truth for list-page view types and their render kinds, with per-hub allowlists so Properties, Add view, and ⌘1–9 shortcuts never offer a view the hub does not render.
|
|
49
|
+
1. **`lib/data-list-view-registry.ts`** — `DATA_LIST_VIEW_REGISTRY` (one `DataListViewDefinition` per `DataListViewType`), `dataListViewDefinition`, `dataListViewTilesForHub` / `dataListViewSelectionTilesForHub` (filtered tiles for a hub's `supportedViewTypes`), `isDataListSurfaceViewType` (now derived from the registry — the previous local `SURFACE_VIEW_TYPES` literal in `lib/list-page-table-properties.ts` is gone), `showsListPageHubMetricsStrip` (false for `calendar` and `dashboard`).
|
|
50
|
+
2. **`components/data-views/list-page-connected-view-body.tsx`** — `<ListPageConnectedViewBody view renderers hubLabel />` switches list-hub view bodies by render kind. Missing kinds render a clear `<ListPageViewNotConfigured>` empty state — never silently fall through to dashboard. Renderers can be `React.ReactNode` or `() => ReactNode` (the latter for lazy / heavy bodies).
|
|
51
|
+
3. **`lib/hub-connected-view-renderers.ts`** — typed `defineHubViewRenderers(supported, renderers)` builder; the `Supported` generic constrains the renderer keys to the hub's actual render kinds, and dev builds emit a console warning when a supported view has no body.
|
|
52
|
+
4. **`lib/question-bank-supported-views.ts`** + **`lib/list-hub-supported-views.ts`** — per-hub allowlists (e.g. Question bank supports `table | list | board | dashboard | folder | panel | tree-panel`; List hub supports `table | list | board | calendar | panel`). Hubs pass these into `ListPageTemplate.supportedViewTypes` and `TablePropertiesDrawerButton.supportedViewTypes`.
|
|
53
|
+
5. **`DataListViewType` gains `"calendar"`** — and `data-list-view-surface.ts` adds the matching `calendar-with-toolbar` render kind. Existing hubs are unaffected (no hub claims `calendar` in its supported set yet); the type is now forward-compatible with a calendar body.
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- **`TablePropertiesDrawer` + `TablePropertiesDrawerButton`** accept optional `supportedViewTypes?: readonly DataListViewType[]`. When set, the view-type tile grid in Properties is filtered to that hub's implemented views (via `dataListViewTilesForHub`) so users cannot switch a hub into a view it does not render. Default behavior (omitting the prop) is unchanged — all registered tiles are shown.
|
|
58
|
+
- **`ListPageTemplate`** accepts the same `supportedViewTypes` prop and filters **both** the Add view dropdown menu and the ⌘1–9 shortcut bindings to the hub's actual views. Previously, `VIEW_TYPES.slice(0, 9)` bound every registered view globally even on hubs that only implement a subset, so a user could ⌘4 → Dashboard into a hub that had no dashboard renderer (it would fall through to the not-configured empty state).
|
|
59
|
+
- **Question bank** (`question-bank-client.tsx`, `question-bank-table.tsx`) now passes `QUESTION_BANK_SUPPORTED_VIEWS` into `ListPageTemplate.supportedViewTypes` and `TablePropertiesDrawerButton.supportedViewTypes` end-to-end. Other hubs (Placements, Team, Compliance, Sites) are unchanged for this release — they still expose every registered view; their `supportedViewTypes` props are opt-in.
|
|
60
|
+
- **`lib/list-page-table-properties.ts`** no longer maintains its own `SURFACE_VIEW_TYPES` literal. `isDataListSurfaceViewType` is re-exported from `lib/data-list-view-registry.ts` so adding a new `DataListViewType` no longer requires editing three files.
|
|
61
|
+
|
|
62
|
+
### Promoted into `@exxatdesignux/ui` (shared primitives)
|
|
63
|
+
|
|
64
|
+
- **`components/resizable`** + **`components/dot-pattern`** moved from `apps/web/components/ui/` to `packages/ui/src/components/ui/`. App imports continue to work through `apps/web/components/ui/{resizable,dot-pattern}.tsx`, which are now one-line re-exports from `@exxatdesignux/ui/components/{resizable,dot-pattern}`. Added `motion@^12.38.0` and `react-resizable-panels@^4.10.0` to `packages/ui/package.json` `dependencies`.
|
|
65
|
+
|
|
66
|
+
### Removed
|
|
67
|
+
|
|
68
|
+
- **`components/section-cards.tsx`** (dead since the shadcn starter — zero imports across `apps/web` and `packages/ui/template/`).
|
|
69
|
+
|
|
70
|
+
### Renamed
|
|
71
|
+
|
|
72
|
+
- **`components/sites-all-client.tsx`** → **`components/sites-client.tsx`**; `SitesAllClient` export → `SitesClient`. The hub is currently unmounted (no `app/(app)/sites/` route), but the file is kept as a reference Sites hub composition and renamed for consistency with `placements-client`, `team-client`, `compliance-client`. Mirrored in `packages/ui/template/`.
|
|
73
|
+
|
|
18
74
|
## [0.2.18] - 2026-05-19
|
|
19
75
|
|
|
20
76
|
### Fixed
|
|
21
77
|
|
|
78
|
+
- **Dark mode surface elevation ladder — popover / card split, secondary panel nestled** (`globals.css`):
|
|
79
|
+
1. **`--popover` lifted to L=0.275** (was 0.225). Sharing the card's subtle 0.225 value made floating dropdowns blend straight into the canvas (only +0.025 above L=0.20) — the dropdown chrome lost its boundary. Popover now sits +0.075 above canvas with a slightly elevated chroma (built-ins 0.022 / custom `max(0.018, c·0.16)`), giving dropdowns and menus the visible lift they need over content. `--card` stays at 0.225 — it's an *inline* surface, not a floating one, and the earlier "subtle wash" reading was correct for it.
|
|
80
|
+
2. **`--secondary-panel-bg` rewired** from `var(--brand-tint)` (which resolved to L=0.30) down to **L=0.22**. The old value made the secondary nav panel *brighter* than the surrounding sidebar (L=0.245), so the Library / question-bank rail popped out as a bright tile instead of nesting in. The new value wedges it between sidebar (0.245) and canvas (0.20). Per-theme overrides carry the brand hue + chroma (Exxat One 0.025, Prism 0.030, Assessment 0.025, custom `max(0.020, c·0.20)`).
|
|
81
|
+
3. **Dark surface ladder is now** `--background` 0.20 → `--secondary-panel-bg` 0.22 → `--card` 0.225 → `--sidebar` / `--input-background` 0.245 → `--popover` 0.275 — clear, monotonic stepping with floating elements lifting further than inline ones.
|
|
82
|
+
- **Dark mode card / popover / input-background — softened to a subtle brand wash, not a tinted panel** (`globals.css`): The previous pass at `L=0.255 C=0.030` made cards read as a *separate tinted surface* floating above canvas. Pulled lightness down to `L=0.225` (only 0.025 above `--background` at L=0.20) and chroma down to `C=0.017` (built-ins) / `max(0.015, calc(c*0.13))` (custom). Cards now read as the same surface family as canvas with a small elevation step and a whisper of brand hue — the saturated brand expression remains on `--brand-tint` and `--sidebar` where it belongs. Input background follows the same shift (L 0.245 / C 0.010). Foreground contrast unchanged (≥ 12:1).
|
|
83
|
+
- **Hydration warnings from the Cursor IDE browser preview** (`app/(app)/layout.tsx`, `components/templates/nested-secondary-panel-shell.tsx`, `components/ask-leo-sidebar.tsx`): The in-IDE browser MCP injects a `data-cursor-ref` attribute on top-level layout chrome *before* React hydrates so it can target those nodes for click automation. React then warned about an attribute it didn't render. Added `suppressHydrationWarning` on just the three SSR-rendered shell roots that the MCP tags. The flag is scoped to *each element's own attributes only* — children still hydrate normally and any real mismatch inside the panel still surfaces. Has zero effect outside the Cursor IDE preview.
|
|
84
|
+
- **Dark mode brand surfaces — every product chrome surface now reads as the brand per product** (`globals.css`):
|
|
85
|
+
1. **`--brand-tint`** was never overridden in `.dark` or any `.theme-*.dark` block, so it cascaded down from the light-mode pale value (e.g. `oklch(0.97 0.02 343)` for Prism). Because the dark `--secondary-panel-bg` formula derived it as `color-mix(--card 32%, --brand-tint 68%)`, the question-bank Library secondary panel rendered as a pale rose / lavender pastel on dark canvas. Fixed by adding `--brand-tint` / `--brand-tint-light` / `--brand-tint-subtle` overrides at dark-mode lightness inside `.dark`, `.theme-one.dark`, `.theme-prism.dark`, `.theme-assessment.dark`, and `.theme-custom.dark`.
|
|
86
|
+
2. **`--secondary-panel-bg` formula in `.dark`** rewritten from `color-mix(--card 32%, --brand-tint 68%)` to plain `var(--brand-tint)`. The old mix folded in `--card`'s neutral hue 270 — never overridden per theme in dark mode — so every product's secondary panel was pulled toward the same purple-neutral. Now the panel renders as a fully brand-tinted dark surface that varies clearly per product: Exxat One `#2c2a46` (navy-lavender), Prism `#402235` (wine), Assessment `#143525` (forest green).
|
|
87
|
+
3. **`--brand-tint` / `--sidebar` chroma** raised from `0.014–0.015` to `0.04–0.055` at their dark-mode lightnesses. At L≈0.245–0.30 the OKLCH hue-perception threshold is much higher than at L≈0.97 (Helmholtz–Kohlrausch effect), so the previous chroma was below the perception floor — products looked identical even though their hues were set correctly. The new chroma is well within WCAG contrast bounds (foreground stays ≥ 11:1 on every variant).
|
|
88
|
+
4. **`--card` / `--popover` / `--input-background`** were only set in base `.dark` at neutral hue 270, never overridden per theme. KPI tiles, popovers, and outlined surfaces therefore stayed dead grey no matter which product was active — directly visible on the dashboard's six KPI tiles in the custom-orange theme. Added per-theme overrides at L≈0.255 with chroma 0.03 (built-ins) or `max(0.022, calc(c * 0.22))` (custom) at brand hue. Cards now render as a "darker version of the brand color" per product: Exxat One `#212131`, Prism `#2d1d27`, Assessment `#15271e`, custom-orange `#2f1e16`.
|
|
89
|
+
- **Dark canvas brand cohesion** (`globals.css`): `--background` chroma in every `.theme-*.dark` block bumped from `0.008` to `0.014`. At L 0.20 the previous chroma sat below the OKLCH hue-perception threshold, so the canvas read as dead-neutral grey regardless of the brand hue. The new value gives the canvas a perceptible whisper of brand chroma without affecting text contrast (foreground stays ≥ 12:1 against background).
|
|
22
90
|
- **`Sidebar`**: Read `sidebar_state` on the server for `defaultOpen` so the documents **Resources** heading and rail chrome match the first client paint (fixes hydration warnings). Skip redundant cookie restore when state already matches.
|
|
23
91
|
- **`TablePropertiesDrawer`**: Portaled **Add filter / sort / rule** menus use `z-[90]` above the properties sheet (`z-[80]`); `Sheet` and dropdowns use `modal={false}`; filter updates apply synchronously (no `startTransition` deferral). Drawer button routes column/display handlers through refs for the portaled sheet.
|
|
24
92
|
|
|
@@ -45,7 +113,7 @@ After the user bumps `@exxatdesignux/ui`, do this in order:
|
|
|
45
113
|
|
|
46
114
|
### Changed
|
|
47
115
|
|
|
48
|
-
- **Tokens**: `globals.css`
|
|
116
|
+
- **Tokens**: `globals.css` refinements and starter **`template/`** parity with the web app (layout, Question bank hub chrome, navigation). *(Note: `theme.css` was a duplicate of `globals.css` and has been removed — see [`0003-globals-css-canonical.md`](../../apps/web/docs/migrations/0003-globals-css-canonical.md).)*
|
|
49
117
|
- **Consumer extras**: Cursor skills + pattern docs refreshed for collaboration / Question bank hub header.
|
|
50
118
|
|
|
51
119
|
### Chore (monorepo)
|
package/bin/sync-extras.mjs
CHANGED
|
@@ -2,12 +2,20 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Consumer: copy packaged design-system extras into the app repo.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Writes (and overwrites) ONLY:
|
|
6
6
|
* - `.cursor/skills/<bundled exxat-* skill folders>/`
|
|
7
|
-
* -
|
|
7
|
+
* - `.claude/skills/<bundled exxat-* skill folders>/` (same SKILL.md shape;
|
|
8
|
+
* Claude Code reads from `.claude/skills/` instead of `.cursor/skills/`)
|
|
9
|
+
* - `.cursor/rules/<bundled exxat-*.mdc rule files>`
|
|
10
|
+
* - `docs/exxat-ds/*.md` (pattern reference docs + consumer-upgrade-checklist.md)
|
|
11
|
+
* - `docs/exxat-ds/handbook/*.md` (HANDBOOK, glossary, voice-and-tone, reference-implementations)
|
|
8
12
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
13
|
+
* The CLI is **namespaced** — it only touches files starting with `exxat-`
|
|
14
|
+
* (skills + rules) so a consumer's own non-Exxat rules/skills are preserved.
|
|
15
|
+
* Product code under `app/`, `pages/`, `src/`, plus the consumer's `AGENTS.md`
|
|
16
|
+
* and any of their own docs are never modified.
|
|
17
|
+
*
|
|
18
|
+
* Run after every `npm install @exxatdesignux/ui@…` / `pnpm update @exxatdesignux/ui`.
|
|
11
19
|
*
|
|
12
20
|
* npx --package=@exxatdesignux/ui@latest exxat-ui sync-extras
|
|
13
21
|
*/
|
|
@@ -20,46 +28,125 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
|
20
28
|
const pkgRoot = resolve(__dirname, "..")
|
|
21
29
|
const cwd = process.cwd()
|
|
22
30
|
|
|
23
|
-
const
|
|
24
|
-
|
|
31
|
+
const SOURCES = {
|
|
32
|
+
skills: join(pkgRoot, "consumer-extras", "cursor-skills"),
|
|
33
|
+
rules: join(pkgRoot, "consumer-extras", "cursor-rules"),
|
|
34
|
+
patterns: join(pkgRoot, "consumer-extras", "patterns"),
|
|
35
|
+
handbook: join(pkgRoot, "consumer-extras", "handbook"),
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const DESTINATIONS = {
|
|
39
|
+
cursorSkills: join(cwd, ".cursor", "skills"),
|
|
40
|
+
claudeSkills: join(cwd, ".claude", "skills"),
|
|
41
|
+
cursorRules: join(cwd, ".cursor", "rules"),
|
|
42
|
+
patterns: join(cwd, "docs", "exxat-ds"),
|
|
43
|
+
handbook: join(cwd, "docs", "exxat-ds", "handbook"),
|
|
44
|
+
}
|
|
25
45
|
|
|
26
|
-
|
|
27
|
-
|
|
46
|
+
let wrote = 0
|
|
47
|
+
let skipped = 0
|
|
28
48
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
49
|
+
/** Replace `dest` with `src` (directory) — preserves only what we own. */
|
|
50
|
+
function replaceDir(src, dest) {
|
|
51
|
+
if (existsSync(dest)) rmSync(dest, { recursive: true, force: true })
|
|
52
|
+
cpSync(src, dest, { recursive: true })
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Skills are bundled as directories like `cursor-skills/exxat-board-cards/SKILL.md`.
|
|
57
|
+
* Copy every `exxat-*` directory into the destination, replacing any prior copy.
|
|
58
|
+
* The same source tree is written to BOTH Cursor and Claude (identical SKILL.md
|
|
59
|
+
* shape, two readers) so one upgrade lights up both clients.
|
|
60
|
+
*/
|
|
61
|
+
function syncSkillsTo(label, dest) {
|
|
62
|
+
if (!existsSync(SOURCES.skills)) {
|
|
63
|
+
console.error("[sync-extras] missing packaged skills:", SOURCES.skills)
|
|
35
64
|
process.exit(1)
|
|
36
65
|
}
|
|
37
|
-
mkdirSync(
|
|
38
|
-
for (const name of readdirSync(
|
|
39
|
-
const src = join(
|
|
66
|
+
mkdirSync(dest, { recursive: true })
|
|
67
|
+
for (const name of readdirSync(SOURCES.skills)) {
|
|
68
|
+
const src = join(SOURCES.skills, name)
|
|
40
69
|
if (!statSync(src).isDirectory()) continue
|
|
41
70
|
if (!name.startsWith("exxat-")) continue
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
71
|
+
replaceDir(src, join(dest, name))
|
|
72
|
+
console.log(`[sync-extras] wrote ${label}/${name}/`)
|
|
73
|
+
wrote++
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Rules ship as flat `.mdc` files under `consumer-extras/cursor-rules/`.
|
|
79
|
+
* Only files starting with `exxat-` are copied so the consumer's own rules
|
|
80
|
+
* (and any other vendor's rules) survive an upgrade untouched.
|
|
81
|
+
*/
|
|
82
|
+
function syncRules() {
|
|
83
|
+
if (!existsSync(SOURCES.rules)) {
|
|
84
|
+
console.warn("[sync-extras] no packaged cursor-rules (optional)")
|
|
85
|
+
skipped++
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
mkdirSync(DESTINATIONS.cursorRules, { recursive: true })
|
|
89
|
+
for (const name of readdirSync(SOURCES.rules)) {
|
|
90
|
+
const src = join(SOURCES.rules, name)
|
|
91
|
+
if (!statSync(src).isFile()) continue
|
|
92
|
+
if (!name.startsWith("exxat-") || !name.endsWith(".mdc")) continue
|
|
93
|
+
cpSync(src, join(DESTINATIONS.cursorRules, name))
|
|
94
|
+
console.log(`[sync-extras] wrote .cursor/rules/${name}`)
|
|
95
|
+
wrote++
|
|
46
96
|
}
|
|
47
97
|
}
|
|
48
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Pattern docs (data-views-pattern, command-menu-pattern, …) + the
|
|
101
|
+
* `consumer-upgrade-checklist.md` land flat in `docs/exxat-ds/`.
|
|
102
|
+
*/
|
|
49
103
|
function syncPatterns() {
|
|
50
|
-
if (!existsSync(
|
|
51
|
-
console.warn("[sync-extras] no packaged patterns (optional)
|
|
104
|
+
if (!existsSync(SOURCES.patterns)) {
|
|
105
|
+
console.warn("[sync-extras] no packaged patterns (optional)")
|
|
106
|
+
skipped++
|
|
52
107
|
return
|
|
53
108
|
}
|
|
54
|
-
mkdirSync(
|
|
55
|
-
for (const name of readdirSync(
|
|
56
|
-
const src = join(
|
|
109
|
+
mkdirSync(DESTINATIONS.patterns, { recursive: true })
|
|
110
|
+
for (const name of readdirSync(SOURCES.patterns)) {
|
|
111
|
+
const src = join(SOURCES.patterns, name)
|
|
57
112
|
if (!statSync(src).isFile()) continue
|
|
58
|
-
cpSync(src, join(
|
|
113
|
+
cpSync(src, join(DESTINATIONS.patterns, name))
|
|
59
114
|
console.log(`[sync-extras] wrote docs/exxat-ds/${name}`)
|
|
115
|
+
wrote++
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Handbook tier (HANDBOOK · glossary · voice-and-tone · reference-implementations)
|
|
121
|
+
* lands in a nested `docs/exxat-ds/handbook/` so the patterns dir stays flat
|
|
122
|
+
* and the handbook's internal cross-links resolve.
|
|
123
|
+
*
|
|
124
|
+
* The bundled handbook files are stage-rewritten copies of the workspace
|
|
125
|
+
* source (`apps/web/docs/*.md`) — links to shipped patterns use `../`, links
|
|
126
|
+
* to unshipped neighbours (blueprints, token-taxonomy, etc.) are absolute
|
|
127
|
+
* GitHub URLs so they continue to work in the consumer's repo.
|
|
128
|
+
*/
|
|
129
|
+
function syncHandbook() {
|
|
130
|
+
if (!existsSync(SOURCES.handbook)) {
|
|
131
|
+
console.warn("[sync-extras] no packaged handbook (optional)")
|
|
132
|
+
skipped++
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
mkdirSync(DESTINATIONS.handbook, { recursive: true })
|
|
136
|
+
for (const name of readdirSync(SOURCES.handbook)) {
|
|
137
|
+
const src = join(SOURCES.handbook, name)
|
|
138
|
+
if (!statSync(src).isFile()) continue
|
|
139
|
+
cpSync(src, join(DESTINATIONS.handbook, name))
|
|
140
|
+
console.log(`[sync-extras] wrote docs/exxat-ds/handbook/${name}`)
|
|
141
|
+
wrote++
|
|
60
142
|
}
|
|
61
143
|
}
|
|
62
144
|
|
|
63
|
-
|
|
145
|
+
syncSkillsTo(".cursor/skills", DESTINATIONS.cursorSkills)
|
|
146
|
+
syncSkillsTo(".claude/skills", DESTINATIONS.claudeSkills)
|
|
147
|
+
syncRules()
|
|
64
148
|
syncPatterns()
|
|
65
|
-
|
|
149
|
+
syncHandbook()
|
|
150
|
+
|
|
151
|
+
console.log(`[sync-extras] Done. ${wrote} files / dirs written; ${skipped} optional sources missing.`)
|
|
152
|
+
console.log("[sync-extras] Product routes and app content were not modified.")
|
|
@@ -6,17 +6,56 @@ touching product routes or pages.
|
|
|
6
6
|
|
|
7
7
|
| Path in this tarball | After `exxat-ui sync-extras` in your repo |
|
|
8
8
|
|----------------------|-------------------------------------------|
|
|
9
|
-
| `CHANGELOG.md` (package root) |
|
|
10
|
-
| `cursor-skills/exxat-*` | `.cursor/skills/exxat-*` (replaced) |
|
|
11
|
-
| `
|
|
9
|
+
| `CHANGELOG.md` (package root) | Stays in `node_modules/…` — read for release notes; **`exxat-ui changelog`** prints it |
|
|
10
|
+
| `cursor-skills/exxat-*` | `.cursor/skills/exxat-*` **and** `.claude/skills/exxat-*` (replaced; same `SKILL.md` shape, two readers) |
|
|
11
|
+
| `cursor-rules/exxat-*.mdc` | `.cursor/rules/exxat-*.mdc` (replaced) — binding **MUST / MUST NOT** files |
|
|
12
|
+
| `patterns/*.md` | `docs/exxat-ds/*.md` (replaced) — pattern docs + **`consumer-upgrade-checklist.md`** |
|
|
13
|
+
| `handbook/*.md` | `docs/exxat-ds/handbook/*.md` (replaced) — `HANDBOOK`, `glossary`, `voice-and-tone`, `reference-implementations` |
|
|
14
|
+
|
|
15
|
+
The CLI is **namespaced**: it only touches files starting with `exxat-` (skills + rules)
|
|
16
|
+
so a consumer's own non-Exxat rules / skills are preserved. Product code under `app/`,
|
|
17
|
+
`pages/`, `src/`, plus the consumer's `AGENTS.md` and any of their own docs are never
|
|
18
|
+
modified.
|
|
12
19
|
|
|
13
20
|
**Components and hooks** still come only from `node_modules/@exxatdesignux/ui`
|
|
14
21
|
via normal semver installs — `sync-extras` does not copy TS source into your app.
|
|
15
22
|
|
|
16
|
-
**Maintainers** (this monorepo): when skills or web docs change, run:
|
|
23
|
+
**Maintainers** (this monorepo): when skills, rules, or web docs change, run:
|
|
17
24
|
|
|
18
25
|
```bash
|
|
19
26
|
pnpm --filter @exxatdesignux/ui vendor:consumer-extras
|
|
20
27
|
```
|
|
21
28
|
|
|
22
29
|
…then bump `packages/ui` version and publish so consumers get the new bundle.
|
|
30
|
+
|
|
31
|
+
## What lands where (full install map)
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
<consumer-repo>/
|
|
35
|
+
├── .cursor/
|
|
36
|
+
│ ├── rules/exxat-*.mdc ← binding MUST / MUST NOT (cursor-rules/)
|
|
37
|
+
│ └── skills/exxat-*/SKILL.md ← agent workflows + checklists (cursor-skills/)
|
|
38
|
+
├── .claude/
|
|
39
|
+
│ └── skills/exxat-*/SKILL.md ← Claude mirror of the same skills (cursor-skills/)
|
|
40
|
+
└── docs/
|
|
41
|
+
└── exxat-ds/
|
|
42
|
+
├── *-pattern.md ← narrative pattern docs (patterns/)
|
|
43
|
+
├── consumer-upgrade-checklist.md
|
|
44
|
+
└── handbook/
|
|
45
|
+
├── HANDBOOK.md
|
|
46
|
+
├── glossary.md
|
|
47
|
+
├── voice-and-tone.md
|
|
48
|
+
└── reference-implementations.md
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Note on handbook link rewriting
|
|
52
|
+
|
|
53
|
+
The bundled handbook files (`handbook/*.md`) are stage-rewritten copies of the
|
|
54
|
+
workspace source (`apps/web/docs/*.md`). Inter-doc links are normalised at
|
|
55
|
+
bundle time:
|
|
56
|
+
|
|
57
|
+
- Links to files we ship → relative (`./glossary.md`, `../data-views-pattern.md`)
|
|
58
|
+
- Links to files we don't ship (blueprints, `token-taxonomy.md`, `component-selection-guide.md`, `lib/*`, root `AGENTS.md`) → absolute GitHub URLs
|
|
59
|
+
|
|
60
|
+
This keeps every link clickable from the consumer's `docs/exxat-ds/handbook/`
|
|
61
|
+
directory without requiring them to clone the workspace.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — WCAG 2.1 AA, ARIA tablists, 24px targets, contrast; see AGENTS.md §8 and exxat-accessibility skill
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Exxat DS — accessibility (binding summary)
|
|
7
|
+
|
|
8
|
+
**Full checklist (ARIA, touch targets, color, sidebar badges, audit follow-ups):** repo **`.cursor/skills/exxat-accessibility/SKILL.md`**.
|
|
9
|
+
|
|
10
|
+
**Product rules in prose + checklist:** **`apps/web/AGENTS.md` §8**.
|
|
11
|
+
|
|
12
|
+
## Non‑negotiables
|
|
13
|
+
|
|
14
|
+
1. **Target:** **WCAG 2.1 Level AA** (2.2 where noted — e.g. target size).
|
|
15
|
+
2. **`role="tablist"`** — only **`role="tab"`** (or equivalent) as direct tab semantics. **MUST NOT** put `role="button"`, menus (`aria-haspopup`), or other controls **inside** the same `tablist` container.
|
|
16
|
+
3. **Composite view switchers** (tabs + per-tab menu + remove): use **`role="toolbar"`** + **`aria-label`**; **`aria-pressed`** on toggles — **MUST NOT** misuse `tab`/`tablist` for those controls.
|
|
17
|
+
4. **Touch targets (2.5.8):** interactive controls **≥ 24×24 CSS px** or **24px** spacing so hit areas do not overlap — use **`min-h-6 min-w-6` / `size-6`** for icon-only targets; avoid **`size-4`** as the sole target.
|
|
18
|
+
5. **Contrast:** normal text **≥ 4.5:1**; UI components / focus where required **≥ 3:1**; muted text on tinted surfaces use tokens against the correct surface (e.g. sidebar), not only `--background`.
|
|
19
|
+
6. **Minimum text size:** visible product copy **≥ 11px** — use **`text-xs`** or larger; **MUST NOT** use arbitrary Tailwind font sizes below that (see **`apps/web/AGENTS.md` §8.3**, **`apps/web/app/globals.css`** `--text-xs`).
|
|
20
|
+
7. **Dialogs / sheets / drawers:** must expose a **Title** (`DialogTitle` / `SheetTitle` / `DrawerTitle`); use **`sr-only`** if visually hidden (shadcn pattern).
|
|
21
|
+
8. **Format hints are persistent, not placeholders (SC 3.3.2 Labels or Instructions, 1.3.1).** Any field that expects a specific format — **date, time, phone, currency, ID pattern, GPA scale, URL, hours, unit-bearing numbers** — MUST show the format as **persistent helper text** via **`FormDescription`** (or the field's description slot). Placeholders disappear on focus and are unreliable for AT, so **MUST NOT** be the sole carrier of the format. Example: GPA → "Out of 4.0"; Date → "MM/DD/YYYY"; Phone → "+1 (555) 555-0100"; Student ID → "STU-YYYY-####". Pair with `inputMode`, `pattern`, or a picker primitive (e.g. `DatePickerField`) where applicable; never rely on a free-text date input.
|
|
22
|
+
9. **Every icon that communicates information MUST have a text alternative** — not just icon-only buttons. Three cases (SC 1.1.1 Non-text Content, 3.3.2 Labels or Instructions, 2.4.6 Headings and Labels):
|
|
23
|
+
|
|
24
|
+
- **A. Decorative icon next to text that already names it** (e.g. `<i class="fa-light fa-calendar-days" aria-hidden /> 12/14/2025 – 12/20/2025`) → icon MUST be **`aria-hidden`**; MUST NOT add `aria-label` (screen readers would announce the meaning twice). No tooltip needed.
|
|
25
|
+
- **B. Informational icon standing alone** — a calendar glyph used to mean "date range", clock for "updated at", pin for "site", graduation cap for "student", trending arrow for direction, status dot, icon-only chart legend — MUST pair **`role="img"` + `aria-label`** (or `aria-labelledby` on a wrapping element) with a visible **`Tooltip`** so sighted users who don't recognise the glyph learn the meaning. The icon wrapper MUST be keyboard-focusable (`tabIndex={0}` on the `span[role="img"]`) so the tooltip opens on focus.
|
|
26
|
+
- **C. Interactive icon-only button/link** (close `×`, chevron, overflow `⋯`, sort direction, filter chip dismiss, copy, Ask Leo toggle, row actions) → MUST pair **`aria-label`** on the `<button>` with a wrapping **`Tooltip`**. `aria-label` alone is NOT enough — sighted mouse users and keyboard users rely on the tooltip to discover what a bare icon does.
|
|
27
|
+
|
|
28
|
+
In all three cases, the inner `<i>` / `<svg>` MUST be `aria-hidden`; the accessible name lives on the wrapping element. Tooltip text MUST match the accessible name. Narrow exception: a chevron inside a labelled composite (`Select`, `Combobox`) where the parent control already names the whole thing. See **§8.6 (Case A/B/C)** in `AGENTS.md` and the accessibility skill.
|
|
29
|
+
10. **Keyboard shortcut hints inside buttons** MUST use **`<Kbd variant="bare">`** (no background, no border, inherits `currentColor` at 70%). The default `tile` variant is reserved for **tooltips** and **menu `shortcut=` slots**. Glue multi-key chords into one bare kbd (e.g. `<Kbd variant="bare">⌘⌥K</Kbd>`), not one tile per key. Reference: `Next` / `Back` buttons in `new-placement-form.tsx`; see `.cursor/rules/exxat-kbd-shortcuts.mdc`.
|
|
30
|
+
|
|
31
|
+
After changing **views toolbar** or **tab** UIs, re-run **axe** (or equivalent) on **Placements** (or the affected page).
|
|
32
|
+
|
|
33
|
+
**Charts:** Keyboard exploration uses **`ChartFigure`**; selected data points should have **visible** focus feedback — see skill **§ Charts (keyboard exploration)** and **`AGENTS.md` §4.3** (`chart-keyboard-selection`).
|
|
34
|
+
|
|
35
|
+
## See also
|
|
36
|
+
|
|
37
|
+
- **`apps/web/AGENTS.md`** §8 — accessibility in project context.
|
|
38
|
+
- **`.cursor/rules/exxat-kbd-shortcuts.mdc`** — shortcuts paired with `Kbd` hints.
|
|
39
|
+
- **`apps/web/.cursor/rules/exxat-dashboard-view-charts.mdc`** — Data view chart keyboard parity.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — board (kanban) cards; ListPageBoardCard, badges, primitives
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Exxat DS — board cards
|
|
7
|
+
|
|
8
|
+
**Authoritative detail:** **`apps/web/AGENTS.md` §4.4** and **`.cursor/skills/exxat-board-cards/SKILL.md`**.
|
|
9
|
+
|
|
10
|
+
## MUST
|
|
11
|
+
|
|
12
|
+
1. **Shell** — Use **`ListPageBoardCard`** from **`components/data-views/list-page-board-card.tsx`** for product board cards (same **`Card` `size="sm"`** treatment as Placements).
|
|
13
|
+
2. **Hierarchy** — **Title** (`ListPageBoardCardTitleRow`) → optional **avatar** (`ListPageBoardCardAvatar` on `trailing`) → **status row** (`ListPageBoardCardBadgeRow` + **`ListHubStatusBadge`** **`surface="board"`**) when the entity has status → **body** (`ListPageBoardCardBody`) with **`BoardCardTwoLineBlock`** / **`BoardCardIconRow`** (**`components/data-views/board-card-primitives.tsx`**).
|
|
14
|
+
3. **Status** — **Placements** (**`PLACEMENT_STATUS_*`** + **`StatusBadge`** in **`placements-table-cells.tsx`**), **Team / Compliance / Question bank** (**`ListHubStatusBadge`** + entity maps): all labels/tints/icons live in **`lib/list-status-badges.ts`**. **`surface="table"`** in grid/list rows, **`surface="board"`** on kanban cards. Prefer semantic **`LIST_HUB_STATUS_TINT_*`**. **MUST NOT** use **`uppercase`** on those chips.
|
|
15
|
+
4. **Simple column boards** — **`ListPageBoardTemplate`** + **`renderCard`**; compose **`ListPageBoardCard`** inside **`renderCard`**.
|
|
16
|
+
|
|
17
|
+
## MUST NOT
|
|
18
|
+
|
|
19
|
+
- Duplicate **`ListPageBoardCard`** with ad-hoc **`<button>`** + border/padding classes for the same hub pattern.
|
|
20
|
+
- Show **status** only as plain text in the **body** when the hub uses **status** elsewhere — use the **badge row** + shared maps.
|
|
21
|
+
|
|
22
|
+
## See also
|
|
23
|
+
|
|
24
|
+
- **`apps/web/AGENTS.md` §4.4**, **§13** checklist
|
|
25
|
+
- **`apps/web/docs/data-views-pattern.md`** — Board UI reuse
|
|
26
|
+
- **`lib/initials-from-name.ts`** — owner initials when mock has no `initials` field
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Do not duplicate parent navigation with a Back control when breadcrumbs exist
|
|
3
|
+
globs: apps/web/**/*.tsx
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Exxat DS — breadcrumbs and back navigation
|
|
8
|
+
|
|
9
|
+
## MUST NOT
|
|
10
|
+
|
|
11
|
+
When a page uses **`SiteHeader`** with **`breadcrumbs`** (a visible trail such as Patterns → Library → current title), **do not** add a **“Back to …”** link or button in the page body that goes to the same parent as the breadcrumb segment. Breadcrumbs already provide hierarchy and one-click navigation up the tree.
|
|
12
|
+
|
|
13
|
+
## MAY
|
|
14
|
+
|
|
15
|
+
- Rely on **`SiteHeader`** breadcrumbs only for returning to parent routes.
|
|
16
|
+
- Use a **single** explicit back affordance on flows that **omit** breadcrumbs by design (e.g. full-screen step, modal route) where product copy requires it.
|
|
17
|
+
|
|
18
|
+
## See also
|
|
19
|
+
|
|
20
|
+
- `components/site-header.tsx`
|
|
21
|
+
- `components/templates/primary-page-template.tsx`
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — when to use cards vs DataTable rows vs simple list rows.
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Exxat DS — cards vs rows vs lists
|
|
7
|
+
|
|
8
|
+
## MUST
|
|
9
|
+
|
|
10
|
+
1. **Dense, comparable records (10+)** — **`DataTable`** + **`ListPageTemplate`** + **`useTableState`** for primary hubs (**`exxat-data-tables.mdc`**).
|
|
11
|
+
2. **Board / tiles / visual browse** — **`ListPageBoardCard`** (and related shells), **`ListPageViewFrame`** for non-table bodies (**`exxat-board-cards.mdc`**, **`exxat-list-page-view-shells.mdc`**).
|
|
12
|
+
3. **One dataset** — Cards and tables read the **same** **`tableState.rows`**; no forked mock arrays (**`exxat-centralized-list-dataset.mdc`**).
|
|
13
|
+
|
|
14
|
+
## MUST NOT
|
|
15
|
+
|
|
16
|
+
- Replace a **primary data hub grid** with a **card wall** when users need column sort, filter chips, and export parity.
|
|
17
|
+
- Introduce a **second table stack** for the same entity.
|
|
18
|
+
|
|
19
|
+
## See also
|
|
20
|
+
|
|
21
|
+
- **`docs/card-vs-rows-pattern.md`** · **`.cursor/skills/exxat-card-vs-list-rows/SKILL.md`**
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Single source of truth for hub rows, KPIs, table properties, detail views, and shared view chrome — dataset + presentation consistency across DataListViewType surfaces
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Exxat DS — centralized list hub dataset + presentation
|
|
7
|
+
|
|
8
|
+
**Authoritative detail:** **`apps/web/AGENTS.md` §4.1** and **§4.5** (connected views + view shells), **`docs/data-views-pattern.md`**, **`.cursor/rules/exxat-list-page-connected-views.mdc`**, and **`.cursor/rules/exxat-list-page-view-shells.mdc`**. This rule tightens **where data lives** and **where reusable view chrome lives** so **table, list, board, dashboard, folder, panel, tree, and inspectors** never drift.
|
|
9
|
+
|
|
10
|
+
## Single dataset
|
|
11
|
+
|
|
12
|
+
1. **Rows** — One typed collection per entity (**`lib/mock/<entity>.ts`** until API wiring). **MUST NOT** maintain a second parallel array (e.g. “tree only” or “panel only”) with overlapping IDs unless it is a **derived view** (computed from the same source via selectors).
|
|
13
|
+
|
|
14
|
+
2. **`useTableState`** — One instance per tab table; **`tableState.rows`** is the **only** filtered/sorted row bag that **list / board / dashboard / folder / panel / tree** surfaces read. **MUST NOT** bypass filters by importing raw mock arrays into alternate views.
|
|
15
|
+
|
|
16
|
+
3. **Detail / inspector / finder / split-panel bodies** — Resolve the active record by **`id`** (or stable key) against **`tableState.rows`** (or the same upstream state that feeds **`useTableState`**). **MUST NOT** hydrate inspector fields from ad-hoc literals when the row already exists on the dataset.
|
|
17
|
+
|
|
18
|
+
4. **Columns & table “properties”** — Column defs **`accessorKey` / cell renderers** map to the **same** row interface as KPI helpers and board cards. **MUST NOT** introduce incompatible duplicate TypeScript shapes per view.
|
|
19
|
+
|
|
20
|
+
5. **`TablePropertiesDrawer`** — Stays wired to the **`DataTable`** that owns **`useTableState`** (**§4.2**): **`currentView`**, **`onViewChange`**, column visibility/density — all reflect manipulation of the **same** underlying rows.
|
|
21
|
+
|
|
22
|
+
6. **Labels / status / chips** — Prefer shared maps (**`lib/list-status-badges.ts`**, **`lib/data-list-view.ts`** tiles, entity-specific maps **next to** **`lib/mock/<entity>.ts`**). **MUST NOT** fork label strings or badge colors per view file.
|
|
23
|
+
|
|
24
|
+
7. **KPIs & charts** — **`MetricItem`** / **`MetricInsight`** builders take **`tableState.rows`** (or equivalent filtered list). Same inputs as the grid after search/filters.
|
|
25
|
+
|
|
26
|
+
## Centralized presentation (layout + components)
|
|
27
|
+
|
|
28
|
+
8. **`ListPageViewFrame`** — Non-**`DataTable`** view bodies (**folder**, **panel**, icon grids, comparable dashboard sections) **MUST** use **`ListPageViewFrame`** (and exported max-width constants) instead of copy-pasted **`mx-*` / `max-w-*`** per hub (**`exxat-list-page-view-shells.mdc`**).
|
|
29
|
+
|
|
30
|
+
9. **`components/data-views/`** — New **record-bearing** view layouts (**grids**, **OS folder**, **finder split**) **MUST** land as **generic** building blocks under **`data-views/`** with **`rows`**, **`getRowId`**, render props — hub **`TeamTable`** / **`QuestionBankTable`** **only** wires props and branch logic (**`AGENTS.md` §4.5**).
|
|
31
|
+
|
|
32
|
+
10. **Hub client composition** — One **`*-client.tsx`** owns **`useTableState`**, passes **`tableState.rows`** into every **`viewType`** branch, and mounts **template** slots (**metrics**, **export**, **`beforeSiteHeader`**) — **MUST NOT** split the same hub across multiple clients with different row sources.
|
|
33
|
+
|
|
34
|
+
## MUST NOT
|
|
35
|
+
|
|
36
|
+
- Ship alternate mock datasets per **`DataListViewType`** for the same hub without documenting them as **computed derivatives** of one canonical list.
|
|
37
|
+
- Duplicate entity fields in inspector-only types that contradict **`QuestionBankItem`** / **`Placement`** / etc.; extend the shared interface **once**.
|
|
38
|
+
|
|
39
|
+
## See also
|
|
40
|
+
|
|
41
|
+
- **`.cursor/skills/exxat-centralized-list-dataset/SKILL.md`** — workflow + checklist.
|
|
42
|
+
- **`.cursor/rules/exxat-table-properties-drawer.mdc`** — **`currentView`** / **`onViewChange`**.
|
|
43
|
+
- **`exxat-list-page-connected-views.mdc`** — toolbar + **`tableState.rows`**.
|
|
44
|
+
- **`exxat-list-page-view-shells.mdc`** — **`ListPageViewFrame`** + **`data-views/`** extraction.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Shared hubs — collaboration header, invite sheet, library access vs directory roles
|
|
3
|
+
globs: apps/web/components/**/*.{tsx,ts},apps/web/lib/**/*.{tsx,ts}
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Exxat DS — collaboration & access
|
|
8
|
+
|
|
9
|
+
**Authoritative detail:** **`apps/web/AGENTS.md` §4.7** and **`apps/web/docs/collaboration-access-pattern.md`**.
|
|
10
|
+
|
|
11
|
+
## MUST
|
|
12
|
+
|
|
13
|
+
1. **Shared hubs** — Use **`PageHeader`** **`variant="collaboration"`** with **`collaborators`** (`PageHeaderCollaborator[]`) and optional **`accessInfo`**.
|
|
14
|
+
2. **Header chrome** — **Empty roster:** outline **Add collaborator** (`COLLABORATION_HEADER_ADD_LABEL` / **`addCollaboratorLabel`**) opens the invite sheet. **Non-empty roster:** face rail only; faces and **`+N`** open the same sheet via **`onCollaboratorsOpen`**.
|
|
15
|
+
3. **Invite entry** — **⋯ More** → **Invite people** on the entity page header; opens **`InviteCollaboratorsDrawer`** (floating sheet, same family as **`ExportDrawer`**).
|
|
16
|
+
4. **State** — Prefer **`CollaborationAccessFlow`** on hub **`*-client.tsx`**; otherwise own **`collaborators`** + **`inviteOpen`**. Successful invite updates **`collaborators`** so the header and sheet roster match.
|
|
17
|
+
5. **Library access** — Labels and invite options from **`lib/collaborator-access.ts`**; trailing roster badge = access (Owner / Editor / Commenter / Viewer).
|
|
18
|
+
6. **Directory roles** — Optional **`roles`** on **`PageHeaderCollaborator`** as **`Badge variant="outline"`** chips (Faculty, Program coordinator, Director) — **not** library access.
|
|
19
|
+
7. **Roster layout** — One bordered list with row dividers; per row: **name**, **email**, role tags, access badge — **not** one card per person.
|
|
20
|
+
8. **Invite field** — **`FieldGroup`** + **`Field`**; combined email + access row uses **`InputGroup`** + **`InputGroupInput`** + **`InputGroupAddon`** with shadcn **`Select`** (**`SelectGroup`** / **`SelectItem`**, **`position="popper"`**); persistent **`FieldDescription`** for email format; **no** **`toast()`** (**`exxat-no-toast.mdc`**).
|
|
21
|
+
|
|
22
|
+
## MUST NOT
|
|
23
|
+
|
|
24
|
+
- A second invite control **beside** a populated face rail.
|
|
25
|
+
- Fork access enums/labels per hub — extend **`collaborator-access.ts`** once.
|
|
26
|
+
- Use **`Select`** inside **`InputGroupAddon`** without **`InputGroupInput`** / **`SelectGroup`**; put email above name in the roster (order is **name → email → role tags**).
|
|
27
|
+
|
|
28
|
+
## See also
|
|
29
|
+
|
|
30
|
+
- **`exxat-page-vs-drawer.mdc`** — invite is a **sheet**, not a new route.
|
|
31
|
+
- **`exxat-kbd-shortcuts.mdc`** — workflow **Cancel** / **Send invite** shortcuts on the sheet.
|
|
32
|
+
- **`exxat-question-bank-hub-header.mdc`** — Question bank library: when URL is **folder-scoped**, **⋯ More** also includes **Customize folder** (hub client hosts **`QuestionBankNewFolderSheet`**).
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — global command palette (⌘K) as search + quick AI vs Ask Leo for long answers.
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Exxat DS — global command palette (`CommandMenu`)
|
|
7
|
+
|
|
8
|
+
## Intent
|
|
9
|
+
|
|
10
|
+
- **`CommandMenu`** (**⌘K** / **Ctrl+K**) is **global search** (routes, library, patterns, AI starters, optional row data such as placements / student names) — see **`apps/web/AGENTS.md` §7.1** and **`apps/web/docs/command-menu-pattern.md`**.
|
|
11
|
+
- **Quick / lookup / short AI:** Prefer **results inside the palette** when the product can return compact answers or lightweight “research” without leaving the flow.
|
|
12
|
+
- **Long or complex answers:** **Ask Leo** side panel (**⌘⌥K** / **Ctrl+Alt+K**)—not forced into the palette.
|
|
13
|
+
|
|
14
|
+
## Implementation pointers
|
|
15
|
+
|
|
16
|
+
- Shell: `apps/web/components/command-menu.tsx`; config **`buildCommandMenuConfig()`** in **`apps/web/lib/command-menu-config.ts`**; optional **`dataGroups`** from **`apps/web/lib/command-menu-search-data.ts`** (e.g. **`getCommandMenuSearchDataGroups()`**), wired in **`apps/web/app/(app)/layout.tsx`**. Keep domain mapping out of the shell.
|
|
17
|
+
- Large indexes: set **`searchOnly: true`** on **`CommandMenuGroup`** so **`command-menu.tsx`** skips the group until the user types (avoids listing every row on open; cmdk shows all items when the search string is empty).
|
|
18
|
+
|
|
19
|
+
## See also
|
|
20
|
+
|
|
21
|
+
- **`apps/web/AGENTS.md` §7.1**
|
|
22
|
+
- **`exxat-kbd-shortcuts.mdc`** (⌘K vs ⌘⌥K)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Data view dashboard — centralized charts, storage, keyboard parity with gallery, edit layout, coach marks
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Exxat DS — Data view dashboard (list hubs)
|
|
7
|
+
|
|
8
|
+
**Authoritative detail:** **`apps/web/AGENTS.md` §4.3** and **`exxat-dashboard-view-charts`** (this rule).
|
|
9
|
+
|
|
10
|
+
## Two dashboard surfaces (do not fork chart engines)
|
|
11
|
+
|
|
12
|
+
| Surface | Entry | Shared building blocks |
|
|
13
|
+
|--------|--------|-------------------------|
|
|
14
|
+
| **Full-page dashboard** | `app/(app)/dashboard/page.tsx` | `DashboardTabs`, `ChartsOverview` / gallery demos in `charts-overview.tsx` |
|
|
15
|
+
| **Data tab** on Placements / Team / Compliance | `PlacementsTable`, `TeamTable`, `ComplianceTable` → `*DashboardChartsSection` | `ChartFigure`, `ChartCard`, `useChartVariant()`, `data-view-dashboard-charts*.tsx` |
|
|
16
|
+
|
|
17
|
+
**MUST NOT** duplicate “another” chart system for Data view — extend **`charts-overview`** patterns and **`lib/chart-keyboard-selection`**.
|
|
18
|
+
|
|
19
|
+
## MUST — accessibility & data
|
|
20
|
+
|
|
21
|
+
1. **`ChartFigure`** + **`ChartDataTable`** (`sr-only`) for every chart on the Data dashboard — same as **`charts-overview.tsx`**.
|
|
22
|
+
2. **`ChartCard`** wraps chart content; **`KeyMetrics`** **`variant="card"`** for the **`key-metrics`** tile; KPI count **1–4** persisted in layout.
|
|
23
|
+
|
|
24
|
+
## MUST — keyboard selection (parity with `/dashboard` gallery)
|
|
25
|
+
|
|
26
|
+
- Import **`CHART_KBD_ACTIVE_BAR`** and **`CHART_KBD_ACTIVE_PIE_SHAPE`** from **`@/lib/chart-keyboard-selection`**.
|
|
27
|
+
- **Bar** series: **`activeBar={CHART_KBD_ACTIVE_BAR}`** + **`activeIndex={activeIndex ?? undefined}`** (and **`Cell`** only for per-bar **fill**, not opacity-only selection).
|
|
28
|
+
- **Pie / donut:** **`activeShape={CHART_KBD_ACTIVE_PIE_SHAPE}`** + **`activeIndex`** + slice **`stroke`** / **`strokeWidth`** consistent with gallery donuts.
|
|
29
|
+
- **MUST NOT** use **`fillOpacity` dimming alone** on **`Cell`** as the sole “selected” state for keyboard exploration.
|
|
30
|
+
|
|
31
|
+
## MUST — customisation UX
|
|
32
|
+
|
|
33
|
+
- **Edit layout** control: **`aria-label="Edit dashboard layout"`** (toolbar pen-ruler). Coach marks may target **`[aria-label='Edit dashboard layout']`**.
|
|
34
|
+
- On-canvas: drag reorder, remove, width (half / full), chart type, add/remove cards, reset. **No** separate Sheet for layout.
|
|
35
|
+
- While **`layoutEditMode`**: hide **`DataTableToolbar`** (search / filters / Properties row); **Done** / **Cancel** on canvas.
|
|
36
|
+
|
|
37
|
+
## MUST — persistence (centralized)
|
|
38
|
+
|
|
39
|
+
- **One bundle:** **`lib/data-view-dashboard-storage.ts`** — key **`exxat-ds:data-view-dashboards:v1`**, scopes **`placements` | `team` | `compliance`**.
|
|
40
|
+
- Placements helpers: **`loadDashboardLayout`** / **`saveDashboardLayout`** in **`data-view-dashboard-charts.tsx`** (wrap storage API).
|
|
41
|
+
- **MUST NOT** introduce parallel **`localStorage`** keys for the same **`DashboardLayout`** shape without updating the storage module.
|
|
42
|
+
|
|
43
|
+
## SHOULD — coach marks
|
|
44
|
+
|
|
45
|
+
- Register **customize dashboard** flows in **`lib/coach-mark-registry.ts`**; shared copy in **`lib/dashboard-customize-coach-mark.ts`**.
|
|
46
|
+
- Use **`useCoachMark`** **`enabled`** / **`dependsOnDismissedFlowId`** when a tour only applies on **dashboard** view or after another flow (**`COACH_MARK_FLOW_COMPLETED_EVENT`**).
|
|
47
|
+
|
|
48
|
+
## Reference files
|
|
49
|
+
|
|
50
|
+
- `components/data-view-dashboard-charts.tsx` (Placements)
|
|
51
|
+
- `components/data-view-dashboard-charts-team.tsx`, `data-view-dashboard-charts-compliance.tsx`
|
|
52
|
+
- `components/placements-table.tsx` — dashboard tab body wires `PlacementsDashboardChartsSection` + layout-edit toolbar inline (no separate `DashboardShell` component)
|
|
53
|
+
- `lib/chart-keyboard-selection.ts`, `lib/data-view-dashboard-storage.ts`, `lib/dashboard-customize-coach-mark.ts`
|