@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.
- package/CHANGELOG.md +15 -0
- package/consumer-extras/AGENTS.md +76 -0
- package/consumer-extras/README.md +5 -1
- package/consumer-extras/cursor-skills/exxat-centralized-list-dataset/SKILL.md +14 -3
- package/consumer-extras/cursor-skills/exxat-consumer-app/SKILL.md +37 -0
- package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +21 -6
- package/consumer-extras/cursor-skills/exxat-focused-workflow-page/SKILL.md +57 -0
- package/consumer-extras/cursor-skills/exxat-primary-nav-secondary-panel/SKILL.md +4 -2
- package/consumer-extras/patterns/consumer-app-pattern.md +39 -0
- package/consumer-extras/patterns/consumer-upgrade-checklist.md +20 -0
- package/consumer-extras/patterns/data-views-pattern.md +40 -3
- package/consumer-extras/patterns/focused-workflow-page-pattern.md +84 -0
- package/consumer-extras/patterns/shell-surface-elevation-pattern.md +5 -3
- package/package.json +2 -1
- package/src/components/ui/button-group.tsx +81 -0
- package/src/components/ui/button.tsx +4 -4
- package/src/globals.css +7 -1858
- package/src/theme.css +10 -1126
- package/src/tokens/README.md +15 -0
- package/src/tokens/base.css +337 -0
- package/src/tokens/high-contrast.css +1195 -0
- package/src/tokens/layers.css +224 -0
- package/src/tokens/tailwind-bridge.css +118 -0
- package/src/tokens/themes.css +201 -0
- package/template/AGENTS.md +60 -22
- package/template/app/(app)/dashboard/loading.tsx +3 -15
- package/template/app/(app)/dashboard/page.tsx +2 -14
- package/template/app/(app)/data-list/layout.tsx +43 -0
- package/template/app/(app)/data-list/page.tsx +2 -2
- package/template/app/(app)/examples/focused-workflow/page.tsx +5 -0
- package/template/app/(app)/examples/page.tsx +1 -0
- package/template/app/(app)/loading.tsx +1 -18
- package/template/app/(app)/question-bank/find/page.tsx +2 -1
- package/template/app/(app)/question-bank/library/page.tsx +2 -1
- package/template/app/(app)/question-bank/list/page.tsx +2 -1
- package/template/app/(app)/question-bank/new/page.tsx +15 -23
- package/template/app/(app)/question-bank/page.tsx +2 -1
- package/template/app/(app)/settings/page.tsx +4 -5
- package/template/app/globals.css +7 -1964
- package/template/components/app-route-loading.tsx +14 -0
- package/template/components/app-sidebar.tsx +70 -55
- package/template/components/data-views/index.ts +37 -9
- package/template/components/data-views/list-page-calendar-view.tsx +593 -0
- package/template/components/data-views/list-page-connected-view-body.tsx +66 -0
- package/template/components/data-views/list-page-folder-columns-panel.tsx +345 -0
- package/template/components/data-views/list-page-split-hub-chrome.tsx +8 -0
- package/template/components/examples/focused-workflow-showcase.tsx +183 -0
- package/template/components/list-hub-board-view.tsx +68 -0
- package/template/components/list-hub-client.tsx +186 -0
- package/template/components/list-hub-list-view.tsx +36 -0
- package/template/components/list-hub-panel-activator.tsx +8 -0
- package/template/components/list-hub-secondary-nav.tsx +121 -0
- package/template/components/list-hub-table.tsx +336 -0
- package/template/components/new-question-composer.tsx +6 -24
- package/template/components/product-switcher.tsx +3 -2
- package/template/components/question-bank-client.tsx +4 -1
- package/template/components/question-bank-folder-columns-panel.tsx +104 -0
- package/template/components/question-bank-table.tsx +143 -485
- package/template/components/secondary-panel/nav-link-rows.tsx +83 -0
- package/template/components/secondary-panel.tsx +4 -44
- package/template/components/secondary-panels/list-hub-panel.tsx +39 -0
- package/template/components/secondary-panels/question-bank-panel.tsx +39 -0
- package/template/components/secondary-panels/registry.tsx +15 -0
- package/template/components/settings-appearance-card.tsx +3 -2
- package/template/components/settings-client.tsx +59 -15
- package/template/components/settings-form-row.tsx +9 -4
- package/template/components/table-properties/drawer-button.tsx +13 -0
- package/template/components/table-properties/drawer.tsx +65 -4
- package/template/components/templates/focused-workflow-layouts.tsx +448 -0
- package/template/components/templates/focused-workflow-page-template.tsx +69 -0
- package/template/components/templates/list-page.tsx +29 -5
- package/template/components/templates/nested-secondary-panel-shell.tsx +2 -1
- package/template/components/templates/page-loading-shell.tsx +262 -0
- package/template/components/ui/button-group.tsx +1 -0
- package/template/docs/consumer-app-pattern.md +39 -0
- package/template/docs/data-views-pattern.md +40 -3
- package/template/docs/drawer-vs-dialog-pattern.md +3 -1
- package/template/docs/focused-workflow-page-pattern.md +84 -0
- package/template/docs/shell-surface-elevation-pattern.md +5 -3
- package/template/lib/command-menu-search-data.ts +11 -27
- package/template/lib/data-list-display-options.ts +16 -2
- package/template/lib/data-list-view-registry.ts +104 -0
- package/template/lib/data-list-view-surface.ts +15 -1
- package/template/lib/data-list-view.ts +10 -1
- package/template/lib/data-view-dashboard-storage.ts +38 -35
- package/template/lib/hub-connected-view-renderers.ts +58 -0
- package/template/lib/list-hub-nav.ts +121 -0
- package/template/lib/list-hub-supported-views.ts +10 -0
- package/template/lib/list-page-table-properties.ts +3 -7
- package/template/lib/list-status-badges.ts +4 -97
- package/template/lib/mock/list-hub-directory.ts +27 -0
- package/template/lib/mock/list-hub-kpi.ts +27 -0
- package/template/lib/mock/navigation.tsx +1 -0
- package/template/lib/page-loading-variant.ts +40 -0
- package/template/lib/question-bank-supported-views.ts +13 -0
- package/template/lib/table-state-lifecycle.ts +2 -2
- package/template/app/(app)/data-list/[id]/page.tsx +0 -44
- package/template/app/(app)/data-list/new/page.tsx +0 -34
- package/template/components/compliance-board-view.tsx +0 -142
- package/template/components/compliance-client.tsx +0 -92
- package/template/components/compliance-list-view.tsx +0 -54
- package/template/components/compliance-page-header.tsx +0 -89
- package/template/components/compliance-table.tsx +0 -612
- package/template/components/data-view-dashboard-charts-compliance.tsx +0 -963
- package/template/components/data-view-dashboard-charts-team.tsx +0 -971
- package/template/components/data-view-dashboard-charts.tsx +0 -1503
- package/template/components/new-placement-back-btn.tsx +0 -28
- package/template/components/new-placement-form.tsx +0 -1068
- package/template/components/placement-board-card.tsx +0 -262
- package/template/components/placement-detail.tsx +0 -438
- package/template/components/placements-board-view.tsx +0 -404
- package/template/components/placements-client.tsx +0 -252
- package/template/components/placements-list-view.tsx +0 -171
- package/template/components/placements-page-header.tsx +0 -166
- package/template/components/placements-table-cells.test.tsx +0 -22
- package/template/components/placements-table-cells.tsx +0 -173
- package/template/components/placements-table-columns.tsx +0 -640
- package/template/components/placements-table.tsx +0 -1642
- package/template/components/rotations-empty-state.tsx +0 -50
- package/template/components/rotations-panel-activator.tsx +0 -8
- package/template/components/sites-all-client.tsx +0 -154
- package/template/components/sites-board-view.tsx +0 -67
- package/template/components/sites-list-view.tsx +0 -42
- package/template/components/sites-table.tsx +0 -382
- package/template/components/team-board-view.tsx +0 -122
- package/template/components/team-client.tsx +0 -100
- package/template/components/team-list-view.tsx +0 -59
- package/template/components/team-page-header.tsx +0 -92
- package/template/components/team-table.tsx +0 -693
- package/template/lib/data-view-dashboard-placements-layout.ts +0 -215
- package/template/lib/mock/compliance-kpi.ts +0 -61
- package/template/lib/mock/compliance.ts +0 -146
- package/template/lib/mock/placements-kpi.ts +0 -134
- package/template/lib/mock/placements.ts +0 -183
- package/template/lib/mock/sites-directory.ts +0 -16
- package/template/lib/mock/sites-kpi.ts +0 -25
- package/template/lib/mock/team-kpi.ts +0 -60
- package/template/lib/mock/team.ts +0 -118
- package/template/lib/placement-board-card-layout.ts +0 -79
- package/template/lib/placement-lifecycle.ts +0 -5
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { usePathname } from "next/navigation"
|
|
4
|
+
|
|
5
|
+
import { PageLoadingByVariant } from "@/components/templates/page-loading-shell"
|
|
6
|
+
import { resolvePageLoadingVariant } from "@/lib/page-loading-variant"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Route-level loading UI — keeps `SidebarInset` + header chrome; skeleton matches destination template.
|
|
10
|
+
*/
|
|
11
|
+
export function AppRouteLoading() {
|
|
12
|
+
const pathname = usePathname()
|
|
13
|
+
return <PageLoadingByVariant variant={resolvePageLoadingVariant(pathname)} />
|
|
14
|
+
}
|
|
@@ -105,11 +105,22 @@ function normalizedLocationHash(locationHash: string): string {
|
|
|
105
105
|
return locationHash.startsWith("#") ? locationHash.slice(1) : locationHash
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Paths where a sibling sidebar row uses `#fragment` on the same pathname
|
|
110
|
+
* (e.g. Settings vs Tokens & themes). The plain-path row must not stay active
|
|
111
|
+
* when the hash belongs to that sibling.
|
|
112
|
+
*/
|
|
113
|
+
const NAV_EXCLUSIVE_HASH_BY_PATH: Readonly<Record<string, readonly string[]>> = {
|
|
114
|
+
"/settings": ["appearance"],
|
|
115
|
+
"/help": ["more"],
|
|
116
|
+
}
|
|
117
|
+
|
|
108
118
|
/**
|
|
109
119
|
* Whether `pathname` (+ optional `location.hash`) matches a sidebar `href`.
|
|
110
120
|
* When several links share the same path (e.g. `/settings`), disambiguate with `#fragment`
|
|
111
121
|
* in each `href` — those rows use the `frag !== null` branch below.
|
|
112
|
-
* For `href` without `#…`, an in-page hash (e.g. QB view tabs) does not clear the match
|
|
122
|
+
* For `href` without `#…`, an in-page hash (e.g. QB view tabs) does not clear the match,
|
|
123
|
+
* except when the hash is reserved for a hash-sibling row on that path.
|
|
113
124
|
*/
|
|
114
125
|
function isNavActive(pathname: string, url: string, locationHash = ""): boolean {
|
|
115
126
|
const pathOnly = navUrlPath(url)
|
|
@@ -131,7 +142,13 @@ function isNavActive(pathname: string, url: string, locationHash = ""): boolean
|
|
|
131
142
|
|
|
132
143
|
if (pathOnly === "/") return pathname === "/" && h === ""
|
|
133
144
|
/** Exact path match — ignore `location.hash` when the nav `href` has no `#…` fragment (QB view tabs use hash). */
|
|
134
|
-
if (pathname === pathOnly)
|
|
145
|
+
if (pathname === pathOnly) {
|
|
146
|
+
if (h !== "") {
|
|
147
|
+
const exclusive = NAV_EXCLUSIVE_HASH_BY_PATH[pathOnly]
|
|
148
|
+
if (exclusive?.includes(h)) return false
|
|
149
|
+
}
|
|
150
|
+
return true
|
|
151
|
+
}
|
|
135
152
|
// Design system library — active on hub and detail routes.
|
|
136
153
|
if (pathOnly === "/library") {
|
|
137
154
|
return pathname.startsWith("/library/")
|
|
@@ -827,60 +844,58 @@ function ProductLogoButton() {
|
|
|
827
844
|
|
|
828
845
|
return (
|
|
829
846
|
<DropdownMenu>
|
|
830
|
-
<
|
|
831
|
-
<
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
</DropdownMenuTrigger>
|
|
877
|
-
</TooltipTrigger>
|
|
878
|
-
<TooltipContent side="right" align="center" hidden={state !== "collapsed" || isMobile}>
|
|
879
|
-
{current.label}
|
|
880
|
-
</TooltipContent>
|
|
881
|
-
</Tooltip>
|
|
847
|
+
<DropdownMenuTrigger asChild>
|
|
848
|
+
<SidebarMenuButton
|
|
849
|
+
size="lg"
|
|
850
|
+
tooltip={iconRail ? current.label : undefined}
|
|
851
|
+
className={cn(
|
|
852
|
+
"py-2 text-sidebar-foreground data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground",
|
|
853
|
+
expandedOrMobile &&
|
|
854
|
+
"h-auto min-h-12 !overflow-visible items-center [&>span:last-child]:!overflow-visible [&>span:last-child]:!whitespace-normal [&>span:last-child]:text-clip",
|
|
855
|
+
"group-data-[collapsible=icon]:items-center group-data-[collapsible=icon]:justify-center",
|
|
856
|
+
iconRail &&
|
|
857
|
+
"group-data-[collapsible=icon]:!size-9 group-data-[collapsible=icon]:!min-h-9 group-data-[collapsible=icon]:!max-h-9 group-data-[collapsible=icon]:!p-0 group-data-[collapsible=icon]:overflow-visible",
|
|
858
|
+
)}
|
|
859
|
+
aria-label={`Current product: ${current.label}. Switch product`}
|
|
860
|
+
suppressHydrationWarning
|
|
861
|
+
>
|
|
862
|
+
{iconRail ? (
|
|
863
|
+
// Match the school selector footprint in the icon rail (32px frame,
|
|
864
|
+
// 28px mark — same visual weight as the avatar with inset padding).
|
|
865
|
+
<span className="flex size-8 shrink-0 items-center justify-center">
|
|
866
|
+
<ExxatProductMark product={current.id} className="size-7" />
|
|
867
|
+
</span>
|
|
868
|
+
) : (
|
|
869
|
+
<span className="flex min-h-0 min-w-0 flex-1 items-stretch gap-2">
|
|
870
|
+
<span
|
|
871
|
+
className="flex min-h-0 min-w-0 flex-1 items-center justify-start overflow-visible"
|
|
872
|
+
aria-hidden="true"
|
|
873
|
+
>
|
|
874
|
+
<ExxatProductLogo
|
|
875
|
+
product={current.id}
|
|
876
|
+
variant="mutedSuffix"
|
|
877
|
+
className="w-auto max-w-[min(100%,280px)] object-left object-contain"
|
|
878
|
+
/>
|
|
879
|
+
</span>
|
|
880
|
+
<span
|
|
881
|
+
className="flex w-6 shrink-0 items-center justify-center self-stretch text-muted-foreground"
|
|
882
|
+
aria-hidden="true"
|
|
883
|
+
>
|
|
884
|
+
<i
|
|
885
|
+
className="fa-light fa-chevron-down block text-xs leading-none"
|
|
886
|
+
aria-hidden="true"
|
|
887
|
+
/>
|
|
888
|
+
</span>
|
|
889
|
+
</span>
|
|
890
|
+
)}
|
|
891
|
+
</SidebarMenuButton>
|
|
892
|
+
</DropdownMenuTrigger>
|
|
882
893
|
|
|
883
|
-
<DropdownMenuContent
|
|
894
|
+
<DropdownMenuContent
|
|
895
|
+
align="start"
|
|
896
|
+
side={iconRail ? "right" : "bottom"}
|
|
897
|
+
sideOffset={iconRail ? 8 : 4}
|
|
898
|
+
>
|
|
884
899
|
<DropdownMenuLabel className="text-xs text-muted-foreground">
|
|
885
900
|
Switch product
|
|
886
901
|
</DropdownMenuLabel>
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Central exports for list-page data surfaces and shared view chrome.
|
|
3
3
|
*
|
|
4
|
-
* **Pattern:** `ListPageTemplate` +
|
|
4
|
+
* **Pattern:** `ListPageTemplate` + hub `*-table.tsx` — one `useTableState`, one toolbar,
|
|
5
5
|
* table | list | board | dashboard from the same component (`AGENTS.md` §4, `docs/data-views-pattern.md`).
|
|
6
6
|
*
|
|
7
7
|
* **View UI:** `ViewSegmentedControl` matches the template’s views toolbar (`bg-muted/60` pills).
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
export { PlacementsTable } from "@/components/placements-table"
|
|
11
|
-
export type { PlacementsTableProps, PlacementsTableHandle } from "@/components/placements-table"
|
|
12
|
-
export type { PlacementLifecycleTabId } from "@/lib/placement-lifecycle"
|
|
13
10
|
export type { DataListViewType } from "@/lib/data-list-view"
|
|
14
11
|
export { DATA_LIST_VIEW_TILES, dataListViewIcon, dataListViewLabel } from "@/lib/data-list-view"
|
|
15
12
|
|
|
@@ -39,6 +36,7 @@ export {
|
|
|
39
36
|
export {
|
|
40
37
|
ListPageSplitHubChrome,
|
|
41
38
|
LIST_PAGE_SPLIT_HUB_HEIGHT_STYLE,
|
|
39
|
+
LIST_PAGE_CALENDAR_HEIGHT_STYLE,
|
|
42
40
|
type ListPageSplitHubChromeProps,
|
|
43
41
|
} from "@/components/data-views/list-page-split-hub-chrome"
|
|
44
42
|
|
|
@@ -63,8 +61,7 @@ export {
|
|
|
63
61
|
type OutlineTreeSurface,
|
|
64
62
|
} from "@/components/data-views/outline-tree-menu"
|
|
65
63
|
|
|
66
|
-
|
|
67
|
-
export type { QuestionBankFolderTreeBranchProps } from "@/components/data-views/question-bank-folder-tree-branch"
|
|
64
|
+
/** Question-bank nav only — import from `@/components/data-views/question-bank-folder-tree-branch`. */
|
|
68
65
|
|
|
69
66
|
export {
|
|
70
67
|
LIST_PAGE_SPLIT_MILLER_COLUMN_PANEL_CLASS,
|
|
@@ -83,10 +80,12 @@ export {
|
|
|
83
80
|
type FolderDetailsShellProps,
|
|
84
81
|
} from "@/components/folder-details-shell"
|
|
85
82
|
|
|
83
|
+
/** Hub-specific tree+inspector — import from `@/components/hub-tree-panel-view` (not generic DS). */
|
|
84
|
+
|
|
86
85
|
export {
|
|
87
|
-
|
|
88
|
-
type
|
|
89
|
-
} from "@/components/
|
|
86
|
+
ListPageFolderColumnsPanel,
|
|
87
|
+
type ListPageFolderColumnsPanelProps,
|
|
88
|
+
} from "@/components/data-views/list-page-folder-columns-panel"
|
|
90
89
|
|
|
91
90
|
export {
|
|
92
91
|
ListPageTreePanelShell,
|
|
@@ -103,6 +102,35 @@ export {
|
|
|
103
102
|
/** Generic folder icon-grid — reusable across all list hubs. */
|
|
104
103
|
export { FolderGridView, type FolderGridViewProps } from "@/components/data-views/folder-grid-view"
|
|
105
104
|
|
|
105
|
+
/** Month calendar — same `tableState.rows` as table / list / board. */
|
|
106
|
+
export {
|
|
107
|
+
ListPageCalendarView,
|
|
108
|
+
type ListPageCalendarViewProps,
|
|
109
|
+
} from "@/components/data-views/list-page-calendar-view"
|
|
110
|
+
|
|
111
|
+
/** Hub view router — switch on `DataListViewRenderKind`; missing renderer = explicit empty state. */
|
|
112
|
+
export {
|
|
113
|
+
ListPageConnectedViewBody,
|
|
114
|
+
ListPageViewNotConfigured,
|
|
115
|
+
type ListPageConnectedViewBodyProps,
|
|
116
|
+
type ListPageConnectedViewRenderers,
|
|
117
|
+
} from "@/components/data-views/list-page-connected-view-body"
|
|
118
|
+
|
|
119
|
+
export {
|
|
120
|
+
DATA_LIST_VIEW_REGISTRY,
|
|
121
|
+
dataListViewDefinition,
|
|
122
|
+
dataListViewTilesForHub,
|
|
123
|
+
showsListPageHubMetricsStrip,
|
|
124
|
+
isDataListViewTypeSupported,
|
|
125
|
+
} from "@/lib/data-list-view-registry"
|
|
126
|
+
|
|
127
|
+
export {
|
|
128
|
+
defineHubViewRenderers,
|
|
129
|
+
hubRenderKindsForSupported,
|
|
130
|
+
type HubConnectedViewRenderers,
|
|
131
|
+
type HubRenderKindForViews,
|
|
132
|
+
} from "@/lib/hub-connected-view-renderers"
|
|
133
|
+
|
|
106
134
|
/** Generic vertical row list — used by every hub's "list" tab. Composes
|
|
107
135
|
* `ListPageBoardCard layout="row"` via a `renderRow` prop. */
|
|
108
136
|
export { DataRowList, type DataRowListProps } from "@/components/data-views/data-row-list"
|