@saltcorn/builder 0.9.4-beta.9 → 0.9.4
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 +20 -12
- package/package.json +3 -2
- package/src/components/Builder.js +34 -54
- package/src/components/Library.js +16 -4
- package/src/components/RenderNode.js +5 -0
- package/src/components/Toolbox.js +61 -2
- package/src/components/elements/Action.js +12 -0
- package/src/components/elements/Aggregation.js +200 -104
- package/src/components/elements/BoxModelEditor.js +8 -8
- package/src/components/elements/Column.js +16 -2
- package/src/components/elements/Columns.js +3 -3
- package/src/components/elements/Container.js +25 -1
- package/src/components/elements/DropMenu.js +31 -2
- package/src/components/elements/Field.js +22 -20
- package/src/components/elements/HTMLCode.js +1 -1
- package/src/components/elements/JoinField.js +25 -1
- package/src/components/elements/Link.js +1 -0
- package/src/components/elements/ListColumn.js +177 -0
- package/src/components/elements/ListColumns.js +62 -0
- package/src/components/elements/Tabs.js +26 -0
- package/src/components/elements/Text.js +4 -2
- package/src/components/elements/View.js +37 -32
- package/src/components/elements/ViewLink.js +21 -14
- package/src/components/elements/utils.js +64 -24
- package/src/components/storage.js +65 -5
- package/tests/relations_finder.test.js +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/builder",
|
|
3
|
-
"version": "0.9.4
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"description": "Drag and drop view builder for Saltcorn, open-source no-code platform",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"homepage": "https://saltcorn.com",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"@babel/preset-react": "7.9.4",
|
|
21
21
|
"@craftjs/core": "0.1.0-beta.20",
|
|
22
22
|
"@craftjs/utils": "0.1.0-beta.20",
|
|
23
|
-
"@saltcorn/common-code": "0.9.4
|
|
23
|
+
"@saltcorn/common-code": "0.9.4",
|
|
24
24
|
"saltcorn-craft-layers-noeye": "0.1.0-beta.22",
|
|
25
25
|
"@fonticonpicker/react-fonticonpicker": "1.2.0",
|
|
26
26
|
"@fortawesome/fontawesome-svg-core": "1.2.34",
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"react-bootstrap-icons": "1.5.0",
|
|
40
40
|
"react-contenteditable": "3.3.5",
|
|
41
41
|
"react-dom": "16.13.1",
|
|
42
|
+
"react-select": "4.3.1",
|
|
42
43
|
"react-test-renderer": "16.13.1",
|
|
43
44
|
"react-transition-group": "4.4.1",
|
|
44
45
|
"@tippyjs/react": "4.2.6",
|
|
@@ -18,7 +18,7 @@ import { JoinField } from "./elements/JoinField";
|
|
|
18
18
|
import { Aggregation } from "./elements/Aggregation";
|
|
19
19
|
import { LineBreak } from "./elements/LineBreak";
|
|
20
20
|
import { ViewLink } from "./elements/ViewLink";
|
|
21
|
-
import { Columns } from "./elements/Columns";
|
|
21
|
+
import { Columns, ntimes } from "./elements/Columns";
|
|
22
22
|
import { SearchBar } from "./elements/SearchBar";
|
|
23
23
|
import { HTMLCode } from "./elements/HTMLCode";
|
|
24
24
|
import { Action } from "./elements/Action";
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
ToolboxEdit,
|
|
38
38
|
ToolboxPage,
|
|
39
39
|
ToolboxFilter,
|
|
40
|
+
ToolboxList,
|
|
40
41
|
} from "./Toolbox";
|
|
41
42
|
import { craftToSaltcorn, layoutToNodes } from "./storage";
|
|
42
43
|
import { Card } from "./elements/Card";
|
|
@@ -53,6 +54,7 @@ import {
|
|
|
53
54
|
faTrashAlt,
|
|
54
55
|
faSave,
|
|
55
56
|
faExclamationTriangle,
|
|
57
|
+
faPlus,
|
|
56
58
|
} from "@fortawesome/free-solid-svg-icons";
|
|
57
59
|
import {
|
|
58
60
|
faCaretSquareLeft,
|
|
@@ -65,6 +67,8 @@ import {
|
|
|
65
67
|
} from "./elements/utils";
|
|
66
68
|
import { InitNewElement, Library } from "./Library";
|
|
67
69
|
import { RenderNode } from "./RenderNode";
|
|
70
|
+
import { ListColumn } from "./elements/ListColumn";
|
|
71
|
+
import { ListColumns } from "./elements/ListColumns";
|
|
68
72
|
const { Provider } = optionsCtx;
|
|
69
73
|
|
|
70
74
|
/**
|
|
@@ -253,58 +257,20 @@ const SettingsPanel = () => {
|
|
|
253
257
|
);
|
|
254
258
|
};
|
|
255
259
|
|
|
256
|
-
|
|
257
|
-
* @returns {button}
|
|
258
|
-
* @category saltcorn-builder
|
|
259
|
-
* @subcategory components
|
|
260
|
-
* @namespace
|
|
261
|
-
*/
|
|
262
|
-
const SaveButton = () => {
|
|
260
|
+
const AddColumnButton = () => {
|
|
263
261
|
const { query, actions } = useEditor(() => {});
|
|
264
262
|
const options = useContext(optionsCtx);
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const data = craftToSaltcorn(JSON.parse(query.serialize()));
|
|
271
|
-
const urlroot = options.page_id ? "pageedit" : "viewedit";
|
|
272
|
-
fetch(`/${urlroot}/savebuilder/${options.page_id || options.view_id}`, {
|
|
273
|
-
method: "POST", // or 'PUT'
|
|
274
|
-
headers: {
|
|
275
|
-
"Content-Type": "application/json",
|
|
276
|
-
"CSRF-Token": options.csrfToken,
|
|
277
|
-
},
|
|
278
|
-
body: JSON.stringify(data),
|
|
279
|
-
});
|
|
263
|
+
const addColumn = () => {
|
|
264
|
+
actions.addNodeTree(
|
|
265
|
+
query.parseReactElement(<ListColumn />).toNodeTree(),
|
|
266
|
+
"ROOT"
|
|
267
|
+
);
|
|
280
268
|
};
|
|
281
|
-
return
|
|
282
|
-
<button
|
|
283
|
-
className="
|
|
284
|
-
|
|
285
|
-
>
|
|
286
|
-
Save
|
|
269
|
+
return (
|
|
270
|
+
<button className="btn btn-primary mt-2" onClick={addColumn}>
|
|
271
|
+
<FontAwesomeIcon icon={faPlus} className="me-2" />
|
|
272
|
+
Add column
|
|
287
273
|
</button>
|
|
288
|
-
) : (
|
|
289
|
-
""
|
|
290
|
-
);
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* @returns {a|""}
|
|
295
|
-
* @category saltcorn-builder
|
|
296
|
-
* @subcategory components
|
|
297
|
-
* @namespace
|
|
298
|
-
*/
|
|
299
|
-
const ViewPageLink = () => {
|
|
300
|
-
const { query, actions } = useEditor(() => {});
|
|
301
|
-
const options = useContext(optionsCtx);
|
|
302
|
-
return options.page_id ? (
|
|
303
|
-
<a target="_blank" className="d-block" href={`/page/${options.page_name}`}>
|
|
304
|
-
View page in new tab »
|
|
305
|
-
</a>
|
|
306
|
-
) : (
|
|
307
|
-
""
|
|
308
274
|
);
|
|
309
275
|
};
|
|
310
276
|
|
|
@@ -357,14 +323,18 @@ const NextButton = ({ layout }) => {
|
|
|
357
323
|
const options = useContext(optionsCtx);
|
|
358
324
|
|
|
359
325
|
useEffect(() => {
|
|
360
|
-
layoutToNodes(layout, query, actions);
|
|
326
|
+
layoutToNodes(layout, query, actions, "ROOT", options);
|
|
361
327
|
}, []);
|
|
362
328
|
|
|
363
329
|
/**
|
|
364
330
|
* @returns {void}
|
|
365
331
|
*/
|
|
366
332
|
const onClick = () => {
|
|
367
|
-
const { columns, layout } = craftToSaltcorn(
|
|
333
|
+
const { columns, layout } = craftToSaltcorn(
|
|
334
|
+
JSON.parse(query.serialize()),
|
|
335
|
+
"ROOT",
|
|
336
|
+
options
|
|
337
|
+
);
|
|
368
338
|
document
|
|
369
339
|
.querySelector("form#scbuildform input[name=columns]")
|
|
370
340
|
.setAttribute("value", encodeURIComponent(JSON.stringify(columns)));
|
|
@@ -430,6 +400,7 @@ const Builder = ({ options, layout, mode }) => {
|
|
|
430
400
|
<div className="card mt-1" accordiontitle="Components">
|
|
431
401
|
{{
|
|
432
402
|
show: <ToolboxShow expanded={isLeftEnlarged} />,
|
|
403
|
+
list: <ToolboxList expanded={isLeftEnlarged} />,
|
|
433
404
|
edit: <ToolboxEdit expanded={isLeftEnlarged} />,
|
|
434
405
|
page: <ToolboxPage expanded={isLeftEnlarged} />,
|
|
435
406
|
filter: <ToolboxFilter expanded={isLeftEnlarged} />,
|
|
@@ -442,7 +413,7 @@ const Builder = ({ options, layout, mode }) => {
|
|
|
442
413
|
</div>
|
|
443
414
|
<div
|
|
444
415
|
className="card toolbox-card pe-0"
|
|
445
|
-
style={isLeftEnlarged ? { width: "
|
|
416
|
+
style={isLeftEnlarged ? { width: "13.4rem" } : {}}
|
|
446
417
|
>
|
|
447
418
|
<div className="card-header p-2 d-flex justify-content-between">
|
|
448
419
|
<div>Layers</div>
|
|
@@ -466,7 +437,9 @@ const Builder = ({ options, layout, mode }) => {
|
|
|
466
437
|
</div>
|
|
467
438
|
<div
|
|
468
439
|
id="builder-main-canvas"
|
|
469
|
-
className={`col builder-mode-${options.mode}
|
|
440
|
+
className={`col builder-mode-${options.mode} ${
|
|
441
|
+
options.mode !== "list" ? "emptymsg" : ""
|
|
442
|
+
}`}
|
|
470
443
|
>
|
|
471
444
|
<div>
|
|
472
445
|
<Frame
|
|
@@ -493,10 +466,17 @@ const Builder = ({ options, layout, mode }) => {
|
|
|
493
466
|
Tabs,
|
|
494
467
|
Table,
|
|
495
468
|
ToggleFilter,
|
|
469
|
+
ListColumn,
|
|
470
|
+
ListColumns,
|
|
496
471
|
}}
|
|
497
472
|
>
|
|
498
|
-
|
|
473
|
+
{options.mode === "list" ? (
|
|
474
|
+
<Element canvas is={ListColumns}></Element>
|
|
475
|
+
) : (
|
|
476
|
+
<Element canvas is={Column}></Element>
|
|
477
|
+
)}
|
|
499
478
|
</Frame>
|
|
479
|
+
{options.mode === "list" ? <AddColumnButton /> : null}
|
|
500
480
|
</div>
|
|
501
481
|
</div>
|
|
502
482
|
<div className="col-sm-auto builder-sidebar">
|
|
@@ -93,7 +93,11 @@ const InitNewElement = ({ nodekeys, savingState, setSavingState }) => {
|
|
|
93
93
|
const doSave = (query) => {
|
|
94
94
|
if (!query.serialize) return;
|
|
95
95
|
|
|
96
|
-
const data = craftToSaltcorn(
|
|
96
|
+
const data = craftToSaltcorn(
|
|
97
|
+
JSON.parse(query.serialize()),
|
|
98
|
+
"ROOT",
|
|
99
|
+
options
|
|
100
|
+
);
|
|
97
101
|
const urlroot = options.page_id ? "pageedit" : "viewedit";
|
|
98
102
|
if (savedData.current === false) {
|
|
99
103
|
//do not save on first call
|
|
@@ -124,7 +128,10 @@ const InitNewElement = ({ nodekeys, savingState, setSavingState }) => {
|
|
|
124
128
|
});
|
|
125
129
|
})
|
|
126
130
|
.catch((e) => {
|
|
127
|
-
const text =
|
|
131
|
+
const text =
|
|
132
|
+
e.message === "Failed to fetch"
|
|
133
|
+
? "Network connection lost"
|
|
134
|
+
: e || "Unable to save";
|
|
128
135
|
// don't log duplicates
|
|
129
136
|
if (savingState.error) setSavingState({ isSaving: false, error: text });
|
|
130
137
|
else {
|
|
@@ -157,7 +164,8 @@ const InitNewElement = ({ nodekeys, savingState, setSavingState }) => {
|
|
|
157
164
|
layout.layout ? layout.layout : layout,
|
|
158
165
|
query,
|
|
159
166
|
actions,
|
|
160
|
-
node.parent
|
|
167
|
+
node.parent,
|
|
168
|
+
options
|
|
161
169
|
);
|
|
162
170
|
setTimeout(() => {
|
|
163
171
|
actions.delete(id);
|
|
@@ -209,7 +217,11 @@ const Library = ({ expanded }) => {
|
|
|
209
217
|
* @returns {void}
|
|
210
218
|
*/
|
|
211
219
|
const addSelected = () => {
|
|
212
|
-
const layout = craftToSaltcorn(
|
|
220
|
+
const layout = craftToSaltcorn(
|
|
221
|
+
JSON.parse(query.serialize()),
|
|
222
|
+
selected,
|
|
223
|
+
options
|
|
224
|
+
);
|
|
213
225
|
const data = { layout, icon, name: newName };
|
|
214
226
|
fetch(`/library/savefrombuilder`, {
|
|
215
227
|
method: "POST", // or 'PUT'
|
|
@@ -80,6 +80,11 @@ const RenderNode = ({ render }) => {
|
|
|
80
80
|
currentDOM.style.left = left;
|
|
81
81
|
}, [dom, getPos]);
|
|
82
82
|
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (name === "Column" && parent && parent !== "ROOT")
|
|
85
|
+
actions.selectNode(parent);
|
|
86
|
+
}, [isActive]);
|
|
87
|
+
|
|
83
88
|
useEffect(() => {
|
|
84
89
|
document
|
|
85
90
|
.getElementById("builder-main-canvas")
|
|
@@ -490,7 +490,7 @@ const AggregationElem = ({ connectors, child_field_list, agg_field_opts }) => (
|
|
|
490
490
|
>
|
|
491
491
|
<Aggregation
|
|
492
492
|
agg_relation={child_field_list[0]}
|
|
493
|
-
agg_field={headOr(agg_field_opts[child_field_list[0]], "")}
|
|
493
|
+
agg_field={headOr(agg_field_opts[child_field_list[0]], "")?.name}
|
|
494
494
|
stat={"Count"}
|
|
495
495
|
textStyle={""}
|
|
496
496
|
aggwhere={""}
|
|
@@ -570,6 +570,54 @@ const ToolboxShow = ({ expanded }) => {
|
|
|
570
570
|
expanded
|
|
571
571
|
);
|
|
572
572
|
};
|
|
573
|
+
export /**
|
|
574
|
+
* @returns {Fragment}
|
|
575
|
+
* @category saltcorn-builder
|
|
576
|
+
* @subcategory components / Toolbox
|
|
577
|
+
* @namespace
|
|
578
|
+
*/
|
|
579
|
+
const ToolboxList = ({ expanded }) => {
|
|
580
|
+
const { connectors, query } = useEditor();
|
|
581
|
+
const options = useContext(optionsCtx);
|
|
582
|
+
const {
|
|
583
|
+
fields,
|
|
584
|
+
field_view_options,
|
|
585
|
+
child_field_list,
|
|
586
|
+
agg_field_opts,
|
|
587
|
+
views,
|
|
588
|
+
images,
|
|
589
|
+
} = options;
|
|
590
|
+
return chunkToolBox(
|
|
591
|
+
[
|
|
592
|
+
<TextElem connectors={connectors} />,
|
|
593
|
+
<FieldElem
|
|
594
|
+
connectors={connectors}
|
|
595
|
+
fields={fields}
|
|
596
|
+
field_view_options={field_view_options}
|
|
597
|
+
/>,
|
|
598
|
+
<JoinFieldElem connectors={connectors} options={options} />,
|
|
599
|
+
<ViewLinkElem connectors={connectors} options={options} />,
|
|
600
|
+
<ActionElem connectors={connectors} options={options} />,
|
|
601
|
+
<LinkElem connectors={connectors} />,
|
|
602
|
+
<AggregationElem
|
|
603
|
+
connectors={connectors}
|
|
604
|
+
child_field_list={child_field_list}
|
|
605
|
+
agg_field_opts={agg_field_opts}
|
|
606
|
+
/>,
|
|
607
|
+
// <ViewElem connectors={connectors} views={views} />,
|
|
608
|
+
// <ContainerElem connectors={connectors} />,
|
|
609
|
+
// <CardElem connectors={connectors} />,
|
|
610
|
+
// <TabsElem connectors={connectors} />,
|
|
611
|
+
<HTMLElem connectors={connectors} />,
|
|
612
|
+
<DropMenuElem connectors={connectors} />,
|
|
613
|
+
// <TableElem connectors={connectors} />,
|
|
614
|
+
...(options.allowMultipleElementsPerColumn
|
|
615
|
+
? [<LineBreakElem connectors={connectors} />]
|
|
616
|
+
: []),
|
|
617
|
+
],
|
|
618
|
+
expanded
|
|
619
|
+
);
|
|
620
|
+
};
|
|
573
621
|
|
|
574
622
|
export /**
|
|
575
623
|
* @returns {Fragment}
|
|
@@ -580,7 +628,13 @@ export /**
|
|
|
580
628
|
const ToolboxFilter = ({ expanded }) => {
|
|
581
629
|
const { connectors, query } = useEditor();
|
|
582
630
|
const options = useContext(optionsCtx);
|
|
583
|
-
const {
|
|
631
|
+
const {
|
|
632
|
+
fields,
|
|
633
|
+
views,
|
|
634
|
+
field_view_options,
|
|
635
|
+
child_field_list,
|
|
636
|
+
agg_field_opts,
|
|
637
|
+
} = options;
|
|
584
638
|
return chunkToolBox(
|
|
585
639
|
[
|
|
586
640
|
<TextElem connectors={connectors} />,
|
|
@@ -595,6 +649,11 @@ const ToolboxFilter = ({ expanded }) => {
|
|
|
595
649
|
<ToggleFilterElem connectors={connectors} fields={fields} />,
|
|
596
650
|
<SearchElem connectors={connectors} />,
|
|
597
651
|
<ActionElem connectors={connectors} options={options} />,
|
|
652
|
+
<AggregationElem
|
|
653
|
+
connectors={connectors}
|
|
654
|
+
child_field_list={child_field_list}
|
|
655
|
+
agg_field_opts={agg_field_opts}
|
|
656
|
+
/>,
|
|
598
657
|
<ContainerElem connectors={connectors} />,
|
|
599
658
|
<CardElem connectors={connectors} />,
|
|
600
659
|
<TabsElem connectors={connectors} />,
|
|
@@ -110,6 +110,7 @@ const ActionSettings = () => {
|
|
|
110
110
|
step_only_ifs: node.data.props.step_only_ifs,
|
|
111
111
|
step_action_names: node.data.props.step_action_names,
|
|
112
112
|
setting_action_n: node.data.props.setting_action_n,
|
|
113
|
+
spinner: node.data.props.spinner,
|
|
113
114
|
}));
|
|
114
115
|
const {
|
|
115
116
|
actions: { setProp },
|
|
@@ -127,6 +128,7 @@ const ActionSettings = () => {
|
|
|
127
128
|
setting_action_n,
|
|
128
129
|
step_only_ifs,
|
|
129
130
|
step_action_names,
|
|
131
|
+
spinner,
|
|
130
132
|
} = node;
|
|
131
133
|
const options = useContext(optionsCtx);
|
|
132
134
|
const getCfgFields = (fv) => (options.actionConfigForms || {})[fv];
|
|
@@ -278,6 +280,16 @@ const ActionSettings = () => {
|
|
|
278
280
|
/>
|
|
279
281
|
<label className="form-check-label">User confirmation?</label>
|
|
280
282
|
</div>
|
|
283
|
+
<div className="form-check">
|
|
284
|
+
<input
|
|
285
|
+
className="form-check-input"
|
|
286
|
+
name="block"
|
|
287
|
+
type="checkbox"
|
|
288
|
+
checked={spinner}
|
|
289
|
+
onChange={setAProp("spinner", { checked: true })}
|
|
290
|
+
/>
|
|
291
|
+
<label className="form-check-label">Spinner on click</label>
|
|
292
|
+
</div>
|
|
281
293
|
{action_style !== "on_page_load" ? (
|
|
282
294
|
<BlockSetting block={block} setProp={setProp} />
|
|
283
295
|
) : null}
|