@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
|
@@ -68,6 +68,7 @@ import {
|
|
|
68
68
|
} from "@/components/ui/dropdown-menu"
|
|
69
69
|
import { Tip } from "@/components/ui/tip"
|
|
70
70
|
import { PageHeader } from "@/components/page-header"
|
|
71
|
+
import { NewFocusTemplate } from "@/components/templates/new-focus-template"
|
|
71
72
|
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
|
|
72
73
|
import {
|
|
73
74
|
SelectionTileGrid,
|
|
@@ -270,6 +271,10 @@ type QuestionFormValues = z.infer<typeof questionSchema>
|
|
|
270
271
|
|
|
271
272
|
const OPTION_LETTERS = ["A", "B", "C", "D", "E", "F", "G", "H"] as const
|
|
272
273
|
|
|
274
|
+
// Runtime ID generators — called from user actions (Add option, Add pair, etc.) which
|
|
275
|
+
// only fire AFTER hydration, so `Math.random()` is safe here. NEVER call these from
|
|
276
|
+
// `buildInitial*` factories below — those run during the server render too and a
|
|
277
|
+
// random ID would mismatch the client tree on hydration.
|
|
273
278
|
function newOptionId() {
|
|
274
279
|
return `opt-${Math.random().toString(36).slice(2, 9)}`
|
|
275
280
|
}
|
|
@@ -286,16 +291,20 @@ function newBlankId() {
|
|
|
286
291
|
return `blk-${Math.random().toString(36).slice(2, 9)}`
|
|
287
292
|
}
|
|
288
293
|
|
|
294
|
+
// SSR-safe defaults — deterministic, index-based IDs so the server and client render
|
|
295
|
+
// the same `id` / `key` / DOM attributes (e.g. `Checkbox` IDs derived from `option.id`).
|
|
296
|
+
// Hydration mismatch issue: `Math.random()` produces different values per call, so
|
|
297
|
+
// using it in initial defaults makes every render emit a different tree.
|
|
289
298
|
function buildInitialOptions(type: AuthoringQuestionType): QuestionFormValues["options"] {
|
|
290
299
|
if (type === "true_false") {
|
|
291
300
|
return [
|
|
292
|
-
{ id:
|
|
293
|
-
{ id:
|
|
301
|
+
{ id: "opt-true", text: "True", isCorrect: false, rationale: "" },
|
|
302
|
+
{ id: "opt-false", text: "False", isCorrect: false, rationale: "" },
|
|
294
303
|
]
|
|
295
304
|
}
|
|
296
305
|
if (type === "mcq_single" || type === "mcq_multiple") {
|
|
297
|
-
return Array.from({ length: AUTHORING_DEFAULT_OPTION_COUNT }, () => ({
|
|
298
|
-
id:
|
|
306
|
+
return Array.from({ length: AUTHORING_DEFAULT_OPTION_COUNT }, (_, i) => ({
|
|
307
|
+
id: `opt-init-${i + 1}`,
|
|
299
308
|
text: "",
|
|
300
309
|
isCorrect: false,
|
|
301
310
|
rationale: "",
|
|
@@ -305,13 +314,20 @@ function buildInitialOptions(type: AuthoringQuestionType): QuestionFormValues["o
|
|
|
305
314
|
}
|
|
306
315
|
|
|
307
316
|
function buildInitialPairs(): QuestionFormValues["pairs"] {
|
|
308
|
-
return Array.from({ length: 3 }, () => ({
|
|
317
|
+
return Array.from({ length: 3 }, (_, i) => ({
|
|
318
|
+
id: `pair-init-${i + 1}`,
|
|
319
|
+
left: "",
|
|
320
|
+
right: "",
|
|
321
|
+
}))
|
|
309
322
|
}
|
|
310
323
|
function buildInitialOrderedItems(): QuestionFormValues["orderedItems"] {
|
|
311
|
-
return Array.from({ length: 4 }, () => ({
|
|
324
|
+
return Array.from({ length: 4 }, (_, i) => ({
|
|
325
|
+
id: `ord-init-${i + 1}`,
|
|
326
|
+
text: "",
|
|
327
|
+
}))
|
|
312
328
|
}
|
|
313
329
|
function buildInitialFillBlankAnswers(): QuestionFormValues["fillBlankAnswers"] {
|
|
314
|
-
return [{ id:
|
|
330
|
+
return [{ id: "blk-init-1", accepted: "" }]
|
|
315
331
|
}
|
|
316
332
|
|
|
317
333
|
function folderBreadcrumb(folderId: string, folders: QuestionBankFolder[]): string {
|
|
@@ -419,7 +435,7 @@ function BuilderSection({
|
|
|
419
435
|
<h2 className="text-sm font-semibold text-foreground">
|
|
420
436
|
{title}
|
|
421
437
|
{required ? (
|
|
422
|
-
<span className="
|
|
438
|
+
<span className="ms-1 text-destructive" aria-hidden="true">
|
|
423
439
|
*
|
|
424
440
|
</span>
|
|
425
441
|
) : null}
|
|
@@ -764,6 +780,8 @@ interface NewQuestionComposerProps {
|
|
|
764
780
|
defaultFolderId?: string
|
|
765
781
|
/** Where to send the user when they cancel or save. */
|
|
766
782
|
backHref: string
|
|
783
|
+
/** Label displayed in the `SiteHeader` back-icon (e.g. "Back to Favorites"). */
|
|
784
|
+
backLabel?: string
|
|
767
785
|
folders?: QuestionBankFolder[]
|
|
768
786
|
}
|
|
769
787
|
|
|
@@ -771,6 +789,7 @@ export function NewQuestionComposer({
|
|
|
771
789
|
draftQuestionId,
|
|
772
790
|
defaultFolderId,
|
|
773
791
|
backHref,
|
|
792
|
+
backLabel = "Back",
|
|
774
793
|
folders = DEFAULT_QUESTION_BANK_FOLDERS,
|
|
775
794
|
}: NewQuestionComposerProps) {
|
|
776
795
|
const router = useRouter()
|
|
@@ -1089,6 +1108,292 @@ export function NewQuestionComposer({
|
|
|
1089
1108
|
</>
|
|
1090
1109
|
)
|
|
1091
1110
|
|
|
1111
|
+
// Inspector body — wired into `NewFocusTemplate.form-inspector` via the `inspector`
|
|
1112
|
+
// render-prop. Renders both the collapsed-rail and expanded-card states so the composer
|
|
1113
|
+
// keeps its existing UX while the template owns the outer `<aside>` chrome (width
|
|
1114
|
+
// transition + sticky positioning).
|
|
1115
|
+
const inspectorContent = (
|
|
1116
|
+
<>
|
|
1117
|
+
{!inspectorOpen ? (
|
|
1118
|
+
<div
|
|
1119
|
+
className={cn(
|
|
1120
|
+
"flex w-12 flex-col items-center gap-1 rounded-xl bg-[var(--secondary-panel-bg)] px-1.5 py-2 ring-1 ring-border shadow-sm",
|
|
1121
|
+
)}
|
|
1122
|
+
>
|
|
1123
|
+
<Tip side="left" label="Show inspector">
|
|
1124
|
+
<button
|
|
1125
|
+
type="button"
|
|
1126
|
+
onClick={() => setInspectorOpen(true)}
|
|
1127
|
+
aria-label="Show inspector"
|
|
1128
|
+
aria-expanded={false}
|
|
1129
|
+
className={cn(
|
|
1130
|
+
"flex size-9 shrink-0 items-center justify-center rounded-md text-sidebar-foreground transition-colors",
|
|
1131
|
+
"hover:bg-sidebar-accent/50",
|
|
1132
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset",
|
|
1133
|
+
)}
|
|
1134
|
+
>
|
|
1135
|
+
<i
|
|
1136
|
+
className="fa-light fa-arrow-left-to-line text-[15px] leading-none"
|
|
1137
|
+
aria-hidden="true"
|
|
1138
|
+
/>
|
|
1139
|
+
</button>
|
|
1140
|
+
</Tip>
|
|
1141
|
+
</div>
|
|
1142
|
+
) : (
|
|
1143
|
+
<div
|
|
1144
|
+
className={cn(
|
|
1145
|
+
"flex flex-col gap-6 rounded-xl border border-border bg-card p-4",
|
|
1146
|
+
"lg:max-h-[calc(100dvh-var(--header-height)-2rem)] lg:overflow-y-auto lg:overscroll-y-contain",
|
|
1147
|
+
)}
|
|
1148
|
+
>
|
|
1149
|
+
{/* Inspector header — title + collapse control. */}
|
|
1150
|
+
<div className="flex items-center justify-between">
|
|
1151
|
+
<p className="text-xs font-medium text-muted-foreground">
|
|
1152
|
+
Inspector
|
|
1153
|
+
</p>
|
|
1154
|
+
<Tip side="bottom" label="Hide inspector">
|
|
1155
|
+
<Button
|
|
1156
|
+
type="button"
|
|
1157
|
+
variant="ghost"
|
|
1158
|
+
size="icon-sm"
|
|
1159
|
+
onClick={() => setInspectorOpen(false)}
|
|
1160
|
+
aria-label="Hide inspector"
|
|
1161
|
+
aria-expanded={true}
|
|
1162
|
+
>
|
|
1163
|
+
<i
|
|
1164
|
+
className="fa-light fa-arrow-right-to-line"
|
|
1165
|
+
aria-hidden="true"
|
|
1166
|
+
/>
|
|
1167
|
+
</Button>
|
|
1168
|
+
</Tip>
|
|
1169
|
+
</div>
|
|
1170
|
+
|
|
1171
|
+
{/* Question format — first-time landing shows the full
|
|
1172
|
+
`SelectionTileGrid` (matches the "File format" pattern
|
|
1173
|
+
in `ExportDrawer`). After the author picks once it
|
|
1174
|
+
collapses into a single selected-type tile with a
|
|
1175
|
+
"Change" affordance that re-opens the grid. */}
|
|
1176
|
+
<FormField
|
|
1177
|
+
control={form.control}
|
|
1178
|
+
name="type"
|
|
1179
|
+
render={({ field }) => (
|
|
1180
|
+
<FormItem>
|
|
1181
|
+
<FormControl>
|
|
1182
|
+
{typeChooserOpen ? (
|
|
1183
|
+
<SelectionTileGrid
|
|
1184
|
+
sectionLabel="Question format"
|
|
1185
|
+
options={QUESTION_TYPE_TILES}
|
|
1186
|
+
columns={2}
|
|
1187
|
+
value={field.value}
|
|
1188
|
+
onValueChange={v => changeType(v)}
|
|
1189
|
+
interaction="radio"
|
|
1190
|
+
idPrefix="qb-format"
|
|
1191
|
+
itemVariant="outline"
|
|
1192
|
+
itemMotion="pop"
|
|
1193
|
+
/>
|
|
1194
|
+
) : (
|
|
1195
|
+
<div className="flex flex-col gap-2">
|
|
1196
|
+
<Label
|
|
1197
|
+
className="text-xs font-medium text-muted-foreground"
|
|
1198
|
+
>
|
|
1199
|
+
Question format
|
|
1200
|
+
</Label>
|
|
1201
|
+
<button
|
|
1202
|
+
type="button"
|
|
1203
|
+
onClick={() => setTypeChooserOpen(true)}
|
|
1204
|
+
aria-label={`Change question format — currently ${activeType.label}`}
|
|
1205
|
+
className={cn(
|
|
1206
|
+
"group flex items-center gap-3 rounded-lg border border-border bg-background px-3 py-2.5 text-left transition-colors",
|
|
1207
|
+
"hover:border-foreground/30 hover:bg-muted/40",
|
|
1208
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
1209
|
+
)}
|
|
1210
|
+
>
|
|
1211
|
+
<span
|
|
1212
|
+
className="inline-flex size-9 shrink-0 items-center justify-center rounded-md bg-[var(--icon-disc-brand-bg)] text-[var(--icon-disc-brand-fg)]"
|
|
1213
|
+
aria-hidden="true"
|
|
1214
|
+
>
|
|
1215
|
+
<i className={cn("fa-light text-base", activeType.icon)} />
|
|
1216
|
+
</span>
|
|
1217
|
+
<span className="flex min-w-0 flex-1 flex-col">
|
|
1218
|
+
<span className="truncate text-sm font-medium text-foreground">
|
|
1219
|
+
{activeType.label}
|
|
1220
|
+
</span>
|
|
1221
|
+
<span className="truncate text-xs text-muted-foreground">
|
|
1222
|
+
{activeType.tileSummary ?? activeType.description}
|
|
1223
|
+
</span>
|
|
1224
|
+
</span>
|
|
1225
|
+
<span
|
|
1226
|
+
className="inline-flex size-7 items-center justify-center rounded-md text-muted-foreground transition-colors group-hover:bg-muted group-hover:text-foreground"
|
|
1227
|
+
aria-hidden="true"
|
|
1228
|
+
>
|
|
1229
|
+
<i className="fa-light fa-pen-to-square text-xs" />
|
|
1230
|
+
</span>
|
|
1231
|
+
</button>
|
|
1232
|
+
</div>
|
|
1233
|
+
)}
|
|
1234
|
+
</FormControl>
|
|
1235
|
+
{typeChooserOpen ? (
|
|
1236
|
+
<FormDescription>{activeType.description}</FormDescription>
|
|
1237
|
+
) : null}
|
|
1238
|
+
<FormMessage />
|
|
1239
|
+
</FormItem>
|
|
1240
|
+
)}
|
|
1241
|
+
/>
|
|
1242
|
+
|
|
1243
|
+
{/* Location — compact selected tile (mirrors the
|
|
1244
|
+
collapsed question-format card). Click opens a Command
|
|
1245
|
+
popover with search and an "Add new folder" footer
|
|
1246
|
+
that bridges into `QuestionBankNewFolderSheet`. */}
|
|
1247
|
+
<FormField
|
|
1248
|
+
control={form.control}
|
|
1249
|
+
name="folderId"
|
|
1250
|
+
render={({ field }) => (
|
|
1251
|
+
<FormItem>
|
|
1252
|
+
<Label
|
|
1253
|
+
className="text-xs font-medium text-muted-foreground"
|
|
1254
|
+
>
|
|
1255
|
+
Location
|
|
1256
|
+
</Label>
|
|
1257
|
+
<FormControl>
|
|
1258
|
+
<FolderPickerControl
|
|
1259
|
+
folders={localFolders}
|
|
1260
|
+
value={field.value}
|
|
1261
|
+
onChange={v => field.onChange(v)}
|
|
1262
|
+
open={folderPickerOpen}
|
|
1263
|
+
onOpenChange={setFolderPickerOpen}
|
|
1264
|
+
onRequestNewFolder={() => {
|
|
1265
|
+
setFolderPickerOpen(false)
|
|
1266
|
+
setNewFolderOpen(true)
|
|
1267
|
+
}}
|
|
1268
|
+
/>
|
|
1269
|
+
</FormControl>
|
|
1270
|
+
<FormMessage />
|
|
1271
|
+
</FormItem>
|
|
1272
|
+
)}
|
|
1273
|
+
/>
|
|
1274
|
+
|
|
1275
|
+
{/* Difficulty — meter + AI estimate + PBI + folder note.
|
|
1276
|
+
Defaults to AI mode (the meter follows the folder
|
|
1277
|
+
recommendation); "Override" flips to manual chips for
|
|
1278
|
+
authors who want to lock the level themselves. */}
|
|
1279
|
+
<FormField
|
|
1280
|
+
control={form.control}
|
|
1281
|
+
name="difficulty"
|
|
1282
|
+
render={({ field }) => (
|
|
1283
|
+
<DifficultyMeter
|
|
1284
|
+
value={field.value}
|
|
1285
|
+
onChange={v => field.onChange(v)}
|
|
1286
|
+
mode={difficultyMode}
|
|
1287
|
+
onModeChange={setDifficultyMode}
|
|
1288
|
+
insight={difficultyInsight}
|
|
1289
|
+
/>
|
|
1290
|
+
)}
|
|
1291
|
+
/>
|
|
1292
|
+
|
|
1293
|
+
<FormField
|
|
1294
|
+
control={form.control}
|
|
1295
|
+
name="bloom"
|
|
1296
|
+
render={({ field }) => (
|
|
1297
|
+
<InspectorSection title="Bloom's taxonomy">
|
|
1298
|
+
<ToggleGroup
|
|
1299
|
+
type="single"
|
|
1300
|
+
variant="outline"
|
|
1301
|
+
size="sm"
|
|
1302
|
+
spacing={1}
|
|
1303
|
+
value={field.value}
|
|
1304
|
+
onValueChange={v => field.onChange(v)}
|
|
1305
|
+
className="flex-wrap"
|
|
1306
|
+
>
|
|
1307
|
+
{AUTHORING_BLOOM_OPTIONS.map(b => (
|
|
1308
|
+
<ToggleGroupItem
|
|
1309
|
+
key={b.value}
|
|
1310
|
+
value={b.value}
|
|
1311
|
+
title={b.hint}
|
|
1312
|
+
className="rounded-full px-3"
|
|
1313
|
+
>
|
|
1314
|
+
{b.label}
|
|
1315
|
+
</ToggleGroupItem>
|
|
1316
|
+
))}
|
|
1317
|
+
</ToggleGroup>
|
|
1318
|
+
</InspectorSection>
|
|
1319
|
+
)}
|
|
1320
|
+
/>
|
|
1321
|
+
|
|
1322
|
+
<FormField
|
|
1323
|
+
control={form.control}
|
|
1324
|
+
name="cogLevel"
|
|
1325
|
+
render={({ field }) => (
|
|
1326
|
+
<InspectorSection
|
|
1327
|
+
title="NBME cognitive level"
|
|
1328
|
+
description="Distinct from Bloom — broader buckets used by NBME item writers."
|
|
1329
|
+
>
|
|
1330
|
+
<ToggleGroup
|
|
1331
|
+
type="single"
|
|
1332
|
+
variant="outline"
|
|
1333
|
+
size="sm"
|
|
1334
|
+
spacing={1}
|
|
1335
|
+
value={field.value}
|
|
1336
|
+
onValueChange={v => field.onChange(v)}
|
|
1337
|
+
className="flex-wrap"
|
|
1338
|
+
>
|
|
1339
|
+
{AUTHORING_COG_LEVEL_OPTIONS.map(c => (
|
|
1340
|
+
<ToggleGroupItem
|
|
1341
|
+
key={c.value}
|
|
1342
|
+
value={c.value}
|
|
1343
|
+
title={c.hint}
|
|
1344
|
+
className="rounded-full px-3"
|
|
1345
|
+
>
|
|
1346
|
+
{c.label}
|
|
1347
|
+
</ToggleGroupItem>
|
|
1348
|
+
))}
|
|
1349
|
+
</ToggleGroup>
|
|
1350
|
+
</InspectorSection>
|
|
1351
|
+
)}
|
|
1352
|
+
/>
|
|
1353
|
+
|
|
1354
|
+
<InspectorSection title="Tags" htmlFor="qb-tag-input">
|
|
1355
|
+
{watchedTags.length > 0 ? (
|
|
1356
|
+
<div className="flex flex-wrap gap-1.5">
|
|
1357
|
+
{watchedTags.map(t => (
|
|
1358
|
+
<Badge key={t} variant="secondary" className="gap-1.5">
|
|
1359
|
+
<span>#{t}</span>
|
|
1360
|
+
<Tip side="top" label={`Remove tag ${t}`}>
|
|
1361
|
+
<button
|
|
1362
|
+
type="button"
|
|
1363
|
+
onClick={() => removeTag(t)}
|
|
1364
|
+
aria-label={`Remove tag ${t}`}
|
|
1365
|
+
className="-me-0.5 inline-flex size-3.5 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-background hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
1366
|
+
>
|
|
1367
|
+
<i
|
|
1368
|
+
className="fa-light fa-xmark text-[9px]"
|
|
1369
|
+
aria-hidden="true"
|
|
1370
|
+
/>
|
|
1371
|
+
</button>
|
|
1372
|
+
</Tip>
|
|
1373
|
+
</Badge>
|
|
1374
|
+
))}
|
|
1375
|
+
</div>
|
|
1376
|
+
) : null}
|
|
1377
|
+
<Input
|
|
1378
|
+
id="qb-tag-input"
|
|
1379
|
+
value={tagDraft}
|
|
1380
|
+
onChange={e => setTagDraft(e.target.value)}
|
|
1381
|
+
onKeyDown={e => {
|
|
1382
|
+
if (e.key === "Enter" || e.key === ",") {
|
|
1383
|
+
e.preventDefault()
|
|
1384
|
+
commitTag()
|
|
1385
|
+
}
|
|
1386
|
+
}}
|
|
1387
|
+
onBlur={commitTag}
|
|
1388
|
+
placeholder="STEMI, antibiotics…"
|
|
1389
|
+
className="h-8 text-xs"
|
|
1390
|
+
/>
|
|
1391
|
+
</InspectorSection>
|
|
1392
|
+
</div>
|
|
1393
|
+
)}
|
|
1394
|
+
</>
|
|
1395
|
+
)
|
|
1396
|
+
|
|
1092
1397
|
return (
|
|
1093
1398
|
<Form {...form}>
|
|
1094
1399
|
{/* Global shortcuts — bound while the composer is mounted. The
|
|
@@ -1108,125 +1413,140 @@ export function NewQuestionComposer({
|
|
|
1108
1413
|
onInvoke={() => setInspectorOpen(o => !o)}
|
|
1109
1414
|
/>
|
|
1110
1415
|
|
|
1416
|
+
{/*
|
|
1417
|
+
`<form>` participates in the (app)/layout flex row alongside the sidebar +
|
|
1418
|
+
secondary panel + Ask Leo rail, so it MUST behave like a normal flex column
|
|
1419
|
+
host (flex-1 + min-w-0). Without these classes the form shrinks to its
|
|
1420
|
+
intrinsic content width and the page collapses into a narrow column on the
|
|
1421
|
+
left with the rest of the viewport empty. See `new-placement-form.tsx`.
|
|
1422
|
+
*/}
|
|
1111
1423
|
<form
|
|
1112
1424
|
onSubmit={form.handleSubmit(values => persist({ ...values, status: "in_review" }, "publish"))}
|
|
1113
1425
|
noValidate
|
|
1114
1426
|
aria-label="New question form"
|
|
1115
|
-
className="flex flex-
|
|
1427
|
+
className="flex min-h-0 min-w-0 flex-1 flex-col"
|
|
1116
1428
|
>
|
|
1117
|
-
<
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1429
|
+
<NewFocusTemplate
|
|
1430
|
+
variant="form-inspector"
|
|
1431
|
+
title="New question"
|
|
1432
|
+
back={{ href: backHref, label: backLabel, ariaLabel: `Back to ${backLabel}` }}
|
|
1433
|
+
useSiteHeaderBack
|
|
1434
|
+
hideInspectorToggle
|
|
1435
|
+
inspectorOpen={inspectorOpen}
|
|
1436
|
+
onInspectorOpenChange={setInspectorOpen}
|
|
1437
|
+
inspectorAriaLabel="Question inspector"
|
|
1438
|
+
inspector={() => inspectorContent}
|
|
1439
|
+
header={
|
|
1440
|
+
<PageHeader
|
|
1441
|
+
title="New question"
|
|
1442
|
+
subtitle={headerSubtitle}
|
|
1443
|
+
actions={
|
|
1444
|
+
<>
|
|
1445
|
+
{/* Save as draft — icon-only button (leftmost). */}
|
|
1446
|
+
<Tip side="bottom" label="Save as draft">
|
|
1447
|
+
<Button
|
|
1448
|
+
type="button"
|
|
1449
|
+
size="lg"
|
|
1450
|
+
variant="outline"
|
|
1451
|
+
className="aspect-square px-0"
|
|
1452
|
+
disabled={submitting}
|
|
1453
|
+
onClick={handleSaveAsDraft}
|
|
1454
|
+
aria-label="Save as draft"
|
|
1455
|
+
>
|
|
1456
|
+
<i className="fa-light fa-file-pen text-base" aria-hidden="true" />
|
|
1457
|
+
</Button>
|
|
1458
|
+
</Tip>
|
|
1459
|
+
|
|
1460
|
+
{/* Primary — Save Question. Full validation; the
|
|
1461
|
+
question moves to In review and the user is
|
|
1462
|
+
returned to the parent hub. */}
|
|
1124
1463
|
<Button
|
|
1125
1464
|
type="button"
|
|
1126
1465
|
size="lg"
|
|
1127
|
-
variant="outline"
|
|
1128
|
-
className="aspect-square px-0"
|
|
1129
1466
|
disabled={submitting}
|
|
1130
|
-
|
|
1131
|
-
|
|
1467
|
+
aria-busy={submitting}
|
|
1468
|
+
onClick={handleSaveQuestion}
|
|
1132
1469
|
>
|
|
1133
|
-
|
|
1470
|
+
{submitting ? (
|
|
1471
|
+
<>
|
|
1472
|
+
<i
|
|
1473
|
+
className="fa-light fa-spinner-third fa-spin text-[13px]"
|
|
1474
|
+
aria-hidden="true"
|
|
1475
|
+
/>
|
|
1476
|
+
Saving…
|
|
1477
|
+
</>
|
|
1478
|
+
) : (
|
|
1479
|
+
<>
|
|
1480
|
+
Save question
|
|
1481
|
+
<KbdGroup className="ms-1.5">
|
|
1482
|
+
<Kbd variant="bare">⏎</Kbd>
|
|
1483
|
+
</KbdGroup>
|
|
1484
|
+
</>
|
|
1485
|
+
)}
|
|
1134
1486
|
</Button>
|
|
1135
|
-
</Tip>
|
|
1136
|
-
|
|
1137
|
-
{/* Primary — Save Question. Full validation; the
|
|
1138
|
-
question moves to In review and the user is
|
|
1139
|
-
returned to the parent hub. */}
|
|
1140
|
-
<Button
|
|
1141
|
-
type="button"
|
|
1142
|
-
size="lg"
|
|
1143
|
-
disabled={submitting}
|
|
1144
|
-
aria-busy={submitting}
|
|
1145
|
-
onClick={handleSaveQuestion}
|
|
1146
|
-
>
|
|
1147
|
-
{submitting ? (
|
|
1148
|
-
<>
|
|
1149
|
-
<i
|
|
1150
|
-
className="fa-light fa-spinner-third fa-spin text-[13px]"
|
|
1151
|
-
aria-hidden="true"
|
|
1152
|
-
/>
|
|
1153
|
-
Saving…
|
|
1154
|
-
</>
|
|
1155
|
-
) : (
|
|
1156
|
-
<>
|
|
1157
|
-
Save question
|
|
1158
|
-
<KbdGroup className="ml-1.5">
|
|
1159
|
-
<Kbd variant="bare">⏎</Kbd>
|
|
1160
|
-
</KbdGroup>
|
|
1161
|
-
</>
|
|
1162
|
-
)}
|
|
1163
|
-
</Button>
|
|
1164
1487
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1488
|
+
{/* More — overflow menu (⌘⌥M). */}
|
|
1489
|
+
<DropdownMenu open={moreOpen} onOpenChange={setMoreOpen}>
|
|
1490
|
+
<Tip side="bottom" label="More actions">
|
|
1491
|
+
<DropdownMenuTrigger asChild>
|
|
1492
|
+
<Button
|
|
1493
|
+
type="button"
|
|
1494
|
+
size="lg"
|
|
1495
|
+
variant="outline"
|
|
1496
|
+
className="aspect-square px-0"
|
|
1497
|
+
aria-label="More actions"
|
|
1498
|
+
>
|
|
1499
|
+
<i
|
|
1500
|
+
className="fa-light fa-ellipsis text-base"
|
|
1501
|
+
aria-hidden="true"
|
|
1502
|
+
/>
|
|
1503
|
+
</Button>
|
|
1504
|
+
</DropdownMenuTrigger>
|
|
1505
|
+
</Tip>
|
|
1506
|
+
<DropdownMenuContent align="end">
|
|
1507
|
+
<DropdownMenuItem
|
|
1508
|
+
shortcut="⌘⌥]"
|
|
1509
|
+
onSelect={() => {
|
|
1510
|
+
window.setTimeout(
|
|
1511
|
+
() => setInspectorOpen(o => !o),
|
|
1512
|
+
0,
|
|
1513
|
+
)
|
|
1514
|
+
}}
|
|
1175
1515
|
>
|
|
1176
1516
|
<i
|
|
1177
|
-
className=
|
|
1517
|
+
className={cn(
|
|
1518
|
+
"fa-light",
|
|
1519
|
+
inspectorOpen ? "fa-sidebar-flip" : "fa-sidebar",
|
|
1520
|
+
)}
|
|
1178
1521
|
aria-hidden="true"
|
|
1179
1522
|
/>
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
<DropdownMenuItem
|
|
1204
|
-
shortcut="Esc"
|
|
1205
|
-
onSelect={() => {
|
|
1206
|
-
window.setTimeout(() => handleCancel(), 0)
|
|
1207
|
-
}}
|
|
1208
|
-
variant="destructive"
|
|
1209
|
-
>
|
|
1210
|
-
<i className="fa-light fa-trash-can" aria-hidden="true" />
|
|
1211
|
-
Discard draft
|
|
1212
|
-
</DropdownMenuItem>
|
|
1213
|
-
</DropdownMenuContent>
|
|
1214
|
-
</DropdownMenu>
|
|
1215
|
-
</>
|
|
1216
|
-
}
|
|
1217
|
-
className="px-0 lg:px-0"
|
|
1218
|
-
/>
|
|
1219
|
-
|
|
1220
|
-
{/* ── 2-column body — scrolls with the page; inspector sticky (lg+) */}
|
|
1221
|
-
<div
|
|
1222
|
-
className={cn(
|
|
1223
|
-
"flex flex-col gap-8",
|
|
1224
|
-
"lg:flex-row lg:items-start",
|
|
1225
|
-
inspectorOpen ? "lg:gap-x-10" : "lg:gap-x-4",
|
|
1226
|
-
)}
|
|
1523
|
+
{inspectorOpen ? "Hide inspector" : "Show inspector"}
|
|
1524
|
+
</DropdownMenuItem>
|
|
1525
|
+
<DropdownMenuSeparator />
|
|
1526
|
+
<DropdownMenuItem
|
|
1527
|
+
shortcut="Esc"
|
|
1528
|
+
onSelect={() => {
|
|
1529
|
+
window.setTimeout(() => handleCancel(), 0)
|
|
1530
|
+
}}
|
|
1531
|
+
variant="destructive"
|
|
1532
|
+
>
|
|
1533
|
+
<i className="fa-light fa-trash-can" aria-hidden="true" />
|
|
1534
|
+
Discard draft
|
|
1535
|
+
</DropdownMenuItem>
|
|
1536
|
+
</DropdownMenuContent>
|
|
1537
|
+
</DropdownMenu>
|
|
1538
|
+
</>
|
|
1539
|
+
}
|
|
1540
|
+
className="px-0 lg:px-0"
|
|
1541
|
+
/>
|
|
1542
|
+
}
|
|
1543
|
+
maxWidthClassName="mx-auto w-full max-w-[1100px]"
|
|
1544
|
+
contentClassName="px-8 py-4 pb-16 flex flex-col gap-6"
|
|
1545
|
+
bodyClassName="min-h-0 flex-1 overflow-y-auto overscroll-y-contain"
|
|
1227
1546
|
>
|
|
1228
|
-
{/* ── Builder (no Card chrome)
|
|
1229
|
-
|
|
1547
|
+
{/* ── Builder (no Card chrome) — direct child of the template's
|
|
1548
|
+
form-inspector grid; inspector lives in the `inspector` prop. */}
|
|
1549
|
+
<div className="flex flex-col gap-7 lg:pe-2">
|
|
1230
1550
|
{/* Question prompt — a real bordered field, not an
|
|
1231
1551
|
inline-editable headline. Larger heading-weight font keeps
|
|
1232
1552
|
the question front-and-centre, but the visible border /
|
|
@@ -1758,297 +2078,7 @@ export function NewQuestionComposer({
|
|
|
1758
2078
|
/>
|
|
1759
2079
|
) : null}
|
|
1760
2080
|
</div>
|
|
1761
|
-
|
|
1762
|
-
{/* ── Inspector (right rail) — sticky while the page scrolls (lg+) */}
|
|
1763
|
-
<aside
|
|
1764
|
-
aria-label="Question inspector"
|
|
1765
|
-
className={cn(
|
|
1766
|
-
"shrink-0",
|
|
1767
|
-
"lg:pl-2",
|
|
1768
|
-
inspectorOpen ? "lg:w-[320px]" : "lg:w-14",
|
|
1769
|
-
)}
|
|
1770
|
-
>
|
|
1771
|
-
{!inspectorOpen ? (
|
|
1772
|
-
<div
|
|
1773
|
-
className={cn(
|
|
1774
|
-
"flex w-12 flex-col items-center gap-1 rounded-xl bg-[var(--secondary-panel-bg)] px-1.5 py-2 ring-1 ring-border shadow-sm",
|
|
1775
|
-
"lg:sticky lg:top-4 lg:z-10",
|
|
1776
|
-
)}
|
|
1777
|
-
>
|
|
1778
|
-
<Tip side="left" label="Show inspector">
|
|
1779
|
-
<button
|
|
1780
|
-
type="button"
|
|
1781
|
-
onClick={() => setInspectorOpen(true)}
|
|
1782
|
-
aria-label="Show inspector"
|
|
1783
|
-
aria-expanded={false}
|
|
1784
|
-
className={cn(
|
|
1785
|
-
"flex size-9 shrink-0 items-center justify-center rounded-md text-sidebar-foreground transition-colors",
|
|
1786
|
-
"hover:bg-sidebar-accent/50",
|
|
1787
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset",
|
|
1788
|
-
)}
|
|
1789
|
-
>
|
|
1790
|
-
<i
|
|
1791
|
-
className="fa-light fa-arrow-left-to-line text-[15px] leading-none"
|
|
1792
|
-
aria-hidden="true"
|
|
1793
|
-
/>
|
|
1794
|
-
</button>
|
|
1795
|
-
</Tip>
|
|
1796
|
-
</div>
|
|
1797
|
-
) : (
|
|
1798
|
-
<div
|
|
1799
|
-
className={cn(
|
|
1800
|
-
"flex flex-col gap-6 rounded-xl border border-border bg-card p-4",
|
|
1801
|
-
"lg:sticky lg:top-4 lg:z-10",
|
|
1802
|
-
"lg:max-h-[calc(100dvh-var(--header-height)-2rem)] lg:overflow-y-auto lg:overscroll-y-contain",
|
|
1803
|
-
)}
|
|
1804
|
-
>
|
|
1805
|
-
{/* Inspector header — title + collapse control. */}
|
|
1806
|
-
<div className="flex items-center justify-between">
|
|
1807
|
-
<p className="text-xs font-medium text-muted-foreground">
|
|
1808
|
-
Inspector
|
|
1809
|
-
</p>
|
|
1810
|
-
<Tip side="bottom" label="Hide inspector">
|
|
1811
|
-
<Button
|
|
1812
|
-
type="button"
|
|
1813
|
-
variant="ghost"
|
|
1814
|
-
size="icon-sm"
|
|
1815
|
-
onClick={() => setInspectorOpen(false)}
|
|
1816
|
-
aria-label="Hide inspector"
|
|
1817
|
-
aria-expanded={true}
|
|
1818
|
-
>
|
|
1819
|
-
<i
|
|
1820
|
-
className="fa-light fa-arrow-right-to-line"
|
|
1821
|
-
aria-hidden="true"
|
|
1822
|
-
/>
|
|
1823
|
-
</Button>
|
|
1824
|
-
</Tip>
|
|
1825
|
-
</div>
|
|
1826
|
-
|
|
1827
|
-
{/* Question format — first-time landing shows the full
|
|
1828
|
-
`SelectionTileGrid` (matches the "File format" pattern
|
|
1829
|
-
in `ExportDrawer`). After the author picks once it
|
|
1830
|
-
collapses into a single selected-type tile with a
|
|
1831
|
-
"Change" affordance that re-opens the grid. */}
|
|
1832
|
-
<FormField
|
|
1833
|
-
control={form.control}
|
|
1834
|
-
name="type"
|
|
1835
|
-
render={({ field }) => (
|
|
1836
|
-
<FormItem>
|
|
1837
|
-
<FormControl>
|
|
1838
|
-
{typeChooserOpen ? (
|
|
1839
|
-
<SelectionTileGrid
|
|
1840
|
-
sectionLabel="Question format"
|
|
1841
|
-
options={QUESTION_TYPE_TILES}
|
|
1842
|
-
columns={2}
|
|
1843
|
-
value={field.value}
|
|
1844
|
-
onValueChange={v => changeType(v)}
|
|
1845
|
-
interaction="radio"
|
|
1846
|
-
idPrefix="qb-format"
|
|
1847
|
-
itemVariant="outline"
|
|
1848
|
-
itemMotion="pop"
|
|
1849
|
-
/>
|
|
1850
|
-
) : (
|
|
1851
|
-
<div className="flex flex-col gap-2">
|
|
1852
|
-
<Label
|
|
1853
|
-
className="text-xs font-medium text-muted-foreground"
|
|
1854
|
-
>
|
|
1855
|
-
Question format
|
|
1856
|
-
</Label>
|
|
1857
|
-
<button
|
|
1858
|
-
type="button"
|
|
1859
|
-
onClick={() => setTypeChooserOpen(true)}
|
|
1860
|
-
aria-label={`Change question format — currently ${activeType.label}`}
|
|
1861
|
-
className={cn(
|
|
1862
|
-
"group flex items-center gap-3 rounded-lg border border-border bg-background px-3 py-2.5 text-left transition-colors",
|
|
1863
|
-
"hover:border-foreground/30 hover:bg-muted/40",
|
|
1864
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
1865
|
-
)}
|
|
1866
|
-
>
|
|
1867
|
-
<span
|
|
1868
|
-
className="inline-flex size-9 shrink-0 items-center justify-center rounded-md bg-[var(--icon-disc-brand-bg)] text-[var(--icon-disc-brand-fg)]"
|
|
1869
|
-
aria-hidden="true"
|
|
1870
|
-
>
|
|
1871
|
-
<i className={cn("fa-light text-base", activeType.icon)} />
|
|
1872
|
-
</span>
|
|
1873
|
-
<span className="flex min-w-0 flex-1 flex-col">
|
|
1874
|
-
<span className="truncate text-sm font-medium text-foreground">
|
|
1875
|
-
{activeType.label}
|
|
1876
|
-
</span>
|
|
1877
|
-
<span className="truncate text-xs text-muted-foreground">
|
|
1878
|
-
{activeType.tileSummary ?? activeType.description}
|
|
1879
|
-
</span>
|
|
1880
|
-
</span>
|
|
1881
|
-
<span
|
|
1882
|
-
className="inline-flex size-7 items-center justify-center rounded-md text-muted-foreground transition-colors group-hover:bg-muted group-hover:text-foreground"
|
|
1883
|
-
aria-hidden="true"
|
|
1884
|
-
>
|
|
1885
|
-
<i className="fa-light fa-pen-to-square text-xs" />
|
|
1886
|
-
</span>
|
|
1887
|
-
</button>
|
|
1888
|
-
</div>
|
|
1889
|
-
)}
|
|
1890
|
-
</FormControl>
|
|
1891
|
-
{typeChooserOpen ? (
|
|
1892
|
-
<FormDescription>{activeType.description}</FormDescription>
|
|
1893
|
-
) : null}
|
|
1894
|
-
<FormMessage />
|
|
1895
|
-
</FormItem>
|
|
1896
|
-
)}
|
|
1897
|
-
/>
|
|
1898
|
-
|
|
1899
|
-
{/* Location — compact selected tile (mirrors the
|
|
1900
|
-
collapsed question-format card). Click opens a Command
|
|
1901
|
-
popover with search and an "Add new folder" footer
|
|
1902
|
-
that bridges into `QuestionBankNewFolderSheet`. */}
|
|
1903
|
-
<FormField
|
|
1904
|
-
control={form.control}
|
|
1905
|
-
name="folderId"
|
|
1906
|
-
render={({ field }) => (
|
|
1907
|
-
<FormItem>
|
|
1908
|
-
<Label
|
|
1909
|
-
className="text-xs font-medium text-muted-foreground"
|
|
1910
|
-
>
|
|
1911
|
-
Location
|
|
1912
|
-
</Label>
|
|
1913
|
-
<FormControl>
|
|
1914
|
-
<FolderPickerControl
|
|
1915
|
-
folders={localFolders}
|
|
1916
|
-
value={field.value}
|
|
1917
|
-
onChange={v => field.onChange(v)}
|
|
1918
|
-
open={folderPickerOpen}
|
|
1919
|
-
onOpenChange={setFolderPickerOpen}
|
|
1920
|
-
onRequestNewFolder={() => {
|
|
1921
|
-
setFolderPickerOpen(false)
|
|
1922
|
-
setNewFolderOpen(true)
|
|
1923
|
-
}}
|
|
1924
|
-
/>
|
|
1925
|
-
</FormControl>
|
|
1926
|
-
<FormMessage />
|
|
1927
|
-
</FormItem>
|
|
1928
|
-
)}
|
|
1929
|
-
/>
|
|
1930
|
-
|
|
1931
|
-
{/* Difficulty — meter + AI estimate + PBI + folder note.
|
|
1932
|
-
Defaults to AI mode (the meter follows the folder
|
|
1933
|
-
recommendation); "Override" flips to manual chips for
|
|
1934
|
-
authors who want to lock the level themselves. */}
|
|
1935
|
-
<FormField
|
|
1936
|
-
control={form.control}
|
|
1937
|
-
name="difficulty"
|
|
1938
|
-
render={({ field }) => (
|
|
1939
|
-
<DifficultyMeter
|
|
1940
|
-
value={field.value}
|
|
1941
|
-
onChange={v => field.onChange(v)}
|
|
1942
|
-
mode={difficultyMode}
|
|
1943
|
-
onModeChange={setDifficultyMode}
|
|
1944
|
-
insight={difficultyInsight}
|
|
1945
|
-
/>
|
|
1946
|
-
)}
|
|
1947
|
-
/>
|
|
1948
|
-
|
|
1949
|
-
<FormField
|
|
1950
|
-
control={form.control}
|
|
1951
|
-
name="bloom"
|
|
1952
|
-
render={({ field }) => (
|
|
1953
|
-
<InspectorSection title="Bloom's taxonomy">
|
|
1954
|
-
<ToggleGroup
|
|
1955
|
-
type="single"
|
|
1956
|
-
variant="outline"
|
|
1957
|
-
size="sm"
|
|
1958
|
-
spacing={1}
|
|
1959
|
-
value={field.value}
|
|
1960
|
-
onValueChange={v => field.onChange(v)}
|
|
1961
|
-
className="flex-wrap"
|
|
1962
|
-
>
|
|
1963
|
-
{AUTHORING_BLOOM_OPTIONS.map(b => (
|
|
1964
|
-
<ToggleGroupItem
|
|
1965
|
-
key={b.value}
|
|
1966
|
-
value={b.value}
|
|
1967
|
-
title={b.hint}
|
|
1968
|
-
className="rounded-full px-3"
|
|
1969
|
-
>
|
|
1970
|
-
{b.label}
|
|
1971
|
-
</ToggleGroupItem>
|
|
1972
|
-
))}
|
|
1973
|
-
</ToggleGroup>
|
|
1974
|
-
</InspectorSection>
|
|
1975
|
-
)}
|
|
1976
|
-
/>
|
|
1977
|
-
|
|
1978
|
-
<FormField
|
|
1979
|
-
control={form.control}
|
|
1980
|
-
name="cogLevel"
|
|
1981
|
-
render={({ field }) => (
|
|
1982
|
-
<InspectorSection
|
|
1983
|
-
title="NBME cognitive level"
|
|
1984
|
-
description="Distinct from Bloom — broader buckets used by NBME item writers."
|
|
1985
|
-
>
|
|
1986
|
-
<ToggleGroup
|
|
1987
|
-
type="single"
|
|
1988
|
-
variant="outline"
|
|
1989
|
-
size="sm"
|
|
1990
|
-
spacing={1}
|
|
1991
|
-
value={field.value}
|
|
1992
|
-
onValueChange={v => field.onChange(v)}
|
|
1993
|
-
className="flex-wrap"
|
|
1994
|
-
>
|
|
1995
|
-
{AUTHORING_COG_LEVEL_OPTIONS.map(c => (
|
|
1996
|
-
<ToggleGroupItem
|
|
1997
|
-
key={c.value}
|
|
1998
|
-
value={c.value}
|
|
1999
|
-
title={c.hint}
|
|
2000
|
-
className="rounded-full px-3"
|
|
2001
|
-
>
|
|
2002
|
-
{c.label}
|
|
2003
|
-
</ToggleGroupItem>
|
|
2004
|
-
))}
|
|
2005
|
-
</ToggleGroup>
|
|
2006
|
-
</InspectorSection>
|
|
2007
|
-
)}
|
|
2008
|
-
/>
|
|
2009
|
-
|
|
2010
|
-
<InspectorSection title="Tags" htmlFor="qb-tag-input">
|
|
2011
|
-
{watchedTags.length > 0 ? (
|
|
2012
|
-
<div className="flex flex-wrap gap-1.5">
|
|
2013
|
-
{watchedTags.map(t => (
|
|
2014
|
-
<Badge key={t} variant="secondary" className="gap-1.5">
|
|
2015
|
-
<span>#{t}</span>
|
|
2016
|
-
<Tip side="top" label={`Remove tag ${t}`}>
|
|
2017
|
-
<button
|
|
2018
|
-
type="button"
|
|
2019
|
-
onClick={() => removeTag(t)}
|
|
2020
|
-
aria-label={`Remove tag ${t}`}
|
|
2021
|
-
className="-mr-0.5 inline-flex size-3.5 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-background hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
2022
|
-
>
|
|
2023
|
-
<i
|
|
2024
|
-
className="fa-light fa-xmark text-[9px]"
|
|
2025
|
-
aria-hidden="true"
|
|
2026
|
-
/>
|
|
2027
|
-
</button>
|
|
2028
|
-
</Tip>
|
|
2029
|
-
</Badge>
|
|
2030
|
-
))}
|
|
2031
|
-
</div>
|
|
2032
|
-
) : null}
|
|
2033
|
-
<Input
|
|
2034
|
-
id="qb-tag-input"
|
|
2035
|
-
value={tagDraft}
|
|
2036
|
-
onChange={e => setTagDraft(e.target.value)}
|
|
2037
|
-
onKeyDown={e => {
|
|
2038
|
-
if (e.key === "Enter" || e.key === ",") {
|
|
2039
|
-
e.preventDefault()
|
|
2040
|
-
commitTag()
|
|
2041
|
-
}
|
|
2042
|
-
}}
|
|
2043
|
-
onBlur={commitTag}
|
|
2044
|
-
placeholder="STEMI, antibiotics…"
|
|
2045
|
-
className="h-8 text-xs"
|
|
2046
|
-
/>
|
|
2047
|
-
</InspectorSection>
|
|
2048
|
-
</div>
|
|
2049
|
-
)}
|
|
2050
|
-
</aside>
|
|
2051
|
-
</div>
|
|
2081
|
+
</NewFocusTemplate>
|
|
2052
2082
|
</form>
|
|
2053
2083
|
|
|
2054
2084
|
{/* New folder — invoked from the location picker. Re-uses the
|
|
@@ -2188,7 +2218,7 @@ function OptionRow({
|
|
|
2188
2218
|
</div>
|
|
2189
2219
|
|
|
2190
2220
|
{rationaleOpen && !locked ? (
|
|
2191
|
-
<div className="
|
|
2221
|
+
<div className="ps-10">
|
|
2192
2222
|
<Textarea
|
|
2193
2223
|
value={option.rationale}
|
|
2194
2224
|
onChange={e => onRationaleChange(e.target.value)}
|