@saltcorn/builder 1.7.0-alpha.1 → 1.7.0-beta.1
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/builder_bundle.js +86 -4207
- package/package.json +3 -2
- package/src/components/Builder.js +41 -5
- package/src/components/Library.js +272 -78
- package/src/components/RenderNode.js +53 -13
- package/src/components/Toolbox.js +74 -0
- package/src/components/elements/LibraryInstance.js +83 -0
- package/src/components/elements/LibrarySlotInstance.js +214 -0
- package/src/components/elements/MessageForm.js +101 -0
- package/src/components/elements/MessageList.js +167 -0
- package/src/components/elements/MonacoEditor.js +39 -27
- package/src/components/storage.js +192 -5
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @category saltcorn-builder
|
|
3
|
+
* @module components/elements/MessageList
|
|
4
|
+
* @subcategory components / elements
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { useContext } from "react";
|
|
8
|
+
import useTranslation from "../../hooks/useTranslation";
|
|
9
|
+
import optionsCtx from "../context";
|
|
10
|
+
import { useNode } from "@craftjs/core";
|
|
11
|
+
import { setAPropGen, Accordion, SettingsRow } from "./utils";
|
|
12
|
+
|
|
13
|
+
export /**
|
|
14
|
+
* The list of messages in a Room view. The messages themselves are rendered by
|
|
15
|
+
* the room's message show view, so this stands in for them on the canvas.
|
|
16
|
+
*
|
|
17
|
+
* @param {object} props
|
|
18
|
+
* @param {string} props.height
|
|
19
|
+
* @returns {div}
|
|
20
|
+
* @namespace
|
|
21
|
+
* @category saltcorn-builder
|
|
22
|
+
* @subcategory components
|
|
23
|
+
*/
|
|
24
|
+
const MessageList = ({ height }) => {
|
|
25
|
+
const { t } = useTranslation();
|
|
26
|
+
const {
|
|
27
|
+
selected,
|
|
28
|
+
connectors: { connect, drag },
|
|
29
|
+
} = useNode((node) => ({ selected: node.events.selected }));
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div
|
|
33
|
+
className={`sc-room-msglist-preview ${selected ? "selected-node" : ""}`}
|
|
34
|
+
ref={(dom) => connect(drag(dom))}
|
|
35
|
+
style={{
|
|
36
|
+
border: "1px dashed #adb5bd",
|
|
37
|
+
borderRadius: "0.25rem",
|
|
38
|
+
padding: "0.5rem",
|
|
39
|
+
minHeight: "5rem",
|
|
40
|
+
maxHeight: height || undefined,
|
|
41
|
+
overflowY: height ? "auto" : undefined,
|
|
42
|
+
}}
|
|
43
|
+
>
|
|
44
|
+
<div className="text-muted small mb-1">
|
|
45
|
+
{t("Messages")}
|
|
46
|
+
{height ? ` (${t("scrolls")}: ${height})` : ""}
|
|
47
|
+
</div>
|
|
48
|
+
<div className="mb-1">{t("An earlier message")}</div>
|
|
49
|
+
<div className="mb-1">{t("A reply to it")}</div>
|
|
50
|
+
<div>{t("The most recent message")}</div>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export /**
|
|
56
|
+
* @returns {div}
|
|
57
|
+
* @namespace
|
|
58
|
+
* @category saltcorn-builder
|
|
59
|
+
* @subcategory components
|
|
60
|
+
*/
|
|
61
|
+
const MessageListSettings = () => {
|
|
62
|
+
const { t } = useTranslation();
|
|
63
|
+
const options = useContext(optionsCtx);
|
|
64
|
+
const node = useNode((node) => ({
|
|
65
|
+
height: node.data.props.height,
|
|
66
|
+
currentSettingsTab: node.data.props.currentSettingsTab,
|
|
67
|
+
animateName: node.data.props.animateName,
|
|
68
|
+
animateDelay: node.data.props.animateDelay,
|
|
69
|
+
animateDuration: node.data.props.animateDuration,
|
|
70
|
+
animateInitialHide: node.data.props.animateInitialHide,
|
|
71
|
+
}));
|
|
72
|
+
const {
|
|
73
|
+
actions: { setProp },
|
|
74
|
+
height,
|
|
75
|
+
currentSettingsTab,
|
|
76
|
+
} = node;
|
|
77
|
+
const setAProp = setAPropGen(setProp);
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<Accordion
|
|
81
|
+
value={currentSettingsTab}
|
|
82
|
+
onChange={(ix) => setProp((prop) => (prop.currentSettingsTab = ix))}
|
|
83
|
+
>
|
|
84
|
+
<div accordiontitle={t("Messages")} className="w-100">
|
|
85
|
+
<label>{t("Height")}</label>
|
|
86
|
+
<input
|
|
87
|
+
type="text"
|
|
88
|
+
className="form-control"
|
|
89
|
+
value={height || ""}
|
|
90
|
+
spellCheck={false}
|
|
91
|
+
onChange={setAProp("height")}
|
|
92
|
+
/>
|
|
93
|
+
<small className="text-muted">
|
|
94
|
+
{t(
|
|
95
|
+
"A CSS length, for example 400px or 60vh. The list scrolls within this height. Leave blank to grow with the page"
|
|
96
|
+
)}
|
|
97
|
+
</small>
|
|
98
|
+
</div>
|
|
99
|
+
<table className="w-100" accordiontitle={t("Animate")}>
|
|
100
|
+
<tbody>
|
|
101
|
+
<SettingsRow
|
|
102
|
+
field={{
|
|
103
|
+
name: "animateName",
|
|
104
|
+
label: t("Animation"),
|
|
105
|
+
type: "select",
|
|
106
|
+
options: ["None", ...(options.keyframes || [])],
|
|
107
|
+
}}
|
|
108
|
+
node={node}
|
|
109
|
+
setProp={setProp}
|
|
110
|
+
/>
|
|
111
|
+
<SettingsRow
|
|
112
|
+
field={{
|
|
113
|
+
name: "animateDuration",
|
|
114
|
+
label: t("Duration (s)"),
|
|
115
|
+
type: "Float",
|
|
116
|
+
}}
|
|
117
|
+
node={node}
|
|
118
|
+
setProp={setProp}
|
|
119
|
+
/>
|
|
120
|
+
<SettingsRow
|
|
121
|
+
field={{
|
|
122
|
+
name: "animateDelay",
|
|
123
|
+
label: t("Delay (s)"),
|
|
124
|
+
type: "Float",
|
|
125
|
+
}}
|
|
126
|
+
node={node}
|
|
127
|
+
setProp={setProp}
|
|
128
|
+
/>
|
|
129
|
+
<SettingsRow
|
|
130
|
+
field={{
|
|
131
|
+
name: "animateInitialHide",
|
|
132
|
+
label: t("Initially hidden"),
|
|
133
|
+
type: "Bool",
|
|
134
|
+
}}
|
|
135
|
+
node={node}
|
|
136
|
+
setProp={setProp}
|
|
137
|
+
/>
|
|
138
|
+
</tbody>
|
|
139
|
+
</table>
|
|
140
|
+
</Accordion>
|
|
141
|
+
);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @type {object}
|
|
146
|
+
*/
|
|
147
|
+
MessageList.craft = {
|
|
148
|
+
displayName: "MessageList",
|
|
149
|
+
props: {
|
|
150
|
+
height: "",
|
|
151
|
+
animateName: "None",
|
|
152
|
+
animateDuration: "",
|
|
153
|
+
animateDelay: "",
|
|
154
|
+
animateInitialHide: false,
|
|
155
|
+
},
|
|
156
|
+
related: {
|
|
157
|
+
settings: MessageListSettings,
|
|
158
|
+
segment_type: "message_list",
|
|
159
|
+
fields: [
|
|
160
|
+
{ name: "height" },
|
|
161
|
+
{ name: "animateName" },
|
|
162
|
+
{ name: "animateDuration" },
|
|
163
|
+
{ name: "animateDelay" },
|
|
164
|
+
{ name: "animateInitialHide" },
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
};
|
|
@@ -138,42 +138,49 @@ export const SingleLineEditor = React.forwardRef(
|
|
|
138
138
|
: value;
|
|
139
139
|
|
|
140
140
|
return (
|
|
141
|
-
<div ref={ref} className="form-control
|
|
141
|
+
<div ref={ref} className="form-control monaco-input monaco-input-singleline">
|
|
142
142
|
{isEmpty && !activePrefix && placeholder && (
|
|
143
143
|
<div style={{
|
|
144
144
|
position: "absolute",
|
|
145
|
-
top: "
|
|
146
|
-
left: "
|
|
145
|
+
top: "0.375rem",
|
|
146
|
+
left: "10px",
|
|
147
|
+
right: "10px",
|
|
148
|
+
bottom: "0.375rem",
|
|
149
|
+
display: "flex",
|
|
150
|
+
alignItems: "center",
|
|
147
151
|
color: "#999",
|
|
148
152
|
pointerEvents: "none",
|
|
149
153
|
zIndex: 1,
|
|
150
154
|
fontSize: "14px",
|
|
151
155
|
whiteSpace: "nowrap",
|
|
156
|
+
overflow: "hidden",
|
|
152
157
|
}}>
|
|
153
158
|
{placeholder}
|
|
154
159
|
</div>
|
|
155
160
|
)}
|
|
156
|
-
<
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
161
|
+
<div className="monaco-input-editor">
|
|
162
|
+
<Editor
|
|
163
|
+
className={className || ""}
|
|
164
|
+
height="100%"
|
|
165
|
+
value={editorValue}
|
|
166
|
+
onChange={(fullValue) => {
|
|
167
|
+
const userValue = activePrefix
|
|
168
|
+
? (fullValue || "").substring((fullValue || "").indexOf("\n") + 1)
|
|
169
|
+
: fullValue;
|
|
170
|
+
onChange && onChange(userValue);
|
|
171
|
+
setProp &&
|
|
172
|
+
propKey &&
|
|
173
|
+
setProp((prop) => (prop[propKey] = userValue));
|
|
174
|
+
}}
|
|
175
|
+
defaultLanguage="typescript"
|
|
176
|
+
//onMount={handleEditorDidMount}
|
|
177
|
+
//beforeMount={handleEditorWillMount}
|
|
178
|
+
options={singleLineEditorOptions}
|
|
179
|
+
//theme="myCoolTheme"
|
|
180
|
+
beforeMount={handleEditorWillMount}
|
|
181
|
+
onMount={handleEditorDidMount}
|
|
182
|
+
/>
|
|
183
|
+
</div>
|
|
177
184
|
</div>
|
|
178
185
|
);
|
|
179
186
|
}
|
|
@@ -203,16 +210,21 @@ export const MultiLineCodeEditor = ({ setProp, value, onChange, isModalEditor =
|
|
|
203
210
|
const resolvedPlaceholder = placeholder || "// enter code here";
|
|
204
211
|
|
|
205
212
|
return (
|
|
206
|
-
<div className="form-control
|
|
213
|
+
<div className="form-control monaco-input">
|
|
207
214
|
{isEmpty && !isModalEditor && (
|
|
208
215
|
<div style={{
|
|
209
216
|
position: "absolute",
|
|
210
|
-
top: "
|
|
211
|
-
left: "
|
|
217
|
+
top: "0.375rem",
|
|
218
|
+
left: "10px",
|
|
219
|
+
right: "10px",
|
|
220
|
+
height: "21px",
|
|
221
|
+
display: "flex",
|
|
222
|
+
alignItems: "center",
|
|
212
223
|
color: "#999",
|
|
213
224
|
pointerEvents: "none",
|
|
214
225
|
zIndex: 1,
|
|
215
226
|
fontSize: "14px",
|
|
227
|
+
overflow: "hidden",
|
|
216
228
|
}}>
|
|
217
229
|
{resolvedPlaceholder}
|
|
218
230
|
</div>
|
|
@@ -31,7 +31,11 @@ import { ToggleFilter } from "./elements/ToggleFilter";
|
|
|
31
31
|
import { DropMenu } from "./elements/DropMenu";
|
|
32
32
|
import { Container } from "./elements/Container";
|
|
33
33
|
import { Prompt } from "./elements/Prompt";
|
|
34
|
+
import { MessageList } from "./elements/MessageList";
|
|
35
|
+
import { MessageForm } from "./elements/MessageForm";
|
|
34
36
|
import { rand_ident } from "./elements/utils";
|
|
37
|
+
import { LibraryInstance } from "./elements/LibraryInstance";
|
|
38
|
+
import { LibrarySlotInstance } from "./elements/LibrarySlotInstance";
|
|
35
39
|
|
|
36
40
|
/**
|
|
37
41
|
* @param {object} segment
|
|
@@ -82,9 +86,83 @@ const allElements = [
|
|
|
82
86
|
ListColumn,
|
|
83
87
|
ListColumns,
|
|
84
88
|
Prompt,
|
|
89
|
+
MessageList,
|
|
90
|
+
MessageForm,
|
|
85
91
|
];
|
|
86
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Fills each slot in a shared component's layout with its saved field pick or dropped-in content.
|
|
95
|
+
* @param {object} segment
|
|
96
|
+
* @param {object[]} [slots]
|
|
97
|
+
* @returns {void}
|
|
98
|
+
*/
|
|
99
|
+
const applySlotFills = (segment, slots) => {
|
|
100
|
+
if (!segment) return;
|
|
101
|
+
if (Array.isArray(segment)) {
|
|
102
|
+
segment.forEach((s) => applySlotFills(s, slots));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (segment.type === "library-slot") {
|
|
106
|
+
const fill = (slots || []).find((s) => s.name === segment.name);
|
|
107
|
+
if (fill) Object.assign(segment, fill);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (segment.above) applySlotFills(segment.above, slots);
|
|
111
|
+
else if (segment.besides) applySlotFills(segment.besides, slots);
|
|
112
|
+
else if (segment.contents) applySlotFills(segment.contents, slots);
|
|
113
|
+
};
|
|
114
|
+
|
|
87
115
|
export /**
|
|
116
|
+
* Fetches the latest content for every shared component in a layout,
|
|
117
|
+
* including ones nested inside another shared component.
|
|
118
|
+
* @param {object} layout
|
|
119
|
+
* @param {object} options
|
|
120
|
+
* @param {Set<number>} [visitedIds] - library ids seen on this branch, so a
|
|
121
|
+
* library that contains itself resolves to nothing instead of looping forever
|
|
122
|
+
* @returns {Promise<void>}
|
|
123
|
+
* @category saltcorn-builder
|
|
124
|
+
* @subcategory components
|
|
125
|
+
* @namespace
|
|
126
|
+
*/
|
|
127
|
+
const resolveLibraryRefs = async (layout, options, visitedIds = new Set()) => {
|
|
128
|
+
const go = async (segment) => {
|
|
129
|
+
if (!segment) return;
|
|
130
|
+
if (Array.isArray(segment)) {
|
|
131
|
+
await Promise.all(segment.map(go));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (segment.type === "library" && segment.library_id) {
|
|
135
|
+
if (visitedIds.has(segment.library_id)) {
|
|
136
|
+
segment.contents = {};
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const r = await fetch(`/library/content/${segment.library_id}`, {
|
|
140
|
+
headers: { "CSRF-Token": options.csrfToken },
|
|
141
|
+
});
|
|
142
|
+
const lib = await r.json();
|
|
143
|
+
if (lib.error) return;
|
|
144
|
+
segment.library_name = lib.name;
|
|
145
|
+
segment.contents = lib.layout?.layout
|
|
146
|
+
? lib.layout.layout
|
|
147
|
+
: lib.layout || {};
|
|
148
|
+
applySlotFills(segment.contents, segment.slots);
|
|
149
|
+
await resolveLibraryRefs(
|
|
150
|
+
segment.contents,
|
|
151
|
+
options,
|
|
152
|
+
new Set(visitedIds).add(segment.library_id)
|
|
153
|
+
);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (segment.above) await go(segment.above);
|
|
157
|
+
else if (segment.besides) await go(segment.besides);
|
|
158
|
+
else if (segment.contents) await go(segment.contents);
|
|
159
|
+
};
|
|
160
|
+
await go(layout);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export /**
|
|
164
|
+
* Builds real placed nodes from a saved layout tree, the reverse of what
|
|
165
|
+
* craftToSaltcorn does.
|
|
88
166
|
* @param {object} layout
|
|
89
167
|
* @param {object} query
|
|
90
168
|
* @param {object} actions
|
|
@@ -103,6 +181,7 @@ const layoutToNodes = (
|
|
|
103
181
|
index = false
|
|
104
182
|
) => {
|
|
105
183
|
/**
|
|
184
|
+
* Turns one saved layout segment into its matching Craft element.
|
|
106
185
|
* @param {object} segment
|
|
107
186
|
* @param {string} ix
|
|
108
187
|
* @returns {Element|Text|View|Action|Tabs|Columns}
|
|
@@ -235,6 +314,36 @@ const layoutToNodes = (
|
|
|
235
314
|
isFormula={segment.isFormula || {}}
|
|
236
315
|
/>
|
|
237
316
|
);
|
|
317
|
+
} else if (segment.type === "library") {
|
|
318
|
+
// segment.contents/library_name must already be filled in by
|
|
319
|
+
// resolveLibraryRefs before this runs - toTag itself can't be async
|
|
320
|
+
return (
|
|
321
|
+
<Element
|
|
322
|
+
key={ix}
|
|
323
|
+
canvas
|
|
324
|
+
is={LibraryInstance}
|
|
325
|
+
library_id={segment.library_id}
|
|
326
|
+
library_name={segment.library_name}
|
|
327
|
+
custom={segment._custom || {}}
|
|
328
|
+
>
|
|
329
|
+
{toTag(segment.contents)}
|
|
330
|
+
</Element>
|
|
331
|
+
);
|
|
332
|
+
} else if (segment.type === "library-slot") {
|
|
333
|
+
return (
|
|
334
|
+
<Element
|
|
335
|
+
key={ix}
|
|
336
|
+
canvas
|
|
337
|
+
is={LibrarySlotInstance}
|
|
338
|
+
slot_name={segment.name}
|
|
339
|
+
kind={segment.kind || "field"}
|
|
340
|
+
field={segment.field}
|
|
341
|
+
fieldview={segment.fieldview}
|
|
342
|
+
custom={segment._custom || {}}
|
|
343
|
+
>
|
|
344
|
+
{toTag(segment.contents)}
|
|
345
|
+
</Element>
|
|
346
|
+
);
|
|
238
347
|
} else if (segment.type === "tabs") {
|
|
239
348
|
let contentsArray = segment.contents.map(toTag);
|
|
240
349
|
let contents;
|
|
@@ -397,6 +506,7 @@ const layoutToNodes = (
|
|
|
397
506
|
*/
|
|
398
507
|
|
|
399
508
|
export /**
|
|
509
|
+
* Turns the placed canvas into a saved layout.
|
|
400
510
|
* @param {object[]} nodes
|
|
401
511
|
* @param {string} [startFrom = "ROOT" ]
|
|
402
512
|
* @returns {object}
|
|
@@ -406,6 +516,9 @@ export /**
|
|
|
406
516
|
*/
|
|
407
517
|
const craftToSaltcorn = (nodes, startFrom = "ROOT", options) => {
|
|
408
518
|
var columns = [];
|
|
519
|
+
// edits to shared components save to their own _sc_library row, not
|
|
520
|
+
// this page/view's layout - collected as go() walks the tree
|
|
521
|
+
var libraryUpdates = [];
|
|
409
522
|
/**
|
|
410
523
|
* @param {object} node
|
|
411
524
|
* @returns {void|object}
|
|
@@ -419,15 +532,55 @@ const craftToSaltcorn = (nodes, startFrom = "ROOT", options) => {
|
|
|
419
532
|
|
|
420
533
|
const get_nodes = (node) => {
|
|
421
534
|
if (!node.nodes || node.nodes.length == 0) return;
|
|
422
|
-
else if (node.nodes.length == 1)
|
|
423
|
-
|
|
535
|
+
else if (node.nodes.length == 1)
|
|
536
|
+
return go(nodes[node.nodes[0]], node.nodes[0]);
|
|
537
|
+
else
|
|
538
|
+
return removeEmpty({
|
|
539
|
+
above: node.nodes.map((nm) => go(nodes[nm], nm)),
|
|
540
|
+
});
|
|
424
541
|
};
|
|
425
542
|
|
|
426
543
|
/**
|
|
544
|
+
* Finds each slot's fill in a layout tree, keeps it for this page/view,
|
|
545
|
+
* and clears it from the tree so it isn't saved to the shared row.
|
|
546
|
+
* @param {object} segment - layout tree (or subtree) to scan
|
|
547
|
+
* @param {object[]} [found] - accumulator, used internally for recursion
|
|
548
|
+
* @returns {object[]} the fills collected so far
|
|
549
|
+
*/
|
|
550
|
+
const collectAndStripSlots = (segment, found = []) => {
|
|
551
|
+
if (!segment) return found;
|
|
552
|
+
if (Array.isArray(segment)) {
|
|
553
|
+
segment.forEach((s) => collectAndStripSlots(s, found));
|
|
554
|
+
return found;
|
|
555
|
+
}
|
|
556
|
+
if (segment.type === "library-slot") {
|
|
557
|
+
found.push({
|
|
558
|
+
name: segment.name,
|
|
559
|
+
kind: segment.kind,
|
|
560
|
+
field: segment.field,
|
|
561
|
+
fieldview: segment.fieldview,
|
|
562
|
+
contents: segment.contents,
|
|
563
|
+
});
|
|
564
|
+
delete segment.field;
|
|
565
|
+
delete segment.fieldview;
|
|
566
|
+
delete segment.contents;
|
|
567
|
+
return found;
|
|
568
|
+
}
|
|
569
|
+
if (segment.above) collectAndStripSlots(segment.above, found);
|
|
570
|
+
else if (segment.besides) collectAndStripSlots(segment.besides, found);
|
|
571
|
+
else if (segment.contents) collectAndStripSlots(segment.contents, found);
|
|
572
|
+
return found;
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Turns one placed node (and everything under it) back into a plain
|
|
577
|
+
* layout segment, the reverse of what layoutToNodes does.
|
|
427
578
|
* @param {object} node
|
|
579
|
+
* @param {string} [id] - this node's own id, so a LibraryInstance save can
|
|
580
|
+
* be told apart from a sibling placement (see doSave)
|
|
428
581
|
* @returns {object}
|
|
429
582
|
*/
|
|
430
|
-
const go = (node) => {
|
|
583
|
+
const go = (node, id) => {
|
|
431
584
|
if (!node) return;
|
|
432
585
|
let customProps = {};
|
|
433
586
|
const mergedCustom = { ...(node?.props?.custom || {}), ...(node?.custom || {}) };
|
|
@@ -489,6 +642,40 @@ const craftToSaltcorn = (nodes, startFrom = "ROOT", options) => {
|
|
|
489
642
|
});
|
|
490
643
|
return lc;
|
|
491
644
|
}
|
|
645
|
+
if (node.displayName === LibrarySlotInstance.craft.displayName) {
|
|
646
|
+
// keeps its picked field or dropped content right here - if it's
|
|
647
|
+
// part of a shared component, that gets separated out before saving
|
|
648
|
+
const kind = node.props.kind;
|
|
649
|
+
return {
|
|
650
|
+
type: "library-slot",
|
|
651
|
+
kind,
|
|
652
|
+
name: node.props.slot_name,
|
|
653
|
+
...(kind === "container"
|
|
654
|
+
? { contents: get_nodes(node) }
|
|
655
|
+
: { field: node.props.field, fieldview: node.props.fieldview }),
|
|
656
|
+
...customProps,
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
if (node.displayName === LibraryInstance.craft.displayName) {
|
|
660
|
+
// must come before the generic canvas case below, or a reload turns
|
|
661
|
+
// this into a plain copy. skip if emptied out, so we don't blank the shared row
|
|
662
|
+
const childContent = get_nodes(node);
|
|
663
|
+
let slots = [];
|
|
664
|
+
if (childContent !== undefined) {
|
|
665
|
+
slots = collectAndStripSlots(childContent);
|
|
666
|
+
libraryUpdates.push({
|
|
667
|
+
library_id: node.props.library_id,
|
|
668
|
+
layout: childContent,
|
|
669
|
+
node_id: id,
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
return {
|
|
673
|
+
type: "library",
|
|
674
|
+
library_id: node.props.library_id,
|
|
675
|
+
...(slots.length ? { slots } : {}),
|
|
676
|
+
...customProps,
|
|
677
|
+
};
|
|
678
|
+
}
|
|
492
679
|
if (node.isCanvas) {
|
|
493
680
|
return get_nodes(node);
|
|
494
681
|
}
|
|
@@ -671,9 +858,9 @@ const craftToSaltcorn = (nodes, startFrom = "ROOT", options) => {
|
|
|
671
858
|
};
|
|
672
859
|
}
|
|
673
860
|
};
|
|
674
|
-
const layout = go(nodes[startFrom]) || {};
|
|
861
|
+
const layout = go(nodes[startFrom], startFrom) || {};
|
|
675
862
|
/*console.log("nodes", JSON.stringify(nodes));
|
|
676
863
|
console.log("cols", JSON.stringify(columns));
|
|
677
864
|
console.log("layout", JSON.stringify(layout));*/
|
|
678
|
-
return { columns, layout };
|
|
865
|
+
return { columns, layout, libraryUpdates };
|
|
679
866
|
};
|