panelgrid 0.4.1 → 0.4.3

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/index.cjs CHANGED
@@ -6,6 +6,7 @@ const require_usePanel = require('./usePanel.cjs');
6
6
  exports.PanelGridProvider = require_PanelGridProvider.PanelGridProvider;
7
7
  exports.PanelGridRenderer = require_PanelGridRenderer.PanelGridRenderer;
8
8
  exports.rearrangePanels = require_rearrangement.rearrangePanels;
9
+ exports.resolveWithLockRollback = require_rearrangement.resolveWithLockRollback;
9
10
  exports.usePanel = require_usePanel.usePanel;
10
11
  exports.usePanelGridControls = require_PanelGridProvider.usePanelGridControls;
11
12
  exports.usePanelGridState = require_PanelGridProvider.usePanelGridState;
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { PanelCoordinate, PanelId, RearrangementFunction } from "./types.cjs";
2
- import { rearrangePanels } from "./helpers/rearrangement.cjs";
2
+ import { rearrangePanels, resolveWithLockRollback } from "./helpers/rearrangement.cjs";
3
3
  import { PanelGridProvider, usePanelGridControls, usePanelGridState } from "./PanelGridProvider.cjs";
4
4
  import { PanelGridRenderer } from "./PanelGridRenderer.cjs";
5
5
  import { usePanel } from "./usePanel.cjs";
6
- export { type PanelCoordinate, PanelGridProvider, PanelGridRenderer, type PanelId, type RearrangementFunction, rearrangePanels, usePanel, usePanelGridControls, usePanelGridState };
6
+ export { type PanelCoordinate, PanelGridProvider, PanelGridRenderer, type PanelId, type RearrangementFunction, rearrangePanels, resolveWithLockRollback, usePanel, usePanelGridControls, usePanelGridState };
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { PanelCoordinate, PanelId, RearrangementFunction } from "./types.mjs";
2
- import { rearrangePanels } from "./helpers/rearrangement.mjs";
2
+ import { rearrangePanels, resolveWithLockRollback } from "./helpers/rearrangement.mjs";
3
3
  import { PanelGridProvider, usePanelGridControls, usePanelGridState } from "./PanelGridProvider.mjs";
4
4
  import { PanelGridRenderer } from "./PanelGridRenderer.mjs";
5
5
  import { usePanel } from "./usePanel.mjs";
6
- export { type PanelCoordinate, PanelGridProvider, PanelGridRenderer, type PanelId, type RearrangementFunction, rearrangePanels, usePanel, usePanelGridControls, usePanelGridState };
6
+ export { type PanelCoordinate, PanelGridProvider, PanelGridRenderer, type PanelId, type RearrangementFunction, rearrangePanels, resolveWithLockRollback, usePanel, usePanelGridControls, usePanelGridState };
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { rearrangePanels } from "./helpers/rearrangement.mjs";
1
+ import { rearrangePanels, resolveWithLockRollback } from "./helpers/rearrangement.mjs";
2
2
  import { PanelGridProvider, usePanelGridControls, usePanelGridState } from "./PanelGridProvider.mjs";
3
3
  import { PanelGridRenderer } from "./PanelGridRenderer.mjs";
4
4
  import { usePanel } from "./usePanel.mjs";
5
5
 
6
- export { PanelGridProvider, PanelGridRenderer, rearrangePanels, usePanel, usePanelGridControls, usePanelGridState };
6
+ export { PanelGridProvider, PanelGridRenderer, rearrangePanels, resolveWithLockRollback, usePanel, usePanelGridControls, usePanelGridState };
package/dist/styles.css CHANGED
@@ -54,6 +54,10 @@
54
54
  z-index: calc(infinity);
55
55
  }
56
56
 
