@saltcorn/builder 1.1.0-beta.0 → 1.1.0-beta.1

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.
@@ -0,0 +1,96 @@
1
+ /*
2
+ object-assign
3
+ (c) Sindre Sorhus
4
+ @license MIT
5
+ */
6
+
7
+ /*!
8
+ Copyright (c) 2017 Jed Watson.
9
+ Licensed under the MIT License (MIT), see
10
+ http://jedwatson.github.io/classnames
11
+ */
12
+
13
+ /*!
14
+ * Font Awesome Free 5.15.2 by @fontawesome - https://fontawesome.com
15
+ * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
16
+ */
17
+
18
+ /*! *****************************************************************************
19
+ Copyright (c) Microsoft Corporation.
20
+
21
+ Permission to use, copy, modify, and/or distribute this software for any
22
+ purpose with or without fee is hereby granted.
23
+
24
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
25
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
26
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
27
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
28
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
29
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
30
+ PERFORMANCE OF THIS SOFTWARE.
31
+ ***************************************************************************** */
32
+
33
+ /*! *****************************************************************************
34
+ Copyright (c) Microsoft Corporation. All rights reserved.
35
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use
36
+ this file except in compliance with the License. You may obtain a copy of the
37
+ License at http://www.apache.org/licenses/LICENSE-2.0
38
+
39
+ THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
40
+ KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
41
+ WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
42
+ MERCHANTABLITY OR NON-INFRINGEMENT.
43
+
44
+ See the Apache Version 2.0 License for specific language governing permissions
45
+ and limitations under the License.
46
+ ***************************************************************************** */
47
+
48
+ /*!*
49
+ * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
50
+ * For licensing, see LICENSE.md.
51
+ */
52
+
53
+ /**
54
+ * @license
55
+ * Lodash <https://lodash.com/>
56
+ * Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
57
+ * Released under MIT license <https://lodash.com/license>
58
+ * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
59
+ * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
60
+ */
61
+
62
+ /** @license React v0.19.1
63
+ * scheduler.production.min.js
64
+ *
65
+ * Copyright (c) Facebook, Inc. and its affiliates.
66
+ *
67
+ * This source code is licensed under the MIT license found in the
68
+ * LICENSE file in the root directory of this source tree.
69
+ */
70
+
71
+ /** @license React v16.13.1
72
+ * react-dom.production.min.js
73
+ *
74
+ * Copyright (c) Facebook, Inc. and its affiliates.
75
+ *
76
+ * This source code is licensed under the MIT license found in the
77
+ * LICENSE file in the root directory of this source tree.
78
+ */
79
+
80
+ /** @license React v16.13.1
81
+ * react-is.production.min.js
82
+ *
83
+ * Copyright (c) Facebook, Inc. and its affiliates.
84
+ *
85
+ * This source code is licensed under the MIT license found in the
86
+ * LICENSE file in the root directory of this source tree.
87
+ */
88
+
89
+ /** @license React v16.13.1
90
+ * react.production.min.js
91
+ *
92
+ * Copyright (c) Facebook, Inc. and its affiliates.
93
+ *
94
+ * This source code is licensed under the MIT license found in the
95
+ * LICENSE file in the root directory of this source tree.
96
+ */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saltcorn/builder",
3
- "version": "1.1.0-beta.0",
3
+ "version": "1.1.0-beta.1",
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",
@@ -20,7 +20,7 @@
20
20
  "@babel/preset-react": "7.24.7",
21
21
  "@craftjs/core": "0.1.0-beta.20",
22
22
  "@craftjs/utils": "0.1.0-beta.20",
23
- "@saltcorn/common-code": "1.1.0-beta.0",
23
+ "@saltcorn/common-code": "1.1.0-beta.1",
24
24
  "saltcorn-craft-layers-noeye": "0.1.0-beta.22",
25
25
  "@fonticonpicker/react-fonticonpicker": "1.2.0",
26
26
  "@fortawesome/fontawesome-svg-core": "1.2.34",
@@ -60,15 +60,13 @@ import {
60
60
  faCaretSquareLeft,
61
61
  faCaretSquareRight,
62
62
  } from "@fortawesome/free-regular-svg-icons";
63
- import {
64
- Accordion,
65
- ErrorBoundary,
66
- recursivelyCloneToElems,
67
- } from "./elements/utils";
63
+ import { Accordion, ErrorBoundary } from "./elements/utils";
68
64
  import { InitNewElement, Library } from "./Library";
69
65
  import { RenderNode } from "./RenderNode";
