@saltcorn/builder 1.6.0-alpha.1 → 1.6.0-alpha.11

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 (40) hide show
  1. package/dist/builder_bundle.js +85 -1
  2. package/dist/builder_bundle.js.LICENSE.txt +18 -51
  3. package/package.json +31 -27
  4. package/src/components/Builder.js +445 -155
  5. package/src/components/Library.js +25 -13
  6. package/src/components/RenderNode.js +26 -8
  7. package/src/components/Toolbox.js +333 -269
  8. package/src/components/elements/Action.js +144 -29
  9. package/src/components/elements/Aggregation.js +20 -23
  10. package/src/components/elements/ArrayManager.js +17 -10
  11. package/src/components/elements/BoxModelEditor.js +19 -17
  12. package/src/components/elements/Card.js +47 -34
  13. package/src/components/elements/Clone.js +74 -2
  14. package/src/components/elements/Column.js +1 -1
  15. package/src/components/elements/Columns.js +130 -121
  16. package/src/components/elements/Container.js +185 -92
  17. package/src/components/elements/DropDownFilter.js +10 -8
  18. package/src/components/elements/DropMenu.js +18 -9
  19. package/src/components/elements/Field.js +9 -7
  20. package/src/components/elements/HTMLCode.js +3 -1
  21. package/src/components/elements/Image.js +20 -15
  22. package/src/components/elements/JoinField.js +15 -11
  23. package/src/components/elements/Link.js +18 -16
  24. package/src/components/elements/ListColumn.js +7 -3
  25. package/src/components/elements/ListColumns.js +4 -1
  26. package/src/components/elements/MonacoEditor.js +4 -2
  27. package/src/components/elements/Page.js +7 -4
  28. package/src/components/elements/RelationBadges.js +16 -11
  29. package/src/components/elements/RelationOnDemandPicker.js +18 -12
  30. package/src/components/elements/SearchBar.js +37 -10
  31. package/src/components/elements/Table.js +72 -65
  32. package/src/components/elements/Tabs.js +18 -15
  33. package/src/components/elements/Text.js +19 -14
  34. package/src/components/elements/ToggleFilter.js +28 -25
  35. package/src/components/elements/View.js +36 -18
  36. package/src/components/elements/ViewLink.js +15 -11
  37. package/src/components/elements/utils.js +224 -55
  38. package/src/components/storage.js +33 -134
  39. package/src/hooks/useTranslation.js +11 -0
  40. package/src/index.js +6 -3
@@ -4,13 +4,14 @@
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 { Column } from "./Column";
9
+ import useTranslation from "../../hooks/useTranslation";
10
+ import PreviewCtx from "../preview_context";
9
11
 
10
12
  import { Element, useNode } from "@craftjs/core";
