react-grid-layout
Version:
A draggable and resizable grid layout with responsive breakpoints, for React.
1,300 lines (1,298 loc) • 37.7 kB
JavaScript
import { setTransform, setTopLeft, perc, resizeItemInDirection, defaultPositionStrategy, defaultGridConfig, defaultDragConfig, defaultResizeConfig, defaultDropConfig, getCompactor, bottom, getLayoutItem, cloneLayoutItem, moveElement, compact, withLayoutItem, getAllCollisions, getBreakpointFromWidth, getColsFromBreakpoint, findOrGenerateResponsiveLayout, cloneLayout, getIndentationValue, correctBounds } from './chunk-4HNUMWQK.mjs';
import { calcXY, calcGridItemWHPx, clamp, calcGridColWidth, calcWH, calcGridItemPosition } from './chunk-2KUHNJXF.mjs';
import React2, { useState, useRef, useMemo, useCallback, useEffect } from 'react';
import { DraggableCore } from 'react-draggable';
import { Resizable } from 'react-resizable';
import clsx from 'clsx';
import { jsx, jsxs } from 'react/jsx-runtime';
import { deepEqual } from 'fast-equals';
function GridItem(props) {
const {
children,
cols,
containerWidth,
margin,
containerPadding,
rowHeight,
maxRows,
isDraggable,
isResizable,
isBounded,
static: isStatic,
useCSSTransforms = true,
usePercentages = false,
transformScale = 1,
droppingPosition,
className = "",
style,
handle = "",
cancel = "",
x,
y,
w,
h,
minW = 1,
maxW = Infinity,
minH = 1,
maxH = Infinity,
i,
resizeHandles,
resizeHandle,
onDragStart: onDragStartProp,
onDrag: onDragProp,
onDragStop: onDragStopProp,
onResizeStart: onResizeStartProp,
onResize: onResizeProp,
onResizeStop: onResizeStopProp
} = props;
const [dragging, setDragging] = useState(false);
const [resizing, setResizing] = useState(false);
const elementRef = useRef(null);
const dragPositionRef = useRef({ left: 0, top: 0 });
const resizePositionRef = useRef({
top: 0,
left: 0,
width: 0,
height: 0
});
const prevDroppingPositionRef = useRef(
void 0
);
const positionParams = useMemo(
() => ({
cols,
containerPadding,
containerWidth,
margin,
maxRows,
rowHeight
}),
[cols, containerPadding, containerWidth, margin, maxRows, rowHeight]
);
const createStyle = useCallback(
(pos2) => {
if (useCSSTransforms) {
return setTransform(pos2);
}
const styleObj = setTopLeft(pos2);
if (usePercentages) {
return {
...styleObj,
left: perc(pos2.left / containerWidth),
width: perc(pos2.width / containerWidth)
};
}
return styleObj;
},
[useCSSTransforms, usePercentages, containerWidth]
);
const onDragStart = useCallback(
(e, { node }) => {
if (!onDragStartProp) return;
const { offsetParent } = node;
if (!offsetParent) return;
const parentRect = offsetParent.getBoundingClientRect();
const clientRect = node.getBoundingClientRect();
const cLeft = clientRect.left / transformScale;
const pLeft = parentRect.left / transformScale;
const cTop = clientRect.top / transformScale;
const pTop = parentRect.top / transformScale;
const newPosition = {
left: cLeft - pLeft + offsetParent.scrollLeft,
top: cTop - pTop + offsetParent.scrollTop
};
dragPositionRef.current = newPosition;
setDragging(true);
const { x: newX, y: newY } = calcXY(
positionParams,
newPosition.top,
newPosition.left,
w,
h
);
onDragStartProp(i, newX, newY, {
e,
node,
newPosition
});
},
[onDragStartProp, transformScale, positionParams, w, h, i]
);
const onDrag = useCallback(
(e, { node, deltaX, deltaY }) => {
if (!onDragProp || !dragging) return;
let top = dragPositionRef.current.top + deltaY;
let left = dragPositionRef.current.left + deltaX;
if (isBounded) {
const { offsetParent } = node;
if (offsetParent) {
const bottomBoundary = offsetParent.clientHeight - calcGridItemWHPx(h, rowHeight, margin[1]);
top = clamp(top, 0, bottomBoundary);
const colWidth = calcGridColWidth(positionParams);
const rightBoundary = containerWidth - calcGridItemWHPx(w, colWidth, margin[0]);
left = clamp(left, 0, rightBoundary);
}
}
const newPosition = { top, left };
dragPositionRef.current = newPosition;
const { x: newX, y: newY } = calcXY(positionParams, top, left, w, h);
onDragProp(i, newX, newY, {
e,
node,
newPosition
});
},
[
onDragProp,
dragging,
isBounded,
h,
rowHeight,
margin,
positionParams,
containerWidth,
w,
i
]
);
const onDragStop = useCallback(
(e, { node }) => {
if (!onDragStopProp || !dragging) return;
const { left, top } = dragPositionRef.current;
const newPosition = { top, left };
setDragging(false);
dragPositionRef.current = { left: 0, top: 0 };
const { x: newX, y: newY } = calcXY(positionParams, top, left, w, h);
onDragStopProp(i, newX, newY, {
e,
node,
newPosition
});
},
[onDragStopProp, dragging, positionParams, w, h, i]
);
const onResizeHandler = useCallback(
(e, { node, size, handle: resizeHandle2 }, position, handlerName) => {
const handler = handlerName === "onResizeStart" ? onResizeStartProp : handlerName === "onResize" ? onResizeProp : onResizeStopProp;
if (!handler) return;
let updatedSize;
if (node) {
updatedSize = resizeItemInDirection(
resizeHandle2,
position,
size,
containerWidth
);
} else {
updatedSize = {
...size,
top: position.top,
left: position.left
};
}
resizePositionRef.current = updatedSize;
let { w: newW, h: newH } = calcWH(
positionParams,
updatedSize.width,
updatedSize.height,
x,
y,
resizeHandle2
);
newW = clamp(newW, Math.max(minW, 1), maxW);
newH = clamp(newH, minH, maxH);
handler(i, newW, newH, {
e: e.nativeEvent,
node,
size: updatedSize,
handle: resizeHandle2
});
},
[
onResizeStartProp,
onResizeProp,
onResizeStopProp,
containerWidth,
positionParams,
x,
y,
minW,
maxW,
minH,
maxH,
i
]
);
const handleResizeStart = useCallback(
(e, data) => {
setResizing(true);
const pos2 = calcGridItemPosition(positionParams, x, y, w, h);
const typedData = {
...data,
handle: data.handle
};
onResizeHandler(e, typedData, pos2, "onResizeStart");
},
[onResizeHandler, positionParams, x, y, w, h]
);
const handleResize = useCallback(
(e, data) => {
const pos2 = calcGridItemPosition(positionParams, x, y, w, h);
const typedData = {
...data,
handle: data.handle
};
onResizeHandler(e, typedData, pos2, "onResize");
},
[onResizeHandler, positionParams, x, y, w, h]
);
const handleResizeStop = useCallback(
(e, data) => {
setResizing(false);
resizePositionRef.current = { top: 0, left: 0, width: 0, height: 0 };
const pos2 = calcGridItemPosition(positionParams, x, y, w, h);
const typedData = {
...data,
handle: data.handle
};
onResizeHandler(e, typedData, pos2, "onResizeStop");
},
[onResizeHandler, positionParams, x, y, w, h]
);
useEffect(() => {
if (!droppingPosition) return;
const node = elementRef.current;
if (!node) return;
const prevDroppingPosition = prevDroppingPositionRef.current || {
left: 0,
top: 0
};
const shouldDrag = dragging && (droppingPosition.left !== prevDroppingPosition.left || droppingPosition.top !== prevDroppingPosition.top);
if (!dragging) {
const fakeData = {
node,
deltaX: droppingPosition.left,
deltaY: droppingPosition.top,
lastX: 0,
lastY: 0,
x: droppingPosition.left,
y: droppingPosition.top
};
onDragStart(droppingPosition.e, fakeData);
} else if (shouldDrag) {
const deltaX = droppingPosition.left - dragPositionRef.current.left;
const deltaY = droppingPosition.top - dragPositionRef.current.top;
const fakeData = {
node,
deltaX,
deltaY,
lastX: dragPositionRef.current.left,
lastY: dragPositionRef.current.top,
x: droppingPosition.left,
y: droppingPosition.top
};
onDrag(droppingPosition.e, fakeData);
}
prevDroppingPositionRef.current = droppingPosition;
}, [droppingPosition, dragging, onDragStart, onDrag]);
const pos = calcGridItemPosition(
positionParams,
x,
y,
w,
h,
dragging ? dragPositionRef.current : null,
resizing ? resizePositionRef.current : null
);
const child = React2.Children.only(children);
const maxWidth = calcGridItemPosition(positionParams, 0, 0, cols, 0).width;
const mins = calcGridItemPosition(positionParams, 0, 0, minW, minH);
const maxes = calcGridItemPosition(positionParams, 0, 0, maxW, maxH);
const minConstraints = [mins.width, mins.height];
const maxConstraints = [
Math.min(maxes.width, maxWidth),
Math.min(maxes.height, Infinity)
];
const childProps = child.props;
const childClassName = childProps["className"];
const childStyle = childProps["style"];
let newChild = React2.cloneElement(child, {
ref: elementRef,
className: clsx("react-grid-item", childClassName, className, {
static: isStatic,
resizing,
"react-draggable": isDraggable,
"react-draggable-dragging": dragging,
dropping: Boolean(droppingPosition),
cssTransforms: useCSSTransforms
}),
style: {
...style,
...childStyle,
...createStyle(pos)
}
});
const resizableHandle = resizeHandle;
newChild = /* @__PURE__ */ jsx(
Resizable,
{
draggableOpts: { disabled: !isResizable },
className: isResizable ? void 0 : "react-resizable-hide",
width: pos.width,
height: pos.height,
minConstraints,
maxConstraints,
onResizeStart: handleResizeStart,
onResize: handleResize,
onResizeStop: handleResizeStop,
transformScale,
resizeHandles,
handle: resizableHandle,
children: newChild
}
);
newChild = /* @__PURE__ */ jsx(
DraggableCore,
{
disabled: !isDraggable,
onStart: onDragStart,
onDrag,
onStop: onDragStop,
handle,
cancel: ".react-resizable-handle" + (cancel ? "," + cancel : ""),
scale: transformScale,
nodeRef: elementRef,
children: newChild
}
);
return newChild;
}
var noop = () => {
};
var layoutClassName = "react-grid-layout";
var isFirefox = false;
try {
isFirefox = /firefox/i.test(navigator.userAgent);
} catch {
}
function childrenEqual(a, b) {
const aArr = React2.Children.toArray(a);
const bArr = React2.Children.toArray(b);
if (aArr.length !== bArr.length) return false;
for (let i = 0; i < aArr.length; i++) {
const aChild = aArr[i];
const bChild = bArr[i];
if (aChild?.key !== bChild?.key) return false;
}
return true;
}
function synchronizeLayoutWithChildren(initialLayout, children, cols, compactType, allowOverlap) {
const layout = [];
const childKeys = /* @__PURE__ */ new Set();
React2.Children.forEach(children, (child) => {
if (!React2.isValidElement(child) || child.key === null) return;
const key = String(child.key);
childKeys.add(key);
const existingItem = initialLayout.find((l) => l.i === key);
if (existingItem) {
layout.push(cloneLayoutItem(existingItem));
} else {
const childProps = child.props;
const dataGrid = childProps["data-grid"];
if (dataGrid) {
layout.push({
i: key,
x: dataGrid.x ?? 0,
y: dataGrid.y ?? 0,
w: dataGrid.w ?? 1,
h: dataGrid.h ?? 1,
minW: dataGrid.minW,
maxW: dataGrid.maxW,
minH: dataGrid.minH,
maxH: dataGrid.maxH,
static: dataGrid.static,
isDraggable: dataGrid.isDraggable,
isResizable: dataGrid.isResizable,
resizeHandles: dataGrid.resizeHandles,
isBounded: dataGrid.isBounded
});
} else {
layout.push({
i: key,
x: 0,
y: bottom(layout),
w: 1,
h: 1
});
}
}
});
const corrected = correctBounds(layout, { cols });
return compact(corrected, compactType, cols, allowOverlap);
}
function GridLayout(props) {
const {
// Required
children,
width,
// Composable config interfaces
gridConfig: gridConfigProp,
dragConfig: dragConfigProp,
resizeConfig: resizeConfigProp,
dropConfig: dropConfigProp,
positionStrategy = defaultPositionStrategy,
compactor: compactorProp,
// Layout data
layout: propsLayout = [],
droppingItem: droppingItemProp,
// Container props
autoSize = true,
className = "",
style = {},
innerRef,
// Callbacks
onLayoutChange = noop,
onDragStart: onDragStartProp = noop,
onDrag: onDragProp = noop,
onDragStop: onDragStopProp = noop,
onResizeStart: onResizeStartProp = noop,
onResize: onResizeProp = noop,
onResizeStop: onResizeStopProp = noop,
onDrop: onDropProp = noop,
onDropDragOver: onDropDragOverProp = noop
} = props;
const gridConfig = useMemo(
() => ({ ...defaultGridConfig, ...gridConfigProp }),
[gridConfigProp]
);
const dragConfig = useMemo(
() => ({ ...defaultDragConfig, ...dragConfigProp }),
[dragConfigProp]
);
const resizeConfig = useMemo(
() => ({ ...defaultResizeConfig, ...resizeConfigProp }),
[resizeConfigProp]
);
const dropConfig = useMemo(
() => ({ ...defaultDropConfig, ...dropConfigProp }),
[dropConfigProp]
);
const { cols, rowHeight, maxRows, margin, containerPadding } = gridConfig;
const {
enabled: isDraggable,
bounded: isBounded,
handle: draggableHandle,
cancel: draggableCancel
} = dragConfig;
const {
enabled: isResizable,
handles: resizeHandles,
handleComponent: resizeHandle
} = resizeConfig;
const { enabled: isDroppable, defaultItem: defaultDropItem } = dropConfig;
const compactor = compactorProp ?? getCompactor("vertical");
const compactType = compactor.type;
const allowOverlap = compactor.allowOverlap;
const preventCollision = compactor.preventCollision ?? false;
const droppingItem = useMemo(
() => droppingItemProp ?? {
i: "__dropping-elem__",
...defaultDropItem
},
[droppingItemProp, defaultDropItem]
);
const useCSSTransforms = positionStrategy.type === "transform";
const transformScale = positionStrategy.scale;
const effectiveContainerPadding = containerPadding ?? margin;
const [mounted, setMounted] = useState(false);
const [layout, setLayout] = useState(
() => synchronizeLayoutWithChildren(
propsLayout,
children,
cols,
compactType,
allowOverlap
)
);
const [activeDrag, setActiveDrag] = useState(null);
const [resizing, setResizing] = useState(false);
const [droppingDOMNode, setDroppingDOMNode] = useState(
null
);
const [droppingPosition, setDroppingPosition] = useState();
const oldDragItemRef = useRef(null);
const oldResizeItemRef = useRef(null);
const oldLayoutRef = useRef(null);
const dragEnterCounterRef = useRef(0);
const prevLayoutRef = useRef(layout);
const prevPropsLayoutRef = useRef(propsLayout);
const prevChildrenRef = useRef(children);
const prevCompactTypeRef = useRef(compactType);
useEffect(() => {
setMounted(true);
if (!deepEqual(layout, propsLayout)) {
onLayoutChange(layout);
}
}, []);
useEffect(() => {
if (activeDrag) return;
const layoutChanged = !deepEqual(propsLayout, prevPropsLayoutRef.current);
const childrenChanged = !childrenEqual(children, prevChildrenRef.current);
const compactTypeChanged = compactType !== prevCompactTypeRef.current;
if (layoutChanged || childrenChanged || compactTypeChanged) {
const baseLayout = layoutChanged ? propsLayout : layout;
const newLayout = synchronizeLayoutWithChildren(
baseLayout,
children,
cols,
compactType,
allowOverlap
);
setLayout(newLayout);
}
prevPropsLayoutRef.current = propsLayout;
prevChildrenRef.current = children;
prevCompactTypeRef.current = compactType;
}, [
propsLayout,
children,
cols,
compactType,
allowOverlap,
activeDrag,
layout
]);
useEffect(() => {
if (!activeDrag && !deepEqual(layout, prevLayoutRef.current)) {
prevLayoutRef.current = layout;
onLayoutChange(layout);
}
}, [layout, activeDrag, onLayoutChange]);
const containerHeight = useMemo(() => {
if (!autoSize) return void 0;
const nbRow = bottom(layout);
const containerPaddingY = effectiveContainerPadding[1];
return nbRow * rowHeight + (nbRow - 1) * margin[1] + containerPaddingY * 2 + "px";
}, [autoSize, layout, rowHeight, margin, effectiveContainerPadding]);
const onDragStart = useCallback(
(i, _x, _y, data) => {
const l = getLayoutItem(layout, i);
if (!l) return;
const placeholder = {
w: l.w,
h: l.h,
x: l.x,
y: l.y,
i
};
oldDragItemRef.current = cloneLayoutItem(l);
oldLayoutRef.current = layout;
setActiveDrag(placeholder);
onDragStartProp(layout, l, l, null, data.e, data.node);
},
[layout, onDragStartProp]
);
const onDrag = useCallback(
(i, x, y, data) => {
const oldDragItem = oldDragItemRef.current;
const l = getLayoutItem(layout, i);
if (!l) return;
const placeholder = {
w: l.w,
h: l.h,
x: l.x,
y: l.y,
i
};
const newLayout = moveElement(
layout,
l,
x,
y,
true,
preventCollision,
compactType,
cols,
allowOverlap
);
onDragProp(newLayout, oldDragItem, l, placeholder, data.e, data.node);
setLayout(
allowOverlap ? newLayout : compact(newLayout, compactType, cols)
);
setActiveDrag(placeholder);
},
[layout, preventCollision, compactType, cols, allowOverlap, onDragProp]
);
const onDragStop = useCallback(
(i, x, y, data) => {
if (!activeDrag) return;
const oldDragItem = oldDragItemRef.current;
const l = getLayoutItem(layout, i);
if (!l) return;
const newLayout = moveElement(
layout,
l,
x,
y,
true,
preventCollision,
compactType,
cols,
allowOverlap
);
const finalLayout = allowOverlap ? newLayout : compact(newLayout, compactType, cols);
onDragStopProp(finalLayout, oldDragItem, l, null, data.e, data.node);
const oldLayout = oldLayoutRef.current;
oldDragItemRef.current = null;
oldLayoutRef.current = null;
setActiveDrag(null);
setLayout(finalLayout);
if (oldLayout && !deepEqual(oldLayout, finalLayout)) {
onLayoutChange(finalLayout);
}
},
[
activeDrag,
layout,
preventCollision,
compactType,
cols,
allowOverlap,
onDragStopProp,
onLayoutChange
]
);
const onResizeStart = useCallback(
(i, _w, _h, data) => {
const l = getLayoutItem(layout, i);
if (!l) return;
oldResizeItemRef.current = cloneLayoutItem(l);
oldLayoutRef.current = layout;
setResizing(true);
onResizeStartProp(layout, l, l, null, data.e, data.node);
},
[layout, onResizeStartProp]
);
const onResize = useCallback(
(i, w, h, data) => {
const oldResizeItem = oldResizeItemRef.current;
const { handle } = data;
let shouldMoveItem = false;
let newX;
let newY;
const [newLayout, l] = withLayoutItem(layout, i, (item) => {
newX = item.x;
newY = item.y;
if (["sw", "w", "nw", "n", "ne"].includes(handle)) {
if (["sw", "nw", "w"].includes(handle)) {
newX = item.x + (item.w - w);
w = item.x !== newX && newX < 0 ? item.w : w;
newX = newX < 0 ? 0 : newX;
}
if (["ne", "n", "nw"].includes(handle)) {
newY = item.y + (item.h - h);
h = item.y !== newY && newY < 0 ? item.h : h;
newY = newY < 0 ? 0 : newY;
}
shouldMoveItem = true;
}
if (preventCollision && !allowOverlap) {
const collisions = getAllCollisions(layout, {
...item,
w,
h,
x: newX ?? item.x,
y: newY ?? item.y
}).filter((layoutItem) => layoutItem.i !== item.i);
if (collisions.length > 0) {
newY = item.y;
h = item.h;
newX = item.x;
w = item.w;
shouldMoveItem = false;
}
}
item.w = w;
item.h = h;
return item;
});
if (!l) return;
let finalLayout = newLayout;
if (shouldMoveItem && newX !== void 0 && newY !== void 0) {
finalLayout = moveElement(
newLayout,
l,
newX,
newY,
true,
preventCollision,
compactType,
cols,
allowOverlap
);
}
const placeholder = {
w: l.w,
h: l.h,
x: l.x,
y: l.y,
i,
static: true
};
onResizeProp(
finalLayout,
oldResizeItem,
l,
placeholder,
data.e,
data.node
);
setLayout(
allowOverlap ? finalLayout : compact(finalLayout, compactType, cols)
);
setActiveDrag(placeholder);
},
[layout, preventCollision, allowOverlap, compactType, cols, onResizeProp]
);
const onResizeStop = useCallback(
(i, _w, _h, data) => {
const oldResizeItem = oldResizeItemRef.current;
const l = getLayoutItem(layout, i);
const finalLayout = allowOverlap ? layout : compact(layout, compactType, cols);
onResizeStopProp(
finalLayout,
oldResizeItem,
l ?? null,
null,
data.e,
data.node
);
const oldLayout = oldLayoutRef.current;
oldResizeItemRef.current = null;
oldLayoutRef.current = null;
setActiveDrag(null);
setResizing(false);
setLayout(finalLayout);
if (oldLayout && !deepEqual(oldLayout, finalLayout)) {
onLayoutChange(finalLayout);
}
},
[layout, allowOverlap, compactType, cols, onResizeStopProp, onLayoutChange]
);
const removeDroppingPlaceholder = useCallback(() => {
const newLayout = compact(
layout.filter((l) => l.i !== droppingItem.i),
compactType,
cols,
allowOverlap
);
setLayout(newLayout);
setDroppingDOMNode(null);
setActiveDrag(null);
setDroppingPosition(void 0);
}, [layout, droppingItem.i, compactType, cols, allowOverlap]);
const handleDragOver = useCallback(
(e) => {
e.preventDefault();
e.stopPropagation();
if (isFirefox && !e.nativeEvent.target?.classList.contains(
layoutClassName
)) {
return false;
}
const onDragOverResult = onDropDragOverProp(e);
if (onDragOverResult === false) {
if (droppingDOMNode) {
removeDroppingPlaceholder();
}
return false;
}
const finalDroppingItem = { ...droppingItem, ...onDragOverResult };
const gridRect = e.currentTarget.getBoundingClientRect();
const layerX = e.clientX - gridRect.left;
const layerY = e.clientY - gridRect.top;
const newDroppingPosition = {
left: layerX / transformScale,
top: layerY / transformScale,
e: e.nativeEvent
};
if (!droppingDOMNode) {
const positionParams = {
cols,
margin,
maxRows,
rowHeight,
containerWidth: width,
containerPadding: effectiveContainerPadding
};
const calculatedPosition = calcXY(
positionParams,
layerY,
layerX,
finalDroppingItem.w,
finalDroppingItem.h
);
setDroppingDOMNode(/* @__PURE__ */ jsx("div", {}, finalDroppingItem.i));
setDroppingPosition(newDroppingPosition);
setLayout([
...layout,
{
...finalDroppingItem,
x: calculatedPosition.x,
y: calculatedPosition.y,
static: false,
isDraggable: true
}
]);
} else if (droppingPosition) {
const shouldUpdate = droppingPosition.left !== layerX || droppingPosition.top !== layerY;
if (shouldUpdate) {
setDroppingPosition(newDroppingPosition);
}
}
},
[
droppingDOMNode,
droppingPosition,
droppingItem,
onDropDragOverProp,
removeDroppingPlaceholder,
transformScale,
cols,
margin,
maxRows,
rowHeight,
width,
effectiveContainerPadding,
layout
]
);
const handleDragLeave = useCallback(
(e) => {
e.preventDefault();
e.stopPropagation();
dragEnterCounterRef.current--;
if (dragEnterCounterRef.current === 0) {
removeDroppingPlaceholder();
}
},
[removeDroppingPlaceholder]
);
const handleDragEnter = useCallback((e) => {
e.preventDefault();
e.stopPropagation();
dragEnterCounterRef.current++;
}, []);
const handleDrop = useCallback(
(e) => {
e.preventDefault();
e.stopPropagation();
const item = layout.find((l) => l.i === droppingItem.i);
dragEnterCounterRef.current = 0;
removeDroppingPlaceholder();
onDropProp(layout, item, e.nativeEvent);
},
[layout, droppingItem.i, removeDroppingPlaceholder, onDropProp]
);
const processGridItem = useCallback(
(child, isDroppingItem) => {
if (!child || !child.key) return null;
const l = getLayoutItem(layout, String(child.key));
if (!l) return null;
const draggable = typeof l.isDraggable === "boolean" ? l.isDraggable : !l.static && isDraggable;
const resizable = typeof l.isResizable === "boolean" ? l.isResizable : !l.static && isResizable;
const resizeHandlesOptions = l.resizeHandles || [...resizeHandles];
const bounded = draggable && isBounded && l.isBounded !== false;
const resizeHandleElement = resizeHandle;
return /* @__PURE__ */ jsx(
GridItem,
{
containerWidth: width,
cols,
margin,
containerPadding: effectiveContainerPadding,
maxRows,
rowHeight,
cancel: draggableCancel,
handle: draggableHandle,
onDragStart,
onDrag,
onDragStop,
onResizeStart,
onResize,
onResizeStop,
isDraggable: draggable,
isResizable: resizable,
isBounded: bounded,
useCSSTransforms: useCSSTransforms && mounted,
usePercentages: !mounted,
transformScale,
w: l.w,
h: l.h,
x: l.x,
y: l.y,
i: l.i,
minH: l.minH,
minW: l.minW,
maxH: l.maxH,
maxW: l.maxW,
static: l.static,
droppingPosition: isDroppingItem ? droppingPosition : void 0,
resizeHandles: resizeHandlesOptions,
resizeHandle: resizeHandleElement,
children: child
},
l.i
);
},
[
layout,
width,
cols,
margin,
effectiveContainerPadding,
maxRows,
rowHeight,
draggableCancel,
draggableHandle,
onDragStart,
onDrag,
onDragStop,
onResizeStart,
onResize,
onResizeStop,
isDraggable,
isResizable,
isBounded,
useCSSTransforms,
mounted,
transformScale,
droppingPosition,
resizeHandles,
resizeHandle
]
);
const renderPlaceholder = () => {
if (!activeDrag) return null;
return /* @__PURE__ */ jsx(
GridItem,
{
w: activeDrag.w,
h: activeDrag.h,
x: activeDrag.x,
y: activeDrag.y,
i: activeDrag.i,
className: `react-grid-placeholder ${resizing ? "placeholder-resizing" : ""}`,
containerWidth: width,
cols,
margin,
containerPadding: effectiveContainerPadding,
maxRows,
rowHeight,
isDraggable: false,
isResizable: false,
isBounded: false,
useCSSTransforms,
transformScale,
children: /* @__PURE__ */ jsx("div", {})
}
);
};
const mergedClassName = clsx(layoutClassName, className);
const mergedStyle = {
height: containerHeight,
...style
};
return /* @__PURE__ */ jsxs(
"div",
{
ref: innerRef,
className: mergedClassName,
style: mergedStyle,
onDrop: isDroppable ? handleDrop : void 0,
onDragLeave: isDroppable ? handleDragLeave : void 0,
onDragEnter: isDroppable ? handleDragEnter : void 0,
onDragOver: isDroppable ? handleDragOver : void 0,
children: [
React2.Children.map(children, (child) => {
if (!React2.isValidElement(child)) return null;
return processGridItem(child);
}),
isDroppable && droppingDOMNode && processGridItem(droppingDOMNode, true),
renderPlaceholder()
]
}
);
}
var DEFAULT_BREAKPOINTS = {
lg: 1200,
md: 996,
sm: 768,
xs: 480,
xxs: 0
};
var DEFAULT_COLS = {
lg: 12,
md: 10,
sm: 6,
xs: 4,
xxs: 2
};
var noop2 = () => {
};
function synchronizeLayoutWithChildren2(initialLayout, children, cols, compactType, allowOverlap) {
const layout = [];
React2.Children.forEach(children, (child) => {
if (!React2.isValidElement(child) || child.key === null) return;
const key = String(child.key);
const existingItem = initialLayout.find((l) => l.i === key);
if (existingItem) {
layout.push({
...existingItem,
i: key
});
} else {
const childProps = child.props;
const dataGrid = childProps["data-grid"];
if (dataGrid) {
layout.push({
i: key,
x: dataGrid.x ?? 0,
y: dataGrid.y ?? 0,
w: dataGrid.w ?? 1,
h: dataGrid.h ?? 1,
minW: dataGrid.minW,
maxW: dataGrid.maxW,
minH: dataGrid.minH,
maxH: dataGrid.maxH,
static: dataGrid.static,
isDraggable: dataGrid.isDraggable,
isResizable: dataGrid.isResizable,
resizeHandles: dataGrid.resizeHandles,
isBounded: dataGrid.isBounded
});
} else {
layout.push({
i: key,
x: 0,
y: bottom(layout),
w: 1,
h: 1
});
}
}
});
const corrected = correctBounds(layout, { cols });
return compact(corrected, compactType, cols, allowOverlap);
}
function ResponsiveGridLayout(props) {
const {
children,
width,
breakpoint: propBreakpoint,
breakpoints = DEFAULT_BREAKPOINTS,
cols: colsConfig = DEFAULT_COLS,
layouts: propsLayouts = {},
rowHeight = 150,
maxRows = Infinity,
margin: propMargin = [10, 10],
containerPadding: propContainerPadding = null,
compactor: compactorProp,
onBreakpointChange = noop2,
onLayoutChange = noop2,
onWidthChange = noop2,
...restProps
} = props;
const compactor = compactorProp ?? getCompactor("vertical");
const compactType = compactor.type;
const allowOverlap = compactor.allowOverlap;
const initialBreakpoint = useMemo(() => {
return propBreakpoint ?? getBreakpointFromWidth(breakpoints, width);
}, []);
const initialCols = useMemo(() => {
return getColsFromBreakpoint(initialBreakpoint, colsConfig);
}, [initialBreakpoint, colsConfig]);
const initialLayout = useMemo(() => {
return findOrGenerateResponsiveLayout(
propsLayouts,
breakpoints,
initialBreakpoint,
initialBreakpoint,
initialCols,
compactType
);
}, []);
const [breakpoint, setBreakpoint] = useState(initialBreakpoint);
const [cols, setCols] = useState(initialCols);
const [layout, setLayout] = useState(initialLayout);
const [layouts, setLayouts] = useState(propsLayouts);
const prevWidthRef = useRef(width);
const prevBreakpointRef = useRef(propBreakpoint);
const prevBreakpointsRef = useRef(breakpoints);
const prevColsRef = useRef(colsConfig);
const prevLayoutsRef = useRef(propsLayouts);
const prevCompactTypeRef = useRef(compactType);
useEffect(() => {
if (!deepEqual(propsLayouts, prevLayoutsRef.current)) {
const newLayout = findOrGenerateResponsiveLayout(
propsLayouts,
breakpoints,
breakpoint,
breakpoint,
cols,
compactType
);
setLayout(newLayout);
setLayouts(propsLayouts);
prevLayoutsRef.current = propsLayouts;
}
}, [propsLayouts, breakpoints, breakpoint, cols, compactType]);
useEffect(() => {
if (compactType !== prevCompactTypeRef.current) {
const newLayout = compact(
cloneLayout(layout),
compactType,
cols,
allowOverlap
);
const newLayouts = {
...layouts,
[breakpoint]: newLayout
};
setLayout(newLayout);
setLayouts(newLayouts);
onLayoutChange(newLayout, newLayouts);
prevCompactTypeRef.current = compactType;
}
}, [
compactType,
layout,
cols,
allowOverlap,
layouts,
breakpoint,
onLayoutChange
]);
useEffect(() => {
const widthChanged = width !== prevWidthRef.current;
const breakpointPropChanged = propBreakpoint !== prevBreakpointRef.current;
const breakpointsChanged = !deepEqual(
breakpoints,
prevBreakpointsRef.current
);
const colsChanged = !deepEqual(colsConfig, prevColsRef.current);
if (widthChanged || breakpointPropChanged || breakpointsChanged || colsChanged) {
const newBreakpoint = propBreakpoint ?? getBreakpointFromWidth(breakpoints, width);
const newCols = getColsFromBreakpoint(newBreakpoint, colsConfig);
const lastBreakpoint = breakpoint;
if (lastBreakpoint !== newBreakpoint || breakpointsChanged || colsChanged) {
const newLayouts = { ...layouts };
if (!newLayouts[lastBreakpoint]) {
newLayouts[lastBreakpoint] = cloneLayout(layout);
}
let newLayout = findOrGenerateResponsiveLayout(
newLayouts,
breakpoints,
newBreakpoint,
lastBreakpoint,
newCols,
compactType
);
newLayout = synchronizeLayoutWithChildren2(
newLayout,
children,
newCols,
compactType,
allowOverlap
);
newLayouts[newBreakpoint] = newLayout;
setBreakpoint(newBreakpoint);
setCols(newCols);
setLayout(newLayout);
setLayouts(newLayouts);
onBreakpointChange(newBreakpoint, newCols);
onLayoutChange(newLayout, newLayouts);
}
const currentMargin2 = getIndentationValue(
propMargin,
newBreakpoint
);
const currentPadding = propContainerPadding ? getIndentationValue(
propContainerPadding,
newBreakpoint
) : null;
onWidthChange(width, currentMargin2, newCols, currentPadding);
prevWidthRef.current = width;
prevBreakpointRef.current = propBreakpoint;
prevBreakpointsRef.current = breakpoints;
prevColsRef.current = colsConfig;
}
}, [
width,
propBreakpoint,
breakpoints,
colsConfig,
breakpoint,
cols,
layout,
layouts,
children,
compactType,
allowOverlap,
propMargin,
propContainerPadding,
onBreakpointChange,
onLayoutChange,
onWidthChange
]);
const handleLayoutChange = useCallback(
(newLayout) => {
const newLayouts = {
...layouts,
[breakpoint]: newLayout
};
setLayout(newLayout);
setLayouts(newLayouts);
onLayoutChange(newLayout, newLayouts);
},
[layouts, breakpoint, onLayoutChange]
);
const currentMargin = useMemo(() => {
return getIndentationValue(
propMargin,
breakpoint
);
}, [propMargin, breakpoint]);
const currentContainerPadding = useMemo(() => {
if (propContainerPadding === null) return null;
return getIndentationValue(
propContainerPadding,
breakpoint
);
}, [propContainerPadding, breakpoint]);
const gridConfig = useMemo(
() => ({
cols,
rowHeight,
maxRows,
margin: currentMargin,
containerPadding: currentContainerPadding
}),
[cols, rowHeight, maxRows, currentMargin, currentContainerPadding]
);
return /* @__PURE__ */ jsx(
GridLayout,
{
...restProps,
width,
gridConfig,
compactor,
onLayoutChange: handleLayoutChange,
layout,
children
}
);
}
export { GridItem, GridLayout, ResponsiveGridLayout };
//# sourceMappingURL=chunk-R35HZZTA.mjs.map
//# sourceMappingURL=chunk-R35HZZTA.mjs.map