@elyra/canvas 13.30.0 → 13.31.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 (77) hide show
  1. package/dist/canvas-controller-5714c16e.js +2 -0
  2. package/dist/canvas-controller-5714c16e.js.map +1 -0
  3. package/dist/canvas-controller-fb3341f8.js +2 -0
  4. package/dist/canvas-controller-fb3341f8.js.map +1 -0
  5. package/dist/common-canvas-cb2af30e.js +2 -0
  6. package/dist/common-canvas-cb2af30e.js.map +1 -0
  7. package/dist/common-canvas-fed3a116.js +2 -0
  8. package/dist/common-canvas-fed3a116.js.map +1 -0
  9. package/dist/{common-canvas-utils-bbe543ed.js → common-canvas-utils-04c7ddd2.js} +1 -1
  10. package/dist/{common-canvas-utils-bbe543ed.js.map → common-canvas-utils-04c7ddd2.js.map} +1 -1
  11. package/dist/{common-canvas-utils-a489e236.js → common-canvas-utils-26fdf006.js} +1 -1
  12. package/dist/{common-canvas-utils-a489e236.js.map → common-canvas-utils-26fdf006.js.map} +1 -1
  13. package/dist/common-canvas.es.js +1 -1
  14. package/dist/common-canvas.js +1 -1
  15. package/dist/common-properties-6709d67d.js +2 -0
  16. package/dist/common-properties-6709d67d.js.map +1 -0
  17. package/dist/common-properties-9ea710be.js +2 -0
  18. package/dist/common-properties-9ea710be.js.map +1 -0
  19. package/dist/{context-menu-wrapper-ea061c95.js → context-menu-wrapper-c6dea37b.js} +2 -2
  20. package/dist/{context-menu-wrapper-ea061c95.js.map → context-menu-wrapper-c6dea37b.js.map} +1 -1
  21. package/dist/{context-menu-wrapper-7caefd31.js → context-menu-wrapper-ff90de76.js} +2 -2
  22. package/dist/{context-menu-wrapper-7caefd31.js.map → context-menu-wrapper-ff90de76.js.map} +1 -1
  23. package/dist/keyboard-utils-485bef78.js +2 -0
  24. package/dist/keyboard-utils-485bef78.js.map +1 -0
  25. package/dist/keyboard-utils-f7c4ec40.js +2 -0
  26. package/dist/keyboard-utils-f7c4ec40.js.map +1 -0
  27. package/dist/lib/canvas-controller.es.js +1 -1
  28. package/dist/lib/canvas-controller.js +1 -1
  29. package/dist/lib/canvas.es.js +1 -1
  30. package/dist/lib/canvas.js +1 -1
  31. package/dist/lib/context-menu.es.js +1 -1
  32. package/dist/lib/context-menu.js +1 -1
  33. package/dist/lib/properties.es.js +1 -1
  34. package/dist/lib/properties.js +1 -1
  35. package/dist/{toolbar-4ce5b537.js → toolbar-1db25a07.js} +2 -2
  36. package/dist/{toolbar-4ce5b537.js.map → toolbar-1db25a07.js.map} +1 -1
  37. package/dist/{toolbar-40d93a80.js → toolbar-4c42861f.js} +2 -2
  38. package/dist/{toolbar-40d93a80.js.map → toolbar-4c42861f.js.map} +1 -1
  39. package/package.json +4 -3
  40. package/src/command-actions/createAutoNodeAction.js +0 -7
  41. package/src/common-canvas/canvas-controller.js +28 -34
  42. package/src/common-canvas/cc-bottom-panel.jsx +21 -26
  43. package/src/common-canvas/cc-panels.jsx +16 -5
  44. package/src/common-canvas/cc-right-flyout.jsx +17 -33
  45. package/src/common-canvas/common-canvas-utils.js +6 -6
  46. package/src/common-canvas/keyboard-utils.js +3 -2
  47. package/src/common-canvas/svg-canvas-d3.js +4 -4
  48. package/src/common-canvas/svg-canvas-renderer.js +21 -7
  49. package/src/common-canvas/svg-canvas-utils-drag-objects.js +41 -7
  50. package/src/common-canvas/svg-canvas-utils-zoom.js +23 -5
  51. package/src/common-properties/components/editor-form/editor-form.jsx +2 -0
  52. package/src/common-properties/components/wide-flyout/wide-flyout.jsx +85 -5
  53. package/src/common-properties/controls/datepicker/datepicker.jsx +1 -0
  54. package/src/common-properties/controls/dropdown/dropdown.jsx +17 -11
  55. package/src/common-properties/form/ControlInfo.js +1 -1
  56. package/src/common-properties/panels/tearsheet/tearsheet.jsx +2 -1
  57. package/src/object-model/layout-dimensions.js +6 -0
  58. package/stats.html +1 -1
  59. package/types/canvas-controller.ts +18 -4
  60. package/types/common-canvas.ts +5 -0
  61. package/types/common-properties.ts +1 -1
  62. package/dist/canvas-controller-5b727491.js +0 -2
  63. package/dist/canvas-controller-5b727491.js.map +0 -1
  64. package/dist/canvas-controller-f1489f81.js +0 -2
  65. package/dist/canvas-controller-f1489f81.js.map +0 -1
  66. package/dist/common-canvas-4807ed78.js +0 -2
  67. package/dist/common-canvas-4807ed78.js.map +0 -1
  68. package/dist/common-canvas-a41d6dc9.js +0 -2
  69. package/dist/common-canvas-a41d6dc9.js.map +0 -1
  70. package/dist/common-properties-75f30ccf.js +0 -2
  71. package/dist/common-properties-75f30ccf.js.map +0 -1
  72. package/dist/common-properties-e2a321a6.js +0 -2
  73. package/dist/common-properties-e2a321a6.js.map +0 -1
  74. package/dist/keyboard-utils-232a10e1.js +0 -2
  75. package/dist/keyboard-utils-232a10e1.js.map +0 -1
  76. package/dist/keyboard-utils-ce49412d.js +0 -2
  77. package/dist/keyboard-utils-ce49412d.js.map +0 -1
