basalt-ui 1.27.0 → 1.29.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 +28 -35
- package/CHANGELOG.md +72 -0
- package/MIGRATING.md +759 -22
- package/README.md +56 -69
- package/agent/rules/basalt-batteries.md +60 -168
- package/agent/rules/basalt-charts.md +56 -75
- package/agent/rules/basalt-controls.md +81 -87
- package/agent/rules/basalt-mantine.md +64 -98
- package/agent/rules/basalt-state.md +42 -60
- package/agent/rules/basalt-tokens.md +46 -67
- package/agent/skills/basalt-app/SKILL.md +2 -0
- package/agent/templates/CLAUDE-block.md.tpl +14 -14
- package/configs/oxlint-plugin.js +964 -33
- package/configs/oxlint-plugin.test.ts +902 -2
- package/configs/oxlint.json +6 -0
- package/dist/agent/ai-sdk-transport.d.ts.map +1 -1
- package/dist/agent/ai-sdk-transport.js +7 -4
- 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/transport.d.ts +13 -2
- package/dist/agent/transport.d.ts.map +1 -1
- package/dist/agent/use-agent-thread-runs.d.ts +12 -1
- package/dist/agent/use-agent-thread-runs.d.ts.map +1 -1
- package/dist/agent/use-agent-thread-runs.js +20 -6
- 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/index.d.ts +1 -1
- package/dist/agent-chat/index.d.ts.map +1 -1
- 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 +9 -6
- package/dist/agent-chat/thread-feed-row.d.ts +32 -6
- package/dist/agent-chat/thread-feed-row.d.ts.map +1 -1
- package/dist/agent-chat/thread-feed-row.js +61 -26
- package/dist/agent-chat/thread-feed.d.ts +13 -2
- package/dist/agent-chat/thread-feed.d.ts.map +1 -1
- package/dist/agent-chat/thread-feed.js +16 -7
- 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/agent-chat/virtualize.d.ts +39 -0
- package/dist/agent-chat/virtualize.d.ts.map +1 -1
- package/dist/agent-chat/virtualize.js +15 -0
- package/dist/charts/hooks/useBandPlot.d.ts.map +1 -1
- package/dist/charts/hooks/useBandPlot.js +15 -8
- package/dist/charts/index.d.ts +11 -10
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/index.js +53 -9
- 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 +13 -9
- 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 +9 -6
- package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
- package/dist/charts/kinds/DualPanel.js +37 -9
- 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 +13 -11
- 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 +32 -4
- package/dist/charts/primitives/Axes.d.ts.map +1 -1
- package/dist/charts/primitives/Axes.js +49 -7
- 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 +45 -4
- package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.js +70 -21
- 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 +52 -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 +85 -6
- 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 +7 -4
- package/dist/charts/utils/format.d.ts.map +1 -1
- package/dist/charts/utils/format.js +17 -24
- 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/audit-allows.d.ts +24 -0
- package/dist/cli/audit-allows.d.ts.map +1 -0
- package/dist/cli/audit-allows.js +240 -0
- package/dist/cli/check-theme.d.ts +10 -0
- package/dist/cli/check-theme.d.ts.map +1 -0
- package/dist/cli/check-theme.js +139 -0
- package/dist/cli/doctor.d.ts +47 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +197 -0
- package/dist/cli/guard-hook.d.ts +9 -0
- package/dist/cli/guard-hook.d.ts.map +1 -0
- package/dist/cli/guard-hook.js +93 -0
- package/dist/cli/help.d.ts +9 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/help.js +53 -0
- package/dist/cli/index.d.ts +360 -270
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +85 -1901
- package/dist/cli/init.d.ts +4 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +165 -0
- package/dist/cli/sync.d.ts +43 -0
- package/dist/cli/sync.d.ts.map +1 -0
- package/dist/cli/sync.js +206 -0
- package/dist/cli/tokens-css.d.ts +51 -0
- package/dist/cli/tokens-css.d.ts.map +1 -0
- package/dist/cli/tokens-css.js +165 -0
- 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 +12 -2
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +9 -1
- package/dist/commands/overlays-mount.d.ts +16 -11
- package/dist/commands/overlays-mount.d.ts.map +1 -1
- package/dist/commands/overlays-mount.js +23 -14
- package/dist/commands/shell-bridge.d.ts +57 -0
- package/dist/commands/shell-bridge.d.ts.map +1 -0
- package/dist/commands/shell-bridge.js +32 -0
- package/dist/common/errors.d.ts +69 -0
- package/dist/common/errors.d.ts.map +1 -0
- package/dist/common/errors.js +64 -0
- package/dist/common/index.d.ts +17 -0
- package/dist/common/index.d.ts.map +1 -0
- package/dist/common/index.js +35 -0
- package/dist/common/is-dev.d.ts +3 -0
- package/dist/common/is-dev.d.ts.map +1 -0
- package/dist/common/is-dev.js +6 -0
- package/dist/{motion/index.d.ts → common/motion.d.ts} +1 -1
- package/dist/common/motion.d.ts.map +1 -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/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 +3 -4
- package/dist/content/markdown.d.ts.map +1 -1
- package/dist/content/markdown.js +3 -4
- 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 +50 -7
- 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 +18 -6
- package/dist/dashboard/delta-badge.d.ts.map +1 -1
- package/dist/dashboard/delta-badge.js +33 -1
- package/dist/dashboard/empty-state.d.ts +8 -4
- package/dist/dashboard/empty-state.d.ts.map +1 -1
- package/dist/dashboard/empty-state.js +36 -6
- package/dist/dashboard/index.d.ts +9 -5
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +13 -1
- package/dist/dashboard/query-state.d.ts +39 -27
- package/dist/dashboard/query-state.d.ts.map +1 -1
- package/dist/dashboard/query-state.js +109 -58
- package/dist/dashboard/section.d.ts +58 -0
- package/dist/dashboard/section.d.ts.map +1 -0
- package/dist/dashboard/section.js +118 -0
- package/dist/{section → dashboard}/section.module.css +11 -0
- 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 +75 -26
- 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 +56 -43
- 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/dashboard/widget-header.d.ts +132 -0
- package/dist/dashboard/widget-header.d.ts.map +1 -0
- package/dist/{widget-header → dashboard}/widget-header.js +42 -27
- package/dist/{widget-header → dashboard}/widget-header.module.css +72 -7
- 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 +385 -139
- package/dist/data/data-table.module.css +64 -0
- package/dist/data/data-table.module.css.d.ts +11 -0
- 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/table.js +2 -16
- 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 +4 -4
- package/dist/data/virtual.d.ts.map +1 -1
- package/dist/dev-dock.d.ts +28 -0
- package/dist/dev-dock.d.ts.map +1 -0
- package/dist/dev-dock.js +85 -0
- package/dist/format/date.d.ts +62 -0
- package/dist/format/date.d.ts.map +1 -0
- package/dist/format/date.js +106 -0
- package/dist/format/index.d.ts +39 -0
- package/dist/format/index.d.ts.map +1 -0
- package/dist/format/index.js +54 -0
- package/dist/format/measure.d.ts +15 -0
- package/dist/format/measure.d.ts.map +1 -0
- package/dist/format/measure.js +16 -0
- package/dist/format/number.d.ts +69 -0
- package/dist/format/number.d.ts.map +1 -0
- package/dist/format/number.js +62 -0
- package/dist/format/shared.d.ts +27 -0
- package/dist/format/shared.d.ts.map +1 -0
- package/dist/format/shared.js +29 -0
- 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 +33 -9
- package/dist/forms/field.d.ts.map +1 -1
- package/dist/forms/field.js +7 -6
- 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 -4
- 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 +2 -9
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +38 -28
- package/dist/guard/types.d.ts +8 -10
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/index.d.ts +21 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -6
- 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 +11 -7
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +5 -3
- 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/store.d.ts +7 -0
- package/dist/notifications/store.d.ts.map +1 -1
- package/dist/provider/connectivity-indicator.d.ts +13 -0
- package/dist/provider/connectivity-indicator.d.ts.map +1 -0
- package/dist/{connectivity → provider}/connectivity-indicator.js +3 -1
- package/dist/{connectivity → provider}/connectivity-provider.d.ts +1 -1
- package/dist/provider/connectivity-provider.d.ts.map +1 -0
- package/dist/{connectivity → provider}/connectivity-provider.js +4 -1
- package/dist/{connectivity/types.d.ts → provider/connectivity-types.d.ts} +3 -3
- package/dist/provider/connectivity-types.d.ts.map +1 -0
- package/dist/provider/error-fallback.module.css +21 -0
- package/dist/provider/error-fallback.module.css.d.ts +9 -0
- package/dist/provider/index.d.ts +29 -13
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +82 -34
- package/dist/provider/lab-theme.d.ts.map +1 -1
- package/dist/provider/lab-theme.js +2 -1
- package/dist/provider/use-connectivity.d.ts +3 -0
- package/dist/provider/use-connectivity.d.ts.map +1 -0
- package/dist/query-client.d.ts +63 -0
- package/dist/query-client.d.ts.map +1 -0
- package/dist/query-client.js +33 -0
- package/dist/{query/devtools.d.ts → query-devtools.d.ts} +2 -2
- package/dist/query-devtools.d.ts.map +1 -0
- package/dist/{query/devtools.js → query-devtools.js} +1 -0
- package/dist/register.d.ts +17 -3
- package/dist/register.d.ts.map +1 -1
- package/dist/router-tanstack/index.d.ts +4 -7
- package/dist/router-tanstack/index.d.ts.map +1 -1
- package/dist/router-tanstack/index.js +1 -9
- package/dist/router-tanstack/nav.d.ts +1 -1
- package/dist/router-tanstack/nav.d.ts.map +1 -1
- package/dist/router-tanstack/search-store.d.ts +3 -6
- 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 +1 -1
- package/dist/router-tanstack/use-nav.d.ts.map +1 -1
- package/dist/router-tanstack/use-nav.js +1 -1
- 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 +5 -3
- 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 +118 -0
- package/dist/shell/app-main.module.css.d.ts +11 -0
- package/dist/shell/app-mobile-nav.d.ts +4 -3
- package/dist/shell/app-mobile-nav.d.ts.map +1 -1
- package/dist/shell/app-mobile-nav.js +53 -17
- 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 +10 -4
- package/dist/shell/app-sidebar.d.ts.map +1 -1
- package/dist/shell/app-sidebar.js +78 -103
- package/dist/shell/app-sidebar.module.css +63 -96
- package/dist/shell/app-sidebar.module.css.d.ts +0 -5
- package/dist/shell/index.d.ts +24 -10
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +60 -29
- 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 +1 -1
- 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/{nav/types.d.ts → shell/nav-types.d.ts} +16 -7
- package/dist/shell/nav-types.d.ts.map +1 -0
- 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 +175 -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/page-title.d.ts +14 -0
- package/dist/shell/page-title.d.ts.map +1 -0
- package/dist/shell/page-title.js +25 -0
- package/dist/shell/page-title.module.css +33 -0
- package/dist/shell/page-title.module.css.d.ts +11 -0
- package/dist/shell/sidebar-block-model.d.ts +1 -1
- package/dist/shell/sidebar-block-model.d.ts.map +1 -1
- package/dist/shell/sidebar-blocks.d.ts +1 -1
- package/dist/shell/sidebar-blocks.d.ts.map +1 -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/shell/use-breakpoint.d.ts +11 -0
- package/dist/shell/use-breakpoint.d.ts.map +1 -0
- package/dist/shell/use-breakpoint.js +33 -0
- package/dist/state/fields.d.ts +26 -22
- package/dist/state/fields.d.ts.map +1 -1
- package/dist/state/fields.js +19 -18
- 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/styles.css +36 -0
- package/dist/surfaces.d.ts +25 -47
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +72 -55
- 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/theme-toggle.d.ts +9 -0
- package/dist/theme/theme-toggle.d.ts.map +1 -0
- package/dist/{theme-toggle/index.js → theme/theme-toggle.js} +9 -3
- 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/tokens/index.d.ts +43 -0
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +45 -6
- package/dist/tokens/palette.d.ts +57 -25
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +44 -15
- package/dist/tokens.css +5 -5
- package/llms.txt +11 -26
- package/package.json +54 -34
- package/src/agent/ai-sdk-transport.ts +11 -4
- 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/transport.ts +15 -2
- package/src/agent/use-agent-thread-runs.ts +65 -15
- package/src/agent-chat/composer.tsx +124 -103
- package/src/agent-chat/index.ts +6 -4
- package/src/agent-chat/relative-time.ts +2 -1
- package/src/agent-chat/thread-detail-panel.tsx +10 -6
- package/src/agent-chat/thread-feed-row.tsx +117 -34
- package/src/agent-chat/thread-feed.tsx +59 -27
- 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/agent-chat/virtualize.ts +48 -0
- package/src/charts/hooks/useBandPlot.ts +26 -8
- package/src/charts/index.ts +46 -14
- package/src/charts/kinds/BandStrip.tsx +36 -14
- package/src/charts/kinds/Bars.tsx +68 -20
- package/src/charts/kinds/Donut.tsx +30 -3
- package/src/charts/kinds/DualPanel.tsx +60 -13
- 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 +72 -35
- package/src/charts/layout/auto-margin.ts +134 -21
- package/src/charts/layout/log-ticks.ts +90 -0
- package/src/charts/primitives/Axes.tsx +82 -5
- package/src/charts/primitives/CartesianChart.tsx +194 -41
- package/src/charts/primitives/ChartCard.tsx +111 -27
- package/src/charts/primitives/ChartFrame.tsx +111 -26
- package/src/charts/primitives/ChartLegend.tsx +43 -13
- package/src/charts/primitives/ChartPending.tsx +194 -10
- 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 +19 -27
- package/src/charts/utils/ticks.ts +149 -4
- package/src/cli/audit-allows.ts +371 -0
- package/src/cli/check-theme.ts +197 -0
- package/src/cli/doctor.ts +331 -0
- package/src/cli/guard-hook.ts +115 -0
- package/src/cli/help.ts +101 -0
- package/src/cli/index.ts +269 -3434
- package/src/cli/init.ts +247 -0
- package/src/cli/sync.ts +324 -0
- package/src/cli/tokens-css.ts +252 -0
- 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 +22 -1
- package/src/commands/overlays-mount.tsx +63 -31
- package/src/commands/shell-bridge.ts +86 -0
- package/src/common/errors.ts +143 -0
- package/src/common/index.ts +23 -0
- package/src/common/is-dev.ts +4 -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/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 +6 -8
- 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 +90 -10
- 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 +75 -4
- package/src/dashboard/empty-state.tsx +67 -24
- package/src/dashboard/index.ts +24 -3
- package/src/dashboard/query-state.tsx +142 -85
- package/src/{section → dashboard}/section.module.css +11 -0
- package/src/dashboard/section.tsx +287 -0
- package/src/dashboard/settings-section.tsx +88 -32
- 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 +186 -128
- 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/{widget-header → dashboard}/widget-header.module.css +72 -7
- package/src/dashboard/widget-header.tsx +292 -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 +921 -348
- package/src/data/query-branch.ts +53 -0
- package/src/data/table.ts +9 -23
- package/src/data/virtual-list.tsx +190 -56
- package/src/data/virtual.ts +8 -4
- package/src/dev-dock.tsx +159 -0
- package/src/format/date.ts +184 -0
- package/src/format/index.ts +69 -0
- package/src/format/measure.ts +31 -0
- package/src/format/number.ts +129 -0
- package/src/format/shared.ts +63 -0
- package/src/forms/FormErrorSummary.tsx +13 -6
- package/src/forms/create-form.ts +2 -2
- package/src/forms/field.ts +42 -13
- 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 +37 -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 +99 -70
- package/src/guard/types.ts +7 -10
- package/src/index.ts +92 -19
- 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 +17 -8
- package/src/notifications/mount-guard.ts +39 -0
- package/src/notifications/notify.tsx +122 -3
- package/src/notifications/store.ts +7 -0
- package/src/{connectivity → provider}/connectivity-indicator.tsx +7 -2
- package/src/{connectivity → provider}/connectivity-provider.tsx +5 -2
- package/src/{connectivity/types.ts → provider/connectivity-types.ts} +5 -2
- package/src/provider/error-fallback.module.css +21 -0
- package/src/provider/error-fallback.module.css.d.ts +9 -0
- package/src/provider/index.tsx +116 -28
- package/src/provider/lab-theme.ts +2 -1
- package/src/{connectivity → provider}/use-connectivity.ts +4 -3
- package/src/query-client.ts +100 -0
- package/src/{query/devtools.tsx → query-devtools.tsx} +6 -4
- package/src/register.ts +20 -4
- package/src/router-tanstack/index.ts +4 -15
- package/src/router-tanstack/nav.ts +1 -1
- package/src/router-tanstack/search-store.ts +6 -14
- package/src/router-tanstack/use-nav.ts +19 -14
- 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 +20 -10
- package/src/shell/app-header.module.css +10 -5
- package/src/shell/app-main.module.css +118 -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 +71 -12
- package/src/shell/app-sidebar-account.tsx +29 -6
- package/src/shell/app-sidebar.module.css +63 -96
- package/src/shell/app-sidebar.module.css.d.ts +0 -5
- package/src/shell/app-sidebar.tsx +61 -105
- package/src/shell/index.tsx +179 -93
- package/src/shell/isomorphic-layout-effect.ts +14 -0
- package/src/shell/mobile-nav-model.ts +67 -11
- package/src/shell/nav-count-badge.tsx +6 -1
- package/src/{nav/types.ts → shell/nav-types.ts} +15 -6
- 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 +327 -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/page-title.module.css +33 -0
- package/src/shell/page-title.module.css.d.ts +11 -0
- package/src/shell/page-title.tsx +57 -0
- package/src/shell/sidebar-block-model.ts +1 -1
- package/src/shell/sidebar-blocks.tsx +1 -1
- package/src/shell/sidebar-search.module.css +1 -1
- package/src/shell/sidebar-search.tsx +13 -4
- package/src/shell/use-breakpoint.ts +73 -0
- package/src/state/fields.ts +50 -53
- package/src/state/persisted-or-local.ts +50 -0
- package/src/state.ts +0 -9
- package/src/styles.css +36 -0
- package/src/surfaces.ts +72 -57
- package/src/theme/ctl-theme.tsx +10 -0
- package/src/theme/index.ts +136 -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-toggle/index.tsx → theme/theme-toggle.tsx} +11 -4
- package/src/theme-lab/boot.ts +140 -0
- package/src/theme-lab/index.tsx +21 -112
- package/src/tokens/index.ts +60 -6
- package/src/tokens/palette.ts +70 -30
- 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/connectivity/connectivity-indicator.d.ts +0 -11
- package/dist/connectivity/connectivity-indicator.d.ts.map +0 -1
- package/dist/connectivity/connectivity-provider.d.ts.map +0 -1
- package/dist/connectivity/index.d.ts +0 -13
- package/dist/connectivity/index.d.ts.map +0 -1
- package/dist/connectivity/index.js +0 -8
- package/dist/connectivity/types.d.ts.map +0 -1
- package/dist/connectivity/use-connectivity.d.ts +0 -3
- package/dist/connectivity/use-connectivity.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/data/index.d.ts +0 -38
- package/dist/data/index.d.ts.map +0 -1
- package/dist/data/index.js +0 -2
- 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/motion/index.d.ts.map +0 -1
- package/dist/nav/types.d.ts.map +0 -1
- package/dist/notifications/overlay.d.ts +0 -20
- package/dist/notifications/overlay.d.ts.map +0 -1
- package/dist/notifications/overlay.js +0 -12
- 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/devtools.d.ts.map +0 -1
- package/dist/query/error-message.d.ts +0 -41
- package/dist/query/error-message.d.ts.map +0 -1
- package/dist/query/error-message.js +0 -30
- package/dist/query/error-message.test.d.ts +0 -2
- package/dist/query/error-message.test.d.ts.map +0 -1
- package/dist/query/index.d.ts +0 -47
- package/dist/query/index.d.ts.map +0 -1
- package/dist/query/index.js +0 -56
- package/dist/router-tanstack/field.d.ts +0 -14
- package/dist/router-tanstack/field.d.ts.map +0 -1
- package/dist/router-tanstack/field.js +0 -4
- package/dist/router-tanstack/multi-search-param-store.d.ts +0 -43
- package/dist/router-tanstack/multi-search-param-store.d.ts.map +0 -1
- package/dist/router-tanstack/multi-search-param-store.js +0 -30
- 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.d.ts +0 -42
- package/dist/router-tanstack/search-param-store.d.ts.map +0 -1
- package/dist/router-tanstack/search-param-store.js +0 -25
- 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/index.d.ts +0 -2
- package/dist/section/index.d.ts.map +0 -1
- package/dist/section/index.js +0 -4
- package/dist/section/section.d.ts +0 -24
- package/dist/section/section.d.ts.map +0 -1
- package/dist/section/section.js +0 -126
- 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/theme-toggle/index.d.ts +0 -8
- package/dist/theme-toggle/index.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.d.ts +0 -13
- package/dist/widget-header/delta-badge.d.ts.map +0 -1
- package/dist/widget-header/delta-badge.js +0 -29
- 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/index.d.ts +0 -3
- package/dist/widget-header/index.d.ts.map +0 -1
- package/dist/widget-header/index.js +0 -6
- 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.d.ts +0 -67
- package/dist/widget-header/widget-header.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/connectivity/index.ts +0 -18
- 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/data/index.ts +0 -41
- 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/notifications/overlay.tsx +0 -64
- 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/query/error-message.ts +0 -70
- package/src/query/index.ts +0 -97
- package/src/router-tanstack/field.ts +0 -35
- package/src/router-tanstack/multi-search-param-store.test.ts +0 -124
- package/src/router-tanstack/multi-search-param-store.ts +0 -106
- 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-param-store.ts +0 -100
- 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/index.ts +0 -1
- package/src/section/section.test.tsx +0 -204
- package/src/section/section.tsx +0 -220
- 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/delta-badge.tsx +0 -57
- package/src/widget-header/index.ts +0 -2
- package/src/widget-header/no-mantine.test.ts +0 -33
- package/src/widget-header/widget-header.test.tsx +0 -187
- package/src/widget-header/widget-header.tsx +0 -213
- /package/dist/{motion/index.js → common/motion.js} +0 -0
- /package/dist/{section → dashboard}/section.module.css.d.ts +0 -0
- /package/dist/{widget-header → dashboard}/widget-header.module.css.d.ts +0 -0
- /package/dist/{connectivity/types.js → provider/connectivity-types.js} +0 -0
- /package/dist/{connectivity → provider}/use-connectivity.js +0 -0
- /package/dist/{nav/types.js → shell/nav-types.js} +0 -0
- /package/src/{motion/index.ts → common/motion.ts} +0 -0
- /package/src/{section → dashboard}/section.module.css.d.ts +0 -0
- /package/src/{widget-header → dashboard}/widget-header.module.css.d.ts +0 -0
package/src/data/data-table.tsx
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* exactly as before.
|
|
10
10
|
*
|
|
11
11
|
* @example
|
|
12
|
-
* import { BasaltDataTable } from 'basalt-ui/data'
|
|
12
|
+
* import { BasaltDataTable } from 'basalt-ui/data/table'
|
|
13
13
|
* import { createColumnHelper } from '@tanstack/react-table'
|
|
14
14
|
*
|
|
15
15
|
* type Row = { name: string; age: number }
|
|
@@ -32,7 +32,17 @@
|
|
|
32
32
|
* enablePagination
|
|
33
33
|
* />
|
|
34
34
|
*/
|
|
35
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
Box,
|
|
37
|
+
Checkbox,
|
|
38
|
+
Group,
|
|
39
|
+
Pagination,
|
|
40
|
+
Select,
|
|
41
|
+
Skeleton,
|
|
42
|
+
Table,
|
|
43
|
+
Text,
|
|
44
|
+
TextInput,
|
|
45
|
+
} from '@mantine/core'
|
|
36
46
|
import type { MantineSpacing } from '@mantine/core'
|
|
37
47
|
import type {
|
|
38
48
|
Column,
|
|
@@ -42,6 +52,7 @@ import type {
|
|
|
42
52
|
FilterFn,
|
|
43
53
|
PaginationState,
|
|
44
54
|
RowData,
|
|
55
|
+
RowSelectionState,
|
|
45
56
|
SortingState,
|
|
46
57
|
Table as TanstackTable,
|
|
47
58
|
Updater,
|
|
@@ -54,9 +65,20 @@ import {
|
|
|
54
65
|
getSortedRowModel,
|
|
55
66
|
useReactTable,
|
|
56
67
|
} from '@tanstack/react-table'
|
|
57
|
-
import type {
|
|
58
|
-
|
|
68
|
+
import type {
|
|
69
|
+
CSSProperties,
|
|
70
|
+
KeyboardEvent as ReactKeyboardEvent,
|
|
71
|
+
ReactNode,
|
|
72
|
+
RefObject,
|
|
73
|
+
} from 'react'
|
|
74
|
+
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react'
|
|
75
|
+
import { cx } from '../common/props'
|
|
76
|
+
import type { BasaltProps, SlotStylesProps } from '../common/props'
|
|
77
|
+
import { BASALT_PREFIX } from '../common/errors'
|
|
78
|
+
import { assertRequiredProps, useValidateProps } from '../common/validate'
|
|
59
79
|
import { FilterSet } from '../controls'
|
|
80
|
+
import { ActionGroup, BarActionSlot } from '../controls/actions'
|
|
81
|
+
import type { BarAction, SlotActions } from '../controls/actions'
|
|
60
82
|
// `EnumFilter` is deliberately NOT on the `./controls` barrel (`docs/CONTROLS-SPEC.md` §3 —
|
|
61
83
|
// reaching for it directly is hand-rolling a filter, `basalt/hand-rolled-filter`); a facet column
|
|
62
84
|
// is the one place inside the framework itself that legitimately builds a `FieldHandle` over
|
|
@@ -66,7 +88,12 @@ import { MultiSelectFilter } from '../controls/multi-select-filter'
|
|
|
66
88
|
import { CtlSlot } from '../theme'
|
|
67
89
|
import { alpha, VX } from '../tokens'
|
|
68
90
|
import type { EnumField, FieldHandle, MultiField } from '../state'
|
|
69
|
-
import { WidgetHeader } from '../
|
|
91
|
+
import { WidgetHeader } from '../dashboard'
|
|
92
|
+
import { ErrorState } from '../dashboard/query-state'
|
|
93
|
+
import type { QueryStateLike } from '../dashboard/query-state'
|
|
94
|
+
import { dataQueryBranch } from './query-branch'
|
|
95
|
+
import { isDev } from '../common/is-dev'
|
|
96
|
+
import classes from './data-table.module.css'
|
|
70
97
|
|
|
71
98
|
// ── Column alignment ──────────────────────────────────────────────────────────
|
|
72
99
|
|
|
@@ -122,20 +149,30 @@ export type DataTableFacetOption = {
|
|
|
122
149
|
label: string
|
|
123
150
|
}
|
|
124
151
|
|
|
152
|
+
/**
|
|
153
|
+
* A TanStack column id typed off the row shape `T` where `accessor`/manual `id` inference allows
|
|
154
|
+
* it — `Extract<keyof T, string>` for a real field name, widened by `(string & {})` so a
|
|
155
|
+
* synthetic/derived column id (no matching key on `T`) still type-checks. Defaults to `unknown` so
|
|
156
|
+
* a bare `DataTableFacet[]` / `DataTableColumnPinning` (no type argument) keeps working exactly as
|
|
157
|
+
* before. Shared by {@link DataTableFacet}'s `columnId` and {@link DataTableColumnPinning}'s
|
|
158
|
+
* `left`/`right`.
|
|
159
|
+
*/
|
|
160
|
+
export type DataTableColumnId<T = unknown> = Extract<keyof T, string> | (string & {})
|
|
161
|
+
|
|
125
162
|
/**
|
|
126
163
|
* Declares one faceted column filter, rendered as an `EnumFilter` pill (or `MultiSelectFilter`
|
|
127
164
|
* when `multiple` is set) inside a `FilterSet` in the toolbar (`docs/CONTROLS-SPEC.md` §3).
|
|
128
165
|
*
|
|
129
166
|
* @example
|
|
130
|
-
* const facets: DataTableFacet[] = [
|
|
167
|
+
* const facets: DataTableFacet<Employee>[] = [
|
|
131
168
|
* { columnId: 'department', label: 'Department', options: [{ value: 'Engineering', label: 'Engineering' }] },
|
|
132
169
|
* { columnId: 'role', label: 'Role', multiple: true, options: roleOptions },
|
|
133
170
|
* ]
|
|
134
171
|
* <BasaltDataTable data={rows} columns={columns} facets={facets} />
|
|
135
172
|
*/
|
|
136
|
-
export type DataTableFacet = {
|
|
173
|
+
export type DataTableFacet<T = unknown> = {
|
|
137
174
|
/** The TanStack column id — the column's manual `id`, or its `accessorKey` string. */
|
|
138
|
-
columnId:
|
|
175
|
+
columnId: DataTableColumnId<T>
|
|
139
176
|
/** The pill's label — read at rest (single-select) and as the `Filters`/count fallback (multi). */
|
|
140
177
|
label: string
|
|
141
178
|
/** Selectable options for this facet. */
|
|
@@ -144,6 +181,17 @@ export type DataTableFacet = {
|
|
|
144
181
|
multiple?: boolean
|
|
145
182
|
}
|
|
146
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Column ids pinned to each edge — the same `T`-derived id union as {@link DataTableFacet}'s
|
|
186
|
+
* `columnId`, applied to TanStack's own `ColumnPinningState` shape (`{ left, right }`, each a bare
|
|
187
|
+
* `string[]` unrelated to any row type). Structurally assignable to `ColumnPinningState`, so no
|
|
188
|
+
* cast is needed where this flows into `useState<ColumnPinningState>`.
|
|
189
|
+
*/
|
|
190
|
+
export type DataTableColumnPinning<T = unknown> = {
|
|
191
|
+
left?: DataTableColumnId<T>[]
|
|
192
|
+
right?: DataTableColumnId<T>[]
|
|
193
|
+
}
|
|
194
|
+
|
|
147
195
|
/**
|
|
148
196
|
* The "no filter" member of a single-select facet's `FieldHandle`. A closed enum field always has
|
|
149
197
|
* a real member as its fallback (C4) — there is no "unset" — so a facet, whose underlying
|
|
@@ -161,7 +209,7 @@ export type DataTableFacet = {
|
|
|
161
209
|
const FACET_ALL_VALUE = '__basalt_facet_all__'
|
|
162
210
|
|
|
163
211
|
/** The synthetic row's label — see {@link FACET_ALL_VALUE}. */
|
|
164
|
-
function facetAllLabel(facet: DataTableFacet): string {
|
|
212
|
+
function facetAllLabel<T>(facet: DataTableFacet<T>): string {
|
|
165
213
|
return `Any ${facet.label.toLowerCase()}`
|
|
166
214
|
}
|
|
167
215
|
|
|
@@ -174,7 +222,7 @@ function facetAllLabel(facet: DataTableFacet): string {
|
|
|
174
222
|
*/
|
|
175
223
|
function facetEnumHandle<T>(
|
|
176
224
|
column: Column<T, unknown>,
|
|
177
|
-
facet: DataTableFacet
|
|
225
|
+
facet: DataTableFacet<T>,
|
|
178
226
|
): FieldHandle<EnumField<string>> {
|
|
179
227
|
return {
|
|
180
228
|
kind: 'enum',
|
|
@@ -204,7 +252,7 @@ function facetEnumHandle<T>(
|
|
|
204
252
|
*/
|
|
205
253
|
function facetMultiHandle<T>(
|
|
206
254
|
column: Column<T, unknown>,
|
|
207
|
-
facet: DataTableFacet
|
|
255
|
+
facet: DataTableFacet<T>,
|
|
208
256
|
): FieldHandle<MultiField<string>> {
|
|
209
257
|
return {
|
|
210
258
|
kind: 'multi',
|
|
@@ -227,8 +275,106 @@ function facetMultiHandle<T>(
|
|
|
227
275
|
}
|
|
228
276
|
}
|
|
229
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Resolves a facet's `columnId` against the table. A mistyped id used to fall through to
|
|
280
|
+
* `if (!column) return null` — the facet pill silently vanished from the toolbar, the same class
|
|
281
|
+
* of defect the sibling `meta.align` lane and `manualPagination` both treat as a dev-time throw.
|
|
282
|
+
* Mirrors `resolveAlign`'s dev-throw shape: throw naming the id in dev
|
|
283
|
+
* (`basaltViteConfig` defines `process.env.NODE_ENV`, so a production bundle constant-folds this
|
|
284
|
+
* away), degrade to "no pill for this facet" in production.
|
|
285
|
+
*/
|
|
286
|
+
function resolveFacetColumn<T>(
|
|
287
|
+
table: TanstackTable<T>,
|
|
288
|
+
facet: DataTableFacet<T>,
|
|
289
|
+
): Column<T, unknown> | undefined {
|
|
290
|
+
const column = table.getColumn(facet.columnId)
|
|
291
|
+
if (column) return column
|
|
292
|
+
if (isDev()) {
|
|
293
|
+
const knownIds = table
|
|
294
|
+
.getAllColumns()
|
|
295
|
+
.map((known) => known.id)
|
|
296
|
+
.join(', ')
|
|
297
|
+
throw new Error(
|
|
298
|
+
`BasaltDataTable: facet columnId "${facet.columnId}" matches no column — known column ids: ` +
|
|
299
|
+
`${knownIds}.`,
|
|
300
|
+
)
|
|
301
|
+
}
|
|
302
|
+
return undefined
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ── Row selection ─────────────────────────────────────────────────────────────
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* The synthetic checkbox column `enableRowSelection` prepends. Its id is namespaced so it can never
|
|
309
|
+
* collide with a real accessor key, and it is excluded from sorting — a checkbox column sorts by
|
|
310
|
+
* selection state, which is a reordering nobody asked for.
|
|
311
|
+
*/
|
|
312
|
+
const SELECT_COLUMN_ID = '__basalt_select__'
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Built per render inside a `useMemo`, not hoisted as a constant: `ColumnDef` is generic in `T`, and
|
|
316
|
+
* a single shared instance would have to be cast at every call site.
|
|
317
|
+
*
|
|
318
|
+
* `onClick`'s `stopPropagation` is the load-bearing line. The checkbox lives inside the row, and a
|
|
319
|
+
* row carrying `onRowActivate` opens a detail from a click — so without it, ticking the box also
|
|
320
|
+
* navigated away from the table the tick was meant to act on.
|
|
321
|
+
*/
|
|
322
|
+
function selectionColumn<T>(): ColumnDef<T, unknown> {
|
|
323
|
+
return {
|
|
324
|
+
id: SELECT_COLUMN_ID,
|
|
325
|
+
enableSorting: false,
|
|
326
|
+
header: ({ table }) => (
|
|
327
|
+
<Checkbox
|
|
328
|
+
size="xs"
|
|
329
|
+
aria-label="Select all rows"
|
|
330
|
+
checked={table.getIsAllPageRowsSelected()}
|
|
331
|
+
indeterminate={table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected()}
|
|
332
|
+
onChange={table.getToggleAllPageRowsSelectedHandler()}
|
|
333
|
+
/>
|
|
334
|
+
),
|
|
335
|
+
cell: ({ row }) => (
|
|
336
|
+
<Checkbox
|
|
337
|
+
size="xs"
|
|
338
|
+
aria-label={`Select row ${row.index + 1}`}
|
|
339
|
+
checked={row.getIsSelected()}
|
|
340
|
+
disabled={!row.getCanSelect()}
|
|
341
|
+
onChange={row.getToggleSelectedHandler()}
|
|
342
|
+
onClick={(event) => event.stopPropagation()}
|
|
343
|
+
/>
|
|
344
|
+
),
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* The bar a selection raises, above the table and below its header — the count, then the actions,
|
|
350
|
+
* through the SAME `BarAction[]` vocabulary `PageBar` uses, so the ≤3-inline fold and the mobile
|
|
351
|
+
* kebab are basalt's here too (law C7/C15) instead of a hand-rolled `Group` of buttons.
|
|
352
|
+
*
|
|
353
|
+
* It renders only while at least one row is selected. A bar reserved at zero is a row of page
|
|
354
|
+
* furniture that says nothing, and a table that changes height when the first box is ticked is the
|
|
355
|
+
* price of not reserving it — the cheaper of the two, because the selection is what the reader just
|
|
356
|
+
* caused.
|
|
357
|
+
*/
|
|
358
|
+
function BulkActionBar({ count, actions }: { count: number; actions: BarAction[] }) {
|
|
359
|
+
return (
|
|
360
|
+
<Group className={classes.bulkBar} gap="xs" align="center" wrap="wrap" mb="xs">
|
|
361
|
+
<Text className={classes.bulkCount}>{count} selected</Text>
|
|
362
|
+
<CtlSlot>
|
|
363
|
+
<ActionGroup secondary={actions} />
|
|
364
|
+
</CtlSlot>
|
|
365
|
+
</Group>
|
|
366
|
+
)
|
|
367
|
+
}
|
|
368
|
+
|
|
230
369
|
// ── Props ─────────────────────────────────────────────────────────────────────
|
|
231
370
|
|
|
371
|
+
/**
|
|
372
|
+
* The four boxes `BasaltDataTable` paints: `root` is the outer fragment's replacement wrapper,
|
|
373
|
+
* `toolbar` is the search/facets/actions row, `table` is the Mantine `Table` itself, `footer` is
|
|
374
|
+
* the pagination bar.
|
|
375
|
+
*/
|
|
376
|
+
export type BasaltDataTableSlot = 'root' | 'toolbar' | 'table' | 'footer'
|
|
377
|
+
|
|
232
378
|
/**
|
|
233
379
|
* Props for {@link BasaltDataTable}.
|
|
234
380
|
*
|
|
@@ -242,268 +388,383 @@ function facetMultiHandle<T>(
|
|
|
242
388
|
* emptyState: <Text c="dimmed">No results found.</Text>,
|
|
243
389
|
* }
|
|
244
390
|
*/
|
|
245
|
-
export type BasaltDataTableProps<T> =
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
391
|
+
export type BasaltDataTableProps<T> = BasaltProps &
|
|
392
|
+
SlotStylesProps<BasaltDataTableSlot> & {
|
|
393
|
+
// ── Header (WidgetHeader, docs/CONTROLS-SPEC.md §2.2) ──────────────────────────
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Optional heading rendered above the toolbar via `WidgetHeader tier="widget"`. `count` always
|
|
397
|
+
* reads `table.getRowCount()` (C11) — never a raw `data.length` — so it tracks the row model
|
|
398
|
+
* (post-filter/-pagination) rather than the unfiltered input.
|
|
399
|
+
*/
|
|
400
|
+
title?: string
|
|
401
|
+
/** Optional leading icon, forwarded to `WidgetHeader`. */
|
|
402
|
+
icon?: ReactNode
|
|
403
|
+
/** Optional muted line rendered below the title row. */
|
|
404
|
+
subtitle?: string
|
|
405
|
+
|
|
406
|
+
/** Row data array. */
|
|
407
|
+
data: T[]
|
|
408
|
+
/**
|
|
409
|
+
* Column definitions. Use `createColumnHelper<T>()` from `@tanstack/react-table`
|
|
410
|
+
* for a typed accessor builder — `col.accessor('field', …)` columns sort by value.
|
|
411
|
+
*/
|
|
412
|
+
// The cell-value type varies per column, so the array is heterogeneous in TValue. `any` here
|
|
413
|
+
// is the TanStack-idiomatic escape (accessor columns infer their own TValue); row typing stays
|
|
414
|
+
// exact via the `T` data generic.
|
|
415
|
+
// oxlint-disable-next-line typescript/no-explicit-any -- TanStack-idiomatic heterogeneous column array
|
|
416
|
+
columns: ColumnDef<T, any>[]
|
|
417
|
+
/**
|
|
418
|
+
* Enable column sorting via clickable headers. Client-side — the rows in `data` are what get
|
|
419
|
+
* reordered.
|
|
420
|
+
*
|
|
421
|
+
* Under `manualPagination` that is one page, so leaving this on without also passing
|
|
422
|
+
* `manualSorting` is a contract violation and throws in dev — see `manualPagination`.
|
|
423
|
+
* @default true
|
|
424
|
+
*/
|
|
425
|
+
enableSorting?: boolean
|
|
426
|
+
/**
|
|
427
|
+
* Hand sorting to the server: the table stops reordering `data` locally and renders it in the
|
|
428
|
+
* order given, while the headers keep toggling `SortingState` and reporting it through
|
|
429
|
+
* `onSortingChange` — which is where you re-request the sorted page.
|
|
430
|
+
*
|
|
431
|
+
* This is the required companion to `manualPagination` whenever sort headers are live; without
|
|
432
|
+
* it a server-paginated table sorts one page and the "of N" bar presents that as a sort of all N.
|
|
433
|
+
* @default false
|
|
434
|
+
* @example
|
|
435
|
+
* <BasaltDataTable
|
|
436
|
+
* data={page.rows}
|
|
437
|
+
* columns={columns}
|
|
438
|
+
* enablePagination
|
|
439
|
+
* manualPagination
|
|
440
|
+
* manualSorting
|
|
441
|
+
* rowCount={page.total}
|
|
442
|
+
* onSortingChange={(s) => refetch({ sort: s[0] })}
|
|
443
|
+
* />
|
|
444
|
+
*/
|
|
445
|
+
manualSorting?: boolean
|
|
446
|
+
/** Stripe alternate rows. Forwarded to Mantine `Table` — `'odd'`/`'even'` pick the phase. */
|
|
447
|
+
striped?: boolean | 'odd' | 'even'
|
|
448
|
+
/** Highlight hovered rows. Forwarded to Mantine `Table`. */
|
|
449
|
+
highlightOnHover?: boolean
|
|
450
|
+
/**
|
|
451
|
+
* Rendered when no rows are VISIBLE — `data` is empty, or a filter/page matches none of it.
|
|
452
|
+
* Falls back to a simple message when omitted. (Before 1.25.0 this tracked `data.length` alone,
|
|
453
|
+
* so a search matching nothing rendered a blank `<tbody>` with no message at all.)
|
|
454
|
+
*/
|
|
455
|
+
emptyState?: ReactNode
|
|
456
|
+
/**
|
|
457
|
+
* When true, renders skeleton placeholder rows instead of the empty-state branch.
|
|
458
|
+
* The header remains visible. Use while async data is loading.
|
|
459
|
+
*
|
|
460
|
+
* Superseded by `query`, which resolves this branch AND the two this prop cannot express — pass
|
|
461
|
+
* both and `query` wins, with a dev warning.
|
|
462
|
+
*/
|
|
463
|
+
isLoading?: boolean
|
|
464
|
+
/**
|
|
465
|
+
* The result behind `data`, resolved into a body: pending → the skeleton rows below, error with
|
|
466
|
+
* no data → an `ErrorState` row spanning the table (with the query's own `refetch` behind
|
|
467
|
+
* Retry), anything else → the rows, or `emptyState` when none are visible.
|
|
468
|
+
*
|
|
469
|
+
* This is law C3's uniform container contract (`docs/CONTROLS-SPEC.md` §1). The table had NO
|
|
470
|
+
* error branch at all (components audit #3), so a 500 rendered *No data to display.* — the
|
|
471
|
+
* exact false claim `QueryState` was built to delete, one container over.
|
|
472
|
+
*
|
|
473
|
+
* The header, toolbar, `<thead>` and pagination bar stay drawn through every branch: only the
|
|
474
|
+
* `<tbody>` swaps, which is why this is not `QueryState` wrapped around the table. A refetch
|
|
475
|
+
* that fails while rows are already on screen keeps the rows — compose `QueryState` AROUND the
|
|
476
|
+
* table when you want its cached-data banner too.
|
|
477
|
+
*
|
|
478
|
+
* @example
|
|
479
|
+
* const q = useQuery({ queryKey: ['rows'], queryFn: () => api.rows() })
|
|
480
|
+
* <BasaltDataTable data={q.data ?? []} columns={columns} query={q} />
|
|
481
|
+
*/
|
|
482
|
+
query?: QueryStateLike<unknown>
|
|
483
|
+
/**
|
|
484
|
+
* Number of skeleton rows to render when `isLoading` is true.
|
|
485
|
+
* @default 5
|
|
486
|
+
*/
|
|
487
|
+
skeletonRows?: number
|
|
488
|
+
/**
|
|
489
|
+
* Initial sorting state. Drives `useState` initial value — useful for restoring
|
|
490
|
+
* sort order from URL search params (e.g. `initialSorting={Route.useSearch().sorting}`).
|
|
491
|
+
* @example
|
|
492
|
+
* // URL-sync pattern with TanStack Router
|
|
493
|
+
* const { sorting } = Route.useSearch()
|
|
494
|
+
* <BasaltDataTable initialSorting={sorting} onSortingChange={(s) => navigate({ search: { sorting: s } })} … />
|
|
495
|
+
*/
|
|
496
|
+
initialSorting?: SortingState
|
|
497
|
+
/**
|
|
498
|
+
* Called whenever the internal sorting state changes. Receives the new `SortingState`.
|
|
499
|
+
* The table continues to manage sorting internally (uncontrolled) when this is omitted.
|
|
500
|
+
*/
|
|
501
|
+
onSortingChange?: (sorting: SortingState) => void
|
|
502
|
+
|
|
503
|
+
// ── Toolbar (search + facets + actions) ──────────────────────────────────────
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Shows a global search `TextInput` above the table, wired to TanStack's `globalFilter` state
|
|
507
|
+
* (substring match against every column's stringified cell value).
|
|
508
|
+
* @default false
|
|
509
|
+
* @example
|
|
510
|
+
* <BasaltDataTable data={rows} columns={columns} enableGlobalFilter searchIcon={<IconSearch size={14} />} />
|
|
511
|
+
*/
|
|
512
|
+
enableGlobalFilter?: boolean
|
|
513
|
+
/**
|
|
514
|
+
* Placeholder for the global search input.
|
|
515
|
+
* @default 'Search…'
|
|
516
|
+
*/
|
|
517
|
+
globalFilterPlaceholder?: string
|
|
518
|
+
/**
|
|
519
|
+
* Leading icon rendered inside the global search input (Mantine `leftSection`). basalt-ui ships
|
|
520
|
+
* no icon set — pass any `ReactNode` (e.g. `<IconSearch size={14} />`).
|
|
521
|
+
*/
|
|
522
|
+
searchIcon?: ReactNode
|
|
523
|
+
/**
|
|
524
|
+
* Initial global filter value. Drives `useState` initial value — mirrors `initialSorting` for
|
|
525
|
+
* restoring the search term from a URL search param.
|
|
526
|
+
*/
|
|
527
|
+
initialGlobalFilter?: string
|
|
528
|
+
/** Called whenever the internal global filter value changes. */
|
|
529
|
+
onGlobalFilterChange?: (value: string) => void
|
|
530
|
+
/**
|
|
531
|
+
* Faceted column filters, rendered in the toolbar as `EnumFilter`/`MultiSelectFilter` pills
|
|
532
|
+
* inside a `FilterSet`, wired to TanStack's per-column `columnFilters` state. The toolbar
|
|
533
|
+
* renders whenever this array is non-empty, `enableGlobalFilter` is set, or `actions` is passed.
|
|
534
|
+
* @example
|
|
535
|
+
* facets={[{ columnId: 'department', label: 'Department', options: departmentOptions }]}
|
|
536
|
+
*/
|
|
537
|
+
facets?: DataTableFacet<T>[]
|
|
538
|
+
/** Called whenever the faceted column filters change — the seam for server-side faceting. */
|
|
539
|
+
onColumnFiltersChange?: (filters: ColumnFiltersState) => void
|
|
540
|
+
/**
|
|
541
|
+
* Hand filtering to the server: the table stops narrowing `data` locally and renders every row
|
|
542
|
+
* given, while the search input and facets keep reporting through `onGlobalFilterChange` /
|
|
543
|
+
* `onColumnFiltersChange` — which is where you re-request the filtered page.
|
|
544
|
+
*
|
|
545
|
+
* Required alongside `manualPagination` whenever `enableGlobalFilter` or `facets` is set;
|
|
546
|
+
* without it the controls narrow one page while "of N" keeps counting the whole set.
|
|
547
|
+
* @default false
|
|
548
|
+
*/
|
|
549
|
+
manualFiltering?: boolean
|
|
550
|
+
/**
|
|
551
|
+
* Right-aligned toolbar slot (e.g. an "Export" button). Renders the toolbar row even when no
|
|
552
|
+
* search input or facets are configured. Renamed from `toolbarActions` — the toolbar (search +
|
|
553
|
+
* facets + this slot) is wrapped in `CtlSlot` (C1/C5), so its controls resolve to the `ctl` tier.
|
|
554
|
+
*
|
|
555
|
+
* Two accepted forms (law C15): a typed `BarAction[]`, projected through the same row `PageBar`
|
|
556
|
+
* and `ActionGroup` use — ≤3 inline, the rest folded into `More`, one kebab below `sm` — or an
|
|
557
|
+
* opaque `ReactNode`, rendered verbatim exactly as before.
|
|
558
|
+
*/
|
|
559
|
+
actions?: SlotActions
|
|
560
|
+
|
|
561
|
+
// ── Row activation + selection ────────────────────────────────────────────────
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Makes each row openable — click, or Enter on a focused row — with the ROW's original datum.
|
|
565
|
+
* The hook that pairs a table with `PageAside`: row → detail, without the caller adding an
|
|
566
|
+
* `onClick` to every cell renderer.
|
|
567
|
+
*
|
|
568
|
+
* The row keeps its native `<tr>` semantics and takes `tabIndex=0` plus `data-activatable`,
|
|
569
|
+
* never `role="button"`: a role swap takes the row out of the table's row/cell semantics, so a
|
|
570
|
+
* screen reader stops announcing each cell's column header — which is the reason the data is in
|
|
571
|
+
* a table. Cursor and focus ring come from the module class.
|
|
572
|
+
*
|
|
573
|
+
* A click that originates inside the selection checkbox does NOT activate (the checkbox stops
|
|
574
|
+
* the event); a click on a control your own `cell` renders should do the same.
|
|
575
|
+
*
|
|
576
|
+
* @example
|
|
577
|
+
* <BasaltDataTable data={rows} columns={columns} onRowActivate={(row) => setSelected(row)} />
|
|
578
|
+
*/
|
|
579
|
+
onRowActivate?: (row: T) => void
|
|
580
|
+
/**
|
|
581
|
+
* Prepends a checkbox column (header = select-all on the page) and arms TanStack's row-selection
|
|
582
|
+
* feature. Selection is uncontrolled unless `rowSelection` is passed.
|
|
583
|
+
* @default false
|
|
584
|
+
*/
|
|
585
|
+
enableRowSelection?: boolean
|
|
586
|
+
/**
|
|
587
|
+
* Controlled selection — TanStack's own `RowSelectionState` (`Record<rowId, boolean>`), keyed by
|
|
588
|
+
* `getRowId` (row index by default). Omit for uncontrolled selection held by the table.
|
|
589
|
+
*/
|
|
590
|
+
rowSelection?: RowSelectionState
|
|
591
|
+
/** Called with the next `RowSelectionState` on every selection change, controlled or not. */
|
|
592
|
+
onRowSelectionChange?: (selection: RowSelectionState) => void
|
|
593
|
+
/**
|
|
594
|
+
* Stable row id, handed to TanStack. Pass one whenever the selection must survive a re-sort, a
|
|
595
|
+
* page change or a refetch — the default id is the row's INDEX, so row 3 stays "selected" when a
|
|
596
|
+
* different record moves into position 3.
|
|
597
|
+
*/
|
|
598
|
+
getRowId?: (row: T, index: number) => string
|
|
599
|
+
/**
|
|
600
|
+
* The actions a selection offers, rendered in a bar above the table while at least one row is
|
|
601
|
+
* selected — and nothing at all while none is.
|
|
602
|
+
*
|
|
603
|
+
* A FUNCTION of the selected rows, not a bare array, and that is the whole signature decision:
|
|
604
|
+
* `BarActionItem.onClick` is `() => void` (the one vocabulary every home shares, unforked), so
|
|
605
|
+
* the rows have to reach the handler through the closure the caller writes here. It also means
|
|
606
|
+
* an UNCONTROLLED table can offer bulk actions at all — the selection lives inside the table,
|
|
607
|
+
* and this is how it gets out.
|
|
608
|
+
*
|
|
609
|
+
* @example
|
|
610
|
+
* bulkActions={(rows) => [
|
|
611
|
+
* { key: 'export', label: `Export ${rows.length}`, onClick: () => exportCsv(rows) },
|
|
612
|
+
* { key: 'delete', label: 'Delete', danger: true, onClick: () => remove(rows) },
|
|
613
|
+
* ]}
|
|
614
|
+
*/
|
|
615
|
+
bulkActions?: (rows: T[]) => BarAction[]
|
|
616
|
+
|
|
617
|
+
// ── Pagination ────────────────────────────────────────────────────────────────
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Enables the bottom pagination bar ("Showing X–Y of N", rows-per-page `Select`, Mantine
|
|
621
|
+
* `Pagination`) wired to TanStack's `pagination` state. Client-side by default — see
|
|
622
|
+
* `manualPagination` for server-driven pagination.
|
|
623
|
+
* @default false
|
|
624
|
+
*/
|
|
625
|
+
enablePagination?: boolean
|
|
626
|
+
/**
|
|
627
|
+
* Rows-per-page choices offered in the pagination bar's Select.
|
|
628
|
+
* @default [10, 25, 50, 100]
|
|
629
|
+
*/
|
|
630
|
+
pageSizeOptions?: number[]
|
|
631
|
+
/**
|
|
632
|
+
* Initial pagination state. Drives `useState` initial value — mirrors `initialSorting` for
|
|
633
|
+
* restoring the page/size from a URL search param.
|
|
634
|
+
*/
|
|
635
|
+
initialPagination?: PaginationState
|
|
636
|
+
/** Called whenever the internal pagination state changes. */
|
|
637
|
+
onPaginationChange?: (pagination: PaginationState) => void
|
|
638
|
+
/**
|
|
639
|
+
* Disables local pagination slicing — pass the already-paginated page of `data` plus `rowCount`
|
|
640
|
+
* (and optionally `pageCount`) for server-driven pagination. `onPaginationChange` is where you
|
|
641
|
+
* fetch the next page.
|
|
642
|
+
*
|
|
643
|
+
* **It imposes a contract, enforced at render.** `data` is now one page, but the pagination bar
|
|
644
|
+
* says "Showing 1–25 of 412" — so every OTHER client-side control becomes a claim about 412 rows
|
|
645
|
+
* that it can only make about 25. Each has to be resolved explicitly, because only the call site
|
|
646
|
+
* knows whether the server does the work:
|
|
647
|
+
*
|
|
648
|
+
* - `rowCount` (or `pageCount`) must be given, or "of N" counts the page and the pager collapses
|
|
649
|
+
* to a single page nobody can leave;
|
|
650
|
+
* - sorting must be handed over with `manualSorting`, or switched off with `enableSorting={false}`;
|
|
651
|
+
* - `enableGlobalFilter` / `facets`, if used, must be handed over with `manualFiltering`;
|
|
652
|
+
* - `enablePagination` must be on, or `manualPagination` is inert and the page renders as if it
|
|
653
|
+
* were the whole table.
|
|
654
|
+
*
|
|
655
|
+
* Unresolved, each throws in dev and degrades to the honest render in production (no sort
|
|
656
|
+
* headers, no filter controls, no "of N") — never to the plausible wrong answer.
|
|
657
|
+
* @default false
|
|
658
|
+
* @example
|
|
659
|
+
* <BasaltDataTable
|
|
660
|
+
* data={page.rows}
|
|
661
|
+
* columns={columns}
|
|
662
|
+
* enablePagination
|
|
663
|
+
* manualPagination
|
|
664
|
+
* rowCount={page.total}
|
|
665
|
+
* initialPagination={{ pageIndex: page.index, pageSize: page.size }}
|
|
666
|
+
* onPaginationChange={(p) => fetchPage(p)}
|
|
667
|
+
* />
|
|
668
|
+
*/
|
|
669
|
+
manualPagination?: boolean
|
|
670
|
+
/** Total row count across all pages. Required for `manualPagination` to render "of N" and compute page count. */
|
|
671
|
+
rowCount?: number
|
|
672
|
+
/** Total page count, when known. Only consulted under `manualPagination`; omit to derive it from `rowCount`/`pageSize`. */
|
|
673
|
+
pageCount?: number
|
|
674
|
+
|
|
675
|
+
// ── Column pinning ────────────────────────────────────────────────────────────
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Enables sticky left/right column pinning — pinned columns stick to the edge with a panel
|
|
679
|
+
* background and a hairline shadow separator while the table scrolls horizontally. Pin columns
|
|
680
|
+
* via `initialColumnPinning` (`{ left: string[], right: string[] }`).
|
|
681
|
+
* @default false
|
|
682
|
+
* @example
|
|
683
|
+
* <BasaltDataTable data={rows} columns={columns} enablePinning initialColumnPinning={{ left: ['name'] }} />
|
|
684
|
+
*/
|
|
685
|
+
enablePinning?: boolean
|
|
686
|
+
/** Initial column-pinning state — which column ids stick to the left/right edge. */
|
|
687
|
+
initialColumnPinning?: DataTableColumnPinning<T>
|
|
688
|
+
|
|
689
|
+
// ── Body chrome ─────────────────────────────────────────────────────────────
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Caps the scrolling body (px number or CSS length) so a long table cannot blow a card out
|
|
693
|
+
* vertically — the header stays put and the rows scroll under it. Renders Mantine's
|
|
694
|
+
* `Table.ScrollContainer type="native"`, which is the same node the docs sanction as the raw
|
|
695
|
+
* escape for a bespoke table: the blessed lane and the escape hatch produce identical DOM, so
|
|
696
|
+
* adopting one does not contradict the other.
|
|
697
|
+
*
|
|
698
|
+
* `type="native"` is required rather than preferred: `ScrollArea`'s custom viewport is the
|
|
699
|
+
* positioning context a sticky `<thead>` resolves against, so the default `'scrollarea'` type
|
|
700
|
+
* pins the header to the page viewport instead of the table's box.
|
|
701
|
+
*/
|
|
702
|
+
maxHeight?: number | string
|
|
703
|
+
/**
|
|
704
|
+
* `min-width` below which the body scrolls horizontally. Setting either this or `maxHeight`
|
|
705
|
+
* turns on the CAPPED scroll container; `maxHeight` alone implies `minWidth: 0`.
|
|
706
|
+
*
|
|
707
|
+
* It is a FLOOR, not the containment switch — since the shell made `AppShell.Main` the
|
|
708
|
+
* scrollport, every table renders inside a `Table.ScrollContainer type="native"` whether or not
|
|
709
|
+
* this is set (`minWidth: 0` when it is not), so a table wider than its column scrolls inside
|
|
710
|
+
* its own card instead of widening the page. Reach for this only to hold a MINIMUM table width
|
|
711
|
+
* — e.g. so eight columns keep their readable widths and scroll rather than compress.
|
|
712
|
+
*
|
|
713
|
+
* The one table whose wrapper is MEASURED rather than declared is `stickyHeader` with neither
|
|
714
|
+
* this nor `maxHeight`; see that prop and `useMeasuredContainment` in the implementation for
|
|
715
|
+
* why, and pair it with `maxHeight` if the table must both scroll and stick.
|
|
716
|
+
*/
|
|
717
|
+
minWidth?: number | string
|
|
718
|
+
/**
|
|
719
|
+
* Sticky header row. Pair with `maxHeight` to stick within the table rather than the page.
|
|
720
|
+
*
|
|
721
|
+
* On its own — no `maxHeight`, no `minWidth` — the header sticks against the page's scrollport
|
|
722
|
+
* (`AppShell.Main`) only while the table FITS its container, and basalt measures that: the
|
|
723
|
+
* wrapper declares no overflow while it fits and flips to `overflow-x: auto` once the table is
|
|
724
|
+
* wider, publishing `data-contained`. The columns stay reachable either way and the page never
|
|
725
|
+
* scrolls sideways, but a sticky header cannot stick inside an uncapped overflow box — it has
|
|
726
|
+
* no scroll range — so it is inert at that width. A dev warning states the trade; pair this
|
|
727
|
+
* with `maxHeight` for a table that must both scroll and stick at every width.
|
|
728
|
+
*/
|
|
729
|
+
stickyHeader?: boolean
|
|
730
|
+
/**
|
|
731
|
+
* Offset for a sticky header sitting under whatever chrome scrolls above it (px number or CSS
|
|
732
|
+
* length).
|
|
733
|
+
*
|
|
734
|
+
* PAGE-scroll only, and IGNORED whenever `maxHeight`/`minWidth` turn on the scroll container:
|
|
735
|
+
* that box is then the header's own scrollport, so an offset would park the `<thead>` that many
|
|
736
|
+
* pixels down inside the body — painting it over the rows instead of above them. Inside the
|
|
737
|
+
* container the header sticks to the scroller's own top edge.
|
|
738
|
+
*
|
|
739
|
+
* Inside `BasaltShell` the page's scrollport is `AppShell.Main`, and the app header sits
|
|
740
|
+
* OUTSIDE it — so the only chrome left to clear is `PageBar` row 2's band:
|
|
741
|
+
* `var(--basalt-page-bar-h, 0px)`, not the header height plus it.
|
|
742
|
+
*/
|
|
743
|
+
stickyHeaderOffset?: number | string
|
|
744
|
+
/** Vertical cell padding. Forwarded to Mantine `Table`. */
|
|
745
|
+
verticalSpacing?: MantineSpacing
|
|
746
|
+
/** Horizontal cell padding. Forwarded to Mantine `Table`. */
|
|
747
|
+
horizontalSpacing?: MantineSpacing
|
|
748
|
+
/** Row separators. Forwarded to Mantine `Table`. @default true (Mantine's own default) */
|
|
749
|
+
withRowBorders?: boolean
|
|
750
|
+
/**
|
|
751
|
+
* Outer table border — a full box around the table.
|
|
752
|
+
*
|
|
753
|
+
* **`false` by default since this minor** (it was `true`). Three separate faults, all visible on
|
|
754
|
+
* the playground's `Controls (mobile)` page at 390px: its TOP edge sat directly under the table's
|
|
755
|
+
* own `WidgetHeader`, separating a header from its own content; its BOTTOM edge landed on the last
|
|
756
|
+
* row's own hairline, two rules at zero distance; and its left/right edges carried no information
|
|
757
|
+
* at all on a table that fills the page (`docs/DESIGN-SPEC.md` §8 — a border is a layout divider,
|
|
758
|
+
* and there is nothing here to divide). The head-row rule and the between-row rules stay, so the
|
|
759
|
+
* table still reads as a table.
|
|
760
|
+
*
|
|
761
|
+
* Pass `true` for a table that genuinely needs a frame — a small table floating in whitespace
|
|
762
|
+
* beside other content, where the box IS the grouping.
|
|
763
|
+
*
|
|
764
|
+
* @default false
|
|
765
|
+
*/
|
|
766
|
+
withTableBorder?: boolean
|
|
767
|
+
}
|
|
507
768
|
|
|
508
769
|
// ── Sort indicator ────────────────────────────────────────────────────────────
|
|
509
770
|
|
|
@@ -538,19 +799,38 @@ const RANGE_LABEL_STYLE: CSSProperties = {
|
|
|
538
799
|
const DEFAULT_PAGE_SIZE_OPTIONS = [10, 25, 50, 100]
|
|
539
800
|
|
|
540
801
|
/**
|
|
541
|
-
* The header row's two halves. The lead
|
|
542
|
-
*
|
|
543
|
-
*
|
|
802
|
+
* The header row's two halves. The lead GROWS and the toolbar does not — a title can ellipsize into
|
|
803
|
+
* whatever is left, and a search field that grew with it was 600px on a wide table.
|
|
804
|
+
*
|
|
805
|
+
* Both SHRINK, and that half is not a preference. `CtlSlot` between the header row and the toolbar
|
|
806
|
+
* is `display: contents`, so the toolbar `Group` IS the row's flex item — and with the
|
|
807
|
+
* `flex: 0 0 auto` it used to carry (plus flex's default `min-width: auto`) it could not give up a
|
|
808
|
+
* pixel. MEASURED at 390x844 on `/data`: a 220px search beside a 230px facet pill row, gap 11, is a
|
|
809
|
+
* 461px item in a 302px column, and `AppShell.Main` read `scrollWidth` 505 against `clientWidth`
|
|
810
|
+
* 390 — the whole app draggable sideways over a table header. `0 1 auto` + `min-width: 0` lets the
|
|
811
|
+
* item shrink to the column, and `wrap="wrap"` then does the rest: the search takes one line and
|
|
812
|
+
* the pills the next. Nothing here is width-gated, so it holds in a narrow grid cell at any
|
|
813
|
+
* viewport, the same way the chart tier resolves.
|
|
544
814
|
*/
|
|
545
815
|
const TABLE_HEADER_LEAD_STYLE: CSSProperties = { flex: '1 1 auto', minWidth: 0 }
|
|
546
|
-
const TABLE_TOOLBAR_STYLE: CSSProperties = { flex: '0
|
|
816
|
+
const TABLE_TOOLBAR_STYLE: CSSProperties = { flex: '0 1 auto', minWidth: 0 }
|
|
547
817
|
/**
|
|
548
818
|
* The toolbar search's width. Mantine's Input grows to its container, which in a `justify:
|
|
549
819
|
* space-between` row means "whatever is left" — so the field was 600px on a wide table and 180px on a
|
|
550
820
|
* narrow one, for the same one-word query. A stated width also makes the toolbar's own minimum width
|
|
551
821
|
* knowable, which is what lets it wrap under the title below `sm` with no media query.
|
|
822
|
+
*
|
|
823
|
+
* It is a BASIS, not a `width`: `flex: 0 1 <SEARCH_WIDTH>px` holds the field at 220px wherever 220
|
|
824
|
+
* is available and lets it shrink where it is not, floored at `12ch` (a legible query plus the
|
|
825
|
+
* search glyph) and capped at `100%` so it can never be the box that outgrows its column. A literal
|
|
826
|
+
* `w={220}` in a 302px column was half of the overflow above.
|
|
552
827
|
*/
|
|
553
828
|
const SEARCH_WIDTH = 220
|
|
829
|
+
const TABLE_SEARCH_STYLE: CSSProperties = {
|
|
830
|
+
flex: `0 1 ${SEARCH_WIDTH}px`,
|
|
831
|
+
minWidth: '12ch',
|
|
832
|
+
maxWidth: '100%',
|
|
833
|
+
}
|
|
554
834
|
/** The rows-per-page Select's width — see `SEARCH_WIDTH` for why a toolbar field states one. */
|
|
555
835
|
const PAGE_SIZE_WIDTH = 116
|
|
556
836
|
|
|
@@ -606,13 +886,6 @@ function getPinnedCellStyle<T>(
|
|
|
606
886
|
|
|
607
887
|
// ── The manual-pagination contract ────────────────────────────────────────────
|
|
608
888
|
|
|
609
|
-
/** The house dev gate (`src/provider`, `src/charts/kinds/BandStrip` use the same expression):
|
|
610
|
-
* `basaltViteConfig` defines `process.env.NODE_ENV`, so a production bundle constant-folds this to
|
|
611
|
-
* `false` and drops the throw. Read per call, never hoisted, so a test can flip it. */
|
|
612
|
-
function isDev(): boolean {
|
|
613
|
-
return process.env['NODE_ENV'] !== 'production'
|
|
614
|
-
}
|
|
615
|
-
|
|
616
889
|
/** One client-side control that `manualPagination` turns into a claim the table cannot support. */
|
|
617
890
|
type ManualPaginationBreach = 'inert' | 'total' | 'sorting' | 'filtering'
|
|
618
891
|
|
|
@@ -695,6 +968,99 @@ function enforceManualPaginationContract(breaches: ManualPaginationBreach[]): vo
|
|
|
695
968
|
console.error(message)
|
|
696
969
|
}
|
|
697
970
|
|
|
971
|
+
/**
|
|
972
|
+
* MEASURED CONTAINMENT — the wrapper for the ONE table shape whose overflow mode cannot be decided
|
|
973
|
+
* statically: `stickyHeader` with neither `maxHeight` nor `minWidth`.
|
|
974
|
+
*
|
|
975
|
+
* Every other table renders inside `Table.ScrollContainer type="native"`, because a bare `<table>`
|
|
976
|
+
* sizes to its own min-content and a five-column one measured 448px inside a 390px viewport,
|
|
977
|
+
* dragging the whole page sideways (`tests/layout/no-horizontal-overflow.layout.test.ts`). This
|
|
978
|
+
* shape cannot take that container unconditionally: an `overflow-x: auto` box computes `overflow-y`
|
|
979
|
+
* to `auto` as well, which makes it the sticky header's own scrollport — and with no height cap
|
|
980
|
+
* that box has no scroll range at all, so the `<thead>` scrolls clean away (MEASURED in Chrome:
|
|
981
|
+
* top 10.5 → −389.5 across a 400px scroll). Containing it unconditionally deletes the feature;
|
|
982
|
+
* leaving it bare unconditionally widens the page.
|
|
983
|
+
*
|
|
984
|
+
* So the wrapper's overflow is decided by MEASUREMENT, the same fit-check `useTrackFits`
|
|
985
|
+
* (`controls/panel-row.tsx`) runs for a segmented track. While the table FITS its wrapper the
|
|
986
|
+
* wrapper is `overflow: visible` — bare, and the page-sticky header sticks against `AppShell.Main`
|
|
987
|
+
* exactly as it did before. Once the table is WIDER the wrapper flips to `overflow-x: auto`, the
|
|
988
|
+
* columns stay reachable by horizontal scroll, and the header is necessarily inert at that width.
|
|
989
|
+
* That is the honest trade: a header that stops sticking is recoverable, a page that scrolls
|
|
990
|
+
* sideways takes every fixed and sticky element on it with it.
|
|
991
|
+
*
|
|
992
|
+
* Three properties that are easy to lose:
|
|
993
|
+
*
|
|
994
|
+
* - **The observer stays alive in BOTH states** — no one-way latch, unlike `useTrackFits`. The
|
|
995
|
+
* wrapper must revert to bare when the space comes back (the window widened, the sidebar
|
|
996
|
+
* collapsed, the aside closed), and a contained table's `offsetWidth` is still its min-content
|
|
997
|
+
* width, so the same comparison keeps answering correctly from inside the contained state.
|
|
998
|
+
* - **Equality counts as FITS**, which is what stops the two states oscillating: a contained
|
|
999
|
+
* wrapper that grows past the table reads `offsetWidth === clientWidth` (the `width: 100%` table
|
|
1000
|
+
* resolves against the container) and flips back exactly once.
|
|
1001
|
+
* - **A zero `clientWidth` is `unknown`, not overflow** — the ancestor chain has not been laid out
|
|
1002
|
+
* yet (the aside animates its width in from 0, `docs/ASIDE-SPEC.md` §0). Committing to contained
|
|
1003
|
+
* on that reading would wrap a table that was never too wide.
|
|
1004
|
+
*
|
|
1005
|
+
* `useLayoutEffect` runs before paint, so a table that is already measurable resolves without a
|
|
1006
|
+
* flash of the wrong mode. SSR reaches neither the effect nor a `window`, and the default state is
|
|
1007
|
+
* bare — the same node the server and the first client paint both render.
|
|
1008
|
+
*/
|
|
1009
|
+
function useMeasuredContainment(active: boolean): {
|
|
1010
|
+
wrapperRef: RefObject<HTMLDivElement | null>
|
|
1011
|
+
contained: boolean
|
|
1012
|
+
} {
|
|
1013
|
+
const wrapperRef = useRef<HTMLDivElement>(null)
|
|
1014
|
+
const [contained, setContained] = useState(false)
|
|
1015
|
+
|
|
1016
|
+
useLayoutEffect(() => {
|
|
1017
|
+
if (!active) return
|
|
1018
|
+
const wrapper = wrapperRef.current
|
|
1019
|
+
if (wrapper === null) return
|
|
1020
|
+
|
|
1021
|
+
// Three answers, not two — see the docblock. `unknown` moves neither the state nor the
|
|
1022
|
+
// observer; it is simply not an answer yet.
|
|
1023
|
+
const checkFit = (): 'fits' | 'contained' | 'unknown' => {
|
|
1024
|
+
const table = wrapper.querySelector('table')
|
|
1025
|
+
if (table === null) return 'unknown'
|
|
1026
|
+
if (wrapper.clientWidth === 0 || table.offsetWidth === 0) return 'unknown'
|
|
1027
|
+
return table.offsetWidth <= wrapper.clientWidth ? 'fits' : 'contained'
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
const apply = (): void => {
|
|
1031
|
+
const result = checkFit()
|
|
1032
|
+
if (result === 'unknown') return
|
|
1033
|
+
const next = result === 'contained'
|
|
1034
|
+
setContained((current) => (current === next ? current : next))
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
if (typeof ResizeObserver === 'undefined') {
|
|
1038
|
+
// No recovery path without an observer, so the one same-tick read is this environment's
|
|
1039
|
+
// only chance. `unknown` stays bare, which is the pre-measurement default anyway.
|
|
1040
|
+
apply()
|
|
1041
|
+
return
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
const observer = new ResizeObserver(apply)
|
|
1045
|
+
observer.observe(wrapper)
|
|
1046
|
+
const table = wrapper.querySelector('table')
|
|
1047
|
+
// The TABLE too, not only the wrapper: a column set that re-measures (a facet filter changing
|
|
1048
|
+
// the widest cell, a font settling) moves the table's min-content width while the wrapper's
|
|
1049
|
+
// box never changes at all.
|
|
1050
|
+
if (table !== null) observer.observe(table)
|
|
1051
|
+
|
|
1052
|
+
// Same check once, synchronously, so an already-settled table resolves before paint rather
|
|
1053
|
+
// than waiting for the observer's first async tick.
|
|
1054
|
+
apply()
|
|
1055
|
+
|
|
1056
|
+
return () => {
|
|
1057
|
+
observer.disconnect()
|
|
1058
|
+
}
|
|
1059
|
+
}, [active])
|
|
1060
|
+
|
|
1061
|
+
return { wrapperRef, contained: active && contained }
|
|
1062
|
+
}
|
|
1063
|
+
|
|
698
1064
|
// ── Component ─────────────────────────────────────────────────────────────────
|
|
699
1065
|
|
|
700
1066
|
/**
|
|
@@ -722,48 +1088,63 @@ function enforceManualPaginationContract(breaches: ManualPaginationBreach[]): vo
|
|
|
722
1088
|
* emptyState={<Text c="dimmed">No users found.</Text>}
|
|
723
1089
|
* />
|
|
724
1090
|
*/
|
|
725
|
-
export function BasaltDataTable<T>({
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
data,
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
1091
|
+
export function BasaltDataTable<T>(props: BasaltDataTableProps<T>) {
|
|
1092
|
+
// F-ERR-1: without this, a table missing `data`/`columns` fails deep inside `useReactTable` as a
|
|
1093
|
+
// raw `TypeError` caught by `BasaltErrorBoundary` — a blank subtree with no message naming either
|
|
1094
|
+
// prop.
|
|
1095
|
+
assertRequiredProps('BasaltDataTable', props, ['data', 'columns'])
|
|
1096
|
+
const {
|
|
1097
|
+
title,
|
|
1098
|
+
icon,
|
|
1099
|
+
subtitle,
|
|
1100
|
+
data,
|
|
1101
|
+
columns,
|
|
1102
|
+
enableSorting = true,
|
|
1103
|
+
manualSorting = false,
|
|
1104
|
+
striped,
|
|
1105
|
+
highlightOnHover,
|
|
1106
|
+
emptyState,
|
|
1107
|
+
isLoading = false,
|
|
1108
|
+
query,
|
|
1109
|
+
skeletonRows = 5,
|
|
1110
|
+
initialSorting,
|
|
1111
|
+
onSortingChange,
|
|
1112
|
+
enableGlobalFilter = false,
|
|
1113
|
+
globalFilterPlaceholder = 'Search…',
|
|
1114
|
+
searchIcon,
|
|
1115
|
+
initialGlobalFilter,
|
|
1116
|
+
onGlobalFilterChange,
|
|
1117
|
+
facets,
|
|
1118
|
+
onColumnFiltersChange,
|
|
1119
|
+
manualFiltering = false,
|
|
1120
|
+
actions,
|
|
1121
|
+
onRowActivate,
|
|
1122
|
+
enableRowSelection = false,
|
|
1123
|
+
rowSelection,
|
|
1124
|
+
onRowSelectionChange,
|
|
1125
|
+
getRowId,
|
|
1126
|
+
bulkActions,
|
|
1127
|
+
enablePagination = false,
|
|
1128
|
+
pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,
|
|
1129
|
+
initialPagination,
|
|
1130
|
+
onPaginationChange,
|
|
1131
|
+
manualPagination = false,
|
|
1132
|
+
rowCount,
|
|
1133
|
+
pageCount,
|
|
1134
|
+
enablePinning = false,
|
|
1135
|
+
initialColumnPinning,
|
|
1136
|
+
maxHeight,
|
|
1137
|
+
minWidth,
|
|
1138
|
+
stickyHeader,
|
|
1139
|
+
stickyHeaderOffset,
|
|
1140
|
+
verticalSpacing,
|
|
1141
|
+
horizontalSpacing,
|
|
1142
|
+
withRowBorders,
|
|
1143
|
+
withTableBorder = false,
|
|
1144
|
+
className,
|
|
1145
|
+
style,
|
|
1146
|
+
classNames,
|
|
1147
|
+
} = props
|
|
767
1148
|
const [sorting, setSorting] = useState<SortingState>(initialSorting ?? [])
|
|
768
1149
|
const [globalFilter, setGlobalFilter] = useState(initialGlobalFilter ?? '')
|
|
769
1150
|
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
|
|
@@ -771,6 +1152,51 @@ export function BasaltDataTable<T>({
|
|
|
771
1152
|
initialPagination ?? { pageIndex: 0, pageSize: pageSizeOptions[0] ?? 10 },
|
|
772
1153
|
)
|
|
773
1154
|
const [columnPinning, setColumnPinning] = useState<ColumnPinningState>(initialColumnPinning ?? {})
|
|
1155
|
+
// Uncontrolled by default; `rowSelection`, when passed, is the truth and this only mirrors it so
|
|
1156
|
+
// the updater below has a base to apply against.
|
|
1157
|
+
const [internalRowSelection, setInternalRowSelection] = useState<RowSelectionState>({})
|
|
1158
|
+
const rowSelectionState = rowSelection ?? internalRowSelection
|
|
1159
|
+
|
|
1160
|
+
// `stickyHeaderOffset` is DROPPED whenever the table owns a scroll container — see
|
|
1161
|
+
// `resolvedStickyHeaderOffset` below for why it is always wrong there. Dropping it silently is
|
|
1162
|
+
// the part worth a message: the header still sticks, so the table looks right until it overlaps
|
|
1163
|
+
// the page chrome the offset was measured against, and nothing anywhere says the prop was ignored.
|
|
1164
|
+
//
|
|
1165
|
+
// The second check is `query` beside `isLoading`: `query` resolves the pending branch AND the two
|
|
1166
|
+
// `isLoading` cannot express, so it wins outright. Silently, that reads as the boolean having no
|
|
1167
|
+
// effect — which is indistinguishable from a bug in the caller's own loading flag.
|
|
1168
|
+
useValidateProps(
|
|
1169
|
+
'BasaltDataTable',
|
|
1170
|
+
() => [
|
|
1171
|
+
stickyHeaderOffset === undefined || (maxHeight === undefined && minWidth === undefined)
|
|
1172
|
+
? null
|
|
1173
|
+
: `${BASALT_PREFIX} BasaltDataTable: prop "stickyHeaderOffset" is ignored beside ` +
|
|
1174
|
+
'"maxHeight"/"minWidth" — those render a scroll container, and the sticky `<thead>` then ' +
|
|
1175
|
+
"anchors to that box's own top edge rather than to the window. Drop the offset, or drop " +
|
|
1176
|
+
'the cap and let the page scroll.',
|
|
1177
|
+
query === undefined || props.isLoading === undefined
|
|
1178
|
+
? null
|
|
1179
|
+
: `${BASALT_PREFIX} BasaltDataTable: props "query" and "isLoading" are both set — "query" ` +
|
|
1180
|
+
'wins and "isLoading" is ignored. `query` already resolves the pending branch, plus the ' +
|
|
1181
|
+
'error and empty ones the boolean cannot express. Drop "isLoading".',
|
|
1182
|
+
stickyHeader !== true || maxHeight !== undefined || minWidth !== undefined
|
|
1183
|
+
? null
|
|
1184
|
+
: `${BASALT_PREFIX} BasaltDataTable: prop "stickyHeader" with neither "maxHeight" nor ` +
|
|
1185
|
+
'"minWidth" sticks only while the table FITS its container. basalt measures it: a table ' +
|
|
1186
|
+
'wider than the space it has flips its wrapper to `overflow-x: auto` so the columns ' +
|
|
1187
|
+
'stay reachable and the page never scrolls sideways, and a sticky header cannot stick ' +
|
|
1188
|
+
'inside that box — it has no scroll range. Pair it with "maxHeight" for a table that ' +
|
|
1189
|
+
'must both scroll and stick.',
|
|
1190
|
+
],
|
|
1191
|
+
[
|
|
1192
|
+
stickyHeaderOffset,
|
|
1193
|
+
maxHeight,
|
|
1194
|
+
minWidth,
|
|
1195
|
+
stickyHeader,
|
|
1196
|
+
query === undefined,
|
|
1197
|
+
props.isLoading === undefined,
|
|
1198
|
+
],
|
|
1199
|
+
)
|
|
774
1200
|
|
|
775
1201
|
const handleSortingChange = useCallback(
|
|
776
1202
|
(updater: Updater<SortingState>) => {
|
|
@@ -816,10 +1242,29 @@ export function BasaltDataTable<T>({
|
|
|
816
1242
|
[onPaginationChange],
|
|
817
1243
|
)
|
|
818
1244
|
|
|
1245
|
+
const handleRowSelectionChange = useCallback(
|
|
1246
|
+
(updater: Updater<RowSelectionState>) => {
|
|
1247
|
+
// CONTROLLED: the caller's map is the base AND the only writer. Mirroring it into state too
|
|
1248
|
+
// would leave a copy that the caller never moves, and that stale copy becomes the selection
|
|
1249
|
+
// the moment `rowSelection` goes back to `undefined` — plus every tick would render twice for
|
|
1250
|
+
// one change. Report the next map and let the caller own it.
|
|
1251
|
+
if (rowSelection !== undefined) {
|
|
1252
|
+
onRowSelectionChange?.(typeof updater === 'function' ? updater(rowSelection) : updater)
|
|
1253
|
+
return
|
|
1254
|
+
}
|
|
1255
|
+
setInternalRowSelection((prev) => {
|
|
1256
|
+
const next = typeof updater === 'function' ? updater(prev) : updater
|
|
1257
|
+
onRowSelectionChange?.(next)
|
|
1258
|
+
return next
|
|
1259
|
+
})
|
|
1260
|
+
},
|
|
1261
|
+
[rowSelection, onRowSelectionChange],
|
|
1262
|
+
)
|
|
1263
|
+
|
|
819
1264
|
// Facets are TanStack `columnFilters` entries under the hood — inject an exact-match (or
|
|
820
1265
|
// any-of, for `multiple`) filterFn onto the matching column so the built-in `includesString`
|
|
821
1266
|
// auto-filter (substring match) never misfires against a Select's exact option value.
|
|
822
|
-
const
|
|
1267
|
+
const facetColumns = useMemo(() => {
|
|
823
1268
|
if (!facets || facets.length === 0) return columns
|
|
824
1269
|
const facetById = new Map(facets.map((facet) => [facet.columnId, facet]))
|
|
825
1270
|
return columns.map((column) => {
|
|
@@ -844,6 +1289,14 @@ export function BasaltDataTable<T>({
|
|
|
844
1289
|
})
|
|
845
1290
|
}, [columns, facets])
|
|
846
1291
|
|
|
1292
|
+
// The checkbox column is PREPENDED here rather than asked of the caller, so `columns` stays the
|
|
1293
|
+
// caller's own data columns and `meta.align` / `numeral` never have to reason about a synthetic
|
|
1294
|
+
// one. Everything counting columns downstream reads `columnCount`, below.
|
|
1295
|
+
const tableColumns = useMemo(
|
|
1296
|
+
() => (enableRowSelection ? [selectionColumn<T>(), ...facetColumns] : facetColumns),
|
|
1297
|
+
[facetColumns, enableRowSelection],
|
|
1298
|
+
)
|
|
1299
|
+
|
|
847
1300
|
// Enforced before the table is built, so the degraded options below are the ones TanStack gets —
|
|
848
1301
|
// a degraded table must not merely hide a control, it must not compute the answer either.
|
|
849
1302
|
const hasFacets = Boolean(facets && facets.length > 0)
|
|
@@ -874,12 +1327,18 @@ export function BasaltDataTable<T>({
|
|
|
874
1327
|
columnFilters,
|
|
875
1328
|
columnPinning,
|
|
876
1329
|
...(enablePagination && { pagination }),
|
|
1330
|
+
...(enableRowSelection && { rowSelection: rowSelectionState }),
|
|
877
1331
|
},
|
|
878
1332
|
onSortingChange: handleSortingChange,
|
|
879
1333
|
onGlobalFilterChange: handleGlobalFilterChange,
|
|
880
1334
|
onColumnFiltersChange: handleColumnFiltersChange,
|
|
881
1335
|
onColumnPinningChange: setColumnPinning,
|
|
882
1336
|
...(enablePagination && { onPaginationChange: handlePaginationChange }),
|
|
1337
|
+
...(enableRowSelection && {
|
|
1338
|
+
enableRowSelection: true,
|
|
1339
|
+
onRowSelectionChange: handleRowSelectionChange,
|
|
1340
|
+
}),
|
|
1341
|
+
...(getRowId !== undefined && { getRowId }),
|
|
883
1342
|
getCoreRowModel: getCoreRowModel(),
|
|
884
1343
|
getSortedRowModel: getSortedRowModel(),
|
|
885
1344
|
getFilteredRowModel: getFilteredRowModel(),
|
|
@@ -903,17 +1362,76 @@ export function BasaltDataTable<T>({
|
|
|
903
1362
|
const showSearch = enableGlobalFilter && filteringEnabled
|
|
904
1363
|
const showFacets = hasFacets && filteringEnabled
|
|
905
1364
|
const showToolbar = showSearch || showFacets || Boolean(actions)
|
|
1365
|
+
|
|
1366
|
+
// ── The query branch (law C3) ───────────────────────────────────────────────
|
|
1367
|
+
// `query`, when given, owns the body: it resolves the pending branch `isLoading` used to own,
|
|
1368
|
+
// plus the error branch the table had none of. `isLoading` stays live for every caller who never
|
|
1369
|
+
// had a query result to hand — the dev warning above covers the pair.
|
|
1370
|
+
const branch = query === undefined ? undefined : dataQueryBranch('BasaltDataTable', query)
|
|
1371
|
+
const showSkeleton = branch === undefined ? isLoading : branch === 'pending'
|
|
1372
|
+
const queryError = branch === 'error' && query !== undefined ? query : undefined
|
|
1373
|
+
|
|
1374
|
+
// Every colSpan and every skeleton row counts the RENDERED columns, which is one more than
|
|
1375
|
+
// `columns` once the checkbox column is prepended — a stale count left the empty state and the
|
|
1376
|
+
// error row one cell short of the header.
|
|
1377
|
+
const columnCount = columns.length + (enableRowSelection ? 1 : 0)
|
|
1378
|
+
|
|
1379
|
+
// `getSelectedRowModel()` is TanStack's own memo, so its `rows` array keeps its identity until
|
|
1380
|
+
// the selection or the row model actually moves — which is what makes both memos below hold.
|
|
1381
|
+
const selectedModelRows = enableRowSelection ? table.getSelectedRowModel().rows : undefined
|
|
1382
|
+
const selectedRows = useMemo(
|
|
1383
|
+
() => selectedModelRows?.map((row) => row.original) ?? [],
|
|
1384
|
+
[selectedModelRows],
|
|
1385
|
+
)
|
|
1386
|
+
// `bulkActions` is the caller's function of the selection, and calling it inline rebuilt every
|
|
1387
|
+
// action object on every render of the table — new `onClick` identities into the whole bar, for a
|
|
1388
|
+
// selection that had not changed.
|
|
1389
|
+
const bulkBarActions = useMemo(
|
|
1390
|
+
() =>
|
|
1391
|
+
bulkActions === undefined || selectedRows.length === 0
|
|
1392
|
+
? undefined
|
|
1393
|
+
: bulkActions(selectedRows),
|
|
1394
|
+
[bulkActions, selectedRows],
|
|
1395
|
+
)
|
|
906
1396
|
const headerGroups = enablePinning ? getOrderedHeaderGroups(table) : table.getHeaderGroups()
|
|
907
1397
|
// The rows actually rendered, not `data` — a filter or a page index that matches nothing leaves
|
|
908
1398
|
// `data` non-empty while the body has nothing in it, and a blank body reads as a broken table.
|
|
909
1399
|
const rows = table.getRowModel().rows
|
|
910
1400
|
|
|
1401
|
+
// A capped body or a horizontal floor both need the same native scroll node. Everything else —
|
|
1402
|
+
// pinning included — either takes that same container (the uncapped, non-sticky default) or the
|
|
1403
|
+
// measured wrapper below; there is no third overflow box.
|
|
1404
|
+
const scrolls = maxHeight !== undefined || minWidth !== undefined
|
|
1405
|
+
// `stickyHeaderOffset` is a WINDOW-scroll concept — the height of whatever fixed chrome the page
|
|
1406
|
+
// scrolls under (the AppShell header plus `PageBar` row 2). Inside the scroll container it is
|
|
1407
|
+
// always wrong: that box is the sticky header's own scrollport, so the offset parks the `<thead>`
|
|
1408
|
+
// that many pixels DOWN INSIDE the body rather than above it. MEASURED on `/data-stress`
|
|
1409
|
+
// (`stickyHeader` + `minWidth`, no `maxHeight`): `calc(48px + 46px)` put the header at y=339 with
|
|
1410
|
+
// row 1 at y=284 and row 2 at y=327 — the header painted over row 2 at initial scroll, at 1440x900
|
|
1411
|
+
// and 390x844 alike. `overflow-x: auto` computes `overflow-y` to `auto` too, so the container is a
|
|
1412
|
+
// scrollport even with no `maxHeight` and the header never scrolls back into place. The scroller's
|
|
1413
|
+
// own top edge is the only correct anchor here, so the offset is dropped rather than honoured.
|
|
1414
|
+
//
|
|
1415
|
+
// THE ONE SHAPE WHOSE OVERFLOW IS MEASURED, NOT DECLARED. Every other table renders inside a
|
|
1416
|
+
// `Table.ScrollContainer` (see the render branch below), because a bare `<table>` sizes to its
|
|
1417
|
+
// own min-content and a 5-column table is ~450px wide — it widened the whole page at 390px, which
|
|
1418
|
+
// is the defect `tests/layout/no-horizontal-overflow.layout.test.ts` guards. A page-scrolled
|
|
1419
|
+
// sticky header cannot take that container unconditionally, so it takes the wrapper
|
|
1420
|
+
// `useMeasuredContainment` drives instead: bare while the table fits, `overflow-x: auto` once it
|
|
1421
|
+
// does not. Its docblock carries the full accounting.
|
|
1422
|
+
const pageStickyHeader = !scrolls && stickyHeader === true
|
|
1423
|
+
const containment = useMeasuredContainment(pageStickyHeader)
|
|
1424
|
+
const resolvedStickyHeaderOffset = scrolls ? undefined : stickyHeaderOffset
|
|
1425
|
+
|
|
911
1426
|
const tableNode = (
|
|
912
1427
|
<Table
|
|
1428
|
+
className={cx(classNames?.table)}
|
|
913
1429
|
{...(striped !== undefined && { striped })}
|
|
914
1430
|
{...(highlightOnHover !== undefined && { highlightOnHover })}
|
|
915
1431
|
{...(stickyHeader !== undefined && { stickyHeader })}
|
|
916
|
-
{...(
|
|
1432
|
+
{...(resolvedStickyHeaderOffset !== undefined && {
|
|
1433
|
+
stickyHeaderOffset: resolvedStickyHeaderOffset,
|
|
1434
|
+
})}
|
|
917
1435
|
{...(verticalSpacing !== undefined && { verticalSpacing })}
|
|
918
1436
|
{...(horizontalSpacing !== undefined && { horizontalSpacing })}
|
|
919
1437
|
{...(withRowBorders !== undefined && { withRowBorders })}
|
|
@@ -970,10 +1488,25 @@ export function BasaltDataTable<T>({
|
|
|
970
1488
|
))}
|
|
971
1489
|
</Table.Thead>
|
|
972
1490
|
<Table.Tbody>
|
|
973
|
-
{
|
|
1491
|
+
{queryError !== undefined ? (
|
|
1492
|
+
// The branch the table did not have (components audit #3). It spans every rendered
|
|
1493
|
+
// column and carries the query's OWN `refetch` behind Retry, so the failure is reported
|
|
1494
|
+
// where the rows would have been — not as an empty table saying nothing failed.
|
|
1495
|
+
<Table.Tr>
|
|
1496
|
+
<Table.Td colSpan={columnCount}>
|
|
1497
|
+
<ErrorState
|
|
1498
|
+
error={queryError.error}
|
|
1499
|
+
title="Could not load"
|
|
1500
|
+
tier="section"
|
|
1501
|
+
retrying={queryError.fetchStatus === 'fetching'}
|
|
1502
|
+
onRetry={() => void queryError.refetch()}
|
|
1503
|
+
/>
|
|
1504
|
+
</Table.Td>
|
|
1505
|
+
</Table.Tr>
|
|
1506
|
+
) : showSkeleton ? (
|
|
974
1507
|
Array.from({ length: skeletonRows }, (_, rowIndex) => (
|
|
975
1508
|
<Table.Tr key={`skeleton-${rowIndex}`}>
|
|
976
|
-
{
|
|
1509
|
+
{Array.from({ length: columnCount }, (__, colIndex) => (
|
|
977
1510
|
<Table.Td key={`skeleton-${rowIndex}-${colIndex}`}>
|
|
978
1511
|
<Skeleton height={16} radius="sm" />
|
|
979
1512
|
</Table.Td>
|
|
@@ -982,7 +1515,7 @@ export function BasaltDataTable<T>({
|
|
|
982
1515
|
))
|
|
983
1516
|
) : rows.length === 0 ? (
|
|
984
1517
|
<Table.Tr>
|
|
985
|
-
<Table.Td colSpan={
|
|
1518
|
+
<Table.Td colSpan={columnCount}>
|
|
986
1519
|
{emptyState ?? (
|
|
987
1520
|
<Text c="dimmed" ta="center" size="sm" py="sm">
|
|
988
1521
|
No data to display.
|
|
@@ -1000,7 +1533,28 @@ export function BasaltDataTable<T>({
|
|
|
1000
1533
|
]
|
|
1001
1534
|
: row.getVisibleCells()
|
|
1002
1535
|
return (
|
|
1003
|
-
<Table.Tr
|
|
1536
|
+
<Table.Tr
|
|
1537
|
+
key={row.id}
|
|
1538
|
+
{...(enableRowSelection && row.getIsSelected() && { 'data-selected': true })}
|
|
1539
|
+
{...(onRowActivate !== undefined && {
|
|
1540
|
+
className: classes.activatable,
|
|
1541
|
+
'data-activatable': true,
|
|
1542
|
+
tabIndex: 0,
|
|
1543
|
+
onClick: () => onRowActivate(row.original),
|
|
1544
|
+
// Enter only. Space is the browser's own page-scroll on a focused non-button, and
|
|
1545
|
+
// stealing it from a keyboard reader moving down a long table costs more than the
|
|
1546
|
+
// second activation key buys.
|
|
1547
|
+
onKeyDown: (event: ReactKeyboardEvent<HTMLTableRowElement>) => {
|
|
1548
|
+
if (event.key !== 'Enter') return
|
|
1549
|
+
// Only the ROW's own Enter. A cell may hold a button, a link or the selection
|
|
1550
|
+
// checkbox, and keydown bubbles — so without this an Enter on a nested control
|
|
1551
|
+
// fired that control AND opened the row's detail behind it.
|
|
1552
|
+
if (event.target !== event.currentTarget) return
|
|
1553
|
+
event.preventDefault()
|
|
1554
|
+
onRowActivate(row.original)
|
|
1555
|
+
},
|
|
1556
|
+
})}
|
|
1557
|
+
>
|
|
1004
1558
|
{cells.map((cell) => {
|
|
1005
1559
|
const pinnedStyle = enablePinning
|
|
1006
1560
|
? getPinnedCellStyle(table, cell.column)
|
|
@@ -1030,10 +1584,6 @@ export function BasaltDataTable<T>({
|
|
|
1030
1584
|
</Table>
|
|
1031
1585
|
)
|
|
1032
1586
|
|
|
1033
|
-
// A capped body or a horizontal floor both need the same native scroll node; pinning keeps its
|
|
1034
|
-
// simpler overflow-x Box when neither is set.
|
|
1035
|
-
const scrolls = maxHeight !== undefined || minWidth !== undefined
|
|
1036
|
-
|
|
1037
1587
|
const paginationState = table.getState().pagination
|
|
1038
1588
|
// The CURRENT page size is always an option, even when it is not in `pageSizeOptions`. Mantine's
|
|
1039
1589
|
// Select renders EMPTY when its `value` matches no row, so a table opened at
|
|
@@ -1047,7 +1597,7 @@ export function BasaltDataTable<T>({
|
|
|
1047
1597
|
const rangeEnd = Math.min((paginationState.pageIndex + 1) * paginationState.pageSize, total)
|
|
1048
1598
|
|
|
1049
1599
|
return (
|
|
1050
|
-
|
|
1600
|
+
<div className={cx(classNames?.root, className)} {...(style !== undefined && { style })}>
|
|
1051
1601
|
{/*
|
|
1052
1602
|
* ONE header row: the `WidgetHeader` (title · count) on the left, the toolbar right-aligned in
|
|
1053
1603
|
* the SAME row. It was two stacked rows with `mb="xs"` on each, so a titled table with search
|
|
@@ -1074,11 +1624,17 @@ export function BasaltDataTable<T>({
|
|
|
1074
1624
|
) : null}
|
|
1075
1625
|
{showToolbar && (
|
|
1076
1626
|
<CtlSlot>
|
|
1077
|
-
<Group
|
|
1627
|
+
<Group
|
|
1628
|
+
className={cx(classNames?.toolbar)}
|
|
1629
|
+
gap="xs"
|
|
1630
|
+
wrap="wrap"
|
|
1631
|
+
align="center"
|
|
1632
|
+
style={TABLE_TOOLBAR_STYLE}
|
|
1633
|
+
>
|
|
1078
1634
|
{showSearch && (
|
|
1079
1635
|
<TextInput
|
|
1080
1636
|
radius="md"
|
|
1081
|
-
|
|
1637
|
+
style={TABLE_SEARCH_STYLE}
|
|
1082
1638
|
placeholder={globalFilterPlaceholder}
|
|
1083
1639
|
leftSection={searchIcon}
|
|
1084
1640
|
value={globalFilter}
|
|
@@ -1088,7 +1644,7 @@ export function BasaltDataTable<T>({
|
|
|
1088
1644
|
{showFacets && (
|
|
1089
1645
|
<FilterSet>
|
|
1090
1646
|
{facets?.map((facet) => {
|
|
1091
|
-
const column = table
|
|
1647
|
+
const column = resolveFacetColumn(table, facet)
|
|
1092
1648
|
if (!column) return null
|
|
1093
1649
|
return facet.multiple ? (
|
|
1094
1650
|
<MultiSelectFilter
|
|
@@ -1106,13 +1662,27 @@ export function BasaltDataTable<T>({
|
|
|
1106
1662
|
})}
|
|
1107
1663
|
</FilterSet>
|
|
1108
1664
|
)}
|
|
1109
|
-
{actions}
|
|
1665
|
+
{actions !== undefined && <BarActionSlot actions={actions} />}
|
|
1110
1666
|
</Group>
|
|
1111
1667
|
</CtlSlot>
|
|
1112
1668
|
)}
|
|
1113
1669
|
</Group>
|
|
1114
1670
|
)}
|
|
1115
|
-
{
|
|
1671
|
+
{bulkBarActions !== undefined && (
|
|
1672
|
+
<BulkActionBar count={selectedRows.length} actions={bulkBarActions} />
|
|
1673
|
+
)}
|
|
1674
|
+
{pageStickyHeader ? (
|
|
1675
|
+
// Pinning takes this wrapper too — a pinned column's offsets are `position: sticky` on the
|
|
1676
|
+
// cells and need no overflow box of their own, so the `overflow-x: auto` Box that used to
|
|
1677
|
+
// sit here was the exact inert-sticky defect this shape exists to avoid.
|
|
1678
|
+
<div
|
|
1679
|
+
ref={containment.wrapperRef}
|
|
1680
|
+
className={classes.containment}
|
|
1681
|
+
data-contained={containment.contained ? 'true' : 'false'}
|
|
1682
|
+
>
|
|
1683
|
+
{tableNode}
|
|
1684
|
+
</div>
|
|
1685
|
+
) : (
|
|
1116
1686
|
<Table.ScrollContainer
|
|
1117
1687
|
type="native"
|
|
1118
1688
|
minWidth={minWidth ?? 0}
|
|
@@ -1120,13 +1690,16 @@ export function BasaltDataTable<T>({
|
|
|
1120
1690
|
>
|
|
1121
1691
|
{tableNode}
|
|
1122
1692
|
</Table.ScrollContainer>
|
|
1123
|
-
) : enablePinning ? (
|
|
1124
|
-
<Box style={{ overflowX: 'auto' }}>{tableNode}</Box>
|
|
1125
|
-
) : (
|
|
1126
|
-
tableNode
|
|
1127
1693
|
)}
|
|
1128
1694
|
{enablePagination && (
|
|
1129
|
-
<Group
|
|
1695
|
+
<Group
|
|
1696
|
+
className={cx(classNames?.footer)}
|
|
1697
|
+
justify="space-between"
|
|
1698
|
+
mt="xs"
|
|
1699
|
+
wrap="wrap"
|
|
1700
|
+
gap="xs"
|
|
1701
|
+
align="center"
|
|
1702
|
+
>
|
|
1130
1703
|
<Text style={RANGE_LABEL_STYLE}>
|
|
1131
1704
|
Showing {rangeStart}–{rangeEnd}
|
|
1132
1705
|
{totalIsAuthoritative ? ` of ${total}` : ''}
|
|
@@ -1151,6 +1724,6 @@ export function BasaltDataTable<T>({
|
|
|
1151
1724
|
</Group>
|
|
1152
1725
|
</Group>
|
|
1153
1726
|
)}
|
|
1154
|
-
|
|
1727
|
+
</div>
|
|
1155
1728
|
)
|
|
1156
1729
|
}
|