@reformer/core 11.0.0-beta.1 → 11.0.0-beta.10

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.
Files changed (283) hide show
  1. package/README.md +90 -68
  2. package/dist/behaviors-value-D05z3my6.js +109 -0
  3. package/dist/behaviors.d.ts +1 -1
  4. package/dist/behaviors.js +376 -211
  5. package/dist/create-model-DPSG3I0O.js +30 -0
  6. package/dist/date-utils-90-qZbse.js +43 -0
  7. package/dist/en-HP8XEych.js +44 -0
  8. package/dist/form/aggregate-signals.d.ts +21 -63
  9. package/dist/form/behaviors/collections.d.ts +84 -0
  10. package/dist/form/behaviors/context.d.ts +56 -0
  11. package/dist/form/behaviors/index.d.ts +30 -0
  12. package/dist/form/behaviors/internals.d.ts +41 -0
  13. package/dist/form/{behaviors-node.d.ts → behaviors/node.d.ts} +28 -5
  14. package/dist/form/behaviors/operators.d.ts +59 -0
  15. package/dist/form/behaviors/schema-operators.d.ts +83 -0
  16. package/dist/form/behaviors/types.d.ts +37 -0
  17. package/dist/form/create-form.d.ts +59 -70
  18. package/dist/form/flow/flow.d.ts +87 -0
  19. package/dist/form/form-bundle.d.ts +153 -0
  20. package/dist/form/form-level.d.ts +121 -0
  21. package/dist/form/form-proxy-builder.d.ts +6 -22
  22. package/dist/form/index.d.ts +23 -24
  23. package/dist/form/nodes/form-array-node.d.ts +100 -0
  24. package/dist/form/nodes/form-control-node.d.ts +73 -0
  25. package/dist/form/nodes/form-group-node.d.ts +66 -0
  26. package/dist/form/nodes/node-kind.d.ts +29 -0
  27. package/dist/form/nodes/state/availability.d.ts +6 -0
  28. package/dist/form/nodes/state/component.d.ts +6 -0
  29. package/dist/form/nodes/state/index.d.ts +12 -0
  30. package/dist/form/nodes/state/interaction.d.ts +13 -0
  31. package/dist/form/nodes/state/issues.d.ts +9 -0
  32. package/dist/form/nodes/state/pending.d.ts +6 -0
  33. package/dist/form/schema/compile.d.ts +59 -0
  34. package/dist/form/schema/compiled.d.ts +143 -0
  35. package/dist/form/schema/row-key.d.ts +11 -0
  36. package/dist/form/schema-controller.d.ts +159 -0
  37. package/dist/form/schema-subtree.d.ts +28 -0
  38. package/dist/form/signal-node-registry.d.ts +27 -22
  39. package/dist/form/submission.d.ts +93 -0
  40. package/dist/form/type-guards.d.ts +14 -73
  41. package/dist/form/types/contracts.d.ts +21 -85
  42. package/dist/form/types/field-config.d.ts +27 -0
  43. package/dist/form/types/index.d.ts +5 -57
  44. package/dist/form/types/nodes.d.ts +159 -0
  45. package/dist/form/types/schema-node.d.ts +147 -46
  46. package/dist/form/types/validation-schema.d.ts +25 -17
  47. package/dist/form/validation/context.d.ts +67 -0
  48. package/dist/form/validation/index.d.ts +24 -28
  49. package/dist/form/validation/operators.d.ts +101 -0
  50. package/dist/form/validation/run.d.ts +120 -0
  51. package/dist/form/validation/strategy.d.ts +89 -0
  52. package/dist/form/validation/types.d.ts +83 -0
  53. package/dist/form/validators/date-utils.d.ts +42 -0
  54. package/dist/form/validators/email.d.ts +23 -0
  55. package/dist/form/{validation/validators → validators}/file-type.d.ts +10 -10
  56. package/dist/form/{validation/validators → validators}/file-utils.d.ts +3 -2
  57. package/dist/form/validators/future-date.d.ts +23 -0
  58. package/dist/form/{validation/validators → validators}/index.d.ts +7 -4
  59. package/dist/form/validators/integer.d.ts +22 -0
  60. package/dist/form/validators/is-date.d.ts +24 -0
  61. package/dist/form/validators/is-number.d.ts +25 -0
  62. package/dist/form/validators/max-age.d.ts +28 -0
  63. package/dist/form/validators/max-date.d.ts +25 -0
  64. package/dist/form/{validation/validators → validators}/max-file-size.d.ts +10 -10
  65. package/dist/form/validators/max-files.d.ts +26 -0
  66. package/dist/form/validators/max-length.d.ts +33 -0
  67. package/dist/form/validators/max-total-file-size.d.ts +26 -0
  68. package/dist/form/validators/max.d.ts +26 -0
  69. package/dist/form/validators/min-age.d.ts +27 -0
  70. package/dist/form/validators/min-date.d.ts +25 -0
  71. package/dist/form/{validation/validators → validators}/min-file-size.d.ts +10 -10
  72. package/dist/form/validators/min-files.d.ts +27 -0
  73. package/dist/form/validators/min-length.d.ts +33 -0
  74. package/dist/form/validators/min.d.ts +26 -0
  75. package/dist/form/validators/multiple-of.d.ts +25 -0
  76. package/dist/form/validators/non-negative.d.ts +22 -0
  77. package/dist/form/validators/non-zero.d.ts +22 -0
  78. package/dist/form/validators/past-date.d.ts +23 -0
  79. package/dist/form/validators/pattern.d.ts +26 -0
  80. package/dist/form/{validation/validators → validators}/phone.d.ts +11 -11
  81. package/dist/form/validators/required.d.ts +29 -0
  82. package/dist/form/validators/url.d.ts +45 -0
  83. package/dist/form/validators/validation-issue.d.ts +8 -0
  84. package/dist/i18n/descriptor.d.ts +54 -0
  85. package/dist/i18n/en.json.d.ts +49 -0
  86. package/dist/i18n/loader.d.ts +71 -0
  87. package/dist/i18n/locale-store.d.ts +46 -0
  88. package/dist/i18n/locale.d.ts +90 -0
  89. package/dist/i18n/message-format.d.ts +136 -0
  90. package/dist/i18n/resolve-localized.d.ts +17 -0
  91. package/dist/i18n/ru.json.d.ts +49 -0
  92. package/dist/i18n/translator.d.ts +61 -0
  93. package/dist/i18n/validation-message.d.ts +53 -0
  94. package/dist/i18n.d.ts +26 -0
  95. package/dist/i18n.js +86 -0
  96. package/dist/index.d.ts +17 -5
  97. package/dist/index.js +942 -2601
  98. package/dist/loader-BWkigpVe.js +52 -0
  99. package/dist/locale/en.d.ts +2 -0
  100. package/dist/locale/en.js +5 -0
  101. package/dist/locale/en.json +45 -0
  102. package/dist/locale/index.d.ts +18 -0
  103. package/dist/locale/ru.d.ts +2 -0
  104. package/dist/locale/ru.js +44 -0
  105. package/dist/locale/ru.json +45 -0
  106. package/dist/locale-CG54AU2Q.js +219 -0
  107. package/dist/locale.d.ts +2 -0
  108. package/dist/locale.js +9 -0
  109. package/dist/{state → model}/behaviors-value.d.ts +24 -6
  110. package/dist/{state/form-model.d.ts → model/create-model.d.ts} +18 -4
  111. package/dist/{state → model}/derived-registry.d.ts +1 -1
  112. package/dist/model/index.d.ts +42 -0
  113. package/dist/model/model-nodes.d.ts +190 -0
  114. package/dist/model/model-signals-proxy.d.ts +63 -0
  115. package/dist/model/model-value-proxy.d.ts +84 -0
  116. package/dist/{state → model}/safe-effect.d.ts +50 -2
  117. package/dist/model/types.d.ts +239 -0
  118. package/dist/model.d.ts +2 -0
  119. package/dist/model.js +27 -0
  120. package/dist/node-kind-C-r6ZMdo.js +15 -0
  121. package/dist/platforms/react/field-binding/field-adapter.d.ts +87 -0
  122. package/dist/platforms/react/field-binding/field-handle.d.ts +53 -0
  123. package/dist/platforms/react/form-bundle-context.d.ts +27 -0
  124. package/dist/{form → platforms/react}/hooks/types.d.ts +58 -36
  125. package/dist/platforms/react/hooks/use-form-bundle.d.ts +41 -0
  126. package/dist/platforms/react/hooks/use-form-flow.d.ts +40 -0
  127. package/dist/{form → platforms/react}/hooks/use-form-validation.d.ts +13 -7
  128. package/dist/{form → platforms/react}/hooks/useArrayLength.d.ts +6 -6
  129. package/dist/{form → platforms/react}/hooks/useFormControl.d.ts +7 -9
  130. package/dist/{form → platforms/react}/hooks/useFormControlValue.d.ts +18 -18
  131. package/dist/{form → platforms/react}/hooks/useSignalSubscription.d.ts +9 -1
  132. package/dist/platforms/react/i18n/context.d.ts +18 -0
  133. package/dist/platforms/react/i18n/hooks.d.ts +65 -0
  134. package/dist/platforms/react/i18n/provider.d.ts +59 -0
  135. package/dist/platforms/react/index.d.ts +30 -0
  136. package/dist/react.d.ts +22 -0
  137. package/dist/react.js +505 -0
  138. package/dist/runtime-token.d.ts +1 -2
  139. package/dist/safe-effect-BazLOv1g.js +669 -0
  140. package/dist/schema-controller-CpiO2JoK.js +143 -0
  141. package/dist/signals.d.ts +15 -9
  142. package/dist/signals.js +11 -6
  143. package/dist/strategy-DzvnsWLh.js +375 -0
  144. package/dist/type-guards-D-N_5Ddh.js +19 -0
  145. package/dist/validation-issue-B9mjI8jz.js +7 -0
  146. package/dist/validation-message-Ced9MGxs.js +194 -0
  147. package/dist/validation.d.ts +1 -1
  148. package/dist/validation.js +11 -165
  149. package/dist/validators/email.d.ts +1 -1
  150. package/dist/validators/email.js +5 -8
  151. package/dist/validators/future-date.d.ts +1 -1
  152. package/dist/validators/future-date.js +10 -13
  153. package/dist/validators/integer.d.ts +1 -1
  154. package/dist/validators/integer.js +4 -7
  155. package/dist/validators/is-date.d.ts +1 -1
  156. package/dist/validators/is-date.js +5 -8
  157. package/dist/validators/is-number.d.ts +1 -1
  158. package/dist/validators/is-number.js +4 -7
  159. package/dist/validators/max-age.d.ts +1 -1
  160. package/dist/validators/max-age.js +8 -12
  161. package/dist/validators/max-date.d.ts +1 -1
  162. package/dist/validators/max-date.js +10 -13
  163. package/dist/validators/max-length.d.ts +1 -1
  164. package/dist/validators/max-length.js +7 -10
  165. package/dist/validators/max.d.ts +1 -1
  166. package/dist/validators/max.js +4 -7
  167. package/dist/validators/min-age.d.ts +1 -1
  168. package/dist/validators/min-age.js +10 -11
  169. package/dist/validators/min-date.d.ts +1 -1
  170. package/dist/validators/min-date.js +8 -11
  171. package/dist/validators/min-length.d.ts +1 -1
  172. package/dist/validators/min-length.js +4 -7
  173. package/dist/validators/min.d.ts +1 -1
  174. package/dist/validators/min.js +4 -7
  175. package/dist/validators/multiple-of.d.ts +1 -1
  176. package/dist/validators/multiple-of.js +11 -10
  177. package/dist/validators/non-negative.d.ts +1 -1
  178. package/dist/validators/non-negative.js +2 -5
  179. package/dist/validators/non-zero.d.ts +1 -1
  180. package/dist/validators/non-zero.js +4 -7
  181. package/dist/validators/past-date.d.ts +1 -1
  182. package/dist/validators/past-date.js +10 -13
  183. package/dist/validators/pattern.d.ts +1 -1
  184. package/dist/validators/pattern.js +5 -8
  185. package/dist/validators/phone.d.ts +1 -1
  186. package/dist/validators/phone.js +10 -11
  187. package/dist/validators/required.d.ts +1 -1
  188. package/dist/validators/required.js +4 -7
  189. package/dist/validators/url.d.ts +1 -1
  190. package/dist/validators/url.js +48 -17
  191. package/dist/validators.d.ts +1 -1
  192. package/dist/validators.js +86 -108
  193. package/docs/llms/01-api-reference.md +60 -34
  194. package/docs/llms/02-quick-start.md +112 -74
  195. package/docs/llms/03-api-signatures.md +369 -148
  196. package/docs/llms/04-common-patterns.md +58 -56
  197. package/docs/llms/05-common-mistakes.md +69 -44
  198. package/docs/llms/06-troubleshooting.md +14 -9
  199. package/docs/llms/07-complete-import.md +74 -43
  200. package/docs/llms/08-form-types.md +11 -4
  201. package/docs/llms/09-formschema.md +132 -84
  202. package/docs/llms/10-arrays.md +141 -61
  203. package/docs/llms/11-async-watchfield.md +2 -2
  204. package/docs/llms/13-multi-step.md +134 -63
  205. package/docs/llms/14-extended-mistakes.md +20 -17
  206. package/docs/llms/15-project-structure.md +224 -70
  207. package/docs/llms/16-ui-components.md +86 -46
  208. package/docs/llms/17-nonexistent-api.md +217 -32
  209. package/docs/llms/18-conditional-fields.md +71 -32
  210. package/docs/llms/19-reading-values.md +45 -4
  211. package/docs/llms/20-compute-vs-watch.md +25 -10
  212. package/docs/llms/21-array-operations.md +76 -26
  213. package/docs/llms/22-cycle-detection.md +1 -1
  214. package/docs/llms/23-copy-from.md +2 -2
  215. package/docs/llms/24-sync-fields.md +6 -5
  216. package/docs/llms/25-reset-when.md +14 -7
  217. package/docs/llms/26-transform-value.md +4 -4
  218. package/docs/llms/27-revalidate-when.md +16 -15
  219. package/docs/llms/28-submit-and-reset.md +278 -128
  220. package/docs/llms/29-async-preload.md +41 -31
  221. package/docs/llms/30-type-safety-recipes.md +105 -51
  222. package/docs/llms/31-async-validator-debounce.md +96 -53
  223. package/docs/llms/33-validation-strategy.md +178 -66
  224. package/docs/llms/34-i18n.md +298 -0
  225. package/docs/llms/35-unified-contract.md +309 -0
  226. package/docs/llms/36-issue-messages.md +284 -0
  227. package/docs/llms/37-compiled-schema.md +332 -0
  228. package/docs/llms/38-form-flow.md +396 -0
  229. package/llms-index.json +1 -0
  230. package/llms.txt +10690 -3089
  231. package/package.json +48 -9
  232. package/dist/behaviors-node-DGEFMTQu.js +0 -18
  233. package/dist/behaviors-value-C7dSEhCc.js +0 -134
  234. package/dist/date-utils-dTT_x_Be.js +0 -31
  235. package/dist/derived-registry-CluZCQ7U.js +0 -16
  236. package/dist/form/behaviors.d.ts +0 -133
  237. package/dist/form/error-handler.d.ts +0 -166
  238. package/dist/form/factories/index.d.ts +0 -6
  239. package/dist/form/factories/node-factory.d.ts +0 -188
  240. package/dist/form/form-submitter.d.ts +0 -121
  241. package/dist/form/nodes/array-node.d.ts +0 -276
  242. package/dist/form/nodes/field-node.d.ts +0 -294
  243. package/dist/form/nodes/form-node.d.ts +0 -326
  244. package/dist/form/nodes/group-node.d.ts +0 -284
  245. package/dist/form/nodes/model-array-node.d.ts +0 -121
  246. package/dist/form/status-machine.d.ts +0 -115
  247. package/dist/form/types/deep-schema.d.ts +0 -107
  248. package/dist/form/types/form-proxy.d.ts +0 -141
  249. package/dist/form/unique-id.d.ts +0 -45
  250. package/dist/form/validation/validators/date-utils.d.ts +0 -30
  251. package/dist/form/validation/validators/email.d.ts +0 -23
  252. package/dist/form/validation/validators/future-date.d.ts +0 -23
  253. package/dist/form/validation/validators/integer.d.ts +0 -22
  254. package/dist/form/validation/validators/is-date.d.ts +0 -23
  255. package/dist/form/validation/validators/is-number.d.ts +0 -24
  256. package/dist/form/validation/validators/max-age.d.ts +0 -25
  257. package/dist/form/validation/validators/max-date.d.ts +0 -25
  258. package/dist/form/validation/validators/max-files.d.ts +0 -26
  259. package/dist/form/validation/validators/max-length.d.ts +0 -27
  260. package/dist/form/validation/validators/max-total-file-size.d.ts +0 -26
  261. package/dist/form/validation/validators/max.d.ts +0 -25
  262. package/dist/form/validation/validators/min-age.d.ts +0 -25
  263. package/dist/form/validation/validators/min-date.d.ts +0 -25
  264. package/dist/form/validation/validators/min-files.d.ts +0 -26
  265. package/dist/form/validation/validators/min-length.d.ts +0 -27
  266. package/dist/form/validation/validators/min.d.ts +0 -25
  267. package/dist/form/validation/validators/multiple-of.d.ts +0 -24
  268. package/dist/form/validation/validators/non-negative.d.ts +0 -22
  269. package/dist/form/validation/validators/non-zero.d.ts +0 -22
  270. package/dist/form/validation/validators/past-date.d.ts +0 -23
  271. package/dist/form/validation/validators/pattern.d.ts +0 -33
  272. package/dist/form/validation/validators/required.d.ts +0 -32
  273. package/dist/form/validation/validators/url.d.ts +0 -43
  274. package/dist/form/validation-schema.d.ts +0 -106
  275. package/dist/form/validation-strategy.d.ts +0 -55
  276. package/dist/form/validators.d.ts +0 -11
  277. package/dist/form-model-DYCzGGKF.js +0 -286
  278. package/dist/state/index.d.ts +0 -31
  279. package/dist/state/subscription-manager.d.ts +0 -180
  280. package/dist/state/types.d.ts +0 -144
  281. package/dist/state.d.ts +0 -2
  282. package/dist/state.js +0 -21
  283. package/dist/subscription-manager-705bls97.js +0 -171
