@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.
@@ -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.0.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.0.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">
@@ -411,7 +410,6 @@ const Builder = ({ options, layout, mode }) => {
411
410
 
412
411
  const canvasHeight =
413
412
  Math.max(windowHeight - builderTop, builderHeight, 600) - 10;
414
-
415
413
  return (
416
414
  <ErrorBoundary>
417
415
  <Editor onRender={RenderNode}>
@@ -558,6 +556,17 @@ const Builder = ({ options, layout, mode }) => {
558
556
  </Provider>
559
557
  <div className="d-none preview-scratchpad"></div>
560
558
  </Editor>
559
+ <style>
560
+ {options.icons
561
+ .filter((icon) => icon.startsWith("unicode-"))
562
+ .map(
563
+ (icon) =>
564
+ `i.${icon}:after {content: '${String.fromCharCode(
565
+ parseInt(icon.substring(8, 12), 16)
566
+ )}'}`
567
+ )
568
+ .join("\n")}
569
+ </style>
561
570
  </ErrorBoundary>
562
571
  );
563
572
  };
@@ -16,7 +16,6 @@ import { useEditor, useNode } from "@craftjs/core";
16
16
  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
17
17
  import { faPlus, faTimes } from "@fortawesome/free-solid-svg-icons";
18
18
  import FontIconPicker from "@fonticonpicker/react-fonticonpicker";
19
- import faIcons from "./elements/faicons";
20
19
  import { craftToSaltcorn, layoutToNodes } from "./storage";
21
20
  import optionsCtx from "./context";
22
21
  import { WrapElem } from "./Toolbox";
@@ -143,6 +142,11 @@ const InitNewElement = ({ nodekeys, savingState, setSavingState }) => {
143
142
  }
144
143
  });
145
144
  };
145
+ useEffect(() => {
146
+ window.addEventListener("beforeunload", () => doSave(query));
147
+ window.addEventListener("blur", () => doSave(query));
148
+ window.addEventListener("pagehide", () => doSave(query));
149
+ }, []);
146
150
  const throttledSave = useThrottle(() => {
147
151
  doSave(query);
148
152
  });
