@saltcorn/builder 0.9.4-beta.1 → 0.9.4-beta.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.
@@ -222,6 +222,7 @@ const ContainerSettings = () => {
222
222
  display: node.data.props.display,
223
223
  style: node.data.props.style,
224
224
  imgResponsiveWidths: node.data.props.imgResponsiveWidths,
225
+ click_action: node.data.props.click_action,
225
226
  }));
226
227
  const {
227
228
  actions: { setProp },
@@ -251,6 +252,7 @@ const ContainerSettings = () => {
251
252
  overflow,
252
253
  htmlElement,
253
254
  imgResponsiveWidths,
255
+ click_action,
254
256
  } = node;
255
257
  const options = useContext(optionsCtx);
256
258
  const { uploadedFiles } = useContext(previewCtx);
@@ -738,7 +740,7 @@ const ContainerSettings = () => {
738
740
  )}
739
741
  {["show", "edit", "filter"].includes(options.mode) && (
740
742
  <tr>
741
- <td>
743
+ <td colSpan={2}>
742
744
  <input
743
745
  type="text"
744
746
  className="form-control text-to-display"
@@ -844,7 +846,29 @@ const ContainerSettings = () => {
844
846
  onChange={setAProp("url")}
845
847
  />
846
848
  </OrFormula>
847
-
849
+ {options.triggerActions ? (
850
+ <Fragment>
851
+ <label>Click action</label>
852
+ <select
853
+ value={click_action}
854
+ className="form-control form-select"
855
+ onChange={(e) => {
856
+ if (!e.target) return;
857
+ const value = e.target.value;
858
+ setProp((prop) => {
859
+ prop.click_action = value;
860
+ });
861
+ }}
862
+ >
863
+ <option value="">None</option>
864
+ {options.triggerActions.map((f, ix) => (
865
+ <option key={ix} value={f}>
866
+ {f}
867
+ </option>
868
+ ))}
869
+ </select>
870
+ </Fragment>
871
+ ) : null}
848
872
  <label>Hover color</label>
849
873
  <select
850
874
  value={hoverColor}
@@ -8,7 +8,11 @@ import React, { Fragment, useState } from "react";
8
8
  import { Element, useNode } from "@craftjs/core";
9
9
  import { Column } from "./Column";
10
10
  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
11
- import { faCaretDown } from "@fortawesome/free-solid-svg-icons";
11
+ import {
12
+ faCaretDown,
13
+ faCaretSquareLeft,
14
+ faCaretSquareRight,
15
+ } from "@fortawesome/free-solid-svg-icons";
12
16
  import {
13
17
  SettingsRow,
14
18
  BlockSetting,
@@ -36,6 +40,7 @@ const DropMenu = ({
36
40
  action_textcol,
37
41
  block,
38
42
  label,
43
+ menu_direction,
39
44
  }) => {
40
45
  const {
41
46
  selected,
@@ -72,7 +77,7 @@ const DropMenu = ({
72
77
  <div
73
78
  className={`dropdown-menu dropmenu-dropdown ${
74
79
  showDropdown ? "show" : ""
75
- }`}
80
+ } ${menu_direction === "end" ? "dropdown-menu-end" : ""}`}
76
81
  >
77
82
  <div className="canvas d-flex flex-column">{children}</div>
78
83
  </div>
@@ -96,6 +101,7 @@ const DropMenuSettings = () => {
96
101
  action_bgcol: node.data.props.action_bgcol,
97
102
  action_bordercol: node.data.props.action_bordercol,
98
103
  action_textcol: node.data.props.action_textcol,
104
+ menu_direction: node.data.props.menu_direction,
99
105
  }));
100
106
  const {
101
107
  actions: { setProp },
@@ -118,12 +124,34 @@ const DropMenuSettings = () => {
118
124
  setProp={setProp}
119
125
  keyPrefix="action_"
120
126
  values={node}
127
+ allowRunOnLoad={false}
121
128
  />
122
129
  <tr>
123
130
  <td colSpan="2">
124
131
  <BlockSetting block={block} setProp={setProp} />
125
132
  </td>
126
133
  </tr>
134
+ <SettingsRow
135
+ field={{
136
+ label: "Drop direction",
137
+ name: "menu_direction",
138
+ type: "btn_select",
139
+ options: [
140
+ {
141
+ value: "end",
142
+ title: "End",
143
+ label: <FontAwesomeIcon icon={faCaretSquareLeft} />,
144
+ },
145
+ {
146
+ value: "start",
147
+ title: "Start",
148
+ label: <FontAwesomeIcon icon={faCaretSquareRight} />,
149
+ },
150
+ ],
151
+ }}
152
+ node={node}
153
+ setProp={setProp}
154
+ />
127
155
  </tbody>
128
156
  </table>
129
157
  );
@@ -151,6 +179,7 @@ DropMenu.craft = {
151
179
  "action_bgcol",
152
180
  "action_bordercol",
153
181
  "action_textcol",
182
+ "menu_direction",
154
183
  ],
155
184
  },
156
185
  };
@@ -281,6 +281,7 @@ const LinkSettings = () => {
281
281
  values={node}
282
282
  linkFirst={true}
283
283
  linkIsBlank={true}
284
+ allowRunOnLoad={false}
284
285
  />
285
286
  </tbody>
286
287
  </table>
@@ -1,33 +1,49 @@
1
1
  import React from "react";
2
2
  import { removeWhitespaces } from "./utils";
3
+ import { parseLegacyRelation, RelationType } from "@saltcorn/common-code";
3
4
 
4
- const buildBadgeCfgs = (parsed, parentTbl) => {
5
- const result = [];
6
- let currentCfg = null;
7
- for (const { type, table, key } of parsed) {
8
- if (type === "Inbound") {
9
- if (currentCfg) result.push(currentCfg);
10
- currentCfg = { up: key, table };
11
- } else {
12
- if (!currentCfg && key) result.push({ down: key, table: parentTbl });
13
- else if (currentCfg) {
14
- currentCfg.down = key;
15
- result.push(currentCfg);
5
+ const buildBadgeCfgs = (sourceTblName, type, path, caches) => {
6
+ if (type === RelationType.OWN)
7
+ return [{ table: `${sourceTblName} (same table)` }];
8
+ else if (type === RelationType.INDEPENDENT)
9
+ return [{ table: "None (no relation)" }];
10
+ else if (path.length === 0) return [{ table: "invalid relation" }];
11
+ else {
12
+ const result = [];
13
+ let currentCfg = null;
14
+ let currentTbl = sourceTblName;
15
+ for (const pathElement of path) {
16
+ if (pathElement.inboundKey) {
17
+ if (currentCfg) result.push(currentCfg);
18
+ currentTbl = pathElement.table;
19
+ currentCfg = { up: pathElement.inboundKey, table: currentTbl };
20
+ } else if (pathElement.fkey) {
21
+ if (!currentCfg)
22
+ result.push({ down: pathElement.fkey, table: currentTbl });
23
+ else {
24
+ currentCfg.down = pathElement.fkey;
25
+ result.push(currentCfg);
26
+ }
27
+ const tblObj = caches.tableNameCache[currentTbl];
28
+ const fkey = tblObj.foreign_keys.find(
29
+ (key) => key.name === pathElement.fkey
30
+ );
31
+ currentTbl = fkey.reftable_name;
32
+ currentCfg = { table: currentTbl };
16
33
  }
17
- currentCfg = { table };
18
34
  }
19
- }
20
- if (
21
- currentCfg &&
22
- !result.find(
23
- ({ down, table, up }) =>
24
- down === currentCfg.down &&
25
- table === currentCfg.table &&
26
- up === currentCfg.up
35
+ if (
36
+ currentCfg &&
37
+ !result.find(
38
+ ({ down, table, up }) =>
39
+ down === currentCfg.down &&
40
+ table === currentCfg.table &&
41
+ up === currentCfg.up
42
+ )
27
43
  )
28
- )
29
- result.push(currentCfg);
30
- return result;
44
+ result.push(currentCfg);
45
+ return result;
46
+ }
31
47
  };
32
48
 
33
49
  const buildBadge = ({ up, table, down }, index) => {
@@ -59,41 +75,34 @@ const buildBadge = ({ up, table, down }, index) => {
59
75
  );
60
76
  };
61
77
 
62
- export const RelationBadges = ({
63
- view,
64
- relation,
65
- parentTbl,
66
- tableNameCache,
67
- }) => {
78
+ export const RelationBadges = ({ view, relation, parentTbl, caches }) => {
68
79
  if (relation) {
69
- const parsed = relationHelpers.parseRelationPath(relation, tableNameCache);
70
-
71
80
  return (
72
81
  <div className="overflow-scroll">
73
- {parsed.length > 0
74
- ? buildBadgeCfgs(parsed, parentTbl).map(buildBadge)
75
- : buildBadge({ table: "invalid relation" }, 0)}
82
+ {buildBadgeCfgs(
83
+ relation.sourceTblName,
84
+ relation.type,
85
+ relation.path,
86
+ caches
87
+ ).map(buildBadge)}
76
88
  </div>
77
89
  );
78
90
  } else {
79
91
  if (!view) return buildBadge({ table: "invalid relation" }, 0);
80
92
  const [prefix, rest] = view.split(":");
81
- const parsed = relationHelpers.parseLegacyRelation(prefix, rest, parentTbl);
82
- if (parsed.length === 0)
83
- return buildBadge({ table: "invalid relation" }, 0);
84
- else if (
85
- parsed.length === 1 &&
86
- (parsed[0].type === "Independent" || parsed[0].type === "Own")
87
- )
93
+ if (!rest) return buildBadge({ table: "invalid relation" }, 0);
94
+ const { type, path } = parseLegacyRelation(prefix, rest, parentTbl);
95
+ if (path.length === 0) return buildBadge({ table: "invalid relation" }, 0);
96
+ else if (path.length === 1 && (type === "Independent" || type === "Own"))
88
97
  return (
89
98
  <div className="overflow-scroll">
90
- {buildBadge({ table: parsed[0].table }, 0)}
99
+ {buildBadge({ table: path[0].table }, 0)}
91
100
  </div>
92
101
  );
93
102
  else
94
103
  return (
95
104
  <div className="overflow-scroll">
96
- {buildBadgeCfgs(parsed, parentTbl).map(buildBadge)}
105
+ {buildBadgeCfgs(parentTbl, type, path, caches).map(buildBadge)}
97
106
  </div>
98
107
  );
99
108
  }
@@ -8,7 +8,7 @@ import React, { Fragment, useState, useContext, useEffect } from "react";
8
8
  import { ntimes } from "./Columns";
9
9
  import { Column } from "./Column";
10
10
  import optionsCtx from "../context";
11
- import { setAPropGen, buildOptions } from "./utils";
11
+ import { setAPropGen, buildOptions, ConfigField } from "./utils";
12
12
 
13
13
  import { Element, useNode } from "@craftjs/core";
14
14
 
@@ -31,14 +31,16 @@ const Tabs = ({
31
31
  independent,
32
32
  startClosed,
33
33
  field,
34
+ setting_tab_n,
34
35
  }) => {
35
36
  const {
36
37
  selected,
37
38
  connectors: { connect, drag },
39
+ actions: { setProp },
38
40
  } = useNode((node) => ({ selected: node.events.selected }));
39
- const [showTab, setShowTab] = useState(
40
- tabsStyle === "Accordion" && startClosed ? false : 0
41
- );
41
+
42
+ const showTab = setting_tab_n;
43
+ const setShowTab = (n) => setProp((prop) => (prop.setting_tab_n = n));
42
44
  const [showTabs, setShowTabs] = useState(
43
45
  tabsStyle === "Accordion" && startClosed ? [] : [true]
44
46
  );
@@ -178,9 +180,12 @@ const TabsSettings = () => {
178
180
  deeplink: node.data.props.deeplink,
179
181
  disable_inactive: node.data.props.disable_inactive,
180
182
  serverRendered: node.data.props.serverRendered,
183
+ setting_tab_n: node.data.props.setting_tab_n,
181
184
  tabId: node.data.props.tabId,
182
185
  titles: node.data.props.titles,
186
+ showif: node.data.props.showif,
183
187
  field: node.data.props.field,
188
+ acc_init_opens: node.data.props.acc_init_opens,
184
189
  }));
185
190
  const {
186
191
  actions: { setProp },
@@ -194,7 +199,11 @@ const TabsSettings = () => {
194
199
  field,
195
200
  serverRendered,
196
201
  tabId,
202
+ showif,
203
+ setting_tab_n,
204
+ acc_init_opens,
197
205
  } = node;
206
+ const use_setting_tab_n = setting_tab_n || 0;
198
207
  const options = useContext(optionsCtx);
199
208
  useEffect(() => {
200
209
  if (field)
@@ -263,42 +272,6 @@ const TabsSettings = () => {
263
272
  </Fragment>
264
273
  ) : (
265
274
  <Fragment>
266
- <tr>
267
- <th>
268
- <label>Number of sections</label>
269
- </th>
270
- <td>
271
- <input
272
- type="number"
273
- className="form-control"
274
- value={ntabs}
275
- step="1"
276
- min="0"
277
- max="20"
278
- onChange={setAProp("ntabs")}
279
- />
280
- </td>
281
- </tr>
282
- <tr>
283
- <th colSpan="2">Titles</th>
284
- </tr>
285
- {ntimes(ntabs, (ix) => (
286
- <tr key={ix}>
287
- <th>{ix + 1}</th>
288
- <td>
289
- <input
290
- type="text"
291
- className="form-control text-to-display"
292
- value={titles[ix]}
293
- onChange={(e) => {
294
- if (!e.target) return;
295
- const value = e.target.value;
296
- setProp((prop) => (prop.titles[ix] = value));
297
- }}
298
- />
299
- </td>
300
- </tr>
301
- ))}
302
275
  {tabsStyle === "Accordion" ? (
303
276
  <tr>
304
277
  <td colSpan="2">
@@ -401,6 +374,107 @@ const TabsSettings = () => {
401
374
  </td>
402
375
  </tr>
403
376
  ) : null}
377
+ <tr>
378
+ <th>
379
+ <label>Number of sections</label>
380
+ </th>
381
+ <td>
382
+ <input
383
+ type="number"
384
+ className="form-control"
385
+ value={ntabs}
386
+ step="1"
387
+ min="1"
388
+ max="20"
389
+ onChange={setAProp("ntabs")}
390
+ />
391
+ </td>
392
+ </tr>
393
+ <tr>
394
+ <td colSpan={2}>
395
+ <ConfigField
396
+ field={{
397
+ name: "setting_tab_n",
398
+ label: "Tab number",
399
+ type: "btn_select",
400
+ options: ntimes(ntabs, (i) => ({
401
+ value: i,
402
+ title: `${i + 1}`,
403
+ label: `${i + 1}`,
404
+ })),
405
+ }}
406
+ node={node}
407
+ setProp={setProp}
408
+ props={node}
409
+ ></ConfigField>
410
+ </td>
411
+ </tr>
412
+ <tr>
413
+ <th colSpan="2">Title</th>
414
+ </tr>
415
+ <tr>
416
+ <td colSpan={2}>
417
+ <input
418
+ type="text"
419
+ className="form-control text-to-display"
420
+ value={titles[use_setting_tab_n] || ""}
421
+ onChange={(e) => {
422
+ if (!e.target) return;
423
+ const value = e.target.value;
424
+ setProp((prop) => (prop.titles[use_setting_tab_n] = value));
425
+ }}
426
+ />
427
+ </td>
428
+ </tr>
429
+ {options.mode === "show" ||
430
+ options.mode === "edit" ||
431
+ options.mode === "filter" ? (
432
+ <Fragment>
433
+ <tr>
434
+ <th colSpan="2">Show if formula</th>
435
+ </tr>
436
+ <tr>
437
+ <td colSpan={2}>
438
+ <input
439
+ type="text"
440
+ className="form-control text-to-display"
441
+ value={showif?.[use_setting_tab_n] || ""}
442
+ onChange={(e) => {
443
+ if (!e.target) return;
444
+ const value = e.target.value;
445
+ setProp((prop) => {
446
+ if (!prop.showif) prop.showif = [];
447
+ prop.showif[use_setting_tab_n] = value;
448
+ });
449
+ }}
450
+ />
451
+ </td>
452
+ </tr>
453
+ </Fragment>
454
+ ) : null}
455
+ {tabsStyle === "Accordion" ? (
456
+ <tr>
457
+ <td colSpan="2">
458
+ <div className="form-check">
459
+ <input
460
+ className="form-check-input"
461
+ name="block"
462
+ type="checkbox"
463
+ checked={acc_init_opens?.[use_setting_tab_n] || false}
464
+ onChange={(e) => {
465
+ if (!e.target) return;
466
+ const value = e.target.checked;
467
+ setProp((prop) => {
468
+ if (!prop.acc_init_opens) prop.acc_init_opens = [];
469
+ prop.acc_init_opens[use_setting_tab_n] = value;
470
+ });
471
+ }}
472
+ />
473
+ <label className="form-check-label">Initially open</label>
474
+ </div>
475
+ </td>
476
+ </tr>
477
+ ) : null}
404
478
  </Fragment>
405
479
  )}
406
480
  </tbody>
@@ -414,6 +488,8 @@ const TabsSettings = () => {
414
488
  Tabs.craft = {
415
489
  props: {
416
490
  titles: ["Tab1", "Tab2"],
491
+ showif: [],
492
+ acc_init_opens: [],
417
493
  ntabs: 2,
418
494
  tabsStyle: "Tabs",
419
495
  independent: false,
@@ -421,8 +497,10 @@ Tabs.craft = {
421
497
  deeplink: true,
422
498
  disable_inactive: false,
423
499
  serverRendered: false,
500
+ setting_tab_n: 0,
424
501
  tabId: "",
425
502
  },
503
+ defaultProps: { setting_tab_n: 0, ntabs: 2 },
426
504
  displayName: "Tabs",
427
505
  related: {
428
506
  settings: TabsSettings,