@saltcorn/builder 1.5.0-beta.11 → 1.5.0-beta.13

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.
@@ -45,6 +45,8 @@ See the Apache Version 2.0 License for specific language governing permissions
45
45
  and limitations under the License.
46
46
  ***************************************************************************** */
47
47
 
48
+ /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */
49
+
48
50
  /*!*
49
51
  * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
50
52
  * For licensing, see LICENSE.md.
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@saltcorn/builder",
3
- "version": "1.5.0-beta.11",
3
+ "version": "1.5.0-beta.13",
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",
7
7
  "scripts": {
8
8
  "build": "webpack --mode production",
9
9
  "builddev": "webpack --mode none",
10
- "test": "jest tests --runInBand",
10
+ "test1": "jest tests --runInBand",
11
+ "test": "echo NO TESTS",
11
12
  "tsc": "echo \"Error: no TypeScript support yet\"",
12
13
  "clean": "echo \"Error: no TypeScript support yet\""
13
14
  },
@@ -20,13 +21,14 @@
20
21
  "@babel/preset-react": "7.24.7",
21
22
  "@craftjs/core": "0.1.0-beta.20",
22
23
  "@craftjs/utils": "0.1.0-beta.20",
23
- "@saltcorn/common-code": "1.5.0-beta.11",
24
+ "@saltcorn/common-code": "1.5.0-beta.13",
24
25
  "saltcorn-craft-layers-noeye": "0.1.0-beta.22",
25
26
  "@fonticonpicker/react-fonticonpicker": "1.2.0",
26
27
  "@fortawesome/fontawesome-svg-core": "1.2.34",
27
28
  "@fortawesome/free-regular-svg-icons": "5.15.2",
28
29
  "@fortawesome/free-solid-svg-icons": "5.15.2",
29
30
  "@fortawesome/react-fontawesome": "0.1.14",
31
+ "@monaco-editor/react": "4.7.0",
30
32
  "babel-jest": "^29.7.0",
31
33
  "babel-loader": "8.1.0",
32
34
  "ckeditor4-react": "1.4.2",
@@ -4,13 +4,38 @@
4
4
  * @subcategory components / elements
5
5
  */
6
6
 
7
- import React, { Fragment } from "react";
7
+ import React, { Fragment, useContext } from "react";
8
8
  import { Text } from "./Text";
9
- import { OrFormula, SettingsRow, Accordion, reactifyStyles } from "./utils";
9
+ import {
10
+ OrFormula,
11
+ SettingsRow,
12
+ Accordion,
13
+ reactifyStyles,
14
+ SettingsSectionHeaderRow,
15
+ setAPropGen,
16
+ buildOptions,
17
+ } from "./utils";
10
18
  import { Column } from "./Column";
11
19
 
12
20
  import { Element, useNode } from "@craftjs/core";
13
21
  import { BoxModelEditor } from "./BoxModelEditor";
22
+ import {
23
+ AlignTop,
24
+ AlignMiddle,
25
+ AlignStart,
26
+ AlignEnd,
27
+ AlignCenter,
28
+ AlignBottom,
29
+ SlashCircle,
30
+ Image,
31
+ Images,
32
+ Rainbow,
33
+ Palette,
34
+ } from "react-bootstrap-icons";
35
+
36
+ import optionsCtx from "../context";
37
+ import previewCtx from "../preview_context";
38
+
14
39
  import { bstyleopt } from "./utils";
15
40
 