@@ -272,7 +276,7 @@ const Library = ({ expanded }) => {
272
276
  <FontIconPicker
273
277
  className="w-100"
274
278
  value={icon}
275
- icons={faIcons}
279
+ icons={options.icons}
276
280
  onChange={setIcon}
277
281
  isMulti={false}
278
282
  />
@@ -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
@@ -284,6 +284,7 @@ const ActionSettings = () => {
284
284
  keyPrefix="action_"
285
285
  values={node}
286
286
  allowRunOnLoad={true}
287
+ faIcons={options.icons}
287
288
  />
288
289
  <MinRoleSettingRow minRole={minRole} setProp={setProp} />
289
290
  </tbody>
@@ -127,7 +127,7 @@ const AggregationSettings = () => {
127
127
  </td>
128
128
  <td>
129
129
  <select
130
- className="form-control form-select"
130
+ className="relation form-control form-select"
131
131
  value={agg_relation}
132
132
  onChange={(e) => {
133
133
  if (!e.target) return;
@@ -156,7 +156,7 @@ const AggregationSettings = () => {
156
156
  </td>
157
157
  <td>
158
158
  <select
159
- className="form-control form-select"
159
+ className="agg_field form-control form-select"
160
160
  value={agg_field}
161
161
  onChange={setAProp("agg_field")}
162
162
  >
@@ -175,7 +175,7 @@ const AggregationSettings = () => {
175
175
  <td>
176
176
  <select
177
177
  value={stat}
178
- className="form-control form-select"
178
+ className="stat form-control form-select"
179
179
  onChange={setAProp("stat")}
180
180
  onBlur={setAProp("stat")}
181
181
  >
@@ -245,7 +245,7 @@ const AggregationSettings = () => {
245
245
  <td>
246
246
  <select
247
247
  value={agg_fieldview}
248
- className="form-control form-select"
248
+ className="agg_fieldview form-control form-select"
249
249
  onChange={(e) => {
250
250
  if (!e.target) return;
251
251
  const value = e.target.value;
@@ -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
+ };
@@ -93,7 +93,7 @@ const Columns = ({
93
93
  } = useNode((node) => ({ selected: node.events.selected }));
94
94
  return (
95
95
  <div
96
- className={`row ${selected ? "selected-node" : ""} ${
96
+ className={`row builder-columns ${selected ? "selected-node" : ""} ${
97
97
  typeof gx !== "undefined" && gx !== null ? `gx-${gx}` : ""
98
98
  } ${typeof gy !== "undefined" && gy !== null ? `gy-${gy}` : ""}`}
99
99
  ref={(dom) => connect(drag(dom))}
@@ -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
  }));
@@ -253,6 +250,8 @@ const ContainerSettings = () => {
253
250
  htmlElement,
254
251
  imgResponsiveWidths,
255
252
  click_action,
253
+ style,
254
+ transform,
256
255
  } = node;
257
256
  const options = useContext(optionsCtx);
258
257
  const { uploadedFiles } = useContext(previewCtx);
@@ -264,6 +263,8 @@ const ContainerSettings = () => {
264
263
  * @returns {function}
265
264
  */
266
265
  const setAProp = setAPropGen(setProp);
266
+ //console.log("transform", transform);
267
+
267
268
  return (
268
269
  <Accordion>
269
270
  <div accordiontitle="Box" className="w-100">
@@ -331,6 +332,61 @@ const ContainerSettings = () => {
331
332
  node={node}
332
333
  setProp={setProp}
333
334
  />
335
+ <SettingsRow
336
+ field={{
337
+ name: "position",
338
+ label: "Position",
339
+ type: "select",
340
+ options: ["static", "relative", "fixed", "absolute", "sticky"],
341
+ }}
342
+ node={node}
343
+ setProp={setProp}
344
+ isStyle={true}
345
+ />
346
+ {style?.position && style?.position !== "static" ? (
347
+ <Fragment>
348
+ <SettingsRow
349
+ field={{
350
+ name: "top",
351
+ label: "Top",
352
+ type: "DimUnits",
353
+ }}
354
+ node={node}
355
+ setProp={setProp}
356
+ isStyle={true}
357
+ />
358
+ <SettingsRow
359
+ field={{
360
+ name: "right",
361
+ label: "Right",
362
+ type: "DimUnits",
363
+ }}
364
+ node={node}
365
+ setProp={setProp}
366
+ isStyle={true}
367
+ />
368
+ <SettingsRow
369
+ field={{
370
+ name: "bottom",
371
+ label: "Bottom",
372
+ type: "DimUnits",
373
+ }}
374
+ node={node}
375
+ setProp={setProp}
376
+ isStyle={true}
377
+ />
378
+ <SettingsRow
379
+ field={{
380
+ name: "left",
381
+ label: "Left",
382
+ type: "DimUnits",
383
+ }}
384
+ node={node}
385
+ setProp={setProp}
386
+ isStyle={true}
387
+ />
388
+ </Fragment>
389
+ ) : null}
334
390
  <tr>
335
391
  <td colSpan="2">
336
392
  <div className="form-check">
@@ -620,6 +676,65 @@ const ContainerSettings = () => {
620
676
  )}
621
677
  </tbody>
622
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>
623
738
  <table className="w-100" accordiontitle="Flex properties">
624
739
  <tbody>
625
740
  <SettingsSectionHeaderRow title="Flex item" />
@@ -82,7 +82,7 @@ const DropDownFilterSettings = () => {
82
82
  <td>
83
83
  <select
84
84
  value={name}
85
- className="form-control form-select"
85
+ className="field form-control form-select"
86
86
  onChange={setAProp("name")}
87
87
  >
88
88
  {options.fields.map((f, ix) => (
@@ -4,7 +4,8 @@
4
4
  * @subcategory components / elements
5
5
  */
6
6
 
7
- import React, { Fragment, useState } from "react";
7
+ import React, { Fragment, useState, useContext } from "react";
8
+ import optionsCtx from "../context";
8
9
  import { Element, useNode } from "@craftjs/core";
9
10
  import { Column } from "./Column";
10
11
  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -109,6 +110,7 @@ const DropMenuSettings = () => {
109
110
  label,
110
111
  block,
111
112
  } = node;
113
+ const options = useContext(optionsCtx);
112
114
  return (
113
115
  <table className="w-100">
114
116
  <tbody>
@@ -126,6 +128,7 @@ const DropMenuSettings = () => {
126
128
  keyPrefix="action_"
127
129
  values={node}
128
130
  allowRunOnLoad={false}
131
+ faIcons={options.icons}
129
132
  />
130
133
  <tr>
131
134
  <td colSpan="2">
@@ -160,7 +160,7 @@ const FieldSettings = () => {
160
160
  <td>
161
161
  <select
162
162
  value={name}
163
- className="form-control form-select"
163
+ className="field form-control form-select"
164
164
  onChange={(e) => {
165
165
  if (!e.target) return;
166
166
  const value = e.target.value;
@@ -200,7 +200,7 @@ const FieldSettings = () => {
200
200
  <td>
201
201
  <select
202
202
  value={fieldview}
203
- className="form-control form-select"
203
+ className="fieldview form-control form-select"
204
204
  onChange={(e) => {
205
205
  if (!e.target) return;
206
206
  const value = e.target.value;
@@ -225,7 +225,7 @@ const FieldSettings = () => {
225
225
  <td>
226
226
  <div className="form-check">
227
227
  <input
228
- className="form-check-input"
228
+ className="click-to-edit form-check-input"
229
229
  name="inline"
230
230
  type="checkbox"
231
231
  checked={click_to_edit}
@@ -275,7 +275,7 @@ const FieldSettings = () => {
275
275
  <label>On change action</label>
276
276
  <select
277
277
  value={onchange_action}
278
- className="form-control form-select"
278
+ className="on-change-action form-control form-select"
279
279
  onChange={(e) => {
280
280
  if (!e.target) return;
281
281
  const value = e.target.value;
@@ -216,7 +216,7 @@ const ImageSettings = () => {
216
216
  <td>
217
217
  <select
218
218
  value={field}
219
- className="form-control form-select"
219
+ className="field form-control form-select"
220
220
  onChange={setAProp("field")}
221
221
  >
222
222
  <option value=""></option>
@@ -477,7 +477,7 @@ const JoinFieldSettings = () => {
477
477
  <td>
478
478
  <select
479
479
  value={fieldview}
480
- className="form-control form-select"
480
+ className="fieldview form-control form-select"
481
481
  onChange={(e) => {
482
482
  if (!e.target) return;
483
483
  const value = e.target.value;
@@ -500,7 +500,7 @@ const JoinFieldSettings = () => {
500
500
  <td>
501
501
  <div className="form-check">
502
502
  <input
503
- className="form-check-input"
503
+ className="click-to-edit form-check-input"
504
504
  name="inline"
505
505
  type="checkbox"
506
506
  checked={click_to_edit}
@@ -15,12 +15,14 @@ export /**
15
15
  * @category saltcorn-builder
16
16
  * @subcategory components
17
17
  */
18
- const LineBreak = () => {
18
+ const LineBreak = ({ hr, page_break_after }) => {
19
19
  const {
20
20
  selected,
21
21
  connectors: { connect, drag },
22
22
  } = useNode((node) => ({ selected: node.events.selected }));
23
- return (
23
+ return hr || page_break_after ? (
24
+ <hr></hr>
25
+ ) : (
24
26
  <Fragment>
25
27
  <span
26
28
  className={selected ? "selected-node" : ""}
@@ -33,14 +35,19 @@ const LineBreak = () => {
33
35
  );
34
36
  };
35
37
 
38
+ const fields = [
39
+ { label: "Page break", name: "page_break_after", type: "Bool" },
40
+ { label: "Horizontal rule", name: "hr", type: "Bool" },
41
+ ];
42
+
36
43
  /**
37
44
  * @type {object}
38
45
  */
39
46
  LineBreak.craft = {
40
47
  displayName: "LineBreak",
41
48
  related: {
42
- settings: SettingsFromFields([]),
49
+ settings: SettingsFromFields(fields),
43
50
  segment_type: "line_break",
44
- fields: [],
51
+ fields,
45
52
  },
46
53
  };
@@ -283,6 +283,7 @@ const LinkSettings = () => {
283
283
  linkFirst={true}
284
284
  linkIsBlank={true}
285
285
  allowRunOnLoad={false}
286
+ faIcons={options.icons}
286
287
  />
287
288
  </tbody>
288
289
  </table>
@@ -150,7 +150,7 @@ const fields = [
150
150
  required: true,
151
151
  attributes: {
152
152
  inline: true,
153
- options: ["px", "%", "vw", "em", "rem"],
153
+ options: ["px", "%", "vw", "em", "rem", "cm"],
154
154
  },
155
155
  },
156
156
  {
@@ -258,7 +258,7 @@ const TabsSettings = () => {
258
258
  <td>
259
259
  <select
260
260
  value={field}
261
- className="form-control form-select"
261
+ className="field form-control form-select"
262
262
  onChange={setAProp("field")}
263
263
  >
264
264
  {options.fields.map((f, ix) => (