Comparing version 3.2.0 to 3.3.0
"use strict"; | ||
exports.__esModule = true; | ||
exports.useInfiniteLoader = useInfiniteLoader; | ||
exports.createPositioner = exports.createResizeObserver = exports.useResizeObserver = exports.usePositioner = exports.useContainerPosition = exports.useScroller = exports.List = exports.Masonry = exports.MasonryScroller = exports.useMasonry = void 0; | ||
var React = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("react")); | ||
var _list = /*#__PURE__*/require("./list"); | ||
var _resizeObserverPolyfill = /*#__PURE__*/_interopRequireDefault( /*#__PURE__*/require("resize-observer-polyfill")); | ||
Object.keys(_list).forEach(function (key) { | ||
if (key === "default" || key === "__esModule") return; | ||
exports[key] = _list[key]; | ||
}); | ||
var _trieMemoize = /*#__PURE__*/_interopRequireDefault( /*#__PURE__*/require("trie-memoize")); | ||
var _masonry = /*#__PURE__*/require("./masonry"); | ||
var _oneKeyMap = /*#__PURE__*/_interopRequireDefault( /*#__PURE__*/require("@essentials/one-key-map")); | ||
Object.keys(_masonry).forEach(function (key) { | ||
if (key === "default" || key === "__esModule") return; | ||
exports[key] = _masonry[key]; | ||
}); | ||
var _memoizeOne = /*#__PURE__*/_interopRequireDefault( /*#__PURE__*/require("@essentials/memoize-one")); | ||
var _masonryScroller = /*#__PURE__*/require("./masonry-scroller"); | ||
var _passiveLayoutEffect = /*#__PURE__*/_interopRequireDefault( /*#__PURE__*/require("@react-hook/passive-layout-effect")); | ||
Object.keys(_masonryScroller).forEach(function (key) { | ||
if (key === "default" || key === "__esModule") return; | ||
exports[key] = _masonryScroller[key]; | ||
}); | ||
var _windowScroll = /*#__PURE__*/_interopRequireDefault( /*#__PURE__*/require("@react-hook/window-scroll")); | ||
var _useContainerPosition = /*#__PURE__*/require("./use-container-position"); | ||
var _windowSize = /*#__PURE__*/require("@react-hook/window-size"); | ||
Object.keys(_useContainerPosition).forEach(function (key) { | ||
if (key === "default" || key === "__esModule") return; | ||
exports[key] = _useContainerPosition[key]; | ||
}); | ||
var _latest = /*#__PURE__*/_interopRequireDefault( /*#__PURE__*/require("@react-hook/latest")); | ||
var _useInfiniteLoader = /*#__PURE__*/require("./use-infinite-loader"); | ||
var _requestTimeout = /*#__PURE__*/require("@essentials/request-timeout"); | ||
Object.keys(_useInfiniteLoader).forEach(function (key) { | ||
if (key === "default" || key === "__esModule") return; | ||
exports[key] = _useInfiniteLoader[key]; | ||
}); | ||
var _IntervalTree = /*#__PURE__*/_interopRequireDefault( /*#__PURE__*/require("./IntervalTree")); | ||
var _useMasonry = /*#__PURE__*/require("./use-masonry"); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
Object.keys(_useMasonry).forEach(function (key) { | ||
if (key === "default" || key === "__esModule") return; | ||
exports[key] = _useMasonry[key]; | ||
}); | ||
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } | ||
var _usePositioner = /*#__PURE__*/require("./use-positioner"); | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } | ||
Object.keys(_usePositioner).forEach(function (key) { | ||
if (key === "default" || key === "__esModule") return; | ||
exports[key] = _usePositioner[key]; | ||
}); | ||
const __reactCreateElement__ = React.createElement; | ||
var _useResizeObserver = /*#__PURE__*/require("./use-resize-observer"); | ||
/** | ||
* This hook handles the render phases of the masonry layout and returns the grid as a React element. | ||
* | ||
* @param options Options for configuring the masonry layout renderer. See `UseMasonryOptions`. | ||
*/ | ||
const useMasonry = ({ | ||
// Measurement and layout | ||
positioner, | ||
resizeObserver, | ||
// Grid items | ||
items, | ||
// Container props | ||
as: ContainerComponent = 'div', | ||
id, | ||
className, | ||
style, | ||
role = 'grid', | ||
tabIndex = 0, | ||
containerRef, | ||
// Item props | ||
itemAs: ItemComponent = 'div', | ||
itemStyle, | ||
itemHeightEstimate = 300, | ||
itemKey = defaultGetItemKey, | ||
// Rendering props | ||
overscanBy = 2, | ||
scrollTop, | ||
isScrolling, | ||
height, | ||
render: RenderComponent, | ||
onRender | ||
}) => { | ||
let startIndex = 0; | ||
let stopIndex = void 0; | ||
const forceUpdate = useForceUpdate(); | ||
const setItemRef = getRefSetter(positioner, resizeObserver); | ||
const itemCount = items.length; | ||
const { | ||
columnWidth, | ||
columnCount, | ||
range, | ||
estimateHeight, | ||
size, | ||
shortestColumn | ||
} = positioner; | ||
const measuredCount = size(); | ||
const shortestColumnSize = shortestColumn(); | ||
const children = []; | ||
const itemRole = role + 'item'; | ||
overscanBy = height * overscanBy; | ||
const rangeEnd = scrollTop + overscanBy; | ||
const needsFreshBatch = shortestColumnSize < rangeEnd && measuredCount < itemCount; | ||
range( // We overscan in both directions because users scroll both ways, | ||
// though one must admit scrolling down is more common and thus | ||
// we only overscan by half the downward overscan amount | ||
Math.max(0, scrollTop - overscanBy / 2), rangeEnd, (index, left, top) => { | ||
const data = items[index]; | ||
const key = itemKey(data, index); | ||
const phaseTwoStyle = { | ||
top, | ||
left, | ||
width: columnWidth, | ||
writingMode: 'horizontal-tb', | ||
position: 'absolute' | ||
}; | ||
children.push( /*#__PURE__*/__reactCreateElement__(ItemComponent, { | ||
key: key, | ||
ref: setItemRef(index), | ||
role: itemRole, | ||
style: typeof itemStyle === 'object' && itemStyle !== null ? Object.assign(phaseTwoStyle, itemStyle) : phaseTwoStyle | ||
}, createRenderElement(RenderComponent, index, data, columnWidth))); | ||
Object.keys(_useResizeObserver).forEach(function (key) { | ||
if (key === "default" || key === "__esModule") return; | ||
exports[key] = _useResizeObserver[key]; | ||
}); | ||
if (stopIndex === void 0) { | ||
startIndex = index; | ||
stopIndex = index; | ||
} else { | ||
startIndex = Math.min(startIndex, index); | ||
stopIndex = Math.max(stopIndex, index); | ||
} | ||
}); | ||
var _useScrollToIndex = /*#__PURE__*/require("./use-scroll-to-index"); | ||
if (needsFreshBatch) { | ||
const batchSize = Math.min(itemCount - measuredCount, Math.ceil((scrollTop + overscanBy - shortestColumnSize) / itemHeightEstimate * columnCount)); | ||
let index = measuredCount; | ||
const phaseOneStyle = getCachedSize(columnWidth); | ||
for (; index < measuredCount + batchSize; index++) { | ||
const data = items[index]; | ||
const key = itemKey(data, index); | ||
children.push( /*#__PURE__*/__reactCreateElement__(ItemComponent, { | ||
key: key, | ||
ref: setItemRef(index), | ||
role: itemRole, | ||
style: typeof itemStyle === 'object' ? Object.assign(phaseOneStyle, itemStyle) : phaseOneStyle | ||
}, createRenderElement(RenderComponent, index, data, columnWidth))); | ||
} | ||
} // Calls the onRender callback if the rendered indices changed | ||
React.useEffect(() => { | ||
if (typeof onRender === 'function' && stopIndex !== void 0) onRender(startIndex, stopIndex, items); | ||
didEverMount = '1'; | ||
}, [startIndex, stopIndex, items, onRender]); // If we needed a fresh batch we should reload our components with the measured | ||
// sizes | ||
React.useEffect(() => { | ||
if (needsFreshBatch) forceUpdate(); // eslint-disable-next-line | ||
}, [needsFreshBatch]); // gets the container style object based upon the estimated height and whether or not | ||
// the page is being scrolled | ||
const containerStyle = getContainerStyle(isScrolling, estimateHeight(itemCount, itemHeightEstimate)); | ||
return /*#__PURE__*/__reactCreateElement__(ContainerComponent, { | ||
ref: containerRef, | ||
key: didEverMount, | ||
id: id, | ||
role: role, | ||
className: className, | ||
tabIndex: tabIndex, | ||
style: typeof style === 'object' ? assignUserStyle(containerStyle, style) : containerStyle, | ||
children: children | ||
}); | ||
}; // This is for triggering a remount after SSR has loaded in the client w/ hydrate() | ||
exports.useMasonry = useMasonry; | ||
let didEverMount = '0'; | ||
// | ||
// Render-phase utilities | ||
// ~5.5x faster than createElement without the memo | ||
const createRenderElement = /*#__PURE__*/(0, _trieMemoize.default)([_oneKeyMap.default, {}, WeakMap, _oneKeyMap.default], (RenderComponent, index, data, columnWidth) => /*#__PURE__*/__reactCreateElement__(RenderComponent, { | ||
index: index, | ||
data: data, | ||
width: columnWidth | ||
})); | ||
const getColumns = (width = 0, minimumWidth = 0, gutter = 8, columnCount) => { | ||
columnCount = columnCount || Math.floor(width / (minimumWidth + gutter)) || 1; | ||
const columnWidth = Math.floor((width - gutter * (columnCount - 1)) / columnCount); | ||
return [columnWidth, columnCount]; | ||
}; | ||
const getContainerStyle = /*#__PURE__*/(0, _memoizeOne.default)((isScrolling, estimateHeight) => ({ | ||
position: 'relative', | ||
width: '100%', | ||
maxWidth: '100%', | ||
height: Math.ceil(estimateHeight), | ||
maxHeight: Math.ceil(estimateHeight), | ||
willChange: isScrolling ? 'contents' : void 0, | ||
pointerEvents: isScrolling ? 'none' : void 0 | ||
})); | ||
const cmp2 = (args, pargs) => args[0] === pargs[0] && args[1] === pargs[1]; | ||
const assignUserStyle = /*#__PURE__*/(0, _memoizeOne.default)((containerStyle, userStyle) => Object.assign({}, containerStyle, userStyle), // @ts-ignore | ||
cmp2); | ||
const defaultGetItemKey = (_, i) => i; // the below memoizations for for ensuring shallow equal is reliable for pure | ||
// component children | ||
const getCachedSize = /*#__PURE__*/(0, _memoizeOne.default)(width => ({ | ||
width, | ||
zIndex: -1000, | ||
visibility: 'hidden', | ||
position: 'absolute', | ||
writingMode: 'horizontal-tb' | ||
}), (args, pargs) => args[0] === pargs[0]); | ||
const elementsCache = /*#__PURE__*/new WeakMap(); | ||
const getRefSetter = /*#__PURE__*/(0, _memoizeOne.default)((positioner, resizeObserver) => index => el => { | ||
if (el === null) return; | ||
if (resizeObserver) { | ||
resizeObserver.observe(el); | ||
elementsCache.set(el, index); | ||
} | ||
if (positioner.get(index) === void 0) positioner.set(index, el.offsetHeight); | ||
}, // @ts-ignore | ||
cmp2); // | ||
// Components | ||
/** | ||
* A heavily-optimized component that updates `useMasonry()` when the scroll position of the browser `window` | ||
* changes. This bare-metal component is used by `<Masonry>` under the hood. | ||
*/ | ||
const MasonryScroller = props => { | ||
// We put this in its own layer because it's the thing that will trigger the most updates | ||
// and we don't want to slower ourselves by cycling through all the functions, objects, and effects | ||
// of other hooks | ||
const { | ||
scrollTop, | ||
isScrolling | ||
} = useScroller(props.offset, props.scrollFps); // This is an update-heavy phase and while we could just Object.assign here, | ||
// it is way faster to inline and there's a relatively low hit to he bundle | ||
// size. | ||
return useMasonry({ | ||
scrollTop, | ||
isScrolling, | ||
positioner: props.positioner, | ||
resizeObserver: props.resizeObserver, | ||
items: props.items, | ||
onRender: props.onRender, | ||
as: props.as, | ||
id: props.id, | ||
className: props.className, | ||
style: props.style, | ||
role: props.role, | ||
tabIndex: props.tabIndex, | ||
containerRef: props.containerRef, | ||
itemAs: props.itemAs, | ||
itemStyle: props.itemStyle, | ||
itemHeightEstimate: props.itemHeightEstimate, | ||
itemKey: props.itemKey, | ||
overscanBy: props.overscanBy, | ||
height: props.height, | ||
render: props.render | ||
}); | ||
}; | ||
/** | ||
* A "batteries included" masonry grid which includes all of the implementation details below. This component is the | ||
* easiest way to get off and running in your app, before switching to more advanced implementations, if necessary. | ||
* It will change its column count to fit its container's width and will decide how many rows to render based upon | ||
* the height of the browser `window`. | ||
*/ | ||
exports.MasonryScroller = MasonryScroller; | ||
const Masonry = /*#__PURE__*/React.memo(props => { | ||
const containerRef = React.useRef(null); | ||
const windowSize = (0, _windowSize.useWindowSize)({ | ||
initialWidth: props.ssrWidth, | ||
initialHeight: props.ssrHeight | ||
}); | ||
const containerPos = useContainerPosition(containerRef, windowSize); | ||
const nextProps = Object.assign({ | ||
offset: containerPos.offset, | ||
width: containerPos.width || windowSize[0], | ||
height: windowSize[1], | ||
containerRef | ||
}, props); | ||
nextProps.positioner = usePositioner(nextProps); | ||
nextProps.resizeObserver = useResizeObserver(nextProps.positioner); | ||
return __reactCreateElement__(MasonryScroller, nextProps); | ||
Object.keys(_useScrollToIndex).forEach(function (key) { | ||
if (key === "default" || key === "__esModule") return; | ||
exports[key] = _useScrollToIndex[key]; | ||
}); | ||
exports.Masonry = Masonry; | ||
/** | ||
* This is just a single-column `<Masonry>` component with `rowGutter` prop instead of | ||
* a `columnGutter` prop. | ||
*/ | ||
const List = props => __reactCreateElement__(Masonry, Object.assign({ | ||
role: 'list' | ||
}, props, { | ||
columnGutter: props.rowGutter, | ||
columnCount: 1, | ||
columnWidth: 1 | ||
})); | ||
var _useScroller = /*#__PURE__*/require("./use-scroller"); | ||
exports.List = List; | ||
const emptyObj = {}; | ||
const emptyArr = []; // | ||
// Hooks | ||
const useForceUpdate = () => { | ||
const setState = React.useState(emptyObj)[1]; | ||
return React.useRef(() => setState({})).current; | ||
}; | ||
/** | ||
* A hook for tracking whether the `window` is currently being scrolled and it's scroll position on | ||
* the y-axis. These values are used for determining which grid cells to render and when | ||
* to add styles to the masonry container that maximize scroll performance. | ||
* | ||
* @param offset The vertical space in pixels between the top of the grid container and the top | ||
* of the browser `document.documentElement`. | ||
* @param fps This determines how often (in frames per second) to update the scroll position of the | ||
* browser `window` in state, and as a result the rate the masonry grid recalculates its visible cells. | ||
* The default value of `12` has been very reasonable in my own testing, but if you have particularly | ||
* heavy `render` components it may be prudent to reduce this number. | ||
*/ | ||
const useScroller = (offset = 0, fps = 12) => { | ||
const scrollTop = (0, _windowScroll.default)(fps); | ||
const [isScrolling, setIsScrolling] = React.useState(false); | ||
const didMount = React.useRef(0); | ||
function _ref() { | ||
// This is here to prevent premature bail outs while maintaining high resolution | ||
// unsets. Without it there will always bee a lot of unnecessary DOM writes to style. | ||
setIsScrolling(false); | ||
} | ||
React.useEffect(() => { | ||
if (didMount.current === 1) setIsScrolling(true); | ||
const to = (0, _requestTimeout.requestTimeout)(_ref, 40 + 1000 / fps); | ||
didMount.current = 1; | ||
return () => (0, _requestTimeout.clearRequestTimeout)(to); | ||
}, [fps, scrollTop]); | ||
return { | ||
scrollTop: Math.max(0, scrollTop - offset), | ||
isScrolling | ||
}; | ||
}; | ||
/** | ||
* A hook for measuring the width of the grid container, as well as its distance | ||
* from the top of the document. These values are necessary to correctly calculate the number/width | ||
* of columns to render, as well as the number of rows to render. | ||
* | ||
* @param elementRef A `ref` object created by `React.useRef()`. That ref should be provided to the | ||
* `containerRef` property in `useMasonry()`. | ||
* @param deps You can force this hook to recalculate the `offset` and `width` whenever this | ||
* dependencies list changes. A common dependencies list might look like `[windowWidth, windowHeight]`, | ||
* which would force the hook to recalculate any time the size of the browser `window` changed. | ||
*/ | ||
exports.useScroller = useScroller; | ||
const useContainerPosition = (elementRef, deps = emptyArr) => { | ||
const [containerPosition, setContainerPosition] = React.useState({ | ||
offset: 0, | ||
width: 0 | ||
}); | ||
(0, _passiveLayoutEffect.default)(() => { | ||
const { | ||
current | ||
} = elementRef; | ||
if (current !== null) { | ||
let offset = 0; | ||
let el = current; | ||
do { | ||
offset += el.offsetTop || 0; | ||
el = el.offsetParent; | ||
} while (el); | ||
if (offset !== containerPosition.offset || current.offsetWidth !== containerPosition.width) { | ||
setContainerPosition({ | ||
offset, | ||
width: current.offsetWidth | ||
}); | ||
} | ||
} // eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, deps); | ||
return containerPosition; | ||
}; | ||
exports.useContainerPosition = useContainerPosition; | ||
/** | ||
* This hook creates the grid cell positioner and cache required by `useMasonry()`. This is | ||
* the meat of the grid's layout algorithm, determining which cells to render at a given scroll | ||
* position, as well as where to place new items in the grid. | ||
* | ||
* @param options Properties that determine the number of columns in the grid, as well | ||
* as their widths. | ||
* @param deps This hook will create a new positioner, clearing all existing cached positions, | ||
* whenever the dependencies in this list change. | ||
*/ | ||
const usePositioner = ({ | ||
width, | ||
columnWidth = 200, | ||
columnGutter = 0, | ||
columnCount | ||
}, deps = emptyArr) => { | ||
const initPositioner = () => { | ||
const [computedColumnWidth, computedColumnCount] = getColumns(width, columnWidth, columnGutter, columnCount); | ||
return createPositioner(computedColumnCount, computedColumnWidth, columnGutter); | ||
}; | ||
const [positioner, setPositioner] = React.useState(initPositioner); | ||
const didMount = React.useRef(0); // Create a new positioner when the dependencies change | ||
React.useEffect(() => { | ||
if (didMount.current) setPositioner(initPositioner()); | ||
didMount.current = 1; // eslint-disable-next-line | ||
}, deps); // Updates the item positions any time a prop potentially affecting their | ||
// size changes | ||
(0, _passiveLayoutEffect.default)(() => { | ||
if (didMount.current) { | ||
const cacheSize = positioner.size(); | ||
const nextPositioner = initPositioner(); | ||
let index = 0; | ||
for (; index < cacheSize; index++) { | ||
const pos = positioner.get(index); | ||
nextPositioner.set(index, pos !== void 0 ? pos.height : 0); | ||
} | ||
setPositioner(nextPositioner); | ||
} // eslint-disable-next-line | ||
}, [width, columnWidth, columnGutter, columnCount]); | ||
return positioner; | ||
}; | ||
exports.usePositioner = usePositioner; | ||
/** | ||
* Creates a resize observer that forces updates to the grid cell positions when mutations are | ||
* made to cells affecting their height. | ||
* | ||
* @param positioner The masonry cell positioner created by the `usePositioner()` hook. | ||
*/ | ||
const useResizeObserver = positioner => { | ||
const forceUpdate = useForceUpdate(); | ||
const resizeObserver = createResizeObserver(positioner, forceUpdate); // Cleans up the resize observers when they change or the | ||
// component unmounts | ||
function _ref2() { | ||
return resizeObserver.disconnect(); | ||
} | ||
React.useEffect(() => _ref2, [resizeObserver]); | ||
return resizeObserver; | ||
}; | ||
/** | ||
* Creates a resize observer that fires an `updater` callback whenever the height of | ||
* one or many cells change. The `useResizeObserver()` hook is using this under the hood. | ||
* | ||
* @param positioner A cell positioner created by the `usePositioner()` hook or the `createPositioner()` utility | ||
* @param updater A callback that fires whenever one or many cell heights change. | ||
*/ | ||
exports.useResizeObserver = useResizeObserver; | ||
const createResizeObserver = /*#__PURE__*/(0, _trieMemoize.default)([WeakMap], // TODO: figure out a way to test this | ||
/* istanbul ignore next */ | ||
(positioner, updater) => new _resizeObserverPolyfill.default(entries => { | ||
const updates = []; | ||
let i = 0; | ||
for (; i < entries.length; i++) { | ||
const entry = entries[i]; // There are native resize observers that still don't have | ||
// the borderBoxSize property. For those we fallback to the | ||
// offset height of the target element. | ||
const height = entry.borderBoxSize !== void 0 ? entry.borderBoxSize.blockSize : entry.target.offsetHeight; | ||
if (height > 0) { | ||
const index = elementsCache.get(entry.target); | ||
if (index !== void 0) { | ||
const position = positioner.get(index); | ||
if (position !== void 0 && height !== position.height) updates.push(index, height); | ||
} | ||
} | ||
} | ||
if (updates.length > 0) { | ||
// Updates the size/positions of the cell with the resize | ||
// observer updates | ||
positioner.update(updates); | ||
updater(updates); | ||
} | ||
})); | ||
exports.createResizeObserver = createResizeObserver; | ||
/** | ||
* A utility hook for seamlessly adding infinite scroll behavior to the `useMasonry()` hook. This | ||
* hook invokes a callback each time the last rendered index surpasses the total number of items | ||
* in your items array or the number defined in the `totalItems` option. | ||
* | ||
* @param loadMoreItems This callback is invoked when more rows must be loaded. It will be used to | ||
* determine when to refresh the list with the newly-loaded data. This callback may be called multiple | ||
* times in reaction to a single scroll event, so it's important to memoize its arguments. If you're | ||
* creating this callback inside of a functional component, make sure you wrap it in `React.useCallback()`, | ||
* as well. | ||
* @param options | ||
*/ | ||
function useInfiniteLoader(loadMoreItems, options = emptyObj) { | ||
const { | ||
isItemLoaded, | ||
minimumBatchSize = 16, | ||
threshold = 16, | ||
totalItems = 9e9 | ||
} = options; | ||
const storedLoadMoreItems = (0, _latest.default)(loadMoreItems); | ||
const storedIsItemLoaded = (0, _latest.default)(isItemLoaded); | ||
return React.useCallback((startIndex, stopIndex, items) => { | ||
const unloadedRanges = scanForUnloadedRanges(storedIsItemLoaded.current, minimumBatchSize, items, totalItems, Math.max(0, startIndex - threshold), Math.min(totalItems - 1, (stopIndex || 0) + threshold)); // The user is responsible for memoizing their loadMoreItems() function | ||
// because we don't want to make assumptions about how they want to deal | ||
// with `items` | ||
for (let i = 0; i < unloadedRanges.length - 1; ++i) storedLoadMoreItems.current(unloadedRanges[i], unloadedRanges[++i], items); | ||
}, [totalItems, minimumBatchSize, threshold, storedLoadMoreItems, storedIsItemLoaded]); | ||
} | ||
/** | ||
* Returns all of the ranges within a larger range that contain unloaded rows. | ||
*/ | ||
const scanForUnloadedRanges = (isItemLoaded = defaultIsItemLoaded, minimumBatchSize = 16, items, totalItems = 9e9, startIndex, stopIndex) => { | ||
const unloadedRanges = []; | ||
let rangeStartIndex, | ||
rangeStopIndex, | ||
index = startIndex; | ||
for (; index <= stopIndex; index++) { | ||
if (!isItemLoaded(index, items)) { | ||
rangeStopIndex = index; | ||
if (rangeStartIndex === void 0) rangeStartIndex = index; | ||
} else if (rangeStartIndex !== void 0 && rangeStopIndex !== void 0) { | ||
unloadedRanges.push(rangeStartIndex, rangeStopIndex); | ||
rangeStartIndex = rangeStopIndex = void 0; | ||
} | ||
} // If :rangeStopIndex is not null it means we haven't run out of unloaded rows. | ||
// Scan forward to try filling our :minimumBatchSize. | ||
if (rangeStartIndex !== void 0 && rangeStopIndex !== void 0) { | ||
const potentialStopIndex = Math.min(Math.max(rangeStopIndex, rangeStartIndex + minimumBatchSize - 1), totalItems - 1); | ||
for (index = rangeStopIndex + 1; index <= potentialStopIndex; index++) { | ||
if (!isItemLoaded(index, items)) { | ||
rangeStopIndex = index; | ||
} else { | ||
break; | ||
} | ||
} | ||
unloadedRanges.push(rangeStartIndex, rangeStopIndex); | ||
} // Check to see if our first range ended prematurely. | ||
// In this case we should scan backwards to try filling our :minimumBatchSize. | ||
/* istanbul ignore next */ | ||
if (unloadedRanges.length) { | ||
let firstUnloadedStart = unloadedRanges[0]; | ||
const firstUnloadedStop = unloadedRanges[1]; | ||
while (firstUnloadedStop - firstUnloadedStart + 1 < minimumBatchSize && firstUnloadedStart > 0) { | ||
const index = firstUnloadedStart - 1; | ||
if (!isItemLoaded(index, items)) { | ||
unloadedRanges[0] = firstUnloadedStart = index; | ||
} else { | ||
break; | ||
} | ||
} | ||
} | ||
return unloadedRanges; | ||
}; | ||
const defaultIsItemLoaded = (index, items) => items[index] !== void 0; | ||
// | ||
// Utilities | ||
/** | ||
* Creates a cell positioner for the `useMasonry()` hook. The `usePositioner()` hook uses | ||
* this utility under the hood. | ||
* | ||
* @param columnCount The number of columns in the grid | ||
* @param columnWidth The width of each column in the grid | ||
* @param columnGutter The amount of horizontal and vertical space in pixels to render | ||
* between each grid item. | ||
*/ | ||
const createPositioner = (columnCount, columnWidth, columnGutter = 0) => { | ||
// O(log(n)) lookup of cells to render for a given viewport size | ||
// Store tops and bottoms of each cell for fast intersection lookup. | ||
const intervalTree = (0, _IntervalTree.default)(); // Track the height of each column. | ||
// Layout algorithm below always inserts into the shortest column. | ||
const columnHeights = new Array(columnCount); // Used for O(1) item access | ||
const items = []; // Tracks the item indexes within an individual column | ||
const columnItems = new Array(columnCount); | ||
for (let i = 0; i < columnCount; i++) { | ||
columnHeights[i] = 0; | ||
columnItems[i] = []; | ||
} | ||
return { | ||
columnCount, | ||
columnWidth, | ||
set: (index, height = 0) => { | ||
let column = 0; // finds the shortest column and uses it | ||
for (let i = 1; i < columnHeights.length; i++) { | ||
if (columnHeights[i] < columnHeights[column]) column = i; | ||
} | ||
const top = columnHeights[column] || 0; | ||
columnHeights[column] = top + height + columnGutter; | ||
columnItems[column].push(index); | ||
items[index] = { | ||
left: column * (columnWidth + columnGutter), | ||
top, | ||
height, | ||
column | ||
}; | ||
intervalTree.insert(top, top + height, index); | ||
}, | ||
get: index => items[index], | ||
// This only updates items in the specific columns that have changed, on and after the | ||
// specific items that have changed | ||
update: updates => { | ||
const columns = new Array(columnCount); | ||
let i = 0, | ||
j = 0; // determines which columns have items that changed, as well as the minimum index | ||
// changed in that column, as all items after that index will have their positions | ||
// affected by the change | ||
for (; i < updates.length - 1; i++) { | ||
const index = updates[i]; | ||
const item = items[index]; | ||
item.height = updates[++i]; | ||
intervalTree.remove(index); | ||
intervalTree.insert(item.top, item.top + item.height, index); | ||
columns[item.column] = columns[item.column] === void 0 ? index : Math.min(index, columns[item.column]); | ||
} | ||
for (i = 0; i < columns.length; i++) { | ||
// bails out if the column didn't change | ||
if (columns[i] === void 0) continue; | ||
const itemsInColumn = columnItems[i]; // the index order is sorted with certainty so binary search is a great solution | ||
// here as opposed to Array.indexOf() | ||
const startIndex = binarySearch(itemsInColumn, columns[i]); | ||
const index = columnItems[i][startIndex]; | ||
const startItem = items[index]; | ||
columnHeights[i] = startItem.top + startItem.height + columnGutter; | ||
for (j = startIndex + 1; j < itemsInColumn.length; j++) { | ||
const index = itemsInColumn[j]; | ||
const item = items[index]; | ||
item.top = columnHeights[i]; | ||
columnHeights[i] = item.top + item.height + columnGutter; | ||
intervalTree.remove(index); | ||
intervalTree.insert(item.top, item.top + item.height, index); | ||
} | ||
} | ||
}, | ||
// Render all cells visible within the viewport range defined. | ||
range: (lo, hi, renderCallback) => intervalTree.search(lo, hi, (index, top) => renderCallback(index, items[index].left, top)), | ||
estimateHeight: (itemCount, defaultItemHeight) => { | ||
const tallestColumn = Math.max(0, Math.max.apply(null, columnHeights)); | ||
return itemCount === intervalTree.size ? tallestColumn : tallestColumn + Math.ceil((itemCount - intervalTree.size) / columnCount) * defaultItemHeight; | ||
}, | ||
shortestColumn: () => { | ||
if (columnHeights.length > 1) return Math.min.apply(null, columnHeights); | ||
return columnHeights[0] || 0; | ||
}, | ||
size() { | ||
return intervalTree.size; | ||
} | ||
}; | ||
}; | ||
exports.createPositioner = createPositioner; | ||
/* istanbul ignore next */ | ||
const binarySearch = (a, y) => { | ||
let l = 0; | ||
let h = a.length - 1; | ||
while (l <= h) { | ||
const m = l + h >>> 1; | ||
const x = a[m]; | ||
if (x === y) return m;else if (x <= y) l = m + 1;else h = m - 1; | ||
} | ||
return -1; | ||
}; | ||
if (typeof process !== 'undefined' && process.env.NODE_ENV !== 'production') { | ||
Masonry.displayName = 'Masonry'; | ||
MasonryScroller.displayName = 'MasonryScroller'; | ||
List.displayName = 'List'; | ||
} | ||
Object.keys(_useScroller).forEach(function (key) { | ||
if (key === "default" || key === "__esModule") return; | ||
exports[key] = _useScroller[key]; | ||
}); |
@@ -1,676 +0,10 @@ | ||
import * as React from 'react'; | ||
const __reactCreateElement__ = React.createElement; | ||
import ResizeObserver from 'resize-observer-polyfill'; | ||
import trieMemoize from 'trie-memoize'; | ||
import OneKeyMap from '@essentials/one-key-map'; | ||
import memoizeOne from '@essentials/memoize-one'; | ||
import useLayoutEffect from '@react-hook/passive-layout-effect'; | ||
import useScrollPosition from '@react-hook/window-scroll'; | ||
import { useWindowSize } from '@react-hook/window-size'; | ||
import useLatest from '@react-hook/latest'; | ||
import { requestTimeout, clearRequestTimeout } from '@essentials/request-timeout'; | ||
import createIntervalTree from './IntervalTree'; | ||
/** | ||
* This hook handles the render phases of the masonry layout and returns the grid as a React element. | ||
* | ||
* @param options Options for configuring the masonry layout renderer. See `UseMasonryOptions`. | ||
*/ | ||
export const useMasonry = ({ | ||
// Measurement and layout | ||
positioner, | ||
resizeObserver, | ||
// Grid items | ||
items, | ||
// Container props | ||
as: ContainerComponent = 'div', | ||
id, | ||
className, | ||
style, | ||
role = 'grid', | ||
tabIndex = 0, | ||
containerRef, | ||
// Item props | ||
itemAs: ItemComponent = 'div', | ||
itemStyle, | ||
itemHeightEstimate = 300, | ||
itemKey = defaultGetItemKey, | ||
// Rendering props | ||
overscanBy = 2, | ||
scrollTop, | ||
isScrolling, | ||
height, | ||
render: RenderComponent, | ||
onRender | ||
}) => { | ||
let startIndex = 0; | ||
let stopIndex = void 0; | ||
const forceUpdate = useForceUpdate(); | ||
const setItemRef = getRefSetter(positioner, resizeObserver); | ||
const itemCount = items.length; | ||
const { | ||
columnWidth, | ||
columnCount, | ||
range, | ||
estimateHeight, | ||
size, | ||
shortestColumn | ||
} = positioner; | ||
const measuredCount = size(); | ||
const shortestColumnSize = shortestColumn(); | ||
const children = []; | ||
const itemRole = role + 'item'; | ||
overscanBy = height * overscanBy; | ||
const rangeEnd = scrollTop + overscanBy; | ||
const needsFreshBatch = shortestColumnSize < rangeEnd && measuredCount < itemCount; | ||
range( // We overscan in both directions because users scroll both ways, | ||
// though one must admit scrolling down is more common and thus | ||
// we only overscan by half the downward overscan amount | ||
Math.max(0, scrollTop - overscanBy / 2), rangeEnd, (index, left, top) => { | ||
const data = items[index]; | ||
const key = itemKey(data, index); | ||
const phaseTwoStyle = { | ||
top, | ||
left, | ||
width: columnWidth, | ||
writingMode: 'horizontal-tb', | ||
position: 'absolute' | ||
}; | ||
children.push( /*#__PURE__*/__reactCreateElement__(ItemComponent, { | ||
key: key, | ||
ref: setItemRef(index), | ||
role: itemRole, | ||
style: typeof itemStyle === 'object' && itemStyle !== null ? Object.assign(phaseTwoStyle, itemStyle) : phaseTwoStyle | ||
}, createRenderElement(RenderComponent, index, data, columnWidth))); | ||
if (stopIndex === void 0) { | ||
startIndex = index; | ||
stopIndex = index; | ||
} else { | ||
startIndex = Math.min(startIndex, index); | ||
stopIndex = Math.max(stopIndex, index); | ||
} | ||
}); | ||
if (needsFreshBatch) { | ||
const batchSize = Math.min(itemCount - measuredCount, Math.ceil((scrollTop + overscanBy - shortestColumnSize) / itemHeightEstimate * columnCount)); | ||
let index = measuredCount; | ||
const phaseOneStyle = getCachedSize(columnWidth); | ||
for (; index < measuredCount + batchSize; index++) { | ||
const data = items[index]; | ||
const key = itemKey(data, index); | ||
children.push( /*#__PURE__*/__reactCreateElement__(ItemComponent, { | ||
key: key, | ||
ref: setItemRef(index), | ||
role: itemRole, | ||
style: typeof itemStyle === 'object' ? Object.assign(phaseOneStyle, itemStyle) : phaseOneStyle | ||
}, createRenderElement(RenderComponent, index, data, columnWidth))); | ||
} | ||
} // Calls the onRender callback if the rendered indices changed | ||
React.useEffect(() => { | ||
if (typeof onRender === 'function' && stopIndex !== void 0) onRender(startIndex, stopIndex, items); | ||
didEverMount = '1'; | ||
}, [startIndex, stopIndex, items, onRender]); // If we needed a fresh batch we should reload our components with the measured | ||
// sizes | ||
React.useEffect(() => { | ||
if (needsFreshBatch) forceUpdate(); // eslint-disable-next-line | ||
}, [needsFreshBatch]); // gets the container style object based upon the estimated height and whether or not | ||
// the page is being scrolled | ||
const containerStyle = getContainerStyle(isScrolling, estimateHeight(itemCount, itemHeightEstimate)); | ||
return /*#__PURE__*/__reactCreateElement__(ContainerComponent, { | ||
ref: containerRef, | ||
key: didEverMount, | ||
id: id, | ||
role: role, | ||
className: className, | ||
tabIndex: tabIndex, | ||
style: typeof style === 'object' ? assignUserStyle(containerStyle, style) : containerStyle, | ||
children: children | ||
}); | ||
}; // This is for triggering a remount after SSR has loaded in the client w/ hydrate() | ||
let didEverMount = '0'; | ||
// | ||
// Render-phase utilities | ||
// ~5.5x faster than createElement without the memo | ||
const createRenderElement = /*#__PURE__*/trieMemoize([OneKeyMap, {}, WeakMap, OneKeyMap], (RenderComponent, index, data, columnWidth) => /*#__PURE__*/__reactCreateElement__(RenderComponent, { | ||
index: index, | ||
data: data, | ||
width: columnWidth | ||
})); | ||
const getColumns = (width = 0, minimumWidth = 0, gutter = 8, columnCount) => { | ||
columnCount = columnCount || Math.floor(width / (minimumWidth + gutter)) || 1; | ||
const columnWidth = Math.floor((width - gutter * (columnCount - 1)) / columnCount); | ||
return [columnWidth, columnCount]; | ||
}; | ||
const getContainerStyle = /*#__PURE__*/memoizeOne((isScrolling, estimateHeight) => ({ | ||
position: 'relative', | ||
width: '100%', | ||
maxWidth: '100%', | ||
height: Math.ceil(estimateHeight), | ||
maxHeight: Math.ceil(estimateHeight), | ||
willChange: isScrolling ? 'contents' : void 0, | ||
pointerEvents: isScrolling ? 'none' : void 0 | ||
})); | ||
const cmp2 = (args, pargs) => args[0] === pargs[0] && args[1] === pargs[1]; | ||
const assignUserStyle = /*#__PURE__*/memoizeOne((containerStyle, userStyle) => Object.assign({}, containerStyle, userStyle), // @ts-ignore | ||
cmp2); | ||
const defaultGetItemKey = (_, i) => i; // the below memoizations for for ensuring shallow equal is reliable for pure | ||
// component children | ||
const getCachedSize = /*#__PURE__*/memoizeOne(width => ({ | ||
width, | ||
zIndex: -1000, | ||
visibility: 'hidden', | ||
position: 'absolute', | ||
writingMode: 'horizontal-tb' | ||
}), (args, pargs) => args[0] === pargs[0]); | ||
const elementsCache = /*#__PURE__*/new WeakMap(); | ||
const getRefSetter = /*#__PURE__*/memoizeOne((positioner, resizeObserver) => index => el => { | ||
if (el === null) return; | ||
if (resizeObserver) { | ||
resizeObserver.observe(el); | ||
elementsCache.set(el, index); | ||
} | ||
if (positioner.get(index) === void 0) positioner.set(index, el.offsetHeight); | ||
}, // @ts-ignore | ||
cmp2); // | ||
// Components | ||
/** | ||
* A heavily-optimized component that updates `useMasonry()` when the scroll position of the browser `window` | ||
* changes. This bare-metal component is used by `<Masonry>` under the hood. | ||
*/ | ||
export const MasonryScroller = props => { | ||
// We put this in its own layer because it's the thing that will trigger the most updates | ||
// and we don't want to slower ourselves by cycling through all the functions, objects, and effects | ||
// of other hooks | ||
const { | ||
scrollTop, | ||
isScrolling | ||
} = useScroller(props.offset, props.scrollFps); // This is an update-heavy phase and while we could just Object.assign here, | ||
// it is way faster to inline and there's a relatively low hit to he bundle | ||
// size. | ||
return useMasonry({ | ||
scrollTop, | ||
isScrolling, | ||
positioner: props.positioner, | ||
resizeObserver: props.resizeObserver, | ||
items: props.items, | ||
onRender: props.onRender, | ||
as: props.as, | ||
id: props.id, | ||
className: props.className, | ||
style: props.style, | ||
role: props.role, | ||
tabIndex: props.tabIndex, | ||
containerRef: props.containerRef, | ||
itemAs: props.itemAs, | ||
itemStyle: props.itemStyle, | ||
itemHeightEstimate: props.itemHeightEstimate, | ||
itemKey: props.itemKey, | ||
overscanBy: props.overscanBy, | ||
height: props.height, | ||
render: props.render | ||
}); | ||
}; | ||
/** | ||
* A "batteries included" masonry grid which includes all of the implementation details below. This component is the | ||
* easiest way to get off and running in your app, before switching to more advanced implementations, if necessary. | ||
* It will change its column count to fit its container's width and will decide how many rows to render based upon | ||
* the height of the browser `window`. | ||
*/ | ||
export const Masonry = /*#__PURE__*/React.memo(props => { | ||
const containerRef = React.useRef(null); | ||
const windowSize = useWindowSize({ | ||
initialWidth: props.ssrWidth, | ||
initialHeight: props.ssrHeight | ||
}); | ||
const containerPos = useContainerPosition(containerRef, windowSize); | ||
const nextProps = Object.assign({ | ||
offset: containerPos.offset, | ||
width: containerPos.width || windowSize[0], | ||
height: windowSize[1], | ||
containerRef | ||
}, props); | ||
nextProps.positioner = usePositioner(nextProps); | ||
nextProps.resizeObserver = useResizeObserver(nextProps.positioner); | ||
return __reactCreateElement__(MasonryScroller, nextProps); | ||
}); | ||
/** | ||
* This is just a single-column `<Masonry>` component with `rowGutter` prop instead of | ||
* a `columnGutter` prop. | ||
*/ | ||
export const List = props => __reactCreateElement__(Masonry, Object.assign({ | ||
role: 'list' | ||
}, props, { | ||
columnGutter: props.rowGutter, | ||
columnCount: 1, | ||
columnWidth: 1 | ||
})); | ||
const emptyObj = {}; | ||
const emptyArr = []; // | ||
// Hooks | ||
const useForceUpdate = () => { | ||
const setState = React.useState(emptyObj)[1]; | ||
return React.useRef(() => setState({})).current; | ||
}; | ||
/** | ||
* A hook for tracking whether the `window` is currently being scrolled and it's scroll position on | ||
* the y-axis. These values are used for determining which grid cells to render and when | ||
* to add styles to the masonry container that maximize scroll performance. | ||
* | ||
* @param offset The vertical space in pixels between the top of the grid container and the top | ||
* of the browser `document.documentElement`. | ||
* @param fps This determines how often (in frames per second) to update the scroll position of the | ||
* browser `window` in state, and as a result the rate the masonry grid recalculates its visible cells. | ||
* The default value of `12` has been very reasonable in my own testing, but if you have particularly | ||
* heavy `render` components it may be prudent to reduce this number. | ||
*/ | ||
export const useScroller = (offset = 0, fps = 12) => { | ||
const scrollTop = useScrollPosition(fps); | ||
const [isScrolling, setIsScrolling] = React.useState(false); | ||
const didMount = React.useRef(0); | ||
function _ref() { | ||
// This is here to prevent premature bail outs while maintaining high resolution | ||
// unsets. Without it there will always bee a lot of unnecessary DOM writes to style. | ||
setIsScrolling(false); | ||
} | ||
React.useEffect(() => { | ||
if (didMount.current === 1) setIsScrolling(true); | ||
const to = requestTimeout(_ref, 40 + 1000 / fps); | ||
didMount.current = 1; | ||
return () => clearRequestTimeout(to); | ||
}, [fps, scrollTop]); | ||
return { | ||
scrollTop: Math.max(0, scrollTop - offset), | ||
isScrolling | ||
}; | ||
}; | ||
/** | ||
* A hook for measuring the width of the grid container, as well as its distance | ||
* from the top of the document. These values are necessary to correctly calculate the number/width | ||
* of columns to render, as well as the number of rows to render. | ||
* | ||
* @param elementRef A `ref` object created by `React.useRef()`. That ref should be provided to the | ||
* `containerRef` property in `useMasonry()`. | ||
* @param deps You can force this hook to recalculate the `offset` and `width` whenever this | ||
* dependencies list changes. A common dependencies list might look like `[windowWidth, windowHeight]`, | ||
* which would force the hook to recalculate any time the size of the browser `window` changed. | ||
*/ | ||
export const useContainerPosition = (elementRef, deps = emptyArr) => { | ||
const [containerPosition, setContainerPosition] = React.useState({ | ||
offset: 0, | ||
width: 0 | ||
}); | ||
useLayoutEffect(() => { | ||
const { | ||
current | ||
} = elementRef; | ||
if (current !== null) { | ||
let offset = 0; | ||
let el = current; | ||
do { | ||
offset += el.offsetTop || 0; | ||
el = el.offsetParent; | ||
} while (el); | ||
if (offset !== containerPosition.offset || current.offsetWidth !== containerPosition.width) { | ||
setContainerPosition({ | ||
offset, | ||
width: current.offsetWidth | ||
}); | ||
} | ||
} // eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, deps); | ||
return containerPosition; | ||
}; | ||
/** | ||
* This hook creates the grid cell positioner and cache required by `useMasonry()`. This is | ||
* the meat of the grid's layout algorithm, determining which cells to render at a given scroll | ||
* position, as well as where to place new items in the grid. | ||
* | ||
* @param options Properties that determine the number of columns in the grid, as well | ||
* as their widths. | ||
* @param deps This hook will create a new positioner, clearing all existing cached positions, | ||
* whenever the dependencies in this list change. | ||
*/ | ||
export const usePositioner = ({ | ||
width, | ||
columnWidth = 200, | ||
columnGutter = 0, | ||
columnCount | ||
}, deps = emptyArr) => { | ||
const initPositioner = () => { | ||
const [computedColumnWidth, computedColumnCount] = getColumns(width, columnWidth, columnGutter, columnCount); | ||
return createPositioner(computedColumnCount, computedColumnWidth, columnGutter); | ||
}; | ||
const [positioner, setPositioner] = React.useState(initPositioner); | ||
const didMount = React.useRef(0); // Create a new positioner when the dependencies change | ||
React.useEffect(() => { | ||
if (didMount.current) setPositioner(initPositioner()); | ||
didMount.current = 1; // eslint-disable-next-line | ||
}, deps); // Updates the item positions any time a prop potentially affecting their | ||
// size changes | ||
useLayoutEffect(() => { | ||
if (didMount.current) { | ||
const cacheSize = positioner.size(); | ||
const nextPositioner = initPositioner(); | ||
let index = 0; | ||
for (; index < cacheSize; index++) { | ||
const pos = positioner.get(index); | ||
nextPositioner.set(index, pos !== void 0 ? pos.height : 0); | ||
} | ||
setPositioner(nextPositioner); | ||
} // eslint-disable-next-line | ||
}, [width, columnWidth, columnGutter, columnCount]); | ||
return positioner; | ||
}; | ||
/** | ||
* Creates a resize observer that forces updates to the grid cell positions when mutations are | ||
* made to cells affecting their height. | ||
* | ||
* @param positioner The masonry cell positioner created by the `usePositioner()` hook. | ||
*/ | ||
export const useResizeObserver = positioner => { | ||
const forceUpdate = useForceUpdate(); | ||
const resizeObserver = createResizeObserver(positioner, forceUpdate); // Cleans up the resize observers when they change or the | ||
// component unmounts | ||
function _ref2() { | ||
return resizeObserver.disconnect(); | ||
} | ||
React.useEffect(() => _ref2, [resizeObserver]); | ||
return resizeObserver; | ||
}; | ||
/** | ||
* Creates a resize observer that fires an `updater` callback whenever the height of | ||
* one or many cells change. The `useResizeObserver()` hook is using this under the hood. | ||
* | ||
* @param positioner A cell positioner created by the `usePositioner()` hook or the `createPositioner()` utility | ||
* @param updater A callback that fires whenever one or many cell heights change. | ||
*/ | ||
export const createResizeObserver = /*#__PURE__*/trieMemoize([WeakMap], // TODO: figure out a way to test this | ||
/* istanbul ignore next */ | ||
(positioner, updater) => new ResizeObserver(entries => { | ||
const updates = []; | ||
let i = 0; | ||
for (; i < entries.length; i++) { | ||
const entry = entries[i]; // There are native resize observers that still don't have | ||
// the borderBoxSize property. For those we fallback to the | ||
// offset height of the target element. | ||
const height = entry.borderBoxSize !== void 0 ? entry.borderBoxSize.blockSize : entry.target.offsetHeight; | ||
if (height > 0) { | ||
const index = elementsCache.get(entry.target); | ||
if (index !== void 0) { | ||
const position = positioner.get(index); | ||
if (position !== void 0 && height !== position.height) updates.push(index, height); | ||
} | ||
} | ||
} | ||
if (updates.length > 0) { | ||
// Updates the size/positions of the cell with the resize | ||
// observer updates | ||
positioner.update(updates); | ||
updater(updates); | ||
} | ||
})); | ||
/** | ||
* A utility hook for seamlessly adding infinite scroll behavior to the `useMasonry()` hook. This | ||
* hook invokes a callback each time the last rendered index surpasses the total number of items | ||
* in your items array or the number defined in the `totalItems` option. | ||
* | ||
* @param loadMoreItems This callback is invoked when more rows must be loaded. It will be used to | ||
* determine when to refresh the list with the newly-loaded data. This callback may be called multiple | ||
* times in reaction to a single scroll event, so it's important to memoize its arguments. If you're | ||
* creating this callback inside of a functional component, make sure you wrap it in `React.useCallback()`, | ||
* as well. | ||
* @param options | ||
*/ | ||
export function useInfiniteLoader(loadMoreItems, options = emptyObj) { | ||
const { | ||
isItemLoaded, | ||
minimumBatchSize = 16, | ||
threshold = 16, | ||
totalItems = 9e9 | ||
} = options; | ||
const storedLoadMoreItems = useLatest(loadMoreItems); | ||
const storedIsItemLoaded = useLatest(isItemLoaded); | ||
return React.useCallback((startIndex, stopIndex, items) => { | ||
const unloadedRanges = scanForUnloadedRanges(storedIsItemLoaded.current, minimumBatchSize, items, totalItems, Math.max(0, startIndex - threshold), Math.min(totalItems - 1, (stopIndex || 0) + threshold)); // The user is responsible for memoizing their loadMoreItems() function | ||
// because we don't want to make assumptions about how they want to deal | ||
// with `items` | ||
for (let i = 0; i < unloadedRanges.length - 1; ++i) storedLoadMoreItems.current(unloadedRanges[i], unloadedRanges[++i], items); | ||
}, [totalItems, minimumBatchSize, threshold, storedLoadMoreItems, storedIsItemLoaded]); | ||
} | ||
/** | ||
* Returns all of the ranges within a larger range that contain unloaded rows. | ||
*/ | ||
const scanForUnloadedRanges = (isItemLoaded = defaultIsItemLoaded, minimumBatchSize = 16, items, totalItems = 9e9, startIndex, stopIndex) => { | ||
const unloadedRanges = []; | ||
let rangeStartIndex, | ||
rangeStopIndex, | ||
index = startIndex; | ||
for (; index <= stopIndex; index++) { | ||
if (!isItemLoaded(index, items)) { | ||
rangeStopIndex = index; | ||
if (rangeStartIndex === void 0) rangeStartIndex = index; | ||
} else if (rangeStartIndex !== void 0 && rangeStopIndex !== void 0) { | ||
unloadedRanges.push(rangeStartIndex, rangeStopIndex); | ||
rangeStartIndex = rangeStopIndex = void 0; | ||
} | ||
} // If :rangeStopIndex is not null it means we haven't run out of unloaded rows. | ||
// Scan forward to try filling our :minimumBatchSize. | ||
if (rangeStartIndex !== void 0 && rangeStopIndex !== void 0) { | ||
const potentialStopIndex = Math.min(Math.max(rangeStopIndex, rangeStartIndex + minimumBatchSize - 1), totalItems - 1); | ||
for (index = rangeStopIndex + 1; index <= potentialStopIndex; index++) { | ||
if (!isItemLoaded(index, items)) { | ||
rangeStopIndex = index; | ||
} else { | ||
break; | ||
} | ||
} | ||
unloadedRanges.push(rangeStartIndex, rangeStopIndex); | ||
} // Check to see if our first range ended prematurely. | ||
// In this case we should scan backwards to try filling our :minimumBatchSize. | ||
/* istanbul ignore next */ | ||
if (unloadedRanges.length) { | ||
let firstUnloadedStart = unloadedRanges[0]; | ||
const firstUnloadedStop = unloadedRanges[1]; | ||
while (firstUnloadedStop - firstUnloadedStart + 1 < minimumBatchSize && firstUnloadedStart > 0) { | ||
const index = firstUnloadedStart - 1; | ||
if (!isItemLoaded(index, items)) { | ||
unloadedRanges[0] = firstUnloadedStart = index; | ||
} else { | ||
break; | ||
} | ||
} | ||
} | ||
return unloadedRanges; | ||
}; | ||
const defaultIsItemLoaded = (index, items) => items[index] !== void 0; | ||
// | ||
// Utilities | ||
/** | ||
* Creates a cell positioner for the `useMasonry()` hook. The `usePositioner()` hook uses | ||
* this utility under the hood. | ||
* | ||
* @param columnCount The number of columns in the grid | ||
* @param columnWidth The width of each column in the grid | ||
* @param columnGutter The amount of horizontal and vertical space in pixels to render | ||
* between each grid item. | ||
*/ | ||
export const createPositioner = (columnCount, columnWidth, columnGutter = 0) => { | ||
// O(log(n)) lookup of cells to render for a given viewport size | ||
// Store tops and bottoms of each cell for fast intersection lookup. | ||
const intervalTree = createIntervalTree(); // Track the height of each column. | ||
// Layout algorithm below always inserts into the shortest column. | ||
const columnHeights = new Array(columnCount); // Used for O(1) item access | ||
const items = []; // Tracks the item indexes within an individual column | ||
const columnItems = new Array(columnCount); | ||
for (let i = 0; i < columnCount; i++) { | ||
columnHeights[i] = 0; | ||
columnItems[i] = []; | ||
} | ||
return { | ||
columnCount, | ||
columnWidth, | ||
set: (index, height = 0) => { | ||
let column = 0; // finds the shortest column and uses it | ||
for (let i = 1; i < columnHeights.length; i++) { | ||
if (columnHeights[i] < columnHeights[column]) column = i; | ||
} | ||
const top = columnHeights[column] || 0; | ||
columnHeights[column] = top + height + columnGutter; | ||
columnItems[column].push(index); | ||
items[index] = { | ||
left: column * (columnWidth + columnGutter), | ||
top, | ||
height, | ||
column | ||
}; | ||
intervalTree.insert(top, top + height, index); | ||
}, | ||
get: index => items[index], | ||
// This only updates items in the specific columns that have changed, on and after the | ||
// specific items that have changed | ||
update: updates => { | ||
const columns = new Array(columnCount); | ||
let i = 0, | ||
j = 0; // determines which columns have items that changed, as well as the minimum index | ||
// changed in that column, as all items after that index will have their positions | ||
// affected by the change | ||
for (; i < updates.length - 1; i++) { | ||
const index = updates[i]; | ||
const item = items[index]; | ||
item.height = updates[++i]; | ||
intervalTree.remove(index); | ||
intervalTree.insert(item.top, item.top + item.height, index); | ||
columns[item.column] = columns[item.column] === void 0 ? index : Math.min(index, columns[item.column]); | ||
} | ||
for (i = 0; i < columns.length; i++) { | ||
// bails out if the column didn't change | ||
if (columns[i] === void 0) continue; | ||
const itemsInColumn = columnItems[i]; // the index order is sorted with certainty so binary search is a great solution | ||
// here as opposed to Array.indexOf() | ||
const startIndex = binarySearch(itemsInColumn, columns[i]); | ||
const index = columnItems[i][startIndex]; | ||
const startItem = items[index]; | ||
columnHeights[i] = startItem.top + startItem.height + columnGutter; | ||
for (j = startIndex + 1; j < itemsInColumn.length; j++) { | ||
const index = itemsInColumn[j]; | ||
const item = items[index]; | ||
item.top = columnHeights[i]; | ||
columnHeights[i] = item.top + item.height + columnGutter; | ||
intervalTree.remove(index); | ||
intervalTree.insert(item.top, item.top + item.height, index); | ||
} | ||
} | ||
}, | ||
// Render all cells visible within the viewport range defined. | ||
range: (lo, hi, renderCallback) => intervalTree.search(lo, hi, (index, top) => renderCallback(index, items[index].left, top)), | ||
estimateHeight: (itemCount, defaultItemHeight) => { | ||
const tallestColumn = Math.max(0, Math.max.apply(null, columnHeights)); | ||
return itemCount === intervalTree.size ? tallestColumn : tallestColumn + Math.ceil((itemCount - intervalTree.size) / columnCount) * defaultItemHeight; | ||
}, | ||
shortestColumn: () => { | ||
if (columnHeights.length > 1) return Math.min.apply(null, columnHeights); | ||
return columnHeights[0] || 0; | ||
}, | ||
size() { | ||
return intervalTree.size; | ||
} | ||
}; | ||
}; | ||
/* istanbul ignore next */ | ||
const binarySearch = (a, y) => { | ||
let l = 0; | ||
let h = a.length - 1; | ||
while (l <= h) { | ||
const m = l + h >>> 1; | ||
const x = a[m]; | ||
if (x === y) return m;else if (x <= y) l = m + 1;else h = m - 1; | ||
} | ||
return -1; | ||
}; | ||
if (typeof process !== 'undefined' && process.env.NODE_ENV !== 'production') { | ||
Masonry.displayName = 'Masonry'; | ||
MasonryScroller.displayName = 'MasonryScroller'; | ||
List.displayName = 'List'; | ||
} | ||
export * from './list'; | ||
export * from './masonry'; | ||
export * from './masonry-scroller'; | ||
export * from './use-container-position'; | ||
export * from './use-infinite-loader'; | ||
export * from './use-masonry'; | ||
export * from './use-positioner'; | ||
export * from './use-resize-observer'; | ||
export * from './use-scroll-to-index'; | ||
export * from './use-scroller'; |
@@ -1,2 +0,2 @@ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],e):e((t=t||self).Masonic={},t.React)}(this,(function(t,e){"use strict";function n(){return(n=Object.assign||function(t){for(var e=1;arguments.length>e;e++){var n=arguments[e];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(t[r]=n[r])}return t}).apply(this,arguments)}function r(t,e){var n=-1;return t.some((function(t,r){return t[0]===e?(n=r,1):0})),n}function i(){this.__entries__=[]}function o(){return this.__entries__.length}function u(t){var e=r(this.__entries__,t),n=this.__entries__[e];return n&&n[1]}function s(t,e){var n=r(this.__entries__,t);~n?this.__entries__[n][1]=e:this.__entries__.push([t,e])}function c(t){var e=this.__entries__,n=r(e,t);~n&&e.splice(n,1)}function a(t){return!!~r(this.__entries__,t)}function f(){this.__entries__.splice(0)}function h(t,e){void 0===e&&(e=null);for(var n=0,r=this.__entries__;n<r.length;n++){var i=r[n];t.call(e,i[1],i[0])}}function d(t){return setTimeout((function(){return t(Date.now())}),1e3/60)}function l(){this.connected_=0,this.mutationEventsAdded_=0,this.mutationsObserver_=null,this.observers_=[],this.onTransitionEnd_=this.onTransitionEnd_.bind(this),this.refresh=function(t){function e(){i&&(i=0,t()),o&&r()}function n(){Q(e)}function r(){var t=Date.now();if(i){if(2>t-u)return;o=1}else i=1,o=0,setTimeout(n,20);u=t}var i=0,o=0,u=0;return r}(this.refresh.bind(this))}function v(t){~this.observers_.indexOf(t)||this.observers_.push(t),this.connected_||this.connect_()}function p(t){var e=this.observers_,n=e.indexOf(t);~n&&e.splice(n,1),!e.length&&this.connected_&&this.disconnect_()}function m(){this.updateObservers_()&&this.refresh()}function g(t){return t.gatherActive(),t.hasActive()}function b(t){return t.broadcastActive()}function y(){var t=this.observers_.filter(g);return t.forEach(b),t.length>0}function _(){K&&!this.connected_&&(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),X?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:1,childList:1,characterData:1,subtree:1})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=1),this.connected_=1)}function w(){K&&this.connected_&&(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=0,this.connected_=0)}function P(t){var e=t.propertyName,n=void 0===e?"":e;U.some((function(t){return!!~n.indexOf(t)}))&&this.refresh()}function R(){return this.instance_||(this.instance_=new l),this.instance_}function x(t){return parseFloat(t)||0}function C(t){for(var e=[],n=1;arguments.length>n;n++)e[n-1]=arguments[n];return e.reduce((function(e,n){return e+x(t["border-"+n+"-width"])}),0)}function M(t){return t instanceof tt(t).SVGGraphicsElement}function E(t){return t instanceof tt(t).SVGElement&&"function"==typeof t.getBBox}function L(t){return K?nt(t)?function(t){var e=t.getBBox();return O(0,0,e.width,e.height)}(t):function(t){var e=t.clientWidth,n=t.clientHeight;if(!e&&!n)return et;var r=tt(t).getComputedStyle(t),i=function(t){for(var e={},n=0,r=["top","right","bottom","left"];r.length>n;n++){var i=r[n],o=t["padding-"+i];e[i]=x(o)}return e}(r),o=i.left+i.right,u=i.top+i.bottom,s=x(r.width),c=x(r.height);if("border-box"===r.boxSizing&&(Math.round(s+o)!==e&&(s-=C(r,"left","right")+o),Math.round(c+u)!==n&&(c-=C(r,"top","bottom")+u)),!function(t){return t===tt(t).document.documentElement}(t)){var a=Math.round(s+o)-e,f=Math.round(c+u)-n;1!==Math.abs(a)&&(s-=a),1!==Math.abs(f)&&(c-=f)}return O(i.left,i.top,s,c)}(t):et}function O(t,e,n,r){return{x:t,y:e,width:n,height:r}}function z(t){this.broadcastWidth=0,this.broadcastHeight=0,this.contentRect_=O(0,0,0,0),this.target=t}function T(){var t=L(this.target);return this.contentRect_=t,t.width!==this.broadcastWidth||t.height!==this.broadcastHeight}function A(){var t=this.contentRect_;return this.broadcastWidth=t.width,this.broadcastHeight=t.height,t}function S(t,e){var n,r,i,o,u,s,c,a=(r=(n=e).x,i=n.y,o=n.width,u=n.height,s="undefined"!=typeof DOMRectReadOnly?DOMRectReadOnly:Object,c=Object.create(s.prototype),$(c,{x:r,y:i,width:o,height:u,top:i,right:r+o,bottom:u+i,left:r}),c);$(this,{target:t,contentRect:a})}function k(t,e,n){if(this.activeObservations_=[],this.observations_=new Y,"function"!=typeof t)throw new TypeError("The callback provided as parameter 1 is not a function.");this.callback_=t,this.controller_=e,this.callbackCtx_=n}function W(t){if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");if("undefined"!=typeof Element&&Element instanceof Object){if(!(t instanceof tt(t).Element))throw new TypeError('parameter 1 is not of type "Element".');var e=this.observations_;e.has(t)||(e.set(t,new rt(t)),this.controller_.addObserver(this),this.controller_.refresh())}}function H(t){if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");if("undefined"!=typeof Element&&Element instanceof Object){if(!(t instanceof tt(t).Element))throw new TypeError('parameter 1 is not of type "Element".');var e=this.observations_;e.has(t)&&(e.delete(t),e.size||this.controller_.removeObserver(this))}}function j(){this.clearActive(),this.observations_.clear(),this.controller_.removeObserver(this)}function D(){var t=this;this.clearActive(),this.observations_.forEach((function(e){e.isActive()&&t.activeObservations_.push(e)}))}function B(t){return new it(t.target,t.broadcastRect())}function I(){if(this.hasActive()){var t=this.callbackCtx_,e=this.activeObservations_.map(B);this.callback_.call(t,e,t),this.clearActive()}}function G(){this.activeObservations_.splice(0)}function q(){return this.activeObservations_.length>0}function F(t,n,r){function i(){s.current=0,a()}void 0===n&&(n=30),void 0===r&&(r=0);var o=pt(t),u=1e3/n,s=e.useRef(0),c=e.useRef(),a=function(){return c.current&&clearTimeout(c.current)},f=[n,r,o];return e.useEffect((function(){return i}),f),e.useCallback((function(){var t=arguments,e=gt(),n=function(){s.current=e,a(),o.current.apply(null,t)},i=s.current;if(r&&0===i)return n();if(e-i>u){if(i>0)return n();s.current=e}a(),c.current=setTimeout((function(){n(),s.current=0}),u)}),f)}function N(t,e,n,r){function i(){for(var t=arguments.length,e=new Array(t),n=0;t>n;n++)e[n]=arguments[n];o.current.apply(this,e)}void 0===r&&(r=V);var o=pt(n),u=pt(r);vt((function(){var n=t&&"current"in t?t.current:t;if(n){var r=i;n.addEventListener(e,r);var o=u.current;return function(){n.removeEventListener(e,r),o()}}}),[t,e])}function V(){}var Y=function(){return"undefined"!=typeof Map?Map:(Object.defineProperty(i.prototype,"size",{get:o,enumerable:1,configurable:1}),i.prototype.get=u,i.prototype.set=s,i.prototype.delete=c,i.prototype.has=a,i.prototype.clear=f,i.prototype.forEach=h,i)}(),K="undefined"!=typeof window&&"undefined"!=typeof document&&window.document===document,J=function(){return"undefined"!=typeof global&&global.Math===Math?global:"undefined"!=typeof self&&self.Math===Math?self:"undefined"!=typeof window&&window.Math===Math?window:Function("return this")()}(),Q=function(){return"function"==typeof requestAnimationFrame?requestAnimationFrame.bind(J):d}(),U=["top","right","bottom","left","width","height","size","weight"],X="undefined"!=typeof MutationObserver,Z=function(){return l.prototype.addObserver=v,l.prototype.removeObserver=p,l.prototype.refresh=m,l.prototype.updateObservers_=y,l.prototype.connect_=_,l.prototype.disconnect_=w,l.prototype.onTransitionEnd_=P,l.getInstance=R,l.instance_=null,l}(),$=function(t,e){for(var n=0,r=Object.keys(e);n<r.length;n++){var i=r[n];Object.defineProperty(t,i,{value:e[i],enumerable:0,writable:0,configurable:1})}return t},tt=function(t){return t&&t.ownerDocument&&t.ownerDocument.defaultView||J},et=O(0,0,0,0),nt=function(){return"undefined"!=typeof SVGGraphicsElement?M:E}(),rt=function(){return z.prototype.isActive=T,z.prototype.broadcastRect=A,z}(),it=function(){return S}(),ot=function(){return k.prototype.observe=W,k.prototype.unobserve=H,k.prototype.disconnect=j,k.prototype.gatherActive=D,k.prototype.broadcastActive=I,k.prototype.clearActive=G,k.prototype.hasActive=q,k}(),ut="undefined"!=typeof WeakMap?new WeakMap:new Y,st=function(){return function t(e){if(!(this instanceof t))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var n=Z.getInstance(),r=new ot(e,n,this);ut.set(this,r)}}();["observe","unobserve","disconnect"].forEach((function(t){st.prototype[t]=function(){var e;return(e=ut.get(this))[t].apply(e,arguments)}}));var ct=function(){return void 0!==J.ResizeObserver?J.ResizeObserver:st}(),at=function(t){try{return new t}catch(t){var e={};return{set:function(t,n){e[t]=n},get:function(t){return e[t]}}}},ft=function(t,e){var n,r,i,o,u,s,c,a,f,h=(c=(r=t).length,a=at(r[0]),f=1===c,3>c?{g:function(t){return void 0===(i=a.get(t[0]))||f?i:i.get(t[1])},s:function(t,e){return f?a.set(t[0],e):void 0===(i=a.get(t[0]))?((o=at(r[1])).set(t[1],e),a.set(t[0],o)):i.set(t[1],e),e}}:{g:function(t){for(s=a,u=0;c>u;u++)if(void 0===(s=s.get(t[u])))return;return s},s:function(t,e){for(s=a,u=0;c-1>u;u++)void 0===(o=s.get(t[u]))?(o=at(r[u+1]),s.set(t[u],o),s=o):s=o;return s.set(t[c-1],e),e}}),d=h.g,l=h.s;return function(){return void 0===(n=d(arguments))?l(arguments,e.apply(null,arguments)):n}},ht=function(){var t,e;this.set=void 0,this.get=void 0,this.get=function(n){return n===t?e:void 0},this.set=function(n,r){t=n,e=r}},dt=function(t,e){var n,r,i=e||lt;return function(){return n&&i(arguments,n)?r:r=t.apply(null,n=arguments)}},lt=function(t,e){return t[0]===e[0]&&t[1]===e[1]&&t[2]===e[2]&&t[3]===e[3]},vt=e["undefined"!=typeof document&&void 0!==document.createElement?"useLayoutEffect":"useEffect"],pt=function(t){var n=e.useRef(t);return n.current=t,n},mt="undefined"!=typeof performance?performance:Date,gt=function(){return mt.now()},bt="undefined"==typeof window?null:window,yt=function(){return void 0!==bt.scrollY?bt.scrollY:void 0===bt.pageYOffset?0:bt.pageYOffset},_t=function(t,n,r){function i(){s.current&&clearTimeout(s.current),s.current=void 0}function o(){s.current=void 0}void 0===n&&(n=100),void 0===r&&(r=0);var u=pt(t),s=e.useRef(),c=[n,r,u];return e.useEffect((function(){return i}),c),e.useCallback((function(){var t=arguments,e=s.current;if(void 0===e&&r)return s.current=setTimeout(o,n),u.current.apply(null,t);e&&clearTimeout(e),s.current=setTimeout((function(){s.current=void 0,u.current.apply(null,t)}),n)}),c)},wt={},Pt="undefined"==typeof window?null:window,Rt=function(){return[document.documentElement.clientWidth,document.documentElement.clientHeight]},xt="undefined",Ct=typeof window!==xt?window:{},Mt=typeof performance!==xt?performance:Date,Et=function(){return Mt.now()},Lt="AnimationFrame",Ot="cancel"+Lt,zt="request"+Lt,Tt=Ct[zt]&&Ct[zt].bind(Ct),At=Ct[Ot]&&Ct[Ot].bind(Ct);if(!Tt||!At){var St=0;Tt=function(t){var e=Et(),n=Math.max(St+1e3/60,e);return setTimeout((function(){t(St=n)}),n-e)},At=function(t){return clearTimeout(t)}}var kt={low:0,max:0,high:0,C:2,P:void 0,R:void 0,L:void 0,list:void 0};kt.P=kt,kt.L=kt,kt.R=kt;var Wt=function(t){var e=t.high;t.L===kt&&t.R===kt?t.max=e:t.L===kt?t.max=Math.max(t.R.max,e):t.R===kt?t.max=Math.max(t.L.max,e):t.max=Math.max(Math.max(t.L.max,t.R.max),e)},Ht=function(t){for(var e=t;e.P!==kt;)Wt(e.P),e=e.P},jt=function(t,e){if(e.R!==kt){var n=e.R;e.R=n.L,n.L!==kt&&(n.L.P=e),n.P=e.P,e.P===kt?t.root=n:e===e.P.L?e.P.L=n:e.P.R=n,n.L=e,e.P=n,Wt(e),Wt(n)}},Dt=function(t,e){if(e.L!==kt){var n=e.L;e.L=n.R,n.R!==kt&&(n.R.P=e),n.P=e.P,e.P===kt?t.root=n:e===e.P.R?e.P.R=n:e.P.L=n,n.R=e,e.P=n,Wt(e),Wt(n)}},Bt=function(t,e,n){e.P===kt?t.root=n:e===e.P.L?e.P.L=n:e.P.R=n,n.P=e.P},It=e.createElement,Gt=function(t){var r=t.positioner,i=t.resizeObserver,o=t.items,u=t.as,s=void 0===u?"div":u,c=t.id,a=t.className,f=t.style,h=t.role,d=void 0===h?"grid":h,l=t.tabIndex,v=void 0===l?0:l,p=t.containerRef,m=t.itemAs,g=void 0===m?"div":m,b=t.itemStyle,y=t.itemHeightEstimate,_=void 0===y?300:y,w=t.itemKey,P=void 0===w?Kt:w,R=t.overscanBy,x=void 0===R?2:R,C=t.scrollTop,M=t.isScrolling,E=t.height,L=t.render,O=t.onRender,z=0,T=void 0,A=ee(),S=Ut(r,i),k=o.length,W=r.columnWidth,H=r.columnCount,j=r.range,D=r.estimateHeight,B=r.size,I=r.shortestColumn,G=B(),q=I(),F=[],N=d+"item",V=C+(x*=E),Y=V>q&&k>G;if(j(Math.max(0,C-x/2),V,(function(t,e,r){var i=o[t],u=P(i,t),s={top:r,left:e,width:W,writingMode:"horizontal-tb",position:"absolute"};F.push(It(g,{key:u,ref:S(t),role:N,style:"object"==typeof b&&null!==b?n(s,b):s},Ft(L,t,i,W))),void 0===T?(z=t,T=t):(z=Math.min(z,t),T=Math.max(T,t))})),Y)for(var K=Math.min(k-G,Math.ceil((C+x-q)/_*H)),J=G,Q=Jt(W);G+K>J;J++){var U=o[J],X=P(U,J);F.push(It(g,{key:X,ref:S(J),role:N,style:"object"==typeof b?n(Q,b):Q},Ft(L,J,U,W)))}e.useEffect((function(){"function"==typeof O&&void 0!==T&&O(z,T,o),qt="1"}),[z,T,o,O]),e.useEffect((function(){Y&&A()}),[Y]);var Z=Nt(M,D(k,_));return It(s,{ref:p,key:qt,id:c,role:d,className:a,tabIndex:v,style:"object"==typeof f?Yt(Z,f):Z,children:F})},qt="0",Ft=ft([ht,{},WeakMap,ht],(function(t,e,n,r){return It(t,{index:e,data:n,width:r})})),Nt=dt((function(t,e){return{position:"relative",width:"100%",maxWidth:"100%",height:Math.ceil(e),maxHeight:Math.ceil(e),willChange:t?"contents":void 0,pointerEvents:t?"none":void 0}})),Vt=function(t,e){return t[0]===e[0]&&t[1]===e[1]},Yt=dt((function(t,e){return n({},t,e)}),Vt),Kt=function(t,e){return e},Jt=dt((function(t){return{width:t,zIndex:-1e3,visibility:"hidden",position:"absolute",writingMode:"horizontal-tb"}}),(function(t,e){return t[0]===e[0]})),Qt=new WeakMap,Ut=dt((function(t,e){return function(n){return function(r){null!==r&&(e&&(e.observe(r),Qt.set(r,n)),void 0===t.get(n)&&t.set(n,r.offsetHeight))}}}),Vt),Xt=function(t){var e=ne(t.offset,t.scrollFps),n=e.scrollTop,r=e.isScrolling;return Gt({scrollTop:n,isScrolling:r,positioner:t.positioner,resizeObserver:t.resizeObserver,items:t.items,onRender:t.onRender,as:t.as,id:t.id,className:t.className,style:t.style,role:t.role,tabIndex:t.tabIndex,containerRef:t.containerRef,itemAs:t.itemAs,itemStyle:t.itemStyle,itemHeightEstimate:t.itemHeightEstimate,itemKey:t.itemKey,overscanBy:t.overscanBy,height:t.height,render:t.render})},Zt=e.memo((function(t){var r=e.useRef(null),i=function(t){void 0===t&&(t=wt);var n=t,r=n.wait,i=n.leading,o=n.initialWidth,u=void 0===o?0:o,s=n.initialHeight,c=function(t,n,r){var i=e.useState(t);return[i[0],_t(i[1],n,r)]}("undefined"==typeof document?[u,void 0===s?0:s]:Rt,r,i),a=c[0],f=c[1],h=function(){return f(Rt)};return N(Pt,"resize",h),N(Pt,"orientationchange",h),a}({initialWidth:t.ssrWidth,initialHeight:t.ssrHeight}),o=re(r,i),u=n({offset:o.offset,width:o.width||i[0],height:i[1],containerRef:r},t);return u.positioner=ie(u),u.resizeObserver=oe(u.positioner),It(Xt,u)})),$t={},te=[],ee=function(){var t=e.useState($t)[1];return e.useRef((function(){return t({})})).current},ne=function(t,n){function r(){s(0)}void 0===t&&(t=0),void 0===n&&(n=12);var i=function(t){void 0===t&&(t=30);var n=function(t,n){var r=e.useState(t);return[r[0],F(r[1],n,1)]}("undefined"==typeof window?0:yt,t);return N(bt,"scroll",(function(){return n[1](yt())})),n[0]}(n),o=e.useState(0),u=o[0],s=o[1],c=e.useRef(0);return e.useEffect((function(){1===c.current&&s(1);var t,e,i,o,u=(t=r,e=40+1e3/n,i=Et(),(o={}).v=Tt((function n(){Et()-i<e?o.v=Tt(n):t.call(null)})),o);return c.current=1,function(){return function(t){At(t.v||-1)}(u)}}),[n,i]),{scrollTop:Math.max(0,i-t),isScrolling:u}},re=function(t,n){void 0===n&&(n=te);var r=e.useState({offset:0,width:0}),i=r[0],o=r[1];return vt((function(){var e=t.current;if(null!==e){var n=0,r=e;do{n+=r.offsetTop||0,r=r.offsetParent}while(r);n===i.offset&&e.offsetWidth===i.width||o({offset:n,width:e.offsetWidth})}}),n),i},ie=function(t,n){var r=t.width,i=t.columnWidth,o=void 0===i?200:i,u=t.columnGutter,s=void 0===u?0:u,c=t.columnCount;void 0===n&&(n=te);var a=function(){var t=function(t,e,n,r){return void 0===t&&(t=0),void 0===e&&(e=0),void 0===n&&(n=8),r=r||Math.floor(t/(e+n))||1,[Math.floor((t-n*(r-1))/r),r]}(r,o,s,c),e=t[0],n=t[1];return ce(n,e,s)},f=e.useState(a),h=f[0],d=f[1],l=e.useRef(0);return e.useEffect((function(){l.current&&d(a()),l.current=1}),n),vt((function(){if(l.current){for(var t=h.size(),e=a(),n=0;t>n;n++){var r=h.get(n);e.set(n,void 0!==r?r.height:0)}d(e)}}),[r,o,s,c]),h},oe=function(t){function n(){return i.disconnect()}var r=ee(),i=ue(t,r);return e.useEffect((function(){return n}),[i]),i},ue=ft([WeakMap],(function(t,e){return new ct((function(n){for(var r=[],i=0;i<n.length;i++){var o=n[i],u=void 0!==o.borderBoxSize?o.borderBoxSize.blockSize:o.target.offsetHeight;if(u>0){var s=Qt.get(o.target);if(void 0!==s){var c=t.get(s);void 0!==c&&u!==c.height&&r.push(s,u)}}}r.length>0&&(t.update(r),e(r))}))})),se=function(t,e){return void 0!==e[t]},ce=function(t,e,n){void 0===n&&(n=0);for(var r=function(){var t={root:kt,size:0},e={};return{insert:function(n,r,i){for(var o=t.root,u=kt;o!==kt&&n!==(u=o).low;)o=n<o.low?o.L:o.R;if(n===u.low&&u!==kt){if(!function(t,e,n){for(var r,i=t.list;i;){if(i.index===n)return 0;if(e>i.high)break;r=i,i=i.next}return r||(t.list={index:n,high:e,next:i}),r&&(r.next={index:n,high:e,next:r.next}),1}(u,r,i))return;return u.high=Math.max(u.high,r),Wt(u),Ht(u),e[i]=u,void t.size++}var s={low:n,high:r,max:r,C:0,P:u,L:kt,R:kt,list:{index:i,high:r,next:null}};u===kt?t.root=s:(s.low<u.low?u.L=s:u.R=s,Ht(s)),function(t,e){for(var n;0===e.P.C;)e.P===e.P.P.L?0===(n=e.P.P.R).C?(e.P.C=1,n.C=1,e.P.P.C=0,e=e.P.P):(e===e.P.R&&(e=e.P,jt(t,e)),e.P.C=1,e.P.P.C=0,Dt(t,e.P.P)):0===(n=e.P.P.L).C?(e.P.C=1,n.C=1,e.P.P.C=0,e=e.P.P):(e===e.P.L&&(e=e.P,Dt(t,e)),e.P.C=1,e.P.P.C=0,jt(t,e.P.P));t.root.C=1}(t,s),e[i]=s,t.size++},remove:function(n){var r=e[n];if(void 0!==r){delete e[n];var i=function(t,e){var n=t.list;if(n.index===e)return null===n.next?0:(t.list=n.next,1);var r=n;for(n=n.next;null!==n;){if(n.index===e)return r.next=n.next,1;r=n,n=n.next}}(r,n);if(void 0!==i){if(1===i)return r.high=r.list.high,Wt(r),Ht(r),void t.size--;var o,u=r,s=u.C;r.L===kt?(o=r.R,Bt(t,r,r.R)):r.R===kt?(o=r.L,Bt(t,r,r.L)):(s=(u=function(t){for(;t.L!==kt;)t=t.L;return t}(r.R)).C,o=u.R,u.P===r?o.P=u:(Bt(t,u,u.R),u.R=r.R,u.R.P=u),Bt(t,r,u),u.L=r.L,u.L.P=u,u.C=r.C),Wt(o),Ht(o),1===s&&function(t,e){for(var n;e!==kt&&1===e.C;)e===e.P.L?(0===(n=e.P.R).C&&(n.C=1,e.P.C=0,jt(t,e.P),n=e.P.R),1===n.L.C&&1===n.R.C?(n.C=0,e=e.P):(1===n.R.C&&(n.L.C=1,n.C=0,Dt(t,n),n=e.P.R),n.C=e.P.C,e.P.C=1,n.R.C=1,jt(t,e.P),e=t.root)):(0===(n=e.P.L).C&&(n.C=1,e.P.C=0,Dt(t,e.P),n=e.P.L),1===n.R.C&&1===n.L.C?(n.C=0,e=e.P):(1===n.L.C&&(n.R.C=1,n.C=0,jt(t,n),n=e.P.L),n.C=e.P.C,e.P.C=1,n.L.C=1,Dt(t,e.P),e=t.root));e.C=1}(t,o),t.size--}}},search:function(e,n,r){for(var i=[t.root];0!==i.length;){var o=i.pop();if(o!==kt&&e<=o.max&&(o.L!==kt&&i.push(o.L),o.R!==kt&&i.push(o.R),o.low<=n&&o.high>=e))for(var u=o.list;null!==u;)u.high<e||r(u.index,o.low),u=u.next}},get size(){return t.size}}}(),i=new Array(t),o=[],u=new Array(t),s=0;t>s;s++)i[s]=0,u[s]=[];return{columnCount:t,columnWidth:e,set:function(t,s){void 0===s&&(s=0);for(var c=0,a=1;a<i.length;a++)i[a]<i[c]&&(c=a);var f=i[c]||0;i[c]=f+s+n,u[c].push(t),o[t]={left:c*(e+n),top:f,height:s,column:c},r.insert(f,f+s,t)},get:function(t){return o[t]},update:function(e){for(var s=new Array(t),c=0,a=0;c<e.length-1;c++){var f=e[c],h=o[f];h.height=e[++c],r.remove(f),r.insert(h.top,h.top+h.height,f),s[h.column]=void 0===s[h.column]?f:Math.min(f,s[h.column])}for(c=0;c<s.length;c++)if(void 0!==s[c]){var d=u[c],l=ae(d,s[c]),v=u[c][l],p=o[v];for(i[c]=p.top+p.height+n,a=l+1;a<d.length;a++){var m=d[a],g=o[m];g.top=i[c],i[c]=g.top+g.height+n,r.remove(m),r.insert(g.top,g.top+g.height,m)}}},range:function(t,e,n){return r.search(t,e,(function(t,e){return n(t,o[t].left,e)}))},estimateHeight:function(e,n){var o=Math.max(0,Math.max.apply(null,i));return e===r.size?o:o+Math.ceil((e-r.size)/t)*n},shortestColumn:function(){return i.length>1?Math.min.apply(null,i):i[0]||0},size:function(){return r.size}}},ae=function(t,e){for(var n=0,r=t.length-1;r>=n;){var i=n+r>>>1,o=t[i];if(o===e)return i;o>e?r=i-1:n=i+1}return-1};t.List=function(t){return It(Zt,n({role:"list"},t,{columnGutter:t.rowGutter,columnCount:1,columnWidth:1}))},t.Masonry=Zt,t.MasonryScroller=Xt,t.createPositioner=ce,t.createResizeObserver=ue,t.useContainerPosition=re,t.useInfiniteLoader=function(t,n){void 0===n&&(n=$t);var r=n,i=r.isItemLoaded,o=r.minimumBatchSize,u=void 0===o?16:o,s=r.threshold,c=void 0===s?16:s,a=r.totalItems,f=void 0===a?9e9:a,h=pt(t),d=pt(i);return e.useCallback((function(t,e,n){for(var r=function(t,e,n,r,i,o){void 0===t&&(t=se),void 0===e&&(e=16),void 0===r&&(r=9e9);for(var u,s,c=[],a=i;o>=a;a++)t(a,n)?void 0!==u&&void 0!==s&&(c.push(u,s),u=s=void 0):(s=a,void 0===u&&(u=a));if(void 0!==u&&void 0!==s){var f=Math.min(Math.max(s,u+e-1),r-1);for(a=s+1;f>=a&&!t(a,n);a++)s=a;c.push(u,s)}if(c.length)for(var h=c[0],d=c[1];e>d-h+1&&h>0;){var l=h-1;if(t(l,n))break;c[0]=h=l}return c}(d.current,u,n,f,Math.max(0,t-c),Math.min(f-1,(e||0)+c)),i=0;i<r.length-1;++i)h.current(r[i],r[++i],n)}),[f,u,c,h,d])},t.useMasonry=Gt,t.usePositioner=ie,t.useResizeObserver=oe,t.useScroller=ne,Object.defineProperty(t,"__esModule",{value:1})})); | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],e):e((t=t||self).Masonic={},t.React)}(this,(function(t,e){"use strict";function n(){return(n=Object.assign||function(t){for(var e=1;arguments.length>e;e++){var n=arguments[e];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(t[r]=n[r])}return t}).apply(this,arguments)}function r(t,e,n,r){function o(){for(var t=arguments.length,e=new Array(t),n=0;t>n;n++)e[n]=arguments[n];u.current.apply(this,e)}void 0===r&&(r=i);var u=ct(n),s=ct(r);ft((function(){var n=t&&"current"in t?t.current:t;if(n){var r=o;n.addEventListener(e,r);var i=s.current;return function(){n.removeEventListener(e,r),i()}}}),[t,e])}function i(){}function o(t,n,r){function i(){s.current=0,a()}void 0===n&&(n=30),void 0===r&&(r=0);var o=ct(t),u=1e3/n,s=e.useRef(0),c=e.useRef(),a=function(){return c.current&&clearTimeout(c.current)},f=[n,r,o];return e.useEffect((function(){return i}),f),e.useCallback((function(){var t=arguments,e=pt(),n=function(){s.current=e,a(),o.current.apply(null,t)},i=s.current;if(r&&0===i)return n();if(e-i>u){if(i>0)return n();s.current=e}a(),c.current=setTimeout((function(){n(),s.current=0}),u)}),f)}function u(){}function s(t,n){function r(){a(0)}void 0===t&&(t=0),void 0===n&&(n=12);var i=function(t){void 0===t&&(t=30);var n=function(t,n){var r=e.useState(t);return[r[0],o(r[1],n,1)]}("undefined"==typeof window?0:gt,t);return function(t,e,r,i){function o(){for(var t=arguments.length,e=new Array(t),n=0;t>n;n++)e[n]=arguments[n];s.current.apply(this,e)}void 0===i&&(i=u);var s=ct((function(){return n[1](gt())})),c=ct(i);ft((function(){var e=t&&"current"in t?t.current:t;if(e){var n=o;e.addEventListener("scroll",n);var r=c.current;return function(){e.removeEventListener("scroll",n),r()}}}),[t,"scroll"])}(mt),n[0]}(n),s=e.useState(0),c=s[0],a=s[1],f=e.useRef(0);return e.useEffect((function(){1===f.current&&a(1);var t,e,i,o,u=(t=r,e=40+1e3/n,i=_t(),(o={}).v=Ct((function n(){_t()-i<e?o.v=Ct(n):t.call(null)})),o);return f.current=1,function(){return function(t){Et(t.v||-1)}(u)}}),[n,i]),{scrollTop:Math.max(0,i-t),isScrolling:c}}function c(){var t=e.useState(kt)[1];return e.useRef((function(){return t({})})).current}function a(t){var r=t.positioner,i=t.resizeObserver,o=t.items,u=t.as,s=void 0===u?"div":u,a=t.id,h=t.className,d=t.style,l=t.role,v=void 0===l?"grid":l,p=t.tabIndex,m=void 0===p?0:p,g=t.containerRef,y=t.itemAs,b=void 0===y?"div":y,w=t.itemStyle,_=t.itemHeightEstimate,P=void 0===_?300:_,x=t.itemKey,R=void 0===x?f:x,C=t.overscanBy,E=void 0===C?2:C,M=t.scrollTop,L=t.isScrolling,O=t.height,T=t.render,z=t.onRender,A=0,S=void 0,k=c(),W=qt(r,i),H=o.length,I=r.columnWidth,j=r.columnCount,D=r.range,B=r.estimateHeight,G=r.size,q=r.shortestColumn,F=G(),N=q(),V=[],Y=v+"item",K=ct(z),J=M+(E*=O),Q=J>N&&H>F;if(D(Math.max(0,M-E/2),J,(function(t,e,r){var i=o[t],u=R(i,t),s={top:r,left:e,width:I,writingMode:"horizontal-tb",position:"absolute"};V.push(Wt(b,{key:u,ref:W(t),role:Y,style:"object"==typeof w&&null!==w?n(s,w):s},It(T,t,i,I))),void 0===S?(A=t,S=t):(A=Math.min(A,t),S=Math.max(S,t))})),Q)for(var U=Math.min(H-F,Math.ceil((M+E-N)/P*j)),X=F,Z=Gt(I);F+U>X;X++){var $=o[X],tt=R($,X);V.push(Wt(b,{key:tt,ref:W(X),role:Y,style:"object"==typeof w?n(Z,w):Z},It(T,X,$,I)))}e.useEffect((function(){"function"==typeof K.current&&void 0!==S&&K.current(A,S,o),Ht="1"}),[A,S,o,K]),e.useEffect((function(){Q&&k()}),[Q]);var et=jt(L,B(H,P));return Wt(s,{ref:g,key:Ht,id:a,role:v,className:h,tabIndex:m,style:"object"==typeof d?Bt(et,d):et,children:V})}function f(t,e){return e}function h(t){var e=s(t.offset,t.scrollFps);return a({scrollTop:e.scrollTop,isScrolling:e.isScrolling,positioner:t.positioner,resizeObserver:t.resizeObserver,items:t.items,onRender:t.onRender,as:t.as,id:t.id,className:t.className,style:t.style,role:t.role,tabIndex:t.tabIndex,containerRef:t.containerRef,itemAs:t.itemAs,itemStyle:t.itemStyle,itemHeightEstimate:t.itemHeightEstimate,itemKey:t.itemKey,overscanBy:t.overscanBy,height:t.height,render:t.render})}function d(t,n){void 0===n&&(n=Ft);var r=e.useState({offset:0,width:0}),i=r[0],o=r[1];return ft((function(){var e=t.current;if(null!==e){var n=0,r=e;do{n+=r.offsetTop||0,r=r.offsetParent}while(r);n===i.offset&&e.offsetWidth===i.width||o({offset:n,width:e.offsetWidth})}}),n),i}function l(t,e){var n=-1;return t.some((function(t,r){return t[0]===e?(n=r,1):0})),n}function v(){this.__entries__=[]}function p(){return this.__entries__.length}function m(t){var e=l(this.__entries__,t),n=this.__entries__[e];return n&&n[1]}function g(t,e){var n=l(this.__entries__,t);~n?this.__entries__[n][1]=e:this.__entries__.push([t,e])}function y(t){var e=this.__entries__,n=l(e,t);~n&&e.splice(n,1)}function b(t){return!!~l(this.__entries__,t)}function w(){this.__entries__.splice(0)}function _(t,e){void 0===e&&(e=null);for(var n=0,r=this.__entries__;n<r.length;n++){var i=r[n];t.call(e,i[1],i[0])}}function P(t){return setTimeout((function(){return t(Date.now())}),1e3/60)}function x(){this.connected_=0,this.mutationEventsAdded_=0,this.mutationsObserver_=null,this.observers_=[],this.onTransitionEnd_=this.onTransitionEnd_.bind(this),this.refresh=function(t){function e(){i&&(i=0,t()),o&&r()}function n(){Kt(e)}function r(){var t=Date.now();if(i){if(2>t-u)return;o=1}else i=1,o=0,setTimeout(n,20);u=t}var i=0,o=0,u=0;return r}(this.refresh.bind(this))}function R(t){~this.observers_.indexOf(t)||this.observers_.push(t),this.connected_||this.connect_()}function C(t){var e=this.observers_,n=e.indexOf(t);~n&&e.splice(n,1),!e.length&&this.connected_&&this.disconnect_()}function E(){this.updateObservers_()&&this.refresh()}function M(t){return t.gatherActive(),t.hasActive()}function L(t){return t.broadcastActive()}function O(){var t=this.observers_.filter(M);return t.forEach(L),t.length>0}function T(){Vt&&!this.connected_&&(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),Qt?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:1,childList:1,characterData:1,subtree:1})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=1),this.connected_=1)}function z(){Vt&&this.connected_&&(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=0,this.connected_=0)}function A(t){var e=t.propertyName,n=void 0===e?"":e;Jt.some((function(t){return!!~n.indexOf(t)}))&&this.refresh()}function S(){return this.instance_||(this.instance_=new x),this.instance_}function k(t){return parseFloat(t)||0}function W(t){for(var e=[],n=1;arguments.length>n;n++)e[n-1]=arguments[n];return e.reduce((function(e,n){return e+k(t["border-"+n+"-width"])}),0)}function H(t){return t instanceof Zt(t).SVGGraphicsElement}function I(t){return t instanceof Zt(t).SVGElement&&"function"==typeof t.getBBox}function j(t){return Vt?te(t)?function(t){var e=t.getBBox();return D(0,0,e.width,e.height)}(t):function(t){var e=t.clientWidth,n=t.clientHeight;if(!e&&!n)return $t;var r=Zt(t).getComputedStyle(t),i=function(t){for(var e={},n=0,r=["top","right","bottom","left"];r.length>n;n++){var i=r[n],o=t["padding-"+i];e[i]=k(o)}return e}(r),o=i.left+i.right,u=i.top+i.bottom,s=k(r.width),c=k(r.height);if("border-box"===r.boxSizing&&(Math.round(s+o)!==e&&(s-=W(r,"left","right")+o),Math.round(c+u)!==n&&(c-=W(r,"top","bottom")+u)),!function(t){return t===Zt(t).document.documentElement}(t)){var a=Math.round(s+o)-e,f=Math.round(c+u)-n;1!==Math.abs(a)&&(s-=a),1!==Math.abs(f)&&(c-=f)}return D(i.left,i.top,s,c)}(t):$t}function D(t,e,n,r){return{x:t,y:e,width:n,height:r}}function B(t){this.broadcastWidth=0,this.broadcastHeight=0,this.contentRect_=D(0,0,0,0),this.target=t}function G(){var t=j(this.target);return this.contentRect_=t,t.width!==this.broadcastWidth||t.height!==this.broadcastHeight}function q(){var t=this.contentRect_;return this.broadcastWidth=t.width,this.broadcastHeight=t.height,t}function F(t,e){var n,r,i,o,u,s,c,a=(r=(n=e).x,i=n.y,o=n.width,u=n.height,s="undefined"!=typeof DOMRectReadOnly?DOMRectReadOnly:Object,c=Object.create(s.prototype),Xt(c,{x:r,y:i,width:o,height:u,top:i,right:r+o,bottom:u+i,left:r}),c);Xt(this,{target:t,contentRect:a})}function N(t,e,n){if(this.activeObservations_=[],this.observations_=new Nt,"function"!=typeof t)throw new TypeError("The callback provided as parameter 1 is not a function.");this.callback_=t,this.controller_=e,this.callbackCtx_=n}function V(t){if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");if("undefined"!=typeof Element&&Element instanceof Object){if(!(t instanceof Zt(t).Element))throw new TypeError('parameter 1 is not of type "Element".');var e=this.observations_;e.has(t)||(e.set(t,new ee(t)),this.controller_.addObserver(this),this.controller_.refresh())}}function Y(t){if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");if("undefined"!=typeof Element&&Element instanceof Object){if(!(t instanceof Zt(t).Element))throw new TypeError('parameter 1 is not of type "Element".');var e=this.observations_;e.has(t)&&(e.delete(t),e.size||this.controller_.removeObserver(this))}}function K(){this.clearActive(),this.observations_.clear(),this.controller_.removeObserver(this)}function J(){var t=this;this.clearActive(),this.observations_.forEach((function(e){e.isActive()&&t.activeObservations_.push(e)}))}function Q(t){return new ne(t.target,t.broadcastRect())}function U(){if(this.hasActive()){var t=this.callbackCtx_,e=this.activeObservations_.map(Q);this.callback_.call(t,e,t),this.clearActive()}}function X(){this.activeObservations_.splice(0)}function Z(){return this.activeObservations_.length>0}function $(t){function n(){return i.disconnect()}var r=c(),i=se(t,r);return e.useEffect((function(){return n}),[i]),i}function tt(t){var e=t.high;t.L===ce&&t.R===ce?t.max=e:t.L===ce?t.max=Math.max(t.R.max,e):t.R===ce?t.max=Math.max(t.L.max,e):t.max=Math.max(Math.max(t.L.max,t.R.max),e)}function et(t){for(var e=t;e.P!==ce;)tt(e.P),e=e.P}function nt(t,e){if(e.R!==ce){var n=e.R;e.R=n.L,n.L!==ce&&(n.L.P=e),n.P=e.P,e.P===ce?t.root=n:e===e.P.L?e.P.L=n:e.P.R=n,n.L=e,e.P=n,tt(e),tt(n)}}function rt(t,e){if(e.L!==ce){var n=e.L;e.L=n.R,n.R!==ce&&(n.R.P=e),n.P=e.P,e.P===ce?t.root=n:e===e.P.R?e.P.R=n:e.P.L=n,n.R=e,e.P=n,tt(e),tt(n)}}function it(t,e,n){e.P===ce?t.root=n:e===e.P.L?e.P.L=n:e.P.R=n,n.P=e.P}function ot(t,n){var r=t.width,i=t.columnWidth,o=void 0===i?200:i,u=t.columnGutter,s=void 0===u?0:u,c=t.columnCount;void 0===n&&(n=de);var a=function(){var t=he(r,o,s,c),e=t[0],n=t[1];return ae(n,e,s)},f=e.useState(a),h=f[0],d=f[1],l=e.useRef(0);return ft((function(){l.current&&d(a()),l.current=1}),n),ft((function(){if(l.current){for(var t=h.size(),e=a(),n=0;t>n;n++){var r=h.get(n);e.set(n,void 0!==r?r.height:0)}d(e)}}),[r,o,s,c]),h}function ut(t,n){var r,i,u,s,c=n.align,a=void 0===c?"top":c,f=n.element,h=void 0===f?"undefined"!=typeof window&&window:f,d=n.offset,l=void 0===d?0:d,v=n.height,p=void 0===v?"undefined"!=typeof window?window.innerHeight:0:v,m=ct({positioner:t,element:h,align:a,offset:l,height:p}),g=e.useRef((function(){var t=m.current.element;return t&&"current"in t?t.current:t})).current,y=e.useReducer((function(t,e){var n,r={position:t.position,index:t.index,prevTop:t.prevTop};if("scrollToIndex"===e.type)return{position:m.current.positioner.get(null!==(n=e.value)&&void 0!==n?n:-1),index:e.value,prevTop:void 0};if("setPosition"===e.type)r.position=e.value;else if("setPrevTop"===e.type)r.prevTop=e.value;else if("reset"===e.type)return le;return r}),le),b=y[0],w=y[1],_=o(w,15);i=g(),u=ct((function(){if(!b.position&&b.index){var t=m.current.positioner.get(b.index);t&&w({type:"setPosition",value:t})}})),s=ct(void 0),ft((function(){function t(){if(!n){for(var t=arguments.length,e=new Array(t),r=0;t>r;r++)e[r]=arguments[r];u.current.apply(this,e)}}var e=i&&"current"in i?i.current:i;if(e){var n=0;e.addEventListener("scroll",t);var r=s.current;return function(){n=1,e.removeEventListener("scroll",t),r&&r()}}}),[i,"scroll"]);var P=void 0!==b.index&&(null===(r=m.current.positioner.get(b.index))||void 0===r?void 0:r.top);return e.useEffect((function(){var t=g();if(t){var e=m.current,n=e.height,r=e.align,i=e.offset,o=e.positioner;if(b.position){var u=b.position.top;"bottom"===r?u=u-n+b.position.height:"center"===r&&(u-=(n-b.position.height)/2),t.scrollTo(0,Math.max(0,u+=i));var s=0,c=setTimeout((function(){return!s&&w({type:"reset"})}),400);return function(){s=1,clearTimeout(c)}}if(void 0!==b.index){var a=o.shortestColumn()/o.size()*b.index;b.prevTop&&(a=Math.max(a,b.prevTop+n)),t.scrollTo(0,a),_({type:"setPrevTop",value:a})}}}),[P,b,m,g,_]),e.useRef((function(t){w({type:"scrollToIndex",value:t})})).current}function st(t){var i=e.useRef(null),o=function(t){void 0===t&&(t=ht);var n=t,i=n.wait,o=n.leading,u=n.initialWidth,s=void 0===u?0:u,c=n.initialHeight,a=function(t,n,r){var i=e.useState(t);return[i[0],at(i[1],n,r)]}("undefined"==typeof document?[s,void 0===c?0:c]:lt,i,o),f=a[0],h=a[1],d=function(){return h(lt)};return r(dt,"resize",d),r(dt,"orientationchange",d),f}({initialWidth:t.ssrWidth,initialHeight:t.ssrHeight}),u=d(i,o),s=n({offset:u.offset,width:u.width||o[0],height:o[1],containerRef:i},t);s.positioner=ot(s),s.resizeObserver=$(s.positioner);var c=ut(s.positioner,{height:s.height,offset:u.offset,align:"object"==typeof t.scrollToIndex?t.scrollToIndex.align:void 0});return e.useEffect((function(){var e=t.scrollToIndex;void 0!==e&&c("number"==typeof e?e:e.index)}),[t.scrollToIndex,c]),ve(h,s)}var ct=function(t){var n=e.useRef(t);return n.current=t,n},at=function(t,n,r){function i(){s.current&&clearTimeout(s.current),s.current=void 0}function o(){s.current=void 0}void 0===n&&(n=100),void 0===r&&(r=0);var u=ct(t),s=e.useRef(),c=[n,r,u];return e.useEffect((function(){return i}),c),e.useCallback((function(){var t=arguments,e=s.current;if(void 0===e&&r)return s.current=setTimeout(o,n),u.current.apply(null,t);e&&clearTimeout(e),s.current=setTimeout((function(){s.current=void 0,u.current.apply(null,t)}),n)}),c)},ft=e["undefined"!=typeof document&&void 0!==document.createElement?"useLayoutEffect":"useEffect"],ht={},dt="undefined"==typeof window?null:window,lt=function(){return[document.documentElement.clientWidth,document.documentElement.clientHeight]},vt="undefined"!=typeof performance?performance:Date,pt=function(){return vt.now()},mt="undefined"==typeof window?null:window,gt=function(){return void 0!==mt.scrollY?mt.scrollY:void 0===mt.pageYOffset?0:mt.pageYOffset},yt="undefined",bt=typeof window!==yt?window:{},wt=typeof performance!==yt?performance:Date,_t=function(){return wt.now()},Pt="AnimationFrame",xt="cancel"+Pt,Rt="request"+Pt,Ct=bt[Rt]&&bt[Rt].bind(bt),Et=bt[xt]&&bt[xt].bind(bt);if(!Ct||!Et){var Mt=0;Ct=function(t){var e=_t(),n=Math.max(Mt+1e3/60,e);return setTimeout((function(){t(Mt=n)}),n-e)},Et=function(t){return clearTimeout(t)}}var Lt=function(t){try{return new t}catch(t){var e={};return{set:function(t,n){e[t]=n},get:function(t){return e[t]}}}},Ot=function(t,e){var n,r,i,o,u,s,c,a,f,h=(c=(r=t).length,a=Lt(r[0]),f=1===c,3>c?{g:function(t){return void 0===(i=a.get(t[0]))||f?i:i.get(t[1])},s:function(t,e){return f?a.set(t[0],e):void 0===(i=a.get(t[0]))?((o=Lt(r[1])).set(t[1],e),a.set(t[0],o)):i.set(t[1],e),e}}:{g:function(t){for(s=a,u=0;c>u;u++)if(void 0===(s=s.get(t[u])))return;return s},s:function(t,e){for(s=a,u=0;c-1>u;u++)void 0===(o=s.get(t[u]))?(o=Lt(r[u+1]),s.set(t[u],o),s=o):s=o;return s.set(t[c-1],e),e}}),d=h.g,l=h.s;return function(){return void 0===(n=d(arguments))?l(arguments,e.apply(null,arguments)):n}},Tt=function(){var t,e;this.set=void 0,this.get=void 0,this.get=function(n){return n===t?e:void 0},this.set=function(n,r){t=n,e=r}},zt=function(t,e){var n,r,i=e||At;return function(){return n&&i(arguments,n)?r:r=t.apply(null,n=arguments)}},At=function(t,e){return t[0]===e[0]&&t[1]===e[1]&&t[2]===e[2]&&t[3]===e[3]},St=new WeakMap,kt={},Wt=e.createElement,Ht="0",It=Ot([Tt,{},WeakMap,Tt],(function(t,e,n,r){return Wt(t,{index:e,data:n,width:r})})),jt=zt((function(t,e){return{position:"relative",width:"100%",maxWidth:"100%",height:Math.ceil(e),maxHeight:Math.ceil(e),willChange:t?"contents":void 0,pointerEvents:t?"none":void 0}})),Dt=function(t,e){return t[0]===e[0]&&t[1]===e[1]},Bt=zt((function(t,e){return n({},t,e)}),Dt),Gt=zt((function(t){return{width:t,zIndex:-1e3,visibility:"hidden",position:"absolute",writingMode:"horizontal-tb"}}),(function(t,e){return t[0]===e[0]})),qt=zt((function(t,e){return function(n){return function(r){null!==r&&(e&&(e.observe(r),St.set(r,n)),void 0===t.get(n)&&t.set(n,r.offsetHeight))}}}),Dt),Ft=[],Nt=function(){return"undefined"!=typeof Map?Map:(Object.defineProperty(v.prototype,"size",{get:p,enumerable:1,configurable:1}),v.prototype.get=m,v.prototype.set=g,v.prototype.delete=y,v.prototype.has=b,v.prototype.clear=w,v.prototype.forEach=_,v)}(),Vt="undefined"!=typeof window&&"undefined"!=typeof document&&window.document===document,Yt=function(){return"undefined"!=typeof global&&global.Math===Math?global:"undefined"!=typeof self&&self.Math===Math?self:"undefined"!=typeof window&&window.Math===Math?window:Function("return this")()}(),Kt=function(){return"function"==typeof requestAnimationFrame?requestAnimationFrame.bind(Yt):P}(),Jt=["top","right","bottom","left","width","height","size","weight"],Qt="undefined"!=typeof MutationObserver,Ut=function(){return x.prototype.addObserver=R,x.prototype.removeObserver=C,x.prototype.refresh=E,x.prototype.updateObservers_=O,x.prototype.connect_=T,x.prototype.disconnect_=z,x.prototype.onTransitionEnd_=A,x.getInstance=S,x.instance_=null,x}(),Xt=function(t,e){for(var n=0,r=Object.keys(e);n<r.length;n++){var i=r[n];Object.defineProperty(t,i,{value:e[i],enumerable:0,writable:0,configurable:1})}return t},Zt=function(t){return t&&t.ownerDocument&&t.ownerDocument.defaultView||Yt},$t=D(0,0,0,0),te=function(){return"undefined"!=typeof SVGGraphicsElement?H:I}(),ee=function(){return B.prototype.isActive=G,B.prototype.broadcastRect=q,B}(),ne=function(){return F}(),re=function(){return N.prototype.observe=V,N.prototype.unobserve=Y,N.prototype.disconnect=K,N.prototype.gatherActive=J,N.prototype.broadcastActive=U,N.prototype.clearActive=X,N.prototype.hasActive=Z,N}(),ie="undefined"!=typeof WeakMap?new WeakMap:new Nt,oe=function(){return function t(e){if(!(this instanceof t))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var n=Ut.getInstance(),r=new re(e,n,this);ie.set(this,r)}}();["observe","unobserve","disconnect"].forEach((function(t){oe.prototype[t]=function(){var e;return(e=ie.get(this))[t].apply(e,arguments)}}));var ue=function(){return void 0!==Yt.ResizeObserver?Yt.ResizeObserver:oe}(),se=Ot([WeakMap],(function(t,e){return new ue((function(n){for(var r=[],i=0;i<n.length;i++){var o=n[i],u=void 0!==o.borderBoxSize?o.borderBoxSize.blockSize:o.target.offsetHeight;if(u>0){var s=St.get(o.target);if(void 0!==s){var c=t.get(s);void 0!==c&&u!==c.height&&r.push(s,u)}}}r.length>0&&(t.update(r),e(r))}))})),ce={low:0,max:0,high:0,C:2,P:void 0,R:void 0,L:void 0,list:void 0};ce.P=ce,ce.L=ce,ce.R=ce;var ae=function(t,e,n){void 0===n&&(n=0);for(var r=function(){var t={root:ce,size:0},e={};return{insert:function(n,r,i){for(var o=t.root,u=ce;o!==ce&&n!==(u=o).low;)o=n<o.low?o.L:o.R;if(n===u.low&&u!==ce){if(!function(t,e,n){for(var r,i=t.list;i;){if(i.index===n)return 0;if(e>i.high)break;r=i,i=i.next}return r||(t.list={index:n,high:e,next:i}),r&&(r.next={index:n,high:e,next:r.next}),1}(u,r,i))return;return u.high=Math.max(u.high,r),tt(u),et(u),e[i]=u,void t.size++}var s={low:n,high:r,max:r,C:0,P:u,L:ce,R:ce,list:{index:i,high:r,next:null}};u===ce?t.root=s:(s.low<u.low?u.L=s:u.R=s,et(s)),function(t,e){for(var n;0===e.P.C;)e.P===e.P.P.L?0===(n=e.P.P.R).C?(e.P.C=1,n.C=1,e.P.P.C=0,e=e.P.P):(e===e.P.R&&nt(t,e=e.P),e.P.C=1,e.P.P.C=0,rt(t,e.P.P)):0===(n=e.P.P.L).C?(e.P.C=1,n.C=1,e.P.P.C=0,e=e.P.P):(e===e.P.L&&rt(t,e=e.P),e.P.C=1,e.P.P.C=0,nt(t,e.P.P));t.root.C=1}(t,s),e[i]=s,t.size++},remove:function(n){var r=e[n];if(void 0!==r){delete e[n];var i=function(t,e){var n=t.list;if(n.index===e)return null===n.next?0:(t.list=n.next,1);var r=n;for(n=n.next;null!==n;){if(n.index===e)return r.next=n.next,1;r=n,n=n.next}}(r,n);if(void 0!==i){if(1===i)return r.high=r.list.high,tt(r),et(r),void t.size--;var o,u=r,s=u.C;r.L===ce?(o=r.R,it(t,r,r.R)):r.R===ce?(o=r.L,it(t,r,r.L)):(s=(u=function(t){for(;t.L!==ce;)t=t.L;return t}(r.R)).C,o=u.R,u.P===r?o.P=u:(it(t,u,u.R),u.R=r.R,u.R.P=u),it(t,r,u),u.L=r.L,u.L.P=u,u.C=r.C),tt(o),et(o),1===s&&function(t,e){for(var n;e!==ce&&1===e.C;)e===e.P.L?(0===(n=e.P.R).C&&(n.C=1,e.P.C=0,nt(t,e.P),n=e.P.R),1===n.L.C&&1===n.R.C?(n.C=0,e=e.P):(1===n.R.C&&(n.L.C=1,n.C=0,rt(t,n),n=e.P.R),n.C=e.P.C,e.P.C=1,n.R.C=1,nt(t,e.P),e=t.root)):(0===(n=e.P.L).C&&(n.C=1,e.P.C=0,rt(t,e.P),n=e.P.L),1===n.R.C&&1===n.L.C?(n.C=0,e=e.P):(1===n.L.C&&(n.R.C=1,n.C=0,nt(t,n),n=e.P.L),n.C=e.P.C,e.P.C=1,n.L.C=1,rt(t,e.P),e=t.root));e.C=1}(t,o),t.size--}}},search:function(e,n,r){for(var i=[t.root];0!==i.length;){var o=i.pop();if(o!==ce&&e<=o.max&&(o.L!==ce&&i.push(o.L),o.R!==ce&&i.push(o.R),o.low<=n&&o.high>=e))for(var u=o.list;null!==u;)u.high<e||r(u.index,o.low),u=u.next}},get size(){return t.size}}}(),i=new Array(t),o=[],u=new Array(t),s=0;t>s;s++)i[s]=0,u[s]=[];return{columnCount:t,columnWidth:e,set:function(t,s){void 0===s&&(s=0);for(var c=0,a=1;a<i.length;a++)i[a]<i[c]&&(c=a);var f=i[c]||0;i[c]=f+s+n,u[c].push(t),o[t]={left:c*(e+n),top:f,height:s,column:c},r.insert(f,f+s,t)},get:function(t){return o[t]},update:function(e){for(var s=new Array(t),c=0,a=0;c<e.length-1;c++){var f=e[c],h=o[f];h.height=e[++c],r.remove(f),r.insert(h.top,h.top+h.height,f),s[h.column]=void 0===s[h.column]?f:Math.min(f,s[h.column])}for(c=0;c<s.length;c++)if(void 0!==s[c]){var d=u[c],l=fe(d,s[c]),v=u[c][l],p=o[v];for(i[c]=p.top+p.height+n,a=l+1;a<d.length;a++){var m=d[a],g=o[m];g.top=i[c],i[c]=g.top+g.height+n,r.remove(m),r.insert(g.top,g.top+g.height,m)}}},range:function(t,e,n){return r.search(t,e,(function(t,e){return n(t,o[t].left,e)}))},estimateHeight:function(e,n){var o=Math.max(0,Math.max.apply(null,i));return e===r.size?o:o+Math.ceil((e-r.size)/t)*n},shortestColumn:function(){return i.length>1?Math.min.apply(null,i):i[0]||0},size:function(){return r.size}}},fe=function(t,e){for(var n=0,r=t.length-1;r>=n;){var i=n+r>>>1,o=t[i];if(o===e)return i;o>e?r=i-1:n=i+1}return-1},he=function(t,e,n,r){return void 0===t&&(t=0),void 0===e&&(e=0),void 0===n&&(n=8),r=r||Math.floor(t/(e+n))||1,[Math.floor((t-n*(r-1))/r),r]},de=[],le={index:void 0,position:void 0,prevTop:void 0},ve=e.createElement,pe=e.createElement,me=function(t,e){return void 0!==e[t]},ge={};t.List=function(t){return pe(st,n({role:"list",columnGutter:t.rowGutter,columnCount:1,columnWidth:1},t))},t.Masonry=st,t.MasonryScroller=h,t.createPositioner=ae,t.createResizeObserver=se,t.useContainerPosition=d,t.useInfiniteLoader=function(t,n){void 0===n&&(n=ge);var r=n,i=r.isItemLoaded,o=r.minimumBatchSize,u=void 0===o?16:o,s=r.threshold,c=void 0===s?16:s,a=r.totalItems,f=void 0===a?9e9:a,h=ct(t),d=ct(i);return e.useCallback((function(t,e,n){for(var r=function(t,e,n,r,i,o){void 0===t&&(t=me),void 0===e&&(e=16),void 0===r&&(r=9e9);for(var u,s,c=[],a=i;o>=a;a++)t(a,n)?void 0!==u&&void 0!==s&&(c.push(u,s),u=s=void 0):(s=a,void 0===u&&(u=a));if(void 0!==u&&void 0!==s){var f=Math.min(Math.max(s,u+e-1),r-1);for(a=s+1;f>=a&&!t(a,n);a++)s=a;c.push(u,s)}if(c.length)for(var h=c[0],d=c[1];e>d-h+1&&h>0;){var l=h-1;if(t(l,n))break;c[0]=h=l}return c}(d.current,u,n,f,Math.max(0,t-c),Math.min(f-1,(e||0)+c)),i=0;i<r.length-1;++i)h.current(r[i],r[++i],n)}),[f,u,c,h,d])},t.useMasonry=a,t.usePositioner=ot,t.useResizeObserver=$,t.useScrollToIndex=ut,t.useScroller=s,Object.defineProperty(t,"__esModule",{value:1})})); | ||
//# sourceMappingURL=masonic.js.map |
{ | ||
"name": "masonic", | ||
"version": "3.2.0", | ||
"version": "3.3.0", | ||
"homepage": "https://github.com/jaredLunde/masonic#readme", | ||
@@ -61,3 +61,5 @@ "repository": "github:jaredLunde/masonic", | ||
"lint": "eslint . --ext .ts,.tsx", | ||
"prepublishOnly": "npm run lint && npm run test && npm run build && npm run format", | ||
"prepublishOnly": "cli-confirm \"Did you run 'yarn release' first? (y/N)\"", | ||
"prerelease": "npm run validate && npm run build", | ||
"release": "git add . && standard-version -a", | ||
"test": "jest", | ||
@@ -68,3 +70,4 @@ "validate": "lundle check-types && npm run lint && jest --coverage" | ||
"hooks": { | ||
"pre-commit": "lint-staged" | ||
"pre-commit": "lundle check-types && lint-staged", | ||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS" | ||
} | ||
@@ -74,3 +77,2 @@ }, | ||
"**/*.{ts,tsx,js,jsx}": [ | ||
"lundle build -f types", | ||
"eslint", | ||
@@ -83,2 +85,12 @@ "prettier --write" | ||
}, | ||
"commitlint": { | ||
"extends": [ | ||
"@commitlint/config-conventional" | ||
] | ||
}, | ||
"config": { | ||
"commitizen": { | ||
"path": "./node_modules/cz-conventional-changelog" | ||
} | ||
}, | ||
"eslintConfig": { | ||
@@ -90,2 +102,3 @@ "extends": [ | ||
"eslintIgnore": [ | ||
"benchmarks", | ||
"node_modules", | ||
@@ -95,2 +108,3 @@ "coverage", | ||
"test", | ||
"/types", | ||
"*.config.js" | ||
@@ -125,2 +139,4 @@ ], | ||
"devDependencies": { | ||
"@commitlint/cli": "^8.3.5", | ||
"@commitlint/config-conventional": "^8.3.4", | ||
"@essentials/benchmark": "^1.0.6", | ||
@@ -136,8 +152,10 @@ "@shopify/jest-dom-mocks": "^2.9.0", | ||
"babel-jest": "latest", | ||
"cli-confirm": "^1.0.1", | ||
"cz-conventional-changelog": "3.2.0", | ||
"eslint": "latest", | ||
"eslint-config-lunde": "latest", | ||
"eslint-config-lunde": "^0.2.1", | ||
"husky": "latest", | ||
"jest": "latest", | ||
"lint-staged": "latest", | ||
"lundle": "^0.2.4", | ||
"lundle": "^0.4.2", | ||
"node-fetch": "^2.6.0", | ||
@@ -149,2 +167,3 @@ "prettier": "latest", | ||
"react-test-renderer": "latest", | ||
"standard-version": "^8.0.0", | ||
"typescript": "latest" | ||
@@ -156,4 +175,6 @@ }, | ||
"@essentials/request-timeout": "^1.3.0", | ||
"@react-hook/event": "^1.2.2", | ||
"@react-hook/latest": "^1.0.2", | ||
"@react-hook/passive-layout-effect": "^1.2.0", | ||
"@react-hook/throttle": "^2.2.0", | ||
"@react-hook/window-scroll": "^1.3.0", | ||
@@ -165,5 +186,4 @@ "@react-hook/window-size": "^3.0.6", | ||
"peerDependencies": { | ||
"react": ">=16.8", | ||
"react-dom": ">=16.8" | ||
"react": ">=16.8" | ||
} | ||
} |
@@ -1,473 +0,10 @@ | ||
import * as React from 'react' | ||
import ResizeObserver from 'resize-observer-polyfill' | ||
/** | ||
* This hook handles the render phases of the masonry layout and returns the grid as a React element. | ||
* | ||
* @param options Options for configuring the masonry layout renderer. See `UseMasonryOptions`. | ||
*/ | ||
export declare const useMasonry: ({ | ||
positioner, | ||
resizeObserver, | ||
items, | ||
as: ContainerComponent, | ||
id, | ||
className, | ||
style, | ||
role, | ||
tabIndex, | ||
containerRef, | ||
itemAs: ItemComponent, | ||
itemStyle, | ||
itemHeightEstimate, | ||
itemKey, | ||
overscanBy, | ||
scrollTop, | ||
isScrolling, | ||
height, | ||
render: RenderComponent, | ||
onRender, | ||
}: UseMasonryOptions) => JSX.Element | ||
export interface UseMasonryOptions { | ||
/** | ||
* An array containing the data used by the grid items. | ||
*/ | ||
items: any[] | ||
/** | ||
* A grid cell positioner and cache created by the `usePositioner()` hook or | ||
* the `createPositioner` utility. | ||
*/ | ||
positioner: Positioner | ||
/** | ||
* A resize observer that tracks mutations to the grid cells and forces the | ||
* Masonry grid to recalculate its layout if any cells affect column heights | ||
* change. Check out the `useResizeObserver()` hook. | ||
*/ | ||
resizeObserver?: { | ||
observe: ResizeObserver['observe'] | ||
disconnect: ResizeObserver['observe'] | ||
unobserve: ResizeObserver['unobserve'] | ||
} | ||
/** | ||
* This is the type of element the grid container will be rendered as. | ||
* @default "div"` | ||
*/ | ||
as?: keyof JSX.IntrinsicElements | React.ComponentType<any> | ||
/** | ||
* Optionally gives the grid container an `id` prop. | ||
*/ | ||
id?: string | ||
/** | ||
* Optionally gives the grid container a `className` prop. | ||
*/ | ||
className?: string | ||
/** | ||
* Adds extra `style` attributes to the container in addition to those | ||
* created by the `useMasonry()` hook. | ||
*/ | ||
style?: React.CSSProperties | ||
/** | ||
* Optionally swap out the accessibility `role` prop of the container and its items. | ||
* @default "grid" | ||
*/ | ||
role?: 'grid' | 'list' | ||
/** | ||
* Change the `tabIndex` of the grid container. | ||
* @default 0 | ||
*/ | ||
tabIndex?: number | ||
/** | ||
* Forwards a React ref to the grid container. | ||
*/ | ||
containerRef?: | ||
| ((element: HTMLElement) => void) | ||
| React.MutableRefObject<HTMLElement | null> | ||
/** | ||
* This is the type of element the grid items will be rendered as. | ||
* @default "div" | ||
*/ | ||
itemAs?: keyof JSX.IntrinsicElements | React.ComponentType<any> | ||
/** | ||
* Adds extra `style` attributes to the grid items in addition to those | ||
* created by the `useMasonry()` hook. | ||
*/ | ||
itemStyle?: React.CSSProperties | ||
/** | ||
* This value is used for estimating the initial height of the masonry grid. It is important for | ||
* the UX of the scrolling behavior and in determining how many `items` to render in a batch, so it's | ||
* wise to set this value with some level accuracy, though it doesn't need to be perfect. | ||
* @default 300 | ||
*/ | ||
itemHeightEstimate?: number | ||
/** | ||
* The value returned here must be unique to the item. By default, the key is the item's index. This is ok | ||
* if your collection of items is never modified. Setting this property ensures that the component in `render` | ||
* is reused each time the masonry grid is reflowed. A common pattern would be to return the item's database | ||
* ID here if there is one, e.g. `data => data.id` | ||
* @default (data: any, index: number) => index` | ||
*/ | ||
itemKey?: (data: any, index: number) => string | number | ||
/** | ||
* This number is used for determining the number of grid cells outside of the visible window to render. | ||
* The default value is `2` which means "render 2 windows worth (2 * `height`) of content before and after | ||
* the items in the visible window". A value of `3` would be 3 windows worth of grid cells, so it's a | ||
* linear relationship. | ||
* | ||
* Overscanning is important for preventing tearing when scrolling through items in the grid, but setting | ||
* too high of a value may create too much work for React to handle, so it's best that you tune this | ||
* value accordingly. | ||
* @default 2 | ||
*/ | ||
overscanBy?: number | ||
/** | ||
* This is the height of the window. If you're rendering the grid relative to the browser `window`, | ||
* the current `document.documentElement.clientHeight` is the value you'll want to set here. If you're | ||
* rendering the grid inside of another HTML element, you'll want to provide the current `element.offsetHeight` | ||
* here. | ||
*/ | ||
height: number | ||
/** | ||
* The current scroll progress in pixel of the window the grid is rendered in. If you're rendering | ||
* the grid relative to the browser `window`, you'll want the most current `window.scrollY` here. | ||
* If you're rendering the grid inside of another HTML element, you'll want the current `element.scrollTop` | ||
* value here. The `useScroller()` hook and `<MasonryScroller>` components will help you if you're | ||
* rendering the grid relative to the browser `window`. | ||
*/ | ||
scrollTop: number | ||
/** | ||
* This property is used for determining whether or not the grid container should add styles that | ||
* dramatically increase scroll performance. That is, turning off `pointer-events` and adding a | ||
* `will-change: contents;` value to the style string. You can forgo using this prop, but I would | ||
* not recommend that. The `useScroller()` hook and `<MasonryScroller>` components will help you if | ||
* you're rendering the grid relative to the browser `window`. | ||
* @default false | ||
*/ | ||
isScrolling?: boolean | ||
/** | ||
* This component is rendered for each item of your `items` prop array. It should accept three props: | ||
* `index`, `width`, and `data`. See RenderComponentProps. | ||
*/ | ||
render: React.ComponentType<RenderComponentProps> | ||
/** | ||
* This callback is invoked any time the items currently being rendered by the grid change. | ||
*/ | ||
onRender?: ( | ||
startIndex: number, | ||
stopIndex: number | undefined, | ||
items: any[] | ||
) => void | ||
} | ||
export interface RenderComponentProps { | ||
/** | ||
* The index of the cell in the `items` prop array. | ||
*/ | ||
index: number | ||
/** | ||
* The rendered width of the cell's column. | ||
*/ | ||
width: number | ||
/** | ||
* The data at `items[index]` of your `items` prop array. | ||
*/ | ||
data: any | ||
} | ||
/** | ||
* A heavily-optimized component that updates `useMasonry()` when the scroll position of the browser `window` | ||
* changes. This bare-metal component is used by `<Masonry>` under the hood. | ||
*/ | ||
export declare const MasonryScroller: React.FC<MasonryScrollerProps> | ||
/** | ||
* A "batteries included" masonry grid which includes all of the implementation details below. This component is the | ||
* easiest way to get off and running in your app, before switching to more advanced implementations, if necessary. | ||
* It will change its column count to fit its container's width and will decide how many rows to render based upon | ||
* the height of the browser `window`. | ||
*/ | ||
export declare const Masonry: React.FC<MasonryProps> | ||
export interface MasonryProps | ||
extends Omit< | ||
UseMasonryOptions, | ||
| 'scrollTop' | ||
| 'height' | ||
| 'isScrolling' | ||
| 'positioner' | ||
| 'resizeObserver' | ||
| 'containerRef' | ||
>, | ||
Pick<UsePositionerOptions, 'columnWidth' | 'columnGutter' | 'columnCount'> { | ||
/** | ||
* This is the width that will be used for the browser `window` when rendering this component in SSR. | ||
* This prop isn't relevant for client-side only apps. | ||
*/ | ||
ssrWidth?: number | ||
/** | ||
* This is the height that will be used for the browser `window` when rendering this component in SSR. | ||
* This prop isn't relevant for client-side only apps. | ||
*/ | ||
ssrHeight?: number | ||
/** | ||
* This determines how often (in frames per second) to update the scroll position of the | ||
* browser `window` in state, and as a result the rate the masonry grid recalculates its visible cells. | ||
* The default value of `12` has been very reasonable in my own testing, but if you have particularly | ||
* heavy `render` components it may be prudent to reduce this number. | ||
* @default 12 | ||
*/ | ||
scrollFps?: number | ||
} | ||
export interface MasonryScrollerProps | ||
extends Omit< | ||
MasonryProps, | ||
'columnWidth' | 'columnGutter' | 'columnCount' | 'ssrWidth' | 'ssrHeight' | ||
>, | ||
Pick< | ||
UseMasonryOptions, | ||
'height' | 'positioner' | 'containerRef' | 'resizeObserver' | ||
> { | ||
/** | ||
* The vertical space in pixels between the top of the grid container and the top | ||
* of the browser `document.documentElement`. | ||
* @default 0 | ||
*/ | ||
offset?: number | ||
} | ||
/** | ||
* This is just a single-column `<Masonry>` component with `rowGutter` prop instead of | ||
* a `columnGutter` prop. | ||
*/ | ||
export declare const List: React.FC<ListProps> | ||
export interface ListProps | ||
extends Omit<MasonryProps, 'columGutter' | 'columnCount' | 'columnWidth'> { | ||
/** | ||
* The amount of vertical space in pixels to add between the list cells. | ||
* @default 0 | ||
*/ | ||
rowGutter?: number | ||
} | ||
/** | ||
* A hook for tracking whether the `window` is currently being scrolled and it's scroll position on | ||
* the y-axis. These values are used for determining which grid cells to render and when | ||
* to add styles to the masonry container that maximize scroll performance. | ||
* | ||
* @param offset The vertical space in pixels between the top of the grid container and the top | ||
* of the browser `document.documentElement`. | ||
* @param fps This determines how often (in frames per second) to update the scroll position of the | ||
* browser `window` in state, and as a result the rate the masonry grid recalculates its visible cells. | ||
* The default value of `12` has been very reasonable in my own testing, but if you have particularly | ||
* heavy `render` components it may be prudent to reduce this number. | ||
*/ | ||
export declare const useScroller: ( | ||
offset?: number, | ||
fps?: number | ||
) => { | ||
scrollTop: number | ||
isScrolling: boolean | ||
} | ||
/** | ||
* A hook for measuring the width of the grid container, as well as its distance | ||
* from the top of the document. These values are necessary to correctly calculate the number/width | ||
* of columns to render, as well as the number of rows to render. | ||
* | ||
* @param elementRef A `ref` object created by `React.useRef()`. That ref should be provided to the | ||
* `containerRef` property in `useMasonry()`. | ||
* @param deps You can force this hook to recalculate the `offset` and `width` whenever this | ||
* dependencies list changes. A common dependencies list might look like `[windowWidth, windowHeight]`, | ||
* which would force the hook to recalculate any time the size of the browser `window` changed. | ||
*/ | ||
export declare const useContainerPosition: ( | ||
elementRef: React.MutableRefObject<HTMLElement | null>, | ||
deps?: React.DependencyList | ||
) => ContainerPosition | ||
export interface ContainerPosition { | ||
/** | ||
* The distance in pixels between the top of the element in `elementRef` and the top of | ||
* the `document.documentElement`. | ||
*/ | ||
offset: number | ||
/** | ||
* The `offsetWidth` of the element in `elementRef`. | ||
*/ | ||
width: number | ||
} | ||
/** | ||
* This hook creates the grid cell positioner and cache required by `useMasonry()`. This is | ||
* the meat of the grid's layout algorithm, determining which cells to render at a given scroll | ||
* position, as well as where to place new items in the grid. | ||
* | ||
* @param options Properties that determine the number of columns in the grid, as well | ||
* as their widths. | ||
* @param deps This hook will create a new positioner, clearing all existing cached positions, | ||
* whenever the dependencies in this list change. | ||
*/ | ||
export declare const usePositioner: ( | ||
{width, columnWidth, columnGutter, columnCount}: UsePositionerOptions, | ||
deps?: React.DependencyList | ||
) => Positioner | ||
export interface UsePositionerOptions { | ||
/** | ||
* The width of the container you're rendering the grid within, i.e. the container | ||
* element's `element.offsetWidth` | ||
*/ | ||
width: number | ||
/** | ||
* The minimum column width. The `usePositioner()` hook will automatically size the | ||
* columns to fill their container based upon the `columnWidth` and `columnGutter` values. | ||
* It will never render anything smaller than this width unless its container itself is | ||
* smaller than its value. This property is optional if you're using a static `columnCount`. | ||
* @default 200 | ||
*/ | ||
columnWidth?: number | ||
/** | ||
* This sets the vertical and horizontal space between grid cells in pixels. | ||
*/ | ||
columnGutter?: number | ||
/** | ||
* By default, `usePositioner()` derives the column count from the `columnWidth`, `columnGutter`, | ||
* and `width` props. However, in some situations it is nice to be able to override that behavior | ||
* (e.g. creating a `List` component). | ||
*/ | ||
columnCount?: number | ||
} | ||
/** | ||
* Creates a resize observer that forces updates to the grid cell positions when mutations are | ||
* made to cells affecting their height. | ||
* | ||
* @param positioner The masonry cell positioner created by the `usePositioner()` hook. | ||
*/ | ||
export declare const useResizeObserver: ( | ||
positioner: Positioner | ||
) => ResizeObserver | ||
/** | ||
* Creates a resize observer that fires an `updater` callback whenever the height of | ||
* one or many cells change. The `useResizeObserver()` hook is using this under the hood. | ||
* | ||
* @param positioner A cell positioner created by the `usePositioner()` hook or the `createPositioner()` utility | ||
* @param updater A callback that fires whenever one or many cell heights change. | ||
*/ | ||
export declare const createResizeObserver: ( | ||
positioner: Positioner, | ||
updater: (updates: number[]) => void | ||
) => ResizeObserver | ||
/** | ||
* A utility hook for seamlessly adding infinite scroll behavior to the `useMasonry()` hook. This | ||
* hook invokes a callback each time the last rendered index surpasses the total number of items | ||
* in your items array or the number defined in the `totalItems` option. | ||
* | ||
* @param loadMoreItems This callback is invoked when more rows must be loaded. It will be used to | ||
* determine when to refresh the list with the newly-loaded data. This callback may be called multiple | ||
* times in reaction to a single scroll event, so it's important to memoize its arguments. If you're | ||
* creating this callback inside of a functional component, make sure you wrap it in `React.useCallback()`, | ||
* as well. | ||
* @param options | ||
*/ | ||
export declare function useInfiniteLoader<T extends LoadMoreItemsCallback>( | ||
loadMoreItems: T, | ||
options?: UseInfiniteLoaderOptions | ||
): LoadMoreItemsCallback | ||
export interface UseInfiniteLoaderOptions { | ||
/** | ||
* A callback responsible for determining the loaded state of each item. Should return `true` | ||
* if the item has already been loaded and `false` if not. | ||
* @default (index: number, items: any[]) => boolean | ||
*/ | ||
isItemLoaded?: (index: number, items: any[]) => boolean | ||
/** | ||
* The minimum number of new items to be loaded at a time. This property can be used to | ||
* batch requests and reduce HTTP requests. | ||
* @default 16 | ||
*/ | ||
minimumBatchSize?: number | ||
/** | ||
* The threshold at which to pre-fetch data. A threshold X means that new data should start | ||
* loading when a user scrolls within X cells of the end of your `items` array. | ||
* @default 16 | ||
*/ | ||
threshold?: number | ||
/** | ||
* The total number of items you'll need to eventually load (if known). This can | ||
* be arbitrarily high if not known. | ||
* @default 9e9 | ||
*/ | ||
totalItems?: number | ||
} | ||
export declare type LoadMoreItemsCallback = ( | ||
startIndex: number, | ||
stopIndex: number, | ||
items: any[] | ||
) => any | ||
/** | ||
* Creates a cell positioner for the `useMasonry()` hook. The `usePositioner()` hook uses | ||
* this utility under the hood. | ||
* | ||
* @param columnCount The number of columns in the grid | ||
* @param columnWidth The width of each column in the grid | ||
* @param columnGutter The amount of horizontal and vertical space in pixels to render | ||
* between each grid item. | ||
*/ | ||
export declare const createPositioner: ( | ||
columnCount: number, | ||
columnWidth: number, | ||
columnGutter?: number | ||
) => Positioner | ||
export interface Positioner { | ||
/** | ||
* The number of columns in the grid | ||
*/ | ||
columnCount: number | ||
/** | ||
* The width of each column in the grid | ||
*/ | ||
columnWidth: number | ||
/** | ||
* Sets the position for the cell at `index` based upon the cell's height | ||
*/ | ||
set: (index: number, height: number) => void | ||
/** | ||
* Gets the `PositionerItem` for the cell at `index` | ||
*/ | ||
get: (index: number) => PositionerItem | undefined | ||
/** | ||
* Updates cells based on their indexes and heights | ||
* positioner.update([index, height, index, height, index, height...]) | ||
*/ | ||
update: (updates: number[]) => void | ||
/** | ||
* Searches the interval tree for grid cells with a `top` value in | ||
* betwen `lo` and `hi` and invokes the callback for each item that | ||
* is discovered | ||
*/ | ||
range: ( | ||
lo: number, | ||
hi: number, | ||
renderCallback: (index: number, left: number, top: number) => void | ||
) => void | ||
/** | ||
* Returns the number of grid cells in the cache | ||
*/ | ||
size: () => number | ||
/** | ||
* Estimates the total height of the grid | ||
*/ | ||
estimateHeight: (itemCount: number, defaultItemHeight: number) => number | ||
/** | ||
* Returns the height of the shortest column in the grid | ||
*/ | ||
shortestColumn: () => number | ||
} | ||
export interface PositionerItem { | ||
/** | ||
* This is how far from the top edge of the grid container in pixels the | ||
* item is placed | ||
*/ | ||
top: number | ||
/** | ||
* This is how far from the left edge of the grid container in pixels the | ||
* item is placed | ||
*/ | ||
left: number | ||
/** | ||
* This is the height of the grid cell | ||
*/ | ||
height: number | ||
/** | ||
* This is the column number containing the grid cell | ||
*/ | ||
column: number | ||
} | ||
export * from './list' | ||
export * from './masonry' | ||
export * from './masonry-scroller' | ||
export * from './use-container-position' | ||
export * from './use-infinite-loader' | ||
export * from './use-masonry' | ||
export * from './use-positioner' | ||
export * from './use-resize-observer' | ||
export * from './use-scroll-to-index' | ||
export * from './use-scroller' |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
949909
73
9246
835
12
28
11
3
+ Added@react-hook/event@^1.2.2
+ Added@react-hook/throttle@^2.2.0
- Removedreact-dom@19.0.0(transitive)
- Removedscheduler@0.25.0(transitive)