@exxatdesignux/ui 0.2.18 → 0.2.19

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.
Files changed (140) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/consumer-extras/AGENTS.md +76 -0
  3. package/consumer-extras/README.md +5 -1
  4. package/consumer-extras/cursor-skills/exxat-centralized-list-dataset/SKILL.md +14 -3
  5. package/consumer-extras/cursor-skills/exxat-consumer-app/SKILL.md +37 -0
  6. package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +21 -6
  7. package/consumer-extras/cursor-skills/exxat-focused-workflow-page/SKILL.md +57 -0
  8. package/consumer-extras/cursor-skills/exxat-primary-nav-secondary-panel/SKILL.md +4 -2
  9. package/consumer-extras/patterns/consumer-app-pattern.md +39 -0
  10. package/consumer-extras/patterns/consumer-upgrade-checklist.md +20 -0
  11. package/consumer-extras/patterns/data-views-pattern.md +40 -3
  12. package/consumer-extras/patterns/focused-workflow-page-pattern.md +84 -0
  13. package/consumer-extras/patterns/shell-surface-elevation-pattern.md +5 -3
  14. package/package.json +2 -1
  15. package/src/components/ui/button-group.tsx +81 -0
  16. package/src/components/ui/button.tsx +4 -4
  17. package/src/globals.css +7 -1858
  18. package/src/theme.css +10 -1126
  19. package/src/tokens/README.md +15 -0
  20. package/src/tokens/base.css +337 -0
  21. package/src/tokens/high-contrast.css +1195 -0
  22. package/src/tokens/layers.css +224 -0
  23. package/src/tokens/tailwind-bridge.css +118 -0
  24. package/src/tokens/themes.css +201 -0
  25. package/template/AGENTS.md +60 -22
  26. package/template/app/(app)/dashboard/loading.tsx +3 -15
  27. package/template/app/(app)/dashboard/page.tsx +2 -14
  28. package/template/app/(app)/data-list/layout.tsx +43 -0
  29. package/template/app/(app)/data-list/page.tsx +2 -2
  30. package/template/app/(app)/examples/focused-workflow/page.tsx +5 -0
  31. package/template/app/(app)/examples/page.tsx +1 -0
  32. package/template/app/(app)/loading.tsx +1 -18
  33. package/template/app/(app)/question-bank/find/page.tsx +2 -1
  34. package/template/app/(app)/question-bank/library/page.tsx +2 -1
  35. package/template/app/(app)/question-bank/list/page.tsx +2 -1
  36. package/template/app/(app)/question-bank/new/page.tsx +15 -23
  37. package/template/app/(app)/question-bank/page.tsx +2 -1
  38. package/template/app/(app)/settings/page.tsx +4 -5
  39. package/template/app/globals.css +7 -1964
  40. package/template/components/app-route-loading.tsx +14 -0
  41. package/template/components/app-sidebar.tsx +70 -55
  42. package/template/components/data-views/index.ts +37 -9
  43. package/template/components/data-views/list-page-calendar-view.tsx +593 -0
  44. package/template/components/data-views/list-page-connected-view-body.tsx +66 -0
  45. package/template/components/data-views/list-page-folder-columns-panel.tsx +345 -0
  46. package/template/components/data-views/list-page-split-hub-chrome.tsx +8 -0
  47. package/template/components/examples/focused-workflow-showcase.tsx +183 -0
  48. package/template/components/list-hub-board-view.tsx +68 -0
  49. package/template/components/list-hub-client.tsx +186 -0
  50. package/template/components/list-hub-list-view.tsx +36 -0
  51. package/template/components/list-hub-panel-activator.tsx +8 -0
  52. package/template/components/list-hub-secondary-nav.tsx +121 -0
  53. package/template/components/list-hub-table.tsx +336 -0
  54. package/template/components/new-question-composer.tsx +6 -24
  55. package/template/components/product-switcher.tsx +3 -2
  56. package/template/components/question-bank-client.tsx +4 -1
  57. package/template/components/question-bank-folder-columns-panel.tsx +104 -0
  58. package/template/components/question-bank-table.tsx +143 -485
  59. package/template/components/secondary-panel/nav-link-rows.tsx +83 -0
  60. package/template/components/secondary-panel.tsx +4 -44
  61. package/template/components/secondary-panels/list-hub-panel.tsx +39 -0
  62. package/template/components/secondary-panels/question-bank-panel.tsx +39 -0
  63. package/template/components/secondary-panels/registry.tsx +15 -0
  64. package/template/components/settings-appearance-card.tsx +3 -2
  65. package/template/components/settings-client.tsx +59 -15
  66. package/template/components/settings-form-row.tsx +9 -4
  67. package/template/components/table-properties/drawer-button.tsx +13 -0
  68. package/template/components/table-properties/drawer.tsx +65 -4
  69. package/template/components/templates/focused-workflow-layouts.tsx +448 -0
  70. package/template/components/templates/focused-workflow-page-template.tsx +69 -0
  71. package/template/components/templates/list-page.tsx +29 -5
  72. package/template/components/templates/nested-secondary-panel-shell.tsx +2 -1
  73. package/template/components/templates/page-loading-shell.tsx +262 -0
  74. package/template/components/ui/button-group.tsx +1 -0
  75. package/template/docs/consumer-app-pattern.md +39 -0
  76. package/template/docs/data-views-pattern.md +40 -3
  77. package/template/docs/drawer-vs-dialog-pattern.md +3 -1
  78. package/template/docs/focused-workflow-page-pattern.md +84 -0
  79. package/template/docs/shell-surface-elevation-pattern.md +5 -3
  80. package/template/lib/command-menu-search-data.ts +11 -27
  81. package/template/lib/data-list-display-options.ts +16 -2
  82. package/template/lib/data-list-view-registry.ts +104 -0
  83. package/template/lib/data-list-view-surface.ts +15 -1
  84. package/template/lib/data-list-view.ts +10 -1
  85. package/template/lib/data-view-dashboard-storage.ts +38 -35
  86. package/template/lib/hub-connected-view-renderers.ts +58 -0
  87. package/template/lib/list-hub-nav.ts +121 -0
  88. package/template/lib/list-hub-supported-views.ts +10 -0
  89. package/template/lib/list-page-table-properties.ts +3 -7
  90. package/template/lib/list-status-badges.ts +4 -97
  91. package/template/lib/mock/list-hub-directory.ts +27 -0
  92. package/template/lib/mock/list-hub-kpi.ts +27 -0
  93. package/template/lib/mock/navigation.tsx +1 -0
  94. package/template/lib/page-loading-variant.ts +40 -0
  95. package/template/lib/question-bank-supported-views.ts +13 -0
  96. package/template/lib/table-state-lifecycle.ts +2 -2
  97. package/template/app/(app)/data-list/[id]/page.tsx +0 -44
  98. package/template/app/(app)/data-list/new/page.tsx +0 -34
  99. package/template/components/compliance-board-view.tsx +0 -142
  100. package/template/components/compliance-client.tsx +0 -92
  101. package/template/components/compliance-list-view.tsx +0 -54
  102. package/template/components/compliance-page-header.tsx +0 -89
  103. package/template/components/compliance-table.tsx +0 -612
  104. package/template/components/data-view-dashboard-charts-compliance.tsx +0 -963
  105. package/template/components/data-view-dashboard-charts-team.tsx +0 -971
  106. package/template/components/data-view-dashboard-charts.tsx +0 -1503
  107. package/template/components/new-placement-back-btn.tsx +0 -28
  108. package/template/components/new-placement-form.tsx +0 -1068
  109. package/template/components/placement-board-card.tsx +0 -262
  110. package/template/components/placement-detail.tsx +0 -438
  111. package/template/components/placements-board-view.tsx +0 -404
  112. package/template/components/placements-client.tsx +0 -252
  113. package/template/components/placements-list-view.tsx +0 -171
  114. package/template/components/placements-page-header.tsx +0 -166
  115. package/template/components/placements-table-cells.test.tsx +0 -22
  116. package/template/components/placements-table-cells.tsx +0 -173
  117. package/template/components/placements-table-columns.tsx +0 -640
  118. package/template/components/placements-table.tsx +0 -1642
  119. package/template/components/rotations-empty-state.tsx +0 -50
  120. package/template/components/rotations-panel-activator.tsx +0 -8
  121. package/template/components/sites-all-client.tsx +0 -154
  122. package/template/components/sites-board-view.tsx +0 -67
  123. package/template/components/sites-list-view.tsx +0 -42
  124. package/template/components/sites-table.tsx +0 -382
  125. package/template/components/team-board-view.tsx +0 -122
  126. package/template/components/team-client.tsx +0 -100
  127. package/template/components/team-list-view.tsx +0 -59
  128. package/template/components/team-page-header.tsx +0 -92
  129. package/template/components/team-table.tsx +0 -693
  130. package/template/lib/data-view-dashboard-placements-layout.ts +0 -215
  131. package/template/lib/mock/compliance-kpi.ts +0 -61
  132. package/template/lib/mock/compliance.ts +0 -146
  133. package/template/lib/mock/placements-kpi.ts +0 -134
  134. package/template/lib/mock/placements.ts +0 -183
  135. package/template/lib/mock/sites-directory.ts +0 -16
  136. package/template/lib/mock/sites-kpi.ts +0 -25
  137. package/template/lib/mock/team-kpi.ts +0 -60
  138. package/template/lib/mock/team.ts +0 -118
  139. package/template/lib/placement-board-card-layout.ts +0 -79
  140. package/template/lib/placement-lifecycle.ts +0 -5
