react-grid-layout
Version:
A draggable and resizable grid layout with responsive breakpoints, for React.
868 lines (859 loc) • 24.4 kB
JavaScript
;
// src/core/position.ts
function setTransform({
top,
left,
width,
height
}) {
const translate = `translate(${left}px,${top}px)`;
return {
transform: translate,
WebkitTransform: translate,
MozTransform: translate,
msTransform: translate,
OTransform: translate,
width: `${width}px`,
height: `${height}px`,
position: "absolute"
};
}
function setTopLeft({
top,
left,
width,
height
}) {
return {
top: `${top}px`,
left: `${left}px`,
width: `${width}px`,
height: `${height}px`,
position: "absolute"
};
}
function perc(num) {
return num * 100 + "%";
}
function constrainWidth(left, currentWidth, newWidth, containerWidth) {
return left + newWidth > containerWidth ? currentWidth : newWidth;
}
function constrainHeight(top, currentHeight, newHeight) {
return top < 0 ? currentHeight : newHeight;
}
function constrainLeft(left) {
return Math.max(0, left);
}
function constrainTop(top) {
return Math.max(0, top);
}
var resizeNorth = (currentSize, newSize, _containerWidth) => {
const { left, height, width } = newSize;
const top = currentSize.top - (height - currentSize.height);
return {
left,
width,
height: constrainHeight(top, currentSize.height, height),
top: constrainTop(top)
};
};
var resizeEast = (currentSize, newSize, containerWidth) => {
const { top, left, height, width } = newSize;
return {
top,
height,
width: constrainWidth(
currentSize.left,
currentSize.width,
width,
containerWidth
),
left: constrainLeft(left)
};
};
var resizeWest = (currentSize, newSize, _containerWidth) => {
const { top, height, width } = newSize;
const left = currentSize.left + currentSize.width - width;
if (left < 0) {
return {
height,
width: currentSize.left + currentSize.width,
top: constrainTop(top),
left: 0
};
}
return {
height,
width,
top: constrainTop(top),
left
};
};
var resizeSouth = (currentSize, newSize, _containerWidth) => {
const { top, left, height, width } = newSize;
return {
width,
left,
height: constrainHeight(top, currentSize.height, height),
top: constrainTop(top)
};
};
var resizeNorthEast = (currentSize, newSize, containerWidth) => resizeNorth(
currentSize,
resizeEast(currentSize, newSize, containerWidth));
var resizeNorthWest = (currentSize, newSize, containerWidth) => resizeNorth(
currentSize,
resizeWest(currentSize, newSize));
var resizeSouthEast = (currentSize, newSize, containerWidth) => resizeSouth(
currentSize,
resizeEast(currentSize, newSize, containerWidth));
var resizeSouthWest = (currentSize, newSize, containerWidth) => resizeSouth(
currentSize,
resizeWest(currentSize, newSize));
var resizeHandlerMap = {
n: resizeNorth,
ne: resizeNorthEast,
e: resizeEast,
se: resizeSouthEast,
s: resizeSouth,
sw: resizeSouthWest,
w: resizeWest,
nw: resizeNorthWest
};
function resizeItemInDirection(direction, currentSize, newSize, containerWidth) {
const handler = resizeHandlerMap[direction];
if (!handler) {
return newSize;
}
return handler(currentSize, { ...currentSize, ...newSize }, containerWidth);
}
var transformStrategy = {
type: "transform",
scale: 1,
calcStyle(pos) {
return setTransform(pos);
},
calcDragPosition(clientX, clientY, offsetX, offsetY) {
return {
left: clientX - offsetX,
top: clientY - offsetY
};
}
};
var absoluteStrategy = {
type: "absolute",
scale: 1,
calcStyle(pos) {
return setTopLeft(pos);
},
calcDragPosition(clientX, clientY, offsetX, offsetY) {
return {
left: clientX - offsetX,
top: clientY - offsetY
};
}
};
function createScaledStrategy(scale) {
return {
type: "transform",
scale,
calcStyle(pos) {
return setTransform(pos);
},
calcDragPosition(clientX, clientY, offsetX, offsetY) {
return {
left: (clientX - offsetX) / scale,
top: (clientY - offsetY) / scale
};
}
};
}
var defaultPositionStrategy = transformStrategy;
// src/core/types.ts
var defaultGridConfig = {
cols: 12,
rowHeight: 150,
margin: [10, 10],
containerPadding: null,
maxRows: Infinity
};
var defaultDragConfig = {
enabled: true,
bounded: false,
threshold: 3
};
var defaultResizeConfig = {
enabled: true,
handles: ["se"]
};
var defaultDropConfig = {
enabled: false,
defaultItem: { w: 1, h: 1 }
};
// src/core/collision.ts
function collides(l1, l2) {
if (l1.i === l2.i) return false;
if (l1.x + l1.w <= l2.x) return false;
if (l1.x >= l2.x + l2.w) return false;
if (l1.y + l1.h <= l2.y) return false;
if (l1.y >= l2.y + l2.h) return false;
return true;
}
function getFirstCollision(layout, layoutItem) {
for (let i = 0; i < layout.length; i++) {
const item = layout[i];
if (item !== void 0 && collides(item, layoutItem)) {
return item;
}
}
return void 0;
}
function getAllCollisions(layout, layoutItem) {
return layout.filter((l) => collides(l, layoutItem));
}
// src/core/sort.ts
function sortLayoutItems(layout, compactType) {
if (compactType === "horizontal") {
return sortLayoutItemsByColRow(layout);
}
if (compactType === "vertical") {
return sortLayoutItemsByRowCol(layout);
}
return [...layout];
}
function sortLayoutItemsByRowCol(layout) {
return [...layout].sort((a, b) => {
if (a.y !== b.y) {
return a.y - b.y;
}
return a.x - b.x;
});
}
function sortLayoutItemsByColRow(layout) {
return [...layout].sort((a, b) => {
if (a.x !== b.x) {
return a.x - b.x;
}
return a.y - b.y;
});
}
// src/core/layout.ts
function bottom(layout) {
let max = 0;
for (let i = 0; i < layout.length; i++) {
const item = layout[i];
if (item !== void 0) {
const bottomY = item.y + item.h;
if (bottomY > max) max = bottomY;
}
}
return max;
}
function getLayoutItem(layout, id) {
for (let i = 0; i < layout.length; i++) {
const item = layout[i];
if (item !== void 0 && item.i === id) {
return item;
}
}
return void 0;
}
function getStatics(layout) {
return layout.filter((l) => l.static === true);
}
function cloneLayoutItem(layoutItem) {
return {
i: layoutItem.i,
x: layoutItem.x,
y: layoutItem.y,
w: layoutItem.w,
h: layoutItem.h,
minW: layoutItem.minW,
maxW: layoutItem.maxW,
minH: layoutItem.minH,
maxH: layoutItem.maxH,
moved: Boolean(layoutItem.moved),
static: Boolean(layoutItem.static),
isDraggable: layoutItem.isDraggable,
isResizable: layoutItem.isResizable,
resizeHandles: layoutItem.resizeHandles,
isBounded: layoutItem.isBounded
};
}
function cloneLayout(layout) {
const newLayout = new Array(layout.length);
for (let i = 0; i < layout.length; i++) {
const item = layout[i];
if (item !== void 0) {
newLayout[i] = cloneLayoutItem(item);
}
}
return newLayout;
}
function modifyLayout(layout, layoutItem) {
const newLayout = new Array(layout.length);
for (let i = 0; i < layout.length; i++) {
const item = layout[i];
if (item !== void 0) {
if (layoutItem.i === item.i) {
newLayout[i] = layoutItem;
} else {
newLayout[i] = item;
}
}
}
return newLayout;
}
function withLayoutItem(layout, itemKey, cb) {
let item = getLayoutItem(layout, itemKey);
if (!item) {
return [[...layout], null];
}
item = cb(cloneLayoutItem(item));
const newLayout = modifyLayout(layout, item);
return [newLayout, item];
}
function correctBounds(layout, bounds) {
const collidesWith = getStatics(layout);
for (let i = 0; i < layout.length; i++) {
const l = layout[i];
if (l === void 0) continue;
if (l.x + l.w > bounds.cols) {
l.x = bounds.cols - l.w;
}
if (l.x < 0) {
l.x = 0;
l.w = bounds.cols;
}
if (!l.static) {
collidesWith.push(l);
} else {
while (getFirstCollision(collidesWith, l)) {
l.y++;
}
}
}
return layout;
}
function moveElement(layout, l, x, y, isUserAction, preventCollision, compactType, cols, allowOverlap) {
if (l.static && l.isDraggable !== true) {
return [...layout];
}
if (l.y === y && l.x === x) {
return [...layout];
}
const oldX = l.x;
const oldY = l.y;
if (typeof x === "number") l.x = x;
if (typeof y === "number") l.y = y;
l.moved = true;
let sorted = sortLayoutItems(layout, compactType);
const movingUp = compactType === "vertical" && typeof y === "number" ? oldY >= y : compactType === "horizontal" && typeof x === "number" ? oldX >= x : false;
if (movingUp) {
sorted = sorted.reverse();
}
const collisions = getAllCollisions(sorted, l);
const hasCollisions = collisions.length > 0;
if (hasCollisions && allowOverlap) {
return cloneLayout(layout);
}
if (hasCollisions && preventCollision) {
l.x = oldX;
l.y = oldY;
l.moved = false;
return layout;
}
let resultLayout = [...layout];
for (let i = 0; i < collisions.length; i++) {
const collision = collisions[i];
if (collision === void 0) continue;
if (collision.moved) continue;
if (collision.static) {
resultLayout = moveElementAwayFromCollision(
resultLayout,
collision,
l,
isUserAction,
compactType);
} else {
resultLayout = moveElementAwayFromCollision(
resultLayout,
l,
collision,
isUserAction,
compactType);
}
}
return resultLayout;
}
function moveElementAwayFromCollision(layout, collidesWith, itemToMove, isUserAction, compactType, cols) {
const compactH = compactType === "horizontal";
const compactV = compactType === "vertical";
const preventCollision = collidesWith.static;
if (isUserAction) {
isUserAction = false;
const fakeItem = {
x: compactH ? Math.max(collidesWith.x - itemToMove.w, 0) : itemToMove.x,
y: compactV ? Math.max(collidesWith.y - itemToMove.h, 0) : itemToMove.y,
w: itemToMove.w,
h: itemToMove.h,
i: "-1"
};
const firstCollision = getFirstCollision(layout, fakeItem);
const collisionNorth = firstCollision !== void 0 && firstCollision.y + firstCollision.h > collidesWith.y;
const collisionWest = firstCollision !== void 0 && collidesWith.x + collidesWith.w > firstCollision.x;
if (!firstCollision) {
return moveElement(
layout,
itemToMove,
compactH ? fakeItem.x : void 0,
compactV ? fakeItem.y : void 0,
isUserAction,
preventCollision,
compactType);
}
if (collisionNorth && compactV) {
return moveElement(
layout,
itemToMove,
void 0,
itemToMove.y + 1,
isUserAction,
preventCollision,
compactType);
}
if (collisionNorth && compactType === null) {
collidesWith.y = itemToMove.y;
itemToMove.y = itemToMove.y + itemToMove.h;
return [...layout];
}
if (collisionWest && compactH) {
return moveElement(
layout,
collidesWith,
itemToMove.x,
void 0,
isUserAction,
preventCollision,
compactType);
}
}
const newX = compactH ? itemToMove.x + 1 : void 0;
const newY = compactV ? itemToMove.y + 1 : void 0;
if (newX === void 0 && newY === void 0) {
return [...layout];
}
return moveElement(
layout,
itemToMove,
newX,
newY,
isUserAction,
preventCollision,
compactType);
}
function validateLayout(layout, contextName = "Layout") {
const requiredProps = ["x", "y", "w", "h"];
if (!Array.isArray(layout)) {
throw new Error(`${contextName} must be an array!`);
}
for (let i = 0; i < layout.length; i++) {
const item = layout[i];
if (item === void 0) continue;
for (const key of requiredProps) {
const value = item[key];
if (typeof value !== "number" || Number.isNaN(value)) {
throw new Error(
`ReactGridLayout: ${contextName}[${i}].${key} must be a number! Received: ${String(value)} (${typeof value})`
);
}
}
if (item.i !== void 0 && typeof item.i !== "string") {
throw new Error(
`ReactGridLayout: ${contextName}[${i}].i must be a string! Received: ${String(item.i)} (${typeof item.i})`
);
}
}
}
// src/core/compact-compat.ts
function getStatics2(layout) {
return layout.filter((l) => l.static);
}
var heightWidth = { x: "w", y: "h" };
function resolveCompactionCollision(layout, item, moveToCoord, axis) {
const sizeProp = heightWidth[axis];
item[axis] += 1;
const itemIndex = layout.findIndex((l) => l.i === item.i);
for (let i = itemIndex + 1; i < layout.length; i++) {
const otherItem = layout[i];
if (otherItem === void 0) continue;
if (otherItem.static) continue;
if (otherItem.y > item.y + item.h) break;
if (collides(item, otherItem)) {
resolveCompactionCollision(
layout,
otherItem,
moveToCoord + item[sizeProp],
axis
);
}
}
item[axis] = moveToCoord;
}
function compactItemInternal(compareWith, l, compactType, cols, fullLayout, allowOverlap, b) {
const compactV = compactType === "vertical";
const compactH = compactType === "horizontal";
if (compactV) {
if (typeof b === "number") {
l.y = Math.min(b, l.y);
} else {
l.y = Math.min(bottom(compareWith), l.y);
}
while (l.y > 0 && !getFirstCollision(compareWith, l)) {
l.y--;
}
} else if (compactH) {
while (l.x > 0 && !getFirstCollision(compareWith, l)) {
l.x--;
}
}
let collision;
while ((collision = getFirstCollision(compareWith, l)) !== void 0 && !(compactType === null && allowOverlap)) {
if (compactH) {
resolveCompactionCollision(fullLayout, l, collision.x + collision.w, "x");
} else {
resolveCompactionCollision(fullLayout, l, collision.y + collision.h, "y");
}
if (compactH && l.x + l.w > cols) {
l.x = cols - l.w;
l.y++;
while (l.x > 0 && !getFirstCollision(compareWith, l)) {
l.x--;
}
}
}
l.y = Math.max(l.y, 0);
l.x = Math.max(l.x, 0);
return l;
}
function compact(layout, compactType, cols, allowOverlap) {
const compareWith = getStatics2(layout);
let b = bottom(compareWith);
const sorted = sortLayoutItems(layout, compactType);
const out = new Array(layout.length);
for (let i = 0; i < sorted.length; i++) {
const sortedItem = sorted[i];
if (sortedItem === void 0) continue;
let l = cloneLayoutItem(sortedItem);
if (!l.static) {
l = compactItemInternal(
compareWith,
l,
compactType,
cols,
sorted,
allowOverlap,
b
);
b = Math.max(b, l.y + l.h);
compareWith.push(l);
}
const originalIndex = layout.indexOf(sortedItem);
out[originalIndex] = l;
l.moved = false;
}
return out;
}
function compactItem(compareWith, l, compactType, cols, fullLayout, allowOverlap, maxY) {
return compactItemInternal(
compareWith,
cloneLayoutItem(l),
compactType,
cols,
fullLayout,
allowOverlap,
maxY
);
}
// src/core/compactors.ts
function resolveCompactionCollision2(layout, item, moveToCoord, axis) {
const sizeProp = axis === "x" ? "w" : "h";
item[axis] += 1;
const itemIndex = layout.findIndex((l) => l.i === item.i);
for (let i = itemIndex + 1; i < layout.length; i++) {
const otherItem = layout[i];
if (otherItem === void 0) continue;
if (otherItem.static) continue;
if (otherItem.y > item.y + item.h) break;
if (collides(item, otherItem)) {
resolveCompactionCollision2(
layout,
otherItem,
moveToCoord + item[sizeProp],
axis
);
}
}
item[axis] = moveToCoord;
}
function compactItemVertical(compareWith, l, fullLayout, maxY) {
l.y = Math.min(maxY, l.y);
while (l.y > 0 && !getFirstCollision(compareWith, l)) {
l.y--;
}
let collision;
while ((collision = getFirstCollision(compareWith, l)) !== void 0) {
resolveCompactionCollision2(fullLayout, l, collision.y + collision.h, "y");
}
l.y = Math.max(l.y, 0);
return l;
}
function compactItemHorizontal(compareWith, l, cols, fullLayout) {
while (l.x > 0 && !getFirstCollision(compareWith, l)) {
l.x--;
}
let collision;
while ((collision = getFirstCollision(compareWith, l)) !== void 0) {
resolveCompactionCollision2(fullLayout, l, collision.x + collision.w, "x");
if (l.x + l.w > cols) {
l.x = cols - l.w;
l.y++;
while (l.x > 0 && !getFirstCollision(compareWith, l)) {
l.x--;
}
}
}
l.x = Math.max(l.x, 0);
return l;
}
var verticalCompactor = {
type: "vertical",
allowOverlap: false,
compact(layout, _cols) {
const compareWith = getStatics(layout);
let maxY = bottom(compareWith);
const sorted = sortLayoutItemsByRowCol(layout);
const out = new Array(layout.length);
for (let i = 0; i < sorted.length; i++) {
const sortedItem = sorted[i];
if (sortedItem === void 0) continue;
let l = cloneLayoutItem(sortedItem);
if (!l.static) {
l = compactItemVertical(compareWith, l, sorted, maxY);
maxY = Math.max(maxY, l.y + l.h);
compareWith.push(l);
}
const originalIndex = layout.indexOf(sortedItem);
out[originalIndex] = l;
l.moved = false;
}
return out;
},
onMove(layout, item, x, y, _cols) {
const newLayout = cloneLayout(layout);
const movedItem = newLayout.find((l) => l.i === item.i);
if (movedItem) {
movedItem.x = x;
movedItem.y = y;
movedItem.moved = true;
}
return newLayout;
}
};
var horizontalCompactor = {
type: "horizontal",
allowOverlap: false,
compact(layout, cols) {
const compareWith = getStatics(layout);
const sorted = sortLayoutItemsByColRow(layout);
const out = new Array(layout.length);
for (let i = 0; i < sorted.length; i++) {
const sortedItem = sorted[i];
if (sortedItem === void 0) continue;
let l = cloneLayoutItem(sortedItem);
if (!l.static) {
l = compactItemHorizontal(compareWith, l, cols, sorted);
compareWith.push(l);
}
const originalIndex = layout.indexOf(sortedItem);
out[originalIndex] = l;
l.moved = false;
}
return out;
},
onMove(layout, item, x, y, _cols) {
const newLayout = cloneLayout(layout);
const movedItem = newLayout.find((l) => l.i === item.i);
if (movedItem) {
movedItem.x = x;
movedItem.y = y;
movedItem.moved = true;
}
return newLayout;
}
};
var noCompactor = {
type: null,
allowOverlap: false,
compact(layout, _cols) {
return cloneLayout(layout);
},
onMove(layout, item, x, y, _cols) {
const newLayout = cloneLayout(layout);
const movedItem = newLayout.find((l) => l.i === item.i);
if (movedItem) {
movedItem.x = x;
movedItem.y = y;
movedItem.moved = true;
}
return newLayout;
}
};
var verticalOverlapCompactor = {
...verticalCompactor,
allowOverlap: true,
compact(layout, _cols) {
return cloneLayout(layout);
}
};
var horizontalOverlapCompactor = {
...horizontalCompactor,
allowOverlap: true,
compact(layout, _cols) {
return cloneLayout(layout);
}
};
function getCompactor(compactType, allowOverlap = false, preventCollision = false) {
let baseCompactor;
if (allowOverlap) {
if (compactType === "vertical") baseCompactor = verticalOverlapCompactor;
else if (compactType === "horizontal")
baseCompactor = horizontalOverlapCompactor;
else baseCompactor = noCompactor;
} else {
if (compactType === "vertical") baseCompactor = verticalCompactor;
else if (compactType === "horizontal") baseCompactor = horizontalCompactor;
else baseCompactor = noCompactor;
}
if (preventCollision) {
return { ...baseCompactor, preventCollision };
}
return baseCompactor;
}
// src/core/responsive.ts
function sortBreakpoints(breakpoints) {
const keys = Object.keys(breakpoints);
return keys.sort((a, b) => breakpoints[a] - breakpoints[b]);
}
function getBreakpointFromWidth(breakpoints, width) {
const sorted = sortBreakpoints(breakpoints);
let matching = sorted[0];
if (matching === void 0) {
throw new Error("No breakpoints defined");
}
for (let i = 1; i < sorted.length; i++) {
const breakpointName = sorted[i];
if (breakpointName === void 0) continue;
const breakpointWidth = breakpoints[breakpointName];
if (width > breakpointWidth) {
matching = breakpointName;
}
}
return matching;
}
function getColsFromBreakpoint(breakpoint, cols) {
const colCount = cols[breakpoint];
if (colCount === void 0) {
throw new Error(
`ResponsiveReactGridLayout: \`cols\` entry for breakpoint ${String(breakpoint)} is missing!`
);
}
return colCount;
}
function findOrGenerateResponsiveLayout(layouts, breakpoints, breakpoint, lastBreakpoint, cols, compactType) {
const existingLayout = layouts[breakpoint];
if (existingLayout) {
return cloneLayout(existingLayout);
}
let layout = layouts[lastBreakpoint];
const breakpointsSorted = sortBreakpoints(breakpoints);
const breakpointsAbove = breakpointsSorted.slice(
breakpointsSorted.indexOf(breakpoint)
);
for (let i = 0; i < breakpointsAbove.length; i++) {
const b = breakpointsAbove[i];
if (b === void 0) continue;
const layoutForBreakpoint = layouts[b];
if (layoutForBreakpoint) {
layout = layoutForBreakpoint;
break;
}
}
const clonedLayout = cloneLayout(layout || []);
return compact(correctBounds(clonedLayout, { cols }), compactType, cols);
}
function getIndentationValue(value, breakpoint) {
if (Array.isArray(value)) {
return value;
}
const breakpointMap = value;
const breakpointValue = breakpointMap[breakpoint];
if (breakpointValue !== void 0) {
return breakpointValue;
}
const keys = Object.keys(breakpointMap);
for (const key of keys) {
const v = breakpointMap[key];
if (v !== void 0) {
return v;
}
}
return [10, 10];
}
exports.absoluteStrategy = absoluteStrategy;
exports.bottom = bottom;
exports.cloneLayout = cloneLayout;
exports.cloneLayoutItem = cloneLayoutItem;
exports.collides = collides;
exports.compact = compact;
exports.compactItem = compactItem;
exports.compactItemHorizontal = compactItemHorizontal;
exports.compactItemVertical = compactItemVertical;
exports.correctBounds = correctBounds;
exports.createScaledStrategy = createScaledStrategy;
exports.defaultDragConfig = defaultDragConfig;
exports.defaultDropConfig = defaultDropConfig;
exports.defaultGridConfig = defaultGridConfig;
exports.defaultPositionStrategy = defaultPositionStrategy;
exports.defaultResizeConfig = defaultResizeConfig;
exports.findOrGenerateResponsiveLayout = findOrGenerateResponsiveLayout;
exports.getAllCollisions = getAllCollisions;
exports.getBreakpointFromWidth = getBreakpointFromWidth;
exports.getColsFromBreakpoint = getColsFromBreakpoint;
exports.getCompactor = getCompactor;
exports.getFirstCollision = getFirstCollision;
exports.getIndentationValue = getIndentationValue;
exports.getLayoutItem = getLayoutItem;
exports.getStatics = getStatics;
exports.horizontalCompactor = horizontalCompactor;
exports.horizontalOverlapCompactor = horizontalOverlapCompactor;
exports.modifyLayout = modifyLayout;
exports.moveElement = moveElement;
exports.moveElementAwayFromCollision = moveElementAwayFromCollision;
exports.noCompactor = noCompactor;
exports.perc = perc;
exports.resizeItemInDirection = resizeItemInDirection;
exports.resolveCompactionCollision = resolveCompactionCollision2;
exports.setTopLeft = setTopLeft;
exports.setTransform = setTransform;
exports.sortBreakpoints = sortBreakpoints;
exports.sortLayoutItems = sortLayoutItems;
exports.sortLayoutItemsByColRow = sortLayoutItemsByColRow;
exports.sortLayoutItemsByRowCol = sortLayoutItemsByRowCol;
exports.transformStrategy = transformStrategy;
exports.validateLayout = validateLayout;
exports.verticalCompactor = verticalCompactor;
exports.verticalOverlapCompactor = verticalOverlapCompactor;
exports.withLayoutItem = withLayoutItem;
//# sourceMappingURL=chunk-3WO4SAYB.js.map
//# sourceMappingURL=chunk-3WO4SAYB.js.map