@saltcorn/builder 0.8.7-beta.0 → 0.8.7-beta.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saltcorn/builder",
3
- "version": "0.8.7-beta.0",
3
+ "version": "0.8.7-beta.2",
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",
@@ -24,6 +24,7 @@ import { HTMLCode } from "./elements/HTMLCode";
24
24
  import { Action } from "./elements/Action";
25
25
  import { Image } from "./elements/Image";
26
26
  import { Tabs } from "./elements/Tabs";
27
+ import { Table } from "./elements/Table";
27
28
  import { Empty } from "./elements/Empty";
28
29
  import { DropDownFilter } from "./elements/DropDownFilter";
29
30
  import { DropMenu } from "./elements/DropMenu";
@@ -454,6 +455,7 @@ const Builder = ({ options, layout, mode }) => {
454
455
  DropDownFilter,
455
456
  DropMenu,
456
457
  Tabs,
458
+ Table,
457
459
  ToggleFilter,
458
460
  }}
459
461
  >
@@ -21,6 +21,7 @@ import { ToggleFilter } from "./elements/ToggleFilter";
21
21
  import { Empty } from "./elements/Empty";
22
22
  import { Card } from "./elements/Card";
23
23
  import { Tabs } from "./elements/Tabs";
24
+ import { Table } from "./elements/Table";
24
25
  import { Container } from "./elements/Container";
25
26
  import { Image } from "./elements/Image";
26
27
  import { View } from "./elements/View";
@@ -130,7 +131,7 @@ const ColumnsElem = ({ connectors }) => (
130
131
  title="Split into columns"
131
132
  label="Columns"
132
133
  >
133
- <Columns contents={[]} />
134
+ <Columns contents={[]} setting_col_n={1} />
134
135
  </WrapElem>
135
136
  );
136
137
  /**
@@ -360,7 +361,7 @@ const DropMenuElem = ({ connectors }) => (
360
361
  </WrapElem>
361
362
  );
362
363
 
363
- const PageElem = ({connectors, pages}) => (
364
+ const PageElem = ({ connectors, pages }) => (
364
365
  <WrapElem
365
366
  connectors={connectors}
366
367
  icon="fa-fw far fa-file"
@@ -368,9 +369,7 @@ const PageElem = ({connectors, pages}) => (
368
369
  label="Page"
369
370
  disable={pages.length <= 1}
370
371
  >
371
- <Page
372
- page={pages.length > 0 ? pages[0].name : "page"}
373
- />
372
+ <Page page={pages.length > 0 ? pages[0].name : "page"} />
374
373
  </WrapElem>
375
374
  );
376
375
 
@@ -500,6 +499,18 @@ const AggregationElem = ({ connectors, child_field_list, agg_field_opts }) => (
500
499
  </WrapElem>
501
500
  );
502
501
 
502
+ const TableElem = ({ connectors }) => (
503
+ <WrapElem
504
+ connectors={connectors}
505
+ innerClass="mt-m1px"
506
+ icon="fas fa-table"
507
+ title="Table"
508
+ label="Table"
509
+ >
510
+ <Table contents={[[], []]} rows={2} columns={2} />
511
+ </WrapElem>
512
+ );
513
+
503
514
  export /**
504
515
  * @returns {Fragment}
505
516
  * @category saltcorn-builder
@@ -559,6 +570,9 @@ const ToolboxShow = () => {
559
570
  <HTMLElem connectors={connectors} />
560
571
  <DropMenuElem connectors={connectors} />
561
572
  </div>
573
+ <div className="toolbar-row">
574
+ <TableElem connectors={connectors} />
575
+ </div>
562
576
  </Fragment>
563
577
  );
564
578
  };
@@ -608,6 +622,9 @@ const ToolboxFilter = () => {
608
622
  <HTMLElem connectors={connectors} />
609
623
  <LinkElem connectors={connectors} />
610
624
  </div>
625
+ <div className="toolbar-row">
626
+ <TableElem connectors={connectors} />
627
+ </div>
611
628
  </Fragment>
612
629
  );
613
630
  };
@@ -656,6 +673,9 @@ const ToolboxEdit = () => {
656
673
  <JoinFieldElem connectors={connectors} options={options} />
657
674
  <DropMenuElem connectors={connectors} />
658
675
  </div>
676
+ <div className="toolbar-row">
677
+ <TableElem connectors={connectors} />
678
+ </div>
659
679
  </Fragment>
660
680
  );
661
681
  };
@@ -700,6 +720,9 @@ const ToolboxPage = () => {
700
720
  <DropMenuElem connectors={connectors} />
701
721
  <PageElem connectors={connectors} pages={pages} />
702
722
  </div>
723
+ <div className="toolbar-row">
724
+ <TableElem connectors={connectors} />
725
+ </div>
703
726
  </Fragment>
704
727
  );
705
728
  };
@@ -8,9 +8,22 @@ import React, { Fragment } from "react";
8
8
  import { Column } from "./Column";
9
9
 
10
10
  import { Element, useNode } from "@craftjs/core";
11
- import { Accordion, reactifyStyles } from "./utils";
11
+ import {
12
+ Accordion,
13
+ ConfigField,
14
+ SettingsRow,
15
+ reactifyStyles,
16
+ SettingsSectionHeaderRow,
17
+ } from "./utils";
12
18
  import { BoxModelEditor } from "./BoxModelEditor";
13
-
19
+ import {
20
+ AlignTop,
21
+ AlignMiddle,
22
+ AlignStart,
23
+ AlignEnd,
24
+ AlignCenter,
25
+ AlignBottom,
26
+ } from "react-bootstrap-icons";
14
27
  export /**
15
28
  *
16
29
  * @param {number} n
@@ -60,19 +73,35 @@ export /**
60
73
  * @category saltcorn-builder
61
74
  * @subcategory components
62
75
  */
