@saltcorn/builder 1.3.1-beta.0 → 1.3.1-beta.10
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 +1 -1
- package/package.json +3 -2
- package/src/components/Builder.js +2 -1
- package/src/components/elements/Action.js +100 -91
- package/src/components/elements/DropMenu.js +2 -2
- package/src/components/elements/Link.js +1 -1
- package/src/components/elements/View.js +2 -0
- package/src/components/elements/ViewLink.js +1 -0
- package/src/components/elements/utils.js +25 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/builder",
|
|
3
|
-
"version": "1.3.1-beta.
|
|
3
|
+
"version": "1.3.1-beta.10",
|
|
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.24.7",
|
|
21
21
|
"@craftjs/core": "0.1.0-beta.20",
|
|
22
22
|
"@craftjs/utils": "0.1.0-beta.20",
|
|
23
|
-
"@saltcorn/common-code": "1.3.1-beta.
|
|
23
|
+
"@saltcorn/common-code": "1.3.1-beta.10",
|
|
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",
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"overrides": {
|
|
59
59
|
"immer": "9.0.6",
|
|
60
60
|
"glob-parent": "^5.1.2",
|
|
61
|
+
"undici": "7.12.0",
|
|
61
62
|
"parse5": "7.2.1",
|
|
62
63
|
"parse5-htmlparser2-tree-adapter": "7.0.0"
|
|
63
64
|
},
|
|
@@ -127,7 +127,8 @@ const SettingsPanel = () => {
|
|
|
127
127
|
*/
|
|
128
128
|
const handleUserKeyPress = (event) => {
|
|
129
129
|
const { keyCode, target } = event;
|
|
130
|
-
|
|
130
|
+
const tagName = target.tagName.toLowerCase();
|
|
131
|
+
if ((tagName === "body" || tagName === "button") && selected) {
|
|
131
132
|
//8 backsp, 46 del
|
|
132
133
|
if ((keyCode === 8 || keyCode === 46) && selected.id === "ROOT") {
|
|
133
134
|
deleteChildren();
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
/*global notifyAlert*/
|
|
7
7
|
|
|
8
|
-
import React, { Fragment, useContext } from "react";
|
|
8
|
+
import React, { Fragment, useContext, useEffect } from "react";
|
|
9
9
|
import { useNode } from "@craftjs/core";
|
|
10
10
|
import optionsCtx from "../context";
|
|
11
11
|
import {
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
} from "./utils";
|
|
23
23
|
import { ntimes } from "./Columns";
|
|
24
24
|
import { ArrayManager } from "./ArrayManager";
|
|
25
|
+
import Select from "react-select";
|
|
25
26
|
|
|
26
27
|
export /**
|
|
27
28
|
*
|
|
@@ -155,6 +156,78 @@ const ActionSettings = () => {
|
|
|
155
156
|
step_action_names?.[use_setting_action_n]
|
|
156
157
|
)}`
|
|
157
158
|
: "";
|
|
159
|
+
const setAction = (value0) => {
|
|
160
|
+
const value = value0.value || value0;
|
|
161
|
+
setProp((prop) => {
|
|
162
|
+
prop.name = value;
|
|
163
|
+
if (options.mode === "filter" && value !== "Clear") {
|
|
164
|
+
const rowRequired =
|
|
165
|
+
options.actionConstraints &&
|
|
166
|
+
options.actionConstraints[value]?.requireRow;
|
|
167
|
+
if (!action_row_variable) {
|
|
168
|
+
prop.action_row_variable = rowRequired ? "state" : "none";
|
|
169
|
+
} else if (rowRequired && action_row_variable === "none") {
|
|
170
|
+
prop.action_row_variable = "state";
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (value === "Multi-step action" && !nsteps) prop.nsteps = 1;
|
|
174
|
+
if (value === "Multi-step action" && !setting_action_n)
|
|
175
|
+
prop.setting_action_n = 0;
|
|
176
|
+
if (value === "Multi-step action" && !configuration.steps)
|
|
177
|
+
prop.configuration = { steps: [] };
|
|
178
|
+
});
|
|
179
|
+
setInitialConfig(setProp, value, getCfgFields(value));
|
|
180
|
+
};
|
|
181
|
+
const setMultistepAction = (value0) => {
|
|
182
|
+
const value = value0.value || value0;
|
|
183
|
+
setProp((prop) => {
|
|
184
|
+
if (!prop.step_action_names) prop.step_action_names = [];
|
|
185
|
+
prop.step_action_names[use_setting_action_n] = value;
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
const actionOptions = options.actions.filter(Boolean).map((f, ix) =>
|
|
189
|
+
f.optgroup && !f.options.length
|
|
190
|
+
? null
|
|
191
|
+
: f.optgroup
|
|
192
|
+
? {
|
|
193
|
+
label: f.label,
|
|
194
|
+
options: f.options.map((a, jx) => ({ label: a, value: a })),
|
|
195
|
+
}
|
|
196
|
+
: { label: f, value: f }
|
|
197
|
+
);
|
|
198
|
+
const selectedAction = { label: name, value: name };
|
|
199
|
+
const multiStepActionOptions = options.actions
|
|
200
|
+
.filter((f) => f && !(options.builtInActions || []).includes(f))
|
|
201
|
+
.map((f, ix) =>
|
|
202
|
+
f.optgroup && !f.options.length
|
|
203
|
+
? null
|
|
204
|
+
: f.optgroup
|
|
205
|
+
? {
|
|
206
|
+
label: f.label,
|
|
207
|
+
options: f.options
|
|
208
|
+
.filter(
|
|
209
|
+
(f) =>
|
|
210
|
+
![
|
|
211
|
+
"Multi-step action",
|
|
212
|
+
...(options.builtInActions || []),
|
|
213
|
+
].includes(f)
|
|
214
|
+
)
|
|
215
|
+
.map((a, jx) => ({ label: a, value: a })),
|
|
216
|
+
}
|
|
217
|
+
: { label: f, value: f }
|
|
218
|
+
);
|
|
219
|
+
const selectedMultiStepAction = {
|
|
220
|
+
label: step_action_names?.[use_setting_action_n] || "",
|
|
221
|
+
value: step_action_names?.[use_setting_action_n] || "",
|
|
222
|
+
};
|
|
223
|
+
useEffect(() => {
|
|
224
|
+
apply_showif();
|
|
225
|
+
}, [
|
|
226
|
+
name,
|
|
227
|
+
step_action_names?.[use_setting_action_n] || "",
|
|
228
|
+
JSON.stringify(configuration?.steps?.[use_setting_action_n]),
|
|
229
|
+
]);
|
|
230
|
+
|
|
158
231
|
return (
|
|
159
232
|
<div>
|
|
160
233
|
<table className="w-100">
|
|
@@ -164,55 +237,19 @@ const ActionSettings = () => {
|
|
|
164
237
|
<label>Action</label>
|
|
165
238
|
</td>
|
|
166
239
|
<td>
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
prop.action_row_variable = rowRequired
|
|
181
|
-
? "state"
|
|
182
|
-
: "none";
|
|
183
|
-
} else if (
|
|
184
|
-
rowRequired &&
|
|
185
|
-
action_row_variable === "none"
|
|
186
|
-
) {
|
|
187
|
-
prop.action_row_variable = "state";
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
if (value === "Multi-step action" && !nsteps)
|
|
191
|
-
prop.nsteps = 1;
|
|
192
|
-
if (value === "Multi-step action" && !setting_action_n)
|
|
193
|
-
prop.setting_action_n = 0;
|
|
194
|
-
if (value === "Multi-step action" && !configuration.steps)
|
|
195
|
-
prop.configuration = { steps: [] };
|
|
196
|
-
});
|
|
197
|
-
setInitialConfig(setProp, value, getCfgFields(value));
|
|
198
|
-
}}
|
|
199
|
-
>
|
|
200
|
-
{options.actions.filter(Boolean).map((f, ix) =>
|
|
201
|
-
f.optgroup && !f.options.length ? null : f.optgroup ? (
|
|
202
|
-
<optgroup key={ix} label={f.label}>
|
|
203
|
-
{f.options.map((a, jx) => (
|
|
204
|
-
<option key={jx} value={a}>
|
|
205
|
-
{a}
|
|
206
|
-
</option>
|
|
207
|
-
))}
|
|
208
|
-
</optgroup>
|
|
209
|
-
) : (
|
|
210
|
-
<option key={ix} value={f}>
|
|
211
|
-
{f}
|
|
212
|
-
</option>
|
|
213
|
-
)
|
|
214
|
-
)}
|
|
215
|
-
</select>
|
|
240
|
+
{options.inJestTestingMode ? null : (
|
|
241
|
+
<Select
|
|
242
|
+
options={actionOptions}
|
|
243
|
+
className="react-select action-selector"
|
|
244
|
+
value={selectedAction}
|
|
245
|
+
defaultValue={selectedAction}
|
|
246
|
+
onChange={setAction}
|
|
247
|
+
menuPortalTarget={document.body}
|
|
248
|
+
styles={{
|
|
249
|
+
menuPortal: (base) => ({ ...base, zIndex: 19999 }),
|
|
250
|
+
}}
|
|
251
|
+
></Select>
|
|
252
|
+
)}
|
|
216
253
|
</td>
|
|
217
254
|
</tr>
|
|
218
255
|
{name !== "Clear" && options.mode === "filter" ? (
|
|
@@ -344,47 +381,19 @@ const ActionSettings = () => {
|
|
|
344
381
|
></ArrayManager>
|
|
345
382
|
|
|
346
383
|
<label>Action</label>
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
Select action...
|
|
361
|
-
</option>
|
|
362
|
-
{options.actions
|
|
363
|
-
.filter((f) => !(options.builtInActions || []).includes(f))
|
|
364
|
-
.map((f, ix) =>
|
|
365
|
-
f.optgroup ? (
|
|
366
|
-
<optgroup key={ix} label={f.label}>
|
|
367
|
-
{f.options
|
|
368
|
-
.filter(
|
|
369
|
-
(f) =>
|
|
370
|
-
![
|
|
371
|
-
"Multi-step action",
|
|
372
|
-
...(options.builtInActions || []),
|
|
373
|
-
].includes(f)
|
|
374
|
-
)
|
|
375
|
-
.map((a, jx) => (
|
|
376
|
-
<option key={jx} value={a}>
|
|
377
|
-
{a}
|
|
378
|
-
</option>
|
|
379
|
-
))}
|
|
380
|
-
</optgroup>
|
|
381
|
-
) : (
|
|
382
|
-
<option key={ix} value={f}>
|
|
383
|
-
{f}
|
|
384
|
-
</option>
|
|
385
|
-
)
|
|
386
|
-
)}
|
|
387
|
-
</select>
|
|
384
|
+
{options.inJestTestingMode ? null : (
|
|
385
|
+
<Select
|
|
386
|
+
options={multiStepActionOptions}
|
|
387
|
+
className="react-select multistep-action-selector"
|
|
388
|
+
value={selectedMultiStepAction}
|
|
389
|
+
defaultValue={selectedMultiStepAction}
|
|
390
|
+
onChange={setMultistepAction}
|
|
391
|
+
menuPortalTarget={document.body}
|
|
392
|
+
styles={{
|
|
393
|
+
menuPortal: (base) => ({ ...base, zIndex: 19999 }),
|
|
394
|
+
}}
|
|
395
|
+
></Select>
|
|
396
|
+
)}
|
|
388
397
|
{options.mode !== "page" ? (
|
|
389
398
|
<Fragment>
|
|
390
399
|
<label>Only if... (formula)</label>
|
|
@@ -51,11 +51,11 @@ const DropMenu = ({
|
|
|
51
51
|
//const [dropWidth, setDropWidth] = useState(200);
|
|
52
52
|
return (
|
|
53
53
|
<div
|
|
54
|
-
className={`${selected ? "selected-node" : ""} ${block ? "d-block" : ""}`}
|
|
54
|
+
className={`${selected ? "selected-node" : ""} ${block ? "d-block" : "d-inline"}`}
|
|
55
55
|
ref={(dom) => connect(drag(dom))}
|
|
56
56
|
>
|
|
57
57
|
<button
|
|
58
|
-
className={`btn ${action_style || "btn-primary"} ${action_size || ""} `}
|
|
58
|
+
className={`btn ${action_style || "btn-primary"} d-inline-block ${action_size || ""} `}
|
|
59
59
|
style={
|
|
60
60
|
action_style === "btn-custom-color"
|
|
61
61
|
? {
|
|
@@ -67,7 +67,7 @@ const Link = ({
|
|
|
67
67
|
<button
|
|
68
68
|
className={`${textStyle} is-builder-link ${
|
|
69
69
|
selected ? "selected-node" : ""
|
|
70
|
-
} ${isFormula?.text ? "font-monospace" : ""} ${link_style} ${
|
|
70
|
+
} ${isFormula?.text ? "font-monospace" : ""} ${link_style} ${link_style && link_style.includes("btn") ? "d-inline-block" : ""} ${
|
|
71
71
|
link_size || ""
|
|
72
72
|
} ${block ? "d-block" : ""}`}
|
|
73
73
|
ref={(dom) => connect(drag(dom))}
|
|
@@ -261,6 +261,7 @@ const ViewSettings = () => {
|
|
|
261
261
|
<Select
|
|
262
262
|
options={viewOptions}
|
|
263
263
|
value={selectedView}
|
|
264
|
+
className="react-select view-selector"
|
|
264
265
|
onChange={set_view_name}
|
|
265
266
|
onBlur={set_view_name}
|
|
266
267
|
menuPortalTarget={document.body}
|
|
@@ -302,6 +303,7 @@ const ViewSettings = () => {
|
|
|
302
303
|
<Select
|
|
303
304
|
options={viewOptions}
|
|
304
305
|
value={selectedView}
|
|
306
|
+
className="react-select view-selector"
|
|
305
307
|
onChange={(e) => {
|
|
306
308
|
const target_value = e?.target?.value || e?.value;
|
|
307
309
|
setProp((prop) => {
|
|
@@ -132,15 +132,19 @@ export const FormulaTooltip = () => {
|
|
|
132
132
|
))}
|
|
133
133
|
</Fragment>
|
|
134
134
|
) : null}
|
|
135
|
-
|
|
135
|
+
|
|
136
136
|
<div>
|
|
137
|
-
In view formulae, you can use aggregation formulae. The syntax for this
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
137
|
+
In view formulae, you can use aggregation formulae. The syntax for this
|
|
138
|
+
is
|
|
139
|
+
<code>
|
|
140
|
+
[inbound_table]$[inboundkey_field]$[target_field]$[aggrgation]
|
|
141
|
+
</code>
|
|
142
|
+
The aggregation (which should be lower case) can be ommitted and
|
|
143
|
+
defaults to
|
|
144
|
+
<code>array_agg</code>. Examples: <code>patients$favbook$id$count</code>{" "}
|
|
145
|
+
or
|
|
146
|
+
<code>patients$favbook$id</code>. This is useful if you want a count in
|
|
147
|
+
a view link label without creating a stored calculated field.
|
|
144
148
|
</div>
|
|
145
149
|
<a
|
|
146
150
|
className="d-block"
|
|
@@ -786,7 +790,7 @@ const ConfigForm = ({
|
|
|
786
790
|
tableName,
|
|
787
791
|
fieldName,
|
|
788
792
|
}) => (
|
|
789
|
-
<div>
|
|
793
|
+
<div className="form-namespace">
|
|
790
794
|
{fields.map((f, ix) => {
|
|
791
795
|
if (f.showIf && configuration) {
|
|
792
796
|
let noshow = false;
|
|
@@ -948,6 +952,7 @@ const ConfigField = ({
|
|
|
948
952
|
return (
|
|
949
953
|
<select
|
|
950
954
|
className={`field-${field?.name} form-control form-select`}
|
|
955
|
+
name={field?.name}
|
|
951
956
|
value={value || ""}
|
|
952
957
|
onChange={(e) => e.target && myOnChange(e.target.value)}
|
|
953
958
|
onBlur={(e) => e.target && myOnChange(e.target.value)}
|
|
@@ -966,6 +971,7 @@ const ConfigField = ({
|
|
|
966
971
|
return (
|
|
967
972
|
<input
|
|
968
973
|
type="text"
|
|
974
|
+
name={field?.name}
|
|
969
975
|
className={`field-${field?.name} form-control`}
|
|
970
976
|
value={value || ""}
|
|
971
977
|
spellCheck={false}
|
|
@@ -977,6 +983,7 @@ const ConfigField = ({
|
|
|
977
983
|
<select
|
|
978
984
|
className="fontselect form-control form-select"
|
|
979
985
|
value={value || ""}
|
|
986
|
+
name={field?.name}
|
|
980
987
|
onChange={(e) => e.target && myOnChange(e.target.value)}
|
|
981
988
|
onBlur={(e) => e.target && myOnChange(e.target.value)}
|
|
982
989
|
>
|
|
@@ -997,6 +1004,7 @@ const ConfigField = ({
|
|
|
997
1004
|
step={field.step || 1}
|
|
998
1005
|
min={field.min}
|
|
999
1006
|
max={field.max}
|
|
1007
|
+
name={field?.name}
|
|
1000
1008
|
value={value || ""}
|
|
1001
1009
|
onChange={(e) => e.target && myOnChange(e.target.value)}
|
|
1002
1010
|
/>
|
|
@@ -1007,6 +1015,7 @@ const ConfigField = ({
|
|
|
1007
1015
|
className={`field-${field?.name} form-control`}
|
|
1008
1016
|
value={value || ""}
|
|
1009
1017
|
step={0.01}
|
|
1018
|
+
name={field?.name}
|
|
1010
1019
|
max={or_if_undef(field?.attributes?.max, undefined)}
|
|
1011
1020
|
min={or_if_undef(field?.attributes?.min, undefined)}
|
|
1012
1021
|
onChange={(e) => e.target && myOnChange(e.target.value)}
|
|
@@ -1019,6 +1028,7 @@ const ConfigField = ({
|
|
|
1019
1028
|
type="checkbox"
|
|
1020
1029
|
className={`field-${field?.name} form-check-input`}
|
|
1021
1030
|
checked={value}
|
|
1031
|
+
name={field?.name}
|
|
1022
1032
|
onChange={(e) => e.target && myOnChange(e.target.checked)}
|
|
1023
1033
|
/>
|
|
1024
1034
|
<label className="form-check-label">{field.label}</label>
|
|
@@ -1030,6 +1040,7 @@ const ConfigField = ({
|
|
|
1030
1040
|
type="text"
|
|
1031
1041
|
className={`field-${field?.name} form-control`}
|
|
1032
1042
|
value={value}
|
|
1043
|
+
name={field?.name}
|
|
1033
1044
|
spellCheck={false}
|
|
1034
1045
|
onChange={(e) => e.target && myOnChange(e.target.value)}
|
|
1035
1046
|
/>
|
|
@@ -1040,6 +1051,7 @@ const ConfigField = ({
|
|
|
1040
1051
|
type="text"
|
|
1041
1052
|
className={`field-${field?.name} form-control`}
|
|
1042
1053
|
value={value}
|
|
1054
|
+
name={field?.name}
|
|
1043
1055
|
onChange={(e) => e.target && myOnChange(e.target.value)}
|
|
1044
1056
|
spellCheck={false}
|
|
1045
1057
|
/>
|
|
@@ -1056,12 +1068,14 @@ const ConfigField = ({
|
|
|
1056
1068
|
return (
|
|
1057
1069
|
<Select
|
|
1058
1070
|
options={seloptions}
|
|
1071
|
+
className="react-select selectized-field"
|
|
1059
1072
|
value={seloptions.find((so) => value === so.value)}
|
|
1060
1073
|
onChange={(e) =>
|
|
1061
1074
|
(e.name && myOnChange(e.name)) ||
|
|
1062
1075
|
(e.value && myOnChange(e.value)) ||
|
|
1063
1076
|
(typeof e === "string" && myOnChange(e))
|
|
1064
1077
|
}
|
|
1078
|
+
name={field?.name}
|
|
1065
1079
|
onBlur={(e) =>
|
|
1066
1080
|
(e.name && myOnChange(e.name)) ||
|
|
1067
1081
|
(e.value && myOnChange(e.value)) ||
|
|
@@ -1076,6 +1090,7 @@ const ConfigField = ({
|
|
|
1076
1090
|
<select
|
|
1077
1091
|
className={`field-${field?.name} form-control form-select`}
|
|
1078
1092
|
value={value || ""}
|
|
1093
|
+
name={field?.name}
|
|
1079
1094
|
onChange={(e) => e.target && myOnChange(e.target.value)}
|
|
1080
1095
|
onBlur={(e) => e.target && myOnChange(e.target.value)}
|
|
1081
1096
|
>
|
|
@@ -1790,8 +1805,7 @@ export const buildLayers = (relations, tableName, tableNameCache) => {
|
|
|
1790
1805
|
fkeys: [],
|
|
1791
1806
|
relPath: relation.relationString,
|
|
1792
1807
|
});
|
|
1793
|
-
}
|
|
1794
|
-
else {
|
|
1808
|
+
} else {
|
|
1795
1809
|
let currentTbl = relation.sourceTblName;
|
|
1796
1810
|
for (const pathElement of relation.path) {
|
|
1797
1811
|
if (pathElement.inboundKey) {
|