basalt-ui 1.17.0 → 1.19.0

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 (112) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +14 -0
  3. package/agent/rules/basalt-charts.md +12 -0
  4. package/agent/rules/basalt-mantine.md +71 -6
  5. package/agent/rules/basalt-router.md +129 -30
  6. package/agent/rules/basalt-tokens.md +2 -1
  7. package/agent/skills/basalt-design/SKILL.md +2 -2
  8. package/dist/charts/kinds/DualPanel.d.ts +7 -0
  9. package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
  10. package/dist/charts/kinds/DualPanel.js +10 -3
  11. package/dist/charts/primitives/CartesianChart.d.ts +17 -0
  12. package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
  13. package/dist/charts/primitives/CartesianChart.js +5 -2
  14. package/dist/charts/primitives/ChartTooltip.d.ts +6 -1
  15. package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
  16. package/dist/charts/primitives/ChartTooltip.js +2 -1
  17. package/dist/charts/series.d.ts +5 -0
  18. package/dist/charts/series.d.ts.map +1 -1
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +8 -0
  22. package/dist/nav/types.d.ts +110 -0
  23. package/dist/nav/types.d.ts.map +1 -0
  24. package/dist/nav/types.js +0 -0
  25. package/dist/router-tanstack/index.d.ts +9 -2
  26. package/dist/router-tanstack/index.d.ts.map +1 -1
  27. package/dist/router-tanstack/index.js +7 -0
  28. package/dist/router-tanstack/nav.d.ts +163 -0
  29. package/dist/router-tanstack/nav.d.ts.map +1 -0
  30. package/dist/router-tanstack/nav.js +38 -0
  31. package/dist/router-tanstack/nav.test.d.ts +2 -0
  32. package/dist/router-tanstack/nav.test.d.ts.map +1 -0
  33. package/dist/router-tanstack/search-param-store.d.ts +19 -9
  34. package/dist/router-tanstack/search-param-store.d.ts.map +1 -1
  35. package/dist/router-tanstack/use-nav.d.ts +30 -0
  36. package/dist/router-tanstack/use-nav.d.ts.map +1 -0
  37. package/dist/router-tanstack/use-nav.js +71 -0
  38. package/dist/shell/app-breadcrumbs.d.ts +7 -9
  39. package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
  40. package/dist/shell/app-breadcrumbs.js +8 -4
  41. package/dist/shell/app-mobile-nav.d.ts +23 -24
  42. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  43. package/dist/shell/app-mobile-nav.js +290 -68
  44. package/dist/shell/app-mobile-nav.module.css +166 -11
  45. package/dist/shell/app-mobile-nav.module.css.d.ts +11 -2
  46. package/dist/shell/app-mobile-nav.test.d.ts +2 -0
  47. package/dist/shell/app-mobile-nav.test.d.ts.map +1 -0
  48. package/dist/shell/app-sidebar.d.ts +3 -28
  49. package/dist/shell/app-sidebar.d.ts.map +1 -1
  50. package/dist/shell/app-sidebar.js +40 -174
  51. package/dist/shell/app-sidebar.module.css +3 -2
  52. package/dist/shell/app-sidebar.module.css.d.ts +0 -2
  53. package/dist/shell/index.d.ts +22 -65
  54. package/dist/shell/index.d.ts.map +1 -1
  55. package/dist/shell/index.js +38 -76
  56. package/dist/shell/mobile-nav-model.d.ts +41 -0
  57. package/dist/shell/mobile-nav-model.d.ts.map +1 -0
  58. package/dist/shell/mobile-nav-model.js +244 -0
  59. package/dist/shell/mobile-nav-model.test.d.ts +2 -0
  60. package/dist/shell/mobile-nav-model.test.d.ts.map +1 -0
  61. package/dist/shell/nav-count-badge.d.ts +11 -0
  62. package/dist/shell/nav-count-badge.d.ts.map +1 -0
  63. package/dist/shell/nav-count-badge.js +29 -0
  64. package/dist/surfaces.d.ts +1 -1
  65. package/dist/surfaces.js +1 -1
  66. package/dist/theme/index.d.ts.map +1 -1
  67. package/dist/theme/index.js +8 -7
  68. package/dist/theme/nav-link.module.css +4 -2
  69. package/dist/tokens/index.d.ts.map +1 -1
  70. package/dist/tokens/index.js +2 -0
  71. package/dist/tokens/palette.d.ts +15 -0
  72. package/dist/tokens/palette.d.ts.map +1 -1
  73. package/dist/tokens/palette.js +13 -0
  74. package/dist/tokens.css +2 -0
  75. package/llms.txt +3 -3
  76. package/package.json +1 -1
  77. package/src/charts/kinds/DualPanel.test.tsx +81 -1
  78. package/src/charts/kinds/DualPanel.tsx +26 -4
  79. package/src/charts/primitives/CartesianChart.test.tsx +173 -1
  80. package/src/charts/primitives/CartesianChart.tsx +29 -5
  81. package/src/charts/primitives/ChartTooltip.test.tsx +22 -1
  82. package/src/charts/primitives/ChartTooltip.tsx +7 -1
  83. package/src/charts/series.ts +5 -0
  84. package/src/index.ts +15 -5
  85. package/src/nav/types.ts +115 -0
  86. package/src/router-tanstack/index.ts +21 -2
  87. package/src/router-tanstack/nav.test.ts +111 -0
  88. package/src/router-tanstack/nav.ts +205 -0
  89. package/src/router-tanstack/search-param-store.ts +19 -9
  90. package/src/router-tanstack/use-nav.ts +146 -0
  91. package/src/shell/app-breadcrumbs.tsx +21 -19
  92. package/src/shell/app-mobile-nav.module.css +166 -11
  93. package/src/shell/app-mobile-nav.module.css.d.ts +11 -2
  94. package/src/shell/app-mobile-nav.test.tsx +369 -0
  95. package/src/shell/app-mobile-nav.tsx +436 -102
  96. package/src/shell/app-sidebar.module.css +3 -2
  97. package/src/shell/app-sidebar.module.css.d.ts +0 -2
  98. package/src/shell/app-sidebar.test.tsx +6 -10
  99. package/src/shell/app-sidebar.tsx +49 -206
  100. package/src/shell/index.test.tsx +62 -7
  101. package/src/shell/index.tsx +105 -164
  102. package/src/shell/mobile-nav-model.test.ts +296 -0
  103. package/src/shell/mobile-nav-model.ts +389 -0
  104. package/src/shell/nav-count-badge.tsx +39 -0
  105. package/src/surfaces.ts +1 -1
  106. package/src/theme/density-relations.test.ts +19 -1
  107. package/src/theme/index.ts +8 -7
  108. package/src/theme/nav-link.module.css +4 -2
  109. package/src/theme/spacing.test.ts +11 -0
  110. package/src/tokens/density.test.ts +2 -0
  111. package/src/tokens/index.ts +8 -5
  112. package/src/tokens/palette.ts +16 -0
