@saltcorn/builder 1.6.0-alpha.8 → 1.6.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.
- package/dist/builder_bundle.js +7 -7
- package/dist/builder_bundle.js.LICENSE.txt +2 -0
- package/package.json +3 -2
- package/src/components/Builder.js +367 -186
- package/src/components/RenderNode.js +21 -3
- package/src/components/Toolbox.js +100 -22
- package/src/components/elements/Action.js +11 -121
- package/src/components/elements/ArrayManager.js +10 -5
- package/src/components/elements/BoxModelEditor.js +24 -23
- package/src/components/elements/Card.js +26 -1
- package/src/components/elements/Columns.js +158 -110
- package/src/components/elements/Container.js +43 -8
- package/src/components/elements/CustomLayer.js +285 -0
- package/src/components/elements/DropDownFilter.js +8 -1
- package/src/components/elements/DropMenu.js +10 -4
- package/src/components/elements/HTMLCode.js +3 -1
- package/src/components/elements/MonacoEditor.js +120 -15
- package/src/components/elements/Prompt.js +285 -0
- package/src/components/elements/SearchBar.js +28 -5
- package/src/components/elements/Table.js +10 -12
- package/src/components/elements/Text.js +59 -15
- package/src/components/elements/View.js +19 -7
- package/src/components/elements/ViewLink.js +1 -0
- package/src/components/elements/utils.js +133 -30
- package/src/components/storage.js +33 -7
- package/src/index.js +10 -0
- package/src/utils/responsive_utils.js +139 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
|
|
2
|
+
import React, { useState, useEffect } from "react";
|
|
3
|
+
import { useEditor } from "@craftjs/core";
|
|
4
|
+
import { useLayer } from "@craftjs/layers";
|
|
5
|
+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
6
|
+
import { faChevronDown, faChevronUp, faArrowUp, faArrowDown, faLevelUpAlt } from "@fortawesome/free-solid-svg-icons";
|
|
7
|
+
|
|
8
|
+
const CustomLayer = ({ children }) => {
|
|
9
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
10
|
+
const [editValue, setEditValue] = useState("");
|
|
11
|
+
const [isMouseOver, setIsMouseOver] = useState(false);
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
id,
|
|
15
|
+
depth,
|
|
16
|
+
expanded,
|
|
17
|
+
actions: { toggleLayer, setExpandedState },
|
|
18
|
+
connectors: { layer, drag, layerHeader },
|
|
19
|
+
} = useLayer((layer) => ({
|
|
20
|
+
expanded: layer?.expanded,
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
displayName,
|
|
25
|
+
hasNodes,
|
|
26
|
+
isHiddenColumn,
|
|
27
|
+
selected,
|
|
28
|
+
parentId,
|
|
29
|
+
childIndex,
|
|
30
|
+
siblingCount,
|
|
31
|
+
canMoveOut,
|
|
32
|
+
connectors: editorConnectors,
|
|
33
|
+
actions: editorActions,
|
|
34
|
+
query,
|
|
35
|
+
} = useEditor((state) => {
|
|
36
|
+
const node = state.nodes[id];
|
|
37
|
+
const data = node?.data;
|
|
38
|
+
|
|
39
|
+
let name =
|
|
40
|
+
data?.custom?.displayName ||
|
|
41
|
+
data?.props?.custom?.displayName ||
|
|
42
|
+
data?.displayName ||
|
|
43
|
+
data?.name ||
|
|
44
|
+
id;
|
|
45
|
+
|
|
46
|
+
if (name === "ROOT" || name === "Canvas") {
|
|
47
|
+
name = data?.name || name;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Rename linked Columns for Tabs and Table
|
|
51
|
+
if (name === "Column" && data?.parent) {
|
|
52
|
+
const parentNode = state.nodes[data.parent];
|
|
53
|
+
const parentName = parentNode?.data?.displayName || parentNode?.data?.name;
|
|
54
|
+
const parentLinked = parentNode?.data?.linkedNodes;
|
|
55
|
+
if (parentLinked) {
|
|
56
|
+
const key = Object.keys(parentLinked).find(k => parentLinked[k] === id);
|
|
57
|
+
if (key) {
|
|
58
|
+
if (parentName === "Tabs") {
|
|
59
|
+
const index = parseInt(key.replace("Tab", ""), 10);
|
|
60
|
+
if (!isNaN(index)) {
|
|
61
|
+
name = `Tab ${index + 1}`;
|
|
62
|
+
}
|
|
63
|
+
} else if (parentName === "Table") {
|
|
64
|
+
const match = key.match(/^cell_(\d+)_(\d+)$/);
|
|
65
|
+
if (match) {
|
|
66
|
+
name = `R${parseInt(match[1], 10) + 1}C${parseInt(match[2], 10) + 1}`;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const nodes = data?.nodes;
|
|
74
|
+
const linkedNodes = data?.linkedNodes;
|
|
75
|
+
const hasChildren =
|
|
76
|
+
(nodes && nodes.length > 0) ||
|
|
77
|
+
(linkedNodes && Object.keys(linkedNodes).length > 0);
|
|
78
|
+
|
|
79
|
+
// Hide the ROOT Column and linked Columns of Card/Container
|
|
80
|
+
let shouldHide = false;
|
|
81
|
+
if (id === "ROOT") {
|
|
82
|
+
shouldHide = true;
|
|
83
|
+
} else if ((data?.displayName === "Column" || data?.name === "Column") && data?.parent) {
|
|
84
|
+
const parentNode = state.nodes[data.parent];
|
|
85
|
+
const parentName = parentNode?.data?.displayName || parentNode?.data?.name;
|
|
86
|
+
if (parentName === "Card" || parentName === "Container") {
|
|
87
|
+
const parentLinked = parentNode?.data?.linkedNodes;
|
|
88
|
+
if (parentLinked && Object.values(parentLinked).includes(id)) {
|
|
89
|
+
shouldHide = true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const isSelected =
|
|
95
|
+
state.events?.selected?.has?.(id) || state.events?.selected === id;
|
|
96
|
+
|
|
97
|
+
const parent = data?.parent;
|
|
98
|
+
let childIx = -1;
|
|
99
|
+
let sibCount = 0;
|
|
100
|
+
if (parent && state.nodes[parent]) {
|
|
101
|
+
const parentNodes = state.nodes[parent]?.data?.nodes || [];
|
|
102
|
+
childIx = parentNodes.indexOf(id);
|
|
103
|
+
sibCount = parentNodes.length;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let moveOut = false;
|
|
107
|
+
if (parent && state.nodes[parent]) {
|
|
108
|
+
const grandparent = state.nodes[parent]?.data?.parent;
|
|
109
|
+
if (grandparent && grandparent !== "ROOT" && state.nodes[grandparent]) {
|
|
110
|
+
const greatGrandparent = state.nodes[grandparent]?.data?.parent;
|
|
111
|
+
if (greatGrandparent && state.nodes[greatGrandparent]) {
|
|
112
|
+
moveOut = true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
displayName: name,
|
|
119
|
+
hasNodes: hasChildren,
|
|
120
|
+
isHiddenColumn: shouldHide,
|
|
121
|
+
selected: isSelected,
|
|
122
|
+
parentId: parent,
|
|
123
|
+
childIndex: childIx,
|
|
124
|
+
siblingCount: sibCount,
|
|
125
|
+
canMoveOut: moveOut,
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Keep hidden columns always expanded so their children are visible
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
if (isHiddenColumn && !expanded) {
|
|
132
|
+
setExpandedState(true);
|
|
133
|
+
}
|
|
134
|
+
}, [isHiddenColumn, expanded, setExpandedState]);
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<div
|
|
138
|
+
ref={(dom) => { layer(dom); if (dom) editorConnectors.drop(dom, id); }}
|
|
139
|
+
style={isHiddenColumn ? { marginLeft: "-10px" } : undefined}
|
|
140
|
+
>
|
|
141
|
+
<div
|
|
142
|
+
ref={(dom) => { drag(dom); layerHeader(dom); }}
|
|
143
|
+
className={`builder-layer-node ${isMouseOver ? "hovered" : ""} ${selected ? "selected" : ""}`}
|
|
144
|
+
style={{
|
|
145
|
+
cursor: isHiddenColumn ? "default" : "pointer",
|
|
146
|
+
display: isHiddenColumn ? "none" : "flex",
|
|
147
|
+
alignItems: "center",
|
|
148
|
+
padding: `4px 4px 4px ${depth * 10 + 6}px`,
|
|
149
|
+
overflow: "hidden",
|
|
150
|
+
}}
|
|
151
|
+
onClick={() => editorActions.selectNode(id)}
|
|
152
|
+
onMouseEnter={() => setIsMouseOver(true)}
|
|
153
|
+
onMouseLeave={() => setIsMouseOver(false)}
|
|
154
|
+
>
|
|
155
|
+
|
|
156
|
+
{isEditing ? (
|
|
157
|
+
<input
|
|
158
|
+
value={editValue}
|
|
159
|
+
onChange={(e) => setEditValue(e.target.value)}
|
|
160
|
+
onBlur={() => {
|
|
161
|
+
const trimmed = editValue.trim();
|
|
162
|
+
editorActions.setCustom(id, (custom) => {
|
|
163
|
+
if (trimmed && trimmed !== (custom.displayName || "")) {
|
|
164
|
+
custom.displayName = trimmed;
|
|
165
|
+
} else if (!trimmed) {
|
|
166
|
+
delete custom.displayName;
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
setIsEditing(false);
|
|
170
|
+
}}
|
|
171
|
+
onKeyDown={(e) => {
|
|
172
|
+
if (e.key === "Enter") e.target.blur();
|
|
173
|
+
if (e.key === "Escape") setIsEditing(false);
|
|
174
|
+
}}
|
|
175
|
+
onClick={(e) => e.stopPropagation()}
|
|
176
|
+
onMouseDown={(e) => e.stopPropagation()}
|
|
177
|
+
autoFocus
|
|
178
|
+
style={{
|
|
179
|
+
flexGrow: 1,
|
|
180
|
+
minWidth: 0,
|
|
181
|
+
width: 0,
|
|
182
|
+
fontSize: 13,
|
|
183
|
+
padding: "0 2px",
|
|
184
|
+
border: "1px solid #2680eb",
|
|
185
|
+
outline: "none",
|
|
186
|
+
background: "transparent",
|
|
187
|
+
color: "inherit",
|
|
188
|
+
}}
|
|
189
|
+
/>
|
|
190
|
+
) : (
|
|
191
|
+
<span
|
|
192
|
+
style={{
|
|
193
|
+
fontSize: 13,
|
|
194
|
+
flexGrow: 1,
|
|
195
|
+
minWidth: 0,
|
|
196
|
+
overflow: "hidden",
|
|
197
|
+
textOverflow: "ellipsis",
|
|
198
|
+
whiteSpace: "nowrap",
|
|
199
|
+
}}
|
|
200
|
+
onDoubleClick={(e) => {
|
|
201
|
+
e.stopPropagation();
|
|
202
|
+
setEditValue(displayName);
|
|
203
|
+
setIsEditing(true);
|
|
204
|
+
}}
|
|
205
|
+
>
|
|
206
|
+
{displayName}
|
|
207
|
+
</span>
|
|
208
|
+
)}
|
|
209
|
+
|
|
210
|
+
{isMouseOver && !isEditing && parentId && childIndex >= 0 && (
|
|
211
|
+
<span className="layer-move-buttons" style={{ display: "inline-flex", gap: 2, marginLeft: 4, flexShrink: 0 }}>
|
|
212
|
+
{childIndex > 0 && (
|
|
213
|
+
<span
|
|
214
|
+
title="Move up"
|
|
215
|
+
style={{ cursor: "pointer", padding: "0 2px" }}
|
|
216
|
+
onClick={(e) => {
|
|
217
|
+
e.stopPropagation();
|
|
218
|
+
editorActions.move(id, parentId, childIndex - 1);
|
|
219
|
+
}}
|
|
220
|
+
onMouseDown={(e) => e.stopPropagation()}
|
|
221
|
+
>
|
|
222
|
+
<FontAwesomeIcon icon={faArrowUp} fontSize={10} />
|
|
223
|
+
</span>
|
|
224
|
+
)}
|
|
225
|
+
{childIndex >= 0 && childIndex < siblingCount - 1 && (
|
|
226
|
+
<span
|
|
227
|
+
title="Move down"
|
|
228
|
+
style={{ cursor: "pointer", padding: "0 2px" }}
|
|
229
|
+
onClick={(e) => {
|
|
230
|
+
e.stopPropagation();
|
|
231
|
+
editorActions.move(id, parentId, childIndex + 2);
|
|
232
|
+
}}
|
|
233
|
+
onMouseDown={(e) => e.stopPropagation()}
|
|
234
|
+
>
|
|
235
|
+
<FontAwesomeIcon icon={faArrowDown} fontSize={10} />
|
|
236
|
+
</span>
|
|
237
|
+
)}
|
|
238
|
+
{canMoveOut && (
|
|
239
|
+
<span
|
|
240
|
+
title="Move out of container"
|
|
241
|
+
style={{ cursor: "pointer", padding: "0 2px" }}
|
|
242
|
+
onClick={(e) => {
|
|
243
|
+
e.stopPropagation();
|
|
244
|
+
try {
|
|
245
|
+
const parentData = query.node(parentId).get();
|
|
246
|
+
const grandparentId = parentData.data.parent;
|
|
247
|
+
const grandparentData = query.node(grandparentId).get();
|
|
248
|
+
const greatGrandparentId = grandparentData.data.parent;
|
|
249
|
+
const greatGrandparentChildren = query.node(greatGrandparentId).childNodes();
|
|
250
|
+
const grandparentIndex = greatGrandparentChildren.indexOf(grandparentId);
|
|
251
|
+
editorActions.move(id, greatGrandparentId, grandparentIndex >= 0 ? grandparentIndex + 1 : greatGrandparentChildren.length);
|
|
252
|
+
} catch (err) {
|
|
253
|
+
console.warn("Move out failed:", err);
|
|
254
|
+
}
|
|
255
|
+
}}
|
|
256
|
+
onMouseDown={(e) => e.stopPropagation()}
|
|
257
|
+
>
|
|
258
|
+
<FontAwesomeIcon icon={faLevelUpAlt} fontSize={10} />
|
|
259
|
+
</span>
|
|
260
|
+
)}
|
|
261
|
+
</span>
|
|
262
|
+
)}
|
|
263
|
+
|
|
264
|
+
{hasNodes && (
|
|
265
|
+
<span
|
|
266
|
+
onClick={(e) => {
|
|
267
|
+
e.stopPropagation();
|
|
268
|
+
toggleLayer();
|
|
269
|
+
}}
|
|
270
|
+
>
|
|
271
|
+
<FontAwesomeIcon
|
|
272
|
+
icon={expanded ? faChevronUp : faChevronDown}
|
|
273
|
+
fontSize={14}
|
|
274
|
+
className="float-end fa-lg"
|
|
275
|
+
/>
|
|
276
|
+
</span>
|
|
277
|
+
)}
|
|
278
|
+
</div>
|
|
279
|
+
|
|
280
|
+
{children}
|
|
281
|
+
</div>
|
|
282
|
+
);
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
export default CustomLayer;
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* @subcategory components / elements
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
/* globals validate_bool_expression_elem */
|
|
8
|
+
|
|
9
|
+
import React, { Fragment, useState, useContext, useEffect, useRef } from "react";
|
|
8
10
|
import { useNode } from "@craftjs/core";
|
|
9
11
|
import useTranslation from "../../hooks/useTranslation";
|
|
10
12
|
import optionsCtx from "../context";
|
|
@@ -73,6 +75,7 @@ const DropDownFilterSettings = () => {
|
|
|
73
75
|
}));
|
|
74
76
|
const options = useContext(optionsCtx);
|
|
75
77
|
const setAProp = setAPropGen(setProp);
|
|
78
|
+
const editorRef = useRef(null);
|
|
76
79
|
let select_all_options;
|
|
77
80
|
const field = options.fields.find((f) => f.name === name);
|
|
78
81
|
if (field?.type === "String" && field.attributes?.options)
|
|
@@ -121,9 +124,13 @@ const DropDownFilterSettings = () => {
|
|
|
121
124
|
</td>
|
|
122
125
|
<td>
|
|
123
126
|
<SingleLineEditor
|
|
127
|
+
ref={editorRef}
|
|
124
128
|
value={where}
|
|
125
129
|
setProp={setProp}
|
|
126
130
|
propKey="where"
|
|
131
|
+
onInput={(value) =>
|
|
132
|
+
validate_bool_expression_elem(value, editorRef.current)
|
|
133
|
+
}
|
|
127
134
|
/>
|
|
128
135
|
</td>
|
|
129
136
|
</tr>
|
|
@@ -33,7 +33,7 @@ export /**
|
|
|
33
33
|
* @subcategory components
|
|
34
34
|
*/
|
|
35
35
|
const DropMenu = ({
|
|
36
|
-
|
|
36
|
+
contents,
|
|
37
37
|
action_style,
|
|
38
38
|
action_size,
|
|
39
39
|
action_icon,
|
|
@@ -50,7 +50,6 @@ const DropMenu = ({
|
|
|
50
50
|
connectors: { connect, drag },
|
|
51
51
|
} = useNode((node) => ({ selected: node.events.selected }));
|
|
52
52
|
const [showDropdown, setDropdown] = useState(false);
|
|
53
|
-
//const [dropWidth, setDropWidth] = useState(200);
|
|
54
53
|
return (
|
|
55
54
|
<div
|
|
56
55
|
className={`${selected ? "selected-node" : ""} ${block ? "d-block" : "d-inline"}`}
|
|
@@ -82,7 +81,9 @@ const DropMenu = ({
|
|
|
82
81
|
showDropdown ? "show" : ""
|
|
83
82
|
} ${menu_direction === "end" ? "dropdown-menu-end" : ""}`}
|
|
84
83
|
>
|
|
85
|
-
<
|
|
84
|
+
<Element canvas id="dropmenu-contents" is={Column}>
|
|
85
|
+
{contents}
|
|
86
|
+
</Element>
|
|
86
87
|
</div>
|
|
87
88
|
</div>
|
|
88
89
|
);
|
|
@@ -177,8 +178,13 @@ DropMenu.craft = {
|
|
|
177
178
|
related: {
|
|
178
179
|
settings: DropMenuSettings,
|
|
179
180
|
segment_type: "dropdown_menu",
|
|
180
|
-
hasContents: true,
|
|
181
181
|
fields: [
|
|
182
|
+
{
|
|
183
|
+
label: "Contents",
|
|
184
|
+
name: "contents",
|
|
185
|
+
type: "Nodes",
|
|
186
|
+
nodeID: "dropmenu-contents",
|
|
187
|
+
},
|
|
182
188
|
"label",
|
|
183
189
|
"block",
|
|
184
190
|
"action_style",
|
|
@@ -45,7 +45,9 @@ const fields = (mode) => {
|
|
|
45
45
|
{
|
|
46
46
|
label: "HTML Code",
|
|
47
47
|
name: "text",
|
|
48
|
-
|
|
48
|
+
input_type: "code",
|
|
49
|
+
type: "code",
|
|
50
|
+
attributes: { mode: "text/html" },
|
|
49
51
|
segment_name: "contents",
|
|
50
52
|
onSave: (segment, node_props) => {
|
|
51
53
|
const div = document.createElement("div");
|
|
@@ -3,6 +3,24 @@ import optionsCtx from "../context";
|
|
|
3
3
|
|
|
4
4
|
import Editor, { useMonaco } from "@monaco-editor/react";
|
|
5
5
|
|
|
6
|
+
export const mimeToMonacoLanguage = (mode) => {
|
|
7
|
+
if (!mode) return "typescript";
|
|
8
|
+
const map = {
|
|
9
|
+
"text/javascript": "typescript",
|
|
10
|
+
"application/javascript": "typescript",
|
|
11
|
+
"text/html": "html",
|
|
12
|
+
"text/css": "css",
|
|
13
|
+
"application/json": "json",
|
|
14
|
+
"text/x-sql": "sql",
|
|
15
|
+
"text/x-python": "python",
|
|
16
|
+
"text/x-yaml": "yaml",
|
|
17
|
+
"text/xml": "xml",
|
|
18
|
+
"text/x-markdown": "markdown",
|
|
19
|
+
"text/typescript": "typescript",
|
|
20
|
+
};
|
|
21
|
+
return map[mode] || mode;
|
|
22
|
+
};
|
|
23
|
+
|
|
6
24
|
const setMonacoLanguage = async (monaco, options, isStatements) => {
|
|
7
25
|
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
|
8
26
|
noLib: true,
|
|
@@ -27,33 +45,108 @@ const setMonacoLanguage = async (monaco, options, isStatements) => {
|
|
|
27
45
|
});
|
|
28
46
|
};
|
|
29
47
|
|
|
48
|
+
// add hidden-prefix
|
|
49
|
+
// line 1 holds the typed prefix, the user edits from line 2 onwards
|
|
50
|
+
const setupVirtualPrefix = (editor, monaco) => {
|
|
51
|
+
const model = editor.getModel();
|
|
52
|
+
editor.setHiddenAreas([{ startLineNumber: 1, endLineNumber: 1 }]);
|
|
53
|
+
editor.onDidChangeCursorPosition((e) => {
|
|
54
|
+
if (e.position.lineNumber < 2)
|
|
55
|
+
editor.setPosition({ lineNumber: 2, column: 1 });
|
|
56
|
+
});
|
|
57
|
+
editor.onKeyDown((e) => {
|
|
58
|
+
const pos = editor.getPosition();
|
|
59
|
+
if (
|
|
60
|
+
pos.lineNumber === 2 &&
|
|
61
|
+
pos.column === 1 &&
|
|
62
|
+
e.keyCode === monaco.KeyCode.Backspace
|
|
63
|
+
) {
|
|
64
|
+
e.preventDefault();
|
|
65
|
+
e.stopPropagation();
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
// copy without the prefix line
|
|
69
|
+
editor.addAction({
|
|
70
|
+
id: "copy-editable-only",
|
|
71
|
+
label: "Copy Only User Content",
|
|
72
|
+
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyC],
|
|
73
|
+
run: (ed) => {
|
|
74
|
+
const selection = ed.getSelection();
|
|
75
|
+
if (selection.isEmpty()) return;
|
|
76
|
+
const safeSelection = selection.intersectRanges(
|
|
77
|
+
new monaco.Range(
|
|
78
|
+
2,
|
|
79
|
+
1,
|
|
80
|
+
model.getLineCount(),
|
|
81
|
+
model.getLineMaxColumn(model.getLineCount())
|
|
82
|
+
)
|
|
83
|
+
);
|
|
84
|
+
if (safeSelection)
|
|
85
|
+
navigator.clipboard.writeText(model.getValueInRange(safeSelection));
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
// select all without the prefix line
|
|
89
|
+
editor.addAction({
|
|
90
|
+
id: "select-editable-only",
|
|
91
|
+
label: "Select Only User Content",
|
|
92
|
+
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyA],
|
|
93
|
+
run: (ed) => {
|
|
94
|
+
ed.setSelection(
|
|
95
|
+
new monaco.Range(
|
|
96
|
+
2,
|
|
97
|
+
1,
|
|
98
|
+
model.getLineCount(),
|
|
99
|
+
model.getLineMaxColumn(model.getLineCount())
|
|
100
|
+
)
|
|
101
|
+
);
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
|
|
30
106
|
export const SingleLineEditor = React.forwardRef(
|
|
31
|
-
(
|
|
107
|
+
(
|
|
108
|
+
{ setProp, value, propKey, onChange, onInput, className, stateExpr },
|
|
109
|
+
ref
|
|
110
|
+
) => {
|
|
32
111
|
const options = React.useContext(optionsCtx);
|
|
112
|
+
const activePrefix = stateExpr ? "const _: Row =" : null;
|
|
33
113
|
|
|
34
114
|
const handleEditorWillMount = (monaco) => {
|
|
35
115
|
setMonacoLanguage(monaco, options, false);
|
|
36
116
|
};
|
|
37
117
|
|
|
38
118
|
const handleEditorDidMount = (editor, monaco) => {
|
|
119
|
+
if (activePrefix) {
|
|
120
|
+
setupVirtualPrefix(editor, monaco);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
39
123
|
if (!onInput) return;
|
|
40
124
|
|
|
41
125
|
editor.onDidChangeModelContent(() => {
|
|
42
|
-
|
|
43
|
-
onInput(value);
|
|
126
|
+
onInput(editor.getValue());
|
|
44
127
|
});
|
|
45
128
|
};
|
|
46
129
|
|
|
130
|
+
const isEmpty = !value || value.trim() === "";
|
|
131
|
+
const editorValue = activePrefix
|
|
132
|
+
? `${isEmpty ? "//" : ""} ${activePrefix}\n${value || ""}`
|
|
133
|
+
: value;
|
|
134
|
+
|
|
47
135
|
return (
|
|
48
136
|
<div ref={ref} className="form-control p-0 pt-1">
|
|
49
137
|
<Editor
|
|
50
138
|
placeholder={"sdfffsd"}
|
|
51
139
|
className={className || ""}
|
|
52
140
|
height="22px"
|
|
53
|
-
value={
|
|
54
|
-
onChange={(
|
|
55
|
-
|
|
56
|
-
|
|
141
|
+
value={editorValue}
|
|
142
|
+
onChange={(fullValue) => {
|
|
143
|
+
const userValue = activePrefix
|
|
144
|
+
? (fullValue || "").substring((fullValue || "").indexOf("\n") + 1)
|
|
145
|
+
: fullValue;
|
|
146
|
+
onChange && onChange(userValue);
|
|
147
|
+
setProp &&
|
|
148
|
+
propKey &&
|
|
149
|
+
setProp((prop) => (prop[propKey] = userValue));
|
|
57
150
|
}}
|
|
58
151
|
defaultLanguage="typescript"
|
|
59
152
|
//onMount={handleEditorDidMount}
|
|
@@ -68,25 +161,37 @@ export const SingleLineEditor = React.forwardRef(
|
|
|
68
161
|
}
|
|
69
162
|
);
|
|
70
163
|
|
|
71
|
-
export const MultiLineCodeEditor = ({ setProp, value, onChange, isModalEditor = false }) => {
|
|
164
|
+
export const MultiLineCodeEditor = ({ setProp, value, onChange, isModalEditor = false, mode }) => {
|
|
72
165
|
const options = React.useContext(optionsCtx);
|
|
166
|
+
const resolvedLanguage = mimeToMonacoLanguage(mode);
|
|
167
|
+
const useTypeScriptSetup = resolvedLanguage === "typescript" || resolvedLanguage === "javascript";
|
|
73
168
|
|
|
74
169
|
const handleEditorWillMount = (monaco) => {
|
|
75
|
-
|
|
170
|
+
if (useTypeScriptSetup) {
|
|
171
|
+
setMonacoLanguage(monaco, options, true);
|
|
172
|
+
}
|
|
76
173
|
};
|
|
174
|
+
|
|
175
|
+
const handleEditorDidMount = (editor, monaco) => {
|
|
176
|
+
if (!useTypeScriptSetup) {
|
|
177
|
+
const model = editor.getModel();
|
|
178
|
+
if (model) {
|
|
179
|
+
monaco.editor.setModelLanguage(model, resolvedLanguage);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
77
184
|
return (
|
|
78
185
|
<div className="form-control p-0 pt-2">
|
|
79
186
|
<Editor
|
|
80
187
|
height={isModalEditor ? "100%" : "150px"}
|
|
81
188
|
value={value}
|
|
82
189
|
onChange={onChange}
|
|
83
|
-
defaultLanguage=
|
|
84
|
-
//onMount={handleEditorDidMount}
|
|
85
|
-
//beforeMount={handleEditorWillMount}
|
|
190
|
+
defaultLanguage={resolvedLanguage}
|
|
86
191
|
options={multiLineEditorOptions}
|
|
87
|
-
//theme="myCoolTheme"
|
|
88
192
|
beforeMount={handleEditorWillMount}
|
|
89
|
-
|
|
193
|
+
onMount={handleEditorDidMount}
|
|
194
|
+
className={isModalEditor ? "code-modal-form" : ""}
|
|
90
195
|
/>
|
|
91
196
|
</div>
|
|
92
197
|
);
|
|
@@ -94,7 +199,7 @@ export const MultiLineCodeEditor = ({ setProp, value, onChange, isModalEditor =
|
|
|
94
199
|
|
|
95
200
|
const multiLineEditorOptions = {
|
|
96
201
|
fontSize: "14px",
|
|
97
|
-
minHeight:"80vh",
|
|
202
|
+
minHeight: "80vh",
|
|
98
203
|
fontWeight: "normal",
|
|
99
204
|
wordWrap: "off",
|
|
100
205
|
lineNumbers: "off",
|