@saltcorn/builder 0.9.3-beta.8 → 0.9.3
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/dist/builder_bundle.js +10 -10
- package/package.json +1 -1
- package/src/components/Builder.js +82 -73
- package/src/components/Library.js +28 -10
- package/src/components/elements/Aggregation.js +2 -2
- package/src/components/elements/Field.js +22 -5
- package/src/components/elements/Image.js +229 -227
- package/src/components/elements/JoinField.js +25 -4
- package/src/components/elements/ToggleFilter.js +6 -8
- package/src/components/elements/View.js +42 -19
- package/src/components/elements/ViewLink.js +25 -11
- package/src/components/elements/utils.js +80 -8
- package/src/components/relations_context.js +3 -0
- package/tests/relations_finder.test.js +8 -0
|
@@ -32,28 +32,29 @@ export /**
|
|
|
32
32
|
* @category saltcorn-builder
|
|
33
33
|
* @subcategory components
|
|
34
34
|
*/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
35
|
+
const Image = ({ fileid, block, srctype, url, alt, style }) => {
|
|
36
|
+
const {
|
|
37
|
+
selected,
|
|
38
|
+
connectors: { connect, drag },
|
|
39
|
+
} = useNode((node) => ({ selected: node.events.selected }));
|
|
40
|
+
const theurl = srctype === "File" ? `/files/serve/${fileid}` : url;
|
|
41
|
+
return (
|
|
42
|
+
<span {...blockProps(block)} ref={(dom) => connect(drag(dom))}>
|
|
43
|
+
{fileid === 0 ? (
|
|
44
|
+
"No images Available"
|
|
45
|
+
) : (
|
|
46
|
+
<img
|
|
47
|
+
className={`${style && style.width ? "" : "w-100"} image-widget ${
|
|
48
|
+
selected ? "selected-node" : ""
|
|
49
|
+
}`}
|
|
50
|
+
style={reactifyStyles(style || {})}
|
|
51
|
+
src={theurl}
|
|
52
|
+
alt={alt}
|
|
53
|
+
></img>
|
|
54
|
+
)}
|
|
55
|
+
</span>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
57
58
|
|
|
58
59
|
export /**
|
|
59
60
|
* @returns {table}
|
|
@@ -61,232 +62,233 @@ export /**
|
|
|
61
62
|
* @category saltcorn-builder
|
|
62
63
|
* @subcategory components
|
|
63
64
|
*/
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
65
|
+
const ImageSettings = () => {
|
|
66
|
+
const node = useNode((node) => ({
|
|
67
|
+
fileid: node.data.props.fileid,
|
|
68
|
+
field: node.data.props.field,
|
|
69
|
+
url: node.data.props.url,
|
|
70
|
+
filepath: node.data.props.filepath,
|
|
71
|
+
srctype: node.data.props.srctype,
|
|
72
|
+
alt: node.data.props.alt,
|
|
73
|
+
block: node.data.props.block,
|
|
74
|
+
style: node.data.props.style,
|
|
75
|
+
isFormula: node.data.props.isFormula,
|
|
76
|
+
imgResponsiveWidths: node.data.props.imgResponsiveWidths,
|
|
77
|
+
}));
|
|
78
|
+
const {
|
|
79
|
+
actions: { setProp },
|
|
80
|
+
fileid,
|
|
81
|
+
srctype,
|
|
82
|
+
field,
|
|
83
|
+
url,
|
|
84
|
+
alt,
|
|
85
|
+
block,
|
|
86
|
+
isFormula,
|
|
87
|
+
filepath,
|
|
88
|
+
imgResponsiveWidths,
|
|
89
|
+
style,
|
|
90
|
+
} = node;
|
|
91
|
+
const options = useContext(optionsCtx);
|
|
92
|
+
const { uploadedFiles, setUploadedFiles } = useContext(previewCtx);
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
const handleUpload = (e) => {
|
|
95
|
+
if (e.target.files && e.target.files.length > 0) {
|
|
96
|
+
const formData = new FormData();
|
|
96
97
|
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
formData.append("file", e.target.files[0]);
|
|
99
|
+
formData.append("min_role_read", options.min_role || 1);
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
});
|
|
118
|
-
})
|
|
119
|
-
.catch((error) => {
|
|
120
|
-
console.error("Error:", error);
|
|
101
|
+
fetch("/files/upload", {
|
|
102
|
+
method: "POST",
|
|
103
|
+
body: formData,
|
|
104
|
+
headers: {
|
|
105
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
106
|
+
"CSRF-Token": options.csrfToken,
|
|
107
|
+
},
|
|
108
|
+
})
|
|
109
|
+
.then((response) => response.json())
|
|
110
|
+
.then((result) => {
|
|
111
|
+
setUploadedFiles((upFls) => [
|
|
112
|
+
...upFls,
|
|
113
|
+
{ id: result.success.location, filename: result.success.filename },
|
|
114
|
+
]);
|
|
115
|
+
setProp((prop) => {
|
|
116
|
+
prop.fileid = result.success.location;
|
|
117
|
+
prop.srctype = "File";
|
|
121
118
|
});
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
119
|
+
})
|
|
120
|
+
.catch((error) => {
|
|
121
|
+
console.error("Error:", error);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const setAProp = setAPropGen(setProp);
|
|
126
|
+
const sourceOpts = ["File", "URL", "Upload"];
|
|
127
|
+
if (options.mode === "show") sourceOpts.push("Field");
|
|
128
|
+
return (
|
|
129
|
+
<Accordion>
|
|
130
|
+
<table accordiontitle="Select image">
|
|
131
|
+
<tbody>
|
|
132
|
+
<tr>
|
|
133
|
+
<td colSpan="2">
|
|
134
|
+
<i>
|
|
135
|
+
<small>Preview shown in canvas is indicative</small>
|
|
136
|
+
</i>
|
|
137
|
+
</td>
|
|
138
|
+
</tr>
|
|
139
|
+
<tr>
|
|
140
|
+
<td>
|
|
141
|
+
<label>Source</label>
|
|
142
|
+
</td>
|
|
143
|
+
<td>
|
|
144
|
+
<select
|
|
145
|
+
value={srctype}
|
|
146
|
+
className="form-control form-select"
|
|
147
|
+
onChange={setAProp("srctype")}
|
|
148
|
+
>
|
|
149
|
+
{buildOptions(sourceOpts)}
|
|
150
|
+
</select>
|
|
151
|
+
</td>
|
|
152
|
+
</tr>
|
|
153
|
+
{srctype === "File" && (
|
|
138
154
|
<tr>
|
|
139
155
|
<td>
|
|
140
|
-
<label>
|
|
156
|
+
<label>File</label>
|
|
141
157
|
</td>
|
|
142
158
|
<td>
|
|
143
159
|
<select
|
|
144
|
-
value={
|
|
160
|
+
value={fileid}
|
|
145
161
|
className="form-control form-select"
|
|
146
|
-
onChange={setAProp("
|
|
162
|
+
onChange={setAProp("fileid")}
|
|
163
|
+
onBlur={setAProp("fileid")}
|
|
147
164
|
>
|
|
148
|
-
{
|
|
165
|
+
{options.images.map((f, ix) => (
|
|
166
|
+
<option key={ix} value={f.id}>
|
|
167
|
+
{f.filename}
|
|
168
|
+
</option>
|
|
169
|
+
))}
|
|
170
|
+
{(uploadedFiles || []).map((uf, ix) => (
|
|
171
|
+
<option key={ix} value={uf.id}>
|
|
172
|
+
{uf.filename}
|
|
173
|
+
</option>
|
|
174
|
+
))}
|
|
149
175
|
</select>
|
|
150
176
|
</td>
|
|
151
177
|
</tr>
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
</
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
className="form-control form-select"
|
|
161
|
-
onChange={setAProp("fileid")}
|
|
162
|
-
onBlur={setAProp("fileid")}
|
|
163
|
-
>
|
|
164
|
-
{options.images.map((f, ix) => (
|
|
165
|
-
<option key={ix} value={f.id}>
|
|
166
|
-
{f.filename}
|
|
167
|
-
</option>
|
|
168
|
-
))}
|
|
169
|
-
{(uploadedFiles || []).map((uf, ix) => (
|
|
170
|
-
<option key={ix} value={uf.id}>
|
|
171
|
-
{uf.filename}
|
|
172
|
-
</option>
|
|
173
|
-
))}
|
|
174
|
-
</select>
|
|
175
|
-
</td>
|
|
176
|
-
</tr>
|
|
177
|
-
)}
|
|
178
|
-
{srctype === "URL" && (
|
|
179
|
-
<tr>
|
|
180
|
-
<td>
|
|
181
|
-
<label>URL</label>
|
|
182
|
-
</td>
|
|
183
|
-
<td>
|
|
184
|
-
<OrFormula nodekey="url" {...{ setProp, isFormula, node }}>
|
|
185
|
-
<input
|
|
186
|
-
type="text"
|
|
187
|
-
className="form-control"
|
|
188
|
-
value={url}
|
|
189
|
-
onChange={setAProp("url")}
|
|
190
|
-
/>
|
|
191
|
-
</OrFormula>
|
|
192
|
-
</td>
|
|
193
|
-
</tr>
|
|
194
|
-
)}
|
|
195
|
-
{srctype === "Upload" && (
|
|
196
|
-
<tr>
|
|
197
|
-
<td>
|
|
198
|
-
<label>File</label>
|
|
199
|
-
</td>
|
|
200
|
-
<td>
|
|
178
|
+
)}
|
|
179
|
+
{srctype === "URL" && (
|
|
180
|
+
<tr>
|
|
181
|
+
<td>
|
|
182
|
+
<label>URL</label>
|
|
183
|
+
</td>
|
|
184
|
+
<td>
|
|
185
|
+
<OrFormula nodekey="url" {...{ setProp, isFormula, node }}>
|
|
201
186
|
<input
|
|
202
|
-
type="
|
|
187
|
+
type="text"
|
|
203
188
|
className="form-control"
|
|
204
|
-
value={
|
|
205
|
-
onChange={
|
|
189
|
+
value={url}
|
|
190
|
+
onChange={setAProp("url")}
|
|
206
191
|
/>
|
|
207
|
-
</
|
|
208
|
-
</
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
</
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
<
|
|
192
|
+
</OrFormula>
|
|
193
|
+
</td>
|
|
194
|
+
</tr>
|
|
195
|
+
)}
|
|
196
|
+
{srctype === "Upload" && (
|
|
197
|
+
<tr>
|
|
198
|
+
<td>
|
|
199
|
+
<label>File</label>
|
|
200
|
+
</td>
|
|
201
|
+
<td>
|
|
202
|
+
<input
|
|
203
|
+
type="file"
|
|
204
|
+
className="form-control"
|
|
205
|
+
value={filepath}
|
|
206
|
+
onChange={handleUpload}
|
|
207
|
+
/>
|
|
208
|
+
</td>
|
|
209
|
+
</tr>
|
|
210
|
+
)}
|
|
211
|
+
{srctype === "Field" && (
|
|
212
|
+
<tr>
|
|
213
|
+
<td>
|
|
214
|
+
<label>Field</label>
|
|
215
|
+
</td>
|
|
216
|
+
<td>
|
|
217
|
+
<select
|
|
218
|
+
value={field}
|
|
219
|
+
className="form-control form-select"
|
|
220
|
+
onChange={setAProp("field")}
|
|
221
|
+
>
|
|
222
|
+
<option value=""></option>
|
|
223
|
+
{options.fields
|
|
224
|
+
.filter(
|
|
225
|
+
(f) =>
|
|
226
|
+
f.type === "String" ||
|
|
227
|
+
(f.type && f.type.name === "String") ||
|
|
228
|
+
(f.type && f.type === "File")
|
|
229
|
+
)
|
|
230
|
+
.map((f, ix) => (
|
|
231
|
+
<option key={ix} value={f.name}>
|
|
232
|
+
{f.label}
|
|
233
|
+
</option>
|
|
234
|
+
))}
|
|
235
|
+
</select>
|
|
236
|
+
</td>
|
|
237
|
+
</tr>
|
|
238
|
+
)}
|
|
239
|
+
{srctype !== "Upload" && (
|
|
240
|
+
<tr>
|
|
241
|
+
<td>
|
|
242
|
+
<label>Alt text</label>
|
|
243
|
+
</td>
|
|
244
|
+
<td>
|
|
245
|
+
<OrFormula nodekey="alt" {...{ setProp, isFormula, node }}>
|
|
261
246
|
<input
|
|
262
247
|
type="text"
|
|
263
|
-
value={imgResponsiveWidths}
|
|
264
248
|
className="form-control"
|
|
265
|
-
|
|
249
|
+
value={alt}
|
|
250
|
+
onChange={setAProp("alt")}
|
|
266
251
|
/>
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
252
|
+
</OrFormula>
|
|
253
|
+
</td>
|
|
254
|
+
</tr>
|
|
255
|
+
)}
|
|
256
|
+
{srctype !== "Upload" && (
|
|
257
|
+
<tr>
|
|
258
|
+
<td style={{ verticalAlign: "top" }}>
|
|
259
|
+
<label>Responsive widths</label>
|
|
260
|
+
</td>
|
|
261
|
+
|
|
262
|
+
<td>
|
|
263
|
+
<input
|
|
264
|
+
type="text"
|
|
265
|
+
value={imgResponsiveWidths}
|
|
266
|
+
className="form-control"
|
|
267
|
+
onChange={setAProp("imgResponsiveWidths")}
|
|
268
|
+
/>
|
|
269
|
+
<small>
|
|
270
|
+
<i>
|
|
271
|
+
List of widths to serve resized images, e.g. 300, 400, 600
|
|
272
|
+
</i>
|
|
273
|
+
</small>
|
|
274
|
+
</td>
|
|
275
|
+
</tr>
|
|
276
|
+
)}
|
|
277
|
+
{srctype !== "Upload" && (
|
|
278
|
+
<tr>
|
|
279
|
+
<td colSpan="2">
|
|
280
|
+
<BlockSetting block={block} setProp={setProp} />
|
|
281
|
+
</td>
|
|
282
|
+
</tr>
|
|
283
|
+
)}
|
|
284
|
+
</tbody>
|
|
285
|
+
</table>
|
|
286
|
+
<div accordiontitle="Box" className="w-100">
|
|
287
|
+
<BoxModelEditor setProp={setProp} node={node} sizeWithStyle={true} />
|
|
288
|
+
</div>
|
|
289
|
+
</Accordion>
|
|
290
|
+
);
|
|
291
|
+
};
|
|
290
292
|
|
|
291
293
|
/**
|
|
292
294
|
* @type {object}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
/* eslint-env jquery */
|
|
7
7
|
|
|
8
|
-
import React, { useContext, useEffect, Fragment } from "react";
|
|
8
|
+
import React, { useContext, useEffect, useState, Fragment } from "react";
|
|
9
9
|
import { useNode } from "@craftjs/core";
|
|
10
10
|
import optionsCtx from "../context";
|
|
11
11
|
import {
|
|
@@ -390,9 +390,30 @@ const JoinFieldSettings = () => {
|
|
|
390
390
|
const { setPreviews } = useContext(previewCtx);
|
|
391
391
|
|
|
392
392
|
const fvs = options.field_view_options[name];
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
const cfgFields =
|
|
393
|
+
|
|
394
|
+
const [fetchedCfgFields, setFetchedCfgFields] = useState([]);
|
|
395
|
+
const cfgFields = fetchedCfgFields;
|
|
396
|
+
useEffect(() => {
|
|
397
|
+
fetch(`/field/fieldviewcfgform/${options.tableName}?accept=json`, {
|
|
398
|
+
method: "POST",
|
|
399
|
+
headers: {
|
|
400
|
+
"Content-Type": "application/json",
|
|
401
|
+
"CSRF-Token": options.csrfToken,
|
|
402
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
403
|
+
},
|
|
404
|
+
body: JSON.stringify({
|
|
405
|
+
join_field: name,
|
|
406
|
+
join_fieldview: fieldview,
|
|
407
|
+
type: "JoinField",
|
|
408
|
+
}),
|
|
409
|
+
})
|
|
410
|
+
.then(function (response) {
|
|
411
|
+
if (response.status < 399) return response.json();
|
|
412
|
+
else return [];
|
|
413
|
+
})
|
|
414
|
+
.then(setFetchedCfgFields);
|
|
415
|
+
}, [name, fieldview]);
|
|
416
|
+
|
|
396
417
|
const refetchPreview = fetchFieldPreview({
|
|
397
418
|
options,
|
|
398
419
|
name,
|
|
@@ -7,12 +7,7 @@
|
|
|
7
7
|
import React, { useContext, Fragment } from "react";
|
|
8
8
|
import { useNode } from "@craftjs/core";
|
|
9
9
|
import optionsCtx from "../context";
|
|
10
|
-
import {
|
|
11
|
-
blockProps,
|
|
12
|
-
BlockSetting,
|
|
13
|
-
setAPropGen,
|
|
14
|
-
buildOptions,
|
|
15
|
-
} from "./utils";
|
|
10
|
+
import { blockProps, BlockSetting, setAPropGen, buildOptions } from "./utils";
|
|
16
11
|
|
|
17
12
|
export /**
|
|
18
13
|
* @param {object} props
|
|
@@ -82,7 +77,8 @@ const ToggleFilterSettings = () => {
|
|
|
82
77
|
const options = useContext(optionsCtx);
|
|
83
78
|
const field = options.fields.find((f) => f.name === name);
|
|
84
79
|
const preset_options = field.preset_options;
|
|
85
|
-
const isBool =
|
|
80
|
+
const isBool =
|
|
81
|
+
field && (field.type?.name === "Bool" || field.type === "Bool");
|
|
86
82
|
const setAProp = setAPropGen(setProp);
|
|
87
83
|
return (
|
|
88
84
|
<table className="w-100">
|
|
@@ -101,7 +97,9 @@ const ToggleFilterSettings = () => {
|
|
|
101
97
|
const value = e.target.value;
|
|
102
98
|
setProp((prop) => (prop.name = value));
|
|
103
99
|
const field = options.fields.find((f) => f.name === value);
|
|
104
|
-
const isBool =
|
|
100
|
+
const isBool =
|
|
101
|
+
field &&
|
|
102
|
+
(field.type?.name === "Bool" || field.type === "Bool");
|
|
105
103
|
if (isBool) setProp((prop) => (prop.value = "on"));
|
|
106
104
|
setProp((prop) => (prop.preset_value = ""));
|
|
107
105
|
}
|