@bian-womp/spark-workbench 0.3.42 → 0.3.44

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.
@@ -1 +1 @@
1
- {"version":3,"file":"SelectionBoundOverlay.d.ts","sourceRoot":"","sources":["../../../../src/misc/SelectionBoundOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA+C,MAAM,OAAO,CAAC;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAEnE,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC;IAC3C,SAAS,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAChD,UAAU,EAAE,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC;IACjD,YAAY,EAAE,MAAM,CAAC;CACtB,CAgGA,CAAC"}
1
+ {"version":3,"file":"SelectionBoundOverlay.d.ts","sourceRoot":"","sources":["../../../../src/misc/SelectionBoundOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAMN,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAGnE,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC;IAC3C,SAAS,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAChD,UAAU,EAAE,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC;IACjD,YAAY,EAAE,MAAM,CAAC;CACtB,CAmRA,CAAC"}
package/lib/esm/index.js CHANGED
@@ -5844,6 +5844,13 @@ function useKeyboardShortcutToast() {
5844
5844
  const SelectionBoundOverlay = ({ selection, rfInstance, viewportTick }) => {
5845
5845
  const overlayRef = useRef(null);
5846
5846
  const [parentRect, setParentRect] = useState(null);
5847
+ const [isDragging, setIsDragging] = useState(false);
5848
+ const [bounds, setBounds] = useState(null);
5849
+ const dragStateRef = useRef(null);
5850
+ const moveListenerRef = useRef(null);
5851
+ const upListenerRef = useRef(null);
5852
+ useRef(null);
5853
+ const { wb } = useWorkbenchContext();
5847
5854
  useEffect(() => {
5848
5855
  if (!overlayRef.current)
5849
5856
  return;
@@ -5863,56 +5870,190 @@ const SelectionBoundOverlay = ({ selection, rfInstance, viewportTick }) => {
5863
5870
  window.removeEventListener("scroll", scrollHandler, true);
5864
5871
  };
5865
5872
  }, [viewportTick]);
5866
- const selectionBounds = useMemo(() => {
5873
+ const cleanupDragListeners = useCallback(() => {
5874
+ if (moveListenerRef.current) {
5875
+ window.removeEventListener("mousemove", moveListenerRef.current);
5876
+ moveListenerRef.current = null;
5877
+ }
5878
+ if (upListenerRef.current) {
5879
+ window.removeEventListener("mouseup", upListenerRef.current);
5880
+ upListenerRef.current = null;
5881
+ }
5882
+ dragStateRef.current = null;
5883
+ setIsDragging(false);
5884
+ }, []);
5885
+ useEffect(() => cleanupDragListeners, [cleanupDragListeners]);
5886
+ const handleMouseMove = useCallback((e) => {
5887
+ if (!rfInstance || !wb || !parentRect)
5888
+ return;
5889
+ const dragState = dragStateRef.current;
5890
+ if (!dragState)
5891
+ return;
5892
+ e.preventDefault();
5893
+ e.stopPropagation();
5894
+ const current = rfInstance.screenToFlowPosition({
5895
+ x: e.clientX,
5896
+ y: e.clientY,
5897
+ });
5898
+ const dx = current.x - dragState.startFlow.x;
5899
+ const dy = current.y - dragState.startFlow.y;
5900
+ // Update nodes directly via React Flow for immediate visual feedback
5901
+ const nodes = rfInstance.getNodes();
5902
+ const updatedNodes = nodes.map((node) => {
5903
+ if (dragState.initialPositions[node.id]) {
5904
+ const initialPos = dragState.initialPositions[node.id];
5905
+ return {
5906
+ ...node,
5907
+ position: {
5908
+ x: initialPos.x + dx,
5909
+ y: initialPos.y + dy,
5910
+ },
5911
+ };
5912
+ }
5913
+ return node;
5914
+ });
5915
+ rfInstance.setNodes(updatedNodes);
5916
+ // Also update workbench state
5917
+ const nextPositions = {};
5918
+ for (const [nodeId, pos] of Object.entries(dragState.initialPositions)) {
5919
+ nextPositions[nodeId] = { x: pos.x + dx, y: pos.y + dy };
5920
+ }
5921
+ if (Object.keys(nextPositions).length) {
5922
+ wb.setPositions(nextPositions, { commit: false });
5923
+ }
5924
+ }, [rfInstance, wb, parentRect]);
5925
+ const handleMouseUp = useCallback((e) => {
5926
+ if (!rfInstance || !wb) {
5927
+ cleanupDragListeners();
5928
+ return;
5929
+ }
5930
+ const dragState = dragStateRef.current;
5931
+ if (!dragState) {
5932
+ cleanupDragListeners();
5933
+ return;
5934
+ }
5935
+ e.preventDefault();
5936
+ e.stopPropagation();
5937
+ const current = rfInstance.screenToFlowPosition({
5938
+ x: e.clientX,
5939
+ y: e.clientY,
5940
+ });
5941
+ const dx = current.x - dragState.startFlow.x;
5942
+ const dy = current.y - dragState.startFlow.y;
5943
+ const nextPositions = {};
5944
+ for (const [nodeId, pos] of Object.entries(dragState.initialPositions)) {
5945
+ nextPositions[nodeId] = { x: pos.x + dx, y: pos.y + dy };
5946
+ }
5947
+ if (Object.keys(nextPositions).length) {
5948
+ wb.setPositions(nextPositions, { commit: true });
5949
+ }
5950
+ cleanupDragListeners();
5951
+ }, [cleanupDragListeners, rfInstance, wb]);
5952
+ const handleMouseDown = useCallback((e) => {
5953
+ if (e.button !== 0)
5954
+ return;
5955
+ if (!rfInstance || !wb)
5956
+ return;
5957
+ if (selection.nodes.length < 2)
5958
+ return;
5959
+ e.preventDefault();
5960
+ e.stopPropagation();
5961
+ const startFlow = rfInstance.screenToFlowPosition({
5962
+ x: e.clientX,
5963
+ y: e.clientY,
5964
+ });
5965
+ const positions = wb.getPositions();
5966
+ const initialPositions = {};
5967
+ for (const nodeId of selection.nodes) {
5968
+ const pos = positions[nodeId];
5969
+ if (pos) {
5970
+ initialPositions[nodeId] = pos;
5971
+ }
5972
+ }
5973
+ if (!Object.keys(initialPositions).length)
5974
+ return;
5975
+ dragStateRef.current = { startFlow, initialPositions };
5976
+ setIsDragging(true);
5977
+ moveListenerRef.current = handleMouseMove;
5978
+ upListenerRef.current = handleMouseUp;
5979
+ window.addEventListener("mousemove", handleMouseMove, { passive: false });
5980
+ window.addEventListener("mouseup", handleMouseUp, { passive: false });
5981
+ }, [handleMouseMove, handleMouseUp, rfInstance, selection.nodes, wb]);
5982
+ // Continuous bounds update loop
5983
+ useEffect(() => {
5867
5984
  if (typeof document === "undefined" ||
5868
5985
  !rfInstance ||
5869
5986
  !parentRect ||
5870
5987
  selection.nodes.length < 2) {
5871
- return null;
5988
+ setBounds(null);
5989
+ return;
5872
5990
  }
5873
- let bounds = null;
5874
- for (const nodeId of selection.nodes) {
5875
- const el = document.querySelector(`.react-flow__node[data-id="${nodeId}"]`);
5876
- if (!el)
5877
- continue;
5878
- const rect = el.getBoundingClientRect();
5879
- const relativeLeft = rect.left - parentRect.left;
5880
- const relativeTop = rect.top - parentRect.top;
5881
- const relativeRight = rect.right - parentRect.left;
5882
- const relativeBottom = rect.bottom - parentRect.top;
5883
- if (!bounds) {
5884
- bounds = {
5885
- left: relativeLeft,
5886
- top: relativeTop,
5887
- right: relativeRight,
5888
- bottom: relativeBottom,
5889
- };
5991
+ let animationFrameId = null;
5992
+ let isActive = true;
5993
+ const updateBounds = () => {
5994
+ if (!isActive)
5995
+ return;
5996
+ let calculatedBounds = null;
5997
+ for (const nodeId of selection.nodes) {
5998
+ const el = document.querySelector(`.react-flow__node[data-id="${nodeId}"]`);
5999
+ if (!el)
6000
+ continue;
6001
+ const rect = el.getBoundingClientRect();
6002
+ const relativeLeft = rect.left - parentRect.left;
6003
+ const relativeTop = rect.top - parentRect.top;
6004
+ const relativeRight = rect.right - parentRect.left;
6005
+ const relativeBottom = rect.bottom - parentRect.top;
6006
+ if (!calculatedBounds) {
6007
+ calculatedBounds = {
6008
+ left: relativeLeft,
6009
+ top: relativeTop,
6010
+ right: relativeRight,
6011
+ bottom: relativeBottom,
6012
+ };
6013
+ }
6014
+ else {
6015
+ calculatedBounds.left = Math.min(calculatedBounds.left, relativeLeft);
6016
+ calculatedBounds.top = Math.min(calculatedBounds.top, relativeTop);
6017
+ calculatedBounds.right = Math.max(calculatedBounds.right, relativeRight);
6018
+ calculatedBounds.bottom = Math.max(calculatedBounds.bottom, relativeBottom);
6019
+ }
5890
6020
  }
5891
- else {
5892
- bounds.left = Math.min(bounds.left, relativeLeft);
5893
- bounds.top = Math.min(bounds.top, relativeTop);
5894
- bounds.right = Math.max(bounds.right, relativeRight);
5895
- bounds.bottom = Math.max(bounds.bottom, relativeBottom);
6021
+ setBounds(calculatedBounds);
6022
+ // Continue the animation loop
6023
+ if (isActive) {
6024
+ animationFrameId = requestAnimationFrame(updateBounds);
5896
6025
  }
5897
- }
5898
- return bounds;
6026
+ };
6027
+ // Start the animation loop
6028
+ animationFrameId = requestAnimationFrame(updateBounds);
6029
+ return () => {
6030
+ isActive = false;
6031
+ if (animationFrameId !== null) {
6032
+ cancelAnimationFrame(animationFrameId);
6033
+ }
6034
+ };
5899
6035
  }, [selection.nodes, rfInstance, viewportTick, parentRect]);
5900
- if (!selectionBounds || selection.nodes.length < 2) {
6036
+ if (!bounds || selection.nodes.length < 2) {
5901
6037
  return jsx("div", { ref: overlayRef, style: { display: "none" } });
5902
6038
  }
5903
- const { left, top, right, bottom } = selectionBounds;
6039
+ const { left, top, right, bottom } = bounds;
5904
6040
  const width = right - left;
5905
6041
  const height = bottom - top;
5906
- return (jsx("div", { ref: overlayRef, style: {
6042
+ return (jsx("div", { ref: overlayRef, onMouseDown: handleMouseDown, style: {
5907
6043
  position: "absolute",
5908
6044
  left: `${left}px`,
5909
6045
  top: `${top}px`,
5910
6046
  width: `${width}px`,
5911
6047
  height: `${height}px`,
5912
- border: "1px dashed #0ea5e9",
5913
- pointerEvents: "none",
6048
+ border: isDragging ? "2px solid #0ea5e9" : "1px dashed #0ea5e9",
6049
+ backgroundColor: isDragging
6050
+ ? "rgba(14, 165, 233, 0.05)"
6051
+ : "transparent",
6052
+ pointerEvents: "auto",
6053
+ cursor: isDragging ? "grabbing" : "move",
5914
6054
  zIndex: 4,
5915
6055
  boxSizing: "border-box",
6056
+ transition: isDragging ? "none" : "border 0.1s ease",
5916
6057
  } }));
5917
6058
  };
5918
6059