@yahoo/uds-create-config 4.5.2 → 4.7.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.
Files changed (72) hide show
  1. package/dist/configs/CanvasConfig.d.ts +900 -80
  2. package/dist/configs/react-native-system.d.ts +603 -55
  3. package/dist/configs/system.d.ts +603 -55
  4. package/dist/configs/system.js +7 -1
  5. package/dist/entities/system/Component.d.ts +480 -39
  6. package/dist/entities/system/Component.js +264 -93
  7. package/dist/entities/system/File.d.ts +1 -1
  8. package/dist/entities/system/LinkedSystem.d.ts +2 -2
  9. package/dist/entities/system/Operation.d.ts +1 -1
  10. package/dist/entities/system/StyleProperty.d.ts +8 -0
  11. package/dist/entities/system/StyleProperty.js +12 -1
  12. package/dist/entities/system/Tool.d.ts +1 -1
  13. package/dist/entities/system/component-module.js +1 -1
  14. package/dist/entities/system/defineComponent.d.ts +45 -7
  15. package/dist/entities/system/defineComponent.js +52 -25
  16. package/dist/framework/Config.d.ts +8 -2
  17. package/dist/framework/Config.js +97 -23
  18. package/dist/framework/class-names.d.ts +4 -2
  19. package/dist/framework/class-names.js +56 -17
  20. package/dist/framework/defineDerivedEntity.d.ts +1 -1
  21. package/dist/framework/defineEntity.d.ts +2 -2
  22. package/dist/framework/defineSubEntity.d.ts +3 -2
  23. package/dist/framework/preview-store.d.ts +19 -0
  24. package/dist/framework/preview-store.js +18 -0
  25. package/dist/framework/projections.d.ts +28 -8
  26. package/dist/framework/projections.js +50 -16
  27. package/dist/framework/prop-surface.d.ts +21 -1
  28. package/dist/framework/prop-surface.js +99 -21
  29. package/dist/framework/render-spec.d.ts +103 -3
  30. package/dist/framework/render-spec.js +285 -37
  31. package/dist/framework/schema-version.d.ts +1 -1
  32. package/dist/framework/schema-version.js +5 -5
  33. package/dist/framework/source-integrity.d.ts +14 -1
  34. package/dist/framework/utils/refs.js +10 -1
  35. package/dist/framework/validate-spec.d.ts +2 -0
  36. package/dist/framework/validate-spec.js +13 -0
  37. package/dist/index.d.ts +15 -12
  38. package/dist/index.js +7 -6
  39. package/dist/migrations/2.0.0/v1-artifact.d.ts +305 -29
  40. package/dist/migrations/20260919051027_prop_conditions_and_anatomy_positions_at_rest.d.ts +22 -0
  41. package/dist/migrations/20260919051027_prop_conditions_and_anatomy_positions_at_rest.js +10 -0
  42. package/dist/migrations/20260920004019_preview_conditions_keyed.d.ts +27 -0
  43. package/dist/migrations/20260920004019_preview_conditions_keyed.js +148 -0
  44. package/dist/migrations/20260920161309_prefixed_token_group.d.ts +12 -0
  45. package/dist/migrations/20260920161309_prefixed_token_group.js +10 -0
  46. package/dist/renderer/wrapRegistry.js +8 -2
  47. package/dist/renderer/wrappers/component-refs.d.ts +14 -0
  48. package/dist/renderer/wrappers/component-refs.js +79 -0
  49. package/dist/renderer/wrappers/hex-normalize.d.ts +5 -7
  50. package/dist/renderer/wrappers/hex-normalize.js +11 -10
  51. package/dist/spec/cell.d.ts +171 -0
  52. package/dist/spec/cell.js +361 -0
  53. package/dist/spec/detach.d.ts +125 -0
  54. package/dist/spec/detach.js +511 -0
  55. package/dist/spec/empty-node-slots.d.ts +11 -23
  56. package/dist/spec/empty-node-slots.js +12 -39
  57. package/dist/spec/index.d.ts +5 -3
  58. package/dist/spec/index.js +3 -3
  59. package/dist/spec/instance.d.ts +77 -0
  60. package/dist/spec/instance.js +80 -0
  61. package/dist/spec/preview-content.d.ts +95 -0
  62. package/dist/spec/preview-content.js +149 -0
  63. package/dist/spec/preview-elements.d.ts +54 -15
  64. package/dist/spec/preview-elements.js +109 -29
  65. package/dist/spec/prop-conditions.d.ts +28 -0
  66. package/dist/spec/prop-conditions.js +39 -0
  67. package/dist/spec/slot.d.ts +206 -0
  68. package/dist/spec/slot.js +542 -0
  69. package/dist/spec/visibility.d.ts +82 -0
  70. package/dist/spec/visibility.js +109 -0
  71. package/dist/tsconfig.tsbuildinfo +1 -1
  72. package/package.json +18 -2
