basalt-ui 1.30.2 → 1.32.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 +1 -1
- package/CHANGELOG.md +54 -0
- package/MIGRATING.md +256 -12
- package/agent/rules/basalt-controls.md +19 -19
- package/agent/rules/basalt-mantine.md +5 -5
- package/configs/oxlint-plugin.js +371 -4
- package/configs/oxlint-plugin.test.ts +327 -2
- package/configs/oxlint.json +2 -1
- package/dist/agent-chat/composer.d.ts.map +1 -1
- package/dist/agent-chat/composer.js +41 -43
- package/dist/agent-chat/thread-workspace.d.ts +2 -2
- package/dist/agent-chat/thread-workspace.d.ts.map +1 -1
- package/dist/agent-chat/thread-workspace.js +3 -4
- package/dist/agent-chat/use-keyboard-inset.d.ts +2 -0
- package/dist/agent-chat/use-keyboard-inset.d.ts.map +1 -0
- package/dist/agent-chat/use-keyboard-inset.js +34 -0
- package/dist/charts/cursor/touch-pin.d.ts +45 -0
- package/dist/charts/cursor/touch-pin.d.ts.map +1 -0
- package/dist/charts/cursor/touch-pin.js +79 -0
- package/dist/charts/hooks/use-discrete-cursor.d.ts +66 -0
- package/dist/charts/hooks/use-discrete-cursor.d.ts.map +1 -0
- package/dist/charts/hooks/use-discrete-cursor.js +198 -0
- package/dist/charts/hooks/use-hysteresis.d.ts +18 -0
- package/dist/charts/hooks/use-hysteresis.d.ts.map +1 -0
- package/dist/charts/hooks/use-hysteresis.js +12 -0
- package/dist/charts/hooks/useBandPlot.d.ts +7 -0
- package/dist/charts/hooks/useBandPlot.d.ts.map +1 -1
- package/dist/charts/hooks/useBandPlot.js +62 -18
- package/dist/charts/hooks/useChartCursor.d.ts +40 -11
- package/dist/charts/hooks/useChartCursor.d.ts.map +1 -1
- package/dist/charts/hooks/useChartCursor.js +102 -11
- package/dist/charts/index.d.ts +0 -1
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/index.js +0 -10
- package/dist/charts/kinds/BandStrip.d.ts.map +1 -1
- package/dist/charts/kinds/BandStrip.js +47 -82
- package/dist/charts/kinds/Donut.d.ts +9 -3
- package/dist/charts/kinds/Donut.d.ts.map +1 -1
- package/dist/charts/kinds/Donut.js +110 -48
- package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
- package/dist/charts/kinds/DualPanel.js +138 -145
- package/dist/charts/kinds/Heatmap.d.ts +7 -0
- package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
- package/dist/charts/kinds/Heatmap.js +37 -16
- package/dist/charts/kinds/MirroredBars.d.ts.map +1 -1
- package/dist/charts/kinds/MirroredBars.js +68 -111
- package/dist/charts/kinds/band-chrome.d.ts +31 -0
- package/dist/charts/kinds/band-chrome.d.ts.map +1 -0
- package/dist/charts/kinds/band-chrome.js +71 -0
- package/dist/charts/kinds/donut-layout.d.ts +47 -0
- package/dist/charts/kinds/donut-layout.d.ts.map +1 -0
- package/dist/charts/kinds/donut-layout.js +52 -0
- package/dist/charts/layout/auto-margin.d.ts +7 -2
- package/dist/charts/layout/auto-margin.d.ts.map +1 -1
- package/dist/charts/layout/auto-margin.js +12 -3
- package/dist/charts/layout/end-labels.d.ts +64 -0
- package/dist/charts/layout/end-labels.d.ts.map +1 -0
- package/dist/charts/layout/end-labels.js +76 -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 +62 -16
- package/dist/charts/primitives/CartesianChart.d.ts +7 -6
- package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
- package/dist/charts/primitives/CartesianChart.js +264 -145
- package/dist/charts/primitives/ChartCard.d.ts +0 -14
- package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.js +56 -21
- package/dist/charts/primitives/ChartFrame.d.ts +28 -22
- package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.js +116 -34
- package/dist/charts/primitives/ChartLegend.d.ts +19 -15
- package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
- package/dist/charts/primitives/ChartLegend.js +86 -73
- package/dist/charts/primitives/ChartTooltip.d.ts +5 -0
- package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
- package/dist/charts/primitives/ChartTooltip.js +20 -4
- package/dist/charts/primitives/Crosshair.js +2 -2
- package/dist/charts/primitives/HoverOverlay.d.ts +29 -9
- package/dist/charts/primitives/HoverOverlay.d.ts.map +1 -1
- package/dist/charts/primitives/HoverOverlay.js +54 -27
- package/dist/charts/primitives/HoverOverlay.module.css +13 -0
- package/dist/charts/primitives/HoverOverlay.module.css.d.ts +8 -0
- package/dist/charts/primitives/chart-card-context.d.ts +33 -0
- package/dist/charts/primitives/chart-card-context.d.ts.map +1 -0
- package/dist/charts/primitives/chart-card-context.js +18 -0
- package/dist/charts/primitives/chart-frame-layout.d.ts +93 -113
- package/dist/charts/primitives/chart-frame-layout.d.ts.map +1 -1
- package/dist/charts/primitives/chart-frame-layout.js +75 -34
- package/dist/charts/primitives/chart-layout.d.ts +112 -0
- package/dist/charts/primitives/chart-layout.d.ts.map +1 -0
- package/dist/charts/primitives/chart-layout.js +175 -0
- package/dist/charts/primitives/chart-tier.d.ts +13 -11
- package/dist/charts/primitives/chart-tier.d.ts.map +1 -1
- package/dist/charts/primitives/chart-tier.js +42 -13
- package/dist/charts/primitives/legend-disclosure.d.ts +27 -0
- package/dist/charts/primitives/legend-disclosure.d.ts.map +1 -0
- package/dist/charts/primitives/legend-disclosure.js +153 -0
- package/dist/charts/series.d.ts +4 -1
- package/dist/charts/series.d.ts.map +1 -1
- package/dist/charts/utils/ticks.d.ts +27 -8
- package/dist/charts/utils/ticks.d.ts.map +1 -1
- package/dist/charts/utils/ticks.js +35 -11
- package/dist/common/errors.d.ts +11 -1
- package/dist/common/errors.d.ts.map +1 -1
- package/dist/common/errors.js +16 -2
- package/dist/common/hit-floor.d.ts +10 -0
- package/dist/common/hit-floor.d.ts.map +1 -0
- package/dist/common/hit-floor.js +6 -0
- package/dist/{shell → common}/isomorphic-layout-effect.d.ts +4 -3
- package/dist/common/isomorphic-layout-effect.d.ts.map +1 -0
- package/dist/common/use-measured-widths.d.ts +37 -0
- package/dist/common/use-measured-widths.d.ts.map +1 -0
- package/dist/common/use-measured-widths.js +51 -0
- package/dist/common/use-media-query.d.ts +6 -0
- package/dist/common/use-media-query.d.ts.map +1 -0
- package/dist/common/use-media-query.js +40 -0
- package/dist/common/validate.d.ts +9 -0
- package/dist/common/validate.d.ts.map +1 -1
- package/dist/common/validate.js +9 -4
- package/dist/content/article-layout.d.ts +4 -2
- package/dist/content/article-layout.d.ts.map +1 -1
- package/dist/content/article-layout.js +43 -3
- package/dist/content/article-layout.module.css +45 -4
- package/dist/content/article-layout.module.css.d.ts +3 -0
- package/dist/content/code-block.d.ts.map +1 -1
- package/dist/content/code-block.js +40 -2
- package/dist/content/code-block.module.css +32 -2
- package/dist/content/prose.module.css +13 -8
- package/dist/controls/actions.d.ts +29 -6
- package/dist/controls/actions.d.ts.map +1 -1
- package/dist/controls/actions.js +158 -27
- package/dist/controls/actions.module.css +14 -1
- package/dist/controls/actions.module.css.d.ts +1 -0
- package/dist/controls/controls.module.css +13 -31
- package/dist/controls/enum-filter.js +2 -2
- package/dist/controls/filter-context.d.ts +10 -0
- package/dist/controls/filter-context.d.ts.map +1 -1
- package/dist/controls/filter-context.js +2 -0
- package/dist/controls/filter-pill.d.ts.map +1 -1
- package/dist/controls/filter-pill.js +1 -0
- package/dist/controls/index.d.ts +1 -1
- package/dist/controls/index.d.ts.map +1 -1
- package/dist/controls/index.js +0 -2
- package/dist/controls/multi-select-filter.js +2 -2
- package/dist/controls/sync-button.module.css +2 -1
- package/dist/dashboard/section.d.ts.map +1 -1
- package/dist/dashboard/section.js +1 -0
- package/dist/dashboard/section.module.css +1 -26
- package/dist/dashboard/stat-card.d.ts +1 -1
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +4 -1
- package/dist/dashboard/stat-card.module.css +6 -9
- package/dist/dashboard/stat-group.module.css +33 -66
- package/dist/dashboard/widget-grid.d.ts.map +1 -1
- package/dist/dashboard/widget-grid.module.css +11 -38
- package/dist/dashboard/widget-header.d.ts +14 -4
- package/dist/dashboard/widget-header.d.ts.map +1 -1
- package/dist/dashboard/widget-header.js +23 -6
- package/dist/dashboard/widget-header.module.css +145 -133
- package/dist/dashboard/widget-header.module.css.d.ts +2 -0
- package/dist/data/data-table-state.d.ts +83 -0
- package/dist/data/data-table-state.d.ts.map +1 -0
- package/dist/data/data-table-state.js +139 -0
- package/dist/data/data-table.d.ts +56 -2
- package/dist/data/data-table.d.ts.map +1 -1
- package/dist/data/data-table.js +436 -336
- package/dist/data/data-table.module.css +94 -0
- package/dist/data/data-table.module.css.d.ts +11 -0
- package/dist/data/virtual-list.d.ts.map +1 -1
- package/dist/data/virtual-list.js +10 -0
- package/dist/forms/form-layout.d.ts.map +1 -1
- package/dist/forms/form-layout.js +3 -3
- package/dist/forms/form-layout.module.css +20 -5
- package/dist/forms/form-layout.module.css.d.ts +1 -0
- package/dist/guard/index.d.ts +8 -2
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +68 -2
- package/dist/guard/types.d.ts +3 -3
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/notifications/center.d.ts.map +1 -1
- package/dist/provider/index.d.ts +20 -1
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +30 -8
- package/dist/shell/app-brand.d.ts.map +1 -1
- package/dist/shell/app-brand.js +1 -0
- package/dist/shell/app-brand.module.css +2 -2
- package/dist/shell/app-header.module.css +18 -8
- package/dist/shell/app-main.module.css +54 -3
- package/dist/shell/app-main.module.css.d.ts +1 -0
- package/dist/shell/app-mobile-nav.module.css +16 -4
- package/dist/shell/app-sidebar.module.css +4 -6
- package/dist/shell/aside-docking.d.ts +31 -0
- package/dist/shell/aside-docking.d.ts.map +1 -0
- package/dist/shell/aside-docking.js +19 -0
- package/dist/shell/index.d.ts +1 -0
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +56 -16
- package/dist/shell/mobile-nav-model.d.ts +1 -1
- package/dist/shell/page-aside.d.ts +22 -3
- package/dist/shell/page-aside.d.ts.map +1 -1
- package/dist/shell/page-aside.js +27 -13
- package/dist/shell/page-bar.d.ts.map +1 -1
- package/dist/shell/page-bar.js +10 -5
- package/dist/shell/page-bar.module.css +10 -34
- package/dist/shell/use-breakpoint.d.ts +3 -4
- package/dist/shell/use-breakpoint.d.ts.map +1 -1
- package/dist/shell/use-breakpoint.js +3 -25
- package/dist/shell/use-size-class.d.ts +6 -0
- package/dist/shell/use-size-class.d.ts.map +1 -0
- package/dist/shell/use-size-class.js +19 -0
- package/dist/state/persisted-or-local.d.ts +4 -1
- package/dist/state/persisted-or-local.d.ts.map +1 -1
- package/dist/state/persisted-or-local.js +4 -3
- package/dist/state/persisted.d.ts +10 -0
- package/dist/state/persisted.d.ts.map +1 -1
- package/dist/state/persisted.js +44 -14
- package/dist/styles.css +41 -0
- package/dist/surfaces.d.ts +4 -4
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +11 -3
- package/dist/theme/controls.module.css +7 -0
- package/dist/theme/ctl-theme.d.ts +1 -0
- package/dist/theme/ctl-theme.d.ts.map +1 -1
- package/dist/theme/ctl-theme.js +12 -4
- package/dist/theme/floating.module.css +37 -0
- package/dist/theme/floating.module.css.d.ts +3 -0
- package/dist/theme/index.d.ts +2 -2
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +34 -5
- package/dist/theme/nav-link.module.css +7 -0
- package/dist/theme-lab/derive-controls.d.ts.map +1 -1
- package/dist/tokens/index.d.ts +7 -16
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +23 -28
- package/dist/tokens/palette.d.ts +5 -27
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +8 -30
- package/dist/tokens/size-classes.d.ts +40 -0
- package/dist/tokens/size-classes.d.ts.map +1 -0
- package/dist/tokens/size-classes.js +29 -0
- package/dist/tokens.css +8 -4
- package/llms.txt +3 -3
- package/package.json +18 -17
- package/src/agent-chat/composer.tsx +74 -47
- package/src/agent-chat/thread-workspace.tsx +8 -11
- package/src/agent-chat/use-keyboard-inset.ts +68 -0
- package/src/charts/cursor/touch-pin.ts +138 -0
- package/src/charts/hooks/use-discrete-cursor.ts +335 -0
- package/src/charts/hooks/use-hysteresis.ts +26 -0
- package/src/charts/hooks/useBandPlot.ts +90 -27
- package/src/charts/hooks/useChartCursor.ts +211 -26
- package/src/charts/index.ts +0 -8
- package/src/charts/kinds/BandStrip.tsx +25 -61
- package/src/charts/kinds/Donut.tsx +153 -62
- package/src/charts/kinds/DualPanel.tsx +21 -26
- package/src/charts/kinds/Heatmap.tsx +61 -22
- package/src/charts/kinds/MirroredBars.tsx +23 -65
- package/src/charts/kinds/band-chrome.tsx +91 -0
- package/src/charts/kinds/donut-layout.ts +82 -0
- package/src/charts/layout/auto-margin.ts +24 -5
- package/src/charts/layout/end-labels.ts +135 -0
- package/src/charts/primitives/Axes.tsx +112 -21
- package/src/charts/primitives/CartesianChart.tsx +282 -111
- package/src/charts/primitives/ChartCard.tsx +119 -48
- package/src/charts/primitives/ChartFrame.tsx +204 -60
- package/src/charts/primitives/ChartLegend.tsx +115 -78
- package/src/charts/primitives/ChartTooltip.tsx +24 -3
- package/src/charts/primitives/Crosshair.tsx +2 -2
- package/src/charts/primitives/HoverOverlay.module.css +13 -0
- package/src/charts/primitives/HoverOverlay.module.css.d.ts +8 -0
- package/src/charts/primitives/HoverOverlay.tsx +62 -20
- package/src/charts/primitives/chart-card-context.ts +46 -0
- package/src/charts/primitives/chart-frame-layout.ts +207 -182
- package/src/charts/primitives/chart-layout.ts +318 -0
- package/src/charts/primitives/chart-tier.tsx +66 -21
- package/src/charts/primitives/legend-disclosure.tsx +177 -0
- package/src/charts/series.ts +4 -1
- package/src/charts/utils/ticks.ts +95 -19
- package/src/common/errors.ts +25 -1
- package/src/common/hit-floor.ts +9 -0
- package/src/{shell → common}/isomorphic-layout-effect.ts +4 -3
- package/src/common/use-measured-widths.ts +88 -0
- package/src/common/use-media-query.ts +56 -0
- package/src/common/validate.ts +17 -4
- package/src/content/article-layout.module.css +45 -4
- package/src/content/article-layout.module.css.d.ts +3 -0
- package/src/content/article-layout.tsx +60 -5
- package/src/content/code-block.module.css +32 -2
- package/src/content/code-block.tsx +47 -3
- package/src/content/prose.module.css +13 -8
- package/src/controls/actions.module.css +14 -1
- package/src/controls/actions.module.css.d.ts +1 -0
- package/src/controls/actions.tsx +223 -75
- package/src/controls/controls.module.css +13 -31
- package/src/controls/enum-filter.tsx +2 -2
- package/src/controls/filter-context.tsx +11 -0
- package/src/controls/filter-pill.tsx +1 -0
- package/src/controls/index.ts +0 -1
- package/src/controls/multi-select-filter.tsx +2 -2
- package/src/controls/sync-button.module.css +2 -1
- package/src/dashboard/section.module.css +1 -26
- package/src/dashboard/section.tsx +1 -0
- package/src/dashboard/stat-card.module.css +6 -9
- package/src/dashboard/stat-card.tsx +6 -3
- package/src/dashboard/stat-group.module.css +33 -66
- package/src/dashboard/widget-grid.module.css +11 -38
- package/src/dashboard/widget-grid.tsx +7 -6
- package/src/dashboard/widget-header.module.css +145 -133
- package/src/dashboard/widget-header.module.css.d.ts +2 -0
- package/src/dashboard/widget-header.tsx +45 -11
- package/src/data/data-table-state.ts +218 -0
- package/src/data/data-table.module.css +94 -0
- package/src/data/data-table.module.css.d.ts +11 -0
- package/src/data/data-table.tsx +547 -299
- package/src/data/virtual-list.tsx +31 -0
- package/src/forms/form-layout.module.css +20 -5
- package/src/forms/form-layout.module.css.d.ts +1 -0
- package/src/forms/form-layout.tsx +12 -10
- package/src/guard/index.ts +188 -3
- package/src/guard/types.ts +3 -2
- package/src/index.ts +2 -0
- package/src/notifications/center.tsx +3 -0
- package/src/provider/index.tsx +60 -7
- package/src/shell/app-brand.module.css +2 -2
- package/src/shell/app-brand.tsx +1 -0
- package/src/shell/app-header.module.css +18 -8
- package/src/shell/app-main.module.css +54 -3
- package/src/shell/app-main.module.css.d.ts +1 -0
- package/src/shell/app-mobile-nav.module.css +16 -4
- package/src/shell/app-sidebar.module.css +4 -6
- package/src/shell/aside-docking.ts +54 -0
- package/src/shell/index.tsx +178 -129
- package/src/shell/mobile-nav-model.ts +1 -1
- package/src/shell/page-aside.tsx +70 -21
- package/src/shell/page-bar.module.css +10 -34
- package/src/shell/page-bar.tsx +10 -5
- package/src/shell/use-breakpoint.ts +9 -45
- package/src/shell/use-size-class.ts +31 -0
- package/src/state/persisted-or-local.ts +10 -4
- package/src/state/persisted.ts +76 -17
- package/src/styles.css +41 -0
- package/src/surfaces.ts +9 -1
- package/src/theme/controls.module.css +7 -0
- package/src/theme/ctl-theme.tsx +15 -3
- package/src/theme/floating.module.css +37 -0
- package/src/theme/floating.module.css.d.ts +3 -0
- package/src/theme/index.ts +46 -8
- package/src/theme/nav-link.module.css +7 -0
- package/src/theme-lab/derive-controls.tsx +2 -0
- package/src/tokens/index.ts +20 -30
- package/src/tokens/palette.ts +6 -28
- package/src/tokens/size-classes.ts +46 -0
- package/dist/shell/isomorphic-layout-effect.d.ts.map +0 -1
- /package/dist/{shell → common}/isomorphic-layout-effect.js +0 -0
|
@@ -9,8 +9,13 @@
|
|
|
9
9
|
* by name with rem fallbacks at their level-0 px value (36/28/6), same idiom as ChartCard's
|
|
10
10
|
* `--mantine-spacing-*` fallbacks.
|
|
11
11
|
*
|
|
12
|
-
* Layout
|
|
13
|
-
*
|
|
12
|
+
* Layout keys on the enclosing card's OWN width (`container: basalt-card`, declared by ChartCard /
|
|
13
|
+
* StatCard / Section — docs/DESIGN-CORE.md § Layout, elevation, shapes), never the viewport: below 480px the subtitle folds
|
|
14
|
+
* into the info glyph; from 480px (wave 8) `value` + `delta` move onto the title row. Wrapping and
|
|
15
|
+
* shrinking are unconditional. The subtitle ALSO folds (regardless of width) whenever the card's own
|
|
16
|
+
* height is under 280px — see the `[data-basalt-card-short]` block below (wave 8's header-economy
|
|
17
|
+
* law). No enclosing card, no container: the standard layout. Container conditions are px literals
|
|
18
|
+
* of CONTAINER_CLASSES.
|
|
14
19
|
*/
|
|
15
20
|
|
|
16
21
|
.root {
|
|
@@ -18,73 +23,15 @@
|
|
|
18
23
|
flex-direction: column;
|
|
19
24
|
}
|
|
20
25
|
|
|
26
|
+
/* The row WRAPS only when it does not fit (a lone kebab stays beside the title; a wide `actions` slot
|
|
27
|
+
* takes its own right-aligned line instead of squeezing the heading) — unconditional, so composers
|
|
28
|
+
* with no `basalt-card` container (`SettingsSection`, `BasaltDataTable`) keep the safety net. */
|
|
21
29
|
.titleRow {
|
|
22
30
|
display: flex;
|
|
31
|
+
flex-wrap: wrap;
|
|
23
32
|
align-items: center;
|
|
24
33
|
gap: var(--vx-space-control-gap, 0.375rem);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
/*
|
|
28
|
-
* Below `sm` the title row WRAPS, which is the safety net for an `actions` slot too wide to sit
|
|
29
|
-
* beside the title. `flex-wrap` is the right tool precisely because it is conditional: a 30px kebab
|
|
30
|
-
* fits and stays on the row, while a control whose hypothetical size does not fit takes its own line
|
|
31
|
-
* instead of squeezing the heading to an ellipsis. No breakpoint arithmetic, no max-width fraction,
|
|
32
|
-
* and no prop — the row measures the actual content.
|
|
33
|
-
*
|
|
34
|
-
* `row-gap` is the 8px rhythm step, so a wrapped slot reads as the line under the title rather than
|
|
35
|
-
* as a second header. `.actions` keeps its `margin-inline-start: auto`, which on a wrapped line makes
|
|
36
|
-
* it right-aligned — the same edge it holds when it is on the row.
|
|
37
|
-
*/
|
|
38
|
-
@media (max-width: 47.99375em) {
|
|
39
|
-
.titleRow {
|
|
40
|
-
flex-wrap: wrap;
|
|
41
|
-
row-gap: var(--vx-space-stack-sm);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/*
|
|
45
|
-
* WRAP ONLY WHEN IT DOES NOT FIT (M4 follow-up) — `flex: 1 1 100%` forced EVERY actions slot onto
|
|
46
|
-
* its own row, even a single 24px icon that fit beside the title fine (a KPI card's lone `···`
|
|
47
|
-
* menu dropped under the title and pushed the value down with it). `flex: 0 1 auto` restores
|
|
48
|
-
* ordinary flex sizing: no grow, so a short slot stays put beside the title on the shared row;
|
|
49
|
-
* `min-width: 0` plus `flex-shrink: 1` (part of the same shorthand, overriding the base rule's
|
|
50
|
-
* `flex-shrink: 0`) let it shrink and, once `.titleRow`'s own `flex-wrap: wrap` decides it truly
|
|
51
|
-
* does not fit, drop to its own line. `max-width: 100%` is the cap once it IS alone on that line —
|
|
52
|
-
* without it a `.actions` still carrying unshrinkable content (the `visibleFrom="sm"` control plus
|
|
53
|
-
* a nowrap switch `Group`, see below) can overflow the card rather than shrink into it. The base
|
|
54
|
-
* rule's `margin-inline-start: auto` still applies in both states — right-aligned beside the title
|
|
55
|
-
* when it fits, right-aligned as the lone item on its own wrapped line when it does not.
|
|
56
|
-
*
|
|
57
|
-
* `.actions` keeps its OWN `flex-wrap: wrap` from the prior fix: `Section` can hand it a `tabs`
|
|
58
|
-
* control (`CtlSlot`-wrapped) AND an `actions` control (another `CtlSlot`) in the same slot, and
|
|
59
|
-
* without this a nowrap `Group` of switches beside a view-switch control gets squeezed into one
|
|
60
|
-
* shared line and overflows below its own min-content width — the exact regression this rule
|
|
61
|
-
* once fixed. It is orthogonal to the row-level "wrap only when it does not fit" law above: with
|
|
62
|
-
* one child, wrapping has nothing to do, so a lone `···` menu still stays on the title line.
|
|
63
|
-
*
|
|
64
|
-
* The real flex items get `flex: 1 1 auto; min-width: 0` so they can shrink or (via the `> * > *`
|
|
65
|
-
* twin) stack within whatever width `.actions` itself resolves to — a full-width phone `Select`
|
|
66
|
-
* still reads as its own full-width line because the CONTROL sets that itself (`ViewTabs`'
|
|
67
|
-
* `fullWidth`/`.phoneSelect`), not because `.actions` forces it.
|
|
68
|
-
*
|
|
69
|
-
* The `> * > *` twin exists because `CtlSlot` (`theme/ctl-theme.tsx`) wraps a home's actions in a
|
|
70
|
-
* `display: contents` box — invisible to the flex box tree, so its OWN children (not it) are the
|
|
71
|
-
* real flex items. `display: contents` elements still match `.actions > *` by DOM structure but
|
|
72
|
-
* carry no box to apply `flex`/`min-width` to, hence styling one level deeper as well; a call site
|
|
73
|
-
* that skips `CtlSlot` and hands `actions` a real element directly gets the same rule from the
|
|
74
|
-
* first selector.
|
|
75
|
-
*/
|
|
76
|
-
.actions {
|
|
77
|
-
flex: 0 1 auto;
|
|
78
|
-
flex-wrap: wrap;
|
|
79
|
-
min-width: 0;
|
|
80
|
-
max-width: 100%;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
.actions > *,
|
|
84
|
-
.actions > * > * {
|
|
85
|
-
flex: 1 1 auto;
|
|
86
|
-
min-width: 0;
|
|
87
|
-
}
|
|
34
|
+
row-gap: var(--vx-space-stack-sm);
|
|
88
35
|
}
|
|
89
36
|
|
|
90
37
|
.root[data-tier='section'] .titleRow {
|
|
@@ -203,59 +150,28 @@
|
|
|
203
150
|
}
|
|
204
151
|
|
|
205
152
|
/*
|
|
206
|
-
* The hero-metric row
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*/
|
|
211
|
-
/*
|
|
212
|
-
* The hero-metric row: the value, then its `DeltaBadge`.
|
|
213
|
-
*
|
|
214
|
-
* The gap is the 8px vertical-rhythm step (`--vx-space-stack-sm`), not the 4px it shipped as. 4px is
|
|
215
|
-
* the rhythm's SMALLEST step — the distance between a label and the thing it labels — and at that
|
|
216
|
-
* distance the badge read as a superscript hanging off the numeral rather than as a second fact
|
|
217
|
-
* beside it. It is a token and not a literal so it tracks the density knob with every other spacing
|
|
218
|
-
* in the header (`deriveSpacing`, `tokens/palette.ts`); `theme/layout-rhythm-css.test.ts`'s sibling
|
|
219
|
-
* assertion in `widget-header.test.tsx` pins it against exactly the literal it used to be.
|
|
220
|
-
*
|
|
221
|
-
* `align-items: center`, not `baseline`. The badge is a BOX with its own padding and a mono 11.5px
|
|
222
|
-
* label, so aligning its text baseline to a 24px KPI numeral's baseline put the box itself low —
|
|
223
|
-
* its top edge sat below the numeral's x-height and its bottom edge hung under the numeral's
|
|
224
|
-
* baseline. A chip beside a numeral is centred on the numeral, the same law the `count` tag in the
|
|
225
|
-
* title row above already follows.
|
|
153
|
+
* The hero-metric row: the value, then its `DeltaBadge`, 8px apart (`--vx-space-stack-sm`, tracks
|
|
154
|
+
* density). It always WRAPS — the badge drops under the value when they do not fit, the value never
|
|
155
|
+
* ellipsizes. `center`, not `baseline`: the badge is a padded box, so a chip beside a numeral is
|
|
156
|
+
* centred on it (same law as the `count` tag above).
|
|
226
157
|
*/
|
|
227
158
|
.metrics {
|
|
228
159
|
display: flex;
|
|
229
|
-
flex-wrap:
|
|
160
|
+
flex-wrap: wrap;
|
|
230
161
|
align-items: center;
|
|
231
162
|
gap: var(--vx-space-stack-sm);
|
|
163
|
+
/* Tight when the badge wraps under the value. */
|
|
164
|
+
row-gap: 2px;
|
|
232
165
|
margin-top: 2px;
|
|
233
166
|
min-width: 0;
|
|
234
167
|
}
|
|
235
168
|
|
|
236
|
-
/*
|
|
237
|
-
*
|
|
238
|
-
* `
|
|
239
|
-
* a numeral can — but a KPI's numeral IS the content, and in a 2-up card grid at 390px
|
|
240
|
-
* `$1294.9k ▲18.9%` rendered as `$129… ▲18.9%`. Truncating the number to protect the delta about the
|
|
241
|
-
* number is the wrong way round. Conditional like the title row above it: a short value keeps the
|
|
242
|
-
* badge beside it, and only a value that would otherwise clip gives up the line.
|
|
243
|
-
*/
|
|
244
|
-
@media (max-width: 47.99375em) {
|
|
245
|
-
.metrics {
|
|
246
|
-
flex-wrap: wrap;
|
|
247
|
-
row-gap: 2px;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/* The value is the row's shrinkable half and the delta badge is not: a pre-formatted numeral can
|
|
252
|
-
* ellipsize and still read, a `+18.9%` cannot. Without the clamp the badge grew past the header box
|
|
253
|
-
* and painted over `StatCard`'s 72px sparkline slot beside it. */
|
|
169
|
+
/* NEVER truncated: the numeral IS the content. It shrinks with the card's own width instead —
|
|
170
|
+
* `cqi` against `basalt-card` (the clamp, at the end of this file), from the `kpi` step down to the
|
|
171
|
+
* `lg` step (16px), and `overflow-wrap` breaks a pathological string rather than clipping it. */
|
|
254
172
|
.value {
|
|
255
173
|
min-width: 0;
|
|
256
|
-
overflow:
|
|
257
|
-
text-overflow: ellipsis;
|
|
258
|
-
white-space: nowrap;
|
|
174
|
+
overflow-wrap: anywhere;
|
|
259
175
|
font-family: var(--basalt-font-mono);
|
|
260
176
|
font-size: var(--vx-text-kpi);
|
|
261
177
|
font-weight: 600;
|
|
@@ -266,8 +182,8 @@
|
|
|
266
182
|
/*
|
|
267
183
|
* The unit beside the hero value. Mono like the number it qualifies (so `412 TSS` sits on one
|
|
268
184
|
* baseline grid), `--vx-text-sm` and `--vx-muted` so it never competes with the numeral — the whole
|
|
269
|
-
* point is that a unit is the SMALL half of the pair. `flex-shrink: 0`:
|
|
270
|
-
*
|
|
185
|
+
* point is that a unit is the SMALL half of the pair. `flex-shrink: 0`: a unit clipped to `TS…`
|
|
186
|
+
* would state nothing.
|
|
271
187
|
*/
|
|
272
188
|
.unit {
|
|
273
189
|
flex-shrink: 0;
|
|
@@ -278,14 +194,28 @@
|
|
|
278
194
|
color: var(--vx-muted);
|
|
279
195
|
}
|
|
280
196
|
|
|
281
|
-
/* The C1/C5 home for header actions — always pinned right, regardless of what else is present.
|
|
197
|
+
/* The C1/C5 home for header actions — always pinned right, regardless of what else is present.
|
|
198
|
+
* Law C15: adopts the shared hit overlay (`data-basalt-hit-scope`, `styles.css`); `--vx-hit-gap`
|
|
199
|
+
* caps the expansion at the control gap so neighbouring 24px icons never overlap. */
|
|
282
200
|
.actions {
|
|
201
|
+
--vx-hit-gap: var(--vx-space-control-gap);
|
|
283
202
|
display: inline-flex;
|
|
284
|
-
|
|
203
|
+
/* No grow, but it may shrink and wrap once `.titleRow` decides it does not fit, capped at 100% once
|
|
204
|
+
* alone on its line. Its flex items shrink too; the `> * > *` twin reaches through `CtlSlot`'s
|
|
205
|
+
* `display: contents` wrapper, whose own children are the real items. */
|
|
206
|
+
flex: 0 1 auto;
|
|
207
|
+
flex-wrap: wrap;
|
|
285
208
|
align-items: center;
|
|
286
209
|
gap: var(--vx-space-control-gap, 0.375rem);
|
|
287
210
|
margin-inline-start: auto;
|
|
288
211
|
min-width: 0;
|
|
212
|
+
max-width: 100%;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.actions > *,
|
|
216
|
+
.actions > * > * {
|
|
217
|
+
flex: 1 1 auto;
|
|
218
|
+
min-width: 0;
|
|
289
219
|
}
|
|
290
220
|
|
|
291
221
|
/* Directly under the title row, 4px off it — 1px put it inside the title's own line box, so a
|
|
@@ -306,6 +236,7 @@
|
|
|
306
236
|
/* ── info glyph ─────────────────────────────────────────────────────────────────────────────── */
|
|
307
237
|
|
|
308
238
|
.info {
|
|
239
|
+
--vx-hit-gap: var(--vx-space-control-gap);
|
|
309
240
|
position: relative;
|
|
310
241
|
display: inline-flex;
|
|
311
242
|
flex-shrink: 0;
|
|
@@ -330,6 +261,12 @@
|
|
|
330
261
|
color: var(--vx-muted);
|
|
331
262
|
}
|
|
332
263
|
|
|
264
|
+
/* Folded-subtitle pieces: hidden until a `basalt-card` narrower than 480px shows them (below). */
|
|
265
|
+
.foldGlyph,
|
|
266
|
+
.foldNote {
|
|
267
|
+
display: none;
|
|
268
|
+
}
|
|
269
|
+
|
|
333
270
|
/*
|
|
334
271
|
* The tooltip bubble reads like every other floating surface — panel bg + `shadow-card` + card
|
|
335
272
|
* radius (docs/DESIGN-SPEC.md §5). It opens DOWNWARD by convention, into whatever sits below the
|
|
@@ -383,33 +320,108 @@
|
|
|
383
320
|
opacity: 0.6;
|
|
384
321
|
}
|
|
385
322
|
|
|
323
|
+
/* Card narrower than 480px: the subtitle folds into the info glyph (`InfoGlyph`'s `note`, or
|
|
324
|
+
* `.foldGlyph` when there is no `info`) — exactly one of the two shows. */
|
|
325
|
+
@container basalt-card (max-width: 479.9px) {
|
|
326
|
+
.subtitle {
|
|
327
|
+
display: none;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.foldGlyph {
|
|
331
|
+
display: inline-flex;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.foldNote {
|
|
335
|
+
display: block;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
386
339
|
/*
|
|
387
|
-
*
|
|
388
|
-
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
* definition extends past the box — the clipping only exists for their loading overlay, painted
|
|
393
|
-
* under `[data-loading]`, which is excluded here.
|
|
340
|
+
* The same fold, triggered by the CARD'S OWN HEIGHT instead of its width (wave 8's header-economy
|
|
341
|
+
* law) — a short-but-wide chart card (a KPI-style plot with little vertical room) still needs the
|
|
342
|
+
* subtitle line back. Not a `@container basalt-card` SIZE query: `container-type: size` on an
|
|
343
|
+
* auto-height card collapses it to 0 (see `ChartCard`'s `CARD_SHORT_HEIGHT` doc), so `ChartCard`
|
|
344
|
+
* measures itself with a ResizeObserver instead and sets `data-basalt-card-short` under 280px.
|
|
394
345
|
*/
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
346
|
+
[data-basalt-card-short] .subtitle {
|
|
347
|
+
display: none;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
[data-basalt-card-short] .foldGlyph {
|
|
351
|
+
display: inline-flex;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
[data-basalt-card-short] .foldNote {
|
|
355
|
+
display: block;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/* A short card's KPI numeral is the header's own tallest line (the merged title row has no other
|
|
359
|
+
* source of height once the subtitle folds) — the same floor the width `cqi` clamp already uses,
|
|
360
|
+
* so a short card never introduces a THIRD value size. */
|
|
361
|
+
[data-basalt-card-short] .value {
|
|
362
|
+
font-size: var(--vx-text-lg);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/* The KPI numeral shrinks with its card. `(width >= 0px)` is always true but only inside a
|
|
366
|
+
* `basalt-card` container, so `cqi` never resolves against an unrelated one (or the viewport) —
|
|
367
|
+
* with no card the numeral stays at the plain `kpi` step above. 8cqi reaches the 24px `kpi` step at
|
|
368
|
+
* a 300px card. */
|
|
369
|
+
@container basalt-card (width >= 0px) {
|
|
370
|
+
.value {
|
|
371
|
+
font-size: clamp(var(--vx-text-lg), 8cqi, var(--vx-text-kpi));
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/*
|
|
376
|
+
* From 480px (`CONTAINER_CLASSES.regular`, wave 8's header-economy law) `value` + `delta` ride the
|
|
377
|
+
* title row: the title row dissolves (`display: contents`) so heading, info, count, metrics and
|
|
378
|
+
* actions share the root's one flex line, and `order` puts the metrics before the actions. The
|
|
379
|
+
* metrics take the free space (`margin-inline-start: auto`), which pushes metrics AND actions
|
|
380
|
+
* right; with no metrics, `.actions` keeps its own auto margin. The heading keeps the tier's row
|
|
381
|
+
* height (`min-height` was on `.titleRow`), so a title-less header does not collapse.
|
|
382
|
+
*
|
|
383
|
+
* This threshold moved from `wide` (800px) in wave 5/7 — most cards sit in the 480-799px `regular`
|
|
384
|
+
* band, which never reached the merged row before, keeping the taller stacked layout on the
|
|
385
|
+
* majority of dashboard cards (wave 8, `docs/waves/PLAN.md`).
|
|
386
|
+
*/
|
|
387
|
+
@container basalt-card (min-width: 480px) {
|
|
388
|
+
.root {
|
|
389
|
+
flex-direction: row;
|
|
390
|
+
flex-wrap: wrap;
|
|
391
|
+
align-items: center;
|
|
392
|
+
column-gap: var(--vx-space-control-gap, 0.375rem);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.titleRow {
|
|
396
|
+
display: contents;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.root[data-tier='section'] .heading {
|
|
400
|
+
min-height: var(--vx-space-section-header-height, 2.25rem);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.root[data-tier='widget'] .heading,
|
|
404
|
+
.root[data-tier='group'] .heading {
|
|
405
|
+
min-height: var(--vx-space-widget-header-height, 1.75rem);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.metrics {
|
|
409
|
+
order: 1;
|
|
410
|
+
margin-block: 0;
|
|
411
|
+
margin-inline-start: auto;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.actions {
|
|
415
|
+
order: 2;
|
|
398
416
|
}
|
|
399
417
|
|
|
400
|
-
.
|
|
401
|
-
|
|
418
|
+
.root:has(.metrics) .actions {
|
|
419
|
+
margin-inline-start: 0;
|
|
402
420
|
}
|
|
403
421
|
|
|
404
|
-
.
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
left: 50%;
|
|
409
|
-
translate: -50% -50%;
|
|
410
|
-
width: 100%;
|
|
411
|
-
height: 100%;
|
|
412
|
-
min-width: var(--vx-space-touch-control-height, 2.25rem);
|
|
413
|
-
min-height: var(--vx-space-touch-control-height, 2.25rem);
|
|
422
|
+
.subtitle,
|
|
423
|
+
.sparkline {
|
|
424
|
+
order: 3;
|
|
425
|
+
flex: 0 0 100%;
|
|
414
426
|
}
|
|
415
427
|
}
|
|
@@ -17,6 +17,8 @@ declare const classes: {
|
|
|
17
17
|
readonly sparkline: string
|
|
18
18
|
readonly info: string
|
|
19
19
|
readonly infoTrigger: string
|
|
20
|
+
readonly foldGlyph: string
|
|
21
|
+
readonly foldNote: string
|
|
20
22
|
readonly infoBubble: string
|
|
21
23
|
readonly deltaBadge: string
|
|
22
24
|
readonly deltaGlyph: string
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `BasaltDataTable`'s state hooks — the table's own interactive state, the fold row disclosure, the
|
|
3
|
+
* card projection and the row/card activation props. Internal to `./data/table`; nothing here is on a
|
|
4
|
+
* published subpath.
|
|
5
|
+
*/
|
|
6
|
+
import type { ColumnFiltersState, ColumnPinningState, PaginationState, RowSelectionState, SortingState, Updater } from '@tanstack/react-table';
|
|
7
|
+
import type { KeyboardEvent as ReactKeyboardEvent, RefObject } from 'react';
|
|
8
|
+
export declare const EMPTY_FOLD_SET: ReadonlySet<string>;
|
|
9
|
+
/**
|
|
10
|
+
* Which rows have their fold disclosure open. `row.id` defaults to the row's index in `data` (no
|
|
11
|
+
* `getRowId`) — stable across a client-side sort/filter/pagination, which reorder or subset the SAME
|
|
12
|
+
* core rows. PRUNED rather than cleared on every `data` change: a non-memoized `data` prop (an inline
|
|
13
|
+
* `.map()`, an unstable query result) hands a brand-new array of the SAME rows on every parent
|
|
14
|
+
* re-render, and clearing on that reference alone collapsed every open disclosure the instant
|
|
15
|
+
* anything upstream re-rendered.
|
|
16
|
+
*
|
|
17
|
+
* Pruning by ID ALONE is only safe with `getRowId` — a caller-declared identity that genuinely
|
|
18
|
+
* survives a page turn or a refetch, so an id no longer present (a manual-pagination page turn, a
|
|
19
|
+
* delete) still has to go, or a differently-shaped record seated at a reused id would silently read
|
|
20
|
+
* as pre-expanded. Without `getRowId` the id IS the array index, and an index survives a refetch
|
|
21
|
+
* trivially — it is still "0", "1", … even when every object behind it is a different record.
|
|
22
|
+
* Keeping an expanded index across THAT would silently attach the old disclosure to a stranger's
|
|
23
|
+
* row. So the index-id lane keeps a `prevData` snapshot and asks the narrower, correct question: is
|
|
24
|
+
* the object AT this index still the same reference as last time? A non-memoized array of the SAME
|
|
25
|
+
* rows answers yes at every index (the case above); a page turn or a refetch answers no.
|
|
26
|
+
*/
|
|
27
|
+
export declare function useRowDisclosure<T>({ data, getRowId, }: {
|
|
28
|
+
data: readonly T[];
|
|
29
|
+
getRowId: ((row: T, index: number) => string) | undefined;
|
|
30
|
+
}): {
|
|
31
|
+
expanded: ReadonlySet<string>;
|
|
32
|
+
toggle: (rowId: string) => void;
|
|
33
|
+
};
|
|
34
|
+
/** The `BasaltDataTableProps` fields `useDataTableState` reads — declared here rather than picked
|
|
35
|
+
* off the props type, so the hook owns its contract and this module never imports the component. */
|
|
36
|
+
export type DataTableStateProps = {
|
|
37
|
+
initialSorting?: SortingState | undefined;
|
|
38
|
+
onSortingChange?: ((sorting: SortingState) => void) | undefined;
|
|
39
|
+
initialGlobalFilter?: string | undefined;
|
|
40
|
+
onGlobalFilterChange?: ((value: string) => void) | undefined;
|
|
41
|
+
onColumnFiltersChange?: ((filters: ColumnFiltersState) => void) | undefined;
|
|
42
|
+
initialPagination?: PaginationState | undefined;
|
|
43
|
+
onPaginationChange?: ((pagination: PaginationState) => void) | undefined;
|
|
44
|
+
initialColumnPinning?: ColumnPinningState | undefined;
|
|
45
|
+
rowSelection?: RowSelectionState | undefined;
|
|
46
|
+
onRowSelectionChange?: ((selection: RowSelectionState) => void) | undefined;
|
|
47
|
+
};
|
|
48
|
+
/** The table's own interactive state and the TanStack `on*Change` handlers that write it — every
|
|
49
|
+
* piece uncontrolled with an optional change callback, except row selection, which is controlled
|
|
50
|
+
* whenever `rowSelection` is passed. */
|
|
51
|
+
export declare function useDataTableState(props: DataTableStateProps, defaultPageSize: number): {
|
|
52
|
+
sorting: SortingState;
|
|
53
|
+
onSortingChange: (updater: Updater<SortingState>) => void;
|
|
54
|
+
globalFilter: string;
|
|
55
|
+
onGlobalFilterChange: (updater: Updater<string>) => void;
|
|
56
|
+
columnFilters: ColumnFiltersState;
|
|
57
|
+
onColumnFiltersChange: (updater: Updater<ColumnFiltersState>) => void;
|
|
58
|
+
pagination: PaginationState;
|
|
59
|
+
onPaginationChange: (updater: Updater<PaginationState>) => void;
|
|
60
|
+
columnPinning: ColumnPinningState;
|
|
61
|
+
onColumnPinningChange: import("react").Dispatch<import("react").SetStateAction<ColumnPinningState>>;
|
|
62
|
+
rowSelection: RowSelectionState;
|
|
63
|
+
onRowSelectionChange: (updater: Updater<RowSelectionState>) => void;
|
|
64
|
+
};
|
|
65
|
+
/** Whether the table renders as `renderCard` projections: its own root measured below the `regular`
|
|
66
|
+
* container class. A zero width is an un-laid-out ancestor, not a narrow table, so it moves nothing
|
|
67
|
+
* — the default (and the SSR answer) is the table. The root's width never depends on which body it
|
|
68
|
+
* holds, so the swap cannot oscillate. */
|
|
69
|
+
export declare function useCardProjection(enabled: boolean): {
|
|
70
|
+
rootRef: RefObject<HTMLDivElement | null>;
|
|
71
|
+
cards: boolean;
|
|
72
|
+
};
|
|
73
|
+
/** Click + Enter activation, shared by a row and its card. Enter only: Space is the browser's own
|
|
74
|
+
* page-scroll on a focused non-button, and stealing it from a keyboard reader moving down a long
|
|
75
|
+
* table costs more than the second activation key buys. */
|
|
76
|
+
export declare function activationProps<T>(onRowActivate: ((row: T) => void) | undefined, row: T): {
|
|
77
|
+
className: string;
|
|
78
|
+
'data-activatable': boolean;
|
|
79
|
+
tabIndex: number;
|
|
80
|
+
onClick: () => void;
|
|
81
|
+
onKeyDown: (event: ReactKeyboardEvent<HTMLElement>) => void;
|
|
82
|
+
} | undefined;
|
|
83
|
+
//# sourceMappingURL=data-table-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-table-state.d.ts","sourceRoot":"","sources":["../../src/data/data-table-state.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EACV,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,OAAO,EACR,MAAM,uBAAuB,CAAA;AAE9B,OAAO,KAAK,EAAE,aAAa,IAAI,kBAAkB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAM3E,eAAO,MAAM,cAAc,EAAE,WAAW,CAAC,MAAM,CAAa,CAAA;AAE5D;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,EAClC,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,SAAS,CAAC,EAAE,CAAA;IAClB,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,SAAS,CAAA;CAC1D,GAAG;IAAE,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,CA2BrE;AAuBD;oGACoG;AACpG,MAAM,MAAM,mBAAmB,GAAG;IAChC,cAAc,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;IACzC,eAAe,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAC/D,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC,oBAAoB,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAC5D,qBAAqB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAC3E,iBAAiB,CAAC,EAAE,eAAe,GAAG,SAAS,CAAA;IAC/C,kBAAkB,CAAC,EAAE,CAAC,CAAC,UAAU,EAAE,eAAe,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACxE,oBAAoB,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAA;IACrD,YAAY,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAA;IAC5C,oBAAoB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;CAC5E,CAAA;AAED;;wCAEwC;AACxC,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM;;yDAjCrD,IAAI;;wDAAJ,IAAI;;qEAAJ,IAAI;;+DAAJ,IAAI;;;;oCA2DtB,OAAO,CAAC,iBAAiB,CAAC;EAgCvC;AAED;;;0CAG0C;AAC1C,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG;IACnD,OAAO,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAA;IACzC,KAAK,EAAE,OAAO,CAAA;CACf,CAcA;AAED;;2DAE2D;AAC3D,wBAAgB,eAAe,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,EAAE,GAAG,EAAE,CAAC;;;;;uBAOjE,kBAAkB,CAAC,WAAW,CAAC;cAUrD"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { functionalUpdate } from "@tanstack/react-table";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { useMeasuredWidths } from "../common/use-measured-widths.js";
|
|
4
|
+
import { CONTAINER_CLASSES } from "../tokens/size-classes.js";
|
|
5
|
+
import classes from "./data-table.module.css";
|
|
6
|
+
const EMPTY_FOLD_SET = /* @__PURE__ */ new Set();
|
|
7
|
+
function useRowDisclosure({
|
|
8
|
+
data,
|
|
9
|
+
getRowId
|
|
10
|
+
}) {
|
|
11
|
+
const [expanded, setExpanded] = useState(EMPTY_FOLD_SET);
|
|
12
|
+
const toggle = useCallback((rowId) => {
|
|
13
|
+
setExpanded((current) => {
|
|
14
|
+
const next = new Set(current);
|
|
15
|
+
if (next.has(rowId)) next.delete(rowId);
|
|
16
|
+
else next.add(rowId);
|
|
17
|
+
return next;
|
|
18
|
+
});
|
|
19
|
+
}, []);
|
|
20
|
+
const prevDataRef = useRef(data);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const prevData = prevDataRef.current;
|
|
23
|
+
prevDataRef.current = data;
|
|
24
|
+
setExpanded((current) => {
|
|
25
|
+
if (current.size === 0) return current;
|
|
26
|
+
const liveIds = getRowId && new Set(data.map((row, index) => getRowId(row, index)));
|
|
27
|
+
const isLive = (id) => {
|
|
28
|
+
if (liveIds) return liveIds.has(id);
|
|
29
|
+
const index = Number(id);
|
|
30
|
+
return data[index] !== void 0 && data[index] === prevData[index];
|
|
31
|
+
};
|
|
32
|
+
const next = new Set([...current].filter(isLive));
|
|
33
|
+
return next.size === current.size ? current : next;
|
|
34
|
+
});
|
|
35
|
+
}, [data, getRowId]);
|
|
36
|
+
return { expanded, toggle };
|
|
37
|
+
}
|
|
38
|
+
function useReportedState(initial, onChange) {
|
|
39
|
+
const [value, setValue] = useState(initial);
|
|
40
|
+
const update = useCallback(
|
|
41
|
+
(updater) => {
|
|
42
|
+
setValue((prev) => {
|
|
43
|
+
const next = functionalUpdate(updater, prev);
|
|
44
|
+
onChange?.(next);
|
|
45
|
+
return next;
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
[onChange]
|
|
49
|
+
);
|
|
50
|
+
return [value, update];
|
|
51
|
+
}
|
|
52
|
+
function useDataTableState(props, defaultPageSize) {
|
|
53
|
+
const { rowSelection, onRowSelectionChange } = props;
|
|
54
|
+
const [sorting, onSortingChange] = useReportedState(
|
|
55
|
+
props.initialSorting ?? [],
|
|
56
|
+
props.onSortingChange
|
|
57
|
+
);
|
|
58
|
+
const [globalFilter, onGlobalFilterChange] = useReportedState(
|
|
59
|
+
props.initialGlobalFilter ?? "",
|
|
60
|
+
props.onGlobalFilterChange
|
|
61
|
+
);
|
|
62
|
+
const [columnFilters, onColumnFiltersChange] = useReportedState(
|
|
63
|
+
[],
|
|
64
|
+
props.onColumnFiltersChange
|
|
65
|
+
);
|
|
66
|
+
const [pagination, onPaginationChange] = useReportedState(
|
|
67
|
+
props.initialPagination ?? { pageIndex: 0, pageSize: defaultPageSize },
|
|
68
|
+
props.onPaginationChange
|
|
69
|
+
);
|
|
70
|
+
const [columnPinning, setColumnPinning] = useState(
|
|
71
|
+
props.initialColumnPinning ?? {}
|
|
72
|
+
);
|
|
73
|
+
const [internalRowSelection, setInternalRowSelection] = useState({});
|
|
74
|
+
const handleRowSelectionChange = useCallback(
|
|
75
|
+
(updater) => {
|
|
76
|
+
if (rowSelection !== void 0) {
|
|
77
|
+
onRowSelectionChange?.(functionalUpdate(updater, rowSelection));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
setInternalRowSelection((prev) => {
|
|
81
|
+
const next = functionalUpdate(updater, prev);
|
|
82
|
+
onRowSelectionChange?.(next);
|
|
83
|
+
return next;
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
[rowSelection, onRowSelectionChange]
|
|
87
|
+
);
|
|
88
|
+
return {
|
|
89
|
+
sorting,
|
|
90
|
+
onSortingChange,
|
|
91
|
+
globalFilter,
|
|
92
|
+
onGlobalFilterChange,
|
|
93
|
+
columnFilters,
|
|
94
|
+
onColumnFiltersChange,
|
|
95
|
+
pagination,
|
|
96
|
+
onPaginationChange,
|
|
97
|
+
columnPinning,
|
|
98
|
+
onColumnPinningChange: setColumnPinning,
|
|
99
|
+
rowSelection: rowSelection ?? internalRowSelection,
|
|
100
|
+
onRowSelectionChange: handleRowSelectionChange
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function useCardProjection(enabled) {
|
|
104
|
+
const rootRef = useRef(null);
|
|
105
|
+
const [narrow, setNarrow] = useState(false);
|
|
106
|
+
useMeasuredWidths({
|
|
107
|
+
resolveRoot: () => rootRef.current,
|
|
108
|
+
signature: "",
|
|
109
|
+
enabled,
|
|
110
|
+
onMeasure: (root) => {
|
|
111
|
+
if (root.clientWidth === 0) return;
|
|
112
|
+
const next = root.clientWidth < CONTAINER_CLASSES.regular;
|
|
113
|
+
setNarrow((current) => current === next ? current : next);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
return { rootRef, cards: enabled && narrow };
|
|
117
|
+
}
|
|
118
|
+
function activationProps(onRowActivate, row) {
|
|
119
|
+
if (onRowActivate === void 0) return void 0;
|
|
120
|
+
return {
|
|
121
|
+
className: classes.activatable,
|
|
122
|
+
"data-activatable": true,
|
|
123
|
+
tabIndex: 0,
|
|
124
|
+
onClick: () => onRowActivate(row),
|
|
125
|
+
onKeyDown: (event) => {
|
|
126
|
+
if (event.key !== "Enter") return;
|
|
127
|
+
if (event.target !== event.currentTarget) return;
|
|
128
|
+
event.preventDefault();
|
|
129
|
+
onRowActivate(row);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
export {
|
|
134
|
+
EMPTY_FOLD_SET,
|
|
135
|
+
activationProps,
|
|
136
|
+
useCardProjection,
|
|
137
|
+
useDataTableState,
|
|
138
|
+
useRowDisclosure
|
|
139
|
+
};
|