70
66
  import { ListColumn } from "./elements/ListColumn";
71
67
  import { ListColumns } from "./elements/ListColumns";
68
+ import { recursivelyCloneToElems } from "./elements/Clone";
69
+
72
70
  const { Provider } = optionsCtx;
73
71
 
74
72
  /**
@@ -208,6 +206,7 @@ const SettingsPanel = () => {
208
206
  sibIx + 1
209
207
  );
210
208
  };
209
+
211
210
  return (
212
211
  <div className="settings-panel card mt-1">
213
212
  <div className="card-header px-2 py-1">
@@ -142,6 +142,11 @@ const InitNewElement = ({ nodekeys, savingState, setSavingState }) => {
142
142
  }
143
143
  });
144
144
  };
145
+ useEffect(() => {
146
+ window.addEventListener("beforeunload", () => doSave(query));
147
+ window.addEventListener("blur", () => doSave(query));
148
+ window.addEventListener("pagehide", () => doSave(query));
149
+ }, []);
145
150
  const throttledSave = useThrottle(() => {
146
151
  doSave(query);
147
152
  });
@@ -17,7 +17,7 @@ import {
17
17
  faArrowUp,
18
18
  faArrowsAlt,
19
19
  } from "@fortawesome/free-solid-svg-icons";
20
- import { recursivelyCloneToElems } from "./elements/utils";
20
+ import { recursivelyCloneToElems } from "./elements/Clone";
21
21
  /*
22
22
  Contains code copied from craft.js landing page example
23
23
  Copyright (c) 2020 Previnash Wong Sze Chuan
@@ -0,0 +1,57 @@
1
+ import React, { Fragment, useState, useEffect } from "react";
2
+ import { ListColumn } from "./ListColumn";
3
+ import { Columns, ntimes } from "./Columns";
4
+ import { rand_ident } from "./utils";
5
+ import { Element } from "@craftjs/core";
6
+
7
+ export const recursivelyCloneToElems = (query) => (nodeId, ix) => {
8
+ const { data } = query.node(nodeId).get();
9
+ const { type, props, nodes } = data;
10
+ const newProps = { ...props };
11
+ if (newProps.rndid) {
12
+ newProps.rndid = rand_ident();
13
+ }
14
+
15
+ // console.log("cloning", data.displayName, data.linkedNodes["listcol"]);
16
+
17
+ const children = (nodes || []).map(recursivelyCloneToElems(query));
18
+ if (data.displayName === "Columns") {
19
+ const cols = ntimes(data.props.ncols, (ix) =>
20
+ recursivelyCloneToElems(query)(data.linkedNodes["Col" + ix])
21
+ );
22
+ return React.createElement(Columns, {
23
+ ...newProps,
24
+ ...(typeof ix !== "undefined" ? { key: ix } : {}),
25
+ contents: cols,
26
+ });
27
+ }
28
+
29
+ if (data.displayName === "ListColumn") {
30
+ const col = recursivelyCloneToElems(query)(data.linkedNodes["listcol"]);
31
+
32
+ return React.createElement(ListColumn, {
33
+ ...newProps,
34
+ contents: col,
35
+ });
36
+ }
37
+
38
+ if (data.isCanvas)
39
+ return React.createElement(
40
+ Element,
41
+ {
42
+ ...newProps,
43
+ canvas: true,
44
+ is: type,
45
+ ...(typeof ix !== "undefined" ? { key: ix } : {}),
46
+ },
47
+ children
48
+ );
49
+ return React.createElement(
50
+ type,
51
+ {
52
+ ...newProps,
53
+ ...(typeof ix !== "undefined" ? { key: ix } : {}),
54
+ },
55
+ children
56
+ );
57
+ };
@@ -108,6 +108,7 @@ const Container = ({
108
108
  rotate,
109
109
  style,
110
110
  htmlElement,
111
+ transform,
111
112
  }) => {
112
113
  const {
113
114
  selected,
@@ -125,7 +126,7 @@ const Container = ({
125
126
  } ${selected ? "selected-node" : ""}`,
126
127
  style: {
127
128
  ...parseStyles(customCSS || ""),
128
- ...reactifyStyles(style),
129
+ ...reactifyStyles(style, transform, rotate),
129
130
  display,
130
131
  //padding: padding.map((p) => p + "px").join(" "),
131
132
  //margin: margin.map((p) => p + "px").join(" "),
@@ -166,11 +167,6 @@ const Container = ({
166
167
  width: `${width}${widthUnit || "px"}`,
167
168
  }
168
169
  : {}),
169
- ...(rotate
170
- ? {
171
- transform: `rotate(${rotate}deg)`,
172
- }
173
- : {}),
174
170
  },
175
171
  },
176
172
  children
@@ -221,6 +217,7 @@ const ContainerSettings = () => {
221
217
  rotate: node.data.props.rotate,
222
218
  display: node.data.props.display,
223
219
  style: node.data.props.style,
220
+ transform: node.data.props.transform,
224
221
  imgResponsiveWidths: node.data.props.imgResponsiveWidths,
225
222
  click_action: node.data.props.click_action,
226
223
  }));
@@ -254,6 +251,7 @@ const ContainerSettings = () => {
254
251
  imgResponsiveWidths,
255
252
  click_action,
256
253
  style,
254
+ transform,
257
255
  } = node;
258
256
  const options = useContext(optionsCtx);
259
257
  const { uploadedFiles } = useContext(previewCtx);
@@ -265,6 +263,8 @@ const ContainerSettings = () => {
265
263
  * @returns {function}
266
264
  */
