basalt-ui 1.25.0 → 1.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +8 -6
- package/CHANGELOG.md +26 -0
- package/MIGRATING.md +815 -23
- package/README.md +45 -32
- package/agent/rules/basalt-batteries.md +205 -0
- package/agent/rules/basalt-charts.md +125 -555
- package/agent/rules/basalt-controls.md +160 -0
- package/agent/rules/basalt-mantine.md +146 -439
- package/agent/rules/basalt-state.md +118 -96
- package/agent/rules/basalt-tokens.md +126 -288
- package/agent/skills/basalt-app/SKILL.md +70 -195
- package/agent/skills/basalt-charts/SKILL.md +54 -234
- package/agent/skills/basalt-design/SKILL.md +56 -248
- package/agent/templates/CLAUDE-block.md.tpl +31 -40
- package/agent/templates/DESIGN.md.tpl +22 -51
- package/configs/oxlint-plugin.js +1465 -36
- package/configs/oxlint-plugin.test.ts +1190 -11
- package/configs/oxlint.json +11 -3
- package/dist/charts/cursor/resolve.d.ts +18 -0
- package/dist/charts/cursor/resolve.d.ts.map +1 -1
- package/dist/charts/cursor/resolve.js +15 -6
- package/dist/charts/cursor/scope.d.ts +6 -3
- package/dist/charts/cursor/scope.d.ts.map +1 -1
- package/dist/charts/cursor/store.d.ts +5 -1
- package/dist/charts/cursor/store.d.ts.map +1 -1
- package/dist/charts/cursor/store.js +4 -4
- package/dist/charts/hooks/useChartCursor.d.ts.map +1 -1
- package/dist/charts/hooks/useChartCursor.js +9 -8
- package/dist/charts/index.d.ts +2 -2
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.d.ts +34 -9
- package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.js +29 -114
- package/dist/charts/primitives/ChartCard.test.d.ts +2 -0
- package/dist/charts/primitives/ChartCard.test.d.ts.map +1 -0
- package/dist/charts/sparklines/BarSparkline.d.ts +26 -2
- package/dist/charts/sparklines/BarSparkline.d.ts.map +1 -1
- package/dist/charts/sparklines/BarSparkline.js +18 -4
- package/dist/cli/check-coverage.test.d.ts +2 -0
- package/dist/cli/check-coverage.test.d.ts.map +1 -0
- package/dist/cli/index.d.ts +67 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +225 -7
- package/dist/connectivity/connectivity-indicator.d.ts +4 -1
- package/dist/connectivity/connectivity-indicator.d.ts.map +1 -1
- package/dist/connectivity/connectivity-indicator.js +1 -1
- package/dist/content/index.d.ts +5 -3
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +4 -2
- package/dist/content/prose.module.css +7 -28
- package/dist/controls/actions.d.ts +169 -0
- package/dist/controls/actions.d.ts.map +1 -0
- package/dist/controls/actions.js +287 -0
- package/dist/controls/actions.module.css +62 -0
- package/dist/controls/actions.module.css.d.ts +13 -0
- package/dist/controls/actions.test.d.ts +2 -0
- package/dist/controls/actions.test.d.ts.map +1 -0
- package/dist/controls/actions.type-guard.test.d.ts +2 -0
- package/dist/controls/actions.type-guard.test.d.ts.map +1 -0
- package/dist/controls/compare-filter.d.ts +41 -0
- package/dist/controls/compare-filter.d.ts.map +1 -0
- package/dist/controls/compare-filter.js +23 -0
- package/dist/controls/control-group.d.ts +19 -0
- package/dist/controls/control-group.d.ts.map +1 -0
- package/dist/controls/control-group.js +20 -0
- package/dist/controls/control-group.test.d.ts +2 -0
- package/dist/controls/control-group.test.d.ts.map +1 -0
- package/dist/controls/controls.module.css +542 -0
- package/dist/controls/controls.module.css.d.ts +35 -0
- package/dist/controls/controls.router.test.d.ts +2 -0
- package/dist/controls/controls.router.test.d.ts.map +1 -0
- package/dist/controls/enum-filter.d.ts +39 -0
- package/dist/controls/enum-filter.d.ts.map +1 -0
- package/dist/controls/enum-filter.js +93 -0
- package/dist/controls/filter-context.d.ts +60 -0
- package/dist/controls/filter-context.d.ts.map +1 -0
- package/dist/controls/filter-context.js +69 -0
- package/dist/controls/filter-pill.d.ts +28 -0
- package/dist/controls/filter-pill.d.ts.map +1 -0
- package/dist/controls/filter-pill.js +70 -0
- package/dist/controls/filter-set.d.ts +13 -0
- package/dist/controls/filter-set.d.ts.map +1 -0
- package/dist/controls/filter-set.js +125 -0
- package/dist/controls/filter-set.test.d.ts +2 -0
- package/dist/controls/filter-set.test.d.ts.map +1 -0
- package/dist/controls/filter-sheet.d.ts +117 -0
- package/dist/controls/filter-sheet.d.ts.map +1 -0
- package/dist/controls/filter-sheet.js +146 -0
- package/dist/controls/glyphs.d.ts +27 -0
- package/dist/controls/glyphs.d.ts.map +1 -0
- package/dist/controls/glyphs.js +49 -0
- package/dist/controls/index.d.ts +20 -0
- package/dist/controls/index.d.ts.map +1 -0
- package/dist/controls/index.js +46 -0
- package/dist/controls/multi-select-filter.d.ts +23 -0
- package/dist/controls/multi-select-filter.d.ts.map +1 -0
- package/dist/controls/multi-select-filter.js +89 -0
- package/dist/controls/number-filter.d.ts +26 -0
- package/dist/controls/number-filter.d.ts.map +1 -0
- package/dist/controls/number-filter.js +143 -0
- package/dist/controls/peer-boundary.test.d.ts +2 -0
- package/dist/controls/peer-boundary.test.d.ts.map +1 -0
- package/dist/controls/props-contract.test.d.ts +2 -0
- package/dist/controls/props-contract.test.d.ts.map +1 -0
- package/dist/controls/range-filter.d.ts +43 -0
- package/dist/controls/range-filter.d.ts.map +1 -0
- package/dist/controls/range-filter.js +135 -0
- package/dist/controls/range-filter.type-guard.test.d.ts +2 -0
- package/dist/controls/range-filter.type-guard.test.d.ts.map +1 -0
- package/dist/controls/search-filter.d.ts +10 -0
- package/dist/controls/search-filter.d.ts.map +1 -0
- package/dist/controls/search-filter.js +81 -0
- package/dist/controls/select-filter.d.ts +74 -0
- package/dist/controls/select-filter.d.ts.map +1 -0
- package/dist/controls/select-filter.js +10 -0
- package/dist/controls/sync-button.d.ts +38 -0
- package/dist/controls/sync-button.d.ts.map +1 -0
- package/dist/controls/sync-button.js +119 -0
- package/dist/controls/sync-button.module.css +63 -0
- package/dist/controls/sync-button.module.css.d.ts +11 -0
- package/dist/controls/sync-button.test.d.ts +2 -0
- package/dist/controls/sync-button.test.d.ts.map +1 -0
- package/dist/controls/toggle-filter.d.ts +9 -0
- package/dist/controls/toggle-filter.d.ts.map +1 -0
- package/dist/controls/toggle-filter.js +42 -0
- package/dist/controls/view-tabs.d.ts +23 -0
- package/dist/controls/view-tabs.d.ts.map +1 -0
- package/dist/controls/view-tabs.js +59 -0
- package/dist/controls-dates/date-range-picker.d.ts +4 -0
- package/dist/controls-dates/date-range-picker.d.ts.map +1 -0
- package/dist/controls-dates/date-range-picker.js +18 -0
- package/dist/controls-dates/index.d.ts +9 -0
- package/dist/controls-dates/index.d.ts.map +1 -0
- package/dist/controls-dates/index.js +4 -0
- package/dist/dashboard/delta-badge.d.ts +6 -12
- package/dist/dashboard/delta-badge.d.ts.map +1 -1
- package/dist/dashboard/delta-badge.js +1 -50
- package/dist/dashboard/index.d.ts +1 -1
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +3 -1
- package/dist/dashboard/settings-section.d.ts +6 -4
- package/dist/dashboard/settings-section.d.ts.map +1 -1
- package/dist/dashboard/settings-section.js +23 -16
- package/dist/dashboard/settings-section.module.css +10 -0
- package/dist/dashboard/settings-section.module.css.d.ts +1 -0
- package/dist/dashboard/settings-section.test.d.ts +2 -0
- package/dist/dashboard/settings-section.test.d.ts.map +1 -0
- package/dist/dashboard/stat-card.d.ts +95 -9
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +108 -49
- package/dist/dashboard/stat-card.module.css +153 -0
- package/dist/dashboard/stat-card.module.css.d.ts +15 -0
- package/dist/data/data-table.d.ts +37 -15
- package/dist/data/data-table.d.ts.map +1 -1
- package/dist/data/data-table.js +96 -58
- package/dist/guard/grace.test.d.ts +20 -0
- package/dist/guard/grace.test.d.ts.map +1 -0
- package/dist/guard/index.d.ts +66 -3
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +78 -7
- package/dist/guard/types.d.ts +3 -3
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/index.d.ts +13 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -10
- package/dist/nav/types.d.ts +78 -2
- package/dist/nav/types.d.ts.map +1 -1
- package/dist/notifications/bell.d.ts +4 -1
- package/dist/notifications/bell.d.ts.map +1 -1
- package/dist/notifications/index.d.ts +1 -1
- package/dist/router-tanstack/field.d.ts +14 -0
- package/dist/router-tanstack/field.d.ts.map +1 -0
- package/dist/router-tanstack/field.js +4 -0
- package/dist/router-tanstack/index.d.ts +6 -2
- package/dist/router-tanstack/index.d.ts.map +1 -1
- package/dist/router-tanstack/index.js +4 -0
- package/dist/router-tanstack/multi-search-param-store.d.ts +4 -52
- package/dist/router-tanstack/multi-search-param-store.d.ts.map +1 -1
- package/dist/router-tanstack/multi-search-param-store.js +19 -27
- package/dist/router-tanstack/search-param-store.d.ts +3 -87
- package/dist/router-tanstack/search-param-store.d.ts.map +1 -1
- package/dist/router-tanstack/search-param-store.js +16 -45
- package/dist/router-tanstack/search-store.d.ts +84 -0
- package/dist/router-tanstack/search-store.d.ts.map +1 -0
- package/dist/router-tanstack/search-store.js +271 -0
- package/dist/router-tanstack/search-store.router.test.d.ts +2 -0
- package/dist/router-tanstack/search-store.router.test.d.ts.map +1 -0
- package/dist/router-tanstack/search-store.test.d.ts +2 -0
- package/dist/router-tanstack/search-store.test.d.ts.map +1 -0
- package/dist/router-tanstack/use-nav.d.ts +0 -2
- package/dist/router-tanstack/use-nav.d.ts.map +1 -1
- package/dist/router-tanstack/use-nav.js +34 -12
- package/dist/router-tanstack/use-nav.test.d.ts +2 -0
- package/dist/router-tanstack/use-nav.test.d.ts.map +1 -0
- package/dist/section/index.d.ts +2 -0
- package/dist/section/index.d.ts.map +1 -0
- package/dist/section/index.js +4 -0
- package/dist/section/section.d.ts +24 -0
- package/dist/section/section.d.ts.map +1 -0
- package/dist/section/section.js +126 -0
- package/dist/section/section.module.css +104 -0
- package/dist/section/section.module.css.d.ts +11 -0
- package/dist/section/section.test.d.ts +2 -0
- package/dist/section/section.test.d.ts.map +1 -0
- package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
- package/dist/shell/app-breadcrumbs.js +18 -15
- package/dist/shell/app-header.module.css +77 -37
- package/dist/shell/app-header.module.css.d.ts +2 -1
- package/dist/shell/app-mobile-nav.d.ts +9 -2
- package/dist/shell/app-mobile-nav.d.ts.map +1 -1
- package/dist/shell/app-mobile-nav.js +98 -11
- package/dist/shell/app-mobile-nav.module.css +8 -0
- package/dist/shell/app-mobile-nav.module.css.d.ts +1 -0
- package/dist/shell/app-sidebar.d.ts +55 -28
- package/dist/shell/app-sidebar.d.ts.map +1 -1
- package/dist/shell/app-sidebar.js +116 -67
- package/dist/shell/app-sidebar.module.css +303 -20
- package/dist/shell/app-sidebar.module.css.d.ts +19 -1
- package/dist/shell/index.d.ts +55 -21
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +40 -18
- package/dist/shell/mobile-nav-model.d.ts +13 -2
- package/dist/shell/mobile-nav-model.d.ts.map +1 -1
- package/dist/shell/mobile-nav-model.js +15 -1
- package/dist/shell/nav-count-badge.d.ts +1 -1
- package/dist/shell/page-bar.d.ts +51 -0
- package/dist/shell/page-bar.d.ts.map +1 -0
- package/dist/shell/page-bar.js +154 -0
- package/dist/shell/page-bar.module.css +178 -0
- package/dist/shell/page-bar.module.css.d.ts +17 -0
- package/dist/shell/page-bar.test.d.ts +2 -0
- package/dist/shell/page-bar.test.d.ts.map +1 -0
- package/dist/shell/sidebar-block-model.d.ts +48 -0
- package/dist/shell/sidebar-block-model.d.ts.map +1 -0
- package/dist/shell/sidebar-block-model.js +39 -0
- package/dist/shell/sidebar-blocks.d.ts +32 -0
- package/dist/shell/sidebar-blocks.d.ts.map +1 -0
- package/dist/shell/sidebar-blocks.js +169 -0
- package/dist/shell/sidebar-search.d.ts +13 -1
- package/dist/shell/sidebar-search.d.ts.map +1 -1
- package/dist/shell/sidebar-search.js +34 -1
- package/dist/shell/sidebar-search.module.css +26 -0
- package/dist/shell/sidebar-search.module.css.d.ts +2 -0
- package/dist/state/fields.d.ts +565 -0
- package/dist/state/fields.d.ts.map +1 -0
- package/dist/state/fields.js +405 -0
- package/dist/state/fields.test.d.ts +2 -0
- package/dist/state/fields.test.d.ts.map +1 -0
- package/dist/state/persisted.d.ts +60 -0
- package/dist/state/persisted.d.ts.map +1 -0
- package/dist/state/persisted.js +123 -0
- package/dist/state/persisted.test.d.ts +2 -0
- package/dist/state/persisted.test.d.ts.map +1 -0
- package/dist/state.d.ts +11 -56
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +14 -120
- package/dist/styles.css +13 -0
- package/dist/surfaces.d.ts +106 -24
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +156 -19
- package/dist/theme/controls.module.css +16 -1
- package/dist/theme/ctl-theme.d.ts +32 -0
- package/dist/theme/ctl-theme.d.ts.map +1 -0
- package/dist/theme/ctl-theme.js +54 -0
- package/dist/theme/ctl-theme.test.d.ts +2 -0
- package/dist/theme/ctl-theme.test.d.ts.map +1 -0
- package/dist/theme/ctl-tier-coverage.test.d.ts +2 -0
- package/dist/theme/ctl-tier-coverage.test.d.ts.map +1 -0
- package/dist/theme/divider-law.test.d.ts +2 -0
- package/dist/theme/divider-law.test.d.ts.map +1 -0
- package/dist/theme/icon-slot.d.ts +44 -0
- package/dist/theme/icon-slot.d.ts.map +1 -0
- package/dist/theme/icon-slot.js +21 -0
- package/dist/theme/icon-slot.module.css +47 -0
- package/dist/theme/icon-slot.module.css.d.ts +8 -0
- package/dist/theme/icon-slot.test.d.ts +2 -0
- package/dist/theme/icon-slot.test.d.ts.map +1 -0
- package/dist/theme/index.d.ts +1 -0
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +82 -5
- package/dist/theme/layout-rhythm-css.test.d.ts +2 -0
- package/dist/theme/layout-rhythm-css.test.d.ts.map +1 -0
- package/dist/theme/segmented-control.module.css +20 -0
- package/dist/theme/shadow-surfaces.test.d.ts.map +1 -1
- package/dist/tokens/index.d.ts +12 -0
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +38 -8
- package/dist/tokens/palette.d.ts +97 -61
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +76 -38
- package/dist/tokens.css +10 -2
- package/dist/widget-header/delta-badge.d.ts +13 -0
- package/dist/widget-header/delta-badge.d.ts.map +1 -0
- package/dist/widget-header/delta-badge.js +29 -0
- package/dist/widget-header/delta-badge.test.d.ts +2 -0
- package/dist/widget-header/delta-badge.test.d.ts.map +1 -0
- package/dist/widget-header/index.d.ts +3 -0
- package/dist/widget-header/index.d.ts.map +1 -0
- package/dist/widget-header/index.js +6 -0
- package/dist/widget-header/no-mantine.test.d.ts +2 -0
- package/dist/widget-header/no-mantine.test.d.ts.map +1 -0
- package/dist/widget-header/widget-header.d.ts +67 -0
- package/dist/widget-header/widget-header.d.ts.map +1 -0
- package/dist/widget-header/widget-header.js +89 -0
- package/dist/widget-header/widget-header.module.css +350 -0
- package/dist/widget-header/widget-header.module.css.d.ts +25 -0
- package/dist/widget-header/widget-header.test.d.ts +2 -0
- package/dist/widget-header/widget-header.test.d.ts.map +1 -0
- package/llms.txt +30 -17
- package/package.json +14 -1
- package/src/charts/cursor/resolve.test.ts +29 -1
- package/src/charts/cursor/resolve.ts +38 -6
- package/src/charts/cursor/scope.tsx +6 -3
- package/src/charts/cursor/store.test.ts +19 -1
- package/src/charts/cursor/store.ts +10 -5
- package/src/charts/hooks/useChartCursor.ts +16 -8
- package/src/charts/index.ts +2 -2
- package/src/charts/primitives/CartesianChart.test.tsx +124 -0
- package/src/charts/primitives/ChartCard.test.tsx +66 -0
- package/src/charts/primitives/ChartCard.tsx +86 -146
- package/src/charts/sparklines/BarSparkline.tsx +43 -5
- package/src/cli/check-coverage.test.ts +297 -0
- package/src/cli/check-theme.test.ts +38 -14
- package/src/cli/index.ts +392 -16
- package/src/cli/init-sync.test.ts +97 -0
- package/src/connectivity/connectivity-indicator.tsx +16 -9
- package/src/content/index.ts +16 -3
- package/src/content/prose.module.css +7 -28
- package/src/content/prose.module.css.test.ts +8 -24
- package/src/controls/actions.module.css +62 -0
- package/src/controls/actions.module.css.d.ts +13 -0
- package/src/controls/actions.test.tsx +252 -0
- package/src/controls/actions.tsx +655 -0
- package/src/controls/actions.type-guard.test.ts +63 -0
- package/src/controls/compare-filter.tsx +64 -0
- package/src/controls/control-group.test.tsx +229 -0
- package/src/controls/control-group.tsx +63 -0
- package/src/controls/controls.module.css +542 -0
- package/src/controls/controls.module.css.d.ts +35 -0
- package/src/controls/controls.router.test.tsx +1053 -0
- package/src/controls/enum-filter.tsx +170 -0
- package/src/controls/filter-context.tsx +158 -0
- package/src/controls/filter-pill.tsx +120 -0
- package/src/controls/filter-set.test.tsx +472 -0
- package/src/controls/filter-set.tsx +225 -0
- package/src/controls/filter-sheet.tsx +285 -0
- package/src/controls/glyphs.tsx +81 -0
- package/src/controls/index.ts +49 -0
- package/src/controls/multi-select-filter.tsx +140 -0
- package/src/controls/number-filter.tsx +291 -0
- package/src/controls/peer-boundary.test.ts +85 -0
- package/src/controls/props-contract.test.tsx +137 -0
- package/src/controls/range-filter.tsx +237 -0
- package/src/controls/range-filter.type-guard.test.tsx +122 -0
- package/src/controls/search-filter.tsx +123 -0
- package/src/controls/select-filter.tsx +89 -0
- package/src/controls/sync-button.module.css +63 -0
- package/src/controls/sync-button.module.css.d.ts +11 -0
- package/src/controls/sync-button.test.tsx +183 -0
- package/src/controls/sync-button.tsx +197 -0
- package/src/controls/toggle-filter.tsx +60 -0
- package/src/controls/view-tabs.tsx +110 -0
- package/src/controls-dates/date-range-picker.tsx +42 -0
- package/src/controls-dates/index.ts +8 -0
- package/src/dashboard/delta-badge.tsx +4 -74
- package/src/dashboard/index.ts +6 -1
- package/src/dashboard/settings-section.module.css +10 -0
- package/src/dashboard/settings-section.module.css.d.ts +1 -0
- package/src/dashboard/settings-section.test.tsx +56 -0
- package/src/dashboard/settings-section.tsx +33 -30
- package/src/dashboard/stat-card.module.css +153 -0
- package/src/dashboard/stat-card.module.css.d.ts +15 -0
- package/src/dashboard/stat-card.test.tsx +298 -1
- package/src/dashboard/stat-card.tsx +289 -67
- package/src/data/data-table.test.tsx +96 -11
- package/src/data/data-table.tsx +228 -84
- package/src/guard/check-source.test.ts +159 -13
- package/src/guard/grace.test.ts +158 -0
- package/src/guard/index.ts +185 -29
- package/src/guard/types.ts +4 -2
- package/src/index.ts +51 -5
- package/src/nav/types.ts +84 -2
- package/src/notifications/bell.tsx +6 -3
- package/src/notifications/index.ts +1 -1
- package/src/router-tanstack/field.ts +35 -0
- package/src/router-tanstack/index.ts +28 -2
- package/src/router-tanstack/multi-search-param-store.test.ts +71 -3
- package/src/router-tanstack/multi-search-param-store.ts +46 -77
- package/src/router-tanstack/search-param-store.ts +42 -159
- package/src/router-tanstack/search-store.router.test.tsx +882 -0
- package/src/router-tanstack/search-store.test.ts +578 -0
- package/src/router-tanstack/search-store.ts +546 -0
- package/src/router-tanstack/use-nav.test.tsx +187 -0
- package/src/router-tanstack/use-nav.ts +72 -14
- package/src/section/index.ts +1 -0
- package/src/section/section.module.css +104 -0
- package/src/section/section.module.css.d.ts +11 -0
- package/src/section/section.test.tsx +204 -0
- package/src/section/section.tsx +220 -0
- package/src/shell/app-breadcrumbs.tsx +44 -31
- package/src/shell/app-header.module.css +77 -37
- package/src/shell/app-header.module.css.d.ts +2 -1
- package/src/shell/app-mobile-nav.module.css +8 -0
- package/src/shell/app-mobile-nav.module.css.d.ts +1 -0
- package/src/shell/app-mobile-nav.tsx +147 -8
- package/src/shell/app-sidebar-account.tsx +2 -2
- package/src/shell/app-sidebar.module.css +303 -20
- package/src/shell/app-sidebar.module.css.d.ts +19 -1
- package/src/shell/app-sidebar.test.tsx +456 -41
- package/src/shell/app-sidebar.tsx +276 -140
- package/src/shell/index.test.tsx +291 -5
- package/src/shell/index.tsx +137 -38
- package/src/shell/mobile-nav-model.test.ts +228 -7
- package/src/shell/mobile-nav-model.ts +53 -3
- package/src/shell/nav-count-badge.tsx +1 -1
- package/src/shell/page-bar.module.css +178 -0
- package/src/shell/page-bar.module.css.d.ts +17 -0
- package/src/shell/page-bar.test.tsx +316 -0
- package/src/shell/page-bar.tsx +315 -0
- package/src/shell/sidebar-block-model.ts +83 -0
- package/src/shell/sidebar-blocks.tsx +301 -0
- package/src/shell/sidebar-search.module.css +26 -0
- package/src/shell/sidebar-search.module.css.d.ts +2 -0
- package/src/shell/sidebar-search.tsx +71 -2
- package/src/state/fields.test.tsx +512 -0
- package/src/state/fields.ts +1150 -0
- package/src/state/persisted.test.ts +91 -0
- package/src/state/persisted.ts +261 -0
- package/src/state.ts +52 -240
- package/src/styles.css +13 -0
- package/src/surfaces.ts +204 -36
- package/src/theme/controls.module.css +16 -1
- package/src/theme/ctl-theme.test.tsx +275 -0
- package/src/theme/ctl-theme.tsx +138 -0
- package/src/theme/ctl-tier-coverage.test.ts +170 -0
- package/src/theme/density-relations.test.ts +106 -51
- package/src/theme/divider-law.test.ts +259 -0
- package/src/theme/icon-slot.module.css +47 -0
- package/src/theme/icon-slot.module.css.d.ts +8 -0
- package/src/theme/icon-slot.test.tsx +275 -0
- package/src/theme/icon-slot.tsx +59 -0
- package/src/theme/index.ts +165 -3
- package/src/theme/layout-rhythm-css.test.ts +72 -0
- package/src/theme/radius.test.ts +11 -1
- package/src/theme/segmented-control.module.css +20 -0
- package/src/theme/shadow-surfaces.test.ts +20 -7
- package/src/theme/spacing.test.ts +75 -27
- package/src/theme/use-basalt-spacing.ts +2 -2
- package/src/tokens/density.test.ts +7 -25
- package/src/tokens/index.ts +48 -8
- package/src/tokens/palette.ts +107 -73
- package/src/widget-header/delta-badge.test.tsx +60 -0
- package/src/widget-header/delta-badge.tsx +57 -0
- package/src/widget-header/index.ts +2 -0
- package/src/widget-header/no-mantine.test.ts +33 -0
- package/src/widget-header/widget-header.module.css +350 -0
- package/src/widget-header/widget-header.module.css.d.ts +25 -0
- package/src/widget-header/widget-header.test.tsx +187 -0
- package/src/widget-header/widget-header.tsx +213 -0
- package/agent/rules/basalt-agent.md +0 -452
- package/agent/rules/basalt-app.md +0 -246
- package/agent/rules/basalt-commands.md +0 -320
- package/agent/rules/basalt-content.md +0 -409
- package/agent/rules/basalt-data.md +0 -329
- package/agent/rules/basalt-forms.md +0 -186
- package/agent/rules/basalt-notifications.md +0 -264
- package/agent/rules/basalt-query.md +0 -187
- package/agent/rules/basalt-router.md +0 -336
- package/dist/content/article-filter-bar.d.ts +0 -40
- package/dist/content/article-filter-bar.d.ts.map +0 -1
- package/dist/content/article-filter-bar.js +0 -56
- package/dist/shell/page-header.d.ts +0 -13
- package/dist/shell/page-header.d.ts.map +0 -1
- package/dist/shell/page-header.js +0 -23
- package/src/content/article-filter-bar.tsx +0 -88
- package/src/shell/page-header.tsx +0 -35
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { MantineProvider } from '@mantine/core'
|
|
23
23
|
import { describe, expect, test } from 'bun:test'
|
|
24
|
+
import { readFileSync } from 'node:fs'
|
|
25
|
+
import { resolve } from 'node:path'
|
|
24
26
|
import { renderToStaticMarkup } from 'react-dom/server'
|
|
25
27
|
import { StatCard } from './stat-card'
|
|
26
28
|
import type { StatCardTone } from './stat-card'
|
|
@@ -30,7 +32,7 @@ function render(tone?: StatCardTone) {
|
|
|
30
32
|
<MantineProvider>
|
|
31
33
|
{/* exactOptionalPropertyTypes forbids an explicit tone={undefined} — spread only when set */}
|
|
32
34
|
<StatCard
|
|
33
|
-
|
|
35
|
+
title="Downtime · last 24h"
|
|
34
36
|
value="0 min"
|
|
35
37
|
{...(tone !== undefined ? { tone } : {})}
|
|
36
38
|
/>
|
|
@@ -86,3 +88,298 @@ describe('omitting tone is not "good" — it is untinted, and says nothing', ()
|
|
|
86
88
|
expect(markup).toContain('0 min')
|
|
87
89
|
})
|
|
88
90
|
})
|
|
91
|
+
|
|
92
|
+
describe('the header composes WidgetHeader at the widget tier', () => {
|
|
93
|
+
test('renders an h3 carrying the title', () => {
|
|
94
|
+
const markup = renderToStaticMarkup(
|
|
95
|
+
<MantineProvider>
|
|
96
|
+
<StatCard title="Active Users" value="12,483" />
|
|
97
|
+
</MantineProvider>,
|
|
98
|
+
)
|
|
99
|
+
expect(markup).toContain('<h3')
|
|
100
|
+
expect(markup).toContain('Active Users')
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
describe('info and subtitle reach the composed WidgetHeader', () => {
|
|
105
|
+
function renderHeader(props: { info?: string; subtitle?: string }) {
|
|
106
|
+
return renderToStaticMarkup(
|
|
107
|
+
<MantineProvider>
|
|
108
|
+
<StatCard title="Training load" value="412" {...props} />
|
|
109
|
+
</MantineProvider>,
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
test('the subtitle renders as its own line under the hero row', () => {
|
|
114
|
+
expect(renderHeader({ subtitle: 'TSS · 7-day rolling' })).toContain('TSS · 7-day rolling')
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
test('the info text ships with the glyph, and never inside the heading', () => {
|
|
118
|
+
const markup = renderHeader({ info: 'Sum of per-session TSS over 7 days.' })
|
|
119
|
+
// `WidgetHeader` renders the bubble only while open, so the closed markup carries the named
|
|
120
|
+
// trigger — what matters here is that the card FORWARDED the prop and that the text stayed OUT
|
|
121
|
+
// of the `<h3>`, which is what an `info` rendered inside the heading would silently do.
|
|
122
|
+
expect(markup).toContain('aria-label="More information"')
|
|
123
|
+
const heading = /<h3[^>]*>(.*?)<\/h3>/.exec(markup)?.[1] ?? ''
|
|
124
|
+
expect(heading).toContain('Training load')
|
|
125
|
+
expect(heading).not.toContain('Sum of')
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
test('neither prop renders anything when omitted — no glyph, no empty line', () => {
|
|
129
|
+
expect(renderHeader({})).not.toContain('More information')
|
|
130
|
+
})
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
describe('sparklinePlacement', () => {
|
|
134
|
+
function renderWithSparkline(placement?: 'bleed' | 'right') {
|
|
135
|
+
return renderToStaticMarkup(
|
|
136
|
+
<MantineProvider>
|
|
137
|
+
<StatCard
|
|
138
|
+
title="Active Users"
|
|
139
|
+
value="12,483"
|
|
140
|
+
sparkline={<span data-testid="spark">spark</span>}
|
|
141
|
+
{...(placement !== undefined ? { sparklinePlacement: placement } : {})}
|
|
142
|
+
/>
|
|
143
|
+
</MantineProvider>,
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
test('defaults to bleed — the full-width row bled to the card edges', () => {
|
|
148
|
+
const markup = renderWithSparkline()
|
|
149
|
+
expect(markup).toContain('data-placement="bleed"')
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
test('right sits the sparkline beside the hero-value row', () => {
|
|
153
|
+
const markup = renderWithSparkline('right')
|
|
154
|
+
expect(markup).toContain('data-placement="right"')
|
|
155
|
+
})
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* `unit` and `breakdown` — the two props that existed as hand-rolled cards in three consumers
|
|
160
|
+
* before they existed here. Both are asserted through the same static markup as the rail above; the
|
|
161
|
+
* CSS half (which cannot resolve under `bun test`, where a `.module.css` import is `undefined`) is
|
|
162
|
+
* pinned against the shipped stylesheet TEXT at the bottom of this file, the pattern
|
|
163
|
+
* `page-bar.test.tsx` uses.
|
|
164
|
+
*/
|
|
165
|
+
describe('unit — the hero value is a number and a unit, not one string', () => {
|
|
166
|
+
function renderUnit(props: { unit?: string; value?: string }) {
|
|
167
|
+
return renderToStaticMarkup(
|
|
168
|
+
<MantineProvider>
|
|
169
|
+
<StatCard title="Training load" value="412" {...props} />
|
|
170
|
+
</MantineProvider>,
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
test('renders after the value, and both are present', () => {
|
|
175
|
+
const markup = renderUnit({ unit: 'TSS' })
|
|
176
|
+
expect(markup).toContain('412')
|
|
177
|
+
expect(markup).toContain('TSS')
|
|
178
|
+
// Order matters — a unit BEFORE the numeral reads as a currency prefix, which it is not.
|
|
179
|
+
expect(markup.indexOf('412')).toBeLessThan(markup.indexOf('TSS'))
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
test('omitting it renders nothing extra', () => {
|
|
183
|
+
expect(renderUnit({})).not.toContain('TSS')
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
test('it is not the same channel as `subtitle` — a card may carry both', () => {
|
|
187
|
+
const markup = renderToStaticMarkup(
|
|
188
|
+
<MantineProvider>
|
|
189
|
+
<StatCard title="Training load" value="412" unit="TSS" subtitle="7-day rolling" />
|
|
190
|
+
</MantineProvider>,
|
|
191
|
+
)
|
|
192
|
+
expect(markup).toContain('TSS')
|
|
193
|
+
expect(markup).toContain('7-day rolling')
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* `deltaFormat` — because not every delta is a percentage, and the default said it was.
|
|
199
|
+
*
|
|
200
|
+
* A pace card's trend is `0:12 /km` and a speed card's is `0.3 km/h`; both rendered as `0.3%`, which
|
|
201
|
+
* is a wrong unit on a KPI — the failure worse than showing no chip at all, and the reason the
|
|
202
|
+
* consumer that needed one kept its card hand-rolled (the `HeroCard` fork `shadow-basalt-export`
|
|
203
|
+
* reports). The prop is a FUNCTION over the signed number, not a label string: `delta` still drives
|
|
204
|
+
* the tone and the glyph, so there is one place the sign is decided.
|
|
205
|
+
*/
|
|
206
|
+
describe('deltaFormat — a delta that is not a percentage', () => {
|
|
207
|
+
function renderDelta(props: {
|
|
208
|
+
delta?: number
|
|
209
|
+
deltaFormat?: (delta: number) => string
|
|
210
|
+
deltaGlyph?: boolean
|
|
211
|
+
deltaPeriod?: string
|
|
212
|
+
}) {
|
|
213
|
+
return renderToStaticMarkup(
|
|
214
|
+
<MantineProvider>
|
|
215
|
+
<StatCard title="Pace" value="5:31" unit="/km" {...props} />
|
|
216
|
+
</MantineProvider>,
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
test('the default is still a one-decimal percentage — no existing card moves', () => {
|
|
221
|
+
const markup = renderDelta({ delta: 0.3 })
|
|
222
|
+
expect(markup).toContain('0.3%')
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
test('a formatter replaces the label and the percent is gone', () => {
|
|
226
|
+
const markup = renderDelta({ delta: 0.3, deltaFormat: (v) => `${v.toFixed(1)} km/h` })
|
|
227
|
+
expect(markup).toContain('0.3 km/h')
|
|
228
|
+
expect(markup).not.toContain('0.3%')
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
test('the formatter receives the SIGNED value, so it can print an absolute delta itself', () => {
|
|
232
|
+
// argo's walking-pad pace card: the delta is seconds per km, faster is a negative number, and
|
|
233
|
+
// the sign belongs in the string. `deltaGlyph={false}` stops the ▼ saying it a second time.
|
|
234
|
+
const markup = renderDelta({
|
|
235
|
+
delta: -12,
|
|
236
|
+
deltaFormat: (s) => `${s < 0 ? '−' : '+'}0:${String(Math.abs(s)).padStart(2, '0')} /km`,
|
|
237
|
+
deltaGlyph: false,
|
|
238
|
+
})
|
|
239
|
+
// The chip's whole text, so nothing survives beside it — the glyph, or a stray `12.0%`.
|
|
240
|
+
expect(markup).toContain('>−0:12 /km</span>')
|
|
241
|
+
expect(markup).not.toContain('▼')
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
test('the glyph and the tone still come off the number, not the label', () => {
|
|
245
|
+
const markup = renderDelta({ delta: -12, deltaFormat: (s) => `${Math.abs(s)} s` })
|
|
246
|
+
// Negative reads as danger and keeps its ▼ — the formatter only owns the magnitude string.
|
|
247
|
+
expect(markup).toContain('▼')
|
|
248
|
+
expect(markup).toContain('var(--vx-status-bad)')
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
test('deltaPeriod still rides beside a formatted delta', () => {
|
|
252
|
+
const markup = renderDelta({
|
|
253
|
+
delta: 0.3,
|
|
254
|
+
deltaFormat: (v) => `${v.toFixed(1)} km/h`,
|
|
255
|
+
deltaPeriod: 'WoW',
|
|
256
|
+
})
|
|
257
|
+
expect(markup).toContain('0.3 km/h')
|
|
258
|
+
expect(markup).toContain('WoW')
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
test('a formatter with no delta renders no chip at all', () => {
|
|
262
|
+
expect(renderDelta({ deltaFormat: (v) => `${v} km/h` })).not.toContain('km/h')
|
|
263
|
+
})
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
describe('breakdown — the parts the hero number is made of', () => {
|
|
267
|
+
const ROWS = [
|
|
268
|
+
{ label: 'Paid', value: '1,204' },
|
|
269
|
+
{ label: 'Organic', value: '842' },
|
|
270
|
+
{ label: 'Referral', value: '31', tone: 'bad' as const },
|
|
271
|
+
]
|
|
272
|
+
|
|
273
|
+
function renderBreakdown(breakdown?: readonly { label: string; value: string }[]) {
|
|
274
|
+
return renderToStaticMarkup(
|
|
275
|
+
<MantineProvider>
|
|
276
|
+
<StatCard
|
|
277
|
+
title="Orders"
|
|
278
|
+
value="2,077"
|
|
279
|
+
{...(breakdown !== undefined ? { breakdown } : {})}
|
|
280
|
+
/>
|
|
281
|
+
</MantineProvider>,
|
|
282
|
+
)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
test('every row renders its label and its value', () => {
|
|
286
|
+
const markup = renderBreakdown(ROWS)
|
|
287
|
+
for (const row of ROWS) {
|
|
288
|
+
expect(markup).toContain(row.label)
|
|
289
|
+
expect(markup).toContain(row.value)
|
|
290
|
+
}
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
test('the rows are a description list — each row IS a term and its value', () => {
|
|
294
|
+
const markup = renderBreakdown(ROWS)
|
|
295
|
+
expect(markup).toContain('<dl')
|
|
296
|
+
expect((markup.match(/<dt/g) ?? []).length).toBe(3)
|
|
297
|
+
expect((markup.match(/<dd/g) ?? []).length).toBe(3)
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
test('a toned row carries data-tone; an untoned one carries nothing', () => {
|
|
301
|
+
const markup = renderBreakdown(ROWS)
|
|
302
|
+
expect(markup).toContain('data-tone="bad"')
|
|
303
|
+
// Omission is not `good`, exactly as it is not on the card itself.
|
|
304
|
+
expect(markup).not.toContain('data-tone="good"')
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
test('omitted, and an empty array, render no list at all', () => {
|
|
308
|
+
expect(renderBreakdown()).not.toContain('<dl')
|
|
309
|
+
expect(renderBreakdown([])).not.toContain('<dl')
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
// The reason the rows live INSIDE the header block rather than beside it: with
|
|
313
|
+
// `sparklinePlacement="right"` the card body is a flex ROW, so a third child there would sit next
|
|
314
|
+
// to the trend instead of under the number it splits.
|
|
315
|
+
test('the list sits inside the header block, before the sparkline slot', () => {
|
|
316
|
+
const markup = renderToStaticMarkup(
|
|
317
|
+
<MantineProvider>
|
|
318
|
+
<StatCard
|
|
319
|
+
title="Orders"
|
|
320
|
+
value="2,077"
|
|
321
|
+
breakdown={ROWS}
|
|
322
|
+
sparklinePlacement="right"
|
|
323
|
+
sparkline={<span>spark</span>}
|
|
324
|
+
/>
|
|
325
|
+
</MantineProvider>,
|
|
326
|
+
)
|
|
327
|
+
expect(markup.indexOf('<dl')).toBeLessThan(markup.indexOf('spark'))
|
|
328
|
+
})
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* The CSS half, pinned against the shipped module text. A `.module.css` resolves to `undefined`
|
|
333
|
+
* under `bun test` (no bundler in the loop), so the class names never reach the markup and the only
|
|
334
|
+
* checkable thing is the stylesheet itself.
|
|
335
|
+
*/
|
|
336
|
+
describe('stat-card.module.css — the breakdown block', () => {
|
|
337
|
+
const css = readFileSync(resolve(import.meta.dirname, 'stat-card.module.css'), 'utf8')
|
|
338
|
+
/** Declarations only — the comments above them discuss hairlines and borders by name. */
|
|
339
|
+
const decls = css.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
340
|
+
|
|
341
|
+
function rule(selector: string): string {
|
|
342
|
+
const start = decls.indexOf(`${selector} {`)
|
|
343
|
+
expect(start).toBeGreaterThan(-1)
|
|
344
|
+
return decls.slice(start, decls.indexOf('}', start))
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
test('a row is one line at the TAG tier, from the token and never a literal', () => {
|
|
348
|
+
expect(rule('.breakdownRow')).toContain('min-height: var(--vx-space-control-height-tag)')
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
// THE law this block had to not break: `theme/divider-law.test.ts` inventories every horizontal
|
|
352
|
+
// hairline basalt draws, and §2.1 puts one between OPTION rows and nowhere else. Three rules
|
|
353
|
+
// inside a KPI card would read as a table wearing a card's clothes — and would fail that test.
|
|
354
|
+
test('no row draws a hairline — not a border, not a rule, not a divider', () => {
|
|
355
|
+
expect(decls).not.toMatch(/\.breakdown[^{]*\{[^}]*border/)
|
|
356
|
+
expect(decls).not.toContain('--vx-surface-hairline')
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
test('the label is muted prose and the value is mono ink — the separation is weight, not a line', () => {
|
|
360
|
+
expect(rule('.breakdownLabel')).toContain('color: var(--vx-muted)')
|
|
361
|
+
expect(rule('.breakdownValue')).toContain('font-family: var(--basalt-font-mono)')
|
|
362
|
+
expect(rule('.breakdownValue')).toContain('color: var(--vx-ink)')
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
test('a toned row reads the per-scheme status solid, never a hex', () => {
|
|
366
|
+
for (const tone of ['good', 'warn', 'bad']) {
|
|
367
|
+
expect(rule(`.breakdownRow[data-tone='${tone}'] .breakdownValue`)).toContain(
|
|
368
|
+
`color: var(--vx-status-${tone})`,
|
|
369
|
+
)
|
|
370
|
+
}
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
test('the unit beside the hero value is mono, muted and small — in widget-header, its owner', () => {
|
|
374
|
+
const headerCss = readFileSync(
|
|
375
|
+
resolve(import.meta.dirname, '..', 'widget-header', 'widget-header.module.css'),
|
|
376
|
+
'utf8',
|
|
377
|
+
).replace(/\/\*[\s\S]*?\*\//g, '')
|
|
378
|
+
const start = headerCss.indexOf('.unit {')
|
|
379
|
+
expect(start).toBeGreaterThan(-1)
|
|
380
|
+
const unit = headerCss.slice(start, headerCss.indexOf('}', start))
|
|
381
|
+
expect(unit).toContain('font-family: var(--basalt-font-mono)')
|
|
382
|
+
expect(unit).toContain('font-size: var(--vx-text-sm)')
|
|
383
|
+
expect(unit).toContain('color: var(--vx-muted)')
|
|
384
|
+
})
|
|
385
|
+
})
|