basalt-ui 1.25.0 → 1.27.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.
- package/AGENTS.md +8 -6
- package/CHANGELOG.md +26 -0
- package/MIGRATING.md +815 -23
- package/README.md +45 -32
- package/agent/rules/basalt-batteries.md +205 -0
- package/agent/rules/basalt-charts.md +125 -555
- package/agent/rules/basalt-controls.md +160 -0
- package/agent/rules/basalt-mantine.md +146 -439
- package/agent/rules/basalt-state.md +118 -96
- package/agent/rules/basalt-tokens.md +126 -288
- package/agent/skills/basalt-app/SKILL.md +70 -195
- package/agent/skills/basalt-charts/SKILL.md +54 -234
- package/agent/skills/basalt-design/SKILL.md +56 -248
- package/agent/templates/CLAUDE-block.md.tpl +31 -40
- package/agent/templates/DESIGN.md.tpl +22 -51
- package/configs/oxlint-plugin.js +1465 -36
- package/configs/oxlint-plugin.test.ts +1190 -11
- package/configs/oxlint.json +11 -3
- package/dist/charts/cursor/resolve.d.ts +18 -0
- package/dist/charts/cursor/resolve.d.ts.map +1 -1
- package/dist/charts/cursor/resolve.js +15 -6
- package/dist/charts/cursor/scope.d.ts +6 -3
- package/dist/charts/cursor/scope.d.ts.map +1 -1
- package/dist/charts/cursor/store.d.ts +5 -1
- package/dist/charts/cursor/store.d.ts.map +1 -1
- package/dist/charts/cursor/store.js +4 -4
- package/dist/charts/hooks/useChartCursor.d.ts.map +1 -1
- package/dist/charts/hooks/useChartCursor.js +9 -8
- package/dist/charts/index.d.ts +2 -2
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.d.ts +34 -9
- package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.js +29 -114
- package/dist/charts/primitives/ChartCard.test.d.ts +2 -0
- package/dist/charts/primitives/ChartCard.test.d.ts.map +1 -0
- package/dist/charts/sparklines/BarSparkline.d.ts +26 -2
- package/dist/charts/sparklines/BarSparkline.d.ts.map +1 -1
- package/dist/charts/sparklines/BarSparkline.js +18 -4
- package/dist/cli/check-coverage.test.d.ts +2 -0
- package/dist/cli/check-coverage.test.d.ts.map +1 -0
- package/dist/cli/index.d.ts +67 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +225 -7
- package/dist/connectivity/connectivity-indicator.d.ts +4 -1
- package/dist/connectivity/connectivity-indicator.d.ts.map +1 -1
- package/dist/connectivity/connectivity-indicator.js +1 -1
- package/dist/content/index.d.ts +5 -3
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +4 -2
- package/dist/content/prose.module.css +7 -28
- package/dist/controls/actions.d.ts +169 -0
- package/dist/controls/actions.d.ts.map +1 -0
- package/dist/controls/actions.js +287 -0
- package/dist/controls/actions.module.css +62 -0
- package/dist/controls/actions.module.css.d.ts +13 -0
- package/dist/controls/actions.test.d.ts +2 -0
- package/dist/controls/actions.test.d.ts.map +1 -0
- package/dist/controls/actions.type-guard.test.d.ts +2 -0
- package/dist/controls/actions.type-guard.test.d.ts.map +1 -0
- package/dist/controls/compare-filter.d.ts +41 -0
- package/dist/controls/compare-filter.d.ts.map +1 -0
- package/dist/controls/compare-filter.js +23 -0
- package/dist/controls/control-group.d.ts +19 -0
- package/dist/controls/control-group.d.ts.map +1 -0
- package/dist/controls/control-group.js +20 -0
- package/dist/controls/control-group.test.d.ts +2 -0
- package/dist/controls/control-group.test.d.ts.map +1 -0
- package/dist/controls/controls.module.css +542 -0
- package/dist/controls/controls.module.css.d.ts +35 -0
- package/dist/controls/controls.router.test.d.ts +2 -0
- package/dist/controls/controls.router.test.d.ts.map +1 -0
- package/dist/controls/enum-filter.d.ts +39 -0
- package/dist/controls/enum-filter.d.ts.map +1 -0
- package/dist/controls/enum-filter.js +93 -0
- package/dist/controls/filter-context.d.ts +60 -0
- package/dist/controls/filter-context.d.ts.map +1 -0
- package/dist/controls/filter-context.js +69 -0
- package/dist/controls/filter-pill.d.ts +28 -0
- package/dist/controls/filter-pill.d.ts.map +1 -0
- package/dist/controls/filter-pill.js +70 -0
- package/dist/controls/filter-set.d.ts +13 -0
- package/dist/controls/filter-set.d.ts.map +1 -0
- package/dist/controls/filter-set.js +125 -0
- package/dist/controls/filter-set.test.d.ts +2 -0
- package/dist/controls/filter-set.test.d.ts.map +1 -0
- package/dist/controls/filter-sheet.d.ts +117 -0
- package/dist/controls/filter-sheet.d.ts.map +1 -0
- package/dist/controls/filter-sheet.js +146 -0
- package/dist/controls/glyphs.d.ts +27 -0
- package/dist/controls/glyphs.d.ts.map +1 -0
- package/dist/controls/glyphs.js +49 -0
- package/dist/controls/index.d.ts +20 -0
- package/dist/controls/index.d.ts.map +1 -0
- package/dist/controls/index.js +46 -0
- package/dist/controls/multi-select-filter.d.ts +23 -0
- package/dist/controls/multi-select-filter.d.ts.map +1 -0
- package/dist/controls/multi-select-filter.js +89 -0
- package/dist/controls/number-filter.d.ts +26 -0
- package/dist/controls/number-filter.d.ts.map +1 -0
- package/dist/controls/number-filter.js +143 -0
- package/dist/controls/peer-boundary.test.d.ts +2 -0
- package/dist/controls/peer-boundary.test.d.ts.map +1 -0
- package/dist/controls/props-contract.test.d.ts +2 -0
- package/dist/controls/props-contract.test.d.ts.map +1 -0
- package/dist/controls/range-filter.d.ts +43 -0
- package/dist/controls/range-filter.d.ts.map +1 -0
- package/dist/controls/range-filter.js +135 -0
- package/dist/controls/range-filter.type-guard.test.d.ts +2 -0
- package/dist/controls/range-filter.type-guard.test.d.ts.map +1 -0
- package/dist/controls/search-filter.d.ts +10 -0
- package/dist/controls/search-filter.d.ts.map +1 -0
- package/dist/controls/search-filter.js +81 -0
- package/dist/controls/select-filter.d.ts +74 -0
- package/dist/controls/select-filter.d.ts.map +1 -0
- package/dist/controls/select-filter.js +10 -0
- package/dist/controls/sync-button.d.ts +38 -0
- package/dist/controls/sync-button.d.ts.map +1 -0
- package/dist/controls/sync-button.js +119 -0
- package/dist/controls/sync-button.module.css +63 -0
- package/dist/controls/sync-button.module.css.d.ts +11 -0
- package/dist/controls/sync-button.test.d.ts +2 -0
- package/dist/controls/sync-button.test.d.ts.map +1 -0
- package/dist/controls/toggle-filter.d.ts +9 -0
- package/dist/controls/toggle-filter.d.ts.map +1 -0
- package/dist/controls/toggle-filter.js +42 -0
- package/dist/controls/view-tabs.d.ts +23 -0
- package/dist/controls/view-tabs.d.ts.map +1 -0
- package/dist/controls/view-tabs.js +59 -0
- package/dist/controls-dates/date-range-picker.d.ts +4 -0
- package/dist/controls-dates/date-range-picker.d.ts.map +1 -0
- package/dist/controls-dates/date-range-picker.js +18 -0
- package/dist/controls-dates/index.d.ts +9 -0
- package/dist/controls-dates/index.d.ts.map +1 -0
- package/dist/controls-dates/index.js +4 -0
- package/dist/dashboard/delta-badge.d.ts +6 -12
- package/dist/dashboard/delta-badge.d.ts.map +1 -1
- package/dist/dashboard/delta-badge.js +1 -50
- package/dist/dashboard/index.d.ts +1 -1
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +3 -1
- package/dist/dashboard/settings-section.d.ts +6 -4
- package/dist/dashboard/settings-section.d.ts.map +1 -1
- package/dist/dashboard/settings-section.js +23 -16
- package/dist/dashboard/settings-section.module.css +10 -0
- package/dist/dashboard/settings-section.module.css.d.ts +1 -0
- package/dist/dashboard/settings-section.test.d.ts +2 -0
- package/dist/dashboard/settings-section.test.d.ts.map +1 -0
- package/dist/dashboard/stat-card.d.ts +95 -9
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +108 -49
- package/dist/dashboard/stat-card.module.css +153 -0
- package/dist/dashboard/stat-card.module.css.d.ts +15 -0
- package/dist/data/data-table.d.ts +37 -15
- package/dist/data/data-table.d.ts.map +1 -1
- package/dist/data/data-table.js +96 -58
- package/dist/guard/grace.test.d.ts +20 -0
- package/dist/guard/grace.test.d.ts.map +1 -0
- package/dist/guard/index.d.ts +66 -3
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +78 -7
- package/dist/guard/types.d.ts +3 -3
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/index.d.ts +13 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -10
- package/dist/nav/types.d.ts +78 -2
- package/dist/nav/types.d.ts.map +1 -1
- package/dist/notifications/bell.d.ts +4 -1
- package/dist/notifications/bell.d.ts.map +1 -1
- package/dist/notifications/index.d.ts +1 -1
- package/dist/router-tanstack/field.d.ts +14 -0
- package/dist/router-tanstack/field.d.ts.map +1 -0
- package/dist/router-tanstack/field.js +4 -0
- package/dist/router-tanstack/index.d.ts +6 -2
- package/dist/router-tanstack/index.d.ts.map +1 -1
- package/dist/router-tanstack/index.js +4 -0
- package/dist/router-tanstack/multi-search-param-store.d.ts +4 -52
- package/dist/router-tanstack/multi-search-param-store.d.ts.map +1 -1
- package/dist/router-tanstack/multi-search-param-store.js +19 -27
- package/dist/router-tanstack/search-param-store.d.ts +3 -87
- package/dist/router-tanstack/search-param-store.d.ts.map +1 -1
- package/dist/router-tanstack/search-param-store.js +16 -45
- package/dist/router-tanstack/search-store.d.ts +84 -0
- package/dist/router-tanstack/search-store.d.ts.map +1 -0
- package/dist/router-tanstack/search-store.js +271 -0
- package/dist/router-tanstack/search-store.router.test.d.ts +2 -0
- package/dist/router-tanstack/search-store.router.test.d.ts.map +1 -0
- package/dist/router-tanstack/search-store.test.d.ts +2 -0
- package/dist/router-tanstack/search-store.test.d.ts.map +1 -0
- package/dist/router-tanstack/use-nav.d.ts +0 -2
- package/dist/router-tanstack/use-nav.d.ts.map +1 -1
- package/dist/router-tanstack/use-nav.js +34 -12
- package/dist/router-tanstack/use-nav.test.d.ts +2 -0
- package/dist/router-tanstack/use-nav.test.d.ts.map +1 -0
- package/dist/section/index.d.ts +2 -0
- package/dist/section/index.d.ts.map +1 -0
- package/dist/section/index.js +4 -0
- package/dist/section/section.d.ts +24 -0
- package/dist/section/section.d.ts.map +1 -0
- package/dist/section/section.js +126 -0
- package/dist/section/section.module.css +104 -0
- package/dist/section/section.module.css.d.ts +11 -0
- package/dist/section/section.test.d.ts +2 -0
- package/dist/section/section.test.d.ts.map +1 -0
- package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
- package/dist/shell/app-breadcrumbs.js +18 -15
- package/dist/shell/app-header.module.css +77 -37
- package/dist/shell/app-header.module.css.d.ts +2 -1
- package/dist/shell/app-mobile-nav.d.ts +9 -2
- package/dist/shell/app-mobile-nav.d.ts.map +1 -1
- package/dist/shell/app-mobile-nav.js +98 -11
- package/dist/shell/app-mobile-nav.module.css +8 -0
- package/dist/shell/app-mobile-nav.module.css.d.ts +1 -0
- package/dist/shell/app-sidebar.d.ts +55 -28
- package/dist/shell/app-sidebar.d.ts.map +1 -1
- package/dist/shell/app-sidebar.js +116 -67
- package/dist/shell/app-sidebar.module.css +303 -20
- package/dist/shell/app-sidebar.module.css.d.ts +19 -1
- package/dist/shell/index.d.ts +55 -21
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +40 -18
- package/dist/shell/mobile-nav-model.d.ts +13 -2
- package/dist/shell/mobile-nav-model.d.ts.map +1 -1
- package/dist/shell/mobile-nav-model.js +15 -1
- package/dist/shell/nav-count-badge.d.ts +1 -1
- package/dist/shell/page-bar.d.ts +51 -0
- package/dist/shell/page-bar.d.ts.map +1 -0
- package/dist/shell/page-bar.js +154 -0
- package/dist/shell/page-bar.module.css +178 -0
- package/dist/shell/page-bar.module.css.d.ts +17 -0
- package/dist/shell/page-bar.test.d.ts +2 -0
- package/dist/shell/page-bar.test.d.ts.map +1 -0
- package/dist/shell/sidebar-block-model.d.ts +48 -0
- package/dist/shell/sidebar-block-model.d.ts.map +1 -0
- package/dist/shell/sidebar-block-model.js +39 -0
- package/dist/shell/sidebar-blocks.d.ts +32 -0
- package/dist/shell/sidebar-blocks.d.ts.map +1 -0
- package/dist/shell/sidebar-blocks.js +169 -0
- package/dist/shell/sidebar-search.d.ts +13 -1
- package/dist/shell/sidebar-search.d.ts.map +1 -1
- package/dist/shell/sidebar-search.js +34 -1
- package/dist/shell/sidebar-search.module.css +26 -0
- package/dist/shell/sidebar-search.module.css.d.ts +2 -0
- package/dist/state/fields.d.ts +565 -0
- package/dist/state/fields.d.ts.map +1 -0
- package/dist/state/fields.js +405 -0
- package/dist/state/fields.test.d.ts +2 -0
- package/dist/state/fields.test.d.ts.map +1 -0
- package/dist/state/persisted.d.ts +60 -0
- package/dist/state/persisted.d.ts.map +1 -0
- package/dist/state/persisted.js +123 -0
- package/dist/state/persisted.test.d.ts +2 -0
- package/dist/state/persisted.test.d.ts.map +1 -0
- package/dist/state.d.ts +11 -56
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +14 -120
- package/dist/styles.css +13 -0
- package/dist/surfaces.d.ts +106 -24
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +156 -19
- package/dist/theme/controls.module.css +16 -1
- package/dist/theme/ctl-theme.d.ts +32 -0
- package/dist/theme/ctl-theme.d.ts.map +1 -0
- package/dist/theme/ctl-theme.js +54 -0
- package/dist/theme/ctl-theme.test.d.ts +2 -0
- package/dist/theme/ctl-theme.test.d.ts.map +1 -0
- package/dist/theme/ctl-tier-coverage.test.d.ts +2 -0
- package/dist/theme/ctl-tier-coverage.test.d.ts.map +1 -0
- package/dist/theme/divider-law.test.d.ts +2 -0
- package/dist/theme/divider-law.test.d.ts.map +1 -0
- package/dist/theme/icon-slot.d.ts +44 -0
- package/dist/theme/icon-slot.d.ts.map +1 -0
- package/dist/theme/icon-slot.js +21 -0
- package/dist/theme/icon-slot.module.css +47 -0
- package/dist/theme/icon-slot.module.css.d.ts +8 -0
- package/dist/theme/icon-slot.test.d.ts +2 -0
- package/dist/theme/icon-slot.test.d.ts.map +1 -0
- package/dist/theme/index.d.ts +1 -0
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +82 -5
- package/dist/theme/layout-rhythm-css.test.d.ts +2 -0
- package/dist/theme/layout-rhythm-css.test.d.ts.map +1 -0
- package/dist/theme/segmented-control.module.css +20 -0
- package/dist/theme/shadow-surfaces.test.d.ts.map +1 -1
- package/dist/tokens/index.d.ts +12 -0
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +38 -8
- package/dist/tokens/palette.d.ts +97 -61
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +76 -38
- package/dist/tokens.css +10 -2
- package/dist/widget-header/delta-badge.d.ts +13 -0
- package/dist/widget-header/delta-badge.d.ts.map +1 -0
- package/dist/widget-header/delta-badge.js +29 -0
- package/dist/widget-header/delta-badge.test.d.ts +2 -0
- package/dist/widget-header/delta-badge.test.d.ts.map +1 -0
- package/dist/widget-header/index.d.ts +3 -0
- package/dist/widget-header/index.d.ts.map +1 -0
- package/dist/widget-header/index.js +6 -0
- package/dist/widget-header/no-mantine.test.d.ts +2 -0
- package/dist/widget-header/no-mantine.test.d.ts.map +1 -0
- package/dist/widget-header/widget-header.d.ts +67 -0
- package/dist/widget-header/widget-header.d.ts.map +1 -0
- package/dist/widget-header/widget-header.js +89 -0
- package/dist/widget-header/widget-header.module.css +350 -0
- package/dist/widget-header/widget-header.module.css.d.ts +25 -0
- package/dist/widget-header/widget-header.test.d.ts +2 -0
- package/dist/widget-header/widget-header.test.d.ts.map +1 -0
- package/llms.txt +30 -17
- package/package.json +14 -1
- package/src/charts/cursor/resolve.test.ts +29 -1
- package/src/charts/cursor/resolve.ts +38 -6
- package/src/charts/cursor/scope.tsx +6 -3
- package/src/charts/cursor/store.test.ts +19 -1
- package/src/charts/cursor/store.ts +10 -5
- package/src/charts/hooks/useChartCursor.ts +16 -8
- package/src/charts/index.ts +2 -2
- package/src/charts/primitives/CartesianChart.test.tsx +124 -0
- package/src/charts/primitives/ChartCard.test.tsx +66 -0
- package/src/charts/primitives/ChartCard.tsx +86 -146
- package/src/charts/sparklines/BarSparkline.tsx +43 -5
- package/src/cli/check-coverage.test.ts +297 -0
- package/src/cli/check-theme.test.ts +38 -14
- package/src/cli/index.ts +392 -16
- package/src/cli/init-sync.test.ts +97 -0
- package/src/connectivity/connectivity-indicator.tsx +16 -9
- package/src/content/index.ts +16 -3
- package/src/content/prose.module.css +7 -28
- package/src/content/prose.module.css.test.ts +8 -24
- package/src/controls/actions.module.css +62 -0
- package/src/controls/actions.module.css.d.ts +13 -0
- package/src/controls/actions.test.tsx +252 -0
- package/src/controls/actions.tsx +655 -0
- package/src/controls/actions.type-guard.test.ts +63 -0
- package/src/controls/compare-filter.tsx +64 -0
- package/src/controls/control-group.test.tsx +229 -0
- package/src/controls/control-group.tsx +63 -0
- package/src/controls/controls.module.css +542 -0
- package/src/controls/controls.module.css.d.ts +35 -0
- package/src/controls/controls.router.test.tsx +1053 -0
- package/src/controls/enum-filter.tsx +170 -0
- package/src/controls/filter-context.tsx +158 -0
- package/src/controls/filter-pill.tsx +120 -0
- package/src/controls/filter-set.test.tsx +472 -0
- package/src/controls/filter-set.tsx +225 -0
- package/src/controls/filter-sheet.tsx +285 -0
- package/src/controls/glyphs.tsx +81 -0
- package/src/controls/index.ts +49 -0
- package/src/controls/multi-select-filter.tsx +140 -0
- package/src/controls/number-filter.tsx +291 -0
- package/src/controls/peer-boundary.test.ts +85 -0
- package/src/controls/props-contract.test.tsx +137 -0
- package/src/controls/range-filter.tsx +237 -0
- package/src/controls/range-filter.type-guard.test.tsx +122 -0
- package/src/controls/search-filter.tsx +123 -0
- package/src/controls/select-filter.tsx +89 -0
- package/src/controls/sync-button.module.css +63 -0
- package/src/controls/sync-button.module.css.d.ts +11 -0
- package/src/controls/sync-button.test.tsx +183 -0
- package/src/controls/sync-button.tsx +197 -0
- package/src/controls/toggle-filter.tsx +60 -0
- package/src/controls/view-tabs.tsx +110 -0
- package/src/controls-dates/date-range-picker.tsx +42 -0
- package/src/controls-dates/index.ts +8 -0
- package/src/dashboard/delta-badge.tsx +4 -74
- package/src/dashboard/index.ts +6 -1
- package/src/dashboard/settings-section.module.css +10 -0
- package/src/dashboard/settings-section.module.css.d.ts +1 -0
- package/src/dashboard/settings-section.test.tsx +56 -0
- package/src/dashboard/settings-section.tsx +33 -30
- package/src/dashboard/stat-card.module.css +153 -0
- package/src/dashboard/stat-card.module.css.d.ts +15 -0
- package/src/dashboard/stat-card.test.tsx +298 -1
- package/src/dashboard/stat-card.tsx +289 -67
- package/src/data/data-table.test.tsx +96 -11
- package/src/data/data-table.tsx +228 -84
- package/src/guard/check-source.test.ts +159 -13
- package/src/guard/grace.test.ts +158 -0
- package/src/guard/index.ts +185 -29
- package/src/guard/types.ts +4 -2
- package/src/index.ts +51 -5
- package/src/nav/types.ts +84 -2
- package/src/notifications/bell.tsx +6 -3
- package/src/notifications/index.ts +1 -1
- package/src/router-tanstack/field.ts +35 -0
- package/src/router-tanstack/index.ts +28 -2
- package/src/router-tanstack/multi-search-param-store.test.ts +71 -3
- package/src/router-tanstack/multi-search-param-store.ts +46 -77
- package/src/router-tanstack/search-param-store.ts +42 -159
- package/src/router-tanstack/search-store.router.test.tsx +882 -0
- package/src/router-tanstack/search-store.test.ts +578 -0
- package/src/router-tanstack/search-store.ts +546 -0
- package/src/router-tanstack/use-nav.test.tsx +187 -0
- package/src/router-tanstack/use-nav.ts +72 -14
- package/src/section/index.ts +1 -0
- package/src/section/section.module.css +104 -0
- package/src/section/section.module.css.d.ts +11 -0
- package/src/section/section.test.tsx +204 -0
- package/src/section/section.tsx +220 -0
- package/src/shell/app-breadcrumbs.tsx +44 -31
- package/src/shell/app-header.module.css +77 -37
- package/src/shell/app-header.module.css.d.ts +2 -1
- package/src/shell/app-mobile-nav.module.css +8 -0
- package/src/shell/app-mobile-nav.module.css.d.ts +1 -0
- package/src/shell/app-mobile-nav.tsx +147 -8
- package/src/shell/app-sidebar-account.tsx +2 -2
- package/src/shell/app-sidebar.module.css +303 -20
- package/src/shell/app-sidebar.module.css.d.ts +19 -1
- package/src/shell/app-sidebar.test.tsx +456 -41
- package/src/shell/app-sidebar.tsx +276 -140
- package/src/shell/index.test.tsx +291 -5
- package/src/shell/index.tsx +137 -38
- package/src/shell/mobile-nav-model.test.ts +228 -7
- package/src/shell/mobile-nav-model.ts +53 -3
- package/src/shell/nav-count-badge.tsx +1 -1
- package/src/shell/page-bar.module.css +178 -0
- package/src/shell/page-bar.module.css.d.ts +17 -0
- package/src/shell/page-bar.test.tsx +316 -0
- package/src/shell/page-bar.tsx +315 -0
- package/src/shell/sidebar-block-model.ts +83 -0
- package/src/shell/sidebar-blocks.tsx +301 -0
- package/src/shell/sidebar-search.module.css +26 -0
- package/src/shell/sidebar-search.module.css.d.ts +2 -0
- package/src/shell/sidebar-search.tsx +71 -2
- package/src/state/fields.test.tsx +512 -0
- package/src/state/fields.ts +1150 -0
- package/src/state/persisted.test.ts +91 -0
- package/src/state/persisted.ts +261 -0
- package/src/state.ts +52 -240
- package/src/styles.css +13 -0
- package/src/surfaces.ts +204 -36
- package/src/theme/controls.module.css +16 -1
- package/src/theme/ctl-theme.test.tsx +275 -0
- package/src/theme/ctl-theme.tsx +138 -0
- package/src/theme/ctl-tier-coverage.test.ts +170 -0
- package/src/theme/density-relations.test.ts +106 -51
- package/src/theme/divider-law.test.ts +259 -0
- package/src/theme/icon-slot.module.css +47 -0
- package/src/theme/icon-slot.module.css.d.ts +8 -0
- package/src/theme/icon-slot.test.tsx +275 -0
- package/src/theme/icon-slot.tsx +59 -0
- package/src/theme/index.ts +165 -3
- package/src/theme/layout-rhythm-css.test.ts +72 -0
- package/src/theme/radius.test.ts +11 -1
- package/src/theme/segmented-control.module.css +20 -0
- package/src/theme/shadow-surfaces.test.ts +20 -7
- package/src/theme/spacing.test.ts +75 -27
- package/src/theme/use-basalt-spacing.ts +2 -2
- package/src/tokens/density.test.ts +7 -25
- package/src/tokens/index.ts +48 -8
- package/src/tokens/palette.ts +107 -73
- package/src/widget-header/delta-badge.test.tsx +60 -0
- package/src/widget-header/delta-badge.tsx +57 -0
- package/src/widget-header/index.ts +2 -0
- package/src/widget-header/no-mantine.test.ts +33 -0
- package/src/widget-header/widget-header.module.css +350 -0
- package/src/widget-header/widget-header.module.css.d.ts +25 -0
- package/src/widget-header/widget-header.test.tsx +187 -0
- package/src/widget-header/widget-header.tsx +213 -0
- package/agent/rules/basalt-agent.md +0 -452
- package/agent/rules/basalt-app.md +0 -246
- package/agent/rules/basalt-commands.md +0 -320
- package/agent/rules/basalt-content.md +0 -409
- package/agent/rules/basalt-data.md +0 -329
- package/agent/rules/basalt-forms.md +0 -186
- package/agent/rules/basalt-notifications.md +0 -264
- package/agent/rules/basalt-query.md +0 -187
- package/agent/rules/basalt-router.md +0 -336
- package/dist/content/article-filter-bar.d.ts +0 -40
- package/dist/content/article-filter-bar.d.ts.map +0 -1
- package/dist/content/article-filter-bar.js +0 -56
- package/dist/shell/page-header.d.ts +0 -13
- package/dist/shell/page-header.d.ts.map +0 -1
- package/dist/shell/page-header.js +0 -23
- package/src/content/article-filter-bar.tsx +0 -88
- package/src/shell/page-header.tsx +0 -35
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Section — the tier-2 heading composer (docs/CONTROLS-SPEC.md §2.2). No border/background of its
|
|
3
|
+
* own — one shaded container level per page belongs to the cards inside a Section
|
|
4
|
+
* (`ChartCard`/`StatCard`), not to the Section itself.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* 12px from the header to the body, and the same between two body children — one rhythm, read from
|
|
9
|
+
* the stack scale (`--vx-space-stack-md`) instead of the card-inset scale
|
|
10
|
+
* (`--mantine-spacing-sm`, 13px). A Section is a page-level GROUPING, so its gaps belong to the
|
|
11
|
+
* vertical rhythm, not to the inset ladder its cards use.
|
|
12
|
+
*/
|
|
13
|
+
.root {
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
gap: var(--vx-space-stack-md, 0.75rem);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.body {
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-direction: column;
|
|
22
|
+
gap: var(--vx-space-stack-md, 0.75rem);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/*
|
|
26
|
+
* `summary` — the always-visible row under the header. No border, no background (the Section owns
|
|
27
|
+
* neither); it is a plain flow row, so a consumer's `Group` of stats or one line of `Text` lands
|
|
28
|
+
* where the body would have started.
|
|
29
|
+
*/
|
|
30
|
+
.summary {
|
|
31
|
+
display: flex;
|
|
32
|
+
flex-direction: column;
|
|
33
|
+
gap: var(--mantine-spacing-xs, 0.625rem);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/*
|
|
37
|
+
* The collapse chevron — a plain control, not a Mantine ActionIcon, so it sits directly in the header
|
|
38
|
+
* row (its own CtlSlot fragment would nest a theme provider around one glyph). It is sized to the
|
|
39
|
+
* `ctl` tier BY HAND for exactly that reason: a 12px glyph in a 20px box was a different height from
|
|
40
|
+
* every other control in the same header, which is what made the section header read as ragged. 30px
|
|
41
|
+
* square, glyph centred, no padding — the box IS the tier.
|
|
42
|
+
*/
|
|
43
|
+
.chevron {
|
|
44
|
+
/* `relative` is the anchor for the C15 hit-area overlay in the `< sm` block at the bottom. */
|
|
45
|
+
position: relative;
|
|
46
|
+
display: inline-flex;
|
|
47
|
+
flex: none;
|
|
48
|
+
align-items: center;
|
|
49
|
+
justify-content: center;
|
|
50
|
+
/* `--vx-space-control-height-ctl` is a bare rem — the theme's `ctlSizeVars` (`theme/index.ts`)
|
|
51
|
+
* reconstructs every OTHER ctl-tier box as `calc(<rem> * var(--mantine-scale))` so it tracks
|
|
52
|
+
* Mantine's `theme.scale`; read raw here, this box used to stay fixed while a scaled Button/Input
|
|
53
|
+
* beside it grew or shrank, reintroducing the exact raggedness this control was sized to fix. */
|
|
54
|
+
width: calc(var(--vx-space-control-height-ctl) * var(--mantine-scale, 1));
|
|
55
|
+
height: calc(var(--vx-space-control-height-ctl) * var(--mantine-scale, 1));
|
|
56
|
+
appearance: none;
|
|
57
|
+
margin: 0;
|
|
58
|
+
padding: 0;
|
|
59
|
+
border: none;
|
|
60
|
+
background: none;
|
|
61
|
+
border-radius: var(--vx-radius-ctrl);
|
|
62
|
+
color: var(--vx-muted);
|
|
63
|
+
cursor: pointer;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.chevron:hover,
|
|
67
|
+
.chevron:focus-visible {
|
|
68
|
+
color: var(--vx-ink);
|
|
69
|
+
background: var(--vx-surface-subtle);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.chevron svg {
|
|
73
|
+
transition: transform 150ms ease;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.chevron[data-open='false'] svg {
|
|
77
|
+
transform: rotate(-90deg);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/*
|
|
81
|
+
* Law C15 — the chevron is a 12px glyph in a 20px box, which is a fine mouse target and an
|
|
82
|
+
* unusable thumb one. Below `sm` an `::after` overlay lifts the HIT area to
|
|
83
|
+
* `--vx-space-touch-control-height` (36px at level 0) while the visible glyph stays put — the same
|
|
84
|
+
* overlay `shell/page-bar.module.css` draws for the bar's own controls. `overflow: visible` is part
|
|
85
|
+
* of the fix, not tidying: an overlay that extends past the box is clipped by any `overflow:
|
|
86
|
+
* hidden` on the host (which is what made the identical overlay a no-op on Mantine's Button roots).
|
|
87
|
+
*/
|
|
88
|
+
@media (max-width: 47.99375em) {
|
|
89
|
+
.chevron {
|
|
90
|
+
overflow: visible;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.chevron::after {
|
|
94
|
+
content: '';
|
|
95
|
+
position: absolute;
|
|
96
|
+
top: 50%;
|
|
97
|
+
left: 50%;
|
|
98
|
+
translate: -50% -50%;
|
|
99
|
+
width: 100%;
|
|
100
|
+
height: 100%;
|
|
101
|
+
min-width: var(--vx-space-touch-control-height, 2.25rem);
|
|
102
|
+
min-height: var(--vx-space-touch-control-height, 2.25rem);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed CSS-module declaration (the S4 CSS-modules path). Named string props — not an index
|
|
3
|
+
* signature — so dot access stays verbatim under the package's strict flags.
|
|
4
|
+
*/
|
|
5
|
+
declare const classes: {
|
|
6
|
+
readonly root: string
|
|
7
|
+
readonly body: string
|
|
8
|
+
readonly summary: string
|
|
9
|
+
readonly chevron: string
|
|
10
|
+
}
|
|
11
|
+
export default classes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"section.test.d.ts","sourceRoot":"","sources":["../../src/section/section.test.tsx"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AAmB7C,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,sCAqDA"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Anchor, Group, Text } from "@mantine/core";
|
|
3
3
|
import { VX } from "../tokens/index.js";
|
|
4
|
+
import classes from "./app-header.module.css";
|
|
4
5
|
const crumbStyle = { fontSize: VX.text.md, color: "var(--vx-faint)" };
|
|
5
6
|
const separatorStyle = { fontSize: VX.text.md, color: "var(--vx-surface-border)" };
|
|
6
7
|
const currentStyle = {
|
|
@@ -19,21 +20,23 @@ function AppBreadcrumbs({
|
|
|
19
20
|
}) {
|
|
20
21
|
if (!page) return null;
|
|
21
22
|
return /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", style: { minWidth: 0 }, children: [
|
|
22
|
-
section && /* @__PURE__ */ jsxs(
|
|
23
|
-
/* @__PURE__ */
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
/* @__PURE__ */
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
/* @__PURE__ */
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
/* @__PURE__ */
|
|
36
|
-
|
|
23
|
+
(section !== void 0 || parent !== void 0) && /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", className: classes.crumbAncestors, style: { minWidth: 0 }, children: [
|
|
24
|
+
section && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
25
|
+
/* @__PURE__ */ jsx(Text, { style: crumbStyle, truncate: true, children: section }),
|
|
26
|
+
/* @__PURE__ */ jsx(Text, { style: separatorStyle, children: "/" })
|
|
27
|
+
] }),
|
|
28
|
+
parent && parentAnchor && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
29
|
+
/* @__PURE__ */ jsx(Anchor, { style: crumbStyle, underline: "never", component: parentAnchor, truncate: true, children: parent }),
|
|
30
|
+
/* @__PURE__ */ jsx(Text, { style: separatorStyle, children: "/" })
|
|
31
|
+
] }),
|
|
32
|
+
parent && !parentAnchor && parentHref && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
33
|
+
/* @__PURE__ */ jsx(Anchor, { style: crumbStyle, underline: "never", href: parentHref, truncate: true, children: parent }),
|
|
34
|
+
/* @__PURE__ */ jsx(Text, { style: separatorStyle, children: "/" })
|
|
35
|
+
] }),
|
|
36
|
+
parent && !parentAnchor && !parentHref && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
37
|
+
/* @__PURE__ */ jsx(Text, { style: crumbStyle, truncate: true, children: parent }),
|
|
38
|
+
/* @__PURE__ */ jsx(Text, { style: separatorStyle, children: "/" })
|
|
39
|
+
] })
|
|
37
40
|
] }),
|
|
38
41
|
/* @__PURE__ */ jsx(Text, { style: currentStyle, truncate: true, children: page })
|
|
39
42
|
] });
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
/* App-shell top bar layout.
|
|
2
|
-
* [
|
|
3
|
-
*
|
|
4
|
-
*
|
|
1
|
+
/* App-shell top bar layout. ONE 48px row on every viewport —
|
|
2
|
+
* [breadcrumb] ········ [page bar row 1] | [global actions].
|
|
3
|
+
*
|
|
4
|
+
* The pre-1.26.0 mobile layout wrapped to a second, always-reserved row for the page actions and
|
|
5
|
+
* scrolled it sideways. Both are gone (law C7 and law C14): the header height is now the
|
|
6
|
+
* `appShellHeaderHeight` token at every width, an empty page bar reserves nothing, and overflow
|
|
7
|
+
* folds into `ActionGroup`'s kebab instead of scrolling out of view. */
|
|
5
8
|
|
|
6
9
|
/*
|
|
7
10
|
* Transparent header (docs/DESIGN-SPEC.md §5): NO bottom rule at all. The header and the page body
|
|
@@ -16,58 +19,95 @@
|
|
|
16
19
|
height: 100%;
|
|
17
20
|
}
|
|
18
21
|
|
|
22
|
+
/*
|
|
23
|
+
* **Exactly ONE side of the header row is elastic, and it is the side whose content can ellipsize.**
|
|
24
|
+
* That is the whole rule, and both collisions this header has produced were the same violation of
|
|
25
|
+
* it from opposite directions.
|
|
26
|
+
*
|
|
27
|
+
* First the breadcrumb was `flex-shrink: 0`, so it won the whole width and the primary action
|
|
28
|
+
* rendered UNDERNEATH it. Then BOTH sides were made shrinkable (`.lead` at `0 1 auto`, `.pageBar` at
|
|
29
|
+
* `1 1 auto` with `min-width: 0` pushed onto its children) — and at 390px the controls, which cannot
|
|
30
|
+
* clip, shrank below their content and spilled: a flex container with `justify-content: flex-end`
|
|
31
|
+
* overflows towards its START, so the sync button painted 30px INTO the page title. Measured in
|
|
32
|
+
* Chrome: the title spanned 119.7–197.1 and the sync button 158.3–188.3.
|
|
33
|
+
*
|
|
34
|
+
* So the lead is now the elastic side (`flex: 1 1 auto`, `min-width: 0`, each crumb truncating via
|
|
35
|
+
* its own `truncate` prop) and the control side is rigid. A row of buttons that genuinely does not
|
|
36
|
+
* fit is not a shrinking problem — it is the OVERFLOW FOLD's problem, and basalt already owns that
|
|
37
|
+
* (law C7: below `sm` row 1 is the primary plus ONE kebab).
|
|
38
|
+
*/
|
|
19
39
|
.lead {
|
|
20
40
|
display: flex;
|
|
21
41
|
align-items: center;
|
|
22
42
|
gap: var(--mantine-spacing-sm);
|
|
23
|
-
flex
|
|
43
|
+
flex: 1 1 auto;
|
|
24
44
|
min-width: 0;
|
|
25
45
|
}
|
|
26
46
|
|
|
27
|
-
|
|
28
|
-
|
|
47
|
+
/* No `overflow: hidden` here or on `.lead` above: Mantine's focus ring is `outline: 2px solid;
|
|
48
|
+
* outline-offset: 2px` — OUTSIDE the anchor box — and a wrapper clipping this tightly to the
|
|
49
|
+
* breadcrumb anchors clipped the ring on every edge, leaving the primary in-shell nav link with no
|
|
50
|
+
* visible focus indicator. Truncation is already owned by each crumb's own `truncate` prop
|
|
51
|
+
* (`app-breadcrumbs.tsx`, Mantine's `[data-truncate]` rule sets its own `overflow: hidden;
|
|
52
|
+
* text-overflow: ellipsis; white-space: nowrap` on the element that actually holds the text) — this
|
|
53
|
+
* wrapper only needs to let its children shrink. */
|
|
54
|
+
.lead * {
|
|
29
55
|
min-width: 0;
|
|
30
|
-
display: flex;
|
|
31
|
-
align-items: center;
|
|
32
|
-
justify-content: flex-end;
|
|
33
|
-
gap: var(--mantine-spacing-xs);
|
|
34
|
-
overflow-x: auto;
|
|
35
|
-
scrollbar-width: thin;
|
|
36
56
|
}
|
|
37
57
|
|
|
38
|
-
/*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
|
|
58
|
+
/*
|
|
59
|
+
* The `PageBar` row-1 portal target — the RIGID side of the row (see `.lead` above for the rule and
|
|
60
|
+
* for the two collisions that established it). Never a scroll container — see the header note above.
|
|
61
|
+
*
|
|
62
|
+
* `flex: 0 0 auto` and no `min-width: 0` anywhere below it, deliberately: a button cannot ellipsize,
|
|
63
|
+
* so letting this side shrink does not make it narrower, it makes it OVERFLOW — leftwards, into the
|
|
64
|
+
* breadcrumb, because of the `justify-content: flex-end` two lines down. The previous version pushed
|
|
65
|
+
* `min-width: 0` onto every child for exactly the reason that comment gave (a `wrap="nowrap"` group's
|
|
66
|
+
* automatic minimum size is its min-content width and flex will not go below it) — the reasoning was
|
|
67
|
+
* right and the conclusion was backwards: that floor is the row's protection, not its bug.
|
|
68
|
+
*/
|
|
69
|
+
.pageBar {
|
|
70
|
+
flex: 0 0 auto;
|
|
71
|
+
display: flex;
|
|
42
72
|
flex-wrap: nowrap;
|
|
73
|
+
align-items: center;
|
|
74
|
+
justify-content: flex-end;
|
|
75
|
+
gap: var(--vx-space-control-gap);
|
|
43
76
|
}
|
|
44
77
|
|
|
78
|
+
/*
|
|
79
|
+
* The shell globals sit at the SAME 30px tier as everything else in the row, and they get there the
|
|
80
|
+
* same way a home's slot does — `HeaderGlobalActions` wraps this region in a `CtlSlot`, so a
|
|
81
|
+
* consumer's bare `<ActionIcon>` in `globalActions` defaults to `size="ctl"` with no prop at the call
|
|
82
|
+
* site (law C5). They rendered at Mantine's `md` 28px before, which is 2px under the page group
|
|
83
|
+
* beside them: close enough to look like a mistake rather than a tier.
|
|
84
|
+
*
|
|
85
|
+
* The gap to the page group is `.bar`'s own `sm`, so the globals read as a separate cluster; between
|
|
86
|
+
* themselves they use the control gap, like every other adjacent-control pair.
|
|
87
|
+
*/
|
|
45
88
|
.global {
|
|
46
89
|
display: flex;
|
|
90
|
+
flex-wrap: nowrap;
|
|
47
91
|
align-items: center;
|
|
48
|
-
gap: var(--
|
|
92
|
+
gap: var(--vx-space-control-gap);
|
|
49
93
|
flex-shrink: 0;
|
|
50
94
|
}
|
|
51
95
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.lead {
|
|
59
|
-
order: 1;
|
|
60
|
-
flex: 1 1 auto;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.global {
|
|
64
|
-
order: 2;
|
|
65
|
-
}
|
|
96
|
+
.global :global(.mantine-ActionIcon-root) > svg {
|
|
97
|
+
width: 1rem;
|
|
98
|
+
height: 1rem;
|
|
99
|
+
}
|
|
66
100
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
101
|
+
/*
|
|
102
|
+
* The breadcrumb's ANCESTOR crumbs — hidden below `sm`. See `app-breadcrumbs.tsx` for why: at 390px
|
|
103
|
+
* the row's elastic side truncated `Overview / Dashboard` to `O… / D…`, which is two ellipses and a
|
|
104
|
+
* separator in place of the one word that says where the reader is.
|
|
105
|
+
*
|
|
106
|
+
* `47.99375em` is the `sm` breakpoint in the exact literal form every other basalt CSS module uses —
|
|
107
|
+
* a media query cannot resolve a custom property.
|
|
108
|
+
*/
|
|
109
|
+
@media (max-width: 47.99375em) {
|
|
110
|
+
.crumbAncestors {
|
|
111
|
+
display: none;
|
|
72
112
|
}
|
|
73
113
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReactElement } from 'react';
|
|
2
|
-
import type { MobileNavConfig, MobileNavModel } from '../nav/types.js';
|
|
2
|
+
import type { MobileNavConfig, MobileNavModel, SidebarBlock } from '../nav/types.js';
|
|
3
3
|
import type { BasaltAccountProps } from './account-types.js';
|
|
4
4
|
import type { SettingsMenuItem } from './index.js';
|
|
5
5
|
export type MobileNavProps = {
|
|
@@ -11,6 +11,13 @@ export type MobileNavProps = {
|
|
|
11
11
|
* deletable: everything it used to hold is reachable from More. */
|
|
12
12
|
account?: BasaltAccountProps | undefined;
|
|
13
13
|
settingsMenuItems?: SettingsMenuItem[] | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* The sidebar's blocks, projected. A block with `mobile: 'more'` becomes ONE row in the More
|
|
16
|
+
* surface (`Awaiting action · 3`) that opens a nested 44px-row sheet of its items — the sidebar
|
|
17
|
+
* itself does not exist below `sm`, so this is the only way its blocks are reachable at all.
|
|
18
|
+
* Counted for the menu-vs-sheet threshold by `blockRowCount`, never re-counted here.
|
|
19
|
+
*/
|
|
20
|
+
blocks?: SidebarBlock[] | undefined;
|
|
14
21
|
};
|
|
15
22
|
/**
|
|
16
23
|
* How many rows `account` contributes to the More surface — DELEGATED to `accountRows` rather than
|
|
@@ -21,5 +28,5 @@ export type MobileNavProps = {
|
|
|
21
28
|
* rows — still conjured a More slot that opened empty.
|
|
22
29
|
*/
|
|
23
30
|
export declare function accountRowCount(account: BasaltAccountProps | undefined): number;
|
|
24
|
-
export declare function MobileNav({ model, config, account, settingsMenuItems, }: MobileNavProps): ReactElement;
|
|
31
|
+
export declare function MobileNav({ model, config, account, settingsMenuItems, blocks, }: MobileNavProps): ReactElement;
|
|
25
32
|
//# sourceMappingURL=app-mobile-nav.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-mobile-nav.d.ts","sourceRoot":"","sources":["../../src/shell/app-mobile-nav.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAc,YAAY,EAAa,MAAM,OAAO,CAAA;AAChE,OAAO,KAAK,EACV,eAAe,EAEf,cAAc,
|
|
1
|
+
{"version":3,"file":"app-mobile-nav.d.ts","sourceRoot":"","sources":["../../src/shell/app-mobile-nav.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAc,YAAY,EAAa,MAAM,OAAO,CAAA;AAChE,OAAO,KAAK,EACV,eAAe,EAEf,cAAc,EAEd,YAAY,EAKb,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAmB/C,MAAM,MAAM,cAAc,GAAG;IAC3B,kFAAkF;IAClF,KAAK,EAAE,cAAc,CAAA;IACrB,MAAM,CAAC,EAAE,eAAe,GAAG,SAAS,CAAA;IACpC;;wEAEoE;IACpE,OAAO,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAA;IACxC,iBAAiB,CAAC,EAAE,gBAAgB,EAAE,GAAG,SAAS,CAAA;IAClD;;;;;OAKG;IACH,MAAM,CAAC,EAAE,YAAY,EAAE,GAAG,SAAS,CAAA;CACpC,CAAA;AAkFD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,kBAAkB,GAAG,SAAS,GAAG,MAAM,CAE/E;AAsFD,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,MAAM,EACN,OAAO,EACP,iBAAiB,EACjB,MAAM,GACP,EAAE,cAAc,GAAG,YAAY,CAuW/B"}
|
|
@@ -3,6 +3,8 @@ import { Drawer, Menu, NavLink, ScrollArea, Stack, Text, UnstyledButton } from "
|
|
|
3
3
|
import { useReducedMotion } from "@mantine/hooks";
|
|
4
4
|
import { Fragment as Fragment2, useState } from "react";
|
|
5
5
|
import { NavCountBadge } from "./nav-count-badge.js";
|
|
6
|
+
import { SidebarBlockToneDot } from "./sidebar-blocks.js";
|
|
7
|
+
import { sidebarBlockMobile } from "./sidebar-block-model.js";
|
|
6
8
|
import { useBasaltSpacing } from "../theme/index.js";
|
|
7
9
|
import classes from "./app-mobile-nav.module.css";
|
|
8
10
|
const MENU_MIDDLEWARES = { flip: false, shift: { padding: 8 } };
|
|
@@ -134,9 +136,11 @@ function MobileNav({
|
|
|
134
136
|
model,
|
|
135
137
|
config,
|
|
136
138
|
account,
|
|
137
|
-
settingsMenuItems
|
|
139
|
+
settingsMenuItems,
|
|
140
|
+
blocks
|
|
138
141
|
}) {
|
|
139
142
|
const [openKey, setOpenKey] = useState(null);
|
|
143
|
+
const [openBlockKey, setOpenBlockKey] = useState(null);
|
|
140
144
|
const reduceMotion = useReducedMotion();
|
|
141
145
|
const { step } = useBasaltSpacing();
|
|
142
146
|
const close = () => setOpenKey(null);
|
|
@@ -145,6 +149,61 @@ function MobileNav({
|
|
|
145
149
|
target.scrollTo({ top: 0, behavior: reduceMotion ? "auto" : "smooth" });
|
|
146
150
|
};
|
|
147
151
|
const extraRows = [...accountRows(account), ...settingsRows(settingsMenuItems)];
|
|
152
|
+
const moreBlocks = (blocks ?? []).filter(
|
|
153
|
+
(block) => block.kind !== "custom" && sidebarBlockMobile(block) === "more" && (block.kind !== "list" || block.items.length > 0)
|
|
154
|
+
);
|
|
155
|
+
const listBlocks = moreBlocks.filter((block) => block.kind === "list");
|
|
156
|
+
const activateBlock = (block) => {
|
|
157
|
+
close();
|
|
158
|
+
if (block.kind === "list") {
|
|
159
|
+
setOpenBlockKey(block.key);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
block.onClick?.();
|
|
163
|
+
};
|
|
164
|
+
const menuBlockRow = (block) => /* @__PURE__ */ jsx(Menu.Item, { className: classes.menuItem, onClick: () => activateBlock(block), children: blockRowLabel(block) }, block.key);
|
|
165
|
+
const sheetBlockRow = (block) => /* @__PURE__ */ jsx(
|
|
166
|
+
NavLink,
|
|
167
|
+
{
|
|
168
|
+
classNames: { root: classes.row },
|
|
169
|
+
label: blockRowLabel(block),
|
|
170
|
+
onClick: () => activateBlock(block)
|
|
171
|
+
},
|
|
172
|
+
block.key
|
|
173
|
+
);
|
|
174
|
+
const blockItemRow = (item) => {
|
|
175
|
+
const lead = item.icon ?? (item.tone !== void 0 ? /* @__PURE__ */ jsx(SidebarBlockToneDot, { tone: item.tone }) : void 0);
|
|
176
|
+
const meta = item.meta !== void 0 ? /* @__PURE__ */ jsx(Text, { component: "span", className: classes.rowMeta, children: item.meta }) : void 0;
|
|
177
|
+
const interactive = item.Anchor !== void 0 || item.href !== void 0 || item.onClick !== void 0;
|
|
178
|
+
if (!interactive) {
|
|
179
|
+
return /* @__PURE__ */ jsxs("div", { className: classes.row, children: [
|
|
180
|
+
lead,
|
|
181
|
+
/* @__PURE__ */ jsx(Text, { component: "span", children: item.label }),
|
|
182
|
+
meta
|
|
183
|
+
] }, item.key);
|
|
184
|
+
}
|
|
185
|
+
const shared = {
|
|
186
|
+
classNames: { root: classes.row },
|
|
187
|
+
label: item.label,
|
|
188
|
+
...lead !== void 0 && { leftSection: lead },
|
|
189
|
+
...meta !== void 0 && { rightSection: meta },
|
|
190
|
+
onClick: () => {
|
|
191
|
+
item.onClick?.();
|
|
192
|
+
setOpenBlockKey(null);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
const Anchor = item.Anchor;
|
|
196
|
+
if (Anchor) return /* @__PURE__ */ jsx(NavLink, { component: Anchor, ...shared }, item.key);
|
|
197
|
+
return /* @__PURE__ */ jsx(
|
|
198
|
+
NavLink,
|
|
199
|
+
{
|
|
200
|
+
component: "a",
|
|
201
|
+
...item.href !== void 0 && { href: item.href },
|
|
202
|
+
...shared
|
|
203
|
+
},
|
|
204
|
+
item.key
|
|
205
|
+
);
|
|
206
|
+
};
|
|
148
207
|
const hasCount = (slot) => slot.kind === "link" ? Boolean(slot.item.count) : slot.groups.some((g) => groupHasCount(g.items));
|
|
149
208
|
const tabInner = (slot) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
150
209
|
/* @__PURE__ */ jsxs("span", { className: classes.tabIcon, children: [
|
|
@@ -271,9 +330,9 @@ function MobileNav({
|
|
|
271
330
|
...group.label ? [/* @__PURE__ */ jsx(Menu.Label, { children: group.label }, `${group.key}-label`)] : [],
|
|
272
331
|
...rows(group.items, 0, menuRow)
|
|
273
332
|
]),
|
|
274
|
-
slot.isMore && extraRows.length > 0 && slotHasRows(slot) ? /* @__PURE__ */ jsx(Menu.Divider, {}) : null,
|
|
275
|
-
slot.isMore ?
|
|
276
|
-
slot.isMore ?
|
|
333
|
+
slot.isMore && moreBlocks.length + extraRows.length > 0 && slotHasRows(slot) ? /* @__PURE__ */ jsx(Menu.Divider, {}) : null,
|
|
334
|
+
slot.isMore ? moreBlocks.map(menuBlockRow) : null,
|
|
335
|
+
slot.isMore ? extraRows.map(menuActionRow) : null
|
|
277
336
|
] })
|
|
278
337
|
]
|
|
279
338
|
},
|
|
@@ -304,17 +363,45 @@ function MobileNav({
|
|
|
304
363
|
sheetGroupLabel(group),
|
|
305
364
|
rows(group.items, 0, sheetRow)
|
|
306
365
|
] }, group.key)),
|
|
307
|
-
slot.isMore &&
|
|
308
|
-
slot.isMore ?
|
|
366
|
+
slot.isMore && moreBlocks.length > 0 ? /* @__PURE__ */ jsx(Stack, { gap: 2, children: moreBlocks.map(sheetBlockRow) }) : null,
|
|
367
|
+
slot.isMore && extraRows.length > 0 ? /* @__PURE__ */ jsx(Stack, { gap: 2, children: extraRows.map(sheetActionRow) }) : null
|
|
309
368
|
] }) })
|
|
310
369
|
}
|
|
311
370
|
)
|
|
312
371
|
] }, slot.key);
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
372
|
+
const blockSheet = (block) => /* @__PURE__ */ jsx(
|
|
373
|
+
Drawer,
|
|
374
|
+
{
|
|
375
|
+
opened: openBlockKey === block.key,
|
|
376
|
+
onClose: () => setOpenBlockKey(null),
|
|
377
|
+
position: "bottom",
|
|
378
|
+
padding: "md",
|
|
379
|
+
title: block.label,
|
|
380
|
+
classNames: {
|
|
381
|
+
content: classes.sheet,
|
|
382
|
+
title: classes.sheetTitle,
|
|
383
|
+
header: classes.sheetHeader
|
|
384
|
+
},
|
|
385
|
+
transitionProps: {
|
|
386
|
+
transition: "slide-up",
|
|
387
|
+
duration: reduceMotion ? 0 : 220,
|
|
388
|
+
timingFunction: "cubic-bezier(0.32, 0.72, 0, 1)"
|
|
389
|
+
},
|
|
390
|
+
children: /* @__PURE__ */ jsx(ScrollArea.Autosize, { mah: "62dvh", type: "scroll", children: /* @__PURE__ */ jsx(Stack, { gap: 2, children: block.items.map(blockItemRow) }) })
|
|
391
|
+
},
|
|
392
|
+
`block-${block.key}`
|
|
393
|
+
);
|
|
394
|
+
return /* @__PURE__ */ jsxs("nav", { className: classes.bar, "aria-label": "Primary", children: [
|
|
395
|
+
model.slots.map((slot) => {
|
|
396
|
+
if (slot.kind === "link") return linkTab(slot);
|
|
397
|
+
if (slot.kind === "menu") return menuSlot(slot);
|
|
398
|
+
return sheetSlot(slot);
|
|
399
|
+
}),
|
|
400
|
+
listBlocks.map(blockSheet)
|
|
401
|
+
] });
|
|
402
|
+
}
|
|
403
|
+
function blockRowLabel(block) {
|
|
404
|
+
return block.kind === "list" ? `${block.label} \xB7 ${block.count ?? block.items.length}` : `${block.label} \xB7 ${block.value} of ${block.total}`;
|
|
318
405
|
}
|
|
319
406
|
function slotHasRows(slot) {
|
|
320
407
|
return slot.groups.some((group) => group.items.length > 0);
|
|
@@ -252,6 +252,14 @@
|
|
|
252
252
|
background-color: color-mix(in srgb, var(--vx-ink) 10%, transparent);
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
+
/* Trailing meta on a block-item row (a timestamp, an owner) — the mobile twin of the sidebar's
|
|
256
|
+
* `.blockRowMeta`, so the same `SidebarBlockItem.meta` reads the same on both surfaces. */
|
|
257
|
+
.rowMeta {
|
|
258
|
+
flex: none;
|
|
259
|
+
font-size: var(--vx-text-micro);
|
|
260
|
+
color: var(--vx-faint);
|
|
261
|
+
}
|
|
262
|
+
|
|
255
263
|
/*
|
|
256
264
|
* Active = neutral surface fill + plain text color. Never the identity blue (DESIGN.md). --nl-color
|
|
257
265
|
* forces NavLink's active text/icon color (otherwise the accent) to the plain text color. The
|
|
@@ -1,49 +1,76 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
1
|
import type { BrandConfig, SettingsMenuItem } from './index.js';
|
|
3
|
-
import type { SidebarSection } from '../nav/types.js';
|
|
4
|
-
import type { BasaltAccountProps } from './account-types.js';
|
|
5
|
-
import type { SidebarSearchConfig } from './sidebar-search.js';
|
|
2
|
+
import type { SidebarBlock, SidebarSection } from '../nav/types.js';
|
|
3
|
+
import type { AccountMenuItem, BasaltAccountProps } from './account-types.js';
|
|
4
|
+
import type { SidebarSearchActions, SidebarSearchConfig } from './sidebar-search.js';
|
|
6
5
|
export type AppSidebarProps = {
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Brand identity. Supplying `menu` turns the brand row into a `Name ▾` workspace switcher —
|
|
8
|
+
* the entries are the existing `AccountMenuItem` shape, so a consumer already mapping account
|
|
9
|
+
* actions needs no second vocabulary.
|
|
10
|
+
*/
|
|
11
|
+
brand: BrandConfig & {
|
|
12
|
+
menu?: AccountMenuItem[];
|
|
13
|
+
};
|
|
8
14
|
sections: SidebarSection[];
|
|
9
15
|
collapsed: boolean;
|
|
10
16
|
onToggleCollapse: () => void;
|
|
11
17
|
/**
|
|
12
18
|
* Footer settings-menu entries (theme switcher, devtools, …) — supplied by the consumer.
|
|
13
|
-
* The pinned footer
|
|
14
|
-
*
|
|
15
|
-
*
|
|
19
|
+
* The pinned footer row renders ONLY when this is a non-empty list — apps that put Settings in a
|
|
20
|
+
* nav section (the common case) omit this and get no duplicate footer row.
|
|
21
|
+
*
|
|
22
|
+
* **Three entries or fewer render FLAT**, as one link row each (Settings · Integrations · Invite
|
|
23
|
+
* teammates), and four or more collapse into the single gear "Settings" menu. A menu that opens
|
|
24
|
+
* to show two rows costs a click for nothing; a footer of eight rows costs the nav its height.
|
|
25
|
+
* That threshold is basalt's — `docs/CONTROLS-SPEC.md` §2.3 — and `settingsMenu` is the override
|
|
26
|
+
* for the cases the count cannot see. `brand.version` renders as a faint label under the flat
|
|
27
|
+
* rows, and inside the dropdown in the menu form.
|
|
16
28
|
*/
|
|
17
29
|
settingsMenuItems?: SettingsMenuItem[];
|
|
30
|
+
/**
|
|
31
|
+
* Which footer form `settingsMenuItems` takes. `'auto'` (the default) is the count rule above;
|
|
32
|
+
* `'flat'` and `'menu'` force one regardless of how many entries there are.
|
|
33
|
+
*
|
|
34
|
+
* The override exists because the COUNT is not the whole question. Three rows that are each a
|
|
35
|
+
* theme radio group, a devtools switch and a density slider are three CONTROLS, not three
|
|
36
|
+
* destinations — flat they fill the footer with widgets and push the account row off the fold,
|
|
37
|
+
* and the count rule cannot tell them from three links. Equally, four short destinations may be
|
|
38
|
+
* worth their height on a tall sidebar. `'auto'` stays the default so nothing moves for a
|
|
39
|
+
* consumer that never had an opinion.
|
|
40
|
+
*
|
|
41
|
+
* DESKTOP FOOTER ONLY. Below `sm` there is no sidebar at all and the same entries are flat rows
|
|
42
|
+
* in the bottom bar's More surface either way — a dropdown inside that sheet would be a menu
|
|
43
|
+
* inside a menu.
|
|
44
|
+
*
|
|
45
|
+
* @default 'auto'
|
|
46
|
+
*/
|
|
47
|
+
settingsMenu?: 'auto' | 'flat' | 'menu';
|
|
18
48
|
/**
|
|
19
49
|
* Optional account row rendered below the settings menu in the sidebar footer (see
|
|
20
|
-
* `SidebarAccount` / `BasaltAccountProps`),
|
|
21
|
-
* reproduces today's footer unchanged.
|
|
50
|
+
* `SidebarAccount` / `BasaltAccountProps`) — no separating hairline, the row's own top padding
|
|
51
|
+
* supplies the separation. Omitting it reproduces today's footer unchanged.
|
|
22
52
|
*/
|
|
23
53
|
account?: BasaltAccountProps;
|
|
24
54
|
/**
|
|
25
55
|
* Optional search field rendered directly below the brand and ABOVE the nav scroll region — a
|
|
26
|
-
* fixed, non-scrolling row. Pair with basalt-ui/commands' openSpotlight.
|
|
56
|
+
* fixed, non-scrolling row. Pair with basalt-ui/commands' openSpotlight. `actions` adds up to two
|
|
57
|
+
* icon-only buttons to the right of that row.
|
|
27
58
|
*/
|
|
28
|
-
search?: SidebarSearchConfig
|
|
59
|
+
search?: SidebarSearchConfig & {
|
|
60
|
+
actions?: SidebarSearchActions;
|
|
61
|
+
};
|
|
29
62
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* fighting it for height. Pass `sections={[]}` to use this slot exclusively; the section-spacing
|
|
34
|
-
* rule only fires between adjacent children, so an empty `sections` produces no orphan divider or
|
|
35
|
-
* dead padding above it.
|
|
63
|
+
* Declared blocks (`docs/CONTROLS-SPEC.md` §2.3, law C13) — an "Awaiting action" list, a
|
|
64
|
+
* "Recents" list, a "Getting started" progress row, or a `kind: 'custom'` node for a tree or
|
|
65
|
+
* filter panel no set of rows can express (`kind: 'custom'` is what replaced `navExtra`).
|
|
36
66
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* itself to the rail (it takes `collapsed` and renders its own icon-only form) because the shell
|
|
43
|
-
* owns that control; it cannot adapt content it knows nothing about, so this slot hides instead
|
|
44
|
-
* of squashing it.
|
|
67
|
+
* `placement: 'nav'` appends after `sections` INSIDE the nav scroll region, so a long list
|
|
68
|
+
* scrolls with the nav instead of fighting it for height; `'bottom'` pins above the settings
|
|
69
|
+
* footer. Because these are data and not a `ReactNode` slot, basalt owns both projections a slot
|
|
70
|
+
* could never express: the collapsed rail (a count dot on the icon, a ring on the settings row)
|
|
71
|
+
* and the mobile More sheet — see `sidebarBlockRail` / `sidebarBlockMobile`.
|
|
45
72
|
*/
|
|
46
|
-
|
|
73
|
+
blocks?: SidebarBlock[];
|
|
47
74
|
};
|
|
48
|
-
export declare function AppSidebar({ brand, sections, collapsed, onToggleCollapse, settingsMenuItems, account, search,
|
|
75
|
+
export declare function AppSidebar({ brand, sections, collapsed, onToggleCollapse, settingsMenuItems, settingsMenu, account, search, blocks, }: AppSidebarProps): import("react").JSX.Element;
|
|
49
76
|
//# sourceMappingURL=app-sidebar.d.ts.map
|