63
- const Columns = ({ widths, contents, ncols, style }) => {
76
+ const Columns = ({
77
+ widths,
78
+ contents,
79
+ ncols,
80
+ style,
81
+ gx,
82
+ gy,
83
+ aligns,
84
+ vAligns,
85
+ }) => {
64
86
  const {
65
87
  selected,
66
88
  connectors: { connect, drag },
67
89
  } = useNode((node) => ({ selected: node.events.selected }));
68
90
  return (
69
91
  <div
70
- className={`row ${selected ? "selected-node" : ""}`}
92
+ className={`row ${selected ? "selected-node" : ""} ${
93
+ typeof gx !== "undefined" && gx !== null ? `gx-${gx}` : ""
94
+ } ${typeof gy !== "undefined" && gy !== null ? `gy-${gy}` : ""}`}
71
95
  ref={(dom) => connect(drag(dom))}
72
96
  style={reactifyStyles(style || {})}
73
97
  >
74
98
  {ntimes(ncols, (ix) => (
75
- <div key={ix} className={`split-col col-sm-${getWidth(widths, ix)}`}>
99
+ <div
100
+ key={ix}
101
+ className={`split-col col-sm-${getWidth(widths, ix)} text-${
102
+ aligns?.[ix]
103
+ } align-items-${vAligns?.[ix]}`}
104
+ >
76
105
  <Element canvas id={`Col${ix}`} is={Column}>
77
106
  {contents[ix]}
78
107
  </Element>
@@ -94,6 +123,11 @@ const ColumnsSettings = () => {
94
123
  ncols: node.data.props.ncols,
95
124
  breakpoints: node.data.props.breakpoints,
96
125
  style: node.data.props.style,
126
+ setting_col_n: node.data.props.setting_col_n,
127
+ gx: node.data.props.gx,
128
+ gy: node.data.props.gy,
129
+ vAligns: node.data.props.vAligns,
130
+ aligns: node.data.props.aligns,
97
131
  }));
98
132
  const {
99
133
  actions: { setProp },
@@ -101,7 +135,14 @@ const ColumnsSettings = () => {
101
135
  ncols,
102
136
  breakpoints,
103
137
  style,
138
+ setting_col_n,
139
+ vAligns,
140
+ aligns,
104
141
  } = node;
142
+ const colSetsNode = {
143
+ vAlign: vAligns?.[setting_col_n - 1],
144
+ hAlign: aligns?.[setting_col_n - 1],
145
+ };
105
146
  return (
106
147
  <Accordion>
107
148
  <table accordiontitle="Column properties">
@@ -183,6 +224,91 @@ const ColumnsSettings = () => {
183
224
  ))}
184
225
  </tbody>
185
226
  </table>
227
+ <div accordiontitle="Column settings">
228
+ Settings for column #
229
+ <ConfigField
230
+ field={{
231
+ name: "setting_col_n",
232
+ label: "Column number",
233
+ type: "btn_select",
234
+ options: ntimes(ncols, (i) => ({
235
+ value: i + 1,
236
+ title: `${i + 1}`,
237
+ label: `${i + 1}`,
238
+ })),
239
+ }}
240
+ node={node}
241
+ setProp={setProp}
242
+ props={node}
243
+ ></ConfigField>
244
+ <table>
245
+ <tbody>
246
+ <SettingsSectionHeaderRow title="Align" />
247
+ <SettingsRow
248
+ field={{
249
+ name: "vAlign",
250
+ label: "Vertical",
251
+ type: "btn_select",
252
+ options: [
253
+ { value: "start", title: "All", label: <AlignTop /> },
254
+ { value: "center", title: "All", label: <AlignMiddle /> },
255
+ { value: "end", title: "All", label: <AlignBottom /> },
256
+ ],
257
+ }}
258
+ node={colSetsNode}
259
+ setProp={setProp}
260
+ onChange={(k, v) =>
261
+ setProp((prop) => {
262
+ if (!prop.vAligns) prop.vAligns = [];
263
+ prop.vAligns[setting_col_n - 1] = v;
264
+ })
265
+ }
266
+ />
267
+ <SettingsRow
268
+ field={{
269
+ name: "hAlign",
270
+ label: "Horizontal",
271
+ type: "btn_select",
272
+ options: [
273
+ { value: "start", title: "Left", label: <AlignStart /> },
274
+ { value: "center", title: "Center", label: <AlignCenter /> },
275
+ { value: "end", title: "Right", label: <AlignEnd /> },
276
+ ],
277
+ }}
278
+ node={colSetsNode}
279
+ setProp={setProp}
280
+ onChange={(k, v) =>
281
+ setProp((prop) => {
282
+ if (!prop.aligns) prop.aligns = [];
283
+ prop.aligns[setting_col_n - 1] = v;
284
+ })
285
+ }
286
+ />
287
+ </tbody>
288
+ </table>
289
+ </div>
290
+ <table className="w-100" accordiontitle="Gutters">
291
+ <tbody>
292
+ <SettingsRow
293
+ field={{
294
+ name: "gx",
295
+ label: "Horizontal 0-5",
296
+ type: "Integer",
297
+ }}
298
+ node={node}
299
+ setProp={setProp}
300
+ />
301
+ <SettingsRow
302
+ field={{
303
+ name: "gy",
304
+ label: "Vertical 0-5",
305
+ type: "Integer",
306
+ }}
307
+ node={node}
308
+ setProp={setProp}
309
+ />
310
+ </tbody>
311
+ </table>
186
312
  <div accordiontitle="Box" className="w-100">
187
313
  <BoxModelEditor setProp={setProp} node={node} sizeWithStyle={true} />
188
314
  </div>
@@ -200,6 +326,7 @@ Columns.craft = {
200
326
  ncols: 2,
201
327
  style: {},
202
328
  breakpoints: ["sm", "sm"],
329
+ setting_col_n: 1,
203
330
  },
204
331
  related: {
205
332
  settings: ColumnsSettings,
@@ -389,9 +389,9 @@ const ContainerSettings = () => {
389
389
  label: "Horizontal",
390
390
  type: "btn_select",
391
391
  options: [
392
- { value: "left", title: "Left", label: <AlignStart /> },
392
+ { value: "start", title: "Left", label: <AlignStart /> },
393
393
  { value: "center", title: "Center", label: <AlignCenter /> },
394
- { value: "right", title: "Right", label: <AlignEnd /> },
394
+ { value: "end", title: "Right", label: <AlignEnd /> },
395
395
  ],
396
396
  }}
397
397
  node={node}
@@ -488,12 +488,12 @@ const ContainerSettings = () => {
488
488
  onChange={setAProp("bgFileId")}
489
489
  >
490
490
  {options.images.map((f, ix) => (
491
- <option key={ix} value={f.location}>
491
+ <option key={ix} value={f.id}>
492
492
  {f.filename}
493
493
  </option>
494
494
  ))}
495
495
  {(uploadedFiles || []).map((uf, ix) => (
496
- <option key={ix} value={uf.location}>
496
+ <option key={ix} value={uf.id}>
497
497
  {uf.filename}
498
498
  </option>
499
499
  ))}{" "}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * @category saltcorn-builder
3
+ * @module components/elements/Table
4
+ * @subcategory components / elements
5
+ */
6
+
7
+ import React, { Fragment, useState, useContext, useEffect } from "react";
8
+ import { ntimes } from "./Columns";
9
+ import { Column } from "./Column";
10
+ import optionsCtx from "../context";
11
+ import { setAPropGen, SettingsFromFields } from "./utils";
12
+
13
+ import { Element, useNode } from "@craftjs/core";
14
+
15
+ export /**
16
+ * @param {object} props
17
+ * @param {string[]} props.contents
18
+ * @param {string[]} props.titles
19
+ * @param {string} props.tabsStyle
20
+ * @param {number} props.ntabs
21
+ * @returns {div}
22
+ * @namespace
23
+ * @category saltcorn-builder
24
+ * @subcategory components
25
+ */
26
+ const Table = ({
27
+ contents,
28
+ rows,
29
+ columns,
30
+ bs_style,
31
+ bs_small,
32
+ bs_striped,
33
+ bs_bordered,
34
+ bs_borderless,
35
+ }) => {
36
+ const {
37
+ selected,
38
+ connectors: { connect, drag },
39
+ } = useNode((node) => ({ selected: node.events.selected }));
40
+ return (
41
+ <table
42
+ className={`${selected ? "selected-node" : ""} ${
43
+ bs_style ? "table" : ""
44
+ } ${bs_style && bs_small ? "table-sm" : ""} ${
45
+ bs_style && bs_striped ? "table-striped" : ""
46
+ } ${bs_style && bs_bordered ? "table-bordered" : ""} ${
47
+ bs_style && bs_borderless ? "table-borderless" : ""
48
+ }`}
49
+ ref={(dom) => connect(drag(dom))}
50
+ >
51
+ <tbody>
52
+ {ntimes(rows, (ri) => (
53
+ <tr key={ri}>
54
+ {ntimes(columns, (ci) => (
55
+ <td key={ci}>
56
+ <Element canvas id={`cell_${ri}_${ci}`} is={Column}>
57
+ {contents?.[ri]?.[ci]}
58
+ </Element>
59
+ </td>
60
+ ))}
61
+ </tr>
62
+ ))}
63
+ </tbody>
64
+ </table>
65
+ );
66
+ };
67
+
68
+ const fields = [
69
+ {
70
+ label: "Rows",
71
+ name: "rows",
72
+ type: "Integer",
73
+ attributes: { min: 0 },
74
+ },
75
+ {
76
+ label: "Columns",
77
+ name: "columns",
78
+ type: "Integer",
79
+ attributes: { min: 0 },
80
+ },
81
+ {
82
+ label: "Bootstrap style",
83
+ name: "bs_style",
84
+ type: "Bool",
85
+ },
86
+ {
87
+ label: "Small",
88
+ name: "bs_small",
89
+ type: "Bool",
90
+ showIf: { bs_style: true },
91
+ },
92
+ {
93
+ label: "Striped",
94
+ name: "bs_striped",
95
+ type: "Bool",
96
+ showIf: { bs_style: true },
97
+ },
98
+ {
99
+ label: "Bordered",
100
+ name: "bs_bordered",
101
+ type: "Bool",
102
+ showIf: { bs_style: true },
103
+ },
104
+ {
105
+ label: "Borderless",
106
+ name: "bs_borderless",
107
+ type: "Bool",
108
+ showIf: { bs_style: true },
109
+ },
110
+ ];
111
+
112
+ /**
113
+ * @type {object}
114
+ */
115
+ Table.craft = {
116
+ displayName: "Table",
117
+ related: {
118
+ settings: SettingsFromFields(fields, {
119
+ onChange(fnm, v, setProp) {
120
+ if (fnm === "rows")
121
+ setProp((prop) => {
122
+ ntimes(v, (i) => {
123
+ if (!prop.contents[i]) prop.contents[i] = [];
124
+ });
125
+ });
126
+ },
127
+ }),
128
+ fields,
129
+ },
130
+ };
@@ -208,6 +208,7 @@ const ViewSettings = () => {
208
208
  >
209
209
  <option value="shared">Shared</option>
210
210
  <option value="fixed">Fixed</option>
211
+ <option value="local">Local</option>
211
212
  </select>
212
213
  </div>
213
214
  {state === "fixed" &&
@@ -711,7 +711,7 @@ const ConfigField = ({
711
711
  prop.style[field.name] = v;
712
712
  } else prop[field.name] = v;
713
713
  });
714
- onChange && onChange(field.name, v);
714
+ onChange && onChange(field.name, v, setProp);
715
715
  };
716
716
  const value = or_if_undef(
717
717
  configuration
@@ -937,30 +937,49 @@ export /**
937
937
  * @namespace
938
938
  * @returns {table}
939
939
  */
940
- const SettingsFromFields = (fields) => () => {
941
- const node = useNode((node) => {
942
- const ps = {};
943
- fields.forEach((f) => {
944
- ps[f.name] = node.data.props[f.name];
940
+ const SettingsFromFields =
941
+ (fields, opts = {}) =>
942
+ () => {
943
+ const node = useNode((node) => {
944
+ const ps = {};
945
+ fields.forEach((f) => {
946
+ ps[f.name] = node.data.props[f.name];
947
+ });
948
+ if (fields.some((f) => f.canBeFormula))
949
+ ps.isFormula = node.data.props.isFormula;
950
+ return ps;
945
951
  });
946
- if (fields.some((f) => f.canBeFormula))
947
- ps.isFormula = node.data.props.isFormula;
948
- return ps;
949
- });
950
- const {
951
- actions: { setProp },
952
- } = node;
953
-
954
- return (
955
- <table className="w-100">
956
- <tbody>
957
- {fields.map((f, ix) => (
958
- <SettingsRow field={f} key={ix} node={node} setProp={setProp} />
959
- ))}
960
- </tbody>
961
- </table>
962
- );
963
- };
952
+ const {
953
+ actions: { setProp },
954
+ } = node;
955
+ const noop = () => {};
956
+ return (
957
+ <table className="w-100">
958
+ <tbody>
959
+ {fields.map((f, ix) => {
960
+ if (f.showIf) {
961
+ let noshow = false;
962
+ Object.entries(f.showIf).forEach(([nm, value]) => {
963
+ if (Array.isArray(value))
964
+ noshow = noshow || value.includes(node[nm]);
965
+ else noshow = noshow || value !== node[nm];
966
+ });
967
+ if (noshow) return null;
968
+ }
969
+ return (
970
+ <SettingsRow
971
+ field={f}
972
+ key={ix}
973
+ node={node}
974
+ onChange={opts.onChange || noop}
975
+ setProp={setProp}
976
+ />
977
+ );
978
+ })}
979
+ </tbody>
980
+ </table>
981
+ );
982
+ };
964
983
 
965
984
  export /**
966
985
  * @param {object} props