@elyra/canvas 12.41.0 → 12.42.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 (150) hide show
  1. package/dist/{_baseIteratee-05ccf6a8.js → _baseIteratee-148093b7.js} +3 -3
  2. package/dist/{_baseIteratee-05ccf6a8.js.map → _baseIteratee-148093b7.js.map} +1 -1
  3. package/dist/{canvas-constants-079172c0.js → canvas-constants-13b58448.js} +2 -2
  4. package/dist/{canvas-constants-079172c0.js.map → canvas-constants-13b58448.js.map} +1 -1
  5. package/dist/canvas-controller-a53943e4.js +2 -0
  6. package/dist/canvas-controller-a53943e4.js.map +1 -0
  7. package/dist/canvas-controller-cb1d7420.js +2 -0
  8. package/dist/canvas-controller-cb1d7420.js.map +1 -0
  9. package/dist/common-canvas-42027a3f.js +2 -0
  10. package/dist/common-canvas-42027a3f.js.map +1 -0
  11. package/dist/common-canvas-f758ff42.js +2 -0
  12. package/dist/common-canvas-f758ff42.js.map +1 -0
  13. package/dist/common-canvas.es.js +1 -1
  14. package/dist/common-canvas.es.js.map +1 -1
  15. package/dist/common-canvas.js +1 -1
  16. package/dist/common-canvas.js.map +1 -1
  17. package/dist/common-properties-2e1b7ec7.js +2 -0
  18. package/dist/common-properties-2e1b7ec7.js.map +1 -0
  19. package/dist/common-properties-5e8870e3.js +2 -0
  20. package/dist/common-properties-5e8870e3.js.map +1 -0
  21. package/dist/context-menu-wrapper-49f9a1af.js +2 -0
  22. package/dist/context-menu-wrapper-49f9a1af.js.map +1 -0
  23. package/dist/context-menu-wrapper-5d6a399f.js +2 -0
  24. package/dist/context-menu-wrapper-5d6a399f.js.map +1 -0
  25. package/dist/{datarecord-metadata-v3-schema-59505bc5.js → datarecord-metadata-v3-schema-98ec66e9.js} +2 -2
  26. package/dist/{datarecord-metadata-v3-schema-59505bc5.js.map → datarecord-metadata-v3-schema-98ec66e9.js.map} +1 -1
  27. package/dist/{flexible-table-43e2d052.js → flexible-table-35e9922a.js} +2 -2
  28. package/dist/{flexible-table-43e2d052.js.map → flexible-table-35e9922a.js.map} +1 -1
  29. package/dist/{flexible-table-63ffd573.js → flexible-table-7c7de0f9.js} +1 -1
  30. package/dist/{flexible-table-63ffd573.js.map → flexible-table-7c7de0f9.js.map} +1 -1
  31. package/dist/{icon-0390f1fe.js → icon-9edff40c.js} +2 -2
  32. package/dist/{icon-0390f1fe.js.map → icon-9edff40c.js.map} +1 -1
  33. package/dist/{index-57503b50.js → index-94fec521.js} +2 -2
  34. package/dist/{index-57503b50.js.map → index-94fec521.js.map} +1 -1
  35. package/dist/{index-1cd54914.js → index-e2f8a935.js} +2 -2
  36. package/dist/{index-1cd54914.js.map → index-e2f8a935.js.map} +1 -1
  37. package/dist/{isArrayLikeObject-36898fcb.js → isArrayLikeObject-7a30aa4b.js} +2 -2
  38. package/dist/{isArrayLikeObject-36898fcb.js.map → isArrayLikeObject-7a30aa4b.js.map} +1 -1
  39. package/dist/lib/canvas-controller.es.js +1 -1
  40. package/dist/lib/canvas-controller.js +1 -1
  41. package/dist/lib/canvas.es.js +1 -1
  42. package/dist/lib/canvas.js +1 -1
  43. package/dist/lib/context-menu.es.js +1 -1
  44. package/dist/lib/context-menu.js +1 -1
  45. package/dist/lib/properties/field-picker.es.js +1 -1
  46. package/dist/lib/properties/field-picker.js +1 -1
  47. package/dist/lib/properties/flexible-table.es.js +1 -1
  48. package/dist/lib/properties/flexible-table.js +1 -1
  49. package/dist/lib/properties.es.js +1 -1
  50. package/dist/lib/properties.js +1 -1
  51. package/dist/lib/tooltip.es.js +1 -1
  52. package/dist/lib/tooltip.es.js.map +1 -1
  53. package/dist/lib/tooltip.js +1 -1
  54. package/dist/lib/tooltip.js.map +1 -1
  55. package/dist/styles/common-canvas.min.css +1 -1
  56. package/dist/styles/common-canvas.min.css.map +1 -1
  57. package/dist/toolbar-6acda0a2.js +2 -0
  58. package/dist/toolbar-6acda0a2.js.map +1 -0
  59. package/dist/toolbar-d5647da2.js +2 -0
  60. package/dist/toolbar-d5647da2.js.map +1 -0
  61. package/package.json +12 -4
  62. package/src/color-picker/color-picker.jsx +92 -17
  63. package/src/command-actions/arrangeLayoutAction.js +7 -6
  64. package/src/command-actions/attachNodeToLinksAction.js +4 -4
  65. package/src/command-actions/collapseSuperNodeInPlaceAction.js +5 -5
  66. package/src/command-actions/colorSelectedObjectsAction.js +4 -4
  67. package/src/command-actions/commonPropertiesAction.js +1 -1
  68. package/src/command-actions/convertSuperNodeExternalToLocalAction.js +4 -4
  69. package/src/command-actions/convertSuperNodeLocalToExternalAction.js +4 -4
  70. package/src/command-actions/createAutoNodeAction.js +14 -5
  71. package/src/command-actions/createCommentAction.js +4 -10
  72. package/src/command-actions/createCommentLinkAction.js +4 -4
  73. package/src/command-actions/createNodeAction.js +13 -4
  74. package/src/command-actions/createNodeAttachLinksAction.js +4 -4
  75. package/src/command-actions/createNodeLinkAction.js +13 -4
  76. package/src/command-actions/createNodeLinkDetachedAction.js +4 -4
  77. package/src/command-actions/createNodeOnLinkAction.js +4 -4
  78. package/src/command-actions/createSuperNodeAction.js +7 -7
  79. package/src/command-actions/deconstructSuperNodeAction.js +5 -5
  80. package/src/command-actions/deleteLinkAction.js +4 -4
  81. package/src/command-actions/deleteObjectsAction.js +15 -6
  82. package/src/command-actions/disconnectObjectsAction.js +13 -4
  83. package/src/command-actions/displayPreviousPipelineAction.js +4 -4
  84. package/src/command-actions/displaySubPipelineAction.js +4 -4
  85. package/src/command-actions/editCommentAction.js +4 -4
  86. package/src/command-actions/editDecorationLabelAction.js +4 -4
  87. package/src/command-actions/expandSuperNodeInPlaceAction.js +5 -5
  88. package/src/command-actions/insertNodeIntoLinkAction.js +4 -4
  89. package/src/command-actions/moveObjectsAction.js +4 -4
  90. package/src/command-actions/pasteAction.js +16 -7
  91. package/src/command-actions/saveToPaletteAction.js +4 -4
  92. package/src/command-actions/setLinksStyleAction.js +4 -4
  93. package/src/command-actions/setNodeLabelAction.js +4 -4
  94. package/src/command-actions/setObjectsStyleAction.js +4 -4
  95. package/src/command-actions/sizeAndPositionObjectsAction.js +4 -4
  96. package/src/command-actions/updateLinkAction.js +4 -4
  97. package/src/common-canvas/canvas-controller-menu-utils.js +1 -1
  98. package/src/common-canvas/canvas-controller.js +78 -62
  99. package/src/common-canvas/cc-central-items.jsx +1 -1
  100. package/src/common-canvas/cc-context-toolbar.jsx +9 -13
  101. package/src/common-canvas/cc-toolbar.jsx +2 -0
  102. package/src/common-canvas/svg-canvas-renderer.js +6 -2
  103. package/src/common-canvas/svg-canvas-utils-drag-det-link.js +8 -1
  104. package/src/common-canvas/svg-canvas-utils-drag-new-link.js +1 -1
  105. package/src/common-properties/components/table-buttons/table-buttons.scss +0 -1
  106. package/src/common-properties/controls/expression/expression-builder/expression-builder.jsx +32 -26
  107. package/src/common-properties/controls/expression/expression.jsx +146 -117
  108. package/src/common-properties/controls/expression/expression.scss +43 -45
  109. package/src/common-properties/controls/expression/languages/CLEM-hint.js +86 -159
  110. package/src/common-properties/controls/expression/languages/python-hint.js +53 -104
  111. package/src/common-properties/controls/expression/languages/r-hint.js +55 -130
  112. package/src/common-properties/properties-controller.js +5 -0
  113. package/src/context-menu/common-context-menu.jsx +4 -1
  114. package/src/index.js +12 -2
  115. package/src/object-model/redux/canvas-store.js +4 -3
  116. package/src/toolbar/toolbar-action-item.jsx +90 -314
  117. package/src/toolbar/toolbar-button-item.jsx +354 -0
  118. package/src/toolbar/toolbar-divider-item.jsx +3 -4
  119. package/src/toolbar/toolbar-overflow-item.jsx +82 -36
  120. package/src/toolbar/toolbar-sub-menu-item.jsx +235 -0
  121. package/src/toolbar/toolbar-sub-menu.jsx +254 -0
  122. package/src/toolbar/toolbar-sub-panel.jsx +81 -0
  123. package/src/toolbar/toolbar-sub-utils.js +77 -0
  124. package/src/toolbar/toolbar.jsx +330 -146
  125. package/src/toolbar/toolbar.scss +22 -15
  126. package/src/tooltip/tooltip.jsx +9 -2
  127. package/stats.html +1 -1
  128. package/dist/canvas-controller-1e71b405.js +0 -2
  129. package/dist/canvas-controller-1e71b405.js.map +0 -1
  130. package/dist/canvas-controller-4bed5320.js +0 -2
  131. package/dist/canvas-controller-4bed5320.js.map +0 -1
  132. package/dist/common-canvas-097c5169.js +0 -2
  133. package/dist/common-canvas-097c5169.js.map +0 -1
  134. package/dist/common-canvas-e13c0858.js +0 -2
  135. package/dist/common-canvas-e13c0858.js.map +0 -1
  136. package/dist/common-properties-706cef87.js +0 -2
  137. package/dist/common-properties-706cef87.js.map +0 -1
  138. package/dist/common-properties-9bd69b61.js +0 -2
  139. package/dist/common-properties-9bd69b61.js.map +0 -1
  140. package/dist/context-menu-wrapper-3a7fdec8.js +0 -2
  141. package/dist/context-menu-wrapper-3a7fdec8.js.map +0 -1
  142. package/dist/context-menu-wrapper-fc85d853.js +0 -2
  143. package/dist/context-menu-wrapper-fc85d853.js.map +0 -1
  144. package/dist/toolbar-918ab52e.js +0 -2
  145. package/dist/toolbar-918ab52e.js.map +0 -1
  146. package/dist/toolbar-fdb750f9.js +0 -2
  147. package/dist/toolbar-fdb750f9.js.map +0 -1
  148. package/src/toolbar/toolbar-action-sub-area.jsx +0 -126
  149. package/src/toolbar/toolbar-overflow-menu.jsx +0 -77
  150. package/src/toolbar/toolbar-utils.js +0 -33
