basalt-ui 1.27.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 +59 -0
- package/MIGRATING.md +613 -20
- package/README.md +33 -3
- package/agent/rules/basalt-batteries.md +25 -10
- package/agent/rules/basalt-controls.md +56 -31
- package/agent/rules/basalt-mantine.md +17 -6
- package/agent/rules/basalt-tokens.md +1 -1
- package/configs/oxlint-plugin.js +1056 -28
- package/configs/oxlint-plugin.test.ts +1066 -0
- package/configs/oxlint.json +7 -0
- 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 +3 -2
- package/dist/controls/enum-filter.d.ts.map +1 -1
- package/dist/controls/enum-filter.js +28 -13
- package/dist/controls/filter-context.d.ts +8 -4
- 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 +2 -0
- package/dist/controls/index.d.ts.map +1 -1
- package/dist/controls/index.js +4 -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 +122 -20
- package/dist/controls/number-filter.d.ts +7 -5
- package/dist/controls/number-filter.d.ts.map +1 -1
- package/dist/controls/number-filter.js +40 -11
- 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 +2 -1
- package/dist/controls/range-filter.d.ts.map +1 -1
- package/dist/controls/range-filter.js +57 -35
- 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 +25 -10
- 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 +28 -11
- 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 +7 -1
- 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 +44 -38
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +55 -42
- package/dist/dashboard/stat-card.module.css +46 -31
- package/dist/dashboard/stat-card.module.css.d.ts +1 -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 +384 -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.map +1 -1
- package/dist/guard/index.js +26 -6
- 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/search-store.d.ts.map +1 -1
- package/dist/router-tanstack/search-store.js +1 -1
- 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 +23 -3
- package/dist/state/fields.d.ts.map +1 -1
- package/dist/state/fields.js +13 -2
- 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.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 +42 -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 +69 -4
- package/dist/widget-header/widget-header.d.ts.map +1 -1
- package/dist/widget-header/widget-header.js +42 -27
- package/dist/widget-header/widget-header.module.css +72 -7
- 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 +32 -16
- package/src/controls/filter-context.tsx +14 -6
- 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 +7 -0
- package/src/controls/multi-select-filter.tsx +177 -27
- package/src/controls/number-filter.tsx +61 -23
- 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 +76 -39
- package/src/controls/search-filter.tsx +30 -12
- 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 +36 -16
- 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 +13 -1
- package/src/dashboard/query-state.tsx +154 -84
- package/src/dashboard/settings-section.tsx +87 -31
- package/src/dashboard/stat-card.module.css +46 -31
- package/src/dashboard/stat-card.module.css.d.ts +1 -0
- package/src/dashboard/stat-card.tsx +185 -127
- 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 +919 -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 +73 -18
- package/src/index.ts +74 -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 +1 -0
- package/src/router-tanstack/search-store.ts +3 -10
- 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 +43 -10
- package/src/state/persisted-or-local.ts +50 -0
- package/src/state.ts +0 -9
- package/src/surfaces.ts +42 -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 +72 -7
- package/src/widget-header/widget-header.tsx +150 -71
- 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/range-filter.type-guard.test.d.ts +0 -2
- package/dist/controls/range-filter.type-guard.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/state/persisted.test.d.ts +0 -2
- package/dist/state/persisted.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 -1053
- 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 -137
- package/src/controls/range-filter.type-guard.test.tsx +0 -122
- 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 -385
- package/src/data/data-table.test.tsx +0 -434
- package/src/guard/check-source.test.ts +0 -3388
- package/src/guard/grace.test.ts +0 -158
- 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 -882
- package/src/router-tanstack/search-store.test.ts +0 -578
- 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 -512
- package/src/state/persisted.test.ts +0 -91
- 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
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `PanelRow` — the inspector/facet ROW every control renders on the `panel` surface, and on the
|
|
3
|
+
* mobile `sheet` surface too (`docs/ASIDE-SPEC.md` §1 "Inspector", §3; `docs/CONTROLS-SPEC.md` §3:
|
|
4
|
+
* "sheet = panel rows inside a Drawer") — a labelled block around one control, and a HOME: it wraps
|
|
5
|
+
* its slots in `CtlSlot`, so a control inside carries no `size` of its own (law C5).
|
|
6
|
+
*
|
|
7
|
+
* **Label above, never beside (G4).** At an aside's ~300px, label · control · readout on one line
|
|
8
|
+
* leaves the control about 90px — under 12px per step for a 20-step slider, which is why the CBBI
|
|
9
|
+
* page hand-rolled two-line rows before this existed. The ONE exception is `end`: a control that
|
|
10
|
+
* rides the label line because it is atomic and needs no width (a `Switch`).
|
|
11
|
+
*
|
|
12
|
+
* The skeleton is plain elements plus one CSS module — no Mantine `Group`/`Stack`. A row is drawn
|
|
13
|
+
* dozens of times in one column, and the hairline/inset law it carries (`.row + .row`) is a
|
|
14
|
+
* SIBLING relationship, which a `Stack`'s gap cannot express.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* <PanelRow label="Pi Cycle Top" hint="The 111DMA / 350DMA×2 crossover." readout="0.62">
|
|
18
|
+
* <SliderControl field={weights.field.piCycle} label="Pi Cycle Top" />
|
|
19
|
+
* </PanelRow>
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // A toggle: one line, the control on the label line, no control line at all.
|
|
23
|
+
* <PanelRow label="Reweighted" end={<Switch checked={on} onChange={…} />} />
|
|
24
|
+
*/
|
|
25
|
+
import { SegmentedControl, Select } from '@mantine/core'
|
|
26
|
+
import { useLayoutEffect, useRef, useState } from 'react'
|
|
27
|
+
import type { ReactNode, RefObject } from 'react'
|
|
28
|
+
import { cx } from '../common/props'
|
|
29
|
+
import type { BasaltProps } from '../common/props'
|
|
30
|
+
import { CtlSlot } from '../theme'
|
|
31
|
+
import { InfoGlyph } from '../widget-header/widget-header'
|
|
32
|
+
import classes from './panel-row.module.css'
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Past three options a panel row's choice is a `Select`, not a track. Same arithmetic as
|
|
36
|
+
* `ViewTabs`' phone form, against the aside's ~300px rather than a phone's: a four-segment track at
|
|
37
|
+
* that width gives each label ~60px, which truncates every word longer than "Previous".
|
|
38
|
+
*
|
|
39
|
+
* **The count, not the whole law.** Three options at three WORDS' worth of label is not the same
|
|
40
|
+
* claim as three options at three SENTENCES' worth — "Same period last year" clipped mid-word
|
|
41
|
+
* inside its equal third even at a set of exactly three. `PANEL_TRACK_MAX` is the cheap first gate;
|
|
42
|
+
* `useTrackFits` (below) is the actual width check that decides whether the track renders at all.
|
|
43
|
+
*/
|
|
44
|
+
export const PANEL_TRACK_MAX = 3
|
|
45
|
+
|
|
46
|
+
export type PanelRowProps = BasaltProps & {
|
|
47
|
+
/** The row's name. Rendered above the control, `xs`/550 — never inside it. */
|
|
48
|
+
readonly label: string
|
|
49
|
+
/** Info glyph + tooltip beside the label — the same affordance `WidgetHeader.info` draws. */
|
|
50
|
+
readonly hint?: string
|
|
51
|
+
/**
|
|
52
|
+
* The current value, mono and right-aligned on the label line. A slider's readout, a range's
|
|
53
|
+
* resolved window — anything the control itself does not print.
|
|
54
|
+
*/
|
|
55
|
+
readonly readout?: ReactNode
|
|
56
|
+
/**
|
|
57
|
+
* A control that rides the LABEL line instead of taking one of its own — a `Switch`, and by
|
|
58
|
+
* design almost nothing else. Anything with a width belongs in `children`.
|
|
59
|
+
*/
|
|
60
|
+
readonly end?: ReactNode
|
|
61
|
+
/** Dims the row. The control inside keeps its own `disabled` — this is the visual half. */
|
|
62
|
+
readonly disabled?: boolean
|
|
63
|
+
/**
|
|
64
|
+
* Stamped on the label so the control can point `aria-labelledby` at it. The CONTROL owns the id
|
|
65
|
+
* (it renders this row), so an id published downward could never reach it — see
|
|
66
|
+
* `useControlName`'s doc.
|
|
67
|
+
*/
|
|
68
|
+
readonly labelId?: string
|
|
69
|
+
/**
|
|
70
|
+
* When set, renders the label as a `<label htmlFor={htmlFor}>` instead of a bare `<span>`, so
|
|
71
|
+
* clicking the label text also activates the control — the affordance Mantine's own labelled
|
|
72
|
+
* inputs give for free. Pass the `end` control's id (a `Switch`, typically).
|
|
73
|
+
*/
|
|
74
|
+
readonly htmlFor?: string
|
|
75
|
+
/** The full-width control line. Omitted for a row whose control rides `end`. */
|
|
76
|
+
readonly children?: ReactNode
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function PanelRow({
|
|
80
|
+
label,
|
|
81
|
+
hint,
|
|
82
|
+
readout,
|
|
83
|
+
end,
|
|
84
|
+
disabled,
|
|
85
|
+
labelId,
|
|
86
|
+
htmlFor,
|
|
87
|
+
children,
|
|
88
|
+
className,
|
|
89
|
+
style,
|
|
90
|
+
}: PanelRowProps): ReactNode {
|
|
91
|
+
return (
|
|
92
|
+
<div
|
|
93
|
+
className={cx(classes.row, className)}
|
|
94
|
+
{...(disabled === true && { 'data-disabled': true })}
|
|
95
|
+
{...(style !== undefined && { style })}
|
|
96
|
+
>
|
|
97
|
+
{/* ONE slot for the whole row, not one per line: `CtlSlot`'s marker is `display: contents`,
|
|
98
|
+
so both lines stay flex children of `.row` and the tier reaches `end` and `children`
|
|
99
|
+
through a single provider. */}
|
|
100
|
+
<CtlSlot>
|
|
101
|
+
<div className={classes.head}>
|
|
102
|
+
{htmlFor !== undefined ? (
|
|
103
|
+
<label
|
|
104
|
+
htmlFor={htmlFor}
|
|
105
|
+
className={classes.label}
|
|
106
|
+
{...(labelId !== undefined && { id: labelId })}
|
|
107
|
+
>
|
|
108
|
+
{label}
|
|
109
|
+
</label>
|
|
110
|
+
) : (
|
|
111
|
+
<span className={classes.label} {...(labelId !== undefined && { id: labelId })}>
|
|
112
|
+
{label}
|
|
113
|
+
</span>
|
|
114
|
+
)}
|
|
115
|
+
{hint !== undefined && <InfoGlyph text={hint} />}
|
|
116
|
+
{readout !== undefined && <span className={classes.readout}>{readout}</span>}
|
|
117
|
+
{end !== undefined && <span className={classes.end}>{end}</span>}
|
|
118
|
+
</div>
|
|
119
|
+
{children !== undefined && <div className={classes.control}>{children}</div>}
|
|
120
|
+
</CtlSlot>
|
|
121
|
+
</div>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** One option as both surfaces render it — `disabled` is the `SegmentedControl` half. */
|
|
126
|
+
export type PanelChoiceOption = {
|
|
127
|
+
readonly value: string
|
|
128
|
+
readonly label: string
|
|
129
|
+
readonly disabled?: boolean
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export type PanelChoiceProps = {
|
|
133
|
+
/** The control's accessible name, as `useControlName` resolved it for this surface. */
|
|
134
|
+
readonly nameProps: { 'aria-labelledby': string } | { 'aria-label': string }
|
|
135
|
+
readonly value: string
|
|
136
|
+
readonly options: readonly PanelChoiceOption[]
|
|
137
|
+
/** Numeric labels (`7d` / `30d`) get the mono treatment `segmented-control.module.css` owns. */
|
|
138
|
+
readonly numeric?: boolean
|
|
139
|
+
readonly onChange: (next: string) => void
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The nearest ANCESTOR that actually occupies layout space — skipping every `display: contents`
|
|
144
|
+
* box on the way up. Every home in this package wraps its slot in `CtlSlot` (`theme/ctl-theme.tsx`)
|
|
145
|
+
* to reach the `ctl` tier, and `CtlSlot`'s own marker is a `display: contents` `Box` (deliberately —
|
|
146
|
+
* it must stay out of the slot's own flex/grid layout). A `display: contents` element generates NO
|
|
147
|
+
* box of its own, so its `clientWidth` is always `0` — reading `root.parentElement.clientWidth`
|
|
148
|
+
* directly would report "zero room" for every control mounted straight into a slot (`ViewTabs`'
|
|
149
|
+
* desktop form, `PageBar`'s `actions`), which is not the constraint being measured at all.
|
|
150
|
+
*/
|
|
151
|
+
function layoutParent(el: HTMLElement): HTMLElement | null {
|
|
152
|
+
let parent = el.parentElement
|
|
153
|
+
while (parent !== null && getComputedStyle(parent).display === 'contents') {
|
|
154
|
+
parent = parent.parentElement
|
|
155
|
+
}
|
|
156
|
+
return parent
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* A `SegmentedControl`'s own semantic class for a rendered option's label (`__staticSelector`,
|
|
161
|
+
* present on every Mantine styles-api part regardless of the CSS-module hash) — stable across a
|
|
162
|
+
* version bump the way a generated `_label_1pq5d_5` class is not.
|
|
163
|
+
*/
|
|
164
|
+
const TRACK_LABEL_SELECTOR = '.mantine-SegmentedControl-label'
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The authoritative check, meaningful only while the track is actually mounted — TWO signals, not
|
|
168
|
+
* one, because they catch two DIFFERENT ways a track fails to fit and neither subsumes the other:
|
|
169
|
+
*
|
|
170
|
+
* 1. **Root vs parent** (`root.offsetWidth > parent.clientWidth`) — the track's OWN root refuses to
|
|
171
|
+
* shrink below `min-width: max-content` (`theme/index.ts`'s `SegmentedControl.extend`), so a
|
|
172
|
+
* too-narrow ANCESTOR (a header's `actions` slot squeezing a non-`fullWidth` two-option track)
|
|
173
|
+
* shows up as the root overflowing its container.
|
|
174
|
+
* 2. **Per-label overflow** (`label.scrollWidth > label.clientWidth`) — the ctl-tier `fullWidth`
|
|
175
|
+
* label carries `width: 100%` (`theme/index.ts`, so it fills its equal `.control` share for
|
|
176
|
+
* ellipsis to work at all — `PanelChoice`'s doc). A `width: 100%` CHILD cannot contribute an
|
|
177
|
+
* intrinsic size to its ancestor's max-content computation (percentages are undefined until the
|
|
178
|
+
* container's own size is known, so the browser treats it as contributing ~nothing) — which is
|
|
179
|
+
* why `min-width: max-content` on the root measurably FAILS to grow past its available space at
|
|
180
|
+
* the phone tier: "Week"/"Absolute" clipped inside their equal column ("Wee", "Absolut") while
|
|
181
|
+
* `root.offsetWidth` still read as fitting its parent. Signal 1 cannot see this at all; only
|
|
182
|
+
* reading each rendered label directly can.
|
|
183
|
+
*
|
|
184
|
+
* Both signals require REAL layout, so `unknown` (parent not yet settled) takes priority over
|
|
185
|
+
* either — see `useTrackFits`'s own doc for why a `0` reading is not evidence of overflow.
|
|
186
|
+
*/
|
|
187
|
+
function measureMounted(root: HTMLElement, parent: HTMLElement): 'fits' | 'overflow' | 'unknown' {
|
|
188
|
+
if (parent.clientWidth === 0) return 'unknown'
|
|
189
|
+
if (root.offsetWidth > parent.clientWidth) return 'overflow'
|
|
190
|
+
const labels = root.querySelectorAll<HTMLElement>(TRACK_LABEL_SELECTOR)
|
|
191
|
+
for (const label of labels) {
|
|
192
|
+
if (label.scrollWidth > label.clientWidth) return 'overflow'
|
|
193
|
+
}
|
|
194
|
+
return 'fits'
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Fit-checks a full-width track: {@link PANEL_TRACK_MAX} (and `ViewTabs`' own `PHONE_TRACK_MAX`) is
|
|
199
|
+
* the cheap COUNT gate, not the whole law — three short labels and three long ones are not the same
|
|
200
|
+
* claim at a ~300px column or a phone sheet ("Same period last year" clipped mid-word inside its
|
|
201
|
+
* equal third even though the set was exactly three; "Week"/"Absolute" clipped inside their equal
|
|
202
|
+
* column at the phone tier even though the ROOT read as fitting its parent — see
|
|
203
|
+
* {@link measureMounted}'s doc for why that needs a second signal). This hook is the width gate
|
|
204
|
+
* underneath the count cap, shared so `PanelChoice` and `ViewTabs`' phone form cannot drift the way
|
|
205
|
+
* their two hand-rolled ternaries already had once (`PanelChoice`'s own doc).
|
|
206
|
+
*
|
|
207
|
+
* **A LIVE toggle, not a one-way latch — it reverts.** The moment the track overflows, `PanelChoice`
|
|
208
|
+
* swaps it for a `Select`, which un-mounts the only element `measureMounted` could otherwise read.
|
|
209
|
+
* Two refs survive that swap on purpose: `trackWidthRef` remembers the track's own last-measured
|
|
210
|
+
* width (refreshed live whenever it IS mounted, used only as a cheap pre-filter — see below — never
|
|
211
|
+
* as the final answer), and `parentRef` holds the layout parent found once, at the FIRST mount — the
|
|
212
|
+
* same DOM node hosts either form (`PanelChoice`/`ViewTabs` return one or the other as their sole
|
|
213
|
+
* element, never a wrapper that differs between them), so it is never itself removed by the swap.
|
|
214
|
+
* The `ResizeObserver` therefore never watches a node this hook is about to lose: it observes the
|
|
215
|
+
* PARENT for the whole hook's lifetime and the TRACK only while it exists.
|
|
216
|
+
*
|
|
217
|
+
* While `Select` is showing, an observer tick with room to spare (`parent.clientWidth` at or past
|
|
218
|
+
* the remembered `trackWidthRef`) SPECULATIVELY re-mounts the track rather than trying to predict
|
|
219
|
+
* the exact threshold from a cached number — the two-signal check above cannot be run without a
|
|
220
|
+
* live root, and a WRONG guess here is not user-visible: the second effect below re-verifies with
|
|
221
|
+
* `measureMounted` the instant the fresh root exists, inside the SAME `useLayoutEffect` pass, and
|
|
222
|
+
* reverts synchronously before paint if the speculation was wrong. Nothing here can loop — a
|
|
223
|
+
* `ResizeObserver` only fires again on a GENUINE subsequent size change, not because React
|
|
224
|
+
* re-rendered.
|
|
225
|
+
*
|
|
226
|
+
* A SECOND, dependency-free `useLayoutEffect` is what performs that verification (and, on the
|
|
227
|
+
* ordinary growth-first-detected-while-mounted path, is also what re-arms the observer on the
|
|
228
|
+
* track's own root once it exists) — the FIRST effect only runs once per `[countFits, dataKey]`,
|
|
229
|
+
* but the track itself mounts and unmounts every time `fits` changes, and a `ResizeObserver` has to
|
|
230
|
+
* be told about each new node explicitly; this second effect runs after every render and is a
|
|
231
|
+
* no-op unless `rootRef.current` is a node it is not already watching.
|
|
232
|
+
*
|
|
233
|
+
* The check is THREE-VALUED, not two — `unknown` alongside `fits`/`overflow` — because a
|
|
234
|
+
* `clientWidth` of `0` is not evidence of overflow, it is evidence the ancestor chain has not been
|
|
235
|
+
* LAID OUT yet: the aside animates its width in from 0 while `PageAside` claims the region
|
|
236
|
+
* (`docs/ASIDE-SPEC.md` §0), and every ancestor up to the aside panel itself reads `0` for one or
|
|
237
|
+
* more layout passes while that transition is in flight. `unknown` updates nothing — it is simply
|
|
238
|
+
* not yet an answer, and the observer keeps watching for the ancestor chain to settle.
|
|
239
|
+
*
|
|
240
|
+
* `useLayoutEffect` runs BEFORE the browser paints, so a CONFIDENT overflow reading is never
|
|
241
|
+
* actually shown as a track first: the synchronous `setState` for it lands before paint, not after.
|
|
242
|
+
*
|
|
243
|
+
* `dataKey` is a caller-built string identifying the CURRENT option set (not read inside the effect
|
|
244
|
+
* body, just what forces it to re-run — a fresh mount, a fresh `parentRef`/`trackWidthRef` — when
|
|
245
|
+
* the rendered labels change) — primitive on purpose, so it is an honest `useLayoutEffect`
|
|
246
|
+
* dependency rather than a fresh array reference on every render.
|
|
247
|
+
*/
|
|
248
|
+
export function useTrackFits(
|
|
249
|
+
countFits: boolean,
|
|
250
|
+
dataKey: string,
|
|
251
|
+
): { rootRef: RefObject<HTMLDivElement | null>; fits: boolean } {
|
|
252
|
+
const rootRef = useRef<HTMLDivElement>(null)
|
|
253
|
+
const [rootFits, setRootFits] = useState(true)
|
|
254
|
+
const observerRef = useRef<ResizeObserver | null>(null)
|
|
255
|
+
const parentRef = useRef<HTMLElement | null>(null)
|
|
256
|
+
const trackWidthRef = useRef(0)
|
|
257
|
+
const observedRootRef = useRef<HTMLElement | null>(null)
|
|
258
|
+
|
|
259
|
+
useLayoutEffect(() => {
|
|
260
|
+
if (!countFits) return
|
|
261
|
+
const root = rootRef.current
|
|
262
|
+
if (root === null) return
|
|
263
|
+
const parent = layoutParent(root)
|
|
264
|
+
if (parent === null) return
|
|
265
|
+
parentRef.current = parent
|
|
266
|
+
trackWidthRef.current = root.offsetWidth
|
|
267
|
+
|
|
268
|
+
const resolve = (): void => {
|
|
269
|
+
const p = parentRef.current
|
|
270
|
+
if (p === null) return
|
|
271
|
+
const liveRoot = rootRef.current
|
|
272
|
+
|
|
273
|
+
if (liveRoot !== null) {
|
|
274
|
+
const result = measureMounted(liveRoot, p)
|
|
275
|
+
trackWidthRef.current = liveRoot.offsetWidth
|
|
276
|
+
if (result === 'unknown') return
|
|
277
|
+
const fits = result === 'fits'
|
|
278
|
+
setRootFits((current) => (current === fits ? current : fits))
|
|
279
|
+
return
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// `Select` is mounted — speculate rather than measure (see this hook's own doc). No-op
|
|
283
|
+
// while shrinking or unmeasured: only an actual increase past the last known track width is
|
|
284
|
+
// worth a remount attempt.
|
|
285
|
+
if (p.clientWidth === 0 || trackWidthRef.current === 0) return
|
|
286
|
+
if (p.clientWidth >= trackWidthRef.current) setRootFits(true)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (typeof ResizeObserver === 'undefined') {
|
|
290
|
+
// No recovery path exists without an observer, so a same-tick confident read is the only
|
|
291
|
+
// chance this environment gets.
|
|
292
|
+
resolve()
|
|
293
|
+
return
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const observer = new ResizeObserver(resolve)
|
|
297
|
+
observerRef.current = observer
|
|
298
|
+
observer.observe(parent)
|
|
299
|
+
observer.observe(root)
|
|
300
|
+
observedRootRef.current = root
|
|
301
|
+
|
|
302
|
+
// Resolve once, synchronously, so a track that is ALREADY confidently measurable (its
|
|
303
|
+
// ancestors settled before this component even mounted — the common case) decides before
|
|
304
|
+
// paint instead of waiting for the observer's first async tick.
|
|
305
|
+
resolve()
|
|
306
|
+
|
|
307
|
+
return () => {
|
|
308
|
+
observer.disconnect()
|
|
309
|
+
observerRef.current = null
|
|
310
|
+
observedRootRef.current = null
|
|
311
|
+
}
|
|
312
|
+
}, [countFits, dataKey])
|
|
313
|
+
|
|
314
|
+
// Re-arms the SAME observer on a fresh track root whenever one (re)mounts, AND is what actually
|
|
315
|
+
// VERIFIES a speculative remount (see this hook's own doc) — a wrong guess reverts here,
|
|
316
|
+
// synchronously, before this render ever paints. A no-op on every render where the track is
|
|
317
|
+
// absent (`Select` showing) or already the node being watched — deliberately NO dependency
|
|
318
|
+
// array, since a `ResizeObserver` has to be told about each fresh root explicitly and a fresh
|
|
319
|
+
// root can appear on any render. Cannot loop: every `setRootFits` call below is a functional
|
|
320
|
+
// update gated on the value actually CHANGING, so a render this effect does not alter never
|
|
321
|
+
// re-triggers it.
|
|
322
|
+
// oxlint-disable-next-line react-hooks/exhaustive-deps -- deliberately runs every render, see above
|
|
323
|
+
useLayoutEffect(() => {
|
|
324
|
+
const observer = observerRef.current
|
|
325
|
+
const root = rootRef.current
|
|
326
|
+
const parent = parentRef.current
|
|
327
|
+
if (observer === null || root === null || observedRootRef.current === root) return
|
|
328
|
+
if (observedRootRef.current !== null) observer.unobserve(observedRootRef.current)
|
|
329
|
+
observer.observe(root)
|
|
330
|
+
observedRootRef.current = root
|
|
331
|
+
trackWidthRef.current = root.offsetWidth
|
|
332
|
+
|
|
333
|
+
if (parent === null) return
|
|
334
|
+
const result = measureMounted(root, parent)
|
|
335
|
+
if (result === 'unknown') return
|
|
336
|
+
const fits = result === 'fits'
|
|
337
|
+
setRootFits((current) => (current === fits ? current : fits))
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
return { rootRef, fits: countFits && rootFits }
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* The single-choice control a panel row draws: a full-width track while the set fits both
|
|
345
|
+
* {@link PANEL_TRACK_MAX} and the width check {@link useTrackFits} runs, a `Select` past either.
|
|
346
|
+
*
|
|
347
|
+
* Internal, and one component rather than the same ternary in each control: `EnumFilter` and
|
|
348
|
+
* `ViewTabs` both wrote it out and had already drifted — `ViewTabs`' copy lost `data-numeric`, so a
|
|
349
|
+
* numeric tab set rendered proportional in the aside and mono everywhere else. The split point is
|
|
350
|
+
* the geometry of a ~300px column, which is a property of the ROW, not of any one filter, so it
|
|
351
|
+
* belongs beside the row that imposes it.
|
|
352
|
+
*/
|
|
353
|
+
export function PanelChoice({
|
|
354
|
+
nameProps,
|
|
355
|
+
value,
|
|
356
|
+
options,
|
|
357
|
+
numeric,
|
|
358
|
+
onChange,
|
|
359
|
+
}: PanelChoiceProps): ReactNode {
|
|
360
|
+
const data = options.map((option) => ({
|
|
361
|
+
value: option.value,
|
|
362
|
+
label: option.label,
|
|
363
|
+
disabled: option.disabled === true,
|
|
364
|
+
}))
|
|
365
|
+
const countFits = options.length <= PANEL_TRACK_MAX
|
|
366
|
+
const { rootRef, fits } = useTrackFits(countFits, data.map((d) => d.value).join('|'))
|
|
367
|
+
|
|
368
|
+
if (fits) {
|
|
369
|
+
return (
|
|
370
|
+
<SegmentedControl
|
|
371
|
+
ref={rootRef}
|
|
372
|
+
{...nameProps}
|
|
373
|
+
fullWidth
|
|
374
|
+
value={value}
|
|
375
|
+
data={data}
|
|
376
|
+
{...(numeric === true && { 'data-numeric': true })}
|
|
377
|
+
onChange={onChange}
|
|
378
|
+
/>
|
|
379
|
+
)
|
|
380
|
+
}
|
|
381
|
+
return (
|
|
382
|
+
<Select
|
|
383
|
+
{...nameProps}
|
|
384
|
+
value={value}
|
|
385
|
+
allowDeselect={false}
|
|
386
|
+
data={data}
|
|
387
|
+
onChange={(next) => {
|
|
388
|
+
if (next !== null) onChange(next)
|
|
389
|
+
}}
|
|
390
|
+
/>
|
|
391
|
+
)
|
|
392
|
+
}
|
|
@@ -10,11 +10,12 @@
|
|
|
10
10
|
* `theme/segmented-control.module.css` owns; that attribute is what retired the per-consumer
|
|
11
11
|
* `theme-allow` + inline `fontFamily` hack (C7).
|
|
12
12
|
*
|
|
13
|
-
* In the
|
|
14
|
-
* `
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
13
|
+
* In the PANEL and the SHEET (`docs/CONTROLS-SPEC.md` §3: "sheet = panel rows inside a Drawer")
|
|
14
|
+
* they render as one `Select` — label above, the presets plus a `Custom range…` ROW inside the same
|
|
15
|
+
* list, which reveals the SAME injected picker underneath. A track cannot hold five date presets at
|
|
16
|
+
* ~300px (or across a bottom drawer, where a vertical `SegmentedControl` read as a broken control
|
|
17
|
+
* rather than a choice), and a second control beside the choice would cost the column — or an
|
|
18
|
+
* unconditional calendar pushed below every OTHER filter in the sheet — a row it does not have.
|
|
18
19
|
*
|
|
19
20
|
* The custom picker is INJECTED, never imported: `basalt-ui/controls-dates` holds the
|
|
20
21
|
* `@mantine/dates` implementation, and this module must resolve for a consumer who has no
|
|
@@ -26,27 +27,28 @@
|
|
|
26
27
|
* // range: field.range({ presets: ['7d', '30d', '90d', 'ytd'], fallback: '30d', custom: true })
|
|
27
28
|
* <RangeFilter field={analytics.field.range} customPicker={DateRangePicker} />
|
|
28
29
|
*/
|
|
29
|
-
import { SegmentedControl, Stack } from '@mantine/core'
|
|
30
|
-
import { useState } from 'react'
|
|
30
|
+
import { SegmentedControl, Select, Stack } from '@mantine/core'
|
|
31
|
+
import { useEffect, useRef, useState } from 'react'
|
|
31
32
|
import type { ComponentType, ReactNode } from 'react'
|
|
33
|
+
import type { BasaltProps } from '../common/props'
|
|
34
|
+
import { assertRequiredProps } from '../common/validate'
|
|
35
|
+
import { isDev } from '../utils/is-dev'
|
|
32
36
|
import type { FieldHandle, RangeField, RangeValue } from '../state'
|
|
33
37
|
import classes from './controls.module.css'
|
|
34
38
|
import { useFilterRegistration, useFilterSurface } from './filter-context'
|
|
35
39
|
import { FilterPill } from './filter-pill'
|
|
36
40
|
import { CalendarGlyph } from './glyphs'
|
|
37
|
-
import {
|
|
41
|
+
import { useControlName } from './filter-sheet'
|
|
42
|
+
import { PanelRow } from './panel-row'
|
|
38
43
|
|
|
39
44
|
/** Past four presets the track goes vertical. */
|
|
40
45
|
const VERTICAL_FROM = 5
|
|
46
|
+
/** The panel `Select`'s last row — the same wording the sheet's disclosure uses, and the same
|
|
47
|
+
* `'custom'` preset value the field already declares, so no synthetic option value exists. */
|
|
48
|
+
const CUSTOM_ROW_LABEL = 'Custom range…'
|
|
41
49
|
/** `7d`, `30d`, `24h`, `90` — a label the mono numeric treatment is for. */
|
|
42
50
|
const NUMERIC_LABEL = /^\d/
|
|
43
51
|
|
|
44
|
-
/** The house dev gate — `basaltViteConfig` defines `process.env.NODE_ENV`, so a production bundle
|
|
45
|
-
* constant-folds this to `false` and drops the warning. Read per call, never hoisted. */
|
|
46
|
-
function isDev(): boolean {
|
|
47
|
-
return process.env['NODE_ENV'] !== 'production'
|
|
48
|
-
}
|
|
49
|
-
|
|
50
52
|
/** Once per label — a filter renders on every navigation, and the wiring is fixed at definition. */
|
|
51
53
|
const noPickerWarned = new Set<string>()
|
|
52
54
|
|
|
@@ -80,7 +82,7 @@ export type RangeCustomPickerProps = {
|
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
/** Everything about the props that does not depend on the field's `custom` flag. */
|
|
83
|
-
type RangeFilterBase<P extends string, C extends boolean> = {
|
|
85
|
+
type RangeFilterBase<P extends string, C extends boolean> = BasaltProps & {
|
|
84
86
|
/**
|
|
85
87
|
* A range handle from either store factory. Generic over the field's `custom` flag so a
|
|
86
88
|
* `field.range({ ... })` WITHOUT `custom` (whose handle is `RangeField<P, false>`, and whose
|
|
@@ -110,11 +112,18 @@ export type RangeFilterProps<P extends string, C extends boolean = boolean> = Ra
|
|
|
110
112
|
export function RangeFilter<P extends string, C extends boolean = boolean>(
|
|
111
113
|
props: RangeFilterProps<P, C>,
|
|
112
114
|
): ReactNode {
|
|
115
|
+
// F-ERR-1 — without this a missing `field` surfaces as `undefined is not an object
|
|
116
|
+
// (evaluating 'field.use')`, caught by `BasaltErrorBoundary`.
|
|
117
|
+
assertRequiredProps('RangeFilter', props, ['field'], {
|
|
118
|
+
field: 'bind it to a store field (`store.field.<name>`), never a value/onChange pair.',
|
|
119
|
+
})
|
|
113
120
|
const {
|
|
114
121
|
field,
|
|
115
122
|
icon,
|
|
116
123
|
customPicker,
|
|
117
124
|
label = 'Range',
|
|
125
|
+
className,
|
|
126
|
+
style,
|
|
118
127
|
// One cast, at the one boundary a generic `C` cannot be read through: `C` only ever describes
|
|
119
128
|
// the FIELD's custom flag, and the body handles both.
|
|
120
129
|
} = props as unknown as RangeFilterBase<P, boolean> & {
|
|
@@ -132,49 +141,75 @@ export function RangeFilter<P extends string, C extends boolean = boolean>(
|
|
|
132
141
|
const allowsCustom = options.length !== presets.length
|
|
133
142
|
const numeric = presets.length > 0 && presets.every((option) => NUMERIC_LABEL.test(option.label))
|
|
134
143
|
const inSheet = surface === 'sheet'
|
|
144
|
+
const inPanel = surface === 'panel'
|
|
135
145
|
const Picker = customPicker
|
|
136
146
|
warnCustomWithoutPicker(label, allowsCustom && Picker === undefined)
|
|
137
147
|
// The track is a `radiogroup`; without this it announces unnamed, and the pill it hangs off reads
|
|
138
148
|
// the VALUE (`30d`), not the filter.
|
|
139
|
-
const { labelId, nameProps } = useControlName(label, inSheet)
|
|
149
|
+
const { labelId, nameProps } = useControlName(label, inSheet || inPanel)
|
|
140
150
|
// Expanded when the field ALREADY holds a custom window — a reader who deep-linked one must see
|
|
141
151
|
// it without hunting for the row that holds it. Local, because it is an overlay's open flag and
|
|
142
152
|
// not a filter value (the same line `FilterPill`'s `opened` state draws).
|
|
143
153
|
const [customOpen, setCustomOpen] = useState(value.preset === 'custom')
|
|
154
|
+
// …and RE-SYNCED whenever the preset moves under it. Seeded once, the flag survived a write this
|
|
155
|
+
// control did not make: a `Reset all` or a `field.clear()` from a `FilterSet` put the field back
|
|
156
|
+
// on `30d` while the panel's Select still read `Custom range…` with the picker open beneath it.
|
|
157
|
+
// Tracked against the LAST SEEN preset (`search-filter.tsx`'s `useDebouncedField` idiom) rather
|
|
158
|
+
// than against `value.preset` alone, because the just-clicked `Custom range…` transient is
|
|
159
|
+
// exactly the state where the flag is true and the preset has not moved yet.
|
|
160
|
+
const seenPreset = useRef(value.preset)
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
if (seenPreset.current === value.preset) return
|
|
163
|
+
seenPreset.current = value.preset
|
|
164
|
+
setCustomOpen(value.preset === 'custom')
|
|
165
|
+
}, [value.preset])
|
|
144
166
|
const showPicker = allowsCustom && Picker !== undefined
|
|
145
167
|
|
|
146
|
-
|
|
168
|
+
// The sheet form is the panel form (`docs/CONTROLS-SPEC.md` §3: "sheet = panel rows inside a
|
|
169
|
+
// Drawer") — one `Select` holding the presets plus a `Custom range…` row, which reveals the same
|
|
170
|
+
// injected picker underneath. It used to be a `SheetOptionList` with the picker behind a separate
|
|
171
|
+
// `SheetDisclosure` row; the `Select` already folds both into one control, so the disclosure had
|
|
172
|
+
// no job left.
|
|
173
|
+
if (inPanel || inSheet) {
|
|
147
174
|
return (
|
|
148
|
-
<
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
175
|
+
<PanelRow
|
|
176
|
+
label={label}
|
|
177
|
+
labelId={labelId}
|
|
178
|
+
{...(className !== undefined && { className })}
|
|
179
|
+
{...(style !== undefined && { style })}
|
|
180
|
+
>
|
|
181
|
+
<Select
|
|
182
|
+
{...nameProps}
|
|
183
|
+
allowDeselect={false}
|
|
184
|
+
// `customOpen` is what the row reads while the picker is open but no window has been
|
|
185
|
+
// picked yet — selecting `Custom range…` reveals the calendar, it does not write a
|
|
186
|
+
// half-built `{ preset: 'custom' }` the codec would have to guess at.
|
|
187
|
+
value={customOpen ? 'custom' : value.preset}
|
|
188
|
+
data={[
|
|
189
|
+
...presets.map((option) => ({ value: option.value, label: option.label })),
|
|
190
|
+
...(showPicker ? [{ value: 'custom', label: CUSTOM_ROW_LABEL }] : []),
|
|
191
|
+
]}
|
|
192
|
+
onChange={(next) => {
|
|
193
|
+
if (next === null) return
|
|
194
|
+
if (next === 'custom') {
|
|
195
|
+
setCustomOpen(true)
|
|
196
|
+
return
|
|
197
|
+
}
|
|
155
198
|
setCustomOpen(false)
|
|
156
199
|
// Rendered from `field.options`, so `next` is always a declared preset — the cast
|
|
157
200
|
// restores what the codec already guarantees.
|
|
158
201
|
setValue({ preset: next } as RangeValue<P | 'custom'>)
|
|
159
202
|
}}
|
|
160
203
|
/>
|
|
161
|
-
{showPicker && (
|
|
162
|
-
<
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
setCustomOpen((open) => !open)
|
|
204
|
+
{showPicker && customOpen && (
|
|
205
|
+
<Picker
|
|
206
|
+
value={{ from: value.from, to: value.to }}
|
|
207
|
+
onChange={({ from, to }) => {
|
|
208
|
+
setValue({ preset: 'custom', from, to } as RangeValue<P | 'custom'>)
|
|
167
209
|
}}
|
|
168
|
-
|
|
169
|
-
<Picker
|
|
170
|
-
value={{ from: value.from, to: value.to }}
|
|
171
|
-
onChange={({ from, to }) => {
|
|
172
|
-
setValue({ preset: 'custom', from, to } as RangeValue<P | 'custom'>)
|
|
173
|
-
}}
|
|
174
|
-
/>
|
|
175
|
-
</SheetDisclosure>
|
|
210
|
+
/>
|
|
176
211
|
)}
|
|
177
|
-
</
|
|
212
|
+
</PanelRow>
|
|
178
213
|
)
|
|
179
214
|
}
|
|
180
215
|
|
|
@@ -210,6 +245,8 @@ export function RangeFilter<P extends string, C extends boolean = boolean>(
|
|
|
210
245
|
active={!isDefault}
|
|
211
246
|
numeric={numeric && value.preset !== 'custom'}
|
|
212
247
|
icon={icon ?? <CalendarGlyph />}
|
|
248
|
+
{...(className !== undefined && { className })}
|
|
249
|
+
{...(style !== undefined && { style })}
|
|
213
250
|
>
|
|
214
251
|
<div className={classes.optionList}>{body}</div>
|
|
215
252
|
</FilterPill>
|
|
@@ -15,25 +15,30 @@
|
|
|
15
15
|
import { TextInput } from '@mantine/core'
|
|
16
16
|
import { useEffect, useRef, useState } from 'react'
|
|
17
17
|
import type { ReactNode } from 'react'
|
|
18
|
+
import type { BasaltProps } from '../common/props'
|
|
19
|
+
import { assertRequiredProps } from '../common/validate'
|
|
18
20
|
import type { FieldHandle, StringField } from '../state'
|
|
19
21
|
import { useFilterRegistration, useFilterSurface } from './filter-context'
|
|
20
|
-
import {
|
|
22
|
+
import { useControlName } from './filter-sheet'
|
|
23
|
+
import { PanelRow } from './panel-row'
|
|
21
24
|
|
|
22
25
|
/** One navigation per typed phrase, not per keystroke. */
|
|
23
26
|
const DEBOUNCE_MS = 200
|
|
24
27
|
|
|
25
|
-
export type SearchFilterProps = {
|
|
28
|
+
export type SearchFilterProps = BasaltProps & {
|
|
26
29
|
readonly field: FieldHandle<StringField>
|
|
27
30
|
readonly placeholder?: string
|
|
28
31
|
/** Sheet-form heading. @default 'Search' */
|
|
29
32
|
readonly label?: string
|
|
30
33
|
}
|
|
31
34
|
|
|
32
|
-
export function SearchFilter({
|
|
33
|
-
field
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
export function SearchFilter(props: SearchFilterProps): ReactNode {
|
|
36
|
+
// F-ERR-1 — without this a missing `field` surfaces as `undefined is not an object
|
|
37
|
+
// (evaluating 'field.use')`, caught by `BasaltErrorBoundary`.
|
|
38
|
+
assertRequiredProps('SearchFilter', props, ['field'], {
|
|
39
|
+
field: 'bind it to a store field (`store.field.<name>`), never a value/onChange pair.',
|
|
40
|
+
})
|
|
41
|
+
const { field, placeholder, label = 'Search', className, style } = props
|
|
37
42
|
const [value, setValue] = field.use()
|
|
38
43
|
const surface = useFilterSurface()
|
|
39
44
|
const draft = useDebouncedField(value, setValue)
|
|
@@ -42,8 +47,12 @@ export function SearchFilter({
|
|
|
42
47
|
})
|
|
43
48
|
|
|
44
49
|
// `placeholder` is optional and, even when set, stops being the name the moment the user types —
|
|
45
|
-
// so the name comes from `label`, or from the
|
|
46
|
-
const
|
|
50
|
+
// so the name comes from `label`, or from the visible heading when the surface draws one.
|
|
51
|
+
const named = surface === 'sheet' || surface === 'panel'
|
|
52
|
+
const { labelId, nameProps } = useControlName(label, named)
|
|
53
|
+
// The pill form has no wrapping home — the `TextInput` itself is the root, so it takes the class
|
|
54
|
+
// directly. The panel/sheet forms wrap it in a home that becomes the root instead.
|
|
55
|
+
const isPill = surface !== 'panel' && surface !== 'sheet'
|
|
47
56
|
|
|
48
57
|
const input = (
|
|
49
58
|
<TextInput
|
|
@@ -55,14 +64,23 @@ export function SearchFilter({
|
|
|
55
64
|
onChange={(event) => {
|
|
56
65
|
draft.set(event.currentTarget.value)
|
|
57
66
|
}}
|
|
67
|
+
{...(isPill && className !== undefined && { className })}
|
|
68
|
+
{...(isPill && style !== undefined && { style })}
|
|
58
69
|
/>
|
|
59
70
|
)
|
|
60
71
|
|
|
61
|
-
|
|
72
|
+
// The sheet form is the panel form (`docs/CONTROLS-SPEC.md` §3: "sheet = panel rows inside a
|
|
73
|
+
// Drawer") — the same `PanelRow` label-above-control row on both surfaces.
|
|
74
|
+
if (surface === 'panel' || surface === 'sheet') {
|
|
62
75
|
return (
|
|
63
|
-
<
|
|
76
|
+
<PanelRow
|
|
77
|
+
label={label}
|
|
78
|
+
labelId={labelId}
|
|
79
|
+
{...(className !== undefined && { className })}
|
|
80
|
+
{...(style !== undefined && { style })}
|
|
81
|
+
>
|
|
64
82
|
{input}
|
|
65
|
-
</
|
|
83
|
+
</PanelRow>
|
|
66
84
|
)
|
|
67
85
|
}
|
|
68
86
|
return input
|