@elyra/canvas 12.14.0 → 12.15.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 (69) hide show
  1. package/dist/_baseForOwn-7d4e8506.js.map +1 -1
  2. package/dist/_baseForOwn-d38b560e.js.map +1 -1
  3. package/dist/canvas-constants-34cdb7df.js.map +1 -1
  4. package/dist/canvas-constants-3c09c7f6.js.map +1 -1
  5. package/dist/canvas-controller-720a509c.js +2 -0
  6. package/dist/canvas-controller-720a509c.js.map +1 -0
  7. package/dist/canvas-controller-73113a1b.js +2 -0
  8. package/dist/canvas-controller-73113a1b.js.map +1 -0
  9. package/dist/common-canvas-21b6ab50.js +2 -0
  10. package/dist/common-canvas-21b6ab50.js.map +1 -0
  11. package/dist/common-canvas-baef2726.js +2 -0
  12. package/dist/common-canvas-baef2726.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-6d839df1.js → common-properties-86de4c9f.js} +2 -2
  18. package/dist/{common-properties-40648163.js.map → common-properties-86de4c9f.js.map} +1 -1
  19. package/dist/common-properties-9e579309.js +2 -0
  20. package/dist/{common-properties-6d839df1.js.map → common-properties-9e579309.js.map} +1 -1
  21. package/dist/createClass-32a0cf0f.js.map +1 -1
  22. package/dist/createClass-6db89a23.js.map +1 -1
  23. package/dist/datarecord-metadata-v3-schema-6b6384ff.js.map +1 -1
  24. package/dist/datarecord-metadata-v3-schema-81228a9a.js.map +1 -1
  25. package/dist/en-7a0f1db1.js.map +1 -1
  26. package/dist/en-8647c347.js.map +1 -1
  27. package/dist/extends-1139e06f.js.map +1 -1
  28. package/dist/extends-8d17c85c.js.map +1 -1
  29. package/dist/flexible-table-d3598aa8.js.map +1 -1
  30. package/dist/flexible-table-fe7fbc13.js.map +1 -1
  31. package/dist/getPrototypeOf-a1c3fe64.js.map +1 -1
  32. package/dist/getPrototypeOf-bf88242f.js.map +1 -1
  33. package/dist/index-669f95a7.js.map +1 -1
  34. package/dist/index-6d3404e1.js.map +1 -1
  35. package/dist/isArrayLikeObject-a9c7973b.js.map +1 -1
  36. package/dist/isArrayLikeObject-f3b27f64.js.map +1 -1
  37. package/dist/lib/canvas-controller.es.js +1 -1
  38. package/dist/lib/canvas-controller.js +1 -1
  39. package/dist/lib/canvas.es.js +1 -1
  40. package/dist/lib/canvas.js +1 -1
  41. package/dist/lib/properties.es.js +1 -1
  42. package/dist/lib/properties.js +1 -1
  43. package/dist/styles/common-canvas.min.css +1 -1
  44. package/dist/styles/common-canvas.min.css.map +1 -1
  45. package/dist/toolbar-29ec7983.js.map +1 -1
  46. package/dist/toolbar-3f4b173f.js.map +1 -1
  47. package/package.json +1 -1
  48. package/src/common-canvas/common-canvas-utils.js +37 -4
  49. package/src/common-canvas/svg-canvas-d3.scss +17 -11
  50. package/src/common-canvas/svg-canvas-pipeline.js +10 -3
  51. package/src/common-canvas/svg-canvas-renderer.js +43 -469
  52. package/src/common-canvas/svg-canvas-utils-decs.js +0 -5
  53. package/src/common-canvas/svg-canvas-utils-nodes.js +0 -5
  54. package/src/common-canvas/svg-canvas-utils-textarea.js +472 -0
  55. package/src/common-properties/components/title-editor/title-editor.jsx +2 -2
  56. package/src/common-properties/components/title-editor/title-editor.scss +1 -16
  57. package/src/object-model/layout-dimensions.js +4 -2
  58. package/src/palette/palette-flyout-content-category.jsx +73 -42
  59. package/src/palette/palette.scss +1 -1
  60. package/stats.html +1 -1
  61. package/dist/canvas-controller-8e2bb291.js +0 -2
  62. package/dist/canvas-controller-8e2bb291.js.map +0 -1
  63. package/dist/canvas-controller-bd0d8d59.js +0 -2
  64. package/dist/canvas-controller-bd0d8d59.js.map +0 -1
  65. package/dist/common-canvas-69fe4a67.js +0 -2
  66. package/dist/common-canvas-69fe4a67.js.map +0 -1
  67. package/dist/common-canvas-f5e4af65.js +0 -2
  68. package/dist/common-canvas-f5e4af65.js.map +0 -1
  69. package/dist/common-properties-40648163.js +0 -2
