@node-projects/web-component-designer-visualization-addons 0.1.146 → 0.1.148
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/SimpleScriptEditor.d.ts +6 -1
- package/dist/components/SimpleScriptEditor.js +380 -42
- package/dist/components/VisualizationPropertyGrid.d.ts +2 -0
- package/dist/components/VisualizationPropertyGrid.js +8 -0
- package/dist/index-min.js +54 -46
- package/dist/scripting/ScriptCommands.d.ts +34 -1
- package/dist/scripting/ScriptCommandsDescriptions.js +5 -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 +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BaseCustomWebComponentConstructorAppend, TypedEvent } from "@node-projects/base-custom-webcomponent";
|
|
2
2
|
import { Script } from "../scripting/Script.js";
|
|
3
|
+
import { ScriptCommands } from "../scripting/ScriptCommands.js";
|
|
3
4
|
import { InstanceServiceContainer, ServiceContainer } from "@node-projects/web-component-designer";
|
|
4
5
|
import { ScriptCommandHelp } from "../scripting/ScriptCommandsDescriptions.js";
|
|
5
6
|
import { VisualizationHandler } from "../interfaces/VisualizationHandler.js";
|
|
@@ -40,6 +41,10 @@ export declare class SimpleScriptEditor extends BaseCustomWebComponentConstructo
|
|
|
40
41
|
private _parseCodeViewScript;
|
|
41
42
|
loadScript(script: Script): void;
|
|
42
43
|
private createTreeItem;
|
|
44
|
+
/** Reconstructs a ScriptCommand (and, for "If", its true/else branches) from a tree node. */
|
|
45
|
+
private nodeToCommand;
|
|
46
|
+
/** Walks up from `node` to the nearest branch ("true"/"else") container node, if any. */
|
|
47
|
+
private _getBranchContainerNode;
|
|
43
48
|
addCommand(): Promise<void>;
|
|
44
|
-
getScriptCommands():
|
|
49
|
+
getScriptCommands(): ScriptCommands[];
|
|
45
50
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { BaseCustomWebComponentConstructorAppend, TypedEvent, css, html, } from "@node-projects/base-custom-webcomponent";
|
|
2
2
|
import { ContextMenu, assetsPath, } from "@node-projects/web-component-designer";
|
|
3
3
|
import { typeInfoFromJsonSchema, } from "@node-projects/propertygrid.webcomponent";
|
|
4
|
-
import { defaultOptions } from "@node-projects/web-component-designer-widgets-wunderbaum";
|
|
4
|
+
import { defaultOptions, defaultStyle } from "@node-projects/web-component-designer-widgets-wunderbaum";
|
|
5
5
|
import { Wunderbaum } from "wunderbaum";
|
|
6
6
|
//@ts-ignore
|
|
7
7
|
import wunderbaumStyle from "wunderbaum/dist/wunderbaum.css" with { type: "css" };
|
|
8
8
|
import { VisualizationPropertyGrid } from "./VisualizationPropertyGrid.js";
|
|
9
|
+
import { CodeViewMonaco } from "@node-projects/web-component-designer-codeview-monaco";
|
|
9
10
|
import { SimpleScriptCommandPicker } from "./SimpleScriptCommandPicker.js";
|
|
10
11
|
import { simpleScriptEditorHelpHtml } from "./SimpleScriptEditorHelp.js";
|
|
11
12
|
import { nativeScriptCommandDescriptions, } from "../scripting/ScriptCommandsDescriptions.js";
|
|
@@ -78,13 +79,19 @@ export class SimpleScriptEditor extends BaseCustomWebComponentConstructorAppend
|
|
|
78
79
|
box-sizing: border-box;
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
#commandList
|
|
82
|
-
#commandList i.wb-indent {
|
|
82
|
+
#commandList.hidden {
|
|
83
83
|
display: none;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
#commandList
|
|
87
|
-
|
|
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;
|
|
88
95
|
}
|
|
89
96
|
|
|
90
97
|
.code-view {
|
|
@@ -332,6 +339,7 @@ export class SimpleScriptEditor extends BaseCustomWebComponentConstructorAppend
|
|
|
332
339
|
});
|
|
333
340
|
this.shadowRoot.adoptedStyleSheets = [
|
|
334
341
|
wunderbaumStyle,
|
|
342
|
+
defaultStyle,
|
|
335
343
|
SimpleScriptEditor.style,
|
|
336
344
|
];
|
|
337
345
|
}
|
|
@@ -379,12 +387,213 @@ export class SimpleScriptEditor extends BaseCustomWebComponentConstructorAppend
|
|
|
379
387
|
this._propertygrid.refresh();
|
|
380
388
|
}
|
|
381
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
|
+
};
|
|
382
583
|
this._propertygrid.visualizationHandler = this.visualizationHandler;
|
|
383
584
|
this._propertygrid.visualizationShell = this.visualizationShell;
|
|
384
585
|
this._propertygrid.serviceContainer = this.serviceContainer;
|
|
385
586
|
this._propertygrid.instanceServiceContainer = this.instanceServiceContainer;
|
|
386
587
|
this._propertygrid.bindableObjectsTarget = "script";
|
|
387
|
-
this._propertygrid.
|
|
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
|
+
};
|
|
388
597
|
this._propertygrid.getSpecialEditorForType = async (property, currentValue, propertyPath, wbRender, additionalInfo) => {
|
|
389
598
|
//@ts-ignore
|
|
390
599
|
if (!property.specialAllreadyAdded) {
|
|
@@ -393,6 +602,65 @@ export class SimpleScriptEditor extends BaseCustomWebComponentConstructorAppend
|
|
|
393
602
|
if (property.format === "collection") {
|
|
394
603
|
//TODO: create a collection edt. in property grid control used
|
|
395
604
|
}
|
|
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";
|
|
614
|
+
rB.onclick = () => {
|
|
615
|
+
this._propertygrid.setPropertyValue(propertyPath, undefined);
|
|
616
|
+
this._propertygrid.refresh();
|
|
617
|
+
};
|
|
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
|
+
}
|
|
396
664
|
else if ((typeof currentValue === "object" && currentValue !== null) ||
|
|
397
665
|
property.format === "complex") {
|
|
398
666
|
let rB = document.createElement("button");
|
|
@@ -550,45 +818,53 @@ export class SimpleScriptEditor extends BaseCustomWebComponentConstructorAppend
|
|
|
550
818
|
icon: false,
|
|
551
819
|
source: commandListTreeItems,
|
|
552
820
|
activate: (e) => {
|
|
553
|
-
this._propertygrid.selectedObject = e.node.data.data.item;
|
|
821
|
+
this._propertygrid.selectedObject = e.node.data.data.item ?? null;
|
|
554
822
|
},
|
|
555
823
|
render: (e) => {
|
|
824
|
+
const isBranch = !!e.node.data.data?.branch;
|
|
556
825
|
if (e.isNew) {
|
|
557
826
|
e.nodeElem.oncontextmenu = (ev) => {
|
|
558
827
|
ev.preventDefault();
|
|
559
828
|
return false;
|
|
560
829
|
};
|
|
561
|
-
const handle = document.createElement("div");
|
|
562
|
-
handle.className = "drag-handle";
|
|
563
|
-
handle.title = "Drag to reorder";
|
|
564
|
-
handle.innerHTML =
|
|
565
|
-
'<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>';
|
|
566
830
|
const leadingSpacer = document.createElement("span");
|
|
567
831
|
leadingSpacer.style.cssText = "display:inline-block; width:24px;";
|
|
568
|
-
e.nodeElem.prepend(leadingSpacer
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
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
|
+
}
|
|
592
868
|
}
|
|
593
869
|
//@ts-ignore
|
|
594
870
|
const reserved = e.nodeElem._reservedRowWidth ?? 0;
|
|
@@ -621,15 +897,29 @@ export class SimpleScriptEditor extends BaseCustomWebComponentConstructorAppend
|
|
|
621
897
|
dragEnter: (e) => {
|
|
622
898
|
const isMove = e.event.dataTransfer?.types.includes(SAME_TREE_MOVE_MIME);
|
|
623
899
|
e.event.dataTransfer.dropEffect = isMove ? "move" : "copy";
|
|
624
|
-
|
|
900
|
+
const isBranchTarget = !!e.node.data.data?.branch;
|
|
901
|
+
return isBranchTarget ? new Set(["over"]) : new Set(["before", "after"]);
|
|
625
902
|
},
|
|
626
903
|
dragOver: (e) => {
|
|
627
904
|
const isMove = e.event.dataTransfer?.types.includes(SAME_TREE_MOVE_MIME);
|
|
628
905
|
e.event.dataTransfer.dropEffect = isMove ? "move" : "copy";
|
|
629
906
|
},
|
|
630
907
|
drop: async (e) => {
|
|
631
|
-
const region = e.region == "before" ? "before" : "after";
|
|
632
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";
|
|
633
923
|
if (isMove && this._draggedNode) {
|
|
634
924
|
this._draggedNode.moveTo(e.node, region);
|
|
635
925
|
this._draggedNode.setClass("wb-drag-source", false);
|
|
@@ -649,11 +939,55 @@ export class SimpleScriptEditor extends BaseCustomWebComponentConstructorAppend
|
|
|
649
939
|
this._commandListFancyTree.root.children?.[0]?.setActive();
|
|
650
940
|
}
|
|
651
941
|
createTreeItem(currentItem) {
|
|
652
|
-
|
|
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 {
|
|
653
967
|
title: currentItem.type,
|
|
654
968
|
data: { item: currentItem },
|
|
655
969
|
};
|
|
656
|
-
|
|
970
|
+
}
|
|
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;
|
|
657
991
|
}
|
|
658
992
|
async addCommand() {
|
|
659
993
|
const picker = new SimpleScriptCommandPicker();
|
|
@@ -663,7 +997,11 @@ export class SimpleScriptEditor extends BaseCustomWebComponentConstructorAppend
|
|
|
663
997
|
if (picker.selectedType) {
|
|
664
998
|
const command = { type: picker.selectedType };
|
|
665
999
|
const ti = this.createTreeItem(command);
|
|
666
|
-
this._commandListFancyTree.
|
|
1000
|
+
const branchContainer = this._getBranchContainerNode(this._commandListFancyTree.activeNode);
|
|
1001
|
+
if (branchContainer)
|
|
1002
|
+
branchContainer.addChildren(ti);
|
|
1003
|
+
else
|
|
1004
|
+
this._commandListFancyTree.addChildren(ti);
|
|
667
1005
|
}
|
|
668
1006
|
});
|
|
669
1007
|
const abortController = new AbortController();
|
|
@@ -685,7 +1023,7 @@ export class SimpleScriptEditor extends BaseCustomWebComponentConstructorAppend
|
|
|
685
1023
|
this.loadScript(parsed);
|
|
686
1024
|
}
|
|
687
1025
|
let children = this._commandListFancyTree.root.children;
|
|
688
|
-
return children.map((x) => x
|
|
1026
|
+
return children.map((x) => this.nodeToCommand(x));
|
|
689
1027
|
}
|
|
690
1028
|
}
|
|
691
1029
|
customElements.define(SimpleScriptEditor.is, SimpleScriptEditor);
|
|
@@ -11,5 +11,7 @@ export declare class VisualizationPropertyGrid extends PropertyGrid {
|
|
|
11
11
|
bindableObjectsTarget: BindableObjectsTarget;
|
|
12
12
|
constructor();
|
|
13
13
|
bindingDoubleClicked: (bindableObject: IBindableObject<any>) => void;
|
|
14
|
+
/** Sets the name/label column to a fixed pixel width instead of the default 50/50 split. */
|
|
15
|
+
setNameColumnWidth(px: number): void;
|
|
14
16
|
getEditorForType(property: IProperty, currentValue: any, propertyPath: string, wbRender: WbRenderEventType, additionalInfo?: any): Promise<HTMLElement>;
|
|
15
17
|
}
|
|
@@ -10,6 +10,14 @@ export class VisualizationPropertyGrid extends PropertyGrid {
|
|
|
10
10
|
super();
|
|
11
11
|
}
|
|
12
12
|
bindingDoubleClicked;
|
|
13
|
+
/** Sets the name/label column to a fixed pixel width instead of the default 50/50 split. */
|
|
14
|
+
setNameColumnWidth(px) {
|
|
15
|
+
const col = this._tree?.columns?.[0];
|
|
16
|
+
if (col) {
|
|
17
|
+
col.width = px + 'px';
|
|
18
|
+
this._tree.update('colStructure');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
13
21
|
async getEditorForType(property, currentValue, propertyPath, wbRender, additionalInfo) {
|
|
14
22
|
if (this.getSpecialEditorForType) {
|
|
15
23
|
let edt = await this.getSpecialEditorForType(property, currentValue, propertyPath, wbRender, additionalInfo);
|