@reformer/core 11.0.0-beta.7 → 11.0.0-beta.9
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/README.md +90 -68
- package/dist/behaviors-value-HA_Q03hc.js +108 -0
- package/dist/behaviors.js +382 -217
- package/dist/create-model-BvcbNPgL.js +30 -0
- package/dist/date-utils-90-qZbse.js +43 -0
- package/dist/{en-CjAqn7h0.js → en-HP8XEych.js} +6 -3
- package/dist/form/aggregate-signals.d.ts +21 -63
- package/dist/form/behaviors/collections.d.ts +42 -8
- package/dist/form/behaviors/context.d.ts +37 -4
- package/dist/form/behaviors/index.d.ts +6 -4
- package/dist/form/behaviors/internals.d.ts +9 -3
- package/dist/form/behaviors/node.d.ts +25 -2
- package/dist/form/behaviors/operators.d.ts +17 -9
- package/dist/form/behaviors/schema-operators.d.ts +78 -0
- package/dist/form/behaviors/types.d.ts +25 -5
- package/dist/form/create-form.d.ts +57 -70
- package/dist/form/flow/flow.d.ts +87 -0
- package/dist/form/form-bundle.d.ts +153 -0
- package/dist/form/form-config-base.d.ts +46 -0
- package/dist/form/form-level.d.ts +121 -0
- package/dist/form/form-proxy-builder.d.ts +6 -22
- package/dist/form/index.d.ts +20 -20
- package/dist/form/nodes/form-array-node.d.ts +100 -0
- package/dist/form/nodes/form-control-node.d.ts +73 -0
- package/dist/form/nodes/form-group-node.d.ts +66 -0
- package/dist/form/nodes/node-kind.d.ts +29 -0
- package/dist/form/nodes/state/availability.d.ts +6 -0
- package/dist/form/nodes/state/component.d.ts +6 -0
- package/dist/form/nodes/state/index.d.ts +12 -0
- package/dist/form/nodes/state/interaction.d.ts +13 -0
- package/dist/form/nodes/state/issues.d.ts +9 -0
- package/dist/form/nodes/state/pending.d.ts +6 -0
- package/dist/form/schema/compile.d.ts +59 -0
- package/dist/form/schema/compiled.d.ts +143 -0
- package/dist/form/schema/row-key.d.ts +11 -0
- package/dist/form/schema-controller.d.ts +153 -0
- package/dist/form/schema-subtree.d.ts +28 -0
- package/dist/form/signal-node-registry.d.ts +27 -22
- package/dist/form/submission.d.ts +93 -0
- package/dist/form/type-guards.d.ts +14 -73
- package/dist/form/types/contracts.d.ts +19 -92
- package/dist/form/types/field-config.d.ts +27 -0
- package/dist/form/types/index.d.ts +5 -57
- package/dist/form/types/nodes.d.ts +159 -0
- package/dist/form/types/schema-node.d.ts +147 -46
- package/dist/form/types/validation-schema.d.ts +23 -27
- package/dist/form/validation/context.d.ts +46 -6
- package/dist/form/validation/index.d.ts +9 -8
- package/dist/form/validation/operators.d.ts +71 -17
- package/dist/form/validation/run.d.ts +93 -18
- package/dist/form/validation/strategy.d.ts +41 -13
- package/dist/form/validation/types.d.ts +74 -19
- package/dist/form/validators/date-utils.d.ts +13 -1
- package/dist/form/validators/email.d.ts +8 -7
- package/dist/form/validators/file-type.d.ts +8 -7
- package/dist/form/validators/file-utils.d.ts +2 -1
- package/dist/form/validators/future-date.d.ts +8 -7
- package/dist/form/validators/index.d.ts +1 -1
- package/dist/form/validators/integer.d.ts +8 -7
- package/dist/form/validators/is-date.d.ts +11 -9
- package/dist/form/validators/is-number.d.ts +11 -9
- package/dist/form/validators/max-age.d.ts +13 -9
- package/dist/form/validators/max-date.d.ts +8 -7
- package/dist/form/validators/max-file-size.d.ts +8 -7
- package/dist/form/validators/max-files.d.ts +8 -7
- package/dist/form/validators/max-length.d.ts +14 -7
- package/dist/form/validators/max-total-file-size.d.ts +8 -7
- package/dist/form/validators/max.d.ts +10 -8
- package/dist/form/validators/min-age.d.ts +12 -9
- package/dist/form/validators/min-date.d.ts +8 -7
- package/dist/form/validators/min-file-size.d.ts +8 -7
- package/dist/form/validators/min-files.d.ts +11 -9
- package/dist/form/validators/min-length.d.ts +14 -7
- package/dist/form/validators/min.d.ts +10 -8
- package/dist/form/validators/multiple-of.d.ts +10 -8
- package/dist/form/validators/non-negative.d.ts +8 -7
- package/dist/form/validators/non-zero.d.ts +8 -7
- package/dist/form/validators/past-date.d.ts +8 -7
- package/dist/form/validators/pattern.d.ts +9 -8
- package/dist/form/validators/phone.d.ts +8 -7
- package/dist/form/validators/required.d.ts +11 -9
- package/dist/form/validators/url.d.ts +20 -13
- package/dist/form/validators/validation-issue.d.ts +8 -0
- package/dist/i18n/descriptor.d.ts +1 -1
- package/dist/i18n/en.json.d.ts +5 -1
- package/dist/i18n/locale-store.d.ts +7 -0
- package/dist/i18n/message-format.d.ts +24 -4
- package/dist/i18n/ru.json.d.ts +5 -1
- package/dist/i18n/translator.d.ts +11 -0
- package/dist/i18n/validation-message.d.ts +42 -7
- package/dist/i18n.d.ts +7 -12
- package/dist/i18n.js +66 -283
- package/dist/index.d.ts +16 -7
- package/dist/index.js +958 -2798
- package/dist/loader-BWkigpVe.js +52 -0
- package/dist/locale/en.js +1 -1
- package/dist/locale/en.json +4 -1
- package/dist/locale/ru.js +4 -1
- package/dist/locale/ru.json +4 -1
- package/dist/locale-CG54AU2Q.js +219 -0
- package/dist/locale.js +1 -1
- package/dist/model/behaviors-value.d.ts +24 -6
- package/dist/model/create-model.d.ts +2 -2
- package/dist/model/derived-registry.d.ts +1 -1
- package/dist/model/index.d.ts +16 -5
- package/dist/model/model-nodes.d.ts +138 -8
- package/dist/model/model-signals-proxy.d.ts +11 -5
- package/dist/model/model-value-proxy.d.ts +84 -3
- package/dist/model/safe-effect.d.ts +33 -1
- package/dist/model/types.d.ts +70 -17
- package/dist/model.js +21 -17
- package/dist/node-kind-C-r6ZMdo.js +15 -0
- package/dist/platforms/react/field-binding/field-adapter.d.ts +1 -1
- package/dist/platforms/react/field-binding/field-handle.d.ts +5 -2
- package/dist/platforms/react/form-bundle-context.d.ts +27 -0
- package/dist/platforms/react/hooks/types.d.ts +57 -38
- package/dist/platforms/react/hooks/use-form-bundle.d.ts +19 -4
- package/dist/platforms/react/hooks/use-form-flow.d.ts +40 -0
- package/dist/platforms/react/hooks/use-form-validation.d.ts +12 -6
- package/dist/platforms/react/hooks/useArrayLength.d.ts +6 -6
- package/dist/platforms/react/hooks/useFormControl.d.ts +7 -9
- package/dist/platforms/react/hooks/useFormControlValue.d.ts +18 -19
- package/dist/platforms/react/hooks/useSignalSubscription.d.ts +9 -1
- package/dist/platforms/react/i18n/context.d.ts +6 -1
- package/dist/platforms/react/i18n/hooks.d.ts +16 -10
- package/dist/platforms/react/i18n/provider.d.ts +9 -2
- package/dist/platforms/react/index.d.ts +7 -4
- package/dist/react.d.ts +22 -0
- package/dist/react.js +505 -0
- package/dist/runtime-token.d.ts +1 -2
- package/dist/safe-effect-BbtX3KuN.js +643 -0
- package/dist/schema-controller-CpiO2JoK.js +143 -0
- package/dist/signals.d.ts +15 -9
- package/dist/signals.js +11 -6
- package/dist/strategy-CoFSemuP.js +375 -0
- package/dist/type-guards-D-N_5Ddh.js +19 -0
- package/dist/validation-issue-B9mjI8jz.js +7 -0
- package/dist/validation-message-Ced9MGxs.js +194 -0
- package/dist/validation.js +9 -8
- package/dist/validators/email.js +5 -5
- package/dist/validators/future-date.js +8 -8
- package/dist/validators/integer.js +3 -3
- package/dist/validators/is-date.js +4 -4
- package/dist/validators/is-number.js +4 -4
- package/dist/validators/max-age.js +8 -9
- package/dist/validators/max-date.js +8 -8
- package/dist/validators/max-length.js +7 -7
- package/dist/validators/max.js +4 -4
- package/dist/validators/min-age.js +10 -8
- package/dist/validators/min-date.js +3 -3
- package/dist/validators/min-length.js +5 -5
- package/dist/validators/min.js +4 -4
- package/dist/validators/multiple-of.js +11 -7
- package/dist/validators/non-negative.js +3 -3
- package/dist/validators/non-zero.js +3 -3
- package/dist/validators/past-date.js +8 -8
- package/dist/validators/pattern.js +5 -5
- package/dist/validators/phone.js +11 -9
- package/dist/validators/required.js +2 -2
- package/dist/validators/url.js +48 -12
- package/dist/validators.js +17 -17
- package/docs/llms/01-api-reference.md +60 -34
- package/docs/llms/02-quick-start.md +111 -80
- package/docs/llms/03-api-signatures.md +368 -153
- package/docs/llms/04-common-patterns.md +58 -56
- package/docs/llms/05-common-mistakes.md +69 -44
- package/docs/llms/06-troubleshooting.md +14 -9
- package/docs/llms/07-complete-import.md +74 -43
- package/docs/llms/08-form-types.md +11 -4
- package/docs/llms/09-formschema.md +131 -83
- package/docs/llms/10-arrays.md +119 -69
- package/docs/llms/11-async-watchfield.md +2 -2
- package/docs/llms/13-multi-step.md +134 -63
- package/docs/llms/14-extended-mistakes.md +20 -17
- package/docs/llms/15-project-structure.md +221 -74
- package/docs/llms/16-ui-components.md +55 -42
- package/docs/llms/17-nonexistent-api.md +199 -32
- package/docs/llms/18-conditional-fields.md +66 -32
- package/docs/llms/19-reading-values.md +15 -3
- package/docs/llms/20-compute-vs-watch.md +16 -10
- package/docs/llms/21-array-operations.md +76 -26
- package/docs/llms/22-cycle-detection.md +1 -1
- package/docs/llms/23-copy-from.md +2 -2
- package/docs/llms/24-sync-fields.md +6 -5
- package/docs/llms/25-reset-when.md +14 -7
- package/docs/llms/26-transform-value.md +4 -4
- package/docs/llms/27-revalidate-when.md +16 -15
- package/docs/llms/28-submit-and-reset.md +276 -128
- package/docs/llms/29-async-preload.md +40 -30
- package/docs/llms/30-type-safety-recipes.md +105 -51
- package/docs/llms/31-async-validator-debounce.md +96 -53
- package/docs/llms/33-validation-strategy.md +178 -66
- package/docs/llms/34-i18n.md +69 -43
- package/docs/llms/35-unified-contract.md +307 -0
- package/docs/llms/36-issue-messages.md +284 -0
- package/docs/llms/37-compiled-schema.md +332 -0
- package/docs/llms/38-form-flow.md +397 -0
- package/llms-index.json +1 -1
- package/llms.txt +6968 -3278
- package/package.json +23 -4
- package/dist/behaviors-value-C7dSEhCc.js +0 -134
- package/dist/create-model-B8U-COv-.js +0 -103
- package/dist/date-utils-dTT_x_Be.js +0 -31
- package/dist/form/create-core-form.d.ts +0 -70
- package/dist/form/factories/index.d.ts +0 -6
- package/dist/form/factories/node-factory.d.ts +0 -189
- package/dist/form/form-submitter.d.ts +0 -121
- package/dist/form/nodes/array-node.d.ts +0 -276
- package/dist/form/nodes/field-node.d.ts +0 -297
- package/dist/form/nodes/form-node.d.ts +0 -326
- package/dist/form/nodes/group-node.d.ts +0 -284
- package/dist/form/nodes/model-array-node.d.ts +0 -121
- package/dist/form/nodes/subscription-manager.d.ts +0 -180
- package/dist/form/status-machine.d.ts +0 -115
- package/dist/form/types/deep-schema.d.ts +0 -107
- package/dist/form/types/form-proxy.d.ts +0 -141
- package/dist/form/unique-id.d.ts +0 -45
- package/dist/form/validation/config.d.ts +0 -73
- package/dist/form/validation/error-handler.d.ts +0 -166
- package/dist/form/validators/validation-error.d.ts +0 -8
- package/dist/index-BrNkQj1y.js +0 -105
- package/dist/loader-Dv_Hl7JC.js +0 -216
- package/dist/model-signals-proxy-2vqS243J.js +0 -250
- package/dist/node-DGEFMTQu.js +0 -18
- package/dist/strategy-BXS_gBfz.js +0 -170
- package/dist/validation-error-B-U3RsV0.js +0 -11
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ValidationIssue } from '../../../form/types/index';
|
|
2
2
|
/**
|
|
3
|
-
* Состояние поля формы, возвращаемое хуком {@link useFormControl} для {@link
|
|
3
|
+
* Состояние поля формы, возвращаемое хуком {@link useFormControl} для {@link FormControl}.
|
|
4
4
|
*
|
|
5
5
|
* Содержит реактивные данные поля: значение, состояние валидации, флаги взаимодействия
|
|
6
6
|
* и пользовательские props для компонентов.
|
|
@@ -10,12 +10,12 @@ import { ValidationError } from '../../../form/types/index';
|
|
|
10
10
|
* @example Базовое использование
|
|
11
11
|
* ```tsx
|
|
12
12
|
* interface Props {
|
|
13
|
-
* control:
|
|
13
|
+
* control: FormControl<string>;
|
|
14
14
|
* }
|
|
15
15
|
*
|
|
16
16
|
* function TextField({ control }: Props) {
|
|
17
17
|
* const state = useFormControl(control);
|
|
18
|
-
* const message =
|
|
18
|
+
* const message = useIssueMessage(); // из '@reformer/core/react'
|
|
19
19
|
*
|
|
20
20
|
* return (
|
|
21
21
|
* <div>
|
|
@@ -24,8 +24,8 @@ import { ValidationError } from '../../../form/types/index';
|
|
|
24
24
|
* disabled={state.disabled}
|
|
25
25
|
* onChange={e => control.setValue(e.target.value)}
|
|
26
26
|
* />
|
|
27
|
-
* {state.
|
|
28
|
-
* <span className="error">{message(state.
|
|
27
|
+
* {state.shouldShowIssues && state.issues[0] && (
|
|
28
|
+
* <span className="error">{message(state.issues[0])}</span>
|
|
29
29
|
* )}
|
|
30
30
|
* </div>
|
|
31
31
|
* );
|
|
@@ -89,22 +89,22 @@ export interface FieldControlState<T> {
|
|
|
89
89
|
*
|
|
90
90
|
* @example
|
|
91
91
|
* ```tsx
|
|
92
|
-
* const {
|
|
93
|
-
* const message =
|
|
92
|
+
* const { issues } = useFormControl(field);
|
|
93
|
+
* const message = useIssueMessage(); // из '@reformer/core/react'
|
|
94
94
|
*
|
|
95
95
|
* return (
|
|
96
96
|
* <ul className="error-list">
|
|
97
|
-
* {
|
|
97
|
+
* {issues.map((error, i) => (
|
|
98
98
|
* <li key={i}>{message(error)}</li>
|
|
99
99
|
* ))}
|
|
100
100
|
* </ul>
|
|
101
101
|
* );
|
|
102
102
|
* ```
|
|
103
103
|
*/
|
|
104
|
-
|
|
104
|
+
issues: ValidationIssue[];
|
|
105
105
|
/**
|
|
106
106
|
* Флаг валидности поля.
|
|
107
|
-
* `true` когда поле прошло все валидации (
|
|
107
|
+
* `true` когда поле прошло все валидации (issues.length === 0).
|
|
108
108
|
*
|
|
109
109
|
* @example
|
|
110
110
|
* ```tsx
|
|
@@ -118,7 +118,7 @@ export interface FieldControlState<T> {
|
|
|
118
118
|
valid: boolean;
|
|
119
119
|
/**
|
|
120
120
|
* Флаг невалидности поля.
|
|
121
|
-
* `true` когда есть ошибки валидации (
|
|
121
|
+
* `true` когда есть ошибки валидации (issues.length > 0).
|
|
122
122
|
* Противоположность {@link valid}.
|
|
123
123
|
*
|
|
124
124
|
* @example
|
|
@@ -148,28 +148,44 @@ export interface FieldControlState<T> {
|
|
|
148
148
|
*/
|
|
149
149
|
touched: boolean;
|
|
150
150
|
/**
|
|
151
|
-
*
|
|
152
|
-
*
|
|
151
|
+
* Показывать ли состояние ошибки — `aria-invalid`, рамку: поле невалидно и его трогали или
|
|
152
|
+
* меняли. Предупреждение (`severity: 'warning'`) поле невалидным не делает, поэтому для текстов
|
|
153
|
+
* замечаний есть {@link FieldControlState.shouldShowIssues}.
|
|
153
154
|
*
|
|
154
155
|
* @example
|
|
155
156
|
* ```tsx
|
|
156
|
-
* const { shouldShowError
|
|
157
|
-
*
|
|
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'
|
|
158
171
|
*
|
|
159
172
|
* return (
|
|
160
173
|
* <div>
|
|
161
|
-
* <input {...props} />
|
|
162
|
-
* {
|
|
163
|
-
*
|
|
164
|
-
*
|
|
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
|
+
* ))}
|
|
165
181
|
* </div>
|
|
166
182
|
* );
|
|
167
183
|
* ```
|
|
168
184
|
*/
|
|
169
|
-
|
|
185
|
+
shouldShowIssues: boolean;
|
|
170
186
|
/**
|
|
171
187
|
* Пользовательские props для передачи в UI-компоненты.
|
|
172
|
-
* Устанавливаются через {@link
|
|
188
|
+
* Устанавливаются через {@link FormControl.updateComponentProps}.
|
|
173
189
|
*
|
|
174
190
|
* @example
|
|
175
191
|
* ```tsx
|
|
@@ -210,7 +226,7 @@ export interface FieldControlState<T> {
|
|
|
210
226
|
dirty: boolean;
|
|
211
227
|
}
|
|
212
228
|
/**
|
|
213
|
-
* Состояние массива формы, возвращаемое хуком {@link useFormControl} для {@link
|
|
229
|
+
* Состояние массива формы, возвращаемое хуком {@link useFormControl} для {@link FormArray}.
|
|
214
230
|
*
|
|
215
231
|
* Содержит реактивные данные массива: значения элементов, длину, состояние валидации
|
|
216
232
|
* и флаги взаимодействия.
|
|
@@ -225,7 +241,7 @@ export interface FieldControlState<T> {
|
|
|
225
241
|
* }
|
|
226
242
|
*
|
|
227
243
|
* interface Props {
|
|
228
|
-
* control:
|
|
244
|
+
* control: FormArray<Phone>;
|
|
229
245
|
* }
|
|
230
246
|
*
|
|
231
247
|
* function PhoneList({ control }: Props) {
|
|
@@ -235,7 +251,7 @@ export interface FieldControlState<T> {
|
|
|
235
251
|
* <div>
|
|
236
252
|
* {control.map((item, index) => (
|
|
237
253
|
* <PhoneItem
|
|
238
|
-
* key={
|
|
254
|
+
* key={control.keyAt(index)}
|
|
239
255
|
* control={item}
|
|
240
256
|
* onRemove={() => control.removeAt(index)}
|
|
241
257
|
* />
|
|
@@ -247,7 +263,7 @@ export interface FieldControlState<T> {
|
|
|
247
263
|
* Add Phone
|
|
248
264
|
* </button>
|
|
249
265
|
*
|
|
250
|
-
* {!valid && <p className="error">Please fix phone
|
|
266
|
+
* {!valid && <p className="error">Please fix phone issues</p>}
|
|
251
267
|
* </div>
|
|
252
268
|
* );
|
|
253
269
|
* }
|
|
@@ -293,23 +309,24 @@ export interface ArrayControlState<T> {
|
|
|
293
309
|
*/
|
|
294
310
|
pending: boolean;
|
|
295
311
|
/**
|
|
296
|
-
*
|
|
297
|
-
* Не включает ошибки отдельных элементов.
|
|
312
|
+
* Замечания массива: собственные (правила на ручке массива) и замечания всех его строк.
|
|
298
313
|
*
|
|
299
314
|
* @example
|
|
300
315
|
* ```tsx
|
|
301
|
-
* //
|
|
302
|
-
*
|
|
303
|
-
* validator: (phones) => phones.length >= 1,
|
|
304
|
-
* message: 'At least one phone required'
|
|
305
|
-
* });
|
|
316
|
+
* // Правило массива — в схеме валидации
|
|
317
|
+
* validate(model.$.phones, [minLength(1, { code: 'phones.minLength' })]);
|
|
306
318
|
*
|
|
307
319
|
* // В компоненте
|
|
308
|
-
* const {
|
|
309
|
-
* //
|
|
320
|
+
* const { issues } = useFormControl(phonesArray);
|
|
321
|
+
* // для пустого массива issues содержит замечание с кодом 'phones.minLength'
|
|
310
322
|
* ```
|
|
311
323
|
*/
|
|
312
|
-
|
|
324
|
+
issues: ValidationIssue[];
|
|
325
|
+
/**
|
|
326
|
+
* Собственные замечания массива — только правила на его ручке (`minLength` и т.п.), без
|
|
327
|
+
* замечаний строк. Их показывает ошибка уровня массива (`FieldArray.Error`).
|
|
328
|
+
*/
|
|
329
|
+
ownIssues: ValidationIssue[];
|
|
313
330
|
/**
|
|
314
331
|
* Флаг валидности массива и всех его элементов.
|
|
315
332
|
* `true` только когда массив и все вложенные элементы валидны.
|
|
@@ -327,7 +344,9 @@ export interface ArrayControlState<T> {
|
|
|
327
344
|
touched: boolean;
|
|
328
345
|
/**
|
|
329
346
|
* Флаг изменения.
|
|
330
|
-
* `true`
|
|
347
|
+
* `true` после правки любого элемента или изменения состава через форму (`push`, `removeAt`,
|
|
348
|
+
* `move`, `setValue`…). Как у поля, запись в модель (загрузка данных) и операция, которая состав
|
|
349
|
+
* не поменяла, изменением не считаются; снимает `reset`.
|
|
331
350
|
*
|
|
332
351
|
* @example
|
|
333
352
|
* ```tsx
|
|
@@ -344,7 +363,7 @@ export interface ArrayControlState<T> {
|
|
|
344
363
|
dirty: boolean;
|
|
345
364
|
/**
|
|
346
365
|
* Флаг отключения массива.
|
|
347
|
-
* `true` когда массив отключён (`
|
|
366
|
+
* `true` когда массив отключён (`FormArray.disable()`), в том числе через
|
|
348
367
|
* распространение disable от родительской группы.
|
|
349
368
|
*
|
|
350
369
|
* Используйте для отключения UI-действий добавления/удаления, когда массив
|
|
@@ -1,26 +1,41 @@
|
|
|
1
1
|
import { FormValidationController } from '../../../form/validation/strategy';
|
|
2
|
-
/** Минимум, который хук ожидает от бандла:
|
|
2
|
+
/** Минимум, который хук ожидает от бандла: контроллер живой валидации и снятие — оба опционально. */
|
|
3
3
|
export interface FormBundleLike {
|
|
4
4
|
validation?: {
|
|
5
5
|
controller: FormValidationController;
|
|
6
6
|
};
|
|
7
|
+
/** Снять сборку: поведение, подписки нод, живую проверку. Есть у всех фабрик семейства. */
|
|
8
|
+
dispose?(): void;
|
|
7
9
|
}
|
|
8
10
|
/**
|
|
9
11
|
* Собрать форму один раз и держать её стабильной; живую стратегию валидации армировать в эффекте
|
|
10
|
-
* (не при SSR — там эффекты не выполняются)
|
|
12
|
+
* (не при SSR — там эффекты не выполняются); при размонтировании снять сборку.
|
|
11
13
|
*
|
|
12
14
|
* @typeParam B - Тип бандла конкретной фабрики.
|
|
13
|
-
* @param factory - Фабрика бандла, обычно `() =>
|
|
15
|
+
* @param factory - Фабрика бандла, обычно `() => createForm({…})`. Сборкой владеет хук: при
|
|
16
|
+
* размонтировании он вызывает её `dispose()`, поэтому фабрика должна собирать НОВУЮ форму, а не
|
|
17
|
+
* возвращать чужую, живущую дольше компонента.
|
|
14
18
|
* @returns Стабильный бандл.
|
|
15
19
|
*
|
|
16
20
|
* @example
|
|
17
21
|
* ```tsx
|
|
18
|
-
* const credit = useFormBundle(() =>
|
|
22
|
+
* const credit = useFormBundle(() => createForm<CreditForm>({ model: createCreditModel() }));
|
|
19
23
|
* ```
|
|
20
24
|
*
|
|
21
25
|
* @remarks
|
|
22
26
|
* Передавайте именно фабрику: `useState` трактует функцию как ленивый инициализатор, а некоторые
|
|
23
27
|
* части бандла сами являются функциями (`RenderSchemaProxy`), поэтому `useFormBundle(bundle.render)`
|
|
24
28
|
* вызвал бы её вместо сохранения.
|
|
29
|
+
*
|
|
30
|
+
* Сборка снимается микрозадачей после размонтирования, а не в самой очистке эффекта (см. шапку
|
|
31
|
+
* модуля): код, который сразу после размонтирования пишет в модель, ещё видит живое поведение.
|
|
32
|
+
*
|
|
33
|
+
* **Ограничение StrictMode (только разработка).** StrictMode вызывает ленивый инициализатор
|
|
34
|
+
* `useState` дважды, и фабрика на монтирование срабатывает два раза. Второй бандл React
|
|
35
|
+
* выбрасывает до коммита: хук его не видит, поэтому не армирует и не снимает. Живая проверка у
|
|
36
|
+
* него не запущена, но поведение (`compute`, `onChange`, …) работает, пока бандл не соберёт
|
|
37
|
+
* сборщик мусора. В production инициализатор вызывается один раз и утечки нет. Побочные эффекты
|
|
38
|
+
* на создание (запрос данных в фабрике или в `setup`) в разработке поэтому тоже срабатывают
|
|
39
|
+
* дважды — запускайте их из поведения формы или из эффекта компонента.
|
|
25
40
|
*/
|
|
26
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 '../../../
|
|
2
|
-
import {
|
|
1
|
+
import { FormModel } from '../../../model/types';
|
|
2
|
+
import { ValidationDefinition } from '../../../form/validation';
|
|
3
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:
|
|
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
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Попытка отправки: полный прогон; при замечаниях раскрыта вся форма, `afterFirstSubmit`
|
|
19
|
+
* переходит в живую фазу. `true` — только статус `valid`. Для `onSubmit`. Стабильная ссылка.
|
|
20
|
+
*/
|
|
18
21
|
submit: () => Promise<boolean>;
|
|
19
|
-
/**
|
|
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 =
|
|
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 {
|
|
1
|
+
import { FormArray } from '../../../form/types/index';
|
|
2
2
|
/**
|
|
3
3
|
* React-хук для подписки только на длину массива.
|
|
4
4
|
*
|
|
5
|
-
* Оптимизированная версия {@link useFormControl} для
|
|
6
|
-
* подписывается только на
|
|
5
|
+
* Оптимизированная версия {@link useFormControl} для FormArray, которая
|
|
6
|
+
* подписывается только на длину массива. Компонент не будет ре-рендериться
|
|
7
7
|
* при изменении значений вложенных полей.
|
|
8
8
|
*
|
|
9
9
|
* @typeParam T - Тип элемента массива
|
|
10
|
-
* @param control -
|
|
10
|
+
* @param control - FormArray для подписки
|
|
11
11
|
* @returns Текущая длина массива
|
|
12
12
|
*
|
|
13
13
|
* @example
|
|
@@ -18,7 +18,7 @@ import { ArrayNode } from '../../../form/nodes/array-node';
|
|
|
18
18
|
* return (
|
|
19
19
|
* <div>
|
|
20
20
|
* {arrayNode.map((item, index) => (
|
|
21
|
-
* <ItemRenderer key={
|
|
21
|
+
* <ItemRenderer key={arrayNode.keyAt(index)} item={item} />
|
|
22
22
|
* ))}
|
|
23
23
|
* </div>
|
|
24
24
|
* );
|
|
@@ -27,4 +27,4 @@ import { ArrayNode } from '../../../form/nodes/array-node';
|
|
|
27
27
|
*
|
|
28
28
|
* @group React Hooks
|
|
29
29
|
*/
|
|
30
|
-
export declare function useArrayLength<T extends object>(control:
|
|
30
|
+
export declare function useArrayLength<T extends object>(control: FormArray<T>): number;
|
|
@@ -1,24 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ArrayNode } from '../../../form/nodes/array-node';
|
|
3
|
-
import { FormValue } from '../../../form/types/index';
|
|
1
|
+
import { FormArray, FormControl } from '../../../form/types/index';
|
|
4
2
|
import { FieldControlState, ArrayControlState } from './types';
|
|
5
3
|
/**
|
|
6
|
-
* React-хук для подписки на состояние {@link
|
|
4
|
+
* React-хук для подписки на состояние {@link FormArray}.
|
|
7
5
|
*
|
|
8
6
|
* @typeParam T - Тип элемента массива
|
|
9
|
-
* @param control -
|
|
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:
|
|
12
|
+
export declare function useFormControl<T extends object>(control: FormArray<T> | undefined): ArrayControlState<T>;
|
|
15
13
|
/**
|
|
16
|
-
* React-хук для подписки на состояние {@link
|
|
14
|
+
* React-хук для подписки на состояние {@link FormControl}.
|
|
17
15
|
*
|
|
18
16
|
* @typeParam T - Тип значения поля
|
|
19
|
-
* @param control -
|
|
17
|
+
* @param control - FormControl для подписки
|
|
20
18
|
* @returns Состояние поля {@link FieldControlState}
|
|
21
19
|
*
|
|
22
20
|
* @group React Hooks
|
|
23
21
|
*/
|
|
24
|
-
export declare function useFormControl<T
|
|
22
|
+
export declare function useFormControl<T>(control: FormControl<T>): FieldControlState<T>;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { FormValue } from '../../../form/types/index';
|
|
1
|
+
import { FormControl } from '../../../form/types/index';
|
|
3
2
|
/**
|
|
4
3
|
* React-хук для подписки только на значение поля.
|
|
5
4
|
*
|
|
6
5
|
* Оптимизированная версия {@link useFormControl}, которая подписывается
|
|
7
6
|
* только на сигнал `value`. Компонент не будет ре-рендериться при изменении
|
|
8
|
-
* `
|
|
7
|
+
* `issues`, `touched`, `valid` и других свойств состояния.
|
|
9
8
|
*
|
|
10
9
|
* ## Когда использовать
|
|
11
10
|
*
|
|
@@ -16,21 +15,21 @@ import { FormValue } from '../../../form/types/index';
|
|
|
16
15
|
*
|
|
17
16
|
* ## Когда НЕ использовать
|
|
18
17
|
*
|
|
19
|
-
* Если компоненту нужны `
|
|
18
|
+
* Если компоненту нужны `issues`, `touched`, `disabled` или другие свойства -
|
|
20
19
|
* используйте {@link useFormControl}. Множественные подписки на один контрол
|
|
21
20
|
* через разные хуки менее эффективны, чем одна подписка через `useFormControl`.
|
|
22
21
|
*
|
|
23
22
|
* @typeParam T - Тип значения поля
|
|
24
|
-
* @param control -
|
|
23
|
+
* @param control - FormControl для подписки на значение
|
|
25
24
|
* @returns Текущее значение поля
|
|
26
25
|
*
|
|
27
26
|
* @example Условный рендеринг секции
|
|
28
27
|
* ```tsx
|
|
29
|
-
* import { useFormControlValue } from '@reformer/core';
|
|
28
|
+
* import { useFormControlValue } from '@reformer/core/react';
|
|
30
29
|
*
|
|
31
30
|
* interface FormFields {
|
|
32
|
-
* hasShipping:
|
|
33
|
-
* shippingAddress:
|
|
31
|
+
* hasShipping: FormControl<boolean>;
|
|
32
|
+
* shippingAddress: FormGroup<AddressFields>;
|
|
34
33
|
* }
|
|
35
34
|
*
|
|
36
35
|
* function ShippingSection({ form }: { form: FormFields }) {
|
|
@@ -53,8 +52,8 @@ import { FormValue } from '../../../form/types/index';
|
|
|
53
52
|
* @example Динамические опции на основе другого поля
|
|
54
53
|
* ```tsx
|
|
55
54
|
* interface FormFields {
|
|
56
|
-
* country:
|
|
57
|
-
* city:
|
|
55
|
+
* country: FormControl<string>;
|
|
56
|
+
* city: FormControl<string>;
|
|
58
57
|
* }
|
|
59
58
|
*
|
|
60
59
|
* function CitySelect({ form }: { form: FormFields }) {
|
|
@@ -93,14 +92,14 @@ import { FormValue } from '../../../form/types/index';
|
|
|
93
92
|
*
|
|
94
93
|
* // Хук вызывается на уровне компонента-строки (не в цикле — Rules of Hooks).
|
|
95
94
|
* // Доступ к полям элемента — через Proxy: item.quantity, item.price.
|
|
96
|
-
* function OrderRow({ item }: { item:
|
|
95
|
+
* function OrderRow({ item }: { item: FormGroup<OrderItem> }) {
|
|
97
96
|
* const quantity = useFormControlValue(item.quantity);
|
|
98
97
|
* const price = useFormControlValue(item.price);
|
|
99
98
|
*
|
|
100
99
|
* return <span>Итого: ${(quantity * price).toFixed(2)}</span>;
|
|
101
100
|
* }
|
|
102
101
|
*
|
|
103
|
-
* function OrderList({ items }: { items:
|
|
102
|
+
* function OrderList({ items }: { items: FormArray<OrderItem> }) {
|
|
104
103
|
* return (
|
|
105
104
|
* <div>
|
|
106
105
|
* {items.map((item, index) => (
|
|
@@ -114,7 +113,7 @@ import { FormValue } from '../../../form/types/index';
|
|
|
114
113
|
* @example Preview значения
|
|
115
114
|
* ```tsx
|
|
116
115
|
* interface MarkdownEditorProps {
|
|
117
|
-
* control:
|
|
116
|
+
* control: FormControl<string>;
|
|
118
117
|
* }
|
|
119
118
|
*
|
|
120
119
|
* function MarkdownPreview({ control }: MarkdownEditorProps) {
|
|
@@ -133,8 +132,8 @@ import { FormValue } from '../../../form/types/index';
|
|
|
133
132
|
*
|
|
134
133
|
* // Основной редактор использует useFormControl для полного состояния
|
|
135
134
|
* function MarkdownEditor({ control }: MarkdownEditorProps) {
|
|
136
|
-
* const { value,
|
|
137
|
-
* const message =
|
|
135
|
+
* const { value, shouldShowIssues, issues } = useFormControl(control);
|
|
136
|
+
* const message = useIssueMessage(); // из '@reformer/core/react'
|
|
138
137
|
*
|
|
139
138
|
* return (
|
|
140
139
|
* <div className="editor-container">
|
|
@@ -142,7 +141,7 @@ import { FormValue } from '../../../form/types/index';
|
|
|
142
141
|
* value={value}
|
|
143
142
|
* onChange={e => control.setValue(e.target.value)}
|
|
144
143
|
* />
|
|
145
|
-
* {
|
|
144
|
+
* {shouldShowIssues && <span className="error">{message(issues[0])}</span>}
|
|
146
145
|
*
|
|
147
146
|
* {/* Preview обновляется только при изменении value *}
|
|
148
147
|
* <MarkdownPreview control={control} />
|
|
@@ -153,7 +152,7 @@ import { FormValue } from '../../../form/types/index';
|
|
|
153
152
|
*
|
|
154
153
|
* @example Счётчик символов
|
|
155
154
|
* ```tsx
|
|
156
|
-
* function CharacterCounter({ control, max }: { control:
|
|
155
|
+
* function CharacterCounter({ control, max }: { control: FormControl<string>; max: number }) {
|
|
157
156
|
* const value = useFormControlValue(control);
|
|
158
157
|
* const remaining = max - value.length;
|
|
159
158
|
*
|
|
@@ -166,8 +165,8 @@ import { FormValue } from '../../../form/types/index';
|
|
|
166
165
|
* ```
|
|
167
166
|
*
|
|
168
167
|
* @see {@link useFormControl} - для полного состояния поля
|
|
169
|
-
* @see {@link
|
|
168
|
+
* @see {@link FormControl} - тип контрола поля
|
|
170
169
|
*
|
|
171
170
|
* @group React Hooks
|
|
172
171
|
*/
|
|
173
|
-
export declare function useFormControlValue<T
|
|
172
|
+
export declare function useFormControlValue<T>(control: FormControl<T>): T;
|
|
@@ -9,9 +9,17 @@ export type ExtractSignalValues<T extends SignalMap> = {
|
|
|
9
9
|
export interface SignalConfig<K extends string> {
|
|
10
10
|
key: K;
|
|
11
11
|
useShallowArrayEqual?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Сравнивать объект поверхностно: новый объект с теми же значениями (`updateComponentProps`
|
|
14
|
+
* теми же пропсами) снапшот не меняет.
|
|
15
|
+
*/
|
|
16
|
+
useShallowObjectEqual?: boolean;
|
|
12
17
|
}
|
|
13
18
|
/**
|
|
14
|
-
* Хук для подписки на набор сигналов с
|
|
19
|
+
* Хук для подписки на набор сигналов с кешированием.
|
|
20
|
+
*
|
|
21
|
+
* Подписка — только на сигналы из `configs`: их смена пересобирает снапшот. Остальные сигналы
|
|
22
|
+
* `signals` в снапшот попадают значением на момент сборки и сами компонент не будят.
|
|
15
23
|
* @internal
|
|
16
24
|
*/
|
|
17
25
|
export declare function useSignalSubscription<TSignals extends SignalMap, TSnapshot extends ExtractSignalValues<TSignals>>(signals: TSignals, configs: SignalConfig<Extract<keyof TSignals, string>>[], buildSnapshot: (values: ExtractSignalValues<TSignals>) => TSnapshot,
|
|
@@ -7,7 +7,12 @@ export interface I18nContextValue extends I18nHandle {
|
|
|
7
7
|
readonly pending: boolean;
|
|
8
8
|
/** Ошибка последней загрузки языка. */
|
|
9
9
|
readonly error: unknown;
|
|
10
|
+
/**
|
|
11
|
+
* Повторить загрузку языка, которая упала (`error` задан). Без загрузчика и без ошибки ничего
|
|
12
|
+
* не делает.
|
|
13
|
+
*/
|
|
14
|
+
readonly retry: () => void;
|
|
10
15
|
}
|
|
11
16
|
/** Значение контекста для локали: ручка плюс состояние загрузки. */
|
|
12
|
-
export declare function contextValue(handle: I18nHandle, pending: boolean, error: unknown): I18nContextValue;
|
|
17
|
+
export declare function contextValue(handle: I18nHandle, pending: boolean, error: unknown, retry?: () => void): I18nContextValue;
|
|
13
18
|
export declare const I18nContext: import('react').Context<I18nContextValue>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ValidationIssue } from '../../../form/types/contracts';
|
|
2
2
|
import { Messages } from '../../../i18n/locale';
|
|
3
3
|
import { MessageValues } from '../../../i18n/message-format';
|
|
4
|
+
import { IssueMessages } from '../../../i18n/validation-message';
|
|
4
5
|
import { I18nContextValue } from './context';
|
|
5
6
|
/**
|
|
6
7
|
* Активная локаль: перевод ключей приложения, форматирование чисел, дат и размеров, состояние
|
|
@@ -40,20 +41,25 @@ export declare function useI18n(): I18nContextValue;
|
|
|
40
41
|
*/
|
|
41
42
|
export declare function useMessages<D extends Messages>(defaults: D): (key: keyof D & string, values?: MessageValues) => string;
|
|
42
43
|
/**
|
|
43
|
-
* Резолвер текста
|
|
44
|
-
*
|
|
45
|
-
*
|
|
44
|
+
* Резолвер текста замечания валидации: словарь формы → словарь активной локали по коду →
|
|
45
|
+
* встроенный английский → код. Идентичность функции меняется вместе с локалью и словарём,
|
|
46
|
+
* поэтому показанные замечания переводятся при смене языка без повторной валидации.
|
|
46
47
|
*
|
|
47
|
-
* `FormField` из `@reformer/cdk` использует его сам; вызывайте напрямую, если рисуете
|
|
48
|
+
* `FormField` из `@reformer/cdk` использует его сам; вызывайте напрямую, если рисуете замечания
|
|
48
49
|
* своим компонентом.
|
|
49
50
|
*
|
|
51
|
+
* @param messages - Словарь формы (`defineIssueMessages`). Стабильная ссылка — константа
|
|
52
|
+
* модуля, иначе резолвер будет пересоздаваться на каждый рендер.
|
|
53
|
+
*
|
|
50
54
|
* @example
|
|
51
55
|
* ```tsx
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
+
* const messages = defineIssueMessages({ 'phone.required': 'Укажите телефон' });
|
|
57
|
+
*
|
|
58
|
+
* function FieldIssues({ control }: { control: FormControl<string> }) {
|
|
59
|
+
* const message = useIssueMessage(messages);
|
|
60
|
+
* const { issues } = useFormControl(control);
|
|
61
|
+
* return <>{issues.map((issue) => <span key={issue.code}>{message(issue)}</span>)}</>;
|
|
56
62
|
* }
|
|
57
63
|
* ```
|
|
58
64
|
*/
|
|
59
|
-
export declare function
|
|
65
|
+
export declare function useIssueMessage(messages?: IssueMessages): (issue: ValidationIssue) => string;
|
|
@@ -11,12 +11,19 @@ export interface I18nStaticProviderProps {
|
|
|
11
11
|
export interface I18nLoadingProviderProps {
|
|
12
12
|
/** Код языка. Смена кода запускает загрузку; до её конца на экране прежний язык. */
|
|
13
13
|
lang: string;
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Загрузчик локалей — обычно результат `createLocaleLoader`. Нужна СТАБИЛЬНАЯ ссылка (модульная
|
|
16
|
+
* константа, `useMemo`): новый загрузчик — новое хранилище, и язык запрашивается у него заново.
|
|
17
|
+
* На экране тем временем остаётся прежняя локаль, но инлайн-стрелка гоняла бы запрос на каждый
|
|
18
|
+
* рендер родителя.
|
|
19
|
+
*/
|
|
15
20
|
load: LocaleLoad;
|
|
16
21
|
/**
|
|
17
22
|
* Что показать, пока не загружена самая первая локаль. По умолчанию — ничего: иначе на кадр
|
|
18
23
|
* мелькнул бы встроенный английский. Если локаль уже в кэше загрузчика (`load.preload(lang)`
|
|
19
|
-
* до монтирования), первый рендер синхронный и `fallback` не показывается.
|
|
24
|
+
* до монтирования), первый рендер синхронный и `fallback` не показывается. Упала первая
|
|
25
|
+
* загрузка — дети показываются на встроенном английском, ошибка — в `useI18n().error`,
|
|
26
|
+
* повторить — `useI18n().retry()`.
|
|
20
27
|
*/
|
|
21
28
|
fallback?: ReactNode;
|
|
22
29
|
/** Отказ загрузки. Язык на экране при этом не меняется. */
|