@saltcorn/builder 0.9.4-beta.14 → 0.9.4-beta.15
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
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
|
|
25
25
|
import { RelationBadges } from "./RelationBadges";
|
|
26
26
|
import { RelationOnDemandPicker } from "./RelationOnDemandPicker";
|
|
27
|
+
import Select from "react-select";
|
|
27
28
|
|
|
28
29
|
import {
|
|
29
30
|
RelationsFinder,
|
|
@@ -201,8 +202,8 @@ const ViewLinkSettings = () => {
|
|
|
201
202
|
});
|
|
202
203
|
}
|
|
203
204
|
const set_view_name = (e) => {
|
|
204
|
-
if (e
|
|
205
|
-
const target_value = e.target.value;
|
|
205
|
+
if (e?.target?.value || e?.value) {
|
|
206
|
+
const target_value = e.target?.value || e.value;
|
|
206
207
|
if (target_value !== use_view_name) {
|
|
207
208
|
const newRelations = finder.findRelations(
|
|
208
209
|
tableName,
|
|
@@ -236,6 +237,11 @@ const ViewLinkSettings = () => {
|
|
|
236
237
|
};
|
|
237
238
|
const helpContext = { view_name: use_view_name };
|
|
238
239
|
if (tableName) helpContext.srcTable = tableName;
|
|
240
|
+
const viewOptions = options.views.map(({ name, label }) => ({
|
|
241
|
+
label,
|
|
242
|
+
value: name,
|
|
243
|
+
}));
|
|
244
|
+
const selectedView = viewOptions.find((v) => v.value === use_view_name);
|
|
239
245
|
return (
|
|
240
246
|
<div>
|
|
241
247
|
<table className="w-100">
|
|
@@ -243,18 +249,18 @@ const ViewLinkSettings = () => {
|
|
|
243
249
|
<tr>
|
|
244
250
|
<td colSpan="2">
|
|
245
251
|
<label>View to link to</label>
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
{
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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={{
|
|
260
|
+
menuPortal: (base) => ({ ...base, zIndex: 19999 }),
|
|
261
|
+
}}
|
|
262
|
+
></Select>
|
|
263
|
+
)}
|
|
258
264
|
</td>
|
|
259
265
|
</tr>
|
|
260
266
|
<tr>
|
|
@@ -19,6 +19,7 @@ import faIcons from "./faicons";
|
|
|
19
19
|
import { Columns, ntimes } from "./Columns";
|
|
20
20
|
import Tippy from "@tippyjs/react";
|
|
21
21
|
import { RelationType } from "@saltcorn/common-code";
|
|
22
|
+
import Select from "react-select";
|
|
22
23
|
|
|
23
24
|
export const DynamicFontAwesomeIcon = ({ icon, className }) => {
|
|
24
25
|
if (!icon) return null;
|
|
@@ -139,7 +140,7 @@ export /**
|
|
|
139
140
|
*/
|
|
140
141
|
const OrFormula = ({ setProp, isFormula, node, nodekey, children }) => {
|
|
141
142
|
const { mode } = React.useContext(optionsCtx);
|
|
142
|
-
|
|
143
|
+
const allowFormula = mode === "show" || mode === "list";
|
|
143
144
|
/**
|
|
144
145
|
* @returns {void}
|
|
145
146
|
*/
|
|
@@ -160,14 +161,14 @@ const OrFormula = ({ setProp, isFormula, node, nodekey, children }) => {
|
|
|
160
161
|
});
|
|
161
162
|
};
|
|
162
163
|
let errorString = false;
|
|
163
|
-
if (
|
|
164
|
+
if (allowFormula && isFormula[nodekey]) {
|
|
164
165
|
try {
|
|
165
166
|
Function("return " + node[nodekey]);
|
|
166
167
|
} catch (error) {
|
|
167
168
|
errorString = error.message;
|
|
168
169
|
}
|
|
169
170
|
}
|
|
170
|
-
return
|
|
171
|
+
return !allowFormula ? (
|
|
171
172
|
children
|
|
172
173
|
) : (
|
|
173
174
|
<Fragment>
|
|
@@ -902,24 +903,51 @@ const ConfigField = ({
|
|
|
902
903
|
spellCheck={false}
|
|
903
904
|
/>
|
|
904
905
|
),
|
|
905
|
-
select: () =>
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
906
|
+
select: () => {
|
|
907
|
+
if (field.class?.includes?.("selectizable")) {
|
|
908
|
+
const seloptions = field.options.map((o, ix) =>
|
|
909
|
+
o.name && o.label
|
|
910
|
+
? { value: o.name, label: o.label }
|
|
911
|
+
: { value: o, label: o }
|
|
912
|
+
);
|
|
913
|
+
return (
|
|
914
|
+
<Select
|
|
915
|
+
options={seloptions}
|
|
916
|
+
value={seloptions.find((so) => value === so.value)}
|
|
917
|
+
onChange={(e) =>
|
|
918
|
+
(e.name && myOnChange(e.name)) ||
|
|
919
|
+
(e.value && myOnChange(e.value)) ||
|
|
920
|
+
(typeof e === "string" && myOnChange(e))
|
|
921
|
+
}
|
|
922
|
+
onBlur={(e) =>
|
|
923
|
+
(e.name && myOnChange(e.name)) ||
|
|
924
|
+
(e.value && myOnChange(e.value)) ||
|
|
925
|
+
(typeof e === "string" && myOnChange(e))
|
|
926
|
+
}
|
|
927
|
+
menuPortalTarget={document.body}
|
|
928
|
+
styles={{ menuPortal: (base) => ({ ...base, zIndex: 19999 }) }}
|
|
929
|
+
></Select>
|
|
930
|
+
);
|
|
931
|
+
} else
|
|
932
|
+
return (
|
|
933
|
+
<select
|
|
934
|
+
className="form-control form-select"
|
|
935
|
+
value={value || ""}
|
|
936
|
+
onChange={(e) => e.target && myOnChange(e.target.value)}
|
|
937
|
+
onBlur={(e) => e.target && myOnChange(e.target.value)}
|
|
938
|
+
>
|
|
939
|
+
{field.options.map((o, ix) =>
|
|
940
|
+
o.name && o.label ? (
|
|
941
|
+
<option key={ix} value={o.name}>
|
|
942
|
+
{o.label}
|
|
943
|
+
</option>
|
|
944
|
+
) : (
|
|
945
|
+
<option key={ix}>{o}</option>
|
|
946
|
+
)
|
|
947
|
+
)}
|
|
948
|
+
</select>
|
|
949
|
+
);
|
|
950
|
+
},
|
|
923
951
|
btn_select: () => (
|
|
924
952
|
<div className="btn-group w-100" role="group">
|
|
925
953
|
{field.options.map((o, ix) => (
|
|
@@ -1017,8 +1045,15 @@ export /**
|
|
|
1017
1045
|
* @returns {table}
|
|
1018
1046
|
*/
|
|
1019
1047
|
const SettingsFromFields =
|
|
1020
|
-
(
|
|
1048
|
+
(fieldsIn, opts = {}) =>
|
|
1021
1049
|
() => {
|
|
1050
|
+
const fields = [...fieldsIn];
|
|
1051
|
+
if (opts.additionalFieldsOptionKey) {
|
|
1052
|
+
const options = React.useContext(optionsCtx);
|
|
1053
|
+
|
|
1054
|
+
const addFields = options[opts.additionalFieldsOptionKey];
|
|
1055
|
+
fields.push(...(addFields || []));
|
|
1056
|
+
}
|
|
1022
1057
|
const node = useNode((node) => {
|
|
1023
1058
|
const ps = {};
|
|
1024
1059
|
fields.forEach((f) => {
|
|
@@ -1290,6 +1325,7 @@ const ButtonOrLinkSettingsRows = ({
|
|
|
1290
1325
|
<option value="">Standard</option>
|
|
1291
1326
|
<option value="btn-lg">Large</option>
|
|
1292
1327
|
<option value="btn-sm">Small</option>
|
|
1328
|
+
<option value="btn-sm btn-xs">Extra Small</option>
|
|
1293
1329
|
<option value="btn-block">Block</option>
|
|
1294
1330
|
<option value="btn-block btn-lg">Large block</option>
|
|
1295
1331
|
</select>
|
|
@@ -12,6 +12,8 @@ import { Empty } from "./elements/Empty";
|
|
|
12
12
|
import { Columns, ntimes, sum } from "./elements/Columns";
|
|
13
13
|
import { JoinField } from "./elements/JoinField";
|
|
14
14
|
import { Tabs } from "./elements/Tabs";
|
|
15
|
+
import { ListColumns } from "./elements/ListColumns";
|
|
16
|
+
import { ListColumn } from "./elements/ListColumn";
|
|
15
17
|
import { Table } from "./elements/Table";
|
|
16
18
|
import { Aggregation } from "./elements/Aggregation";
|
|
17
19
|
import { LineBreak } from "./elements/LineBreak";
|
|
@@ -76,6 +78,8 @@ const allElements = [
|
|
|
76
78
|
DropMenu,
|
|
77
79
|
Page,
|
|
78
80
|
Table,
|
|
81
|
+
ListColumn,
|
|
82
|
+
ListColumns,
|
|
79
83
|
];
|
|
80
84
|
|
|
81
85
|
export /**
|
|
@@ -88,7 +92,7 @@ export /**
|
|
|
88
92
|
* @subcategory components
|
|
89
93
|
* @namespace
|
|
90
94
|
*/
|
|
91
|
-
const layoutToNodes = (layout, query, actions, parent = "ROOT") => {
|
|
95
|
+
const layoutToNodes = (layout, query, actions, parent = "ROOT", options) => {
|
|
92
96
|
//console.log("layoutToNodes", JSON.stringify(layout));
|
|
93
97
|
/**
|
|
94
98
|
* @param {object} segment
|
|
@@ -129,7 +133,6 @@ const layoutToNodes = (layout, query, actions, parent = "ROOT") => {
|
|
|
129
133
|
);
|
|
130
134
|
else return <MatchElement key={ix} {...props} />;
|
|
131
135
|
}
|
|
132
|
-
|
|
133
136
|
if (segment.type === "blank") {
|
|
134
137
|
return (
|
|
135
138
|
<Text
|
|
@@ -285,6 +288,27 @@ const layoutToNodes = (layout, query, actions, parent = "ROOT") => {
|
|
|
285
288
|
)}
|
|
286
289
|
/>
|
|
287
290
|
);
|
|
291
|
+
} else if (segment.besides && segment.list_columns) {
|
|
292
|
+
const addFields = options.additionalColumnFields;
|
|
293
|
+
|
|
294
|
+
return segment.besides.map((col, jx) => {
|
|
295
|
+
const addProps = {};
|
|
296
|
+
(addFields || []).forEach((f) => {
|
|
297
|
+
addProps[f.name] = col[f.name];
|
|
298
|
+
});
|
|
299
|
+
return (
|
|
300
|
+
<ListColumn
|
|
301
|
+
key={jx}
|
|
302
|
+
alignment={col.alignment}
|
|
303
|
+
header_label={col.header_label}
|
|
304
|
+
col_width={col.col_width}
|
|
305
|
+
showif={col.showif}
|
|
306
|
+
col_width_units={col.col_width_units}
|
|
307
|
+
contents={toTag(col.contents)}
|
|
308
|
+
{...addProps}
|
|
309
|
+
></ListColumn>
|
|
310
|
+
);
|
|
311
|
+
});
|
|
288
312
|
} else if (segment.besides) {
|
|
289
313
|
return (
|
|
290
314
|
<Columns
|
|
@@ -319,7 +343,7 @@ const layoutToNodes = (layout, query, actions, parent = "ROOT") => {
|
|
|
319
343
|
segment.above.forEach((child) => {
|
|
320
344
|
if (child) go(child, parent);
|
|
321
345
|
});
|
|
322
|
-
} else if (segment.besides) {
|
|
346
|
+
} else if (segment.besides && !segment.list_columns) {
|
|
323
347
|
const node = query
|
|
324
348
|
.parseReactElement(
|
|
325
349
|
<Columns
|
|
@@ -341,7 +365,13 @@ const layoutToNodes = (layout, query, actions, parent = "ROOT") => {
|
|
|
341
365
|
actions.addNodeTree(node, parent);
|
|
342
366
|
} else {
|
|
343
367
|
const tag = toTag(segment);
|
|
344
|
-
if (tag) {
|
|
368
|
+
if (Array.isArray(tag)) {
|
|
369
|
+
tag.forEach((t) => {
|
|
370
|
+
const node = query.parseReactElement(t).toNodeTree();
|
|
371
|
+
//console.log("other", node);
|
|
372
|
+
actions.addNodeTree(node, parent);
|
|
373
|
+
});
|
|
374
|
+
} else if (tag) {
|
|
345
375
|
const node = query.parseReactElement(tag).toNodeTree();
|
|
346
376
|
//console.log("other", node);
|
|
347
377
|
actions.addNodeTree(node, parent);
|
|
@@ -365,7 +395,7 @@ export /**
|
|
|
365
395
|
* @subcategory components
|
|
366
396
|
* @namespace
|
|
367
397
|
*/
|
|
368
|
-
const craftToSaltcorn = (nodes, startFrom = "ROOT") => {
|
|
398
|
+
const craftToSaltcorn = (nodes, startFrom = "ROOT", options) => {
|
|
369
399
|
//console.log(JSON.stringify(nodes, null, 2));
|
|
370
400
|
var columns = [];
|
|
371
401
|
/**
|
|
@@ -421,6 +451,28 @@ const craftToSaltcorn = (nodes, startFrom = "ROOT") => {
|
|
|
421
451
|
}
|
|
422
452
|
return s;
|
|
423
453
|
}
|
|
454
|
+
if (node.displayName === ListColumns.craft.displayName) {
|
|
455
|
+
return {
|
|
456
|
+
besides: node.nodes.map((nm) => go(nodes[nm])),
|
|
457
|
+
list_columns: true,
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
if (node.displayName === ListColumn.craft.displayName) {
|
|
461
|
+
const contents = go(nodes[node.linkedNodes.listcol]);
|
|
462
|
+
const addFields = options.additionalColumnFields;
|
|
463
|
+
const lc = {
|
|
464
|
+
contents,
|
|
465
|
+
col_width: node.props.col_width,
|
|
466
|
+
col_width_units: node.props.col_width_units,
|
|
467
|
+
alignment: node.props.alignment,
|
|
468
|
+
header_label: node.props.header_label,
|
|
469
|
+
showif: node.props.showif,
|
|
470
|
+
};
|
|
471
|
+
(addFields || []).forEach((f) => {
|
|
472
|
+
lc[f.name] = node.props[f.name];
|
|
473
|
+
});
|
|
474
|
+
return lc;
|
|
475
|
+
}
|
|
424
476
|
if (node.isCanvas) {
|
|
425
477
|
if (node.displayName === Container.craft.displayName)
|
|
426
478
|
return {
|