@saltcorn/builder 0.0.1-beta.1

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.
Files changed (38) hide show
  1. package/.babelrc +3 -0
  2. package/CHANGELOG.md +8 -0
  3. package/dist/builder_bundle.js +80 -0
  4. package/package.json +47 -0
  5. package/src/components/Builder.js +477 -0
  6. package/src/components/Library.js +224 -0
  7. package/src/components/RenderNode.js +203 -0
  8. package/src/components/Toolbox.js +688 -0
  9. package/src/components/context.js +9 -0
  10. package/src/components/elements/Action.js +204 -0
  11. package/src/components/elements/Aggregation.js +179 -0
  12. package/src/components/elements/BoxModelEditor.js +398 -0
  13. package/src/components/elements/Card.js +152 -0
  14. package/src/components/elements/Column.js +63 -0
  15. package/src/components/elements/Columns.js +201 -0
  16. package/src/components/elements/Container.js +947 -0
  17. package/src/components/elements/DropDownFilter.js +154 -0
  18. package/src/components/elements/DropMenu.js +156 -0
  19. package/src/components/elements/Empty.js +30 -0
  20. package/src/components/elements/Field.js +239 -0
  21. package/src/components/elements/HTMLCode.js +61 -0
  22. package/src/components/elements/Image.js +320 -0
  23. package/src/components/elements/JoinField.js +206 -0
  24. package/src/components/elements/LineBreak.js +46 -0
  25. package/src/components/elements/Link.js +305 -0
  26. package/src/components/elements/SearchBar.js +141 -0
  27. package/src/components/elements/Tabs.js +347 -0
  28. package/src/components/elements/Text.js +330 -0
  29. package/src/components/elements/ToggleFilter.js +243 -0
  30. package/src/components/elements/View.js +189 -0
  31. package/src/components/elements/ViewLink.js +225 -0
  32. package/src/components/elements/boxmodel.html +253 -0
  33. package/src/components/elements/faicons.js +1643 -0
  34. package/src/components/elements/utils.js +1217 -0
  35. package/src/components/preview_context.js +9 -0
  36. package/src/components/storage.js +506 -0
  37. package/src/index.js +73 -0
  38. package/webpack.config.js +21 -0
