@eifi1/ui-kit 0.49.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -7
- package/dist/admin/user-roster.d.ts +16 -10
- package/dist/admin/user-roster.js +7 -3
- package/dist/admin/user-roster.js.map +1 -1
- package/dist/auth/verify-email.d.ts +9 -1
- package/dist/auth/verify-email.js +3 -1
- package/dist/auth/verify-email.js.map +1 -1
- package/dist/components/amount-input.d.ts +2 -1
- package/dist/components/amount-input.js +3 -2
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/autocomplete.js +1 -0
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/combobox-core.js +1 -0
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox-suggestions.js +1 -0
- package/dist/components/combobox-suggestions.js.map +1 -1
- package/dist/components/currency-select.d.ts +28 -2
- package/dist/components/currency-select.js +56 -10
- package/dist/components/currency-select.js.map +1 -1
- package/dist/components/data-table-mobile.d.ts +11 -2
- package/dist/components/data-table-mobile.js +62 -8
- package/dist/components/data-table-mobile.js.map +1 -1
- package/dist/components/data-table-row.d.ts +1 -1
- package/dist/components/data-table-row.js.map +1 -1
- package/dist/components/data-table.js +3 -1
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/dropdown.js +1 -0
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/full-bleed-dialog.js +1 -0
- package/dist/components/full-bleed-dialog.js.map +1 -1
- package/dist/components/modal.js +1 -0
- package/dist/components/modal.js.map +1 -1
- package/dist/components/number-input.d.ts +2 -1
- package/dist/components/number-input.js +6 -2
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/numpad-sheet.js +2 -0
- package/dist/components/numpad-sheet.js.map +1 -1
- package/dist/components/picker-sheet.js +2 -0
- package/dist/components/picker-sheet.js.map +1 -1
- package/dist/components/popover.js +1 -0
- package/dist/components/popover.js.map +1 -1
- package/dist/eslint/index.js +1 -1
- package/dist/i18n/defaults.js +2 -0
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.js +14 -1
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +3 -0
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/en.js +16 -1
- package/dist/i18n/locales/en.js.map +3 -3
- package/dist/i18n/locales/es.js +14 -1
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.js +14 -1
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.js +14 -1
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.js +14 -1
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.js +14 -1
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.js +1 -0
- package/dist/i18n/review.js.map +1 -1
- package/dist/lib/data-attributes.d.ts +11 -0
- package/dist/lib/data-attributes.js +10 -0
- package/dist/lib/data-attributes.js.map +1 -0
- package/dist/lib/format.d.ts +1 -0
- package/dist/lib/format.js +2 -0
- package/dist/lib/format.js.map +1 -1
- package/dist/lib/minus.d.ts +10 -0
- package/dist/lib/minus.js.map +1 -1
- package/dist/testing/react.d.ts +25 -0
- package/dist/testing/react.js +19 -0
- package/dist/testing/react.js.map +1 -0
- package/dist/tour/tour-dock.d.ts +42 -0
- package/dist/tour/tour-dock.js +80 -0
- package/dist/tour/tour-dock.js.map +1 -0
- package/dist/tour/tour-gates.d.ts +71 -0
- package/dist/tour/tour-gates.js +114 -0
- package/dist/tour/tour-gates.js.map +1 -0
- package/dist/tour/tour-guide-button.d.ts +27 -0
- package/dist/tour/tour-guide-button.js +45 -0
- package/dist/tour/tour-guide-button.js.map +1 -0
- package/dist/tour/tour-guide-labels.d.ts +8 -0
- package/dist/tour/tour-guide-labels.js +9 -0
- package/dist/tour/tour-guide-labels.js.map +1 -0
- package/dist/tour/tour-labels.js +9 -1
- package/dist/tour/tour-labels.js.map +1 -1
- package/dist/tour/tour.d.ts +133 -9
- package/dist/tour/tour.js +594 -70
- package/dist/tour/tour.js.map +1 -1
- package/dist/tour/use-tour-url-sync.d.ts +5 -2
- package/dist/tour/use-tour-url-sync.js +7 -1
- package/dist/tour/use-tour-url-sync.js.map +1 -1
- package/dist/tour.d.ts +2 -0
- package/dist/tour.js +3 -0
- package/dist/tour.js.map +1 -1
- package/dist/wizard/stepper-nav.js +15 -2
- package/dist/wizard/stepper-nav.js.map +1 -1
- package/dist/wizard/types.d.ts +11 -0
- package/dist/wizard/types.js.map +1 -1
- package/dist/wizard/use-wizard.js +11 -5
- package/dist/wizard/use-wizard.js.map +1 -1
- package/package.json +9 -1
- package/src/admin/user-roster.tsx +22 -12
- package/src/auth/verify-email.tsx +10 -0
- package/src/components/amount-input.tsx +5 -4
- package/src/components/autocomplete.tsx +2 -0
- package/src/components/combobox-core.tsx +2 -0
- package/src/components/combobox-suggestions.tsx +2 -0
- package/src/components/currency-select.tsx +95 -6
- package/src/components/data-table-mobile.tsx +99 -8
- package/src/components/data-table-row.tsx +1 -0
- package/src/components/data-table.tsx +9 -1
- package/src/components/dropdown.tsx +2 -0
- package/src/components/full-bleed-dialog.tsx +3 -0
- package/src/components/modal.tsx +3 -0
- package/src/components/number-input.tsx +8 -3
- package/src/components/numpad-sheet.tsx +4 -0
- package/src/components/picker-sheet.tsx +4 -0
- package/src/components/popover.tsx +2 -0
- package/src/i18n/defaults.ts +2 -0
- package/src/i18n/german.ts +13 -0
- package/src/i18n/kit-labels.tsx +3 -0
- package/src/i18n/locales/es.ts +13 -0
- package/src/i18n/locales/fr.ts +13 -0
- package/src/i18n/locales/hu.ts +13 -0
- package/src/i18n/locales/it.ts +13 -0
- package/src/i18n/locales/zh.ts +13 -0
- package/src/i18n/review.ts +1 -0
- package/src/lib/data-attributes.ts +14 -0
- package/src/lib/format.ts +2 -0
- package/src/lib/minus.ts +10 -0
- package/src/testing/react.ts +59 -0
- package/src/tour/tour-dock.ts +132 -0
- package/src/tour/tour-gates.ts +195 -0
- package/src/tour/tour-guide-button.tsx +64 -0
- package/src/tour/tour-guide-labels.ts +15 -0
- package/src/tour/tour-labels.ts +8 -0
- package/src/tour/tour.tsx +949 -86
- package/src/tour/use-tour-url-sync.ts +14 -4
- package/src/tour.ts +3 -0
- package/src/wizard/stepper-nav.tsx +23 -3
- package/src/wizard/types.ts +12 -0
- package/src/wizard/use-wizard.ts +16 -9
|
@@ -41,10 +41,11 @@ import {
|
|
|
41
41
|
} from "./field-parts";
|
|
42
42
|
import { useEndWidth, useNumericField } from "./numeric-field";
|
|
43
43
|
import type { CommitScope } from "./write-lock";
|
|
44
|
+
import { dataAttributes, type DataAttributes } from "../lib/data-attributes";
|
|
44
45
|
|
|
45
46
|
/** {@link AmountInput}'s props — exported since 0.34 (refactor audit 2026-10, API-9): a
|
|
46
47
|
* wrapper typed itself off `ComponentProps<typeof AmountInput>` until then. */
|
|
47
|
-
export interface AmountInputProps extends FieldHelpProps {
|
|
48
|
+
export interface AmountInputProps extends FieldHelpProps, DataAttributes {
|
|
48
49
|
value: string;
|
|
49
50
|
onChange: (value: string) => void;
|
|
50
51
|
currency?: string;
|
|
@@ -427,8 +428,8 @@ export function AmountInput(props: AmountInputProps) {
|
|
|
427
428
|
const { lock, locked, off, isMobile, asDisplay, showCalc, showNumpad, setFocused, mark, draft, setDraft, derived } =
|
|
428
429
|
numeric;
|
|
429
430
|
const invalid = numeric.isInvalid;
|
|
430
|
-
// A locked chip is a read-out: picking a currency is a change too.
|
|
431
|
-
const editable = !!onCurrencyChange && !locked;
|
|
431
|
+
// A locked or disabled chip is a read-out: picking a currency is a change too.
|
|
432
|
+
const editable = !!onCurrencyChange && !locked && !off;
|
|
432
433
|
// Room for the trailing controls — the calculator and the currency chip — once
|
|
433
434
|
// measured, as NumberInput reserves its own (keksdose live #367, DUP-10): their
|
|
434
435
|
// width, the track's `end-1` inset in the boxed shape, and NumberInput's 4px gap.
|
|
@@ -533,7 +534,7 @@ export function AmountInput(props: AmountInputProps) {
|
|
|
533
534
|
const selected = getCurrency(currency);
|
|
534
535
|
|
|
535
536
|
return (
|
|
536
|
-
<div className={cn("w-full", className)}>
|
|
537
|
+
<div {...dataAttributes(props)} className={cn("w-full", className)}>
|
|
537
538
|
{/* Inner relative box holds the input + its absolutely-positioned
|
|
538
539
|
trailing controls (calculator / currency), so those stay anchored to
|
|
539
540
|
the INPUT even when the mobile math bar is rendered below (#334). It is
|
|
@@ -519,6 +519,8 @@ export function Autocomplete<V extends string | number = string>(props: Autocomp
|
|
|
519
519
|
// Every press inside the list keeps focus in the field — rows, the status
|
|
520
520
|
// line, the scrollbar — or the input's blur would close the list first.
|
|
521
521
|
role="presentation"
|
|
522
|
+
// A kit layer, for the tour's layer order (0.50, docs/tours-harmonization.md §11.6).
|
|
523
|
+
data-kit-layer=""
|
|
522
524
|
// Portalled out of the form's `dir`; the field's is put back. The list is
|
|
523
525
|
// exactly the field's width, so `left` places it in either direction.
|
|
524
526
|
dir={dir}
|
|
@@ -1064,6 +1064,8 @@ export function ComboboxPanel<V extends string | number>({
|
|
|
1064
1064
|
// `aria-describedby` has nothing to collide with.
|
|
1065
1065
|
{...rest}
|
|
1066
1066
|
ref={panelRef}
|
|
1067
|
+
// A kit layer, for the tour's layer order (0.50, docs/tours-harmonization.md §11.6).
|
|
1068
|
+
data-kit-layer=""
|
|
1067
1069
|
dir={dir}
|
|
1068
1070
|
onKeyDown={onKeyDown}
|
|
1069
1071
|
// A press in a panel opened from a row is inside this list for the core's outside
|
|
@@ -92,6 +92,8 @@ export function SuggestionList({
|
|
|
92
92
|
return createPortal(
|
|
93
93
|
<div
|
|
94
94
|
ref={panelRef}
|
|
95
|
+
// A kit layer, for the tour's layer order (0.50, docs/tours-harmonization.md §11.6).
|
|
96
|
+
data-kit-layer=""
|
|
95
97
|
// z-50, not the old z-30: the list is a child of <body> now, so it is
|
|
96
98
|
// competing with the app's own overlays rather than with its own siblings.
|
|
97
99
|
className="fixed z-50"
|
|
@@ -2,7 +2,16 @@ import { useCallback, useId } from "react";
|
|
|
2
2
|
import type { ComponentPropsWithoutRef, ReactNode, Ref } from "react";
|
|
3
3
|
import { FieldChevron, FIELD_TRIGGER, FIELD_INVALID, FIELD_FLOATING_PAD } from "./field-base";
|
|
4
4
|
import { FieldLabel } from "./field-parts";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
assignRef,
|
|
7
|
+
EndHintRow,
|
|
8
|
+
LockedReason,
|
|
9
|
+
useFieldMessages,
|
|
10
|
+
useLockReason,
|
|
11
|
+
type DisabledReasonDisplay,
|
|
12
|
+
type FieldHelpProps,
|
|
13
|
+
} from "./field-parts";
|
|
14
|
+
import type { CommitScope } from "./write-lock";
|
|
6
15
|
import { cn } from "../lib/cn";
|
|
7
16
|
import { useComboboxCore } from "./combobox-core";
|
|
8
17
|
import {
|
|
@@ -90,6 +99,31 @@ export interface CurrencySelectProps extends Omit<ComponentPropsWithoutRef<"div"
|
|
|
90
99
|
* filter that only matches the hidden English name is worse than no filter.
|
|
91
100
|
*/
|
|
92
101
|
currencyNames?: Record<string, string>;
|
|
102
|
+
/** As {@link Select}'s: no focus, no list, the settled look, no chevron. */
|
|
103
|
+
disabled?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Why the currency cannot be changed — {@link Select}'s `disabledReason` (keksdose,
|
|
106
|
+
* 2026-10-23: a budget's currency for a guest or a viewer, and while offline, which
|
|
107
|
+
* keksdose drew as a one-option `Select` to keep the reason and lost the flag). The
|
|
108
|
+
* trigger stays focusable and `aria-disabled` (never the `disabled` attribute), keeps
|
|
109
|
+
* its flag and code, opens nothing, and `onChange` is never called; the reason is a
|
|
110
|
+
* line under the field at Large and on a touch screen, else in the kit
|
|
111
|
+
* {@link Tooltip} (`disabledReasonDisplay`), and on the trigger's `aria-describedby`
|
|
112
|
+
* (after a caption, before an error). It wears the settled look of a disabled field
|
|
113
|
+
* and drops its chevron, as a locked Select does. It wins over `disabled`.
|
|
114
|
+
*/
|
|
115
|
+
disabledReason?: ReactNode;
|
|
116
|
+
/** Where the reason shows — {@link Select}'s `disabledReasonDisplay`: a line under the
|
|
117
|
+
* field at Large and on a touch screen by default, the tooltip otherwise. */
|
|
118
|
+
disabledReasonDisplay?: DisabledReasonDisplay;
|
|
119
|
+
/**
|
|
120
|
+
* This picker COMMITS — choosing saves. Under a locked {@link WriteLockProvider} it is
|
|
121
|
+
* locked the `disabledReason` way with the lock's reason (which wins over its own).
|
|
122
|
+
* No provider, or an unlocked one: no effect. {@link Select}'s `commit`. A currency
|
|
123
|
+
* field inside a form with its own Save stays editable under the lock — leave this off
|
|
124
|
+
* there, and put `commit` on the Save.
|
|
125
|
+
*/
|
|
126
|
+
commit?: CommitScope;
|
|
93
127
|
/** The TRIGGER — the focusable `<button>` — as a React 19 ref prop, as on
|
|
94
128
|
* {@link MultiSelect} and {@link CountrySelect}: react-hook-form's focus-on-error
|
|
95
129
|
* calls `focus()` on it (0.34, API-3, refactor audit 2026-10). Not the wrapper. */
|
|
@@ -109,6 +143,10 @@ export function CurrencySelect({
|
|
|
109
143
|
help,
|
|
110
144
|
labels,
|
|
111
145
|
currencyNames,
|
|
146
|
+
disabled,
|
|
147
|
+
disabledReason,
|
|
148
|
+
disabledReasonDisplay,
|
|
149
|
+
commit,
|
|
112
150
|
"aria-label": ariaLabel,
|
|
113
151
|
"aria-describedby": ariaDescribedBy,
|
|
114
152
|
"aria-invalid": ariaInvalid,
|
|
@@ -137,6 +175,15 @@ export function CurrencySelect({
|
|
|
137
175
|
|
|
138
176
|
const selected = getCurrency(value);
|
|
139
177
|
|
|
178
|
+
// The lock — Select's (`useLockReason`): the WriteLock's reason under `commit`, else
|
|
179
|
+
// the caller's own. Locked or `disabled`, nothing opens the list or picks a value.
|
|
180
|
+
const lock = useLockReason(commit, disabledReason);
|
|
181
|
+
const locked = lock.locked;
|
|
182
|
+
const inert = locked || Boolean(disabled);
|
|
183
|
+
// A lock (or `disabled`) arriving while the list is up closes it — adjusted while
|
|
184
|
+
// rendering, as CountrySelect does, so no frame shows a list that cannot be used.
|
|
185
|
+
if (inert && open) setOpen(false);
|
|
186
|
+
|
|
140
187
|
const listboxId = `${useId()}-listbox`;
|
|
141
188
|
// Text is a caption under the field; anything else (a FieldHint) rides the label
|
|
142
189
|
// line — and a FieldHint is that caption at Large and on touch (§4). See `hint`. On
|
|
@@ -152,6 +199,8 @@ export function CurrencySelect({
|
|
|
152
199
|
invalid,
|
|
153
200
|
describedBy: ariaDescribedBy,
|
|
154
201
|
ariaInvalid,
|
|
202
|
+
// A lock's reason sits between the caption and the error, as on Select.
|
|
203
|
+
reasonId: locked && lock.reasonId,
|
|
155
204
|
});
|
|
156
205
|
const { labelHint, describedBy, isInvalid } = field;
|
|
157
206
|
|
|
@@ -169,10 +218,21 @@ export function CurrencySelect({
|
|
|
169
218
|
ref={setTrigger}
|
|
170
219
|
type="button"
|
|
171
220
|
aria-label={name}
|
|
172
|
-
|
|
221
|
+
// The focusable kind of disabled while locked (Select's rule): native `disabled`
|
|
222
|
+
// would take the field out of the tab order, and the reason with it.
|
|
223
|
+
disabled={locked ? undefined : disabled}
|
|
224
|
+
aria-disabled={locked || undefined}
|
|
225
|
+
onClick={(e) => {
|
|
226
|
+
if (inert) {
|
|
227
|
+
e.preventDefault();
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
setOpen((v) => !v);
|
|
231
|
+
}}
|
|
173
232
|
// Down/Up opens the list from the closed trigger, per the APG; Enter and Space
|
|
174
233
|
// already do through the button's own click.
|
|
175
234
|
onKeyDown={(e) => {
|
|
235
|
+
if (inert) return;
|
|
176
236
|
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
177
237
|
e.preventDefault();
|
|
178
238
|
setOpen(true);
|
|
@@ -191,12 +251,27 @@ export function CurrencySelect({
|
|
|
191
251
|
aria-expanded={open}
|
|
192
252
|
aria-invalid={isInvalid || undefined}
|
|
193
253
|
aria-describedby={describedBy}
|
|
194
|
-
className={cn(
|
|
254
|
+
className={cn(
|
|
255
|
+
FIELD_TRIGGER,
|
|
256
|
+
"pe-9",
|
|
257
|
+
label !== undefined && FIELD_FLOATING_PAD,
|
|
258
|
+
// FIELD_BASE's `disabled:` look, for a trigger locked without being `disabled`
|
|
259
|
+
// (the `:disabled` variants cannot match it) — Select's FIELD_LOCKED. No hover:
|
|
260
|
+
// nothing here answers a press.
|
|
261
|
+
locked && "cursor-not-allowed bg-surface-2 text-muted hover:bg-surface-2",
|
|
262
|
+
isInvalid && FIELD_INVALID,
|
|
263
|
+
)}
|
|
195
264
|
>
|
|
196
265
|
{/* The trigger stays COMPACT — flag + code only — so it never clips in a
|
|
197
266
|
narrow field; the full names live in the (wider) popup (feedback
|
|
198
267
|
#308). */}
|
|
199
|
-
<span
|
|
268
|
+
<span
|
|
269
|
+
className={cn(
|
|
270
|
+
"flex items-center gap-2 min-w-0",
|
|
271
|
+
// The settled look reaches the code too: `text-primary` here would outweigh it.
|
|
272
|
+
!selected ? "text-placeholder" : inert ? "text-muted" : "text-primary",
|
|
273
|
+
)}
|
|
274
|
+
>
|
|
200
275
|
{selected ? (
|
|
201
276
|
<>
|
|
202
277
|
<CurrencyFlag country={selected.country} />
|
|
@@ -206,10 +281,23 @@ export function CurrencySelect({
|
|
|
206
281
|
placeholder ?? text.currency
|
|
207
282
|
)}
|
|
208
283
|
</span>
|
|
209
|
-
|
|
284
|
+
{/* No menu to drop while disabled or locked, so no chevron — the arrow is the one
|
|
285
|
+
thing on the control that promises a choice (dev#474, as on Select). */}
|
|
286
|
+
{!inert && <FieldChevron />}
|
|
210
287
|
</button>
|
|
211
288
|
);
|
|
212
289
|
|
|
290
|
+
// Select's frame: the line under the trigger, or the Tooltip with a hidden copy for
|
|
291
|
+
// `aria-describedby`. The wrapper `<div>` keeps `className`, so the layout holds.
|
|
292
|
+
// `flex w-full items-stretch`: the trigger keeps the field's full width in it.
|
|
293
|
+
const control = locked ? (
|
|
294
|
+
<LockedReason lock={lock} display={disabledReasonDisplay} className="flex w-full items-stretch">
|
|
295
|
+
{trigger}
|
|
296
|
+
</LockedReason>
|
|
297
|
+
) : (
|
|
298
|
+
trigger
|
|
299
|
+
);
|
|
300
|
+
|
|
213
301
|
return (
|
|
214
302
|
// The wrapper takes `rest`; the trigger takes the name. This field's label is a
|
|
215
303
|
// floating <span> rather than a <label for>, so a caller who wants the control to
|
|
@@ -230,7 +318,7 @@ export function CurrencySelect({
|
|
|
230
318
|
))}
|
|
231
319
|
{/* No label line to ride: at the field's end edge, outside the box, as an
|
|
232
320
|
unlabelled Select places its FieldHint. */}
|
|
233
|
-
<EndHintRow hint={label === undefined ? labelHint : undefined}>{
|
|
321
|
+
<EndHintRow hint={label === undefined ? labelHint : undefined}>{control}</EndHintRow>
|
|
234
322
|
{field.below}
|
|
235
323
|
{/* Portalled to <body> and anchored to the trigger (dev#548), so where it stands
|
|
236
324
|
in the flow no longer moves it. */}
|
|
@@ -246,6 +334,7 @@ export function CurrencySelect({
|
|
|
246
334
|
// to say what it asks for (live #200).
|
|
247
335
|
sheetTitle={label ?? text.currency}
|
|
248
336
|
onChoose={(code) => {
|
|
337
|
+
if (inert) return;
|
|
249
338
|
onChange(code);
|
|
250
339
|
// Back to the trigger: the panel holding focus is about to unmount, and a
|
|
251
340
|
// keyboard user who has answered the field should stand on it, ready to Tab on.
|
|
@@ -72,7 +72,18 @@ const CARD_NO_TRUNCATE =
|
|
|
72
72
|
* BETWEEN facts rather than between a label and its own value. `relative` holds a
|
|
73
73
|
* header's `sr-only` text (an actions column's), as every kit container must.
|
|
74
74
|
*/
|
|
75
|
-
export function StackedFields<T>({
|
|
75
|
+
export function StackedFields<T>({
|
|
76
|
+
columns,
|
|
77
|
+
row,
|
|
78
|
+
compact,
|
|
79
|
+
liftOwnClicks = false,
|
|
80
|
+
}: {
|
|
81
|
+
columns: DataTableColumn<T>[];
|
|
82
|
+
row: T;
|
|
83
|
+
compact: boolean;
|
|
84
|
+
/** On a stretched card (0.50 K4): lift the own-click fields above its title link. */
|
|
85
|
+
liftOwnClicks?: boolean;
|
|
86
|
+
}) {
|
|
76
87
|
const fields = filledFields(columns, row);
|
|
77
88
|
if (fields.length === 0) return null;
|
|
78
89
|
return (
|
|
@@ -85,7 +96,7 @@ export function StackedFields<T>({ columns, row, compact }: { columns: DataTable
|
|
|
85
96
|
<dt className="text-xs uppercase tracking-wide text-muted">{col.header}</dt>
|
|
86
97
|
<dd
|
|
87
98
|
{...dataAttrsOf(col.cellProps?.(row))}
|
|
88
|
-
className="mt-0.5 min-w-0 break-words text-secondary"
|
|
99
|
+
className={cn("mt-0.5 min-w-0 break-words text-secondary", liftOwnClicks && keepsOwnClicks(col) && LIFTED_FIELD)}
|
|
89
100
|
>
|
|
90
101
|
{value}
|
|
91
102
|
</dd>
|
|
@@ -95,6 +106,21 @@ export function StackedFields<T>({ columns, row, compact }: { columns: DataTable
|
|
|
95
106
|
);
|
|
96
107
|
}
|
|
97
108
|
|
|
109
|
+
/**
|
|
110
|
+
* A stretched card's own-click cell (0.50 K4): above the title control's overlay
|
|
111
|
+
* (`after:z-[1]`), so its link or buttons take their own taps. `w-fit` keeps the lifted
|
|
112
|
+
* box to the cell's content, so the field's empty rest still opens the row.
|
|
113
|
+
*/
|
|
114
|
+
const LIFTED_CELL = "relative z-[2]";
|
|
115
|
+
const LIFTED_FIELD = cn(LIFTED_CELL, "w-fit max-w-full justify-self-start");
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The keyboard frame of a stretched card (0.50 K4): FOCUS_RING's, inside the card,
|
|
119
|
+
* while its title control (`data-card-link`) has the focus.
|
|
120
|
+
*/
|
|
121
|
+
const STRETCHED_RING =
|
|
122
|
+
"has-[[data-card-link]:focus-visible]:ring-inset has-[[data-card-link]:focus-visible]:ring-focus has-[[data-card-link]:focus-visible]:ring-brand";
|
|
123
|
+
|
|
98
124
|
/** The most fields the default phone card shows under its title (0.49 KIT-7). */
|
|
99
125
|
export const MOBILE_CARD_FIELDS = 3;
|
|
100
126
|
|
|
@@ -308,6 +334,13 @@ export interface MobileRowProps<T> {
|
|
|
308
334
|
mobileDir: Direction;
|
|
309
335
|
/** No column the card renders owns a link, so the card itself may be one. */
|
|
310
336
|
mobileCardLinkable: boolean;
|
|
337
|
+
/**
|
|
338
|
+
* The card draws cells that keep their own clicks, so it cannot be one control (0.50
|
|
339
|
+
* K4): its TITLE is the row's link (`rowHref`) or button (`onRowClick`), stretched over
|
|
340
|
+
* the card, and those cells sit above it. Only for the default card, never a
|
|
341
|
+
* `mobileCard`, and never when the title itself keeps its own clicks.
|
|
342
|
+
*/
|
|
343
|
+
mobileCardStretched: boolean;
|
|
311
344
|
mobileCard: DataTableProps<T>["mobileCard"];
|
|
312
345
|
mobilePrimaryCol: DataTableColumn<T> | null;
|
|
313
346
|
/** The `role: "status"` columns, drawn as chips beside the title (0.49 KIT-7). */
|
|
@@ -349,6 +382,7 @@ export function MobileRow<T>({
|
|
|
349
382
|
swipeActionsFor,
|
|
350
383
|
mobileDir,
|
|
351
384
|
mobileCardLinkable,
|
|
385
|
+
mobileCardStretched,
|
|
352
386
|
mobileCard,
|
|
353
387
|
mobilePrimaryCol,
|
|
354
388
|
mobileStatusColumns,
|
|
@@ -389,7 +423,10 @@ export function MobileRow<T>({
|
|
|
389
423
|
// scrolling never meet the gesture. Until 0.40 an open card silently lost it.
|
|
390
424
|
// In selection mode a sideways drag would be a second meaning for a selecting touch.
|
|
391
425
|
const swipeEnabled = !!swipe && !selecting;
|
|
392
|
-
|
|
426
|
+
// The stretched card (0.50 K4): the title is the control, the card a plain box.
|
|
427
|
+
// Selection mode keeps its own card (the whole card is the toggle there).
|
|
428
|
+
const stretched = mobileCardStretched && interactive && !selecting && !mobileCard && !!mobilePrimaryCol;
|
|
429
|
+
const href = mobileCardLinkable || stretched ? rowHref?.(row) : undefined;
|
|
393
430
|
const cardClass = cn(
|
|
394
431
|
"w-full text-start flex items-center",
|
|
395
432
|
// §4: more room per row at Large — a taller row is a bigger tap target too.
|
|
@@ -409,7 +446,13 @@ export function MobileRow<T>({
|
|
|
409
446
|
// AppShell main's `overflow-x-clip` — so the frame went missing on one side. A press
|
|
410
447
|
// keeps the fill and the squeeze above; an outline on every tap of a long list would
|
|
411
448
|
// be louder than the acknowledgement it replaces.
|
|
412
|
-
(interactive || selecting) &&
|
|
449
|
+
(interactive || selecting) &&
|
|
450
|
+
!stretched &&
|
|
451
|
+
cn("[-webkit-tap-highlight-color:transparent] focus-visible:ring-inset", FOCUS_RING),
|
|
452
|
+
// A stretched card (0.50 K4) draws the same frame, inside the card, while its TITLE
|
|
453
|
+
// control has the keyboard: the ring outlines the whole tap target, as on every other
|
|
454
|
+
// card, not the title's line alone. The own-click cells draw their own.
|
|
455
|
+
stretched && cn("relative [-webkit-tap-highlight-color:transparent]", STRETCHED_RING),
|
|
413
456
|
selecting && "cursor-pointer",
|
|
414
457
|
select?.selected && selecting && "bg-brand-soft",
|
|
415
458
|
// A 500 ms hold would otherwise start a text selection, and iOS's link callout —
|
|
@@ -424,6 +467,45 @@ export function MobileRow<T>({
|
|
|
424
467
|
// exactly one anchor exists per row.
|
|
425
468
|
// The default card's filled fields and status values (0.49 KIT-7): an empty one
|
|
426
469
|
// gets no line. Not computed for a `mobileCard`, which draws its own.
|
|
470
|
+
// The stretched card's title control (0.50 K4): the row's link, or a real <button>
|
|
471
|
+
// when the table only has `onRowClick` — a plain control, since nothing interactive
|
|
472
|
+
// sits inside it any more. Its `::after` covers the whole card, so a tap anywhere
|
|
473
|
+
// but on an own-click cell (lifted above it) lands on it, and a long press on the
|
|
474
|
+
// card is a long press on the link ("open in new tab"). Its accessible name is the
|
|
475
|
+
// title alone; the fields read as text after it. `data-row-link` marks it as the
|
|
476
|
+
// row itself for `fromOwnControl`, so the long press still arms on it.
|
|
477
|
+
const stretchedClass =
|
|
478
|
+
"block w-full text-start focus-visible:outline-none after:absolute after:inset-0 after:z-[1] after:content-['']";
|
|
479
|
+
const titleControl = (title: ReactNode) =>
|
|
480
|
+
href ? (
|
|
481
|
+
<RowLink
|
|
482
|
+
href={href}
|
|
483
|
+
data-card-link=""
|
|
484
|
+
data-row-link=""
|
|
485
|
+
onActivate={() => onRowClick!(row)}
|
|
486
|
+
aria-expanded={opensRow ? expanded : undefined}
|
|
487
|
+
className={stretchedClass}
|
|
488
|
+
// Enter follows RowLink's own onClick; Space opens the row too, not the list's scroll.
|
|
489
|
+
onKeyDown={(e) => {
|
|
490
|
+
if (e.key !== " ") return;
|
|
491
|
+
e.preventDefault();
|
|
492
|
+
onRowClick!(row);
|
|
493
|
+
}}
|
|
494
|
+
>
|
|
495
|
+
{title}
|
|
496
|
+
</RowLink>
|
|
497
|
+
) : (
|
|
498
|
+
<button
|
|
499
|
+
type="button"
|
|
500
|
+
data-card-link=""
|
|
501
|
+
data-row-link=""
|
|
502
|
+
aria-expanded={opensRow ? expanded : undefined}
|
|
503
|
+
onClick={() => onRowClick!(row)}
|
|
504
|
+
className={cn(stretchedClass, "font-[inherit]")}
|
|
505
|
+
>
|
|
506
|
+
{title}
|
|
507
|
+
</button>
|
|
508
|
+
);
|
|
427
509
|
const fields = mobileCard ? [] : filledFields(mobileSecondaryColumns, row);
|
|
428
510
|
const statuses = mobileCard ? [] : filledFields(mobileStatusColumns, row);
|
|
429
511
|
const actionCells = mobileCard ? [] : filledFields(mobileActionColumns, row);
|
|
@@ -455,7 +537,7 @@ export function MobileRow<T>({
|
|
|
455
537
|
data-card-title=""
|
|
456
538
|
className="min-w-0 flex-1 break-words font-medium"
|
|
457
539
|
>
|
|
458
|
-
{mobilePrimaryCol.cell(row)}
|
|
540
|
+
{stretched ? titleControl(mobilePrimaryCol.cell(row)) : mobilePrimaryCol.cell(row)}
|
|
459
541
|
</div>
|
|
460
542
|
)}
|
|
461
543
|
{statuses.length > 0 && (
|
|
@@ -472,7 +554,7 @@ export function MobileRow<T>({
|
|
|
472
554
|
{fields.length > 0 && large ? (
|
|
473
555
|
// §4: each label ABOVE its value at Large. Side by side, a label column
|
|
474
556
|
// sized to the widest label leaves a value a sliver of a 240 px card.
|
|
475
|
-
<StackedFields columns={mobileSecondaryColumns} row={row} compact={compact} />
|
|
557
|
+
<StackedFields columns={mobileSecondaryColumns} row={row} compact={compact} liftOwnClicks={stretched} />
|
|
476
558
|
) : fields.length > 0 && (
|
|
477
559
|
<dl
|
|
478
560
|
className={cn(
|
|
@@ -487,7 +569,7 @@ export function MobileRow<T>({
|
|
|
487
569
|
</dt>
|
|
488
570
|
<dd
|
|
489
571
|
{...dataAttrsOf(col.cellProps?.(row))}
|
|
490
|
-
className="min-w-0 text-secondary self-center"
|
|
572
|
+
className={cn("min-w-0 text-secondary self-center", stretched && keepsOwnClicks(col) && LIFTED_FIELD)}
|
|
491
573
|
>
|
|
492
574
|
{value}
|
|
493
575
|
</dd>
|
|
@@ -500,7 +582,11 @@ export function MobileRow<T>({
|
|
|
500
582
|
{actionCells.length > 0 && (
|
|
501
583
|
<div data-card-actions="" className="flex min-w-0 flex-wrap items-center justify-end gap-2">
|
|
502
584
|
{actionCells.map(({ col, value }) => (
|
|
503
|
-
<div
|
|
585
|
+
<div
|
|
586
|
+
key={col.key}
|
|
587
|
+
{...dataAttrsOf(col.cellProps?.(row))}
|
|
588
|
+
className={cn("flex min-w-0 justify-end", stretched && LIFTED_CELL)}
|
|
589
|
+
>
|
|
504
590
|
{value}
|
|
505
591
|
</div>
|
|
506
592
|
))}
|
|
@@ -592,6 +678,11 @@ export function MobileRow<T>({
|
|
|
592
678
|
/>
|
|
593
679
|
{cardInner}
|
|
594
680
|
</div>
|
|
681
|
+
) : stretched ? (
|
|
682
|
+
// The stretched card (0.50 K4): a plain box; its title is the control (above).
|
|
683
|
+
<div {...press} data-card-stretched="" className={cardClass}>
|
|
684
|
+
{cardInner}
|
|
685
|
+
</div>
|
|
595
686
|
) : interactive && href ? (
|
|
596
687
|
// A card with a URL of its own is a LINK, not a div wearing role="button"
|
|
597
688
|
// (feedback #451): the phone's long-press "open in new tab" and a screen
|
|
@@ -1483,6 +1483,13 @@ export function DataTable<T>({
|
|
|
1483
1483
|
// renderer is opaque to us; a link inside one is the caller's to declare by
|
|
1484
1484
|
// marking the column it comes from.
|
|
1485
1485
|
const mobileCardLinkable = (mobileCard ? mobileColumns : [...onCard]).every((c) => !keepsOwnClicks(c));
|
|
1486
|
+
// ...except on the default card (0.50 K4, kastlan): its title becomes the row's link
|
|
1487
|
+
// (or, with only `onRowClick`, a button), stretched over the card, and the own-click
|
|
1488
|
+
// cells — a tel: link, the actions row — sit above it, so "tap to open" stays and no
|
|
1489
|
+
// control nests in another. A `mobileCard` stays the fallback above (its controls
|
|
1490
|
+
// cannot be lifted), and so does a card whose title keeps its own clicks.
|
|
1491
|
+
const mobileCardStretched =
|
|
1492
|
+
!mobileCard && !mobileCardLinkable && !!mobilePrimaryCol && !keepsOwnClicks(mobilePrimaryCol);
|
|
1486
1493
|
// The card list's reading direction, for the one thing on it CSS cannot flip: which
|
|
1487
1494
|
// physical drag a logical `start`/`end` swipe action means. Read off the DOM through
|
|
1488
1495
|
// a ref callback (it runs at commit, like a layout effect) because `dir` is an
|
|
@@ -1646,10 +1653,11 @@ export function DataTable<T>({
|
|
|
1646
1653
|
className={rowClassName?.(row)}
|
|
1647
1654
|
attrs={rowAttributes?.(row)}
|
|
1648
1655
|
interactive={!!onRowClick}
|
|
1649
|
-
href={mobileCardLinkable ? rowHref?.(row) : undefined}
|
|
1656
|
+
href={mobileCardLinkable || mobileCardStretched ? rowHref?.(row) : undefined}
|
|
1650
1657
|
swipeActionsFor={swipeActionsFor}
|
|
1651
1658
|
mobileDir={mobileDir}
|
|
1652
1659
|
mobileCardLinkable={mobileCardLinkable}
|
|
1660
|
+
mobileCardStretched={mobileCardStretched}
|
|
1653
1661
|
mobileCard={mobileCard}
|
|
1654
1662
|
mobilePrimaryCol={mobilePrimaryCol}
|
|
1655
1663
|
mobileStatusColumns={mobileStatusColumns}
|
|
@@ -432,6 +432,8 @@ export function DropdownPanel({
|
|
|
432
432
|
<div
|
|
433
433
|
{...rest}
|
|
434
434
|
ref={setPanel}
|
|
435
|
+
// A kit layer, for the tour's layer order (0.50, docs/tours-harmonization.md §11.6).
|
|
436
|
+
data-kit-layer=""
|
|
435
437
|
dir={dir}
|
|
436
438
|
onKeyDown={handleKeyDown}
|
|
437
439
|
onPointerDownCapture={handlePointerDownCapture}
|
|
@@ -208,6 +208,9 @@ export function FullBleedDialog({
|
|
|
208
208
|
// `tabIndex` below by passing one of them too — this is the element the focus
|
|
209
209
|
// trap holds, and each of those failures is invisible on screen.
|
|
210
210
|
{...rest}
|
|
211
|
+
// A kit layer, for the tour's layer order (0.50, docs/tours-harmonization.md §11.6):
|
|
212
|
+
// a backdrop, so the tour measures the panel inside it to tell a full-screen sheet.
|
|
213
|
+
data-kit-layer="backdrop"
|
|
211
214
|
className={cn(
|
|
212
215
|
"fixed inset-0 z-50 flex items-stretch justify-center bg-black/40 outline-none",
|
|
213
216
|
closing ? "animate-overlay-out" : "animate-overlay",
|
package/src/components/modal.tsx
CHANGED
|
@@ -394,6 +394,9 @@ function ModalPanel({
|
|
|
394
394
|
// that page from a screen reader for 220ms. Not `inert` — that would let a tap
|
|
395
395
|
// fall through the fading backdrop to the row underneath.
|
|
396
396
|
aria-hidden={exiting || undefined}
|
|
397
|
+
// A kit layer, for the tour's layer order (0.50, docs/tours-harmonization.md §11.6):
|
|
398
|
+
// a backdrop, so the tour measures the panel inside it to tell a full-screen sheet.
|
|
399
|
+
data-kit-layer="backdrop"
|
|
397
400
|
className={cn(
|
|
398
401
|
// Live #320. The panel below rises only where it is bottom-anchored: from md up
|
|
399
402
|
// it is centred, and a centred box sliding up from off-screen reads as a
|
|
@@ -16,10 +16,11 @@ import { useEndWidth, useNumericField } from "./numeric-field";
|
|
|
16
16
|
import type { CommitScope } from "./write-lock";
|
|
17
17
|
import { cn } from "../lib/cn";
|
|
18
18
|
import { commitExpression, formatResult } from "../lib/calc";
|
|
19
|
+
import { dataAttributes, type DataAttributes } from "../lib/data-attributes";
|
|
19
20
|
|
|
20
21
|
/** {@link NumberInput}'s props — exported since 0.34 (refactor audit 2026-10, API-9): a
|
|
21
22
|
* wrapper typed itself off `ComponentProps<typeof NumberInput>` until then. */
|
|
22
|
-
export interface NumberInputProps extends FieldHelpProps {
|
|
23
|
+
export interface NumberInputProps extends FieldHelpProps, DataAttributes {
|
|
23
24
|
/** The text, DOT-decimal ("1234.5", or a calculation being typed: "12+5"), as
|
|
24
25
|
* `onChange` reports it — so a caller's `Number(value)` reads it. The field SHOWS it
|
|
25
26
|
* in the locale's mark (see `locale`). A comma here is read as the decimal mark too,
|
|
@@ -549,12 +550,16 @@ export function NumberInput(props: NumberInputProps) {
|
|
|
549
550
|
) : (
|
|
550
551
|
field
|
|
551
552
|
);
|
|
552
|
-
|
|
553
|
+
const data = dataAttributes(props);
|
|
554
|
+
if (numeric.hint.captionText === undefined && numeric.errorEl === null && !reserve) {
|
|
555
|
+
// Bare, unless the caller tags it: then the same plain root as below carries the tags.
|
|
556
|
+
return Object.keys(data).length > 0 ? <div {...data}>{box}</div> : box;
|
|
557
|
+
}
|
|
553
558
|
// Outside the field's own `relative` box, as Select's caption is: the calculator
|
|
554
559
|
// and the unit are `inset-y-0` in it and would stretch down over a second line.
|
|
555
560
|
// `className` stays on the field, so adding a caption cannot change what it styles.
|
|
556
561
|
return (
|
|
557
|
-
<div>
|
|
562
|
+
<div {...data}>
|
|
558
563
|
{box}
|
|
559
564
|
<FieldCaption parts={numeric.hint} />
|
|
560
565
|
{numeric.errorEl}
|
|
@@ -209,6 +209,10 @@ export function NumberPadSheet({
|
|
|
209
209
|
<div
|
|
210
210
|
role="group"
|
|
211
211
|
aria-label={text.panel}
|
|
212
|
+
// A kit layer, and the bottom layer a docked tour card stays clear of (0.50,
|
|
213
|
+
// docs/tours-harmonization.md §11.6).
|
|
214
|
+
data-kit-layer=""
|
|
215
|
+
data-tour-obstruction=""
|
|
212
216
|
// Keep the host input focused when tapping the pad: preventDefault on
|
|
213
217
|
// pointerdown blocks the focus/blur, while the buttons' click still fires.
|
|
214
218
|
onPointerDown={(e) => e.preventDefault()}
|
|
@@ -211,6 +211,10 @@ export function PickerSheet({
|
|
|
211
211
|
// caller has something to add.
|
|
212
212
|
{...rest}
|
|
213
213
|
ref={setSheet}
|
|
214
|
+
// A kit layer and a bottom layer a docked tour card stays clear of (0.50,
|
|
215
|
+
// docs/tours-harmonization.md §11.6).
|
|
216
|
+
data-kit-layer=""
|
|
217
|
+
data-tour-obstruction=""
|
|
214
218
|
role="dialog"
|
|
215
219
|
aria-modal="true"
|
|
216
220
|
// Without it the container cannot take focus, and `useFocusTrap` has nothing to
|
|
@@ -206,6 +206,8 @@ export function Popover({
|
|
|
206
206
|
// to remove one of them by accident.
|
|
207
207
|
{...rest}
|
|
208
208
|
ref={setPanel}
|
|
209
|
+
// A kit layer, for the tour's layer order (0.50, docs/tours-harmonization.md §11.6).
|
|
210
|
+
data-kit-layer=""
|
|
209
211
|
role="dialog"
|
|
210
212
|
id={panelId}
|
|
211
213
|
// The caller's own `aria-label` wins over {@link PopoverLabels.panel}, which
|
package/src/i18n/defaults.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import { DEFAULT_WIZARD_LABELS } from "../wizard/wizard-labels";
|
|
15
15
|
import { DEFAULT_TOUR_LABELS } from "../tour/tour-labels";
|
|
16
16
|
import { DEFAULT_TOUR_CATALOGUE_LABELS } from "../tour/tour-catalogue-labels";
|
|
17
|
+
import { DEFAULT_TOUR_GUIDE_LABELS } from "../tour/tour-guide-labels";
|
|
17
18
|
import { DEFAULT_COMMAND_PALETTE_LABELS } from "../search/command-palette-labels";
|
|
18
19
|
import { DEFAULT_GLOBAL_SEARCH_LABELS } from "../search/global-search-labels";
|
|
19
20
|
import { DEFAULT_SERIES_CHART_LABELS } from "../components/series-chart-labels";
|
|
@@ -173,6 +174,7 @@ export const DEFAULT_UI_KIT_LABELS: UiKitLabels = {
|
|
|
173
174
|
wizard: DEFAULT_WIZARD_LABELS,
|
|
174
175
|
tour: DEFAULT_TOUR_LABELS,
|
|
175
176
|
tourCatalogue: DEFAULT_TOUR_CATALOGUE_LABELS,
|
|
177
|
+
tourGuide: DEFAULT_TOUR_GUIDE_LABELS,
|
|
176
178
|
commandPalette: DEFAULT_COMMAND_PALETTE_LABELS,
|
|
177
179
|
globalSearch: DEFAULT_GLOBAL_SEARCH_LABELS,
|
|
178
180
|
seriesChart: DEFAULT_SERIES_CHART_LABELS,
|
package/src/i18n/german.ts
CHANGED
|
@@ -634,6 +634,19 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
634
634
|
awaitClickHint: "Klicken Sie auf das hervorgehobene Element, um fortzufahren",
|
|
635
635
|
awaitInputHint: "Geben Sie im hervorgehobenen Feld einen Wert ein, um fortzufahren", // 0.46: TourStep.awaitInput (keksdose live #436).
|
|
636
636
|
step: (current, total) => `${n(current)} / ${n(total)}`,
|
|
637
|
+
// 0.50: guided form tours (docs/tours-harmonization.md §11).
|
|
638
|
+
leaveEmpty: "Leer lassen",
|
|
639
|
+
optional: "Optional",
|
|
640
|
+
gateHint: "Füllen Sie das hervorgehobene Feld aus, um fortzufahren",
|
|
641
|
+
gateReady: "Bereit. Drücken Sie Weiter oder die Eingabetaste, um fortzufahren.",
|
|
642
|
+
awaitGateHint: "Speichern Sie, um fortzufahren",
|
|
643
|
+
awaitGateRefused: "Das ist noch nicht gespeichert. Prüfen Sie das Formular und versuchen Sie es erneut.",
|
|
644
|
+
backToStep: (title) => `Zurück zu „${title}“`,
|
|
645
|
+
},
|
|
646
|
+
// 0.50: the form's "Guide me" (TourGuideButton, docs/tours-harmonization.md §11).
|
|
647
|
+
tourGuide: {
|
|
648
|
+
label: "Schritt für Schritt",
|
|
649
|
+
description: "Führt Sie Feld für Feld durch dieses Formular",
|
|
637
650
|
},
|
|
638
651
|
// 0.43.0 — docs/tours-harmonization.md §3, §8: the tours page. keksdose's reviewed words.
|
|
639
652
|
tourCatalogue: {
|
package/src/i18n/kit-labels.tsx
CHANGED
|
@@ -26,6 +26,7 @@ import type { TabsLabels } from "../components/tabs";
|
|
|
26
26
|
import type { WizardLabels } from "../wizard/types";
|
|
27
27
|
import type { TourLabels } from "../tour/tour";
|
|
28
28
|
import type { TourCatalogueLabels } from "../tour/tour-catalogue-labels";
|
|
29
|
+
import type { TourGuideLabels } from "../tour/tour-guide-labels";
|
|
29
30
|
import type { CommandPaletteLabels } from "../search/command-palette";
|
|
30
31
|
import type { GlobalSearchLabels } from "../search/global-search";
|
|
31
32
|
import type { MonthPickerLabels } from "../components/month-picker";
|
|
@@ -335,6 +336,8 @@ export interface UiKitLabels {
|
|
|
335
336
|
tour: TourLabels;
|
|
336
337
|
/** 0.43.0: the tours page — `TourCatalogue` and its card (docs/tours-harmonization.md §3). */
|
|
337
338
|
tourCatalogue: TourCatalogueLabels;
|
|
339
|
+
/** 0.50: the form's "Guide me" — `TourGuideButton` (docs/tours-harmonization.md §11). */
|
|
340
|
+
tourGuide: TourGuideLabels;
|
|
338
341
|
commandPalette: CommandPaletteLabels;
|
|
339
342
|
globalSearch: GlobalSearchLabels;
|
|
340
343
|
seriesChart: SeriesChartLabels;
|
package/src/i18n/locales/es.ts
CHANGED
|
@@ -617,6 +617,19 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
|
|
|
617
617
|
awaitClickHint: "Haga clic en el elemento resaltado para continuar",
|
|
618
618
|
awaitInputHint: "Introduzca un valor en el campo resaltado para continuar", // 0.46: TourStep.awaitInput (keksdose live #436).
|
|
619
619
|
step: (current, total) => `${n(current)} / ${n(total)}`,
|
|
620
|
+
// 0.50: guided form tours (docs/tours-harmonization.md §11).
|
|
621
|
+
leaveEmpty: "Dejar vacío",
|
|
622
|
+
optional: "Opcional",
|
|
623
|
+
gateHint: "Rellene el campo resaltado para continuar",
|
|
624
|
+
gateReady: "Listo. Pulse Siguiente o Intro para continuar.",
|
|
625
|
+
awaitGateHint: "Guarde para continuar",
|
|
626
|
+
awaitGateRefused: "Todavía no se ha guardado. Revise el formulario e inténtelo de nuevo.",
|
|
627
|
+
backToStep: (title) => `Volver a «${title}»`,
|
|
628
|
+
},
|
|
629
|
+
// 0.50: the form's "Guide me" (TourGuideButton, docs/tours-harmonization.md §11).
|
|
630
|
+
tourGuide: {
|
|
631
|
+
label: "Guíeme",
|
|
632
|
+
description: "Le guía por este formulario, campo a campo",
|
|
620
633
|
},
|
|
621
634
|
// 0.43.0 — docs/tours-harmonization.md §3, §8: the tours page.
|
|
622
635
|
tourCatalogue: {
|