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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-sidebar.d.ts","sourceRoot":"","sources":["../../src/shell/app-sidebar.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"app-sidebar.d.ts","sourceRoot":"","sources":["../../src/shell/app-sidebar.tsx"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAe,cAAc,EAAE,MAAM,cAAc,CAAA;AAG7E,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAE1E,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAgBjF,MAAM,MAAM,eAAe,GAAG;IAC5B;;;;OAIG;IACH,KAAK,EAAE,WAAW,GAAG;QAAE,IAAI,CAAC,EAAE,eAAe,EAAE,CAAA;KAAE,CAAA;IACjD,QAAQ,EAAE,cAAc,EAAE,CAAA;IAC1B,SAAS,EAAE,OAAO,CAAA;IAClB,gBAAgB,EAAE,MAAM,IAAI,CAAA;IAC5B;;;;;;;;;;;OAWG;IACH,iBAAiB,CAAC,EAAE,gBAAgB,EAAE,CAAA;IACtC;;;;;;;;;;;;;;;;OAgBG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;IACvC;;;;OAIG;IACH,OAAO,CAAC,EAAE,kBAAkB,CAAA;IAC5B;;;;OAIG;IACH,MAAM,CAAC,EAAE,mBAAmB,GAAG;QAAE,OAAO,CAAC,EAAE,oBAAoB,CAAA;KAAE,CAAA;IACjE;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,YAAY,EAAE,CAAA;CACxB,CAAA;AA4VD,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,SAAS,EACT,gBAAgB,EAChB,iBAAiB,EACjB,YAAqB,EACrB,OAAO,EACP,MAAM,EACN,MAAM,GACP,EAAE,eAAe,+BAiKjB"}
|
|
@@ -14,10 +14,21 @@ import {
|
|
|
14
14
|
UnstyledButton
|
|
15
15
|
} from "@mantine/core";
|
|
16
16
|
import { useDisclosure } from "@mantine/hooks";
|
|
17
|
-
import { useEffect, useRef
|
|
17
|
+
import { useEffect, useRef } from "react";
|
|
18
18
|
import { NavCountBadge } from "./nav-count-badge.js";
|
|
19
19
|
import { SidebarAccount } from "./app-sidebar-account.js";
|
|
20
20
|
import { SidebarSearch } from "./sidebar-search.js";
|
|
21
|
+
import {
|
|
22
|
+
IconChevron,
|
|
23
|
+
SidebarBlockView,
|
|
24
|
+
SidebarProgressRing,
|
|
25
|
+
usePersistedFold
|
|
26
|
+
} from "./sidebar-blocks.js";
|
|
27
|
+
import {
|
|
28
|
+
sidebarBlockPlacement,
|
|
29
|
+
sidebarBlockRail,
|
|
30
|
+
sidebarSectionFoldKey
|
|
31
|
+
} from "./sidebar-block-model.js";
|
|
21
32
|
import { VX } from "../tokens/index.js";
|
|
22
33
|
import { useBasaltSpacing } from "../theme/index.js";
|
|
23
34
|
import classes from "./app-sidebar.module.css";
|
|
@@ -42,23 +53,6 @@ function IconCollapse({ collapsed }) {
|
|
|
42
53
|
}
|
|
43
54
|
);
|
|
44
55
|
}
|
|
45
|
-
function IconChevron({ open }) {
|
|
46
|
-
return /* @__PURE__ */ jsx(
|
|
47
|
-
"svg",
|
|
48
|
-
{
|
|
49
|
-
width: 12,
|
|
50
|
-
height: 12,
|
|
51
|
-
viewBox: "0 0 24 24",
|
|
52
|
-
fill: "none",
|
|
53
|
-
stroke: "currentColor",
|
|
54
|
-
strokeWidth: 2,
|
|
55
|
-
strokeLinecap: "round",
|
|
56
|
-
strokeLinejoin: "round",
|
|
57
|
-
"aria-hidden": true,
|
|
58
|
-
children: open ? /* @__PURE__ */ jsx("path", { d: "M6 9l6 6l6 -6" }) : /* @__PURE__ */ jsx("path", { d: "M9 6l6 6l-6 6" })
|
|
59
|
-
}
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
56
|
function IconGear() {
|
|
63
57
|
return /* @__PURE__ */ jsxs(
|
|
64
58
|
"svg",
|
|
@@ -81,6 +75,31 @@ function IconGear() {
|
|
|
81
75
|
}
|
|
82
76
|
const HOVER_OPEN_DELAY = 150;
|
|
83
77
|
const HOVER_CLOSE_DELAY = 200;
|
|
78
|
+
const FLAT_SETTINGS_MAX = 3;
|
|
79
|
+
function BrandName({
|
|
80
|
+
brand,
|
|
81
|
+
menuWidth
|
|
82
|
+
}) {
|
|
83
|
+
const label = /* @__PURE__ */ jsx(Text, { className: classes.brandName, fz: VX.text.xl, fw: 550, children: brand.name });
|
|
84
|
+
const menu = brand.menu;
|
|
85
|
+
if (menu === void 0 || menu.length === 0) return label;
|
|
86
|
+
return /* @__PURE__ */ jsxs(Menu, { position: "bottom-start", withArrow: true, width: menuWidth, zIndex: 500, children: [
|
|
87
|
+
/* @__PURE__ */ jsx(Menu.Target, { children: /* @__PURE__ */ jsxs(UnstyledButton, { className: classes.brandButton, "aria-label": `${brand.name} workspace`, children: [
|
|
88
|
+
label,
|
|
89
|
+
/* @__PURE__ */ jsx(IconChevron, { open: true })
|
|
90
|
+
] }) }),
|
|
91
|
+
/* @__PURE__ */ jsx(Menu.Dropdown, { children: menu.map((entry) => /* @__PURE__ */ jsx(
|
|
92
|
+
Menu.Item,
|
|
93
|
+
{
|
|
94
|
+
leftSection: entry.icon,
|
|
95
|
+
...entry.danger === true && { color: "red" },
|
|
96
|
+
onClick: entry.onClick,
|
|
97
|
+
children: entry.label
|
|
98
|
+
},
|
|
99
|
+
entry.key
|
|
100
|
+
)) })
|
|
101
|
+
] });
|
|
102
|
+
}
|
|
84
103
|
function hasActiveDescendant(item) {
|
|
85
104
|
if (item.active) return true;
|
|
86
105
|
return item.children?.some((c) => hasActiveDescendant(c)) ?? false;
|
|
@@ -93,6 +112,15 @@ function NavLinkBody({ item, active }) {
|
|
|
93
112
|
leftSection: item.icon,
|
|
94
113
|
rightSection: item.badge ?? (item.count ? /* @__PURE__ */ jsx(NavCountBadge, { count: item.count }) : void 0),
|
|
95
114
|
active,
|
|
115
|
+
// Basalt is the one that stamps `aria-current`, not the theme and not a router `<Link>`'s own
|
|
116
|
+
// default (which a consumer's `SidebarItem.Anchor` may compute independently) — mirrors
|
|
117
|
+
// `app-mobile-nav.tsx`'s identical pattern so both surfaces agree on what "active" means.
|
|
118
|
+
"aria-current": active ? "page" : void 0,
|
|
119
|
+
// `item.ancestor` (a parent on the winner's path — `useNav`'s exclusivity pass) is NEVER
|
|
120
|
+
// active and never carries `aria-current`; it only earns a `data-ancestor` hook for CSS. A
|
|
121
|
+
// leaf item never has `ancestor` set, so this is a no-op there — shared here rather than
|
|
122
|
+
// duplicated per render path.
|
|
123
|
+
...item.ancestor === true && { "data-ancestor": true },
|
|
96
124
|
...item.onClick !== void 0 && { onClick: item.onClick }
|
|
97
125
|
};
|
|
98
126
|
if (Anchor) {
|
|
@@ -100,8 +128,36 @@ function NavLinkBody({ item, active }) {
|
|
|
100
128
|
}
|
|
101
129
|
return /* @__PURE__ */ jsx(NavLink, { component: "a", ...item.href !== void 0 && { href: item.href }, ...shared });
|
|
102
130
|
}
|
|
103
|
-
function SectionLabel({ children
|
|
104
|
-
return /* @__PURE__ */ jsx(Text, { component: "div", px:
|
|
131
|
+
function SectionLabel({ children }) {
|
|
132
|
+
return /* @__PURE__ */ jsx(Text, { component: "div", px: 0, mb: 0, className: classes.sectionLabel, children });
|
|
133
|
+
}
|
|
134
|
+
function NavSection({ section, collapsed }) {
|
|
135
|
+
const [folded, setFolded] = usePersistedFold(
|
|
136
|
+
sidebarSectionFoldKey(section.label),
|
|
137
|
+
Boolean(section.defaultCollapsed)
|
|
138
|
+
);
|
|
139
|
+
const items = section.items.map((item) => /* @__PURE__ */ jsx(NavItemRow, { item, collapsed }, item.key));
|
|
140
|
+
if (!section.collapsible) {
|
|
141
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
142
|
+
/* @__PURE__ */ jsx("div", { className: classes.sectionBand, children: /* @__PURE__ */ jsx(SectionLabel, { children: section.label }) }),
|
|
143
|
+
/* @__PURE__ */ jsx(Stack, { gap: 1, children: items })
|
|
144
|
+
] });
|
|
145
|
+
}
|
|
146
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
147
|
+
/* @__PURE__ */ jsxs(
|
|
148
|
+
UnstyledButton,
|
|
149
|
+
{
|
|
150
|
+
className: `${classes.sectionBand} ${classes.sectionHeader}`,
|
|
151
|
+
onClick: () => setFolded(!folded),
|
|
152
|
+
"aria-expanded": !folded,
|
|
153
|
+
children: [
|
|
154
|
+
/* @__PURE__ */ jsx(SectionLabel, { children: section.label }),
|
|
155
|
+
/* @__PURE__ */ jsx(IconChevron, { open: !folded })
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
),
|
|
159
|
+
/* @__PURE__ */ jsx(Collapse, { expanded: !folded, children: /* @__PURE__ */ jsx(Stack, { gap: 1, children: items }) })
|
|
160
|
+
] });
|
|
105
161
|
}
|
|
106
162
|
function NavItemRow({ item, collapsed }) {
|
|
107
163
|
const [opened, { open, close }] = useDisclosure(false);
|
|
@@ -213,33 +269,51 @@ function AppSidebar({
|
|
|
213
269
|
collapsed,
|
|
214
270
|
onToggleCollapse,
|
|
215
271
|
settingsMenuItems,
|
|
272
|
+
settingsMenu = "auto",
|
|
216
273
|
account,
|
|
217
274
|
search,
|
|
218
|
-
|
|
275
|
+
blocks
|
|
219
276
|
}) {
|
|
220
277
|
const { step } = useBasaltSpacing();
|
|
221
|
-
const [
|
|
222
|
-
|
|
278
|
+
const navBlocks = (blocks ?? []).filter((block) => sidebarBlockPlacement(block) === "nav");
|
|
279
|
+
const bottomBlocks = (blocks ?? []).filter((block) => sidebarBlockPlacement(block) === "bottom");
|
|
280
|
+
const railRing = bottomBlocks.find(
|
|
281
|
+
(block) => block.kind === "progress" && sidebarBlockRail(block) === "ring"
|
|
223
282
|
);
|
|
224
|
-
const
|
|
225
|
-
const
|
|
226
|
-
const
|
|
283
|
+
const settingsItems = settingsMenuItems ?? [];
|
|
284
|
+
const versionLabel = brand.version !== void 0 ? `${brand.name} v${brand.version}` : void 0;
|
|
285
|
+
const ringMark = collapsed && railRing !== void 0 && railRing.kind === "progress" ? /* @__PURE__ */ jsx(SidebarProgressRing, { value: railRing.value, total: railRing.total }) : null;
|
|
286
|
+
const flatSettings = settingsMenu === "flat" || settingsMenu === "auto" && settingsItems.length <= FLAT_SETTINGS_MAX;
|
|
287
|
+
const settingsRow = settingsItems.length === 0 ? null : flatSettings ? /* @__PURE__ */ jsxs("div", { className: classes.footerLinks, children: [
|
|
288
|
+
settingsItems.map((entry, index) => /* @__PURE__ */ jsxs(
|
|
289
|
+
UnstyledButton,
|
|
290
|
+
{
|
|
291
|
+
className: classes.footerBtn,
|
|
292
|
+
onClick: entry.onClick,
|
|
293
|
+
"aria-label": entry.label,
|
|
294
|
+
children: [
|
|
295
|
+
/* @__PURE__ */ jsx("span", { className: classes.footerIconSlot, children: entry.icon ?? /* @__PURE__ */ jsx(IconGear, {}) }),
|
|
296
|
+
/* @__PURE__ */ jsx(Text, { className: classes.footerText, fz: VX.text.md, children: entry.label }),
|
|
297
|
+
index === 0 ? ringMark : null
|
|
298
|
+
]
|
|
299
|
+
},
|
|
300
|
+
entry.key
|
|
301
|
+
)),
|
|
302
|
+
versionLabel !== void 0 && /* @__PURE__ */ jsx(Text, { component: "div", className: classes.footerVersion, children: versionLabel })
|
|
303
|
+
] }) : /* @__PURE__ */ jsx(Group, { gap: "xs", wrap: "nowrap", children: /* @__PURE__ */ jsxs(Menu, { position: "right-start", withArrow: true, width: step.sidebarSettingsMenuWidth, zIndex: 500, children: [
|
|
227
304
|
/* @__PURE__ */ jsx(Menu.Target, { children: /* @__PURE__ */ jsxs(UnstyledButton, { className: classes.footerBtn, "aria-label": "Settings", children: [
|
|
228
305
|
/* @__PURE__ */ jsx(IconGear, {}),
|
|
229
|
-
/* @__PURE__ */ jsx(Text, { className: classes.footerText, fz: VX.text.md, children: "Settings" })
|
|
306
|
+
/* @__PURE__ */ jsx(Text, { className: classes.footerText, fz: VX.text.md, children: "Settings" }),
|
|
307
|
+
ringMark
|
|
230
308
|
] }) }),
|
|
231
309
|
/* @__PURE__ */ jsxs(Menu.Dropdown, { children: [
|
|
232
|
-
|
|
233
|
-
|
|
310
|
+
settingsItems.map((entry) => /* @__PURE__ */ jsx(Menu.Item, { leftSection: entry.icon, onClick: entry.onClick, children: entry.label }, entry.key)),
|
|
311
|
+
versionLabel !== void 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
234
312
|
/* @__PURE__ */ jsx(Menu.Divider, {}),
|
|
235
|
-
/* @__PURE__ */
|
|
236
|
-
brand.name,
|
|
237
|
-
" v",
|
|
238
|
-
brand.version
|
|
239
|
-
] })
|
|
313
|
+
/* @__PURE__ */ jsx(Menu.Label, { children: versionLabel })
|
|
240
314
|
] })
|
|
241
315
|
] })
|
|
242
|
-
] }) })
|
|
316
|
+
] }) });
|
|
243
317
|
return /* @__PURE__ */ jsxs(Stack, { gap: 0, h: "100%", className: classes.root, "data-collapsed": collapsed || void 0, children: [
|
|
244
318
|
/* @__PURE__ */ jsxs(Group, { className: classes.brand, gap: "sm", wrap: "nowrap", children: [
|
|
245
319
|
/* @__PURE__ */ jsxs(Group, { className: classes.brandLead, gap: "sm", wrap: "nowrap", children: [
|
|
@@ -253,7 +327,7 @@ function AppSidebar({
|
|
|
253
327
|
style: { display: "block" }
|
|
254
328
|
}
|
|
255
329
|
),
|
|
256
|
-
/* @__PURE__ */ jsx(
|
|
330
|
+
/* @__PURE__ */ jsx(BrandName, { brand, menuWidth: step.sidebarAccountMenuWidth })
|
|
257
331
|
] }),
|
|
258
332
|
/* @__PURE__ */ jsx(
|
|
259
333
|
ActionIcon,
|
|
@@ -278,40 +352,15 @@ function AppSidebar({
|
|
|
278
352
|
className: classes.navScroll,
|
|
279
353
|
classNames: { viewport: classes.navViewport },
|
|
280
354
|
children: /* @__PURE__ */ jsxs(Stack, { gap: 0, className: classes.nav, children: [
|
|
281
|
-
sections.map((section) => {
|
|
282
|
-
|
|
283
|
-
return /* @__PURE__ */ jsxs("div", { children: [
|
|
284
|
-
/* @__PURE__ */ jsx("div", { className: classes.sectionBand, children: /* @__PURE__ */ jsx(SectionLabel, { flush: true, children: section.label }) }),
|
|
285
|
-
/* @__PURE__ */ jsx(Stack, { gap: 1, children: renderSectionItems(section) })
|
|
286
|
-
] }, section.label);
|
|
287
|
-
}
|
|
288
|
-
const isOpen = !collapsedSections[section.label];
|
|
289
|
-
return /* @__PURE__ */ jsxs("div", { children: [
|
|
290
|
-
/* @__PURE__ */ jsxs(
|
|
291
|
-
UnstyledButton,
|
|
292
|
-
{
|
|
293
|
-
className: `${classes.sectionBand} ${classes.sectionHeader}`,
|
|
294
|
-
onClick: () => setCollapsedSections((prev) => ({
|
|
295
|
-
...prev,
|
|
296
|
-
[section.label]: !prev[section.label]
|
|
297
|
-
})),
|
|
298
|
-
"aria-expanded": isOpen,
|
|
299
|
-
children: [
|
|
300
|
-
/* @__PURE__ */ jsx(SectionLabel, { flush: true, children: section.label }),
|
|
301
|
-
/* @__PURE__ */ jsx(IconChevron, { open: isOpen })
|
|
302
|
-
]
|
|
303
|
-
}
|
|
304
|
-
),
|
|
305
|
-
/* @__PURE__ */ jsx(Collapse, { expanded: isOpen, children: /* @__PURE__ */ jsx(Stack, { gap: 1, children: renderSectionItems(section) }) })
|
|
306
|
-
] }, section.label);
|
|
307
|
-
}),
|
|
308
|
-
navExtra !== void 0 && navExtra !== null && /* @__PURE__ */ jsx("div", { className: classes.navExtra, children: navExtra })
|
|
355
|
+
sections.map((section) => /* @__PURE__ */ jsx(NavSection, { section, collapsed }, section.label)),
|
|
356
|
+
navBlocks.map((block) => /* @__PURE__ */ jsx(SidebarBlockView, { block }, block.key))
|
|
309
357
|
] })
|
|
310
358
|
}
|
|
311
359
|
),
|
|
312
|
-
account
|
|
360
|
+
(bottomBlocks.length > 0 || settingsRow !== null || account !== void 0) && /* @__PURE__ */ jsxs(Stack, { gap: 0, className: classes.footer, children: [
|
|
361
|
+
bottomBlocks.map((block) => /* @__PURE__ */ jsx(SidebarBlockView, { block }, block.key)),
|
|
313
362
|
settingsRow,
|
|
314
|
-
/* @__PURE__ */ jsx(
|
|
363
|
+
account !== void 0 && /* @__PURE__ */ jsx(
|
|
315
364
|
SidebarAccount,
|
|
316
365
|
{
|
|
317
366
|
state: account.state,
|
|
@@ -319,7 +368,7 @@ function AppSidebar({
|
|
|
319
368
|
...account.showEmail !== void 0 && { showEmail: account.showEmail }
|
|
320
369
|
}
|
|
321
370
|
)
|
|
322
|
-
] })
|
|
371
|
+
] })
|
|
323
372
|
] });
|
|
324
373
|
}
|
|
325
374
|
export {
|
|
@@ -40,30 +40,63 @@
|
|
|
40
40
|
|
|
41
41
|
/* Inactive rows: muted text + faint icon. Active rows keep the theme's ink text (via `--nl-color`)
|
|
42
42
|
* untouched — this selector only ever matches when neither `data-active` nor `aria-current="page"`
|
|
43
|
-
* is present.
|
|
44
|
-
|
|
43
|
+
* is present. `[data-ancestor]` is excluded too: it gets its own ink/accent treatment below and
|
|
44
|
+
* must not fall through to muted/faint here. */
|
|
45
|
+
.link:not([data-active]):not([aria-current='page']):not([data-ancestor]) {
|
|
45
46
|
color: var(--vx-muted) !important;
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
.link:not([data-active]):not([aria-current='page'])
|
|
49
|
+
.link:not([data-active]):not([aria-current='page']):not([data-ancestor])
|
|
49
50
|
:global(.mantine-NavLink-section[data-position='left']) {
|
|
50
51
|
color: var(--vx-faint);
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
/* Hover — ink-6% tint + ink text, for inactive rows only (an active row already carries the
|
|
54
|
-
* theme's stronger ink-9% tint and keeps its own hover via `--nl-hover
|
|
55
|
+
* theme's stronger ink-9% tint and keeps its own hover via `--nl-hover`; an ancestor row gets its
|
|
56
|
+
* own hover rule below so its accent icon survives the pointer). */
|
|
55
57
|
@media (hover: hover) {
|
|
56
|
-
.link:not([data-active]):not([aria-current='page']):hover {
|
|
58
|
+
.link:not([data-active]):not([aria-current='page']):not([data-ancestor]):hover {
|
|
57
59
|
background-color: color-mix(in srgb, var(--vx-ink) 6%, transparent) !important;
|
|
58
60
|
color: var(--vx-ink) !important;
|
|
59
61
|
}
|
|
60
62
|
|
|
61
|
-
.link:not([data-active]):not([aria-current='page']):hover
|
|
63
|
+
.link:not([data-active]):not([aria-current='page']):not([data-ancestor]):hover
|
|
62
64
|
:global(.mantine-NavLink-section[data-position='left']) {
|
|
63
65
|
color: var(--vx-ink);
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
68
|
|
|
69
|
+
/*
|
|
70
|
+
* Ancestor row (`SidebarItem.ancestor` — a parent on the path to the active row, never active
|
|
71
|
+
* itself, never `aria-current`): must read visibly WEAKER than active and visibly STRONGER than a
|
|
72
|
+
* hovered inactive row, since all three now share the same 6%/9% ink-tint family. Active carries a
|
|
73
|
+
* persistent `--nl-bg` ink-9% fill + weight 600 (theme `nav-link.module.css`); a hovered inactive
|
|
74
|
+
* row carries a transient ink-6% fill. An ancestor therefore gets NO fill at rest (unlike active)
|
|
75
|
+
* and NO weight change (still 400, unlike active's 600) — only the ink label + accent icon that an
|
|
76
|
+
* active row also carries, so it reads as "on the path" without claiming to BE the destination.
|
|
77
|
+
*/
|
|
78
|
+
.link[data-ancestor] {
|
|
79
|
+
color: var(--vx-ink) !important;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.link[data-ancestor] :global(.mantine-NavLink-section[data-position='left']) {
|
|
83
|
+
color: var(--vx-accent);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* Hover still adds the shared ink-6% tint (same as any inactive row), but must not let the plain
|
|
87
|
+
* inactive hover rule above flatten the icon back to ink — that rule is excluded via
|
|
88
|
+
* `:not([data-ancestor])`, and this one re-asserts the accent icon on top of the shared tint. */
|
|
89
|
+
@media (hover: hover) {
|
|
90
|
+
.link[data-ancestor]:hover {
|
|
91
|
+
background-color: color-mix(in srgb, var(--vx-ink) 6%, transparent) !important;
|
|
92
|
+
color: var(--vx-ink) !important;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.link[data-ancestor]:hover :global(.mantine-NavLink-section[data-position='left']) {
|
|
96
|
+
color: var(--vx-accent);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
67
100
|
/*
|
|
68
101
|
* Icon-only ghost buttons in shell chrome (collapse toggle, mobile close) — docs/DESIGN-SPEC.md
|
|
69
102
|
* §5: transparent, faint icon, hover ink-6% + ink, radius 6. `!important` for the same reason as
|
|
@@ -113,6 +146,22 @@
|
|
|
113
146
|
font-stretch: 88%;
|
|
114
147
|
}
|
|
115
148
|
|
|
149
|
+
/* Workspace switcher (`brand.menu`) — the brand name plus a chevron, as a row-shaped trigger. The
|
|
150
|
+
* chevron is faint until hover so the brand still reads as the brand, not as a control. */
|
|
151
|
+
.brandButton {
|
|
152
|
+
display: flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
gap: var(--vx-space-sidebar-section-label-gap);
|
|
155
|
+
min-width: 0;
|
|
156
|
+
padding: 0;
|
|
157
|
+
color: var(--vx-faint);
|
|
158
|
+
cursor: pointer;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.brandButton:hover {
|
|
162
|
+
color: var(--vx-ink);
|
|
163
|
+
}
|
|
164
|
+
|
|
116
165
|
/*
|
|
117
166
|
* Nav scroll region — a Mantine ScrollArea, whose overlay scrollbar is absolutely positioned inside
|
|
118
167
|
* the (position: relative; overflow: hidden) root, so it floats over the rows instead of reserving
|
|
@@ -151,7 +200,7 @@
|
|
|
151
200
|
|
|
152
201
|
/*
|
|
153
202
|
* Footer settings row — IDENTICAL geometry to a nav row (padding 6px 10px, radius 6, gap 10,
|
|
154
|
-
*
|
|
203
|
+
* 15px): muted text + faint icon at rest, ink-6% tint + ink on hover, exactly like an inactive
|
|
155
204
|
* nav item, so the footer never reads as a separate control family.
|
|
156
205
|
*/
|
|
157
206
|
.footerBtn {
|
|
@@ -211,7 +260,7 @@
|
|
|
211
260
|
}
|
|
212
261
|
|
|
213
262
|
/*
|
|
214
|
-
* Identity initials block (docs/DESIGN-SPEC.md §5): 28px, radius 7, ink-10% bg, mono
|
|
263
|
+
* Identity initials block (docs/DESIGN-SPEC.md §5): 28px, radius 7, ink-10% bg, mono micro (11px)
|
|
215
264
|
* weight 500, muted — replaces the generic person glyph for the authenticated state.
|
|
216
265
|
*/
|
|
217
266
|
.avatar {
|
|
@@ -236,7 +285,7 @@
|
|
|
236
285
|
color: var(--vx-ink);
|
|
237
286
|
}
|
|
238
287
|
|
|
239
|
-
/* Meta line under the name (email / "FREE · MEMBER") — mono
|
|
288
|
+
/* Meta line under the name (email / "FREE · MEMBER") — mono micro (11px), uppercase, faint, tracked
|
|
240
289
|
* 0.03em. */
|
|
241
290
|
.accountMeta {
|
|
242
291
|
font-family: var(--basalt-font-mono);
|
|
@@ -293,6 +342,214 @@
|
|
|
293
342
|
color: var(--vx-ink);
|
|
294
343
|
}
|
|
295
344
|
|
|
345
|
+
/*
|
|
346
|
+
* ── Sidebar blocks (docs/CONTROLS-SPEC.md §2.3) ────────────────────────────────────────────────
|
|
347
|
+
*
|
|
348
|
+
* Same spacing-only separation law as the nav sections above: no rules, no bands, no fills. A
|
|
349
|
+
* block's rows are 32px (`--vx-space-sidebar-block-row-height`) — one step under a nav row, since
|
|
350
|
+
* a block row is reference material rather than a destination.
|
|
351
|
+
*/
|
|
352
|
+
.block {
|
|
353
|
+
min-width: 0;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.blockHeader {
|
|
357
|
+
display: flex;
|
|
358
|
+
align-items: center;
|
|
359
|
+
gap: var(--vx-space-nav-icon-gap);
|
|
360
|
+
width: 100%;
|
|
361
|
+
padding: 0 var(--vx-space-row-inset-x);
|
|
362
|
+
margin-bottom: var(--vx-space-sidebar-section-label-gap);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/* The header's label takes the free width so a count badge / chevron sits hard right. */
|
|
366
|
+
.blockHeader > div:first-of-type {
|
|
367
|
+
flex: 1;
|
|
368
|
+
min-width: 0;
|
|
369
|
+
text-align: left;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/* Collapsible variant — text-only hover, exactly like `.sectionHeader`; never a band. */
|
|
373
|
+
.blockHeaderButton {
|
|
374
|
+
color: var(--vx-faint);
|
|
375
|
+
cursor: pointer;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.blockHeaderButton:hover .sectionLabel {
|
|
379
|
+
color: var(--vx-ink);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.blockIcon {
|
|
383
|
+
position: relative;
|
|
384
|
+
display: flex;
|
|
385
|
+
flex: none;
|
|
386
|
+
align-items: center;
|
|
387
|
+
color: var(--vx-faint);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/*
|
|
391
|
+
* Rail dot — the collapsed rail's whole representation of a `count`. Hidden in the expanded
|
|
392
|
+
* sidebar, where the count renders as a real `NavCountBadge` instead: a dot and a number saying
|
|
393
|
+
* the same thing twice is noise, and the rail has no room for the number.
|
|
394
|
+
*/
|
|
395
|
+
.blockDot {
|
|
396
|
+
position: absolute;
|
|
397
|
+
top: -2px;
|
|
398
|
+
inset-inline-end: -3px;
|
|
399
|
+
display: none;
|
|
400
|
+
width: 7px;
|
|
401
|
+
height: 7px;
|
|
402
|
+
border-radius: 999px;
|
|
403
|
+
background-color: var(--vx-accent);
|
|
404
|
+
box-shadow: 0 0 0 2px var(--mantine-color-body);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.blockItems {
|
|
408
|
+
display: flex;
|
|
409
|
+
flex-direction: column;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/* Block row — 32px, small text, muted; the same ink-6% hover every other sidebar row uses. */
|
|
413
|
+
.blockRow {
|
|
414
|
+
display: flex;
|
|
415
|
+
align-items: center;
|
|
416
|
+
gap: var(--vx-space-nav-icon-gap);
|
|
417
|
+
min-height: var(--vx-space-sidebar-block-row-height);
|
|
418
|
+
padding: 0 var(--vx-space-row-inset-x) !important;
|
|
419
|
+
border-radius: var(--vx-radius-ctrl) !important;
|
|
420
|
+
font-size: var(--vx-text-sm);
|
|
421
|
+
color: var(--vx-muted);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.blockRow :global(.mantine-NavLink-label) {
|
|
425
|
+
font-size: var(--vx-text-sm);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.blockRow :global(.mantine-NavLink-section[data-position='left']) {
|
|
429
|
+
margin-inline-end: 0;
|
|
430
|
+
color: var(--vx-faint);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
@media (hover: hover) {
|
|
434
|
+
.blockRow:not(.blockRowStatic):hover {
|
|
435
|
+
background-color: color-mix(in srgb, var(--vx-ink) 6%, transparent) !important;
|
|
436
|
+
color: var(--vx-ink) !important;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/* A non-interactive row (the "Recents" shape) keeps the geometry and drops every affordance. */
|
|
441
|
+
.blockRowStatic {
|
|
442
|
+
cursor: default;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.blockRowLabel {
|
|
446
|
+
flex: 1;
|
|
447
|
+
min-width: 0;
|
|
448
|
+
overflow: hidden;
|
|
449
|
+
text-overflow: ellipsis;
|
|
450
|
+
white-space: nowrap;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.blockRowMeta {
|
|
454
|
+
flex: none;
|
|
455
|
+
font-size: var(--vx-text-micro);
|
|
456
|
+
color: var(--vx-faint);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/* Status dot standing in for a missing row icon — reads the per-scheme `--vx-status-*` solid. */
|
|
460
|
+
.blockToneDot {
|
|
461
|
+
flex: none;
|
|
462
|
+
width: 6px;
|
|
463
|
+
height: 6px;
|
|
464
|
+
border-radius: 999px;
|
|
465
|
+
background-color: var(--vx-status-neutral);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.blockToneDot[data-tone='good'] {
|
|
469
|
+
background-color: var(--vx-status-good);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.blockToneDot[data-tone='warn'] {
|
|
473
|
+
background-color: var(--vx-status-warn);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.blockToneDot[data-tone='bad'] {
|
|
477
|
+
background-color: var(--vx-status-bad);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/* "Show more" — a text affordance, not a button surface. */
|
|
481
|
+
.blockMore {
|
|
482
|
+
min-height: var(--vx-space-sidebar-block-row-height);
|
|
483
|
+
padding: 0 var(--vx-space-row-inset-x);
|
|
484
|
+
text-align: left;
|
|
485
|
+
font-size: var(--vx-text-sm);
|
|
486
|
+
color: var(--vx-faint);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.blockMore:hover {
|
|
490
|
+
color: var(--vx-ink);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/* Progress block — label + `n of m` over a thin track. */
|
|
494
|
+
.blockProgress {
|
|
495
|
+
display: block;
|
|
496
|
+
width: 100%;
|
|
497
|
+
padding: var(--vx-space-row-inset-y) var(--vx-space-row-inset-x);
|
|
498
|
+
border-radius: var(--vx-radius-ctrl);
|
|
499
|
+
text-align: left;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.blockProgress:hover {
|
|
503
|
+
background-color: color-mix(in srgb, var(--vx-ink) 6%, transparent);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.blockProgressHead {
|
|
507
|
+
display: flex;
|
|
508
|
+
align-items: center;
|
|
509
|
+
justify-content: space-between;
|
|
510
|
+
gap: var(--vx-space-nav-icon-gap);
|
|
511
|
+
margin-bottom: var(--vx-space-sidebar-section-label-gap);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/* The rail's progress mark, rendered on the settings row (never inside the block). */
|
|
515
|
+
.blockRing {
|
|
516
|
+
display: flex;
|
|
517
|
+
flex: none;
|
|
518
|
+
align-items: center;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/*
|
|
522
|
+
* Flat footer links — `settingsMenuItems` at three or fewer, rendered as rows instead of a gear
|
|
523
|
+
* menu (see `AppSidebar`'s JSDoc for the threshold). Each row IS a `.footerBtn`, so the two
|
|
524
|
+
* renderings are the same geometry.
|
|
525
|
+
*/
|
|
526
|
+
.footerLinks {
|
|
527
|
+
display: flex;
|
|
528
|
+
flex-direction: column;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/* Fixed 18px icon column for a flat footer row — an icon-less row keeps the label aligned with
|
|
532
|
+
* its neighbours instead of sliding left. */
|
|
533
|
+
.footerIconSlot {
|
|
534
|
+
display: flex;
|
|
535
|
+
flex: none;
|
|
536
|
+
align-items: center;
|
|
537
|
+
justify-content: center;
|
|
538
|
+
width: 18px;
|
|
539
|
+
color: var(--vx-faint);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.footerBtn:hover .footerIconSlot {
|
|
543
|
+
color: var(--vx-ink);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.footerVersion {
|
|
547
|
+
padding: var(--vx-space-row-inset-y) var(--vx-space-row-inset-x);
|
|
548
|
+
font-family: var(--basalt-font-mono);
|
|
549
|
+
font-size: var(--vx-text-micro);
|
|
550
|
+
color: var(--vx-faint);
|
|
551
|
+
}
|
|
552
|
+
|
|
296
553
|
/*
|
|
297
554
|
* Inline child items — indent under the parent with the ONE allowed sidebar line: a 1px
|
|
298
555
|
* `--vx-divider` left guide on the WRAPPER (not per row, so hover tints don't break the line).
|
|
@@ -350,9 +607,9 @@
|
|
|
350
607
|
}
|
|
351
608
|
|
|
352
609
|
/*
|
|
353
|
-
* Icon-rail (collapsed) — desktop only. Gated behind min-width: sm (48em) so the
|
|
354
|
-
*
|
|
355
|
-
* nav labels; center the remaining icons.
|
|
610
|
+
* Icon-rail (collapsed) — desktop only. Gated behind min-width: sm (48em) so the sidebar doesn't
|
|
611
|
+
* exist at all below that breakpoint (see `MobileNav`) regardless of the persisted collapse flag.
|
|
612
|
+
* Hide the brand text, section labels and nav labels; center the remaining icons.
|
|
356
613
|
*/
|
|
357
614
|
@media (min-width: 48em) {
|
|
358
615
|
.root[data-collapsed] .brand {
|
|
@@ -421,14 +678,40 @@
|
|
|
421
678
|
display: none;
|
|
422
679
|
}
|
|
423
680
|
|
|
424
|
-
/*
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
*
|
|
429
|
-
*
|
|
430
|
-
*
|
|
431
|
-
|
|
681
|
+
/*
|
|
682
|
+
* Block rail projection (`sidebarBlockRail`). `hidden` and `ring` blocks leave the rail entirely
|
|
683
|
+
* — a `custom` block is arbitrary consumer content with no icon-column representation (the
|
|
684
|
+
* treatment `navExtra` had), and a progress block's rail form is the ring on the settings row
|
|
685
|
+
* below, not a squashed track. A `dot` block keeps its icon and nothing else.
|
|
686
|
+
*
|
|
687
|
+
* All of it is CSS, never a JS conditional on `collapsed`: the navbar is permanently
|
|
688
|
+
* `collapsed: { mobile: true }` (there is no mobile drawer — see `MobileNav`), so `collapsed`
|
|
689
|
+
* means only the desktop expanded-vs-rail state, and this media query is what applies it.
|
|
690
|
+
*/
|
|
691
|
+
.root[data-collapsed] .block[data-rail='hidden'],
|
|
692
|
+
.root[data-collapsed] .block[data-rail='ring'] {
|
|
693
|
+
display: none;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
.root[data-collapsed] .block[data-rail='dot'] .blockHeader {
|
|
697
|
+
justify-content: center;
|
|
698
|
+
padding: 0;
|
|
699
|
+
margin-bottom: 0;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
.root[data-collapsed] .block[data-rail='dot'] .blockHeader > *:not(.blockIcon) {
|
|
703
|
+
display: none;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
.root[data-collapsed] .block[data-rail='dot'] .blockItems {
|
|
707
|
+
display: none;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
.root[data-collapsed] .blockDot {
|
|
711
|
+
display: block;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
.root[data-collapsed] .footerVersion {
|
|
432
715
|
display: none;
|
|
433
716
|
}
|
|
434
717
|
}
|