@letar/forms 1.0.0 → 1.0.3

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/index.js CHANGED
@@ -1,11 +1,11 @@
1
- import { FormSyncStatus, FormOfflineIndicator, useOfflineForm } from './chunk-G3HYXHCZ.js';
2
- export { FormOfflineIndicator, FormSyncStatus, useOfflineForm, useOfflineStatus, useSyncQueue } from './chunk-G3HYXHCZ.js';
3
- import { useFormI18n, getLocalizedValue, useLocalizedOptions } from './chunk-GIBNEYK3.js';
4
- export { FormI18nProvider, getLocalizedValue, useFormI18n, useLocalizedOptions } from './chunk-GIBNEYK3.js';
1
+ import { FormSyncStatus, FormOfflineIndicator, useOfflineForm } from './chunk-4V6WBJ76.js';
2
+ export { FormOfflineIndicator, FormSyncStatus, useOfflineForm, useOfflineStatus, useSyncQueue } from './chunk-4V6WBJ76.js';
3
+ import { useFormI18n, getLocalizedValue, useLocalizedOptions } from './chunk-7FEQFDJ7.js';
4
+ export { FormI18nProvider, getLocalizedValue, useFormI18n, useLocalizedOptions } from './chunk-7FEQFDJ7.js';
5
5
  import { createFormHookContexts, createFormHook } from '@tanstack/react-form';
6
6
  import { createContext, forwardRef, memo, useMemo, useCallback, useContext, Fragment, useState, useRef, useEffect, Children, isValidElement, useSyncExternalStore, lazy, Suspense } from 'react';
7
7
  import { jsxs, jsx, Fragment as Fragment$1 } from 'react/jsx-runtime';
8
- import { Field, Stack, Box, Text, HStack, Button, Input, IconButton, Steps, ButtonGroup, VStack, Alert, List, FileUpload, Icon, parseColor, ColorPicker, Portal, PinInput, Spinner, Group, TagsInput, SegmentGroup, RadioCard, RadioGroup, Listbox, Combobox, useFilter, createListCollection, NativeSelect, Select, Switch, Fieldset, CheckboxGroup, Flex, CheckboxCard, Checkbox, NumberInput, Menu, RatingGroup, Slider, For, Editable, Progress, InputGroup, Textarea, Tooltip, Circle, useFileUploadContext, Float, Popover, Center, Image, Dialog, CloseButton, Skeleton } from '@chakra-ui/react';
8
+ import { Field, Stack, Box, Text, HStack, Button, Input, IconButton, Steps, ButtonGroup, VStack, Heading, Alert, List, FileUpload, Icon, parseColor, ColorPicker, Portal, PinInput, Spinner, Group, TagsInput, SegmentGroup, RadioCard, RadioGroup, Listbox, Combobox, useFilter, createListCollection, NativeSelect, Select, Switch, Fieldset, CheckboxGroup, Flex, CheckboxCard, Checkbox, NumberInput, Menu, RatingGroup, Slider, For, Editable, Progress, InputGroup, Textarea, Tooltip, Circle, useFileUploadContext, Float, Popover, Center, Image, Dialog, CloseButton, Skeleton } from '@chakra-ui/react';
9
9
  import { useRouter } from 'next/navigation';
10
10
  import { LuCheck, LuUpload, LuCalendar, LuChevronDown, LuEyeOff, LuEye, LuX, LuCircleHelp, LuFile, LuUnlink, LuLink, LuImage, LuRedo, LuUndo, LuQuote, LuListOrdered, LuList, LuHeading3, LuHeading2, LuHeading1, LuCode, LuStrikethrough, LuUnderline, LuItalic, LuBold } from 'react-icons/lu';
11
11
  import TiptapImage from '@tiptap/extension-image';
@@ -18,6 +18,9 @@ import { withMask } from 'use-mask-input';
18
18
  import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, closestCenter } from '@dnd-kit/core';
19
19
  import { sortableKeyboardCoordinates, SortableContext, verticalListSortingStrategy, useSortable } from '@dnd-kit/sortable';
20
20
  import { CSS } from '@dnd-kit/utilities';
21
+ import JsonView from '@uiw/react-json-view';
22
+ import { githubDarkTheme } from '@uiw/react-json-view/githubDark';
23
+ import { githubLightTheme } from '@uiw/react-json-view/githubLight';
21
24
  import { AnimatePresence, motion } from 'framer-motion';
22
25
  import { z } from 'zod/v4';
23
26
 
