@reformer/core 11.0.0-beta.2 → 11.0.0-beta.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.
@@ -1,6 +1,54 @@
1
- import { signal as M, effect as E } from "@preact/signals-core";
2
- import { e as L } from "./form-model-DhW2HZOB.js";
1
+ import { signal as A, effect as M } from "@preact/signals-core";
2
+ import { e as E } from "./form-model-DhW2HZOB.js";
3
3
  import { g as p } from "./signal-node-registry-tiwel-TV.js";
4
+ function B(e, n, t = {}) {
5
+ const { strategy: r = "submit", debounce: o = 0, liveAfterSubmit: i = "change" } = t, s = A(!1);
6
+ let a = 0, c = !1, d, l;
7
+ const w = async (u) => {
8
+ a++, s.value = !0;
9
+ try {
10
+ return await W(e, n, { touch: u });
11
+ } finally {
12
+ --a === 0 && (s.value = !1);
13
+ }
14
+ }, h = () => {
15
+ w(!1);
16
+ }, F = () => {
17
+ if (!o) {
18
+ h();
19
+ return;
20
+ }
21
+ l && clearTimeout(l), l = setTimeout(h, o);
22
+ }, k = (u, g) => {
23
+ let S = !0;
24
+ return M(() => {
25
+ if (E(e, u), S) {
26
+ S = !1;
27
+ return;
28
+ }
29
+ r === "afterFirstSubmit" && !c || g();
30
+ });
31
+ }, m = {
32
+ async validate() {
33
+ return c = !0, w(!0);
34
+ },
35
+ start() {
36
+ if (d) return () => m.dispose();
37
+ const u = r === "afterFirstSubmit" ? i : r;
38
+ return u === "change" ? d = k((g) => void g.value, F) : u === "blur" && (d = k((g) => void p(g)?.touched.value, h)), () => m.dispose();
39
+ },
40
+ dispose() {
41
+ l && (clearTimeout(l), l = void 0), d?.(), d = void 0;
42
+ },
43
+ get isValidating() {
44
+ return s.value;
45
+ },
46
+ get validating() {
47
+ return s;
48
+ }
49
+ };
50
+ return m;
51
+ }
4
52
  let v = null;
5
53
  function f(e) {
6
54
  if (!v)
@@ -9,83 +57,83 @@ function f(e) {
9
57
  );
10
58
  return v;
11
59
  }
