basalt-ui 1.18.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 (92) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +7 -0
  3. package/agent/rules/basalt-mantine.md +71 -6
  4. package/agent/rules/basalt-router.md +129 -30
  5. package/agent/rules/basalt-tokens.md +2 -1
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +8 -0
  9. package/dist/nav/types.d.ts +110 -0
  10. package/dist/nav/types.d.ts.map +1 -0
  11. package/dist/nav/types.js +0 -0
  12. package/dist/router-tanstack/index.d.ts +9 -2
  13. package/dist/router-tanstack/index.d.ts.map +1 -1
  14. package/dist/router-tanstack/index.js +7 -0
  15. package/dist/router-tanstack/nav.d.ts +163 -0
  16. package/dist/router-tanstack/nav.d.ts.map +1 -0
  17. package/dist/router-tanstack/nav.js +38 -0
  18. package/dist/router-tanstack/nav.test.d.ts +2 -0
  19. package/dist/router-tanstack/nav.test.d.ts.map +1 -0
  20. package/dist/router-tanstack/search-param-store.d.ts +19 -9
  21. package/dist/router-tanstack/search-param-store.d.ts.map +1 -1
  22. package/dist/router-tanstack/use-nav.d.ts +30 -0
  23. package/dist/router-tanstack/use-nav.d.ts.map +1 -0
  24. package/dist/router-tanstack/use-nav.js +71 -0
  25. package/dist/shell/app-breadcrumbs.d.ts +7 -9
  26. package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
  27. package/dist/shell/app-breadcrumbs.js +8 -4
  28. package/dist/shell/app-mobile-nav.d.ts +23 -24
  29. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  30. package/dist/shell/app-mobile-nav.js +290 -68
  31. package/dist/shell/app-mobile-nav.module.css +166 -11
  32. package/dist/shell/app-mobile-nav.module.css.d.ts +11 -2
  33. package/dist/shell/app-mobile-nav.test.d.ts +2 -0
  34. package/dist/shell/app-mobile-nav.test.d.ts.map +1 -0
  35. package/dist/shell/app-sidebar.d.ts +3 -28
  36. package/dist/shell/app-sidebar.d.ts.map +1 -1
  37. package/dist/shell/app-sidebar.js +40 -174
  38. package/dist/shell/app-sidebar.module.css +3 -2
  39. package/dist/shell/app-sidebar.module.css.d.ts +0 -2
  40. package/dist/shell/index.d.ts +22 -65
  41. package/dist/shell/index.d.ts.map +1 -1
  42. package/dist/shell/index.js +38 -76
  43. package/dist/shell/mobile-nav-model.d.ts +41 -0
  44. package/dist/shell/mobile-nav-model.d.ts.map +1 -0
  45. package/dist/shell/mobile-nav-model.js +244 -0
  46. package/dist/shell/mobile-nav-model.test.d.ts +2 -0
  47. package/dist/shell/mobile-nav-model.test.d.ts.map +1 -0
  48. package/dist/shell/nav-count-badge.d.ts +11 -0
  49. package/dist/shell/nav-count-badge.d.ts.map +1 -0
  50. package/dist/shell/nav-count-badge.js +29 -0
  51. package/dist/surfaces.d.ts +1 -1
  52. package/dist/surfaces.js +1 -1
  53. package/dist/theme/index.d.ts.map +1 -1
  54. package/dist/theme/index.js +8 -7
  55. package/dist/theme/nav-link.module.css +4 -2
  56. package/dist/tokens/index.d.ts.map +1 -1
  57. package/dist/tokens/index.js +2 -0
  58. package/dist/tokens/palette.d.ts +15 -0
  59. package/dist/tokens/palette.d.ts.map +1 -1
  60. package/dist/tokens/palette.js +13 -0
  61. package/dist/tokens.css +2 -0
  62. package/llms.txt +3 -3
  63. package/package.json +1 -1
  64. package/src/index.ts +15 -5
  65. package/src/nav/types.ts +115 -0
  66. package/src/router-tanstack/index.ts +21 -2
  67. package/src/router-tanstack/nav.test.ts +111 -0
  68. package/src/router-tanstack/nav.ts +205 -0
  69. package/src/router-tanstack/search-param-store.ts +19 -9
  70. package/src/router-tanstack/use-nav.ts +146 -0
  71. package/src/shell/app-breadcrumbs.tsx +21 -19
  72. package/src/shell/app-mobile-nav.module.css +166 -11
  73. package/src/shell/app-mobile-nav.module.css.d.ts +11 -2
  74. package/src/shell/app-mobile-nav.test.tsx +369 -0
  75. package/src/shell/app-mobile-nav.tsx +436 -102
  76. package/src/shell/app-sidebar.module.css +3 -2
  77. package/src/shell/app-sidebar.module.css.d.ts +0 -2
  78. package/src/shell/app-sidebar.test.tsx +6 -10
  79. package/src/shell/app-sidebar.tsx +49 -206
  80. package/src/shell/index.test.tsx +62 -7
  81. package/src/shell/index.tsx +105 -164
  82. package/src/shell/mobile-nav-model.test.ts +296 -0
  83. package/src/shell/mobile-nav-model.ts +389 -0
  84. package/src/shell/nav-count-badge.tsx +39 -0
  85. package/src/surfaces.ts +1 -1
  86. package/src/theme/density-relations.test.ts +19 -1
  87. package/src/theme/index.ts +8 -7
  88. package/src/theme/nav-link.module.css +4 -2
  89. package/src/theme/spacing.test.ts +11 -0
  90. package/src/tokens/density.test.ts +2 -0
  91. package/src/tokens/index.ts +8 -5
  92. package/src/tokens/palette.ts +16 -0
