basalt-ui 1.24.0 → 1.26.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 +25 -0
- package/MIGRATING.md +547 -21
- package/README.md +46 -33
- package/agent/rules/basalt-batteries.md +205 -0
- package/agent/rules/basalt-charts.md +125 -555
- package/agent/rules/basalt-controls.md +155 -0
- package/agent/rules/basalt-mantine.md +146 -439
- package/agent/rules/basalt-state.md +113 -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 +1275 -31
- package/configs/oxlint-plugin.test.ts +947 -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 +30 -0
- package/dist/controls/enum-filter.d.ts.map +1 -0
- package/dist/controls/enum-filter.js +91 -0
- package/dist/controls/filter-context.d.ts +55 -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 +19 -0
- package/dist/controls/index.d.ts.map +1 -0
- package/dist/controls/index.js +44 -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/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 +29 -0
- package/dist/controls/range-filter.d.ts.map +1 -0
- package/dist/controls/range-filter.js +120 -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/query-state.d.ts +1 -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 +44 -9
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +80 -48
- package/dist/dashboard/stat-card.module.css +82 -0
- package/dist/dashboard/stat-card.module.css.d.ts +11 -0
- package/dist/data/data-table.d.ts +102 -20
- package/dist/data/data-table.d.ts.map +1 -1
- package/dist/data/data-table.js +166 -65
- package/dist/data/table.d.ts +1 -1
- package/dist/data/table.d.ts.map +1 -1
- 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 +82 -7
- package/dist/guard/types.d.ts +3 -3
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/index.d.ts +12 -2
- 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 +81 -0
- package/dist/router-tanstack/search-store.d.ts.map +1 -0
- package/dist/router-tanstack/search-store.js +187 -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 +392 -0
- package/dist/state/fields.d.ts.map +1 -0
- package/dist/state/fields.js +369 -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 +48 -0
- package/dist/state/persisted.d.ts.map +1 -0
- package/dist/state/persisted.js +105 -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 +155 -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 +37 -0
- package/dist/widget-header/widget-header.d.ts.map +1 -0
- package/dist/widget-header/widget-header.js +77 -0
- package/dist/widget-header/widget-header.module.css +335 -0
- package/dist/widget-header/widget-header.module.css.d.ts +24 -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 +637 -0
- package/src/controls/enum-filter.tsx +159 -0
- package/src/controls/filter-context.tsx +153 -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 +48 -0
- package/src/controls/multi-select-filter.tsx +140 -0
- package/src/controls/peer-boundary.test.ts +85 -0
- package/src/controls/props-contract.test.tsx +106 -0
- package/src/controls/range-filter.tsx +184 -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/query-state.tsx +1 -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 +82 -0
- package/src/dashboard/stat-card.module.css.d.ts +11 -0
- package/src/dashboard/stat-card.test.tsx +67 -1
- package/src/dashboard/stat-card.tsx +209 -68
- package/src/data/data-table.test.tsx +282 -6
- package/src/data/data-table.tsx +429 -94
- package/src/data/table.ts +2 -0
- package/src/guard/check-source.test.ts +126 -13
- package/src/guard/grace.test.ts +149 -0
- package/src/guard/index.ts +172 -29
- package/src/guard/types.ts +4 -2
- package/src/index.ts +46 -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 +31 -0
- package/src/router-tanstack/index.ts +24 -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 +661 -0
- package/src/router-tanstack/search-store.test.ts +418 -0
- package/src/router-tanstack/search-store.ts +380 -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 +258 -0
- package/src/state/fields.ts +892 -0
- package/src/state/persisted.ts +211 -0
- package/src/state.ts +46 -240
- package/src/styles.css +13 -0
- package/src/surfaces.ts +203 -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 +335 -0
- package/src/widget-header/widget-header.module.css.d.ts +24 -0
- package/src/widget-header/widget-header.test.tsx +187 -0
- package/src/widget-header/widget-header.tsx +168 -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 -269
- 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
|
@@ -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
|
|
@@ -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 {
|