@saltcorn/builder 1.6.0-beta.9 → 1.6.0-rc.2
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 +7 -7
- package/dist/builder_bundle.js.LICENSE.txt +67 -0
- package/package.json +3 -3
- package/src/components/Builder.js +102 -21
- package/src/components/Library.js +79 -6
- package/src/components/RenderNode.js +20 -10
- package/src/components/elements/Action.js +8 -0
- package/src/components/elements/Aggregation.js +1 -1
- package/src/components/elements/Card.js +10 -10
- package/src/components/elements/Clone.js +2 -2
- package/src/components/elements/Container.js +1 -1
- package/src/components/elements/CustomLayer.js +8 -3
- package/src/components/elements/Link.js +4 -4
- package/src/components/elements/ListColumn.js +2 -4
- package/src/components/elements/MonacoEditor.js +45 -11
- package/src/components/elements/utils.js +52 -42
- package/src/components/storage.js +1 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import React, { Fragment, useState, useEffect, useRef } from "react";
|
|
2
2
|
import optionsCtx from "../context";
|
|
3
3
|
|
|
4
|
-
import Editor, { useMonaco } from "@monaco-editor/react";
|
|
4
|
+
import Editor, { useMonaco, loader } from "@monaco-editor/react";
|
|
5
|
+
|
|
6
|
+
loader.config({ paths: { vs: "/monaco" } });
|
|
5
7
|
|
|
6
8
|
export const mimeToMonacoLanguage = (mode) => {
|
|
7
9
|
if (!mode) return "typescript";
|
|
@@ -21,16 +23,19 @@ export const mimeToMonacoLanguage = (mode) => {
|
|
|
21
23
|
return map[mode] || mode;
|
|
22
24
|
};
|
|
23
25
|
|
|
24
|
-
const setMonacoLanguage = async (monaco, options, isStatements) => {
|
|
26
|
+
const setMonacoLanguage = async (monaco, options, isStatements, nojoins) => {
|
|
25
27
|
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
|
26
28
|
noLib: true,
|
|
27
29
|
allowNonTsExtensions: true,
|
|
28
30
|
});
|
|
29
|
-
|
|
31
|
+
// Separate cache keys so nojoins and full-join contexts fetch independently
|
|
32
|
+
const cacheKey = nojoins ? "setMonacoNoJoins" : "setMonaco";
|
|
33
|
+
if (options[cacheKey]) return;
|
|
34
|
+
options[cacheKey] = true;
|
|
30
35
|
|
|
31
|
-
|
|
36
|
+
const nojoinsPart = nojoins ? "&nojoins=yes" : "";
|
|
32
37
|
const tsres = await fetch(
|
|
33
|
-
`/admin/ts-declares?${options.tableName ? `table=${options.tableName}` : ""}&user=yes`
|
|
38
|
+
`/admin/ts-declares?${options.tableName ? `table=${options.tableName}` : ""}&user=yes${nojoinsPart}`
|
|
34
39
|
);
|
|
35
40
|
const tsds = await tsres.text();
|
|
36
41
|
|
|
@@ -105,7 +110,7 @@ const setupVirtualPrefix = (editor, monaco) => {
|
|
|
105
110
|
|
|
106
111
|
export const SingleLineEditor = React.forwardRef(
|
|
107
112
|
(
|
|
108
|
-
{ setProp, value, propKey, onChange, onInput, className, stateExpr },
|
|
113
|
+
{ setProp, value, propKey, onChange, onInput, className, stateExpr, placeholder },
|
|
109
114
|
ref
|
|
110
115
|
) => {
|
|
111
116
|
const options = React.useContext(optionsCtx);
|
|
@@ -133,9 +138,22 @@ export const SingleLineEditor = React.forwardRef(
|
|
|
133
138
|
: value;
|
|
134
139
|
|
|
135
140
|
return (
|
|
136
|
-
<div ref={ref} className="form-control p-0 pt-1">
|
|
141
|
+
<div ref={ref} className="form-control p-0 pt-1" style={{ position: "relative" }}>
|
|
142
|
+
{isEmpty && !activePrefix && placeholder && (
|
|
143
|
+
<div style={{
|
|
144
|
+
position: "absolute",
|
|
145
|
+
top: "3px",
|
|
146
|
+
left: "14px",
|
|
147
|
+
color: "#999",
|
|
148
|
+
pointerEvents: "none",
|
|
149
|
+
zIndex: 1,
|
|
150
|
+
fontSize: "14px",
|
|
151
|
+
whiteSpace: "nowrap",
|
|
152
|
+
}}>
|
|
153
|
+
{placeholder}
|
|
154
|
+
</div>
|
|
155
|
+
)}
|
|
137
156
|
<Editor
|
|
138
|
-
placeholder={"sdfffsd"}
|
|
139
157
|
className={className || ""}
|
|
140
158
|
height="22px"
|
|
141
159
|
value={editorValue}
|
|
@@ -161,14 +179,14 @@ export const SingleLineEditor = React.forwardRef(
|
|
|
161
179
|
}
|
|
162
180
|
);
|
|
163
181
|
|
|
164
|
-
export const MultiLineCodeEditor = ({ setProp, value, onChange, isModalEditor = false, mode }) => {
|
|
182
|
+
export const MultiLineCodeEditor = ({ setProp, value, onChange, isModalEditor = false, mode, placeholder, nojoins }) => {
|
|
165
183
|
const options = React.useContext(optionsCtx);
|
|
166
184
|
const resolvedLanguage = mimeToMonacoLanguage(mode);
|
|
167
185
|
const useTypeScriptSetup = resolvedLanguage === "typescript" || resolvedLanguage === "javascript";
|
|
168
186
|
|
|
169
187
|
const handleEditorWillMount = (monaco) => {
|
|
170
188
|
if (useTypeScriptSetup) {
|
|
171
|
-
setMonacoLanguage(monaco, options, true);
|
|
189
|
+
setMonacoLanguage(monaco, options, true, nojoins);
|
|
172
190
|
}
|
|
173
191
|
};
|
|
174
192
|
|
|
@@ -181,8 +199,24 @@ export const MultiLineCodeEditor = ({ setProp, value, onChange, isModalEditor =
|
|
|
181
199
|
}
|
|
182
200
|
};
|
|
183
201
|
|
|
202
|
+
const isEmpty = !value || value.trim() === "";
|
|
203
|
+
const resolvedPlaceholder = placeholder || "// enter code here";
|
|
204
|
+
|
|
184
205
|
return (
|
|
185
|
-
<div className="form-control p-0 pt-2">
|
|
206
|
+
<div className="form-control p-0 pt-2" style={{ position: "relative" }}>
|
|
207
|
+
{isEmpty && !isModalEditor && (
|
|
208
|
+
<div style={{
|
|
209
|
+
position: "absolute",
|
|
210
|
+
top: "10px",
|
|
211
|
+
left: "14px",
|
|
212
|
+
color: "#999",
|
|
213
|
+
pointerEvents: "none",
|
|
214
|
+
zIndex: 1,
|
|
215
|
+
fontSize: "14px",
|
|
216
|
+
}}>
|
|
217
|
+
{resolvedPlaceholder}
|
|
218
|
+
</div>
|
|
219
|
+
)}
|
|
186
220
|
<Editor
|
|
187
221
|
height={isModalEditor ? "100%" : "150px"}
|
|
188
222
|
value={value}
|
|
@@ -707,7 +707,8 @@ const fetchPreview = ({ url, body, options, setPreviews, node_id, isView }) => {
|
|
|
707
707
|
else return "";
|
|
708
708
|
})
|
|
709
709
|
.then(function (html) {
|
|
710
|
-
|
|
710
|
+
const scratchpad = document.querySelector(".preview-scratchpad");
|
|
711
|
+
if (scratchpad) scratchpad.innerHTML = html;
|
|
711
712
|
$(".preview-scratchpad").find("iframe").css("pointer-events", "none");
|
|
712
713
|
$(".preview-scratchpad").find("a").attr("href", "#");
|
|
713
714
|
$(".preview-scratchpad")
|
|
@@ -936,7 +937,7 @@ const ColorInput = ({ value, onChange }) =>
|
|
|
936
937
|
</button>
|
|
937
938
|
);
|
|
938
939
|
|
|
939
|
-
const CodeFieldWithModal = ({ value, onChange, setProp, mode, label, hideLabel }) => {
|
|
940
|
+
const CodeFieldWithModal = ({ value, onChange, setProp, mode, label, hideLabel, placeholder, nojoins }) => {
|
|
940
941
|
const [modalOpen, setModalOpen] = useState(false);
|
|
941
942
|
const { t } = useTranslation();
|
|
942
943
|
return (
|
|
@@ -965,6 +966,8 @@ const CodeFieldWithModal = ({ value, onChange, setProp, mode, label, hideLabel }
|
|
|
965
966
|
value={value}
|
|
966
967
|
onChange={onChange}
|
|
967
968
|
mode={mode}
|
|
969
|
+
placeholder={placeholder}
|
|
970
|
+
nojoins={nojoins}
|
|
968
971
|
/>
|
|
969
972
|
{modalOpen ? (
|
|
970
973
|
<div
|
|
@@ -1052,15 +1055,17 @@ const ConfigForm = ({
|
|
|
1052
1055
|
if (f.showIf && configuration) {
|
|
1053
1056
|
let noshow = false;
|
|
1054
1057
|
Object.entries(f.showIf).forEach(([nm, value]) => {
|
|
1058
|
+
const cfgVal = configuration[nm];
|
|
1059
|
+
const effectiveVal = cfgVal === undefined ? false : cfgVal;
|
|
1055
1060
|
if (Array.isArray(value))
|
|
1056
|
-
noshow = noshow || !value.includes(
|
|
1057
|
-
else noshow = noshow || value !==
|
|
1061
|
+
noshow = noshow || !value.includes(effectiveVal);
|
|
1062
|
+
else noshow = noshow || value !== effectiveVal;
|
|
1058
1063
|
});
|
|
1059
1064
|
if (noshow) return null;
|
|
1060
1065
|
}
|
|
1061
1066
|
return (
|
|
1062
1067
|
<div key={ix} className="builder-config-field" data-field-name={f.name}>
|
|
1063
|
-
{!isCheckbox(f) && f.input_type !== "code" ? (
|
|
1068
|
+
{!isCheckbox(f) && (f.input_type !== "code" || f.attributes?.singleline) ? (
|
|
1064
1069
|
<label>
|
|
1065
1070
|
{f.label || f.name}
|
|
1066
1071
|
{f.help ? (
|
|
@@ -1326,19 +1331,11 @@ const ConfigField = ({
|
|
|
1326
1331
|
/>
|
|
1327
1332
|
),
|
|
1328
1333
|
code: () => {
|
|
1329
|
-
if (
|
|
1330
|
-
field?.attributes?.expression_type === "row" ||
|
|
1331
|
-
field?.attributes?.expression_type === "query"
|
|
1332
|
-
) {
|
|
1334
|
+
if (field?.attributes?.singleline) {
|
|
1333
1335
|
return (
|
|
1334
|
-
<
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
className={`field-${field?.name} form-control`}
|
|
1338
|
-
value={value}
|
|
1339
|
-
name={field?.name}
|
|
1340
|
-
onChange={(e) => e.target && myOnChange(e.target.value)}
|
|
1341
|
-
spellCheck={false}
|
|
1336
|
+
<SingleLineEditor
|
|
1337
|
+
value={value || ""}
|
|
1338
|
+
onChange={myOnChange}
|
|
1342
1339
|
/>
|
|
1343
1340
|
);
|
|
1344
1341
|
}
|
|
@@ -1349,6 +1346,8 @@ const ConfigField = ({
|
|
|
1349
1346
|
setProp={setProp}
|
|
1350
1347
|
mode={field?.attributes?.mode}
|
|
1351
1348
|
label={field?.label || field?.name || "Code"}
|
|
1349
|
+
placeholder={field?.attributes?.placeholder}
|
|
1350
|
+
nojoins={field?.attributes?.nojoins}
|
|
1352
1351
|
/>
|
|
1353
1352
|
);
|
|
1354
1353
|
},
|
|
@@ -1382,31 +1381,40 @@ const ConfigField = ({
|
|
|
1382
1381
|
styles={reactSelectStyles()}
|
|
1383
1382
|
></Select>
|
|
1384
1383
|
);
|
|
1385
|
-
} else
|
|
1384
|
+
} else {
|
|
1385
|
+
const explainerText = field.attributes?.explainers?.[value || ""];
|
|
1386
1386
|
return (
|
|
1387
|
-
<
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
{o.
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
{o.
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1387
|
+
<Fragment>
|
|
1388
|
+
<select
|
|
1389
|
+
className={`field-${field?.name} form-control form-select`}
|
|
1390
|
+
value={value || ""}
|
|
1391
|
+
name={field?.name}
|
|
1392
|
+
onChange={(e) => e.target && myOnChange(e.target.value)}
|
|
1393
|
+
onBlur={(e) => e.target && myOnChange(e.target.value)}
|
|
1394
|
+
data-fieldname={field?.name}
|
|
1395
|
+
>
|
|
1396
|
+
{(field.options || []).map((o, ix) =>
|
|
1397
|
+
o.name && o.label ? (
|
|
1398
|
+
<option key={ix} value={o.name}>
|
|
1399
|
+
{o.label}
|
|
1400
|
+
</option>
|
|
1401
|
+
) : o.value && o.label ? (
|
|
1402
|
+
<option key={ix} value={o.value}>
|
|
1403
|
+
{o.label}
|
|
1404
|
+
</option>
|
|
1405
|
+
) : (
|
|
1406
|
+
<option key={ix}>{o}</option>
|
|
1407
|
+
)
|
|
1408
|
+
)}
|
|
1409
|
+
</select>
|
|
1410
|
+
{explainerText && (
|
|
1411
|
+
<div className="alert alert-info py-1 px-2 my-1 small">
|
|
1412
|
+
<strong>{value}</strong>: {explainerText}
|
|
1413
|
+
</div>
|
|
1407
1414
|
)}
|
|
1408
|
-
</
|
|
1415
|
+
</Fragment>
|
|
1409
1416
|
);
|
|
1417
|
+
}
|
|
1410
1418
|
},
|
|
1411
1419
|
btn_select: () => (
|
|
1412
1420
|
<div className="btn-group w-100" role="group">
|
|
@@ -1543,9 +1551,11 @@ const SettingsFromFields =
|
|
|
1543
1551
|
if (f.showIf) {
|
|
1544
1552
|
let noshow = false;
|
|
1545
1553
|
Object.entries(f.showIf).forEach(([nm, value]) => {
|
|
1554
|
+
const cfgVal = node[nm];
|
|
1555
|
+
const effectiveVal = cfgVal === undefined ? false : cfgVal;
|
|
1546
1556
|
if (Array.isArray(value))
|
|
1547
|
-
noshow = noshow || !value.includes(
|
|
1548
|
-
else noshow = noshow || value !==
|
|
1557
|
+
noshow = noshow || !value.includes(effectiveVal);
|
|
1558
|
+
else noshow = noshow || value !== effectiveVal;
|
|
1549
1559
|
});
|
|
1550
1560
|
if (noshow) return null;
|
|
1551
1561
|
}
|
|
@@ -1630,7 +1640,7 @@ const SettingsRow = ({
|
|
|
1630
1640
|
<tr>
|
|
1631
1641
|
{fullWidth ? (
|
|
1632
1642
|
<td colSpan="2">
|
|
1633
|
-
{needLabel && field.input_type !== "code" && <label>{field.label}</label>}
|
|
1643
|
+
{needLabel && (field.input_type !== "code" || field.attributes?.singleline) && <label>{field.label}</label>}
|
|
1634
1644
|
{inner}
|
|
1635
1645
|
{field.sublabel ? (
|
|
1636
1646
|
<i
|