basalt-ui 1.26.0 → 1.28.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 +23 -23
- package/CHANGELOG.md +67 -0
- package/MIGRATING.md +896 -37
- package/README.md +33 -3
- package/agent/rules/basalt-batteries.md +25 -10
- package/agent/rules/basalt-controls.md +73 -43
- package/agent/rules/basalt-mantine.md +17 -6
- package/agent/rules/basalt-state.md +5 -0
- package/agent/rules/basalt-tokens.md +1 -1
- package/configs/oxlint-plugin.js +1286 -73
- package/configs/oxlint-plugin.test.ts +1338 -29
- package/configs/oxlint.json +12 -5
- package/dist/agent/coalesce.d.ts.map +1 -1
- package/dist/agent/coalesce.js +2 -1
- package/dist/agent/merge.d.ts.map +1 -1
- package/dist/agent/merge.js +2 -1
- package/dist/agent/part-list.d.ts.map +1 -1
- package/dist/agent/part-list.js +2 -0
- package/dist/agent/stick-to-bottom.d.ts +3 -6
- package/dist/agent/stick-to-bottom.d.ts.map +1 -1
- package/dist/agent/stick-to-bottom.js +6 -3
- package/dist/agent-chat/composer.d.ts +6 -2
- package/dist/agent-chat/composer.d.ts.map +1 -1
- package/dist/agent-chat/composer.js +49 -33
- package/dist/agent-chat/relative-time.d.ts +0 -9
- package/dist/agent-chat/relative-time.d.ts.map +1 -1
- package/dist/agent-chat/relative-time.js +2 -1
- package/dist/agent-chat/thread-detail-panel.d.ts +3 -2
- package/dist/agent-chat/thread-detail-panel.d.ts.map +1 -1
- package/dist/agent-chat/thread-detail-panel.js +8 -5
- package/dist/agent-chat/thread-feed-row.d.ts +2 -1
- package/dist/agent-chat/thread-feed-row.d.ts.map +1 -1
- package/dist/agent-chat/thread-feed-row.js +92 -92
- package/dist/agent-chat/thread-feed.d.ts +3 -2
- package/dist/agent-chat/thread-feed.d.ts.map +1 -1
- package/dist/agent-chat/thread-feed.js +8 -3
- package/dist/agent-chat/thread-message.d.ts +4 -3
- package/dist/agent-chat/thread-message.d.ts.map +1 -1
- package/dist/agent-chat/thread-message.js +70 -31
- package/dist/agent-chat/thread-outcome-card.d.ts +3 -2
- package/dist/agent-chat/thread-outcome-card.d.ts.map +1 -1
- package/dist/agent-chat/thread-outcome-card.js +9 -2
- package/dist/agent-chat/thread-workspace.d.ts +7 -2
- package/dist/agent-chat/thread-workspace.d.ts.map +1 -1
- package/dist/agent-chat/thread-workspace.js +62 -29
- package/dist/agent-chat/tool-chip.d.ts +3 -2
- package/dist/agent-chat/tool-chip.d.ts.map +1 -1
- package/dist/agent-chat/tool-chip.js +7 -2
- package/dist/charts/hooks/useBandPlot.d.ts.map +1 -1
- package/dist/charts/hooks/useBandPlot.js +15 -8
- package/dist/charts/index.d.ts +8 -7
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/index.js +45 -5
- package/dist/charts/kinds/BandStrip.d.ts +10 -7
- package/dist/charts/kinds/BandStrip.d.ts.map +1 -1
- package/dist/charts/kinds/BandStrip.js +12 -7
- package/dist/charts/kinds/Bars.d.ts +12 -6
- package/dist/charts/kinds/Bars.d.ts.map +1 -1
- package/dist/charts/kinds/Bars.js +37 -11
- package/dist/charts/kinds/Donut.d.ts +6 -5
- package/dist/charts/kinds/Donut.d.ts.map +1 -1
- package/dist/charts/kinds/Donut.js +19 -2
- package/dist/charts/kinds/DualPanel.d.ts +6 -5
- package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
- package/dist/charts/kinds/DualPanel.js +36 -8
- package/dist/charts/kinds/Heatmap.d.ts +10 -6
- package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
- package/dist/charts/kinds/Heatmap.js +58 -31
- package/dist/charts/kinds/MirroredBars.d.ts +6 -5
- package/dist/charts/kinds/MirroredBars.d.ts.map +1 -1
- package/dist/charts/kinds/MirroredBars.js +9 -2
- package/dist/charts/kinds/MultiLine.d.ts +11 -6
- package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
- package/dist/charts/kinds/MultiLine.js +24 -15
- package/dist/charts/kinds/StackedArea.d.ts +18 -8
- package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
- package/dist/charts/kinds/StackedArea.js +29 -4
- package/dist/charts/kinds/ZonedLine.d.ts +12 -8
- package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
- package/dist/charts/kinds/ZonedLine.js +33 -23
- package/dist/charts/layout/auto-margin.d.ts +65 -8
- package/dist/charts/layout/auto-margin.d.ts.map +1 -1
- package/dist/charts/layout/auto-margin.js +45 -10
- package/dist/charts/layout/log-ticks.d.ts +27 -0
- package/dist/charts/layout/log-ticks.d.ts.map +1 -0
- package/dist/charts/layout/log-ticks.js +42 -0
- package/dist/charts/primitives/Axes.d.ts +11 -2
- package/dist/charts/primitives/Axes.d.ts.map +1 -1
- package/dist/charts/primitives/Axes.js +15 -5
- package/dist/charts/primitives/CartesianChart.d.ts +45 -7
- package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
- package/dist/charts/primitives/CartesianChart.js +86 -28
- package/dist/charts/primitives/ChartCard.d.ts +27 -2
- package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.js +37 -17
- package/dist/charts/primitives/ChartFrame.d.ts +30 -4
- package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.js +45 -15
- package/dist/charts/primitives/ChartLegend.d.ts +13 -2
- package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
- package/dist/charts/primitives/ChartLegend.js +33 -13
- package/dist/charts/primitives/ChartPending.d.ts +77 -4
- package/dist/charts/primitives/ChartPending.d.ts.map +1 -1
- package/dist/charts/primitives/ChartPending.js +100 -6
- package/dist/charts/primitives/ChartTooltip.d.ts +7 -0
- package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
- package/dist/charts/primitives/ChartTooltip.js +18 -5
- package/dist/charts/primitives/Crosshair.d.ts +3 -0
- package/dist/charts/primitives/Crosshair.d.ts.map +1 -1
- package/dist/charts/primitives/Crosshair.js +3 -1
- package/dist/charts/primitives/chart-frame-layout.d.ts +95 -0
- package/dist/charts/primitives/chart-frame-layout.d.ts.map +1 -0
- package/dist/charts/primitives/chart-frame-layout.js +89 -0
- package/dist/charts/primitives/chart-tier.d.ts +16 -0
- package/dist/charts/primitives/chart-tier.d.ts.map +1 -0
- package/dist/charts/primitives/chart-tier.js +20 -0
- package/dist/charts/series.d.ts +55 -1
- package/dist/charts/series.d.ts.map +1 -1
- package/dist/charts/series.js +27 -1
- package/dist/charts/sparklines/BarSparkline.d.ts +3 -3
- package/dist/charts/sparklines/BarSparkline.d.ts.map +1 -1
- package/dist/charts/sparklines/BarSparkline.js +9 -3
- package/dist/charts/sparklines/LineSparkline.d.ts +3 -3
- package/dist/charts/sparklines/LineSparkline.d.ts.map +1 -1
- package/dist/charts/sparklines/LineSparkline.js +15 -3
- package/dist/charts/sparklines/index.d.ts +2 -2
- package/dist/charts/sparklines/index.d.ts.map +1 -1
- package/dist/charts/utils/format.d.ts +92 -2
- package/dist/charts/utils/format.d.ts.map +1 -1
- package/dist/charts/utils/format.js +64 -1
- package/dist/charts/utils/ticks.d.ts +81 -2
- package/dist/charts/utils/ticks.d.ts.map +1 -1
- package/dist/charts/utils/ticks.js +54 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/commands/ShortcutsHelp.d.ts +3 -2
- package/dist/commands/ShortcutsHelp.d.ts.map +1 -1
- package/dist/commands/ShortcutsHelp.js +8 -2
- package/dist/commands/define-commands.d.ts.map +1 -1
- package/dist/commands/define-commands.js +2 -2
- package/dist/commands/define-overlays.d.ts +102 -0
- package/dist/commands/define-overlays.d.ts.map +1 -1
- package/dist/commands/define-overlays.js +102 -2
- package/dist/commands/index.d.ts +10 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +3 -1
- package/dist/commands/overlays-mount.d.ts +13 -6
- package/dist/commands/overlays-mount.d.ts.map +1 -1
- package/dist/commands/overlays-mount.js +23 -14
- package/dist/common/errors.d.ts +34 -0
- package/dist/common/errors.d.ts.map +1 -0
- package/dist/common/errors.js +36 -0
- package/dist/common/index.d.ts +15 -0
- package/dist/common/index.d.ts.map +1 -0
- package/dist/common/index.js +29 -0
- package/dist/common/props.d.ts +63 -0
- package/dist/common/props.d.ts.map +1 -0
- package/dist/common/props.js +6 -0
- package/dist/common/query-state-like.d.ts +43 -0
- package/dist/common/query-state-like.d.ts.map +1 -0
- package/dist/common/query-state-like.js +20 -0
- package/dist/common/refs.d.ts +29 -0
- package/dist/common/refs.d.ts.map +1 -0
- package/dist/common/refs.js +29 -0
- package/dist/common/scroll-parent.d.ts +23 -0
- package/dist/common/scroll-parent.d.ts.map +1 -0
- package/dist/common/scroll-parent.js +21 -0
- package/dist/common/validate.d.ts +38 -0
- package/dist/common/validate.d.ts.map +1 -0
- package/dist/common/validate.js +36 -0
- package/dist/connectivity/connectivity-indicator.d.ts +3 -1
- package/dist/connectivity/connectivity-indicator.d.ts.map +1 -1
- package/dist/connectivity/connectivity-indicator.js +3 -1
- package/dist/connectivity/connectivity-provider.d.ts.map +1 -1
- package/dist/connectivity/connectivity-provider.js +4 -1
- package/dist/connectivity/index.d.ts +1 -0
- package/dist/connectivity/index.d.ts.map +1 -1
- package/dist/connectivity/types.d.ts +2 -2
- package/dist/connectivity/types.d.ts.map +1 -1
- package/dist/connectivity/use-connectivity.d.ts.map +1 -1
- package/dist/content/article-layout.module.css +11 -4
- package/dist/content/callout.d.ts +8 -4
- package/dist/content/callout.d.ts.map +1 -1
- package/dist/content/callout.js +15 -8
- package/dist/content/guide.d.ts +5 -4
- package/dist/content/guide.d.ts.map +1 -1
- package/dist/content/guide.js +13 -4
- package/dist/content/heading-anchor.d.ts +24 -2
- package/dist/content/heading-anchor.d.ts.map +1 -1
- package/dist/content/heading-anchor.js +23 -9
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/markdown.d.ts.map +1 -1
- package/dist/content/markdown.js +2 -0
- package/dist/content/prose.module.css +9 -7
- package/dist/content/reading-progress.d.ts +10 -4
- package/dist/content/reading-progress.d.ts.map +1 -1
- package/dist/content/reading-progress.js +26 -10
- package/dist/content/slug.d.ts +4 -0
- package/dist/content/slug.d.ts.map +1 -1
- package/dist/content/toc.d.ts +11 -0
- package/dist/content/toc.d.ts.map +1 -1
- package/dist/content/toc.js +23 -10
- package/dist/controls/actions.d.ts +49 -6
- package/dist/controls/actions.d.ts.map +1 -1
- package/dist/controls/actions.js +39 -8
- package/dist/controls/compare-filter.d.ts +3 -2
- package/dist/controls/compare-filter.d.ts.map +1 -1
- package/dist/controls/compare-filter.js +17 -2
- package/dist/controls/control-group.d.ts +3 -3
- package/dist/controls/control-group.d.ts.map +1 -1
- package/dist/controls/control-group.js +9 -2
- package/dist/controls/controls.module.css +169 -186
- package/dist/controls/controls.module.css.d.ts +9 -11
- package/dist/controls/enum-filter.d.ts +13 -3
- package/dist/controls/enum-filter.d.ts.map +1 -1
- package/dist/controls/enum-filter.js +32 -15
- package/dist/controls/filter-context.d.ts +15 -6
- package/dist/controls/filter-context.d.ts.map +1 -1
- package/dist/controls/filter-pill.d.ts +3 -3
- package/dist/controls/filter-pill.d.ts.map +1 -1
- package/dist/controls/filter-pill.js +4 -1
- package/dist/controls/filter-set.d.ts +3 -2
- package/dist/controls/filter-set.d.ts.map +1 -1
- package/dist/controls/filter-set.js +66 -41
- package/dist/controls/filter-sheet.d.ts +20 -80
- package/dist/controls/filter-sheet.d.ts.map +1 -1
- package/dist/controls/filter-sheet.js +9 -95
- package/dist/controls/index.d.ts +3 -0
- package/dist/controls/index.d.ts.map +1 -1
- package/dist/controls/index.js +6 -0
- package/dist/controls/multi-select-filter.d.ts +18 -2
- package/dist/controls/multi-select-filter.d.ts.map +1 -1
- package/dist/controls/multi-select-filter.js +124 -22
- package/dist/controls/number-filter.d.ts +28 -0
- package/dist/controls/number-filter.d.ts.map +1 -0
- package/dist/controls/number-filter.js +172 -0
- package/dist/controls/panel-row.d.ts +132 -0
- package/dist/controls/panel-row.d.ts.map +1 -0
- package/dist/controls/panel-row.js +173 -0
- package/dist/controls/panel-row.module.css +86 -0
- package/dist/controls/panel-row.module.css.d.ts +13 -0
- package/dist/controls/range-filter.d.ts +20 -5
- package/dist/controls/range-filter.d.ts.map +1 -1
- package/dist/controls/range-filter.js +75 -38
- package/dist/controls/search-filter.d.ts +3 -2
- package/dist/controls/search-filter.d.ts.map +1 -1
- package/dist/controls/search-filter.js +26 -11
- package/dist/controls/select-filter.d.ts +2 -1
- package/dist/controls/select-filter.d.ts.map +1 -1
- package/dist/controls/select-filter.js +4 -0
- package/dist/controls/slider-control.d.ts +36 -0
- package/dist/controls/slider-control.d.ts.map +1 -0
- package/dist/controls/slider-control.js +64 -0
- package/dist/controls/sync-button.d.ts +3 -2
- package/dist/controls/sync-button.d.ts.map +1 -1
- package/dist/controls/sync-button.js +16 -3
- package/dist/controls/toggle-filter.d.ts +3 -2
- package/dist/controls/toggle-filter.d.ts.map +1 -1
- package/dist/controls/toggle-filter.js +29 -12
- package/dist/controls/view-tabs.d.ts +3 -2
- package/dist/controls/view-tabs.d.ts.map +1 -1
- package/dist/controls/view-tabs.js +43 -11
- package/dist/controls-dates/date-range-picker.d.ts +1 -1
- package/dist/controls-dates/date-range-picker.d.ts.map +1 -1
- package/dist/controls-dates/date-range-picker.js +6 -1
- package/dist/dashboard/delta-badge.d.ts +5 -1
- package/dist/dashboard/delta-badge.d.ts.map +1 -1
- package/dist/dashboard/empty-state.d.ts +10 -3
- package/dist/dashboard/empty-state.d.ts.map +1 -1
- package/dist/dashboard/empty-state.js +37 -6
- package/dist/dashboard/index.d.ts +6 -4
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +10 -2
- package/dist/dashboard/query-state.d.ts +48 -27
- package/dist/dashboard/query-state.d.ts.map +1 -1
- package/dist/dashboard/query-state.js +111 -57
- package/dist/dashboard/settings-section.d.ts +19 -9
- package/dist/dashboard/settings-section.d.ts.map +1 -1
- package/dist/dashboard/settings-section.js +74 -25
- package/dist/dashboard/stat-card.d.ts +76 -19
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +78 -38
- package/dist/dashboard/stat-card.module.css +112 -26
- package/dist/dashboard/stat-card.module.css.d.ts +5 -0
- package/dist/dashboard/stat-group.d.ts +41 -0
- package/dist/dashboard/stat-group.d.ts.map +1 -0
- package/dist/dashboard/stat-group.js +30 -0
- package/dist/dashboard/stat-group.module.css +66 -0
- package/dist/dashboard/stat-group.module.css.d.ts +8 -0
- package/dist/dashboard/widget-grid.d.ts +27 -0
- package/dist/dashboard/widget-grid.d.ts.map +1 -0
- package/dist/dashboard/widget-grid.js +41 -0
- package/dist/dashboard/widget-grid.module.css +53 -0
- package/dist/dashboard/widget-grid.module.css.d.ts +9 -0
- package/dist/data/data-table.d.ts +151 -12
- package/dist/data/data-table.d.ts.map +1 -1
- package/dist/data/data-table.js +392 -138
- package/dist/data/data-table.module.css +64 -0
- package/dist/data/data-table.module.css.d.ts +11 -0
- package/dist/data/index.d.ts +2 -2
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +24 -2
- package/dist/data/query-branch.d.ts +28 -0
- package/dist/data/query-branch.d.ts.map +1 -0
- package/dist/data/query-branch.js +10 -0
- package/dist/data/table.d.ts +2 -2
- package/dist/data/table.d.ts.map +1 -1
- package/dist/data/virtual-list.d.ts +65 -6
- package/dist/data/virtual-list.d.ts.map +1 -1
- package/dist/data/virtual-list.js +89 -38
- package/dist/data/virtual.d.ts +1 -1
- package/dist/data/virtual.d.ts.map +1 -1
- package/dist/forms/FormErrorSummary.d.ts +4 -3
- package/dist/forms/FormErrorSummary.d.ts.map +1 -1
- package/dist/forms/FormErrorSummary.js +6 -4
- package/dist/forms/create-form.d.ts +2 -2
- package/dist/forms/field.d.ts +46 -6
- package/dist/forms/field.d.ts.map +1 -1
- package/dist/forms/field.js +10 -5
- package/dist/forms/form-layout.d.ts +142 -0
- package/dist/forms/form-layout.d.ts.map +1 -0
- package/dist/forms/form-layout.js +110 -0
- package/dist/forms/form-layout.module.css +145 -0
- package/dist/forms/form-layout.module.css.d.ts +20 -0
- package/dist/forms/form-state.d.ts +42 -0
- package/dist/forms/form-state.d.ts.map +1 -0
- package/dist/forms/form-state.js +26 -0
- package/dist/forms/index.d.ts +22 -6
- package/dist/forms/index.d.ts.map +1 -1
- package/dist/forms/index.js +19 -3
- package/dist/forms/use-field-array.d.ts +60 -0
- package/dist/forms/use-field-array.d.ts.map +1 -0
- package/dist/forms/use-field-array.js +20 -0
- package/dist/forms/use-form-draft.d.ts +25 -10
- package/dist/forms/use-form-draft.d.ts.map +1 -1
- package/dist/forms/use-form-draft.js +55 -18
- package/dist/forms/use-form-submit.d.ts +88 -0
- package/dist/forms/use-form-submit.d.ts.map +1 -0
- package/dist/forms/use-form-submit.js +63 -0
- package/dist/guard/index.d.ts +1 -1
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +27 -11
- package/dist/index.d.ts +10 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -4
- package/dist/nav/types.d.ts +15 -6
- package/dist/nav/types.d.ts.map +1 -1
- package/dist/notifications/bell.d.ts +3 -2
- package/dist/notifications/bell.d.ts.map +1 -1
- package/dist/notifications/bell.js +73 -56
- package/dist/notifications/center.d.ts +15 -4
- package/dist/notifications/center.d.ts.map +1 -1
- package/dist/notifications/center.js +5 -2
- package/dist/notifications/define-notifications.d.ts +56 -14
- package/dist/notifications/define-notifications.d.ts.map +1 -1
- package/dist/notifications/define-notifications.js +2 -1
- package/dist/notifications/index.d.ts +6 -2
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +5 -1
- package/dist/notifications/mount-guard.d.ts +8 -0
- package/dist/notifications/mount-guard.d.ts.map +1 -0
- package/dist/notifications/mount-guard.js +19 -0
- package/dist/notifications/notify.d.ts +58 -1
- package/dist/notifications/notify.d.ts.map +1 -1
- package/dist/notifications/notify.js +46 -2
- package/dist/notifications/overlay.d.ts.map +1 -1
- package/dist/notifications/overlay.js +2 -0
- package/dist/notifications/store.d.ts +7 -0
- package/dist/notifications/store.d.ts.map +1 -1
- package/dist/provider/index.d.ts +37 -5
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +66 -28
- package/dist/provider/lab-theme.d.ts.map +1 -1
- package/dist/provider/lab-theme.js +2 -1
- package/dist/query/devtools.d.ts.map +1 -1
- package/dist/query/devtools.js +1 -0
- package/dist/router-tanstack/field.d.ts +1 -1
- package/dist/router-tanstack/field.d.ts.map +1 -1
- package/dist/router-tanstack/index.d.ts +1 -1
- package/dist/router-tanstack/index.d.ts.map +1 -1
- package/dist/router-tanstack/search-store.d.ts +4 -1
- package/dist/router-tanstack/search-store.d.ts.map +1 -1
- package/dist/router-tanstack/search-store.js +92 -8
- package/dist/router-tanstack/use-nav.d.ts.map +1 -1
- package/dist/router-tanstack/use-nav.js +1 -1
- package/dist/section/index.d.ts +1 -1
- package/dist/section/index.d.ts.map +1 -1
- package/dist/section/section.d.ts +36 -2
- package/dist/section/section.d.ts.map +1 -1
- package/dist/section/section.js +49 -57
- package/dist/section/section.module.css +11 -0
- package/dist/shell/account-types.d.ts +5 -2
- package/dist/shell/account-types.d.ts.map +1 -1
- package/dist/shell/app-brand.d.ts +13 -0
- package/dist/shell/app-brand.d.ts.map +1 -0
- package/dist/shell/app-brand.js +94 -0
- package/dist/shell/app-brand.module.css +109 -0
- package/dist/shell/app-brand.module.css.d.ts +14 -0
- package/dist/shell/app-breadcrumbs.d.ts +4 -2
- package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
- package/dist/shell/app-breadcrumbs.js +33 -22
- package/dist/shell/app-header.module.css +10 -5
- package/dist/shell/app-main.module.css +107 -0
- package/dist/shell/app-main.module.css.d.ts +11 -0
- package/dist/shell/app-mobile-nav.d.ts +3 -2
- package/dist/shell/app-mobile-nav.d.ts.map +1 -1
- package/dist/shell/app-mobile-nav.js +29 -15
- package/dist/shell/app-mobile-nav.module.css +48 -27
- package/dist/shell/app-mobile-nav.module.css.d.ts +0 -1
- package/dist/shell/app-sidebar-account.d.ts +2 -1
- package/dist/shell/app-sidebar-account.d.ts.map +1 -1
- package/dist/shell/app-sidebar-account.js +57 -22
- package/dist/shell/app-sidebar.d.ts +9 -3
- package/dist/shell/app-sidebar.d.ts.map +1 -1
- package/dist/shell/app-sidebar.js +55 -109
- package/dist/shell/app-sidebar.module.css +53 -96
- package/dist/shell/app-sidebar.module.css.d.ts +0 -5
- package/dist/shell/index.d.ts +14 -6
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +47 -12
- package/dist/shell/isomorphic-layout-effect.d.ts +14 -0
- package/dist/shell/isomorphic-layout-effect.d.ts.map +1 -0
- package/dist/shell/isomorphic-layout-effect.js +5 -0
- package/dist/shell/mobile-nav-model.d.ts.map +1 -1
- package/dist/shell/mobile-nav-model.js +27 -6
- package/dist/shell/nav-count-badge.d.ts +5 -3
- package/dist/shell/nav-count-badge.d.ts.map +1 -1
- package/dist/shell/nav-count-badge.js +3 -1
- package/dist/shell/page-aside.d.ts +43 -0
- package/dist/shell/page-aside.d.ts.map +1 -0
- package/dist/shell/page-aside.js +202 -0
- package/dist/shell/page-aside.module.css +174 -0
- package/dist/shell/page-aside.module.css.d.ts +14 -0
- package/dist/shell/page-bar.d.ts +55 -15
- package/dist/shell/page-bar.d.ts.map +1 -1
- package/dist/shell/page-bar.js +130 -41
- package/dist/shell/page-bar.module.css +107 -13
- package/dist/shell/page-bar.module.css.d.ts +4 -1
- package/dist/shell/sidebar-search.d.ts +4 -2
- package/dist/shell/sidebar-search.d.ts.map +1 -1
- package/dist/shell/sidebar-search.js +9 -4
- package/dist/shell/sidebar-search.module.css +1 -1
- package/dist/state/fields.d.ts +227 -34
- package/dist/state/fields.d.ts.map +1 -1
- package/dist/state/fields.js +77 -30
- package/dist/state/persisted-or-local.d.ts +9 -0
- package/dist/state/persisted-or-local.d.ts.map +1 -0
- package/dist/state/persisted-or-local.js +24 -0
- package/dist/state/persisted.d.ts +12 -0
- package/dist/state/persisted.d.ts.map +1 -1
- package/dist/state/persisted.js +36 -18
- package/dist/state.d.ts +1 -1
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +2 -6
- package/dist/surfaces.d.ts +12 -11
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +43 -12
- package/dist/theme/ctl-theme.d.ts.map +1 -1
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +102 -17
- package/dist/theme/nav-link.module.css +37 -26
- package/dist/theme/segmented-control.module.css +22 -0
- package/dist/theme/segmented-control.module.css.d.ts +1 -0
- package/dist/theme-lab/boot.d.ts +66 -0
- package/dist/theme-lab/boot.d.ts.map +1 -0
- package/dist/theme-lab/boot.js +82 -0
- package/dist/theme-lab/index.d.ts +4 -43
- package/dist/theme-lab/index.d.ts.map +1 -1
- package/dist/theme-lab/index.js +9 -69
- package/dist/theme-toggle/index.d.ts +3 -2
- package/dist/theme-toggle/index.d.ts.map +1 -1
- package/dist/theme-toggle/index.js +8 -2
- package/dist/tokens/index.d.ts +42 -0
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +35 -6
- package/dist/tokens/palette.d.ts +45 -25
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +31 -14
- package/dist/tokens.css +4 -5
- package/dist/utils/is-dev.d.ts +3 -0
- package/dist/utils/is-dev.d.ts.map +1 -0
- package/dist/utils/is-dev.js +6 -0
- package/dist/widget-header/delta-badge.d.ts +7 -1
- package/dist/widget-header/delta-badge.d.ts.map +1 -1
- package/dist/widget-header/delta-badge.js +8 -1
- package/dist/widget-header/index.d.ts +2 -2
- package/dist/widget-header/index.d.ts.map +1 -1
- package/dist/widget-header/index.js +3 -1
- package/dist/widget-header/widget-header.d.ts +99 -4
- package/dist/widget-header/widget-header.d.ts.map +1 -1
- package/dist/widget-header/widget-header.js +45 -18
- package/dist/widget-header/widget-header.module.css +87 -7
- package/dist/widget-header/widget-header.module.css.d.ts +1 -0
- package/llms.txt +6 -6
- package/package.json +49 -25
- package/src/agent/coalesce.ts +2 -1
- package/src/agent/merge.ts +2 -1
- package/src/agent/part-list.tsx +2 -0
- package/src/agent/stick-to-bottom.tsx +9 -11
- package/src/agent-chat/composer.tsx +124 -103
- package/src/agent-chat/relative-time.ts +2 -1
- package/src/agent-chat/thread-detail-panel.tsx +9 -5
- package/src/agent-chat/thread-feed-row.tsx +17 -9
- package/src/agent-chat/thread-feed.tsx +9 -3
- package/src/agent-chat/thread-message.tsx +48 -13
- package/src/agent-chat/thread-outcome-card.tsx +9 -1
- package/src/agent-chat/thread-workspace.tsx +72 -33
- package/src/agent-chat/tool-chip.tsx +8 -2
- package/src/charts/hooks/useBandPlot.ts +26 -8
- package/src/charts/index.ts +39 -4
- package/src/charts/kinds/BandStrip.tsx +36 -14
- package/src/charts/kinds/Bars.tsx +67 -16
- package/src/charts/kinds/Donut.tsx +30 -3
- package/src/charts/kinds/DualPanel.tsx +56 -10
- package/src/charts/kinds/Heatmap.tsx +90 -33
- package/src/charts/kinds/MirroredBars.tsx +21 -3
- package/src/charts/kinds/MultiLine.tsx +61 -18
- package/src/charts/kinds/StackedArea.tsx +77 -6
- package/src/charts/kinds/ZonedLine.tsx +71 -31
- package/src/charts/layout/auto-margin.ts +134 -21
- package/src/charts/layout/log-ticks.ts +90 -0
- package/src/charts/primitives/Axes.tsx +33 -5
- package/src/charts/primitives/CartesianChart.tsx +194 -41
- package/src/charts/primitives/ChartCard.tsx +66 -25
- package/src/charts/primitives/ChartFrame.tsx +107 -26
- package/src/charts/primitives/ChartLegend.tsx +43 -13
- package/src/charts/primitives/ChartPending.tsx +185 -7
- package/src/charts/primitives/ChartTooltip.tsx +37 -5
- package/src/charts/primitives/Crosshair.tsx +6 -3
- package/src/charts/primitives/chart-frame-layout.ts +212 -0
- package/src/charts/primitives/chart-tier.tsx +37 -0
- package/src/charts/series.ts +77 -1
- package/src/charts/sparklines/BarSparkline.tsx +11 -3
- package/src/charts/sparklines/LineSparkline.tsx +18 -4
- package/src/charts/sparklines/index.ts +2 -2
- package/src/charts/utils/format.ts +163 -2
- package/src/charts/utils/ticks.ts +149 -4
- package/src/cli/index.ts +8 -3
- package/src/commands/ShortcutsHelp.tsx +10 -3
- package/src/commands/define-commands.ts +2 -2
- package/src/commands/define-overlays.ts +216 -1
- package/src/commands/index.ts +12 -0
- package/src/commands/overlays-mount.tsx +60 -26
- package/src/common/errors.ts +73 -0
- package/src/common/index.ts +21 -0
- package/src/common/props.ts +71 -0
- package/src/common/query-state-like.ts +64 -0
- package/src/common/refs.ts +58 -0
- package/src/common/scroll-parent.ts +42 -0
- package/src/common/validate.ts +94 -0
- package/src/connectivity/connectivity-indicator.tsx +6 -1
- package/src/connectivity/connectivity-provider.tsx +4 -1
- package/src/connectivity/index.ts +1 -0
- package/src/connectivity/types.ts +5 -2
- package/src/connectivity/use-connectivity.ts +3 -2
- package/src/content/article-layout.module.css +11 -4
- package/src/content/callout.tsx +13 -7
- package/src/content/guide.tsx +14 -4
- package/src/content/heading-anchor.tsx +14 -3
- package/src/content/index.ts +2 -0
- package/src/content/markdown.tsx +2 -0
- package/src/content/prose.module.css +9 -7
- package/src/content/reading-progress.tsx +40 -15
- package/src/content/slug.ts +4 -0
- package/src/content/toc.tsx +47 -10
- package/src/controls/actions.tsx +89 -9
- package/src/controls/compare-filter.tsx +20 -3
- package/src/controls/control-group.tsx +11 -4
- package/src/controls/controls.module.css +169 -186
- package/src/controls/controls.module.css.d.ts +9 -11
- package/src/controls/enum-filter.tsx +46 -19
- package/src/controls/filter-context.tsx +21 -8
- package/src/controls/filter-pill.tsx +6 -3
- package/src/controls/filter-set.tsx +32 -13
- package/src/controls/filter-sheet.tsx +44 -194
- package/src/controls/index.ts +8 -0
- package/src/controls/multi-select-filter.tsx +179 -29
- package/src/controls/number-filter.tsx +329 -0
- package/src/controls/panel-row.module.css +86 -0
- package/src/controls/panel-row.module.css.d.ts +13 -0
- package/src/controls/panel-row.tsx +392 -0
- package/src/controls/range-filter.tsx +133 -43
- package/src/controls/search-filter.tsx +31 -13
- package/src/controls/select-filter.tsx +11 -1
- package/src/controls/slider-control.tsx +159 -0
- package/src/controls/sync-button.tsx +13 -3
- package/src/controls/toggle-filter.tsx +37 -17
- package/src/controls/view-tabs.tsx +63 -11
- package/src/controls-dates/date-range-picker.tsx +8 -1
- package/src/dashboard/delta-badge.tsx +5 -1
- package/src/dashboard/empty-state.tsx +72 -25
- package/src/dashboard/index.ts +19 -2
- package/src/dashboard/query-state.tsx +154 -84
- package/src/dashboard/settings-section.tsx +87 -31
- package/src/dashboard/stat-card.module.css +112 -26
- package/src/dashboard/stat-card.module.css.d.ts +5 -0
- package/src/dashboard/stat-card.tsx +207 -68
- package/src/dashboard/stat-group.module.css +66 -0
- package/src/dashboard/stat-group.module.css.d.ts +8 -0
- package/src/dashboard/stat-group.tsx +74 -0
- package/src/dashboard/widget-grid.module.css +53 -0
- package/src/dashboard/widget-grid.module.css.d.ts +9 -0
- package/src/dashboard/widget-grid.tsx +127 -0
- package/src/data/data-table.module.css +64 -0
- package/src/data/data-table.module.css.d.ts +11 -0
- package/src/data/data-table.tsx +927 -346
- package/src/data/index.ts +30 -2
- package/src/data/query-branch.ts +53 -0
- package/src/data/table.ts +5 -0
- package/src/data/virtual-list.tsx +189 -55
- package/src/data/virtual.ts +5 -1
- package/src/forms/FormErrorSummary.tsx +13 -6
- package/src/forms/create-form.ts +2 -2
- package/src/forms/field.ts +60 -11
- package/src/forms/form-layout.module.css +145 -0
- package/src/forms/form-layout.module.css.d.ts +20 -0
- package/src/forms/form-layout.tsx +305 -0
- package/src/forms/form-state.tsx +94 -0
- package/src/forms/index.ts +38 -7
- package/src/forms/use-field-array.ts +94 -0
- package/src/forms/use-form-draft.ts +145 -53
- package/src/forms/use-form-submit.ts +199 -0
- package/src/guard/index.ts +91 -23
- package/src/index.ts +79 -10
- package/src/nav/types.ts +15 -6
- package/src/notifications/bell.tsx +18 -4
- package/src/notifications/center.tsx +18 -5
- package/src/notifications/define-notifications.ts +64 -18
- package/src/notifications/index.ts +12 -1
- package/src/notifications/mount-guard.ts +41 -0
- package/src/notifications/notify.tsx +122 -3
- package/src/notifications/overlay.tsx +2 -0
- package/src/notifications/store.ts +7 -0
- package/src/provider/index.tsx +101 -13
- package/src/provider/lab-theme.ts +2 -1
- package/src/query/devtools.tsx +1 -0
- package/src/router-tanstack/field.ts +5 -0
- package/src/router-tanstack/index.ts +4 -0
- package/src/router-tanstack/search-store.ts +171 -12
- package/src/router-tanstack/use-nav.ts +18 -13
- package/src/section/index.ts +1 -1
- package/src/section/section.module.css +11 -0
- package/src/section/section.tsx +163 -96
- package/src/shell/account-types.ts +5 -2
- package/src/shell/app-brand.module.css +109 -0
- package/src/shell/app-brand.module.css.d.ts +14 -0
- package/src/shell/app-brand.tsx +140 -0
- package/src/shell/app-breadcrumbs.tsx +19 -9
- package/src/shell/app-header.module.css +10 -5
- package/src/shell/app-main.module.css +107 -0
- package/src/shell/app-main.module.css.d.ts +11 -0
- package/src/shell/app-mobile-nav.module.css +48 -27
- package/src/shell/app-mobile-nav.module.css.d.ts +0 -1
- package/src/shell/app-mobile-nav.tsx +43 -11
- package/src/shell/app-sidebar-account.tsx +29 -6
- package/src/shell/app-sidebar.module.css +53 -96
- package/src/shell/app-sidebar.module.css.d.ts +0 -5
- package/src/shell/app-sidebar.tsx +40 -113
- package/src/shell/index.tsx +156 -65
- package/src/shell/isomorphic-layout-effect.ts +14 -0
- package/src/shell/mobile-nav-model.ts +66 -10
- package/src/shell/nav-count-badge.tsx +6 -1
- package/src/shell/page-aside.module.css +174 -0
- package/src/shell/page-aside.module.css.d.ts +14 -0
- package/src/shell/page-aside.tsx +377 -0
- package/src/shell/page-bar.module.css +107 -13
- package/src/shell/page-bar.module.css.d.ts +4 -1
- package/src/shell/page-bar.tsx +294 -94
- package/src/shell/sidebar-search.module.css +1 -1
- package/src/shell/sidebar-search.tsx +13 -4
- package/src/state/fields.ts +360 -69
- package/src/state/persisted-or-local.ts +50 -0
- package/src/state/persisted.ts +76 -26
- package/src/state.ts +4 -7
- package/src/surfaces.ts +43 -12
- package/src/theme/ctl-theme.tsx +10 -0
- package/src/theme/index.ts +129 -16
- package/src/theme/nav-link.module.css +37 -26
- package/src/theme/segmented-control.module.css +22 -0
- package/src/theme/segmented-control.module.css.d.ts +1 -0
- package/src/theme-lab/boot.ts +140 -0
- package/src/theme-lab/index.tsx +21 -112
- package/src/theme-toggle/index.tsx +10 -3
- package/src/tokens/index.ts +50 -6
- package/src/tokens/palette.ts +58 -30
- package/src/utils/is-dev.ts +4 -0
- package/src/widget-header/delta-badge.tsx +25 -5
- package/src/widget-header/index.ts +10 -2
- package/src/widget-header/widget-header.module.css +87 -7
- package/src/widget-header/widget-header.module.css.d.ts +1 -0
- package/src/widget-header/widget-header.tsx +166 -42
- package/dist/agent/adapter.test.d.ts +0 -2
- package/dist/agent/adapter.test.d.ts.map +0 -1
- package/dist/agent/ai-sdk-transport.test.d.ts +0 -2
- package/dist/agent/ai-sdk-transport.test.d.ts.map +0 -1
- package/dist/agent/coalesce.test.d.ts +0 -2
- package/dist/agent/coalesce.test.d.ts.map +0 -1
- package/dist/agent/foreign.test.d.ts +0 -2
- package/dist/agent/foreign.test.d.ts.map +0 -1
- package/dist/agent/id.test.d.ts +0 -2
- package/dist/agent/id.test.d.ts.map +0 -1
- package/dist/agent/merge.test.d.ts +0 -2
- package/dist/agent/merge.test.d.ts.map +0 -1
- package/dist/agent/part-list.test.d.ts +0 -2
- package/dist/agent/part-list.test.d.ts.map +0 -1
- package/dist/agent/parts.test.d.ts +0 -2
- package/dist/agent/parts.test.d.ts.map +0 -1
- package/dist/agent/thread.test.d.ts +0 -2
- package/dist/agent/thread.test.d.ts.map +0 -1
- package/dist/agent/transport.test.d.ts +0 -2
- package/dist/agent/transport.test.d.ts.map +0 -1
- package/dist/agent/use-agent-stream.test.d.ts +0 -2
- package/dist/agent/use-agent-stream.test.d.ts.map +0 -1
- package/dist/agent/use-agent-thread-runs.foreign.test.d.ts +0 -2
- package/dist/agent/use-agent-thread-runs.foreign.test.d.ts.map +0 -1
- package/dist/agent/use-agent-thread-runs.resume.test.d.ts +0 -2
- package/dist/agent/use-agent-thread-runs.resume.test.d.ts.map +0 -1
- package/dist/agent/use-agent-thread-runs.test.d.ts +0 -2
- package/dist/agent/use-agent-thread-runs.test.d.ts.map +0 -1
- package/dist/agent/use-agent-thread-runs.wedge.test.d.ts +0 -2
- package/dist/agent/use-agent-thread-runs.wedge.test.d.ts.map +0 -1
- package/dist/agent-chat/composer.test.d.ts +0 -2
- package/dist/agent-chat/composer.test.d.ts.map +0 -1
- package/dist/agent-chat/relative-time.test.d.ts +0 -2
- package/dist/agent-chat/relative-time.test.d.ts.map +0 -1
- package/dist/agent-chat/thread-feed-row.test.d.ts +0 -2
- package/dist/agent-chat/thread-feed-row.test.d.ts.map +0 -1
- package/dist/agent-chat/thread-feed.test.d.ts +0 -2
- package/dist/agent-chat/thread-feed.test.d.ts.map +0 -1
- package/dist/agent-chat/thread-message.test.d.ts +0 -2
- package/dist/agent-chat/thread-message.test.d.ts.map +0 -1
- package/dist/agent-chat/thread-workspace.test.d.ts +0 -2
- package/dist/agent-chat/thread-workspace.test.d.ts.map +0 -1
- package/dist/agent-chat/tool-chip.test.d.ts +0 -2
- package/dist/agent-chat/tool-chip.test.d.ts.map +0 -1
- package/dist/agent-chat/virtualize.type-guard.test.d.ts +0 -2
- package/dist/agent-chat/virtualize.type-guard.test.d.ts.map +0 -1
- package/dist/charts/cursor/resolve.test.d.ts +0 -2
- package/dist/charts/cursor/resolve.test.d.ts.map +0 -1
- package/dist/charts/cursor/store.test.d.ts +0 -2
- package/dist/charts/cursor/store.test.d.ts.map +0 -1
- package/dist/charts/kinds/BandStrip.test.d.ts +0 -2
- package/dist/charts/kinds/BandStrip.test.d.ts.map +0 -1
- package/dist/charts/kinds/Bars.test.d.ts +0 -2
- package/dist/charts/kinds/Bars.test.d.ts.map +0 -1
- package/dist/charts/kinds/DualPanel.test.d.ts +0 -2
- package/dist/charts/kinds/DualPanel.test.d.ts.map +0 -1
- package/dist/charts/kinds/MirroredBars.test.d.ts +0 -2
- package/dist/charts/kinds/MirroredBars.test.d.ts.map +0 -1
- package/dist/charts/kinds/MultiLine.test.d.ts +0 -2
- package/dist/charts/kinds/MultiLine.test.d.ts.map +0 -1
- package/dist/charts/kinds/StackedArea.test.d.ts +0 -2
- package/dist/charts/kinds/StackedArea.test.d.ts.map +0 -1
- package/dist/charts/kinds/ZonedLine.test.d.ts +0 -2
- package/dist/charts/kinds/ZonedLine.test.d.ts.map +0 -1
- package/dist/charts/layout/auto-margin.test.d.ts +0 -2
- package/dist/charts/layout/auto-margin.test.d.ts.map +0 -1
- package/dist/charts/primitives/CartesianChart.test.d.ts +0 -2
- package/dist/charts/primitives/CartesianChart.test.d.ts.map +0 -1
- package/dist/charts/primitives/ChartCard.test.d.ts +0 -2
- package/dist/charts/primitives/ChartCard.test.d.ts.map +0 -1
- package/dist/charts/primitives/ChartFrame.test.d.ts +0 -2
- package/dist/charts/primitives/ChartFrame.test.d.ts.map +0 -1
- package/dist/charts/primitives/ChartLegend.test.d.ts +0 -2
- package/dist/charts/primitives/ChartLegend.test.d.ts.map +0 -1
- package/dist/charts/primitives/ChartTooltip.test.d.ts +0 -2
- package/dist/charts/primitives/ChartTooltip.test.d.ts.map +0 -1
- package/dist/charts/primitives/XZoneRects.test.d.ts +0 -2
- package/dist/charts/primitives/XZoneRects.test.d.ts.map +0 -1
- package/dist/charts/series.test.d.ts +0 -2
- package/dist/charts/series.test.d.ts.map +0 -1
- package/dist/charts/utils/domain.test.d.ts +0 -2
- package/dist/charts/utils/domain.test.d.ts.map +0 -1
- package/dist/charts/utils/measure-text.test.d.ts +0 -2
- package/dist/charts/utils/measure-text.test.d.ts.map +0 -1
- package/dist/cli/check-coverage.test.d.ts +0 -2
- package/dist/cli/check-coverage.test.d.ts.map +0 -1
- package/dist/cli/check-theme.test.d.ts +0 -2
- package/dist/cli/check-theme.test.d.ts.map +0 -1
- package/dist/cli/help.test.d.ts +0 -2
- package/dist/cli/help.test.d.ts.map +0 -1
- package/dist/cli/init-sync.test.d.ts +0 -2
- package/dist/cli/init-sync.test.d.ts.map +0 -1
- package/dist/cli/placement-engine.test.d.ts +0 -2
- package/dist/cli/placement-engine.test.d.ts.map +0 -1
- package/dist/cli/shipped-versions.test.d.ts +0 -2
- package/dist/cli/shipped-versions.test.d.ts.map +0 -1
- package/dist/cli/tarball-parity.test.d.ts +0 -2
- package/dist/cli/tarball-parity.test.d.ts.map +0 -1
- package/dist/cli/template-format.test.d.ts +0 -2
- package/dist/cli/template-format.test.d.ts.map +0 -1
- package/dist/cli/template-ledger.test.d.ts +0 -2
- package/dist/cli/template-ledger.test.d.ts.map +0 -1
- package/dist/cli/tokens-css.test.d.ts +0 -2
- package/dist/cli/tokens-css.test.d.ts.map +0 -1
- package/dist/cli/toolchain-wiring.test.d.ts +0 -2
- package/dist/cli/toolchain-wiring.test.d.ts.map +0 -1
- package/dist/cli/waiver-audit.test.d.ts +0 -2
- package/dist/cli/waiver-audit.test.d.ts.map +0 -1
- package/dist/content/article-actions.test.d.ts +0 -2
- package/dist/content/article-actions.test.d.ts.map +0 -1
- package/dist/content/article-model.test.d.ts +0 -2
- package/dist/content/article-model.test.d.ts.map +0 -1
- package/dist/content/block-split.test.d.ts +0 -2
- package/dist/content/block-split.test.d.ts.map +0 -1
- package/dist/content/heading-components.test.d.ts +0 -2
- package/dist/content/heading-components.test.d.ts.map +0 -1
- package/dist/content/markdown.test.d.ts +0 -2
- package/dist/content/markdown.test.d.ts.map +0 -1
- package/dist/content/prose.module.css.test.d.ts +0 -2
- package/dist/content/prose.module.css.test.d.ts.map +0 -1
- package/dist/content/sanitize.test.d.ts +0 -2
- package/dist/content/sanitize.test.d.ts.map +0 -1
- package/dist/content/slug.test.d.ts +0 -2
- package/dist/content/slug.test.d.ts.map +0 -1
- package/dist/controls/actions.test.d.ts +0 -2
- package/dist/controls/actions.test.d.ts.map +0 -1
- package/dist/controls/actions.type-guard.test.d.ts +0 -2
- package/dist/controls/actions.type-guard.test.d.ts.map +0 -1
- package/dist/controls/control-group.test.d.ts +0 -2
- package/dist/controls/control-group.test.d.ts.map +0 -1
- package/dist/controls/controls.router.test.d.ts +0 -2
- package/dist/controls/controls.router.test.d.ts.map +0 -1
- package/dist/controls/filter-set.test.d.ts +0 -2
- package/dist/controls/filter-set.test.d.ts.map +0 -1
- package/dist/controls/peer-boundary.test.d.ts +0 -2
- package/dist/controls/peer-boundary.test.d.ts.map +0 -1
- package/dist/controls/props-contract.test.d.ts +0 -2
- package/dist/controls/props-contract.test.d.ts.map +0 -1
- package/dist/controls/sync-button.test.d.ts +0 -2
- package/dist/controls/sync-button.test.d.ts.map +0 -1
- package/dist/dashboard/query-state.test.d.ts +0 -2
- package/dist/dashboard/query-state.test.d.ts.map +0 -1
- package/dist/dashboard/settings-section.test.d.ts +0 -2
- package/dist/dashboard/settings-section.test.d.ts.map +0 -1
- package/dist/dashboard/stat-card.test.d.ts +0 -2
- package/dist/dashboard/stat-card.test.d.ts.map +0 -1
- package/dist/data/data-table.test.d.ts +0 -2
- package/dist/data/data-table.test.d.ts.map +0 -1
- package/dist/guard/check-source.test.d.ts +0 -2
- package/dist/guard/check-source.test.d.ts.map +0 -1
- package/dist/guard/grace.test.d.ts +0 -20
- package/dist/guard/grace.test.d.ts.map +0 -1
- package/dist/guard/guard-hook.test.d.ts +0 -2
- package/dist/guard/guard-hook.test.d.ts.map +0 -1
- package/dist/provider/index.test.d.ts +0 -2
- package/dist/provider/index.test.d.ts.map +0 -1
- package/dist/provider/lab-theme.test.d.ts +0 -2
- package/dist/provider/lab-theme.test.d.ts.map +0 -1
- package/dist/query/error-message.test.d.ts +0 -2
- package/dist/query/error-message.test.d.ts.map +0 -1
- package/dist/router-tanstack/multi-search-param-store.test.d.ts +0 -2
- package/dist/router-tanstack/multi-search-param-store.test.d.ts.map +0 -1
- package/dist/router-tanstack/nav.test.d.ts +0 -2
- package/dist/router-tanstack/nav.test.d.ts.map +0 -1
- package/dist/router-tanstack/search-param-store.test.d.ts +0 -2
- package/dist/router-tanstack/search-param-store.test.d.ts.map +0 -1
- package/dist/router-tanstack/search-store.router.test.d.ts +0 -2
- package/dist/router-tanstack/search-store.router.test.d.ts.map +0 -1
- package/dist/router-tanstack/search-store.test.d.ts +0 -2
- package/dist/router-tanstack/search-store.test.d.ts.map +0 -1
- package/dist/router-tanstack/use-nav.test.d.ts +0 -2
- package/dist/router-tanstack/use-nav.test.d.ts.map +0 -1
- package/dist/section/section.test.d.ts +0 -2
- package/dist/section/section.test.d.ts.map +0 -1
- package/dist/shell/app-mobile-nav.test.d.ts +0 -2
- package/dist/shell/app-mobile-nav.test.d.ts.map +0 -1
- package/dist/shell/app-sidebar.test.d.ts +0 -2
- package/dist/shell/app-sidebar.test.d.ts.map +0 -1
- package/dist/shell/index.test.d.ts +0 -2
- package/dist/shell/index.test.d.ts.map +0 -1
- package/dist/shell/mobile-nav-model.test.d.ts +0 -2
- package/dist/shell/mobile-nav-model.test.d.ts.map +0 -1
- package/dist/shell/page-bar.test.d.ts +0 -2
- package/dist/shell/page-bar.test.d.ts.map +0 -1
- package/dist/state/fields.test.d.ts +0 -2
- package/dist/state/fields.test.d.ts.map +0 -1
- package/dist/styles.floor.test.d.ts +0 -2
- package/dist/styles.floor.test.d.ts.map +0 -1
- package/dist/theme/border-coverage.test.d.ts +0 -18
- package/dist/theme/border-coverage.test.d.ts.map +0 -1
- package/dist/theme/contrast.test.d.ts +0 -2
- package/dist/theme/contrast.test.d.ts.map +0 -1
- package/dist/theme/ctl-theme.test.d.ts +0 -2
- package/dist/theme/ctl-theme.test.d.ts.map +0 -1
- package/dist/theme/ctl-tier-coverage.test.d.ts +0 -2
- package/dist/theme/ctl-tier-coverage.test.d.ts.map +0 -1
- package/dist/theme/density-option.test.d.ts +0 -2
- package/dist/theme/density-option.test.d.ts.map +0 -1
- package/dist/theme/density-relations.test.d.ts +0 -2
- package/dist/theme/density-relations.test.d.ts.map +0 -1
- package/dist/theme/derive-theme.test.d.ts +0 -2
- package/dist/theme/derive-theme.test.d.ts.map +0 -1
- package/dist/theme/divider-law.test.d.ts +0 -2
- package/dist/theme/divider-law.test.d.ts.map +0 -1
- package/dist/theme/fonts-option.test.d.ts +0 -2
- package/dist/theme/fonts-option.test.d.ts.map +0 -1
- package/dist/theme/icon-slot.test.d.ts +0 -2
- package/dist/theme/icon-slot.test.d.ts.map +0 -1
- package/dist/theme/layout-rhythm-css.test.d.ts +0 -2
- package/dist/theme/layout-rhythm-css.test.d.ts.map +0 -1
- package/dist/theme/radius-option.test.d.ts +0 -2
- package/dist/theme/radius-option.test.d.ts.map +0 -1
- package/dist/theme/radius.test.d.ts +0 -2
- package/dist/theme/radius.test.d.ts.map +0 -1
- package/dist/theme/shadow-surfaces.test.d.ts +0 -26
- package/dist/theme/shadow-surfaces.test.d.ts.map +0 -1
- package/dist/theme/spacing.test.d.ts +0 -2
- package/dist/theme/spacing.test.d.ts.map +0 -1
- package/dist/theme/use-basalt-spacing.test.d.ts +0 -2
- package/dist/theme/use-basalt-spacing.test.d.ts.map +0 -1
- package/dist/theme-lab/derive-controls.test.d.ts +0 -2
- package/dist/theme-lab/derive-controls.test.d.ts.map +0 -1
- package/dist/theme-lab/derive-state.test.d.ts +0 -2
- package/dist/theme-lab/derive-state.test.d.ts.map +0 -1
- package/dist/theme-lab/theme-lab.test.d.ts +0 -2
- package/dist/theme-lab/theme-lab.test.d.ts.map +0 -1
- package/dist/tokens/build-fonts-css.test.d.ts +0 -2
- package/dist/tokens/build-fonts-css.test.d.ts.map +0 -1
- package/dist/tokens/build-radius-css.test.d.ts +0 -2
- package/dist/tokens/build-radius-css.test.d.ts.map +0 -1
- package/dist/tokens/chart-margin.test.d.ts +0 -2
- package/dist/tokens/chart-margin.test.d.ts.map +0 -1
- package/dist/tokens/density.test.d.ts +0 -2
- package/dist/tokens/density.test.d.ts.map +0 -1
- package/dist/tokens/derive-radius.test.d.ts +0 -2
- package/dist/tokens/derive-radius.test.d.ts.map +0 -1
- package/dist/tokens/derive.test.d.ts +0 -2
- package/dist/tokens/derive.test.d.ts.map +0 -1
- package/dist/tokens/palette.test.d.ts +0 -2
- package/dist/tokens/palette.test.d.ts.map +0 -1
- package/dist/tokens/text-scale.test.d.ts +0 -2
- package/dist/tokens/text-scale.test.d.ts.map +0 -1
- package/dist/vite-color-mix.test.d.ts +0 -2
- package/dist/vite-color-mix.test.d.ts.map +0 -1
- package/dist/vite.test.d.ts +0 -2
- package/dist/vite.test.d.ts.map +0 -1
- package/dist/widget-header/delta-badge.test.d.ts +0 -2
- package/dist/widget-header/delta-badge.test.d.ts.map +0 -1
- package/dist/widget-header/no-mantine.test.d.ts +0 -2
- package/dist/widget-header/no-mantine.test.d.ts.map +0 -1
- package/dist/widget-header/widget-header.test.d.ts +0 -2
- package/dist/widget-header/widget-header.test.d.ts.map +0 -1
- package/src/agent/adapter.test.ts +0 -1640
- package/src/agent/ai-sdk-transport.test.ts +0 -658
- package/src/agent/coalesce.test.ts +0 -387
- package/src/agent/foreign.test.ts +0 -51
- package/src/agent/id.test.ts +0 -169
- package/src/agent/merge.test.ts +0 -206
- package/src/agent/part-list.test.tsx +0 -112
- package/src/agent/parts.test.ts +0 -720
- package/src/agent/thread.test.ts +0 -220
- package/src/agent/transport.test.ts +0 -70
- package/src/agent/use-agent-stream.test.tsx +0 -287
- package/src/agent/use-agent-thread-runs.foreign.test.tsx +0 -138
- package/src/agent/use-agent-thread-runs.resume.test.tsx +0 -294
- package/src/agent/use-agent-thread-runs.test.tsx +0 -1120
- package/src/agent/use-agent-thread-runs.wedge.test.tsx +0 -387
- package/src/agent-chat/composer.test.tsx +0 -924
- package/src/agent-chat/relative-time.test.ts +0 -95
- package/src/agent-chat/thread-feed-row.test.tsx +0 -510
- package/src/agent-chat/thread-feed.test.tsx +0 -241
- package/src/agent-chat/thread-message.test.tsx +0 -1285
- package/src/agent-chat/thread-workspace.test.tsx +0 -169
- package/src/agent-chat/tool-chip.test.tsx +0 -139
- package/src/agent-chat/virtualize.type-guard.test.ts +0 -49
- package/src/charts/cursor/resolve.test.ts +0 -151
- package/src/charts/cursor/store.test.ts +0 -64
- package/src/charts/kinds/BandStrip.test.tsx +0 -282
- package/src/charts/kinds/Bars.test.tsx +0 -183
- package/src/charts/kinds/DualPanel.test.tsx +0 -527
- package/src/charts/kinds/MirroredBars.test.tsx +0 -232
- package/src/charts/kinds/MultiLine.test.tsx +0 -218
- package/src/charts/kinds/StackedArea.test.tsx +0 -114
- package/src/charts/kinds/ZonedLine.test.tsx +0 -147
- package/src/charts/layout/auto-margin.test.ts +0 -42
- package/src/charts/primitives/CartesianChart.test.tsx +0 -647
- package/src/charts/primitives/ChartCard.test.tsx +0 -66
- package/src/charts/primitives/ChartFrame.test.tsx +0 -142
- package/src/charts/primitives/ChartLegend.test.tsx +0 -78
- package/src/charts/primitives/ChartTooltip.test.tsx +0 -44
- package/src/charts/primitives/XZoneRects.test.tsx +0 -123
- package/src/charts/series.test.ts +0 -112
- package/src/charts/utils/domain.test.ts +0 -54
- package/src/charts/utils/measure-text.test.ts +0 -36
- package/src/cli/check-coverage.test.ts +0 -297
- package/src/cli/check-theme.test.ts +0 -837
- package/src/cli/help.test.ts +0 -170
- package/src/cli/init-sync.test.ts +0 -343
- package/src/cli/placement-engine.test.ts +0 -298
- package/src/cli/shipped-versions.test.ts +0 -83
- package/src/cli/tarball-parity.test.ts +0 -44
- package/src/cli/template-format.test.ts +0 -64
- package/src/cli/template-ledger.test.ts +0 -219
- package/src/cli/tokens-css.test.ts +0 -249
- package/src/cli/toolchain-wiring.test.ts +0 -1358
- package/src/cli/waiver-audit.test.ts +0 -215
- package/src/content/article-actions.test.ts +0 -78
- package/src/content/article-model.test.ts +0 -119
- package/src/content/block-split.test.ts +0 -75
- package/src/content/heading-components.test.tsx +0 -65
- package/src/content/markdown.test.tsx +0 -603
- package/src/content/prose.module.css.test.ts +0 -39
- package/src/content/sanitize.test.ts +0 -659
- package/src/content/slug.test.ts +0 -62
- package/src/controls/actions.test.tsx +0 -252
- package/src/controls/actions.type-guard.test.ts +0 -63
- package/src/controls/control-group.test.tsx +0 -229
- package/src/controls/controls.router.test.tsx +0 -637
- package/src/controls/filter-set.test.tsx +0 -472
- package/src/controls/peer-boundary.test.ts +0 -85
- package/src/controls/props-contract.test.tsx +0 -106
- package/src/controls/sync-button.test.tsx +0 -183
- package/src/dashboard/query-state.test.tsx +0 -152
- package/src/dashboard/settings-section.test.tsx +0 -56
- package/src/dashboard/stat-card.test.tsx +0 -154
- package/src/data/data-table.test.tsx +0 -434
- package/src/guard/check-source.test.ts +0 -3355
- package/src/guard/grace.test.ts +0 -149
- package/src/guard/guard-hook.test.ts +0 -203
- package/src/provider/index.test.tsx +0 -101
- package/src/provider/lab-theme.test.ts +0 -147
- package/src/query/error-message.test.ts +0 -67
- package/src/router-tanstack/multi-search-param-store.test.ts +0 -124
- package/src/router-tanstack/nav.test.ts +0 -111
- package/src/router-tanstack/search-param-store.test.ts +0 -135
- package/src/router-tanstack/search-store.router.test.tsx +0 -661
- package/src/router-tanstack/search-store.test.ts +0 -418
- package/src/router-tanstack/use-nav.test.tsx +0 -187
- package/src/section/section.test.tsx +0 -204
- package/src/shell/app-mobile-nav.test.tsx +0 -403
- package/src/shell/app-sidebar.test.tsx +0 -509
- package/src/shell/index.test.tsx +0 -428
- package/src/shell/mobile-nav-model.test.ts +0 -517
- package/src/shell/page-bar.test.tsx +0 -316
- package/src/state/fields.test.tsx +0 -258
- package/src/styles.floor.test.ts +0 -36
- package/src/theme/border-coverage.test.ts +0 -272
- package/src/theme/contrast.test.ts +0 -354
- package/src/theme/ctl-theme.test.tsx +0 -275
- package/src/theme/ctl-tier-coverage.test.ts +0 -170
- package/src/theme/density-option.test.ts +0 -96
- package/src/theme/density-relations.test.ts +0 -506
- package/src/theme/derive-theme.test.ts +0 -101
- package/src/theme/divider-law.test.ts +0 -259
- package/src/theme/fonts-option.test.ts +0 -75
- package/src/theme/icon-slot.test.tsx +0 -275
- package/src/theme/layout-rhythm-css.test.ts +0 -72
- package/src/theme/radius-option.test.ts +0 -86
- package/src/theme/radius.test.ts +0 -142
- package/src/theme/shadow-surfaces.test.ts +0 -488
- package/src/theme/spacing.test.ts +0 -495
- package/src/theme/use-basalt-spacing.test.tsx +0 -55
- package/src/theme-lab/derive-controls.test.ts +0 -94
- package/src/theme-lab/derive-state.test.ts +0 -71
- package/src/theme-lab/theme-lab.test.ts +0 -46
- package/src/tokens/build-fonts-css.test.ts +0 -52
- package/src/tokens/build-radius-css.test.ts +0 -25
- package/src/tokens/chart-margin.test.ts +0 -39
- package/src/tokens/density.test.ts +0 -259
- package/src/tokens/derive-radius.test.ts +0 -62
- package/src/tokens/derive.test.ts +0 -30
- package/src/tokens/palette.test.ts +0 -114
- package/src/tokens/text-scale.test.ts +0 -51
- package/src/vite-color-mix.test.ts +0 -46
- package/src/vite.test.ts +0 -423
- package/src/widget-header/delta-badge.test.tsx +0 -60
- package/src/widget-header/no-mantine.test.ts +0 -33
- package/src/widget-header/widget-header.test.tsx +0 -187
|
@@ -16,17 +16,30 @@
|
|
|
16
16
|
* label="All channels"
|
|
17
17
|
* options={CHANNELS.map((c) => ({ value: c, label: `${c} · ${counts[c]}` }))}
|
|
18
18
|
* />
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // The aside's facet column (`docs/ASIDE-SPEC.md` §1): counts and their bars are DATA, so they
|
|
22
|
+
* // arrive as a map rather than baked into the labels the way the runtime catalogue does it.
|
|
23
|
+
* <MultiSelectFilter field={shipments.field.origin} label="Origin" counts={countsByOrigin} />
|
|
19
24
|
*/
|
|
20
25
|
import { Button, Checkbox, Stack } from '@mantine/core'
|
|
21
|
-
import
|
|
26
|
+
import { useState } from 'react'
|
|
27
|
+
import type { CSSProperties, ReactNode } from 'react'
|
|
28
|
+
import type { BasaltProps } from '../common/props'
|
|
29
|
+
import { assertRequiredProps } from '../common/validate'
|
|
22
30
|
import type { FieldHandle, MultiField } from '../state'
|
|
23
31
|
import type { FilterOption } from './select-filter'
|
|
24
32
|
import classes from './controls.module.css'
|
|
25
33
|
import { useFilterRegistration, useFilterSurface } from './filter-context'
|
|
26
34
|
import { FilterPill } from './filter-pill'
|
|
27
|
-
import {
|
|
35
|
+
import { CheckGlyph, useControlName } from './filter-sheet'
|
|
36
|
+
import { PanelRow } from './panel-row'
|
|
28
37
|
|
|
29
|
-
|
|
38
|
+
/** How many facet rows a panel shows before the rest fold behind `Show N more`. Six is the point at
|
|
39
|
+
* which a facet column stops being a list you scan and starts being one you scroll. */
|
|
40
|
+
const PANEL_FACET_MAX = 6
|
|
41
|
+
|
|
42
|
+
export type MultiSelectFilterProps<T extends string> = BasaltProps & {
|
|
30
43
|
readonly field: FieldHandle<MultiField<T>>
|
|
31
44
|
readonly label: string
|
|
32
45
|
readonly icon?: ReactNode
|
|
@@ -43,49 +56,75 @@ export type MultiSelectFilterProps<T extends string> = {
|
|
|
43
56
|
* string-field shape to make it required.
|
|
44
57
|
*/
|
|
45
58
|
readonly options?: readonly FilterOption[]
|
|
59
|
+
/**
|
|
60
|
+
* Per-option counts, rendered on the `panel` surface as a mono number plus a bar proportional to
|
|
61
|
+
* the LARGEST count in the map — the Foundry facet column (`docs/ASIDE-SPEC.md` §1). A missing
|
|
62
|
+
* key is a row with no count and no bar, not a zero.
|
|
63
|
+
*
|
|
64
|
+
* Ignored on the pill and sheet surfaces, where there is no room for a second data channel: put
|
|
65
|
+
* the number in the label there (`options`).
|
|
66
|
+
*/
|
|
67
|
+
readonly counts?: Record<string, number>
|
|
68
|
+
/**
|
|
69
|
+
* How many facet rows the `panel` surface shows before the rest fold behind a `Show N more` row.
|
|
70
|
+
*
|
|
71
|
+
* @default 6
|
|
72
|
+
*/
|
|
73
|
+
readonly max?: number
|
|
46
74
|
}
|
|
47
75
|
|
|
48
|
-
export function MultiSelectFilter<T extends string>({
|
|
49
|
-
field
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
76
|
+
export function MultiSelectFilter<T extends string>(props: MultiSelectFilterProps<T>): ReactNode {
|
|
77
|
+
// F-ERR-1 — without this a missing `field` surfaces as `undefined is not an object
|
|
78
|
+
// (evaluating 'field.use')`, caught by `BasaltErrorBoundary`.
|
|
79
|
+
assertRequiredProps('MultiSelectFilter', props, ['field'], {
|
|
80
|
+
field: 'bind it to a store field (`store.field.<name>`), never a value/onChange pair.',
|
|
81
|
+
})
|
|
82
|
+
const { field, label, icon, noun, counts, max, options: optionsProp, className, style } = props
|
|
55
83
|
const [value, setValue] = field.use()
|
|
56
84
|
const surface = useFilterSurface()
|
|
57
85
|
const isDefault = field.isDefault(value)
|
|
58
86
|
useFilterRegistration(!isDefault, () => {
|
|
59
|
-
|
|
87
|
+
field.clear()
|
|
60
88
|
})
|
|
61
89
|
|
|
62
90
|
// The prop wins whole, never merged — same rule as `EnumFilter`: a catalogue that dropped a row
|
|
63
91
|
// must be able to drop it, and `All channels` is counted against the rows actually shown.
|
|
64
92
|
const options: readonly FilterOption[] = optionsProp ?? field.options
|
|
65
93
|
const inSheet = surface === 'sheet'
|
|
94
|
+
const inPanel = surface === 'panel'
|
|
66
95
|
const carriesInformation = value.length > 0 && value.length < options.length
|
|
67
|
-
const { labelId } = useControlName(label, inSheet)
|
|
96
|
+
const { labelId } = useControlName(label, inSheet || inPanel)
|
|
97
|
+
|
|
98
|
+
// One toggle, shared by both list forms — the rows are rendered from `options`, whose values
|
|
99
|
+
// belong to the field either way (the prop relabels a closed set, it cannot open it), so the cast
|
|
100
|
+
// restores what the codec already guarantees.
|
|
101
|
+
const toggle = (next: string): void => {
|
|
102
|
+
const has = value.includes(next as T)
|
|
103
|
+
setValue(has ? value.filter((v) => v !== next) : [...value, next as T])
|
|
104
|
+
}
|
|
68
105
|
|
|
69
|
-
// The SHEET form is
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
|
|
106
|
+
// The SHEET form is the PANEL form — the facet column (`docs/CONTROLS-SPEC.md` §3: "sheet = panel
|
|
107
|
+
// rows inside a Drawer"). It used to render a `SheetOptionList` in `multi` mode (44px rows, a
|
|
108
|
+
// trailing check, no fold), which meant a set with many options was a sheet as tall as the set;
|
|
109
|
+
// the facet column folds past `max` behind `Show N more` instead, which is the shape a set of
|
|
110
|
+
// unknown size actually needs.
|
|
111
|
+
if (inSheet || inPanel) {
|
|
73
112
|
return (
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
113
|
+
<PanelRow
|
|
114
|
+
label={label}
|
|
115
|
+
labelId={labelId}
|
|
116
|
+
{...(className !== undefined && { className })}
|
|
117
|
+
{...(style !== undefined && { style })}
|
|
118
|
+
>
|
|
119
|
+
<FacetList
|
|
77
120
|
labelId={labelId}
|
|
78
|
-
selected={value}
|
|
79
121
|
options={options}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const has = value.includes(next as T)
|
|
85
|
-
setValue(has ? value.filter((v) => v !== next) : [...value, next as T])
|
|
86
|
-
}}
|
|
122
|
+
selected={value}
|
|
123
|
+
max={max ?? PANEL_FACET_MAX}
|
|
124
|
+
onToggle={toggle}
|
|
125
|
+
{...(counts !== undefined && { counts })}
|
|
87
126
|
/>
|
|
88
|
-
</
|
|
127
|
+
</PanelRow>
|
|
89
128
|
)
|
|
90
129
|
}
|
|
91
130
|
|
|
@@ -119,7 +158,7 @@ export function MultiSelectFilter<T extends string>({
|
|
|
119
158
|
variant="subtle"
|
|
120
159
|
size="ctl"
|
|
121
160
|
onClick={() => {
|
|
122
|
-
|
|
161
|
+
field.clear()
|
|
123
162
|
}}
|
|
124
163
|
>
|
|
125
164
|
Clear
|
|
@@ -133,8 +172,119 @@ export function MultiSelectFilter<T extends string>({
|
|
|
133
172
|
label={carriesInformation ? `${value.length} ${noun ?? label.toLowerCase()}` : label}
|
|
134
173
|
active={!isDefault}
|
|
135
174
|
{...(icon !== undefined && { icon })}
|
|
175
|
+
{...(className !== undefined && { className })}
|
|
176
|
+
{...(style !== undefined && { style })}
|
|
136
177
|
>
|
|
137
178
|
<div className={classes.optionList}>{body}</div>
|
|
138
179
|
</FilterPill>
|
|
139
180
|
)
|
|
140
181
|
}
|
|
182
|
+
|
|
183
|
+
type FacetListProps = {
|
|
184
|
+
readonly labelId: string
|
|
185
|
+
readonly options: readonly FilterOption[]
|
|
186
|
+
readonly selected: readonly string[]
|
|
187
|
+
readonly counts?: Record<string, number>
|
|
188
|
+
/** Rows shown before the rest fold behind `Show N more`. */
|
|
189
|
+
readonly max: number
|
|
190
|
+
readonly onToggle: (value: string) => void
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* The facet column's rows. INTERNAL to this control: a list of counted checkboxes not bound to a
|
|
195
|
+
* `field.multi` is a hand-rolled filter (`basalt/hand-rolled-filter`), so there is no separate
|
|
196
|
+
* `FacetList` export to reach for — the counts are a prop on the control that already owns the set.
|
|
197
|
+
*
|
|
198
|
+
* The bar is scaled against the LARGEST count present, not against a total: a facet column is read
|
|
199
|
+
* as a comparison between its own rows, and dividing by a total nobody supplied would render every
|
|
200
|
+
* bar as a sliver on a set with many options.
|
|
201
|
+
*/
|
|
202
|
+
function FacetList({
|
|
203
|
+
labelId,
|
|
204
|
+
options,
|
|
205
|
+
selected,
|
|
206
|
+
counts,
|
|
207
|
+
max,
|
|
208
|
+
onToggle,
|
|
209
|
+
}: FacetListProps): ReactNode {
|
|
210
|
+
const [expanded, setExpanded] = useState(false)
|
|
211
|
+
const shown = expanded ? options : options.slice(0, max)
|
|
212
|
+
const hidden = options.length - shown.length
|
|
213
|
+
// `0` as the seed, so an empty map (or one whose keys miss every option) yields a peak of 0 rather
|
|
214
|
+
// than `-Infinity`, and every bar resolves to a width of 0 instead of `NaN%`.
|
|
215
|
+
const peak = options.reduce((top, option) => Math.max(top, counts?.[option.value] ?? 0), 0)
|
|
216
|
+
|
|
217
|
+
return (
|
|
218
|
+
// A real `<fieldset>` named by the row's own label — the native element carries the grouping
|
|
219
|
+
// semantics, and the heading is already visible.
|
|
220
|
+
<fieldset className={classes.facetList} aria-labelledby={labelId}>
|
|
221
|
+
{shown.map((option) => {
|
|
222
|
+
const isSelected = selected.includes(option.value)
|
|
223
|
+
const count = counts?.[option.value]
|
|
224
|
+
return (
|
|
225
|
+
<label
|
|
226
|
+
key={option.value}
|
|
227
|
+
className={classes.facetOption}
|
|
228
|
+
{...(isSelected && { 'data-selected': true })}
|
|
229
|
+
{...(option.disabled === true && { 'data-disabled': true })}
|
|
230
|
+
>
|
|
231
|
+
{/* theme-allow raw-form-control — the input is NEVER PAINTED. Visually hidden
|
|
232
|
+
(`.facetInput`), it exists only to carry the row's semantics; the surface the reader
|
|
233
|
+
sees and clicks is the `<label>` around it. */}
|
|
234
|
+
<input
|
|
235
|
+
className={classes.facetInput}
|
|
236
|
+
type="checkbox"
|
|
237
|
+
value={option.value}
|
|
238
|
+
checked={isSelected}
|
|
239
|
+
disabled={option.disabled === true}
|
|
240
|
+
aria-label={option.label}
|
|
241
|
+
onChange={() => {
|
|
242
|
+
onToggle(option.value)
|
|
243
|
+
}}
|
|
244
|
+
/>
|
|
245
|
+
{count === undefined ? (
|
|
246
|
+
// No count/bar on this row — without a visible box, an unselected row was plain
|
|
247
|
+
// text with no affordance that it toggles at all (only a checkmark appeared, and
|
|
248
|
+
// only once selected). `size="ctl"` reads `--checkbox-size-ctl`
|
|
249
|
+
// (`theme/index.ts`'s `ctlSizeVars`), the same var every other home's `Checkbox`
|
|
250
|
+
// resolves through. Decorative — the real toggle semantics live on `.facetInput`.
|
|
251
|
+
<Checkbox.Indicator
|
|
252
|
+
className={classes.facetCheckbox}
|
|
253
|
+
size="ctl"
|
|
254
|
+
checked={isSelected}
|
|
255
|
+
aria-hidden
|
|
256
|
+
{...(option.disabled === true && { disabled: true })}
|
|
257
|
+
/>
|
|
258
|
+
) : (
|
|
259
|
+
peak > 0 && (
|
|
260
|
+
<span
|
|
261
|
+
className={classes.facetBar}
|
|
262
|
+
aria-hidden
|
|
263
|
+
style={{ '--facet-fill': `${(count / peak) * 100}%` } as CSSProperties}
|
|
264
|
+
/>
|
|
265
|
+
)
|
|
266
|
+
)}
|
|
267
|
+
<span className={classes.facetLabel}>{option.label}</span>
|
|
268
|
+
{count !== undefined && <span className={classes.facetCount}>{count}</span>}
|
|
269
|
+
{count !== undefined && isSelected && (
|
|
270
|
+
<span className={classes.facetCheck} aria-hidden>
|
|
271
|
+
<CheckGlyph />
|
|
272
|
+
</span>
|
|
273
|
+
)}
|
|
274
|
+
</label>
|
|
275
|
+
)
|
|
276
|
+
})}
|
|
277
|
+
{hidden > 0 && (
|
|
278
|
+
<button
|
|
279
|
+
type="button"
|
|
280
|
+
className={classes.facetMore}
|
|
281
|
+
onClick={() => {
|
|
282
|
+
setExpanded(true)
|
|
283
|
+
}}
|
|
284
|
+
>
|
|
285
|
+
{`Show ${hidden} more`}
|
|
286
|
+
</button>
|
|
287
|
+
)}
|
|
288
|
+
</fieldset>
|
|
289
|
+
)
|
|
290
|
+
}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `NumberFilter` — one number, one pill (`docs/CONTROLS-SPEC.md` §3), over a `field.number`. Takes
|
|
3
|
+
* `field`, never `value`/`onChange`/`size` (C2/C5), like every other control on this subpath.
|
|
4
|
+
*
|
|
5
|
+
* **Two forms, and `options` is the whole switch.** With `options` the filter is a pill plus a radio
|
|
6
|
+
* list — the same body `SelectFilter` renders, reached through `EnumFilter`'s `ChoiceHandle`, so a
|
|
7
|
+
* numeric preset set (`1` / `2` / `7` nights) is one idiom with the enum filters rather than a
|
|
8
|
+
* second spelling of the same choice. Without `options` it is a pill whose popover holds a `ctl`
|
|
9
|
+
* `NumberInput` with its steppers, and the sheet form is that input in a full-width row.
|
|
10
|
+
*
|
|
11
|
+
* **A threshold is a NUMBER, not a string enum, and that is why this control exists.** Both
|
|
12
|
+
* consumers that needed one wrote around its absence in opposite directions: linewatch kept a raw
|
|
13
|
+
* `SegmentedControl` over `minDuration` (a `control-outside-home` warn, law C1), and argo widened
|
|
14
|
+
* `nights` into a string enum — which puts `'3'` in the URL, makes every comparison a parse, and
|
|
15
|
+
* closes a set that was never closed. `field.number` already carries the codec, the clamp and both
|
|
16
|
+
* lanes; the only thing missing was the control.
|
|
17
|
+
*
|
|
18
|
+
* **The stepper applies on blur or Enter, never per keystroke.** A number is typed digit by digit,
|
|
19
|
+
* so a live write navigates on `4`, `42`, `420` and lands three values in the loader for one
|
|
20
|
+
* intended threshold — and the middle two are real values the store would clamp and persist.
|
|
21
|
+
* `SearchFilter` debounces for the same reason; a number has an explicit commit point (leaving the
|
|
22
|
+
* field, pressing Enter) and a phrase does not, so this one commits rather than waits.
|
|
23
|
+
*
|
|
24
|
+
* **`min`/`max`/`int`/`step` come off the HANDLE, never the call site.**
|
|
25
|
+
* `field.number({ min, max, int, step })` declares them, the codec clamps to them on write, and the
|
|
26
|
+
* handle republishes all four — so the `NumberInput` bounds its own stepper and `int` refuses
|
|
27
|
+
* decimals outright. Three of them are not props here on purpose: they belong to the field, which is
|
|
28
|
+
* the thing that validates the URL, and a second copy at the call site is a second answer to the
|
|
29
|
+
* same question that stops matching the moment the field moves. The clamp is still the backstop for
|
|
30
|
+
* a value that arrives from outside the input (a hand-typed URL, a stale deep link), and the draft
|
|
31
|
+
* follows the field down when it fires (see {@link useCommittedNumber}).
|
|
32
|
+
*
|
|
33
|
+
* `step` survives as a prop only because a call site may legitimately want a COARSER grain than the
|
|
34
|
+
* field's (a 0..600 seconds threshold stepping by 30), and it now DEFAULTS to `field.step` — the
|
|
35
|
+
* resolved one, so `int: true` still implies 1 with the rule stated once, in `field.number`.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* // A preset set — a pill plus a radio list, exactly like SelectFilter.
|
|
39
|
+
* // nights: field.number({ fallback: 2, min: 1, max: 14, int: true })
|
|
40
|
+
* <NumberFilter
|
|
41
|
+
* field={booking.field.nights}
|
|
42
|
+
* label="Nights"
|
|
43
|
+
* options={[
|
|
44
|
+
* { value: 1, label: '1 night' },
|
|
45
|
+
* { value: 2, label: '2 nights' },
|
|
46
|
+
* { value: 7, label: 'A week' },
|
|
47
|
+
* ]}
|
|
48
|
+
* />
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* // An open threshold — a pill holding a stepper, applied on blur or Enter.
|
|
52
|
+
* // minDuration: field.number({ fallback: 0, min: 0, max: 600 })
|
|
53
|
+
* <NumberFilter field={lines.field.minDuration} label="Min duration" step={30} />
|
|
54
|
+
*/
|
|
55
|
+
import { NumberInput } from '@mantine/core'
|
|
56
|
+
import { useEffect, useRef, useState } from 'react'
|
|
57
|
+
import type { ReactNode } from 'react'
|
|
58
|
+
import type { BasaltProps } from '../common/props'
|
|
59
|
+
import { assertRequiredProps } from '../common/validate'
|
|
60
|
+
import type { FieldHandle, NumberField } from '../state'
|
|
61
|
+
import classes from './controls.module.css'
|
|
62
|
+
import { EnumFilter } from './enum-filter'
|
|
63
|
+
import type { ChoiceHandle } from './enum-filter'
|
|
64
|
+
import { useFilterRegistration, useFilterSurface } from './filter-context'
|
|
65
|
+
import { FilterPill } from './filter-pill'
|
|
66
|
+
import { useControlName } from './filter-sheet'
|
|
67
|
+
import { PanelRow } from './panel-row'
|
|
68
|
+
|
|
69
|
+
/** One row of a numeric preset set. Narrower than `FilterOption` — a preset is never `disabled`:
|
|
70
|
+
* the set is declared at the call site, so a row that should not be offered is left out. */
|
|
71
|
+
export type NumberFilterOption = {
|
|
72
|
+
readonly value: number
|
|
73
|
+
readonly label: string
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type NumberFilterProps = BasaltProps & {
|
|
77
|
+
readonly field: FieldHandle<NumberField>
|
|
78
|
+
/** The popover/sheet heading and the accessible name — never the pill text, which reads the
|
|
79
|
+
* VALUE (see `EnumFilter`'s doc for why a pill may only print what is selected). */
|
|
80
|
+
readonly label: string
|
|
81
|
+
readonly icon?: ReactNode
|
|
82
|
+
/** Present → the radio-list form. Absent → the stepper form. */
|
|
83
|
+
readonly options?: readonly NumberFilterOption[]
|
|
84
|
+
/**
|
|
85
|
+
* The stepper's increment, overriding the field's own. Ignored in the `options` form, which has
|
|
86
|
+
* no stepper.
|
|
87
|
+
*
|
|
88
|
+
* @default `field.step` — the field's declared grain, which is `1` for an `int: true` field that
|
|
89
|
+
* declared none. Absent on both, the input keeps Mantine's own default of 1.
|
|
90
|
+
*/
|
|
91
|
+
readonly step?: number
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function NumberFilter(props: NumberFilterProps): ReactNode {
|
|
95
|
+
// F-ERR-1 — without this a missing `field` surfaces as `undefined is not an object
|
|
96
|
+
// (evaluating 'field.use')`, caught by `BasaltErrorBoundary`.
|
|
97
|
+
assertRequiredProps('NumberFilter', props, ['field'], {
|
|
98
|
+
field: 'bind it to a store field (`store.field.<name>`), never a value/onChange pair.',
|
|
99
|
+
})
|
|
100
|
+
const { field, label, icon, options, step, className, style } = props
|
|
101
|
+
// Two BRANCHES, two components, and never a conditional hook: the radio form's state lives in
|
|
102
|
+
// `EnumFilter` and the stepper's in `NumberStepper`, so which one mounts is decided once by a prop
|
|
103
|
+
// that does not change across a mount's life.
|
|
104
|
+
if (options !== undefined) {
|
|
105
|
+
return (
|
|
106
|
+
<NumberChoice
|
|
107
|
+
field={field}
|
|
108
|
+
label={label}
|
|
109
|
+
options={options}
|
|
110
|
+
{...(icon !== undefined && { icon })}
|
|
111
|
+
{...(className !== undefined && { className })}
|
|
112
|
+
{...(style !== undefined && { style })}
|
|
113
|
+
/>
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
// The prop wins over the field's declared grain; `field.step` is the default, so an `int` field
|
|
117
|
+
// steps by 1 with nothing written here (see `NumberFilterProps.step`).
|
|
118
|
+
const grain = step ?? field.step
|
|
119
|
+
return (
|
|
120
|
+
<NumberStepper
|
|
121
|
+
field={field}
|
|
122
|
+
label={label}
|
|
123
|
+
{...(icon !== undefined && { icon })}
|
|
124
|
+
{...(grain !== undefined && { step: grain })}
|
|
125
|
+
{...(className !== undefined && { className })}
|
|
126
|
+
{...(style !== undefined && { style })}
|
|
127
|
+
/>
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The `options` form. It renders no radio list of its own — it ADAPTS the number handle into
|
|
133
|
+
* `EnumFilter`'s `ChoiceHandle<string>` and hands it over, so the popover's `Radio.Group`, the
|
|
134
|
+
* sheet/panel `PanelChoice`, the registration and the pill readout are the ones already tested for
|
|
135
|
+
* `SelectFilter`/`CompareFilter` rather than a numeric copy of all four.
|
|
136
|
+
*
|
|
137
|
+
* The adapter is a projection, not a store: the URL still holds a NUMBER (`nights=3`, not `'3'`),
|
|
138
|
+
* because every write goes back through `setValue(Number(next))` into the same field codec.
|
|
139
|
+
*
|
|
140
|
+
* `field.use()` is called HERE, at a component's top level, and the adapter's own `use()` just
|
|
141
|
+
* returns what it read. The other way round — a `use()` that calls the real hook when `EnumFilter`
|
|
142
|
+
* invokes it — works at runtime (the call site is stable) but makes a plain object method a hook,
|
|
143
|
+
* which is a thing neither `react/rules-of-hooks` nor a reader should have to reason about.
|
|
144
|
+
*/
|
|
145
|
+
function NumberChoice({
|
|
146
|
+
field,
|
|
147
|
+
label,
|
|
148
|
+
icon,
|
|
149
|
+
options,
|
|
150
|
+
className,
|
|
151
|
+
style,
|
|
152
|
+
}: BasaltProps & {
|
|
153
|
+
readonly field: FieldHandle<NumberField>
|
|
154
|
+
readonly label: string
|
|
155
|
+
readonly icon?: ReactNode
|
|
156
|
+
readonly options: readonly NumberFilterOption[]
|
|
157
|
+
}): ReactNode {
|
|
158
|
+
const [value, setValue] = field.use()
|
|
159
|
+
const rows = options.map((option) => ({ value: String(option.value), label: option.label }))
|
|
160
|
+
const choice: ChoiceHandle<string> = {
|
|
161
|
+
fallback: String(field.fallback),
|
|
162
|
+
options: rows,
|
|
163
|
+
use: () =>
|
|
164
|
+
[
|
|
165
|
+
String(value),
|
|
166
|
+
(next: string) => {
|
|
167
|
+
setValue(Number(next))
|
|
168
|
+
},
|
|
169
|
+
] as const,
|
|
170
|
+
isDefault: (v) => field.isDefault(Number(v)),
|
|
171
|
+
clear: () => {
|
|
172
|
+
field.clear()
|
|
173
|
+
},
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// `options` passed explicitly as well as through the handle: `EnumFilter` reads the PROP first
|
|
177
|
+
// (the runtime-catalogue override), and passing the same rows both ways keeps the two agreeing
|
|
178
|
+
// whichever branch of that lookup a future edit takes.
|
|
179
|
+
return (
|
|
180
|
+
<EnumFilter
|
|
181
|
+
field={choice}
|
|
182
|
+
label={label}
|
|
183
|
+
numeric
|
|
184
|
+
options={rows}
|
|
185
|
+
{...(icon !== undefined && { icon })}
|
|
186
|
+
{...(className !== undefined && { className })}
|
|
187
|
+
{...(style !== undefined && { style })}
|
|
188
|
+
/>
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** The stepper form — a pill holding a `ctl` `NumberInput`, or a full-width sheet row. */
|
|
193
|
+
function NumberStepper({
|
|
194
|
+
field,
|
|
195
|
+
label,
|
|
196
|
+
icon,
|
|
197
|
+
step,
|
|
198
|
+
className,
|
|
199
|
+
style,
|
|
200
|
+
}: BasaltProps & {
|
|
201
|
+
readonly field: FieldHandle<NumberField>
|
|
202
|
+
readonly label: string
|
|
203
|
+
readonly icon?: ReactNode
|
|
204
|
+
readonly step?: number
|
|
205
|
+
}): ReactNode {
|
|
206
|
+
const [value, setValue] = field.use()
|
|
207
|
+
const surface = useFilterSurface()
|
|
208
|
+
const inSheet = surface === 'sheet'
|
|
209
|
+
const isDefault = field.isDefault(value)
|
|
210
|
+
useFilterRegistration(!isDefault, () => {
|
|
211
|
+
field.clear()
|
|
212
|
+
})
|
|
213
|
+
const inPanel = surface === 'panel'
|
|
214
|
+
const { labelId, nameProps } = useControlName(label, inSheet || inPanel)
|
|
215
|
+
const draft = useCommittedNumber(value, setValue, { min: field.min, max: field.max })
|
|
216
|
+
|
|
217
|
+
const input = (
|
|
218
|
+
<NumberInput
|
|
219
|
+
{...nameProps}
|
|
220
|
+
size="ctl"
|
|
221
|
+
// The FIELD's bounds, straight off the handle — so the stepper stops at the limit and a typed
|
|
222
|
+
// value cannot exceed it, instead of being silently corrected one commit later. The codec's
|
|
223
|
+
// clamp stays the backstop for everything that does not come through this box.
|
|
224
|
+
{...(field.min !== undefined && { min: field.min })}
|
|
225
|
+
{...(field.max !== undefined && { max: field.max })}
|
|
226
|
+
// `int: true` is refused by the codec, not rounded, so the input must refuse it too — a `.5`
|
|
227
|
+
// typed into an integer field would otherwise decode to `null` and resurrect the fallback.
|
|
228
|
+
{...(field.int && { allowDecimal: false })}
|
|
229
|
+
// Already resolved by `NumberFilter` — the prop, else the field's own grain.
|
|
230
|
+
{...(step !== undefined && { step })}
|
|
231
|
+
value={draft.value}
|
|
232
|
+
onChange={draft.set}
|
|
233
|
+
onBlur={draft.commit}
|
|
234
|
+
onKeyDown={(event) => {
|
|
235
|
+
if (event.key !== 'Enter') return
|
|
236
|
+
// A filter is not inside a form here, but a consumer's page may be — an Enter that submits
|
|
237
|
+
// the page instead of committing the threshold is the one failure mode worth pre-empting.
|
|
238
|
+
event.preventDefault()
|
|
239
|
+
draft.commit()
|
|
240
|
+
}}
|
|
241
|
+
/>
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
// The sheet form is the panel form (`docs/CONTROLS-SPEC.md` §3: "sheet = panel rows inside a
|
|
245
|
+
// Drawer") — the same `PanelRow` label-above-control row on both surfaces.
|
|
246
|
+
if (inPanel || inSheet) {
|
|
247
|
+
return (
|
|
248
|
+
<PanelRow
|
|
249
|
+
label={label}
|
|
250
|
+
labelId={labelId}
|
|
251
|
+
{...(className !== undefined && { className })}
|
|
252
|
+
{...(style !== undefined && { style })}
|
|
253
|
+
>
|
|
254
|
+
{input}
|
|
255
|
+
</PanelRow>
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
<FilterPill
|
|
261
|
+
label={String(value)}
|
|
262
|
+
numeric
|
|
263
|
+
active={!isDefault}
|
|
264
|
+
{...(icon !== undefined && { icon })}
|
|
265
|
+
{...(className !== undefined && { className })}
|
|
266
|
+
{...(style !== undefined && { style })}
|
|
267
|
+
>
|
|
268
|
+
<div className={classes.optionList}>{input}</div>
|
|
269
|
+
</FilterPill>
|
|
270
|
+
)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* A local draft that lands on the field only when the user is DONE with it (blur, Enter), and that
|
|
275
|
+
* follows the field when it changes from outside the input — `Reset all`, a back navigation, a deep
|
|
276
|
+
* link, and the codec's own clamp.
|
|
277
|
+
*
|
|
278
|
+
* The clamp is the interesting direction, and it is why `bounds` is a parameter rather than left to
|
|
279
|
+
* the codec. `commit` clamps to the field's OWN limits before it compares, so the readout follows
|
|
280
|
+
* the clamp even when the store does not move: a second out-of-range `9999` against `max: 600` over
|
|
281
|
+
* a stored `600` writes nothing (the value is already `600`), which means `[value]` never changes and
|
|
282
|
+
* the effect below never runs — and the input went on displaying `9999` over a URL holding `600`,
|
|
283
|
+
* exactly the readout this hook exists to prevent. Only the FIRST out-of-range commit was ever
|
|
284
|
+
* healed.
|
|
285
|
+
*
|
|
286
|
+
* `seen` (the number this hook last committed) plus the effect stay as the backstop for a change that
|
|
287
|
+
* did not come through this box at all — `Reset all`, a deep link, a back navigation — and for a
|
|
288
|
+
* codec clamp the bounds here cannot predict.
|
|
289
|
+
*/
|
|
290
|
+
function useCommittedNumber(
|
|
291
|
+
value: number,
|
|
292
|
+
setValue: (next: number) => void,
|
|
293
|
+
bounds: { min?: number | undefined; max?: number | undefined },
|
|
294
|
+
): { value: number | string; set: (next: number | string) => void; commit: () => void } {
|
|
295
|
+
const [draft, setDraft] = useState<number | string>(value)
|
|
296
|
+
const seen = useRef(value)
|
|
297
|
+
const commitRef = useRef(setValue)
|
|
298
|
+
commitRef.current = setValue
|
|
299
|
+
|
|
300
|
+
useEffect(() => {
|
|
301
|
+
if (seen.current === value) return
|
|
302
|
+
seen.current = value
|
|
303
|
+
setDraft(value)
|
|
304
|
+
}, [value])
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
value: draft,
|
|
308
|
+
set: setDraft,
|
|
309
|
+
commit: () => {
|
|
310
|
+
const parsed = typeof draft === 'number' ? draft : Number.parseFloat(draft)
|
|
311
|
+
// An empty or half-typed box ('', '-', '1e') is not a value — restore the field's, rather
|
|
312
|
+
// than committing `NaN` for the codec to fall back on and calling that the user's intent.
|
|
313
|
+
if (!Number.isFinite(parsed)) {
|
|
314
|
+
setDraft(value)
|
|
315
|
+
return
|
|
316
|
+
}
|
|
317
|
+
// Clamp to what the codec will actually store, then show THAT — a draft the store is about to
|
|
318
|
+
// refuse is a lie whether or not the store's value moves. `int` needs no handling: the input
|
|
319
|
+
// runs `allowDecimal: false` on an integer field, so a decimal never reaches here.
|
|
320
|
+
let next = parsed
|
|
321
|
+
if (bounds.min !== undefined && next < bounds.min) next = bounds.min
|
|
322
|
+
if (bounds.max !== undefined && next > bounds.max) next = bounds.max
|
|
323
|
+
setDraft(next)
|
|
324
|
+
if (next === value) return
|
|
325
|
+
seen.current = next
|
|
326
|
+
commitRef.current(next)
|
|
327
|
+
},
|
|
328
|
+
}
|
|
329
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The `panel` surface's row skeleton (`docs/ASIDE-SPEC.md` §1, §3) — the inspector archetype's
|
|
3
|
+
* geometry, and nothing else.
|
|
4
|
+
*
|
|
5
|
+
* **Label ABOVE, never beside (G4).** At an aside's ~300px a label, a control and a readout on one
|
|
6
|
+
* line leave the control ~90px, which for a 20-step slider is under 12px per step. So the row is two
|
|
7
|
+
* lines: a head line (label · hint · readout, or label · a `Switch`) and a full-width control line.
|
|
8
|
+
* The toggle is the one exception, because a switch beside a label costs nothing and a switch on its
|
|
9
|
+
* own line reads as a second control.
|
|
10
|
+
*
|
|
11
|
+
* Rows separate with a `--vx-divider` hairline rather than sitting in cards — Lightroom, not a
|
|
12
|
+
* dashboard (`docs/ASIDE-SPEC.md` §3). The vertical inset is `--vx-space-row-inset-y` (6), the
|
|
13
|
+
* INSPECTOR row's, deliberately not `--vx-space-settings-row-inset-y` (10), which belongs to a
|
|
14
|
+
* settings FORM row and is nearly twice the height in a column that has none to spare.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
.row {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
gap: var(--vx-space-stack-xs, 0.25rem);
|
|
21
|
+
min-width: 0;
|
|
22
|
+
padding-block: var(--vx-space-row-inset-y);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Between rows only — never above the first or below the last, where the aside's own section
|
|
26
|
+
* chrome already draws the boundary. */
|
|
27
|
+
.row + .row {
|
|
28
|
+
border-top: 1px solid var(--vx-divider);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* `disabled` dims the BODY, it does not remove it: an inspector row that is off still states what it
|
|
32
|
+
* would control, and the control inside keeps its own `disabled` semantics. */
|
|
33
|
+
.row[data-disabled] {
|
|
34
|
+
opacity: 0.55;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.head {
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
gap: var(--vx-space-control-gap, 0.375rem);
|
|
41
|
+
min-width: 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.label {
|
|
45
|
+
font-size: var(--vx-text-xs);
|
|
46
|
+
font-weight: 550;
|
|
47
|
+
color: var(--vx-ink);
|
|
48
|
+
overflow: hidden;
|
|
49
|
+
text-overflow: ellipsis;
|
|
50
|
+
white-space: nowrap;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/*
|
|
54
|
+
* The readout is MONO and right-aligned: it is a number that changes while a thumb is dragged, and a
|
|
55
|
+
* proportional face would reflow the head line on every frame. `margin-inline-start: auto` is what
|
|
56
|
+
* pushes it (or, in a toggle row, the `end` control) to the trailing edge without a spacer element.
|
|
57
|
+
*/
|
|
58
|
+
.readout {
|
|
59
|
+
margin-inline-start: auto;
|
|
60
|
+
flex: none;
|
|
61
|
+
font-family: var(--basalt-font-mono);
|
|
62
|
+
font-size: var(--vx-text-xs);
|
|
63
|
+
font-variant-numeric: tabular-nums;
|
|
64
|
+
color: var(--vx-muted);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.end {
|
|
68
|
+
display: inline-flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
flex: none;
|
|
71
|
+
margin-inline-start: auto;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* A readout AND an `end` control: the readout already took the auto margin, so the control just
|
|
75
|
+
* follows it across the gap. */
|
|
76
|
+
.readout + .end {
|
|
77
|
+
margin-inline-start: 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Column flex, so a control with no width of its own fills the row rather than shrinking to its
|
|
81
|
+
* content — the row IS the width, which is why no control in it carries a `w` (law C5). */
|
|
82
|
+
.control {
|
|
83
|
+
display: flex;
|
|
84
|
+
flex-direction: column;
|
|
85
|
+
min-width: 0;
|
|
86
|
+
}
|