@elyra/canvas 12.42.0 → 12.44.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 (185) hide show
  1. package/dist/{canvas-constants-ff5cf88e.js → canvas-constants-089e7830.js} +2 -2
  2. package/dist/{canvas-constants-ff5cf88e.js.map → canvas-constants-089e7830.js.map} +1 -1
  3. package/dist/{canvas-constants-13b58448.js → canvas-constants-69e90162.js} +2 -2
  4. package/dist/{canvas-constants-13b58448.js.map → canvas-constants-69e90162.js.map} +1 -1
  5. package/dist/canvas-controller-3e6b8ce4.js +2 -0
  6. package/dist/canvas-controller-3e6b8ce4.js.map +1 -0
  7. package/dist/canvas-controller-c6274fad.js +2 -0
  8. package/dist/canvas-controller-c6274fad.js.map +1 -0
  9. package/dist/{canvas-logger-295dafe4.js → canvas-logger-6f4b2551.js} +2 -2
  10. package/dist/{canvas-logger-295dafe4.js.map → canvas-logger-6f4b2551.js.map} +1 -1
  11. package/dist/{canvas-logger-e07a0b4a.js → canvas-logger-ab9d9048.js} +2 -2
  12. package/dist/{canvas-logger-e07a0b4a.js.map → canvas-logger-ab9d9048.js.map} +1 -1
  13. package/dist/common-canvas-6ed21ab6.js +2 -0
  14. package/dist/common-canvas-6ed21ab6.js.map +1 -0
  15. package/dist/common-canvas-8abf016c.js +2 -0
  16. package/dist/common-canvas-8abf016c.js.map +1 -0
  17. package/dist/common-canvas.es.js +1 -1
  18. package/dist/common-canvas.es.js.map +1 -1
  19. package/dist/common-canvas.js +1 -1
  20. package/dist/common-canvas.js.map +1 -1
  21. package/dist/common-properties-88377242.js +2 -0
  22. package/dist/common-properties-88377242.js.map +1 -0
  23. package/dist/common-properties-b295acc8.js +2 -0
  24. package/dist/common-properties-b295acc8.js.map +1 -0
  25. package/dist/context-menu-wrapper-949393c7.js +2 -0
  26. package/dist/context-menu-wrapper-949393c7.js.map +1 -0
  27. package/dist/context-menu-wrapper-f62dfcdb.js +2 -0
  28. package/dist/context-menu-wrapper-f62dfcdb.js.map +1 -0
  29. package/dist/{createClass-440000a3.js → createClass-02596015.js} +2 -2
  30. package/dist/createClass-02596015.js.map +1 -0
  31. package/dist/createClass-155bf7da.js +2 -0
  32. package/dist/createClass-155bf7da.js.map +1 -0
  33. package/dist/datarecord-metadata-v3-schema-07d18e19.js +2 -0
  34. package/dist/{datarecord-metadata-v3-schema-98ec66e9.js.map → datarecord-metadata-v3-schema-07d18e19.js.map} +1 -1
  35. package/dist/datarecord-metadata-v3-schema-df939dd1.js +2 -0
  36. package/dist/{datarecord-metadata-v3-schema-dba0b214.js.map → datarecord-metadata-v3-schema-df939dd1.js.map} +1 -1
  37. package/dist/defineProperty-ad55dbff.js +2 -0
  38. package/dist/{defineProperty-3dc7d8d0.js.map → defineProperty-ad55dbff.js.map} +1 -1
  39. package/dist/{defineProperty-6d406743.js → defineProperty-bcc9968d.js} +2 -2
  40. package/dist/{defineProperty-6d406743.js.map → defineProperty-bcc9968d.js.map} +1 -1
  41. package/dist/flexible-table-c6a8b402.js +2 -0
  42. package/dist/{flexible-table-7c7de0f9.js.map → flexible-table-c6a8b402.js.map} +1 -1
  43. package/dist/flexible-table-f7b294a0.js +2 -0
  44. package/dist/{flexible-table-35e9922a.js.map → flexible-table-f7b294a0.js.map} +1 -1
  45. package/dist/{icon-9edff40c.js → icon-56b27c4f.js} +2 -2
  46. package/dist/{icon-9edff40c.js.map → icon-56b27c4f.js.map} +1 -1
  47. package/dist/{icon-e622f99b.js → icon-8ec2f0ec.js} +2 -2
  48. package/dist/{icon-e622f99b.js.map → icon-8ec2f0ec.js.map} +1 -1
  49. package/dist/index-01cbacf9.js +2 -0
  50. package/dist/{index-e2f8a935.js.map → index-01cbacf9.js.map} +1 -1
  51. package/dist/index-79543d41.js +2 -0
  52. package/dist/{index-94fec521.js.map → index-79543d41.js.map} +1 -1
  53. package/dist/inherits-42ae8426.js +2 -0
  54. package/dist/inherits-42ae8426.js.map +1 -0
  55. package/dist/inherits-75817f22.js +2 -0
  56. package/dist/inherits-75817f22.js.map +1 -0
  57. package/dist/isArrayLikeObject-04f333a5.js +1 -1
  58. package/dist/isArrayLikeObject-04f333a5.js.map +1 -1
  59. package/dist/isArrayLikeObject-7a30aa4b.js +1 -1
  60. package/dist/isArrayLikeObject-7a30aa4b.js.map +1 -1
  61. package/dist/lib/canvas-controller.es.js +1 -1
  62. package/dist/lib/canvas-controller.js +1 -1
  63. package/dist/lib/canvas.es.js +1 -1
  64. package/dist/lib/canvas.js +1 -1
  65. package/dist/lib/command-stack.es.js +1 -1
  66. package/dist/lib/command-stack.es.js.map +1 -1
  67. package/dist/lib/command-stack.js +1 -1
  68. package/dist/lib/command-stack.js.map +1 -1
  69. package/dist/lib/context-menu.es.js +1 -1
  70. package/dist/lib/context-menu.js +1 -1
  71. package/dist/lib/properties/clem.es.js +2 -0
  72. package/dist/lib/properties/clem.es.js.map +1 -0
  73. package/dist/lib/properties/clem.js +2 -0
  74. package/dist/lib/properties/clem.js.map +1 -0
  75. package/dist/lib/properties/field-picker.es.js +1 -1
  76. package/dist/lib/properties/field-picker.js +1 -1
  77. package/dist/lib/properties/flexible-table.es.js +1 -1
  78. package/dist/lib/properties/flexible-table.js +1 -1
  79. package/dist/lib/properties/getPythonHints.es.js +2 -0
  80. package/dist/lib/properties/getPythonHints.es.js.map +1 -0
  81. package/dist/lib/properties/getPythonHints.js +2 -0
  82. package/dist/lib/properties/getPythonHints.js.map +1 -0
  83. package/dist/lib/properties.es.js +1 -1
  84. package/dist/lib/properties.js +1 -1
  85. package/dist/lib/tooltip.es.js +1 -1
  86. package/dist/lib/tooltip.es.js.map +1 -1
  87. package/dist/lib/tooltip.js +1 -1
  88. package/dist/lib/tooltip.js.map +1 -1
  89. package/dist/styles/common-canvas.min.css +1 -1
  90. package/dist/styles/common-canvas.min.css.map +1 -1
  91. package/dist/toolbar-235dfb9d.js +2 -0
  92. package/dist/toolbar-235dfb9d.js.map +1 -0
  93. package/dist/toolbar-6607e35c.js +2 -0
  94. package/dist/toolbar-6607e35c.js.map +1 -0
  95. package/package.json +3 -3
  96. package/rollup.config.js +2 -0
  97. package/src/color-picker/color-picker.jsx +5 -1
  98. package/src/common-canvas/canvas-controller.js +56 -1
  99. package/src/common-canvas/cc-central-items.jsx +0 -4
  100. package/src/common-canvas/cc-toolbar.jsx +35 -13
  101. package/src/common-canvas/common-canvas-utils.js +73 -2
  102. package/src/common-canvas/common-canvas.scss +8 -8
  103. package/src/common-canvas/constants/canvas-constants.js +1 -0
  104. package/src/common-canvas/svg-canvas-d3.scss +3 -2
  105. package/src/common-canvas/svg-canvas-renderer.js +184 -94
  106. package/src/common-canvas/svg-canvas-utils-external.js +1 -1
  107. package/src/common-canvas/svg-canvas-utils-links.js +28 -32
  108. package/src/common-canvas/svg-canvas-utils-nodes.js +5 -13
  109. package/src/common-properties/components/field-picker/field-picker.jsx +4 -0
  110. package/src/common-properties/controls/checkbox/checkbox.scss +0 -1
  111. package/src/common-properties/controls/expression/expression.jsx +3 -5
  112. package/src/common-properties/controls/expression/languages/python-hint.js +18 -30
  113. package/src/common-properties/controls/expression/languages/r-hint.js +16 -8
  114. package/src/common-properties/index.js +4 -2
  115. package/src/icons/icon.scss +1 -1
  116. package/src/index.js +2 -2
  117. package/src/notification-panel/notification-panel.jsx +82 -56
  118. package/src/notification-panel/notification-panel.scss +42 -40
  119. package/src/object-model/config-utils.js +2 -2
  120. package/src/object-model/layout-dimensions.js +82 -87
  121. package/src/object-model/object-model-utils.js +271 -0
  122. package/src/object-model/object-model.js +47 -245
  123. package/src/object-model/redux/reducers/canvasinfo.js +7 -11
  124. package/src/object-model/redux/reducers/canvastoolbar.js +5 -6
  125. package/src/palette/palette-dialog-topbar.jsx +27 -38
  126. package/src/palette/palette-flyout-content-category.jsx +25 -6
  127. package/src/palette/palette.scss +8 -40
  128. package/src/toolbar/index.js +18 -0
  129. package/src/toolbar/toolbar-action-item.jsx +42 -11
  130. package/src/toolbar/toolbar-button-item.jsx +49 -21
  131. package/src/toolbar/toolbar-divider-item.jsx +1 -1
  132. package/src/toolbar/toolbar-overflow-item.jsx +14 -6
  133. package/src/toolbar/toolbar-sub-menu-item.jsx +6 -5
  134. package/src/toolbar/toolbar-sub-menu.jsx +4 -6
  135. package/src/toolbar/toolbar-sub-panel.jsx +31 -18
  136. package/src/toolbar/toolbar-sub-utils.js +21 -12
  137. package/src/toolbar/toolbar.jsx +83 -26
  138. package/src/toolbar/toolbar.scss +47 -47
  139. package/src/tooltip/tooltip.jsx +56 -10
  140. package/stats.html +1 -1
  141. package/assets/images/palette/close_32.svg +0 -1
  142. package/assets/images/palette/palette_close.svg +0 -4
  143. package/assets/images/palette/palette_grid_deselected.svg +0 -2
  144. package/assets/images/palette/palette_grid_hover.svg +0 -2
  145. package/assets/images/palette/palette_grid_selected.svg +0 -2
  146. package/assets/images/palette/palette_list_deselected.svg +0 -1
  147. package/assets/images/palette/palette_list_hover.svg +0 -1
  148. package/assets/images/palette/palette_list_selected.svg +0 -1
  149. package/assets/images/palette/palette_open.svg +0 -4
  150. package/assets/images/zoom_to_fit.svg +0 -8
  151. package/dist/canvas-controller-a53943e4.js +0 -2
  152. package/dist/canvas-controller-a53943e4.js.map +0 -1
  153. package/dist/canvas-controller-cb1d7420.js +0 -2
  154. package/dist/canvas-controller-cb1d7420.js.map +0 -1
  155. package/dist/common-canvas-42027a3f.js +0 -2
  156. package/dist/common-canvas-42027a3f.js.map +0 -1
  157. package/dist/common-canvas-f758ff42.js +0 -2
  158. package/dist/common-canvas-f758ff42.js.map +0 -1
  159. package/dist/common-properties-2e1b7ec7.js +0 -2
  160. package/dist/common-properties-2e1b7ec7.js.map +0 -1
  161. package/dist/common-properties-5e8870e3.js +0 -2
  162. package/dist/common-properties-5e8870e3.js.map +0 -1
  163. package/dist/context-menu-wrapper-49f9a1af.js +0 -2
  164. package/dist/context-menu-wrapper-49f9a1af.js.map +0 -1
  165. package/dist/context-menu-wrapper-5d6a399f.js +0 -2
  166. package/dist/context-menu-wrapper-5d6a399f.js.map +0 -1
  167. package/dist/createClass-440000a3.js.map +0 -1
  168. package/dist/createClass-5ca26865.js +0 -2
  169. package/dist/createClass-5ca26865.js.map +0 -1
  170. package/dist/datarecord-metadata-v3-schema-98ec66e9.js +0 -2
  171. package/dist/datarecord-metadata-v3-schema-dba0b214.js +0 -2
  172. package/dist/defineProperty-3dc7d8d0.js +0 -2
  173. package/dist/flexible-table-35e9922a.js +0 -2
  174. package/dist/flexible-table-7c7de0f9.js +0 -2
  175. package/dist/index-94fec521.js +0 -2
  176. package/dist/index-e2f8a935.js +0 -2
  177. package/dist/inherits-226dfdb2.js +0 -2
  178. package/dist/inherits-226dfdb2.js.map +0 -1
  179. package/dist/inherits-41673c87.js +0 -2
  180. package/dist/inherits-41673c87.js.map +0 -1
  181. package/dist/toolbar-6acda0a2.js +0 -2
  182. package/dist/toolbar-6acda0a2.js.map +0 -1
  183. package/dist/toolbar-d5647da2.js +0 -2
  184. package/dist/toolbar-d5647da2.js.map +0 -1
  185. package/src/palette/palette-dialog-topbar-three-way-icon.jsx +0 -82
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2023 Elyra Authors
2
+ * Copyright 2024 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.
@@ -20,46 +20,59 @@ import PropTypes from "prop-types";
20
20
  import { adjustSubAreaPosition, generateSubAreaStyle } from "./toolbar-sub-utils.js";
