@andreyfedkovich/cozy-ui 0.8.0 → 0.9.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 CHANGED
@@ -3,6 +3,14 @@
3
3
  Формат основан на [Keep a Changelog](https://keepachangelog.com/).
4
4
  Версии соответствуют [Semantic Versioning](https://semver.org/) и git-тегам `v*`.
5
5
 
6
+ ## 0.9.0 - 2026-06-04
7
+
8
+ - **feat:** Единый контракт валидации полей — headless API: `FieldMeta`, `ShowErrorPolicy`, `resolveShowError`, `resolveFieldError`, `resolveFieldMessage`, `useFieldState`, `resolveValueChangeHandler`.
9
+ - **feat:** `Input`, `Textarea`, `Checkbox`, `Select`, `DialogSelect`, `TreeDialogSelect`, `Calendar` — пропсы `fieldMeta` и `showErrorPolicy`; явный `error` по-прежнему переопределяет meta. Дефолтная политика: `invalid && (touched || submitted || hasValue)`.
10
+ - **feat:** Общий A11y для полей — `aria-invalid`, `aria-describedby`, стабильные `id` через `useId()`, сообщение об ошибке с `role="alert"`.
11
+ - **feat:** Value picker’ы (`Select`, `DialogSelect`, `TreeDialogSelect`, `Calendar`) — канонический колбэк `onValueChange`; `onChange` оставлен как deprecated alias. На trigger добавлены `onBlur` / `onFocus` для интеграции с формами.
12
+ - **docs:** README — раздел Field validation, две семьи колбэков (native `onChange` vs picker `onValueChange`).
13
+
6
14
  ## 0.8.0 - 2026-06-02
7
15
 
8
16
  - **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,59 @@ 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`, `hasValue`, `invalid`, `errorMessage` |
406
+ | `ShowErrorPolicy` | `"default"` \| `"onBlur"` \| `"onSubmit"` \| `"always"` \| custom `(meta) => boolean` |
407
+ | `resolveShowError`, `resolveFieldError`, `resolveFieldMessage` | Pure functions (SSR-safe) |
408
+ | `useFieldState` | React hook wrapping the resolvers |
409
+
410
+ **Default policy:** `invalid && (touched || submitted || hasValue)`.
411
+
412
+ **Props on fields:** `error` (explicit override), `fieldMeta`, `showErrorPolicy`.
413
+
414
+ **Callback families:**
415
+
416
+ | Family | Components | Value callback | Focus |
417
+ | ------ | ---------- | -------------- | ----- |
418
+ | Native text | `Input`, `Textarea`, `Checkbox` | `onChange(event)` — DOM | `onBlur` / `onFocus` via `...rest` |
419
+ | Value picker | `Select`, `DialogSelect`, `TreeDialogSelect`, `Calendar` | **`onValueChange(value)`** (canonical); `onChange` deprecated alias | `onBlur` / `onFocus` on trigger |
420
+
421
+ ```tsx
422
+ import { Input, resolveFieldError } from "@andreyfedkovich/cozy-ui";
423
+ import { useState } from "react";
424
+
425
+ const [email, setEmail] = useState("");
426
+ const [touched, setTouched] = useState(false);
427
+ const [submitted, setSubmitted] = useState(false);
428
+
429
+ const meta = {
430
+ touched,
431
+ submitted,
432
+ hasValue: email.trim().length > 0,
433
+ invalid: !email.includes("@"),
434
+ errorMessage: "Enter a valid email.",
435
+ };
436
+
437
+ // Optional: resolve message before render
438
+ resolveFieldError(meta, "default");
439
+
440
+ <Input
441
+ label="Email"
442
+ value={email}
443
+ onChange={(e) => setEmail(e.target.value)}
444
+ onBlur={() => setTouched(true)}
445
+ fieldMeta={meta}
446
+ showErrorPolicy="default"
447
+ />;
448
+ ```
449
+
450
+ **React Hook Form (recipe):** use `register` on `Input`; use `Controller` on `Select` with `onValueChange={(opt) => field.onChange(opt)}`.
451
+
399
452
  #### `Input`
400
453
 
401
454
  Accessible text field with optional label and validation message for product forms.
@@ -405,7 +458,9 @@ Accessible text field with optional label and validation message for product for
405
458
  | `label` | `ReactNode` | — | Field label above the input. |
406
459
  | `tooltipContent` | `ReactNode` | — | Help tooltip on the «?» icon next to the label. |
407
460
  | `tooltipPopperClassName` | `string` | — | Extra class for the tooltip popper. |
408
- | `error` | `string \| null` | — | Validation message under the input. |
461
+ | `error` | `string \| null` | — | Validation message (overrides `fieldMeta`). |
462
+ | `fieldMeta` | `FieldMeta` | — | Form meta for policy-based error display. |
463
+ | `showErrorPolicy` | `ShowErrorPolicy`| `"default"` | When to show `fieldMeta.errorMessage`. |
409
464
  | `disabled` | `boolean` | `false` | Disabled state. |
410
465
  | `className` | `string` | — | Wrapper class. |
411
466
  | `inputClassName` | `string` | — | Native `<input>` class. |
@@ -477,9 +532,13 @@ Date picker field for forms. Value is stored as `yyyy-MM-dd` (or `null`); the tr
477
532
  | `label` | `string` | — | Field label. |
478
533
  | `required` | `boolean` | `false` | Appends ` *` to the label. |
479
534
  | `value` | `string \| null` | — | Selected date as `yyyy-MM-dd`. |
480
- | `onChange` | `(value: string \| null) => void` | — | Called when the user picks or clears a date. |
535
+ | `onValueChange` | `(value: string \| null) => void` | — | Called when the user picks or clears a date. |
536
+ | `onChange` | `(value: string \| null) => void` | — | **Deprecated.** Use `onValueChange`. |
537
+ | `onBlur` / `onFocus` | focus handlers | — | Forwarded to the trigger button. |
481
538
  | `minDate` | `Date` | — | Earliest selectable day (inclusive, local calendar). |
482
- | `error` | `string \| null` | — | Validation message under the field. |
539
+ | `error` | `string \| null` | — | Validation message (overrides `fieldMeta`). |
540
+ | `fieldMeta` | `FieldMeta` | — | Form meta for policy-based error display. |
541
+ | `showErrorPolicy` | `ShowErrorPolicy` | `"default"` | When to show `fieldMeta.errorMessage`. |
483
542
  | `disabled` | `boolean` | `false` | Disables the trigger. |
484
543
  | `tooltipContent` | `ReactNode` | — | Help tooltip on the «i» icon next to the label. |
485
544
  | `tooltipPopperClassName` | `string` | — | Extra class for the tooltip popper. |
@@ -500,14 +559,14 @@ const [startDate, setStartDate] = useState<string | null>(null);
500
559
  label="Дата начала"
501
560
  required
502
561
  value={startDate}
503
- onChange={setStartDate}
562
+ onValueChange={setStartDate}
504
563
  minDate={todayLocalDay()}
505
564
  />;
506
565
 
507
566
  <Calendar
508
567
  label="Дедлайн"
509
568
  value={startDate}
510
- onChange={setStartDate}
569
+ onValueChange={setStartDate}
511
570
  error="Укажите дату."
512
571
  tooltipContent="Дата должна быть не раньше сегодня."
513
572
  />;
@@ -557,7 +616,10 @@ Powerful, virtualized-friendly select with `single` and `multiple` modes, search
557
616
  | `mode` | `"single" \| "multiple"` | — | Selection mode. |
558
617
  | `value` | `CustomOption \| CustomOption[]` | — | Current value. |
559
618
  | `options` | `CustomOption[]` | — | Available options. |
560
- | `onChange` | `(option) => void` | — | Selection callback. |
619
+ | `onValueChange` | `(option) => void` | — | Selection callback (canonical). |
620
+ | `onChange` | `(option) => void` | — | **Deprecated.** Use `onValueChange`. |
621
+ | `onBlur` / `onFocus` | focus handlers on trigger | — | For `touched` tracking. |
622
+ | `fieldMeta` / `showErrorPolicy` | see Field validation | — | Policy-based error display. |
561
623
  | `onSearch` | `(value: string) => void` | — | Async search hook. |
562
624
  | `template` | `"list" \| "table"` | `"list"` | Dropdown layout. |
563
625
  | `columns` | `SelectColumn[]` | — | Required when `template="table"`. |
@@ -584,7 +646,7 @@ const [value, setValue] = useState<CustomOption<unknown, string> | null>(null);
584
646
  placeholder="Pick one"
585
647
  value={value}
586
648
  options={options}
587
- onChange={setValue}
649
+ onValueChange={setValue}
588
650
  />;
589
651
  ```
590
652
 
@@ -609,7 +671,7 @@ import { DialogSelect } from "@andreyfedkovich/cozy-ui";
609
671
  const { items, total } = await res.json();
610
672
  return { options: items.map((p) => ({ value: p.id, label: p.name })), total };
611
673
  }}
