@eifi1/ui-kit 0.4.1 → 0.5.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/LICENSE +32 -0
- package/README.md +498 -57
- package/dist/chart.d.ts +10 -0
- package/dist/chart.js +10 -0
- package/dist/chart.js.map +1 -0
- package/dist/components/account-settings.d.ts +11 -0
- package/dist/components/account-settings.js +19 -12
- package/dist/components/account-settings.js.map +1 -1
- package/dist/components/alert-banner.d.ts +10 -6
- package/dist/components/alert-banner.js +20 -16
- package/dist/components/alert-banner.js.map +1 -1
- package/dist/components/amount-input.d.ts +30 -4
- package/dist/components/amount-input.js +36 -14
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/calculator.d.ts +24 -7
- package/dist/components/calculator.js +37 -20
- package/dist/components/calculator.js.map +1 -1
- package/dist/components/chart-zoom.d.ts +156 -0
- package/dist/components/chart-zoom.js +262 -0
- package/dist/components/chart-zoom.js.map +1 -0
- package/dist/components/chart.d.ts +28 -3
- package/dist/components/chart.js +71 -32
- package/dist/components/chart.js.map +1 -1
- package/dist/components/checkbox.d.ts +51 -0
- package/dist/components/checkbox.js +123 -0
- package/dist/components/checkbox.js.map +1 -0
- package/dist/components/chip.d.ts +128 -0
- package/dist/components/chip.js +357 -0
- package/dist/components/chip.js.map +1 -0
- package/dist/components/choice-parts.d.ts +24 -0
- package/dist/components/choice-parts.js +18 -0
- package/dist/components/choice-parts.js.map +1 -0
- package/dist/components/combobox-core.d.ts +52 -9
- package/dist/components/combobox-core.js +89 -19
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox.d.ts +43 -33
- package/dist/components/combobox.js +409 -340
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/currency-select.d.ts +48 -8
- package/dist/components/currency-select.js +107 -65
- package/dist/components/currency-select.js.map +1 -1
- package/dist/components/data-table-filter-popover.d.ts +15 -3
- package/dist/components/data-table-filter-popover.js +23 -17
- package/dist/components/data-table-filter-popover.js.map +1 -1
- package/dist/components/data-table-filters.d.ts +1 -1
- package/dist/components/data-table-filters.js +3 -2
- package/dist/components/data-table-filters.js.map +1 -1
- package/dist/components/data-table-labels.d.ts +53 -2
- package/dist/components/data-table-labels.js +31 -2
- package/dist/components/data-table-labels.js.map +1 -1
- package/dist/components/data-table-pagination.d.ts +3 -1
- package/dist/components/data-table-pagination.js +16 -10
- package/dist/components/data-table-pagination.js.map +1 -1
- package/dist/components/data-table-sort.js +1 -0
- package/dist/components/data-table-sort.js.map +1 -1
- package/dist/components/data-table.d.ts +1 -1
- package/dist/components/data-table.js +201 -85
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.d.ts +25 -9
- package/dist/components/date-picker.js +232 -94
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/dropdown.d.ts +74 -19
- package/dist/components/dropdown.js +72 -22
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/entity-combobox.d.ts +5 -4
- package/dist/components/entity-combobox.js +107 -76
- package/dist/components/entity-combobox.js.map +1 -1
- package/dist/components/facing-pair.d.ts +36 -0
- package/dist/components/facing-pair.js +40 -0
- package/dist/components/facing-pair.js.map +1 -0
- package/dist/components/field-sync.d.ts +164 -0
- package/dist/components/field-sync.js +264 -0
- package/dist/components/field-sync.js.map +1 -0
- package/dist/components/file-dropzone.d.ts +21 -7
- package/dist/components/file-dropzone.js +28 -7
- package/dist/components/file-dropzone.js.map +1 -1
- package/dist/components/full-bleed-dialog.d.ts +37 -26
- package/dist/components/full-bleed-dialog.js +20 -5
- package/dist/components/full-bleed-dialog.js.map +1 -1
- package/dist/components/grouped-picker.d.ts +21 -10
- package/dist/components/grouped-picker.js +66 -52
- package/dist/components/grouped-picker.js.map +1 -1
- package/dist/components/hover-menu.d.ts +20 -4
- package/dist/components/hover-menu.js +15 -4
- package/dist/components/hover-menu.js.map +1 -1
- package/dist/components/mini-calendar.d.ts +102 -14
- package/dist/components/mini-calendar.js +256 -70
- package/dist/components/mini-calendar.js.map +1 -1
- package/dist/components/modal.d.ts +9 -3
- package/dist/components/modal.js +34 -45
- package/dist/components/modal.js.map +1 -1
- package/dist/components/month-picker.d.ts +79 -0
- package/dist/components/month-picker.js +342 -0
- package/dist/components/month-picker.js.map +1 -0
- package/dist/components/multi-entity-combobox.d.ts +7 -6
- package/dist/components/multi-entity-combobox.js +112 -72
- package/dist/components/multi-entity-combobox.js.map +1 -1
- package/dist/components/multi-select.d.ts +13 -11
- package/dist/components/multi-select.js +161 -84
- package/dist/components/multi-select.js.map +1 -1
- package/dist/components/number-field.d.ts +92 -0
- package/dist/components/number-field.js +111 -0
- package/dist/components/number-field.js.map +1 -0
- package/dist/components/number-input.d.ts +21 -2
- package/dist/components/number-input.js +2 -1
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/numpad-sheet.d.ts +25 -4
- package/dist/components/numpad-sheet.js +30 -13
- package/dist/components/numpad-sheet.js.map +1 -1
- package/dist/components/page-contents.d.ts +90 -0
- package/dist/components/page-contents.js +170 -0
- package/dist/components/page-contents.js.map +1 -0
- package/dist/components/password-strength.d.ts +106 -0
- package/dist/components/password-strength.js +132 -0
- package/dist/components/password-strength.js.map +1 -0
- package/dist/components/picker-sheet.d.ts +23 -15
- package/dist/components/picker-sheet.js +34 -14
- package/dist/components/picker-sheet.js.map +1 -1
- package/dist/components/popover.d.ts +62 -4
- package/dist/components/popover.js +52 -2
- package/dist/components/popover.js.map +1 -1
- package/dist/components/search-field.d.ts +25 -7
- package/dist/components/search-field.js +7 -5
- package/dist/components/search-field.js.map +1 -1
- package/dist/components/series-chart-labels.d.ts +14 -0
- package/dist/components/series-chart-labels.js +11 -0
- package/dist/components/series-chart-labels.js.map +1 -0
- package/dist/components/series-chart.d.ts +207 -0
- package/dist/components/series-chart.js +223 -0
- package/dist/components/series-chart.js.map +1 -0
- package/dist/components/settings-fields.d.ts +14 -4
- package/dist/components/settings-fields.js +19 -8
- package/dist/components/settings-fields.js.map +1 -1
- package/dist/components/signature-pad.d.ts +119 -0
- package/dist/components/signature-pad.js +405 -0
- package/dist/components/signature-pad.js.map +1 -0
- package/dist/components/slider.d.ts +80 -0
- package/dist/components/slider.js +155 -0
- package/dist/components/slider.js.map +1 -0
- package/dist/components/sparkline.d.ts +93 -0
- package/dist/components/sparkline.js +205 -0
- package/dist/components/sparkline.js.map +1 -0
- package/dist/components/stat-tile.d.ts +151 -0
- package/dist/components/stat-tile.js +236 -0
- package/dist/components/stat-tile.js.map +1 -0
- package/dist/components/swipeable-row.d.ts +25 -2
- package/dist/components/swipeable-row.js +31 -0
- package/dist/components/swipeable-row.js.map +1 -1
- package/dist/components/switch.d.ts +60 -0
- package/dist/components/switch.js +81 -0
- package/dist/components/switch.js.map +1 -0
- package/dist/components/time-input.d.ts +58 -0
- package/dist/components/time-input.js +50 -0
- package/dist/components/time-input.js.map +1 -0
- package/dist/components/toggle-group.d.ts +18 -3
- package/dist/components/toggle-group.js +10 -6
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/toggle-legend.d.ts +115 -0
- package/dist/components/toggle-legend.js +114 -0
- package/dist/components/toggle-legend.js.map +1 -0
- package/dist/components/tooltip.d.ts +40 -11
- package/dist/components/tooltip.js +77 -20
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/treemap.d.ts +132 -0
- package/dist/components/treemap.js +179 -0
- package/dist/components/treemap.js.map +1 -0
- package/dist/components/ui.d.ts +159 -46
- package/dist/components/ui.js +220 -115
- package/dist/components/ui.js.map +1 -1
- package/dist/components/use-mobile-reveal.js +1 -0
- package/dist/components/use-mobile-reveal.js.map +1 -1
- package/dist/components/use-table-state.d.ts +1 -1
- package/dist/components/use-table-state.js +38 -17
- package/dist/components/use-table-state.js.map +1 -1
- package/dist/components/user-avatar.d.ts +5 -3
- package/dist/components/user-avatar.js +4 -2
- package/dist/components/user-avatar.js.map +1 -1
- package/dist/components/wizard-stepper.js +3 -2
- package/dist/components/wizard-stepper.js.map +1 -1
- package/dist/{data-table-filters-aXyf0Xub.d.ts → data-table-filters-DM1auUfH.d.ts} +27 -5
- package/dist/data-table.d.ts +7 -0
- package/dist/data-table.js +12 -0
- package/dist/data-table.js.map +1 -0
- package/dist/feedback/feedback-attachment.js +12 -11
- package/dist/feedback/feedback-attachment.js.map +1 -1
- package/dist/feedback/feedback-dialog.js +2 -1
- package/dist/feedback/feedback-dialog.js.map +1 -1
- package/dist/feedback/feedback-inbox.d.ts +33 -7
- package/dist/feedback/feedback-inbox.js +49 -37
- package/dist/feedback/feedback-inbox.js.map +1 -1
- package/dist/feedback.d.ts +5 -0
- package/dist/feedback.js +5 -0
- package/dist/feedback.js.map +1 -0
- package/dist/hooks/use-anchored-panel.js +1 -0
- package/dist/hooks/use-anchored-panel.js.map +1 -1
- package/dist/hooks/use-anchored-rect.js +1 -0
- package/dist/hooks/use-anchored-rect.js.map +1 -1
- package/dist/hooks/use-announce.d.ts +46 -0
- package/dist/hooks/use-announce.js +37 -0
- package/dist/hooks/use-announce.js.map +1 -0
- package/dist/hooks/use-body-scroll-lock.js +1 -0
- package/dist/hooks/use-body-scroll-lock.js.map +1 -1
- package/dist/hooks/use-close-transition.js +1 -0
- package/dist/hooks/use-close-transition.js.map +1 -1
- package/dist/hooks/use-dismiss.js +1 -0
- package/dist/hooks/use-dismiss.js.map +1 -1
- package/dist/hooks/use-focus-trap.d.ts +16 -0
- package/dist/hooks/use-focus-trap.js +67 -0
- package/dist/hooks/use-focus-trap.js.map +1 -0
- package/dist/hooks/use-media-query.js +1 -0
- package/dist/hooks/use-media-query.js.map +1 -1
- package/dist/hooks/use-overlay-history.js +27 -1
- package/dist/hooks/use-overlay-history.js.map +1 -1
- package/dist/hooks/use-row-swipe.js +1 -0
- package/dist/hooks/use-row-swipe.js.map +1 -1
- package/dist/i18n/defaults.d.ts +31 -0
- package/dist/i18n/defaults.js +63 -0
- package/dist/i18n/defaults.js.map +1 -0
- package/dist/i18n/kit-labels.d.ts +250 -0
- package/dist/i18n/kit-labels.js +184 -0
- package/dist/i18n/kit-labels.js.map +1 -0
- package/dist/index.d.ts +47 -22
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/calc.js +1 -0
- package/dist/lib/calc.js.map +1 -1
- package/dist/lib/cn.js +1 -0
- package/dist/lib/cn.js.map +1 -1
- package/dist/lib/dates.js +1 -0
- package/dist/lib/dates.js.map +1 -1
- package/dist/lib/logger.js +1 -0
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/safe-storage.js +1 -0
- package/dist/lib/safe-storage.js.map +1 -1
- package/dist/search/command-palette.d.ts +11 -1
- package/dist/search/command-palette.js +21 -15
- package/dist/search/command-palette.js.map +1 -1
- package/dist/search.d.ts +2 -0
- package/dist/search.js +3 -0
- package/dist/search.js.map +1 -0
- package/dist/shell/app-shell.d.ts +33 -4
- package/dist/shell/app-shell.js +275 -78
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/option-switcher-menu.js +3 -2
- package/dist/shell/option-switcher-menu.js.map +1 -1
- package/dist/shell/role-switcher.d.ts +6 -0
- package/dist/shell/role-switcher.js +7 -4
- package/dist/shell/role-switcher.js.map +1 -1
- package/dist/shell/top-bar.d.ts +15 -7
- package/dist/shell/top-bar.js +3 -5
- package/dist/shell/top-bar.js.map +1 -1
- package/dist/shell/topbar-action-menu.js +3 -2
- package/dist/shell/topbar-action-menu.js.map +1 -1
- package/dist/shell/topbar-controls.d.ts +5 -2
- package/dist/shell/topbar-controls.js +20 -15
- package/dist/shell/topbar-controls.js.map +1 -1
- package/dist/shell.d.ts +13 -0
- package/dist/shell.js +8 -0
- package/dist/shell.js.map +1 -0
- package/dist/theme/chart-palette.d.ts +19 -12
- package/dist/theme/chart-palette.js +24 -30
- package/dist/theme/chart-palette.js.map +1 -1
- package/dist/theme/color.d.ts +78 -0
- package/dist/theme/color.js +148 -0
- package/dist/theme/color.js.map +1 -0
- package/dist/theme/palette-derive.d.ts +140 -0
- package/dist/theme/palette-derive.js +376 -0
- package/dist/theme/palette-derive.js.map +1 -0
- package/dist/theme/palette-presets.d.ts +14 -1
- package/dist/theme/palette-presets.js +231 -3
- package/dist/theme/palette-presets.js.map +1 -1
- package/dist/theme/palette-store.js +1 -0
- package/dist/theme/palette-store.js.map +1 -1
- package/dist/theme/theme-store.js +1 -0
- package/dist/theme/theme-store.js.map +1 -1
- package/dist/tour/tour.d.ts +2 -1
- package/dist/tour/tour.js +15 -9
- package/dist/tour/tour.js.map +1 -1
- package/dist/tour.d.ts +2 -0
- package/dist/tour.js +3 -0
- package/dist/tour.js.map +1 -0
- package/dist/wizard/stepper-nav.js +10 -8
- package/dist/wizard/stepper-nav.js.map +1 -1
- package/dist/wizard/types.d.ts +18 -2
- package/dist/wizard/types.js +4 -1
- package/dist/wizard/types.js.map +1 -1
- package/dist/wizard/use-wizard.js +12 -4
- package/dist/wizard/use-wizard.js.map +1 -1
- package/dist/wizard/validation.js +1 -0
- package/dist/wizard/validation.js.map +1 -1
- package/dist/wizard/wizard-context.js +1 -0
- package/dist/wizard/wizard-context.js.map +1 -1
- package/dist/wizard/wizard-step.js +1 -0
- package/dist/wizard/wizard-step.js.map +1 -1
- package/dist/wizard/wizard-summary.js +5 -3
- package/dist/wizard/wizard-summary.js.map +1 -1
- package/dist/wizard.d.ts +8 -0
- package/dist/wizard.js +9 -0
- package/dist/wizard.js.map +1 -0
- package/package.json +66 -13
- package/src/chart.ts +22 -0
- package/src/components/account-settings.tsx +275 -0
- package/src/components/alert-banner.tsx +75 -0
- package/src/components/amount-input.tsx +471 -0
- package/src/components/calculator.tsx +252 -0
- package/src/components/chart-zoom.tsx +499 -0
- package/src/components/chart.tsx +363 -0
- package/src/components/checkbox.tsx +222 -0
- package/src/components/chip.tsx +570 -0
- package/src/components/choice-parts.ts +33 -0
- package/src/components/combobox-core.tsx +540 -0
- package/src/components/combobox.tsx +1026 -0
- package/src/components/currency-select.tsx +269 -0
- package/src/components/data-table-filter-popover.tsx +252 -0
- package/src/components/data-table-filters.ts +176 -0
- package/src/components/data-table-labels.ts +190 -0
- package/src/components/data-table-pagination.tsx +126 -0
- package/src/components/data-table-sort.ts +70 -0
- package/src/components/data-table.tsx +1740 -0
- package/src/components/date-picker.tsx +631 -0
- package/src/components/dropdown.tsx +341 -0
- package/src/components/entity-combobox.tsx +215 -0
- package/src/components/facing-pair.ts +86 -0
- package/src/components/field-sync.tsx +472 -0
- package/src/components/file-dropzone.tsx +181 -0
- package/src/components/full-bleed-dialog.tsx +179 -0
- package/src/components/grouped-picker.tsx +157 -0
- package/src/components/hover-menu.tsx +183 -0
- package/src/components/mini-calendar.tsx +554 -0
- package/src/components/modal.tsx +287 -0
- package/src/components/month-picker.tsx +549 -0
- package/src/components/multi-entity-combobox.tsx +226 -0
- package/src/components/multi-select.tsx +303 -0
- package/src/components/number-field.tsx +225 -0
- package/src/components/number-input.tsx +211 -0
- package/src/components/numpad-sheet.tsx +264 -0
- package/src/components/page-contents.tsx +289 -0
- package/src/components/password-strength.tsx +258 -0
- package/src/components/picker-sheet.tsx +251 -0
- package/src/components/popover.tsx +195 -0
- package/src/components/search-field.tsx +146 -0
- package/src/components/series-chart-labels.ts +26 -0
- package/src/components/series-chart.tsx +481 -0
- package/src/components/settings-fields.tsx +68 -0
- package/src/components/signature-pad.tsx +630 -0
- package/src/components/slider.tsx +313 -0
- package/src/components/sparkline.tsx +329 -0
- package/src/components/stat-tile.tsx +448 -0
- package/src/components/swipeable-row.tsx +271 -0
- package/src/components/switch.tsx +170 -0
- package/src/components/time-input.tsx +107 -0
- package/src/components/toggle-group.tsx +153 -0
- package/src/components/toggle-legend.tsx +229 -0
- package/src/components/tooltip.tsx +477 -0
- package/src/components/treemap.tsx +390 -0
- package/src/components/ui.tsx +1284 -0
- package/src/components/use-mobile-reveal.ts +54 -0
- package/src/components/use-table-state.ts +295 -0
- package/src/components/user-avatar.tsx +61 -0
- package/src/components/wizard-stepper.tsx +37 -0
- package/src/data-table.ts +19 -0
- package/src/feedback/feedback-attachment.tsx +246 -0
- package/src/feedback/feedback-dialog.tsx +209 -0
- package/src/feedback/feedback-inbox.tsx +615 -0
- package/src/feedback.ts +8 -0
- package/src/hooks/use-anchored-panel.ts +169 -0
- package/src/hooks/use-anchored-rect.ts +78 -0
- package/src/hooks/use-announce.ts +85 -0
- package/src/hooks/use-body-scroll-lock.ts +94 -0
- package/src/hooks/use-close-transition.ts +99 -0
- package/src/hooks/use-dismiss.ts +77 -0
- package/src/hooks/use-focus-trap.ts +160 -0
- package/src/hooks/use-media-query.ts +23 -0
- package/src/hooks/use-overlay-history.ts +345 -0
- package/src/hooks/use-row-swipe.ts +212 -0
- package/src/i18n/defaults.ts +68 -0
- package/src/i18n/kit-labels.tsx +447 -0
- package/src/index.ts +169 -0
- package/src/lib/calc.ts +309 -0
- package/src/lib/cn.ts +5 -0
- package/src/lib/dates.ts +187 -0
- package/src/lib/logger.ts +73 -0
- package/src/lib/safe-storage.ts +37 -0
- package/src/search/command-palette.tsx +342 -0
- package/src/search.ts +3 -0
- package/src/shell/app-shell.tsx +772 -0
- package/src/shell/option-switcher-menu.tsx +72 -0
- package/src/shell/role-switcher.tsx +46 -0
- package/src/shell/top-bar.tsx +39 -0
- package/src/shell/topbar-action-menu.tsx +91 -0
- package/src/shell/topbar-controls.tsx +229 -0
- package/src/shell.ts +10 -0
- package/src/theme/chart-palette.ts +145 -0
- package/src/theme/color.ts +266 -0
- package/src/theme/palette-derive.ts +681 -0
- package/src/theme/palette-presets.ts +760 -0
- package/src/theme/palette-store.ts +94 -0
- package/src/theme/theme-store.ts +145 -0
- package/src/tour/tour.tsx +586 -0
- package/src/tour.ts +3 -0
- package/src/vite-env.d.ts +26 -0
- package/src/wizard/stepper-nav.tsx +221 -0
- package/src/wizard/types.ts +178 -0
- package/src/wizard/use-wizard.ts +345 -0
- package/src/wizard/validation.ts +37 -0
- package/src/wizard/wizard-context.tsx +29 -0
- package/src/wizard/wizard-step.tsx +14 -0
- package/src/wizard/wizard-summary.tsx +69 -0
- package/src/wizard.ts +14 -0
- package/tokens.css +222 -20
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useSyncExternalStore } from "react";
|
|
4
|
+
import { Treemap as RechartsTreemap } from "recharts";
|
|
5
|
+
import { ChartContainer, ChartTooltip, ChartTooltipContent } from "./chart.js";
|
|
6
|
+
import { PALETTE_HEX, textOn } from "../theme/chart-palette.js";
|
|
7
|
+
import { parseHex } from "../theme/color.js";
|
|
8
|
+
const CHART_TOKENS = 9;
|
|
9
|
+
function readChartTokens() {
|
|
10
|
+
const root = document.documentElement;
|
|
11
|
+
const fallback = root.classList.contains("dark") ? PALETTE_HEX.dark : PALETTE_HEX.light;
|
|
12
|
+
const style = getComputedStyle(root);
|
|
13
|
+
return Array.from({ length: CHART_TOKENS }, (_, i) => {
|
|
14
|
+
const v = style.getPropertyValue(`--chart-${i + 1}`).trim();
|
|
15
|
+
return parseHex(v) ? v : fallback[i];
|
|
16
|
+
}).join(",");
|
|
17
|
+
}
|
|
18
|
+
function subscribeChartTokens(onChange) {
|
|
19
|
+
const observer = new MutationObserver(onChange);
|
|
20
|
+
observer.observe(document.documentElement, {
|
|
21
|
+
attributes: true,
|
|
22
|
+
attributeFilter: ["class", "style", "data-theme"]
|
|
23
|
+
});
|
|
24
|
+
return () => observer.disconnect();
|
|
25
|
+
}
|
|
26
|
+
const SERVER_TOKENS = PALETTE_HEX.light.join(",");
|
|
27
|
+
function useChartTokenHex() {
|
|
28
|
+
const joined = useSyncExternalStore(subscribeChartTokens, readChartTokens, () => SERVER_TOKENS);
|
|
29
|
+
return joined.split(",");
|
|
30
|
+
}
|
|
31
|
+
const LABEL_FONT_SIZE = 12;
|
|
32
|
+
const NOTE_FONT_SIZE = 11;
|
|
33
|
+
function fitLabel(name, boxWidth, fontSize) {
|
|
34
|
+
const max = Math.floor((boxWidth - 12) / (fontSize * 0.58));
|
|
35
|
+
if (max < 3) return null;
|
|
36
|
+
return name.length <= max ? name : `${name.slice(0, max - 1).trimEnd()}\u2026`;
|
|
37
|
+
}
|
|
38
|
+
function TreemapCell({
|
|
39
|
+
depth,
|
|
40
|
+
x = 0,
|
|
41
|
+
y = 0,
|
|
42
|
+
width = 0,
|
|
43
|
+
height = 0,
|
|
44
|
+
name,
|
|
45
|
+
fill,
|
|
46
|
+
id,
|
|
47
|
+
onNodeClick,
|
|
48
|
+
redactNames,
|
|
49
|
+
nodeNote
|
|
50
|
+
}) {
|
|
51
|
+
if (!depth) return null;
|
|
52
|
+
const px = Math.round(x);
|
|
53
|
+
const py = Math.round(y);
|
|
54
|
+
const pw = Math.round(width);
|
|
55
|
+
const ph = Math.round(height);
|
|
56
|
+
const tileFill = fill ?? "var(--chart-1)";
|
|
57
|
+
const ink = textOn(tileFill);
|
|
58
|
+
const label = pw >= 44 && ph >= 20 ? fitLabel(name ?? "", pw, LABEL_FONT_SIZE) : null;
|
|
59
|
+
const rawNote = id != null && nodeNote ? nodeNote(id) : void 0;
|
|
60
|
+
const note = label != null && rawNote != null && ph >= 36 ? fitLabel(rawNote, pw, NOTE_FONT_SIZE) : null;
|
|
61
|
+
const activate = id != null && onNodeClick ? () => onNodeClick(id, name ?? "") : void 0;
|
|
62
|
+
const onKeyDown = activate ? (e) => {
|
|
63
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
64
|
+
e.preventDefault();
|
|
65
|
+
activate();
|
|
66
|
+
} : void 0;
|
|
67
|
+
return /* @__PURE__ */ jsxs(
|
|
68
|
+
"g",
|
|
69
|
+
{
|
|
70
|
+
onClick: activate,
|
|
71
|
+
onKeyDown,
|
|
72
|
+
role: activate ? "button" : void 0,
|
|
73
|
+
tabIndex: activate ? 0 : void 0,
|
|
74
|
+
"aria-label": activate ? name : void 0,
|
|
75
|
+
"data-private": activate && redactNames ? "" : void 0,
|
|
76
|
+
cursor: activate ? "pointer" : void 0,
|
|
77
|
+
className: activate ? "outline-none [&:focus-visible>rect]:stroke-[var(--brand)]" : void 0,
|
|
78
|
+
children: [
|
|
79
|
+
/* @__PURE__ */ jsx(
|
|
80
|
+
"rect",
|
|
81
|
+
{
|
|
82
|
+
x: px,
|
|
83
|
+
y: py,
|
|
84
|
+
width: pw,
|
|
85
|
+
height: ph,
|
|
86
|
+
fill: tileFill,
|
|
87
|
+
stroke: "var(--bg-surface)",
|
|
88
|
+
strokeWidth: 2,
|
|
89
|
+
rx: 3
|
|
90
|
+
}
|
|
91
|
+
),
|
|
92
|
+
label != null && /* @__PURE__ */ jsx(
|
|
93
|
+
"text",
|
|
94
|
+
{
|
|
95
|
+
"data-private": redactNames ? "" : void 0,
|
|
96
|
+
x: px + 6,
|
|
97
|
+
y: py + 15,
|
|
98
|
+
fill: ink,
|
|
99
|
+
stroke: tileFill,
|
|
100
|
+
strokeWidth: 2.5,
|
|
101
|
+
strokeLinejoin: "round",
|
|
102
|
+
paintOrder: "stroke",
|
|
103
|
+
fontSize: LABEL_FONT_SIZE,
|
|
104
|
+
fontWeight: 600,
|
|
105
|
+
style: { pointerEvents: "none" },
|
|
106
|
+
children: label
|
|
107
|
+
}
|
|
108
|
+
),
|
|
109
|
+
note != null && /* @__PURE__ */ jsx(
|
|
110
|
+
"text",
|
|
111
|
+
{
|
|
112
|
+
x: px + 6,
|
|
113
|
+
y: py + 29,
|
|
114
|
+
fill: ink,
|
|
115
|
+
fillOpacity: 0.85,
|
|
116
|
+
stroke: tileFill,
|
|
117
|
+
strokeWidth: 2.5,
|
|
118
|
+
strokeLinejoin: "round",
|
|
119
|
+
paintOrder: "stroke",
|
|
120
|
+
fontSize: NOTE_FONT_SIZE,
|
|
121
|
+
fontWeight: 500,
|
|
122
|
+
style: { pointerEvents: "none" },
|
|
123
|
+
children: note
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
const TREEMAP_CONFIG = {};
|
|
131
|
+
function RedactedTooltipContent(props) {
|
|
132
|
+
return /* @__PURE__ */ jsx("div", { "data-private": true, children: /* @__PURE__ */ jsx(ChartTooltipContent, { ...props }) });
|
|
133
|
+
}
|
|
134
|
+
const DEFAULT_NUMBER = new Intl.NumberFormat();
|
|
135
|
+
function Treemap({
|
|
136
|
+
data,
|
|
137
|
+
valueFormatter,
|
|
138
|
+
height = 320,
|
|
139
|
+
onNodeClick,
|
|
140
|
+
redactNames = false,
|
|
141
|
+
nodeNote,
|
|
142
|
+
maxTiles,
|
|
143
|
+
colors,
|
|
144
|
+
style,
|
|
145
|
+
...rest
|
|
146
|
+
}) {
|
|
147
|
+
const tokenHex = useChartTokenHex();
|
|
148
|
+
const ramp = colors && colors.length > 0 ? colors : tokenHex;
|
|
149
|
+
const nodes = data.map((d, idx) => ({
|
|
150
|
+
id: d.id,
|
|
151
|
+
name: d.name,
|
|
152
|
+
size: d.value,
|
|
153
|
+
fill: d.fill ?? ramp[idx % ramp.length]
|
|
154
|
+
})).filter((n) => Number.isFinite(n.size) && n.size > 0).slice(0, maxTiles ?? void 0);
|
|
155
|
+
if (nodes.length === 0) return null;
|
|
156
|
+
const total = nodes.reduce((acc, n) => acc + n.size, 0);
|
|
157
|
+
const format = (v) => {
|
|
158
|
+
const share = total > 0 ? v / total : void 0;
|
|
159
|
+
return valueFormatter ? valueFormatter(v, share) : DEFAULT_NUMBER.format(v);
|
|
160
|
+
};
|
|
161
|
+
const Content = redactNames ? RedactedTooltipContent : ChartTooltipContent;
|
|
162
|
+
return /* @__PURE__ */ jsx(ChartContainer, { ...rest, config: TREEMAP_CONFIG, style: { ...style, height }, children: /* @__PURE__ */ jsx(
|
|
163
|
+
RechartsTreemap,
|
|
164
|
+
{
|
|
165
|
+
data: nodes,
|
|
166
|
+
dataKey: "size",
|
|
167
|
+
fill: "var(--chart-1)",
|
|
168
|
+
isAnimationActive: false,
|
|
169
|
+
content: /* @__PURE__ */ jsx(TreemapCell, { onNodeClick, redactNames, nodeNote }),
|
|
170
|
+
children: /* @__PURE__ */ jsx(ChartTooltip, { content: /* @__PURE__ */ jsx(Content, { hideLabel: true, valueFormatter: format }) })
|
|
171
|
+
}
|
|
172
|
+
) });
|
|
173
|
+
}
|
|
174
|
+
export {
|
|
175
|
+
Treemap,
|
|
176
|
+
TreemapCell,
|
|
177
|
+
fitLabel
|
|
178
|
+
};
|
|
179
|
+
//# sourceMappingURL=treemap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/treemap.tsx"],"sourcesContent":["// A tile chart (\"treemap\"): one rectangle per node, its AREA the node's share of what\n// is drawn. Lifted out of keksdose's reports, where five cards drew it (spending by\n// group and by category, net worth by account and by type, allocation, payees, food\n// groups) — every domain word is gone and every string it could print is a prop.\n//\n// It uses the kit's own chart shell (`ChartContainer`, `ChartTooltipContent`) and the\n// kit's own ink rule (`textOn`), so it re-skins with the palette like every other\n// chart. The one thing it cannot take from a CSS var is the tile colour: the label ink\n// is MEASURED against the fill, and a `var(--chart-3)` string measures as nothing — see\n// `useChartTokenHex` below for how the concrete hex is found instead.\nimport { useSyncExternalStore } from \"react\";\nimport type { ComponentProps, ComponentPropsWithoutRef, KeyboardEvent, ReactNode } from \"react\";\nimport { Treemap as RechartsTreemap } from \"recharts\";\nimport { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from \"./chart\";\nimport { PALETTE_HEX, textOn } from \"../theme/chart-palette\";\nimport { parseHex } from \"../theme/color\";\n\n/**\n * One tile.\n *\n * `value`, not keksdose's `total`: a treemap's areas are not always money (keksdose\n * already sizes one by transaction COUNT), and `value` is the word every other chart\n * in recharts uses for the number a mark is sized by. Migrating is a rename at the\n * mapping step — `{ id, name, value: row.total }`.\n */\nexport interface TreemapNode {\n /** Stable key, handed back by {@link TreemapProps.onNodeClick} and\n * {@link TreemapProps.nodeNote}. Names can collide; ids must not. */\n id: string;\n name: string;\n /** The tile's area. A node whose value is not a positive finite number is NOT\n * drawn — an area cannot be negative, and recharts lays a negative size out as\n * nonsense rather than refusing it. Saying so is the caller's job: a group that\n * vanishes silently between two views of the same data is a bug report. */\n value: number;\n /** Overrides the by-index palette colour for this tile — e.g. to paint every child\n * in its parent group's colour, so the groups stay legible in one picture. A\n * concrete `#rrggbb` keeps the label ink measured; anything else (a CSS var) gets\n * `currentColor` ink, because there is nothing to measure. */\n fill?: string;\n}\n\n/**\n * Everything a `<div>` takes reaches the chart's root (`ChartContainer`), so a test id\n * or a `data-tour` anchor can find it. `children` is the chart's own.\n */\nexport interface TreemapProps extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n data: TreemapNode[];\n /**\n * Formats a node's value in the tooltip. Handed the node's SHARE of what is drawn\n * (0–1) as a second argument, because a tile is read for how big it is relative to\n * the rest, and \"€412.30\" alone makes the reader estimate that ratio off the areas.\n * The share is `undefined` when the drawn total is not positive.\n *\n * Default: the bare number through `Intl.NumberFormat` in the runtime's locale. A\n * host that has a currency or a locale passes this — the kit knows neither.\n */\n valueFormatter?: (value: number, share: number | undefined) => ReactNode;\n /** Chart height in px. Default 320. */\n height?: number;\n /** A tile was activated — by click, or by Enter/Space on a focused tile. Setting\n * this makes every tile a focusable `role=\"button\"` named by its node name. */\n onNodeClick?: (id: string, name: string) => void;\n /**\n * The node names are the user's own data (payees, not categories): the tile labels\n * and the whole tooltip bubble get `data-private`, the same attribute the kit's\n * `Tooltip redact` and `ChartTooltipContent`'s value cell carry, so the host's one\n * privacy rule reaches them. Off by default — a category name is a label of the app.\n */\n redactNames?: boolean;\n /**\n * A second, smaller line under a tile's label — one extra figure per node that the\n * name cannot carry (a year-on-year `+12%`, `new`). Return `undefined` for a node\n * with nothing to add. Never tagged `data-private`: a ratio reveals no amount.\n *\n * ON THE TILE rather than in the tooltip, for two reasons. The tooltip identifies a\n * node by name or by raw value, so a per-node note would be keyed on an amount (two\n * nodes of equal size would swap notes). And a tooltip is a pointer affordance: a\n * phone never hovers, so a figure that only exists on hover does not exist there.\n */\n nodeNote?: (id: string) => string | undefined;\n /**\n * Draw at most this many tiles (the first N drawable nodes, in the order given —\n * sort first). Unbounded by default. A tile under ~44x20px has no room for a label,\n * and a map made of unlabelled rectangles answers nothing; around 24 is where the\n * smallest tile of a skewed series reaches that floor on a phone-width card. The\n * caller owns the \"N more not shown\" line, since it knows how many it passed.\n */\n maxTiles?: number;\n /**\n * The categorical ramp tiles take by index, as concrete hex. Default: the page's\n * live `--chart-1…9` tokens (see `useChartTokenHex`). Pass one when the host\n * already holds the active palette — e.g. `createPaletteStore`'s `useChartHex()` —\n * so a caller colouring a legend beside the map is guaranteed the same list.\n */\n colors?: readonly string[];\n}\n\n// ── The categorical ramp, as concrete hex ──────────────────────────────────\n//\n// The label ink is measured against the tile's fill, so the fill has to be a colour\n// and not a reference to one. keksdose read it from its palette STORE, which is an\n// app object the kit cannot import. What the kit does own is the token layer: whether\n// a palette arrives through `applyTokenSet` (inline properties on <html>) or through\n// tokens.css (`:root` / `.dark`), the resolved `--chart-N` on <html> is the colour\n// every other chart on the page is being painted with. So that is what is read, and\n// re-read whenever <html>'s class or style changes — which is exactly when a theme\n// toggle or a palette switch lands.\n\nconst CHART_TOKENS = 9;\n\n/** The resolved ramp, joined into ONE string so `useSyncExternalStore` compares a\n * primitive: a fresh array per read would never be `Object.is`-equal to the last and\n * would re-render forever. */\nfunction readChartTokens(): string {\n const root = document.documentElement;\n const fallback = root.classList.contains(\"dark\") ? PALETTE_HEX.dark : PALETTE_HEX.light;\n const style = getComputedStyle(root);\n return Array.from({ length: CHART_TOKENS }, (_, i) => {\n const v = style.getPropertyValue(`--chart-${i + 1}`).trim();\n // An unresolved token (a host that never loaded tokens.css, a test DOM) falls\n // back to the built-in ramp for the current theme rather than to no colour.\n return parseHex(v) ? v : fallback[i];\n }).join(\",\");\n}\n\nfunction subscribeChartTokens(onChange: () => void): () => void {\n const observer = new MutationObserver(onChange);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"class\", \"style\", \"data-theme\"],\n });\n return () => observer.disconnect();\n}\n\nconst SERVER_TOKENS = PALETTE_HEX.light.join(\",\");\n\nfunction useChartTokenHex(): string[] {\n const joined = useSyncExternalStore(subscribeChartTokens, readChartTokens, () => SERVER_TOKENS);\n return joined.split(\",\");\n}\n\n// ── Tile ──────────────────────────────────────────────────────────────────\n\nconst LABEL_FONT_SIZE = 12;\n/** The optional second line, a step down from the name so the tile reads name-first. */\nconst NOTE_FONT_SIZE = 11;\n\n/**\n * Fit a label to its tile: the text itself when it fits, an ellipsised head when it\n * does not, `null` when not even three characters would. Without this a long name ran\n * on across its neighbours and collided with THEIR labels. 0.58em is the average\n * advance of a UI sans at this weight; it only needs to be close, since the tile keeps\n * 6px of padding either side. The full name stays in the tooltip.\n */\nexport function fitLabel(name: string, boxWidth: number, fontSize: number): string | null {\n const max = Math.floor((boxWidth - 12) / (fontSize * 0.58));\n if (max < 3) return null;\n return name.length <= max ? name : `${name.slice(0, max - 1).trimEnd()}…`;\n}\n\n/** What recharts hands a `content` element for each node, plus the three options the\n * chart forwards. Everything optional because recharts clones the element it was\n * given and fills the geometry in — the element as written carries only the options. */\nexport interface TreemapCellProps {\n depth?: number;\n x?: number;\n y?: number;\n width?: number;\n height?: number;\n name?: string;\n fill?: string;\n id?: string;\n onNodeClick?: (id: string, name: string) => void;\n redactNames?: boolean;\n nodeNote?: (id: string) => string | undefined;\n}\n\n/**\n * One tile. Exported so a caller drawing its own recharts `<Treemap>` gets the same\n * label rules.\n *\n * recharts' default cell inherits the chart-level `stroke`, an SVG presentation\n * attribute that lands on the `<svg>` root and cascades into the label — so the glyphs\n * were painted with a 1px outline ON TOP of themselves, which reads as blur. This cell\n * draws its own label with a halo in the TILE colour painted BEHIND the fill\n * (`paint-order: stroke`), which defeats any inherited stroke and keeps the text solid\n * where it overlaps a lighter or darker part of its own tile.\n */\nexport function TreemapCell({\n depth,\n x = 0,\n y = 0,\n width = 0,\n height = 0,\n name,\n fill,\n id,\n onNodeClick,\n redactNames,\n nodeNote,\n}: TreemapCellProps) {\n // Depth 0 is recharts' synthetic root, which spans the whole chart.\n if (!depth) return null;\n // Whole pixels: a rect on a half pixel renders its edge across two half-lit\n // columns, and the text baseline inherits that blur.\n const px = Math.round(x);\n const py = Math.round(y);\n const pw = Math.round(width);\n const ph = Math.round(height);\n const tileFill = fill ?? \"var(--chart-1)\";\n const ink = textOn(tileFill);\n // Below ~44x20 there is no room for a readable word, so no label at all rather than\n // a one-letter stub bleeding over the tile edge.\n const label = pw >= 44 && ph >= 20 ? fitLabel(name ?? \"\", pw, LABEL_FONT_SIZE) : null;\n // Only under a label that is itself drawn — a stray \"+12%\" on an unnamed rectangle\n // names nothing — and only where a second baseline fits: the note's baseline sits\n // 29px down and an 11px glyph hangs ~2px below it, so 36 leaves the same ~5px under\n // the note that the label keeps under itself at the 20px floor.\n const rawNote = id != null && nodeNote ? nodeNote(id) : undefined;\n const note =\n label != null && rawNote != null && ph >= 36 ? fitLabel(rawNote, pw, NOTE_FONT_SIZE) : null;\n const activate = id != null && onNodeClick ? () => onNodeClick(id, name ?? \"\") : undefined;\n const onKeyDown = activate\n ? (e: KeyboardEvent<SVGGElement>) => {\n if (e.key !== \"Enter\" && e.key !== \" \") return;\n e.preventDefault();\n activate();\n }\n : undefined;\n return (\n <g\n onClick={activate}\n onKeyDown={onKeyDown}\n // A clickable tile is a button to a keyboard and a screen reader too; the\n // drilldown it opens was mouse-only in the app this came from. Named by the node\n // name, which is also why the group carries `data-private` under `redactNames`.\n role={activate ? \"button\" : undefined}\n tabIndex={activate ? 0 : undefined}\n aria-label={activate ? name : undefined}\n data-private={activate && redactNames ? \"\" : undefined}\n cursor={activate ? \"pointer\" : undefined}\n // CSS beats a presentation attribute, so this recolours the rect's own stroke\n // for as long as the tile has keyboard focus.\n className={activate ? \"outline-none [&:focus-visible>rect]:stroke-[var(--brand)]\" : undefined}\n >\n {/* The 2px gutter is the card showing through, not a white grid — on a dark\n surface white lines glare harder than the tiles. */}\n <rect\n x={px}\n y={py}\n width={pw}\n height={ph}\n fill={tileFill}\n stroke=\"var(--bg-surface)\"\n strokeWidth={2}\n rx={3}\n />\n {label != null && (\n <text\n // A plain attribute, so the host's `[data-private]` rule reaches an SVG\n // <text> exactly as it reaches a table cell.\n data-private={redactNames ? \"\" : undefined}\n x={px + 6}\n y={py + 15}\n fill={ink}\n stroke={tileFill}\n strokeWidth={2.5}\n strokeLinejoin=\"round\"\n paintOrder=\"stroke\"\n fontSize={LABEL_FONT_SIZE}\n fontWeight={600}\n style={{ pointerEvents: \"none\" }}\n >\n {label}\n </text>\n )}\n {note != null && (\n <text\n x={px + 6}\n y={py + 29}\n fill={ink}\n fillOpacity={0.85}\n stroke={tileFill}\n strokeWidth={2.5}\n strokeLinejoin=\"round\"\n paintOrder=\"stroke\"\n fontSize={NOTE_FONT_SIZE}\n fontWeight={500}\n style={{ pointerEvents: \"none\" }}\n >\n {note}\n </text>\n )}\n </g>\n );\n}\n\n// ── Chart ─────────────────────────────────────────────────────────────────\n\n// Nodes carry their own `fill`, and `ChartTooltipContent` falls back to it for the\n// swatch, so no series config is needed.\nconst TREEMAP_CONFIG: ChartConfig = {};\n\n/**\n * The shared tooltip with the whole bubble tagged `data-private`, for names that are\n * the user's own data.\n *\n * The bubble, not just the name: `ChartTooltipContent` prints the name either from\n * `item.name` or from a config `label`, and only the config route could be tagged — but\n * a config entry keyed by a payee name is not a CSS identifier, and `ChartStyle` would\n * (rightly) refuse it. The bubble holds the name and the value, and the value cell is\n * already `data-private`, so tagging the wrapper hides nothing extra.\n *\n * recharts CLONES the `content` element to inject `active`/`payload`, so this has to BE\n * that element — a `<div>` wrapped around `<ChartTooltipContent />` in the JSX would\n * take those props itself and hand the tooltip nothing.\n */\nfunction RedactedTooltipContent(props: ComponentProps<typeof ChartTooltipContent>) {\n return (\n <div data-private>\n <ChartTooltipContent {...props} />\n </div>\n );\n}\n\nconst DEFAULT_NUMBER = new Intl.NumberFormat();\n\n/**\n * Tile chart: one rectangle per {@link TreemapNode}, sized by `value`, coloured by\n * index from the chart ramp (or by the node's own `fill`), labelled in whichever of the\n * kit's two inks measures better against it. Hover shows the name and the formatted\n * value; `onNodeClick` turns the tiles into buttons.\n *\n * Renders nothing when no node has a positive value — an empty box is a hole in the\n * card, and the caller's own sentence is the better answer there.\n *\n * Needs `recharts`, like everything behind `@eifi1/ui-kit/chart`.\n */\nexport function Treemap({\n data,\n valueFormatter,\n height = 320,\n onNodeClick,\n redactNames = false,\n nodeNote,\n maxTiles,\n colors,\n style,\n ...rest\n}: TreemapProps) {\n const tokenHex = useChartTokenHex();\n const ramp = colors && colors.length > 0 ? colors : tokenHex;\n // Coloured by index into `data` AS GIVEN, before anything is dropped: a caller\n // painting a legend or a second map from the same list must land on the same hue\n // for the same row, and filtering first would shift every colour after a gap.\n const nodes = data\n .map((d, idx) => ({\n id: d.id,\n name: d.name,\n size: d.value,\n fill: d.fill ?? ramp[idx % ramp.length],\n }))\n .filter((n) => Number.isFinite(n.size) && n.size > 0)\n .slice(0, maxTiles ?? undefined);\n if (nodes.length === 0) return null;\n\n // The share is of what is DRAWN, so it always reads as \"of this picture\".\n const total = nodes.reduce((acc, n) => acc + n.size, 0);\n const format = (v: number): ReactNode => {\n const share = total > 0 ? v / total : undefined;\n return valueFormatter ? valueFormatter(v, share) : DEFAULT_NUMBER.format(v);\n };\n const Content = redactNames ? RedactedTooltipContent : ChartTooltipContent;\n return (\n <ChartContainer {...rest} config={TREEMAP_CONFIG} style={{ ...style, height }}>\n {/* No chart-level `stroke`: recharts spreads it onto the <svg> root, from where\n it inherits into every label (see TreemapCell). Each cell strokes its own rect. */}\n <RechartsTreemap\n data={nodes}\n dataKey=\"size\"\n fill=\"var(--chart-1)\"\n isAnimationActive={false}\n content={<TreemapCell onNodeClick={onNodeClick} redactNames={redactNames} nodeNote={nodeNote} />}\n >\n <ChartTooltip content={<Content hideLabel valueFormatter={format} />} />\n </RechartsTreemap>\n </ChartContainer>\n );\n}\n"],"mappings":";AAuOI,SAiBE,KAjBF;AA7NJ,SAAS,4BAA4B;AAErC,SAAS,WAAW,uBAAuB;AAC3C,SAAS,gBAAgB,cAAc,2BAA6C;AACpF,SAAS,aAAa,cAAc;AACpC,SAAS,gBAAgB;AA8FzB,MAAM,eAAe;AAKrB,SAAS,kBAA0B;AACjC,QAAM,OAAO,SAAS;AACtB,QAAM,WAAW,KAAK,UAAU,SAAS,MAAM,IAAI,YAAY,OAAO,YAAY;AAClF,QAAM,QAAQ,iBAAiB,IAAI;AACnC,SAAO,MAAM,KAAK,EAAE,QAAQ,aAAa,GAAG,CAAC,GAAG,MAAM;AACpD,UAAM,IAAI,MAAM,iBAAiB,WAAW,IAAI,CAAC,EAAE,EAAE,KAAK;AAG1D,WAAO,SAAS,CAAC,IAAI,IAAI,SAAS,CAAC;AAAA,EACrC,CAAC,EAAE,KAAK,GAAG;AACb;AAEA,SAAS,qBAAqB,UAAkC;AAC9D,QAAM,WAAW,IAAI,iBAAiB,QAAQ;AAC9C,WAAS,QAAQ,SAAS,iBAAiB;AAAA,IACzC,YAAY;AAAA,IACZ,iBAAiB,CAAC,SAAS,SAAS,YAAY;AAAA,EAClD,CAAC;AACD,SAAO,MAAM,SAAS,WAAW;AACnC;AAEA,MAAM,gBAAgB,YAAY,MAAM,KAAK,GAAG;AAEhD,SAAS,mBAA6B;AACpC,QAAM,SAAS,qBAAqB,sBAAsB,iBAAiB,MAAM,aAAa;AAC9F,SAAO,OAAO,MAAM,GAAG;AACzB;AAIA,MAAM,kBAAkB;AAExB,MAAM,iBAAiB;AAShB,SAAS,SAAS,MAAc,UAAkB,UAAiC;AACxF,QAAM,MAAM,KAAK,OAAO,WAAW,OAAO,WAAW,KAAK;AAC1D,MAAI,MAAM,EAAG,QAAO;AACpB,SAAO,KAAK,UAAU,MAAM,OAAO,GAAG,KAAK,MAAM,GAAG,MAAM,CAAC,EAAE,QAAQ,CAAC;AACxE;AA8BO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,QAAQ;AAAA,EACR,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AAEnB,MAAI,CAAC,MAAO,QAAO;AAGnB,QAAM,KAAK,KAAK,MAAM,CAAC;AACvB,QAAM,KAAK,KAAK,MAAM,CAAC;AACvB,QAAM,KAAK,KAAK,MAAM,KAAK;AAC3B,QAAM,KAAK,KAAK,MAAM,MAAM;AAC5B,QAAM,WAAW,QAAQ;AACzB,QAAM,MAAM,OAAO,QAAQ;AAG3B,QAAM,QAAQ,MAAM,MAAM,MAAM,KAAK,SAAS,QAAQ,IAAI,IAAI,eAAe,IAAI;AAKjF,QAAM,UAAU,MAAM,QAAQ,WAAW,SAAS,EAAE,IAAI;AACxD,QAAM,OACJ,SAAS,QAAQ,WAAW,QAAQ,MAAM,KAAK,SAAS,SAAS,IAAI,cAAc,IAAI;AACzF,QAAM,WAAW,MAAM,QAAQ,cAAc,MAAM,YAAY,IAAI,QAAQ,EAAE,IAAI;AACjF,QAAM,YAAY,WACd,CAAC,MAAkC;AACjC,QAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,IAAK;AACxC,MAAE,eAAe;AACjB,aAAS;AAAA,EACX,IACA;AACJ,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAS;AAAA,MACT;AAAA,MAIA,MAAM,WAAW,WAAW;AAAA,MAC5B,UAAU,WAAW,IAAI;AAAA,MACzB,cAAY,WAAW,OAAO;AAAA,MAC9B,gBAAc,YAAY,cAAc,KAAK;AAAA,MAC7C,QAAQ,WAAW,YAAY;AAAA,MAG/B,WAAW,WAAW,8DAA8D;AAAA,MAIpF;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,GAAG;AAAA,YACH,GAAG;AAAA,YACH,OAAO;AAAA,YACP,QAAQ;AAAA,YACR,MAAM;AAAA,YACN,QAAO;AAAA,YACP,aAAa;AAAA,YACb,IAAI;AAAA;AAAA,QACN;AAAA,QACC,SAAS,QACR;AAAA,UAAC;AAAA;AAAA,YAGC,gBAAc,cAAc,KAAK;AAAA,YACjC,GAAG,KAAK;AAAA,YACR,GAAG,KAAK;AAAA,YACR,MAAM;AAAA,YACN,QAAQ;AAAA,YACR,aAAa;AAAA,YACb,gBAAe;AAAA,YACf,YAAW;AAAA,YACX,UAAU;AAAA,YACV,YAAY;AAAA,YACZ,OAAO,EAAE,eAAe,OAAO;AAAA,YAE9B;AAAA;AAAA,QACH;AAAA,QAED,QAAQ,QACP;AAAA,UAAC;AAAA;AAAA,YACC,GAAG,KAAK;AAAA,YACR,GAAG,KAAK;AAAA,YACR,MAAM;AAAA,YACN,aAAa;AAAA,YACb,QAAQ;AAAA,YACR,aAAa;AAAA,YACb,gBAAe;AAAA,YACf,YAAW;AAAA,YACX,UAAU;AAAA,YACV,YAAY;AAAA,YACZ,OAAO,EAAE,eAAe,OAAO;AAAA,YAE9B;AAAA;AAAA,QACH;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAMA,MAAM,iBAA8B,CAAC;AAgBrC,SAAS,uBAAuB,OAAmD;AACjF,SACE,oBAAC,SAAI,gBAAY,MACf,8BAAC,uBAAqB,GAAG,OAAO,GAClC;AAEJ;AAEA,MAAM,iBAAiB,IAAI,KAAK,aAAa;AAatC,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAiB;AACf,QAAM,WAAW,iBAAiB;AAClC,QAAM,OAAO,UAAU,OAAO,SAAS,IAAI,SAAS;AAIpD,QAAM,QAAQ,KACX,IAAI,CAAC,GAAG,SAAS;AAAA,IAChB,IAAI,EAAE;AAAA,IACN,MAAM,EAAE;AAAA,IACR,MAAM,EAAE;AAAA,IACR,MAAM,EAAE,QAAQ,KAAK,MAAM,KAAK,MAAM;AAAA,EACxC,EAAE,EACD,OAAO,CAAC,MAAM,OAAO,SAAS,EAAE,IAAI,KAAK,EAAE,OAAO,CAAC,EACnD,MAAM,GAAG,YAAY,MAAS;AACjC,MAAI,MAAM,WAAW,EAAG,QAAO;AAG/B,QAAM,QAAQ,MAAM,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,MAAM,CAAC;AACtD,QAAM,SAAS,CAAC,MAAyB;AACvC,UAAM,QAAQ,QAAQ,IAAI,IAAI,QAAQ;AACtC,WAAO,iBAAiB,eAAe,GAAG,KAAK,IAAI,eAAe,OAAO,CAAC;AAAA,EAC5E;AACA,QAAM,UAAU,cAAc,yBAAyB;AACvD,SACE,oBAAC,kBAAgB,GAAG,MAAM,QAAQ,gBAAgB,OAAO,EAAE,GAAG,OAAO,OAAO,GAG1E;AAAA,IAAC;AAAA;AAAA,MACC,MAAM;AAAA,MACN,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,mBAAmB;AAAA,MACnB,SAAS,oBAAC,eAAY,aAA0B,aAA0B,UAAoB;AAAA,MAE9F,8BAAC,gBAAa,SAAS,oBAAC,WAAQ,WAAS,MAAC,gBAAgB,QAAQ,GAAI;AAAA;AAAA,EACxE,GACF;AAEJ;","names":[]}
|
package/dist/components/ui.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { SelectHTMLAttributes, ReactNode, InputHTMLAttributes, ButtonHTMLAttributes, ComponentPropsWithoutRef, TextareaHTMLAttributes } from 'react';
|
|
3
3
|
|
|
4
4
|
type ButtonVariant = "primary" | "secondary" | "ghost" | "danger" | "brand";
|
|
5
5
|
/**
|
|
@@ -10,23 +10,27 @@ type ButtonVariant = "primary" | "secondary" | "ghost" | "danger" | "brand";
|
|
|
10
10
|
* `<Button>`, so the two stay in lockstep.
|
|
11
11
|
*/
|
|
12
12
|
declare function buttonClasses(variant?: ButtonVariant, className?: string): string;
|
|
13
|
-
|
|
13
|
+
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
14
14
|
variant?: ButtonVariant;
|
|
15
|
+
/** In a flex row next to a taller labelled field, fill the field's height so the
|
|
16
|
+
* two line up. No effect outside a flex row. */
|
|
15
17
|
stretch?: boolean;
|
|
16
|
-
}
|
|
17
|
-
declare
|
|
18
|
+
}
|
|
19
|
+
declare function Button({ variant, stretch, className, ...rest }: ButtonProps): react.JSX.Element;
|
|
20
|
+
interface IconButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
18
21
|
variant?: ButtonVariant;
|
|
19
22
|
/** Box size: md = 36px (matches the top bar), sm = 32px. The icon stays 20px. */
|
|
20
23
|
size?: "sm" | "md";
|
|
21
|
-
}
|
|
24
|
+
}
|
|
25
|
+
declare const IconButton: react.ForwardRefExoticComponent<IconButtonProps & react.RefAttributes<HTMLButtonElement>>;
|
|
22
26
|
declare const FIELD_BASE: string;
|
|
23
27
|
/** Cancels FIELD_BASE's read-only treatment for a field that is `readOnly` for a
|
|
24
28
|
* reason other than "you may not edit this". Attribute-scoped for the same reason
|
|
25
29
|
* FIELD_BASE is — the two have to cancel on the identical selector or twMerge
|
|
26
30
|
* cannot make the later one win. */
|
|
27
|
-
declare const FIELD_WRITABLE_LOOK = "[&[readonly]]:bg-
|
|
31
|
+
declare const FIELD_WRITABLE_LOOK = "[&[readonly]]:bg-[var(--bg-surface)] [&[readonly]]:text-[var(--text-primary)]";
|
|
28
32
|
declare const FIELD_FLOATING_PAD = "pt-4 pb-1";
|
|
29
|
-
declare const FIELD_INVALID = "border-
|
|
33
|
+
declare const FIELD_INVALID = "border-[var(--danger-border)] ring-1 ring-[var(--danger-border)] focus:border-[var(--danger)] focus:ring-[var(--danger)]";
|
|
30
34
|
declare const FLOATING_INPUT_CLASS: string;
|
|
31
35
|
/** The phone breakpoint the display treatment below keys off — the same one the
|
|
32
36
|
* numpad sheet uses, kept in one place so the two can't drift apart. */
|
|
@@ -54,8 +58,12 @@ declare const PHONE_QUERY = "(max-width: 767px)";
|
|
|
54
58
|
* Size and weight are NOT here — an amount wants display type, a subject line
|
|
55
59
|
* wants a heading — so each control adds its own on top.
|
|
56
60
|
*/
|
|
57
|
-
declare const FIELD_DISPLAY = "block w-full border-x-0 border-t-0 border-b border-[var(--border)] bg-transparent px-0 pt-0 pb-1 text-[var(--text-primary)] shadow-none placeholder:text-
|
|
61
|
+
declare const FIELD_DISPLAY = "block w-full border-x-0 border-t-0 border-b border-[var(--border)] bg-transparent px-0 pt-0 pb-1 text-[var(--text-primary)] shadow-none placeholder:text-[var(--text-placeholder)] focus:border-[var(--brand)] focus:outline-none focus:ring-0 disabled:opacity-60";
|
|
58
62
|
declare const FIELD_TRIGGER: string;
|
|
63
|
+
/** An alias rather than an interface: the chevron adds nothing of its own to an
|
|
64
|
+
* `<svg>`'s props, and an interface declaring no members is the same type wearing a
|
|
65
|
+
* name that suggests otherwise. */
|
|
66
|
+
type FieldChevronProps = Omit<ComponentPropsWithoutRef<"svg">, "children">;
|
|
59
67
|
/** The dropdown chevron, shared by the native {@link Select} and every custom
|
|
60
68
|
* {@link FIELD_TRIGGER} control.
|
|
61
69
|
*
|
|
@@ -64,9 +72,7 @@ declare const FIELD_TRIGGER: string;
|
|
|
64
72
|
* pushes that box's midline down — so a labelled currency/multi-select chevron
|
|
65
73
|
* sat visibly lower than the native select's right beside it (feedback #400).
|
|
66
74
|
* Pair it with `pr-9` on the trigger so the value can't run underneath. */
|
|
67
|
-
declare function FieldChevron({ className }:
|
|
68
|
-
className?: string;
|
|
69
|
-
}): react.JSX.Element;
|
|
75
|
+
declare function FieldChevron({ className, ...rest }: FieldChevronProps): react.JSX.Element;
|
|
70
76
|
declare const FLOATING_LABEL_CLASS: string;
|
|
71
77
|
declare const FLOATING_LABEL_STATIC: string;
|
|
72
78
|
/**
|
|
@@ -77,8 +83,7 @@ declare const FLOATING_LABEL_STATIC: string;
|
|
|
77
83
|
* dropdown controls that need a ref + menu keep their own wrapper but the same label
|
|
78
84
|
* (via {@link FieldLabel}) and trigger ({@link FIELD_TRIGGER}) styles.
|
|
79
85
|
*/
|
|
80
|
-
|
|
81
|
-
className?: string;
|
|
86
|
+
interface FloatingFieldProps extends ComponentPropsWithoutRef<"div"> {
|
|
82
87
|
htmlFor?: string;
|
|
83
88
|
label?: ReactNode;
|
|
84
89
|
staticLabel?: boolean;
|
|
@@ -99,7 +104,8 @@ declare function FloatingField({ className, htmlFor, label, staticLabel, srOnlyL
|
|
|
99
104
|
* the label that gives way (steering-design feedback #48). */
|
|
100
105
|
hint?: ReactNode;
|
|
101
106
|
children: ReactNode;
|
|
102
|
-
}
|
|
107
|
+
}
|
|
108
|
+
declare function FloatingField({ className, htmlFor, label, staticLabel, srOnlyLabel, hint, children, ...rest }: FloatingFieldProps): react.JSX.Element;
|
|
103
109
|
/**
|
|
104
110
|
* The "?" that explains a field, on the field's own label line (dev#468).
|
|
105
111
|
*
|
|
@@ -116,20 +122,34 @@ declare function FloatingField({ className, htmlFor, label, staticLabel, srOnlyL
|
|
|
116
122
|
* too. The text is also its accessible name, so a screen reader gets it without
|
|
117
123
|
* the bubble ever opening.
|
|
118
124
|
*/
|
|
119
|
-
|
|
125
|
+
interface FieldHintProps extends Omit<ComponentPropsWithoutRef<"button">, "children"> {
|
|
126
|
+
/** The explanation. It is both the tooltip's text and, by default, the button's
|
|
127
|
+
* accessible name, so a screen reader gets it without the bubble ever opening. */
|
|
120
128
|
label: string;
|
|
121
129
|
side?: "left" | "right" | "top" | "bottom";
|
|
122
|
-
}
|
|
130
|
+
}
|
|
131
|
+
declare function FieldHint({ label, side, className, "aria-label": ariaLabel, ...rest }: FieldHintProps): react.JSX.Element;
|
|
123
132
|
/**
|
|
124
133
|
* The floating label for a custom-dropdown trigger (a `<span>`, since the trigger is a
|
|
125
134
|
* button not a labelable input). Same placement as {@link FloatingField}'s static label,
|
|
126
135
|
* so every labelled field lines up. Render inside a `relative` wrapper, before the trigger.
|
|
127
136
|
*/
|
|
128
|
-
|
|
137
|
+
interface FieldLabelProps extends ComponentPropsWithoutRef<"span"> {
|
|
129
138
|
children: ReactNode;
|
|
130
|
-
|
|
131
|
-
}): react.JSX.Element;
|
|
132
|
-
|
|
139
|
+
}
|
|
140
|
+
declare function FieldLabel({ children, className, ...rest }: FieldLabelProps): react.JSX.Element;
|
|
141
|
+
/** The two names the password reveal toggle can wear. See {@link Input}. */
|
|
142
|
+
interface PasswordRevealLabels {
|
|
143
|
+
/** While the value is hidden — activating the toggle will show it. */
|
|
144
|
+
show: string;
|
|
145
|
+
/** While the value is shown. */
|
|
146
|
+
hide: string;
|
|
147
|
+
}
|
|
148
|
+
declare const DEFAULT_PASSWORD_REVEAL_LABELS: PasswordRevealLabels;
|
|
149
|
+
/** Caller's labels over the English defaults — the same shape as
|
|
150
|
+
* `resolveDataTableLabels`, so a consumer translates every kit string one way. */
|
|
151
|
+
declare function resolvePasswordRevealLabels(partial?: Partial<PasswordRevealLabels>): PasswordRevealLabels;
|
|
152
|
+
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
|
133
153
|
label?: ReactNode;
|
|
134
154
|
/** Classes for the `<input>` itself, as distinct from `className`, which
|
|
135
155
|
* styles the field WRAPPER once a `label` turns this into a FloatingField.
|
|
@@ -153,20 +173,37 @@ declare const Input: react.ForwardRefExoticComponent<InputHTMLAttributes<HTMLInp
|
|
|
153
173
|
* and painted the field exactly as if it were fine. Setting the prop sets the
|
|
154
174
|
* attribute too, so the two can no longer be spelled separately. */
|
|
155
175
|
invalid?: boolean;
|
|
156
|
-
|
|
157
|
-
|
|
176
|
+
/** What is wrong with the value, in the caller's own words ("That IBAN has 21
|
|
177
|
+
* digits"). Rendered under the field, pointed at by the control's
|
|
178
|
+
* `aria-describedby` — MERGED with any the caller already passed — and implies
|
|
179
|
+
* `invalid`, so the field paints as well as announces. `invalid` alone still
|
|
180
|
+
* covers the case where the message lives elsewhere. See {@link useFieldError}. */
|
|
181
|
+
error?: ReactNode;
|
|
182
|
+
/** Names for the password reveal toggle, English by default — it is the one
|
|
183
|
+
* string this component renders on its own behalf, and a German form was
|
|
184
|
+
* reading it out in English. See {@link PasswordRevealLabels}. */
|
|
185
|
+
passwordLabels?: Partial<PasswordRevealLabels>;
|
|
186
|
+
}
|
|
187
|
+
declare const Input: react.ForwardRefExoticComponent<InputProps & react.RefAttributes<HTMLInputElement>>;
|
|
188
|
+
interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
|
|
158
189
|
label?: ReactNode;
|
|
190
|
+
/** See {@link Input}'s `invalid`. */
|
|
159
191
|
invalid?: boolean;
|
|
192
|
+
/** See {@link Input}'s `error`. */
|
|
193
|
+
error?: ReactNode;
|
|
160
194
|
/** A {@link FieldHint} for the label line — see {@link FloatingField}. */
|
|
161
195
|
hint?: ReactNode;
|
|
162
|
-
}
|
|
163
|
-
declare const
|
|
196
|
+
}
|
|
197
|
+
declare const Select: react.ForwardRefExoticComponent<SelectProps & react.RefAttributes<HTMLSelectElement>>;
|
|
198
|
+
interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
164
199
|
label?: ReactNode;
|
|
165
200
|
/** See {@link Input}'s `invalid`. */
|
|
166
201
|
invalid?: boolean;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
202
|
+
/** See {@link Input}'s `error`. */
|
|
203
|
+
error?: ReactNode;
|
|
204
|
+
}
|
|
205
|
+
declare const Textarea: react.ForwardRefExoticComponent<TextareaProps & react.RefAttributes<HTMLTextAreaElement>>;
|
|
206
|
+
interface CardProps extends ComponentPropsWithoutRef<"div"> {
|
|
170
207
|
children: ReactNode;
|
|
171
208
|
/**
|
|
172
209
|
* When true, drop the card chrome (border, rounded corners, shadow) on mobile so the
|
|
@@ -174,22 +211,64 @@ declare function Card({ className, children, flush, }: {
|
|
|
174
211
|
* content cards on data pages; leave off for centered dialog/panel cards.
|
|
175
212
|
*/
|
|
176
213
|
flush?: boolean;
|
|
177
|
-
}
|
|
178
|
-
declare function
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
214
|
+
}
|
|
215
|
+
declare function Card({ className, children, flush, ...rest }: CardProps): react.JSX.Element;
|
|
216
|
+
/** The sub-parts add nothing to a `<div>`'s props — they are the SAME element with a
|
|
217
|
+
* `data-slot` and a padding rhythm — so each name is an alias rather than an empty
|
|
218
|
+
* interface pretending to be more. They exist so a consumer's own wrapper can say
|
|
219
|
+
* `CardHeaderProps` instead of `ComponentProps<typeof CardHeader>`. */
|
|
220
|
+
type CardHeaderProps = ComponentPropsWithoutRef<"div">;
|
|
221
|
+
type CardTitleProps = ComponentPropsWithoutRef<"div">;
|
|
222
|
+
type CardDescriptionProps = ComponentPropsWithoutRef<"div">;
|
|
223
|
+
type CardActionProps = ComponentPropsWithoutRef<"div">;
|
|
224
|
+
type CardContentProps = ComponentPropsWithoutRef<"div">;
|
|
225
|
+
type CardFooterProps = ComponentPropsWithoutRef<"div">;
|
|
226
|
+
declare function CardHeader({ className, ...props }: CardHeaderProps): react.JSX.Element;
|
|
227
|
+
declare function CardTitle({ className, ...props }: CardTitleProps): react.JSX.Element;
|
|
228
|
+
declare function CardDescription({ className, ...props }: CardDescriptionProps): react.JSX.Element;
|
|
229
|
+
declare function CardAction({ className, ...props }: CardActionProps): react.JSX.Element;
|
|
230
|
+
declare function CardContent({ className, ...props }: CardContentProps): react.JSX.Element;
|
|
231
|
+
declare function CardFooter({ className, ...props }: CardFooterProps): react.JSX.Element;
|
|
232
|
+
/** A `<span>`'s props plus the words a reader hears — see {@link CardHeaderProps}.
|
|
233
|
+
* The ring is drawn with a border, so there is nothing inside it to put children in. */
|
|
234
|
+
interface SpinnerProps extends Omit<ComponentPropsWithoutRef<"span">, "children"> {
|
|
235
|
+
/** What the spinner means, for a screen reader. Default: `common.loading` from the
|
|
236
|
+
* {@link UiKitProvider}, else "Loading…". */
|
|
237
|
+
label?: string;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* A spinning ring that also SAYS it is loading.
|
|
241
|
+
*
|
|
242
|
+
* It used to be a bordered span and nothing else, so a reader met no element at all
|
|
243
|
+
* where a sighted user saw the page working — or, beside an emptied list, met the
|
|
244
|
+
* empty list and concluded there was nothing there. `role="status"` makes it a polite
|
|
245
|
+
* live region, and the text inside is what that region announces. Text rather than
|
|
246
|
+
* `aria-label`: a live region's announcement is its CONTENT, and several readers
|
|
247
|
+
* ignore a name on one.
|
|
248
|
+
*
|
|
249
|
+
* `relative` so the `sr-only` text has a local containing block (see
|
|
250
|
+
* sr-only-containment.test). The role goes BEFORE the spread: a caller showing the
|
|
251
|
+
* spinner next to text that already says "Loading" can pass `aria-hidden` or its own
|
|
252
|
+
* `role` and have it win.
|
|
253
|
+
*/
|
|
254
|
+
declare function Spinner({ className, label, ...rest }: SpinnerProps): react.JSX.Element;
|
|
255
|
+
interface EmptyStateProps extends Omit<ComponentPropsWithoutRef<"div">, "children"> {
|
|
256
|
+
/** The box renders `title` and `hint` in its own two-line rhythm, which is what makes
|
|
257
|
+
* every empty state in three apps look like the same thing — so there is no
|
|
258
|
+
* `children` slot to put arbitrary content in. */
|
|
188
259
|
title: string;
|
|
189
260
|
hint?: string;
|
|
190
|
-
|
|
191
|
-
}): react.JSX.Element;
|
|
192
|
-
|
|
261
|
+
}
|
|
262
|
+
declare function EmptyState({ title, hint, className, ...rest }: EmptyStateProps): react.JSX.Element;
|
|
263
|
+
/**
|
|
264
|
+
* The strip's own props sit on a `<div>`: the tablist IS the root element, so anything
|
|
265
|
+
* a caller hangs on it — a `data-tour` anchor for the kit's guided tour, a test id, an
|
|
266
|
+
* `aria-describedby` — lands there. `onChange` is omitted from the div's props because
|
|
267
|
+
* this component's `onChange` hands over the chosen TAB ID, not a DOM event, and
|
|
268
|
+
* `children` because the strip renders `tabs` — a caller who wants a PANEL wires it up
|
|
269
|
+
* through `panelId`, which is the whole point of that prop.
|
|
270
|
+
*/
|
|
271
|
+
interface TabsProps<T extends string> extends Omit<ComponentPropsWithoutRef<"div">, "onChange" | "children"> {
|
|
193
272
|
/** `label` is a ReactNode so tabs can pair an icon with text; `badge` is an
|
|
194
273
|
* optional trailing node (e.g. a count pill). `href` marks a tab that IS a route:
|
|
195
274
|
* it renders as an anchor so it can be middle-/⌘-clicked into a new tab (Keksdose
|
|
@@ -229,11 +308,45 @@ interface TabsProps<T extends string> {
|
|
|
229
308
|
* already fit a phone row, and turning those into chips would be an unrequested
|
|
230
309
|
* redesign of three other pages. */
|
|
231
310
|
wrap?: boolean;
|
|
232
|
-
/**
|
|
233
|
-
*
|
|
234
|
-
*
|
|
311
|
+
/**
|
|
312
|
+
* Accessible name for the `role="tablist"` container. Every tab carries its own
|
|
313
|
+
* text, so this names the GROUP, not the tabs; leave it unset where a visible
|
|
314
|
+
* heading immediately above already does that job.
|
|
315
|
+
*
|
|
316
|
+
* @deprecated Pass `aria-label` instead. This kit had three spellings for one idea
|
|
317
|
+
* — `ariaLabel`, `aria-label` and this `label` — and settled on the DOM one, which
|
|
318
|
+
* is also the one that arrives for free now that the strip spreads its rest props.
|
|
319
|
+
* `label` still works and is unchanged; it names the strip only when `aria-label`
|
|
320
|
+
* is absent.
|
|
321
|
+
*/
|
|
235
322
|
label?: string;
|
|
323
|
+
/**
|
|
324
|
+
* `id` of the element the caller renders the open tab's content into — the missing
|
|
325
|
+
* half of `role="tab"`. A tab that controls nothing is a tab in name only: a screen
|
|
326
|
+
* reader announces "tab, 2 of 5" and then has no way to take the user to what it
|
|
327
|
+
* opened, and no way back.
|
|
328
|
+
*
|
|
329
|
+
* **This component does not render the panel, on purpose.** `Tabs` is the STRIP; the
|
|
330
|
+
* content lives wherever the caller put it — three Keksdose pages render it in a
|
|
331
|
+
* sibling `<Card>`, one renders it through a router outlet, and a tab can BE a route
|
|
332
|
+
* (`href`), in which case the panel is a whole page this component never sees.
|
|
333
|
+
* Wrapping `children` here would mean either moving that content into the strip's
|
|
334
|
+
* subtree — a layout change on every page that uses tabs — or shipping a wrapper
|
|
335
|
+
* that only some callers could use. So the two halves are joined by an id instead.
|
|
336
|
+
*
|
|
337
|
+
* Wire the other end yourself:
|
|
338
|
+
*
|
|
339
|
+
* ```tsx
|
|
340
|
+
* <Tabs tabs={tabs} active={active} onChange={setActive} panelId="report-panel" />
|
|
341
|
+
* <div id="report-panel" role="tabpanel" aria-labelledby="report-panel-tab" tabIndex={-1}>
|
|
342
|
+
* ```
|
|
343
|
+
*
|
|
344
|
+
* `aria-controls` goes on the OPEN tab only, and `${panelId}-tab` is its id — the
|
|
345
|
+
* closed tabs' panels are not in the document, and a dangling `aria-controls`
|
|
346
|
+
* describes a tab as opening something that is not there.
|
|
347
|
+
*/
|
|
348
|
+
panelId?: string;
|
|
236
349
|
}
|
|
237
|
-
declare function Tabs<T extends string>({ tabs, active, onChange, className, wrap, label }: TabsProps<T>): react.JSX.Element;
|
|
350
|
+
declare function Tabs<T extends string>({ tabs, active, onChange, className, wrap, label, panelId, "aria-label": ariaLabel, ...rest }: TabsProps<T>): react.JSX.Element;
|
|
238
351
|
|
|
239
|
-
export { Button, type ButtonVariant, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, EmptyState, FIELD_BASE, FIELD_DISPLAY, FIELD_FLOATING_PAD, FIELD_INVALID, FIELD_TRIGGER, FIELD_WRITABLE_LOOK, FLOATING_INPUT_CLASS, FLOATING_LABEL_CLASS, FLOATING_LABEL_STATIC, FieldChevron, FieldHint, FieldLabel, FloatingField, IconButton, Input, PHONE_QUERY, Select, Spinner, Tabs, type TabsProps, Textarea, buttonClasses };
|
|
352
|
+
export { Button, type ButtonProps, type ButtonVariant, Card, CardAction, type CardActionProps, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, DEFAULT_PASSWORD_REVEAL_LABELS, EmptyState, type EmptyStateProps, FIELD_BASE, FIELD_DISPLAY, FIELD_FLOATING_PAD, FIELD_INVALID, FIELD_TRIGGER, FIELD_WRITABLE_LOOK, FLOATING_INPUT_CLASS, FLOATING_LABEL_CLASS, FLOATING_LABEL_STATIC, FieldChevron, type FieldChevronProps, FieldHint, type FieldHintProps, FieldLabel, type FieldLabelProps, FloatingField, type FloatingFieldProps, IconButton, type IconButtonProps, Input, type InputProps, PHONE_QUERY, type PasswordRevealLabels, Select, type SelectProps, Spinner, type SpinnerProps, Tabs, type TabsProps, Textarea, type TextareaProps, buttonClasses, resolvePasswordRevealLabels };
|