@eifi1/ui-kit 0.10.0 → 0.12.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 +66 -37
- package/dist/chart.d.ts +3 -1
- package/dist/chart.js +2 -0
- package/dist/chart.js.map +1 -1
- package/dist/components/account-settings-labels.d.ts +105 -0
- package/dist/components/account-settings-labels.js +81 -0
- package/dist/components/account-settings-labels.js.map +1 -0
- package/dist/components/account-settings.d.ts +36 -52
- package/dist/components/account-settings.js +44 -8
- package/dist/components/account-settings.js.map +1 -1
- package/dist/components/alert-banner.d.ts +26 -1
- package/dist/components/alert-banner.js +10 -7
- package/dist/components/alert-banner.js.map +1 -1
- package/dist/components/amount-input.d.ts +26 -4
- package/dist/components/amount-input.js +5 -2
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/authed-image.d.ts +68 -0
- package/dist/components/authed-image.js +117 -0
- package/dist/components/authed-image.js.map +1 -0
- package/dist/components/breadcrumbs.d.ts +2 -1
- package/dist/components/breadcrumbs.js +5 -2
- package/dist/components/breadcrumbs.js.map +1 -1
- package/dist/components/bulk-action-bar.d.ts +51 -5
- package/dist/components/bulk-action-bar.js +88 -41
- package/dist/components/bulk-action-bar.js.map +1 -1
- package/dist/components/button-group.d.ts +107 -3
- package/dist/components/button-group.js +60 -5
- package/dist/components/button-group.js.map +1 -1
- package/dist/components/calculator.d.ts +17 -4
- package/dist/components/calendar-heatmap.d.ts +162 -0
- package/dist/components/calendar-heatmap.js +336 -0
- package/dist/components/calendar-heatmap.js.map +1 -0
- package/dist/components/chip.d.ts +2 -1
- package/dist/components/chip.js +6 -3
- package/dist/components/chip.js.map +1 -1
- package/dist/components/choice-card.d.ts +66 -2
- package/dist/components/choice-card.js +108 -1
- package/dist/components/choice-card.js.map +1 -1
- package/dist/components/combobox-core.js +2 -0
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox.d.ts +2 -2
- package/dist/components/combobox.js +31 -9
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/copy-button.d.ts +52 -70
- package/dist/components/copy-button.js +1 -1
- package/dist/components/copy-button.js.map +1 -1
- package/dist/components/data-table-cells.d.ts +63 -0
- package/dist/components/data-table-cells.js +64 -0
- package/dist/components/data-table-cells.js.map +1 -0
- package/dist/components/data-table-filter-popover.d.ts +1 -1
- package/dist/components/data-table-filters.d.ts +1 -1
- package/dist/components/data-table-filters.js +31 -1
- package/dist/components/data-table-filters.js.map +1 -1
- package/dist/components/data-table-labels.d.ts +13 -0
- package/dist/components/data-table-labels.js +8 -1
- package/dist/components/data-table-labels.js.map +1 -1
- package/dist/components/data-table.d.ts +1 -1
- package/dist/components/data-table.js +104 -12
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.d.ts +3 -1
- package/dist/components/date-picker.js +15 -2
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/description-list.d.ts +23 -3
- package/dist/components/description-list.js +23 -3
- package/dist/components/description-list.js.map +1 -1
- package/dist/components/dialog-frame.d.ts +1 -0
- package/dist/components/entity-combobox.d.ts +1 -1
- package/dist/components/entity-combobox.js +18 -3
- package/dist/components/entity-combobox.js.map +1 -1
- package/dist/components/error-boundary.d.ts +92 -0
- package/dist/components/error-boundary.js +111 -0
- package/dist/components/error-boundary.js.map +1 -0
- package/dist/components/field.d.ts +69 -0
- package/dist/components/field.js +75 -0
- package/dist/components/field.js.map +1 -0
- package/dist/components/file-button.d.ts +51 -208
- package/dist/components/file-dropzone.d.ts +50 -2
- package/dist/components/floating-panel.d.ts +156 -4
- package/dist/components/floating-panel.js +193 -29
- package/dist/components/floating-panel.js.map +1 -1
- package/dist/components/form-actions.d.ts +53 -0
- package/dist/components/form-actions.js +98 -0
- package/dist/components/form-actions.js.map +1 -0
- package/dist/components/image-grid.d.ts +67 -0
- package/dist/components/image-grid.js +112 -0
- package/dist/components/image-grid.js.map +1 -0
- package/dist/components/lightbox.d.ts +104 -0
- package/dist/components/lightbox.js +229 -0
- package/dist/components/lightbox.js.map +1 -0
- package/dist/components/line-items.d.ts +114 -0
- package/dist/components/line-items.js +238 -0
- package/dist/components/line-items.js.map +1 -0
- package/dist/components/list.d.ts +37 -3
- package/dist/components/list.js +39 -14
- package/dist/components/list.js.map +1 -1
- package/dist/components/loading-state.d.ts +24 -0
- package/dist/components/loading-state.js +32 -0
- package/dist/components/loading-state.js.map +1 -0
- package/dist/components/menu-item.d.ts +13 -2
- package/dist/components/menu-item.js +11 -2
- package/dist/components/menu-item.js.map +1 -1
- package/dist/components/mini-calendar.d.ts +82 -5
- package/dist/components/mini-calendar.js +212 -64
- package/dist/components/mini-calendar.js.map +1 -1
- package/dist/components/modal.d.ts +20 -1
- package/dist/components/modal.js +26 -2
- package/dist/components/modal.js.map +1 -1
- package/dist/components/month-picker.d.ts +21 -1
- package/dist/components/month-picker.js +139 -26
- package/dist/components/month-picker.js.map +1 -1
- package/dist/components/multi-entity-combobox.d.ts +1 -1
- package/dist/components/multi-entity-combobox.js +19 -3
- package/dist/components/multi-entity-combobox.js.map +1 -1
- package/dist/components/nav-pills.d.ts +67 -0
- package/dist/components/nav-pills.js +69 -0
- package/dist/components/nav-pills.js.map +1 -0
- package/dist/components/number-field.d.ts +26 -5
- package/dist/components/number-field.js +5 -7
- package/dist/components/number-field.js.map +1 -1
- package/dist/components/number-input.d.ts +27 -5
- package/dist/components/number-input.js +7 -1
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/numpad-sheet.d.ts +17 -4
- package/dist/components/passkeys-setting.d.ts +67 -0
- package/dist/components/passkeys-setting.js +214 -0
- package/dist/components/passkeys-setting.js.map +1 -0
- package/dist/components/picker-sheet.js +2 -1
- package/dist/components/picker-sheet.js.map +1 -1
- package/dist/components/pie-chart-labels.d.ts +20 -0
- package/dist/components/pie-chart-labels.js +12 -0
- package/dist/components/pie-chart-labels.js.map +1 -0
- package/dist/components/pie-chart.d.ts +101 -0
- package/dist/components/pie-chart.js +283 -0
- package/dist/components/pie-chart.js.map +1 -0
- package/dist/components/progress-bar.d.ts +73 -3
- package/dist/components/progress-bar.js +110 -8
- package/dist/components/progress-bar.js.map +1 -1
- package/dist/components/qr-code.d.ts +34 -0
- package/dist/components/qr-code.js +57 -0
- package/dist/components/qr-code.js.map +1 -0
- package/dist/components/search-field.d.ts +15 -3
- package/dist/components/search-field.js +5 -1
- package/dist/components/search-field.js.map +1 -1
- package/dist/components/series-chart.d.ts +16 -0
- package/dist/components/series-chart.js +4 -1
- package/dist/components/series-chart.js.map +1 -1
- package/dist/components/settings-fields.d.ts +50 -1
- package/dist/components/signed-amount.d.ts +121 -0
- package/dist/components/signed-amount.js +152 -0
- package/dist/components/signed-amount.js.map +1 -0
- package/dist/components/skeleton.d.ts +10 -3
- package/dist/components/skeleton.js +12 -2
- package/dist/components/skeleton.js.map +1 -1
- package/dist/components/stat-tile.d.ts +2 -1
- package/dist/components/stat-tile.js +5 -2
- package/dist/components/stat-tile.js.map +1 -1
- package/dist/components/status-dot.d.ts +6 -2
- package/dist/components/status-dot.js +6 -1
- package/dist/components/status-dot.js.map +1 -1
- package/dist/components/table.d.ts +77 -11
- package/dist/components/table.js +85 -17
- package/dist/components/table.js.map +1 -1
- package/dist/components/text-link.d.ts +176 -0
- package/dist/components/text-link.js +109 -0
- package/dist/components/text-link.js.map +1 -0
- package/dist/components/text.d.ts +8 -1
- package/dist/components/text.js +1 -0
- package/dist/components/text.js.map +1 -1
- package/dist/components/time-input.d.ts +50 -1
- package/dist/components/toggle-group.d.ts +28 -0
- package/dist/components/toggle-group.js +58 -9
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/toggle-legend.d.ts +36 -3
- package/dist/components/toggle-legend.js +49 -0
- package/dist/components/toggle-legend.js.map +1 -1
- package/dist/components/tooltip.d.ts +11 -3
- package/dist/components/tooltip.js +2 -0
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/ui.d.ts +52 -729
- package/dist/components/ui.js +296 -27
- package/dist/components/ui.js.map +1 -1
- package/dist/components/use-table-state.d.ts +3 -52
- package/dist/components/use-table-state.js +128 -30
- package/dist/components/use-table-state.js.map +1 -1
- package/dist/components/user-avatar.d.ts +25 -3
- package/dist/components/user-avatar.js +32 -3
- package/dist/components/user-avatar.js.map +1 -1
- package/dist/{data-table-filters-Dh9uF_S-.d.ts → data-table-Drun8Fk9.d.ts} +305 -61
- package/dist/data-table.d.ts +2 -1
- package/dist/data-table.js +5 -1
- package/dist/data-table.js.map +1 -1
- package/dist/feedback/feedback-dialog.d.ts +26 -3
- package/dist/feedback/feedback-dialog.js +16 -2
- package/dist/feedback/feedback-dialog.js.map +1 -1
- package/dist/feedback/feedback-thread.d.ts +133 -0
- package/dist/feedback/feedback-thread.js +237 -0
- package/dist/feedback/feedback-thread.js.map +1 -0
- package/dist/feedback.d.ts +2 -1
- package/dist/feedback.js +1 -0
- package/dist/feedback.js.map +1 -1
- package/dist/hooks/use-authed-src.d.ts +71 -0
- package/dist/hooks/use-authed-src.js +63 -0
- package/dist/hooks/use-authed-src.js.map +1 -0
- package/dist/hooks/use-file-drop.d.ts +50 -2
- package/dist/hooks/use-hotkey.d.ts +59 -0
- package/dist/hooks/use-hotkey.js +105 -0
- package/dist/hooks/use-hotkey.js.map +1 -0
- package/dist/hooks/use-search-param-state.d.ts +83 -0
- package/dist/hooks/use-search-param-state.js +94 -0
- package/dist/hooks/use-search-param-state.js.map +1 -0
- package/dist/i18n/defaults.d.ts +17 -4
- package/dist/i18n/defaults.js +33 -1
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +47 -310
- package/dist/i18n/kit-labels.js +20 -5
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/de-CH-informal.d.ts +17 -4
- package/dist/i18n/locales/de-CH.d.ts +17 -4
- package/dist/i18n/locales/de-informal.d.ts +17 -4
- package/dist/i18n/locales/de.d.ts +17 -4
- package/dist/i18n/locales/de.js +165 -4
- package/dist/i18n/locales/de.js.map +1 -1
- package/dist/i18n/locales/es.d.ts +17 -4
- package/dist/i18n/locales/es.js +162 -4
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +17 -4
- package/dist/i18n/locales/fr.js +162 -4
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +17 -4
- package/dist/i18n/locales/hu.js +162 -4
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +17 -4
- package/dist/i18n/locales/it.js +162 -4
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +17 -4
- package/dist/i18n/locales/zh.js +162 -4
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/index.d.ts +45 -21
- package/dist/index.js +43 -1
- package/dist/index.js.map +1 -1
- package/dist/kit-labels-v3biUF1L.d.ts +1645 -0
- package/dist/lib/clipping.d.ts +21 -5
- package/dist/lib/clipping.js +3 -0
- package/dist/lib/clipping.js.map +1 -1
- package/dist/lib/dates.d.ts +17 -1
- package/dist/lib/dates.js +19 -0
- package/dist/lib/dates.js.map +1 -1
- package/dist/lib/format.d.ts +144 -0
- package/dist/lib/format.js +149 -0
- package/dist/lib/format.js.map +1 -0
- package/dist/lib/qr-encode.d.ts +44 -0
- package/dist/lib/qr-encode.js +338 -0
- package/dist/lib/qr-encode.js.map +1 -0
- package/dist/rhf/fields.d.ts +241 -0
- package/dist/rhf/fields.js +601 -0
- package/dist/rhf/fields.js.map +1 -0
- package/dist/rhf/form.d.ts +50 -1
- package/dist/rhf/line-items.d.ts +31 -0
- package/dist/rhf/line-items.js +59 -0
- package/dist/rhf/line-items.js.map +1 -0
- package/dist/rhf/use-rhf-wizard-step.d.ts +22 -0
- package/dist/rhf/use-rhf-wizard-step.js +38 -0
- package/dist/rhf/use-rhf-wizard-step.js.map +1 -0
- package/dist/rhf.d.ts +63 -1
- package/dist/rhf.js +3 -0
- package/dist/rhf.js.map +1 -1
- package/dist/shell/app-shell.d.ts +99 -2
- package/dist/shell/app-shell.js +58 -3
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/auth-layout.d.ts +74 -0
- package/dist/shell/auth-layout.js +97 -0
- package/dist/shell/auth-layout.js.map +1 -0
- package/dist/shell/top-bar-brand.d.ts +81 -0
- package/dist/shell/top-bar-brand.js +47 -0
- package/dist/shell/top-bar-brand.js.map +1 -0
- package/dist/shell/topbar-action-menu.d.ts +84 -3
- package/dist/shell/topbar-action-menu.js +115 -27
- package/dist/shell/topbar-action-menu.js.map +1 -1
- package/dist/shell.d.ts +58 -2
- package/dist/shell.js +2 -0
- package/dist/shell.js.map +1 -1
- package/dist/wizard/stepper-nav.d.ts +49 -1
- package/dist/wizard/wizard-context.d.ts +30 -1
- package/dist/wizard/wizard-context.js +23 -2
- package/dist/wizard/wizard-context.js.map +1 -1
- package/dist/wizard/wizard-step.d.ts +20 -5
- package/dist/wizard/wizard-step.js +16 -3
- package/dist/wizard/wizard-step.js.map +1 -1
- package/dist/wizard.d.ts +51 -3
- package/package.json +1 -1
- package/src/chart.ts +4 -0
- package/src/components/account-settings-labels.ts +190 -0
- package/src/components/account-settings.tsx +110 -63
- package/src/components/alert-banner.tsx +36 -7
- package/src/components/amount-input.tsx +13 -1
- package/src/components/authed-image.tsx +190 -0
- package/src/components/breadcrumbs.tsx +8 -2
- package/src/components/bulk-action-bar.tsx +163 -57
- package/src/components/button-group.tsx +129 -4
- package/src/components/calendar-heatmap.tsx +599 -0
- package/src/components/chip.tsx +8 -4
- package/src/components/choice-card.tsx +208 -1
- package/src/components/combobox-core.tsx +5 -0
- package/src/components/combobox.tsx +31 -9
- package/src/components/copy-button.tsx +3 -2
- package/src/components/data-table-cells.tsx +119 -0
- package/src/components/data-table-filters.ts +77 -0
- package/src/components/data-table-labels.ts +23 -0
- package/src/components/data-table.tsx +231 -13
- package/src/components/date-picker.tsx +19 -3
- package/src/components/description-list.tsx +52 -3
- package/src/components/entity-combobox.tsx +18 -3
- package/src/components/error-boundary.tsx +216 -0
- package/src/components/field.tsx +188 -0
- package/src/components/floating-panel.tsx +380 -16
- package/src/components/form-actions.tsx +191 -0
- package/src/components/image-grid.tsx +181 -0
- package/src/components/lightbox.tsx +374 -0
- package/src/components/line-items.tsx +385 -0
- package/src/components/list.tsx +93 -18
- package/src/components/loading-state.tsx +47 -0
- package/src/components/menu-item.tsx +29 -3
- package/src/components/mini-calendar.tsx +317 -57
- package/src/components/modal.tsx +61 -3
- package/src/components/month-picker.tsx +181 -26
- package/src/components/multi-entity-combobox.tsx +19 -3
- package/src/components/nav-pills.tsx +152 -0
- package/src/components/number-field.tsx +15 -10
- package/src/components/number-input.tsx +16 -1
- package/src/components/passkeys-setting.tsx +331 -0
- package/src/components/picker-sheet.tsx +7 -1
- package/src/components/pie-chart-labels.ts +32 -0
- package/src/components/pie-chart.tsx +479 -0
- package/src/components/progress-bar.tsx +241 -13
- package/src/components/qr-code.tsx +83 -0
- package/src/components/search-field.tsx +20 -4
- package/src/components/series-chart.tsx +21 -0
- package/src/components/signed-amount.tsx +286 -0
- package/src/components/skeleton.tsx +21 -3
- package/src/components/stat-tile.tsx +8 -2
- package/src/components/status-dot.tsx +10 -2
- package/src/components/table.tsx +186 -23
- package/src/components/text-link.tsx +248 -0
- package/src/components/text.tsx +9 -1
- package/src/components/toggle-group.tsx +106 -13
- package/src/components/toggle-legend.tsx +87 -2
- package/src/components/tooltip.tsx +17 -3
- package/src/components/ui.tsx +572 -44
- package/src/components/use-table-state.ts +265 -33
- package/src/components/user-avatar.tsx +57 -3
- package/src/data-table.ts +10 -0
- package/src/feedback/feedback-dialog.tsx +42 -3
- package/src/feedback/feedback-thread.tsx +434 -0
- package/src/feedback.ts +1 -0
- package/src/hooks/use-authed-src.ts +161 -0
- package/src/hooks/use-hotkey.ts +172 -0
- package/src/hooks/use-search-param-state.ts +187 -0
- package/src/i18n/defaults.ts +32 -0
- package/src/i18n/kit-labels.tsx +104 -8
- package/src/i18n/locales/de.ts +162 -0
- package/src/i18n/locales/es.ts +159 -0
- package/src/i18n/locales/fr.ts +162 -0
- package/src/i18n/locales/hu.ts +160 -0
- package/src/i18n/locales/it.ts +160 -0
- package/src/i18n/locales/zh.ts +159 -0
- package/src/index.ts +64 -0
- package/src/lib/clipping.ts +22 -4
- package/src/lib/dates.ts +37 -0
- package/src/lib/format.ts +325 -0
- package/src/lib/qr-encode.ts +420 -0
- package/src/rhf/fields.tsx +1054 -0
- package/src/rhf/line-items.tsx +130 -0
- package/src/rhf/use-rhf-wizard-step.ts +113 -0
- package/src/rhf.ts +8 -0
- package/src/shell/app-shell.tsx +147 -2
- package/src/shell/auth-layout.tsx +190 -0
- package/src/shell/top-bar-brand.tsx +76 -0
- package/src/shell/topbar-action-menu.tsx +260 -49
- package/src/shell.ts +2 -0
- package/src/wizard/wizard-context.tsx +50 -1
- package/src/wizard/wizard-step.tsx +43 -6
- package/tokens.css +12 -0
|
@@ -2,7 +2,7 @@ import { useId } from "react";
|
|
|
2
2
|
import type { ComponentPropsWithoutRef, KeyboardEvent, ReactElement, ReactNode } from "react";
|
|
3
3
|
import { cn } from "../lib/cn";
|
|
4
4
|
import { horizontalStep } from "../lib/direction";
|
|
5
|
-
import { FIELD_INVALID, FloatingField } from "./ui";
|
|
5
|
+
import { FIELD_INVALID, FloatingField, Label } from "./ui";
|
|
6
6
|
|
|
7
7
|
export interface ToggleOption<T extends string> {
|
|
8
8
|
value: T;
|
|
@@ -67,6 +67,19 @@ export interface ToggleGroupBaseProps<T extends string>
|
|
|
67
67
|
* group's own box is dropped: two nested borders read as a control in a control.
|
|
68
68
|
*/
|
|
69
69
|
label?: ReactNode;
|
|
70
|
+
/**
|
|
71
|
+
* Where `label` goes. `"field"` (default): the field chrome described under `label`.
|
|
72
|
+
* `"above"`: the kit's {@link Label} over the bare group — the shape of a {@link Field}
|
|
73
|
+
* — with `hint` beside the label and `error` under the group, for a form that sets
|
|
74
|
+
* its labels above its fields (kastlan's international-rent-calculator.tsx, whose DE
|
|
75
|
+
* cap pair sits in a `Field` column between two labelled-above inputs, where the
|
|
76
|
+
* chrome's inner label would be the only one of its kind).
|
|
77
|
+
*
|
|
78
|
+
* Inside a `Field`, pass no `label` at all and spread the render-prop instead —
|
|
79
|
+
* `{(ids, { labelId }) => <ToggleGroup {...ids} aria-labelledby={labelId} … />}` —
|
|
80
|
+
* so the Field's label names the group and its hint and error describe it.
|
|
81
|
+
*/
|
|
82
|
+
labelPlacement?: "field" | "above";
|
|
70
83
|
/** A {@link FieldHint} on the label line, as on a labelled {@link Select}. Only with
|
|
71
84
|
* `label`. */
|
|
72
85
|
hint?: ReactNode;
|
|
@@ -76,11 +89,25 @@ export interface ToggleGroupBaseProps<T extends string>
|
|
|
76
89
|
error?: ReactNode;
|
|
77
90
|
}
|
|
78
91
|
|
|
92
|
+
/**
|
|
93
|
+
* A line under the group saying what the CHOSEN option means — lenkbank's ToggleField
|
|
94
|
+
* `hint` (features/gear/common.tsx:97): not help behind a "?" but a caption, and one
|
|
95
|
+
* that changes as the choice does. Pass a function of the value for that; it is
|
|
96
|
+
* attached with `aria-describedby`, and a function caption is also a polite live
|
|
97
|
+
* region, because a description is read when the group is entered and not again when
|
|
98
|
+
* an arrow key changes the choice underneath it.
|
|
99
|
+
*
|
|
100
|
+
* In muted 11px text under the field (or the bare group), above an `error`.
|
|
101
|
+
*/
|
|
102
|
+
type ToggleGroupCaption<V> = ReactNode | ((value: V) => ReactNode);
|
|
103
|
+
|
|
79
104
|
/** The group as it has always been: one option is always the answer. */
|
|
80
105
|
export interface ToggleGroupRequiredProps<T extends string> extends ToggleGroupBaseProps<T> {
|
|
81
106
|
allowEmpty?: false;
|
|
82
107
|
value: T;
|
|
83
108
|
onChange: (value: T) => void;
|
|
109
|
+
/** See {@link ToggleGroupCaption}. */
|
|
110
|
+
caption?: ToggleGroupCaption<T>;
|
|
84
111
|
}
|
|
85
112
|
|
|
86
113
|
/**
|
|
@@ -97,6 +124,8 @@ export interface ToggleGroupClearableProps<T extends string> extends ToggleGroup
|
|
|
97
124
|
allowEmpty: true;
|
|
98
125
|
value: T | null;
|
|
99
126
|
onChange: (value: T | null) => void;
|
|
127
|
+
/** See {@link ToggleGroupCaption}. `null` while nothing is chosen. */
|
|
128
|
+
caption?: ToggleGroupCaption<T | null>;
|
|
100
129
|
}
|
|
101
130
|
|
|
102
131
|
/** `allowEmpty` picks the shape, so `onChange` is typed `(T) => void` unless the group
|
|
@@ -127,6 +156,7 @@ export function ToggleGroup<T extends string>(props: ToggleGroupProps<T>): React
|
|
|
127
156
|
disabled = false,
|
|
128
157
|
size = "md",
|
|
129
158
|
label,
|
|
159
|
+
labelPlacement = "field",
|
|
130
160
|
hint,
|
|
131
161
|
error,
|
|
132
162
|
"aria-label": ariaLabelAttr,
|
|
@@ -134,11 +164,21 @@ export function ToggleGroup<T extends string>(props: ToggleGroupProps<T>): React
|
|
|
134
164
|
} = props;
|
|
135
165
|
const labelId = useId();
|
|
136
166
|
const errorId = useId();
|
|
137
|
-
const
|
|
138
|
-
|
|
167
|
+
const labelled = label !== undefined && label !== null && label !== false && label !== "";
|
|
168
|
+
// `field` is the chrome; a label placed above keeps the bare group's own box.
|
|
169
|
+
const field = labelled && labelPlacement === "field";
|
|
170
|
+
const above = labelled && labelPlacement === "above";
|
|
171
|
+
const hasError = labelled && error !== undefined && error !== null && error !== false && error !== "";
|
|
139
172
|
// Taken off the rest so neither reaches the DOM; `props` keeps them paired, which is
|
|
140
173
|
// what lets the `onChange` below be called with `null` only in the mode that allows it.
|
|
141
|
-
const { allowEmpty: _allowEmpty, onChange: _onChange, ...rest } = restWithMode;
|
|
174
|
+
const { allowEmpty: _allowEmpty, onChange: _onChange, caption, ...rest } = restWithMode;
|
|
175
|
+
// Invalid from outside too: a `Field` hands the bare group `aria-invalid`, and the
|
|
176
|
+
// border has to say what the attribute says.
|
|
177
|
+
const outsideInvalid = rest["aria-invalid"] === true || rest["aria-invalid"] === "true";
|
|
178
|
+
const captionId = useId();
|
|
179
|
+
const captionIsLive = typeof caption === "function";
|
|
180
|
+
const captionNode = captionIsLive ? (caption as (v: T | null) => ReactNode)(value) : caption;
|
|
181
|
+
const hasCaption = captionNode !== undefined && captionNode !== null && captionNode !== false && captionNode !== "";
|
|
142
182
|
const choose = (next: T) => {
|
|
143
183
|
if (props.allowEmpty) props.onChange(next === value ? null : next);
|
|
144
184
|
else props.onChange(next);
|
|
@@ -182,10 +222,10 @@ export function ToggleGroup<T extends string>(props: ToggleGroupProps<T>): React
|
|
|
182
222
|
// The DOM spelling wins; `ariaLabel` is the fallback for the call sites that
|
|
183
223
|
// have not moved yet.
|
|
184
224
|
aria-label={ariaLabelAttr ?? ariaLabel}
|
|
185
|
-
aria-labelledby={
|
|
225
|
+
aria-labelledby={labelled && ariaLabelAttr === undefined && ariaLabel === undefined ? labelId : rest["aria-labelledby"]}
|
|
186
226
|
aria-invalid={hasError || rest["aria-invalid"] || undefined}
|
|
187
227
|
aria-describedby={
|
|
188
|
-
|
|
228
|
+
[rest["aria-describedby"], hasCaption && captionId, hasError && errorId].filter(Boolean).join(" ") || undefined
|
|
189
229
|
}
|
|
190
230
|
// `aria-disabled` on the group as well as `disabled` on each button: a radio
|
|
191
231
|
// group is what the user is being refused, and a screen reader announcing
|
|
@@ -209,10 +249,12 @@ export function ToggleGroup<T extends string>(props: ToggleGroupProps<T>): React
|
|
|
209
249
|
// package does; `cursor-not-allowed` is on the buttons, which is what a
|
|
210
250
|
// pointer is actually over.
|
|
211
251
|
disabled && "opacity-60",
|
|
252
|
+
// The bare group (no chrome to paint) wears the invalid border itself.
|
|
253
|
+
!field && (outsideInvalid || (above && hasError)) && FIELD_INVALID,
|
|
212
254
|
// Inside the field's chrome the group is only a row of segments: no border, no
|
|
213
255
|
// surface, no padding of its own, and the field (not the group) is what dims.
|
|
214
256
|
field && "border-0 bg-transparent p-0 shadow-none opacity-100",
|
|
215
|
-
!
|
|
257
|
+
!labelled && className,
|
|
216
258
|
)}
|
|
217
259
|
>
|
|
218
260
|
{options.map((opt, index) => {
|
|
@@ -273,7 +315,61 @@ export function ToggleGroup<T extends string>(props: ToggleGroupProps<T>): React
|
|
|
273
315
|
})}
|
|
274
316
|
</div>
|
|
275
317
|
);
|
|
276
|
-
|
|
318
|
+
// Rendered whenever the caption is — `aria-live` must be on the element BEFORE its
|
|
319
|
+
// text changes, or the change is not announced — but empty (no height, no margin)
|
|
320
|
+
// when there is nothing to say, so a function caption that returns null for some
|
|
321
|
+
// options leaves no gap. Empty rather than `hidden`: some readers do not announce
|
|
322
|
+
// text that appears inside an element coming back from `display: none`.
|
|
323
|
+
const captionEl =
|
|
324
|
+
hasCaption || captionIsLive ? (
|
|
325
|
+
<p
|
|
326
|
+
id={captionId}
|
|
327
|
+
aria-live={captionIsLive ? "polite" : undefined}
|
|
328
|
+
className={cn("text-[11px] leading-snug text-[var(--text-muted)]", hasCaption && "mt-1")}
|
|
329
|
+
>
|
|
330
|
+
{hasCaption ? captionNode : null}
|
|
331
|
+
</p>
|
|
332
|
+
) : null;
|
|
333
|
+
const errorEl = hasError ? (
|
|
334
|
+
<p id={errorId} className="mt-1 text-[11px] leading-tight text-[var(--danger)]">
|
|
335
|
+
{error}
|
|
336
|
+
</p>
|
|
337
|
+
) : null;
|
|
338
|
+
if (above) {
|
|
339
|
+
// `relative` so a caller's `sr-only` label cannot escape (sr-only-containment).
|
|
340
|
+
return (
|
|
341
|
+
<div className={cn("relative grid min-w-0 gap-1.5", className)}>
|
|
342
|
+
<div className="flex items-center gap-1">
|
|
343
|
+
{/* A `<label>` with no `htmlFor`: a group is not labelable, so it is named
|
|
344
|
+
by `aria-labelledby` on the group; the element keeps the Field look. */}
|
|
345
|
+
<Label
|
|
346
|
+
id={labelId}
|
|
347
|
+
disabled={disabled}
|
|
348
|
+
data-error={hasError || undefined}
|
|
349
|
+
className="data-[error=true]:text-[var(--danger)]"
|
|
350
|
+
>
|
|
351
|
+
{label}
|
|
352
|
+
</Label>
|
|
353
|
+
{hint}
|
|
354
|
+
</div>
|
|
355
|
+
<div className="min-w-0">
|
|
356
|
+
{group}
|
|
357
|
+
{captionEl}
|
|
358
|
+
{errorEl}
|
|
359
|
+
</div>
|
|
360
|
+
</div>
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
if (!field) {
|
|
364
|
+
if (!captionEl) return group;
|
|
365
|
+
// The group keeps its `className`, as without a caption; the wrapper only stacks.
|
|
366
|
+
return (
|
|
367
|
+
<div className="min-w-0">
|
|
368
|
+
{group}
|
|
369
|
+
{captionEl}
|
|
370
|
+
</div>
|
|
371
|
+
);
|
|
372
|
+
}
|
|
277
373
|
return (
|
|
278
374
|
// `h-full` + `flex-1`: the chrome fills a grid or stretched flex row, which is what
|
|
279
375
|
// levels it with a select beside it whatever the browser makes of the select.
|
|
@@ -294,11 +390,8 @@ export function ToggleGroup<T extends string>(props: ToggleGroupProps<T>): React
|
|
|
294
390
|
{group}
|
|
295
391
|
</div>
|
|
296
392
|
</FloatingField>
|
|
297
|
-
{
|
|
298
|
-
|
|
299
|
-
{error}
|
|
300
|
-
</p>
|
|
301
|
-
)}
|
|
393
|
+
{captionEl}
|
|
394
|
+
{errorEl}
|
|
302
395
|
</div>
|
|
303
396
|
);
|
|
304
397
|
}
|
|
@@ -68,14 +68,21 @@ export interface LegendEntry {
|
|
|
68
68
|
/**
|
|
69
69
|
* What the entry's mark looks like. `swatch` — a filled square, the default — says
|
|
70
70
|
* WHICH MEASUREMENT. `stroke` says WHICH QUANTITY, for the charts that carry both at
|
|
71
|
-
* once and tell them apart by colour and by dash.
|
|
71
|
+
* once and tell them apart by colour and by dash. `dot` is a round mark, for a
|
|
72
|
+
* MARKER on the chart (a point, an event) rather than a series.
|
|
72
73
|
*/
|
|
73
|
-
marker?: "swatch" | "stroke";
|
|
74
|
+
marker?: "swatch" | "stroke" | "dot";
|
|
74
75
|
/** Which of {@link STROKE_PATTERNS}, when the marker is a stroke — the same index
|
|
75
76
|
* `SeriesChartSeries.dash` takes, so a legend cannot promise a dot-dash the plot
|
|
76
77
|
* draws dashed. Or the same custom `stroke-dasharray` string the series draws with
|
|
77
78
|
* (keksdose's `"4 3"`). */
|
|
78
79
|
dash?: number | string;
|
|
80
|
+
/**
|
|
81
|
+
* A mark of the caller's own, drawn in place of {@link marker} — for a key whose
|
|
82
|
+
* mark is not a colour at all (a fading bar for "open-ended", a hairline for
|
|
83
|
+
* "today"). Decorative: it is wrapped `aria-hidden`, and the label is the name.
|
|
84
|
+
*/
|
|
85
|
+
icon?: ReactNode;
|
|
79
86
|
}
|
|
80
87
|
|
|
81
88
|
export interface ToggleLegendProps {
|
|
@@ -163,6 +170,65 @@ export function ToggleLegend({
|
|
|
163
170
|
);
|
|
164
171
|
}
|
|
165
172
|
|
|
173
|
+
export interface StaticLegendProps {
|
|
174
|
+
entries: LegendEntry[];
|
|
175
|
+
/** See {@link ToggleLegendProps.orientation}. */
|
|
176
|
+
orientation?: "horizontal" | "vertical";
|
|
177
|
+
/** The list's accessible name. Default: the `seriesChart.legend` label. */
|
|
178
|
+
"aria-label"?: string;
|
|
179
|
+
className?: string;
|
|
180
|
+
/** Per-instance strings over `<UiKitProvider labels={{ seriesChart }}>`. */
|
|
181
|
+
labels?: Partial<SeriesChartLabels>;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* A legend that is a KEY, not a control: the same entries and marks as
|
|
186
|
+
* {@link ToggleLegend} — swatch, stroke (with the chart's own dash), dot, or a mark of
|
|
187
|
+
* the caller's own — but nothing to press.
|
|
188
|
+
*
|
|
189
|
+
* A separate component rather than `ToggleLegend` without `onToggle`, because the two
|
|
190
|
+
* are different things to a screen reader: a group of switches announces a pressed
|
|
191
|
+
* state on every entry, a key is a LIST, read as "list, 4 items" and walked with the
|
|
192
|
+
* list keys, and costs no tab stops. The apps drew this by hand under the charts that
|
|
193
|
+
* say what a colour or a dash means without letting you switch it (kastlan's lease
|
|
194
|
+
* timeline, keksdose's cash buffer).
|
|
195
|
+
*
|
|
196
|
+
* Unlike `ToggleLegend` it draws a single entry: a key with one line still says what
|
|
197
|
+
* the one mark on the chart means.
|
|
198
|
+
*/
|
|
199
|
+
export function StaticLegend({
|
|
200
|
+
entries,
|
|
201
|
+
orientation = "horizontal",
|
|
202
|
+
"aria-label": ariaLabel,
|
|
203
|
+
className,
|
|
204
|
+
labels: labelsProp,
|
|
205
|
+
}: StaticLegendProps) {
|
|
206
|
+
const labels = useKitLabels("seriesChart", DEFAULT_SERIES_CHART_LABELS, labelsProp);
|
|
207
|
+
if (entries.length === 0) return null;
|
|
208
|
+
return (
|
|
209
|
+
<ul
|
|
210
|
+
aria-label={ariaLabel ?? labels.legend}
|
|
211
|
+
className={cn(
|
|
212
|
+
"m-0 flex list-none gap-x-3 gap-y-1 p-0",
|
|
213
|
+
orientation === "vertical"
|
|
214
|
+
? "flex-col items-start justify-center"
|
|
215
|
+
: "mt-2 flex-wrap items-center",
|
|
216
|
+
className,
|
|
217
|
+
)}
|
|
218
|
+
>
|
|
219
|
+
{entries.map((entry) => (
|
|
220
|
+
<li
|
|
221
|
+
key={entry.key}
|
|
222
|
+
className="flex items-center gap-1.5 text-[11px] text-[var(--text-secondary)]"
|
|
223
|
+
>
|
|
224
|
+
<LegendMark entry={entry} off={false} />
|
|
225
|
+
{entry.label}
|
|
226
|
+
</li>
|
|
227
|
+
))}
|
|
228
|
+
</ul>
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
166
232
|
/**
|
|
167
233
|
* The column a vertical legend stands in: beside the charts, on their horizontal
|
|
168
234
|
* centre line. `h-full` + `justify-center`, so it centres in a grid cell or a flex row
|
|
@@ -205,6 +271,25 @@ export function LegendGroup({ title, children }: { title: ReactNode; children: R
|
|
|
205
271
|
* and the line used to take its own 0.35 on top — 0.35 × 0.35, about 12 %, a mark
|
|
206
272
|
* gone rather than dimmed, while the square beside it read at the full 35 %. */
|
|
207
273
|
function LegendMark({ entry, off }: { entry: LegendEntry; off: boolean }) {
|
|
274
|
+
if (entry.icon != null) {
|
|
275
|
+
return (
|
|
276
|
+
<span aria-hidden className="inline-flex shrink-0 items-center">
|
|
277
|
+
{entry.icon}
|
|
278
|
+
</span>
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
if (entry.marker === "dot") {
|
|
282
|
+
return (
|
|
283
|
+
<span
|
|
284
|
+
aria-hidden
|
|
285
|
+
className="size-2 shrink-0 rounded-full"
|
|
286
|
+
style={{
|
|
287
|
+
backgroundColor: off ? "transparent" : entry.color,
|
|
288
|
+
boxShadow: `inset 0 0 0 1.5px ${entry.color}`,
|
|
289
|
+
}}
|
|
290
|
+
/>
|
|
291
|
+
);
|
|
292
|
+
}
|
|
208
293
|
if (entry.marker === "stroke") {
|
|
209
294
|
return (
|
|
210
295
|
<svg aria-hidden viewBox="0 0 20 2" className="h-0.5 w-5 shrink-0 overflow-visible">
|
|
@@ -17,6 +17,13 @@ import { useAnchoredRect, type AnchorRect } from "../hooks/use-anchored-rect";
|
|
|
17
17
|
import { dirOf, type Direction } from "../lib/direction";
|
|
18
18
|
import { hasClippingAncestor } from "../lib/clipping";
|
|
19
19
|
|
|
20
|
+
/** The attribute that marks an element as a clipping container for {@link Tooltip}'s
|
|
21
|
+
* auto-portal, whatever its computed `overflow` — `<div data-clips>` or
|
|
22
|
+
* `<div {...{ [CLIPS_ATTRIBUTE]: "" }}>`. Put it on an app's own scroller so a test
|
|
23
|
+
* environment without stylesheets (jsdom) portals the same tooltips the browser
|
|
24
|
+
* does. DataTable's body and Table's wrapper already carry it. */
|
|
25
|
+
export { CLIPS_ATTRIBUTE } from "../lib/clipping";
|
|
26
|
+
|
|
20
27
|
/** Where the bubble sits. `start` / `end` follow the reading direction — `end` is the
|
|
21
28
|
* right in LTR and the left in RTL — and are what a layout that mirrors should ask
|
|
22
29
|
* for. `left` / `right` stay physical, for a bubble tied to something that does not
|
|
@@ -75,8 +82,8 @@ export interface TooltipProps extends ComponentPropsWithoutRef<"span"> {
|
|
|
75
82
|
/**
|
|
76
83
|
* Where the bubble lives. Left out (the default since 0.10.0), the tooltip decides for
|
|
77
84
|
* itself: the bubble stays next to the trigger unless an ancestor clips or scrolls
|
|
78
|
-
* (`overflow` other than `visible
|
|
79
|
-
* scroll container" below. `true` always portals, `false` never does; both are exactly
|
|
85
|
+
* (`overflow` other than `visible`, or the {@link CLIPS_ATTRIBUTE} marker), in which
|
|
86
|
+
* case it is portalled — see "Inside a scroll container" below. `true` always portals, `false` never does; both are exactly
|
|
80
87
|
* what they were before the default existed.
|
|
81
88
|
*/
|
|
82
89
|
portal?: boolean;
|
|
@@ -144,7 +151,14 @@ type TooltipVariantProps = Omit<TooltipProps, "side" | "portal"> & { side: Toolt
|
|
|
144
151
|
* being one: every tooltip in a table, a drawer or a scrolling card had to remember it,
|
|
145
152
|
* and the ones that forgot were only found by someone scrolling sideways. So with
|
|
146
153
|
* `portal` left out, the tooltip looks for a clipping ancestor itself — any element
|
|
147
|
-
* between it and `<body>` whose computed `overflow-x` / `overflow-y` is not `visible
|
|
154
|
+
* between it and `<body>` whose computed `overflow-x` / `overflow-y` is not `visible`,
|
|
155
|
+
* or that carries {@link CLIPS_ATTRIBUTE} (`data-clips`). The marker is what makes the
|
|
156
|
+
* answer the same under test: jsdom computes no Tailwind, so there every scroller
|
|
157
|
+
* reads `visible` and a table-cell tooltip used to stay in place — its always-mounted
|
|
158
|
+
* bubble then repeated the label in the cell's accessible name and `textContent`
|
|
159
|
+
* ("CheckingChecking"), and apps pinned `portal` to stop it. The kit's own scrollers
|
|
160
|
+
* are marked, so a tooltip in a DataTable or Table cell portals in jsdom exactly as it
|
|
161
|
+
* does in the browser.
|
|
148
162
|
* It looks at MOUNT, not only on open: dev#488's phantom scroll is caused by a bubble
|
|
149
163
|
* nobody opened, so a check that waited for the hover would find the damage already
|
|
150
164
|
* done. It looks again on every open, for a container that started scrolling after
|