11
13
  import {
12
14
  Accordion,
13
- ConfigField,
14
15
  SettingsRow,
15
16
  reactifyStyles,
16
17
  SettingsSectionHeaderRow,
@@ -18,6 +19,7 @@ import {
18
19
  parseStyles,
19
20
  } from "./utils";
20
21
  import { BoxModelEditor } from "./BoxModelEditor";
22
+ import { ArrayManager } from "./ArrayManager";
21
23
  import {
22
24
  AlignTop,
23
25
  AlignMiddle,
@@ -65,6 +67,35 @@ const resetWidths = (ncols) => ntimes(ncols - 1, () => Math.floor(12 / ncols));
65
67
  const getWidth = (widths, colix) =>
66
68
  colix < widths.length ? widths[colix] : 12 - sum(widths);
67
69
 
70
+ /**
71
+ * Bootstrap breakpoint minimum widths (px)
72
+ */
73
+ const BREAKPOINT_MIN_WIDTH = {
74
+ "": 0,
75
+ sm: 576,
76
+ md: 768,
77
+ lg: 992,
78
+ xl: 1200,
79
+ };
80
+
81
+ const PREVIEW_DEVICE_WIDTH = {
82
+ desktop: Infinity,
83
+ tablet: 768,
84
+ mobile: 375,
85
+ };
86
+
87
+ const getColClass = (width, breakpoint, previewDevice) => {
88
+ if (!previewDevice || previewDevice === "desktop") {
89
+ const bp = breakpoint || "sm";
90
+ return bp ? `col-${bp}-${width}` : `col-${width}`;
91
+ }
92
+ const deviceWidth = PREVIEW_DEVICE_WIDTH[previewDevice] || Infinity;
93
+ const bpMin = BREAKPOINT_MIN_WIDTH[breakpoint || "sm"] || 0;
94
+ if (deviceWidth < bpMin) return "col-12";
95
+ const bp = breakpoint || "sm";
96
+ return bp ? `col-${bp}-${width}` : `col-${width}`;
97
+ };
98
+
68
99
  export /**
69
100
  * @param {object} opts
70
101
  * @param {number[]} opts.widths
@@ -87,11 +118,13 @@ const Columns = ({
87
118
  colClasses,
88
119
  colStyles,
89
120
  customClass,
121
+ breakpoints,
90
122
  }) => {
91
123
  const {
92
124
  selected,
93
125
  connectors: { connect, drag },
94
126
  } = useNode((node) => ({ selected: node.events.selected }));
127
+ const { previewDevice } = useContext(PreviewCtx);
95
128
  return (
96
129
  <div
97
130
  className={`row builder-columns ${customClass || ""} ${selected ? "selected-node" : ""} ${
@@ -103,7 +136,11 @@ const Columns = ({
103
136
  {ntimes(ncols, (ix) => (
104
137
  <div
105
138
  key={ix}
106
- className={`split-col col-sm-${getWidth(widths, ix)} text-${
139
+ className={`split-col ${getColClass(
140
+ getWidth(widths, ix),
141
+ breakpoints?.[ix],
142
+ previewDevice
143
+ )} text-${
107
144
  aligns?.[ix]
108
145
  } align-items-${vAligns?.[ix]} ${colClasses?.[ix] || ""}`}
109
146
  style={parseStyles(colStyles?.[ix] || "")}
@@ -124,6 +161,7 @@ export /**
124
161
  * @subcategory components
125
162
  */
126
163
  const ColumnsSettings = () => {
164
+ const { t } = useTranslation();
127
165
  const node = useNode((node) => ({
128
166
  widths: node.data.props.widths,
129
167
  ncols: node.data.props.ncols,
@@ -154,122 +192,93 @@ const ColumnsSettings = () => {
154
192
  currentSettingsTab,
155
193
  } = node;
156
194
  const colSetsNode = {
157
- vAlign: vAligns?.[setting_col_n - 1],
158
- hAlign: aligns?.[setting_col_n - 1],
159
- colClass: colClasses?.[setting_col_n - 1] || "",
160
- colStyle: colStyles?.[setting_col_n - 1] || "",
195
+ vAlign: vAligns?.[setting_col_n],
196
+ hAlign: aligns?.[setting_col_n],
197
+ colClass: colClasses?.[setting_col_n] || "",
198
+ colStyle: colStyles?.[setting_col_n] || "",
161
199
  };
162
200
  return (
163
201
  <Accordion
164
202
  value={currentSettingsTab}
165
203
  onChange={(ix) => setProp((prop) => (prop.currentSettingsTab = ix))}
166
204
  >
167
- <table accordiontitle="Column properties">
168
- <tbody>
169
- <tr>
170
- <td colSpan="3">
171
- <label>Number of columns</label>
172
- </td>
173
- <td>
174
- <input
175
- type="number"
176
- value={ncols}
177
- className="form-control"
178
- step="1"
179
- min="1"
180
- max="6"
181
- onChange={(e) => {
182
- if (!e.target) return;
183
- const value = e.target.value;
184
- setProp((prop) => {
185
- prop.ncols = value;
186
- prop.widths = resetWidths(value);
187
- });
188
- }}
189
- />
190
- </td>
191
- </tr>
192
- <tr>
193
- <th colSpan="4">Widths &amp; Breakpoint</th>
194
- </tr>
195
- {ntimes(ncols, (ix) => (
196
- <Fragment key={ix}>
197
- <tr>
198
- <th colSpan="4">Column {ix + 1}</th>
199
- </tr>
200
- <tr>
201
- <td>
202
- <label>Width</label>
203
- </td>
204
- <td align="right">
205
- {ix < ncols - 1 ? (
206
- <input
207
- type="number"
208
- value={widths[ix]}
209
- className="form-control"
210
- step="1"
211
- min="1"
212
- max={12 - (sum(widths) - widths[ix]) - 1}
213
- onChange={(e) => {
214
- if (!e.target) return;
215
- const value = e.target.value;
216
- setProp((prop) => (prop.widths[ix] = +value));
217
- }}
218
- />
219
- ) : (
220
- `${12 - sum(widths)}`
221
- )}
222
- </td>
223
- <td>/12</td>
224
- <td>
225
- <select
226
- className="form-control form-select"
227
- value={breakpoints[ix]}
205
+ <div accordiontitle={t("Column properties")}>
206
+ <ArrayManager
207
+ node={node}
208
+ setProp={setProp}
209
+ countProp={"ncols"}
210
+ currentProp={"setting_col_n"}
211
+ managedArrays={["widths", "breakpoints", "aligns", "vAligns", "colClasses", "colStyles"]}
212
+ manageContents={true}
213
+ contentsKey={"besides"}
214
+ initialAddProps={{
215
+ breakpoints: "sm",
216
+ }}
217
+ onLayoutChange={(layout, action) => {
218
+ if (action === "add" || action === "delete") {
219
+ const n = layout.besides.length;
220
+ layout.widths = ntimes(n, () => Math.floor(12 / n));
221
+ }
222
+ }}
223
+ />
224
+ <table className="w-100 mt-2">
225
+ <tbody>
226
+ <tr>
227
+ <th colSpan="4">{t("Width & Breakpoint")}</th>
228
+ </tr>
229
+ <tr>
230
+ <td>
231
+ <label>{t("Width")}</label>
232
+ </td>
233
+ <td colSpan="3" align="right">
234
+ {setting_col_n < ncols - 1 ? (
235
+ <input
236
+ type="number"
237
+ value={widths[setting_col_n]}
238
+ className="form-control"
239
+ step="1"
240
+ min="1"
241
+ max={12 - (sum(widths) - widths[setting_col_n]) - 1}
228
242
  onChange={(e) => {
229
243
  if (!e.target) return;
230
244
  const value = e.target.value;
231
- setProp((prop) => (prop.breakpoints[ix] = value));
245
+ setProp((prop) => (prop.widths[setting_col_n] = +value));
232
246
  }}
233
- >
234
- <option disabled>Breakpoint</option>
235
- <option value="">none</option>
236
- {buildBootstrapOptions(["sm", "md", "lg"])}
237
- </select>
238
- </td>
239
- </tr>
240
- </Fragment>
241
- ))}
242
- </tbody>
243
- </table>
244
- <div accordiontitle="Column settings">
245
- Settings for column #
246
- <ConfigField
247
- field={{
248
- name: "setting_col_n",
249
- label: "Column number",
250
- type: "btn_select",
251
- options: ntimes(ncols, (i) => ({
252
- value: i + 1,
253
- title: `${i + 1}`,
254
- label: `${i + 1}`,
255
- })),
256
- }}
257
- node={node}
258
- setProp={setProp}
259
- props={node}
260
- ></ConfigField>
261
- <table className="w-100">
262
- <tbody>
263
- <SettingsSectionHeaderRow title="Align" />
247
+ />
248
+ ) : (
249
+ `${12 - sum(widths)}`
250
+ )}
251
+ </td>
252
+ </tr>
253
+ <tr>
254
+ <td>
255
+ <label>{t("Breakpoint")}</label>
256
+ </td>
257
+ <td colSpan="3">
258
+ <select
259
+ className="form-control form-select"
260
+ value={breakpoints[setting_col_n]}
261
+ onChange={(e) => {
262
+ if (!e.target) return;
263
+ const value = e.target.value;
264
+ setProp((prop) => (prop.breakpoints[setting_col_n] = value));
265
+ }}
266
+ >
267
+ <option value="">{t("none")}</option>
268
+ {buildBootstrapOptions(["sm", "md", "lg"])}
269
+ </select>
270
+ </td>
271
+ </tr>
272
+ <SettingsSectionHeaderRow title={t("Align")} />
264
273
  <SettingsRow
265
274
  field={{
266
275
  name: "vAlign",
267
- label: "Vertical",
276
+ label: t("Vertical"),
268
277
  type: "btn_select",
269
278
  options: [
270
- { value: "start", title: "Start", label: <AlignTop /> },
271
- { value: "center", title: "Center", label: <AlignMiddle /> },
272
- { value: "end", title: "End", label: <AlignBottom /> },
279
+ { value: "start", title: t("Start"), label: <AlignTop /> },
280
+ { value: "center", title: t("Center"), label: <AlignMiddle /> },
281
+ { value: "end", title: t("End"), label: <AlignBottom /> },
273
282
  ],
274
283
  }}
275
284
  node={colSetsNode}
@@ -277,19 +286,19 @@ const ColumnsSettings = () => {
277
286
  onChange={(k, v) =>
278
287
  setProp((prop) => {
279
288
  if (!prop.vAligns) prop.vAligns = [];
280
- prop.vAligns[setting_col_n - 1] = v;
289
+ prop.vAligns[setting_col_n] = v;
281
290
  })
282
291
  }
283
292
  />
284
293
  <SettingsRow
285
294
  field={{
286
295
  name: "hAlign",
287
- label: "Horizontal",
296
+ label: t("Horizontal"),
288
297
  type: "btn_select",
289
298
  options: [
290
- { value: "start", title: "Left", label: <AlignStart /> },
291
- { value: "center", title: "Center", label: <AlignCenter /> },
292
- { value: "end", title: "Right", label: <AlignEnd /> },
299
+ { value: "start", title: t("Left"), label: <AlignStart /> },
300
+ { value: "center", title: t("Center"), label: <AlignCenter /> },
301
+ { value: "end", title: t("Right"), label: <AlignEnd /> },
293
302
  ],
294
303
  }}
295
304
  node={colSetsNode}
@@ -297,14 +306,14 @@ const ColumnsSettings = () => {
297
306
  onChange={(k, v) =>
298
307
  setProp((prop) => {
299
308
  if (!prop.aligns) prop.aligns = [];
300
- prop.aligns[setting_col_n - 1] = v;
309
+ prop.aligns[setting_col_n] = v;
301
310
  })
302
311
  }
303
312
  />
304
313
  <SettingsRow
305
314
  field={{
306
315
  name: "colClass",
307
- label: "Class",
316
+ label: t("Class"),
308
317
  type: "String",
309
318
  }}
310
319
  node={colSetsNode}
@@ -312,34 +321,34 @@ const ColumnsSettings = () => {
312
321
  onChange={(k, v) =>
313
322
  setProp((prop) => {
314
323
  if (!prop.colClasses) prop.colClasses = [];
315
- prop.colClasses[setting_col_n - 1] = v;
324
+ prop.colClasses[setting_col_n] = v;
316
325
  })
317
326
  }
318
327
  />
319
328
  <SettingsRow
320
329
  field={{
321
330
  name: "colStyle",
322
- label: "CSS",
323
- type: "textarea",
331
+ label: t("CSS"),
332
+ type: "String",
324
333
  }}
325
334
  node={colSetsNode}
326
335
  setProp={setProp}
327
336
  onChange={(k, v) =>
328
337
  setProp((prop) => {
329
338
  if (!prop.colStyles) prop.colStyles = [];
330
- prop.colStyles[setting_col_n - 1] = v;
339
+ prop.colStyles[setting_col_n] = v;
331
340
  })
332
341
  }
333
342
  />
334
343
  </tbody>
335
344
  </table>
336
345
  </div>
337
- <table className="w-100" accordiontitle="Gutters and class">
346
+ <table className="w-100" accordiontitle={t("Gutters and class")}>
338
347
  <tbody>
339
348
  <SettingsRow
340
349
  field={{
341
350
  name: "gx",
342
- label: "Horizontal 0-5",
351
+ label: t("Horizontal 0-5"),
343
352
  type: "Integer",
344
353
  }}
345
354
  node={node}
@@ -348,7 +357,7 @@ const ColumnsSettings = () => {
348
357
  <SettingsRow
349
358
  field={{
350
359
  name: "gy",
351
- label: "Vertical 0-5",
360
+ label: t("Vertical 0-5"),
352
361
  type: "Integer",
353
362
  }}
354
363
  node={node}
@@ -357,7 +366,7 @@ const ColumnsSettings = () => {
357
366
  <SettingsRow
358
367
  field={{
359
368
  name: "customClass",
360
- label: "Custom class",
369
+ label: t("Custom class"),
361
370
  type: "String",
362
371
  }}
363
372
  node={node}
@@ -365,7 +374,7 @@ const ColumnsSettings = () => {
365
374
  />
366
375
  </tbody>
367
376
  </table>
368
- <div accordiontitle="Box" className="w-100">
377
+ <div accordiontitle={t("Box")} className="w-100">
369
378
  <BoxModelEditor setProp={setProp} node={node} sizeWithStyle={true} />
370
379
  </div>
371
380
  </Accordion>
@@ -382,7 +391,7 @@ Columns.craft = {
382
391
  ncols: 2,
383
392
  style: {},
384
393
  breakpoints: ["sm", "sm"],
385
- setting_col_n: 1,
394
+ setting_col_n: 0,
386
395
  customClass: "",
387
396
  },
388
397
  related: {