package/dist/index.d.ts CHANGED
@@ -10,7 +10,7 @@ export { createBasaltTheme, baseTheme, cssVariablesResolver } from './theme/inde
10
10
  export type { BasaltFontsConfig, CreateBasaltThemeOptions } from './theme/index.js';
11
11
  export { ThemeToggle, type ThemeToggleProps } from './theme-toggle/index.js';
12
12
  export { MOTION_DURATION, MOTION_SPRING, MOTION_EASE_STANDARD } from './motion/index.js';
13
- export { BasaltShell, NavCountBadge, AppSidebar, SidebarSearch, type SidebarSearchConfig, SidebarAccount, MobileNav, AppBreadcrumbs, type BreadcrumbLinkRenderer, PageHeaderProvider, PageActions, PageActionsOutlet, type SidebarSection, type SidebarItem, type BasaltShellProps, type BrandConfig, type SettingsMenuItem, type AppSidebarProps, type MobileNavItem, type MobileNavSection, type NavLinkRenderer, type MobileNavLinkRenderer, type AccountBadgeTone, type BasaltIdentity, type BasaltRole, type BasaltPlan, type AccountMenuItem, type BasaltAccountState, type BasaltAccountActions, type BasaltAccountProps, } from './shell/index.js';
13
+ export { BasaltShell, NavCountBadge, AppSidebar, SidebarSearch, type SidebarSearchConfig, SidebarAccount, MobileNav, type MobileNavProps, projectMobileNav, type ProjectMobileNavOptions, MOBILE_MAX_TABS_DEFAULT, MOBILE_MENU_MAX_DEFAULT, MOBILE_MORE_KEY, AppBreadcrumbs, PageHeaderProvider, PageActions, PageActionsOutlet, type SidebarSection, type SidebarItem, type NavAnchor, type NavAnchorProps, type NavMobilePlacement, type NavSectionMobile, type MobileNavConfig, type MobileNavGroup, type MobileNavModel, type MobileNavSlot, type MobileNavSurface, type BasaltShellProps, type BrandConfig, type SettingsMenuItem, type AppSidebarProps, type AccountBadgeTone, type BasaltIdentity, type BasaltRole, type BasaltPlan, type AccountMenuItem, type BasaltAccountState, type BasaltAccountActions, type BasaltAccountProps, } from './shell/index.js';
14
14
  export { type BasaltRegister, type Slot, type Series, type SeriesKey, type AsyncState, assertNever, type StandardSchemaV1, } from './register.js';
15
15
  export { createPersistedState, type PersistedStateOptions, useOnlineStatus, readPersistedValue, } from './state.js';
16
16
  export { DeltaBadge, type DeltaBadgeProps, StatCard, type StatCardProps, type StatCardTone, EmptyState, type EmptyStateProps, SettingsSection, type SettingsSectionProps, SettingsRow, type SettingsRowProps, DangerZone, type DangerZoneProps, } from './dashboard/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -12,6 +12,10 @@ import {
12
12
  SidebarSearch,
13
13
  SidebarAccount,
14
14
  MobileNav,
15
+ projectMobileNav,
16
+ MOBILE_MAX_TABS_DEFAULT,
17
+ MOBILE_MENU_MAX_DEFAULT,
18
+ MOBILE_MORE_KEY,
15
19
  AppBreadcrumbs,
16
20
  PageHeaderProvider,
17
21
  PageActions,