@@ -657,9 +657,19 @@ export default class SVGCanvasUtilsZoom {
657
657
  newZoomTransform.y !== this.zoomTransform.y;
658
658
  }
659
659
 
660
- // Zooms the canvas to fit in the current viewport.
661
- zoomToFit() {
660
+ // Zooms all the canvas objects to fit in the current viewport.
661
+ zoomToFit(animateTime) {
662
662
  const canvasDimensions = this.getCanvasDimensionsWithPadding();
663
+ const zoom = this.getZoomToFit(canvasDimensions);
664
+ if (zoom) {
665
+ this.zoomCanvasInvokeZoomBehavior(zoom, animateTime);
666
+ }
667
+ }
668
+
669
+ // Returns a zoom object that can be used to zoom canvas objects (nodes,
670
+ // comments and/or detached links), that occupy the canvasDimensions,
671
+ // to fit in the current viewport.
672
+ getZoomToFit(canvasDimensions) {
663
673
  const viewPortDimensions = this.getViewportDimensions();
664
674
 
665
675
  if (canvasDimensions) {
@@ -673,8 +683,9 @@ export default class SVGCanvasUtilsZoom {
673
683
  x -= newScale * canvasDimensions.left;
674
684
  y -= newScale * canvasDimensions.top;
675
685
 
676
- this.zoomCanvasInvokeZoomBehavior({ x: x, y: y, k: newScale });
686
+ return { x: x, y: y, k: newScale };
677
687
  }
688
+ return null;
678
689
  }
679
690
 
680
691
  // Returns a zoom object that will, if applied to the canvas, zoom the objects
@@ -689,10 +700,17 @@ export default class SVGCanvasUtilsZoom {
689
700
  if (nodes.length > 0 || comments.length > 0 || links.length > 0) {
690
701
  const canvasDimensions = CanvasUtils.getCanvasDimensions(nodes, comments, links, 0, 0, true);
691
702
  const canv = this.convertRectAdjustedForScaleWithPadding(canvasDimensions, 1, 30);
692
- const xPosInt = parseInt(xPos, 10);
693
- const yPosInt = typeof yPos === "undefined" ? xPosInt : parseInt(yPos, 10);
694
703
 
695
704
  if (canv) {
705
+ // If the bounding rectangle (canv) doesn't completely fit in the viewport
706
+ // we do a zoom to fit to bring the requested objects into view.
707
+ if (canv.width > transformedSVGRect.width || canv.height > transformedSVGRect.height) {
708
+ return this.getZoomToFit(canv);
709
+ }
710
+
711
+ const xPosInt = parseInt(xPos, 10);
712
+ const yPosInt = typeof yPos === "undefined" ? xPosInt : parseInt(yPos, 10);
713
+
696
714
  let xOffset;
697
715
  let yOffset;
698
716
 
@@ -531,6 +531,7 @@ class EditorForm extends React.Component {
531
531
  <TearSheet
532
532
  open={this.props.controller.getActiveTearsheet() !== null}
533
533
  onCloseCallback={onCloseCallback}
534
+ cancelHandler={onCloseCallback}
534
535
  key={panel.id}
535
536
  tearsheet={this.visibleTearsheet}
536
537
  />
@@ -683,6 +684,7 @@ class EditorForm extends React.Component {
683
684
  stackedTearsheet = (<TearSheet
684
685
  open
685
686
  stacked
687
+ cancelHandler={this.closeFieldPicker}
686
688
  tearsheet={{
687
689
  title: title,
688
690
  content: this.fieldPicker()
@@ -24,23 +24,65 @@ import { Portal } from "react-portal";
24
24
  export default class WideFlyout extends Component {
25
25
  constructor(props) {
26
26
  super(props);
27
+ this.modalRef = React.createRef();
27
28
  this.state = {
28
29
  style: {
29
30
  height: 0
30
31
  }
31
32
  };
32
33
  this.updateDimensions = this.updateDimensions.bind(this);
34
+ this.handleTabKey = this.handleTabKey.bind(this);
33
35
  }
34
36
 
35
37
  componentDidMount() {
36
38
  this.updateDimensions();
37
39
  window.addEventListener("resize", this.updateDimensions);
40
+ document.addEventListener("keydown", this.handleTabKey);
41
+ this.focusOnFirstFocusable(); // Set initial focus inside the modal.
42
+ }
43
+ componentDidUpdate(prevProps) {
44
+ // If modal is still open, and new item added.
45
+ const modal = this.getActiveModal();
46
+ if (!prevProps.show && this.props.show && modal) {
47
+ // If focus is outside modal, move it to first focusable
48
+ this.focusOnFirstFocusable();
49
+ return;
50
+ }
51
+ if (this.props.show && modal) {
52
+ const active = document.activeElement;
53
+ // Restore focus if lost due to modal content changes like typing or adding new fields
54
+ if (active === document.body || !modal.contains(active)) {
55
+ this.focusOnFirstFocusable();
56
+ }
57
+ }
38
58
  }
39
-
40
59
  componentWillUnmount() {
41
60
  window.removeEventListener("resize", this.updateDimensions);
61
+ document.removeEventListener("keydown", this.handleTabKey);
62
+ }
63
+ // Returns an array of focusable elements inside the modal.
64
+ getFocusables() {
65
+ const modal = this.getActiveModal();
66
+ if (!modal) {
67
+ return [];
68
+ }
69
+ return Array.from(
70
+ modal.querySelectorAll(
71
+ "button, a[href], input, select, textarea, [tabindex]:not([tabindex='-1'])"
72
+ )).filter((el) => el.offsetParent !== null); // Filter out hidden/disabled elements.
73
+ }
74
+ // Focus on the first focusable element once modal opens.
75
+ getActiveModal() {
76
+ const modals = Array.from(document.querySelectorAll("div[role='dialog'].properties-wf-content.show"));
77
+ // Pick the last one — it's the top-most modal (deepest in DOM)
78
+ return modals.length > 0 ? modals[modals.length - 1] : null;
79
+ }
80
+ focusOnFirstFocusable() {
81
+ const focusables = this.getFocusables();
82
+ if (focusables.length > 0) {
83
+ focusables[0].focus();
84
+ }
42
85
  }
43
-
44
86
  updateDimensions() {
45
87
  if (this.wideFlyout) {
46
88
  // used to find correct parent
@@ -66,6 +108,39 @@ export default class WideFlyout extends Component {
66
108
  }
67
109
  return null;
68
110
  }
111
+ // Handles focus trap inside the modal when using Tab or Shift+tab key.
112
+ handleTabKey(e) {
113
+ if (e.key !== "Tab") {
114
+ return;
115
+ }
116
+ const modal = this.getActiveModal();
117
+ if (!modal || !this.props.show) {
118
+ return;
119
+ }
120
+ const focusables = this.getFocusables();
121
+ if (focusables.length === 0) {
122
+ return;
123
+ }
124
+ const first = focusables[0];
125
+ const last = focusables[focusables.length - 1];
126
+ const active = document.activeElement;
127
+ // If focus is outside modal or on <body>,Then bring back to the first element inside modal.
128
+ if (this.props.show && (!modal.contains(active) || active === document.body)) {
129
+ e.preventDefault();
130
+ // If "Shift+Tab" then focus should be on last element inside the modal else first.
131
+ (e.shiftKey ? last : first).focus();
132
+ return;
133
+ }
134
+ if (e.shiftKey) {
135
+ if (active === first) {
136
+ e.preventDefault();
137
+ last.focus();
138
+ }
139
+ } else if (active === last) {
140
+ e.preventDefault();
141
+ first.focus();
142
+ }
143
+ }
69
144
 
70
145
  render() {
71
146
  const overlay = (<div className={classNames("properties-wf-overlay", { "show": this.props.show })} />);
@@ -87,10 +162,15 @@ export default class WideFlyout extends Component {
87
162
  children = (<div className="properties-wf-children"> {this.props.children} </div>);
88
163
  }
89
164
  return (
90
- <div className="properties-wf-modal" ref={ (ref) => (this.wideFlyout = ref) }>
165
+ <div className="properties-wf-modal" ref={(ref) => (this.wideFlyout = ref)}>
91
166
  <Portal node={this.commonPropertiesParent}>
92
- { overlay }
93
- <div className={classNames("properties-wf-content", { "show": this.props.show, "properties-light-disabled": !this.props.light })} style={this.state.style}>
167
+ {overlay}
168
+ <div
169
+ ref={this.modalRef}
170
+ role="dialog"
171
+ className={classNames("properties-wf-content", { "show": this.props.show, "properties-light-disabled": !this.props.light })}
172
+ style={this.state.style}
173
+ >
94
174
  {title}
95
175
  {children}
96
176
  {buttons}
@@ -82,6 +82,7 @@ class DatepickerControl extends React.Component {
82
82
  onChange={this.handleChange.bind(this)}
83
83
  locale={this.locale}
84
84
  readOnly={this.props.readOnly}
85
+ value={this.state.value}
85
86
  >
86
87
  <DatePickerInput
87
88
  {...validationProps}
@@ -193,18 +193,24 @@ class DropDown extends React.Component {
193
193
  this.props.controller.updatePropertyValue(this.props.propertyId, value);
194
194
  }
195
195
 
196
- // evt is null when onBlur, empty string when clicking the 'x' to clear input
197
- handleOnInputChange(evt) {
196
+
197
+ handleOnInputChange(evt, options) {
198
198
  const currentValue = this.props.controller.getPropertyValue(this.props.propertyId);
199
199
 
200
200
  // Don't update property value during initial render
201
201
  if ((typeof currentValue === "undefined" || currentValue === null) && evt === "") {
202
202
  return;
203
203
  }
204
-
205
- if (evt !== null && evt !== currentValue) {
206
- const value = evt;
207
- this.props.controller.updatePropertyValue(this.props.propertyId, value);
204
+ const value = evt;
205
+ if (this.props.control.customValueAllowed) {
206
+ if (evt !== null && evt !== currentValue) {
207
+ this.props.controller.updatePropertyValue(this.props.propertyId, value);
208
+ }
209
+ } else { // check and update only if input is present in options when customValueAllowed=false
210
+ const isValidInput = options.some((opt) => opt.label === evt);
211
+ if (isValidInput && evt !== null && evt !== currentValue) {
212
+ this.props.controller.updatePropertyValue(this.props.propertyId, value);
213
+ }
208
214
  }
209
215
  }
210
216
 
@@ -278,7 +284,7 @@ class DropDown extends React.Component {
278
284
  { options }
279
285
  </Select>
280
286
  );
281
- } else if (this.props.control.customValueAllowed) { // combobox dropdown not allowed in tables
287
+ } else if (this.props.control.customValueAllowed || this.props.control.shouldFilterItem) { // combobox dropdown not allowed in tables
282
288
  const shouldFilterItem = this.props.control.shouldFilterItem === true ? { shouldFilterItem: this.filterItems } : {};
283
289
  dropdownComponent = (
284
290
  <ComboBox
@@ -286,15 +292,15 @@ class DropDown extends React.Component {
286
292
  aria-label={this.props.control.label ? this.props.control.label.text : ""}
287
293
  id={`${ControlUtils.getDataId(this.props.propertyId)}-dropdown`}
288
294
  disabled={this.props.state === STATES.DISABLED || this.disableEmptyListDropdown}
289
- placeholder={dropDown.selectedOption.label}
290
- selectedItem={dropDown.selectedOption.label}
295
+ placeholder={dropDown.selectedOption?.label}
296
+ selectedItem={dropDown.selectedOption?.label}
291
297
  items={dropDown.options}
292
298
  onChange={this.handleComboOnChange}
293
- onInputChange={this.handleOnInputChange}
299
+ onInputChange={(evt) => this.handleOnInputChange(evt, dropDown.options)}
294
300
  translateWithId={(id) => listBoxMenuIconTranslationIds[id]}
295
301
  titleText={this.props.controlItem}
296
302
  helperText={this.props.control.helperText}
297
- allowCustomValue
303
+ allowCustomValue={this.props.control.customValueAllowed}
298
304
  {...shouldFilterItem}
299
305
  />
300
306
  );
@@ -172,7 +172,7 @@ export class Control {
172
172
  if (typeof settings.visible === "boolean") {
173
173
  this.visible = settings.visible;
174
174
  }
175
- if (typeof settings.width === "number") {
175
+ if (typeof settings.width === "number" || (!isNaN(parseInt(settings.width, 10)) && settings.width?.endsWith("px"))) {
176
176
  this.width = settings.width;
177
177
  }
178
178
  if (settings.editStyle) {
@@ -52,6 +52,7 @@ class TearSheet extends Component {
52
52
  size="lg"
53
53
  aria-label={formatMessage(this.props.intl, MESSAGE_KEYS.PROPERTIES_LABEL, { label: title })}
54
54
  preventCloseOnClickOutside
55
+ onClose={this.props.cancelHandler} // for case where user closes the modal by pressing ESC
55
56
  >
56
57
  {title === null
57
58
  ? null
@@ -93,7 +94,7 @@ TearSheet.propTypes = {
93
94
  applyLabel: PropTypes.string, // Required if showPropertiesButtons is true
94
95
  rejectLabel: PropTypes.string, // Required if showPropertiesButtons is true
95
96
  okHandler: PropTypes.func, // Required if showPropertiesButtons is true
96
- cancelHandler: PropTypes.func, // Required if showPropertiesButtons is true
97
+ cancelHandler: PropTypes.func.isRequired,
97
98
  applyOnBlur: PropTypes.bool.isRequired,
98
99
  intl: PropTypes.object.isRequired,
99
100
  controller: PropTypes.object.isRequired,
@@ -121,6 +121,9 @@ const horizontalDefaultLayout = {
121
121
  // Allows the user to resize the node.
122
122
  nodeResizable: false,
123
123
 
124
+ // Allows the user to move the node. Can be: true or false.
125
+ nodeMovable: true,
126
+
124
127
  // The size of the node sizing area that extends around the node, over
125
128
  // which the mouse pointer will change to the sizing arrows.
126
129
  nodeSizingArea: 10,
@@ -600,6 +603,9 @@ const verticalDefaultLayout = {
600
603
  // Allows the user to resize the node.
601
604
  nodeResizable: false,
602
605
 
606
+ // Allows the user to move the node. Can be: true or false.
607
+ nodeMovable: true,
608
+
603
609
  // The size of the node sizing area that extends around the node, over
604
610
  // which the mouse pointer will change to the sizing arrows.
605
611
  nodeSizingArea: 10,