@@ -30,11 +30,6 @@ export default class SvgCanvasDecs {
30
30
  return null;
31
31
  }
32
32
 
33
- getDecLabelForeignClass(dec) {
34
- const outlineClass = dec.label_outline ? " d3-node-label-outline" : "";
35
- return "d3-foreign-object" + outlineClass;
36
- }
37
-
38
33
  getDecLabelClass(dec, objType) {
39
34
  const lineTypeClass = dec.label_single_line ? " d3-label-single-line" : " d3-label-multi-line";
40
35
  const justificationClass = dec.label_align === "center" ? " d3-label-center" : "";
@@ -27,11 +27,6 @@ export default class SvgCanvasNodes {
27
27
  return "d3-node-image";
28
28
  }
29
29
 
30
- getNodeLabelForeignClass(node) {
31
- const outlineClass = node.layout.labelOutline ? " d3-node-label-outline" : "";
32
- return "d3-foreign-object" + outlineClass;
33
- }
34
-
35
30
  getNodeLabelClass(node) {
36
31
  if (CanvasUtils.isExpandedSupernode(node)) {
37
32
  return "d3-node-label d3-label-single-line " + this.getMessageLabelClass(node.messages);
@@ -0,0 +1,472 @@
1
+ /*
2
+ * Copyright 2017-2022 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
+ /* eslint no-invalid-this: "off" */
17
+
18
+ import * as d3Selection from "d3-selection";
19
+ const d3 = Object.assign({}, d3Selection);
20
+
21
+ import { unescape as unescapeText } from "lodash";
22
+
23
+ import Logger from "../logging/canvas-logger.js";
24
+ import CanvasUtils from "./common-canvas-utils.js";
25
+ import SvgCanvasMarkdown from "./svg-canvas-utils-markdown.js";
26
+
27
+ const BACKSPACE_KEY = 8;
28
+ const RETURN_KEY = 13;
29
+ const ESC_KEY = 27;
30
+ const LEFT_ARROW_KEY = 37;
31
+ const UP_ARROW_KEY = 38;
32
+ const RIGHT_ARROW_KEY = 39;
33
+ const DOWN_ARROW_KEY = 40;
34
+ const DELETE_KEY = 46;
35
+ const A_KEY = 65;
36
+ const B_KEY = 66;
37
+ const E_KEY = 69;
38
+ const I_KEY = 73;
39
+ const K_KEY = 75;
40
+ const X_KEY = 88;
41
+ const LAB_KEY = 188; // Left angle bracket <
42
+ const RAB_KEY = 190; // Right angle bracket >
43
+ const SEVEN_KEY = 55;
44
+ const EIGHT_KEY = 56;
45
+
46
+ const SCROLL_PADDING = 12;
47
+
48
+ export default class SvgCanvasTextArea {
49
+
50
+ constructor(config, dispUtils, nodeUtils, decUtils, canvasController,
51
+ canvasDiv, activePipeline, displayCommentsCallback, displayLinksCallback, getCommentToolbarPosCallback) {
52
+
53
+ this.config = config;
54
+ this.dispUtils = dispUtils;
55
+ this.nodeUtils = nodeUtils;
56
+ this.decUtils = decUtils;
57
+ this.canvasController = canvasController;
58
+ this.canvasDiv = canvasDiv;
59
+ this.activePipeline = activePipeline;
60
+ this.displayCommentsCallback = displayCommentsCallback;
61
+ this.displayLinksCallback = displayLinksCallback;
62
+ this.getCommentToolbarPosCallback = getCommentToolbarPosCallback;
63
+
64
+ this.logger = new Logger("SvgCanvasTextArea");
65
+
66
+ // Allows us to track the editing of text (either comments or node labels)
67
+ this.editingText = false;
68
+ this.editingTextId = "";
69
+
70
+ // Keeps track of when text is in the process of being added.
71
+ this.addingTextToTextArea = false;
72
+
73
+ // Keeps track of textarea height.
74
+ this.textAreaHeight = null;
75
+ }
76
+
77
+ isEditingText() {
78
+ return this.editingText;
79
+ }
80
+
81
+ getEditingTextId() {
82
+ return this.editingTextId;
83
+ }
84
+
85
+ displayCommentTextArea(d, parentDomObj) {
86
+ this.editingTextData = {
87
+ id: d.id,
88
+ text: d.content,
89
+ singleLine: false,
90
+ maxCharacters: null,
91
+ allowReturnKey: true,
92
+ textCanBeEmpty: true,
93
+ xPos: 0,
94
+ yPos: 0,
95
+ width: d.width,
96
+ height: d.height,
97
+ className: "d3-comment-entry",
98
+ parentDomObj: parentDomObj,
99
+ keyboardInputCallback: this.config.enableMarkdownInComments ? this.commentKeyboardHandler.bind(this) : null,
100
+ autoSizeCallback: this.autoSizeComment.bind(this),
101
+ saveTextChangesCallback: this.saveCommentChanges.bind(this),
102
+ closeTextAreaCallback: this.closeCommentTextArea.bind(this)
103
+ };
104
+ this.displayTextArea(this.editingTextData);
105
+
106
+ if (this.config.enableMarkdownInComments && this.dispUtils.isDisplayingFullPage()) {
107
+ const pos = this.getCommentToolbarPosCallback(d);
108
+ this.canvasController.openTextToolbar(pos.x, pos.y,
109
+ this.markdownActionHandler.bind(this),
110
+ this.blurInTextToolbar.bind(this));
111
+ }
112
+ }
113
+
114
+ // Handles markdown actions initiated through the keyboard.
115
+ commentKeyboardHandler(d3Event) {
116
+ const action = this.getMarkdownAction(d3Event);
117
+ if (action) {
118
+ if (action !== "return") {
119
+ CanvasUtils.stopPropagationAndPreventDefault(d3Event);
120
+ }
121
+ this.markdownActionHandler(action, d3Event);
122
+ }
123
+ }
124
+
125
+ // Called when the blur event occurs for the text toolbar.
126
+ blurInTextToolbar(evt) {
127
+ // When Cypress tests are running a call to focus() in addTextToTextArea()
128
+ // can cause an incorrect blur event to be generated for the text toolbar
129
+ // (where relatedTarget is null). This flag therefore allows us to avoid
130
+ // thus blue events that occur while addTextToTextArea() is executing.
131
+ if (this.addingTextToTextArea) {
132
+ return;
133
+ }
134
+
135
+ // If there is a relatedTarget and it is set to one of the elements for the
136
+ // textarea, texttoolbar, etc we ignore the blur event.
137
+ if (evt.relatedTarget &&
138
+ (CanvasUtils.getParentElementWithClass(evt.relatedTarget, "d3-comment-entry") ||
139
+ CanvasUtils.getParentElementWithClass(evt.relatedTarget, "text-toolbar") ||
140
+ CanvasUtils.getParentElementWithClass(evt.relatedTarget, "bx--overflow-menu-options__btn"))) {
141
+ return;
142
+ }
143
+
144
+ // If the blur event is ocurring for an object outside of the textarea and
145
+ // text toolbar we save the current text and close the textarea.
146
+ const commentParent = d3.select(this.editingTextData.parentDomObj);
147
+ const foreignObject = commentParent.selectAll(".d3-foreign-object-text-entry");
148
+ const commentEntry = this.canvasDiv.selectAll(".d3-comment-entry");
149
+ const commentEntryElement = commentEntry.node();
150
+ this.saveAndCloseTextArea(foreignObject, this.editingTextData, commentEntryElement.value, evt);
151
+ }
152
+
153
+ // Applies a markdown action to the comment text being edited using
154
+ // the same commands as the toolbar.
155
+ getMarkdownAction(d3Event) {
156
+ if (CanvasUtils.isCmndCtrlPressed(d3Event)) {
157
+ switch (d3Event.keyCode) {
158
+ case B_KEY: return "bold";
159
+ case I_KEY: return "italics";
160
+ case X_KEY: return d3Event.shiftKey ? "strikethrough" : null;
161
+ case SEVEN_KEY: return d3Event.shiftKey ? "numberedList" : null;
162
+ case EIGHT_KEY: return d3Event.shiftKey ? "bulletedList" : null;
163
+ case E_KEY: return "code";
164
+ case K_KEY: return "link";
165
+ case LAB_KEY: return "decreaseHashes";
166
+ case RAB_KEY: return d3Event.shiftKey ? "quote" : "increaseHashes";
167
+ default:
168
+ }
169
+ } else if (d3Event.keyCode === RETURN_KEY) {
170
+ return "return";
171
+ }
172
+
173
+ return null;
174
+ }
175
+
176
+ // Handles any actions requested on the comment text to add markdown
177
+ // characters to the text. evt can be either a d3Event object from D3 when
178
+ // this method is called from keyboard entry in the textarea or it can be
179
+ // a synthetic event object from React when called from the text toolbar.
180
+ markdownActionHandler(action, evt) {
181
+ this.logger.log("markdownActionHandler - action = " + action);
182
+
183
+ const commentEntry = this.canvasDiv.selectAll(".d3-comment-entry");
184
+ const commentEntryElement = commentEntry.node();
185
+ const start = commentEntryElement.selectionStart;
186
+ const end = commentEntryElement.selectionEnd;
187
+ const text = commentEntryElement.value;
188
+
189
+ const mdObj = SvgCanvasMarkdown.processMarkdownAction(action, text, start, end);
190
+ if (mdObj) {
191
+ CanvasUtils.stopPropagationAndPreventDefault(evt);
192
+ this.addTextToTextArea(mdObj, commentEntryElement);
193
+ }
194
+ }
195
+
196
+ // Replaces the text in the currently displayed textarea with the text
197
+ // passed in. We use execCommand because this adds the inserted text to the
198
+ // textarea's undo/redo stack whereas setting the text directly into the
199
+ // textarea control does not.
200
+ addTextToTextArea(mdObj, commentEntryElement) {
201
+ this.addingTextToTextArea = true;
202
+ const text = unescapeText(mdObj.newText);
203
+ commentEntryElement.focus();
204
+ commentEntryElement.select();
205
+ document.execCommand("insertText", false, text);
206
+ commentEntryElement.setSelectionRange(mdObj.newStart, mdObj.newEnd);
207
+ this.addingTextToTextArea = false;
208
+ }
209
+
210
+ autoSizeComment(textArea, foreignObject, data) {
211
+ this.logger.log("autoSizeComment - textAreaHt = " + this.textAreaHeight + " scroll ht = " + textArea.scrollHeight);
212
+
213
+ const scrollHeight = textArea.scrollHeight + SCROLL_PADDING;
214
+ if (this.textAreaHeight < scrollHeight) {
215
+ this.textAreaHeight = scrollHeight;
216
+ foreignObject.style("height", this.textAreaHeight + "px");
217
+ this.activePipeline.getComment(data.id).height = this.textAreaHeight;
218
+ this.displayCommentsCallback();
219
+ this.displayLinksCallback();
220
+ }
221
+ }
222
+
223
+ saveCommentChanges(id, newText, newHeight) {
224
+ const comment = this.activePipeline.getComment(id);
225
+ const data = {
226
+ editType: "editComment",
227
+ editSource: "canvas",
228
+ id: comment.id,
229
+ content: newText,
230
+ width: comment.width,
231
+ height: newHeight,
232
+ x_pos: comment.x_pos,
233
+ y_pos: comment.y_pos,
234
+ pipelineId: this.activePipeline.id
235
+ };
236
+ this.canvasController.editActionHandler(data);
237
+ }
238
+
239
+ closeCommentTextArea() {
240
+ this.canvasController.closeTextToolbar();
241
+ }
242
+
243
+ displayNodeLabelTextArea(node, parentDomObj) {
244
+ d3.select(parentDomObj)
245
+ .selectAll("div")
246
+ .attr("style", "display:none;");
247
+
248
+ this.editingTextData = {
249
+ id: node.id,
250
+ text: node.label,
251
+ singleLine: node.layout.labelSingleLine,
252
+ maxCharacters: node.layout.labelMaxCharacters,
253
+ allowReturnKey: node.layout.labelAllowReturnKey,
254
+ textCanBeEmpty: false,
255
+ xPos: this.nodeUtils.getNodeLabelTextAreaPosX(node),
256
+ yPos: this.nodeUtils.getNodeLabelTextAreaPosY(node),
257
+ width: this.nodeUtils.getNodeLabelTextAreaWidth(node),
258
+ height: this.nodeUtils.getNodeLabelTextAreaHeight(node),
259
+ className: this.nodeUtils.getNodeLabelTextAreaClass(node),
260
+ parentDomObj: parentDomObj,
261
+ autoSizeCallback: this.autoSizeMultiLineLabel.bind(this),
262
+ saveTextChangesCallback: this.saveNodeLabelChanges.bind(this),
263
+ closeTextAreaCallback: this.closeNodeLabelTextArea.bind(this)
264
+ };
265
+ this.displayTextArea(this.editingTextData);
266
+ }
267
+
268
+ // Increases the size of the editable multi-line text area for a label based
269
+ // on the characters entered, and also ensures the maximum number of
270
+ // characters for the label, if one is provided, is not exceeded.
271
+ // This callback works for editable multi-line node labels and also
272
+ // editable multi-line text decorations for either nodes or links.
273
+ autoSizeMultiLineLabel(textArea, foreignObject, data) {
274
+ this.logger.log("autoSizeNodeLabel - textAreaHt = " + this.textAreaHeight + " scroll ht = " + textArea.scrollHeight);
275
+
276
+ // Restrict max characters in case text was pasted in to the text area.
277
+ if (data.maxCharacters &&
278
+ textArea.value.length > data.maxCharacters) {
279
+ textArea.value = textArea.value.substring(0, data.maxCharacters);
280
+ }
281
+ // Temporarily set the height to zero so the scrollHeight will get set to
282
+ // the full height of the text in the textarea. This allows us to close up
283
+ // the text area when the lines of text reduce.
284
+ foreignObject.style("height", 0);
285
+ const scrollHeight = textArea.scrollHeight + SCROLL_PADDING;
286
+ this.textAreaHeight = scrollHeight;
287
+ foreignObject.style("height", this.textAreaHeight + "px");
288
+ }
289
+
290
+ saveNodeLabelChanges(id, newText, newHeight, taData) {
291
+ const data = {
292
+ editType: "setNodeLabel",
293
+ editSource: "canvas",
294
+ nodeId: id,
295
+ label: newText,
296
+ pipelineId: this.activePipeline.id
297
+ };
298
+ this.canvasController.editActionHandler(data);
299
+ }
300
+
301
+ // Called when the node label text area is closed. Sets the style of the
302
+ // div for the node label so the label is displayed (because it was hidden
303
+ // when the text area opened).
304
+ closeNodeLabelTextArea(nodeId) {
305
+ d3.select(this.editingTextData.parentDomObj)
306
+ .selectAll("div")
307
+ .attr("style", null);
308
+ }
309
+
310
+ // Displays a text area for an editable text decoration on either a node
311
+ // or link.
312
+ displayDecLabelTextArea(dec, obj, objType, parentDomObj) {
313
+ this.displayTextArea({
314
+ id: dec.id,
315
+ text: dec.label,
316
+ singleLine: dec.label_single_line || false,
317
+ maxCharacters: dec.label_max_characters || null,
318
+ allowReturnKey: dec.label_allow_return_key || false,
319
+ textCanBeEmpty: false,
320
+ xPos: this.decUtils.getDecLabelTextAreaPosX(),
321
+ yPos: this.decUtils.getDecLabelTextAreaPosY(),
322
+ width: this.decUtils.getDecLabelTextAreaWidth(dec, obj, objType),
323
+ height: this.decUtils.getDecLabelTextAreaHeight(dec, obj, objType),
324
+ className: this.decUtils.getDecLabelTextAreaClass(dec),
325
+ parentDomObj: parentDomObj,
326
+ objId: obj.id,
327
+ objType: objType,
328
+ autoSizeCallback: this.autoSizeMultiLineLabel.bind(this),
329
+ saveTextChangesCallback: this.saveDecLabelChanges.bind(this),
330
+ closeTextAreaCallback: null
331
+ });
332
+ }
333
+
334
+ // Handles saved changes to editable text decorations.
335
+ saveDecLabelChanges(id, newText, newHeight, taData) {
336
+ const data = {
337
+ editType: "editDecorationLabel",
338
+ editSource: "canvas",
339
+ decId: id,
340
+ objId: taData.objId,
341
+ objType: taData.objType,
342
+ label: newText,
343
+ pipelineId: this.activePipeline.id
344
+ };
345
+ this.canvasController.editActionHandler(data);
346
+ }
347
+
348
+ // Displays a text area to allow text entry and editing for: comments;
349
+ // node labels; or text decorations on either a node or link.
350
+ displayTextArea(data) {
351
+ this.textAreaHeight = data.height; // Save for comparison during auto-resize
352
+ this.editingText = true;
353
+ this.editingTextId = data.id;
354
+
355
+ const foreignObject = d3.select(data.parentDomObj)
356
+ .append("foreignObject")
357
+ .attr("class", "d3-foreign-object-text-entry")
358
+ .attr("width", data.width)
359
+ .attr("height", data.height)
360
+ .attr("x", data.xPos)
361
+ .attr("y", data.yPos);
362
+
363
+ const textArea = foreignObject
364
+ .append("xhtml:textarea")
365
+ .attr("class", data.className)
366
+ .text(unescapeText(data.text))
367
+ .on("keydown", (d3Event) => {
368
+ // Don't accept return key press when text is all on one line or
369
+ // if application doesn't want line feeds inserted in the label.
370
+ if ((data.singleLine || !data.allowReturnKey) &&
371
+ d3Event.keyCode === RETURN_KEY) {
372
+ CanvasUtils.stopPropagationAndPreventDefault(d3Event);
373
+ }
374
+ // If user presses ESC key revert back to original text by just
375
+ // closing the text area.
376
+ if (d3Event.keyCode === ESC_KEY) {
377
+ CanvasUtils.stopPropagationAndPreventDefault(d3Event);
378
+ this.textAreaEscKeyPressed = true;
379
+ this.closeTextArea(foreignObject, data);
380
+ }
381
+ // Prevent user entering more than any allowed maximum for characters.
382
+ if (data.maxCharacters &&
383
+ d3Event.target.value.length >= data.maxCharacters &&
384
+ !this.textAreaAllowedKeys(d3Event)) {
385
+ CanvasUtils.stopPropagationAndPreventDefault(d3Event);
386
+ }
387
+ // Call any specific keyboard handler for the type of
388
+ // text being edited.
389
+ if (data.keyboardInputCallback) {
390
+ data.keyboardInputCallback(d3Event);
391
+ }
392
+ })
393
+ .on("keyup", (d3Event) => {
394
+ data.autoSizeCallback(d3Event.target, foreignObject, data);
395
+ })
396
+ .on("paste", (d3Event) => {
397
+ this.logger.log("Text area - Paste - Scroll Ht = " + d3Event.target.scrollHeight);
398
+ // Allow some time for pasted text (from context menu) to be
399
+ // loaded into the text area. Otherwise the text is not there
400
+ // and the auto size does not increase the height correctly.
401
+ setTimeout(data.autoSizeCallback, 500, d3Event.target, foreignObject, data);
402
+ })
403
+ .on("blur", (d3Event, d) => {
404
+ this.logger.log("Text area - blur");
405
+ // If the esc key was pressed to cause the blur event just return
406
+ // so label returns to what it was before editing started.
407
+ if (this.textAreaEscKeyPressed) {
408
+ this.textAreaEscKeyPressed = false;
409
+ return;
410
+ }
411
+
412
+ // If the user clicked on an element in the text toolbar to cause the
413
+ // blur event, just return.
414
+ if (d3Event.relatedTarget && CanvasUtils.getParentElementWithClass(d3Event.relatedTarget, "text-toolbar")) {
415
+ return;
416
+ }
417
+
418
+ this.saveAndCloseTextArea(foreignObject, data, d3Event.target.value, d3Event);
419
+ })
420
+ .on("focus", (d3Event, d) => {
421
+ this.logger.log("Text area - focus");
422
+ data.autoSizeCallback(d3Event.target, foreignObject, data);
423
+ })
424
+ .on("mousedown click dblclick contextmenu", (d3Event, d) => {
425
+ d3Event.stopPropagation(); // Allow default behavior to show system contenxt menu
426
+ });
427
+
428
+ textArea.node().focus();
429
+
430
+ // Set the cusrsor to the end of the text.
431
+ textArea.node().setSelectionRange(data.text.length, data.text.length);
432
+ }
433
+
434
+ saveAndCloseTextArea(foreignObject, data, newValue, d3Event) {
435
+ // If there is no text for the label and textCanBeEmpty is false
436
+ // just return, so label returns to what it was before editing started.
437
+ if (!newValue && !data.textCanBeEmpty) {
438
+ CanvasUtils.stopPropagationAndPreventDefault(d3Event);
439
+ this.closeTextArea(foreignObject, data);
440
+ return;
441
+ }
442
+ const newText = newValue; // Save the text before closing the foreign object
443
+ this.closeTextArea(foreignObject, data);
444
+ if (data.text !== newText || this.textAreaHeight !== data.height) {
445
+ this.isCommentBeingUpdated = true;
446
+ data.saveTextChangesCallback(data.id, newText, this.textAreaHeight, data);
447
+ this.isCommentBeingUpdatd = false;
448
+ }
449
+ }
450
+
451
+ // Closes the text area and resets the flags.
452
+ closeTextArea(foreignObject, data) {
453
+ if (data.closeTextAreaCallback) {
454
+ data.closeTextAreaCallback(data.id);
455
+ }
456
+ foreignObject.remove();
457
+ this.editingText = false;
458
+ this.editingTextId = "";
459
+ }
460
+
461
+ // Returns true if one of the keys that are allowed in the text area, when
462
+ // checking for maximum characters, has been pressed.
463
+ textAreaAllowedKeys(d3Event) {
464
+ return d3Event.keyCode === DELETE_KEY ||
465
+ d3Event.keyCode === BACKSPACE_KEY ||
466
+ d3Event.keyCode === LEFT_ARROW_KEY ||
467
+ d3Event.keyCode === RIGHT_ARROW_KEY ||
468
+ d3Event.keyCode === UP_ARROW_KEY ||
469
+ d3Event.keyCode === DOWN_ARROW_KEY ||
470
+ (d3Event.keyCode === A_KEY && CanvasUtils.isCmndCtrlPressed(d3Event));
471
+ }
472
+ }
@@ -24,7 +24,7 @@ import { TextInput, Button } from "carbon-components-react";
24
24
  import { MESSAGE_KEYS, CONDITION_MESSAGE_TYPE } from "./../../constants/constants";
25
25
  import * as PropertyUtils from "./../../util/property-utils";
26
26
  import classNames from "classnames";
27
- import { Information16, Edit16, Close16 } from "@carbon/icons-react";
27
+ import { Help16, Edit16, Close16 } from "@carbon/icons-react";
28
28
 
29
29
 
30
30
  class TitleEditor extends Component {
@@ -121,7 +121,7 @@ class TitleEditor extends Component {
121
121
  data-id="help"
122
122
  onClick={this.helpClickHandler}
123
123
  tooltipPosition="bottom"
124
- renderIcon={Information16}
124
+ renderIcon={Help16}
125
125
  size="small"
126
126
  iconDescription={helpButtonLabel}
127
127
  hasIconOnly
@@ -34,7 +34,7 @@
34
34
  }
35
35
  .properties-title-editor-btn.help {
36
36
  &.help {
37
- margin: 0 0 0 $spacing-02; // spacing between heading label and help button
37
+ margin-left: $spacing-02; // spacing between heading label and help button
38
38
  }
39
39
  }
40
40
  }
@@ -84,21 +84,6 @@
84
84
  width: calc(100% - 2px); // subtract 2px for input active right border
85
85
  }
86
86
 
87
- .properties-title-editor-btn.help, .properties-title-editor-btn.edit {
88
- fill: $icon-02;
89
- // edit & help buttons size - 32x32
90
- width: $spacing-07;
91
- min-height: $spacing-07;
92
- padding-left: $spacing-04;
93
- padding-right: $spacing-04;
94
- &:hover {
95
- background-color: $ui-03;
96
- }
97
- &:active {
98
- background-color: $ui-03;
99
- }
100
- }
101
-
102
87
  // Close icon without heading
103
88
  .properties-close-button {
104
89
  position: relative;
@@ -353,7 +353,8 @@ const portsHorizontalDefaultLayout = {
353
353
  commentSizingArea: 10,
354
354
 
355
355
  // Add comment toolbar action, default offset from viewport
356
- addCommentOffset: 30,
356
+ addCommentOffsetX: 30,
357
+ addCommentOffsetY: 50,
357
358
 
358
359
  // Comment port (circle) radius
359
360
  commentPortRadius: 5,
@@ -742,7 +743,8 @@ const portsVerticalDefaultLayout = {
742
743
  commentSizingArea: 10,
743
744
 
744
745
  // Add comment toolbar action, default offset from viewport
745
- addCommentOffset: 30,
746
+ addCommentOffsetX: 30,
747
+ addCommentOffsetY: 50,
746
748
 
747
749
  // Comment port (circle) radius
748
750
  commentPortRadius: 5,