@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.
@@ -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
- if (options.setMonaco) return;
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
- options.setMonaco = true;
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
- $(".preview-scratchpad").html(html);
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(configuration[nm]);
1057
- else noshow = noshow || value !== configuration[nm];
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
- <textarea
1335
- rows="6"
1336
- type="text"
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
- <select
1388
- className={`field-${field?.name} form-control form-select`}
1389
- value={value || ""}
1390
- name={field?.name}
1391
- onChange={(e) => e.target && myOnChange(e.target.value)}
1392
- onBlur={(e) => e.target && myOnChange(e.target.value)}
1393
- data-fieldname={field?.name}
1394
- >
1395
- {(field.options || []).map((o, ix) =>
1396
- o.name && o.label ? (
1397
- <option key={ix} value={o.name}>
1398
- {o.label}
1399
- </option>
1400
- ) : o.value && o.label ? (
1401
- <option key={ix} value={o.value}>
1402
- {o.label}
1403
- </option>
1404
- ) : (
1405
- <option key={ix}>{o}</option>
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
- </select>
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(node[nm]);
1548
- else noshow = noshow || value !== node[nm];
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
@@ -152,7 +152,7 @@ const layoutToNodes = (
152
152
  }
153
153
  });
154
154
  if (related.fields.some((f) => f.canBeFormula))
155
- props.isFormula = segment.isFormula;
155
+ props.isFormula = segment.isFormula ?? {};
156
156
  if (related.hasContents)
157
157
  return (
158
158
  <Element