@eifi1/ui-kit 0.22.0 → 0.24.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 +8 -7
- package/dist/components/amount-input.d.ts +34 -7
- package/dist/components/amount-input.js +14 -10
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/autocomplete.d.ts +26 -0
- package/dist/components/autocomplete.js +164 -137
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/button-group.d.ts +8 -7
- package/dist/components/calculator.d.ts +8 -7
- package/dist/components/column-mapper.d.ts +235 -0
- package/dist/components/column-mapper.js +411 -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 +113 -3
- package/dist/components/combobox.js +456 -313
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/confirm-dialog.d.ts +8 -7
- package/dist/components/confirm-dialog.js +7 -8
- package/dist/components/confirm-dialog.js.map +1 -1
- package/dist/components/copy-button.d.ts +5 -4
- package/dist/components/country-select.d.ts +50 -7
- package/dist/components/country-select.js +65 -9
- package/dist/components/country-select.js.map +1 -1
- package/dist/components/danger-confirm.d.ts +8 -7
- package/dist/components/danger-confirm.js +64 -7
- package/dist/components/danger-confirm.js.map +1 -1
- package/dist/components/date-picker.d.ts +41 -0
- package/dist/components/date-picker.js +269 -8
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/entity-combobox.d.ts +48 -2
- package/dist/components/entity-combobox.js +159 -114
- package/dist/components/entity-combobox.js.map +1 -1
- package/dist/components/facing-pair.d.ts +8 -7
- package/dist/components/field-parts.d.ts +116 -6
- package/dist/components/field-parts.js +84 -0
- package/dist/components/field-parts.js.map +1 -1
- 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 +8 -7
- package/dist/components/file-dropzone.d.ts +8 -7
- package/dist/components/form-actions.d.ts +8 -7
- package/dist/components/form-actions.js +23 -3
- package/dist/components/form-actions.js.map +1 -1
- package/dist/components/iban-input.d.ts +8 -7
- package/dist/components/icon-picker.d.ts +10 -1
- package/dist/components/icon-picker.js +12 -4
- package/dist/components/icon-picker.js.map +1 -1
- package/dist/components/language-select.d.ts +8 -7
- package/dist/components/money-field.d.ts +11 -7
- package/dist/components/money-field.js.map +1 -1
- package/dist/components/month-picker.js +2 -1
- package/dist/components/month-picker.js.map +1 -1
- package/dist/components/multi-entity-combobox.d.ts +31 -2
- package/dist/components/multi-entity-combobox.js +152 -103
- package/dist/components/multi-entity-combobox.js.map +1 -1
- package/dist/components/number-field.d.ts +8 -7
- package/dist/components/number-input.d.ts +8 -7
- package/dist/components/numpad-sheet.d.ts +8 -7
- package/dist/components/phone-input.d.ts +8 -7
- 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 +8 -7
- package/dist/components/settings-fields.d.ts +8 -7
- package/dist/components/share-card.d.ts +8 -7
- package/dist/components/swatch-picker.d.ts +17 -1
- package/dist/components/swatch-picker.js +10 -4
- package/dist/components/swatch-picker.js.map +1 -1
- package/dist/components/text-link.d.ts +8 -7
- package/dist/components/tile-radio.d.ts +20 -0
- package/dist/components/tile-radio.js +25 -8
- package/dist/components/tile-radio.js.map +1 -1
- package/dist/components/time-input.d.ts +8 -7
- package/dist/components/toggle-group.js +2 -2
- 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 +5 -4
- package/dist/components/ui.js +60 -28
- package/dist/components/ui.js.map +1 -1
- package/dist/feedback/feedback-attachment.d.ts +66 -1
- package/dist/feedback/feedback-attachment.js +185 -65
- package/dist/feedback/feedback-attachment.js.map +1 -1
- package/dist/feedback/feedback-dialog.d.ts +66 -1
- package/dist/feedback/feedback-dialog.js.map +1 -1
- package/dist/feedback/feedback-inbox.d.ts +67 -252
- package/dist/feedback/feedback-inbox.js.map +1 -1
- package/dist/feedback/feedback-thread.d.ts +66 -287
- package/dist/feedback/feedback-thread.js +16 -3
- package/dist/feedback/feedback-thread.js.map +1 -1
- package/dist/{kit-labels-R8mIAc9N.d.ts → feedback-DOwPu-Il.d.ts} +1273 -30
- package/dist/feedback.d.ts +65 -3
- package/dist/hooks/use-file-drop.d.ts +8 -7
- package/dist/i18n/defaults.d.ts +8 -7
- package/dist/i18n/defaults.js +3 -1
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.d.ts +8 -7
- package/dist/i18n/german.js +36 -2
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +5 -4
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/languages.d.ts +8 -7
- package/dist/i18n/locales/de-CH.d.ts +8 -7
- package/dist/i18n/locales/en.d.ts +8 -7
- package/dist/i18n/locales/en.js +7 -0
- package/dist/i18n/locales/en.js.map +1 -1
- package/dist/i18n/locales/es.d.ts +8 -7
- package/dist/i18n/locales/es.js +37 -2
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +8 -7
- package/dist/i18n/locales/fr.js +36 -2
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +8 -7
- package/dist/i18n/locales/hu.js +38 -2
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +8 -7
- package/dist/i18n/locales/it.js +37 -2
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +8 -7
- package/dist/i18n/locales/zh.js +34 -2
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.d.ts +8 -7
- package/dist/i18n/review.js +20 -1
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.js +16 -0
- 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/strip-fade.d.ts +2 -2
- package/dist/lib/strip-fade.js.map +1 -1
- 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 +142 -13
- package/dist/rhf/fields.js +185 -3
- package/dist/rhf/fields.js.map +1 -1
- package/dist/rhf/form.d.ts +8 -7
- package/dist/rhf.d.ts +9 -8
- package/dist/rhf.js.map +1 -1
- package/dist/shell/app-shell.d.ts +8 -7
- package/dist/shell/top-bar-brand.d.ts +8 -7
- 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 +8 -7
- package/dist/table-text.d.ts +1 -1
- package/dist/wizard/stepper-nav.d.ts +38 -9
- package/dist/wizard/stepper-nav.js +4 -0
- package/dist/wizard/stepper-nav.js.map +1 -1
- package/dist/wizard.d.ts +8 -7
- package/package.json +1 -1
- package/src/components/amount-input.tsx +50 -14
- package/src/components/autocomplete.tsx +188 -116
- package/src/components/column-mapper.tsx +768 -0
- package/src/components/combobox-core.tsx +82 -9
- package/src/components/combobox.tsx +655 -335
- package/src/components/confirm-dialog.tsx +12 -8
- package/src/components/country-select.tsx +135 -22
- package/src/components/danger-confirm.tsx +274 -24
- package/src/components/date-picker.tsx +423 -10
- package/src/components/entity-combobox.tsx +243 -129
- package/src/components/field-parts.tsx +224 -5
- package/src/components/field-strip.tsx +149 -0
- package/src/components/form-actions.tsx +52 -5
- package/src/components/icon-picker.tsx +23 -4
- package/src/components/money-field.tsx +3 -0
- package/src/components/month-picker.tsx +2 -1
- package/src/components/multi-entity-combobox.tsx +221 -121
- package/src/components/reauth-dialog.tsx +17 -18
- package/src/components/swatch-picker.tsx +29 -5
- package/src/components/tile-radio.tsx +74 -13
- package/src/components/toggle-group.tsx +2 -2
- package/src/components/trigger-aria.ts +5 -0
- package/src/components/ui.tsx +114 -33
- package/src/feedback/feedback-attachment.tsx +372 -72
- package/src/feedback/feedback-dialog.tsx +7 -3
- package/src/feedback/feedback-inbox.tsx +3 -2
- package/src/feedback/feedback-thread.tsx +112 -9
- package/src/i18n/defaults.ts +2 -0
- package/src/i18n/german.ts +42 -0
- package/src/i18n/kit-labels.tsx +4 -0
- package/src/i18n/locales/en.ts +27 -5
- package/src/i18n/locales/es.ts +41 -0
- package/src/i18n/locales/fr.ts +42 -0
- package/src/i18n/locales/hu.ts +37 -0
- package/src/i18n/locales/it.ts +41 -0
- package/src/i18n/locales/zh.ts +32 -0
- package/src/i18n/review.ts +19 -0
- package/src/index.ts +18 -0
- package/src/lib/column-mapping.ts +234 -0
- package/src/lib/strip-fade.ts +2 -2
- package/src/lib/table-text.ts +271 -0
- package/src/rhf/fields.tsx +421 -6
- package/src/rhf.ts +2 -1
- package/src/shell/topbar-action-menu.tsx +134 -38
- package/src/wizard/stepper-nav.tsx +35 -1
- package/dist/components/field-anatomy.d.ts +0 -95
- package/dist/components/field-anatomy.js +0 -84
- package/dist/components/field-anatomy.js.map +0 -1
- package/dist/feedback-attachment-NhUm7Zga.d.ts +0 -367
- package/src/components/field-anatomy.tsx +0 -190
package/src/rhf/fields.tsx
CHANGED
|
@@ -9,10 +9,13 @@
|
|
|
9
9
|
* <RhfDateField name="start" label="Start" />
|
|
10
10
|
* <RhfSelect name="type" label="Type" options={TYPES} />
|
|
11
11
|
* <RhfCombobox name="tenantId" label="Tenant" options={tenants} clearable />
|
|
12
|
+
* <RhfInlineEntityCombobox name="accountId" label="Account" options={accounts} />
|
|
12
13
|
* <RhfCheckbox name="isDefault" label="Default account" />
|
|
13
14
|
* <RhfTimeInput name="meetingTime" label="Time" />
|
|
14
15
|
* <RhfDateRangePicker fromName="periodFrom" toName="periodTo" label="Period" />
|
|
15
16
|
* <RhfToggleGroup name="interval" label="Interval" options={INTERVALS} />
|
|
17
|
+
* <RhfCountrySelect name="country" label="Country" preferred={["CH", "DE"]} />
|
|
18
|
+
* <RhfMonthPicker name="fiscalYear" label="Fiscal year" mode="year" valueAsNumber />
|
|
16
19
|
* </Form>
|
|
17
20
|
* ```
|
|
18
21
|
*
|
|
@@ -66,6 +69,8 @@ import {
|
|
|
66
69
|
import { TimeInput, type TimeInputProps } from "../components/time-input";
|
|
67
70
|
import { IbanInput, type IbanInputProps } from "../components/iban-input";
|
|
68
71
|
import { PhoneInput, type PhoneInputProps } from "../components/phone-input";
|
|
72
|
+
import { CountrySelect, type CountrySelectProps } from "../components/country-select";
|
|
73
|
+
import { MonthPicker, type MonthPickerProps } from "../components/month-picker";
|
|
69
74
|
import {
|
|
70
75
|
ToggleGroup,
|
|
71
76
|
type ToggleGroupBaseProps,
|
|
@@ -74,7 +79,12 @@ import {
|
|
|
74
79
|
} from "../components/toggle-group";
|
|
75
80
|
import { Checkbox, type CheckboxProps } from "../components/checkbox";
|
|
76
81
|
import { EntityCombobox, type EntityComboboxProps } from "../components/entity-combobox";
|
|
77
|
-
import {
|
|
82
|
+
import {
|
|
83
|
+
Combobox,
|
|
84
|
+
InlineEntityCombobox,
|
|
85
|
+
type ComboboxProps,
|
|
86
|
+
type InlineEntityComboboxProps,
|
|
87
|
+
} from "../components/combobox";
|
|
78
88
|
import type { ComboClearValue } from "../components/combobox-core";
|
|
79
89
|
import { cn } from "../lib/cn";
|
|
80
90
|
|
|
@@ -571,6 +581,10 @@ export type RhfMoneyFieldProps<
|
|
|
571
581
|
* with `rules` as well if a clamp needs explaining. */
|
|
572
582
|
min?: number;
|
|
573
583
|
max?: number;
|
|
584
|
+
/** Classes for the `<input>` — `className` is the item's box. {@link RhfTextField},
|
|
585
|
+
* {@link RhfTextarea} and {@link RhfNumberField} take it; since 0.23 AmountInput
|
|
586
|
+
* does too (keksdose G7: `text-end` for a money column). */
|
|
587
|
+
inputClassName?: string;
|
|
574
588
|
};
|
|
575
589
|
|
|
576
590
|
/** The amount a text reads as, or `undefined` for a draft that is not one yet. */
|
|
@@ -1197,6 +1211,252 @@ export function RhfPhoneInput<
|
|
|
1197
1211
|
);
|
|
1198
1212
|
}
|
|
1199
1213
|
|
|
1214
|
+
// ── country and month (0.23) ─────────────────────────────────────────────────
|
|
1215
|
+
|
|
1216
|
+
type OwnCountrySelectProps = Omit<
|
|
1217
|
+
CountrySelectProps<boolean>,
|
|
1218
|
+
| "value"
|
|
1219
|
+
| "onChange"
|
|
1220
|
+
| "onBlur"
|
|
1221
|
+
| "label"
|
|
1222
|
+
| "hint"
|
|
1223
|
+
| "error"
|
|
1224
|
+
| "invalid"
|
|
1225
|
+
| "disabled"
|
|
1226
|
+
| "className"
|
|
1227
|
+
| "id"
|
|
1228
|
+
| "ref"
|
|
1229
|
+
| "clearable"
|
|
1230
|
+
>;
|
|
1231
|
+
|
|
1232
|
+
export type RhfCountrySelectProps<
|
|
1233
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
1234
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
1235
|
+
TTransformed = TFieldValues,
|
|
1236
|
+
> = RhfFieldBaseProps<TFieldValues, TName, TTransformed> &
|
|
1237
|
+
OwnCountrySelectProps & {
|
|
1238
|
+
/** Offer the picker's clear "×" (and Delete on the trigger) while a country is
|
|
1239
|
+
* chosen. A clear stores `clearValue`. */
|
|
1240
|
+
clearable?: boolean;
|
|
1241
|
+
/** What a clear stores. Default `null`; `""` for a schema that spells "no
|
|
1242
|
+
* country" as an empty string (a zod `z.string().max(2)`, kastlan's). The picker
|
|
1243
|
+
* reads either back as empty. */
|
|
1244
|
+
clearValue?: ComboClearValue;
|
|
1245
|
+
/** Classes for the picker — `className` is the item's box. */
|
|
1246
|
+
selectClassName?: string;
|
|
1247
|
+
};
|
|
1248
|
+
|
|
1249
|
+
/**
|
|
1250
|
+
* {@link CountrySelect} bound to one field, which stores the ISO 3166-1 alpha-2 code
|
|
1251
|
+
* ("CH", always upper-case), or `clearValue` once cleared (`clearable`). kastlan asked
|
|
1252
|
+
* for it (0.22 adoption): its address form wired a `CountrySelect` through
|
|
1253
|
+
* {@link RhfField}'s render, and with no ref on the picker react-hook-form's
|
|
1254
|
+
* focus-on-error had nothing to focus — the one field on the form a failed submit
|
|
1255
|
+
* could not take you to.
|
|
1256
|
+
*
|
|
1257
|
+
* Bound the way {@link RhfIbanInput} and {@link RhfPhoneInput} are: `field.ref` reaches
|
|
1258
|
+
* the trigger (the picker's `ref`, 0.23), the form's error paints the trigger and is
|
|
1259
|
+
* the shell's message under it, the hint is the shell's description — so neither is
|
|
1260
|
+
* ever handed to the picker, and its box never changes when one comes or goes. The
|
|
1261
|
+
* form's label names the trigger together with the chosen country, and is the phone
|
|
1262
|
+
* sheet's title. `commit` and `disabledReason` are the picker's own: a country that
|
|
1263
|
+
* saves itself stays focusable under a lock and says why.
|
|
1264
|
+
*
|
|
1265
|
+
* ```tsx
|
|
1266
|
+
* <RhfCountrySelect name="country" label="Country" required
|
|
1267
|
+
* preferred={["CH", "LI", "DE", "AT", "FR", "IT"]} rules={{ required: "Choose a country" }} />
|
|
1268
|
+
* ```
|
|
1269
|
+
*/
|
|
1270
|
+
export function RhfCountrySelect<
|
|
1271
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
1272
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
1273
|
+
TTransformed = TFieldValues,
|
|
1274
|
+
>({
|
|
1275
|
+
name,
|
|
1276
|
+
control,
|
|
1277
|
+
rules,
|
|
1278
|
+
label,
|
|
1279
|
+
hint,
|
|
1280
|
+
required,
|
|
1281
|
+
disabled,
|
|
1282
|
+
excludeWhenDisabled,
|
|
1283
|
+
className,
|
|
1284
|
+
clearable,
|
|
1285
|
+
clearValue = null,
|
|
1286
|
+
selectClassName,
|
|
1287
|
+
labels,
|
|
1288
|
+
...countryProps
|
|
1289
|
+
}: RhfCountrySelectProps<TFieldValues, TName, TTransformed>) {
|
|
1290
|
+
const labelled = hasContent(label);
|
|
1291
|
+
// A string label is also what the field IS — the picker's `country` word, which
|
|
1292
|
+
// titles the phone sheet. A caller's own `labels.country` still wins.
|
|
1293
|
+
const ownLabels = typeof label === "string" ? { country: label, ...labels } : labels;
|
|
1294
|
+
return (
|
|
1295
|
+
<RhfField
|
|
1296
|
+
{...{ name, control, rules, label, hint, required, disabled, excludeWhenDisabled, className }}
|
|
1297
|
+
render={({ field, invalid, labelId }) => {
|
|
1298
|
+
const value: unknown = field.value;
|
|
1299
|
+
return (
|
|
1300
|
+
// FormControl clones this element with `id` / `aria-describedby` /
|
|
1301
|
+
// `aria-invalid`, which the picker routes to its trigger.
|
|
1302
|
+
<CountrySelect<boolean>
|
|
1303
|
+
aria-required={required || undefined}
|
|
1304
|
+
// The form's label, then the country (see the picker's `aria-labelledby`).
|
|
1305
|
+
aria-labelledby={labelled ? labelId : undefined}
|
|
1306
|
+
{...countryProps}
|
|
1307
|
+
labels={ownLabels}
|
|
1308
|
+
className={selectClassName}
|
|
1309
|
+
ref={field.ref}
|
|
1310
|
+
value={typeof value === "string" ? value : null}
|
|
1311
|
+
clearable={clearable}
|
|
1312
|
+
onChange={(code) => field.onChange(code ?? clearValue)}
|
|
1313
|
+
// Focus leaving the picker (the wrapper's focusout) marks the field touched.
|
|
1314
|
+
onBlur={field.onBlur}
|
|
1315
|
+
disabled={field.disabled}
|
|
1316
|
+
invalid={invalid}
|
|
1317
|
+
/>
|
|
1318
|
+
);
|
|
1319
|
+
}}
|
|
1320
|
+
/>
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
type OwnMonthPickerProps = Omit<
|
|
1325
|
+
MonthPickerProps,
|
|
1326
|
+
"value" | "onChange" | "onBlur" | "label" | "hint" | "error" | "invalid" | "disabled" | "className" | "id" | "mode"
|
|
1327
|
+
>;
|
|
1328
|
+
|
|
1329
|
+
/** `mode` picks what the form stores, and `valueAsNumber` is only offered for a year. */
|
|
1330
|
+
type RhfMonthPickerMode =
|
|
1331
|
+
| {
|
|
1332
|
+
/** A month, stored as `"YYYY-MM"` (the default). */
|
|
1333
|
+
mode?: "month";
|
|
1334
|
+
valueAsNumber?: undefined;
|
|
1335
|
+
}
|
|
1336
|
+
| {
|
|
1337
|
+
/** A year, stored as `"YYYY"` — or as a number with `valueAsNumber`. */
|
|
1338
|
+
mode: "year";
|
|
1339
|
+
/** Store the year as a number (`2026`) rather than `"2026"` — kastlan's budget
|
|
1340
|
+
* `fiscal_year` is an integer column. A number in the form reads back as its
|
|
1341
|
+
* year. */
|
|
1342
|
+
valueAsNumber?: boolean;
|
|
1343
|
+
};
|
|
1344
|
+
|
|
1345
|
+
export type RhfMonthPickerProps<
|
|
1346
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
1347
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
1348
|
+
TTransformed = TFieldValues,
|
|
1349
|
+
> = RhfFieldBaseProps<TFieldValues, TName, TTransformed> &
|
|
1350
|
+
OwnMonthPickerProps & {
|
|
1351
|
+
/** Classes for the picker — `className` is the item's box. As {@link RhfDateField}'s. */
|
|
1352
|
+
inputClassName?: string;
|
|
1353
|
+
} & RhfMonthPickerMode;
|
|
1354
|
+
|
|
1355
|
+
function MonthControl({
|
|
1356
|
+
field,
|
|
1357
|
+
invalid,
|
|
1358
|
+
id,
|
|
1359
|
+
valueAsNumber,
|
|
1360
|
+
mode,
|
|
1361
|
+
inputClassName,
|
|
1362
|
+
pickerProps,
|
|
1363
|
+
required,
|
|
1364
|
+
...aria
|
|
1365
|
+
}: {
|
|
1366
|
+
field: ControllerRenderProps;
|
|
1367
|
+
invalid: boolean;
|
|
1368
|
+
id: string;
|
|
1369
|
+
valueAsNumber?: boolean;
|
|
1370
|
+
mode?: "month" | "year";
|
|
1371
|
+
inputClassName?: string;
|
|
1372
|
+
pickerProps: OwnMonthPickerProps;
|
|
1373
|
+
required?: boolean;
|
|
1374
|
+
"aria-describedby"?: string;
|
|
1375
|
+
"aria-invalid"?: MonthPickerProps["aria-invalid"];
|
|
1376
|
+
}) {
|
|
1377
|
+
// MonthPicker forwards no ref; it routes `id` to its trigger, where the handle finds
|
|
1378
|
+
// it — RhfDateField's way.
|
|
1379
|
+
useFocusHandle(field.ref, () => document.getElementById(id));
|
|
1380
|
+
const value: unknown = field.value;
|
|
1381
|
+
return (
|
|
1382
|
+
<MonthPicker
|
|
1383
|
+
{...pickerProps}
|
|
1384
|
+
{...aria}
|
|
1385
|
+
// MonthPicker routes `aria-required` to its trigger since 0.23.0.
|
|
1386
|
+
aria-required={required || undefined}
|
|
1387
|
+
id={id}
|
|
1388
|
+
mode={mode}
|
|
1389
|
+
value={value === null || value === undefined ? "" : String(value)}
|
|
1390
|
+
onChange={(key) => {
|
|
1391
|
+
field.onChange(mode === "year" && valueAsNumber ? Number(key) : key);
|
|
1392
|
+
// A pick is the whole interaction — there is no text to leave — so it touches.
|
|
1393
|
+
field.onBlur();
|
|
1394
|
+
}}
|
|
1395
|
+
disabled={field.disabled}
|
|
1396
|
+
invalid={invalid}
|
|
1397
|
+
className={inputClassName}
|
|
1398
|
+
/>
|
|
1399
|
+
);
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
/**
|
|
1403
|
+
* {@link MonthPicker} bound to one field: a month as `"YYYY-MM"`, or with `mode="year"` a
|
|
1404
|
+
* year as `"YYYY"` (`valueAsNumber` stores `2026`). kastlan asked for it with
|
|
1405
|
+
* {@link RhfCountrySelect}: its budget wizard's fiscal year was a `MonthPicker
|
|
1406
|
+
* mode="year"` inside {@link RhfField}'s render — the `String()` in, the `Number()`
|
|
1407
|
+
* out, `invalid` by hand — and a failed submit could not focus it, because the picker
|
|
1408
|
+
* forwards no ref.
|
|
1409
|
+
*
|
|
1410
|
+
* Bound as {@link RhfDateField} is: a focus handle finds the trigger by its id, so
|
|
1411
|
+
* focus-on-error lands there; the form's error paints the trigger and is the shell's
|
|
1412
|
+
* message; the hint is the shell's description; neither is handed to the picker, so
|
|
1413
|
+
* its box never changes. Picking marks the field touched.
|
|
1414
|
+
*
|
|
1415
|
+
* ```tsx
|
|
1416
|
+
* <RhfMonthPicker name="fiscalYear" label="Fiscal year" mode="year" valueAsNumber
|
|
1417
|
+
* min="2020" max="2029" rules={{ required: "Choose a year" }} />
|
|
1418
|
+
* ```
|
|
1419
|
+
*/
|
|
1420
|
+
export function RhfMonthPicker<
|
|
1421
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
1422
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
1423
|
+
TTransformed = TFieldValues,
|
|
1424
|
+
>({
|
|
1425
|
+
name,
|
|
1426
|
+
control,
|
|
1427
|
+
rules,
|
|
1428
|
+
label,
|
|
1429
|
+
hint,
|
|
1430
|
+
required,
|
|
1431
|
+
disabled,
|
|
1432
|
+
excludeWhenDisabled,
|
|
1433
|
+
className,
|
|
1434
|
+
inputClassName,
|
|
1435
|
+
mode,
|
|
1436
|
+
valueAsNumber,
|
|
1437
|
+
...pickerProps
|
|
1438
|
+
}: RhfMonthPickerProps<TFieldValues, TName, TTransformed>) {
|
|
1439
|
+
return (
|
|
1440
|
+
<RhfField
|
|
1441
|
+
{...{ name, control, rules, label, hint, required, disabled, excludeWhenDisabled, className }}
|
|
1442
|
+
render={({ field, invalid, id }) => (
|
|
1443
|
+
// FormControl clones this element with `aria-describedby` / `aria-invalid`,
|
|
1444
|
+
// which MonthControl hands on to the picker's trigger.
|
|
1445
|
+
<MonthControl
|
|
1446
|
+
field={field as unknown as ControllerRenderProps}
|
|
1447
|
+
invalid={invalid}
|
|
1448
|
+
id={id}
|
|
1449
|
+
mode={mode}
|
|
1450
|
+
valueAsNumber={valueAsNumber}
|
|
1451
|
+
inputClassName={inputClassName}
|
|
1452
|
+
pickerProps={pickerProps}
|
|
1453
|
+
required={required}
|
|
1454
|
+
/>
|
|
1455
|
+
)}
|
|
1456
|
+
/>
|
|
1457
|
+
);
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1200
1460
|
// ── select ───────────────────────────────────────────────────────────────────
|
|
1201
1461
|
|
|
1202
1462
|
export interface RhfSelectOption {
|
|
@@ -1515,11 +1775,17 @@ export type RhfComboboxProps<
|
|
|
1515
1775
|
| "emptyLabel"
|
|
1516
1776
|
| "clearable"
|
|
1517
1777
|
| "clearLabel"
|
|
1778
|
+
| "closeLabel"
|
|
1518
1779
|
| "onCreate"
|
|
1519
1780
|
| "createLabel"
|
|
1781
|
+
| "createEmptyLabel"
|
|
1782
|
+
| "createCommit"
|
|
1783
|
+
| "commit"
|
|
1784
|
+
| "disabledReason"
|
|
1520
1785
|
| "filter"
|
|
1521
1786
|
| "minChars"
|
|
1522
1787
|
| "debounceMs"
|
|
1788
|
+
| "aria-label"
|
|
1523
1789
|
> & {
|
|
1524
1790
|
/** What a clear stores — the picker's `clearValue`. Default `null`; `""` for a
|
|
1525
1791
|
* schema that spells "no choice" as an empty string. */
|
|
@@ -1537,6 +1803,8 @@ function EntityControl<V extends string | number>({
|
|
|
1537
1803
|
describedBy,
|
|
1538
1804
|
clearValue,
|
|
1539
1805
|
comboClassName,
|
|
1806
|
+
sheetTitle,
|
|
1807
|
+
focusRef,
|
|
1540
1808
|
comboProps,
|
|
1541
1809
|
}: {
|
|
1542
1810
|
field: ControllerRenderProps;
|
|
@@ -1547,12 +1815,15 @@ function EntityControl<V extends string | number>({
|
|
|
1547
1815
|
describedBy: string | undefined;
|
|
1548
1816
|
clearValue: ComboClearValue;
|
|
1549
1817
|
comboClassName?: string;
|
|
1818
|
+
sheetTitle: ReactNode;
|
|
1819
|
+
/** `field.ref`, handed over on its own so the compiler reads `field` as data. */
|
|
1820
|
+
focusRef: ControllerRenderProps["ref"];
|
|
1550
1821
|
comboProps: Omit<RhfComboboxProps<FieldValues, string, V>, keyof RhfFieldBaseProps | "clearValue" | "comboClassName">;
|
|
1551
1822
|
}) {
|
|
1552
1823
|
const box = useRef<HTMLDivElement>(null);
|
|
1553
1824
|
// EntityCombobox puts `id` and the ARIA below on its trigger, so the label's
|
|
1554
|
-
// `htmlFor` names it
|
|
1555
|
-
|
|
1825
|
+
// `htmlFor` names it and the hint describes it; `field.ref` reaches the trigger
|
|
1826
|
+
// through the picker's own `ref` (0.24), as RhfCountrySelect's does.
|
|
1556
1827
|
// EntityCombobox reports no blur: focus leaving the picker marks the field touched.
|
|
1557
1828
|
const onBlur = field.onBlur;
|
|
1558
1829
|
useLayoutEffect(() => {
|
|
@@ -1565,7 +1836,11 @@ function EntityControl<V extends string | number>({
|
|
|
1565
1836
|
return (
|
|
1566
1837
|
<div ref={box}>
|
|
1567
1838
|
<EntityCombobox<V, ComboClearValue>
|
|
1839
|
+
// The form's label titles the phone sheet: the picker is not handed `label`,
|
|
1840
|
+
// which the FormLabel above already draws.
|
|
1841
|
+
sheetTitle={sheetTitle}
|
|
1568
1842
|
{...comboProps}
|
|
1843
|
+
ref={focusRef}
|
|
1569
1844
|
className={comboClassName}
|
|
1570
1845
|
id={id}
|
|
1571
1846
|
aria-describedby={describedBy}
|
|
@@ -1587,6 +1862,14 @@ function EntityControl<V extends string | number>({
|
|
|
1587
1862
|
* The kit's {@link EntityCombobox}: an id-keyed pick from `options` or `loadOptions`.
|
|
1588
1863
|
* Stores the option's `value`, or `clearValue` on a clear (only offered with
|
|
1589
1864
|
* `clearable`).
|
|
1865
|
+
*
|
|
1866
|
+
* 0.24: `field.ref` goes through the picker's own `ref` to its trigger (it used to be
|
|
1867
|
+
* a focus handle that found the trigger by id — the same element, now by the route
|
|
1868
|
+
* every other binding takes); the write lock (`commit`, `disabledReason`), the create
|
|
1869
|
+
* row's `createEmptyLabel` / `createCommit`, `closeLabel` and `aria-label` pass
|
|
1870
|
+
* through; and the form's label titles the phone sheet, which fell back to the
|
|
1871
|
+
* placeholder. For the INLINE picker — a text input, the account field typed into —
|
|
1872
|
+
* see {@link RhfInlineEntityCombobox}.
|
|
1590
1873
|
*/
|
|
1591
1874
|
export function RhfCombobox<
|
|
1592
1875
|
TFieldValues extends FieldValues = FieldValues,
|
|
@@ -1622,6 +1905,8 @@ export function RhfCombobox<
|
|
|
1622
1905
|
describedBy={describedBy}
|
|
1623
1906
|
clearValue={clearValue}
|
|
1624
1907
|
comboClassName={comboClassName}
|
|
1908
|
+
sheetTitle={hasContent(label) ? label : undefined}
|
|
1909
|
+
focusRef={field.ref}
|
|
1625
1910
|
comboProps={comboProps}
|
|
1626
1911
|
/>
|
|
1627
1912
|
)}
|
|
@@ -1629,6 +1914,123 @@ export function RhfCombobox<
|
|
|
1629
1914
|
);
|
|
1630
1915
|
}
|
|
1631
1916
|
|
|
1917
|
+
type OwnInlineEntityComboboxProps<V extends string | number> = Omit<
|
|
1918
|
+
InlineEntityComboboxProps<V, ComboClearValue>,
|
|
1919
|
+
| "value"
|
|
1920
|
+
| "defaultValue"
|
|
1921
|
+
| "onChange"
|
|
1922
|
+
| "onBlur"
|
|
1923
|
+
| "label"
|
|
1924
|
+
| "hint"
|
|
1925
|
+
| "error"
|
|
1926
|
+
| "invalid"
|
|
1927
|
+
| "disabled"
|
|
1928
|
+
| "className"
|
|
1929
|
+
| "id"
|
|
1930
|
+
| "ref"
|
|
1931
|
+
| "clearValue"
|
|
1932
|
+
>;
|
|
1933
|
+
|
|
1934
|
+
export type RhfInlineEntityComboboxProps<
|
|
1935
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
1936
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
1937
|
+
V extends string | number = string | number,
|
|
1938
|
+
TTransformed = TFieldValues,
|
|
1939
|
+
> = RhfFieldBaseProps<TFieldValues, TName, TTransformed> &
|
|
1940
|
+
OwnInlineEntityComboboxProps<V> & {
|
|
1941
|
+
/** What a clear stores — emptying the text, or the "×" with `clearable`. Default
|
|
1942
|
+
* `null`; `""` for a schema that spells "no choice" as an empty string. The
|
|
1943
|
+
* picker reads either back as empty. */
|
|
1944
|
+
clearValue?: ComboClearValue;
|
|
1945
|
+
/** Classes for the picker — `className` is the item's box. As {@link RhfCombobox}'s. */
|
|
1946
|
+
comboClassName?: string;
|
|
1947
|
+
};
|
|
1948
|
+
|
|
1949
|
+
/**
|
|
1950
|
+
* {@link InlineEntityCombobox} bound to one field, which stores the chosen option's
|
|
1951
|
+
* `value` (an id), or `clearValue` once the text is emptied or the "×" pressed.
|
|
1952
|
+
*
|
|
1953
|
+
* kastlan asked for it (0.24): its account picker IS an `InlineEntityCombobox` — the
|
|
1954
|
+
* account number is typed straight into the field — and the budget line-item form
|
|
1955
|
+
* wired it through {@link RhfField}'s render. The picker took no `ref`, so react-hook-
|
|
1956
|
+
* form's focus-on-error had nothing to focus: a line item saved without an account
|
|
1957
|
+
* failed its submit and left the caret where it was, the one field on the form a
|
|
1958
|
+
* failed submit could not take you to. (The same gap {@link RhfCountrySelect} closed
|
|
1959
|
+
* for the address form's country in 0.23.)
|
|
1960
|
+
*
|
|
1961
|
+
* Bound the way {@link RhfCountrySelect} is: `field.ref` reaches the `<input>` (the
|
|
1962
|
+
* picker's `ref`, 0.24), the form's error paints the input and is the shell's message
|
|
1963
|
+
* under it, and the hint is the shell's description — so neither is ever handed to the
|
|
1964
|
+
* picker, and its box never changes when one comes or goes. The form's label names
|
|
1965
|
+
* the input through `<label for>` and titles the phone sheet (`sheetTitle`); the picker
|
|
1966
|
+
* draws no floating label of its own. Focus leaving the picker marks the field
|
|
1967
|
+
* touched.
|
|
1968
|
+
*
|
|
1969
|
+
* Everything else is the picker's and passes through: `commit` and `disabledReason`
|
|
1970
|
+
* (a picker whose choice saves stays focusable under a lock and says why), `clearable`,
|
|
1971
|
+
* `autoFocus`, `aria-label` for an unlabelled cell, and the create row — `onCreate`,
|
|
1972
|
+
* `createLabel`, `createEmptyLabel`, `createCommit`. Making the record is the
|
|
1973
|
+
* caller's, and so is storing its id once it exists:
|
|
1974
|
+
*
|
|
1975
|
+
* ```tsx
|
|
1976
|
+
* <RhfInlineEntityCombobox<LineItem, "account_id", number>
|
|
1977
|
+
* name="account_id" label="Account" required options={accounts}
|
|
1978
|
+
* rules={{ required: "Choose an account" }}
|
|
1979
|
+
* onCreate={async (name) => form.setValue("account_id", (await createAccount(name)).id)} />
|
|
1980
|
+
* ```
|
|
1981
|
+
*
|
|
1982
|
+
* For the button-shaped picker with an async `loadOptions`, see {@link RhfCombobox}.
|
|
1983
|
+
*/
|
|
1984
|
+
export function RhfInlineEntityCombobox<
|
|
1985
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
1986
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
1987
|
+
V extends string | number = string | number,
|
|
1988
|
+
TTransformed = TFieldValues,
|
|
1989
|
+
>({
|
|
1990
|
+
name,
|
|
1991
|
+
control,
|
|
1992
|
+
rules,
|
|
1993
|
+
label,
|
|
1994
|
+
hint,
|
|
1995
|
+
required,
|
|
1996
|
+
disabled,
|
|
1997
|
+
excludeWhenDisabled,
|
|
1998
|
+
className,
|
|
1999
|
+
clearValue = null,
|
|
2000
|
+
comboClassName,
|
|
2001
|
+
...comboProps
|
|
2002
|
+
}: RhfInlineEntityComboboxProps<TFieldValues, TName, V, TTransformed>) {
|
|
2003
|
+
return (
|
|
2004
|
+
<RhfField
|
|
2005
|
+
{...{ name, control, rules, label, hint, required, disabled, excludeWhenDisabled, className }}
|
|
2006
|
+
render={({ field, invalid }) => {
|
|
2007
|
+
const value: unknown = field.value;
|
|
2008
|
+
return (
|
|
2009
|
+
// FormControl clones this element with `id` / `aria-describedby` /
|
|
2010
|
+
// `aria-invalid`, which the picker routes to its <input>.
|
|
2011
|
+
<InlineEntityCombobox<V, ComboClearValue>
|
|
2012
|
+
aria-required={required || undefined}
|
|
2013
|
+
// The form's label is the phone sheet's heading — the picker is not handed
|
|
2014
|
+
// `label`, which the FormLabel above already draws. A caller's own wins.
|
|
2015
|
+
sheetTitle={hasContent(label) ? label : undefined}
|
|
2016
|
+
{...comboProps}
|
|
2017
|
+
className={comboClassName}
|
|
2018
|
+
ref={field.ref}
|
|
2019
|
+
value={(value ?? null) as V | ComboClearValue | null}
|
|
2020
|
+
clearValue={clearValue}
|
|
2021
|
+
onChange={(v) => field.onChange(v)}
|
|
2022
|
+
// The wrapper's blur, which the <input>'s bubbles to after the picker has
|
|
2023
|
+
// judged any loose text — so the value is in before the field is touched.
|
|
2024
|
+
onBlur={field.onBlur}
|
|
2025
|
+
disabled={field.disabled}
|
|
2026
|
+
invalid={invalid}
|
|
2027
|
+
/>
|
|
2028
|
+
);
|
|
2029
|
+
}}
|
|
2030
|
+
/>
|
|
2031
|
+
);
|
|
2032
|
+
}
|
|
2033
|
+
|
|
1632
2034
|
export type RhfTextComboboxProps<
|
|
1633
2035
|
TFieldValues extends FieldValues = FieldValues,
|
|
1634
2036
|
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
@@ -1645,6 +2047,9 @@ export type RhfTextComboboxProps<
|
|
|
1645
2047
|
| "optionAdornment"
|
|
1646
2048
|
| "autoFocus"
|
|
1647
2049
|
| "aria-label"
|
|
2050
|
+
| "closeLabel"
|
|
2051
|
+
| "commit"
|
|
2052
|
+
| "disabledReason"
|
|
1648
2053
|
> & {
|
|
1649
2054
|
/** Classes for the combobox. */
|
|
1650
2055
|
comboClassName?: string;
|
|
@@ -1685,6 +2090,8 @@ export function RhfTextCombobox<
|
|
|
1685
2090
|
id={id}
|
|
1686
2091
|
describedBy={describedBy}
|
|
1687
2092
|
className={comboClassName}
|
|
2093
|
+
sheetTitle={hasContent(label) ? label : undefined}
|
|
2094
|
+
focusRef={field.ref}
|
|
1688
2095
|
comboProps={comboProps}
|
|
1689
2096
|
/>
|
|
1690
2097
|
)}
|
|
@@ -1700,6 +2107,8 @@ function TextComboControl({
|
|
|
1700
2107
|
id,
|
|
1701
2108
|
describedBy,
|
|
1702
2109
|
className,
|
|
2110
|
+
sheetTitle,
|
|
2111
|
+
focusRef,
|
|
1703
2112
|
comboProps,
|
|
1704
2113
|
}: {
|
|
1705
2114
|
field: ControllerRenderProps;
|
|
@@ -1709,16 +2118,22 @@ function TextComboControl({
|
|
|
1709
2118
|
id: string;
|
|
1710
2119
|
describedBy: string | undefined;
|
|
1711
2120
|
className?: string;
|
|
2121
|
+
sheetTitle: ReactNode;
|
|
2122
|
+
/** `field.ref` — see EntityControl. */
|
|
2123
|
+
focusRef: ControllerRenderProps["ref"];
|
|
1712
2124
|
comboProps: Omit<RhfTextComboboxProps, keyof RhfFieldBaseProps | "comboClassName">;
|
|
1713
2125
|
}) {
|
|
1714
2126
|
// The combobox routes `id` and its ARIA to its <input>, so the label's `htmlFor`
|
|
1715
|
-
// names it
|
|
1716
|
-
// own `error` prop, which it merges
|
|
1717
|
-
|
|
2127
|
+
// names it and the hint describes it; `field.ref` reaches the <input> through the
|
|
2128
|
+
// combobox's own `ref` (0.24). The error is its own `error` prop, which it merges
|
|
2129
|
+
// in after the hint.
|
|
1718
2130
|
const value: unknown = field.value;
|
|
1719
2131
|
return (
|
|
1720
2132
|
<Combobox
|
|
2133
|
+
// The form's label titles the phone sheet (see EntityControl).
|
|
2134
|
+
sheetTitle={sheetTitle}
|
|
1721
2135
|
{...comboProps}
|
|
2136
|
+
ref={focusRef}
|
|
1722
2137
|
id={id}
|
|
1723
2138
|
aria-describedby={describedBy}
|
|
1724
2139
|
aria-required={required || undefined}
|
package/src/rhf.ts
CHANGED
|
@@ -11,7 +11,8 @@ export * from "./rhf/form";
|
|
|
11
11
|
export * from "./rhf/use-rhf-wizard-step";
|
|
12
12
|
// Bound fields — one line per field (RhfTextField, RhfNumberField, RhfIntegerField, RhfMoneyField,
|
|
13
13
|
// RhfDateField, RhfTextarea, RhfSelect, RhfCheckbox, RhfCombobox, RhfTextCombobox; 0.22: RhfTimeInput,
|
|
14
|
-
// RhfDateRangePicker, RhfToggleGroup, RhfIbanInput, RhfPhoneInput
|
|
14
|
+
// RhfDateRangePicker, RhfToggleGroup, RhfIbanInput, RhfPhoneInput; 0.23: RhfCountrySelect,
|
|
15
|
+
// RhfMonthPicker; 0.24: RhfInlineEntityCombobox)
|
|
15
16
|
// and RhfField, the shell they are built on.
|
|
16
17
|
export * from "./rhf/fields";
|
|
17
18
|
// LineItems over useFieldArray.
|