267
265
  const setAProp = setAPropGen(setProp);
266
+ //console.log("transform", transform);
267
+
268
268
  return (
269
269
  <Accordion>
270
270
  <div accordiontitle="Box" className="w-100">
@@ -676,6 +676,65 @@ const ContainerSettings = () => {
676
676
  )}
677
677
  </tbody>
678
678
  </table>
679
+ <table className="w-100" accordiontitle="Transform">
680
+ <tbody>
681
+ <SettingsRow
682
+ field={{
683
+ name: "rotate",
684
+ label: "Rotate°",
685
+ type: "Integer",
686
+ }}
687
+ node={node}
688
+ setProp={setProp}
689
+ />
690
+ <SettingsRow
691
+ field={{ name: "skewX", label: "SkewX°", type: "Integer" }}
692
+ node={node}
693
+ setProp={setProp}
694
+ subProp="transform"
695
+ valuePostfix="deg"
696
+ />
697
+ <SettingsRow
698
+ field={{ name: "skewY", label: "SkewY°", type: "Integer" }}
699
+ node={node}
700
+ setProp={setProp}
701
+ subProp="transform"
702
+ valuePostfix="deg"
703
+ />
704
+ <SettingsRow
705
+ field={{ name: "scaleX", label: "ScaleX", type: "Float" }}
706
+ node={node}
707
+ setProp={setProp}
708
+ subProp="transform"
709
+ />
710
+ <SettingsRow
711
+ field={{ name: "scaleY", label: "ScaleY", type: "Float" }}
712
+ node={node}
713
+ setProp={setProp}
714
+ subProp="transform"
715
+ />
716
+ <SettingsRow
717
+ field={{
718
+ name: "translateX",
719
+ label: "TranslateX",
720
+ type: "DimUnits",
721
+ }}
722
+ node={node}
723
+ setProp={setProp}
724
+ subProp="transform"
725
+ />
726
+ <SettingsRow
727
+ field={{
728
+ name: "translateY",
729
+ label: "TranslateY",
730
+ type: "DimUnits",
731
+ }}
732
+ node={node}
733
+ setProp={setProp}
734
+ subProp="transform"
735
+ />
736
+ </tbody>
737
+ </table>
679
738
  <table className="w-100" accordiontitle="Flex properties">
680
739
  <tbody>
681
740
  <SettingsSectionHeaderRow title="Flex item" />
@@ -21,7 +21,6 @@ import {
21
21
  } from "@fortawesome/free-solid-svg-icons";
22
22
  import { useNode, Element } from "@craftjs/core";
23
23
  import FontIconPicker from "@fonticonpicker/react-fonticonpicker";
24
- import { Columns, ntimes } from "./Columns";
25
24
  import Tippy from "@tippyjs/react";
26
25
  import { RelationType } from "@saltcorn/common-code";
27
26
  import Select from "react-select";
@@ -666,7 +665,7 @@ export const parseStyles = (styles) =>
666
665
  * @param {object} styles
667
666
  * @returns {object}
668
667
  */