612
- onChange={(opt) => console.log(opt)}
674
+ onValueChange={(opt) => console.log(opt)}
613
675
  />;
614
676
  ```
615
677
 
@@ -634,7 +696,7 @@ import { TreeDialogSelect } from "@andreyfedkovich/cozy-ui";
634
696
  })}
635
697
  searchNodes={async (search) => ({ matches: await searchTreeWithPath(search) })}
636
698
  leafConfirmOnly
637
- onChange={(node) => console.log(node)}
699
+ onValueChange={(node) => console.log(node)}
638
700
  />;
639
701
  ```
640
702
 
@@ -1100,6 +1162,19 @@ const [layout, setLayout] = useState<"agent" | "editor">("agent");
1100
1162
 
1101
1163
  ## Hooks & helpers
1102
1164
 
1165
+ ### Field validation
1166
+
1167
+ ```ts
1168
+ import {
1169
+ type FieldMeta,
1170
+ type ShowErrorPolicy,
1171
+ resolveFieldError,
1172
+ resolveFieldMessage,
1173
+ resolveShowError,
1174
+ useFieldState,
1175
+ } from "@andreyfedkovich/cozy-ui";
1176
+ ```
1177
+
1103
1178
  ### `useMeasureElement`
1104
1179
 
1105
1180
  Tracks the size of a DOM element via `ResizeObserver`.
