@elyra/canvas 13.43.2 → 13.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 (158) hide show
  1. package/.nvmrc +1 -0
  2. package/dist/Close-00f026a1.js +2 -0
  3. package/dist/{Close-46e688b8.js.map → Close-00f026a1.js.map} +1 -1
  4. package/dist/Close-3a206dc0.js +2 -0
  5. package/dist/{Close-ced84b07.js.map → Close-3a206dc0.js.map} +1 -1
  6. package/dist/canvas-controller-42118244.js +2 -0
  7. package/dist/canvas-controller-42118244.js.map +1 -0
  8. package/dist/canvas-controller-897a5f51.js +2 -0
  9. package/dist/canvas-controller-897a5f51.js.map +1 -0
  10. package/dist/common-canvas-7e31f7ac.js +2 -0
  11. package/dist/common-canvas-7e31f7ac.js.map +1 -0
  12. package/dist/common-canvas-d4a073a3.js +2 -0
  13. package/dist/common-canvas-d4a073a3.js.map +1 -0
  14. package/dist/common-canvas.cjs +2 -0
  15. package/dist/common-canvas.cjs.map +1 -0
  16. package/dist/common-canvas.js +1 -1
  17. package/dist/common-canvas.js.map +1 -1
  18. package/dist/common-properties-3d129050.js +2 -0
  19. package/dist/{common-properties-36906b79.js.map → common-properties-3d129050.js.map} +1 -1
  20. package/dist/common-properties-d1008563.js +2 -0
  21. package/dist/{common-properties-5695fc39.js.map → common-properties-d1008563.js.map} +1 -1
  22. package/dist/flexible-table-1b07a6ff.js +2 -0
  23. package/dist/{flexible-table-d624ee4e.js.map → flexible-table-1b07a6ff.js.map} +1 -1
  24. package/dist/flexible-table-40f665e8.js +2 -0
  25. package/dist/{flexible-table-d1d15df2.js.map → flexible-table-40f665e8.js.map} +1 -1
  26. package/dist/index-653a393b.js +2 -0
  27. package/dist/{index-5383b802.js.map → index-653a393b.js.map} +1 -1
  28. package/dist/index-a6a232be.js +2 -0
  29. package/dist/{index-50b29b6d.js.map → index-a6a232be.js.map} +1 -1
  30. package/dist/lib/canvas-controller.cjs +2 -0
  31. package/dist/lib/canvas-controller.cjs.map +1 -0
  32. package/dist/lib/canvas-controller.js +1 -1
  33. package/dist/lib/canvas.cjs +2 -0
  34. package/dist/lib/canvas.cjs.map +1 -0
  35. package/dist/lib/canvas.js +1 -1
  36. package/dist/lib/command-stack.cjs +2 -0
  37. package/dist/lib/command-stack.cjs.map +1 -0
  38. package/dist/lib/command-stack.js +1 -1
  39. package/dist/lib/command-stack.js.map +1 -1
  40. package/dist/lib/context-menu.cjs +2 -0
  41. package/dist/lib/context-menu.cjs.map +1 -0
  42. package/dist/lib/context-menu.js +1 -1
  43. package/dist/lib/properties/clem.cjs +2 -0
  44. package/dist/lib/properties/clem.cjs.map +1 -0
  45. package/dist/lib/properties/clem.js +1 -1
  46. package/dist/lib/properties/clem.js.map +1 -1
  47. package/dist/lib/properties/field-picker.cjs +2 -0
  48. package/dist/lib/properties/field-picker.cjs.map +1 -0
  49. package/dist/lib/properties/field-picker.js +1 -1
  50. package/dist/lib/properties/flexible-table.cjs +2 -0
  51. package/dist/lib/properties/flexible-table.cjs.map +1 -0
  52. package/dist/lib/properties/flexible-table.js +1 -1
  53. package/dist/lib/properties/getPythonHints.cjs +2 -0
  54. package/dist/lib/properties/getPythonHints.cjs.map +1 -0
  55. package/dist/lib/properties/getPythonHints.js +1 -1
  56. package/dist/lib/properties/getPythonHints.js.map +1 -1
  57. package/dist/lib/properties.cjs +2 -0
  58. package/dist/lib/properties.cjs.map +1 -0
  59. package/dist/lib/properties.js +1 -1
  60. package/dist/lib/tooltip.cjs +2 -0
  61. package/dist/lib/tooltip.cjs.map +1 -0
  62. package/dist/lib/tooltip.js +1 -1
  63. package/dist/lib/tooltip.js.map +1 -1
  64. package/dist/styles/common-canvas.min.css +1 -1
  65. package/dist/styles/common-canvas.min.css.map +1 -1
  66. package/locales/common-canvas/locales/de.json +0 -1
  67. package/locales/common-canvas/locales/es.json +0 -1
  68. package/locales/common-canvas/locales/fr.json +0 -1
  69. package/locales/common-canvas/locales/it.json +0 -1
  70. package/locales/common-canvas/locales/ja.json +0 -1
  71. package/locales/common-canvas/locales/ko.json +0 -1
  72. package/locales/common-canvas/locales/pt-BR.json +0 -1
  73. package/locales/common-canvas/locales/sv.json +0 -1
  74. package/locales/common-canvas/locales/zh-CN.json +0 -1
  75. package/locales/common-canvas/locales/zh-TW.json +0 -1
  76. package/locales/common-properties/locales/de.json +1 -0
  77. package/locales/common-properties/locales/es.json +1 -0
  78. package/locales/common-properties/locales/fr.json +1 -0
  79. package/locales/common-properties/locales/it.json +1 -0
  80. package/locales/common-properties/locales/ja.json +1 -0
  81. package/locales/common-properties/locales/ko.json +1 -0
  82. package/locales/common-properties/locales/pt-BR.json +1 -0
  83. package/locales/common-properties/locales/sv.json +1 -0
  84. package/locales/common-properties/locales/zh-CN.json +1 -0
  85. package/locales/common-properties/locales/zh-TW.json +1 -0
  86. package/locales/palette/locales/de.json +4 -1
  87. package/locales/palette/locales/es.json +4 -1
  88. package/locales/palette/locales/fr.json +4 -1
  89. package/locales/palette/locales/it.json +4 -1
  90. package/locales/palette/locales/ja.json +4 -1
  91. package/locales/palette/locales/ko.json +4 -1
  92. package/locales/palette/locales/pt-BR.json +4 -1
  93. package/locales/palette/locales/sv.json +4 -1
  94. package/locales/palette/locales/zh-CN.json +4 -1
  95. package/locales/palette/locales/zh-TW.json +4 -1
  96. package/locales/toolbar/locales/de.json +1 -5
  97. package/locales/toolbar/locales/es.json +1 -5
  98. package/locales/toolbar/locales/fr.json +1 -5
  99. package/locales/toolbar/locales/it.json +1 -5
  100. package/locales/toolbar/locales/ja.json +1 -5
  101. package/locales/toolbar/locales/ko.json +1 -5
  102. package/locales/toolbar/locales/pt-BR.json +1 -5
  103. package/locales/toolbar/locales/sv.json +1 -5
  104. package/locales/toolbar/locales/zh-CN.json +1 -5
  105. package/locales/toolbar/locales/zh-TW.json +1 -5
  106. package/package.json +12 -6
  107. package/rollup.config.js +2 -2
  108. package/src/common-canvas/canvas-controller.js +8 -0
  109. package/src/common-canvas/svg-canvas-renderer.js +41 -20
  110. package/src/common-canvas/svg-canvas-utils-drag-objects.js +0 -6
  111. package/src/common-canvas/svg-canvas-utils-textarea.js +4 -2
  112. package/src/common-properties/controls/expression/expression.scss +16 -3
  113. package/src/object-model/api-pipeline.js +4 -0
  114. package/src/object-model/pipeline-in-handler.js +3 -0
  115. package/src/object-model/pipeline-out-handler.js +5 -0
  116. package/src/object-model/redux/reducers/canvasinfo.js +1 -0
  117. package/src/object-model/redux/reducers/links.js +10 -0
  118. package/stats.html +1 -1
  119. package/types/canvas-controller.d.ts +27 -0
  120. package/types/index.d.cts +21 -0
  121. package/dist/Close-46e688b8.js +0 -2
  122. package/dist/Close-ced84b07.js +0 -2
  123. package/dist/canvas-controller-129092b6.js +0 -2
  124. package/dist/canvas-controller-129092b6.js.map +0 -1
  125. package/dist/canvas-controller-a0473a44.js +0 -2
  126. package/dist/canvas-controller-a0473a44.js.map +0 -1
  127. package/dist/common-canvas-08eacbb2.js +0 -2
  128. package/dist/common-canvas-08eacbb2.js.map +0 -1
  129. package/dist/common-canvas-dcc39cb1.js +0 -2
  130. package/dist/common-canvas-dcc39cb1.js.map +0 -1
  131. package/dist/common-canvas.es.js +0 -2
  132. package/dist/common-canvas.es.js.map +0 -1
  133. package/dist/common-properties-36906b79.js +0 -2
  134. package/dist/common-properties-5695fc39.js +0 -2
  135. package/dist/flexible-table-d1d15df2.js +0 -2
  136. package/dist/flexible-table-d624ee4e.js +0 -2
  137. package/dist/index-50b29b6d.js +0 -2
  138. package/dist/index-5383b802.js +0 -2
  139. package/dist/lib/canvas-controller.es.js +0 -2
  140. package/dist/lib/canvas-controller.es.js.map +0 -1
  141. package/dist/lib/canvas.es.js +0 -2
  142. package/dist/lib/canvas.es.js.map +0 -1
  143. package/dist/lib/command-stack.es.js +0 -2
  144. package/dist/lib/command-stack.es.js.map +0 -1
  145. package/dist/lib/context-menu.es.js +0 -2
  146. package/dist/lib/context-menu.es.js.map +0 -1
  147. package/dist/lib/properties/clem.es.js +0 -2
  148. package/dist/lib/properties/clem.es.js.map +0 -1
  149. package/dist/lib/properties/field-picker.es.js +0 -2
  150. package/dist/lib/properties/field-picker.es.js.map +0 -1
  151. package/dist/lib/properties/flexible-table.es.js +0 -2
  152. package/dist/lib/properties/flexible-table.es.js.map +0 -1
  153. package/dist/lib/properties/getPythonHints.es.js +0 -2
  154. package/dist/lib/properties/getPythonHints.es.js.map +0 -1
  155. package/dist/lib/properties.es.js +0 -2
  156. package/dist/lib/properties.es.js.map +0 -1
  157. package/dist/lib/tooltip.es.js +0 -2
  158. package/dist/lib/tooltip.es.js.map +0 -1
