@eifi1/ui-kit 0.21.0 → 0.23.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 +42 -25
- package/dist/chart.d.ts +19 -14
- package/dist/components/account-chips.d.ts +10 -3
- package/dist/components/account-chips.js +5 -2
- package/dist/components/account-chips.js.map +1 -1
- package/dist/components/account-settings.d.ts +33 -2
- package/dist/components/account-settings.js +43 -21
- package/dist/components/account-settings.js.map +1 -1
- package/dist/components/amount-input.d.ts +53 -14
- package/dist/components/amount-input.js +17 -28
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/autocomplete.d.ts +29 -0
- package/dist/components/autocomplete.js +134 -104
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/button-group.d.ts +19 -14
- package/dist/components/calculator.d.ts +19 -14
- package/dist/components/checkbox-group.d.ts +104 -0
- package/dist/components/checkbox-group.js +111 -0
- package/dist/components/checkbox-group.js.map +1 -0
- package/dist/components/checkbox.d.ts +19 -0
- package/dist/components/checkbox.js +30 -7
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/chip.d.ts +18 -0
- package/dist/components/chip.js +47 -10
- package/dist/components/chip.js.map +1 -1
- package/dist/components/column-mapper.d.ts +182 -0
- package/dist/components/column-mapper.js +375 -0
- package/dist/components/column-mapper.js.map +1 -0
- package/dist/components/combobox-core.d.ts +39 -2
- package/dist/components/combobox-core.js +29 -6
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox.d.ts +91 -2
- package/dist/components/combobox.js +454 -308
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/confirm-dialog.d.ts +69 -161
- package/dist/components/confirm-dialog.js +7 -8
- package/dist/components/confirm-dialog.js.map +1 -1
- package/dist/components/copy-button.d.ts +16 -11
- package/dist/components/country-select.d.ts +167 -0
- package/dist/components/country-select.js +293 -0
- package/dist/components/country-select.js.map +1 -0
- package/dist/components/currency-select.d.ts +17 -1
- package/dist/components/currency-select.js +56 -23
- package/dist/components/currency-select.js.map +1 -1
- package/dist/components/danger-confirm.d.ts +69 -166
- package/dist/components/danger-confirm.js +144 -50
- package/dist/components/danger-confirm.js.map +1 -1
- package/dist/components/data-table-cells.d.ts +1 -2
- package/dist/components/data-table-filter-popover.d.ts +1 -2
- package/dist/components/data-table-filters.d.ts +1 -2
- package/dist/components/data-table-labels.d.ts +4 -97
- package/dist/components/data-table-labels.js +9 -1
- 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.d.ts +1 -2
- package/dist/components/data-table.js +98 -3
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.d.ts +66 -5
- package/dist/components/date-picker.js +378 -66
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/entity-combobox.d.ts +42 -1
- package/dist/components/entity-combobox.js +154 -113
- package/dist/components/entity-combobox.js.map +1 -1
- package/dist/components/facing-pair.d.ts +19 -14
- package/dist/components/field-parts.d.ts +224 -0
- package/dist/components/field-parts.js +156 -0
- package/dist/components/field-parts.js.map +1 -0
- package/dist/components/field-strip.d.ts +105 -0
- package/dist/components/field-strip.js +46 -0
- package/dist/components/field-strip.js.map +1 -0
- package/dist/components/file-button.d.ts +19 -14
- package/dist/components/file-button.js +13 -3
- package/dist/components/file-button.js.map +1 -1
- package/dist/components/file-dropzone.d.ts +41 -15
- package/dist/components/file-dropzone.js +57 -34
- package/dist/components/file-dropzone.js.map +1 -1
- package/dist/components/form-actions.d.ts +18 -13
- package/dist/components/form-actions.js +69 -5
- package/dist/components/form-actions.js.map +1 -1
- package/dist/components/iban-input.d.ts +69 -0
- package/dist/components/iban-input.js +125 -0
- package/dist/components/iban-input.js.map +1 -0
- package/dist/components/icon-picker.d.ts +21 -2
- package/dist/components/icon-picker.js +19 -4
- package/dist/components/icon-picker.js.map +1 -1
- package/dist/components/inline-edit-field.d.ts +167 -0
- package/dist/components/inline-edit-field.js +293 -0
- package/dist/components/inline-edit-field.js.map +1 -0
- package/dist/components/language-select.d.ts +130 -0
- package/dist/components/language-select.js +18 -0
- package/dist/components/language-select.js.map +1 -0
- package/dist/components/line-items.d.ts +25 -3
- package/dist/components/line-items.js +55 -46
- package/dist/components/line-items.js.map +1 -1
- package/dist/components/list.d.ts +25 -0
- package/dist/components/list.js +16 -5
- package/dist/components/list.js.map +1 -1
- package/dist/components/money-field.d.ts +32 -14
- package/dist/components/money-field.js.map +1 -1
- package/dist/components/month-picker.d.ts +52 -4
- package/dist/components/month-picker.js +291 -86
- package/dist/components/month-picker.js.map +1 -1
- package/dist/components/multi-entity-combobox.d.ts +26 -1
- package/dist/components/multi-entity-combobox.js +143 -102
- package/dist/components/multi-entity-combobox.js.map +1 -1
- package/dist/components/multi-select.d.ts +8 -1
- package/dist/components/multi-select.js +9 -4
- package/dist/components/multi-select.js.map +1 -1
- package/dist/components/number-field.d.ts +34 -19
- package/dist/components/number-field.js +6 -20
- package/dist/components/number-field.js.map +1 -1
- package/dist/components/number-input.d.ts +79 -19
- package/dist/components/number-input.js +90 -38
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/numpad-sheet.d.ts +19 -14
- package/dist/components/one-time-code-input.d.ts +100 -0
- package/dist/components/one-time-code-input.js +137 -0
- package/dist/components/one-time-code-input.js.map +1 -0
- package/dist/components/phone-input.d.ts +69 -0
- package/dist/components/phone-input.js +171 -0
- package/dist/components/phone-input.js.map +1 -0
- package/dist/components/reauth-dialog.d.ts +2 -2
- package/dist/components/reauth-dialog.js +6 -8
- package/dist/components/reauth-dialog.js.map +1 -1
- package/dist/components/series-chart.d.ts +19 -14
- package/dist/components/settings-fields.d.ts +19 -14
- package/dist/components/share-card.d.ts +16 -11
- package/dist/components/sign-chip.d.ts +66 -0
- package/dist/components/sign-chip.js +39 -0
- package/dist/components/sign-chip.js.map +1 -0
- package/dist/components/swatch-picker.d.ts +43 -2
- package/dist/components/swatch-picker.js +80 -32
- package/dist/components/swatch-picker.js.map +1 -1
- package/dist/components/switch.d.ts +19 -0
- package/dist/components/switch.js +24 -6
- package/dist/components/switch.js.map +1 -1
- package/dist/components/text-link.d.ts +18 -13
- package/dist/components/tile-radio.d.ts +88 -13
- package/dist/components/tile-radio.js +28 -11
- package/dist/components/tile-radio.js.map +1 -1
- package/dist/components/time-input.d.ts +24 -15
- package/dist/components/time-input.js.map +1 -1
- package/dist/components/toggle-group.d.ts +47 -1
- package/dist/components/toggle-group.js +110 -69
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/trigger-aria.d.ts +3 -0
- package/dist/components/trigger-aria.js +3 -1
- package/dist/components/trigger-aria.js.map +1 -1
- package/dist/components/ui.d.ts +16 -11
- package/dist/components/ui.js +284 -88
- package/dist/components/ui.js.map +1 -1
- package/dist/components/use-table-state.d.ts +1 -2
- package/dist/{data-table-3mi2R3so.d.ts → data-table-labels-B7OdnM0S.d.ts} +140 -3
- package/dist/data-table.d.ts +1 -2
- package/dist/data-table.js +2 -1
- package/dist/data-table.js.map +1 -1
- package/dist/feedback/feedback-attachment.d.ts +1 -1
- package/dist/feedback/feedback-attachment.js +348 -41
- package/dist/feedback/feedback-attachment.js.map +1 -1
- package/dist/feedback/feedback-dialog.d.ts +1 -1
- package/dist/feedback/feedback-dialog.js.map +1 -1
- package/dist/feedback/feedback-inbox.d.ts +3 -2
- package/dist/feedback/feedback-inbox.js.map +1 -1
- package/dist/feedback/feedback-thread.d.ts +68 -253
- package/dist/feedback/feedback-thread.js +46 -10
- package/dist/feedback/feedback-thread.js.map +1 -1
- package/dist/{kit-labels-D1J2_jyD.d.ts → feedback-BxeQVzwq.d.ts} +1206 -20
- package/dist/{feedback-attachment-WFiXJ8We.d.ts → feedback-attachment-fGAzZPf0.d.ts} +178 -11
- package/dist/feedback.d.ts +66 -2
- package/dist/hooks/use-file-drop.d.ts +19 -14
- package/dist/i18n/defaults.d.ts +19 -14
- package/dist/i18n/defaults.js +19 -2
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.d.ts +19 -14
- package/dist/i18n/german.js +84 -5
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +15 -10
- package/dist/i18n/kit-labels.js +8 -2
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/languages.d.ts +19 -14
- package/dist/i18n/locales/de-CH.d.ts +19 -14
- package/dist/i18n/locales/en.d.ts +19 -14
- package/dist/i18n/locales/en.js +16 -0
- package/dist/i18n/locales/en.js.map +1 -1
- package/dist/i18n/locales/es.d.ts +19 -14
- package/dist/i18n/locales/es.js +83 -5
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +19 -14
- package/dist/i18n/locales/fr.js +82 -5
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +19 -14
- package/dist/i18n/locales/hu.js +85 -5
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +19 -14
- package/dist/i18n/locales/it.js +84 -5
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +19 -14
- package/dist/i18n/locales/zh.js +80 -5
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.d.ts +19 -14
- package/dist/i18n/review.js +27 -1
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +21 -10
- package/dist/index.js +73 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/column-mapping.d.ts +81 -0
- package/dist/lib/column-mapping.js +108 -0
- package/dist/lib/column-mapping.js.map +1 -0
- package/dist/lib/countries.d.ts +43 -0
- package/dist/lib/countries.js +39 -0
- package/dist/lib/countries.js.map +1 -0
- package/dist/lib/decimal-marks.d.ts +46 -0
- package/dist/lib/decimal-marks.js +40 -0
- package/dist/lib/decimal-marks.js.map +1 -0
- package/dist/lib/iban.d.ts +98 -0
- package/dist/lib/iban.js +154 -0
- package/dist/lib/iban.js.map +1 -0
- package/dist/lib/isin.d.ts +30 -0
- package/dist/lib/isin.js +36 -0
- package/dist/lib/isin.js.map +1 -0
- package/dist/lib/phone.d.ts +69 -0
- package/dist/lib/phone.js +121 -0
- package/dist/lib/phone.js.map +1 -0
- package/dist/lib/table-text.d.ts +109 -1
- package/dist/lib/table-text.js +122 -1
- package/dist/lib/table-text.js.map +1 -1
- package/dist/rhf/fields.d.ts +233 -18
- package/dist/rhf/fields.js +456 -3
- package/dist/rhf/fields.js.map +1 -1
- package/dist/rhf/form.d.ts +19 -14
- package/dist/rhf.d.ts +22 -15
- package/dist/rhf.js.map +1 -1
- package/dist/shell/app-shell.d.ts +18 -13
- package/dist/shell/top-bar-brand.d.ts +19 -14
- package/dist/shell/topbar-action-menu.d.ts +36 -3
- package/dist/shell/topbar-action-menu.js +74 -33
- package/dist/shell/topbar-action-menu.js.map +1 -1
- package/dist/shell.d.ts +18 -13
- package/dist/table-text.d.ts +1 -1
- package/dist/tour/tour.js +20 -24
- package/dist/tour/tour.js.map +1 -1
- package/dist/wizard/stepper-nav.d.ts +49 -16
- package/dist/wizard/stepper-nav.js +4 -0
- package/dist/wizard/stepper-nav.js.map +1 -1
- package/dist/wizard.d.ts +19 -14
- package/package.json +1 -1
- package/src/components/account-chips.tsx +17 -3
- package/src/components/account-settings.tsx +92 -26
- package/src/components/amount-input.tsx +68 -56
- package/src/components/autocomplete.tsx +198 -114
- package/src/components/checkbox-group.tsx +229 -0
- package/src/components/checkbox.tsx +72 -8
- package/src/components/chip.tsx +80 -10
- package/src/components/column-mapper.tsx +666 -0
- package/src/components/combobox-core.tsx +82 -9
- package/src/components/combobox.tsx +661 -340
- package/src/components/confirm-dialog.tsx +12 -8
- package/src/components/country-select.tsx +556 -0
- package/src/components/currency-select.tsx +109 -33
- package/src/components/danger-confirm.tsx +429 -82
- package/src/components/data-table-labels.ts +16 -1
- package/src/components/data-table.tsx +175 -6
- package/src/components/date-picker.tsx +595 -71
- package/src/components/entity-combobox.tsx +235 -124
- package/src/components/field-parts.tsx +403 -0
- package/src/components/field-strip.tsx +149 -0
- package/src/components/file-button.tsx +36 -3
- package/src/components/file-dropzone.tsx +106 -38
- package/src/components/form-actions.tsx +147 -6
- package/src/components/iban-input.tsx +231 -0
- package/src/components/icon-picker.tsx +47 -6
- package/src/components/inline-edit-field.tsx +533 -0
- package/src/components/language-select.tsx +78 -0
- package/src/components/line-items.tsx +35 -2
- package/src/components/list.tsx +55 -6
- package/src/components/money-field.tsx +13 -0
- package/src/components/month-picker.tsx +400 -63
- package/src/components/multi-entity-combobox.tsx +207 -120
- package/src/components/multi-select.tsx +80 -58
- package/src/components/number-field.tsx +28 -46
- package/src/components/number-input.tsx +210 -43
- package/src/components/one-time-code-input.tsx +288 -0
- package/src/components/phone-input.tsx +299 -0
- package/src/components/reauth-dialog.tsx +17 -18
- package/src/components/sign-chip.tsx +103 -0
- package/src/components/swatch-picker.tsx +143 -31
- package/src/components/switch.tsx +58 -7
- package/src/components/tile-radio.tsx +145 -29
- package/src/components/time-input.tsx +8 -1
- package/src/components/toggle-group.tsx +206 -73
- package/src/components/trigger-aria.ts +5 -0
- package/src/components/ui.tsx +479 -138
- package/src/data-table.ts +2 -1
- package/src/feedback/feedback-attachment.tsx +597 -46
- package/src/feedback/feedback-dialog.tsx +9 -3
- package/src/feedback/feedback-inbox.tsx +3 -2
- package/src/feedback/feedback-thread.tsx +113 -9
- package/src/i18n/defaults.ts +18 -1
- package/src/i18n/german.ts +89 -0
- package/src/i18n/kit-labels.tsx +82 -2
- package/src/i18n/locales/en.ts +36 -4
- package/src/i18n/locales/es.ts +87 -0
- package/src/i18n/locales/fr.ts +87 -0
- package/src/i18n/locales/hu.ts +83 -0
- package/src/i18n/locales/it.ts +88 -0
- package/src/i18n/locales/zh.ts +76 -0
- package/src/i18n/review.ts +26 -0
- package/src/index.ts +71 -3
- package/src/lib/column-mapping.ts +234 -0
- package/src/lib/countries.ts +91 -0
- package/src/lib/decimal-marks.ts +99 -0
- package/src/lib/iban.ts +171 -0
- package/src/lib/isin.ts +62 -0
- package/src/lib/phone.ts +211 -0
- package/src/lib/table-text.ts +271 -0
- package/src/rhf/fields.tsx +860 -9
- package/src/rhf.ts +3 -1
- package/src/shell/topbar-action-menu.tsx +134 -38
- package/src/tour/tour.tsx +66 -28
- package/src/wizard/stepper-nav.tsx +35 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { forwardRef, useCallback, useEffect, useId, useRef } from "react";
|
|
1
|
+
import { forwardRef, useCallback, useContext, useEffect, useId, useRef } from "react";
|
|
2
2
|
import { Check, Minus } from "lucide-react";
|
|
3
3
|
import type { ChangeEvent, InputHTMLAttributes, ReactNode } from "react";
|
|
4
4
|
import { cn } from "../lib/cn";
|
|
5
5
|
import { assignRef, hasMessage, mergeDescribedBy } from "./choice-parts";
|
|
6
|
+
import { LockedReason, RequiredStarOnLegend, useLockReason } from "./field-parts";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* A checkbox that is still `<input type="checkbox">`.
|
|
@@ -87,6 +88,25 @@ export interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement
|
|
|
87
88
|
* label WITHOUT a literal "*". No mark without a label.
|
|
88
89
|
*/
|
|
89
90
|
required?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Why the box cannot be changed — {@link Button}'s `disabledReason`, for a checkbox
|
|
93
|
+
* that SAVES on change (keksdose K3: a setting ticked straight into the server, kept
|
|
94
|
+
* behind a hand-rolled `SaveGuard` that forced a native `disabled` — out of the tab
|
|
95
|
+
* order, so the reason never reached a keyboard).
|
|
96
|
+
*
|
|
97
|
+
* With a reason the box is `aria-disabled` instead: still focusable, a click or Space
|
|
98
|
+
* changes nothing and fires no `onChange` / `onCheckedChange`, the reason is in the
|
|
99
|
+
* kit {@link Tooltip} on the box and on its `aria-describedby`, and the row fades as a
|
|
100
|
+
* disabled one does. It wins over `disabled`. Changing the lock mounts the box anew (it
|
|
101
|
+
* moves in or out of the Tooltip), as a locked Button is.
|
|
102
|
+
*/
|
|
103
|
+
disabledReason?: ReactNode;
|
|
104
|
+
/**
|
|
105
|
+
* This box COMMITS — ticking it saves. Under a locked {@link WriteLockProvider} it is
|
|
106
|
+
* locked the `disabledReason` way with the lock's reason (which wins over its own).
|
|
107
|
+
* No provider, or an unlocked one: no effect. Button's `commit`, for keksdose K3.
|
|
108
|
+
*/
|
|
109
|
+
commit?: boolean;
|
|
90
110
|
}
|
|
91
111
|
|
|
92
112
|
/**
|
|
@@ -113,12 +133,22 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(function Che
|
|
|
113
133
|
id,
|
|
114
134
|
disabled,
|
|
115
135
|
required,
|
|
136
|
+
disabledReason,
|
|
137
|
+
commit,
|
|
138
|
+
onClick,
|
|
116
139
|
...rest
|
|
117
140
|
},
|
|
118
141
|
ref,
|
|
119
142
|
) {
|
|
120
143
|
const generated = useId();
|
|
121
144
|
const inputId = id ?? generated;
|
|
145
|
+
const lock = useLockReason(commit, disabledReason);
|
|
146
|
+
const locked = lock.locked;
|
|
147
|
+
// Locked is disabled the focusable way: the row fades and the words stop inviting a
|
|
148
|
+
// click exactly as they do for `disabled`.
|
|
149
|
+
const looksDisabled = Boolean(disabled) || locked;
|
|
150
|
+
// In a CheckboxGroup the legend carries the star; the box keeps `required` itself.
|
|
151
|
+
const starOnLegend = useContext(RequiredStarOnLegend);
|
|
122
152
|
const descriptionId = `${inputId}-description`;
|
|
123
153
|
const errorId = `${inputId}-error`;
|
|
124
154
|
const showError = hasMessage(error);
|
|
@@ -142,29 +172,53 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(function Che
|
|
|
142
172
|
if (local.current) local.current.indeterminate = indeterminate;
|
|
143
173
|
});
|
|
144
174
|
|
|
145
|
-
const
|
|
146
|
-
<span className={cn("relative inline-flex shrink-0", bare && className)}>
|
|
175
|
+
const control = (
|
|
176
|
+
<span className={cn("relative inline-flex shrink-0", bare && !locked && className)}>
|
|
147
177
|
<input
|
|
148
178
|
ref={setRef}
|
|
149
179
|
id={bare ? id : inputId}
|
|
150
|
-
disabled
|
|
180
|
+
// A reason wins over `disabled`, as on Button: the point of giving one is that
|
|
181
|
+
// the box stays reachable.
|
|
182
|
+
disabled={locked ? undefined : disabled}
|
|
151
183
|
required={required}
|
|
152
184
|
{...rest}
|
|
153
185
|
// After the spread, like Switch: a props object spread at a checkbox must not
|
|
154
186
|
// be able to turn it into something else.
|
|
155
187
|
type="checkbox"
|
|
188
|
+
aria-disabled={locked || rest["aria-disabled"] || undefined}
|
|
156
189
|
// OR-ed with the spread so a caller's own `aria-invalid` survives — Input's rule.
|
|
157
190
|
aria-invalid={isInvalid || rest["aria-invalid"] || undefined}
|
|
158
191
|
aria-describedby={mergeDescribedBy(
|
|
159
192
|
rest["aria-describedby"],
|
|
160
193
|
showDescription && descriptionId,
|
|
194
|
+
locked && lock.reasonId,
|
|
161
195
|
showError && errorId,
|
|
162
196
|
)}
|
|
197
|
+
// A locked box's click is swallowed, as a locked Button's is.
|
|
198
|
+
onClick={locked ? undefined : onClick}
|
|
163
199
|
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
|
200
|
+
if (locked) {
|
|
201
|
+
// Put the tick back, and report nothing. Undone here rather than by
|
|
202
|
+
// cancelling the click: a cancelled click is reverted by the browser AFTER
|
|
203
|
+
// React has restored a controlled box, and jsdom reverts it by toggling, which
|
|
204
|
+
// turned a restored tick back over (the suites the apps run would have seen a
|
|
205
|
+
// locked box change). Setting the property also keeps React's own record of
|
|
206
|
+
// the value in step, so an uncontrolled box reports its next real change.
|
|
207
|
+
// Covers a click on the label and Space too — both arrive as a click.
|
|
208
|
+
e.currentTarget.checked = !e.currentTarget.checked;
|
|
209
|
+
e.currentTarget.indeterminate = indeterminate;
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
164
212
|
onChange?.(e);
|
|
165
213
|
onCheckedChange?.(e.target.checked);
|
|
166
214
|
}}
|
|
167
|
-
className={cn(
|
|
215
|
+
className={cn(
|
|
216
|
+
BOX,
|
|
217
|
+
indeterminate && BOX_MIXED,
|
|
218
|
+
isInvalid && BOX_INVALID,
|
|
219
|
+
locked && "cursor-not-allowed",
|
|
220
|
+
inputClassName,
|
|
221
|
+
)}
|
|
168
222
|
/>
|
|
169
223
|
{/* The glyph is drawn over the box, not inside it (an input has no children),
|
|
170
224
|
and ignores the pointer so a click on the tick still toggles the input. The
|
|
@@ -185,6 +239,16 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(function Che
|
|
|
185
239
|
)}
|
|
186
240
|
</span>
|
|
187
241
|
);
|
|
242
|
+
// The reason's Tooltip goes round the BOX, not the row: the row is the caller's
|
|
243
|
+
// layout (`className`), and a wrapper round it would move that into a span. A bare
|
|
244
|
+
// box hands its `className` to the wrapper, which is then the outermost element.
|
|
245
|
+
const box = locked ? (
|
|
246
|
+
<LockedReason lock={lock} className={cn("shrink-0", bare && className)}>
|
|
247
|
+
{control}
|
|
248
|
+
</LockedReason>
|
|
249
|
+
) : (
|
|
250
|
+
control
|
|
251
|
+
);
|
|
188
252
|
|
|
189
253
|
if (bare) return box;
|
|
190
254
|
|
|
@@ -197,7 +261,7 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(function Che
|
|
|
197
261
|
"flex items-start gap-2",
|
|
198
262
|
// The whole row fades, the way ToggleGroup and the fields do, so the label does
|
|
199
263
|
// not stay at full strength beside a box you cannot change.
|
|
200
|
-
|
|
264
|
+
looksDisabled && "opacity-60",
|
|
201
265
|
className,
|
|
202
266
|
)}
|
|
203
267
|
>
|
|
@@ -210,11 +274,11 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(function Che
|
|
|
210
274
|
"block text-sm leading-5 text-[var(--text-primary)]",
|
|
211
275
|
// `select-none`: a double-click to toggle twice would otherwise select the
|
|
212
276
|
// words, and a drag that starts on them starts a native text drag.
|
|
213
|
-
|
|
277
|
+
looksDisabled ? "cursor-not-allowed" : "cursor-pointer select-none",
|
|
214
278
|
)}
|
|
215
279
|
>
|
|
216
280
|
{label}
|
|
217
|
-
{required && (
|
|
281
|
+
{required && !starOnLegend && (
|
|
218
282
|
<span aria-hidden className="ms-0.5 text-[var(--danger)]">
|
|
219
283
|
*
|
|
220
284
|
</span>
|
package/src/components/chip.tsx
CHANGED
|
@@ -7,6 +7,8 @@ import { horizontalStep } from "../lib/direction";
|
|
|
7
7
|
import { FIELD_INVALID } from "./ui";
|
|
8
8
|
import { DEFAULT_COMMON_LABELS, useKitLabels, useKitLink, useKitLocale } from "../i18n/kit-labels";
|
|
9
9
|
import { pickLinkRenderer } from "./text-link";
|
|
10
|
+
import { Tooltip } from "./tooltip";
|
|
11
|
+
import { useCommitReason } from "./write-lock";
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* A chip: a compact pill carrying one value.
|
|
@@ -623,6 +625,8 @@ export type ChipProps = ChipBaseProps &
|
|
|
623
625
|
* href, whichever element draws it.
|
|
624
626
|
*/
|
|
625
627
|
renderLink?: (props: ChipLinkProps) => ReactElement;
|
|
628
|
+
commit?: never;
|
|
629
|
+
disabledReason?: never;
|
|
626
630
|
}
|
|
627
631
|
| {
|
|
628
632
|
/** The link shape again, with an `onClick`. `href` is a required string here,
|
|
@@ -636,6 +640,8 @@ export type ChipProps = ChipBaseProps &
|
|
|
636
640
|
onClick?: (event: MouseEvent<HTMLAnchorElement>) => void;
|
|
637
641
|
/** See the link shape above. */
|
|
638
642
|
renderLink?: (props: ChipLinkProps) => ReactElement;
|
|
643
|
+
commit?: never;
|
|
644
|
+
disabledReason?: never;
|
|
639
645
|
}
|
|
640
646
|
| {
|
|
641
647
|
href?: never;
|
|
@@ -643,6 +649,20 @@ export type ChipProps = ChipBaseProps &
|
|
|
643
649
|
/** Renders the chip as a toggle button. Mutually exclusive with `href`. Receives
|
|
644
650
|
* the click, so a chip inside a clickable row can `stopPropagation()`. */
|
|
645
651
|
onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
652
|
+
/**
|
|
653
|
+
* Why the toggle cannot be pressed right now — {@link Button}'s
|
|
654
|
+
* `disabledReason`, on the chip's button shape (keksdose K3: a filter or flag
|
|
655
|
+
* chip that saves on press, under a read-only lock). The chip stays focusable
|
|
656
|
+
* and `aria-disabled`, a press (and its `onRemove` ×) does nothing, and the
|
|
657
|
+
* reason shows in the kit {@link Tooltip} and describes the chip. Wins over
|
|
658
|
+
* `disabled`. A link chip navigates rather than commits, so it takes neither
|
|
659
|
+
* this nor `commit`; an inert chip has nothing to refuse.
|
|
660
|
+
*/
|
|
661
|
+
disabledReason?: ReactNode;
|
|
662
|
+
/** This chip COMMITS — pressing it saves. Under a locked
|
|
663
|
+
* {@link WriteLockProvider} it takes the `disabledReason` path with the lock's
|
|
664
|
+
* reason, as {@link Button}'s `commit` does. */
|
|
665
|
+
commit?: boolean;
|
|
646
666
|
}
|
|
647
667
|
);
|
|
648
668
|
|
|
@@ -678,11 +698,17 @@ export const Chip = forwardRef<HTMLElement, ChipProps>(function Chip(
|
|
|
678
698
|
onKeyDown,
|
|
679
699
|
onFocus,
|
|
680
700
|
snapEdges = false,
|
|
701
|
+
disabledReason: ownDisabledReason,
|
|
702
|
+
commit,
|
|
681
703
|
...rest
|
|
682
704
|
},
|
|
683
705
|
ref,
|
|
684
706
|
) {
|
|
685
707
|
const common = useKitLabels("common", DEFAULT_COMMON_LABELS, { remove: removeLabel });
|
|
708
|
+
const reasonId = useId();
|
|
709
|
+
const disabledReason = useCommitReason(commit, ownDisabledReason);
|
|
710
|
+
// Only the BUTTON shape commits: a link navigates, an inert chip does nothing.
|
|
711
|
+
const locked = Boolean(onClick) && !href && hasReason(disabledReason);
|
|
686
712
|
const kitLink = useKitLink();
|
|
687
713
|
const s = SIZE[size];
|
|
688
714
|
const interactive = !!href || !!onClick;
|
|
@@ -694,7 +720,10 @@ export const Chip = forwardRef<HTMLElement, ChipProps>(function Chip(
|
|
|
694
720
|
const type = cn(caps && CAPS[size], variant === "solid" && "justify-center tabular-nums");
|
|
695
721
|
const surface = cn(
|
|
696
722
|
surfaceOf(tone, variant, selected),
|
|
697
|
-
disabled
|
|
723
|
+
// A reason wins over `disabled`: the focusable kind of disabled, with the cursor
|
|
724
|
+
// that says "refused" rather than "nothing here".
|
|
725
|
+
disabled && !locked && "cursor-default opacity-50",
|
|
726
|
+
locked && "cursor-not-allowed opacity-50",
|
|
698
727
|
);
|
|
699
728
|
const snap = snapEdges && !dot;
|
|
700
729
|
const snapRef = useSnapEdges(snap);
|
|
@@ -713,7 +742,7 @@ export const Chip = forwardRef<HTMLElement, ChipProps>(function Chip(
|
|
|
713
742
|
type,
|
|
714
743
|
radius,
|
|
715
744
|
surface,
|
|
716
|
-
interactive && !disabled && "cursor-pointer hover:brightness-[0.97] dark:hover:brightness-110",
|
|
745
|
+
interactive && !disabled && !locked && "cursor-pointer hover:brightness-[0.97] dark:hover:brightness-110",
|
|
717
746
|
// An inert dot chip is text in a column: no inset, so it aligns with the header.
|
|
718
747
|
dot && !interactive && "px-0",
|
|
719
748
|
// The under-4px a snap adds is split between the two sides, not stacked at the end.
|
|
@@ -781,15 +810,21 @@ export const Chip = forwardRef<HTMLElement, ChipProps>(function Chip(
|
|
|
781
810
|
// The chip may itself be a link; removing it must not also follow it.
|
|
782
811
|
e.preventDefault();
|
|
783
812
|
e.stopPropagation();
|
|
813
|
+
// Removing a value from a chip that commits is a commit too.
|
|
814
|
+
if (locked) return;
|
|
784
815
|
onRemove();
|
|
785
816
|
}}
|
|
786
|
-
disabled={disabled || removeDisabled}
|
|
817
|
+
disabled={(disabled && !locked) || removeDisabled}
|
|
818
|
+
aria-disabled={locked || undefined}
|
|
819
|
+
aria-describedby={locked ? reasonId : undefined}
|
|
787
820
|
className={cn(
|
|
788
821
|
// Logical margins: the × sits at the END of the pill, which is the left in RTL.
|
|
789
822
|
"-me-0.5 ms-0.5 shrink-0 p-0.5 transition-colors",
|
|
790
823
|
shape === "square" ? "rounded-sm" : "rounded-full",
|
|
791
|
-
"
|
|
792
|
-
|
|
824
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand)]",
|
|
825
|
+
!locked && "hover:bg-[var(--bg-active)]",
|
|
826
|
+
locked && "cursor-not-allowed",
|
|
827
|
+
((disabled && !locked) || removeDisabled) && "pointer-events-none",
|
|
793
828
|
// A disabled chip is already dimmed as a whole; dimming the × again on top
|
|
794
829
|
// would take it to a quarter. `removeDisabled` alone dims just the ×.
|
|
795
830
|
removeDisabled && !disabled && "opacity-40",
|
|
@@ -814,8 +849,9 @@ export const Chip = forwardRef<HTMLElement, ChipProps>(function Chip(
|
|
|
814
849
|
CHIP_RING,
|
|
815
850
|
s.split,
|
|
816
851
|
type,
|
|
817
|
-
!disabled && "cursor-pointer hover:bg-current/10",
|
|
818
|
-
disabled && "cursor-default",
|
|
852
|
+
!disabled && !locked && "cursor-pointer hover:bg-current/10",
|
|
853
|
+
disabled && !locked && "cursor-default",
|
|
854
|
+
locked && "cursor-not-allowed",
|
|
819
855
|
);
|
|
820
856
|
|
|
821
857
|
// ── The three shapes.
|
|
@@ -855,12 +891,21 @@ export const Chip = forwardRef<HTMLElement, ChipProps>(function Chip(
|
|
|
855
891
|
// turn it into a button instead — it falls through to the inert span, as before.
|
|
856
892
|
if (onClick && !href) {
|
|
857
893
|
const onButtonClick = onClick as (event: MouseEvent<HTMLButtonElement>) => void;
|
|
894
|
+
const ownDescribedBy = rest["aria-describedby"];
|
|
858
895
|
const button = (
|
|
859
896
|
<button
|
|
860
897
|
ref={(remove ? ref : ownRef) as React.Ref<HTMLButtonElement>}
|
|
861
898
|
type="button"
|
|
862
|
-
onClick={
|
|
863
|
-
|
|
899
|
+
onClick={(e) => {
|
|
900
|
+
// Locked: the press is swallowed — the chip saves on press, and a lock that
|
|
901
|
+
// let it through would toggle a state that is never stored.
|
|
902
|
+
if (locked) {
|
|
903
|
+
e.preventDefault();
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
onButtonClick(e);
|
|
907
|
+
}}
|
|
908
|
+
disabled={disabled && !locked}
|
|
864
909
|
// Only when `selected` is PASSED: a chip with `onClick` and no `selected` is an
|
|
865
910
|
// action button (it does something), not a toggle (it is on or off), and
|
|
866
911
|
// announcing it "not pressed" would claim a state it does not have.
|
|
@@ -871,11 +916,30 @@ export const Chip = forwardRef<HTMLElement, ChipProps>(function Chip(
|
|
|
871
916
|
aria-checked={isCheckbox ? !!selected : undefined}
|
|
872
917
|
className={remove ? inner : look}
|
|
873
918
|
{...rest}
|
|
919
|
+
// After the spread, merged with it: the lock's state and reason are not the
|
|
920
|
+
// caller's to drop by passing an `aria-describedby` of their own.
|
|
921
|
+
aria-disabled={locked || (rest["aria-disabled"] as boolean | "true" | "false" | undefined)}
|
|
922
|
+
aria-describedby={
|
|
923
|
+
locked ? [ownDescribedBy, reasonId].filter(Boolean).join(" ") : (ownDescribedBy as string | undefined)
|
|
924
|
+
}
|
|
874
925
|
>
|
|
875
926
|
{body}
|
|
876
927
|
</button>
|
|
877
928
|
);
|
|
878
|
-
|
|
929
|
+
const shaped = remove ? pill(button) : button;
|
|
930
|
+
if (!locked) return shaped;
|
|
931
|
+
return (
|
|
932
|
+
// The chip in a FRAGMENT, as a locked Button is: the bubble is visual, and the
|
|
933
|
+
// hidden copy beside it is what the chip (and its ×) is described by.
|
|
934
|
+
<Tooltip label={disabledReason}>
|
|
935
|
+
<>
|
|
936
|
+
{shaped}
|
|
937
|
+
<span id={reasonId} hidden>
|
|
938
|
+
{disabledReason}
|
|
939
|
+
</span>
|
|
940
|
+
</>
|
|
941
|
+
</Tooltip>
|
|
942
|
+
);
|
|
879
943
|
}
|
|
880
944
|
|
|
881
945
|
return (
|
|
@@ -897,6 +961,12 @@ export const Chip = forwardRef<HTMLElement, ChipProps>(function Chip(
|
|
|
897
961
|
);
|
|
898
962
|
});
|
|
899
963
|
|
|
964
|
+
/** `null`, `false` and `""` are what a `cond && reason` gives on the happy path — no
|
|
965
|
+
* reason, so no lock. */
|
|
966
|
+
function hasReason(node: ReactNode): boolean {
|
|
967
|
+
return node !== undefined && node !== null && node !== false && node !== "";
|
|
968
|
+
}
|
|
969
|
+
|
|
900
970
|
/** Calls a chip's `renderLink` with the props it was given — see its call site. */
|
|
901
971
|
function RenderedLink({
|
|
902
972
|
render,
|