@reformer/renderer-json 4.0.1 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/json-form-renderer.d.ts +13 -4
- package/dist/components/schema-error-panel.d.ts +8 -0
- package/dist/context/json-renderer-context.d.ts +6 -0
- package/dist/converter/json-to-render-schema.d.ts +11 -27
- package/dist/index-CI7YxPq2.js +45 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.js +195 -164
- package/dist/operators.d.ts +46 -0
- package/dist/schema/form-schema.schema.json.d.ts +81 -0
- package/dist/schema/index.d.ts +20 -0
- package/dist/types/json-schema.d.ts +59 -108
- package/dist/validate.d.ts +23 -0
- package/dist/validate.js +4467 -0
- package/dist/validate.test.d.ts +1 -0
- package/package.json +8 -1
|
@@ -13,6 +13,16 @@ export interface JsonFormRendererProps<T> {
|
|
|
13
13
|
renderBehavior?: RenderBehaviorFn<T>;
|
|
14
14
|
/** Колбэк, получающий построенный `RenderSchemaProxy` для внешних манипуляций. */
|
|
15
15
|
onSchemaReady?: (schema: RenderSchemaProxy<T>) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Валидировать JSON-схему против мета-схемы перед рендером. При ошибках рисует
|
|
18
|
+
* {@link SchemaErrorPanel} вместо формы. ajv грузится **динамически** (`import('../validate')`) —
|
|
19
|
+
* в prod-бандл не попадает, пока `validate` не включён.
|
|
20
|
+
*
|
|
21
|
+
* По умолчанию `false`. Чтобы валидировать только в dev, приложение передаёт значение из
|
|
22
|
+
* СВОЕГО окружения: `validate={import.meta.env.DEV}` — детекцию dev нельзя «запечь» в пакет,
|
|
23
|
+
* т.к. `import.meta.env.DEV` инлайнится в `false` при production-сборке самого пакета.
|
|
24
|
+
*/
|
|
25
|
+
validate?: boolean;
|
|
16
26
|
}
|
|
17
27
|
/**
|
|
18
28
|
* Главный компонент пакета. Рендерит форму, описанную JSON-схемой.
|
|
@@ -71,10 +81,9 @@ export interface JsonFormRendererProps<T> {
|
|
|
71
81
|
* ```
|
|
72
82
|
*
|
|
73
83
|
* **Note**: `JsonFormRenderer` принимает ТОЛЬКО `{ schema, renderBehavior?, onSchemaReady? }`.
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* (см. recipe `multi-step-from-json` в `docs/llms/`).
|
|
84
|
+
* Под M1 модель (`FormModel`) передаётся через {@link JsonRendererProvider} settings (`model`);
|
|
85
|
+
* листья JSON-схемы биндятся к её сигналам конвертером `convertJsonToM1Tree`.
|
|
77
86
|
*
|
|
78
87
|
* @see [docs/llms/01-overview.md](../../docs/llms/01-overview.md)
|
|
79
88
|
*/
|
|
80
|
-
export declare function JsonFormRenderer<T>({ schema, renderBehavior, onSchemaReady, }: JsonFormRendererProps<T>): ReactNode;
|
|
89
|
+
export declare function JsonFormRenderer<T>({ schema, renderBehavior, onSchemaReady, validate, }: JsonFormRendererProps<T>): ReactNode;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
/** Props of {@link SchemaErrorPanel}. */
|
|
3
|
+
export interface SchemaErrorPanelProps {
|
|
4
|
+
/** Человекочитаемые ошибки (`validateFormSchema().errors`). */
|
|
5
|
+
errors: string[];
|
|
6
|
+
}
|
|
7
|
+
/** Список ошибок схемы (path + message), с пометкой источника. Без внешних UI-зависимостей. */
|
|
8
|
+
export declare function SchemaErrorPanel({ errors }: SchemaErrorPanelProps): ReactNode;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { RendererSettings } from '@reformer/renderer-react';
|
|
3
|
+
import { FormModel } from '@reformer/core';
|
|
3
4
|
import { ComponentRegistry } from '../registry/types';
|
|
4
5
|
/**
|
|
5
6
|
* Расширенные настройки рендерера: всё из `RendererSettings` плюс реестр.
|
|
@@ -7,6 +8,11 @@ import { ComponentRegistry } from '../registry/types';
|
|
|
7
8
|
export interface JsonRendererSettings extends RendererSettings {
|
|
8
9
|
/** Реестр компонентов и source-значений. См. {@link defineRegistry}. */
|
|
9
10
|
registry?: ComponentRegistry;
|
|
11
|
+
/**
|
|
12
|
+
* Модель данных (M1, единая схема). Если задана — JSON-листья биндятся к сигналам модели
|
|
13
|
+
* (`model.signalAt(selector)`), а форма строится из той же JSON-схемы (без отдельной схемы формы).
|
|
14
|
+
*/
|
|
15
|
+
model?: FormModel<any>;
|
|
10
16
|
}
|
|
11
17
|
/**
|
|
12
18
|
* Props {@link JsonRendererProvider}.
|
|
@@ -1,31 +1,15 @@
|
|
|
1
|
-
import { RenderSchemaFn } from '@reformer/renderer-react';
|
|
1
|
+
import { RenderSchemaFn, RenderNode } from '@reformer/renderer-react';
|
|
2
|
+
import { FormModel } from '@reformer/core';
|
|
2
3
|
import { JsonFormSchema } from '../types/json-schema';
|
|
3
4
|
import { ComponentRegistry } from '../registry/types';
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Конвертирует JSON-схему в функцию RenderSchemaFn, которую можно
|
|
8
|
-
* передать в FormRenderer из @reformer/renderer-react.
|
|
9
|
-
*
|
|
10
|
-
* @param schema - JSON-схема формы
|
|
11
|
-
* @param registry - Реестр компонентов для резолва имён
|
|
12
|
-
* @returns RenderSchemaFn для FormRenderer
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```typescript
|
|
16
|
-
* const jsonSchema = {
|
|
17
|
-
* root: {
|
|
18
|
-
* component: 'Box',
|
|
19
|
-
* children: [
|
|
20
|
-
* { model: 'email' },
|
|
21
|
-
* { model: 'password', component: 'InputPassword' }
|
|
22
|
-
* ]
|
|
23
|
-
* }
|
|
24
|
-
* };
|
|
25
|
-
*
|
|
26
|
-
* const renderSchema = createRenderSchemaFromJson(jsonSchema, registry);
|
|
27
|
-
*
|
|
28
|
-
* <FormRenderer render={renderSchema} settings={{ fieldWrapper: FormField }} />
|
|
29
|
-
* ```
|
|
6
|
+
* Сырое дерево RenderNode из JSON (M1) — для `createForm({ model, schema })`.
|
|
7
|
+
* @group Converter
|
|
30
8
|
*/
|
|
31
|
-
export declare function
|
|
9
|
+
export declare function convertJsonToM1Tree<T>(schema: JsonFormSchema, registry: ComponentRegistry, model: FormModel<T>): RenderNode<T>;
|
|
10
|
+
/**
|
|
11
|
+
* `RenderSchemaFn` из JSON (M1) — для `FormRenderer`/`JsonFormRenderer`. Листья привязываются к
|
|
12
|
+
* сигналам модели (`'$model(path)'` → `model.signalAt`), компоненты/источники — из реестра.
|
|
13
|
+
* @group Converter
|
|
14
|
+
*/
|
|
15
|
+
export declare function createRenderSchemaFromJsonM1<T>(schema: JsonFormSchema, registry: ComponentRegistry, model: FormModel<T>): RenderSchemaFn<T>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const a = /^\$(model|component|dataSource)\((.+)\)$/;
|
|
2
|
+
function n(e) {
|
|
3
|
+
if (typeof e != "string") return null;
|
|
4
|
+
const t = a.exec(e);
|
|
5
|
+
return t ? { op: t[1], arg: t[2] } : null;
|
|
6
|
+
}
|
|
7
|
+
const y = (e) => n(e)?.op === "model", g = (e) => n(e)?.op === "component", S = (e) => n(e)?.op === "dataSource", s = "http://json-schema.org/draft-07/schema#", p = "https://reformer.dev/schemas/form-schema.schema.json", c = "ReFormer JSON form schema (M1, string-operator DSL)", m = "Base meta-schema: validates node structure + operator string syntax ($model/$component/$dataSource). Component-name enum is tightened per-app via buildFormSchemaMetaSchema; $dataSource names and $model paths are checked outside this schema.", d = "object", f = ["root"], l = !1, u = { $schema: { type: "string" }, version: { type: "string" }, root: { $ref: "#/definitions/node" } }, $ = { modelOp: { type: "string", pattern: "^\\$model\\(.+\\)$", description: 'Model binding: "$model(path)" — path resolved at runtime (not validated here).' }, componentOp: { type: "string", pattern: "^\\$component\\(.+\\)$", description: 'Registry component: "$component(Name)". Name is tightened to a registry enum by buildFormSchemaMetaSchema.' }, node: { oneOf: [{ $ref: "#/definitions/fieldNode" }, { $ref: "#/definitions/arrayNode" }, { $ref: "#/definitions/containerNode" }] }, fieldNode: { type: "object", required: ["value"], additionalProperties: !1, properties: { selector: { type: "string" }, value: { $ref: "#/definitions/modelOp" }, component: { $ref: "#/definitions/componentOp" }, componentProps: { type: "object" }, wrapper: { $ref: "#/definitions/node" } } }, arrayNode: { type: "object", required: ["array", "item"], additionalProperties: !1, properties: { selector: { type: "string" }, array: { $ref: "#/definitions/modelOp" }, item: { type: "object", required: ["$template"], additionalProperties: !1, properties: { $template: { $ref: "#/definitions/node" } } }, initialValue: { type: "object" }, componentProps: { type: "object" } } }, containerNode: { type: "object", required: ["component"], additionalProperties: !1, properties: { selector: { type: "string" }, component: { $ref: "#/definitions/componentOp" }, componentProps: { type: "object" }, children: { type: "array", items: { $ref: "#/definitions/node" } } } } }, r = {
|
|
8
|
+
$schema: s,
|
|
9
|
+
$id: p,
|
|
10
|
+
title: c,
|
|
11
|
+
description: m,
|
|
12
|
+
type: d,
|
|
13
|
+
required: f,
|
|
14
|
+
additionalProperties: l,
|
|
15
|
+
properties: u,
|
|
16
|
+
definitions: $
|
|
17
|
+
}, O = r;
|
|
18
|
+
function b(e) {
|
|
19
|
+
return e.names().filter((t) => {
|
|
20
|
+
const o = e.get(t)?.type;
|
|
21
|
+
return o === "field" || o === "container";
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function N(e) {
|
|
25
|
+
return e.names().filter((t) => e.get(t)?.type === "source");
|
|
26
|
+
}
|
|
27
|
+
const h = (e) => e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
28
|
+
function j(e) {
|
|
29
|
+
const t = JSON.parse(JSON.stringify(r)), o = e?.componentNames;
|
|
30
|
+
if (o && o.length > 0) {
|
|
31
|
+
const i = o.map(h).join("|");
|
|
32
|
+
t.definitions.componentOp.pattern = `^\\$component\\((${i})\\)$`;
|
|
33
|
+
}
|
|
34
|
+
return t;
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
g as a,
|
|
38
|
+
S as b,
|
|
39
|
+
j as c,
|
|
40
|
+
N as d,
|
|
41
|
+
O as f,
|
|
42
|
+
b as g,
|
|
43
|
+
y as i,
|
|
44
|
+
n as p
|
|
45
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -7,11 +7,16 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export { JsonFormRenderer } from './components/json-form-renderer';
|
|
9
9
|
export type { JsonFormRendererProps } from './components/json-form-renderer';
|
|
10
|
-
export
|
|
11
|
-
export {
|
|
10
|
+
export { SchemaErrorPanel } from './components/schema-error-panel';
|
|
11
|
+
export type { SchemaErrorPanelProps } from './components/schema-error-panel';
|
|
12
|
+
export type { JsonFormSchema, JsonNode, JsonFieldNode, JsonArrayNode, JsonContainerNode, } from './types/json-schema';
|
|
13
|
+
export { isFieldNode, isArrayNode, isContainerNode } from './types/json-schema';
|
|
14
|
+
export { parseOperator, isModelOp, isComponentOp, isDataSourceOp } from './operators';
|
|
15
|
+
export type { ModelOp, ComponentOp, DataSourceOp, JsonOperator, ParsedOperator } from './operators';
|
|
12
16
|
export { defineRegistry } from './registry/component-registry';
|
|
13
17
|
export { FIELD_WRAPPER } from './registry/constants';
|
|
14
18
|
export type { ComponentRegistry, ComponentMetadata, RegistryBuilder } from './registry/types';
|
|
15
19
|
export { JsonRendererProvider, useJsonRendererSettings } from './context/json-renderer-context';
|
|
16
20
|
export type { JsonRendererSettings, JsonRendererProviderProps, } from './context/json-renderer-context';
|
|
17
|
-
export {
|
|
21
|
+
export { createRenderSchemaFromJsonM1, convertJsonToM1Tree, } from './converter/json-to-render-schema';
|
|
22
|
+
export { formSchemaMetaSchema, buildFormSchemaMetaSchema, getComponentNames, getSourceNames, } from './schema';
|
package/dist/index.js
CHANGED
|
@@ -1,38 +1,39 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { useContext as
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
1
|
+
import { jsx as u, jsxs as M } from "react/jsx-runtime";
|
|
2
|
+
import { useContext as N, createContext as O, useMemo as g, useState as C, useEffect as F } from "react";
|
|
3
|
+
import { createRenderSchema as j, FormRenderer as k } from "@reformer/renderer-react";
|
|
4
|
+
import { i as f, a as R, p as m, b as A } from "./index-CI7YxPq2.js";
|
|
5
|
+
import { c as rr, f as er, g as tr, d as nr } from "./index-CI7YxPq2.js";
|
|
6
|
+
class h {
|
|
6
7
|
own = /* @__PURE__ */ new Map();
|
|
7
8
|
parent = null;
|
|
8
9
|
get(e) {
|
|
9
10
|
return this.own.get(e) ?? this.parent?.get(e);
|
|
10
11
|
}
|
|
11
12
|
getSource(e) {
|
|
12
|
-
const
|
|
13
|
-
if (!(!
|
|
14
|
-
return
|
|
13
|
+
const t = this.get(e);
|
|
14
|
+
if (!(!t || t.type !== "source"))
|
|
15
|
+
return t.component;
|
|
15
16
|
}
|
|
16
17
|
has(e) {
|
|
17
18
|
return this.own.has(e) || (this.parent?.has(e) ?? !1);
|
|
18
19
|
}
|
|
19
20
|
names() {
|
|
20
|
-
const e = this.parent?.names() ?? [],
|
|
21
|
-
return [.../* @__PURE__ */ new Set([...e, ...
|
|
21
|
+
const e = this.parent?.names() ?? [], t = Array.from(this.own.keys());
|
|
22
|
+
return [.../* @__PURE__ */ new Set([...e, ...t])];
|
|
22
23
|
}
|
|
23
|
-
_set(e,
|
|
24
|
-
this.own.has(e) && console.warn(`[ComponentRegistry] Overwriting entry: ${e}`), this.own.set(e,
|
|
24
|
+
_set(e, t) {
|
|
25
|
+
this.own.has(e) && console.warn(`[ComponentRegistry] Overwriting entry: ${e}`), this.own.set(e, t);
|
|
25
26
|
}
|
|
26
|
-
static withParent(e,
|
|
27
|
-
const n = new
|
|
28
|
-
return n.parent = e,
|
|
27
|
+
static withParent(e, t) {
|
|
28
|
+
const n = new h();
|
|
29
|
+
return n.parent = e, t.own.forEach((o, i) => {
|
|
29
30
|
n.own.set(i, o);
|
|
30
31
|
}), n;
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
|
-
function
|
|
34
|
-
const e = new
|
|
35
|
-
return
|
|
34
|
+
function K(r) {
|
|
35
|
+
const e = new h();
|
|
36
|
+
return r({
|
|
36
37
|
field(n, o, i) {
|
|
37
38
|
e._set(n, { component: o, type: "field", description: i });
|
|
38
39
|
},
|
|
@@ -44,169 +45,199 @@ function _(t) {
|
|
|
44
45
|
}
|
|
45
46
|
}), e;
|
|
46
47
|
}
|
|
47
|
-
const
|
|
48
|
-
function
|
|
49
|
-
const
|
|
50
|
-
const i = n?.get(
|
|
48
|
+
const v = "$fieldWrapper", S = O({});
|
|
49
|
+
function Q({ settings: r, children: e }) {
|
|
50
|
+
const t = N(S), n = g(() => t.registry && r.registry ? h.withParent(t.registry, r.registry) : r.registry ?? t.registry, [t.registry, r.registry]), o = g(() => {
|
|
51
|
+
const i = n?.get(v)?.component;
|
|
51
52
|
return {
|
|
52
|
-
...r,
|
|
53
53
|
...t,
|
|
54
|
+
...r,
|
|
54
55
|
registry: n,
|
|
55
|
-
fieldWrapper:
|
|
56
|
+
fieldWrapper: r.fieldWrapper ?? i ?? t.fieldWrapper
|
|
56
57
|
};
|
|
57
|
-
}, [
|
|
58
|
-
return /* @__PURE__ */
|
|
59
|
-
}
|
|
60
|
-
function
|
|
61
|
-
return
|
|
62
|
-
}
|
|
63
|
-
function
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
58
|
+
}, [t, r, n]);
|
|
59
|
+
return /* @__PURE__ */ u(S.Provider, { value: o, children: e });
|
|
60
|
+
}
|
|
61
|
+
function W() {
|
|
62
|
+
return N(S);
|
|
63
|
+
}
|
|
64
|
+
function $(r) {
|
|
65
|
+
const e = r;
|
|
66
|
+
return f(e.array) && typeof e.item == "object" && e.item !== null && "$template" in e.item;
|
|
67
|
+
}
|
|
68
|
+
function E(r) {
|
|
69
|
+
return f(r.value);
|
|
70
|
+
}
|
|
71
|
+
function D(r) {
|
|
72
|
+
return R(r.component) && !E(r) && !$(r);
|
|
73
|
+
}
|
|
74
|
+
function P(r, e) {
|
|
75
|
+
if (!r) return;
|
|
76
|
+
const t = m(r)?.arg;
|
|
77
|
+
if (!t) throw new Error(`Invalid $component operator: "${r}"`);
|
|
78
|
+
const n = e.get(t);
|
|
79
|
+
if (!n)
|
|
80
|
+
throw new Error(
|
|
81
|
+
`Component "${t}" not found in registry. Available: ${e.names().join(", ")}`
|
|
82
|
+
);
|
|
83
|
+
if (n.type === "source")
|
|
84
|
+
throw new Error(`Entry "${t}" is a 'source' and cannot be used as $component(...)`);
|
|
85
|
+
return n.component;
|
|
86
|
+
}
|
|
87
|
+
function L(r, e) {
|
|
88
|
+
const t = e.get(r);
|
|
89
|
+
if (!t)
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Data source "${r}" not found in registry. Available: ${e.names().join(", ")}`
|
|
92
|
+
);
|
|
93
|
+
return t.component;
|
|
94
|
+
}
|
|
95
|
+
function _(r, e) {
|
|
96
|
+
return e.split(".").reduce((t, n) => t == null ? t : t[n], r);
|
|
97
|
+
}
|
|
98
|
+
function I(r) {
|
|
99
|
+
if (r === null || typeof r != "object") return !1;
|
|
100
|
+
const e = r;
|
|
101
|
+
return f(e.value) || f(e.array) || R(e.component);
|
|
102
|
+
}
|
|
103
|
+
function V(r) {
|
|
104
|
+
return r == null ? r : JSON.parse(JSON.stringify(r));
|
|
105
|
+
}
|
|
106
|
+
function b(r, e, t) {
|
|
107
|
+
if (A(r)) return L(m(r).arg, t);
|
|
108
|
+
if (R(r)) return P(r, t);
|
|
109
|
+
if (f(r)) return e.signalAt(m(r).arg);
|
|
110
|
+
if (I(r)) return p(r, e, t);
|
|
111
|
+
if (Array.isArray(r)) return r.map((n) => b(n, e, t));
|
|
112
|
+
if (r !== null && typeof r == "object") {
|
|
113
|
+
const n = r, o = {};
|
|
114
|
+
for (const i of Object.keys(n)) o[i] = b(n[i], e, t);
|
|
115
|
+
return o;
|
|
71
116
|
}
|
|
72
|
-
|
|
73
|
-
throw new Error(`Invalid field path: "${e}" - path resolved to null/undefined`);
|
|
74
|
-
return n;
|
|
117
|
+
return r;
|
|
75
118
|
}
|
|
76
|
-
function
|
|
77
|
-
if (
|
|
78
|
-
const e = t;
|
|
79
|
-
return typeof e.model == "string" || typeof e.component == "string";
|
|
80
|
-
}
|
|
81
|
-
function S(t) {
|
|
82
|
-
return t !== null && typeof t == "object" && p(t.$template);
|
|
83
|
-
}
|
|
84
|
-
function x(t, e) {
|
|
85
|
-
const r = $(), n = s(t, r, e), o = ({ control: i }) => P(R, { node: n, form: i });
|
|
86
|
-
return o.displayName = "TemplateItemFC", o;
|
|
87
|
-
}
|
|
88
|
-
function a(t, e, r) {
|
|
89
|
-
if (typeof t == "string") {
|
|
90
|
-
const n = r.get(t);
|
|
91
|
-
if (n && n.type === "source") {
|
|
92
|
-
const o = n.component;
|
|
93
|
-
return typeof o == "function" ? (...i) => {
|
|
94
|
-
const c = o(...i);
|
|
95
|
-
return p(c) ? s(c, i[0], r) : c;
|
|
96
|
-
} : o;
|
|
97
|
-
}
|
|
98
|
-
return t;
|
|
99
|
-
}
|
|
100
|
-
if (S(t))
|
|
101
|
-
return x(t.$template, r);
|
|
102
|
-
if (p(t))
|
|
103
|
-
return s(t, e, r);
|
|
104
|
-
if (Array.isArray(t))
|
|
105
|
-
return t.map((n) => a(n, e, r));
|
|
106
|
-
if (typeof t == "function")
|
|
107
|
-
return (...n) => {
|
|
108
|
-
const o = t(...n);
|
|
109
|
-
return p(o) ? s(o, n[0], r) : o;
|
|
110
|
-
};
|
|
111
|
-
if (t !== null && typeof t == "object") {
|
|
112
|
-
const n = t, o = {};
|
|
113
|
-
let i = !1;
|
|
114
|
-
for (const c of Object.keys(n)) {
|
|
115
|
-
const f = a(n[c], e, r);
|
|
116
|
-
f !== n[c] && (i = !0), o[c] = f;
|
|
117
|
-
}
|
|
118
|
-
return i ? o : t;
|
|
119
|
-
}
|
|
120
|
-
return t;
|
|
121
|
-
}
|
|
122
|
-
function d(t, e, r) {
|
|
119
|
+
function w(r, e, t) {
|
|
120
|
+
if (!r) return;
|
|
123
121
|
const n = {};
|
|
124
|
-
for (const o of Object.keys(
|
|
125
|
-
const i = t[o];
|
|
126
|
-
if ((o === "control" || o.endsWith("Control")) && typeof i == "string") {
|
|
127
|
-
n[o] = h(e, i);
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
if ((o === "component" || o.endsWith("Component")) && typeof i == "string") {
|
|
131
|
-
const c = r.get(i);
|
|
132
|
-
if (c) {
|
|
133
|
-
n[o] = c.component;
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
n[o] = a(i, e, r);
|
|
138
|
-
}
|
|
122
|
+
for (const o of Object.keys(r)) n[o] = b(r[o], e, t);
|
|
139
123
|
return n;
|
|
140
124
|
}
|
|
141
|
-
function
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
125
|
+
function p(r, e, t) {
|
|
126
|
+
if ($(r)) {
|
|
127
|
+
const n = _(e, m(r.array).arg), o = r.item.$template, i = r.initialValue;
|
|
128
|
+
return {
|
|
129
|
+
...r.selector ? { selector: r.selector } : {},
|
|
130
|
+
array: n,
|
|
131
|
+
initialValue: () => i ? V(i) : {},
|
|
132
|
+
item: (y) => p(o, y, t),
|
|
133
|
+
componentProps: w(r.componentProps, e, t)
|
|
149
134
|
};
|
|
150
|
-
return t.selector && (i.selector = t.selector), t.componentProps && (i.componentProps = d(
|
|
151
|
-
t.componentProps,
|
|
152
|
-
e,
|
|
153
|
-
r
|
|
154
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
155
|
-
)), i;
|
|
156
135
|
}
|
|
157
|
-
if (
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
throw new Error(
|
|
165
|
-
`Entry "${t.component}" is a 'source' and cannot be used as component`
|
|
166
|
-
);
|
|
167
|
-
const i = {
|
|
168
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
169
|
-
component: o.component
|
|
136
|
+
if (E(r)) {
|
|
137
|
+
const n = m(r.value).arg, o = e.signalAt(n);
|
|
138
|
+
return !o && typeof console < "u" && console.warn(`[JsonRenderer/M1] No model signal for "${n}".`), {
|
|
139
|
+
...r.selector ? { selector: r.selector } : {},
|
|
140
|
+
value: o,
|
|
141
|
+
component: P(r.component, t),
|
|
142
|
+
componentProps: w(r.componentProps, e, t)
|
|
170
143
|
};
|
|
171
|
-
return t.selector && (i.selector = t.selector), t.componentProps && (i.componentProps = d(
|
|
172
|
-
t.componentProps,
|
|
173
|
-
e,
|
|
174
|
-
r
|
|
175
|
-
)), t.children && t.children.length > 0 && (i.children = t.children.map((c) => s(c, e, r))), i;
|
|
176
144
|
}
|
|
177
|
-
|
|
178
|
-
|
|
145
|
+
if (D(r))
|
|
146
|
+
return {
|
|
147
|
+
...r.selector ? { selector: r.selector } : {},
|
|
148
|
+
component: P(r.component, t),
|
|
149
|
+
componentProps: w(r.componentProps, e, t),
|
|
150
|
+
children: r.children?.map((n) => p(n, e, t))
|
|
151
|
+
};
|
|
152
|
+
throw new Error(`Invalid JSON node (M1): ${JSON.stringify(r)}`);
|
|
153
|
+
}
|
|
154
|
+
function U(r, e, t) {
|
|
155
|
+
return p(r.root, t, e);
|
|
156
|
+
}
|
|
157
|
+
function T(r, e, t) {
|
|
158
|
+
return () => p(r.root, t, e);
|
|
159
|
+
}
|
|
160
|
+
function q({ errors: r }) {
|
|
161
|
+
return /* @__PURE__ */ M(
|
|
162
|
+
"div",
|
|
163
|
+
{
|
|
164
|
+
role: "alert",
|
|
165
|
+
"data-testid": "schema-error-panel",
|
|
166
|
+
style: {
|
|
167
|
+
border: "1px solid #f5a9a9",
|
|
168
|
+
background: "#fff5f5",
|
|
169
|
+
color: "#9b1c1c",
|
|
170
|
+
borderRadius: 8,
|
|
171
|
+
padding: "16px 20px",
|
|
172
|
+
font: "14px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace"
|
|
173
|
+
},
|
|
174
|
+
children: [
|
|
175
|
+
/* @__PURE__ */ M("strong", { style: { display: "block", marginBottom: 8, fontSize: 15 }, children: [
|
|
176
|
+
"Невалидная JSON-схема формы (",
|
|
177
|
+
r.length,
|
|
178
|
+
")"
|
|
179
|
+
] }),
|
|
180
|
+
/* @__PURE__ */ u("ul", { style: { margin: 0, paddingLeft: 20 }, children: r.map((e, t) => /* @__PURE__ */ u("li", { style: { marginBottom: 4 }, children: e }, t)) })
|
|
181
|
+
]
|
|
182
|
+
}
|
|
179
183
|
);
|
|
180
184
|
}
|
|
181
|
-
function
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
function L({
|
|
185
|
-
schema: t,
|
|
185
|
+
function X({
|
|
186
|
+
schema: r,
|
|
186
187
|
renderBehavior: e,
|
|
187
|
-
onSchemaReady:
|
|
188
|
+
onSchemaReady: t,
|
|
189
|
+
validate: n = !1
|
|
188
190
|
}) {
|
|
189
|
-
const { registry:
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
191
|
+
const { registry: o, model: i, ...y } = W(), [a, d] = C(
|
|
192
|
+
n ? void 0 : null
|
|
193
|
+
);
|
|
194
|
+
F(() => {
|
|
195
|
+
if (!n) {
|
|
196
|
+
d(null);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
let l = !1;
|
|
200
|
+
return d(void 0), import("./validate.js").then(({ validateFormSchema: s }) => {
|
|
201
|
+
if (l) return;
|
|
202
|
+
const { valid: J, errors: x } = s(r, { registry: o });
|
|
203
|
+
d(J ? null : x);
|
|
204
|
+
}).catch((s) => {
|
|
205
|
+
l || d([`Schema validator failed to load: ${String(s)}`]);
|
|
206
|
+
}), () => {
|
|
207
|
+
l = !0;
|
|
208
|
+
};
|
|
209
|
+
}, [n, r, o]);
|
|
210
|
+
const c = g(() => {
|
|
211
|
+
if (!i)
|
|
212
|
+
throw new Error(
|
|
213
|
+
"JsonFormRenderer: settings.model is required (M1). Pass the FormModel via JsonRendererProvider."
|
|
214
|
+
);
|
|
215
|
+
if (a !== null) return null;
|
|
216
|
+
const l = T(r, o, i), s = j(l);
|
|
217
|
+
return e && e(s), s;
|
|
218
|
+
}, [r, o, e, i, a]);
|
|
219
|
+
return g(() => {
|
|
220
|
+
c && t && t(c);
|
|
221
|
+
}, [c]), a && a.length > 0 ? /* @__PURE__ */ u(q, { errors: a }) : c ? /* @__PURE__ */ u(k, { render: c, settings: y }) : null;
|
|
202
222
|
}
|
|
203
223
|
export {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
224
|
+
v as FIELD_WRAPPER,
|
|
225
|
+
X as JsonFormRenderer,
|
|
226
|
+
Q as JsonRendererProvider,
|
|
227
|
+
q as SchemaErrorPanel,
|
|
228
|
+
rr as buildFormSchemaMetaSchema,
|
|
229
|
+
U as convertJsonToM1Tree,
|
|
230
|
+
T as createRenderSchemaFromJsonM1,
|
|
231
|
+
K as defineRegistry,
|
|
232
|
+
er as formSchemaMetaSchema,
|
|
233
|
+
tr as getComponentNames,
|
|
234
|
+
nr as getSourceNames,
|
|
235
|
+
$ as isArrayNode,
|
|
236
|
+
R as isComponentOp,
|
|
237
|
+
D as isContainerNode,
|
|
238
|
+
A as isDataSourceOp,
|
|
239
|
+
E as isFieldNode,
|
|
240
|
+
f as isModelOp,
|
|
241
|
+
m as parseOperator,
|
|
242
|
+
W as useJsonRendererSettings
|
|
212
243
|
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Операторы JSON-схемы (M1) — СТРОКОВЫЕ ссылки на модель/реестр.
|
|
3
|
+
*
|
|
4
|
+
* JSON нельзя «вызвать функцию», поэтому привязки кодируются СТРОКАМИ функц-стиля с дискриминатором:
|
|
5
|
+
*
|
|
6
|
+
* - `"$model(path)"` — путь к полю/массиву модели: лист → `model.signalAt(path)`, массив → `model.<path>`.
|
|
7
|
+
* - `"$component(Name)"` — имя компонента в реестре (`reg.field`/`reg.container`).
|
|
8
|
+
* - `"$dataSource(NAME)"` — имя registry-source (`reg.source`): options/itemLabel/константы/loading-компоненты.
|
|
9
|
+
*
|
|
10
|
+
* Схема остаётся чистым JSON (копируется в `.json`, приходит строкой с сервера). Типобезопасность
|
|
11
|
+
* на этапе компиляции даётся template-literal типами ({@link ModelOp} и т.д.) — литерал
|
|
12
|
+
* `'$model(loanType)'` проверяется без вызова функций. Голые строки (не-`$`, напр. `label`) не резолвятся.
|
|
13
|
+
*
|
|
14
|
+
* @module reformer/renderer-json/operators
|
|
15
|
+
*/
|
|
16
|
+
/** Строка-оператор привязки к полю/массиву модели: `` `$model(${path})` ``. */
|
|
17
|
+
export type ModelOp = `$model(${string})`;
|
|
18
|
+
/** Строка-оператор ссылки на компонент реестра: `` `$component(${name})` ``. */
|
|
19
|
+
export type ComponentOp = `$component(${string})`;
|
|
20
|
+
/** Строка-оператор ссылки на registry-source: `` `$dataSource(${name})` ``. */
|
|
21
|
+
export type DataSourceOp = `$dataSource(${string})`;
|
|
22
|
+
/** Любой строковый оператор JSON-схемы. */
|
|
23
|
+
export type JsonOperator = ModelOp | ComponentOp | DataSourceOp;
|
|
24
|
+
/** Разобранный оператор: тип + аргумент (путь/имя). */
|
|
25
|
+
export interface ParsedOperator {
|
|
26
|
+
op: 'model' | 'component' | 'dataSource';
|
|
27
|
+
arg: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Разбор строки-оператора `"$op(arg)"`. Возвращает `null` для не-операторов (обычных строк),
|
|
31
|
+
* чтобы вызывающий оставил значение как есть.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* parseOperator('$model(loanType)'); // { op: 'model', arg: 'loanType' }
|
|
36
|
+
* parseOperator('$dataSource(LOAN_TYPES)'); // { op: 'dataSource', arg: 'LOAN_TYPES' }
|
|
37
|
+
* parseOperator('Введите сумму'); // null (обычная строка)
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function parseOperator(value: unknown): ParsedOperator | null;
|
|
41
|
+
/** Type-guard: строка — `"$model(...)"`. */
|
|
42
|
+
export declare const isModelOp: (v: unknown) => v is ModelOp;
|
|
43
|
+
/** Type-guard: строка — `"$component(...)"`. */
|
|
44
|
+
export declare const isComponentOp: (v: unknown) => v is ComponentOp;
|
|
45
|
+
/** Type-guard: строка — `"$dataSource(...)"`. */
|
|
46
|
+
export declare const isDataSourceOp: (v: unknown) => v is DataSourceOp;
|