@saltcorn/builder 0.9.4-beta.14 → 0.9.4-beta.16
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 +12 -3
- package/src/components/RenderNode.js +5 -0
- package/src/components/Toolbox.js +48 -0
- package/src/components/elements/Aggregation.js +6 -4
- package/src/components/elements/Column.js +16 -2
- package/src/components/elements/HTMLCode.js +1 -1
- package/src/components/elements/ListColumn.js +177 -0
- package/src/components/elements/ListColumns.js +62 -0
- package/src/components/elements/Text.js +4 -2
- package/src/components/elements/View.js +18 -15
- package/src/components/elements/ViewLink.js +20 -14
- package/src/components/elements/utils.js +58 -22
- package/src/components/storage.js +57 -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-beta.
|
|
3
|
+
"version": "0.9.4-beta.16",
|
|
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-beta.
|
|
23
|
+
"@saltcorn/common-code": "0.9.4-beta.16",
|
|
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
|
|
@@ -160,7 +164,8 @@ const InitNewElement = ({ nodekeys, savingState, setSavingState }) => {
|
|
|
160
164
|
layout.layout ? layout.layout : layout,
|
|
161
165
|
query,
|
|
162
166
|
actions,
|
|
163
|
-
node.parent
|
|
167
|
+
node.parent,
|
|
168
|
+
options
|
|
164
169
|
);
|
|
165
170
|
setTimeout(() => {
|
|
166
171
|
actions.delete(id);
|
|
@@ -212,7 +217,11 @@ const Library = ({ expanded }) => {
|
|
|
212
217
|
* @returns {void}
|
|
213
218
|
*/
|
|
214
219
|
const addSelected = () => {
|
|
215
|
-
const layout = craftToSaltcorn(
|
|
220
|
+
const layout = craftToSaltcorn(
|
|
221
|
+
JSON.parse(query.serialize()),
|
|
222
|
+
selected,
|
|
223
|
+
options
|
|
224
|
+
);
|
|
216
225
|
const data = { layout, icon, name: newName };
|
|
217
226
|
fetch(`/library/savefrombuilder`, {
|
|
218
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")
|
|
@@ -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}
|
|
@@ -141,13 +141,15 @@ const AggregationSettings = () => {
|
|
|
141
141
|
)}
|
|
142
142
|
{options.fields
|
|
143
143
|
.filter((f) => f.type === "Date" || f.type.name === "Date")
|
|
144
|
-
.map((f) => (
|
|
145
|
-
<option value={`Latest ${f.name}`}>
|
|
144
|
+
.map((f, ix) => (
|
|
145
|
+
<option key={ix} value={`Latest ${f.name}`}>
|
|
146
|
+
Latest {f.name}
|
|
147
|
+
</option>
|
|
146
148
|
))}
|
|
147
149
|
{options.fields
|
|
148
150
|
.filter((f) => f.type === "Date" || f.type.name === "Date")
|
|
149
|
-
.map((f) => (
|
|
150
|
-
<option value={`Earliest ${f.name}`}>
|
|
151
|
+
.map((f, ix) => (
|
|
152
|
+
<option key={ix} value={`Earliest ${f.name}`}>
|
|
151
153
|
Earliest {f.name}
|
|
152
154
|
</option>
|
|
153
155
|
))}
|
|
@@ -25,12 +25,20 @@ const Column = ({ children, align }) => {
|
|
|
25
25
|
id,
|
|
26
26
|
connectors: { connect, drag },
|
|
27
27
|
} = useNode((node) => ({ selected: node.events.selected }));
|
|
28
|
+
const options = useContext(optionsCtx);
|
|
29
|
+
|
|
28
30
|
return (
|
|
29
31
|
<div
|
|
30
|
-
className={selected ? "selected-node" : ""}
|
|
32
|
+
className={`${selected ? "selected-node" : ""} ${
|
|
33
|
+
options.mode === "list" ? "flex-50 list-col-contents" : ""
|
|
34
|
+
}`}
|
|
31
35
|
ref={(dom) => connect(drag(dom))}
|
|
32
36
|
>
|
|
33
|
-
<div
|
|
37
|
+
<div
|
|
38
|
+
className={`canvas ${id === "ROOT" ? "root-canvas" : ""} ${
|
|
39
|
+
options.mode === "list" ? "list-empty-msg list-col-canvas" : ""
|
|
40
|
+
}`}
|
|
41
|
+
>
|
|
34
42
|
{children}
|
|
35
43
|
</div>
|
|
36
44
|
</div>
|
|
@@ -56,6 +64,12 @@ Column.craft = {
|
|
|
56
64
|
props: {},
|
|
57
65
|
rules: {
|
|
58
66
|
canDrag: () => true,
|
|
67
|
+
canMoveIn: (incomming, current) => {
|
|
68
|
+
if (current?.data?.props?.singleOccupancy && current.data.nodes?.length)
|
|
69
|
+
return false;
|
|
70
|
+
|
|
71
|
+
return true;
|
|
72
|
+
},
|
|
59
73
|
},
|
|
60
74
|
related: {
|
|
61
75
|
settings: ColumnSettings,
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @category saltcorn-builder
|
|
3
|
+
* @module components/elements/Column
|
|
4
|
+
* @subcategory components / elements
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, {
|
|
8
|
+
useContext,
|
|
9
|
+
Fragment,
|
|
10
|
+
useRef,
|
|
11
|
+
useEffect,
|
|
12
|
+
useState,
|
|
13
|
+
} from "react";
|
|
14
|
+
|
|
15
|
+
import { Element, useNode, useEditor } from "@craftjs/core";
|
|
16
|
+
import { setAPropGen, SettingsFromFields } from "./utils";
|
|
17
|
+
import { Column } from "./Column";
|
|
18
|
+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
19
|
+
import { faArrowUp, faArrowDown } from "@fortawesome/free-solid-svg-icons";
|
|
20
|
+
import optionsCtx from "../context";
|
|
21
|
+
|
|
22
|
+
export /**
|
|
23
|
+
*
|
|
24
|
+
* @param {object} props
|
|
25
|
+
* @param {string} props.children
|
|
26
|
+
* @param {*} props.align
|
|
27
|
+
* @returns {div}
|
|
28
|
+
* @category saltcorn-builder
|
|
29
|
+
* @subcategory components
|
|
30
|
+
* @namespace
|
|
31
|
+
*/
|
|
32
|
+
const ListColumn = ({
|
|
33
|
+
alignment,
|
|
34
|
+
colIndex,
|
|
35
|
+
contents,
|
|
36
|
+
header_label,
|
|
37
|
+
showif,
|
|
38
|
+
col_width,
|
|
39
|
+
col_width_units,
|
|
40
|
+
}) => {
|
|
41
|
+
const {
|
|
42
|
+
selected,
|
|
43
|
+
id,
|
|
44
|
+
connectors: { connect, drag },
|
|
45
|
+
} = useNode((node) => ({ selected: node.events.selected }));
|
|
46
|
+
const { actions, query, isActve } = useEditor((state) => ({}));
|
|
47
|
+
const options = useContext(optionsCtx);
|
|
48
|
+
|
|
49
|
+
const {
|
|
50
|
+
data: { parent },
|
|
51
|
+
} = query.node(id).get();
|
|
52
|
+
const siblings = query.node(parent).childNodes();
|
|
53
|
+
const nChildren = siblings.length;
|
|
54
|
+
const childIx = siblings.findIndex((sib) => sib === id);
|
|
55
|
+
|
|
56
|
+
const moveDown = () => {
|
|
57
|
+
const {
|
|
58
|
+
data: { parent },
|
|
59
|
+
} = query.node(id).get();
|
|
60
|
+
actions.move(id, parent, childIx + 2);
|
|
61
|
+
};
|
|
62
|
+
const moveUp = () => {
|
|
63
|
+
const {
|
|
64
|
+
data: { parent },
|
|
65
|
+
} = query.node(id).get();
|
|
66
|
+
actions.move(id, parent, childIx - 1);
|
|
67
|
+
};
|
|
68
|
+
return (
|
|
69
|
+
<div
|
|
70
|
+
className={`${
|
|
71
|
+
selected ? "selected-node" : ""
|
|
72
|
+
} d-flex w-100 list-column-outer`}
|
|
73
|
+
ref={(dom) => connect(drag(dom))}
|
|
74
|
+
>
|
|
75
|
+
<div className={`list-column flex-50 p-2`}>
|
|
76
|
+
<div className="d-flex justify-content-between h-100">
|
|
77
|
+
<div className="">
|
|
78
|
+
Column {childIx}
|
|
79
|
+
{header_label ? `: ${header_label}` : ""}
|
|
80
|
+
<br />
|
|
81
|
+
{showif ? (
|
|
82
|
+
<span className="badge bg-secondary me-2">showif</span>
|
|
83
|
+
) : (
|
|
84
|
+
""
|
|
85
|
+
)}
|
|
86
|
+
{alignment && alignment !== "Default" ? (
|
|
87
|
+
<span className="badge bg-secondary me-2">Align {alignment}</span>
|
|
88
|
+
) : (
|
|
89
|
+
""
|
|
90
|
+
)}
|
|
91
|
+
{col_width ? (
|
|
92
|
+
<span className="badge bg-secondary me-2">
|
|
93
|
+
{col_width}
|
|
94
|
+
{col_width_units}
|
|
95
|
+
</span>
|
|
96
|
+
) : (
|
|
97
|
+
""
|
|
98
|
+
)}
|
|
99
|
+
</div>
|
|
100
|
+
<div className="d-flex flex-column h-100 justify-content-between">
|
|
101
|
+
{childIx !== null && childIx > 0 ? (
|
|
102
|
+
<FontAwesomeIcon icon={faArrowUp} onClick={moveUp} />
|
|
103
|
+
) : (
|
|
104
|
+
<span></span>
|
|
105
|
+
)}
|
|
106
|
+
{childIx !== null && childIx < nChildren - 1 ? (
|
|
107
|
+
<FontAwesomeIcon icon={faArrowDown} onClick={moveDown} />
|
|
108
|
+
) : (
|
|
109
|
+
<span></span>
|
|
110
|
+
)}
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
<Element
|
|
115
|
+
canvas
|
|
116
|
+
id={`listcol`}
|
|
117
|
+
is={Column}
|
|
118
|
+
singleOccupancy={!options.allowMultipleElementsPerColumn}
|
|
119
|
+
>
|
|
120
|
+
{contents}
|
|
121
|
+
</Element>
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const fields = [
|
|
127
|
+
{
|
|
128
|
+
name: "header_label",
|
|
129
|
+
label: "Header label",
|
|
130
|
+
type: "String",
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: "showif",
|
|
134
|
+
label: "Show if true",
|
|
135
|
+
sublabel: "Formula. Leave blank to always show",
|
|
136
|
+
class: "validate-expression",
|
|
137
|
+
type: "String",
|
|
138
|
+
required: false,
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: "col_width",
|
|
142
|
+
label: "Column width",
|
|
143
|
+
type: "Integer",
|
|
144
|
+
attributes: { asideNext: true },
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: "col_width_units",
|
|
148
|
+
label: "Units",
|
|
149
|
+
type: "String",
|
|
150
|
+
required: true,
|
|
151
|
+
attributes: {
|
|
152
|
+
inline: true,
|
|
153
|
+
options: ["px", "%", "vw", "em", "rem"],
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: "alignment",
|
|
158
|
+
label: "Alignment",
|
|
159
|
+
input_type: "select",
|
|
160
|
+
options: ["Default", "Left", "Center", "Right"],
|
|
161
|
+
},
|
|
162
|
+
];
|
|
163
|
+
ListColumn.craft = {
|
|
164
|
+
displayName: "ListColumn",
|
|
165
|
+
props: {},
|
|
166
|
+
rules: {
|
|
167
|
+
canDrag: () => true,
|
|
168
|
+
},
|
|
169
|
+
related: {
|
|
170
|
+
settings: SettingsFromFields(fields, {
|
|
171
|
+
additionalFieldsOptionKey: "additionalColumnFields",
|
|
172
|
+
}),
|
|
173
|
+
segment_type: "list_column",
|
|
174
|
+
hasContents: true,
|
|
175
|
+
colFields: fields,
|
|
176
|
+
},
|
|
177
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @category saltcorn-builder
|
|
3
|
+
* @module components/elements/ListColumns
|
|
4
|
+
* @subcategory components / elements
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { useContext, Fragment } from "react";
|
|
8
|
+
|
|
9
|
+
import { Element, useNode } from "@craftjs/core";
|
|
10
|
+
import optionsCtx from "../context";
|
|
11
|
+
|
|
12
|
+
export /**
|
|
13
|
+
*
|
|
14
|
+
* @param {object} props
|
|
15
|
+
* @param {string} props.children
|
|
16
|
+
* @param {*} props.align
|
|
17
|
+
* @returns {div}
|
|
18
|
+
* @category saltcorn-builder
|
|
19
|
+
* @subcategory components
|
|
20
|
+
* @namespace
|
|
21
|
+
*/
|
|
22
|
+
const ListColumns = ({ children, align }) => {
|
|
23
|
+
const {
|
|
24
|
+
selected,
|
|
25
|
+
id,
|
|
26
|
+
connectors: { connect, drag },
|
|
27
|
+
} = useNode((node) => ({ selected: node.events.selected }));
|
|
28
|
+
return (
|
|
29
|
+
<div className={selected ? "selected-node" : ""}>
|
|
30
|
+
<div className={` ${id === "ROOT" ? "root-canvas" : ""}`}>{children}</div>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export /**
|
|
36
|
+
* @returns {div}
|
|
37
|
+
* @category saltcorn-builder
|
|
38
|
+
* @subcategory components
|
|
39
|
+
* @namespace
|
|
40
|
+
*/
|
|
41
|
+
const ListColumnsSettings = () => {
|
|
42
|
+
useNode((node) => ({}));
|
|
43
|
+
return <div></div>;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @type {object}
|
|
48
|
+
*/
|
|
49
|
+
ListColumns.craft = {
|
|
50
|
+
displayName: "ListColumns",
|
|
51
|
+
props: {},
|
|
52
|
+
rules: {
|
|
53
|
+
canDrag: () => false,
|
|
54
|
+
canDrop: () => false,
|
|
55
|
+
canMoveIn: (incoming) => {
|
|
56
|
+
return incoming?.data?.displayName === "ListColumn";
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
related: {
|
|
60
|
+
settings: ListColumnsSettings,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
@@ -179,9 +179,11 @@ const TextSettings = () => {
|
|
|
179
179
|
} = node;
|
|
180
180
|
const { mode, fields } = useContext(optionsCtx);
|
|
181
181
|
const setAProp = setAPropGen(setProp);
|
|
182
|
+
const allowFormula = mode === "show" || mode === "list";
|
|
183
|
+
|
|
182
184
|
return (
|
|
183
185
|
<div>
|
|
184
|
-
{
|
|
186
|
+
{allowFormula && (
|
|
185
187
|
<div className="form-check">
|
|
186
188
|
<input
|
|
187
189
|
type="checkbox"
|
|
@@ -197,7 +199,7 @@ const TextSettings = () => {
|
|
|
197
199
|
</div>
|
|
198
200
|
)}
|
|
199
201
|
<label>Text to display</label>
|
|
200
|
-
{
|
|
202
|
+
{allowFormula && isFormula.text ? (
|
|
201
203
|
<input
|
|
202
204
|
type="text"
|
|
203
205
|
className="text-to-display form-control"
|
|
@@ -9,6 +9,7 @@ import { useNode } from "@craftjs/core";
|
|
|
9
9
|
import optionsCtx from "../context";
|
|
10
10
|
import previewCtx from "../preview_context";
|
|
11
11
|
import relationsCtx from "../relations_context";
|
|
12
|
+
import Select from "react-select";
|
|
12
13
|
|
|
13
14
|
import {
|
|
14
15
|
fetchViewPreview,
|
|
@@ -199,8 +200,8 @@ const ViewSettings = () => {
|
|
|
199
200
|
const helpContext = { view_name: viewname };
|
|
200
201
|
if (options.tableName) helpContext.srcTable = options.tableName;
|
|
201
202
|
const set_view_name = (e) => {
|
|
202
|
-
if (e
|
|
203
|
-
const target_value = e.target.value;
|
|
203
|
+
if (e?.target?.value || e?.value) {
|
|
204
|
+
const target_value = e.target?.value || e.value;
|
|
204
205
|
if (target_value !== viewname) {
|
|
205
206
|
if (options.mode === "filter") {
|
|
206
207
|
setProp((prop) => {
|
|
@@ -237,25 +238,27 @@ const ViewSettings = () => {
|
|
|
237
238
|
}
|
|
238
239
|
}
|
|
239
240
|
};
|
|
240
|
-
|
|
241
|
+
const viewOptions = options.views.map(({ name, label }) => ({
|
|
242
|
+
label,
|
|
243
|
+
value: name,
|
|
244
|
+
}));
|
|
245
|
+
const selectedView = viewOptions.find((v) => v.value === viewname);
|
|
241
246
|
return (
|
|
242
247
|
<div>
|
|
243
248
|
{relationsData ? (
|
|
244
249
|
<Fragment>
|
|
245
250
|
<div>
|
|
246
251
|
<label>View to {options.mode === "show" ? "embed" : "show"}</label>
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
))}
|
|
258
|
-
</select>
|
|
252
|
+
{options.inJestTestingMode ? null : (
|
|
253
|
+
<Select
|
|
254
|
+
options={viewOptions}
|
|
255
|
+
value={selectedView}
|
|
256
|
+
onChange={set_view_name}
|
|
257
|
+
onBlur={set_view_name}
|
|
258
|
+
menuPortalTarget={document.body}
|
|
259
|
+
styles={{ menuPortal: (base) => ({ ...base, zIndex: 19999 }) }}
|
|
260
|
+
></Select>
|
|
261
|
+
)}
|
|
259
262
|
</div>
|
|
260
263
|
{options.mode !== "filter" && (
|
|
261
264
|
<div>
|