@exxatdesignux/ui 0.5.2 → 0.5.4
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 +18 -0
- package/README.md +1 -1
- package/consumer-extras/cursor-rules/exxat-accessibility.mdc +1 -1
- package/consumer-extras/cursor-rules/exxat-data-tables.mdc +8 -6
- package/consumer-extras/cursor-rules/exxat-drawer-vs-dialog.mdc +4 -4
- package/consumer-extras/cursor-rules/exxat-ds-agents.mdc +6 -1
- package/consumer-extras/cursor-rules/exxat-hub-supported-views.mdc +54 -0
- package/consumer-extras/cursor-rules/exxat-nav-single-active.mdc +31 -0
- package/consumer-extras/cursor-rules/exxat-no-vaul.mdc +25 -0
- package/consumer-extras/cursor-rules/exxat-page-header-actions.mdc +31 -0
- package/consumer-extras/cursor-rules/exxat-table-row-preview.mdc +24 -0
- package/consumer-extras/cursor-rules/exxat-tabs-chrome.mdc +31 -0
- package/consumer-extras/cursor-skills/exxat-drawer-vs-dialog/SKILL.md +5 -5
- package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +10 -5
- package/consumer-extras/cursor-skills/exxat-ds-skill/references/accessibility.md +1 -1
- package/consumer-extras/cursor-skills/exxat-ds-skill/references/data-table-pattern.md +15 -5
- package/consumer-extras/cursor-skills/exxat-token-economy/SKILL.md +14 -5
- package/consumer-extras/handbook/HANDBOOK.md +1 -1
- package/consumer-extras/handbook/reference-implementations.md +2 -2
- package/consumer-extras/patterns/consumer-upgrade-checklist.md +14 -1
- package/consumer-extras/patterns/data-views-pattern.md +6 -0
- package/consumer-extras/patterns/drawer-vs-dialog-pattern.md +50 -0
- package/consumer-extras/patterns/hub-supported-views-pattern.md +53 -0
- package/dist/components/data-table/index.js +13 -9
- package/dist/components/data-table/index.js.map +1 -1
- package/dist/components/data-table/pagination.js +13 -9
- package/dist/components/data-table/pagination.js.map +1 -1
- package/dist/components/data-views/hub-table.d.ts +8 -4
- package/dist/components/data-views/hub-table.js +25 -10
- package/dist/components/data-views/hub-table.js.map +1 -1
- package/dist/components/data-views/index.d.ts +1 -1
- package/dist/components/data-views/index.js +25 -10
- package/dist/components/data-views/index.js.map +1 -1
- package/dist/components/data-views/list-page-connected-view-body.d.ts +1 -1
- package/dist/components/data-views/list-page-connected-view-body.js +1 -0
- package/dist/components/data-views/list-page-connected-view-body.js.map +1 -1
- package/dist/components/table-properties/drawer-button.js +1 -0
- package/dist/components/table-properties/drawer-button.js.map +1 -1
- package/dist/components/table-properties/drawer.js +1 -0
- package/dist/components/table-properties/drawer.js.map +1 -1
- package/dist/components/table-properties/index.d.ts +1 -1
- package/dist/components/table-properties/index.js +1 -0
- package/dist/components/table-properties/index.js.map +1 -1
- package/dist/components/templates/index.d.ts +1 -1
- package/dist/components/templates/index.js +12 -2
- package/dist/components/templates/index.js.map +1 -1
- package/dist/components/templates/list-page.d.ts +4 -2
- package/dist/components/templates/list-page.js +12 -2
- package/dist/components/templates/list-page.js.map +1 -1
- package/dist/{data-list-view-registry-CyBoBML4.d.ts → data-list-view-registry-BstmlfQ3.d.ts} +16 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.js +135 -126
- package/dist/index.js.map +1 -1
- package/dist/lib/data-list-view-registry.d.ts +1 -1
- package/dist/lib/data-list-view-registry.js +17 -1
- package/dist/lib/data-list-view-registry.js.map +1 -1
- package/dist/lib/data-list-view-surface.d.ts +1 -1
- package/dist/lib/data-list-view-surface.js +1 -0
- package/dist/lib/data-list-view-surface.js.map +1 -1
- package/dist/lib/list-page-table-properties.d.ts +1 -1
- package/dist/lib/list-page-table-properties.js +1 -0
- package/dist/lib/list-page-table-properties.js.map +1 -1
- package/dist/lib/nav-active.d.ts +38 -0
- package/dist/lib/nav-active.js +104 -0
- package/dist/lib/nav-active.js.map +1 -0
- package/package.json +1 -2
- package/src/components/data-table/index.tsx +25 -17
- package/src/components/data-views/hub-table.tsx +9 -3
- package/src/components/templates/list-page.tsx +9 -3
- package/src/index.ts +1 -1
- package/src/lib/data-list-view-registry.ts +31 -0
- package/src/lib/nav-active.ts +162 -0
- package/template/.claude/skills/exxat-ds-skill/SKILL.md +2 -1
- package/template/.cursor/rules/exxat-accessibility.mdc +1 -1
- package/template/AGENTS.md +18 -3
- package/template/components/columns-client.tsx +3 -2
- package/template/components/columns-showcase.tsx +22 -18
- package/template/components/exxat-product-logo.tsx +1 -1
- package/template/components/library-table.tsx +62 -23
- package/template/components/new-library-item-form.tsx +0 -7
- package/template/components/product-wordmark.tsx +1 -1
- package/template/components/sidebar/app-sidebar.tsx +14 -106
- package/template/components/sidebar/secondary-nav.tsx +22 -4
- package/template/components/site-header.tsx +1 -1
- package/template/components/tokens-hub-auxiliary-views.tsx +301 -0
- package/template/components/tokens-themes-client.tsx +44 -16
- package/template/docs/HANDBOOK.md +2 -2
- package/template/docs/component-selection-guide.md +1 -1
- package/template/docs/consumer-upgrade-checklist.md +51 -0
- package/template/docs/data-views-pattern.md +6 -0
- package/template/docs/drawer-vs-dialog-pattern.md +8 -8
- package/template/docs/glossary.md +2 -1
- package/template/docs/hub-supported-views-pattern.md +53 -0
- package/template/docs/reference-implementations.md +2 -2
- package/template/lib/full-hub-supported-views.ts +8 -0
- package/template/lib/library-supported-views.ts +5 -12
- package/template/lib/motion-ui.ts +2 -2
- package/template/package.json +1 -1
- package/tokens/hooks-index.json +2 -2
- package/dist/components/ui/drawer.d.ts +0 -16
- package/dist/components/ui/drawer.js +0 -125
- package/dist/components/ui/drawer.js.map +0 -1
- package/src/components/ui/drawer.tsx +0 -134
- package/template/components/ui/drawer.tsx +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- **Remove Vaul `Drawer` primitive** — side panels use **`Sheet`** only (`ExportDrawer`, `TablePropertiesDrawer`, invite/folder sheets). **`vaul`** dropped from package dependencies; consumers should remove it from their app and delete any `components/ui/drawer.tsx` shim.
|
|
8
|
+
- **New consumer agent rules (vendored via `sync-extras`):** `exxat-no-vaul`, `exxat-tabs-chrome` (no full-width tab stretch), `exxat-page-header-actions`, `exxat-table-row-preview` (HoverCard + DS cells).
|
|
9
|
+
- **`consumer-upgrade-checklist.md`** — Prism-style audit table (tabs, header buttons, popovers, hub views).
|
|
10
|
+
- **Docs:** sheet-panel vs dialog pattern; landing page start-fresh three-step commands at `@0.5.3+`.
|
|
11
|
+
|
|
12
|
+
## 0.5.3
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- **List hubs default to seven connected views.** `FULL_HUB_SUPPORTED_VIEWS` (table, list, board, dashboard, folder, panel, tree-panel) is exported from `lib/data-list-view-registry` and used as the default `supportedViewTypes` on `HubTable` and `ListPageTemplate`. `PRIMARY_HUB_SUPPORTED_VIEWS` remains for documented four-view exceptions.
|
|
17
|
+
- **`prepare` builds `dist/` when missing** (`scripts/ensure-dist.mjs`) so fresh installs resolve `@exxatdesignux/ui` subpath imports without a manual build step.
|
|
18
|
+
- **`nav-active` helpers** (`lib/nav-active.ts`) for single-active sidebar / secondary nav links.
|
|
19
|
+
- **Vendored agent docs:** `exxat-hub-supported-views.mdc` and `hub-supported-views-pattern.md` in `consumer-extras` so npm consumers get Add view parity guardrails.
|
|
20
|
+
|
|
3
21
|
## 0.5.2
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -89,7 +89,7 @@ code are never touched.
|
|
|
89
89
|
## What's inside
|
|
90
90
|
|
|
91
91
|
- **Primitives** (`@exxatdesignux/ui/components/<name>`) — Button, Input,
|
|
92
|
-
Select, Dialog, AlertDialog, Sheet,
|
|
92
|
+
Select, Dialog, AlertDialog, Sheet, Popover, DropdownMenu,
|
|
93
93
|
ContextMenu, Tabs, Accordion, Tooltip, Toast-free Banner, ScrollArea,
|
|
94
94
|
Slider, HoverCard, Avatar, Badge, StatusBadge, Calendar, Kbd, …
|
|
95
95
|
- **Hub stack** — `DataTable<TRow>`, `useTableState<TRow>`,
|
|
@@ -17,7 +17,7 @@ alwaysApply: true
|
|
|
17
17
|
4. **Touch targets (2.5.8):** interactive controls **≥ 24×24 CSS px** or **24px** spacing so hit areas do not overlap — use **`min-h-6 min-w-6` / `size-6`** for icon-only targets; avoid **`size-4`** as the sole target.
|
|
18
18
|
5. **Contrast:** normal text **≥ 4.5:1**; UI components / focus where required **≥ 3:1**; muted text on tinted surfaces use tokens against the correct surface (e.g. sidebar), not only `--background`.
|
|
19
19
|
6. **Minimum text size:** visible product copy **≥ 11px** — use **`text-xs`** or larger; **MUST NOT** use arbitrary Tailwind font sizes below that (see **`apps/web/AGENTS.md` §8.3**, **`apps/web/app/globals.css`** `--text-xs`).
|
|
20
|
-
7. **Dialogs / sheets
|
|
20
|
+
7. **Dialogs / sheets:** must expose a **Title** (`DialogTitle` / `SheetTitle`); use **`sr-only`** if visually hidden (shadcn pattern).
|
|
21
21
|
8. **Format hints are persistent, not placeholders (SC 3.3.2 Labels or Instructions, 1.3.1).** Any field that expects a specific format — **date, time, phone, currency, ID pattern, GPA scale, URL, hours, unit-bearing numbers** — MUST show the format as **persistent helper text** via **`FormDescription`** (or the field's description slot). Placeholders disappear on focus and are unreliable for AT, so **MUST NOT** be the sole carrier of the format. Example: GPA → "Out of 4.0"; Date → "MM/DD/YYYY"; Phone → "+1 (555) 555-0100"; Student ID → "STU-YYYY-####". Pair with `inputMode`, `pattern`, or a picker primitive (e.g. `DatePickerField`) where applicable; never rely on a free-text date input.
|
|
22
22
|
9. **Every icon that communicates information MUST have a text alternative** — not just icon-only buttons. Three cases (SC 1.1.1 Non-text Content, 3.3.2 Labels or Instructions, 2.4.6 Headings and Labels):
|
|
23
23
|
|
|
@@ -7,7 +7,7 @@ alwaysApply: true
|
|
|
7
7
|
|
|
8
8
|
## Use one stack for product data lists
|
|
9
9
|
|
|
10
|
-
For **any app screen that shows a browsable, filterable grid of records** (
|
|
10
|
+
For **any app screen that shows a browsable, filterable grid of records** (directories, tokens, columns showcase, question banks, etc.):
|
|
11
11
|
|
|
12
12
|
1. **Inside `ListPageTemplate` (every primary hub + every showcase page that mounts a hub-style grid) MUST use `HubTable`** from `@/components/data-views`. `HubTable` is the **canonical** wrapper that wires `useTableState`, the toolbar (**search + filter chips + filter dropdown + sort**), the **Table properties drawer**, view-type tiles, bulk-actions, and conditional rules in one place. Pages that drop down to raw `<DataTable>` silently lose filters and Properties — **MUST NOT** ship a hub or showcase that way.
|
|
13
13
|
2. **Outside `ListPageTemplate`** (rare — e.g. embedded mini-grids, modal sub-tables, drawer body lists), use `DataTable` from `@/components/data-table` directly. Even then, prefer composing a small `toolbarSlot` with `TablePropertiesDrawerButton` whenever the grid is more than a handful of rows.
|
|
@@ -17,14 +17,13 @@ For **any app screen that shows a browsable, filterable grid of records** (lists
|
|
|
17
17
|
6. **Table properties:** Always reachable. `HubTable` mounts `TablePropertiesDrawerButton` in `toolbarSlot`; on **`ListPageTemplate`** pages with **table / list / board / dashboard** tabs it **MUST** also receive **`currentView`** and **`onViewChange`** (see **`apps/web/AGENTS.md` §4.2** and **`.cursor/rules/exxat-table-properties-drawer.mdc`**) so Properties matches the selected view.
|
|
18
18
|
7. **Dropdown menus:** `DropdownMenuContent` uses the shared **`@exxatdesignux/ui`** default (**intrinsic `w-max`**, **`min-w-52`**, capped **`max-w`**) for view settings, row ⋯, column menus, and filter pickers — **pure CSS**, no **`ResizeObserver`**. Override only for deliberate narrow/wide rails (e.g. pagination **`w-20`**, account trigger-width, school switcher **`!w-max min-w-72 …`**). See **`docs/data-views-pattern.md`** (“Dropdown menus”).
|
|
19
19
|
8. **Cell renderers MUST come from `@/components/data-views` (`table-cells.tsx`).** The DS ships **`ProgressCell`**, **`CurrencyCell`**, **`NumericCell`**, **`RatingCell`**, **`SignalBarsCell`**, **`BooleanToggleCell`**, **`AttachmentCountCell`**, **`ExternalLinkCell`**, **`RelativeTimeCell`**, **`PeopleAvatarRailCell`**, **`PillCell`**, **`TagListCell`**, and a generic **`RowActionsCell<TRow>`**. A `ColumnDef['cell']` for these patterns is a **one-liner** that calls the named cell. **MUST NOT** inline `Intl.NumberFormat`, raw `<a target="_blank">`, `[1,2,3,4,5].map(s => …)` star loops, paperclip + count chips, custom face-rail `AvatarGroup`s, or per-hub `DropdownMenu` overflow menus inside `cell:` — those are signals you're re-deriving a shipped primitive. Catalog: `apps/web/components/columns-showcase.tsx` (`/columns`). Skill: `.cursor/skills/exxat-token-economy/SKILL.md` §3.
|
|
20
|
+
9. **Add view parity** — **`FULL_HUB_SUPPORTED_VIEWS`** + a real renderer per view — **`.cursor/rules/exxat-hub-supported-views.mdc`**, **`docs/exxat-ds/patterns/hub-supported-views-pattern.md`**.
|
|
20
21
|
|
|
21
22
|
**Reference implementations:**
|
|
22
23
|
|
|
23
|
-
- `components/
|
|
24
|
-
- `components/
|
|
25
|
-
- `components/
|
|
26
|
-
- `components/columns-showcase.tsx` — minimal hub (single `table` view); good reference for showcase pages.
|
|
27
|
-
- `components/tokens-themes-client.tsx` — minimal hub + secondary panel scope.
|
|
24
|
+
- `components/library-table.tsx` + `components/library-client.tsx` — **canonical** seven-view hub (All questions).
|
|
25
|
+
- `components/columns-showcase.tsx` — cell-pattern catalog via **`LibraryTable`** (`columnDefs` + folder state); same Add view as Library.
|
|
26
|
+
- `components/tokens-themes-client.tsx` + `components/tokens-hub-auxiliary-views.tsx` — tokens hub with **`FULL_HUB_SUPPORTED_VIEWS`**.
|
|
28
27
|
|
|
29
28
|
## Do not
|
|
30
29
|
|
|
@@ -32,12 +31,15 @@ For **any app screen that shows a browsable, filterable grid of records** (lists
|
|
|
32
31
|
- Do **not** mount raw `<DataTable>` inside `ListPageTemplate.renderContent` — use `HubTable`. Raw `<DataTable>` does not ship the Properties drawer or filter chips; users lose discoverability.
|
|
33
32
|
- Do **not** introduce a second “table component” pattern for the same product surfaces (splitting search/filters/properties across incompatible implementations).
|
|
34
33
|
- Do **not** inline-implement progress bars, currency formatters, rating stars, relative-time helpers, attachment chips, external-link wrappers, face rails, type pills, tag lists, or row-action dropdowns inside a `ColumnDef['cell']`. Import the named cell from `@/components/data-views` instead. New hub with novel cell needs MUST extend `table-cells.tsx` (and ship the catalog entry in `columns-showcase.tsx`), not fork inline JSX.
|
|
34
|
+
- Do **not** trim **`supportedViewTypes`** to table-only or **`PRIMARY_HUB_SUPPORTED_VIEWS`** on hubs that should match Library — see **`exxat-hub-supported-views.mdc`**.
|
|
35
35
|
|
|
36
36
|
## Exceptions
|
|
37
37
|
|
|
38
38
|
- **Tiny, read-only tables inside charts or analytics cards** (e.g. chart figure captions / summary matrices) may use minimal markup when they are not primary data-list experiences — still prefer tokens and accessibility, but the full hub stack is not required there.
|
|
39
39
|
- **Drawer body and dialog sub-grids** (small, secondary, scoped to a transient flow) — raw `<DataTable>` is acceptable; still expose search if rows > ~10.
|
|
40
|
+
- **Documented narrow allowlists** in `lib/*-supported-views.ts` when product truly omits folder/panel/tree (comment required).
|
|
40
41
|
|
|
41
42
|
## See also
|
|
42
43
|
|
|
43
44
|
- **`apps/web/AGENTS.md`** — full MUST/MUST NOT, list-page template, primary hubs, checklist.
|
|
45
|
+
- **`.cursor/rules/exxat-hub-supported-views.mdc`** — Add view menu + renderer matrix.
|
|
@@ -7,16 +7,16 @@ alwaysApply: true
|
|
|
7
7
|
|
|
8
8
|
## MUST
|
|
9
9
|
|
|
10
|
-
1. **
|
|
10
|
+
1. **Sheet panel** — Hub-**adjacent** work where **parent context** (list, filters, selection) stays relevant: properties, export, invites, long option lists beside the grid. Use **`Sheet`** (`showOverlay={false}`, floating inset) — **MUST NOT** add a Vaul `Drawer` primitive.
|
|
11
11
|
2. **Dialog** — **Blocking**, **short** focus: destructive confirm, legal/acknowledgment, single-step choice, alert when the user must not interact with the page behind until resolved.
|
|
12
12
|
3. **Route** — **Primary**, **long**, or **bookmarkable** flows — **`AGENTS.md` §6.4**, **`.cursor/rules/exxat-page-vs-drawer.mdc`**.
|
|
13
13
|
|
|
14
14
|
## MUST NOT
|
|
15
15
|
|
|
16
|
-
- Put **irreversible delete** only in a dismissible toast — use **dialog** (or
|
|
17
|
-
- Use a **centered dialog** for **wide tables of export columns** when a **
|
|
16
|
+
- Put **irreversible delete** only in a dismissible toast — use **dialog** (or sheet with explicit confirm) per **`exxat-no-toast.mdc`**.
|
|
17
|
+
- Use a **centered dialog** for **wide tables of export columns** when a **sheet panel** matches mental model and space.
|
|
18
18
|
|
|
19
19
|
## See also
|
|
20
20
|
|
|
21
21
|
- **`docs/drawer-vs-dialog-pattern.md`** · **`.cursor/skills/exxat-drawer-vs-dialog/SKILL.md`**
|
|
22
|
-
- **`exxat-page-vs-drawer.mdc`** (
|
|
22
|
+
- **`exxat-page-vs-drawer.mdc`** (sheet vs **route**)
|
|
@@ -40,6 +40,11 @@ Before implementing or reviewing **list / table / board / dashboard / data-heavy
|
|
|
40
40
|
25. **No SLDS leakage** — **`.cursor/rules/exxat-no-slds-leakage.mdc`** — no `slds-*` classes, no `<lightning-*>` markup, no SLDS token names, no synthetic-shadow assumptions. ESLint catches via `exxat-ds/no-slds-classes` + `exxat-ds/no-lightning-elements` (from **`@exxatdesignux/eslint-plugin`**).
|
|
41
41
|
26. **Blueprints + selection guide** — **`apps/web/docs/blueprints/`** + **`apps/web/docs/component-selection-guide.md`** — framework-agnostic specs + decision tree across the whole DS. Start there before composing a new hub.
|
|
42
42
|
27. **Migrations** — **`apps/web/docs/migrations/`** — every deprecation gets a numbered entry (`NNNN-<slug>.md`). Token deprecations surface in `tokens/hooks-index.json` as `deprecated: true` and are lint-flagged.
|
|
43
|
+
28. **Hub supported views (Add view parity)** — **`exxat-hub-supported-views.mdc`** + **`docs/exxat-ds/patterns/hub-supported-views-pattern.md`** — **`FULL_HUB_SUPPORTED_VIEWS`** (seven views, same as Library); sync **`ListPageTemplate`** + **`HubTable`**; real renderers per view; **`LibraryTable`** for **`LibraryItem`** catalogs; **`tokens-hub-auxiliary-views.tsx`** for tokens.
|
|
44
|
+
29. **No Vaul** — **`exxat-no-vaul.mdc`** — side panels = **`Sheet`** only; remove **`vaul`** from consumer apps on **`@exxatdesignux/ui@0.5.3+`**.
|
|
45
|
+
30. **Tabs chrome** — **`exxat-tabs-chrome.mdc`** — hub views = **`ListPageTemplate`** toolbar (`w-max`); record tabs = **`TabsList`** `w-fit`, never full-width stretch.
|
|
46
|
+
31. **Page header actions** — **`exxat-page-header-actions.mdc`** — filled primary + outline overflow; no hand-built grey buttons.
|
|
47
|
+
32. **Table row preview** — **`exxat-table-row-preview.mdc`** — **`HoverCard`** + DS badges/cells; no bespoke popover cards.
|
|
43
48
|
|
|
44
49
|
## Also read
|
|
45
50
|
|
|
@@ -50,7 +55,7 @@ Before implementing or reviewing **list / table / board / dashboard / data-heavy
|
|
|
50
55
|
- **`apps/web/docs/blueprints/`** — framework-agnostic specs (start: `page-header.md`, `data-table.md`).
|
|
51
56
|
- **`apps/web/docs/migrations/`** — token rename + removal history.
|
|
52
57
|
- **`packages/ui/tokens/hooks-index.json`** — machine-readable token index (regenerate via `pnpm --filter @exxatdesignux/ui tokens:index`).
|
|
53
|
-
- `.cursor/rules/exxat-data-tables.mdc`, `exxat-list-page-connected-views.mdc`, **`exxat-centralized-list-dataset.mdc`**, **`exxat-list-page-view-shells.mdc`**, **`exxat-fontawesome-icons.mdc`**, **`exxat-mono-ids.mdc`**, **`exxat-primary-nav-secondary-panel.mdc`**, **`exxat-library-hub-header.mdc`**, **`exxat-collaboration-access.mdc`**, **`exxat-dedicated-search-surfaces.mdc`**, **`exxat-kpi-trends.mdc`**, **`exxat-kpi-flat-band.mdc`**, **`exxat-drawer-vs-dialog.mdc`**, **`exxat-card-vs-list-rows.mdc`**, **`exxat-kpi-max-four.mdc`**, **`exxat-reuse-before-custom.mdc`**, `exxat-table-properties-drawer.mdc`, **`exxat-board-cards.mdc`**, **`exxat-page-vs-drawer.mdc`**, **`exxat-no-toast.mdc`**, **`exxat-command-menu.mdc`**, `exxat-kbd-shortcuts.mdc`, `exxat-accessibility.mdc` at repo root; **`apps/web/.cursor/rules/exxat-dashboard-view-charts.mdc`** for Data tab charts.
|
|
58
|
+
- `.cursor/rules/exxat-data-tables.mdc`, **`exxat-hub-supported-views.mdc`**, **`exxat-no-vaul.mdc`**, **`exxat-tabs-chrome.mdc`**, **`exxat-page-header-actions.mdc`**, **`exxat-table-row-preview.mdc`**, `exxat-list-page-connected-views.mdc`, **`exxat-centralized-list-dataset.mdc`**, **`exxat-list-page-view-shells.mdc`**, **`exxat-fontawesome-icons.mdc`**, **`exxat-mono-ids.mdc`**, **`exxat-primary-nav-secondary-panel.mdc`**, **`exxat-library-hub-header.mdc`**, **`exxat-collaboration-access.mdc`**, **`exxat-dedicated-search-surfaces.mdc`**, **`exxat-kpi-trends.mdc`**, **`exxat-kpi-flat-band.mdc`**, **`exxat-drawer-vs-dialog.mdc`**, **`exxat-card-vs-list-rows.mdc`**, **`exxat-kpi-max-four.mdc`**, **`exxat-reuse-before-custom.mdc`**, `exxat-table-properties-drawer.mdc`, **`exxat-board-cards.mdc`**, **`exxat-page-vs-drawer.mdc`**, **`exxat-no-toast.mdc`**, **`exxat-command-menu.mdc`**, `exxat-kbd-shortcuts.mdc`, `exxat-accessibility.mdc` at repo root; **`apps/web/.cursor/rules/exxat-dashboard-view-charts.mdc`** for Data tab charts.
|
|
54
59
|
- **`apps/web/docs/kpi-flat-band-pattern.md`**, **`apps/web/docs/shell-surface-elevation-pattern.md`** — flat KPI strip + sidebar/secondary/page OKLCH stack.
|
|
55
60
|
- **`apps/web/docs/library-hub-header-pattern.md`** — folder-scoped library header **Customize folder**.
|
|
56
61
|
- **`apps/web/docs/collaboration-access-pattern.md`** — shared hub face rail + invite sheet.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: List-page hubs — FULL_HUB_SUPPORTED_VIEWS, Add view parity, and real renderers (never trimmed allowlists or placeholder list rows)
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Exxat DS — hub supported views (Add view parity)
|
|
7
|
+
|
|
8
|
+
**Canonical reference:** `components/library-table.tsx` + `components/library-client.tsx` (All questions / Library).
|
|
9
|
+
|
|
10
|
+
**Registry:** `FULL_HUB_SUPPORTED_VIEWS` in `@exxatdesignux/ui/lib/data-list-view-registry` (re-export `@/lib/data-list-view-registry`).
|
|
11
|
+
|
|
12
|
+
## Default allowlist (seven views)
|
|
13
|
+
|
|
14
|
+
Every **`ListPageTemplate`** + **`HubTable`** hub that mounts a product data grid **MUST** expose the same **Add view** menu as Library unless product documents a narrower exception in the hub’s `*-supported-views.ts` file:
|
|
15
|
+
|
|
16
|
+
| # | View | Render kind | How to implement |
|
|
17
|
+
|---|------|-------------|------------------|
|
|
18
|
+
| 1 | Table | `data-table` | Default `HubTable` / custom `columnDefs` |
|
|
19
|
+
| 2 | List | `list-with-toolbar` | **`ListPageBoardCard`** `layout="row"` via `renderListRow` — same as `library-table.tsx` |
|
|
20
|
+
| 3 | Board | `board-with-toolbar` | `renderers["board-with-toolbar"]` or `renderBoardCard` + `boardGroups` |
|
|
21
|
+
| 4 | Dashboard | `dashboard-with-toolbar` | `renderers["dashboard-with-toolbar"]` with **`KeyMetrics`** / charts on `state.rows` |
|
|
22
|
+
| 5 | Folder | `folder-with-toolbar` | `renderers["folder-with-toolbar"]` (e.g. `LibraryOsFolderView`, `FolderGridView`) |
|
|
23
|
+
| 6 | List & details | `panel-with-toolbar` | `renderers["panel-with-toolbar"]` (Miller columns — see `library-table.tsx`) |
|
|
24
|
+
| 7 | Tree & details | `tree-panel-with-toolbar` | `renderers["tree-panel-with-toolbar"]` (e.g. `HubTreePanelView`) |
|
|
25
|
+
|
|
26
|
+
`HubTable` and `ListPageTemplate` default to **`FULL_HUB_SUPPORTED_VIEWS`** when `supportedViewTypes` is omitted.
|
|
27
|
+
|
|
28
|
+
**`PRIMARY_HUB_SUPPORTED_VIEWS`** (table, list, board, dashboard only) is for hubs that **document** they intentionally omit folder/panel/tree — not the default for new work.
|
|
29
|
+
|
|
30
|
+
## MUST (every new or touched hub)
|
|
31
|
+
|
|
32
|
+
1. **Sync allowlists** — Pass the **same** `supportedViewTypes` to **`ListPageTemplate`** and **`HubTable`** (or only omit on both so the default applies).
|
|
33
|
+
2. **Implement every allowed view** — Each entry in `supportedViewTypes` needs a renderer (explicit `renderers` map and/or `renderListRow` / `renderBoardCard` + `boardGroups`). Dev warns when a view is allowed but missing; users see **“does not implement X view”** if you ship the allowlist without bodies.
|
|
34
|
+
3. **List rows use DS chrome** — **`ListPageBoardCard`** (Library list pattern). **MUST NOT** ship a bare two-line `renderListRow` (title + mono id only).
|
|
35
|
+
4. **Library-shaped rows** — Hubs on **`LibraryItem`** with a custom table (e.g. Column types) **MUST** delegate non-table views to **`LibraryTable`** (`columnDefs` + `hubLabels` + `folders` state) — do not reimplement folder/panel/tree in isolation.
|
|
36
|
+
5. **Token hubs** — Use **`FULL_HUB_SUPPORTED_VIEWS`** + **`components/tokens-hub-auxiliary-views.tsx`** (`renderTokenListRow`, `buildTokensHubRenderers`) — **MUST NOT** use `supportedViewTypes={["table"]}`.
|
|
37
|
+
|
|
38
|
+
## MUST NOT
|
|
39
|
+
|
|
40
|
+
- Pass **`PRIMARY_HUB_SUPPORTED_VIEWS`** or `["table", "list", "board", "dashboard"]` on a hub that should match Library Add view parity.
|
|
41
|
+
- Pass **`supportedViewTypes={["table"]}`** on any primary hub or showcase inside **`ListPageTemplate`**.
|
|
42
|
+
- Copy **`COLUMNS_SUPPORTED_VIEWS = PRIMARY_…`** without implementing folder/panel/tree.
|
|
43
|
+
- Invent a new per-page allowlist constant without a matching renderer for every listed view.
|
|
44
|
+
|
|
45
|
+
## Narrow exceptions (document in code comment)
|
|
46
|
+
|
|
47
|
+
- Hubs that genuinely lack calendar/folder/panel/tree (e.g. legacy Placements-only four views) — export `ENTITY_SUPPORTED_VIEWS` in `lib/*-supported-views.ts` and **comment why** it is narrower than `FULL_HUB_SUPPORTED_VIEWS`.
|
|
48
|
+
- Calendar is **not** in `FULL_HUB_SUPPORTED_VIEWS`; add only when the hub implements `calendar-with-toolbar`.
|
|
49
|
+
|
|
50
|
+
## See also
|
|
51
|
+
|
|
52
|
+
- **`.cursor/rules/exxat-data-tables.mdc`**, **`exxat-list-page-connected-views.mdc`**, **`exxat-list-page-view-shells.mdc`**
|
|
53
|
+
- **`docs/exxat-ds/patterns/hub-supported-views-pattern.md`** (vendored from monorepo `apps/web/docs/`)
|
|
54
|
+
- **`docs/exxat-ds/patterns/data-views-pattern.md`**
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Sidebar and secondary nav — exactly one active item per route (longest path match)
|
|
3
|
+
globs: "**/app-sidebar.tsx,**/secondary-nav.tsx,**/navigation.tsx,**/lib/nav-active.ts"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Exxat DS — single active nav item (MUST)
|
|
8
|
+
|
|
9
|
+
Only **one** primary sidebar row, collapsible child, or secondary-panel link may show the active/selected state for the current route.
|
|
10
|
+
|
|
11
|
+
## MUST
|
|
12
|
+
|
|
13
|
+
1. **Use shared helpers** from `@exxatdesignux/ui/lib/nav-active` (or the app copy synced from the DS template):
|
|
14
|
+
- `collectNavUrls` + `buildNavHashClaims` for the full nav tree
|
|
15
|
+
- `isNavHrefActive(pathname, url, allNavUrls, { locationHash, hashClaimsByPath })` for sidebar `SidebarMenuButton` / `isActive`
|
|
16
|
+
- `resolveActiveNavHref(pathname, allLinkHrefs)` for secondary-panel links — compare the returned href to each link’s `href` (longest prefix wins)
|
|
17
|
+
2. **Never** mark a nav item active with bare `pathname === href` or `pathname.startsWith(href + "/")` when other nav targets share that prefix (e.g. `/dashboard` vs `/dashboard/students`).
|
|
18
|
+
3. **Hash disambiguation** — when multiple items share the same path (e.g. `/settings` vs `/settings#appearance`), use `buildNavHashClaims` so the no-fragment row defers to the hash-specific row.
|
|
19
|
+
4. **Collapsible parents** — when any child is active, the **parent** row stays visually neutral in the expanded sidebar; only the child gets `data-active` (icon rail may still highlight the parent — see `isCollapsibleParentMenuButtonActive` in `app-sidebar.tsx`).
|
|
20
|
+
5. **Keep `ListPageTemplate.supportedViewTypes` and `HubTable.supportedViewTypes` in sync** on the same hub — do not pass `supportedViewTypes={["table"]}` on a primary hub unless that hub truly implements only table.
|
|
21
|
+
|
|
22
|
+
## MUST NOT
|
|
23
|
+
|
|
24
|
+
- Duplicate bespoke `isNavActive` / `isActive` logic that ignores longest-prefix matching.
|
|
25
|
+
- Ship a new nav surface (sidebar, secondary rail, hub scope list) without wiring the helpers above.
|
|
26
|
+
|
|
27
|
+
## Reference
|
|
28
|
+
|
|
29
|
+
- `packages/ui/src/lib/nav-active.ts`
|
|
30
|
+
- `packages/ui/template/components/sidebar/app-sidebar.tsx`
|
|
31
|
+
- `packages/ui/template/components/sidebar/secondary-nav.tsx`
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — no Vaul Drawer primitive; side panels use Sheet only
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Exxat DS — no Vaul
|
|
7
|
+
|
|
8
|
+
## MUST
|
|
9
|
+
|
|
10
|
+
- Side panels (export, properties, invite, folder create) use **`Sheet`** from `@exxatdesignux/ui/components/sheet` with the floating inset pattern (`showOverlay={false}`, `getFloatingSheetInsetProps`).
|
|
11
|
+
- Product code names may end in `*Drawer` (`ExportDrawer`, `TablePropertiesDrawer`) — implementation is **`Sheet`**, not Vaul.
|
|
12
|
+
|
|
13
|
+
## MUST NOT
|
|
14
|
+
|
|
15
|
+
- Add **`vaul`** to `package.json` or import `@/components/ui/drawer` / Vaul `Drawer`.
|
|
16
|
+
- Scaffold or extend the removed shadcn Vaul wrapper — it was deleted from `@exxatdesignux/ui@0.5.3+`.
|
|
17
|
+
|
|
18
|
+
## Consumer apps
|
|
19
|
+
|
|
20
|
+
After upgrading to **`@exxatdesignux/ui@0.5.3`**, remove **`vaul`** from the app `package.json`, delete any local `components/ui/drawer.tsx` re-export, and re-run **`exxat-ui sync-extras`**.
|
|
21
|
+
|
|
22
|
+
## See also
|
|
23
|
+
|
|
24
|
+
- **`.cursor/rules/exxat-drawer-vs-dialog.mdc`**
|
|
25
|
+
- **`docs/exxat-ds/patterns/drawer-vs-dialog-pattern.md`**
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — PageHeader actions slot; View as / overflow / primary CTA patterns
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Exxat DS — page header actions
|
|
7
|
+
|
|
8
|
+
**Blueprint:** `docs/exxat-ds/handbook/HANDBOOK.md` + `node_modules/@exxatdesignux/ui/template/docs/blueprints/page-header.md`
|
|
9
|
+
|
|
10
|
+
## Anatomy (actions slot only)
|
|
11
|
+
|
|
12
|
+
| Control | DS pattern |
|
|
13
|
+
|---------|------------|
|
|
14
|
+
| **Primary CTA** | One **`Button`** `variant="default"` `size="lg"` (filled) — e.g. “Add student”, “Ask Leo” when it is the main action |
|
|
15
|
+
| **Secondary / scope** (“View as”, “All students”) | **`Button variant="outline" size="lg"`** + chevron, or **`Select`** / **`DropdownMenu`** trigger styled as outline button — **not** plain grey rectangles or unstyled `<button>` |
|
|
16
|
+
| **Overflow (⋯)** | **`Button variant="outline" size="icon-lg"`** (or `size="lg"` icon-only) opening **`DropdownMenu`** — Export, settings, invite |
|
|
17
|
+
| **Collaboration** | **`PageHeader variant="collaboration"`** face rail — not a custom avatar row |
|
|
18
|
+
|
|
19
|
+
## MUST
|
|
20
|
+
|
|
21
|
+
- Compose **`PageHeader`** + pass **`actions`** as DS **`Button`** / **`DropdownMenu`** primitives.
|
|
22
|
+
- Keep **one** filled primary; put Export and long-tail items under **⋯**.
|
|
23
|
+
|
|
24
|
+
## MUST NOT
|
|
25
|
+
|
|
26
|
+
- Hand-built header button rows that skip **`PageHeader`** and **`Button`** variants.
|
|
27
|
+
- Multiple filled primaries beside each other (e.g. “Ask Leo” + “Add student” both solid black unless product explicitly documents hierarchy — prefer outline for secondary).
|
|
28
|
+
|
|
29
|
+
## Reference
|
|
30
|
+
|
|
31
|
+
- **`docs/exxat-ds/handbook/reference-implementations.md`**
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — table row identity preview uses HoverCard + DS tokens, not bespoke Popover cards
|
|
3
|
+
alwaysApply: false
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Exxat DS — table row preview (hover / click)
|
|
7
|
+
|
|
8
|
+
When a **data table** cell opens a **rich preview** of a person or record (student, placement, team member):
|
|
9
|
+
|
|
10
|
+
## MUST
|
|
11
|
+
|
|
12
|
+
- Use **`HoverCard`** (`@/components/ui/hover-card`) for hover-driven previews, or **`Popover`** only when the preview is **click-pinned** and small.
|
|
13
|
+
- Structure identity with **`AvatarInitials`**, name, **`font-mono tabular-nums`** on IDs only (**`exxat-mono-ids.mdc`**), **`ListHubStatusBadge`** + **`lib/list-status-badges.ts`** for status — **color + icon** (**`exxat-accessibility.mdc`** 1.4.1).
|
|
14
|
+
- Reuse table cell patterns from **`@/components/data-views`** (`PeopleAvatarRailCell`, `PillCell`, progress cells) — see **`columns-showcase.tsx`**.
|
|
15
|
+
|
|
16
|
+
## MUST NOT
|
|
17
|
+
|
|
18
|
+
- Bespoke **`PopoverContent`** layouts with ad-hoc spacing, one-off progress bars, or status chips without the shared badge map.
|
|
19
|
+
- Raw email as the only identifier when a display name exists (**`exxat-person-identity-display.mdc`**).
|
|
20
|
+
|
|
21
|
+
## Reference
|
|
22
|
+
|
|
23
|
+
- **`HoverCard`** docs in `packages/ui/src/components/ui/hover-card.tsx`
|
|
24
|
+
- **`apps/web/docs/blueprints/data-table.md`**
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exxat DS — Tabs and view toolbars must not stretch full width; use ListPageTemplate for hub views
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Exxat DS — tabs chrome (no full-width stretch)
|
|
7
|
+
|
|
8
|
+
## List hubs (All students, Placements, …)
|
|
9
|
+
|
|
10
|
+
**MUST** use **`ListPageTemplate`** view toolbar — `role="toolbar"` + **`w-max`** segmented control (`viewSegmentedToolbarClass`), **not** Radix **`Tabs`** stretched across the page.
|
|
11
|
+
|
|
12
|
+
- Reference: `packages/ui/src/components/templates/list-page.tsx` (views toolbar block).
|
|
13
|
+
- **MUST NOT** build a custom full-width tab bar for “All students · 16” + Add view.
|
|
14
|
+
|
|
15
|
+
## Entity / record detail (Overview, Academics, Placements, …)
|
|
16
|
+
|
|
17
|
+
**MUST** use **`Tabs`**, **`TabsList`**, **`TabsTrigger`** from `@/components/ui/tabs`:
|
|
18
|
+
|
|
19
|
+
- **`TabsList`** defaults to **`inline-flex w-fit`** — **MUST NOT** pass `className="w-full"` or wrap the list in a full-width flex container that forces triggers to **`flex-1`**.
|
|
20
|
+
- Prefer **`variant="line"`** for record sub-nav under a **`PageHeader`** (`record-home` blueprint).
|
|
21
|
+
- **`TabsTrigger`** may use `flex-1` only inside a **`w-fit`** list with a fixed number of tabs — never inside a bar that spans the content column.
|
|
22
|
+
|
|
23
|
+
## MUST NOT
|
|
24
|
+
|
|
25
|
+
- A single grey/white bar spanning the entire content width with tabs distributed edge-to-edge.
|
|
26
|
+
- Mix hub **view tabs** (table/list/board) with entity **section tabs** (Overview/Academics) — different patterns.
|
|
27
|
+
|
|
28
|
+
## See also
|
|
29
|
+
|
|
30
|
+
- **`docs/exxat-ds/handbook/HANDBOOK.md`** — header never carries view tabs
|
|
31
|
+
- **`docs/exxat-ds/patterns/data-views-pattern.md`** — `ListPageTemplate` view toolbar
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Exxat DS — drawer vs dialog
|
|
2
2
|
|
|
3
|
-
Use when choosing **Radix `Dialog` / `AlertDialog`** vs
|
|
3
|
+
Use when choosing **Radix `Dialog` / `AlertDialog`** vs **floating `Sheet` panels** vs **route** for a flow.
|
|
4
4
|
|
|
5
5
|
## Read first
|
|
6
6
|
|
|
@@ -10,11 +10,11 @@ Use when choosing **Radix `Dialog` / `AlertDialog`** vs **`Sheet` / `Drawer`** v
|
|
|
10
10
|
|
|
11
11
|
## Checklist
|
|
12
12
|
|
|
13
|
-
1. **Must the user see the hub while acting?** Yes →
|
|
14
|
-
2. **Destructive confirm?** Prefer **dialog** (`AlertDialog`) unless the product explicitly keeps context in a
|
|
15
|
-
3. **Title + focus** — Dialog
|
|
13
|
+
1. **Must the user see the hub while acting?** Yes → **`Sheet`** panel (properties, export, invite). No and short → **dialog**. Long / own URL → **route**.
|
|
14
|
+
2. **Destructive confirm?** Prefer **dialog** (`AlertDialog`) unless the product explicitly keeps context in a sheet with the same safeguards.
|
|
15
|
+
3. **Title + focus** — Dialog and sheet both need an accessible **title**; restore focus to trigger on close.
|
|
16
16
|
|
|
17
17
|
## Repo references
|
|
18
18
|
|
|
19
|
-
- `TablePropertiesDrawer`, `ExportDrawer`, `InviteCollaboratorsDrawer` —
|
|
19
|
+
- `TablePropertiesDrawer`, `ExportDrawer`, `InviteCollaboratorsDrawer` — floating **`Sheet`** (names end in “Drawer” but implementation is `Sheet` only).
|
|
20
20
|
- Delete / irreversible — dialog pattern, not toast (**`exxat-no-toast.mdc`**).
|
|
@@ -251,15 +251,20 @@ For "System" variants, use the **`SplitSystemSvg`** helper. It renders `ChromeIl
|
|
|
251
251
|
Any **primary nav destination** that shows a list of records **must** use this composition (same as Placements / Team):
|
|
252
252
|
|
|
253
253
|
```
|
|
254
|
-
ListPageTemplate
|
|
254
|
+
ListPageTemplate (supportedViewTypes = FULL_HUB_SUPPORTED_VIEWS — seven views)
|
|
255
255
|
├── PageHeader (title, subtitle with count, primary CTA, ⋯ more menu)
|
|
256
256
|
├── KeyMetrics (flat variant, single row)
|
|
257
257
|
└── renderContent()
|
|
258
|
-
└──
|
|
258
|
+
└── HubTable + useTableState + TablePropertiesDrawer + renderers per view
|
|
259
259
|
```
|
|
260
260
|
|
|
261
|
+
**Add view parity (binding):** `.cursor/rules/exxat-hub-supported-views.mdc`, `apps/web/docs/hub-supported-views-pattern.md`. **MUST NOT** use `supportedViewTypes={["table"]}` or four-view-only allowlists without a documented exception. List view **MUST** use **`ListPageBoardCard`** (`library-table.tsx`).
|
|
262
|
+
|
|
261
263
|
**Reference implementations:**
|
|
262
|
-
- `components/
|
|
264
|
+
- `components/library-client.tsx` + `components/library-table.tsx` — **canonical seven-view hub** (All questions)
|
|
265
|
+
- `components/columns-showcase.tsx` — custom table via **`LibraryTable`** + same seven views
|
|
266
|
+
- `components/tokens-themes-client.tsx` + `components/tokens-hub-auxiliary-views.tsx`
|
|
267
|
+
- `components/team-client.tsx` + `components/team-table.tsx` — entity hub pattern
|
|
263
268
|
- `components/placements-client.tsx` + `components/placements-table.tsx` — Placements (most complete)
|
|
264
269
|
|
|
265
270
|
**Files to create for a new hub page `Foo`:**
|
|
@@ -617,7 +622,7 @@ Full checklist in `references/accessibility.md`. Summary of the most-violated ru
|
|
|
617
622
|
### Structure
|
|
618
623
|
- One `<main id="main-content" tabIndex={-1}>` per page
|
|
619
624
|
- One `<h1>` per page (via `PageHeader`) — `SiteHeader` title is NOT an h1
|
|
620
|
-
- `DialogTitle` / `SheetTitle`
|
|
625
|
+
- `DialogTitle` / `SheetTitle` always present (use `className="sr-only"` if visually hidden)
|
|
621
626
|
|
|
622
627
|
### ARIA roles
|
|
623
628
|
- `role="tablist"` → only `role="tab"` children. **Never** put buttons, menus, or other controls inside `tablist`
|
|
@@ -874,7 +879,7 @@ Copy and complete for every list/table/hub page:
|
|
|
874
879
|
- [ ] All icons: `aria-hidden="true"`; Ask Leo: `fa-duotone fa-solid fa-star-christmas text-brand`
|
|
875
880
|
- [ ] **Every icon that communicates info has a text alternative** — Case A adjacent label (preferred), Case B `role="img"` + `aria-label` + `Tooltip` (calendar-for-date, status dot, trend arrow, icon-only legend), Case C `aria-label` + `Tooltip` on icon-only buttons; target ≥ 24×24 px. See §12 *Icons that communicate information*.
|
|
876
881
|
- [ ] **`Kbd` inside a `Button` uses `variant="bare"`** (glue chords into one bare kbd); **`Kbd` inside `TooltipContent` uses the default tile** — see §11 Keyboard shortcuts
|
|
877
|
-
- [ ] `DialogTitle`/`SheetTitle
|
|
882
|
+
- [ ] `DialogTitle`/`SheetTitle` present on every overlay
|
|
878
883
|
- [ ] `role="tablist"` contains only tab-role children
|
|
879
884
|
- [ ] No new shadcn components, no hardcoded colors, no duplicate component abstractions
|
|
880
885
|
|
|
@@ -42,7 +42,7 @@ This is the single source of truth for all accessibility requirements in Exxat D
|
|
|
42
42
|
- Heading hierarchy: one `<h1>` (via `PageHeader`), logical `<h2>` → `<h3>` (no skipping)
|
|
43
43
|
- `SiteHeader` title in the breadcrumb bar is NOT an `<h1>`
|
|
44
44
|
- Multiple `<nav>` elements: each must have `aria-label`
|
|
45
|
-
- Modal/Sheet/Dialog: `DialogTitle`/`SheetTitle
|
|
45
|
+
- Modal/Sheet/Dialog: `DialogTitle`/`SheetTitle` ALWAYS present — use `sr-only` if visually hidden
|
|
46
46
|
- `<aside>` panels: `aria-label` (e.g. "Ask Leo assistant", "Rotation navigation")
|
|
47
47
|
- Data tables: `<table>` + `<thead>` + `<th scope="col">`; sortable columns: `aria-sort` on `<th>`
|
|
48
48
|
- Listbox: `role="listbox"` + `role="option"` + `aria-selected`
|
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
# Data Table Pattern — Full Implementation Guide
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Primary hub stack:** `ListPageTemplate` → **`HubTable`** (not raw `DataTable` inside hubs).
|
|
4
|
+
|
|
5
|
+
**Add view parity:** `FULL_HUB_SUPPORTED_VIEWS` on **`ListPageTemplate`** + **`HubTable`**; implement every allowed view — **`.cursor/rules/exxat-hub-supported-views.mdc`**, **`apps/web/docs/hub-supported-views-pattern.md`**.
|
|
6
|
+
|
|
7
|
+
**Reference implementations:**
|
|
8
|
+
- `components/library-table.tsx` + `library-client.tsx` — canonical seven-view hub
|
|
9
|
+
- `components/columns-showcase.tsx` — `LibraryTable` + custom `columnDefs`
|
|
10
|
+
- `components/tokens-themes-client.tsx` + `tokens-hub-auxiliary-views.tsx`
|
|
11
|
+
- `components/team-table.tsx`, `components/placements-table.tsx` — entity hubs
|
|
4
12
|
|
|
5
13
|
---
|
|
6
14
|
|
|
7
15
|
## Stack Summary
|
|
8
16
|
|
|
9
17
|
```
|
|
10
|
-
|
|
11
|
-
└──
|
|
12
|
-
└──
|
|
13
|
-
|
|
18
|
+
HubTable (inside ListPageTemplate) ← canonical hub wrapper
|
|
19
|
+
└── DataTable ← base table component
|
|
20
|
+
└── useTableState ← sort/filter/column/group state
|
|
21
|
+
└── toolbarSlot ← properties button + filter chips + search
|
|
22
|
+
└── TablePropertiesDrawer
|
|
23
|
+
└── renderListRow / renderers ← list, board, dashboard, folder, panel, tree
|
|
14
24
|
```
|
|
15
25
|
|
|
16
26
|
All imports:
|
|
@@ -38,7 +38,7 @@ Open **only** these files. Skip everything else unless one of these files cites
|
|
|
38
38
|
|
|
39
39
|
| Task | Read (minimum) | Skip (do not open) |
|
|
40
40
|
|------|----------------|--------------------|
|
|
41
|
-
| **Build a new hub page** (table + KPIs + view tabs) | `apps/web/components/
|
|
41
|
+
| **Build a new hub page** (table + KPIs + view tabs) | `apps/web/components/library-table.tsx` + `library-client.tsx`, `exxat-hub-supported-views.mdc` (or `hub-supported-views-pattern.md`) | `AGENTS.md`, `hub-table.tsx` source, all `*-pattern.md` |
|
|
42
42
|
| **Add a column / cell pattern** | `apps/web/components/columns-showcase.tsx` (the live catalog) | `data-table/index.tsx`, the whole `types.ts` |
|
|
43
43
|
| **Add a board / kanban view** | `apps/web/components/placements-board-card.tsx`, `exxat-board-cards.mdc` only | All other rules |
|
|
44
44
|
| **Add a KPI strip** | `docs/exxat-ds/handbook/reference-implementations.md` § KPI flat band, `exxat-kpi-max-four.mdc`, `exxat-kpi-trends.mdc` | `key-metrics.tsx` source |
|
|
@@ -63,8 +63,11 @@ Answer **yes / no / N/A** to each. A **no** means re-plan; you'll save a regener
|
|
|
63
63
|
3. **Color + icon on every status chip?** — `ListHubStatusBadge` + a tint from `lib/list-status-badges.ts` + an FA icon. Color alone fails WCAG 1.4.1.
|
|
64
64
|
4. **≤ 4 KPIs on the primary strip?** — `KEY_METRICS_KPI_COUNT_MAX = 4`. A fifth becomes a `MetricInsight` or a chart.
|
|
65
65
|
5. **No toasts for product feedback?** — use `LocalBanner` / `SystemBanner` / inline status. Toasts are reserved for build-tool messages.
|
|
66
|
+
6. **Seven views + real bodies?** — `FULL_HUB_SUPPORTED_VIEWS` on **`ListPageTemplate`** + **`HubTable`** (sync both); every allowed view has a renderer; list uses **`ListPageBoardCard`** — not `["table"]` / `PRIMARY_HUB_SUPPORTED_VIEWS` / empty `renderers={}`.
|
|
67
|
+
7. **Sheet only (no Vaul)?** — side panels use **`Sheet`**; **`vaul`** must not be in `package.json`.
|
|
68
|
+
8. **Header + tabs + table preview?** — **`PageHeader`** + DS **`Button`** variants for actions; hub views via **`ListPageTemplate`** (not full-width tabs); record tabs **`TabsList`** `w-fit`; row preview via **`HoverCard`** + shared cells — not custom popovers.
|
|
66
69
|
|
|
67
|
-
If all
|
|
70
|
+
If all eight are **yes**, generate. If any is **no**, either narrow the requirements
|
|
68
71
|
with **one** clarifying question or fix the gap silently and note it in your response.
|
|
69
72
|
|
|
70
73
|
---
|
|
@@ -83,7 +86,7 @@ When the user says "X", reach for "Y". Save the search.
|
|
|
83
86
|
| chip, badge, status, tag | `Badge`, `ListHubStatusBadge`, `StatusBadge` | `@/components/{ui/badge,list-hub-status-badge}` |
|
|
84
87
|
| dropdown, menu, ⋯ | `DropdownMenu` family | `@/components/ui/dropdown-menu` |
|
|
85
88
|
| tooltip, hint | `Tip` (or `Tooltip`) | `@/components/ui/tip` |
|
|
86
|
-
| sheet,
|
|
89
|
+
| sheet, side panel | `Sheet` family, `ExportDrawer`, `TablePropertiesDrawer` | `@/components/ui/sheet` |
|
|
87
90
|
| dialog, modal, confirm | `Dialog` family | `@/components/ui/dialog` |
|
|
88
91
|
| table, list, grid (product data) | `HubTable` inside `ListPageTemplate` | `@/components/data-views` |
|
|
89
92
|
| KPI, metric, stat | `KeyMetrics` + `MetricItem` | `@/components/key-metrics` |
|
|
@@ -141,8 +144,11 @@ import { PageHeader } from "@/components/page-header"
|
|
|
141
144
|
import { KeyMetrics, type MetricItem } from "@/components/key-metrics"
|
|
142
145
|
import { ListPageTemplate, type ViewTab, HubTable } from "@/components/data-views"
|
|
143
146
|
|
|
147
|
+
import { FULL_HUB_SUPPORTED_VIEWS } from "@/lib/data-list-view-registry"
|
|
148
|
+
|
|
144
149
|
const ENTITY_TABS: ViewTab[] = [{ id: "all", label: "All", viewType: "table", icon: "fa-table", filterId: "all" }]
|
|
145
|
-
|
|
150
|
+
/** Seven views (Library parity) unless product documents a narrower list in lib/entity-supported-views.ts */
|
|
151
|
+
const ENTITY_SUPPORTED_VIEWS = FULL_HUB_SUPPORTED_VIEWS
|
|
146
152
|
|
|
147
153
|
export function EntityClient({ rows }) {
|
|
148
154
|
const [tabs, setTabs] = React.useState<ViewTab[]>(ENTITY_TABS)
|
|
@@ -163,7 +169,10 @@ export function EntityClient({ rows }) {
|
|
|
163
169
|
hubLabel="Entity" lifecycleTabLabel="Entity"
|
|
164
170
|
getRowId={r => r.id} getRowSelectionLabel={r => r.name}
|
|
165
171
|
defaultSort={{ key: "name", dir: "asc" }}
|
|
166
|
-
|
|
172
|
+
renderListRow={row => /* ListPageBoardCard layout="row" — copy library-table.tsx */}
|
|
173
|
+
renderers={{
|
|
174
|
+
/* board-with-toolbar, dashboard-with-toolbar, folder/panel/tree — see library-table.tsx or tokens-hub-auxiliary-views.tsx */
|
|
175
|
+
}}
|
|
167
176
|
/>
|
|
168
177
|
)}
|
|
169
178
|
/>
|
|
@@ -33,7 +33,7 @@ This is the **happy path** for the most common task: "I have an entity (placemen
|
|
|
33
33
|
| 5 | Compose the page client with `PrimaryPageTemplate` → `ListPageTemplate` (KPIs in `metrics`, view tabs in `defaultTabs`, the `HubTable` in `renderContent`). | `apps/web/components/<entity>-client.tsx` | `exxat-list-page-connected-views.mdc` |
|
|
34
34
|
| 6 | Add to nav (`lib/mock/navigation.tsx`). If the hub needs scoped sub-navigation (e.g. categories), declare `secondaryPanel: "<id>"` and register the panel. | `apps/web/lib/mock/navigation.tsx`, `apps/web/components/sidebar/secondary-panel.tsx` | `exxat-primary-nav-secondary-panel.mdc` |
|
|
35
35
|
|
|
36
|
-
**Reference
|
|
36
|
+
**Reference pages to copy:** `apps/web/components/library-table.tsx` + `library-client.tsx` (seven-view hub), `columns-showcase.tsx` (`LibraryTable` + custom columns), `tokens-themes-client.tsx` + `tokens-hub-auxiliary-views.tsx`. See **`hub-supported-views-pattern.md`** (vendored under `docs/exxat-ds/patterns/`).
|
|
37
37
|
|
|
38
38
|
> **Stop signs.** If you find yourself building a parallel table stack, a second metrics strip, a custom filter row, or pasting raw `<DataTable>` into `renderContent` — **stop and re-read** `.cursor/rules/exxat-reuse-before-custom.mdc`.
|
|
39
39
|
|
|
@@ -27,9 +27,9 @@ If you find yourself diverging from the reference page, ask **why** before shipp
|
|
|
27
27
|
| Hub with finder / split-panel view | `apps/web/components/sites-table.tsx` + `sites-client.tsx` | [`list-page-template`](https://github.com/ExxatDesign/Exxat-DS-Workspace/blob/main/apps/web/docs/blueprints/list-page-template.md) | [`exxat-list-page-view-shells`](../../../.cursor/rules/exxat-list-page-view-shells.mdc) | [`data-views-pattern`](../data-views-pattern.md) |
|
|
28
28
|
| Hub with secondary panel scope (folder rail) | `apps/web/components/library-table.tsx` + `library-client.tsx` | [`list-page-template`](https://github.com/ExxatDesign/Exxat-DS-Workspace/blob/main/apps/web/docs/blueprints/list-page-template.md) | [`exxat-primary-nav-secondary-panel`](../../../.cursor/rules/exxat-primary-nav-secondary-panel.mdc), [`exxat-library-hub-header`](../../../.cursor/rules/exxat-library-hub-header.mdc) | [`library-hub-header-pattern`](https://github.com/ExxatDesign/Exxat-DS-Workspace/blob/main/apps/web/docs/library-hub-header-pattern.md) |
|
|
29
29
|
| Hub with secondary panel scope (token categories) — **smallest** secondary-panel reference | `apps/web/components/tokens-themes-client.tsx` + `tokens-secondary-nav.tsx` | [`list-page-template`](https://github.com/ExxatDesign/Exxat-DS-Workspace/blob/main/apps/web/docs/blueprints/list-page-template.md) | [`exxat-primary-nav-secondary-panel`](../../../.cursor/rules/exxat-primary-nav-secondary-panel.mdc) | [`shell-surface-elevation-pattern`](../shell-surface-elevation-pattern.md) |
|
|
30
|
-
|
|
|
30
|
+
| Cell-pattern catalog — **18 SaaS cell patterns** via custom `columnDefs`; **seven views** via **`LibraryTable`** (same Add view as Library). | `apps/web/components/columns-showcase.tsx` + `columns-client.tsx` | [`list-page-template`](https://github.com/ExxatDesign/Exxat-DS-Workspace/blob/main/apps/web/docs/blueprints/list-page-template.md), [`data-table`](https://github.com/ExxatDesign/Exxat-DS-Workspace/blob/main/apps/web/docs/blueprints/data-table.md), [`hub-supported-views-pattern`](https://github.com/ExxatDesign/Exxat-DS-Workspace/blob/main/apps/web/docs/hub-supported-views-pattern.md) | [`exxat-data-tables`](../../../.cursor/rules/exxat-data-tables.mdc), [`exxat-hub-supported-views`](../../../.cursor/rules/exxat-hub-supported-views.mdc) | — |
|
|
31
31
|
|
|
32
|
-
> **First-time hub builder:**
|
|
32
|
+
> **First-time hub builder:** copy **`library-table.tsx`** + **`library-client.tsx`**; use **`columns-showcase.tsx`** for custom columns; **`tokens-themes-client.tsx`** + **`tokens-hub-auxiliary-views.tsx`** for tokens. Read **`hub-supported-views-pattern.md`** before changing Add view.
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
|
@@ -29,8 +29,21 @@ Use it when you need to know **what files exist**, **how shims re-export** `@exx
|
|
|
29
29
|
|
|
30
30
|
- Keep **`@exxatdesignux/ui`** on the same semver your team tested; prefer explicit **`^x.y.z`** or pinned **`x.y.z`**.
|
|
31
31
|
- Match **`engines.node`** in your app to the value declared in **`node_modules/@exxatdesignux/ui/package.json`** (see CHANGELOG if it changed).
|
|
32
|
+
- **≥ 0.5.3:** Remove **`vaul`** from your app `package.json` and delete any `components/ui/drawer.tsx` shim — side panels use **`Sheet`** only (**`.cursor/rules/exxat-no-vaul.mdc`**).
|
|
32
33
|
|
|
33
|
-
## 5.
|
|
34
|
+
## 5. Consumer UI audit (after sync-extras)
|
|
35
|
+
|
|
36
|
+
If the app was built before current agent rules, verify:
|
|
37
|
+
|
|
38
|
+
| Symptom | Fix |
|
|
39
|
+
|---------|-----|
|
|
40
|
+
| Full-width tab bar on list hub | Use **`ListPageTemplate`** view toolbar — **`exxat-tabs-chrome.mdc`** |
|
|
41
|
+
| Full-width Overview / Academics tabs | **`TabsList`** must stay **`w-fit`** — no `w-full` / `flex-1` stretch |
|
|
42
|
+
| Grey custom header buttons | **`PageHeader`** + **`Button`** variants — **`exxat-page-header-actions.mdc`** |
|
|
43
|
+
| Bespoke student popover in table | **`HoverCard`** + shared cells/badges — **`exxat-table-row-preview.mdc`** |
|
|
44
|
+
| Custom hub table / trimmed Add view | **`HubTable`** + **`FULL_HUB_SUPPORTED_VIEWS`** — **`exxat-hub-supported-views.mdc`** |
|
|
45
|
+
|
|
46
|
+
## 6. Still stuck?
|
|
34
47
|
|
|
35
48
|
- **`npx --package=@exxatdesignux/ui@latest exxat-ui doctor`** — compares local CLI version vs npm **`latest`**.
|
|
36
49
|
- **`npx --package=@exxatdesignux/ui@latest exxat-ui update`** — install commands and reminders.
|
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
|
|
7
7
|
This document describes how list pages combine **views**, **toolbar** behavior, **filters**, **properties**, and **persistence** in this codebase.
|
|
8
8
|
|
|
9
|
+
## Add view parity (seven views)
|
|
10
|
+
|
|
11
|
+
**Binding:** `.cursor/rules/exxat-hub-supported-views.mdc`. **Detail:** `docs/hub-supported-views-pattern.md`.
|
|
12
|
+
|
|
13
|
+
Every list hub **should** use **`FULL_HUB_SUPPORTED_VIEWS`** (table, list, board, dashboard, folder, panel, tree-panel) on both **`ListPageTemplate`** and **`HubTable`**, with a working renderer per view. **Library** (`library-table.tsx`) is the reference. Do not ship table-only or four-view allowlists on showcase/catalog hubs unless product documents an exception.
|
|
14
|
+
|
|
9
15
|
## Reuse existing components (required)
|
|
10
16
|
|
|
11
17
|
**Prefer composing what already exists** over rebuilding one-off tabs, search, filters, or property panels. The **Placements** flow is the reference implementation; new list/table/board pages should wire the same building blocks with new data and column definitions.
|