@saltcorn/builder 0.6.3 → 0.6.4-beta.4

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.
@@ -8,8 +8,15 @@ import React, { useContext, Fragment } from "react";
8
8
  import { useNode } from "@craftjs/core";
9
9
  import optionsCtx from "../context";
10
10
  import previewCtx from "../preview_context";
11
+ import { BoxModelEditor } from "./BoxModelEditor";
11
12
 
12
- import { blockProps, BlockSetting, TextStyleSetting, OrFormula } from "./utils";
13
+ import {
14
+ blockProps,
15
+ BlockSetting,
16
+ reactifyStyles,
17
+ Accordion,
18
+ OrFormula,
19
+ } from "./utils";
13
20
 
14
21
  export /**
15
22
  * @param {object} props
@@ -23,7 +30,7 @@ export /**
23
30
  * @category saltcorn-builder
24
31
  * @subcategory components
25
32
  */
26
- const Image = ({ fileid, block, srctype, url, alt }) => {
33
+ const Image = ({ fileid, block, srctype, url, alt, style }) => {
27
34
  const {
28
35
  selected,
29
36
  connectors: { connect, drag },
@@ -35,7 +42,10 @@ const Image = ({ fileid, block, srctype, url, alt }) => {
35
42
  "No images Available"
36
43
  ) : (
37
44
  <img
38
- className={`w-100 image-widget ${selected ? "selected-node" : ""}`}
45
+ className={`${style && style.width ? "" : "w-100"} image-widget ${
46
+ selected ? "selected-node" : ""
47
+ }`}
48
+ style={reactifyStyles(style || {})}
39
49
  src={theurl}
40
50
  alt={alt}
41
51
  ></img>
@@ -59,6 +69,7 @@ const ImageSettings = () => {
59
69
  srctype: node.data.props.srctype,
60
70
  alt: node.data.props.alt,
61
71
  block: node.data.props.block,
72
+ style: node.data.props.style,
62
73
  isFormula: node.data.props.isFormula,
63
74
  }));
64
75
  const {
@@ -71,6 +82,7 @@ const ImageSettings = () => {
71
82
  block,
72
83
  isFormula,
73
84
  filepath,
85
+ style,
74
86
  } = node;
75
87
  const options = useContext(optionsCtx);
76
88
  const { uploadedFiles, setUploadedFiles } = useContext(previewCtx);
@@ -113,141 +125,146 @@ const ImageSettings = () => {
113
125
  }
114
126
  };
115
127
  return (
116
- <table>
117
- <tbody>
118
- <tr>
119
- <td colSpan="2">
120
- <i>
121
- <small>Preview shown in canvas is indicative</small>
122
- </i>
123
- </td>
124
- </tr>
125
- <tr>
126
- <td>
127
- <label>Source</label>
128
- </td>
129
- <td>
130
- <select
131
- value={srctype}
132
- className="form-control"
133
- onChange={setAProp("srctype")}
134
- >
135
- <option>File</option>
136
- <option>URL</option>
137
- <option>Upload</option>
138
- {options.mode === "show" && <option>Field</option>}
139
- </select>
140
- </td>
141
- </tr>
142
- {srctype === "File" && (
128
+ <Accordion>
129
+ <table accordiontitle="Select image">
130
+ <tbody>
143
131
  <tr>
144
- <td>
145
- <label>File</label>
146
- </td>
147
- <td>
148
- <select
149
- value={fileid}
150
- className="form-control"
151
- onChange={setAProp("fileid")}
152
- >
153
- {options.images.map((f, ix) => (
154
- <option key={ix} value={f.id}>
155
- {f.filename}
156
- </option>
157
- ))}
158
- {(uploadedFiles || []).map((uf, ix) => (
159
- <option key={ix} value={uf.id}>
160
- {uf.filename}
161
- </option>
162
- ))}
163
- </select>
164
- </td>
165
- </tr>
166
- )}
167
- {srctype === "URL" && (
168
- <tr>
169
- <td>
170
- <label>URL</label>
171
- </td>
172
- <td>
173
- <OrFormula nodekey="url" {...{ setProp, isFormula, node }}>
174
- <input
175
- type="text"
176
- className="form-control"
177
- value={url}
178
- onChange={setAProp("url")}
179
- />
180
- </OrFormula>
181
- </td>
182
- </tr>
183
- )}
184
- {srctype === "Upload" && (
185
- <tr>
186
- <td>
187
- <label>File</label>
188
- </td>
189
- <td>
190
- <input
191
- type="file"
192
- className="form-control"
193
- value={filepath}
194
- onChange={handleUpload}
195
- />
132
+ <td colSpan="2">
133
+ <i>
134
+ <small>Preview shown in canvas is indicative</small>
135
+ </i>
196
136
  </td>
197
137
  </tr>
198
- )}
199
- {srctype === "Field" && (
200
138
  <tr>
201
139
  <td>
202
- <label>Field</label>
140
+ <label>Source</label>
203
141
  </td>
204
142
  <td>
205
143
  <select
206
- value={field}
144
+ value={srctype}
207
145
  className="form-control"
208
- onChange={setAProp("field")}
146
+ onChange={setAProp("srctype")}
209
147
  >
210
- {options.fields
211
- .filter(
212
- (f) =>
213
- (f.type && f.type.name === "String") ||
214
- f.reftable_name === "_sc_files"
215
- )
216
- .map((f, ix) => (
217
- <option key={ix} value={f.name}>
218
- {f.label}
219
- </option>
220
- ))}
148
+ <option>File</option>
149
+ <option>URL</option>
150
+ <option>Upload</option>
151
+ {options.mode === "show" && <option>Field</option>}
221
152
  </select>
222
153
  </td>
223
154
  </tr>
224
- )}
225
- {srctype !== "Upload" && (
226
- <tr>
227
- <td>
228
- <label>Alt text</label>
229
- </td>
230
- <td>
231
- <OrFormula nodekey="alt" {...{ setProp, isFormula, node }}>
155
+ {srctype === "File" && (
156
+ <tr>
157
+ <td>
158
+ <label>File</label>
159
+ </td>
160
+ <td>
161
+ <select
162
+ value={fileid}
163
+ className="form-control"
164
+ onChange={setAProp("fileid")}
165
+ >
166
+ {options.images.map((f, ix) => (
167
+ <option key={ix} value={f.id}>
168
+ {f.filename}
169
+ </option>
170
+ ))}
171
+ {(uploadedFiles || []).map((uf, ix) => (
172
+ <option key={ix} value={uf.id}>
173
+ {uf.filename}
174
+ </option>
175
+ ))}
176
+ </select>
177
+ </td>
178
+ </tr>
179
+ )}
180
+ {srctype === "URL" && (
181
+ <tr>
182
+ <td>
183
+ <label>URL</label>
184
+ </td>
185
+ <td>
186
+ <OrFormula nodekey="url" {...{ setProp, isFormula, node }}>
187
+ <input
188
+ type="text"
189
+ className="form-control"
190
+ value={url}
191
+ onChange={setAProp("url")}
192
+ />
193
+ </OrFormula>
194
+ </td>
195
+ </tr>
196
+ )}
197
+ {srctype === "Upload" && (
198
+ <tr>
199
+ <td>
200
+ <label>File</label>
201
+ </td>
202
+ <td>
232
203
  <input
233
- type="text"
204
+ type="file"
234
205
  className="form-control"
235
- value={alt}
236
- onChange={setAProp("alt")}
206
+ value={filepath}
207
+ onChange={handleUpload}
237
208
  />
238
- </OrFormula>
239
- </td>
240
- </tr>
241
- )}
242
- {srctype !== "Upload" && (
243
- <tr>
244
- <td colSpan="2">
245
- <BlockSetting block={block} setProp={setProp} />
246
- </td>
247
- </tr>
248
- )}
249
- </tbody>
250
- </table>
209
+ </td>
210
+ </tr>
211
+ )}
212
+ {srctype === "Field" && (
213
+ <tr>
214
+ <td>
215
+ <label>Field</label>
216
+ </td>
217
+ <td>
218
+ <select
219
+ value={field}
220
+ className="form-control"
221
+ onChange={setAProp("field")}
222
+ >
223
+ {options.fields
224
+ .filter(
225
+ (f) =>
226
+ (f.type && f.type.name === "String") ||
227
+ f.reftable_name === "_sc_files"
228
+ )
229
+ .map((f, ix) => (
230
+ <option key={ix} value={f.name}>
231
+ {f.label}
232
+ </option>
233
+ ))}
234
+ </select>
235
+ </td>
236
+ </tr>
237
+ )}
238
+ {srctype !== "Upload" && (
239
+ <tr>
240
+ <td>
241
+ <label>Alt text</label>
242
+ </td>
243
+ <td>
244
+ <OrFormula nodekey="alt" {...{ setProp, isFormula, node }}>
245
+ <input
246
+ type="text"
247
+ className="form-control"
248
+ value={alt}
249
+ onChange={setAProp("alt")}
250
+ />
251
+ </OrFormula>
252
+ </td>
253
+ </tr>
254
+ )}
255
+ {srctype !== "Upload" && (
256
+ <tr>
257
+ <td colSpan="2">
258
+ <BlockSetting block={block} setProp={setProp} />
259
+ </td>
260
+ </tr>
261
+ )}
262
+ </tbody>
263
+ </table>
264
+ <div accordiontitle="Box" className="w-100">
265
+ <BoxModelEditor setProp={setProp} node={node} sizeWithStyle={true} />
266
+ </div>
267
+ </Accordion>
251
268
  );
252
269
  };
253
270
 
@@ -261,6 +278,7 @@ Image.craft = {
261
278
  block: false,
262
279
  isFormula: {},
263
280
  srctype: "File",
281
+ style: {},
264
282
  },
265
283
  related: {
266
284
  settings: ImageSettings,
@@ -272,6 +290,7 @@ Image.craft = {
272
290
  { name: "fileid", default: 0 },
273
291
  "field",
274
292
  "block",
293
+ { name: "style", default: {} },
275
294
  ],
276
295
  },
277
296
  };
