@saltcorn/builder 1.0.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.
@@ -23,7 +23,6 @@ import ContentEditable from "react-contenteditable";
23
23
  import optionsCtx from "../context";
24
24
  import CKEditor from "ckeditor4-react";
25
25
  import FontIconPicker from "@fonticonpicker/react-fonticonpicker";
26
- import faIcons from "./faicons";
27
26
  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
28
27
  import fas from "@fortawesome/free-solid-svg-icons";
29
28
  import far from "@fortawesome/free-regular-svg-icons";
@@ -104,9 +103,9 @@ const Text = ({
104
103
  <div
105
104
  className={`${
106
105
  isBlock(block, inline, textStyle) ? "d-block" : "d-inline-block"
107
- } ${textStyle} is-text ${isFormula.text ? "font-monospace" : ""} ${
108
- selected ? "selected-node" : ""
109
- }`}
106
+ } ${Array.isArray(textStyle) ? textStyle.join(" ") : textStyle} is-text ${
107
+ isFormula.text ? "font-monospace" : ""
108
+ } ${selected ? "selected-node" : ""}`}
110
109
  ref={(dom) => connect(drag(dom))}
111
110
  onClick={(e) => selected && setEditable(true)}
112
111
  style={{
@@ -177,7 +176,7 @@ const TextSettings = () => {
177
176
  font,
178
177
  style,
179
178
  } = node;
180
- const { mode, fields } = useContext(optionsCtx);
179
+ const { mode, fields, icons } = useContext(optionsCtx);
181
180
  const setAProp = setAPropGen(setProp);
182
181
  const allowFormula = mode === "show" || mode === "list";
183
182
 
@@ -251,7 +250,7 @@ const TextSettings = () => {
251
250
  <FontIconPicker
252
251
  className="w-100"
253
252
  value={icon}
254
- icons={faIcons}
253
+ icons={icons}
255
254
  onChange={(value) => setProp((prop) => (prop.icon = value))}
256
255
  isMulti={false}
257
256
  />
@@ -90,7 +90,7 @@ const ToggleFilterSettings = () => {
90
90
  <td>
91
91
  <select
92
92
  value={name}
93
- className="form-control form-select"
93
+ className="field form-control form-select"
94
94
  onChange={(e) => {
95
95
  if (e?.target) {
96
96
  if (!e.target) return;
@@ -331,6 +331,7 @@ const ViewLinkSettings = () => {
331
331
  linkFirst={true}
332
332
  linkIsBlank={true}
333
333
  allowRunOnLoad={false}
334
+ faIcons={options.icons}
334
335
  />
335
336
  </tbody>
336
337
  </table>
@@ -12,11 +12,15 @@ import {
12
12
  faChevronRight,
13
13
  faInfoCircle,
14
14
  faQuestionCircle,
15
+ faBold,
16
+ faItalic,
17
+ faFont,
18
+ faCommentSlash,
19
+ faUnderline,
20
+ faTerminal,
15
21
  } from "@fortawesome/free-solid-svg-icons";
16
22
  import { useNode, Element } from "@craftjs/core";
17
23
  import FontIconPicker from "@fonticonpicker/react-fonticonpicker";
18
- import faIcons from "./faicons";
19
- import { Columns, ntimes } from "./Columns";
20
24
  import Tippy from "@tippyjs/react";
21
25
  import { RelationType } from "@saltcorn/common-code";
22
26
  import Select from "react-select";
@@ -61,7 +65,8 @@ const BlockSetting = ({ block, setProp }) => (
61
65
  );
62
66
 
63
67
  export const BlockOrInlineSetting = ({ block, inline, textStyle, setProp }) =>
64
- !textStyle || !textStyle.startsWith("h") ? (
68
+ !textStyle ||
69
+ !textStyleToArray(textStyle).some((ts) => ts && ts.startsWith("h")) ? (
65
70
  <BlockSetting block={block} setProp={setProp} />
66
71
  ) : (
67
72
  <div className="form-check">
@@ -326,6 +331,89 @@ const TextStyleSelect = ({ textStyle, setProp }) => {
326
331
  </select>
327
332
  );
328
333
  };
334
+ const textStyleToArray = (textStyle) =>
335
+ Array.isArray(textStyle) ? textStyle : !textStyle ? [] : [textStyle];
336
+
337
+ const TextStyleSelectBtns = ({ textStyle, setProp }) => {
338
+ const styleArray = textStyleToArray(textStyle);
339
+ const clickH = (h) => {
340
+ const noH = styleArray.filter((s) => !(s.length == 2 && s[0] === "h"));
341
+ const selected = styleArray.includes(`h${h}`);
342
+ if (!selected) noH.push(`h${h}`);
343
+ setProp((prop) => (prop.textStyle = noH));
344
+ };
345
+ const clickStyle = (style) => {
346
+ const noH = styleArray.filter((s) => s !== style);
347
+ const selected = styleArray.includes(style);
348
+ if (!selected) noH.push(style);
349
+ setProp((prop) => (prop.textStyle = noH));
350
+ };
351
+ const StyleButton = ({ styleName, icon, title, size }) => (
352
+ <button
353
+ type="button"
354
+ title={title}
355
+ onClick={() => clickStyle(styleName)}
356
+ className={`btn btn-sm btn-${
357
+ !styleArray.includes(styleName) ? "outline-" : ""
358
+ }secondary style-${styleName}`}
359
+ >
360
+ <FontAwesomeIcon icon={icon} size={size || undefined} />
361
+ </button>
362
+ );
363
+
364
+ return (
365
+ <div>
366
+ <div className="btn-group w-100" role="group">
367
+ {[1, 2, 3, 4, 5, 6].map((h) => (
368
+ <button
369
+ key={h}
370
+ type="button"
371
+ title={`Heading ${h}`}
372
+ onClick={() => clickH(h)}
373
+ className={`btn btn-sm btn-${
374
+ !styleArray.includes(`h${h}`) ? "outline-" : ""
375
+ }secondary style-h${h}`}
376
+ >
377
+ H{h}
378
+ </button>
379
+ ))}
380
+ </div>
381
+ <div className="btn-group w-100" role="group">
382
+ <StyleButton
383
+ styleName="fw-bold"
384
+ icon={faBold}
385
+ title="Bold"
386
+ ></StyleButton>
387
+ <StyleButton
388
+ styleName="fst-italic"
389
+ icon={faItalic}
390
+ title="Italics"
391
+ ></StyleButton>
392
+ <StyleButton
393
+ styleName="small"
394
+ icon={faFont}
395
+ title="Small"
396
+ size="xs"
397
+ ></StyleButton>
398
+ <StyleButton
399
+ styleName="text-muted"
400
+ icon={faCommentSlash}
401
+ title="Muted"
402
+ ></StyleButton>
403
+ <StyleButton
404
+ styleName="text-underline"
405
+ icon={faUnderline}
406
+ title="Underline"
407
+ ></StyleButton>
408
+ <StyleButton
409
+ styleName="font-monospace"
410
+ icon={faTerminal}
411
+ title="Monospace"
412
+ ></StyleButton>
413
+ </div>
414
+ </div>
415
+ );
416
+ };
329
417
 
330
418
  export /**
331
419
  * @param {object} props
@@ -357,11 +445,8 @@ export /**
357
445
  const TextStyleRow = ({ textStyle, setProp }) => {
358
446
  return (
359
447
  <tr>
360
- <td>
361
- <label>Text Style</label>
362
- </td>
363
- <td>
364
- <TextStyleSelect textStyle={textStyle} setProp={setProp} />
448
+ <td colSpan={2}>
449
+ <TextStyleSelectBtns textStyle={textStyle} setProp={setProp} />
365
450
  </td>
366
451
  </tr>
367
452
  );
@@ -544,7 +629,7 @@ export /**
544
629
  * @namespace
545
630
  */
546
631
  const SelectUnits = ({ vert, autoable, ...props }) => {
547
- const options = ["px", "%", vert ? "vh" : "vw", "em", "rem"];
632
+ const options = ["px", "%", vert ? "vh" : "vw", "em", "rem", "cm"];
548
633
  if (autoable) options.push("auto");
549
634
  return <select {...props}>{buildOptions(options)}</select>;
550
635
  };
@@ -580,7 +665,7 @@ export const parseStyles = (styles) =>
580
665
  * @param {object} styles
581
666
  * @returns {object}
582
667
  */
583
- export const reactifyStyles = (styles) => {
668
+ export const reactifyStyles = (styles, transform, rotate) => {
584
669
  const toCamel = (s) => {
585
670
  return s.replace(/([-][a-z])/gi, ($1) => {
586
671
  return $1.toUpperCase().replace("-", "");
@@ -590,6 +675,17 @@ export const reactifyStyles = (styles) => {
590
675
  Object.keys(styles).forEach((k) => {
591
676
  reactified[toCamel(k)] = styles[k];
592
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
+
593
689
  return reactified;
594
690
  };
595
691
 
@@ -745,6 +841,8 @@ const ConfigField = ({
745
841
  props,
746
842
  setter,
747
843
  isStyle,
844
+ subProp,
845
+ valuePostfix,
748
846
  }) => {
749
847
  /**
750
848
  * @param {object} v
@@ -752,7 +850,8 @@ const ConfigField = ({
752
850
  */
753
851
  const options = React.useContext(optionsCtx);
754
852
 
755
- const myOnChange = (v) => {
853
+ const myOnChange = (v0) => {
854
+ const v = valuePostfix && (v0 || v0 === 0) ? v0 + valuePostfix : v0;
756
855
  setProp((prop) => {
757
856
  if (setter) setter(prop, field.name, v);
758
857
  else if (configuration) {
@@ -761,18 +860,25 @@ const ConfigField = ({
761
860
  } else if (isStyle) {
762
861
  if (!prop.style) prop.style = {};
763
862
  prop.style[field.name] = v;
863
+ } else if (subProp) {
864
+ if (!prop[subProp]) prop[subProp] = {};
865
+ prop[subProp][field.name] = v;
764
866
  } else prop[field.name] = v;
765
867
  });
766
868
  onChange && onChange(field.name, v, setProp);
767
869
  };
768
- const value = or_if_undef(
870
+ let value = or_if_undef(
769
871
  configuration
770
872
  ? configuration[field.name]
771
873
  : isStyle
772
874
  ? props.style[field.name]
875
+ : subProp
876
+ ? props[subProp]?.[field.name]
773
877
  : props[field.name],
774
878
  field.default
775
879
  );
880
+ if (valuePostfix)
881
+ value = `${value}`.replaceAll(valuePostfix || "__nosuchstring", "");
776
882
  if (field.input_type === "fromtype") field.input_type = null;
777
883
  if (
778
884
  field.type &&
@@ -811,7 +917,7 @@ const ConfigField = ({
811
917
  const options = getOptions();
812
918
  return (
813
919
  <select
814
- className="form-control form-select"
920
+ className={`field-${field?.name} form-control form-select`}
815
921
  value={value || ""}
816
922
  onChange={(e) => e.target && myOnChange(e.target.value)}
817
923
  onBlur={(e) => e.target && myOnChange(e.target.value)}
@@ -830,7 +936,7 @@ const ConfigField = ({
830
936
  return (
831
937
  <input
832
938
  type="text"
833
- className="form-control"
939
+ className={`field-${field?.name} form-control`}
834
940
  value={value || ""}
835
941
  onChange={(e) => e.target && myOnChange(e.target.value)}
836
942
  />
@@ -838,7 +944,7 @@ const ConfigField = ({
838
944
  },
839
945
  Font: () => (
840
946
  <select
841
- className="form-control form-select"
947
+ className="fontselect form-control form-select"
842
948
  value={value || ""}
843
949
  onChange={(e) => e.target && myOnChange(e.target.value)}
844
950
  onBlur={(e) => e.target && myOnChange(e.target.value)}
@@ -856,7 +962,7 @@ const ConfigField = ({
856
962
  Integer: () => (
857
963
  <input
858
964
  type="number"
859
- className="form-control"
965
+ className={`field-${field?.name} form-control`}
860
966
  step={field.step || 1}
861
967
  min={field.min}
862
968
  max={field.max}
@@ -867,7 +973,7 @@ const ConfigField = ({
867
973
  Float: () => (
868
974
  <input
869
975
  type="number"
870
- className="form-control"
976
+ className={`field-${field?.name} form-control`}
871
977
  value={value || ""}
872
978
  step={0.01}
873
979
  onChange={(e) => e.target && myOnChange(e.target.value)}
@@ -878,7 +984,7 @@ const ConfigField = ({
878
984
  <div className="form-check">
879
985
  <input
880
986
  type="checkbox"
881
- className="form-check-input"
987
+ className={`field-${field?.name} form-check-input`}
882
988
  checked={value}
883
989
  onChange={(e) => e.target && myOnChange(e.target.checked)}
884
990
  />
@@ -889,7 +995,7 @@ const ConfigField = ({
889
995
  <textarea
890
996
  rows="6"
891
997
  type="text"
892
- className="form-control"
998
+ className={`field-${field?.name} form-control`}
893
999
  value={value}
894
1000
  onChange={(e) => e.target && myOnChange(e.target.value)}
895
1001
  />
@@ -898,7 +1004,7 @@ const ConfigField = ({
898
1004
  <textarea
899
1005
  rows="6"
900
1006
  type="text"
901
- className="form-control"
1007
+ className={`field-${field?.name} form-control`}
902
1008
  value={value}
903
1009
  onChange={(e) => e.target && myOnChange(e.target.value)}
904
1010
  spellCheck={false}
@@ -934,7 +1040,7 @@ const ConfigField = ({
934
1040
  } else
935
1041
  return (
936
1042
  <select
937
- className="form-control form-select"
1043
+ className={`field-${field?.name} form-control form-select`}
938
1044
  value={value || ""}
939
1045
  onChange={(e) => e.target && myOnChange(e.target.value)}
940
1046
  onBlur={(e) => e.target && myOnChange(e.target.value)}
@@ -963,7 +1069,7 @@ const ConfigField = ({
963
1069
  title={o.title || o.value}
964
1070
  type="button"
965
1071
  style={{ width: `${Math.floor(100 / field.options.length)}%` }}
966
- className={`btn btn-sm btn-${
1072
+ className={`field-${field?.name} btn btn-sm btn-${
967
1073
  value !== o.value ? "outline-" : ""
968
1074
  }secondary ${field.btnClass || ""}`}
969
1075
  onClick={() => myOnChange(o.value)}
@@ -978,7 +1084,7 @@ const ConfigField = ({
978
1084
  if (isStyle && value === "auto") {
979
1085
  styleVal = "";
980
1086
  styleDim = "auto";
981
- } else if (isStyle && value && typeof value === "string") {
1087
+ } else if ((isStyle || subProp) && value && typeof value === "string") {
982
1088
  const matches = value.match(/^([-]?[0-9]+\.?[0-9]*)(.*)/);
983
1089
  if (matches) {
984
1090
  styleVal = matches[1];
@@ -990,13 +1096,13 @@ const ConfigField = ({
990
1096
  {styleDim !== "auto" && (
991
1097
  <input
992
1098
  type="number"
993
- value={(isStyle ? styleVal : value) || ""}
994
- className="w-50 form-control-sm d-inline dimunit"
1099
+ value={(isStyle || subProp ? styleVal : value) || ""}
1100
+ className={`field-${field?.name} w-50 form-control-sm d-inline dimunit`}
995
1101
  disabled={field.autoable && styleDim === "auto"}
996
1102
  onChange={(e) =>
997
1103
  e?.target &&
998
1104
  myOnChange(
999
- isStyle
1105
+ isStyle || subProp
1000
1106
  ? `${e.target.value}${styleDim || "px"}`
1001
1107
  : e.target.value
1002
1108
  )
@@ -1007,13 +1113,13 @@ const ConfigField = ({
1007
1113
  value={or_if_undef(
1008
1114
  configuration
1009
1115
  ? configuration[field.name + "Unit"]
1010
- : isStyle
1116
+ : isStyle || subProp
1011
1117
  ? styleDim
1012
1118
  : props[field.name + "Unit"],
1013
1119
  "px"
1014
1120
  )}
1015
1121
  autoable={field.autoable}
1016
- className={`w-${
1122
+ className={`field-${field?.name} w-${
1017
1123
  styleDim === "auto" ? 100 : 50
1018
1124
  } form-control-sm d-inline dimunit`}
1019
1125
  vert={!field.horiz}
@@ -1032,6 +1138,13 @@ const ConfigField = ({
1032
1138
  myStyleVal,
1033
1139
  0
1034
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}`;
1035
1148
  } else prop[field.name + "Unit"] = target_value;
1036
1149
  });
1037
1150
  }}
@@ -1128,7 +1241,15 @@ export /**
1128
1241
  * @subcategory components / elements / utils
1129
1242
  * @namespace
1130
1243
  */
1131
- 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
+ }) => {
1132
1253
  const fullWidth = ["String", "Bool", "textarea"].includes(field.type);
1133
1254
  const needLabel = field.type !== "Bool";
1134
1255
  const inner = field.canBeFormula ? (
@@ -1151,6 +1272,8 @@ const SettingsRow = ({ field, node, setProp, onChange, isStyle }) => {
1151
1272
  setProp={setProp}
1152
1273
  onChange={onChange}
1153
1274
  isStyle={isStyle}
1275
+ subProp={subProp}
1276
+ valuePostfix={valuePostfix}
1154
1277
  />
1155
1278
  );
1156
1279
  return (
@@ -1265,6 +1388,7 @@ const ButtonOrLinkSettingsRows = ({
1265
1388
  linkFirst = false,
1266
1389
  linkIsBlank = false,
1267
1390
  allowRunOnLoad = false,
1391
+ faIcons = [],
1268
1392
  }) => {
1269
1393
  const setAProp = setAPropGen(setProp);
1270
1394
  const addBtnClass = (s) => (btnClass ? `${btnClass} ${s}` : s);
@@ -1353,7 +1477,7 @@ const ButtonOrLinkSettingsRows = ({
1353
1477
  setProp((prop) => (prop[keyPrefix + "icon"] = value))
1354
1478
  }
1355
1479
  isMulti={false}
1356
- icons={faIcons}
1480
+ icons={faIcons || []}
1357
1481
  />
1358
1482
  </td>
1359
1483
  </tr>
@@ -1441,47 +1565,11 @@ export const bstyleopt = (style) => ({
1441
1565
  export const rand_ident = () =>
1442
1566
  Math.floor(Math.random() * 16777215).toString(16);
1443
1567
 
1444
- export const recursivelyCloneToElems = (query) => (nodeId, ix) => {
1445
- const { data } = query.node(nodeId).get();
1446
- const { type, props, nodes } = data;
1447
- const newProps = { ...props };
1448
- if (newProps.rndid) {
1449
- newProps.rndid = rand_ident();
1450
- }
1451
- const children = (nodes || []).map(recursivelyCloneToElems(query));
1452
- if (data.displayName === "Columns") {
1453
- const cols = ntimes(data.props.ncols, (ix) =>
1454
- recursivelyCloneToElems(query)(data.linkedNodes["Col" + ix])
1455
- );
1456
- return React.createElement(Columns, {
1457
- ...newProps,
1458
- ...(typeof ix !== "undefined" ? { key: ix } : {}),
1459
- contents: cols,
1460
- });
1461
- }
1462
- if (data.isCanvas)
1463
- return React.createElement(
1464
- Element,
1465
- {
1466
- ...newProps,
1467
- canvas: true,
1468
- is: type,
1469
- ...(typeof ix !== "undefined" ? { key: ix } : {}),
1470
- },
1471
- children
1472
- );
1473
- return React.createElement(
1474
- type,
1475
- {
1476
- ...newProps,
1477
- ...(typeof ix !== "undefined" ? { key: ix } : {}),
1478
- },
1479
- children
1480
- );
1481
- };
1482
-
1483
1568
  export const isBlock = (block, inline, textStyle) =>
1484
- !textStyle || !textStyle.startsWith("h") ? block : !inline;
1569
+ !textStyle ||
1570
+ !textStyleToArray(textStyle).some((ts) => ts && ts.startsWith("h"))
1571
+ ? block
1572
+ : !inline;
1485
1573
 
1486
1574
  export const setAPropGen =
1487
1575
  (setProp) =>
@@ -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
  };