@exxatdesignux/ui 0.5.2 → 0.5.3
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 +9 -0
- package/consumer-extras/cursor-rules/exxat-data-tables.mdc +8 -6
- package/consumer-extras/cursor-rules/exxat-ds-agents.mdc +2 -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-skills/exxat-ds-skill/SKILL.md +8 -3
- 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 +11 -4
- package/consumer-extras/handbook/HANDBOOK.md +1 -1
- package/consumer-extras/handbook/reference-implementations.md +2 -2
- package/consumer-extras/patterns/data-views-pattern.md +6 -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 -1
- package/dist/index.js +135 -13
- 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 -1
- 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 -0
- 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/AGENTS.md +16 -1
- 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/tokens-hub-auxiliary-views.tsx +301 -0
- package/template/components/tokens-themes-client.tsx +44 -16
- package/template/docs/HANDBOOK.md +1 -1
- package/template/docs/data-views-pattern.md +6 -0
- 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/package.json +1 -0
- package/tokens/hooks-index.json +2 -2
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { dataListViewAddShortcut, dataListViewIcon, dataListViewLabel } from './data-list-view.js';
|
|
2
|
-
export { D as DATA_LIST_SURFACE_VIEW_TYPES, a as DATA_LIST_VIEW_REGISTRY, b as DataListViewDefinition, c as DataListViewRenderKind, d as dataListViewDefinition, e as dataListViewSelectionTilesForHub, f as dataListViewTilesForHub, g as getDataListViewRenderKind, i as isDataListSurfaceViewType, h as isDataListViewTypeSupported, s as showsListPageHubMetricsStrip } from '../data-list-view-registry-
|
|
2
|
+
export { A as ALL_DATA_LIST_VIEW_TYPES, D as DATA_LIST_SURFACE_VIEW_TYPES, a as DATA_LIST_VIEW_REGISTRY, b as DataListViewDefinition, c as DataListViewRenderKind, F as FULL_HUB_SUPPORTED_VIEWS, P as PRIMARY_HUB_SUPPORTED_VIEWS, d as dataListViewDefinition, e as dataListViewSelectionTilesForHub, f as dataListViewTilesForHub, g as getDataListViewRenderKind, i as isDataListSurfaceViewType, h as isDataListViewTypeSupported, s as showsListPageHubMetricsStrip } from '../data-list-view-registry-BstmlfQ3.js';
|
|
@@ -62,6 +62,22 @@ var BY_VALUE = new Map(
|
|
|
62
62
|
DEFINITIONS.map((d) => [d.value, d])
|
|
63
63
|
);
|
|
64
64
|
var DATA_LIST_VIEW_REGISTRY = DEFINITIONS;
|
|
65
|
+
var PRIMARY_HUB_SUPPORTED_VIEWS = [
|
|
66
|
+
"table",
|
|
67
|
+
"list",
|
|
68
|
+
"board",
|
|
69
|
+
"dashboard"
|
|
70
|
+
];
|
|
71
|
+
var FULL_HUB_SUPPORTED_VIEWS = [
|
|
72
|
+
"table",
|
|
73
|
+
"list",
|
|
74
|
+
"board",
|
|
75
|
+
"dashboard",
|
|
76
|
+
"folder",
|
|
77
|
+
"panel",
|
|
78
|
+
"tree-panel"
|
|
79
|
+
];
|
|
80
|
+
var ALL_DATA_LIST_VIEW_TYPES = DATA_LIST_VIEW_REGISTRY.map((d) => d.value);
|
|
65
81
|
function dataListViewDefinition(view) {
|
|
66
82
|
const def = BY_VALUE.get(view);
|
|
67
83
|
if (!def) {
|
|
@@ -97,6 +113,6 @@ function isDataListViewTypeSupported(view, supported) {
|
|
|
97
113
|
return supported.includes(view);
|
|
98
114
|
}
|
|
99
115
|
|
|
100
|
-
export { DATA_LIST_SURFACE_VIEW_TYPES, DATA_LIST_VIEW_REGISTRY, dataListViewAddShortcut, dataListViewDefinition, dataListViewIcon, dataListViewLabel, dataListViewSelectionTilesForHub, dataListViewTilesForHub, getDataListViewRenderKind, isDataListSurfaceViewType, isDataListViewTypeSupported, showsListPageHubMetricsStrip };
|
|
116
|
+
export { ALL_DATA_LIST_VIEW_TYPES, DATA_LIST_SURFACE_VIEW_TYPES, DATA_LIST_VIEW_REGISTRY, FULL_HUB_SUPPORTED_VIEWS, PRIMARY_HUB_SUPPORTED_VIEWS, dataListViewAddShortcut, dataListViewDefinition, dataListViewIcon, dataListViewLabel, dataListViewSelectionTilesForHub, dataListViewTilesForHub, getDataListViewRenderKind, isDataListSurfaceViewType, isDataListViewTypeSupported, showsListPageHubMetricsStrip };
|
|
101
117
|
//# sourceMappingURL=data-list-view-registry.js.map
|
|
102
118
|
//# sourceMappingURL=data-list-view-registry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/data-list-view.ts","../../src/lib/data-list-view-surface.ts","../../src/lib/data-list-view-registry.ts"],"names":[],"mappings":";AAiBO,IAAM,oBAAA,GAIP;AAAA,EACJ,EAAE,KAAA,EAAO,OAAA,EAAa,IAAA,EAAM,UAAA,EAAoB,OAAO,YAAA,EAAa;AAAA,EACpE,EAAE,KAAA,EAAO,MAAA,EAAa,IAAA,EAAM,SAAA,EAAoB,OAAO,WAAA,EAAY;AAAA,EACnE,EAAE,KAAA,EAAO,OAAA,EAAa,IAAA,EAAM,kBAAA,EAAoB,OAAO,YAAA,EAAa;AAAA,EACpE,EAAE,KAAA,EAAO,WAAA,EAAa,IAAA,EAAM,gBAAA,EAAoB,OAAO,gBAAA,EAAiB;AAAA,EACxE,EAAE,KAAA,EAAO,UAAA,EAAa,IAAA,EAAM,kBAAA,EAAoB,OAAO,eAAA,EAAgB;AAAA,EACvE,EAAE,KAAA,EAAO,QAAA,EAAa,IAAA,EAAM,WAAA,EAAoB,OAAO,aAAA,EAAc;AAAA,EACrE,EAAE,KAAA,EAAO,OAAA,EAAc,IAAA,EAAM,YAAA,EAAmB,OAAO,gBAAA,EAAiB;AAAA,EACxE,EAAE,KAAA,EAAO,YAAA,EAAc,IAAA,EAAM,YAAA,EAAmB,OAAO,gBAAA;AACzD,CAAA;AAGO,SAAS,kBAAkB,IAAA,EAAgC;AAChE,EAAA,OAAO,qBAAqB,IAAA,CAAK,CAAA,CAAA,KAAK,EAAE,KAAA,KAAU,IAAI,GAAG,KAAA,IAAS,IAAA;AACpE;AAGO,SAAS,iBAAiB,IAAA,EAAgC;AAC/D,EAAA,OAAO,qBAAqB,IAAA,CAAK,CAAA,CAAA,KAAK,EAAE,KAAA,KAAU,IAAI,GAAG,IAAA,IAAQ,UAAA;AACnE;AAGO,SAAS,wBAAwB,KAAA,EAAmC;AACzE,EAAA,IAAI,KAAA,GAAQ,CAAA,IAAK,KAAA,GAAQ,CAAA,EAAG,OAAO,MAAA;AACnC,EAAA,OAAO,MAAA,CAAO,QAAQ,CAAC,CAAA;AACzB;;;ACTO,SAAS,0BAA0B,IAAA,EAAgD;AACxF,EAAA,QAAQ,IAAA;AAAM,IACZ,KAAK,OAAA;AACH,MAAA,OAAO,YAAA;AAAA,IACT,KAAK,MAAA;AACH,MAAA,OAAO,mBAAA;AAAA,IACT,KAAK,OAAA;AACH,MAAA,OAAO,oBAAA;AAAA,IACT,KAAK,WAAA;AACH,MAAA,OAAO,wBAAA;AAAA,IACT,KAAK,UAAA;AACH,MAAA,OAAO,uBAAA;AAAA,IACT,KAAK,QAAA;AACH,MAAA,OAAO,qBAAA;AAAA,IACT,KAAK,OAAA;AACH,MAAA,OAAO,oBAAA;AAAA,IACT,KAAK,YAAA;AACH,MAAA,OAAO,yBAAA;AAAA,IACT,SAAS;AACP,MAAA,MAAM,EAAA,GAAY,IAAA;AAClB,MAAA,OAAO,EAAA;AAAA,IACT;AAAA;AAEJ;;;AC7BA,IAAM,WAAA,GAAwC,oBAAA,CAAqB,GAAA,CAAI,CAAA,IAAA,KAAQ;AAC7E,EAAA,MAAM,UAAA,GAAa,yBAAA,CAA0B,IAAA,CAAK,KAAK,CAAA;AACvD,EAAA,MAAM,eAAA,GAAkB,UAAA,KAAe,uBAAA,IAA2B,UAAA,KAAe,wBAAA;AACjF,EAAA,OAAO;AAAA,IACL,OAAO,IAAA,CAAK,KAAA;AAAA,IACZ,OAAO,IAAA,CAAK,KAAA;AAAA,IACZ,MAAM,IAAA,CAAK,IAAA;AAAA,IACX,UAAA;AAAA,IACA;AAAA,GACF;AACF,CAAC,CAAA;AAED,IAAM,WAAW,IAAI,GAAA;AAAA,EACnB,YAAY,GAAA,CAAI,CAAA,CAAA,KAAK,CAAC,CAAA,CAAE,KAAA,EAAO,CAAC,CAAC;AACnC,CAAA;AAEO,IAAM,uBAAA,GAA6D;AAEnE,SAAS,uBAAuB,IAAA,EAAgD;AACrF,EAAA,MAAM,GAAA,GAAM,QAAA,CAAS,GAAA,CAAI,IAAI,CAAA;AAC7B,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,0BAAA,EAA6B,IAAI,CAAA,CAAE,CAAA;AAAA,EACrD;AACA,EAAA,OAAO,GAAA;AACT;AAGO,SAAS,6BAA6B,IAAA,EAAiC;AAC5E,EAAA,OAAO,sBAAA,CAAuB,IAAI,CAAA,CAAE,eAAA;AACtC;AAGO,SAAS,wBAAwB,SAAA,EAAwC;AAC9E,EAAA,MAAM,OAAA,GAAU,IAAI,GAAA,CAAI,SAAS,CAAA;AACjC,EAAA,OAAO,uBAAA,CAAwB,MAAA,CAAO,CAAA,CAAA,KAAK,OAAA,CAAQ,GAAA,CAAI,EAAE,KAAK,CAAC,CAAA,CAAE,GAAA,CAAI,CAAA,CAAA,MAAM;AAAA,IACzE,MAAM,CAAA,CAAE,KAAA;AAAA,IACR,OAAO,CAAA,CAAE,KAAA;AAAA,IACT,MAAM,CAAA,CAAE;AAAA,GACV,CAAE,CAAA;AACJ;AAGO,SAAS,iCAAiC,SAAA,EAAwC;AACvF,EAAA,OAAO,uBAAA,CAAwB,SAAS,CAAA,CAAE,GAAA,CAAI,CAAA,CAAA,MAAM;AAAA,IAClD,OAAO,CAAA,CAAE,IAAA;AAAA,IACT,OAAO,CAAA,CAAE,KAAA;AAAA,IACT,MAAM,CAAA,CAAE;AAAA,GACV,CAAE,CAAA;AACJ;AAGO,IAAM,+BAA8D,IAAI,GAAA;AAAA,EAC7E,uBAAA,CAAwB,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,KAAK;AAC1C;AAEO,SAAS,0BAA0B,QAAA,EAAgD;AACxF,EAAA,OAAO,4BAAA,CAA6B,IAAI,QAA4B,CAAA;AACtE;AAEO,SAAS,2BAAA,CACd,MACA,SAAA,EACS;AACT,EAAA,OAAO,SAAA,CAAU,SAAS,IAAI,CAAA;AAChC","file":"data-list-view-registry.js","sourcesContent":["/**\n * Data list “view type” — shared by Properties drawer, ListPageTemplate tabs, and client state.\n *\n * **Single source of truth** for view labels/icons: use `DATA_LIST_VIEW_TILES` and\n * `dataListViewLabel` / `dataListViewIcon` on every page so Table / List / Board / Dashboard\n * stay consistent and stay wired to the same `useTableState` dataset (see `docs/data-views-pattern.md`).\n */\nexport type DataListViewType =\n | \"table\"\n | \"list\"\n | \"board\"\n | \"dashboard\"\n | \"calendar\"\n | \"folder\"\n | \"panel\"\n | \"tree-panel\"\n\nexport const DATA_LIST_VIEW_TILES: readonly {\n value: DataListViewType\n label: string\n icon: string\n}[] = [\n { value: \"table\", icon: \"fa-table\", label: \"Table view\" },\n { value: \"list\", icon: \"fa-list\", label: \"List view\" },\n { value: \"board\", icon: \"fa-table-columns\", label: \"Board view\" },\n { value: \"dashboard\", icon: \"fa-chart-mixed\", label: \"Dashboard view\" },\n { value: \"calendar\", icon: \"fa-calendar-days\", label: \"Calendar view\" },\n { value: \"folder\", icon: \"fa-grid-2\", label: \"Folder view\" },\n { value: \"panel\", icon: \"fa-sidebar\", label: \"List & details\" },\n { value: \"tree-panel\", icon: \"fa-sitemap\", label: \"Tree & details\" },\n]\n\n/** User-facing name for tabs, Properties summary rows, and tooltips (not entity-specific). */\nexport function dataListViewLabel(view: DataListViewType): string {\n return DATA_LIST_VIEW_TILES.find(t => t.value === view)?.label ?? view\n}\n\n/** Font Awesome icon class (no prefix) for tab / toolbar state when view changes. */\nexport function dataListViewIcon(view: DataListViewType): string {\n return DATA_LIST_VIEW_TILES.find(t => t.value === view)?.icon ?? \"fa-table\"\n}\n\n/** Add-view menu hint + `<Shortcut>` keys (1–9). Skipped in inputs via `useShortcut`. */\nexport function dataListViewAddShortcut(index: number): string | undefined {\n if (index < 0 || index > 8) return undefined\n return String(index + 1)\n}\n","/**\n * Maps `DataListViewType` to the UI surface pattern for list pages.\n *\n * **Data:** One `useTableState(fullRows, columns, …)` per tab; **filtered/sorted rows**\n * (`tableState.rows`) are the single source of truth for List, Board, and Dashboard.\n * Table view renders the same state via `DataTable`.\n *\n * | View | Surface |\n * |------------|---------|\n * | `table` | `DataTable` |\n * | `list` | `DataTableToolbar` + list layout |\n * | `board` | `DataTableToolbar` + board / kanban |\n * | `dashboard`| `DataTableToolbar` + KPI (`KeyMetrics`) + optional charts (`ChartCard`, Recharts, etc.) |\n * | `calendar` | `DataTableToolbar` + `ListPageCalendarView` (month grid + day detail) |\n * | `folder` | `DataTableToolbar` + icon grid (macOS-Finder-style) |\n * | `panel` | `DataTableToolbar` + resizable split (list / tree column + detail inspector) |\n */\n\nimport type { DataListViewType } from \"./data-list-view\"\n\nexport { showsListPageHubMetricsStrip } from \"./data-list-view-registry\"\n\n/** What to render for the active view tab (routing / branching). */\nexport type DataListViewRenderKind =\n | \"data-table\"\n | \"list-with-toolbar\"\n | \"board-with-toolbar\"\n | \"dashboard-with-toolbar\"\n | \"calendar-with-toolbar\"\n | \"folder-with-toolbar\"\n | \"panel-with-toolbar\"\n | \"tree-panel-with-toolbar\"\n\n/**\n * Stable classification for switch/if chains. **Every** `DataListViewType` maps to exactly one kind.\n * Use this so `dashboard` is never mistaken for `board` (a common bug when only `list` is special-cased).\n */\nexport function getDataListViewRenderKind(view: DataListViewType): DataListViewRenderKind {\n switch (view) {\n case \"table\":\n return \"data-table\"\n case \"list\":\n return \"list-with-toolbar\"\n case \"board\":\n return \"board-with-toolbar\"\n case \"dashboard\":\n return \"dashboard-with-toolbar\"\n case \"calendar\":\n return \"calendar-with-toolbar\"\n case \"folder\":\n return \"folder-with-toolbar\"\n case \"panel\":\n return \"panel-with-toolbar\"\n case \"tree-panel\":\n return \"tree-panel-with-toolbar\"\n default: {\n const _x: never = view\n return _x\n }\n }\n}\n\nexport function usesDataTableComponent(view: DataListViewType): boolean {\n return view === \"table\"\n}\n\n/** KPI band + optional charts — not the kanban board. */\nexport function usesDashboardSurface(view: DataListViewType): boolean {\n return view === \"dashboard\"\n}\n\n/** Shared toolbar (search, filters, properties); body differs by view. */\nexport function usesToolbarWithFilteredRows(view: DataListViewType): boolean {\n return (\n view === \"list\" ||\n view === \"board\" ||\n view === \"dashboard\" ||\n view === \"calendar\" ||\n view === \"folder\" ||\n view === \"panel\" ||\n view === \"tree-panel\"\n )\n}\n","/**\n * Central registry for list-page view types — labels, render kinds, and hub chrome rules.\n *\n * **Add a new view once here** (plus a body in `components/data-views/`). Hubs declare\n * `supportedViewTypes` on `ListPageTemplate`; table components branch with\n * `getDataListViewRenderKind` + `ListPageConnectedViewBody` (never a dashboard fallback).\n *\n * @see `docs/data-views-pattern.md` — \"View registry and connected bodies\"\n */\n\nimport {\n DATA_LIST_VIEW_TILES,\n type DataListViewType,\n dataListViewAddShortcut,\n dataListViewIcon,\n dataListViewLabel,\n} from \"./data-list-view\"\nimport {\n getDataListViewRenderKind,\n type DataListViewRenderKind,\n} from \"./data-list-view-surface\"\n\nexport interface DataListViewDefinition {\n value: DataListViewType\n label: string\n icon: string\n renderKind: DataListViewRenderKind\n /** `ListPageTemplate` metrics slot above the views toolbar. */\n hubMetricsStrip: boolean\n}\n\nconst DEFINITIONS: DataListViewDefinition[] = DATA_LIST_VIEW_TILES.map(tile => {\n const renderKind = getDataListViewRenderKind(tile.value)\n const hubMetricsStrip = renderKind !== \"calendar-with-toolbar\" && renderKind !== \"dashboard-with-toolbar\"\n return {\n value: tile.value,\n label: tile.label,\n icon: tile.icon,\n renderKind,\n hubMetricsStrip,\n }\n})\n\nconst BY_VALUE = new Map<DataListViewType, DataListViewDefinition>(\n DEFINITIONS.map(d => [d.value, d]),\n)\n\nexport const DATA_LIST_VIEW_REGISTRY: readonly DataListViewDefinition[] = DEFINITIONS\n\nexport function dataListViewDefinition(view: DataListViewType): DataListViewDefinition {\n const def = BY_VALUE.get(view)\n if (!def) {\n throw new Error(`Unknown DataListViewType: ${view}`)\n }\n return def\n}\n\n/** `ListPageTemplate` hub KPI strip — false for calendar and dashboard (inline KPIs). */\nexport function showsListPageHubMetricsStrip(view: DataListViewType): boolean {\n return dataListViewDefinition(view).hubMetricsStrip\n}\n\n/** Tiles for Add view + Properties when a hub only supports a subset of views. */\nexport function dataListViewTilesForHub(supported: readonly DataListViewType[]) {\n const allowed = new Set(supported)\n return DATA_LIST_VIEW_REGISTRY.filter(d => allowed.has(d.value)).map(d => ({\n type: d.value,\n label: d.label,\n icon: d.icon,\n }))\n}\n\n/** `SelectionTileGrid` options for Properties when a hub supports a subset of views. */\nexport function dataListViewSelectionTilesForHub(supported: readonly DataListViewType[]) {\n return dataListViewTilesForHub(supported).map(t => ({\n value: t.type,\n label: t.label,\n icon: t.icon,\n }))\n}\n\n/** View types that expose Table Properties (all registered `DataListViewType` values). */\nexport const DATA_LIST_SURFACE_VIEW_TYPES: ReadonlySet<DataListViewType> = new Set(\n DATA_LIST_VIEW_REGISTRY.map(d => d.value),\n)\n\nexport function isDataListSurfaceViewType(viewType: string): viewType is DataListViewType {\n return DATA_LIST_SURFACE_VIEW_TYPES.has(viewType as DataListViewType)\n}\n\nexport function isDataListViewTypeSupported(\n view: DataListViewType,\n supported: readonly DataListViewType[],\n): boolean {\n return supported.includes(view)\n}\n\nexport {\n dataListViewAddShortcut,\n dataListViewIcon,\n dataListViewLabel,\n getDataListViewRenderKind,\n type DataListViewRenderKind,\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/lib/data-list-view.ts","../../src/lib/data-list-view-surface.ts","../../src/lib/data-list-view-registry.ts"],"names":[],"mappings":"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","file":"data-list-view-registry.js","sourcesContent":["/**\n * Data list “view type” — shared by Properties drawer, ListPageTemplate tabs, and client state.\n *\n * **Single source of truth** for view labels/icons: use `DATA_LIST_VIEW_TILES` and\n * `dataListViewLabel` / `dataListViewIcon` on every page so Table / List / Board / Dashboard\n * stay consistent and stay wired to the same `useTableState` dataset (see `docs/data-views-pattern.md`).\n */\nexport type DataListViewType =\n | \"table\"\n | \"list\"\n | \"board\"\n | \"dashboard\"\n | \"calendar\"\n | \"folder\"\n | \"panel\"\n | \"tree-panel\"\n\nexport const DATA_LIST_VIEW_TILES: readonly {\n value: DataListViewType\n label: string\n icon: string\n}[] = [\n { value: \"table\", icon: \"fa-table\", label: \"Table view\" },\n { value: \"list\", icon: \"fa-list\", label: \"List view\" },\n { value: \"board\", icon: \"fa-table-columns\", label: \"Board view\" },\n { value: \"dashboard\", icon: \"fa-chart-mixed\", label: \"Dashboard view\" },\n { value: \"calendar\", icon: \"fa-calendar-days\", label: \"Calendar view\" },\n { value: \"folder\", icon: \"fa-grid-2\", label: \"Folder view\" },\n { value: \"panel\", icon: \"fa-sidebar\", label: \"List & details\" },\n { value: \"tree-panel\", icon: \"fa-sitemap\", label: \"Tree & details\" },\n]\n\n/** User-facing name for tabs, Properties summary rows, and tooltips (not entity-specific). */\nexport function dataListViewLabel(view: DataListViewType): string {\n return DATA_LIST_VIEW_TILES.find(t => t.value === view)?.label ?? view\n}\n\n/** Font Awesome icon class (no prefix) for tab / toolbar state when view changes. */\nexport function dataListViewIcon(view: DataListViewType): string {\n return DATA_LIST_VIEW_TILES.find(t => t.value === view)?.icon ?? \"fa-table\"\n}\n\n/** Add-view menu hint + `<Shortcut>` keys (1–9). Skipped in inputs via `useShortcut`. */\nexport function dataListViewAddShortcut(index: number): string | undefined {\n if (index < 0 || index > 8) return undefined\n return String(index + 1)\n}\n","/**\n * Maps `DataListViewType` to the UI surface pattern for list pages.\n *\n * **Data:** One `useTableState(fullRows, columns, …)` per tab; **filtered/sorted rows**\n * (`tableState.rows`) are the single source of truth for List, Board, and Dashboard.\n * Table view renders the same state via `DataTable`.\n *\n * | View | Surface |\n * |------------|---------|\n * | `table` | `DataTable` |\n * | `list` | `DataTableToolbar` + list layout |\n * | `board` | `DataTableToolbar` + board / kanban |\n * | `dashboard`| `DataTableToolbar` + KPI (`KeyMetrics`) + optional charts (`ChartCard`, Recharts, etc.) |\n * | `calendar` | `DataTableToolbar` + `ListPageCalendarView` (month grid + day detail) |\n * | `folder` | `DataTableToolbar` + icon grid (macOS-Finder-style) |\n * | `panel` | `DataTableToolbar` + resizable split (list / tree column + detail inspector) |\n */\n\nimport type { DataListViewType } from \"./data-list-view\"\n\nexport { showsListPageHubMetricsStrip } from \"./data-list-view-registry\"\n\n/** What to render for the active view tab (routing / branching). */\nexport type DataListViewRenderKind =\n | \"data-table\"\n | \"list-with-toolbar\"\n | \"board-with-toolbar\"\n | \"dashboard-with-toolbar\"\n | \"calendar-with-toolbar\"\n | \"folder-with-toolbar\"\n | \"panel-with-toolbar\"\n | \"tree-panel-with-toolbar\"\n\n/**\n * Stable classification for switch/if chains. **Every** `DataListViewType` maps to exactly one kind.\n * Use this so `dashboard` is never mistaken for `board` (a common bug when only `list` is special-cased).\n */\nexport function getDataListViewRenderKind(view: DataListViewType): DataListViewRenderKind {\n switch (view) {\n case \"table\":\n return \"data-table\"\n case \"list\":\n return \"list-with-toolbar\"\n case \"board\":\n return \"board-with-toolbar\"\n case \"dashboard\":\n return \"dashboard-with-toolbar\"\n case \"calendar\":\n return \"calendar-with-toolbar\"\n case \"folder\":\n return \"folder-with-toolbar\"\n case \"panel\":\n return \"panel-with-toolbar\"\n case \"tree-panel\":\n return \"tree-panel-with-toolbar\"\n default: {\n const _x: never = view\n return _x\n }\n }\n}\n\nexport function usesDataTableComponent(view: DataListViewType): boolean {\n return view === \"table\"\n}\n\n/** KPI band + optional charts — not the kanban board. */\nexport function usesDashboardSurface(view: DataListViewType): boolean {\n return view === \"dashboard\"\n}\n\n/** Shared toolbar (search, filters, properties); body differs by view. */\nexport function usesToolbarWithFilteredRows(view: DataListViewType): boolean {\n return (\n view === \"list\" ||\n view === \"board\" ||\n view === \"dashboard\" ||\n view === \"calendar\" ||\n view === \"folder\" ||\n view === \"panel\" ||\n view === \"tree-panel\"\n )\n}\n","/**\n * Central registry for list-page view types — labels, render kinds, and hub chrome rules.\n *\n * **Add a new view once here** (plus a body in `components/data-views/`). Hubs declare\n * `supportedViewTypes` on `ListPageTemplate`; table components branch with\n * `getDataListViewRenderKind` + `ListPageConnectedViewBody` (never a dashboard fallback).\n *\n * @see `docs/data-views-pattern.md` — \"View registry and connected bodies\"\n * @see `.cursor/rules/exxat-hub-supported-views.mdc` — default hubs use **`FULL_HUB_SUPPORTED_VIEWS`** (seven views)\n */\n\nimport {\n DATA_LIST_VIEW_TILES,\n type DataListViewType,\n dataListViewAddShortcut,\n dataListViewIcon,\n dataListViewLabel,\n} from \"./data-list-view\"\nimport {\n getDataListViewRenderKind,\n type DataListViewRenderKind,\n} from \"./data-list-view-surface\"\n\nexport interface DataListViewDefinition {\n value: DataListViewType\n label: string\n icon: string\n renderKind: DataListViewRenderKind\n /** `ListPageTemplate` metrics slot above the views toolbar. */\n hubMetricsStrip: boolean\n}\n\nconst DEFINITIONS: DataListViewDefinition[] = DATA_LIST_VIEW_TILES.map(tile => {\n const renderKind = getDataListViewRenderKind(tile.value)\n const hubMetricsStrip = renderKind !== \"calendar-with-toolbar\" && renderKind !== \"dashboard-with-toolbar\"\n return {\n value: tile.value,\n label: tile.label,\n icon: tile.icon,\n renderKind,\n hubMetricsStrip,\n }\n})\n\nconst BY_VALUE = new Map<DataListViewType, DataListViewDefinition>(\n DEFINITIONS.map(d => [d.value, d]),\n)\n\nexport const DATA_LIST_VIEW_REGISTRY: readonly DataListViewDefinition[] = DEFINITIONS\n\n/**\n * Default view allowlist for **primary list hubs** (Placements / Team / Students-style pages).\n * Pair with `ListPageTemplate` + `HubTable` when the hub implements table, list, board,\n * and dashboard renderers. Omit `supportedViewTypes` on both components to use this default.\n */\nexport const PRIMARY_HUB_SUPPORTED_VIEWS = [\n \"table\",\n \"list\",\n \"board\",\n \"dashboard\",\n] as const satisfies readonly DataListViewType[]\n\n/**\n * Default allowlist for **list-page hubs** in this design system (Library / Column types /\n * Tokens, etc.). Matches the All questions hub: table, list, board, dashboard, folder,\n * panel, and tree-panel. Pair with renderers for each kind on `HubTable` + `ListPageTemplate`.\n */\nexport const FULL_HUB_SUPPORTED_VIEWS = [\n \"table\",\n \"list\",\n \"board\",\n \"dashboard\",\n \"folder\",\n \"panel\",\n \"tree-panel\",\n] as const satisfies readonly DataListViewType[]\n\n/** Every registered view type (includes folder, panel, calendar, tree-panel). */\nexport const ALL_DATA_LIST_VIEW_TYPES = DATA_LIST_VIEW_REGISTRY.map(d => d.value)\n\nexport function dataListViewDefinition(view: DataListViewType): DataListViewDefinition {\n const def = BY_VALUE.get(view)\n if (!def) {\n throw new Error(`Unknown DataListViewType: ${view}`)\n }\n return def\n}\n\n/** `ListPageTemplate` hub KPI strip — false for calendar and dashboard (inline KPIs). */\nexport function showsListPageHubMetricsStrip(view: DataListViewType): boolean {\n return dataListViewDefinition(view).hubMetricsStrip\n}\n\n/** Tiles for Add view + Properties when a hub only supports a subset of views. */\nexport function dataListViewTilesForHub(supported: readonly DataListViewType[]) {\n const allowed = new Set(supported)\n return DATA_LIST_VIEW_REGISTRY.filter(d => allowed.has(d.value)).map(d => ({\n type: d.value,\n label: d.label,\n icon: d.icon,\n }))\n}\n\n/** `SelectionTileGrid` options for Properties when a hub supports a subset of views. */\nexport function dataListViewSelectionTilesForHub(supported: readonly DataListViewType[]) {\n return dataListViewTilesForHub(supported).map(t => ({\n value: t.type,\n label: t.label,\n icon: t.icon,\n }))\n}\n\n/** View types that expose Table Properties (all registered `DataListViewType` values). */\nexport const DATA_LIST_SURFACE_VIEW_TYPES: ReadonlySet<DataListViewType> = new Set(\n DATA_LIST_VIEW_REGISTRY.map(d => d.value),\n)\n\nexport function isDataListSurfaceViewType(viewType: string): viewType is DataListViewType {\n return DATA_LIST_SURFACE_VIEW_TYPES.has(viewType as DataListViewType)\n}\n\nexport function isDataListViewTypeSupported(\n view: DataListViewType,\n supported: readonly DataListViewType[],\n): boolean {\n return supported.includes(view)\n}\n\nexport {\n dataListViewAddShortcut,\n dataListViewIcon,\n dataListViewLabel,\n getDataListViewRenderKind,\n type DataListViewRenderKind,\n}\n"]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import './data-list-view.js';
|
|
2
|
-
export { c as DataListViewRenderKind, g as getDataListViewRenderKind, s as showsListPageHubMetricsStrip, u as usesDashboardSurface, j as usesDataTableComponent, k as usesToolbarWithFilteredRows } from '../data-list-view-registry-
|
|
2
|
+
export { c as DataListViewRenderKind, g as getDataListViewRenderKind, s as showsListPageHubMetricsStrip, u as usesDashboardSurface, j as usesDataTableComponent, k as usesToolbarWithFilteredRows } from '../data-list-view-registry-BstmlfQ3.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/data-list-view.ts","../../src/lib/data-list-view-registry.ts","../../src/lib/data-list-view-surface.ts"],"names":[],"mappings":"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","file":"data-list-view-surface.js","sourcesContent":["/**\n * Data list “view type” — shared by Properties drawer, ListPageTemplate tabs, and client state.\n *\n * **Single source of truth** for view labels/icons: use `DATA_LIST_VIEW_TILES` and\n * `dataListViewLabel` / `dataListViewIcon` on every page so Table / List / Board / Dashboard\n * stay consistent and stay wired to the same `useTableState` dataset (see `docs/data-views-pattern.md`).\n */\nexport type DataListViewType =\n | \"table\"\n | \"list\"\n | \"board\"\n | \"dashboard\"\n | \"calendar\"\n | \"folder\"\n | \"panel\"\n | \"tree-panel\"\n\nexport const DATA_LIST_VIEW_TILES: readonly {\n value: DataListViewType\n label: string\n icon: string\n}[] = [\n { value: \"table\", icon: \"fa-table\", label: \"Table view\" },\n { value: \"list\", icon: \"fa-list\", label: \"List view\" },\n { value: \"board\", icon: \"fa-table-columns\", label: \"Board view\" },\n { value: \"dashboard\", icon: \"fa-chart-mixed\", label: \"Dashboard view\" },\n { value: \"calendar\", icon: \"fa-calendar-days\", label: \"Calendar view\" },\n { value: \"folder\", icon: \"fa-grid-2\", label: \"Folder view\" },\n { value: \"panel\", icon: \"fa-sidebar\", label: \"List & details\" },\n { value: \"tree-panel\", icon: \"fa-sitemap\", label: \"Tree & details\" },\n]\n\n/** User-facing name for tabs, Properties summary rows, and tooltips (not entity-specific). */\nexport function dataListViewLabel(view: DataListViewType): string {\n return DATA_LIST_VIEW_TILES.find(t => t.value === view)?.label ?? view\n}\n\n/** Font Awesome icon class (no prefix) for tab / toolbar state when view changes. */\nexport function dataListViewIcon(view: DataListViewType): string {\n return DATA_LIST_VIEW_TILES.find(t => t.value === view)?.icon ?? \"fa-table\"\n}\n\n/** Add-view menu hint + `<Shortcut>` keys (1–9). Skipped in inputs via `useShortcut`. */\nexport function dataListViewAddShortcut(index: number): string | undefined {\n if (index < 0 || index > 8) return undefined\n return String(index + 1)\n}\n","/**\n * Central registry for list-page view types — labels, render kinds, and hub chrome rules.\n *\n * **Add a new view once here** (plus a body in `components/data-views/`). Hubs declare\n * `supportedViewTypes` on `ListPageTemplate`; table components branch with\n * `getDataListViewRenderKind` + `ListPageConnectedViewBody` (never a dashboard fallback).\n *\n * @see `docs/data-views-pattern.md` — \"View registry and connected bodies\"\n */\n\nimport {\n DATA_LIST_VIEW_TILES,\n type DataListViewType,\n dataListViewAddShortcut,\n dataListViewIcon,\n dataListViewLabel,\n} from \"./data-list-view\"\nimport {\n getDataListViewRenderKind,\n type DataListViewRenderKind,\n} from \"./data-list-view-surface\"\n\nexport interface DataListViewDefinition {\n value: DataListViewType\n label: string\n icon: string\n renderKind: DataListViewRenderKind\n /** `ListPageTemplate` metrics slot above the views toolbar. */\n hubMetricsStrip: boolean\n}\n\nconst DEFINITIONS: DataListViewDefinition[] = DATA_LIST_VIEW_TILES.map(tile => {\n const renderKind = getDataListViewRenderKind(tile.value)\n const hubMetricsStrip = renderKind !== \"calendar-with-toolbar\" && renderKind !== \"dashboard-with-toolbar\"\n return {\n value: tile.value,\n label: tile.label,\n icon: tile.icon,\n renderKind,\n hubMetricsStrip,\n }\n})\n\nconst BY_VALUE = new Map<DataListViewType, DataListViewDefinition>(\n DEFINITIONS.map(d => [d.value, d]),\n)\n\nexport const DATA_LIST_VIEW_REGISTRY: readonly DataListViewDefinition[] = DEFINITIONS\n\nexport function dataListViewDefinition(view: DataListViewType): DataListViewDefinition {\n const def = BY_VALUE.get(view)\n if (!def) {\n throw new Error(`Unknown DataListViewType: ${view}`)\n }\n return def\n}\n\n/** `ListPageTemplate` hub KPI strip — false for calendar and dashboard (inline KPIs). */\nexport function showsListPageHubMetricsStrip(view: DataListViewType): boolean {\n return dataListViewDefinition(view).hubMetricsStrip\n}\n\n/** Tiles for Add view + Properties when a hub only supports a subset of views. */\nexport function dataListViewTilesForHub(supported: readonly DataListViewType[]) {\n const allowed = new Set(supported)\n return DATA_LIST_VIEW_REGISTRY.filter(d => allowed.has(d.value)).map(d => ({\n type: d.value,\n label: d.label,\n icon: d.icon,\n }))\n}\n\n/** `SelectionTileGrid` options for Properties when a hub supports a subset of views. */\nexport function dataListViewSelectionTilesForHub(supported: readonly DataListViewType[]) {\n return dataListViewTilesForHub(supported).map(t => ({\n value: t.type,\n label: t.label,\n icon: t.icon,\n }))\n}\n\n/** View types that expose Table Properties (all registered `DataListViewType` values). */\nexport const DATA_LIST_SURFACE_VIEW_TYPES: ReadonlySet<DataListViewType> = new Set(\n DATA_LIST_VIEW_REGISTRY.map(d => d.value),\n)\n\nexport function isDataListSurfaceViewType(viewType: string): viewType is DataListViewType {\n return DATA_LIST_SURFACE_VIEW_TYPES.has(viewType as DataListViewType)\n}\n\nexport function isDataListViewTypeSupported(\n view: DataListViewType,\n supported: readonly DataListViewType[],\n): boolean {\n return supported.includes(view)\n}\n\nexport {\n dataListViewAddShortcut,\n dataListViewIcon,\n dataListViewLabel,\n getDataListViewRenderKind,\n type DataListViewRenderKind,\n}\n","/**\n * Maps `DataListViewType` to the UI surface pattern for list pages.\n *\n * **Data:** One `useTableState(fullRows, columns, …)` per tab; **filtered/sorted rows**\n * (`tableState.rows`) are the single source of truth for List, Board, and Dashboard.\n * Table view renders the same state via `DataTable`.\n *\n * | View | Surface |\n * |------------|---------|\n * | `table` | `DataTable` |\n * | `list` | `DataTableToolbar` + list layout |\n * | `board` | `DataTableToolbar` + board / kanban |\n * | `dashboard`| `DataTableToolbar` + KPI (`KeyMetrics`) + optional charts (`ChartCard`, Recharts, etc.) |\n * | `calendar` | `DataTableToolbar` + `ListPageCalendarView` (month grid + day detail) |\n * | `folder` | `DataTableToolbar` + icon grid (macOS-Finder-style) |\n * | `panel` | `DataTableToolbar` + resizable split (list / tree column + detail inspector) |\n */\n\nimport type { DataListViewType } from \"./data-list-view\"\n\nexport { showsListPageHubMetricsStrip } from \"./data-list-view-registry\"\n\n/** What to render for the active view tab (routing / branching). */\nexport type DataListViewRenderKind =\n | \"data-table\"\n | \"list-with-toolbar\"\n | \"board-with-toolbar\"\n | \"dashboard-with-toolbar\"\n | \"calendar-with-toolbar\"\n | \"folder-with-toolbar\"\n | \"panel-with-toolbar\"\n | \"tree-panel-with-toolbar\"\n\n/**\n * Stable classification for switch/if chains. **Every** `DataListViewType` maps to exactly one kind.\n * Use this so `dashboard` is never mistaken for `board` (a common bug when only `list` is special-cased).\n */\nexport function getDataListViewRenderKind(view: DataListViewType): DataListViewRenderKind {\n switch (view) {\n case \"table\":\n return \"data-table\"\n case \"list\":\n return \"list-with-toolbar\"\n case \"board\":\n return \"board-with-toolbar\"\n case \"dashboard\":\n return \"dashboard-with-toolbar\"\n case \"calendar\":\n return \"calendar-with-toolbar\"\n case \"folder\":\n return \"folder-with-toolbar\"\n case \"panel\":\n return \"panel-with-toolbar\"\n case \"tree-panel\":\n return \"tree-panel-with-toolbar\"\n default: {\n const _x: never = view\n return _x\n }\n }\n}\n\nexport function usesDataTableComponent(view: DataListViewType): boolean {\n return view === \"table\"\n}\n\n/** KPI band + optional charts — not the kanban board. */\nexport function usesDashboardSurface(view: DataListViewType): boolean {\n return view === \"dashboard\"\n}\n\n/** Shared toolbar (search, filters, properties); body differs by view. */\nexport function usesToolbarWithFilteredRows(view: DataListViewType): boolean {\n return (\n view === \"list\" ||\n view === \"board\" ||\n view === \"dashboard\" ||\n view === \"calendar\" ||\n view === \"folder\" ||\n view === \"panel\" ||\n view === \"tree-panel\"\n )\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/lib/data-list-view.ts","../../src/lib/data-list-view-registry.ts","../../src/lib/data-list-view-surface.ts"],"names":[],"mappings":"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","file":"data-list-view-surface.js","sourcesContent":["/**\n * Data list “view type” — shared by Properties drawer, ListPageTemplate tabs, and client state.\n *\n * **Single source of truth** for view labels/icons: use `DATA_LIST_VIEW_TILES` and\n * `dataListViewLabel` / `dataListViewIcon` on every page so Table / List / Board / Dashboard\n * stay consistent and stay wired to the same `useTableState` dataset (see `docs/data-views-pattern.md`).\n */\nexport type DataListViewType =\n | \"table\"\n | \"list\"\n | \"board\"\n | \"dashboard\"\n | \"calendar\"\n | \"folder\"\n | \"panel\"\n | \"tree-panel\"\n\nexport const DATA_LIST_VIEW_TILES: readonly {\n value: DataListViewType\n label: string\n icon: string\n}[] = [\n { value: \"table\", icon: \"fa-table\", label: \"Table view\" },\n { value: \"list\", icon: \"fa-list\", label: \"List view\" },\n { value: \"board\", icon: \"fa-table-columns\", label: \"Board view\" },\n { value: \"dashboard\", icon: \"fa-chart-mixed\", label: \"Dashboard view\" },\n { value: \"calendar\", icon: \"fa-calendar-days\", label: \"Calendar view\" },\n { value: \"folder\", icon: \"fa-grid-2\", label: \"Folder view\" },\n { value: \"panel\", icon: \"fa-sidebar\", label: \"List & details\" },\n { value: \"tree-panel\", icon: \"fa-sitemap\", label: \"Tree & details\" },\n]\n\n/** User-facing name for tabs, Properties summary rows, and tooltips (not entity-specific). */\nexport function dataListViewLabel(view: DataListViewType): string {\n return DATA_LIST_VIEW_TILES.find(t => t.value === view)?.label ?? view\n}\n\n/** Font Awesome icon class (no prefix) for tab / toolbar state when view changes. */\nexport function dataListViewIcon(view: DataListViewType): string {\n return DATA_LIST_VIEW_TILES.find(t => t.value === view)?.icon ?? \"fa-table\"\n}\n\n/** Add-view menu hint + `<Shortcut>` keys (1–9). Skipped in inputs via `useShortcut`. */\nexport function dataListViewAddShortcut(index: number): string | undefined {\n if (index < 0 || index > 8) return undefined\n return String(index + 1)\n}\n","/**\n * Central registry for list-page view types — labels, render kinds, and hub chrome rules.\n *\n * **Add a new view once here** (plus a body in `components/data-views/`). Hubs declare\n * `supportedViewTypes` on `ListPageTemplate`; table components branch with\n * `getDataListViewRenderKind` + `ListPageConnectedViewBody` (never a dashboard fallback).\n *\n * @see `docs/data-views-pattern.md` — \"View registry and connected bodies\"\n * @see `.cursor/rules/exxat-hub-supported-views.mdc` — default hubs use **`FULL_HUB_SUPPORTED_VIEWS`** (seven views)\n */\n\nimport {\n DATA_LIST_VIEW_TILES,\n type DataListViewType,\n dataListViewAddShortcut,\n dataListViewIcon,\n dataListViewLabel,\n} from \"./data-list-view\"\nimport {\n getDataListViewRenderKind,\n type DataListViewRenderKind,\n} from \"./data-list-view-surface\"\n\nexport interface DataListViewDefinition {\n value: DataListViewType\n label: string\n icon: string\n renderKind: DataListViewRenderKind\n /** `ListPageTemplate` metrics slot above the views toolbar. */\n hubMetricsStrip: boolean\n}\n\nconst DEFINITIONS: DataListViewDefinition[] = DATA_LIST_VIEW_TILES.map(tile => {\n const renderKind = getDataListViewRenderKind(tile.value)\n const hubMetricsStrip = renderKind !== \"calendar-with-toolbar\" && renderKind !== \"dashboard-with-toolbar\"\n return {\n value: tile.value,\n label: tile.label,\n icon: tile.icon,\n renderKind,\n hubMetricsStrip,\n }\n})\n\nconst BY_VALUE = new Map<DataListViewType, DataListViewDefinition>(\n DEFINITIONS.map(d => [d.value, d]),\n)\n\nexport const DATA_LIST_VIEW_REGISTRY: readonly DataListViewDefinition[] = DEFINITIONS\n\n/**\n * Default view allowlist for **primary list hubs** (Placements / Team / Students-style pages).\n * Pair with `ListPageTemplate` + `HubTable` when the hub implements table, list, board,\n * and dashboard renderers. Omit `supportedViewTypes` on both components to use this default.\n */\nexport const PRIMARY_HUB_SUPPORTED_VIEWS = [\n \"table\",\n \"list\",\n \"board\",\n \"dashboard\",\n] as const satisfies readonly DataListViewType[]\n\n/**\n * Default allowlist for **list-page hubs** in this design system (Library / Column types /\n * Tokens, etc.). Matches the All questions hub: table, list, board, dashboard, folder,\n * panel, and tree-panel. Pair with renderers for each kind on `HubTable` + `ListPageTemplate`.\n */\nexport const FULL_HUB_SUPPORTED_VIEWS = [\n \"table\",\n \"list\",\n \"board\",\n \"dashboard\",\n \"folder\",\n \"panel\",\n \"tree-panel\",\n] as const satisfies readonly DataListViewType[]\n\n/** Every registered view type (includes folder, panel, calendar, tree-panel). */\nexport const ALL_DATA_LIST_VIEW_TYPES = DATA_LIST_VIEW_REGISTRY.map(d => d.value)\n\nexport function dataListViewDefinition(view: DataListViewType): DataListViewDefinition {\n const def = BY_VALUE.get(view)\n if (!def) {\n throw new Error(`Unknown DataListViewType: ${view}`)\n }\n return def\n}\n\n/** `ListPageTemplate` hub KPI strip — false for calendar and dashboard (inline KPIs). */\nexport function showsListPageHubMetricsStrip(view: DataListViewType): boolean {\n return dataListViewDefinition(view).hubMetricsStrip\n}\n\n/** Tiles for Add view + Properties when a hub only supports a subset of views. */\nexport function dataListViewTilesForHub(supported: readonly DataListViewType[]) {\n const allowed = new Set(supported)\n return DATA_LIST_VIEW_REGISTRY.filter(d => allowed.has(d.value)).map(d => ({\n type: d.value,\n label: d.label,\n icon: d.icon,\n }))\n}\n\n/** `SelectionTileGrid` options for Properties when a hub supports a subset of views. */\nexport function dataListViewSelectionTilesForHub(supported: readonly DataListViewType[]) {\n return dataListViewTilesForHub(supported).map(t => ({\n value: t.type,\n label: t.label,\n icon: t.icon,\n }))\n}\n\n/** View types that expose Table Properties (all registered `DataListViewType` values). */\nexport const DATA_LIST_SURFACE_VIEW_TYPES: ReadonlySet<DataListViewType> = new Set(\n DATA_LIST_VIEW_REGISTRY.map(d => d.value),\n)\n\nexport function isDataListSurfaceViewType(viewType: string): viewType is DataListViewType {\n return DATA_LIST_SURFACE_VIEW_TYPES.has(viewType as DataListViewType)\n}\n\nexport function isDataListViewTypeSupported(\n view: DataListViewType,\n supported: readonly DataListViewType[],\n): boolean {\n return supported.includes(view)\n}\n\nexport {\n dataListViewAddShortcut,\n dataListViewIcon,\n dataListViewLabel,\n getDataListViewRenderKind,\n type DataListViewRenderKind,\n}\n","/**\n * Maps `DataListViewType` to the UI surface pattern for list pages.\n *\n * **Data:** One `useTableState(fullRows, columns, …)` per tab; **filtered/sorted rows**\n * (`tableState.rows`) are the single source of truth for List, Board, and Dashboard.\n * Table view renders the same state via `DataTable`.\n *\n * | View | Surface |\n * |------------|---------|\n * | `table` | `DataTable` |\n * | `list` | `DataTableToolbar` + list layout |\n * | `board` | `DataTableToolbar` + board / kanban |\n * | `dashboard`| `DataTableToolbar` + KPI (`KeyMetrics`) + optional charts (`ChartCard`, Recharts, etc.) |\n * | `calendar` | `DataTableToolbar` + `ListPageCalendarView` (month grid + day detail) |\n * | `folder` | `DataTableToolbar` + icon grid (macOS-Finder-style) |\n * | `panel` | `DataTableToolbar` + resizable split (list / tree column + detail inspector) |\n */\n\nimport type { DataListViewType } from \"./data-list-view\"\n\nexport { showsListPageHubMetricsStrip } from \"./data-list-view-registry\"\n\n/** What to render for the active view tab (routing / branching). */\nexport type DataListViewRenderKind =\n | \"data-table\"\n | \"list-with-toolbar\"\n | \"board-with-toolbar\"\n | \"dashboard-with-toolbar\"\n | \"calendar-with-toolbar\"\n | \"folder-with-toolbar\"\n | \"panel-with-toolbar\"\n | \"tree-panel-with-toolbar\"\n\n/**\n * Stable classification for switch/if chains. **Every** `DataListViewType` maps to exactly one kind.\n * Use this so `dashboard` is never mistaken for `board` (a common bug when only `list` is special-cased).\n */\nexport function getDataListViewRenderKind(view: DataListViewType): DataListViewRenderKind {\n switch (view) {\n case \"table\":\n return \"data-table\"\n case \"list\":\n return \"list-with-toolbar\"\n case \"board\":\n return \"board-with-toolbar\"\n case \"dashboard\":\n return \"dashboard-with-toolbar\"\n case \"calendar\":\n return \"calendar-with-toolbar\"\n case \"folder\":\n return \"folder-with-toolbar\"\n case \"panel\":\n return \"panel-with-toolbar\"\n case \"tree-panel\":\n return \"tree-panel-with-toolbar\"\n default: {\n const _x: never = view\n return _x\n }\n }\n}\n\nexport function usesDataTableComponent(view: DataListViewType): boolean {\n return view === \"table\"\n}\n\n/** KPI band + optional charts — not the kanban board. */\nexport function usesDashboardSurface(view: DataListViewType): boolean {\n return view === \"dashboard\"\n}\n\n/** Shared toolbar (search, filters, properties); body differs by view. */\nexport function usesToolbarWithFilteredRows(view: DataListViewType): boolean {\n return (\n view === \"list\" ||\n view === \"board\" ||\n view === \"dashboard\" ||\n view === \"calendar\" ||\n view === \"folder\" ||\n view === \"panel\" ||\n view === \"tree-panel\"\n )\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { DataListViewType } from './data-list-view.js';
|
|
3
|
-
export { i as isDataListSurfaceViewType } from '../data-list-view-registry-
|
|
3
|
+
export { i as isDataListSurfaceViewType } from '../data-list-view-registry-BstmlfQ3.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Connects ListPageTemplate “View → Edit” to a surface that hosts TablePropertiesDrawer
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/data-list-view.ts","../../src/lib/data-list-view-surface.ts","../../src/lib/data-list-view-registry.ts","../../src/lib/list-page-table-properties.ts"],"names":[],"mappings":"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","file":"list-page-table-properties.js","sourcesContent":["/**\n * Data list “view type” — shared by Properties drawer, ListPageTemplate tabs, and client state.\n *\n * **Single source of truth** for view labels/icons: use `DATA_LIST_VIEW_TILES` and\n * `dataListViewLabel` / `dataListViewIcon` on every page so Table / List / Board / Dashboard\n * stay consistent and stay wired to the same `useTableState` dataset (see `docs/data-views-pattern.md`).\n */\nexport type DataListViewType =\n | \"table\"\n | \"list\"\n | \"board\"\n | \"dashboard\"\n | \"calendar\"\n | \"folder\"\n | \"panel\"\n | \"tree-panel\"\n\nexport const DATA_LIST_VIEW_TILES: readonly {\n value: DataListViewType\n label: string\n icon: string\n}[] = [\n { value: \"table\", icon: \"fa-table\", label: \"Table view\" },\n { value: \"list\", icon: \"fa-list\", label: \"List view\" },\n { value: \"board\", icon: \"fa-table-columns\", label: \"Board view\" },\n { value: \"dashboard\", icon: \"fa-chart-mixed\", label: \"Dashboard view\" },\n { value: \"calendar\", icon: \"fa-calendar-days\", label: \"Calendar view\" },\n { value: \"folder\", icon: \"fa-grid-2\", label: \"Folder view\" },\n { value: \"panel\", icon: \"fa-sidebar\", label: \"List & details\" },\n { value: \"tree-panel\", icon: \"fa-sitemap\", label: \"Tree & details\" },\n]\n\n/** User-facing name for tabs, Properties summary rows, and tooltips (not entity-specific). */\nexport function dataListViewLabel(view: DataListViewType): string {\n return DATA_LIST_VIEW_TILES.find(t => t.value === view)?.label ?? view\n}\n\n/** Font Awesome icon class (no prefix) for tab / toolbar state when view changes. */\nexport function dataListViewIcon(view: DataListViewType): string {\n return DATA_LIST_VIEW_TILES.find(t => t.value === view)?.icon ?? \"fa-table\"\n}\n\n/** Add-view menu hint + `<Shortcut>` keys (1–9). Skipped in inputs via `useShortcut`. */\nexport function dataListViewAddShortcut(index: number): string | undefined {\n if (index < 0 || index > 8) return undefined\n return String(index + 1)\n}\n","/**\n * Maps `DataListViewType` to the UI surface pattern for list pages.\n *\n * **Data:** One `useTableState(fullRows, columns, …)` per tab; **filtered/sorted rows**\n * (`tableState.rows`) are the single source of truth for List, Board, and Dashboard.\n * Table view renders the same state via `DataTable`.\n *\n * | View | Surface |\n * |------------|---------|\n * | `table` | `DataTable` |\n * | `list` | `DataTableToolbar` + list layout |\n * | `board` | `DataTableToolbar` + board / kanban |\n * | `dashboard`| `DataTableToolbar` + KPI (`KeyMetrics`) + optional charts (`ChartCard`, Recharts, etc.) |\n * | `calendar` | `DataTableToolbar` + `ListPageCalendarView` (month grid + day detail) |\n * | `folder` | `DataTableToolbar` + icon grid (macOS-Finder-style) |\n * | `panel` | `DataTableToolbar` + resizable split (list / tree column + detail inspector) |\n */\n\nimport type { DataListViewType } from \"./data-list-view\"\n\nexport { showsListPageHubMetricsStrip } from \"./data-list-view-registry\"\n\n/** What to render for the active view tab (routing / branching). */\nexport type DataListViewRenderKind =\n | \"data-table\"\n | \"list-with-toolbar\"\n | \"board-with-toolbar\"\n | \"dashboard-with-toolbar\"\n | \"calendar-with-toolbar\"\n | \"folder-with-toolbar\"\n | \"panel-with-toolbar\"\n | \"tree-panel-with-toolbar\"\n\n/**\n * Stable classification for switch/if chains. **Every** `DataListViewType` maps to exactly one kind.\n * Use this so `dashboard` is never mistaken for `board` (a common bug when only `list` is special-cased).\n */\nexport function getDataListViewRenderKind(view: DataListViewType): DataListViewRenderKind {\n switch (view) {\n case \"table\":\n return \"data-table\"\n case \"list\":\n return \"list-with-toolbar\"\n case \"board\":\n return \"board-with-toolbar\"\n case \"dashboard\":\n return \"dashboard-with-toolbar\"\n case \"calendar\":\n return \"calendar-with-toolbar\"\n case \"folder\":\n return \"folder-with-toolbar\"\n case \"panel\":\n return \"panel-with-toolbar\"\n case \"tree-panel\":\n return \"tree-panel-with-toolbar\"\n default: {\n const _x: never = view\n return _x\n }\n }\n}\n\nexport function usesDataTableComponent(view: DataListViewType): boolean {\n return view === \"table\"\n}\n\n/** KPI band + optional charts — not the kanban board. */\nexport function usesDashboardSurface(view: DataListViewType): boolean {\n return view === \"dashboard\"\n}\n\n/** Shared toolbar (search, filters, properties); body differs by view. */\nexport function usesToolbarWithFilteredRows(view: DataListViewType): boolean {\n return (\n view === \"list\" ||\n view === \"board\" ||\n view === \"dashboard\" ||\n view === \"calendar\" ||\n view === \"folder\" ||\n view === \"panel\" ||\n view === \"tree-panel\"\n )\n}\n","/**\n * Central registry for list-page view types — labels, render kinds, and hub chrome rules.\n *\n * **Add a new view once here** (plus a body in `components/data-views/`). Hubs declare\n * `supportedViewTypes` on `ListPageTemplate`; table components branch with\n * `getDataListViewRenderKind` + `ListPageConnectedViewBody` (never a dashboard fallback).\n *\n * @see `docs/data-views-pattern.md` — \"View registry and connected bodies\"\n */\n\nimport {\n DATA_LIST_VIEW_TILES,\n type DataListViewType,\n dataListViewAddShortcut,\n dataListViewIcon,\n dataListViewLabel,\n} from \"./data-list-view\"\nimport {\n getDataListViewRenderKind,\n type DataListViewRenderKind,\n} from \"./data-list-view-surface\"\n\nexport interface DataListViewDefinition {\n value: DataListViewType\n label: string\n icon: string\n renderKind: DataListViewRenderKind\n /** `ListPageTemplate` metrics slot above the views toolbar. */\n hubMetricsStrip: boolean\n}\n\nconst DEFINITIONS: DataListViewDefinition[] = DATA_LIST_VIEW_TILES.map(tile => {\n const renderKind = getDataListViewRenderKind(tile.value)\n const hubMetricsStrip = renderKind !== \"calendar-with-toolbar\" && renderKind !== \"dashboard-with-toolbar\"\n return {\n value: tile.value,\n label: tile.label,\n icon: tile.icon,\n renderKind,\n hubMetricsStrip,\n }\n})\n\nconst BY_VALUE = new Map<DataListViewType, DataListViewDefinition>(\n DEFINITIONS.map(d => [d.value, d]),\n)\n\nexport const DATA_LIST_VIEW_REGISTRY: readonly DataListViewDefinition[] = DEFINITIONS\n\nexport function dataListViewDefinition(view: DataListViewType): DataListViewDefinition {\n const def = BY_VALUE.get(view)\n if (!def) {\n throw new Error(`Unknown DataListViewType: ${view}`)\n }\n return def\n}\n\n/** `ListPageTemplate` hub KPI strip — false for calendar and dashboard (inline KPIs). */\nexport function showsListPageHubMetricsStrip(view: DataListViewType): boolean {\n return dataListViewDefinition(view).hubMetricsStrip\n}\n\n/** Tiles for Add view + Properties when a hub only supports a subset of views. */\nexport function dataListViewTilesForHub(supported: readonly DataListViewType[]) {\n const allowed = new Set(supported)\n return DATA_LIST_VIEW_REGISTRY.filter(d => allowed.has(d.value)).map(d => ({\n type: d.value,\n label: d.label,\n icon: d.icon,\n }))\n}\n\n/** `SelectionTileGrid` options for Properties when a hub supports a subset of views. */\nexport function dataListViewSelectionTilesForHub(supported: readonly DataListViewType[]) {\n return dataListViewTilesForHub(supported).map(t => ({\n value: t.type,\n label: t.label,\n icon: t.icon,\n }))\n}\n\n/** View types that expose Table Properties (all registered `DataListViewType` values). */\nexport const DATA_LIST_SURFACE_VIEW_TYPES: ReadonlySet<DataListViewType> = new Set(\n DATA_LIST_VIEW_REGISTRY.map(d => d.value),\n)\n\nexport function isDataListSurfaceViewType(viewType: string): viewType is DataListViewType {\n return DATA_LIST_SURFACE_VIEW_TYPES.has(viewType as DataListViewType)\n}\n\nexport function isDataListViewTypeSupported(\n view: DataListViewType,\n supported: readonly DataListViewType[],\n): boolean {\n return supported.includes(view)\n}\n\nexport {\n dataListViewAddShortcut,\n dataListViewIcon,\n dataListViewLabel,\n getDataListViewRenderKind,\n type DataListViewRenderKind,\n}\n","/**\n * Connects ListPageTemplate “View → Edit” to a surface that hosts TablePropertiesDrawer\n * (PlacementsTable, TeamTable, ComplianceTable, …). Import from `@/components/table-properties`\n * or use here — see `createListPageEditViewHandler`.\n *\n * View **labels** for tabs and Properties are centralized in `@/lib/data-list-view`\n * (`DATA_LIST_VIEW_TILES`, `dataListViewLabel`, `dataListViewIcon`).\n */\n\nimport * as React from \"react\"\n\nimport { dataListViewIcon, type DataListViewType } from \"./data-list-view\"\nimport { isDataListSurfaceViewType } from \"./data-list-view-registry\"\n\nexport { isDataListSurfaceViewType }\n\n/** Minimal ref API any list/table surface exposes for the shared Properties drawer. */\nexport interface OpenTablePropertiesHandle {\n openPropertiesDrawer: () => void\n}\n\nexport interface CreateListPageEditViewHandlerOptions {\n /** Delay before opening Properties after switching to table (ms). Default 160. */\n switchDelayMs?: number\n}\n\n/**\n * Returns `ListPageTemplate`’s `onEditView` handler: optionally coerces the tab to `table`\n * when the view type is unknown, then calls `ref.current.openPropertiesDrawer()`.\n */\nexport function createListPageEditViewHandler(\n tableRef: React.RefObject<OpenTablePropertiesHandle | null>,\n options?: CreateListPageEditViewHandlerOptions\n) {\n const delay = options?.switchDelayMs ?? 160\n return (\n tab: { viewType: string },\n { updateTab }: { updateTab: (patch: { viewType?: DataListViewType; icon?: string }) => void }\n ) => {\n const mustSwitchToTableSurface = !isDataListSurfaceViewType(tab.viewType)\n if (mustSwitchToTableSurface) {\n updateTab({ viewType: \"table\", icon: dataListViewIcon(\"table\") })\n }\n window.setTimeout(() => {\n tableRef.current?.openPropertiesDrawer()\n }, mustSwitchToTableSurface ? delay : 0)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/lib/data-list-view.ts","../../src/lib/data-list-view-surface.ts","../../src/lib/data-list-view-registry.ts","../../src/lib/list-page-table-properties.ts"],"names":[],"mappings":"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","file":"list-page-table-properties.js","sourcesContent":["/**\n * Data list “view type” — shared by Properties drawer, ListPageTemplate tabs, and client state.\n *\n * **Single source of truth** for view labels/icons: use `DATA_LIST_VIEW_TILES` and\n * `dataListViewLabel` / `dataListViewIcon` on every page so Table / List / Board / Dashboard\n * stay consistent and stay wired to the same `useTableState` dataset (see `docs/data-views-pattern.md`).\n */\nexport type DataListViewType =\n | \"table\"\n | \"list\"\n | \"board\"\n | \"dashboard\"\n | \"calendar\"\n | \"folder\"\n | \"panel\"\n | \"tree-panel\"\n\nexport const DATA_LIST_VIEW_TILES: readonly {\n value: DataListViewType\n label: string\n icon: string\n}[] = [\n { value: \"table\", icon: \"fa-table\", label: \"Table view\" },\n { value: \"list\", icon: \"fa-list\", label: \"List view\" },\n { value: \"board\", icon: \"fa-table-columns\", label: \"Board view\" },\n { value: \"dashboard\", icon: \"fa-chart-mixed\", label: \"Dashboard view\" },\n { value: \"calendar\", icon: \"fa-calendar-days\", label: \"Calendar view\" },\n { value: \"folder\", icon: \"fa-grid-2\", label: \"Folder view\" },\n { value: \"panel\", icon: \"fa-sidebar\", label: \"List & details\" },\n { value: \"tree-panel\", icon: \"fa-sitemap\", label: \"Tree & details\" },\n]\n\n/** User-facing name for tabs, Properties summary rows, and tooltips (not entity-specific). */\nexport function dataListViewLabel(view: DataListViewType): string {\n return DATA_LIST_VIEW_TILES.find(t => t.value === view)?.label ?? view\n}\n\n/** Font Awesome icon class (no prefix) for tab / toolbar state when view changes. */\nexport function dataListViewIcon(view: DataListViewType): string {\n return DATA_LIST_VIEW_TILES.find(t => t.value === view)?.icon ?? \"fa-table\"\n}\n\n/** Add-view menu hint + `<Shortcut>` keys (1–9). Skipped in inputs via `useShortcut`. */\nexport function dataListViewAddShortcut(index: number): string | undefined {\n if (index < 0 || index > 8) return undefined\n return String(index + 1)\n}\n","/**\n * Maps `DataListViewType` to the UI surface pattern for list pages.\n *\n * **Data:** One `useTableState(fullRows, columns, …)` per tab; **filtered/sorted rows**\n * (`tableState.rows`) are the single source of truth for List, Board, and Dashboard.\n * Table view renders the same state via `DataTable`.\n *\n * | View | Surface |\n * |------------|---------|\n * | `table` | `DataTable` |\n * | `list` | `DataTableToolbar` + list layout |\n * | `board` | `DataTableToolbar` + board / kanban |\n * | `dashboard`| `DataTableToolbar` + KPI (`KeyMetrics`) + optional charts (`ChartCard`, Recharts, etc.) |\n * | `calendar` | `DataTableToolbar` + `ListPageCalendarView` (month grid + day detail) |\n * | `folder` | `DataTableToolbar` + icon grid (macOS-Finder-style) |\n * | `panel` | `DataTableToolbar` + resizable split (list / tree column + detail inspector) |\n */\n\nimport type { DataListViewType } from \"./data-list-view\"\n\nexport { showsListPageHubMetricsStrip } from \"./data-list-view-registry\"\n\n/** What to render for the active view tab (routing / branching). */\nexport type DataListViewRenderKind =\n | \"data-table\"\n | \"list-with-toolbar\"\n | \"board-with-toolbar\"\n | \"dashboard-with-toolbar\"\n | \"calendar-with-toolbar\"\n | \"folder-with-toolbar\"\n | \"panel-with-toolbar\"\n | \"tree-panel-with-toolbar\"\n\n/**\n * Stable classification for switch/if chains. **Every** `DataListViewType` maps to exactly one kind.\n * Use this so `dashboard` is never mistaken for `board` (a common bug when only `list` is special-cased).\n */\nexport function getDataListViewRenderKind(view: DataListViewType): DataListViewRenderKind {\n switch (view) {\n case \"table\":\n return \"data-table\"\n case \"list\":\n return \"list-with-toolbar\"\n case \"board\":\n return \"board-with-toolbar\"\n case \"dashboard\":\n return \"dashboard-with-toolbar\"\n case \"calendar\":\n return \"calendar-with-toolbar\"\n case \"folder\":\n return \"folder-with-toolbar\"\n case \"panel\":\n return \"panel-with-toolbar\"\n case \"tree-panel\":\n return \"tree-panel-with-toolbar\"\n default: {\n const _x: never = view\n return _x\n }\n }\n}\n\nexport function usesDataTableComponent(view: DataListViewType): boolean {\n return view === \"table\"\n}\n\n/** KPI band + optional charts — not the kanban board. */\nexport function usesDashboardSurface(view: DataListViewType): boolean {\n return view === \"dashboard\"\n}\n\n/** Shared toolbar (search, filters, properties); body differs by view. */\nexport function usesToolbarWithFilteredRows(view: DataListViewType): boolean {\n return (\n view === \"list\" ||\n view === \"board\" ||\n view === \"dashboard\" ||\n view === \"calendar\" ||\n view === \"folder\" ||\n view === \"panel\" ||\n view === \"tree-panel\"\n )\n}\n","/**\n * Central registry for list-page view types — labels, render kinds, and hub chrome rules.\n *\n * **Add a new view once here** (plus a body in `components/data-views/`). Hubs declare\n * `supportedViewTypes` on `ListPageTemplate`; table components branch with\n * `getDataListViewRenderKind` + `ListPageConnectedViewBody` (never a dashboard fallback).\n *\n * @see `docs/data-views-pattern.md` — \"View registry and connected bodies\"\n * @see `.cursor/rules/exxat-hub-supported-views.mdc` — default hubs use **`FULL_HUB_SUPPORTED_VIEWS`** (seven views)\n */\n\nimport {\n DATA_LIST_VIEW_TILES,\n type DataListViewType,\n dataListViewAddShortcut,\n dataListViewIcon,\n dataListViewLabel,\n} from \"./data-list-view\"\nimport {\n getDataListViewRenderKind,\n type DataListViewRenderKind,\n} from \"./data-list-view-surface\"\n\nexport interface DataListViewDefinition {\n value: DataListViewType\n label: string\n icon: string\n renderKind: DataListViewRenderKind\n /** `ListPageTemplate` metrics slot above the views toolbar. */\n hubMetricsStrip: boolean\n}\n\nconst DEFINITIONS: DataListViewDefinition[] = DATA_LIST_VIEW_TILES.map(tile => {\n const renderKind = getDataListViewRenderKind(tile.value)\n const hubMetricsStrip = renderKind !== \"calendar-with-toolbar\" && renderKind !== \"dashboard-with-toolbar\"\n return {\n value: tile.value,\n label: tile.label,\n icon: tile.icon,\n renderKind,\n hubMetricsStrip,\n }\n})\n\nconst BY_VALUE = new Map<DataListViewType, DataListViewDefinition>(\n DEFINITIONS.map(d => [d.value, d]),\n)\n\nexport const DATA_LIST_VIEW_REGISTRY: readonly DataListViewDefinition[] = DEFINITIONS\n\n/**\n * Default view allowlist for **primary list hubs** (Placements / Team / Students-style pages).\n * Pair with `ListPageTemplate` + `HubTable` when the hub implements table, list, board,\n * and dashboard renderers. Omit `supportedViewTypes` on both components to use this default.\n */\nexport const PRIMARY_HUB_SUPPORTED_VIEWS = [\n \"table\",\n \"list\",\n \"board\",\n \"dashboard\",\n] as const satisfies readonly DataListViewType[]\n\n/**\n * Default allowlist for **list-page hubs** in this design system (Library / Column types /\n * Tokens, etc.). Matches the All questions hub: table, list, board, dashboard, folder,\n * panel, and tree-panel. Pair with renderers for each kind on `HubTable` + `ListPageTemplate`.\n */\nexport const FULL_HUB_SUPPORTED_VIEWS = [\n \"table\",\n \"list\",\n \"board\",\n \"dashboard\",\n \"folder\",\n \"panel\",\n \"tree-panel\",\n] as const satisfies readonly DataListViewType[]\n\n/** Every registered view type (includes folder, panel, calendar, tree-panel). */\nexport const ALL_DATA_LIST_VIEW_TYPES = DATA_LIST_VIEW_REGISTRY.map(d => d.value)\n\nexport function dataListViewDefinition(view: DataListViewType): DataListViewDefinition {\n const def = BY_VALUE.get(view)\n if (!def) {\n throw new Error(`Unknown DataListViewType: ${view}`)\n }\n return def\n}\n\n/** `ListPageTemplate` hub KPI strip — false for calendar and dashboard (inline KPIs). */\nexport function showsListPageHubMetricsStrip(view: DataListViewType): boolean {\n return dataListViewDefinition(view).hubMetricsStrip\n}\n\n/** Tiles for Add view + Properties when a hub only supports a subset of views. */\nexport function dataListViewTilesForHub(supported: readonly DataListViewType[]) {\n const allowed = new Set(supported)\n return DATA_LIST_VIEW_REGISTRY.filter(d => allowed.has(d.value)).map(d => ({\n type: d.value,\n label: d.label,\n icon: d.icon,\n }))\n}\n\n/** `SelectionTileGrid` options for Properties when a hub supports a subset of views. */\nexport function dataListViewSelectionTilesForHub(supported: readonly DataListViewType[]) {\n return dataListViewTilesForHub(supported).map(t => ({\n value: t.type,\n label: t.label,\n icon: t.icon,\n }))\n}\n\n/** View types that expose Table Properties (all registered `DataListViewType` values). */\nexport const DATA_LIST_SURFACE_VIEW_TYPES: ReadonlySet<DataListViewType> = new Set(\n DATA_LIST_VIEW_REGISTRY.map(d => d.value),\n)\n\nexport function isDataListSurfaceViewType(viewType: string): viewType is DataListViewType {\n return DATA_LIST_SURFACE_VIEW_TYPES.has(viewType as DataListViewType)\n}\n\nexport function isDataListViewTypeSupported(\n view: DataListViewType,\n supported: readonly DataListViewType[],\n): boolean {\n return supported.includes(view)\n}\n\nexport {\n dataListViewAddShortcut,\n dataListViewIcon,\n dataListViewLabel,\n getDataListViewRenderKind,\n type DataListViewRenderKind,\n}\n","/**\n * Connects ListPageTemplate “View → Edit” to a surface that hosts TablePropertiesDrawer\n * (PlacementsTable, TeamTable, ComplianceTable, …). Import from `@/components/table-properties`\n * or use here — see `createListPageEditViewHandler`.\n *\n * View **labels** for tabs and Properties are centralized in `@/lib/data-list-view`\n * (`DATA_LIST_VIEW_TILES`, `dataListViewLabel`, `dataListViewIcon`).\n */\n\nimport * as React from \"react\"\n\nimport { dataListViewIcon, type DataListViewType } from \"./data-list-view\"\nimport { isDataListSurfaceViewType } from \"./data-list-view-registry\"\n\nexport { isDataListSurfaceViewType }\n\n/** Minimal ref API any list/table surface exposes for the shared Properties drawer. */\nexport interface OpenTablePropertiesHandle {\n openPropertiesDrawer: () => void\n}\n\nexport interface CreateListPageEditViewHandlerOptions {\n /** Delay before opening Properties after switching to table (ms). Default 160. */\n switchDelayMs?: number\n}\n\n/**\n * Returns `ListPageTemplate`’s `onEditView` handler: optionally coerces the tab to `table`\n * when the view type is unknown, then calls `ref.current.openPropertiesDrawer()`.\n */\nexport function createListPageEditViewHandler(\n tableRef: React.RefObject<OpenTablePropertiesHandle | null>,\n options?: CreateListPageEditViewHandlerOptions\n) {\n const delay = options?.switchDelayMs ?? 160\n return (\n tab: { viewType: string },\n { updateTab }: { updateTab: (patch: { viewType?: DataListViewType; icon?: string }) => void }\n ) => {\n const mustSwitchToTableSurface = !isDataListSurfaceViewType(tab.viewType)\n if (mustSwitchToTableSurface) {\n updateTab({ viewType: \"table\", icon: dataListViewIcon(\"table\") })\n }\n window.setTimeout(() => {\n tableRef.current?.openPropertiesDrawer()\n }, mustSwitchToTableSurface ? delay : 0)\n }\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sidebar / secondary-nav active-state helpers.
|
|
3
|
+
*
|
|
4
|
+
* Only one nav target should be "selected" at a time. Prefix matching
|
|
5
|
+
* (`/dashboard` active on `/dashboard/students`) is correct only when no
|
|
6
|
+
* more-specific nav href also matches the current pathname.
|
|
7
|
+
*/
|
|
8
|
+
declare function normalizePathname(pathname: string): string;
|
|
9
|
+
/** Path segment before `#` (if any). */
|
|
10
|
+
declare function navUrlPath(url: string): string;
|
|
11
|
+
/** Hash fragment after `#`, or `null` when the href has no fragment. */
|
|
12
|
+
declare function navUrlFragment(url: string): string | null;
|
|
13
|
+
declare function normalizedLocationHash(locationHash: string): string;
|
|
14
|
+
/** Path → hash fragments claimed by *another* nav item at the same path. */
|
|
15
|
+
declare function buildNavHashClaims(urls: readonly string[]): ReadonlyMap<string, ReadonlySet<string>>;
|
|
16
|
+
/**
|
|
17
|
+
* Longest matching nav path for `pathname` among `candidateUrls` (path + prefix rules).
|
|
18
|
+
* Returns the winning href string, or `null`.
|
|
19
|
+
*/
|
|
20
|
+
declare function resolveActiveNavHref(pathname: string, candidateUrls: readonly string[], options?: {
|
|
21
|
+
locationHash?: string;
|
|
22
|
+
hashClaimsByPath?: ReadonlyMap<string, ReadonlySet<string>>;
|
|
23
|
+
}): string | null;
|
|
24
|
+
/** True when `url` is the single best match for `pathname` among all nav hrefs. */
|
|
25
|
+
declare function isNavHrefActive(pathname: string, url: string, allNavUrls: readonly string[], options?: {
|
|
26
|
+
locationHash?: string;
|
|
27
|
+
hashClaimsByPath?: ReadonlyMap<string, ReadonlySet<string>>;
|
|
28
|
+
}): boolean;
|
|
29
|
+
/** Collect every `url` from a nav tree (primary rows + children). */
|
|
30
|
+
declare function collectNavUrls(items: ReadonlyArray<{
|
|
31
|
+
url?: string;
|
|
32
|
+
children?: ReadonlyArray<{
|
|
33
|
+
url?: string;
|
|
34
|
+
children?: unknown;
|
|
35
|
+
}>;
|
|
36
|
+
}>): string[];
|
|
37
|
+
|
|
38
|
+
export { buildNavHashClaims, collectNavUrls, isNavHrefActive, navUrlFragment, navUrlPath, normalizePathname, normalizedLocationHash, resolveActiveNavHref };
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// src/lib/nav-active.ts
|
|
2
|
+
function normalizePathname(pathname) {
|
|
3
|
+
if (pathname.length > 1 && pathname.endsWith("/")) return pathname.slice(0, -1);
|
|
4
|
+
return pathname;
|
|
5
|
+
}
|
|
6
|
+
function navUrlPath(url) {
|
|
7
|
+
const hash = url.indexOf("#");
|
|
8
|
+
return hash >= 0 ? url.slice(0, hash) : url;
|
|
9
|
+
}
|
|
10
|
+
function navUrlFragment(url) {
|
|
11
|
+
const i = url.indexOf("#");
|
|
12
|
+
return i >= 0 ? url.slice(i + 1) : null;
|
|
13
|
+
}
|
|
14
|
+
function normalizedLocationHash(locationHash) {
|
|
15
|
+
if (!locationHash) return "";
|
|
16
|
+
return locationHash.startsWith("#") ? locationHash.slice(1) : locationHash;
|
|
17
|
+
}
|
|
18
|
+
function buildNavHashClaims(urls) {
|
|
19
|
+
const map = /* @__PURE__ */ new Map();
|
|
20
|
+
for (const url of urls) {
|
|
21
|
+
const p = navUrlPath(url);
|
|
22
|
+
const f = navUrlFragment(url);
|
|
23
|
+
if (!p || f === null) continue;
|
|
24
|
+
let set = map.get(p);
|
|
25
|
+
if (!set) {
|
|
26
|
+
set = /* @__PURE__ */ new Set();
|
|
27
|
+
map.set(p, set);
|
|
28
|
+
}
|
|
29
|
+
set.add(f);
|
|
30
|
+
}
|
|
31
|
+
return map;
|
|
32
|
+
}
|
|
33
|
+
function navHasMoreSpecificHashMatch(pathname, locationHash, hashClaimsByPath) {
|
|
34
|
+
const claims = hashClaimsByPath.get(pathname);
|
|
35
|
+
if (!claims) return false;
|
|
36
|
+
const h = normalizedLocationHash(locationHash);
|
|
37
|
+
if (h === "") return false;
|
|
38
|
+
return claims.has(h);
|
|
39
|
+
}
|
|
40
|
+
function pathnameMatchesNavPath(pathname, pathOnly, locationHash, hashClaimsByPath) {
|
|
41
|
+
const norm = normalizePathname(pathname);
|
|
42
|
+
const h = normalizedLocationHash(locationHash);
|
|
43
|
+
if (pathOnly === "/") {
|
|
44
|
+
if (norm !== "/" || h !== "") return false;
|
|
45
|
+
return !navHasMoreSpecificHashMatch("/", locationHash, hashClaimsByPath);
|
|
46
|
+
}
|
|
47
|
+
if (norm === pathOnly) {
|
|
48
|
+
return !navHasMoreSpecificHashMatch(pathOnly, locationHash, hashClaimsByPath);
|
|
49
|
+
}
|
|
50
|
+
if (pathOnly === "/library") {
|
|
51
|
+
return norm.startsWith("/library/");
|
|
52
|
+
}
|
|
53
|
+
if (pathOnly.startsWith("/library/")) {
|
|
54
|
+
return norm === pathOnly;
|
|
55
|
+
}
|
|
56
|
+
return norm.startsWith(`${pathOnly}/`);
|
|
57
|
+
}
|
|
58
|
+
function resolveActiveNavHref(pathname, candidateUrls, options) {
|
|
59
|
+
const norm = normalizePathname(pathname);
|
|
60
|
+
const hashClaims = options?.hashClaimsByPath ?? buildNavHashClaims(candidateUrls);
|
|
61
|
+
const locationHash = options?.locationHash ?? "";
|
|
62
|
+
let bestHref = null;
|
|
63
|
+
let bestLen = -1;
|
|
64
|
+
for (const url of candidateUrls) {
|
|
65
|
+
const pathOnly = navUrlPath(url);
|
|
66
|
+
const frag = navUrlFragment(url);
|
|
67
|
+
if (!pathOnly || pathOnly === "#") continue;
|
|
68
|
+
let matches = false;
|
|
69
|
+
if (frag !== null) {
|
|
70
|
+
const h = normalizedLocationHash(locationHash);
|
|
71
|
+
if (pathOnly === "/") matches = norm === "/" && h === frag;
|
|
72
|
+
else if (pathOnly === "/library") matches = norm.startsWith("/library/") && h === frag;
|
|
73
|
+
else if (pathOnly.startsWith("/library/")) matches = norm === pathOnly && h === frag;
|
|
74
|
+
else matches = norm === pathOnly && h === frag;
|
|
75
|
+
} else {
|
|
76
|
+
matches = pathnameMatchesNavPath(norm, pathOnly, locationHash, hashClaims);
|
|
77
|
+
}
|
|
78
|
+
if (matches && pathOnly.length > bestLen) {
|
|
79
|
+
bestHref = url;
|
|
80
|
+
bestLen = pathOnly.length;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return bestHref;
|
|
84
|
+
}
|
|
85
|
+
function isNavHrefActive(pathname, url, allNavUrls, options) {
|
|
86
|
+
const active = resolveActiveNavHref(pathname, allNavUrls, options);
|
|
87
|
+
if (!active) return false;
|
|
88
|
+
return navUrlPath(active) === navUrlPath(url) && navUrlFragment(active) === navUrlFragment(url);
|
|
89
|
+
}
|
|
90
|
+
function collectNavUrls(items) {
|
|
91
|
+
const out = [];
|
|
92
|
+
const walk = (list) => {
|
|
93
|
+
for (const it of list) {
|
|
94
|
+
if (typeof it.url === "string" && it.url.length > 0 && it.url !== "#") out.push(it.url);
|
|
95
|
+
if (Array.isArray(it.children)) walk(it.children);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
walk(items);
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export { buildNavHashClaims, collectNavUrls, isNavHrefActive, navUrlFragment, navUrlPath, normalizePathname, normalizedLocationHash, resolveActiveNavHref };
|
|
103
|
+
//# sourceMappingURL=nav-active.js.map
|
|
104
|
+
//# sourceMappingURL=nav-active.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/nav-active.ts"],"names":[],"mappings":"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","file":"nav-active.js","sourcesContent":["/**\n * Sidebar / secondary-nav active-state helpers.\n *\n * Only one nav target should be \"selected\" at a time. Prefix matching\n * (`/dashboard` active on `/dashboard/students`) is correct only when no\n * more-specific nav href also matches the current pathname.\n */\n\nexport function normalizePathname(pathname: string): string {\n if (pathname.length > 1 && pathname.endsWith(\"/\")) return pathname.slice(0, -1)\n return pathname\n}\n\n/** Path segment before `#` (if any). */\nexport function navUrlPath(url: string): string {\n const hash = url.indexOf(\"#\")\n return hash >= 0 ? url.slice(0, hash) : url\n}\n\n/** Hash fragment after `#`, or `null` when the href has no fragment. */\nexport function navUrlFragment(url: string): string | null {\n const i = url.indexOf(\"#\")\n return i >= 0 ? url.slice(i + 1) : null\n}\n\nexport function normalizedLocationHash(locationHash: string): string {\n if (!locationHash) return \"\"\n return locationHash.startsWith(\"#\") ? locationHash.slice(1) : locationHash\n}\n\n/** Path → hash fragments claimed by *another* nav item at the same path. */\nexport function buildNavHashClaims(\n urls: readonly string[],\n): ReadonlyMap<string, ReadonlySet<string>> {\n const map = new Map<string, Set<string>>()\n for (const url of urls) {\n const p = navUrlPath(url)\n const f = navUrlFragment(url)\n if (!p || f === null) continue\n let set = map.get(p)\n if (!set) {\n set = new Set<string>()\n map.set(p, set)\n }\n set.add(f)\n }\n return map\n}\n\nfunction navHasMoreSpecificHashMatch(\n pathname: string,\n locationHash: string,\n hashClaimsByPath: ReadonlyMap<string, ReadonlySet<string>>,\n): boolean {\n const claims = hashClaimsByPath.get(pathname)\n if (!claims) return false\n const h = normalizedLocationHash(locationHash)\n if (h === \"\") return false\n return claims.has(h)\n}\n\nfunction pathnameMatchesNavPath(\n pathname: string,\n pathOnly: string,\n locationHash: string,\n hashClaimsByPath: ReadonlyMap<string, ReadonlySet<string>>,\n): boolean {\n const norm = normalizePathname(pathname)\n const h = normalizedLocationHash(locationHash)\n\n if (pathOnly === \"/\") {\n if (norm !== \"/\" || h !== \"\") return false\n return !navHasMoreSpecificHashMatch(\"/\", locationHash, hashClaimsByPath)\n }\n\n if (norm === pathOnly) {\n return !navHasMoreSpecificHashMatch(pathOnly, locationHash, hashClaimsByPath)\n }\n\n if (pathOnly === \"/library\") {\n return norm.startsWith(\"/library/\")\n }\n if (pathOnly.startsWith(\"/library/\")) {\n return norm === pathOnly\n }\n\n return norm.startsWith(`${pathOnly}/`)\n}\n\n/**\n * Longest matching nav path for `pathname` among `candidateUrls` (path + prefix rules).\n * Returns the winning href string, or `null`.\n */\nexport function resolveActiveNavHref(\n pathname: string,\n candidateUrls: readonly string[],\n options?: {\n locationHash?: string\n hashClaimsByPath?: ReadonlyMap<string, ReadonlySet<string>>\n },\n): string | null {\n const norm = normalizePathname(pathname)\n const hashClaims = options?.hashClaimsByPath ?? buildNavHashClaims(candidateUrls)\n const locationHash = options?.locationHash ?? \"\"\n\n let bestHref: string | null = null\n let bestLen = -1\n\n for (const url of candidateUrls) {\n const pathOnly = navUrlPath(url)\n const frag = navUrlFragment(url)\n if (!pathOnly || pathOnly === \"#\") continue\n\n let matches = false\n if (frag !== null) {\n const h = normalizedLocationHash(locationHash)\n if (pathOnly === \"/\") matches = norm === \"/\" && h === frag\n else if (pathOnly === \"/library\") matches = norm.startsWith(\"/library/\") && h === frag\n else if (pathOnly.startsWith(\"/library/\")) matches = norm === pathOnly && h === frag\n else matches = norm === pathOnly && h === frag\n } else {\n matches = pathnameMatchesNavPath(norm, pathOnly, locationHash, hashClaims)\n }\n\n if (matches && pathOnly.length > bestLen) {\n bestHref = url\n bestLen = pathOnly.length\n }\n }\n\n return bestHref\n}\n\n/** True when `url` is the single best match for `pathname` among all nav hrefs. */\nexport function isNavHrefActive(\n pathname: string,\n url: string,\n allNavUrls: readonly string[],\n options?: {\n locationHash?: string\n hashClaimsByPath?: ReadonlyMap<string, ReadonlySet<string>>\n },\n): boolean {\n const active = resolveActiveNavHref(pathname, allNavUrls, options)\n if (!active) return false\n return navUrlPath(active) === navUrlPath(url) && navUrlFragment(active) === navUrlFragment(url)\n}\n\n/** Collect every `url` from a nav tree (primary rows + children). */\nexport function collectNavUrls(\n items: ReadonlyArray<{ url?: string; children?: ReadonlyArray<{ url?: string; children?: unknown }> }>,\n): string[] {\n const out: string[] = []\n const walk = (list: ReadonlyArray<{ url?: string; children?: ReadonlyArray<{ url?: string; children?: unknown }> }>) => {\n for (const it of list) {\n if (typeof it.url === \"string\" && it.url.length > 0 && it.url !== \"#\") out.push(it.url)\n if (Array.isArray(it.children)) walk(it.children)\n }\n }\n walk(items)\n return out\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exxatdesignux/ui",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"description": "Exxat shared design system (components, hooks, tokens). Monorepo setup: clone repo then pnpm bootstrap at workspace root — see github.com/ExxatDesign/Exxat-DS-Workspace README.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "Exxat Design",
|
|
@@ -732,13 +732,15 @@ function useBulkBarFixedToTableScrollEl(
|
|
|
732
732
|
const scheduled = rafThrottle(apply)
|
|
733
733
|
const ro = new ResizeObserver(scheduled)
|
|
734
734
|
ro.observe(el)
|
|
735
|
+
el.addEventListener("scroll", scheduled, { passive: true })
|
|
735
736
|
window.addEventListener("resize", scheduled, { passive: true })
|
|
736
|
-
window.addEventListener("scroll", scheduled, { passive: true
|
|
737
|
+
window.addEventListener("scroll", scheduled, { passive: true })
|
|
737
738
|
return () => {
|
|
738
739
|
scheduled.cancel()
|
|
739
740
|
ro.disconnect()
|
|
741
|
+
el.removeEventListener("scroll", scheduled)
|
|
740
742
|
window.removeEventListener("resize", scheduled)
|
|
741
|
-
window.removeEventListener("scroll", scheduled
|
|
743
|
+
window.removeEventListener("scroll", scheduled)
|
|
742
744
|
}
|
|
743
745
|
}, [active, fullWidth, scrollRef])
|
|
744
746
|
return style
|
|
@@ -920,15 +922,17 @@ function DataTableInner<TData extends Record<string, unknown>>({
|
|
|
920
922
|
}
|
|
921
923
|
|
|
922
924
|
update()
|
|
923
|
-
// rAF-coalesce: capture-phase scroll fires for every ancestor (sidebar,
|
|
924
|
-
// dashboard panels, anchored sheets), so a single getBoundingClientRect
|
|
925
|
-
// per frame is more than enough to keep the sticky header aligned.
|
|
926
925
|
const scheduled = rafThrottle(update)
|
|
927
|
-
|
|
926
|
+
// Listen on the table scrollport + window resize only. Avoid capture-phase
|
|
927
|
+
// `window` scroll — it fires for every nested scroll container (sidebar,
|
|
928
|
+
// secondary panel, sheets) and kept the main thread busy while the app looked idle.
|
|
929
|
+
wrapEl.addEventListener("scroll", scheduled, { passive: true })
|
|
930
|
+
window.addEventListener("scroll", scheduled, { passive: true })
|
|
928
931
|
window.addEventListener("resize", scheduled, { passive: true })
|
|
929
932
|
return () => {
|
|
930
933
|
scheduled.cancel()
|
|
931
|
-
|
|
934
|
+
wrapEl.removeEventListener("scroll", scheduled)
|
|
935
|
+
window.removeEventListener("scroll", scheduled)
|
|
932
936
|
window.removeEventListener("resize", scheduled)
|
|
933
937
|
}
|
|
934
938
|
}, [showColumnHeaders, rows.length, displayCols.length])
|
|
@@ -1440,11 +1444,11 @@ function DataTableInner<TData extends Record<string, unknown>>({
|
|
|
1440
1444
|
const cs = cellStyle(col.key)
|
|
1441
1445
|
|
|
1442
1446
|
const tdBase = cn(
|
|
1443
|
-
`px-3 ${rowPy} align-middle`,
|
|
1447
|
+
`px-3 ${rowPy} align-middle max-w-0`,
|
|
1444
1448
|
showGridlines && !isEdgePin && "border-e border-border last:border-e-0",
|
|
1445
1449
|
"border-b border-border group-last/row:border-b-0",
|
|
1446
1450
|
isPinned && [
|
|
1447
|
-
"z-20 pinned-cell",
|
|
1451
|
+
"z-20 pinned-cell relative",
|
|
1448
1452
|
"bg-dt-row-bg",
|
|
1449
1453
|
"group-data-[state=selected]/row:bg-dt-row-selected",
|
|
1450
1454
|
"group-hover/row:bg-dt-row-hover",
|
|
@@ -1508,11 +1512,13 @@ function DataTableInner<TData extends Record<string, unknown>>({
|
|
|
1508
1512
|
)}
|
|
1509
1513
|
style={tdStyle}
|
|
1510
1514
|
>
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1515
|
+
<div className="min-w-0 overflow-hidden">
|
|
1516
|
+
{col.cell(row, {
|
|
1517
|
+
rowIndex,
|
|
1518
|
+
selected: isSelected,
|
|
1519
|
+
onSelect: checked => checked ? setSelected(prev => new Set([...prev, rowId])) : toggleRow(rowId),
|
|
1520
|
+
})}
|
|
1521
|
+
</div>
|
|
1516
1522
|
</td>
|
|
1517
1523
|
)
|
|
1518
1524
|
}
|
|
@@ -1521,9 +1527,11 @@ function DataTableInner<TData extends Record<string, unknown>>({
|
|
|
1521
1527
|
const rawVal = String(row[col.key] ?? "")
|
|
1522
1528
|
return (
|
|
1523
1529
|
<td key={col.key} className={cn(tdBase, "text-sm text-foreground/80")} style={tdStyle}>
|
|
1524
|
-
<
|
|
1525
|
-
{rawVal}
|
|
1526
|
-
|
|
1530
|
+
<div className="min-w-0 overflow-hidden">
|
|
1531
|
+
<span className={wrap ? "whitespace-normal" : "block truncate"} title={!wrap ? rawVal : undefined}>
|
|
1532
|
+
{rawVal}
|
|
1533
|
+
</span>
|
|
1534
|
+
</div>
|
|
1527
1535
|
</td>
|
|
1528
1536
|
)
|
|
1529
1537
|
})}
|
|
@@ -35,6 +35,7 @@ import { useTableState } from "../data-table/use-table-state"
|
|
|
35
35
|
import type { DataListViewType } from "../../lib/data-list-view"
|
|
36
36
|
import {
|
|
37
37
|
getDataListViewRenderKind,
|
|
38
|
+
FULL_HUB_SUPPORTED_VIEWS,
|
|
38
39
|
type DataListViewRenderKind,
|
|
39
40
|
} from "../../lib/data-list-view-registry"
|
|
40
41
|
import {
|
|
@@ -157,8 +158,12 @@ export interface HubTableProps<TRow extends Record<string, unknown>> {
|
|
|
157
158
|
/** Active view from the `ListPageTemplate` tab. */
|
|
158
159
|
view: DataListViewType
|
|
159
160
|
onViewChange?: (v: DataListViewType) => void
|
|
160
|
-
/**
|
|
161
|
-
|
|
161
|
+
/**
|
|
162
|
+
* Allowlist passed to `TablePropertiesDrawerButton` so Properties cannot offer unsupported views.
|
|
163
|
+
* Defaults to {@link FULL_HUB_SUPPORTED_VIEWS} when omitted — keep in sync with
|
|
164
|
+
* `ListPageTemplate.supportedViewTypes` on the same hub.
|
|
165
|
+
*/
|
|
166
|
+
supportedViewTypes?: readonly DataListViewType[]
|
|
162
167
|
/** Used by `ListPageViewNotConfigured` when a supported view has no renderer. */
|
|
163
168
|
hubLabel: string
|
|
164
169
|
/** Shown below "Properties" in the drawer header. */
|
|
@@ -258,7 +263,7 @@ export function HubTable<TRow extends Record<string, unknown>>({
|
|
|
258
263
|
columns,
|
|
259
264
|
view,
|
|
260
265
|
onViewChange,
|
|
261
|
-
supportedViewTypes,
|
|
266
|
+
supportedViewTypes: supportedViewTypesProp,
|
|
262
267
|
hubLabel,
|
|
263
268
|
lifecycleTabLabel,
|
|
264
269
|
searchAriaLabel,
|
|
@@ -294,6 +299,7 @@ export function HubTable<TRow extends Record<string, unknown>>({
|
|
|
294
299
|
boardColumnCountBadgeClassName,
|
|
295
300
|
boardEmptyColumnLabel,
|
|
296
301
|
}: HubTableProps<TRow>) {
|
|
302
|
+
const supportedViewTypes = supportedViewTypesProp ?? FULL_HUB_SUPPORTED_VIEWS
|
|
297
303
|
const filterFields = React.useMemo(() => columnsToFilterFields(columns), [columns])
|
|
298
304
|
const fieldDefinitions = React.useMemo(() => columnsToFieldDefinitions(columns), [columns])
|
|
299
305
|
const resolveColumnLabel = React.useCallback(
|
|
@@ -52,6 +52,7 @@ import {
|
|
|
52
52
|
createListPageEditViewHandler,
|
|
53
53
|
type OpenTablePropertiesHandle,
|
|
54
54
|
} from "../../lib/list-page-table-properties"
|
|
55
|
+
import { FULL_HUB_SUPPORTED_VIEWS } from "../../lib/data-list-view-registry"
|
|
55
56
|
import {
|
|
56
57
|
viewSegmentedToolbarClass,
|
|
57
58
|
viewSegmentedButtonClass,
|
|
@@ -129,7 +130,9 @@ export interface ListPageTemplateProps {
|
|
|
129
130
|
/**
|
|
130
131
|
* Subset of view types the hub actually implements (e.g. List hub omits Dashboard/Folder).
|
|
131
132
|
* When set, the Add view menu and ⌘1–9 shortcuts are filtered so users cannot add a view the
|
|
132
|
-
* hub cannot render.
|
|
133
|
+
* hub cannot render. When omitted, defaults to {@link FULL_HUB_SUPPORTED_VIEWS}
|
|
134
|
+
* (table, list, board, dashboard). Pass an explicit allowlist for specialized hubs
|
|
135
|
+
* (e.g. tokens table-only, library with folder/calendar).
|
|
133
136
|
*
|
|
134
137
|
* Pair with `TablePropertiesDrawerButton.supportedViewTypes` to keep Properties consistent.
|
|
135
138
|
*/
|
|
@@ -195,8 +198,11 @@ export function ListPageTemplate({
|
|
|
195
198
|
// this, a user could ⌘1 → Dashboard into a hub that cannot render it (would show the
|
|
196
199
|
// not-configured empty state). Memoized because it filters on every render path.
|
|
197
200
|
const addableViewTypes = React.useMemo(() => {
|
|
198
|
-
|
|
199
|
-
|
|
201
|
+
const allowlist =
|
|
202
|
+
supportedViewTypes && supportedViewTypes.length > 0
|
|
203
|
+
? supportedViewTypes
|
|
204
|
+
: FULL_HUB_SUPPORTED_VIEWS
|
|
205
|
+
const allowed = new Set(allowlist)
|
|
200
206
|
return VIEW_TYPES.filter(v => allowed.has(v.type))
|
|
201
207
|
}, [supportedViewTypes])
|
|
202
208
|
const controlled =
|
package/src/index.ts
CHANGED
|
@@ -46,6 +46,7 @@ export {
|
|
|
46
46
|
export * from "./lib/list-page-table-properties"
|
|
47
47
|
export * from "./lib/data-list-view"
|
|
48
48
|
export * from "./lib/data-list-view-registry"
|
|
49
|
+
export * from "./lib/nav-active"
|
|
49
50
|
export * from "./lib/data-list-view-surface"
|
|
50
51
|
export * from "./lib/data-list-display-options"
|
|
51
52
|
|