@@ -11,6 +11,7 @@ import {
11
11
  blockProps,
12
12
  BlockSetting,
13
13
  TextStyleRow,
14
+ ConfigForm,
14
15
  fetchFieldPreview,
15
16
  } from "./utils";
16
17
  import previewCtx from "../preview_context";
@@ -76,6 +77,7 @@ const JoinFieldSettings = () => {
76
77
  name,
77
78
  block,
78
79
  textStyle,
80
+ configuration,
79
81
  fieldview,
80
82
  node_id,
81
83
  } = useNode((node) => ({
@@ -83,17 +85,22 @@ const JoinFieldSettings = () => {
83
85
  block: node.data.props.block,
84
86
  textStyle: node.data.props.textStyle,
85
87
  fieldview: node.data.props.fieldview,
88
+ configuration: node.data.props.configuration,
89
+
86
90
  node_id: node.id,
87
91
  }));
88
92
  const options = useContext(optionsCtx);
89
93
  const { setPreviews } = useContext(previewCtx);
90
94
 
91
95
  const fvs = options.field_view_options[name];
96
+ const getCfgFields = (fv) =>
97
+ ((options.fieldViewConfigForms || {})[name] || {})[fv];
98
+ const cfgFields = getCfgFields(fieldview);
92
99
  const refetchPreview = fetchFieldPreview({
93
100
  options,
94
101
  name,
95
102
  fieldview,
96
- configuration: {},
103
+ configuration,
97
104
  setPreviews,
98
105
  node_id,
99
106
  });
@@ -167,6 +174,14 @@ const JoinFieldSettings = () => {
167
174
  <TextStyleRow textStyle={textStyle} setProp={setProp} />
168
175
  </tbody>
169
176
  </table>
177
+ {cfgFields ? (
178
+ <ConfigForm
179
+ fields={cfgFields}
180
+ configuration={configuration || {}}
181
+ setProp={setProp}
182
+ onChange={(k, v) => refetchPreview({ configuration: { [k]: v } })}
183
+ />
184
+ ) : null}
170
185
  </Fragment>
171
186
  );
172
187
  };
