@saltcorn/builder 1.6.0-beta.1 → 1.6.0-beta.3
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.
- package/dist/builder_bundle.js +2 -2
- package/package.json +2 -2
- package/src/components/Builder.js +5 -2
- package/src/components/RenderNode.js +17 -7
- package/src/components/elements/Aggregation.js +17 -9
- package/src/components/elements/CustomLayer.js +7 -4
- package/src/components/elements/SearchBar.js +2 -1
- package/src/components/elements/Text.js +45 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/builder",
|
|
3
|
-
"version": "1.6.0-beta.
|
|
3
|
+
"version": "1.6.0-beta.3",
|
|
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",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"@fortawesome/free-solid-svg-icons": "5.15.2",
|
|
31
31
|
"@fortawesome/react-fontawesome": "0.1.14",
|
|
32
32
|
"@monaco-editor/react": "4.7.0",
|
|
33
|
-
"@saltcorn/common-code": "1.6.0-beta.
|
|
33
|
+
"@saltcorn/common-code": "1.6.0-beta.3",
|
|
34
34
|
"@tippyjs/react": "4.2.6",
|
|
35
35
|
"babel-jest": "^29.7.0",
|
|
36
36
|
"babel-loader": "9.2.1",
|
|
@@ -826,6 +826,9 @@ const Builder = ({ options, layout, mode }) => {
|
|
|
826
826
|
|
|
827
827
|
const canvasHeight =
|
|
828
828
|
Math.max(windowHeight - builderTop, builderHeight, 600) - 10;
|
|
829
|
+
|
|
830
|
+
const smallSidebarWidth = options.isRTL ? '17.5rem' : '16.5rem'
|
|
831
|
+
|
|
829
832
|
return (
|
|
830
833
|
<ErrorBoundary>
|
|
831
834
|
<Editor
|
|
@@ -885,7 +888,7 @@ const Builder = ({ options, layout, mode }) => {
|
|
|
885
888
|
layoutToNodes,
|
|
886
889
|
}}
|
|
887
890
|
>
|
|
888
|
-
<div className="row" ref={ref} style={{ marginTop: "-5px" }}>
|
|
891
|
+
<div className="row" ref={ref} style={{ marginTop: "-5px" }} dir={options.isRTL ? "rtl" : "ltr"}>
|
|
889
892
|
<div
|
|
890
893
|
className={`col-sm-auto left-builder-col ${
|
|
891
894
|
isLeftEnlarged
|
|
@@ -973,7 +976,7 @@ const Builder = ({ options, layout, mode }) => {
|
|
|
973
976
|
</div>
|
|
974
977
|
</div>
|
|
975
978
|
<div className="col-sm-auto builder-sidebar">
|
|
976
|
-
<div style={{ width: isEnlarged ? "28rem" :
|
|
979
|
+
<div style={{ width: isEnlarged ? "28rem" : smallSidebarWidth }}>
|
|
977
980
|
{document.getElementById("builder-header-actions") &&
|
|
978
981
|
createPortal(
|
|
979
982
|
<Fragment>
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
import { useNode, useEditor } from "@craftjs/core";
|
|
8
8
|
//import { ROOT_NODE } from "@craftjs/utils";
|
|
9
|
-
import React, { useEffect, useRef, useCallback, Fragment } from "react";
|
|
9
|
+
import React, { useEffect, useRef, useCallback, Fragment, useContext } from "react";
|
|
10
10
|
import ReactDOM from "react-dom";
|
|
11
11
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
12
|
+
import optionsCtx from "./context";
|
|
12
13
|
import {
|
|
13
14
|
faCopy,
|
|
14
15
|
faUndo,
|
|
@@ -32,6 +33,7 @@ export /**
|
|
|
32
33
|
*/
|
|
33
34
|
const RenderNode = ({ render }) => {
|
|
34
35
|
const { id } = useNode();
|
|
36
|
+
const options = useContext(optionsCtx);
|
|
35
37
|
const { actions, query, isActive } = useEditor((state) => ({
|
|
36
38
|
isActive: state.nodes[id].events.selected,
|
|
37
39
|
}));
|
|
@@ -60,14 +62,16 @@ const RenderNode = ({ render }) => {
|
|
|
60
62
|
const { top, left, bottom, height, width, right } = dom
|
|
61
63
|
? dom.getBoundingClientRect()
|
|
62
64
|
: { top: 0, left: 0, bottom: 0, right: 0, height: 0, width: 0 };
|
|
65
|
+
const rightPos = window.innerWidth - right;
|
|
63
66
|
return {
|
|
64
67
|
top: `${top > 0 ? top : bottom}px`,
|
|
65
68
|
left: `${left}px`,
|
|
69
|
+
right: `${rightPos}px`,
|
|
66
70
|
topn: top,
|
|
67
71
|
leftn: left,
|
|
72
|
+
rightn: rightPos,
|
|
68
73
|
height,
|
|
69
74
|
width,
|
|
70
|
-
right,
|
|
71
75
|
bottom,
|
|
72
76
|
};
|
|
73
77
|
}, []);
|
|
@@ -75,10 +79,16 @@ const RenderNode = ({ render }) => {
|
|
|
75
79
|
const scroll = useCallback(() => {
|
|
76
80
|
const { current: currentDOM } = currentRef;
|
|
77
81
|
if (!currentDOM) return;
|
|
78
|
-
const
|
|
79
|
-
currentDOM.style.top = top;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
const pos = getPos(dom);
|
|
83
|
+
currentDOM.style.top = pos.top;
|
|
84
|
+
if (options.isRTL) {
|
|
85
|
+
currentDOM.style.right = pos.right;
|
|
86
|
+
currentDOM.style.left = 'auto';
|
|
87
|
+
} else {
|
|
88
|
+
currentDOM.style.left = pos.left;
|
|
89
|
+
currentDOM.style.right = 'auto';
|
|
90
|
+
}
|
|
91
|
+
}, [dom, getPos, options.isRTL]);
|
|
82
92
|
|
|
83
93
|
const hiddenColumnParents = new Set(["Card", "Container", "Table", "DropMenu"]);
|
|
84
94
|
useEffect(() => {
|
|
@@ -145,7 +155,7 @@ const RenderNode = ({ render }) => {
|
|
|
145
155
|
isActive ? "activeind" : "hoverind"
|
|
146
156
|
} px-1 text-white`}
|
|
147
157
|
style={{
|
|
148
|
-
left: getPos(dom).left,
|
|
158
|
+
...(options.isRTL ? { right: getPos(dom).right, left: 'auto' } : { left: getPos(dom).left, right: 'auto' }),
|
|
149
159
|
top: getPos(dom).top,
|
|
150
160
|
zIndex: 1029,
|
|
151
161
|
}}
|
|
@@ -5,7 +5,13 @@
|
|
|
5
5
|
*/
|
|
6
6
|
/* globals validate_expression_elem */
|
|
7
7
|
|
|
8
|
-
import React, {
|
|
8
|
+
import React, {
|
|
9
|
+
Fragment,
|
|
10
|
+
useState,
|
|
11
|
+
useContext,
|
|
12
|
+
useEffect,
|
|
13
|
+
useRef,
|
|
14
|
+
} from "react";
|
|
9
15
|
import useTranslation from "../../hooks/useTranslation";
|
|
10
16
|
import { useNode } from "@craftjs/core";
|
|
11
17
|
import optionsCtx from "../context";
|
|
@@ -157,7 +163,9 @@ const AggregationSettings = () => {
|
|
|
157
163
|
<tr>
|
|
158
164
|
<td>
|
|
159
165
|
<label>
|
|
160
|
-
{options.mode === "filter"
|
|
166
|
+
{options.mode === "filter"
|
|
167
|
+
? t("Field")
|
|
168
|
+
: t("Child table field")}
|
|
161
169
|
</label>
|
|
162
170
|
</td>
|
|
163
171
|
<td>
|
|
@@ -187,13 +195,13 @@ const AggregationSettings = () => {
|
|
|
187
195
|
>
|
|
188
196
|
{buildOptions(
|
|
189
197
|
[
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
198
|
+
"Count",
|
|
199
|
+
"CountUnique",
|
|
200
|
+
"Avg",
|
|
201
|
+
"Sum",
|
|
202
|
+
"Max",
|
|
203
|
+
"Min",
|
|
204
|
+
"Array_Agg",
|
|
197
205
|
],
|
|
198
206
|
{ valAttr: true }
|
|
199
207
|
)}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
|
|
2
|
-
import React, { useState, useEffect } from "react";
|
|
2
|
+
import React, { useState, useEffect, useContext } from "react";
|
|
3
3
|
import { useEditor } from "@craftjs/core";
|
|
4
4
|
import { useLayer } from "@craftjs/layers";
|
|
5
5
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
6
6
|
import { faChevronDown, faChevronUp, faArrowUp, faArrowDown, faLevelUpAlt } from "@fortawesome/free-solid-svg-icons";
|
|
7
|
+
import optionsCtx from "../context";
|
|
7
8
|
|
|
8
9
|
const CustomLayer = ({ children }) => {
|
|
10
|
+
const options = useContext(optionsCtx);
|
|
11
|
+
const isRTL = options?.isRTL;
|
|
9
12
|
const [isEditing, setIsEditing] = useState(false);
|
|
10
13
|
const [editValue, setEditValue] = useState("");
|
|
11
14
|
const [isMouseOver, setIsMouseOver] = useState(false);
|
|
@@ -136,7 +139,7 @@ const CustomLayer = ({ children }) => {
|
|
|
136
139
|
return (
|
|
137
140
|
<div
|
|
138
141
|
ref={(dom) => { layer(dom); if (dom) editorConnectors.drop(dom, id); }}
|
|
139
|
-
style={isHiddenColumn ? { marginLeft: "-10px" } : undefined}
|
|
142
|
+
style={isHiddenColumn ? (isRTL ? { marginRight: "-10px" } : { marginLeft: "-10px" }) : undefined}
|
|
140
143
|
>
|
|
141
144
|
<div
|
|
142
145
|
ref={(dom) => { drag(dom); layerHeader(dom); }}
|
|
@@ -145,7 +148,7 @@ const CustomLayer = ({ children }) => {
|
|
|
145
148
|
cursor: isHiddenColumn ? "default" : "pointer",
|
|
146
149
|
display: isHiddenColumn ? "none" : "flex",
|
|
147
150
|
alignItems: "center",
|
|
148
|
-
padding: `4px 4px 4px ${depth * 10 + 6}px`,
|
|
151
|
+
padding: isRTL ? `4px ${depth * 10 + 6}px 4px 4px` : `4px 4px 4px ${depth * 10 + 6}px`,
|
|
149
152
|
overflow: "hidden",
|
|
150
153
|
}}
|
|
151
154
|
onClick={() => editorActions.selectNode(id)}
|
|
@@ -208,7 +211,7 @@ const CustomLayer = ({ children }) => {
|
|
|
208
211
|
)}
|
|
209
212
|
|
|
210
213
|
{isMouseOver && !isEditing && parentId && childIndex >= 0 && (
|
|
211
|
-
<span className="layer-move-buttons" style={{ display: "inline-flex", gap: 2,
|
|
214
|
+
<span className="layer-move-buttons" style={{ display: "inline-flex", gap: 2, marginInlineStart: 4, flexShrink: 0 }}>
|
|
212
215
|
{childIndex > 0 && (
|
|
213
216
|
<span
|
|
214
217
|
title="Move up"
|
|
@@ -25,6 +25,7 @@ export /**
|
|
|
25
25
|
*/
|
|
26
26
|
const SearchBar = ({ has_dropdown, children, contents, show_badges }) => {
|
|
27
27
|
const { t } = useTranslation();
|
|
28
|
+
const options = useContext(optionsCtx);
|
|
28
29
|
const {
|
|
29
30
|
selected,
|
|
30
31
|
connectors: { connect, drag },
|
|
@@ -80,7 +81,7 @@ const SearchBar = ({ has_dropdown, children, contents, show_badges }) => {
|
|
|
80
81
|
className={`dropdown-menu searchbar-dropdown ${
|
|
81
82
|
showDropdown ? "show" : ""
|
|
82
83
|
}`}
|
|
83
|
-
style={{ width: dropWidth, left: 0 }}
|
|
84
|
+
style={{ width: dropWidth, ...(options?.isRTL ? { right: 0 } : { left: 0 }) }}
|
|
84
85
|
>
|
|
85
86
|
<Element canvas id="searchbar-contents" is={Column}>
|
|
86
87
|
{renderContents()}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @subcategory components / elements
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import React, { useState, useContext, useEffect, Fragment } from "react";
|
|
7
|
+
import React, { useState, useContext, useEffect, useRef, Fragment } from "react";
|
|
8
8
|
import { useNode } from "@craftjs/core";
|
|
9
9
|
import {
|
|
10
10
|
blockProps,
|
|
@@ -82,7 +82,7 @@ export /**
|
|
|
82
82
|
* @subcategory components
|
|
83
83
|
*/
|
|
84
84
|
const Text = ({
|
|
85
|
-
text,
|
|
85
|
+
text: propText,
|
|
86
86
|
block,
|
|
87
87
|
inline,
|
|
88
88
|
isFormula,
|
|
@@ -97,13 +97,20 @@ const Text = ({
|
|
|
97
97
|
const {
|
|
98
98
|
connectors: { connect, drag },
|
|
99
99
|
selected,
|
|
100
|
+
nodeText,
|
|
100
101
|
actions: { setProp },
|
|
101
102
|
} = useNode((state) => ({
|
|
102
103
|
selected: state.events.selected,
|
|
103
104
|
dragged: state.events.dragged,
|
|
105
|
+
nodeText: state.data.props.text,
|
|
104
106
|
}));
|
|
107
|
+
// Use nodeText from store (reacts to undo) with fallback to prop
|
|
108
|
+
const text = nodeText !== undefined ? nodeText : propText;
|
|
105
109
|
const [editable, setEditable] = useState(false);
|
|
106
110
|
const { previewDevice } = useContext(PreviewCtx);
|
|
111
|
+
const ckInitRef = useRef(true);
|
|
112
|
+
const lastSavedTextRef = useRef(text);
|
|
113
|
+
const skipDestroyRef = useRef(false);
|
|
107
114
|
|
|
108
115
|
const baseStyle = {
|
|
109
116
|
...(font ? { fontFamily: font } : {}),
|
|
@@ -117,6 +124,19 @@ const Text = ({
|
|
|
117
124
|
);
|
|
118
125
|
if (activeFontSize) baseStyle.fontSize = activeFontSize;
|
|
119
126
|
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (editable) {
|
|
129
|
+
ckInitRef.current = true;
|
|
130
|
+
lastSavedTextRef.current = text;
|
|
131
|
+
}
|
|
132
|
+
}, [editable]);
|
|
133
|
+
// Close CKEditor when text changes externally (e.g. undo/redo)
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
if (editable && text !== lastSavedTextRef.current) {
|
|
136
|
+
skipDestroyRef.current = true;
|
|
137
|
+
setEditable(false);
|
|
138
|
+
}
|
|
139
|
+
}, [text]);
|
|
120
140
|
useEffect(() => {
|
|
121
141
|
!selected && setEditable(false);
|
|
122
142
|
}, [selected]);
|
|
@@ -149,9 +169,29 @@ const Text = ({
|
|
|
149
169
|
<CKEditor
|
|
150
170
|
initData={text || ""}
|
|
151
171
|
style={{ display: "inline" }}
|
|
152
|
-
onChange={(e) =>
|
|
153
|
-
|
|
154
|
-
|
|
172
|
+
onChange={(e) => {
|
|
173
|
+
if (ckInitRef.current) {
|
|
174
|
+
ckInitRef.current = false;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (e?.editor) {
|
|
178
|
+
const newText = e.editor.getData();
|
|
179
|
+
setProp((props) => (props.text = newText), 500);
|
|
180
|
+
lastSavedTextRef.current = newText;
|
|
181
|
+
}
|
|
182
|
+
}}
|
|
183
|
+
onBeforeDestroy={(e) => {
|
|
184
|
+
if (skipDestroyRef.current) {
|
|
185
|
+
skipDestroyRef.current = false;
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (e?.editor) {
|
|
189
|
+
const newText = e.editor.getData();
|
|
190
|
+
if (newText !== lastSavedTextRef.current) {
|
|
191
|
+
setProp((props) => (props.text = newText));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}}
|
|
155
195
|
config={ckConfig}
|
|
156
196
|
type="inline"
|
|
157
197
|
/>
|