@exxatdesignux/ui 0.2.15 → 0.2.17

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 (110) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/consumer-extras/cursor-skills/exxat-collaboration-access/SKILL.md +3 -1
  3. package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +151 -3
  4. package/consumer-extras/cursor-skills/exxat-ds-skill/references/accessibility.md +142 -0
  5. package/consumer-extras/cursor-skills/exxat-ds-skill/references/coach-marks.md +169 -0
  6. package/consumer-extras/cursor-skills/exxat-ds-skill/references/data-table-pattern.md +382 -0
  7. package/consumer-extras/cursor-skills/exxat-mono-ids/SKILL.md +56 -0
  8. package/consumer-extras/cursor-skills/exxat-primary-nav-secondary-panel/SKILL.md +17 -1
  9. package/consumer-extras/patterns/collaboration-access-pattern.md +2 -0
  10. package/package.json +3 -3
  11. package/src/components/ui/banner.tsx +2 -0
  12. package/src/components/ui/chart.tsx +57 -2
  13. package/src/components/ui/sidebar.tsx +1 -0
  14. package/src/globals.css +21 -2
  15. package/src/theme.css +4 -2
  16. package/template/.claude/skills/exxat-ds-skill/SKILL.md +1 -1
  17. package/template/.cursor/rules/exxat-mono-ids.mdc +30 -0
  18. package/template/AGENTS.md +23 -18
  19. package/template/app/(app)/data-list/page.tsx +2 -2
  20. package/template/app/(app)/question-bank/layout.tsx +27 -7
  21. package/template/app/(app)/question-bank/new/page.tsx +58 -0
  22. package/template/app/globals.css +136 -2
  23. package/template/app/layout.tsx +41 -5
  24. package/template/components/app-sidebar.tsx +141 -59
  25. package/template/components/ask-leo-sidebar.tsx +1 -4
  26. package/template/components/brand-color-picker.tsx +344 -0
  27. package/template/components/compliance-list-view.tsx +33 -51
  28. package/template/components/compliance-table.tsx +24 -0
  29. package/template/components/data-table/index.tsx +68 -24
  30. package/template/components/data-table/pagination.tsx +0 -1
  31. package/template/components/data-table/types.ts +4 -1
  32. package/template/components/data-table/use-table-state.ts +243 -94
  33. package/template/components/data-views/data-row-list.tsx +183 -0
  34. package/template/components/data-views/finder-panel-view.tsx +2 -2
  35. package/template/components/data-views/index.ts +26 -3
  36. package/template/components/data-views/list-page-split-details-placeholder.tsx +3 -3
  37. package/template/components/data-views/list-page-split-hub-tokens.ts +1 -1
  38. package/template/components/data-views/list-page-tree-column-header.tsx +1 -1
  39. package/template/components/data-views/os-folder-glyph.tsx +8 -0
  40. package/template/components/data-views/outline-tree-menu.tsx +157 -0
  41. package/template/components/data-views/question-bank-folder-tree-branch.tsx +210 -0
  42. package/template/components/export-drawer.tsx +1 -1
  43. package/template/components/exxat-product-logo.tsx +173 -379
  44. package/template/components/folder-details-shell.tsx +1 -1
  45. package/template/components/hub-tree-panel-view.tsx +88 -80
  46. package/template/components/invite-collaborators-drawer.tsx +5 -3
  47. package/template/components/key-metrics.tsx +116 -51
  48. package/template/components/new-placement-form.tsx +4 -2
  49. package/template/components/new-question-composer.tsx +2208 -0
  50. package/template/components/page-breadcrumb-trail.tsx +131 -0
  51. package/template/components/page-header.tsx +21 -11
  52. package/template/components/{data-views/placement-board-card.tsx → placement-board-card.tsx} +1 -1
  53. package/template/components/placement-detail.tsx +1 -1
  54. package/template/components/placements-board-view.tsx +1 -1
  55. package/template/components/{data-list-client.tsx → placements-client.tsx} +9 -7
  56. package/template/components/placements-list-view.tsx +18 -132
  57. package/template/components/{data-list-table-cells.test.tsx → placements-table-cells.test.tsx} +2 -2
  58. package/template/components/{data-list-table-cells.tsx → placements-table-cells.tsx} +1 -1
  59. package/template/components/placements-table-columns.tsx +2 -2
  60. package/template/components/{data-list-table.tsx → placements-table.tsx} +67 -58
  61. package/template/components/product-switcher.tsx +26 -11
  62. package/template/components/product-wordmark.tsx +285 -0
  63. package/template/components/question-bank-client.tsx +130 -70
  64. package/template/components/question-bank-hub-client.tsx +108 -115
  65. package/template/components/question-bank-list-view.tsx +30 -54
  66. package/template/components/question-bank-new-folder-sheet.tsx +1 -1
  67. package/template/components/question-bank-page-header.tsx +18 -2
  68. package/template/components/question-bank-secondary-nav.tsx +12 -228
  69. package/template/components/question-bank-table.tsx +30 -5
  70. package/template/components/rotations-empty-state.tsx +3 -0
  71. package/template/components/secondary-panel.tsx +24 -4
  72. package/template/components/settings-appearance-card.tsx +584 -141
  73. package/template/components/site-header.tsx +56 -32
  74. package/template/components/sites-list-view.tsx +31 -36
  75. package/template/components/sites-table.tsx +24 -0
  76. package/template/components/table-properties/drawer.tsx +1 -1
  77. package/template/components/team-client.tsx +1 -1
  78. package/template/components/team-list-view.tsx +34 -50
  79. package/template/components/team-table.tsx +29 -3
  80. package/template/components/templates/dedicated-search-landing-template.tsx +86 -20
  81. package/template/components/templates/list-page.tsx +1 -3
  82. package/template/components/templates/nested-secondary-panel-shell.tsx +11 -6
  83. package/template/components/ui/dot-pattern.tsx +50 -26
  84. package/template/components/ui/leo-icon.tsx +23 -3
  85. package/template/contexts/product-context.tsx +51 -7
  86. package/template/contexts/system-banner-context.tsx +112 -4
  87. package/template/docs/collaboration-access-pattern.md +2 -0
  88. package/template/docs/question-bank-hub-header-pattern.md +25 -0
  89. package/template/eslint.config.mjs +18 -0
  90. package/template/hooks/use-secondary-panel-hub-nav.ts +17 -1
  91. package/template/hooks/use-sidebar-reflow-zoom.ts +21 -11
  92. package/template/lib/data-list-persistence.ts +57 -257
  93. package/template/lib/dev-log.test.ts +6 -5
  94. package/template/lib/exxat-palette.json +1462 -0
  95. package/template/lib/exxat-palette.ts +136 -0
  96. package/template/lib/list-page-table-properties.ts +1 -1
  97. package/template/lib/list-status-badges.ts +1 -1
  98. package/template/lib/mailto.ts +29 -0
  99. package/template/lib/mock/navigation.tsx +30 -1
  100. package/template/lib/placement-board-card-layout.ts +1 -1
  101. package/template/lib/product-brand.ts +268 -0
  102. package/template/lib/question-bank-authoring.ts +308 -0
  103. package/template/lib/question-bank-nav.ts +70 -0
  104. package/template/lib/raf-throttle.ts +45 -0
  105. package/template/lib/table-state-lifecycle.ts +474 -0
  106. package/template/next.config.mjs +156 -0
  107. package/template/package.json +6 -6
  108. package/template/stores/app-store.ts +46 -1
  109. package/template/components/command-menu-01.tsx +0 -133
  110. package/template/components/command-menu-02.tsx +0 -386