669
- export const reactifyStyles = (styles) => {
668
+ export const reactifyStyles = (styles, transform, rotate) => {
670
669
  const toCamel = (s) => {
671
670
  return s.replace(/([-][a-z])/gi, ($1) => {
672
671
  return $1.toUpperCase().replace("-", "");
@@ -676,6 +675,17 @@ export const reactifyStyles = (styles) => {
676
675
  Object.keys(styles).forEach((k) => {
677
676
  reactified[toCamel(k)] = styles[k];
678
677
  });
678
+ if (transform) {
679
+ reactified.transform = Object.entries(transform)
680
+ .filter(([k, v]) => v !== "")
681
+ .map(([k, v]) => `${k}(${v})`)
682
+ .join(" ");
683
+ }
684
+ if (rotate) {
685
+ if (!reactified.transform) reactified.transform = `rotate(${rotate}deg)`;
686
+ else reactified.transform = `${reactified.transform} rotate(${rotate}deg)`;
687
+ }
688
+
679
689
  return reactified;
680
690
  };
681
691
 
@@ -831,6 +841,8 @@ const ConfigField = ({
831
841
  props,
832
842
  setter,
833
843
  isStyle,
844
+ subProp,
845
+ valuePostfix,
834
846
  }) => {
835
847
  /**
836
848
  * @param {object} v
@@ -838,7 +850,8 @@ const ConfigField = ({
838
850
  */
839
851
  const options = React.useContext(optionsCtx);
840
852
 
841
- const myOnChange = (v) => {
853
+ const myOnChange = (v0) => {
854
+ const v = valuePostfix && (v0 || v0 === 0) ? v0 + valuePostfix : v0;
842
855
  setProp((prop) => {
843
856
  if (setter) setter(prop, field.name, v);
844
857
  else if (configuration) {
@@ -847,18 +860,25 @@ const ConfigField = ({
847
860
  } else if (isStyle) {
848
861
  if (!prop.style) prop.style = {};
849
862
  prop.style[field.name] = v;
863
+ } else if (subProp) {
864
+ if (!prop[subProp]) prop[subProp] = {};
865
+ prop[subProp][field.name] = v;
850
866
  } else prop[field.name] = v;
851
867
  });
852
868
  onChange && onChange(field.name, v, setProp);
853
869
  };
854
- const value = or_if_undef(
870
+ let value = or_if_undef(
855
871
  configuration
856
872
  ? configuration[field.name]
857
873
  : isStyle
858
874
  ? props.style[field.name]
875
+ : subProp
876
+ ? props[subProp]?.[field.name]
859
877
  : props[field.name],
860
878
  field.default
861
879
  );
880
+ if (valuePostfix)
881
+ value = `${value}`.replaceAll(valuePostfix || "__nosuchstring", "");
862
882
  if (field.input_type === "fromtype") field.input_type = null;
863
883
  if (
864
884
  field.type &&
@@ -1064,7 +1084,7 @@ const ConfigField = ({
1064
1084
  if (isStyle && value === "auto") {
1065
1085
  styleVal = "";
1066
1086
  styleDim = "auto";
1067
- } else if (isStyle && value && typeof value === "string") {
1087
+ } else if ((isStyle || subProp) && value && typeof value === "string") {
1068
1088
  const matches = value.match(/^([-]?[0-9]+\.?[0-9]*)(.*)/);
1069
1089
  if (matches) {
1070
1090
  styleVal = matches[1];
@@ -1076,13 +1096,13 @@ const ConfigField = ({
1076
1096
  {styleDim !== "auto" && (
1077
1097
  <input
1078
1098
  type="number"
1079
- value={(isStyle ? styleVal : value) || ""}
1099
+ value={(isStyle || subProp ? styleVal : value) || ""}
1080
1100
  className={`field-${field?.name} w-50 form-control-sm d-inline dimunit`}
1081
1101
  disabled={field.autoable && styleDim === "auto"}
1082
1102
  onChange={(e) =>
1083
1103
  e?.target &&
1084
1104
  myOnChange(
1085
- isStyle
1105
+ isStyle || subProp
1086
1106
  ? `${e.target.value}${styleDim || "px"}`
1087
1107
  : e.target.value
1088
1108
  )
@@ -1093,7 +1113,7 @@ const ConfigField = ({
1093
1113
  value={or_if_undef(
1094
1114
  configuration
1095
1115
  ? configuration[field.name + "Unit"]
1096
- : isStyle
1116
+ : isStyle || subProp
1097
1117
  ? styleDim
1098
1118
  : props[field.name + "Unit"],
1099
1119
  "px"
@@ -1118,6 +1138,13 @@ const ConfigField = ({
1118
1138
  myStyleVal,
1119
1139
  0
1120
1140
  )}${target_value}`;
1141
+ }
1142
+ if (subProp) {
1143
+ if (!prop[subProp]) prop[subProp] = {};
1144
+ prop[subProp][field.name] = `${or_if_undef(
1145
+ myStyleVal,
1146
+ 0
1147
+ )}${target_value}`;
1121
1148
  } else prop[field.name + "Unit"] = target_value;
1122
1149
  });
1123
1150
  }}
@@ -1214,7 +1241,15 @@ export /**
1214
1241
  * @subcategory components / elements / utils
1215
1242
  * @namespace
1216
1243
  */
1217
- const SettingsRow = ({ field, node, setProp, onChange, isStyle }) => {
1244
+ const SettingsRow = ({
1245
+ field,
1246
+ node,
1247
+ setProp,
1248
+ onChange,
1249
+ isStyle,
1250
+ subProp,
1251
+ valuePostfix,
1252
+ }) => {
1218
1253
  const fullWidth = ["String", "Bool", "textarea"].includes(field.type);
1219
1254
  const needLabel = field.type !== "Bool";
1220
1255
  const inner = field.canBeFormula ? (
@@ -1237,6 +1272,8 @@ const SettingsRow = ({ field, node, setProp, onChange, isStyle }) => {
1237
1272
  setProp={setProp}
1238
1273
  onChange={onChange}
1239
1274
  isStyle={isStyle}
1275
+ subProp={subProp}
1276
+ valuePostfix={valuePostfix}
1240
1277
  />
1241
1278
  );
1242
1279
  return (
@@ -1528,45 +1565,6 @@ export const bstyleopt = (style) => ({
1528
1565
  export const rand_ident = () =>
1529
1566
  Math.floor(Math.random() * 16777215).toString(16);
1530
1567
 
1531
- export const recursivelyCloneToElems = (query) => (nodeId, ix) => {
1532
- const { data } = query.node(nodeId).get();
1533
- const { type, props, nodes } = data;
1534
- const newProps = { ...props };
1535
- if (newProps.rndid) {
1536
- newProps.rndid = rand_ident();
1537
- }
1538
- const children = (nodes || []).map(recursivelyCloneToElems(query));
1539
- if (data.displayName === "Columns") {
1540
- const cols = ntimes(data.props.ncols, (ix) =>
1541
- recursivelyCloneToElems(query)(data.linkedNodes["Col" + ix])
1542
- );
1543
- return React.createElement(Columns, {
1544
- ...newProps,
1545
- ...(typeof ix !== "undefined" ? { key: ix } : {}),
1546
- contents: cols,
1547
- });
1548
- }
1549
- if (data.isCanvas)
1550
- return React.createElement(
1551
- Element,
1552
- {
1553
- ...newProps,
1554
- canvas: true,
1555
- is: type,
1556
- ...(typeof ix !== "undefined" ? { key: ix } : {}),
1557
- },
1558
- children
1559
- );
1560
- return React.createElement(
1561
- type,
1562
- {
1563
- ...newProps,
1564
- ...(typeof ix !== "undefined" ? { key: ix } : {}),
1565
- },
1566
- children
1567
- );
1568
- };
1569
-
1570
1568
  export const isBlock = (block, inline, textStyle) =>
1571
1569
  !textStyle ||
1572
1570
  !textStyleToArray(textStyle).some((ts) => ts && ts.startsWith("h"))
@@ -246,6 +246,7 @@ const layoutToNodes = (layout, query, actions, parent = "ROOT", options) => {
246
246
  imgResponsiveWidths={segment.imgResponsiveWidths}
247
247
  bgType={segment.bgType || "None"}
248
248
  style={segment.style || {}}
249
+ transform={segment.transform || {}}
249
250
  bgColor={segment.bgColor || "#ffffff"}
250
251
  setTextColor={!!segment.setTextColor}
251
252
  textColor={segment.textColor || "#000000"}
@@ -541,6 +542,7 @@ const craftToSaltcorn = (nodes, startFrom = "ROOT", options) => {
541
542
  click_action: node.props.click_action,
542
543
  rotate: node.props.rotate,
543
544
  style: node.props.style,
545
+ transform: node.props.transform,
544
546
  ...customProps,
545
547
  };
546
548
  else return get_nodes(node);
package/webpack.config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const path = require("path");
2
-
2
+ const webpack = require('webpack')
3
3
  module.exports = {
4
4
  module: {
5
5
  rules: [
@@ -18,4 +18,10 @@ module.exports = {
18
18
  //libraryTarget: 'window',
19
19
  //libraryExport: 'default'
20
20
  },
21
+ plugins: [
22
+ // fix "process is not defined" error:
23
+ new webpack.ProvidePlugin({
24
+ process: 'process/browser',
25
+ }),
26
+ ]
21
27
  };