@saltcorn/builder 1.1.3-beta.9 → 1.1.3-rc.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saltcorn/builder",
3
- "version": "1.1.3-beta.9",
3
+ "version": "1.1.3-rc.0",
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.1.3-beta.9",
23
+ "@saltcorn/common-code": "1.1.3-rc.0",
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",
@@ -72,10 +72,10 @@ const Action = ({
72
72
  color: action_textcol || "#000000",
73
73
  }
74
74
  : action_style === "on_page_load"
75
- ? {
76
- border: "1px red dashed",
77
- }
78
- : {}
75
+ ? {
76
+ border: "1px red dashed",
77
+ }
78
+ : {}
79
79
  }
80
80
  >
81
81
  <DynamicFontAwesomeIcon icon={action_icon} className="me-1" />
@@ -134,7 +134,6 @@ const ActionSettings = () => {
134
134
  step_action_names,
135
135
  spinner,
136
136
  is_submit_action,
137
-
138
137
  } = node;
139
138
  const options = useContext(optionsCtx);
140
139
  const getCfgFields = (fv) => (options.actionConfigForms || {})[fv];
@@ -149,13 +148,13 @@ const ActionSettings = () => {
149
148
  const cfg_link = (options.triggerActions || []).includes(name)
150
149
  ? `/actions/configure/${encodeURIComponent(name)}`
151
150
  : name === "Multi-step action" &&
152
- (options.triggerActions || []).includes(
153
- step_action_names?.[use_setting_action_n]
154
- )
155
- ? `/actions/configure/${encodeURIComponent(
156
- step_action_names?.[use_setting_action_n]
157
- )}`
158
- : "";
151
+ (options.triggerActions || []).includes(
152
+ step_action_names?.[use_setting_action_n]
153
+ )
154
+ ? `/actions/configure/${encodeURIComponent(
155
+ step_action_names?.[use_setting_action_n]
156
+ )}`
157
+ : "";
159
158
  return (
160
159
  <div>
161
160
  <table className="w-100">
@@ -393,6 +392,7 @@ const ActionSettings = () => {
393
392
  type="text"
394
393
  className="form-control text-to-display"
395
394
  value={step_only_ifs?.[use_setting_action_n] || ""}
395
+ spellCheck={false}
396
396
  onChange={(e) => {
397
397
  if (!e.target) return;
398
398
  const value = e.target.value;
@@ -83,10 +83,10 @@ const AggregationSettings = () => {
83
83
  stat === "Percent true" || stat === "Percent false"
84
84
  ? "Float"
85
85
  : stat === "Count" || stat === "CountUnique"
86
- ? "Integer"
87
- : stat === "Array_Agg"
88
- ? "Array"
89
- : targetFieldType;
86
+ ? "Integer"
87
+ : stat === "Array_Agg"
88
+ ? "Array"
89
+ : targetFieldType;
90
90
  const fvs = options.agg_fieldview_options[outcomeType];
91
91
 
92
92
  const [fetchedCfgFields, setFetchedCfgFields] = useState([]);
@@ -107,7 +107,7 @@ const AggregationSettings = () => {
107
107
  agg_outcome_type: outcomeType,
108
108
  agg_fieldview,
109
109
  agg_field: targetField?.name,
110
- mode: options?.mode
110
+ mode: options?.mode,
111
111
  }),
112
112
  }
113
113
  )
@@ -233,7 +233,9 @@ const AggregationSettings = () => {
233
233
  type="text"
234
234
  className="form-control"
235
235
  value={aggwhere}
236
+ spellCheck={false}
236
237
  onChange={setAProp("aggwhere")}
238
+ onInput={(e) => validate_expression_elem($(e.target))}
237
239
  />
238
240
  </td>
239
241
  </tr>
@@ -629,6 +629,7 @@ const ContainerSettings = () => {
629
629
  value={imgResponsiveWidths}
630
630
  className="form-control"
631
631
  onChange={setAProp("imgResponsiveWidths")}
632
+ spellCheck={false}
632
633
  />
633
634
  <small>
634
635
  <i>
@@ -951,7 +952,9 @@ const ContainerSettings = () => {
951
952
  type="text"
952
953
  className="form-control text-to-display"
953
954
  value={showIfFormula}
955
+ spellCheck={false}
954
956
  onChange={setAProp("showIfFormula")}
957
+ onInput={(e) => validate_expression_elem($(e.target))}
955
958
  />
956
959
  <div style={{ marginTop: "-5px" }}>
957
960
  <small className="text-muted font-monospace">
@@ -1048,6 +1051,7 @@ const ContainerSettings = () => {
1048
1051
  <input
1049
1052
  type="text"
1050
1053
  className="form-control"
1054
+ spellCheck={false}
1051
1055
  value={url}
1052
1056
  onChange={setAProp("url")}
1053
1057
  />
@@ -1097,6 +1101,7 @@ const ContainerSettings = () => {
1097
1101
  type="text"
1098
1102
  className="form-control text-to-display"
1099
1103
  value={customId}
1104
+ spellCheck={false}
1100
1105
  onChange={setAProp("customId")}
1101
1106
  />
1102
1107
  </OrFormula>
@@ -1108,6 +1113,7 @@ const ContainerSettings = () => {
1108
1113
  type="text"
1109
1114
  className="form-control text-to-display"
1110
1115
  value={customClass}
1116
+ spellCheck={false}
1111
1117
  onChange={setAProp("customClass")}
1112
1118
  />
1113
1119
  </OrFormula>
@@ -1120,6 +1126,7 @@ const ContainerSettings = () => {
1120
1126
  className="text-to-display form-control"
1121
1127
  value={customCSS}
1122
1128
  onChange={setAProp("customCSS")}
1129
+ spellCheck={false}
1123
1130
  ></textarea>
1124
1131
  </div>
1125
1132
  </Accordion>
@@ -33,19 +33,28 @@ export /**
33
33
  * @category saltcorn-builder
34
34
  * @subcategory components
35
35
  */
36
- const Image = ({ fileid, block, srctype, url, alt, style }) => {
36
+ const Image = ({ fileid, block, srctype, url, alt, style, customClass }) => {
37
37
  const {
38
38
  selected,
39
39
  connectors: { connect, drag },
40
40
  } = useNode((node) => ({ selected: node.events.selected }));
41
41
  const theurl = srctype === "File" ? `/files/serve/${fileid}` : url;
42
42
  return fileid === 0 ? (
43
- <span>No images Available</span>
43
+ <span
44
+ {...blockProps(block)}
45
+ className={`${style && style.width ? "" : "w-100"} ${customClass || ""} image-widget ${
46
+ selected ? "selected-node" : ""
47
+ }`}
48
+ ref={(dom) => connect(drag(dom))}
49
+ style={reactifyStyles(style || {})}
50
+ >
51
+ No images Available
52
+ </span>
44
53
  ) : (
45
54
  <img
46
55
  {...blockProps(block)}
47
56
  ref={(dom) => connect(drag(dom))}
48
- className={`${style && style.width ? "" : "w-100"} image-widget ${
57
+ className={`${style && style.width ? "" : "w-100"} ${customClass || ""} image-widget ${
49
58
  selected ? "selected-node" : ""
50
59
  }`}
51
60
  style={reactifyStyles(style || {})}
@@ -72,6 +81,7 @@ const ImageSettings = () => {
72
81
  block: node.data.props.block,
73
82
  style: node.data.props.style,
74
83
  isFormula: node.data.props.isFormula,
84
+ customClass: node.data.props.customClass,
75
85
  imgResponsiveWidths: node.data.props.imgResponsiveWidths,
76
86
  }));
77
87
  const {
@@ -85,6 +95,7 @@ const ImageSettings = () => {
85
95
  isFormula,
86
96
  filepath,
87
97
  imgResponsiveWidths,
98
+ customClass,
88
99
  style,
89
100
  } = node;
90
101
  const options = useContext(optionsCtx);
@@ -185,6 +196,7 @@ const ImageSettings = () => {
185
196
  <input
186
197
  type="text"
187
198
  className="form-control"
199
+ spellCheck={false}
188
200
  value={url}
189
201
  onChange={setAProp("url")}
190
202
  />
@@ -262,6 +274,7 @@ const ImageSettings = () => {
262
274
  <input
263
275
  type="text"
264
276
  value={imgResponsiveWidths}
277
+ spellCheck={false}
265
278
  className="form-control"
266
279
  onChange={setAProp("imgResponsiveWidths")}
267
280
  />
@@ -284,6 +297,18 @@ const ImageSettings = () => {
284
297
  setProp={setProp}
285
298
  isStyle={true}
286
299
  />
300
+ <tr>
301
+ <td>Class</td>
302
+ <td>
303
+ <input
304
+ type="text"
305
+ value={customClass}
306
+ className="form-control"
307
+ onChange={setAProp("customClass")}
308
+ spellCheck={false}
309
+ />
310
+ </td>
311
+ </tr>
287
312
  {srctype !== "Upload" && (
288
313
  <tr>
289
314
  <td colSpan="2">
@@ -321,6 +346,7 @@ Image.craft = {
321
346
  { name: "srctype", default: "File" },
322
347
  { name: "fileid", default: 0 },
323
348
  "field",
349
+ "customClass",
324
350
  "block",
325
351
  "imgResponsiveWidths",
326
352
  { name: "style", default: {} },
@@ -449,6 +449,7 @@ const JoinFieldSettings = () => {
449
449
  name="join_field"
450
450
  data-fieldname="join_field"
451
451
  readOnly="readonly"
452
+ spellCheck={false}
452
453
  value={name}
453
454
  />
454
455
  </td>
@@ -190,6 +190,7 @@ const LinkSettings = () => {
190
190
  <OrFormula nodekey="url" {...{ setProp, isFormula, node }}>
191
191
  <input
192
192
  type="text"
193
+ spellCheck={false}
193
194
  className="form-control "
194
195
  value={url}
195
196
  onChange={setAProp("url")}
@@ -269,6 +270,7 @@ const LinkSettings = () => {
269
270
  <td>
270
271
  <input
271
272
  type="text"
273
+ spellCheck={false}
272
274
  className="form-control"
273
275
  value={view_state_fml}
274
276
  onChange={setAProp("view_state_fml")}
@@ -348,8 +350,8 @@ const LinkSettings = () => {
348
350
  link_src === "Page"
349
351
  ? url.replace("/page/", `/pageedit/edit/`)
350
352
  : link_src === "View"
351
- ? url.replace("/view/", `/viewedit/config/`)
352
- : ""
353
+ ? url.replace("/view/", `/viewedit/config/`)
354
+ : ""
353
355
  }
354
356
  >
355
357
  Configure this {link_src}
@@ -121,8 +121,8 @@ const Tabs = ({
121
121
  (typeof titles[ix].label === "undefined"
122
122
  ? titles[ix]
123
123
  : titles[ix].label === ""
124
- ? "(empty)"
125
- : titles[ix].label)}
124
+ ? "(empty)"
125
+ : titles[ix].label)}
126
126
  </a>
127
127
  </li>
128
128
  );
@@ -331,6 +331,7 @@ const TabsSettings = () => {
331
331
  <td>
332
332
  <input
333
333
  type="text"
334
+ spellCheck={false}
334
335
  className="form-control"
335
336
  value={tabId}
336
337
  onChange={setAProp("tabId")}
@@ -398,6 +399,7 @@ const TabsSettings = () => {
398
399
  type="text"
399
400
  className="form-control text-to-display"
400
401
  value={titles[use_setting_tab_n] || ""}
402
+ spellCheck={false}
401
403
  onChange={(e) => {
402
404
  if (!e.target) return;
403
405
  const value = e.target.value;
@@ -417,6 +419,7 @@ const TabsSettings = () => {
417
419
  <td colSpan={2}>
418
420
  <input
419
421
  type="text"
422
+ spellCheck={false}
420
423
  className="form-control text-to-display"
421
424
  value={showif?.[use_setting_tab_n] || ""}
422
425
  onChange={(e) => {
@@ -85,6 +85,7 @@ const Text = ({
85
85
  icon,
86
86
  font,
87
87
  style,
88
+ customClass,
88
89
  }) => {
89
90
  const {
90
91
  connectors: { connect, drag },
@@ -103,7 +104,7 @@ const Text = ({
103
104
  <div
104
105
  className={`${
105
106
  isBlock(block, inline, textStyle) ? "d-block" : "d-inline-block"
106
- } ${Array.isArray(textStyle) ? textStyle.join(" ") : textStyle} is-text ${
107
+ } ${customClass || ""} ${Array.isArray(textStyle) ? textStyle.join(" ") : textStyle} is-text ${
107
108
  isFormula.text ? "font-monospace" : ""
108
109
  } ${selected ? "selected-node" : ""}`}
109
110
  ref={(dom) => connect(drag(dom))}
@@ -160,6 +161,7 @@ const TextSettings = () => {
160
161
  isFormula: node.data.props.isFormula,
161
162
  textStyle: node.data.props.textStyle,
162
163
  labelFor: node.data.props.labelFor,
164
+ customClass: node.data.props.customClass,
163
165
  icon: node.data.props.icon,
164
166
  font: node.data.props.font,
165
167
  style: node.data.props.style,
@@ -175,6 +177,7 @@ const TextSettings = () => {
175
177
  icon,
176
178
  font,
177
179
  style,
180
+ customClass,
178
181
  } = node;
179
182
  const { mode, fields, icons } = useContext(optionsCtx);
180
183
  const setAProp = setAPropGen(setProp);
@@ -204,6 +207,7 @@ const TextSettings = () => {
204
207
  className="text-to-display form-control"
205
208
  value={text}
206
209
  onChange={setAProp("text")}
210
+ spellCheck={false}
207
211
  />
208
212
  ) : (
209
213
  <ErrorBoundary>
@@ -298,6 +302,18 @@ const TextSettings = () => {
298
302
  setProp={setProp}
299
303
  isStyle={true}
300
304
  />
305
+ <tr>
306
+ <td>Class</td>
307
+ <td>
308
+ <input
309
+ type="text"
310
+ value={customClass}
311
+ className="form-control"
312
+ onChange={setAProp("customClass")}
313
+ spellCheck={false}
314
+ />
315
+ </td>
316
+ </tr>
301
317
  <SettingsRow
302
318
  field={{
303
319
  name: "color",
@@ -384,6 +384,7 @@ const ViewSettings = () => {
384
384
  className="viewlink-label form-control"
385
385
  value={extra_state_fml}
386
386
  onChange={setAProp("extra_state_fml")}
387
+ spellCheck={false}
387
388
  />
388
389
  {errorString ? (
389
390
  <small className="text-danger font-monospace d-block">
@@ -184,7 +184,7 @@ const ViewLinkSettings = () => {
184
184
  relationsCache[options.tableName][safeViewName]
185
185
  );
186
186
  let safeRelation = null;
187
- if (relation) {
187
+ if (relation && subView) {
188
188
  const subView = views.find((view) => view.name === safeViewName);
189
189
  const subTbl = tables.find((tbl) => tbl.id === subView.table_id);
190
190
  safeRelation = new Relation(
@@ -315,6 +315,7 @@ const ViewLinkSettings = () => {
315
315
  className="viewlink-label form-control"
316
316
  value={extra_state_fml}
317
317
  onChange={setAProp("extra_state_fml")}
318
+ spellCheck={false}
318
319
  />
319
320
  {errorString ? (
320
321
  <small className="text-danger font-monospace d-block">
@@ -195,6 +195,7 @@ const OrFormula = ({ setProp, isFormula, node, nodekey, children }) => {
195
195
  type="text"
196
196
  className="form-control text-to-display"
197
197
  value={node[nodekey] || ""}
198
+ spellCheck={false}
198
199
  onChange={(e) => {
199
200
  if (e.target) {
200
201
  const target_value = e.target.value;
@@ -882,10 +883,10 @@ const ConfigField = ({
882
883
  let stored_value = configuration
883
884
  ? configuration[field.name]
884
885
  : isStyle
885
- ? props.style[field.name]
886
- : subProp
887
- ? props[subProp]?.[field.name]
888
- : props[field.name];
886
+ ? props.style[field.name]
887
+ : subProp
888
+ ? props[subProp]?.[field.name]
889
+ : props[field.name];
889
890
 
890
891
  let value = or_if_undef(stored_value, field.default);
891
892
  if (valuePostfix)
@@ -955,6 +956,7 @@ const ConfigField = ({
955
956
  type="text"
956
957
  className={`field-${field?.name} form-control`}
957
958
  value={value || ""}
959
+ spellCheck={false}
958
960
  onChange={(e) => e.target && myOnChange(e.target.value)}
959
961
  />
960
962
  );
@@ -1016,6 +1018,7 @@ const ConfigField = ({
1016
1018
  type="text"
1017
1019
  className={`field-${field?.name} form-control`}
1018
1020
  value={value}
1021
+ spellCheck={false}
1019
1022
  onChange={(e) => e.target && myOnChange(e.target.value)}
1020
1023
  />
1021
1024
  ),
@@ -1035,8 +1038,8 @@ const ConfigField = ({
1035
1038
  o.name && o.label
1036
1039
  ? { value: o.name, label: o.label }
1037
1040
  : o.value && o.label
1038
- ? { value: o.value, label: o.label }
1039
- : { value: o, label: o }
1041
+ ? { value: o.value, label: o.label }
1042
+ : { value: o, label: o }
1040
1043
  );
1041
1044
  return (
1042
1045
  <Select
@@ -1133,8 +1136,8 @@ const ConfigField = ({
1133
1136
  configuration
1134
1137
  ? configuration[field.name + "Unit"]
1135
1138
  : isStyle || subProp
1136
- ? styleDim
1137
- : props[field.name + "Unit"],
1139
+ ? styleDim
1140
+ : props[field.name + "Unit"],
1138
1141
  "px"
1139
1142
  )}
1140
1143
  autoable={field.autoable}
@@ -160,6 +160,7 @@ const layoutToNodes = (
160
160
  inline={segment.inline || false}
161
161
  textStyle={segment.textStyle || ""}
162
162
  labelFor={segment.labelFor || ""}
163
+ customClass={segment.customClass || ""}
163
164
  style={segment.style || {}}
164
165
  icon={segment.icon}
165
166
  font={segment.font || ""}
@@ -580,6 +581,7 @@ const craftToSaltcorn = (nodes, startFrom = "ROOT", options) => {
580
581
  textStyle: node.props.textStyle,
581
582
  isFormula: node.props.isFormula,
582
583
  labelFor: node.props.labelFor,
584
+ customClass: node.props.customClass,
583
585
  style: node.props.style,
584
586
  icon: node.props.icon,
585
587
  font: node.props.font,