@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.
Files changed (82) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/consumer-extras/cursor-rules/exxat-data-tables.mdc +8 -6
  3. package/consumer-extras/cursor-rules/exxat-ds-agents.mdc +2 -1
  4. package/consumer-extras/cursor-rules/exxat-hub-supported-views.mdc +54 -0
  5. package/consumer-extras/cursor-rules/exxat-nav-single-active.mdc +31 -0
  6. package/consumer-extras/cursor-skills/exxat-ds-skill/SKILL.md +8 -3
  7. package/consumer-extras/cursor-skills/exxat-ds-skill/references/data-table-pattern.md +15 -5
  8. package/consumer-extras/cursor-skills/exxat-token-economy/SKILL.md +11 -4
  9. package/consumer-extras/handbook/HANDBOOK.md +1 -1
  10. package/consumer-extras/handbook/reference-implementations.md +2 -2
  11. package/consumer-extras/patterns/data-views-pattern.md +6 -0
  12. package/consumer-extras/patterns/hub-supported-views-pattern.md +53 -0
  13. package/dist/components/data-table/index.js +13 -9
  14. package/dist/components/data-table/index.js.map +1 -1
  15. package/dist/components/data-table/pagination.js +13 -9
  16. package/dist/components/data-table/pagination.js.map +1 -1
  17. package/dist/components/data-views/hub-table.d.ts +8 -4
  18. package/dist/components/data-views/hub-table.js +25 -10
  19. package/dist/components/data-views/hub-table.js.map +1 -1
  20. package/dist/components/data-views/index.d.ts +1 -1
  21. package/dist/components/data-views/index.js +25 -10
  22. package/dist/components/data-views/index.js.map +1 -1
  23. package/dist/components/data-views/list-page-connected-view-body.d.ts +1 -1
  24. package/dist/components/data-views/list-page-connected-view-body.js +1 -0
  25. package/dist/components/data-views/list-page-connected-view-body.js.map +1 -1
  26. package/dist/components/table-properties/drawer-button.js +1 -0
  27. package/dist/components/table-properties/drawer-button.js.map +1 -1
  28. package/dist/components/table-properties/drawer.js +1 -0
  29. package/dist/components/table-properties/drawer.js.map +1 -1
  30. package/dist/components/table-properties/index.d.ts +1 -1
  31. package/dist/components/table-properties/index.js +1 -0
  32. package/dist/components/table-properties/index.js.map +1 -1
  33. package/dist/components/templates/index.d.ts +1 -1
  34. package/dist/components/templates/index.js +12 -2
  35. package/dist/components/templates/index.js.map +1 -1
  36. package/dist/components/templates/list-page.d.ts +4 -2
  37. package/dist/components/templates/list-page.js +12 -2
  38. package/dist/components/templates/list-page.js.map +1 -1
  39. package/dist/{data-list-view-registry-CyBoBML4.d.ts → data-list-view-registry-BstmlfQ3.d.ts} +16 -1
  40. package/dist/index.d.ts +2 -1
  41. package/dist/index.js +135 -13
  42. package/dist/index.js.map +1 -1
  43. package/dist/lib/data-list-view-registry.d.ts +1 -1
  44. package/dist/lib/data-list-view-registry.js +17 -1
  45. package/dist/lib/data-list-view-registry.js.map +1 -1
  46. package/dist/lib/data-list-view-surface.d.ts +1 -1
  47. package/dist/lib/data-list-view-surface.js +1 -0
  48. package/dist/lib/data-list-view-surface.js.map +1 -1
  49. package/dist/lib/list-page-table-properties.d.ts +1 -1
  50. package/dist/lib/list-page-table-properties.js +1 -0
  51. package/dist/lib/list-page-table-properties.js.map +1 -1
  52. package/dist/lib/nav-active.d.ts +38 -0
  53. package/dist/lib/nav-active.js +104 -0
  54. package/dist/lib/nav-active.js.map +1 -0
  55. package/package.json +1 -1
  56. package/src/components/data-table/index.tsx +25 -17
  57. package/src/components/data-views/hub-table.tsx +9 -3
  58. package/src/components/templates/list-page.tsx +9 -3
  59. package/src/index.ts +1 -0
  60. package/src/lib/data-list-view-registry.ts +31 -0
  61. package/src/lib/nav-active.ts +162 -0
  62. package/template/.claude/skills/exxat-ds-skill/SKILL.md +2 -1
  63. package/template/AGENTS.md +16 -1
  64. package/template/components/columns-client.tsx +3 -2
  65. package/template/components/columns-showcase.tsx +22 -18
  66. package/template/components/exxat-product-logo.tsx +1 -1
  67. package/template/components/library-table.tsx +62 -23
  68. package/template/components/new-library-item-form.tsx +0 -7
  69. package/template/components/product-wordmark.tsx +1 -1
  70. package/template/components/sidebar/app-sidebar.tsx +14 -106
  71. package/template/components/sidebar/secondary-nav.tsx +22 -4
  72. package/template/components/tokens-hub-auxiliary-views.tsx +301 -0
  73. package/template/components/tokens-themes-client.tsx +44 -16
  74. package/template/docs/HANDBOOK.md +1 -1
  75. package/template/docs/data-views-pattern.md +6 -0
  76. package/template/docs/glossary.md +2 -1
  77. package/template/docs/hub-supported-views-pattern.md +53 -0
  78. package/template/docs/reference-implementations.md +2 -2
  79. package/template/lib/full-hub-supported-views.ts +8 -0
  80. package/template/lib/library-supported-views.ts +5 -12
  81. package/template/package.json +1 -0
  82. 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-CyBoBML4.js';
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":"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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 */\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-CyBoBML4.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-BstmlfQ3.js';
@@ -26,6 +26,7 @@ var BY_VALUE = new Map(
26
26
  DEFINITIONS.map((d) => [d.value, d])
27
27
  );
