@ship-ui/core 0.15.32 → 0.15.35

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.
@@ -758,6 +758,9 @@ class ShipBlueprintComponent {
758
758
  #ZOOM_SPEED;
759
759
  #MAX_ZOOM;
760
760
  #MIN_ZOOM;
761
+ #NODE_WIDTH;
762
+ #NODE_HEADER_HEIGHT;
763
+ #PORT_ROW_HEIGHT;
761
764
  #document;
762
765
  #platformId;
763
766
  #selfRef;
@@ -773,12 +776,17 @@ class ShipBlueprintComponent {
773
776
  #draggedNodeId;
774
777
  #dragOffset;
775
778
  #connections;
779
+ #canvasWidth;
780
+ #canvasHeight;
776
781
  #ctx;
777
782
  #resizeObserver;
778
783
  constructor() {
779
784
  this.#ZOOM_SPEED = 0.01;
780
785
  this.#MAX_ZOOM = 1.5;
781
786
  this.#MIN_ZOOM = 0.5;
787
+ this.#NODE_WIDTH = 180;
788
+ this.#NODE_HEADER_HEIGHT = 40;
789
+ this.#PORT_ROW_HEIGHT = 28;
782
790
  this.#document = inject(DOCUMENT);
783
791
  this.#platformId = inject(PLATFORM_ID);
784
792
  this.#selfRef = inject((ElementRef));
@@ -813,6 +821,31 @@ class ShipBlueprintComponent {
813
821
  this.#draggedNodeId = signal(null, ...(ngDevMode ? [{ debugName: "#draggedNodeId" }] : []));
814
822
  this.#dragOffset = signal(null, ...(ngDevMode ? [{ debugName: "#dragOffset" }] : []));
815
823
  this.#connections = signal([], ...(ngDevMode ? [{ debugName: "#connections" }] : []));
824
+ this.#canvasWidth = signal(0, ...(ngDevMode ? [{ debugName: "#canvasWidth" }] : []));
825
+ this.#canvasHeight = signal(0, ...(ngDevMode ? [{ debugName: "#canvasHeight" }] : []));
826
+ this.visibleNodes = computed(() => {
827
+ const nodes = this.nodes();
828
+ const panX = this.panX();
829
+ const panY = this.panY();
830
+ const zoom = this.zoomLevel();
831
+ const width = this.#canvasWidth();
832
+ const height = this.#canvasHeight();
833
+ if (width === 0 || height === 0) {
834
+ return nodes;
835
+ }
836
+ const bufferX = width / zoom;
837
+ const bufferY = height / zoom;
838
+ const viewbox = {
839
+ x1: -panX / zoom - bufferX,
840
+ y1: -panY / zoom - bufferY,
841
+ x2: (-panX + width) / zoom + bufferX,
842
+ y2: (-panY + height) / zoom + bufferY,
843
+ };
844
+ return nodes.filter((node) => {
845
+ const [x, y] = node.coordinates;
846
+ return x > viewbox.x1 && x < viewbox.x2 && y > viewbox.y1 && y < viewbox.y2;
847
+ });
848
+ }, ...(ngDevMode ? [{ debugName: "visibleNodes" }] : []));
816
849
  if (isPlatformBrowser(this.#platformId)) {
817
850
  effect(() => {
818
851
  this.asDots();
@@ -862,13 +895,71 @@ class ShipBlueprintComponent {
862
895
  this.#resizeObserver.disconnect();
863
896
  }
864
897
  }
865
- #validateNodes() {
866
- const duplicatePortIds = findDuplicatePortIDs(this.nodes());
867
- const duplicateNodeIds = findDuplicateNodeIDs(this.nodes());
868
- this.validationErrors.set({
869
- duplicateNodeIds,
870
- duplicatePortIds,
871
- });
898
+ onMouseUp(event) {
899
+ if (this.isLocked())
900
+ return;
901
+ this.endPan();
902
+ this.endNodeDrag();
903
+ }
904
+ onClick(event) {
905
+ const target = event.target;
906
+ if (this.draggingConnection()) {
907
+ if (!target.closest('.port')) {
908
+ this.cancelPortDrag();
909
+ }
910
+ }
911
+ else if (this.isLocked() && !target.closest('.midpoint-div')) {
912
+ this.closeMidpointDiv();
913
+ }
914
+ else {
915
+ this.#handleConnectionClick(event);
916
+ }
917
+ }
918
+ onEscape(event) {
919
+ if (this.draggingConnection()) {
920
+ this.cancelPortDrag();
921
+ }
922
+ else if (this.isLocked()) {
923
+ this.closeMidpointDiv();
924
+ }
925
+ }
926
+ onMouseMove(event) {
927
+ if (this.isLocked())
928
+ return;
929
+ if (this.#isNodeDragging()) {
930
+ this.nodeDrag(event);
931
+ }
932
+ else if (this.draggingConnection()) {
933
+ this.updatePathOnMove(event);
934
+ }
935
+ else {
936
+ this.pan(event);
937
+ if (this.isHoveringNode()) {
938
+ this.highlightedConnection.set(null);
939
+ }
940
+ else {
941
+ this.#checkConnectionHover(event);
942
+ }
943
+ }
944
+ }
945
+ onTouchMove(event) {
946
+ event.preventDefault();
947
+ if (this.isLocked())
948
+ return;
949
+ if (this.#isNodeDragging()) {
950
+ this.nodeDrag(event.touches[0]);
951
+ }
952
+ else if (this.draggingConnection()) {
953
+ this.updatePathOnMove(event.touches[0]);
954
+ }
955
+ else {
956
+ this.handleTouchMove(event);
957
+ }
958
+ }
959
+ onDocumentTouchEnd(event) {
960
+ if (this.isLocked())
961
+ return;
962
+ this.handleTouchEnd();
872
963
  }
873
964
  applyAutolayout() {
874
965
  const newNodes = layoutNodes(this.nodes());
@@ -883,6 +974,8 @@ class ShipBlueprintComponent {
883
974
  this.#ctx.scale(dpr, dpr);
884
975
  canvas.style.width = `${rect.width}px`;
885
976
  canvas.style.height = `${rect.height}px`;
977
+ this.#canvasWidth.set(rect.width);
978
+ this.#canvasHeight.set(rect.height);
886
979
  this.drawCanvas();
887
980
  }
888
981
  drawCanvas() {
@@ -975,72 +1068,6 @@ class ShipBlueprintComponent {
975
1068
  ctx.moveTo(x1, y1);
976
1069
  ctx.bezierCurveTo(x1 + dx, y1, x2 - dx, y2, x2, y2);
977
1070
  }
978
- onMouseUp(event) {
979
- if (this.isLocked())
980
- return;
981
- this.endPan();
982
- this.endNodeDrag();
983
- }
984
- onClick(event) {
985
- const target = event.target;
986
- if (this.draggingConnection()) {
987
- if (!target.closest('.port')) {
988
- this.cancelPortDrag();
989
- }
990
- }
991
- else if (this.isLocked() && !target.closest('.midpoint-div')) {
992
- this.closeMidpointDiv();
993
- }
994
- else {
995
- this.#handleConnectionClick(event);
996
- }
997
- }
998
- onEscape(event) {
999
- if (this.draggingConnection()) {
1000
- this.cancelPortDrag();
1001
- }
1002
- else if (this.isLocked()) {
1003
- this.closeMidpointDiv();
1004
- }
1005
- }
1006
- onMouseMove(event) {
1007
- if (this.isLocked())
1008
- return;
1009
- if (this.#isNodeDragging()) {
1010
- this.nodeDrag(event);
1011
- }
1012
- else if (this.draggingConnection()) {
1013
- this.updatePathOnMove(event);
1014
- }
1015
- else {
1016
- this.pan(event);
1017
- if (this.isHoveringNode()) {
1018
- this.highlightedConnection.set(null);
1019
- }
1020
- else {
1021
- this.#checkConnectionHover(event);
1022
- }
1023
- }
1024
- }
1025
- onTouchMove(event) {
1026
- event.preventDefault();
1027
- if (this.isLocked())
1028
- return;
1029
- if (this.#isNodeDragging()) {
1030
- this.nodeDrag(event.touches[0]);
1031
- }
1032
- else if (this.draggingConnection()) {
1033
- this.updatePathOnMove(event.touches[0]);
1034
- }
1035
- else {
1036
- this.handleTouchMove(event);
1037
- }
1038
- }
1039
- onDocumentTouchEnd(event) {
1040
- if (this.isLocked())
1041
- return;
1042
- this.handleTouchEnd();
1043
- }
1044
1071
  startNodeDrag(event, nodeId) {
1045
1072
  event.stopPropagation();
1046
1073
  event.preventDefault();
@@ -1163,16 +1190,25 @@ class ShipBlueprintComponent {
1163
1190
  if (!node)
1164
1191
  return [0, 0];
1165
1192
  const portEl = this.#selfRef.nativeElement.querySelector(`[data-node-id="${nodeId}"][data-port-id="${portId}"]`);
1166
- if (!portEl)
1193
+ if (portEl) {
1194
+ const nodeWrapper = this.#selfRef.nativeElement.querySelector('.nodes-wrapper');
1195
+ const wrapperRect = nodeWrapper.getBoundingClientRect();
1196
+ const portRect = portEl.getBoundingClientRect();
1197
+ const portCenterX = portRect.left + portRect.width / 2;
1198
+ const portCenterY = portRect.top + portRect.height / 2;
1199
+ const worldX = (portCenterX - wrapperRect.left) / this.zoomLevel();
1200
+ const worldY = (portCenterY - wrapperRect.top) / this.zoomLevel();
1201
+ return [worldX, worldY];
1202
+ }
1203
+ const isInput = node.inputs.some((p) => p.id === portId);
1204
+ const portIndex = isInput
1205
+ ? node.inputs.findIndex((p) => p.id === portId)
1206
+ : node.outputs.findIndex((p) => p.id === portId);
1207
+ if (portIndex === -1)
1167
1208
  return node.coordinates;
1168
- const nodeWrapper = this.#selfRef.nativeElement.querySelector('.nodes-wrapper');
1169
- const wrapperRect = nodeWrapper.getBoundingClientRect();
1170
- const portRect = portEl.getBoundingClientRect();
1171
- const portCenterX = portRect.left + portRect.width / 2;
1172
- const portCenterY = portRect.top + portRect.height / 2;
1173
- const worldX = (portCenterX - wrapperRect.left) / this.zoomLevel();
1174
- const worldY = (portCenterY - wrapperRect.top) / this.zoomLevel();
1175
- return [worldX, worldY];
1209
+ const portYOffset = this.#NODE_HEADER_HEIGHT + portIndex * this.#PORT_ROW_HEIGHT + this.#PORT_ROW_HEIGHT / 2;
1210
+ const portXOffset = isInput ? 0 : this.#NODE_WIDTH;
1211
+ return [node.coordinates[0] + portXOffset, node.coordinates[1] + portYOffset];
1176
1212
  }
1177
1213
  startPan(event) {
1178
1214
  if (this.isLocked())
@@ -1419,6 +1455,14 @@ class ShipBlueprintComponent {
1419
1455
  }
1420
1456
  return newCoordinates;
1421
1457
  }
1458
+ #validateNodes() {
1459
+ const duplicatePortIds = findDuplicatePortIDs(this.nodes());
1460
+ const duplicateNodeIds = findDuplicateNodeIDs(this.nodes());
1461
+ this.validationErrors.set({
1462
+ duplicateNodeIds,
1463
+ duplicatePortIds,
1464
+ });
1465
+ }
1422
1466
  #panToCoordinates(coords) {
1423
1467
  const [x, y] = coords;
1424
1468
  const rect = this.#selfRef.nativeElement.getBoundingClientRect();
@@ -1468,7 +1512,7 @@ class ShipBlueprintComponent {
1468
1512
  class="nodes-wrapper"
1469
1513
  [style.transform]="'translate(' + panX() + 'px, ' + panY() + 'px) scale(' + zoomLevel() + ')'"
1470
1514
  [style.transform-origin]="'0 0'">
1471
- @for (node of nodes(); track node.id) {
1515
+ @for (node of visibleNodes(); track node.id) {
1472
1516
  <sh-card
1473
1517
  class="node type-c"
1474
1518
  [style.transform]="getDisplayCoordinates(node)"
@@ -1561,7 +1605,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImpor
1561
1605
  class="nodes-wrapper"
1562
1606
  [style.transform]="'translate(' + panX() + 'px, ' + panY() + 'px) scale(' + zoomLevel() + ')'"
1563
1607
  [style.transform-origin]="'0 0'">
1564
- @for (node of nodes(); track node.id) {
1608
+ @for (node of visibleNodes(); track node.id) {
1565
1609
  <sh-card
1566
1610
  class="node type-c"
1567
1611
  [style.transform]="getDisplayCoordinates(node)"
@@ -1717,7 +1761,9 @@ class ShipCheckboxComponent {
1717
1761
  <sh-icon class="inherit indeterminate-indicator">minus-bold</sh-icon>
1718
1762
  </div>
1719
1763
 
1720
- <ng-content />
1764
+ <div class="label">
1765
+ <ng-content />
1766
+ </div>
1721
1767
  `, isInline: true, dependencies: [{ kind: "component", type: ShipIconComponent, selector: "sh-icon" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1722
1768
  }
1723
1769
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImport: i0, type: ShipCheckboxComponent, decorators: [{
@@ -1731,7 +1777,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.0", ngImpor
1731
1777
  <sh-icon class="inherit indeterminate-indicator">minus-bold</sh-icon>
1732
1778
  </div>
1733
1779
 
1734
- <ng-content />
1780
+ <div class="label">
1781
+ <ng-content />
1782
+ </div>
1735
1783
  `,
1736
1784
  changeDetection: ChangeDetectionStrategy.OnPush,
1737
1785
  }]