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,146 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, Drawer } from "@mantine/core";
|
|
3
|
+
import { useId } from "react";
|
|
4
|
+
import classes from "./controls.module.css";
|
|
5
|
+
function FilterSheet({
|
|
6
|
+
opened,
|
|
7
|
+
onClose,
|
|
8
|
+
onResetAll,
|
|
9
|
+
title = "Filters",
|
|
10
|
+
children
|
|
11
|
+
}) {
|
|
12
|
+
return /* @__PURE__ */ jsx(
|
|
13
|
+
Drawer,
|
|
14
|
+
{
|
|
15
|
+
opened,
|
|
16
|
+
onClose,
|
|
17
|
+
position: "bottom",
|
|
18
|
+
radius: "sm",
|
|
19
|
+
classNames: { header: classes.sheetHeader, title: classes.sheetTitle },
|
|
20
|
+
title: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
21
|
+
/* @__PURE__ */ jsx("span", { className: classes.sheetTitleText, children: title }),
|
|
22
|
+
/* @__PURE__ */ jsx(Button, { variant: "subtle", size: "ctl", onClick: onResetAll, children: "Reset all" })
|
|
23
|
+
] }),
|
|
24
|
+
children: /* @__PURE__ */ jsx("div", { className: classes.sheetBody, children })
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
function CheckGlyph() {
|
|
29
|
+
return /* @__PURE__ */ jsx(
|
|
30
|
+
"svg",
|
|
31
|
+
{
|
|
32
|
+
width: 16,
|
|
33
|
+
height: 16,
|
|
34
|
+
viewBox: "0 0 24 24",
|
|
35
|
+
fill: "none",
|
|
36
|
+
stroke: "currentColor",
|
|
37
|
+
strokeWidth: 2.25,
|
|
38
|
+
strokeLinecap: "round",
|
|
39
|
+
strokeLinejoin: "round",
|
|
40
|
+
"aria-hidden": true,
|
|
41
|
+
children: /* @__PURE__ */ jsx("path", { d: "M5 12.5l5 5l9 -11" })
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
function SheetOptionList({
|
|
46
|
+
mode,
|
|
47
|
+
selected,
|
|
48
|
+
options,
|
|
49
|
+
onToggle,
|
|
50
|
+
labelId
|
|
51
|
+
}) {
|
|
52
|
+
const name = useId();
|
|
53
|
+
return (
|
|
54
|
+
// A real `<fieldset>`, not `role="group"` on a div — the native element carries the grouping
|
|
55
|
+
// semantics and gets its own box reset in the module (`.sheetList`). `aria-labelledby` points at
|
|
56
|
+
// the `SheetField` heading rather than a `<legend>`: the heading is already visible above the
|
|
57
|
+
// list, and a legend would render it a second time.
|
|
58
|
+
/* @__PURE__ */ jsx("fieldset", { className: classes.sheetList, "aria-labelledby": labelId, children: options.map((option) => {
|
|
59
|
+
const isSelected = selected.includes(option.value);
|
|
60
|
+
return /* @__PURE__ */ jsxs(
|
|
61
|
+
"label",
|
|
62
|
+
{
|
|
63
|
+
className: classes.sheetOption,
|
|
64
|
+
...isSelected && { "data-selected": true },
|
|
65
|
+
...option.disabled === true && { "data-disabled": true },
|
|
66
|
+
children: [
|
|
67
|
+
/* @__PURE__ */ jsx(
|
|
68
|
+
"input",
|
|
69
|
+
{
|
|
70
|
+
className: classes.sheetOptionInput,
|
|
71
|
+
type: mode === "single" ? "radio" : "checkbox",
|
|
72
|
+
name,
|
|
73
|
+
value: option.value,
|
|
74
|
+
checked: isSelected,
|
|
75
|
+
disabled: option.disabled === true,
|
|
76
|
+
"aria-label": option.label,
|
|
77
|
+
onChange: () => {
|
|
78
|
+
onToggle(option.value);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
),
|
|
82
|
+
/* @__PURE__ */ jsx("span", { className: classes.sheetOptionLabel, children: option.label }),
|
|
83
|
+
isSelected && /* @__PURE__ */ jsx("span", { className: classes.sheetOptionCheck, "aria-hidden": true, children: /* @__PURE__ */ jsx(CheckGlyph, {}) })
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
option.value
|
|
87
|
+
);
|
|
88
|
+
}) })
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
function SheetDisclosure({
|
|
92
|
+
label,
|
|
93
|
+
expanded,
|
|
94
|
+
onToggle,
|
|
95
|
+
children
|
|
96
|
+
}) {
|
|
97
|
+
return /* @__PURE__ */ jsxs("div", { className: classes.sheetList, children: [
|
|
98
|
+
/* @__PURE__ */ jsxs(
|
|
99
|
+
"button",
|
|
100
|
+
{
|
|
101
|
+
type: "button",
|
|
102
|
+
className: classes.sheetOption,
|
|
103
|
+
"aria-expanded": expanded,
|
|
104
|
+
...expanded && { "data-selected": true },
|
|
105
|
+
onClick: onToggle,
|
|
106
|
+
children: [
|
|
107
|
+
/* @__PURE__ */ jsx("span", { className: classes.sheetOptionLabel, children: label }),
|
|
108
|
+
expanded && /* @__PURE__ */ jsx("span", { className: classes.sheetOptionCheck, "aria-hidden": true, children: /* @__PURE__ */ jsx(CheckGlyph, {}) })
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
),
|
|
112
|
+
expanded && /* @__PURE__ */ jsx("div", { className: classes.sheetDisclosureBody, children })
|
|
113
|
+
] });
|
|
114
|
+
}
|
|
115
|
+
function SheetField({ label, labelId, children }) {
|
|
116
|
+
return /* @__PURE__ */ jsxs("div", { className: classes.sheetField, children: [
|
|
117
|
+
/* @__PURE__ */ jsx("span", { className: classes.sheetLabel, ...labelId !== void 0 && { id: labelId }, children: label }),
|
|
118
|
+
children
|
|
119
|
+
] });
|
|
120
|
+
}
|
|
121
|
+
function useControlName(label, inSheet) {
|
|
122
|
+
const labelId = useId();
|
|
123
|
+
return {
|
|
124
|
+
labelId,
|
|
125
|
+
nameProps: inSheet ? { "aria-labelledby": labelId } : { "aria-label": label }
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function SheetRow({ children }) {
|
|
129
|
+
return /* @__PURE__ */ jsx("div", { className: classes.sheetRow, children });
|
|
130
|
+
}
|
|
131
|
+
const sheetRowClass = classes.sheetRow;
|
|
132
|
+
const sheetRowClassNames = {
|
|
133
|
+
root: classes.sheetRow,
|
|
134
|
+
body: classes.sheetRowBody,
|
|
135
|
+
label: classes.sheetRowLabel
|
|
136
|
+
};
|
|
137
|
+
export {
|
|
138
|
+
FilterSheet,
|
|
139
|
+
SheetDisclosure,
|
|
140
|
+
SheetField,
|
|
141
|
+
SheetOptionList,
|
|
142
|
+
SheetRow,
|
|
143
|
+
sheetRowClass,
|
|
144
|
+
sheetRowClassNames,
|
|
145
|
+
useControlName
|
|
146
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The control tier's own inline glyphs (`docs/CONTROLS-SPEC.md` §3).
|
|
3
|
+
*
|
|
4
|
+
* basalt ships NO icon dependency — every `icon` slot in the public API takes a `ReactNode` the
|
|
5
|
+
* consumer fills. These are the few glyphs the FRAMEWORK draws for itself, where a default icon is
|
|
6
|
+
* part of the control's identity rather than a caller's decision: a range filter reads as a date
|
|
7
|
+
* picker because it carries a calendar, a compare filter because it carries the swap arrows, and
|
|
8
|
+
* the mobile `Filters (n)` pill because it carries a funnel. Same convention the shell already uses
|
|
9
|
+
* for its own chrome (`shell/sidebar-search.tsx`, `shell/app-sidebar.tsx`): a 24×24 `viewBox`,
|
|
10
|
+
* `stroke="currentColor"`, sized down by the `size` prop.
|
|
11
|
+
*
|
|
12
|
+
* INTERNAL — not on the `./controls` barrel. A consumer wanting a different glyph passes `icon`.
|
|
13
|
+
*/
|
|
14
|
+
import type { ReactNode } from 'react';
|
|
15
|
+
/** `RangeFilter`'s default leading icon. */
|
|
16
|
+
export declare function CalendarGlyph(): ReactNode;
|
|
17
|
+
/** `CompareFilter`'s default leading icon — the arrows-swap glyph. */
|
|
18
|
+
export declare function SwapGlyph(): ReactNode;
|
|
19
|
+
/** The mobile `Filters (n)` pill's leading icon. */
|
|
20
|
+
export declare function FunnelGlyph(): ReactNode;
|
|
21
|
+
/**
|
|
22
|
+
* Every pill's trailing affordance — 14px, muted, and an SVG rather than the `⇅` character it
|
|
23
|
+
* replaced. A text glyph rendered at the label's own font metrics, so it sat off the optical centre
|
|
24
|
+
* and shifted with the font stack; the path is centred in its own box.
|
|
25
|
+
*/
|
|
26
|
+
export declare function UpDownGlyph(): ReactNode;
|
|
27
|
+
//# sourceMappingURL=glyphs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glyphs.d.ts","sourceRoot":"","sources":["../../src/controls/glyphs.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAwBtC,4CAA4C;AAC5C,wBAAgB,aAAa,IAAI,SAAS,CASzC;AAED,sEAAsE;AACtE,wBAAgB,SAAS,IAAI,SAAS,CAOrC;AAED,oDAAoD;AACpD,wBAAgB,WAAW,IAAI,SAAS,CAMvC;AAED;;;;GAIG;AACH,wBAAgB,WAAW,IAAI,SAAS,CAOvC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
const ICON_SIZE = 16;
|
|
3
|
+
const AFFORDANCE_SIZE = 14;
|
|
4
|
+
function Stroke({ size = ICON_SIZE, children }) {
|
|
5
|
+
return /* @__PURE__ */ jsx(
|
|
6
|
+
"svg",
|
|
7
|
+
{
|
|
8
|
+
width: size,
|
|
9
|
+
height: size,
|
|
10
|
+
viewBox: "0 0 24 24",
|
|
11
|
+
fill: "none",
|
|
12
|
+
stroke: "currentColor",
|
|
13
|
+
strokeWidth: 1.75,
|
|
14
|
+
strokeLinecap: "round",
|
|
15
|
+
strokeLinejoin: "round",
|
|
16
|
+
"aria-hidden": true,
|
|
17
|
+
children
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
function CalendarGlyph() {
|
|
22
|
+
return /* @__PURE__ */ jsxs(Stroke, { children: [
|
|
23
|
+
/* @__PURE__ */ jsx("rect", { x: 3, y: 5, width: 18, height: 16, rx: 2.5 }),
|
|
24
|
+
/* @__PURE__ */ jsx("path", { d: "M3 10h18" }),
|
|
25
|
+
/* @__PURE__ */ jsx("path", { d: "M8 3v4" }),
|
|
26
|
+
/* @__PURE__ */ jsx("path", { d: "M16 3v4" })
|
|
27
|
+
] });
|
|
28
|
+
}
|
|
29
|
+
function SwapGlyph() {
|
|
30
|
+
return /* @__PURE__ */ jsxs(Stroke, { children: [
|
|
31
|
+
/* @__PURE__ */ jsx("path", { d: "M4 8h13l-3.5 -3.5" }),
|
|
32
|
+
/* @__PURE__ */ jsx("path", { d: "M20 16h-13l3.5 3.5" })
|
|
33
|
+
] });
|
|
34
|
+
}
|
|
35
|
+
function FunnelGlyph() {
|
|
36
|
+
return /* @__PURE__ */ jsx(Stroke, { children: /* @__PURE__ */ jsx("path", { d: "M4 4h16l-6.5 8v7l-3 -1.5v-5.5z" }) });
|
|
37
|
+
}
|
|
38
|
+
function UpDownGlyph() {
|
|
39
|
+
return /* @__PURE__ */ jsxs(Stroke, { size: AFFORDANCE_SIZE, children: [
|
|
40
|
+
/* @__PURE__ */ jsx("path", { d: "M8 9l4 -4l4 4" }),
|
|
41
|
+
/* @__PURE__ */ jsx("path", { d: "M8 15l4 4l4 -4" })
|
|
42
|
+
] });
|
|
43
|
+
}
|
|
44
|
+
export {
|
|
45
|
+
CalendarGlyph,
|
|
46
|
+
FunnelGlyph,
|
|
47
|
+
SwapGlyph,
|
|
48
|
+
UpDownGlyph
|
|
49
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `basalt-ui/controls` — the Mantine-coupled control tier (`docs/CONTROLS-SPEC.md` §3).
|
|
3
|
+
*
|
|
4
|
+
* Every control here renders at the `ctl` tier from the home that hosts it and owns its own
|
|
5
|
+
* desktop/mobile swap in CSS (law C9). Actions and sync land in wave 3; the filter/tab family
|
|
6
|
+
* follows in wave 4.
|
|
7
|
+
*/
|
|
8
|
+
export { ActionGroup, OverflowMenu, DESKTOP_SECONDARY_MAX, MOBILE_GLOBAL_BAR_MAX, barActionMobile, globalActionMobile, type BarAction, type BarActionItem, type BarActionMenu, type BarActionCustom, type ActionGroupProps, type GlobalAction, type OverflowMenuProps, } from './actions.js';
|
|
9
|
+
export { SyncButton, formatAge, AGE_REFRESH_MS, type SyncButtonProps } from './sync-button.js';
|
|
10
|
+
export { ControlGroup, type ControlGroupProps } from './control-group.js';
|
|
11
|
+
export { FilterSet, type FilterSetProps } from './filter-set.js';
|
|
12
|
+
export { RangeFilter, type RangeCustomPickerProps, type RangeFilterProps } from './range-filter.js';
|
|
13
|
+
export { CompareFilter, COMPARE_VALUES, type CompareFilterProps, type CompareValue, } from './compare-filter.js';
|
|
14
|
+
export { SelectFilter, type FilterOption, type SelectFilterEnumProps, type SelectFilterProps, type SelectFilterStringProps, } from './select-filter.js';
|
|
15
|
+
export { MultiSelectFilter, type MultiSelectFilterProps } from './multi-select-filter.js';
|
|
16
|
+
export { SearchFilter, type SearchFilterProps } from './search-filter.js';
|
|
17
|
+
export { ToggleFilter, type ToggleFilterProps } from './toggle-filter.js';
|
|
18
|
+
export { ViewTabs, type ViewTabsOption, type ViewTabsProps } from './view-tabs.js';
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/controls/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,WAAW,EACX,YAAY,EACZ,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EACf,kBAAkB,EAClB,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,iBAAiB,GACvB,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAA;AAC3F,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAMtE,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7D,OAAO,EAAE,WAAW,EAAE,KAAK,sBAAsB,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAChG,OAAO,EACL,aAAa,EACb,cAAc,EACd,KAAK,kBAAkB,EACvB,KAAK,YAAY,GAClB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,GAC7B,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,iBAAiB,EAAE,KAAK,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AACtF,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACtE,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACtE,OAAO,EAAE,QAAQ,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ActionGroup,
|
|
3
|
+
OverflowMenu,
|
|
4
|
+
DESKTOP_SECONDARY_MAX,
|
|
5
|
+
MOBILE_GLOBAL_BAR_MAX,
|
|
6
|
+
barActionMobile,
|
|
7
|
+
globalActionMobile
|
|
8
|
+
} from "./actions.js";
|
|
9
|
+
import { SyncButton, formatAge, AGE_REFRESH_MS } from "./sync-button.js";
|
|
10
|
+
import { ControlGroup } from "./control-group.js";
|
|
11
|
+
import { FilterSet } from "./filter-set.js";
|
|
12
|
+
import { RangeFilter } from "./range-filter.js";
|
|
13
|
+
import {
|
|
14
|
+
CompareFilter,
|
|
15
|
+
COMPARE_VALUES
|
|
16
|
+
} from "./compare-filter.js";
|
|
17
|
+
import {
|
|
18
|
+
SelectFilter
|
|
19
|
+
} from "./select-filter.js";
|
|
20
|
+
import { MultiSelectFilter } from "./multi-select-filter.js";
|
|
21
|
+
import { SearchFilter } from "./search-filter.js";
|
|
22
|
+
import { ToggleFilter } from "./toggle-filter.js";
|
|
23
|
+
import { ViewTabs } from "./view-tabs.js";
|
|
24
|
+
export {
|
|
25
|
+
AGE_REFRESH_MS,
|
|
26
|
+
ActionGroup,
|
|
27
|
+
COMPARE_VALUES,
|
|
28
|
+
CompareFilter,
|
|
29
|
+
ControlGroup,
|
|
30
|
+
DESKTOP_SECONDARY_MAX,
|
|
31
|
+
FilterSet,
|
|
32
|
+
MOBILE_GLOBAL_BAR_MAX,
|
|
33
|
+
MultiSelectFilter,
|
|
34
|
+
OverflowMenu,
|
|
35
|
+
RangeFilter,
|
|
36
|
+
SearchFilter,
|
|
37
|
+
SelectFilter,
|
|
38
|
+
SyncButton,
|
|
39
|
+
ToggleFilter,
|
|
40
|
+
ViewTabs,
|
|
41
|
+
barActionMobile,
|
|
42
|
+
formatAge,
|
|
43
|
+
globalActionMobile
|
|
44
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { FieldHandle, MultiField } from '../state.js';
|
|
3
|
+
import type { FilterOption } from './select-filter.js';
|
|
4
|
+
export type MultiSelectFilterProps<T extends string> = {
|
|
5
|
+
readonly field: FieldHandle<MultiField<T>>;
|
|
6
|
+
readonly label: string;
|
|
7
|
+
readonly icon?: ReactNode;
|
|
8
|
+
/**
|
|
9
|
+
* The plural noun in the `3 channels` count.
|
|
10
|
+
*
|
|
11
|
+
* @default the label, lowercased
|
|
12
|
+
*/
|
|
13
|
+
readonly noun?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Overrides `field.options` at render — a runtime catalogue whose labels carry live data
|
|
16
|
+
* (`web · 1.2k`). The VALUES still belong to the field: a multi field is a closed set, so this
|
|
17
|
+
* relabels and reorders rows, it does not open new ones. Unlike `SelectFilter` there is no
|
|
18
|
+
* string-field shape to make it required.
|
|
19
|
+
*/
|
|
20
|
+
readonly options?: readonly FilterOption[];
|
|
21
|
+
};
|
|
22
|
+
export declare function MultiSelectFilter<T extends string>({ field, label, icon, noun, options: optionsProp, }: MultiSelectFilterProps<T>): ReactNode;
|
|
23
|
+
//# sourceMappingURL=multi-select-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-select-filter.d.ts","sourceRoot":"","sources":["../../src/controls/multi-select-filter.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAMnD,MAAM,MAAM,sBAAsB,CAAC,CAAC,SAAS,MAAM,IAAI;IACrD,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAA;IACzB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,YAAY,EAAE,CAAA;CAC3C,CAAA;AAED,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,EAAE,EAClD,KAAK,EACL,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,OAAO,EAAE,WAAW,GACrB,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAAG,SAAS,CAsFvC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, Checkbox, Stack } from "@mantine/core";
|
|
3
|
+
import classes from "./controls.module.css";
|
|
4
|
+
import { useFilterRegistration, useFilterSurface } from "./filter-context.js";
|
|
5
|
+
import { FilterPill } from "./filter-pill.js";
|
|
6
|
+
import { SheetField, SheetOptionList, useControlName } from "./filter-sheet.js";
|
|
7
|
+
function MultiSelectFilter({
|
|
8
|
+
field,
|
|
9
|
+
label,
|
|
10
|
+
icon,
|
|
11
|
+
noun,
|
|
12
|
+
options: optionsProp
|
|
13
|
+
}) {
|
|
14
|
+
const [value, setValue] = field.use();
|
|
15
|
+
const surface = useFilterSurface();
|
|
16
|
+
const isDefault = field.isDefault(value);
|
|
17
|
+
useFilterRegistration(!isDefault, () => {
|
|
18
|
+
setValue(field.fallback);
|
|
19
|
+
});
|
|
20
|
+
const options = optionsProp ?? field.options;
|
|
21
|
+
const inSheet = surface === "sheet";
|
|
22
|
+
const carriesInformation = value.length > 0 && value.length < options.length;
|
|
23
|
+
const { labelId } = useControlName(label, inSheet);
|
|
24
|
+
if (inSheet) {
|
|
25
|
+
return /* @__PURE__ */ jsx(SheetField, { label, labelId, children: /* @__PURE__ */ jsx(
|
|
26
|
+
SheetOptionList,
|
|
27
|
+
{
|
|
28
|
+
mode: "multi",
|
|
29
|
+
labelId,
|
|
30
|
+
selected: value,
|
|
31
|
+
options,
|
|
32
|
+
onToggle: (next) => {
|
|
33
|
+
const has = value.includes(next);
|
|
34
|
+
setValue(has ? value.filter((v) => v !== next) : [...value, next]);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
) });
|
|
38
|
+
}
|
|
39
|
+
const body = (
|
|
40
|
+
// Mantine's own `label`, for the same reason `enum-filter.tsx` uses it — `Checkbox.Group`
|
|
41
|
+
// overwrites any `aria-label`/`aria-labelledby` with its own. Named from `label`, never from the
|
|
42
|
+
// pill text, which reads `3 channels` and describes the selection rather than the filter.
|
|
43
|
+
/* @__PURE__ */ jsxs(
|
|
44
|
+
Checkbox.Group,
|
|
45
|
+
{
|
|
46
|
+
label,
|
|
47
|
+
classNames: { label: classes.groupLabel },
|
|
48
|
+
value: [...value],
|
|
49
|
+
onChange: (next) => {
|
|
50
|
+
setValue(next);
|
|
51
|
+
},
|
|
52
|
+
children: [
|
|
53
|
+
/* @__PURE__ */ jsx(Stack, { gap: 2, children: options.map((option) => /* @__PURE__ */ jsx(
|
|
54
|
+
Checkbox,
|
|
55
|
+
{
|
|
56
|
+
value: option.value,
|
|
57
|
+
label: option.label,
|
|
58
|
+
...option.disabled === true && { disabled: true }
|
|
59
|
+
},
|
|
60
|
+
option.value
|
|
61
|
+
)) }),
|
|
62
|
+
!isDefault && /* @__PURE__ */ jsx(
|
|
63
|
+
Button,
|
|
64
|
+
{
|
|
65
|
+
variant: "subtle",
|
|
66
|
+
size: "ctl",
|
|
67
|
+
onClick: () => {
|
|
68
|
+
setValue(field.fallback);
|
|
69
|
+
},
|
|
70
|
+
children: "Clear"
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
);
|
|
77
|
+
return /* @__PURE__ */ jsx(
|
|
78
|
+
FilterPill,
|
|
79
|
+
{
|
|
80
|
+
label: carriesInformation ? `${value.length} ${noun ?? label.toLowerCase()}` : label,
|
|
81
|
+
active: !isDefault,
|
|
82
|
+
...icon !== void 0 && { icon },
|
|
83
|
+
children: /* @__PURE__ */ jsx("div", { className: classes.optionList, children: body })
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
export {
|
|
88
|
+
MultiSelectFilter
|
|
89
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"peer-boundary.test.d.ts","sourceRoot":"","sources":["../../src/controls/peer-boundary.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"props-contract.test.d.ts","sourceRoot":"","sources":["../../src/controls/props-contract.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from 'react';
|
|
2
|
+
import type { FieldHandle, RangeField } from '../state.js';
|
|
3
|
+
/**
|
|
4
|
+
* The contract a custom-range picker satisfies — `basalt-ui/controls-dates`' `DateRangePicker`, or a
|
|
5
|
+
* consumer's own. Dates are ISO (`YYYY-MM-DD`), the same shape `field.range` stores and
|
|
6
|
+
* `toWindow()` emits, so the picker never learns the store's encoding.
|
|
7
|
+
*/
|
|
8
|
+
export type RangeCustomPickerProps = {
|
|
9
|
+
readonly value: {
|
|
10
|
+
readonly from?: string | undefined;
|
|
11
|
+
readonly to?: string | undefined;
|
|
12
|
+
};
|
|
13
|
+
readonly onChange: (next: {
|
|
14
|
+
from: string;
|
|
15
|
+
to: string;
|
|
16
|
+
}) => void;
|
|
17
|
+
};
|
|
18
|
+
export type RangeFilterProps<P extends string> = {
|
|
19
|
+
readonly field: FieldHandle<RangeField<P>>;
|
|
20
|
+
/** Leading pill icon. Defaults to a calendar glyph — a range filter reads as a date control, so
|
|
21
|
+
* the glyph is part of the control's identity, not a per-call-site decision. */
|
|
22
|
+
readonly icon?: ReactNode;
|
|
23
|
+
/** Rendered when the field declares `custom: true`. Omitted → presets only. */
|
|
24
|
+
readonly customPicker?: ComponentType<RangeCustomPickerProps>;
|
|
25
|
+
/** Sheet-form heading. The pill's own label is the VALUE, which is what a bar reads. @default 'Range' */
|
|
26
|
+
readonly label?: string;
|
|
27
|
+
};
|
|
28
|
+
export declare function RangeFilter<P extends string>({ field, icon, customPicker, label, }: RangeFilterProps<P>): ReactNode;
|
|
29
|
+
//# sourceMappingURL=range-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"range-filter.d.ts","sourceRoot":"","sources":["../../src/controls/range-filter.tsx"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAc,MAAM,UAAU,CAAA;AAYnE;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAA;IACxF,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CAChE,CAAA;AAED,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,MAAM,IAAI;IAC/C,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA;IAC1C;qFACiF;IACjF,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAA;IACzB,+EAA+E;IAC/E,QAAQ,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC,sBAAsB,CAAC,CAAA;IAC7D,yGAAyG;IACzG,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,EAC5C,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,KAAe,GAChB,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,SAAS,CA8FjC"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { SegmentedControl, Stack } from "@mantine/core";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import classes from "./controls.module.css";
|
|
5
|
+
import { useFilterRegistration, useFilterSurface } from "./filter-context.js";
|
|
6
|
+
import { FilterPill } from "./filter-pill.js";
|
|
7
|
+
import { CalendarGlyph } from "./glyphs.js";
|
|
8
|
+
import { SheetDisclosure, SheetField, SheetOptionList, useControlName } from "./filter-sheet.js";
|
|
9
|
+
const VERTICAL_FROM = 5;
|
|
10
|
+
const NUMERIC_LABEL = /^\d/;
|
|
11
|
+
function RangeFilter({
|
|
12
|
+
field,
|
|
13
|
+
icon,
|
|
14
|
+
customPicker,
|
|
15
|
+
label = "Range"
|
|
16
|
+
}) {
|
|
17
|
+
const [value, setValue] = field.use();
|
|
18
|
+
const surface = useFilterSurface();
|
|
19
|
+
const isDefault = field.isDefault(value);
|
|
20
|
+
useFilterRegistration(!isDefault, () => {
|
|
21
|
+
setValue(field.fallback);
|
|
22
|
+
});
|
|
23
|
+
const options = field.options;
|
|
24
|
+
const presets = options.filter((option) => option.value !== "custom");
|
|
25
|
+
const allowsCustom = options.length !== presets.length;
|
|
26
|
+
const numeric = presets.length > 0 && presets.every((option) => NUMERIC_LABEL.test(option.label));
|
|
27
|
+
const inSheet = surface === "sheet";
|
|
28
|
+
const Picker = customPicker;
|
|
29
|
+
const { labelId, nameProps } = useControlName(label, inSheet);
|
|
30
|
+
const [customOpen, setCustomOpen] = useState(value.preset === "custom");
|
|
31
|
+
const showPicker = allowsCustom && Picker !== void 0;
|
|
32
|
+
if (inSheet) {
|
|
33
|
+
return /* @__PURE__ */ jsxs(SheetField, { label, labelId, children: [
|
|
34
|
+
/* @__PURE__ */ jsx(
|
|
35
|
+
SheetOptionList,
|
|
36
|
+
{
|
|
37
|
+
mode: "single",
|
|
38
|
+
labelId,
|
|
39
|
+
selected: [value.preset],
|
|
40
|
+
options: presets.map((option) => ({ value: option.value, label: option.label })),
|
|
41
|
+
onToggle: (next) => {
|
|
42
|
+
setCustomOpen(false);
|
|
43
|
+
setValue({ preset: next });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
),
|
|
47
|
+
showPicker && /* @__PURE__ */ jsx(
|
|
48
|
+
SheetDisclosure,
|
|
49
|
+
{
|
|
50
|
+
label: "Custom range\u2026",
|
|
51
|
+
expanded: customOpen,
|
|
52
|
+
onToggle: () => {
|
|
53
|
+
setCustomOpen((open) => !open);
|
|
54
|
+
},
|
|
55
|
+
children: /* @__PURE__ */ jsx(
|
|
56
|
+
Picker,
|
|
57
|
+
{
|
|
58
|
+
value: { from: value.from, to: value.to },
|
|
59
|
+
onChange: ({ from, to }) => {
|
|
60
|
+
setValue({ preset: "custom", from, to });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
] });
|
|
67
|
+
}
|
|
68
|
+
const body = /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
|
|
69
|
+
/* @__PURE__ */ jsx(
|
|
70
|
+
SegmentedControl,
|
|
71
|
+
{
|
|
72
|
+
...nameProps,
|
|
73
|
+
size: "ctl",
|
|
74
|
+
orientation: presets.length >= VERTICAL_FROM ? "vertical" : "horizontal",
|
|
75
|
+
value: value.preset,
|
|
76
|
+
data: presets.map((option) => ({ value: option.value, label: option.label })),
|
|
77
|
+
...numeric && { "data-numeric": true },
|
|
78
|
+
onChange: (next) => {
|
|
79
|
+
setValue({ preset: next });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
),
|
|
83
|
+
showPicker && /* @__PURE__ */ jsx(
|
|
84
|
+
Picker,
|
|
85
|
+
{
|
|
86
|
+
value: { from: value.from, to: value.to },
|
|
87
|
+
onChange: ({ from, to }) => {
|
|
88
|
+
setValue({ preset: "custom", from, to });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
] });
|
|
93
|
+
return /* @__PURE__ */ jsx(
|
|
94
|
+
FilterPill,
|
|
95
|
+
{
|
|
96
|
+
label: rangeLabel(value, options),
|
|
97
|
+
active: !isDefault,
|
|
98
|
+
numeric: numeric && value.preset !== "custom",
|
|
99
|
+
icon: icon ?? /* @__PURE__ */ jsx(CalendarGlyph, {}),
|
|
100
|
+
children: /* @__PURE__ */ jsx("div", { className: classes.optionList, children: body })
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
const DAY_FORMAT = new Intl.DateTimeFormat("en-US", {
|
|
105
|
+
month: "short",
|
|
106
|
+
day: "numeric",
|
|
107
|
+
// ISO date strings parse as UTC midnight; formatting them in the runtime's zone would render the
|
|
108
|
+
// previous day west of Greenwich — and differently on server than on client (a hydration
|
|
109
|
+
// mismatch), the same hazard `formatArticleDate` avoids.
|
|
110
|
+
timeZone: "UTC"
|
|
111
|
+
});
|
|
112
|
+
function rangeLabel(value, options) {
|
|
113
|
+
if (value.preset === "custom" && value.from !== void 0 && value.to !== void 0) {
|
|
114
|
+
return `${DAY_FORMAT.format(new Date(value.from))} \u2013 ${DAY_FORMAT.format(new Date(value.to))}`;
|
|
115
|
+
}
|
|
116
|
+
return options.find((option) => option.value === value.preset)?.label ?? value.preset;
|
|
117
|
+
}
|
|
118
|
+
export {
|
|
119
|
+
RangeFilter
|
|
120
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { FieldHandle, StringField } from '../state.js';
|
|
3
|
+
export type SearchFilterProps = {
|
|
4
|
+
readonly field: FieldHandle<StringField>;
|
|
5
|
+
readonly placeholder?: string;
|
|
6
|
+
/** Sheet-form heading. @default 'Search' */
|
|
7
|
+
readonly label?: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function SearchFilter({ field, placeholder, label, }: SearchFilterProps): ReactNode;
|
|
10
|
+
//# sourceMappingURL=search-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-filter.d.ts","sourceRoot":"","sources":["../../src/controls/search-filter.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAOxD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,CAAA;IACxC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,4CAA4C;IAC5C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,WAAW,EACX,KAAgB,GACjB,EAAE,iBAAiB,GAAG,SAAS,CAiC/B"}
|