@pixerate/editor-svelte 0.4.0 → 0.5.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.
@@ -651,22 +651,578 @@ function restorePanelPointerEvents(panelSelector = ".svelte-flow__panel") {
651
651
  panel.style.pointerEvents = "auto";
652
652
  });
653
653
  }
654
+
655
+ // src/canvas/createCanvasTrajectory.ts
656
+ import { cubicOut as cubicOut2 } from "svelte/easing";
657
+ var linearTrajectory = (origin, target, progress) => {
658
+ return {
659
+ position: {
660
+ x: origin.x + (target.x - origin.x) * progress,
661
+ y: origin.y + (target.y - origin.y) * progress
662
+ },
663
+ scale: progress,
664
+ opacity: Math.min(1, progress * 1.5)
665
+ };
666
+ };
667
+ function createFanOutTrajectory(options = {}) {
668
+ const {
669
+ curvature = 0.4,
670
+ startScale = 0,
671
+ endScale = 1,
672
+ startOpacity = 0,
673
+ endOpacity = 1
674
+ } = options;
675
+ return (origin, target, progress, index, total) => {
676
+ const dx = target.x - origin.x;
677
+ const dy = target.y - origin.y;
678
+ const normIndex = total > 1 ? (index - (total - 1) / 2) / ((total - 1) / 2) : 0;
679
+ const midX = origin.x + dx * 0.4;
680
+ const midY = origin.y + dy * 0.1 - normIndex * Math.abs(dx) * 0.15 * curvature;
681
+ const t = progress;
682
+ const oneMinusT = 1 - t;
683
+ const x = oneMinusT * oneMinusT * origin.x + 2 * oneMinusT * t * midX + t * t * target.x;
684
+ const y = oneMinusT * oneMinusT * origin.y + 2 * oneMinusT * t * midY + t * t * target.y;
685
+ const scale = startScale + (endScale - startScale) * progress;
686
+ const opacity = startOpacity + (endOpacity - startOpacity) * progress;
687
+ return {
688
+ position: { x, y },
689
+ scale,
690
+ opacity
691
+ };
692
+ };
693
+ }
694
+ function createBezierTrajectory(getControlPoint) {
695
+ return (origin, target, progress) => {
696
+ const cp = getControlPoint(origin, target);
697
+ const t = progress;
698
+ const oneMinusT = 1 - t;
699
+ const x = oneMinusT * oneMinusT * origin.x + 2 * oneMinusT * t * cp.x + t * t * target.x;
700
+ const y = oneMinusT * oneMinusT * origin.y + 2 * oneMinusT * t * cp.y + t * t * target.y;
701
+ return {
702
+ position: { x, y },
703
+ scale: progress,
704
+ opacity: progress
705
+ };
706
+ };
707
+ }
708
+ function runMultiNodeTransition(transitions, options = {}) {
709
+ if (transitions.length === 0) {
710
+ options.onComplete?.();
711
+ return () => {
712
+ };
713
+ }
714
+ const { defaultDuration = 400, defaultEasing = cubicOut2, onUpdate, onComplete } = options;
715
+ let isCancelled = false;
716
+ const startTime = Date.now();
717
+ const items = transitions.map((tr, i) => ({
718
+ ...tr,
719
+ index: tr.index ?? i,
720
+ total: tr.total ?? transitions.length,
721
+ duration: tr.duration ?? defaultDuration,
722
+ easing: tr.easing ?? defaultEasing,
723
+ delay: tr.delay ?? 0,
724
+ trajectory: tr.trajectory ?? linearTrajectory
725
+ }));
726
+ function frame() {
727
+ if (isCancelled) return;
728
+ const now = Date.now();
729
+ let allCompleted = true;
730
+ for (let i = 0; i < items.length; i++) {
731
+ const item = items[i];
732
+ const elapsed = now - (startTime + item.delay);
733
+ if (elapsed < 0) {
734
+ allCompleted = false;
735
+ continue;
736
+ }
737
+ const rawProgress = item.duration <= 0 ? 1 : Math.min(elapsed / item.duration, 1);
738
+ const progress = item.duration <= 0 ? 1 : item.easing(rawProgress);
739
+ const point = item.trajectory(item.from, item.to, progress, item.index, item.total);
740
+ item.node.position = { ...point.position };
741
+ if (point.scale !== void 0 || point.opacity !== void 0) {
742
+ if (!item.node.data) {
743
+ item.node.data = {};
744
+ }
745
+ if (point.scale !== void 0) {
746
+ item.node.data.scale = point.scale;
747
+ }
748
+ if (point.opacity !== void 0) {
749
+ item.node.data.opacity = point.opacity;
750
+ }
751
+ }
752
+ if (rawProgress < 1) {
753
+ allCompleted = false;
754
+ }
755
+ }
756
+ onUpdate?.();
757
+ if (!allCompleted) {
758
+ if (typeof requestAnimationFrame !== "undefined") {
759
+ requestAnimationFrame(frame);
760
+ }
761
+ } else {
762
+ onComplete?.();
763
+ }
764
+ }
765
+ frame();
766
+ return () => {
767
+ isCancelled = true;
768
+ };
769
+ }
770
+
771
+ // src/canvas/createCanvasDisplacement.ts
772
+ import dagre2 from "@dagrejs/dagre";
773
+ function computeNodesBoundingBox(nodes, defaultWidth = 200, defaultHeight = 60) {
774
+ if (nodes.length === 0) {
775
+ return { x: 0, y: 0, width: 0, height: 0 };
776
+ }
777
+ let minX = Infinity;
778
+ let minY = Infinity;
779
+ let maxX = -Infinity;
780
+ let maxY = -Infinity;
781
+ for (const node of nodes) {
782
+ const width = node.measured?.width ?? node.width ?? defaultWidth;
783
+ const height = node.measured?.height ?? node.height ?? defaultHeight;
784
+ minX = Math.min(minX, node.position.x);
785
+ minY = Math.min(minY, node.position.y);
786
+ maxX = Math.max(maxX, node.position.x + width);
787
+ maxY = Math.max(maxY, node.position.y + height);
788
+ }
789
+ return {
790
+ x: minX,
791
+ y: minY,
792
+ width: maxX - minX,
793
+ height: maxY - minY
794
+ };
795
+ }
796
+ function calculateDirectionalDisplacement(params) {
797
+ const {
798
+ existingNodes,
799
+ originNode,
800
+ childNodes,
801
+ getNodesBounds,
802
+ options = {}
803
+ } = params;
804
+ const {
805
+ direction = "right",
806
+ gap = 40,
807
+ spreadSiblingLanes = false,
808
+ siblingTolerance = 50,
809
+ minY
810
+ } = options;
811
+ const displacedMap = /* @__PURE__ */ new Map();
812
+ if (childNodes.length === 0 || existingNodes.length === 0) {
813
+ return displacedMap;
814
+ }
815
+ const childBounds = getNodesBounds ? getNodesBounds(childNodes) : computeNodesBoundingBox(childNodes);
816
+ const originWidth = originNode.measured?.width ?? originNode.width ?? 200;
817
+ const originHeight = originNode.measured?.height ?? originNode.height ?? 60;
818
+ const deltaPrimary = direction === "right" || direction === "left" ? childBounds.width + gap : childBounds.height + gap;
819
+ const deltaUp = Math.max(0, originNode.position.y - childBounds.y + gap);
820
+ let deltaDown = Math.max(
821
+ 0,
822
+ childBounds.y + childBounds.height - (originNode.position.y + originHeight) + gap
823
+ );
824
+ let effectiveDeltaUp = deltaUp;
825
+ if (direction === "vertical" && minY !== void 0 && deltaUp > 0) {
826
+ const aboveNodes = existingNodes.filter(
827
+ (n) => n.id !== originNode.id && n.position.y < originNode.position.y
828
+ );
829
+ if (aboveNodes.length > 0) {
830
+ const minAboveY = Math.min(...aboveNodes.map((n) => n.position.y));
831
+ const maxAllowedUp = Math.max(0, minAboveY - minY);
832
+ effectiveDeltaUp = Math.min(deltaUp, maxAllowedUp);
833
+ const unmetUp = deltaUp - effectiveDeltaUp;
834
+ deltaDown += unmetUp;
835
+ }
836
+ }
837
+ for (const node of existingNodes) {
838
+ if (node.id === originNode.id) continue;
839
+ let newX = node.position.x;
840
+ let newY = node.position.y;
841
+ let moved = false;
842
+ if (direction === "right") {
843
+ if (node.position.x >= originNode.position.x + originWidth * 0.5) {
844
+ newX += deltaPrimary;
845
+ moved = true;
846
+ } else if (spreadSiblingLanes) {
847
+ const isVerticallyOverlapping = node.position.y + (node.height ?? 60) >= childBounds.y - gap && node.position.y <= childBounds.y + childBounds.height + gap;
848
+ if (isVerticallyOverlapping && Math.abs(node.position.x - originNode.position.x) <= siblingTolerance) {
849
+ const pushDirection = node.position.y >= originNode.position.y ? 1 : -1;
850
+ newY += pushDirection * (childBounds.height * 0.5 + gap);
851
+ moved = true;
852
+ }
853
+ }
854
+ } else if (direction === "left") {
855
+ if (node.position.x <= originNode.position.x) {
856
+ newX -= deltaPrimary;
857
+ moved = true;
858
+ }
859
+ } else if (direction === "down") {
860
+ if (node.position.y >= originNode.position.y + originHeight * 0.5) {
861
+ newY += deltaPrimary;
862
+ moved = true;
863
+ }
864
+ } else if (direction === "up") {
865
+ if (node.position.y <= originNode.position.y) {
866
+ newY -= deltaPrimary;
867
+ moved = true;
868
+ }
869
+ } else if (direction === "vertical") {
870
+ if (node.position.y < originNode.position.y) {
871
+ if (effectiveDeltaUp > 0) {
872
+ newY -= effectiveDeltaUp;
873
+ moved = true;
874
+ }
875
+ } else if (node.position.y >= originNode.position.y + originHeight * 0.5) {
876
+ if (deltaDown > 0) {
877
+ newY += deltaDown;
878
+ moved = true;
879
+ }
880
+ }
881
+ }
882
+ if (moved) {
883
+ displacedMap.set(node.id, { x: newX, y: newY });
884
+ }
885
+ }
886
+ return displacedMap;
887
+ }
888
+ function calculateReflowDisplacement(params) {
889
+ const {
890
+ existingNodes,
891
+ existingEdges,
892
+ originNode,
893
+ childNodes,
894
+ childEdges = [],
895
+ options = {}
896
+ } = params;
897
+ const {
898
+ direction = "LR",
899
+ ranksep = 80,
900
+ nodesep = 30,
901
+ defaultNodeWidth = 200,
902
+ defaultNodeHeight = 60
903
+ } = options;
904
+ const displacedMap = /* @__PURE__ */ new Map();
905
+ if (existingNodes.length === 0) return displacedMap;
906
+ const allNodes = [...existingNodes, ...childNodes];
907
+ const allEdges = [...existingEdges, ...childEdges];
908
+ const dagreGraph = new dagre2.graphlib.Graph();
909
+ dagreGraph.setDefaultEdgeLabel(() => ({}));
910
+ dagreGraph.setGraph({ rankdir: direction, ranksep, nodesep });
911
+ const allNodeIds = new Set(allNodes.map((n) => n.id));
912
+ allNodes.forEach((node) => {
913
+ const nodeWidth = node.measured?.width ?? node.width ?? defaultNodeWidth;
914
+ const nodeHeight = node.measured?.height ?? node.height ?? defaultNodeHeight;
915
+ dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
916
+ });
917
+ allEdges.forEach((edge) => {
918
+ if (allNodeIds.has(edge.source) && allNodeIds.has(edge.target)) {
919
+ dagreGraph.setEdge(edge.source, edge.target);
920
+ }
921
+ });
922
+ dagre2.layout(dagreGraph);
923
+ const dagreOrigin = dagreGraph.node(originNode.id);
924
+ if (!dagreOrigin) return displacedMap;
925
+ const originWidth = originNode.measured?.width ?? originNode.width ?? defaultNodeWidth;
926
+ const originHeight = originNode.measured?.height ?? originNode.height ?? defaultNodeHeight;
927
+ const anchorOffsetX = originNode.position.x - (dagreOrigin.x - originWidth / 2);
928
+ const anchorOffsetY = originNode.position.y - (dagreOrigin.y - originHeight / 2);
929
+ existingNodes.forEach((node) => {
930
+ if (node.id === originNode.id) return;
931
+ const dagreNode = dagreGraph.node(node.id);
932
+ if (!dagreNode) return;
933
+ const nodeWidth = node.measured?.width ?? node.width ?? defaultNodeWidth;
934
+ const nodeHeight = node.measured?.height ?? node.height ?? defaultNodeHeight;
935
+ const targetX = dagreNode.x - nodeWidth / 2 + anchorOffsetX;
936
+ const targetY = dagreNode.y - nodeHeight / 2 + anchorOffsetY;
937
+ if (Math.abs(targetX - node.position.x) > 0.5 || Math.abs(targetY - node.position.y) > 0.5) {
938
+ displacedMap.set(node.id, { x: targetX, y: targetY });
939
+ }
940
+ });
941
+ return displacedMap;
942
+ }
943
+
944
+ // src/canvas/createCanvasExplosion.svelte.ts
945
+ import * as $5 from "svelte/internal/client";
946
+ import { cubicOut as cubicOut3 } from "svelte/easing";
947
+ function createCanvasExplosion(graph, options = {}) {
948
+ let explodedNodeIds = $5.state($5.proxy([]));
949
+ const snapshots = /* @__PURE__ */ new Map();
950
+ let activeAnimationCancel = null;
951
+ function isExploded(nodeId) {
952
+ return $5.get(explodedNodeIds).includes(nodeId);
953
+ }
954
+ function explodeNode(parentId, explodeOptions) {
955
+ if (isExploded(parentId)) {
956
+ return false;
957
+ }
958
+ const parentNode = graph.nodes.find((n) => n.id === parentId);
959
+ if (!parentNode) {
960
+ return false;
961
+ }
962
+ const {
963
+ childNodes,
964
+ childEdges = [],
965
+ connectParentToChildren = "all",
966
+ displacementStrategy = "directional",
967
+ directionalOptions = {},
968
+ reflowOptions = {},
969
+ trajectory,
970
+ duration = 500,
971
+ staggerDelay = 20,
972
+ easing = cubicOut3,
973
+ onComplete
974
+ } = explodeOptions;
975
+ if (childNodes.length === 0) {
976
+ return false;
977
+ }
978
+ let displacementMap;
979
+ if (displacementStrategy === "dagre") {
980
+ displacementMap = calculateReflowDisplacement({
981
+ existingNodes: graph.nodes,
982
+ existingEdges: graph.edges,
983
+ originNode: parentNode,
984
+ childNodes,
985
+ childEdges,
986
+ getNodesBounds: options.getNodesBounds,
987
+ options: { ...reflowOptions, direction: reflowOptions.direction ?? "LR" }
988
+ });
989
+ } else if (displacementStrategy === "none") {
990
+ displacementMap = /* @__PURE__ */ new Map();
991
+ } else {
992
+ displacementMap = calculateDirectionalDisplacement({
993
+ existingNodes: graph.nodes,
994
+ originNode: parentNode,
995
+ childNodes,
996
+ getNodesBounds: options.getNodesBounds,
997
+ options: directionalOptions
998
+ });
999
+ }
1000
+ const displacedNodesSnapshot = Array.from(displacementMap.entries()).map(([id]) => {
1001
+ const node = graph.nodes.find((n) => n.id === id);
1002
+ return {
1003
+ id,
1004
+ originalPosition: { ...node?.position ?? { x: 0, y: 0 } }
1005
+ };
1006
+ });
1007
+ const edgesToAdd = [...childEdges];
1008
+ const defaultCreateEdge = (sourceId, targetId) => {
1009
+ if (graph.createEdge) {
1010
+ return graph.createEdge(sourceId, targetId);
1011
+ }
1012
+ return {
1013
+ id: crypto.randomUUID(),
1014
+ type: "default",
1015
+ source: sourceId,
1016
+ target: targetId
1017
+ };
1018
+ };
1019
+ if (connectParentToChildren === "all" || connectParentToChildren === true) {
1020
+ childNodes.forEach((child) => {
1021
+ const exists = edgesToAdd.some((e) => e.source === parentId && e.target === child.id);
1022
+ if (!exists) {
1023
+ edgesToAdd.push(defaultCreateEdge(parentId, child.id));
1024
+ }
1025
+ });
1026
+ } else if (connectParentToChildren === "first" && childNodes.length > 0) {
1027
+ const firstChild = childNodes[0];
1028
+ const exists = edgesToAdd.some((e) => e.source === parentId && e.target === firstChild.id);
1029
+ if (!exists) {
1030
+ edgesToAdd.push(defaultCreateEdge(parentId, firstChild.id));
1031
+ }
1032
+ }
1033
+ const snapshot = {
1034
+ parentId,
1035
+ childNodeIds: childNodes.map((n) => n.id),
1036
+ childEdgeIds: edgesToAdd.map((e) => e.id),
1037
+ displacedNodes: displacedNodesSnapshot,
1038
+ timestamp: Date.now()
1039
+ };
1040
+ snapshots.set(parentId, snapshot);
1041
+ const originPos = { ...parentNode.position };
1042
+ const targetPositions = childNodes.map((child) => ({ ...child.position }));
1043
+ childNodes.forEach((child) => {
1044
+ child.position = { ...originPos };
1045
+ if (!child.data) child.data = {};
1046
+ child.data.scale = 0;
1047
+ child.data.opacity = 0;
1048
+ });
1049
+ graph.addNodeOrNodes(childNodes, void 0, false);
1050
+ if (edgesToAdd.length > 0) {
1051
+ graph.addEdges(edgesToAdd, false);
1052
+ }
1053
+ const chosenTrajectory = trajectory ?? createFanOutTrajectory({ curvature: 0.35 });
1054
+ const transitions = [];
1055
+ const effectiveStagger = duration === 0 ? 0 : staggerDelay;
1056
+ childNodes.forEach((child, i) => {
1057
+ transitions.push({
1058
+ node: child,
1059
+ from: originPos,
1060
+ to: targetPositions[i],
1061
+ trajectory: chosenTrajectory,
1062
+ delay: i * effectiveStagger,
1063
+ duration,
1064
+ easing,
1065
+ index: i,
1066
+ total: childNodes.length
1067
+ });
1068
+ });
1069
+ displacementMap.forEach((targetPos, nodeId) => {
1070
+ const node = graph.nodes.find((n) => n.id === nodeId);
1071
+ if (node) {
1072
+ transitions.push({
1073
+ node,
1074
+ from: { ...node.position },
1075
+ to: targetPos,
1076
+ trajectory: linearTrajectory,
1077
+ duration,
1078
+ easing
1079
+ });
1080
+ }
1081
+ });
1082
+ if (activeAnimationCancel) {
1083
+ activeAnimationCancel();
1084
+ }
1085
+ activeAnimationCancel = runMultiNodeTransition(transitions, {
1086
+ defaultDuration: duration,
1087
+ defaultEasing: easing,
1088
+ onUpdate: () => {
1089
+ graph.setNodes([...graph.nodes]);
1090
+ },
1091
+ onComplete: () => {
1092
+ graph.setNodes([...graph.nodes]);
1093
+ $5.set(explodedNodeIds, [...$5.get(explodedNodeIds), parentId], true);
1094
+ activeAnimationCancel = null;
1095
+ options.onSave?.();
1096
+ graph.onSave?.();
1097
+ onComplete?.();
1098
+ }
1099
+ });
1100
+ return true;
1101
+ }
1102
+ function collapseNode(parentId, collapseOptions = {}) {
1103
+ const snapshot = snapshots.get(parentId);
1104
+ if (!snapshot) {
1105
+ return false;
1106
+ }
1107
+ const parentNode = graph.nodes.find((n) => n.id === parentId);
1108
+ if (!parentNode) {
1109
+ return false;
1110
+ }
1111
+ const {
1112
+ duration = 400,
1113
+ staggerDelay = 15,
1114
+ easing = cubicOut3,
1115
+ trajectory,
1116
+ onComplete
1117
+ } = collapseOptions;
1118
+ const childIdSet = new Set(snapshot.childNodeIds);
1119
+ const activeChildNodes = graph.nodes.filter((n) => childIdSet.has(n.id));
1120
+ const originPos = { ...parentNode.position };
1121
+ const reverseTrajectory = trajectory ?? createFanOutTrajectory({
1122
+ curvature: 0.2,
1123
+ startScale: 1,
1124
+ endScale: 0,
1125
+ startOpacity: 1,
1126
+ endOpacity: 0
1127
+ });
1128
+ const transitions = [];
1129
+ const effectiveStagger = duration === 0 ? 0 : staggerDelay;
1130
+ activeChildNodes.forEach((child, i) => {
1131
+ transitions.push({
1132
+ node: child,
1133
+ from: { ...child.position },
1134
+ to: originPos,
1135
+ trajectory: reverseTrajectory,
1136
+ delay: (activeChildNodes.length - 1 - i) * effectiveStagger,
1137
+ duration,
1138
+ easing,
1139
+ index: i,
1140
+ total: activeChildNodes.length
1141
+ });
1142
+ });
1143
+ snapshot.displacedNodes.forEach(({ id, originalPosition }) => {
1144
+ const node = graph.nodes.find((n) => n.id === id);
1145
+ if (node) {
1146
+ transitions.push({
1147
+ node,
1148
+ from: { ...node.position },
1149
+ to: originalPosition,
1150
+ trajectory: linearTrajectory,
1151
+ duration,
1152
+ easing
1153
+ });
1154
+ }
1155
+ });
1156
+ if (activeAnimationCancel) {
1157
+ activeAnimationCancel();
1158
+ }
1159
+ activeAnimationCancel = runMultiNodeTransition(transitions, {
1160
+ defaultDuration: duration,
1161
+ defaultEasing: easing,
1162
+ onUpdate: () => {
1163
+ graph.setNodes([...graph.nodes]);
1164
+ },
1165
+ onComplete: () => {
1166
+ graph.removeNodes(activeChildNodes, false);
1167
+ const edgeIdSet = new Set(snapshot.childEdgeIds);
1168
+ const edgesToRemove = graph.edges.filter((e) => edgeIdSet.has(e.id));
1169
+ if (edgesToRemove.length > 0) {
1170
+ graph.removeEdges(edgesToRemove, false);
1171
+ }
1172
+ snapshots.delete(parentId);
1173
+ $5.set(explodedNodeIds, $5.get(explodedNodeIds).filter((id) => id !== parentId), true);
1174
+ activeAnimationCancel = null;
1175
+ options.onSave?.();
1176
+ graph.onSave?.();
1177
+ onComplete?.();
1178
+ }
1179
+ });
1180
+ return true;
1181
+ }
1182
+ function toggleExplode(parentId, explodeOptions, collapseOptions) {
1183
+ if (isExploded(parentId)) {
1184
+ return collapseNode(parentId, collapseOptions);
1185
+ }
1186
+ return explodeNode(parentId, explodeOptions);
1187
+ }
1188
+ function getSnapshot(parentId) {
1189
+ return snapshots.get(parentId);
1190
+ }
1191
+ return {
1192
+ get explodedNodeIds() {
1193
+ return $5.get(explodedNodeIds);
1194
+ },
1195
+ isExploded,
1196
+ explodeNode,
1197
+ collapseNode,
1198
+ toggleExplode,
1199
+ getSnapshot
1200
+ };
1201
+ }
654
1202
  export {
1203
+ calculateDirectionalDisplacement,
655
1204
  calculateGraphDifferences,
1205
+ calculateReflowDisplacement,
656
1206
  centerNodes,
1207
+ computeNodesBoundingBox,
1208
+ createBezierTrajectory,
657
1209
  createCanvasClipboard,
658
1210
  createCanvasDocking,
1211
+ createCanvasExplosion,
659
1212
  createCanvasGraph,
660
1213
  createCanvasInteractions,
661
1214
  createCanvasShortcuts,
1215
+ createFanOutTrajectory,
662
1216
  defaultSvelteFlowPreset,
663
1217
  getCenteredNodePosition,
664
1218
  getLayoutedNodes,
665
1219
  isEventFromTextInput,
666
1220
  isValidCanvasNode,
667
1221
  isValidUrl,
1222
+ linearTrajectory,
668
1223
  miroCompatiblePreset,
669
1224
  replaceNodeInGraph,
670
- restorePanelPointerEvents
1225
+ restorePanelPointerEvents,
1226
+ runMultiNodeTransition
671
1227
  };
672
1228
  //# sourceMappingURL=index.js.map