@@ -0,0 +1,542 @@
1
+ import { canonicalCondition, isRef, kindOf, pathOf, splitRef } from "../framework/utils/refs.js";
2
+ import { rootLayerOf } from "../entities/system/element.js";
3
+ import { createSliceMemo } from "../framework/memo.js";
4
+ import "../framework/preview-store.js";
5
+ import { resolveComponentProps } from "../framework/projections.js";
6
+ import { routesIntoText } from "../framework/prop-surface.js";
7
+ import { isPreviewElement } from "./preview-elements.js";
8
+ import { slotTargetsOf } from "../framework/render-spec.js";
9
+ import { previewConditionAt, previewContentList, previewValueSource } from "./preview-content.js";
10
+ //#region src/spec/slot.ts
11
+ /**
12
+ * A slot of a component: one prop that content is placed in, as the config states it.
13
+ *
14
+ * A `Slot` is a value over one config, so every question about a slot has one owner: where its
15
+ * content lands (`layer`, `into`), what it takes (`accepts`, `takes`), what a cell shows in it
16
+ * (`content`, `source`) and how that content changes (`append`, `insertAt`, `replaceAt`, `removeAt`,
17
+ * `set`).
18
+ * Its content is a list of preview elements stored on the `preview` entry the cell reads it from;
19
+ * a bare element stored on the prop reads as a list of one.
20
+ *
21
+ * The list of a component's slots is cached per component slice, the way every component
22
+ * projection is, so a surface may ask per render. `Component.slots`, `Component.slot`,
23
+ * `Component.slotHostedBy` and `Component.namedSlots` are the door; the functions below back them.
24
+ */
25
+ const factsCache = createSliceMemo({
26
+ kinds: ["component"],
27
+ compute: (_config) => /* @__PURE__ */ new Map()
28
+ });
29
+ const listsByConfig = /* @__PURE__ */ new WeakMap();
30
+ var Slot = class {
31
+ /** The component that declares the slot. */
32
+ component;
33
+ /** The prop the content arrives as on the instance (`children`, `startIcon`). */
34
+ from;
35
+ /** The layer that holds the content. */
36
+ layer;
37
+ /** The prop the content lands under on the layer's element. */
38
+ into;
39
+ /** The refs the slot accepts (`component:X`, `icon:phosphor`); empty when unconstrained. */
40
+ accepts;
41
+ /** Built by {@link listSlots}; a surface reaches one through `Component.slots` or `Component.slot`. */
42
+ constructor(config, component, facts) {
43
+ this.config = config;
44
+ this.component = component;
45
+ this.from = facts.from;
46
+ this.layer = facts.layer;
47
+ this.into = facts.into;
48
+ this.accepts = facts.accepts;
49
+ }
50
+ /** Whether this is the component's `children`, positioned inside one of its layers. */
51
+ get isChildren() {
52
+ return this.from === "children";
53
+ }
54
+ /**
55
+ * Whether the slot takes an element: anything when it accepts nothing in particular, else an
56
+ * element whose ref one of the accepted refs names, exactly or as a member of a group the ref
57
+ * names (`icon:phosphor` takes `icon:phosphor/StarIcon`). A `@source` marker on either side is
58
+ * not part of the name. `element` is a spec type, a bare component path, or the ref a preview
59
+ * element stores.
60
+ */
61
+ takes(element) {
62
+ if (this.accepts.length === 0) return true;
63
+ const named = elementRefOf(this.config, element);
64
+ if (!named) return false;
65
+ const kind = kindOf(named);
66
+ const elementPath = pathOf(named);
67
+ return this.accepts.some((accepted) => {
68
+ if (kindOf(accepted) !== kind) return false;
69
+ const acceptedPath = pathOf(accepted);
70
+ if (acceptedPath === elementPath) return true;
71
+ return !(this.config.resolve(kind, acceptedPath) !== void 0) && elementPath.startsWith(`${acceptedPath}/`);
72
+ });
73
+ }
74
+ /** The stored field a cell reads the slot's content from, or `undefined` when nothing is stored. */
75
+ source(cell) {
76
+ return previewValueSource({
77
+ config: this.config,
78
+ path: this.component,
79
+ cell,
80
+ prop: this.from
81
+ });
82
+ }
83
+ /** The elements a cell shows in the slot, in order. */
84
+ content(cell) {
85
+ return previewContentList(this.stored(this.source(cell)));
86
+ }
87
+ /**
88
+ * Append `element` to the content under `cell`: the entry keyed on exactly that coordinate, `*`
89
+ * for none, created where none spells it yet and seeded with the list the coordinate shows, so the
90
+ * cells it covers gain the element and every other cell keeps what it had.
91
+ */
92
+ append({ cell, element }) {
93
+ return this.set({
94
+ cell,
95
+ elements: [...this.content(cell), element]
96
+ });
97
+ }
98
+ /**
99
+ * The content shown under `cell` with `element` placed at `address`: at `index` of the list when
100
+ * the path is empty, past the end appending; otherwise inside the element at `index`, where a
101
+ * number step is the position among that element's children and a string step the element-valued
102
+ * prop it becomes. `undefined` when the address names another slot or a path that reaches nothing.
103
+ * What a surface paints while a drop is still being decided, and what {@link insertAt} writes.
104
+ */
105
+ placed({ cell, address, element }) {
106
+ if (address.prop !== this.from) return void 0;
107
+ return placeIn(this.content(cell), address, element);
108
+ }
109
+ /**
110
+ * The element at `from` moved to `to`, both in this slot's content under `cell`, on the entry the
111
+ * cell reads the content from. `to` names a position in the content with the moved element
112
+ * already taken out, the way a drop between the remaining elements resolves it. Empty when either
113
+ * address names nothing, or `to` reaches inside the moved element.
114
+ */
115
+ moveAt({ cell, from, to }) {
116
+ if (from.prop !== this.from || to.prop !== this.from) return [];
117
+ if (reachesInside(from, to)) return [];
118
+ const source = this.source(cell);
119
+ if (!source) return [];
120
+ const list = previewContentList(this.stored(source));
121
+ const element = elementIn(list, from);
122
+ if (!element) return [];
123
+ const without = removeIn(list, from);
124
+ if (!without) return [];
125
+ const content = placeIn(without, to, element);
126
+ if (!content) return [];
127
+ return this.writeSource({
128
+ source,
129
+ content
130
+ });
131
+ }
132
+ /** The element at `address` in the content shown under `cell`, or `undefined` when it names nothing. */
133
+ elementAt({ cell, address }) {
134
+ if (address.prop !== this.from) return void 0;
135
+ return elementIn(this.content(cell), address);
136
+ }
137
+ /**
138
+ * A copy of the element at `address`, placed right after it: the next index of the list, or the
139
+ * next position among its parent's children. Written on the entry the cell reads the content
140
+ * from, like {@link replaceAt}, so a copy made in a conditional cell of `*` content is every
141
+ * cell's. An element held as a prop has no "after", so it is not duplicated. Empty when the
142
+ * address names nothing.
143
+ */
144
+ duplicateAt({ cell, address }) {
145
+ if (address.prop !== this.from) return [];
146
+ const last = address.path[address.path.length - 1];
147
+ if (typeof last === "string") return [];
148
+ const source = this.source(cell);
149
+ if (!source) return [];
150
+ const list = previewContentList(this.stored(source));
151
+ const element = elementIn(list, address);
152
+ if (!element) return [];
153
+ const content = placeIn(list, last === void 0 ? {
154
+ ...address,
155
+ index: address.index + 1
156
+ } : {
157
+ ...address,
158
+ path: [...address.path.slice(0, -1), last + 1]
159
+ }, structuredClone(element));
160
+ if (!content) return [];
161
+ return this.writeSource({
162
+ source,
163
+ content
164
+ });
165
+ }
166
+ /** Write the content {@link placed} gives, onto the cell's own entry the way `append` writes.
167
+ * Empty when nothing is placed. */
168
+ insertAt({ cell, address, element }) {
169
+ const content = this.placed({
170
+ cell,
171
+ address,
172
+ element
173
+ });
174
+ return content ? this.set({
175
+ cell,
176
+ elements: content
177
+ }) : [];
178
+ }
179
+ /**
180
+ * Swap the element at `address` for `element`, on the entry the cell reads the content from —
181
+ * `*` included, so a `*` value shown in a conditional cell changes for every cell rather than
182
+ * being copied into a new keyed entry. Empty when the address names nothing.
183
+ */
184
+ replaceAt({ cell, address, element }) {
185
+ return this.rewrite({
186
+ cell,
187
+ address,
188
+ next: element
189
+ });
190
+ }
191
+ /**
192
+ * Take the element at `address` out, on the entry the cell reads the content from. A list emptied
193
+ * this way clears the prop, and an entry left with no props goes with it. Empty when the address
194
+ * names nothing.
195
+ */
196
+ removeAt({ cell, address }) {
197
+ return this.rewrite({
198
+ cell,
199
+ address,
200
+ next: null
201
+ });
202
+ }
203
+ /**
204
+ * Store `elements` as the whole content under `cell`: one `sub-update` on the `preview` entry
205
+ * keyed on the coordinate, `*` for none, created where none spells it and joined where one does.
206
+ * What a picker that shows the whole list writes; the verbs above build on it.
207
+ */
208
+ set({ cell, elements }) {
209
+ const conditional = Object.keys(cell).length > 0;
210
+ const when = conditional ? { ...cell } : null;
211
+ const exact = conditional ? previewConditionAt({
212
+ config: this.config,
213
+ path: this.component,
214
+ cell
215
+ }) : void 0;
216
+ let key = "*";
217
+ if (exact) key = exact.key;
218
+ else if (when) key = canonicalCondition(when);
219
+ return [{
220
+ kind: "component",
221
+ operation: "sub-update",
222
+ path: this.component,
223
+ field: "preview",
224
+ key,
225
+ data: {
226
+ ...exact ? {} : { when },
227
+ [`props.${this.from}`]: storedContent(elements)
228
+ }
229
+ }];
230
+ }
231
+ stored(source) {
232
+ if (!source) return void 0;
233
+ const body = this.config.resolve("component", this.component)?.body;
234
+ if (source.kind === "defaultProps") return body?.defaultProps?.[this.from];
235
+ return body?.preview?.[source.key]?.props?.[this.from];
236
+ }
237
+ /** `replaceAt` and `removeAt`: one element rewritten or dropped, written back where it was read. */
238
+ rewrite({ cell, address, next }) {
239
+ if (address.prop !== this.from) return [];
240
+ const source = this.source(cell);
241
+ if (!source) return [];
242
+ const list = previewContentList(this.stored(source));
243
+ const target = list[address.index];
244
+ if (!target) return [];
245
+ const rewritten = rewriteAt(target, address.path, next);
246
+ if (rewritten === void 0) return [];
247
+ const content = [...list];
248
+ if (rewritten === null) content.splice(address.index, 1);
249
+ else content[address.index] = rewritten;
250
+ return this.writeSource({
251
+ source,
252
+ content
253
+ });
254
+ }
255
+ /** The edits that store `content` on `source`: the field the content came from, cleared when the
256
+ * list is empty, and the entry it leaves with nothing else going with it. */
257
+ writeSource({ source, content }) {
258
+ const value = content.length === 0 ? null : storedContent(content);
259
+ if (source.kind === "defaultProps") return [{
260
+ kind: "component",
261
+ operation: "update",
262
+ path: this.component,
263
+ data: { [`defaultProps.${this.from}`]: value }
264
+ }];
265
+ const body = this.config.resolve("component", this.component)?.body;
266
+ const remaining = Object.keys(body?.preview?.[source.key]?.props ?? {}).filter((name) => name !== this.from);
267
+ if (value === null && remaining.length === 0) return [{
268
+ kind: "component",
269
+ operation: "sub-delete",
270
+ path: this.component,
271
+ field: "preview",
272
+ key: source.key
273
+ }];
274
+ return [{
275
+ kind: "component",
276
+ operation: "sub-update",
277
+ path: this.component,
278
+ field: "preview",
279
+ key: source.key,
280
+ data: { [`props.${this.from}`]: value }
281
+ }];
282
+ }
283
+ };
284
+ /**
285
+ * Every slot of a component that content can be placed in: its named node slots, and its `children`
286
+ * when a layer of several positions them. A text slot is typed in place rather than filled, so it is
287
+ * not one; nor is `children` landing on the root, which is the open hole every container has. The
288
+ * same array comes back for the same config.
289
+ */
290
+ function listSlots({ config, path }) {
291
+ let byPath = listsByConfig.get(config);
292
+ if (!byPath) {
293
+ byPath = /* @__PURE__ */ new Map();
294
+ listsByConfig.set(config, byPath);
295
+ }
296
+ const hit = byPath.get(path);
297
+ if (hit) return hit;
298
+ const slots = slotFacts(config, path).map((facts) => new Slot(config, path, facts));
299
+ byPath.set(path, slots);
300
+ return slots;
301
+ }
302
+ /** The named node slots alone: what a design node's render injects an empty placeholder for. */
303
+ function namedSlots({ config, path }) {
304
+ return listSlots({
305
+ config,
306
+ path
307
+ }).filter((slot) => !slot.isChildren);
308
+ }
309
+ /** The slot a prop names, or `undefined` when the prop is not a slot content is placed in. */
310
+ function slotOf({ config, path, slot }) {
311
+ return listSlots({
312
+ config,
313
+ path
314
+ }).find((entry) => entry.from === slot);
315
+ }
316
+ /** The slot whose content lands in `layer`, or `undefined` when the layer hosts none. */
317
+ function slotHostedBy({ config, path, layer }) {
318
+ return listSlots({
319
+ config,
320
+ path
321
+ }).find((entry) => entry.layer === layer);
322
+ }
323
+ /** A list stored back the way content is authored: one element bare, several as the list. */
324
+ function storedContent(list) {
325
+ return list.length === 1 ? list[0] : [...list];
326
+ }
327
+ /**
328
+ * `root` with the element at `steps` replaced by `next`, or removed when `next` is `null`;
329
+ * `undefined` when a step names nothing. A number steps into a child, a string into an
330
+ * element-valued prop.
331
+ */
332
+ function rewriteAt(root, steps, next) {
333
+ const [step, ...rest] = steps;
334
+ if (step === void 0) return next;
335
+ if (typeof step === "number") {
336
+ const child = root.children?.[step];
337
+ if (!child) return void 0;
338
+ const replaced = rewriteAt(child, rest, next);
339
+ if (replaced === void 0) return void 0;
340
+ const children = [...root.children ?? []];
341
+ if (replaced === null) children.splice(step, 1);
342
+ else children[step] = replaced;
343
+ const out = {
344
+ ...root,
345
+ children
346
+ };
347
+ if (children.length === 0) delete out.children;
348
+ return out;
349
+ }
350
+ const held = root.props?.[step];
351
+ if (!isPreviewElement(held)) return void 0;
352
+ const replaced = rewriteAt(held, rest, next);
353
+ if (replaced === void 0) return void 0;
354
+ const props = { ...root.props };
355
+ if (replaced === null) delete props[step];
356
+ else props[step] = replaced;
357
+ const out = {
358
+ ...root,
359
+ props
360
+ };
361
+ if (Object.keys(props).length === 0) delete out.props;
362
+ return out;
363
+ }
364
+ /** The element `address` names in `content`, or `undefined`. */
365
+ function elementIn(content, address) {
366
+ let current = content[address.index];
367
+ for (const step of address.path) {
368
+ if (!current) return void 0;
369
+ const next = typeof step === "number" ? current.children?.[step] : current.props?.[step];
370
+ current = isPreviewElement(next) ? next : void 0;
371
+ }
372
+ return current;
373
+ }
374
+ /** `content` with `element` placed at `address`; `undefined` when a step reaches nothing. */
375
+ function placeIn(content, address, element) {
376
+ const next = [...content];
377
+ if (address.path.length === 0) {
378
+ const index = Math.min(Math.max(address.index, 0), next.length);
379
+ next.splice(index, 0, element);
380
+ return next;
381
+ }
382
+ const target = next[address.index];
383
+ if (!target) return void 0;
384
+ const inner = insertInto(target, address.path, element);
385
+ if (inner === void 0) return void 0;
386
+ next[address.index] = inner;
387
+ return next;
388
+ }
389
+ /** `content` without the element at `address`; `undefined` when it names nothing. */
390
+ function removeIn(content, address) {
391
+ const next = [...content];
392
+ const target = next[address.index];
393
+ if (!target) return void 0;
394
+ if (address.path.length === 0) {
395
+ next.splice(address.index, 1);
396
+ return next;
397
+ }
398
+ const inner = rewriteAt(target, address.path, null);
399
+ if (inner === void 0 || inner === null) return void 0;
400
+ next[address.index] = inner;
401
+ return next;
402
+ }
403
+ /** Whether `to` is `from` or a position inside the element `from` names. */
404
+ function reachesInside(from, to) {
405
+ if (to.index !== from.index) return false;
406
+ if (to.path.length < from.path.length) return false;
407
+ return from.path.every((step, i) => to.path[i] === step);
408
+ }
409
+ /**
410
+ * `root` with `element` placed along `steps`: the last step is where it goes, a number among the
411
+ * children there (past the end appending) or a string as that element-valued prop; the steps
412
+ * before it walk the same way {@link rewriteAt} does. `undefined` when a step reaches nothing.
413
+ */
414
+ function insertInto(root, steps, element) {
415
+ const [step, ...rest] = steps;
416
+ if (step === void 0) return void 0;
417
+ if (rest.length === 0) {
418
+ if (typeof step === "number") {
419
+ const children = [...root.children ?? []];
420
+ children.splice(Math.min(Math.max(step, 0), children.length), 0, element);
421
+ return {
422
+ ...root,
423
+ children
424
+ };
425
+ }
426
+ return {
427
+ ...root,
428
+ props: {
429
+ ...root.props,
430
+ [step]: element
431
+ }
432
+ };
433
+ }
434
+ if (typeof step === "number") {
435
+ const child = root.children?.[step];
436
+ if (!child) return void 0;
437
+ const placed = insertInto(child, rest, element);
438
+ if (placed === void 0) return void 0;
439
+ const children = [...root.children ?? []];
440
+ children[step] = placed;
441
+ return {
442
+ ...root,
443
+ children
444
+ };
445
+ }
446
+ const held = root.props?.[step];
447
+ if (!isPreviewElement(held)) return void 0;
448
+ const placed = insertInto(held, rest, element);
449
+ if (placed === void 0) return void 0;
450
+ return {
451
+ ...root,
452
+ props: {
453
+ ...root.props,
454
+ [step]: placed
455
+ }
456
+ };
457
+ }
458
+ /** The `kind:path` an element names, or `undefined` for an intrinsic tag, which no `accepts` can name. */
459
+ function elementRefOf(config, element) {
460
+ if (isRef(element)) return element.__ref;
461
+ const { kind } = splitRef(element);
462
+ if (kind && kind !== element && config.entityOf(kind)) return element;
463
+ return config.resolve("component", element) ? `component:${element}` : void 0;
464
+ }
465
+ /** Every ref an `accepts` declaration names, one or a list. */
466
+ function acceptsRefsOf(accepts) {
467
+ return (Array.isArray(accepts) ? accepts : [accepts]).filter(isRef).map((entry) => entry.__ref);
468
+ }
469
+ /** The slots of a component as facts, computed once per component slice. */
470
+ function slotFacts(config, path) {
471
+ const cache = factsCache(config);
472
+ const hit = cache.get(path);
473
+ if (hit) return hit;
474
+ const body = config.resolve("component", path)?.body;
475
+ const facts = factsOfBody({
476
+ config,
477
+ body: body ?? {},
478
+ routes: slotTargetsOf(config, path)
479
+ });
480
+ cache.set(path, facts);
481
+ return facts;
482
+ }
483
+ function factsOfBody({ config, body, routes }) {
484
+ const props = body.props;
485
+ const layers = body.layers;
486
+ const fillable = routes.filter((route) => {
487
+ if ((props?.[route.from])?.text === true) return false;
488
+ return !routesToText(config, layers, route);
489
+ });
490
+ const root = layers ? rootLayerOf(layers)[0] : void 0;
491
+ const positionedChildren = layers !== void 0 && Object.keys(layers).length >= 2;
492
+ return fillable.filter((route) => route.from !== "children" || positionedChildren && route.layer !== root).map((route) => ({
493
+ ...route,
494
+ accepts: acceptsOf({
495
+ config,
496
+ props,
497
+ layers,
498
+ route
499
+ })
500
+ }));
501
+ }
502
+ /**
503
+ * The refs a slot accepts: its own `accepts`, else the `accepts` of the prop at the far end of its
504
+ * route, read in the config that owns the host layer's component, since a borrowed host with no
505
+ * derived row is spelled in its source's namespace.
506
+ */
507
+ function acceptsOf({ config, props, layers, route }) {
508
+ const decl = props?.[route.from];
509
+ const own = acceptsRefsOf(decl?.accepts);
510
+ if (own.length > 0) return own;
511
+ return acceptsRefsOf(farEndProp(config, layers, route)?.accepts);
512
+ }
513
+ /** The component a layer's element names, resolved where it lives; `undefined` for an intrinsic tag. */
514
+ function hostComponent(config, hostRef) {
515
+ if (!isRef(hostRef) && !hostRef.includes(":")) return config.resolve("component", hostRef);
516
+ return (config.configFor(hostRef) ?? config).resolve("component", pathOf(hostRef));
517
+ }
518
+ /** The declaration a route lands on, when the host layer is a component; `undefined` for a tag. */
519
+ function farEndProp(config, layers, route) {
520
+ const hostRef = layers?.[route.layer];
521
+ if (!hostRef || !hostComponent(config, hostRef)) return void 0;
522
+ return resolveComponentProps(config.configFor(hostRef) ?? config, pathOf(hostRef))[route.into];
523
+ }
524
+ /**
525
+ * Whether a route ends in text: the host layer is a component whose prop at the far end is a text
526
+ * slot, or forwards on to one (a badge's `children` onto its text layer), so the layer holds a
527
+ * string. An intrinsic host has no declaration and takes nodes; a host that declares no such prop
528
+ * cannot take the route at all, which reads the same way here.
529
+ */
530
+ function routesToText(config, layers, route) {
531
+ const hostRef = layers?.[route.layer];
532
+ if (!hostRef || !hostComponent(config, hostRef)) return false;
533
+ const into = farEndProp(config, layers, route);
534
+ if (!into) return true;
535
+ if (into.text === true) return true;
536
+ if (into.type !== "forward") return false;
537
+ const owner = config.configFor(hostRef) ?? config;
538
+ const hostLayers = (owner.resolve("component", pathOf(hostRef))?.body)?.layers;
539
+ return routesIntoText(owner, hostLayers, into);
540
+ }
541
+ //#endregion
542
+ export { listSlots, namedSlots, slotHostedBy, slotOf };
@@ -0,0 +1,82 @@
1
+ import { ComponentStyleBody, ProjectedVisibility } from "../entities/system/Component.js";
2
+ import { StateCondition } from "@json-render/core";
3
+
4
+ //#region src/spec/visibility.d.ts
5
+ /** One `axis differs from value` test: json-render's `$state` test carrying only `neq`. */
6
+ type VariantExclusionTest = Pick<StateCondition, '$state' | 'neq'>;
7
+ /** The clause that hides a layer at one coordinate: one test, or an `$or` of one per pinned axis. */
8
+ type VariantExclusion = VariantExclusionTest | {
9
+ $or: VariantExclusionTest[];
10
+ };
11
+ /**
12
+ * The clause that hides a layer at one coordinate, or `null` for a cell that pins nothing: a
13
+ * zero-axis component has one cell, and hiding the layer there is deleting it.
14
+ */
15
+ declare function variantExclusion(cellProps: Readonly<Record<string, unknown>>): VariantExclusion | null;
16
+ /**
17
+ * A condition split into the clauses the eye writes, one per variant a layer is hidden in, and the
18
+ * rest, which is what a boolean binding reads and edits. A surface that rewrites the rest puts the
19
+ * exclusions back with `spelledWith`, so a hide in one variant survives a change of binding.
20
+ */
21
+ declare function splitVariantExclusions(visible: ProjectedVisibility | undefined): {
22
+ exclusions: unknown[];
23
+ rest: unknown[];
24
+ };
25
+ /** The stored spelling of `clauses` followed by `exclusions`: `null` for none, a bare clause for one,
26
+ * the and-list for several. */
27
+ declare function spelledWith({
28
+ clauses,
29
+ exclusions
30
+ }: {
31
+ clauses: readonly unknown[];
32
+ exclusions: readonly unknown[];
33
+ }): unknown | null;
34
+ /** Whether the node's condition carries the eye's exclusion for this cell. */
35
+ declare function excludesVariant({
36
+ visible,
37
+ cellProps
38
+ }: {
39
+ visible: ProjectedVisibility | undefined;
40
+ cellProps: Readonly<Record<string, unknown>>;
41
+ }): boolean;
42
+ /**
43
+ * The condition with this cell excluded: the stored one plus the clause, or unchanged when the
44
+ * clause is already there. `undefined` when the cell pins nothing, since there is no coordinate to
45
+ * exclude.
46
+ */
47
+ declare function withVariantHidden({
48
+ visible,
49
+ cellProps
50
+ }: {
51
+ visible: ProjectedVisibility | undefined;
52
+ cellProps: Readonly<Record<string, unknown>>;
53
+ }): unknown | undefined;
54
+ /**
55
+ * The condition with this cell's exclusion taken out, `null` when nothing is left (which clears the
56
+ * field), or `undefined` when the condition never excluded it.
57
+ */
58
+ declare function withVariantShown({
59
+ visible,
60
+ cellProps
61
+ }: {
62
+ visible: ProjectedVisibility | undefined;
63
+ cellProps: Readonly<Record<string, unknown>>;
64
+ }): unknown | null | undefined;
65
+ /**
66
+ * The style rules that hide `layer` with `display: none` under `state`, in stored order. The
67
+ * projection keeps no source for a hidden layer, so a surface asking which rule hid it reads them
68
+ * here.
69
+ */
70
+ declare function layerHidingRules({
71
+ component,
72
+ layer,
73
+ state
74
+ }: {
75
+ component: {
76
+ readonly styles?: Readonly<Record<string, ComponentStyleBody>> | null;
77
+ };
78
+ layer: string; /** The instance's prop bag, defaults included, the same bag `layerStyles` resolves against. */
79
+ state: Record<string, unknown>;
80
+ }): ComponentStyleBody[];
81
+ //#endregion
82
+ export { VariantExclusion, VariantExclusionTest, excludesVariant, layerHidingRules, spelledWith, splitVariantExclusions, variantExclusion, withVariantHidden, withVariantShown };