package/package.json CHANGED
@@ -1,14 +1,19 @@
1
1
  {
2
2
  "name": "@elyra/canvas",
3
- "version": "13.43.2",
3
+ "version": "13.44.0",
4
4
  "description": "Elyra common-canvas",
5
- "main": "dist/common-canvas.js",
6
- "module": "dist/common-canvas.es.js",
5
+ "main": "dist/common-canvas.cjs",
6
+ "module": "dist/common-canvas.js",
7
7
  "exports": {
8
8
  ".": {
9
- "import": "./dist/common-canvas.es.js",
10
- "require": "./dist/common-canvas.js",
11
- "types": "./types/index.d.ts"
9
+ "import": {
10
+ "types": "./types/index.d.ts",
11
+ "default": "./dist/common-canvas.js"
12
+ },
13
+ "require": {
14
+ "types": "./types/index.d.cts",
15
+ "default": "./dist/common-canvas.cjs"
16
+ }
12
17
  },
13
18
  "./src/*": "./src/*",
14
19
  "./locales/*/locales": "./locales/*/locales/index.js",
@@ -24,6 +29,7 @@
24
29
  },
25
30
  "license": "Apache-2.0",
26
31
  "sideEffects": false,
32
+ "type": "module",
27
33
  "types": "./types/index.d.ts",
28
34
  "scripts": {
29
35
  "build": "npx stylelint '**/*.scss' && grunt && rollup -c",
package/rollup.config.js CHANGED
@@ -42,14 +42,14 @@ export default {
42
42
  },
43
43
  output: [
44
44
  {
45
- entryFileNames: "[name].js",
45
+ entryFileNames: "[name].cjs",
46
46
  dir: "./dist",
47
47
  format: "cjs",
48
48
  sourcemap: true,
49
49
  exports: "auto"
50
50
  },
51
51
  {
52
- entryFileNames: "[name].[format].js",
52
+ entryFileNames: "[name].js",
53
53
  dir: "./dist",
54
54
  format: "esm",
55
55
  sourcemap: true
@@ -1353,6 +1353,14 @@ export default class CanvasController {
1353
1353
  this.objectModel.getAPIPipeline(pipelineId).setLinkProperties(linkId, properties);
1354
1354
  }
1355
1355
 
1356
+ // Sets the link parameters
1357
+ // linkId - The ID of the link
1358
+ // parameters - An array of parameters
1359
+ // pipelineId - The ID of the pipeline
1360
+ setLinkParameters(linkId, parameters, pipelineId) {
1361
+ this.objectModel.getAPIPipeline(pipelineId).setLinkParameters(linkId, parameters);
1362
+ }
1363
+
1356
1364
  // Sets the source properties in the data link identified by the linkId. The
1357
1365
  // srcNodeId and srcNodePortId will be set to the values provided. If
1358
1366
  // srcNodePortId is set to null the current srcNodePortId will be removed
@@ -1913,7 +1913,7 @@ export default class SVGCanvasRenderer {
1913
1913
  // Node Body
1914
1914
  nonBindingNodeGrps
1915
1915
  .selectChildren(".d3-node-body-outline")
1916
- .data((d) => (d.layout.nodeShapeDisplay ? [d] : []), (d) => d.id)
1916
+ .data((d) => (d.layout.nodeShapeDisplay || CanvasUtils.isExpandedSupernode(d) ? [d] : []), (d) => d.id)
1917
1917
  .join(
1918
1918
  (enter) =>
1919
1919
  enter
@@ -4724,20 +4724,26 @@ export default class SVGCanvasRenderer {
4724
4724
  }
4725
4725
 
4726
4726
  displayLinksSubset(selection, linksArray) {
4727
- selection
4728
- .data(linksArray, (link) => link.id)
4729
- .join(
4730
- (enter) => this.createLinks(enter)
4731
- )
4732
- .attr("class", (d) => this.getLinkGroupClass(d))
4733
- .attr("tabindex", -1)
4734
- .attr("style", (d) => this.getLinkGrpStyle(d))
4735
- .attr("data-selected", (d) => (this.activePipeline.isSelected(d.id) ? true : null))
4736
- .attr("aria-label", (d) => CanvasUtils.getLinkAriaLabel(d, this.canvasController.labelUtil))
4737
- .attr("aria-roledescription", this.canvasController.labelUtil.getLabel("link.ariaRoleDescription"))
4738
- .call((joinedLinkGrps) => {
4739
- this.updateLinks(joinedLinkGrps, linksArray);
4740
- });
4727
+ // The D3 join operation can sometimes cause focus to be lost from any
4728
+ // currently focused object involved in the join (even if no new objects
4729
+ // have been created or if no objects are removed) so we preserve and
4730
+ // reinstate the focus after this operation is complete.
4731
+ this.preserveFocus(() => {
4732
+ selection
4733
+ .data(linksArray, (link) => link.id)
4734
+ .join(
4735
+ (enter) => this.createLinks(enter)
4736
+ )
4737
+ .attr("class", (d) => this.getLinkGroupClass(d))
4738
+ .attr("tabindex", -1)
4739
+ .attr("style", (d) => this.getLinkGrpStyle(d))
4740
+ .attr("data-selected", (d) => (this.activePipeline.isSelected(d.id) ? true : null))
4741
+ .attr("aria-label", (d) => CanvasUtils.getLinkAriaLabel(d, this.canvasController.labelUtil))
4742
+ .attr("aria-roledescription", this.canvasController.labelUtil.getLabel("link.ariaRoleDescription"))
4743
+ .call((joinedLinkGrps) => {
4744
+ this.updateLinks(joinedLinkGrps, linksArray);
4745
+ });
4746
+ });
4741
4747
  }
4742
4748
 
4743
4749
  // Creates all newly created links specified in the enter selection.
@@ -6578,11 +6584,8 @@ export default class SVGCanvasRenderer {
6578
6584
  // appears in the viewport. If the event was a MouseEvent we don't zoom to reveal
6579
6585
  // because it interferes with double-click events (also it's not necessary because
6580
6586
  // the object will be at least partially visible in the view port for it to be clicked).
6581
- // Also, we don't zoom to reveal if the object is a link in a full-page subflow
6582
- // because a) they are almost always on display and b) if the link is to, or from, a
6583
- // binding node zoomToReveal will cause the nodes to be incorrectly moved.
6584
- if (CanvasUtils.isKeyboardEvent(evt) &&
6585
- !(type === "link" && this.dispUtils.isDisplayingSubFlowFullPage())) {
6587
+ // Also, some objects should not cause a zoom, so check for them.
6588
+ if (CanvasUtils.isKeyboardEvent(evt) && this.shouldObjectCauseZoom(obj, type)) {
6586
6589
  const zoom = this.canvasController.getZoomToReveal([obj.id]);
6587
6590
 
6588
6591
  if (zoom) {
@@ -6602,6 +6605,24 @@ export default class SVGCanvasRenderer {
6602
6605
  }
6603
6606
  }
6604
6607
 
6608
+ // Returns true if the object should cause a 'zoom to reveal' to occur when focus
6609
+ // is moved to it. That is, if we are displaying a sub-flow full-page when:
6610
+ // 1. The object is a link - because
6611
+ // a) they are almost always on display and
6612
+ // b) if the link is to, or from, a binding node zoomToReveal will cause the
6613
+ // nodes to be incorrectly moved.
6614
+ // 2. The object is a binding node -- because they are always in a fixed position.
6615
+ shouldObjectCauseZoom(obj, type) {
6616
+ if (this.dispUtils.isDisplayingSubFlowFullPage()) {
6617
+ if (type === "link") {
6618
+ return false;
6619
+ } else if (type === "node" && CanvasUtils.isSuperBindingNode(obj)) {
6620
+ return false;
6621
+ }
6622
+ }
6623
+ return true;
6624
+ }
6625
+
6605
6626
  // Moves the focus highlighting to the next appropriate sub-object within
6606
6627
  // the parent object.
6607
6628
  setFocusNextSubObject(parentObj, evt) {
@@ -409,12 +409,6 @@ export default class SVGCanvasUtilsDragObjects {
409
409
  this.ren.displayMovedLinks();
410
410
  this.ren.displayCanvasAccoutrements();
411
411
 
412
- if (CanvasUtils.isExpandedSupernode(resizeObj)) {
413
- if (this.ren.dispUtils.isDisplayingSubFlow()) {
414
- this.ren.displayBindingNodesToFitSVG();
415
- }
416
- this.ren.superRenderers.forEach((renderer) => renderer.displaySVGToFitSupernode());
417
- }
418
412
  this.logger.logEndTimer("displayObjects");
419
413
  }
420
414
  }
@@ -88,7 +88,7 @@ export default class SvgCanvasTextArea {
88
88
  displayCommentTextArea(d, parentDomObj) {
89
89
  this.editingTextData = {
90
90
  id: d.id,
91
- comment: d,
91
+ focusReturn: d,
92
92
  text: d.content,
93
93
  singleLine: false,
94
94
  maxCharacters: null,
@@ -465,6 +465,7 @@ export default class SvgCanvasTextArea {
465
465
 
466
466
  this.editingTextData = {
467
467
  id: node.id,
468
+ focusReturn: node,
468
469
  text: node.label,
469
470
  singleLine: node.layout.labelSingleLine,
470
471
  maxCharacters: node.layout.labelMaxCharacters,
@@ -538,6 +539,7 @@ export default class SvgCanvasTextArea {
538
539
 
539
540
  this.editingTextData = {
540
541
  id: dec.id,
542
+ focusReturn: obj,
541
543
  text: dec.label,
542
544
  singleLine: dec.label_single_line || false,
543
545
  maxCharacters: dec.label_max_characters || null,
@@ -707,10 +709,10 @@ export default class SvgCanvasTextArea {
707
709
  // Tidy up
708
710
  this.foreignObjectComment.remove();
709
711
  this.foreignObjectComment = null;
710
- this.canvasController.setFocusObject(data.comment, null, true);
711
712
  }
712
713
  this.editingText = false;
713
714
  this.editingTextId = "";
715
+ this.canvasController.setFocusObject(data.focusReturn, null, true);
714
716
  }
715
717
 
716
718
  // Returns true if one of the keys that are allowed in the text area, when
@@ -69,7 +69,7 @@
69
69
 
70
70
  &.cm-focused {
71
71
  outline: 2px solid carbon.$focus;
72
- outline-offset: -2px;
72
+ outline-offset: 0px;
73
73
  }
74
74
 
75
75
  .cm-gutters {
@@ -96,14 +96,27 @@
96
96
  .cm-activeLine {
97
97
  background-color: carbon.$background-selected; // works for both dark n light
98
98
  }
99
- .cm-activeLineGutter {
100
- background-color: carbon.$text-placeholder;
99
+
100
+ .cm-lineNumbers .cm-gutterElement:not(.cm-activeLineGutter) {
101
101
  color: carbon.$text-primary;
102
102
  }
103
+
104
+ .cm-lineNumbers .cm-activeLineGutter {
105
+ background-clip: padding-box;
106
+ background-color: carbon.$layer-accent-hover-02;
107
+ color: carbon.$text-primary;
108
+ }
109
+
110
+ .cm-gutter.cm-foldGutter > div.cm-gutterElement.cm-activeLineGutter {
111
+ background-color: carbon.$layer-accent-hover-02;
112
+ color: carbon.$text-primary;
113
+ }
114
+
103
115
  &.cm-focused .cm-selectionBackground, ::selection {
104
116
  /* stylelint-disable-next-line declaration-no-important */
105
117
  background: carbon.$support-info-inverse !important; // add !important to override the selected text style
106
118
  }
119
+
107
120
  .cm-tooltip-autocomplete {
108
121
  z-index: 1110;
109
122
  background: carbon.$field-02; // Background color of the autocompletion menu to support dark mode
@@ -1382,6 +1382,10 @@ export default class APIPipeline {
1382
1382
  this.store.dispatch({ type: "SET_LINK_PROPERTIES", data: { linkId: linkId, linkProperties: linkProperties }, pipelineId: this.pipelineId });
1383
1383
  }
1384
1384
 
1385
+ setLinkParameters(linkId, parameters) {
1386
+ this.store.dispatch({ type: "SET_LINK_PARAMETERS", data: { linkId: linkId, parameters: parameters }, pipelineId: this.pipelineId });
1387
+ }
1388
+
1385
1389
  setNodeDataLinkSrcInfo(linkId, srcNodeId, srcNodePortId) {
1386
1390
  this.store.dispatch({ type: "SET_LINK_SRC_INFO", data: { linkId: linkId, srcNodeId: srcNodeId, srcNodePortId: srcNodePortId }, pipelineId: this.pipelineId });
1387
1391
  }
@@ -397,6 +397,9 @@ export default class PipelineInHandler {
397
397
  if (link.description) { // description is also optional
398
398
  newLink.description = link.description;
399
399
  }
400
+ if (link.parameters) { // parameters is also optional
401
+ newLink.parameters = link.parameters;
402
+ }
400
403
 
401
404
  return newLink;
402
405
  }
@@ -483,6 +483,11 @@ export default class PipelineOutHandler {
483
483
  if (link.description) {
484
484
  newLink.description = link.description;
485
485
  }
486
+
487
+ if (link.parameters) {
488
+ newLink.parameters = link.parameters;
489
+ }
490
+
486
491
  return newLink;
487
492
  }
488
493
  }
@@ -167,6 +167,7 @@ export default (state = {}, action) => {
167
167
  case "SET_NODE_MESSAGES":
168
168
  case "SET_NODE_DECORATIONS":
169
169
  case "SET_LINK_DECORATIONS":
170
+ case "SET_LINK_PARAMETERS":
170
171
  case "ADD_NODE_ATTR":
171
172
  case "REMOVE_NODE_ATTR":
172
173
  case "SET_NODE_LABEL":
@@ -135,6 +135,16 @@ export default (state = [], action) => {
135
135
  return link;
136
136
  });
137
137
 
138
+ case "SET_LINK_PARAMETERS":
139
+ return state.map((link, index) => {
140
+ if (action.data.linkId === link.id) {
141
+ const newLink = Object.assign({}, link);
142
+ newLink.parameters = action.data.parameters;
143
+ return newLink;
144
+ }
145
+ return link;
146
+ });
147
+
138
148
  case "SET_LINK_SRC_INFO":
139
149
  return state.map((link) => {
140
150
  if (link.id === action.data.linkId) {