UNPKG

@douyinfe/semi-ui

Version:

A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.

326 lines (325 loc) 12.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _get2 = _interopRequireDefault(require("lodash/get")); var _isFunction2 = _interopRequireDefault(require("lodash/isFunction")); var _isEqual2 = _interopRequireDefault(require("lodash/isEqual")); var _react = _interopRequireDefault(require("react")); var _classnames = _interopRequireDefault(require("classnames")); var _baseComponent = _interopRequireDefault(require("../_base/baseComponent")); var _propTypes = _interopRequireDefault(require("prop-types")); var _constants = require("@douyinfe/semi-foundation/lib/cjs/overflowList/constants"); var _resizeObserver = _interopRequireDefault(require("../resizeObserver")); var _intersectionObserver = _interopRequireDefault(require("./intersectionObserver")); var _foundation = _interopRequireDefault(require("@douyinfe/semi-foundation/lib/cjs/overflowList/foundation")); require("@douyinfe/semi-foundation/lib/cjs/overflowList/overflowList.css"); var _utils = require("../_utils"); var _fastCopy = _interopRequireDefault(require("fast-copy")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const prefixCls = _constants.cssClasses.PREFIX; const Boundary = _constants.strings.BOUNDARY_MAP; const OverflowDirection = _constants.strings.OVERFLOW_DIR; const RenderMode = _constants.strings.MODE_MAP; // reference to https://github.com/palantir/blueprint/blob/1aa71605/packages/core/src/components/overflow-list/overflowList.tsx#L34 class OverflowList extends _baseComponent.default { constructor(props) { var _this; super(props); _this = this; this.scroller = null; this.spacer = null; this.isScrollMode = () => { const { renderMode } = this.props; return renderMode === RenderMode.SCROLL; }; this.resize = function () { let entries = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; var _a; const containerWidth = (_a = entries[0]) === null || _a === void 0 ? void 0 : _a.target.clientWidth; _this.setState({ containerWidth, overflowStatus: 'calculating' }); }; this.reintersect = entries => { this.foundation.handleIntersect(entries); }; this.mergeRef = (ref, node, key) => { this.itemRefs[key] = node; if (typeof ref === 'function') { ref(node); } else if (typeof ref === 'object' && ref && 'current' in ref) { ref.current = node; } }; this.renderOverflow = () => { const overflow = this.foundation.getOverflowItem(); return this.props.overflowRenderer(overflow); }; this.getItemKey = (item, defaultKey) => { const { itemKey } = this.props; if ((0, _isFunction2.default)(itemKey)) { return itemKey(item); } return (0, _get2.default)(item, itemKey || 'key', defaultKey); }; this.renderItemList = () => { const { className, wrapperClassName, wrapperStyle, style, visibleItemRenderer, renderMode, collapseFrom } = this.props; const { visible, overflowStatus } = this.state; let overflow = this.renderOverflow(); if (!this.isScrollMode()) { if (Array.isArray(overflow)) { overflow = /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, overflow); } if (/*#__PURE__*/_react.default.isValidElement(overflow)) { const child = /*#__PURE__*/_react.default.cloneElement(overflow); overflow = /*#__PURE__*/_react.default.createElement(_resizeObserver.default, { onResize: _ref => { let [entry] = _ref; this.setState({ overflowWidth: entry.target.clientWidth, overflowStatus: 'calculating' }); } }, /*#__PURE__*/_react.default.createElement("div", { className: `${prefixCls}-overflow` }, child)); } } const inner = renderMode === RenderMode.SCROLL ? (() => { const list = [/*#__PURE__*/_react.default.createElement("div", { className: (0, _classnames.default)(wrapperClassName, `${prefixCls}-scroll-wrapper`), ref: ref => { this.scroller = ref; }, style: Object.assign({}, wrapperStyle), key: `${prefixCls}-scroll-wrapper` }, visible.map(visibleItemRenderer).map(item => { const { forwardRef, key } = item; return /*#__PURE__*/_react.default.cloneElement(item, { ref: node => this.mergeRef(forwardRef, node, key), 'data-scrollkey': `${key}`, key }); }))]; if (this.props.overflowRenderDirection === "both") { list.unshift(overflow[0]); list.push(overflow[1]); } else if (this.props.overflowRenderDirection === "start") { list.unshift(overflow[1]); list.unshift(overflow[0]); } else { list.push(overflow[0]); list.push(overflow[1]); } return list; })() : [collapseFrom === Boundary.START ? overflow : null, visible.map((item, idx) => { const { key } = item; const element = visibleItemRenderer(item, idx); const child = /*#__PURE__*/_react.default.cloneElement(element); return /*#__PURE__*/_react.default.createElement(_resizeObserver.default, { key: key !== null && key !== void 0 ? key : idx, onResize: _ref2 => { let [entry] = _ref2; return this.onItemResize(entry, item, idx); } }, /*#__PURE__*/_react.default.createElement("div", { key: key !== null && key !== void 0 ? key : idx, className: `${prefixCls}-item` }, child)); }), collapseFrom === Boundary.END ? overflow : null]; const list = /*#__PURE__*/_react.default.createElement('div', { className: (0, _classnames.default)(`${prefixCls}`, className), style: Object.assign(Object.assign({}, style), renderMode === RenderMode.COLLAPSE ? { maxWidth: '100%', visibility: overflowStatus === "calculating" ? "hidden" : "visible" } : null) }, ...inner); return list; }; this.onItemResize = (entry, item, idx) => { const key = this.getItemKey(item, idx); const width = this.itemSizeMap.get(key); if (!width) { this.itemSizeMap.set(key, entry.target.clientWidth); } else if (width !== entry.target.clientWidth) { // 某个item发生resize后,重新计算 this.itemSizeMap.set(key, entry.target.clientWidth); this.setState({ overflowStatus: 'calculating' }); } const { maxCount } = this.state; // 已经按照最大值maxCount渲染完毕,触发真正的渲染 // Already rendered maxCount items, trigger the real rendering if (this.itemSizeMap.size === maxCount) { this.setState({ overflowStatus: 'calculating' }); } }; this.state = { direction: OverflowDirection.GROW, lastOverflowCount: 0, overflow: [], visible: [], containerWidth: 0, visibleState: new Map(), itemSizeMap: new Map(), overflowStatus: "calculating", pivot: -1, overflowWidth: 0, maxCount: 0 }; this.foundation = new _foundation.default(this.adapter); this.previousWidths = new Map(); this.itemRefs = {}; this.itemSizeMap = new Map(); } static getDerivedStateFromProps(props, prevState) { const { prevProps } = prevState; const newState = {}; newState.prevProps = props; const needUpdate = name => { return !prevProps && name in props || prevProps && !(0, _isEqual2.default)(prevProps[name], props[name]); }; if (needUpdate('items') || needUpdate('style')) { // reset visible state if the above props change. newState.direction = OverflowDirection.GROW; newState.lastOverflowCount = 0; newState.maxCount = 0; if (props.renderMode === RenderMode.SCROLL) { newState.visible = props.items; newState.overflow = []; } else { let maxCount = props.items.length; if (Math.floor(prevState.containerWidth / _constants.numbers.MINIMUM_HTML_ELEMENT_WIDTH) !== 0) { maxCount = Math.min(maxCount, Math.floor(prevState.containerWidth / _constants.numbers.MINIMUM_HTML_ELEMENT_WIDTH)); } const isCollapseFromStart = props.collapseFrom === Boundary.START; const visible = isCollapseFromStart ? (0, _fastCopy.default)(props.items).reverse().slice(0, maxCount) : props.items.slice(0, maxCount); const overflow = isCollapseFromStart ? (0, _fastCopy.default)(props.items).reverse().slice(maxCount) : props.items.slice(maxCount); newState.visible = visible; newState.overflow = overflow; newState.maxCount = maxCount; } newState.pivot = -1; newState.overflowStatus = "calculating"; } return newState; } get adapter() { return Object.assign(Object.assign({}, super.adapter), { updateVisibleState: visibleState => { this.setState({ visibleState }, () => { var _a, _b; (_b = (_a = this.props).onVisibleStateChange) === null || _b === void 0 ? void 0 : _b.call(_a, visibleState); }); }, updateStates: states => { this.setState(Object.assign({}, states)); }, notifyIntersect: res => { this.props.onIntersect && this.props.onIntersect(res); }, getItemSizeMap: () => this.itemSizeMap }); } componentDidUpdate(prevProps, prevState) { const prevItemsKeys = prevProps.items.map(item => item.key); const nowItemsKeys = this.props.items.map(item => item.key); // Determine whether to update by comparing key values if (!(0, _isEqual2.default)(prevItemsKeys, nowItemsKeys)) { this.itemRefs = {}; this.setState({ visibleState: new Map() }); } const { overflow, containerWidth, visible, overflowStatus } = this.state; if (this.isScrollMode() || overflowStatus !== "calculating") { return; } this.foundation.handleCollapseOverflow(); } render() { const list = this.renderItemList(); const { renderMode } = this.props; if (renderMode === RenderMode.SCROLL) { return /*#__PURE__*/_react.default.createElement(_intersectionObserver.default, { onIntersect: this.reintersect, root: this.scroller, threshold: this.props.threshold, items: this.itemRefs }, list); } return /*#__PURE__*/_react.default.createElement(_resizeObserver.default, { onResize: this.resize }, list); } } OverflowList.__SemiComponentName__ = "OverflowList"; OverflowList.defaultProps = (0, _utils.getDefaultPropsFromGlobalConfig)(OverflowList.__SemiComponentName__, { collapseFrom: 'end', minVisibleItems: 0, overflowRenderer: () => null, renderMode: 'collapse', threshold: 0.75, visibleItemRenderer: () => null, onOverflow: () => null, overflowRenderDirection: "both" }); OverflowList.propTypes = { // if render in scroll mode, key is required in items className: _propTypes.default.string, collapseFrom: _propTypes.default.oneOf(_constants.strings.BOUNDARY_SET), direction: _propTypes.default.oneOf(_constants.strings.POSITION_SET), items: _propTypes.default.array, minVisibleItems: _propTypes.default.number, onIntersect: _propTypes.default.func, onOverflow: _propTypes.default.func, overflowRenderer: _propTypes.default.func, renderMode: _propTypes.default.oneOf(_constants.strings.MODE_SET), style: _propTypes.default.object, threshold: _propTypes.default.number, visibleItemRenderer: _propTypes.default.func, wrapperClassName: _propTypes.default.string, wrapperStyle: _propTypes.default.object, collapseMask: _propTypes.default.object, overflowRenderDirection: _propTypes.default.string }; var _default = exports.default = OverflowList;