@reformer/ui-kit 8.0.0 → 9.0.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/dist/components/state/error-state.d.ts +1 -2
- package/dist/components/state/index.d.ts +2 -0
- package/dist/components/state/loading-state.d.ts +1 -2
- package/dist/components/ui/checkbox.d.ts +1 -1
- package/dist/components/ui/input-mask.d.ts +1 -1
- package/dist/components/ui/input-password.d.ts +1 -1
- package/dist/components/ui/input.d.ts +30 -18
- package/dist/components/ui/select.d.ts +3 -3
- package/dist/components/ui/textarea.d.ts +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/ui/form-field.js +141 -133
- package/dist/ui/input.js +52 -42
- package/llms.txt +52 -37
- package/package.json +6 -8
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
interface ErrorStateProps {
|
|
2
|
+
export interface ErrorStateProps {
|
|
3
3
|
/** Текст ошибки, показывается под заголовком. */
|
|
4
4
|
error: string;
|
|
5
5
|
/** Заголовок блока ошибки. По умолчанию `'Ошибка загрузки'`. */
|
|
@@ -28,4 +28,3 @@ interface ErrorStateProps {
|
|
|
28
28
|
* ```
|
|
29
29
|
*/
|
|
30
30
|
export declare function ErrorState({ error, title, onRetry, retryLabel, className, }: ErrorStateProps): ReactNode;
|
|
31
|
-
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
interface LoadingStateProps {
|
|
2
|
+
export interface LoadingStateProps {
|
|
3
3
|
/** Основной текст. По умолчанию `'Загрузка данных...'`. */
|
|
4
4
|
title?: string;
|
|
5
5
|
/** Вспомогательный текст под спиннером. По умолчанию `'Пожалуйста, подождите'`. */
|
|
@@ -23,4 +23,3 @@ interface LoadingStateProps {
|
|
|
23
23
|
* ```
|
|
24
24
|
*/
|
|
25
25
|
export declare function LoadingState({ title, subtitle, className, }: LoadingStateProps): ReactNode;
|
|
26
|
-
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
/** Props компонента {@link Checkbox}. */
|
|
3
|
-
export interface CheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'type'> {
|
|
3
|
+
export interface CheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'type' | 'checked' | 'defaultChecked'> {
|
|
4
4
|
/** Дополнительный CSS-класс для самого input. */
|
|
5
5
|
className?: string;
|
|
6
6
|
/** Состояние чекбокса. `undefined` рендерится как `false`. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
/** Props компонента {@link InputMask}. */
|
|
3
|
-
export interface InputMaskProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange'> {
|
|
3
|
+
export interface InputMaskProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'defaultValue'> {
|
|
4
4
|
/** Дополнительный CSS-класс. */
|
|
5
5
|
className?: string;
|
|
6
6
|
/** Текущее значение поля. `null`/`undefined` рендерится как пустое поле. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
/** Props компонента {@link InputPassword}. */
|
|
3
|
-
export interface InputPasswordProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'type'> {
|
|
3
|
+
export interface InputPasswordProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'type' | 'defaultValue'> {
|
|
4
4
|
/** Дополнительный CSS-класс. */
|
|
5
5
|
className?: string;
|
|
6
6
|
/** Текущее значение пароля. `null`/`undefined` рендерится как пустое поле. */
|
|
@@ -1,29 +1,41 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
/**
|
|
3
|
-
|
|
2
|
+
/** Общие props компонента {@link Input}, не зависящие от `type`. */
|
|
3
|
+
interface InputBaseProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'defaultValue' | 'type'> {
|
|
4
4
|
/** Дополнительный CSS-класс (мерджится с дефолтными Tailwind-классами через `tailwind-merge`). */
|
|
5
5
|
className?: string;
|
|
6
|
-
/**
|
|
7
|
-
* Текущее значение поля. Для `type='number'` ожидается `number | null`,
|
|
8
|
-
* для остальных — `string | null`. `null`/`undefined` рендерится как пустое поле.
|
|
9
|
-
*/
|
|
10
|
-
value?: string | number | null;
|
|
11
|
-
/**
|
|
12
|
-
* Обработчик изменений. Получает уже распарсенное значение:
|
|
13
|
-
* - для `type='number'` — `number` или `null` (для пустой строки),
|
|
14
|
-
* - иначе — `string` или `null`.
|
|
15
|
-
* `NaN` не прокидывается. При `min >= 0` отрицательные значения превращаются в `0`.
|
|
16
|
-
*/
|
|
17
|
-
onChange?: (value: string | number | null) => void;
|
|
18
6
|
/** Срабатывает при потере фокуса. Используется `FormField` для пометки `touched`. */
|
|
19
7
|
onBlur?: () => void;
|
|
20
|
-
/** HTML-тип input. Для `'number'` включается специальный парсинг значения. */
|
|
21
|
-
type?: 'text' | 'email' | 'number' | 'tel' | 'url' | 'password';
|
|
22
8
|
/** Подсказка внутри поля. */
|
|
23
9
|
placeholder?: string;
|
|
24
10
|
/** Блокирует ввод и редактирование. */
|
|
25
11
|
disabled?: boolean;
|
|
26
12
|
}
|
|
13
|
+
/** Props числового поля (`type="number"`): `value`/`onChange` работают с `number`. */
|
|
14
|
+
export interface NumberInputProps extends InputBaseProps {
|
|
15
|
+
/** HTML-тип input. Включает специальный парсинг значения. */
|
|
16
|
+
type: 'number';
|
|
17
|
+
/** Текущее значение. `null`/`undefined` рендерится как пустое поле. */
|
|
18
|
+
value?: number | null;
|
|
19
|
+
/**
|
|
20
|
+
* Обработчик изменений. Получает уже распарсенное `number` или `null` (для пустой строки).
|
|
21
|
+
* `NaN` не прокидывается. При `min >= 0` отрицательные значения превращаются в `0`.
|
|
22
|
+
*/
|
|
23
|
+
onChange?: (value: number | null) => void;
|
|
24
|
+
}
|
|
25
|
+
/** Props строкового поля (любой `type`, кроме `number`): `value`/`onChange` — строковые. */
|
|
26
|
+
export interface TextInputProps extends InputBaseProps {
|
|
27
|
+
/** HTML-тип input. По умолчанию `'text'`. */
|
|
28
|
+
type?: 'text' | 'email' | 'tel' | 'url' | 'password';
|
|
29
|
+
/** Текущее значение. `null`/`undefined` рендерится как пустое поле. */
|
|
30
|
+
value?: string | null;
|
|
31
|
+
/** Обработчик изменений. Получает строку или `null` (для пустой строки). */
|
|
32
|
+
onChange?: (value: string | null) => void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Props компонента {@link Input} — дискриминированный union по `type`:
|
|
36
|
+
* `type="number"` даёт `number | null` для `value`/`onChange`, любой другой `type` — `string | null`.
|
|
37
|
+
*/
|
|
38
|
+
export type InputProps = NumberInputProps | TextInputProps;
|
|
27
39
|
/**
|
|
28
40
|
* Текстовое поле ввода. Контролируемый компонент с тривиальным API: `value`/`onChange`
|
|
29
41
|
* получает строку (или число для `type="number"`). Все нативные `<input>`-атрибуты
|
|
@@ -44,7 +56,7 @@ export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElem
|
|
|
44
56
|
* <Input
|
|
45
57
|
* type="number"
|
|
46
58
|
* value={age}
|
|
47
|
-
* onChange={
|
|
59
|
+
* onChange={setAge}
|
|
48
60
|
* min={0}
|
|
49
61
|
* placeholder="Возраст"
|
|
50
62
|
* />
|
|
@@ -62,7 +74,7 @@ export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElem
|
|
|
62
74
|
* type="email"
|
|
63
75
|
* value={value}
|
|
64
76
|
* disabled={disabled}
|
|
65
|
-
* onChange={(v) => control.setValue(
|
|
77
|
+
* onChange={(v) => control.setValue(v ?? '')}
|
|
66
78
|
* onBlur={() => control.markAsTouched()}
|
|
67
79
|
* aria-invalid={shouldShowError}
|
|
68
80
|
* placeholder={shouldShowError ? errors[0]?.message : 'you@example.com'}
|
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
4
4
|
export type { ResourceConfig, ResourceLoadParams, ResourceItem, ResourceResult, ResourceStrategy, NormalizedOption, } from './select-resource';
|
|
5
5
|
/** Props компонента {@link Select}. */
|
|
6
|
-
export interface SelectProps
|
|
6
|
+
export interface SelectProps extends Omit<React.ComponentProps<typeof SelectPrimitive.Root>, 'value' | 'onValueChange'> {
|
|
7
7
|
/** Дополнительный CSS-класс для триггера. */
|
|
8
8
|
className?: string;
|
|
9
9
|
/** Выбранное значение. Всегда строка из `option.value`. `null` — ничего не выбрано. */
|
|
@@ -13,7 +13,7 @@ export interface SelectProps<T> extends Omit<React.ComponentProps<typeof SelectP
|
|
|
13
13
|
/** Срабатывает при закрытии дропдауна (через `onOpenChange(false)`). */
|
|
14
14
|
onBlur?: () => void;
|
|
15
15
|
/** Асинхронный источник опций. Если задан вместе с `options`, приоритет у `options`. */
|
|
16
|
-
resource?: ResourceConfig<
|
|
16
|
+
resource?: ResourceConfig<unknown>;
|
|
17
17
|
/**
|
|
18
18
|
* Inline-варианты. `value` приводится к строке. Опции с одинаковым `group`
|
|
19
19
|
* объединяются в `SelectGroup` с `SelectLabel` (см. рецепт grouped options).
|
|
@@ -97,7 +97,7 @@ export interface SelectProps<T> extends Omit<React.ComponentProps<typeof SelectP
|
|
|
97
97
|
* <Select value={country} onChange={setCountry} resource={countries} />
|
|
98
98
|
* ```
|
|
99
99
|
*/
|
|
100
|
-
declare const Select: React.ForwardRefExoticComponent<SelectProps
|
|
100
|
+
declare const Select: React.ForwardRefExoticComponent<SelectProps & {
|
|
101
101
|
'data-testid'?: string;
|
|
102
102
|
'aria-invalid'?: boolean | "true" | "false";
|
|
103
103
|
} & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
/** Props компонента {@link Textarea}. */
|
|
3
|
-
export interface TextareaProps extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'value' | 'onChange'> {
|
|
3
|
+
export interface TextareaProps extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'value' | 'onChange' | 'defaultValue'> {
|
|
4
4
|
/** Дополнительный CSS-класс. */
|
|
5
5
|
className?: string;
|
|
6
6
|
/** Текущее значение. `null`/`undefined` рендерится как пустое поле. */
|
package/dist/index.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export { Textarea } from './components/ui/textarea';
|
|
|
27
27
|
export type { TextareaProps } from './components/ui/textarea';
|
|
28
28
|
export { ErrorState } from './components/state/error-state';
|
|
29
29
|
export { LoadingState } from './components/state/loading-state';
|
|
30
|
+
export type { ErrorStateProps } from './components/state/error-state';
|
|
31
|
+
export type { LoadingStateProps } from './components/state/loading-state';
|
|
30
32
|
export { FormArraySection } from './components/form-array/form-array-section';
|
|
31
33
|
export type { FormArraySectionProps } from './components/form-array/form-array-section';
|
|
32
34
|
export { FormWizard } from './components/form-wizard/form-wizard';
|
package/dist/ui/form-field.js
CHANGED
|
@@ -1,219 +1,227 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import * as
|
|
3
|
-
import { forwardRef as
|
|
1
|
+
import { jsx as d, jsxs as $, Fragment as w } from "react/jsx-runtime";
|
|
2
|
+
import * as A from "react";
|
|
3
|
+
import { forwardRef as b, Children as T, isValidElement as V, cloneElement as W, useId as M, useMemo as x, createContext as k, useContext as R } from "react";
|
|
4
4
|
import { useFormControl as O } from "@reformer/core";
|
|
5
|
-
import { Checkbox as
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
import { Checkbox as z } from "./checkbox.js";
|
|
6
|
+
function G(...e) {
|
|
7
|
+
return (t) => {
|
|
8
|
+
for (const o of e)
|
|
9
|
+
typeof o == "function" ? o(t) : o != null && (o.current = t);
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function H(e, t) {
|
|
13
|
+
const o = { ...e };
|
|
14
|
+
for (const r of Object.keys(t)) {
|
|
15
|
+
const n = e[r], l = t[r];
|
|
16
|
+
r.startsWith("on") && typeof n == "function" && typeof l == "function" ? o[r] = (...i) => {
|
|
17
|
+
l(...i), n(...i);
|
|
18
|
+
} : r === "className" && typeof n == "string" && typeof l == "string" ? o[r] = [l, n].filter(Boolean).join(" ") : r === "style" && typeof n == "object" && typeof l == "object" ? o[r] = { ...l, ...n } : r === "disabled" ? o[r] = !!n || !!l : l !== void 0 && (o[r] = l);
|
|
13
19
|
}
|
|
14
|
-
return
|
|
20
|
+
return o;
|
|
15
21
|
}
|
|
16
|
-
const
|
|
17
|
-
({ children: e, ...
|
|
18
|
-
const r =
|
|
19
|
-
if (!
|
|
22
|
+
const F = b(
|
|
23
|
+
({ children: e, ...t }, o) => {
|
|
24
|
+
const r = T.only(e);
|
|
25
|
+
if (!V(r))
|
|
20
26
|
return null;
|
|
21
|
-
const
|
|
22
|
-
return
|
|
23
|
-
...
|
|
24
|
-
ref:
|
|
27
|
+
const n = r.props, l = H(t, n), i = r.ref, s = G(o, i);
|
|
28
|
+
return W(r, {
|
|
29
|
+
...l,
|
|
30
|
+
ref: s
|
|
25
31
|
});
|
|
26
32
|
}
|
|
27
33
|
);
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
function
|
|
31
|
-
const e =
|
|
34
|
+
F.displayName = "Slot";
|
|
35
|
+
const j = k(null);
|
|
36
|
+
function I() {
|
|
37
|
+
const e = R(j);
|
|
32
38
|
if (!e)
|
|
33
39
|
throw new Error(
|
|
34
40
|
"FormField.* components must be used within <FormField.Root>. Wrap your field with <FormField.Root control={control}>."
|
|
35
41
|
);
|
|
36
42
|
return e;
|
|
37
43
|
}
|
|
44
|
+
const J = (e) => e.message || e.code, K = k(J);
|
|
45
|
+
function D() {
|
|
46
|
+
return R(K);
|
|
47
|
+
}
|
|
38
48
|
function N({
|
|
39
49
|
control: e,
|
|
40
|
-
children:
|
|
41
|
-
id:
|
|
50
|
+
children: t,
|
|
51
|
+
id: o,
|
|
42
52
|
hasDescription: r = !1
|
|
43
53
|
}) {
|
|
44
|
-
const
|
|
54
|
+
const n = M(), l = o ?? n, i = x(
|
|
45
55
|
() => ({
|
|
46
|
-
controlId: `control-${
|
|
47
|
-
labelId: `label-${
|
|
48
|
-
descriptionId: `desc-${
|
|
49
|
-
errorId: `error-${
|
|
56
|
+
controlId: `control-${l}`,
|
|
57
|
+
labelId: `label-${l}`,
|
|
58
|
+
descriptionId: `desc-${l}`,
|
|
59
|
+
errorId: `error-${l}`
|
|
50
60
|
}),
|
|
51
|
-
[
|
|
52
|
-
), s = O(e), c =
|
|
61
|
+
[l]
|
|
62
|
+
), s = O(e), c = D(), f = x(() => {
|
|
53
63
|
const {
|
|
54
|
-
value:
|
|
55
|
-
errors:
|
|
64
|
+
value: a,
|
|
65
|
+
errors: u,
|
|
56
66
|
pending: m,
|
|
57
|
-
disabled:
|
|
67
|
+
disabled: C,
|
|
58
68
|
valid: y,
|
|
59
|
-
invalid:
|
|
60
|
-
touched:
|
|
61
|
-
shouldShowError:
|
|
69
|
+
invalid: g,
|
|
70
|
+
touched: E,
|
|
71
|
+
shouldShowError: v,
|
|
62
72
|
componentProps: p
|
|
63
73
|
} = s;
|
|
64
74
|
return {
|
|
65
|
-
value:
|
|
66
|
-
errors:
|
|
75
|
+
value: a,
|
|
76
|
+
errors: u,
|
|
67
77
|
pending: m,
|
|
68
|
-
disabled:
|
|
78
|
+
disabled: C,
|
|
69
79
|
valid: y,
|
|
70
|
-
invalid:
|
|
71
|
-
touched:
|
|
72
|
-
shouldShowError:
|
|
73
|
-
error:
|
|
80
|
+
invalid: g,
|
|
81
|
+
touched: E,
|
|
82
|
+
shouldShowError: v,
|
|
83
|
+
error: v && u[0] ? c(u[0]) : void 0,
|
|
74
84
|
label: p.label,
|
|
75
85
|
required: !!p.required,
|
|
76
86
|
componentProps: p,
|
|
77
87
|
control: e,
|
|
78
|
-
ids:
|
|
88
|
+
ids: i,
|
|
79
89
|
hasDescription: r
|
|
80
90
|
};
|
|
81
|
-
}, [s, e,
|
|
82
|
-
return /* @__PURE__ */
|
|
91
|
+
}, [s, e, i, r, c]);
|
|
92
|
+
return /* @__PURE__ */ d(j.Provider, { value: f, children: t });
|
|
83
93
|
}
|
|
84
94
|
N.displayName = "FormField.Root";
|
|
85
|
-
const
|
|
86
|
-
({ asChild: e = !1, children:
|
|
87
|
-
const { label:
|
|
88
|
-
return !c && !
|
|
95
|
+
const S = b(
|
|
96
|
+
({ asChild: e = !1, children: t, forceRender: o = !1, ...r }, n) => {
|
|
97
|
+
const { label: l, required: i, ids: s } = I(), c = t ?? l;
|
|
98
|
+
return !c && !o ? null : /* @__PURE__ */ $(e ? F : "label", { ref: n, id: s.labelId, htmlFor: e ? void 0 : s.controlId, ...r, children: [
|
|
89
99
|
c,
|
|
90
|
-
|
|
100
|
+
i && /* @__PURE__ */ d("span", { "aria-hidden": "true", children: " *" })
|
|
91
101
|
] });
|
|
92
102
|
}
|
|
93
103
|
);
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
({ asChild: e = !1, children:
|
|
104
|
+
S.displayName = "FormField.Label";
|
|
105
|
+
const q = b(
|
|
106
|
+
({ asChild: e = !1, children: t, ...o }, r) => {
|
|
97
107
|
const {
|
|
98
|
-
control:
|
|
99
|
-
value:
|
|
100
|
-
disabled:
|
|
108
|
+
control: n,
|
|
109
|
+
value: l,
|
|
110
|
+
disabled: i,
|
|
101
111
|
shouldShowError: s,
|
|
102
112
|
errors: c,
|
|
103
|
-
required:
|
|
113
|
+
required: f,
|
|
104
114
|
ids: a,
|
|
105
|
-
hasDescription:
|
|
106
|
-
componentProps:
|
|
107
|
-
} =
|
|
108
|
-
|
|
115
|
+
hasDescription: u,
|
|
116
|
+
componentProps: m
|
|
117
|
+
} = I(), C = [
|
|
118
|
+
u ? a.descriptionId : null,
|
|
109
119
|
s && c.length > 0 ? a.errorId : null
|
|
110
|
-
].filter(Boolean).join(" ") || void 0,
|
|
120
|
+
].filter(Boolean).join(" ") || void 0, y = {
|
|
111
121
|
id: a.controlId,
|
|
112
122
|
"aria-labelledby": a.labelId,
|
|
113
123
|
"aria-invalid": s ? !0 : void 0,
|
|
114
|
-
"aria-describedby":
|
|
124
|
+
"aria-describedby": C,
|
|
115
125
|
"aria-errormessage": s && c.length > 0 ? a.errorId : void 0,
|
|
116
|
-
"aria-required":
|
|
126
|
+
"aria-required": f ? !0 : void 0
|
|
117
127
|
};
|
|
118
|
-
if (
|
|
119
|
-
return /* @__PURE__ */
|
|
120
|
-
const
|
|
121
|
-
return /* @__PURE__ */
|
|
122
|
-
|
|
128
|
+
if (t || e)
|
|
129
|
+
return /* @__PURE__ */ d(F, { ref: r, ...y, ...o, children: t });
|
|
130
|
+
const g = n.component, { testId: E, ...v } = m ?? {};
|
|
131
|
+
return /* @__PURE__ */ d(
|
|
132
|
+
g,
|
|
123
133
|
{
|
|
124
134
|
ref: r,
|
|
125
|
-
...
|
|
126
|
-
...
|
|
127
|
-
...
|
|
128
|
-
value:
|
|
129
|
-
disabled:
|
|
130
|
-
onChange: (
|
|
131
|
-
onBlur: () =>
|
|
135
|
+
...v,
|
|
136
|
+
...y,
|
|
137
|
+
...o,
|
|
138
|
+
value: l,
|
|
139
|
+
disabled: i,
|
|
140
|
+
onChange: (p) => n.setValue(p),
|
|
141
|
+
onBlur: () => n.markAsTouched()
|
|
132
142
|
}
|
|
133
143
|
);
|
|
134
144
|
}
|
|
135
145
|
);
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
({ asChild: e = !1, multi:
|
|
139
|
-
const { shouldShowError:
|
|
140
|
-
if (!
|
|
141
|
-
const
|
|
142
|
-
return
|
|
143
|
-
|
|
146
|
+
q.displayName = "FormField.Control";
|
|
147
|
+
const B = b(
|
|
148
|
+
({ asChild: e = !1, multi: t = !1, render: o, children: r, ...n }, l) => {
|
|
149
|
+
const { shouldShowError: i, errors: s, ids: c } = I(), f = D();
|
|
150
|
+
if (!i || s.length === 0) return null;
|
|
151
|
+
const a = e ? F : "p";
|
|
152
|
+
return o ? /* @__PURE__ */ d(w, { children: s.map((u, m) => /* @__PURE__ */ d(
|
|
153
|
+
a,
|
|
144
154
|
{
|
|
145
155
|
id: m === 0 ? c.errorId : void 0,
|
|
146
156
|
role: "alert",
|
|
147
|
-
...
|
|
148
|
-
children:
|
|
157
|
+
...n,
|
|
158
|
+
children: o(u, m)
|
|
149
159
|
},
|
|
150
|
-
|
|
151
|
-
)) }) :
|
|
152
|
-
|
|
160
|
+
u.code ?? m
|
|
161
|
+
)) }) : t ? /* @__PURE__ */ d(w, { children: s.map((u, m) => /* @__PURE__ */ d(
|
|
162
|
+
a,
|
|
153
163
|
{
|
|
154
164
|
id: m === 0 ? c.errorId : void 0,
|
|
155
165
|
role: "alert",
|
|
156
|
-
...
|
|
157
|
-
children:
|
|
166
|
+
...n,
|
|
167
|
+
children: f(u)
|
|
158
168
|
},
|
|
159
|
-
|
|
160
|
-
)) }) : /* @__PURE__ */
|
|
169
|
+
u.code ?? m
|
|
170
|
+
)) }) : /* @__PURE__ */ d(a, { ref: l, id: c.errorId, role: "alert", ...n, children: r ?? f(s[0]) });
|
|
161
171
|
}
|
|
162
172
|
);
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
({ asChild: e = !1, children:
|
|
166
|
-
const { ids:
|
|
167
|
-
return /* @__PURE__ */
|
|
173
|
+
B.displayName = "FormField.Error";
|
|
174
|
+
const L = b(
|
|
175
|
+
({ asChild: e = !1, children: t, ...o }, r) => {
|
|
176
|
+
const { ids: n } = I();
|
|
177
|
+
return /* @__PURE__ */ d(e ? F : "p", { ref: r, id: n.descriptionId, ...o, children: t });
|
|
168
178
|
}
|
|
169
179
|
);
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
E(G);
|
|
179
|
-
function H({
|
|
180
|
+
L.displayName = "FormField.Description";
|
|
181
|
+
const h = N;
|
|
182
|
+
h.Root = N;
|
|
183
|
+
h.Label = S;
|
|
184
|
+
h.Control = q;
|
|
185
|
+
h.Error = B;
|
|
186
|
+
h.Description = L;
|
|
187
|
+
function P({
|
|
180
188
|
className: e,
|
|
181
|
-
testIdProp:
|
|
182
|
-
isCheckbox:
|
|
189
|
+
testIdProp: t,
|
|
190
|
+
isCheckbox: o,
|
|
183
191
|
customChildren: r
|
|
184
192
|
}) {
|
|
185
|
-
const { componentProps:
|
|
186
|
-
return /* @__PURE__ */
|
|
187
|
-
!
|
|
188
|
-
|
|
193
|
+
const { componentProps: n, pending: l } = I(), i = t ?? n?.testId ?? "unknown";
|
|
194
|
+
return /* @__PURE__ */ $("div", { className: e, "data-testid": `field-${i}`, children: [
|
|
195
|
+
!o && /* @__PURE__ */ d(
|
|
196
|
+
h.Label,
|
|
189
197
|
{
|
|
190
198
|
className: "block mb-1 text-sm font-medium",
|
|
191
|
-
"data-testid": `label-${
|
|
199
|
+
"data-testid": `label-${i}`
|
|
192
200
|
}
|
|
193
201
|
),
|
|
194
|
-
r ? /* @__PURE__ */
|
|
195
|
-
/* @__PURE__ */
|
|
196
|
-
|
|
202
|
+
r ? /* @__PURE__ */ d(h.Control, { asChild: !0, children: r }) : /* @__PURE__ */ d(h.Control, { "data-testid": `input-${i}` }),
|
|
203
|
+
/* @__PURE__ */ d(
|
|
204
|
+
h.Error,
|
|
197
205
|
{
|
|
198
206
|
className: "text-destructive text-sm mt-1 block",
|
|
199
|
-
"data-testid": `error-${
|
|
207
|
+
"data-testid": `error-${i}`
|
|
200
208
|
}
|
|
201
209
|
),
|
|
202
|
-
|
|
210
|
+
l && /* @__PURE__ */ d("span", { className: "text-gray-500 text-sm mt-1 block", children: "Проверка..." })
|
|
203
211
|
] });
|
|
204
212
|
}
|
|
205
|
-
const
|
|
206
|
-
const
|
|
207
|
-
return /* @__PURE__ */
|
|
208
|
-
|
|
213
|
+
const Q = ({ control: e, className: t, testId: o, children: r }) => {
|
|
214
|
+
const n = e.component === z;
|
|
215
|
+
return /* @__PURE__ */ d(h.Root, { control: e, children: /* @__PURE__ */ d(
|
|
216
|
+
P,
|
|
209
217
|
{
|
|
210
|
-
className:
|
|
211
|
-
testIdProp:
|
|
212
|
-
isCheckbox:
|
|
218
|
+
className: t,
|
|
219
|
+
testIdProp: o,
|
|
220
|
+
isCheckbox: n,
|
|
213
221
|
customChildren: r
|
|
214
222
|
}
|
|
215
223
|
) });
|
|
216
|
-
},
|
|
224
|
+
}, _ = A.memo(Q, (e, t) => e.control === t.control && e.className === t.className && e.testId === t.testId && e.children === t.children);
|
|
217
225
|
export {
|
|
218
|
-
|
|
226
|
+
_ as FormField
|
|
219
227
|
};
|
package/dist/ui/input.js
CHANGED
|
@@ -1,48 +1,58 @@
|
|
|
1
1
|
import { jsx as g } from "react/jsx-runtime";
|
|
2
2
|
import * as u from "react";
|
|
3
|
-
import { c as
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
if (
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
s !== void 0 && s >= 0 && o < 0 ? r?.(0) : r?.(o);
|
|
16
|
-
}
|
|
3
|
+
import { c as b } from "../utils-DtaLkIY8.js";
|
|
4
|
+
const h = u.forwardRef((e, l) => {
|
|
5
|
+
const o = e.type ?? "text", r = (n) => {
|
|
6
|
+
const t = n.target.value;
|
|
7
|
+
if (e.type === "number")
|
|
8
|
+
if (t === "")
|
|
9
|
+
e.onChange?.(null);
|
|
10
|
+
else {
|
|
11
|
+
const i = Number(t);
|
|
12
|
+
if (!isNaN(i)) {
|
|
13
|
+
const a = e.min !== void 0 ? Number(e.min) : void 0;
|
|
14
|
+
a !== void 0 && a >= 0 && i < 0 ? e.onChange?.(0) : e.onChange?.(i);
|
|
17
15
|
}
|
|
18
|
-
else
|
|
19
|
-
r?.(t || null);
|
|
20
|
-
}, b = u.useMemo(() => i == null ? "" : e === "number" && typeof i == "number" ? isNaN(i) ? "" : i.toString() : String(i), [i, e]);
|
|
21
|
-
return /* @__PURE__ */ g(
|
|
22
|
-
"input",
|
|
23
|
-
{
|
|
24
|
-
ref: c,
|
|
25
|
-
type: e,
|
|
26
|
-
value: b,
|
|
27
|
-
disabled: m,
|
|
28
|
-
placeholder: d,
|
|
29
|
-
"data-slot": "input",
|
|
30
|
-
className: v(
|
|
31
|
-
"h-9 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-xs transition-colors",
|
|
32
|
-
"placeholder:text-muted-foreground",
|
|
33
|
-
"focus-visible:outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
34
|
-
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
35
|
-
"aria-invalid:border-destructive aria-invalid:ring-destructive/20",
|
|
36
|
-
l
|
|
37
|
-
),
|
|
38
|
-
onChange: f,
|
|
39
|
-
onBlur: a,
|
|
40
|
-
...n
|
|
41
16
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
)
|
|
45
|
-
|
|
17
|
+
else
|
|
18
|
+
e.onChange?.(t || null);
|
|
19
|
+
}, s = u.useMemo(() => {
|
|
20
|
+
const n = e.value;
|
|
21
|
+
return n == null ? "" : e.type === "number" && typeof n == "number" ? isNaN(n) ? "" : n.toString() : String(n);
|
|
22
|
+
}, [e.value, e.type]), {
|
|
23
|
+
className: d,
|
|
24
|
+
onBlur: c,
|
|
25
|
+
placeholder: m,
|
|
26
|
+
disabled: v,
|
|
27
|
+
value: y,
|
|
28
|
+
onChange: N,
|
|
29
|
+
type: x,
|
|
30
|
+
...f
|
|
31
|
+
} = e;
|
|
32
|
+
return /* @__PURE__ */ g(
|
|
33
|
+
"input",
|
|
34
|
+
{
|
|
35
|
+
ref: l,
|
|
36
|
+
type: o,
|
|
37
|
+
value: s,
|
|
38
|
+
disabled: v,
|
|
39
|
+
placeholder: m,
|
|
40
|
+
"data-slot": "input",
|
|
41
|
+
className: b(
|
|
42
|
+
"h-9 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-xs transition-colors",
|
|
43
|
+
"placeholder:text-muted-foreground",
|
|
44
|
+
"focus-visible:outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
45
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
46
|
+
"aria-invalid:border-destructive aria-invalid:ring-destructive/20",
|
|
47
|
+
d
|
|
48
|
+
),
|
|
49
|
+
onChange: r,
|
|
50
|
+
onBlur: c,
|
|
51
|
+
...f
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
h.displayName = "Input";
|
|
46
56
|
export {
|
|
47
|
-
|
|
57
|
+
h as Input
|
|
48
58
|
};
|
package/llms.txt
CHANGED
|
@@ -2398,7 +2398,7 @@ Props компонента {@link Checkbox}.
|
|
|
2398
2398
|
```typescript
|
|
2399
2399
|
export interface CheckboxProps extends Omit<
|
|
2400
2400
|
React.InputHTMLAttributes<HTMLInputElement>,
|
|
2401
|
-
'value' | 'onChange' | 'type'
|
|
2401
|
+
'value' | 'onChange' | 'type' | 'checked' | 'defaultChecked'
|
|
2402
2402
|
> {
|
|
2403
2403
|
/** Дополнительный CSS-класс для самого input. */
|
|
2404
2404
|
className?: string;
|
|
@@ -2583,6 +2583,28 @@ return <ErrorState error={error} onRetry={() => window.location.reload()} />;
|
|
|
2583
2583
|
|
|
2584
2584
|
_Source: src/components/state/error-state.tsx_
|
|
2585
2585
|
|
|
2586
|
+
### ErrorStateProps
|
|
2587
|
+
|
|
2588
|
+
**Kind:** `interface`
|
|
2589
|
+
|
|
2590
|
+
**Signature:**
|
|
2591
|
+
```typescript
|
|
2592
|
+
export interface ErrorStateProps {
|
|
2593
|
+
/** Текст ошибки, показывается под заголовком. */
|
|
2594
|
+
error: string;
|
|
2595
|
+
/** Заголовок блока ошибки. По умолчанию `'Ошибка загрузки'`. */
|
|
2596
|
+
title?: string;
|
|
2597
|
+
/** Колбэк повторной попытки. Если задан — рендерится кнопка «Повторить». */
|
|
2598
|
+
onRetry?: () => void;
|
|
2599
|
+
/** Подпись кнопки повтора. По умолчанию `'Повторить'`. */
|
|
2600
|
+
retryLabel?: string;
|
|
2601
|
+
/** Внешний CSS-класс контейнера. По умолчанию `'w-full'`. */
|
|
2602
|
+
className?: string;
|
|
2603
|
+
}
|
|
2604
|
+
```
|
|
2605
|
+
|
|
2606
|
+
_Source: src/components/state/error-state.tsx_
|
|
2607
|
+
|
|
2586
2608
|
### ExampleCard
|
|
2587
2609
|
|
|
2588
2610
|
**Kind:** `function`
|
|
@@ -3187,7 +3209,7 @@ import { Input } from '@reformer/ui-kit';
|
|
|
3187
3209
|
<Input
|
|
3188
3210
|
type="number"
|
|
3189
3211
|
value={age}
|
|
3190
|
-
onChange={
|
|
3212
|
+
onChange={setAge}
|
|
3191
3213
|
min={0}
|
|
3192
3214
|
placeholder="Возраст"
|
|
3193
3215
|
/>
|
|
@@ -3205,7 +3227,7 @@ return (
|
|
|
3205
3227
|
type="email"
|
|
3206
3228
|
value={value}
|
|
3207
3229
|
disabled={disabled}
|
|
3208
|
-
onChange={(v) => control.setValue(
|
|
3230
|
+
onChange={(v) => control.setValue(v ?? '')}
|
|
3209
3231
|
onBlur={() => control.markAsTouched()}
|
|
3210
3232
|
aria-invalid={shouldShowError}
|
|
3211
3233
|
placeholder={shouldShowError ? errors[0]?.message : 'you@example.com'}
|
|
@@ -3266,7 +3288,7 @@ Props компонента {@link InputMask}.
|
|
|
3266
3288
|
```typescript
|
|
3267
3289
|
export interface InputMaskProps extends Omit<
|
|
3268
3290
|
React.InputHTMLAttributes<HTMLInputElement>,
|
|
3269
|
-
'value' | 'onChange'
|
|
3291
|
+
'value' | 'onChange' | 'defaultValue'
|
|
3270
3292
|
> {
|
|
3271
3293
|
/** Дополнительный CSS-класс. */
|
|
3272
3294
|
className?: string;
|
|
@@ -3341,7 +3363,7 @@ Props компонента {@link InputPassword}.
|
|
|
3341
3363
|
```typescript
|
|
3342
3364
|
export interface InputPasswordProps extends Omit<
|
|
3343
3365
|
React.InputHTMLAttributes<HTMLInputElement>,
|
|
3344
|
-
'value' | 'onChange' | 'type'
|
|
3366
|
+
'value' | 'onChange' | 'type' | 'defaultValue'
|
|
3345
3367
|
> {
|
|
3346
3368
|
/** Дополнительный CSS-класс. */
|
|
3347
3369
|
className?: string;
|
|
@@ -3367,39 +3389,14 @@ _Source: src/components/ui/input-password.tsx_
|
|
|
3367
3389
|
|
|
3368
3390
|
### InputProps
|
|
3369
3391
|
|
|
3370
|
-
**Kind:** `
|
|
3392
|
+
**Kind:** `type`
|
|
3371
3393
|
|
|
3372
|
-
Props компонента {@link Input}
|
|
3394
|
+
Props компонента {@link Input} — дискриминированный union по `type`:
|
|
3395
|
+
`type="number"` даёт `number | null` для `value`/`onChange`, любой другой `type` — `string | null`.
|
|
3373
3396
|
|
|
3374
3397
|
**Signature:**
|
|
3375
3398
|
```typescript
|
|
3376
|
-
export
|
|
3377
|
-
React.InputHTMLAttributes<HTMLInputElement>,
|
|
3378
|
-
'value' | 'onChange'
|
|
3379
|
-
> {
|
|
3380
|
-
/** Дополнительный CSS-класс (мерджится с дефолтными Tailwind-классами через `tailwind-merge`). */
|
|
3381
|
-
className?: string;
|
|
3382
|
-
/**
|
|
3383
|
-
* Текущее значение поля. Для `type='number'` ожидается `number | null`,
|
|
3384
|
-
* для остальных — `string | null`. `null`/`undefined` рендерится как пустое поле.
|
|
3385
|
-
*/
|
|
3386
|
-
value?: string | number | null;
|
|
3387
|
-
/**
|
|
3388
|
-
* Обработчик изменений. Получает уже распарсенное значение:
|
|
3389
|
-
* - для `type='number'` — `number` или `null` (для пустой строки),
|
|
3390
|
-
* - иначе — `string` или `null`.
|
|
3391
|
-
* `NaN` не прокидывается. При `min >= 0` отрицательные значения превращаются в `0`.
|
|
3392
|
-
*/
|
|
3393
|
-
onChange?: (value: string | number | null) => void;
|
|
3394
|
-
/** Срабатывает при потере фокуса. Используется `FormField` для пометки `touched`. */
|
|
3395
|
-
onBlur?: () => void;
|
|
3396
|
-
/** HTML-тип input. Для `'number'` включается специальный парсинг значения. */
|
|
3397
|
-
type?: 'text' | 'email' | 'number' | 'tel' | 'url' | 'password';
|
|
3398
|
-
/** Подсказка внутри поля. */
|
|
3399
|
-
placeholder?: string;
|
|
3400
|
-
/** Блокирует ввод и редактирование. */
|
|
3401
|
-
disabled?: boolean;
|
|
3402
|
-
}
|
|
3399
|
+
export type InputProps = NumberInputProps | TextInputProps;
|
|
3403
3400
|
```
|
|
3404
3401
|
|
|
3405
3402
|
_Source: src/components/ui/input.tsx_
|
|
@@ -3437,6 +3434,24 @@ return <LoadingState />;
|
|
|
3437
3434
|
|
|
3438
3435
|
_Source: src/components/state/loading-state.tsx_
|
|
3439
3436
|
|
|
3437
|
+
### LoadingStateProps
|
|
3438
|
+
|
|
3439
|
+
**Kind:** `interface`
|
|
3440
|
+
|
|
3441
|
+
**Signature:**
|
|
3442
|
+
```typescript
|
|
3443
|
+
export interface LoadingStateProps {
|
|
3444
|
+
/** Основной текст. По умолчанию `'Загрузка данных...'`. */
|
|
3445
|
+
title?: string;
|
|
3446
|
+
/** Вспомогательный текст под спиннером. По умолчанию `'Пожалуйста, подождите'`. */
|
|
3447
|
+
subtitle?: string;
|
|
3448
|
+
/** Внешний CSS-класс контейнера. По умолчанию центрирует спиннер с отступами. */
|
|
3449
|
+
className?: string;
|
|
3450
|
+
}
|
|
3451
|
+
```
|
|
3452
|
+
|
|
3453
|
+
_Source: src/components/state/loading-state.tsx_
|
|
3454
|
+
|
|
3440
3455
|
### RadioGroup
|
|
3441
3456
|
|
|
3442
3457
|
**Kind:** `const`
|
|
@@ -3887,7 +3902,7 @@ Props компонента {@link Select}.
|
|
|
3887
3902
|
|
|
3888
3903
|
**Signature:**
|
|
3889
3904
|
```typescript
|
|
3890
|
-
export interface SelectProps
|
|
3905
|
+
export interface SelectProps extends Omit<
|
|
3891
3906
|
React.ComponentProps<typeof SelectPrimitive.Root>,
|
|
3892
3907
|
'value' | 'onValueChange'
|
|
3893
3908
|
> {
|
|
@@ -3900,7 +3915,7 @@ export interface SelectProps<T> extends Omit<
|
|
|
3900
3915
|
/** Срабатывает при закрытии дропдауна (через `onOpenChange(false)`). */
|
|
3901
3916
|
onBlur?: () => void;
|
|
3902
3917
|
/** Асинхронный источник опций. Если задан вместе с `options`, приоритет у `options`. */
|
|
3903
|
-
resource?: ResourceConfig<
|
|
3918
|
+
resource?: ResourceConfig<unknown>;
|
|
3904
3919
|
/**
|
|
3905
3920
|
* Inline-варианты. `value` приводится к строке. Опции с одинаковым `group`
|
|
3906
3921
|
* объединяются в `SelectGroup` с `SelectLabel` (см. рецепт grouped options).
|
|
@@ -4151,7 +4166,7 @@ Props компонента {@link Textarea}.
|
|
|
4151
4166
|
```typescript
|
|
4152
4167
|
export interface TextareaProps extends Omit<
|
|
4153
4168
|
React.TextareaHTMLAttributes<HTMLTextAreaElement>,
|
|
4154
|
-
'value' | 'onChange'
|
|
4169
|
+
'value' | 'onChange' | 'defaultValue'
|
|
4155
4170
|
> {
|
|
4156
4171
|
/** Дополнительный CSS-класс. */
|
|
4157
4172
|
className?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reformer/ui-kit",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0",
|
|
4
4
|
"description": "Styled form components with Tailwind CSS and Radix UI for @reformer ecosystem",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -121,23 +121,22 @@
|
|
|
121
121
|
"llms.txt"
|
|
122
122
|
],
|
|
123
123
|
"peerDependencies": {
|
|
124
|
-
"@radix-ui/react-select": "^2.0.0",
|
|
125
|
-
"@radix-ui/react-slot": "^1.0.0",
|
|
126
124
|
"@reformer/cdk": ">=1.0.0",
|
|
127
125
|
"@reformer/core": ">=1.1.0",
|
|
128
126
|
"@reformer/renderer-react": ">=1.0.0",
|
|
129
|
-
"lucide-react": ">=0.400.0 <1.0.0",
|
|
130
127
|
"react": "^18.0.0 || ^19.0.0",
|
|
131
128
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
132
129
|
},
|
|
133
130
|
"dependencies": {
|
|
131
|
+
"@radix-ui/react-select": "^2.2.6",
|
|
132
|
+
"@radix-ui/react-slot": "^1.2.4",
|
|
134
133
|
"class-variance-authority": "^0.7.0",
|
|
135
134
|
"clsx": "^2.0.0",
|
|
136
|
-
"
|
|
135
|
+
"lucide-react": "^0.553.0",
|
|
136
|
+
"tailwind-merge": "^2.0.0 || ^3.0.0",
|
|
137
|
+
"tslib": "^2.6.0"
|
|
137
138
|
},
|
|
138
139
|
"devDependencies": {
|
|
139
|
-
"@radix-ui/react-select": "^2.2.6",
|
|
140
|
-
"@radix-ui/react-slot": "^1.2.4",
|
|
141
140
|
"@reformer/cdk": "*",
|
|
142
141
|
"@reformer/core": "*",
|
|
143
142
|
"@reformer/renderer-react": "*",
|
|
@@ -148,7 +147,6 @@
|
|
|
148
147
|
"@vitest/utils": "^4.0.8",
|
|
149
148
|
"class-variance-authority": "^0.7.1",
|
|
150
149
|
"clsx": "^2.1.1",
|
|
151
|
-
"lucide-react": "^0.553.0",
|
|
152
150
|
"react": "^19.2.1",
|
|
153
151
|
"react-dom": "^19.2.1",
|
|
154
152
|
"tailwind-merge": "^3.4.0",
|