@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
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# Exxat DS — Voice and tone
|
|
2
|
+
|
|
3
|
+
> The single biggest driver of "this feels like one product" — or "this feels random" — is **copy**. Empty states, errors, buttons, banners, validation, labels. This doc is the binding reference for all of it.
|
|
4
|
+
>
|
|
5
|
+
> **Audience:** designers writing copy, engineers shipping strings, AI agents drafting UI text. Reviewed in PR alongside `.cursor/rules/exxat-accessibility.mdc`.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Voice (the constant)
|
|
10
|
+
|
|
11
|
+
Exxat copy is **clear, direct, and respectful**. We talk to grown professionals (faculty, admins) and to students. We never:
|
|
12
|
+
|
|
13
|
+
- talk down (no "Oops!" / "Whoops" / "Uh-oh"),
|
|
14
|
+
- over-celebrate ("Awesome!" / "Great job!" / 🎉),
|
|
15
|
+
- guilt-trip ("You haven't… yet" / "Don't forget!"),
|
|
16
|
+
- hide the cause behind metaphor ("Something went wrong" with no detail),
|
|
17
|
+
- shout (sentence case everywhere; ALL CAPS only for system status acronyms).
|
|
18
|
+
|
|
19
|
+
We **always**:
|
|
20
|
+
|
|
21
|
+
- name the **thing**: the entity (placement, site, student), the action (Save, Invite, Export), the outcome (saved, sent, downloaded),
|
|
22
|
+
- say what **changed** or what **the user can do next**,
|
|
23
|
+
- use **present tense** (`Saving` / `Saved` / `Try again`), not past-aspirational (`We've saved your changes!`),
|
|
24
|
+
- use **active voice** (`Exxat couldn't save the placement.` not `The placement could not be saved.`).
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 2. Tone (the variable)
|
|
29
|
+
|
|
30
|
+
Tone shifts by surface. Same voice, different temperature.
|
|
31
|
+
|
|
32
|
+
| Surface | Tone | Example |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| Primary action label | Imperative, 1–3 words | "Save", "Invite people", "Export" |
|
|
35
|
+
| Empty state (filtered) | Helpful, names the filter | "No placements match the current filters. Clear them to see all 247." |
|
|
36
|
+
| Empty state (no data ever) | Welcoming, names the next step | "No placements yet. Add your first one to get started." |
|
|
37
|
+
| Inline validation | Neutral, says the rule | "Use the format MM/DD/YYYY." |
|
|
38
|
+
| Banner (informational) | Calm, explains why | "Read-only — this term has ended." |
|
|
39
|
+
| Banner (warning) | Direct, says the consequence | "Saving will overwrite 3 students' assignments." |
|
|
40
|
+
| Banner (error) | Honest, names the failure + next step | "Exxat couldn't reach the placement service. Try again, or check your network." |
|
|
41
|
+
| Dialog (destructive confirm) | Specific, names the object | "Delete placement P-2026-014? This cannot be undone." |
|
|
42
|
+
| Microcopy (helper, hints) | Brief, format-only | "Out of 4.0", "MM/DD/YYYY" |
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 3. Buttons
|
|
47
|
+
|
|
48
|
+
| Pattern | Use | Example |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| **Sentence case** | Always | `Save`, `Invite people`, `Export selection` |
|
|
51
|
+
| **Title case** | Never | ~~`Save Changes`~~ → `Save changes` |
|
|
52
|
+
| **Verb-first imperative** | Primary actions | `Save`, `Send invite`, `Create placement`, `Download CSV` |
|
|
53
|
+
| **No "Please"** | The button is the request | ~~`Please save`~~ → `Save` |
|
|
54
|
+
| **Don't repeat the field name** | Inline context is enough | ~~`Save changes to placement`~~ → `Save` |
|
|
55
|
+
| **"Cancel" not "Discard"** | Cancel returns to a safe state; Discard is only for destructive draft loss | dialog primary = `Save changes`; cancel = `Cancel` |
|
|
56
|
+
| **Loading verb** | When in-flight, swap to `…` form, preserve width | `Save` → `Saving…` |
|
|
57
|
+
| **Disabled tooltip** | Always say why | "Add at least one collaborator first." |
|
|
58
|
+
|
|
59
|
+
### Don't
|
|
60
|
+
|
|
61
|
+
| ❌ Don't | ✅ Do |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `Click here` | `Open placement` |
|
|
64
|
+
| `OK` | `Save`, `Got it`, `Continue` (the actual outcome) |
|
|
65
|
+
| `Submit` | `Send invite`, `Save changes`, `Create placement` |
|
|
66
|
+
| `Yes` / `No` in destructive dialogs | `Delete placement`, `Cancel` |
|
|
67
|
+
| `Loading…` for >2 s with no detail | `Loading placements…` (name the thing) |
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 4. Empty states
|
|
72
|
+
|
|
73
|
+
There are **three** kinds. The copy is different for each.
|
|
74
|
+
|
|
75
|
+
### 4a. Filter-empty ("zero matches")
|
|
76
|
+
|
|
77
|
+
The dataset has rows; the active filters hide all of them. Tell the user the filters are responsible and offer to clear them.
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
No placements match the current filters.
|
|
81
|
+
[Clear filters]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Rules:
|
|
85
|
+
|
|
86
|
+
- Name the entity ("placements", not "items").
|
|
87
|
+
- Mention "the current filters" — not just "no results".
|
|
88
|
+
- Surface a **Clear filters** action if the toolbar has any filters applied.
|
|
89
|
+
- If the search box is the only filter, the message becomes `No placements match "<query>".` (quote the query exactly).
|
|
90
|
+
|
|
91
|
+
### 4b. True-empty ("no data ever")
|
|
92
|
+
|
|
93
|
+
The dataset is empty for this user / scope. Tell them what to do next.
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
No placements yet.
|
|
97
|
+
Add your first one to start tracking student rotations.
|
|
98
|
+
[New placement]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Rules:
|
|
102
|
+
|
|
103
|
+
- One-line headline + one-line context.
|
|
104
|
+
- The CTA is the same imperative used elsewhere in the app — don't invent "Start now" for an empty state.
|
|
105
|
+
- If creation requires a precursor ("Add at least one site first"), say so and link to the precursor.
|
|
106
|
+
|
|
107
|
+
### 4c. Permission-empty ("not allowed to see")
|
|
108
|
+
|
|
109
|
+
The user can't see anything because of access. Don't show counts; don't suggest creating.
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
You don't have access to this hub.
|
|
113
|
+
Ask a coordinator to invite you with Viewer or higher access.
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Rules:
|
|
117
|
+
|
|
118
|
+
- Name the access level required.
|
|
119
|
+
- Don't show row counts the user can't see.
|
|
120
|
+
- Don't expose the existence of restricted records by mentioning "0 of N hidden".
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 5. Errors
|
|
125
|
+
|
|
126
|
+
Errors must answer three questions: **What happened? Why? What can the user do?**
|
|
127
|
+
|
|
128
|
+
| Element | Required? | Example |
|
|
129
|
+
|---|---|---|
|
|
130
|
+
| Subject | required | `Exxat couldn't save the placement.` |
|
|
131
|
+
| Cause | when knowable | `The site was deleted while you were editing.` |
|
|
132
|
+
| Next step | required | `Choose another site or refresh.` |
|
|
133
|
+
|
|
134
|
+
### Don't
|
|
135
|
+
|
|
136
|
+
- `Something went wrong.` — every word is empty. Replace with the subject + cause.
|
|
137
|
+
- `Error 500: Internal Server Error.` — leak the trace ID to logs, not the user. User-facing: "Exxat had a problem. Try again in a moment."
|
|
138
|
+
- Toasts for errors. Use `SystemBanner` (route-level) or inline `FormMessage` (field-level). See [`exxat-no-toast.mdc`](../../../.cursor/rules/exxat-no-toast.mdc).
|
|
139
|
+
|
|
140
|
+
### Field-level (`FormMessage`)
|
|
141
|
+
|
|
142
|
+
Match the rule, not the field name. Keep under 60 chars.
|
|
143
|
+
|
|
144
|
+
| Rule | Message |
|
|
145
|
+
|---|---|
|
|
146
|
+
| Required | `This is required.` |
|
|
147
|
+
| Format (date) | `Use MM/DD/YYYY.` |
|
|
148
|
+
| Format (phone) | `Use +1 (555) 555-0100.` |
|
|
149
|
+
| Range (number) | `Enter a number between 0 and 4.0.` |
|
|
150
|
+
| Unique | `That ID is already in use.` |
|
|
151
|
+
| Server | `Exxat couldn't save this field. Try again.` |
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## 6. Banners
|
|
156
|
+
|
|
157
|
+
We use **persistent banners** (not toasts) for all product feedback. See [`exxat-no-toast.mdc`](../../../.cursor/rules/exxat-no-toast.mdc) and `LocalBanner` / `SystemBanner` in `packages/ui`.
|
|
158
|
+
|
|
159
|
+
| Variant | Use | Lead with |
|
|
160
|
+
|---|---|---|
|
|
161
|
+
| `info` | Read-only / scope notice / "you're viewing X" | The fact: "Read-only — this term has ended." |
|
|
162
|
+
| `success` | Confirmed state change (long-lived; toasts forbidden) | The outcome: "Invite sent to 3 collaborators." |
|
|
163
|
+
| `warning` | Action will cause consequence | The consequence: "Saving will overwrite 3 students' assignments." |
|
|
164
|
+
| `error` | Recoverable failure | The subject + next step: "Exxat couldn't reach the placement service. Try again." |
|
|
165
|
+
| `destructive` | Pending destructive change | The object + reversibility: "This term will be archived in 7 days. Restore now to keep it." |
|
|
166
|
+
|
|
167
|
+
Rules:
|
|
168
|
+
|
|
169
|
+
- One banner per region. If two compete, the higher-severity wins.
|
|
170
|
+
- Pair a banner with an **action** when there's something the user can do (`[Restore]`, `[Try again]`, `[Open log]`).
|
|
171
|
+
- No exclamation marks. The variant color carries the tone.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 7. Status badges
|
|
176
|
+
|
|
177
|
+
Status labels live in [`lib/list-status-badges.ts`](https://github.com/ExxatDesign/Exxat-DS-Workspace/blob/main/apps/web/lib/list-status-badges.ts) and are shared across every surface (table, board card, list row). Don't fork labels per page.
|
|
178
|
+
|
|
179
|
+
| Type | Conventions | Example |
|
|
180
|
+
|---|---|---|
|
|
181
|
+
| Lifecycle | Title case, single word where possible | `Active`, `Draft`, `Archived`, `Pending` |
|
|
182
|
+
| Outcome | Past-tense verb when an action completed | `Approved`, `Rejected`, `Sent`, `Skipped` |
|
|
183
|
+
| Risk | Plain language, no jargon | `On track`, `At risk`, `Blocked` |
|
|
184
|
+
| Mandatory pairing | Color **AND** icon (WCAG 1.4.1 — never color alone) | green check, amber triangle, red circle-x |
|
|
185
|
+
|
|
186
|
+
Never: `OK`, `N/A`, `?` as a status label. Show the actual lifecycle state or `Unknown` with a tooltip describing how to resolve it.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## 8. KPI copy (the highest-stakes copy in the product)
|
|
191
|
+
|
|
192
|
+
KPIs read top-down: **label → value → trend chip → description**. The reader is scanning, not reading. Each line earns its place.
|
|
193
|
+
|
|
194
|
+
| Field | Rule | Example |
|
|
195
|
+
|---|---|---|
|
|
196
|
+
| `label` | Sentence case noun phrase. ≤ 24 chars. No "Total" prefix (the value is total by default). | `Active placements`, `New invites`, `Compliance issues` |
|
|
197
|
+
| `value` | The number. Use `tabular-nums`. Round to a sensible precision. Suffix unit (`%`, `hrs`) without space when natural. | `247`, `12 %`, `38 hrs` |
|
|
198
|
+
| `delta` | The **count** of change. Empty (`""` or `0`) hides the chip entirely. Never prose. | `+12`, `-3`, `+8 %` |
|
|
199
|
+
| `description` | The caption beneath the value row. Prose explaining what the number is or how it splits. Never the delta. | `vs last week`, `across 4 sites`, `left + right` |
|
|
200
|
+
|
|
201
|
+
See [`exxat-kpi-trends.mdc`](../../../.cursor/rules/exxat-kpi-trends.mdc) for the trend-polarity rules (when up means bad, set `trendPolarity: "lower_is_better"`).
|
|
202
|
+
|
|
203
|
+
### Don't
|
|
204
|
+
|
|
205
|
+
- `Total: 247` — drop "Total".
|
|
206
|
+
- `247 placements` in `value` — put `247` in `value`, `placements` is in `label`.
|
|
207
|
+
- `+5 (up 12 % from last week)` in `delta` — `delta = "+5"`, `description = "vs last week"`.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## 9. Form labels and helper text
|
|
212
|
+
|
|
213
|
+
| Element | Rule | Example |
|
|
214
|
+
|---|---|---|
|
|
215
|
+
| Label | Sentence case, no colon, no "*" — pair `*` decoration with `aria-required` only. | `Date of birth` |
|
|
216
|
+
| Required marker | Visible `*` (`aria-hidden`), programmatic `aria-required="true"`. | `Date of birth *` |
|
|
217
|
+
| Helper text | Persistent, format-first. Use `FormDescription`. Never placeholder-only. | `MM/DD/YYYY` |
|
|
218
|
+
| Placeholder | May mirror the format. Never the sole carrier. | `MM/DD/YYYY` |
|
|
219
|
+
| Inline error | Replaces helper while active. Match the rule, not the field name. | `Use MM/DD/YYYY.` |
|
|
220
|
+
| Unit in label vs description | Units go in description when context-dependent (`Out of 4.0` under "GPA"), in label when fixed (`Hours per week`). | — |
|
|
221
|
+
|
|
222
|
+
See [`exxat-accessibility.mdc`](../../../.cursor/rules/exxat-accessibility.mdc) §"Form fields — format hints MUST be persistent".
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## 10. Dates, times, numbers
|
|
227
|
+
|
|
228
|
+
| Type | Format | Example |
|
|
229
|
+
|---|---|---|
|
|
230
|
+
| Date (UI) | Locale-aware, defaults to `MM/DD/YYYY` for en-US | `12/14/2025` |
|
|
231
|
+
| Date range | Same format, en-dash with hair-thin spaces (or hyphen-minus in mono) | `12/14/2025 – 12/20/2025` |
|
|
232
|
+
| Time | 12-hour with am/pm in lowercase | `2:30 pm` |
|
|
233
|
+
| Relative time | Use sparingly; pair with absolute on hover/tooltip | `Updated 3 hours ago` (tip: `12/15/2025, 11:14 am`) |
|
|
234
|
+
| Numbers | `tabular-nums`. Use thousands separator (`12,547`) for ≥ 4 digits | `12,547` |
|
|
235
|
+
| Currency | Symbol-first, no space (`$1,200.00`) | `$1,200.00` |
|
|
236
|
+
| Percent | No space (`12 %` is wrong, use `12%`) | `12%` |
|
|
237
|
+
| Duration | Short form (`38 hrs`, `2 wks`) | `38 hrs` |
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## 11. Reviewer checklist (paste into PRs that touch copy)
|
|
242
|
+
|
|
243
|
+
- [ ] Sentence case (not title case) everywhere.
|
|
244
|
+
- [ ] No `Click here`, `Submit`, `OK`, `Loading…` without a noun.
|
|
245
|
+
- [ ] No toasts; banners or inline status instead.
|
|
246
|
+
- [ ] Empty state names the entity and surfaces a next action or "Clear filters".
|
|
247
|
+
- [ ] Errors answer: what / why / what now.
|
|
248
|
+
- [ ] Status labels come from `lib/list-status-badges.ts`, not new strings.
|
|
249
|
+
- [ ] KPI `delta` is a count; KPI `description` is prose. Polarity matches the metric.
|
|
250
|
+
- [ ] Form helper text is `FormDescription`, not placeholder-only.
|
|
251
|
+
- [ ] No exclamation marks outside `success` banners (and even those rarely).
|
|
252
|
+
- [ ] No emoji unless explicitly approved for that surface.
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## See also
|
|
257
|
+
|
|
258
|
+
- [`HANDBOOK.md`](./HANDBOOK.md) — where this fits in the docs map
|
|
259
|
+
- [`glossary.md`](./glossary.md) — vocabulary
|
|
260
|
+
- [`.cursor/rules/exxat-no-toast.mdc`](../../../.cursor/rules/exxat-no-toast.mdc) — why banners, not toasts
|
|
261
|
+
- [`.cursor/rules/exxat-kpi-trends.mdc`](../../../.cursor/rules/exxat-kpi-trends.mdc) — KPI delta vs description
|
|
262
|
+
- [`.cursor/rules/exxat-accessibility.mdc`](../../../.cursor/rules/exxat-accessibility.mdc) — format-hint persistence rule
|
|
@@ -42,4 +42,4 @@ The command menu is the app’s **global search and AI entry**—one place to ju
|
|
|
42
42
|
|
|
43
43
|
---
|
|
44
44
|
|
|
45
|
-
Keep this document aligned with **`
|
|
45
|
+
Keep this document aligned with **`apps/web/AGENTS.md` §7.1** when behavior or copy changes.
|
|
@@ -10,17 +10,17 @@ This document describes how list pages combine **views**, **toolbar** behavior,
|
|
|
10
10
|
|
|
11
11
|
| Need | Reuse | Where Placements uses it |
|
|
12
12
|
| --- | --- | --- |
|
|
13
|
-
| **View tabs** (table / list / board, lifecycle filters) | `ListPageTemplate` (`ViewTab`, `renderContent`, optional metrics + export) | `components/
|
|
14
|
-
| **Table shell** (search, filter bar, sort, grouping, columns, pagination) | `DataTable`, `DataTableToolbar`, `useTableState` | `components/data-table/`, `components/
|
|
15
|
-
| **Properties drawer** (display, columns, filters, sort, view type tiles) | `TablePropertiesDrawer` from `@/components/table-properties` | `DrawerToolbar` / list–board shells in `
|
|
16
|
-
| **Board / list** | `PlacementsBoardView`, `
|
|
13
|
+
| **View tabs** (table / list / board, lifecycle filters) | `ListPageTemplate` (`ViewTab`, `renderContent`, optional metrics + export) | `components/placements-client.tsx` + `components/templates/list-page.tsx` |
|
|
14
|
+
| **Table shell** (search, filter bar, sort, grouping, columns, pagination) | `DataTable`, `DataTableToolbar`, `useTableState` | `components/data-table/`, `components/placements-table.tsx` |
|
|
15
|
+
| **Properties drawer** (display, columns, filters, sort, view type tiles) | `TablePropertiesDrawer` from `@/components/table-properties` | `DrawerToolbar` / list–board shells in `placements-table.tsx` |
|
|
16
|
+
| **Board / list** | `PlacementsBoardView`, `PlacementListRowContent` (wrapped by `HubTable.renderListRow` → `DataRowList`) + same `useTableState` | `PlacementsTable` |
|
|
17
17
|
| **Page header** (primary CTA + More ⋯ + export) | `PlacementsPageHeader` or `TeamPageHeader` | `components/placements-page-header.tsx`, `components/team-page-header.tsx` |
|
|
18
|
-
| **Team page (primary template)** | `TeamClient` = `ListPageTemplate` + `KeyMetrics` + `TeamPageHeader` + `TeamTable` (same composition as `
|
|
18
|
+
| **Team page (primary template)** | `TeamClient` = `ListPageTemplate` + `KeyMetrics` + `TeamPageHeader` + `TeamTable` (same composition as `PlacementsClient`) | `components/team-client.tsx`, `lib/mock/team-kpi.ts` |
|
|
19
19
|
| **Team roster** | `TeamTable` — `DataTable` + `useTableState` + `TablePropertiesDrawer`; list/board/dashboard read **`tableState.rows`** | `components/team-table.tsx` |
|
|
20
20
|
| **Dashboard view (list tab)** | **`KeyMetrics`** (`variant="flat"` or `"card"`) — same KPI system as the template metrics strip; **do not** add ad-hoc `Card` grids for entity summaries | `TeamTable` dashboard branch, `lib/mock/team-kpi.ts` |
|
|
21
21
|
| **List hub metrics strip** | **`KeyMetrics variant="flat"`** — transparent cells, OKLCH brand glow only, border hairlines (**no** grey panel) | **`docs/kpi-flat-band-pattern.md`**, Placements / Team / Question bank clients |
|
|
22
22
|
| **Secondary panel chrome** | **`--secondary-panel-bg`** on **`NestedSecondaryPanelShell`** (lighter than sidebar, follows active product) | **`docs/shell-surface-elevation-pattern.md`**, Question bank |
|
|
23
|
-
| **Export** | `ExportDrawer` | `ListPageTemplate` export props; `
|
|
23
|
+
| **Export** | `ExportDrawer` | `ListPageTemplate` export props; `PlacementsClient` |
|
|
24
24
|
| **View body layout** (gutter + centered max-width for folder / icon / panel-style content) | **`ListPageViewFrame`** (`components/data-views/list-page-view-frame.tsx`, re-exported from `components/data-views`) | **`FolderGridView`** (uses the frame); **`QuestionBankOsFolderView`** — see **`AGENTS.md` §4.5** |
|
|
25
25
|
|
|
26
26
|
**Rules:** (1) Import and compose these components; pass **props** and **column defs** for your entity. (2) If something is missing, **extend the shared component** under `components/` (e.g. a new optional slot on `DataTableToolbar`) rather than copying markup into a single page. (3) Card-only or lightweight pages may use a smaller **Properties** sheet only when there is **no** table—otherwise use `TablePropertiesDrawer` with `DataTable` (see Team).
|
|
@@ -29,7 +29,7 @@ This document describes how list pages combine **views**, **toolbar** behavior,
|
|
|
29
29
|
|
|
30
30
|
If the page uses a **`DataTable`** (or equivalent data grid) as the main surface, it **must** sit under **`ListPageTemplate`** so users get the **views toolbar** (tabs, add view, per-tab settings). The default `ViewTab` entries should include at least one tab whose `viewType` is **`table`** when the primary experience is tabular data. **Do not** render a `DataTable` alone under only `PageHeader` + body.
|
|
31
31
|
|
|
32
|
-
- **Reference:** `
|
|
32
|
+
- **Reference:** `PlacementsClient` (Placements) and `TeamClient` (Team).
|
|
33
33
|
- **Rationale:** Consistent navigation, saved views, per-tab view type (table / list / board), and export at the template level.
|
|
34
34
|
|
|
35
35
|
## View layout frame (centered, reusable)
|
|
@@ -41,7 +41,7 @@ Non-table view branches (e.g. **folder** icon grid, **panel** finder, OS-style f
|
|
|
41
41
|
## Architecture
|
|
42
42
|
|
|
43
43
|
- **Page shell** — `ListPageTemplate` owns the views toolbar (tabs), optional metrics, and export drawer. Content for the active tab is rendered via `renderContent`.
|
|
44
|
-
- **Per–lifecycle (or category) data** — `
|
|
44
|
+
- **Per–lifecycle (or category) data** — `PlacementsTable` swaps **columns** and **row sets** based on `lifecycleTabId` (e.g. All / Upcoming / Ongoing / Completed). Each lifecycle tab gets its **own** `PlacementsTable` instance (`key={tab.filterId}`) so `useTableState` resets correctly when columns change.
|
|
45
45
|
- **Shared table state** — `useTableState` holds sort, search, filters, grouping, column order/visibility/pins/widths, row height, gridlines, and pagination flags. **Table**, **list**, **board**, and **dashboard** all read the same state so switching view type keeps behavior aligned.
|
|
46
46
|
|
|
47
47
|
## Mock data and connected views
|
|
@@ -59,10 +59,10 @@ Non-table view branches (e.g. **folder** icon grid, **panel** finder, OS-style f
|
|
|
59
59
|
|
|
60
60
|
| Concern | Table | List | Board | Dashboard (view tab) |
|
|
61
61
|
| --- | --- | --- | --- | --- |
|
|
62
|
-
| Primary surface | `DataTable` | `
|
|
62
|
+
| Primary surface | `DataTable` | `DataRowList` + entity row body (e.g. `PlacementListRowContent`) wired via `HubTable.renderListRow` | `PlacementsBoardView` / entity board | **`KeyMetrics`** (+ optional charts via shared dashboard components) |
|
|
63
63
|
| Data source | `useTableState` | **`tableState.rows`** | **`tableState.rows`** | **`tableState.rows`** via KPI helpers |
|
|
64
64
|
| Column headers / labels | `showColumnLabels` | Same source columns, list layout | Phase columns + optional board column menu | N/A (metrics from same columns/filters) |
|
|
65
|
-
| Row click / navigation | From `
|
|
65
|
+
| Row click / navigation | From `PlacementsTable` | From list shell | Card `onOpen` | N/A |
|
|
66
66
|
| Pagination | Optional `PaginationBar` + `CountSyncer` | Same pattern | N/A (board uses phase columns) | N/A |
|
|
67
67
|
|
|
68
68
|
## Toolbar and properties
|
|
@@ -99,10 +99,10 @@ Non-table view branches (e.g. **folder** icon grid, **panel** finder, OS-style f
|
|
|
99
99
|
|
|
100
100
|
## Persistence
|
|
101
101
|
|
|
102
|
-
- **Page-level** (`lib/data-list-persistence.ts`, key `exxat-ds:data-list:page:v1`): `displayOptions`, `showMetrics`, `tabs`, `activeTabId`. Loaded in `
|
|
103
|
-
- **Per–lifecycle tab** (key `exxat-ds:data-list:lifecycle:v1:<filterId>`): sort, search, filters, group by, column order/hidden/widths/pins/wrap, column menu search map, row height, gridlines, filter bar visibility, search popover, conditional rules, pagination toggle and page/size. Hydrated in `
|
|
102
|
+
- **Page-level** (`lib/data-list-persistence.ts`, key `exxat-ds:data-list:page:v1`): `displayOptions`, `showMetrics`, `tabs`, `activeTabId`. Loaded in `PlacementsClient` with `useLayoutEffect`, saved debounced on change. `ListPageTemplate` runs in **controlled** mode when `tabs` / `onTabsChange` / `activeTabId` / `onActiveTabChange` are passed.
|
|
103
|
+
- **Per–lifecycle tab** (key `exxat-ds:data-list:lifecycle:v1:<filterId>`): sort, search, filters, group by, column order/hidden/widths/pins/wrap, column menu search map, row height, gridlines, filter bar visibility, search popover, conditional rules, pagination toggle and page/size. Hydrated in `PlacementsTable` with `useLayoutEffect`; saved debounced when those fields change.
|
|
104
104
|
|
|
105
|
-
To add a new page: copy the `
|
|
105
|
+
To add a new page: copy the `PlacementsClient` pattern (controlled `ListPageTemplate` + storage key namespace) or call `schedulePageSave` / `scheduleLifecycleSave` with your own keys.
|
|
106
106
|
|
|
107
107
|
## Rules of thumb
|
|
108
108
|
|
|
@@ -158,7 +158,7 @@ Canonical rules: **`AGENTS.md` §6.4**, root **`.cursor/rules/exxat-page-vs-draw
|
|
|
158
158
|
|
|
159
159
|
When a route is a **primary** destination in nav (main hub for an entity) **and** the dataset is **large** or **highly interactive**:
|
|
160
160
|
|
|
161
|
-
- Use the **primary page template**: `ListPageTemplate` (tabs, metrics strip, export drawer at template level) + data client pattern as in **`
|
|
161
|
+
- Use the **primary page template**: `ListPageTemplate` (tabs, metrics strip, export drawer at template level) + data client pattern as in **`PlacementsClient`** / **`PlacementsTable`** — not a minimal `PageHeader`-only layout.
|
|
162
162
|
- Smaller satellite pages may use **`PageHeader` + section content**; once the dataset grows past the dense-list threshold, add the toolbar rules above and consider promoting to the full template if the page becomes a main hub.
|
|
163
163
|
|
|
164
164
|
---
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
/** Single-date calendar for table filters (YYYY-MM-DD). */
|
|
4
|
+
declare function FilterDateCalendar({ valueYmd, onChangeYmd, label, }: {
|
|
5
|
+
valueYmd: string | undefined;
|
|
6
|
+
onChangeYmd: (ymd: string | undefined) => void;
|
|
7
|
+
label: string;
|
|
8
|
+
}): react_jsx_runtime.JSX.Element;
|
|
9
|
+
|
|
10
|
+
export { FilterDateCalendar };
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React2 from 'react';
|
|
3
|
+
import { ChevronLeftIcon, ChevronRightIcon, ChevronDownIcon } from 'lucide-react';
|
|
4
|
+
import { getDefaultClassNames, DayPicker } from 'react-day-picker';
|
|
5
|
+
import { clsx } from 'clsx';
|
|
6
|
+
import { twMerge } from 'tailwind-merge';
|
|
7
|
+
import { cva } from 'class-variance-authority';
|
|
8
|
+
import { Slot } from 'radix-ui';
|
|
9
|
+
import { jsx } from 'react/jsx-runtime';
|
|
10
|
+
|
|
11
|
+
function cn(...inputs) {
|
|
12
|
+
return twMerge(clsx(inputs));
|
|
13
|
+
}
|
|
14
|
+
var buttonVariants = cva(
|
|
15
|
+
"group/button inline-flex shrink-0 cursor-pointer items-center justify-center rounded-md border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
16
|
+
{
|
|
17
|
+
variants: {
|
|
18
|
+
variant: {
|
|
19
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/80",
|
|
20
|
+
outline: "border-input bg-background hover:bg-interactive-hover hover:text-interactive-hover-foreground aria-expanded:bg-interactive-hover aria-expanded:text-interactive-hover-foreground dark:bg-input/15 dark:hover:bg-input/25",
|
|
21
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
22
|
+
ghost: "hover:bg-interactive-hover hover:text-interactive-hover-foreground aria-expanded:bg-interactive-hover aria-expanded:text-interactive-hover-foreground dark:hover:bg-interactive-hover-subtle",
|
|
23
|
+
destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
|
|
24
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
25
|
+
},
|
|
26
|
+
size: {
|
|
27
|
+
default: "h-9 gap-1.5 px-3 has-data-[icon=inline-end]:pe-2.5 has-data-[icon=inline-start]:ps-2.5",
|
|
28
|
+
xs: "h-6 gap-1 px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
29
|
+
sm: "h-8 gap-1 px-3 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pe-2 has-data-[icon=inline-start]:ps-2 [&_svg:not([class*='size-'])]:size-3.5",
|
|
30
|
+
lg: "h-10 gap-1.5 px-4 has-data-[icon=inline-end]:pe-3.5 has-data-[icon=inline-start]:ps-3.5",
|
|
31
|
+
icon: "size-9",
|
|
32
|
+
"icon-xs": "size-6 in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
|
|
33
|
+
"icon-sm": "size-8 in-data-[slot=button-group]:rounded-lg",
|
|
34
|
+
"icon-lg": "size-10"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
defaultVariants: {
|
|
38
|
+
variant: "default",
|
|
39
|
+
size: "default"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
var Button = React2.forwardRef(({ className, variant = "default", size = "default", asChild = false, ...props }, ref) => {
|
|
44
|
+
const Comp = asChild ? Slot.Root : "button";
|
|
45
|
+
return /* @__PURE__ */ jsx(
|
|
46
|
+
Comp,
|
|
47
|
+
{
|
|
48
|
+
ref,
|
|
49
|
+
"data-slot": "button",
|
|
50
|
+
"data-variant": variant,
|
|
51
|
+
"data-size": size,
|
|
52
|
+
className: cn(buttonVariants({ variant, size }), className),
|
|
53
|
+
...props
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
Button.displayName = "Button";
|
|
58
|
+
function Calendar({
|
|
59
|
+
className,
|
|
60
|
+
classNames,
|
|
61
|
+
showOutsideDays = true,
|
|
62
|
+
captionLayout = "label",
|
|
63
|
+
buttonVariant = "ghost",
|
|
64
|
+
formatters,
|
|
65
|
+
components,
|
|
66
|
+
...props
|
|
67
|
+
}) {
|
|
68
|
+
const defaultClassNames = getDefaultClassNames();
|
|
69
|
+
return /* @__PURE__ */ jsx(
|
|
70
|
+
DayPicker,
|
|
71
|
+
{
|
|
72
|
+
showOutsideDays,
|
|
73
|
+
className: cn(
|
|
74
|
+
"group/calendar bg-background p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
|
|
75
|
+
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
|
|
76
|
+
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
|
|
77
|
+
className
|
|
78
|
+
),
|
|
79
|
+
captionLayout,
|
|
80
|
+
formatters: {
|
|
81
|
+
formatMonthDropdown: (date) => date.toLocaleString("default", { month: "short" }),
|
|
82
|
+
...formatters
|
|
83
|
+
},
|
|
84
|
+
classNames: {
|
|
85
|
+
root: cn("w-fit", defaultClassNames.root),
|
|
86
|
+
months: cn(
|
|
87
|
+
"relative flex flex-col gap-4 md:flex-row",
|
|
88
|
+
defaultClassNames.months
|
|
89
|
+
),
|
|
90
|
+
month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
|
|
91
|
+
nav: cn(
|
|
92
|
+
"absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
|
|
93
|
+
defaultClassNames.nav
|
|
94
|
+
),
|
|
95
|
+
button_previous: cn(
|
|
96
|
+
buttonVariants({ variant: buttonVariant }),
|
|
97
|
+
"size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
|
|
98
|
+
defaultClassNames.button_previous
|
|
99
|
+
),
|
|
100
|
+
button_next: cn(
|
|
101
|
+
buttonVariants({ variant: buttonVariant }),
|
|
102
|
+
"size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
|
|
103
|
+
defaultClassNames.button_next
|
|
104
|
+
),
|
|
105
|
+
month_caption: cn(
|
|
106
|
+
"flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
|
|
107
|
+
defaultClassNames.month_caption
|
|
108
|
+
),
|
|
109
|
+
dropdowns: cn(
|
|
110
|
+
"flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
|
|
111
|
+
defaultClassNames.dropdowns
|
|
112
|
+
),
|
|
113
|
+
dropdown_root: cn(
|
|
114
|
+
"relative rounded-md border border-input shadow-xs has-focus:border-ring has-focus:ring-[3px] has-focus:ring-ring/50",
|
|
115
|
+
defaultClassNames.dropdown_root
|
|
116
|
+
),
|
|
117
|
+
dropdown: cn(
|
|
118
|
+
"absolute inset-0 bg-popover opacity-0",
|
|
119
|
+
defaultClassNames.dropdown
|
|
120
|
+
),
|
|
121
|
+
caption_label: cn(
|
|
122
|
+
"font-medium select-none",
|
|
123
|
+
captionLayout === "label" ? "text-sm" : "flex h-8 items-center gap-1 rounded-md pe-1 ps-2 text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
|
|
124
|
+
defaultClassNames.caption_label
|
|
125
|
+
),
|
|
126
|
+
table: "w-full border-collapse",
|
|
127
|
+
weekdays: cn("flex", defaultClassNames.weekdays),
|
|
128
|
+
weekday: cn(
|
|
129
|
+
"flex-1 rounded-md text-[0.8rem] font-normal text-muted-foreground select-none",
|
|
130
|
+
defaultClassNames.weekday
|
|
131
|
+
),
|
|
132
|
+
week: cn("mt-2 flex w-full", defaultClassNames.week),
|
|
133
|
+
week_number_header: cn(
|
|
134
|
+
"w-(--cell-size) select-none",
|
|
135
|
+
defaultClassNames.week_number_header
|
|
136
|
+
),
|
|
137
|
+
week_number: cn(
|
|
138
|
+
"text-[0.8rem] text-muted-foreground select-none",
|
|
139
|
+
defaultClassNames.week_number
|
|
140
|
+
),
|
|
141
|
+
day: cn(
|
|
142
|
+
"group/day relative aspect-square h-full w-full p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-md",
|
|
143
|
+
props.showWeekNumber ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-md" : "[&:first-child[data-selected=true]_button]:rounded-l-md",
|
|
144
|
+
defaultClassNames.day
|
|
145
|
+
),
|
|
146
|
+
range_start: cn(
|
|
147
|
+
"rounded-l-md bg-accent",
|
|
148
|
+
defaultClassNames.range_start
|
|
149
|
+
),
|
|
150
|
+
range_middle: cn("rounded-none", defaultClassNames.range_middle),
|
|
151
|
+
range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end),
|
|
152
|
+
today: cn(
|
|
153
|
+
"rounded-md bg-accent text-accent-foreground data-[selected=true]:rounded-none",
|
|
154
|
+
defaultClassNames.today
|
|
155
|
+
),
|
|
156
|
+
outside: cn(
|
|
157
|
+
"text-muted-foreground aria-selected:text-muted-foreground",
|
|
158
|
+
defaultClassNames.outside
|
|
159
|
+
),
|
|
160
|
+
disabled: cn(
|
|
161
|
+
"text-muted-foreground opacity-50",
|
|
162
|
+
defaultClassNames.disabled
|
|
163
|
+
),
|
|
164
|
+
hidden: cn("invisible", defaultClassNames.hidden),
|
|
165
|
+
...classNames
|
|
166
|
+
},
|
|
167
|
+
components: {
|
|
168
|
+
Root: ({ className: className2, rootRef, ...props2 }) => {
|
|
169
|
+
return /* @__PURE__ */ jsx(
|
|
170
|
+
"div",
|
|
171
|
+
{
|
|
172
|
+
"data-slot": "calendar",
|
|
173
|
+
ref: rootRef,
|
|
174
|
+
className: cn(className2),
|
|
175
|
+
...props2
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
},
|
|
179
|
+
Chevron: ({ className: className2, orientation, ...props2 }) => {
|
|
180
|
+
if (orientation === "left") {
|
|
181
|
+
return /* @__PURE__ */ jsx(ChevronLeftIcon, { className: cn("size-4", className2), ...props2 });
|
|
182
|
+
}
|
|
183
|
+
if (orientation === "right") {
|
|
184
|
+
return /* @__PURE__ */ jsx(
|
|
185
|
+
ChevronRightIcon,
|
|
186
|
+
{
|
|
187
|
+
className: cn("size-4", className2),
|
|
188
|
+
...props2
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
return /* @__PURE__ */ jsx(ChevronDownIcon, { className: cn("size-4", className2), ...props2 });
|
|
193
|
+
},
|
|
194
|
+
DayButton: CalendarDayButton,
|
|
195
|
+
WeekNumber: ({ children, ...props2 }) => {
|
|
196
|
+
return /* @__PURE__ */ jsx("td", { ...props2, children: /* @__PURE__ */ jsx("div", { className: "flex size-(--cell-size) items-center justify-center text-center", children }) });
|
|
197
|
+
},
|
|
198
|
+
...components
|
|
199
|
+
},
|
|
200
|
+
...props
|
|
201
|
+
}
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
function CalendarDayButton({
|
|
205
|
+
className,
|
|
206
|
+
day,
|
|
207
|
+
modifiers,
|
|
208
|
+
...props
|
|
209
|
+
}) {
|
|
210
|
+
const defaultClassNames = getDefaultClassNames();
|
|
211
|
+
const ref = React2.useRef(null);
|
|
212
|
+
React2.useEffect(() => {
|
|
213
|
+
if (modifiers.focused) ref.current?.focus();
|
|
214
|
+
}, [modifiers.focused]);
|
|
215
|
+
return /* @__PURE__ */ jsx(
|
|
216
|
+
Button,
|
|
217
|
+
{
|
|
218
|
+
ref,
|
|
219
|
+
variant: "ghost",
|
|
220
|
+
size: "icon",
|
|
221
|
+
"data-day": day.date.toLocaleDateString(),
|
|
222
|
+
"data-selected-single": modifiers.selected && !modifiers.range_start && !modifiers.range_end && !modifiers.range_middle,
|
|
223
|
+
"data-range-start": modifiers.range_start,
|
|
224
|
+
"data-range-end": modifiers.range_end,
|
|
225
|
+
"data-range-middle": modifiers.range_middle,
|
|
226
|
+
className: cn(
|
|
227
|
+
"flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-accent-foreground [&>span]:text-xs [&>span]:opacity-70",
|
|
228
|
+
defaultClassNames.day,
|
|
229
|
+
className
|
|
230
|
+
),
|
|
231
|
+
...props
|
|
232
|
+
}
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// src/lib/date-filter.ts
|
|
237
|
+
function ymdToLocalDate(ymd) {
|
|
238
|
+
if (!ymd || !/^\d{4}-\d{2}-\d{2}$/.test(ymd)) return void 0;
|
|
239
|
+
const [y, m, d] = ymd.split("-").map(Number);
|
|
240
|
+
return new Date(y, m - 1, d, 12, 0, 0, 0);
|
|
241
|
+
}
|
|
242
|
+
function localDateToYmd(d) {
|
|
243
|
+
const y = d.getFullYear();
|
|
244
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
245
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
246
|
+
return `${y}-${m}-${day}`;
|
|
247
|
+
}
|
|
248
|
+
function FilterDateCalendar({
|
|
249
|
+
valueYmd,
|
|
250
|
+
onChangeYmd,
|
|
251
|
+
label
|
|
252
|
+
}) {
|
|
253
|
+
const [timeZone, setTimeZone] = React2.useState();
|
|
254
|
+
React2.useEffect(() => {
|
|
255
|
+
setTimeZone(Intl.DateTimeFormat().resolvedOptions().timeZone);
|
|
256
|
+
}, []);
|
|
257
|
+
return /* @__PURE__ */ jsx(
|
|
258
|
+
"div",
|
|
259
|
+
{
|
|
260
|
+
className: "rounded-lg border border-border overflow-hidden",
|
|
261
|
+
role: "group",
|
|
262
|
+
"aria-label": label,
|
|
263
|
+
children: /* @__PURE__ */ jsx(
|
|
264
|
+
Calendar,
|
|
265
|
+
{
|
|
266
|
+
mode: "single",
|
|
267
|
+
selected: ymdToLocalDate(valueYmd),
|
|
268
|
+
onSelect: (d) => onChangeYmd(d ? localDateToYmd(d) : void 0),
|
|
269
|
+
captionLayout: "dropdown",
|
|
270
|
+
timeZone,
|
|
271
|
+
className: "rounded-lg border-0"
|
|
272
|
+
}
|
|
273
|
+
)
|
|
274
|
+
}
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export { FilterDateCalendar };
|
|
279
|
+
//# sourceMappingURL=filter-date-calendar.js.map
|
|
280
|
+
//# sourceMappingURL=filter-date-calendar.js.map
|