@@ -0,0 +1,2 @@
1
+ export * from './model/index'
2
+ export {}
package/dist/model.js ADDED
@@ -0,0 +1,27 @@
1
+ import { c as s, e as r, a as l } from "./create-model-DPSG3I0O.js";
2
+ import { a as d, g as o, f as t, d as c, i as f, b as n, c as m, m as u, r as p, s as b, e as g, u as h } from "./safe-effect-BazLOv1g.js";
3
+ import { c as k, a as v, r as D, b as F, s as x, t as y, w as C } from "./behaviors-value-D05z3my6.js";
4
+ export {
5
+ d as arrayOf,
6
+ k as computeFrom,
7
+ v as copyFrom,
8
+ s as createModel,
9
+ o as deepEqual,
10
+ t as deferredWritesSettled,
11
+ r as eachLeafSignal,
12
+ l as eachValueSignal,
13
+ c as isDerived,
14
+ f as isModelContainerSignal,
15
+ n as isValueSignal,
16
+ m as markDerived,
17
+ u as modelOf,
18
+ D as resetWhen,
19
+ F as revalidateWhen,
20
+ p as runOutsideEffect,
21
+ b as safeCallback,
22
+ g as safeDebouncedCallback,
23
+ x as syncFields,
24
+ y as transformValue,
25
+ h as unmarkDerived,
26
+ C as watchField
27
+ };
@@ -0,0 +1,15 @@
1
+ const n = Symbol.for("@reformer/core/form-node-kind"), e = Symbol.for("@reformer/core/form-node-raw");
2
+ function t(r) {
3
+ if (typeof r != "object" || r === null) return;
4
+ const o = r[n];
5
+ return o === "control" || o === "group" || o === "array" ? o : void 0;
6
+ }
7
+ function f(r) {
8
+ return r[e] ?? r;
9
+ }
10
+ export {
11
+ n as N,
12
+ e as R,
13
+ t as k,
14
+ f as r
15
+ };
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Адаптер поля — как свести value-based seam формы (`value` + `onChange(value)` + `onBlur`) к
3
+ * контракту конкретного контрола. Чистые данные и чистые функции (без React): один механизм на
4
+ * всех, кто связывает поле с контролом, — `FormField.Control` из `@reformer/cdk` и рендерер
5
+ * `@reformer/renderer-react`.
6
+ *
7
+ * Контрол объявляет свой диалект статикой `reformerAdapter` (см. {@link getFieldAdapter}), поэтому
8
+ * отдельные «field-версии» компонентов не нужны: в `component` поля кладётся сам компонент.
9
+ *
10
+ * @module platforms/react/field-binding
11
+ */
12
+ /**
13
+ * Адаптер поля: описывает диалект контрола (`checked`/`onCheckedChange`, `value`/`onValueChange`,
14
+ * DOM-событие в `onChange`, …). Все ключи необязательны — по умолчанию seam проходит как есть.
15
+ *
16
+ * @example Radix Checkbox
17
+ * ```ts
18
+ * const checkedAdapter: FieldAdapter = {
19
+ * valueProp: 'checked',
20
+ * changeProp: 'onCheckedChange',
21
+ * fromEmit: (c) => c === true,
22
+ * toValue: (v) => v ?? false,
23
+ * };
24
+ * ```
25
+ */
26
+ export interface FieldAdapter {
27
+ /** Проп, из которого контрол читает значение (по умолчанию `'value'`). */
28
+ valueProp?: string;
29
+ /** Колбэк, через который контрол эмитит изменение (по умолчанию `'onChange'`). */
30
+ changeProp?: string;
31
+ /**
32
+ * Эмит контрола → значение поля (по умолчанию — как есть). `rest` — прочие props контрола
33
+ * (например, чтобы достать `options` при резолве значения).
34
+ */
35
+ fromEmit?: (arg: unknown, rest: Record<string, unknown>) => unknown;
36
+ /** Значение поля → `valueProp` контрола (коэрсия `null`/`undefined`; по умолчанию — как есть). */
37
+ toValue?: (value: unknown) => unknown;
38
+ /** Проброс blur нестандартным каналом (по умолчанию — `onBlur`). */
39
+ bindBlur?: (onBlur: () => void) => Record<string, unknown>;
40
+ /** Ключи, которые убрать из `componentProps` перед спредом в контрол. */
41
+ strip?: string[];
42
+ /**
43
+ * Передавать ли контролу ноду формы пропом `control` (только рендерер). По умолчанию `false`:
44
+ * issues/touched обслуживает обёртка поля. Ставь `true`, если контрол сам вызывает
45
+ * `useFormControl(control)`.
46
+ */
47
+ passControl?: boolean;
48
+ }
49
+ /** Компонент, объявивший свой диалект статикой. */
50
+ export interface FieldAdapterCarrier {
51
+ reformerAdapter?: FieldAdapter;
52
+ }
53
+ /**
54
+ * Props, которые адресованы обёртке поля, а не контролу: `labelTooltip` рисует `FormField` у
55
+ * подписи. Они приходят в общем мешке `componentProps`, и без среза утекли бы в DOM контрола.
56
+ */
57
+ export declare const FIELD_WRAPPER_ONLY_PROPS: readonly string[];
58
+ /**
59
+ * Адаптер, объявленный компонентом статикой `reformerAdapter`, либо `undefined`.
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * Checkbox.reformerAdapter = checkedAdapter;
64
+ * getFieldAdapter(Checkbox); // → checkedAdapter
65
+ * ```
66
+ */
67
+ export declare function getFieldAdapter(component: unknown): FieldAdapter | undefined;
68
+ /** Seam поля: текущее значение и value-based колбэки. */
69
+ export interface FieldSeam {
70
+ value: unknown;
71
+ onChange: (value: unknown) => void;
72
+ onBlur: () => void;
73
+ }
74
+ /**
75
+ * Props контрола под адаптер: срезает служебные ключи обёртки и `strip` адаптера, кладёт значение
76
+ * в `valueProp` (через `toValue`), вешает `changeProp`, который прогоняет эмит контрола через
77
+ * `fromEmit` в value-based `onChange` поля, и пробрасывает blur (`bindBlur` либо `onBlur`).
78
+ * Без адаптера seam проходит как есть (`value` / `onChange` / `onBlur`).
79
+ *
80
+ * `componentProps` спредятся ПЕРВЫМИ — seam их перекрывает при совпадении ключей.
81
+ *
82
+ * @param adapter - Адаптер контрола или `undefined` (value-based контрол).
83
+ * @param seam - Значение поля и его колбэки.
84
+ * @param componentProps - Прочие props контрола.
85
+ * @returns Плоский объект props для спреда в контрол.
86
+ */
87
+ export declare function bindFieldProps(adapter: FieldAdapter | undefined, seam: FieldSeam, componentProps?: Record<string, unknown>): Record<string, unknown>;
@@ -0,0 +1,53 @@
1
+ import { Ref, RefObject } from 'react';
2
+ /**
3
+ * Базовый императивный контракт поля формы.
4
+ *
5
+ * Обёртка поля (`FormField.Control` из `@reformer/cdk`, рендерер `@reformer/renderer-react`)
6
+ * строит его из DOM-узла контрола. Композиты со своим handle (`open`/`close`/`reload`/…) отдают
7
+ * его как есть — он расширяет этот контракт.
8
+ *
9
+ * Достаётся из render-схемы по селектору:
10
+ * `schema.node(sel).getRef<FieldHandle>().current?.focus()`.
11
+ *
12
+ * Покрывает ТОЛЬКО императивные действия. Реактивное состояние (value / disabled / options /
13
+ * validation) живёт в behaviors и через handle не дублируется.
14
+ */
15
+ export interface FieldHandle {
16
+ /** Сфокусировать поле (делегирует на DOM-элемент контрола). */
17
+ focus(): void;
18
+ /** Снять фокус. */
19
+ blur(): void;
20
+ /** Проскроллить поле в область видимости. */
21
+ scrollIntoView(opts?: ScrollIntoViewOptions): void;
22
+ /** Живой DOM-элемент поля (или `null` до монтирования / для размонтированной ноды). */
23
+ getElement(): HTMLElement | null;
24
+ }
25
+ /**
26
+ * Собирает базовый {@link FieldHandle}, делегирующий на DOM-элемент по ссылке `el`.
27
+ * Все вызовы null-safe: до монтирования (`el.current === null`) — no-op, без исключений.
28
+ */
29
+ export declare function makeElementFieldHandle(el: RefObject<HTMLElement | null>): FieldHandle;
30
+ /**
31
+ * Ref для контрола поля + императивный handle для потребителя.
32
+ *
33
+ * Возвращённый ref вешается на контрол. На `outerRef` публикуется:
34
+ * - handle самого контрола, если тот его реализует (`useImperativeHandle` композита);
35
+ * - иначе — базовый {@link FieldHandle}, построенный из DOM-узла контрола.
36
+ *
37
+ * Хук вызывается безусловно; вешать ref на контрол стоит только при заданном `outerRef` —
38
+ * так function-компонент без ref-поддержки не получает лишний ref.
39
+ *
40
+ * @param outerRef - ref потребителя (из `forwardRef` или `schema.node(sel).getRef()`).
41
+ * @returns ref, который нужно передать контролу.
42
+ */
43
+ export declare function useFieldHandleRef<H = FieldHandle>(outerRef: Ref<H> | undefined): RefObject<unknown>;
44
+ /**
45
+ * Что публиковать потребителю по ref контрола: handle композита как есть, иначе (DOM-узел или
46
+ * ещё пусто) — базовый {@link FieldHandle}, читающий узел лениво из `inner`. С `cache` базовый
47
+ * handle создаётся один раз и дальше переиспользуется.
48
+ *
49
+ * @internal Экспортируется для юнит-тестов.
50
+ */
51
+ export declare function resolveFieldHandle(inner: RefObject<unknown>, cache?: {
52
+ current: FieldHandle | null;
53
+ }): unknown;
@@ -0,0 +1,27 @@
1
+ import { FormBundle } from '../../form/form-bundle';
2
+ /**
3
+ * Контекст сборки. Значение — бандл `createForm`; вне рендерера — `null`.
4
+ *
5
+ * @example
6
+ * ```tsx
7
+ * <FormBundleContext.Provider value={bundle}>
8
+ * <MyWizard />
9
+ * </FormBundleContext.Provider>
10
+ * ```
11
+ */
12
+ export declare const FormBundleContext: import('react').Context<FormBundle<any> | null>;
13
+ /**
14
+ * Бандл сборки, внутри которой отрисован компонент; `null` — компонент стоит вне рендерера
15
+ * (например, визард в разметке React-руками получает форму и поток шагов пропсами).
16
+ *
17
+ * @typeParam T - Форма данных модели.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * function StepFooter() {
22
+ * const bundle = useFormBundleContext<CreditForm>();
23
+ * return <button onClick={() => void bundle?.validate()}>Проверить</button>;
24
+ * }
25
+ * ```
26
+ */
27
+ export declare function useFormBundleContext<T = unknown>(): FormBundle<T> | null;
@@ -1,6 +1,6 @@
1
- import { ValidationError } from '../types/index';
1
+ import { ValidationIssue } from '../../../form/types/index';
2
2
  /**
3
- * Состояние поля формы, возвращаемое хуком {@link useFormControl} для {@link FieldNode}.
3
+ * Состояние поля формы, возвращаемое хуком {@link useFormControl} для {@link FormControl}.
4
4
  *
5
5
  * Содержит реактивные данные поля: значение, состояние валидации, флаги взаимодействия
6
6
  * и пользовательские props для компонентов.
@@ -10,11 +10,12 @@ import { ValidationError } from '../types/index';
10
10
  * @example Базовое использование
11
11
  * ```tsx
12
12
  * interface Props {
13
- * control: FieldNode<string>;
13
+ * control: FormControl<string>;
14
14
  * }
15
15
  *
16
16
  * function TextField({ control }: Props) {
17
17
  * const state = useFormControl(control);
18
+ * const message = useIssueMessage(); // из '@reformer/core/react'
18
19
  *
19
20
  * return (
20
21
  * <div>
@@ -23,8 +24,8 @@ import { ValidationError } from '../types/index';
23
24
  * disabled={state.disabled}
24
25
  * onChange={e => control.setValue(e.target.value)}
25
26
  * />
26
- * {state.shouldShowError && state.errors[0] && (
27
- * <span className="error">{state.errors[0].message}</span>
27
+ * {state.shouldShowIssues && state.issues[0] && (
28
+ * <span className="error">{message(state.issues[0])}</span>
28
29
  * )}
29
30
  * </div>
30
31
  * );
@@ -88,21 +89,22 @@ export interface FieldControlState<T> {
88
89
  *
89
90
  * @example
90
91
  * ```tsx
91
- * const { errors } = useFormControl(field);
92
+ * const { issues } = useFormControl(field);
93
+ * const message = useIssueMessage(); // из '@reformer/core/react'
92
94
  *
93
95
  * return (
94
96
  * <ul className="error-list">
95
- * {errors.map((error, i) => (
96
- * <li key={i}>{error.message}</li>
97
+ * {issues.map((error, i) => (
98
+ * <li key={i}>{message(error)}</li>
97
99
  * ))}
98
100
  * </ul>
99
101
  * );
100
102
  * ```
101
103
  */
