@elyra/canvas 12.28.2 → 12.30.0

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.
Files changed (88) hide show
  1. package/dist/canvas-controller-6fe261d9.js +2 -0
  2. package/dist/canvas-controller-6fe261d9.js.map +1 -0
  3. package/dist/canvas-controller-ea7d4a8f.js +2 -0
  4. package/dist/canvas-controller-ea7d4a8f.js.map +1 -0
  5. package/dist/common-canvas-2953ff65.js +2 -0
  6. package/dist/common-canvas-2953ff65.js.map +1 -0
  7. package/dist/common-canvas-fbd62592.js +2 -0
  8. package/dist/common-canvas-fbd62592.js.map +1 -0
  9. package/dist/common-canvas.es.js +1 -1
  10. package/dist/common-canvas.js +1 -1
  11. package/dist/common-properties-0df4ed36.js +2 -0
  12. package/dist/common-properties-0df4ed36.js.map +1 -0
  13. package/dist/common-properties-8409565f.js +2 -0
  14. package/dist/common-properties-8409565f.js.map +1 -0
  15. package/dist/flexible-table-3a78cdf3.js +2 -0
  16. package/dist/{flexible-table-a3180c2b.js.map → flexible-table-3a78cdf3.js.map} +1 -1
  17. package/dist/flexible-table-6e801de4.js +2 -0
  18. package/dist/{flexible-table-25f1f3a4.js.map → flexible-table-6e801de4.js.map} +1 -1
  19. package/dist/index-92422c18.js +2 -0
  20. package/dist/index-92422c18.js.map +1 -0
  21. package/dist/index-aee893ad.js +2 -0
  22. package/dist/index-aee893ad.js.map +1 -0
  23. package/dist/lib/canvas-controller.es.js +1 -1
  24. package/dist/lib/canvas-controller.js +1 -1
  25. package/dist/lib/canvas.es.js +1 -1
  26. package/dist/lib/canvas.js +1 -1
  27. package/dist/lib/properties/field-picker.es.js +1 -1
  28. package/dist/lib/properties/field-picker.js +1 -1
  29. package/dist/lib/properties/flexible-table.es.js +1 -1
  30. package/dist/lib/properties/flexible-table.js +1 -1
  31. package/dist/lib/properties.es.js +1 -1
  32. package/dist/lib/properties.js +1 -1
  33. package/dist/lib/tooltip.es.js +1 -1
  34. package/dist/lib/tooltip.es.js.map +1 -1
  35. package/dist/lib/tooltip.js +1 -1
  36. package/dist/lib/tooltip.js.map +1 -1
  37. package/dist/styles/common-canvas.min.css +1 -1
  38. package/dist/styles/common-canvas.min.css.map +1 -1
  39. package/dist/{toolbar-2bbc9542.js → toolbar-3fdd090b.js} +1 -1
  40. package/dist/{toolbar-2bbc9542.js.map → toolbar-3fdd090b.js.map} +1 -1
  41. package/dist/{toolbar-c173e22a.js → toolbar-5437484a.js} +1 -1
  42. package/dist/{toolbar-c173e22a.js.map → toolbar-5437484a.js.map} +1 -1
  43. package/locales/common-properties/locales/en.json +1 -1
  44. package/locales/common-properties/locales/eo.json +1 -1
  45. package/package.json +2 -2
  46. package/src/common-canvas/canvas-controller.js +19 -15
  47. package/src/common-canvas/svg-canvas-d3.scss +5 -0
  48. package/src/common-canvas/svg-canvas-pipeline.js +12 -6
  49. package/src/common-canvas/svg-canvas-renderer.js +156 -103
  50. package/src/common-canvas/svg-canvas-utils-external.js +34 -0
  51. package/src/common-properties/actions/button/button.jsx +2 -1
  52. package/src/common-properties/actions/image/image.jsx +2 -1
  53. package/src/common-properties/components/title-editor/title-editor.jsx +3 -0
  54. package/src/common-properties/components/virtualized-table/virtualized-table.scss +2 -4
  55. package/src/common-properties/controls/datepicker/datepicker.scss +9 -0
  56. package/src/common-properties/controls/passwordfield/passwordfield.jsx +16 -18
  57. package/src/common-properties/form/ActionInfo.js +3 -1
  58. package/src/common-properties/form/EditorForm.js +1 -1
  59. package/src/common-properties/form/PropertyDef.js +1 -1
  60. package/src/common-properties/panels/subtabs/subtabs.jsx +4 -2
  61. package/src/common-properties/panels/tearsheet/tearsheet.jsx +21 -16
  62. package/src/common-properties/panels/tearsheet/tearsheet.scss +1 -1
  63. package/src/common-properties/ui-conditions/ui-conditions.js +6 -1
  64. package/src/object-model/layout-dimensions.js +20 -2
  65. package/src/object-model/object-model.js +1 -1
  66. package/src/object-model/pipeline-out-handler.js +1 -1
  67. package/src/palette/palette-content-list-item.jsx +3 -2
  68. package/src/palette/palette-dialog-content-grid-node.jsx +3 -2
  69. package/src/tooltip/tooltip.jsx +1 -1
  70. package/stats.html +1 -1
  71. package/dist/canvas-controller-1bbd9c0e.js +0 -2
  72. package/dist/canvas-controller-1bbd9c0e.js.map +0 -1
  73. package/dist/canvas-controller-d6aa7d4d.js +0 -2
  74. package/dist/canvas-controller-d6aa7d4d.js.map +0 -1
  75. package/dist/common-canvas-9c735f47.js +0 -2
  76. package/dist/common-canvas-9c735f47.js.map +0 -1
  77. package/dist/common-canvas-a02e75c1.js +0 -2
  78. package/dist/common-canvas-a02e75c1.js.map +0 -1
  79. package/dist/common-properties-21c4c338.js +0 -2
  80. package/dist/common-properties-21c4c338.js.map +0 -1
  81. package/dist/common-properties-80e20c2a.js +0 -2
  82. package/dist/common-properties-80e20c2a.js.map +0 -1
  83. package/dist/flexible-table-25f1f3a4.js +0 -2
  84. package/dist/flexible-table-a3180c2b.js +0 -2
  85. package/dist/index-501de495.js +0 -2
  86. package/dist/index-501de495.js.map +0 -1
  87. package/dist/index-8c2f3663.js +0 -2
  88. package/dist/index-8c2f3663.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2017-2022 Elyra Authors
