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
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, DOM-free layout math behind `ChartFrame`'s plot/legend split — extracted so the arithmetic
|
|
3
|
+
* is testable without a `ResizeObserver` and `ChartFrame.tsx` stays the measuring/rendering shell.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { VX } from '../../tokens'
|
|
7
|
+
import type { ChartMargin } from '../../tokens'
|
|
8
|
+
import type { LegendEntry } from './ChartLegend'
|
|
9
|
+
import { measureText } from '../utils/measure-text'
|
|
10
|
+
|
|
11
|
+
/** Line box of one legend row at `VX.legendFontSize`. */
|
|
12
|
+
const LEGEND_LINE_H = Math.ceil(VX.legendFontSize * 1.35)
|
|
13
|
+
/** `ChartLegend`'s own vertical wrapper padding (`8px 0 2px`). */
|
|
14
|
+
const LEGEND_PAD_Y = 10
|
|
15
|
+
/** Swatch plus the gap to its label — the fixed part of a legend entry's width. */
|
|
16
|
+
const LEGEND_SWATCH_W = 24
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The two size tiers a chart resolves to. There are exactly two, and there is no `tablet`: the
|
|
20
|
+
* tier exists to answer "is there room for full-size chrome around the plot", which is a yes/no
|
|
21
|
+
* question, and a third rung would need a third calibrated metric set nothing has asked for.
|
|
22
|
+
*/
|
|
23
|
+
export type ChartTier = 'phone' | 'desktop'
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The tier a chart's chrome resolves to, from the width of the box it was MEASURED in — never
|
|
27
|
+
* from a media query.
|
|
28
|
+
*
|
|
29
|
+
* A viewport breakpoint answers the wrong question. A chart in a 2-column grid cell on a 1440px
|
|
30
|
+
* desktop is exactly as narrow as one filling a phone, and `@media` cannot see that; the
|
|
31
|
+
* `ResizeObserver` `ChartFrame` already runs can. It also keeps this file (and the whole chart
|
|
32
|
+
* layer) Mantine-free — `theme.breakpoints` is on the coupled side of the boundary.
|
|
33
|
+
*
|
|
34
|
+
* An UNMEASURED box (`containerW <= 0` — SSR, or before the observer's first callback) resolves
|
|
35
|
+
* to `'desktop'`: the first frame must not paint phone chrome that then re-lays-out one frame
|
|
36
|
+
* later, and `resolvePlotRect` already treats an unmeasured width as the first-frame case.
|
|
37
|
+
*/
|
|
38
|
+
export function resolveChartTier(containerW: number): ChartTier {
|
|
39
|
+
return containerW > 0 && containerW < VX.phoneChartWidth ? 'phone' : 'desktop'
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* How much of `VX.margin` a phone-tier chart keeps as its FLOOR. The measured law is unchanged —
|
|
44
|
+
* a side may still only grow past its floor (`autoMargin`) — this only stops a static token from
|
|
45
|
+
* spending 44px of a 360px chart on a gutter three characters wide.
|
|
46
|
+
*/
|
|
47
|
+
const PHONE_MARGIN_SCALE = 0.75
|
|
48
|
+
|
|
49
|
+
/** Phone-tier crosshair dot radius. One step in, so the marker still reads as a punched hole at
|
|
50
|
+
* the smaller stroke widths a narrow chart draws. */
|
|
51
|
+
const PHONE_DOT_R = Math.max(VX.dotR - 1, 2)
|
|
52
|
+
|
|
53
|
+
/** Legend entries a phone-tier legend renders before rolling the rest into `+N more`. Two is the
|
|
54
|
+
* cap because a third entry wraps at every realistic phone width, and a wrapped legend is what
|
|
55
|
+
* eats the plot (`legendEntryCap`). */
|
|
56
|
+
const PHONE_LEGEND_MAX_ROWS = 2
|
|
57
|
+
|
|
58
|
+
/** Tooltip `minWidth`, per tier — 140px is 39% of a 360px screen. */
|
|
59
|
+
const TOOLTIP_MIN_WIDTH = { desktop: 140, phone: 110 } as const
|
|
60
|
+
|
|
61
|
+
/** Every size a chart's chrome resolves per tier. One object so a new tier-sensitive size is added
|
|
62
|
+
* in one place and read by name, rather than each primitive branching on the tier itself. */
|
|
63
|
+
export type ChartTierMetrics = {
|
|
64
|
+
/** Which tier these are, so a consumer of the metrics never needs both values threaded. */
|
|
65
|
+
tier: ChartTier
|
|
66
|
+
/** Axis tick label font size, px. Threaded into BOTH `autoMargin`'s measurement and the painted
|
|
67
|
+
* axis — measured labels must be the painted labels (`docs/CHARTS-SPEC.md` §1). */
|
|
68
|
+
axisFont: number
|
|
69
|
+
/** Legend label font size, px. */
|
|
70
|
+
legendFontSize: number
|
|
71
|
+
/** Crosshair dot radius, px. */
|
|
72
|
+
dotR: number
|
|
73
|
+
/** Floating tooltip `minWidth`, px. */
|
|
74
|
+
tooltipMinWidth: number
|
|
75
|
+
/** Default entry cap on the legend, or `undefined` for no default cap. An explicit
|
|
76
|
+
* `legend.maxRows` still wins as the upper bound. */
|
|
77
|
+
legendMaxRows: number | undefined
|
|
78
|
+
/** Per-side margin FLOORS (`VX.margin` is a floor, never a ceiling — §1). */
|
|
79
|
+
margin: ChartMargin
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const DESKTOP_METRICS: ChartTierMetrics = {
|
|
83
|
+
tier: 'desktop',
|
|
84
|
+
axisFont: VX.axisFont,
|
|
85
|
+
legendFontSize: VX.legendFontSize,
|
|
86
|
+
dotR: VX.dotR,
|
|
87
|
+
tooltipMinWidth: TOOLTIP_MIN_WIDTH.desktop,
|
|
88
|
+
legendMaxRows: undefined,
|
|
89
|
+
margin: VX.margin,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const PHONE_METRICS: ChartTierMetrics = {
|
|
93
|
+
tier: 'phone',
|
|
94
|
+
// One step DOWN the shared type ladder, never an arbitrary px value: micro → nano for ticks,
|
|
95
|
+
// sm → xs for the legend (`TEXT` in tokens/index.ts).
|
|
96
|
+
axisFont: VX.text.nano,
|
|
97
|
+
legendFontSize: VX.text.xs,
|
|
98
|
+
dotR: PHONE_DOT_R,
|
|
99
|
+
tooltipMinWidth: TOOLTIP_MIN_WIDTH.phone,
|
|
100
|
+
legendMaxRows: PHONE_LEGEND_MAX_ROWS,
|
|
101
|
+
margin: {
|
|
102
|
+
top: Math.round(VX.margin.top * PHONE_MARGIN_SCALE),
|
|
103
|
+
right: Math.round(VX.margin.right * PHONE_MARGIN_SCALE),
|
|
104
|
+
bottom: Math.round(VX.margin.bottom * PHONE_MARGIN_SCALE),
|
|
105
|
+
left: Math.round(VX.margin.left * PHONE_MARGIN_SCALE),
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** The resolved sizes for one tier. Frozen module constants — never a new object per render. */
|
|
110
|
+
export function chartTierMetrics(tier: ChartTier): ChartTierMetrics {
|
|
111
|
+
return tier === 'phone' ? PHONE_METRICS : DESKTOP_METRICS
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Resolve the plot rect from the measured box and the measured legend band. Pure and exported so
|
|
116
|
+
* the two floors below are testable without a `ResizeObserver`.
|
|
117
|
+
*
|
|
118
|
+
* Two floors, and each is only a floor while it is honest:
|
|
119
|
+
* - **width** — `minWidth` is a FIRST-FRAME guard, applied only while `containerW` is still 0
|
|
120
|
+
* (unmeasured, or SSR, where the observer never fires). Once measured the plot tracks the box
|
|
121
|
+
* exactly, floored at 1 so no scale divides by zero: applying a 200px floor forever drew an SVG
|
|
122
|
+
* wider than its own container inside any narrower grid cell.
|
|
123
|
+
* - **height** — the legend band is subtracted first (it always was), but the remainder can no
|
|
124
|
+
* longer collapse: eight entries wrapping to five rows at phone width ate a fixed
|
|
125
|
+
* `height={240}` toward zero and the body stopped rendering entirely. The plot stops at
|
|
126
|
+
* `VX.minPlotHeight` and the frame's own box grows by the difference instead — it is a flex
|
|
127
|
+
* column with `height: auto`, so that growth is automatic. Under `fill` the box CANNOT grow, so
|
|
128
|
+
* the legend is capped instead ({@link legendEntryCap}). An unmeasured box (`resolvedHeight <=
|
|
129
|
+
* 0`, i.e. a `fill` frame before its first measurement) stays at 0 and renders nothing, exactly
|
|
130
|
+
* as before — the floor must never invent a height for a box nobody has measured.
|
|
131
|
+
*/
|
|
132
|
+
export function resolvePlotRect(input: {
|
|
133
|
+
containerW: number
|
|
134
|
+
resolvedHeight: number
|
|
135
|
+
minWidth: number
|
|
136
|
+
sideLegendWidth: number
|
|
137
|
+
topBottomLegendHeight: number
|
|
138
|
+
}): { width: number; height: number } {
|
|
139
|
+
const { containerW, resolvedHeight, minWidth, sideLegendWidth, topBottomLegendHeight } = input
|
|
140
|
+
return {
|
|
141
|
+
width: containerW === 0 ? minWidth : Math.max(containerW - sideLegendWidth, 1),
|
|
142
|
+
height:
|
|
143
|
+
resolvedHeight <= 0 ? 0 : Math.max(resolvedHeight - topBottomLegendHeight, VX.minPlotHeight),
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Height of a legend band `rows` rows tall, wrapper padding included. */
|
|
148
|
+
const legendBandHeight = (rows: number): number =>
|
|
149
|
+
LEGEND_PAD_Y + rows * LEGEND_LINE_H + Math.max(rows - 1, 0) * VX.legendGap
|
|
150
|
+
|
|
151
|
+
/** Width one legend entry occupies — swatch, gap, label, and the note that rides after it. */
|
|
152
|
+
const legendEntryWidth = (item: LegendEntry): number =>
|
|
153
|
+
LEGEND_SWATCH_W +
|
|
154
|
+
measureText(
|
|
155
|
+
item.note === undefined ? item.label : `${item.label} ${item.note}`,
|
|
156
|
+
VX.legendFontSize,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
/** How many of `items` fit in `rows` wrapped rows of `width` — the same greedy wrap the flex
|
|
160
|
+
* container performs, measured rather than assumed (`docs/CHARTS-SPEC.md` §1). */
|
|
161
|
+
function entriesWithinRows(items: readonly LegendEntry[], width: number, rows: number): number {
|
|
162
|
+
let row = 1
|
|
163
|
+
let x = 0
|
|
164
|
+
let fitted = 0
|
|
165
|
+
for (const item of items) {
|
|
166
|
+
const w = legendEntryWidth(item)
|
|
167
|
+
const next = x === 0 ? w : x + VX.legendGap + w
|
|
168
|
+
if (next > width && x > 0) {
|
|
169
|
+
row += 1
|
|
170
|
+
if (row > rows) return fitted
|
|
171
|
+
x = w
|
|
172
|
+
} else {
|
|
173
|
+
x = next
|
|
174
|
+
}
|
|
175
|
+
fitted += 1
|
|
176
|
+
}
|
|
177
|
+
return fitted
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* The entry cap a `fill` frame's legend must respect so the plot keeps `VX.minPlotHeight`.
|
|
182
|
+
*
|
|
183
|
+
* A fixed-height frame grows to fit its legend; a `fill` frame is pinned to its cell, so the only
|
|
184
|
+
* remaining lever is `ChartLegend`'s `maxRows` rollup (an ENTRY cap — see its JSDoc). The rows the
|
|
185
|
+
* legend may take follow from the height left over; how many entries that is follows from
|
|
186
|
+
* MEASURING the labels, not from assuming one entry per row — otherwise a five-entry legend that
|
|
187
|
+
* fits on one line would roll up to `+2 more` in every 240px cell, moving rendering for charts
|
|
188
|
+
* that never had the bug. Returns `undefined` when the whole legend already fits.
|
|
189
|
+
*/
|
|
190
|
+
export function legendEntryCap(input: {
|
|
191
|
+
items: readonly LegendEntry[]
|
|
192
|
+
containerW: number
|
|
193
|
+
/** Frame height the legend may consume — `resolvedHeight - VX.minPlotHeight`. */
|
|
194
|
+
available: number
|
|
195
|
+
/** An explicit caller cap always wins as the upper bound. */
|
|
196
|
+
callerMaxRows?: number
|
|
197
|
+
}): number | undefined {
|
|
198
|
+
const { items, containerW, available, callerMaxRows } = input
|
|
199
|
+
if (containerW <= 0 || items.length === 0) return callerMaxRows
|
|
200
|
+
const rows = Math.max(
|
|
201
|
+
1,
|
|
202
|
+
Math.floor((available - LEGEND_PAD_Y + VX.legendGap) / (LEGEND_LINE_H + VX.legendGap)),
|
|
203
|
+
)
|
|
204
|
+
if (
|
|
205
|
+
legendBandHeight(rows) <= available &&
|
|
206
|
+
entriesWithinRows(items, containerW, rows) >= items.length
|
|
207
|
+
) {
|
|
208
|
+
return callerMaxRows
|
|
209
|
+
}
|
|
210
|
+
const fitted = Math.max(1, entriesWithinRows(items, containerW, rows))
|
|
211
|
+
return callerMaxRows === undefined ? fitted : Math.min(fitted, callerMaxRows)
|
|
212
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react'
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
import { chartTierMetrics, resolveChartTier } from './chart-frame-layout'
|
|
4
|
+
import type { ChartTier, ChartTierMetrics } from './chart-frame-layout'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Ambient chart tier. `ChartFrame` publishes the tier it resolved from its own MEASURED width;
|
|
8
|
+
* the axes, the legend, the crosshair dots and the tooltip read it back without every kind having
|
|
9
|
+
* to thread a prop it does not otherwise care about.
|
|
10
|
+
*
|
|
11
|
+
* Default `'desktop'`, so a primitive rendered outside a `ChartFrame` (a hand-composed plot, a
|
|
12
|
+
* `TooltipRow` in a consumer's own tooltip) keeps exactly today's sizes.
|
|
13
|
+
*/
|
|
14
|
+
const ChartTierContext = createContext<ChartTier>('desktop')
|
|
15
|
+
|
|
16
|
+
export type ChartTierProviderProps = {
|
|
17
|
+
tier: ChartTier
|
|
18
|
+
children: ReactNode
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Publishes a resolved tier to the subtree. Mounted once, by `ChartFrame`. */
|
|
22
|
+
export function ChartTierProvider({ tier, children }: ChartTierProviderProps): ReactNode {
|
|
23
|
+
return <ChartTierContext.Provider value={tier}>{children}</ChartTierContext.Provider>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** The tier of the nearest measured `ChartFrame`, or `'desktop'` outside one. */
|
|
27
|
+
export function useChartTier(): ChartTier {
|
|
28
|
+
return useContext(ChartTierContext)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The resolved sizes for the ambient tier — the form every primitive actually wants. */
|
|
32
|
+
export function useChartTierMetrics(): ChartTierMetrics {
|
|
33
|
+
return chartTierMetrics(useContext(ChartTierContext))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { chartTierMetrics, resolveChartTier }
|
|
37
|
+
export type { ChartTier, ChartTierMetrics }
|
package/src/charts/series.ts
CHANGED
|
@@ -7,6 +7,13 @@
|
|
|
7
7
|
* share one definition.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import {
|
|
11
|
+
curveLinear,
|
|
12
|
+
curveMonotoneX,
|
|
13
|
+
curveStep,
|
|
14
|
+
curveStepAfter,
|
|
15
|
+
curveStepBefore,
|
|
16
|
+
} from '@visx/curve'
|
|
10
17
|
import type { LegendEntry } from './primitives/ChartLegend'
|
|
11
18
|
|
|
12
19
|
/**
|
|
@@ -24,6 +31,43 @@ export type SeriesMark = 'line' | 'bar' | 'area'
|
|
|
24
31
|
/** Only meaningful for `mark: 'line'`. */
|
|
25
32
|
export type SeriesDash = 'solid' | 'dashed'
|
|
26
33
|
|
|
34
|
+
/**
|
|
35
|
+
* The interpolation between two plotted points. Named rather than a raw d3 `CurveFactory` so the
|
|
36
|
+
* set is closed, serializable, and identical across every kind — a chart cannot pick a curve the
|
|
37
|
+
* legend swatch and the sibling chart next to it do not also understand.
|
|
38
|
+
*
|
|
39
|
+
* - `monotone` (default, today's behaviour everywhere) — smoothed, overshoot-free.
|
|
40
|
+
* - `linear` — straight segments. The honest choice when the points are samples, not a signal.
|
|
41
|
+
* - `step` / `stepAfter` / `stepBefore` — the value HOLDS between samples and changes at one x.
|
|
42
|
+
* Reach for these when the quantity is piecewise-constant (a price tier, a config value, a
|
|
43
|
+
* discrete state): a smoothed curve through them draws intermediate values that never existed.
|
|
44
|
+
* `stepAfter` holds the value forward from its own x, `stepBefore` holds it back from the next,
|
|
45
|
+
* `step` changes at the midpoint.
|
|
46
|
+
*/
|
|
47
|
+
export type SeriesCurve = 'monotone' | 'linear' | 'step' | 'stepAfter' | 'stepBefore'
|
|
48
|
+
|
|
49
|
+
/** d3's curve-factory shape, taken from a curve we already import rather than from
|
|
50
|
+
* `@visx/vendor/d3-shape` — that package is a transitive of `@visx/curve`, not one of the nine
|
|
51
|
+
* exact-pinned peers, so naming it in an import would add an undeclared dependency. */
|
|
52
|
+
type CurveFactory = typeof curveMonotoneX
|
|
53
|
+
|
|
54
|
+
const CURVES: Record<SeriesCurve, CurveFactory> = {
|
|
55
|
+
monotone: curveMonotoneX,
|
|
56
|
+
linear: curveLinear,
|
|
57
|
+
step: curveStep,
|
|
58
|
+
stepAfter: curveStepAfter,
|
|
59
|
+
stepBefore: curveStepBefore,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The d3 curve factory for a {@link SeriesCurve}. `undefined` resolves to `curveMonotoneX` — the
|
|
64
|
+
* value every kind hard-coded before this seam existed, so an omitted `curve` moves nothing.
|
|
65
|
+
* Exported for a hand-composed plot that draws its own `LinePath` from the same `series` array.
|
|
66
|
+
*/
|
|
67
|
+
export function curveFor(curve?: SeriesCurve): CurveFactory {
|
|
68
|
+
return curve === undefined ? curveMonotoneX : CURVES[curve]
|
|
69
|
+
}
|
|
70
|
+
|
|
27
71
|
/** Drives legend grouping/dividers. */
|
|
28
72
|
export type SeriesRole = 'series' | 'overlay' | 'reference'
|
|
29
73
|
|
|
@@ -56,6 +100,12 @@ export type SeriesStyle = {
|
|
|
56
100
|
mark: SeriesMark
|
|
57
101
|
/** Default 'solid'. */
|
|
58
102
|
dash?: SeriesDash
|
|
103
|
+
/**
|
|
104
|
+
* Interpolation between points for the line/area shapes this series draws. Default `'monotone'`
|
|
105
|
+
* — today's behaviour, unchanged, everywhere. See {@link SeriesCurve} for when a step curve is
|
|
106
|
+
* the honest one. Silently no-ops on `mark: 'bar'`, which has nothing to interpolate.
|
|
107
|
+
*/
|
|
108
|
+
curve?: SeriesCurve
|
|
59
109
|
/** Default VX.lineWidth (one width, everywhere). */
|
|
60
110
|
strokeWidth?: number
|
|
61
111
|
/** bar/area: the swatch honors this so it cannot lie. */
|
|
@@ -95,7 +145,12 @@ export type SeriesStyle = {
|
|
|
95
145
|
|
|
96
146
|
/** Full descriptor = visual identity + data accessors. Generic over the point type. */
|
|
97
147
|
export type ChartSeries<T> = SeriesStyle & {
|
|
98
|
-
/**
|
|
148
|
+
/**
|
|
149
|
+
* null = an ABSENCE, never a zero: a real gap in the line/area (`defined`, not a straight
|
|
150
|
+
* interpolation across the hole) plus a skipped tooltip row and no crosshair dot. In a STACKED
|
|
151
|
+
* kind the absence is contagious — a band with no value leaves no cumulative total, so the whole
|
|
152
|
+
* stack gaps at that x (`StackedArea`'s JSDoc has the accounting).
|
|
153
|
+
*/
|
|
99
154
|
getValue: (d: T) => number | null
|
|
100
155
|
/** Per-series override of the shared tooltip/value formatter. Receives the hovered datum
|
|
101
156
|
* alongside the value so a row can read fields beyond the plotted number (e.g. `97.5 kg (92.5 ×
|
|
@@ -195,3 +250,24 @@ export function deriveTooltipRows<T>(
|
|
|
195
250
|
}
|
|
196
251
|
return rows
|
|
197
252
|
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Coerces a `ChartSeries.getValue` result into what a plotted point stores as its y: `null`,
|
|
256
|
+
* `undefined` and `NaN` all collapse to `null`, which downstream `definedOn` reads as a
|
|
257
|
+
* documented gap rather than a value to plot. Shared by `MultiLine`'s `seriesPoints` and
|
|
258
|
+
* `ZonedLine`'s `linePoints` — both used to reimplement this coercion independently.
|
|
259
|
+
*/
|
|
260
|
+
export function toPlotPoint(v: number | null | undefined): number | null {
|
|
261
|
+
return v === null || v === undefined || Number.isNaN(v) ? null : v
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* A `defined` predicate for `LinePath`/`AreaClosed`/`Threshold`, bound to one y-scale. A null
|
|
266
|
+
* `__y` is a documented gap (`ChartSeries.getValue`), and a non-positive value on a log scale maps
|
|
267
|
+
* to `NaN` via the scale itself — without excluding both, d3's generators either draw an
|
|
268
|
+
* interpolated straight line across the hole or blank the entire path from that point on (SVG's
|
|
269
|
+
* `NaN` path-command handling). Shared by `MultiLine` and `ZonedLine`.
|
|
270
|
+
*/
|
|
271
|
+
export function definedOn(scale: (y: number) => number): (d: { __y: number | null }) => boolean {
|
|
272
|
+
return (d) => d.__y !== null && Number.isFinite(scale(d.__y))
|
|
273
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { scaleLinear } from '@visx/scale'
|
|
2
2
|
import { useMemo } from 'react'
|
|
3
|
+
import type { BasaltProps } from '../../common/props'
|
|
3
4
|
import { VX } from '../../tokens'
|
|
4
5
|
|
|
5
|
-
type BarSparklineProps = {
|
|
6
|
+
export type BarSparklineProps = BasaltProps & {
|
|
6
7
|
data: number[]
|
|
7
8
|
width: number
|
|
8
9
|
height: number
|
|
@@ -47,6 +48,8 @@ export function BarSparkline({
|
|
|
47
48
|
emphasizeLast = false,
|
|
48
49
|
emphasisColor,
|
|
49
50
|
ariaLabel,
|
|
51
|
+
className,
|
|
52
|
+
style,
|
|
50
53
|
}: BarSparklineProps) {
|
|
51
54
|
const fillColor = color ?? VX.faint
|
|
52
55
|
const lastColor = emphasisColor ?? VX.accent
|
|
@@ -57,14 +60,19 @@ export function BarSparkline({
|
|
|
57
60
|
return scaleLinear<number>({ domain: [0, max], range: [0, height] })
|
|
58
61
|
}, [data, height])
|
|
59
62
|
|
|
60
|
-
|
|
63
|
+
const rootProps = {
|
|
64
|
+
...(className !== undefined && { className }),
|
|
65
|
+
...(style !== undefined && { style }),
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!data.length) return <svg width={width} height={height} {...rootProps} {...a11yProps} />
|
|
61
69
|
|
|
62
70
|
const step = width / data.length
|
|
63
71
|
const barWidth = Math.max(step - barGap, 1)
|
|
64
72
|
const lastIndex = data.length - 1
|
|
65
73
|
|
|
66
74
|
return (
|
|
67
|
-
<svg width={width} height={height} {...a11yProps}>
|
|
75
|
+
<svg width={width} height={height} {...rootProps} {...a11yProps}>
|
|
68
76
|
{data.map((v, i) => {
|
|
69
77
|
const bh = Math.max(yScale(Math.max(v, 0)), 0)
|
|
70
78
|
const isLast = emphasizeLast && i === lastIndex
|
|
@@ -3,9 +3,10 @@ import { Group } from '@visx/group'
|
|
|
3
3
|
import { scaleLinear } from '@visx/scale'
|
|
4
4
|
import { LinePath } from '@visx/shape'
|
|
5
5
|
import { useMemo } from 'react'
|
|
6
|
+
import type { BasaltProps } from '../../common/props'
|
|
6
7
|
import { VX } from '../../tokens'
|
|
7
8
|
|
|
8
|
-
type LineSparklineProps = {
|
|
9
|
+
export type LineSparklineProps = BasaltProps & {
|
|
9
10
|
data: number[]
|
|
10
11
|
width: number
|
|
11
12
|
height: number
|
|
@@ -18,7 +19,15 @@ type LineSparklineProps = {
|
|
|
18
19
|
* Quiet single-hue trend line (docs/DESIGN-SPEC.md §5: "single 1.6px faint line, no fill, no
|
|
19
20
|
* axes, no dots"). Sparklines default to `VX.faint`, not the bolder `VX.line` used by full charts.
|
|
20
21
|
*/
|
|
21
|
-
export function LineSparkline({
|
|
22
|
+
export function LineSparkline({
|
|
23
|
+
data,
|
|
24
|
+
width,
|
|
25
|
+
height,
|
|
26
|
+
color,
|
|
27
|
+
ariaLabel,
|
|
28
|
+
className,
|
|
29
|
+
style,
|
|
30
|
+
}: LineSparklineProps) {
|
|
22
31
|
const strokeColor = color ?? VX.faint
|
|
23
32
|
const a11yProps = ariaLabel !== undefined ? { role: 'img' as const, 'aria-label': ariaLabel } : {}
|
|
24
33
|
|
|
@@ -36,12 +45,17 @@ export function LineSparkline({ data, width, height, color, ariaLabel }: LineSpa
|
|
|
36
45
|
return scaleLinear<number>({ domain: [min - pad, max + pad], range: [height, 0] })
|
|
37
46
|
}, [data, height])
|
|
38
47
|
|
|
39
|
-
|
|
48
|
+
const rootProps = {
|
|
49
|
+
...(className !== undefined && { className }),
|
|
50
|
+
...(style !== undefined && { style }),
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (data.length < 2) return <svg width={width} height={height} {...rootProps} {...a11yProps} />
|
|
40
54
|
|
|
41
55
|
const indexed = data.map((v, i) => ({ v, i }))
|
|
42
56
|
|
|
43
57
|
return (
|
|
44
|
-
<svg width={width} height={height} {...a11yProps}>
|
|
58
|
+
<svg width={width} height={height} {...rootProps} {...a11yProps}>
|
|
45
59
|
<Group>
|
|
46
60
|
<LinePath<{ v: number; i: number }>
|
|
47
61
|
data={indexed}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { LineSparkline } from './LineSparkline'
|
|
2
|
-
export { BarSparkline } from './BarSparkline'
|
|
1
|
+
export { LineSparkline, type LineSparklineProps } from './LineSparkline'
|
|
2
|
+
export { BarSparkline, type BarSparklineProps } from './BarSparkline'
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
|
2
2
|
const SHORT_DAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* What EVERY formatter here prints for input it cannot represent — `NaN`, `±Infinity`, an
|
|
6
|
+
* `Invalid Date`. An em dash (U+2014), the same glyph the tooltip already uses for a missing value.
|
|
7
|
+
*
|
|
8
|
+
* The rule is one rule on purpose. `Intl.NumberFormat` renders `NaN` as the literal string `"NaN"`
|
|
9
|
+
* and `Infinity` as `"∞"`, so a y axis whose domain collapsed (an empty series, a 0/0 rate, a log
|
|
10
|
+
* scale reaching zero) painted its own arithmetic accident as a tick label and a tooltip read
|
|
11
|
+
* `NaN%`. A chart may say it does not know; it may not print a number that is not one.
|
|
12
|
+
*/
|
|
13
|
+
export const NON_FINITE = '\u2014'
|
|
14
|
+
|
|
15
|
+
/** Axis-friendly short date: DD.MM. A non-finite or invalid date prints {@link NON_FINITE}. */
|
|
5
16
|
export function fmtAxisDate(value: unknown): string {
|
|
17
|
+
if (typeof value === 'number' && !Number.isFinite(value)) return NON_FINITE
|
|
6
18
|
if (value instanceof Date) {
|
|
19
|
+
if (Number.isNaN(value.getTime())) return NON_FINITE
|
|
7
20
|
const dd = String(value.getDate()).padStart(2, '0')
|
|
8
21
|
const mm = String(value.getMonth() + 1).padStart(2, '0')
|
|
9
22
|
return `${dd}.${mm}`
|
|
@@ -14,9 +27,12 @@ export function fmtAxisDate(value: unknown): string {
|
|
|
14
27
|
return s
|
|
15
28
|
}
|
|
16
29
|
|
|
17
|
-
/** Tooltip-friendly long date: "Mon Apr 21 2026"
|
|
30
|
+
/** Tooltip-friendly long date: "Mon Apr 21 2026". A non-finite or invalid date prints
|
|
31
|
+
* {@link NON_FINITE}. */
|
|
18
32
|
export function fmtTooltipDate(date: unknown): string {
|
|
33
|
+
if (typeof date === 'number' && !Number.isFinite(date)) return NON_FINITE
|
|
19
34
|
if (date instanceof Date) {
|
|
35
|
+
if (Number.isNaN(date.getTime())) return NON_FINITE
|
|
20
36
|
return `${SHORT_DAYS[date.getDay()]} ${MONTHS[date.getMonth()]} ${date.getDate()} ${date.getFullYear()}`
|
|
21
37
|
}
|
|
22
38
|
const s = String(date ?? '')
|
|
@@ -25,3 +41,148 @@ export function fmtTooltipDate(date: unknown): string {
|
|
|
25
41
|
const d = new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]))
|
|
26
42
|
return `${SHORT_DAYS[d.getDay()]} ${MONTHS[d.getMonth()]} ${d.getDate()} ${d.getFullYear()}`
|
|
27
43
|
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The number-format law.
|
|
47
|
+
*
|
|
48
|
+
* `format.ts` shipped two date formatters and nothing else, so every axis and every tooltip in
|
|
49
|
+
* every demo and consumer hand-rolled its own `` `$${v}k` `` — a template literal that is not
|
|
50
|
+
* locale-aware, not compact-aware, and different in each of the places it was written. These four
|
|
51
|
+
* are `Intl.NumberFormat` with the arguments already decided, so a chart states WHAT a number is
|
|
52
|
+
* rather than how to punctuate it.
|
|
53
|
+
*
|
|
54
|
+
* `locale` defaults to `undefined`, which is `Intl`'s own "use the runtime's locale" — the correct
|
|
55
|
+
* default for an app whose reader is the person looking at it. Pass it explicitly (`'en-US'`) when
|
|
56
|
+
* the output must be stable regardless of where it runs, which is what the tests here do.
|
|
57
|
+
*
|
|
58
|
+
* Every one of them returns {@link NON_FINITE} for input that is not a finite number — see its
|
|
59
|
+
* JSDoc for why that is a law of this module rather than a per-call-site guard.
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
/** Options every formatter here accepts. */
|
|
63
|
+
type LocaleOption = {
|
|
64
|
+
/** BCP-47 tag. Default `undefined` — the runtime's own locale. */
|
|
65
|
+
locale?: string
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* `Intl.NumberFormat` construction is the expensive half of formatting and a chart formats one
|
|
70
|
+
* label per tick per frame, so instances are memoized on their own arguments. The key is the
|
|
71
|
+
* serialized options because that is exactly what identifies an instance.
|
|
72
|
+
*/
|
|
73
|
+
const formatterCache = new Map<string, Intl.NumberFormat>()
|
|
74
|
+
|
|
75
|
+
/** The compact `K` suffix, matched only where it follows the digits it qualifies — so a currency
|
|
76
|
+
* prefix that happens to contain one (`HK$`) survives the lowercasing. */
|
|
77
|
+
const COMPACT_K = /(\d\s?)K/
|
|
78
|
+
|
|
79
|
+
function numberFormat(
|
|
80
|
+
locale: string | undefined,
|
|
81
|
+
options: Intl.NumberFormatOptions,
|
|
82
|
+
): Intl.NumberFormat {
|
|
83
|
+
const key = `${locale ?? ''}|${JSON.stringify(options)}`
|
|
84
|
+
const cached = formatterCache.get(key)
|
|
85
|
+
if (cached !== undefined) return cached
|
|
86
|
+
const created = new Intl.NumberFormat(locale, options)
|
|
87
|
+
formatterCache.set(key, created)
|
|
88
|
+
return created
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export type FmtCompactOptions = LocaleOption & {
|
|
92
|
+
/** Significant-ish fraction digits on the compacted number. Default 1 (`1.2k`, `3.4M`). */
|
|
93
|
+
digits?: number
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Compact notation: `1.2k`, `3.4M`, `-820`. The axis formatter for any count whose range spans
|
|
98
|
+
* more than three digits — a y axis reading `1,200,000` spends a third of the plot width on a
|
|
99
|
+
* gutter. A non-finite value prints {@link NON_FINITE}.
|
|
100
|
+
*
|
|
101
|
+
* Note `Intl`'s compact notation is LOCALE text, not a suffix table: `en` gives `1.2K`, and this
|
|
102
|
+
* lowercases the `K` to match the package's own numeral style while leaving every other locale's
|
|
103
|
+
* word (`1,2 Mio.`) untouched. Only a `K` attached to the DIGITS is touched — `HK$1.2K` must keep
|
|
104
|
+
* its currency prefix, which a bare `replace('K', 'k')` ate.
|
|
105
|
+
*/
|
|
106
|
+
export function fmtCompact(value: number, options: FmtCompactOptions = {}): string {
|
|
107
|
+
if (!Number.isFinite(value)) return NON_FINITE
|
|
108
|
+
const { digits = 1, locale } = options
|
|
109
|
+
return numberFormat(locale, {
|
|
110
|
+
notation: 'compact',
|
|
111
|
+
compactDisplay: 'short',
|
|
112
|
+
maximumFractionDigits: digits,
|
|
113
|
+
})
|
|
114
|
+
.format(value)
|
|
115
|
+
.replace(COMPACT_K, '$1k')
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export type FmtPercentOptions = LocaleOption & {
|
|
119
|
+
/** Fraction digits. Default 0 (`42%`). */
|
|
120
|
+
digits?: number
|
|
121
|
+
/**
|
|
122
|
+
* What the incoming number MEANS. `'ratio'` (default) is `0.42 → 42%`; `'percent'` is
|
|
123
|
+
* `42 → 42%`. Required as an explicit option rather than guessed from magnitude: a ratio of 1.2
|
|
124
|
+
* and a percentage of 1.2 are both perfectly ordinary numbers, and a heuristic would silently
|
|
125
|
+
* render one of them 100× wrong.
|
|
126
|
+
*/
|
|
127
|
+
input?: 'ratio' | 'percent'
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Percentages, from either a ratio or an already-scaled percentage. A non-finite value prints
|
|
131
|
+
* {@link NON_FINITE} — a rate is the commonest source of a 0/0. */
|
|
132
|
+
export function fmtPercent(value: number, options: FmtPercentOptions = {}): string {
|
|
133
|
+
if (!Number.isFinite(value)) return NON_FINITE
|
|
134
|
+
const { digits = 0, input = 'ratio', locale } = options
|
|
135
|
+
return numberFormat(locale, {
|
|
136
|
+
style: 'percent',
|
|
137
|
+
minimumFractionDigits: digits,
|
|
138
|
+
maximumFractionDigits: digits,
|
|
139
|
+
}).format(input === 'percent' ? value / 100 : value)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export type FmtCurrencyOptions = LocaleOption & {
|
|
143
|
+
/** ISO 4217 code — required. There is no default currency; a wrong symbol is worse than a
|
|
144
|
+
* verbose call site. */
|
|
145
|
+
currency: string
|
|
146
|
+
/** Compact notation for the amount (`$1.2k`). Default false. */
|
|
147
|
+
compact?: boolean
|
|
148
|
+
/** Fraction digits. Default 0 compacted, else the currency's own default (`undefined`). */
|
|
149
|
+
digits?: number
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Money. `fmtCurrency(1234, { currency: 'USD' })` → `$1,234`. A non-finite amount prints
|
|
153
|
+
* {@link NON_FINITE}. */
|
|
154
|
+
export function fmtCurrency(value: number, options: FmtCurrencyOptions): string {
|
|
155
|
+
if (!Number.isFinite(value)) return NON_FINITE
|
|
156
|
+
const { currency, compact = false, digits, locale } = options
|
|
157
|
+
const fractionDigits = digits ?? (compact ? 1 : 0)
|
|
158
|
+
return numberFormat(locale, {
|
|
159
|
+
style: 'currency',
|
|
160
|
+
currency,
|
|
161
|
+
...(compact && { notation: 'compact', compactDisplay: 'short' }),
|
|
162
|
+
minimumFractionDigits: compact ? 0 : fractionDigits,
|
|
163
|
+
maximumFractionDigits: fractionDigits,
|
|
164
|
+
})
|
|
165
|
+
.format(value)
|
|
166
|
+
.replace(COMPACT_K, '$1k')
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Grouped integer: `12,480`. Rounds — an integer formatter that printed `12,480.4` would be
|
|
170
|
+
* lying about its own name. A non-finite value prints {@link NON_FINITE}. */
|
|
171
|
+
export function fmtInt(value: number, options: LocaleOption = {}): string {
|
|
172
|
+
if (!Number.isFinite(value)) return NON_FINITE
|
|
173
|
+
return numberFormat(options.locale, { maximumFractionDigits: 0 }).format(value)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Every shipped formatter under one name, so a chart can be handed the whole set
|
|
178
|
+
* (`format={formatters.compact}`) and a consumer has one import to reach for instead of four.
|
|
179
|
+
* Same members as the named exports — this is a convenience, never a second implementation.
|
|
180
|
+
*/
|
|
181
|
+
export const formatters = {
|
|
182
|
+
compact: fmtCompact,
|
|
183
|
+
percent: fmtPercent,
|
|
184
|
+
currency: fmtCurrency,
|
|
185
|
+
int: fmtInt,
|
|
186
|
+
axisDate: fmtAxisDate,
|
|
187
|
+
tooltipDate: fmtTooltipDate,
|
|
188
|
+
} as const
|