102
- errors: ValidationError[];
104
+ issues: ValidationIssue[];
103
105
  /**
104
106
  * Флаг валидности поля.
105
- * `true` когда поле прошло все валидации (errors.length === 0).
107
+ * `true` когда поле прошло все валидации (issues.length === 0).
106
108
  *
107
109
  * @example
108
110
  * ```tsx
@@ -116,7 +118,7 @@ export interface FieldControlState<T> {
116
118
  valid: boolean;
117
119
  /**
118
120
  * Флаг невалидности поля.
119
- * `true` когда есть ошибки валидации (errors.length > 0).
121
+ * `true` когда есть ошибки валидации (issues.length > 0).
120
122
  * Противоположность {@link valid}.
121
123
  *
122
124
  * @example
@@ -146,27 +148,44 @@ export interface FieldControlState<T> {
146
148
  */
147
149
  touched: boolean;
148
150
  /**
149
- * Флаг для отображения ошибки.
150
- * Комбинация touched && invalid - удобный shortcut для UI.
151
+ * Показывать ли состояние ошибки — `aria-invalid`, рамку: поле невалидно и его трогали или
152
+ * меняли. Предупреждение (`severity: 'warning'`) поле невалидным не делает, поэтому для текстов
153
+ * замечаний есть {@link FieldControlState.shouldShowIssues}.
151
154
  *
152
155
  * @example
153
156
  * ```tsx
154
- * const { shouldShowError, errors } = useFormControl(field);
157
+ * const { shouldShowError } = useFormControl(field);
158
+ *
159
+ * return <input {...props} aria-invalid={shouldShowError} />;
160
+ * ```
161
+ */
162
+ shouldShowError: boolean;
163
+ /**
164
+ * Показывать ли тексты замечаний: у поля есть замечания — ошибки или предупреждения, — и его
165
+ * трогали или меняли.
166
+ *
167
+ * @example
168
+ * ```tsx
169
+ * const { shouldShowError, shouldShowIssues, issues } = useFormControl(field);
170
+ * const message = useIssueMessage(); // из '@reformer/core/react'
155
171
  *
156
172
  * return (
157
173
  * <div>
158
- * <input {...props} />
159
- * {shouldShowError && (
160
- * <span className="error">{errors[0]?.message}</span>
161
- * )}
174
+ * <input {...props} aria-invalid={shouldShowError} />
175
+ * {shouldShowIssues &&
176
+ * issues.map((issue) => (
177
+ * <span key={issue.code} className={issue.severity === 'warning' ? 'warning' : 'error'}>
178
+ * {message(issue)}
179
+ * </span>
180
+ * ))}
162
181
  * </div>
163
182
  * );
164
183
  * ```
165
184
  */