2
+ * Copyright 2017-2023 Elyra Authors
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@ const markdownIt = require("markdown-it")({
33
33
  });
34
34
 
35
35
  import { cloneDeep, escape as escapeText, forOwn, get } from "lodash";
36
+ import { addNodeExternalObject, addDecExternalObject, removeExternalObject } from "./svg-canvas-utils-external.js";
36
37
  import { ASSOC_RIGHT_SIDE_CURVE, ASSOCIATION_LINK, NODE_LINK, COMMENT_LINK,
37
38
  ASSOC_VAR_CURVE_LEFT, ASSOC_VAR_CURVE_RIGHT, ASSOC_VAR_DOUBLE_BACK_RIGHT,
38
39
  LINK_TYPE_CURVE, LINK_TYPE_ELBOW, LINK_TYPE_STRAIGHT,
@@ -417,7 +418,9 @@ export default class SVGCanvasRenderer {
417
418
 
418
419
  if (this.config.enablePositionNodeOnRightFlyoutOpen &&
419
420
  this.canvasController.isRightFlyoutOpen()) {
420
- const posInfo = this.config.enablePositionNodeOnRightFlyoutOpen;
421
+ const posInfo = (typeof this.config.enablePositionNodeOnRightFlyoutOpen === "boolean")
422
+ ? { x: 50, y: 50 }
423
+ : this.config.enablePositionNodeOnRightFlyoutOpen;
421
424
  const x = posInfo.x ? posInfo.x : 50;
422
425
  const y = posInfo.y ? posInfo.y : 50;
423
426
  const selNodeIds = this.activePipeline.getSelectedNodeIds();
@@ -866,29 +869,16 @@ export default class SVGCanvasRenderer {
866
869
  // plus its width and height. This needs to be called each time a new node
867
870
  // is dragged from the palette, in case the dimensions of the ghost node
868
871
  // have changed because the canvas has been zoomed.
869
- getGhostNode(nodeTemplate) {
872
+ getGhostNode(node) {
870
873
  const that = this;
871
- const ghost = this.getGhostDimensions();
872
- const node = this.canvasController.convertNodeTemplate(nodeTemplate);
873
- node.layout = this.nodeLayout;
874
- if (node.is_expanded) {
875
- node.width = node.expanded_width;
876
- node.height = node.expanded_height;
877
- } else if (node.isResized) {
878
- node.width = node.resizeWidth;
879
- node.height = node.resizeHeight;
880
- } else {
881
- node.width = ghost.width;
882
- node.height = ghost.height;
883
- }
884
- const nodeImage = this.getNodeImage(node);
885
- const nodeImageType = this.getImageType(nodeImage);
886
874
  const ghostDivSel = this.getGhostDivSel();
887
875
 
888
876
  // Calculate the ghost area width which is the maximum of either the node
889
877
  // label or the default node width.
890
878
  const ghostAreaWidth = Math.max(this.nodeLayout.labelWidth, node.width);
891
879
 
880
+ let xOffset = 0;
881
+
892
882
  // Remove any existing SVG object from the div
893
883
  ghostDivSel
894
884
  .selectAll(".d3-ghost-svg")
@@ -915,75 +905,82 @@ export default class SVGCanvasRenderer {
915
905
  .attr("width", node.width)
916
906
  .attr("height", node.height);
917
907
 
918
- ghostGrp
919
- .append(nodeImageType)
920
- .attr("class", "d3-node-image")
921
- .each(function() { that.setNodeImageContent(this, node); })
922
- .attr("x", this.nodeUtils.getNodeImagePosX(node))
923
- .attr("y", this.nodeUtils.getNodeImagePosY(node))
924
- .attr("width", this.nodeUtils.getNodeImageWidth(node))
925
- .attr("height", this.nodeUtils.getNodeImageHeight(node));
926
-
927
- const fObject = ghostGrp
928
- .append("foreignObject")
929
- .attr("x", this.nodeUtils.getNodeLabelPosX(node))
930
- .attr("y", this.nodeUtils.getNodeLabelPosY(node))
931
- .attr("width", this.nodeUtils.getNodeLabelWidth(node))
932
- .attr("height", this.nodeUtils.getNodeLabelHeight(node))
933
- .attr("class", "d3-foreign-object-ghost-label");
934
-
935
- const fObjectDiv = fObject
936
- .append("xhtml:div")
937
- .attr("class", this.nodeUtils.getNodeLabelClass(node));
938
-
939
- const fObjectSpan = fObjectDiv
940
- .append("xhtml:span")
941
- .html(node.label);
942
-
943
- // At the time of writing, Firefox takes the ghost image from only those
944
- // objects that are visible (ignoring any invisible objects like the div
945
- // and SVG area) consequently we position the node and label so the label
946
- // (if bigger than the node width) is positioned up against the left edge
947
- // of the invisible div and SVG area. If the label is shorter than the node
948
- // width, the node is positioned up against the left edge of the SVG. We do
949
- // this by translating the group object in the x direction.
950
-
951
- // First calculate the display width of the label. The span will be the
952
- // full text but it may be constricted by the label width in the layout.
953
- const labelSpanWidth = fObjectSpan.node().getBoundingClientRect().width + 4; // Include border for label
954
- const nodeLabelWidth = this.nodeUtils.getNodeLabelWidth(node);
955
- const labelDisplayLength = Math.min(nodeLabelWidth, labelSpanWidth);
956
-
957
- // Next calculate the amount, if any, the label protrudes beyond the edge
958
- // of the node width and move the ghost group by that amount.
959
- const xOffset = Math.max(0, (labelDisplayLength - ghost.width) / 2) * this.zoomTransform.k;
960
- ghostGrp.attr("transform", `translate(${xOffset}, 0) scale(${this.zoomTransform.k})`);
961
-
962
- // If the label is center justified, restrict the label width to the
963
- // display amount and adjust the x coordinate to compensate for the change
964
- // in width.
965
- if (node.layout.labelAlign === "center") {
966
- const labelDiff = Math.max(0, (nodeLabelWidth - labelDisplayLength) / 2);
967
-
968
- fObject
969
- .attr("width", labelDisplayLength)
970
- .attr("x", this.nodeUtils.getNodeLabelPosX(node) + labelDiff);
971
- fObjectDiv.attr("width", labelDisplayLength);
908
+ if (!this.nodeLayout.nodeExternalObject) {
909
+ const nodeImage = this.getNodeImage(node);
910
+ const nodeImageType = this.getImageType(nodeImage);
911
+
912
+ ghostGrp
913
+ .append(nodeImageType)
914
+ .attr("class", "d3-node-image")
915
+ .each(function() { that.setNodeImageContent(this, node); })
916
+ .attr("x", this.nodeUtils.getNodeImagePosX(node))
917
+ .attr("y", this.nodeUtils.getNodeImagePosY(node))
918
+ .attr("width", this.nodeUtils.getNodeImageWidth(node))
919
+ .attr("height", this.nodeUtils.getNodeImageHeight(node));
920
+
921
+ const fObject = ghostGrp
922
+ .append("foreignObject")
923
+ .attr("x", this.nodeUtils.getNodeLabelPosX(node))
924
+ .attr("y", this.nodeUtils.getNodeLabelPosY(node))
925
+ .attr("width", this.nodeUtils.getNodeLabelWidth(node))
926
+ .attr("height", this.nodeUtils.getNodeLabelHeight(node))
927
+ .attr("class", "d3-foreign-object-ghost-label");
928
+
929
+ const fObjectDiv = fObject
930
+ .append("xhtml:div")
931
+ .attr("class", this.nodeUtils.getNodeLabelClass(node));
932
+
933
+ const fObjectSpan = fObjectDiv
934
+ .append("xhtml:span")
935
+ .html(node.label);
936
+
937
+ // At the time of writing, Firefox takes the ghost image from only those
938
+ // objects that are visible (ignoring any invisible objects like the div
939
+ // and SVG area) consequently we position the node and label so the label
940
+ // (if bigger than the node width) is positioned up against the left edge
941
+ // of the invisible div and SVG area. If the label is shorter than the node
942
+ // width, the node is positioned up against the left edge of the SVG. We do
943
+ // this by translating the group object in the x direction.
944
+
945
+ // First calculate the display width of the label. The span will be the
946
+ // full text but it may be constricted by the label width in the layout.
947
+ const labelSpanWidth = fObjectSpan.node().getBoundingClientRect().width + 4; // Include border for label
948
+ const nodeLabelWidth = this.nodeUtils.getNodeLabelWidth(node);
949
+ const labelDisplayLength = Math.min(nodeLabelWidth, labelSpanWidth);
950
+
951
+ // Next calculate the amount, if any, the label protrudes beyond the edge
952
+ // of the node width and move the ghost group by that amount.
953
+ xOffset = Math.max(0, (labelDisplayLength - node.width) / 2) * this.zoomTransform.k;
954
+
955
+ // If the label is center justified, restrict the label width to the
956
+ // display amount and adjust the x coordinate to compensate for the change
957
+ // in width.
958
+ if (node.layout.labelAlign === "center") {
959
+ const labelDiff = Math.max(0, (nodeLabelWidth - labelDisplayLength) / 2);
960
+
961
+ fObject
962
+ .attr("width", labelDisplayLength)
963
+ .attr("x", this.nodeUtils.getNodeLabelPosX(node) + labelDiff);
964
+ fObjectDiv.attr("width", labelDisplayLength);
965
+ }
972
966
  }
973
967
 
968
+ ghostGrp.attr("transform", `translate(${xOffset}, 0) scale(${this.zoomTransform.k})`);
969
+
974
970
  // Get the amount the actual browser page is 'zoomed'. This is differet
975
971
  // to the zoom amount for the canvas objects.
976
972
  const browserZoom = this.getBrowserZoom();
977
973
 
978
974
  // Calculate the center of the node area for positioning the mouse pointer
979
975
  // on the image when it is being dragged.
980
- const centerX = (xOffset + ((ghost.width / 2) * this.zoomTransform.k)) * browserZoom;
981
- const centerY = ((ghost.height / 2) * this.zoomTransform.k) * browserZoom;
976
+ const centerX = (xOffset + ((node.width / 2) * this.zoomTransform.k)) * browserZoom;
977
+ const centerY = ((node.height / 2) * this.zoomTransform.k) * browserZoom;
982
978
 
983
979
  return {
984
980
  element: ghostDivSel.node(),
985
981
  centerX: centerX,
986
- centerY: centerY
982
+ centerY: centerY,
983
+ nodeTemplate: node
987
984
  };
988
985
  }
989
986
 
@@ -1010,17 +1007,6 @@ export default class SVGCanvasRenderer {
1010
1007
  return browserZoom;
1011
1008
  }
1012
1009
 
1013
- // Returns an object containing the dimensions of the ghost node that hovers
1014
- // over canvas when a node is being dragged from the palette. The ghost node
1015
- // is based on the default node width and height so any change to these values
1016
- // that might be made to a node by the layoutHandler will not be reflected here.
1017
- getGhostDimensions() {
1018
- return {
1019
- width: this.nodeLayout.defaultNodeWidth,
1020
- height: this.nodeLayout.defaultNodeHeight
1021
- };
1022
- }
1023
-
1024
1010
  nodeTemplateDragStart(nodeTemplate) {
1025
1011
  if (this.isNodeTemplateInsertableIntoLink(nodeTemplate)) {
1026
1012
  this.setDataLinkSelectionAreaWider(true);
@@ -1049,12 +1035,11 @@ export default class SVGCanvasRenderer {
1049
1035
 
1050
1036
  if (this.isNodeTemplateAttachableToDetachedLinks(nodeTemplate)) {
1051
1037
  const mousePos = this.convertPageCoordsToCanvasCoords(x, y);
1052
- const ghost = this.getGhostDimensions();
1053
1038
  const ghostArea = {
1054
- x1: mousePos.x - (ghost.width / 2),
1055
- y1: mousePos.y - (ghost.height / 2),
1056
- x2: mousePos.x + (ghost.width / 2),
1057
- y2: mousePos.y + (ghost.height / 2)
1039
+ x1: mousePos.x - (nodeTemplate.width / 2),
1040
+ y1: mousePos.y - (nodeTemplate.height / 2),
1041
+ x2: mousePos.x + (nodeTemplate.width / 2),
1042
+ y2: mousePos.y + (nodeTemplate.height / 2)
1058
1043
  };
1059
1044
  const template = this.canvasController.convertNodeTemplate(nodeTemplate);
1060
1045
  const links = this.getAttachableLinksForNodeAtPos(template, ghostArea);
@@ -1204,12 +1189,13 @@ export default class SVGCanvasRenderer {
1204
1189
  this.dragNewLinkOverNode = null;
1205
1190
  }
1206
1191
 
1207
- // Processes the drop of a palette node template onto the canvas.
1192
+ // Processes the drop of a palette node template onto the canvas. The
1193
+ // nodeTemplate is in internal format.
1208
1194
  nodeTemplateDropped(nodeTemplate, x, y) {
1209
1195
  if (nodeTemplate === null) {
1210
1196
  return;
1211
1197
  }
1212
- const transPos = this.transformMousePosForNode(x, y);
1198
+ const transPos = this.transformMousePosForNode(x, y, nodeTemplate);
1213
1199
 
1214
1200
  // If the node template was dropped on a link
1215
1201
  if (this.dragOverLink) {
@@ -1242,13 +1228,17 @@ export default class SVGCanvasRenderer {
1242
1228
 
1243
1229
  // Transforms the mouse position passed in to be appropriate for a palette
1244
1230
  // node or external object being dragged over the canvas.
1245
- transformMousePosForNode(x, y) {
1231
+ transformMousePosForNode(x, y, node) {
1246
1232
  const mousePos = this.convertPageCoordsToCanvasCoords(x, y);
1247
1233
 
1248
1234
  // Offset mousePos so new node appears in center of mouse location.
1249
- const ghost = this.getGhostDimensions();
1250
- mousePos.x -= ghost.width / 2;
1251
- mousePos.y -= ghost.height / 2;
1235
+ if (node && node.width && node.height) {
1236
+ mousePos.x -= node.width / 2;
1237
+ mousePos.y -= node.height / 2;
1238
+ } else {
1239
+ mousePos.x -= this.nodeLayout.defaultNodeWidth / 2;
1240
+ mousePos.y -= this.nodeLayout.defaultNodeHeight / 2;
1241
+ }
1252
1242
 
1253
1243
  return this.getMousePosSnapToGrid(mousePos);
1254
1244
  }
@@ -2670,7 +2660,9 @@ export default class SVGCanvasRenderer {
2670
2660
  selection
2671
2661
  .data(data, (d) => d.id)
2672
2662
  .join(
2673
- (enter) => this.createNodes(enter)
2663
+ (enter) => this.createNodes(enter),
2664
+ null,
2665
+ (remove) => this.removeNodes(remove)
2674
2666
  )
2675
2667
  .attr("transform", (d) => `translate(${d.x_pos}, ${d.y_pos})`)
2676
2668
  .attr("class", (d) => this.getNodeGroupClass(d))
@@ -2703,10 +2695,15 @@ export default class SVGCanvasRenderer {
2703
2695
  .attr("class", "d3-node-selection-highlight");
2704
2696
 
2705
2697
  // Node Body
2706
- newNodeGroups.filter((d) => !CanvasUtils.isSuperBindingNode(d))
2698
+ newNodeGroups.filter((d) => !CanvasUtils.isSuperBindingNode(d) && d.layout.nodeShapeDisplay)
2707
2699
  .append("path")
2708
2700
  .attr("class", "d3-node-body-outline");
2709
2701
 
2702
+ // Optional foreign object to contain a React object
2703
+ newNodeGroups.filter((d) => d.layout.nodeExternalObject)
2704
+ .append("foreignObject")
2705
+ .attr("class", "d3-foreign-object-external-node");
2706
+
2710
2707
  // Node Image
2711
2708
  newNodeGroups.filter((d) => !CanvasUtils.isSuperBindingNode(d) && d.layout.imageDisplay)
2712
2709
  .each((node, i, nodeGrps) => {
@@ -2718,7 +2715,7 @@ export default class SVGCanvasRenderer {
2718
2715
  });
2719
2716
 
2720
2717
  // Node Label
2721
- newNodeGroups.filter((d) => !CanvasUtils.isSuperBindingNode(d))
2718
+ newNodeGroups.filter((d) => !CanvasUtils.isSuperBindingNode(d) && d.layout.labelDisplay)
2722
2719
  .append("foreignObject")
2723
2720
  .attr("class", "d3-foreign-object-node-label")
2724
2721
  .call(this.attachNodeLabelListeners.bind(this))
@@ -2752,6 +2749,15 @@ export default class SVGCanvasRenderer {
2752
2749
  .attr("d", (d) => this.getNodeShapePath(d))
2753
2750
  .attr("style", (d) => this.getNodeBodyStyle(d, "default"));
2754
2751
 
2752
+ // Optional foreign object to contain a React object
2753
+ joinedNodeGrps.selectChildren(".d3-foreign-object-external-node")
2754
+ .datum((d) => this.activePipeline.getNode(d.id))
2755
+ .attr("width", (d) => d.width)
2756
+ .attr("height", (d) => d.height)
2757
+ .attr("x", 0)
2758
+ .attr("y", 0)
2759
+ .each(addNodeExternalObject.bind(this));
2760
+
2755
2761
  // Node Image
2756
2762
  joinedNodeGrps.selectChildren(".d3-node-image")
2757
2763
  .datum((d) => this.activePipeline.getNode(d.id))
@@ -2806,6 +2812,20 @@ export default class SVGCanvasRenderer {
2806
2812
  this.logger.logEndTimer("updateNodes");
2807
2813
  }
2808
2814
 
2815
+ removeNodes(removeSel) {
2816
+ // Remove any foreign objects for react nodes, if necessary.
2817
+ removeSel
2818
+ .selectChildren(".d3-foreign-object-external-node")
2819
+ .each(removeExternalObject.bind(this));
2820
+
2821
+ // Remove all nodes in the selection.
2822
+ removeSel.remove();
2823
+ }
2824
+
2825
+ onClick() {
2826
+ window.console.log("onClick");
2827
+ }
2828
+
2809
2829
  // Handles the display of a supernode sub-flow contents or hides the contents
2810
2830
  // as necessary.
2811
2831
  displaySupernodeContents(d, supernodeD3Object) {
@@ -3138,6 +3158,10 @@ export default class SVGCanvasRenderer {
3138
3158
  attachInputPortListeners(inputPorts, node) {
3139
3159
  inputPorts
3140
3160
  .on("mousedown", (d3Event, port) => {
3161
+ if (!this.config.enableEditingActions) {
3162
+ CanvasUtils.stopPropagationAndPreventDefault(d3Event);
3163
+ return;
3164
+ }
3141
3165
  if (this.config.enableAssocLinkCreation) {
3142
3166
  // Make sure this is just a left mouse button click - we don't want context menu click starting a line being drawn
3143
3167
  if (d3Event.button === 0) {
@@ -3197,6 +3221,10 @@ export default class SVGCanvasRenderer {
3197
3221
  attachOutputPortListeners(outputPorts, node) {
3198
3222
  outputPorts
3199
3223
  .on("mousedown", (d3Event, port) => {
3224
+ if (!this.config.enableEditingActions) {
3225
+ CanvasUtils.stopPropagationAndPreventDefault(d3Event);
3226
+ return;
3227
+ }
3200
3228
  // Make sure this is just a left mouse button click - we don't want context menu click starting a line being drawn
3201
3229
  if (d3Event.button === 0) {
3202
3230
  CanvasUtils.stopPropagationAndPreventDefault(d3Event); // Stops the node drag behavior when clicking on the handle/circle
@@ -3452,12 +3480,13 @@ export default class SVGCanvasRenderer {
3452
3480
  this.updateDecPaths(dec, decSel, objType);
3453
3481
  this.updateDecImages(dec, decSel, objType, d);
3454
3482
  this.updateDecLabels(dec, decSel, objType, d);
3483
+ this.updateDecJsxObjs(dec, decSel, objType, d);
3455
3484
  }
3456
3485
 
3457
3486
  updateDecOutlines(dec, decSel, objType, d) {
3458
3487
  let outlnSel = decSel.selectChild("rect");
3459
3488
 
3460
- if (!dec.label && !dec.path && dec.outline !== false) {
3489
+ if (!dec.label && !dec.path && !dec.jsx && dec.outline !== false) {
3461
3490
  outlnSel = outlnSel.empty() ? decSel.append("rect") : outlnSel;
3462
3491
  outlnSel
3463
3492
  .attr("class", this.decUtils.getDecClass(dec, `d3-${objType}-dec-outline`))
@@ -3505,7 +3534,7 @@ export default class SVGCanvasRenderer {
3505
3534
  }
3506
3535
 
3507
3536
  updateDecLabels(dec, decSel, objType, d) {
3508
- let labelSel = decSel.selectChild("foreignObject");
3537
+ let labelSel = decSel.selectChild(".d3-foreign-object-dec-label");
3509
3538
 
3510
3539
  if (dec.label) {
3511
3540
  if (labelSel.empty()) {
@@ -3532,6 +3561,29 @@ export default class SVGCanvasRenderer {
3532
3561
  }
3533
3562
  }
3534
3563
 
3564
+ updateDecJsxObjs(dec, decSel, objType, d) {
3565
+ let extSel = decSel.selectChild(".d3-foreign-object-dec-jsx");
3566
+
3567
+ if (dec.jsx) {
3568
+ if (extSel.empty()) {
3569
+ extSel = decSel
3570
+ .append("foreignObject")
3571
+ .attr("class", "d3-foreign-object-dec-jsx")
3572
+ .attr("tabindex", -1)
3573
+ .attr("x", 0)
3574
+ .attr("y", 0)
3575
+ .call(this.attachDecLabelListeners.bind(this, d, objType));
3576
+ }
3577
+ extSel
3578
+ .attr("width", this.decUtils.getDecWidth(dec, d, objType))
3579
+ .attr("height", this.decUtils.getDecHeight(dec, d, objType))
3580
+ .each(addDecExternalObject.bind(this));
3581
+ } else {
3582
+ extSel.each(removeExternalObject.bind(this));
3583
+ extSel.remove();
3584
+ }
3585
+ }
3586
+
3535
3587
  attachDecLabelListeners(obj, objType, decLabels) {
3536
3588
  decLabels
3537
3589
  .on("dblclick", (d3Event, dec) => {
@@ -6859,7 +6911,8 @@ export default class SVGCanvasRenderer {
6859
6911
  // "Curve" link to make it look presentable. I know, I tried!
6860
6912
  getArrowHeadTransform(d) {
6861
6913
  const angle =
6862
- this.canvasLayout.linkType === LINK_TYPE_ELBOW
6914
+ this.canvasLayout.linkType === LINK_TYPE_ELBOW ||
6915
+ this.canvasLayout.linkType === LINK_TYPE_CURVE
6863
6916
  ? this.getAngleBasedOnLinkDirection()
6864
6917
  : Math.atan2((d.y2 - d.y1), (d.x2 - d.x1)) * (180 / Math.PI);
6865
6918
 
@@ -0,0 +1,34 @@
1
+ /*
2
+ * Copyright 2023 Elyra Authors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import React from "react";
17
+ import ReactDOM from "react-dom";
18
+
19
+ export const addNodeExternalObject = (node, i, foreignObjects) => {
20
+ ReactDOM.render(
21
+ <node.layout.nodeExternalObject
22
+ nodeData={node}
23
+ />,
24
+ foreignObjects[i]
25
+ );
26
+ };
27
+
28
+ export const addDecExternalObject = (dec, i, foreignObjects) => {
29
+ ReactDOM.render(dec.jsx, foreignObjects[i]);
30
+ };
31
+
32
+ export const removeExternalObject = (obj, i, foreignObjects) => {
33
+ ReactDOM.unmountComponentAtNode(foreignObjects[i]);
34
+ };
@@ -70,7 +70,8 @@ class ButtonAction extends React.Component {
70
70
  }
71
71
 
72
72
  render() {
73
- const className = classNames("properties-action-button", { "hide": this.props.state === STATES.HIDDEN });
73
+ const customClassName = this.props.action.className ? this.props.action.className : "";
74
+ const className = classNames("properties-action-button", { "hide": this.props.state === STATES.HIDDEN }, customClassName);
74
75
  const disabled = this.props.state === STATES.DISABLED;
75
76
  const actionButtonKind = this.getActionButtonKind();
76
77
  const actionButtonSize = this.getActionButtonSize();
@@ -49,9 +49,10 @@ class ImageAction extends React.Component {
49
49
  }
50
50
 
51
51
  const disabled = this.props.state === STATES.DISABLED;
52
+ const customClassName = this.props.action.className ? this.props.action.className : "";
52
53
  const className = classNames("properties-action-image", { "left": this.props.action.image.placement === "left" },
53
54
  { "right": this.props.action.image.placement === "right" }, { "hide": this.props.state === STATES.HIDDEN },
54
- { "disabled": disabled });
55
+ { "disabled": disabled }, customClassName);
55
56
 
56
57
  const image = (
57
58
  <div data-id={this.props.action.name}>
@@ -93,6 +93,9 @@ class TitleEditor extends Component {
93
93
  }
94
94
 
95
95
  render() {
96
+ if (this.props.title === null) {
97
+ return null;
98
+ }
96
99
  const propertiesTitleEditButtonLabel = PropertyUtils.formatMessage(this.props.controller.getReactIntl(), MESSAGE_KEYS.TITLE_EDITOR_LABEL);
97
100
  const helpButtonLabel = PropertyUtils.formatMessage(this.props.controller.getReactIntl(), MESSAGE_KEYS.TITLE_EDITOR_HELPBUTTON_LABEL);
98
101
  const closeButtonLabel = PropertyUtils.formatMessage(this.props.controller.getReactIntl(), MESSAGE_KEYS.PROPERTIESEDIT_CLOSEBUTTON_LABEL);
@@ -171,11 +171,9 @@
171
171
  margin-left: 0;
172
172
  }
173
173
 
174
- // Override padding for sortable columns to show space between column label and sort icon
175
174
  .ReactVirtualized__Table__sortableHeaderColumn {
176
- .properties-tooltips-container {
177
- padding: 0;
178
- padding-left: $spacing-05;
175
+ .properties-vt-label-tip-icon {
176
+ padding-right: $spacing-03; // spacing between column label and sort icon
179
177
  }
180
178
  &:hover {
181
179
  background-color: $active-ui;
@@ -21,3 +21,12 @@
21
21
  }
22
22
  }
23
23
  }
24
+
25
+ .properties-datepicker {
26
+ .bx--date-picker-container {
27
+ .bx--form-requirement,
28
+ .bx--form__helper-text {
29
+ max-width: 18rem; // https://github.com/carbon-design-system/carbon/issues/12937
30
+ }
31
+ }
32
+ }
@@ -17,7 +17,7 @@
17
17
  import React from "react";
18
18
  import PropTypes from "prop-types";
19
19
  import { connect } from "react-redux";
20
- import { PasswordInput, Form } from "carbon-components-react";
20
+ import { PasswordInput } from "carbon-components-react";
21
21
  import ValidationMessage from "./../../components/validation-message";
22
22
  import * as ControlUtils from "./../../util/control-utils";
23
23
  import { STATES, MESSAGE_KEYS } from "./../../constants/constants.js";
@@ -49,23 +49,21 @@ class PasswordControl extends React.Component {
49
49
  const validationProps = ControlUtils.getValidationProps(this.props.messageInfo, this.props.tableControl);
50
50
  return (
51
51
  <div className={className} data-id={ControlUtils.getDataId(this.props.propertyId)}>
52
- <Form>
53
- <PasswordInput
54
- {...validationProps}
55
- autoComplete="off"
56
- id={this.id}
57
- disabled={this.props.state === STATES.DISABLED}
58
- placeholder={this.props.control.additionalText}
59
- onChange={this.handleChange.bind(this)}
60
- value={value}
61
- labelText={this.props.controlItem}
62
- hideLabel={this.props.tableControl}
63
- light={this.props.controller.getLight() && this.props.control.light}
64
- tooltipAlignment="end"
65
- showPasswordLabel={showPasswordLabel}
66
- hidePasswordLabel={hidePasswordLabel}
67
- />
68
- </Form>
52
+ <PasswordInput
53
+ {...validationProps}
54
+ autoComplete="off"
55
+ id={this.id}
56
+ disabled={this.props.state === STATES.DISABLED}
57
+ placeholder={this.props.control.additionalText}
58
+ onChange={this.handleChange.bind(this)}
59
+ value={value}
60
+ labelText={this.props.controlItem}
61
+ hideLabel={this.props.tableControl}
62
+ light={this.props.controller.getLight() && this.props.control.light}
63
+ tooltipAlignment="end"
64
+ showPasswordLabel={showPasswordLabel}
65
+ hidePasswordLabel={hidePasswordLabel}
66
+ />
69
67
  <ValidationMessage inTable={this.props.tableControl} tableOnly state={this.props.state} messageInfo={this.props.messageInfo} />
70
68
  </div>);
71
69
  }
@@ -18,7 +18,7 @@
18
18
  import { ResourceDef } from "../util/L10nProvider";
19
19
 
20
20
  export class Action {
21
- constructor(actionName, label, description, control, data, image, button) {
21
+ constructor(actionName, label, description, control, data, image, button, className) {
22
22
  this.name = actionName;
23
23
  this.label = label;
24
24
  this.description = description;
@@ -26,6 +26,7 @@ export class Action {
26
26
  this.data = data;
27
27
  this.image = image;
28
28
  this.button = button;
29
+ this.className = className;
29
30
  }
30
31
  }
31
32
 
@@ -40,6 +41,7 @@ class ActionDef {
40
41
  actionDef.data = action.data;
41
42
  actionDef.image = action.image;
42
43
  actionDef.button = action.button;
44
+ actionDef.className = action.class_name;
43
45
  return actionDef;
44
46
  }
45
47
  return null;
@@ -763,7 +763,7 @@ function _makeAction(action, l10nProvider) {
763
763
  if (action.description) {
764
764
  actionDesc = new Description(l10nProvider.l10nDesc(action, action.id));
765
765
  }
766
- return new Action(action.id, actionLabel, actionDesc, action.control, action.data, action.image, action.button);
766
+ return new Action(action.id, actionLabel, actionDesc, action.control, action.data, action.image, action.button, action.className);
767
767
  }
768
768
 
769
769
  function _makeButtons(buttonMetadata, l10nProvider) {
@@ -62,7 +62,7 @@ export class PropertyDef {
62
62
  const actionMetadata = ActionMetadata.makeActionMetadata(propertyOf(uihints)("action_info"));
63
63
  const groupMetadata = GroupMetadata.makeGroupMetadata(propertyOf(uihints)("group_info"), parameterMetadata.getParameters());
64
64
 
65
- const label = titleDefinition && titleDefinition.title ? titleDefinition.title : "";
65
+ const label = titleDefinition && titleDefinition.title ? titleDefinition.title : null;
66
66
  const labelEditable = titleDefinition && typeof titleDefinition.editable !== "undefined" ? titleDefinition.editable : DEFAULT_LABEL_EDITABLE;
67
67
 
68
68
  return new PropertyDef(
@@ -20,6 +20,7 @@ import classNames from "classnames";
20
20
  import { Tabs, Tab } from "carbon-components-react";
21
21
  import { getDataId } from "./../../util/control-utils";
22
22
  import { STATES } from "./../../constants/constants.js";
23
+ import { v4 as uuid4 } from "uuid";
23
24
 
24
25
  class Subtabs extends React.Component {
25
26
  constructor(props) {
@@ -27,6 +28,7 @@ class Subtabs extends React.Component {
27
28
  this.state = {
28
29
  activeTabId: ""
29
30
  };
31
+ this.uuid = uuid4();
30
32
  }
31
33
 
32
34
  onClick(tabId) {
@@ -50,8 +52,8 @@ class Subtabs extends React.Component {
50
52
 
51
53
  subTabs.push(
52
54
  <Tab
53
- id={"subtabs.tab." + i}
54
- key={"subtabs.tab." + i}
55
+ id={`subtabs.tab.${i}-${this.uuid}`}
56
+ key={`subtabs.tab.${i}-${this.uuid}`}
55
57
  disabled={panelState === STATES.DISABLED}
56
58
  className={classNames("properties-subtab", { "properties-leftnav-subtab-item": this.props.leftnav })}
57
59
  tabIndex={tabIdx}