@@ -1,44 +1,28 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { AppShell, Badge } from "@mantine/core";
3
- import { useDisclosure, useLocalStorage } from "@mantine/hooks";
2
+ import { AppShell } from "@mantine/core";
3
+ import { useLocalStorage } from "@mantine/hooks";
4
+ import { useMemo } from "react";
4
5
  import { AppSidebar } from "./app-sidebar.js";
5
- import { MobileNav } from "./app-mobile-nav.js";
6
+ import { MobileNav, accountRowCount } from "./app-mobile-nav.js";
7
+ import { projectMobileNav } from "./mobile-nav-model.js";
6
8
  import { AppBreadcrumbs } from "./app-breadcrumbs.js";
7
9
  import { PageActionsOutlet, PageHeaderProvider } from "./page-header.js";
8
- import { VX } from "../tokens/index.js";
9
10
  import { useBasaltSpacing } from "../theme/index.js";
10
11
  import headerClasses from "./app-header.module.css";
12
+ import mobileNavClasses from "./app-mobile-nav.module.css";
11
13
  import { AppSidebar as AppSidebar2 } from "./app-sidebar.js";
14
+ import { NavCountBadge } from "./nav-count-badge.js";
12
15
  import { SidebarSearch } from "./sidebar-search.js";
13
16
  import { SidebarAccount } from "./app-sidebar-account.js";
17
+ import { MobileNav as MobileNav2 } from "./app-mobile-nav.js";
14
18
  import {
15
- MobileNav as MobileNav2
16
- } from "./app-mobile-nav.js";
19
+ projectMobileNav as projectMobileNav2,
20
+ MOBILE_MAX_TABS_DEFAULT,
21
+ MOBILE_MENU_MAX_DEFAULT,
22
+ MOBILE_MORE_KEY
23
+ } from "./mobile-nav-model.js";
17
24
  import { AppBreadcrumbs as AppBreadcrumbs2 } from "./app-breadcrumbs.js";
18
25
  import { PageHeaderProvider as PageHeaderProvider2, PageActions, PageActionsOutlet as PageActionsOutlet2 } from "./page-header.js";