166
- shouldShowError: boolean;
185
+ shouldShowIssues: boolean;
167
186
  /**
168
187
  * Пользовательские props для передачи в UI-компоненты.
169
- * Устанавливаются через {@link FieldNode.updateComponentProps}.
188
+ * Устанавливаются через {@link FormControl.updateComponentProps}.
170
189
  *
171
190
  * @example
172
191
  * ```tsx
@@ -207,7 +226,7 @@ export interface FieldControlState<T> {
207
226
  dirty: boolean;
208
227
  }
209
228
  /**
210
- * Состояние массива формы, возвращаемое хуком {@link useFormControl} для {@link ArrayNode}.
229
+ * Состояние массива формы, возвращаемое хуком {@link useFormControl} для {@link FormArray}.
211
230
  *
212
231
  * Содержит реактивные данные массива: значения элементов, длину, состояние валидации
213
232
  * и флаги взаимодействия.
@@ -222,7 +241,7 @@ export interface FieldControlState<T> {
222
241
  * }
223
242
  *
224
243
  * interface Props {
225
- * control: ArrayNode<Phone>;
244
+ * control: FormArray<Phone>;
226
245
  * }
227
246
  *
228
247
  * function PhoneList({ control }: Props) {
@@ -232,7 +251,7 @@ export interface FieldControlState<T> {
232
251
  * <div>
233
252
  * {control.map((item, index) => (
234
253
  * <PhoneItem
235
- * key={item.id}
254
+ * key={control.keyAt(index)}
236
255
  * control={item}
237
256
  * onRemove={() => control.removeAt(index)}
238
257
  * />
@@ -244,7 +263,7 @@ export interface FieldControlState<T> {
244
263
  * Add Phone
245
264
  * </button>
246
265
  *
247
- * {!valid && <p className="error">Please fix phone errors</p>}
266
+ * {!valid && <p className="error">Please fix phone issues</p>}
248
267
  * </div>
249
268
  * );
250
269
  * }
@@ -290,23 +309,24 @@ export interface ArrayControlState<T> {
290
309
  */