12
- function b(e, r) {
13
- let t = e.errors.get(r);
14
- return t || e.errors.set(r, t = []), t;
60
+ function b(e, n) {
61
+ let t = e.errors.get(n);
62
+ return t || e.errors.set(n, t = []), t;
15
63
  }
16
- const y = (e) => e.whenStack.every((r) => r());
17
- function D(e, r) {
18
- const t = f("validate"), n = b(t, e);
64
+ const y = (e) => e.whenStack.every((n) => n());
65
+ function D(e, n) {
66
+ const t = f("validate"), r = b(t, e);
19
67
  if (!y(t)) return;
20
68
  const o = e.peek();
21
- for (const i of r) {
69
+ for (const i of n) {
22
70
  const s = i(o, t.model, t.model);
23
- s && n.push(s);
71
+ s && r.push(s);
24
72
  }
25
73
  }
26
- function N(e, r) {
27
- const t = f("validateAsync"), n = b(t, e);
74
+ function N(e, n) {
75
+ const t = f("validateAsync"), r = b(t, e);
28
76
  if (!y(t)) return;
29
77
  const o = e.peek(), i = t.signal;
30
- for (const s of r)
78
+ for (const s of n)
31
79
  t.pending.push(
32
80
  s(o, { signal: i }).then(
33
81
  (a) => {
34
- a && !i.aborted && n.push(a);
82
+ a && !i.aborted && r.push(a);
35
83
  },
36
84
  () => {
37
85
  }
38
86
  )
39
87
  );
40
88
  }
41
- function R(e, r) {
89
+ function R(e, n) {
42
90
  const t = f("validateWhen");
43
91
  t.whenStack.push(e);
44
92
  try {
45
- r();
93
+ n();
46
94
  } finally {
47
95
  t.whenStack.pop();
48
96
  }
49
97
  }
50
- function _(e, r) {
51
- const t = f("cross"), n = b(t, e);
98
+ function _(e, n) {
99
+ const t = f("cross"), r = b(t, e);
52
100
  if (!y(t)) return;
53
- const o = r(t.model.get());
54
- o && n.push(o);
101
+ const o = n(t.model.get());
102
+ o && r.push(o);
55
103
  }
56
- function $(e, r) {
104
+ function $(e, n) {
57
105
  f("each");
58
106
  const t = e.length;
59
- for (let n = 0; n < t; n++) r(e.at(n));
107
+ for (let r = 0; r < t; r++) n(e.at(r));
60
108
  }
61
109
  function j(...e) {
62
110
  const t = f("apply").model;
63
- for (const n of e) n({ model: t });
111
+ for (const r of e) r({ model: t });
64
112
  }
65
113
  function z(e) {
66
114
  return e;
67
115
  }
68
- const F = /* @__PURE__ */ new WeakMap();
69
- function P(e, r) {
70
- let t = F.get(e);
71
- t || F.set(e, t = /* @__PURE__ */ new WeakMap());
72
- let n = t.get(r);
73
- return n || t.set(r, n = { fields: /* @__PURE__ */ new Set(), ac: null }), n;
116
+ const x = /* @__PURE__ */ new WeakMap();
117
+ function L(e, n) {
118
+ let t = x.get(e);
119
+ t || x.set(e, t = /* @__PURE__ */ new WeakMap());
120
+ let r = t.get(n);
121
+ return r || t.set(n, r = { fields: /* @__PURE__ */ new Set(), ac: null }), r;
74
122
  }
75
- const T = (e) => {
76
- for (const r of e.values()) for (const t of r) if (t.severity !== "warning") return !0;
123
+ const P = (e) => {
124
+ for (const n of e.values()) for (const t of n) if (t.severity !== "warning") return !0;
77
125
  return !1;
78
126
  };
79
- function W(e) {
80
- return e.aborted ? Promise.resolve() : new Promise((r) => {
81
- e.addEventListener("abort", () => r(), { once: !0 });
127
+ function T(e) {
128
+ return e.aborted ? Promise.resolve() : new Promise((n) => {
129
+ e.addEventListener("abort", () => n(), { once: !0 });
82
130
  });
83
131
  }
84
- async function V(e, r, t) {
85
- const n = P(e, r);
86
- n.ac?.abort();
132
+ async function W(e, n, t) {
133
+ const r = L(e, n);
134
+ r.ac?.abort();
87
135
  const o = new AbortController();
88
- n.ac = o;
136
+ r.ac = o;
89
137
  const i = {
90
138
  model: e,
91
139
  errors: /* @__PURE__ */ new Map(),
@@ -95,73 +143,25 @@ async function V(e, r, t) {
95
143
  }, s = v;
96
144
  v = i;
97
145
  try {
98
- r({ model: e });
146
+ n({ model: e });
99
147
  } finally {
100
148
  v = s;
101
149
  }
102
- if (i.pending.length && await Promise.race([Promise.all(i.pending), W(o.signal)]), o.signal.aborted) return !1;
150
+ if (i.pending.length && await Promise.race([Promise.all(i.pending), T(o.signal)]), o.signal.aborted) return !1;
103
151
  const a = new Set(i.errors.keys());
104
- for (const c of n.fields) a.has(c) || p(c)?.setErrors([]);
152
+ for (const c of r.fields) a.has(c) || p(c)?.setErrors([]);
105
153
  for (const c of a) p(c)?.setErrors(i.errors.get(c) ?? []);
106
154
  if (t?.touch) for (const c of a) p(c)?.markAsTouched();
107
- return n.fields = a, !T(i.errors);
108
- }
109
- function G(e, r, t = {}) {
110
- const { strategy: n = "submit", debounce: o = 0, liveAfterSubmit: i = "change" } = t, s = M(!1);
111
- let a = 0, c = !1, w = !1, d, l;
112
- const k = async (u) => {
113
- a++, s.value = !0;
114
- try {
115
- return await V(e, r, { touch: u });
116
- } finally {
117
- --a === 0 && (s.value = !1);
118
- }
119
- }, h = () => {
120
- k(!1);
121
- }, A = () => {
122
- if (!o) {
123
- h();
124
- return;
125
- }
126
- l && clearTimeout(l), l = setTimeout(h, o);
127
- }, S = (u, g) => {
128
- let x = !0;
129
- return E(() => {
130
- if (L(e, u), x) {
131
- x = !1;
132
- return;
133
- }
134
- n === "afterFirstSubmit" && !c || g();
135
- });
136
- }, m = {
137
- async validate() {
138
- return c = !0, k(!0);
139
- },
140
- start() {
141
- if (w || d) return () => m.dispose();
142
- const u = n === "afterFirstSubmit" ? i : n;
143
- return u === "change" ? d = S((g) => void g.value, A) : u === "blur" && (d = S((g) => void p(g)?.touched.value, h)), () => m.dispose();
144
- },
145
- dispose() {
146
- w = !0, l && (clearTimeout(l), l = void 0), d?.(), d = void 0;
147
- },
148
- get isValidating() {
149
- return s.value;
150
- },
151
- get validating() {
152
- return s;
153
- }
154
- };
155
- return m;
155
+ return r.fields = a, !P(i.errors);
156
156
  }
157
157
  export {
158
158
  j as apply,
159
- G as createFormValidation,
159
+ B as createFormValidation,
160
160
  _ as cross,
161
161
  z as defineValidationSchema,
162
162
  $ as each,
163
163
  D as validate,
164
164
  N as validateAsync,
165
- V as validateModel,
165
+ W as validateModel,
166
166
  R as validateWhen
167
167
  };
@@ -7,14 +7,15 @@
7
7
  > `Checkbox`-обёртки с `label`-prop'ами — это anti-pattern. См.
8
8
  > `find_recipe(package="@reformer/ui-kit", topic="form-field-integration")`.
9
9
 
10
- Архитектура M1: сначала создаётся **модель данных** (`createModel`), затем **форма**
11
- (`createForm({ model, schema })`), где схема привязывает поля к сигналам модели
12
- (`model.$.field`). Layout-схема НЕ несёт валидаторов — валидация живёт в отдельной
10
+ Архитектура M1: **модель данных** — источник истины, схема привязывает поля к её сигналам
11
+ (`model.$.field`), а сборка идёт ОДНИМ вызовом `createCoreForm` (для рендера ui-kit;
12
+ в рендерерах — `createReactForm` / `createJsonForm`), который создаёт модель, строит форму,
13
+ запускает поведение и собирает валидацию. Layout-схема НЕ несёт валидаторов — валидация живёт в отдельной
13
14
  схеме `defineValidationSchema` из `@reformer/core/validation` и запускается внешним
14
15
  раннером `validateModel(model, schema)`.
15
16
 
16
17
  ```typescript
17
- import { createModel, createForm, type FormProxy } from '@reformer/core';
18
+ import { createCoreForm, createModel, useFormBundle, type FormProxy } from '@reformer/core';
18
19
  import { defineValidationSchema, validate, validateModel } from '@reformer/core/validation';
19
20
  import { required, email } from '@reformer/core/validators';
20
21
  import { FormField, Input, Button } from '@reformer/ui-kit';
@@ -49,8 +50,14 @@ const contactValidation = defineValidationSchema<ContactForm>(({ model }) => {
49
50
  validate(model.$.email, [required({ message: 'Email is required' }), email({ message: 'Invalid email' })]);
50
51
  });
51
52
 
52
- // 5. Form — ноды поверх сигналов модели
53
- const form = createForm<ContactForm>({ model, schema });
53
+ // 5. Сборка ОДНИМ вызовом: модель + ноды поверх её сигналов + валидация.
54
+ // В React оборачивают в useFormBundle — ленивый useState, фабрика зовётся один раз.
55
+ const contact = createCoreForm<ContactForm>({
56
+ model,
57
+ schema: buildSchema, // билдер (model) => tree
58
+ validation: contactValidation,
59
+ });
60
+ const form = contact.form;
54
61
 
55
62
  // 6. Use in React component — thin JSX, FormField does ALL heavy lifting
56
63
  function ContactFormComponent() {
@@ -121,7 +128,7 @@ const schema = {
121
128
  properties: { array: model.properties, item: propertyItem },
122
129
  };
123
130
 
124
- const form = createForm<MyForm>({ model, schema });
131
+ const { form } = createCoreForm<MyForm>({ model, schema: () => schema });
125
132
 
126
133
  // Операции над массивом — на модели:
127
134
  model.properties.push({ type: 'apartment', description: '', estimatedValue: 0 });
@@ -15,7 +15,7 @@ src/
15
15
  │
16
16
  ├── forms/
17
17
  │ └── [form-name]/ # Form module — flat, one file per concern
18
- │ ├── index.tsx # entry + whole form: createModel→createForm→<FormWizard> with all steps inline; arrays via FormArraySection
18
+ │ ├── index.tsx # entry + whole form: ONE createCoreForm call → <FormWizard> with all steps inline; arrays via FormArraySection
19
19
  │ ├── types.ts # form type + enums + { value, label } option type + constant dictionaries
20
20
  │ ├── model.ts # createModel + initial values + empty-array-element factories
21
21
  │ ├── form.schema.ts # FormSchema: { value: model.$.x, component, componentProps }
@@ -60,11 +60,15 @@ export const creditApplicationBehavior = defineFormBehavior<CreditApplicationFor
60
60
  });
61
61
 
62
62
  // forms/credit-application/index.tsx — entry: assembles the form, renders <FormWizard> with all steps inline
63
- import { createForm } from '@reformer/core';
64
- export const createCreditApplicationForm = () => {
65
- const model = createCreditApplicationModel();
66
- return createForm({ model, schema: creditApplicationSchema(model), behavior: creditApplicationBehavior });
67
- };
63
+ import { createCoreForm } from '@reformer/core';
64
+ // ONE call: model + form + behavior + validation. In the page: useFormBundle(createCreditApplicationForm).
65
+ export const createCreditApplicationForm = () =>
66
+ createCoreForm({
67
+ model: createCreditApplicationModel(),
68
+ schema: creditApplicationSchema, // builder (model) => tree
69
+ behavior: creditApplicationBehavior,
70
+ validation: creditApplicationValidation, // { steps, extras } → bundle.validation
71
+ });
68
72
  ```
69
73
 
70
74
  ### Scaling up: folders (large forms)
@@ -90,10 +94,10 @@ forms/
90
94
  │
91
95
  ├── schema/ # The form definition
92
96
  │ ├── model.ts # createModel factory + initial values + array-element factories
93
- │ ├── schema.ts # createForm schema tree ({ value: model.$.x, component })
97
+ │ ├── schema.ts # schema builder (model) => tree ({ value: model.$.x, component })
94
98
  │ ├── validation.ts # defineValidationSchema + validateModel config ({ validateStep, validateAll })
95
99
  │ ├── behavior.ts # defineFormBehavior(...)
96
- │ └── create-form.ts # Assembly: createForm({ model, schema, behavior })
100
+ │ └── create-form.ts # Assembly: createCoreForm({ model, schema, behavior, validation })
97
101
  │
98
102
  └── components/
99
103
  ├── steps/ # One component per wizard step