/*! * Copyright (c) 2024 PLANKA Software GmbH * Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md */ import React, { useCallback, useRef, useState } from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; import styles from './Masonry.module.scss'; const pixelsToNumber = (pixels) => { return Number(pixels.replace('px', '')); }; const Masonry = React.memo(({ children, columns, spacing }) => { const [maxColumnHeight, setMaxColumnHeight] = useState(0); const wrapperRef = useRef(null); const resizeObserverRef = useRef(null); const handleChildResize = useCallback(() => { const { current: wrapperElement } = wrapperRef; const columnHeights = new Array(columns).fill(0); wrapperElement.childNodes.forEach((childElement) => { if (childElement.nodeType !== Node.ELEMENT_NODE || childElement.dataset.lineBreak) { return; } const childComputedStyle = window.getComputedStyle(childElement); const childHeight = Math.ceil(pixelsToNumber(childComputedStyle.height)) + pixelsToNumber(childComputedStyle.marginTop) + pixelsToNumber(childComputedStyle.marginBottom); const index = columnHeights.indexOf(Math.min(...columnHeights)); columnHeights[index] += childHeight; childElement.style.order = index + 1; // eslint-disable-line no-param-reassign }); ReactDOM.flushSync(() => { setMaxColumnHeight(Math.max(...columnHeights)); }); }, [columns]); const handleWrapperRef = useCallback( (element) => { wrapperRef.current = element; if (resizeObserverRef.current) { resizeObserverRef.current.disconnect(); } if (!element) { resizeObserverRef.current = null; return; } resizeObserverRef.current = new ResizeObserver(handleChildResize); element.childNodes.forEach((childElement) => { if (!childElement.dataset.lineBreak) { resizeObserverRef.current.observe(childElement); } }); }, [children, handleChildResize], // eslint-disable-line react-hooks/exhaustive-deps ); const styledChildren = React.Children.map( children, (child) => child && React.cloneElement(child, { style: { margin: `${spacing / 2}px`, width: `calc(${(100 / columns).toFixed(2)}% - ${spacing}px)`, }, }), ); const lineBreaks = [...Array(columns > 0 ? columns - 1 : 0)].map((_, index) => ( )); return (