@@ -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.
@@ -31,36 +31,40 @@ export default class ExpressionBuilder extends React.Component {
31
31
  this.onChange = this.onChange.bind(this);
32
32
  this.onBlur = this.onBlur.bind(this);
33
33
  this.onSelectionChange = this.onSelectionChange.bind(this);
34
- this.lastCursorPos = null;
35
-
34
+ this.getCodemirrorState = this.getCodemirrorState.bind(this);
36
35
  }
37
36
 
38
37
  onChange(newValue) {
39
38
  const value = (typeof newValue === "string") ? newValue : newValue.toString();
40
- let cursor = this.editor.getCursor();
39
+ const somethingSelected = this.getCodemirrorState()?.selection.ranges.some((r) => !r.empty);
40
+ let cursor = this.getCodemirrorState()?.selection.main.head;
41
+ if (cursor === 0 && !somethingSelected) { // TODO: Doesn't work when I explicitly set the cursor to 0
42
+ // When nothing selected, set cursor at the end of the line
43
+ this.editor.dispatch({ selection: { anchor: this.getCodemirrorState()?.doc.length } });
44
+ cursor = this.getCodemirrorState()?.selection.main.head;
45
+ this.editor.focus();
46
+ }
41
47
  let selectionOffset = 1;
42
- if (this.editor.somethingSelected()) {
48
+ if (somethingSelected) {
43
49
  selectionOffset = 0;
44
- this.editor.replaceSelection(value);
50
+ this.editor.dispatch(this.getCodemirrorState()?.replaceSelection(value), { scrollIntoView: true });
51
+ this.editor.focus();
45
52
  } else {
46
53
  let buffer = " ";
47
- if (this.lastCursorPos) {
48
- this.editor.setCursor(this.lastCursorPos);
49
- cursor = this.lastCursorPos;
50
- }
51
54
  // if adding to a parenth/bracket/brace expression, no need for space
52
- const charBefore = this.editor.getLine(cursor.line)[cursor.ch - 1];
55
+ const currentLineNumber = this.getCodemirrorState()?.doc.lineAt(this.getCodemirrorState()?.selection.main.head).number;
56
+ const charBefore = this.getCodemirrorState()?.doc.line(currentLineNumber).text[cursor - 1];
53
57
  // edge case of cursor being at line 0, char 0 is still handled here
54
58
  if (["(", "[", "{"].indexOf(charBefore) !== -1) {
55
59
  buffer = "";
56
60
  }
57
- this.editor.replaceSelection(buffer + value);
61
+ this.editor.dispatch(this.getCodemirrorState()?.replaceSelection(buffer + value), { scrollIntoView: true });
62
+ this.editor.focus();
58
63
  }
59
64
  this._setSelection(value, cursor, selectionOffset);
60
65
  // This is needed to generate a render so that the selection will appear.
61
- const exprValue = this.editor.getValue();
66
+ const exprValue = this.getCodemirrorState()?.doc.toString();
62
67
  this.props.controller.updatePropertyValue(this.props.propertyId, exprValue, true);
63
- this.lastCursorPos = this.editor.getCursor();
64
68
  }
65
69
 
66
70
  onSelectionChange(selection) {
@@ -68,9 +72,8 @@ export default class ExpressionBuilder extends React.Component {
68
72
  }
69
73
 
70
74
  onBlur(editor, evt) {
71
- this.lastCursorPos = editor.getCursor();
72
75
  const currentValue = this.props.controller.getPropertyValue(this.props.propertyId);
73
- const newValue = this.editor.getValue();
76
+ const newValue = editor?.viewState?.state?.doc.toString();
74
77
  const skipValidate = this.expressionSelectionPanel && evt && this.expressionSelectionPanel.contains(evt.relatedTarget);
75
78
  // update property value when value is updated OR value is to be validated
76
79
  if (!isEqual(currentValue, newValue) || !skipValidate) {
@@ -78,38 +81,41 @@ export default class ExpressionBuilder extends React.Component {
78
81
  }
79
82
  }
80
83
 
81
- editorDidMount(editor, next) {
82
- this.editor = editor;
84
+ getCodemirrorState() {
85
+ return this.editor?.viewState?.state;
83
86
  }
84
87
 
88
+ editorDidMount(editor) {
89
+ this.editor = editor;
90
+ }
85
91
 
86
92
  _setSelection(value, cursor, selectionOffset) {
87
93
  // first set the selection to the first param holder of new value
88
94
  if (typeof value === "string") {
89
95
  const firstParam = value.indexOf("?");
90
96
  if (firstParam !== -1) {
91
- const selection = { anchor: { line: cursor.line, ch: cursor.ch + firstParam + selectionOffset + 1 },
92
- head: { line: cursor.line, ch: cursor.ch + firstParam + selectionOffset } };
97
+ const selection = { anchor: cursor + firstParam + selectionOffset + 1, head: cursor + firstParam + selectionOffset };
98
+ this.editor.dispatch({ selection: selection });
93
99
  this.onSelectionChange([selection]);
94
100
  return;
95
101
  }
96
102
  }
97
103
  // if the newValue doesn't have a param holder
98
104
  // set it to the first param holder found in the expression
99
- const lineCount = this.editor.lineCount();
105
+ const lineCount = this.getCodemirrorState()?.doc.lines;
100
106
  for (let index = 0; index < lineCount; index++) {
101
- const line = this.editor.getLine(index);
107
+ const line = this.getCodemirrorState()?.doc.line(index + 1).text;
102
108
  const paramOffset = line.indexOf("?");
103
109
  if (paramOffset !== -1) {
104
- const selection = { anchor: { line: index, ch: paramOffset + 1 },
105
- head: { line: index, ch: paramOffset } };
110
+ const selection = { anchor: paramOffset + 1, head: paramOffset };
111
+ this.editor.dispatch({ selection: selection });
106
112
  this.onSelectionChange([selection]);
107
113
  return;
108
114
  }
109
115
  }
110
116
  // if no parameter holders found then set it to end of insert string
111
- const insertSelection = { anchor: { line: cursor.line, ch: cursor.ch + value.length + selectionOffset },
112
- head: { line: cursor.line, ch: cursor.ch + value.length + selectionOffset } };
117
+ const insertSelection = { anchor: this.getCodemirrorState()?.selection.main.anchor,
118
+ head: this.getCodemirrorState()?.selection.main.head };
113
119
  this.onSelectionChange([insertSelection]);
114
120
  return;
115
121
  }
@@ -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.
@@ -19,11 +19,10 @@
19
19
  import React from "react";
20
20
  import PropTypes from "prop-types";
21
21
  import { connect } from "react-redux";
22
- import { UnControlled as CodeMirror } from "react-codemirror2";
23
22
  import Icon from "./../../../icons/icon.jsx";
24
23
  import { Button } from "carbon-components-react";
25
24
  import classNames from "classnames";
26
- import { isEqual } from "lodash";
25
+ import { isEqual, concat } from "lodash";
27
26
  import ValidationMessage from "./../../components/validation-message";
28
27
  import WideFlyout from "./../../components/wide-flyout";
29
28
  import { formatMessage } from "./../../util/property-utils";
@@ -35,76 +34,67 @@ import { STATES } from "./../../constants/constants";
35
34
  import { get } from "lodash";
36
35
  import ExpressionToggle from "./expression-toggle/expression-toggle";
37
36
 
38
- import { register as registerPython } from "./languages/python-hint";
39
- import { register as registerR } from "./languages/r-hint";
40
- import { register as registerClem } from "./languages/CLEM-hint";
41
-
42
- // required for server side rendering.
43
- let cm = null;
44
- if (typeof window !== "undefined" && typeof window.navigator !== "undefined") {
45
- cm = require("codemirror");
46
- require("codemirror/addon/hint/show-hint");
47
- require("codemirror/addon/display/placeholder");
48
- require("codemirror/addon/display/autorefresh");
49
- require("codemirror/mode/javascript/javascript");
50
- require("codemirror/addon/hint/javascript-hint");
51
- require("codemirror/addon/hint/sql-hint");
52
- require("codemirror/mode/sql/sql");
53
- require("codemirror/mode/python/python");
54
- require("codemirror/mode/r/r");
55
- registerPython(cm);
56
- registerR(cm);
57
- registerClem(cm);
58
- }
59
-
37
+ import { keymap, placeholder } from "@codemirror/view";
38
+ import { defaultKeymap, indentWithTab, insertNewline } from "@codemirror/commands";
39
+ import { basicSetup, EditorView } from "codemirror";
40
+ import { Compartment } from "@codemirror/state";
41
+ import { tags } from "@lezer/highlight";
42
+ import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
43
+ import { python } from "@codemirror/lang-python";
44
+ import { r } from "codemirror-lang-r";
45
+ import { sql } from "@codemirror/lang-sql";
46
+ import { javascript } from "@codemirror/lang-javascript";
47
+
48
+ import { getPythonHints } from "./languages/python-hint";
49
+ import { getRHints } from "./languages/r-hint";
50
+ import { clem } from "./languages/CLEM-hint";
60
51
 
61
52
  const pxPerChar = 8.5;
62
53
  const pxPerLine = 26;
63
54
  const defaultCharPerLine = 30;
64
55
  const maxLineHeight = 15 * pxPerLine; // 20 lines
65
56
  const minLineHeight = 4 * pxPerLine; // 4 lines
66
-
67
57
  class ExpressionControl extends React.Component {
68
58
  constructor(props) {
69
59
  super(props);
70
60
  this.state = {
71
61
  showExpressionBuilder: false,
72
- validationInProgress: false
62
+ validationInProgress: false,
63
+ expressionEditorHeight: 0
73
64
  };
74
-
75
- this.origHint = "";
65
+ this.editable = new Compartment; // eslint-disable-line new-parens
66
+ this.editorRef = React.createRef();
67
+ this.origHint = [];
76
68
  this.expressionInfo = this.props.controller.getExpressionInfo();
77
69
  this.handleValidate = this.handleValidate.bind(this);
78
70
  this.hasValidate = this.hasValidate.bind(this);
79
71
  this.cancelExpressionBuilder = this.cancelExpressionBuilder.bind(this);
80
72
  this.hideExpressionBuilder = this.hideExpressionBuilder.bind(this);
81
73
  this.showExpressionBuilder = this.showExpressionBuilder.bind(this);
82
- this.editorDidMount = this.editorDidMount.bind(this);
83
74
  this.addonHints = this.addonHints.bind(this);
84
75
  this.getDatasetFields = this.getDatasetFields.bind(this);
85
76
  this.handleBlur = this.handleBlur.bind(this);
86
- this.handleKeyDown = this.handleKeyDown.bind(this);
87
-
88
- this.handleChange = (editor, data, newValue) => {
89
- // this is needed when characters are added into the expression builder because
90
- // entering chars does not go through onChange() in expression builder.
91
- // This is needed to adjust the selection position in code mirror.
92
- if (Array.isArray(data.text) && data.text.length === 1 && data.text[0].length === 1 && this.props.onSelectionChange) {
93
- // if a string was replaced, need to calc newPos from the 'data.from' otherwise use 'data.to'
94
- const newPos = (data.removed[0].length > 0) ? { line: data.from.line, ch: data.from.ch + 1 } : { line: data.to.line, ch: data.to.ch + 1 };
95
- this.props.onSelectionChange([{ anchor: newPos, head: newPos }]);
96
- }
97
- if (this.state.validateIcon) {
98
- this.setState({
99
- validateIcon: null
100
- });
101
- this.props.controller.updateErrorMessage(this.props.propertyId, DEFAULT_VALIDATION_MESSAGE);
102
- }
103
- };
77
+ this.createCodeMirrorEditor = this.createCodeMirrorEditor.bind(this);
78
+ this.events = this.events.bind(this);
79
+ this.handleUpdate = this.handleUpdate.bind(this);
80
+ this.setCodeMirrorEditable = this.setCodeMirrorEditable.bind(this);
81
+ this.getCodemirrorState = this.getCodemirrorState.bind(this);
82
+ }
83
+
84
+ componentDidMount() {
85
+ this.createCodeMirrorEditor();
104
86
  }
105
87
 
106
88
  // this is needed to ensure expression builder selection works.
107
89
  componentDidUpdate(prevProps) {
90
+ // When code is edited in expression builder, reflect changes in expression flyout
91
+ if (!isEqual(this.getCodemirrorState()?.doc.toString(), this.props.value)) {
92
+ this.editor.dispatch({ changes: { from: 0, to: this.getCodemirrorState()?.doc.length, insert: this.props.value } });
93
+ }
94
+ // Toggle editable mode in Codemirror editor
95
+ if (!isEqual(prevProps.state, this.props.state)) {
96
+ this.setCodeMirrorEditable(!(this.props.state === STATES.DISABLED));
97
+ }
108
98
  if (
109
99
  this.props.selectionRange &&
110
100
  this.props.selectionRange.length > 0 &&
@@ -112,17 +102,21 @@ class ExpressionControl extends React.Component {
112
102
  this.editor
113
103
  ) {
114
104
  this.props.selectionRange.forEach((selected) => {
115
- this.editor.setSelection(selected.anchor, selected.head);
105
+ this.editor.dispatch({ selection: selected });
116
106
  });
117
107
  this.editor.focus();
118
108
  }
119
109
  }
120
110
 
121
- // reset to the original autocomplete handler
122
- componentWillUnmount() {
123
- if (this.origHint && cm) {
124
- cm.registerHelper("hint", this.props.control.language, this.origHint);
125
- }
111
+ getCodemirrorState() {
112
+ return this.editor?.viewState?.state;
113
+ }
114
+
115
+ // Set codemirror editor non-editable when disabled
116
+ setCodeMirrorEditable(value) {
117
+ this.editor.dispatch({
118
+ effects: this.editable.reconfigure(EditorView.editable.of(value))
119
+ });
126
120
  }
127
121
 
128
122
  // get the set of dataset field names
@@ -130,39 +124,24 @@ class ExpressionControl extends React.Component {
130
124
  const results = [];
131
125
  const fields = this.props.controller.getDatasetMetadataFields();
132
126
  for (const field of fields) {
133
- results.push(field.name);
127
+ results.push({ label: field.name, type: "variable" });
134
128
  }
135
129
  return results;
136
130
  }
137
131
 
138
132
  // Add the dataset field names to the autocomplete list
139
- addonHints(editor, options) {
140
- var results = {};
141
- var cur = editor.getCursor();
142
- var token = editor.getTokenAt(cur);
143
- if (this.origHint) {
144
- // get the list of autocomplete names from the language autocomplete handler
145
- results = this.origHint(editor, options);
146
-
147
- // add to the start of the autocomplete list the set of dataset field names that complete the
148
- // string that has been entered.
149
- var parameters = this.getDatasetFields();
150
- for (var i = 0; i < parameters.length; ++i) {
151
- const parameter = parameters[i];
152
- if (parameter.lastIndexOf(token.string, 0) === 0 && results.list.indexOf(parameter) === -1) {
153
- results.list.unshift(parameter);
154
- } else if (token.string === " " && token.type === null) {
155
- results.list.unshift(parameter);
156
- }
157
- }
133
+ addonHints(context) {
134
+ const word = context.matchBefore(/\w*/);
135
+ if (word.from === word.to && !context.explicit) {
136
+ return null;
158
137
  }
159
- return results;
138
+ return {
139
+ from: word.from,
140
+ options: concat(this.origHint, this.getDatasetFields())
141
+ };
160
142
  }
161
143
 
162
- // Save original autocomplete handler and then register our custom handler
163
- // that will add data set filed names to autocomplete list.
164
- editorDidMount(editor, next) {
165
- this.editor = editor;
144
+ createCodeMirrorEditor() {
166
145
  // set the default height, should be between 4 and 20 lines
167
146
  const controlWidth = (this.expressionEditorDiv) ? this.expressionEditorDiv.clientWidth : 0;
168
147
  const charPerLine = (controlWidth > 0) ? controlWidth / pxPerChar : defaultCharPerLine;
@@ -172,9 +151,8 @@ class ExpressionControl extends React.Component {
172
151
  // let an explicit prop override the calculated height
173
152
  height = this.props.control.rows ? pxPerLine * this.props.control.rows : height;
174
153
  height = this.props.height ? this.props.height : height;
175
- this.editor.setSize(null, Math.max(Math.floor(height), minLineHeight));
154
+ this.setState({ expressionEditorHeight: Math.max(Math.floor(height), minLineHeight) });
176
155
 
177
- this.origHint = editor.getHelper({ line: 0, ch: 0 }, "hint");
178
156
  // this next line is a hack to overcome a Codemirror problem. To support SparkSQL, a subset of SQL,
179
157
  // we need to register with Codemirror the language as the value of "text/x-hive". When Codemirror
180
158
  // registers the autocomplete addon it registers is as "sql" not the subset "text/x-hive"
@@ -183,22 +161,61 @@ class ExpressionControl extends React.Component {
183
161
  let language = this.props.control.language;
184
162
  switch (this.props.control.language) {
185
163
  case "text/x-hive":
186
- language = "sql";
164
+ language = sql();
187
165
  break;
188
166
  case "text/x-python":
189
- language = "python";
167
+ language = python();
168
+ this.origHint = getPythonHints();
190
169
  break;
191
170
  case "text/x-rsrc":
192
- language = "r";
171
+ language = r();
172
+ this.origHint = getRHints();
173
+ break;
174
+ case "javascript":
175
+ language = javascript();
193
176
  break;
194
177
  default:
178
+ language = clem();
195
179
  }
196
- if (cm) {
197
- cm.registerHelper("hint", language, this.addonHints);
198
- }
199
180
 
181
+ // Custom completions add to the language completions
182
+ const customCompletions = language.language.data.of({
183
+ autocomplete: this.addonHints
184
+ });
185
+
186
+ // Syntax highlighting
187
+ const myHighlightStyle = HighlightStyle.define([
188
+ { tag: tags.keyword, class: "cm-keyword" },
189
+ { tag: tags.number, class: "cm-number" },
190
+ { tag: tags.definition(tags.name), class: "cm-def" },
191
+ { tag: tags.comment, class: "cm-comment" },
192
+ { tag: tags.variableName, class: "cm-variable" },
193
+ { tag: tags.punctuation, class: "cm-punctuation" },
194
+ { tag: tags.propertyName, class: "cm-property" },
195
+ { tag: tags.operator, class: "cm-operator" },
196
+ { tag: tags.string, class: "cm-string" },
197
+ { tag: tags.meta, class: "cm-meta" }
198
+ ]);
199
+
200
+ this.editor = new EditorView({
201
+ doc: this.props.value,
202
+ extensions: [
203
+ keymap.of([{ key: "Enter", run: insertNewline }, indentWithTab, defaultKeymap]), // This should be before basicSetup to insertNewLine on "Enter"
204
+ customCompletions,
205
+ syntaxHighlighting(myHighlightStyle),
206
+ basicSetup,
207
+ this.events(),
208
+ language,
209
+ placeholder(this.props.control.additionalText),
210
+ this.handleUpdate(),
211
+ this.editable.of(EditorView.editable.of(!(this.props.state === STATES.DISABLED)))
212
+ ],
213
+ parent: this.editorRef.current
214
+ });
215
+
216
+ // Set editor in the expression-builder
200
217
  if (this.props.editorDidMount) {
201
- this.props.editorDidMount(editor, next);
218
+ this.props.editorDidMount(this.editor);
202
219
  }
203
220
  }
204
221
 
@@ -232,7 +249,6 @@ class ExpressionControl extends React.Component {
232
249
  validateIcon: response.type,
233
250
  validationInProgress: false
234
251
  });
235
- this.editor.display.input.blur();
236
252
  });
237
253
  }
238
254
 
@@ -240,28 +256,56 @@ class ExpressionControl extends React.Component {
240
256
  return typeof this.props.controller.getHandlers().validationHandler === "function";
241
257
  }
242
258
 
243
- handleKeyDown(editor, evt) {
244
- // this is needed to move the cursor to the new line if selection is being used in the expression builder.
245
- if (evt.code === "Enter") {
246
- if (this.props.selectionRange && this.props.selectionRange.length > 0 && this.props.onSelectionChange) {
247
- const newPos = { line: this.props.selectionRange[0].anchor.line + 1, ch: 0 };
248
- this.props.onSelectionChange([{ anchor: newPos, head: newPos }]);
259
+ // Event handlers for CM6
260
+ events() {
261
+ const that = this;
262
+ const eventHandlers = EditorView.domEventHandlers({
263
+ blur(evt, view) {
264
+ that.handleBlur(view, evt);
249
265
  }
250
- }
266
+ });
267
+ return eventHandlers;
251
268
  }
252
269
 
253
270
  handleBlur(editor, evt) {
254
- if (this.props.onBlur) {
271
+ const cancelButtonClicked = evt && evt.relatedTarget && evt.relatedTarget.classList.contains("properties-cancel-button");
272
+ if (this.props.onBlur && !cancelButtonClicked) {
255
273
  // this will ensure the expression builder can save values onBlur
256
274
  this.props.onBlur(editor, evt);
257
275
  } else {
258
- const newValue = this.editor.getValue();
276
+ const newValue = editor?.viewState?.state?.doc.toString();
259
277
  // don't validate when opening the expression builder
260
278
  const skipValidate = evt && evt.relatedTarget && evt.relatedTarget.classList.contains("properties-expression-button");
261
279
  this.props.controller.updatePropertyValue(this.props.propertyId, newValue, skipValidate);
262
280
  }
263
281
  }
264
282
 
283
+ handleUpdate() {
284
+ const onUpdate = EditorView.updateListener.of((viewUpdate) => {
285
+ if (viewUpdate.docChanged) {
286
+ // this is needed when a single character is added into the expression builder because
287
+ // entering chars does not go through onChange() in expression builder.
288
+ // This is needed to adjust the selection position in code mirror.
289
+ if (
290
+ Array.isArray(viewUpdate.changedRanges) &&
291
+ viewUpdate.changedRanges.length === 1 &&
292
+ Math.abs(viewUpdate.changes.newLength - viewUpdate.changes.length) === 1 &&
293
+ this.props.onSelectionChange
294
+ ) {
295
+ const newPos = viewUpdate.changedRanges[0].toB;
296
+ this.props.onSelectionChange([{ anchor: newPos, head: newPos }]);
297
+ }
298
+ if (this.state.validateIcon) {
299
+ this.setState({
300
+ validateIcon: null
301
+ });
302
+ this.props.controller.updateErrorMessage(this.props.propertyId, DEFAULT_VALIDATION_MESSAGE);
303
+ }
304
+ }
305
+ });
306
+ return onUpdate;
307
+ }
308
+
265
309
  _showBuilderButton() {
266
310
  // only show the button if there are function lists available and
267
311
  // not explicitly told not to by the this.props.builder
@@ -276,7 +320,6 @@ class ExpressionControl extends React.Component {
276
320
  messageInfo = null;
277
321
  }
278
322
 
279
- const theme = (this.props.state === STATES.DISABLED) ? "disabled" : messageType;
280
323
  const reactIntl = this.props.controller.getReactIntl();
281
324
 
282
325
  const button = this._showBuilderButton() ? (
@@ -309,15 +352,7 @@ class ExpressionControl extends React.Component {
309
352
  {validateIcon}
310
353
  </div>)
311
354
  : null;
312
- const mirrorOptions = {
313
- mode: this.props.control.language,
314
- placeholder: this.props.control.additionalText,
315
- theme: theme + " custom",
316
- readOnly: (this.props.state === STATES.DISABLED) ? "nocursor" : false,
317
- extraKeys: { "Ctrl-Space": "autocomplete" },
318
- autoRefresh: true,
319
- lineNumbers: true
320
- };
355
+
321
356
  const applyLabel = formatMessage(reactIntl, MESSAGE_KEYS.APPLYBUTTON_LABEL);
322
357
  const rejectLabel = formatMessage(reactIntl, MESSAGE_KEYS.REJECTBUTTON_LABEL);
323
358
  const expressonTitle = formatMessage(reactIntl, MESSAGE_KEYS.EXPRESSION_BUILDER_TITLE);
@@ -367,6 +402,8 @@ class ExpressionControl extends React.Component {
367
402
  />);
368
403
  }
369
404
 
405
+ const codemirrorClassName = classNames(`elyra-CodeMirror ${messageType} ${this.props.state}`);
406
+
370
407
  return (
371
408
  <div className="properties-expression-editor-wrapper" >
372
409
  {this.props.controlItem}
@@ -377,15 +414,7 @@ class ExpressionControl extends React.Component {
377
414
  <div ref={ (ref) => (this.expressionEditorDiv = ref) } data-id={ControlUtils.getDataId(this.props.propertyId)}
378
415
  className={className}
379
416
  >
380
- <CodeMirror
381
- ref= { (ref) => (this.codeMirror = ref)}
382
- options={mirrorOptions}
383
- onChange={this.handleChange}
384
- onKeyDown={this.handleKeyDown}
385
- onBlur={this.handleBlur}
386
- editorDidMount={this.editorDidMount}
387
- value={this.props.value}
388
- />
417
+ <div className={codemirrorClassName} ref={this.editorRef} style={{ height: this.state.expressionEditorHeight }} />
389
418
  <ValidationMessage state={this.props.state} messageInfo={messageInfo} inTable={this.props.tableControl} />
390
419
  </div>
391
420
  </div>
@@ -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.
@@ -17,80 +17,78 @@
17
17
  @import "./expression-builder/expression-builder";
18
18
  @import "./expression-toggle/expression-toggle";
19
19
 
20
- .react-codemirror2 {
21
- .CodeMirror {
20
+ .elyra-CodeMirror {
21
+ .cm-editor {
22
+ height: inherit;
22
23
  width: 100%;
23
24
  background: $field-02;
24
25
  color: $text-01;
25
26
 
26
- .CodeMirror-gutters {
27
+ .cm-gutters {
27
28
  border-right: none;
28
29
  background-color: inherit;
29
30
  }
30
31
 
31
- .CodeMirror-lines {
32
- padding-top: $spacing-05;
33
- .CodeMirror-line-like {
32
+ .cm-content {
33
+ .cm-line {
34
34
  padding-left: $spacing-03;
35
+ @include carbon--type-style("code-02");
35
36
  }
36
- .CodeMirror-placeholder {
37
- opacity: 0.5;
37
+ .cm-placeholder {
38
38
  @include carbon--type-style("code-02");
39
39
  }
40
- .CodeMirror-cursor {
40
+ .cm-cursor {
41
41
  border-left: 1px solid $text-01;
42
42
  }
43
- .CodeMirror-code {
44
-
45
- .CodeMirror-linenumber {
46
- @include carbon--type-style("body-short-01");
47
- }
48
-
49
- .CodeMirror-line {
50
- padding-left: $spacing-03;
43
+ .cm-lineNumbers .cm-gutterElement {
44
+ @include carbon--type-style("body-short-01");
45
+ }
46
+ }
51
47
 
52
- span {
53
- @include carbon--type-style("code-02");
54
- }
55
- }
48
+ .cm-tooltip-autocomplete {
49
+ z-index: 1110;
50
+ ul {
51
+ white-space: normal; // Wraps the long autocompletion text on multiple lines
56
52
  }
57
53
  }
58
54
  }
59
55
 
60
- .cm-s-error.CodeMirror { border: $spacing-01 solid $support-01; }
61
-
62
- .cm-s-warning.CodeMirror { border: $spacing-01 solid $support-03; }
63
-
64
- .cm-s-disabled.CodeMirror { opacity: 0.5}
56
+ .cm-line .cm-keyword { color: $link-01; }
65
57
 
66
- .cm-s-custom .cm-keyword { color: $link-01; }
58
+ .cm-line .cm-number { color: $text-02; }
67
59
 
68
- .cm-s-custom .cm-number { color: $text-02; }
60
+ .cm-line .cm-def { color: $support-01; }
69
61
 
70
- .cm-s-custom .cm-def { color: $support-01; }
62
+ .cm-line .cm-comment { color: $support-02; }
71
63
 
72
- .cm-s-custom .cm-comment { color: $support-02; }
73
-
74
- .cm-s-custom .cm-variable,
75
- .cm-s-custom .cm-punctuation,
76
- .cm-s-custom .cm-property,
77
- .cm-s-custom .cm-operator {
64
+ .cm-line .cm-variable,
65
+ .cm-line .cm-punctuation,
66
+ .cm-line .cm-property,
67
+ .cm-line .cm-operator {
78
68
  color: $text-01;
79
69
  }
80
- .cm-s-custom .cm-variable-2 { color: $text-01; }
81
70
 
82
- .cm-s-custom .cm-variable-3, .cm-s-custom .cm-type { color: $text-01; }
71
+ .cm-line .cm-string { color: $text-error; }
83
72
 
84
- .cm-s-custom .cm-string { color: $text-error; }
73
+ .cm-line .cm-meta { color: $ui-01; }
74
+ }
85
75
 
86
- .cm-s-custom .cm-string-2 { color: $text-error; }
76
+ .elyra-CodeMirror.disabled {
77
+ .cm-editor {
78
+ opacity: 0.5;
79
+ }
80
+ }
87
81
 
88
- .cm-s-custom .cm-meta { color: $ui-01; }
82
+ .elyra-CodeMirror.warning {
83
+ .cm-editor {
84
+ border: $spacing-01 solid $support-03;
85
+ }
89
86
  }
90
87
 
91
- /* Override so Codemirror autocomplete shows up on top */
92
- .CodeMirror-hints {
93
- z-index: 1110;
88
+ .elyra-CodeMirror.error {
89
+ .cm-editor {
90
+ border: $spacing-01 solid $support-01;
91
+ }
94
92
  }
95
93
 
96
94
  .properties-expression-editor {
@@ -104,7 +102,7 @@
104
102
  }
105
103
 
106
104
  .properties-light-disabled {
107
- .CodeMirror {
105
+ .elyra-CodeMirror .cm-editor {
108
106
  background: $field-01;
109
107
  }
110
108
  }