57
+ .panelgrid-panel--position-locked {
58
+ cursor: default;
59
+ }
60
+
57
61
  .panelgrid-panel--with-transition {
58
62
  transition:
59
63
  top 0.15s ease-out,
package/dist/types.d.cts CHANGED
@@ -14,6 +14,7 @@ interface PanelCoordinate {
14
14
  w: number;
15
15
  h: number;
16
16
  lockSize?: boolean;
17
+ lockPosition?: boolean;
17
18
  }
18
19
  /**
19
20
  * Custom rearrangement function type
package/dist/types.d.mts CHANGED
@@ -14,6 +14,7 @@ interface PanelCoordinate {
14
14
  w: number;
15
15
  h: number;
16
16
  lockSize?: boolean;
17
+ lockPosition?: boolean;
17
18
  }
18
19
  /**
19
20
  * Custom rearrangement function type
@@ -44,6 +44,20 @@ function panelGridReducer(state, action) {
44
44
  lockSize: false
45
45
  } : panel)
46
46
  };
47
+ case "LOCK_PANEL_POSITION": return {
48
+ ...state,
49
+ panels: state.panels.map((panel) => panel.id === action.panelId ? {
50
+ ...panel,
51
+ lockPosition: true
52
+ } : panel)
53
+ };
54
+ case "UNLOCK_PANEL_POSITION": return {
55
+ ...state,
56
+ panels: state.panels.map((panel) => panel.id === action.panelId ? {
57
+ ...panel,
58
+ lockPosition: false
59
+ } : panel)
60
+ };
47
61
  default: return state;
48
62
  }
49
63
  }
@@ -119,12 +133,14 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, resizeHandlePosition
119
133
  animationTimeoutsRef.current.delete(timeoutId);
120
134
  }, ANIMATION_DURATION);
121
135
  animationTimeoutsRef.current.add(timeoutId);
136
+ return nextPanels;
122
137
  }, [
123
138
  columnCount,
124
139
  internalState,
125
140
  rearrangement
126
141
  ]);
127
142
  const createDragHandler = (0, react.useCallback)((panel) => (e) => {
143
+ if (panel.lockPosition) return;
128
144
  internalState.activePanelId = panel.id;
129
145
  const draggingElement = internalState.draggableElements[panel.id];
130
146
  if (!draggingElement) return;
@@ -165,19 +181,21 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, resizeHandlePosition
165
181
  const droppedTop = parseFloat(draggingElement.style.top) || 0;
166
182
  const nextGridX = require_gridCalculations.pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);
167
183
  const nextGridY = require_gridCalculations.pixelsToGridPosition(droppedTop, baseSize, gap);
184
+ const resultPanel = updatePanelsWithAnimation({
185
+ ...panel,
186
+ x: nextGridX,
187
+ y: nextGridY
188
+ }, state.panels).find((p) => p.id === panel.id);
189
+ const actualGridX = resultPanel?.x ?? nextGridX;
190
+ const actualGridY = resultPanel?.y ?? nextGridY;
168
191
  require_animation.applySnapAnimation({
169
192
  element: draggingElement,
170
193
  droppedLeft,
171
194
  droppedTop,
172
- nextLeft: require_gridCalculations.gridPositionToPixels(nextGridX, baseSize, gap),
173
- nextTop: require_gridCalculations.gridPositionToPixels(nextGridY, baseSize, gap),
195
+ nextLeft: require_gridCalculations.gridPositionToPixels(actualGridX, baseSize, gap),
196
+ nextTop: require_gridCalculations.gridPositionToPixels(actualGridY, baseSize, gap),
174
197
  originalTransition
175
198
  });
176
- updatePanelsWithAnimation({
177
- ...panel,
178
- x: nextGridX,
179
- y: nextGridY
180
- }, state.panels);
181
199
  document.body.classList.remove("panelgrid-dragging");
182
200
  internalState.activePanelId = null;
183
201
  mouseMoveListenerCtrl.abort();
@@ -254,10 +272,21 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, resizeHandlePosition
254
272
  const nextGridY = require_gridCalculations.pixelsToGridPosition(droppedTop, baseSize, gap);
255
273
  const nextGridW = require_gridCalculations.pixelsToGridSize(rect.width, baseSize, gap, columnCount, nextGridX);
256
274
  const nextGridH = require_gridCalculations.pixelsToGridSize(rect.height, baseSize, gap, columnCount, nextGridY);
257
- const left = require_gridCalculations.gridPositionToPixels(nextGridX, baseSize, gap);
258
- const top = require_gridCalculations.gridPositionToPixels(nextGridY, baseSize, gap);
259
- const width = require_gridCalculations.gridToPixels(nextGridW, baseSize, gap);
260
- const height = require_gridCalculations.gridToPixels(nextGridH, baseSize, gap);
275
+ const resultPanel = updatePanelsWithAnimation({
276
+ ...panel,
277
+ x: nextGridX,
278
+ y: nextGridY,
279
+ w: nextGridW,
280
+ h: nextGridH
281
+ }, state.panels).find((p) => p.id === panel.id);
282
+ const actualX = resultPanel?.x ?? nextGridX;
283
+ const actualY = resultPanel?.y ?? nextGridY;
284
+ const actualW = resultPanel?.w ?? nextGridW;
285
+ const actualH = resultPanel?.h ?? nextGridH;
286
+ const left = require_gridCalculations.gridPositionToPixels(actualX, baseSize, gap);
287
+ const top = require_gridCalculations.gridPositionToPixels(actualY, baseSize, gap);
288
+ const width = require_gridCalculations.gridToPixels(actualW, baseSize, gap);
289
+ const height = require_gridCalculations.gridToPixels(actualH, baseSize, gap);
261
290
  draggingElement.style.width = `${rect.width}px`;
262
291
  draggingElement.style.height = `${rect.height}px`;
263
292
  draggingElement.style.cursor = initialCursor;
@@ -270,13 +299,6 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, resizeHandlePosition
270
299
  draggingElement.style.zIndex = initialZIndex;
271
300
  draggingElement.style.transition = "width 0.1s ease-out, height 0.1s ease-out, top 0.1s ease-out, left 0.1s ease-out";
272
301
  });
273
- updatePanelsWithAnimation({
274
- ...panel,
275
- x: nextGridX,
276
- y: nextGridY,
277
- w: nextGridW,
278
- h: nextGridH
279
- }, state.panels);
280
302
  isResizing = false;
281
303
  internalState.activePanelId = null;
282
304
  document.body.classList.remove("panelgrid-resizing");
@@ -310,6 +332,7 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, resizeHandlePosition
310
332
  panelProps: {
311
333
  key: panel.id,
312
334
  lockSize: panel.lockSize,
335
+ lockPosition: panel.lockPosition,
313
336
  positionData: {
314
337
  x: panel.x,
315
338
  y: panel.y,
@@ -369,6 +392,18 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, resizeHandlePosition
369
392
  panelId: id
370
393
  });
371
394
  }, []),
395
+ lockPanelPosition: (0, react.useCallback)((id) => {
396
+ dispatch({
397
+ type: "LOCK_PANEL_POSITION",
398
+ panelId: id
399
+ });
400
+ }, []),
401
+ unlockPanelPosition: (0, react.useCallback)((id) => {
402
+ dispatch({
403
+ type: "UNLOCK_PANEL_POSITION",
404
+ panelId: id
405
+ });
406
+ }, []),
372
407
  exportState: (0, react.useCallback)(() => {
373
408
  return state.panels;
374
409
  }, [state.panels])
@@ -1 +1 @@
1
- {"version":3,"file":"usePanelGrid.cjs","names":["findNewPositionToAddPanel","newPanelCoordinate: PanelCoordinate","RESIZE_CURSOR_MAP: Record<string, string>","rearrangePanels","detectAnimatingPanels","e","pixelsToGridPosition","gridPositionToPixels","pixelsToGridSize","gridToPixels"],"sources":["../src/usePanelGrid.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useReducer, useRef } from \"react\";\nimport {\n applySnapAnimation,\n detectAnimatingPanels,\n gridPositionToPixels,\n gridToPixels,\n pixelsToGridPosition,\n pixelsToGridSize,\n rearrangePanels,\n} from \"./helpers\";\nimport { findNewPositionToAddPanel } from \"./helpers/rearrangement\";\nimport type { PanelCoordinate, PanelId, RearrangementFunction, ResizeHandlePosition } from \"./types\";\n\ninterface PanelGridOptions {\n panels: PanelCoordinate[];\n columnCount: number;\n baseSize: number;\n gap: number;\n resizeHandlePositions: ResizeHandlePosition[];\n rearrangement?: RearrangementFunction;\n}\n\nexport interface PanelGridState {\n panels: PanelCoordinate[];\n}\n\ninterface InternalPanelState {\n activePanelId: PanelId | null;\n draggableElements: Record<PanelId, HTMLElement | null>;\n animatingPanels: Set<PanelId>;\n}\n\nexport type PanelGridAction =\n | { type: \"UPDATE_PANELS\"; newPanels: PanelCoordinate[] }\n | { type: \"ADD_PANEL\"; newPanel: Partial<PanelCoordinate>; columnCount: number }\n | { type: \"REMOVE_PANEL\"; panelId: PanelId }\n | { type: \"LOCK_PANEL_SIZE\"; panelId: PanelId }\n | { type: \"UNLOCK_PANEL_SIZE\"; panelId: PanelId };\n\nexport function panelGridReducer(state: PanelGridState, action: PanelGridAction): PanelGridState {\n switch (action.type) {\n case \"UPDATE_PANELS\":\n return {\n ...state,\n panels: action.newPanels,\n };\n case \"ADD_PANEL\": {\n const { newPanel, columnCount } = action;\n const newPosition = findNewPositionToAddPanel(newPanel, state.panels, columnCount);\n const newPanelCoordinate: PanelCoordinate = {\n id: newPanel.id || Math.random().toString(36).substring(2, 15),\n x: newPosition.x,\n y: newPosition.y,\n w: newPanel.w || 1,\n h: newPanel.h || 1,\n };\n return {\n ...state,\n panels: [...state.panels, newPanelCoordinate],\n };\n }\n case \"REMOVE_PANEL\":\n return {\n ...state,\n panels: state.panels.filter((panel) => panel.id !== action.panelId),\n };\n case \"LOCK_PANEL_SIZE\":\n return {\n ...state,\n panels: state.panels.map((panel) => (panel.id === action.panelId ? { ...panel, lockSize: true } : panel)),\n };\n case \"UNLOCK_PANEL_SIZE\":\n return {\n ...state,\n panels: state.panels.map((panel) => (panel.id === action.panelId ? { ...panel, lockSize: false } : panel)),\n };\n default:\n return state;\n }\n}\n\nconst ANIMATION_DURATION = 300;\ntype TimeoutId = ReturnType<typeof setTimeout>;\n\nconst RESIZE_CURSOR_MAP: Record<string, string> = {\n nw: \"nwse-resize\",\n ne: \"nesw-resize\",\n se: \"nwse-resize\",\n sw: \"nesw-resize\",\n n: \"ns-resize\",\n s: \"ns-resize\",\n e: \"ew-resize\",\n w: \"ew-resize\",\n};\n\nexport function usePanelGrid({\n panels,\n columnCount,\n baseSize,\n gap,\n resizeHandlePositions: _resizeHandlePositions,\n rearrangement,\n}: PanelGridOptions) {\n const [state, dispatch] = useReducer(panelGridReducer, {\n panels,\n });\n const ghostPanelRef = useRef<HTMLDivElement | null>(null);\n const animationTimeoutsRef = useRef<Set<TimeoutId>>(new Set());\n\n const panelMap = useMemo(() => {\n const map = new Map<PanelId, PanelCoordinate>();\n state.panels.forEach((panel) => {\n map.set(panel.id, panel);\n });\n return map;\n }, [state.panels]);\n\n const internalState = useRef<InternalPanelState>({\n activePanelId: null,\n draggableElements: {},\n animatingPanels: new Set(),\n }).current;\n\n // Cleanup animation timeouts on unmount\n useEffect(() => {\n return () => {\n animationTimeoutsRef.current.forEach((timeoutId) => clearTimeout(timeoutId));\n animationTimeoutsRef.current.clear();\n };\n }, []);\n\n // Ghost panel helper functions\n // Direct DOM manipulation is intentionally used here for performance.\n // This avoids React re-renders during high-frequency mousemove events.\n const showGhostPanel = useCallback((left: number, top: number, width: number, height: number) => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.display = \"block\";\n ghostPanelRef.current.style.left = `${left}px`;\n ghostPanelRef.current.style.top = `${top}px`;\n ghostPanelRef.current.style.width = `${width}px`;\n ghostPanelRef.current.style.height = `${height}px`;\n ghostPanelRef.current.style.outline = \"1px dashed rgba(0, 0, 0, 0.2)\";\n }, []);\n\n const updateGhostPanelPosition = useCallback((left: number, top: number) => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.left = `${left}px`;\n ghostPanelRef.current.style.top = `${top}px`;\n }, []);\n\n const updateGhostPanelSize = useCallback((width: number, height: number) => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.width = `${width}px`;\n ghostPanelRef.current.style.height = `${height}px`;\n }, []);\n\n const hideGhostPanel = useCallback(() => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.display = \"none\";\n }, []);\n\n // Callback to update panels and trigger animations\n const updatePanelsWithAnimation = useCallback(\n (updatedPanel: PanelCoordinate, currentPanels: PanelCoordinate[]) => {\n // Use custom rearrangement function if provided, otherwise use default\n const rearrange = rearrangement || rearrangePanels;\n const nextPanels = rearrange(updatedPanel, currentPanels, columnCount);\n\n // Detect which panels have been rearranged\n internalState.animatingPanels = detectAnimatingPanels({\n oldPanels: currentPanels,\n newPanels: nextPanels,\n excludePanelId: updatedPanel.id,\n });\n\n dispatch({ type: \"UPDATE_PANELS\", newPanels: nextPanels });\n\n // Clear animating panels after animation completes\n const timeoutId = setTimeout(() => {\n internalState.animatingPanels.clear();\n animationTimeoutsRef.current.delete(timeoutId);\n }, ANIMATION_DURATION);\n animationTimeoutsRef.current.add(timeoutId);\n },\n [columnCount, internalState, rearrangement]\n );\n\n // Create drag handler for a specific panel\n const createDragHandler = useCallback(\n (panel: PanelCoordinate) => (e: React.MouseEvent<HTMLDivElement>) => {\n internalState.activePanelId = panel.id;\n const draggingElement = internalState.draggableElements[panel.id];\n if (!draggingElement) return;\n\n let isDragging = true;\n const initialX = e.clientX;\n const initialY = e.clientY;\n const offsetX = draggingElement.offsetLeft;\n const offsetY = draggingElement.offsetTop;\n const originalTransition = draggingElement.style.transition;\n\n document.body.classList.add(\"panelgrid-dragging\");\n\n draggingElement.classList.add(\"panelgrid-panel--dragging\");\n draggingElement.style.transition = \"\";\n\n showGhostPanel(offsetX, offsetY, draggingElement.offsetWidth, draggingElement.offsetHeight);\n\n const mouseUpListenerCtrl = new AbortController();\n const mouseMoveListenerCtrl = new AbortController();\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isDragging) return;\n if (!draggingElement) return;\n\n const currentX = e.clientX;\n const currentY = e.clientY;\n const deltaX = currentX - initialX;\n const deltaY = currentY - initialY;\n\n draggingElement.style.left = offsetX + deltaX + \"px\";\n draggingElement.style.top = offsetY + deltaY + \"px\";\n\n // Update ghost panel position to snap to grid\n const droppedLeft = offsetX + deltaX;\n const droppedTop = offsetY + deltaY;\n const nextGridX = pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);\n const nextLeft = gridPositionToPixels(nextGridX, baseSize, gap);\n const nextTop = gridPositionToPixels(nextGridY, baseSize, gap);\n\n updateGhostPanelPosition(nextLeft, nextTop);\n\n e.preventDefault(); // Prevent text selection during drag\n };\n\n const onMouseUp = () => {\n if (!draggingElement) return;\n\n isDragging = false;\n draggingElement.classList.remove(\"panelgrid-panel--dragging\");\n\n hideGhostPanel();\n\n const droppedLeft = parseFloat(draggingElement.style.left) || 0;\n const droppedTop = parseFloat(draggingElement.style.top) || 0;\n\n const nextGridX = pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);\n\n const nextLeft = gridPositionToPixels(nextGridX, baseSize, gap);\n const nextTop = gridPositionToPixels(nextGridY, baseSize, gap);\n\n // Apply snap-back animation\n applySnapAnimation({\n element: draggingElement,\n droppedLeft,\n droppedTop,\n nextLeft,\n nextTop,\n originalTransition,\n });\n\n updatePanelsWithAnimation({ ...panel, x: nextGridX, y: nextGridY }, state.panels);\n\n document.body.classList.remove(\"panelgrid-dragging\");\n internalState.activePanelId = null;\n\n mouseMoveListenerCtrl.abort();\n mouseUpListenerCtrl.abort();\n };\n\n document.addEventListener(\"mousemove\", onMouseMove, {\n signal: mouseMoveListenerCtrl.signal,\n });\n document.addEventListener(\"mouseup\", onMouseUp, {\n signal: mouseUpListenerCtrl.signal,\n });\n },\n [\n baseSize,\n gap,\n internalState,\n state.panels,\n updatePanelsWithAnimation,\n showGhostPanel,\n updateGhostPanelPosition,\n hideGhostPanel,\n columnCount,\n ]\n );\n\n // Create resize handler for a specific panel\n const createResizeHandler = useCallback(\n (panel: PanelCoordinate) => (e: React.MouseEvent<HTMLSpanElement>) => {\n e.stopPropagation();\n let isResizing = true;\n internalState.activePanelId = panel.id;\n const draggingElement = internalState.draggableElements[panel.id];\n if (!draggingElement) return;\n\n const startX = e.clientX;\n const startY = e.clientY;\n const initialTop = draggingElement.offsetTop;\n const initialLeft = draggingElement.offsetLeft;\n const initialWidth = draggingElement.offsetWidth;\n const initialHeight = draggingElement.offsetHeight;\n const initialZIndex = draggingElement.style.zIndex;\n const initialCursor = draggingElement.style.cursor;\n\n const resizeHandle = e.currentTarget;\n const handlePosition = resizeHandle.dataset.pgResizeHandle;\n\n const northSideResizeEnabled = handlePosition?.includes(\"n\");\n const westSideResizeEnabled = handlePosition?.includes(\"w\");\n const isVerticalResizeOnly = handlePosition === \"n\" || handlePosition === \"s\";\n const isHorizontalResizeOnly = handlePosition === \"e\" || handlePosition === \"w\";\n\n document.body.classList.add(\"panelgrid-resizing\");\n\n draggingElement.style.cursor = RESIZE_CURSOR_MAP[handlePosition || \"se\"] || \"nwse-resize\";\n draggingElement.style.transition = \"\";\n\n showGhostPanel(draggingElement.offsetLeft, draggingElement.offsetTop, initialWidth, initialHeight);\n\n const mouseMoveController = new AbortController();\n const mouseUpController = new AbortController();\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isResizing) return;\n if (!draggingElement) return;\n\n const deltaX = e.clientX - startX;\n const deltaY = e.clientY - startY;\n\n // Calculate dimensions once, accounting for all resize directions\n const newWidth = westSideResizeEnabled\n ? Math.max(initialWidth - deltaX, 1)\n : isVerticalResizeOnly\n ? initialWidth\n : initialWidth + deltaX;\n\n const newHeight = northSideResizeEnabled\n ? Math.max(initialHeight - deltaY, 1)\n : isHorizontalResizeOnly\n ? initialHeight\n : initialHeight + deltaY;\n\n draggingElement.style.width = `${newWidth}px`;\n draggingElement.style.height = `${newHeight}px`;\n draggingElement.style.zIndex = \"calc(infinity)\";\n\n // Update position for north/west resizing\n if (northSideResizeEnabled) {\n draggingElement.style.top = `${initialTop + deltaY}px`;\n }\n if (westSideResizeEnabled) {\n draggingElement.style.left = `${initialLeft + deltaX}px`;\n }\n\n // Calculate current position (needed for grid calculations)\n const currentLeft = westSideResizeEnabled ? initialLeft + deltaX : initialLeft;\n const currentTop = northSideResizeEnabled ? initialTop + deltaY : initialTop;\n\n // Update ghost panel - calculate grid position BEFORE grid size\n const nextGridX = pixelsToGridPosition(currentLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(currentTop, baseSize, gap);\n const nextGridW = pixelsToGridSize(newWidth, baseSize, gap, columnCount, nextGridX);\n const nextGridH = pixelsToGridSize(newHeight, baseSize, gap, columnCount, nextGridY);\n\n const snappedWidth = gridToPixels(nextGridW, baseSize, gap);\n const snappedHeight = gridToPixels(nextGridH, baseSize, gap);\n const snappedLeft = gridPositionToPixels(nextGridX, baseSize, gap);\n const snappedTop = gridPositionToPixels(nextGridY, baseSize, gap);\n\n updateGhostPanelPosition(snappedLeft, snappedTop);\n updateGhostPanelSize(snappedWidth, snappedHeight);\n };\n\n const onMouseUp = () => {\n if (!draggingElement) return;\n\n hideGhostPanel();\n\n const rect = draggingElement.getBoundingClientRect();\n const droppedLeft = parseFloat(draggingElement.style.left) || 0;\n const droppedTop = parseFloat(draggingElement.style.top) || 0;\n\n const nextGridX = pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);\n\n const nextGridW = pixelsToGridSize(rect.width, baseSize, gap, columnCount, nextGridX);\n const nextGridH = pixelsToGridSize(rect.height, baseSize, gap, columnCount, nextGridY);\n\n const left = gridPositionToPixels(nextGridX, baseSize, gap);\n const top = gridPositionToPixels(nextGridY, baseSize, gap);\n const width = gridToPixels(nextGridW, baseSize, gap);\n const height = gridToPixels(nextGridH, baseSize, gap);\n\n draggingElement.style.width = `${rect.width}px`;\n draggingElement.style.height = `${rect.height}px`;\n draggingElement.style.cursor = initialCursor;\n draggingElement.style.transition = \"\";\n\n window.requestAnimationFrame(() => {\n draggingElement.style.top = `${top}px`;\n draggingElement.style.left = `${left}px`;\n draggingElement.style.width = `${width}px`;\n draggingElement.style.height = `${height}px`;\n draggingElement.style.zIndex = initialZIndex;\n draggingElement.style.transition =\n \"width 0.1s ease-out, height 0.1s ease-out, top 0.1s ease-out, left 0.1s ease-out\";\n });\n\n updatePanelsWithAnimation({ ...panel, x: nextGridX, y: nextGridY, w: nextGridW, h: nextGridH }, state.panels);\n\n isResizing = false;\n internalState.activePanelId = null;\n document.body.classList.remove(\"panelgrid-resizing\");\n\n mouseMoveController.abort();\n mouseUpController.abort();\n };\n\n document.addEventListener(\"mousemove\", onMouseMove, {\n signal: mouseMoveController.signal,\n });\n document.addEventListener(\"mouseup\", onMouseUp, {\n signal: mouseUpController.signal,\n });\n },\n [\n baseSize,\n gap,\n internalState,\n state.panels,\n updatePanelsWithAnimation,\n showGhostPanel,\n updateGhostPanelPosition,\n updateGhostPanelSize,\n hideGhostPanel,\n columnCount,\n ]\n );\n\n // Create ref callback for panel elements\n const createRefCallback = useCallback(\n (panelId: PanelId) => (element: HTMLElement | null) => {\n if (!element) return;\n if (!internalState.draggableElements[panelId]) {\n internalState.draggableElements[panelId] = element;\n }\n },\n [internalState]\n );\n\n // Memoize panel props to avoid recreating on every render\n const panelsWithProps = useMemo(() => {\n return state.panels.map((panel) => {\n const isAnimating = internalState.animatingPanels.has(panel.id);\n const isActive = internalState.activePanelId === panel.id;\n\n return {\n panelProps: {\n key: panel.id,\n lockSize: panel.lockSize,\n positionData: {\n x: panel.x,\n y: panel.y,\n w: panel.w,\n h: panel.h,\n },\n style: {\n top: gridPositionToPixels(panel.y, baseSize, gap),\n left: gridPositionToPixels(panel.x, baseSize, gap),\n width: gridToPixels(panel.w, baseSize, gap),\n height: gridToPixels(panel.h, baseSize, gap),\n transition:\n isAnimating && !isActive\n ? \"top 0.3s ease-out, left 0.3s ease-out, width 0.3s ease-out, height 0.3s ease-out\"\n : undefined,\n },\n ref: createRefCallback(panel.id),\n onMouseDown: createDragHandler(panel),\n },\n resizeHandleProps: panel.lockSize\n ? undefined\n : {\n onMouseDown: createResizeHandler(panel),\n },\n };\n });\n }, [\n state.panels,\n baseSize,\n gap,\n internalState.animatingPanels,\n internalState.activePanelId,\n createRefCallback,\n createDragHandler,\n createResizeHandler,\n ]);\n\n const addPanel = useCallback(\n (panel: Partial<PanelCoordinate>) => {\n dispatch({\n type: \"ADD_PANEL\",\n newPanel: {\n ...panel,\n id: panel.id || Math.random().toString(36).substring(2, 15),\n },\n columnCount,\n });\n },\n [columnCount]\n );\n\n const removePanel = useCallback((id: PanelId) => {\n dispatch({ type: \"REMOVE_PANEL\", panelId: id });\n }, []);\n\n const lockPanelSize = useCallback((id: PanelId) => {\n dispatch({ type: \"LOCK_PANEL_SIZE\", panelId: id });\n }, []);\n\n const unlockPanelSize = useCallback((id: PanelId) => {\n dispatch({ type: \"UNLOCK_PANEL_SIZE\", panelId: id });\n }, []);\n\n const exportState = useCallback(() => {\n return state.panels;\n }, [state.panels]);\n\n return {\n panels: panelsWithProps,\n panelMap,\n ghostPanelRef,\n addPanel,\n removePanel,\n lockPanelSize,\n unlockPanelSize,\n exportState,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"usePanelGrid.cjs","names":["findNewPositionToAddPanel","newPanelCoordinate: PanelCoordinate","RESIZE_CURSOR_MAP: Record<string, string>","rearrangePanels","detectAnimatingPanels","e","pixelsToGridPosition","gridPositionToPixels","pixelsToGridSize","gridToPixels"],"sources":["../src/usePanelGrid.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useReducer, useRef } from \"react\";\nimport {\n applySnapAnimation,\n detectAnimatingPanels,\n gridPositionToPixels,\n gridToPixels,\n pixelsToGridPosition,\n pixelsToGridSize,\n rearrangePanels,\n} from \"./helpers\";\nimport { findNewPositionToAddPanel } from \"./helpers/rearrangement\";\nimport type { PanelCoordinate, PanelId, RearrangementFunction, ResizeHandlePosition } from \"./types\";\n\ninterface PanelGridOptions {\n panels: PanelCoordinate[];\n columnCount: number;\n baseSize: number;\n gap: number;\n resizeHandlePositions: ResizeHandlePosition[];\n rearrangement?: RearrangementFunction;\n}\n\nexport interface PanelGridState {\n panels: PanelCoordinate[];\n}\n\ninterface InternalPanelState {\n activePanelId: PanelId | null;\n draggableElements: Record<PanelId, HTMLElement | null>;\n animatingPanels: Set<PanelId>;\n}\n\nexport type PanelGridAction =\n | { type: \"UPDATE_PANELS\"; newPanels: PanelCoordinate[] }\n | { type: \"ADD_PANEL\"; newPanel: Partial<PanelCoordinate>; columnCount: number }\n | { type: \"REMOVE_PANEL\"; panelId: PanelId }\n | { type: \"LOCK_PANEL_SIZE\"; panelId: PanelId }\n | { type: \"UNLOCK_PANEL_SIZE\"; panelId: PanelId }\n | { type: \"LOCK_PANEL_POSITION\"; panelId: PanelId }\n | { type: \"UNLOCK_PANEL_POSITION\"; panelId: PanelId };\n\nexport function panelGridReducer(state: PanelGridState, action: PanelGridAction): PanelGridState {\n switch (action.type) {\n case \"UPDATE_PANELS\":\n return {\n ...state,\n panels: action.newPanels,\n };\n case \"ADD_PANEL\": {\n const { newPanel, columnCount } = action;\n const newPosition = findNewPositionToAddPanel(newPanel, state.panels, columnCount);\n const newPanelCoordinate: PanelCoordinate = {\n id: newPanel.id || Math.random().toString(36).substring(2, 15),\n x: newPosition.x,\n y: newPosition.y,\n w: newPanel.w || 1,\n h: newPanel.h || 1,\n };\n return {\n ...state,\n panels: [...state.panels, newPanelCoordinate],\n };\n }\n case \"REMOVE_PANEL\":\n return {\n ...state,\n panels: state.panels.filter((panel) => panel.id !== action.panelId),\n };\n case \"LOCK_PANEL_SIZE\":\n return {\n ...state,\n panels: state.panels.map((panel) => (panel.id === action.panelId ? { ...panel, lockSize: true } : panel)),\n };\n case \"UNLOCK_PANEL_SIZE\":\n return {\n ...state,\n panels: state.panels.map((panel) => (panel.id === action.panelId ? { ...panel, lockSize: false } : panel)),\n };\n case \"LOCK_PANEL_POSITION\":\n return {\n ...state,\n panels: state.panels.map((panel) => (panel.id === action.panelId ? { ...panel, lockPosition: true } : panel)),\n };\n case \"UNLOCK_PANEL_POSITION\":\n return {\n ...state,\n panels: state.panels.map((panel) => (panel.id === action.panelId ? { ...panel, lockPosition: false } : panel)),\n };\n default:\n return state;\n }\n}\n\nconst ANIMATION_DURATION = 300;\ntype TimeoutId = ReturnType<typeof setTimeout>;\n\nconst RESIZE_CURSOR_MAP: Record<string, string> = {\n nw: \"nwse-resize\",\n ne: \"nesw-resize\",\n se: \"nwse-resize\",\n sw: \"nesw-resize\",\n n: \"ns-resize\",\n s: \"ns-resize\",\n e: \"ew-resize\",\n w: \"ew-resize\",\n};\n\nexport function usePanelGrid({\n panels,\n columnCount,\n baseSize,\n gap,\n resizeHandlePositions: _resizeHandlePositions,\n rearrangement,\n}: PanelGridOptions) {\n const [state, dispatch] = useReducer(panelGridReducer, {\n panels,\n });\n const ghostPanelRef = useRef<HTMLDivElement | null>(null);\n const animationTimeoutsRef = useRef<Set<TimeoutId>>(new Set());\n\n const panelMap = useMemo(() => {\n const map = new Map<PanelId, PanelCoordinate>();\n state.panels.forEach((panel) => {\n map.set(panel.id, panel);\n });\n return map;\n }, [state.panels]);\n\n const internalState = useRef<InternalPanelState>({\n activePanelId: null,\n draggableElements: {},\n animatingPanels: new Set(),\n }).current;\n\n // Cleanup animation timeouts on unmount\n useEffect(() => {\n return () => {\n animationTimeoutsRef.current.forEach((timeoutId) => clearTimeout(timeoutId));\n animationTimeoutsRef.current.clear();\n };\n }, []);\n\n // Ghost panel helper functions\n // Direct DOM manipulation is intentionally used here for performance.\n // This avoids React re-renders during high-frequency mousemove events.\n const showGhostPanel = useCallback((left: number, top: number, width: number, height: number) => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.display = \"block\";\n ghostPanelRef.current.style.left = `${left}px`;\n ghostPanelRef.current.style.top = `${top}px`;\n ghostPanelRef.current.style.width = `${width}px`;\n ghostPanelRef.current.style.height = `${height}px`;\n ghostPanelRef.current.style.outline = \"1px dashed rgba(0, 0, 0, 0.2)\";\n }, []);\n\n const updateGhostPanelPosition = useCallback((left: number, top: number) => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.left = `${left}px`;\n ghostPanelRef.current.style.top = `${top}px`;\n }, []);\n\n const updateGhostPanelSize = useCallback((width: number, height: number) => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.width = `${width}px`;\n ghostPanelRef.current.style.height = `${height}px`;\n }, []);\n\n const hideGhostPanel = useCallback(() => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.display = \"none\";\n }, []);\n\n // Callback to update panels and trigger animations\n // Returns the rearranged panels so callers can inspect the actual result position\n const updatePanelsWithAnimation = useCallback(\n (updatedPanel: PanelCoordinate, currentPanels: PanelCoordinate[]): PanelCoordinate[] => {\n // Use custom rearrangement function if provided, otherwise use default\n const rearrange = rearrangement || rearrangePanels;\n const nextPanels = rearrange(updatedPanel, currentPanels, columnCount);\n\n // Detect which panels have been rearranged\n internalState.animatingPanels = detectAnimatingPanels({\n oldPanels: currentPanels,\n newPanels: nextPanels,\n excludePanelId: updatedPanel.id,\n });\n\n dispatch({ type: \"UPDATE_PANELS\", newPanels: nextPanels });\n\n // Clear animating panels after animation completes\n const timeoutId = setTimeout(() => {\n internalState.animatingPanels.clear();\n animationTimeoutsRef.current.delete(timeoutId);\n }, ANIMATION_DURATION);\n animationTimeoutsRef.current.add(timeoutId);\n\n return nextPanels;\n },\n [columnCount, internalState, rearrangement]\n );\n\n // Create drag handler for a specific panel\n const createDragHandler = useCallback(\n (panel: PanelCoordinate) => (e: React.MouseEvent<HTMLDivElement>) => {\n if (panel.lockPosition) return;\n internalState.activePanelId = panel.id;\n const draggingElement = internalState.draggableElements[panel.id];\n if (!draggingElement) return;\n\n let isDragging = true;\n const initialX = e.clientX;\n const initialY = e.clientY;\n const offsetX = draggingElement.offsetLeft;\n const offsetY = draggingElement.offsetTop;\n const originalTransition = draggingElement.style.transition;\n\n document.body.classList.add(\"panelgrid-dragging\");\n\n draggingElement.classList.add(\"panelgrid-panel--dragging\");\n draggingElement.style.transition = \"\";\n\n showGhostPanel(offsetX, offsetY, draggingElement.offsetWidth, draggingElement.offsetHeight);\n\n const mouseUpListenerCtrl = new AbortController();\n const mouseMoveListenerCtrl = new AbortController();\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isDragging) return;\n if (!draggingElement) return;\n\n const currentX = e.clientX;\n const currentY = e.clientY;\n const deltaX = currentX - initialX;\n const deltaY = currentY - initialY;\n\n draggingElement.style.left = offsetX + deltaX + \"px\";\n draggingElement.style.top = offsetY + deltaY + \"px\";\n\n // Update ghost panel position to snap to grid\n const droppedLeft = offsetX + deltaX;\n const droppedTop = offsetY + deltaY;\n const nextGridX = pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);\n const nextLeft = gridPositionToPixels(nextGridX, baseSize, gap);\n const nextTop = gridPositionToPixels(nextGridY, baseSize, gap);\n\n updateGhostPanelPosition(nextLeft, nextTop);\n\n e.preventDefault(); // Prevent text selection during drag\n };\n\n const onMouseUp = () => {\n if (!draggingElement) return;\n\n isDragging = false;\n draggingElement.classList.remove(\"panelgrid-panel--dragging\");\n\n hideGhostPanel();\n\n const droppedLeft = parseFloat(draggingElement.style.left) || 0;\n const droppedTop = parseFloat(draggingElement.style.top) || 0;\n\n const nextGridX = pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);\n\n // Rearrange first so we know the actual result position.\n // On rollback (e.g. collision with a locked panel), the panel ends up at its original\n // position rather than the dropped position. applySnapAnimation must target the actual\n // result — otherwise the DOM is left at the dropped position while React's cached style\n // (same reference, no useMemo invalidation) never corrects it.\n const nextPanels = updatePanelsWithAnimation({ ...panel, x: nextGridX, y: nextGridY }, state.panels);\n const resultPanel = nextPanels.find((p) => p.id === panel.id);\n const actualGridX = resultPanel?.x ?? nextGridX;\n const actualGridY = resultPanel?.y ?? nextGridY;\n\n const nextLeft = gridPositionToPixels(actualGridX, baseSize, gap);\n const nextTop = gridPositionToPixels(actualGridY, baseSize, gap);\n\n applySnapAnimation({\n element: draggingElement,\n droppedLeft,\n droppedTop,\n nextLeft,\n nextTop,\n originalTransition,\n });\n\n document.body.classList.remove(\"panelgrid-dragging\");\n internalState.activePanelId = null;\n\n mouseMoveListenerCtrl.abort();\n mouseUpListenerCtrl.abort();\n };\n\n document.addEventListener(\"mousemove\", onMouseMove, {\n signal: mouseMoveListenerCtrl.signal,\n });\n document.addEventListener(\"mouseup\", onMouseUp, {\n signal: mouseUpListenerCtrl.signal,\n });\n },\n [\n baseSize,\n gap,\n internalState,\n state.panels,\n updatePanelsWithAnimation,\n showGhostPanel,\n updateGhostPanelPosition,\n hideGhostPanel,\n columnCount,\n ]\n );\n\n // Create resize handler for a specific panel\n const createResizeHandler = useCallback(\n (panel: PanelCoordinate) => (e: React.MouseEvent<HTMLSpanElement>) => {\n e.stopPropagation();\n let isResizing = true;\n internalState.activePanelId = panel.id;\n const draggingElement = internalState.draggableElements[panel.id];\n if (!draggingElement) return;\n\n const startX = e.clientX;\n const startY = e.clientY;\n const initialTop = draggingElement.offsetTop;\n const initialLeft = draggingElement.offsetLeft;\n const initialWidth = draggingElement.offsetWidth;\n const initialHeight = draggingElement.offsetHeight;\n const initialZIndex = draggingElement.style.zIndex;\n const initialCursor = draggingElement.style.cursor;\n\n const resizeHandle = e.currentTarget;\n const handlePosition = resizeHandle.dataset.pgResizeHandle;\n\n const northSideResizeEnabled = handlePosition?.includes(\"n\");\n const westSideResizeEnabled = handlePosition?.includes(\"w\");\n const isVerticalResizeOnly = handlePosition === \"n\" || handlePosition === \"s\";\n const isHorizontalResizeOnly = handlePosition === \"e\" || handlePosition === \"w\";\n\n document.body.classList.add(\"panelgrid-resizing\");\n\n draggingElement.style.cursor = RESIZE_CURSOR_MAP[handlePosition || \"se\"] || \"nwse-resize\";\n draggingElement.style.transition = \"\";\n\n showGhostPanel(draggingElement.offsetLeft, draggingElement.offsetTop, initialWidth, initialHeight);\n\n const mouseMoveController = new AbortController();\n const mouseUpController = new AbortController();\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isResizing) return;\n if (!draggingElement) return;\n\n const deltaX = e.clientX - startX;\n const deltaY = e.clientY - startY;\n\n // Calculate dimensions once, accounting for all resize directions\n const newWidth = westSideResizeEnabled\n ? Math.max(initialWidth - deltaX, 1)\n : isVerticalResizeOnly\n ? initialWidth\n : initialWidth + deltaX;\n\n const newHeight = northSideResizeEnabled\n ? Math.max(initialHeight - deltaY, 1)\n : isHorizontalResizeOnly\n ? initialHeight\n : initialHeight + deltaY;\n\n draggingElement.style.width = `${newWidth}px`;\n draggingElement.style.height = `${newHeight}px`;\n draggingElement.style.zIndex = \"calc(infinity)\";\n\n // Update position for north/west resizing\n if (northSideResizeEnabled) {\n draggingElement.style.top = `${initialTop + deltaY}px`;\n }\n if (westSideResizeEnabled) {\n draggingElement.style.left = `${initialLeft + deltaX}px`;\n }\n\n // Calculate current position (needed for grid calculations)\n const currentLeft = westSideResizeEnabled ? initialLeft + deltaX : initialLeft;\n const currentTop = northSideResizeEnabled ? initialTop + deltaY : initialTop;\n\n // Update ghost panel - calculate grid position BEFORE grid size\n const nextGridX = pixelsToGridPosition(currentLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(currentTop, baseSize, gap);\n const nextGridW = pixelsToGridSize(newWidth, baseSize, gap, columnCount, nextGridX);\n const nextGridH = pixelsToGridSize(newHeight, baseSize, gap, columnCount, nextGridY);\n\n const snappedWidth = gridToPixels(nextGridW, baseSize, gap);\n const snappedHeight = gridToPixels(nextGridH, baseSize, gap);\n const snappedLeft = gridPositionToPixels(nextGridX, baseSize, gap);\n const snappedTop = gridPositionToPixels(nextGridY, baseSize, gap);\n\n updateGhostPanelPosition(snappedLeft, snappedTop);\n updateGhostPanelSize(snappedWidth, snappedHeight);\n };\n\n const onMouseUp = () => {\n if (!draggingElement) return;\n\n hideGhostPanel();\n\n const rect = draggingElement.getBoundingClientRect();\n const droppedLeft = parseFloat(draggingElement.style.left) || 0;\n const droppedTop = parseFloat(draggingElement.style.top) || 0;\n\n const nextGridX = pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);\n\n const nextGridW = pixelsToGridSize(rect.width, baseSize, gap, columnCount, nextGridX);\n const nextGridH = pixelsToGridSize(rect.height, baseSize, gap, columnCount, nextGridY);\n\n // Rearrange first to get the actual result position/size.\n // On rollback the panel may end up at its original position; the RAF must target\n // that actual position so the DOM stays in sync with React state.\n const nextPanels = updatePanelsWithAnimation(\n { ...panel, x: nextGridX, y: nextGridY, w: nextGridW, h: nextGridH },\n state.panels\n );\n const resultPanel = nextPanels.find((p) => p.id === panel.id);\n const actualX = resultPanel?.x ?? nextGridX;\n const actualY = resultPanel?.y ?? nextGridY;\n const actualW = resultPanel?.w ?? nextGridW;\n const actualH = resultPanel?.h ?? nextGridH;\n\n const left = gridPositionToPixels(actualX, baseSize, gap);\n const top = gridPositionToPixels(actualY, baseSize, gap);\n const width = gridToPixels(actualW, baseSize, gap);\n const height = gridToPixels(actualH, baseSize, gap);\n\n draggingElement.style.width = `${rect.width}px`;\n draggingElement.style.height = `${rect.height}px`;\n draggingElement.style.cursor = initialCursor;\n draggingElement.style.transition = \"\";\n\n window.requestAnimationFrame(() => {\n draggingElement.style.top = `${top}px`;\n draggingElement.style.left = `${left}px`;\n draggingElement.style.width = `${width}px`;\n draggingElement.style.height = `${height}px`;\n draggingElement.style.zIndex = initialZIndex;\n draggingElement.style.transition =\n \"width 0.1s ease-out, height 0.1s ease-out, top 0.1s ease-out, left 0.1s ease-out\";\n });\n\n isResizing = false;\n internalState.activePanelId = null;\n document.body.classList.remove(\"panelgrid-resizing\");\n\n mouseMoveController.abort();\n mouseUpController.abort();\n };\n\n document.addEventListener(\"mousemove\", onMouseMove, {\n signal: mouseMoveController.signal,\n });\n document.addEventListener(\"mouseup\", onMouseUp, {\n signal: mouseUpController.signal,\n });\n },\n [\n baseSize,\n gap,\n internalState,\n state.panels,\n updatePanelsWithAnimation,\n showGhostPanel,\n updateGhostPanelPosition,\n updateGhostPanelSize,\n hideGhostPanel,\n columnCount,\n ]\n );\n\n // Create ref callback for panel elements\n const createRefCallback = useCallback(\n (panelId: PanelId) => (element: HTMLElement | null) => {\n if (!element) return;\n if (!internalState.draggableElements[panelId]) {\n internalState.draggableElements[panelId] = element;\n }\n },\n [internalState]\n );\n\n // Memoize panel props to avoid recreating on every render\n const panelsWithProps = useMemo(() => {\n return state.panels.map((panel) => {\n const isAnimating = internalState.animatingPanels.has(panel.id);\n const isActive = internalState.activePanelId === panel.id;\n\n return {\n panelProps: {\n key: panel.id,\n lockSize: panel.lockSize,\n lockPosition: panel.lockPosition,\n positionData: {\n x: panel.x,\n y: panel.y,\n w: panel.w,\n h: panel.h,\n },\n style: {\n top: gridPositionToPixels(panel.y, baseSize, gap),\n left: gridPositionToPixels(panel.x, baseSize, gap),\n width: gridToPixels(panel.w, baseSize, gap),\n height: gridToPixels(panel.h, baseSize, gap),\n transition:\n isAnimating && !isActive\n ? \"top 0.3s ease-out, left 0.3s ease-out, width 0.3s ease-out, height 0.3s ease-out\"\n : undefined,\n },\n ref: createRefCallback(panel.id),\n onMouseDown: createDragHandler(panel),\n },\n resizeHandleProps: panel.lockSize\n ? undefined\n : {\n onMouseDown: createResizeHandler(panel),\n },\n };\n });\n }, [\n state.panels,\n baseSize,\n gap,\n internalState.animatingPanels,\n internalState.activePanelId,\n createRefCallback,\n createDragHandler,\n createResizeHandler,\n ]);\n\n const addPanel = useCallback(\n (panel: Partial<PanelCoordinate>) => {\n dispatch({\n type: \"ADD_PANEL\",\n newPanel: {\n ...panel,\n id: panel.id || Math.random().toString(36).substring(2, 15),\n },\n columnCount,\n });\n },\n [columnCount]\n );\n\n const removePanel = useCallback((id: PanelId) => {\n dispatch({ type: \"REMOVE_PANEL\", panelId: id });\n }, []);\n\n const lockPanelSize = useCallback((id: PanelId) => {\n dispatch({ type: \"LOCK_PANEL_SIZE\", panelId: id });\n }, []);\n\n const unlockPanelSize = useCallback((id: PanelId) => {\n dispatch({ type: \"UNLOCK_PANEL_SIZE\", panelId: id });\n }, []);\n\n const lockPanelPosition = useCallback((id: PanelId) => {\n dispatch({ type: \"LOCK_PANEL_POSITION\", panelId: id });\n }, []);\n\n const unlockPanelPosition = useCallback((id: PanelId) => {\n dispatch({ type: \"UNLOCK_PANEL_POSITION\", panelId: id });\n }, []);\n\n const exportState = useCallback(() => {\n return state.panels;\n }, [state.panels]);\n\n return {\n panels: panelsWithProps,\n panelMap,\n ghostPanelRef,\n addPanel,\n removePanel,\n lockPanelSize,\n unlockPanelSize,\n lockPanelPosition,\n unlockPanelPosition,\n exportState,\n };\n}\n"],"mappings":"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"}
@@ -22,6 +22,7 @@ declare function usePanelGrid({
22
22
  panelProps: {
23
23
  key: PanelId;
24
24
  lockSize: boolean | undefined;
25
+ lockPosition: boolean | undefined;
25
26
  positionData: {
26
27
  x: number;
27
28
  y: number;
@@ -48,6 +49,8 @@ declare function usePanelGrid({
48
49
  removePanel: (id: PanelId) => void;
49
50
  lockPanelSize: (id: PanelId) => void;
50
51
  unlockPanelSize: (id: PanelId) => void;
52
+ lockPanelPosition: (id: PanelId) => void;
53
+ unlockPanelPosition: (id: PanelId) => void;
51
54
  exportState: () => PanelCoordinate[];
52
55
  };
53
56
  //#endregion
@@ -22,6 +22,7 @@ declare function usePanelGrid({
22
22
  panelProps: {
23
23
  key: PanelId;
24
24
  lockSize: boolean | undefined;
25
+ lockPosition: boolean | undefined;
25
26
  positionData: {
26
27
  x: number;
27
28
  y: number;
@@ -48,6 +49,8 @@ declare function usePanelGrid({
48
49
  removePanel: (id: PanelId) => void;
49
50
  lockPanelSize: (id: PanelId) => void;
50
51
  unlockPanelSize: (id: PanelId) => void;
52
+ lockPanelPosition: (id: PanelId) => void;
53
+ unlockPanelPosition: (id: PanelId) => void;
51
54
  exportState: () => PanelCoordinate[];
52
55
  };
53
56
  //#endregion
@@ -44,6 +44,20 @@ function panelGridReducer(state, action) {
44
44
  lockSize: false
45
45
  } : panel)
46
46
  };
47
+ case "LOCK_PANEL_POSITION": return {
48
+ ...state,
49
+ panels: state.panels.map((panel) => panel.id === action.panelId ? {
50
+ ...panel,
51
+ lockPosition: true
52
+ } : panel)
53
+ };
54
+ case "UNLOCK_PANEL_POSITION": return {
55
+ ...state,
56
+ panels: state.panels.map((panel) => panel.id === action.panelId ? {
57
+ ...panel,
58
+ lockPosition: false
59
+ } : panel)
60
+ };
47
61
  default: return state;
48
62
  }
49
63
  }
@@ -119,12 +133,14 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, resizeHandlePosition
119
133
  animationTimeoutsRef.current.delete(timeoutId);
120
134
  }, ANIMATION_DURATION);
121
135
  animationTimeoutsRef.current.add(timeoutId);
136
+ return nextPanels;
122
137
  }, [
123
138
  columnCount,
124
139
  internalState,
125
140
  rearrangement
126
141
  ]);
127
142
  const createDragHandler = useCallback((panel) => (e) => {
143
+ if (panel.lockPosition) return;
128
144
  internalState.activePanelId = panel.id;
129
145
  const draggingElement = internalState.draggableElements[panel.id];
130
146
  if (!draggingElement) return;
@@ -165,19 +181,21 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, resizeHandlePosition
165
181
  const droppedTop = parseFloat(draggingElement.style.top) || 0;
166
182
  const nextGridX = pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);
167
183
  const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);
184
+ const resultPanel = updatePanelsWithAnimation({
185
+ ...panel,
186
+ x: nextGridX,
187
+ y: nextGridY
188
+ }, state.panels).find((p) => p.id === panel.id);
189
+ const actualGridX = resultPanel?.x ?? nextGridX;
190
+ const actualGridY = resultPanel?.y ?? nextGridY;
168
191
  applySnapAnimation({
169
192
  element: draggingElement,
170
193
  droppedLeft,
171
194
  droppedTop,
172
- nextLeft: gridPositionToPixels(nextGridX, baseSize, gap),
173
- nextTop: gridPositionToPixels(nextGridY, baseSize, gap),
195
+ nextLeft: gridPositionToPixels(actualGridX, baseSize, gap),
196
+ nextTop: gridPositionToPixels(actualGridY, baseSize, gap),
174
197
  originalTransition
175
198
  });
176
- updatePanelsWithAnimation({
177
- ...panel,
178
- x: nextGridX,
179
- y: nextGridY
180
- }, state.panels);
181
199
  document.body.classList.remove("panelgrid-dragging");
182
200
  internalState.activePanelId = null;
183
201
  mouseMoveListenerCtrl.abort();
@@ -254,10 +272,21 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, resizeHandlePosition
254
272
  const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);
255
273
  const nextGridW = pixelsToGridSize(rect.width, baseSize, gap, columnCount, nextGridX);
256
274
  const nextGridH = pixelsToGridSize(rect.height, baseSize, gap, columnCount, nextGridY);
257
- const left = gridPositionToPixels(nextGridX, baseSize, gap);
258
- const top = gridPositionToPixels(nextGridY, baseSize, gap);
259
- const width = gridToPixels(nextGridW, baseSize, gap);
260
- const height = gridToPixels(nextGridH, baseSize, gap);
275
+ const resultPanel = updatePanelsWithAnimation({
276
+ ...panel,
277
+ x: nextGridX,
278
+ y: nextGridY,
279
+ w: nextGridW,
280
+ h: nextGridH
281
+ }, state.panels).find((p) => p.id === panel.id);
282
+ const actualX = resultPanel?.x ?? nextGridX;
283
+ const actualY = resultPanel?.y ?? nextGridY;
284
+ const actualW = resultPanel?.w ?? nextGridW;
285
+ const actualH = resultPanel?.h ?? nextGridH;
286
+ const left = gridPositionToPixels(actualX, baseSize, gap);
287
+ const top = gridPositionToPixels(actualY, baseSize, gap);
288
+ const width = gridToPixels(actualW, baseSize, gap);
289
+ const height = gridToPixels(actualH, baseSize, gap);
261
290
  draggingElement.style.width = `${rect.width}px`;
262
291
  draggingElement.style.height = `${rect.height}px`;
263
292
  draggingElement.style.cursor = initialCursor;
@@ -270,13 +299,6 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, resizeHandlePosition
270
299
  draggingElement.style.zIndex = initialZIndex;
271
300
  draggingElement.style.transition = "width 0.1s ease-out, height 0.1s ease-out, top 0.1s ease-out, left 0.1s ease-out";
272
301
  });
273
- updatePanelsWithAnimation({
274
- ...panel,
275
- x: nextGridX,
276
- y: nextGridY,
277
- w: nextGridW,
278
- h: nextGridH
279
- }, state.panels);
280
302
  isResizing = false;
281
303
  internalState.activePanelId = null;
282
304
  document.body.classList.remove("panelgrid-resizing");
@@ -310,6 +332,7 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, resizeHandlePosition
310
332
  panelProps: {
311
333
  key: panel.id,
312
334
  lockSize: panel.lockSize,
335
+ lockPosition: panel.lockPosition,
313
336
  positionData: {
314
337
  x: panel.x,
315
338
  y: panel.y,
@@ -369,6 +392,18 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, resizeHandlePosition
369
392
  panelId: id
370
393
  });
371
394
  }, []),
395
+ lockPanelPosition: useCallback((id) => {
396
+ dispatch({
397
+ type: "LOCK_PANEL_POSITION",
398
+ panelId: id
399
+ });
400
+ }, []),
401
+ unlockPanelPosition: useCallback((id) => {
402
+ dispatch({
403
+ type: "UNLOCK_PANEL_POSITION",
404
+ panelId: id
405
+ });
406
+ }, []),
372
407
  exportState: useCallback(() => {
373
408
  return state.panels;
374
409
  }, [state.panels])