21
21
 
22
22
  const ESC_KEY = 27;
23
+ const LEFT_ARROW_KEY = 37;
24
+ const RIGHT_ARROW_KEY = 39;
25
+
23
26
 
24
27
  class ToolbarSubPanel extends React.Component {
25
28
  constructor(props) {
26
29
  super(props);
27
30
 
28
- this.areaRef = React.createRef();
29
-
30
- this.onClick = this.onClick.bind(this);
31
31
  this.onKeyDown = this.onKeyDown.bind(this);
32
+ this.closeSubPanel = this.closeSubPanel.bind(this);
32
33
  }
33
34
 
34
35
  componentDidMount() {
35
- if (this.props.containingDivId) {
36
- adjustSubAreaPosition(this.areaRef,
37
- this.props.containingDivId, this.props.expandDirection, this.props.actionItemRect);
38
- }
36
+ adjustSubAreaPosition(this.areaRef,
37
+ this.props.containingDivId, this.props.expandDirection, this.props.actionItemRect);
39
38
  }
40
39
 
41
- onClick() {
42
- this.props.closeSubArea();
40
+ componentDidUpdate() {
41
+ adjustSubAreaPosition(this.areaRef,
42
+ this.props.containingDivId, this.props.expandDirection, this.props.actionItemRect);
43
43
  }
44
44
 
45
45
  onKeyDown(evt) {
46
46
  if (evt.keyCode === ESC_KEY) {
47
47
  this.props.closeSubArea();
48
48
  evt.stopPropagation();
49
+
50
+ } else if (evt.keyCode === LEFT_ARROW_KEY || evt.keyCode === RIGHT_ARROW_KEY) {
51
+ evt.stopPropagation();
49
52
  }
50
53
  }
51
54
 
55
+ // If the user clicks the panel background, by default focus would go
56
+ // through to the toolbar and focus would be lost from this sub-panel.
57
+ // This method prevents any focus event going through to the toolbar.
58
+ onFocus(evt) {
59
+ evt.stopPropagation();
60
+ evt.preventDefault();
61
+ }
62
+
63
+ closeSubPanel(evt) {
64
+ this.props.closeSubArea(); // Don't pass a paremeter otherwise it will check closeSubAreaOnClick.
65
+ }
66
+
52
67
  render() {
53
68
  const style = generateSubAreaStyle(this.props.expandDirection, this.props.actionItemRect);
54
69
 
55
70
  if (this.props.subPanel) {
56
- const subPanel = typeof this.props.subPanel === "object"
57
- ? this.props.subPanel
58
- : (<this.props.subPanel closeSubPanel={this.props.closeSubArea} subPanelData={this.props.subPanelData} />);
59
-
60
71
  return (
61
- <div ref={this.areaRef} style={style} className={"toolbar-popover-list subpanel"} onClick={this.onClick} onKeyDown={this.onKeyDown}>
62
- {subPanel}
72
+ <div ref={(ref) => (this.areaRef = ref)} style={style} className={"toolbar-popover-list subpanel"} tabIndex={-1}
73
+ onKeyDown={this.onKeyDown} onFocus={this.onFocus}
74
+ >
75
+ <this.props.subPanel closeSubPanel={this.closeSubPanel} subPanelData={this.props.subPanelData} />
63
76
  </div>
64
77
  );
65
78
  }
@@ -69,11 +82,11 @@ class ToolbarSubPanel extends React.Component {
69
82
  }
70
83
 
71
84
  ToolbarSubPanel.propTypes = {
72
- subPanel: PropTypes.any,
85
+ subPanel: PropTypes.any.isRequired,
73
86
  subPanelData: PropTypes.object,
74
87
  closeSubArea: PropTypes.func,
75
88
  setToolbarFocusAction: PropTypes.func,
76
- actionItemRect: PropTypes.object.isRequired,
89
+ actionItemRect: PropTypes.object,
77
90
  expandDirection: PropTypes.string.isRequired,
78
91
  containingDivId: PropTypes.string
79
92
  };
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2023 Elyra Authors
2
+ * Copyright 2024 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.
@@ -15,20 +15,25 @@
15
15
  */
16
16
 
17
17
  // These utility functions are used by both toolbar-sub-menu.jsx AND
18
- // toolbar-sub-panel.jsx to position the areaEf (menu or panel) relative
19
- // to the parent actionItemRect, passed in, in the direction indicated by
20
- // the expandDirection parameter and constrained within the <div>
21
- // specified by the containingDivId parameter.
18
+ // toolbar-sub-panel.jsx to position the sub-area (sub-menu or sub-panel)
19
+ // relative to the parent actionItemRect, passed in, in the direction
20
+ // indicated by the expandDirection parameter and constrained within
21
+ // the <div> specified by the containingDivId parameter.
22
22
 
23
23
 
24
24
  // Adjust the position of the sub-area to make sure it doesn't extend
25
25
  // outside the containing divs boundary. We need to do this after the subarea
26
26
  // has been mounted so we can query its size and position.
27
27
  export function adjustSubAreaPosition(areaRef, containingDivId, expandDirection, actionItemRect) {
28
+ if (!areaRef || !actionItemRect || !containingDivId) {
29
+ return;
30
+ }
28
31
  const containingDiv = document.getElementById(containingDivId);
29
- const containingDivRect = containingDiv.getBoundingClientRect();
32
+ const containingDivRect = containingDiv
33
+ ? containingDiv.getBoundingClientRect()
34
+ : { top: -1000, bottom: 1000, left: -1000, right: 1000 }; // To enable Jest tests.
30
35
 
31
- const thisAreaRect = areaRef.current.getBoundingClientRect();
36
+ const thisAreaRect = areaRef.getBoundingClientRect();
32
37
 
33
38
  const outsideBottom = thisAreaRect.bottom - containingDivRect.bottom;
34
39
  const outsideRight = thisAreaRect.right - containingDivRect.right;
@@ -40,28 +45,32 @@ export function adjustSubAreaPosition(areaRef, containingDivId, expandDirection,
40
45
  ? actionItemRect.top - thisAreaRect.height
41
46
  : actionItemRect.bottom - outsideBottom;
42
47
 
43
- areaRef.current.style.top = newTop + "px";
48
+ areaRef.style.top = newTop + "px";
44
49
  }
45
50
 
46
51
  if (outsideRight > 0) {
47
- const newLeft = actionItemRect.left - outsideRight - 2;
48
- areaRef.current.style.left = newLeft + "px";
52
+ const newLeft = actionItemRect.left - outsideRight;
53
+ areaRef.style.left = newLeft + "px";
49
54
  }
50
55
 
51
56
  } else {
52
57
  if (outsideBottom > 0) {
53
58
  const newTop = thisAreaRect.top - outsideBottom - 2;
54
- areaRef.current.style.top = newTop + "px";
59
+ areaRef.style.top = newTop + "px";
55
60
  }
56
61
 
57
62
  if (outsideRight > 0) {
58
63
  const newLeft = actionItemRect.left - thisAreaRect.width;
59
- areaRef.current.style.left = newLeft + "px";
64
+ areaRef.style.left = newLeft + "px";
60
65
  }
61
66
  }
62
67
  }
63
68
 
64
69
  export function generateSubAreaStyle(expandDirection, actionItemRect) {
70
+ if (!actionItemRect) {
71
+ return null;
72
+ }
73
+
65
74
  if (expandDirection === "vertical") {
66
75
  return {
67
76
  top: actionItemRect.bottom + 1,
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2017-2023 Elyra Authors
2
+ * Copyright 2017-2024 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.
@@ -69,6 +69,7 @@ class Toolbar extends React.Component {
69
69
  this.generateToolbarItems = this.generateToolbarItems.bind(this);
70
70
  this.setFocusAction = this.setFocusAction.bind(this);
71
71
  this.setFocusOnItem = this.setFocusOnItem.bind(this);
72
+ this.closeAnyOpenSubArea = this.closeAnyOpenSubArea.bind(this);
72
73
  }
73
74
 
74
75
  // If, after updating, we are left in a situation where this.state.focusAction
@@ -76,13 +77,12 @@ class Toolbar extends React.Component {
76
77
  // item. This might happen when an item with focus is activated and the action it
77
78
  // performs causes itself to become disabled. For example, if the delete item is
78
79
  // activated the selected objects are deleted and since no objects are now selected
79
- // the delete item (which has focus) will become disabled.
80
+ // the delete item (which has focus) will become disabled. It may also happen if the
81
+ // toolbar config is updated and the current focusAction item is removed.
80
82
  componentDidUpdate() {
81
- if (this.isFocusInToolbar) {
82
- const index = this.getFocusableItemRefs().findIndex((item) => this.getRefAction(item) === this.state.focusAction);
83
- if (index === -1) {
84
- this.setFocusOnFirstItem();
85
- }
83
+ const index = this.getFocusableItemRefs().findIndex((item) => this.getRefAction(item) === this.state.focusAction);
84
+ if (index === -1) {
85
+ this.setFocusOnFirstItem();
86
86
  }
87
87
  }
88
88
 
@@ -106,11 +106,15 @@ class Toolbar extends React.Component {
106
106
  }
107
107
  }
108
108
 
109
- // When focus leaves the toolbar make sure we record it so we don't
110
- // accidentally set focus on a toolbar item when re-rendering with
111
- // the focus elsewhere.
112
- onBlur() {
113
- this.isFocusInToolbar = false;
109
+ // When focus leaves the toolbar make sure we record it. This prevents
110
+ // us accidentally setting focus on a toolbar item when the toolbar
111
+ // re-renders with the focus elsewhere.
112
+ onBlur(evt) {
113
+ // If the focus is being moved to an object outside the toolbar div
114
+ // we set isFocusInToolbar to false.
115
+ if (!evt.relatedTarget?.closest(".toolbar-div")) {
116
+ this.isFocusInToolbar = false;
117
+ }
114
118
  }
115
119
 
116
120
  // This is called when the user presses a key with focus on one of the
@@ -136,17 +140,51 @@ class Toolbar extends React.Component {
136
140
  }
137
141
 
138
142
  // When the toolbar resizes, check each toolbar item to see if it has
139
- // a sub-menu open and, if it does, close it.
143
+ // an open sub-area and, if that item is not a focusable item, close
144
+ // the sub-area. The item may no longer be focusable it is it was wrapped
145
+ // into the overflow menu. Also, check to see if the current focus action
146
+ // item is focusable and, if not, set focus on the first focusable item.
140
147
  onToolbarResize() {
141
- this.leftItemRefs.forEach((ref) => this.closeSubMenuOnRef(ref));
142
- this.rightItemRefs.forEach((ref) => this.closeSubMenuOnRef(ref));
143
- this.overflowItemRefs.forEach((ref) => this.closeOverflowMenuOnRef(ref));
148
+ const focusableItemRefs = this.getFocusableItemRefs();
149
+ // Note: isFocusActionFocusable needs to be calculated here before any
150
+ // update to the toolbar caused by the code in the subsequent if ststement.
151
+ const isFocusActionFocusable = this.isFocusActionFocusable(this.state.focusAction, focusableItemRefs);
152
+ const refWithOpenSubArea = this.getRefWithOpenSubArea();
153
+
154
+ if (refWithOpenSubArea) {
155
+ const action = refWithOpenSubArea.current.getAction();
156
+ const isFocusActionWithOpenSubAreaFocusable = this.isFocusActionFocusable(action, focusableItemRefs);
157
+
158
+ if (!isFocusActionWithOpenSubAreaFocusable) {
159
+ refWithOpenSubArea.current.closeSubArea();
160
+
161
+ } else {
162
+ // This forces a refresh that will cause the position of any
163
+ // open sub-area to be recaulculated based on the new toolbar width.
164
+ this.setFocusAction(this.state.focusAction);
165
+ }
166
+ }
144
167
 
145
- if (this.isFocusInToolbar) {
168
+ // If the focus action item is not focusable (maybe because it has been
169
+ // moved into the overflow menu) then set focus on the first focusable item.
170
+ if (!isFocusActionFocusable) {
146
171
  this.setFocusOnFirstItem();
147
172
  }
148
173
  }
149
174
 
175
+ // Returns the ref to any item that currently has an open sub-area or null
176
+ // if no item has an open sub-area.
177
+ getRefWithOpenSubArea() {
178
+ let subAreaOpenRef = this.leftItemRefs.find((ref) => ref.current.isSubAreaDisplayed());
179
+ if (!subAreaOpenRef) {
180
+ subAreaOpenRef = this.rightItemRefs.find((ref) => ref.current.isSubAreaDisplayed());
181
+ }
182
+ if (!subAreaOpenRef) {
183
+ subAreaOpenRef = this.overflowItemRefs.find((ref) => ref.current.isSubAreaDisplayed());
184
+ }
185
+ return subAreaOpenRef;
186
+ }
187
+
150
188
  // Either sets the focus on the item for the action passed in or, if
151
189
  // no action is passed in, set the focus on the current focusAction.
152
190
  // Setting the current focusAction is used to return focus back to an
@@ -192,18 +230,18 @@ class Toolbar extends React.Component {
192
230
 
193
231
  getPreviousItemRef(focusableItemRefs) {
194
232
  const index = focusableItemRefs.findIndex((item) => this.getRefAction(item) === this.state.focusAction);
195
- if (index > 0) {
196
- return focusableItemRefs[index - 1];
233
+ if (index === 0) {
234
+ return focusableItemRefs[focusableItemRefs.length - 1];
197
235
  }
198
- return null;
236
+ return focusableItemRefs[index - 1];
199
237
  }
200
238
 
201
239
  getNextItemRef(focusableItemRefs) {
202
240
  const index = focusableItemRefs.findIndex((item) => this.getRefAction(item) === this.state.focusAction);
203
- if (index < focusableItemRefs.length - 1) {
204
- return focusableItemRefs[index + 1];
241
+ if (index === focusableItemRefs.length - 1) {
242
+ return focusableItemRefs[0];
205
243
  }
206
- return null;
244
+ return focusableItemRefs[index + 1];
207
245
  }
208
246
 
209
247
  getRefAction(ref) {
@@ -313,6 +351,14 @@ class Toolbar extends React.Component {
313
351
  return index;
314
352
  }
315
353
 
354
+ // Returns true of the current focus action item is one of the focusable
355
+ // items. (It may not be if it has been placed in the overflow menu).
356
+ isFocusActionFocusable(focusAction, focusableItemRefs) {
357
+ const indexFocusAction = focusableItemRefs.findIndex((ref) =>
358
+ ref.current.props.actionObj?.action === focusAction);
359
+ return indexFocusAction > -1;
360
+ }
361
+
316
362
  // Returns a reference to the first item that is not on the
317
363
  // top (visible) row of the toolbar.
318
364
  findFirstRightItemRefNotOnTopRow() {
@@ -386,6 +432,7 @@ class Toolbar extends React.Component {
386
432
  toolbarFocusAction={this.state.focusAction}
387
433
  setToolbarFocusAction={this.setFocusOnItem}
388
434
  isFocusInToolbar={this.isFocusInToolbar}
435
+ closeAnyOpenSubArea={this.closeAnyOpenSubArea}
389
436
  size={this.props.size}
390
437
  />
391
438
  );
@@ -421,6 +468,7 @@ class Toolbar extends React.Component {
421
468
  toolbarFocusAction={this.state.focusAction}
422
469
  setToolbarFocusAction={this.setFocusOnItem}
423
470
  isFocusInToolbar={this.isFocusInToolbar}
471
+ closeAnyOpenSubArea={this.closeAnyOpenSubArea}
424
472
  />
425
473
  );
426
474
 
@@ -440,15 +488,24 @@ class Toolbar extends React.Component {
440
488
  return subMenuActions;
441
489
  }
442
490
 
443
- closeSubMenuOnRef(ref) {
444
- if (ref.current.state.subAreaDisplayed) {
491
+ closeAnyOpenSubArea() {
492
+ this.leftItemRefs.forEach((ref) => this.closeSubAreaOnRef(ref));
493
+ this.rightItemRefs.forEach((ref) => this.closeSubAreaOnRef(ref));
494
+ this.overflowItemRefs.forEach((ref) => this.closeOverflowMenuOnRef(ref));
495
+ }
496
+
497
+ closeSubAreaOnRef(ref) {
498
+ if (ref.current.props.actionObj.setExtIsSubAreaDisplayed) {
499
+ ref.current.props.actionObj.setExtIsSubAreaDisplayed(false);
500
+
501
+ } else if (ref.current.state.subAreaDisplayed) {
445
502
  ref.current.closeSubArea();
446
503
  }
447
504
  }
448
505
 
449
506
  closeOverflowMenuOnRef(ref) {
450
507
  if (ref.current.state.showExtendedMenu) {
451
- ref.current.closeSubMenu();
508
+ ref.current.closeSubArea();
452
509
  }
453
510
  }
454
511
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2017-2023 Elyra Authors
2
+ * Copyright 2017-2024 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.
@@ -30,16 +30,16 @@ $toolbar-divider-width: 1px;
30
30
  background-color: $ui-01;
31
31
  box-sizing: content-box;
32
32
  position: relative;
33
-
34
33
  display: flex;
35
34
  justify-content: space-between;
35
+ outline: none; // Turn outline off and use carbon style for focus below.
36
36
 
37
37
  /* Prevent elements from being dragged onto canvas */
38
38
  -webkit-user-drag: none;
39
39
 
40
40
  &:focus {
41
- border-color: $interactive-01;
42
- box-shadow: inset 0 0 0 1px $interactive-01, inset 0 0 0 2px $ui-background;
41
+ border-color: $interactive-03;
42
+ box-shadow: inset 0 0 0 2px $interactive-03;
43
43
  }
44
44
 
45
45
  /* Small size toolbar */
@@ -106,6 +106,7 @@ $toolbar-divider-width: 1px;
106
106
  & button {
107
107
  padding: 0;
108
108
  min-height: 30px;
109
+ height: $toolbar-button-height;
109
110
  }
110
111
 
111
112
  &.default,
@@ -201,10 +202,15 @@ $toolbar-divider-width: 1px;
201
202
  &.disabled.default {
202
203
  color: $disabled-02;
203
204
  fill: $disabled-02; // For custom svg images
205
+ & .toolbar-tick-mark {
206
+ fill: $disabled-02;
207
+ }
208
+ & .toolbar-text-content {
209
+ stroke: $disabled-02;
210
+ }
204
211
  }
205
212
 
206
213
  .toolbar-icon {
207
- height: $toolbar-icon-size;
208
214
  & svg {
209
215
  height: $toolbar-icon-size;
210
216
  width: $toolbar-icon-size;
@@ -212,52 +218,52 @@ $toolbar-divider-width: 1px;
212
218
  -webkit-user-drag: none;
213
219
  }
214
220
  }
215
- }
216
221
 
217
- .toolbar-text-content {
218
- position: absolute;
219
- top: 14px;
220
- font-size: 9px;
221
- line-height: 9px;
222
- color: $icon-01;
223
- }
222
+ .toolbar-text-content {
223
+ position: absolute;
224
+ top: 14px;
225
+ font-size: 9px;
226
+ line-height: 9px;
227
+ color: $icon-01;
228
+ }
224
229
 
225
- .toolbar-icon-label {
226
- line-height: 16px;
230
+ .toolbar-icon-label {
231
+ line-height: 16px;
227
232
 
228
- &.before {
229
- padding-right: 8px;
230
- }
233
+ &.before {
234
+ padding-right: 8px;
235
+ }
231
236
 
232
- &.after {
233
- padding-left: 8px;
237
+ &.after {
238
+ padding-left: 8px;
239
+ }
240
+
241
+ &.overflow {
242
+ padding-left: 8px;
243
+ word-break: break-word;
244
+ hyphens: auto;
245
+ }
234
246
  }
235
247
 
236
- &.overflow {
248
+ .toolbar-up-down-chevron {
237
249
  padding-left: 8px;
238
- word-break: break-word;
239
- hyphens: auto;
250
+ height: 16px;
251
+ pointer-events: none;
240
252
  }
241
- }
242
253
 
243
- .toolbar-up-down-chevron {
244
- padding-left: 8px;
245
- height: 16px;
246
- pointer-events: none;
247
- }
248
-
249
- // Triangular tick mark in bottom right corner of an expandable icon
250
- .toolbar-tick-svg {
251
- position: absolute;
252
- bottom: 0;
253
- right: 0;
254
- height: $toolbar-button-height;
255
- width: $toolbar-button-height;
256
- pointer-events: none;
257
- }
254
+ // Triangular tick mark in bottom right corner of an expandable icon
255
+ .toolbar-tick-svg {
256
+ position: absolute;
257
+ bottom: 0;
258
+ right: 0;
259
+ height: $toolbar-button-height;
260
+ width: $toolbar-button-height;
261
+ pointer-events: none;
258
262
 
259
- .toolbar-tick-mark {
260
- fill: $interactive-02;
263
+ & .toolbar-tick-mark {
264
+ fill: $icon-01;
265
+ }
266
+ }
261
267
  }
262
268
 
263
269
  .toolbar-jsx-item {
@@ -285,14 +291,8 @@ $toolbar-divider-width: 1px;
285
291
  &.ghost {
286
292
  & button {
287
293
  background-color: $ui-03;
288
- border-color: transparent;
289
- box-shadow: none;
290
294
  border-bottom: 2px solid $interactive-01;
291
295
  }
292
- & button:focus {
293
- border-color: $interactive-01;
294
- box-shadow: inset 0 0 0 1px $interactive-01, inset 0 0 0 2px $ui-background;
295
- }
296
296
  }
297
297
  }
298
298
 
@@ -79,6 +79,13 @@ class ToolTip extends React.Component {
79
79
  if (tooltipTrigger && tooltip) {
80
80
  this.updateTooltipLayout(tooltip, tooltipTrigger, tooltip.getAttribute("direction"));
81
81
  }
82
+
83
+ const linkElement = this.targetRef.querySelector("a");
84
+
85
+ // Focus on link when tooltip with link is opened
86
+ if (linkElement) {
87
+ linkElement.focus();
88
+ }
82
89
  }
83
90
  }
84
91
  }
@@ -87,6 +94,10 @@ class ToolTip extends React.Component {
87
94
  if (evt.key === "Tab") {
88
95
  this.tabKeyPressed = true;
89
96
  }
97
+ if (evt.key === "Escape") {
98
+ this.triggerRef.focus();
99
+ this.setTooltipVisible(false);
100
+ }
90
101
  }
91
102
 
92
103
  getStyleValue(value) {
@@ -320,6 +331,9 @@ class ToolTip extends React.Component {
320
331
  // To prevent this default behavior, stopPropagation and preventDefault is used.
321
332
  evt.stopPropagation();
322
333
  evt.preventDefault();
334
+
335
+ // When tooltip with link is closed and another tooltip is opened newly opened tooltip should have focus.
336
+ this.triggerRef.focus();
323
337
  if (this.state.isTooltipVisible) {
324
338
  // Tooltip is visible and user clicks on trigger element again, hide tooltip
325
339
  this.setTooltipVisible(false);
@@ -405,22 +419,53 @@ class ToolTip extends React.Component {
405
419
  if (this.props.className) {
406
420
  tipClass += " " + this.props.className;
407
421
  }
408
-
422
+ let linkClicked = false;
409
423
  let link = null;
410
424
  if (this.state.isTooltipVisible && this.props.tooltipLinkHandler && this.props.link) {
411
425
  const linkInformation = this.props.tooltipLinkHandler(this.props.link);
412
426
  // Verify tooltipLinkHandler returns object in correct format
413
427
  if (typeof linkInformation === "object" && linkInformation.label && linkInformation.url) {
414
- link = (<Link
415
- className="tooltip-link"
416
- id={this.props.link.id}
417
- href={linkInformation.url}
418
- target="_blank"
419
- rel="noopener"
420
- visited={false}
428
+ link = (<div
429
+ ref={(ref) => (this.linkRef = ref)}
430
+ onKeyDown={(evt) => {
431
+ evt.stopPropagation();
432
+ evt.preventDefault();
433
+
434
+ // When 'Esc' is pressed shift the focus to tooltip icon so that user can navigate following elements.
435
+ if (evt.key === "Escape") {
436
+ this.triggerRef.focus();
437
+ this.setTooltipVisible(false);
438
+ } else if (evt.key === "Enter") { // Open active/highlighted link when Enter or Return is clicked.
439
+ const focusedElement = this.linkRef.children[0];
440
+ if (focusedElement) {
441
+ window.open(focusedElement, "_blank");
442
+ }
443
+ }
444
+ }}
445
+ onBlur={() => {
446
+ if (linkClicked) { // Keep tooltip open when link is clicked
447
+ this.setTooltipVisible(true);
448
+ } else { // Close the tooltip and shift focus to tooltip icon
449
+ this.triggerRef.focus();
450
+ this.setTooltipVisible(false);
451
+ }
452
+ }
453
+ }
454
+ onClick={() => {
455
+ linkClicked = true;
456
+ }}
421
457
  >
422
- {linkInformation.label}
423
- </Link>);
458
+ <Link
459
+ className="tooltip-link"
460
+ id={this.props.link.id}
461
+ href={linkInformation.url}
462
+ target="_blank"
463
+ rel="noopener"
464
+ visited={false}
465
+ >
466
+ {linkInformation.label}
467
+ </Link>
468
+ </div>);
424
469
  }
425
470
  }
426
471
 
@@ -435,6 +480,7 @@ class ToolTip extends React.Component {
435
480
  className={tipClass}
436
481
  aria-hidden={!this.state.isTooltipVisible}
437
482
  direction={this.props.direction}
483
+ ref={(ref) => (this.targetRef = ref)}
438
484
  >
439
485
  <svg className="tipArrow" x="0px" y="0px" viewBox="0 0 9.1 16.1">
440
486
  <polyline points="9.1,15.7 1.4,8.1 9.1,0.5" />