@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.
- 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 +8 -2
- package/dist/framework/Config.js +97 -23
- 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/source-integrity.d.ts +14 -1
- 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 +15 -12
- package/dist/index.js +7 -6
- 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,511 @@
|
|
|
1
|
+
import { isRef, kindOf, pathOf, refLeaf } from "../framework/utils/refs.js";
|
|
2
|
+
import { rootLayerOf } from "../entities/system/element.js";
|
|
3
|
+
import { declaredPropDefaults } from "../framework/projections.js";
|
|
4
|
+
import { isPreviewElement } from "./preview-elements.js";
|
|
5
|
+
import { entryKeyOf, resolveVisibility, slotTargetsOf } from "../framework/render-spec.js";
|
|
6
|
+
import { Component } from "../entities/system/Component.js";
|
|
7
|
+
//#region src/spec/detach.ts
|
|
8
|
+
/**
|
|
9
|
+
* Detaching a placed instance: the layer that renders a component becomes that component's anatomy,
|
|
10
|
+
* inlined as the edited component's own layers and baked at the instance's props.
|
|
11
|
+
*
|
|
12
|
+
* An instance shows its component under the props the node sets. Detaching keeps what it showed and
|
|
13
|
+
* gives the person the layers to restyle: each source node that renders under those props becomes a
|
|
14
|
+
* node here, its layer's element kept, the style rules that held folded into the node's props, a
|
|
15
|
+
* gate the props decide resolved away, a forwarded value written where it landed, and the content
|
|
16
|
+
* the instance held moved under the layer its slot routed to. What the instance's props no longer
|
|
17
|
+
* decide is gone with the instance; a gate on a runtime state stays.
|
|
18
|
+
*/
|
|
19
|
+
function bodyOf(config, path) {
|
|
20
|
+
return config.resolve("component", path)?.body;
|
|
21
|
+
}
|
|
22
|
+
/** The layer a node renders, or `undefined` for a node that renders something else (a `Slot`). */
|
|
23
|
+
function layerOf(element) {
|
|
24
|
+
return isRef(element) && kindOf(element.__ref) === "layer" ? pathOf(element.__ref) : void 0;
|
|
25
|
+
}
|
|
26
|
+
/** The anatomy a component stores, or the one node a component without one is. */
|
|
27
|
+
function anatomyOf(body) {
|
|
28
|
+
if (body.anatomy && Object.keys(body.anatomy).length > 0) return body.anatomy;
|
|
29
|
+
const [root] = rootLayerOf(body.layers ?? {});
|
|
30
|
+
return { root: { element: { __ref: `layer:${root}` } } };
|
|
31
|
+
}
|
|
32
|
+
/** Whether a rule's condition holds under a prop bag, a ref compared by its leaf. */
|
|
33
|
+
function ruleHolds(when, state) {
|
|
34
|
+
if (!when) return true;
|
|
35
|
+
return Object.entries(when).every(([prop, value]) => String(refLeaf(state[prop])) === String(refLeaf(value)));
|
|
36
|
+
}
|
|
37
|
+
/** The rules in the order their bags apply: the unconditional ones, then by how many props a
|
|
38
|
+
* condition pins, so a narrower rule writes over a wider one the way the stylesheet cascades them. */
|
|
39
|
+
function orderedRules(styles) {
|
|
40
|
+
return Object.values(styles ?? {}).sort((a, b) => Object.keys(a.when ?? {}).length - Object.keys(b.when ?? {}).length);
|
|
41
|
+
}
|
|
42
|
+
/** The prop a forward lands its value under on the target layer. */
|
|
43
|
+
function intoName(into) {
|
|
44
|
+
if (typeof into === "string") return into.split("/").pop() ?? into;
|
|
45
|
+
if (isRef(into)) return into.__ref.split("#props/").pop() ?? "children";
|
|
46
|
+
return "children";
|
|
47
|
+
}
|
|
48
|
+
/** A layer key no layer of the component holds yet: `base` itself, else `base` numbered from 2. */
|
|
49
|
+
function freeLayerKey(layers, base) {
|
|
50
|
+
if (!(base in layers)) return base;
|
|
51
|
+
let n = 2;
|
|
52
|
+
while (`${base}${n}` in layers) n += 1;
|
|
53
|
+
return `${base}${n}`;
|
|
54
|
+
}
|
|
55
|
+
/** A layer key spelled after a component: its leaf, lower-cased at the front (`Badge` → `badge`). */
|
|
56
|
+
function layerKeyFor(componentPath) {
|
|
57
|
+
const leaf = componentPath.split("/").pop() ?? componentPath;
|
|
58
|
+
return `${leaf.charAt(0).toLowerCase()}${leaf.slice(1)}`;
|
|
59
|
+
}
|
|
60
|
+
/** The scalar props of a preview element, which is what an anatomy node's props hold. */
|
|
61
|
+
function scalarProps(props) {
|
|
62
|
+
const out = {};
|
|
63
|
+
for (const [name, value] of Object.entries(props ?? {})) if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") out[name] = value;
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Preview elements as subtree nodes, keyed under `prefix` so they join the baked subtree without
|
|
68
|
+
* colliding with the source's node names. A child that is itself a component stays an instance
|
|
69
|
+
* node; only the element being detached is baked.
|
|
70
|
+
*/
|
|
71
|
+
function previewNodes(elements, prefix, into) {
|
|
72
|
+
return elements.map((element, index) => {
|
|
73
|
+
const key = `${prefix}${index}`;
|
|
74
|
+
const node = {
|
|
75
|
+
element: element.element,
|
|
76
|
+
...element.text !== void 0 ? { text: element.text } : {}
|
|
77
|
+
};
|
|
78
|
+
const props = scalarProps(element.props);
|
|
79
|
+
if (Object.keys(props).length > 0) node.props = props;
|
|
80
|
+
into.set(key, node);
|
|
81
|
+
const children = previewNodes(element.children ?? [], `${key}_`, into);
|
|
82
|
+
if (children.length > 0) node.children = children;
|
|
83
|
+
for (const [prop, value] of Object.entries(element.props ?? {})) {
|
|
84
|
+
if (!isPreviewElement(value)) continue;
|
|
85
|
+
const [held] = previewNodes([value], `${key}_${prop}_`, into);
|
|
86
|
+
if (held) node.children = [...node.children ?? [], held];
|
|
87
|
+
}
|
|
88
|
+
return key;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The instance's component's anatomy under the instance's props: each source node that renders
|
|
93
|
+
* under those props, its layer's element kept, the style rules that held folded into its props, a
|
|
94
|
+
* gate the props decide resolved away, a forwarded value or text written where it landed, and a
|
|
95
|
+
* drilled override kept on its layer. `null` when the component stores nothing or its entry node
|
|
96
|
+
* does not render.
|
|
97
|
+
*/
|
|
98
|
+
function bakeInstance({ config, source, instanceProps, layerProps, filled }) {
|
|
99
|
+
const sourcePath = pathOf(source);
|
|
100
|
+
const owner = config.configFor(source) ?? config;
|
|
101
|
+
const sourceBody = bodyOf(owner, sourcePath);
|
|
102
|
+
if (!sourceBody) return null;
|
|
103
|
+
const sourceLayers = sourceBody.layers ?? {};
|
|
104
|
+
const sourceAnatomy = anatomyOf(sourceBody);
|
|
105
|
+
const state = {
|
|
106
|
+
...declaredPropDefaults(sourceBody.props),
|
|
107
|
+
...sourceBody.defaultProps ?? {},
|
|
108
|
+
...instanceProps
|
|
109
|
+
};
|
|
110
|
+
for (const from of filled) if (state[from] === void 0) state[from] = true;
|
|
111
|
+
const kept = /* @__PURE__ */ new Map();
|
|
112
|
+
for (const [key, sourceNode] of Object.entries(sourceAnatomy)) {
|
|
113
|
+
const shown = resolveVisibility(sourceNode.visible, state);
|
|
114
|
+
if (shown === false) continue;
|
|
115
|
+
const sourceLayer = layerOf(sourceNode.element);
|
|
116
|
+
if (sourceLayer === void 0) continue;
|
|
117
|
+
kept.set(key, {
|
|
118
|
+
element: sourceLayers[sourceLayer] ?? "div",
|
|
119
|
+
...sourceNode.props ? { props: { ...sourceNode.props } } : {},
|
|
120
|
+
...shown === void 0 && sourceNode.visible !== void 0 ? { visible: sourceNode.visible } : {},
|
|
121
|
+
...sourceNode.text !== void 0 ? { text: sourceNode.text } : {}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
for (const [key, sourceNode] of Object.entries(sourceAnatomy)) {
|
|
125
|
+
const inserted = kept.get(key);
|
|
126
|
+
if (!inserted) continue;
|
|
127
|
+
const children = (sourceNode.children ?? []).filter((child) => kept.has(child));
|
|
128
|
+
if (children.length > 0) inserted.children = children;
|
|
129
|
+
}
|
|
130
|
+
const keyOfLayer = (sourceLayer) => {
|
|
131
|
+
for (const [key, sourceNode] of Object.entries(sourceAnatomy)) if (layerOf(sourceNode.element) === sourceLayer && kept.has(key)) return key;
|
|
132
|
+
};
|
|
133
|
+
const nodeOfLayer = (sourceLayer) => {
|
|
134
|
+
const key = keyOfLayer(sourceLayer);
|
|
135
|
+
return key === void 0 ? void 0 : kept.get(key);
|
|
136
|
+
};
|
|
137
|
+
const setProp = (target, name, value) => {
|
|
138
|
+
target.props = {
|
|
139
|
+
...target.props,
|
|
140
|
+
[name]: value
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
for (const rule of orderedRules(sourceBody.styles)) {
|
|
144
|
+
if (!ruleHolds(rule.when, state)) continue;
|
|
145
|
+
for (const [sourceLayer, bag] of Object.entries(rule.layers ?? {})) {
|
|
146
|
+
const target = nodeOfLayer(sourceLayer);
|
|
147
|
+
if (!target) continue;
|
|
148
|
+
for (const [name, value] of Object.entries(bag)) setProp(target, name, value);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const [sourceRoot] = rootLayerOf(sourceLayers);
|
|
152
|
+
const routes = slotTargetsOf(owner, sourcePath);
|
|
153
|
+
const declared = sourceBody.props ?? {};
|
|
154
|
+
for (const [name, value] of Object.entries(instanceProps)) {
|
|
155
|
+
if (value === void 0) continue;
|
|
156
|
+
const decl = declared[name];
|
|
157
|
+
const scalar = typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
158
|
+
if (decl?.type === "forward" && isRef(decl.target) && scalar) {
|
|
159
|
+
const target = nodeOfLayer(pathOf(decl.target.__ref));
|
|
160
|
+
const into = intoName(decl.into);
|
|
161
|
+
if (!target) continue;
|
|
162
|
+
if (into === "children") target.text = String(value);
|
|
163
|
+
else setProp(target, into, value);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (decl?.type === "slot" && typeof value === "string") {
|
|
167
|
+
const target = nodeOfLayer(routes.find((r) => r.from === name)?.layer ?? sourceRoot);
|
|
168
|
+
if (target) target.text = value;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (decl === void 0 || decl.type === "styleProperty") {
|
|
172
|
+
const target = nodeOfLayer(sourceRoot);
|
|
173
|
+
if (target && scalar) setProp(target, name, value);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
for (const [sourceLayer, bag] of Object.entries(layerProps)) {
|
|
177
|
+
const target = nodeOfLayer(sourceLayer);
|
|
178
|
+
if (!target) continue;
|
|
179
|
+
for (const [name, value] of Object.entries(bag)) if (value !== void 0 && value !== null) setProp(target, name, value);
|
|
180
|
+
}
|
|
181
|
+
const entry = entryKeyOf(sourceAnatomy);
|
|
182
|
+
if (!kept.has(entry)) return null;
|
|
183
|
+
return {
|
|
184
|
+
entry,
|
|
185
|
+
nodes: kept,
|
|
186
|
+
keyOfLayer,
|
|
187
|
+
routes,
|
|
188
|
+
sourceRoot
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* The edits that detach the instance the layer `layer` of `path` renders: one `element-insert` of
|
|
193
|
+
* the instance's component's anatomy under the instance's parent at its index, a move for each
|
|
194
|
+
* element the instance held in a slot, and the instance's deletion last. `null` when the layer
|
|
195
|
+
* renders no component, or nothing places it.
|
|
196
|
+
*/
|
|
197
|
+
function detachInstanceEdit({ config, path, layer }) {
|
|
198
|
+
const body = bodyOf(config, path);
|
|
199
|
+
if (!body) return null;
|
|
200
|
+
const layers = body.layers ?? {};
|
|
201
|
+
const element = layers[layer];
|
|
202
|
+
if (!isRef(element) || kindOf(element.__ref) !== "component") return null;
|
|
203
|
+
const anatomy = anatomyOf(body);
|
|
204
|
+
const node = anatomy[layer];
|
|
205
|
+
if (!node) return null;
|
|
206
|
+
let placed;
|
|
207
|
+
for (const [parentKey, parent] of Object.entries(anatomy)) {
|
|
208
|
+
const index = parent.children?.indexOf(layer) ?? -1;
|
|
209
|
+
if (index >= 0) {
|
|
210
|
+
placed = {
|
|
211
|
+
parent: parentKey,
|
|
212
|
+
index
|
|
213
|
+
};
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
for (const [slotProp, fills] of Object.entries(parent.slots ?? {})) {
|
|
217
|
+
const at = fills.indexOf(layer);
|
|
218
|
+
if (at >= 0) {
|
|
219
|
+
placed = {
|
|
220
|
+
parent: parentKey,
|
|
221
|
+
index: at,
|
|
222
|
+
slotProp
|
|
223
|
+
};
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (placed) break;
|
|
228
|
+
}
|
|
229
|
+
if (!placed) return null;
|
|
230
|
+
const instanceProps = { ...node.props ?? {} };
|
|
231
|
+
const layerProps = instanceProps.layerProps ?? {};
|
|
232
|
+
delete instanceProps.layerProps;
|
|
233
|
+
const filled = /* @__PURE__ */ new Set();
|
|
234
|
+
for (const [from, fills] of Object.entries(node.slots ?? {})) if (fills.length > 0) filled.add(from);
|
|
235
|
+
if (node.children?.length) filled.add("children");
|
|
236
|
+
const baked = bakeInstance({
|
|
237
|
+
config,
|
|
238
|
+
source: element.__ref,
|
|
239
|
+
instanceProps,
|
|
240
|
+
layerProps,
|
|
241
|
+
filled
|
|
242
|
+
});
|
|
243
|
+
if (!baked) return null;
|
|
244
|
+
const { entry, nodes: kept, keyOfLayer, routes, sourceRoot } = baked;
|
|
245
|
+
const newKey = freeLayerKey(layers, layer);
|
|
246
|
+
const grafted = (local) => local === entry ? newKey : `${newKey}__${local}`;
|
|
247
|
+
const edits = [{
|
|
248
|
+
kind: "component",
|
|
249
|
+
operation: "element-insert",
|
|
250
|
+
path,
|
|
251
|
+
key: newKey,
|
|
252
|
+
data: {
|
|
253
|
+
parent: placed.parent,
|
|
254
|
+
index: placed.index,
|
|
255
|
+
...placed.slotProp ? { slotProp: placed.slotProp } : {},
|
|
256
|
+
subtree: {
|
|
257
|
+
root: entry,
|
|
258
|
+
elements: Object.fromEntries(kept)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}];
|
|
262
|
+
const moveTo = (fills, from) => {
|
|
263
|
+
const local = keyOfLayer(routes.find((r) => r.from === from)?.layer ?? sourceRoot) ?? entry;
|
|
264
|
+
for (const fill of fills) edits.push({
|
|
265
|
+
kind: "component",
|
|
266
|
+
operation: "element-move",
|
|
267
|
+
path,
|
|
268
|
+
key: fill,
|
|
269
|
+
data: { parent: grafted(local) }
|
|
270
|
+
});
|
|
271
|
+
};
|
|
272
|
+
moveTo(node.children ?? [], "children");
|
|
273
|
+
for (const [from, fills] of Object.entries(node.slots ?? {})) moveTo(fills, from);
|
|
274
|
+
edits.push({
|
|
275
|
+
kind: "component",
|
|
276
|
+
operation: "element-delete",
|
|
277
|
+
path,
|
|
278
|
+
key: layer
|
|
279
|
+
});
|
|
280
|
+
return edits;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* The edits that detach one element of a slot's content: the element the address names in the
|
|
284
|
+
* content the cell shows becomes anatomy under the layer its slot routes to, baked at the props
|
|
285
|
+
* the element sets, and the content loses it. Its own children and the elements it hands to its
|
|
286
|
+
* slots come along as instance nodes under the layers the element's component routes them to.
|
|
287
|
+
* `null` for an address inside another element, or one naming no component.
|
|
288
|
+
*/
|
|
289
|
+
function detachContentEdit({ config, path, cell, address }) {
|
|
290
|
+
const body = bodyOf(config, path);
|
|
291
|
+
if (!body) return null;
|
|
292
|
+
const slot = Component.cell({
|
|
293
|
+
config,
|
|
294
|
+
path,
|
|
295
|
+
cell
|
|
296
|
+
}).slot(address.prop);
|
|
297
|
+
const held = slot?.content[address.index];
|
|
298
|
+
const element = held ? elementAt(held, address.path) : void 0;
|
|
299
|
+
if (!slot || !element) return null;
|
|
300
|
+
if (!isRef(element.element) || kindOf(element.element.__ref) !== "component") return null;
|
|
301
|
+
if (address.path.length > 0) {
|
|
302
|
+
if (detachContentRefusal({
|
|
303
|
+
config,
|
|
304
|
+
path,
|
|
305
|
+
cell,
|
|
306
|
+
address
|
|
307
|
+
})) return null;
|
|
308
|
+
const baked = detachedPreviewElement({
|
|
309
|
+
config,
|
|
310
|
+
source: element.element.__ref,
|
|
311
|
+
element
|
|
312
|
+
});
|
|
313
|
+
return baked ? slot.replaceAt({
|
|
314
|
+
address,
|
|
315
|
+
element: baked
|
|
316
|
+
}) : null;
|
|
317
|
+
}
|
|
318
|
+
const anatomy = anatomyOf(body);
|
|
319
|
+
const hostKey = Object.keys(anatomy).find((key) => layerOf(anatomy[key]?.element) === slot.layer);
|
|
320
|
+
if (!hostKey) return null;
|
|
321
|
+
const instanceProps = scalarProps(element.props);
|
|
322
|
+
if (element.text !== void 0) instanceProps.children = element.text;
|
|
323
|
+
const filled = /* @__PURE__ */ new Set();
|
|
324
|
+
if (element.children?.length) filled.add("children");
|
|
325
|
+
for (const [prop, value] of Object.entries(element.props ?? {})) if (isPreviewElement(value)) filled.add(prop);
|
|
326
|
+
const baked = bakeInstance({
|
|
327
|
+
config,
|
|
328
|
+
source: element.element.__ref,
|
|
329
|
+
instanceProps,
|
|
330
|
+
layerProps: {},
|
|
331
|
+
filled
|
|
332
|
+
});
|
|
333
|
+
if (!baked) return null;
|
|
334
|
+
const { entry, nodes, keyOfLayer, routes, sourceRoot } = baked;
|
|
335
|
+
const attach = (held, from) => {
|
|
336
|
+
if (held.length === 0) return;
|
|
337
|
+
const local = keyOfLayer(routes.find((r) => r.from === from)?.layer ?? sourceRoot) ?? entry;
|
|
338
|
+
const target = nodes.get(local);
|
|
339
|
+
if (!target) return;
|
|
340
|
+
const keys = previewNodes(held, `${from}_`, nodes);
|
|
341
|
+
target.children = [...target.children ?? [], ...keys];
|
|
342
|
+
};
|
|
343
|
+
attach(element.children ?? [], "children");
|
|
344
|
+
for (const [prop, value] of Object.entries(element.props ?? {})) if (isPreviewElement(value)) attach([value], prop);
|
|
345
|
+
return [{
|
|
346
|
+
kind: "component",
|
|
347
|
+
operation: "element-insert",
|
|
348
|
+
path,
|
|
349
|
+
key: freeLayerKey(body.layers ?? {}, layerKeyFor(pathOf(element.element.__ref))),
|
|
350
|
+
data: {
|
|
351
|
+
parent: hostKey,
|
|
352
|
+
index: anatomy[hostKey]?.children?.length ?? 0,
|
|
353
|
+
subtree: {
|
|
354
|
+
root: entry,
|
|
355
|
+
elements: Object.fromEntries(nodes)
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}, ...slot.removeAt(address)];
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* The anatomy an instance of `source` renders under `props`, as the elements a page document
|
|
362
|
+
* stores: the layer's element as the spec `type` (a component as its `component:` marker), the baked
|
|
363
|
+
* bag and any text as props, the kept edges as children. What a design-canvas Detach splices in
|
|
364
|
+
* place of the instance; the content the instance held goes under `slotHosts[<prop>]`.
|
|
365
|
+
*/
|
|
366
|
+
function detachedElementSpec({ config, source, props, filled = /* @__PURE__ */ new Set() }) {
|
|
367
|
+
const instanceProps = { ...props };
|
|
368
|
+
const layerProps = instanceProps.layerProps ?? {};
|
|
369
|
+
delete instanceProps.layerProps;
|
|
370
|
+
const baked = bakeInstance({
|
|
371
|
+
config,
|
|
372
|
+
source,
|
|
373
|
+
instanceProps,
|
|
374
|
+
layerProps,
|
|
375
|
+
filled
|
|
376
|
+
});
|
|
377
|
+
if (!baked) return null;
|
|
378
|
+
const elements = {};
|
|
379
|
+
for (const [key, node] of baked.nodes) {
|
|
380
|
+
const bag = { ...node.props };
|
|
381
|
+
if (node.text !== void 0) bag.children = node.text;
|
|
382
|
+
elements[key] = {
|
|
383
|
+
type: isRef(node.element) ? node.element.__ref : String(node.element),
|
|
384
|
+
...Object.keys(bag).length > 0 ? { props: bag } : {},
|
|
385
|
+
...node.children?.length ? { children: [...node.children] } : {}
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
const slotHosts = {};
|
|
389
|
+
for (const route of baked.routes) slotHosts[route.from] = baked.keyOfLayer(route.layer) ?? baked.entry;
|
|
390
|
+
if (slotHosts.children === void 0) slotHosts.children = baked.keyOfLayer(baked.sourceRoot) ?? baked.entry;
|
|
391
|
+
return {
|
|
392
|
+
root: baked.entry,
|
|
393
|
+
elements,
|
|
394
|
+
slotHosts
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
/** The element an address's path reaches inside `root`: a number steps into a child, a string into an
|
|
398
|
+
* element-valued prop. */
|
|
399
|
+
function elementAt(root, steps) {
|
|
400
|
+
let current = root;
|
|
401
|
+
for (const step of steps) {
|
|
402
|
+
if (!current) return void 0;
|
|
403
|
+
const next = typeof step === "number" ? current.children?.[step] : current.props?.[step];
|
|
404
|
+
current = isPreviewElement(next) ? next : void 0;
|
|
405
|
+
}
|
|
406
|
+
return current;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* A preview element that renders a component, as the tree its component is made of, baked at the
|
|
410
|
+
* props the element sets: each kept source node becomes an element (a component as its ref, a tag
|
|
411
|
+
* as itself), the baked bag as its props, its text as text, its edges as children. The children
|
|
412
|
+
* and element-valued props the original held ride along under the elements the component routes
|
|
413
|
+
* them to. `null` when the component stores nothing.
|
|
414
|
+
*/
|
|
415
|
+
function detachedPreviewElement({ config, source, element }) {
|
|
416
|
+
const instanceProps = scalarProps(element.props);
|
|
417
|
+
if (element.text !== void 0) instanceProps.children = element.text;
|
|
418
|
+
const filled = /* @__PURE__ */ new Set();
|
|
419
|
+
if (element.children?.length) filled.add("children");
|
|
420
|
+
for (const [prop, value] of Object.entries(element.props ?? {})) if (isPreviewElement(value)) filled.add(prop);
|
|
421
|
+
const baked = bakeInstance({
|
|
422
|
+
config,
|
|
423
|
+
source,
|
|
424
|
+
instanceProps,
|
|
425
|
+
layerProps: {},
|
|
426
|
+
filled
|
|
427
|
+
});
|
|
428
|
+
if (!baked) return null;
|
|
429
|
+
const built = /* @__PURE__ */ new Map();
|
|
430
|
+
const build = (key) => {
|
|
431
|
+
const hit = built.get(key);
|
|
432
|
+
if (hit) return hit;
|
|
433
|
+
const node = baked.nodes.get(key);
|
|
434
|
+
if (!node) return void 0;
|
|
435
|
+
const out = { element: isRef(node.element) ? { __ref: node.element.__ref } : String(node.element) };
|
|
436
|
+
const props = {};
|
|
437
|
+
for (const [name, value] of Object.entries(node.props ?? {})) if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || isPreviewElement(value) || isRef(value)) props[name] = value;
|
|
438
|
+
if (Object.keys(props).length > 0) out.props = props;
|
|
439
|
+
if (node.text !== void 0) out.text = node.text;
|
|
440
|
+
const children = (node.children ?? []).map(build).filter((child) => child !== void 0);
|
|
441
|
+
if (children.length > 0) out.children = children;
|
|
442
|
+
built.set(key, out);
|
|
443
|
+
return out;
|
|
444
|
+
};
|
|
445
|
+
const root = build(baked.entry);
|
|
446
|
+
if (!root) return null;
|
|
447
|
+
const attach = (held, from) => {
|
|
448
|
+
if (held.length === 0) return;
|
|
449
|
+
const route = baked.routes.find((r) => r.from === from);
|
|
450
|
+
const local = baked.keyOfLayer(route?.layer ?? baked.sourceRoot) ?? baked.entry;
|
|
451
|
+
const target = built.get(local);
|
|
452
|
+
if (!target) return;
|
|
453
|
+
target.children = [...target.children ?? [], ...held];
|
|
454
|
+
};
|
|
455
|
+
attach(element.children ?? [], "children");
|
|
456
|
+
for (const [prop, value] of Object.entries(element.props ?? {})) if (isPreviewElement(value)) attach([value], prop);
|
|
457
|
+
return root;
|
|
458
|
+
}
|
|
459
|
+
/** The root element a detached instance of `source` would render, under `props`. */
|
|
460
|
+
function detachedRootElement({ config, source, props }) {
|
|
461
|
+
const baked = bakeInstance({
|
|
462
|
+
config,
|
|
463
|
+
source,
|
|
464
|
+
instanceProps: props,
|
|
465
|
+
layerProps: {},
|
|
466
|
+
filled: /* @__PURE__ */ new Set()
|
|
467
|
+
});
|
|
468
|
+
const root = baked?.nodes.get(baked.entry)?.element;
|
|
469
|
+
if (typeof root === "string") return root;
|
|
470
|
+
return isRef(root) ? root : void 0;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Why the element an address names inside another element cannot detach in place: the slot of the
|
|
474
|
+
* parent's component that holds it refuses what the element becomes. `null` when it can, or when
|
|
475
|
+
* the address is not inside another element.
|
|
476
|
+
*/
|
|
477
|
+
function detachContentRefusal({ config, path, cell, address }) {
|
|
478
|
+
if (address.path.length === 0) return null;
|
|
479
|
+
const held = Component.cell({
|
|
480
|
+
config,
|
|
481
|
+
path,
|
|
482
|
+
cell
|
|
483
|
+
}).slot(address.prop)?.content[address.index];
|
|
484
|
+
if (!held) return null;
|
|
485
|
+
const parentSteps = address.path.slice(0, -1);
|
|
486
|
+
const last = address.path[address.path.length - 1];
|
|
487
|
+
const parent = elementAt(held, parentSteps);
|
|
488
|
+
const element = parent ? elementAt(parent, [last]) : void 0;
|
|
489
|
+
if (!parent || !element) return null;
|
|
490
|
+
if (!isRef(parent.element) || kindOf(parent.element.__ref) !== "component") return null;
|
|
491
|
+
if (!isRef(element.element) || kindOf(element.element.__ref) !== "component") return null;
|
|
492
|
+
const slot = Component.slot({
|
|
493
|
+
config,
|
|
494
|
+
path: pathOf(parent.element.__ref),
|
|
495
|
+
slot: typeof last === "number" ? "children" : last
|
|
496
|
+
});
|
|
497
|
+
if (!slot) return null;
|
|
498
|
+
const props = scalarProps(element.props);
|
|
499
|
+
const becomes = detachedRootElement({
|
|
500
|
+
config,
|
|
501
|
+
source: element.element.__ref,
|
|
502
|
+
props
|
|
503
|
+
});
|
|
504
|
+
if (becomes === void 0 || slot.takes(becomes)) return null;
|
|
505
|
+
return {
|
|
506
|
+
slot,
|
|
507
|
+
becomes
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
//#endregion
|
|
511
|
+
export { detachContentEdit, detachContentRefusal, detachInstanceEdit, detachedElementSpec, detachedPreviewElement, detachedRootElement };
|
|
@@ -1,34 +1,22 @@
|
|
|
1
1
|
import { Config } from "../framework/Config.js";
|
|
2
|
-
import { SlotTarget } from "../framework/render-spec.js";
|
|
3
2
|
import { Spec } from "@json-render/core";
|
|
4
3
|
|
|
5
4
|
//#region src/spec/empty-node-slots.d.ts
|
|
6
|
-
/**
|
|
7
|
-
* The component's named node slots — `slotTargetsOf` minus text slots and
|
|
8
|
-
* minus `children`. `children` is the open content hole every container has
|
|
9
|
-
* (a structural insert, not a named fill), and a text slot is an inline-edit
|
|
10
|
-
* surface, not a place a node goes.
|
|
11
|
-
*
|
|
12
|
-
* Text-ness is checked on BOTH ends of the route: the component's own decl
|
|
13
|
-
* (`title: { type: 'slot', text: true }`) and the prop the route lands on — a
|
|
14
|
-
* `forward` into `Text#children` is typed in place, not filled with a dropped
|
|
15
|
-
* node, and only the far end says so. A route whose host component doesn't
|
|
16
|
-
* declare the landing prop is dropped too: the fill op would refuse it, so it
|
|
17
|
-
* must never be offered as a target.
|
|
18
|
-
*/
|
|
19
|
-
declare function namedSlotTargets(config: Config, componentPath: string): SlotTarget[];
|
|
20
5
|
/**
|
|
21
6
|
* Materialize every EMPTY layer-bound node slot of `spec`'s root as a `Slot`
|
|
22
7
|
* element named in the root's `slots` record, so an unfilled slot is a stable
|
|
23
8
|
* element in the spec rather than an absent entry — the studio canvas needs a
|
|
24
9
|
* drop target, the layers panel a row, and the inspector an "Empty" state.
|
|
25
|
-
* Slots the spec already fills are left as-is
|
|
26
|
-
*
|
|
10
|
+
* Slots the spec already fills are left as-is, whether the fill is a `slots`
|
|
11
|
+
* entry or a value authored on the prop itself (a string title). Rendering
|
|
12
|
+
* routes a `slots` entry onto the prop it names, so a placeholder injected over
|
|
13
|
+
* an authored value would replace it for as long as the placeholder is there.
|
|
14
|
+
* Returns `spec` unchanged when there's nothing to inject (no node slots, or
|
|
15
|
+
* all of them filled).
|
|
27
16
|
*
|
|
28
|
-
* `slotNames` is the component's named node slots —
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* not one), and only the config can answer that.
|
|
17
|
+
* `slotNames` is the component's named node slots — `Slot.named`'s `from` names. Callers pass
|
|
18
|
+
* names rather than a props record because which props are node slots is route-dependent (a
|
|
19
|
+
* `forward` into a text prop is not one), and only the config can answer that.
|
|
32
20
|
*/
|
|
33
21
|
declare function withEmptyNodeSlots(spec: Spec, slotNames: readonly string[]): Spec;
|
|
34
22
|
/**
|
|
@@ -84,7 +72,7 @@ declare function withContainerChildSlots(spec: Spec, acceptsChildNodes: AcceptsC
|
|
|
84
72
|
/**
|
|
85
73
|
* Compose both slot injections into the canvas's render view: layer-bound node
|
|
86
74
|
* slots on a component instance's root (`withEmptyNodeSlots`, keyed by the
|
|
87
|
-
* component's `
|
|
75
|
+
* component's `Slot.named`) plus a trailing insertion `Slot` in every container,
|
|
88
76
|
* tree-wide (`withContainerChildSlots`). This is the spec the studio canvas
|
|
89
77
|
* renders so empty slots show as drop targets — it is NEVER persisted. Stored
|
|
90
78
|
* specs stay slot-free; `stripInjectedSlots` is the inverse, applied at every
|
|
@@ -111,4 +99,4 @@ declare function withRenderSlots(spec: Spec, slotNames: readonly string[] | null
|
|
|
111
99
|
*/
|
|
112
100
|
declare function stripInjectedSlots(spec: Spec): Spec;
|
|
113
101
|
//#endregion
|
|
114
|
-
export { AcceptsChildNodes, ChildNodesClassification, ClassifyChildNodes, buildAcceptsChildNodes, buildClassifyChildNodes,
|
|
102
|
+
export { AcceptsChildNodes, ChildNodesClassification, ClassifyChildNodes, buildAcceptsChildNodes, buildClassifyChildNodes, stripInjectedSlots, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots };
|
|
@@ -1,50 +1,22 @@
|
|
|
1
|
-
import { pathOf } from "../framework/utils/refs.js";
|
|
2
1
|
import { childrenPolicy } from "../entities/system/element.js";
|
|
3
|
-
import { resolveComponentProps } from "../framework/projections.js";
|
|
4
|
-
import { slotTargetsOf } from "../framework/render-spec.js";
|
|
5
2
|
import { Component } from "../entities/system/Component.js";
|
|
6
3
|
//#region src/spec/empty-node-slots.ts
|
|
7
4
|
const SLOT_TYPE = "Slot";
|
|
8
5
|
/**
|
|
9
|
-
* The component's named node slots — `slotTargetsOf` minus text slots and
|
|
10
|
-
* minus `children`. `children` is the open content hole every container has
|
|
11
|
-
* (a structural insert, not a named fill), and a text slot is an inline-edit
|
|
12
|
-
* surface, not a place a node goes.
|
|
13
|
-
*
|
|
14
|
-
* Text-ness is checked on BOTH ends of the route: the component's own decl
|
|
15
|
-
* (`title: { type: 'slot', text: true }`) and the prop the route lands on — a
|
|
16
|
-
* `forward` into `Text#children` is typed in place, not filled with a dropped
|
|
17
|
-
* node, and only the far end says so. A route whose host component doesn't
|
|
18
|
-
* declare the landing prop is dropped too: the fill op would refuse it, so it
|
|
19
|
-
* must never be offered as a target.
|
|
20
|
-
*/
|
|
21
|
-
function namedSlotTargets(config, componentPath) {
|
|
22
|
-
const component = Component.get(config, componentPath);
|
|
23
|
-
const props = component?.props;
|
|
24
|
-
const layers = component?.layers;
|
|
25
|
-
return slotTargetsOf(config, componentPath).filter((target) => {
|
|
26
|
-
if (target.from === "children") return false;
|
|
27
|
-
const declared = props?.[target.from];
|
|
28
|
-
if (declared?.type === "slot" && declared.text === true) return false;
|
|
29
|
-
const hostRef = layers?.[target.layer];
|
|
30
|
-
if (!hostRef || !Component.get(config, hostRef)) return true;
|
|
31
|
-
const into = resolveComponentProps(config.configFor(hostRef) ?? config, pathOf(hostRef))[target.into];
|
|
32
|
-
if (!into) return false;
|
|
33
|
-
return !(into.type === "slot" && into.text === true);
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
6
|
* Materialize every EMPTY layer-bound node slot of `spec`'s root as a `Slot`
|
|
38
7
|
* element named in the root's `slots` record, so an unfilled slot is a stable
|
|
39
8
|
* element in the spec rather than an absent entry — the studio canvas needs a
|
|
40
9
|
* drop target, the layers panel a row, and the inspector an "Empty" state.
|
|
41
|
-
* Slots the spec already fills are left as-is
|
|
42
|
-
*
|
|
10
|
+
* Slots the spec already fills are left as-is, whether the fill is a `slots`
|
|
11
|
+
* entry or a value authored on the prop itself (a string title). Rendering
|
|
12
|
+
* routes a `slots` entry onto the prop it names, so a placeholder injected over
|
|
13
|
+
* an authored value would replace it for as long as the placeholder is there.
|
|
14
|
+
* Returns `spec` unchanged when there's nothing to inject (no node slots, or
|
|
15
|
+
* all of them filled).
|
|
43
16
|
*
|
|
44
|
-
* `slotNames` is the component's named node slots —
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* not one), and only the config can answer that.
|
|
17
|
+
* `slotNames` is the component's named node slots — `Slot.named`'s `from` names. Callers pass
|
|
18
|
+
* names rather than a props record because which props are node slots is route-dependent (a
|
|
19
|
+
* `forward` into a text prop is not one), and only the config can answer that.
|
|
48
20
|
*/
|
|
49
21
|
function withEmptyNodeSlots(spec, slotNames) {
|
|
50
22
|
const names = slotNames;
|
|
@@ -57,6 +29,7 @@ function withEmptyNodeSlots(spec, slotNames) {
|
|
|
57
29
|
let injected = false;
|
|
58
30
|
for (const name of names) {
|
|
59
31
|
if (root.slots?.[name] !== void 0) continue;
|
|
32
|
+
if (root.props?.[name] !== void 0) continue;
|
|
60
33
|
const key = `${rootKey}__${name}__slot`;
|
|
61
34
|
elements[key] = {
|
|
62
35
|
type: SLOT_TYPE,
|
|
@@ -143,7 +116,7 @@ function withContainerChildSlots(spec, acceptsChildNodes) {
|
|
|
143
116
|
/**
|
|
144
117
|
* Compose both slot injections into the canvas's render view: layer-bound node
|
|
145
118
|
* slots on a component instance's root (`withEmptyNodeSlots`, keyed by the
|
|
146
|
-
* component's `
|
|
119
|
+
* component's `Slot.named`) plus a trailing insertion `Slot` in every container,
|
|
147
120
|
* tree-wide (`withContainerChildSlots`). This is the spec the studio canvas
|
|
148
121
|
* renders so empty slots show as drop targets — it is NEVER persisted. Stored
|
|
149
122
|
* specs stay slot-free; `stripInjectedSlots` is the inverse, applied at every
|
|
@@ -192,4 +165,4 @@ function stripInjectedSlots(spec) {
|
|
|
192
165
|
return next;
|
|
193
166
|
}
|
|
194
167
|
//#endregion
|
|
195
|
-
export { buildAcceptsChildNodes, buildClassifyChildNodes,
|
|
168
|
+
export { buildAcceptsChildNodes, buildClassifyChildNodes, stripInjectedSlots, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots };
|
package/dist/spec/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { GraftableElement, GraftableSpec, PreviewElement, PreviewRef, PreviewSpecNode, PreviewValue, graftPreviewElements, graftSlotContent, hasPreviewElementDeep, isComponentRef, isPreviewElement, isPreviewRef, previewElementNodes, previewElementType } from "./preview-elements.js";
|
|
2
|
+
import { Slot, SlotCell } from "./slot.js";
|
|
3
|
+
import { Cell, CellSlot } from "./cell.js";
|
|
3
4
|
import { DocumentRename, RewritableSpec, SpecRewrite, documentRenamesFromPatches, rewriteSpecRefs } from "./rewrite-refs.js";
|
|
4
5
|
import { applyForcedModifiers, collectForcedModifierProps } from "./apply-forced-modifiers.js";
|
|
6
|
+
import { AcceptsChildNodes, ChildNodesClassification, ClassifyChildNodes, buildAcceptsChildNodes, buildClassifyChildNodes, stripInjectedSlots, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots } from "./empty-node-slots.js";
|
|
5
7
|
import { AssetComponentMap, AssetJsxForm, AssetNodeResolver, assetJsxForm, jsxIdentifier, parseAssetType } from "./asset-jsx.js";
|
|
6
8
|
import { CollapseTextLabelsResult, CollapsedTextLabel, collapseWrappedTextLabels } from "./collapse-text-labels.js";
|
|
7
9
|
import { JsxToSpecOptions, UNRESOLVED, hasUnresolvedProps, jsxToSpec, stripUnresolvedProps } from "./jsxToSpec.js";
|
|
@@ -26,4 +28,4 @@ import { SpecToJsxOptions, SpecToJsxResult, specToJsx } from "./specToJsx.js";
|
|
|
26
28
|
* element, without changing the rendered layer hierarchy. */
|
|
27
29
|
declare const SYNTHETIC_TEXT_SLOT_PROP = "data-uds-synthetic-text-slot";
|
|
28
30
|
//#endregion
|
|
29
|
-
export { type AcceptsChildNodes, type AssetComponentMap, type AssetJsxForm, type AssetNodeResolver, type ChildNodesClassification, type ClassifyChildNodes, type CollapseTextLabelsResult, type CollapsedTextLabel, type DocumentRename, type GraftableElement, type GraftableSpec, type JsxToSpecOptions, type PreviewElement, type PreviewSpecNode, type PreviewValue, type RewritableSpec, SYNTHETIC_TEXT_SLOT_PROP, type SpecRewrite, type SpecToJsxOptions, type SpecToJsxResult, UNRESOLVED, applyForcedModifiers, assetJsxForm, buildAcceptsChildNodes, buildClassifyChildNodes, collapseWrappedTextLabels, collectForcedModifierProps, documentRenamesFromPatches, graftPreviewElements, hasPreviewElementDeep, hasUnresolvedProps, isPreviewElement, jsxIdentifier, jsxToSpec,
|
|
31
|
+
export { type AcceptsChildNodes, type AssetComponentMap, type AssetJsxForm, type AssetNodeResolver, type Cell, type CellSlot, type ChildNodesClassification, type ClassifyChildNodes, type CollapseTextLabelsResult, type CollapsedTextLabel, type DocumentRename, type GraftableElement, type GraftableSpec, type JsxToSpecOptions, type PreviewElement, type PreviewRef, type PreviewSpecNode, type PreviewValue, type RewritableSpec, SYNTHETIC_TEXT_SLOT_PROP, type Slot, type SlotCell, type SpecRewrite, type SpecToJsxOptions, type SpecToJsxResult, UNRESOLVED, applyForcedModifiers, assetJsxForm, buildAcceptsChildNodes, buildClassifyChildNodes, collapseWrappedTextLabels, collectForcedModifierProps, documentRenamesFromPatches, graftPreviewElements, graftSlotContent, hasPreviewElementDeep, hasUnresolvedProps, isComponentRef, isPreviewElement, isPreviewRef, jsxIdentifier, jsxToSpec, parseAssetType, previewElementNodes, previewElementType, rewriteSpecRefs, specToJsx, stripInjectedSlots, stripUnresolvedProps, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots };
|