@letar/forms 1.0.3 → 1.2.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 +331 -0
- package/README.md +9 -9
- package/README.ru.md +389 -0
- package/analytics.js +3 -0
- package/analytics.js.map +1 -0
- package/chunk-2PSXYC3I.js +1782 -0
- package/chunk-2PSXYC3I.js.map +1 -0
- package/chunk-5D6S6EGF.js +206 -0
- package/chunk-5D6S6EGF.js.map +1 -0
- package/chunk-6E7VJAJT.js +78 -0
- package/chunk-6E7VJAJT.js.map +1 -0
- package/chunk-CGXKRCSM.js +117 -0
- package/chunk-CGXKRCSM.js.map +1 -0
- package/chunk-DQUVUMCX.js +982 -0
- package/chunk-DQUVUMCX.js.map +1 -0
- package/chunk-K3J4L26K.js +345 -0
- package/chunk-K3J4L26K.js.map +1 -0
- package/chunk-MAYUFA5K.js +822 -0
- package/chunk-MAYUFA5K.js.map +1 -0
- package/{chunk-4V6WBJ76.js → chunk-MVGXZNHP.js} +2 -2
- package/{chunk-4V6WBJ76.js.map → chunk-MVGXZNHP.js.map} +1 -1
- package/chunk-MZDTJSF7.js +299 -0
- package/chunk-MZDTJSF7.js.map +1 -0
- package/chunk-Q5EOF36Y.js +709 -0
- package/chunk-Q5EOF36Y.js.map +1 -0
- package/{chunk-7FEQFDJ7.js → chunk-R2RTCKXY.js} +2 -2
- package/{chunk-7FEQFDJ7.js.map → chunk-R2RTCKXY.js.map} +1 -1
- package/chunk-XFWLD5EO.js +1045 -0
- package/chunk-XFWLD5EO.js.map +1 -0
- package/fields/boolean.js +5 -0
- package/fields/boolean.js.map +1 -0
- package/fields/datetime.js +5 -0
- package/fields/datetime.js.map +1 -0
- package/fields/number.js +5 -0
- package/fields/number.js.map +1 -0
- package/fields/selection.js +5 -0
- package/fields/selection.js.map +1 -0
- package/fields/specialized.js +5 -0
- package/fields/specialized.js.map +1 -0
- package/fields/text.js +5 -0
- package/fields/text.js.map +1 -0
- package/hcaptcha-U4XIT3HS.js +64 -0
- package/hcaptcha-U4XIT3HS.js.map +1 -0
- package/i18n.js +1 -1
- package/index.js +3736 -4962
- package/index.js.map +1 -1
- package/offline.js +1 -1
- package/package.json +59 -4
- package/recaptcha-PKAUAY2S.js +56 -0
- package/recaptcha-PKAUAY2S.js.map +1 -0
- package/server-errors.js +3 -0
- package/server-errors.js.map +1 -0
- package/turnstile-7FXTBSLW.js +36 -0
- package/turnstile-7FXTBSLW.js.map +1 -0
- package/validators/ru.js +73 -0
- package/validators/ru.js.map +1 -0
|
@@ -0,0 +1,1045 @@
|
|
|
1
|
+
import { useFormI18n, getLocalizedValue, useLocalizedOptions } from './chunk-R2RTCKXY.js';
|
|
2
|
+
import { createContext, memo, useContext, useState, useEffect, useMemo, useRef, useCallback, Component } from 'react';
|
|
3
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
4
|
+
import { Field, HStack, Tooltip, Circle, Portal, VStack, Text, Box, createListCollection } from '@chakra-ui/react';
|
|
5
|
+
import { LuCircleHelp } from 'react-icons/lu';
|
|
6
|
+
|
|
7
|
+
var FormGroupContext = createContext(null);
|
|
8
|
+
function FormGroup({ name, children }) {
|
|
9
|
+
const parentContext = useContext(FormGroupContext);
|
|
10
|
+
const contextValue = {
|
|
11
|
+
originalName: name,
|
|
12
|
+
name: parentContext ? `${parentContext.name}.${name}` : name
|
|
13
|
+
};
|
|
14
|
+
return /* @__PURE__ */ jsx(FormGroupContext.Provider, { value: contextValue, children });
|
|
15
|
+
}
|
|
16
|
+
function useFormGroup() {
|
|
17
|
+
return useContext(FormGroupContext);
|
|
18
|
+
}
|
|
19
|
+
var DeclarativeFormContext = createContext(null);
|
|
20
|
+
function useDeclarativeForm() {
|
|
21
|
+
const context = useContext(DeclarativeFormContext);
|
|
22
|
+
if (!context) {
|
|
23
|
+
throw new Error("useDeclarativeForm must be used inside a Form component");
|
|
24
|
+
}
|
|
25
|
+
return context;
|
|
26
|
+
}
|
|
27
|
+
function useDeclarativeFormOptional() {
|
|
28
|
+
return useContext(DeclarativeFormContext);
|
|
29
|
+
}
|
|
30
|
+
function FieldTooltip({ title, description, example, impact }) {
|
|
31
|
+
return /* @__PURE__ */ jsxs(Tooltip.Root, { openDelay: 200, positioning: { placement: "top" }, children: [
|
|
32
|
+
/* @__PURE__ */ jsx(Tooltip.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
33
|
+
Circle,
|
|
34
|
+
{
|
|
35
|
+
size: "5",
|
|
36
|
+
cursor: "help",
|
|
37
|
+
color: "fg.muted",
|
|
38
|
+
_hover: { color: "colorPalette.fg" },
|
|
39
|
+
transition: "color 0.2s",
|
|
40
|
+
display: "inline-flex",
|
|
41
|
+
children: /* @__PURE__ */ jsx(LuCircleHelp, { size: 16 })
|
|
42
|
+
}
|
|
43
|
+
) }),
|
|
44
|
+
/* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Tooltip.Positioner, { children: /* @__PURE__ */ jsxs(Tooltip.Content, { children: [
|
|
45
|
+
/* @__PURE__ */ jsx(Tooltip.Arrow, { children: /* @__PURE__ */ jsx(Tooltip.ArrowTip, {}) }),
|
|
46
|
+
/* @__PURE__ */ jsxs(VStack, { align: "start", gap: 2, maxW: "280px", p: 1, children: [
|
|
47
|
+
title && /* @__PURE__ */ jsx(Text, { fontWeight: "semibold", fontSize: "sm", children: title }),
|
|
48
|
+
/* @__PURE__ */ jsx(Text, { fontSize: "sm", children: description }),
|
|
49
|
+
example && /* @__PURE__ */ jsxs(Box, { bg: "bg.emphasized", px: 2, py: 1, borderRadius: "md", w: "full", children: [
|
|
50
|
+
/* @__PURE__ */ jsx(Text, { fontSize: "xs", color: "fg.muted", children: "Example:" }),
|
|
51
|
+
/* @__PURE__ */ jsxs(Text, { fontSize: "sm", fontStyle: "italic", children: [
|
|
52
|
+
'"',
|
|
53
|
+
example,
|
|
54
|
+
'"'
|
|
55
|
+
] })
|
|
56
|
+
] }),
|
|
57
|
+
impact && /* @__PURE__ */ jsx(Text, { fontSize: "xs", color: "green.fg", children: impact })
|
|
58
|
+
] })
|
|
59
|
+
] }) }) })
|
|
60
|
+
] });
|
|
61
|
+
}
|
|
62
|
+
function FieldLabel({ label, tooltip, required }) {
|
|
63
|
+
if (!label) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return /* @__PURE__ */ jsxs(Field.Label, { children: [
|
|
67
|
+
tooltip ? /* @__PURE__ */ jsxs(HStack, { gap: 1, children: [
|
|
68
|
+
/* @__PURE__ */ jsx("span", { children: label }),
|
|
69
|
+
/* @__PURE__ */ jsx(FieldTooltip, { ...tooltip })
|
|
70
|
+
] }) : label,
|
|
71
|
+
required && /* @__PURE__ */ jsx(Field.RequiredIndicator, {})
|
|
72
|
+
] });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/lib/declarative/zod-utils.ts
|
|
76
|
+
function unwrapSchema(schema) {
|
|
77
|
+
if (!schema?._zod?.def) {
|
|
78
|
+
return schema;
|
|
79
|
+
}
|
|
80
|
+
const type = schema._zod.def.type;
|
|
81
|
+
if (type === "optional" || type === "nullable" || type === "default") {
|
|
82
|
+
const inner = schema._zod.def.inner ?? schema._zod.def.innerType;
|
|
83
|
+
if (inner) {
|
|
84
|
+
return unwrapSchema(inner);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return schema;
|
|
88
|
+
}
|
|
89
|
+
function unwrapSchemaWithRequired(schema) {
|
|
90
|
+
if (!schema?._zod?.def) {
|
|
91
|
+
return { schema, required: true };
|
|
92
|
+
}
|
|
93
|
+
const type = schema._zod.def.type;
|
|
94
|
+
if (type === "optional" || type === "nullable") {
|
|
95
|
+
const inner = schema._zod.def.inner ?? schema._zod.def.innerType;
|
|
96
|
+
if (inner) {
|
|
97
|
+
const result = unwrapSchemaWithRequired(inner);
|
|
98
|
+
return { schema: result.schema, required: false };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (type === "default") {
|
|
102
|
+
const inner = schema._zod.def.inner ?? schema._zod.def.innerType;
|
|
103
|
+
if (inner) {
|
|
104
|
+
const result = unwrapSchemaWithRequired(inner);
|
|
105
|
+
return { schema: result.schema, required: false };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return { schema, required: true };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/lib/declarative/schema-constraints.ts
|
|
112
|
+
function getZodConstraints(schema, path) {
|
|
113
|
+
if (!schema) {
|
|
114
|
+
return { schemaType: "unknown" };
|
|
115
|
+
}
|
|
116
|
+
const fieldSchema = getSchemaAtPath(schema, path);
|
|
117
|
+
if (!fieldSchema) {
|
|
118
|
+
return { schemaType: "unknown" };
|
|
119
|
+
}
|
|
120
|
+
const def = fieldSchema._zod?.def;
|
|
121
|
+
if (!def) {
|
|
122
|
+
return { schemaType: "unknown" };
|
|
123
|
+
}
|
|
124
|
+
const type = def.type;
|
|
125
|
+
const checks = def.checks || [];
|
|
126
|
+
switch (type) {
|
|
127
|
+
case "string":
|
|
128
|
+
return {
|
|
129
|
+
schemaType: "string",
|
|
130
|
+
string: extractStringConstraints(checks)
|
|
131
|
+
};
|
|
132
|
+
case "number":
|
|
133
|
+
return {
|
|
134
|
+
schemaType: "number",
|
|
135
|
+
number: extractNumberConstraints(checks)
|
|
136
|
+
};
|
|
137
|
+
case "date":
|
|
138
|
+
return {
|
|
139
|
+
schemaType: "date",
|
|
140
|
+
date: extractDateConstraints(checks)
|
|
141
|
+
};
|
|
142
|
+
case "array":
|
|
143
|
+
return {
|
|
144
|
+
schemaType: "array",
|
|
145
|
+
array: extractArrayConstraints(checks)
|
|
146
|
+
};
|
|
147
|
+
case "boolean":
|
|
148
|
+
return { schemaType: "boolean" };
|
|
149
|
+
case "enum":
|
|
150
|
+
return { schemaType: "enum" };
|
|
151
|
+
default:
|
|
152
|
+
return { schemaType: "unknown" };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function extractConstraints(checks, handlers) {
|
|
156
|
+
const constraints = {};
|
|
157
|
+
for (const check of checks) {
|
|
158
|
+
const checkDef = check._zod?.def;
|
|
159
|
+
if (!checkDef) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const handler = handlers[checkDef.check];
|
|
163
|
+
if (handler) {
|
|
164
|
+
handler(constraints, checkDef);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return constraints;
|
|
168
|
+
}
|
|
169
|
+
var stringConstraintHandlers = {
|
|
170
|
+
min_length: (c, def) => {
|
|
171
|
+
c.minLength = def.minimum;
|
|
172
|
+
},
|
|
173
|
+
max_length: (c, def) => {
|
|
174
|
+
c.maxLength = def.maximum;
|
|
175
|
+
},
|
|
176
|
+
length_equals: (c, def) => {
|
|
177
|
+
c.minLength = def.length;
|
|
178
|
+
c.maxLength = def.length;
|
|
179
|
+
},
|
|
180
|
+
string_format: (c, def) => {
|
|
181
|
+
if (def.format === "email") {
|
|
182
|
+
c.inputType = "email";
|
|
183
|
+
} else if (def.format === "url") {
|
|
184
|
+
c.inputType = "url";
|
|
185
|
+
} else if (def.format === "regex" && def.pattern?.source) {
|
|
186
|
+
c.pattern = def.pattern.source;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
function extractStringConstraints(checks) {
|
|
191
|
+
return extractConstraints(checks, stringConstraintHandlers);
|
|
192
|
+
}
|
|
193
|
+
var numberConstraintHandlers = {
|
|
194
|
+
greater_than: (c, def) => {
|
|
195
|
+
c.min = def.value;
|
|
196
|
+
},
|
|
197
|
+
less_than: (c, def) => {
|
|
198
|
+
c.max = def.value;
|
|
199
|
+
},
|
|
200
|
+
number_format: (c, def) => {
|
|
201
|
+
if (def.format === "safeint") {
|
|
202
|
+
c.isInteger = true;
|
|
203
|
+
c.step = 1;
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
multiple_of: (c, def) => {
|
|
207
|
+
c.step = def.value;
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
function extractNumberConstraints(checks) {
|
|
211
|
+
return extractConstraints(checks, numberConstraintHandlers);
|
|
212
|
+
}
|
|
213
|
+
var dateConstraintHandlers = {
|
|
214
|
+
greater_than: (c, def) => {
|
|
215
|
+
if (def.value) {
|
|
216
|
+
c.min = formatDateToISO(def.value);
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
less_than: (c, def) => {
|
|
220
|
+
if (def.value) {
|
|
221
|
+
c.max = formatDateToISO(def.value);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
function extractDateConstraints(checks) {
|
|
226
|
+
return extractConstraints(checks, dateConstraintHandlers);
|
|
227
|
+
}
|
|
228
|
+
var arrayConstraintHandlers = {
|
|
229
|
+
min_length: (c, def) => {
|
|
230
|
+
c.minItems = def.minimum;
|
|
231
|
+
},
|
|
232
|
+
max_length: (c, def) => {
|
|
233
|
+
c.maxItems = def.maximum;
|
|
234
|
+
},
|
|
235
|
+
length: (c, def) => {
|
|
236
|
+
c.minItems = def.length;
|
|
237
|
+
c.maxItems = def.length;
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
function extractArrayConstraints(checks) {
|
|
241
|
+
return extractConstraints(checks, arrayConstraintHandlers);
|
|
242
|
+
}
|
|
243
|
+
function getSchemaAtPath(schema, path) {
|
|
244
|
+
if (!schema || !path) {
|
|
245
|
+
return schema;
|
|
246
|
+
}
|
|
247
|
+
const parts = path.split(".");
|
|
248
|
+
let current = schema;
|
|
249
|
+
for (const part of parts) {
|
|
250
|
+
current = unwrapSchema(current);
|
|
251
|
+
if (!current) {
|
|
252
|
+
return void 0;
|
|
253
|
+
}
|
|
254
|
+
if (/^\d+$/.test(part)) {
|
|
255
|
+
if (current._zod?.def?.type === "array") {
|
|
256
|
+
current = current._zod.def.element;
|
|
257
|
+
}
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (current._zod?.def?.type === "object") {
|
|
261
|
+
const shape = current._zod.def.shape;
|
|
262
|
+
if (shape && part in shape) {
|
|
263
|
+
current = shape[part];
|
|
264
|
+
} else {
|
|
265
|
+
return void 0;
|
|
266
|
+
}
|
|
267
|
+
} else {
|
|
268
|
+
return void 0;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return unwrapSchema(current);
|
|
272
|
+
}
|
|
273
|
+
function formatDateToISO(value) {
|
|
274
|
+
let date;
|
|
275
|
+
if (value instanceof Date) {
|
|
276
|
+
date = value;
|
|
277
|
+
} else if (typeof value === "string") {
|
|
278
|
+
date = new Date(value);
|
|
279
|
+
} else if (typeof value === "number") {
|
|
280
|
+
date = new Date(value);
|
|
281
|
+
} else {
|
|
282
|
+
return "";
|
|
283
|
+
}
|
|
284
|
+
if (isNaN(date.getTime())) {
|
|
285
|
+
return "";
|
|
286
|
+
}
|
|
287
|
+
const year = date.getFullYear();
|
|
288
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
289
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
290
|
+
return `${year}-${month}-${day}`;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/lib/declarative/schema-meta.ts
|
|
294
|
+
function getFieldMeta(schema, path) {
|
|
295
|
+
if (!schema) {
|
|
296
|
+
return { required: false };
|
|
297
|
+
}
|
|
298
|
+
const result = getSchemaAtPath2(schema, path);
|
|
299
|
+
if (!result.schema) {
|
|
300
|
+
return { required: false };
|
|
301
|
+
}
|
|
302
|
+
const fieldSchema = result.schema;
|
|
303
|
+
const meta = typeof fieldSchema?.meta === "function" ? fieldSchema.meta() : void 0;
|
|
304
|
+
return {
|
|
305
|
+
ui: meta?.ui,
|
|
306
|
+
required: result.required
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function unwrapToBaseSchema(schema) {
|
|
310
|
+
if (!schema?._zod?.def) {
|
|
311
|
+
return schema;
|
|
312
|
+
}
|
|
313
|
+
const type = schema._zod.def.type;
|
|
314
|
+
if (type === "effects" || type === "transform" || type === "preprocess") {
|
|
315
|
+
const inner = schema._zod.def.inner ?? schema._zod.def.schema;
|
|
316
|
+
if (inner) {
|
|
317
|
+
return unwrapToBaseSchema(inner);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
if (type === "pipeline") {
|
|
321
|
+
const inner = schema._zod.def.in;
|
|
322
|
+
if (inner) {
|
|
323
|
+
return unwrapToBaseSchema(inner);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return schema;
|
|
327
|
+
}
|
|
328
|
+
function getSchemaAtPath2(schema, path) {
|
|
329
|
+
if (!schema || !path) {
|
|
330
|
+
return { schema, required: true };
|
|
331
|
+
}
|
|
332
|
+
const parts = path.split(".");
|
|
333
|
+
let current = schema;
|
|
334
|
+
let isRequired = true;
|
|
335
|
+
for (const part of parts) {
|
|
336
|
+
current = unwrapToBaseSchema(current);
|
|
337
|
+
const unwrapped = unwrapSchemaWithRequired(current);
|
|
338
|
+
current = unwrapped.schema;
|
|
339
|
+
if (!unwrapped.required) {
|
|
340
|
+
isRequired = false;
|
|
341
|
+
}
|
|
342
|
+
if (!current) {
|
|
343
|
+
return { schema: void 0, required: false };
|
|
344
|
+
}
|
|
345
|
+
current = unwrapToBaseSchema(current);
|
|
346
|
+
if (/^\d+$/.test(part)) {
|
|
347
|
+
if (current._zod?.def?.type === "array") {
|
|
348
|
+
current = current._zod.def.element;
|
|
349
|
+
}
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
if (current._zod?.def?.type === "object") {
|
|
353
|
+
const shape = current._zod.def.shape;
|
|
354
|
+
if (shape && part in shape) {
|
|
355
|
+
current = shape[part];
|
|
356
|
+
} else {
|
|
357
|
+
return { schema: void 0, required: false };
|
|
358
|
+
}
|
|
359
|
+
} else {
|
|
360
|
+
return { schema: void 0, required: false };
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
current = unwrapToBaseSchema(current);
|
|
364
|
+
const finalUnwrap = unwrapSchemaWithRequired(current);
|
|
365
|
+
return {
|
|
366
|
+
// Возвращаем схему ДО unwrap — на ней can быть мета (.default().meta())
|
|
367
|
+
schema: current,
|
|
368
|
+
required: isRequired && finalUnwrap.required
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// src/lib/declarative/form-fields/base/base-field.tsx
|
|
373
|
+
function useDeclarativeField(name) {
|
|
374
|
+
const { form, schema, primitiveArrayIndex, disabled, readOnly } = useDeclarativeForm();
|
|
375
|
+
const parentGroup = useFormGroup();
|
|
376
|
+
let fullPath;
|
|
377
|
+
if (name) {
|
|
378
|
+
fullPath = parentGroup ? `${parentGroup.name}.${name}` : name;
|
|
379
|
+
} else if (parentGroup) {
|
|
380
|
+
fullPath = parentGroup.name;
|
|
381
|
+
} else {
|
|
382
|
+
throw new Error("Field must have a name prop or be inside Form.Group.List for primitive arrays");
|
|
383
|
+
}
|
|
384
|
+
const schemaInfo = getFieldMeta(schema, fullPath);
|
|
385
|
+
const constraints = getZodConstraints(schema, fullPath);
|
|
386
|
+
return {
|
|
387
|
+
form,
|
|
388
|
+
fullPath,
|
|
389
|
+
name: name ?? String(primitiveArrayIndex),
|
|
390
|
+
meta: schemaInfo.ui,
|
|
391
|
+
required: schemaInfo.required,
|
|
392
|
+
formDisabled: disabled ?? false,
|
|
393
|
+
formReadOnly: readOnly ?? false,
|
|
394
|
+
constraints
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
function useDebounce(value, delay = 300) {
|
|
398
|
+
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
399
|
+
useEffect(() => {
|
|
400
|
+
const timer = setTimeout(() => setDebouncedValue(value), delay);
|
|
401
|
+
return () => clearTimeout(timer);
|
|
402
|
+
}, [value, delay]);
|
|
403
|
+
return debouncedValue;
|
|
404
|
+
}
|
|
405
|
+
function useAsyncSearch(options = {}) {
|
|
406
|
+
const { useQuery, debounce = 300, minChars = 1, initialValue = "" } = options;
|
|
407
|
+
const [inputValue, setInputValue] = useState(initialValue);
|
|
408
|
+
const debouncedSearch = useDebounce(inputValue, debounce);
|
|
409
|
+
const shouldQuery = debouncedSearch.length >= minChars;
|
|
410
|
+
const queryResult = useQuery?.(shouldQuery ? debouncedSearch : "");
|
|
411
|
+
const { data, isLoading = false, error } = queryResult ?? {};
|
|
412
|
+
return {
|
|
413
|
+
inputValue,
|
|
414
|
+
setInputValue,
|
|
415
|
+
debouncedSearch,
|
|
416
|
+
shouldQuery,
|
|
417
|
+
isLoading,
|
|
418
|
+
data,
|
|
419
|
+
error
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
var FieldErrorBoundary = class extends Component {
|
|
423
|
+
constructor(props) {
|
|
424
|
+
super(props);
|
|
425
|
+
this.state = { hasError: false, error: null };
|
|
426
|
+
}
|
|
427
|
+
static getDerivedStateFromError(error) {
|
|
428
|
+
return { hasError: true, error };
|
|
429
|
+
}
|
|
430
|
+
componentDidCatch(error, errorInfo) {
|
|
431
|
+
console.error(`[Form] \u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u043E\u043B\u0435 "${this.props.fieldName}":`, error, errorInfo);
|
|
432
|
+
}
|
|
433
|
+
render() {
|
|
434
|
+
if (this.state.hasError) {
|
|
435
|
+
return /* @__PURE__ */ jsx(Box, { p: 3, borderWidth: "1px", borderColor: "red.500", borderRadius: "md", bg: "red.50", _dark: { bg: "red.950" }, children: /* @__PURE__ */ jsxs(Text, { color: "red.600", _dark: { color: "red.300" }, fontSize: "sm", children: [
|
|
436
|
+
'\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u043E\u043B\u0435 "',
|
|
437
|
+
this.props.fieldName,
|
|
438
|
+
'": ',
|
|
439
|
+
this.state.error?.message
|
|
440
|
+
] }) });
|
|
441
|
+
}
|
|
442
|
+
return this.props.children;
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
// src/lib/declarative/form-fields/base/field-utils.ts
|
|
447
|
+
function formatFieldErrors(errors) {
|
|
448
|
+
return errors.map((e) => extractErrorMessage(e)).filter(Boolean).join(", ");
|
|
449
|
+
}
|
|
450
|
+
function extractErrorMessage(e) {
|
|
451
|
+
if (typeof e === "string") {
|
|
452
|
+
return e;
|
|
453
|
+
}
|
|
454
|
+
if (e === null || e === void 0) {
|
|
455
|
+
return "";
|
|
456
|
+
}
|
|
457
|
+
if (typeof e === "object") {
|
|
458
|
+
if (Array.isArray(e)) {
|
|
459
|
+
return e.map((item) => {
|
|
460
|
+
if (typeof item === "object" && item && "message" in item) {
|
|
461
|
+
return item.message;
|
|
462
|
+
}
|
|
463
|
+
if (typeof item === "string") {
|
|
464
|
+
return item;
|
|
465
|
+
}
|
|
466
|
+
return "";
|
|
467
|
+
}).filter(Boolean).join(", ");
|
|
468
|
+
}
|
|
469
|
+
if ("message" in e && typeof e.message === "string") {
|
|
470
|
+
return e.message;
|
|
471
|
+
}
|
|
472
|
+
if ("issues" in e && Array.isArray(e.issues)) {
|
|
473
|
+
return e.issues.map((issue) => {
|
|
474
|
+
if (typeof issue === "object" && issue && "message" in issue) {
|
|
475
|
+
return issue.message;
|
|
476
|
+
}
|
|
477
|
+
return "";
|
|
478
|
+
}).filter(Boolean).join(", ");
|
|
479
|
+
}
|
|
480
|
+
if ("fieldErrors" in e && typeof e.fieldErrors === "object" && e.fieldErrors) {
|
|
481
|
+
const fieldErrors = e.fieldErrors;
|
|
482
|
+
return Object.values(fieldErrors).flat().filter(Boolean).join(", ");
|
|
483
|
+
}
|
|
484
|
+
if ("_errors" in e && Array.isArray(e._errors)) {
|
|
485
|
+
return e._errors.filter(Boolean).join(", ");
|
|
486
|
+
}
|
|
487
|
+
try {
|
|
488
|
+
const json = JSON.stringify(e);
|
|
489
|
+
if (json === "{}" || json.length > 200) {
|
|
490
|
+
return "";
|
|
491
|
+
}
|
|
492
|
+
if (process.env.NODE_ENV === "development") {
|
|
493
|
+
console.warn("[form-components] Unknown error format:", e);
|
|
494
|
+
}
|
|
495
|
+
return json;
|
|
496
|
+
} catch {
|
|
497
|
+
return "";
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return String(e);
|
|
501
|
+
}
|
|
502
|
+
function hasFieldErrors(errors) {
|
|
503
|
+
return Boolean(errors && errors.length > 0);
|
|
504
|
+
}
|
|
505
|
+
function getFieldErrors(field) {
|
|
506
|
+
const errors = field.state.meta.errors ?? [];
|
|
507
|
+
const hasError = hasFieldErrors(errors);
|
|
508
|
+
const errorMessage = hasError ? formatFieldErrors(errors) : "";
|
|
509
|
+
return { errors, hasError, errorMessage };
|
|
510
|
+
}
|
|
511
|
+
function getAsyncValidateFromSchema(schema, fieldPath) {
|
|
512
|
+
if (!schema) return void 0;
|
|
513
|
+
try {
|
|
514
|
+
const unwrapped = unwrapSchema(schema);
|
|
515
|
+
if (!unwrapped?._zod?.def?.shape) return void 0;
|
|
516
|
+
const parts = fieldPath.split(".");
|
|
517
|
+
let current = unwrapped._zod.def.shape;
|
|
518
|
+
for (const part of parts) {
|
|
519
|
+
if (/^\d+$/.test(part)) continue;
|
|
520
|
+
const fieldSchema = current[part];
|
|
521
|
+
if (!fieldSchema) return void 0;
|
|
522
|
+
const fieldUnwrapped = unwrapSchema(fieldSchema);
|
|
523
|
+
const fieldType = fieldUnwrapped?._zod?.def?.type;
|
|
524
|
+
if (fieldType === "object" && fieldUnwrapped._zod.def.shape) {
|
|
525
|
+
current = fieldUnwrapped._zod.def.shape;
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
if (fieldType === "array" && fieldUnwrapped._zod.def.element) {
|
|
529
|
+
const elementUnwrapped = unwrapSchema(fieldUnwrapped._zod.def.element);
|
|
530
|
+
if (elementUnwrapped?._zod?.def?.shape) {
|
|
531
|
+
current = elementUnwrapped._zod.def.shape;
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
if (fieldSchema?.meta) {
|
|
536
|
+
const meta = fieldSchema.meta();
|
|
537
|
+
if (meta?.asyncValidate && typeof meta.asyncValidate === "function") {
|
|
538
|
+
return {
|
|
539
|
+
asyncValidate: meta.asyncValidate,
|
|
540
|
+
asyncDebounce: meta.asyncDebounce,
|
|
541
|
+
asyncTrigger: meta.asyncTrigger
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return void 0;
|
|
546
|
+
}
|
|
547
|
+
} catch {
|
|
548
|
+
return void 0;
|
|
549
|
+
}
|
|
550
|
+
return void 0;
|
|
551
|
+
}
|
|
552
|
+
function useAsyncFieldValidation(schema, fieldPath, propsConfig) {
|
|
553
|
+
const abortRef = useRef(null);
|
|
554
|
+
const cacheRef = useRef(/* @__PURE__ */ new Map());
|
|
555
|
+
const schemaConfig = useMemo(
|
|
556
|
+
() => getAsyncValidateFromSchema(schema, fieldPath),
|
|
557
|
+
[schema, fieldPath]
|
|
558
|
+
);
|
|
559
|
+
const asyncFn = propsConfig?.asyncValidate ?? schemaConfig?.asyncValidate;
|
|
560
|
+
const debounce = propsConfig?.asyncDebounce ?? schemaConfig?.asyncDebounce ?? 500;
|
|
561
|
+
const trigger = propsConfig?.asyncTrigger ?? schemaConfig?.asyncTrigger ?? "onBlur";
|
|
562
|
+
const wrappedAsyncFn = useCallback(
|
|
563
|
+
async ({ value }) => {
|
|
564
|
+
if (!asyncFn) return void 0;
|
|
565
|
+
if (typeof navigator !== "undefined" && !navigator.onLine) return void 0;
|
|
566
|
+
const cacheKey = String(value);
|
|
567
|
+
if (cacheRef.current.has(cacheKey)) {
|
|
568
|
+
return cacheRef.current.get(cacheKey);
|
|
569
|
+
}
|
|
570
|
+
abortRef.current?.abort();
|
|
571
|
+
const controller = new AbortController();
|
|
572
|
+
abortRef.current = controller;
|
|
573
|
+
try {
|
|
574
|
+
const result = await asyncFn(value);
|
|
575
|
+
if (controller.signal.aborted) return void 0;
|
|
576
|
+
cacheRef.current.set(cacheKey, result);
|
|
577
|
+
return result;
|
|
578
|
+
} catch (err) {
|
|
579
|
+
if (err instanceof DOMException && err.name === "AbortError") return void 0;
|
|
580
|
+
return void 0;
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
[asyncFn]
|
|
584
|
+
);
|
|
585
|
+
const validators = useMemo(() => {
|
|
586
|
+
if (!asyncFn) return void 0;
|
|
587
|
+
if (trigger === "onChange") {
|
|
588
|
+
return { onChangeAsync: wrappedAsyncFn };
|
|
589
|
+
}
|
|
590
|
+
return { onBlurAsync: wrappedAsyncFn };
|
|
591
|
+
}, [asyncFn, trigger, wrappedAsyncFn]);
|
|
592
|
+
return {
|
|
593
|
+
validators,
|
|
594
|
+
asyncDebounceMs: asyncFn ? debounce : void 0,
|
|
595
|
+
hasAsyncValidation: !!asyncFn
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// src/lib/declarative/constraint-hints.ts
|
|
600
|
+
var EN_TRANSLATIONS = {
|
|
601
|
+
string_exact: "Exactly {n} {chars}",
|
|
602
|
+
string_range: "From {min} to {max} characters",
|
|
603
|
+
string_max: "Maximum {n} {chars}",
|
|
604
|
+
string_min: "Minimum {n} {chars}",
|
|
605
|
+
number_range: "From {min} to {max}{suffix}",
|
|
606
|
+
number_max: "Maximum {max}{suffix}",
|
|
607
|
+
number_min: "Minimum {min}{suffix}",
|
|
608
|
+
number_integer: "Integer",
|
|
609
|
+
number_integer_suffix: " (integer)",
|
|
610
|
+
date_range: "From {min} to {max}",
|
|
611
|
+
date_after: "Not before {min}",
|
|
612
|
+
date_before: "Not after {max}",
|
|
613
|
+
array_exact: "Exactly {n} {items}",
|
|
614
|
+
array_range: "From {min} to {max} items",
|
|
615
|
+
array_max: "Maximum {n} {items}",
|
|
616
|
+
array_min: "Minimum {n} {items}"
|
|
617
|
+
};
|
|
618
|
+
var RU_TRANSLATIONS = {
|
|
619
|
+
string_exact: "\u0420\u043E\u0432\u043D\u043E {n} {chars}",
|
|
620
|
+
string_range: "\u041E\u0442 {min} \u0434\u043E {max} \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432",
|
|
621
|
+
string_max: "\u041C\u0430\u043A\u0441\u0438\u043C\u0443\u043C {n} {chars}",
|
|
622
|
+
string_min: "\u041C\u0438\u043D\u0438\u043C\u0443\u043C {n} {chars}",
|
|
623
|
+
number_range: "\u041E\u0442 {min} \u0434\u043E {max}{suffix}",
|
|
624
|
+
number_max: "\u041C\u0430\u043A\u0441\u0438\u043C\u0443\u043C {max}{suffix}",
|
|
625
|
+
number_min: "\u041C\u0438\u043D\u0438\u043C\u0443\u043C {min}{suffix}",
|
|
626
|
+
number_integer: "\u0426\u0435\u043B\u043E\u0435 \u0447\u0438\u0441\u043B\u043E",
|
|
627
|
+
number_integer_suffix: " (\u0446\u0435\u043B\u043E\u0435)",
|
|
628
|
+
date_range: "\u0421 {min} \u043F\u043E {max}",
|
|
629
|
+
date_after: "\u041D\u0435 \u0440\u0430\u043D\u0435\u0435 {min}",
|
|
630
|
+
date_before: "\u041D\u0435 \u043F\u043E\u0437\u0434\u043D\u0435\u0435 {max}",
|
|
631
|
+
array_exact: "\u0420\u043E\u0432\u043D\u043E {n} {items}",
|
|
632
|
+
array_range: "\u041E\u0442 {min} \u0434\u043E {max} \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u043E\u0432",
|
|
633
|
+
array_max: "\u041C\u0430\u043A\u0441\u0438\u043C\u0443\u043C {n} {items}",
|
|
634
|
+
array_min: "\u041C\u0438\u043D\u0438\u043C\u0443\u043C {n} {items}"
|
|
635
|
+
};
|
|
636
|
+
var BUILTIN_TRANSLATIONS = {
|
|
637
|
+
en: EN_TRANSLATIONS,
|
|
638
|
+
ru: RU_TRANSLATIONS
|
|
639
|
+
};
|
|
640
|
+
var CHAR_PLURALS = {
|
|
641
|
+
en: { one: "character", other: "characters" },
|
|
642
|
+
ru: { one: "\u0441\u0438\u043C\u0432\u043E\u043B", few: "\u0441\u0438\u043C\u0432\u043E\u043B\u0430", many: "\u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432", other: "\u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432" }
|
|
643
|
+
};
|
|
644
|
+
var ITEM_PLURALS = {
|
|
645
|
+
en: { one: "item", other: "items" },
|
|
646
|
+
ru: { one: "\u044D\u043B\u0435\u043C\u0435\u043D\u0442", few: "\u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430", many: "\u044D\u043B\u0435\u043C\u0435\u043D\u0442\u043E\u0432", other: "\u044D\u043B\u0435\u043C\u0435\u043D\u0442\u043E\u0432" }
|
|
647
|
+
};
|
|
648
|
+
function pluralizeWord(n, locale, plurals) {
|
|
649
|
+
const lang = locale.split("-")[0];
|
|
650
|
+
const forms = plurals[lang] ?? plurals.en;
|
|
651
|
+
const rule = new Intl.PluralRules(locale).select(n);
|
|
652
|
+
return forms[rule] ?? forms.other;
|
|
653
|
+
}
|
|
654
|
+
function formatNumber(n, locale) {
|
|
655
|
+
if (Number.isInteger(n)) {
|
|
656
|
+
return new Intl.NumberFormat(locale).format(n);
|
|
657
|
+
}
|
|
658
|
+
return new Intl.NumberFormat(locale, { maximumFractionDigits: 2 }).format(n);
|
|
659
|
+
}
|
|
660
|
+
function formatDate(dateStr, locale) {
|
|
661
|
+
try {
|
|
662
|
+
const date = new Date(dateStr);
|
|
663
|
+
return new Intl.DateTimeFormat(locale, {
|
|
664
|
+
day: "numeric",
|
|
665
|
+
month: "long",
|
|
666
|
+
year: "numeric"
|
|
667
|
+
}).format(date);
|
|
668
|
+
} catch {
|
|
669
|
+
return dateStr;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
function template(str, vars) {
|
|
673
|
+
return str.replace(/\{(\w+)\}/g, (_, key) => String(vars[key] ?? ""));
|
|
674
|
+
}
|
|
675
|
+
function getTranslations(locale, custom) {
|
|
676
|
+
const lang = locale.split("-")[0];
|
|
677
|
+
const base = BUILTIN_TRANSLATIONS[lang] ?? BUILTIN_TRANSLATIONS[locale] ?? EN_TRANSLATIONS;
|
|
678
|
+
return base;
|
|
679
|
+
}
|
|
680
|
+
function generateConstraintHint(constraints, locale = "en", customTranslations) {
|
|
681
|
+
if (!constraints) {
|
|
682
|
+
return void 0;
|
|
683
|
+
}
|
|
684
|
+
const t = getTranslations(locale);
|
|
685
|
+
switch (constraints.schemaType) {
|
|
686
|
+
case "string":
|
|
687
|
+
return generateStringHint(constraints.string, locale, t);
|
|
688
|
+
case "number":
|
|
689
|
+
return generateNumberHint(constraints.number, locale, t);
|
|
690
|
+
case "date":
|
|
691
|
+
return generateDateHint(constraints.date, locale, t);
|
|
692
|
+
case "array":
|
|
693
|
+
return generateArrayHint(constraints.array, locale, t);
|
|
694
|
+
default:
|
|
695
|
+
return void 0;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
function generateStringHint(constraints, locale, t) {
|
|
699
|
+
if (!constraints) return void 0;
|
|
700
|
+
const { minLength, maxLength, inputType } = constraints;
|
|
701
|
+
if (inputType === "email" || inputType === "url") {
|
|
702
|
+
if (maxLength) {
|
|
703
|
+
return template(t.string_max, { n: maxLength, chars: pluralizeWord(maxLength, locale, CHAR_PLURALS) });
|
|
704
|
+
}
|
|
705
|
+
return void 0;
|
|
706
|
+
}
|
|
707
|
+
if (minLength !== void 0 && maxLength !== void 0) {
|
|
708
|
+
if (minLength === maxLength) {
|
|
709
|
+
return template(t.string_exact, { n: minLength, chars: pluralizeWord(minLength, locale, CHAR_PLURALS) });
|
|
710
|
+
}
|
|
711
|
+
return template(t.string_range, { min: minLength, max: maxLength });
|
|
712
|
+
}
|
|
713
|
+
if (maxLength !== void 0) {
|
|
714
|
+
return template(t.string_max, { n: maxLength, chars: pluralizeWord(maxLength, locale, CHAR_PLURALS) });
|
|
715
|
+
}
|
|
716
|
+
if (minLength !== void 0) {
|
|
717
|
+
return template(t.string_min, { n: minLength, chars: pluralizeWord(minLength, locale, CHAR_PLURALS) });
|
|
718
|
+
}
|
|
719
|
+
return void 0;
|
|
720
|
+
}
|
|
721
|
+
function generateNumberHint(constraints, locale, t) {
|
|
722
|
+
if (!constraints) return void 0;
|
|
723
|
+
const { min, max, isInteger } = constraints;
|
|
724
|
+
const suffix = isInteger ? t.number_integer_suffix : "";
|
|
725
|
+
if (min !== void 0 && max !== void 0) {
|
|
726
|
+
return template(t.number_range, { min: formatNumber(min, locale), max: formatNumber(max, locale), suffix });
|
|
727
|
+
}
|
|
728
|
+
if (max !== void 0) {
|
|
729
|
+
return template(t.number_max, { max: formatNumber(max, locale), suffix });
|
|
730
|
+
}
|
|
731
|
+
if (min !== void 0) {
|
|
732
|
+
return template(t.number_min, { min: formatNumber(min, locale), suffix });
|
|
733
|
+
}
|
|
734
|
+
if (isInteger) {
|
|
735
|
+
return t.number_integer;
|
|
736
|
+
}
|
|
737
|
+
return void 0;
|
|
738
|
+
}
|
|
739
|
+
function generateDateHint(constraints, locale, t) {
|
|
740
|
+
if (!constraints) return void 0;
|
|
741
|
+
const { min, max } = constraints;
|
|
742
|
+
if (min && max) {
|
|
743
|
+
return template(t.date_range, { min: formatDate(min, locale), max: formatDate(max, locale) });
|
|
744
|
+
}
|
|
745
|
+
if (min) {
|
|
746
|
+
return template(t.date_after, { min: formatDate(min, locale) });
|
|
747
|
+
}
|
|
748
|
+
if (max) {
|
|
749
|
+
return template(t.date_before, { max: formatDate(max, locale) });
|
|
750
|
+
}
|
|
751
|
+
return void 0;
|
|
752
|
+
}
|
|
753
|
+
function generateArrayHint(constraints, locale, t) {
|
|
754
|
+
if (!constraints) return void 0;
|
|
755
|
+
const { minItems, maxItems } = constraints;
|
|
756
|
+
if (minItems !== void 0 && maxItems !== void 0) {
|
|
757
|
+
if (minItems === maxItems) {
|
|
758
|
+
return template(t.array_exact, { n: minItems, items: pluralizeWord(minItems, locale, ITEM_PLURALS) });
|
|
759
|
+
}
|
|
760
|
+
return template(t.array_range, { min: minItems, max: maxItems });
|
|
761
|
+
}
|
|
762
|
+
if (maxItems !== void 0) {
|
|
763
|
+
return template(t.array_max, { n: maxItems, items: pluralizeWord(maxItems, locale, ITEM_PLURALS) });
|
|
764
|
+
}
|
|
765
|
+
if (minItems !== void 0) {
|
|
766
|
+
return template(t.array_min, { n: minItems, items: pluralizeWord(minItems, locale, ITEM_PLURALS) });
|
|
767
|
+
}
|
|
768
|
+
return void 0;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// src/lib/declarative/form-fields/base/autocomplete-map.ts
|
|
772
|
+
var AUTOCOMPLETE_MAP = {
|
|
773
|
+
// Идентификация
|
|
774
|
+
email: "email",
|
|
775
|
+
"e-mail": "email",
|
|
776
|
+
username: "username",
|
|
777
|
+
// Имя
|
|
778
|
+
name: "name",
|
|
779
|
+
fullName: "name",
|
|
780
|
+
full_name: "name",
|
|
781
|
+
firstName: "given-name",
|
|
782
|
+
first_name: "given-name",
|
|
783
|
+
lastName: "family-name",
|
|
784
|
+
last_name: "family-name",
|
|
785
|
+
surname: "family-name",
|
|
786
|
+
middleName: "additional-name",
|
|
787
|
+
middle_name: "additional-name",
|
|
788
|
+
patronymic: "additional-name",
|
|
789
|
+
// Телефон
|
|
790
|
+
phone: "tel",
|
|
791
|
+
tel: "tel",
|
|
792
|
+
mobile: "tel",
|
|
793
|
+
telephone: "tel",
|
|
794
|
+
// Пароль
|
|
795
|
+
password: "current-password",
|
|
796
|
+
newPassword: "new-password",
|
|
797
|
+
new_password: "new-password",
|
|
798
|
+
confirmPassword: "new-password",
|
|
799
|
+
confirm_password: "new-password",
|
|
800
|
+
// Адрес
|
|
801
|
+
address: "street-address",
|
|
802
|
+
street: "street-address",
|
|
803
|
+
city: "address-level2",
|
|
804
|
+
state: "address-level1",
|
|
805
|
+
region: "address-level1",
|
|
806
|
+
zip: "postal-code",
|
|
807
|
+
postal: "postal-code",
|
|
808
|
+
postalCode: "postal-code",
|
|
809
|
+
postal_code: "postal-code",
|
|
810
|
+
zipCode: "postal-code",
|
|
811
|
+
zip_code: "postal-code",
|
|
812
|
+
country: "country-name",
|
|
813
|
+
// Организация
|
|
814
|
+
company: "organization",
|
|
815
|
+
organization: "organization",
|
|
816
|
+
companyName: "organization",
|
|
817
|
+
company_name: "organization",
|
|
818
|
+
jobTitle: "organization-title",
|
|
819
|
+
job_title: "organization-title",
|
|
820
|
+
// Платёжные данные
|
|
821
|
+
cardNumber: "cc-number",
|
|
822
|
+
card_number: "cc-number",
|
|
823
|
+
cardName: "cc-name",
|
|
824
|
+
card_name: "cc-name",
|
|
825
|
+
expiry: "cc-exp",
|
|
826
|
+
cvv: "cc-csc",
|
|
827
|
+
cvc: "cc-csc"
|
|
828
|
+
};
|
|
829
|
+
function resolveAutoComplete(fieldName, metaAutocomplete, propAutocomplete) {
|
|
830
|
+
if (metaAutocomplete !== void 0) {
|
|
831
|
+
return metaAutocomplete;
|
|
832
|
+
}
|
|
833
|
+
const lastSegment = fieldName.split(".").pop() ?? fieldName;
|
|
834
|
+
const cleanName = lastSegment.replace(/\[\d+\]/g, "");
|
|
835
|
+
return AUTOCOMPLETE_MAP[cleanName];
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// src/lib/declarative/form-fields/base/use-resolved-field-props.ts
|
|
839
|
+
function useResolvedFieldProps(name, props) {
|
|
840
|
+
const {
|
|
841
|
+
form,
|
|
842
|
+
fullPath,
|
|
843
|
+
meta,
|
|
844
|
+
required: schemaRequired,
|
|
845
|
+
formDisabled,
|
|
846
|
+
formReadOnly,
|
|
847
|
+
constraints
|
|
848
|
+
} = useDeclarativeField(name);
|
|
849
|
+
const i18n = useFormI18n();
|
|
850
|
+
const i18nKey = meta?.i18nKey;
|
|
851
|
+
const resolvedTitle = getLocalizedValue(i18n, i18nKey, "title", meta?.title);
|
|
852
|
+
const resolvedPlaceholder = getLocalizedValue(i18n, i18nKey, "placeholder", meta?.placeholder);
|
|
853
|
+
const resolvedDescription = getLocalizedValue(i18n, i18nKey, "description", meta?.description);
|
|
854
|
+
const autoHint = generateConstraintHint(constraints, i18n?.locale ?? "en");
|
|
855
|
+
const helperText = props.helperText ?? resolvedDescription ?? autoHint;
|
|
856
|
+
const localizedOptions = useLocalizedOptions(meta?.options);
|
|
857
|
+
return {
|
|
858
|
+
form,
|
|
859
|
+
fullPath,
|
|
860
|
+
// Props override i18n override schema meta
|
|
861
|
+
label: props.label ?? resolvedTitle,
|
|
862
|
+
placeholder: props.placeholder ?? resolvedPlaceholder,
|
|
863
|
+
helperText,
|
|
864
|
+
// Tooltip from props or schema meta
|
|
865
|
+
tooltip: props.tooltip ?? meta?.tooltip,
|
|
866
|
+
// Required from schema or prop
|
|
867
|
+
required: props.required ?? schemaRequired,
|
|
868
|
+
// Form-level + local props (local wins)
|
|
869
|
+
disabled: props.disabled ?? formDisabled,
|
|
870
|
+
readOnly: props.readOnly ?? formReadOnly,
|
|
871
|
+
// Constraints for additional component configuration
|
|
872
|
+
constraints,
|
|
873
|
+
// Options with i18n translations
|
|
874
|
+
options: localizedOptions,
|
|
875
|
+
// HTML autocomplete (авто-определение по имени + meta override)
|
|
876
|
+
autocomplete: resolveAutoComplete(fullPath, meta?.autocomplete)
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
function createField(options) {
|
|
880
|
+
const { displayName, render } = options;
|
|
881
|
+
const useFieldState = options.useFieldState ?? (() => ({}));
|
|
882
|
+
function FieldComponent(props) {
|
|
883
|
+
const {
|
|
884
|
+
name,
|
|
885
|
+
label,
|
|
886
|
+
placeholder,
|
|
887
|
+
helperText,
|
|
888
|
+
required,
|
|
889
|
+
disabled,
|
|
890
|
+
readOnly,
|
|
891
|
+
tooltip,
|
|
892
|
+
asyncValidate,
|
|
893
|
+
asyncDebounce,
|
|
894
|
+
asyncTrigger,
|
|
895
|
+
...componentProps
|
|
896
|
+
} = props;
|
|
897
|
+
const { form, fullPath, ...resolvedRest } = useResolvedFieldProps(name, {
|
|
898
|
+
label,
|
|
899
|
+
placeholder,
|
|
900
|
+
helperText,
|
|
901
|
+
required,
|
|
902
|
+
disabled,
|
|
903
|
+
readOnly,
|
|
904
|
+
tooltip
|
|
905
|
+
});
|
|
906
|
+
const resolved = {
|
|
907
|
+
label: resolvedRest.label,
|
|
908
|
+
placeholder: resolvedRest.placeholder,
|
|
909
|
+
helperText: resolvedRest.helperText,
|
|
910
|
+
tooltip: resolvedRest.tooltip,
|
|
911
|
+
required: resolvedRest.required,
|
|
912
|
+
disabled: resolvedRest.disabled,
|
|
913
|
+
readOnly: resolvedRest.readOnly,
|
|
914
|
+
constraints: resolvedRest.constraints,
|
|
915
|
+
options: resolvedRest.options,
|
|
916
|
+
autocomplete: resolvedRest.autocomplete
|
|
917
|
+
};
|
|
918
|
+
const fieldState = useFieldState(componentProps, resolved);
|
|
919
|
+
const declarativeCtx = useDeclarativeFormOptional();
|
|
920
|
+
const asyncValidation = useAsyncFieldValidation(
|
|
921
|
+
declarativeCtx?.schema,
|
|
922
|
+
fullPath,
|
|
923
|
+
asyncValidate ? { asyncValidate, asyncDebounce, asyncTrigger } : void 0
|
|
924
|
+
);
|
|
925
|
+
return /* @__PURE__ */ jsx(FieldErrorBoundary, { fieldName: fullPath, children: /* @__PURE__ */ jsx(
|
|
926
|
+
form.Field,
|
|
927
|
+
{
|
|
928
|
+
name: fullPath,
|
|
929
|
+
...asyncValidation.validators ? { validators: asyncValidation.validators } : {},
|
|
930
|
+
...asyncValidation.asyncDebounceMs ? { asyncDebounceMs: asyncValidation.asyncDebounceMs } : {},
|
|
931
|
+
children: (field) => {
|
|
932
|
+
const errors = field.state.meta.errors;
|
|
933
|
+
const isTouched = field.state.meta.isTouched;
|
|
934
|
+
const hasError = isTouched && hasFieldErrors(errors);
|
|
935
|
+
const errorMessage = hasError ? formatFieldErrors(errors) : "";
|
|
936
|
+
const isValidating = !!field.state.meta.isValidating;
|
|
937
|
+
return render({
|
|
938
|
+
field,
|
|
939
|
+
value: field.state.value,
|
|
940
|
+
fullPath,
|
|
941
|
+
resolved,
|
|
942
|
+
hasError,
|
|
943
|
+
errorMessage,
|
|
944
|
+
isValidating,
|
|
945
|
+
fieldState,
|
|
946
|
+
componentProps
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
) });
|
|
951
|
+
}
|
|
952
|
+
FieldComponent.displayName = displayName;
|
|
953
|
+
return FieldComponent;
|
|
954
|
+
}
|
|
955
|
+
function FieldError({
|
|
956
|
+
hasError,
|
|
957
|
+
errorMessage,
|
|
958
|
+
helperText,
|
|
959
|
+
isValidating
|
|
960
|
+
}) {
|
|
961
|
+
if (isValidating) {
|
|
962
|
+
return /* @__PURE__ */ jsx(Field.HelperText, { color: "blue.500", children: "\u27F3 \u041F\u0440\u043E\u0432\u0435\u0440\u044F\u044E..." });
|
|
963
|
+
}
|
|
964
|
+
if (hasError) {
|
|
965
|
+
return /* @__PURE__ */ jsx(Field.ErrorText, { children: errorMessage });
|
|
966
|
+
}
|
|
967
|
+
if (helperText) {
|
|
968
|
+
return /* @__PURE__ */ jsx(Field.HelperText, { children: helperText });
|
|
969
|
+
}
|
|
970
|
+
return null;
|
|
971
|
+
}
|
|
972
|
+
var FieldWrapper = memo(function FieldWrapper2({
|
|
973
|
+
resolved,
|
|
974
|
+
hasError,
|
|
975
|
+
errorMessage,
|
|
976
|
+
isValidating,
|
|
977
|
+
children
|
|
978
|
+
}) {
|
|
979
|
+
return /* @__PURE__ */ jsxs(
|
|
980
|
+
Field.Root,
|
|
981
|
+
{
|
|
982
|
+
invalid: hasError,
|
|
983
|
+
required: resolved.required,
|
|
984
|
+
disabled: resolved.disabled,
|
|
985
|
+
readOnly: resolved.readOnly,
|
|
986
|
+
children: [
|
|
987
|
+
/* @__PURE__ */ jsx(FieldLabel, { label: resolved.label, tooltip: resolved.tooltip, required: resolved.required }),
|
|
988
|
+
children,
|
|
989
|
+
/* @__PURE__ */ jsx(
|
|
990
|
+
FieldError,
|
|
991
|
+
{
|
|
992
|
+
hasError,
|
|
993
|
+
errorMessage,
|
|
994
|
+
helperText: resolved.helperText,
|
|
995
|
+
isValidating
|
|
996
|
+
}
|
|
997
|
+
)
|
|
998
|
+
]
|
|
999
|
+
}
|
|
1000
|
+
);
|
|
1001
|
+
});
|
|
1002
|
+
function SelectionFieldLabel({ label, tooltip, required }) {
|
|
1003
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1004
|
+
tooltip ? /* @__PURE__ */ jsxs(HStack, { gap: 1, children: [
|
|
1005
|
+
/* @__PURE__ */ jsx("span", { children: label }),
|
|
1006
|
+
/* @__PURE__ */ jsx(FieldTooltip, { ...tooltip })
|
|
1007
|
+
] }) : label,
|
|
1008
|
+
required && /* @__PURE__ */ jsx(Field.RequiredIndicator, {})
|
|
1009
|
+
] });
|
|
1010
|
+
}
|
|
1011
|
+
function getOptionLabel(item) {
|
|
1012
|
+
return typeof item.label === "string" ? item.label : String(item.value);
|
|
1013
|
+
}
|
|
1014
|
+
function useGroupedOptions(options) {
|
|
1015
|
+
const collection = useMemo(() => {
|
|
1016
|
+
const hasGroups = options.some((opt) => opt.group);
|
|
1017
|
+
return createListCollection({
|
|
1018
|
+
items: options,
|
|
1019
|
+
itemToString: getOptionLabel,
|
|
1020
|
+
itemToValue: (item) => item.value,
|
|
1021
|
+
isItemDisabled: (item) => item.disabled ?? false,
|
|
1022
|
+
...hasGroups && {
|
|
1023
|
+
groupBy: (item) => item.group ?? ""
|
|
1024
|
+
}
|
|
1025
|
+
});
|
|
1026
|
+
}, [options]);
|
|
1027
|
+
const groups = useMemo(() => {
|
|
1028
|
+
const hasGroups = options.some((opt) => opt.group);
|
|
1029
|
+
if (!hasGroups) {
|
|
1030
|
+
return null;
|
|
1031
|
+
}
|
|
1032
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
1033
|
+
for (const opt of options) {
|
|
1034
|
+
const group = opt.group ?? "";
|
|
1035
|
+
const existing = groupMap.get(group) ?? [];
|
|
1036
|
+
groupMap.set(group, [...existing, opt]);
|
|
1037
|
+
}
|
|
1038
|
+
return groupMap;
|
|
1039
|
+
}, [options]);
|
|
1040
|
+
return { collection, groups };
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
export { DeclarativeFormContext, FieldError, FieldLabel, FieldTooltip, FieldWrapper, FormGroup, SelectionFieldLabel, createField, formatFieldErrors, getFieldErrors, getOptionLabel, getZodConstraints, hasFieldErrors, unwrapSchema, useAsyncSearch, useDebounce, useDeclarativeField, useDeclarativeForm, useDeclarativeFormOptional, useFormGroup, useGroupedOptions, useResolvedFieldProps };
|
|
1044
|
+
//# sourceMappingURL=chunk-XFWLD5EO.js.map
|
|
1045
|
+
//# sourceMappingURL=chunk-XFWLD5EO.js.map
|