@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
JavaScript
;
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);