@saltcorn/builder 0.8.7 → 0.8.8-beta.0

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",
3
+ "version": "0.8.8-beta.0",
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",
@@ -436,7 +436,7 @@ const ViewLinkElem = ({ connectors, options }) => (
436
436
  options.link_view_opts.length > 0 ? options.link_view_opts[0].name : ""
437
437
  }
438
438
  block={false}
439
- minRole={10}
439
+ minRole={100}
440
440
  label={""}
441
441
  />
442
442
  </WrapElem>
@@ -459,8 +459,9 @@ const ActionElem = ({ connectors, options }) => (
459
459
  >
460
460
  <Action
461
461
  name={options.actions[0]}
462
+ action_row_variable={""}
462
463
  block={false}
463
- minRole={10}
464
+ minRole={100}
464
465
  confirm={false}
465
466
  action_label={""}
466
467
  isFormula={{}}
@@ -3,12 +3,12 @@
3
3
  * @module components/elements/Action
4
4
  * @subcategory components / elements
5
5
  */
6
+ /*global notifyAlert*/
6
7
 
7
8
  import React, { Fragment, useContext } from "react";
8
9
  import { useNode } from "@craftjs/core";
9
10
  import optionsCtx from "../context";
10
11
  import {
11
- blockProps,
12
12
  BlockSetting,
13
13
  MinRoleSettingRow,
14
14
  OrFormula,
@@ -17,6 +17,7 @@ import {
17
17
  ButtonOrLinkSettingsRows,
18
18
  DynamicFontAwesomeIcon,
19
19
  setAPropGen,
20
+ buildOptions,
20
21
  } from "./utils";
21
22
 
22
23
  export /**
@@ -89,6 +90,8 @@ export /**
89
90
  const ActionSettings = () => {
90
91
  const node = useNode((node) => ({
91
92
  name: node.data.props.name,
93
+ action_row_variable: node.data.props.action_row_variable,
94
+ action_row_limit: node.data.props.action_row_limit,
92
95
  block: node.data.props.block,
93
96
  minRole: node.data.props.minRole,
94
97
  confirm: node.data.props.confirm,
@@ -105,6 +108,8 @@ const ActionSettings = () => {
105
108
  const {
106
109
  actions: { setProp },
107
110
  name,
111
+ action_row_variable,
112
+ action_row_limit,
108
113
  block,
109
114
  minRole,
110
115
  isFormula,
@@ -117,7 +122,6 @@ const ActionSettings = () => {
117
122
  const getCfgFields = (fv) => (options.actionConfigForms || {})[fv];
118
123
  const cfgFields = getCfgFields(name);
119
124
  const setAProp = setAPropGen(setProp);
120
-
121
125
  return (
122
126
  <div>
123
127
  <table className="w-100">
@@ -133,7 +137,24 @@ const ActionSettings = () => {
133
137
  onChange={(e) => {
134
138
  if (!e.target) return;
135
139
  const value = e.target.value;
136
- setProp((prop) => (prop.name = value));
140
+ setProp((prop) => {
141
+ prop.name = value;
142
+ if (options.mode === "filter" && value !== "Clear") {
143
+ const rowRequired =
144
+ options.actionConstraints &&
145
+ options.actionConstraints[value]?.requireRow;
146
+ if (!action_row_variable) {
147
+ prop.action_row_variable = rowRequired
148
+ ? "state"
149
+ : "none";
150
+ } else if (
151
+ rowRequired &&
152
+ action_row_variable === "none"
153
+ ) {
154
+ prop.action_row_variable = "state";
155
+ }
156
+ }
157
+ });
137
158
  setInitialConfig(setProp, value, getCfgFields(value));
138
159
  }}
139
160
  >
@@ -145,6 +166,55 @@ const ActionSettings = () => {
145
166
  </select>
146
167
  </td>
147
168
  </tr>
169
+ {name !== "Clear" && options.mode === "filter" ? (
170
+ <tr>
171
+ <td>
172
+ <label>Row variable</label>
173
+ </td>
174
+ <td>
175
+ <select
176
+ value={action_row_variable}
177
+ className="form-control form-select"
178
+ onChange={(e) => {
179
+ if (!e.target) return;
180
+ const value = e.target.value;
181
+ const rowRequired =
182
+ options.actionConstraints &&
183
+ options.actionConstraints[name]?.requireRow;
184
+ if (value === "none" && rowRequired) {
185
+ notifyAlert({
186
+ type: "warning",
187
+ text: `${name} requires a row, none is not possible`,
188
+ });
189
+ } else
190
+ setProp((prop) => (prop.action_row_variable = value));
191
+ setInitialConfig(setProp, value, getCfgFields(value));
192
+ }}
193
+ >
194
+ {buildOptions(["none", "state", "each_matching_row"], {
195
+ valAttr: true,
196
+ keyAttr: true,
197
+ })}
198
+ </select>
199
+ </td>
200
+ </tr>
201
+ ) : null}
202
+ {action_row_variable === "each_matching_row" ? (
203
+ <tr>
204
+ <td>
205
+ <label>Rows limit</label>
206
+ </td>
207
+ <td>
208
+ <input
209
+ type="number"
210
+ className="form-control"
211
+ min="0"
212
+ value={action_row_limit}
213
+ onChange={setAProp("action_row_limit")}
214
+ />
215
+ </td>
216
+ </tr>
217
+ ) : null}
148
218
  {action_style !== "on_page_load" ? (
149
219
  <tr>
150
220
  <td colSpan="2">
@@ -7,7 +7,13 @@
7
7
  import React, { useContext } from "react";
8
8
  import { useNode } from "@craftjs/core";
9
9
  import optionsCtx from "../context";
10
- import { blockProps, BlockSetting, TextStyleRow, setAPropGen } from "./utils";
10
+ import {
11
+ blockProps,
12
+ BlockSetting,
13
+ TextStyleRow,
14
+ setAPropGen,
15
+ buildOptions,
16
+ } from "./utils";
11
17
 
12
18
  export /**
13
19
  * @param {object} props
@@ -21,21 +27,21 @@ export /**
21
27
  * @subcategory components
22
28
  * @namespace
23
29
  */
24
- const Aggregation = ({ agg_relation, agg_field, stat, block, textStyle }) => {
25
- const {
26
- selected,
27
- connectors: { connect, drag },
28
- } = useNode((node) => ({ selected: node.events.selected }));
29
- return (
30
- <span
31
- className={`${textStyle} ${selected ? "selected-node" : ""}`}
32
- {...blockProps(block)}
33
- ref={(dom) => connect(drag(dom))}
34
- >
35
- [{stat} {agg_relation} {agg_field}]
36
- </span>
37
- );
38
- };
30
+ const Aggregation = ({ agg_relation, agg_field, stat, block, textStyle }) => {
31
+ const {
32
+ selected,
33
+ connectors: { connect, drag },
34
+ } = useNode((node) => ({ selected: node.events.selected }));
35
+ return (
36
+ <span
37
+ className={`${textStyle} ${selected ? "selected-node" : ""}`}
38
+ {...blockProps(block)}
39
+ ref={(dom) => connect(drag(dom))}
40
+ >
41
+ [{stat} {agg_relation} {agg_field}]
42
+ </span>
43
+ );
44
+ };
39
45
 
40
46
  export /**
41
47
  * @returns {table}
@@ -43,126 +49,134 @@ export /**
43
49
  * @subcategory components
44
50
  * @namespace
45
51
  */
46
- const AggregationSettings = () => {
47
- const {
48
- actions: { setProp },
49
- agg_relation,
50
- agg_field,
51
- stat,
52
- aggwhere,
53
- block,
54
- textStyle,
55
- } = useNode((node) => ({
56
- agg_relation: node.data.props.agg_relation,
57
- agg_field: node.data.props.agg_field,
58
- aggwhere: node.data.props.aggwhere,
59
- stat: node.data.props.stat,
60
- block: node.data.props.block,
61
- textStyle: node.data.props.textStyle,
62
- }));
63
- const options = useContext(optionsCtx);
64
- const setAProp = setAPropGen(setProp);
52
+ const AggregationSettings = () => {
53
+ const {
54
+ actions: { setProp },
55
+ agg_relation,
56
+ agg_field,
57
+ stat,
58
+ aggwhere,
59
+ block,
60
+ textStyle,
61
+ } = useNode((node) => ({
62
+ agg_relation: node.data.props.agg_relation,
63
+ agg_field: node.data.props.agg_field,
64
+ aggwhere: node.data.props.aggwhere,
65
+ stat: node.data.props.stat,
66
+ block: node.data.props.block,
67
+ textStyle: node.data.props.textStyle,
68
+ }));
69
+ const options = useContext(optionsCtx);
70
+ const setAProp = setAPropGen(setProp);
65
71
 
66
- return (
67
- <table>
68
- <tbody>
69
- <tr>
70
- <td>
71
- <label>Relation</label>
72
- </td>
73
- <td>
74
- <select
75
- className="form-control form-select"
76
- value={agg_relation}
77
- onChange={(e) => {
78
- if (!e.target) return;
79
- const value = e.target.value;
80
- setProp((prop) => {
81
- prop.agg_relation = value;
82
- const fs = options.agg_field_opts[value];
83
- if (fs && fs.length > 0) prop.agg_field = fs[0];
84
- });
85
- }}
86
- >
87
- {options.child_field_list.map((f, ix) => (
88
- <option key={ix} value={f}>
89
- {f}
90
- </option>
72
+ return (
73
+ <table>
74
+ <tbody>
75
+ <tr>
76
+ <td>
77
+ <label>Relation</label>
78
+ </td>
79
+ <td>
80
+ <select
81
+ className="form-control form-select"
82
+ value={agg_relation}
83
+ onChange={(e) => {
84
+ if (!e.target) return;
85
+ const value = e.target.value;
86
+ setProp((prop) => {
87
+ prop.agg_relation = value;
88
+ const fs = options.agg_field_opts[value];
89
+ if (fs && fs.length > 0) prop.agg_field = fs[0];
90
+ });
91
+ }}
92
+ >
93
+ {options.child_field_list.map((f, ix) => (
94
+ <option key={ix} value={f}>
95
+ {f}
96
+ </option>
97
+ ))}
98
+ </select>
99
+ </td>
100
+ </tr>
101
+ <tr>
102
+ <td>
103
+ <label>Child table field</label>
104
+ </td>
105
+ <td>
106
+ <select
107
+ className="form-control form-select"
108
+ value={agg_field}
109
+ onChange={setAProp("agg_field")}
110
+ >
111
+ {(options.agg_field_opts[agg_relation] || []).map((f, ix) => (
112
+ <option key={ix} value={f}>
113
+ {f}
114
+ </option>
115
+ ))}
116
+ </select>
117
+ </td>
118
+ </tr>
119
+ <tr>
120
+ <td>
121
+ <label>Statistic</label>
122
+ </td>
123
+ <td>
124
+ <select
125
+ value={stat}
126
+ className="form-control form-select"
127
+ onChange={setAProp("stat")}
128
+ onBlur={setAProp("stat")}
129
+ >
130
+ {buildOptions(
131
+ [
132
+ "Count",
133
+ "CountUnique",
134
+ "Avg",
135
+ "Sum",
136
+ "Max",
137
+ "Min",
138
+ "Array_Agg",
139
+ ],
140
+ { valAttr: true }
141
+ )}
142
+ {options.fields
143
+ .filter((f) => f.type.name === "Date")
144
+ .map((f) => (
145
+ <option value={`Latest ${f.name}`}>Latest {f.name}</option>
91
146
  ))}
92
- </select>
93
- </td>
94
- </tr>
95
- <tr>
96
- <td>
97
- <label>Child table field</label>
98
- </td>
99
- <td>
100
- <select
101
- className="form-control form-select"
102
- value={agg_field}
103
- onChange={setAProp("agg_field")}
104
- >
105
- {(options.agg_field_opts[agg_relation] || []).map((f, ix) => (
106
- <option key={ix} value={f}>
107
- {f}
147
+ {options.fields
148
+ .filter((f) => f.type.name === "Date")
149
+ .map((f) => (
150
+ <option value={`Earliest ${f.name}`}>
151
+ Earliest {f.name}
108
152
  </option>
109
153
  ))}
110
- </select>
111
- </td>
112
- </tr>
113
- <tr>
114
- <td>
115
- <label>Statistic</label>
116
- </td>
117
- <td>
118
- <select
119
- value={stat}
120
- className="form-control form-select"
121
- onChange={setAProp("stat")}
122
- onBlur={setAProp("stat")}
123
- >
124
- <option value={"Count"}>Count</option>
125
- <option value={"Avg"}>Avg</option>
126
- <option value={"Sum"}>Sum</option>
127
- <option value={"Max"}>Max</option>
128
- <option value={"Min"}>Min</option>
129
- <option value={"Array_Agg"}>Array_Agg</option>
130
- {options.fields
131
- .filter((f) => f.type.name === "Date")
132
- .map((f) => (
133
- <option value={`Latest ${f.name}`}>Latest {f.name}</option>
134
- ))}
135
- {options.fields
136
- .filter((f) => f.type.name === "Date")
137
- .map((f) => (
138
- <option value={`Earliest ${f.name}`}>Earliest {f.name}</option>
139
- ))}
140
- </select>
141
- </td>
142
- </tr>
143
- <tr>
144
- <td>
145
- <label>Where</label>
146
- </td>
147
- <td>
148
- <input
149
- type="text"
150
- className="form-control"
151
- value={aggwhere}
152
- onChange={setAProp("aggwhere")}
153
- />
154
- </td>
155
- </tr>
156
- <TextStyleRow textStyle={textStyle} setProp={setProp} />
157
- <tr>
158
- <td colSpan="2">
159
- <BlockSetting block={block} setProp={setProp} />
160
- </td>
161
- </tr>
162
- </tbody>
163
- </table>
164
- );
165
- };
154
+ </select>
155
+ </td>
156
+ </tr>
157
+ <tr>
158
+ <td>
159
+ <label>Where</label>
160
+ </td>
161
+ <td>
162
+ <input
163
+ type="text"
164
+ className="form-control"
165
+ value={aggwhere}
166
+ onChange={setAProp("aggwhere")}
167
+ />
168
+ </td>
169
+ </tr>
170
+ <TextStyleRow textStyle={textStyle} setProp={setProp} />
171
+ <tr>
172
+ <td colSpan="2">
173
+ <BlockSetting block={block} setProp={setProp} />
174
+ </td>
175
+ </tr>
176
+ </tbody>
177
+ </table>
178
+ );
179
+ };
166
180
 
167
181
  /**
168
182
  * @type {object}
@@ -14,6 +14,7 @@ import {
14
14
  SettingsRow,
15
15
  reactifyStyles,
16
16
  SettingsSectionHeaderRow,
17
+ buildBootstrapOptions,
17
18
  } from "./utils";
18
19
  import { BoxModelEditor } from "./BoxModelEditor";
19
20
  import {
@@ -213,10 +214,8 @@ const ColumnsSettings = () => {
213
214
  }}
214
215
  >
215
216
  <option disabled>Breakpoint</option>
216
- <option value="">None</option>
217
- <option value="sm">Small</option>
218
- <option value="md">Medium</option>
219
- <option value="lg">Large</option>
217
+ <option value="">none</option>
218
+ {buildBootstrapOptions(["sm", "md", "lg"])}
220
219
  </select>
221
220
  </td>
222
221
  </tr>
@@ -10,16 +10,15 @@ import { Element, useNode } from "@craftjs/core";
10
10
  import optionsCtx from "../context";
11
11
  import {
12
12
  Accordion,
13
- BlockSetting,
14
13
  OrFormula,
15
14
  parseStyles,
16
- SelectUnits,
17
15
  SettingsSectionHeaderRow,
18
16
  SettingsRow,
19
17
  reactifyStyles,
20
- bstyleopt,
21
18
  setAPropGen,
22
19
  FormulaTooltip,
20
+ buildOptions,
21
+ buildBootstrapOptions,
23
22
  } from "./utils";
24
23
  import {
25
24
  BorderOuter,
@@ -47,15 +46,6 @@ import { faScroll, faRobot } from "@fortawesome/free-solid-svg-icons";
47
46
  import { BoxModelEditor } from "./BoxModelEditor";
48
47
  import previewCtx from "../preview_context";
49
48
 
50
- /**
51
- *
52
- * @param {string} string
53
- * @returns {string}
54
- */
55
- function capitalizeFirstLetter(string) {
56
- return string.charAt(0).toUpperCase() + string.slice(1);
57
- }
58
-
59
49
  export /**
60
50
  * @param {object} props
61
51
  * @param {*} props.children
@@ -511,9 +501,7 @@ const ContainerSettings = () => {
511
501
  className="form-control-sm form-select"
512
502
  onChange={setAProp("imageSize")}
513
503
  >
514
- <option>contain</option>
515
- <option>cover</option>
516
- <option>repeat</option>
504
+ {buildOptions(["contain", "cover", "repeat"])}
517
505
  </select>
518
506
  </td>
519
507
  </tr>
@@ -823,10 +811,7 @@ const ContainerSettings = () => {
823
811
  onChange={setAProp("minScreenWidth")}
824
812
  >
825
813
  <option value="">all</option>
826
- <option value="sm">small</option>
827
- <option value="md">medium</option>
828
- <option value="lg">large</option>
829
- <option value="xl">x-large</option>
814
+ {buildBootstrapOptions(["sm", "md", "lg", "xl"])}
830
815
  </select>
831
816
  </td>
832
817
  </tr>
@@ -866,11 +851,10 @@ const ContainerSettings = () => {
866
851
  className="form-control form-select"
867
852
  onChange={setAProp("hoverColor")}
868
853
  >
869
- <option value="">None</option>
870
- <option value="gray">gray</option>
871
- <option value="gray-dark">gray-dark</option>
872
- <option value="light">light</option>
873
- <option value="dark">dark</option>
854
+ <option value="">none</option>
855
+ {buildOptions(["gray", "gray-dark", "light", "dark"], {
856
+ valAttr: true,
857
+ })}
874
858
  </select>
875
859
  </div>
876
860
 
@@ -17,6 +17,7 @@ import {
17
17
  Accordion,
18
18
  OrFormula,
19
19
  setAPropGen,
20
+ buildOptions,
20
21
  } from "./utils";
21
22
 
22
23
  export /**
@@ -121,6 +122,8 @@ export /**
121
122
  }
122
123
  };
123
124
  const setAProp = setAPropGen(setProp);
125
+ const sourceOpts = ["File", "URL", "Upload"];
126
+ if (options.mode === "show") sourceOpts.push("Field");
124
127
  return (
125
128
  <Accordion>
126
129
  <table accordiontitle="Select image">
@@ -142,10 +145,7 @@ export /**
142
145
  className="form-control form-select"
143
146
  onChange={setAProp("srctype")}
144
147
  >
145
- <option>File</option>
146
- <option>URL</option>
147
- <option>Upload</option>
148
- {options.mode === "show" && <option>Field</option>}
148
+ {buildOptions(sourceOpts)}
149
149
  </select>
150
150
  </td>
151
151
  </tr>