@@ -7,21 +7,10 @@
7
7
  *
8
8
  * ├─ PageHeader (title + actions; parent trail is in `SiteHeader`)
9
9
  * │ · "New question" + "V1 · Last updated …" subtitle
10
- * │ · single primary CTA Save as draft ()
11
- * │ · overflow menu (⌘⌥M) for inspector toggle + discard
12
- * ├─ 2-column layout (lg+): page scrolls as one; inspector card `sticky` on lg+
13
- * │ ┌─ Builder (left, no card chrome)
14
- * │ │ · Question prompt (h1-style Textarea — type-aware)
15
- * │ │ · Answer block — varies by question type
16
- * │ │ · Explanation / rubric / model answer
17
- * │ │ · References (repeatable list)
18
- * │ └─ Inspector (right, bg-card panel)
19
- * │ · Question format (SelectionTileGrid → compact)
20
- * │ · Location (folder SelectionTileGrid)
21
- * │ · Difficulty / Bloom / NBME (chips)
22
- * │ · Tags (Input + Badge list)
23
- * │ Sidebar-style collapse (⌘⌥]) — collapsed rail mimics
24
- * │ `NestedSecondaryPanelShell` icon mode.
10
+ * │ · Save question (⏎) + Save as draft + ⋯ discard (⌘⌥M)
11
+ * └─ Single column (`FocusedWorkflowPageTemplate` see `docs/focused-workflow-page-pattern.md`)
12
+ * · Details format, folder, difficulty, Bloom, NBME, tags
13
+ * · Question prompt, answer block, explanation, references
25
14
  *
