UNPKG

@mui/lab

Version:

Laboratory for new Material UI modules.

370 lines (367 loc) 14.1 kB
"use strict"; 'use client'; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseToNumber = exports.getStyle = exports.default = void 0; var _composeClasses = _interopRequireDefault(require("@mui/utils/composeClasses")); var ReactDOM = _interopRequireWildcard(require("react-dom")); var _styles = require("@mui/material/styles"); var _system = require("@mui/system"); var _useForkRef = _interopRequireDefault(require("@mui/utils/useForkRef")); var _useEnhancedEffect = _interopRequireDefault(require("@mui/utils/useEnhancedEffect")); var _deepmerge = _interopRequireDefault(require("@mui/utils/deepmerge")); var _clsx = _interopRequireDefault(require("clsx")); var _propTypes = _interopRequireDefault(require("prop-types")); var React = _interopRequireWildcard(require("react")); var _masonryClasses = require("./masonryClasses"); var _jsxRuntime = require("react/jsx-runtime"); const parseToNumber = val => { return Number(val.replace('px', '')); }; exports.parseToNumber = parseToNumber; const lineBreakStyle = { flexBasis: '100%', width: 0, margin: 0, padding: 0 }; const useUtilityClasses = ownerState => { const { classes } = ownerState; const slots = { root: ['root'] }; return (0, _composeClasses.default)(slots, _masonryClasses.getMasonryUtilityClass, classes); }; const getStyle = ({ ownerState, theme }) => { let styles = { width: '100%', display: 'flex', flexFlow: 'column wrap', alignContent: 'flex-start', boxSizing: 'border-box', '& > *': { boxSizing: 'border-box' } }; const stylesSSR = {}; // Only applicable for Server-Side Rendering if (ownerState.isSSR) { const orderStyleSSR = {}; const defaultSpacing = parseToNumber(theme.spacing(ownerState.defaultSpacing)); for (let i = 1; i <= ownerState.defaultColumns; i += 1) { orderStyleSSR[`&:nth-of-type(${ownerState.defaultColumns}n+${i % ownerState.defaultColumns})`] = { order: i }; } stylesSSR.height = ownerState.defaultHeight; stylesSSR.margin = -(defaultSpacing / 2); stylesSSR['& > *'] = { ...styles['& > *'], ...orderStyleSSR, margin: defaultSpacing / 2, width: `calc(${(100 / ownerState.defaultColumns).toFixed(2)}% - ${defaultSpacing}px)` }; return { ...styles, ...stylesSSR }; } const spacingValues = (0, _system.unstable_resolveBreakpointValues)({ values: ownerState.spacing, breakpoints: theme.breakpoints.values }); const transformer = (0, _system.createUnarySpacing)(theme); const spacingStyleFromPropValue = propValue => { let spacing; // in case of string/number value if (typeof propValue === 'string' && !Number.isNaN(Number(propValue)) || typeof propValue === 'number') { const themeSpacingValue = Number(propValue); spacing = (0, _system.getValue)(transformer, themeSpacingValue); } else { spacing = propValue; } return { margin: `calc(0px - (${spacing} / 2))`, '& > *': { margin: `calc(${spacing} / 2)` }, ...(ownerState.maxColumnHeight && { height: typeof spacing === 'number' ? Math.ceil(ownerState.maxColumnHeight + parseToNumber(spacing)) : `calc(${ownerState.maxColumnHeight}px + ${spacing})` }) }; }; styles = (0, _deepmerge.default)(styles, (0, _system.handleBreakpoints)({ theme }, spacingValues, spacingStyleFromPropValue)); const columnValues = (0, _system.unstable_resolveBreakpointValues)({ values: ownerState.columns, breakpoints: theme.breakpoints.values }); const columnStyleFromPropValue = propValue => { const columnValue = Number(propValue); const width = `${(100 / columnValue).toFixed(2)}%`; const spacing = typeof spacingValues === 'string' && !Number.isNaN(Number(spacingValues)) || typeof spacingValues === 'number' ? (0, _system.getValue)(transformer, Number(spacingValues)) : '0px'; return { '& > *': { width: `calc(${width} - ${spacing})` } }; }; styles = (0, _deepmerge.default)(styles, (0, _system.handleBreakpoints)({ theme }, columnValues, columnStyleFromPropValue)); // configure width for responsive spacing values if (typeof spacingValues === 'object') { styles = (0, _deepmerge.default)(styles, (0, _system.handleBreakpoints)({ theme }, spacingValues, (propValue, breakpoint) => { if (breakpoint) { const themeSpacingValue = Number(propValue); const lastBreakpoint = Object.keys(columnValues).pop(); const spacing = (0, _system.getValue)(transformer, themeSpacingValue); const column = typeof columnValues === 'object' ? columnValues[breakpoint] || columnValues[lastBreakpoint] : columnValues; const width = `${(100 / column).toFixed(2)}%`; return { '& > *': { width: `calc(${width} - ${spacing})` } }; } return null; })); } return styles; }; exports.getStyle = getStyle; const MasonryRoot = (0, _styles.styled)('div', { name: 'MuiMasonry', slot: 'Root' })(getStyle); const Masonry = /*#__PURE__*/React.forwardRef(function Masonry(inProps, ref) { const props = (0, _styles.useThemeProps)({ props: inProps, name: 'MuiMasonry' }); const { children, className, component = 'div', columns = 4, spacing = 1, sequential = false, defaultColumns, defaultHeight, defaultSpacing, ...other } = props; const masonryRef = React.useRef(); const [maxColumnHeight, setMaxColumnHeight] = React.useState(); const isSSR = !maxColumnHeight && defaultHeight && defaultColumns !== undefined && defaultSpacing !== undefined; const [numberOfLineBreaks, setNumberOfLineBreaks] = React.useState(isSSR ? defaultColumns - 1 : 0); const ownerState = { ...props, spacing, columns, maxColumnHeight, defaultColumns, defaultHeight, defaultSpacing, isSSR }; const classes = useUtilityClasses(ownerState); const handleResize = React.useCallback(() => { if (!masonryRef.current) { return; } const masonry = masonryRef.current; const firstVisibleChild = Array.from(masonry.childNodes).find(child => child.nodeType === Node.ELEMENT_NODE && child.dataset.class !== 'line-break' && window.getComputedStyle(child).display !== 'none'); if (!firstVisibleChild) { return; } const parentWidth = masonry.clientWidth; const firstChildWidth = firstVisibleChild.clientWidth; if (parentWidth === 0 || firstChildWidth === 0) { return; } const firstChildComputedStyle = window.getComputedStyle(firstVisibleChild); const firstChildMarginLeft = parseToNumber(firstChildComputedStyle.marginLeft); const firstChildMarginRight = parseToNumber(firstChildComputedStyle.marginRight); const currentNumberOfColumns = Math.round(parentWidth / (firstChildWidth + firstChildMarginLeft + firstChildMarginRight)); const columnHeights = new Array(currentNumberOfColumns).fill(0); let skip = false; let nextOrder = 1; masonry.childNodes.forEach(child => { if (child.nodeType !== Node.ELEMENT_NODE || child.dataset.class === 'line-break' || skip) { return; } const childComputedStyle = window.getComputedStyle(child); if (childComputedStyle.display === 'none') { return; } const childMarginTop = parseToNumber(childComputedStyle.marginTop); const childMarginBottom = parseToNumber(childComputedStyle.marginBottom); const childHeight = parseToNumber(childComputedStyle.height) ? Math.ceil(parseToNumber(childComputedStyle.height)) + childMarginTop + childMarginBottom : 0; if (childHeight === 0) { skip = true; return; } for (let i = 0; i < child.childNodes.length; i += 1) { const nestedChild = child.childNodes[i]; if (nestedChild.tagName === 'IMG' && nestedChild.clientHeight === 0) { skip = true; break; } } if (!skip) { if (sequential) { columnHeights[nextOrder - 1] += childHeight; child.style.order = nextOrder; nextOrder += 1; if (nextOrder > currentNumberOfColumns) { nextOrder = 1; } } else { const currentMinColumnIndex = columnHeights.indexOf(Math.min(...columnHeights)); columnHeights[currentMinColumnIndex] += childHeight; const order = currentMinColumnIndex + 1; child.style.order = order; } } }); if (!skip) { queueMicrotask(() => { if (masonryRef.current) { ReactDOM.flushSync(() => { setMaxColumnHeight(Math.max(...columnHeights)); setNumberOfLineBreaks(currentNumberOfColumns > 0 ? currentNumberOfColumns - 1 : 0); }); } }); } }, [sequential]); (0, _useEnhancedEffect.default)(() => { if (typeof ResizeObserver === 'undefined' || typeof MutationObserver === 'undefined') { return undefined; } const masonry = masonryRef.current; if (!masonry) { return undefined; } let resizeTimeout; const debouncedHandleResize = () => { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(handleResize, 16); // ~60fps }; const resizeObserver = new ResizeObserver(debouncedHandleResize); // Observes for child additions or removals to update the layout. const mutationObserver = new MutationObserver(mutations => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node instanceof HTMLElement && node.dataset.class !== 'line-break') { resizeObserver.observe(node); } }); mutation.removedNodes.forEach(node => { if (node instanceof HTMLElement && node.dataset.class !== 'line-break') { resizeObserver.unobserve(node); } }); }); handleResize(); }); Array.from(masonry.childNodes).forEach(childNode => { if (childNode instanceof HTMLElement && childNode.dataset.class !== 'line-break') { resizeObserver.observe(childNode); } }); mutationObserver.observe(masonry, { childList: true }); handleResize(); return () => { clearTimeout(resizeTimeout); resizeObserver.disconnect(); mutationObserver.disconnect(); }; }, [handleResize, columns, spacing, children]); const handleRef = (0, _useForkRef.default)(ref, masonryRef); // A line break is added to the end of each column to prevent columns from merging. const lineBreaks = new Array(numberOfLineBreaks).fill('').map((_, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { "data-class": "line-break", style: { ...lineBreakStyle, order: index + 1 } }, index)); return /*#__PURE__*/(0, _jsxRuntime.jsxs)(MasonryRoot, { as: component, className: (0, _clsx.default)(classes.root, className), ref: handleRef, ownerState: ownerState, ...other, children: [children, lineBreaks] }); }); process.env.NODE_ENV !== "production" ? Masonry.propTypes /* remove-proptypes */ = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ // └─────────────────────────────────────────────────────────────────────┘ /** * The content of the component. */ children: _propTypes.default /* @typescript-to-proptypes-ignore */.node.isRequired, /** * Override or extend the styles applied to the component. */ classes: _propTypes.default.object, /** * @ignore */ className: _propTypes.default.string, /** * Number of columns. * @default 4 */ columns: _propTypes.default.oneOfType([_propTypes.default.arrayOf(_propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string])), _propTypes.default.number, _propTypes.default.object, _propTypes.default.string]), /** * The component used for the root node. * Either a string to use a HTML element or a component. */ component: _propTypes.default.elementType, /** * The default number of columns of the component. This is provided for server-side rendering. */ defaultColumns: _propTypes.default.number, /** * The default height of the component in px. This is provided for server-side rendering. */ defaultHeight: _propTypes.default.number, /** * The default spacing of the component. Like `spacing`, it is a factor of the theme's spacing. This is provided for server-side rendering. */ defaultSpacing: _propTypes.default.number, /** * Allows using sequential order rather than adding to shortest column * @default false */ sequential: _propTypes.default.bool, /** * Defines the space between children. It is a factor of the theme's spacing. * @default 1 */ spacing: _propTypes.default.oneOfType([_propTypes.default.arrayOf(_propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string])), _propTypes.default.number, _propTypes.default.object, _propTypes.default.string]), /** * Allows defining system overrides as well as additional CSS styles. */ sx: _propTypes.default.oneOfType([_propTypes.default.arrayOf(_propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.object, _propTypes.default.bool])), _propTypes.default.func, _propTypes.default.object]) } : void 0; var _default = exports.default = Masonry;