UNPKG

@pluve/ace

Version:

一套React版本的 Taro UI 组件库

1,481 lines (1,314 loc) 116 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var React = require('react'); var components = require('@tarojs/components'); var runtime = require('@tarojs/runtime'); var taro = require('@tarojs/taro'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var Badge = function (_a) { var _b = _a.dot, dot = _b === void 0 ? false : _b, _c = _a.value, value = _c === void 0 ? '' : _c, _d = _a.maxValue, maxValue = _d === void 0 ? 99 : _d, children = _a.children, _e = _a.className, className = _e === void 0 ? '' : _e; // 处理value展示 var formatValue = function () { var numValue = +value; if (Number.isNaN(numValue)) { return numValue; } return numValue > maxValue ? maxValue + "+" : numValue; }; var badgeVal = formatValue(); return (React__default['default'].createElement(components.View, { className: "ace-badge " + className }, children, dot && React__default['default'].createElement(components.View, { className: "ace-badge-dot" }), !dot && badgeVal !== '' && React__default['default'].createElement(components.View, { className: "ace-badge-num" }, badgeVal))); }; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } function __spread() { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; } var Button = function (_a) { var children = _a.children, className = _a.className, restProps = __rest(_a, ["children", "className"]); return (React__default['default'].createElement(components.Button, __assign({ className: "ace-button " + className }, restProps), children)); }; /** * checkBox UI组件 author:cxl */ var selectImg = // eslint-disable-next-line max-len 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAEJUlEQVRYR7WXX2gcVRTGf2c2JrYUxQSkJRHqf2gUFJXSbBIbqUSLQYwl9h/61Fas7CapRpRCDIVCVbK7sS8NPohFC8UiLbUN1ZpqdvIgUtEWAhWsEFoUmrWxjUk2O3Nkso37Z2azs00zT7Nzv/N93z177rn3CqU+3+6tYsnS+7ClBtFKEAE7gcgoydRFmt66Ugql+ALPii5pgcCLoKsRWQ7kxyqqfwI/AkeZnDjGuvfGivHPb+BEXwV32jsQox24txhZ3vhFVCOMSz/rQ9OFYgsbiPfWYpTtB9aWKJwLVx0kZe/k6Y4RLx5vA0O96zACnyGyYkHic8HKZWxrCw0dZ/L53Abi0Wcx5EuQO26J+P8m9CpKK/XhwWzeXAPx6MMYhuPSKbJFePQSltVIQ+fvc+QZA4e7y6muOonwzCIoZ1HqANOJFpp6Us7HjAEzug0x+hdX/Aa76qsEwwczBga7l1FReRbkwVtnQBMglZ58qudJJp6iqWcqnYF4pBUjcOTWiOskar9BcmaA8orNwD5EylzctrWe+o6TaQNm7BAiGxduQCdQfY1ge2YyZjQERi9CIIdf9ROC4W1COv3nQFYuyIDqNVS3Ut9+LIdnlr/qD6Aqr0ONcFUeF37oW0WZ/gxSvgAD49i6mfrwCRfHcGwnKjFXBtAJpu1HBKfrBcq+KSiufArWQTCaEely4ZS/gY0EQ6dcY/HY6wgfe9YAKJa9RhjqayFAbtoyTPupC4VwwOla6UTko6zlO4ZabQQ7vnOLR9/EkBiIUXBylj4vDEebwRjwnNm11AM815nIGRuOhUGiqF7Bkg00hr73SLuDiXhs2XlQu0UwI2uQwLDbpSZRu5Vgx9euMadp2fqb1+aCGd2FGB8WFwfsVKMwGFlJeeAcwjKPVI2BvkJd+LSvAjVjXYjs84WFf7FSjwrpPeAsQq1315qnyLID4rF3MWSvT3GnrG4sw3RxHUBk+zzB49jWJqdzeWLM6G7E2ONf3NGfa0RO1OwZwHAvo9zW9Q/oFuraj+d8NmPdiLxfkrgDVpqdpZtuxem/wUR4cl4i5TroVoLho+ll2bcHYfdNiP/EpbEgbT3JzHbsd0NSphD9HLgbpKVkcSfA0lYawl85rxkD3d0GzZXOUeylmyL1G6R6hFOJNnp67FwDzq/BvhrK9Qwi9/vlKwmnXEAnm6h/5/JcnPtQakYeQ4zjINUlkRcDK6NY9gs0tv+aDS1wLI89gSFfIDxUjNffuI5gpTbRsOuXfHzhi8np3mpuD0QR2eBPpABKOURyqpOmLufa5nqK3w3N6MuIvA2yukQjJrb9geuAkkdS3IATcGD7bdSuWotIG0IdUONxcRlHdRQhjs1hZhJDc0fv+Yz7M5DN4DStFXctR/UeDFk6OyR6nWRglAvn/2JH/0wpmfoP49JvBuSf9PgAAAAASUVORK5CYII='; var noSelectImg = // eslint-disable-next-line max-len 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACvElEQVRYR82XTWsTURSG3zNNWimSFlsa7VrQnYLBhSJUpTPJZLIolYh/wVbRlaBWq1XBlaKtf0EMliySzGQmaAXRhUTQnYLrakortmixTTtH7sSUmH5MQhbXWc6957zPzPm45xIkP9SofrFYDJZKCxoTVBCOMGM/AV3CnoFFInwB4z0xnHC4x45EIuVGfPsCpNMz3YH21UtEPAJQbyNOAZ5npqm11eWHQ0NDP3ay2RHANO0zLmgS4LDnhPARTNNM9DpI65+XlpYWxOtQKNRT5rYDxHwCxMNgHKqIUkkBj+q69nw7iC0BmJlytjMBF1c9WaCoAFd0XXvZyB8wTfuUC9wHEPEipOBeXFPHiIjr7TcBCPGs5Twl4CyAMil0WdcGn2xlvBOM8GPahfPs8gMAQQaeGTH1XL2fTQDZvH0HLq4R8F0hZTgWG3zVyFdvt8eyCgMuu9MM7IGCu0ZUu1679x+ASsyRArDWRoraqnhVSECss+sACChAsjYnNgBEtgc7yp9EwpFCo/GoOtXKl9fb5vLOCLs8KRKzvPLrYLU6NgAyOXucCDdFwsVj6tFmY+4H6yW25bwTicmMW4m4Nl4pLKFYLAa/zc3PijpXgNONZrufaP363+p4IfrE3r7eftGsPIBczjaYkBF1bsS0w806bmZ/1rI/iD5BjEQ8rmU9gKxpPwJwAaAbhq5ONOOw2b1Z0xkD+DaAx4auXawAWPYbMI4xKScTLZadH1DGKgwQuzMgvDVi2nEPIGPaJQL6Agr3R6PRr35OWlnP5/P71lyaZWAuoWvhagh+A+hY/rnYkUwmV1sR8LNNpVLtnbu7VgCsGLq26/8AkB8C2UkovQylNyLprdjrBTIPIwEg/TgWEFIHkmoHkzqSCQjpQ2kVQtpYXnuYSLuY1EKk0+nuQHunnKtZLYi0y6nf+d7q+h/kmO0w5BgVcgAAAABJRU5ErkJggg=='; var CheckBox = function (_a) { var _b = _a.value, value = _b === void 0 ? [] : _b, type = _a.type, _c = _a.selectedValue, selectedValue = _c === void 0 ? [] : _c; var flag = false; if (type === 'all') { // 全选 flag = JSON.stringify(value) === JSON.stringify(selectedValue) && selectedValue.length > 0; } else { // 单选 flag = selectedValue.indexOf(value[0]) !== -1; } return (React__default['default'].createElement(components.View, { className: "ace-checkbox-wrap" }, React__default['default'].createElement(components.Image, { src: flag ? selectImg : noSelectImg, className: "ace-checkbox-select" }))); }; function createCommonjsModule(fn, basedir, module) { return module = { path: basedir, exports: {}, require: function (path, base) { return commonjsRequire(path, (base === undefined || base === null) ? module.path : base); } }, fn(module, module.exports), module.exports; } function commonjsRequire () { throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs'); } // https://github.com/alexreardon/memoize-one#readme function areInputsEqual (newInputs, lastInputs) { if (newInputs.length !== lastInputs.length) { return false } for (let i = 0; i < newInputs.length; i++) { if (newInputs[i] !== lastInputs[i]) { return false } } return true } function memoizeOne (resultFn, isEqual) { // eslint-disable-next-line no-void if (isEqual === void 0) { isEqual = areInputsEqual; } let lastThis; let lastArgs = []; let lastResult; let calledOnce = false; function memoized () { const newArgs = []; for (let _i = 0; _i < arguments.length; _i++) { newArgs[_i] = arguments[_i]; } if (calledOnce && lastThis === this && isEqual(newArgs, lastArgs)) { return lastResult } lastResult = resultFn.apply(this, newArgs); calledOnce = true; lastThis = this; lastArgs = newArgs; return lastResult } return memoized } function cancelTimeout (timeoutID) { runtime.cancelAnimationFrame(timeoutID.id); } function requestTimeout (callback, delay) { const start = runtime.now(); function tick () { if (runtime.now() - start >= delay) { // eslint-disable-next-line no-useless-call callback.call(null); } else { timeoutID.id = runtime.requestAnimationFrame(tick); } } const timeoutID = { id: runtime.requestAnimationFrame(tick) }; return timeoutID } let cachedRTLResult = null; // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements. // Chrome does not seem to adhere; its scrollLeft values are positive (measured relative to the left). // Safari's elastic bounce makes detecting this even more complicated wrt potential false positives. // The safest way to check this is to intentionally set a negative offset, // and then verify that the subsequent "scroll" event matches the negative offset. // If it does not match, then we can assume a non-standard RTL scroll implementation. function getRTLOffsetType (recalculate = false) { if (cachedRTLResult === null || recalculate) { const outerDiv = document.createElement('div'); const outerStyle = outerDiv.style; outerStyle.width = '50px'; outerStyle.height = '50px'; outerStyle.overflow = 'scroll'; outerStyle.direction = 'rtl'; const innerDiv = document.createElement('div'); const innerStyle = innerDiv.style; innerStyle.width = '100px'; innerStyle.height = '100px'; outerDiv.appendChild(innerDiv); document.body.appendChild(outerDiv); if (outerDiv.scrollLeft > 0) { cachedRTLResult = 'positive-descending'; } else { outerDiv.scrollLeft = 1; if (outerDiv.scrollLeft === 0) { cachedRTLResult = 'negative'; } else { cachedRTLResult = 'positive-ascending'; } } document.body.removeChild(outerDiv); return cachedRTLResult } return cachedRTLResult } const IS_SCROLLING_DEBOUNCE_INTERVAL = 150; const defaultItemKey = (index) => index; function createListComponent ({ getItemOffset, getEstimatedTotalSize, getItemSize, getOffsetForIndexAndAlignment, getStartIndexForOffset, getStopIndexForStartIndex, initInstanceProps, shouldResetStyleCacheOnItemSizeChange, Vue }) { return { props: { direction: { type: String, default: 'ltr' }, itemData: Array, layout: { type: String, default: 'vertical' }, useIsScrolling: { type: Boolean, default: false }, overscanCount: { type: Number, default: 1 }, wclass: String, height: {}, innerRef: String, innerElementType: { type: String, default: 'view' }, itemCount: Number, wstyle: String, width: String, itemSize: { required: true }, item: { required: true }, initialScrollOffset: { type: String, defalt: 0 }, scrollNative: Function }, data () { return { instance: this, isScrolling: false, scrollDirection: 'forward', scrollOffset: typeof this.$props.initialScrollOffset === 'number' ? this.$props.initialScrollOffset : 0, scrollUpdateWasRequested: false, resetIsScrollingTimeoutId: null } }, methods: { _instanceProps () { initInstanceProps(this.$props, this); }, scrollTo (scrollOffset) { scrollOffset = Math.max(0, scrollOffset); if (this.scrollOffset === scrollOffset) { return } this.scrollDirection = this.scrollOffset < scrollOffset ? 'forward' : 'backward'; this.scrollOffset = scrollOffset; this.scrollUpdateWasRequested = true; Vue.nextTick(this._resetIsScrollingDebounced); }, scrollToItem (index, align = 'auto') { const { itemCount } = this.$props; const { scrollOffset } = this.$data; index = Math.max(0, Math.min(index, itemCount - 1)); this.scrollTo( getOffsetForIndexAndAlignment( this.$props, index, align, scrollOffset, this._instanceProps() ) ); }, _callOnItemsRendered: memoizeOne( function ( overscanStartIndex, overscanStopIndex, visibleStartIndex, visibleStopIndex ) { return this.$props.onItemsRendered({ overscanStartIndex, overscanStopIndex, visibleStartIndex, visibleStopIndex }) } ), _callOnScroll: memoizeOne( function ( scrollDirection, scrollOffset, scrollUpdateWasRequested ) { this.$emit('scroll', { scrollDirection, scrollOffset, scrollUpdateWasRequested }); } ), _callPropsCallbacks () { if (typeof this.$props.onItemsRendered === 'function') { const { itemCount } = this.$props; if (itemCount > 0) { const [ overscanStartIndex, overscanStopIndex, visibleStartIndex, visibleStopIndex ] = this._getRangeToRender(); this._callOnItemsRendered( overscanStartIndex, overscanStopIndex, visibleStartIndex, visibleStopIndex ); } } this._callOnScroll( this.scrollDirection, this.scrollOffset, this.scrollUpdateWasRequested ); }, _getStyleValue (value) { return typeof value === 'number' ? value + 'px' : value == null ? '' : value }, _getItemStyle (index) { const { direction, itemSize, layout } = this.$props; const itemStyleCache = this._getItemStyleCache( shouldResetStyleCacheOnItemSizeChange && itemSize, shouldResetStyleCacheOnItemSizeChange && layout, shouldResetStyleCacheOnItemSizeChange && direction ); let style; if (itemStyleCache.hasOwnProperty(index)) { style = itemStyleCache[index]; } else { const offset = getItemOffset(this.$props, index, this._instanceProps()); const size = getItemSize(this.$props, index, this._instanceProps()); // TODO Deprecate direction "horizontal" const isHorizontal = direction === 'horizontal' || layout === 'horizontal'; const isRtl = direction === 'rtl'; const offsetHorizontal = isHorizontal ? offset : 0; itemStyleCache[index] = style = { position: 'absolute', left: isRtl ? undefined : offsetHorizontal, right: isRtl ? offsetHorizontal : undefined, top: !isHorizontal ? offset : 0, height: !isHorizontal ? size : '100%', width: isHorizontal ? size : '100%' }; } for (const k in style) { if (style.hasOwnProperty(k)) { style[k] = this._getStyleValue(style[k]); } } return style }, _getItemStyleCache: memoizeOne(() => ({})), _getRangeToRender () { const { itemCount, overscanCount } = this.$props; const { isScrolling, scrollDirection, scrollOffset } = this.$data; if (itemCount === 0) { return [0, 0, 0, 0] } const startIndex = getStartIndexForOffset( this.$props, scrollOffset, this._instanceProps() ); const stopIndex = getStopIndexForStartIndex( this.$props, startIndex, scrollOffset, this._instanceProps() ); // Overscan by one item in each direction so that tab/focus works. // If there isn't at least one extra item, tab loops back around. const overscanBackward = !isScrolling || scrollDirection === 'backward' ? Math.max(1, overscanCount) : 1; const overscanForward = !isScrolling || scrollDirection === 'forward' ? Math.max(1, overscanCount) : 1; return [ Math.max(0, startIndex - overscanBackward), Math.max(0, Math.min(itemCount - 1, stopIndex + overscanForward)), startIndex, stopIndex ] }, _onScrollHorizontal (event) { const clientWidth = this.$props.width; const { scrollLeft, scrollWidth } = event.currentTarget; if (this.$props.scrollNative) { this.$props.scrollNative(event); } if (this.scrollOffset === scrollLeft) { return } const { direction } = this.$props; let scrollOffset = scrollLeft; if (direction === 'rtl') { // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements. // This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left). // It's also easier for this component if we convert offsets to the same format as they would be in for ltr. // So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it. switch (getRTLOffsetType()) { case 'negative': scrollOffset = -scrollLeft; break case 'positive-descending': scrollOffset = scrollWidth - clientWidth - scrollLeft; break } } // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds. scrollOffset = Math.max( 0, Math.min(scrollOffset, scrollWidth - clientWidth) ); this.isScrolling = true; this.scrollDirection = this.scrollOffset < scrollLeft ? 'forward' : 'backward'; this.scrollOffset = scrollOffset; this.scrollUpdateWasRequested = false; Vue.nextTick(this._resetIsScrollingDebounced); }, _onScrollVertical (event) { const clientHeight = this.$props.height; const { scrollHeight, scrollTop } = event.currentTarget; if (this.$props.scrollNative) { this.$props.scrollNative(event); } if (this.scrollOffset === scrollTop) { return } // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds. const scrollOffset = Math.max( 0, Math.min(scrollTop, scrollHeight - clientHeight) ); this.isScrolling = true; this.scrollDirection = this.scrollOffset < scrollOffset ? 'forward' : 'backward'; this.scrollOffset = scrollOffset; this.scrollUpdateWasRequested = false; Vue.nextTick(this._resetIsScrollingDebounced); }, _resetIsScrollingDebounced () { if (this.resetIsScrollingTimeoutId !== null) { cancelTimeout(this.resetIsScrollingTimeoutId); } this.resetIsScrollingTimeoutId = requestTimeout( this._resetIsScrolling, IS_SCROLLING_DEBOUNCE_INTERVAL ); }, _resetIsScrolling () { this.resetIsScrollingTimeoutId = null; this.isScrolling = false; Vue.nextTick(() => { this._getItemStyleCache(-1, null); }); } }, mounted () { const { direction, initialScrollOffset, layout } = this.$props; if (typeof initialScrollOffset === 'number' && this._outerRef != null) { const outerRef = this._outerRef; // TODO Deprecate direction "horizontal" if (direction === 'horizontal' || layout === 'horizontal') { outerRef.scrollLeft = initialScrollOffset; } else { outerRef.scrollTop = initialScrollOffset; } } this._callPropsCallbacks(); }, updated () { const { direction, layout } = this.$props; const { scrollOffset, scrollUpdateWasRequested } = this.$data; if (scrollUpdateWasRequested && this._outerRef != null) { const outerRef = this._outerRef; // TODO Deprecate direction "horizontal" if (direction === 'horizontal' || layout === 'horizontal') { if (direction === 'rtl') { // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements. // This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left). // So we need to determine which browser behavior we're dealing with, and mimic it. switch (getRTLOffsetType()) { case 'negative': outerRef.scrollLeft = -scrollOffset; break case 'positive-ascending': outerRef.scrollLeft = scrollOffset; break default: { const { clientWidth, scrollWidth } = outerRef; outerRef.scrollLeft = scrollWidth - clientWidth - scrollOffset; break } } } else { outerRef.scrollLeft = scrollOffset; } } else { outerRef.scrollTop = scrollOffset; } } this._callPropsCallbacks(); }, beforeDestroy () { if (this.resetIsScrollingTimeoutId !== null) { cancelTimeout(this.resetIsScrollingTimeoutId); } }, render (h) { const { item, wclass, direction, height, innerRef, innerElementType, itemCount, itemData, itemKey = defaultItemKey, layout, wstyle, useIsScrolling, width } = this.$props; const { isScrolling } = this.$data; // TODO Deprecate direction "horizontal" const isHorizontal = direction === 'horizontal' || layout === 'horizontal'; const onScroll = isHorizontal ? this._onScrollHorizontal : this._onScrollVertical; const [startIndex, stopIndex] = this._getRangeToRender(); const items = []; if (itemCount > 0) { for (let index = startIndex; index <= stopIndex; index++) { items.push( h(item, { key: itemKey(index, itemData), props: { data: itemData, index, isScrolling: useIsScrolling ? isScrolling : undefined, css: this._getItemStyle(index) } }) ); } } // Read this value AFTER items have been created, // So their actual sizes (if variable) are taken into consideration. const estimatedTotalSize = getEstimatedTotalSize( this.$props, this._instanceProps() ); return h( 'scroll-view', { class: wclass, ref: this._outerRefSetter, style: { position: 'relative', height: this._getStyleValue(height), width: this._getStyleValue(width), overflow: 'auto', WebkitOverflowScrolling: 'touch', willChange: 'transform', direction, ...wstyle }, attrs: { scrollY: layout === 'vertical', scrollX: layout === 'horizontal' }, on: { scroll: onScroll } }, [ h( innerElementType, { ref: innerRef, style: { height: this._getStyleValue(isHorizontal ? '100%' : estimatedTotalSize), pointerEvents: isScrolling ? 'none' : undefined, width: this._getStyleValue(isHorizontal ? estimatedTotalSize : '100%') } }, items ) ] ) } } } var require$$0 = { install: (Vue) => { const VirtualList = createListComponent({ Vue, getItemOffset: ({ itemSize }, index) => index * itemSize, getItemSize: ({ itemSize }) => itemSize, getEstimatedTotalSize: ({ itemCount, itemSize }) => itemSize * itemCount, getOffsetForIndexAndAlignment: ({ direction, height, itemCount, itemSize, layout, width }, index, align, scrollOffset) => { // TODO Deprecate direction "horizontal" const isHorizontal = direction === 'horizontal' || layout === 'horizontal'; const size = isHorizontal ? width : height; const lastItemOffset = Math.max(0, itemCount * itemSize - size); const maxOffset = Math.min(lastItemOffset, index * itemSize); const minOffset = Math.max(0, index * itemSize - size + itemSize); if (align === 'smart') { if (scrollOffset >= minOffset - size && scrollOffset <= maxOffset + size) { align = 'auto'; } else { align = 'center'; } } switch (align) { case 'start': return maxOffset case 'end': return minOffset case 'center': { // "Centered" offset is usually the average of the min and max. // But near the edges of the list, this doesn't hold true. const middleOffset = Math.round(minOffset + (maxOffset - minOffset) / 2); if (middleOffset < Math.ceil(size / 2)) { return 0 // near the beginning } else if (middleOffset > lastItemOffset + Math.floor(size / 2)) { return lastItemOffset // near the end } else { return middleOffset } } case 'auto': default: if (scrollOffset >= minOffset && scrollOffset <= maxOffset) { return scrollOffset } else if (scrollOffset < minOffset) { return minOffset } else { return maxOffset } } }, getStartIndexForOffset: ({ itemCount, itemSize }, offset) => { return Math.max(0, Math.min(itemCount - 1, Math.floor(offset / itemSize))) }, getStopIndexForStartIndex: ({ direction, height, itemCount, itemSize, layout, width }, startIndex, scrollOffset) => { // TODO Deprecate direction "horizontal" const isHorizontal = direction === 'horizontal' || layout === 'horizontal'; const offset = startIndex * itemSize; const size = isHorizontal ? width : height; const numVisibleItems = Math.ceil((size + scrollOffset - offset) / itemSize); return Math.max(0, Math.min(itemCount - 1, startIndex + numVisibleItems - 1 // -1 is because stop index is inclusive )) }, initInstanceProps () { // Noop }, shouldResetStyleCacheOnItemSizeChange: true }); Vue.component('virtual-list', VirtualList); } }; /* eslint-disable no-sequences */ const IS_SCROLLING_DEBOUNCE_INTERVAL$1 = 150; const defaultItemKey$1 = (index) => index; // In DEV mode, this Set helps us only log a warning once per component instance. // This avoids spamming the console every time a render happens. let devWarningsDirection = null; let devWarningsTagName = null; if (process.env.NODE_ENV !== 'production') { if (typeof window !== 'undefined' && typeof window.WeakSet !== 'undefined') { devWarningsDirection = /* #__PURE__ */ new WeakSet(); devWarningsTagName = /* #__PURE__ */ new WeakSet(); } } function createListComponent$1 ({ getItemOffset, getEstimatedTotalSize, getItemSize, getOffsetForIndexAndAlignment, getStartIndexForOffset, getStopIndexForStartIndex, initInstanceProps, shouldResetStyleCacheOnItemSizeChange, validateProps }) { let _class, _temp; return _temp = _class = class List extends React.PureComponent { // Always use explicit constructor for React components. // It produces less code after transpilation. (#26) // eslint-disable-next-line no-useless-constructor constructor (props) { super(props); this._instanceProps = initInstanceProps(this.props, this); this._outerRef = void 0; this._resetIsScrollingTimeoutId = null; this.state = { instance: this, isScrolling: false, scrollDirection: 'forward', scrollOffset: typeof this.props.initialScrollOffset === 'number' ? this.props.initialScrollOffset : 0, scrollUpdateWasRequested: false }; this._callOnItemsRendered = void 0; this._callOnItemsRendered = memoizeOne((overscanStartIndex, overscanStopIndex, visibleStartIndex, visibleStopIndex) => this.props.onItemsRendered({ overscanStartIndex, overscanStopIndex, visibleStartIndex, visibleStopIndex })); this._callOnScroll = void 0; this._callOnScroll = memoizeOne((scrollDirection, scrollOffset, scrollUpdateWasRequested) => this.props.onScroll({ scrollDirection, scrollOffset, scrollUpdateWasRequested })); this._getItemStyle = void 0; this._getStyleValue = value => { return typeof value === 'number' ? value + 'px' : value == null ? '' : value }; this._getItemStyle = index => { const { direction, itemSize, layout } = this.props; const itemStyleCache = this._getItemStyleCache(shouldResetStyleCacheOnItemSizeChange && itemSize, shouldResetStyleCacheOnItemSizeChange && layout, shouldResetStyleCacheOnItemSizeChange && direction); let style; if (itemStyleCache.hasOwnProperty(index)) { style = itemStyleCache[index]; } else { const offset = getItemOffset(this.props, index, this._instanceProps); const size = getItemSize(this.props, index, this._instanceProps); // TODO Deprecate direction "horizontal" const isHorizontal = direction === 'horizontal' || layout === 'horizontal'; const isRtl = direction === 'rtl'; const offsetHorizontal = isHorizontal ? offset : 0; itemStyleCache[index] = style = { position: 'absolute', left: isRtl ? undefined : offsetHorizontal, right: isRtl ? offsetHorizontal : undefined, top: !isHorizontal ? offset : 0, height: !isHorizontal ? size : '100%', width: isHorizontal ? size : '100%' }; } for (const k in style) { if (style.hasOwnProperty(k)) { style[k] = this._getStyleValue(style[k]); } } return style }; this._getItemStyleCache = void 0; this._getItemStyleCache = memoizeOne(() => ({})); this._onScrollHorizontal = event => { const { clientWidth, scrollLeft, scrollWidth } = event.currentTarget; this.setState(prevState => { if (prevState.scrollOffset === scrollLeft) { // Scroll position may have been updated by cDM/cDU, // In which case we don't need to trigger another render, // And we don't want to update state.isScrolling. return null } const { direction } = this.props; let scrollOffset = scrollLeft; if (direction === 'rtl') { // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements. // This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left). // It's also easier for this component if we convert offsets to the same format as they would be in for ltr. // So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it. switch (getRTLOffsetType()) { case 'negative': scrollOffset = -scrollLeft; break case 'positive-descending': scrollOffset = scrollWidth - clientWidth - scrollLeft; break } } // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds. scrollOffset = Math.max(0, Math.min(scrollOffset, scrollWidth - clientWidth)); return { isScrolling: true, scrollDirection: prevState.scrollOffset < scrollLeft ? 'forward' : 'backward', scrollOffset, scrollUpdateWasRequested: false } }, this._resetIsScrollingDebounced); }; this._onScrollVertical = event => { const { clientHeight, scrollHeight, scrollTop } = event.currentTarget; this.setState(prevState => { if (prevState.scrollOffset === scrollTop) { // Scroll position may have been updated by cDM/cDU, // In which case we don't need to trigger another render, // And we don't want to update state.isScrolling. return null } // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds. const scrollOffset = Math.max(0, Math.min(scrollTop, scrollHeight - clientHeight)); return { isScrolling: true, scrollDirection: prevState.scrollOffset < scrollOffset ? 'forward' : 'backward', scrollOffset, scrollUpdateWasRequested: false } }, this._resetIsScrollingDebounced); }; this._outerRefSetter = ref => { const { outerRef } = this.props; this._outerRef = ref; if (typeof outerRef === 'function') { outerRef(ref); } else if (outerRef != null && typeof outerRef === 'object' && outerRef.hasOwnProperty('current')) { outerRef.current = ref; } }; this._resetIsScrollingDebounced = () => { if (this._resetIsScrollingTimeoutId !== null) { cancelTimeout(this._resetIsScrollingTimeoutId); } this._resetIsScrollingTimeoutId = requestTimeout(this._resetIsScrolling, IS_SCROLLING_DEBOUNCE_INTERVAL$1); }; this._resetIsScrolling = () => { this._resetIsScrollingTimeoutId = null; this.setState({ isScrolling: false }, () => { // Clear style cache after state update has been committed. // This way we don't break pure sCU for items that don't use isScrolling param. this._getItemStyleCache(-1, null); }); }; } static getDerivedStateFromProps (nextProps, prevState) { validateSharedProps(nextProps, prevState); validateProps(nextProps); return null } scrollTo (scrollOffset) { scrollOffset = Math.max(0, scrollOffset); this.setState(prevState => { if (prevState.scrollOffset === scrollOffset) { return null } return { scrollDirection: prevState.scrollOffset < scrollOffset ? 'forward' : 'backward', scrollOffset: scrollOffset, scrollUpdateWasRequested: true } }, this._resetIsScrollingDebounced); } scrollToItem (index, align = 'auto') { const { itemCount } = this.props; const { scrollOffset } = this.state; index = Math.max(0, Math.min(index, itemCount - 1)); this.scrollTo(getOffsetForIndexAndAlignment(this.props, index, align, scrollOffset, this._instanceProps)); } componentDidMount () { const { direction, initialScrollOffset, layout } = this.props; if (typeof initialScrollOffset === 'number' && this._outerRef != null) { const outerRef = this._outerRef; // TODO Deprecate direction "horizontal" if (direction === 'horizontal' || layout === 'horizontal') { outerRef.scrollLeft = initialScrollOffset; } else { outerRef.scrollTop = initialScrollOffset; } } this._callPropsCallbacks(); } componentDidUpdate () { const { direction, layout } = this.props; const { scrollOffset, scrollUpdateWasRequested } = this.state; if (scrollUpdateWasRequested && this._outerRef != null) { const outerRef = this._outerRef; // TODO Deprecate direction "horizontal" if (direction === 'horizontal' || layout === 'horizontal') { if (direction === 'rtl') { // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements. // This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left). // So we need to determine which browser behavior we're dealing with, and mimic it. switch (getRTLOffsetType()) { case 'negative': outerRef.scrollLeft = -scrollOffset; break case 'positive-ascending': outerRef.scrollLeft = scrollOffset; break default: const { clientWidth, scrollWidth } = outerRef; outerRef.scrollLeft = scrollWidth - clientWidth - scrollOffset; break } } else { outerRef.scrollLeft = scrollOffset; } } else { outerRef.scrollTop = scrollOffset; } } this._callPropsCallbacks(); } componentWillUnmount () { if (this._resetIsScrollingTimeoutId !== null) { cancelTimeout(this._resetIsScrollingTimeoutId); } } render () { const { children, className, direction, height, innerRef, innerElementType, innerTagName, itemCount, itemData, itemKey = defaultItemKey$1, layout, outerElementType, outerTagName, style, useIsScrolling, width, ...rest } = this.props; const { isScrolling, scrollOffset, scrollUpdateWasRequested } = this.state; // TODO Deprecate direction "horizontal" const isHorizontal = direction === 'horizontal' || layout === 'horizontal'; const onScroll = isHorizontal ? this._onScrollHorizontal : this._onScrollVertical; const [startIndex, stopIndex] = this._getRangeToRender(); const items = []; if (itemCount > 0) { for (let index = startIndex; index <= stopIndex; index++) { items.push(React.createElement(children, { data: itemData, key: itemKey(index, itemData), index, isScrolling: useIsScrolling ? isScrolling : undefined, style: this._getItemStyle(index) })); } } // Read this value AFTER items have been created, // So their actual sizes (if variable) are taken into consideration. const estimatedTotalSize = getEstimatedTotalSize(this.props, this._instanceProps); const outerElementProps = { ...rest, className, onScroll, ref: this._outerRefSetter, layout, style: { position: 'relative', height: this._getStyleValue(height), width: this._getStyleValue(width), overflow: 'auto', WebkitOverflowScrolling: 'touch', willChange: 'transform', direction, ...style } }; if (scrollUpdateWasRequested) { if (isHorizontal) { outerElementProps.scrollLeft = scrollOffset; } else { outerElementProps.scrollTop = scrollOffset; } } return React.createElement(outerElementType || outerTagName || 'div', outerElementProps, React.createElement(innerElementType || innerTagName || 'div', { children: items, ref: innerRef, style: { height: this._getStyleValue(isHorizontal ? '100%' : estimatedTotalSize), pointerEvents: isScrolling ? 'none' : 'auto', width: this._getStyleValue(isHorizontal ? estimatedTotalSize : '100%') } })) } _callPropsCallbacks () { if (typeof this.props.onItemsRendered === 'function') { const { itemCount } = this.props; if (itemCount > 0) { const [overscanStartIndex, overscanStopIndex, visibleStartIndex, visibleStopIndex] = this._getRangeToRender(); this._callOnItemsRendered(overscanStartIndex, overscanStopIndex, visibleStartIndex, visibleStopIndex); } } if (typeof this.props.onScroll === 'function') { const { scrollDirection, scrollOffset, scrollUpdateWasRequested } = this.state; this._callOnScroll(scrollDirection, scrollOffset, scrollUpdateWasRequested); } } // Lazily create and cache item styles while scrolling, // So that pure component sCU will prevent re-renders. // We maintain this cache, and pass a style prop rather than index, // So that List can clear cached styles and force item re-render if necessary. _getRangeToRender () { const { itemCount, overscanCount } = this.props; const { isScrolling, scrollDirection, scrollOffset } = this.state; if (itemCount === 0) { return [0, 0, 0, 0] } const startIndex = getStartIndexForOffset(this.props, scrollOffset, this._instanceProps); const stopIndex = getStopIndexForStartIndex(this.props, startIndex, scrollOffset, this._instanceProps); // Overscan by one item in each direction so that tab/focus works. // If there isn't at least one extra item, tab loops back around. const overscanBackward = !isScrolling || scrollDirection === 'backward' ? Math.max(1, overscanCount) : 1; const overscanForward = !isScrolling || scrollDirection === 'forward' ? Math.max(1, overscanCount) : 1; return [Math.max(0, startIndex - overscanBackward), Math.max(0, Math.min(itemCount - 1, stopIndex + overscanForward)), startIndex, stopIndex] } }, _class.defaultProps = { direction: 'ltr', itemData: undefined, layout: 'vertical', overscanCount: 2, useIsScrolling: false }, _temp } // NOTE: I considered further wrapping individual items with a pure ListItem component. // This would avoid ever calling the render function for the same index more than once, // But it would also add the overhead of a lot of components/fibers. // I assume people already do this (render function returning a class component), // So my doing it would just unnecessarily double the wrappers. const validateSharedProps = ({ children, direction, height, layout, innerTagName, outerTagName, width }, { instance }) => { if (process.env.NODE_ENV !== 'production') { if (innerTagName != null || outerTagName != null) { if (devWarningsTagName && !devWarningsTagName.has(instance)) { devWarningsTagName.add(instance); console.warn('The innerTagName and outerTagName props have been deprecated. ' + 'Please use the innerElementType and outerElementType props instead.'); } } // TODO Deprecate direction "horizontal" const isHorizontal = direction === 'horizontal' || layout === 'horizontal'; switch (direction) { case 'horizontal': case 'vertical': if (devWarningsDirection && !devWarningsDirection.has(instance)) { devWarningsDirection.add(instance); console.warn('The direction prop should be either "ltr" (default) or "rtl". ' + 'Please use the layout prop to specify "vertical" (default) or "horizontal" orientation.'); } break case 'ltr': case 'rtl': // Valid values break default: throw Error('An invalid "direction" prop has been specified. ' + 'Value should be either "ltr" or "rtl". ' + `"${direction}" was specified.`) } switch (layout) { case 'horizontal': case 'vertical': // Valid values break default: throw Error('An invalid "layout" prop has been specified. ' + 'Value should be either "horizontal" or "vertical". ' + `"${layout}" was specified.`) } if (children == null) { throw Error('An invalid "children" prop has been specified. ' + 'Value should be a React component. ' + `"${children === null ? 'null' : typeof children}" was specified.`) } if (isHorizontal && typeof width !== 'number') { throw Error('An invalid "width" prop has been specified. ' + 'Horizontal lists must specify a number for width. ' + `"${width === null ? 'null' : typeof width}" was specified.`) } else if (!isHorizontal && typeof height !== 'number') { throw Error('An invalid "height" prop has been specified. ' + 'Vertical lists must specify a number for height. ' + `"${height === null ? 'null' : typeof height}" was specified.`) } } }; const FixedSizeList = /* #__PURE__ */ createListComponent$1({ getItemOffset: ({ itemSize }, index) => index * itemSize, getItemSize: ({ itemSize }) => itemSize, getEstimatedTotalSize: ({ itemCount, itemSize }) => itemSize * itemCount, getOffsetForIndexAndAlignment: ({ direction, height, itemCount, itemSize, layout, width }, index, align, scrollOffset) => { // TODO Deprecate direction "horizontal" const isHorizontal = direction === 'horizontal' || layout === 'horizontal'; const size = isHorizontal ? width : height; const lastItemOffset = Math.max(0, itemCount * itemSize - size); const maxOffset = Math.min(lastItemOffset, index * itemSize); const minOffset = Math.max(0, index * itemSize - size + itemSize); if (align === 'smart') { if (scrollOffset >= minOffset - size && scrollOffset <= maxOffset + size) { align = 'auto'; } else { align = 'center'; } } switch (align) { case 'start': return maxOffset case 'end': return minOffset case 'center': { // "Centered" offset is usually the average of the min and max. // But near the edges of the list, this doesn't hold true. const middleOffset = Math.round(minOffset + (maxOffset - minOffset) / 2); if (middleOffset < Math.ceil(size / 2)) { return 0 // near the beginning } else if (middleOffset > lastItemOffset + Math.floor(size / 2)) { return lastItemOffset // near the end } else { return middleOffset } } case 'auto': default: if (scrollOffset >= minOffset && scrollOffset <= maxOffset) { return scrollOffset } else if (scrollOffset < minOffset) { return minOffset } else { return maxOffset } } }, getStartIndexForOffset: ({ itemCount, itemSize }, offset) => Math.max(0, Math.min(itemCount - 1, Math.floor(offset / itemSize))), getStopIndexForStartIndex: ({ direction, height, itemCount, itemSize, layout, width }, startIndex, scrollOffset) => { // TODO Deprecate direction "horizontal" const isHorizontal = direction === 'horizontal' || layout === 'horizontal'; const offset = startIndex * itemSize; const size = isHorizontal ? width : height; const numVisibleItems = Math.ceil((size + scrollOffset - offset) / itemSize); return Math.max(0, Math.min(itemCount - 1, startIndex + numVisibleItems - 1 // -1 is because stop index is inclusive )) }, initInstanceProps () { // Noop }, shouldResetStyleCacheOnItemSizeChange: true, validateProps: ({ itemSize }) => { if (process.env.NODE_ENV !== 'production') { if (typeof itemSize !== 'number') { throw Error('An invalid "itemSize" prop has been specified. ' + 'Value should be a number. ' + `"${itemSize === null ? 'null' : typeof itemSize}" was specified.`) } } } }); function convertPxToInt (style) { if (typeof style === 'string') { const str = style.toLowerCase(); if (/px$/.test(str)) { return Number(str.replace(/px$/, '')) } } return style } const OuterScrollView = React__default['default'].forwardRef( (props, ref) => { const { style, onScroll, onScrollNative, layout, ...rest } = props; const handleScroll = event => { onScroll({ ...event, currentTarget: { ...event.detail, clientWidth: convertPxToInt(style.width), clientHeight: convertPxToInt(style.height) } }); if (typeof onScrollNative === 'function') { onScrollNative(event); } }; return React__d