@@ -0,0 +1,320 @@
1
+ /**
2
+ * @category saltcorn-builder
3
+ * @module components/elements/Image
4
+ * @subcategory components / elements
5
+ */
6
+
7
+ import React, { useContext, Fragment } from "react";
8
+ import { useNode } from "@craftjs/core";
9
+ import optionsCtx from "../context";
10
+ import previewCtx from "../preview_context";
11
+ import { BoxModelEditor } from "./BoxModelEditor";
12
+
13
+ import {
14
+ blockProps,
15
+ BlockSetting,
16
+ reactifyStyles,
17
+ Accordion,
18
+ OrFormula,
19
+ } from "./utils";
20
+
21
+ export /**
22
+ * @param {object} props
23
+ * @param {string} props.fileid
24
+ * @param {boolean} props.block
25
+ * @param {string} props.srctype
26
+ * @param {string} props.url
27
+ * @param {string} props.alt
28
+ * @returns {span}
29
+ * @namespace
30
+ * @category saltcorn-builder
31
+ * @subcategory components
32
+ */
33
+ const Image = ({ fileid, block, srctype, url, alt, style }) => {
34
+ const {
35
+ selected,
36
+ connectors: { connect, drag },
37
+ } = useNode((node) => ({ selected: node.events.selected }));
38
+ const theurl = srctype === "File" ? `/files/serve/${fileid}` : url;
39
+ return (
40
+ <span {...blockProps(block)} ref={(dom) => connect(drag(dom))}>
41
+ {fileid === 0 ? (
42
+ "No images Available"
43
+ ) : (
44
+ <img
45
+ className={`${style && style.width ? "" : "w-100"} image-widget ${
46
+ selected ? "selected-node" : ""
47
+ }`}
48
+ style={reactifyStyles(style || {})}
49
+ src={theurl}
50
+ alt={alt}
51
+ ></img>
52
+ )}
53
+ </span>
54
+ );
55
+ };
56
+
57
+ export /**
58
+ * @returns {table}
59
+ * @namespace
60
+ * @category saltcorn-builder
61
+ * @subcategory components
62
+ */
63
+ const ImageSettings = () => {
64
+ const node = useNode((node) => ({
65
+ fileid: node.data.props.fileid,
66
+ field: node.data.props.field,
67
+ url: node.data.props.url,
68
+ filepath: node.data.props.filepath,
69
+ srctype: node.data.props.srctype,
70
+ alt: node.data.props.alt,
71
+ block: node.data.props.block,
72
+ style: node.data.props.style,
73
+ isFormula: node.data.props.isFormula,
74
+ imgResponsiveWidths: node.data.props.imgResponsiveWidths,
75
+ }));
76
+ const {
77
+ actions: { setProp },
78
+ fileid,
79
+ srctype,
80
+ field,
81
+ url,
82
+ alt,
83
+ block,
84
+ isFormula,
85
+ filepath,
86
+ imgResponsiveWidths,
87
+ style,
88
+ } = node;
89
+ const options = useContext(optionsCtx);
90
+ const { uploadedFiles, setUploadedFiles } = useContext(previewCtx);
91
+
92
+ const handleUpload = (e) => {
93
+ if (e.target.files && e.target.files.length > 0) {
94
+ const formData = new FormData();
95
+
96
+ formData.append("file", e.target.files[0]);
97
+ formData.append("min_role_read", options.min_role || 1);
98
+
99
+ fetch("/files/upload", {
100
+ method: "POST",
101
+ body: formData,
102
+ headers: {
103
+ "X-Requested-With": "XMLHttpRequest",
104
+ "CSRF-Token": options.csrfToken,
105
+ },
106
+ })
107
+ .then((response) => response.json())
108
+ .then((result) => {
109
+ setUploadedFiles((upFls) => [
110
+ ...upFls,
111
+ { id: result.success.id, filename: result.success.filename },
112
+ ]);
113
+ setProp((prop) => {
114
+ prop.fileid = result.success.id;
115
+ prop.srctype = "File";
116
+ });
117
+ })
118
+ .catch((error) => {
119
+ console.error("Error:", error);
120
+ });
121
+ }
122
+ };
123
+ const setAProp = (key) => (e) => {
124
+ if (e.target) {
125
+ const target_value = e.target.value;
126
+ setProp((prop) => (prop[key] = target_value));
127
+ }
128
+ };
129
+ return (
130
+ <Accordion>
131
+ <table accordiontitle="Select image">
132
+ <tbody>
133
+ <tr>
134
+ <td colSpan="2">
135
+ <i>
136
+ <small>Preview shown in canvas is indicative</small>
137
+ </i>
138
+ </td>
139
+ </tr>
140
+ <tr>
141
+ <td>
142
+ <label>Source</label>
143
+ </td>
144
+ <td>
145
+ <select
146
+ value={srctype}
147
+ className="form-control form-select"
148
+ onChange={setAProp("srctype")}
149
+ >
150
+ <option>File</option>
151
+ <option>URL</option>
152
+ <option>Upload</option>
153
+ {options.mode === "show" && <option>Field</option>}
154
+ </select>
155
+ </td>
156
+ </tr>
157
+ {srctype === "File" && (
158
+ <tr>
159
+ <td>
160
+ <label>File</label>
161
+ </td>
162
+ <td>
163
+ <select
164
+ value={fileid}
165
+ className="form-control form-select"
166
+ onChange={setAProp("fileid")}
167
+ >
168
+ {options.images.map((f, ix) => (
169
+ <option key={ix} value={f.id}>
170
+ {f.filename}
171
+ </option>
172
+ ))}
173
+ {(uploadedFiles || []).map((uf, ix) => (
174
+ <option key={ix} value={uf.id}>
175
+ {uf.filename}
176
+ </option>
177
+ ))}
178
+ </select>
179
+ </td>
180
+ </tr>
181
+ )}
182
+ {srctype === "URL" && (
183
+ <tr>
184
+ <td>
185
+ <label>URL</label>
186
+ </td>
187
+ <td>
188
+ <OrFormula nodekey="url" {...{ setProp, isFormula, node }}>
189
+ <input
190
+ type="text"
191
+ className="form-control"
192
+ value={url}
193
+ onChange={setAProp("url")}
194
+ />
195
+ </OrFormula>
196
+ </td>
197
+ </tr>
198
+ )}
199
+ {srctype === "Upload" && (
200
+ <tr>
201
+ <td>
202
+ <label>File</label>
203
+ </td>
204
+ <td>
205
+ <input
206
+ type="file"
207
+ className="form-control"
208
+ value={filepath}
209
+ onChange={handleUpload}
210
+ />
211
+ </td>
212
+ </tr>
213
+ )}
214
+ {srctype === "Field" && (
215
+ <tr>
216
+ <td>
217
+ <label>Field</label>
218
+ </td>
219
+ <td>
220
+ <select
221
+ value={field}
222
+ className="form-control form-select"
223
+ onChange={setAProp("field")}
224
+ >
225
+ {options.fields
226
+ .filter(
227
+ (f) =>
228
+ (f.type && f.type.name === "String") ||
229
+ f.reftable_name === "_sc_files"
230
+ )
231
+ .map((f, ix) => (
232
+ <option key={ix} value={f.name}>
233
+ {f.label}
234
+ </option>
235
+ ))}
236
+ </select>
237
+ </td>
238
+ </tr>
239
+ )}
240
+ {srctype !== "Upload" && (
241
+ <tr>
242
+ <td>
243
+ <label>Alt text</label>
244
+ </td>
245
+ <td>
246
+ <OrFormula nodekey="alt" {...{ setProp, isFormula, node }}>
247
+ <input
248
+ type="text"
249
+ className="form-control"
250
+ value={alt}
251
+ onChange={setAProp("alt")}
252
+ />
253
+ </OrFormula>
254
+ </td>
255
+ </tr>
256
+ )}
257
+ {srctype !== "Upload" && (
258
+ <tr>
259
+ <td>
260
+ <label>Responsive widths</label>
261
+ </td>
262
+
263
+ <td>
264
+ <input
265
+ type="text"
266
+ value={imgResponsiveWidths}
267
+ className="form-control"
268
+ onChange={setAProp("imgResponsiveWidths")}
269
+ />
270
+ <small>
271
+ <i>
272
+ List of widths to serve resized images, e.g. 300, 400, 600
273
+ </i>
274
+ </small>
275
+ </td>
276
+ </tr>
277
+ )}
278
+ {srctype !== "Upload" && (
279
+ <tr>
280
+ <td colSpan="2">
281
+ <BlockSetting block={block} setProp={setProp} />
282
+ </td>
283
+ </tr>
284
+ )}
285
+ </tbody>
286
+ </table>
287
+ <div accordiontitle="Box" className="w-100">
288
+ <BoxModelEditor setProp={setProp} node={node} sizeWithStyle={true} />
289
+ </div>
290
+ </Accordion>
291
+ );
292
+ };
293
+
294
+ /**
295
+ * @type {object}
296
+ */
297
+ Image.craft = {
298
+ displayName: "Image",
299
+ defaultProps: {
300
+ alt: "",
301
+ block: false,
302
+ isFormula: {},
303
+ srctype: "File",
304
+ style: {},
305
+ },
306
+ related: {
307
+ settings: ImageSettings,
308
+ segment_type: "image",
309
+ fields: [
310
+ { name: "alt", canBeFormula: true },
311
+ { name: "url", canBeFormula: true },
312
+ { name: "srctype", default: "File" },
313
+ { name: "fileid", default: 0 },
314
+ "field",
315
+ "block",
316
+ "imgResponsiveWidths",
317
+ { name: "style", default: {} },
318
+ ],
319
+ },
320
+ };
@@ -0,0 +1,206 @@
1
+ /**
2
+ * @category saltcorn-builder
3
+ * @module components/elements/JoinField
4
+ * @subcategory components / elements
5
+ */
6
+
7
+ import React, { useContext, useEffect, Fragment } from "react";
8
+ import { useNode } from "@craftjs/core";
9
+ import optionsCtx from "../context";
10
+ import {
11
+ blockProps,
12
+ BlockSetting,
13
+ TextStyleRow,
14
+ ConfigForm,
15
+ fetchFieldPreview,
16
+ } from "./utils";
17
+ import previewCtx from "../preview_context";
18
+
19
+ export /**
20
+ * @param {object} props
21
+ * @param {string} props.name
22
+ * @param {boolean} props.block
23
+ * @param {object} props.fieldview
24
+ * @param {string} props.textStyle
25
+ * @returns {span}
26
+ * @namespace
27
+ * @category saltcorn-builder
28
+ * @subcategory components
29
+ */
30
+ const JoinField = ({ name, block, fieldview, textStyle }) => {
31
+ const {
32
+ selected,
33
+ node_id,
34
+ connectors: { connect, drag },
35
+ } = useNode((node) => ({ selected: node.events.selected, node_id: node.id }));
36
+ const { previews, setPreviews } = useContext(previewCtx);
37
+ const myPreview = previews[node_id];
38
+ const options = useContext(optionsCtx);
39
+
40
+ useEffect(() => {
41
+ fetchFieldPreview({
42
+ options,
43
+ name,
44
+ fieldview,
45
+ configuration: {},
46
+ setPreviews,
47
+ node_id,
48
+ })();
49
+ }, []);
50
+ return (
51
+ <span
52
+ className={`${textStyle} ${selected ? "selected-node" : ""}`}
53
+ {...blockProps(block)}
54
+ ref={(dom) => connect(drag(dom))}
55
+ >
56
+ {myPreview ? (
57
+ <div
58
+ className="d-inline"
59
+ dangerouslySetInnerHTML={{ __html: myPreview }}
60
+ ></div>
61
+ ) : (
62
+ `[${name}]`
63
+ )}
64
+ </span>
65
+ );
66
+ };
67
+
68
+ export /**
69
+ * @returns {Fragment}
70
+ * @namespace
71
+ * @category saltcorn-builder
72
+ * @subcategory components
73
+ */
74
+ const JoinFieldSettings = () => {
75
+ const {
76
+ actions: { setProp },
77
+ name,
78
+ block,
79
+ textStyle,
80
+ configuration,
81
+ fieldview,
82
+ node_id,
83
+ } = useNode((node) => ({
84
+ name: node.data.props.name,
85
+ block: node.data.props.block,
86
+ textStyle: node.data.props.textStyle,
87
+ fieldview: node.data.props.fieldview,
88
+ configuration: node.data.props.configuration,
89
+
90
+ node_id: node.id,
91
+ }));
92
+ const options = useContext(optionsCtx);
93
+ const { setPreviews } = useContext(previewCtx);
94
+
95
+ const fvs = options.field_view_options[name];
96
+ const getCfgFields = (fv) =>
97
+ ((options.fieldViewConfigForms || {})[name] || {})[fv];
98
+ const cfgFields = getCfgFields(fieldview);
99
+ const refetchPreview = fetchFieldPreview({
100
+ options,
101
+ name,
102
+ fieldview,
103
+ configuration,
104
+ setPreviews,
105
+ node_id,
106
+ });
107
+ return (
108
+ <Fragment>
109
+ <i>
110
+ <small>
111
+ Previews shown in canvas are indicative based on random rows
112
+ </small>
113
+ </i>
114
+ <table className="w-100">
115
+ <tbody>
116
+ <tr>
117
+ <td>
118
+ <label>Join field</label>
119
+ </td>
120
+ <td>
121
+ <select
122
+ value={name}
123
+ className="form-control form-select"
124
+ onChange={(e) => {
125
+ setProp((prop) => (prop.name = e.target.value));
126
+ const newfvs = options.field_view_options[e.target.value];
127
+ if (newfvs && newfvs.length > 0) {
128
+ setProp((prop) => (prop.fieldview = newfvs[0]));
129
+ refetchPreview({
130
+ name: e.target.value,
131
+ fieldview: newfvs[0],
132
+ });
133
+ } else refetchPreview({ name: e.target.value });
134
+ }}
135
+ >
136
+ {options.parent_field_list.map((f, ix) => (
137
+ <option key={ix} value={f}>
138
+ {f}
139
+ </option>
140
+ ))}
141
+ </select>
142
+ </td>
143
+ </tr>
144
+ {fvs && (
145
+ <tr>
146
+ <td>
147
+ <label>Field view</label>
148
+ </td>
149
+
150
+ <td>
151
+ <select
152
+ value={fieldview}
153
+ className="form-control form-select"
154
+ onChange={(e) => {
155
+ setProp((prop) => (prop.fieldview = e.target.value));
156
+ refetchPreview({ fieldview: e.target.value });
157
+ }}
158
+ >
159
+ {(fvs || []).map((fvnm, ix) => (
160
+ <option key={ix} value={fvnm}>
161
+ {fvnm}
162
+ </option>
163
+ ))}
164
+ </select>
165
+ </td>
166
+ </tr>
167
+ )}
168
+ <tr>
169
+ <td></td>
170
+ <td>
171
+ <BlockSetting block={block} setProp={setProp} />
172
+ </td>
173
+ </tr>
174
+ <TextStyleRow textStyle={textStyle} setProp={setProp} />
175
+ </tbody>
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}
185
+ </Fragment>
186
+ );
187
+ };
188
+
189
+ /**
190
+ * @type {object}
191
+ */
192
+ JoinField.craft = {
193
+ displayName: "JoinField",
194
+ related: {
195
+ settings: JoinFieldSettings,
196
+ segment_type: "join_field",
197
+ column_type: "JoinField",
198
+ fields: [
199
+ { name: "name", segment_name: "join_field", column_name: "join_field" },
200
+ "fieldview",
201
+ "textStyle",
202
+ "block",
203
+ { name: "configuration", default: {} },
204
+ ],
205
+ },
206
+ };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * @category saltcorn-builder
3
+ * @module components/elements/LineBreak
4
+ * @subcategory components / elements
5
+ */
6
+
7
+ import React, { Fragment } from "react";
8
+ import { useNode } from "@craftjs/core";
9
+ import { SettingsFromFields } from "./utils";
10
+
11
+ export /**
12
+ * @param {object} [props = {}]
13
+ * @returns {Fragment}
14
+ * @namespace
15
+ * @category saltcorn-builder
16
+ * @subcategory components
17
+ */
18
+ const LineBreak = ({}) => {
19
+ const {
20
+ selected,
21
+ connectors: { connect, drag },
22
+ } = useNode((node) => ({ selected: node.events.selected }));
23
+ return (
24
+ <Fragment>
25
+ <span
26
+ className={selected ? "selected-node" : ""}
27
+ ref={(dom) => connect(drag(dom))}
28
+ >
29
+
30
+ </span>
31
+ <br />
32
+ </Fragment>
33
+ );
34
+ };
35
+
36
+ /**
37
+ * @type {object}
38
+ */
39
+ LineBreak.craft = {
40
+ displayName: "LineBreak",
41
+ related: {
42
+ settings: SettingsFromFields([]),
43
+ segment_type: "line_break",
44
+ fields: [],
45
+ },
46
+ };