16
41
  export /**
@@ -35,6 +60,15 @@ const Card = ({
35
60
  style,
36
61
  footer,
37
62
  hasFooter,
63
+ hAlign,
64
+ bgType,
65
+ bgColor,
66
+ bgFileId,
67
+ imageSize,
68
+ imageLocation,
69
+ gradStartColor,
70
+ gradEndColor,
71
+ gradDirection,
38
72
  }) => {
39
73
  const {
40
74
  selected,
@@ -43,12 +77,38 @@ const Card = ({
43
77
 
44
78
  return (
45
79
  <div
46
- className={`card ${shadow ? "shadow" : ""} builder ${
80
+ className={`card ${shadow ? "shadow" : ""} text-${hAlign} builder ${
47
81
  selected ? "selected-node" : ""
48
82
  }`}
49
- style={reactifyStyles(style)}
83
+ style={{
84
+ ...reactifyStyles(style),
85
+ ...(bgType === "Image" && bgFileId && imageLocation === "Card"
86
+ ? {
87
+ backgroundImage: `url('/files/serve/${bgFileId}')`,
88
+ backgroundSize:
89
+ imageSize === "repeat" ? undefined : imageSize || "contain",
90
+ backgroundRepeat:
91
+ imageSize === "repeat" ? imageSize : "no-repeat",
92
+ }
93
+ : {}),
94
+ ...(bgType === "Color"
95
+ ? {
96
+ backgroundColor: bgColor,
97
+ }
98
+ : {}),
99
+ ...(bgType === "Gradient"
100
+ ? {
101
+ backgroundImage: `linear-gradient(${
102
+ gradDirection || 0
103
+ }deg, ${gradStartColor}, ${gradEndColor})`,
104
+ }
105
+ : {}),
106
+ }}
50
107
  ref={(dom) => connect(drag(dom))}
51
108
  >
109
+ {bgType === "Image" && bgFileId && imageLocation === "Top" ? (
110
+ <img src={`/files/serve/${bgFileId}`} className="card-img-top" />
111
+ ) : null}
52
112
  {title && title.length > 0 && (
53
113
  <div className="card-header">
54
114
  {isFormula?.title ? (
@@ -58,7 +118,20 @@ const Card = ({
58
118
  )}
59
119
  </div>
60
120
  )}
61
- <div className={`card-body ${noPadding ? "p-0" : ""}`}>
121
+ <div
122
+ className={`card-body ${noPadding ? "p-0" : ""}`}
123
+ style={
124
+ bgType === "Image" && bgFileId && imageLocation === "Body"
125
+ ? {
126
+ backgroundImage: `url('/files/serve/${bgFileId}')`,
127
+ backgroundSize:
128
+ imageSize === "repeat" ? undefined : imageSize || "contain",
129
+ backgroundRepeat:
130
+ imageSize === "repeat" ? imageSize : "no-repeat",
131
+ }
132
+ : {}
133
+ }
134
+ >
62
135
  <div className="canvas">{children}</div>
63
136
  </div>
64
137
  {hasFooter ? (
@@ -80,7 +153,9 @@ export /**
80
153
  */