19
- function NavCountBadge({ count }) {
20
- if (!count) return null;
21
- return /* @__PURE__ */ jsx(
22
- Badge,
23
- {
24
- size: "sm",
25
- styles: {
26
- root: {
27
- backgroundColor: "color-mix(in srgb, var(--vx-ink) 8%, transparent)",
28
- color: "var(--vx-muted)",
29
- fontFamily: "var(--basalt-font-mono)",
30
- fontSize: VX.text.micro,
31
- fontWeight: 500,
32
- height: 16,
33
- padding: "0 5px",
34
- marginLeft: "auto",
35
- borderRadius: "var(--vx-radius-tight)"
36
- }
37
- },
38
- children: count
39
- }
40
- );
41
- }
42
26
  function findActiveCrumb(sections) {
43
27
  for (const section of sections) {
44
28
  const found = findActiveWithParent(section.items);
@@ -46,67 +30,38 @@ function findActiveCrumb(sections) {
46
30
  return {
47
31
  section: section.label,
48
32
  parent: found.parent,
33
+ parentAnchor: found.parentAnchor,
49
34
  parentHref: found.parentHref,
50
35
  page: found.page
51
36
  };
52
37
  }
53
38
  return void 0;
54
39
  }
55
- function findActiveWithParent(items, parentLabel, parentHref) {
40
+ function findActiveWithParent(items, parentLabel, parentAnchor, parentHref) {
56
41
  for (const item of items) {
57
42
  if (item.children) {
58
- const found = findActiveWithParent(item.children, item.label, item.href);
43
+ const found = findActiveWithParent(item.children, item.label, item.Anchor, item.href);
59
44
  if (found) return found;
60
45
  }
61
- if (item.active) return { parent: parentLabel, parentHref, page: item.label };
46
+ if (item.active) return { parent: parentLabel, parentAnchor, parentHref, page: item.label };
62
47
  }
63
48
  return void 0;
64
49
  }
65
- function toMobileSections(sections, closeMobile) {
66
- return sections.filter((s) => s.mobileTab !== false).map((s) => ({
67
- key: s.label,
68
- label: s.label,
69
- icon: s.icon,
70
- active: s.items.some((i) => i.active),
71
- items: flattenSidebarItems(s.items).map(
72
- (i) => ({
73
- key: i.key,
74
- label: i.label,
75
- icon: i.icon,
76
- ...i.href !== void 0 && { href: i.href },
77
- ...i.active !== void 0 && { active: i.active },
78
- onClick: (e) => {
79
- i.onClick?.(e);
80
- closeMobile();
81
- }
82
- })
83
- )
84
- }));
85
- }
86
- function flattenSidebarItems(items) {
87
- return items.flatMap((item) => [
88
- item,
89
- ...item.children ? flattenSidebarItems(item.children) : []
90
- ]);
91
- }
92
50
  function BasaltShell({
93
51
  brand,
94
52
  sections,
95
- renderNavLink,
53
+ mobileNav,
96
54
  globalActions,
97
- sidebarFooterExtra,
98
55
  sidebarNavExtra,
99
56
  settingsMenuItems,
100
57
  storageKey = "basalt-sidebar-collapsed",
101
58
  collapsed: collapsedProp,
102
59
  onCollapsedChange,
103
- renderBreadcrumbLink,
104
60
  account,
105
61
  search,
106
62
  children
107
63
  }) {
108
64
  const { step } = useBasaltSpacing();
109
- const [mobileOpened, { toggle: toggleMobile, close: closeMobile }] = useDisclosure();
110
65
  const [storedCollapsed, setStoredCollapsed] = useLocalStorage({
111
66
  key: storageKey,
112
67
  defaultValue: false,
@@ -120,7 +75,11 @@ function BasaltShell({
120
75
  onCollapsedChange?.(next);
121
76
  };
122
77
  const activeCrumb = findActiveCrumb(sections);
123
- const mobileSections = toMobileSections(sections, closeMobile);
78
+ const extraMoreRows = accountRowCount(account) + (settingsMenuItems?.length ?? 0) + (mobileNav?.moreExtra ? 1 : 0);
79
+ const model = useMemo(
80
+ () => projectMobileNav(sections, { config: mobileNav, extraMoreRows }),
81
+ [sections, mobileNav, extraMoreRows]
82
+ );
124
83
  return /* @__PURE__ */ jsx(PageHeaderProvider, { children: /* @__PURE__ */ jsxs(
125
84
  AppShell,
126
85
  {
@@ -135,13 +94,14 @@ function BasaltShell({
135
94
  sm: collapsed ? step.appShellNavbarRailWidth : step.appShellNavbarWidth
136
95
  },
137
96
  breakpoint: "sm",
138
- collapsed: { mobile: !mobileOpened }
97
+ // No mobile sidebar drawer, ever — the bottom bar is the entire mobile nav.
98
+ collapsed: { mobile: true }
139
99
  },
140
- footer: { height: { base: 52, sm: 0 } },
100
+ footer: { height: { base: step.mobileNavBarHeight, sm: 0 } },
141
101
  padding: "sm",
142
102
  children: [
143
103
  /* @__PURE__ */ jsx(AppShell.Header, { px: "md", withBorder: false, children: /* @__PURE__ */ jsxs("div", { className: headerClasses.bar, children: [
144
- /* @__PURE__ */ jsx("div", { className: headerClasses.lead, children: /* @__PURE__ */ jsx(AppBreadcrumbs, { ...activeCrumb, renderBreadcrumbLink }) }),
104
+ /* @__PURE__ */ jsx("div", { className: headerClasses.lead, children: /* @__PURE__ */ jsx(AppBreadcrumbs, { ...activeCrumb }) }),
145
105
  /* @__PURE__ */ jsx(PageActionsOutlet, { className: headerClasses.pageActions }),
146
106
  globalActions && /* @__PURE__ */ jsx("div", { className: headerClasses.global, children: globalActions })
147
107
  ] }) }),
@@ -152,22 +112,20 @@ function BasaltShell({
152
112
  sections,
153
113
  collapsed,
154
114
  onToggleCollapse: toggleCollapse,
155
- onClose: closeMobile,
156
- footerExtra: sidebarFooterExtra,
157
115
  navExtra: sidebarNavExtra,
158
- ...renderNavLink !== void 0 && { renderNavLink },
159
116
  ...settingsMenuItems !== void 0 && { settingsMenuItems },
160
117
  ...account !== void 0 && { account },
161
118
  ...search !== void 0 && { search }
162
119
  }
163
120
  ) }),
164
- /* @__PURE__ */ jsx(AppShell.Main, { children }),
165
- /* @__PURE__ */ jsx(AppShell.Footer, { hiddenFrom: "sm", p: 0, children: /* @__PURE__ */ jsx(
121
+ /* @__PURE__ */ jsx(AppShell.Main, { className: mobileNavClasses.mainSafeArea, children }),
122
+ /* @__PURE__ */ jsx(AppShell.Footer, { hiddenFrom: "sm", p: 0, withBorder: false, children: /* @__PURE__ */ jsx(
166
123
  MobileNav,
167
124
  {
168
- sections: mobileSections,
169
- onOpenMore: toggleMobile,
170
- ...renderNavLink !== void 0 && { renderNavLink }
125
+ model,
126
+ config: mobileNav,
127
+ ...account !== void 0 && { account },
128
+ ...settingsMenuItems !== void 0 && { settingsMenuItems }
171
129
  }
172
130
  ) })
173
131
  ]
@@ -178,11 +136,15 @@ export {
178
136
  AppBreadcrumbs2 as AppBreadcrumbs,
179
137
  AppSidebar2 as AppSidebar,
180
138
  BasaltShell,
139
+ MOBILE_MAX_TABS_DEFAULT,
140
+ MOBILE_MENU_MAX_DEFAULT,
141
+ MOBILE_MORE_KEY,
181
142
  MobileNav2 as MobileNav,
182
143
  NavCountBadge,
183
144
  PageActions,
184
145
  PageActionsOutlet2 as PageActionsOutlet,
185
146
  PageHeaderProvider2 as PageHeaderProvider,
186
147
  SidebarAccount,
187
- SidebarSearch
148
+ SidebarSearch,
149
+ projectMobileNav2 as projectMobileNav
188
150
  };
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The mobile-bar projection — `SidebarSection[]` + `MobileNavConfig` → `MobileNavModel`.
3
+ *
4
+ * PURE BY CONTRACT: no React, no Mantine, no DOM, no JSX. Everything the bottom bar decides —
5
+ * which destinations get a slot, which slot is a plain navigation link and which raises a surface,
6
+ * whether that surface is a menu or a sheet, what falls into the overflow "More" slot — is decided
7
+ * here and unit-testable with no renderer. `app-mobile-nav.tsx` only paints the result.
8
+ *
9
+ * The law it implements is the interaction model, not a heuristic: a slot is a DESTINATION, so a
10
+ * tap navigates with no overlay to dismiss. An overlay exists only where a slot genuinely holds
11
+ * more than one destination, and its surface is INFERRED from how many rows it holds rather than
12
+ * configured — six rows is the arithmetic ceiling for a menu that pops out of a 56px bar without
13
+ * ever rendering below the fold (6 x 44px + 8px padding = 272px against 415px of headroom on the
14
+ * smallest supported viewport), so past that the surface becomes a bottom sheet.
15
+ */
16
+ import type { MobileNavConfig, MobileNavModel, SidebarSection } from '../nav/types.js';
17
+ /** Hard cap on bar slots, INCLUDING More. Five 72px slots is the widest a 360px viewport holds. */
18
+ export declare const MOBILE_MAX_TABS_DEFAULT = 5;
19
+ /**
20
+ * Rows a slot may hold before it becomes a sheet instead of a menu. Arithmetic, not taste — see
21
+ * the module doc: six rows is the last count a bottom-anchored, non-flipping menu fits above the
22
+ * bar on the smallest supported viewport.
23
+ */
24
+ export declare const MOBILE_MENU_MAX_DEFAULT = 6;
25
+ /** The overflow slot's reserved key. A slot like any other — it just exists only when needed. */
26
+ export declare const MOBILE_MORE_KEY = "__more";
27
+ export type ProjectMobileNavOptions = {
28
+ config?: MobileNavConfig | undefined;
29
+ /**
30
+ * Extra rows the More surface will hold — account + settings + `moreExtra`. Drives both
31
+ * `needsMore` and the menu-vs-sheet threshold. The model cannot see those rows (they are
32
+ * Mantine-rendered chrome, not `SidebarItem`s), so the shell counts them and passes the total.
33
+ */
34
+ extraMoreRows?: number;
35
+ };
36
+ /**
37
+ * Projects `sections` onto the bar. Implements §2.3's thirteen rules in order, and is the ONLY
38
+ * place slot selection happens — `MobileNav` receives a finished model and paints it.
39
+ */
40
+ export declare function projectMobileNav(sections: SidebarSection[], opts?: ProjectMobileNavOptions): MobileNavModel;
41
+ //# sourceMappingURL=mobile-nav-model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobile-nav-model.d.ts","sourceRoot":"","sources":["../../src/shell/mobile-nav-model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EACV,eAAe,EAEf,cAAc,EAId,cAAc,EACf,MAAM,cAAc,CAAA;AAErB,mGAAmG;AACnG,eAAO,MAAM,uBAAuB,IAAI,CAAA;AAExC;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,IAAI,CAAA;AAExC,iGAAiG;AACjG,eAAO,MAAM,eAAe,WAAW,CAAA;AAEvC,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,CAAC,EAAE,eAAe,GAAG,SAAS,CAAA;IACpC;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAoQD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,cAAc,EAAE,EAC1B,IAAI,CAAC,EAAE,uBAAuB,GAC7B,cAAc,CA2EhB"}
@@ -0,0 +1,244 @@
1
+ const MOBILE_MAX_TABS_DEFAULT = 5;
2
+ const MOBILE_MENU_MAX_DEFAULT = 6;
3
+ const MOBILE_MORE_KEY = "__more";
4
+ const DEV = process.env["NODE_ENV"] !== "production";
5
+ function placementOf(item, sectionHidden) {
6
+ if (sectionHidden) return "hidden";
7
+ const mobile = item.mobile;
8
+ if (mobile === void 0) return "more";
9
+ if (mobile === true) return "tab";
10
+ if (mobile === false) return "hidden";
11
+ return mobile;
12
+ }
13
+ function isSectionHidden(section) {
14
+ return section.mobile === false;
15
+ }
16
+ function hasActiveDestination(item) {
17
+ if (item.active) return true;
18
+ return item.children?.some(hasActiveDestination) ?? false;
19
+ }
20
+ function countRows(items) {
21
+ return items.reduce((total, item) => total + 1 + countRows(item.children ?? []), 0);
22
+ }
23
+ function collectKeys(items, out) {
24
+ for (const item of items) {
25
+ out.add(item.key);
26
+ collectKeys(item.children ?? [], out);
27
+ }
28
+ }
29
+ function flattenItems(items, out) {
30
+ for (const item of items) {
31
+ out.push(item);
32
+ flattenItems(item.children ?? [], out);
33
+ }
34
+ }
35
+ function pruneTree(items, keep) {
36
+ const out = [];
37
+ for (const item of items) {
38
+ const children = pruneTree(item.children ?? [], keep);
39
+ if (!keep(item)) {
40
+ out.push(...children);
41
+ continue;
42
+ }
43
+ const next = { ...item };
44
+ if (children.length > 0) next.children = children;
45
+ else delete next.children;
46
+ out.push(next);
47
+ }
48
+ return out;
49
+ }
50
+ function itemCandidate(item) {
51
+ return {
52
+ key: item.key,
53
+ covered: /* @__PURE__ */ new Set([item.key]),
54
+ toSlot: () => ({
55
+ kind: "link",
56
+ key: item.key,
57
+ label: item.label,
58
+ short: item.short ?? item.label,
59
+ icon: item.icon,
60
+ active: hasActiveDestination(item),
61
+ item
62
+ })
63
+ };
64
+ }
65
+ function sectionCandidate(section, menuMax) {
66
+ const mobile = section.mobile === false || section.mobile === void 0 ? void 0 : section.mobile;
67
+ const items = pruneTree(section.items, (item) => placementOf(item, false) !== "hidden");
68
+ const covered = /* @__PURE__ */ new Set();
69
+ collectKeys(items, covered);
70
+ const label = mobile?.label ?? section.label;
71
+ const rows = countRows(items);
72
+ const only = items[0];
73
+ return {
74
+ key: section.label,
75
+ covered,
76
+ toSlot: () => {
77
+ if (rows === 0) return void 0;
78
+ if (rows === 1 && only !== void 0 && !only.disabled) {
79
+ return {
80
+ kind: "link",
81
+ key: only.key,
82
+ label,
83
+ short: label,
84
+ // The section's own identity wins when it declares one — the consumer configured the
85
+ // SLOT — and falls back to the single destination's icon when it does not.
86
+ icon: mobile?.icon ?? section.icon ?? only.icon,
87
+ active: hasActiveDestination(only),
88
+ item: only
89
+ };
90
+ }
91
+ return {
92
+ kind: rows <= menuMax ? "menu" : "sheet",
93
+ key: section.label,
94
+ label,
95
+ short: label,
96
+ icon: mobile?.icon ?? section.icon,
97
+ active: items.some(hasActiveDestination),
98
+ // One section, so the rows need no group heading — the tab itself names them.
99
+ groups: [{ key: section.label, items }],
100
+ isMore: false
101
+ };
102
+ }
103
+ };
104
+ }
105
+ function buildCandidates(sections, config, maxTabs, menuMax) {
106
+ const flat = [];
107
+ for (const section of sections) {
108
+ if (isSectionHidden(section)) continue;
109
+ flattenItems(section.items, flat);
110
+ }
111
+ const tabbable = (item) => {
112
+ if (!item.disabled) return true;
113
+ if (DEV) {
114
+ console.warn(
115
+ `[basalt] MobileNav: "${item.key}" asks for a bar slot but is disabled \u2014 a disabled destination never becomes a tab. It still renders, disabled, in the More surface.`
116
+ );
117
+ }
118
+ return false;
119
+ };
120
+ if (config?.tabs !== void 0) {
121
+ const candidates2 = [];
122
+ for (const id of config.tabs) {
123
+ const item = flat.find((candidate) => candidate.key === id);
124
+ if (item !== void 0) {
125
+ if (tabbable(item)) candidates2.push(itemCandidate(item));
126
+ continue;
127
+ }
128
+ const section = sections.find((s) => s.label === id && !isSectionHidden(s));
129
+ if (section !== void 0) {
130
+ candidates2.push(sectionCandidate(section, menuMax));
131
+ continue;
132
+ }
133
+ if (DEV) {
134
+ console.warn(
135
+ `[basalt] MobileNav: mobileNav.tabs names "${id}", which is neither a destination key nor a visible section label \u2014 ignoring it.`
136
+ );
137
+ }
138
+ }
139
+ return candidates2;
140
+ }
141
+ const candidates = [];
142
+ for (const section of sections) {
143
+ if (isSectionHidden(section)) continue;
144
+ if (section.mobile !== void 0 && section.mobile !== false && section.mobile.tab === true) {
145
+ candidates.push(sectionCandidate(section, menuMax));
146
+ continue;
147
+ }
148
+ const items = [];
149
+ flattenItems(section.items, items);
150
+ for (const item of items) {
151
+ if (placementOf(item, false) !== "tab") continue;
152
+ if (tabbable(item)) candidates.push(itemCandidate(item));
153
+ }
154
+ }
155
+ if (candidates.length > 0) return candidates;
156
+ const fallback = [];
157
+ for (const section of sections) {
158
+ if (isSectionHidden(section)) continue;
159
+ for (const item of section.items) {
160
+ if (fallback.length >= maxTabs - 1) return fallback;
161
+ if (item.disabled) continue;
162
+ if (placementOf(item, false) === "hidden") continue;
163
+ fallback.push(itemCandidate(item));
164
+ }
165
+ }
166
+ return fallback;
167
+ }
168
+ function buildOverflow(sections, covered) {
169
+ const groups = [];
170
+ for (const section of sections) {
171
+ const hidden = isSectionHidden(section);
172
+ if (hidden) continue;
173
+ const items = pruneTree(
174
+ section.items,
175
+ (item) => placementOf(item, hidden) !== "hidden" && !covered.has(item.key)
176
+ );
177
+ if (items.length === 0) continue;
178
+ groups.push({ key: section.label, label: section.label, items });
179
+ }
180
+ return groups;
181
+ }
182
+ function projectMobileNav(sections, opts) {
183
+ const config = opts?.config;
184
+ const maxTabs = Math.max(1, config?.maxTabs ?? MOBILE_MAX_TABS_DEFAULT);
185
+ const menuMax = Math.max(1, config?.menuMax ?? MOBILE_MENU_MAX_DEFAULT);
186
+ const extraMoreRows = opts?.extraMoreRows ?? 0;
187
+ const candidates = buildCandidates(sections, config, maxTabs, menuMax);
188
+ const coveredByAll = /* @__PURE__ */ new Set();
189
+ for (const candidate of candidates) for (const key of candidate.covered) coveredByAll.add(key);
190
+ const overflowIfNoneDropped = buildOverflow(sections, coveredByAll);
191
+ const needsMore = countRows(overflowIfNoneDropped.flatMap((group) => group.items)) > 0 || extraMoreRows > 0 || candidates.length > maxTabs;
192
+ const limit = needsMore ? maxTabs - 1 : maxTabs;
193
+ const kept = candidates.slice(0, limit);
194
+ const dropped = candidates.slice(limit);
195
+ if (DEV && dropped.length > 0) {
196
+ console.warn(
197
+ `[basalt] MobileNav: ${candidates.length} slot candidates for ${limit} available slot(s) \u2014 the last ${dropped.length} moved into the More surface: ${dropped.map((candidate) => `"${candidate.key}"`).join(", ")}. Raise mobileNav.maxTabs or drop a mobile: 'tab'.`
198
+ );
199
+ }
200
+ const covered = /* @__PURE__ */ new Set();
201
+ for (const candidate of kept) for (const key of candidate.covered) covered.add(key);
202
+ const slots = [];
203
+ for (const candidate of kept) {
204
+ const slot = candidate.toSlot();
205
+ if (slot !== void 0) slots.push(slot);
206
+ }
207
+ if (!needsMore) return { slots };
208
+ const groups = buildOverflow(sections, covered);
209
+ const overflowItems = groups.flatMap((group) => group.items);
210
+ const rows = countRows(overflowItems) + extraMoreRows;
211
+ if (rows === 0) return { slots };
212
+ const label = config?.moreLabel ?? "More";
213
+ const only = overflowItems[0];
214
+ if (rows === 1 && extraMoreRows === 0 && only !== void 0 && !only.disabled) {
215
+ slots.push({
216
+ kind: "link",
217
+ key: only.key,
218
+ label: only.label,
219
+ short: only.short ?? only.label,
220
+ icon: only.icon,
221
+ active: hasActiveDestination(only),
222
+ item: only
223
+ });
224
+ return { slots };
225
+ }
226
+ slots.push({
227
+ kind: rows <= menuMax ? "menu" : "sheet",
228
+ key: MOBILE_MORE_KEY,
229
+ label,
230
+ short: label,
231
+ // No icon: the More glyph is chrome the renderer owns, and this module holds no JSX.
232
+ icon: void 0,
233
+ active: overflowItems.some(hasActiveDestination),
234
+ groups,
235
+ isMore: true
236
+ });
237
+ return { slots };
238
+ }
239
+ export {
240
+ MOBILE_MAX_TABS_DEFAULT,
241
+ MOBILE_MENU_MAX_DEFAULT,
242
+ MOBILE_MORE_KEY,
243
+ projectMobileNav
244
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mobile-nav-model.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobile-nav-model.test.d.ts","sourceRoot":"","sources":["../../src/shell/mobile-nav-model.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Sidebar nav count badge (docs/DESIGN-SPEC.md §5): mono 10.5px, ink-8% bg, radius 5, height 16,
3
+ * padding 0 5px, muted text; `marginLeft: auto` pins it to the row end on any render path.
4
+ * `styles` (inline) rather than a token color prop, since none of Mantine's variant/color
5
+ * combinations land on the ink-tint idiom. Returns `null` for a zero/empty count so the badge slot
6
+ * stays clean ("ink earns its color", DESIGN.md).
7
+ */
8
+ export declare function NavCountBadge({ count }: {
9
+ count: number;
10
+ }): import("react").JSX.Element | null;
11
+ //# sourceMappingURL=nav-count-badge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nav-count-badge.d.ts","sourceRoot":"","sources":["../../src/shell/nav-count-badge.tsx"],"names":[],"mappings":"AASA;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,sCAsBzD"}
@@ -0,0 +1,29 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Badge } from "@mantine/core";
3
+ import { VX } from "../tokens/index.js";
4
+ function NavCountBadge({ count }) {
5
+ if (!count) return null;
6
+ return /* @__PURE__ */ jsx(
7
+ Badge,
8
+ {
9
+ size: "sm",
10
+ styles: {
11
+ root: {
12
+ backgroundColor: "color-mix(in srgb, var(--vx-ink) 8%, transparent)",
13
+ color: "var(--vx-muted)",
14
+ fontFamily: "var(--basalt-font-mono)",
15
+ fontSize: VX.text.micro,
16
+ fontWeight: 500,
17
+ height: 16,
18
+ padding: "0 5px",
19
+ marginLeft: "auto",
20
+ borderRadius: "var(--vx-radius-tight)"
21
+ }
22
+ },
23
+ children: count
24
+ }
25
+ );
26
+ }
27
+ export {
28
+ NavCountBadge
29
+ };
@@ -209,7 +209,7 @@ export declare const SURFACES: {
209
209
  readonly rule: "router";
210
210
  readonly skill: readonly ["basalt-app"];
211
211
  readonly guardKinds: readonly [];
212
- readonly description: "TanStack Router bridge: useBasaltNav (active route) + useRouterBreadcrumbs + createSearchParamStore (single-select URL-state store) + createMultiSearchParamStore (multi-select URL-state store)";
212
+ readonly description: "TanStack Router bridge: defineNav/navGroup/navTarget (one typed nav definition) + useNav (sections + mobileNav, spread onto BasaltShell) + useBasaltNav (active route) + useRouterBreadcrumbs + createSearchParamStore (single-select URL-state store) + createMultiSearchParamStore (multi-select URL-state store)";
213
213
  readonly optionalPeers: readonly ["@tanstack/react-router"];
214
214
  readonly globs: {
215
215
  readonly shipped: readonly [];
package/dist/surfaces.js CHANGED
@@ -120,7 +120,7 @@ const SURFACES = {
120
120
  rule: "router",
121
121
  skill: ["basalt-app"],
122
122
  guardKinds: [],
123
- description: "TanStack Router bridge: useBasaltNav (active route) + useRouterBreadcrumbs + createSearchParamStore (single-select URL-state store) + createMultiSearchParamStore (multi-select URL-state store)",
123
+ description: "TanStack Router bridge: defineNav/navGroup/navTarget (one typed nav definition) + useNav (sections + mobileNav, spread onto BasaltShell) + useBasaltNav (active route) + useRouterBreadcrumbs + createSearchParamStore (single-select URL-state store) + createMultiSearchParamStore (multi-select URL-state store)",
124
124
  optionalPeers: ["@tanstack/react-router"],
125
125
  globs: {
126
126
  shipped: [],
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AA+DA,OAAO,KAAK,EACV,oBAAoB,EAGpB,oBAAoB,EAErB,MAAM,eAAe,CAAA;AAStB,OAAO,KAAK,EAAe,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAE/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAEpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAOlD;;;;;GAKG;AACH,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAIlD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AASvD,OAAO,QAAQ,eAAe,CAAC;IAC7B,UAAU,iBAAiB;QACzB,YAAY,CAAC,EAAE,YAAY,CAAA;QAC3B,WAAW,CAAC,EAAE,iBAAiB,CAAA;QAC/B,YAAY,CAAC,EAAE,YAAY,CAAA;QAC3B,aAAa,CAAC,EAAE,WAAW,CAAA;KAC5B;CACF;AAk5BD;+EAC+E;AAC/E,eAAO,MAAM,SAAS,EAAE,oBAAuD,CAAA;AAmJ/E;;8FAE8F;AAC9F,eAAO,MAAM,oBAAoB,EAAE,oBACc,CAAA;AAEjD,kEAAkE;AAClE,MAAM,MAAM,wBAAwB,GAAG;IACrC;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;IAE9B;;;;;;;;;;;;;;;;OAgBG;IACH,KAAK,CAAC,EAAE,iBAAiB,CAAA;IAEzB;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAYD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,CAAC,EAAE,oBAAoB,EAChC,OAAO,CAAC,EAAE,wBAAwB,GACjC,oBAAoB,CA+BtB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AA+DA,OAAO,KAAK,EACV,oBAAoB,EAGpB,oBAAoB,EAErB,MAAM,eAAe,CAAA;AAStB,OAAO,KAAK,EAAe,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAE/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAEpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAOlD;;;;;GAKG;AACH,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAIlD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AASvD,OAAO,QAAQ,eAAe,CAAC;IAC7B,UAAU,iBAAiB;QACzB,YAAY,CAAC,EAAE,YAAY,CAAA;QAC3B,WAAW,CAAC,EAAE,iBAAiB,CAAA;QAC/B,YAAY,CAAC,EAAE,YAAY,CAAA;QAC3B,aAAa,CAAC,EAAE,WAAW,CAAA;KAC5B;CACF;AAm5BD;+EAC+E;AAC/E,eAAO,MAAM,SAAS,EAAE,oBAAuD,CAAA;AAmJ/E;;8FAE8F;AAC9F,eAAO,MAAM,oBAAoB,EAAE,oBACc,CAAA;AAEjD,kEAAkE;AAClE,MAAM,MAAM,wBAAwB,GAAG;IACrC;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;IAE9B;;;;;;;;;;;;;;;;OAgBG;IACH,KAAK,CAAC,EAAE,iBAAiB,CAAA;IAEzB;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAYD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,CAAC,EAAE,oBAAoB,EAChC,OAAO,CAAC,EAAE,wBAAwB,GACjC,oBAAoB,CA+BtB"}
@@ -304,10 +304,10 @@ function buildTheme(data, options = {}) {
304
304
  // panel-filled row read as a button sitting in the nav rather than as the current location.
305
305
  // Selection is carried by the tint + ink text + weight 600 + the accent icon instead. Forced
306
306
  // here at the THEME level via NavLink's `--nl-*` vars so it holds for every render path —
307
- // including a consumer's router `<Link>` via `renderNavLink`, which never sees the shell CSS
308
- // module; the active ICON is accent-colored via `nav-link.module.css` (targets the
309
- // `[data-position='left']` leftSection, which the flat `vars`/`styles` API can't express
310
- // conditionally on `[data-active]`).
307
+ // including a consumer's router `<Link>` hosted through `SidebarItem.Anchor`, which never
308
+ // sees the shell CSS module; the active ICON is accent-colored via `nav-link.module.css`
309
+ // (targets the `[data-position='left']` leftSection, which the flat `vars`/`styles` API
310
+ // can't express conditionally on `[data-active]`).
311
311
  //
312
312
  // `--nl-hover` (ink-13%) only ever reaches an ACTIVE row in practice — the inactive hover is
313
313
  // set at ink-6% by higher-specificity `:not([data-active])` rules in `nav-link.module.css` /
@@ -323,9 +323,10 @@ function buildTheme(data, options = {}) {
323
323
  children: {}
324
324
  }),
325
325
  // Dense, rounded nav rows — applied at the THEME level so they survive EVERY render path,
326
- // including a consumer's router `<Link>` via `renderNavLink`, which never sees the shell CSS
327
- // module (the module's `.link` only reaches the shell's own fallback `<NavLink>`). Same
328
- // reasoning as the `--nl-*` fill above: layout is single-sourced here, not in two places.
326
+ // including a consumer's router `<Link>` hosted through `SidebarItem.Anchor`, which never
327
+ // sees the shell CSS module (the module's `.link` only reaches the shell's own fallback
328
+ // `<NavLink>`). Same reasoning as the `--nl-*` fill above: layout is single-sourced here,
329
+ // not in two places.
329
330
  // The active-weight/shadow/icon-accent state selectors can't live in `styles` (flat inline
330
331
  // props only) — they're in nav-link.module.css, wired via `classNames` so they reach the
331
332
  // same every-render-path scope.
@@ -5,7 +5,8 @@
5
5
  * tint rather than as a button, so the row never competes with the real filled controls around it.
6
6
  *
7
7
  * Applied at the THEME level via `classNames` so it reaches EVERY NavLink render path — including a
8
- * consumer's router `<Link>` mounted through `renderNavLink`, which a shell-scoped CSS module never
8
+ * consumer's router `<Link>` mounted through `SidebarItem.Anchor`, which a shell-scoped CSS
9
+ * module never
9
10
  * sees. Mantine's `styles` prop only takes flat inline properties (a nested `&[data-active]` there
10
11
  * throws a React warning), so the state selectors live here. React Router's NavLink marks the
11
12
  * active item via `aria-current` rather than the `active` prop's `data-active`, so both states are
@@ -42,7 +43,8 @@
42
43
 
43
44
  /*
44
45
  * Inactive hover — the ink-6% ghost tint + ink text/icon (docs/DESIGN-SPEC.md §5). Lives here (not
45
- * only in the shell module) so a consumer's `renderNavLink` path gets the same hover; Mantine's own
46
+ * only in the shell module) so a consumer's `SidebarItem.Anchor` path gets the same hover;
47
+ * Mantine's own
46
48
  * hover fill uses `:where(scheme) .m_*:hover` (gray-0/dark-6), which this outranks on specificity.
47
49
  * Active rows keep their panel fill + `--nl-hover` untouched.
48
50
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tokens/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tokens/index.ts"],"names":[],"mappings":"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"}
@@ -357,6 +357,8 @@ function spaceDecls(space) {
357
357
  decl("space-sidebar-child-row-indent", `${space.step.sidebarChildRowIndent}px`),
358
358
  decl("space-app-header-mobile-actions-height", `${space.step.appHeaderMobileActionsHeight}px`),
359
359
  decl("space-mobile-nav-tab-gap", `${space.step.mobileNavTabGap}px`),
360
+ decl("space-mobile-nav-icon-size", `${space.step.mobileNavIconSize}px`),
361
+ decl("space-mobile-nav-row-height", `${space.step.mobileNavRowHeight}px`),
360
362
  decl("space-agent-rail-inset-x", `${space.step.agentRailInsetX}px`),
361
363
  decl("space-agent-part-gap-top", `${space.step.agentPartGapTop}px`),
362
364
  decl("space-agent-code-inset", `${space.step.agentCodeInset}px`),