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
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { NavAnchor } from '../nav/types.js';
|
|
3
|
+
import type { AnyNavLink } from '../router-tanstack/nav.js';
|
|
4
|
+
/**
|
|
5
|
+
* A button-shaped action. `Anchor` is the router seam basalt HOSTS (the same seam `SidebarItem`
|
|
6
|
+
* uses — pass the one `useNav` builds, or your router's `Link`); `link` is the no-router fallback
|
|
7
|
+
* and only its `to` is honoured, since resolving `search`/`params` needs a router. Supply `Anchor`
|
|
8
|
+
* whenever the destination carries either.
|
|
9
|
+
*/
|
|
10
|
+
export type BarActionItem = {
|
|
11
|
+
key: string;
|
|
12
|
+
label: string;
|
|
13
|
+
kind?: undefined;
|
|
14
|
+
icon?: ReactNode;
|
|
15
|
+
onClick?: () => void;
|
|
16
|
+
link?: AnyNavLink;
|
|
17
|
+
Anchor?: NavAnchor;
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
loading?: boolean;
|
|
20
|
+
danger?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Joins this action to its adjacent `group: true` siblings into ONE `ControlGroup` — shared
|
|
23
|
+
* borders, radius on the outer ends only, no gap. Declare it on every member of the run: a set of
|
|
24
|
+
* three where the middle one omits it is two runs of one, which is the same row it was before.
|
|
25
|
+
*
|
|
26
|
+
* It is an opt-in because "these act on the same thing" is a fact only the caller has. A `‹ Today ›`
|
|
27
|
+
* stepper is the shape; three independent actions that happen to sit beside each other are not, and
|
|
28
|
+
* joining those would claim a relationship the reader would then have to un-learn.
|
|
29
|
+
*/
|
|
30
|
+
group?: true;
|
|
31
|
+
/** Mobile placement. @default 'bar' for `primary`, `'more'` for everything else */
|
|
32
|
+
mobile?: 'bar' | 'more' | 'hidden';
|
|
33
|
+
};
|
|
34
|
+
/** A nested group. Always folds into the `More` menu on both viewports — it never takes bar width. */
|
|
35
|
+
export type BarActionMenu = {
|
|
36
|
+
key: string;
|
|
37
|
+
kind: 'menu';
|
|
38
|
+
label: string;
|
|
39
|
+
icon?: ReactNode;
|
|
40
|
+
items: BarAction[];
|
|
41
|
+
};
|
|
42
|
+
/** An escape hatch for a control basalt does not model (a live chip, a consumer's own widget). */
|
|
43
|
+
export type BarActionCustom = {
|
|
44
|
+
key: string;
|
|
45
|
+
kind: 'custom';
|
|
46
|
+
node: ReactNode;
|
|
47
|
+
mobile?: 'bar' | 'more' | 'hidden';
|
|
48
|
+
};
|
|
49
|
+
export type BarAction = BarActionItem | BarActionMenu | BarActionCustom;
|
|
50
|
+
/**
|
|
51
|
+
* A home's `actions` slot. `primary` is SINGULAR by type — that is the whole enforcement of law
|
|
52
|
+
* C6's "exactly one primary"; there is no runtime check because a second one cannot be written.
|
|
53
|
+
*/
|
|
54
|
+
export type ActionGroupProps = {
|
|
55
|
+
primary?: BarAction;
|
|
56
|
+
secondary?: BarAction[];
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* A shell-owned, persistent header action (timer, notifications, a global `SyncButton`).
|
|
60
|
+
* `node` is rendered as-is — basalt owns only the placement.
|
|
61
|
+
*
|
|
62
|
+
* Below `sm` a `'more'` action renders inside the header's single kebab, which means its `node` is
|
|
63
|
+
* mounted a second time there (lazily — a Mantine `Menu.Dropdown` mounts its children on open).
|
|
64
|
+
* Keep `'more'` for self-contained nodes and give anything holding live state `mobile: 'bar'`,
|
|
65
|
+
* which renders exactly once.
|
|
66
|
+
*/
|
|
67
|
+
export type GlobalAction = {
|
|
68
|
+
key: string;
|
|
69
|
+
node: ReactNode;
|
|
70
|
+
/** @default 'bar' for the first two, `'more'` for the rest */
|
|
71
|
+
mobile?: 'bar' | 'more' | 'hidden';
|
|
72
|
+
};
|
|
73
|
+
/** Secondaries rendered inline on desktop; the rest fold into `More` (`docs/CONTROLS-SPEC.md` §2.1). */
|
|
74
|
+
export declare const DESKTOP_SECONDARY_MAX = 3;
|
|
75
|
+
/** `GlobalAction`s without an explicit `mobile` that still land on the mobile bar. */
|
|
76
|
+
export declare const MOBILE_GLOBAL_BAR_MAX = 2;
|
|
77
|
+
/**
|
|
78
|
+
* What the shell hands the page's `ActionGroup` so the header ends up with ONE kebab instead of
|
|
79
|
+
* two: the shell's `mobile: 'more'` global actions (already mapped to `kind: 'custom'` rows), and
|
|
80
|
+
* a claim the mobile kebab takes on mount. While a page's kebab holds the claim, the shell renders
|
|
81
|
+
* no kebab of its own.
|
|
82
|
+
*
|
|
83
|
+
* Lives here, in the lowest layer, so `src/shell/**` imports downward into `src/controls/**` and
|
|
84
|
+
* never the reverse — a context declared in the shell would make `./controls` pull the shell in.
|
|
85
|
+
*/
|
|
86
|
+
export type BarExtras = {
|
|
87
|
+
mobileMoreActions: readonly BarAction[];
|
|
88
|
+
/** Claims the single mobile kebab. Returns the release, so it works straight out of an effect. */
|
|
89
|
+
claimKebab: () => () => void;
|
|
90
|
+
};
|
|
91
|
+
/** Internal — mounted by `BasaltShell` (via `PageBarProvider`), never by a consumer. */
|
|
92
|
+
export declare function BarExtrasProvider({ value, children, }: {
|
|
93
|
+
value: BarExtras;
|
|
94
|
+
children: ReactNode;
|
|
95
|
+
}): ReactNode;
|
|
96
|
+
/** Resolved mobile placement of one action. A `kind: 'menu'` group is always a `More` row. */
|
|
97
|
+
export declare function barActionMobile(action: BarAction, isPrimary: boolean): 'bar' | 'more' | 'hidden';
|
|
98
|
+
/** `GlobalAction[]` → the same placement law, with "the first two ride the bar" as the default. */
|
|
99
|
+
export declare function globalActionMobile(action: GlobalAction, index: number): 'bar' | 'more' | 'hidden';
|
|
100
|
+
/** A `GlobalAction` reaching a `BarAction` slot — the kebab renders one row shape, not two. */
|
|
101
|
+
export declare function globalActionAsBarAction(action: GlobalAction): BarActionCustom;
|
|
102
|
+
export type OverflowMenuProps = {
|
|
103
|
+
actions: readonly BarAction[];
|
|
104
|
+
/**
|
|
105
|
+
* `more` — the labelled desktop `More` button. `kebab` — the mobile ⋯ icon button.
|
|
106
|
+
* @default 'more'
|
|
107
|
+
*/
|
|
108
|
+
trigger?: 'more' | 'kebab';
|
|
109
|
+
/** Trigger label (and the kebab's accessible name). @default 'More' */
|
|
110
|
+
label?: string;
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* The overflow fold — the only place an action that doesn't fit goes (law C7). Rows are 44px below
|
|
114
|
+
* `sm` (`--vx-space-sheet-row-height`), so the mobile kebab is already a touch surface without a
|
|
115
|
+
* second, sheet-shaped component.
|
|
116
|
+
*/
|
|
117
|
+
export declare function OverflowMenu({ actions, trigger, label, }: OverflowMenuProps): ReactNode;
|
|
118
|
+
/**
|
|
119
|
+
* Internal — the shared projection behind `ActionGroup` (and `PageBar`'s two rows).
|
|
120
|
+
*
|
|
121
|
+
* `host` is the whole reason this is not just `ActionGroup`. The shell's `mobile: 'more'` global
|
|
122
|
+
* actions must land in EXACTLY ONE kebab, and the only instance entitled to them is `PageBar`'s
|
|
123
|
+
* row 1. When every `ActionGroup` read `BarExtrasContext`, a page with `filtersEnd` (or any
|
|
124
|
+
* consumer composing an `ActionGroup` into a `Section`/`ChartCard` `actions` slot) grew a SECOND
|
|
125
|
+
* kebab that re-mounted the shell's global nodes and stole the claim from the shell's own fallback.
|
|
126
|
+
* `host: 'slot'` — the public `ActionGroup`'s value — gets `NO_EXTRAS`: no global rows, no claim.
|
|
127
|
+
*/
|
|
128
|
+
type BarActionRowProps = ActionGroupProps & {
|
|
129
|
+
/** `page` = `PageBar` row 1, the one instance that reads the shell extras and takes the claim. */
|
|
130
|
+
host: 'page' | 'slot';
|
|
131
|
+
/**
|
|
132
|
+
* `desktop` renders the desktop variant only — `PageBar` row 2, whose items live in the row-1
|
|
133
|
+
* kebab below `sm` (spec §2.1: mobile row 2 is tabs + the first pill + `Filters (n)`, nothing
|
|
134
|
+
* else). @default 'both'
|
|
135
|
+
*/
|
|
136
|
+
viewport?: 'both' | 'desktop';
|
|
137
|
+
/**
|
|
138
|
+
* Actions projected onto the MOBILE variant only, never the desktop row — `PageBar.filtersEnd`,
|
|
139
|
+
* whose desktop home is row 2. Each still obeys its own `mobile` placement, so a `'bar'` one
|
|
140
|
+
* becomes an inline icon button and a `'hidden'` one stays desktop-only.
|
|
141
|
+
*/
|
|
142
|
+
mobileOnly?: readonly BarAction[];
|
|
143
|
+
/**
|
|
144
|
+
* The home's `SyncButton`, mounted ONCE between the secondaries and the primary — see the ordering
|
|
145
|
+
* note in the body. Only `PageBar` row 1 passes it; a home with no sync passes nothing and the
|
|
146
|
+
* desktop row stays a single group.
|
|
147
|
+
*/
|
|
148
|
+
syncNode?: ReactNode;
|
|
149
|
+
};
|
|
150
|
+
export declare function BarActionRow({ primary, secondary, host, viewport, mobileOnly, syncNode, }: BarActionRowProps): ReactNode;
|
|
151
|
+
/**
|
|
152
|
+
* A home's `actions` slot, projected for both viewports.
|
|
153
|
+
*
|
|
154
|
+
* Desktop: `primary` as a `filled` button · up to `DESKTOP_SECONDARY_MAX` secondaries as `default`
|
|
155
|
+
* buttons · everything past that, plus every `kind: 'menu'`, in one `More` menu.
|
|
156
|
+
* Mobile: `primary` as an icon button when it ships an icon and a compact labelled button when it
|
|
157
|
+
* does not · one kebab holding every `mobile: 'more'` action · `mobile: 'hidden'` drops the action
|
|
158
|
+
* entirely.
|
|
159
|
+
*
|
|
160
|
+
* The shell's `mobile: 'more'` global actions are folded in by `PageBar`'s ROW-1 group only, not by
|
|
161
|
+
* this component — see `BarActionRowProps.host` for why every other home has to stay out of them.
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* <PageBar actions={{ primary: { key: 'new', label: 'New run', onClick: start },
|
|
165
|
+
* secondary: [{ key: 'export', label: 'Export', onClick: exportCsv }] }} />
|
|
166
|
+
*/
|
|
167
|
+
export declare function ActionGroup(props: ActionGroupProps): ReactNode;
|
|
168
|
+
export {};
|
|
169
|
+
//# sourceMappingURL=actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/controls/actions.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ActionIcon, Button, Group, Menu } from "@mantine/core";
|
|
3
|
+
import { createContext, useContext, useEffect } from "react";
|
|
4
|
+
import { IconSlot } from "../theme/icon-slot.js";
|
|
5
|
+
import { ControlGroup } from "./control-group.js";
|
|
6
|
+
import classes from "./actions.module.css";
|
|
7
|
+
const DESKTOP_SECONDARY_MAX = 3;
|
|
8
|
+
const MOBILE_GLOBAL_BAR_MAX = 2;
|
|
9
|
+
const NO_EXTRAS = {
|
|
10
|
+
mobileMoreActions: [],
|
|
11
|
+
claimKebab: () => () => {
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const BarExtrasContext = createContext(NO_EXTRAS);
|
|
15
|
+
function BarExtrasProvider({
|
|
16
|
+
value,
|
|
17
|
+
children
|
|
18
|
+
}) {
|
|
19
|
+
return /* @__PURE__ */ jsx(BarExtrasContext.Provider, { value, children });
|
|
20
|
+
}
|
|
21
|
+
function barActionMobile(action, isPrimary) {
|
|
22
|
+
if (action.kind === "menu") return "more";
|
|
23
|
+
return action.mobile ?? (isPrimary ? "bar" : "more");
|
|
24
|
+
}
|
|
25
|
+
function globalActionMobile(action, index) {
|
|
26
|
+
return action.mobile ?? (index < MOBILE_GLOBAL_BAR_MAX ? "bar" : "more");
|
|
27
|
+
}
|
|
28
|
+
function globalActionAsBarAction(action) {
|
|
29
|
+
return { key: action.key, kind: "custom", node: action.node, mobile: "more" };
|
|
30
|
+
}
|
|
31
|
+
function DotsGlyph() {
|
|
32
|
+
return /* @__PURE__ */ jsxs("svg", { width: 18, height: 18, viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, children: [
|
|
33
|
+
/* @__PURE__ */ jsx("circle", { cx: 12, cy: 5, r: 1.75 }),
|
|
34
|
+
/* @__PURE__ */ jsx("circle", { cx: 12, cy: 12, r: 1.75 }),
|
|
35
|
+
/* @__PURE__ */ jsx("circle", { cx: 12, cy: 19, r: 1.75 })
|
|
36
|
+
] });
|
|
37
|
+
}
|
|
38
|
+
function ChevronGlyph() {
|
|
39
|
+
return /* @__PURE__ */ jsx("svg", { width: 14, height: 14, viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx(
|
|
40
|
+
"path",
|
|
41
|
+
{
|
|
42
|
+
d: "M6 9l6 6 6-6",
|
|
43
|
+
stroke: "currentColor",
|
|
44
|
+
strokeWidth: 2,
|
|
45
|
+
strokeLinecap: "round",
|
|
46
|
+
strokeLinejoin: "round"
|
|
47
|
+
}
|
|
48
|
+
) });
|
|
49
|
+
}
|
|
50
|
+
const MOBILE_PRIMARY_PADDING_X = 10;
|
|
51
|
+
const BAR_GAP = 6;
|
|
52
|
+
function BarButton({
|
|
53
|
+
action,
|
|
54
|
+
variant,
|
|
55
|
+
px
|
|
56
|
+
}) {
|
|
57
|
+
const shared = {
|
|
58
|
+
variant,
|
|
59
|
+
...px !== void 0 && { px },
|
|
60
|
+
disabled: action.disabled === true,
|
|
61
|
+
loading: action.loading === true,
|
|
62
|
+
// EVERY icon in this file goes through `IconSlot` — the box is the framework's, never the
|
|
63
|
+
// caller's glyph (see `theme/icon-slot.tsx`). Mantine's `leftSection` is a bare flex box with no
|
|
64
|
+
// size of its own, so a consumer's `width="24"` SVG used to set this row's height.
|
|
65
|
+
...action.icon !== void 0 && { leftSection: /* @__PURE__ */ jsx(IconSlot, { children: action.icon }) },
|
|
66
|
+
...action.danger === true && { color: "red" },
|
|
67
|
+
...action.onClick !== void 0 && { onClick: action.onClick }
|
|
68
|
+
};
|
|
69
|
+
if (action.Anchor !== void 0) {
|
|
70
|
+
return /* @__PURE__ */ jsx(Button, { component: action.Anchor, ...shared, children: action.label });
|
|
71
|
+
}
|
|
72
|
+
if (action.link !== void 0) {
|
|
73
|
+
return /* @__PURE__ */ jsx(Button, { component: "a", href: action.link.to, ...shared, children: action.label });
|
|
74
|
+
}
|
|
75
|
+
return /* @__PURE__ */ jsx(Button, { ...shared, children: action.label });
|
|
76
|
+
}
|
|
77
|
+
function BarIconButton({
|
|
78
|
+
action,
|
|
79
|
+
variant
|
|
80
|
+
}) {
|
|
81
|
+
const shared = {
|
|
82
|
+
variant,
|
|
83
|
+
"aria-label": action.label,
|
|
84
|
+
disabled: action.disabled === true,
|
|
85
|
+
loading: action.loading === true,
|
|
86
|
+
...action.danger === true && { color: "red" },
|
|
87
|
+
...action.onClick !== void 0 && { onClick: action.onClick }
|
|
88
|
+
};
|
|
89
|
+
const body = /* @__PURE__ */ jsx(IconSlot, { children: action.icon });
|
|
90
|
+
if (action.Anchor !== void 0) {
|
|
91
|
+
return /* @__PURE__ */ jsx(ActionIcon, { component: action.Anchor, ...shared, children: body });
|
|
92
|
+
}
|
|
93
|
+
if (action.link !== void 0) {
|
|
94
|
+
return /* @__PURE__ */ jsx(ActionIcon, { component: "a", href: action.link.to, ...shared, children: body });
|
|
95
|
+
}
|
|
96
|
+
return /* @__PURE__ */ jsx(ActionIcon, { ...shared, children: body });
|
|
97
|
+
}
|
|
98
|
+
function MenuRow({ action }) {
|
|
99
|
+
const shared = {
|
|
100
|
+
className: action.danger === true ? `${classes.menuItem} ${classes.menuItemDanger}` : classes.menuItem,
|
|
101
|
+
disabled: action.disabled === true,
|
|
102
|
+
...action.icon !== void 0 && { leftSection: /* @__PURE__ */ jsx(IconSlot, { children: action.icon }) },
|
|
103
|
+
...action.onClick !== void 0 && { onClick: action.onClick }
|
|
104
|
+
};
|
|
105
|
+
if (action.Anchor !== void 0 && action.disabled !== true) {
|
|
106
|
+
return /* @__PURE__ */ jsx(Menu.Item, { component: action.Anchor, ...shared, children: action.label });
|
|
107
|
+
}
|
|
108
|
+
if (action.link !== void 0 && action.disabled !== true) {
|
|
109
|
+
return /* @__PURE__ */ jsx(Menu.Item, { component: "a", href: action.link.to, ...shared, children: action.label });
|
|
110
|
+
}
|
|
111
|
+
return /* @__PURE__ */ jsx(Menu.Item, { ...shared, children: action.label });
|
|
112
|
+
}
|
|
113
|
+
function menuRows(actions) {
|
|
114
|
+
return actions.flatMap((action) => {
|
|
115
|
+
if (action.kind === "menu") {
|
|
116
|
+
return [
|
|
117
|
+
/* @__PURE__ */ jsx(Menu.Label, { children: action.label }, `${action.key}-label`),
|
|
118
|
+
...menuRows(action.items)
|
|
119
|
+
];
|
|
120
|
+
}
|
|
121
|
+
if (action.kind === "custom") {
|
|
122
|
+
return [
|
|
123
|
+
/* @__PURE__ */ jsx("div", { className: classes.customRow, children: action.node }, action.key)
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
return [/* @__PURE__ */ jsx(MenuRow, { action }, action.key)];
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function OverflowMenu({
|
|
130
|
+
actions,
|
|
131
|
+
trigger = "more",
|
|
132
|
+
label = "More"
|
|
133
|
+
}) {
|
|
134
|
+
if (actions.length === 0) return null;
|
|
135
|
+
return /* @__PURE__ */ jsxs(
|
|
136
|
+
Menu,
|
|
137
|
+
{
|
|
138
|
+
position: "bottom-end",
|
|
139
|
+
offset: 6,
|
|
140
|
+
withinPortal: true,
|
|
141
|
+
zIndex: 400,
|
|
142
|
+
closeOnItemClick: true,
|
|
143
|
+
closeOnClickOutside: true,
|
|
144
|
+
closeOnEscape: true,
|
|
145
|
+
trapFocus: true,
|
|
146
|
+
returnFocus: true,
|
|
147
|
+
menuItemTabIndex: 0,
|
|
148
|
+
children: [
|
|
149
|
+
/* @__PURE__ */ jsx(Menu.Target, { children: trigger === "kebab" ? (
|
|
150
|
+
// `size="ctl"` explicitly, not inherited: the kebab is rendered by `HeaderGlobalActions`
|
|
151
|
+
// too, which is shell chrome rather than a home's slot, so there is no `CtlSlot` above it
|
|
152
|
+
// there. 28px beside 30px buttons is the mismatch the header read as.
|
|
153
|
+
/* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "ctl", "aria-label": label, children: /* @__PURE__ */ jsx(IconSlot, { className: classes.kebabIcon, children: /* @__PURE__ */ jsx(DotsGlyph, {}) }) })
|
|
154
|
+
) : /* @__PURE__ */ jsx(
|
|
155
|
+
Button,
|
|
156
|
+
{
|
|
157
|
+
variant: "default",
|
|
158
|
+
rightSection: /* @__PURE__ */ jsx(IconSlot, { className: classes.chevronIcon, children: /* @__PURE__ */ jsx(ChevronGlyph, {}) }),
|
|
159
|
+
children: label
|
|
160
|
+
}
|
|
161
|
+
) }),
|
|
162
|
+
/* @__PURE__ */ jsx(Menu.Dropdown, { children: menuRows(actions) })
|
|
163
|
+
]
|
|
164
|
+
}
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
function joinRuns(actions, opts) {
|
|
168
|
+
const joinable = (action) => action.kind === void 0 && action.key !== opts.excludeKey && (action.group === true || opts.viewport === "mobile" && action.icon !== void 0);
|
|
169
|
+
const runs = [];
|
|
170
|
+
for (const action of actions) {
|
|
171
|
+
const tail = runs.at(-1);
|
|
172
|
+
if (joinable(action) && tail !== void 0 && tail.every(joinable)) tail.push(action);
|
|
173
|
+
else runs.push([action]);
|
|
174
|
+
}
|
|
175
|
+
return runs;
|
|
176
|
+
}
|
|
177
|
+
function BarActionRow({
|
|
178
|
+
primary,
|
|
179
|
+
secondary,
|
|
180
|
+
host,
|
|
181
|
+
viewport = "both",
|
|
182
|
+
mobileOnly = [],
|
|
183
|
+
syncNode
|
|
184
|
+
}) {
|
|
185
|
+
const context = useContext(BarExtrasContext);
|
|
186
|
+
const extras = host === "page" ? context : NO_EXTRAS;
|
|
187
|
+
const list = secondary ?? [];
|
|
188
|
+
const flat = list.filter((a) => a.kind !== "menu");
|
|
189
|
+
const menus = list.filter((a) => a.kind === "menu");
|
|
190
|
+
const inline = flat.slice(0, DESKTOP_SECONDARY_MAX);
|
|
191
|
+
const desktopOverflow = [...flat.slice(DESKTOP_SECONDARY_MAX), ...menus];
|
|
192
|
+
const wantsMobile = viewport === "both";
|
|
193
|
+
const mobileList = [...list, ...mobileOnly];
|
|
194
|
+
const mobileBar = wantsMobile ? [
|
|
195
|
+
...primary !== void 0 && barActionMobile(primary, true) === "bar" ? [primary] : [],
|
|
196
|
+
...mobileList.filter((a) => barActionMobile(a, false) === "bar")
|
|
197
|
+
] : [];
|
|
198
|
+
const mobileMore = wantsMobile ? [
|
|
199
|
+
...primary !== void 0 && barActionMobile(primary, true) === "more" ? [primary] : [],
|
|
200
|
+
...mobileList.filter((a) => barActionMobile(a, false) === "more"),
|
|
201
|
+
...extras.mobileMoreActions
|
|
202
|
+
] : [];
|
|
203
|
+
const claimsKebab = mobileMore.length > 0;
|
|
204
|
+
const claimKebab = extras.claimKebab;
|
|
205
|
+
useEffect(() => {
|
|
206
|
+
if (!claimsKebab) return;
|
|
207
|
+
return claimKebab();
|
|
208
|
+
}, [claimsKebab, claimKebab]);
|
|
209
|
+
const desktopPrimary = primary !== void 0 ? /* @__PURE__ */ jsx(BarEntry, { action: primary, emphasis: "primary" }) : null;
|
|
210
|
+
const hasDesktop = primary !== void 0 || list.length > 0;
|
|
211
|
+
const hasMobile = mobileBar.length > 0 || claimsKebab;
|
|
212
|
+
if (!hasDesktop && !hasMobile && syncNode === void 0) return null;
|
|
213
|
+
const desktopLead = inline.length > 0 || desktopOverflow.length > 0 || syncNode === void 0;
|
|
214
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
215
|
+
hasDesktop && desktopLead && /* @__PURE__ */ jsxs(Group, { gap: BAR_GAP, wrap: "nowrap", visibleFrom: "sm", children: [
|
|
216
|
+
joinRuns(inline, { viewport: "desktop" }).map(
|
|
217
|
+
(run) => run.length === 1 ? /* @__PURE__ */ jsx(BarEntry, { action: run[0], emphasis: "secondary" }, run[0].key) : /* @__PURE__ */ jsx(ControlGroup, { children: run.map((action) => /* @__PURE__ */ jsx(BarEntry, { action, emphasis: "secondary", iconOnly: true }, action.key)) }, `join-${run[0].key}`)
|
|
218
|
+
),
|
|
219
|
+
/* @__PURE__ */ jsx(OverflowMenu, { actions: desktopOverflow }),
|
|
220
|
+
syncNode === void 0 && desktopPrimary
|
|
221
|
+
] }),
|
|
222
|
+
syncNode,
|
|
223
|
+
syncNode !== void 0 && desktopPrimary !== null && /* @__PURE__ */ jsx(Group, { gap: BAR_GAP, wrap: "nowrap", visibleFrom: "sm", children: desktopPrimary }),
|
|
224
|
+
hasMobile && /* @__PURE__ */ jsxs(Group, { gap: BAR_GAP, wrap: "nowrap", hiddenFrom: "sm", children: [
|
|
225
|
+
joinRuns(mobileBar, { viewport: "mobile", excludeKey: primary?.key }).map(
|
|
226
|
+
(run) => run.length === 1 ? /* @__PURE__ */ jsx(
|
|
227
|
+
BarEntry,
|
|
228
|
+
{
|
|
229
|
+
action: run[0],
|
|
230
|
+
emphasis: run[0].key === primary?.key ? "mobile-primary" : "mobile-secondary"
|
|
231
|
+
},
|
|
232
|
+
run[0].key
|
|
233
|
+
) : /* @__PURE__ */ jsx(ControlGroup, { children: run.map((action) => /* @__PURE__ */ jsx(
|
|
234
|
+
BarEntry,
|
|
235
|
+
{
|
|
236
|
+
action,
|
|
237
|
+
emphasis: action.key === primary?.key ? "mobile-primary" : "mobile-secondary"
|
|
238
|
+
},
|
|
239
|
+
action.key
|
|
240
|
+
)) }, `join-${run[0].key}`)
|
|
241
|
+
),
|
|
242
|
+
claimsKebab && /* @__PURE__ */ jsx(OverflowMenu, { actions: mobileMore, trigger: "kebab", label: "More actions" })
|
|
243
|
+
] })
|
|
244
|
+
] });
|
|
245
|
+
}
|
|
246
|
+
function ActionGroup(props) {
|
|
247
|
+
return /* @__PURE__ */ jsx(BarActionRow, { ...props, host: "slot" });
|
|
248
|
+
}
|
|
249
|
+
function BarEntry({
|
|
250
|
+
action,
|
|
251
|
+
emphasis,
|
|
252
|
+
iconOnly
|
|
253
|
+
}) {
|
|
254
|
+
if (action.kind === "custom") return /* @__PURE__ */ jsx("span", { className: classes.customSlot, children: action.node });
|
|
255
|
+
if (action.kind === "menu") return /* @__PURE__ */ jsx(OverflowMenu, { actions: [action], label: action.label });
|
|
256
|
+
if (emphasis === "mobile-primary") return /* @__PURE__ */ jsx(MobileBarEntry, { action, variant: "filled" });
|
|
257
|
+
if (emphasis === "mobile-secondary") return /* @__PURE__ */ jsx(MobileBarEntry, { action, variant: "subtle" });
|
|
258
|
+
if (iconOnly === true && action.icon !== void 0) {
|
|
259
|
+
return /* @__PURE__ */ jsx(BarIconButton, { action, variant: "default" });
|
|
260
|
+
}
|
|
261
|
+
return /* @__PURE__ */ jsx(BarButton, { action, variant: emphasis === "primary" ? "filled" : "default" });
|
|
262
|
+
}
|
|
263
|
+
function MobileBarEntry({
|
|
264
|
+
action,
|
|
265
|
+
variant
|
|
266
|
+
}) {
|
|
267
|
+
if (action.icon !== void 0) return /* @__PURE__ */ jsx(BarIconButton, { action, variant });
|
|
268
|
+
return /* @__PURE__ */ jsx(
|
|
269
|
+
BarButton,
|
|
270
|
+
{
|
|
271
|
+
action,
|
|
272
|
+
variant: variant === "filled" ? "filled" : "default",
|
|
273
|
+
px: MOBILE_PRIMARY_PADDING_X
|
|
274
|
+
}
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
export {
|
|
278
|
+
ActionGroup,
|
|
279
|
+
BarActionRow,
|
|
280
|
+
BarExtrasProvider,
|
|
281
|
+
DESKTOP_SECONDARY_MAX,
|
|
282
|
+
MOBILE_GLOBAL_BAR_MAX,
|
|
283
|
+
OverflowMenu,
|
|
284
|
+
barActionMobile,
|
|
285
|
+
globalActionAsBarAction,
|
|
286
|
+
globalActionMobile
|
|
287
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* `OverflowMenu` row chrome. Rows are `ctl`-tier (30px) on desktop and 44px below `sm`
|
|
3
|
+
* (`--vx-space-sheet-row-height`, law C15) — the mobile kebab IS the sheet, so there is no second
|
|
4
|
+
* component for the touch case.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
.menuItem {
|
|
8
|
+
min-height: calc(var(--vx-space-control-height-ctl) * var(--mantine-scale, 1));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* Danger rows read the status token, not a Mantine shade — `--vx-bad-solid` resolves per color
|
|
13
|
+
* scheme, a pinned `red.6` does not (see the `mantine-shade-index` guard).
|
|
14
|
+
*/
|
|
15
|
+
.menuItemDanger {
|
|
16
|
+
color: var(--vx-bad-solid);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* A `kind: 'custom'` row hosts the consumer's own node, so it only supplies the row box. */
|
|
20
|
+
.customRow {
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
min-height: calc(var(--vx-space-control-height-ctl) * var(--mantine-scale, 1));
|
|
24
|
+
padding: 0 var(--vx-space-row-inset-x);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@media (max-width: 47.99375em) {
|
|
28
|
+
.menuItem,
|
|
29
|
+
.customRow {
|
|
30
|
+
min-height: var(--vx-space-sheet-row-height);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/*
|
|
35
|
+
* A `kind: 'custom'` node's slot — see `BarEntry`'s doc. The whole point is the HEIGHT: basalt does
|
|
36
|
+
* not draw the node, so the only thing it can guarantee is the box it sits in. `inline-flex` +
|
|
37
|
+
* `align-items: center` centres a 20px Badge inside the 30px tier instead of letting it sit on the
|
|
38
|
+
* row's baseline, and `flex: none` keeps it out of the row's own shrink arithmetic.
|
|
39
|
+
*/
|
|
40
|
+
.customSlot {
|
|
41
|
+
display: inline-flex;
|
|
42
|
+
flex: none;
|
|
43
|
+
align-items: center;
|
|
44
|
+
height: calc(var(--vx-space-control-height-ctl) * var(--mantine-scale, 1));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/*
|
|
48
|
+
* The fold's own two glyphs, sized by overriding the icon slot's box var — never by the `width`
|
|
49
|
+
* attribute on the SVG, which is a presentation attribute the slot's CSS outranks anyway. The kebab
|
|
50
|
+
* reads at 18px because it is the ENTIRE content of an icon button (there is no label beside it to
|
|
51
|
+
* balance against), while `More`'s trailing chevron is the same 14px every pill's trailing
|
|
52
|
+
* affordance takes (`controls.module.css`'s `.glyph`).
|
|
53
|
+
*/
|
|
54
|
+
.kebabIcon {
|
|
55
|
+
--vx-space-icon-size: 1.125rem;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.chevronIcon {
|
|
59
|
+
--vx-space-icon-size: 0.875rem;
|
|
60
|
+
|
|
61
|
+
color: var(--vx-faint);
|
|
62
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
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 menuItem: string
|
|
7
|
+
readonly menuItemDanger: string
|
|
8
|
+
readonly customRow: string
|
|
9
|
+
readonly customSlot: string
|
|
10
|
+
readonly kebabIcon: string
|
|
11
|
+
readonly chevronIcon: string
|
|
12
|
+
}
|
|
13
|
+
export default classes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.test.d.ts","sourceRoot":"","sources":["../../src/controls/actions.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.type-guard.test.d.ts","sourceRoot":"","sources":["../../src/controls/actions.type-guard.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `CompareFilter` — the period-comparison enum every dashboard grows
|
|
3
|
+
* (`docs/CONTROLS-SPEC.md` §3). A `SelectFilter` narrowed to the three values basalt names, so the
|
|
4
|
+
* label, the option order and the fallback are the same on every page instead of three per app.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* // compare: field.enum(['none', 'previous', 'year'], 'none')
|
|
8
|
+
* <CompareFilter field={analytics.field.compare} />
|
|
9
|
+
*/
|
|
10
|
+
import type { ReactNode } from 'react';
|
|
11
|
+
import type { EnumField, FieldHandle } from '../state.js';
|
|
12
|
+
/** The three comparison bases — declare the field as `field.enum(COMPARE_VALUES, 'none')`. */
|
|
13
|
+
export type CompareValue = 'none' | 'previous' | 'year';
|
|
14
|
+
/** The declared value order, so a consumer's `field.enum` and this control cannot disagree. */
|
|
15
|
+
export declare const COMPARE_VALUES: readonly CompareValue[];
|
|
16
|
+
/**
|
|
17
|
+
* The three option labels basalt names — the other half of "the same on every page instead of three
|
|
18
|
+
* per app", which this control promised and did not deliver.
|
|
19
|
+
*
|
|
20
|
+
* `field.enum` labels an option with the raw VALUE until a consumer calls `store.labels()`, so a
|
|
21
|
+
* `CompareFilter` over an unlabelled field printed `none` / `previous` / `year` in its popover and,
|
|
22
|
+
* now that the pill reads the value (see `EnumFilter`'s doc), in the bar too. Every app then wrote
|
|
23
|
+
* the same three strings into its own `labels()` call, which is three chances to disagree about what
|
|
24
|
+
* `year` means.
|
|
25
|
+
*
|
|
26
|
+
* These are DEFAULTS, not overrides: a field whose label differs from its value has been labelled
|
|
27
|
+
* deliberately, and that label wins. Detecting it as `label !== value` is exactly the signal
|
|
28
|
+
* `optionsFor` leaves behind (`state/fields.ts` — `label: map?.[value] ?? value`), so nothing new
|
|
29
|
+
* has to be threaded through the store to ask the question.
|
|
30
|
+
*/
|
|
31
|
+
export declare const COMPARE_LABELS: Readonly<Record<CompareValue, string>>;
|
|
32
|
+
export type CompareFilterProps = {
|
|
33
|
+
readonly field: FieldHandle<EnumField<CompareValue>>;
|
|
34
|
+
/** @default 'Compare' */
|
|
35
|
+
readonly label?: string;
|
|
36
|
+
/** Leading pill icon. Defaults to an arrows-swap glyph — like `RangeFilter`'s calendar, the mark
|
|
37
|
+
* is part of what makes the pill legible as a period comparison. */
|
|
38
|
+
readonly icon?: ReactNode;
|
|
39
|
+
};
|
|
40
|
+
export declare function CompareFilter({ field, label, icon }: CompareFilterProps): ReactNode;
|
|
41
|
+
//# sourceMappingURL=compare-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare-filter.d.ts","sourceRoot":"","sources":["../../src/controls/compare-filter.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAItD,8FAA8F;AAC9F,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAA;AAEvD,+FAA+F;AAC/F,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAAiC,CAAA;AAEnF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAIjE,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAA;IACpD,yBAAyB;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB;yEACqE;IACrE,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAA;CAC1B,CAAA;AAED,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,KAAiB,EAAE,IAAI,EAAE,EAAE,kBAAkB,GAAG,SAAS,CAS/F"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { EnumFilter } from "./enum-filter.js";
|
|
3
|
+
import { SwapGlyph } from "./glyphs.js";
|
|
4
|
+
const COMPARE_VALUES = ["none", "previous", "year"];
|
|
5
|
+
const COMPARE_LABELS = {
|
|
6
|
+
none: "No comparison",
|
|
7
|
+
previous: "Previous period",
|
|
8
|
+
year: "Same period last year"
|
|
9
|
+
};
|
|
10
|
+
function CompareFilter({ field, label = "Compare", icon }) {
|
|
11
|
+
const options = field.options.map(
|
|
12
|
+
(option) => option.label === option.value && isCompareValue(option.value) ? { value: option.value, label: COMPARE_LABELS[option.value] } : option
|
|
13
|
+
);
|
|
14
|
+
return /* @__PURE__ */ jsx(EnumFilter, { field, label, icon: icon ?? /* @__PURE__ */ jsx(SwapGlyph, {}), options });
|
|
15
|
+
}
|
|
16
|
+
function isCompareValue(value) {
|
|
17
|
+
return COMPARE_VALUES.includes(value);
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
COMPARE_LABELS,
|
|
21
|
+
COMPARE_VALUES,
|
|
22
|
+
CompareFilter
|
|
23
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export type ControlGroupProps = {
|
|
3
|
+
readonly children: ReactNode;
|
|
4
|
+
/**
|
|
5
|
+
* `none` (the default) JOINS the children: no gap, one shared hairline between each pair, and the
|
|
6
|
+
* corner radius only on the outer ends, so the set reads as one box.
|
|
7
|
+
*
|
|
8
|
+
* `tight` keeps the tier's own control gap and only guarantees the alignment and the no-wrap. It
|
|
9
|
+
* is for a set that belongs together but must stay separately pressable — three destructive
|
|
10
|
+
* actions, or a pair where one child is `filled` and joining it would put a fill edge against a
|
|
11
|
+
* border edge.
|
|
12
|
+
*
|
|
13
|
+
* @default 'none'
|
|
14
|
+
*/
|
|
15
|
+
readonly gap?: 'none' | 'tight';
|
|
16
|
+
readonly className?: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function ControlGroup({ children, gap, className }: ControlGroupProps): ReactNode;
|
|
19
|
+
//# sourceMappingURL=control-group.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control-group.d.ts","sourceRoot":"","sources":["../../src/controls/control-group.tsx"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;IAC5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC5B,CAAA;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,GAAY,EAAE,SAAS,EAAE,EAAE,iBAAiB,GAAG,SAAS,CAWhG"}
|