81
154
  const CardSettings = () => {
82
155
  const node = useNode((node) => {
83
- const ps = {};
156
+ const ps = {
157
+ currentSettingsTab: node.data.props.currentSettingsTab,
158
+ };
84
159
  fields.forEach((f) => {
85
160
  ps[f.name] = node.data.props[f.name];
86
161
  });
@@ -90,10 +165,27 @@ const CardSettings = () => {
90
165
  });
91
166
  const {
92
167
  actions: { setProp },
168
+ bgType,
169
+ gradStartColor,
170
+ gradEndColor,
171
+ gradDirection,
172
+ bgFileId,
173
+ bgField,
174
+ imageSize,
175
+ imageLocation,
176
+ bgColor,
177
+ isFormula,
178
+ currentSettingsTab,
93
179
  } = node;
180
+ const options = useContext(optionsCtx);
181
+ const { uploadedFiles } = useContext(previewCtx);
182
+ const setAProp = setAPropGen(setProp);
94
183
 
95
184
  return (
96
- <Accordion>
185
+ <Accordion
186
+ value={currentSettingsTab}
187
+ onChange={(ix) => setProp((prop) => (prop.currentSettingsTab = ix))}
188
+ >
97
189
  <table className="w-100" accordiontitle="Card properties">
98
190
  <tbody>
99
191
  <SettingsRow
@@ -108,7 +200,7 @@ const CardSettings = () => {
108
200
  />
109
201
  <SettingsRow
110
202
  field={{
111
- label: "URL",
203
+ label: "Click URL",
112
204
  name: "url",
113
205
  type: "String",
114
206
  canBeFormula: true,
@@ -155,6 +247,214 @@ const CardSettings = () => {
155
247
  <div accordiontitle="Box" className="w-100">
156
248
  <BoxModelEditor setProp={setProp} node={node} sizeWithStyle={true} />
157
249
  </div>
250
+ <table className="w-100" accordiontitle="Contents">
251
+ <tbody>
252
+ <SettingsSectionHeaderRow title="Align" />
253
+ <SettingsRow
254
+ field={{
255
+ name: "hAlign",
256
+ label: "Horizontal",
257
+ type: "btn_select",
258
+ options: [
259
+ { value: "start", title: "Left", label: <AlignStart /> },
260
+ { value: "center", title: "Center", label: <AlignCenter /> },
261
+ { value: "end", title: "Right", label: <AlignEnd /> },
262
+ ],
263
+ }}
264
+ node={node}
265
+ setProp={setProp}
266
+ />
267
+ <SettingsSectionHeaderRow title="Background" />
268
+ <SettingsRow
269
+ field={{
270
+ name: "bgType",
271
+ label: "Type",
272
+ type: "btn_select",
273
+ options: [
274
+ { value: "None", label: <SlashCircle /> },
275
+ { value: "Image", label: <Image /> },
276
+ ...(options.mode === "show"
277
+ ? [{ value: "Image Field", label: <Images /> }]
278
+ : []),
279
+ { value: "Color", label: <Palette /> },
280
+ { value: "Gradient", label: <Rainbow /> },
281
+ ],
282
+ }}
283
+ node={node}
284
+ setProp={setProp}
285
+ onChange={(v) =>
286
+ setProp((prop) => {
287
+ prop.bgFileId =
288
+ prop.bgFileId ||
289
+ ((options.images || []).length + uploadedFiles.length > 0 &&
290
+ options.images?.[0]?.location);
291
+ })
292
+ }
293
+ />
294
+ {bgType === "Gradient" && (
295
+ <Fragment>
296
+ <tr>
297
+ <td>Start</td>
298
+ <td>
299
+ <OrFormula
300
+ nodekey="gradStartColor"
301
+ {...{ setProp, isFormula, node }}
302
+ >
303
+ <input
304
+ type="color"
305
+ value={gradStartColor}
306
+ className="form-control-sm w-50"
307
+ onChange={setAProp("gradStartColor")}
308
+ />
309
+ </OrFormula>
310
+ </td>
311
+ </tr>
312
+ <tr>
313
+ <td>End</td>
314
+ <td>
315
+ <OrFormula
316
+ nodekey="gradEndColor"
317
+ {...{ setProp, isFormula, node }}
318
+ >
319
+ <input
320
+ type="color"
321
+ value={gradEndColor}
322
+ className="form-control-sm w-50"
323
+ onChange={setAProp("gradEndColor")}
324
+ />
325
+ </OrFormula>
326
+ </td>
327
+ </tr>
328
+ <tr>
329
+ <td>Direction (&deg;)</td>
330
+ <td>
331
+ <OrFormula
332
+ nodekey="gradDirection"
333
+ {...{ setProp, isFormula, node }}
334
+ >
335
+ <input
336
+ type="number"
337
+ min="0"
338
+ max="360"
339
+ value={gradDirection}
340
+ className="form-control-sm w-50"
341
+ onChange={setAProp("gradDirection")}
342
+ />
343
+ </OrFormula>
344
+ </td>
345
+ </tr>
346
+ </Fragment>
347
+ )}
348
+ {bgType === "Image" && (
349
+ <tr>
350
+ <td>
351
+ <label>File</label>
352
+ </td>
353
+ <td>
354
+ <select
355
+ value={bgFileId}
356
+ className="form-control-sm w-100 form-select"
357
+ onChange={setAProp("bgFileId")}
358
+ >
359
+ {options.images.map((f, ix) => (
360
+ <option key={ix} value={f.id}>
361
+ {f.filename}
362
+ </option>
363
+ ))}
364
+ {(uploadedFiles || []).map((uf, ix) => (
365
+ <option key={ix} value={uf.id}>
366
+ {uf.filename}
367
+ </option>
368
+ ))}{" "}
369
+ </select>
370
+ </td>
371
+ </tr>
372
+ )}
373
+ {bgType === "Image Field" && (
374
+ <tr>
375
+ <td>
376
+ <label>File field</label>
377
+ </td>
378
+ <td>
379
+ <select
380
+ value={bgField}
381
+ className="form-control-sm w-100 form-select"
382
+ onChange={setAProp("bgField")}
383
+ >
384
+ {options.fields
385
+ .filter(
386
+ (f) =>
387
+ f.type === "String" ||
388
+ (f.type && f.type.name === "String") ||
389
+ (f.type && f.type === "File")
390
+ )
391
+ .map((f, ix) => (
392
+ <option key={ix} value={f.name}>
393
+ {f.label}
394
+ </option>
395
+ ))}
396
+ </select>
397
+ </td>
398
+ </tr>
399
+ )}
400
+ {(bgType === "Image" || bgType === "Image Field") && (
401
+ <Fragment>
402
+ <tr>
403
+ <td>
404
+ <label>Location</label>
405
+ </td>
406
+
407
+ <td>
408
+ <select
409
+ value={imageLocation}
410
+ className="form-control-sm form-select"
411
+ onChange={setAProp("imageLocation")}
412
+ >
413
+ <option>Card</option>
414
+ <option>Body</option>
415
+ <option>Top</option>
416
+ </select>
417
+ </td>
418
+ </tr>
419
+ </Fragment>
420
+ )}
421
+ {(bgType === "Image" || bgType === "Image Field") &&
422
+ imageLocation !== "Top" && (
423
+ <Fragment>
424
+ <tr>
425
+ <td>
426
+ <label>Size</label>
427
+ </td>
428
+
429
+ <td>
430
+ <select
431
+ value={imageSize}
432
+ className="form-control-sm form-select"
433
+ onChange={setAProp("imageSize")}
434
+ >
435
+ {buildOptions(["contain", "cover", "repeat"])}
436
+ </select>
437
+ </td>
438
+ </tr>
439
+ </Fragment>
440
+ )}
441
+ {bgType === "Color" && (
442
+ <tr>
443
+ <td>Color</td>
444
+ <td>
445
+ <OrFormula nodekey="bgColor" {...{ setProp, isFormula, node }}>
446
+ <input
447
+ type="color"
448
+ value={bgColor}
449
+ className="form-control-sm w-50"
450
+ onChange={setAProp("bgColor")}
451
+ />
452
+ </OrFormula>
453
+ </td>
454
+ </tr>
455
+ )}
456
+ </tbody>
457
+ </table>
158
458
  </Accordion>
159
459
  );
160
460
  };
@@ -174,6 +474,17 @@ const fields = [
174
474
  { label: "No padding", name: "noPadding", type: "Bool" },
175
475
  { label: "Footer", name: "footer", type: "Nodes", nodeID: "cardfooter" },
176
476
  { name: "style", default: {} },
477
+ { label: "Class", name: "class", type: "String", canBeFormula: true },
478
+ { name: "hAlign" },
479
+ { name: "bgType" },
480
+ { name: "gradStartColor" },
481
+ { name: "gradEndColor" },
482
+ { name: "gradDirection" },
483
+ { name: "bgFileId" },
484
+ { name: "bgField" },
485
+ { name: "imageSize" },
486
+ { name: "imageLocation" },
487
+ { name: "bgColor" },
177
488
  ];
178
489
 
179
490
  /**
@@ -137,6 +137,7 @@ const ColumnsSettings = () => {
137
137
  colClasses: node.data.props.colClasses,
138
138
  colStyles: node.data.props.colStyles,
139
139
  customClass: node.data.props.customClass,
140
+ currentSettingsTab: node.data.props.currentSettingsTab,
140
141
  }));
141
142
  const {
142
143
  actions: { setProp },
@@ -150,6 +151,7 @@ const ColumnsSettings = () => {
150
151
  colClasses,
151
152
  colStyles,
152
153
  customClass,
154
+ currentSettingsTab,
153
155
  } = node;
154
156
  const colSetsNode = {
155
157
  vAlign: vAligns?.[setting_col_n - 1],
@@ -158,7 +160,10 @@ const ColumnsSettings = () => {
158
160
  colStyle: colStyles?.[setting_col_n - 1] || "",
159
161
  };
160
162
  return (
161
- <Accordion>
163
+ <Accordion
164
+ value={currentSettingsTab}
165
+ onChange={(ix) => setProp((prop) => (prop.currentSettingsTab = ix))}
166
+ >
162
167
  <table accordiontitle="Column properties">
163
168
  <tbody>
164
169
  <tr>
@@ -136,7 +136,11 @@ const Container = ({
136
136
  minHeight: minHeight ? `${minHeight}${minHeightUnit || "px"}` : null,
137
137
  ...(bgType === "Image" && bgFileId
138
138
  ? {
139
- backgroundImage: `url('/files/serve/${bgFileId}')`,
139
+ backgroundImage: `url('${
140
+ /^(?:[a-z]+:)?\/\//i.test(bgFileId)
141
+ ? bgFileId
142
+ : `/files/serve/${bgFileId}`
143
+ }')`,
140
144
  backgroundSize:
141
145
  imageSize === "repeat" ? undefined : imageSize || "contain",
142
146
  backgroundRepeat:
@@ -229,6 +233,7 @@ const ContainerSettings = () => {
229
233
  animateDelay: node.data.props.animateDelay,
230
234
  animateDuration: node.data.props.animateDuration,
231
235
  animateInitialHide: node.data.props.animateInitialHide,
236
+ currentSettingsTab: node.data.props.currentSettingsTab,
232
237
  }));
233
238
  const {
234
239
  actions: { setProp },
@@ -263,6 +268,7 @@ const ContainerSettings = () => {
263
268
  style,
264
269
  transform,
265
270
  bgField,
271
+ currentSettingsTab,
266
272
  } = node;
267
273
  const options = useContext(optionsCtx);
268
274
  const { uploadedFiles } = useContext(previewCtx);
@@ -277,7 +283,10 @@ const ContainerSettings = () => {
277
283
  //console.log("transform", transform);
278
284
 
279
285
  return (
280
- <Accordion>
286
+ <Accordion
287
+ value={currentSettingsTab}
288
+ onChange={(ix) => setProp((prop) => (prop.currentSettingsTab = ix))}
289
+ >
281
290
  <div accordiontitle="Box" className="w-100">
282
291
  <BoxModelEditor setProp={setProp} node={node} />
283
292
  </div>
@@ -38,7 +38,12 @@ const Image = ({ fileid, block, srctype, url, alt, style, customClass }) => {
38
38
  selected,
39
39
  connectors: { connect, drag },
40
40
  } = useNode((node) => ({ selected: node.events.selected }));
41
- const theurl = srctype === "File" ? `/files/serve/${fileid}` : url;
41
+ const theurl =
42
+ srctype === "File"
43
+ ? /^(?:[a-z]+:)?\/\//i.test(fileid)
44
+ ? fileid
45
+ : `/files/serve/${fileid}`
46
+ : url;
42
47
  return fileid === 0 ? (
43
48
  <span
44
49
  {...blockProps(block)}
@@ -83,6 +88,7 @@ const ImageSettings = () => {
83
88
  isFormula: node.data.props.isFormula,
84
89
  customClass: node.data.props.customClass,
85
90
  imgResponsiveWidths: node.data.props.imgResponsiveWidths,
91
+ currentSettingsTab: node.data.props.currentSettingsTab,
86
92
  }));
87
93
  const {
88
94
  actions: { setProp },
@@ -97,6 +103,7 @@ const ImageSettings = () => {
97
103
  imgResponsiveWidths,
98
104
  customClass,
99
105
  style,
106
+ currentSettingsTab,
100
107
  } = node;
101
108
  const options = useContext(optionsCtx);
102
109
  const { uploadedFiles, setUploadedFiles } = useContext(previewCtx);
@@ -136,7 +143,10 @@ const ImageSettings = () => {
136
143
  const sourceOpts = ["File", "URL", "Upload"];
137
144
  if (options.mode === "show") sourceOpts.push("Field");
138
145
  return (
139
- <Accordion>
146
+ <Accordion
147
+ value={currentSettingsTab}
148
+ onChange={(ix) => setProp((prop) => (prop.currentSettingsTab = ix))}
149
+ >
140
150
  <table accordiontitle="Select image">
141
151
  <tbody>
142
152
  <tr>