@webx-ui/schema 0.1.1 → 0.2.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.
@@ -32,9 +32,9 @@ declare const _default: import('vue').DefineComponent<__VLS_PublicProps, {
32
32
  }>, {
33
33
  can: (permission: string) => boolean;
34
34
  patch: Patch;
35
+ translate: Translate;
35
36
  size: "sm" | "md" | "lg";
36
37
  disabled: boolean;
37
- translate: Translate;
38
38
  labelWidth: string;
39
39
  errors: ValidationErrors;
40
40
  types: TypeRegistry;
@@ -0,0 +1,29 @@
1
+ import { RenderContext } from './render';
2
+ import { ScreenModel, ScreenNode } from './types';
3
+ /**
4
+ * The one type with a nested model: the value is a list of records, and the node's
5
+ * children are the fields of one of them. Every child is bound by its own `name` to a key
6
+ * of the item, which is why a repeater is the only place where a name means nesting.
7
+ *
8
+ * Nothing of `WxRepeater`'s own is declared here — `title`, `itemLabel`, `min` and the
9
+ * rest ride in as attributes, so a prop the screen did not set stays unset instead of
10
+ * arriving as `false`.
11
+ */
12
+ type __VLS_Props = {
13
+ /** The node being drawn — its children are one item's fields. */
14
+ node: ScreenNode;
15
+ /** The renderer's context; each row gets a copy bound to its own item. */
16
+ context: RenderContext;
17
+ /** The renderer hands every field these two. A repeater has no use for either. */
18
+ name?: string;
19
+ localized?: boolean;
20
+ };
21
+ type __VLS_PublicProps = {
22
+ modelValue?: ScreenModel[];
23
+ } & __VLS_Props;
24
+ declare const _default: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
25
+ "update:modelValue": (value: ScreenModel[]) => any;
26
+ }, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
27
+ "onUpdate:modelValue"?: ((value: ScreenModel[]) => any) | undefined;
28
+ }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
29
+ export default _default;
package/dist/index.d.ts CHANGED
@@ -3,5 +3,5 @@ export { applyPatch, collectIds, findNode } from './patch';
3
3
  export { evaluateCondition, isVisible } from './visible';
4
4
  export { validatePatch, validateScreen } from './validate';
5
5
  export { coreTypes, defineTypes, describeTypes, typesTable, type TypeDescription } from './registry';
6
- export { translateDeep, words, TRANS_MARKER, type RenderContext } from './render';
6
+ export { translateDeep, words, WxScreenNodes, TRANS_MARKER, type RenderContext } from './render';
7
7
  export { default as WxScreenRenderer } from './ScreenRenderer';
