basalt-ui 1.25.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 +18 -0
- package/MIGRATING.md +546 -20
- package/README.md +45 -32
- package/agent/rules/basalt-batteries.md +205 -0
- package/agent/rules/basalt-charts.md +125 -555
- package/agent/rules/basalt-controls.md +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/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 +37 -15
- package/dist/data/data-table.d.ts.map +1 -1
- package/dist/data/data-table.js +88 -58
- package/dist/guard/grace.test.d.ts +20 -0
- package/dist/guard/grace.test.d.ts.map +1 -0
- package/dist/guard/index.d.ts +66 -3
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +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/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 +96 -11
- package/src/data/data-table.tsx +220 -84
- 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 -329
- package/agent/rules/basalt-forms.md +0 -186
- package/agent/rules/basalt-notifications.md +0 -264
- package/agent/rules/basalt-query.md +0 -187
- package/agent/rules/basalt-router.md +0 -336
- package/dist/content/article-filter-bar.d.ts +0 -40
- package/dist/content/article-filter-bar.d.ts.map +0 -1
- package/dist/content/article-filter-bar.js +0 -56
- package/dist/shell/page-header.d.ts +0 -13
- package/dist/shell/page-header.d.ts.map +0 -1
- package/dist/shell/page-header.js +0 -23
- package/src/content/article-filter-bar.tsx +0 -88
- package/src/shell/page-header.tsx +0 -35
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* `PageBar` layout (docs/CONTROLS-SPEC.md §2.1).
|
|
3
|
+
*
|
|
4
|
+
* Law C7 in CSS form: `flex-wrap: nowrap` on every row and NO `overflow-x` anywhere. A home that
|
|
5
|
+
* scrolls sideways hides its own controls; the fold is computed instead — `More` for actions
|
|
6
|
+
* (`ActionGroup`), a `Filters (n)` sheet for filters (`FilterSet`'s job, wave 4). The filter
|
|
7
|
+
* container below only supplies `min-width: 0` so that fold has a box to measure against.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/* The shell-less bar: both rows, sticky at the top of the document. One layer under the AppShell
|
|
11
|
+
* header (`--app-shell-header-z-index`, default 100) so it never wins a stacking fight with ordinary
|
|
12
|
+
* in-flow content — Mantine's Input section, Indicator, sticky table headers, and Slider thumbs all
|
|
13
|
+
* sit at `z-index: 1` or higher, which used to paint over a bare `z-index: 1` bar while scrolling. */
|
|
14
|
+
.bar {
|
|
15
|
+
position: sticky;
|
|
16
|
+
top: 0;
|
|
17
|
+
z-index: calc(var(--app-shell-header-z-index, 100) - 1);
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
gap: var(--vx-space-stack-xs);
|
|
21
|
+
background-color: var(--vx-surface-bg);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
* Row 2 inside a shell: in the page flow, sticky under the 48px header. `--app-shell-header-height`
|
|
26
|
+
* is Mantine's own AppShell variable, so the offset tracks the header at any density with no React
|
|
27
|
+
* state and no measurement. `z-index` sits one layer below the header itself (see `.bar` above) —
|
|
28
|
+
* high enough to stay above ordinary page content, never above the AppShell header.
|
|
29
|
+
*/
|
|
30
|
+
.row2Sticky {
|
|
31
|
+
position: sticky;
|
|
32
|
+
top: var(--app-shell-header-height, 0px);
|
|
33
|
+
z-index: calc(var(--app-shell-header-z-index, 100) - 1);
|
|
34
|
+
background-color: var(--vx-surface-bg);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.row1 {
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-wrap: nowrap;
|
|
40
|
+
align-items: center;
|
|
41
|
+
gap: var(--vx-space-control-gap);
|
|
42
|
+
min-width: 0;
|
|
43
|
+
min-height: var(--vx-space-page-bar-row-height);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.lead {
|
|
47
|
+
display: flex;
|
|
48
|
+
flex-wrap: nowrap;
|
|
49
|
+
align-items: center;
|
|
50
|
+
gap: var(--vx-space-control-gap);
|
|
51
|
+
flex-shrink: 0;
|
|
52
|
+
min-width: 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.icon {
|
|
56
|
+
display: inline-flex;
|
|
57
|
+
align-items: center;
|
|
58
|
+
color: var(--vx-muted);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/*
|
|
62
|
+
* The shell-less page title (law C8: the breadcrumb names the page inside a shell, this does it
|
|
63
|
+
* outside one). Head-font treatment, same 88%/550 as every `WidgetHeader` title.
|
|
64
|
+
*/
|
|
65
|
+
.title {
|
|
66
|
+
margin: 0;
|
|
67
|
+
font-family: var(--basalt-font-head);
|
|
68
|
+
font-size: var(--vx-text-xl);
|
|
69
|
+
font-weight: 550;
|
|
70
|
+
font-stretch: 88%;
|
|
71
|
+
color: var(--vx-ink);
|
|
72
|
+
white-space: nowrap;
|
|
73
|
+
overflow: hidden;
|
|
74
|
+
text-overflow: ellipsis;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/*
|
|
78
|
+
* Icons inside a row-1 button are 16px and sit 6px from their label. Mantine spends
|
|
79
|
+
* `--mantine-spacing-xs` (11px) on a Button's section gap regardless of the size tier, which is the
|
|
80
|
+
* BODY tier's number — at 30px it detached the glyph from the word it belongs to. Scoped to the row
|
|
81
|
+
* rather than themed on `Button` globally: a `size="md"` button in a form still wants Mantine's gap.
|
|
82
|
+
*/
|
|
83
|
+
.row1 :global(.mantine-Button-section[data-position='left']),
|
|
84
|
+
.row2 :global(.mantine-Button-section[data-position='left']) {
|
|
85
|
+
margin-inline-end: var(--vx-space-control-gap);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.row1 :global(.mantine-Button-section) > svg,
|
|
89
|
+
.row1 :global(.mantine-ActionIcon-root) > svg {
|
|
90
|
+
width: 1rem;
|
|
91
|
+
height: 1rem;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* Actions and sync are right-aligned; the lead (or the breadcrumb, in a shell) holds the left. */
|
|
95
|
+
.row1End {
|
|
96
|
+
display: flex;
|
|
97
|
+
flex-wrap: nowrap;
|
|
98
|
+
align-items: center;
|
|
99
|
+
justify-content: flex-end;
|
|
100
|
+
gap: var(--vx-space-control-gap);
|
|
101
|
+
flex: 1 1 auto;
|
|
102
|
+
min-width: 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/*
|
|
106
|
+
* Row 2's rhythm is 8px · pills · 8px, NOT a 40px reserved row.
|
|
107
|
+
*
|
|
108
|
+
* `min-height: var(--vx-space-page-bar-row-height)` (40px) left a 30px pill floating inside a 40px
|
|
109
|
+
* box with the 5px above and below it belonging to nothing — and because the row sat in a `Stack`
|
|
110
|
+
* that already spaced it, the visible gap between the header and the pills read as ~19px while the
|
|
111
|
+
* gap from the pills to the first card read as ~19px too. Symmetric padding on the row itself is the
|
|
112
|
+
* same total height (46px vs 40px) with both halves owned by one declaration, so the sticky bar's
|
|
113
|
+
* published height covers exactly the band it paints.
|
|
114
|
+
*/
|
|
115
|
+
.row2 {
|
|
116
|
+
display: flex;
|
|
117
|
+
flex-wrap: nowrap;
|
|
118
|
+
align-items: center;
|
|
119
|
+
gap: var(--vx-space-control-gap);
|
|
120
|
+
min-width: 0;
|
|
121
|
+
padding-block: var(--vx-space-stack-sm);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.filters {
|
|
125
|
+
display: flex;
|
|
126
|
+
flex-wrap: nowrap;
|
|
127
|
+
align-items: center;
|
|
128
|
+
gap: var(--vx-space-control-gap);
|
|
129
|
+
flex: 1 1 auto;
|
|
130
|
+
min-width: 0;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.filtersEnd {
|
|
134
|
+
display: flex;
|
|
135
|
+
flex-wrap: nowrap;
|
|
136
|
+
align-items: center;
|
|
137
|
+
gap: var(--vx-space-control-gap);
|
|
138
|
+
margin-inline-start: auto;
|
|
139
|
+
flex-shrink: 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/*
|
|
143
|
+
* Law C15 — every touch target inside a home is at least `touchControlHeight` (36px at level 0)
|
|
144
|
+
* below `sm`, while the VISIBLE box stays the 30px `ctl` tier. `::after` and not `::before`:
|
|
145
|
+
* Mantine's Button owns `::before` for its loading overlay (`@mantine/core/styles/Button.css`),
|
|
146
|
+
* so a hit area drawn there would fight it. `pointer-events` stays default so the overlay is the
|
|
147
|
+
* one receiving the tap.
|
|
148
|
+
*
|
|
149
|
+
* `overflow: visible` is LOAD-BEARING, not tidying. Mantine's Button and ActionIcon roots both set
|
|
150
|
+
* `overflow: hidden` (`Button.css`, `ActionIcon.css`), which clips an overlay that by definition
|
|
151
|
+
* extends past the 30px box — so without this the 36px target simply does not exist and a tap 3px
|
|
152
|
+
* above the control misses. What `overflow: hidden` buys those roots is clipping the loading
|
|
153
|
+
* overlay's blur, and that overlay is only painted under `[data-loading]`, which is excluded below.
|
|
154
|
+
*/
|
|
155
|
+
@media (max-width: 47.99375em) {
|
|
156
|
+
.row1 :is(button, a),
|
|
157
|
+
.row2 :is(button, a) {
|
|
158
|
+
position: relative;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.row1 :is(button, a):not([data-loading]),
|
|
162
|
+
.row2 :is(button, a):not([data-loading]) {
|
|
163
|
+
overflow: visible;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.row1 :is(button, a)::after,
|
|
167
|
+
.row2 :is(button, a)::after {
|
|
168
|
+
content: '';
|
|
169
|
+
position: absolute;
|
|
170
|
+
top: 50%;
|
|
171
|
+
left: 50%;
|
|
172
|
+
translate: -50% -50%;
|
|
173
|
+
width: 100%;
|
|
174
|
+
height: 100%;
|
|
175
|
+
min-width: var(--vx-space-touch-control-height);
|
|
176
|
+
min-height: var(--vx-space-touch-control-height);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
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 bar: string
|
|
7
|
+
readonly row2Sticky: string
|
|
8
|
+
readonly row1: string
|
|
9
|
+
readonly lead: string
|
|
10
|
+
readonly icon: string
|
|
11
|
+
readonly title: string
|
|
12
|
+
readonly row2: string
|
|
13
|
+
readonly row1End: string
|
|
14
|
+
readonly filters: string
|
|
15
|
+
readonly filtersEnd: string
|
|
16
|
+
}
|
|
17
|
+
export default classes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-bar.test.d.ts","sourceRoot":"","sources":["../../src/shell/page-bar.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sidebar-block law — every default a `SidebarBlock` leaves unstated, plus the two persisted
|
|
3
|
+
* fold-key grammars.
|
|
4
|
+
*
|
|
5
|
+
* PURE BY CONTRACT, for the same reason `mobile-nav-model.ts` is: three consumers read these
|
|
6
|
+
* defaults and none of them may disagree. `app-sidebar.tsx` decides where a block renders and
|
|
7
|
+
* whether the rail keeps it, `sidebar-blocks.tsx` paints it, and `mobile-nav-model.ts` counts the
|
|
8
|
+
* More-sheet rows it will produce — a count that picks `menu` vs `sheet` before any of it renders.
|
|
9
|
+
* A default re-derived per call site is how the count and the renderer drift apart (see
|
|
10
|
+
* `accountRowCount`'s doc for the same failure with account rows).
|
|
11
|
+
*/
|
|
12
|
+
import type { SidebarBlock } from '../nav/types.js';
|
|
13
|
+
/** Envelope version for a persisted fold flag. Bump only if the value stops being a boolean. */
|
|
14
|
+
export declare const FOLD_VERSION = 1;
|
|
15
|
+
/**
|
|
16
|
+
* `'nav'` renders after the nav sections inside the scroll region; `'bottom'` pins above the
|
|
17
|
+
* settings footer. A progress block is `'bottom'` — it is a pinned status row, not a nav tail.
|
|
18
|
+
*/
|
|
19
|
+
export declare function sidebarBlockPlacement(block: SidebarBlock): 'nav' | 'bottom';
|
|
20
|
+
/**
|
|
21
|
+
* The collapsed-rail projection. A list block earns a dot only when it carries a `count` — a rail
|
|
22
|
+
* icon with no number behind it is a dot that means nothing — and a progress block a ring on the
|
|
23
|
+
* settings row. A `custom` block has no rail representation at all (arbitrary content in ~48px of
|
|
24
|
+
* icon column), which is exactly what `sidebarNavExtra` did before it.
|
|
25
|
+
*/
|
|
26
|
+
export declare function sidebarBlockRail(block: SidebarBlock): 'dot' | 'ring' | 'hidden';
|
|
27
|
+
/**
|
|
28
|
+
* The mobile projection. A list is reachable from More by default — below `sm` the sidebar does
|
|
29
|
+
* not render at all, so a hidden list is unreachable, not merely unshown. A progress row defaults
|
|
30
|
+
* to `'hidden'`: it is ambient status, and every More row costs headroom the menu-vs-sheet
|
|
31
|
+
* threshold is measured against.
|
|
32
|
+
*/
|
|
33
|
+
export declare function sidebarBlockMobile(block: SidebarBlock): 'more' | 'hidden';
|
|
34
|
+
/** Rows a list block paints, honouring `max` — the "Show more" toggle reveals the remainder. */
|
|
35
|
+
export declare function sidebarBlockVisibleCount(itemCount: number, max: number | undefined): number;
|
|
36
|
+
/**
|
|
37
|
+
* A section label as a storage-key segment. Lowercase, every run of non-alphanumerics collapsed to
|
|
38
|
+
* one `-`, no leading/trailing separator — `'Awaiting action'` → `'awaiting-action'`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function slugifyLabel(label: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* `createPersistedState` namespaces every key as `basalt:<key>`, so these return the UNNAMESPACED
|
|
43
|
+
* halves — the real storage keys are `basalt:sidebar-block:<key>` and
|
|
44
|
+
* `basalt:sidebar-section:<label-slug>` (`docs/CONTROLS-SPEC.md` §2.3).
|
|
45
|
+
*/
|
|
46
|
+
export declare function sidebarBlockFoldKey(blockKey: string): string;
|
|
47
|
+
export declare function sidebarSectionFoldKey(label: string): string;
|
|
48
|
+
//# sourceMappingURL=sidebar-block-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sidebar-block-model.d.ts","sourceRoot":"","sources":["../../src/shell/sidebar-block-model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD,gGAAgG;AAChG,eAAO,MAAM,YAAY,IAAI,CAAA;AAE7B;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,GAAG,KAAK,GAAG,QAAQ,CAG3E;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAS/E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,GAAG,QAAQ,CAIzE;AAED,gGAAgG;AAChG,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAG3F;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKlD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3D"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const FOLD_VERSION = 1;
|
|
2
|
+
function sidebarBlockPlacement(block) {
|
|
3
|
+
if (block.kind === "progress") return block.placement ?? "bottom";
|
|
4
|
+
return block.placement ?? "nav";
|
|
5
|
+
}
|
|
6
|
+
function sidebarBlockRail(block) {
|
|
7
|
+
if (block.kind === "custom") return "hidden";
|
|
8
|
+
if (block.kind === "progress") return block.rail ?? "ring";
|
|
9
|
+
if (block.icon === void 0 || block.count === void 0) return "hidden";
|
|
10
|
+
return block.rail ?? "dot";
|
|
11
|
+
}
|
|
12
|
+
function sidebarBlockMobile(block) {
|
|
13
|
+
if (block.kind === "custom") return "hidden";
|
|
14
|
+
if (block.kind === "progress") return block.mobile ?? "hidden";
|
|
15
|
+
return block.mobile ?? "more";
|
|
16
|
+
}
|
|
17
|
+
function sidebarBlockVisibleCount(itemCount, max) {
|
|
18
|
+
if (max === void 0 || max >= itemCount) return itemCount;
|
|
19
|
+
return Math.max(0, max);
|
|
20
|
+
}
|
|
21
|
+
function slugifyLabel(label) {
|
|
22
|
+
return label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
23
|
+
}
|
|
24
|
+
function sidebarBlockFoldKey(blockKey) {
|
|
25
|
+
return `sidebar-block:${blockKey}`;
|
|
26
|
+
}
|
|
27
|
+
function sidebarSectionFoldKey(label) {
|
|
28
|
+
return `sidebar-section:${slugifyLabel(label)}`;
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
FOLD_VERSION,
|
|
32
|
+
sidebarBlockFoldKey,
|
|
33
|
+
sidebarBlockMobile,
|
|
34
|
+
sidebarBlockPlacement,
|
|
35
|
+
sidebarBlockRail,
|
|
36
|
+
sidebarBlockVisibleCount,
|
|
37
|
+
sidebarSectionFoldKey,
|
|
38
|
+
slugifyLabel
|
|
39
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { SidebarBlockTone, SidebarCustomBlock, SidebarListBlock, SidebarProgressBlock } from '../nav/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Fold/expand chevron. Lives here rather than in `app-sidebar.tsx` purely for direction: that
|
|
4
|
+
* module imports this one, so a glyph shared by a collapsible SECTION header and a collapsible
|
|
5
|
+
* BLOCK header has to sit on this side of the edge or the two files cycle.
|
|
6
|
+
*/
|
|
7
|
+
export declare function IconChevron({ open }: {
|
|
8
|
+
open: boolean;
|
|
9
|
+
}): import("react").JSX.Element;
|
|
10
|
+
/**
|
|
11
|
+
* A persisted fold flag (`true` = folded). Exactly one hook call regardless of `key`, so swapping
|
|
12
|
+
* keys mid-life never moves the hook count.
|
|
13
|
+
*
|
|
14
|
+
* Every fold in the sidebar goes through this — block folds at `basalt:sidebar-block:<key>` and
|
|
15
|
+
* nav-section folds at `basalt:sidebar-section:<label-slug>`. Through 1.25.0 the section folds were
|
|
16
|
+
* one `useState` keyed by label, so every reload re-opened a section the user had closed.
|
|
17
|
+
*/
|
|
18
|
+
export declare function usePersistedFold(key: string, initial: boolean): readonly [boolean, (next: boolean) => void];
|
|
19
|
+
/** Status dot — the icon fallback for a toned row. Reads `--vx-status-*` through `data-tone`. */
|
|
20
|
+
export declare function SidebarBlockToneDot({ tone }: {
|
|
21
|
+
tone: SidebarBlockTone;
|
|
22
|
+
}): import("react").JSX.Element;
|
|
23
|
+
/** `value` of `total` as a ring — the collapsed rail's stand-in for a progress block. */
|
|
24
|
+
export declare function SidebarProgressRing({ value, total }: {
|
|
25
|
+
value: number;
|
|
26
|
+
total: number;
|
|
27
|
+
}): import("react").JSX.Element;
|
|
28
|
+
/** One block, whichever kind it is. `AppSidebar` places it; this decides nothing but the paint. */
|
|
29
|
+
export declare function SidebarBlockView({ block, }: {
|
|
30
|
+
block: SidebarListBlock | SidebarProgressBlock | SidebarCustomBlock;
|
|
31
|
+
}): import("react").JSX.Element;
|
|
32
|
+
//# sourceMappingURL=sidebar-blocks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sidebar-blocks.d.ts","sourceRoot":"","sources":["../../src/shell/sidebar-blocks.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAEV,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,EACrB,MAAM,cAAc,CAAA;AAYrB;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,+BAgBtD;AAsBD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,GACf,SAAS,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC,CAE7C;AAcD,iGAAiG;AACjG,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,+BAEvE;AAED,yFAAyF;AACzF,wBAAgB,mBAAmB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,+BAwBrF;AA4JD,mGAAmG;AACnG,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,GACN,EAAE;IACD,KAAK,EAAE,gBAAgB,GAAG,oBAAoB,GAAG,kBAAkB,CAAA;CACpE,+BAIA"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Collapse, NavLink, Progress, Text, UnstyledButton } from "@mantine/core";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { NavCountBadge } from "./nav-count-badge.js";
|
|
5
|
+
import { createPersistedState } from "../state.js";
|
|
6
|
+
import {
|
|
7
|
+
FOLD_VERSION,
|
|
8
|
+
sidebarBlockFoldKey,
|
|
9
|
+
sidebarBlockRail,
|
|
10
|
+
sidebarBlockVisibleCount
|
|
11
|
+
} from "./sidebar-block-model.js";
|
|
12
|
+
import { IconSlot } from "../theme/icon-slot.js";
|
|
13
|
+
import classes from "./app-sidebar.module.css";
|
|
14
|
+
function IconChevron({ open }) {
|
|
15
|
+
return /* @__PURE__ */ jsx(
|
|
16
|
+
"svg",
|
|
17
|
+
{
|
|
18
|
+
width: 12,
|
|
19
|
+
height: 12,
|
|
20
|
+
viewBox: "0 0 24 24",
|
|
21
|
+
fill: "none",
|
|
22
|
+
stroke: "currentColor",
|
|
23
|
+
strokeWidth: 2,
|
|
24
|
+
strokeLinecap: "round",
|
|
25
|
+
strokeLinejoin: "round",
|
|
26
|
+
"aria-hidden": true,
|
|
27
|
+
children: open ? /* @__PURE__ */ jsx("path", { d: "M6 9l6 6l6 -6" }) : /* @__PURE__ */ jsx("path", { d: "M9 6l6 6l-6 6" })
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
const foldStores = /* @__PURE__ */ new Map();
|
|
32
|
+
function foldStore(key, initial) {
|
|
33
|
+
const cached = foldStores.get(key);
|
|
34
|
+
if (cached) return cached;
|
|
35
|
+
const store = createPersistedState({ key, version: FOLD_VERSION, initial });
|
|
36
|
+
foldStores.set(key, store);
|
|
37
|
+
return store;
|
|
38
|
+
}
|
|
39
|
+
function usePersistedFold(key, initial) {
|
|
40
|
+
return foldStore(key, initial)();
|
|
41
|
+
}
|
|
42
|
+
function BlockLabel({ children }) {
|
|
43
|
+
return /* @__PURE__ */ jsx(Text, { component: "div", className: classes.sectionLabel, children });
|
|
44
|
+
}
|
|
45
|
+
function SidebarBlockToneDot({ tone }) {
|
|
46
|
+
return /* @__PURE__ */ jsx("span", { className: classes.blockToneDot, "data-tone": tone, "aria-hidden": true });
|
|
47
|
+
}
|
|
48
|
+
function SidebarProgressRing({ value, total }) {
|
|
49
|
+
const CIRCUMFERENCE = 2 * Math.PI * 8;
|
|
50
|
+
const ratio = total > 0 ? Math.min(1, Math.max(0, value / total)) : 0;
|
|
51
|
+
return /* @__PURE__ */ jsx("span", { className: classes.blockRing, "data-basalt-rail-ring": true, children: /* @__PURE__ */ jsxs("svg", { width: 20, height: 20, viewBox: "0 0 20 20", "aria-hidden": true, children: [
|
|
52
|
+
/* @__PURE__ */ jsx("circle", { cx: 10, cy: 10, r: 8, fill: "none", stroke: "var(--vx-divider)", strokeWidth: 2 }),
|
|
53
|
+
/* @__PURE__ */ jsx(
|
|
54
|
+
"circle",
|
|
55
|
+
{
|
|
56
|
+
cx: 10,
|
|
57
|
+
cy: 10,
|
|
58
|
+
r: 8,
|
|
59
|
+
fill: "none",
|
|
60
|
+
stroke: "var(--vx-accent)",
|
|
61
|
+
strokeWidth: 2,
|
|
62
|
+
strokeLinecap: "round",
|
|
63
|
+
strokeDasharray: CIRCUMFERENCE,
|
|
64
|
+
strokeDashoffset: CIRCUMFERENCE * (1 - ratio),
|
|
65
|
+
transform: "rotate(-90 10 10)"
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
] }) });
|
|
69
|
+
}
|
|
70
|
+
function BlockRow({ item }) {
|
|
71
|
+
const rawLead = item.icon ?? (item.tone !== void 0 ? /* @__PURE__ */ jsx(SidebarBlockToneDot, { tone: item.tone }) : void 0);
|
|
72
|
+
const lead = rawLead === void 0 ? void 0 : /* @__PURE__ */ jsx(IconSlot, { children: rawLead });
|
|
73
|
+
const meta = item.meta !== void 0 ? /* @__PURE__ */ jsx(Text, { component: "span", className: classes.blockRowMeta, children: item.meta }) : void 0;
|
|
74
|
+
const interactive = item.Anchor !== void 0 || item.href !== void 0 || item.onClick !== void 0;
|
|
75
|
+
if (!interactive) {
|
|
76
|
+
return /* @__PURE__ */ jsxs("div", { className: `${classes.blockRow} ${classes.blockRowStatic}`, children: [
|
|
77
|
+
lead,
|
|
78
|
+
/* @__PURE__ */ jsx(Text, { component: "span", className: classes.blockRowLabel, children: item.label }),
|
|
79
|
+
meta
|
|
80
|
+
] });
|
|
81
|
+
}
|
|
82
|
+
const shared = {
|
|
83
|
+
classNames: { root: classes.blockRow },
|
|
84
|
+
label: item.label,
|
|
85
|
+
...lead !== void 0 && { leftSection: lead },
|
|
86
|
+
...meta !== void 0 && { rightSection: meta },
|
|
87
|
+
...item.onClick !== void 0 && { onClick: item.onClick }
|
|
88
|
+
};
|
|
89
|
+
const Anchor = item.Anchor;
|
|
90
|
+
if (Anchor) return /* @__PURE__ */ jsx(NavLink, { component: Anchor, ...shared });
|
|
91
|
+
return /* @__PURE__ */ jsx(NavLink, { component: "a", ...item.href !== void 0 && { href: item.href }, ...shared });
|
|
92
|
+
}
|
|
93
|
+
function ListBlock({ block }) {
|
|
94
|
+
const [folded, setFolded] = usePersistedFold(sidebarBlockFoldKey(block.key), false);
|
|
95
|
+
const [showAll, setShowAll] = useState(false);
|
|
96
|
+
const rail = sidebarBlockRail(block);
|
|
97
|
+
const visible = showAll ? block.items.length : sidebarBlockVisibleCount(block.items.length, block.max);
|
|
98
|
+
const hidden = block.items.length - visible;
|
|
99
|
+
const label = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
100
|
+
block.icon !== void 0 && /* @__PURE__ */ jsxs("span", { className: classes.blockIcon, children: [
|
|
101
|
+
block.icon,
|
|
102
|
+
rail === "dot" && /* @__PURE__ */ jsx("span", { className: classes.blockDot, "aria-hidden": true })
|
|
103
|
+
] }),
|
|
104
|
+
/* @__PURE__ */ jsx(BlockLabel, { children: block.label }),
|
|
105
|
+
block.count ? /* @__PURE__ */ jsx(NavCountBadge, { count: block.count }) : null
|
|
106
|
+
] });
|
|
107
|
+
const body = /* @__PURE__ */ jsxs("div", { className: classes.blockItems, children: [
|
|
108
|
+
block.items.slice(0, visible).map((item) => /* @__PURE__ */ jsx(BlockRow, { item }, item.key)),
|
|
109
|
+
hidden > 0 || showAll ? /* @__PURE__ */ jsx(
|
|
110
|
+
UnstyledButton,
|
|
111
|
+
{
|
|
112
|
+
className: classes.blockMore,
|
|
113
|
+
onClick: () => setShowAll(!showAll),
|
|
114
|
+
"aria-expanded": showAll,
|
|
115
|
+
children: showAll ? "Show less" : "Show more"
|
|
116
|
+
}
|
|
117
|
+
) : null
|
|
118
|
+
] });
|
|
119
|
+
return /* @__PURE__ */ jsx("div", { className: classes.block, "data-rail": rail, children: block.collapsible === true ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
120
|
+
/* @__PURE__ */ jsxs(
|
|
121
|
+
UnstyledButton,
|
|
122
|
+
{
|
|
123
|
+
className: `${classes.blockHeader} ${classes.blockHeaderButton}`,
|
|
124
|
+
onClick: () => setFolded(!folded),
|
|
125
|
+
"aria-expanded": !folded,
|
|
126
|
+
children: [
|
|
127
|
+
label,
|
|
128
|
+
/* @__PURE__ */ jsx(IconChevron, { open: !folded })
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
),
|
|
132
|
+
/* @__PURE__ */ jsx(Collapse, { expanded: !folded, children: body })
|
|
133
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
134
|
+
/* @__PURE__ */ jsx("div", { className: classes.blockHeader, children: label }),
|
|
135
|
+
body
|
|
136
|
+
] }) });
|
|
137
|
+
}
|
|
138
|
+
function ProgressBlock({ block }) {
|
|
139
|
+
const percent = block.total > 0 ? Math.min(100, Math.max(0, block.value / block.total * 100)) : 0;
|
|
140
|
+
const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
141
|
+
/* @__PURE__ */ jsxs("div", { className: classes.blockProgressHead, children: [
|
|
142
|
+
/* @__PURE__ */ jsx(BlockLabel, { children: block.label }),
|
|
143
|
+
/* @__PURE__ */ jsxs(Text, { component: "span", className: classes.blockRowMeta, children: [
|
|
144
|
+
block.value,
|
|
145
|
+
" of ",
|
|
146
|
+
block.total
|
|
147
|
+
] })
|
|
148
|
+
] }),
|
|
149
|
+
/* @__PURE__ */ jsx(Progress, { value: percent, size: "xs", "aria-label": block.label })
|
|
150
|
+
] });
|
|
151
|
+
return /* @__PURE__ */ jsx("div", { className: classes.block, "data-rail": sidebarBlockRail(block), children: block.onClick !== void 0 ? /* @__PURE__ */ jsx(UnstyledButton, { className: classes.blockProgress, onClick: block.onClick, children: inner }) : /* @__PURE__ */ jsx("div", { className: classes.blockProgress, children: inner }) });
|
|
152
|
+
}
|
|
153
|
+
function CustomBlock({ block }) {
|
|
154
|
+
return /* @__PURE__ */ jsx("div", { className: classes.block, "data-rail": "hidden", children: block.node });
|
|
155
|
+
}
|
|
156
|
+
function SidebarBlockView({
|
|
157
|
+
block
|
|
158
|
+
}) {
|
|
159
|
+
if (block.kind === "list") return /* @__PURE__ */ jsx(ListBlock, { block });
|
|
160
|
+
if (block.kind === "progress") return /* @__PURE__ */ jsx(ProgressBlock, { block });
|
|
161
|
+
return /* @__PURE__ */ jsx(CustomBlock, { block });
|
|
162
|
+
}
|
|
163
|
+
export {
|
|
164
|
+
IconChevron,
|
|
165
|
+
SidebarBlockToneDot,
|
|
166
|
+
SidebarBlockView,
|
|
167
|
+
SidebarProgressRing,
|
|
168
|
+
usePersistedFold
|
|
169
|
+
};
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
import type { BarAction } from '../controls/actions.js';
|
|
2
|
+
/**
|
|
3
|
+
* The ⌘K row's trailing icon buttons — ONE or TWO, by type (`docs/CONTROLS-SPEC.md` §2.3).
|
|
4
|
+
*
|
|
5
|
+
* The tuple IS the enforcement: a third button pushes the search trigger under the width where its
|
|
6
|
+
* placeholder stays readable, and there is no third thing a sidebar search row is for. Each entry
|
|
7
|
+
* renders icon-only at the `icon` tier (24px, `--ai-size-icon`) with `label` demoted to the
|
|
8
|
+
* accessible name; a `kind: 'menu'` entry becomes an `OverflowMenu` kebab, a `kind: 'custom'` one
|
|
9
|
+
* is mounted verbatim.
|
|
10
|
+
*/
|
|
11
|
+
export type SidebarSearchActions = [BarAction] | [BarAction, BarAction];
|
|
1
12
|
export type SidebarSearchConfig = {
|
|
2
13
|
/** Opens the search palette — e.g. `openSpotlight` from `basalt-ui/commands`. */
|
|
3
14
|
onOpen: () => void;
|
|
@@ -6,7 +17,8 @@ export type SidebarSearchConfig = {
|
|
|
6
17
|
/** Keyboard-shortcut hint shown on the right (e.g. '⌘K'). Auto-detected mac/other when omitted. */
|
|
7
18
|
shortcut?: string;
|
|
8
19
|
};
|
|
9
|
-
export declare function SidebarSearch({ onOpen, placeholder, shortcut, collapsed, }: SidebarSearchConfig & {
|
|
20
|
+
export declare function SidebarSearch({ onOpen, placeholder, shortcut, actions, collapsed, }: SidebarSearchConfig & {
|
|
21
|
+
actions?: SidebarSearchActions;
|
|
10
22
|
collapsed?: boolean;
|
|
11
23
|
}): import("react").JSX.Element;
|
|
12
24
|
//# sourceMappingURL=sidebar-search.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidebar-search.d.ts","sourceRoot":"","sources":["../../src/shell/sidebar-search.tsx"],"names":[],"mappings":"AAYA,MAAM,MAAM,mBAAmB,GAAG;IAChC,iFAAiF;IACjF,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mGAAmG;IACnG,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;
|
|
1
|
+
{"version":3,"file":"sidebar-search.d.ts","sourceRoot":"","sources":["../../src/shell/sidebar-search.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAGpD;;;;;;;;GAQG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;AAEvE,MAAM,MAAM,mBAAmB,GAAG;IAChC,iFAAiF;IACjF,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mGAAmG;IACnG,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAgED,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,WAAuB,EACvB,QAAQ,EACR,OAAO,EACP,SAAS,GACV,EAAE,mBAAmB,GAAG;IAAE,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,+BA6C/E"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ActionIcon, Kbd, Text, UnstyledButton } from "@mantine/core";
|
|
3
|
+
import { OverflowMenu } from "../controls/actions.js";
|
|
3
4
|
import classes from "./sidebar-search.module.css";
|
|
4
5
|
function IconSearch() {
|
|
5
6
|
return /* @__PURE__ */ jsxs(
|
|
@@ -21,10 +22,37 @@ function IconSearch() {
|
|
|
21
22
|
}
|
|
22
23
|
);
|
|
23
24
|
}
|
|
25
|
+
function initial(label) {
|
|
26
|
+
return [...label][0]?.toUpperCase() ?? "?";
|
|
27
|
+
}
|
|
28
|
+
function SearchAction({ action }) {
|
|
29
|
+
if (action.kind === "custom") return action.node;
|
|
30
|
+
if (action.kind === "menu") {
|
|
31
|
+
return /* @__PURE__ */ jsx(OverflowMenu, { actions: action.items, trigger: "kebab", label: action.label });
|
|
32
|
+
}
|
|
33
|
+
const shared = {
|
|
34
|
+
variant: "subtle",
|
|
35
|
+
size: "icon",
|
|
36
|
+
className: classes.actionBtn,
|
|
37
|
+
"aria-label": action.label,
|
|
38
|
+
disabled: action.disabled === true,
|
|
39
|
+
...action.onClick !== void 0 && { onClick: action.onClick }
|
|
40
|
+
};
|
|
41
|
+
const body = action.icon ?? initial(action.label);
|
|
42
|
+
const Anchor = action.Anchor;
|
|
43
|
+
if (Anchor !== void 0) {
|
|
44
|
+
return /* @__PURE__ */ jsx(ActionIcon, { component: Anchor, ...shared, children: body });
|
|
45
|
+
}
|
|
46
|
+
if (action.link !== void 0) {
|
|
47
|
+
return /* @__PURE__ */ jsx(ActionIcon, { component: "a", href: action.link.to, ...shared, children: body });
|
|
48
|
+
}
|
|
49
|
+
return /* @__PURE__ */ jsx(ActionIcon, { ...shared, children: body });
|
|
50
|
+
}
|
|
24
51
|
function SidebarSearch({
|
|
25
52
|
onOpen,
|
|
26
53
|
placeholder = "Search\u2026",
|
|
27
54
|
shortcut,
|
|
55
|
+
actions,
|
|
28
56
|
collapsed
|
|
29
57
|
}) {
|
|
30
58
|
const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPod|iPad/i.test(navigator.platform);
|
|
@@ -42,7 +70,7 @@ function SidebarSearch({
|
|
|
42
70
|
}
|
|
43
71
|
);
|
|
44
72
|
}
|
|
45
|
-
|
|
73
|
+
const trigger = /* @__PURE__ */ jsxs(
|
|
46
74
|
UnstyledButton,
|
|
47
75
|
{
|
|
48
76
|
type: "button",
|
|
@@ -56,6 +84,11 @@ function SidebarSearch({
|
|
|
56
84
|
]
|
|
57
85
|
}
|
|
58
86
|
);
|
|
87
|
+
if (actions === void 0) return trigger;
|
|
88
|
+
return /* @__PURE__ */ jsxs("div", { className: classes.row, children: [
|
|
89
|
+
trigger,
|
|
90
|
+
actions.map((action) => /* @__PURE__ */ jsx(SearchAction, { action }, action.key))
|
|
91
|
+
] });
|
|
59
92
|
}
|
|
60
93
|
export {
|
|
61
94
|
SidebarSearch
|
|
@@ -5,6 +5,18 @@
|
|
|
5
5
|
* button as a matched pair, so the two must never drift onto different depth tokens.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
/*
|
|
9
|
+
* The row that appears ONLY when `search.actions` is supplied — one or two icon-only buttons beside
|
|
10
|
+
* the trigger. Without actions the trigger stays the row's sole node (no wrapper), so the DOM of
|
|
11
|
+
* every consumer that doesn't use them is untouched.
|
|
12
|
+
*/
|
|
13
|
+
.row {
|
|
14
|
+
display: flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
gap: var(--vx-space-sidebar-search-gap);
|
|
17
|
+
min-width: 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
8
20
|
.trigger {
|
|
9
21
|
display: flex;
|
|
10
22
|
align-items: center;
|
|
@@ -74,3 +86,17 @@
|
|
|
74
86
|
background-color: color-mix(in srgb, var(--vx-ink) 6%, transparent) !important;
|
|
75
87
|
color: var(--vx-ink) !important;
|
|
76
88
|
}
|
|
89
|
+
|
|
90
|
+
/* Trailing search-row action — the same ghost treatment as `.ghostIcon` / `.railBtn`, at the `icon`
|
|
91
|
+
* tier (24px) the component asks for explicitly. Never re-points `--ai-size`: unlike `.railBtn`,
|
|
92
|
+
* this button is NOT a second state of the trigger and must not track its height. */
|
|
93
|
+
.actionBtn {
|
|
94
|
+
flex: none;
|
|
95
|
+
color: var(--vx-faint) !important;
|
|
96
|
+
border-radius: var(--vx-radius-ctrl) !important;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.actionBtn:hover {
|
|
100
|
+
background-color: color-mix(in srgb, var(--vx-ink) 6%, transparent) !important;
|
|
101
|
+
color: var(--vx-ink) !important;
|
|
102
|
+
}
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
* (`noUncheckedIndexedAccess`, `noPropertyAccessFromIndexSignature`, `exactOptionalPropertyTypes`).
|
|
5
5
|
*/
|
|
6
6
|
declare const classes: {
|
|
7
|
+
readonly row: string
|
|
7
8
|
readonly trigger: string
|
|
8
9
|
readonly label: string
|
|
9
10
|
readonly railBtn: string
|
|
11
|
+
readonly actionBtn: string
|
|
10
12
|
}
|
|
11
13
|
export default classes
|