@eifi1/ui-kit 0.35.0 → 0.36.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 +35 -5
- package/dist/admin/role-select.d.ts +3 -2
- package/dist/admin/role-select.js +2 -0
- package/dist/admin/role-select.js.map +1 -1
- package/dist/components/amount-input.d.ts +2 -2
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/autocomplete.d.ts +2 -2
- package/dist/components/autocomplete.js +2 -1
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/button-look.d.ts +1 -1
- package/dist/components/button-look.js +9 -4
- package/dist/components/button-look.js.map +1 -1
- package/dist/components/checkbox-group.d.ts +5 -2
- package/dist/components/checkbox-group.js +15 -4
- package/dist/components/checkbox-group.js.map +1 -1
- package/dist/components/column-mapper.d.ts +5 -3
- package/dist/components/column-mapper.js +2 -0
- package/dist/components/column-mapper.js.map +1 -1
- package/dist/components/combobox.d.ts +2 -2
- package/dist/components/combobox.js +2 -1
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/country-select.d.ts +3 -3
- package/dist/components/country-select.js +3 -0
- package/dist/components/country-select.js.map +1 -1
- package/dist/components/currency-select.d.ts +3 -2
- package/dist/components/currency-select.js +10 -1
- package/dist/components/currency-select.js.map +1 -1
- package/dist/components/date-field.d.ts +2 -1
- package/dist/components/date-field.js.map +1 -1
- package/dist/components/date-picker.js +3 -0
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/date-range-picker.js +3 -0
- package/dist/components/date-range-picker.js.map +1 -1
- package/dist/components/entity-combobox.d.ts +2 -2
- package/dist/components/entity-combobox.js +2 -1
- package/dist/components/entity-combobox.js.map +1 -1
- package/dist/components/field-parts.d.ts +66 -11
- package/dist/components/field-parts.js +32 -10
- package/dist/components/field-parts.js.map +1 -1
- package/dist/components/field-strip.d.ts +3 -2
- package/dist/components/field-strip.js +2 -1
- package/dist/components/field-strip.js.map +1 -1
- package/dist/components/field.d.ts +3 -2
- package/dist/components/field.js +24 -15
- package/dist/components/field.js.map +1 -1
- package/dist/components/help-popover.d.ts +9 -7
- package/dist/components/help-popover.js.map +1 -1
- package/dist/components/icon-picker.d.ts +3 -2
- package/dist/components/icon-picker.js +3 -0
- package/dist/components/icon-picker.js.map +1 -1
- package/dist/components/inline-entity-combobox.d.ts +2 -2
- package/dist/components/inline-entity-combobox.js +2 -1
- package/dist/components/inline-entity-combobox.js.map +1 -1
- package/dist/components/input.d.ts +2 -2
- package/dist/components/input.js +2 -1
- package/dist/components/input.js.map +1 -1
- package/dist/components/month-picker.d.ts +2 -1
- package/dist/components/month-picker.js +8 -1
- package/dist/components/month-picker.js.map +1 -1
- package/dist/components/multi-entity-combobox.d.ts +2 -2
- package/dist/components/multi-entity-combobox.js +2 -1
- package/dist/components/multi-entity-combobox.js.map +1 -1
- package/dist/components/multi-select.d.ts +2 -2
- package/dist/components/multi-select.js +3 -0
- package/dist/components/multi-select.js.map +1 -1
- package/dist/components/number-input.d.ts +2 -2
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/numeric-field.d.ts +5 -2
- package/dist/components/numeric-field.js +4 -1
- package/dist/components/numeric-field.js.map +1 -1
- package/dist/components/one-time-code-input.d.ts +2 -1
- package/dist/components/one-time-code-input.js +2 -1
- package/dist/components/one-time-code-input.js.map +1 -1
- package/dist/components/phone-input.d.ts +1 -1
- package/dist/components/phone-input.js +4 -0
- package/dist/components/phone-input.js.map +1 -1
- package/dist/components/popover.js +1 -1
- package/dist/components/popover.js.map +1 -1
- package/dist/components/select.d.ts +4 -3
- package/dist/components/select.js +2 -1
- package/dist/components/select.js.map +1 -1
- package/dist/components/slider.d.ts +3 -2
- package/dist/components/slider.js +7 -1
- package/dist/components/slider.js.map +1 -1
- package/dist/components/stat-tile.js +2 -11
- package/dist/components/stat-tile.js.map +1 -1
- package/dist/components/swatch-picker.d.ts +3 -2
- package/dist/components/swatch-picker.js +3 -0
- package/dist/components/swatch-picker.js.map +1 -1
- package/dist/components/text-link.js +3 -1
- package/dist/components/text-link.js.map +1 -1
- package/dist/components/textarea.d.ts +2 -1
- package/dist/components/textarea.js +9 -3
- package/dist/components/textarea.js.map +1 -1
- package/dist/components/toggle-group.d.ts +2 -2
- package/dist/components/toggle-group.js +7 -3
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/eslint/index.js +1 -1
- package/dist/i18n/defaults.js +5 -1
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.js +97 -1
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +15 -0
- package/dist/i18n/kit-labels.js +21 -1
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/en.js +103 -2
- package/dist/i18n/locales/en.js.map +3 -3
- package/dist/i18n/locales/es.js +95 -1
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.js +96 -1
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.js +95 -1
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.js +96 -1
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.js +95 -1
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.js +13 -0
- package/dist/i18n/review.js.map +1 -1
- package/dist/i18n/shared-labels.js +3 -1
- package/dist/i18n/shared-labels.js.map +1 -1
- package/dist/index.d.ts +21 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/device-settings.d.ts +72 -0
- package/dist/lib/device-settings.js +64 -0
- package/dist/lib/device-settings.js.map +1 -0
- package/dist/lib/plain-text.d.ts +11 -0
- package/dist/lib/plain-text.js +16 -0
- package/dist/lib/plain-text.js.map +1 -0
- package/dist/lib/safe-storage.d.ts +6 -0
- package/dist/lib/safe-storage.js +11 -0
- package/dist/lib/safe-storage.js.map +1 -1
- package/dist/notifications/notification-parts.d.ts +39 -0
- package/dist/notifications/notification-parts.js +47 -0
- package/dist/notifications/notification-parts.js.map +1 -0
- package/dist/notifications/notification-types-setting.d.ts +62 -0
- package/dist/notifications/notification-types-setting.js +192 -0
- package/dist/notifications/notification-types-setting.js.map +1 -0
- package/dist/notifications/notification-types.d.ts +75 -0
- package/dist/notifications/notification-types.js +2 -0
- package/dist/notifications/notification-types.js.map +1 -0
- package/dist/notifications/notifications-labels.d.ts +131 -0
- package/dist/notifications/notifications-labels.js +83 -0
- package/dist/notifications/notifications-labels.js.map +1 -0
- package/dist/notifications/push-device-setting.d.ts +37 -0
- package/dist/notifications/push-device-setting.js +164 -0
- package/dist/notifications/push-device-setting.js.map +1 -0
- package/dist/notifications/push-devices-setting.d.ts +44 -0
- package/dist/notifications/push-devices-setting.js +203 -0
- package/dist/notifications/push-devices-setting.js.map +1 -0
- package/dist/notifications/quiet-hours-setting.d.ts +47 -0
- package/dist/notifications/quiet-hours-setting.js +131 -0
- package/dist/notifications/quiet-hours-setting.js.map +1 -0
- package/dist/notifications/time-zone-select.d.ts +50 -0
- package/dist/notifications/time-zone-select.js +76 -0
- package/dist/notifications/time-zone-select.js.map +1 -0
- package/dist/notifications/unsubscribe-panel.d.ts +53 -0
- package/dist/notifications/unsubscribe-panel.js +139 -0
- package/dist/notifications/unsubscribe-panel.js.map +1 -0
- package/dist/notifications/use-push-device.d.ts +122 -0
- package/dist/notifications/use-push-device.js +230 -0
- package/dist/notifications/use-push-device.js.map +1 -0
- package/dist/rhf/fields.d.ts +26 -19
- package/dist/rhf/fields.js +51 -22
- package/dist/rhf/fields.js.map +1 -1
- package/dist/settings/device-reset-setting-labels.d.ts +3 -0
- package/dist/settings/device-reset-setting-labels.js +15 -0
- package/dist/settings/device-reset-setting-labels.js.map +1 -0
- package/dist/settings/device-reset-setting.d.ts +58 -0
- package/dist/settings/device-reset-setting.js +51 -0
- package/dist/settings/device-reset-setting.js.map +1 -0
- package/dist/shell/app-shell.js +5 -0
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/theme/contrast-tokens.d.ts +42 -4
- package/dist/theme/contrast-tokens.js +56 -9
- package/dist/theme/contrast-tokens.js.map +1 -1
- package/dist/theme/contrast.d.ts +4 -2
- package/dist/theme/contrast.js +13 -0
- package/dist/theme/contrast.js.map +1 -1
- package/dist/theme/palette-derive.d.ts +15 -1
- package/dist/theme/palette-derive.js +37 -8
- package/dist/theme/palette-derive.js.map +1 -1
- package/dist/theme/palette-presets.d.ts +2 -1
- package/dist/theme/palette-presets.js +5 -5
- package/dist/theme/palette-presets.js.map +1 -1
- package/dist/theme/palette-store.js +6 -0
- package/dist/theme/palette-store.js.map +1 -1
- package/dist/theme/text-size.js +6 -0
- package/dist/theme/text-size.js.map +1 -1
- package/dist/theme/theme-store.js +6 -0
- package/dist/theme/theme-store.js.map +1 -1
- package/package.json +1 -1
- package/src/admin/role-select.tsx +4 -1
- package/src/components/amount-input.tsx +2 -1
- package/src/components/autocomplete.tsx +5 -2
- package/src/components/button-look.ts +15 -5
- package/src/components/checkbox-group.tsx +22 -4
- package/src/components/column-mapper.tsx +6 -2
- package/src/components/combobox.tsx +5 -2
- package/src/components/country-select.tsx +6 -1
- package/src/components/currency-select.tsx +12 -3
- package/src/components/date-field.tsx +2 -2
- package/src/components/date-picker.tsx +3 -0
- package/src/components/date-range-picker.tsx +3 -0
- package/src/components/entity-combobox.tsx +5 -2
- package/src/components/field-parts.tsx +107 -19
- package/src/components/field-strip.tsx +4 -3
- package/src/components/field.tsx +28 -15
- package/src/components/help-popover.tsx +9 -7
- package/src/components/icon-picker.tsx +8 -3
- package/src/components/inline-entity-combobox.tsx +5 -2
- package/src/components/input.tsx +4 -2
- package/src/components/month-picker.tsx +10 -3
- package/src/components/multi-entity-combobox.tsx +5 -2
- package/src/components/multi-select.tsx +6 -1
- package/src/components/number-input.tsx +2 -2
- package/src/components/numeric-field.ts +10 -2
- package/src/components/one-time-code-input.tsx +5 -3
- package/src/components/phone-input.tsx +4 -0
- package/src/components/popover.tsx +3 -1
- package/src/components/select.tsx +6 -3
- package/src/components/slider.tsx +10 -3
- package/src/components/stat-tile.tsx +3 -18
- package/src/components/swatch-picker.tsx +9 -4
- package/src/components/text-link.tsx +3 -1
- package/src/components/textarea.tsx +11 -4
- package/src/components/toggle-group.tsx +21 -5
- package/src/i18n/defaults.ts +4 -0
- package/src/i18n/german.ts +113 -0
- package/src/i18n/kit-labels.tsx +51 -1
- package/src/i18n/locales/en.ts +6 -0
- package/src/i18n/locales/es.ts +106 -0
- package/src/i18n/locales/fr.ts +106 -0
- package/src/i18n/locales/hu.ts +104 -0
- package/src/i18n/locales/it.ts +105 -0
- package/src/i18n/locales/zh.ts +96 -0
- package/src/i18n/review.ts +13 -0
- package/src/i18n/shared-labels.ts +2 -0
- package/src/index.ts +50 -0
- package/src/lib/device-settings.ts +146 -0
- package/src/lib/plain-text.ts +22 -0
- package/src/lib/safe-storage.ts +16 -0
- package/src/notifications/notification-parts.tsx +83 -0
- package/src/notifications/notification-types-setting.tsx +309 -0
- package/src/notifications/notification-types.ts +80 -0
- package/src/notifications/notifications-labels.ts +238 -0
- package/src/notifications/push-device-setting.tsx +224 -0
- package/src/notifications/push-devices-setting.tsx +306 -0
- package/src/notifications/quiet-hours-setting.tsx +185 -0
- package/src/notifications/time-zone-select.tsx +126 -0
- package/src/notifications/unsubscribe-panel.tsx +203 -0
- package/src/notifications/use-push-device.ts +392 -0
- package/src/rhf/fields.tsx +77 -29
- package/src/settings/device-reset-setting-labels.ts +20 -0
- package/src/settings/device-reset-setting.tsx +119 -0
- package/src/shell/app-shell.tsx +8 -0
- package/src/theme/contrast-tokens.ts +106 -12
- package/src/theme/contrast.ts +30 -4
- package/src/theme/palette-derive.ts +68 -7
- package/src/theme/palette-presets.ts +9 -6
- package/src/theme/palette-store.ts +12 -0
- package/src/theme/text-size.ts +8 -0
- package/src/theme/theme-store.ts +10 -0
- package/tokens.css +38 -4
|
@@ -148,6 +148,7 @@ export function PhoneInput({
|
|
|
148
148
|
inputClassName,
|
|
149
149
|
label,
|
|
150
150
|
hint,
|
|
151
|
+
help,
|
|
151
152
|
error,
|
|
152
153
|
invalid,
|
|
153
154
|
disabled,
|
|
@@ -225,6 +226,9 @@ export function PhoneInput({
|
|
|
225
226
|
// since 0.34, not a copy of its rule (DUP-2, DUP-8).
|
|
226
227
|
const messages = useFieldMessages({
|
|
227
228
|
hint,
|
|
229
|
+
// The number's label line takes the help's "?" (0.36); the hint is then the caption.
|
|
230
|
+
help,
|
|
231
|
+
name: label ?? rest["aria-label"],
|
|
228
232
|
error,
|
|
229
233
|
invalid,
|
|
230
234
|
describedBy: ariaDescribedBy,
|
|
@@ -10,7 +10,9 @@ import { dirOf } from "../lib/direction";
|
|
|
10
10
|
import type { Direction } from "../lib/direction";
|
|
11
11
|
import { useKitLabels, type LabelOverride } from "../i18n/kit-labels";
|
|
12
12
|
import { DEFAULT_POPOVER_LABELS } from "./popover-labels";
|
|
13
|
-
|
|
13
|
+
// From ../lib/refs, not field-parts (round 0.36): field-parts renders a HelpPopover for a
|
|
14
|
+
// field's `help`, so importing from there would close a cycle, as it did for Tooltip.
|
|
15
|
+
import { mergeRefs } from "../lib/refs";
|
|
14
16
|
|
|
15
17
|
/** The panel's accessible name. A `role="dialog"` with no name announces as "dialog"
|
|
16
18
|
* and nothing else, which is worse than the unlabelled div it replaced — so it is a
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
LockedReason,
|
|
7
7
|
mergeDescribedBy,
|
|
8
8
|
type DisabledReasonDisplay,
|
|
9
|
+
type FieldHelpProps,
|
|
9
10
|
useFieldError,
|
|
10
11
|
useFieldHint,
|
|
11
12
|
useLockReason,
|
|
@@ -20,7 +21,7 @@ import {
|
|
|
20
21
|
fieldBelow,
|
|
21
22
|
} from "./field-base";
|
|
22
23
|
|
|
23
|
-
export interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, "size"
|
|
24
|
+
export interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, "size">, FieldHelpProps {
|
|
24
25
|
label?: ReactNode;
|
|
25
26
|
/** See {@link Input}'s `invalid`. */
|
|
26
27
|
invalid?: boolean;
|
|
@@ -33,7 +34,8 @@ export interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement
|
|
|
33
34
|
* set in the value's type on top of both the label and the value. On an
|
|
34
35
|
* UNLABELLED Select (no label line) a FieldHint sits at the field's end edge,
|
|
35
36
|
* outside the box. Since 0.34 a FieldHint is the caption at Large and on a touch
|
|
36
|
-
* screen, as on Input (`useInlineHint`); a `HelpPopover` stays a "?".
|
|
37
|
+
* screen, as on Input (`useInlineHint`); a `HelpPopover` stays a "?". Beside a
|
|
38
|
+
* `help` (0.36) the hint is the caption at every size: the help's "?" is the only one. */
|
|
37
39
|
hint?: ReactNode;
|
|
38
40
|
/**
|
|
39
41
|
* `"sm"`: a 28px, 12px-type select for a toolbar or a table header, where the
|
|
@@ -109,6 +111,7 @@ export function Select(props: SelectProps) {
|
|
|
109
111
|
invalid,
|
|
110
112
|
error,
|
|
111
113
|
hint: hintProp,
|
|
114
|
+
help,
|
|
112
115
|
size,
|
|
113
116
|
selectClassName,
|
|
114
117
|
disabledReason,
|
|
@@ -138,7 +141,7 @@ export function Select(props: SelectProps) {
|
|
|
138
141
|
// and on a touch screen is its words, a caption like any text (§4; a HelpPopover stays
|
|
139
142
|
// on the label line, §10.23). See `hint` above. Through useFieldHint since 0.34, not a
|
|
140
143
|
// copy of its rule (DUP-2, refactor audit 2026-10).
|
|
141
|
-
const hintParts = useFieldHint(hintProp, rest["aria-describedby"]);
|
|
144
|
+
const hintParts = useFieldHint(hintProp, rest["aria-describedby"], { help, name: label ?? rest["aria-label"] });
|
|
142
145
|
const hint = hintParts.labelHint;
|
|
143
146
|
const { isInvalid, describedBy, errorEl } = useFieldError(
|
|
144
147
|
error,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useId } from "react";
|
|
2
2
|
import type { CSSProperties, ChangeEvent, InputHTMLAttributes, ReactNode, Ref } from "react";
|
|
3
3
|
import { cn } from "../lib/cn";
|
|
4
|
-
import { useFieldMessages } from "./field-parts";
|
|
4
|
+
import { useFieldMessages, type FieldHelpProps } from "./field-parts";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* A range control on a native `<input type="range">`.
|
|
@@ -123,7 +123,8 @@ export interface SliderProps
|
|
|
123
123
|
extends Omit<
|
|
124
124
|
InputHTMLAttributes<HTMLInputElement>,
|
|
125
125
|
"type" | "value" | "defaultValue" | "onChange" | "min" | "max" | "step" | "children"
|
|
126
|
-
|
|
126
|
+
>,
|
|
127
|
+
FieldHelpProps {
|
|
127
128
|
/** The value. On a log scale this is the real value, not the track position. */
|
|
128
129
|
value: number;
|
|
129
130
|
/** The new value — a number, not the DOM event, like `ToggleGroup`. Fired on every
|
|
@@ -201,6 +202,7 @@ export function Slider({
|
|
|
201
202
|
formatValue,
|
|
202
203
|
label,
|
|
203
204
|
hint,
|
|
205
|
+
help,
|
|
204
206
|
readout,
|
|
205
207
|
marks,
|
|
206
208
|
className,
|
|
@@ -254,7 +256,12 @@ export function Slider({
|
|
|
254
256
|
const hasMarkLabels = normalizedMarks.some((m) => m.label !== undefined);
|
|
255
257
|
|
|
256
258
|
// The label line's "?", or the caption under the track (DUP-2).
|
|
257
|
-
const messages = useFieldMessages({
|
|
259
|
+
const messages = useFieldMessages({
|
|
260
|
+
hint,
|
|
261
|
+
help,
|
|
262
|
+
name: label ?? rest["aria-label"],
|
|
263
|
+
describedBy: rest["aria-describedby"],
|
|
264
|
+
});
|
|
258
265
|
const labelHint = messages.labelHint;
|
|
259
266
|
const hasHeader = label !== undefined || labelHint !== undefined || readout !== undefined;
|
|
260
267
|
const plainReadout = typeof readout === "string" || typeof readout === "number";
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { createContext,
|
|
1
|
+
import { createContext, useContext, useId, useLayoutEffect, useState } from "react";
|
|
2
2
|
import type { ComponentPropsWithRef, CSSProperties, ReactElement, ReactNode } from "react";
|
|
3
3
|
import { ArrowDown, ArrowUp, Minus } from "lucide-react";
|
|
4
4
|
import { cn } from "../lib/cn";
|
|
5
|
+
// The sparkline's name: the label's text (lib since 0.36, which a field's `help` "?" shares).
|
|
6
|
+
import { plainText } from "../lib/plain-text";
|
|
5
7
|
import { useKitLabels, useKitLink, useKitLocale, type LabelOverride } from "../i18n/kit-labels";
|
|
6
8
|
import { pickLinkRenderer, RenderedKitLink } from "./text-link";
|
|
7
9
|
import { Card } from "./card";
|
|
@@ -612,23 +614,6 @@ function FigureRow({
|
|
|
612
614
|
);
|
|
613
615
|
}
|
|
614
616
|
|
|
615
|
-
/**
|
|
616
|
-
* The text of a label node, for the sparkline's name: a string or number as is, an
|
|
617
|
-
* element by its children, recursively. A `<Tooltip label="…">Spend</Tooltip>` label
|
|
618
|
-
* yields "Spend" — the text the reader sees, not the bubble's. `undefined` when there
|
|
619
|
-
* is no text to find (an icon), so the sparkline falls back to its unnamed summary.
|
|
620
|
-
*/
|
|
621
|
-
function plainText(node: ReactNode): string | undefined {
|
|
622
|
-
const walk = (n: ReactNode): string => {
|
|
623
|
-
if (typeof n === "string" || typeof n === "number") return String(n);
|
|
624
|
-
if (Array.isArray(n)) return n.map(walk).join("");
|
|
625
|
-
if (isValidElement<{ children?: ReactNode }>(n)) return walk(n.props.children);
|
|
626
|
-
return "";
|
|
627
|
-
};
|
|
628
|
-
const text = walk(node).replace(/\s+/g, " ").trim();
|
|
629
|
-
return text === "" ? undefined : text;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
617
|
/**
|
|
633
618
|
* Whether `el`'s text is wider than its box — i.e. whether `truncate` is cutting it.
|
|
634
619
|
* Re-measured whenever the box resizes (a grid reflowing, a translation loading), not
|
|
@@ -7,7 +7,7 @@ import type { TileItem, TileSize } from "./tile-radio";
|
|
|
7
7
|
import { Tooltip } from "./tooltip";
|
|
8
8
|
import { useCommitReason, type CommitScope } from "./write-lock";
|
|
9
9
|
import { hasContent } from "../lib/presence";
|
|
10
|
-
import { LabelStrip, mergeDescribedBy, useFieldMessages } from "./field-parts";
|
|
10
|
+
import { LabelStrip, mergeDescribedBy, useFieldMessages, type FieldHelpProps } from "./field-parts";
|
|
11
11
|
import { DEFAULT_SWATCH_PICKER_LABELS } from "./swatch-picker-labels";
|
|
12
12
|
|
|
13
13
|
export interface SwatchPickerLabels {
|
|
@@ -62,7 +62,8 @@ export interface SwatchOption<T extends string> {
|
|
|
62
62
|
* is omitted rather than shadowed, as on {@link ToggleGroup}.
|
|
63
63
|
*/
|
|
64
64
|
export interface SwatchPickerProps<T extends string>
|
|
65
|
-
extends Omit<ComponentPropsWithRef<"div">, "onChange" | "children" | "defaultValue"
|
|
65
|
+
extends Omit<ComponentPropsWithRef<"div">, "onChange" | "children" | "defaultValue">,
|
|
66
|
+
FieldHelpProps {
|
|
66
67
|
options: SwatchOption<T>[];
|
|
67
68
|
/** The chosen colour, or `null` for none. */
|
|
68
69
|
value: T | null;
|
|
@@ -145,6 +146,7 @@ export function SwatchPicker<T extends string>({
|
|
|
145
146
|
labels,
|
|
146
147
|
label,
|
|
147
148
|
hint,
|
|
149
|
+
help,
|
|
148
150
|
disabledReason: ownDisabledReason,
|
|
149
151
|
commit,
|
|
150
152
|
className,
|
|
@@ -157,10 +159,13 @@ export function SwatchPicker<T extends string>({
|
|
|
157
159
|
const disabledReason = useCommitReason(commit, ownDisabledReason);
|
|
158
160
|
const locked = hasContent(disabledReason);
|
|
159
161
|
const labelled = hasContent(label);
|
|
160
|
-
// The label line's "?", or the caption under the swatches (DUP-2). Only with a label
|
|
161
|
-
// The caller's ids, the caption, then the group's state:
|
|
162
|
+
// The label line's "?", or the caption under the swatches (DUP-2). Only with a label,
|
|
163
|
+
// the help's "?" too (0.36). The caller's ids, the caption, then the group's state:
|
|
164
|
+
// mixed, locked.
|
|
162
165
|
const messages = useFieldMessages({
|
|
163
166
|
hint: labelled ? hint : undefined,
|
|
167
|
+
help: labelled ? help : undefined,
|
|
168
|
+
name: label,
|
|
164
169
|
describedBy: rest["aria-describedby"],
|
|
165
170
|
reasonId: mergeDescribedBy(mixed && mixedId, locked && reasonId),
|
|
166
171
|
});
|
|
@@ -228,7 +228,9 @@ export interface TextLinkProps
|
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
const TONE: Record<TextLinkTone, string> = {
|
|
231
|
-
brand
|
|
231
|
+
// Under More contrast the brand as text, `--brand-muted`, at rest and hovered (0.36,
|
|
232
|
+
// colour-roles §13.3 decision 8): `--brand` is a mark's colour, solved to 3:1.
|
|
233
|
+
brand: "text-brand hover:text-[var(--brand-hover)] contrast:text-brand-muted contrast:hover:text-brand-muted",
|
|
232
234
|
muted: "text-muted hover:text-primary",
|
|
233
235
|
danger: "text-danger hover:text-[var(--danger-hover)]",
|
|
234
236
|
inherit: "",
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { useId, useLayoutEffect, useRef } from "react";
|
|
2
2
|
import type { ChangeEvent, ReactNode, Ref, TextareaHTMLAttributes } from "react";
|
|
3
3
|
import { cn } from "../lib/cn";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
EndHintRow,
|
|
6
|
+
FieldCaption,
|
|
7
|
+
mergeDescribedBy,
|
|
8
|
+
useFieldError,
|
|
9
|
+
useFieldHint,
|
|
10
|
+
type FieldHelpProps,
|
|
11
|
+
} from "./field-parts";
|
|
5
12
|
import {
|
|
6
13
|
CharacterCount,
|
|
7
14
|
FIELD_BASE,
|
|
@@ -221,7 +228,7 @@ function TextareaLabelStrip() {
|
|
|
221
228
|
return <span ref={ref} aria-hidden data-slot="label-strip" className={TEXTAREA_LABEL_STRIP} />;
|
|
222
229
|
}
|
|
223
230
|
|
|
224
|
-
export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement
|
|
231
|
+
export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement>, FieldHelpProps {
|
|
225
232
|
label?: ReactNode;
|
|
226
233
|
/** See {@link Input}'s `invalid`. */
|
|
227
234
|
invalid?: boolean;
|
|
@@ -240,13 +247,13 @@ export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElemen
|
|
|
240
247
|
}
|
|
241
248
|
|
|
242
249
|
export function Textarea(props: TextareaProps) {
|
|
243
|
-
const { ref, className, label, id, placeholder, invalid, error, hint, showCount, countLabels, ...rest } = props;
|
|
250
|
+
const { ref, className, label, id, placeholder, invalid, error, hint, help, showCount, countLabels, ...rest } = props;
|
|
244
251
|
// See Input's `reserve`.
|
|
245
252
|
const reserve = "error" in props || "hint" in props || "showCount" in props;
|
|
246
253
|
const generated = useId();
|
|
247
254
|
const fieldId = id ?? generated;
|
|
248
255
|
const countId = useId();
|
|
249
|
-
const hintParts = useFieldHint(hint, rest["aria-describedby"]);
|
|
256
|
+
const hintParts = useFieldHint(hint, rest["aria-describedby"], { help, name: label ?? rest["aria-label"] });
|
|
250
257
|
const maxLength = rest.maxLength;
|
|
251
258
|
const counting = Boolean(showCount) && typeof maxLength === "number" && maxLength > 0;
|
|
252
259
|
const length = useTypedLength(rest.value, rest.defaultValue);
|
|
@@ -11,11 +11,13 @@ import {
|
|
|
11
11
|
DISABLED_REASON_LINE_CLASS,
|
|
12
12
|
FIELD_CAPTION_CLASS,
|
|
13
13
|
FIELD_ERROR_CLASS,
|
|
14
|
+
fieldHelpMark,
|
|
14
15
|
isTextHint,
|
|
15
16
|
LabelStrip,
|
|
16
17
|
useDisabledReasonLine,
|
|
17
18
|
useInlineHint,
|
|
18
19
|
type DisabledReasonDisplay,
|
|
20
|
+
type FieldHelpProps,
|
|
19
21
|
} from "./field-parts";
|
|
20
22
|
import { FOCUS_RING_WIDTH } from "./focus-ring";
|
|
21
23
|
|
|
@@ -35,7 +37,8 @@ export interface ToggleOption<T extends string> {
|
|
|
35
37
|
* worse than one that does not compile.
|
|
36
38
|
*/
|
|
37
39
|
export interface ToggleGroupBaseProps<T extends string>
|
|
38
|
-
extends Omit<ComponentPropsWithoutRef<"div">, "onChange" | "children"
|
|
40
|
+
extends Omit<ComponentPropsWithoutRef<"div">, "onChange" | "children">,
|
|
41
|
+
FieldHelpProps {
|
|
39
42
|
options: ToggleOption<T>[];
|
|
40
43
|
className?: string;
|
|
41
44
|
/** Applied to every option button (e.g. to tune height/rounding to match
|
|
@@ -380,6 +383,7 @@ export function ToggleGroup<T extends string>(props: ToggleGroupProps<T>): React
|
|
|
380
383
|
labelPlacement = "field",
|
|
381
384
|
chromeClassName,
|
|
382
385
|
hint,
|
|
386
|
+
help,
|
|
383
387
|
error,
|
|
384
388
|
disabledReason: ownDisabledReason,
|
|
385
389
|
disabledReasonDisplay,
|
|
@@ -407,10 +411,22 @@ export function ToggleGroup<T extends string>(props: ToggleGroupProps<T>): React
|
|
|
407
411
|
// §4: at Large and on touch the reason is a line under the group, not a bubble.
|
|
408
412
|
const reasonLine = useDisabledReasonLine(disabledReasonDisplay) && locked;
|
|
409
413
|
// §4: likewise a FieldHint "?" on the label line becomes a caption under the group at
|
|
410
|
-
// Large and on touch; text is a caption at every size, as Select's (see `hint`).
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
const
|
|
414
|
+
// Large and on touch; text is a caption at every size, as Select's (see `hint`). With
|
|
415
|
+
// a `help` (0.36, only with `label`, as `hint`), its "?" is the label line's only one,
|
|
416
|
+
// and the hint — a FieldHint's words included — is the caption at every size.
|
|
417
|
+
const helpMark = fieldHelpMark(hasContent(label) ? help : undefined, label);
|
|
418
|
+
const withHelp = helpMark !== undefined;
|
|
419
|
+
const inlineHint = useInlineHint(hint, withHelp);
|
|
420
|
+
const hintAsCaption = withHelp
|
|
421
|
+
? hasContent(inlineHint)
|
|
422
|
+
? inlineHint
|
|
423
|
+
: undefined
|
|
424
|
+
: isTextHint(hint)
|
|
425
|
+
? hint
|
|
426
|
+
: inlineHint !== hint
|
|
427
|
+
? inlineHint
|
|
428
|
+
: undefined;
|
|
429
|
+
const labelHint = helpMark ?? (hintAsCaption === undefined ? hint : undefined);
|
|
414
430
|
// Locked looks disabled — the dimmed group, the not-allowed cursor, no hover offer —
|
|
415
431
|
// but stays focusable; `disabled` alone also takes the segments out of the tab order.
|
|
416
432
|
const dimmed = disabled || locked;
|
package/src/i18n/defaults.ts
CHANGED
|
@@ -87,6 +87,7 @@ import { DEFAULT_DEMO_LABELS } from "../demo/demo-labels";
|
|
|
87
87
|
import { DEFAULT_BILLING_LABELS } from "../billing/billing-labels";
|
|
88
88
|
import { DEFAULT_PLAN_CHANGE_LABELS } from "../admin/plan-change-labels";
|
|
89
89
|
import { DEFAULT_APPEARANCE_LABELS } from "../components/appearance-labels";
|
|
90
|
+
import { DEFAULT_DEVICE_RESET_LABELS } from "../settings/device-reset-setting-labels";
|
|
90
91
|
import { DEFAULT_ROW_ACTIONS_LABELS } from "../components/row-actions-labels";
|
|
91
92
|
import { DEFAULT_APP_SHELL_MORE_LABELS } from "../shell/app-shell-labels";
|
|
92
93
|
import { DEFAULT_ACCOUNT_STATE_LABELS } from "../components/account-chips-labels";
|
|
@@ -100,6 +101,7 @@ import { DEFAULT_IBAN_INPUT_LABELS } from "../components/iban-input-labels";
|
|
|
100
101
|
import { DEFAULT_PHONE_INPUT_LABELS } from "../components/phone-input-labels";
|
|
101
102
|
import { DEFAULT_SIGN_CHIP_LABELS } from "../components/sign-chip-labels";
|
|
102
103
|
import { DEFAULT_COLUMN_MAPPER_LABELS } from "../components/column-mapper-labels";
|
|
104
|
+
import { DEFAULT_NOTIFICATIONS_LABELS } from "../notifications/notifications-labels";
|
|
103
105
|
import {
|
|
104
106
|
DEFAULT_APP_SHELL_LABELS,
|
|
105
107
|
DEFAULT_CALCULATOR_LABELS,
|
|
@@ -231,6 +233,7 @@ export const DEFAULT_UI_KIT_LABELS: UiKitLabels = {
|
|
|
231
233
|
billing: DEFAULT_BILLING_LABELS,
|
|
232
234
|
planChange: DEFAULT_PLAN_CHANGE_LABELS,
|
|
233
235
|
appearance: DEFAULT_APPEARANCE_LABELS,
|
|
236
|
+
deviceReset: DEFAULT_DEVICE_RESET_LABELS,
|
|
234
237
|
rowActions: DEFAULT_ROW_ACTIONS_LABELS,
|
|
235
238
|
appShellMore: DEFAULT_APP_SHELL_MORE_LABELS,
|
|
236
239
|
characterCount: DEFAULT_CHARACTER_COUNT_LABELS,
|
|
@@ -240,4 +243,5 @@ export const DEFAULT_UI_KIT_LABELS: UiKitLabels = {
|
|
|
240
243
|
phoneInput: DEFAULT_PHONE_INPUT_LABELS,
|
|
241
244
|
signChip: DEFAULT_SIGN_CHIP_LABELS,
|
|
242
245
|
columnMapper: DEFAULT_COLUMN_MAPPER_LABELS,
|
|
246
|
+
notifications: DEFAULT_NOTIFICATIONS_LABELS,
|
|
243
247
|
};
|
package/src/i18n/german.ts
CHANGED
|
@@ -268,6 +268,8 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
268
268
|
noResults: "Keine Ergebnisse",
|
|
269
269
|
fieldValue: (field, value) => `${field}: ${value}`,
|
|
270
270
|
opensInNewTab: "öffnet in einem neuen Tab",
|
|
271
|
+
aboutField: (field) => `Hilfe zu „${field}“`,
|
|
272
|
+
help: "Hilfe",
|
|
271
273
|
},
|
|
272
274
|
dataTable: {
|
|
273
275
|
columns: "Spalten",
|
|
@@ -1072,6 +1074,102 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
1072
1074
|
rateLimited,
|
|
1073
1075
|
failed: "Das hat nicht geklappt. Bitte erneut versuchen.",
|
|
1074
1076
|
},
|
|
1077
|
+
// 0.36.0 — docs/notifications-harmonization.md §4.2: the notification cards. The push
|
|
1078
|
+
// and device words follow keksdose's reviewed German, formal.
|
|
1079
|
+
notifications: {
|
|
1080
|
+
typesTitle: "Benachrichtigungen zu",
|
|
1081
|
+
typesDescription: "Wählen Sie, worüber Sie benachrichtigt werden, und wie.",
|
|
1082
|
+
byEmail: "Per E-Mail",
|
|
1083
|
+
emailUnverified: "Bestätigen Sie Ihre Adresse, um diese zu erhalten.",
|
|
1084
|
+
digestCadence: "Häufigkeit",
|
|
1085
|
+
digestWeekly: "Wöchentlich",
|
|
1086
|
+
digestMonthly: "Monatlich",
|
|
1087
|
+
typesLoading: "Benachrichtigungseinstellungen werden geladen…",
|
|
1088
|
+
typesEmpty: "Für dieses Konto gibt es keine Benachrichtigungen.",
|
|
1089
|
+
quietTitle: "Ruhezeiten",
|
|
1090
|
+
quietDescription: (inbox) =>
|
|
1091
|
+
inbox
|
|
1092
|
+
? "In diesem Zeitfenster kommen keine Push-Benachrichtigungen. Im Posteingang kommen sie trotzdem an."
|
|
1093
|
+
: "In diesem Zeitfenster kommen keine Push-Benachrichtigungen.",
|
|
1094
|
+
quietFrom: "Von",
|
|
1095
|
+
quietUntil: "Bis",
|
|
1096
|
+
quietIncomplete: "Legen Sie eine Start- und eine Endzeit fest. Ruhezeiten gelten erst, wenn beide gesetzt sind.",
|
|
1097
|
+
timeZone: "Zeitzone",
|
|
1098
|
+
deviceZone: (zone) => `Dieses Gerät ist in der Zeitzone ${zone}.`,
|
|
1099
|
+
useDeviceZone: "Übernehmen",
|
|
1100
|
+
regions: {
|
|
1101
|
+
Africa: "Afrika",
|
|
1102
|
+
America: "Amerika",
|
|
1103
|
+
Antarctica: "Antarktis",
|
|
1104
|
+
Arctic: "Arktis",
|
|
1105
|
+
Asia: "Asien",
|
|
1106
|
+
Atlantic: "Atlantik",
|
|
1107
|
+
Australia: "Australien",
|
|
1108
|
+
Europe: "Europa",
|
|
1109
|
+
Indian: "Indischer Ozean",
|
|
1110
|
+
Pacific: "Pazifik",
|
|
1111
|
+
},
|
|
1112
|
+
pushTitle: "Push-Benachrichtigungen",
|
|
1113
|
+
pushDescription: "Benachrichtigungen auf diesem Gerät, auch wenn die App geschlossen ist.",
|
|
1114
|
+
unsupported: "Dieser Browser unterstützt keine Push-Benachrichtigungen.",
|
|
1115
|
+
iosInstall: (app) =>
|
|
1116
|
+
`Fügen Sie ${app} auf dem iPhone oder iPad zuerst zum Home-Bildschirm hinzu: iOS stellt Benachrichtigungen nur der installierten App zu.`,
|
|
1117
|
+
notConfigured: "Push-Benachrichtigungen sind auf diesem Server nicht eingerichtet.",
|
|
1118
|
+
blocked: "Benachrichtigungen sind in den Einstellungen dieses Browsers blockiert.",
|
|
1119
|
+
on: "Benachrichtigungen sind für dieses Gerät eingeschaltet.",
|
|
1120
|
+
off: "Benachrichtigungen sind für dieses Gerät ausgeschaltet.",
|
|
1121
|
+
otherAccount:
|
|
1122
|
+
"Dieser Browser erhält Benachrichtigungen für ein anderes Konto. Schalten Sie sie hier ein, um sie für das angemeldete Konto zu erhalten.",
|
|
1123
|
+
enable: "Benachrichtigungen einschalten",
|
|
1124
|
+
disable: "Ausschalten",
|
|
1125
|
+
sendTest: "Test senden",
|
|
1126
|
+
enableFailed: "Benachrichtigungen konnten nicht eingeschaltet werden.",
|
|
1127
|
+
noServiceWorker:
|
|
1128
|
+
"Der Service Worker der App ist nicht erreichbar. Laden Sie die Seite neu und versuchen Sie es erneut.",
|
|
1129
|
+
pushTimeout:
|
|
1130
|
+
"Der Push-Dienst des Browsers antwortet nicht. Prüfen Sie Ihre Verbindung und versuchen Sie es erneut.",
|
|
1131
|
+
testSent: (count) =>
|
|
1132
|
+
count === 1 ? `Test an ${n(count)} Gerät gesendet` : `Test an ${n(count)} Geräte gesendet`,
|
|
1133
|
+
testNone:
|
|
1134
|
+
"Kein Gerät hat den Test angenommen. Schalten Sie zuerst die Benachrichtigungen für dieses Gerät ein.",
|
|
1135
|
+
testFailed: "Die Test-Benachrichtigung konnte nicht gesendet werden.",
|
|
1136
|
+
explainerTitle: "Benachrichtigungen einschalten?",
|
|
1137
|
+
explainerBody: (app) =>
|
|
1138
|
+
`${app} sendet nur Benachrichtigungen zu dem, was Sie auswählen, und Sie können sie jederzeit ausschalten.`,
|
|
1139
|
+
explainerHint: "Als Nächstes fragt Ihr Browser nach der Berechtigung.",
|
|
1140
|
+
explainerContinue: "Weiter",
|
|
1141
|
+
devicesTitle: "Benachrichtigungsgeräte",
|
|
1142
|
+
devicesDescription:
|
|
1143
|
+
"Alle Geräte, die Push-Benachrichtigungen für Ihr Konto erhalten. Entfernen Sie die, die Sie nicht mehr nutzen.",
|
|
1144
|
+
devicesLoading: "Geräte werden geladen…",
|
|
1145
|
+
devicesEmpty: "Noch kein Gerät erhält Benachrichtigungen.",
|
|
1146
|
+
unknownDevice: "Unbekanntes Gerät",
|
|
1147
|
+
thisDevice: "Dieses Gerät",
|
|
1148
|
+
added: (date) => `Hinzugefügt am ${date}`,
|
|
1149
|
+
lastDelivery: (date) => `Letzte Zustellung am ${date}`,
|
|
1150
|
+
noDelivery: "Noch keine Zustellung",
|
|
1151
|
+
failures: (count) =>
|
|
1152
|
+
count === 1
|
|
1153
|
+
? `${n(count)} fehlgeschlagene Zustellung in Folge`
|
|
1154
|
+
: `${n(count)} fehlgeschlagene Zustellungen in Folge`,
|
|
1155
|
+
remove: "Entfernen",
|
|
1156
|
+
// Starts with `remove`, the button's visible text, for voice control.
|
|
1157
|
+
removeItem: (device) => `Entfernen: ${device}`,
|
|
1158
|
+
removePrompt: "Keine Benachrichtigungen mehr an dieses Gerät senden?",
|
|
1159
|
+
unsubscribeTitle: "E-Mail-Benachrichtigungen",
|
|
1160
|
+
unsubscribeAbout: (type) => `Sie erhalten E-Mails zu: ${type}.`,
|
|
1161
|
+
unsubscribeGeneric: "Sie erhalten Benachrichtigungs-E-Mails.",
|
|
1162
|
+
stopThese: "Diese E-Mails abbestellen",
|
|
1163
|
+
stopAll: (app) => `Alle Benachrichtigungs-E-Mails von ${app} abbestellen`,
|
|
1164
|
+
stoppedThese: "Erledigt. Sie erhalten diese E-Mails nicht mehr.",
|
|
1165
|
+
stoppedAll: (app) => `Erledigt. Sie erhalten keine Benachrichtigungs-E-Mails von ${app} mehr.`,
|
|
1166
|
+
accountMail: "E-Mails zu Ihrem Konto erhalten Sie weiterhin.",
|
|
1167
|
+
turnBackOn: "Sie können sie jederzeit in den Einstellungen wieder einschalten.",
|
|
1168
|
+
openSettings: "Einstellungen öffnen",
|
|
1169
|
+
invalidLink:
|
|
1170
|
+
"Dieser Link funktioniert nicht mehr. Sie können E-Mails stattdessen in den Einstellungen abbestellen.",
|
|
1171
|
+
failed: "Das hat nicht geklappt. Bitte erneut versuchen.",
|
|
1172
|
+
},
|
|
1075
1173
|
sessions: {
|
|
1076
1174
|
title: "Sitzungen",
|
|
1077
1175
|
description:
|
|
@@ -1634,6 +1732,21 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
1634
1732
|
contrastModes: { system: "System", standard: "Standard", more: "Mehr" },
|
|
1635
1733
|
saveFailed: "Konnte nicht in Ihrem Konto gespeichert werden. Auf diesem Gerät gilt es trotzdem.",
|
|
1636
1734
|
},
|
|
1735
|
+
// 0.36.0 — docs/settings-harmonization.md §11.3: resetting this device's settings, the
|
|
1736
|
+
// last card of "Darstellung". "Theme", as `topBar.theme`; "Bereiche" for the panels
|
|
1737
|
+
// (the sidebar, folded groups).
|
|
1738
|
+
deviceReset: {
|
|
1739
|
+
title: "Dieses Gerät zurücksetzen",
|
|
1740
|
+
description:
|
|
1741
|
+
"Setzt die Darstellung auf diesem Gerät und die Ansicht seiner Tabellen und Bereiche auf die Standardwerte zurück. Ihr Konto, Ihre Daten und Ihre Anmeldung bleiben unberührt.",
|
|
1742
|
+
action: "Einstellungen dieses Geräts zurücksetzen",
|
|
1743
|
+
confirmTitle: "Einstellungen dieses Geräts zurücksetzen?",
|
|
1744
|
+
confirmBody:
|
|
1745
|
+
"Theme, Farben, Textgröße, Kontrast und Sprache kehren hier zu ihren Standardwerten zurück, und Tabellen und Bereiche öffnen sich wie beim ersten Mal. Wo Ihr Konto eine Sprache, eine Textgröße oder einen Kontrast festlegt, folgt dieses Gerät wieder dem Konto. Ihr Konto und Ihre anderen Geräte bleiben, wie sie sind.",
|
|
1746
|
+
confirmAction: "Zurücksetzen",
|
|
1747
|
+
done: "Die Einstellungen dieses Geräts sind auf die Standardwerte zurückgesetzt.",
|
|
1748
|
+
keywords: "zurücksetzen standard standardwerte gerät wiederherstellen darstellung tabellen layout",
|
|
1749
|
+
},
|
|
1637
1750
|
rowActions: {
|
|
1638
1751
|
actions: "Aktionen",
|
|
1639
1752
|
actionsFor: (name) => `Aktionen für ${name}`,
|
package/src/i18n/kit-labels.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext, useContext, useMemo } from "react";
|
|
1
|
+
import { createContext, useContext, useEffect, useMemo } from "react";
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_APP_SHELL_LABELS,
|
|
4
4
|
DEFAULT_CALCULATOR_LABELS,
|
|
@@ -98,6 +98,7 @@ import type { DemoLabels } from "../demo/demo-labels";
|
|
|
98
98
|
import type { BillingLabels } from "../billing/billing-labels";
|
|
99
99
|
import type { PlanChangeLabels } from "../admin/plan-change";
|
|
100
100
|
import type { AppearanceLabels } from "../components/appearance-labels";
|
|
101
|
+
import type { DeviceResetLabels } from "../settings/device-reset-setting";
|
|
101
102
|
import type { RowActionsLabels } from "../components/row-actions";
|
|
102
103
|
import type { AppShellMoreLabels } from "../shell/app-shell";
|
|
103
104
|
import type { AccountStateLabels } from "../components/account-chips";
|
|
@@ -111,6 +112,7 @@ import type { IbanInputLabels } from "../components/iban-input";
|
|
|
111
112
|
import type { PhoneInputLabels } from "../components/phone-input";
|
|
112
113
|
import type { SignChipLabels } from "../components/sign-chip";
|
|
113
114
|
import type { ColumnMapperLabels } from "../components/column-mapper";
|
|
115
|
+
import type { NotificationsLabels } from "../notifications/notifications-labels";
|
|
114
116
|
|
|
115
117
|
/**
|
|
116
118
|
* EVERY string the kit renders, as one typed tree — and an optional provider that
|
|
@@ -168,6 +170,12 @@ export interface CommonLabels {
|
|
|
168
170
|
/** Read after a link that opens a new tab (`<Button href external>`, TextLink):
|
|
169
171
|
* nothing else tells a screen reader the page is about to change tabs. */
|
|
170
172
|
opensInNewTab: string;
|
|
173
|
+
/** The name of a field's `help` "?" and its panel (round 0.36): "About <field>", the
|
|
174
|
+
* field's label as the argument — "About Jerk shape". A `HelpPopover` passed as
|
|
175
|
+
* `help` keeps its own name. */
|
|
176
|
+
aboutField: (field: string) => string;
|
|
177
|
+
/** …and of a `help` "?" on a field with no label and no `aria-label` to name it by. */
|
|
178
|
+
help: string;
|
|
171
179
|
}
|
|
172
180
|
|
|
173
181
|
/** `DatePicker` / `DateRangePicker` chrome. The calendar inside has its own
|
|
@@ -421,6 +429,9 @@ export interface UiKitLabels {
|
|
|
421
429
|
planChange: PlanChangeLabels;
|
|
422
430
|
/** 0.32.0: the text-size and contrast settings (docs/text-size-harmonization.md §6). */
|
|
423
431
|
appearance: AppearanceLabels;
|
|
432
|
+
/** 0.36.0: resetting this device's settings, the appearance group's last card
|
|
433
|
+
* (docs/settings-harmonization.md §11.3). */
|
|
434
|
+
deviceReset: DeviceResetLabels;
|
|
424
435
|
/** 0.32.0: a row's actions folded into a "⋯" menu at Large (text size §10.8). */
|
|
425
436
|
rowActions: RowActionsLabels;
|
|
426
437
|
/** 0.32.0: the phone bar's "More" cell and sheet (text size §10.7). */
|
|
@@ -440,6 +451,10 @@ export interface UiKitLabels {
|
|
|
440
451
|
/** 0.23.0: `ColumnMapper` / `ColumnRoleTable` — a pasted or dropped table, a role per
|
|
441
452
|
* column. */
|
|
442
453
|
columnMapper: ColumnMapperLabels;
|
|
454
|
+
/** 0.36.0: the notification cards — the types, quiet hours and the zone picker, this
|
|
455
|
+
* device's push, the device list, and the unsubscribe page
|
|
456
|
+
* (docs/notifications-harmonization.md §4.2). */
|
|
457
|
+
notifications: NotificationsLabels;
|
|
443
458
|
}
|
|
444
459
|
|
|
445
460
|
/**
|
|
@@ -660,9 +675,44 @@ export function UiKitProvider({
|
|
|
660
675
|
}),
|
|
661
676
|
[outer, labels, locale, weekStartsOn, linkComponent, chartTooltipPlacement, formatDate, tableStorageKeyPrefix],
|
|
662
677
|
);
|
|
678
|
+
useTableViewsJoin(value.tableStorageKeyPrefix ?? DEFAULT_TABLE_STORAGE_KEY_PREFIX);
|
|
663
679
|
return <KitI18nContext.Provider value={value}>{children}</KitI18nContext.Provider>;
|
|
664
680
|
}
|
|
665
681
|
|
|
682
|
+
/** The kit's own table prefix: `DEFAULT_PERSIST_PREFIX` of components/use-table-state,
|
|
683
|
+
* restated because that module imports the router (a test pins the two equal). */
|
|
684
|
+
const DEFAULT_TABLE_STORAGE_KEY_PREFIX = "hbui-table:";
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Reset this device's settings (docs/settings-harmonization.md §11.4): the tables below a
|
|
688
|
+
* provider keep their views under its prefix, so the provider joins the registry with
|
|
689
|
+
* that prefix — a view, no `reset`, since a table reads its view when it next mounts —
|
|
690
|
+
* while it is mounted. Nested providers with one prefix hold one entry.
|
|
691
|
+
*
|
|
692
|
+
* Through a literal `import()`, not a static import: every catalogue reaches this module,
|
|
693
|
+
* and a catalogue may reach nothing outside i18n/ (scripts/check-module-graph.mjs,
|
|
694
|
+
* english-catalogue-graph.test.ts). The theme stores and the shell import the registry
|
|
695
|
+
* statically, so it is loaded already and the join lands a microtask after the mount.
|
|
696
|
+
*/
|
|
697
|
+
function useTableViewsJoin(prefix: string): void {
|
|
698
|
+
useEffect(() => {
|
|
699
|
+
let leave: (() => void) | undefined;
|
|
700
|
+
let gone = false;
|
|
701
|
+
import("../lib/device-settings").then(
|
|
702
|
+
({ registerDeviceSetting }) => {
|
|
703
|
+
if (!gone) leave = registerDeviceSetting({ key: prefix, prefix: true, kind: "view" });
|
|
704
|
+
},
|
|
705
|
+
// A module of the kit's own that fails to load has nothing to join; the provider
|
|
706
|
+
// and its tables work as before.
|
|
707
|
+
() => undefined,
|
|
708
|
+
);
|
|
709
|
+
return () => {
|
|
710
|
+
gone = true;
|
|
711
|
+
leave?.();
|
|
712
|
+
};
|
|
713
|
+
}, [prefix]);
|
|
714
|
+
}
|
|
715
|
+
|
|
666
716
|
function isRecord(v: unknown): v is Record<string, unknown> {
|
|
667
717
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
668
718
|
}
|
package/src/i18n/locales/en.ts
CHANGED
|
@@ -198,6 +198,12 @@ export function uiKitLabelsEn(numberLocale = "en-US"): UiKitLabels {
|
|
|
198
198
|
? "1 item others can see will pass to an administrator."
|
|
199
199
|
: `${n(count)} items others can see will pass to an administrator.`,
|
|
200
200
|
},
|
|
201
|
+
notifications: {
|
|
202
|
+
...d.notifications,
|
|
203
|
+
testSent: (count) => (count === 1 ? "Test sent to 1 device" : `Test sent to ${n(count)} devices`),
|
|
204
|
+
failures: (count) =>
|
|
205
|
+
count === 1 ? "1 failed delivery in a row" : `${n(count)} failed deliveries in a row`,
|
|
206
|
+
},
|
|
201
207
|
settings: {
|
|
202
208
|
...d.settings,
|
|
203
209
|
matchCount: (count) => (count === 1 ? "1 match" : `${n(count)} matches`),
|