@node-projects/web-component-designer-visualization-addons 0.1.145 → 0.1.147
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/components/EventAssignment.d.ts +3 -1
- package/dist/components/EventAssignment.js +7 -2
- package/dist/components/SimpleScriptCommandPicker.d.ts +25 -0
- package/dist/components/SimpleScriptCommandPicker.js +271 -0
- package/dist/components/SimpleScriptEditor.d.ts +27 -6
- package/dist/components/SimpleScriptEditor.js +877 -143
- package/dist/components/SimpleScriptEditorHelp.d.ts +1 -0
- package/dist/components/SimpleScriptEditorHelp.js +151 -0
- package/dist/components/VisualizationPropertyGrid.d.ts +2 -0
- package/dist/components/VisualizationPropertyGrid.js +8 -0
- package/dist/index-min.js +577 -49
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/interfaces/VisualizationShell.d.ts +11 -0
- package/dist/scripting/JsonSnippetHighlighter.d.ts +3 -0
- package/dist/scripting/JsonSnippetHighlighter.js +16 -0
- package/dist/scripting/ScriptCommands.d.ts +34 -1
- package/dist/scripting/ScriptCommandsDescriptions.d.ts +9 -0
- package/dist/scripting/ScriptCommandsDescriptions.js +159 -0
- package/dist/scripting/ScriptSystem.d.ts +3 -2
- package/dist/scripting/ScriptSystem.js +25 -0
- package/dist/scripting/ScriptUpgrader.d.ts +1 -1
- package/dist/scripting/ScriptUpgrader.js +4 -0
- package/package.json +3 -3
|
@@ -1,80 +1,349 @@
|
|
|
1
|
-
import { BaseCustomWebComponentConstructorAppend, css, html } from "@node-projects/base-custom-webcomponent";
|
|
2
|
-
import { ContextMenu } from "@node-projects/web-component-designer";
|
|
3
|
-
import { typeInfoFromJsonSchema } from "@node-projects/propertygrid.webcomponent";
|
|
4
|
-
import { defaultOptions } from "@node-projects/web-component-designer-widgets-wunderbaum";
|
|
5
|
-
import { Wunderbaum } from
|
|
1
|
+
import { BaseCustomWebComponentConstructorAppend, TypedEvent, css, html, } from "@node-projects/base-custom-webcomponent";
|
|
2
|
+
import { ContextMenu, assetsPath, } from "@node-projects/web-component-designer";
|
|
3
|
+
import { typeInfoFromJsonSchema, } from "@node-projects/propertygrid.webcomponent";
|
|
4
|
+
import { defaultOptions, defaultStyle } from "@node-projects/web-component-designer-widgets-wunderbaum";
|
|
5
|
+
import { Wunderbaum } from "wunderbaum";
|
|
6
6
|
//@ts-ignore
|
|
7
|
-
import wunderbaumStyle from
|
|
7
|
+
import wunderbaumStyle from "wunderbaum/dist/wunderbaum.css" with { type: "css" };
|
|
8
8
|
import { VisualizationPropertyGrid } from "./VisualizationPropertyGrid.js";
|
|
9
|
-
import
|
|
9
|
+
import { CodeViewMonaco } from "@node-projects/web-component-designer-codeview-monaco";
|
|
10
|
+
import { SimpleScriptCommandPicker } from "./SimpleScriptCommandPicker.js";
|
|
11
|
+
import { simpleScriptEditorHelpHtml } from "./SimpleScriptEditorHelp.js";
|
|
12
|
+
import { nativeScriptCommandDescriptions, } from "../scripting/ScriptCommandsDescriptions.js";
|
|
13
|
+
import "@node-projects/splitview.webcomponent";
|
|
10
14
|
import { ScriptUpgrades } from "../scripting/ScriptUpgrader.js";
|
|
15
|
+
const SAME_TREE_MOVE_MIME = "application/x-simple-script-move";
|
|
11
16
|
export class SimpleScriptEditor extends BaseCustomWebComponentConstructorAppend {
|
|
12
17
|
static style = css `
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
.list{
|
|
17
|
-
display: grid;
|
|
18
|
-
grid-template-columns: 1fr 40px;
|
|
19
|
-
width: calc(100% - 6px);
|
|
20
|
-
box-sizing: border-box;
|
|
21
|
-
margin: 3px;
|
|
22
|
-
}
|
|
18
|
+
:host {
|
|
19
|
+
background: white;
|
|
20
|
+
}
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
.root {
|
|
23
|
+
width: 100%;
|
|
24
|
+
height: 100%;
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
box-sizing: border-box;
|
|
29
|
+
}
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
.content {
|
|
32
|
+
flex: 1 1 auto;
|
|
33
|
+
min-height: 0;
|
|
34
|
+
position: relative;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.split-view {
|
|
38
|
+
height: calc(100% - 4px);
|
|
39
|
+
width: calc(100% - 4px);
|
|
40
|
+
position: relative;
|
|
41
|
+
margin: 2px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.list-pane {
|
|
45
|
+
width: 48%;
|
|
46
|
+
height: 100%;
|
|
47
|
+
position: relative;
|
|
48
|
+
box-sizing: border-box;
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-direction: column;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.list-content {
|
|
54
|
+
flex: 1 1 auto;
|
|
55
|
+
min-height: 0;
|
|
56
|
+
position: relative;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.grid-pane {
|
|
60
|
+
width: 52%;
|
|
61
|
+
height: 100%;
|
|
62
|
+
position: relative;
|
|
63
|
+
box-sizing: border-box;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.split-view.code-mode .grid-pane {
|
|
67
|
+
display: none;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.split-view.code-mode::part(splitter) {
|
|
71
|
+
display: none;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
#commandList {
|
|
75
|
+
position: absolute;
|
|
76
|
+
inset: 0;
|
|
77
|
+
overflow-x: hidden;
|
|
78
|
+
overflow-y: auto;
|
|
79
|
+
box-sizing: border-box;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
#commandList.hidden {
|
|
83
|
+
display: none;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#commandList {
|
|
87
|
+
--wb-icon-outer-width: 22px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
#commandList i.wb-expander {
|
|
91
|
+
background-size: 9px 9px;
|
|
92
|
+
background-position-x: 6px;
|
|
93
|
+
background-position-y: 6px;
|
|
94
|
+
opacity: 0.55;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.code-view {
|
|
98
|
+
position: absolute;
|
|
99
|
+
inset: 0;
|
|
100
|
+
display: none;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.code-view.visible {
|
|
104
|
+
display: block;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
node-projects-visualization-property-grid {
|
|
108
|
+
width: 100%;
|
|
109
|
+
height: 100%;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.toolbar {
|
|
113
|
+
display: flex;
|
|
114
|
+
align-items: center;
|
|
115
|
+
gap: 8px;
|
|
116
|
+
box-sizing: border-box;
|
|
117
|
+
flex: 0 0 auto;
|
|
118
|
+
width: 100%;
|
|
119
|
+
height: 36px;
|
|
120
|
+
padding: 4px 8px;
|
|
121
|
+
border-top: 1px solid #ccc;
|
|
122
|
+
background-color: rgb(230, 230, 230);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.toolbar button {
|
|
126
|
+
box-sizing: border-box;
|
|
127
|
+
padding: 6px 16px;
|
|
128
|
+
border-radius: 4px;
|
|
129
|
+
border: 1px solid #b0b0b0;
|
|
130
|
+
background: white;
|
|
131
|
+
font-size: 12px;
|
|
132
|
+
cursor: pointer;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.toolbar button:hover:not(:disabled) {
|
|
136
|
+
background: #f0f0f0;
|
|
137
|
+
border-color: #909090;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.toolbar button:active:not(:disabled) {
|
|
141
|
+
background: #e2e2e2;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.toolbar button:disabled {
|
|
145
|
+
opacity: 0.5;
|
|
146
|
+
cursor: not-allowed;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.toolbar button.primary {
|
|
150
|
+
background: #2c6e9b;
|
|
151
|
+
border-color: #2c6e9b;
|
|
152
|
+
color: white;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.toolbar button.primary:hover:not(:disabled) {
|
|
156
|
+
background: #255d83;
|
|
157
|
+
border-color: #255d83;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.toolbar button.primary:active:not(:disabled) {
|
|
161
|
+
background: #1e4d6b;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.toolbar .spacer {
|
|
165
|
+
flex: 1 1 auto;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.toolbar .icon-btn {
|
|
169
|
+
display: flex;
|
|
170
|
+
align-items: center;
|
|
171
|
+
justify-content: center;
|
|
172
|
+
width: 22px;
|
|
173
|
+
height: 22px;
|
|
174
|
+
padding: 0;
|
|
175
|
+
box-sizing: border-box;
|
|
176
|
+
border: none;
|
|
177
|
+
background: transparent;
|
|
178
|
+
cursor: pointer;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.toolbar .icon-btn:hover {
|
|
182
|
+
background: #eee;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.drag-handle {
|
|
186
|
+
position: absolute;
|
|
187
|
+
left: 0;
|
|
188
|
+
top: 0;
|
|
189
|
+
height: 100%;
|
|
190
|
+
width: 24px;
|
|
191
|
+
display: flex;
|
|
192
|
+
align-items: center;
|
|
193
|
+
justify-content: center;
|
|
194
|
+
color: #888;
|
|
195
|
+
cursor: grab;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.cmd {
|
|
199
|
+
position: absolute;
|
|
200
|
+
right: 0;
|
|
201
|
+
top: 0;
|
|
202
|
+
height: 100%;
|
|
203
|
+
display: flex;
|
|
204
|
+
align-items: center;
|
|
205
|
+
gap: 2px;
|
|
206
|
+
padding: 0 2px;
|
|
207
|
+
background: white;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.cmd img {
|
|
211
|
+
width: 12px;
|
|
212
|
+
height: 12px;
|
|
213
|
+
cursor: pointer;
|
|
214
|
+
}
|
|
215
|
+
`;
|
|
38
216
|
static template = html `
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
217
|
+
<div class="root">
|
|
218
|
+
<div class="content">
|
|
219
|
+
<node-projects-split-view
|
|
220
|
+
id="splitView"
|
|
221
|
+
class="split-view"
|
|
222
|
+
orientation="horizontal"
|
|
223
|
+
>
|
|
224
|
+
<div id="listPane" class="list-pane">
|
|
225
|
+
<div id="listContent" class="list-content">
|
|
226
|
+
<div id="commandList"></div>
|
|
227
|
+
<node-projects-code-view-monaco
|
|
228
|
+
id="codeView"
|
|
229
|
+
class="code-view"
|
|
230
|
+
language="json"
|
|
231
|
+
></node-projects-code-view-monaco>
|
|
232
|
+
</div>
|
|
233
|
+
<div class="toolbar">
|
|
234
|
+
<button
|
|
235
|
+
id="addCommandBtn"
|
|
236
|
+
class="primary"
|
|
237
|
+
@click="[[this.addCommand()]]"
|
|
238
|
+
>
|
|
239
|
+
Add Command
|
|
240
|
+
</button>
|
|
241
|
+
<button id="codeToggleBtn" @click="[[this.toggleCodeView()]]">
|
|
242
|
+
Code
|
|
243
|
+
</button>
|
|
244
|
+
<span class="spacer"></span>
|
|
245
|
+
<button
|
|
246
|
+
class="icon-btn"
|
|
247
|
+
title="Help"
|
|
248
|
+
@click="[[this.helpClicked()]]"
|
|
249
|
+
>
|
|
250
|
+
<svg viewBox="0 0 512 512" width="16" height="16">
|
|
251
|
+
<path
|
|
252
|
+
d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"
|
|
253
|
+
/>
|
|
254
|
+
</svg>
|
|
255
|
+
</button>
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
<div class="grid-pane">
|
|
259
|
+
<node-projects-visualization-property-grid
|
|
260
|
+
id="propertygrid"
|
|
261
|
+
></node-projects-visualization-property-grid>
|
|
262
|
+
</div>
|
|
263
|
+
</node-projects-split-view>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
`;
|
|
267
|
+
static is = "node-projects-visualization-simple-script-editor";
|
|
268
|
+
_title;
|
|
269
|
+
get title() {
|
|
270
|
+
return this._title;
|
|
271
|
+
}
|
|
272
|
+
set title(value) {
|
|
273
|
+
this._title = value;
|
|
274
|
+
}
|
|
57
275
|
serviceContainer;
|
|
58
276
|
instanceServiceContainer;
|
|
59
277
|
visualizationHandler;
|
|
60
278
|
visualizationShell;
|
|
61
279
|
scriptCommandsTypeInfo;
|
|
62
280
|
propertiesTypeInfo;
|
|
281
|
+
helpRequested = new TypedEvent();
|
|
282
|
+
commandDescriptions = {
|
|
283
|
+
...nativeScriptCommandDescriptions,
|
|
284
|
+
};
|
|
63
285
|
_script;
|
|
64
286
|
_commandListDiv;
|
|
65
287
|
_commandListFancyTree;
|
|
66
|
-
_possibleCommands;
|
|
67
288
|
_propertygrid;
|
|
289
|
+
_splitView;
|
|
290
|
+
_codeView;
|
|
291
|
+
_codeToggleBtn;
|
|
292
|
+
_addCommandBtn;
|
|
293
|
+
_showingCode = false;
|
|
294
|
+
_dragFromHandle = false;
|
|
295
|
+
_draggedNode = null;
|
|
68
296
|
constructor() {
|
|
69
297
|
super();
|
|
70
298
|
this._restoreCachedInititalValues();
|
|
71
|
-
this._commandListDiv = this._getDomElement(
|
|
72
|
-
this.
|
|
73
|
-
|
|
74
|
-
this.
|
|
299
|
+
this._commandListDiv = this._getDomElement("commandList");
|
|
300
|
+
this._propertygrid =
|
|
301
|
+
this._getDomElement("propertygrid");
|
|
302
|
+
this._splitView = this._getDomElement("splitView");
|
|
303
|
+
this._codeView = this._getDomElement("codeView");
|
|
304
|
+
this._codeToggleBtn =
|
|
305
|
+
this._getDomElement("codeToggleBtn");
|
|
306
|
+
this._addCommandBtn =
|
|
307
|
+
this._getDomElement("addCommandBtn");
|
|
308
|
+
const listContent = this._getDomElement("listContent");
|
|
309
|
+
listContent.addEventListener("mousedown", (ev) => {
|
|
310
|
+
this._dragFromHandle = !!ev.target?.closest(".drag-handle");
|
|
311
|
+
});
|
|
312
|
+
document.addEventListener("dragend", () => {
|
|
313
|
+
this._draggedNode = null;
|
|
314
|
+
}, true);
|
|
315
|
+
listContent.addEventListener("dragover", (ev) => {
|
|
316
|
+
ev.preventDefault();
|
|
317
|
+
if (ev.dataTransfer) {
|
|
318
|
+
const isMove = ev.dataTransfer.types.includes(SAME_TREE_MOVE_MIME);
|
|
319
|
+
ev.dataTransfer.dropEffect = isMove ? "move" : "copy";
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
listContent.addEventListener("drop", (ev) => {
|
|
323
|
+
ev.preventDefault();
|
|
324
|
+
const isMove = ev.dataTransfer?.types.includes(SAME_TREE_MOVE_MIME);
|
|
325
|
+
if (isMove && this._draggedNode) {
|
|
326
|
+
const children = this._commandListFancyTree.root.children;
|
|
327
|
+
const last = children?.[children.length - 1];
|
|
328
|
+
if (last)
|
|
329
|
+
this._draggedNode.moveTo(last, "after");
|
|
330
|
+
this._draggedNode.setClass("wb-drag-source", false);
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
const type = ev.dataTransfer?.getData("text/plain");
|
|
334
|
+
if (type) {
|
|
335
|
+
const command = { type };
|
|
336
|
+
this._commandListFancyTree.root.addChildren(this.createTreeItem(command));
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
this.shadowRoot.adoptedStyleSheets = [
|
|
341
|
+
wunderbaumStyle,
|
|
342
|
+
defaultStyle,
|
|
343
|
+
SimpleScriptEditor.style,
|
|
344
|
+
];
|
|
75
345
|
}
|
|
76
346
|
async ready() {
|
|
77
|
-
this.addPossibleCommands();
|
|
78
347
|
this._parseAttributesToProperties();
|
|
79
348
|
this._bindingsParse(null, true);
|
|
80
349
|
this._assignEvents();
|
|
@@ -84,168 +353,528 @@ export class SimpleScriptEditor extends BaseCustomWebComponentConstructorAppend
|
|
|
84
353
|
pg.visualizationShell = this.visualizationShell;
|
|
85
354
|
pg.serviceContainer = this.serviceContainer;
|
|
86
355
|
pg.instanceServiceContainer = this.instanceServiceContainer;
|
|
87
|
-
pg.bindableObjectsTarget =
|
|
356
|
+
pg.bindableObjectsTarget = "script";
|
|
88
357
|
pg.getTypeInfo = (obj, type) => typeInfoFromJsonSchema(this.propertiesTypeInfo, obj, type);
|
|
89
358
|
pg.showHead = false;
|
|
90
|
-
pg.typeName =
|
|
359
|
+
pg.typeName = "IScriptMultiplexValue";
|
|
91
360
|
pg.title = 'Complex for "' + data.propertyPath + '"';
|
|
92
|
-
if (typeof data.value ===
|
|
361
|
+
if (typeof data.value === "object")
|
|
93
362
|
pg.selectedObject = data.value ?? {};
|
|
94
363
|
else
|
|
95
364
|
pg.selectedObject = {};
|
|
96
365
|
pg.bindingDoubleClicked = (b) => {
|
|
97
|
-
if (b.bindabletype ==
|
|
98
|
-
pg.setPropertyValue(
|
|
366
|
+
if (b.bindabletype == "property") {
|
|
367
|
+
pg.setPropertyValue("source", "property");
|
|
99
368
|
}
|
|
100
|
-
else if (b.bindabletype ==
|
|
101
|
-
pg.setPropertyValue(
|
|
369
|
+
else if (b.bindabletype == "context") {
|
|
370
|
+
pg.setPropertyValue("source", "context");
|
|
102
371
|
}
|
|
103
372
|
else {
|
|
104
|
-
pg.setPropertyValue(
|
|
373
|
+
pg.setPropertyValue("source", "signal");
|
|
105
374
|
}
|
|
106
|
-
pg.setPropertyValue(
|
|
375
|
+
pg.setPropertyValue("name", b.fullName);
|
|
107
376
|
pg.refresh();
|
|
108
377
|
};
|
|
109
|
-
let res = await this.visualizationShell.openConfirmation(pg, {
|
|
378
|
+
let res = await this.visualizationShell.openConfirmation(pg, {
|
|
379
|
+
x: 100,
|
|
380
|
+
y: 100,
|
|
381
|
+
width: 400,
|
|
382
|
+
height: 500,
|
|
383
|
+
parent: this,
|
|
384
|
+
});
|
|
110
385
|
if (res) {
|
|
111
386
|
this._propertygrid.setPropertyValue(data.propertyPath, pg.selectedObject);
|
|
112
387
|
this._propertygrid.refresh();
|
|
113
388
|
}
|
|
114
389
|
};
|
|
390
|
+
let editFormula = async (data) => {
|
|
391
|
+
const editor = new CodeViewMonaco();
|
|
392
|
+
editor.language = "javascript";
|
|
393
|
+
editor.style.position = "relative";
|
|
394
|
+
editor.code = data.value ?? "";
|
|
395
|
+
const res = await this.visualizationShell.openConfirmation(editor, {
|
|
396
|
+
title: "Edit formula",
|
|
397
|
+
x: 100,
|
|
398
|
+
y: 50,
|
|
399
|
+
width: 700,
|
|
400
|
+
height: 450,
|
|
401
|
+
parent: this,
|
|
402
|
+
});
|
|
403
|
+
if (res) {
|
|
404
|
+
this._propertygrid.setPropertyValue(data.propertyPath, editor.code);
|
|
405
|
+
this._propertygrid.refresh();
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
const signalSources = [
|
|
409
|
+
"signal",
|
|
410
|
+
"property",
|
|
411
|
+
"elementProperty",
|
|
412
|
+
"signalInProperty",
|
|
413
|
+
"event",
|
|
414
|
+
"parameter",
|
|
415
|
+
"context",
|
|
416
|
+
"complexString",
|
|
417
|
+
"complexSignal",
|
|
418
|
+
"expression",
|
|
419
|
+
];
|
|
420
|
+
const identifierRegex = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
421
|
+
const jsReservedWords = new Set([
|
|
422
|
+
"break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete", "do",
|
|
423
|
+
"else", "export", "extends", "finally", "for", "function", "if", "import", "in", "instanceof",
|
|
424
|
+
"new", "return", "super", "switch", "this", "throw", "try", "typeof", "var", "void", "while",
|
|
425
|
+
"with", "yield", "let", "static", "enum", "await", "implements", "package", "protected",
|
|
426
|
+
"interface", "private", "public", "null", "true", "false", "undefined", "arguments", "eval",
|
|
427
|
+
]);
|
|
428
|
+
const signalSourceDescriptions = {
|
|
429
|
+
signal: "read the value from a Signal",
|
|
430
|
+
property: "read the value from a property of the customControl (not usable in screens)",
|
|
431
|
+
elementProperty: "a property defined on the element raising the event",
|
|
432
|
+
signalInProperty: "read the value from a Signal (wich name is in the Property)",
|
|
433
|
+
event: "read the value of a property of the event object",
|
|
434
|
+
parameter: "a parameter you hand over",
|
|
435
|
+
context: "a value of the context",
|
|
436
|
+
complexString: "a string with signals (contained in {})",
|
|
437
|
+
complexSignal: "read the value from a signal wich name is build here (it can contain other signals in {})",
|
|
438
|
+
expression: "js expression, 'ctx' is context object",
|
|
439
|
+
};
|
|
440
|
+
let editSignalList = async (data) => {
|
|
441
|
+
const arr = (data.value ?? []).map((v) => ({
|
|
442
|
+
...v,
|
|
443
|
+
}));
|
|
444
|
+
const getVarNameError = (i) => {
|
|
445
|
+
const raw = (arr[i].varName ?? "").trim();
|
|
446
|
+
if (!raw)
|
|
447
|
+
return null;
|
|
448
|
+
if (!identifierRegex.test(raw))
|
|
449
|
+
return `"${raw}" is not a valid JavaScript variable name.`;
|
|
450
|
+
if (jsReservedWords.has(raw))
|
|
451
|
+
return `"${raw}" is a reserved word and cannot be used as a variable name.`;
|
|
452
|
+
if (arr.some((e, j) => j !== i && (e.varName ?? "").trim() === raw))
|
|
453
|
+
return `"${raw}" is used by more than one signal.`;
|
|
454
|
+
return null;
|
|
455
|
+
};
|
|
456
|
+
const controlStyle = "height:22px; box-sizing:border-box; font-size:12px;";
|
|
457
|
+
const container = document.createElement("div");
|
|
458
|
+
container.style.cssText =
|
|
459
|
+
"display:flex; flex-direction:column; gap:6px; padding:8px; height:100%; box-sizing:border-box; overflow-y:auto; font-size:12px;";
|
|
460
|
+
const header = document.createElement("div");
|
|
461
|
+
header.style.cssText = "display:flex; align-items:center; gap:4px; font-weight:bold;";
|
|
462
|
+
const headerName = document.createElement("span");
|
|
463
|
+
headerName.textContent = "Name";
|
|
464
|
+
headerName.style.cssText = "flex:0 0 120px;";
|
|
465
|
+
const headerType = document.createElement("span");
|
|
466
|
+
headerType.textContent = "Typ";
|
|
467
|
+
headerType.style.cssText = "flex:0 0 130px;";
|
|
468
|
+
const headerSignal = document.createElement("span");
|
|
469
|
+
headerSignal.textContent = "Signal";
|
|
470
|
+
headerSignal.style.cssText = "flex:1 1 auto;";
|
|
471
|
+
const headerSpacer = document.createElement("span");
|
|
472
|
+
headerSpacer.style.cssText = "flex:0 0 22px;";
|
|
473
|
+
header.append(headerName, headerType, headerSignal, headerSpacer);
|
|
474
|
+
container.appendChild(header);
|
|
475
|
+
const rowsContainer = document.createElement("div");
|
|
476
|
+
rowsContainer.style.cssText =
|
|
477
|
+
"display:flex; flex-direction:column; gap:4px;";
|
|
478
|
+
container.appendChild(rowsContainer);
|
|
479
|
+
const descPanel = document.createElement("div");
|
|
480
|
+
descPanel.style.cssText =
|
|
481
|
+
"flex:0 0 auto; margin-top:4px; padding-top:6px; border-top:1px solid #ccc; font-size:12px;";
|
|
482
|
+
const descTitle = document.createElement("div");
|
|
483
|
+
descTitle.style.cssText = "font-weight:bold; margin-bottom:2px;";
|
|
484
|
+
const descText = document.createElement("div");
|
|
485
|
+
descText.style.cssText = "color:#333; white-space:pre-line;";
|
|
486
|
+
descPanel.append(descTitle, descText);
|
|
487
|
+
const showSourceDescription = (source) => {
|
|
488
|
+
descTitle.textContent = "Typ: " + source;
|
|
489
|
+
descText.textContent = signalSourceDescriptions[source] ?? "";
|
|
490
|
+
};
|
|
491
|
+
const varNameInputs = [];
|
|
492
|
+
const revalidate = () => {
|
|
493
|
+
arr.forEach((_, i) => {
|
|
494
|
+
const input = varNameInputs[i];
|
|
495
|
+
if (!input)
|
|
496
|
+
return;
|
|
497
|
+
const err = getVarNameError(i);
|
|
498
|
+
input.title = err ?? `Variable name used in the formula (defaults to __${i} if empty)`;
|
|
499
|
+
input.style.borderColor = err ? "#c0392b" : "";
|
|
500
|
+
input.style.backgroundColor = err ? "#fdecea" : "";
|
|
501
|
+
});
|
|
502
|
+
};
|
|
503
|
+
const renderRows = () => {
|
|
504
|
+
rowsContainer.innerHTML = "";
|
|
505
|
+
varNameInputs.length = 0;
|
|
506
|
+
arr.forEach((entry, i) => {
|
|
507
|
+
const row = document.createElement("div");
|
|
508
|
+
row.style.cssText = "display:flex; align-items:center; gap:4px;";
|
|
509
|
+
const varNameInput = document.createElement("input");
|
|
510
|
+
varNameInput.value = entry.varName ?? "";
|
|
511
|
+
varNameInput.placeholder = "__" + i;
|
|
512
|
+
varNameInput.style.cssText = controlStyle + " flex:0 0 120px; min-width: 120px; font-family:monospace;";
|
|
513
|
+
varNameInput.oninput = () => {
|
|
514
|
+
entry.varName = varNameInput.value || undefined;
|
|
515
|
+
revalidate();
|
|
516
|
+
};
|
|
517
|
+
varNameInputs.push(varNameInput);
|
|
518
|
+
row.appendChild(varNameInput);
|
|
519
|
+
const sourceSelect = document.createElement("select");
|
|
520
|
+
sourceSelect.style.cssText = controlStyle + " flex:0 0 130px;";
|
|
521
|
+
for (const s of signalSources) {
|
|
522
|
+
const opt = document.createElement("option");
|
|
523
|
+
opt.value = s;
|
|
524
|
+
opt.textContent = s;
|
|
525
|
+
if (entry.source === s)
|
|
526
|
+
opt.selected = true;
|
|
527
|
+
sourceSelect.appendChild(opt);
|
|
528
|
+
}
|
|
529
|
+
sourceSelect.onchange = () => {
|
|
530
|
+
entry.source = sourceSelect.value;
|
|
531
|
+
showSourceDescription(sourceSelect.value);
|
|
532
|
+
};
|
|
533
|
+
sourceSelect.onfocus = () => showSourceDescription(sourceSelect.value);
|
|
534
|
+
row.appendChild(sourceSelect);
|
|
535
|
+
const nameInput = document.createElement("input");
|
|
536
|
+
nameInput.value = entry.name ?? "";
|
|
537
|
+
nameInput.placeholder = "e.g. srm.rbg{__tagRoot}.error";
|
|
538
|
+
nameInput.style.cssText = controlStyle + " flex:1 1 auto;";
|
|
539
|
+
nameInput.oninput = () => {
|
|
540
|
+
entry.name = nameInput.value;
|
|
541
|
+
};
|
|
542
|
+
row.appendChild(nameInput);
|
|
543
|
+
const delBtn = document.createElement("button");
|
|
544
|
+
delBtn.title = "Remove signal";
|
|
545
|
+
delBtn.style.cssText = controlStyle + " flex:0 0 22px; width:22px; padding:0; display:flex; align-items:center; justify-content:center;";
|
|
546
|
+
const delIcon = document.createElement("img");
|
|
547
|
+
delIcon.src = assetsPath + "icons/delete.svg";
|
|
548
|
+
delIcon.style.cssText = "width:12px; height:12px;";
|
|
549
|
+
delBtn.appendChild(delIcon);
|
|
550
|
+
delBtn.onclick = () => {
|
|
551
|
+
arr.splice(i, 1);
|
|
552
|
+
renderRows();
|
|
553
|
+
};
|
|
554
|
+
row.appendChild(delBtn);
|
|
555
|
+
rowsContainer.appendChild(row);
|
|
556
|
+
});
|
|
557
|
+
revalidate();
|
|
558
|
+
};
|
|
559
|
+
renderRows();
|
|
560
|
+
const addBtn = document.createElement("button");
|
|
561
|
+
addBtn.textContent = "+ Add signal";
|
|
562
|
+
addBtn.style.cssText = controlStyle + " align-self:flex-start; padding:0 8px;";
|
|
563
|
+
addBtn.onclick = () => {
|
|
564
|
+
arr.push({ source: "signal", name: "" });
|
|
565
|
+
renderRows();
|
|
566
|
+
};
|
|
567
|
+
container.appendChild(addBtn);
|
|
568
|
+
showSourceDescription(arr[0]?.source ?? "signal");
|
|
569
|
+
container.appendChild(descPanel);
|
|
570
|
+
const res = await this.visualizationShell.openConfirmation(container, {
|
|
571
|
+
title: "Edit signals",
|
|
572
|
+
x: 100,
|
|
573
|
+
y: 50,
|
|
574
|
+
width: 500,
|
|
575
|
+
height: 400,
|
|
576
|
+
parent: this,
|
|
577
|
+
});
|
|
578
|
+
if (res) {
|
|
579
|
+
this._propertygrid.setPropertyValue(data.propertyPath, arr);
|
|
580
|
+
this._propertygrid.refresh();
|
|
581
|
+
}
|
|
582
|
+
};
|
|
115
583
|
this._propertygrid.visualizationHandler = this.visualizationHandler;
|
|
116
584
|
this._propertygrid.visualizationShell = this.visualizationShell;
|
|
117
585
|
this._propertygrid.serviceContainer = this.serviceContainer;
|
|
118
586
|
this._propertygrid.instanceServiceContainer = this.instanceServiceContainer;
|
|
119
|
-
this._propertygrid.bindableObjectsTarget =
|
|
120
|
-
this._propertygrid.
|
|
587
|
+
this._propertygrid.bindableObjectsTarget = "script";
|
|
588
|
+
this._propertygrid.setNameColumnWidth(130);
|
|
589
|
+
this._propertygrid.getTypeInfo = (obj, type) => {
|
|
590
|
+
const info = typeInfoFromJsonSchema(this.scriptCommandsTypeInfo, obj, type);
|
|
591
|
+
// trueCommands/elseCommands of "If" are edited as nested tree nodes, not in the property grid
|
|
592
|
+
if (info && (type === "If" || obj?.type === "If")) {
|
|
593
|
+
info.properties = info.properties.filter((p) => p.name !== "trueCommands" && p.name !== "elseCommands");
|
|
594
|
+
}
|
|
595
|
+
return info;
|
|
596
|
+
};
|
|
121
597
|
this._propertygrid.getSpecialEditorForType = async (property, currentValue, propertyPath, wbRender, additionalInfo) => {
|
|
122
598
|
//@ts-ignore
|
|
123
599
|
if (!property.specialAllreadyAdded) {
|
|
124
600
|
//@ts-ignore
|
|
125
601
|
property.specialAllreadyAdded = true;
|
|
126
|
-
if (property.format ===
|
|
602
|
+
if (property.format === "collection") {
|
|
127
603
|
//TODO: create a collection edt. in property grid control used
|
|
128
604
|
}
|
|
129
|
-
else if (
|
|
130
|
-
let rB = document.createElement(
|
|
131
|
-
rB.style.height =
|
|
132
|
-
rB.style.position =
|
|
133
|
-
rB.style.display =
|
|
134
|
-
rB.style.justifyContent =
|
|
135
|
-
rB.style.width =
|
|
136
|
-
rB.style.boxSizing =
|
|
137
|
-
rB.innerText =
|
|
605
|
+
else if (property.format === "signalList") {
|
|
606
|
+
let rB = document.createElement("button");
|
|
607
|
+
rB.style.height = "calc(100% - 6px)";
|
|
608
|
+
rB.style.position = "relative";
|
|
609
|
+
rB.style.display = "flex";
|
|
610
|
+
rB.style.justifyContent = "center";
|
|
611
|
+
rB.style.width = "20px";
|
|
612
|
+
rB.style.boxSizing = "content-box";
|
|
613
|
+
rB.innerText = "del";
|
|
138
614
|
rB.onclick = () => {
|
|
139
615
|
this._propertygrid.setPropertyValue(propertyPath, undefined);
|
|
140
616
|
this._propertygrid.refresh();
|
|
141
617
|
};
|
|
142
|
-
wbRender.nodeElem.insertAdjacentElement(
|
|
143
|
-
|
|
144
|
-
d
|
|
145
|
-
|
|
618
|
+
wbRender.nodeElem.insertAdjacentElement("afterbegin", rB);
|
|
619
|
+
const arr = Array.isArray(currentValue) ? currentValue : [];
|
|
620
|
+
let d = document.createElement("div");
|
|
621
|
+
d.style.display = "flex";
|
|
622
|
+
let sp = document.createElement("span");
|
|
623
|
+
sp.innerText = arr
|
|
624
|
+
.map((v, i) => (v?.varName || "__" + i) + ": " + (v?.source ?? "") + ":" + (v?.name ?? ""))
|
|
625
|
+
.join(", ");
|
|
626
|
+
sp.style.overflow = "hidden";
|
|
627
|
+
sp.style.whiteSpace = "nowrap";
|
|
628
|
+
sp.style.textOverflow = "ellipsis";
|
|
629
|
+
sp.style.flexGrow = "1";
|
|
630
|
+
sp.title = JSON.stringify(arr);
|
|
631
|
+
d.appendChild(sp);
|
|
632
|
+
let b = document.createElement("button");
|
|
633
|
+
b.innerText = "...";
|
|
634
|
+
b.onclick = () => {
|
|
635
|
+
editSignalList({ value: arr, propertyPath });
|
|
636
|
+
};
|
|
637
|
+
d.appendChild(b);
|
|
638
|
+
wbRender.nodeElem.style.display = "flex";
|
|
639
|
+
return d;
|
|
640
|
+
}
|
|
641
|
+
else if (property.format === "formula") {
|
|
642
|
+
const wrapper = document.createElement("div");
|
|
643
|
+
wrapper.style.cssText =
|
|
644
|
+
"display:flex; width:100%; height:100%; align-items:stretch;";
|
|
645
|
+
const editor = new CodeViewMonaco();
|
|
646
|
+
editor.language = "javascript";
|
|
647
|
+
editor.singleRow = true;
|
|
648
|
+
editor.style.cssText =
|
|
649
|
+
"flex:1 1 auto; min-width:0; height:100%; position:relative; overflow:hidden;";
|
|
650
|
+
editor.code = currentValue ?? "";
|
|
651
|
+
editor.addEventListener("code-changed", () => {
|
|
652
|
+
this._propertygrid.setPropertyValue(propertyPath, editor.code);
|
|
653
|
+
});
|
|
654
|
+
wrapper.appendChild(editor);
|
|
655
|
+
const expandBtn = document.createElement("button");
|
|
656
|
+
expandBtn.innerText = "...";
|
|
657
|
+
expandBtn.title = "Open in larger editor";
|
|
658
|
+
expandBtn.style.cssText = "flex:0 0 28px; width:20px;";
|
|
659
|
+
expandBtn.onclick = () => editFormula({ value: editor.code, propertyPath });
|
|
660
|
+
wrapper.appendChild(expandBtn);
|
|
661
|
+
wbRender.nodeElem.style.display = "flex";
|
|
662
|
+
return wrapper;
|
|
663
|
+
}
|
|
664
|
+
else if ((typeof currentValue === "object" && currentValue !== null) ||
|
|
665
|
+
property.format === "complex") {
|
|
666
|
+
let rB = document.createElement("button");
|
|
667
|
+
rB.style.height = "calc(100% - 6px)";
|
|
668
|
+
rB.style.position = "relative";
|
|
669
|
+
rB.style.display = "flex";
|
|
670
|
+
rB.style.justifyContent = "center";
|
|
671
|
+
rB.style.width = "20px";
|
|
672
|
+
rB.style.boxSizing = "content-box";
|
|
673
|
+
rB.innerText = "del";
|
|
674
|
+
rB.onclick = () => {
|
|
675
|
+
this._propertygrid.setPropertyValue(propertyPath, undefined);
|
|
676
|
+
this._propertygrid.refresh();
|
|
677
|
+
};
|
|
678
|
+
wbRender.nodeElem.insertAdjacentElement("afterbegin", rB);
|
|
679
|
+
let d = document.createElement("div");
|
|
680
|
+
d.style.display = "flex";
|
|
681
|
+
let sp = document.createElement("span");
|
|
146
682
|
if (currentValue)
|
|
147
|
-
sp.innerText =
|
|
683
|
+
sp.innerText =
|
|
684
|
+
(currentValue.source ?? "") + ": " + (currentValue.name ?? "");
|
|
148
685
|
else
|
|
149
|
-
sp.innerText =
|
|
150
|
-
sp.style.overflow =
|
|
151
|
-
sp.style.whiteSpace =
|
|
152
|
-
sp.style.textOverflow =
|
|
153
|
-
sp.style.flexGrow =
|
|
686
|
+
sp.innerText = "";
|
|
687
|
+
sp.style.overflow = "hidden";
|
|
688
|
+
sp.style.whiteSpace = "nowrap";
|
|
689
|
+
sp.style.textOverflow = "ellipsis";
|
|
690
|
+
sp.style.flexGrow = "1";
|
|
154
691
|
sp.title = JSON.stringify(currentValue);
|
|
155
692
|
d.appendChild(sp);
|
|
156
|
-
let b = document.createElement(
|
|
157
|
-
b.innerText =
|
|
693
|
+
let b = document.createElement("button");
|
|
694
|
+
b.innerText = "...";
|
|
158
695
|
b.onclick = () => {
|
|
159
696
|
editComplex({ value: currentValue, propertyPath });
|
|
160
697
|
};
|
|
161
698
|
d.appendChild(b);
|
|
162
|
-
wbRender.nodeElem.style.display =
|
|
699
|
+
wbRender.nodeElem.style.display = "flex";
|
|
163
700
|
return d;
|
|
164
701
|
}
|
|
165
702
|
else {
|
|
166
|
-
let b = document.createElement(
|
|
167
|
-
b.style.height =
|
|
168
|
-
b.style.position =
|
|
169
|
-
b.style.display =
|
|
170
|
-
b.style.justifyContent =
|
|
171
|
-
b.style.width =
|
|
172
|
-
b.style.boxSizing =
|
|
173
|
-
b.title =
|
|
174
|
-
b.style.opacity =
|
|
175
|
-
b.innerText =
|
|
703
|
+
let b = document.createElement("button");
|
|
704
|
+
b.style.height = "calc(100% - 6px)";
|
|
705
|
+
b.style.position = "relative";
|
|
706
|
+
b.style.display = "flex";
|
|
707
|
+
b.style.justifyContent = "center";
|
|
708
|
+
b.style.width = "20px";
|
|
709
|
+
b.style.boxSizing = "content-box";
|
|
710
|
+
b.title = "complex property value";
|
|
711
|
+
b.style.opacity = "0.2";
|
|
712
|
+
b.innerText = "...";
|
|
176
713
|
b.onclick = () => {
|
|
177
714
|
editComplex({ value: currentValue, propertyPath });
|
|
178
715
|
};
|
|
179
|
-
wbRender.nodeElem.insertAdjacentElement(
|
|
180
|
-
wbRender.nodeElem.style.display =
|
|
716
|
+
wbRender.nodeElem.insertAdjacentElement("afterbegin", b);
|
|
717
|
+
wbRender.nodeElem.style.display = "flex";
|
|
181
718
|
}
|
|
182
719
|
}
|
|
183
720
|
return null;
|
|
184
721
|
};
|
|
185
722
|
this._propertygrid.propertyNodeContextMenu.on((data) => {
|
|
186
|
-
ContextMenu.show([
|
|
187
|
-
|
|
723
|
+
ContextMenu.show([
|
|
724
|
+
{
|
|
725
|
+
title: "edit complex value",
|
|
188
726
|
action: async () => {
|
|
189
727
|
editComplex(data);
|
|
190
|
-
}
|
|
728
|
+
},
|
|
191
729
|
},
|
|
192
730
|
{
|
|
193
|
-
title:
|
|
731
|
+
title: "edit string",
|
|
194
732
|
action: async () => {
|
|
195
733
|
const v = prompt("enter value:");
|
|
196
734
|
if (v) {
|
|
197
735
|
this._propertygrid.setPropertyValue(data.propertyPath, v);
|
|
198
736
|
this._propertygrid.refresh();
|
|
199
737
|
}
|
|
200
|
-
}
|
|
738
|
+
},
|
|
201
739
|
},
|
|
202
740
|
{
|
|
203
|
-
title:
|
|
741
|
+
title: "remove complex value",
|
|
204
742
|
action: async () => {
|
|
205
743
|
this._propertygrid.setPropertyValue(data.propertyPath, undefined);
|
|
206
744
|
this._propertygrid.refresh();
|
|
207
|
-
}
|
|
208
|
-
}
|
|
745
|
+
},
|
|
746
|
+
},
|
|
747
|
+
], data.event);
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
helpClicked() {
|
|
751
|
+
const iframe = document.createElement("iframe");
|
|
752
|
+
iframe.srcdoc = simpleScriptEditorHelpHtml;
|
|
753
|
+
iframe.style.cssText =
|
|
754
|
+
"width:100%; height:100%; border:none; display:block;";
|
|
755
|
+
this.visualizationShell.openModal(iframe, {
|
|
756
|
+
title: "Simple Script Editor Help",
|
|
757
|
+
parent: this,
|
|
758
|
+
x: -650,
|
|
759
|
+
y: -100,
|
|
760
|
+
width: 620,
|
|
761
|
+
height: 620,
|
|
209
762
|
});
|
|
763
|
+
this.helpRequested.emit();
|
|
764
|
+
}
|
|
765
|
+
toggleCodeView() {
|
|
766
|
+
if (!this._showingCode) {
|
|
767
|
+
const json = JSON.stringify({ commands: this.getScriptCommands() }, null, 2);
|
|
768
|
+
this._codeView.update(json);
|
|
769
|
+
this._showCodeView(true);
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
const parsed = this._parseCodeViewScript();
|
|
773
|
+
if (!parsed)
|
|
774
|
+
return;
|
|
775
|
+
this._showCodeView(false);
|
|
776
|
+
this.loadScript(parsed);
|
|
777
|
+
}
|
|
778
|
+
_showCodeView(show) {
|
|
779
|
+
this._codeView.classList.toggle("visible", show);
|
|
780
|
+
this._commandListDiv.classList.toggle("hidden", show);
|
|
781
|
+
this._splitView.classList.toggle("code-mode", show);
|
|
782
|
+
this._addCommandBtn.disabled = show;
|
|
783
|
+
this._codeToggleBtn.innerText = show ? "Visual" : "Code";
|
|
784
|
+
this._showingCode = show;
|
|
785
|
+
if (show)
|
|
786
|
+
requestAnimationFrame(() => this._codeView.activated());
|
|
210
787
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
let
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
continue;
|
|
217
|
-
let option = document.createElement('option');
|
|
218
|
-
option.innerText = c;
|
|
219
|
-
this._possibleCommands.add(option);
|
|
788
|
+
/** Parses+validates the code view's JSON. Returns null (and alerts) on invalid JSON. */
|
|
789
|
+
_parseCodeViewScript() {
|
|
790
|
+
let parsed;
|
|
791
|
+
try {
|
|
792
|
+
parsed = JSON.parse(this._codeView.getText());
|
|
220
793
|
}
|
|
794
|
+
catch (e) {
|
|
795
|
+
alert("Invalid JSON:\n" + e.message);
|
|
796
|
+
return null;
|
|
797
|
+
}
|
|
798
|
+
if (!Array.isArray(parsed?.commands)) {
|
|
799
|
+
alert('The JSON must have a "commands" array.');
|
|
800
|
+
return null;
|
|
801
|
+
}
|
|
802
|
+
return parsed;
|
|
221
803
|
}
|
|
222
804
|
loadScript(script) {
|
|
223
805
|
this._script = script;
|
|
806
|
+
if (this._commandListFancyTree) {
|
|
807
|
+
this._commandListFancyTree.destroy();
|
|
808
|
+
this._commandListDiv = this._getDomElement("commandList");
|
|
809
|
+
}
|
|
224
810
|
let commandListTreeItems = [];
|
|
225
811
|
for (let c of this._script.commands) {
|
|
226
812
|
c = ScriptUpgrades.upgradeScriptCommand(c);
|
|
227
813
|
commandListTreeItems.push(this.createTreeItem(c));
|
|
228
814
|
}
|
|
229
|
-
;
|
|
230
815
|
this._commandListFancyTree = new Wunderbaum({
|
|
231
816
|
...defaultOptions,
|
|
232
817
|
element: this._commandListDiv,
|
|
233
818
|
icon: false,
|
|
234
819
|
source: commandListTreeItems,
|
|
235
820
|
activate: (e) => {
|
|
236
|
-
this._propertygrid.selectedObject = e.node.data.data.item;
|
|
821
|
+
this._propertygrid.selectedObject = e.node.data.data.item ?? null;
|
|
237
822
|
},
|
|
238
823
|
render: (e) => {
|
|
824
|
+
const isBranch = !!e.node.data.data?.branch;
|
|
239
825
|
if (e.isNew) {
|
|
240
|
-
|
|
241
|
-
span.oncontextmenu = (ev) => {
|
|
242
|
-
e.node.setActive();
|
|
243
|
-
if (e.node.data.contextMenu) {
|
|
244
|
-
e.node.data.contextMenu(ev, e.node.data, e.node);
|
|
245
|
-
}
|
|
826
|
+
e.nodeElem.oncontextmenu = (ev) => {
|
|
246
827
|
ev.preventDefault();
|
|
247
828
|
return false;
|
|
248
829
|
};
|
|
830
|
+
const leadingSpacer = document.createElement("span");
|
|
831
|
+
leadingSpacer.style.cssText = "display:inline-block; width:24px;";
|
|
832
|
+
e.nodeElem.prepend(leadingSpacer);
|
|
833
|
+
if (!isBranch) {
|
|
834
|
+
const handle = document.createElement("div");
|
|
835
|
+
handle.className = "drag-handle";
|
|
836
|
+
handle.title = "Drag to reorder";
|
|
837
|
+
handle.innerHTML =
|
|
838
|
+
'<svg viewBox="0 0 24 24" width="14" height="14"><path d="M4 6h16M4 12h16M4 18h16" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>';
|
|
839
|
+
e.nodeElem.prepend(handle);
|
|
840
|
+
const rowElem = e.nodeElem.parentElement;
|
|
841
|
+
const spacer = document.createElement("span");
|
|
842
|
+
spacer.style.cssText = "display:inline-block; width:32px;";
|
|
843
|
+
e.nodeElem.appendChild(spacer);
|
|
844
|
+
const cmd = document.createElement("div");
|
|
845
|
+
cmd.className = "cmd";
|
|
846
|
+
const copyImg = document.createElement("img");
|
|
847
|
+
copyImg.src = assetsPath + "icons/copy.svg";
|
|
848
|
+
copyImg.title = "Duplicate";
|
|
849
|
+
copyImg.onclick = () => {
|
|
850
|
+
const clone = structuredClone(e.node.data.data.item);
|
|
851
|
+
const newNode = e.node.parent.addChildren(this.createTreeItem(clone));
|
|
852
|
+
newNode.moveTo(e.node, "after");
|
|
853
|
+
};
|
|
854
|
+
const delImg = document.createElement("img");
|
|
855
|
+
delImg.src = assetsPath + "icons/delete.svg";
|
|
856
|
+
delImg.title = "Remove";
|
|
857
|
+
delImg.onclick = () => e.node.remove();
|
|
858
|
+
cmd.append(copyImg, delImg);
|
|
859
|
+
rowElem.appendChild(cmd);
|
|
860
|
+
//@ts-ignore
|
|
861
|
+
e.nodeElem._reservedRowWidth =
|
|
862
|
+
leadingSpacer.offsetWidth + spacer.offsetWidth;
|
|
863
|
+
}
|
|
864
|
+
else {
|
|
865
|
+
//@ts-ignore
|
|
866
|
+
e.nodeElem._reservedRowWidth = leadingSpacer.offsetWidth;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
//@ts-ignore
|
|
870
|
+
const reserved = e.nodeElem._reservedRowWidth ?? 0;
|
|
871
|
+
if (reserved) {
|
|
872
|
+
const titleSpan = e.nodeElem.querySelector("span.wb-title");
|
|
873
|
+
if (titleSpan) {
|
|
874
|
+
const w = parseFloat(titleSpan.style.width);
|
|
875
|
+
if (!isNaN(w))
|
|
876
|
+
titleSpan.style.width = Math.max(0, w - reserved) + "px";
|
|
877
|
+
}
|
|
249
878
|
}
|
|
250
879
|
},
|
|
251
880
|
dnd: {
|
|
@@ -254,42 +883,147 @@ export class SimpleScriptEditor extends BaseCustomWebComponentConstructorAppend
|
|
|
254
883
|
preventVoidMoves: false,
|
|
255
884
|
serializeClipboardData: false,
|
|
256
885
|
dragStart: (e) => {
|
|
886
|
+
if (!this._dragFromHandle)
|
|
887
|
+
return false;
|
|
888
|
+
this._draggedNode = e.node;
|
|
257
889
|
e.event.dataTransfer.effectAllowed = "move";
|
|
258
890
|
e.event.dataTransfer.dropEffect = "move";
|
|
891
|
+
e.event.dataTransfer.setData(SAME_TREE_MOVE_MIME, "1");
|
|
259
892
|
return true;
|
|
260
893
|
},
|
|
894
|
+
dragEnd: () => {
|
|
895
|
+
this._draggedNode = null;
|
|
896
|
+
},
|
|
261
897
|
dragEnter: (e) => {
|
|
262
|
-
e.event.dataTransfer.
|
|
263
|
-
|
|
898
|
+
const isMove = e.event.dataTransfer?.types.includes(SAME_TREE_MOVE_MIME);
|
|
899
|
+
e.event.dataTransfer.dropEffect = isMove ? "move" : "copy";
|
|
900
|
+
const isBranchTarget = !!e.node.data.data?.branch;
|
|
901
|
+
return isBranchTarget ? new Set(["over"]) : new Set(["before", "after"]);
|
|
264
902
|
},
|
|
265
903
|
dragOver: (e) => {
|
|
266
|
-
e.event.dataTransfer.
|
|
904
|
+
const isMove = e.event.dataTransfer?.types.includes(SAME_TREE_MOVE_MIME);
|
|
905
|
+
e.event.dataTransfer.dropEffect = isMove ? "move" : "copy";
|
|
267
906
|
},
|
|
268
907
|
drop: async (e) => {
|
|
269
|
-
e.
|
|
270
|
-
|
|
271
|
-
|
|
908
|
+
const isMove = e.event.dataTransfer?.types.includes(SAME_TREE_MOVE_MIME);
|
|
909
|
+
if (e.region === "over") {
|
|
910
|
+
if (isMove && this._draggedNode) {
|
|
911
|
+
this._draggedNode.moveTo(e.node, "appendChild");
|
|
912
|
+
this._draggedNode.setClass("wb-drag-source", false);
|
|
913
|
+
}
|
|
914
|
+
else {
|
|
915
|
+
const type = e.event.dataTransfer?.getData("text/plain");
|
|
916
|
+
if (type) {
|
|
917
|
+
e.node.addChildren(this.createTreeItem({ type }));
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
const region = e.region == "before" ? "before" : "after";
|
|
923
|
+
if (isMove && this._draggedNode) {
|
|
924
|
+
this._draggedNode.moveTo(e.node, region);
|
|
925
|
+
this._draggedNode.setClass("wb-drag-source", false);
|
|
926
|
+
}
|
|
927
|
+
else {
|
|
928
|
+
const type = e.event.dataTransfer?.getData("text/plain");
|
|
929
|
+
if (type) {
|
|
930
|
+
const command = { type };
|
|
931
|
+
const ti = this.createTreeItem(command);
|
|
932
|
+
const newNode = e.node.parent.addChildren(ti);
|
|
933
|
+
newNode.moveTo(e.node, region);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
},
|
|
937
|
+
},
|
|
272
938
|
});
|
|
939
|
+
this._commandListFancyTree.root.children?.[0]?.setActive();
|
|
273
940
|
}
|
|
274
941
|
createTreeItem(currentItem) {
|
|
275
|
-
|
|
942
|
+
if (currentItem.type === "If") {
|
|
943
|
+
const ifCommand = currentItem;
|
|
944
|
+
ifCommand.trueCommands ??= [];
|
|
945
|
+
ifCommand.elseCommands ??= [];
|
|
946
|
+
return {
|
|
947
|
+
title: "If",
|
|
948
|
+
expanded: true,
|
|
949
|
+
data: { item: currentItem },
|
|
950
|
+
children: [
|
|
951
|
+
{
|
|
952
|
+
title: "true",
|
|
953
|
+
expanded: true,
|
|
954
|
+
data: { branch: "true" },
|
|
955
|
+
children: ifCommand.trueCommands.map((c) => this.createTreeItem(c)),
|
|
956
|
+
},
|
|
957
|
+
{
|
|
958
|
+
title: "else",
|
|
959
|
+
expanded: true,
|
|
960
|
+
data: { branch: "else" },
|
|
961
|
+
children: ifCommand.elseCommands.map((c) => this.createTreeItem(c)),
|
|
962
|
+
},
|
|
963
|
+
],
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
return {
|
|
276
967
|
title: currentItem.type,
|
|
277
968
|
data: { item: currentItem },
|
|
278
|
-
contextMenu: (e, data, node) => {
|
|
279
|
-
ContextMenu.show([{ title: 'Remove Item', action: (e) => node.remove() }], e);
|
|
280
|
-
}
|
|
281
969
|
};
|
|
282
|
-
return cti;
|
|
283
970
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
971
|
+
/** Reconstructs a ScriptCommand (and, for "If", its true/else branches) from a tree node. */
|
|
972
|
+
nodeToCommand(node) {
|
|
973
|
+
const item = node.data.data.item;
|
|
974
|
+
if (item.type === "If") {
|
|
975
|
+
const trueNode = node.children?.find((c) => c.data.data?.branch === "true");
|
|
976
|
+
const elseNode = node.children?.find((c) => c.data.data?.branch === "else");
|
|
977
|
+
item.trueCommands = (trueNode?.children ?? []).map((c) => this.nodeToCommand(c));
|
|
978
|
+
item.elseCommands = (elseNode?.children ?? []).map((c) => this.nodeToCommand(c));
|
|
979
|
+
}
|
|
980
|
+
return item;
|
|
981
|
+
}
|
|
982
|
+
/** Walks up from `node` to the nearest branch ("true"/"else") container node, if any. */
|
|
983
|
+
_getBranchContainerNode(node) {
|
|
984
|
+
let n = node;
|
|
985
|
+
while (n) {
|
|
986
|
+
if (n.data?.data?.branch)
|
|
987
|
+
return n;
|
|
988
|
+
n = n.parent;
|
|
989
|
+
}
|
|
990
|
+
return null;
|
|
991
|
+
}
|
|
992
|
+
async addCommand() {
|
|
993
|
+
const picker = new SimpleScriptCommandPicker();
|
|
994
|
+
picker.loadCommands(this.scriptCommandsTypeInfo, this.commandDescriptions);
|
|
995
|
+
picker.title = "Add Script Command";
|
|
996
|
+
picker.commandTypeChosen.on(() => {
|
|
997
|
+
if (picker.selectedType) {
|
|
998
|
+
const command = { type: picker.selectedType };
|
|
999
|
+
const ti = this.createTreeItem(command);
|
|
1000
|
+
const branchContainer = this._getBranchContainerNode(this._commandListFancyTree.activeNode);
|
|
1001
|
+
if (branchContainer)
|
|
1002
|
+
branchContainer.addChildren(ti);
|
|
1003
|
+
else
|
|
1004
|
+
this._commandListFancyTree.addChildren(ti);
|
|
1005
|
+
}
|
|
1006
|
+
});
|
|
1007
|
+
const abortController = new AbortController();
|
|
1008
|
+
picker.closeRequested.on(() => abortController.abort());
|
|
1009
|
+
await this.visualizationShell.openModal(picker, {
|
|
1010
|
+
title: "Add Script Command",
|
|
1011
|
+
x: 300,
|
|
1012
|
+
y: 50,
|
|
1013
|
+
width: 620,
|
|
1014
|
+
height: 400,
|
|
1015
|
+
parent: this,
|
|
1016
|
+
abortSignal: abortController.signal,
|
|
1017
|
+
});
|
|
289
1018
|
}
|
|
290
1019
|
getScriptCommands() {
|
|
1020
|
+
if (this._showingCode) {
|
|
1021
|
+
const parsed = this._parseCodeViewScript();
|
|
1022
|
+
if (parsed)
|
|
1023
|
+
this.loadScript(parsed);
|
|
1024
|
+
}
|
|
291
1025
|
let children = this._commandListFancyTree.root.children;
|
|
292
|
-
return children.map(x => x
|
|
1026
|
+
return children.map((x) => this.nodeToCommand(x));
|
|
293
1027
|
}
|
|
294
1028
|
}
|
|
295
1029
|
customElements.define(SimpleScriptEditor.is, SimpleScriptEditor);
|