26
15
  * Composes existing primitives — `PageHeader`, `Form`/`FormField`,
27
16
  * `Input`, `Textarea`, `Checkbox`, `Badge`, `Button`, `Tip`, `Kbd`,
@@ -776,11 +765,9 @@ export function NewQuestionComposer({
776
765
  const router = useRouter()
777
766
  const [submitting, setSubmitting] = React.useState(false)
778
767
  const [tagDraft, setTagDraft] = React.useState("")
779
- const [inspectorOpen, setInspectorOpen] = React.useState(true)
780
768
  const [moreOpen, setMoreOpen] = React.useState(false)
781
- /** Question-type chooser visibility — collapses into a single
782
- "selected type" tile once the author picks the first time so the
783
- inspector stays compact for the rest of the authoring flow. */
769
+ const [inspectorOpen, setInspectorOpen] = React.useState(true)
770
+ /** Question-type chooser collapses to a compact row after first pick. */
784
771
  const [typeChooserOpen, setTypeChooserOpen] = React.useState(true)
785
772
  /** Local folder list — extended in-place when the author adds one
786
773
  from the location picker so the new entry is selectable without
@@ -1102,11 +1089,6 @@ export function NewQuestionComposer({
1102
1089
  disabled={submitting}
1103
1090
  onInvoke={() => setMoreOpen(o => !o)}
1104
1091
  />
1105
- <Shortcut
1106
- keys="⌘⌥]"
1107
- disabled={submitting}
1108
- onInvoke={() => setInspectorOpen(o => !o)}
1109
- />
1110
1092
 
1111
1093
  <form
1112
1094
  onSubmit={form.handleSubmit(values => persist({ ...values, status: "in_review" }, "publish"))}
@@ -52,6 +52,7 @@ export function ProductSwitcher() {
52
52
  <DropdownMenuTrigger asChild>
53
53
  <SidebarMenuButton
54
54
  size="lg"
55
+ tooltip={iconRail ? current.label : undefined}
55
56
  className={cn(
56
57
  "items-start py-2 text-sidebar-foreground data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground",
57
58
  expandedOrMobile &&
@@ -91,8 +92,8 @@ export function ProductSwitcher() {
91
92
 
92
93
  <DropdownMenuContent
93
94
  align="start"
94
- side="bottom"
95
- sideOffset={4}
95
+ side={iconRail ? "right" : "bottom"}
96
+ sideOffset={iconRail ? 8 : 4}
96
97
  >
97
98
  <DropdownMenuLabel className="text-xs text-muted-foreground">
98
99
  Switch product
@@ -27,6 +27,7 @@ import { QUESTION_BANK_ITEMS } from "@/lib/mock/question-bank"
27
27
  import { QUESTION_BANK_HEADER_COLLABORATORS } from "@/lib/mock/question-bank-header-collaborators"
28
28
  import { DEFAULT_QUESTION_BANK_FOLDERS, type QuestionBankFolder } from "@/lib/mock/question-bank-folders"
29
29
  import { questionBankKpiInsight, questionBankKpiMetrics } from "@/lib/mock/question-bank-kpi"
30
+ import { QUESTION_BANK_SUPPORTED_VIEWS } from "@/lib/question-bank-supported-views"
30
31
  import {
31
32
  applyQuestionBankHubDisplayFilters,
32
33
  isQuestionBankDefaultNav,
@@ -122,7 +123,6 @@ export function QuestionBankClient() {
122
123
 
123
124
  // Stable Set of tab ids — defaults are constant so this only updates if tabs change.
124
125
  const tabIds = React.useMemo(() => new Set(tabs.map(t => t.id)), [tabs])
125
-
126
126
  // Keep the latest pathname / searchParamsKey / tabIds available to the (stable) hashchange
127
127
  // listener via refs, so we don't re-subscribe a window listener on every URL change.
128
128
  const navRef = React.useRef({ pathname, searchParamsKey, tabIds, hubBasePath })
@@ -314,6 +314,7 @@ export function QuestionBankClient() {
314
314
  onFoldersChange={setFolders}
315
315
  onItemsChange={setItems}
316
316
  view={tab.viewType}
317
+ supportedViewTypes={QUESTION_BANK_SUPPORTED_VIEWS}
317
318
  onViewChange={(v: DataListViewType) => updateTab({ viewType: v, icon: dataListViewIcon(v) })}
318
319
  />
319
320
  )}
@@ -417,6 +418,7 @@ export function QuestionBankClient() {
417
418
  />
418
419
  )}
419
420
  showMetrics={showMetrics}
421
+ supportedViewTypes={QUESTION_BANK_SUPPORTED_VIEWS}
420
422
  exportOpen={exportOpen}
421
423
  onExportOpenChange={setExportOpen}
422
424
  exportTotalRows={count}
@@ -433,6 +435,7 @@ export function QuestionBankClient() {
433
435
  onFoldersChange={setFolders}
434
436
  onItemsChange={setItems}
435
437
  view={tab.viewType}
438
+ supportedViewTypes={QUESTION_BANK_SUPPORTED_VIEWS}
436
439
  onViewChange={(v: DataListViewType) => updateTab({ viewType: v, icon: dataListViewIcon(v) })}
437
440
  />
438
441
  )}
@@ -0,0 +1,104 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Button } from "@/components/ui/button"
5
+ import {
6
+ DropdownMenu,
7
+ DropdownMenuContent,
8
+ DropdownMenuItem,
9
+ DropdownMenuTrigger,
10
+ } from "@/components/ui/dropdown-menu"
11
+ import { cn } from "@/lib/utils"
12
+ import { ListPageFolderColumnsPanel } from "@/components/data-views/list-page-folder-columns-panel"
13
+ import { FolderDetailsShell } from "@/components/folder-details-shell"
14
+ import type { QuestionBankItem } from "@/lib/mock/question-bank"
15
+ import {
16
+ type QuestionBankFolder,
17
+ QUESTION_BANK_FOLDER_COLOR_STYLES,
18
+ QUESTION_BANK_FOLDER_ICON_COLORS,
19
+ } from "@/lib/mock/question-bank-folders"
20
+
21
+ function isQuestionBankFolder(
22
+ item: QuestionBankFolder | QuestionBankItem,
23
+ ): item is QuestionBankFolder {
24
+ return "parentId" in item && !("stem" in item)
25
+ }
26
+
27
+ export interface QuestionBankFolderColumnsPanelProps {
28
+ folders: QuestionBankFolder[]
29
+ rows: QuestionBankItem[]
30
+ panelRenderDetail: (row: QuestionBankItem) => React.ReactNode
31
+ onAddFolder: (parentId: string | null) => void
32
+ onAddQuestion: (parentId: string | null) => void
33
+ onCustomizeFolder?: (folder: QuestionBankFolder) => void
34
+ }
35
+
36
+ /** Question bank **panel** view — Miller columns over folders + questions. */
37
+ export function QuestionBankFolderColumnsPanel({
38
+ folders,
39
+ rows,
40
+ panelRenderDetail,
41
+ onAddFolder,
42
+ onAddQuestion,
43
+ onCustomizeFolder,
44
+ }: QuestionBankFolderColumnsPanelProps) {
45
+ return (
46
+ <ListPageFolderColumnsPanel<QuestionBankFolder, QuestionBankItem>
47
+ folders={folders}
48
+ items={rows}
49
+ isFolder={isQuestionBankFolder}
50
+ getFolderParentId={f => f.parentId}
51
+ getFolderName={f => f.name}
52
+ getItemFolderId={i => i.folderId}
53
+ getItemLabel={i => i.stem}
54
+ renderItemDetail={panelRenderDetail}
55
+ onAddFolder={onAddFolder}
56
+ onAddItem={onAddQuestion}
57
+ addItemAriaLabel="Add question"
58
+ renderFolderDetail={(folder, { folders: allFolders, items }) => (
59
+ <FolderDetailsShell folder={folder} folders={allFolders} questions={items} />
60
+ )}
61
+ renderFolderRowClassName={(folder, { isSelected, depth }) =>
62
+ !isSelected && folder.colorKey && depth > 0
63
+ ? QUESTION_BANK_FOLDER_COLOR_STYLES[folder.colorKey]?.tile
64
+ : undefined
65
+ }
66
+ renderFolderIcon={(folder, { isSelected }) => (
67
+ <i
68
+ className={cn(
69
+ "fa-folder shrink-0 text-sm",
70
+ isSelected ? "fa-solid" : "fa-light",
71
+ folder.colorKey && QUESTION_BANK_FOLDER_ICON_COLORS[folder.colorKey],
72
+ )}
73
+ aria-hidden="true"
74
+ />
75
+ )}
76
+ renderItemMeta={(item, { isSelected }) =>
77
+ item.type === "multiple_choice"
78
+ ? "MCQ"
79
+ : item.difficulty?.charAt(0).toUpperCase() ?? ""
80
+ }
81
+ renderFolderActions={folder => (
82
+ <DropdownMenu>
83
+ <DropdownMenuTrigger asChild>
84
+ <Button
85
+ type="button"
86
+ size="icon-xs"
87
+ variant="ghost"
88
+ aria-label={`Actions for folder ${folder.name}`}
89
+ className="shrink-0 opacity-0 transition-opacity group-hover:opacity-100"
90
+ >
91
+ <i className="fa-light fa-ellipsis text-xs" aria-hidden="true" />
92
+ </Button>
93
+ </DropdownMenuTrigger>
94
+ <DropdownMenuContent align="end">
95
+ <DropdownMenuItem onSelect={() => onCustomizeFolder?.(folder)}>
96
+ <i className="fa-light fa-wand-magic-sparkles text-xs" aria-hidden="true" />
97
+ Customize
98
+ </DropdownMenuItem>
99
+ </DropdownMenuContent>
100
+ </DropdownMenu>
101
+ )}
102
+ />
103
+ )
104
+ }