@@ -185,6 +200,7 @@ JoinField.craft = {
185
200
  "fieldview",
186
201
  "textStyle",
187
202
  "block",
203
+ { name: "configuration", default: {} },
188
204
  ],
189
205
  },
190
206
  };
@@ -8,12 +8,13 @@ import React, { useState, useContext, useEffect, Fragment } from "react";
8
8
  import { useNode } from "@craftjs/core";
9
9
  import {
10
10
  blockProps,
11
- BlockSetting,
11
+ BlockOrInlineSetting,
12
12
  TextStyleSetting,
13
13
  OrFormula,
14
14
  ErrorBoundary,
15
15
  TextStyleRow,
16
16
  DynamicFontAwesomeIcon,
17
+ isBlock,
17
18
  } from "./utils";
18
19
  import ContentEditable from "react-contenteditable";
19
20
  import optionsCtx from "../context";
@@ -53,7 +54,7 @@ const ckConfig = {
53
54
  };
54
55
 
55
56
  /**
56
- * @param {string} str
57
+ * @param {string} str
57
58
  * @returns {string}
58
59
  */
59
60
  function escape_tags(str) {
@@ -73,7 +74,7 @@ export /**
73
74
  * @category saltcorn-builder
74
75
  * @subcategory components
75
76
  */
76
- const Text = ({ text, block, isFormula, textStyle, icon, font }) => {
77
+ const Text = ({ text, block, inline, isFormula, textStyle, icon, font }) => {
77
78
  const {
78
79
  connectors: { connect, drag },
79
80
  selected,
@@ -89,10 +90,11 @@ const Text = ({ text, block, isFormula, textStyle, icon, font }) => {
89
90
  }, [selected]);
90
91
  return (
91
92
  <div
92
- className={`${!block ? "d-inline-block" : ""} ${textStyle} is-text ${
93
- isFormula.text ? "text-monospace" : ""
94
- } ${selected ? "selected-node" : ""}`}
95
- {...blockProps(block)}
93
+ className={`${
94
+ isBlock(block, inline, textStyle) ? "d-block" : "d-inline-block"
95
+ } ${textStyle} is-text ${isFormula.text ? "text-monospace" : ""} ${
96
+ selected ? "selected-node" : ""
97
+ }`}
96
98
  ref={(dom) => connect(drag(dom))}
97
99
  onClick={(e) => selected && setEditable(true)}
98
100
  style={font ? { fontFamily: font } : {}}
@@ -133,11 +135,12 @@ export /**
133
135
  * @namespace
134
136
  * @category saltcorn-builder
135
137
  * @subcategory components
136
- */
138
+ */
137
139
  const TextSettings = () => {
138
140
  const node = useNode((node) => ({
139
141
  text: node.data.props.text,
140
142
  block: node.data.props.block,
143
+ inline: node.data.props.inline,
141
144
  isFormula: node.data.props.isFormula,
142
145
  textStyle: node.data.props.textStyle,
143
146
  labelFor: node.data.props.labelFor,
@@ -148,6 +151,7 @@ const TextSettings = () => {
148
151
  actions: { setProp },
149
152
  text,
150
153
  block,
154
+ inline,
151
155
  textStyle,
152
156
  isFormula,
153
157
  labelFor,
@@ -246,7 +250,12 @@ const TextSettings = () => {
246
250
  </tr>
247
251
  </tbody>
248
252
  </table>
249
- <BlockSetting block={block} setProp={setProp} />
253
+ <BlockOrInlineSetting
254
+ block={block}
255
+ inline={inline}
256
+ textStyle={textStyle}
257
+ setProp={setProp}
258
+ />
250
259
  </div>
251
260
  );
252
261
  };
@@ -258,6 +267,7 @@ Text.craft = {
258
267
  defaultProps: {
259
268
  text: "Click here",
260
269
  block: false,
270
+ inline: false,
261
271
  isFormula: {},
262
272
  textStyle: "",
263
273
  labelFor: "",
@@ -55,6 +55,27 @@ const BlockSetting = ({ block, setProp }) => (
55
55
  </div>
56
56
  );
57
57
 
58
+ export const BlockOrInlineSetting = ({ block, inline, textStyle, setProp }) =>
59
+ !textStyle || !textStyle.startsWith("h") ? (
60
+ <BlockSetting block={block} setProp={setProp} />
61
+ ) : (
62
+ <div className="form-check">
63
+ <input
64
+ className="form-check-input"
65
+ name="inline"
66
+ type="checkbox"
67
+ checked={inline}
68
+ onChange={(e) => {
69
+ if (e.target) {
70
+ const target_value = e.target.checked;
71
+ setProp((prop) => (prop.inline = target_value));
72
+ }
73
+ }}
74
+ />
75
+ <label className="form-check-label">Inline display</label>
76
+ </div>
77
+ );
78
+
58
79
  export /**
59
80
  * @param {object} props
60
81
  * @param {function} props.setProp
@@ -1168,3 +1189,6 @@ export const recursivelyCloneToElems = (query) => (nodeId, ix) => {
1168
1189
  children
1169
1190
  );
1170
1191
  };
1192
+
1193
+ export const isBlock = (block, inline, textStyle) =>
1194
+ !textStyle || !textStyle.startsWith("h") ? block : !inline;
@@ -125,6 +125,7 @@ const layoutToNodes = (layout, query, actions, parent = "ROOT") => {
125
125
  text={segment.contents}
126
126
  isFormula={segment.isFormula || {}}
127
127
  block={segment.block || false}
128
+ inline={segment.inline || false}
128
129
  textStyle={segment.textStyle || ""}
129
130
  labelFor={segment.labelFor || ""}
130
131
  icon={segment.icon}
@@ -235,6 +236,7 @@ const layoutToNodes = (layout, query, actions, parent = "ROOT") => {
235
236
  breakpoints={segment.breakpoints || default_breakpoints(segment)}
236
237
  ncols={segment.besides.length}
237
238
  widths={getColWidths(segment)}
239
+ style={segment.style || {}}
238
240
  contents={segment.besides.map(toTag)}
239
241
  />
240
242
  );
@@ -261,6 +263,7 @@ const layoutToNodes = (layout, query, actions, parent = "ROOT") => {
261
263
  widths={getColWidths(segment)}
262
264
  breakpoints={segment.breakpoints || default_breakpoints(segment)}
263
265
  ncols={segment.besides.length}
266
+ style={segment.style || {}}
264
267
  contents={segment.besides.map(toTag)}
265
268
  />
266
269
  )
@@ -387,6 +390,7 @@ const craftToSaltcorn = (nodes, startFrom = "ROOT") => {
387
390
  type: "blank",
388
391
  contents: node.props.text,
389
392
  block: node.props.block,
393
+ inline: node.props.inline,
390
394
  textStyle: node.props.textStyle,
391
395
  isFormula: node.props.isFormula,
392
396
  labelFor: node.props.labelFor,
@@ -400,6 +404,7 @@ const craftToSaltcorn = (nodes, startFrom = "ROOT") => {
400
404
  return {
401
405
  besides: widths.map((w, ix) => go(nodes[node.linkedNodes["Col" + ix]])),
402
406
  breakpoints: node.props.breakpoints,
407
+ style: node.props.style,
403
408
  widths,
404
409
  };
405
410
  }