@eifi1/ui-kit 0.48.2 → 0.50.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/README.md +34 -16
- package/dist/admin/admin-action-confirm.js +1 -1
- package/dist/admin/admin-action-confirm.js.map +1 -1
- package/dist/admin/invitations-panel.js +5 -1
- package/dist/admin/invitations-panel.js.map +1 -1
- package/dist/admin/user-roster.d.ts +24 -12
- package/dist/admin/user-roster.js +21 -6
- package/dist/admin/user-roster.js.map +1 -1
- package/dist/auth/accept-invitation.js +2 -2
- package/dist/auth/accept-invitation.js.map +1 -1
- package/dist/auth/not-found-page.js +1 -1
- package/dist/auth/not-found-page.js.map +1 -1
- package/dist/auth/reset-password-form.js +2 -2
- package/dist/auth/reset-password-form.js.map +1 -1
- package/dist/auth/verify-email.d.ts +9 -1
- package/dist/auth/verify-email.js +5 -3
- package/dist/auth/verify-email.js.map +1 -1
- package/dist/billing/plan-card.js +2 -2
- package/dist/billing/plan-card.js.map +1 -1
- package/dist/components/amount-input.d.ts +2 -1
- package/dist/components/amount-input.js +3 -2
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/autocomplete.js +1 -0
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/breadcrumbs.d.ts +19 -1
- package/dist/components/breadcrumbs.js +11 -4
- package/dist/components/breadcrumbs.js.map +1 -1
- package/dist/components/button-look.d.ts +37 -7
- package/dist/components/button-look.js +33 -6
- package/dist/components/button-look.js.map +1 -1
- package/dist/components/button.d.ts +4 -1
- package/dist/components/button.js +2 -2
- package/dist/components/button.js.map +1 -1
- package/dist/components/calendar-heatmap.js +2 -1
- package/dist/components/calendar-heatmap.js.map +1 -1
- package/dist/components/chart-edge.d.ts +10 -0
- package/dist/components/chart-edge.js +57 -0
- package/dist/components/chart-edge.js.map +1 -0
- package/dist/components/chart.js +2 -1
- package/dist/components/chart.js.map +1 -1
- package/dist/components/chip.d.ts +8 -1
- package/dist/components/chip.js +4 -1
- package/dist/components/chip.js.map +1 -1
- package/dist/components/combobox-core.js +1 -0
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox-suggestions.js +1 -0
- package/dist/components/combobox-suggestions.js.map +1 -1
- package/dist/components/confirm-context.d.ts +5 -0
- package/dist/components/confirm-context.js +7 -0
- package/dist/components/confirm-context.js.map +1 -0
- package/dist/components/confirm-dialog.js +3 -3
- package/dist/components/confirm-dialog.js.map +1 -1
- package/dist/components/currency-select.d.ts +28 -2
- package/dist/components/currency-select.js +56 -10
- package/dist/components/currency-select.js.map +1 -1
- package/dist/components/danger-confirm.d.ts +2 -2
- package/dist/components/danger-confirm.js +1 -1
- package/dist/components/danger-confirm.js.map +1 -1
- package/dist/components/data-table-fit.js +21 -3
- package/dist/components/data-table-fit.js.map +1 -1
- package/dist/components/data-table-mobile.d.ts +58 -3
- package/dist/components/data-table-mobile.js +147 -16
- package/dist/components/data-table-mobile.js.map +1 -1
- package/dist/components/data-table-row-actions.d.ts +2 -1
- package/dist/components/data-table-row-actions.js +7 -0
- package/dist/components/data-table-row-actions.js.map +1 -1
- package/dist/components/data-table-row.d.ts +1 -1
- package/dist/components/data-table-row.js.map +1 -1
- package/dist/components/data-table.d.ts +34 -1
- package/dist/components/data-table.js +37 -10
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/dropdown.js +1 -0
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/error-boundary.js +2 -2
- package/dist/components/error-boundary.js.map +1 -1
- package/dist/components/floating-panel.d.ts +36 -13
- package/dist/components/floating-panel.js +27 -7
- package/dist/components/floating-panel.js.map +1 -1
- package/dist/components/form-actions.d.ts +1 -1
- package/dist/components/form-actions.js +1 -1
- package/dist/components/form-actions.js.map +1 -1
- package/dist/components/full-bleed-dialog.js +1 -0
- package/dist/components/full-bleed-dialog.js.map +1 -1
- package/dist/components/icon-button.d.ts +11 -0
- package/dist/components/icon-button.js +7 -5
- package/dist/components/icon-button.js.map +1 -1
- package/dist/components/modal.js +1 -0
- package/dist/components/modal.js.map +1 -1
- package/dist/components/nav-pills.d.ts +8 -1
- package/dist/components/nav-pills.js +61 -38
- package/dist/components/nav-pills.js.map +1 -1
- package/dist/components/number-input.d.ts +2 -1
- package/dist/components/number-input.js +6 -2
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/numpad-sheet.js +2 -0
- package/dist/components/numpad-sheet.js.map +1 -1
- package/dist/components/page-header-context.d.ts +8 -0
- package/dist/components/page-header-context.js +7 -0
- package/dist/components/page-header-context.js.map +1 -0
- package/dist/components/page-header.d.ts +14 -2
- package/dist/components/page-header.js +10 -1
- package/dist/components/page-header.js.map +1 -1
- package/dist/components/picker-sheet.js +2 -0
- package/dist/components/picker-sheet.js.map +1 -1
- package/dist/components/pie-chart.js +2 -1
- package/dist/components/pie-chart.js.map +1 -1
- package/dist/components/popover.js +1 -0
- package/dist/components/popover.js.map +1 -1
- package/dist/components/progress-bar.js +5 -4
- package/dist/components/progress-bar.js.map +1 -1
- package/dist/components/reauth-dialog.js +1 -1
- package/dist/components/reauth-dialog.js.map +1 -1
- package/dist/components/row-actions-labels.js +3 -1
- package/dist/components/row-actions-labels.js.map +1 -1
- package/dist/components/row-actions.d.ts +43 -9
- package/dist/components/row-actions.js +78 -15
- package/dist/components/row-actions.js.map +1 -1
- package/dist/components/section-nav.d.ts +37 -0
- package/dist/components/section-nav.js +33 -0
- package/dist/components/section-nav.js.map +1 -0
- package/dist/components/series-chart.js +8 -3
- package/dist/components/series-chart.js.map +1 -1
- package/dist/components/share-card.js +1 -1
- package/dist/components/share-card.js.map +1 -1
- package/dist/components/signature-pad.js +1 -1
- package/dist/components/signature-pad.js.map +1 -1
- package/dist/components/sparkline.js +2 -1
- package/dist/components/sparkline.js.map +1 -1
- package/dist/components/stat-tile.js +14 -7
- package/dist/components/stat-tile.js.map +1 -1
- package/dist/components/status-dot.d.ts +5 -1
- package/dist/components/status-dot.js +4 -1
- package/dist/components/status-dot.js.map +1 -1
- package/dist/components/tooltip.js +1 -1
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/translation-review-editor.js +2 -2
- package/dist/components/translation-review-editor.js.map +1 -1
- package/dist/components/translation-review.js +2 -2
- package/dist/components/translation-review.js.map +1 -1
- package/dist/components/treemap.d.ts +5 -2
- package/dist/components/treemap.js +26 -6
- package/dist/components/treemap.js.map +1 -1
- package/dist/components/use-table-state.js +37 -6
- package/dist/components/use-table-state.js.map +1 -1
- package/dist/data-table.js.map +1 -1
- package/dist/demo/demo-banner.js +1 -1
- package/dist/demo/demo-banner.js.map +1 -1
- package/dist/demo/demo-ended.js +1 -1
- package/dist/demo/demo-ended.js.map +1 -1
- package/dist/demo/demo-start.js +1 -1
- package/dist/demo/demo-start.js.map +1 -1
- package/dist/eslint/index.js +1 -1
- package/dist/feedback/feedback-inbox.js +1 -1
- package/dist/feedback/feedback-inbox.js.map +1 -1
- package/dist/hooks/use-dismiss.d.ts +9 -1
- package/dist/hooks/use-dismiss.js +25 -5
- package/dist/hooks/use-dismiss.js.map +1 -1
- package/dist/hooks/use-document-title.d.ts +14 -0
- package/dist/hooks/use-document-title.js +22 -0
- package/dist/hooks/use-document-title.js.map +1 -0
- package/dist/i18n/defaults.js +2 -0
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.js +20 -3
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +34 -1
- package/dist/i18n/kit-labels.js +43 -2
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/en.js +22 -3
- package/dist/i18n/locales/en.js.map +3 -3
- package/dist/i18n/locales/es.js +20 -3
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.js +20 -3
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.js +20 -3
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.js +20 -3
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.js +20 -3
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.js +3 -0
- package/dist/i18n/review.js.map +1 -1
- package/dist/i18n/shared-labels.js +1 -0
- package/dist/i18n/shared-labels.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/landing/landing-actions.js +2 -2
- package/dist/landing/landing-actions.js.map +1 -1
- package/dist/landing/public-header.js +2 -2
- package/dist/landing/public-header.js.map +1 -1
- package/dist/lib/clipboard-minus.d.ts +27 -0
- package/dist/lib/clipboard-minus.js +63 -0
- package/dist/lib/clipboard-minus.js.map +1 -0
- package/dist/lib/data-attributes.d.ts +11 -0
- package/dist/lib/data-attributes.js +10 -0
- package/dist/lib/data-attributes.js.map +1 -0
- package/dist/lib/document-title.d.ts +17 -0
- package/dist/lib/document-title.js +29 -0
- package/dist/lib/document-title.js.map +1 -0
- package/dist/lib/float-clearance.js +8 -2
- package/dist/lib/float-clearance.js.map +1 -1
- package/dist/lib/format.d.ts +36 -10
- package/dist/lib/format.js +57 -11
- package/dist/lib/format.js.map +1 -1
- package/dist/lib/minus.d.ts +32 -0
- package/dist/lib/minus.js +14 -0
- package/dist/lib/minus.js.map +1 -0
- package/dist/lib/status-tone.d.ts +30 -0
- package/dist/lib/status-tone.js +17 -0
- package/dist/lib/status-tone.js.map +1 -0
- package/dist/settings/settings-layout.js +3 -2
- package/dist/settings/settings-layout.js.map +1 -1
- package/dist/shell/account-menu-language.d.ts +1 -1
- package/dist/shell/account-menu-language.js +9 -1
- package/dist/shell/account-menu-language.js.map +1 -1
- package/dist/shell/app-shell-labels.js +2 -1
- package/dist/shell/app-shell-labels.js.map +1 -1
- package/dist/shell/app-shell.d.ts +58 -16
- package/dist/shell/app-shell.js +151 -43
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/language-code.d.ts +3 -0
- package/dist/shell/language-code.js +8 -0
- package/dist/shell/language-code.js.map +1 -0
- package/dist/shell/topbar-controls.d.ts +14 -6
- package/dist/shell/topbar-controls.js +14 -17
- package/dist/shell/topbar-controls.js.map +1 -1
- package/dist/testing/css-variables.generated.d.ts +1 -1
- package/dist/testing/css-variables.generated.js +1 -1
- package/dist/testing/css-variables.generated.js.map +1 -1
- package/dist/testing/react.d.ts +25 -0
- package/dist/testing/react.js +19 -0
- package/dist/testing/react.js.map +1 -0
- package/dist/theme/chart-palette.d.ts +82 -15
- package/dist/theme/chart-palette.js +51 -24
- package/dist/theme/chart-palette.js.map +1 -1
- package/dist/theme/palette-presets.d.ts +9 -0
- package/dist/theme/palette-presets.js +77 -62
- package/dist/theme/palette-presets.js.map +1 -1
- package/dist/tour/tour-dock.d.ts +42 -0
- package/dist/tour/tour-dock.js +80 -0
- package/dist/tour/tour-dock.js.map +1 -0
- package/dist/tour/tour-gates.d.ts +71 -0
- package/dist/tour/tour-gates.js +114 -0
- package/dist/tour/tour-gates.js.map +1 -0
- package/dist/tour/tour-guide-button.d.ts +27 -0
- package/dist/tour/tour-guide-button.js +45 -0
- package/dist/tour/tour-guide-button.js.map +1 -0
- package/dist/tour/tour-guide-labels.d.ts +8 -0
- package/dist/tour/tour-guide-labels.js +9 -0
- package/dist/tour/tour-guide-labels.js.map +1 -0
- package/dist/tour/tour-labels.js +9 -1
- package/dist/tour/tour-labels.js.map +1 -1
- package/dist/tour/tour.d.ts +133 -9
- package/dist/tour/tour.js +594 -70
- package/dist/tour/tour.js.map +1 -1
- package/dist/tour/use-tour-url-sync.d.ts +5 -2
- package/dist/tour/use-tour-url-sync.js +7 -1
- package/dist/tour/use-tour-url-sync.js.map +1 -1
- package/dist/tour.d.ts +2 -0
- package/dist/tour.js +3 -0
- package/dist/tour.js.map +1 -1
- package/dist/wizard/stepper-nav.d.ts +1 -1
- package/dist/wizard/stepper-nav.js +18 -5
- package/dist/wizard/stepper-nav.js.map +1 -1
- package/dist/wizard/types.d.ts +11 -0
- package/dist/wizard/types.js.map +1 -1
- package/dist/wizard/use-wizard.js +11 -5
- package/dist/wizard/use-wizard.js.map +1 -1
- package/package.json +9 -1
- package/src/admin/admin-action-confirm.tsx +1 -1
- package/src/admin/invitations-panel.tsx +7 -3
- package/src/admin/user-roster.tsx +41 -14
- package/src/auth/accept-invitation.tsx +3 -3
- package/src/auth/not-found-page.tsx +1 -1
- package/src/auth/reset-password-form.tsx +3 -3
- package/src/auth/verify-email.tsx +13 -3
- package/src/billing/plan-card.tsx +2 -2
- package/src/components/amount-input.tsx +5 -4
- package/src/components/autocomplete.tsx +2 -0
- package/src/components/breadcrumbs.tsx +32 -4
- package/src/components/button-look.ts +86 -15
- package/src/components/button.tsx +6 -3
- package/src/components/calendar-heatmap.tsx +2 -1
- package/src/components/chart-edge.ts +73 -0
- package/src/components/chart.tsx +3 -1
- package/src/components/chip.tsx +10 -1
- package/src/components/combobox-core.tsx +2 -0
- package/src/components/combobox-suggestions.tsx +2 -0
- package/src/components/confirm-context.ts +7 -0
- package/src/components/confirm-dialog.tsx +3 -3
- package/src/components/currency-select.tsx +95 -6
- package/src/components/danger-confirm.tsx +3 -3
- package/src/components/data-table-fit.ts +41 -6
- package/src/components/data-table-mobile.tsx +259 -17
- package/src/components/data-table-row-actions.tsx +9 -1
- package/src/components/data-table-row.tsx +3 -0
- package/src/components/data-table.tsx +91 -19
- package/src/components/dropdown.tsx +2 -0
- package/src/components/error-boundary.tsx +2 -2
- package/src/components/floating-panel.tsx +67 -16
- package/src/components/form-actions.tsx +2 -2
- package/src/components/full-bleed-dialog.tsx +3 -0
- package/src/components/icon-button.tsx +21 -8
- package/src/components/modal.tsx +3 -0
- package/src/components/nav-pills.tsx +29 -2
- package/src/components/number-input.tsx +8 -3
- package/src/components/numpad-sheet.tsx +4 -0
- package/src/components/page-header-context.ts +8 -0
- package/src/components/page-header.tsx +34 -2
- package/src/components/picker-sheet.tsx +4 -0
- package/src/components/pie-chart.tsx +2 -1
- package/src/components/popover.tsx +2 -0
- package/src/components/progress-bar.tsx +6 -3
- package/src/components/reauth-dialog.tsx +1 -1
- package/src/components/row-actions-labels.ts +2 -0
- package/src/components/row-actions.tsx +154 -33
- package/src/components/section-nav.tsx +65 -0
- package/src/components/series-chart.tsx +12 -3
- package/src/components/share-card.tsx +1 -1
- package/src/components/signature-pad.tsx +1 -1
- package/src/components/sparkline.tsx +2 -1
- package/src/components/stat-tile.tsx +19 -7
- package/src/components/status-dot.tsx +7 -1
- package/src/components/tooltip.tsx +3 -1
- package/src/components/translation-review-editor.tsx +2 -2
- package/src/components/translation-review.tsx +2 -2
- package/src/components/treemap.tsx +34 -8
- package/src/components/use-table-state.ts +63 -8
- package/src/data-table.ts +3 -3
- package/src/demo/demo-banner.tsx +1 -1
- package/src/demo/demo-ended.tsx +1 -1
- package/src/demo/demo-start.tsx +1 -1
- package/src/feedback/feedback-inbox.tsx +1 -1
- package/src/hooks/use-dismiss.ts +43 -4
- package/src/hooks/use-document-title.ts +31 -0
- package/src/i18n/defaults.ts +2 -0
- package/src/i18n/german.ts +17 -0
- package/src/i18n/kit-labels.tsx +79 -1
- package/src/i18n/locales/es.ts +17 -0
- package/src/i18n/locales/fr.ts +17 -0
- package/src/i18n/locales/hu.ts +17 -0
- package/src/i18n/locales/it.ts +17 -0
- package/src/i18n/locales/zh.ts +17 -0
- package/src/i18n/review.ts +3 -0
- package/src/i18n/shared-labels.ts +1 -0
- package/src/index.ts +13 -0
- package/src/landing/landing-actions.tsx +2 -2
- package/src/landing/public-header.tsx +2 -2
- package/src/lib/clipboard-minus.ts +94 -0
- package/src/lib/data-attributes.ts +14 -0
- package/src/lib/document-title.ts +41 -0
- package/src/lib/float-clearance.ts +19 -3
- package/src/lib/format.ts +117 -30
- package/src/lib/minus.ts +50 -0
- package/src/lib/status-tone.ts +43 -0
- package/src/settings/settings-layout.tsx +3 -2
- package/src/shell/account-menu-language.tsx +14 -2
- package/src/shell/app-shell-labels.ts +1 -0
- package/src/shell/app-shell.tsx +291 -68
- package/src/shell/language-code.ts +5 -0
- package/src/shell/topbar-controls.tsx +35 -15
- package/src/testing/css-variables.generated.ts +6 -6
- package/src/testing/react.ts +59 -0
- package/src/theme/chart-palette.ts +102 -20
- package/src/theme/palette-presets.ts +121 -84
- package/src/tour/tour-dock.ts +132 -0
- package/src/tour/tour-gates.ts +195 -0
- package/src/tour/tour-guide-button.tsx +64 -0
- package/src/tour/tour-guide-labels.ts +15 -0
- package/src/tour/tour-labels.ts +8 -0
- package/src/tour/tour.tsx +949 -86
- package/src/tour/use-tour-url-sync.ts +14 -4
- package/src/tour.ts +3 -0
- package/src/wizard/stepper-nav.tsx +27 -7
- package/src/wizard/types.ts +12 -0
- package/src/wizard/use-wizard.ts +16 -9
- package/tokens.css +28 -18
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// The live side of `chartEdge` (theme/chart-palette.ts): which colours a FILLED mark is
|
|
2
|
+
// painted with and which grounds it sits on, read off <html> as the page resolves them —
|
|
3
|
+
// so the 1px ink edge follows a theme toggle and a palette switch without the chart
|
|
4
|
+
// knowing either store. Shared by SeriesChart (bars, areas) and Treemap (tiles).
|
|
5
|
+
import { useCallback, useSyncExternalStore } from "react";
|
|
6
|
+
import { chartEdge } from "../theme/chart-palette";
|
|
7
|
+
import { parseHex } from "../theme/color";
|
|
8
|
+
|
|
9
|
+
/** The tokens a theme or palette change can move, read as ONE string so
|
|
10
|
+
* `useSyncExternalStore` compares a primitive (a fresh array would never be equal). */
|
|
11
|
+
const WATCHED = [
|
|
12
|
+
"--bg-surface",
|
|
13
|
+
"--bg-page",
|
|
14
|
+
...Array.from({ length: 9 }, (_, i) => `--chart-${i + 1}`),
|
|
15
|
+
"--chart-ink",
|
|
16
|
+
"--chart-muted",
|
|
17
|
+
"--money-income",
|
|
18
|
+
"--money-expense",
|
|
19
|
+
"--money-net",
|
|
20
|
+
"--money-neutral",
|
|
21
|
+
"--brand",
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
function readWatched(): string {
|
|
25
|
+
const style = getComputedStyle(document.documentElement);
|
|
26
|
+
return WATCHED.map((name) => style.getPropertyValue(name).trim()).join(",");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function subscribe(onChange: () => void): () => void {
|
|
30
|
+
const observer = new MutationObserver(onChange);
|
|
31
|
+
observer.observe(document.documentElement, {
|
|
32
|
+
attributes: true,
|
|
33
|
+
attributeFilter: ["class", "style", "data-theme", "data-contrast"],
|
|
34
|
+
});
|
|
35
|
+
return () => observer.disconnect();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Server render: nothing measured, so no edge — the client adds it on hydration. */
|
|
39
|
+
const SERVER = "";
|
|
40
|
+
|
|
41
|
+
const BARE_VAR = /^var\(\s*(--[A-Za-z0-9_-]+)\s*\)$/;
|
|
42
|
+
|
|
43
|
+
/** A colour as the page resolves it: a hex as it is, a bare `var(--token)` read off
|
|
44
|
+
* <html>, anything else (a `color-mix()`, a var with a fallback) null. */
|
|
45
|
+
export function resolveChartColor(color: string, style: Pick<CSSStyleDeclaration, "getPropertyValue">): string | null {
|
|
46
|
+
const trimmed = color.trim();
|
|
47
|
+
if (parseHex(trimmed)) return trimmed;
|
|
48
|
+
const m = BARE_VAR.exec(trimmed);
|
|
49
|
+
if (!m) return null;
|
|
50
|
+
const value = style.getPropertyValue(m[1]).trim();
|
|
51
|
+
return parseHex(value) ? value : null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* `(fill) => edge`: the ink stroke a filled mark in `fill` needs on this page, or
|
|
56
|
+
* `undefined` — only for a colour under 3:1 against the card or the page (WCAG 1.4.11).
|
|
57
|
+
* `fill` may be a hex or a bare `var(--chart-N)` / `var(--money-…)`; anything it cannot
|
|
58
|
+
* measure gets no edge.
|
|
59
|
+
*/
|
|
60
|
+
export function useChartEdge(): (fill: string | undefined) => string | undefined {
|
|
61
|
+
const snapshot = useSyncExternalStore(subscribe, readWatched, () => SERVER);
|
|
62
|
+
return useCallback(
|
|
63
|
+
(fill: string | undefined) => {
|
|
64
|
+
if (!fill || snapshot === SERVER || typeof document === "undefined") return undefined;
|
|
65
|
+
const style = getComputedStyle(document.documentElement);
|
|
66
|
+
const hex = resolveChartColor(fill, style);
|
|
67
|
+
if (!hex) return undefined;
|
|
68
|
+
const surfaces = ["--bg-surface", "--bg-page"].map((n) => style.getPropertyValue(n).trim());
|
|
69
|
+
return chartEdge(hex, surfaces);
|
|
70
|
+
},
|
|
71
|
+
[snapshot],
|
|
72
|
+
);
|
|
73
|
+
}
|
package/src/components/chart.tsx
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
} from "recharts";
|
|
18
18
|
import { cn } from "../lib/cn";
|
|
19
19
|
import { isRtl } from "../lib/direction";
|
|
20
|
+
import { TYPOGRAPHIC_MINUS } from "../lib/minus";
|
|
20
21
|
|
|
21
22
|
export interface ChartSeriesConfig {
|
|
22
23
|
label: ReactNode;
|
|
@@ -376,7 +377,8 @@ export function ChartTooltipContent({
|
|
|
376
377
|
if (inRow && !shown) text = NO_VALUE;
|
|
377
378
|
else if (formatValue) text = formatValue(raw);
|
|
378
379
|
else if (raw == null) text = NO_VALUE;
|
|
379
|
-
|
|
380
|
+
// The bare number's minus is U+2212, as every kit figure's (0.49, KIT-1).
|
|
381
|
+
else text = valueFormatter ? valueFormatter(raw) : String(raw).replace(/-/g, TYPOGRAPHIC_MINUS);
|
|
380
382
|
return (
|
|
381
383
|
<div
|
|
382
384
|
key={`${cfgKey}-${i}`}
|
package/src/components/chip.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import type { KeyboardEvent, MouseEvent, ReactElement, ReactNode, Ref } from "re
|
|
|
3
3
|
import { Check, X } from "lucide-react";
|
|
4
4
|
import type { LucideIcon } from "lucide-react";
|
|
5
5
|
import { cn } from "../lib/cn";
|
|
6
|
+
import { statusTone, type StatusKind } from "../lib/status-tone";
|
|
6
7
|
import { TOUCH_TARGET_LARGE } from "./button-look";
|
|
7
8
|
import { FOCUS_RING } from "./focus-ring";
|
|
8
9
|
import { assignRef, LockedReason, type DisabledReasonDisplay } from "./field-parts";
|
|
@@ -342,6 +343,12 @@ const REMOVE_HIT_AREA_LARGE =
|
|
|
342
343
|
interface ChipBaseProps {
|
|
343
344
|
children: ReactNode;
|
|
344
345
|
tone?: ChipTone;
|
|
346
|
+
/**
|
|
347
|
+
* What the value means as a status (0.49 KIT-8), instead of a `tone`: `good` (success),
|
|
348
|
+
* `attention` (warning), `problem` (danger), `active` (info), `inactive` (neutral) —
|
|
349
|
+
* {@link statusTone}. An explicit `tone` wins. Never `brand` for a status.
|
|
350
|
+
*/
|
|
351
|
+
status?: StatusKind;
|
|
345
352
|
size?: ChipSize;
|
|
346
353
|
/** See {@link ChipVariant}. Default `soft`. */
|
|
347
354
|
variant?: ChipVariant;
|
|
@@ -537,7 +544,8 @@ export type ChipProps = ChipBaseProps &
|
|
|
537
544
|
export function Chip({
|
|
538
545
|
ref,
|
|
539
546
|
children,
|
|
540
|
-
tone
|
|
547
|
+
tone: toneProp,
|
|
548
|
+
status,
|
|
541
549
|
size = "md",
|
|
542
550
|
variant = "soft",
|
|
543
551
|
shape = "pill",
|
|
@@ -563,6 +571,7 @@ export function Chip({
|
|
|
563
571
|
commit,
|
|
564
572
|
...rest
|
|
565
573
|
}: ChipProps) {
|
|
574
|
+
const tone: ChipTone = toneProp ?? (status ? statusTone(status) : "neutral");
|
|
566
575
|
const common = useKitLabels("common", DEFAULT_COMMON_LABELS, { remove: removeLabel });
|
|
567
576
|
const reasonId = useId();
|
|
568
577
|
const disabledReason = useCommitReason(commit, ownDisabledReason);
|
|
@@ -1064,6 +1064,8 @@ export function ComboboxPanel<V extends string | number>({
|
|
|
1064
1064
|
// `aria-describedby` has nothing to collide with.
|
|
1065
1065
|
{...rest}
|
|
1066
1066
|
ref={panelRef}
|
|
1067
|
+
// A kit layer, for the tour's layer order (0.50, docs/tours-harmonization.md §11.6).
|
|
1068
|
+
data-kit-layer=""
|
|
1067
1069
|
dir={dir}
|
|
1068
1070
|
onKeyDown={onKeyDown}
|
|
1069
1071
|
// A press in a panel opened from a row is inside this list for the core's outside
|
|
@@ -92,6 +92,8 @@ export function SuggestionList({
|
|
|
92
92
|
return createPortal(
|
|
93
93
|
<div
|
|
94
94
|
ref={panelRef}
|
|
95
|
+
// A kit layer, for the tour's layer order (0.50, docs/tours-harmonization.md §11.6).
|
|
96
|
+
data-kit-layer=""
|
|
95
97
|
// z-50, not the old z-30: the list is a child of <body> now, so it is
|
|
96
98
|
// competing with the app's own overlays rather than with its own siblings.
|
|
97
99
|
className="fixed z-50"
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createContext } from "react";
|
|
2
|
+
import type { ConfirmFn } from "./confirm-dialog";
|
|
3
|
+
|
|
4
|
+
/** The nearest `ConfirmProvider`'s `confirm`, or `null`. Internal: its own module so a
|
|
5
|
+
* part that may sit outside a provider (`RowActions`, 0.49 KIT-12) can ask without
|
|
6
|
+
* `useConfirm()`'s throw, and without adding a name to the barrel. */
|
|
7
|
+
export const ConfirmContext = createContext<ConfirmFn | null>(null);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
2
2
|
import type { ReactNode } from "react";
|
|
3
3
|
|
|
4
4
|
import { useKitLabels } from "../i18n/kit-labels";
|
|
@@ -8,6 +8,7 @@ import { DialogFrame } from "./dialog-frame";
|
|
|
8
8
|
import { Button } from "./button";
|
|
9
9
|
import { useWriteLock, writeLockFor, type CommitScope } from "./write-lock";
|
|
10
10
|
import { DEFAULT_CONFIRM_DIALOG_LABELS } from "./confirm-dialog-labels";
|
|
11
|
+
import { ConfirmContext } from "./confirm-context";
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* The `confirmDialog` namespace of `<UiKitProvider labels>`: the two buttons' fallback
|
|
@@ -89,7 +90,6 @@ export interface ConfirmOptions {
|
|
|
89
90
|
|
|
90
91
|
export type ConfirmFn = (options: ConfirmOptions) => Promise<boolean>;
|
|
91
92
|
|
|
92
|
-
const ConfirmContext = createContext<ConfirmFn | null>(null);
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
95
|
* `const confirm = useConfirm()` → `if (!(await confirm({ title }))) return`.
|
|
@@ -298,7 +298,7 @@ function ConfirmDialogView({
|
|
|
298
298
|
<Button
|
|
299
299
|
ref={confirmRef}
|
|
300
300
|
type="button"
|
|
301
|
-
variant={tone === "danger" ? "danger" : "
|
|
301
|
+
variant={tone === "danger" ? "danger" : "default"}
|
|
302
302
|
disabled={!matches}
|
|
303
303
|
disabledReason={request.lockReason}
|
|
304
304
|
onClick={() => {
|
|
@@ -2,7 +2,16 @@ import { useCallback, useId } from "react";
|
|
|
2
2
|
import type { ComponentPropsWithoutRef, ReactNode, Ref } from "react";
|
|
3
3
|
import { FieldChevron, FIELD_TRIGGER, FIELD_INVALID, FIELD_FLOATING_PAD } from "./field-base";
|
|
4
4
|
import { FieldLabel } from "./field-parts";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
assignRef,
|
|
7
|
+
EndHintRow,
|
|
8
|
+
LockedReason,
|
|
9
|
+
useFieldMessages,
|
|
10
|
+
useLockReason,
|
|
11
|
+
type DisabledReasonDisplay,
|
|
12
|
+
type FieldHelpProps,
|
|
13
|
+
} from "./field-parts";
|
|
14
|
+
import type { CommitScope } from "./write-lock";
|
|
6
15
|
import { cn } from "../lib/cn";
|
|
7
16
|
import { useComboboxCore } from "./combobox-core";
|
|
8
17
|
import {
|
|
@@ -90,6 +99,31 @@ export interface CurrencySelectProps extends Omit<ComponentPropsWithoutRef<"div"
|
|
|
90
99
|
* filter that only matches the hidden English name is worse than no filter.
|
|
91
100
|
*/
|
|
92
101
|
currencyNames?: Record<string, string>;
|
|
102
|
+
/** As {@link Select}'s: no focus, no list, the settled look, no chevron. */
|
|
103
|
+
disabled?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Why the currency cannot be changed — {@link Select}'s `disabledReason` (keksdose,
|
|
106
|
+
* 2026-10-23: a budget's currency for a guest or a viewer, and while offline, which
|
|
107
|
+
* keksdose drew as a one-option `Select` to keep the reason and lost the flag). The
|
|
108
|
+
* trigger stays focusable and `aria-disabled` (never the `disabled` attribute), keeps
|
|
109
|
+
* its flag and code, opens nothing, and `onChange` is never called; the reason is a
|
|
110
|
+
* line under the field at Large and on a touch screen, else in the kit
|
|
111
|
+
* {@link Tooltip} (`disabledReasonDisplay`), and on the trigger's `aria-describedby`
|
|
112
|
+
* (after a caption, before an error). It wears the settled look of a disabled field
|
|
113
|
+
* and drops its chevron, as a locked Select does. It wins over `disabled`.
|
|
114
|
+
*/
|
|
115
|
+
disabledReason?: ReactNode;
|
|
116
|
+
/** Where the reason shows — {@link Select}'s `disabledReasonDisplay`: a line under the
|
|
117
|
+
* field at Large and on a touch screen by default, the tooltip otherwise. */
|
|
118
|
+
disabledReasonDisplay?: DisabledReasonDisplay;
|
|
119
|
+
/**
|
|
120
|
+
* This picker COMMITS — choosing saves. Under a locked {@link WriteLockProvider} it is
|
|
121
|
+
* locked the `disabledReason` way with the lock's reason (which wins over its own).
|
|
122
|
+
* No provider, or an unlocked one: no effect. {@link Select}'s `commit`. A currency
|
|
123
|
+
* field inside a form with its own Save stays editable under the lock — leave this off
|
|
124
|
+
* there, and put `commit` on the Save.
|
|
125
|
+
*/
|
|
126
|
+
commit?: CommitScope;
|
|
93
127
|
/** The TRIGGER — the focusable `<button>` — as a React 19 ref prop, as on
|
|
94
128
|
* {@link MultiSelect} and {@link CountrySelect}: react-hook-form's focus-on-error
|
|
95
129
|
* calls `focus()` on it (0.34, API-3, refactor audit 2026-10). Not the wrapper. */
|
|
@@ -109,6 +143,10 @@ export function CurrencySelect({
|
|
|
109
143
|
help,
|
|
110
144
|
labels,
|
|
111
145
|
currencyNames,
|
|
146
|
+
disabled,
|
|
147
|
+
disabledReason,
|
|
148
|
+
disabledReasonDisplay,
|
|
149
|
+
commit,
|
|
112
150
|
"aria-label": ariaLabel,
|
|
113
151
|
"aria-describedby": ariaDescribedBy,
|
|
114
152
|
"aria-invalid": ariaInvalid,
|
|
@@ -137,6 +175,15 @@ export function CurrencySelect({
|
|
|
137
175
|
|
|
138
176
|
const selected = getCurrency(value);
|
|
139
177
|
|
|
178
|
+
// The lock — Select's (`useLockReason`): the WriteLock's reason under `commit`, else
|
|
179
|
+
// the caller's own. Locked or `disabled`, nothing opens the list or picks a value.
|
|
180
|
+
const lock = useLockReason(commit, disabledReason);
|
|
181
|
+
const locked = lock.locked;
|
|
182
|
+
const inert = locked || Boolean(disabled);
|
|
183
|
+
// A lock (or `disabled`) arriving while the list is up closes it — adjusted while
|
|
184
|
+
// rendering, as CountrySelect does, so no frame shows a list that cannot be used.
|
|
185
|
+
if (inert && open) setOpen(false);
|
|
186
|
+
|
|
140
187
|
const listboxId = `${useId()}-listbox`;
|
|
141
188
|
// Text is a caption under the field; anything else (a FieldHint) rides the label
|
|
142
189
|
// line — and a FieldHint is that caption at Large and on touch (§4). See `hint`. On
|
|
@@ -152,6 +199,8 @@ export function CurrencySelect({
|
|
|
152
199
|
invalid,
|
|
153
200
|
describedBy: ariaDescribedBy,
|
|
154
201
|
ariaInvalid,
|
|
202
|
+
// A lock's reason sits between the caption and the error, as on Select.
|
|
203
|
+
reasonId: locked && lock.reasonId,
|
|
155
204
|
});
|
|
156
205
|
const { labelHint, describedBy, isInvalid } = field;
|
|
157
206
|
|
|
@@ -169,10 +218,21 @@ export function CurrencySelect({
|
|
|
169
218
|
ref={setTrigger}
|
|
170
219
|
type="button"
|
|
171
220
|
aria-label={name}
|
|
172
|
-
|
|
221
|
+
// The focusable kind of disabled while locked (Select's rule): native `disabled`
|
|
222
|
+
// would take the field out of the tab order, and the reason with it.
|
|
223
|
+
disabled={locked ? undefined : disabled}
|
|
224
|
+
aria-disabled={locked || undefined}
|
|
225
|
+
onClick={(e) => {
|
|
226
|
+
if (inert) {
|
|
227
|
+
e.preventDefault();
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
setOpen((v) => !v);
|
|
231
|
+
}}
|
|
173
232
|
// Down/Up opens the list from the closed trigger, per the APG; Enter and Space
|
|
174
233
|
// already do through the button's own click.
|
|
175
234
|
onKeyDown={(e) => {
|
|
235
|
+
if (inert) return;
|
|
176
236
|
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
177
237
|
e.preventDefault();
|
|
178
238
|
setOpen(true);
|
|
@@ -191,12 +251,27 @@ export function CurrencySelect({
|
|
|
191
251
|
aria-expanded={open}
|
|
192
252
|
aria-invalid={isInvalid || undefined}
|
|
193
253
|
aria-describedby={describedBy}
|
|
194
|
-
className={cn(
|
|
254
|
+
className={cn(
|
|
255
|
+
FIELD_TRIGGER,
|
|
256
|
+
"pe-9",
|
|
257
|
+
label !== undefined && FIELD_FLOATING_PAD,
|
|
258
|
+
// FIELD_BASE's `disabled:` look, for a trigger locked without being `disabled`
|
|
259
|
+
// (the `:disabled` variants cannot match it) — Select's FIELD_LOCKED. No hover:
|
|
260
|
+
// nothing here answers a press.
|
|
261
|
+
locked && "cursor-not-allowed bg-surface-2 text-muted hover:bg-surface-2",
|
|
262
|
+
isInvalid && FIELD_INVALID,
|
|
263
|
+
)}
|
|
195
264
|
>
|
|
196
265
|
{/* The trigger stays COMPACT — flag + code only — so it never clips in a
|
|
197
266
|
narrow field; the full names live in the (wider) popup (feedback
|
|
198
267
|
#308). */}
|
|
199
|
-
<span
|
|
268
|
+
<span
|
|
269
|
+
className={cn(
|
|
270
|
+
"flex items-center gap-2 min-w-0",
|
|
271
|
+
// The settled look reaches the code too: `text-primary` here would outweigh it.
|
|
272
|
+
!selected ? "text-placeholder" : inert ? "text-muted" : "text-primary",
|
|
273
|
+
)}
|
|
274
|
+
>
|
|
200
275
|
{selected ? (
|
|
201
276
|
<>
|
|
202
277
|
<CurrencyFlag country={selected.country} />
|
|
@@ -206,10 +281,23 @@ export function CurrencySelect({
|
|
|
206
281
|
placeholder ?? text.currency
|
|
207
282
|
)}
|
|
208
283
|
</span>
|
|
209
|
-
|
|
284
|
+
{/* No menu to drop while disabled or locked, so no chevron — the arrow is the one
|
|
285
|
+
thing on the control that promises a choice (dev#474, as on Select). */}
|
|
286
|
+
{!inert && <FieldChevron />}
|
|
210
287
|
</button>
|
|
211
288
|
);
|
|
212
289
|
|
|
290
|
+
// Select's frame: the line under the trigger, or the Tooltip with a hidden copy for
|
|
291
|
+
// `aria-describedby`. The wrapper `<div>` keeps `className`, so the layout holds.
|
|
292
|
+
// `flex w-full items-stretch`: the trigger keeps the field's full width in it.
|
|
293
|
+
const control = locked ? (
|
|
294
|
+
<LockedReason lock={lock} display={disabledReasonDisplay} className="flex w-full items-stretch">
|
|
295
|
+
{trigger}
|
|
296
|
+
</LockedReason>
|
|
297
|
+
) : (
|
|
298
|
+
trigger
|
|
299
|
+
);
|
|
300
|
+
|
|
213
301
|
return (
|
|
214
302
|
// The wrapper takes `rest`; the trigger takes the name. This field's label is a
|
|
215
303
|
// floating <span> rather than a <label for>, so a caller who wants the control to
|
|
@@ -230,7 +318,7 @@ export function CurrencySelect({
|
|
|
230
318
|
))}
|
|
231
319
|
{/* No label line to ride: at the field's end edge, outside the box, as an
|
|
232
320
|
unlabelled Select places its FieldHint. */}
|
|
233
|
-
<EndHintRow hint={label === undefined ? labelHint : undefined}>{
|
|
321
|
+
<EndHintRow hint={label === undefined ? labelHint : undefined}>{control}</EndHintRow>
|
|
234
322
|
{field.below}
|
|
235
323
|
{/* Portalled to <body> and anchored to the trigger (dev#548), so where it stands
|
|
236
324
|
in the flow no longer moves it. */}
|
|
@@ -246,6 +334,7 @@ export function CurrencySelect({
|
|
|
246
334
|
// to say what it asks for (live #200).
|
|
247
335
|
sheetTitle={label ?? text.currency}
|
|
248
336
|
onChoose={(code) => {
|
|
337
|
+
if (inert) return;
|
|
249
338
|
onChange(code);
|
|
250
339
|
// Back to the trigger: the panel holding focus is about to unmount, and a
|
|
251
340
|
// keyboard user who has answered the field should stand on it, ready to Tab on.
|
|
@@ -449,14 +449,14 @@ export interface DangerConfirmProps extends Omit<ComponentPropsWithRef<"div">, "
|
|
|
449
449
|
tone?: "danger" | "warning";
|
|
450
450
|
/**
|
|
451
451
|
* The confirm button's variant, apart from `tone` (0.24, keksdose). Default: what
|
|
452
|
-
* `tone` picks, as before — `"danger"` for `"danger"`, `"
|
|
452
|
+
* `tone` picks, as before — `"danger"` for `"danger"`, `"default"` for `"warning"`.
|
|
453
453
|
*
|
|
454
454
|
* `tone` says how bad the outcome is; this says how loud the last press is, and the two
|
|
455
455
|
* need not agree. keksdose's `UserActionConfirm` (an admin resets somebody else's
|
|
456
456
|
* password) asks an AMBER question — what it guards against is mis-targeting, not data
|
|
457
457
|
* loss, so the danger colour stays its one `severe` consequence's — and ends in a RED
|
|
458
458
|
* Go, as its hand-built panel did before it moved onto this tile in 0.23; with only
|
|
459
|
-
* `tone`, the amber question made Go the neutral
|
|
459
|
+
* `tone`, the amber question made Go the neutral `default` fill. `tone="warning"
|
|
460
460
|
* confirmVariant="danger"` is that panel again.
|
|
461
461
|
*/
|
|
462
462
|
confirmVariant?: ButtonVariant;
|
|
@@ -914,7 +914,7 @@ export function DangerConfirm({
|
|
|
914
914
|
<Button
|
|
915
915
|
id={confirmId}
|
|
916
916
|
type="submit"
|
|
917
|
-
variant={confirmVariant ?? (tone === "warning" ? "
|
|
917
|
+
variant={confirmVariant ?? (tone === "warning" ? "default" : "danger")}
|
|
918
918
|
disabled={!canConfirm}
|
|
919
919
|
// Held by a guard (the caller's or a built-in one), or armed and then locked
|
|
920
920
|
// (or rendered armed under a lock): the confirm says why the way Button
|
|
@@ -15,7 +15,9 @@ import type { DataTableColumn } from "./data-table";
|
|
|
15
15
|
// room for them (its need re-measured, plus the column's own header width from when
|
|
16
16
|
// it was left out), so narrower content (a filter, another page) counts as much as a
|
|
17
17
|
// wider scroller. The scroller's own width, through a ResizeObserver: a sidebar, a
|
|
18
|
-
// narrower window, the Columns drawer and Large text all count the same.
|
|
18
|
+
// narrower window, the Columns drawer and Large text all count the same. A column
|
|
19
|
+
// whose return the very next measure undoes stays out until the table has more room
|
|
20
|
+
// for it than it had then (0.48.3, keksdose B2: see the bring-back in `measure`).
|
|
19
21
|
|
|
20
22
|
/** The least a `grow` column keeps before the table starts leaving columns out: about
|
|
21
23
|
* a sentence of a title. In rem, so Large text asks for more. */
|
|
@@ -94,6 +96,13 @@ export function useColumnFit<T>({
|
|
|
94
96
|
// table's need as it is now, plus this, fits the scroller (keksdose S3, 0.48: not the
|
|
95
97
|
// whole table's need at hide time, which went stale when the content narrowed).
|
|
96
98
|
const ownWidth = useRef(new Map<string, number>());
|
|
99
|
+
// A column whose return the very next measure undid, at the same scroller width: the
|
|
100
|
+
// room the table had for it when it came back (keksdose B2, 0.48.3). It stays out
|
|
101
|
+
// until the table has more. Dropped once the column is no longer left out for width.
|
|
102
|
+
const stuck = useRef(new Map<string, number>());
|
|
103
|
+
// What the last measure brought back, each column with the room it had then, and at
|
|
104
|
+
// which scroller width: read (and cleared) by the measure right after it.
|
|
105
|
+
const lastShown = useRef<{ avail: number; rooms: Map<string, number> } | null>(null);
|
|
97
106
|
|
|
98
107
|
const hasFit = columns.some((c) => c.fitPriority != null);
|
|
99
108
|
const active = enabled && hasFit;
|
|
@@ -118,6 +127,10 @@ export function useColumnFit<T>({
|
|
|
118
127
|
|
|
119
128
|
const measure = useCallback(() => {
|
|
120
129
|
const { order, current, growKey, active, scroller } = latest.current;
|
|
130
|
+
// Only the measure right after a bring-back can undo it.
|
|
131
|
+
const flip = lastShown.current;
|
|
132
|
+
lastShown.current = null;
|
|
133
|
+
for (const k of stuck.current.keys()) if (!current.includes(k)) stuck.current.delete(k);
|
|
121
134
|
const el = scroller();
|
|
122
135
|
if (!active || !el) return;
|
|
123
136
|
const table = el.querySelector("table");
|
|
@@ -150,20 +163,42 @@ export function useColumnFit<T>({
|
|
|
150
163
|
need -= w;
|
|
151
164
|
next.push(col.key);
|
|
152
165
|
}
|
|
153
|
-
if (next.length
|
|
166
|
+
if (next.length === current.length) return;
|
|
167
|
+
// The last measure brought these back at this same width, and the table cannot
|
|
168
|
+
// hold them: their header width fell short of what they cost drawn (see below).
|
|
169
|
+
if (flip && Math.abs(flip.avail - avail) <= 0.5)
|
|
170
|
+
for (const k of next.slice(current.length)) {
|
|
171
|
+
const room = flip.rooms.get(k);
|
|
172
|
+
if (room != null) stuck.current.set(k, room);
|
|
173
|
+
}
|
|
174
|
+
setHidden(next);
|
|
154
175
|
return;
|
|
155
176
|
}
|
|
156
177
|
// Bring back, the last left out first, while the table as it is now has room for
|
|
157
|
-
// them
|
|
158
|
-
//
|
|
178
|
+
// them by their recorded header width. A column that costs more drawn than that is
|
|
179
|
+
// left out again by the next measure, before paint. That alone does not settle it:
|
|
180
|
+
// the re-hide records its header's width again, and the header can measure the same
|
|
181
|
+
// while the column costs more (`virtualize`'s locked widths, keksdose B2, 0.48.3: the
|
|
182
|
+
// drawn column cost the table ~80–110px more than its header), so it came back, went, came back … until React's
|
|
183
|
+
// update limit (#185). So a column whose return the next measure undid, at the same
|
|
184
|
+
// scroller width, waits for more room than it had then: a wider scroller or narrower
|
|
185
|
+
// content. Whatever the error in its width, that is one invisible flip per room it is
|
|
186
|
+
// tried at.
|
|
159
187
|
let n = current.length;
|
|
188
|
+
const rooms = new Map<string, number>();
|
|
160
189
|
while (n > 0) {
|
|
161
|
-
const
|
|
190
|
+
const k = current[n - 1];
|
|
191
|
+
const w = ownWidth.current.get(k);
|
|
162
192
|
if (w == null || need + w > avail + 0.5) break;
|
|
193
|
+
const room = stuck.current.get(k);
|
|
194
|
+
if (room != null && avail - need <= room + 0.5) break;
|
|
195
|
+
rooms.set(k, avail - need);
|
|
163
196
|
need += w;
|
|
164
197
|
n--;
|
|
165
198
|
}
|
|
166
|
-
if (n
|
|
199
|
+
if (n === current.length) return;
|
|
200
|
+
lastShown.current = { avail, rooms };
|
|
201
|
+
setHidden(current.slice(0, n));
|
|
167
202
|
}, [headRefs]);
|
|
168
203
|
|
|
169
204
|
// After a render that changed what is drawn: rows, a filter or a page can widen a
|