@@ -27,31 +30,31 @@ function useTypedFormContext() {
27
30
  return useMemo(
28
31
  () => ({
29
32
  /**
30
- * Оригинальный form API из TanStack Form.
31
- * Используйте form.store для useStore подписок.
33
+ * Original form API from TanStack Form.
34
+ * Use form.store for useStore subscriptions.
32
35
  */
33
36
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
37
  form: rawForm,
35
38
  /**
36
- * Типизированный setFieldValue.
37
- * Используйте вместо form.setFieldValue для правильной типизации.
39
+ * Typed setFieldValue.
40
+ * Use instead of form.setFieldValue for proper typing.
38
41
  */
39
42
  setFieldValue: (name, value) => {
40
43
  rawForm.setFieldValue(name, value);
41
44
  },
42
45
  /**
43
- * Типизированный селектор для values.
44
- * Используйте внутри form.Subscribe: `selector={(s) => values(s)}`
46
+ * Typed selector for values.
47
+ * Use inside form.Subscribe: `selector={(s) => values(s)}`
45
48
  */
46
49
  values: (state) => state.values,
47
50
  /**
48
- * Получить текущие значения формы (snapshot).
49
- * Внимание: это не реактивно! Для реактивного доступа используйте form.Subscribe.
51
+ * Get current form values (snapshot).
52
+ * Note: this is not reactive! For reactive access use form.Subscribe.
50
53
  */
51
54
  getValues: () => rawForm.state.values,
52
55
  /**
53
- * Подписаться на конкретное поле.
54
- * Возвращает селектор для использования в form.Subscribe.
56
+ * Subscribe to a specific field.
57
+ * Returns a selector for use in form.Subscribe.
55
58
  */
56
59
  field: (name) => (state) => state.values[name]
57
60
  }),
@@ -199,10 +202,10 @@ function useDeclarativeFormOptional() {
199
202
  }
200
203
  function DirtyGuard({
201
204
  message = "You have unsaved changes. Are you sure you want to leave?",
202
- dialogTitle = "\u041D\u0435\u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D\u043D\u044B\u0435 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F",
203
- dialogDescription = "\u0423 \u0432\u0430\u0441 \u0435\u0441\u0442\u044C \u043D\u0435\u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D\u043D\u044B\u0435 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F. \u0412\u044B \u0443\u0432\u0435\u0440\u0435\u043D\u044B, \u0447\u0442\u043E \u0445\u043E\u0442\u0438\u0442\u0435 \u043F\u043E\u043A\u0438\u043D\u0443\u0442\u044C \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0443?",
204
- confirmText = "\u041F\u043E\u043A\u0438\u043D\u0443\u0442\u044C",
205
- cancelText = "\u041E\u0441\u0442\u0430\u0442\u044C\u0441\u044F",
205
+ dialogTitle = "Unsaved changes",
206
+ dialogDescription = "You have unsaved changes. Are you sure you want to leave this page?",
207
+ confirmText = "Leave",
208
+ cancelText = "Stay",
206
209
  enabled = true,
207
210
  onBlock
208
211
  }) {
@@ -406,7 +409,7 @@ function FieldTooltip({ title, description, example, impact }) {
406
409
  title && /* @__PURE__ */ jsx(Text, { fontWeight: "semibold", fontSize: "sm", children: title }),
407
410
  /* @__PURE__ */ jsx(Text, { fontSize: "sm", children: description }),
408
411
  example && /* @__PURE__ */ jsxs(Box, { bg: "bg.emphasized", px: 2, py: 1, borderRadius: "md", w: "full", children: [
409
- /* @__PURE__ */ jsx(Text, { fontSize: "xs", color: "fg.muted", children: "\u041F\u0440\u0438\u043C\u0435\u0440:" }),
412
+ /* @__PURE__ */ jsx(Text, { fontSize: "xs", color: "fg.muted", children: "Example:" }),
410
413
  /* @__PURE__ */ jsxs(Text, { fontSize: "sm", fontStyle: "italic", children: [
411
414
  '"',
412
415
  example,
@@ -498,144 +501,176 @@ function getFieldErrors(field) {
498
501
  }
499
502
 
500
503
  // src/lib/declarative/constraint-hints.ts
501
- function generateConstraintHint(constraints) {
504
+ var EN_TRANSLATIONS = {
505
+ string_exact: "Exactly {n} {chars}",
506
+ string_range: "From {min} to {max} characters",
507
+ string_max: "Maximum {n} {chars}",
508
+ string_min: "Minimum {n} {chars}",
509
+ number_range: "From {min} to {max}{suffix}",
510
+ number_max: "Maximum {max}{suffix}",
511
+ number_min: "Minimum {min}{suffix}",
512
+ number_integer: "Integer",
513
+ number_integer_suffix: " (integer)",
514
+ date_range: "From {min} to {max}",
515
+ date_after: "Not before {min}",
516
+ date_before: "Not after {max}",
517
+ array_exact: "Exactly {n} {items}",
518
+ array_range: "From {min} to {max} items",
519
+ array_max: "Maximum {n} {items}",
520
+ array_min: "Minimum {n} {items}"
521
+ };
522
+ var RU_TRANSLATIONS = {
523
+ string_exact: "\u0420\u043E\u0432\u043D\u043E {n} {chars}",
524
+ string_range: "\u041E\u0442 {min} \u0434\u043E {max} \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432",
525
+ string_max: "\u041C\u0430\u043A\u0441\u0438\u043C\u0443\u043C {n} {chars}",
526
+ string_min: "\u041C\u0438\u043D\u0438\u043C\u0443\u043C {n} {chars}",
527
+ number_range: "\u041E\u0442 {min} \u0434\u043E {max}{suffix}",
528
+ number_max: "\u041C\u0430\u043A\u0441\u0438\u043C\u0443\u043C {max}{suffix}",
529
+ number_min: "\u041C\u0438\u043D\u0438\u043C\u0443\u043C {min}{suffix}",
530
+ number_integer: "\u0426\u0435\u043B\u043E\u0435 \u0447\u0438\u0441\u043B\u043E",
531
+ number_integer_suffix: " (\u0446\u0435\u043B\u043E\u0435)",
532
+ date_range: "\u0421 {min} \u043F\u043E {max}",
533
+ date_after: "\u041D\u0435 \u0440\u0430\u043D\u0435\u0435 {min}",
534
+ date_before: "\u041D\u0435 \u043F\u043E\u0437\u0434\u043D\u0435\u0435 {max}",
535
+ array_exact: "\u0420\u043E\u0432\u043D\u043E {n} {items}",
536
+ array_range: "\u041E\u0442 {min} \u0434\u043E {max} \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u043E\u0432",
537
+ array_max: "\u041C\u0430\u043A\u0441\u0438\u043C\u0443\u043C {n} {items}",
538
+ array_min: "\u041C\u0438\u043D\u0438\u043C\u0443\u043C {n} {items}"
539
+ };
540
+ var BUILTIN_TRANSLATIONS = {
541
+ en: EN_TRANSLATIONS,
542
+ ru: RU_TRANSLATIONS
543
+ };
544
+ var CHAR_PLURALS = {
545
+ en: { one: "character", other: "characters" },
546
+ 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" }
547
+ };
548
+ var ITEM_PLURALS = {
549
+ en: { one: "item", other: "items" },
550
+ 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" }
551
+ };
552
+ function pluralizeWord(n, locale, plurals) {
553
+ const lang = locale.split("-")[0];
554
+ const forms = plurals[lang] ?? plurals.en;
555
+ const rule = new Intl.PluralRules(locale).select(n);
556
+ return forms[rule] ?? forms.other;
557
+ }
558
+ function formatNumber(n, locale) {
559
+ if (Number.isInteger(n)) {
560
+ return new Intl.NumberFormat(locale).format(n);
561
+ }
562
+ return new Intl.NumberFormat(locale, { maximumFractionDigits: 2 }).format(n);
563
+ }
564
+ function formatDate(dateStr, locale) {
565
+ try {
566
+ const date = new Date(dateStr);
567
+ return new Intl.DateTimeFormat(locale, {
568
+ day: "numeric",
569
+ month: "long",
570
+ year: "numeric"
571
+ }).format(date);
572
+ } catch {
573
+ return dateStr;
574
+ }
575
+ }
576
+ function template(str, vars) {
577
+ return str.replace(/\{(\w+)\}/g, (_, key) => String(vars[key] ?? ""));
578
+ }
579
+ function getTranslations(locale, custom) {
580
+ const lang = locale.split("-")[0];
581
+ const base = BUILTIN_TRANSLATIONS[lang] ?? BUILTIN_TRANSLATIONS[locale] ?? EN_TRANSLATIONS;
582
+ return base;
583
+ }
584
+ function generateConstraintHint(constraints, locale = "en", customTranslations) {
502
585
  if (!constraints) {
503
586
  return void 0;
504
587
  }
588
+ const t = getTranslations(locale);
505
589
  switch (constraints.schemaType) {
506
590
  case "string":
507
- return generateStringHint(constraints.string);
591
+ return generateStringHint(constraints.string, locale, t);
508
592
  case "number":
509
- return generateNumberHint(constraints.number);
593
+ return generateNumberHint(constraints.number, locale, t);
510
594
  case "date":
511
- return generateDateHint(constraints.date);
595
+ return generateDateHint(constraints.date, locale, t);
512
596
  case "array":
513
- return generateArrayHint(constraints.array);
597
+ return generateArrayHint(constraints.array, locale, t);
514
598
  default:
515
599
  return void 0;
516
600
  }
517
601
  }
518
- function generateStringHint(constraints) {
519
- if (!constraints) {
520
- return void 0;
521
- }
602
+ function generateStringHint(constraints, locale, t) {
603
+ if (!constraints) return void 0;
522
604
  const { minLength, maxLength, inputType } = constraints;
523
- if (inputType === "email") {
605
+ if (inputType === "email" || inputType === "url") {
524
606
  if (maxLength) {
525
- return `\u041C\u0430\u043A\u0441\u0438\u043C\u0443\u043C ${maxLength} \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432`;
526
- }
527
- return void 0;
528
- }
529
- if (inputType === "url") {
530
- if (maxLength) {
531
- return `\u041C\u0430\u043A\u0441\u0438\u043C\u0443\u043C ${maxLength} \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432`;
607
+ return template(t.string_max, { n: maxLength, chars: pluralizeWord(maxLength, locale, CHAR_PLURALS) });
532
608
  }
533
609
  return void 0;
534
610
  }
535
611
  if (minLength !== void 0 && maxLength !== void 0) {
536
612
  if (minLength === maxLength) {
537
- return `\u0420\u043E\u0432\u043D\u043E ${minLength} ${pluralize(minLength, "\u0441\u0438\u043C\u0432\u043E\u043B", "\u0441\u0438\u043C\u0432\u043E\u043B\u0430", "\u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432")}`;
613
+ return template(t.string_exact, { n: minLength, chars: pluralizeWord(minLength, locale, CHAR_PLURALS) });
538
614
  }
539
- return `\u041E\u0442 ${minLength} \u0434\u043E ${maxLength} \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432`;
615
+ return template(t.string_range, { min: minLength, max: maxLength });
540
616
  }
541
617
  if (maxLength !== void 0) {
542
- return `\u041C\u0430\u043A\u0441\u0438\u043C\u0443\u043C ${maxLength} ${pluralize(maxLength, "\u0441\u0438\u043C\u0432\u043E\u043B", "\u0441\u0438\u043C\u0432\u043E\u043B\u0430", "\u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432")}`;
618
+ return template(t.string_max, { n: maxLength, chars: pluralizeWord(maxLength, locale, CHAR_PLURALS) });
543
619
  }
544
620
  if (minLength !== void 0) {
545
- return `\u041C\u0438\u043D\u0438\u043C\u0443\u043C ${minLength} ${pluralize(minLength, "\u0441\u0438\u043C\u0432\u043E\u043B", "\u0441\u0438\u043C\u0432\u043E\u043B\u0430", "\u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432")}`;
621
+ return template(t.string_min, { n: minLength, chars: pluralizeWord(minLength, locale, CHAR_PLURALS) });
546
622
  }
547
623
  return void 0;
548
624
  }
549
- function generateNumberHint(constraints) {
550
- if (!constraints) {
551
- return void 0;
552
- }
625
+ function generateNumberHint(constraints, locale, t) {
626
+ if (!constraints) return void 0;
553
627
  const { min, max, isInteger } = constraints;
628
+ const suffix = isInteger ? t.number_integer_suffix : "";
554
629
  if (min !== void 0 && max !== void 0) {
555
- const suffix = isInteger ? " (\u0446\u0435\u043B\u043E\u0435)" : "";
556
- return `\u041E\u0442 ${formatNumber(min)} \u0434\u043E ${formatNumber(max)}${suffix}`;
630
+ return template(t.number_range, { min: formatNumber(min, locale), max: formatNumber(max, locale), suffix });
557
631
  }
558
632
  if (max !== void 0) {
559
- const suffix = isInteger ? " (\u0446\u0435\u043B\u043E\u0435)" : "";
560
- return `\u041C\u0430\u043A\u0441\u0438\u043C\u0443\u043C ${formatNumber(max)}${suffix}`;
633
+ return template(t.number_max, { max: formatNumber(max, locale), suffix });
561
634
  }
562
635
  if (min !== void 0) {
563
- const suffix = isInteger ? " (\u0446\u0435\u043B\u043E\u0435)" : "";
564
- return `\u041C\u0438\u043D\u0438\u043C\u0443\u043C ${formatNumber(min)}${suffix}`;
636
+ return template(t.number_min, { min: formatNumber(min, locale), suffix });
565
637
  }
566
638
  if (isInteger) {
567
- return "\u0426\u0435\u043B\u043E\u0435 \u0447\u0438\u0441\u043B\u043E";
639
+ return t.number_integer;
568
640
  }
569
641
  return void 0;
570
642
  }
571
- function generateDateHint(constraints) {
572
- if (!constraints) {
573
- return void 0;
574
- }
643
+ function generateDateHint(constraints, locale, t) {
644
+ if (!constraints) return void 0;
575
645
  const { min, max } = constraints;
576
646
  if (min && max) {
577
- return `\u0421 ${formatDate(min)} \u043F\u043E ${formatDate(max)}`;
647
+ return template(t.date_range, { min: formatDate(min, locale), max: formatDate(max, locale) });
578
648
  }
579
649
  if (min) {
580
- return `\u041D\u0435 \u0440\u0430\u043D\u0435\u0435 ${formatDate(min)}`;
650
+ return template(t.date_after, { min: formatDate(min, locale) });
581
651
  }
582
652
  if (max) {
583
- return `\u041D\u0435 \u043F\u043E\u0437\u0434\u043D\u0435\u0435 ${formatDate(max)}`;
653
+ return template(t.date_before, { max: formatDate(max, locale) });
584
654
  }
585
655
  return void 0;
586
656
  }
587
- function generateArrayHint(constraints) {
588
- if (!constraints) {
589
- return void 0;
590
- }
657
+ function generateArrayHint(constraints, locale, t) {
658
+ if (!constraints) return void 0;
591
659
  const { minItems, maxItems } = constraints;
592
660
  if (minItems !== void 0 && maxItems !== void 0) {
593
661
  if (minItems === maxItems) {
594
- return `\u0420\u043E\u0432\u043D\u043E ${minItems} ${pluralize(minItems, "\u044D\u043B\u0435\u043C\u0435\u043D\u0442", "\u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430", "\u044D\u043B\u0435\u043C\u0435\u043D\u0442\u043E\u0432")}`;
662
+ return template(t.array_exact, { n: minItems, items: pluralizeWord(minItems, locale, ITEM_PLURALS) });
595
663
  }
596
- return `\u041E\u0442 ${minItems} \u0434\u043E ${maxItems} \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u043E\u0432`;
664
+ return template(t.array_range, { min: minItems, max: maxItems });
597
665
  }
598
666
  if (maxItems !== void 0) {
599
- return `\u041C\u0430\u043A\u0441\u0438\u043C\u0443\u043C ${maxItems} ${pluralize(maxItems, "\u044D\u043B\u0435\u043C\u0435\u043D\u0442", "\u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430", "\u044D\u043B\u0435\u043C\u0435\u043D\u0442\u043E\u0432")}`;
667
+ return template(t.array_max, { n: maxItems, items: pluralizeWord(maxItems, locale, ITEM_PLURALS) });
600
668
  }
601
669
  if (minItems !== void 0) {
602
- return `\u041C\u0438\u043D\u0438\u043C\u0443\u043C ${minItems} ${pluralize(minItems, "\u044D\u043B\u0435\u043C\u0435\u043D\u0442", "\u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430", "\u044D\u043B\u0435\u043C\u0435\u043D\u0442\u043E\u0432")}`;
670
+ return template(t.array_min, { n: minItems, items: pluralizeWord(minItems, locale, ITEM_PLURALS) });
603
671
  }
604
672
  return void 0;
605
673
  }
606
- function pluralize(n, one, few, many) {
607
- const abs = Math.abs(n);
608
- const mod10 = abs % 10;
609
- const mod100 = abs % 100;
610
- if (mod100 >= 11 && mod100 <= 19) {
611
- return many;
612
- }
613
- if (mod10 === 1) {
614
- return one;
615
- }
616
- if (mod10 >= 2 && mod10 <= 4) {
617
- return few;
618
- }
619
- return many;
620
- }
621
- function formatNumber(n) {
622
- if (Number.isInteger(n)) {
623
- return n.toLocaleString("ru-RU");
624
- }
625
- return n.toLocaleString("ru-RU", { maximumFractionDigits: 2 });
626
- }
627
- function formatDate(dateStr) {
628
- try {
629
- const date = new Date(dateStr);
630
- return date.toLocaleDateString("ru-RU", {
631
- day: "numeric",
632
- month: "long",
633
- year: "numeric"
634
- });
635
- } catch {
636
- return dateStr;
637
- }
638
- }
639
674
 
640
675
  // src/lib/declarative/zod-utils.ts
641
676
  function unwrapSchema(schema) {
@@ -928,7 +963,7 @@ function getSchemaAtPath2(schema, path) {
928
963
  current = unwrapToBaseSchema(current);
929
964
  const finalUnwrap = unwrapSchemaWithRequired(current);
930
965
  return {
931
- // Возвращаем схему ДО unwrap — на ней может быть мета (.default().meta())
966
+ // Возвращаем схему ДО unwrap — на ней can быть мета (.default().meta())
932
967
  schema: current,
933
968
  required: isRequired2 && finalUnwrap.required
934
969
  };
@@ -976,7 +1011,7 @@ function useResolvedFieldProps(name, props) {
976
1011
  const resolvedTitle = getLocalizedValue(i18n, i18nKey, "title", meta?.title);
977
1012
  const resolvedPlaceholder = getLocalizedValue(i18n, i18nKey, "placeholder", meta?.placeholder);
978
1013
  const resolvedDescription = getLocalizedValue(i18n, i18nKey, "description", meta?.description);
979
- const autoHint = generateConstraintHint(constraints);
1014
+ const autoHint = generateConstraintHint(constraints, i18n?.locale ?? "en");
980
1015
  const helperText = props.helperText ?? resolvedDescription ?? autoHint;
981
1016
  const localizedOptions = useLocalizedOptions(meta?.options);
982
1017
  return {
@@ -993,9 +1028,9 @@ function useResolvedFieldProps(name, props) {
993
1028
  // Form-level + local props (local wins)
994
1029
  disabled: props.disabled ?? formDisabled,
995
1030
  readOnly: props.readOnly ?? formReadOnly,
996
- // Constraints для дополнительной настройки компонентов
1031
+ // Constraints for additional component configuration
997
1032
  constraints,
998
- // Options с i18n переводами
1033
+ // Options with i18n translations
999
1034
  options: localizedOptions
1000
1035
  };
1001
1036
  }
@@ -1173,7 +1208,7 @@ var FieldEditable = createField({
1173
1208
  onValueChange: (details) => field.handleChange(details.value),
1174
1209
  disabled: resolved.disabled,
1175
1210
  readOnly: resolved.readOnly,
1176
- placeholder: resolved.placeholder ?? "\u041A\u043B\u0438\u043A\u043D\u0438\u0442\u0435 \u0434\u043B\u044F \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F",
1211
+ placeholder: resolved.placeholder ?? "Click to edit",
1177
1212
  activationMode,
1178
1213
  submitMode: submitOnBlur ? "blur" : "enter",
1179
1214
  children: [
@@ -1221,7 +1256,7 @@ var FieldPassword = createField({
1221
1256
  size: "sm",
1222
1257
  variant: "ghost",
1223
1258
  height: "calc(100% - {spacing.2})",
1224
- "aria-label": "\u041F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0432\u0438\u0434\u0438\u043C\u043E\u0441\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044F",
1259
+ "aria-label": "Toggle password visibility",
1225
1260
  disabled: resolved.disabled,
1226
1261
  onPointerDown: (e) => {
1227
1262
  if (resolved.disabled) {
@@ -1254,11 +1289,11 @@ var FieldPassword = createField({
1254
1289
  });
1255
1290
  var DEFAULT_REQUIREMENTS = ["minLength:8", "uppercase", "lowercase", "number", "special"];
1256
1291
  var REQUIREMENT_LABELS = {
1257
- "minLength:8": "\u041C\u0438\u043D\u0438\u043C\u0443\u043C 8 \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432",
1258
- uppercase: "\u041C\u0438\u043D\u0438\u043C\u0443\u043C \u043E\u0434\u043D\u0430 \u0437\u0430\u0433\u043B\u0430\u0432\u043D\u0430\u044F \u0431\u0443\u043A\u0432\u0430",
1259
- lowercase: "\u041C\u0438\u043D\u0438\u043C\u0443\u043C \u043E\u0434\u043D\u0430 \u0441\u0442\u0440\u043E\u0447\u043D\u0430\u044F \u0431\u0443\u043A\u0432\u0430",
1260
- number: "\u041C\u0438\u043D\u0438\u043C\u0443\u043C \u043E\u0434\u043D\u0430 \u0446\u0438\u0444\u0440\u0430",
1261
- special: "\u041C\u0438\u043D\u0438\u043C\u0443\u043C \u043E\u0434\u0438\u043D \u0441\u043F\u0435\u0446\u0438\u0430\u043B\u044C\u043D\u044B\u0439 \u0441\u0438\u043C\u0432\u043E\u043B (!@#$%^&*)"
1292
+ "minLength:8": "Minimum 8 characters",
1293
+ uppercase: "At least one uppercase letter",
1294
+ lowercase: "At least one lowercase letter",
1295
+ number: "At least one digit",
1296
+ special: "At least one special character (!@#$%^&*)"
1262
1297
  };
1263
1298
  function checkRequirement(password, requirement) {
1264
1299
  switch (requirement) {
@@ -1285,15 +1320,15 @@ function calculateStrength(password, requirements) {
1285
1320
  }
1286
1321
  function getStrengthInfo(strength) {
1287
1322
  if (strength < 25) {
1288
- return { label: "\u0421\u043B\u0430\u0431\u044B\u0439", colorPalette: "red" };
1323
+ return { label: "Weak", colorPalette: "red" };
1289
1324
  }
1290
1325
  if (strength < 50) {
1291
- return { label: "\u0421\u0440\u0435\u0434\u043D\u0438\u0439", colorPalette: "orange" };
1326
+ return { label: "Medium", colorPalette: "orange" };
1292
1327
  }
1293
1328
  if (strength < 75) {
1294
- return { label: "\u0425\u043E\u0440\u043E\u0448\u0438\u0439", colorPalette: "yellow" };
1329
+ return { label: "Good", colorPalette: "yellow" };
1295
1330
  }
1296
- return { label: "\u0421\u0438\u043B\u044C\u043D\u044B\u0439", colorPalette: "green" };
1331
+ return { label: "Strong", colorPalette: "green" };
1297
1332
  }
1298
1333
  var FieldPasswordStrength = createField({
1299
1334
  displayName: "FieldPasswordStrength",
@@ -1325,7 +1360,7 @@ var FieldPasswordStrength = createField({
1325
1360
  value,
1326
1361
  onChange: (e) => field.handleChange(e.target.value),
1327
1362
  onBlur: field.handleBlur,
1328
- placeholder: resolved.placeholder ?? "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043F\u0430\u0440\u043E\u043B\u044C",
1363
+ placeholder: resolved.placeholder ?? "Enter password",
1329
1364
  "data-field-name": fullPath,
1330
1365
  flex: 1
1331
1366
  }
@@ -1333,7 +1368,7 @@ var FieldPasswordStrength = createField({
1333
1368
  /* @__PURE__ */ jsx(
1334
1369
  IconButton,
1335
1370
  {
1336
- "aria-label": visible ? "\u0421\u043A\u0440\u044B\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044C" : "\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044C",
1371
+ "aria-label": visible ? "Hide password" : "Show password",
1337
1372
  onClick: toggle,
1338
1373
  variant: "ghost",
1339
1374
  size: "sm",
@@ -1343,7 +1378,7 @@ var FieldPasswordStrength = createField({
1343
1378
  ] }),
1344
1379
  value && /* @__PURE__ */ jsxs(Box, { children: [
1345
1380
  /* @__PURE__ */ jsxs(HStack, { justify: "space-between", mb: 1, children: [
1346
- /* @__PURE__ */ jsx(Text, { fontSize: "xs", color: "fg.muted", children: "\u0421\u0438\u043B\u0430" }),
1381
+ /* @__PURE__ */ jsx(Text, { fontSize: "xs", color: "fg.muted", children: "Strength" }),
1347
1382
  /* @__PURE__ */ jsx(Text, { fontSize: "xs", fontWeight: "medium", color: `${colorPalette}.600`, children: strengthLabel })
1348
1383
  ] }),
1349
1384
  /* @__PURE__ */ jsx(Progress.Root, { value: strength, colorPalette, size: "xs", children: /* @__PURE__ */ jsx(Progress.Track, { children: /* @__PURE__ */ jsx(Progress.Range, {}) }) })
@@ -1374,13 +1409,13 @@ function ImagePopover({ editor, config, disabled }) {
1374
1409
  const handleUpload = useCallback(
1375
1410
  async (file) => {
1376
1411
  if (!file.type.startsWith("image/")) {
1377
- setErrorMessage("\u0424\u0430\u0439\u043B \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435\u043C");
1412
+ setErrorMessage("File must be an image");
1378
1413
  setUploadState("error");
1379
1414
  return;
1380
1415
  }
1381
1416
  if (file.size > maxSize) {
1382
1417
  const maxSizeMB = (maxSize / 1024 / 1024).toFixed(0);
1383
- setErrorMessage(`\u0420\u0430\u0437\u043C\u0435\u0440 \u0444\u0430\u0439\u043B\u0430 \u043D\u0435 \u0434\u043E\u043B\u0436\u0435\u043D \u043F\u0440\u0435\u0432\u044B\u0448\u0430\u0442\u044C ${maxSizeMB}MB`);
1418
+ setErrorMessage(`Size file\u0430 \u043D\u0435 must \u043F\u0440\u0435\u0432\u044B\u0448\u0430\u0442\u044C ${maxSizeMB}MB`);
1384
1419
  setUploadState("error");
1385
1420
  return;
1386
1421
  }
@@ -1400,17 +1435,17 @@ function ImagePopover({ editor, config, disabled }) {
1400
1435
  });
1401
1436
  const result = await response.json();
1402
1437
  if (!response.ok) {
1403
- throw new Error(result.error || "\u041E\u0448\u0438\u0431\u043A\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438");
1438
+ throw new Error(result.error || "Upload error");
1404
1439
  }
1405
1440
  if (result.url) {
1406
1441
  ;
1407
1442
  editor.chain().focus().setImage({ src: result.url }).run();
1408
1443
  handleClose();
1409
1444
  } else {
1410
- throw new Error("URL \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F \u043D\u0435 \u043F\u043E\u043B\u0443\u0447\u0435\u043D");
1445
+ throw new Error("Image URL not received");
1411
1446
  }
1412
1447
  } catch (err) {
1413
- setErrorMessage(err instanceof Error ? err.message : "\u041E\u0448\u0438\u0431\u043A\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438");
1448
+ setErrorMessage(err instanceof Error ? err.message : "Upload error");
1414
1449
  setUploadState("error");
1415
1450
  } finally {
1416
1451
  if (preview) {
@@ -1457,7 +1492,7 @@ function ImagePopover({ editor, config, disabled }) {
1457
1492
  /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
1458
1493
  IconButton,
1459
1494
  {
1460
- "aria-label": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435",
1495
+ "aria-label": "Insert image",
1461
1496
  size: "sm",
1462
1497
  variant: "ghost",
1463
1498
  onClick: () => setIsOpen(true),
@@ -1493,13 +1528,13 @@ function ImagePopover({ editor, config, disabled }) {
1493
1528
  onClick: () => fileInputRef.current?.click(),
1494
1529
  children: /* @__PURE__ */ jsx(Center, { children: /* @__PURE__ */ jsxs(VStack, { gap: 2, children: [
1495
1530
  /* @__PURE__ */ jsx(Icon, { fontSize: "2xl", color: "fg.muted", children: /* @__PURE__ */ jsx(LuUpload, {}) }),
1496
- /* @__PURE__ */ jsx(Text, { fontSize: "sm", fontWeight: "medium", textAlign: "center", children: "\u041F\u0435\u0440\u0435\u0442\u0430\u0449\u0438\u0442\u0435 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435 \u0441\u044E\u0434\u0430" }),
1497
- /* @__PURE__ */ jsx(Text, { fontSize: "xs", color: "fg.muted", children: "\u0438\u043B\u0438 \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u0434\u043B\u044F \u0432\u044B\u0431\u043E\u0440\u0430" })
1531
+ /* @__PURE__ */ jsx(Text, { fontSize: "sm", fontWeight: "medium", textAlign: "center", children: "Drag image here" }),
1532
+ /* @__PURE__ */ jsx(Text, { fontSize: "xs", color: "fg.muted", children: "or click to select" })
1498
1533
  ] }) })
1499
1534
  }
1500
1535
  ),
1501
1536
  /* @__PURE__ */ jsxs(Text, { fontSize: "xs", color: "fg.muted", textAlign: "center", children: [
1502
- "PNG, JPG, WEBP \u0434\u043E ",
1537
+ "PNG, JPG, WEBP up to ",
1503
1538
  (maxSize / 1024 / 1024).toFixed(0),
1504
1539
  "MB"
1505
1540
  ] }),
@@ -1513,24 +1548,24 @@ function ImagePopover({ editor, config, disabled }) {
1513
1548
  style: { display: "none" }
1514
1549
  }
1515
1550
  ),
1516
- /* @__PURE__ */ jsx(HStack, { justify: "flex-end", children: /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: handleClose, children: "\u041E\u0442\u043C\u0435\u043D\u0430" }) })
1551
+ /* @__PURE__ */ jsx(HStack, { justify: "flex-end", children: /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: handleClose, children: "Cancel" }) })
1517
1552
  ] }),
1518
1553
  uploadState === "uploading" && /* @__PURE__ */ jsxs(VStack, { gap: 3, align: "stretch", children: [
1519
- previewUrl && /* @__PURE__ */ jsx(Box, { borderRadius: "md", overflow: "hidden", bg: "bg.subtle", children: /* @__PURE__ */ jsx(Image, { src: previewUrl, alt: "\u041F\u0440\u0435\u0432\u044C\u044E", maxH: "150px", w: "100%", objectFit: "contain" }) }),
1554
+ previewUrl && /* @__PURE__ */ jsx(Box, { borderRadius: "md", overflow: "hidden", bg: "bg.subtle", children: /* @__PURE__ */ jsx(Image, { src: previewUrl, alt: "Preview", maxH: "150px", w: "100%", objectFit: "contain" }) }),
1520
1555
  /* @__PURE__ */ jsx(Center, { py: 2, children: /* @__PURE__ */ jsxs(HStack, { gap: 2, children: [
1521
1556
  /* @__PURE__ */ jsx(Spinner, { size: "sm", color: "colorPalette.500" }),
1522
- /* @__PURE__ */ jsx(Text, { fontSize: "sm", color: "fg.muted", children: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430..." })
1557
+ /* @__PURE__ */ jsx(Text, { fontSize: "sm", color: "fg.muted", children: "Loading..." })
1523
1558
  ] }) })
1524
1559
  ] }),
1525
1560
  uploadState === "error" && /* @__PURE__ */ jsxs(VStack, { gap: 3, align: "stretch", children: [
1526
1561
  previewUrl && /* @__PURE__ */ jsxs(Box, { borderRadius: "md", overflow: "hidden", bg: "bg.subtle", position: "relative", children: [
1527
- /* @__PURE__ */ jsx(Image, { src: previewUrl, alt: "\u041F\u0440\u0435\u0432\u044C\u044E", maxH: "150px", w: "100%", objectFit: "contain", opacity: 0.5 }),
1562
+ /* @__PURE__ */ jsx(Image, { src: previewUrl, alt: "Preview", maxH: "150px", w: "100%", objectFit: "contain", opacity: 0.5 }),
1528
1563
  /* @__PURE__ */ jsx(Center, { position: "absolute", inset: 0, bg: "blackAlpha.500", borderRadius: "md", children: /* @__PURE__ */ jsx(Icon, { color: "red.400", fontSize: "2xl", children: /* @__PURE__ */ jsx(LuX, {}) }) })
1529
1564
  ] }),
1530
1565
  /* @__PURE__ */ jsx(Text, { fontSize: "sm", color: "red.400", textAlign: "center", children: errorMessage }),
1531
1566
  /* @__PURE__ */ jsxs(HStack, { justify: "center", gap: 2, children: [
1532
- /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: handleClose, children: "\u041E\u0442\u043C\u0435\u043D\u0430" }),
1533
- /* @__PURE__ */ jsx(Button, { size: "sm", colorPalette: "brand", onClick: handleRetry, children: "\u041F\u043E\u043F\u0440\u043E\u0431\u043E\u0432\u0430\u0442\u044C \u0441\u043D\u043E\u0432\u0430" })
1567
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: handleClose, children: "Cancel" }),
1568
+ /* @__PURE__ */ jsx(Button, { size: "sm", colorPalette: "brand", onClick: handleRetry, children: "Try again" })
1534
1569
  ] })
1535
1570
  ] })
1536
1571
  ] })
@@ -1579,7 +1614,7 @@ function LinkPopover({ editor, disabled }) {
1579
1614
  /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
1580
1615
  IconButton,
1581
1616
  {
1582
- "aria-label": isActive ? "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0441\u044B\u043B\u043A\u0443" : "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0441\u0441\u044B\u043B\u043A\u0443",
1617
+ "aria-label": isActive ? "Remove \u0441\u0441\u044B\u043B\u043A\u0443" : "Add \u0441\u0441\u044B\u043B\u043A\u0443",
1583
1618
  size: "sm",
1584
1619
  variant: isActive ? "solid" : "ghost",
1585
1620
  colorPalette: isActive ? "brand" : void 0,
@@ -1603,9 +1638,9 @@ function LinkPopover({ editor, disabled }) {
1603
1638
  }
1604
1639
  ) }),
1605
1640
  /* @__PURE__ */ jsxs(HStack, { gap: 2, justify: "flex-end", children: [
1606
- editor.isActive("link") && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", colorPalette: "red", onClick: handleRemove, children: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C" }),
1607
- /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: handleClose, children: "\u041E\u0442\u043C\u0435\u043D\u0430" }),
1608
- /* @__PURE__ */ jsx(Button, { size: "sm", colorPalette: "brand", onClick: handleSubmit, disabled: !url.trim(), children: "\u041F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C" })
1641
+ editor.isActive("link") && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", colorPalette: "red", onClick: handleRemove, children: "Remove" }),
1642
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: handleClose, children: "Cancel" }),
1643
+ /* @__PURE__ */ jsx(Button, { size: "sm", colorPalette: "brand", onClick: handleSubmit, disabled: !url.trim(), children: "Apply" })
1609
1644
  ] })
1610
1645
  ] }) })
1611
1646
  ] }) }) })
@@ -1630,13 +1665,13 @@ var DEFAULT_TOOLBAR_BUTTONS = [
1630
1665
  var TOOLBAR_CONFIG = {
1631
1666
  bold: {
1632
1667
  icon: /* @__PURE__ */ jsx(LuBold, {}),
1633
- label: "\u0416\u0438\u0440\u043D\u044B\u0439",
1668
+ label: "Bold",
1634
1669
  action: (editor) => editor?.chain().focus().toggleBold().run(),
1635
1670
  isActive: (editor) => editor?.isActive("bold") ?? false
1636
1671
  },
1637
1672
  italic: {
1638
1673
  icon: /* @__PURE__ */ jsx(LuItalic, {}),
1639
- label: "\u041A\u0443\u0440\u0441\u0438\u0432",
1674
+ label: "Italic",
1640
1675
  action: (editor) => editor?.chain().focus().toggleItalic().run(),
1641
1676
  isActive: (editor) => editor?.isActive("italic") ?? false
1642
1677
  },
@@ -1648,7 +1683,7 @@ var TOOLBAR_CONFIG = {
1648
1683
  },
1649
1684
  strike: {
1650
1685
  icon: /* @__PURE__ */ jsx(LuStrikethrough, {}),
1651
- label: "\u0417\u0430\u0447\u0451\u0440\u043A\u043D\u0443\u0442\u044B\u0439",
1686
+ label: "Strikethrough",
1652
1687
  action: (editor) => editor?.chain().focus().toggleStrike().run(),
1653
1688
  isActive: (editor) => editor?.isActive("strike") ?? false
1654
1689
  },
@@ -1678,19 +1713,19 @@ var TOOLBAR_CONFIG = {
1678
1713
  },
1679
1714
  bulletList: {
1680
1715
  icon: /* @__PURE__ */ jsx(LuList, {}),
1681
- label: "\u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0441\u043F\u0438\u0441\u043E\u043A",
1716
+ label: "Bullet list",
1682
1717
  action: (editor) => editor?.chain().focus().toggleBulletList().run(),
1683
1718
  isActive: (editor) => editor?.isActive("bulletList") ?? false
1684
1719
  },
1685
1720
  orderedList: {
1686
1721
  icon: /* @__PURE__ */ jsx(LuListOrdered, {}),
1687
- label: "\u041D\u0443\u043C\u0435\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0441\u043F\u0438\u0441\u043E\u043A",
1722
+ label: "Ordered list",
1688
1723
  action: (editor) => editor?.chain().focus().toggleOrderedList().run(),
1689
1724
  isActive: (editor) => editor?.isActive("orderedList") ?? false
1690
1725
  },
1691
1726
  blockquote: {
1692
1727
  icon: /* @__PURE__ */ jsx(LuQuote, {}),
1693
- label: "\u0426\u0438\u0442\u0430\u0442\u0430",
1728
+ label: "Quote",
1694
1729
  action: (editor) => editor?.chain().focus().toggleBlockquote().run(),
1695
1730
  isActive: (editor) => editor?.isActive("blockquote") ?? false
1696
1731
  },
@@ -1711,18 +1746,18 @@ var TOOLBAR_CONFIG = {
1711
1746
  },
1712
1747
  undo: {
1713
1748
  icon: /* @__PURE__ */ jsx(LuUndo, {}),
1714
- label: "\u041E\u0442\u043C\u0435\u043D\u0438\u0442\u044C",
1749
+ label: "Undo",
1715
1750
  action: (editor) => editor?.chain().focus().undo().run()
1716
1751
  },
1717
1752
  redo: {
1718
1753
  icon: /* @__PURE__ */ jsx(LuRedo, {}),
1719
- label: "\u041F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C",
1754
+ label: "Redo",
1720
1755
  action: (editor) => editor?.chain().focus().redo().run()
1721
1756
  },
1722
- // Кнопка image обрабатывается отдельно через ImagePopover (аналогично link)
1757
+ // Кнопка image processesся отдельно через ImagePopover (аналогично link)
1723
1758
  image: {
1724
1759
  icon: /* @__PURE__ */ jsx(LuImage, {}),
1725
- label: "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435",
1760
+ label: "Insert image",
1726
1761
  action: () => {
1727
1762
  }
1728
1763
  }
@@ -1826,9 +1861,9 @@ function RichTextEditor({
1826
1861
  }
1827
1862
  }),
1828
1863
  Placeholder.configure({
1829
- placeholder: placeholder ?? "\u041D\u0430\u0447\u043D\u0438\u0442\u0435 \u0432\u0432\u043E\u0434\u0438\u0442\u044C..."
1864
+ placeholder: placeholder ?? "Start typing..."
1830
1865
  }),
1831
- // Добавляем Image extension только если imageUpload настроен
1866
+ // Add Image extension only if imageUpload is configured
1832
1867
  ...imageUpload ? [
1833
1868
  TiptapImage.configure({
1834
1869
  inline: false,
@@ -1843,7 +1878,7 @@ function RichTextEditor({
1843
1878
  }, [placeholder, imageUpload]);
1844
1879
  const editor = useEditor({
1845
1880
  // Cast needed: minor @tiptap/core version drift (e.g. 3.20.0 vs 3.20.1) causes nominal type mismatch
1846
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- несовместимость версий @tiptap/core
1881
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- @tiptap/core version incompatibility
1847
1882
  extensions,
1848
1883
  content: outputFormat === "json" && value ? safeParseJSON(value) : value || "",
1849
1884
  editable: !disabled && !readOnly,
@@ -2421,27 +2456,27 @@ function getPresetRange(preset) {
2421
2456
  function getPresetLabel(preset) {
2422
2457
  switch (preset) {
2423
2458
  case "today":
2424
- return "\u0421\u0435\u0433\u043E\u0434\u043D\u044F";
2459
+ return "Today";
2425
2460
  case "yesterday":
2426
- return "\u0412\u0447\u0435\u0440\u0430";
2461
+ return "Yesterday";
2427
2462
  case "thisWeek":
2428
- return "\u042D\u0442\u0430 \u043D\u0435\u0434\u0435\u043B\u044F";
2463
+ return "This week";
2429
2464
  case "lastWeek":
2430
- return "\u041F\u0440\u043E\u0448\u043B\u0430\u044F \u043D\u0435\u0434\u0435\u043B\u044F";
2465
+ return "Last week";
2431
2466
  case "thisMonth":
2432
- return "\u042D\u0442\u043E\u0442 \u043C\u0435\u0441\u044F\u0446";
2467
+ return "This month";
2433
2468
  case "lastMonth":
2434
- return "\u041F\u0440\u043E\u0448\u043B\u044B\u0439 \u043C\u0435\u0441\u044F\u0446";
2469
+ return "Last month";
2435
2470
  case "thisYear":
2436
- return "\u042D\u0442\u043E\u0442 \u0433\u043E\u0434";
2471
+ return "This year";
2437
2472
  }
2438
2473
  }
2439
2474
  var FieldDateRange = createField({
2440
2475
  displayName: "FieldDateRange",
2441
2476
  render: ({ field, fullPath, resolved, hasError, errorMessage, componentProps }) => {
2442
2477
  const {
2443
- startLabel = "\u041D\u0430\u0447\u0430\u043B\u043E",
2444
- endLabel = "\u041A\u043E\u043D\u0435\u0446",
2478
+ startLabel = "Start",
2479
+ endLabel = "End",
2445
2480
  startPlaceholder,
2446
2481
  endPlaceholder,
2447
2482
  min,
@@ -2510,7 +2545,7 @@ var FieldDateRange = createField({
2510
2545
  presets && presets.length > 0 && !resolved.readOnly && /* @__PURE__ */ jsxs(Menu.Root, { children: [
2511
2546
  /* @__PURE__ */ jsx(Menu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size, disabled: resolved.disabled, children: [
2512
2547
  /* @__PURE__ */ jsx(LuCalendar, {}),
2513
- "\u041F\u0440\u0435\u0441\u0435\u0442\u044B",
2548
+ "Presets",
2514
2549
  /* @__PURE__ */ jsx(LuChevronDown, {})
2515
2550
  ] }) }),
2516
2551
  /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Menu.Positioner, { children: /* @__PURE__ */ jsx(Menu.Content, { children: presets.map((preset) => /* @__PURE__ */ jsx(Menu.Item, { value: preset, onClick: () => handlePreset(preset), children: getPresetLabel(preset) }, preset)) }) }) })
@@ -2659,7 +2694,7 @@ var FieldDuration = createField({
2659
2694
  /* @__PURE__ */ jsx(NumberInput.IncrementTrigger, {}),
2660
2695
  /* @__PURE__ */ jsx(NumberInput.DecrementTrigger, {})
2661
2696
  ] }),
2662
- /* @__PURE__ */ jsx(NumberInput.Input, { placeholder: resolved.placeholder ?? "\u043C\u0438\u043D", "data-field-name": fullPath })
2697
+ /* @__PURE__ */ jsx(NumberInput.Input, { placeholder: resolved.placeholder ?? "min", "data-field-name": fullPath })
2663
2698
  ]
2664
2699
  }
2665
2700
  ),
@@ -2735,13 +2770,13 @@ var FieldDuration = createField({
2735
2770
  });
2736
2771
  var DAYS_OF_WEEK = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];
2737
2772
  var DEFAULT_DAY_NAMES = {
2738
- monday: "\u041F\u043E\u043D\u0435\u0434\u0435\u043B\u044C\u043D\u0438\u043A",
2739
- tuesday: "\u0412\u0442\u043E\u0440\u043D\u0438\u043A",
2740
- wednesday: "\u0421\u0440\u0435\u0434\u0430",
2741
- thursday: "\u0427\u0435\u0442\u0432\u0435\u0440\u0433",
2742
- friday: "\u041F\u044F\u0442\u043D\u0438\u0446\u0430",
2743
- saturday: "\u0421\u0443\u0431\u0431\u043E\u0442\u0430",
2744
- sunday: "\u0412\u043E\u0441\u043A\u0440\u0435\u0441\u0435\u043D\u044C\u0435"
2773
+ monday: "Monday",
2774
+ tuesday: "Tuesday",
2775
+ wednesday: "Wednesday",
2776
+ thursday: "Thursday",
2777
+ friday: "Friday",
2778
+ saturday: "Saturday",
2779
+ sunday: "Sunday"
2745
2780
  };
2746
2781
  var DEFAULT_WORKING_HOURS = {
2747
2782
  monday: { open: "09:00", close: "18:00" },
@@ -2753,15 +2788,15 @@ var DEFAULT_WORKING_HOURS = {
2753
2788
  sunday: null
2754
2789
  };
2755
2790
  var SWITCH_STYLES = {
2756
- /** Ширина трека switch */
2791
+ /** Switch track width */
2757
2792
  trackWidth: "36px",
2758
- /** Высота трека switch */
2793
+ /** Switch track height */
2759
2794
  trackHeight: "20px",
2760
- /** Размер круглого индикатора (thumb) */
2795
+ /** Round indicator (thumb) size */
2761
2796
  thumbSize: "16px",
2762
- /** Отступ thumb от края (2px с каждой стороны для центрирования в 20px треке) */
2797
+ /** Thumb offset from edge (2px each side for centering in 20px track) */
2763
2798
  thumbOffset: "2px",
2764
- /** Позиция thumb во включённом состоянии (trackWidth - thumbSize - thumbOffset = 36 - 16 - 2 = 18) */
2799
+ /** Thumb position in enabled state (trackWidth - thumbSize - thumbOffset = 36 - 16 - 2 = 18) */
2765
2800
  thumbEnabledLeft: "18px"
2766
2801
  };
2767
2802
  function isValidTimeRange(open, close) {
@@ -2851,11 +2886,11 @@ var ScheduleContent = memo(function ScheduleContent2({
2851
2886
  /* @__PURE__ */ jsx(FieldLabel, { label: resolvedLabel, tooltip: resolvedTooltip, required: resolvedRequired }),
2852
2887
  /* @__PURE__ */ jsxs(Stack, { gap: 3, children: [
2853
2888
  invalidDays.length > 0 && /* @__PURE__ */ jsx(Box, { p: 3, bg: "red.50", borderWidth: "1px", borderColor: "red.200", borderRadius: "md", children: /* @__PURE__ */ jsxs(Text, { color: "red.600", fontSize: "sm", fontWeight: "medium", children: [
2854
- "\u0412\u0440\u0435\u043C\u044F \u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u044F \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043F\u043E\u0437\u0436\u0435 \u0432\u0440\u0435\u043C\u0435\u043D\u0438 \u043D\u0430\u0447\u0430\u043B\u0430: ",
2889
+ "End time must be after start time: ",
2855
2890
  invalidDays.map((d) => mergedDayNames[d]).join(", ")
2856
2891
  ] }) }),
2857
2892
  showCopyToWeekdays && days.includes("monday") && /* @__PURE__ */ jsxs(HStack, { gap: 2, flexWrap: "wrap", children: [
2858
- /* @__PURE__ */ jsx(Text, { fontSize: "sm", color: "fg.muted", children: "\u0411\u044B\u0441\u0442\u0440\u044B\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F:" }),
2893
+ /* @__PURE__ */ jsx(Text, { fontSize: "sm", color: "fg.muted", children: "Quick actions:" }),
2859
2894
  /* @__PURE__ */ jsx(
2860
2895
  Button,
2861
2896
  {
@@ -2988,8 +3023,8 @@ function FieldSchedule({
2988
3023
  defaultSchedule = DEFAULT_WORKING_HOURS,
2989
3024
  days = DAYS_OF_WEEK,
2990
3025
  showCopyToWeekdays = true,
2991
- offLabel = "\u0412\u044B\u0445\u043E\u0434\u043D\u043E\u0439",
2992
- copyToWeekdaysLabel = "\u0421\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u041F\u043D \u043D\u0430 \u0431\u0443\u0434\u043D\u0438",
3026
+ offLabel = "Day off",
3027
+ copyToWeekdaysLabel = "Copy Mon to weekdays",
2993
3028
  defaultOpenTime = "09:00",
2994
3029
  defaultCloseTime = "18:00"
2995
3030
  }) {
@@ -3196,19 +3231,19 @@ var FieldAutocomplete = createField({
3196
3231
  children: [
3197
3232
  resolved.label && /* @__PURE__ */ jsx(Combobox.Label, { children: /* @__PURE__ */ jsx(SelectionFieldLabel, { label: resolved.label, tooltip: resolved.tooltip, required: resolved.required }) }),
3198
3233
  /* @__PURE__ */ jsxs(Combobox.Control, { children: [
3199
- /* @__PURE__ */ jsx(Combobox.Input, { placeholder: resolved.placeholder ?? "\u041D\u0430\u0447\u043D\u0438\u0442\u0435 \u0432\u0432\u043E\u0434..." }),
3234
+ /* @__PURE__ */ jsx(Combobox.Input, { placeholder: resolved.placeholder ?? "Start typing..." }),
3200
3235
  /* @__PURE__ */ jsxs(Combobox.IndicatorGroup, { children: [
3201
3236
  fieldState.isLoading && /* @__PURE__ */ jsx(Spinner, { size: "xs" }),
3202
3237
  /* @__PURE__ */ jsx(Combobox.Trigger, {})
3203
3238
  ] })
3204
3239
  ] }),
3205
3240
  /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Combobox.Positioner, { children: /* @__PURE__ */ jsxs(Combobox.Content, { children: [
3206
- fieldState.isLoading && fieldState.suggestions.length === 0 && /* @__PURE__ */ jsx(Combobox.Empty, { children: componentProps.loadingMessage ?? "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430..." }),
3207
- !fieldState.isLoading && fieldState.suggestions.length === 0 && fieldState.inputValue.length >= minChars && /* @__PURE__ */ jsx(Combobox.Empty, { children: componentProps.emptyMessage ?? "\u041D\u0435\u0442 \u043F\u043E\u0434\u0441\u043A\u0430\u0437\u043E\u043A" }),
3241
+ fieldState.isLoading && fieldState.suggestions.length === 0 && /* @__PURE__ */ jsx(Combobox.Empty, { children: componentProps.loadingMessage ?? "Loading..." }),
3242
+ !fieldState.isLoading && fieldState.suggestions.length === 0 && fieldState.inputValue.length >= minChars && /* @__PURE__ */ jsx(Combobox.Empty, { children: componentProps.emptyMessage ?? "No suggestions" }),
3208
3243
  !fieldState.isLoading && fieldState.suggestions.length === 0 && fieldState.inputValue.length < minChars && fieldState.inputValue.length > 0 && /* @__PURE__ */ jsxs(Combobox.Empty, { children: [
3209
- "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043C\u0438\u043D\u0438\u043C\u0443\u043C ",
3244
+ "Enter at least ",
3210
3245
  minChars,
3211
- " \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432"
3246
+ " characters"
3212
3247
  ] }),
3213
3248
  fieldState.suggestions.map((item) => /* @__PURE__ */ jsxs(Combobox.Item, { item, children: [
3214
3249
  /* @__PURE__ */ jsx(Combobox.ItemText, { children: item.label }),
@@ -3358,7 +3393,7 @@ var FieldCombobox = createField({
3358
3393
  children: [
3359
3394
  resolved.label && /* @__PURE__ */ jsx(Combobox.Label, { children: /* @__PURE__ */ jsx(SelectionFieldLabel, { label: resolved.label, tooltip: resolved.tooltip, required: resolved.required }) }),
3360
3395
  /* @__PURE__ */ jsxs(Combobox.Control, { children: [
3361
- /* @__PURE__ */ jsx(Combobox.Input, { placeholder: resolved.placeholder ?? "\u041F\u043E\u0438\u0441\u043A..." }),
3396
+ /* @__PURE__ */ jsx(Combobox.Input, { placeholder: resolved.placeholder ?? "Search..." }),
3362
3397
  /* @__PURE__ */ jsxs(Combobox.IndicatorGroup, { children: [
3363
3398
  fieldState.isLoading && /* @__PURE__ */ jsx(Spinner, { size: "xs" }),
3364
3399
  fieldState.resolvedClearable && /* @__PURE__ */ jsx(Combobox.ClearTrigger, {}),
@@ -3366,12 +3401,12 @@ var FieldCombobox = createField({
3366
3401
  ] })
3367
3402
  ] }),
3368
3403
  /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Combobox.Positioner, { children: /* @__PURE__ */ jsxs(Combobox.Content, { children: [
3369
- fieldState.isLoading && fieldState.options.length === 0 && /* @__PURE__ */ jsx(Combobox.Empty, { children: componentProps.loadingMessage ?? "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430..." }),
3370
- !fieldState.isLoading && fieldState.options.length === 0 && fieldState.inputValue.length >= minChars && /* @__PURE__ */ jsx(Combobox.Empty, { children: componentProps.emptyMessage ?? "\u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E" }),
3404
+ fieldState.isLoading && fieldState.options.length === 0 && /* @__PURE__ */ jsx(Combobox.Empty, { children: componentProps.loadingMessage ?? "Loading..." }),
3405
+ !fieldState.isLoading && fieldState.options.length === 0 && fieldState.inputValue.length >= minChars && /* @__PURE__ */ jsx(Combobox.Empty, { children: componentProps.emptyMessage ?? "Nothing found" }),
3371
3406
  !fieldState.isLoading && fieldState.options.length === 0 && fieldState.inputValue.length < minChars && fieldState.inputValue.length > 0 && /* @__PURE__ */ jsxs(Combobox.Empty, { children: [
3372
- "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043C\u0438\u043D\u0438\u043C\u0443\u043C ",
3407
+ "Enter at least ",
3373
3408
  minChars,
3374
- " \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432"
3409
+ " characters"
3375
3410
  ] }),
3376
3411
  fieldState.groups ? Array.from(fieldState.groups.entries()).map(([groupName, groupOptions]) => /* @__PURE__ */ jsxs(Combobox.ItemGroup, { children: [
3377
3412
  groupName && /* @__PURE__ */ jsx(Combobox.ItemGroupLabel, { children: groupName }),
@@ -3380,7 +3415,7 @@ var FieldCombobox = createField({
3380
3415
  /* @__PURE__ */ jsx(Combobox.ItemIndicator, {})
3381
3416
  ] }, opt.value))
3382
3417
  ] }, groupName)) : (
3383
- /* Плоские опции */
3418
+ /* Flat options */
3384
3419
  fieldState.options.map((opt) => /* @__PURE__ */ jsxs(Combobox.Item, { item: opt, children: [
3385
3420
  /* @__PURE__ */ jsx(Combobox.ItemText, { children: getOptionLabel(opt) }),
3386
3421
  /* @__PURE__ */ jsx(Combobox.ItemIndicator, {})
@@ -3433,7 +3468,7 @@ var FieldListbox = createField({
3433
3468
  children: [
3434
3469
  resolved.label && /* @__PURE__ */ jsx(Listbox.Label, { fontSize: componentProps.size ?? "md", children: /* @__PURE__ */ jsx(SelectionFieldLabel, { label: resolved.label, tooltip: resolved.tooltip, required: resolved.required }) }),
3435
3470
  /* @__PURE__ */ jsx(Listbox.Content, { maxH: componentProps.maxHeight, children: fieldState.groups ? (
3436
- /* Сгруппированные опции */
3471
+ /* Grouped options */
3437
3472
  Array.from(fieldState.groups.entries()).map(([groupName, groupOptions]) => /* @__PURE__ */ jsxs(Listbox.ItemGroup, { children: [
3438
3473
  groupName && /* @__PURE__ */ jsx(Listbox.ItemGroupLabel, { children: groupName }),
3439
3474
  groupOptions.map((opt) => /* @__PURE__ */ jsxs(Listbox.Item, { item: opt, children: [
@@ -3442,7 +3477,7 @@ var FieldListbox = createField({
3442
3477
  ] }, opt.value))
3443
3478
  ] }, groupName))
3444
3479
  ) : (
3445
- /* Плоские опции */
3480
+ /* Flat options */
3446
3481
  componentProps.options.map((opt) => /* @__PURE__ */ jsxs(Listbox.Item, { item: opt, children: [
3447
3482
  /* @__PURE__ */ jsx(Listbox.ItemText, { children: getOptionLabel(opt) }),
3448
3483
  /* @__PURE__ */ jsx(Listbox.ItemIndicator, {})
@@ -3574,6 +3609,10 @@ var FieldRadioGroup = createField({
3574
3609
  disabled: resolved.disabled,
3575
3610
  readOnly: resolved.readOnly,
3576
3611
  "data-field-name": fullPath,
3612
+ display: "flex",
3613
+ flexDirection: componentProps.orientation === "horizontal" ? "row" : "column",
3614
+ gap: componentProps.orientation === "horizontal" ? 4 : 2,
3615
+ flexWrap: "wrap",
3577
3616
  children: componentProps.options.map((opt) => /* @__PURE__ */ jsxs(RadioGroup.Item, { value: opt.value, disabled: opt.disabled, children: [
3578
3617
  /* @__PURE__ */ jsx(RadioGroup.ItemHiddenInput, { onBlur: field.handleBlur }),
3579
3618
  /* @__PURE__ */ jsx(RadioGroup.ItemIndicator, {}),
@@ -3751,10 +3790,61 @@ var FieldTags = createField({
3751
3790
  );
3752
3791
  }
3753
3792
  });
3793
+
3794
+ // src/lib/declarative/form-fields/specialized/providers/dadata.ts
3795
+ var DADATA_URL = "https://suggestions.dadata.ru/suggestions/api/4_1/rs/suggest/address";
3796
+ function createDaDataProvider(config) {
3797
+ const { token, baseUrl = DADATA_URL } = config;
3798
+ return {
3799
+ async getSuggestions(query, options) {
3800
+ const body = {
3801
+ query,
3802
+ count: options?.count ?? 10
3803
+ };
3804
+ if (options?.bounds) {
3805
+ if (options.bounds.from) body.from_bound = { value: options.bounds.from };
3806
+ if (options.bounds.to) body.to_bound = { value: options.bounds.to };
3807
+ }
3808
+ if (options?.filters) {
3809
+ body.locations = [options.filters];
3810
+ }
3811
+ const response = await fetch(baseUrl, {
3812
+ method: "POST",
3813
+ headers: {
3814
+ "Content-Type": "application/json",
3815
+ Accept: "application/json",
3816
+ Authorization: `Token ${token}`
3817
+ },
3818
+ body: JSON.stringify(body)
3819
+ });
3820
+ if (!response.ok) {
3821
+ return [];
3822
+ }
3823
+ const data = await response.json();
3824
+ const suggestions = data.suggestions ?? [];
3825
+ return suggestions.map(
3826
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3827
+ (s) => ({
3828
+ label: s.value,
3829
+ value: s.value,
3830
+ data: s.data
3831
+ })
3832
+ );
3833
+ }
3834
+ };
3835
+ }
3836
+ function useAddressProvider(propProvider, token) {
3837
+ const formContext2 = useDeclarativeFormOptional();
3838
+ if (propProvider) return propProvider;
3839
+ if (formContext2?.addressProvider) return formContext2.addressProvider;
3840
+ if (token) return createDaDataProvider({ token });
3841
+ return null;
3842
+ }
3754
3843
  var FieldAddress = createField({
3755
3844
  displayName: "FieldAddress",
3756
3845
  useFieldState: (props) => {
3757
- const { token, minChars = 3, debounceMs = 300, locations } = props;
3846
+ const { provider: propProvider, token, minChars = 3, debounceMs = 300, locations } = props;
3847
+ const provider = useAddressProvider(propProvider, token);
3758
3848
  const [inputValue, setInputValue] = useState("");
3759
3849
  const [suggestions, setSuggestions] = useState([]);
3760
3850
  const [isLoading, setIsLoading] = useState(false);
@@ -3765,38 +3855,26 @@ var FieldAddress = createField({
3765
3855
  const debouncedQuery = useDebounce(inputValue, debounceMs);
3766
3856
  const fetchSuggestions = useCallback(
3767
3857
  async (query) => {
3768
- if (query.length < minChars) {
3858
+ if (query.length < minChars || !provider) {
3769
3859
  setSuggestions([]);
3770
3860
  return;
3771
3861
  }
3772
3862
  setIsLoading(true);
3773
3863
  try {
3774
- const response = await fetch("https://suggestions.dadata.ru/suggestions/api/4_1/rs/suggest/address", {
3775
- method: "POST",
3776
- headers: {
3777
- "Content-Type": "application/json",
3778
- Accept: "application/json",
3779
- Authorization: `Token ${token}`
3780
- },
3781
- body: JSON.stringify({
3782
- query,
3783
- count: 10,
3784
- locations
3785
- })
3864
+ const results = await provider.getSuggestions(query, {
3865
+ count: 10,
3866
+ filters: locations ? Object.assign({}, ...locations) : void 0
3786
3867
  });
3787
- if (response.ok) {
3788
- const data = await response.json();
3789
- setSuggestions(data.suggestions || []);
3790
- setIsOpen(true);
3791
- }
3868
+ setSuggestions(results);
3869
+ setIsOpen(true);
3792
3870
  } catch (error) {
3793
- console.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438 DaData:", error);
3871
+ console.error("Error loading address suggestions:", error);
3794
3872
  setSuggestions([]);
3795
3873
  } finally {
3796
3874
  setIsLoading(false);
3797
3875
  }
3798
3876
  },
3799
- [token, minChars, locations]
3877
+ [provider, minChars, locations]
3800
3878
  );
3801
3879
  useEffect(() => {
3802
3880
  if (debouncedQuery) {
@@ -3918,7 +3996,7 @@ var FieldAddress = createField({
3918
3996
  },
3919
3997
  onBlur: field.handleBlur,
3920
3998
  onKeyDown: handleKeyDown,
3921
- placeholder: resolved.placeholder ?? "\u041D\u0430\u0447\u043D\u0438\u0442\u0435 \u0432\u0432\u043E\u0434\u0438\u0442\u044C \u0430\u0434\u0440\u0435\u0441...",
3999
+ placeholder: resolved.placeholder ?? "Start typing address...",
3922
4000
  "data-field-name": fullPath
3923
4001
  }
3924
4002
  ),
@@ -3946,7 +4024,7 @@ var FieldAddress = createField({
3946
4024
  _hover: { bg: "bg.muted" },
3947
4025
  onClick: () => handleSelect(suggestion),
3948
4026
  onMouseEnter: () => setHighlightedIndex(index),
3949
- children: /* @__PURE__ */ jsx(Text, { fontSize: "sm", children: suggestion.value })
4027
+ children: /* @__PURE__ */ jsx(Text, { fontSize: "sm", children: suggestion.label })
3950
4028
  },
3951
4029
  suggestion.value + index
3952
4030
  ))
@@ -4074,11 +4152,11 @@ var FieldFileUpload = createField({
4074
4152
  variant = "button",
4075
4153
  showSize = false,
4076
4154
  clearable = true,
4077
- dropzoneLabel = "\u041F\u0435\u0440\u0435\u0442\u0430\u0449\u0438\u0442\u0435 \u0444\u0430\u0439\u043B\u044B \u0441\u044E\u0434\u0430",
4155
+ dropzoneLabel = "Drag and drop files here",
4078
4156
  dropzoneDescription,
4079
- buttonText = "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0444\u0430\u0439\u043B"
4157
+ buttonText = "Upload file"
4080
4158
  } = componentProps;
4081
- const placeholder = resolved.placeholder ?? "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0444\u0430\u0439\u043B(\u044B)";
4159
+ const placeholder = resolved.placeholder ?? "Select file(s)";
4082
4160
  const normalizedAccept = accept ? typeof accept === "string" ? accept.split(",").map((s) => s.trim()) : accept : void 0;
4083
4161
  const isImageUpload = normalizedAccept?.some((type) => type.startsWith("image/") || type === "image/*");
4084
4162
  return /* @__PURE__ */ jsxs(Field.Root, { invalid: hasError, required: resolved.required, disabled: resolved.disabled, children: [
@@ -4120,7 +4198,7 @@ var FieldFileUpload = createField({
4120
4198
  if (acceptedFiles.length > 1) {
4121
4199
  return /* @__PURE__ */ jsxs("span", { children: [
4122
4200
  acceptedFiles.length,
4123
- " \u0444\u0430\u0439\u043B\u043E\u0432"
4201
+ " files"
4124
4202
  ] });
4125
4203
  }
4126
4204
  return /* @__PURE__ */ jsx(Text, { color: "fg.subtle", children: placeholder });
@@ -4194,9 +4272,9 @@ var FieldOTPInput = createField({
4194
4272
  }
4195
4273
  ),
4196
4274
  onResend && /* @__PURE__ */ jsx(HStack, { mt: 3, justify: "center", children: countdown > 0 ? /* @__PURE__ */ jsxs(Text, { fontSize: "sm", color: "fg.muted", children: [
4197
- "\u041F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C \u0447\u0435\u0440\u0435\u0437 ",
4275
+ "Redo in ",
4198
4276
  formatCountdown(countdown)
4199
- ] }) : /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: handleResend, disabled: isResending, loading: isResending, children: "\u041E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C \u043F\u043E\u0432\u0442\u043E\u0440\u043D\u043E" }) })
4277
+ ] }) : /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: handleResend, disabled: isResending, loading: isResending, children: "Submit again" }) })
4200
4278
  ] }) });
4201
4279
  }
4202
4280
  });
@@ -4702,7 +4780,7 @@ function renderFieldByType(type, props) {
4702
4780
  const selectOptions = resolveSelectOptions(fieldProps, relationOptions, enumValues);
4703
4781
  const nativeSelectOptions = resolveNativeSelectOptions(fieldProps, relationOptions, enumValues);
4704
4782
  switch (type) {
4705
- // Текстовые поля
4783
+ // Text fields
4706
4784
  case "string":
4707
4785
  return /* @__PURE__ */ jsx(
4708
4786
  FieldString,
@@ -4726,7 +4804,7 @@ function renderFieldByType(type, props) {
4726
4804
  return /* @__PURE__ */ jsx(FieldEditable, { ...baseProps, ...fieldProps }, name);
4727
4805
  case "richText":
4728
4806
  return /* @__PURE__ */ jsx(FieldRichText, { ...baseProps, ...fieldProps }, name);
4729
- // Числовые поля
4807
+ // Number fields
4730
4808
  case "number":
4731
4809
  return /* @__PURE__ */ jsx(
4732
4810
  FieldNumber,
@@ -4769,7 +4847,7 @@ function renderFieldByType(type, props) {
4769
4847
  return /* @__PURE__ */ jsx(FieldCurrency, { ...baseProps, ...fieldProps }, name);
4770
4848
  case "percentage":
4771
4849
  return /* @__PURE__ */ jsx(FieldPercentage, { ...baseProps, ...fieldProps }, name);
4772
- // Дата и время
4850
+ // Date and time
4773
4851
  case "date":
4774
4852
  return /* @__PURE__ */ jsx(
4775
4853
  FieldDate,
@@ -4791,13 +4869,13 @@ function renderFieldByType(type, props) {
4791
4869
  return /* @__PURE__ */ jsx(FieldDuration, { ...baseProps, ...fieldProps }, name);
4792
4870
  case "schedule":
4793
4871
  return /* @__PURE__ */ jsx(FieldSchedule, { ...baseProps, ...fieldProps }, name);
4794
- // Булевые поля
4872
+ // Boolean fields
4795
4873
  case "checkbox":
4796
4874
  return /* @__PURE__ */ jsx(FieldCheckbox, { ...baseProps, ...fieldProps }, name);
4797
4875
  case "switch":
4798
4876
  return /* @__PURE__ */ jsx(FieldSwitch, { ...baseProps, ...fieldProps }, name);
4799
- // Выбор из списка
4800
- // Options разрешаются с приоритетом: fieldProps.options > relationOptions > enumValues
4877
+ // Selection from list
4878
+ // Options resolved with priority: fieldProps.options > relationOptions > enumValues
4801
4879
  case "select":
4802
4880
  return /* @__PURE__ */ jsx(FieldSelect, { ...baseProps, options: selectOptions ?? [], ...fieldProps }, name);
4803
4881
  case "nativeSelect":
@@ -4818,7 +4896,7 @@ function renderFieldByType(type, props) {
4818
4896
  return /* @__PURE__ */ jsx(FieldCheckboxCard, { ...baseProps, options: selectOptions ?? [], ...fieldProps }, name);
4819
4897
  case "tags":
4820
4898
  return /* @__PURE__ */ jsx(FieldTags, { ...baseProps, ...fieldProps }, name);
4821
- // Специализированные поля
4899
+ // Specialized fields
4822
4900
  case "phone":
4823
4901
  return /* @__PURE__ */ jsx(FieldPhone, { ...baseProps, ...fieldProps }, name);
4824
4902
  case "address": {
@@ -5169,7 +5247,7 @@ function analyzeArrayElement(elementSchema, parentPath, ctx) {
5169
5247
  ui: getUIMeta2(elementSchema),
5170
5248
  required: isRequired(elementSchema),
5171
5249
  constraints: {}
5172
- // Constraints для элементов определяются отдельно
5250
+ // Constraints for elements are determined separately
5173
5251
  };
5174
5252
  if (zodType === "object" && unwrapped._zod?.def?.shape) {
5175
5253
  const children = traverseSchemaShape(unwrapped._zod.def.shape, path, { ...ctx, depth: ctx.depth + 1 });
@@ -5257,11 +5335,11 @@ function renderField(field, recursive, fieldWrapper) {
5257
5335
  emptyContent: /* @__PURE__ */ jsx(EmptyArrayContent, { label: ui?.title ?? name }),
5258
5336
  wrapper: ({ children }) => /* @__PURE__ */ jsxs(VStack, { align: "stretch", gap: 2, children: [
5259
5337
  children,
5260
- /* @__PURE__ */ jsx(ListButtonAdd, { defaultValue: createDefaultValue(elementChildren), children: "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C" })
5338
+ /* @__PURE__ */ jsx(ListButtonAdd, { defaultValue: createDefaultValue(elementChildren), children: "Add" })
5261
5339
  ] }),
5262
5340
  children: /* @__PURE__ */ jsxs(VStack, { align: "stretch", gap: 2, p: 2, borderWidth: 1, borderRadius: "md", children: [
5263
5341
  elementChildren.map((child) => renderField(child, recursive, fieldWrapper)),
5264
- /* @__PURE__ */ jsx(ListButtonRemove, { children: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C" })
5342
+ /* @__PURE__ */ jsx(ListButtonRemove, { children: "Remove" })
5265
5343
  ] })
5266
5344
  },
5267
5345
  name
@@ -5280,7 +5358,7 @@ function renderField(field, recursive, fieldWrapper) {
5280
5358
  }
5281
5359
  function EmptyArrayContent({ label }) {
5282
5360
  return /* @__PURE__ */ jsxs(VStack, { py: 4, color: "fg.muted", children: [
5283
- '\u041D\u0435\u0442 \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u043E\u0432 \u0432 "',
5361
+ 'No items in "',
5284
5362
  label,
5285
5363
  '"'
5286
5364
  ] });
@@ -5320,7 +5398,7 @@ function createDefaultValue(children) {
5320
5398
  function FormAutoFields({ include, exclude, recursive = true, fieldWrapper }) {
5321
5399
  const { schema } = useDeclarativeForm();
5322
5400
  if (!schema) {
5323
- throw new Error("Form.AutoFields \u0442\u0440\u0435\u0431\u0443\u0435\u0442 schema prop \u043D\u0430 Form \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0435");
5401
+ throw new Error("Form.AutoFields requires schema prop on Form component");
5324
5402
  }
5325
5403
  const allFields = traverseSchema(schema);
5326
5404
  const fields = filterFields(allFields, { include, exclude });
@@ -5405,7 +5483,7 @@ function CascadingSelectContent({
5405
5483
  const newOptions = Array.isArray(result) ? result : result.options;
5406
5484
  setOptions(newOptions);
5407
5485
  } catch (error) {
5408
- console.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438 \u043E\u043F\u0446\u0438\u0439 \u043A\u0430\u0441\u043A\u0430\u0434\u043D\u043E\u0433\u043E select:", error);
5486
+ console.error("Error loading cascading select options:", error);
5409
5487
  setOptions([]);
5410
5488
  } finally {
5411
5489
  setIsLoading(false);
@@ -5534,11 +5612,20 @@ function FieldCascadingSelect(props) {
5534
5612
  ) });
5535
5613
  }
5536
5614
  FieldCascadingSelect.displayName = "FieldCascadingSelect";
5615
+ function useCityProvider(propProvider, token) {
5616
+ const formContext2 = useDeclarativeFormOptional();
5617
+ if (propProvider) return propProvider;
5618
+ if (formContext2?.addressProvider) return formContext2.addressProvider;
5619
+ if (token) return createDaDataProvider({ token });
5620
+ const envKey = typeof window !== "undefined" ? process.env.NEXT_PUBLIC_DADATA_API_KEY : "";
5621
+ if (envKey) return createDaDataProvider({ token: envKey });
5622
+ return null;
5623
+ }
5537
5624
  var FieldCity = createField({
5538
5625
  displayName: "FieldCity",
5539
5626
  useFieldState: (props) => {
5540
- const { token, minChars = 2, debounceMs = 300 } = props;
5541
- const apiKey = token || (typeof window !== "undefined" ? process.env.NEXT_PUBLIC_DADATA_API_KEY : "") || "";
5627
+ const { provider: propProvider, token, minChars = 2, debounceMs = 300 } = props;
5628
+ const provider = useCityProvider(propProvider, token);
5542
5629
  const [inputValue, setInputValue] = useState("");
5543
5630
  const [suggestions, setSuggestions] = useState([]);
5544
5631
  const [isLoading, setIsLoading] = useState(false);
@@ -5550,39 +5637,26 @@ var FieldCity = createField({
5550
5637
  const initializedRef = useRef(false);
5551
5638
  const fetchSuggestions = useCallback(
5552
5639
  async (query) => {
5553
- if (query.length < minChars || !apiKey) {
5640
+ if (query.length < minChars || !provider) {
5554
5641
  setSuggestions([]);
5555
5642
  return;
5556
5643
  }
5557
5644
  setIsLoading(true);
5558
5645
  try {
5559
- const response = await fetch("https://suggestions.dadata.ru/suggestions/api/4_1/rs/suggest/address", {
5560
- method: "POST",
5561
- headers: {
5562
- "Content-Type": "application/json",
5563
- Accept: "application/json",
5564
- Authorization: `Token ${apiKey}`
5565
- },
5566
- body: JSON.stringify({
5567
- query,
5568
- count: 7,
5569
- from_bound: { value: "city" },
5570
- to_bound: { value: "settlement" }
5571
- })
5646
+ const results = await provider.getSuggestions(query, {
5647
+ count: 7,
5648
+ bounds: { from: "city", to: "settlement" }
5572
5649
  });
5573
- if (response.ok) {
5574
- const data = await response.json();
5575
- setSuggestions(data.suggestions || []);
5576
- setIsOpen(data.suggestions?.length > 0);
5577
- }
5650
+ setSuggestions(results);
5651
+ setIsOpen(results.length > 0);
5578
5652
  } catch (error) {
5579
- console.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438 \u0433\u043E\u0440\u043E\u0434\u043E\u0432 DaData:", error);
5653
+ console.error("Error loading city suggestions:", error);
5580
5654
  setSuggestions([]);
5581
5655
  } finally {
5582
5656
  setIsLoading(false);
5583
5657
  }
5584
5658
  },
5585
- [apiKey, minChars]
5659
+ [provider, minChars]
5586
5660
  );
5587
5661
  useEffect(() => {
5588
5662
  if (justSelectedRef.current) {
@@ -5642,7 +5716,7 @@ var FieldCity = createField({
5642
5716
  setInputValue(fieldValue);
5643
5717
  }
5644
5718
  const handleSelect = (suggestion) => {
5645
- const cityName = suggestion.data.city || suggestion.data.settlement || suggestion.value;
5719
+ const cityName = suggestion.data?.city || suggestion.data?.settlement || suggestion.value;
5646
5720
  justSelectedRef.current = true;
5647
5721
  setInputValue(cityName);
5648
5722
  setIsOpen(false);
@@ -5699,9 +5773,14 @@ var FieldCity = createField({
5699
5773
  setIsOpen(true);
5700
5774
  }
5701
5775
  },
5702
- onBlur: field.handleBlur,
5776
+ onBlur: () => {
5777
+ if (inputValue && inputValue !== field.state.value) {
5778
+ field.handleChange(inputValue);
5779
+ }
5780
+ field.handleBlur();
5781
+ },
5703
5782
  onKeyDown: handleKeyDown,
5704
- placeholder: resolved.placeholder ?? "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0433\u043E\u0440\u043E\u0434",
5783
+ placeholder: resolved.placeholder ?? "Enter city",
5705
5784
  "data-field-name": fullPath
5706
5785
  }
5707
5786
  ),
@@ -5730,9 +5809,9 @@ var FieldCity = createField({
5730
5809
  _hover: { bg: "bg.muted" },
5731
5810
  onClick: () => handleSelect(suggestion),
5732
5811
  onMouseEnter: () => setHighlightedIndex(index),
5733
- children: /* @__PURE__ */ jsx(Text, { fontSize: "sm", children: suggestion.value })
5812
+ children: /* @__PURE__ */ jsx(Text, { fontSize: "sm", children: suggestion.label })
5734
5813
  },
5735
- `${suggestion.data.city_fias_id}-${index}`
5814
+ `${suggestion.value}-${index}`
5736
5815
  ))
5737
5816
  }
5738
5817
  )
@@ -5743,6 +5822,61 @@ var FieldCity = createField({
5743
5822
  );
5744
5823
  }
5745
5824
  });
5825
+ function useIsDarkMode() {
5826
+ const [isDark, setIsDark] = useState(false);
5827
+ useEffect(() => {
5828
+ const check = () => {
5829
+ const html = document.documentElement;
5830
+ const isDarkNow = html.classList.contains("dark") || html.getAttribute("data-theme") === "dark" || html.style.colorScheme === "dark";
5831
+ setIsDark(isDarkNow);
5832
+ };
5833
+ check();
5834
+ const observer = new MutationObserver(check);
5835
+ observer.observe(document.documentElement, {
5836
+ attributes: true,
5837
+ attributeFilter: ["class", "data-theme", "style"]
5838
+ });
5839
+ return () => observer.disconnect();
5840
+ }, []);
5841
+ return isDark;
5842
+ }
5843
+ function FormDebugValues({
5844
+ title = "Form Values",
5845
+ collapsed = 2,
5846
+ showInProduction = false
5847
+ }) {
5848
+ const { form } = useDeclarativeForm();
5849
+ const isDark = useIsDarkMode();
5850
+ if (process.env.NODE_ENV === "production" && !showInProduction) {
5851
+ return null;
5852
+ }
5853
+ const jsonTheme = isDark ? githubDarkTheme : githubLightTheme;
5854
+ return /* @__PURE__ */ jsx(form.Subscribe, { selector: (state) => state.values, children: (values) => /* @__PURE__ */ jsxs(
5855
+ Box,
5856
+ {
5857
+ borderWidth: "1px",
5858
+ borderColor: "border.muted",
5859
+ borderRadius: "md",
5860
+ p: 3,
5861
+ mt: 4,
5862
+ fontSize: "sm",
5863
+ fontFamily: "mono",
5864
+ bg: "bg.subtle",
5865
+ children: [
5866
+ title && /* @__PURE__ */ jsx(Heading, { size: "xs", mb: 2, color: "fg.muted", children: title }),
5867
+ /* @__PURE__ */ jsx(
5868
+ JsonView,
5869
+ {
5870
+ value: values,
5871
+ collapsed,
5872
+ displayDataTypes: false,
5873
+ style: { ...jsonTheme, backgroundColor: "transparent" }
5874
+ }
5875
+ )
5876
+ ]
5877
+ }
5878
+ ) });
5879
+ }
5746
5880
 
5747
5881
  // src/lib/declarative/form-root/form-validators.ts
5748
5882
  function buildValidators(schema, validateOn) {
@@ -5964,7 +6098,7 @@ function useFormFeatures({
5964
6098
  }
5965
6099
  return { success: true };
5966
6100
  } catch (error) {
5967
- return { success: false, error: error instanceof Error ? error.message : "\u041E\u0448\u0438\u0431\u043A\u0430 \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u0438" };
6101
+ return { success: false, error: error instanceof Error ? error.message : "Error \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u0438" };
5968
6102
  }
5969
6103
  },
5970
6104
  [onlineSubmit, isPersistenceEnabled, persistenceResult]
@@ -6053,7 +6187,9 @@ function FormSimple({
6053
6187
  validateOn,
6054
6188
  disabled,
6055
6189
  readOnly,
6190
+ debug,
6056
6191
  middleware,
6192
+ addressProvider,
6057
6193
  children
6058
6194
  }) {
6059
6195
  const features = useFormFeatures({
@@ -6110,13 +6246,14 @@ function FormSimple({
6110
6246
  schema,
6111
6247
  offlineState: features.offlineState,
6112
6248
  disabled,
6113
- readOnly
6249
+ readOnly,
6250
+ addressProvider
6114
6251
  }),
6115
- [form, schema, features.offlineState, disabled, readOnly]
6252
+ [form, schema, features.offlineState, disabled, readOnly, addressProvider]
6116
6253
  );
6117
6254
  return /* @__PURE__ */ jsxs(DeclarativeFormContext.Provider, { value: contextValue, children: [
6118
6255
  features.isPersistenceEnabled && /* @__PURE__ */ jsx(features.persistenceResult.RestoreDialog, {}),
6119
- /* @__PURE__ */ jsx(
6256
+ /* @__PURE__ */ jsxs(
6120
6257
  "form",
6121
6258
  {
6122
6259
  onSubmit: (e) => {
@@ -6124,7 +6261,10 @@ function FormSimple({
6124
6261
  e.stopPropagation();
6125
6262
  form.handleSubmit();
6126
6263
  },
6127
- children
6264
+ children: [
6265
+ children,
6266
+ debug && /* @__PURE__ */ jsx(FormDebugValues, { showInProduction: debug === "force" })
6267
+ ]
6128
6268
  }
6129
6269
  )
6130
6270
  ] });
@@ -6183,7 +6323,7 @@ function useFormApi(config) {
6183
6323
  );
6184
6324
  }
6185
6325
  function FormLoadingState() {
6186
- return /* @__PURE__ */ jsx("div", { style: { opacity: 0.5, padding: "1rem" }, children: /* @__PURE__ */ jsx("p", { children: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0434\u0430\u043D\u043D\u044B\u0445 \u0444\u043E\u0440\u043C\u044B..." }) });
6326
+ return /* @__PURE__ */ jsx("div", { style: { opacity: 0.5, padding: "1rem" }, children: /* @__PURE__ */ jsx("p", { children: "Loading form data..." }) });
6187
6327
  }
6188
6328
  function FormWithApi({
6189
6329
  api,
@@ -6195,7 +6335,9 @@ function FormWithApi({
6195
6335
  validateOn,
6196
6336
  disabled,
6197
6337
  readOnly,
6338
+ debug,
6198
6339
  middleware,
6340
+ addressProvider,
6199
6341
  children
6200
6342
  }) {
6201
6343
  const formApi = useFormApi(api);
@@ -6253,7 +6395,7 @@ function FormWithApi({
6253
6395
  const contextValue = {
6254
6396
  form,
6255
6397
  schema,
6256
- // Экспортируем состояние API для компонентов, которым оно нужно
6398
+ // Export API state for components that need it
6257
6399
  apiState: {
6258
6400
  isEditMode: formApi.isEditMode,
6259
6401
  isLoading: formApi.isLoading,
@@ -6263,14 +6405,15 @@ function FormWithApi({
6263
6405
  },
6264
6406
  offlineState: features.offlineState,
6265
6407
  disabled,
6266
- readOnly
6408
+ readOnly,
6409
+ addressProvider
6267
6410
  };
6268
6411
  if (formApi.isLoading) {
6269
6412
  return /* @__PURE__ */ jsx(FormLoadingState, {});
6270
6413
  }
6271
6414
  return /* @__PURE__ */ jsxs(DeclarativeFormContext.Provider, { value: contextValue, children: [
6272
6415
  features.isPersistenceEnabled && /* @__PURE__ */ jsx(features.persistenceResult.RestoreDialog, {}),
6273
- /* @__PURE__ */ jsx(
6416
+ /* @__PURE__ */ jsxs(
6274
6417
  "form",
6275
6418
  {
6276
6419
  onSubmit: (e) => {
@@ -6278,7 +6421,10 @@ function FormWithApi({
6278
6421
  e.stopPropagation();
6279
6422
  form.handleSubmit();
6280
6423
  },
6281
- children
6424
+ children: [
6425
+ children,
6426
+ debug && /* @__PURE__ */ jsx(FormDebugValues, { showInProduction: debug === "force" })
6427
+ ]
6282
6428
  }
6283
6429
  )
6284
6430
  ] }, dataLoaded ? "loaded" : "initial");
@@ -6293,6 +6439,7 @@ function FormRoot({
6293
6439
  validateOn,
6294
6440
  disabled,
6295
6441
  readOnly,
6442
+ debug,
6296
6443
  children
6297
6444
  }) {
6298
6445
  if (api) {
@@ -6308,6 +6455,7 @@ function FormRoot({
6308
6455
  validateOn,
6309
6456
  disabled,
6310
6457
  readOnly,
6458
+ debug,
6311
6459
  children
6312
6460
  }
6313
6461
  );
@@ -6328,6 +6476,7 @@ function FormRoot({
6328
6476
  validateOn,
6329
6477
  disabled,
6330
6478
  readOnly,
6479
+ debug,
6331
6480
  children
6332
6481
  }
6333
6482
  );
@@ -6405,7 +6554,7 @@ function FormBuilder({
6405
6554
  middleware,
6406
6555
  disabled,
6407
6556
  readOnly,
6408
- submitLabel = "\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C",
6557
+ submitLabel = "Save",
6409
6558
  children
6410
6559
  }) {
6411
6560
  return /* @__PURE__ */ jsxs(
@@ -6461,13 +6610,13 @@ function extractAllErrors(errors) {
6461
6610
  return messages;
6462
6611
  }
6463
6612
  function FormErrors({
6464
- title = "\u0418\u0441\u043F\u0440\u0430\u0432\u044C\u0442\u0435 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0435 \u043E\u0448\u0438\u0431\u043A\u0438:",
6613
+ title = "Please fix the following errors:",
6465
6614
  showBeforeSubmit = false
6466
6615
  }) {
6467
6616
  const { form, apiState } = useDeclarativeForm();
6468
6617
  const serverError = apiState?.mutationError;
6469
6618
  const errorInfo = serverError && "info" in serverError ? serverError.info : null;
6470
- const serverErrorMessage = serverError ? serverError.message || errorInfo?.message || "\u041E\u0448\u0438\u0431\u043A\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430" : null;
6619
+ const serverErrorMessage = serverError ? serverError.message || errorInfo?.message || "Server error" : null;
6471
6620
  return /* @__PURE__ */ jsx(
6472
6621
  form.Subscribe,
6473
6622
  {
@@ -6500,14 +6649,15 @@ function FormFromSchema({
6500
6649
  schema,
6501
6650
  initialValue,
6502
6651
  onSubmit,
6503
- submitLabel = "\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C",
6652
+ submitLabel = "Save",
6504
6653
  showReset = false,
6505
- resetLabel = "\u0421\u0431\u0440\u043E\u0441\u0438\u0442\u044C",
6654
+ resetLabel = "Reset",
6506
6655
  exclude,
6507
6656
  validateOn,
6508
6657
  middleware,
6509
6658
  disabled,
6510
6659
  readOnly,
6660
+ debug,
6511
6661
  persistence,
6512
6662
  offline,
6513
6663
  beforeButtons,
@@ -6524,6 +6674,7 @@ function FormFromSchema({
6524
6674
  middleware,
6525
6675
  disabled,
6526
6676
  readOnly,
6677
+ debug,
6527
6678
  persistence,
6528
6679
  offline,
6529
6680
  children: /* @__PURE__ */ jsxs(VStack, { align: "stretch", gap, children: [
@@ -6856,7 +7007,7 @@ function FormSteps({
6856
7007
  () => ({
6857
7008
  currentStep,
6858
7009
  stepCount,
6859
- // Геттер для steps — возвращает актуальное значение через ref
7010
+ // Getter for steps — returns current value via ref
6860
7011
  get steps() {
6861
7012
  return sortedStepsRef.current;
6862
7013
  },
@@ -6891,8 +7042,8 @@ function FormSteps({
6891
7042
  },
6892
7043
  clearStepPersistence: clearPersistence
6893
7044
  }),
6894
- // ВАЖНО: sortedSteps, hiddenFields, onStepComplete НЕ в deps —
6895
- // доступны через refs/getters, предотвращает бесконечный цикл
7045
+ // IMPORTANT: sortedSteps, hiddenFields, onStepComplete NOT in deps —
7046
+ // accessed via refs/getters, prevents infinite loop
6896
7047
  [
6897
7048
  currentStep,
6898
7049
  stepCount,
@@ -7168,7 +7319,7 @@ function FormStepsStep({
7168
7319
  if (!when) {
7169
7320
  return;
7170
7321
  }
7171
- const unsubscribe = form.store.subscribe(() => {
7322
+ const subscription = form.store.subscribe(() => {
7172
7323
  const fieldValue = getNestedValue(form.state.values, when.field);
7173
7324
  const newIsVisible = evaluateWhenCondition(when, fieldValue);
7174
7325
  if (newIsVisible !== wasVisibleRef.current) {
@@ -7176,7 +7327,7 @@ function FormStepsStep({
7176
7327
  setIsVisible(newIsVisible);
7177
7328
  }
7178
7329
  });
7179
- return unsubscribe;
7330
+ return () => subscription.unsubscribe();
7180
7331
  }, [form, when]);
7181
7332
  const stepsRef = useRef(steps);
7182
7333
  stepsRef.current = steps;
@@ -7216,8 +7367,8 @@ function FormStepsStep({
7216
7367
  const fieldNamesRef = useRef([]);
7217
7368
  const currentFieldNames = useMemo(
7218
7369
  () => extractFieldNames(children, fieldExtractionPath),
7219
- // Используем segment path как proxy для определения когда структура может измениться
7220
- // children НЕ включаемони меняются на каждый рендер
7370
+ // Use segment path as proxy to determine when structure may change
7371
+ // children NOT includedthey change every render
7221
7372
  [fieldExtractionPath]
7222
7373
  );
7223
7374
  const fieldNamesChanged = currentFieldNames.length !== fieldNamesRef.current.length || currentFieldNames.some((name, i) => name !== fieldNamesRef.current[i]);
@@ -7243,17 +7394,17 @@ function FormStepsStep({
7243
7394
  const index = indexRef.current;
7244
7395
  const slideVariants = useMemo(
7245
7396
  () => ({
7246
- // Начальное состояние: элемент появляется с нужной стороны
7397
+ // Initial state: element appears from the appropriate side
7247
7398
  initial: {
7248
7399
  opacity: 0,
7249
7400
  x: direction === "forward" ? SLIDE_OFFSET : -SLIDE_OFFSET
7250
7401
  },
7251
- // Финальное состояние: элемент на месте
7402
+ // Final state: element in place
7252
7403
  animate: {
7253
7404
  opacity: 1,
7254
7405
  x: 0
7255
7406
  },
7256
- // Состояние выхода: элемент уходит в противоположную сторону
7407
+ // Exit state: element leaves to the opposite side
7257
7408
  exit: {
7258
7409
  opacity: 0,
7259
7410
  x: direction === "forward" ? -SLIDE_OFFSET : SLIDE_OFFSET
@@ -7422,7 +7573,8 @@ function createForm(options = {}) {
7422
7573
  extraListboxes = {},
7423
7574
  lazySelects = {},
7424
7575
  lazyComboboxes = {},
7425
- lazyListboxes = {}
7576
+ lazyListboxes = {},
7577
+ addressProvider
7426
7578
  } = options;
7427
7579
  const lazySelectComponents = createLazyComponents(lazySelects);
7428
7580
  const lazyComboboxComponents = createLazyComponents(lazyComboboxes);
@@ -7450,7 +7602,8 @@ function createForm(options = {}) {
7450
7602
  const ExtendedForm = Object.assign(
7451
7603
  // Root component
7452
7604
  function ExtendedFormRoot(props) {
7453
- return Form2(props);
7605
+ const mergedProps = addressProvider && !props.addressProvider ? { ...props, addressProvider } : props;
7606
+ return Form2(mergedProps);
7454
7607
  },
7455
7608
  {
7456
7609
  Group: Form2.Group,
@@ -7460,6 +7613,7 @@ function createForm(options = {}) {
7460
7613
  Combobox: ExtendedCombobox,
7461
7614
  Listbox: ExtendedListbox,
7462
7615
  Errors: Form2.Errors,
7616
+ DebugValues: Form2.DebugValues,
7463
7617
  DirtyGuard: Form2.DirtyGuard,
7464
7618
  When: Form2.When,
7465
7619
  Steps: Form2.Steps,
@@ -7489,7 +7643,16 @@ function useFieldActions(fieldName) {
7489
7643
  },
7490
7644
  [fullPath]
7491
7645
  );
7492
- const subscribe = useCallback((callback) => form.store.subscribe(callback), [form]);
7646
+ const subscribe = useCallback(
7647
+ (callback) => {
7648
+ const subscription = form.store.subscribe(callback);
7649
+ if (typeof subscription === "function") {
7650
+ return subscription;
7651
+ }
7652
+ return () => subscription.unsubscribe();
7653
+ },
7654
+ [form]
7655
+ );
7493
7656
  const getValueSnapshot = useCallback(
7494
7657
  () => getNestedValue2(form.state.values),
7495
7658
  [form, getNestedValue2]
@@ -7635,12 +7798,12 @@ var commonMeta = {
7635
7798
  fieldProps: { disabled: true }
7636
7799
  },
7637
7800
  createdAt: {
7638
- title: "\u0421\u043E\u0437\u0434\u0430\u043D\u043E",
7801
+ title: "Created",
7639
7802
  fieldType: "date",
7640
7803
  fieldProps: { readOnly: true }
7641
7804
  },
7642
7805
  updatedAt: {
7643
- title: "\u041E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u043E",
7806
+ title: "Updated",
7644
7807
  fieldType: "date",
7645
7808
  fieldProps: { readOnly: true }
7646
7809
  }
@@ -7802,6 +7965,7 @@ var Form2 = Object.assign(Form, {
7802
7965
  Field: FormField2,
7803
7966
  Button: FormButton,
7804
7967
  Errors: FormErrors,
7968
+ DebugValues: FormDebugValues,
7805
7969
  DirtyGuard,
7806
7970
  When: FormWhen,
7807
7971
  Steps: FormSteps2,