291
310
  pending: boolean;
292
311
  /**
293
- * Массив ошибок валидации уровня массива.
294
- * Не включает ошибки отдельных элементов.
312
+ * Замечания массива: собственные (правила на ручке массива) и замечания всех его строк.
295
313
  *
296
314
  * @example
297
315
  * ```tsx
298
- * // Валидатор массива
299
- * validators.apply(phonesArray, {
300
- * validator: (phones) => phones.length >= 1,
301
- * message: 'At least one phone required'
302
- * });
316
+ * // Правило массива — в схеме валидации
317
+ * validate(model.$.phones, [minLength(1, { code: 'phones.minLength' })]);
303
318
  *
304
319
  * // В компоненте
305
- * const { errors } = useFormControl(phonesArray);
306
- * // errors содержит ошибку "At least one phone required" если массив пуст
320
+ * const { issues } = useFormControl(phonesArray);
321
+ * // для пустого массива issues содержит замечание с кодом 'phones.minLength'
307
322
  * ```
308
323
  */
309
- errors: ValidationError[];
324
+ issues: ValidationIssue[];
325
+ /**
326
+ * Собственные замечания массива — только правила на его ручке (`minLength` и т.п.), без
327
+ * замечаний строк. Их показывает ошибка уровня массива (`FieldArray.Error`).
328
+ */
329
+ ownIssues: ValidationIssue[];
310
330
  /**
311
331
  * Флаг валидности массива и всех его элементов.
312
332
  * `true` только когда массив и все вложенные элементы валидны.
@@ -324,7 +344,9 @@ export interface ArrayControlState<T> {
324
344
  touched: boolean;
325
345
  /**
326
346
  * Флаг изменения.
327
- * `true` когда значение массива отличается от начального.
347
+ * `true` после правки любого элемента или изменения состава через форму (`push`, `removeAt`,
348
+ * `move`, `setValue`…). Как у поля, запись в модель (загрузка данных) и операция, которая состав
349
+ * не поменяла, изменением не считаются; снимает `reset`.
328
350
  *
329
351
  * @example
330
352
  * ```tsx
@@ -341,7 +363,7 @@ export interface ArrayControlState<T> {
341
363
  dirty: boolean;
342
364
  /**
343
365
  * Флаг отключения массива.
344
- * `true` когда массив отключён (`ArrayNode.disable()`), в том числе через
366
+ * `true` когда массив отключён (`FormArray.disable()`), в том числе через
345
367
  * распространение disable от родительской группы.
346
368
  *
347
369
  * Используйте для отключения UI-действий добавления/удаления, когда массив
@@ -0,0 +1,41 @@
1
+ import { FormValidationController } from '../../../form/validation/strategy';
2
+ /** Минимум, который хук ожидает от бандла: контроллер живой валидации и снятие — оба опционально. */
3
+ export interface FormBundleLike {
4
+ validation?: {
5
+ controller: FormValidationController;
6
+ };
7
+ /** Снять сборку: поведение, подписки нод, живую проверку. Есть у всех фабрик семейства. */
8
+ dispose?(): void;
9
+ }
10
+ /**
11
+ * Собрать форму один раз и держать её стабильной; живую стратегию валидации армировать в эффекте
12
+ * (не при SSR — там эффекты не выполняются); при размонтировании снять сборку.
13
+ *
14
+ * @typeParam B - Тип бандла конкретной фабрики.
15
+ * @param factory - Фабрика бандла, обычно `() => createForm({…})`. Сборкой владеет хук: при
16
+ * размонтировании он вызывает её `dispose()`, поэтому фабрика должна собирать НОВУЮ форму, а не
17
+ * возвращать чужую, живущую дольше компонента.
18
+ * @returns Стабильный бандл.
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * const credit = useFormBundle(() => createForm<CreditForm>({ model: createCreditModel() }));
23
+ * ```
24
+ *
25
+ * @remarks
26
+ * Передавайте именно фабрику: `useState` трактует функцию как ленивый инициализатор, а некоторые
27
+ * части бандла сами являются функциями (`RenderSchemaProxy`), поэтому `useFormBundle(bundle.render)`
28
+ * вызвал бы её вместо сохранения.
29
+ *
30
+ * Сборка снимается микрозадачей после размонтирования, а не в самой очистке эффекта (см. шапку
31
+ * модуля): код, который сразу после размонтирования пишет в модель, ещё видит живое поведение.
32
+ *
33
+ * **Ограничение StrictMode (только разработка).** StrictMode вызывает ленивый инициализатор
34
+ * `useState` дважды, и фабрика на монтирование срабатывает два раза. Второй бандл React
35
+ * выбрасывает до коммита: хук его не видит, поэтому не армирует и не снимает. Живая проверка у
36
+ * него не запущена, но поведение (`compute`, `onChange`, …) работает, пока бандл не соберёт
37
+ * сборщик мусора. В production инициализатор вызывается один раз и утечки нет. Побочные эффекты
38
+ * на создание (запрос данных в фабрике или в `setup`) в разработке поэтому тоже срабатывают
39
+ * дважды — запускайте их из поведения формы или из эффекта компонента.
40
+ */
41
+ export declare function useFormBundle<B extends FormBundleLike>(factory: () => B): B;
@@ -0,0 +1,40 @@
1
+ import { FormFlow } from '../../../form/flow/flow';
2
+ /**
3
+ * Состояние потока на момент рендера.
4
+ *
5
+ * @group React
6
+ */
7
+ export interface FormFlowSnapshot {
8
+ /** Текущий шаг. */
9
+ readonly current: string;
10
+ /** Номер текущего шага с нуля. */
11
+ readonly index: number;
12
+ /** Пройденные шаги. */
13
+ readonly completed: ReadonlySet<string>;
14
+ readonly isFirst: boolean;
15
+ readonly isLast: boolean;
16
+ /** Идёт ли проверка шага. */
17
+ readonly validating: boolean;
18
+ }
19
+ /**
20
+ * Подписаться на поток шагов: компонент перерисовывается при смене шага, пройденных шагов и
21
+ * состояния проверки. Переходы — методами самого потока (`flow.next()`, `flow.goTo(id)`).
22
+ *
23
+ * @param flow - Поток шагов (`bundle.flow`).
24
+ * @returns {@link FormFlowSnapshot}.
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * function Steps({ flow }: { flow: FormFlow }) {
29
+ * const { current, isLast, validating } = useFormFlow(flow);
30
+ * return (
31
+ * <button disabled={isLast || validating} onClick={() => void flow.next()}>
32
+ * Далее ({current})
33
+ * </button>
34
+ * );
35
+ * }
36
+ * ```
37
+ *
38
+ * @group React
39
+ */
40
+ export declare function useFormFlow(flow: FormFlow): FormFlowSnapshot;
@@ -1,11 +1,11 @@
1
- import { FormModel } from '../../index';
2
- import { ValidationSchema } from '../validation-schema';
3
- import { ValidationStrategyKind } from '../validation-strategy';
1
+ import { FormModel } from '../../../model/types';
2
+ import { ValidationDefinition } from '../../../form/validation';
3
+ import { ValidationStrategyKind } from '../../../form/validation/strategy';
4
4
  export interface UseFormValidationArgs<T> {
5
5
  /** Модель данных формы. */
6
6
  model: FormModel<T>;
7
7
  /** Схема валидации. **Стабильная ссылка** (module-level `const` / `useMemo`) — иначе ломается дедуп раннера. */
8
- schema: ValidationSchema<T>;
8
+ schema: ValidationDefinition<T>;
9
9
  /** Стратегия запуска. Default `'submit'`. */
10
10
  strategy?: ValidationStrategyKind;
11
11
  /** Debounce (мс) для live-фаз (`change` / live-часть `afterFirstSubmit`). */
@@ -14,9 +14,15 @@ export interface UseFormValidationArgs<T> {
14
14
  liveAfterSubmit?: 'change' | 'blur';
15
15
  }
16
16
  export interface UseFormValidationResult {
17
- /** Полный прогон + раскрытие ошибок (`touch:true`); для `onSubmit`. Стабильная ссылка. */
17
+ /**
18
+ * Попытка отправки: полный прогон; при замечаниях раскрыта вся форма, `afterFirstSubmit`
19
+ * переходит в живую фазу. `true` — только статус `valid`. Для `onSubmit`. Стабильная ссылка.
20
+ */
18
21
  submit: () => Promise<boolean>;
19
- /** Алиас `submit` для не-submit сценариев. */
22
+ /**
23
+ * Проверка без отправки: полный прогон, раскрыты замечания проверенных полей, фаза
24
+ * `afterFirstSubmit` не меняется. Для кнопки «Проверить». Стабильная ссылка.
25
+ */
20
26
  validate: () => Promise<boolean>;
21
27
  /** Идёт ли прогон (submit или live) — для блокировки кнопки. */
22
28
  isValidating: boolean;
@@ -31,7 +37,7 @@ export interface UseFormValidationResult {
31
37
  *
32
38
  * @example
33
39
  * ```tsx
34
- * const validation = defineValidationSchema<Form>(({ model }) => { ... }); // module-level
40
+ * const validation = defineValidation<Form>(({ model }) => { ... }); // module-level
35
41
  *
36
42
  * function MyForm() {
37
43
  * const { submit, isValidating } = useFormValidation({
@@ -1,13 +1,13 @@
1
- import { ArrayNode } from '../nodes/array-node';
1
+ import { FormArray } from '../../../form/types/index';
2
2
  /**
3
3
  * React-хук для подписки только на длину массива.
4
4
  *
5
- * Оптимизированная версия {@link useFormControl} для ArrayNode, которая
6
- * подписывается только на сигнал `length`. Компонент не будет ре-рендериться
5
+ * Оптимизированная версия {@link useFormControl} для FormArray, которая
6
+ * подписывается только на длину массива. Компонент не будет ре-рендериться
7
7
  * при изменении значений вложенных полей.
8
8
  *
9
9
  * @typeParam T - Тип элемента массива
10
- * @param control - ArrayNode для подписки
10
+ * @param control - FormArray для подписки
11
11
  * @returns Текущая длина массива
12
12
  *
13
13
  * @example
@@ -18,7 +18,7 @@ import { ArrayNode } from '../nodes/array-node';
18
18
  * return (
19
19
  * <div>
20
20
  * {arrayNode.map((item, index) => (
21
- * <ItemRenderer key={item.id} item={item} />
21
+ * <ItemRenderer key={arrayNode.keyAt(index)} item={item} />
22
22
  * ))}
23
23
  * </div>
24
24
  * );
@@ -27,4 +27,4 @@ import { ArrayNode } from '../nodes/array-node';
27
27
  *
28
28
  * @group React Hooks
29
29
  */
30
- export declare function useArrayLength<T extends object>(control: ArrayNode<T>): number;
30
+ export declare function useArrayLength<T extends object>(control: FormArray<T>): number;
@@ -1,24 +1,22 @@
1
- import { FieldNode } from '../nodes/field-node';
2
- import { ArrayNode } from '../nodes/array-node';
3
- import { FormValue } from '../types/index';
1
+ import { FormArray, FormControl } from '../../../form/types/index';
4
2
  import { FieldControlState, ArrayControlState } from './types';
5
3
  /**
6
- * React-хук для подписки на состояние {@link ArrayNode}.
4
+ * React-хук для подписки на состояние {@link FormArray}.
7
5
  *
8
6
  * @typeParam T - Тип элемента массива
9
- * @param control - ArrayNode или undefined
7
+ * @param control - FormArray или undefined
10
8
  * @returns Состояние массива {@link ArrayControlState}
11
9
  *
12
10
  * @group React Hooks
13
11
  */
14
- export declare function useFormControl<T extends object>(control: ArrayNode<T> | undefined): ArrayControlState<T>;
12
+ export declare function useFormControl<T extends object>(control: FormArray<T> | undefined): ArrayControlState<T>;
15
13
  /**
16
- * React-хук для подписки на состояние {@link FieldNode}.
14
+ * React-хук для подписки на состояние {@link FormControl}.
17
15
  *
18
16
  * @typeParam T - Тип значения поля
19
- * @param control - FieldNode для подписки
17
+ * @param control - FormControl для подписки
20
18
  * @returns Состояние поля {@link FieldControlState}
21
19
  *
22
20
  * @group React Hooks
23
21
  */
24
- export declare function useFormControl<T extends FormValue>(control: FieldNode<T>): FieldControlState<T>;
22
+ export declare function useFormControl<T>(control: FormControl<T>): FieldControlState<T>;