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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createElement, useMemo } from "react";
|
|
2
2
|
import { Link, useMatchRoute, useRouterState } from "@tanstack/react-router";
|
|
3
|
+
import { flattenNav } from "./nav.js";
|
|
3
4
|
function useNav(config, opts) {
|
|
4
5
|
const matchRoute = useMatchRoute();
|
|
5
6
|
const href = useRouterState({ select: (s) => s.location.href });
|
|
@@ -7,29 +8,52 @@ function useNav(config, opts) {
|
|
|
7
8
|
const isActiveOverride = opts?.isActive;
|
|
8
9
|
const sections = useMemo(() => {
|
|
9
10
|
const badgeOf = (id) => badges?.[id];
|
|
10
|
-
const
|
|
11
|
+
const matchOf = (item) => isActiveOverride?.(item) ?? Boolean(
|
|
11
12
|
matchRoute({
|
|
12
13
|
to: item.link.to,
|
|
13
14
|
...item.link.params !== void 0 && { params: item.link.params },
|
|
14
|
-
// Prefix match by default so a parent stays lit on its children; `'/'` would then match
|
|
15
|
-
// everything, so it is always exact.
|
|
16
15
|
fuzzy: item.exact !== true && item.link.to !== "/"
|
|
17
16
|
})
|
|
18
17
|
);
|
|
18
|
+
const allItems = flattenNav(config);
|
|
19
|
+
const specificity = (to) => to.split("/").filter(Boolean).length;
|
|
20
|
+
const matchedById = /* @__PURE__ */ new Map();
|
|
21
|
+
let winnerId;
|
|
22
|
+
let winnerTo;
|
|
23
|
+
for (const item of allItems) {
|
|
24
|
+
const matched = matchOf(item);
|
|
25
|
+
matchedById.set(item.id, matched);
|
|
26
|
+
if (matched && (winnerTo === void 0 || specificity(item.link.to) > specificity(winnerTo))) {
|
|
27
|
+
winnerId = item.id;
|
|
28
|
+
winnerTo = item.link.to;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const resolveState = (item) => {
|
|
32
|
+
if (item.id === winnerId) return { active: true, ancestor: false };
|
|
33
|
+
const matched = matchedById.get(item.id) ?? false;
|
|
34
|
+
const ancestor = winnerTo !== void 0 && matched && winnerTo.startsWith(`${item.link.to}/`);
|
|
35
|
+
return { active: false, ancestor };
|
|
36
|
+
};
|
|
19
37
|
const toSidebarItem = (item) => {
|
|
20
|
-
const Anchor = (props) => createElement(Link, {
|
|
38
|
+
const Anchor = (props) => createElement(Link, {
|
|
39
|
+
...item.link,
|
|
40
|
+
activeOptions: { exact: true },
|
|
41
|
+
...props
|
|
42
|
+
});
|
|
21
43
|
const badge = badgeOf(item.id);
|
|
22
44
|
const children = item.children?.map(toSidebarItem);
|
|
45
|
+
const { active, ancestor } = resolveState(item);
|
|
23
46
|
return {
|
|
24
47
|
key: item.id,
|
|
25
48
|
label: item.label,
|
|
26
49
|
icon: item.icon,
|
|
27
50
|
Anchor,
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
active
|
|
51
|
+
// Exactly one destination in the WHOLE definition reads `active` (`resolveState` above).
|
|
52
|
+
// Rolling that up into a mobile SLOT (any descendant active → the slot reads active) is
|
|
53
|
+
// still `projectMobileNav`'s job, which reads `children` for that. `ancestor` is the
|
|
54
|
+
// desktop-only middle state: a matched item that sits on the winner's path but isn't it.
|
|
55
|
+
active,
|
|
56
|
+
...ancestor && { ancestor },
|
|
33
57
|
...item.short !== void 0 && { short: item.short },
|
|
34
58
|
...item.mobile !== void 0 && { mobile: item.mobile },
|
|
35
59
|
...item.disabled !== void 0 && { disabled: item.disabled },
|
|
@@ -47,7 +71,6 @@ function useNav(config, opts) {
|
|
|
47
71
|
}));
|
|
48
72
|
}, [config, matchRoute, href, badges, isActiveOverride]);
|
|
49
73
|
const mobile = config.mobile;
|
|
50
|
-
const moreExtra = opts?.moreExtra;
|
|
51
74
|
const getScrollElement = opts?.getScrollElement;
|
|
52
75
|
const mobileNav = useMemo(() => {
|
|
53
76
|
const groupLabelById = new Map(config.groups.map((g) => [g.id, g.label]));
|
|
@@ -60,10 +83,9 @@ function useNav(config, opts) {
|
|
|
60
83
|
...mobile?.maxTabs !== void 0 && { maxTabs: mobile.maxTabs },
|
|
61
84
|
...mobile?.menuMax !== void 0 && { menuMax: mobile.menuMax },
|
|
62
85
|
...mobile?.moreLabel !== void 0 && { moreLabel: mobile.moreLabel },
|
|
63
|
-
...moreExtra !== void 0 && { moreExtra },
|
|
64
86
|
...getScrollElement !== void 0 && { getScrollElement }
|
|
65
87
|
};
|
|
66
|
-
}, [config, mobile,
|
|
88
|
+
}, [config, mobile, getScrollElement]);
|
|
67
89
|
return { sections, mobileNav };
|
|
68
90
|
}
|
|
69
91
|
export {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-nav.test.d.ts","sourceRoot":"","sources":["../../src/router-tanstack/use-nav.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/section/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,WAAW,CAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { WidgetHeaderProps } from '../widget-header/index.js';
|
|
3
|
+
export type SectionProps = Omit<WidgetHeaderProps, 'tier'> & {
|
|
4
|
+
/** Rendered in the header row, in its own `CtlSlot` — hidden while the section is collapsed. */
|
|
5
|
+
tabs?: ReactNode;
|
|
6
|
+
/** Renders a chevron toggle in the header; the header stays drawn when closed, only `children`
|
|
7
|
+
* (and `tabs`) unmount. @default false */
|
|
8
|
+
collapsible?: boolean;
|
|
9
|
+
/** Persists the fold state at `basalt:section:<persistKey>`. Omit for a local, unpersisted fold
|
|
10
|
+
* (`useState`). */
|
|
11
|
+
persistKey?: string;
|
|
12
|
+
/** The fold state a section opens on, respected only while no persisted value exists — a section
|
|
13
|
+
* a reader has closed stays closed. `false` is what a long secondary block wants. @default true */
|
|
14
|
+
defaultOpen?: boolean;
|
|
15
|
+
/** Rendered directly under the header and ALWAYS visible, collapsed or not: a collapsed section
|
|
16
|
+
* still states its headline figures, which is what makes collapsing it a real option. Keep it to
|
|
17
|
+
* one row — the body is what `children` is for. */
|
|
18
|
+
summary?: ReactNode;
|
|
19
|
+
/** Turns the root into a scroll anchor (`id` + `scroll-margin-top` cleared for both sticky bars). */
|
|
20
|
+
id?: string;
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
};
|
|
23
|
+
export declare function Section({ title, icon, subtitle, info, value, delta, deltaPeriod, sparkline, count, actions, tabs, collapsible, persistKey, defaultOpen, summary, id, children, }: SectionProps): import("react").JSX.Element;
|
|
24
|
+
//# sourceMappingURL=section.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"section.d.ts","sourceRoot":"","sources":["../../src/section/section.tsx"],"names":[],"mappings":"AA8CA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAGzD,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAAG;IAC3D,gGAAgG;IAChG,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;8CAC0C;IAC1C,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;uBACmB;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;uGACmG;IACnG,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;;uDAEmD;IACnD,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,qGAAqG;IACrG,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,SAAS,CAAA;CACpB,CAAA;AAgFD,wBAAgB,OAAO,CAAC,EACtB,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,KAAK,EACL,WAAW,EACX,SAAS,EACT,KAAK,EACL,OAAO,EACP,IAAI,EACJ,WAAmB,EACnB,UAAU,EACV,WAAkB,EAClB,OAAO,EACP,EAAE,EACF,QAAQ,GACT,EAAE,YAAY,+BAiDd"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Children, useId, useMemo, useState } from "react";
|
|
3
|
+
import { CtlSlot } from "../theme/index.js";
|
|
4
|
+
import { createPersistedState } from "../state.js";
|
|
5
|
+
import { WidgetHeader } from "../widget-header/index.js";
|
|
6
|
+
import classes from "./section.module.css";
|
|
7
|
+
function isDev() {
|
|
8
|
+
return process.env["NODE_ENV"] !== "production";
|
|
9
|
+
}
|
|
10
|
+
function warnPastActionBudget(title, actions) {
|
|
11
|
+
if (!isDev()) return;
|
|
12
|
+
const count = Children.count(actions);
|
|
13
|
+
if (count <= 3) return;
|
|
14
|
+
console.warn(
|
|
15
|
+
`Section "${title}": ${count} actions exceeds the \u22643 budget (docs/CONTROLS-SPEC.md C6) \u2014 move the rest behind a menu.`
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
const UNPERSISTED_KEY = "__local__";
|
|
19
|
+
function useSectionOpen(persistKey, defaultOpen) {
|
|
20
|
+
const [localOpen, setLocalOpen] = useState(defaultOpen);
|
|
21
|
+
const usePersistedOpen = useMemo(
|
|
22
|
+
() => createPersistedState({
|
|
23
|
+
key: `section:${persistKey ?? UNPERSISTED_KEY}`,
|
|
24
|
+
version: 1,
|
|
25
|
+
initial: defaultOpen
|
|
26
|
+
}),
|
|
27
|
+
[persistKey, defaultOpen]
|
|
28
|
+
);
|
|
29
|
+
const [persistedOpen, setPersistedOpen] = usePersistedOpen();
|
|
30
|
+
if (persistKey !== void 0) return [persistedOpen, setPersistedOpen];
|
|
31
|
+
return [localOpen, setLocalOpen];
|
|
32
|
+
}
|
|
33
|
+
function ChevronToggle({
|
|
34
|
+
open,
|
|
35
|
+
onToggle,
|
|
36
|
+
controls
|
|
37
|
+
}) {
|
|
38
|
+
return /* @__PURE__ */ jsx(
|
|
39
|
+
"button",
|
|
40
|
+
{
|
|
41
|
+
type: "button",
|
|
42
|
+
className: classes.chevron,
|
|
43
|
+
"data-open": open,
|
|
44
|
+
"aria-expanded": open,
|
|
45
|
+
"aria-controls": controls,
|
|
46
|
+
"aria-label": open ? "Collapse section" : "Expand section",
|
|
47
|
+
onClick: onToggle,
|
|
48
|
+
children: /* @__PURE__ */ jsx(
|
|
49
|
+
"svg",
|
|
50
|
+
{
|
|
51
|
+
width: 12,
|
|
52
|
+
height: 12,
|
|
53
|
+
viewBox: "0 0 24 24",
|
|
54
|
+
fill: "none",
|
|
55
|
+
stroke: "currentColor",
|
|
56
|
+
strokeWidth: 2.5,
|
|
57
|
+
"aria-hidden": "true",
|
|
58
|
+
children: /* @__PURE__ */ jsx("path", { d: "M6 9l6 6 6-6", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
function Section({
|
|
65
|
+
title,
|
|
66
|
+
icon,
|
|
67
|
+
subtitle,
|
|
68
|
+
info,
|
|
69
|
+
value,
|
|
70
|
+
delta,
|
|
71
|
+
deltaPeriod,
|
|
72
|
+
sparkline,
|
|
73
|
+
count,
|
|
74
|
+
actions,
|
|
75
|
+
tabs,
|
|
76
|
+
collapsible = false,
|
|
77
|
+
persistKey,
|
|
78
|
+
defaultOpen = true,
|
|
79
|
+
summary,
|
|
80
|
+
id,
|
|
81
|
+
children
|
|
82
|
+
}) {
|
|
83
|
+
const [open, setOpen] = useSectionOpen(persistKey, defaultOpen);
|
|
84
|
+
const bodyId = useId();
|
|
85
|
+
if (actions !== void 0) warnPastActionBudget(title, actions);
|
|
86
|
+
const headerActions = tabs !== void 0 || actions !== void 0 || collapsible ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
87
|
+
tabs !== void 0 && open && /* @__PURE__ */ jsx(CtlSlot, { children: tabs }),
|
|
88
|
+
actions !== void 0 && /* @__PURE__ */ jsx(CtlSlot, { children: actions }),
|
|
89
|
+
collapsible && /* @__PURE__ */ jsx(ChevronToggle, { open, onToggle: () => setOpen(!open), controls: bodyId })
|
|
90
|
+
] }) : void 0;
|
|
91
|
+
return /* @__PURE__ */ jsxs(
|
|
92
|
+
"div",
|
|
93
|
+
{
|
|
94
|
+
className: classes.root,
|
|
95
|
+
...id !== void 0 && { id },
|
|
96
|
+
...id !== void 0 && {
|
|
97
|
+
style: {
|
|
98
|
+
scrollMarginTop: "calc(var(--app-shell-header-height, 0px) + var(--basalt-page-bar-h, 0px))"
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
children: [
|
|
102
|
+
/* @__PURE__ */ jsx(
|
|
103
|
+
WidgetHeader,
|
|
104
|
+
{
|
|
105
|
+
tier: "section",
|
|
106
|
+
title,
|
|
107
|
+
...icon !== void 0 && { icon },
|
|
108
|
+
...subtitle !== void 0 && { subtitle },
|
|
109
|
+
...info !== void 0 && { info },
|
|
110
|
+
...value !== void 0 && { value },
|
|
111
|
+
...delta !== void 0 && { delta },
|
|
112
|
+
...deltaPeriod !== void 0 && { deltaPeriod },
|
|
113
|
+
...sparkline !== void 0 && { sparkline },
|
|
114
|
+
...count !== void 0 && { count },
|
|
115
|
+
...headerActions !== void 0 && { actions: headerActions }
|
|
116
|
+
}
|
|
117
|
+
),
|
|
118
|
+
summary !== void 0 && /* @__PURE__ */ jsx("div", { className: classes.summary, children: summary }),
|
|
119
|
+
open && /* @__PURE__ */ jsx("div", { id: bodyId, className: classes.body, children })
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
export {
|
|
125
|
+
Section
|
|
126
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Section — the tier-2 heading composer (docs/CONTROLS-SPEC.md §2.2). No border/background of its
|
|
3
|
+
* own — one shaded container level per page belongs to the cards inside a Section
|
|
4
|
+
* (`ChartCard`/`StatCard`), not to the Section itself.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* 12px from the header to the body, and the same between two body children — one rhythm, read from
|
|
9
|
+
* the stack scale (`--vx-space-stack-md`) instead of the card-inset scale
|
|
10
|
+
* (`--mantine-spacing-sm`, 13px). A Section is a page-level GROUPING, so its gaps belong to the
|
|
11
|
+
* vertical rhythm, not to the inset ladder its cards use.
|
|
12
|
+
*/
|
|
13
|
+
.root {
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
gap: var(--vx-space-stack-md, 0.75rem);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.body {
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-direction: column;
|
|
22
|
+
gap: var(--vx-space-stack-md, 0.75rem);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/*
|
|
26
|
+
* `summary` — the always-visible row under the header. No border, no background (the Section owns
|
|
27
|
+
* neither); it is a plain flow row, so a consumer's `Group` of stats or one line of `Text` lands
|
|
28
|
+
* where the body would have started.
|
|
29
|
+
*/
|
|
30
|
+
.summary {
|
|
31
|
+
display: flex;
|
|
32
|
+
flex-direction: column;
|
|
33
|
+
gap: var(--mantine-spacing-xs, 0.625rem);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/*
|
|
37
|
+
* The collapse chevron — a plain control, not a Mantine ActionIcon, so it sits directly in the header
|
|
38
|
+
* row (its own CtlSlot fragment would nest a theme provider around one glyph). It is sized to the
|
|
39
|
+
* `ctl` tier BY HAND for exactly that reason: a 12px glyph in a 20px box was a different height from
|
|
40
|
+
* every other control in the same header, which is what made the section header read as ragged. 30px
|
|
41
|
+
* square, glyph centred, no padding — the box IS the tier.
|
|
42
|
+
*/
|
|
43
|
+
.chevron {
|
|
44
|
+
/* `relative` is the anchor for the C15 hit-area overlay in the `< sm` block at the bottom. */
|
|
45
|
+
position: relative;
|
|
46
|
+
display: inline-flex;
|
|
47
|
+
flex: none;
|
|
48
|
+
align-items: center;
|
|
49
|
+
justify-content: center;
|
|
50
|
+
/* `--vx-space-control-height-ctl` is a bare rem — the theme's `ctlSizeVars` (`theme/index.ts`)
|
|
51
|
+
* reconstructs every OTHER ctl-tier box as `calc(<rem> * var(--mantine-scale))` so it tracks
|
|
52
|
+
* Mantine's `theme.scale`; read raw here, this box used to stay fixed while a scaled Button/Input
|
|
53
|
+
* beside it grew or shrank, reintroducing the exact raggedness this control was sized to fix. */
|
|
54
|
+
width: calc(var(--vx-space-control-height-ctl) * var(--mantine-scale, 1));
|
|
55
|
+
height: calc(var(--vx-space-control-height-ctl) * var(--mantine-scale, 1));
|
|
56
|
+
appearance: none;
|
|
57
|
+
margin: 0;
|
|
58
|
+
padding: 0;
|
|
59
|
+
border: none;
|
|
60
|
+
background: none;
|
|
61
|
+
border-radius: var(--vx-radius-ctrl);
|
|
62
|
+
color: var(--vx-muted);
|
|
63
|
+
cursor: pointer;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.chevron:hover,
|
|
67
|
+
.chevron:focus-visible {
|
|
68
|
+
color: var(--vx-ink);
|
|
69
|
+
background: var(--vx-surface-subtle);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.chevron svg {
|
|
73
|
+
transition: transform 150ms ease;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.chevron[data-open='false'] svg {
|
|
77
|
+
transform: rotate(-90deg);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/*
|
|
81
|
+
* Law C15 — the chevron is a 12px glyph in a 20px box, which is a fine mouse target and an
|
|
82
|
+
* unusable thumb one. Below `sm` an `::after` overlay lifts the HIT area to
|
|
83
|
+
* `--vx-space-touch-control-height` (36px at level 0) while the visible glyph stays put — the same
|
|
84
|
+
* overlay `shell/page-bar.module.css` draws for the bar's own controls. `overflow: visible` is part
|
|
85
|
+
* of the fix, not tidying: an overlay that extends past the box is clipped by any `overflow:
|
|
86
|
+
* hidden` on the host (which is what made the identical overlay a no-op on Mantine's Button roots).
|
|
87
|
+
*/
|
|
88
|
+
@media (max-width: 47.99375em) {
|
|
89
|
+
.chevron {
|
|
90
|
+
overflow: visible;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.chevron::after {
|
|
94
|
+
content: '';
|
|
95
|
+
position: absolute;
|
|
96
|
+
top: 50%;
|
|
97
|
+
left: 50%;
|
|
98
|
+
translate: -50% -50%;
|
|
99
|
+
width: 100%;
|
|
100
|
+
height: 100%;
|
|
101
|
+
min-width: var(--vx-space-touch-control-height, 2.25rem);
|
|
102
|
+
min-height: var(--vx-space-touch-control-height, 2.25rem);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed CSS-module declaration (the S4 CSS-modules path). Named string props — not an index
|
|
3
|
+
* signature — so dot access stays verbatim under the package's strict flags.
|
|
4
|
+
*/
|
|
5
|
+
declare const classes: {
|
|
6
|
+
readonly root: string
|
|
7
|
+
readonly body: string
|
|
8
|
+
readonly summary: string
|
|
9
|
+
readonly chevron: string
|
|
10
|
+
}
|
|
11
|
+
export default classes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"section.test.d.ts","sourceRoot":"","sources":["../../src/section/section.test.tsx"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-breadcrumbs.d.ts","sourceRoot":"","sources":["../../src/shell/app-breadcrumbs.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"app-breadcrumbs.d.ts","sourceRoot":"","sources":["../../src/shell/app-breadcrumbs.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAmB7C,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,MAAM,EACN,YAAY,EACZ,UAAU,EACV,IAAI,GACL,EAAE;IACD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,2FAA2F;IAC3F,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B;;;OAGG;IACH,YAAY,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IACpC,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,sCAqDA"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Anchor, Group, Text } from "@mantine/core";
|
|
3
3
|
import { VX } from "../tokens/index.js";
|
|
4
|
+
import classes from "./app-header.module.css";
|
|
4
5
|
const crumbStyle = { fontSize: VX.text.md, color: "var(--vx-faint)" };
|
|
5
6
|
const separatorStyle = { fontSize: VX.text.md, color: "var(--vx-surface-border)" };
|
|
6
7
|
const currentStyle = {
|
|
@@ -19,21 +20,23 @@ function AppBreadcrumbs({
|
|
|
19
20
|
}) {
|
|
20
21
|
if (!page) return null;
|
|
21
22
|
return /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", style: { minWidth: 0 }, children: [
|
|
22
|
-
section && /* @__PURE__ */ jsxs(
|
|
23
|
-
/* @__PURE__ */
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
/* @__PURE__ */
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
/* @__PURE__ */
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
/* @__PURE__ */
|
|
36
|
-
|
|
23
|
+
(section !== void 0 || parent !== void 0) && /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", className: classes.crumbAncestors, style: { minWidth: 0 }, children: [
|
|
24
|
+
section && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
25
|
+
/* @__PURE__ */ jsx(Text, { style: crumbStyle, truncate: true, children: section }),
|
|
26
|
+
/* @__PURE__ */ jsx(Text, { style: separatorStyle, children: "/" })
|
|
27
|
+
] }),
|
|
28
|
+
parent && parentAnchor && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
29
|
+
/* @__PURE__ */ jsx(Anchor, { style: crumbStyle, underline: "never", component: parentAnchor, truncate: true, children: parent }),
|
|
30
|
+
/* @__PURE__ */ jsx(Text, { style: separatorStyle, children: "/" })
|
|
31
|
+
] }),
|
|
32
|
+
parent && !parentAnchor && parentHref && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
33
|
+
/* @__PURE__ */ jsx(Anchor, { style: crumbStyle, underline: "never", href: parentHref, truncate: true, children: parent }),
|
|
34
|
+
/* @__PURE__ */ jsx(Text, { style: separatorStyle, children: "/" })
|
|
35
|
+
] }),
|
|
36
|
+
parent && !parentAnchor && !parentHref && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
37
|
+
/* @__PURE__ */ jsx(Text, { style: crumbStyle, truncate: true, children: parent }),
|
|
38
|
+
/* @__PURE__ */ jsx(Text, { style: separatorStyle, children: "/" })
|
|
39
|
+
] })
|
|
37
40
|
] }),
|
|
38
41
|
/* @__PURE__ */ jsx(Text, { style: currentStyle, truncate: true, children: page })
|
|
39
42
|
] });
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
/* App-shell top bar layout.
|
|
2
|
-
* [
|
|
3
|
-
*
|
|
4
|
-
*
|
|
1
|
+
/* App-shell top bar layout. ONE 48px row on every viewport —
|
|
2
|
+
* [breadcrumb] ········ [page bar row 1] | [global actions].
|
|
3
|
+
*
|
|
4
|
+
* The pre-1.26.0 mobile layout wrapped to a second, always-reserved row for the page actions and
|
|
5
|
+
* scrolled it sideways. Both are gone (law C7 and law C14): the header height is now the
|
|
6
|
+
* `appShellHeaderHeight` token at every width, an empty page bar reserves nothing, and overflow
|
|
7
|
+
* folds into `ActionGroup`'s kebab instead of scrolling out of view. */
|
|
5
8
|
|
|
6
9
|
/*
|
|
7
10
|
* Transparent header (docs/DESIGN-SPEC.md §5): NO bottom rule at all. The header and the page body
|
|
@@ -16,58 +19,95 @@
|
|
|
16
19
|
height: 100%;
|
|
17
20
|
}
|
|
18
21
|
|
|
22
|
+
/*
|
|
23
|
+
* **Exactly ONE side of the header row is elastic, and it is the side whose content can ellipsize.**
|
|
24
|
+
* That is the whole rule, and both collisions this header has produced were the same violation of
|
|
25
|
+
* it from opposite directions.
|
|
26
|
+
*
|
|
27
|
+
* First the breadcrumb was `flex-shrink: 0`, so it won the whole width and the primary action
|
|
28
|
+
* rendered UNDERNEATH it. Then BOTH sides were made shrinkable (`.lead` at `0 1 auto`, `.pageBar` at
|
|
29
|
+
* `1 1 auto` with `min-width: 0` pushed onto its children) — and at 390px the controls, which cannot
|
|
30
|
+
* clip, shrank below their content and spilled: a flex container with `justify-content: flex-end`
|
|
31
|
+
* overflows towards its START, so the sync button painted 30px INTO the page title. Measured in
|
|
32
|
+
* Chrome: the title spanned 119.7–197.1 and the sync button 158.3–188.3.
|
|
33
|
+
*
|
|
34
|
+
* So the lead is now the elastic side (`flex: 1 1 auto`, `min-width: 0`, each crumb truncating via
|
|
35
|
+
* its own `truncate` prop) and the control side is rigid. A row of buttons that genuinely does not
|
|
36
|
+
* fit is not a shrinking problem — it is the OVERFLOW FOLD's problem, and basalt already owns that
|
|
37
|
+
* (law C7: below `sm` row 1 is the primary plus ONE kebab).
|
|
38
|
+
*/
|
|
19
39
|
.lead {
|
|
20
40
|
display: flex;
|
|
21
41
|
align-items: center;
|
|
22
42
|
gap: var(--mantine-spacing-sm);
|
|
23
|
-
flex
|
|
43
|
+
flex: 1 1 auto;
|
|
24
44
|
min-width: 0;
|
|
25
45
|
}
|
|
26
46
|
|
|
27
|
-
|
|
28
|
-
|
|
47
|
+
/* No `overflow: hidden` here or on `.lead` above: Mantine's focus ring is `outline: 2px solid;
|
|
48
|
+
* outline-offset: 2px` — OUTSIDE the anchor box — and a wrapper clipping this tightly to the
|
|
49
|
+
* breadcrumb anchors clipped the ring on every edge, leaving the primary in-shell nav link with no
|
|
50
|
+
* visible focus indicator. Truncation is already owned by each crumb's own `truncate` prop
|
|
51
|
+
* (`app-breadcrumbs.tsx`, Mantine's `[data-truncate]` rule sets its own `overflow: hidden;
|
|
52
|
+
* text-overflow: ellipsis; white-space: nowrap` on the element that actually holds the text) — this
|
|
53
|
+
* wrapper only needs to let its children shrink. */
|
|
54
|
+
.lead * {
|
|
29
55
|
min-width: 0;
|
|
30
|
-
display: flex;
|
|
31
|
-
align-items: center;
|
|
32
|
-
justify-content: flex-end;
|
|
33
|
-
gap: var(--mantine-spacing-xs);
|
|
34
|
-
overflow-x: auto;
|
|
35
|
-
scrollbar-width: thin;
|
|
36
56
|
}
|
|
37
57
|
|
|
38
|
-
/*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
|
|
58
|
+
/*
|
|
59
|
+
* The `PageBar` row-1 portal target — the RIGID side of the row (see `.lead` above for the rule and
|
|
60
|
+
* for the two collisions that established it). Never a scroll container — see the header note above.
|
|
61
|
+
*
|
|
62
|
+
* `flex: 0 0 auto` and no `min-width: 0` anywhere below it, deliberately: a button cannot ellipsize,
|
|
63
|
+
* so letting this side shrink does not make it narrower, it makes it OVERFLOW — leftwards, into the
|
|
64
|
+
* breadcrumb, because of the `justify-content: flex-end` two lines down. The previous version pushed
|
|
65
|
+
* `min-width: 0` onto every child for exactly the reason that comment gave (a `wrap="nowrap"` group's
|
|
66
|
+
* automatic minimum size is its min-content width and flex will not go below it) — the reasoning was
|
|
67
|
+
* right and the conclusion was backwards: that floor is the row's protection, not its bug.
|
|
68
|
+
*/
|
|
69
|
+
.pageBar {
|
|
70
|
+
flex: 0 0 auto;
|
|
71
|
+
display: flex;
|
|
42
72
|
flex-wrap: nowrap;
|
|
73
|
+
align-items: center;
|
|
74
|
+
justify-content: flex-end;
|
|
75
|
+
gap: var(--vx-space-control-gap);
|
|
43
76
|
}
|
|
44
77
|
|
|
78
|
+
/*
|
|
79
|
+
* The shell globals sit at the SAME 30px tier as everything else in the row, and they get there the
|
|
80
|
+
* same way a home's slot does — `HeaderGlobalActions` wraps this region in a `CtlSlot`, so a
|
|
81
|
+
* consumer's bare `<ActionIcon>` in `globalActions` defaults to `size="ctl"` with no prop at the call
|
|
82
|
+
* site (law C5). They rendered at Mantine's `md` 28px before, which is 2px under the page group
|
|
83
|
+
* beside them: close enough to look like a mistake rather than a tier.
|
|
84
|
+
*
|
|
85
|
+
* The gap to the page group is `.bar`'s own `sm`, so the globals read as a separate cluster; between
|
|
86
|
+
* themselves they use the control gap, like every other adjacent-control pair.
|
|
87
|
+
*/
|
|
45
88
|
.global {
|
|
46
89
|
display: flex;
|
|
90
|
+
flex-wrap: nowrap;
|
|
47
91
|
align-items: center;
|
|
48
|
-
gap: var(--
|
|
92
|
+
gap: var(--vx-space-control-gap);
|
|
49
93
|
flex-shrink: 0;
|
|
50
94
|
}
|
|
51
95
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.lead {
|
|
59
|
-
order: 1;
|
|
60
|
-
flex: 1 1 auto;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.global {
|
|
64
|
-
order: 2;
|
|
65
|
-
}
|
|
96
|
+
.global :global(.mantine-ActionIcon-root) > svg {
|
|
97
|
+
width: 1rem;
|
|
98
|
+
height: 1rem;
|
|
99
|
+
}
|
|
66
100
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
101
|
+
/*
|
|
102
|
+
* The breadcrumb's ANCESTOR crumbs — hidden below `sm`. See `app-breadcrumbs.tsx` for why: at 390px
|
|
103
|
+
* the row's elastic side truncated `Overview / Dashboard` to `O… / D…`, which is two ellipses and a
|
|
104
|
+
* separator in place of the one word that says where the reader is.
|
|
105
|
+
*
|
|
106
|
+
* `47.99375em` is the `sm` breakpoint in the exact literal form every other basalt CSS module uses —
|
|
107
|
+
* a media query cannot resolve a custom property.
|
|
108
|
+
*/
|
|
109
|
+
@media (max-width: 47.99375em) {
|
|
110
|
+
.crumbAncestors {
|
|
111
|
+
display: none;
|
|
72
112
|
}
|
|
73
113
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReactElement } from 'react';
|
|
2
|
-
import type { MobileNavConfig, MobileNavModel } from '../nav/types.js';
|
|
2
|
+
import type { MobileNavConfig, MobileNavModel, SidebarBlock } from '../nav/types.js';
|
|
3
3
|
import type { BasaltAccountProps } from './account-types.js';
|
|
4
4
|
import type { SettingsMenuItem } from './index.js';
|
|
5
5
|
export type MobileNavProps = {
|
|
@@ -11,6 +11,13 @@ export type MobileNavProps = {
|
|
|
11
11
|
* deletable: everything it used to hold is reachable from More. */
|
|
12
12
|
account?: BasaltAccountProps | undefined;
|
|
13
13
|
settingsMenuItems?: SettingsMenuItem[] | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* The sidebar's blocks, projected. A block with `mobile: 'more'` becomes ONE row in the More
|
|
16
|
+
* surface (`Awaiting action · 3`) that opens a nested 44px-row sheet of its items — the sidebar
|
|
17
|
+
* itself does not exist below `sm`, so this is the only way its blocks are reachable at all.
|
|
18
|
+
* Counted for the menu-vs-sheet threshold by `blockRowCount`, never re-counted here.
|
|
19
|
+
*/
|
|
20
|
+
blocks?: SidebarBlock[] | undefined;
|
|
14
21
|
};
|
|
15
22
|
/**
|
|
16
23
|
* How many rows `account` contributes to the More surface — DELEGATED to `accountRows` rather than
|
|
@@ -21,5 +28,5 @@ export type MobileNavProps = {
|
|
|
21
28
|
* rows — still conjured a More slot that opened empty.
|
|
22
29
|
*/
|
|
23
30
|
export declare function accountRowCount(account: BasaltAccountProps | undefined): number;
|
|
24
|
-
export declare function MobileNav({ model, config, account, settingsMenuItems, }: MobileNavProps): ReactElement;
|
|
31
|
+
export declare function MobileNav({ model, config, account, settingsMenuItems, blocks, }: MobileNavProps): ReactElement;
|
|
25
32
|
//# sourceMappingURL=app-mobile-nav.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-mobile-nav.d.ts","sourceRoot":"","sources":["../../src/shell/app-mobile-nav.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAc,YAAY,EAAa,MAAM,OAAO,CAAA;AAChE,OAAO,KAAK,EACV,eAAe,EAEf,cAAc,
|
|
1
|
+
{"version":3,"file":"app-mobile-nav.d.ts","sourceRoot":"","sources":["../../src/shell/app-mobile-nav.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAc,YAAY,EAAa,MAAM,OAAO,CAAA;AAChE,OAAO,KAAK,EACV,eAAe,EAEf,cAAc,EAEd,YAAY,EAKb,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAmB/C,MAAM,MAAM,cAAc,GAAG;IAC3B,kFAAkF;IAClF,KAAK,EAAE,cAAc,CAAA;IACrB,MAAM,CAAC,EAAE,eAAe,GAAG,SAAS,CAAA;IACpC;;wEAEoE;IACpE,OAAO,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAA;IACxC,iBAAiB,CAAC,EAAE,gBAAgB,EAAE,GAAG,SAAS,CAAA;IAClD;;;;;OAKG;IACH,MAAM,CAAC,EAAE,YAAY,EAAE,GAAG,SAAS,CAAA;CACpC,CAAA;AAkFD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,kBAAkB,GAAG,SAAS,GAAG,MAAM,CAE/E;AAsFD,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,MAAM,EACN,OAAO,EACP,iBAAiB,EACjB,MAAM,GACP,EAAE,cAAc,GAAG,YAAY,CAuW/B"}
|