package/dist/index.js CHANGED
@@ -1,27 +1,27 @@
1
- import { WxAlert as T, WxText as C, WxColorPicker as I, WxDatePicker as V, WxRadioGroup as M, WxCheckbox as q, WxSwitch as D, WxSelect as F, WxInputNumber as B, WxTextarea as K, WxInput as U, WxDivider as J, WxCol as G, WxRow as Y, WxCard as _, WxTab as H, WxTabs as L, WxFormItem as Q, WxForm as X } from "@webx-ui/core";
2
- import { defineComponent as N, h as d, useModel as Z, computed as p, watch as v, markRaw as ee, toRaw as ne, openBlock as te, createBlock as oe, unref as $, withCtx as ie, createVNode as re, mergeModels as A } from "vue";
3
- function m(e, t) {
1
+ import { WxFormItem as D, WxRepeater as F, WxAlert as B, WxText as U, WxColorPicker as K, WxDatePicker as v, WxRadioGroup as J, WxCheckbox as G, WxSwitch as Y, WxSelect as _, WxInputNumber as H, WxTextarea as L, WxInput as Q, WxDivider as X, WxCol as Z, WxRow as ee, WxCard as ne, WxTab as te, WxTabs as oe, WxForm as ie } from "@webx-ui/core";
2
+ import { defineComponent as $, h as d, useModel as N, computed as p, openBlock as O, createBlock as z, unref as y, mergeProps as re, withCtx as E, createVNode as V, mergeModels as x, watch as se, markRaw as ae, toRaw as le } from "vue";
3
+ function h(e, t) {
4
4
  for (let n = 0; n < e.length; n += 1) {
5
5
  const i = e[n];
6
6
  if (i.id === t) return { list: e, index: n, node: i };
7
7
  if (i.children) {
8
- const o = m(i.children, t);
8
+ const o = h(i.children, t);
9
9
  if (o) return o;
10
10
  }
11
11
  }
12
12
  return null;
13
13
  }
14
- function We(e, t) {
15
- return m(e, t)?.node ?? null;
14
+ function Se(e, t) {
15
+ return h(e, t)?.node ?? null;
16
16
  }
17
- function h(e) {
17
+ function b(e) {
18
18
  const t = [], n = (i) => {
19
19
  for (const o of i)
20
20
  t.push(o.id), o.children && n(o.children);
21
21
  };
22
22
  return n(e), t;
23
23
  }
24
- function S(e, t) {
24
+ function j(e, t) {
25
25
  if (!t || t === "last") return e.length;
26
26
  if (t === "first") return 0;
27
27
  const [n, i] = t.split(":", 2), o = e.findIndex((r) => r.id === i);
@@ -36,40 +36,40 @@ function f(e) {
36
36
  }
37
37
  return e;
38
38
  }
39
- function se(e, t) {
39
+ function ce(e, t) {
40
40
  const n = f(e), i = [];
41
41
  return t.forEach((o, r) => {
42
- const s = ae(n, o);
42
+ const s = de(n, o);
43
43
  s && i.push({ index: r, op: o, message: s });
44
44
  }), { root: n, errors: i };
45
45
  }
46
- function ae(e, t) {
47
- const n = m(e, t.target);
46
+ function de(e, t) {
47
+ const n = h(e, t.target);
48
48
  if (!n) return `target "${t.target}" not found`;
49
49
  switch (t.op) {
50
50
  case "add": {
51
- const i = j(e, t.node);
51
+ const i = P(e, t.node);
52
52
  if (i) return `id "${i}" already exists in the screen`;
53
- const o = n.node.children ??= [], r = S(o, t.position);
53
+ const o = n.node.children ??= [], r = j(o, t.position);
54
54
  return typeof r == "string" ? r : (o.splice(r, 0, f(t.node)), null);
55
55
  }
56
56
  case "remove":
57
57
  return n.list.splice(n.index, 1), null;
58
58
  case "replace": {
59
- const i = e.filter((r) => r !== n.node), o = j(i, t.node, n.node);
59
+ const i = e.filter((r) => r !== n.node), o = P(i, t.node, n.node);
60
60
  return o ? `id "${o}" already exists in the screen` : (n.list.splice(n.index, 1, f(t.node)), null);
61
61
  }
62
62
  case "move": {
63
63
  let i = n.list;
64
64
  if (t.to !== void 0) {
65
- const r = m(e, t.to);
65
+ const r = h(e, t.to);
66
66
  if (!r) return `destination "${t.to}" not found`;
67
- if (r.node === n.node || O(n.node, r.node))
67
+ if (r.node === n.node || T(n.node, r.node))
68
68
  return `cannot move "${t.target}" into itself`;
69
69
  i = r.node.children ??= [];
70
70
  }
71
71
  n.list.splice(n.index, 1);
72
- const o = S(i, t.position);
72
+ const o = j(i, t.position);
73
73
  return typeof o == "string" ? (n.list.splice(n.index, 0, n.node), o) : (i.splice(o, 0, n.node), null);
74
74
  }
75
75
  case "set": {
@@ -79,28 +79,28 @@ function ae(e, t) {
79
79
  }
80
80
  }
81
81
  }
82
- function O(e, t) {
83
- return (e.children ?? []).some((n) => n === t || O(n, t));
82
+ function T(e, t) {
83
+ return (e.children ?? []).some((n) => n === t || T(n, t));
84
84
  }
85
- function j(e, t, n) {
86
- const i = new Set(h(e));
87
- if (n) for (const o of h([n])) i.delete(o);
88
- return h([t]).find((o) => i.has(o)) ?? null;
85
+ function P(e, t, n) {
86
+ const i = new Set(b(e));
87
+ if (n) for (const o of b([n])) i.delete(o);
88
+ return b([t]).find((o) => i.has(o)) ?? null;
89
89
  }
90
- function b(e, t) {
90
+ function g(e, t) {
91
91
  return e === t ? !0 : e === null || t === null || typeof e != "object" || typeof t != "object" ? !1 : JSON.stringify(e) === JSON.stringify(t);
92
92
  }
93
- function g(e, t) {
94
- if ("all" in e) return e.all.every((i) => g(i, t));
95
- if ("any" in e) return e.any.some((i) => g(i, t));
93
+ function k(e, t) {
94
+ if ("all" in e) return e.all.every((i) => k(i, t));
95
+ if ("any" in e) return e.any.some((i) => k(i, t));
96
96
  const n = t[e.when];
97
- return "in" in e ? e.in.some((i) => b(i, n)) : "not" in e ? !b(e.not, n) : b(e.is, n);
97
+ return "in" in e ? e.in.some((i) => g(i, n)) : "not" in e ? !g(e.not, n) : g(e.is, n);
98
98
  }
99
- function ce(e, t) {
99
+ function fe(e, t) {
100
100
  const { visible: n } = e;
101
- return n === void 0 || n === !0 ? !0 : n === !1 ? !1 : g(n, t);
101
+ return n === void 0 || n === !0 ? !0 : n === !1 ? !1 : k(n, t);
102
102
  }
103
- const E = /* @__PURE__ */ new Set([
103
+ const C = /* @__PURE__ */ new Set([
104
104
  "id",
105
105
  "type",
106
106
  "name",
@@ -112,12 +112,12 @@ const E = /* @__PURE__ */ new Set([
112
112
  "slot",
113
113
  "visible",
114
114
  "can"
115
- ]), le = /* @__PURE__ */ new Set(["add", "remove", "replace", "move", "set"]);
116
- function u(e) {
115
+ ]), ue = /* @__PURE__ */ new Set(["add", "remove", "replace", "move", "set"]);
116
+ function m(e) {
117
117
  return typeof e == "object" && e !== null && !Array.isArray(e);
118
118
  }
119
- function R(e, t, n) {
120
- if (!u(e)) {
119
+ function I(e, t, n) {
120
+ if (!m(e)) {
121
121
  n.push({ path: t, message: "visible must be a boolean or a condition object" });
122
122
  return;
123
123
  }
@@ -127,7 +127,7 @@ function R(e, t, n) {
127
127
  n.push({ path: t, message: '"all" / "any" must be an array of conditions' });
128
128
  return;
129
129
  }
130
- o.forEach((r, s) => R(r, `${t}[${s}]`, n));
130
+ o.forEach((r, s) => I(r, `${t}[${s}]`, n));
131
131
  return;
132
132
  }
133
133
  if (typeof e.when != "string") {
@@ -137,125 +137,68 @@ function R(e, t, n) {
137
137
  const i = ["is", "in", "not"].filter((o) => o in e);
138
138
  i.length !== 1 ? n.push({ path: t, message: 'a condition needs exactly one of "is", "in", "not"' }) : i[0] === "in" && !Array.isArray(e.in) && n.push({ path: t, message: '"in" must be an array' });
139
139
  }
140
- function w(e, t, n, i) {
141
- if (!u(e)) {
140
+ function A(e, t, n, i) {
141
+ if (!m(e)) {
142
142
  n.push({ path: t, message: "a node must be an object" });
143
143
  return;
144
144
  }
145
145
  for (const o of Object.keys(e))
146
- E.has(o) || n.push({ path: t, message: `unknown key "${o}"` });
146
+ C.has(o) || n.push({ path: t, message: `unknown key "${o}"` });
147
147
  typeof e.id != "string" || e.id === "" ? n.push({ path: t, message: '"id" is required and must be a non-empty string' }) : i.has(e.id) ? n.push({ path: t, message: `duplicate id "${e.id}"` }) : i.add(e.id), (typeof e.type != "string" || e.type === "") && n.push({ path: t, message: '"type" is required and must be a non-empty string' });
148
148
  for (const o of ["name", "label", "help"])
149
149
  o in e && typeof e[o] != "string" && n.push({ path: t, message: `"${o}" must be a string` });
150
- "localized" in e && typeof e.localized != "boolean" && n.push({ path: t, message: '"localized" must be a boolean' }), "props" in e && !u(e.props) && n.push({ path: t, message: '"props" must be an object' }), "slot" in e && e.slot !== null && typeof e.slot != "string" && n.push({ path: t, message: '"slot" must be a string or null' }), "can" in e && e.can !== null && typeof e.can != "string" && n.push({ path: t, message: '"can" must be a string or null' }), "visible" in e && typeof e.visible != "boolean" && R(e.visible, `${t}.visible`, n), "children" in e && (Array.isArray(e.children) ? e.children.forEach(
151
- (o, r) => w(o, `${t}.children[${r}]`, n, i)
150
+ "localized" in e && typeof e.localized != "boolean" && n.push({ path: t, message: '"localized" must be a boolean' }), "props" in e && !m(e.props) && n.push({ path: t, message: '"props" must be an object' }), "slot" in e && e.slot !== null && typeof e.slot != "string" && n.push({ path: t, message: '"slot" must be a string or null' }), "can" in e && e.can !== null && typeof e.can != "string" && n.push({ path: t, message: '"can" must be a string or null' }), "visible" in e && typeof e.visible != "boolean" && I(e.visible, `${t}.visible`, n), "children" in e && (Array.isArray(e.children) ? e.children.forEach(
151
+ (o, r) => A(o, `${t}.children[${r}]`, n, i)
152
152
  ) : n.push({ path: t, message: '"children" must be an array' }));
153
153
  }
154
- function $e(e) {
154
+ function je(e) {
155
155
  const t = [];
156
156
  if (!Array.isArray(e)) return [{ path: "root", message: "root must be an array of nodes" }];
157
157
  const n = /* @__PURE__ */ new Set();
158
- return e.forEach((i, o) => w(i, `root[${o}]`, t, n)), t;
158
+ return e.forEach((i, o) => A(i, `root[${o}]`, t, n)), t;
159
159
  }
160
- function Ae(e) {
160
+ function Pe(e) {
161
161
  const t = [];
162
162
  return Array.isArray(e) ? (e.forEach((n, i) => {
163
163
  const o = `patch[${i}]`;
164
- if (!u(n)) {
164
+ if (!m(n)) {
165
165
  t.push({ path: o, message: "an operation must be an object" });
166
166
  return;
167
167
  }
168
- if (typeof n.op != "string" || !le.has(n.op)) {
168
+ if (typeof n.op != "string" || !ue.has(n.op)) {
169
169
  t.push({ path: o, message: '"op" must be one of add, remove, replace, move, set' });
170
170
  return;
171
171
  }
172
- if ((typeof n.target != "string" || n.target === "") && t.push({ path: o, message: '"target" is required: the id of a node' }), (n.op === "add" || n.op === "replace") && !u(n.node) ? t.push({ path: o, message: `"${n.op}" needs a "node"` }) : (n.op === "add" || n.op === "replace") && w(n.node, `${o}.node`, t, /* @__PURE__ */ new Set()), "position" in n && !de(n.position) && t.push({ path: o, message: '"position" must be first, last, before:<id> or after:<id>' }), n.op === "move" && "to" in n && typeof n.to != "string" && t.push({ path: o, message: '"to" must be the id of the new parent' }), n.op === "set")
172
+ if ((typeof n.target != "string" || n.target === "") && t.push({ path: o, message: '"target" is required: the id of a node' }), (n.op === "add" || n.op === "replace") && !m(n.node) ? t.push({ path: o, message: `"${n.op}" needs a "node"` }) : (n.op === "add" || n.op === "replace") && A(n.node, `${o}.node`, t, /* @__PURE__ */ new Set()), "position" in n && !pe(n.position) && t.push({ path: o, message: '"position" must be first, last, before:<id> or after:<id>' }), n.op === "move" && "to" in n && typeof n.to != "string" && t.push({ path: o, message: '"to" must be the id of the new parent' }), n.op === "set")
173
173
  for (const r of Object.keys(n))
174
- r !== "op" && r !== "target" && (!E.has(r) || r === "id") && t.push({ path: o, message: `"set" cannot change "${r}"` });
174
+ r !== "op" && r !== "target" && (!C.has(r) || r === "id") && t.push({ path: o, message: `"set" cannot change "${r}"` });
175
175
  }), t) : [{ path: "patch", message: "a patch must be an array" }];
176
176
  }
177
- function de(e) {
178
- return typeof e == "string" && (e === "first" || e === "last" || e.startsWith("before:") || e.startsWith("after:"));
179
- }
180
- const fe = {
181
- "wx-tabs": { component: L, kind: "layout" },
182
- "wx-tab": {
183
- component: H,
184
- kind: "layout",
185
- labelProp: "label",
186
- // A tab is selected by value, and the node's id is the one stable thing it has.
187
- bind: (e) => ({ value: e.id })
188
- },
189
- "wx-card": { component: _, kind: "layout", labelProp: "title" },
190
- "wx-row": { component: Y, kind: "layout" },
191
- "wx-col": { component: G, kind: "layout" },
192
- "wx-divider": { component: J, kind: "layout", labelProp: "label" },
193
- "wx-input": { component: U, kind: "field" },
194
- "wx-textarea": { component: K, kind: "field" },
195
- "wx-input-number": { component: B, kind: "field" },
196
- "wx-select": { component: F, kind: "field" },
197
- "wx-switch": { component: D, kind: "field" },
198
- "wx-checkbox": { component: q, kind: "field" },
199
- "wx-radio-group": { component: M, kind: "field" },
200
- "wx-date-picker": { component: V, kind: "field" },
201
- "wx-color-picker": { component: I, kind: "field" },
202
- "wx-text": { component: C, kind: "display" },
203
- "wx-alert": { component: T, kind: "display", labelProp: "title" }
204
- };
205
- function Se(e) {
206
- return e;
207
- }
208
- function ue(e) {
209
- const t = e.component;
210
- return t.name ?? t.__name ?? "anonymous";
211
- }
212
177
  function pe(e) {
213
- return e.kind === "field" ? "form item" : e.labelProp ? `prop \`${e.labelProp}\`` : e.kind === "display" ? "default slot" : "—";
214
- }
215
- function me(e) {
216
- return Object.entries(e).map(([t, n]) => ({
217
- type: t,
218
- kind: n.kind,
219
- component: ue(n),
220
- label: pe(n)
221
- }));
222
- }
223
- function je(e) {
224
- const t = me(e).map((r) => [
225
- `\`${r.type}\``,
226
- r.kind,
227
- `\`${r.component}\``,
228
- r.label
229
- ]), n = ["Type", "Kind", "Component", "`label` goes to"], i = n.map(
230
- (r, s) => Math.max(r.length, ...t.map((a) => a[s].length))
231
- ), o = (r) => `| ${r.map((s, a) => s.padEnd(i[a])).join(" | ")} |`;
232
- return [o(n), o(i.map((r) => "-".repeat(r))), ...t.map(o)].join(
233
- `
234
- `
235
- );
178
+ return typeof e == "string" && (e === "first" || e === "last" || e.startsWith("before:") || e.startsWith("after:"));
236
179
  }
237
- const P = "trans::", ye = (e) => e;
238
- function x(e, t) {
239
- return typeof e == "string" && e.startsWith(P) ? t(e.slice(P.length)) : e;
180
+ const R = "trans::", me = (e) => e;
181
+ function w(e, t) {
182
+ return typeof e == "string" && e.startsWith(R) ? t(e.slice(R.length)) : e;
240
183
  }
241
- function k(e, t) {
242
- if (typeof e == "string") return x(e, t);
243
- if (Array.isArray(e)) return e.map((n) => k(n, t));
184
+ function W(e, t) {
185
+ if (typeof e == "string") return w(e, t);
186
+ if (Array.isArray(e)) return e.map((n) => W(n, t));
244
187
  if (e && typeof e == "object") {
245
188
  const n = {};
246
- for (const [i, o] of Object.entries(e)) n[i] = k(o, t);
189
+ for (const [i, o] of Object.entries(e)) n[i] = W(o, t);
247
190
  return n;
248
191
  }
249
192
  return e;
250
193
  }
251
- function he(e) {
194
+ function ye(e) {
252
195
  return d(
253
196
  "div",
254
197
  { key: e.id, class: "wx-screen__unknown", role: "note" },
255
198
  `Unknown type: ${e.type}`
256
199
  );
257
200
  }
258
- function be(e, t, n) {
201
+ function he(e, t, n) {
259
202
  const i = /* @__PURE__ */ new Map();
260
203
  for (const r of e.children ?? []) {
261
204
  const s = r.slot ?? t, a = i.get(s) ?? [];
@@ -263,57 +206,150 @@ function be(e, t, n) {
263
206
  }
264
207
  const o = {};
265
208
  for (const [r, s] of i)
266
- o[r] = () => z(s, n);
209
+ o[r] = () => M(s, n);
267
210
  return o;
268
211
  }
269
- function ge(e, t) {
270
- if (e.can && !t.can(e.can) || !ce(e, t.model)) return null;
212
+ function be(e, t) {
213
+ if (e.can && !t.can(e.can) || !fe(e, t.model)) return null;
271
214
  const n = t.types[e.type];
272
- if (!n) return he(e);
215
+ if (!n) return ye(e);
273
216
  const { translate: i } = t, o = {
274
217
  key: e.id,
275
- ...k(e.props ?? {}, i),
218
+ ...W(e.props ?? {}, i),
276
219
  ...n.bind?.(e)
277
- }, r = x(e.label, i);
220
+ }, r = w(e.label, i);
278
221
  if (n.kind === "field") {
279
222
  const s = e.name, a = d(n.component, {
280
223
  ...o,
224
+ /* A nested field draws the children itself, so it needs the node and the way down. */
225
+ ...n.nested ? { node: e, context: t } : {},
281
226
  name: s,
282
227
  localized: e.localized || void 0,
283
228
  modelValue: s === void 0 ? void 0 : t.model[s],
284
- "onUpdate:modelValue": (y) => {
285
- s !== void 0 && t.update(s, y);
229
+ "onUpdate:modelValue": (u) => {
230
+ s !== void 0 && t.update(s, u);
286
231
  }
287
232
  });
288
233
  return d(
289
- Q,
290
- { key: e.id, name: s, label: r, help: x(e.help, i) },
234
+ D,
235
+ { key: e.id, name: s, label: r, help: w(e.help, i) },
291
236
  () => a
292
237
  );
293
238
  }
294
- return n.kind === "layout" ? (n.labelProp && r !== void 0 && (o[n.labelProp] = r), d(n.component, o, be(e, n.childrenSlot ?? "default", t))) : n.labelProp ? (r !== void 0 && (o[n.labelProp] = r), d(n.component, o)) : d(n.component, o, r === void 0 ? void 0 : () => r);
239
+ return n.kind === "layout" ? (n.labelProp && r !== void 0 && (o[n.labelProp] = r), d(n.component, o, he(e, n.childrenSlot ?? "default", t))) : n.labelProp ? (r !== void 0 && (o[n.labelProp] = r), d(n.component, o)) : d(n.component, o, r === void 0 ? void 0 : () => r);
295
240
  }
296
- function z(e, t) {
241
+ function M(e, t) {
297
242
  const n = [];
298
243
  for (const i of e) {
299
- const o = ge(i, t);
244
+ const o = be(i, t);
300
245
  o && n.push(o);
301
246
  }
302
247
  return n;
303
248
  }
304
- const xe = N({
249
+ const q = $({
305
250
  name: "WxScreenNodes",
306
251
  props: {
307
252
  nodes: { type: Array, required: !0 },
308
253
  context: { type: Object, required: !0 }
309
254
  },
310
255
  setup(e) {
311
- return () => z(e.nodes, e.context);
256
+ return () => M(e.nodes, e.context);
257
+ }
258
+ }), ge = /* @__PURE__ */ $({
259
+ name: "WxScreenRepeater",
260
+ inheritAttrs: !1,
261
+ __name: "ScreenRepeater",
262
+ props: /* @__PURE__ */ x({
263
+ node: {},
264
+ context: {},
265
+ name: {},
266
+ localized: { type: Boolean }
267
+ }, {
268
+ modelValue: { default: () => [] },
269
+ modelModifiers: {}
270
+ }),
271
+ emits: ["update:modelValue"],
272
+ setup(e) {
273
+ const t = N(e, "modelValue"), n = p(() => Array.isArray(t.value) ? t.value : []);
274
+ return (i, o) => (O(), z(y(F), re(i.$attrs, {
275
+ "model-value": n.value,
276
+ "onUpdate:modelValue": o[0] || (o[0] = (r) => t.value = r)
277
+ }), {
278
+ default: E(({ item: r, update: s }) => [
279
+ V(y(q), {
280
+ nodes: e.node.children ?? [],
281
+ context: {
282
+ ...e.context,
283
+ model: r,
284
+ update: (a, u) => s({ [a]: u })
285
+ }
286
+ }, null, 8, ["nodes", "context"])
287
+ ]),
288
+ _: 1
289
+ }, 16, ["model-value"]));
312
290
  }
313
- }), Pe = /* @__PURE__ */ N({
291
+ }), xe = {
292
+ "wx-tabs": { component: oe, kind: "layout" },
293
+ "wx-tab": {
294
+ component: te,
295
+ kind: "layout",
296
+ labelProp: "label",
297
+ // A tab is selected by value, and the node's id is the one stable thing it has.
298
+ bind: (e) => ({ value: e.id })
299
+ },
300
+ "wx-card": { component: ne, kind: "layout", labelProp: "title" },
301
+ "wx-row": { component: ee, kind: "layout" },
302
+ "wx-col": { component: Z, kind: "layout" },
303
+ "wx-divider": { component: X, kind: "layout", labelProp: "label" },
304
+ "wx-input": { component: Q, kind: "field" },
305
+ "wx-textarea": { component: L, kind: "field" },
306
+ "wx-input-number": { component: H, kind: "field" },
307
+ "wx-select": { component: _, kind: "field" },
308
+ "wx-switch": { component: Y, kind: "field" },
309
+ "wx-checkbox": { component: G, kind: "field" },
310
+ "wx-radio-group": { component: J, kind: "field" },
311
+ "wx-date-picker": { component: v, kind: "field" },
312
+ "wx-color-picker": { component: K, kind: "field" },
313
+ "wx-repeater": { component: ge, kind: "field", nested: !0 },
314
+ "wx-text": { component: U, kind: "display" },
315
+ "wx-alert": { component: B, kind: "display", labelProp: "title" }
316
+ };
317
+ function Re(e) {
318
+ return e;
319
+ }
320
+ function ke(e) {
321
+ const t = e.component;
322
+ return t.name ?? t.__name ?? "anonymous";
323
+ }
324
+ function we(e) {
325
+ return e.kind === "field" ? "form item" : e.labelProp ? `prop \`${e.labelProp}\`` : e.kind === "display" ? "default slot" : "—";
326
+ }
327
+ function We(e) {
328
+ return Object.entries(e).map(([t, n]) => ({
329
+ type: t,
330
+ kind: n.kind,
331
+ component: ke(n),
332
+ label: we(n)
333
+ }));
334
+ }
335
+ function Ne(e) {
336
+ const t = We(e).map((r) => [
337
+ `\`${r.type}\``,
338
+ r.kind,
339
+ `\`${r.component}\``,
340
+ r.label
341
+ ]), n = ["Type", "Kind", "Component", "`label` goes to"], i = n.map(
342
+ (r, s) => Math.max(r.length, ...t.map((a) => a[s].length))
343
+ ), o = (r) => `| ${r.map((s, a) => s.padEnd(i[a])).join(" | ")} |`;
344
+ return [o(n), o(i.map((r) => "-".repeat(r))), ...t.map(o)].join(
345
+ `
346
+ `
347
+ );
348
+ }
349
+ const Oe = /* @__PURE__ */ $({
314
350
  name: "WxScreenRenderer",
315
351
  __name: "ScreenRenderer",
316
- props: /* @__PURE__ */ A({
352
+ props: /* @__PURE__ */ x({
317
353
  root: {},
318
354
  patch: { default: () => [] },
319
355
  errors: { default: void 0 },
@@ -328,36 +364,36 @@ const xe = N({
328
364
  modelValue: { default: () => ({}) },
329
365
  modelModifiers: {}
330
366
  }),
331
- emits: /* @__PURE__ */ A(["patchError"], ["update:modelValue"]),
367
+ emits: /* @__PURE__ */ x(["patchError"], ["update:modelValue"]),
332
368
  setup(e, { expose: t, emit: n }) {
333
- const i = e, o = n, r = Z(e, "modelValue"), s = p(() => se(i.root, i.patch));
334
- v(
369
+ const i = e, o = n, r = N(e, "modelValue"), s = p(() => ce(i.root, i.patch));
370
+ se(
335
371
  () => s.value.errors,
336
- (c) => {
337
- for (const l of c)
338
- console.error(`[webx-ui/schema] patch[${l.index}] ${l.op.op}: ${l.message}`);
339
- o("patchError", c);
372
+ (l) => {
373
+ for (const c of l)
374
+ console.error(`[webx-ui/schema] patch[${c.index}] ${c.op.op}: ${c.message}`);
375
+ o("patchError", l);
340
376
  },
341
377
  { immediate: !0 }
342
378
  );
343
379
  const a = p(() => {
344
- const c = {};
345
- for (const [l, W] of Object.entries({ ...fe, ...i.types }))
346
- c[l] = { ...W, component: ee(ne(W.component)) };
347
- return c;
348
- }), y = p(() => ({
380
+ const l = {};
381
+ for (const [c, S] of Object.entries({ ...xe, ...i.types }))
382
+ l[c] = { ...S, component: ae(le(S.component)) };
383
+ return l;
384
+ }), u = p(() => ({
349
385
  types: a.value,
350
386
  model: r.value,
351
- update: (c, l) => {
352
- r.value = { ...r.value, [c]: l };
387
+ update: (l, c) => {
388
+ r.value = { ...r.value, [l]: c };
353
389
  },
354
- translate: i.translate ?? ye,
390
+ translate: i.translate ?? me,
355
391
  can: i.can ?? (() => !0)
356
392
  }));
357
393
  return t({
358
394
  /** The tree after the patch — what is actually on screen. */
359
395
  tree: p(() => s.value.root)
360
- }), (c, l) => (te(), oe($(X), {
396
+ }), (l, c) => (O(), z(y(ie), {
361
397
  class: "wx-screen",
362
398
  errors: e.errors,
363
399
  disabled: e.disabled,
@@ -365,10 +401,10 @@ const xe = N({
365
401
  "label-width": e.labelWidth,
366
402
  size: e.size
367
403
  }, {
368
- default: ie(() => [
369
- re($(xe), {
404
+ default: E(() => [
405
+ V(y(q), {
370
406
  nodes: s.value.root,
371
- context: y.value
407
+ context: u.value
372
408
  }, null, 8, ["nodes", "context"])
373
409
  ]),
374
410
  _: 1
@@ -376,20 +412,21 @@ const xe = N({
376
412
  }
377
413
  });
378
414
  export {
379
- P as TRANS_MARKER,
380
- Pe as WxScreenRenderer,
381
- se as applyPatch,
382
- h as collectIds,
383
- fe as coreTypes,
384
- Se as defineTypes,
385
- me as describeTypes,
386
- g as evaluateCondition,
387
- We as findNode,
388
- ce as isVisible,
389
- k as translateDeep,
390
- je as typesTable,
391
- Ae as validatePatch,
392
- $e as validateScreen,
393
- x as words
415
+ R as TRANS_MARKER,
416
+ q as WxScreenNodes,
417
+ Oe as WxScreenRenderer,
418
+ ce as applyPatch,
419
+ b as collectIds,
420
+ xe as coreTypes,
421
+ Re as defineTypes,
422
+ We as describeTypes,
423
+ k as evaluateCondition,
424
+ Se as findNode,
425
+ fe as isVisible,
426
+ W as translateDeep,
427
+ Ne as typesTable,
428
+ Pe as validatePatch,
429
+ je as validateScreen,
430
+ w as words
394
431
  };
395
432
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/patch.ts","../src/visible.ts","../src/validate.ts","../src/registry.ts","../src/render.ts","../src/ScreenRenderer.vue"],"sourcesContent":["import type { Patch, PatchError, PatchOperation, PatchPosition, ScreenNode } from './types'\n\ninterface Located {\n /** The array the node sits in: the root, or a parent's `children`. */\n list: ScreenNode[]\n index: number\n node: ScreenNode\n}\n\n/** Depth-first search by `id`, remembering the array the match lives in. */\nexport function locate(root: ScreenNode[], id: string): Located | null {\n for (let index = 0; index < root.length; index += 1) {\n const node = root[index]!\n if (node.id === id) return { list: root, index, node }\n if (node.children) {\n const found = locate(node.children, id)\n if (found) return found\n }\n }\n return null\n}\n\nexport function findNode(root: ScreenNode[], id: string): ScreenNode | null {\n return locate(root, id)?.node ?? null\n}\n\n/** Every `id` in the tree, in document order. Duplicates are kept, so callers can spot them. */\nexport function collectIds(root: ScreenNode[]): string[] {\n const ids: string[] = []\n const walk = (nodes: ScreenNode[]) => {\n for (const node of nodes) {\n ids.push(node.id)\n if (node.children) walk(node.children)\n }\n }\n walk(root)\n return ids\n}\n\n/** Index a node should be inserted at in `list`, or a message when the anchor is missing. */\nfunction indexFor(list: ScreenNode[], position: PatchPosition | undefined): number | string {\n if (!position || position === 'last') return list.length\n if (position === 'first') return 0\n const [where, anchor] = position.split(':', 2) as ['before' | 'after', string]\n const at = list.findIndex((node) => node.id === anchor)\n if (at === -1) return `no sibling \"${anchor}\" to insert ${where}`\n return where === 'before' ? at : at + 1\n}\n\n/**\n * Deep copy of a JSON value. `structuredClone` refuses a Vue reactive proxy, and a\n * tree that came out of a `ref` is exactly that; reading through the proxy is fine.\n */\nexport function clone<T>(value: T): T {\n if (Array.isArray(value)) return value.map(clone) as T\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {}\n for (const [key, item] of Object.entries(value)) out[key] = clone(item)\n return out as T\n }\n return value\n}\n\n/**\n * Applies a patch to a tree and returns the result as a new tree; the input is not\n * touched. An operation that cannot be applied — a `target` that does not exist, an\n * anchor that is missing — is skipped and reported, and the ones after it still run:\n * a project patch that survived a rename in the module must not silence the rest.\n */\nexport function applyPatch(\n root: ScreenNode[],\n patch: Patch,\n): { root: ScreenNode[]; errors: PatchError[] } {\n const tree = clone(root)\n const errors: PatchError[] = []\n\n patch.forEach((op, index) => {\n const message = apply(tree, op)\n if (message) errors.push({ index, op, message })\n })\n\n return { root: tree, errors }\n}\n\n/** Mutates `tree`; returns a message when the operation was refused. */\nfunction apply(tree: ScreenNode[], op: PatchOperation): string | null {\n const found = locate(tree, op.target)\n if (!found) return `target \"${op.target}\" not found`\n\n switch (op.op) {\n case 'add': {\n const clash = duplicateIn(tree, op.node)\n if (clash) return `id \"${clash}\" already exists in the screen`\n const list = (found.node.children ??= [])\n const at = indexFor(list, op.position)\n if (typeof at === 'string') return at\n list.splice(at, 0, clone(op.node))\n return null\n }\n case 'remove':\n found.list.splice(found.index, 1)\n return null\n case 'replace': {\n // The replaced node's own ids are gone, so only the rest of the tree can clash.\n const others = tree.filter((node) => node !== found.node)\n const clash = duplicateIn(others, op.node, found.node)\n if (clash) return `id \"${clash}\" already exists in the screen`\n found.list.splice(found.index, 1, clone(op.node))\n return null\n }\n case 'move': {\n let list = found.list\n if (op.to !== undefined) {\n const parent = locate(tree, op.to)\n if (!parent) return `destination \"${op.to}\" not found`\n if (parent.node === found.node || contains(found.node, parent.node)) {\n return `cannot move \"${op.target}\" into itself`\n }\n list = parent.node.children ??= []\n }\n found.list.splice(found.index, 1)\n const at = indexFor(list, op.position)\n if (typeof at === 'string') {\n // Put it back where it was: a refused move leaves the tree untouched.\n found.list.splice(found.index, 0, found.node)\n return at\n }\n list.splice(at, 0, found.node)\n return null\n }\n case 'set': {\n for (const [key, value] of Object.entries(op)) {\n if (key === 'op' || key === 'target') continue\n if (key === 'props') {\n found.node.props = { ...found.node.props, ...(value as Record<string, unknown>) }\n } else {\n ;(found.node as unknown as Record<string, unknown>)[key] = clone(value)\n }\n }\n return null\n }\n }\n}\n\nfunction contains(ancestor: ScreenNode, node: ScreenNode): boolean {\n return (ancestor.children ?? []).some((child) => child === node || contains(child, node))\n}\n\n/** First id of `incoming` that already exists in `tree`, ignoring `except`'s own subtree. */\nfunction duplicateIn(tree: ScreenNode[], incoming: ScreenNode, except?: ScreenNode): string | null {\n const existing = new Set(collectIds(tree))\n if (except) for (const id of collectIds([except])) existing.delete(id)\n return collectIds([incoming]).find((id) => existing.has(id)) ?? null\n}\n","import type { ScreenModel, ScreenNode, VisibilityCondition } from './types'\n\n/** Loose enough for `{ \"is\": 1 }` to match a numeric input, strict enough for `\"1\"` not to. */\nfunction same(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (a === null || b === null || typeof a !== 'object' || typeof b !== 'object') return false\n return JSON.stringify(a) === JSON.stringify(b)\n}\n\nexport function evaluateCondition(condition: VisibilityCondition, model: ScreenModel): boolean {\n if ('all' in condition) return condition.all.every((item) => evaluateCondition(item, model))\n if ('any' in condition) return condition.any.some((item) => evaluateCondition(item, model))\n\n const value = model[condition.when]\n if ('in' in condition) return condition.in.some((item) => same(item, value))\n if ('not' in condition) return !same(condition.not, value)\n return same(condition.is, value)\n}\n\n/** Whether a node should render, given the current model. Absent `visible` means yes. */\nexport function isVisible(node: ScreenNode, model: ScreenModel): boolean {\n const { visible } = node\n if (visible === undefined || visible === true) return true\n if (visible === false) return false\n return evaluateCondition(visible, model)\n}\n","import type { Patch, ScreenError, ScreenNode, VisibilityCondition } from './types'\n\nconst NODE_KEYS = new Set([\n 'id',\n 'type',\n 'name',\n 'label',\n 'help',\n 'localized',\n 'props',\n 'children',\n 'slot',\n 'visible',\n 'can',\n])\n\nconst OPS = new Set(['add', 'remove', 'replace', 'move', 'set'])\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction checkCondition(value: unknown, path: string, errors: ScreenError[]): void {\n if (!isRecord(value)) {\n errors.push({ path, message: 'visible must be a boolean or a condition object' })\n return\n }\n if ('all' in value || 'any' in value) {\n const list = (value.all ?? value.any) as unknown\n if (!Array.isArray(list)) {\n errors.push({ path, message: '\"all\" / \"any\" must be an array of conditions' })\n return\n }\n list.forEach((item, index) => checkCondition(item, `${path}[${index}]`, errors))\n return\n }\n if (typeof value.when !== 'string') {\n errors.push({ path, message: 'a condition needs \"when\": the name of a field' })\n return\n }\n const forms = ['is', 'in', 'not'].filter((key) => key in value)\n if (forms.length !== 1) {\n errors.push({ path, message: 'a condition needs exactly one of \"is\", \"in\", \"not\"' })\n } else if (forms[0] === 'in' && !Array.isArray(value.in)) {\n errors.push({ path, message: '\"in\" must be an array' })\n }\n}\n\nfunction checkNode(value: unknown, path: string, errors: ScreenError[], seen: Set<string>): void {\n if (!isRecord(value)) {\n errors.push({ path, message: 'a node must be an object' })\n return\n }\n\n for (const key of Object.keys(value)) {\n if (!NODE_KEYS.has(key)) errors.push({ path, message: `unknown key \"${key}\"` })\n }\n\n if (typeof value.id !== 'string' || value.id === '') {\n errors.push({ path, message: '\"id\" is required and must be a non-empty string' })\n } else if (seen.has(value.id)) {\n errors.push({ path, message: `duplicate id \"${value.id}\"` })\n } else {\n seen.add(value.id)\n }\n\n if (typeof value.type !== 'string' || value.type === '') {\n errors.push({ path, message: '\"type\" is required and must be a non-empty string' })\n }\n\n for (const key of ['name', 'label', 'help'] as const) {\n if (key in value && typeof value[key] !== 'string') {\n errors.push({ path, message: `\"${key}\" must be a string` })\n }\n }\n if ('localized' in value && typeof value.localized !== 'boolean') {\n errors.push({ path, message: '\"localized\" must be a boolean' })\n }\n if ('props' in value && !isRecord(value.props)) {\n errors.push({ path, message: '\"props\" must be an object' })\n }\n if ('slot' in value && value.slot !== null && typeof value.slot !== 'string') {\n errors.push({ path, message: '\"slot\" must be a string or null' })\n }\n if ('can' in value && value.can !== null && typeof value.can !== 'string') {\n errors.push({ path, message: '\"can\" must be a string or null' })\n }\n if ('visible' in value && typeof value.visible !== 'boolean') {\n checkCondition(value.visible as VisibilityCondition, `${path}.visible`, errors)\n }\n\n if ('children' in value) {\n if (!Array.isArray(value.children)) {\n errors.push({ path, message: '\"children\" must be an array' })\n } else {\n value.children.forEach((child, index) =>\n checkNode(child, `${path}.children[${index}]`, errors, seen),\n )\n }\n }\n}\n\n/**\n * Checks a tree the way the JSON schema would, without a schema library: required\n * keys, closed key set, value types, and — what a schema cannot say — unique ids.\n * An empty list means the tree is sound.\n */\nexport function validateScreen(root: unknown): ScreenError[] {\n const errors: ScreenError[] = []\n if (!Array.isArray(root)) return [{ path: 'root', message: 'root must be an array of nodes' }]\n const seen = new Set<string>()\n root.forEach((node, index) => checkNode(node, `root[${index}]`, errors, seen))\n return errors\n}\n\n/** Same for a patch: each operation has the keys its `op` calls for. */\nexport function validatePatch(patch: unknown): ScreenError[] {\n const errors: ScreenError[] = []\n if (!Array.isArray(patch)) return [{ path: 'patch', message: 'a patch must be an array' }]\n\n patch.forEach((op, index) => {\n const path = `patch[${index}]`\n if (!isRecord(op)) {\n errors.push({ path, message: 'an operation must be an object' })\n return\n }\n if (typeof op.op !== 'string' || !OPS.has(op.op)) {\n errors.push({ path, message: '\"op\" must be one of add, remove, replace, move, set' })\n return\n }\n if (typeof op.target !== 'string' || op.target === '') {\n errors.push({ path, message: '\"target\" is required: the id of a node' })\n }\n if ((op.op === 'add' || op.op === 'replace') && !isRecord(op.node)) {\n errors.push({ path, message: `\"${op.op}\" needs a \"node\"` })\n } else if (op.op === 'add' || op.op === 'replace') {\n checkNode(op.node, `${path}.node`, errors, new Set())\n }\n if ('position' in op && !isPosition(op.position)) {\n errors.push({ path, message: '\"position\" must be first, last, before:<id> or after:<id>' })\n }\n if (op.op === 'move' && 'to' in op && typeof op.to !== 'string') {\n errors.push({ path, message: '\"to\" must be the id of the new parent' })\n }\n if (op.op === 'set') {\n for (const key of Object.keys(op)) {\n if (key !== 'op' && key !== 'target' && (!NODE_KEYS.has(key) || key === 'id')) {\n errors.push({ path, message: `\"set\" cannot change \"${key}\"` })\n }\n }\n }\n })\n\n return errors\n}\n\nfunction isPosition(value: unknown): boolean {\n return (\n typeof value === 'string' &&\n (value === 'first' ||\n value === 'last' ||\n value.startsWith('before:') ||\n value.startsWith('after:'))\n )\n}\n\nexport type { Patch, ScreenNode }\n","import {\n WxAlert,\n WxCard,\n WxCheckbox,\n WxCol,\n WxColorPicker,\n WxDatePicker,\n WxDivider,\n WxInput,\n WxInputNumber,\n WxRadioGroup,\n WxRow,\n WxSelect,\n WxSwitch,\n WxTab,\n WxTabs,\n WxText,\n WxTextarea,\n} from '@webx-ui/core'\nimport type { NodeKind, TypeEntry, TypeRegistry } from './types'\n\n/**\n * The types every panel has: the core's layout, form and display components under\n * their full names. A module or a project adds its own the same way — `wx-media` comes\n * from `module-media`, `map` from whoever has a map.\n */\nexport const coreTypes: TypeRegistry = {\n 'wx-tabs': { component: WxTabs, kind: 'layout' },\n 'wx-tab': {\n component: WxTab,\n kind: 'layout',\n labelProp: 'label',\n // A tab is selected by value, and the node's id is the one stable thing it has.\n bind: (node) => ({ value: node.id }),\n },\n 'wx-card': { component: WxCard, kind: 'layout', labelProp: 'title' },\n 'wx-row': { component: WxRow, kind: 'layout' },\n 'wx-col': { component: WxCol, kind: 'layout' },\n 'wx-divider': { component: WxDivider, kind: 'layout', labelProp: 'label' },\n\n 'wx-input': { component: WxInput, kind: 'field' },\n 'wx-textarea': { component: WxTextarea, kind: 'field' },\n 'wx-input-number': { component: WxInputNumber, kind: 'field' },\n 'wx-select': { component: WxSelect, kind: 'field' },\n 'wx-switch': { component: WxSwitch, kind: 'field' },\n 'wx-checkbox': { component: WxCheckbox, kind: 'field' },\n 'wx-radio-group': { component: WxRadioGroup, kind: 'field' },\n 'wx-date-picker': { component: WxDatePicker, kind: 'field' },\n 'wx-color-picker': { component: WxColorPicker, kind: 'field' },\n\n 'wx-text': { component: WxText, kind: 'display' },\n 'wx-alert': { component: WxAlert, kind: 'display', labelProp: 'title' },\n}\n\n/** Identity with a type: keeps a project's registry object checked without an import of the type. */\nexport function defineTypes<T extends TypeRegistry>(types: T): T {\n return types\n}\n\nexport interface TypeDescription {\n type: string\n kind: NodeKind\n component: string\n /** Where the node's `label` ends up, in words. */\n label: string\n}\n\nfunction componentName(entry: TypeEntry): string {\n const component = entry.component as { name?: string; __name?: string }\n return component.name ?? component.__name ?? 'anonymous'\n}\n\nfunction labelDestination(entry: TypeEntry): string {\n if (entry.kind === 'field') return 'form item'\n if (entry.labelProp) return `prop \\`${entry.labelProp}\\``\n return entry.kind === 'display' ? 'default slot' : '—'\n}\n\n/** One row per type, in registry order — what the documentation table is made of. */\nexport function describeTypes(types: TypeRegistry): TypeDescription[] {\n return Object.entries(types).map(([type, entry]) => ({\n type,\n kind: entry.kind,\n component: componentName(entry),\n label: labelDestination(entry),\n }))\n}\n\n/**\n * The registry as a Markdown table, padded the way Prettier pads one, so the generated\n * block in the guide survives `prettier --check` and a test can compare it verbatim.\n */\nexport function typesTable(types: TypeRegistry): string {\n const rows = describeTypes(types).map((row) => [\n `\\`${row.type}\\``,\n row.kind,\n `\\`${row.component}\\``,\n row.label,\n ])\n const header = ['Type', 'Kind', 'Component', '`label` goes to']\n const widths = header.map((cell, column) =>\n Math.max(cell.length, ...rows.map((row) => row[column]!.length)),\n )\n const line = (cells: string[]) =>\n `| ${cells.map((cell, column) => cell.padEnd(widths[column]!)).join(' | ')} |`\n return [line(header), line(widths.map((width) => '-'.repeat(width))), ...rows.map(line)].join(\n '\\n',\n )\n}\n","import { defineComponent, h, type PropType, type VNode } from 'vue'\nimport { WxFormItem } from '@webx-ui/core'\nimport { isVisible } from './visible'\nimport type { ScreenModel, ScreenNode, Translate, TypeRegistry } from './types'\n\nexport const TRANS_MARKER = 'trans::'\n\n/** What the recursive renderer needs at every level. */\nexport interface RenderContext {\n types: TypeRegistry\n model: ScreenModel\n update: (name: string, value: unknown) => void\n translate: Translate\n can: (permission: string) => boolean\n}\n\n/** Without a dictionary the key itself shows — honest, and easy to spot in a screenshot. */\nexport const keyAsIs: Translate = (key) => key\n\n/** Translates a marked string; anything else — including `undefined` — passes through. */\nexport function words<T>(value: T, translate: Translate): T {\n if (typeof value === 'string' && value.startsWith(TRANS_MARKER)) {\n return translate(value.slice(TRANS_MARKER.length)) as T\n }\n return value\n}\n\n/** Same, through arrays and objects: `props.options[].label` is the common case. */\nexport function translateDeep<T>(value: T, translate: Translate): T {\n if (typeof value === 'string') return words(value, translate)\n if (Array.isArray(value)) return value.map((item) => translateDeep(item, translate)) as T\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {}\n for (const [key, item] of Object.entries(value)) out[key] = translateDeep(item, translate)\n return out as T\n }\n return value\n}\n\nfunction renderUnknown(node: ScreenNode): VNode {\n return h(\n 'div',\n { key: node.id, class: 'wx-screen__unknown', role: 'note' },\n `Unknown type: ${node.type}`,\n )\n}\n\n/** Children grouped by the slot they asked for. */\nfunction childSlots(\n node: ScreenNode,\n defaultSlot: string,\n context: RenderContext,\n): Record<string, () => VNode[]> {\n const groups = new Map<string, ScreenNode[]>()\n for (const child of node.children ?? []) {\n const name = child.slot ?? defaultSlot\n const list = groups.get(name) ?? []\n list.push(child)\n groups.set(name, list)\n }\n const slots: Record<string, () => VNode[]> = {}\n for (const [name, children] of groups) {\n slots[name] = () => renderNodes(children, context)\n }\n return slots\n}\n\nexport function renderNode(node: ScreenNode, context: RenderContext): VNode | null {\n if (node.can && !context.can(node.can)) return null\n if (!isVisible(node, context.model)) return null\n\n const entry = context.types[node.type]\n if (!entry) return renderUnknown(node)\n\n const { translate } = context\n const props: Record<string, unknown> = {\n key: node.id,\n ...translateDeep(node.props ?? {}, translate),\n ...entry.bind?.(node),\n }\n const label = words(node.label, translate)\n\n if (entry.kind === 'field') {\n const name = node.name\n const control = h(entry.component, {\n ...props,\n name,\n localized: node.localized || undefined,\n modelValue: name === undefined ? undefined : context.model[name],\n 'onUpdate:modelValue': (value: unknown) => {\n if (name !== undefined) context.update(name, value)\n },\n })\n return h(\n WxFormItem,\n { key: node.id, name, label, help: words(node.help, translate) },\n () => control,\n )\n }\n\n if (entry.kind === 'layout') {\n if (entry.labelProp && label !== undefined) props[entry.labelProp] = label\n return h(entry.component, props, childSlots(node, entry.childrenSlot ?? 'default', context))\n }\n\n // display\n if (entry.labelProp) {\n if (label !== undefined) props[entry.labelProp] = label\n return h(entry.component, props)\n }\n return h(entry.component, props, label === undefined ? undefined : () => label)\n}\n\nexport function renderNodes(nodes: ScreenNode[], context: RenderContext): VNode[] {\n const out: VNode[] = []\n for (const node of nodes) {\n const rendered = renderNode(node, context)\n if (rendered) out.push(rendered)\n }\n return out\n}\n\n/** A list of nodes as a component, so the tree can recurse through slots. */\nexport const WxScreenNodes = defineComponent({\n name: 'WxScreenNodes',\n props: {\n nodes: { type: Array as PropType<ScreenNode[]>, required: true },\n context: { type: Object as PropType<RenderContext>, required: true },\n },\n setup(props) {\n return () => renderNodes(props.nodes, props.context)\n },\n})\n","<script setup lang=\"ts\">\nimport { computed, markRaw, toRaw, watch } from 'vue'\nimport { WxForm } from '@webx-ui/core'\nimport { applyPatch } from './patch'\nimport { coreTypes } from './registry'\nimport { keyAsIs, WxScreenNodes, type RenderContext } from './render'\nimport type {\n Patch,\n PatchError,\n ScreenModel,\n ScreenNode,\n Translate,\n TypeRegistry,\n ValidationErrors,\n} from './types'\n\ndefineOptions({ name: 'WxScreenRenderer' })\n\nconst props = withDefaults(\n defineProps<{\n /** The tree to draw — a module's screen, already patched by the server. */\n root: ScreenNode[]\n /** Client-side patch applied on top, in order. */\n patch?: Patch\n /** Server-side validation errors, keyed by field name; shown under the fields. */\n errors?: ValidationErrors\n /** Project types, merged over the core ones. */\n types?: TypeRegistry\n /** Turns `trans::` strings into words. Without it the key shows. */\n translate?: Translate\n /** Decides `can`. Without it every node is allowed. */\n can?: (permission: string) => boolean\n disabled?: boolean\n labelPosition?: 'top' | 'left'\n labelWidth?: string\n size?: 'sm' | 'md' | 'lg'\n }>(),\n {\n patch: () => [],\n errors: undefined,\n types: undefined,\n translate: undefined,\n can: undefined,\n disabled: false,\n labelPosition: undefined,\n labelWidth: undefined,\n size: undefined,\n },\n)\n\nconst emit = defineEmits<{\n /** Operations that could not be applied. Also reported to the console. */\n patchError: [errors: PatchError[]]\n}>()\n\nconst model = defineModel<ScreenModel>({ default: () => ({}) })\n\nconst applied = computed(() => applyPatch(props.root, props.patch))\n\nwatch(\n () => applied.value.errors,\n (errors) => {\n for (const error of errors) {\n console.error(`[webx-ui/schema] patch[${error.index}] ${error.op.op}: ${error.message}`)\n }\n emit('patchError', errors)\n },\n { immediate: true },\n)\n\n/**\n * A registry kept in a `ref` arrives as a reactive proxy, and Vue warns about a\n * component that is one; the raw component is what `h()` wants anyway.\n */\nconst registry = computed<TypeRegistry>(() => {\n const merged: TypeRegistry = {}\n for (const [type, entry] of Object.entries({ ...coreTypes, ...props.types })) {\n merged[type] = { ...entry, component: markRaw(toRaw(entry.component)) }\n }\n return merged\n})\n\nconst context = computed<RenderContext>(() => ({\n types: registry.value,\n model: model.value,\n update: (name, value) => {\n model.value = { ...model.value, [name]: value }\n },\n translate: props.translate ?? keyAsIs,\n can: props.can ?? (() => true),\n}))\n\ndefineExpose({\n /** The tree after the patch — what is actually on screen. */\n tree: computed(() => applied.value.root),\n})\n</script>\n\n<template>\n <wx-form\n class=\"wx-screen\"\n :errors=\"errors\"\n :disabled=\"disabled\"\n :label-position=\"labelPosition\"\n :label-width=\"labelWidth\"\n :size=\"size\"\n >\n <wx-screen-nodes :nodes=\"applied.root\" :context=\"context\" />\n </wx-form>\n</template>\n\n<style>\n/* Global on purpose: the placeholder is created by a render function, outside any scope. */\n.wx-screen__unknown {\n padding: var(--wx-space-8) var(--wx-space-12);\n border: 1px dashed var(--wx-color-danger);\n border-radius: var(--wx-radius-control);\n background: var(--wx-color-danger-soft);\n color: var(--wx-color-danger);\n font-family: var(--wx-font-family-mono);\n font-size: var(--wx-font-size-sm);\n}\n</style>\n"],"names":["locate","root","id","index","node","found","findNode","collectIds","ids","walk","nodes","indexFor","list","position","where","anchor","at","clone","value","out","key","item","applyPatch","patch","tree","errors","op","message","apply","clash","duplicateIn","others","parent","contains","ancestor","child","incoming","except","existing","same","a","b","evaluateCondition","condition","model","isVisible","visible","NODE_KEYS","OPS","isRecord","checkCondition","path","forms","checkNode","seen","validateScreen","validatePatch","isPosition","coreTypes","WxTabs","WxTab","WxCard","WxRow","WxCol","WxDivider","WxInput","WxTextarea","WxInputNumber","WxSelect","WxSwitch","WxCheckbox","WxRadioGroup","WxDatePicker","WxColorPicker","WxText","WxAlert","defineTypes","types","componentName","entry","component","labelDestination","describeTypes","type","typesTable","rows","row","header","widths","cell","column","line","cells","width","TRANS_MARKER","keyAsIs","words","translate","translateDeep","renderUnknown","h","childSlots","defaultSlot","context","groups","name","slots","children","renderNodes","renderNode","props","label","control","WxFormItem","rendered","WxScreenNodes","defineComponent","__props","emit","__emit","_useModel","applied","computed","watch","error","registry","merged","markRaw","toRaw","__expose","_createBlock","_unref","WxForm","_createVNode"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/patch.ts","../src/visible.ts","../src/validate.ts","../src/render.ts","../src/ScreenRepeater.vue","../src/registry.ts","../src/ScreenRenderer.vue"],"sourcesContent":["import type { Patch, PatchError, PatchOperation, PatchPosition, ScreenNode } from './types'\n\ninterface Located {\n /** The array the node sits in: the root, or a parent's `children`. */\n list: ScreenNode[]\n index: number\n node: ScreenNode\n}\n\n/** Depth-first search by `id`, remembering the array the match lives in. */\nexport function locate(root: ScreenNode[], id: string): Located | null {\n for (let index = 0; index < root.length; index += 1) {\n const node = root[index]!\n if (node.id === id) return { list: root, index, node }\n if (node.children) {\n const found = locate(node.children, id)\n if (found) return found\n }\n }\n return null\n}\n\nexport function findNode(root: ScreenNode[], id: string): ScreenNode | null {\n return locate(root, id)?.node ?? null\n}\n\n/** Every `id` in the tree, in document order. Duplicates are kept, so callers can spot them. */\nexport function collectIds(root: ScreenNode[]): string[] {\n const ids: string[] = []\n const walk = (nodes: ScreenNode[]) => {\n for (const node of nodes) {\n ids.push(node.id)\n if (node.children) walk(node.children)\n }\n }\n walk(root)\n return ids\n}\n\n/** Index a node should be inserted at in `list`, or a message when the anchor is missing. */\nfunction indexFor(list: ScreenNode[], position: PatchPosition | undefined): number | string {\n if (!position || position === 'last') return list.length\n if (position === 'first') return 0\n const [where, anchor] = position.split(':', 2) as ['before' | 'after', string]\n const at = list.findIndex((node) => node.id === anchor)\n if (at === -1) return `no sibling \"${anchor}\" to insert ${where}`\n return where === 'before' ? at : at + 1\n}\n\n/**\n * Deep copy of a JSON value. `structuredClone` refuses a Vue reactive proxy, and a\n * tree that came out of a `ref` is exactly that; reading through the proxy is fine.\n */\nexport function clone<T>(value: T): T {\n if (Array.isArray(value)) return value.map(clone) as T\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {}\n for (const [key, item] of Object.entries(value)) out[key] = clone(item)\n return out as T\n }\n return value\n}\n\n/**\n * Applies a patch to a tree and returns the result as a new tree; the input is not\n * touched. An operation that cannot be applied — a `target` that does not exist, an\n * anchor that is missing — is skipped and reported, and the ones after it still run:\n * a project patch that survived a rename in the module must not silence the rest.\n */\nexport function applyPatch(\n root: ScreenNode[],\n patch: Patch,\n): { root: ScreenNode[]; errors: PatchError[] } {\n const tree = clone(root)\n const errors: PatchError[] = []\n\n patch.forEach((op, index) => {\n const message = apply(tree, op)\n if (message) errors.push({ index, op, message })\n })\n\n return { root: tree, errors }\n}\n\n/** Mutates `tree`; returns a message when the operation was refused. */\nfunction apply(tree: ScreenNode[], op: PatchOperation): string | null {\n const found = locate(tree, op.target)\n if (!found) return `target \"${op.target}\" not found`\n\n switch (op.op) {\n case 'add': {\n const clash = duplicateIn(tree, op.node)\n if (clash) return `id \"${clash}\" already exists in the screen`\n const list = (found.node.children ??= [])\n const at = indexFor(list, op.position)\n if (typeof at === 'string') return at\n list.splice(at, 0, clone(op.node))\n return null\n }\n case 'remove':\n found.list.splice(found.index, 1)\n return null\n case 'replace': {\n // The replaced node's own ids are gone, so only the rest of the tree can clash.\n const others = tree.filter((node) => node !== found.node)\n const clash = duplicateIn(others, op.node, found.node)\n if (clash) return `id \"${clash}\" already exists in the screen`\n found.list.splice(found.index, 1, clone(op.node))\n return null\n }\n case 'move': {\n let list = found.list\n if (op.to !== undefined) {\n const parent = locate(tree, op.to)\n if (!parent) return `destination \"${op.to}\" not found`\n if (parent.node === found.node || contains(found.node, parent.node)) {\n return `cannot move \"${op.target}\" into itself`\n }\n list = parent.node.children ??= []\n }\n found.list.splice(found.index, 1)\n const at = indexFor(list, op.position)\n if (typeof at === 'string') {\n // Put it back where it was: a refused move leaves the tree untouched.\n found.list.splice(found.index, 0, found.node)\n return at\n }\n list.splice(at, 0, found.node)\n return null\n }\n case 'set': {\n for (const [key, value] of Object.entries(op)) {\n if (key === 'op' || key === 'target') continue\n if (key === 'props') {\n found.node.props = { ...found.node.props, ...(value as Record<string, unknown>) }\n } else {\n ;(found.node as unknown as Record<string, unknown>)[key] = clone(value)\n }\n }\n return null\n }\n }\n}\n\nfunction contains(ancestor: ScreenNode, node: ScreenNode): boolean {\n return (ancestor.children ?? []).some((child) => child === node || contains(child, node))\n}\n\n/** First id of `incoming` that already exists in `tree`, ignoring `except`'s own subtree. */\nfunction duplicateIn(tree: ScreenNode[], incoming: ScreenNode, except?: ScreenNode): string | null {\n const existing = new Set(collectIds(tree))\n if (except) for (const id of collectIds([except])) existing.delete(id)\n return collectIds([incoming]).find((id) => existing.has(id)) ?? null\n}\n","import type { ScreenModel, ScreenNode, VisibilityCondition } from './types'\n\n/** Loose enough for `{ \"is\": 1 }` to match a numeric input, strict enough for `\"1\"` not to. */\nfunction same(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (a === null || b === null || typeof a !== 'object' || typeof b !== 'object') return false\n return JSON.stringify(a) === JSON.stringify(b)\n}\n\nexport function evaluateCondition(condition: VisibilityCondition, model: ScreenModel): boolean {\n if ('all' in condition) return condition.all.every((item) => evaluateCondition(item, model))\n if ('any' in condition) return condition.any.some((item) => evaluateCondition(item, model))\n\n const value = model[condition.when]\n if ('in' in condition) return condition.in.some((item) => same(item, value))\n if ('not' in condition) return !same(condition.not, value)\n return same(condition.is, value)\n}\n\n/** Whether a node should render, given the current model. Absent `visible` means yes. */\nexport function isVisible(node: ScreenNode, model: ScreenModel): boolean {\n const { visible } = node\n if (visible === undefined || visible === true) return true\n if (visible === false) return false\n return evaluateCondition(visible, model)\n}\n","import type { Patch, ScreenError, ScreenNode, VisibilityCondition } from './types'\n\nconst NODE_KEYS = new Set([\n 'id',\n 'type',\n 'name',\n 'label',\n 'help',\n 'localized',\n 'props',\n 'children',\n 'slot',\n 'visible',\n 'can',\n])\n\nconst OPS = new Set(['add', 'remove', 'replace', 'move', 'set'])\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction checkCondition(value: unknown, path: string, errors: ScreenError[]): void {\n if (!isRecord(value)) {\n errors.push({ path, message: 'visible must be a boolean or a condition object' })\n return\n }\n if ('all' in value || 'any' in value) {\n const list = (value.all ?? value.any) as unknown\n if (!Array.isArray(list)) {\n errors.push({ path, message: '\"all\" / \"any\" must be an array of conditions' })\n return\n }\n list.forEach((item, index) => checkCondition(item, `${path}[${index}]`, errors))\n return\n }\n if (typeof value.when !== 'string') {\n errors.push({ path, message: 'a condition needs \"when\": the name of a field' })\n return\n }\n const forms = ['is', 'in', 'not'].filter((key) => key in value)\n if (forms.length !== 1) {\n errors.push({ path, message: 'a condition needs exactly one of \"is\", \"in\", \"not\"' })\n } else if (forms[0] === 'in' && !Array.isArray(value.in)) {\n errors.push({ path, message: '\"in\" must be an array' })\n }\n}\n\nfunction checkNode(value: unknown, path: string, errors: ScreenError[], seen: Set<string>): void {\n if (!isRecord(value)) {\n errors.push({ path, message: 'a node must be an object' })\n return\n }\n\n for (const key of Object.keys(value)) {\n if (!NODE_KEYS.has(key)) errors.push({ path, message: `unknown key \"${key}\"` })\n }\n\n if (typeof value.id !== 'string' || value.id === '') {\n errors.push({ path, message: '\"id\" is required and must be a non-empty string' })\n } else if (seen.has(value.id)) {\n errors.push({ path, message: `duplicate id \"${value.id}\"` })\n } else {\n seen.add(value.id)\n }\n\n if (typeof value.type !== 'string' || value.type === '') {\n errors.push({ path, message: '\"type\" is required and must be a non-empty string' })\n }\n\n for (const key of ['name', 'label', 'help'] as const) {\n if (key in value && typeof value[key] !== 'string') {\n errors.push({ path, message: `\"${key}\" must be a string` })\n }\n }\n if ('localized' in value && typeof value.localized !== 'boolean') {\n errors.push({ path, message: '\"localized\" must be a boolean' })\n }\n if ('props' in value && !isRecord(value.props)) {\n errors.push({ path, message: '\"props\" must be an object' })\n }\n if ('slot' in value && value.slot !== null && typeof value.slot !== 'string') {\n errors.push({ path, message: '\"slot\" must be a string or null' })\n }\n if ('can' in value && value.can !== null && typeof value.can !== 'string') {\n errors.push({ path, message: '\"can\" must be a string or null' })\n }\n if ('visible' in value && typeof value.visible !== 'boolean') {\n checkCondition(value.visible as VisibilityCondition, `${path}.visible`, errors)\n }\n\n if ('children' in value) {\n if (!Array.isArray(value.children)) {\n errors.push({ path, message: '\"children\" must be an array' })\n } else {\n value.children.forEach((child, index) =>\n checkNode(child, `${path}.children[${index}]`, errors, seen),\n )\n }\n }\n}\n\n/**\n * Checks a tree the way the JSON schema would, without a schema library: required\n * keys, closed key set, value types, and — what a schema cannot say — unique ids.\n * An empty list means the tree is sound.\n */\nexport function validateScreen(root: unknown): ScreenError[] {\n const errors: ScreenError[] = []\n if (!Array.isArray(root)) return [{ path: 'root', message: 'root must be an array of nodes' }]\n const seen = new Set<string>()\n root.forEach((node, index) => checkNode(node, `root[${index}]`, errors, seen))\n return errors\n}\n\n/** Same for a patch: each operation has the keys its `op` calls for. */\nexport function validatePatch(patch: unknown): ScreenError[] {\n const errors: ScreenError[] = []\n if (!Array.isArray(patch)) return [{ path: 'patch', message: 'a patch must be an array' }]\n\n patch.forEach((op, index) => {\n const path = `patch[${index}]`\n if (!isRecord(op)) {\n errors.push({ path, message: 'an operation must be an object' })\n return\n }\n if (typeof op.op !== 'string' || !OPS.has(op.op)) {\n errors.push({ path, message: '\"op\" must be one of add, remove, replace, move, set' })\n return\n }\n if (typeof op.target !== 'string' || op.target === '') {\n errors.push({ path, message: '\"target\" is required: the id of a node' })\n }\n if ((op.op === 'add' || op.op === 'replace') && !isRecord(op.node)) {\n errors.push({ path, message: `\"${op.op}\" needs a \"node\"` })\n } else if (op.op === 'add' || op.op === 'replace') {\n checkNode(op.node, `${path}.node`, errors, new Set())\n }\n if ('position' in op && !isPosition(op.position)) {\n errors.push({ path, message: '\"position\" must be first, last, before:<id> or after:<id>' })\n }\n if (op.op === 'move' && 'to' in op && typeof op.to !== 'string') {\n errors.push({ path, message: '\"to\" must be the id of the new parent' })\n }\n if (op.op === 'set') {\n for (const key of Object.keys(op)) {\n if (key !== 'op' && key !== 'target' && (!NODE_KEYS.has(key) || key === 'id')) {\n errors.push({ path, message: `\"set\" cannot change \"${key}\"` })\n }\n }\n }\n })\n\n return errors\n}\n\nfunction isPosition(value: unknown): boolean {\n return (\n typeof value === 'string' &&\n (value === 'first' ||\n value === 'last' ||\n value.startsWith('before:') ||\n value.startsWith('after:'))\n )\n}\n\nexport type { Patch, ScreenNode }\n","import { defineComponent, h, type PropType, type VNode } from 'vue'\nimport { WxFormItem } from '@webx-ui/core'\nimport { isVisible } from './visible'\nimport type { ScreenModel, ScreenNode, Translate, TypeRegistry } from './types'\n\nexport const TRANS_MARKER = 'trans::'\n\n/** What the recursive renderer needs at every level. */\nexport interface RenderContext {\n types: TypeRegistry\n model: ScreenModel\n update: (name: string, value: unknown) => void\n translate: Translate\n can: (permission: string) => boolean\n}\n\n/** Without a dictionary the key itself shows — honest, and easy to spot in a screenshot. */\nexport const keyAsIs: Translate = (key) => key\n\n/** Translates a marked string; anything else — including `undefined` — passes through. */\nexport function words<T>(value: T, translate: Translate): T {\n if (typeof value === 'string' && value.startsWith(TRANS_MARKER)) {\n return translate(value.slice(TRANS_MARKER.length)) as T\n }\n return value\n}\n\n/** Same, through arrays and objects: `props.options[].label` is the common case. */\nexport function translateDeep<T>(value: T, translate: Translate): T {\n if (typeof value === 'string') return words(value, translate)\n if (Array.isArray(value)) return value.map((item) => translateDeep(item, translate)) as T\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {}\n for (const [key, item] of Object.entries(value)) out[key] = translateDeep(item, translate)\n return out as T\n }\n return value\n}\n\nfunction renderUnknown(node: ScreenNode): VNode {\n return h(\n 'div',\n { key: node.id, class: 'wx-screen__unknown', role: 'note' },\n `Unknown type: ${node.type}`,\n )\n}\n\n/** Children grouped by the slot they asked for. */\nfunction childSlots(\n node: ScreenNode,\n defaultSlot: string,\n context: RenderContext,\n): Record<string, () => VNode[]> {\n const groups = new Map<string, ScreenNode[]>()\n for (const child of node.children ?? []) {\n const name = child.slot ?? defaultSlot\n const list = groups.get(name) ?? []\n list.push(child)\n groups.set(name, list)\n }\n const slots: Record<string, () => VNode[]> = {}\n for (const [name, children] of groups) {\n slots[name] = () => renderNodes(children, context)\n }\n return slots\n}\n\nexport function renderNode(node: ScreenNode, context: RenderContext): VNode | null {\n if (node.can && !context.can(node.can)) return null\n if (!isVisible(node, context.model)) return null\n\n const entry = context.types[node.type]\n if (!entry) return renderUnknown(node)\n\n const { translate } = context\n const props: Record<string, unknown> = {\n key: node.id,\n ...translateDeep(node.props ?? {}, translate),\n ...entry.bind?.(node),\n }\n const label = words(node.label, translate)\n\n if (entry.kind === 'field') {\n const name = node.name\n const control = h(entry.component, {\n ...props,\n /* A nested field draws the children itself, so it needs the node and the way down. */\n ...(entry.nested ? { node, context } : {}),\n name,\n localized: node.localized || undefined,\n modelValue: name === undefined ? undefined : context.model[name],\n 'onUpdate:modelValue': (value: unknown) => {\n if (name !== undefined) context.update(name, value)\n },\n })\n return h(\n WxFormItem,\n { key: node.id, name, label, help: words(node.help, translate) },\n () => control,\n )\n }\n\n if (entry.kind === 'layout') {\n if (entry.labelProp && label !== undefined) props[entry.labelProp] = label\n return h(entry.component, props, childSlots(node, entry.childrenSlot ?? 'default', context))\n }\n\n // display\n if (entry.labelProp) {\n if (label !== undefined) props[entry.labelProp] = label\n return h(entry.component, props)\n }\n return h(entry.component, props, label === undefined ? undefined : () => label)\n}\n\nexport function renderNodes(nodes: ScreenNode[], context: RenderContext): VNode[] {\n const out: VNode[] = []\n for (const node of nodes) {\n const rendered = renderNode(node, context)\n if (rendered) out.push(rendered)\n }\n return out\n}\n\n/** A list of nodes as a component, so the tree can recurse through slots. */\nexport const WxScreenNodes = defineComponent({\n name: 'WxScreenNodes',\n props: {\n nodes: { type: Array as PropType<ScreenNode[]>, required: true },\n context: { type: Object as PropType<RenderContext>, required: true },\n },\n setup(props) {\n return () => renderNodes(props.nodes, props.context)\n },\n})\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { WxRepeater } from '@webx-ui/core'\nimport { WxScreenNodes, type RenderContext } from './render'\nimport type { ScreenModel, ScreenNode } from './types'\n\ndefineOptions({ name: 'WxScreenRepeater', inheritAttrs: false })\n\n/**\n * The one type with a nested model: the value is a list of records, and the node's\n * children are the fields of one of them. Every child is bound by its own `name` to a key\n * of the item, which is why a repeater is the only place where a name means nesting.\n *\n * Nothing of `WxRepeater`'s own is declared here — `title`, `itemLabel`, `min` and the\n * rest ride in as attributes, so a prop the screen did not set stays unset instead of\n * arriving as `false`.\n */\ndefineProps<{\n /** The node being drawn — its children are one item's fields. */\n node: ScreenNode\n /** The renderer's context; each row gets a copy bound to its own item. */\n context: RenderContext\n /** The renderer hands every field these two. A repeater has no use for either. */\n name?: string\n localized?: boolean\n}>()\n\nconst model = defineModel<ScreenModel[]>({ default: () => [] })\n\nconst items = computed<ScreenModel[]>(() => (Array.isArray(model.value) ? model.value : []))\n</script>\n\n<template>\n <wx-repeater v-bind=\"$attrs\" :model-value=\"items\" @update:model-value=\"model = $event\">\n <template #default=\"{ item, update }\">\n <wx-screen-nodes\n :nodes=\"node.children ?? []\"\n :context=\"{\n ...context,\n model: item,\n update: (key: string, value: unknown) => update({ [key]: value }),\n }\"\n />\n </template>\n </wx-repeater>\n</template>\n","import {\n WxAlert,\n WxCard,\n WxCheckbox,\n WxCol,\n WxColorPicker,\n WxDatePicker,\n WxDivider,\n WxInput,\n WxInputNumber,\n WxRadioGroup,\n WxRow,\n WxSelect,\n WxSwitch,\n WxTab,\n WxTabs,\n WxText,\n WxTextarea,\n} from '@webx-ui/core'\nimport ScreenRepeater from './ScreenRepeater.vue'\nimport type { NodeKind, TypeEntry, TypeRegistry } from './types'\n\n/**\n * The types every panel has: the core's layout, form and display components under\n * their full names. A module or a project adds its own the same way — `wx-media` comes\n * from `module-media`, `map` from whoever has a map.\n */\nexport const coreTypes: TypeRegistry = {\n 'wx-tabs': { component: WxTabs, kind: 'layout' },\n 'wx-tab': {\n component: WxTab,\n kind: 'layout',\n labelProp: 'label',\n // A tab is selected by value, and the node's id is the one stable thing it has.\n bind: (node) => ({ value: node.id }),\n },\n 'wx-card': { component: WxCard, kind: 'layout', labelProp: 'title' },\n 'wx-row': { component: WxRow, kind: 'layout' },\n 'wx-col': { component: WxCol, kind: 'layout' },\n 'wx-divider': { component: WxDivider, kind: 'layout', labelProp: 'label' },\n\n 'wx-input': { component: WxInput, kind: 'field' },\n 'wx-textarea': { component: WxTextarea, kind: 'field' },\n 'wx-input-number': { component: WxInputNumber, kind: 'field' },\n 'wx-select': { component: WxSelect, kind: 'field' },\n 'wx-switch': { component: WxSwitch, kind: 'field' },\n 'wx-checkbox': { component: WxCheckbox, kind: 'field' },\n 'wx-radio-group': { component: WxRadioGroup, kind: 'field' },\n 'wx-date-picker': { component: WxDatePicker, kind: 'field' },\n 'wx-color-picker': { component: WxColorPicker, kind: 'field' },\n 'wx-repeater': { component: ScreenRepeater, kind: 'field', nested: true },\n\n 'wx-text': { component: WxText, kind: 'display' },\n 'wx-alert': { component: WxAlert, kind: 'display', labelProp: 'title' },\n}\n\n/** Identity with a type: keeps a project's registry object checked without an import of the type. */\nexport function defineTypes<T extends TypeRegistry>(types: T): T {\n return types\n}\n\nexport interface TypeDescription {\n type: string\n kind: NodeKind\n component: string\n /** Where the node's `label` ends up, in words. */\n label: string\n}\n\nfunction componentName(entry: TypeEntry): string {\n const component = entry.component as { name?: string; __name?: string }\n return component.name ?? component.__name ?? 'anonymous'\n}\n\nfunction labelDestination(entry: TypeEntry): string {\n if (entry.kind === 'field') return 'form item'\n if (entry.labelProp) return `prop \\`${entry.labelProp}\\``\n return entry.kind === 'display' ? 'default slot' : '—'\n}\n\n/** One row per type, in registry order — what the documentation table is made of. */\nexport function describeTypes(types: TypeRegistry): TypeDescription[] {\n return Object.entries(types).map(([type, entry]) => ({\n type,\n kind: entry.kind,\n component: componentName(entry),\n label: labelDestination(entry),\n }))\n}\n\n/**\n * The registry as a Markdown table, padded the way Prettier pads one, so the generated\n * block in the guide survives `prettier --check` and a test can compare it verbatim.\n */\nexport function typesTable(types: TypeRegistry): string {\n const rows = describeTypes(types).map((row) => [\n `\\`${row.type}\\``,\n row.kind,\n `\\`${row.component}\\``,\n row.label,\n ])\n const header = ['Type', 'Kind', 'Component', '`label` goes to']\n const widths = header.map((cell, column) =>\n Math.max(cell.length, ...rows.map((row) => row[column]!.length)),\n )\n const line = (cells: string[]) =>\n `| ${cells.map((cell, column) => cell.padEnd(widths[column]!)).join(' | ')} |`\n return [line(header), line(widths.map((width) => '-'.repeat(width))), ...rows.map(line)].join(\n '\\n',\n )\n}\n","<script setup lang=\"ts\">\nimport { computed, markRaw, toRaw, watch } from 'vue'\nimport { WxForm } from '@webx-ui/core'\nimport { applyPatch } from './patch'\nimport { coreTypes } from './registry'\nimport { keyAsIs, WxScreenNodes, type RenderContext } from './render'\nimport type {\n Patch,\n PatchError,\n ScreenModel,\n ScreenNode,\n Translate,\n TypeRegistry,\n ValidationErrors,\n} from './types'\n\ndefineOptions({ name: 'WxScreenRenderer' })\n\nconst props = withDefaults(\n defineProps<{\n /** The tree to draw — a module's screen, already patched by the server. */\n root: ScreenNode[]\n /** Client-side patch applied on top, in order. */\n patch?: Patch\n /** Server-side validation errors, keyed by field name; shown under the fields. */\n errors?: ValidationErrors\n /** Project types, merged over the core ones. */\n types?: TypeRegistry\n /** Turns `trans::` strings into words. Without it the key shows. */\n translate?: Translate\n /** Decides `can`. Without it every node is allowed. */\n can?: (permission: string) => boolean\n disabled?: boolean\n labelPosition?: 'top' | 'left'\n labelWidth?: string\n size?: 'sm' | 'md' | 'lg'\n }>(),\n {\n patch: () => [],\n errors: undefined,\n types: undefined,\n translate: undefined,\n can: undefined,\n disabled: false,\n labelPosition: undefined,\n labelWidth: undefined,\n size: undefined,\n },\n)\n\nconst emit = defineEmits<{\n /** Operations that could not be applied. Also reported to the console. */\n patchError: [errors: PatchError[]]\n}>()\n\nconst model = defineModel<ScreenModel>({ default: () => ({}) })\n\nconst applied = computed(() => applyPatch(props.root, props.patch))\n\nwatch(\n () => applied.value.errors,\n (errors) => {\n for (const error of errors) {\n console.error(`[webx-ui/schema] patch[${error.index}] ${error.op.op}: ${error.message}`)\n }\n emit('patchError', errors)\n },\n { immediate: true },\n)\n\n/**\n * A registry kept in a `ref` arrives as a reactive proxy, and Vue warns about a\n * component that is one; the raw component is what `h()` wants anyway.\n */\nconst registry = computed<TypeRegistry>(() => {\n const merged: TypeRegistry = {}\n for (const [type, entry] of Object.entries({ ...coreTypes, ...props.types })) {\n merged[type] = { ...entry, component: markRaw(toRaw(entry.component)) }\n }\n return merged\n})\n\nconst context = computed<RenderContext>(() => ({\n types: registry.value,\n model: model.value,\n update: (name, value) => {\n model.value = { ...model.value, [name]: value }\n },\n translate: props.translate ?? keyAsIs,\n can: props.can ?? (() => true),\n}))\n\ndefineExpose({\n /** The tree after the patch — what is actually on screen. */\n tree: computed(() => applied.value.root),\n})\n</script>\n\n<template>\n <wx-form\n class=\"wx-screen\"\n :errors=\"errors\"\n :disabled=\"disabled\"\n :label-position=\"labelPosition\"\n :label-width=\"labelWidth\"\n :size=\"size\"\n >\n <wx-screen-nodes :nodes=\"applied.root\" :context=\"context\" />\n </wx-form>\n</template>\n\n<style>\n/* Global on purpose: the placeholder is created by a render function, outside any scope. */\n.wx-screen__unknown {\n padding: var(--wx-space-8) var(--wx-space-12);\n border: 1px dashed var(--wx-color-danger);\n border-radius: var(--wx-radius-control);\n background: var(--wx-color-danger-soft);\n color: var(--wx-color-danger);\n font-family: var(--wx-font-family-mono);\n font-size: var(--wx-font-size-sm);\n}\n</style>\n"],"names":["locate","root","id","index","node","found","findNode","collectIds","ids","walk","nodes","indexFor","list","position","where","anchor","at","clone","value","out","key","item","applyPatch","patch","tree","errors","op","message","apply","clash","duplicateIn","others","parent","contains","ancestor","child","incoming","except","existing","same","a","b","evaluateCondition","condition","model","isVisible","visible","NODE_KEYS","OPS","isRecord","checkCondition","path","forms","checkNode","seen","validateScreen","validatePatch","isPosition","TRANS_MARKER","keyAsIs","words","translate","translateDeep","renderUnknown","h","childSlots","defaultSlot","context","groups","name","slots","children","renderNodes","renderNode","entry","props","label","control","WxFormItem","rendered","WxScreenNodes","defineComponent","_useModel","__props","items","computed","_createBlock","_unref","WxRepeater","_mergeProps","_ctx","_cache","$event","_withCtx","update","_createVNode","coreTypes","WxTabs","WxTab","WxCard","WxRow","WxCol","WxDivider","WxInput","WxTextarea","WxInputNumber","WxSelect","WxSwitch","WxCheckbox","WxRadioGroup","WxDatePicker","WxColorPicker","ScreenRepeater","WxText","WxAlert","defineTypes","types","componentName","component","labelDestination","describeTypes","type","typesTable","rows","row","header","widths","cell","column","line","cells","width","emit","__emit","applied","watch","error","registry","merged","markRaw","toRaw","__expose","WxForm"],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -113,6 +113,12 @@ export interface TypeEntry {
113
113
  labelProp?: string;
114
114
  /** Props derived from the node itself, beyond `props` — a tab's `value`, say. */
115
115
  bind?: (node: ScreenNode) => Record<string, unknown>;
116
+ /**
117
+ * A `field` that draws its own children: it is handed `node` and `context` on top of
118
+ * the model binding, and decides what its children are bound to. `wx-repeater` is the
119
+ * one type that needs this — only it knows what one item of its list is.
120
+ */
121
+ nested?: boolean;
116
122
  }
117
123
  export type TypeRegistry = Record<string, TypeEntry>;
118
124
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webx-ui/schema",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Screens as JSON for WebX UI admin panels: the node format, patches, the type registry and the renderer.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -44,7 +44,7 @@
44
44
  "vue": "^3.5.0"
45
45
  },
46
46
  "dependencies": {
47
- "@webx-ui/core": "^0.18.0"
47
+ "@webx-ui/core": "^0.19.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/node": "^24.10.1",