@yahoo/uds-create-config 4.6.0 → 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.
- package/dist/configs/CanvasConfig.d.ts +900 -80
- package/dist/configs/react-native-system.d.ts +603 -55
- package/dist/configs/system.d.ts +603 -55
- package/dist/configs/system.js +7 -1
- package/dist/entities/system/Component.d.ts +480 -39
- package/dist/entities/system/Component.js +264 -93
- package/dist/entities/system/File.d.ts +1 -1
- package/dist/entities/system/LinkedSystem.d.ts +2 -2
- package/dist/entities/system/Operation.d.ts +1 -1
- package/dist/entities/system/StyleProperty.d.ts +8 -0
- package/dist/entities/system/StyleProperty.js +12 -1
- package/dist/entities/system/Tool.d.ts +1 -1
- package/dist/entities/system/component-module.js +1 -1
- package/dist/entities/system/defineComponent.d.ts +45 -7
- package/dist/entities/system/defineComponent.js +52 -25
- package/dist/framework/Config.d.ts +2 -2
- package/dist/framework/Config.js +59 -7
- package/dist/framework/class-names.d.ts +4 -2
- package/dist/framework/class-names.js +56 -17
- package/dist/framework/defineDerivedEntity.d.ts +1 -1
- package/dist/framework/defineEntity.d.ts +2 -2
- package/dist/framework/defineSubEntity.d.ts +3 -2
- package/dist/framework/preview-store.d.ts +19 -0
- package/dist/framework/preview-store.js +18 -0
- package/dist/framework/projections.d.ts +28 -8
- package/dist/framework/projections.js +50 -16
- package/dist/framework/prop-surface.d.ts +21 -1
- package/dist/framework/prop-surface.js +99 -21
- package/dist/framework/render-spec.d.ts +103 -3
- package/dist/framework/render-spec.js +285 -37
- package/dist/framework/schema-version.d.ts +1 -1
- package/dist/framework/schema-version.js +5 -5
- package/dist/framework/utils/refs.js +10 -1
- package/dist/framework/validate-spec.d.ts +2 -0
- package/dist/framework/validate-spec.js +13 -0
- package/dist/index.d.ts +14 -11
- package/dist/index.js +6 -5
- package/dist/migrations/2.0.0/v1-artifact.d.ts +305 -29
- package/dist/migrations/20260919051027_prop_conditions_and_anatomy_positions_at_rest.d.ts +22 -0
- package/dist/migrations/20260919051027_prop_conditions_and_anatomy_positions_at_rest.js +10 -0
- package/dist/migrations/20260920004019_preview_conditions_keyed.d.ts +27 -0
- package/dist/migrations/20260920004019_preview_conditions_keyed.js +148 -0
- package/dist/migrations/20260920161309_prefixed_token_group.d.ts +12 -0
- package/dist/migrations/20260920161309_prefixed_token_group.js +10 -0
- package/dist/renderer/wrapRegistry.js +8 -2
- package/dist/renderer/wrappers/component-refs.d.ts +14 -0
- package/dist/renderer/wrappers/component-refs.js +79 -0
- package/dist/renderer/wrappers/hex-normalize.d.ts +5 -7
- package/dist/renderer/wrappers/hex-normalize.js +11 -10
- package/dist/spec/cell.d.ts +171 -0
- package/dist/spec/cell.js +361 -0
- package/dist/spec/detach.d.ts +125 -0
- package/dist/spec/detach.js +511 -0
- package/dist/spec/empty-node-slots.d.ts +11 -23
- package/dist/spec/empty-node-slots.js +12 -39
- package/dist/spec/index.d.ts +5 -3
- package/dist/spec/index.js +3 -3
- package/dist/spec/instance.d.ts +77 -0
- package/dist/spec/instance.js +80 -0
- package/dist/spec/preview-content.d.ts +95 -0
- package/dist/spec/preview-content.js +149 -0
- package/dist/spec/preview-elements.d.ts +54 -15
- package/dist/spec/preview-elements.js +109 -29
- package/dist/spec/prop-conditions.d.ts +28 -0
- package/dist/spec/prop-conditions.js +39 -0
- package/dist/spec/slot.d.ts +206 -0
- package/dist/spec/slot.js +542 -0
- package/dist/spec/visibility.d.ts +82 -0
- package/dist/spec/visibility.js +109 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -2
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { canonicalCondition, refLeaf } from "../framework/utils/refs.js";
|
|
2
|
+
import { holdingPreviewConditions, previewConditionalProps, previewDefaults } from "../framework/projections.js";
|
|
3
|
+
import { propApplies, surfaceProps } from "../framework/prop-surface.js";
|
|
4
|
+
import { ruleApplies } from "../framework/layer-styles.js";
|
|
5
|
+
import { propsIntoLayer } from "../framework/render-spec.js";
|
|
6
|
+
import { cellAtDefaults, parsePreviewContentKey, previewConditionAt, previewValueSource } from "./preview-content.js";
|
|
7
|
+
import { listSlots, slotOf } from "./slot.js";
|
|
8
|
+
//#region src/spec/cell.ts
|
|
9
|
+
/** A stored condition with every ref read as its leaf. */
|
|
10
|
+
function leaves(when) {
|
|
11
|
+
return Object.fromEntries(Object.entries(when).map(([prop, value]) => [prop, refLeaf(value)]));
|
|
12
|
+
}
|
|
13
|
+
const cellsByConfig = /* @__PURE__ */ new WeakMap();
|
|
14
|
+
function coordinateKey(path, cell) {
|
|
15
|
+
return `${path}\u0000${Object.entries(cell).sort(([a], [b]) => a < b ? -1 : 1).map(([prop, value]) => `${prop}=${JSON.stringify(value)}`).join("&")}`;
|
|
16
|
+
}
|
|
17
|
+
const UNSET = Symbol("unset");
|
|
18
|
+
/** Whether `a` governs ahead of `b`: more specific, then a preview entry over a style rule, then
|
|
19
|
+
* reaching the layer, then the later stored. */
|
|
20
|
+
function outranks(a, b) {
|
|
21
|
+
const specificity = Object.keys(a.when).length - Object.keys(b.when).length;
|
|
22
|
+
if (specificity !== 0) return specificity > 0;
|
|
23
|
+
if (a.source !== b.source) return a.source === "preview";
|
|
24
|
+
if (a.reaches !== b.reaches) return a.reaches;
|
|
25
|
+
return a.order > b.order;
|
|
26
|
+
}
|
|
27
|
+
var Cell = class {
|
|
28
|
+
/** The component the cell shows. */
|
|
29
|
+
component;
|
|
30
|
+
/** The axis values the cell pins; empty for the component's own row. */
|
|
31
|
+
coordinate;
|
|
32
|
+
memoDefaults = UNSET;
|
|
33
|
+
memoConditional = UNSET;
|
|
34
|
+
memoProps = UNSET;
|
|
35
|
+
memoEntry = UNSET;
|
|
36
|
+
memoHolding = UNSET;
|
|
37
|
+
memoOwn = UNSET;
|
|
38
|
+
memoOwnConditions = UNSET;
|
|
39
|
+
slotViews = /* @__PURE__ */ new Map();
|
|
40
|
+
/** Built by {@link cellAt}; a surface reaches one through `Component.cell`. */
|
|
41
|
+
constructor(config, component, coordinate) {
|
|
42
|
+
this.config = config;
|
|
43
|
+
this.component = component;
|
|
44
|
+
this.coordinate = coordinate;
|
|
45
|
+
}
|
|
46
|
+
/** What every cell starts from: the declared defaults, `defaultProps`, then the `*` preview entry. */
|
|
47
|
+
get defaults() {
|
|
48
|
+
if (this.memoDefaults === UNSET) this.memoDefaults = previewDefaults(this.config, this.component);
|
|
49
|
+
return this.memoDefaults;
|
|
50
|
+
}
|
|
51
|
+
/** What the conditional `preview` entries that hold for this cell add, later entries over earlier. */
|
|
52
|
+
get conditional() {
|
|
53
|
+
if (this.memoConditional === UNSET) this.memoConditional = previewConditionalProps(this.config, this.component, { ...this.coordinate });
|
|
54
|
+
return this.memoConditional;
|
|
55
|
+
}
|
|
56
|
+
/** The values in force: the defaults, what holds for the cell, then the coordinate itself. */
|
|
57
|
+
get props() {
|
|
58
|
+
if (this.memoProps === UNSET) this.memoProps = {
|
|
59
|
+
...this.defaults,
|
|
60
|
+
...this.conditional,
|
|
61
|
+
...this.coordinate
|
|
62
|
+
};
|
|
63
|
+
return this.memoProps;
|
|
64
|
+
}
|
|
65
|
+
/** The `preview` entry whose condition is exactly this coordinate, or `undefined`. */
|
|
66
|
+
get entry() {
|
|
67
|
+
if (this.memoEntry === UNSET) this.memoEntry = previewConditionAt({
|
|
68
|
+
config: this.config,
|
|
69
|
+
path: this.component,
|
|
70
|
+
cell: this.coordinate
|
|
71
|
+
});
|
|
72
|
+
return this.memoEntry;
|
|
73
|
+
}
|
|
74
|
+
/** The conditional `preview` entries that hold for this cell, in stored order. */
|
|
75
|
+
get holding() {
|
|
76
|
+
if (this.memoHolding === UNSET) this.memoHolding = holdingPreviewConditions({
|
|
77
|
+
config: this.config,
|
|
78
|
+
path: this.component,
|
|
79
|
+
cell: this.coordinate
|
|
80
|
+
});
|
|
81
|
+
return this.memoHolding;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The props this cell shows as its own: the ones a condition reveals under its values, and the
|
|
85
|
+
* ones its holding preview entries set. What every cell shares belongs to the component's own
|
|
86
|
+
* row, so a cell repeats none of it.
|
|
87
|
+
*/
|
|
88
|
+
get own() {
|
|
89
|
+
if (this.memoOwn === UNSET) {
|
|
90
|
+
const own = /* @__PURE__ */ new Set();
|
|
91
|
+
for (const prop of surfaceProps(this.config, this.component)) if (prop.when && propApplies(prop, this.props)) own.add(prop.name);
|
|
92
|
+
for (const name of Object.keys(this.conditional)) own.add(name);
|
|
93
|
+
this.memoOwn = own;
|
|
94
|
+
}
|
|
95
|
+
return this.memoOwn;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The conditions the own props are shown under, each once. A gated prop is shown under its own
|
|
99
|
+
* `when`, which can be wider than the cell (`type = color` on a `color, xs` cell). A prop the
|
|
100
|
+
* cell's preview pins has no `when` of its own, so the coordinate is what shows it.
|
|
101
|
+
*/
|
|
102
|
+
get ownConditions() {
|
|
103
|
+
if (this.memoOwnConditions === UNSET) {
|
|
104
|
+
const coordinate = Object.fromEntries(Object.entries(this.coordinate).map(([prop, value]) => [prop, [value]]));
|
|
105
|
+
const declared = new Map(surfaceProps(this.config, this.component).map((prop) => [prop.name, prop.when]));
|
|
106
|
+
const distinct = /* @__PURE__ */ new Map();
|
|
107
|
+
for (const name of this.own) {
|
|
108
|
+
const when = declared.get(name) ?? coordinate;
|
|
109
|
+
distinct.set(canonicalCondition(when), when);
|
|
110
|
+
}
|
|
111
|
+
this.memoOwnConditions = [...distinct.values()];
|
|
112
|
+
}
|
|
113
|
+
return this.memoOwnConditions;
|
|
114
|
+
}
|
|
115
|
+
/** Whether the coordinate is the component's defaults, so its preview is every tile's. A forced
|
|
116
|
+
* modifier is a state the defaults never show, so a cell with one is not the defaults. */
|
|
117
|
+
isDefaults(modifiers = []) {
|
|
118
|
+
return cellAtDefaults({
|
|
119
|
+
config: this.config,
|
|
120
|
+
path: this.component,
|
|
121
|
+
cell: this.coordinate,
|
|
122
|
+
modifiers
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/** Whether a stored condition holds at this cell, by the same test a style rule is applied with. */
|
|
126
|
+
holds(when) {
|
|
127
|
+
return ruleApplies(when, this.props);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* The condition in force for `layer` at this cell, or `null` when only the base holds and an edit
|
|
131
|
+
* is this cell's alone.
|
|
132
|
+
*
|
|
133
|
+
* The candidates are the conditions that hold here and speak only of the axes the coordinate pins,
|
|
134
|
+
* so a reach can be spelled as cells of the grid: the style rules whose bag names the layer, the
|
|
135
|
+
* preview entries that set a prop routed into the layer, and every preview entry holding at the
|
|
136
|
+
* cell, since an entry such as `type = color` is what defines that row. A condition with a list
|
|
137
|
+
* value is not a coordinate and is left out. The most specific wins; at equal specificity a
|
|
138
|
+
* preview entry beats a style rule, since an entry is what defines a row, one that reaches the
|
|
139
|
+
* layer beats one that does not, and a later stored one beats an earlier.
|
|
140
|
+
*
|
|
141
|
+
* A preview content key (`messages__children__2`) names an element a slot's content placed, so the
|
|
142
|
+
* entries filling that slot are what reach it.
|
|
143
|
+
*/
|
|
144
|
+
governing(layer = null) {
|
|
145
|
+
const pinned = new Set(Object.keys(this.coordinate));
|
|
146
|
+
const isCoordinate = (when) => {
|
|
147
|
+
const entries = Object.entries(when);
|
|
148
|
+
return entries.length > 0 && entries.every(([prop, value]) => {
|
|
149
|
+
const leaf = refLeaf(value);
|
|
150
|
+
return pinned.has(prop) && (typeof leaf === "string" || typeof leaf === "number" || typeof leaf === "boolean");
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
const content = layer ? parsePreviewContentKey(layer) : null;
|
|
154
|
+
const reaching = new Set(content ? [content.prop] : layer ? propsIntoLayer({
|
|
155
|
+
config: this.config,
|
|
156
|
+
path: this.component,
|
|
157
|
+
layer
|
|
158
|
+
}) : []);
|
|
159
|
+
const candidates = [];
|
|
160
|
+
let order = 0;
|
|
161
|
+
if (layer && !content) {
|
|
162
|
+
const { styles = {} } = this.config.resolve("component", this.component)?.body ?? {};
|
|
163
|
+
for (const [key, rule] of Object.entries(styles)) {
|
|
164
|
+
if (!rule.when || !isCoordinate(rule.when)) continue;
|
|
165
|
+
if (Object.keys(rule.layers?.[layer] ?? {}).length === 0) continue;
|
|
166
|
+
if (!this.holds(rule.when)) continue;
|
|
167
|
+
candidates.push({
|
|
168
|
+
source: "style",
|
|
169
|
+
key,
|
|
170
|
+
when: leaves(rule.when),
|
|
171
|
+
reaches: true,
|
|
172
|
+
order: order++
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
for (const { key, condition } of this.holding) {
|
|
177
|
+
if (!condition.when || !isCoordinate(condition.when)) continue;
|
|
178
|
+
candidates.push({
|
|
179
|
+
source: "preview",
|
|
180
|
+
key,
|
|
181
|
+
when: leaves(condition.when),
|
|
182
|
+
reaches: [...reaching].some((prop) => condition.props[prop] !== void 0),
|
|
183
|
+
order: order++
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
let winner = null;
|
|
187
|
+
for (const candidate of candidates) if (!winner || outranks(candidate, winner)) winner = candidate;
|
|
188
|
+
return winner ? {
|
|
189
|
+
source: winner.source,
|
|
190
|
+
key: winner.key,
|
|
191
|
+
when: winner.when
|
|
192
|
+
} : null;
|
|
193
|
+
}
|
|
194
|
+
/** The condition an edit to `layer` at this cell writes under: {@link governing}'s, else the
|
|
195
|
+
* coordinate itself. */
|
|
196
|
+
whenFor(layer = null) {
|
|
197
|
+
return this.governing(layer)?.when ?? { ...this.coordinate };
|
|
198
|
+
}
|
|
199
|
+
/** The stored field the value this cell shows for `prop` comes from, or `undefined` when the
|
|
200
|
+
* component stores none. */
|
|
201
|
+
sourceOf(prop) {
|
|
202
|
+
return previewValueSource({
|
|
203
|
+
config: this.config,
|
|
204
|
+
path: this.component,
|
|
205
|
+
cell: this.coordinate,
|
|
206
|
+
prop
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
/** The component's slots, each read and written at this coordinate. */
|
|
210
|
+
slots() {
|
|
211
|
+
return listSlots({
|
|
212
|
+
config: this.config,
|
|
213
|
+
path: this.component
|
|
214
|
+
}).map((slot) => this.view(slot));
|
|
215
|
+
}
|
|
216
|
+
/** The slot a prop names, read and written at this coordinate; `undefined` when the prop is not one. */
|
|
217
|
+
slot(prop) {
|
|
218
|
+
const slot = slotOf({
|
|
219
|
+
config: this.config,
|
|
220
|
+
path: this.component,
|
|
221
|
+
slot: prop
|
|
222
|
+
});
|
|
223
|
+
return slot ? this.view(slot) : void 0;
|
|
224
|
+
}
|
|
225
|
+
view(slot) {
|
|
226
|
+
let view = this.slotViews.get(slot.from);
|
|
227
|
+
if (!view) {
|
|
228
|
+
view = new CellSlot(slot, this.coordinate);
|
|
229
|
+
this.slotViews.set(slot.from, view);
|
|
230
|
+
}
|
|
231
|
+
return view;
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
/**
|
|
235
|
+
* A slot as one cell shows it: the route and acceptance of the {@link Slot}, with its content read
|
|
236
|
+
* and written at the cell's coordinate, so a gesture on a cell never hands the coordinate back.
|
|
237
|
+
*/
|
|
238
|
+
var CellSlot = class {
|
|
239
|
+
constructor(slot, coordinate) {
|
|
240
|
+
this.slot = slot;
|
|
241
|
+
this.coordinate = coordinate;
|
|
242
|
+
}
|
|
243
|
+
get component() {
|
|
244
|
+
return this.slot.component;
|
|
245
|
+
}
|
|
246
|
+
/** The prop the content arrives as on the instance. */
|
|
247
|
+
get from() {
|
|
248
|
+
return this.slot.from;
|
|
249
|
+
}
|
|
250
|
+
/** The layer that holds the content. */
|
|
251
|
+
get layer() {
|
|
252
|
+
return this.slot.layer;
|
|
253
|
+
}
|
|
254
|
+
/** The prop the content lands under on the layer's element. */
|
|
255
|
+
get into() {
|
|
256
|
+
return this.slot.into;
|
|
257
|
+
}
|
|
258
|
+
/** The refs the slot accepts; empty when unconstrained. */
|
|
259
|
+
get accepts() {
|
|
260
|
+
return this.slot.accepts;
|
|
261
|
+
}
|
|
262
|
+
get isChildren() {
|
|
263
|
+
return this.slot.isChildren;
|
|
264
|
+
}
|
|
265
|
+
/** Whether the slot takes an element — see {@link Slot.takes}. */
|
|
266
|
+
takes(element) {
|
|
267
|
+
return this.slot.takes(element);
|
|
268
|
+
}
|
|
269
|
+
/** The elements this cell shows in the slot, in order. */
|
|
270
|
+
get content() {
|
|
271
|
+
return this.slot.content(this.coordinate);
|
|
272
|
+
}
|
|
273
|
+
/** The stored field this cell reads the content from, or `undefined` when nothing is stored. */
|
|
274
|
+
get source() {
|
|
275
|
+
return this.slot.source(this.coordinate);
|
|
276
|
+
}
|
|
277
|
+
/** Append to the content under this coordinate — see {@link Slot.append}. */
|
|
278
|
+
append(element) {
|
|
279
|
+
return this.slot.append({
|
|
280
|
+
cell: this.coordinate,
|
|
281
|
+
element
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
/** The content with one element placed at an address — see {@link Slot.placed}. */
|
|
285
|
+
placed({ address, element }) {
|
|
286
|
+
return this.slot.placed({
|
|
287
|
+
cell: this.coordinate,
|
|
288
|
+
address,
|
|
289
|
+
element
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
/** The element at an address — see {@link Slot.elementAt}. */
|
|
293
|
+
elementAt(address) {
|
|
294
|
+
return this.slot.elementAt({
|
|
295
|
+
cell: this.coordinate,
|
|
296
|
+
address
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
/** The element at one address moved to another — see {@link Slot.moveAt}. */
|
|
300
|
+
moveAt({ from, to }) {
|
|
301
|
+
return this.slot.moveAt({
|
|
302
|
+
cell: this.coordinate,
|
|
303
|
+
from,
|
|
304
|
+
to
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
/** A copy of the element at an address, placed after it — see {@link Slot.duplicateAt}. */
|
|
308
|
+
duplicateAt(address) {
|
|
309
|
+
return this.slot.duplicateAt({
|
|
310
|
+
cell: this.coordinate,
|
|
311
|
+
address
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
/** Write one element at an address — see {@link Slot.insertAt}. */
|
|
315
|
+
insertAt({ address, element }) {
|
|
316
|
+
return this.slot.insertAt({
|
|
317
|
+
cell: this.coordinate,
|
|
318
|
+
address,
|
|
319
|
+
element
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
/** Swap one element in place — see {@link Slot.replaceAt}. */
|
|
323
|
+
replaceAt({ address, element }) {
|
|
324
|
+
return this.slot.replaceAt({
|
|
325
|
+
cell: this.coordinate,
|
|
326
|
+
address,
|
|
327
|
+
element
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
/** Take one element out — see {@link Slot.removeAt}. */
|
|
331
|
+
removeAt(address) {
|
|
332
|
+
return this.slot.removeAt({
|
|
333
|
+
cell: this.coordinate,
|
|
334
|
+
address
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
/** Store the whole content under this coordinate — see {@link Slot.set}. */
|
|
338
|
+
set(elements) {
|
|
339
|
+
return this.slot.set({
|
|
340
|
+
cell: this.coordinate,
|
|
341
|
+
elements
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
/** The component under a coordinate. Always answers: a path naming no component reads as a cell
|
|
346
|
+
* that stores nothing. The same config, path and coordinate hand back the same cell. */
|
|
347
|
+
function cellAt({ config, path, cell }) {
|
|
348
|
+
let byKey = cellsByConfig.get(config);
|
|
349
|
+
if (!byKey) {
|
|
350
|
+
byKey = /* @__PURE__ */ new Map();
|
|
351
|
+
cellsByConfig.set(config, byKey);
|
|
352
|
+
}
|
|
353
|
+
const key = coordinateKey(path, cell);
|
|
354
|
+
const hit = byKey.get(key);
|
|
355
|
+
if (hit) return hit;
|
|
356
|
+
const made = new Cell(config, path, cell);
|
|
357
|
+
byKey.set(key, made);
|
|
358
|
+
return made;
|
|
359
|
+
}
|
|
360
|
+
//#endregion
|
|
361
|
+
export { cellAt };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { ConfigEdit } from "../framework/config-op.js";
|
|
2
|
+
import { Ref } from "../framework/utils/refs.js";
|
|
3
|
+
import { Config } from "../framework/Config.js";
|
|
4
|
+
import { PreviewElement } from "./preview-elements.js";
|
|
5
|
+
import { PreviewContentAddress } from "./preview-content.js";
|
|
6
|
+
import { Slot, SlotCell } from "./slot.js";
|
|
7
|
+
|
|
8
|
+
//#region src/spec/detach.d.ts
|
|
9
|
+
/**
|
|
10
|
+
* The edits that detach the instance the layer `layer` of `path` renders: one `element-insert` of
|
|
11
|
+
* the instance's component's anatomy under the instance's parent at its index, a move for each
|
|
12
|
+
* element the instance held in a slot, and the instance's deletion last. `null` when the layer
|
|
13
|
+
* renders no component, or nothing places it.
|
|
14
|
+
*/
|
|
15
|
+
declare function detachInstanceEdit({
|
|
16
|
+
config,
|
|
17
|
+
path,
|
|
18
|
+
layer
|
|
19
|
+
}: {
|
|
20
|
+
config: Config; /** The edited component. */
|
|
21
|
+
path: string; /** The layer whose element is the instance's component. */
|
|
22
|
+
layer: string;
|
|
23
|
+
}): ConfigEdit[] | null;
|
|
24
|
+
/**
|
|
25
|
+
* The edits that detach one element of a slot's content: the element the address names in the
|
|
26
|
+
* content the cell shows becomes anatomy under the layer its slot routes to, baked at the props
|
|
27
|
+
* the element sets, and the content loses it. Its own children and the elements it hands to its
|
|
28
|
+
* slots come along as instance nodes under the layers the element's component routes them to.
|
|
29
|
+
* `null` for an address inside another element, or one naming no component.
|
|
30
|
+
*/
|
|
31
|
+
declare function detachContentEdit({
|
|
32
|
+
config,
|
|
33
|
+
path,
|
|
34
|
+
cell,
|
|
35
|
+
address
|
|
36
|
+
}: {
|
|
37
|
+
config: Config; /** The edited component. */
|
|
38
|
+
path: string; /** The coordinate whose content the address reads. */
|
|
39
|
+
cell: SlotCell;
|
|
40
|
+
address: PreviewContentAddress;
|
|
41
|
+
}): ConfigEdit[] | null;
|
|
42
|
+
/** A baked instance as page-spec elements: json-render's shape, keyed by the source's node names. */
|
|
43
|
+
interface DetachedElementSpec {
|
|
44
|
+
readonly root: string;
|
|
45
|
+
readonly elements: Record<string, {
|
|
46
|
+
type: string;
|
|
47
|
+
props?: Record<string, unknown>;
|
|
48
|
+
children?: string[];
|
|
49
|
+
}>;
|
|
50
|
+
/** For each slot prop of the source, the element its content belongs under. */
|
|
51
|
+
readonly slotHosts: Record<string, string>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The anatomy an instance of `source` renders under `props`, as the elements a page document
|
|
55
|
+
* stores: the layer's element as the spec `type` (a component as its `component:` marker), the baked
|
|
56
|
+
* bag and any text as props, the kept edges as children. What a design-canvas Detach splices in
|
|
57
|
+
* place of the instance; the content the instance held goes under `slotHosts[<prop>]`.
|
|
58
|
+
*/
|
|
59
|
+
declare function detachedElementSpec({
|
|
60
|
+
config,
|
|
61
|
+
source,
|
|
62
|
+
props,
|
|
63
|
+
filled
|
|
64
|
+
}: {
|
|
65
|
+
config: Config; /** The component ref the instance renders (`component:Badge`). */
|
|
66
|
+
source: string; /** The instance's props, a `layerProps` bag included. */
|
|
67
|
+
props: Record<string, unknown>;
|
|
68
|
+
/**
|
|
69
|
+
* The slot props the instance fills with elements. A layer shown only while its slot has content
|
|
70
|
+
* (`media`, `actions`) is kept for a filled slot and dropped for an empty one, so the content has
|
|
71
|
+
* that layer to land under rather than falling back to the root.
|
|
72
|
+
*/
|
|
73
|
+
filled?: ReadonlySet<string>;
|
|
74
|
+
}): DetachedElementSpec | null;
|
|
75
|
+
/**
|
|
76
|
+
* A preview element that renders a component, as the tree its component is made of, baked at the
|
|
77
|
+
* props the element sets: each kept source node becomes an element (a component as its ref, a tag
|
|
78
|
+
* as itself), the baked bag as its props, its text as text, its edges as children. The children
|
|
79
|
+
* and element-valued props the original held ride along under the elements the component routes
|
|
80
|
+
* them to. `null` when the component stores nothing.
|
|
81
|
+
*/
|
|
82
|
+
declare function detachedPreviewElement({
|
|
83
|
+
config,
|
|
84
|
+
source,
|
|
85
|
+
element
|
|
86
|
+
}: {
|
|
87
|
+
config: Config; /** The component ref the element renders (`component:Button`). */
|
|
88
|
+
source: string;
|
|
89
|
+
element: PreviewElement;
|
|
90
|
+
}): PreviewElement | null;
|
|
91
|
+
/** Why a slot would not hold a detached element: the slot, and what the element becomes. */
|
|
92
|
+
interface DetachRefusal {
|
|
93
|
+
/** The slot the element sits in, on its parent's component. */
|
|
94
|
+
readonly slot: Slot;
|
|
95
|
+
/** What the detached element's root is: a tag, or a component ref. */
|
|
96
|
+
readonly becomes: string | Ref;
|
|
97
|
+
}
|
|
98
|
+
/** The root element a detached instance of `source` would render, under `props`. */
|
|
99
|
+
declare function detachedRootElement({
|
|
100
|
+
config,
|
|
101
|
+
source,
|
|
102
|
+
props
|
|
103
|
+
}: {
|
|
104
|
+
config: Config;
|
|
105
|
+
source: string;
|
|
106
|
+
props: Record<string, unknown>;
|
|
107
|
+
}): string | Ref | undefined;
|
|
108
|
+
/**
|
|
109
|
+
* Why the element an address names inside another element cannot detach in place: the slot of the
|
|
110
|
+
* parent's component that holds it refuses what the element becomes. `null` when it can, or when
|
|
111
|
+
* the address is not inside another element.
|
|
112
|
+
*/
|
|
113
|
+
declare function detachContentRefusal({
|
|
114
|
+
config,
|
|
115
|
+
path,
|
|
116
|
+
cell,
|
|
117
|
+
address
|
|
118
|
+
}: {
|
|
119
|
+
config: Config;
|
|
120
|
+
path: string;
|
|
121
|
+
cell: SlotCell;
|
|
122
|
+
address: PreviewContentAddress;
|
|
123
|
+
}): DetachRefusal | null;
|
|
124
|
+
//#endregion
|
|
125
|
+
export { DetachRefusal, DetachedElementSpec, detachContentEdit, detachContentRefusal, detachInstanceEdit, detachedElementSpec, detachedPreviewElement, detachedRootElement };
|