@saltcorn/builder 1.6.0-alpha.7 → 1.6.0-alpha.8

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 +65 -0
  3. package/package.json +32 -28
  4. package/src/components/Builder.js +334 -122
  5. package/src/components/Library.js +25 -13
  6. package/src/components/RenderNode.js +6 -6
  7. package/src/components/Toolbox.js +333 -269
  8. package/src/components/elements/Action.js +145 -30
  9. package/src/components/elements/Aggregation.js +20 -23
  10. package/src/components/elements/ArrayManager.js +7 -5
  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 +27 -25
  16. package/src/components/elements/Container.js +170 -90
  17. package/src/components/elements/DropDownFilter.js +10 -8
  18. package/src/components/elements/DropMenu.js +8 -5
  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 +10 -6
  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 +18 -11
  36. package/src/components/elements/ViewLink.js +15 -11
  37. package/src/components/elements/utils.js +224 -55
  38. package/src/components/storage.js +27 -129
  39. package/src/hooks/useTranslation.js +11 -0
  40. package/src/index.js +6 -3
@@ -68,7 +68,7 @@ export const SingleLineEditor = React.forwardRef(
68
68
  }
69
69
  );
70
70
 
71
- export const MultiLineCodeEditor = ({ setProp, value, onChange }) => {
71
+ export const MultiLineCodeEditor = ({ setProp, value, onChange, isModalEditor = false }) => {
72
72
  const options = React.useContext(optionsCtx);
73
73
 
74
74
  const handleEditorWillMount = (monaco) => {
@@ -77,7 +77,7 @@ export const MultiLineCodeEditor = ({ setProp, value, onChange }) => {
77
77
  return (
78
78
  <div className="form-control p-0 pt-2">
79
79
  <Editor
80
- height="150px"
80
+ height={isModalEditor ? "100%" : "150px"}
81
81
  value={value}
82
82
  onChange={onChange}
83
83
  defaultLanguage="typescript"
@@ -86,6 +86,7 @@ export const MultiLineCodeEditor = ({ setProp, value, onChange }) => {
86
86
  options={multiLineEditorOptions}
87
87
  //theme="myCoolTheme"
88
88
  beforeMount={handleEditorWillMount}
89
+ className={isModalEditor ? 'code-modal-form' : ''}
89
90
  />
90
91
  </div>
91
92
  );
@@ -93,6 +94,7 @@ export const MultiLineCodeEditor = ({ setProp, value, onChange }) => {
93
94
 
94
95
  const multiLineEditorOptions = {
95
96
  fontSize: "14px",
97
+ minHeight:"80vh",
96
98
  fontWeight: "normal",
97
99
  wordWrap: "off",
98
100
  lineNumbers: "off",
@@ -1,10 +1,12 @@
1
- import React, { Fragment, useContext, useEffect } from "react";
1
+ import React, { Fragment, useState, useContext, useEffect } from "react";
2
+ import useTranslation from "../../hooks/useTranslation";
2
3
  import { useNode } from "@craftjs/core";
3
4
  import optionsCtx from "../context";
4
5
  import previewCtx from "../preview_context";
5
6
  import { fetchPagePreview, setAPropGen } from "./utils";
6
7
 
7
8
  export const Page = ({ page }) => {
9
+ const { t } = useTranslation();
8
10
  const {
9
11
  selected,
10
12
  node_id,
@@ -35,13 +37,14 @@ export const Page = ({ page }) => {
35
37
  dangerouslySetInnerHTML={{ __html: myPreview }}
36
38
  ></div>
37
39
  ) : (
38
- `Page: ${page}`
40
+ `${t("Page")}: ${page}`
39
41
  )}
40
42
  </div>
41
43
  );
42
44
  };
43
45
 
44
46
  export const PageSettings = () => {
47
+ const { t } = useTranslation();
45
48
  const node = useNode((node) => ({
46
49
  page: node.data.props.page,
47
50
  node_id: node.id,
@@ -58,7 +61,7 @@ export const PageSettings = () => {
58
61
  <div>
59
62
  <Fragment>
60
63
  <div>
61
- <label>Page to embed</label>
64
+ <label>{t("Page to embed")}</label>
62
65
  <select
63
66
  value={page}
64
67
  className="form-control form-select"
@@ -83,7 +86,7 @@ export const PageSettings = () => {
83
86
  target="_blank"
84
87
  href={`/pageedit/edit/${page}`}
85
88
  >
86
- Edit this page
89
+ {t("Edit this page")}
87
90
  </a>
88
91
  ) : null}
89
92
  </div>
@@ -1,4 +1,6 @@
1
- import React from "react";
1
+ import React, { Fragment, useState, useContext } from "react";
2
+ import useTranslation from "../../hooks/useTranslation";
3
+ import optionsCtx from "../context";
2
4
  import { removeWhitespaces } from "./utils";
3
5
  import {
4
6
  parseLegacyRelation,
@@ -6,14 +8,14 @@ import {
6
8
  Relation,
7
9
  } from "@saltcorn/common-code";
8
10
 
9
- const buildBadgeCfgs = (sourceTblName, type, path, caches, relString) => {
11
+ const buildBadgeCfgs = (sourceTblName, type, path, caches, relString, t) => {
10
12
  if (type === RelationType.OWN)
11
- return [{ table: `${sourceTblName} (same table)` }];
13
+ return [{ table: `${sourceTblName} ${t("(same table)")}` }];
12
14
  else if (type === RelationType.INDEPENDENT)
13
- return [{ table: "None (no relation)" }];
14
- else if (path.length === 0) return [{ table: "invalid relation" }];
15
+ return [{ table: t("None (no relation)") }];
16
+ else if (path.length === 0) return [{ table: t("invalid relation") }];
15
17
  else if (relString === Relation.fixedUserRelation)
16
- return [{ table: "logged in user" }];
18
+ return [{ table: t("logged in user") }];
17
19
  else {
18
20
  const result = [];
19
21
  let currentCfg = null;
@@ -82,6 +84,7 @@ const buildBadge = ({ up, table, down }, index) => {
82
84
  };
83
85
 
84
86
  export const RelationBadges = ({ view, relation, parentTbl, caches }) => {
87
+ const { t } = useTranslation();
85
88
  if (relation) {
86
89
  return (
87
90
  <div className="overflow-scroll">
@@ -90,16 +93,17 @@ export const RelationBadges = ({ view, relation, parentTbl, caches }) => {
90
93
  relation.type,
91
94
  relation.path,
92
95
  caches,
93
- relation.relationString
96
+ relation.relationString,
97
+ t
94
98
  ).map(buildBadge)}
95
99
  </div>
96
100
  );
97
101
  } else {
98
- if (!view) return buildBadge({ table: "invalid relation" }, 0);
102
+ if (!view) return buildBadge({ table: t("invalid relation") }, 0);
99
103
  const [prefix, rest] = view.split(":");
100
- if (!rest) return buildBadge({ table: "invalid relation" }, 0);
104
+ if (!rest) return buildBadge({ table: t("invalid relation") }, 0);
101
105
  const { type, path } = parseLegacyRelation(prefix, rest, parentTbl);
102
- if (path.length === 0) return buildBadge({ table: "invalid relation" }, 0);
106
+ if (path.length === 0) return buildBadge({ table: t("invalid relation") }, 0);
103
107
  else if (path.length === 1 && (type === "Independent" || type === "Own"))
104
108
  return (
105
109
  <div className="overflow-scroll">
@@ -114,7 +118,8 @@ export const RelationBadges = ({ view, relation, parentTbl, caches }) => {
114
118
  type,
115
119
  path,
116
120
  caches,
117
- relation.relationString
121
+ relation.relationString,
122
+ t
118
123
  ).map(buildBadge)}
119
124
  </div>
120
125
  );
@@ -1,8 +1,17 @@
1
+ /* globals $ */
2
+
1
3
  import React from "react";
4
+ import { createRoot } from "react-dom/client";
2
5
  import { removeWhitespaces, rand_ident } from "./utils";
3
- import ReactDOM from "react-dom";
6
+ import useTranslation from "../../hooks/useTranslation";
4
7
 
5
8
  const maxLevelDefault = 10;
9
+ const renderInto = (container, node) => {
10
+ if (!container) return;
11
+ const root = container.__scRoot || createRoot(container);
12
+ container.__scRoot = root;
13
+ root.render(node);
14
+ };
6
15
 
7
16
  const keyLabel = (key, type) =>
8
17
  type === "fk" ? `${key.name}` : `${key.name} (from ${key.table})`;
@@ -78,15 +87,11 @@ const Relation = ({ cfg }) => {
78
87
  maxLevel,
79
88
  setMaxLevel,
80
89
  };
81
- ReactDOM.render(
82
- <RelationLayer cfg={layerCfg} />,
83
- document.getElementById(nextDropId),
84
- () => {
85
- toggleLayers(level, maxLevel, [`#${toggleId}`]);
86
- setActiveClasses(level, maxLevel, itemId);
87
- if (level > maxLevel) setMaxLevel(level);
88
- }
89
- );
90
+ const container = document.getElementById(nextDropId);
91
+ renderInto(container, <RelationLayer cfg={layerCfg} />);
92
+ toggleLayers(level, maxLevel, [`#${toggleId}`]);
93
+ setActiveClasses(level, maxLevel, itemId);
94
+ if (level > maxLevel) setMaxLevel(level);
90
95
  }}
91
96
  >
92
97
  {keyLabel(relation, type)}
@@ -178,6 +183,7 @@ const RelationLayer = ({ cfg }) => {
178
183
  * @returns
179
184
  */
180
185
  export const RelationOnDemandPicker = ({ relations, update }) => {
186
+ const { t } = useTranslation();
181
187
  const [maxLevel, setMaxLevel] = React.useState(maxLevelDefault);
182
188
  const toggleId = "_relation_picker_toggle_";
183
189
  const layerCfg = {
@@ -189,7 +195,7 @@ export const RelationOnDemandPicker = ({ relations, update }) => {
189
195
  };
190
196
  return (
191
197
  <div>
192
- <label>Relation</label>
198
+ <label>{t("Relation")}</label>
193
199
  <div style={{ zIndex: 10000 }} className="dropstart">
194
200
  <button
195
201
  id={toggleId}
@@ -201,7 +207,7 @@ export const RelationOnDemandPicker = ({ relations, update }) => {
201
207
  setMaxLevel(maxLevelDefault);
202
208
  }}
203
209
  >
204
- Select
210
+ {t("Select")}
205
211
  </button>
206
212
  <div className="dropdown-menu">
207
213
  <RelationLayer cfg={layerCfg} />
@@ -4,8 +4,10 @@
4
4
  * @subcategory components / elements
5
5
  */
6
6
 
7
- import React, { Fragment, useState } from "react";
8
- import { Element, useNode } from "@craftjs/core";
7
+ import React, { Fragment, useState, useEffect, useContext } from "react";
8
+ import useTranslation from "../../hooks/useTranslation";
9
+ import optionsCtx from "../context";
10
+ import { useNode } from "@craftjs/core";
9
11
  import { Column } from "./Column";
10
12
  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
11
13
  import { faCaretDown } from "@fortawesome/free-solid-svg-icons";
@@ -22,6 +24,7 @@ export /**
22
24
  * @subcategory components
23
25
  */
24
26
  const SearchBar = ({ has_dropdown, children, show_badges }) => {
27
+ const { t } = useTranslation();
25
28
  const {
26
29
  selected,
27
30
  connectors: { connect, drag },
@@ -47,7 +50,7 @@ const SearchBar = ({ has_dropdown, children, show_badges }) => {
47
50
  <input
48
51
  type="text"
49
52
  className="form-control bg-light"
50
- placeholder="Search..."
53
+ placeholder={t("Search...")}
51
54
  readOnly={true}
52
55
  />
53
56
 
@@ -85,6 +88,7 @@ export /**
85
88
  * @subcategory components
86
89
  */
87
90
  const SearchBarSettings = () => {
91
+ const { t } = useTranslation();
88
92
  const {
89
93
  actions: { setProp },
90
94
  has_dropdown,
@@ -107,7 +111,7 @@ const SearchBarSettings = () => {
107
111
  checked={has_dropdown}
108
112
  onChange={setAProp("has_dropdown", { checked: true })}
109
113
  />
110
- <label className="form-check-label">Has Dropdown</label>
114
+ <label className="form-check-label">{t("Has Dropdown")}</label>
111
115
  </div>
112
116
  <div className="form-check">
113
117
  <input
@@ -117,7 +121,7 @@ const SearchBarSettings = () => {
117
121
  checked={show_badges}
118
122
  onChange={setAProp("show_badges", { checked: true })}
119
123
  />
120
- <label className="form-check-label">Show current state badges</label>
124
+ <label className="form-check-label">{t("Show current state badges")}</label>
121
125
  </div>
122
126
  <div className="form-check">
123
127
  <input
@@ -127,7 +131,7 @@ const SearchBarSettings = () => {
127
131
  checked={autofocus}
128
132
  onChange={setAProp("autofocus", { checked: true })}
129
133
  />
130
- <label className="form-check-label">Autofocus</label>
134
+ <label className="form-check-label">{t("Autofocus")}</label>
131
135
  </div>
132
136
  </div>
133
137
  );
@@ -7,6 +7,7 @@
7
7
  import React, { Fragment, useState, useContext, useEffect } from "react";
8
8
  import { ntimes } from "./Columns";
9
9
  import { Column } from "./Column";
10
+ import useTranslation from "../../hooks/useTranslation";
10
11
  import optionsCtx from "../context";
11
12
  import { setAPropGen, SettingsFromFields } from "./utils";
12
13
 
@@ -67,60 +68,76 @@ const Table = ({
67
68
  );
68
69
  };
69
70
 
70
- const fields = [
71
- {
72
- label: "Rows",
73
- name: "rows",
74
- type: "Integer",
75
- attributes: { min: 0 },
76
- },
77
- {
78
- label: "Columns",
79
- name: "columns",
80
- type: "Integer",
81
- attributes: { min: 0 },
82
- },
83
- {
84
- name: "customClass",
85
- label: "Custom class",
86
- type: "String",
87
- },
88
- {
89
- label: "Bootstrap style",
90
- name: "bs_style",
91
- type: "Bool",
92
- },
93
- {
94
- label: "Small",
95
- name: "bs_small",
96
- type: "Bool",
97
- showIf: { bs_style: true },
98
- },
99
- {
100
- label: "Striped",
101
- name: "bs_striped",
102
- type: "Bool",
103
- showIf: { bs_style: true },
104
- },
105
- {
106
- label: "Bordered",
107
- name: "bs_bordered",
108
- type: "Bool",
109
- showIf: { bs_style: true },
110
- },
111
- {
112
- label: "Borderless",
113
- name: "bs_borderless",
114
- type: "Bool",
115
- showIf: { bs_style: true },
116
- },
117
- {
118
- label: "Auto width",
119
- name: "bs_wauto",
120
- type: "Bool",
121
- showIf: { bs_style: true },
122
- },
123
- ];
71
+ const TableSettings = () => {
72
+ const { t } = useTranslation();
73
+ const fields = [
74
+ {
75
+ label: t("Rows"),
76
+ name: "rows",
77
+ type: "Integer",
78
+ attributes: { min: 0 },
79
+ },
80
+ {
81
+ label: t("Columns"),
82
+ name: "columns",
83
+ type: "Integer",
84
+ attributes: { min: 0 },
85
+ },
86
+ {
87
+ name: "customClass",
88
+ label: t("Custom class"),
89
+ type: "String",
90
+ },
91
+ {
92
+ label: t("Bootstrap style"),
93
+ name: "bs_style",
94
+ type: "Bool",
95
+ },
96
+ {
97
+ label: t("Small"),
98
+ name: "bs_small",
99
+ type: "Bool",
100
+ showIf: { bs_style: true },
101
+ },
102
+ {
103
+ label: t("Striped"),
104
+ name: "bs_striped",
105
+ type: "Bool",
106
+ showIf: { bs_style: true },
107
+ },
108
+ {
109
+ label: t("Bordered"),
110
+ name: "bs_bordered",
111
+ type: "Bool",
112
+ showIf: { bs_style: true },
113
+ },
114
+ {
115
+ label: t("Borderless"),
116
+ name: "bs_borderless",
117
+ type: "Bool",
118
+ showIf: { bs_style: true },
119
+ },
120
+ {
121
+ label: t("Auto width"),
122
+ name: "bs_wauto",
123
+ type: "Bool",
124
+ showIf: { bs_style: true },
125
+ },
126
+ ];
127
+ return (
128
+ <SettingsFromFields
129
+ fields={fields}
130
+ onChange={(fnm, v, setProp) => {
131
+ if (fnm === "rows")
132
+ setProp((prop) => {
133
+ ntimes(v, (i) => {
134
+ if (!prop.contents[i]) prop.contents[i] = [];
135
+ });
136
+ });
137
+ }}
138
+ />
139
+ );
140
+ };
124
141
 
125
142
  /**
126
143
  * @type {object}
@@ -128,16 +145,6 @@ const fields = [
128
145
  Table.craft = {
129
146
  displayName: "Table",
130
147
  related: {
131
- settings: SettingsFromFields(fields, {
132
- onChange(fnm, v, setProp) {
133
- if (fnm === "rows")
134
- setProp((prop) => {
135
- ntimes(v, (i) => {
136
- if (!prop.contents[i]) prop.contents[i] = [];
137
- });
138
- });
139
- },
140
- }),
141
- fields,
148
+ settings: TableSettings,
142
149
  },
143
150
  };
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import React, { Fragment, useState, useContext, useEffect } from "react";
8
+ import useTranslation from "../../hooks/useTranslation";
8
9
  import { ntimes } from "./Columns";
9
10
  import { Column } from "./Column";
10
11
  import optionsCtx from "../context";
@@ -34,6 +35,7 @@ const Tabs = ({
34
35
  field,
35
36
  setting_tab_n,
36
37
  }) => {
38
+ const { t } = useTranslation();
37
39
  const {
38
40
  selected,
39
41
  connectors: { connect, drag },
@@ -121,7 +123,7 @@ const Tabs = ({
121
123
  (typeof titles[ix].label === "undefined"
122
124
  ? titles[ix]
123
125
  : titles[ix].label === ""
124
- ? "(empty)"
126
+ ? t("(empty)")
125
127
  : titles[ix].label)}
126
128
  </a>
127
129
  </li>
@@ -173,6 +175,7 @@ export /**
173
175
  * @subcategory components
174
176
  */
175
177
  const TabsSettings = () => {
178
+ const { t } = useTranslation();
176
179
  const node = useNode((node) => ({
177
180
  tabsStyle: node.data.props.tabsStyle,
178
181
  ntabs: node.data.props.ntabs,
@@ -237,11 +240,11 @@ const TabsSettings = () => {
237
240
  styleOptions.push("Value switch");
238
241
 
239
242
  return (
240
- <table className="w-100" accordiontitle="Placement">
243
+ <table className="w-100" accordiontitle={t("Placement")}>
241
244
  <tbody>
242
245
  <tr>
243
246
  <th>
244
- <label>Style</label>
247
+ <label>{t("Style")}</label>
245
248
  </th>
246
249
  <td>
247
250
  <select
@@ -257,7 +260,7 @@ const TabsSettings = () => {
257
260
  <Fragment>
258
261
  <tr>
259
262
  <td>
260
- <label>Field</label>
263
+ <label>{t("Field")}</label>
261
264
  </td>
262
265
  <td>
263
266
  <select
@@ -289,7 +292,7 @@ const TabsSettings = () => {
289
292
  onChange={setAProp("independent", { checked: true })}
290
293
  />
291
294
  <label className="form-check-label">
292
- Open independently
295
+ {t("Open independently")}
293
296
  </label>
294
297
  </div>
295
298
  </td>
@@ -305,7 +308,7 @@ const TabsSettings = () => {
305
308
  onChange={setAProp("lazyLoadViews", { checked: true })}
306
309
  />
307
310
  <label className="form-check-label">
308
- Lazy load views
311
+ {t("Lazy load views")}
309
312
  </label>
310
313
  </div>
311
314
  </td>
@@ -323,7 +326,7 @@ const TabsSettings = () => {
323
326
  checked={deeplink}
324
327
  onChange={setAProp("deeplink", { checked: true })}
325
328
  />
326
- <label className="form-check-label">Deep link</label>
329
+ <label className="form-check-label">{t("Deep link")}</label>
327
330
  </div>
328
331
  </td>
329
332
  </tr>
@@ -338,7 +341,7 @@ const TabsSettings = () => {
338
341
  onChange={setAProp("serverRendered", { checked: true })}
339
342
  />
340
343
  <label className="form-check-label">
341
- Server rendering
344
+ {t("Server rendering")}
342
345
  </label>
343
346
  </div>
344
347
  </td>
@@ -346,7 +349,7 @@ const TabsSettings = () => {
346
349
  {serverRendered ? (
347
350
  <tr>
348
351
  <th>
349
- <label>Identifier</label>
352
+ <label>{t("Identifier")}</label>
350
353
  </th>
351
354
  <td>
352
355
  <input
@@ -370,7 +373,7 @@ const TabsSettings = () => {
370
373
  onChange={setAProp("lazyLoadViews", { checked: true })}
371
374
  />
372
375
  <label className="form-check-label">
373
- Lazy load views
376
+ {t("Lazy load views")}
374
377
  </label>
375
378
  </div>
376
379
  </td>
@@ -389,7 +392,7 @@ const TabsSettings = () => {
389
392
  onChange={setAProp("startClosed", { checked: true })}
390
393
  />
391
394
  <label className="form-check-label">
392
- Inititally closed
395
+ {t("Initially closed")}
393
396
  </label>
394
397
  </div>
395
398
  </td>
@@ -407,7 +410,7 @@ const TabsSettings = () => {
407
410
  onChange={setAProp("disable_inactive", { checked: true })}
408
411
  />
409
412
  <label className="form-check-label">
410
- Disable inactive inputs
413
+ {t("Disable inactive inputs")}
411
414
  </label>
412
415
  </div>
413
416
  </td>
@@ -427,7 +430,7 @@ const TabsSettings = () => {
427
430
  </td>
428
431
  </tr>
429
432
  <tr>
430
- <th colSpan="2">Title</th>
433
+ <th colSpan="2">{t("Title")}</th>
431
434
  </tr>
432
435
  <tr>
433
436
  <td colSpan={2}>
@@ -449,7 +452,7 @@ const TabsSettings = () => {
449
452
  options.mode === "filter" ? (
450
453
  <Fragment>
451
454
  <tr>
452
- <th colSpan="2">Show if formula</th>
455
+ <th colSpan="2">{t("Show if formula")}</th>
453
456
  </tr>
454
457
  <tr>
455
458
  <td colSpan={2}>
@@ -489,7 +492,7 @@ const TabsSettings = () => {
489
492
  });
490
493
  }}
491
494
  />
492
- <label className="form-check-label">Initially open</label>
495
+ <label className="form-check-label">{t("Initially open")}</label>
493
496
  </div>
494
497
  </td>
495
498
  </tr>