UNPKG

@zapperwing/pinterest-view

Version:

A Pinterest-style grid layout component for React.js with responsive design and dynamic content support

758 lines (732 loc) 35.4 kB
"use strict"; function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = exports.GridInline = void 0; var _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _reactSizeme = _interopRequireDefault(require("react-sizeme")); var _excluded = ["itemKey", "component", "rect", "style", "rtl", "children", "onHeightChange"], _excluded2 = ["gridRef", "children"]; // src/components/StackGrid.js /* eslint-disable max-classes-per-file */ /* eslint-disable react/default-props-match-prop-types */ /* eslint-disable react/no-unused-prop-types */ /* eslint-disable react/jsx-filename-extension */ /* eslint-disable react/jsx-props-no-spreading */ /* eslint-disable no-param-reassign */ function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; } function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); } function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); } function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } } function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); } function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); } function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); } function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; } function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; } var isNumber = function isNumber(v) { return typeof v === 'number' && Number.isFinite(v); }; var isPercentageNumber = function isPercentageNumber(v) { return typeof v === 'string' && /^\d+(\.\d+)?%$/.test(v); }; var getColumnConfig = function getColumnConfig(containerWidth, columnWidth, gutterWidth) { if (isNumber(columnWidth)) { // Keep the column width fixed, only calculate number of columns var columnCount = Math.floor((containerWidth + gutterWidth) / (columnWidth + gutterWidth)); return { columnCount: Math.max(1, columnCount), columnWidth: columnWidth }; } if (isPercentageNumber(columnWidth)) { var percentage = parseFloat(columnWidth) / 100; var _columnCount = Math.floor(1 / percentage); var actualColumnWidth = (containerWidth - (_columnCount - 1) * gutterWidth) / _columnCount; return { columnCount: _columnCount, columnWidth: actualColumnWidth }; } throw new Error('columnWidth must be a number or percentage string'); }; // Optimized column finding - O(n) instead of O(n*m) var getShortestColumn = function getShortestColumn(heights) { var minIndex = 0; var minHeight = heights[0]; for (var i = 1; i < heights.length; i += 1) { if (heights[i] < minHeight) { minHeight = heights[i]; minIndex = i; } } return minIndex; }; var GridItem = /*#__PURE__*/_react["default"].memo(/*#__PURE__*/_react["default"].forwardRef(function (_ref, _ref2) { var itemKey = _ref.itemKey, Element = _ref.component, _ref$rect = _ref.rect, rect = _ref$rect === void 0 ? { top: 0, left: 0, width: 0, height: 0 } : _ref$rect, style = _ref.style, rtl = _ref.rtl, children = _ref.children, onHeightChange = _ref.onHeightChange, rest = _objectWithoutProperties(_ref, _excluded); var itemRef = _react["default"].useRef(null); var _React$useState = _react["default"].useState(true), _React$useState2 = _slicedToArray(_React$useState, 2), isInitialRender = _React$useState2[0], setIsInitialRender = _React$useState2[1]; _react["default"].useEffect(function () { setIsInitialRender(false); }, []); _react["default"].useEffect(function () { if (!itemRef.current || typeof onHeightChange !== 'function') return; // Debounced height update to prevent cascade of updates var timeoutId; var updateHeight = function updateHeight() { clearTimeout(timeoutId); timeoutId = setTimeout(function () { if (!itemRef.current) return; var _itemRef$current$getB = itemRef.current.getBoundingClientRect(), height = _itemRef$current$getB.height; onHeightChange(height); }, 100); // Debounce by 100ms }; // Initial height updateHeight(); // ResizeObserver for height changes - use contentRect to avoid reflow var ro = new ResizeObserver(function (entries) { var entry = entries[0]; if (entry) { var height = entry.contentRect.height; onHeightChange(height); } }); ro.observe(itemRef.current); return function () { clearTimeout(timeoutId); ro.disconnect(); }; }, [onHeightChange, itemKey]); var itemStyle = _objectSpread(_objectSpread({}, style), {}, { position: 'absolute', top: rect.top, left: rtl ? 'auto' : rect.left, right: rtl ? rect.left : 'auto', width: rect.width, zIndex: 1, // Only apply transitions after initial render to prevent performance issues transition: isInitialRender ? 'none' : 'top 0.3s ease-out, left 0.3s ease-out, right 0.3s ease-out, width 0.3s ease-out', // CSS containment for better reflow isolation contain: 'layout style', willChange: isInitialRender ? 'auto' : 'transform' }); return /*#__PURE__*/_react["default"].createElement(Element, _extends({}, rest, { ref: function ref(node) { if (typeof _ref2 === 'function') _ref2(node);else if (_ref2) _ref2.current = node; itemRef.current = node; }, className: "grid-item", style: itemStyle }), children); }), function (prevProps, nextProps) { return prevProps.itemKey === nextProps.itemKey && prevProps.rect.top === nextProps.rect.top && prevProps.rect.left === nextProps.rect.left && prevProps.rect.width === nextProps.rect.width && prevProps.rect.height === nextProps.rect.height && prevProps.rtl === nextProps.rtl && prevProps.children === nextProps.children; }); GridItem.displayName = 'GridItem'; GridItem.propTypes = { itemKey: _propTypes["default"].string, component: _propTypes["default"].string, rect: _propTypes["default"].shape({ top: _propTypes["default"].number, left: _propTypes["default"].number, width: _propTypes["default"].number, height: _propTypes["default"].number }), style: _propTypes["default"].shape({}), rtl: _propTypes["default"].bool, children: _propTypes["default"].node, onHeightChange: _propTypes["default"].func }; GridItem.defaultProps = { itemKey: '', component: 'div', rect: { top: 0, left: 0, width: 0, height: 0 }, style: {}, rtl: false, children: null, onHeightChange: null }; var GridInlinePropTypes = { className: _propTypes["default"].string, style: _propTypes["default"].shape({}), component: _propTypes["default"].string, itemComponent: _propTypes["default"].string, children: _propTypes["default"].node, rtl: _propTypes["default"].bool, onLayout: _propTypes["default"].func, gridRef: _propTypes["default"].func, size: _propTypes["default"].shape({ width: _propTypes["default"].number, height: _propTypes["default"].number, registerRef: _propTypes["default"].func, unregisterRef: _propTypes["default"].func }), columnWidth: _propTypes["default"].oneOfType([_propTypes["default"].number, _propTypes["default"].string]), gutterWidth: _propTypes["default"].number, gutterHeight: _propTypes["default"].number, virtualized: _propTypes["default"].bool, debug: _propTypes["default"].bool, virtualizationBuffer: _propTypes["default"].number, scrollContainer: _propTypes["default"].instanceOf(HTMLElement) }; var GridInlineDefaultProps = { className: '', style: {}, component: 'div', itemComponent: 'div', children: null, rtl: false, onLayout: function onLayout() {}, gridRef: null, size: null, columnWidth: 150, gutterWidth: 5, gutterHeight: 5, virtualized: false, debug: true, virtualizationBuffer: 800, scrollContainer: null }; var GridInline = exports.GridInline = /*#__PURE__*/function (_Component) { function GridInline(props) { var _this; _classCallCheck(this, GridInline); _this = _callSuper(this, GridInline, [props]); // Throttled debug logging _defineProperty(_this, "debugLog", function (message) { var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; var force = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var debug = _this.props.debug; if (!debug) return; var now = Date.now(); var timeSinceLastLog = now - _this.lastLogTime; // Throttle logs to prevent console spam, but allow forced logs if (!force && timeSinceLastLog < 1000) return; _this.lastLogTime = now; if (data) { console.log("[StackGrid] ".concat(message), data); } else { console.log("[StackGrid] ".concat(message)); } }); _defineProperty(_this, "handleScroll", function () { var virtualized = _this.props.virtualized; if (!_this.mounted || !virtualized || !_this.scroller) return; // Use requestAnimationFrame for better performance if (_this.scrollRAF) return; _this.scrollRAF = requestAnimationFrame(function () { // Get scroll position from the appropriate container var scrollTop = _this.scroller === window ? window.pageYOffset || document.documentElement.scrollTop : _this.scroller.scrollTop; var currentScrollTop = _this.state.scrollTop; // Only update if scroll position changed significantly if (Math.abs(scrollTop - currentScrollTop) > 50) { _this.debugLog('Scroll position changed', { from: currentScrollTop, to: scrollTop, delta: scrollTop - currentScrollTop, scroller: _this.scroller === window ? 'window' : 'custom container' }); _this.setState({ scrollTop: scrollTop }); } _this.scrollRAF = null; }); }); _defineProperty(_this, "handleResize", function () { if (!_this.mounted) return; _this.debugLog('Window resize detected'); _this.scheduleLayout(); }); _defineProperty(_this, "scheduleLayout", function () { if (_this.layoutRequestId) { cancelAnimationFrame(_this.layoutRequestId); } _this.layoutRequestId = requestAnimationFrame(function () { _this.updateLayout(); _this.layoutRequestId = null; }); }); _defineProperty(_this, "updateLayout", function () { if (!_this.mounted) return; var _this$props = _this.props, size = _this$props.size, children = _this$props.children, columnWidth = _this$props.columnWidth, gutterWidth = _this$props.gutterWidth, gutterHeight = _this$props.gutterHeight; var containerWidth = size === null || size === void 0 ? void 0 : size.width; if (!containerWidth || containerWidth <= 0) return; var validChildren = _react["default"].Children.toArray(children).filter(_react.isValidElement); if (validChildren.length === 0) { _this.setState({ rects: [], height: 0 }); return; } try { var _getColumnConfig = getColumnConfig(containerWidth, columnWidth, gutterWidth), columnCount = _getColumnConfig.columnCount, actualColumnWidth = _getColumnConfig.columnWidth; var columnHeights = new Array(columnCount).fill(0); var rects = validChildren.map(function (child) { // Find shortest column var shortestColumnIndex = getShortestColumn(columnHeights); // Get cached height or use default var itemHeight = _this.heightCache.get(child.key) || 200; // Calculate position var left = shortestColumnIndex * (actualColumnWidth + gutterWidth); var top = columnHeights[shortestColumnIndex]; // Update column height columnHeights[shortestColumnIndex] = top + itemHeight + gutterHeight; // Store column assignment for efficient updates _this.columnAssignments.set(child.key, shortestColumnIndex); return { top: top, left: left, width: actualColumnWidth, height: itemHeight }; }); var height = Math.max.apply(Math, _toConsumableArray(columnHeights)) - gutterHeight; _this.debugLog('Layout calculated', { columns: columnCount, items: validChildren.length, containerWidth: containerWidth, columnWidth: actualColumnWidth, totalHeight: height, heightCacheSize: _this.heightCache.size }); _this.setState({ rects: rects, height: height }, function () { var onLayout = _this.props.onLayout; if (typeof onLayout === 'function') { onLayout({ height: height }); } }); } catch (error) { console.error('Layout calculation error:', error); } }); _defineProperty(_this, "handleHeightChange", function (key, height) { var oldHeight = _this.heightCache.get(key); if (oldHeight !== height) { _this.debugLog('Item height changed', { key: key, from: oldHeight, to: height, delta: height - (oldHeight || 0) }); _this.heightCache.set(key, height); // Update the layout to push other cards down _this.updateLayoutForHeightChange(key, oldHeight, height); } }); _defineProperty(_this, "updateLayoutForHeightChange", function (changedKey, oldHeight, newHeight) { if (!_this.mounted) return; var _this$props2 = _this.props, children = _this$props2.children, columnWidth = _this$props2.columnWidth, gutterWidth = _this$props2.gutterWidth, gutterHeight = _this$props2.gutterHeight, size = _this$props2.size; var containerWidth = size === null || size === void 0 ? void 0 : size.width; if (!containerWidth || containerWidth <= 0) return; var validChildren = _react["default"].Children.toArray(children).filter(_react.isValidElement); var changedIndex = validChildren.findIndex(function (child) { return child.key === changedKey; }); if (changedIndex === -1) return; try { var _getColumnConfig2 = getColumnConfig(containerWidth, columnWidth, gutterWidth), columnCount = _getColumnConfig2.columnCount, actualColumnWidth = _getColumnConfig2.columnWidth; // Recalculate layout from the changed item onwards var columnHeights = new Array(columnCount).fill(0); var rects = []; // First pass: calculate positions up to the changed item for (var i = 0; i < changedIndex; i += 1) { var child = validChildren[i]; var itemHeight = _this.heightCache.get(child.key) || 200; // Find shortest column var shortestColumnIndex = getShortestColumn(columnHeights); var left = shortestColumnIndex * (actualColumnWidth + gutterWidth); var top = columnHeights[shortestColumnIndex]; columnHeights[shortestColumnIndex] = top + itemHeight + gutterHeight; rects.push({ top: top, left: left, width: actualColumnWidth, height: itemHeight }); } // Second pass: recalculate from changed item onwards for (var _i = changedIndex; _i < validChildren.length; _i += 1) { var _child = validChildren[_i]; var _itemHeight = _this.heightCache.get(_child.key) || 200; // Find shortest column var _shortestColumnIndex = getShortestColumn(columnHeights); var _left = _shortestColumnIndex * (actualColumnWidth + gutterWidth); var _top = columnHeights[_shortestColumnIndex]; columnHeights[_shortestColumnIndex] = _top + _itemHeight + gutterHeight; rects.push({ top: _top, left: _left, width: actualColumnWidth, height: _itemHeight }); } var height = Math.max.apply(Math, _toConsumableArray(columnHeights)) - gutterHeight; _this.debugLog('Layout updated for height change', { changedKey: changedKey, oldHeight: oldHeight, newHeight: newHeight, totalHeight: height }); _this.setState({ rects: rects, height: height }, function () { var onLayout = _this.props.onLayout; if (typeof onLayout === 'function') { onLayout({ height: height }); } }); } catch (error) { console.error('Layout update error:', error); } }); _this.containerRef = /*#__PURE__*/_react["default"].createRef(); _this.heightCache = new Map(); // Using Map for better performance _this.columnAssignments = new Map(); // Track which column each item belongs to _this.mounted = false; _this.layoutRequestId = null; // For debounced layout updates _this.scrollRAF = null; // For optimized scroll handling _this.lastLogTime = 0; // For throttling debug logs // Initialize scroller in constructor to prevent null access in render _this.scroller = props.scrollContainer || window; _this.state = { rects: [], height: 0, scrollTop: 0 }; return _this; } _inherits(GridInline, _Component); return _createClass(GridInline, [{ key: "componentDidMount", value: function componentDidMount() { var _size$registerRef; this.mounted = true; var _this$props3 = this.props, size = _this$props3.size, gridRef = _this$props3.gridRef, virtualized = _this$props3.virtualized, columnWidth = _this$props3.columnWidth, children = _this$props3.children, scrollContainer = _this$props3.scrollContainer; size === null || size === void 0 || (_size$registerRef = size.registerRef) === null || _size$registerRef === void 0 || _size$registerRef.call(size, this); gridRef === null || gridRef === void 0 || gridRef(this); // Update scroller if it changed this.scroller = scrollContainer || window; this.debugLog('Component mounted', { virtualized: virtualized, columnWidth: columnWidth, childrenCount: _react["default"].Children.count(children), scrollContainer: scrollContainer ? 'custom' : 'window' }, true); // Listen to scroll events only if virtualized if (virtualized) { this.scroller.addEventListener('scroll', this.handleScroll, { passive: true }); this.debugLog('Scroll listener attached (virtualized mode)', { scroller: this.scroller === window ? 'window' : 'custom container' }); } window.addEventListener('resize', this.handleResize, { passive: true }); // Initial layout this.scheduleLayout(); } }, { key: "componentDidUpdate", value: function componentDidUpdate(prevProps) { var _prevProps$size, _this2 = this; var _this$props4 = this.props, children = _this$props4.children, size = _this$props4.size, columnWidth = _this$props4.columnWidth, gutterWidth = _this$props4.gutterWidth, gutterHeight = _this$props4.gutterHeight, scrollContainer = _this$props4.scrollContainer, virtualized = _this$props4.virtualized; var childrenChanged = prevProps.children !== children; var sizeChanged = ((_prevProps$size = prevProps.size) === null || _prevProps$size === void 0 ? void 0 : _prevProps$size.width) !== (size === null || size === void 0 ? void 0 : size.width); var layoutPropsChanged = prevProps.columnWidth !== columnWidth || prevProps.gutterWidth !== gutterWidth || prevProps.gutterHeight !== gutterHeight; var scrollContainerChanged = prevProps.scrollContainer !== scrollContainer; // Handle scroll container changes if (scrollContainerChanged && this.mounted) { // Remove old scroll listener if (prevProps.virtualized) { var oldScroller = prevProps.scrollContainer || window; oldScroller.removeEventListener('scroll', this.handleScroll); } // Set up new scroll container this.scroller = scrollContainer || window; // Add new scroll listener if (virtualized) { this.scroller.addEventListener('scroll', this.handleScroll, { passive: true }); this.debugLog('Scroll container changed', { from: prevProps.scrollContainer ? 'custom' : 'window', to: scrollContainer ? 'custom' : 'window' }); } } // Clean up height cache for removed children if (childrenChanged) { var currentKeys = new Set(_react["default"].Children.toArray(children).filter(_react.isValidElement).map(function (child) { return child.key; })); this.heightCache.forEach(function (value, key) { if (!currentKeys.has(key)) { _this2.heightCache["delete"](key); _this2.columnAssignments["delete"](key); } }); } if (childrenChanged || sizeChanged || layoutPropsChanged) { var _prevProps$size2; this.debugLog('Layout update triggered', { childrenChanged: childrenChanged, sizeChanged: sizeChanged ? "".concat((_prevProps$size2 = prevProps.size) === null || _prevProps$size2 === void 0 ? void 0 : _prevProps$size2.width, " \u2192 ").concat(size === null || size === void 0 ? void 0 : size.width) : false, layoutPropsChanged: layoutPropsChanged, newChildrenCount: _react["default"].Children.count(children) }); this.scheduleLayout(); } } }, { key: "componentWillUnmount", value: function componentWillUnmount() { var _size$unregisterRef; this.mounted = false; var _this$props5 = this.props, size = _this$props5.size, virtualized = _this$props5.virtualized; size === null || size === void 0 || (_size$unregisterRef = size.unregisterRef) === null || _size$unregisterRef === void 0 || _size$unregisterRef.call(size, this); this.debugLog('Component unmounting', null, true); if (virtualized && this.scroller) { this.scroller.removeEventListener('scroll', this.handleScroll); } window.removeEventListener('resize', this.handleResize); if (this.layoutRequestId) { cancelAnimationFrame(this.layoutRequestId); } if (this.scrollRAF) { cancelAnimationFrame(this.scrollRAF); } } }, { key: "render", value: function render() { var _this3 = this; var _this$props6 = this.props, className = _this$props6.className, style = _this$props6.style, ElementType = _this$props6.component, itemComponent = _this$props6.itemComponent, children = _this$props6.children, rtl = _this$props6.rtl, virtualized = _this$props6.virtualized; var _this$state = this.state, rects = _this$state.rects, height = _this$state.height, scrollTop = _this$state.scrollTop; var validChildren = _react["default"].Children.toArray(children).filter(_react.isValidElement); var containerStyle = _objectSpread({ position: 'relative', height: height }, style); var renderedItems = validChildren; var virtualizedCount = 0; // Optimized virtualization if (virtualized && this.scroller) { var virtualizationBuffer = this.props.virtualizationBuffer; // Get viewport height from the appropriate container var viewportHeight = this.scroller === window ? window.innerHeight : this.scroller.clientHeight; var visibleTop = scrollTop - virtualizationBuffer; var visibleBottom = scrollTop + viewportHeight + virtualizationBuffer; var beforeCount = renderedItems.length; renderedItems = validChildren.filter(function (child, index) { var rect = rects[index]; if (!rect) return false; var itemTop = rect.top; var itemBottom = itemTop + rect.height; return itemBottom >= visibleTop && itemTop <= visibleBottom; }); virtualizedCount = beforeCount - renderedItems.length; if (virtualizedCount > 0) { this.debugLog('Virtualization active', { total: validChildren.length, rendered: renderedItems.length, hidden: virtualizedCount, scrollTop: scrollTop, visibleRange: [visibleTop, visibleBottom], viewportHeight: viewportHeight, scroller: this.scroller === window ? 'window' : 'custom container' }); } } var gridItems = renderedItems.map(function (child) { var originalIndex = validChildren.indexOf(child); var rect = rects[originalIndex]; if (!rect) return null; return /*#__PURE__*/_react["default"].createElement(GridItem, { key: child.key, itemKey: child.key, component: itemComponent, rect: rect, rtl: rtl, onHeightChange: function onHeightChange(itemHeight) { return _this3.handleHeightChange(child.key, itemHeight); } }, child); }); return /*#__PURE__*/_react["default"].createElement(ElementType, { className: className, style: containerStyle, ref: this.containerRef }, gridItems); } }]); }(_react.Component); // Move propTypes and defaultProps outside the class GridInline.propTypes = GridInlinePropTypes; GridInline.defaultProps = GridInlineDefaultProps; var StackGridPropTypes = { children: _propTypes["default"].node, className: _propTypes["default"].string, style: _propTypes["default"].shape({}), gridRef: _propTypes["default"].func, component: _propTypes["default"].string, itemComponent: _propTypes["default"].string, columnWidth: _propTypes["default"].oneOfType([_propTypes["default"].number, _propTypes["default"].string]).isRequired, gutterWidth: _propTypes["default"].number, gutterHeight: _propTypes["default"].number, onLayout: _propTypes["default"].func, rtl: _propTypes["default"].bool, virtualized: _propTypes["default"].bool, debug: _propTypes["default"].bool, size: _propTypes["default"].shape({ width: _propTypes["default"].number, height: _propTypes["default"].number, registerRef: _propTypes["default"].func, unregisterRef: _propTypes["default"].func }), scrollContainer: _propTypes["default"].instanceOf(HTMLElement) }; var StackGridDefaultProps = { children: null, className: '', style: {}, gridRef: null, component: 'div', itemComponent: 'div', gutterWidth: 5, gutterHeight: 5, onLayout: null, rtl: false, virtualized: false, debug: false, size: null, scrollContainer: null }; var StackGrid = /*#__PURE__*/function (_Component2) { function StackGrid(props) { var _this4; _classCallCheck(this, StackGrid); _this4 = _callSuper(this, StackGrid, [props]); // eslint-disable-next-line no-unused-vars _defineProperty(_this4, "handleRef", function (gridInlineInstance) { // eslint-disable-next-line no-unused-vars _this4.grid = gridInlineInstance; // Store reference for potential future use var gridRef = _this4.props.gridRef; gridRef === null || gridRef === void 0 || gridRef(_this4); }); _this4.grid = null; // Used for gridRef callback return _this4; } _inherits(StackGrid, _Component2); return _createClass(StackGrid, [{ key: "render", value: function render() { var _this$props7 = this.props, gridRef = _this$props7.gridRef, children = _this$props7.children, restOfProps = _objectWithoutProperties(_this$props7, _excluded2); return /*#__PURE__*/_react["default"].createElement(GridInline, _extends({}, restOfProps, { gridRef: this.handleRef }), children); } }]); }(_react.Component); // Move propTypes and defaultProps outside the class StackGrid.propTypes = StackGridPropTypes; StackGrid.defaultProps = StackGridDefaultProps; var _default = exports["default"] = (0, _reactSizeme["default"])({ monitorHeight: false, monitorWidth: true })(StackGrid);