@@ -64,6 +68,9 @@ export {
64
68
  DangerZone,
65
69
  DeltaBadge,
66
70
  EmptyState,
71
+ MOBILE_MAX_TABS_DEFAULT,
72
+ MOBILE_MENU_MAX_DEFAULT,
73
+ MOBILE_MORE_KEY,
67
74
  MOTION_DURATION,
68
75
  MOTION_EASE_STANDARD,
69
76
  MOTION_SPRING,
@@ -93,6 +100,7 @@ export {
93
100
  createThreadsStore,
94
101
  cssVariablesResolver,
95
102
  heuristicOutcome,
103
+ projectMobileNav,
96
104
  readPersistedValue,
97
105
  threadPartRenderers,
98
106
  threadsStoreAdapterContract,
@@ -0,0 +1,110 @@
1
+ /**
2
+ * The shared nav vocabulary. TYPE-ONLY BY CONTRACT — never add a value export here.
3
+ * `src/shell/**` (Mantine) and `src/router-tanstack/**` (headless) both `import type` from it,
4
+ * which is erased by tsup, so `dist/router-tanstack/index.js` gains no edge into `shell/` and
5
+ * `scripts/check-dist-layering.mjs` stays green with no config change.
6
+ */
7
+ import type { MouseEvent, ReactNode } from 'react';
8
+ /** Where a destination appears on the mobile bar. `true` ≡ `'tab'`, `false` ≡ `'hidden'`. */
9
+ export type NavMobilePlacement = 'tab' | 'more' | 'hidden';
10
+ /**
11
+ * Props basalt hands a nav anchor. Deliberately a PLAIN FUNCTION TYPE, not `ComponentType`
12
+ * or `ElementType`: both are unions, and a union defeats Mantine's polymorphic `component`
13
+ * inference (`<NavLink component={…}>` then rejects `label`). VERIFIED both ways.
14
+ */
15
+ export type NavAnchorProps = {
16
+ className?: string;
17
+ children?: ReactNode;
18
+ onClick?: (e: MouseEvent<HTMLElement>) => void;
19
+ 'aria-current'?: 'page' | undefined;
20
+ 'aria-label'?: string;
21
+ };
22
+ export type NavAnchor = (props: NavAnchorProps) => ReactNode;
23
+ /** A single nav destination. */
24
+ export type SidebarItem = {
25
+ key: string;
26
+ label: string;
27
+ /** Bar/menu label. Falls back to `label`. Keep ≤ 10 chars — a 5-slot bar is ~72px wide. */
28
+ short?: string;
29
+ /** Mobile placement. `true` ≡ `'tab'`, `false` ≡ `'hidden'`. @default 'more' */
30
+ mobile?: boolean | NavMobilePlacement;
31
+ icon: ReactNode;
32
+ /**
33
+ * The router seam. basalt renders every pixel of chrome (desktop row, 56px slot, 44px sheet
34
+ * row) and only hosts this component. When absent the row falls back to `<a href>` + `onClick`.
35
+ */
36
+ Anchor?: NavAnchor;
37
+ /** Plain href — the no-router fallback, and the breadcrumb parent target. */
38
+ href?: string;
39
+ active?: boolean;
40
+ disabled?: boolean;
41
+ onClick?: (e: MouseEvent) => void;
42
+ /** Rich desktop badge (e.g. `NavCountBadge`). Desktop only. */
43
+ badge?: ReactNode;
44
+ /** Unread count. Renders `NavCountBadge` on desktop when `badge` is absent, and an accent
45
+ * dot on the mobile slot icon. `0` renders nothing. */
46
+ count?: number;
47
+ children?: SidebarItem[];
48
+ };
49
+ /** Per-section mobile configuration. `false` hides the whole section from mobile. */
50
+ export type NavSectionMobile = {
51
+ /** Give this SECTION a bar slot; its destinations open per §2.2 cardinality inference. */
52
+ tab?: true;
53
+ /** Slot label override; falls back to `SidebarSection.label`. */
54
+ label?: string;
55
+ /** Slot icon override; falls back to `SidebarSection.icon`. */
56
+ icon?: ReactNode;
57
+ };
58
+ export type SidebarSection = {
59
+ label: string;
60
+ items: SidebarItem[];
61
+ icon?: ReactNode;
62
+ collapsible?: boolean;
63
+ defaultCollapsed?: boolean;
64
+ /** Mobile behaviour for this section. Omit for the default (items place themselves). */
65
+ mobile?: false | NavSectionMobile;
66
+ };
67
+ export type MobileNavSurface = 'link' | 'menu' | 'sheet';
68
+ /** One row inside a `menu`/`sheet` slot, grouped under its source section. */
69
+ export type MobileNavGroup = {
70
+ key: string;
71
+ label?: string;
72
+ items: SidebarItem[];
73
+ };
74
+ export type MobileNavSlot = {
75
+ kind: 'link';
76
+ key: string;
77
+ label: string;
78
+ short: string;
79
+ icon: ReactNode;
80
+ active: boolean;
81
+ item: SidebarItem;
82
+ } | {
83
+ kind: 'menu' | 'sheet';
84
+ key: string;
85
+ label: string;
86
+ short: string;
87
+ icon: ReactNode;
88
+ active: boolean;
89
+ groups: MobileNavGroup[];
90
+ isMore: boolean;
91
+ };
92
+ /** Pure projection of `sections` onto the bar. Unit-testable with no DOM. */
93
+ export type MobileNavModel = {
94
+ slots: MobileNavSlot[];
95
+ };
96
+ export type MobileNavConfig = {
97
+ /** Explicit slot order by item/section key. Overrides every `item.mobile` placement. */
98
+ tabs?: readonly string[];
99
+ /** Hard cap on bar slots, INCLUDING More. @default 5 */
100
+ maxTabs?: number;
101
+ /** Rows a slot may hold before it becomes a sheet instead of a menu. @default 6 */
102
+ menuMax?: number;
103
+ /** @default 'More' */
104
+ moreLabel?: string;
105
+ /** Extra content pinned to the bottom of the More surface. */
106
+ moreExtra?: ReactNode;
107
+ /** Scroll container for active-slot re-tap. @default document.scrollingElement */
108
+ getScrollElement?: () => HTMLElement | null;
109
+ };
110
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/nav/types.ts"],"names":[],"mappings":"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"}
File without changes
@@ -1,11 +1,18 @@
1
1
  /**
2
2
  * ./router-tanstack — headless TanStack Router bridge for the app shell.
3
3
  * Optional peer: @tanstack/react-router.
4
- * Augments StaticDataRouteOption with title/icon/navSection for breadcrumb
5
- * and nav-active wiring — no Mantine, no JSX, no navigate() helper.
4
+ * Carries the typed navigation definition (`defineNav`/`navGroup`/`navTarget`/`flattenNav`) and
5
+ * `useNav`, which resolves it into `BasaltShell`'s `sections` + `mobileNav` props; plus the
6
+ * StaticDataRouteOption augmentation (title/icon/navSection) for router-derived breadcrumbs and
7
+ * the search-param stores — no Mantine, no JSX.
6
8
  */
7
9
  export { createSearchParamStore, type SearchParamStore, type SearchParamStoreOptions, } from './search-param-store.js';
8
10
  export { createMultiSearchParamStore, type MultiSearchParamStore, type MultiSearchParamStoreOptions, } from './multi-search-param-store.js';
11
+ export { defineNav, navGroup, navTarget, flattenNav } from './nav.js';
12
+ export type { NavItemMeta, NavGroupMeta, AnyNavItem, AnyNavGroup, AnyNavLink, FlatNavItem, NavConfig, NavItemId, NavGroupId, NavTabId, NavIndex, } from './nav.js';
13
+ export { useNav } from './use-nav.js';
14
+ export type { UseNavOptions, NavBinding } from './use-nav.js';
15
+ export type { NavAnchor, NavAnchorProps, NavMobilePlacement } from '../nav/types.js';
9
16
  import type { ReactNode } from 'react';
10
17
  declare module '@tanstack/react-router' {
11
18
  interface StaticDataRouteOption {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/router-tanstack/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,sBAAsB,EACtB,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,GAC7B,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,2BAA2B,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,4BAA4B,GAClC,MAAM,4BAA4B,CAAA;AACnC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAKtC,OAAO,QAAQ,wBAAwB,CAAC;IACtC,UAAU,qBAAqB;QAC7B,0EAA0E;QAC1E,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,oEAAoE;QACpE,IAAI,CAAC,EAAE,SAAS,CAAA;QAChB,wDAAwD;QACxD,UAAU,CAAC,EAAE,MAAM,CAAA;KACpB;CACF;AAID,mFAAmF;AACnF,MAAM,MAAM,SAAS,GAAG;IACtB,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAA;CAChE,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,IAAI,SAAS,CAUxC;AAID,iEAAiE;AACjE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAA;IACb,oFAAoF;IACpF,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,IAAI,gBAAgB,EAAE,CASzD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/router-tanstack/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,sBAAsB,EACtB,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,GAC7B,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,2BAA2B,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,4BAA4B,GAClC,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAClE,YAAY,EACV,WAAW,EACX,YAAY,EACZ,UAAU,EACV,WAAW,EACX,UAAU,EACV,WAAW,EACX,SAAS,EACT,SAAS,EACT,UAAU,EACV,QAAQ,EACR,QAAQ,GACT,MAAM,OAAO,CAAA;AACd,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAClC,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC1D,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAKtC,OAAO,QAAQ,wBAAwB,CAAC;IACtC,UAAU,qBAAqB;QAC7B,0EAA0E;QAC1E,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,oEAAoE;QACpE,IAAI,CAAC,EAAE,SAAS,CAAA;QAChB,wDAAwD;QACxD,UAAU,CAAC,EAAE,MAAM,CAAA;KACpB;CACF;AAID,mFAAmF;AACnF,MAAM,MAAM,SAAS,GAAG;IACtB,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAA;CAChE,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,IAAI,SAAS,CAUxC;AAID,iEAAiE;AACjE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAA;IACb,oFAAoF;IACpF,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,IAAI,gBAAgB,EAAE,CASzD"}
@@ -4,6 +4,8 @@ import {
4
4
  import {
5
5
  createMultiSearchParamStore
6
6
  } from "./multi-search-param-store.js";
7
+ import { defineNav, navGroup, navTarget, flattenNav } from "./nav.js";
8
+ import { useNav } from "./use-nav.js";
7
9
  import { useLocation, useMatches } from "@tanstack/react-router";
8
10
  function useBasaltNav() {
9
11
  const currentPath = useLocation({ select: (l) => l.pathname });
@@ -24,6 +26,11 @@ function useRouterBreadcrumbs() {
24
26
  export {
25
27
  createMultiSearchParamStore,
26
28
  createSearchParamStore,
29
+ defineNav,
30
+ flattenNav,
31
+ navGroup,
32
+ navTarget,
27
33
  useBasaltNav,
34
+ useNav,
28
35
  useRouterBreadcrumbs
29
36
  };
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Typed navigation definition. Wraps TanStack's own `linkOptions` rather than reimplementing its
3
+ * validator, so `to` / `search` / `params` get the router's full checking, and nav metadata rides
4
+ * OUTSIDE the validated object in a sibling `link:` key.
5
+ *
6
+ * Why not flat (`{ id, label, to, search }`) via a carrier `TComp` over `ValidateLinkOptions`:
7
+ * `Constrain<T, C> = (T extends C ? T : never) | C` is an ASSIGNABILITY check, and assignability
8
+ * does not do excess-property checking — so `{ id, label, to: '/charts', colour: 'red' }` compiles
9
+ * silently. Compile-verified. A config whose selling point is "typos are compile errors" cannot
10
+ * have a hole exactly where metadata typos live.
11
+ *
12
+ * No JSX and no Mantine in this module — `./router-tanstack` is a headless barrel by contract.
13
+ */
14
+ import type { ReactNode } from 'react';
15
+ import type { NavMobilePlacement, NavSectionMobile } from '../nav/types.js';
16
+ export type NavItemMeta = {
17
+ id: string;
18
+ label: string;
19
+ /** Bar/menu label. Falls back to `label`. */
20
+ short?: string;
21
+ icon?: ReactNode;
22
+ disabled?: boolean;
23
+ /** Mobile placement. `true` ≡ `'tab'`, `false` ≡ `'hidden'`. @default 'more' */
24
+ mobile?: boolean | NavMobilePlacement;
25
+ /** Active on an exact path match only. Default: prefix (`fuzzy: true`). `'/'` is always exact. */
26
+ exact?: boolean;
27
+ };
28
+ export type NavGroupMeta = {
29
+ id: string;
30
+ label: string;
31
+ icon?: ReactNode;
32
+ collapsible?: boolean;
33
+ defaultCollapsed?: boolean;
34
+ mobile?: false | NavSectionMobile;
35
+ };
36
+ /** Widened read shapes. The framework reads through these; a consumer never writes them. */
37
+ export type AnyNavLink = {
38
+ to: string;
39
+ search?: unknown;
40
+ params?: unknown;
41
+ };
42
+ export type AnyNavItem = NavItemMeta & {
43
+ link: AnyNavLink;
44
+ children?: ReadonlyArray<AnyNavItem>;
45
+ };
46
+ export type AnyNavGroup = NavGroupMeta & {
47
+ items: ReadonlyArray<AnyNavItem>;
48
+ };
49
+ /** Exact-object check: any key outside `Shape` is typed `never`, which surfaces as an error. */
50
+ export type Exact<T, Shape> = T & {
51
+ readonly [K in Exclude<keyof T, keyof Shape>]: never;
52
+ };
53
+ /**
54
+ * `Exact` for ONE item, recursing into `children`.
55
+ *
56
+ * The recursion is the whole point: `AnyNavItem['children']` is a plain
57
+ * `ReadonlyArray<AnyNavItem>` constraint, and a constraint is an assignability check — so a
58
+ * top-level-only `Exact` leaves the metadata-typo hole wide open exactly one level down
59
+ * (`children: [{ id, label, colour: 'red', link }]` compiled silently). Reapplying `Exact` per
60
+ * child, at every depth, is what makes the nested `link:` design actually deliver "every typo is a
61
+ * compile error" rather than "every typo in the top row is a compile error".
62
+ *
63
+ * The `[K in Extract<keyof T, 'children'>]` form rather than a `children?:` property is what keeps
64
+ * this inference-safe: it evaluates to `{}` for an item that declares no children (so no phantom
65
+ * key is added to the checked shape and nothing becomes required), and the intersection with
66
+ * `Exact<T, AnyNavItem>` keeps `T` in a naked inference position so the caller's literal types
67
+ * still flow out of `navGroup`'s `const T`.
68
+ */
69
+ export type ExactNavItem<T> = Exact<T, AnyNavItem> & {
70
+ readonly [K in Extract<keyof T, 'children'>]: T[K] extends ReadonlyArray<AnyNavItem> ? NavItemsInput<T[K]> : T[K];
71
+ };
72
+ /** Per-element `Exact` over a tuple of items, so an unknown key errors at the item that owns it. */
73
+ export type NavItemsInput<T extends ReadonlyArray<AnyNavItem>> = {
74
+ readonly [K in keyof T]: ExactNavItem<T[K]>;
75
+ };
76
+ type ItemsOf<G> = G extends ReadonlyArray<{
77
+ items: ReadonlyArray<infer I>;
78
+ }> ? I : never;
79
+ /**
80
+ * One item plus every descendant, at arbitrary depth. Recursive so the TYPE side matches what
81
+ * `findById` / `flattenNav` already do at RUNTIME — a one-level walk made `navTarget(nav,
82
+ * 'grandchild')` a compile error against an id the runtime resolves fine.
83
+ */
84
+ type WithDescendants<I> = I | (I extends {
85
+ children: ReadonlyArray<infer C>;
86
+ } ? WithDescendants<C> : never);
87
+ type AllItemsOf<G> = WithDescendants<ItemsOf<G>>;
88
+ /**
89
+ * id → link options, as a mapped type with key remapping. Do NOT replace this with
90
+ * `Extract<AllItemsOf<G>, { id: K }>['link']` — that form was compile-tested and returns the
91
+ * WHOLE union, silently losing per-id precision.
92
+ */
93
+ export type NavIndex<G> = {
94
+ [I in AllItemsOf<G> as I extends {
95
+ id: infer K extends string;
96
+ } ? K : never]: I extends {
97
+ link: infer L;
98
+ } ? L : never;
99
+ };
100
+ /**
101
+ * The literal union of every addressable destination id in a definition — parents, children and
102
+ * grandchildren alike, at ARBITRARY depth, matching what `findById` / `flattenNav` resolve at
103
+ * runtime. Verified to seven levels of `children` with no instantiation-depth complaint.
104
+ *
105
+ * If this ever resolves to plain `string`, the entire nav API validates nothing while reporting
106
+ * zero errors — so any change to the generics above must be re-checked against
107
+ * `apps/playground/src/nav.type-guard.tsx`, whose `@ts-expect-error` rows go UNUSED (TS2578) the
108
+ * moment the union widens.
109
+ */
110
+ export type NavItemId<G> = Extract<keyof NavIndex<G>, string>;
111
+ export type NavGroupId<G> = G extends ReadonlyArray<{
112
+ id: infer I;
113
+ }> ? I : never;
114
+ export type NavTabId<G> = NavItemId<G> | NavGroupId<G>;
115
+ export type NavConfig<G extends ReadonlyArray<AnyNavGroup>, T extends ReadonlyArray<NavTabId<G>>> = {
116
+ groups: G;
117
+ mobile?: {
118
+ /** Explicit bar order by item/group id. Omit to let each item's `mobile` field decide. */
119
+ tabs?: T;
120
+ /** @default 5 */ maxTabs?: number;
121
+ /** @default 6 */ menuMax?: number;
122
+ /** @default 'More' */ moreLabel?: string;
123
+ };
124
+ };
125
+ /**
126
+ * One nav group. `const M` on the meta is load-bearing: without it the group `id` widens to
127
+ * `string` from `NavGroupMeta` and `mobile.tabs` silently stops validating group ids.
128
+ */
129
+ export declare function navGroup<const M extends NavGroupMeta, const T extends ReadonlyArray<AnyNavItem>>(meta: Exact<M, NavGroupMeta>, items: NavItemsInput<T>): M & {
130
+ items: T;
131
+ };
132
+ /**
133
+ * The whole navigation — desktop and mobile — in one inferred definition.
134
+ *
135
+ * There is deliberately NO compile-time cap on `mobile.tabs.length`. Both a
136
+ * `T extends … & { length: 0|1|2|3|4|5 }` constraint and a `tabs: T & MaxFive` property form were
137
+ * compile-tested: each makes the length check work but silently degrades `NavTabId<G>` to `string`,
138
+ * killing the far more valuable id-union validation. The cap is a runtime concern — the shell's
139
+ * `projectMobileNav` slices to `maxTabs` and DEV-warns about what it dropped.
140
+ */
141
+ export declare function defineNav<const G extends ReadonlyArray<AnyNavGroup>, const T extends ReadonlyArray<NavTabId<G>>>(config: NavConfig<G, T>): NavConfig<G, T>;
142
+ /**
143
+ * The link options for one destination, by id. Spreadable into `<Link>`, `router.navigate()`
144
+ * and `redirect()`. This is what collapses a consumer's Spotlight commands and index redirect.
145
+ *
146
+ * `id` is the definition's literal id union (`NavItemId`) at any nesting depth, so a nested child
147
+ * or grandchild is addressable exactly like a top-level item — the type side and `findById`'s
148
+ * runtime walk agree.
149
+ */
150
+ export declare function navTarget<const C extends {
151
+ groups: ReadonlyArray<AnyNavGroup>;
152
+ }, K extends NavItemId<C['groups']>>(config: C, id: K): NavIndex<C['groups']>[K];
153
+ /** A flattened destination, tagged with the group it was declared in. */
154
+ export type FlatNavItem = AnyNavItem & {
155
+ groupId: string;
156
+ groupLabel: string;
157
+ };
158
+ /** Depth-first flatten, parent then children — the leaf every other surface reads. */
159
+ export declare function flattenNav(config: {
160
+ groups: ReadonlyArray<AnyNavGroup>;
161
+ }): FlatNavItem[];
162
+ export {};
163
+ //# sourceMappingURL=nav.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nav.d.ts","sourceRoot":"","sources":["../../src/router-tanstack/nav.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAIxE,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,gFAAgF;IAChF,MAAM,CAAC,EAAE,OAAO,GAAG,kBAAkB,CAAA;IACrC,kGAAkG;IAClG,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,MAAM,CAAC,EAAE,KAAK,GAAG,gBAAgB,CAAA;CAClC,CAAA;AAED,4FAA4F;AAC5F,MAAM,MAAM,UAAU,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AAC3E,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,EAAE,aAAa,CAAC,UAAU,CAAC,CAAA;CAAE,CAAA;AACjG,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG;IAAE,KAAK,EAAE,aAAa,CAAC,UAAU,CAAC,CAAA;CAAE,CAAA;AAI7E,gGAAgG;AAChG,MAAM,MAAM,KAAK,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,GAAG;IAAE,QAAQ,EAAE,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,CAAC,GAAG,KAAK;CAAE,CAAA;AAE1F;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,GAAG;IACnD,QAAQ,EAAE,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,aAAa,CAAC,UAAU,CAAC,GAChF,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GACnB,CAAC,CAAC,CAAC,CAAC;CACT,CAAA;AAED,oGAAoG;AACpG,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,aAAa,CAAC,UAAU,CAAC,IAAI;IAC/D,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5C,CAAA;AAED,KAAK,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,aAAa,CAAC;IAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;CAAE,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;AAExF;;;;GAIG;AACH,KAAK,eAAe,CAAC,CAAC,IAClB,CAAC,GACD,CAAC,CAAC,SAAS;IAAE,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;CAAE,GAAG,eAAe,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAA;AAEjF,KAAK,UAAU,CAAC,CAAC,IAAI,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAA;AAEhD;;;;GAIG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI;KACvB,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS;QAAE,EAAE,EAAE,MAAM,CAAC,SAAS,MAAM,CAAA;KAAE,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,SAAS;QACtF,IAAI,EAAE,MAAM,CAAC,CAAA;KACd,GACG,CAAC,GACD,KAAK;CACV,CAAA;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;AAC7D,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,SAAS,aAAa,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;AAChF,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAA;AAEtD,MAAM,MAAM,SAAS,CACnB,CAAC,SAAS,aAAa,CAAC,WAAW,CAAC,EACpC,CAAC,SAAS,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAClC;IACF,MAAM,EAAE,CAAC,CAAA;IACT,MAAM,CAAC,EAAE;QACP,0FAA0F;QAC1F,IAAI,CAAC,EAAE,CAAC,CAAA;QACR,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;QAClC,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;QAClC,sBAAsB,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAC1C,CAAA;CACF,CAAA;AAID;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,CAAC,CAAC,SAAS,YAAY,EAAE,KAAK,CAAC,CAAC,SAAS,aAAa,CAAC,UAAU,CAAC,EAC9F,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,EAC5B,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,GACtB,CAAC,GAAG;IAAE,KAAK,EAAE,CAAC,CAAA;CAAE,CAElB;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CACvB,KAAK,CAAC,CAAC,SAAS,aAAa,CAAC,WAAW,CAAC,EAC1C,KAAK,CAAC,CAAC,SAAS,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAC1C,MAAM,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAE1C;AAaD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACvB,KAAK,CAAC,CAAC,SAAS;IAAE,MAAM,EAAE,aAAa,CAAC,WAAW,CAAC,CAAA;CAAE,EACtD,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAChC,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAM5C;AAED,yEAAyE;AACzE,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9E,sFAAsF;AACtF,wBAAgB,UAAU,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,aAAa,CAAC,WAAW,CAAC,CAAA;CAAE,GAAG,WAAW,EAAE,CAUxF"}
@@ -0,0 +1,38 @@
1
+ function navGroup(meta, items) {
2
+ return { ...meta, items };
3
+ }
4
+ function defineNav(config) {
5
+ return config;
6
+ }
7
+ function findById(items, id) {
8
+ for (const item of items) {
9
+ if (item.id === id) return item;
10
+ const hit = item.children ? findById(item.children, id) : void 0;
11
+ if (hit) return hit;
12
+ }
13
+ return void 0;
14
+ }
15
+ function navTarget(config, id) {
16
+ for (const g of config.groups) {
17
+ const hit = findById(g.items, id);
18
+ if (hit) return hit.link;
19
+ }
20
+ throw new Error(`navTarget: no nav item with id "${id}"`);
21
+ }
22
+ function flattenNav(config) {
23
+ const out = [];
24
+ const walk = (items, groupId, groupLabel) => {
25
+ for (const item of items) {
26
+ out.push({ ...item, groupId, groupLabel });
27
+ if (item.children) walk(item.children, groupId, groupLabel);
28
+ }
29
+ };
30
+ for (const g of config.groups) walk(g.items, g.id, g.label);
31
+ return out;
32
+ }
33
+ export {
34
+ defineNav,
35
+ flattenNav,
36
+ navGroup,
37
+ navTarget
38
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=nav.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nav.test.d.ts","sourceRoot":"","sources":["../../src/router-tanstack/nav.test.ts"],"names":[],"mappings":""}
@@ -49,16 +49,26 @@
49
49
  * return <Charts data={data} range={range} />
50
50
  * }
51
51
  *
52
- * **5. Nav links** — carry the param across sub-pages via search={true},
53
- * guarded by a path-prefix check so non-dashboard links stay clean:
52
+ * **5. Nav links** — carry the param across sub-pages, set per destination in
53
+ * the nav definition so only the dashboard sub-tree inherits it and every
54
+ * other link stays clean. Use a CLICK-TIME THUNK over `readStored`:
54
55
  *
55
- * const renderNavLink = (item, { active }) => {
56
- * const isDashboard = item.href?.startsWith('/dashboard/')
57
- * || item.href === '/dashboard'
58
- * return <MantineNavLink component={Link} to={item.href}
59
- * {...(isDashboard ? { search: true } : {})}
60
- * label={item.label} active={active} />
61
- * }
56
+ * const dashSearch = () => ({ range: dashboardRange.readStored() ?? '30d' })
57
+ *
58
+ * navGroup({ id: 'dash', label: 'Dashboard' }, [
59
+ * { id: 'dash-overview', label: 'Overview', icon: <IconHome />,
60
+ * link: linkOptions({ to: '/dashboard', search: dashSearch }) },
61
+ * { id: 'dash-activity', label: 'Activity', icon: <IconPulse />,
62
+ * link: linkOptions({ to: '/dashboard/activity', search: dashSearch }) },
63
+ * ])
64
+ *
65
+ * NOT `search: true`. That flag means "keep the current search" and TanStack
66
+ * only offers it where the target's search is OPTIONAL — a route wired to a
67
+ * store's `validateSearch` always returns the param, so the router requires a
68
+ * value and `search: true` is a type error. The thunk is also the better
69
+ * behaviour: `<Link>` re-evaluates it at click time, so arriving from OUTSIDE
70
+ * the sub-tree restores the last selection instead of landing on the factory
71
+ * default, and a definition evaluated once at module scope never goes stale.
62
72
  */
63
73
  export type SearchParamStoreOptions<T extends string, P extends string = string> = {
64
74
  /** localStorage key (namespaced `basalt:<key>` automatically). */
@@ -1 +1 @@
1
- {"version":3,"file":"search-param-store.d.ts","sourceRoot":"","sources":["../../src/router-tanstack/search-param-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AAMH,MAAM,MAAM,uBAAuB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI;IACjF,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAA;IACX,mEAAmE;IACnE,KAAK,EAAE,CAAC,CAAA;IACR,2DAA2D;IAC3D,MAAM,EAAE,SAAS,CAAC,EAAE,CAAA;IACpB,kEAAkE;IAClE,QAAQ,EAAE,CAAC,CAAA;IACX,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI;IAC1E;;;OAGG;IACH,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK;SAAG,CAAC,IAAI,CAAC,GAAG,CAAC;KAAE,CAAA;IACpE;;;OAGG;IACH,QAAQ,EAAE,MAAM,SAAS,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,CAAA;IAC/C,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC,GAAG,IAAI,CAAA;CAC3B,CAAA;AAID,wBAAgB,sBAAsB,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,CAAC,CAAC,SAAS,MAAM,EACnF,IAAI,EAAE,uBAAuB,CAAC,CAAC,EAAE,CAAC,CAAC,GAClC,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,CAyBxB"}
1
+ {"version":3,"file":"search-param-store.d.ts","sourceRoot":"","sources":["../../src/router-tanstack/search-param-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuEG;AAMH,MAAM,MAAM,uBAAuB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI;IACjF,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAA;IACX,mEAAmE;IACnE,KAAK,EAAE,CAAC,CAAA;IACR,2DAA2D;IAC3D,MAAM,EAAE,SAAS,CAAC,EAAE,CAAA;IACpB,kEAAkE;IAClE,QAAQ,EAAE,CAAC,CAAA;IACX,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI;IAC1E;;;OAGG;IACH,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK;SAAG,CAAC,IAAI,CAAC,GAAG,CAAC;KAAE,CAAA;IACpE;;;OAGG;IACH,QAAQ,EAAE,MAAM,SAAS,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,CAAA;IAC/C,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC,GAAG,IAAI,CAAA;CAC3B,CAAA;AAID,wBAAgB,sBAAsB,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,CAAC,CAAC,SAAS,MAAM,EACnF,IAAI,EAAE,uBAAuB,CAAC,CAAC,EAAE,CAAC,CAAC,GAClC,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,CAyBxB"}
@@ -0,0 +1,30 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { MobileNavConfig, SidebarSection } from '../nav/types.js';
3
+ import type { AnyNavGroup, AnyNavItem, NavConfig, NavItemId, NavTabId } from './nav.js';
4
+ export type UseNavOptions<G extends ReadonlyArray<AnyNavGroup>> = {
5
+ /**
6
+ * Live per-destination badges, keys typed to the definition's id union.
7
+ * A `number` becomes `item.count` (desktop `NavCountBadge` + mobile dot).
8
+ * Anything else becomes `item.badge` (desktop only).
9
+ */
10
+ badges?: Partial<Record<NavItemId<G>, number | ReactNode>>;
11
+ /** Override active detection entirely. */
12
+ isActive?: (item: AnyNavItem) => boolean;
13
+ /** Passed straight through into the returned `mobileNav`. */
14
+ moreExtra?: ReactNode;
15
+ getScrollElement?: () => HTMLElement | null;
16
+ };
17
+ /** Spread straight onto `<BasaltShell {...nav} />` — both keys are `BasaltShellProps` keys. */
18
+ export type NavBinding = {
19
+ sections: SidebarSection[];
20
+ mobileNav: MobileNavConfig;
21
+ };
22
+ /**
23
+ * Resolve a nav definition against the live router.
24
+ *
25
+ * @example
26
+ * const nav = useNav(NAV, { badges: { calendar: 3 } })
27
+ * return <BasaltShell brand={brand} {...nav}>{children}</BasaltShell>
28
+ */
29
+ export declare function useNav<const G extends ReadonlyArray<AnyNavGroup>, const T extends ReadonlyArray<NavTabId<G>>>(config: NavConfig<G, T>, opts?: UseNavOptions<G>): NavBinding;
30
+ //# sourceMappingURL=use-nav.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-nav.d.ts","sourceRoot":"","sources":["../../src/router-tanstack/use-nav.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAe,SAAS,EAAE,MAAM,OAAO,CAAA;AAEnD,OAAO,KAAK,EAAE,eAAe,EAA0B,cAAc,EAAE,MAAM,cAAc,CAAA;AAC3F,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEpF,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,aAAa,CAAC,WAAW,CAAC,IAAI;IAChE;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAA;IAC1D,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAA;IACxC,6DAA6D;IAC7D,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,WAAW,GAAG,IAAI,CAAA;CAC5C,CAAA;AAED,+FAA+F;AAC/F,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,EAAE,cAAc,EAAE,CAAC;IAAC,SAAS,EAAE,eAAe,CAAA;CAAE,CAAA;AAEnF;;;;;;GAMG;AACH,wBAAgB,MAAM,CACpB,KAAK,CAAC,CAAC,SAAS,aAAa,CAAC,WAAW,CAAC,EAC1C,KAAK,CAAC,CAAC,SAAS,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAC1C,MAAM,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,UAAU,CAqG9D"}
@@ -0,0 +1,71 @@
1
+ import { createElement, useMemo } from "react";
2
+ import { Link, useMatchRoute, useRouterState } from "@tanstack/react-router";
3
+ function useNav(config, opts) {
4
+ const matchRoute = useMatchRoute();
5
+ const href = useRouterState({ select: (s) => s.location.href });
6
+ const badges = opts?.badges;
7
+ const isActiveOverride = opts?.isActive;
8
+ const sections = useMemo(() => {
9
+ const badgeOf = (id) => badges?.[id];
10
+ const isActive = (item) => isActiveOverride?.(item) ?? Boolean(
11
+ matchRoute({
12
+ to: item.link.to,
13
+ ...item.link.params !== void 0 && { params: item.link.params },
14
+ // Prefix match by default so a parent stays lit on its children; `'/'` would then match
15
+ // everything, so it is always exact.
16
+ fuzzy: item.exact !== true && item.link.to !== "/"
17
+ })
18
+ );
19
+ const toSidebarItem = (item) => {
20
+ const Anchor = (props) => createElement(Link, { ...item.link, ...props });
21
+ const badge = badgeOf(item.id);
22
+ const children = item.children?.map(toSidebarItem);
23
+ return {
24
+ key: item.id,
25
+ label: item.label,
26
+ icon: item.icon,
27
+ Anchor,
28
+ // Per-destination active only. Rolling a child's active state up into its parent is the
29
+ // MOBILE SLOT's rule (§2.3.12) and belongs to `projectMobileNav`, which reads `children`
30
+ // anyway — doing it here too would also light a desktop parent row whose own route does
31
+ // not match.
32
+ active: isActive(item),
33
+ ...item.short !== void 0 && { short: item.short },
34
+ ...item.mobile !== void 0 && { mobile: item.mobile },
35
+ ...item.disabled !== void 0 && { disabled: item.disabled },
36
+ ...typeof badge === "number" ? { count: badge } : badge === void 0 || badge === null ? {} : { badge },
37
+ ...children !== void 0 && { children }
38
+ };
39
+ };
40
+ return config.groups.map((group) => ({
41
+ label: group.label,
42
+ items: group.items.map(toSidebarItem),
43
+ ...group.icon !== void 0 && { icon: group.icon },
44
+ ...group.collapsible !== void 0 && { collapsible: group.collapsible },
45
+ ...group.defaultCollapsed !== void 0 && { defaultCollapsed: group.defaultCollapsed },
46
+ ...group.mobile !== void 0 && { mobile: group.mobile }
47
+ }));
48
+ }, [config, matchRoute, href, badges, isActiveOverride]);
49
+ const mobile = config.mobile;
50
+ const moreExtra = opts?.moreExtra;
51
+ const getScrollElement = opts?.getScrollElement;
52
+ const mobileNav = useMemo(() => {
53
+ const groupLabelById = new Map(config.groups.map((g) => [g.id, g.label]));
54
+ const tabs = mobile?.tabs?.map((id) => {
55
+ const key = id;
56
+ return groupLabelById.get(key) ?? key;
57
+ });
58
+ return {
59
+ ...tabs !== void 0 && { tabs },
60
+ ...mobile?.maxTabs !== void 0 && { maxTabs: mobile.maxTabs },
61
+ ...mobile?.menuMax !== void 0 && { menuMax: mobile.menuMax },
62
+ ...mobile?.moreLabel !== void 0 && { moreLabel: mobile.moreLabel },
63
+ ...moreExtra !== void 0 && { moreExtra },
64
+ ...getScrollElement !== void 0 && { getScrollElement }
65
+ };
66
+ }, [config, mobile, moreExtra, getScrollElement]);
67
+ return { sections, mobileNav };
68
+ }
69
+ export {
70
+ useNav
71
+ };
@@ -1,17 +1,15 @@
1
- import type { ReactNode } from 'react';
2
- export type BreadcrumbLinkRenderer = (href: string, label: string) => ReactNode;
3
- export declare function AppBreadcrumbs({ section, parent, parentHref, renderBreadcrumbLink, page, }: {
1
+ import type { NavAnchor } from '../nav/types.js';
2
+ export declare function AppBreadcrumbs({ section, parent, parentAnchor, parentHref, page, }: {
4
3
  section?: string;
5
4
  /** Parent item label — shown when the active page is a nested child (e.g. "Dashboard"). */
6
5
  parent?: string | undefined;
7
- /** Parent item href — when provided, the parent label renders as a clickable link. */
8
- parentHref?: string | undefined;
9
6
  /**
10
- * Optional router link renderer for the parent breadcrumb segment. When provided, the parent
11
- * label is rendered through this callback instead of a plain `<a href>`, enabling client-side
12
- * navigation (e.g. TanStack `<Link>`).
7
+ * The parent destination's router anchor (`SidebarItem.Anchor`), so the crumb navigates
8
+ * client-side. basalt still owns every pixel of the crumb — the anchor only hosts the label.
13
9
  */
14
- renderBreadcrumbLink?: BreadcrumbLinkRenderer | undefined;
10
+ parentAnchor?: NavAnchor | undefined;
11
+ /** Parent item href — the no-router fallback; ignored when `parentAnchor` is present. */
12
+ parentHref?: string | undefined;
15
13
  page?: string;
16
14
  }): import("react").JSX.Element | null;
17
15
  //# sourceMappingURL=app-breadcrumbs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-breadcrumbs.d.ts","sourceRoot":"","sources":["../../src/shell/app-breadcrumbs.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAGrD,MAAM,MAAM,sBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAA;AAiB/E,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,MAAM,EACN,UAAU,EACV,oBAAoB,EACpB,IAAI,GACL,EAAE;IACD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,2FAA2F;IAC3F,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAA;IACzD,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,sCAqCA"}
1
+ {"version":3,"file":"app-breadcrumbs.d.ts","sourceRoot":"","sources":["../../src/shell/app-breadcrumbs.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAkB7C,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,MAAM,EACN,YAAY,EACZ,UAAU,EACV,IAAI,GACL,EAAE;IACD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,2FAA2F;IAC3F,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B;;;OAGG;IACH,YAAY,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IACpC,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,sCAyCA"}