@elyra/canvas 13.47.0 → 13.47.1
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.
- package/dist/Close-10f1a909.js +2 -0
- package/dist/{Close-ec5c2552.js.map → Close-10f1a909.js.map} +1 -1
- package/dist/{Close-45a7703f.js → Close-c106c4d1.js} +2 -2
- package/dist/{Close-45a7703f.js.map → Close-c106c4d1.js.map} +1 -1
- package/dist/{canvas-controller-035b844d.js → canvas-controller-4a117bab.js} +2 -2
- package/dist/{canvas-controller-035b844d.js.map → canvas-controller-4a117bab.js.map} +1 -1
- package/dist/{canvas-controller-28eb66c7.js → canvas-controller-7077c820.js} +2 -2
- package/dist/{canvas-controller-28eb66c7.js.map → canvas-controller-7077c820.js.map} +1 -1
- package/dist/common-canvas-ad71b995.js +2 -0
- package/dist/common-canvas-ad71b995.js.map +1 -0
- package/dist/common-canvas-e6bd5796.js +2 -0
- package/dist/common-canvas-e6bd5796.js.map +1 -0
- package/dist/common-canvas.cjs +1 -1
- package/dist/common-canvas.js +1 -1
- package/dist/{common-properties-7744267c.js → common-properties-e6044a7e.js} +2 -2
- package/dist/{common-properties-7744267c.js.map → common-properties-e6044a7e.js.map} +1 -1
- package/dist/{common-properties-532a5a7e.js → common-properties-fd7dd5f2.js} +2 -2
- package/dist/{common-properties-532a5a7e.js.map → common-properties-fd7dd5f2.js.map} +1 -1
- package/dist/lib/canvas-controller.cjs +1 -1
- package/dist/lib/canvas-controller.js +1 -1
- package/dist/lib/canvas.cjs +1 -1
- package/dist/lib/canvas.js +1 -1
- package/dist/lib/properties.cjs +1 -1
- package/dist/lib/properties.js +1 -1
- package/dist/styles/common-canvas.min.css +1 -1
- package/dist/styles/common-canvas.min.css.map +1 -1
- package/package.json +2 -2
- package/src/common-canvas/svg-canvas-d3.scss +10 -6
- package/src/common-canvas/svg-canvas-renderer.js +8 -189
- package/src/common-canvas/svg-canvas-utils-nodes.js +227 -0
- package/src/object-model/layout-dimensions.js +2 -2
- package/src/palette/palette.scss +3 -2
- package/src/toolbar/toolbar.jsx +20 -8
- package/src/toolbar/toolbar.scss +1 -1
- package/stats.html +1 -1
- package/dist/Close-ec5c2552.js +0 -2
- package/dist/common-canvas-7076af7a.js +0 -2
- package/dist/common-canvas-7076af7a.js.map +0 -1
- package/dist/common-canvas-f222fa61.js +0 -2
- package/dist/common-canvas-f222fa61.js.map +0 -1
|
@@ -473,4 +473,231 @@ export default class SvgCanvasNodes {
|
|
|
473
473
|
});
|
|
474
474
|
return node;
|
|
475
475
|
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Returns an SVG path that draws the shape for a rectangle node. This is
|
|
479
|
+
* drawn as a path rather than an SVG rect element to keep calling code generic.
|
|
480
|
+
* highlightGap may be a number (uniform gap on all sides) or an object with
|
|
481
|
+
* leftGap, rightGap, topGap, bottomGap properties for per-side control.
|
|
482
|
+
*
|
|
483
|
+
* @param {Object} data - Node data with width and height properties.
|
|
484
|
+
* @param {number|Object} highlightGap - Outward inflation applied to all sides.
|
|
485
|
+
* @returns {string} SVG path string.
|
|
486
|
+
*/
|
|
487
|
+
static getRectangleNodeShapePath(data, highlightGap = 0) {
|
|
488
|
+
const gaps = typeof highlightGap === "object"
|
|
489
|
+
? highlightGap
|
|
490
|
+
: {
|
|
491
|
+
leftGap: highlightGap,
|
|
492
|
+
rightGap: highlightGap,
|
|
493
|
+
topGap: highlightGap,
|
|
494
|
+
bottomGap: highlightGap
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
const l = 0 - gaps.leftGap;
|
|
498
|
+
const t = 0 - gaps.topGap;
|
|
499
|
+
const r = data.width + gaps.rightGap;
|
|
500
|
+
const b = data.height + gaps.bottomGap;
|
|
501
|
+
|
|
502
|
+
return "M " + l + " " + t + " L " + r + " " + t + " " + r + " " + b + " " + l + " " + b + " Z";
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Returns an SVG path that draws a rectangle-with-rounded-corners node shape.
|
|
507
|
+
*
|
|
508
|
+
* @param {Object} data - Node data with width and height properties.
|
|
509
|
+
* @param {number} highlightGap - Uniform outward inflation applied to all sides.
|
|
510
|
+
* @returns {string} SVG path string.
|
|
511
|
+
*/
|
|
512
|
+
static getRectRoundCornersShapePath(data, highlightGap = 0) {
|
|
513
|
+
const c = 10; // Corner size
|
|
514
|
+
const l = 0 - highlightGap;
|
|
515
|
+
const t = 0 - highlightGap;
|
|
516
|
+
const r = data.width + highlightGap;
|
|
517
|
+
const b = data.height + highlightGap;
|
|
518
|
+
const lc = l + c;
|
|
519
|
+
const rc = r - c;
|
|
520
|
+
const tc = t + c;
|
|
521
|
+
const bc = b - c;
|
|
522
|
+
|
|
523
|
+
return "M " + lc + " " + t + " L " + rc + " " + t + " " +
|
|
524
|
+
"Q " + r + " " + t + " " + r + " " + tc + " " +
|
|
525
|
+
"L " + r + " " + bc + " " +
|
|
526
|
+
"Q " + r + " " + b + " " + rc + " " + b + " " +
|
|
527
|
+
"L " + lc + " " + b + " " +
|
|
528
|
+
"Q " + l + " " + b + " " + l + " " + bc + " " +
|
|
529
|
+
"L " + l + " " + tc + " " +
|
|
530
|
+
"Q " + l + " " + t + " " + lc + " " + t +
|
|
531
|
+
" Z";
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Returns an SVG path for the 'port-arcs' outline shape that encloses both
|
|
536
|
+
* the node body and all ports regardless of where the ports are positioned.
|
|
537
|
+
* The highlightGap uniformly inflates the outline away from the node and ports.
|
|
538
|
+
*
|
|
539
|
+
* The path is built by walking each side of the inflated node rectangle in
|
|
540
|
+
* order and inserting an outward circular arc bump wherever a port's arc
|
|
541
|
+
* circle protrudes through that side. Port arcs are emitted sorted along the
|
|
542
|
+
* walking direction so the path never doubles back.
|
|
543
|
+
*
|
|
544
|
+
* @param {Object} data - Node data object with width, height, inputs, outputs, layout.
|
|
545
|
+
* @param {number} highlightGap - Additional outward offset applied to the whole outline.
|
|
546
|
+
* @returns {string} SVG path string.
|
|
547
|
+
*/
|
|
548
|
+
static getPortArcsNodeShapePath(data, highlightGap = 0) {
|
|
549
|
+
const gap = highlightGap || 0;
|
|
550
|
+
const arcR = data.layout.portArcRadius;
|
|
551
|
+
const allPorts = [...(data.inputs || []), ...(data.outputs || [])];
|
|
552
|
+
|
|
553
|
+
// Inflated rectangle bounds
|
|
554
|
+
const L = -gap;
|
|
555
|
+
const T = -gap;
|
|
556
|
+
const R = data.width + gap;
|
|
557
|
+
const B = data.height + gap;
|
|
558
|
+
|
|
559
|
+
// Returns the two y-intercepts where a port's arc circle crosses a
|
|
560
|
+
// vertical edge at xEdge, or null if it does not reach the edge.
|
|
561
|
+
const verticalEdgeIntercepts = (port, xEdge) => {
|
|
562
|
+
const dx = xEdge - port.cx;
|
|
563
|
+
const disc = (arcR * arcR) - (dx * dx);
|
|
564
|
+
if (disc < 0) {
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
const half = Math.sqrt(disc);
|
|
568
|
+
return [port.cy - half, port.cy + half];
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
// Returns the two x-intercepts where a port's arc circle crosses a
|
|
572
|
+
// horizontal edge at yEdge, or null if it does not reach the edge.
|
|
573
|
+
const horizEdgeIntercepts = (port, yEdge) => {
|
|
574
|
+
const dy = yEdge - port.cy;
|
|
575
|
+
const disc = (arcR * arcR) - (dy * dy);
|
|
576
|
+
if (disc < 0) {
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
const half = Math.sqrt(disc);
|
|
580
|
+
return [port.cx - half, port.cx + half];
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
// Builds the segment of the path that walks along a vertical edge
|
|
584
|
+
// (left or right) inserting outward arc bumps for ports that protrude.
|
|
585
|
+
// xEdge - x coordinate of the inflated edge line.
|
|
586
|
+
// fromY/toY - start and end y along the edge (fromY < toY when going
|
|
587
|
+
// downward; toY < fromY when going upward).
|
|
588
|
+
// outwardSweep - SVG arc sweep-flag (1 = clockwise, 0 = anti-clockwise).
|
|
589
|
+
const verticalEdgeWithBumps = (xEdge, fromY, toY, outwardSweep) => {
|
|
590
|
+
const goingDown = toY > fromY;
|
|
591
|
+
const exterior = goingDown ? "right" : "left";
|
|
592
|
+
|
|
593
|
+
// Collect ports whose arc circles protrude through this edge, keeping
|
|
594
|
+
// only those whose centre is on the exterior side (or on the edge
|
|
595
|
+
// itself) so that interior ports don't create outward bumps.
|
|
596
|
+
const bumps = [];
|
|
597
|
+
allPorts.forEach((port) => {
|
|
598
|
+
if (!verticalEdgeIntercepts(port, xEdge)) {
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
const isExterior = exterior === "right"
|
|
602
|
+
? port.cx >= data.width - data.layout.portArcRadius
|
|
603
|
+
: port.cx <= data.layout.portArcRadius;
|
|
604
|
+
if (!isExterior) {
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
// Arc endpoints are always the full diameter span centred on port.cy,
|
|
608
|
+
// clamped to the edge's travel range so arcs near corners don't overshoot.
|
|
609
|
+
const yMin = Math.min(fromY, toY);
|
|
610
|
+
const yMax = Math.max(fromY, toY);
|
|
611
|
+
const entryY = Math.max(port.cy - arcR, yMin);
|
|
612
|
+
const exitY = Math.min(port.cy + arcR, yMax);
|
|
613
|
+
if (exitY > entryY) {
|
|
614
|
+
bumps.push({ entryY, exitY, port });
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
// Sort bumps in the direction of travel.
|
|
619
|
+
bumps.sort((a, b) => (goingDown ? a.entryY - b.entryY : b.entryY - a.entryY));
|
|
620
|
+
|
|
621
|
+
let segment = "";
|
|
622
|
+
let cursor = fromY;
|
|
623
|
+
bumps.forEach((bump) => {
|
|
624
|
+
const bumpEntry = goingDown ? bump.entryY : bump.exitY;
|
|
625
|
+
const bumpExit = goingDown ? bump.exitY : bump.entryY;
|
|
626
|
+
if (goingDown ? bumpEntry > cursor : bumpEntry < cursor) {
|
|
627
|
+
segment += ` L ${xEdge} ${bumpEntry}`;
|
|
628
|
+
}
|
|
629
|
+
segment += ` A ${arcR} ${arcR} 180 0 ${outwardSweep} ${xEdge} ${bumpExit}`;
|
|
630
|
+
cursor = bumpExit;
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
// Final straight segment to the corner.
|
|
634
|
+
if (goingDown ? cursor < toY : cursor > toY) {
|
|
635
|
+
segment += ` L ${xEdge} ${toY}`;
|
|
636
|
+
}
|
|
637
|
+
return segment;
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
// Builds the segment of the path that walks along a horizontal edge
|
|
641
|
+
// (top or bottom) inserting outward arc bumps for ports that protrude.
|
|
642
|
+
// yEdge - y coordinate of the inflated edge line.
|
|
643
|
+
// fromX/toX - start and end x along the edge.
|
|
644
|
+
// outwardSweep - SVG arc sweep-flag (1 = clockwise, 0 = anti-clockwise).
|
|
645
|
+
const horizEdgeWithBumps = (yEdge, fromX, toX, outwardSweep) => {
|
|
646
|
+
const goingRight = toX > fromX;
|
|
647
|
+
const exterior = goingRight ? "top" : "bottom";
|
|
648
|
+
|
|
649
|
+
const bumps = [];
|
|
650
|
+
allPorts.forEach((port) => {
|
|
651
|
+
if (!horizEdgeIntercepts(port, yEdge)) {
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
const isExterior = exterior === "top"
|
|
655
|
+
? port.cy <= data.layout.portArcRadius
|
|
656
|
+
: port.cy >= data.height - data.layout.portArcRadius;
|
|
657
|
+
if (!isExterior) {
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
// Arc endpoints are always the full diameter span centred on port.cx,
|
|
661
|
+
// clamped to the edge's travel range so arcs near corners don't overshoot.
|
|
662
|
+
const xMin = Math.min(fromX, toX);
|
|
663
|
+
const xMax = Math.max(fromX, toX);
|
|
664
|
+
const entryX = Math.max(port.cx - arcR, xMin);
|
|
665
|
+
const exitX = Math.min(port.cx + arcR, xMax);
|
|
666
|
+
if (exitX > entryX) {
|
|
667
|
+
bumps.push({ entryX, exitX, port });
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
bumps.sort((a, b) => (goingRight ? a.entryX - b.entryX : b.entryX - a.entryX));
|
|
672
|
+
|
|
673
|
+
let segment = "";
|
|
674
|
+
let cursor = fromX;
|
|
675
|
+
bumps.forEach((bump) => {
|
|
676
|
+
const bumpEntry = goingRight ? bump.entryX : bump.exitX;
|
|
677
|
+
const bumpExit = goingRight ? bump.exitX : bump.entryX;
|
|
678
|
+
if (goingRight ? bumpEntry > cursor : bumpEntry < cursor) {
|
|
679
|
+
segment += ` L ${bumpEntry} ${yEdge}`;
|
|
680
|
+
}
|
|
681
|
+
segment += ` A ${arcR} ${arcR} 180 0 ${outwardSweep} ${bumpExit} ${yEdge}`;
|
|
682
|
+
cursor = bumpExit;
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
if (goingRight ? cursor < toX : cursor > toX) {
|
|
686
|
+
segment += ` L ${toX} ${yEdge}`;
|
|
687
|
+
}
|
|
688
|
+
return segment;
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
// Assemble the full closed path walking clockwise around the inflated rectangle.
|
|
692
|
+
// All four edges use sweep=1 to produce outward-facing bulges. In SVG's y-down
|
|
693
|
+
// coordinate system with the entry/exit reversal logic in horizEdgeWithBumps,
|
|
694
|
+
// sweep=1 consistently bows away from the node centre for all sides.
|
|
695
|
+
let path = `M ${L} ${T}`;
|
|
696
|
+
path += horizEdgeWithBumps(T, L, R, 1); // top edge, left-to-right, bumps upward
|
|
697
|
+
path += verticalEdgeWithBumps(R, T, B, 1); // right edge, top-to-bottom, bumps rightward
|
|
698
|
+
path += horizEdgeWithBumps(B, R, L, 1); // bottom edge, right-to-left, bumps downward
|
|
699
|
+
path += verticalEdgeWithBumps(L, B, T, 1); // left edge, bottom-to-top, bumps leftward
|
|
700
|
+
path += " Z";
|
|
701
|
+
return path;
|
|
702
|
+
}
|
|
476
703
|
}
|
|
@@ -120,7 +120,7 @@ const horizontalDefaultLayout = {
|
|
|
120
120
|
dropShadow: true,
|
|
121
121
|
|
|
122
122
|
// The gap between a node and its selection highlight rectangle
|
|
123
|
-
nodeHighlightGap:
|
|
123
|
+
nodeHighlightGap: 4,
|
|
124
124
|
|
|
125
125
|
// Allows the user to resize the node.
|
|
126
126
|
nodeResizable: false,
|
|
@@ -444,7 +444,7 @@ const horizontalDefaultLayout = {
|
|
|
444
444
|
commentCornerResizeArea: 10,
|
|
445
445
|
|
|
446
446
|
// The gap between a comment and its selection highlight rectangle
|
|
447
|
-
commentHighlightGap:
|
|
447
|
+
commentHighlightGap: 4,
|
|
448
448
|
|
|
449
449
|
// The gap between a comment and its sizing area rectangle
|
|
450
450
|
commentSizingArea: 10,
|
package/src/palette/palette.scss
CHANGED
|
@@ -158,7 +158,7 @@ $palette-dialog-list-item-height: 46px;
|
|
|
158
158
|
|
|
159
159
|
.palette-list-item-icon {
|
|
160
160
|
width: 28px; /* Not specifying height preserves the image's aspect ratio. */
|
|
161
|
-
|
|
161
|
+
fill: carbon.$icon-primary;
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
.palette-list-item-text-div {
|
|
@@ -432,7 +432,7 @@ $palette-dialog-list-item-height: 46px;
|
|
|
432
432
|
.palette-list-item-icon {
|
|
433
433
|
width: 28px; /* Not specifying height preserves the image's aspect ratio. */
|
|
434
434
|
line-height: $palette-dialog-list-item-height; /* Must be same as containing div height */
|
|
435
|
-
|
|
435
|
+
fill: carbon.$icon-primary;
|
|
436
436
|
margin-left: 0;
|
|
437
437
|
margin-right: 10px;
|
|
438
438
|
}
|
|
@@ -542,6 +542,7 @@ $palette-dialog-list-item-height: 46px;
|
|
|
542
542
|
.palette-dialog-grid-node-icon .node-icon {
|
|
543
543
|
width: carbon.$spacing-10;
|
|
544
544
|
height: carbon.$spacing-10;
|
|
545
|
+
fill: carbon.$icon-primary;
|
|
545
546
|
}
|
|
546
547
|
|
|
547
548
|
.palette-dialog-grid-node-icon-warning {
|
package/src/toolbar/toolbar.jsx
CHANGED
|
@@ -96,7 +96,7 @@ class Toolbar extends React.Component {
|
|
|
96
96
|
// toolbar config is updated and the current focusAction item is removed.
|
|
97
97
|
componentDidUpdate() {
|
|
98
98
|
const focusableItems = this.getFocusableItemRefs();
|
|
99
|
-
const index =
|
|
99
|
+
const index = this.getFocusActionIndex(focusableItems);
|
|
100
100
|
|
|
101
101
|
if (focusableItems.length === 0 && this.state.focusAction !== "disabled") {
|
|
102
102
|
this.setFocusOnFirstDisabledItem();
|
|
@@ -122,15 +122,16 @@ class Toolbar extends React.Component {
|
|
|
122
122
|
this.isFocusInToolbar = true;
|
|
123
123
|
|
|
124
124
|
// If focus occurs because of a click on the toolbar body
|
|
125
|
-
// (not on a button)
|
|
126
|
-
//
|
|
125
|
+
// (not on a button), move focus to the first item only if
|
|
126
|
+
// no button currently has focus (focusAction === "toolbar").
|
|
127
|
+
// Otherwise, maintain focus on the button specified by focusAction.
|
|
127
128
|
if (evt.target?.classList?.contains("toolbar-div")) {
|
|
128
129
|
if (this.state.focusAction === "toolbar") {
|
|
129
130
|
this.setFocusOnFirstItem();
|
|
130
|
-
|
|
131
|
-
|
|
132
131
|
} else {
|
|
133
|
-
|
|
132
|
+
// Keep focus on the button specified by this.state.focusAction
|
|
133
|
+
const currentFocusRef = this.getFocusActionRef();
|
|
134
|
+
currentFocusRef?.current?.buttonItemRef?.current?.setButtonFocus();
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
137
|
}
|
|
@@ -279,7 +280,7 @@ class Toolbar extends React.Component {
|
|
|
279
280
|
}
|
|
280
281
|
|
|
281
282
|
getPreviousItemRef(focusableItemRefs) {
|
|
282
|
-
const index =
|
|
283
|
+
const index = this.getFocusActionIndex(focusableItemRefs);
|
|
283
284
|
if (index === 0) {
|
|
284
285
|
return focusableItemRefs[focusableItemRefs.length - 1];
|
|
285
286
|
}
|
|
@@ -287,13 +288,24 @@ class Toolbar extends React.Component {
|
|
|
287
288
|
}
|
|
288
289
|
|
|
289
290
|
getNextItemRef(focusableItemRefs) {
|
|
290
|
-
const index =
|
|
291
|
+
const index = this.getFocusActionIndex(focusableItemRefs);
|
|
291
292
|
if (index === focusableItemRefs.length - 1) {
|
|
292
293
|
return focusableItemRefs[0];
|
|
293
294
|
}
|
|
294
295
|
return focusableItemRefs[index + 1];
|
|
295
296
|
}
|
|
296
297
|
|
|
298
|
+
// Returns the index of the item in focusableItemRefs that matches this.state.focusAction
|
|
299
|
+
getFocusActionIndex(focusableItemRefs) {
|
|
300
|
+
return focusableItemRefs.findIndex((item) => this.getRefAction(item) === this.state.focusAction);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Returns the ref of the item that matches this.state.focusAction
|
|
304
|
+
getFocusActionRef() {
|
|
305
|
+
const focusableItemRefs = this.getFocusableItemRefs();
|
|
306
|
+
return focusableItemRefs.find((ref) => this.getRefAction(ref) === this.state.focusAction);
|
|
307
|
+
}
|
|
308
|
+
|
|
297
309
|
getRefAction(ref) {
|
|
298
310
|
return ref.current?.getAction();
|
|
299
311
|
}
|
package/src/toolbar/toolbar.scss
CHANGED
|
@@ -370,7 +370,7 @@ $toolbar-divider-width: 1px;
|
|
|
370
370
|
}
|
|
371
371
|
|
|
372
372
|
&.disabled.default {
|
|
373
|
-
color: carbon.$
|
|
373
|
+
color: carbon.$icon-disabled;
|
|
374
374
|
fill: carbon.$button-disabled; // For custom svg images
|
|
375
375
|
& .toolbar-tick-mark {
|
|
376
376
|
fill: carbon.$button-disabled;
|