@@ -0,0 +1,210 @@
1
+ "use client"
2
+
3
+ /**
4
+ * Question bank **Folders** tree — shared outline primitives (`OutlineTree*`) so the rail
5
+ * matches `HubTreePanelView` and shadcn sidebar file-tree rhythm.
6
+ */
7
+
8
+ import * as React from "react"
9
+ import Link from "next/link"
10
+ import { Collapsible, CollapsibleTrigger } from "@/components/ui/collapsible"
11
+ import { Button } from "@/components/ui/button"
12
+ import {
13
+ DropdownMenu,
14
+ DropdownMenuContent,
15
+ DropdownMenuItem,
16
+ DropdownMenuSeparator,
17
+ DropdownMenuTrigger,
18
+ } from "@/components/ui/dropdown-menu"
19
+ import { Tip } from "@/components/ui/tip"
20
+ import { cn } from "@/lib/utils"
21
+ import {
22
+ OutlineTreeCollapsibleContentRail,
23
+ OutlineTreeMenuItem,
24
+ OutlineTreeSub,
25
+ } from "@/components/data-views/outline-tree-menu"
26
+ import type { QuestionBankFolder } from "@/lib/mock/question-bank-folders"
27
+ import { QUESTION_BANK_FOLDER_ICON_COLORS } from "@/lib/mock/question-bank-folders"
28
+ import {
29
+ isQuestionBankNavActive,
30
+ questionBankHubScopeHref,
31
+ type QuestionBankNavState,
32
+ } from "@/lib/question-bank-nav"
33
+
34
+ export interface QuestionBankFolderTreeBranchProps {
35
+ folder: QuestionBankFolder
36
+ folders: QuestionBankFolder[]
37
+ pathname: string
38
+ hubSearchParams: URLSearchParams
39
+ nav: QuestionBankNavState
40
+ canManageFolders: boolean
41
+ canManageAccess: boolean
42
+ onAddSubfolder: (parentId: string) => void
43
+ onCustomizeFolder: (folder: QuestionBankFolder) => void
44
+ onManageAccess: () => void
45
+ onDeleteFolder: (folder: QuestionBankFolder) => void
46
+ }
47
+
48
+ export function QuestionBankFolderTreeBranch({
49
+ folder,
50
+ folders,
51
+ pathname,
52
+ hubSearchParams,
53
+ nav,
54
+ canManageFolders,
55
+ canManageAccess,
56
+ onAddSubfolder,
57
+ onCustomizeFolder,
58
+ onManageAccess,
59
+ onDeleteFolder,
60
+ }: QuestionBankFolderTreeBranchProps) {
61
+ const childFolders = React.useMemo(
62
+ () =>
63
+ folders
64
+ .filter(f => f.parentId === folder.id)
65
+ .sort((a, b) => a.name.localeCompare(b.name)),
66
+ [folders, folder.id],
67
+ )
68
+
69
+ const hasSubfolders = childFolders.length > 0
70
+ const isRootFolder = folder.parentId === null
71
+
72
+ const folderHref = questionBankHubScopeHref(pathname, hubSearchParams, {
73
+ scope: "folder",
74
+ folderId: folder.id,
75
+ })
76
+ const folderActive = isQuestionBankNavActive(pathname, nav, "folder", folder.id)
77
+
78
+ return (
79
+ <Collapsible defaultOpen={isRootFolder} className="group/collapsible">
80
+ <div
81
+ className={cn(
82
+ "group/row flex min-w-0 items-center rounded-md px-2 transition-colors",
83
+ folderActive
84
+ ? "bg-sidebar-accent font-medium text-sidebar-accent-foreground ring-1 ring-inset ring-sidebar-border/80"
85
+ : "text-sidebar-foreground hover:bg-sidebar-accent/50",
86
+ )}
87
+ >
88
+ {hasSubfolders ? (
89
+ <CollapsibleTrigger asChild>
90
+ <button
91
+ type="button"
92
+ className="flex size-8 shrink-0 items-center justify-center text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
93
+ aria-label={`${folder.name} — expand or collapse`}
94
+ >
95
+ <i
96
+ className="fa-light fa-chevron-right text-xs transition-transform duration-150 group-data-[state=open]/collapsible:rotate-90"
97
+ aria-hidden
98
+ />
99
+ </button>
100
+ </CollapsibleTrigger>
101
+ ) : (
102
+ <div className="size-8 shrink-0" aria-hidden />
103
+ )}
104
+ <Tip label={folder.name} side="right">
105
+ <Link
106
+ href={folderHref}
107
+ scroll={false}
108
+ aria-current={folderActive ? "page" : undefined}
109
+ className={cn(
110
+ "flex min-w-0 flex-1 items-center gap-2 py-1.5 text-left text-sm transition-colors",
111
+ "rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset",
112
+ !folderActive && "text-sidebar-foreground",
113
+ )}
114
+ >
115
+ <i
116
+ className={cn(
117
+ "fa-light inline-flex size-4 shrink-0 items-center justify-center text-sm leading-none",
118
+ folder.icon,
119
+ QUESTION_BANK_FOLDER_ICON_COLORS[folder.colorKey],
120
+ )}
121
+ aria-hidden
122
+ />
123
+ <span className="min-w-0 flex-1 truncate leading-tight">{folder.name}</span>
124
+ {hasSubfolders ? (
125
+ <span className="shrink-0 text-xs tabular-nums text-muted-foreground">{childFolders.length}</span>
126
+ ) : null}
127
+ </Link>
128
+ </Tip>
129
+ {canManageFolders ? (
130
+ <DropdownMenu>
131
+ <Tip label={`Folder actions for ${folder.name}`} side="right">
132
+ <DropdownMenuTrigger asChild>
133
+ <Button
134
+ type="button"
135
+ size="icon-xs"
136
+ variant="ghost"
137
+ aria-label={`Folder actions for ${folder.name}`}
138
+ className="shrink-0 text-muted-foreground opacity-0 transition-opacity group-hover/row:opacity-100 group-focus-within/row:opacity-100"
139
+ onClick={event => event.stopPropagation()}
140
+ >
141
+ <i className="fa-light fa-ellipsis text-xs" aria-hidden="true" />
142
+ </Button>
143
+ </DropdownMenuTrigger>
144
+ </Tip>
145
+ <DropdownMenuContent align="end">
146
+ <DropdownMenuItem
147
+ onSelect={() => {
148
+ window.setTimeout(() => onAddSubfolder(folder.id), 0)
149
+ }}
150
+ >
151
+ <i className="fa-light fa-plus text-xs" aria-hidden="true" />
152
+ Add folder
153
+ </DropdownMenuItem>
154
+ <DropdownMenuItem
155
+ onSelect={() => {
156
+ window.setTimeout(() => onCustomizeFolder(folder), 0)
157
+ }}
158
+ >
159
+ <i className="fa-light fa-wand-magic-sparkles text-xs" aria-hidden="true" />
160
+ Customize
161
+ </DropdownMenuItem>
162
+ <DropdownMenuItem
163
+ disabled={!canManageAccess}
164
+ onSelect={() => {
165
+ window.setTimeout(() => onManageAccess(), 0)
166
+ }}
167
+ >
168
+ <i className="fa-light fa-user-gear text-xs" aria-hidden="true" />
169
+ Manage access
170
+ </DropdownMenuItem>
171
+ <DropdownMenuSeparator />
172
+ <DropdownMenuItem
173
+ variant="destructive"
174
+ onSelect={() => {
175
+ window.setTimeout(() => onDeleteFolder(folder), 0)
176
+ }}
177
+ >
178
+ <i className="fa-light fa-trash text-xs" aria-hidden="true" />
179
+ Delete
180
+ </DropdownMenuItem>
181
+ </DropdownMenuContent>
182
+ </DropdownMenu>
183
+ ) : null}
184
+ </div>
185
+ {hasSubfolders ? (
186
+ <OutlineTreeCollapsibleContentRail>
187
+ <OutlineTreeSub surface="sidebar" guideLayout="chevronRail">
188
+ {childFolders.map(child => (
189
+ <OutlineTreeMenuItem key={child.id}>
190
+ <QuestionBankFolderTreeBranch
191
+ folder={child}
192
+ folders={folders}
193
+ pathname={pathname}
194
+ hubSearchParams={hubSearchParams}
195
+ nav={nav}
196
+ canManageFolders={canManageFolders}
197
+ canManageAccess={canManageAccess}
198
+ onAddSubfolder={onAddSubfolder}
199
+ onCustomizeFolder={onCustomizeFolder}
200
+ onManageAccess={onManageAccess}
201
+ onDeleteFolder={onDeleteFolder}
202
+ />
203
+ </OutlineTreeMenuItem>
204
+ ))}
205
+ </OutlineTreeSub>
206
+ </OutlineTreeCollapsibleContentRail>
207
+ ) : null}
208
+ </Collapsible>
209
+ )
210
+ }
@@ -145,7 +145,7 @@ export function ExportDrawer({
145
145
  side="right"
146
146
  showCloseButton={false}
147
147
  showOverlay={false}
148
- className="z-[60] w-80 sm:max-w-80 p-0 gap-0 flex flex-col border border-border shadow-xl rounded-xl overflow-hidden"
148
+ className="z-[80] w-80 sm:max-w-80 p-0 gap-0 flex flex-col border border-border shadow-xl rounded-xl overflow-hidden"
149
149
  style={{ top: "0.5rem", bottom: "0.5rem", right: "0.5rem", height: "calc(100vh - 1rem)" }}
150
150
  >
151
151
  {/* Header */}