28
28
  var DATA_LIST_VIEW_REGISTRY = DEFINITIONS;
29
+ DATA_LIST_VIEW_REGISTRY.map((d) => d.value);
29
30
  function dataListViewDefinition(view) {
30
31
  const def = BY_VALUE.get(view);
31
32
  if (!def) {
@@ -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-CyBoBML4.js';
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
@@ -55,6 +55,7 @@ new Map(
55
55
  DEFINITIONS.map((d) => [d.value, d])
56
56
  );
57
57
  var DATA_LIST_VIEW_REGISTRY = DEFINITIONS;
58
+ DATA_LIST_VIEW_REGISTRY.map((d) => d.value);
58
59
  var DATA_LIST_SURFACE_VIEW_TYPES = new Set(
59
60
  DATA_LIST_VIEW_REGISTRY.map((d) => d.value)
60
61
  );
@@ -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.2",
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, capture: 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, { capture: true })
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
- window.addEventListener("scroll", scheduled, { passive: true, capture: true })
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
- window.removeEventListener("scroll", scheduled, { capture: true })
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
- {col.cell(row, {
1512
- rowIndex,
1513
- selected: isSelected,
1514
- onSelect: checked => checked ? setSelected(prev => new Set([...prev, rowId])) : toggleRow(rowId),
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
- <span className={wrap ? "whitespace-normal" : "block truncate"} title={!wrap ? rawVal : undefined}>
1525
- {rawVal}
1526
- </span>
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
- /** Allowlist passed to `TablePropertiesDrawerButton` so Properties cannot offer unsupported views. */
161
- supportedViewTypes: readonly DataListViewType[]
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. Defaults to all registered view types (`DATA_LIST_VIEW_TILES`).
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
- if (!supportedViewTypes || supportedViewTypes.length === 0) return VIEW_TYPES
199
- const allowed = new Set(supportedViewTypes)
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