@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.
@@ -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(): any[];
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 i.wb-expander,
82
- #commandList i.wb-indent {
82
+ #commandList.hidden {
83
83
  display: none;
84
84
  }
85
85
 
86
- #commandList.hidden {
87
- display: none;
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.getTypeInfo = (obj, type) => typeInfoFromJsonSchema(this.scriptCommandsTypeInfo, obj, type);
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, handle);
569
- const rowElem = e.nodeElem.parentElement;
570
- const spacer = document.createElement("span");
571
- spacer.style.cssText = "display:inline-block; width:32px;";
572
- e.nodeElem.appendChild(spacer);
573
- const cmd = document.createElement("div");
574
- cmd.className = "cmd";
575
- const copyImg = document.createElement("img");
576
- copyImg.src = assetsPath + "icons/copy.svg";
577
- copyImg.title = "Duplicate";
578
- copyImg.onclick = () => {
579
- const clone = structuredClone(e.node.data.data.item);
580
- const newNode = e.node.parent.addChildren(this.createTreeItem(clone));
581
- newNode.moveTo(e.node, "after");
582
- };
583
- const delImg = document.createElement("img");
584
- delImg.src = assetsPath + "icons/delete.svg";
585
- delImg.title = "Remove";
586
- delImg.onclick = () => e.node.remove();
587
- cmd.append(copyImg, delImg);
588
- rowElem.appendChild(cmd);
589
- //@ts-ignore
590
- e.nodeElem._reservedRowWidth =
591
- leadingSpacer.offsetWidth + spacer.offsetWidth;
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
- return true;
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
- let cti = {
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
- return cti;
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.addChildren(ti);
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.data.data.item);
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);