UNPKG

react-grid-layout

Version:

A draggable and resizable grid layout with responsive breakpoints, for React.

431 lines (428 loc) 12.4 kB
'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