react-grid-layout
Version:
A draggable and resizable grid layout with responsive breakpoints, for React.
431 lines (428 loc) • 12.4 kB
JavaScript
'use strict';
var chunk2DDROKB2_js = require('./chunk-2DDROKB2.js');
var chunkBJFPTW5Q_js = require('./chunk-BJFPTW5Q.js');
var react = require('react');
var fastEquals = require('fast-equals');
function useContainerWidth(options = {}) {
const { measureBeforeMount = false, initialWidth = 1280 } = options;
const [width, setWidth] = react.useState(initialWidth);
const [mounted, setMounted] = react.useState(!measureBeforeMount);
const containerRef = react.useRef(null);
const observerRef = react.useRef(null);
const measureWidth = react.useCallback(() => {
const node = containerRef.current;
if (node) {
const newWidth = node.offsetWidth;
setWidth(newWidth);
if (!mounted) {
setMounted(true);
}
}
}, [mounted]);
react.useEffect(() => {
const node = containerRef.current;
if (!node) return;
measureWidth();
if (typeof ResizeObserver !== "undefined") {
observerRef.current = new ResizeObserver((entries) => {
const entry = entries[0];
if (entry) {
const newWidth = entry.contentRect.width;
setWidth(newWidth);
}
});
observerRef.current.observe(node);
}
return () => {
if (observerRef.current) {
observerRef.current.disconnect();
observerRef.current = null;
}
};
}, [measureWidth]);
return {
width,
mounted,
containerRef,
measureWidth
};
}
function useGridLayout(options) {
const {
layout: propsLayout,
cols,
preventCollision = false,
onLayoutChange,
compactor = chunk2DDROKB2_js.verticalCompactor
} = options;
const isDraggingRef = react.useRef(false);
const [layout, setLayoutState] = react.useState(() => {
const corrected = chunkBJFPTW5Q_js.correctBounds(chunkBJFPTW5Q_js.cloneLayout(propsLayout), { cols });
return compactor.compact(corrected, cols);
});
const [dragState, setDragState] = react.useState({
activeDrag: null,
oldDragItem: null,
oldLayout: null
});
const [resizeState, setResizeState] = react.useState({
resizing: false,
oldResizeItem: null,
oldLayout: null
});
const [dropState, setDropState] = react.useState({
droppingDOMNode: null,
droppingPosition: null
});
const prevLayoutRef = react.useRef(layout);
const setLayout = react.useCallback(
(newLayout) => {
const corrected = chunkBJFPTW5Q_js.correctBounds(chunkBJFPTW5Q_js.cloneLayout(newLayout), { cols });
const compacted = compactor.compact(corrected, cols);
setLayoutState(compacted);
},
[cols, compactor]
);
react.useEffect(() => {
if (isDraggingRef.current) return;
if (!fastEquals.deepEqual(propsLayout, prevLayoutRef.current)) {
setLayout(propsLayout);
}
}, [propsLayout, setLayout]);
react.useEffect(() => {
if (!fastEquals.deepEqual(layout, prevLayoutRef.current)) {
prevLayoutRef.current = layout;
onLayoutChange?.(layout);
}
}, [layout, onLayoutChange]);
const onDragStart = react.useCallback(
(itemId, x, y) => {
const item = chunkBJFPTW5Q_js.getLayoutItem(layout, itemId);
if (!item) return null;
isDraggingRef.current = true;
const placeholder = {
...chunkBJFPTW5Q_js.cloneLayoutItem(item),
x,
y,
static: false,
moved: false
};
setDragState({
activeDrag: placeholder,
oldDragItem: chunkBJFPTW5Q_js.cloneLayoutItem(item),
oldLayout: chunkBJFPTW5Q_js.cloneLayout(layout)
});
return placeholder;
},
[layout]
);
const onDrag = react.useCallback(
(itemId, x, y) => {
const item = chunkBJFPTW5Q_js.getLayoutItem(layout, itemId);
if (!item) return;
setDragState((prev) => ({
...prev,
activeDrag: prev.activeDrag ? { ...prev.activeDrag, x, y } : null
}));
const newLayout = chunkBJFPTW5Q_js.moveElement(
layout,
item,
x,
y,
true,
// isUserAction
preventCollision,
compactor.type,
cols,
compactor.allowOverlap
);
const compacted = compactor.compact(newLayout, cols);
setLayoutState(compacted);
},
[layout, cols, compactor, preventCollision]
);
const onDragStop = react.useCallback(
(itemId, x, y) => {
const item = chunkBJFPTW5Q_js.getLayoutItem(layout, itemId);
if (!item) return;
const newLayout = chunkBJFPTW5Q_js.moveElement(
layout,
item,
x,
y,
true,
preventCollision,
compactor.type,
cols,
compactor.allowOverlap
);
const compacted = compactor.compact(newLayout, cols);
isDraggingRef.current = false;
setDragState({
activeDrag: null,
oldDragItem: null,
oldLayout: null
});
setLayoutState(compacted);
},
[layout, cols, compactor, preventCollision]
);
const onResizeStart = react.useCallback(
(itemId) => {
const item = chunkBJFPTW5Q_js.getLayoutItem(layout, itemId);
if (!item) return null;
setResizeState({
resizing: true,
oldResizeItem: chunkBJFPTW5Q_js.cloneLayoutItem(item),
oldLayout: chunkBJFPTW5Q_js.cloneLayout(layout)
});
return item;
},
[layout]
);
const onResize = react.useCallback(
(itemId, w, h, x, y) => {
const newLayout = layout.map((item) => {
if (item.i === itemId) {
const updated = {
...item,
w,
h
};
if (x !== void 0) updated.x = x;
if (y !== void 0) updated.y = y;
return updated;
}
return item;
});
const corrected = chunkBJFPTW5Q_js.correctBounds(newLayout, { cols });
const compacted = compactor.compact(corrected, cols);
setLayoutState(compacted);
},
[layout, cols, compactor]
);
const onResizeStop = react.useCallback(
(itemId, w, h) => {
onResize(itemId, w, h);
setResizeState({
resizing: false,
oldResizeItem: null,
oldLayout: null
});
},
[onResize]
);
const onDropDragOver = react.useCallback(
(droppingItem, position) => {
const existingItem = chunkBJFPTW5Q_js.getLayoutItem(layout, droppingItem.i);
if (!existingItem) {
const newLayout = [...layout, droppingItem];
const corrected = chunkBJFPTW5Q_js.correctBounds(newLayout, { cols });
const compacted = compactor.compact(corrected, cols);
setLayoutState(compacted);
}
setDropState({
droppingDOMNode: null,
// Will be set by component
droppingPosition: position
});
},
[layout, cols, compactor]
);
const onDropDragLeave = react.useCallback(() => {
const newLayout = layout.filter((item) => item.i !== "__dropping-elem__");
setLayoutState(newLayout);
setDropState({
droppingDOMNode: null,
droppingPosition: null
});
}, [layout]);
const onDrop = react.useCallback(
(droppingItem) => {
const newLayout = layout.map((item) => {
if (item.i === "__dropping-elem__") {
return {
...item,
i: droppingItem.i,
static: false
};
}
return item;
});
const corrected = chunkBJFPTW5Q_js.correctBounds(newLayout, { cols });
const compacted = compactor.compact(corrected, cols);
setLayoutState(compacted);
setDropState({
droppingDOMNode: null,
droppingPosition: null
});
},
[layout, cols, compactor]
);
const containerHeight = react.useMemo(() => chunkBJFPTW5Q_js.bottom(layout), [layout]);
const isInteracting = dragState.activeDrag !== null || resizeState.resizing || dropState.droppingPosition !== null;
return {
layout,
setLayout,
dragState,
resizeState,
dropState,
onDragStart,
onDrag,
onDragStop,
onResizeStart,
onResize,
onResizeStop,
onDropDragOver,
onDropDragLeave,
onDrop,
containerHeight,
isInteracting,
compactor
};
}
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
};
function useResponsiveLayout(options) {
const {
width,
breakpoints = DEFAULT_BREAKPOINTS,
cols: colsConfig = DEFAULT_COLS,
layouts: propsLayouts = {},
compactor = chunk2DDROKB2_js.verticalCompactor,
onBreakpointChange,
onLayoutChange,
onWidthChange
} = options;
const sortedBreakpoints = react.useMemo(
() => chunk2DDROKB2_js.sortBreakpoints(breakpoints),
[breakpoints]
);
const initialBreakpoint = react.useMemo(
() => chunk2DDROKB2_js.getBreakpointFromWidth(breakpoints, width),
// Only calculate on mount, not on width changes
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
);
const initialCols = react.useMemo(
() => chunk2DDROKB2_js.getColsFromBreakpoint(initialBreakpoint, colsConfig),
[initialBreakpoint, colsConfig]
);
const [breakpoint, setBreakpoint] = react.useState(initialBreakpoint);
const [cols, setCols] = react.useState(initialCols);
const [layouts, setLayoutsState] = react.useState(() => {
const cloned = {};
for (const bp of sortedBreakpoints) {
const layout2 = propsLayouts[bp];
if (layout2) {
cloned[bp] = chunkBJFPTW5Q_js.cloneLayout(layout2);
}
}
return cloned;
});
const prevWidthRef = react.useRef(width);
const prevBreakpointRef = react.useRef(breakpoint);
const prevPropsLayoutsRef = react.useRef(propsLayouts);
const prevLayoutsRef = react.useRef(layouts);
const layout = react.useMemo(() => {
return chunk2DDROKB2_js.findOrGenerateResponsiveLayout(
layouts,
breakpoints,
breakpoint,
prevBreakpointRef.current,
cols,
compactor
);
}, [layouts, breakpoints, breakpoint, cols, compactor]);
const setLayoutForBreakpoint = react.useCallback((bp, newLayout) => {
setLayoutsState((prev) => ({
...prev,
[bp]: chunkBJFPTW5Q_js.cloneLayout(newLayout)
}));
}, []);
const setLayouts = react.useCallback((newLayouts) => {
const cloned = {};
for (const bp of Object.keys(newLayouts)) {
const layoutForBp = newLayouts[bp];
if (layoutForBp) {
cloned[bp] = chunkBJFPTW5Q_js.cloneLayout(layoutForBp);
}
}
setLayoutsState(cloned);
}, []);
react.useEffect(() => {
if (prevWidthRef.current === width) return;
prevWidthRef.current = width;
const newBreakpoint = chunk2DDROKB2_js.getBreakpointFromWidth(breakpoints, width);
const newCols = chunk2DDROKB2_js.getColsFromBreakpoint(newBreakpoint, colsConfig);
onWidthChange?.(width, [10, 10], newCols, null);
if (newBreakpoint !== breakpoint) {
const newLayout = chunk2DDROKB2_js.findOrGenerateResponsiveLayout(
layouts,
breakpoints,
newBreakpoint,
breakpoint,
newCols,
compactor
);
const updatedLayouts = {
...layouts,
[newBreakpoint]: newLayout
};
setLayoutsState(updatedLayouts);
setBreakpoint(newBreakpoint);
setCols(newCols);
onBreakpointChange?.(newBreakpoint, newCols);
prevBreakpointRef.current = newBreakpoint;
}
}, [
width,
breakpoints,
colsConfig,
breakpoint,
layouts,
compactor,
onBreakpointChange,
onWidthChange
]);
react.useEffect(() => {
if (!fastEquals.deepEqual(propsLayouts, prevPropsLayoutsRef.current)) {
setLayouts(propsLayouts);
prevPropsLayoutsRef.current = propsLayouts;
}
}, [propsLayouts, setLayouts]);
react.useEffect(() => {
if (!fastEquals.deepEqual(layouts, prevLayoutsRef.current)) {
prevLayoutsRef.current = layouts;
onLayoutChange?.(layout, layouts);
}
}, [layout, layouts, onLayoutChange]);
return {
layout,
layouts,
breakpoint,
cols,
setLayoutForBreakpoint,
setLayouts,
sortedBreakpoints
};
}
exports.DEFAULT_BREAKPOINTS = DEFAULT_BREAKPOINTS;
exports.DEFAULT_COLS = DEFAULT_COLS;
exports.useContainerWidth = useContainerWidth;
exports.useGridLayout = useGridLayout;
exports.useResponsiveLayout = useResponsiveLayout;
//# sourceMappingURL=chunk-QGXQSZII.js.map
//# sourceMappingURL=chunk-QGXQSZII.js.map