@andreyfedkovich/cozy-ui 0.8.0 → 0.10.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/CHANGELOG.md +19 -0
- package/README.md +94 -10
- package/dist-lib/index.d.ts +218 -24
- package/dist-lib/styles.css +1 -1
- package/dist-lib/styles.modules.css +1 -1
- package/dist-lib/ui-library.cjs.js +12 -12
- package/dist-lib/ui-library.cjs.js.map +1 -1
- package/dist-lib/ui-library.es.js +4759 -4394
- package/dist-lib/ui-library.es.js.map +1 -1
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
3
3
|
Формат основан на [Keep a Changelog](https://keepachangelog.com/).
|
|
4
4
|
Версии соответствуют [Semantic Versioning](https://semver.org/) и git-тегам `v*`.
|
|
5
5
|
|
|
6
|
+
## 0.10.0 - 2026-06-04
|
|
7
|
+
|
|
8
|
+
- **feat:** New `ShowErrorPolicy` presets — `draftFriendly`, `wizardStep`, `savedInvalid`, `onBlurOrSubmit`. Recommended for ERP/wizard forms; legacy `default` unchanged (shows on `hasValue` alone).
|
|
9
|
+
- **feat:** Extended `FieldMeta` — `stepSubmitted`, `validationPending`, `errorKind`. `resolveDisplayError` suppresses stale `required` when value is non-empty.
|
|
10
|
+
- **feat:** `useFormFields` + `FieldBinding` — replaces per-app binding glue (~188 LOC in erp-hr). `markStepSubmitted`, `markFormSubmitted`, `resetInteraction`.
|
|
11
|
+
- **feat:** `useValidationRequest` — async validate with generation/stale guard and `validationPending`.
|
|
12
|
+
- **feat:** `attemptWizardStep`, `attemptFormSubmit` — validate-on-click for wizard and submit (no `disabled={!isValid}`).
|
|
13
|
+
- **feat:** `suppressError` prop on field components; dev warning when `error={null}` suppresses invalid `fieldMeta`.
|
|
14
|
+
- **feat:** Export `useFieldPresentation`, `FieldErrorCaption`, `FormField`, `hasFieldValue`.
|
|
15
|
+
- **docs:** `docs/validation-recipes.md` — recipes, anti-patterns, erp-hr migration (Задание B), consumer verification snippet.
|
|
16
|
+
|
|
17
|
+
## 0.9.0 - 2026-06-04
|
|
18
|
+
|
|
19
|
+
- **feat:** Единый контракт валидации полей — headless API: `FieldMeta`, `ShowErrorPolicy`, `resolveShowError`, `resolveFieldError`, `resolveFieldMessage`, `useFieldState`, `resolveValueChangeHandler`.
|
|
20
|
+
- **feat:** `Input`, `Textarea`, `Checkbox`, `Select`, `DialogSelect`, `TreeDialogSelect`, `Calendar` — пропсы `fieldMeta` и `showErrorPolicy`; явный `error` по-прежнему переопределяет meta. Дефолтная политика: `invalid && (touched || submitted || hasValue)`.
|
|
21
|
+
- **feat:** Общий A11y для полей — `aria-invalid`, `aria-describedby`, стабильные `id` через `useId()`, сообщение об ошибке с `role="alert"`.
|
|
22
|
+
- **feat:** Value picker’ы (`Select`, `DialogSelect`, `TreeDialogSelect`, `Calendar`) — канонический колбэк `onValueChange`; `onChange` оставлен как deprecated alias. На trigger добавлены `onBlur` / `onFocus` для интеграции с формами.
|
|
23
|
+
- **docs:** README — раздел Field validation, две семьи колбэков (native `onChange` vs picker `onValueChange`).
|
|
24
|
+
|
|
6
25
|
## 0.8.0 - 2026-06-02
|
|
7
26
|
|
|
8
27
|
- **feat:** Split published CSS into three entry points for Tailwind v3 host compatibility: `styles.css` (full bundle), `styles.modules.css` (SCSS modules only), and `styles.tailwind.css` (Tailwind v4 utilities). Existing `import "@andreyfedkovich/cozy-ui/styles.css"` is unchanged.
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ npm i @andreyfedkovich/cozy-ui
|
|
|
31
31
|
- [Design tokens](#design-tokens)
|
|
32
32
|
- [Component API](#component-api)
|
|
33
33
|
- [Layout & content](#layout--content) — `BaseBlock`, `Card`, `CollapsableBlock`, `Collapse`, `Carousel`, `EmptyComponent`, `Spinner`
|
|
34
|
-
- [Inputs & forms](#inputs--forms) — `Button`, `RadioGroupButton`, `Input`, `Textarea`, `Calendar`, `Checkbox`, `Select`, `DialogSelect`, `TreeDialogSelect`, `InputCaption`, `Label`
|
|
34
|
+
- [Inputs & forms](#inputs--forms) — field validation helpers, `Button`, `RadioGroupButton`, `Input`, `Textarea`, `Calendar`, `Checkbox`, `Select`, `DialogSelect`, `TreeDialogSelect`, `InputCaption`, `Label`
|
|
35
35
|
- [Navigation](#navigation) — `Tabs`, `TabsRounded`, `Stepper`
|
|
36
36
|
- [Overlays](#overlays) — `Popover`, `TooltipDark`, `TooltipLight`
|
|
37
37
|
- [Utility](#utility) — `Tag`, `CopyTextTrigger`
|
|
@@ -396,6 +396,64 @@ const [view, setView] = useState<"grid" | "list">("grid");
|
|
|
396
396
|
/>;
|
|
397
397
|
```
|
|
398
398
|
|
|
399
|
+
#### Field validation (headless)
|
|
400
|
+
|
|
401
|
+
Form state stays in your app (React Hook Form, TanStack Form, or `useState`). Cozy UI provides a shared **`FieldMeta`** contract and **`showErrorPolicy`** so all fields resolve “when to show the error” the same way.
|
|
402
|
+
|
|
403
|
+
| Export | Description |
|
|
404
|
+
| ------ | ----------- |
|
|
405
|
+
| `FieldMeta` | `touched`, `dirty`, `submitted`, `stepSubmitted`, `hasValue`, `invalid`, `errorMessage`, `validationPending`, `errorKind` |
|
|
406
|
+
| `ShowErrorPolicy` | `"default"` (legacy) \| `"draftFriendly"` \| `"wizardStep"` \| `"onBlur"` \| `"onSubmit"` \| custom |
|
|
407
|
+
| `resolveShowError`, `resolveFieldError`, `resolveFieldMessage`, `resolveDisplayError` | Pure functions (SSR-safe) |
|
|
408
|
+
| `useFieldState`, `useFormFields`, `useValidationRequest` | React hooks |
|
|
409
|
+
| `attemptWizardStep`, `attemptFormSubmit` | Validate-on-click helpers |
|
|
410
|
+
|
|
411
|
+
**Recommended policy for ERP/drafts:** `draftFriendly` — no flash on first keystroke; saved invalid visible on load.
|
|
412
|
+
|
|
413
|
+
**Legacy `default` policy:** `invalid && (touched || submitted || hasValue)`.
|
|
414
|
+
|
|
415
|
+
**Props on fields:** `error`, `suppressError`, `fieldMeta`, `showErrorPolicy`.
|
|
416
|
+
|
|
417
|
+
See [`docs/validation-recipes.md`](docs/validation-recipes.md) for recipes and erp-hr migration.
|
|
418
|
+
|
|
419
|
+
**Callback families:**
|
|
420
|
+
|
|
421
|
+
| Family | Components | Value callback | Focus |
|
|
422
|
+
| ------ | ---------- | -------------- | ----- |
|
|
423
|
+
| Native text | `Input`, `Textarea`, `Checkbox` | `onChange(event)` — DOM | `onBlur` / `onFocus` via `...rest` |
|
|
424
|
+
| Value picker | `Select`, `DialogSelect`, `TreeDialogSelect`, `Calendar` | **`onValueChange(value)`** (canonical); `onChange` deprecated alias | `onBlur` / `onFocus` on trigger |
|
|
425
|
+
|
|
426
|
+
```tsx
|
|
427
|
+
import { Input, resolveFieldError } from "@andreyfedkovich/cozy-ui";
|
|
428
|
+
import { useState } from "react";
|
|
429
|
+
|
|
430
|
+
const [email, setEmail] = useState("");
|
|
431
|
+
const [touched, setTouched] = useState(false);
|
|
432
|
+
const [submitted, setSubmitted] = useState(false);
|
|
433
|
+
|
|
434
|
+
const meta = {
|
|
435
|
+
touched,
|
|
436
|
+
submitted,
|
|
437
|
+
hasValue: email.trim().length > 0,
|
|
438
|
+
invalid: !email.includes("@"),
|
|
439
|
+
errorMessage: "Enter a valid email.",
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
// Optional: resolve message before render
|
|
443
|
+
resolveFieldError(meta, "default");
|
|
444
|
+
|
|
445
|
+
<Input
|
|
446
|
+
label="Email"
|
|
447
|
+
value={email}
|
|
448
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
449
|
+
onBlur={() => setTouched(true)}
|
|
450
|
+
fieldMeta={meta}
|
|
451
|
+
showErrorPolicy="default"
|
|
452
|
+
/>;
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
**React Hook Form (recipe):** use `register` on `Input`; use `Controller` on `Select` with `onValueChange={(opt) => field.onChange(opt)}`.
|
|
456
|
+
|
|
399
457
|
#### `Input`
|
|
400
458
|
|
|
401
459
|
Accessible text field with optional label and validation message for product forms.
|
|
@@ -405,7 +463,9 @@ Accessible text field with optional label and validation message for product for
|
|
|
405
463
|
| `label` | `ReactNode` | — | Field label above the input. |
|
|
406
464
|
| `tooltipContent` | `ReactNode` | — | Help tooltip on the «?» icon next to the label. |
|
|
407
465
|
| `tooltipPopperClassName` | `string` | — | Extra class for the tooltip popper. |
|
|
408
|
-
| `error` | `string \| null` | — | Validation message
|
|
466
|
+
| `error` | `string \| null` | — | Validation message (overrides `fieldMeta`). |
|
|
467
|
+
| `fieldMeta` | `FieldMeta` | — | Form meta for policy-based error display. |
|
|
468
|
+
| `showErrorPolicy` | `ShowErrorPolicy`| `"default"` | When to show `fieldMeta.errorMessage`. |
|
|
409
469
|
| `disabled` | `boolean` | `false` | Disabled state. |
|
|
410
470
|
| `className` | `string` | — | Wrapper class. |
|
|
411
471
|
| `inputClassName` | `string` | — | Native `<input>` class. |
|
|
@@ -477,9 +537,13 @@ Date picker field for forms. Value is stored as `yyyy-MM-dd` (or `null`); the tr
|
|
|
477
537
|
| `label` | `string` | — | Field label. |
|
|
478
538
|
| `required` | `boolean` | `false` | Appends ` *` to the label. |
|
|
479
539
|
| `value` | `string \| null` | — | Selected date as `yyyy-MM-dd`. |
|
|
480
|
-
| `
|
|
540
|
+
| `onValueChange` | `(value: string \| null) => void` | — | Called when the user picks or clears a date. |
|
|
541
|
+
| `onChange` | `(value: string \| null) => void` | — | **Deprecated.** Use `onValueChange`. |
|
|
542
|
+
| `onBlur` / `onFocus` | focus handlers | — | Forwarded to the trigger button. |
|
|
481
543
|
| `minDate` | `Date` | — | Earliest selectable day (inclusive, local calendar). |
|
|
482
|
-
| `error` | `string \| null` | — | Validation message
|
|
544
|
+
| `error` | `string \| null` | — | Validation message (overrides `fieldMeta`). |
|
|
545
|
+
| `fieldMeta` | `FieldMeta` | — | Form meta for policy-based error display. |
|
|
546
|
+
| `showErrorPolicy` | `ShowErrorPolicy` | `"default"` | When to show `fieldMeta.errorMessage`. |
|
|
483
547
|
| `disabled` | `boolean` | `false` | Disables the trigger. |
|
|
484
548
|
| `tooltipContent` | `ReactNode` | — | Help tooltip on the «i» icon next to the label. |
|
|
485
549
|
| `tooltipPopperClassName` | `string` | — | Extra class for the tooltip popper. |
|
|
@@ -500,14 +564,14 @@ const [startDate, setStartDate] = useState<string | null>(null);
|
|
|
500
564
|
label="Дата начала"
|
|
501
565
|
required
|
|
502
566
|
value={startDate}
|
|
503
|
-
|
|
567
|
+
onValueChange={setStartDate}
|
|
504
568
|
minDate={todayLocalDay()}
|
|
505
569
|
/>;
|
|
506
570
|
|
|
507
571
|
<Calendar
|
|
508
572
|
label="Дедлайн"
|
|
509
573
|
value={startDate}
|
|
510
|
-
|
|
574
|
+
onValueChange={setStartDate}
|
|
511
575
|
error="Укажите дату."
|
|
512
576
|
tooltipContent="Дата должна быть не раньше сегодня."
|
|
513
577
|
/>;
|
|
@@ -557,7 +621,10 @@ Powerful, virtualized-friendly select with `single` and `multiple` modes, search
|
|
|
557
621
|
| `mode` | `"single" \| "multiple"` | — | Selection mode. |
|
|
558
622
|
| `value` | `CustomOption \| CustomOption[]` | — | Current value. |
|
|
559
623
|
| `options` | `CustomOption[]` | — | Available options. |
|
|
560
|
-
| `
|
|
624
|
+
| `onValueChange` | `(option) => void` | — | Selection callback (canonical). |
|
|
625
|
+
| `onChange` | `(option) => void` | — | **Deprecated.** Use `onValueChange`. |
|
|
626
|
+
| `onBlur` / `onFocus` | focus handlers on trigger | — | For `touched` tracking. |
|
|
627
|
+
| `fieldMeta` / `showErrorPolicy` | see Field validation | — | Policy-based error display. |
|
|
561
628
|
| `onSearch` | `(value: string) => void` | — | Async search hook. |
|
|
562
629
|
| `template` | `"list" \| "table"` | `"list"` | Dropdown layout. |
|
|
563
630
|
| `columns` | `SelectColumn[]` | — | Required when `template="table"`. |
|
|
@@ -584,7 +651,7 @@ const [value, setValue] = useState<CustomOption<unknown, string> | null>(null);
|
|
|
584
651
|
placeholder="Pick one"
|
|
585
652
|
value={value}
|
|
586
653
|
options={options}
|
|
587
|
-
|
|
654
|
+
onValueChange={setValue}
|
|
588
655
|
/>;
|
|
589
656
|
```
|
|
590
657
|
|
|
@@ -609,7 +676,7 @@ import { DialogSelect } from "@andreyfedkovich/cozy-ui";
|
|
|
609
676
|
const { items, total } = await res.json();
|
|
610
677
|
return { options: items.map((p) => ({ value: p.id, label: p.name })), total };
|
|
611
678
|
}}
|
|
612
|
-
|
|
679
|
+
onValueChange={(opt) => console.log(opt)}
|
|
613
680
|
/>;
|
|
614
681
|
```
|
|
615
682
|
|
|
@@ -634,7 +701,7 @@ import { TreeDialogSelect } from "@andreyfedkovich/cozy-ui";
|
|
|
634
701
|
})}
|
|
635
702
|
searchNodes={async (search) => ({ matches: await searchTreeWithPath(search) })}
|
|
636
703
|
leafConfirmOnly
|
|
637
|
-
|
|
704
|
+
onValueChange={(node) => console.log(node)}
|
|
638
705
|
/>;
|
|
639
706
|
```
|
|
640
707
|
|
|
@@ -1100,6 +1167,23 @@ const [layout, setLayout] = useState<"agent" | "editor">("agent");
|
|
|
1100
1167
|
|
|
1101
1168
|
## Hooks & helpers
|
|
1102
1169
|
|
|
1170
|
+
### Field validation
|
|
1171
|
+
|
|
1172
|
+
```ts
|
|
1173
|
+
import {
|
|
1174
|
+
type FieldMeta,
|
|
1175
|
+
type ShowErrorPolicy,
|
|
1176
|
+
resolveFieldError,
|
|
1177
|
+
resolveFieldMessage,
|
|
1178
|
+
resolveDisplayError,
|
|
1179
|
+
resolveShowError,
|
|
1180
|
+
useFieldState,
|
|
1181
|
+
useFormFields,
|
|
1182
|
+
useValidationRequest,
|
|
1183
|
+
attemptWizardStep,
|
|
1184
|
+
} from "@andreyfedkovich/cozy-ui";
|
|
1185
|
+
```
|
|
1186
|
+
|
|
1103
1187
|
### `useMeasureElement`
|
|
1104
1188
|
|
|
1105
1189
|
Tracks the size of a DOM element via `ResizeObserver`.
|
package/dist-lib/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ import { default as FeedbackIcon } from './feedback.svg?react';
|
|
|
25
25
|
import { default as FileReloadIcon } from './fileReload.svg?react';
|
|
26
26
|
import { default as FileSync } from './fileSync.svg?react';
|
|
27
27
|
import { default as FilterIcon } from './filter.svg?react';
|
|
28
|
+
import { FocusEventHandler } from 'react';
|
|
28
29
|
import { default as FolderEditIcon } from './folderEdit.svg?react';
|
|
29
30
|
import { ForwardRefExoticComponent } from 'react';
|
|
30
31
|
import { default as GraduateIcon } from './graduate.svg?react';
|
|
@@ -112,6 +113,37 @@ export { ArrowDownIcon }
|
|
|
112
113
|
|
|
113
114
|
export { ArrowRightIcon }
|
|
114
115
|
|
|
116
|
+
/** Validate-on-click for form Submit. */
|
|
117
|
+
export declare function attemptFormSubmit<TValidation>(options: AttemptFormSubmitOptions<TValidation>): Promise<AttemptFormSubmitResult<TValidation>>;
|
|
118
|
+
|
|
119
|
+
export declare type AttemptFormSubmitOptions<TValidation> = {
|
|
120
|
+
markFormSubmitted: () => void;
|
|
121
|
+
validate: () => Promise<TValidation> | TValidation;
|
|
122
|
+
hasErrors: (validation: TValidation) => boolean;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export declare type AttemptFormSubmitResult<TValidation> = {
|
|
126
|
+
ok: boolean;
|
|
127
|
+
validation: TValidation;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Validate-on-click for wizard "Next" — does not require disabled={!isValid}.
|
|
132
|
+
*/
|
|
133
|
+
export declare function attemptWizardStep<TValidation>(options: AttemptWizardStepOptions<TValidation>): Promise<AttemptWizardStepResult<TValidation>>;
|
|
134
|
+
|
|
135
|
+
export declare type AttemptWizardStepOptions<TValidation> = {
|
|
136
|
+
markStepSubmitted: (step: number) => void;
|
|
137
|
+
validate: () => Promise<TValidation> | TValidation;
|
|
138
|
+
step: number;
|
|
139
|
+
hasStepErrors: (validation: TValidation, step: number) => boolean;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export declare type AttemptWizardStepResult<TValidation> = {
|
|
143
|
+
ok: boolean;
|
|
144
|
+
validation: TValidation;
|
|
145
|
+
};
|
|
146
|
+
|
|
115
147
|
export declare const BaseBlock: FC<BaseBlockProps>;
|
|
116
148
|
|
|
117
149
|
declare interface BaseBlockProps {
|
|
@@ -137,17 +169,17 @@ declare type ButtonSize = "small" | "medium" | "large";
|
|
|
137
169
|
|
|
138
170
|
declare type ButtonVariant = "default" | "primary" | "secondary" | "text" | "link" | "danger";
|
|
139
171
|
|
|
140
|
-
export declare const Calendar: ({ label, required, value, onChange, minDate, error, disabled, tooltipContent, tooltipPopperClassName, className, }: CalendarProps) => JSX.Element;
|
|
172
|
+
export declare const Calendar: ({ label, required, value, onValueChange, onChange, minDate, error, suppressError, fieldMeta, showErrorPolicy, disabled, onBlur, onFocus, tooltipContent, tooltipPopperClassName, className, }: CalendarProps) => JSX.Element;
|
|
141
173
|
|
|
142
|
-
export declare interface CalendarProps {
|
|
174
|
+
export declare interface CalendarProps extends ValueFieldCallbacks<string | null>, FieldValidationProps {
|
|
143
175
|
label: string;
|
|
144
176
|
required?: boolean;
|
|
145
177
|
value?: string | null;
|
|
146
|
-
onChange: (value: string | null) => void;
|
|
147
178
|
/** Нижняя граница выбора (включительно), локальный календарный день */
|
|
148
179
|
minDate?: Date;
|
|
149
|
-
error?: string | null;
|
|
150
180
|
disabled?: boolean;
|
|
181
|
+
onBlur?: FocusEventHandler<HTMLButtonElement>;
|
|
182
|
+
onFocus?: FocusEventHandler<HTMLButtonElement>;
|
|
151
183
|
/** Подсказка по наведению на иконку «?» справа от подписи */
|
|
152
184
|
tooltipContent?: ReactNode;
|
|
153
185
|
tooltipPopperClassName?: string;
|
|
@@ -203,9 +235,8 @@ export { ChatIcon }
|
|
|
203
235
|
|
|
204
236
|
export declare const Checkbox: ForwardRefExoticComponent<CheckboxProps & RefAttributes<HTMLInputElement>>;
|
|
205
237
|
|
|
206
|
-
export declare interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type"
|
|
238
|
+
export declare interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type">, FieldValidationProps {
|
|
207
239
|
label?: ReactNode;
|
|
208
|
-
error?: string | null;
|
|
209
240
|
checkboxClassName?: string;
|
|
210
241
|
/** Подсказка по наведению на иконку «?» справа от подписи */
|
|
211
242
|
tooltipContent?: ReactNode;
|
|
@@ -387,8 +418,7 @@ export declare interface CustomOption<T, S = string> {
|
|
|
387
418
|
meta?: T;
|
|
388
419
|
}
|
|
389
420
|
|
|
390
|
-
declare type CustomSelectProps<T, S> = {
|
|
391
|
-
onChange?: (option: CustomOption<T, S>) => void;
|
|
421
|
+
declare type CustomSelectProps<T, S> = ValueFieldCallbacks<CustomOption<T, S>> & FieldValidationProps & {
|
|
392
422
|
options?: CustomOption<T, S>[];
|
|
393
423
|
placeholder: string;
|
|
394
424
|
dropdownRender?: (menu: ReactNode) => ReactNode;
|
|
@@ -416,7 +446,8 @@ declare type CustomSelectProps<T, S> = {
|
|
|
416
446
|
disabled?: boolean;
|
|
417
447
|
onClose?: () => void;
|
|
418
448
|
portalTarget?: Element;
|
|
419
|
-
|
|
449
|
+
onBlur?: default_2.FocusEventHandler<HTMLDivElement>;
|
|
450
|
+
onFocus?: default_2.FocusEventHandler<HTMLDivElement>;
|
|
420
451
|
template?: "list" | "table";
|
|
421
452
|
columns?: SelectColumn<T, S>[];
|
|
422
453
|
total?: number;
|
|
@@ -483,7 +514,7 @@ export declare interface DetailViewProps {
|
|
|
483
514
|
id?: string;
|
|
484
515
|
}
|
|
485
516
|
|
|
486
|
-
export declare const DialogSelect: <T, S extends string | number>({ value, placeholder, loadOptions, onChange, onClear, columns, label, tooltipContent, tooltipPopperClassName, title, searchPlaceholder, selectButtonText, closeButtonText, manualButtonText, onManualAdd, pageSize, debounceMs, disabled, error, className, inputClassName, selectedOptionRender, }: DialogSelectProps<T, S>) => JSX.Element;
|
|
517
|
+
export declare const DialogSelect: <T, S extends string | number>({ value, placeholder, loadOptions, onValueChange, onChange, onBlur, onFocus, onClear, columns, label, tooltipContent, tooltipPopperClassName, title, searchPlaceholder, selectButtonText, closeButtonText, manualButtonText, onManualAdd, pageSize, debounceMs, disabled, error, suppressError, fieldMeta, showErrorPolicy, className, inputClassName, selectedOptionRender, }: DialogSelectProps<T, S>) => JSX.Element;
|
|
487
518
|
|
|
488
519
|
export declare type DialogSelectColumn<T, S extends string | number> = {
|
|
489
520
|
key: string;
|
|
@@ -492,12 +523,13 @@ export declare type DialogSelectColumn<T, S extends string | number> = {
|
|
|
492
523
|
render: (option: CustomOption<T, S>) => ReactNode;
|
|
493
524
|
};
|
|
494
525
|
|
|
495
|
-
export declare interface DialogSelectProps<T, S extends string | number> {
|
|
526
|
+
export declare interface DialogSelectProps<T, S extends string | number> extends ValueFieldCallbacks<CustomOption<T, S>>, FieldValidationProps {
|
|
496
527
|
value?: CustomOption<T, S> | null;
|
|
497
528
|
placeholder: string;
|
|
498
529
|
loadOptions: (params: LoadOptionsParams) => Promise<LoadOptionsResult<T, S>>;
|
|
499
|
-
onChange?: (option: CustomOption<T, S>) => void;
|
|
500
530
|
onClear?: () => void;
|
|
531
|
+
onBlur?: default_2.FocusEventHandler<HTMLDivElement>;
|
|
532
|
+
onFocus?: default_2.FocusEventHandler<HTMLDivElement>;
|
|
501
533
|
columns?: DialogSelectColumn<T, S>[];
|
|
502
534
|
label?: ReactNode;
|
|
503
535
|
/** Подсказка по наведению на иконку «?» справа от подписи */
|
|
@@ -512,7 +544,6 @@ export declare interface DialogSelectProps<T, S extends string | number> {
|
|
|
512
544
|
pageSize?: number;
|
|
513
545
|
debounceMs?: number;
|
|
514
546
|
disabled?: boolean;
|
|
515
|
-
error?: string | null;
|
|
516
547
|
className?: string;
|
|
517
548
|
inputClassName?: string;
|
|
518
549
|
selectedOptionRender?: (option: CustomOption<T, S>) => ReactNode;
|
|
@@ -556,14 +587,65 @@ export { EnvelopIcon }
|
|
|
556
587
|
|
|
557
588
|
export { FeedbackIcon }
|
|
558
589
|
|
|
590
|
+
export declare type FieldBinding = {
|
|
591
|
+
fieldMeta: FieldMeta;
|
|
592
|
+
showErrorPolicy?: ShowErrorPolicy;
|
|
593
|
+
onBlur: () => void;
|
|
594
|
+
onDirty?: () => void;
|
|
595
|
+
};
|
|
596
|
+
|
|
559
597
|
declare interface FieldComponentProps extends Omit<DetailField, "value"> {
|
|
560
598
|
value?: ReactNode;
|
|
561
599
|
children?: ReactNode;
|
|
562
600
|
className?: string;
|
|
563
601
|
}
|
|
564
602
|
|
|
603
|
+
export declare function FieldErrorCaption({ id, message }: FieldErrorCaptionProps): ReactNode;
|
|
604
|
+
|
|
605
|
+
declare type FieldErrorCaptionProps = {
|
|
606
|
+
id: string;
|
|
607
|
+
message: string | null;
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
export declare type FieldErrorKind = "required" | "semantic" | "custom";
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Headless field state for policy-based error display.
|
|
614
|
+
*
|
|
615
|
+
* - `touched` — user blurred the field
|
|
616
|
+
* - `dirty` — user changed the value in the current session (any change, not only clear)
|
|
617
|
+
* - `submitted` — form-level submit failed (`markFormSubmitted`)
|
|
618
|
+
* - `stepSubmitted` — wizard step "Next" failed (`markStepSubmitted`)
|
|
619
|
+
* - `hasValue` — non-empty value (trimmed string, number, selected option, etc.)
|
|
620
|
+
* - `validationPending` — async validate in flight; optionally suppresses display
|
|
621
|
+
* - `errorKind` — drives `resolveDisplayError` (stale required suppression)
|
|
622
|
+
*/
|
|
623
|
+
export declare type FieldMeta = {
|
|
624
|
+
touched?: boolean;
|
|
625
|
+
dirty?: boolean;
|
|
626
|
+
submitted?: boolean;
|
|
627
|
+
stepSubmitted?: boolean;
|
|
628
|
+
hasValue?: boolean;
|
|
629
|
+
invalid?: boolean;
|
|
630
|
+
errorMessage?: string | null;
|
|
631
|
+
validationPending?: boolean;
|
|
632
|
+
errorKind?: FieldErrorKind;
|
|
633
|
+
};
|
|
634
|
+
|
|
565
635
|
declare const FieldRow: FC<FieldComponentProps>;
|
|
566
636
|
|
|
637
|
+
export declare type FieldValidationProps = {
|
|
638
|
+
/**
|
|
639
|
+
* Explicit error string overrides `fieldMeta` + policy.
|
|
640
|
+
* Omit to use `fieldMeta`. Do not pass `null` — use {@link suppressError} instead.
|
|
641
|
+
*/
|
|
642
|
+
error?: string | null;
|
|
643
|
+
/** Force-hide any error from `fieldMeta` or explicit `error`. */
|
|
644
|
+
suppressError?: boolean;
|
|
645
|
+
fieldMeta?: FieldMeta;
|
|
646
|
+
showErrorPolicy?: ShowErrorPolicy;
|
|
647
|
+
};
|
|
648
|
+
|
|
567
649
|
export { FileReloadIcon }
|
|
568
650
|
|
|
569
651
|
export { FileSync }
|
|
@@ -572,12 +654,29 @@ export { FilterIcon }
|
|
|
572
654
|
|
|
573
655
|
export { FolderEditIcon }
|
|
574
656
|
|
|
657
|
+
/**
|
|
658
|
+
* Optional wrapper that spreads fieldMeta, showErrorPolicy, and onBlur into a field control.
|
|
659
|
+
*/
|
|
660
|
+
export declare function FormField({ bind, children }: FormFieldProps): ReactNode;
|
|
661
|
+
|
|
662
|
+
export declare type FormFieldProps = {
|
|
663
|
+
bind: FieldBinding;
|
|
664
|
+
label?: ReactNode;
|
|
665
|
+
required?: boolean;
|
|
666
|
+
children: (props: FieldBinding) => ReactNode;
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
export declare type FormFieldsApi<TValidation> = ReturnType<typeof useFormFields<TValidation>>;
|
|
670
|
+
|
|
575
671
|
export { GraduateIcon }
|
|
576
672
|
|
|
577
673
|
export { GridIcon }
|
|
578
674
|
|
|
579
675
|
declare const GroupBlock: FC<SettingsGroup>;
|
|
580
676
|
|
|
677
|
+
/** Whether a field value counts as non-empty for `FieldMeta.hasValue`. */
|
|
678
|
+
export declare function hasFieldValue(value: unknown): boolean;
|
|
679
|
+
|
|
581
680
|
export { HeartIcon }
|
|
582
681
|
|
|
583
682
|
export { HelpIcon }
|
|
@@ -610,7 +709,7 @@ export declare const Input: ForwardRefExoticComponent<InputProps & RefAttributes
|
|
|
610
709
|
|
|
611
710
|
export declare const InputCaption: React_2.FC<PropsWithChildren<InputCaptionProps>>;
|
|
612
711
|
|
|
613
|
-
declare interface InputCaptionProps {
|
|
712
|
+
declare interface InputCaptionProps extends React_2.HTMLAttributes<HTMLParagraphElement> {
|
|
614
713
|
isFullWidth?: boolean;
|
|
615
714
|
/** Visual tone. Defaults to `error` for backwards compatibility with validation messages. */
|
|
616
715
|
variant?: InputCaptionVariant;
|
|
@@ -620,15 +719,16 @@ declare interface InputCaptionProps {
|
|
|
620
719
|
|
|
621
720
|
export declare type InputCaptionVariant = "neutral" | "error" | "success";
|
|
622
721
|
|
|
623
|
-
export declare interface InputProps extends InputHTMLAttributes<HTMLInputElement
|
|
722
|
+
export declare interface InputProps extends InputHTMLAttributes<HTMLInputElement>, FieldValidationProps {
|
|
624
723
|
label?: ReactNode;
|
|
625
|
-
error?: string | null;
|
|
626
724
|
inputClassName?: string;
|
|
627
725
|
/** Подсказка по наведению на иконку «?» справа от подписи */
|
|
628
726
|
tooltipContent?: ReactNode;
|
|
629
727
|
tooltipPopperClassName?: string;
|
|
630
728
|
}
|
|
631
729
|
|
|
730
|
+
export declare function isFieldInvalid(meta: FieldMeta): boolean;
|
|
731
|
+
|
|
632
732
|
export { IslandIcon }
|
|
633
733
|
|
|
634
734
|
declare interface ItemComponentProps extends SettingsItem {
|
|
@@ -782,6 +882,35 @@ declare interface RadioGroupButtonProps<T extends string | number> {
|
|
|
782
882
|
|
|
783
883
|
export { ReloadIcon }
|
|
784
884
|
|
|
885
|
+
/**
|
|
886
|
+
* Suppresses stale or in-flight errors before display.
|
|
887
|
+
* WHY: API may still return "required" while the value is already non-empty.
|
|
888
|
+
*/
|
|
889
|
+
export declare function resolveDisplayError(options: {
|
|
890
|
+
errorMessage?: string | null;
|
|
891
|
+
errorKind?: FieldErrorKind;
|
|
892
|
+
hasValue?: boolean;
|
|
893
|
+
validationPending?: boolean;
|
|
894
|
+
}): string | null;
|
|
895
|
+
|
|
896
|
+
export declare function resolveDisplayErrorFromMeta(meta: FieldMeta): string | null;
|
|
897
|
+
|
|
898
|
+
export declare function resolveFieldError(meta: FieldMeta | undefined, policy?: ShowErrorPolicy): string | null;
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Resolves the error message to display: explicit `error` wins over fieldMeta + policy.
|
|
902
|
+
*/
|
|
903
|
+
export declare function resolveFieldMessage(options: {
|
|
904
|
+
error?: string | null;
|
|
905
|
+
suppressError?: boolean;
|
|
906
|
+
fieldMeta?: FieldMeta;
|
|
907
|
+
showErrorPolicy?: ShowErrorPolicy;
|
|
908
|
+
}): string | null;
|
|
909
|
+
|
|
910
|
+
export declare function resolveShowError(meta: FieldMeta | undefined, policy?: ShowErrorPolicy): boolean;
|
|
911
|
+
|
|
912
|
+
export declare function resolveValueChangeHandler<T>(callbacks: ValueFieldCallbacks<T>): ((value: T) => void) | undefined;
|
|
913
|
+
|
|
785
914
|
export { SchoolIcon }
|
|
786
915
|
|
|
787
916
|
export { SearchIcon }
|
|
@@ -798,7 +927,7 @@ declare interface SectionComponentProps extends Omit<DetailSection, "fields"> {
|
|
|
798
927
|
declare interface SectionProps extends SideNavSection {
|
|
799
928
|
}
|
|
800
929
|
|
|
801
|
-
export declare const Select: <T, S extends string | number>({ options, value, mode, placeholder, onChange, dropdownRender, optionRender, selectedOptionRender, dropdownIcon, tagRender, dropDownClassName, optionClassName, inputClassName, deleteIconClassName, onDelete, onClear, label, tooltipContent, tooltipPopperClassName, onSearch, searchClassName, searchPlaceholder, isLoading, disabled, onClose, portalTarget, error, template, columns, total, }: CustomSelectProps<T, S>) => JSX.Element;
|
|
930
|
+
export declare const Select: <T, S extends string | number>({ options, value, mode, placeholder, onValueChange, onChange, onBlur, onFocus, dropdownRender, optionRender, selectedOptionRender, dropdownIcon, tagRender, dropDownClassName, optionClassName, inputClassName, deleteIconClassName, onDelete, onClear, label, tooltipContent, tooltipPopperClassName, onSearch, searchClassName, searchPlaceholder, isLoading, disabled, onClose, portalTarget, error, suppressError, fieldMeta, showErrorPolicy, template, columns, total, }: CustomSelectProps<T, S>) => JSX.Element;
|
|
802
931
|
|
|
803
932
|
export declare type SelectColumn<T, S> = {
|
|
804
933
|
key: string;
|
|
@@ -878,6 +1007,8 @@ export declare interface SettingsViewProps {
|
|
|
878
1007
|
id?: string;
|
|
879
1008
|
}
|
|
880
1009
|
|
|
1010
|
+
export declare type ShowErrorPolicy = "default" | "onBlur" | "onSubmit" | "always" | "draftFriendly" | "wizardStep" | "savedInvalid" | "onBlurOrSubmit" | ((meta: FieldMeta) => boolean);
|
|
1011
|
+
|
|
881
1012
|
export declare const SideNav: SideNavComponent;
|
|
882
1013
|
|
|
883
1014
|
declare type SideNavComponent = FC<SideNavProps> & {
|
|
@@ -1036,10 +1167,9 @@ export { TaskListIcon }
|
|
|
1036
1167
|
|
|
1037
1168
|
export declare const Textarea: ForwardRefExoticComponent<TextareaProps & RefAttributes<HTMLTextAreaElement>>;
|
|
1038
1169
|
|
|
1039
|
-
export declare interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement
|
|
1170
|
+
export declare interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement>, FieldValidationProps {
|
|
1040
1171
|
label?: ReactNode;
|
|
1041
|
-
error
|
|
1042
|
-
/** Neutral helper text under the textarea (hidden when `error` is set). */
|
|
1172
|
+
/** Neutral helper text under the textarea (hidden when error is shown). */
|
|
1043
1173
|
hint?: ReactNode;
|
|
1044
1174
|
hintVariant?: InputCaptionVariant;
|
|
1045
1175
|
textareaClassName?: string;
|
|
@@ -1098,7 +1228,7 @@ export declare type TooltipTrigger = "hover" | "click";
|
|
|
1098
1228
|
/** `yyyy-MM-dd` for API / form state */
|
|
1099
1229
|
export declare const toYmdString: (d: Date) => string;
|
|
1100
1230
|
|
|
1101
|
-
export declare const TreeDialogSelect: <T, S extends string | number>({ value, placeholder, loadChildren: loadChildrenProp, loadNodes, searchNodes, onChange, onClear, label, tooltipContent, tooltipPopperClassName, title, searchPlaceholder, selectButtonText, closeButtonText, confirmButtonText, debounceMs, disabled, error, className, inputClassName, selectedOptionRender, nodeRender, leafConfirmOnly, }: TreeDialogSelectProps<T, S>) => JSX.Element;
|
|
1231
|
+
export declare const TreeDialogSelect: <T, S extends string | number>({ value, placeholder, loadChildren: loadChildrenProp, loadNodes, searchNodes, onValueChange, onChange, onBlur, onFocus, onClear, label, tooltipContent, tooltipPopperClassName, title, searchPlaceholder, selectButtonText, closeButtonText, confirmButtonText, debounceMs, disabled, error, suppressError, fieldMeta, showErrorPolicy, className, inputClassName, selectedOptionRender, nodeRender, leafConfirmOnly, }: TreeDialogSelectProps<T, S>) => JSX.Element;
|
|
1102
1232
|
|
|
1103
1233
|
/** Pass either {@link loadNodes} or {@link loadChildren} (deprecated alias). */
|
|
1104
1234
|
export declare type TreeDialogSelectProps<T, S extends string | number> = TreeDialogSelectShared<T, S> & ({
|
|
@@ -1109,12 +1239,13 @@ export declare type TreeDialogSelectProps<T, S extends string | number> = TreeDi
|
|
|
1109
1239
|
loadNodes?: TreeLoader<T, S>;
|
|
1110
1240
|
});
|
|
1111
1241
|
|
|
1112
|
-
declare interface TreeDialogSelectShared<T, S extends string | number> {
|
|
1242
|
+
declare interface TreeDialogSelectShared<T, S extends string | number> extends ValueFieldCallbacks<TreeNode<T, S>>, FieldValidationProps {
|
|
1113
1243
|
value?: TreeNode<T, S> | null;
|
|
1114
1244
|
placeholder: string;
|
|
1115
1245
|
searchNodes?: (search: string) => Promise<TreeSearchResult<T, S>>;
|
|
1116
|
-
onChange?: (node: TreeNode<T, S>) => void;
|
|
1117
1246
|
onClear?: () => void;
|
|
1247
|
+
onBlur?: default_2.FocusEventHandler<HTMLDivElement>;
|
|
1248
|
+
onFocus?: default_2.FocusEventHandler<HTMLDivElement>;
|
|
1118
1249
|
label?: ReactNode;
|
|
1119
1250
|
/** Подсказка по наведению на иконку «?» справа от подписи */
|
|
1120
1251
|
tooltipContent?: ReactNode;
|
|
@@ -1126,7 +1257,6 @@ declare interface TreeDialogSelectShared<T, S extends string | number> {
|
|
|
1126
1257
|
confirmButtonText?: string;
|
|
1127
1258
|
debounceMs?: number;
|
|
1128
1259
|
disabled?: boolean;
|
|
1129
|
-
error?: string | null;
|
|
1130
1260
|
className?: string;
|
|
1131
1261
|
inputClassName?: string;
|
|
1132
1262
|
selectedOptionRender?: (node: TreeNode<T, S>) => ReactNode;
|
|
@@ -1176,6 +1306,54 @@ declare interface UseDropdownPositionProps {
|
|
|
1176
1306
|
onAnchorFrame?: (placement: DropdownPosition) => void;
|
|
1177
1307
|
}
|
|
1178
1308
|
|
|
1309
|
+
export declare function useFieldPresentation(options: UseFieldPresentationOptions): {
|
|
1310
|
+
controlId: string;
|
|
1311
|
+
errorId: string;
|
|
1312
|
+
hintId: string | undefined;
|
|
1313
|
+
errorMessage: string | null;
|
|
1314
|
+
showError: boolean;
|
|
1315
|
+
ariaInvalid: boolean | undefined;
|
|
1316
|
+
ariaDescribedBy: string | undefined;
|
|
1317
|
+
};
|
|
1318
|
+
|
|
1319
|
+
export declare type UseFieldPresentationOptions = {
|
|
1320
|
+
error?: string | null;
|
|
1321
|
+
suppressError?: boolean;
|
|
1322
|
+
fieldMeta?: FieldMeta;
|
|
1323
|
+
showErrorPolicy?: ShowErrorPolicy;
|
|
1324
|
+
/** When set and no error, included in aria-describedby */
|
|
1325
|
+
hintId?: string;
|
|
1326
|
+
idPrefix?: string;
|
|
1327
|
+
};
|
|
1328
|
+
|
|
1329
|
+
export declare function useFieldState(fieldMeta: FieldMeta | undefined, policy?: ShowErrorPolicy, explicitError?: string | null, suppressError?: boolean): {
|
|
1330
|
+
showError: boolean;
|
|
1331
|
+
errorMessage: string | null;
|
|
1332
|
+
showErrorByPolicy: boolean;
|
|
1333
|
+
};
|
|
1334
|
+
|
|
1335
|
+
export declare function useFormFields<TValidation>(options?: UseFormFieldsOptions<TValidation>): {
|
|
1336
|
+
bindField: (path: string, value: unknown) => FieldBinding;
|
|
1337
|
+
markFieldTouched: (path: string) => void;
|
|
1338
|
+
markFieldDirty: (path: string) => void;
|
|
1339
|
+
markStepSubmitted: (step: number) => void;
|
|
1340
|
+
markFormSubmitted: () => void;
|
|
1341
|
+
resetInteraction: () => void;
|
|
1342
|
+
formSubmitted: boolean;
|
|
1343
|
+
submittedSteps: Set<number>;
|
|
1344
|
+
showErrorPolicy: ShowErrorPolicy;
|
|
1345
|
+
};
|
|
1346
|
+
|
|
1347
|
+
export declare type UseFormFieldsOptions<TValidation> = {
|
|
1348
|
+
showErrorPolicy?: ShowErrorPolicy;
|
|
1349
|
+
validation?: TValidation;
|
|
1350
|
+
getFieldError?: (validation: TValidation | undefined, path: string) => string | null;
|
|
1351
|
+
getFieldErrorKind?: (validation: TValidation | undefined, path: string) => FieldErrorKind | undefined;
|
|
1352
|
+
/** Maps field path to wizard step index for stepSubmitted. */
|
|
1353
|
+
stepForField?: (path: string) => number | undefined;
|
|
1354
|
+
validationPending?: boolean;
|
|
1355
|
+
};
|
|
1356
|
+
|
|
1179
1357
|
export declare const useMeasureElement: (element?: HTMLElement | null) => {
|
|
1180
1358
|
height: number;
|
|
1181
1359
|
width: number;
|
|
@@ -1183,6 +1361,22 @@ export declare const useMeasureElement: (element?: HTMLElement | null) => {
|
|
|
1183
1361
|
|
|
1184
1362
|
export { UserSwitchIcon }
|
|
1185
1363
|
|
|
1364
|
+
export declare function useValidationRequest<T>(validateFn: () => Promise<T>): {
|
|
1365
|
+
validate: () => Promise<T>;
|
|
1366
|
+
validationPending: boolean;
|
|
1367
|
+
validationGeneration: number;
|
|
1368
|
+
isStale: (generation: number) => boolean;
|
|
1369
|
+
};
|
|
1370
|
+
|
|
1371
|
+
export declare type ValidationRequestApi<T> = ReturnType<typeof useValidationRequest<T>>;
|
|
1372
|
+
|
|
1373
|
+
/** Picker controls: canonical value callback + deprecated alias. */
|
|
1374
|
+
export declare type ValueFieldCallbacks<T> = {
|
|
1375
|
+
onValueChange?: (value: T) => void;
|
|
1376
|
+
/** @deprecated Use {@link onValueChange}. Removed in next major. */
|
|
1377
|
+
onChange?: (value: T) => void;
|
|
1378
|
+
};
|
|
1379
|
+
|
|
1186
1380
|
export { WalletIcon }
|
|
1187
1381
|
|
|
1188
1382
|
export { WarnIcon }
|