@@ -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';
@@ -137,17 +138,17 @@ declare type ButtonSize = "small" | "medium" | "large";
137
138
 
138
139
  declare type ButtonVariant = "default" | "primary" | "secondary" | "text" | "link" | "danger";
139
140
 
140
- export declare const Calendar: ({ label, required, value, onChange, minDate, error, disabled, tooltipContent, tooltipPopperClassName, className, }: CalendarProps) => JSX.Element;
141
+ export declare const Calendar: ({ label, required, value, onValueChange, onChange, minDate, error, fieldMeta, showErrorPolicy, disabled, onBlur, onFocus, tooltipContent, tooltipPopperClassName, className, }: CalendarProps) => JSX.Element;
141
142
 
142
- export declare interface CalendarProps {
143
+ export declare interface CalendarProps extends ValueFieldCallbacks<string | null>, FieldValidationProps {
143
144
  label: string;
144
145
  required?: boolean;
145
146
  value?: string | null;
146
- onChange: (value: string | null) => void;
147
147
  /** Нижняя граница выбора (включительно), локальный календарный день */
148
148
  minDate?: Date;
149
- error?: string | null;
150
149
  disabled?: boolean;
150
+ onBlur?: FocusEventHandler<HTMLButtonElement>;
151
+ onFocus?: FocusEventHandler<HTMLButtonElement>;
151
152
  /** Подсказка по наведению на иконку «?» справа от подписи */
152
153
  tooltipContent?: ReactNode;
153
154
  tooltipPopperClassName?: string;
@@ -203,9 +204,8 @@ export { ChatIcon }
203
204
 
204
205
  export declare const Checkbox: ForwardRefExoticComponent<CheckboxProps & RefAttributes<HTMLInputElement>>;
205
206
 
206
- export declare interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type"> {
207
+ export declare interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type">, FieldValidationProps {
207
208
  label?: ReactNode;
208
- error?: string | null;
209
209
  checkboxClassName?: string;
210
210
  /** Подсказка по наведению на иконку «?» справа от подписи */
211
211
  tooltipContent?: ReactNode;
@@ -387,8 +387,7 @@ export declare interface CustomOption<T, S = string> {
387
387
  meta?: T;
388
388
  }
389
389
 
390
- declare type CustomSelectProps<T, S> = {
391
- onChange?: (option: CustomOption<T, S>) => void;
390
+ declare type CustomSelectProps<T, S> = ValueFieldCallbacks<CustomOption<T, S>> & FieldValidationProps & {
392
391
  options?: CustomOption<T, S>[];
393
392
  placeholder: string;
394
393
  dropdownRender?: (menu: ReactNode) => ReactNode;
@@ -416,7 +415,8 @@ declare type CustomSelectProps<T, S> = {
416
415
  disabled?: boolean;
417
416
  onClose?: () => void;
418
417
  portalTarget?: Element;
419
- error?: string | null;
418
+ onBlur?: default_2.FocusEventHandler<HTMLDivElement>;
419
+ onFocus?: default_2.FocusEventHandler<HTMLDivElement>;
420
420
  template?: "list" | "table";
421
421
  columns?: SelectColumn<T, S>[];
422
422
  total?: number;
@@ -483,7 +483,7 @@ export declare interface DetailViewProps {
483
483
  id?: string;
484
484
  }
485
485
 
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;
486
+ 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, fieldMeta, showErrorPolicy, className, inputClassName, selectedOptionRender, }: DialogSelectProps<T, S>) => JSX.Element;
487
487
 
488
488
  export declare type DialogSelectColumn<T, S extends string | number> = {
489
489
  key: string;
@@ -492,12 +492,13 @@ export declare type DialogSelectColumn<T, S extends string | number> = {
492
492
  render: (option: CustomOption<T, S>) => ReactNode;
493
493
  };
494
494
 
495
- export declare interface DialogSelectProps<T, S extends string | number> {
495
+ export declare interface DialogSelectProps<T, S extends string | number> extends ValueFieldCallbacks<CustomOption<T, S>>, FieldValidationProps {
496
496
  value?: CustomOption<T, S> | null;
497
497
  placeholder: string;
498
498
  loadOptions: (params: LoadOptionsParams) => Promise<LoadOptionsResult<T, S>>;
499
- onChange?: (option: CustomOption<T, S>) => void;
500
499
  onClear?: () => void;
500
+ onBlur?: default_2.FocusEventHandler<HTMLDivElement>;
501
+ onFocus?: default_2.FocusEventHandler<HTMLDivElement>;
501
502
  columns?: DialogSelectColumn<T, S>[];
502
503
  label?: ReactNode;
503
504
  /** Подсказка по наведению на иконку «?» справа от подписи */
@@ -512,7 +513,6 @@ export declare interface DialogSelectProps<T, S extends string | number> {
512
513
  pageSize?: number;
513
514
  debounceMs?: number;
514
515
  disabled?: boolean;
515
- error?: string | null;
516
516
  className?: string;
517
517
  inputClassName?: string;
518
518
  selectedOptionRender?: (option: CustomOption<T, S>) => ReactNode;
@@ -562,8 +562,24 @@ declare interface FieldComponentProps extends Omit<DetailField, "value"> {
562
562
  className?: string;
563
563
  }
564
564
 
565
+ export declare type FieldMeta = {
566
+ touched?: boolean;
567
+ dirty?: boolean;
568
+ submitted?: boolean;
569
+ hasValue?: boolean;
570
+ invalid?: boolean;
571
+ errorMessage?: string | null;
572
+ };
573
+
565
574
  declare const FieldRow: FC<FieldComponentProps>;
566
575
 
576
+ export declare type FieldValidationProps = {
577
+ /** Explicit error overrides {@link fieldMeta} + policy when set. */
578
+ error?: string | null;
579
+ fieldMeta?: FieldMeta;
580
+ showErrorPolicy?: ShowErrorPolicy;
581
+ };
582
+
567
583
  export { FileReloadIcon }
568
584
 
569
585
  export { FileSync }
@@ -610,7 +626,7 @@ export declare const Input: ForwardRefExoticComponent<InputProps & RefAttributes
610
626
 
611
627
  export declare const InputCaption: React_2.FC<PropsWithChildren<InputCaptionProps>>;
612
628
 
613
- declare interface InputCaptionProps {
629
+ declare interface InputCaptionProps extends React_2.HTMLAttributes<HTMLParagraphElement> {
614
630
  isFullWidth?: boolean;
615
631
  /** Visual tone. Defaults to `error` for backwards compatibility with validation messages. */
616
632
  variant?: InputCaptionVariant;
@@ -620,15 +636,16 @@ declare interface InputCaptionProps {
620
636
 
621
637
  export declare type InputCaptionVariant = "neutral" | "error" | "success";
622
638
 
623
- export declare interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
639
+ export declare interface InputProps extends InputHTMLAttributes<HTMLInputElement>, FieldValidationProps {
624
640
  label?: ReactNode;
625
- error?: string | null;
626
641
  inputClassName?: string;
627
642
  /** Подсказка по наведению на иконку «?» справа от подписи */
628
643
  tooltipContent?: ReactNode;
629
644
  tooltipPopperClassName?: string;
630
645
  }
631
646
 
647
+ export declare function isFieldInvalid(meta: FieldMeta): boolean;
648
+
632
649
  export { IslandIcon }
633
650
 
634
651
  declare interface ItemComponentProps extends SettingsItem {
@@ -782,6 +799,21 @@ declare interface RadioGroupButtonProps<T extends string | number> {
782
799
 
783
800
  export { ReloadIcon }
784
801
 
802
+ export declare function resolveFieldError(meta: FieldMeta | undefined, policy?: ShowErrorPolicy): string | null;
803
+
804
+ /**
805
+ * Resolves the error message to display: explicit `error` wins over fieldMeta + policy.
806
+ */
807
+ export declare function resolveFieldMessage(options: {
808
+ error?: string | null;
809
+ fieldMeta?: FieldMeta;
810
+ showErrorPolicy?: ShowErrorPolicy;
811
+ }): string | null;
812
+
813
+ export declare function resolveShowError(meta: FieldMeta | undefined, policy?: ShowErrorPolicy): boolean;
814
+
815
+ export declare function resolveValueChangeHandler<T>(callbacks: ValueFieldCallbacks<T>): ((value: T) => void) | undefined;
816
+
785
817
  export { SchoolIcon }
786
818
 
787
819
  export { SearchIcon }
@@ -798,7 +830,7 @@ declare interface SectionComponentProps extends Omit<DetailSection, "fields"> {
798
830
  declare interface SectionProps extends SideNavSection {
799
831
  }
800
832
 
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;
833
+ 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, fieldMeta, showErrorPolicy, template, columns, total, }: CustomSelectProps<T, S>) => JSX.Element;
802
834
 
803
835
  export declare type SelectColumn<T, S> = {
804
836
  key: string;
@@ -878,6 +910,8 @@ export declare interface SettingsViewProps {
878
910
  id?: string;
879
911
  }
880
912
 
913
+ export declare type ShowErrorPolicy = "default" | "onBlur" | "onSubmit" | "always" | ((meta: FieldMeta) => boolean);
914
+
881
915
  export declare const SideNav: SideNavComponent;
882
916
 
883
917
  declare type SideNavComponent = FC<SideNavProps> & {
@@ -1036,10 +1070,9 @@ export { TaskListIcon }
1036
1070
 
1037
1071
  export declare const Textarea: ForwardRefExoticComponent<TextareaProps & RefAttributes<HTMLTextAreaElement>>;
1038
1072
 
1039
- export declare interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
1073
+ export declare interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement>, FieldValidationProps {
1040
1074
  label?: ReactNode;
1041
- error?: string | null;
1042
- /** Neutral helper text under the textarea (hidden when `error` is set). */
1075
+ /** Neutral helper text under the textarea (hidden when error is shown). */
1043
1076
  hint?: ReactNode;
1044
1077
  hintVariant?: InputCaptionVariant;
1045
1078
  textareaClassName?: string;
@@ -1098,7 +1131,7 @@ export declare type TooltipTrigger = "hover" | "click";
1098
1131
  /** `yyyy-MM-dd` for API / form state */
1099
1132
  export declare const toYmdString: (d: Date) => string;
1100
1133
 
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;
1134
+ 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, fieldMeta, showErrorPolicy, className, inputClassName, selectedOptionRender, nodeRender, leafConfirmOnly, }: TreeDialogSelectProps<T, S>) => JSX.Element;
1102
1135
 
1103
1136
  /** Pass either {@link loadNodes} or {@link loadChildren} (deprecated alias). */
1104
1137
  export declare type TreeDialogSelectProps<T, S extends string | number> = TreeDialogSelectShared<T, S> & ({
@@ -1109,12 +1142,13 @@ export declare type TreeDialogSelectProps<T, S extends string | number> = TreeDi
1109
1142
  loadNodes?: TreeLoader<T, S>;
1110
1143
  });
1111
1144
 
1112
- declare interface TreeDialogSelectShared<T, S extends string | number> {
1145
+ declare interface TreeDialogSelectShared<T, S extends string | number> extends ValueFieldCallbacks<TreeNode<T, S>>, FieldValidationProps {
1113
1146
  value?: TreeNode<T, S> | null;
1114
1147
  placeholder: string;
1115
1148
  searchNodes?: (search: string) => Promise<TreeSearchResult<T, S>>;
1116
- onChange?: (node: TreeNode<T, S>) => void;
1117
1149
  onClear?: () => void;
1150
+ onBlur?: default_2.FocusEventHandler<HTMLDivElement>;
1151
+ onFocus?: default_2.FocusEventHandler<HTMLDivElement>;
1118
1152
  label?: ReactNode;
1119
1153
  /** Подсказка по наведению на иконку «?» справа от подписи */
1120
1154
  tooltipContent?: ReactNode;
@@ -1126,7 +1160,6 @@ declare interface TreeDialogSelectShared<T, S extends string | number> {
1126
1160
  confirmButtonText?: string;
1127
1161
  debounceMs?: number;
1128
1162
  disabled?: boolean;
1129
- error?: string | null;
1130
1163
  className?: string;
1131
1164
  inputClassName?: string;
1132
1165
  selectedOptionRender?: (node: TreeNode<T, S>) => ReactNode;
@@ -1176,6 +1209,12 @@ declare interface UseDropdownPositionProps {
1176
1209
  onAnchorFrame?: (placement: DropdownPosition) => void;
1177
1210
  }
1178
1211
 
1212
+ export declare function useFieldState(fieldMeta: FieldMeta | undefined, policy?: ShowErrorPolicy, explicitError?: string | null): {
1213
+ showError: boolean;
1214
+ errorMessage: string | null;
1215
+ showErrorByPolicy: boolean;
1216
+ };
1217
+
1179
1218
  export declare const useMeasureElement: (element?: HTMLElement | null) => {
1180
1219
  height: number;
1181
1220
  width: number;
@@ -1183,6 +1222,13 @@ export declare const useMeasureElement: (element?: HTMLElement | null) => {
1183
1222
 
1184
1223
  export { UserSwitchIcon }
1185
1224
 
1225
+ /** Picker controls: canonical value callback + deprecated alias. */
1226
+ export declare type ValueFieldCallbacks<T> = {
1227
+ onValueChange?: (value: T) => void;
1228
+ /** @deprecated Use {@link onValueChange}. Removed in next major. */
1229
+ onChange?: (value: T) => void;
1230
+ };
1231
+
1186
1232
  export { WalletIcon }
1187
1233
 
1188
1234
  export { WarnIcon }