UNPKG

element-plus

Version:

A Component Library for Vue3.0

1,421 lines (1,406 loc) 58.3 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var util = require('../utils/util'); var throwError = require('../utils/error'); var vue = require('vue'); var memo = require('lodash/memoize'); var isServer = require('../utils/isServer'); var raf = require('../utils/raf'); var dom = require('../utils/dom'); var getScrollBarWidth = require('../utils/scrollbar-width'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var throwError__default = /*#__PURE__*/_interopDefaultLegacy(throwError); var memo__default = /*#__PURE__*/_interopDefaultLegacy(memo); var isServer__default = /*#__PURE__*/_interopDefaultLegacy(isServer); var getScrollBarWidth__default = /*#__PURE__*/_interopDefaultLegacy(getScrollBarWidth); /** * Make a map and return a function for checking if a key * is in that map. * IMPORTANT: all calls of this function must be prefixed with * \/\*#\_\_PURE\_\_\*\/ * So that rollup can tree-shake them if necessary. */ const EMPTY_OBJ = (process.env.NODE_ENV !== 'production') ? Object.freeze({}) : {}; const EMPTY_ARR = (process.env.NODE_ENV !== 'production') ? Object.freeze([]) : []; const NOOP = () => { }; const hasOwnProperty = Object.prototype.hasOwnProperty; const hasOwn = (val, key) => hasOwnProperty.call(val, key); const isFunction = (val) => typeof val === 'function'; const isObject = (val) => val !== null && typeof val === 'object'; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); const DEFAULT_DYNAMIC_LIST_ITEM_SIZE = 50; const ITEM_RENDER_EVT = "item-rendered"; const SCROLL_EVT = "scroll"; const FORWARD = "forward"; const BACKWARD = "backward"; const AUTO_ALIGNMENT = "auto"; const SMART_ALIGNMENT = "smart"; const START_ALIGNMENT = "start"; const CENTERED_ALIGNMENT = "center"; const END_ALIGNMENT = "end"; const HORIZONTAL = "horizontal"; const VERTICAL = "vertical"; const LTR = "ltr"; const RTL = "rtl"; const RTL_OFFSET_NAG = "negative"; const RTL_OFFSET_POS_ASC = "positive-ascending"; const RTL_OFFSET_POS_DESC = "positive-descending"; const DefaultListProps = { cache: { type: Number, default: 2 }, className: { type: String, default: "" }, containerElement: { type: [String, Object], default: "div" }, data: { type: [Array], default: () => [] }, direction: { type: String, default: "ltr", validator: (val) => { return val === LTR || val === RTL; } }, estimatedItemSize: { type: [Number] }, height: { type: [String, Number], required: true }, layout: { type: String, default: VERTICAL }, initScrollOffset: { type: Number, default: 0 }, innerElement: { type: [String, Object], default: "div" }, total: { type: Number, required: true }, itemSize: { type: [Number, Function], required: true }, style: { type: [Object, String, Array], default: () => ({}) }, useIsScrolling: { type: Boolean, default: false }, width: { type: [Number, String], required: true } }; const DefaultGridProps = { className: DefaultListProps.className, columnCache: DefaultListProps.cache, columnWidth: DefaultListProps.itemSize, containerElement: DefaultListProps.containerElement, data: DefaultListProps.data, direction: DefaultListProps.direction, estimatedColumnWidth: DefaultListProps.estimatedItemSize, estimatedRowHeight: DefaultListProps.estimatedItemSize, height: __spreadProps(__spreadValues({}, DefaultListProps.height), { validator: (val) => util.isNumber(val) }), initScrollLeft: DefaultListProps.initScrollOffset, initScrollTop: DefaultListProps.initScrollOffset, innerElement: DefaultListProps.innerElement, rowCache: DefaultListProps.cache, rowHeight: DefaultListProps.itemSize, style: DefaultListProps.style, useIsScrolling: DefaultListProps.useIsScrolling, width: __spreadProps(__spreadValues({}, DefaultListProps.width), { validator: (val) => { return util.isNumber(val); } }), totalColumn: DefaultListProps.total, totalRow: DefaultListProps.total }; const DefaultScrollBarProps = { layout: DefaultListProps.layout, total: Number, ratio: Number, clientSize: Number, scrollFrom: Number, visible: Boolean }; const SCROLLBAR_MIN_SIZE = 20; const getScrollDir = (prev, cur) => prev < cur ? FORWARD : BACKWARD; const isHorizontal = (dir) => dir === LTR || dir === RTL || dir === HORIZONTAL; const isRTL = (dir) => dir === RTL; let cachedRTLResult = null; 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 = RTL_OFFSET_POS_DESC; } else { outerDiv.scrollLeft = 1; if (outerDiv.scrollLeft === 0) { cachedRTLResult = RTL_OFFSET_NAG; } else { cachedRTLResult = RTL_OFFSET_POS_ASC; } } document.body.removeChild(outerDiv); return cachedRTLResult; } return cachedRTLResult; } function renderThumbStyle({ move, size, bar }, layout) { const style = {}; const translate = `translate${bar.axis}(${move}px)`; style[bar.size] = size; style.transform = translate; style.msTransform = translate; style.webkitTransform = translate; if (layout === "horizontal") { style.height = "100%"; } else { style.width = "100%"; } return style; } const isFF = typeof navigator !== "undefined" && isObject(navigator) && /Firefox/i.test(navigator.userAgent); const LayoutKeys = { [HORIZONTAL]: "deltaX", [VERTICAL]: "deltaY" }; const useWheel = ({ atEndEdge, atStartEdge, layout }, onWheelDelta) => { let frameHandle = null; let offset = 0; const hasReachedEdge = (offset2) => { const edgeReached = offset2 < 0 && atStartEdge.value || offset2 > 0 && atEndEdge.value; return edgeReached; }; const onWheel = (e) => { raf.cAF(frameHandle); const newOffset = e[LayoutKeys[layout.value]]; if (hasReachedEdge(offset) && hasReachedEdge(offset + newOffset)) return; offset += newOffset; if (!isFF) { e.preventDefault(); } frameHandle = raf.rAF(() => { onWheelDelta(offset); offset = 0; }); }; return { hasReachedEdge, onWheel }; }; const BAR_MAP = { vertical: { offset: "offsetHeight", scroll: "scrollTop", scrollSize: "scrollHeight", size: "height", key: "vertical", axis: "Y", client: "clientY", direction: "top" }, horizontal: { offset: "offsetWidth", scroll: "scrollLeft", scrollSize: "scrollWidth", size: "width", key: "horizontal", axis: "X", client: "clientX", direction: "left" } }; const ScrollBar = vue.defineComponent({ name: "ElVirtualScrollBar", props: DefaultScrollBarProps, emits: ["scroll", "start-move", "stop-move"], setup(props, { emit }) { const trackRef = vue.ref(null); const thumbRef = vue.ref(null); let frameHandle = null; let onselectstartStore = null; const state = vue.reactive({ isDragging: false, traveled: 0 }); const bar = vue.computed(() => BAR_MAP[props.layout]); const trackStyle = vue.computed(() => ({ display: props.visible ? null : "none", position: "absolute", width: HORIZONTAL === props.layout ? "100%" : "6px", height: HORIZONTAL === props.layout ? "6px" : "100%", right: "2px", bottom: "2px", borderRadius: "4px" })); const thumbSize = vue.computed(() => { if (props.ratio >= 100) { return Number.POSITIVE_INFINITY; } if (props.ratio >= 50) { return props.ratio * props.clientSize / 100; } const SCROLLBAR_MAX_SIZE = props.clientSize / 3; return Math.floor(Math.min(Math.max(props.ratio * props.clientSize, SCROLLBAR_MIN_SIZE), SCROLLBAR_MAX_SIZE)); }); const thumbStyle = vue.computed(() => { if (!Number.isFinite(thumbSize.value)) { return { display: "none" }; } const thumb = `${thumbSize.value}px`; const style = renderThumbStyle({ bar: bar.value, size: thumb, move: state.traveled }, props.layout); return style; }); const totalSteps = vue.computed(() => Math.floor(props.clientSize - thumbSize.value - 4)); const attachEvents = () => { dom.on(window, "mousemove", onMouseMove); dom.on(window, "mouseup", onMouseUp); const thumbEl = thumbRef.value; onselectstartStore = document.onselectstart; document.onselectstart = () => false; dom.on(thumbEl, "touchmove", onMouseMove); dom.on(thumbEl, "touchend", onMouseUp); }; const detachEvents = () => { dom.off(window, "mousemove", onMouseMove); dom.off(window, "mouseup", onMouseUp); document.onselectstart = onselectstartStore; onselectstartStore = null; const thumbEl = thumbRef.value; dom.off(thumbEl, "touchmove", onMouseMove); dom.off(thumbEl, "touchend", onMouseUp); }; const onThumbMouseDown = (e) => { e.stopImmediatePropagation(); if (e.ctrlKey || [1, 2].includes(e.button)) { return; } state.isDragging = true; state[bar.value.axis] = e.currentTarget[bar.value.offset] - (e[bar.value.client] - e.currentTarget.getBoundingClientRect()[bar.value.direction]); emit("start-move"); attachEvents(); }; const onMouseUp = () => { state.isDragging = false; state[bar.value.axis] = 0; emit("stop-move"); detachEvents(); }; const onMouseMove = (e) => { const { isDragging } = state; if (!isDragging) return; const prevPage = state[bar.value.axis]; if (!prevPage) return; raf.cAF(frameHandle); const offset = (trackRef.value.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]) * -1; const thumbClickPosition = thumbRef.value[bar.value.offset] - prevPage; const distance = offset - thumbClickPosition; frameHandle = raf.rAF(() => { state.traveled = Math.max(2, Math.min(distance, totalSteps.value)); emit("scroll", distance, totalSteps.value); }); }; const onScrollbarTouchStart = (e) => e.preventDefault(); vue.watch(() => props.scrollFrom, (v) => { if (state.isDragging) return; state.traveled = Math.ceil(v * props.clientSize / (props.clientSize / totalSteps.value)); }); vue.onMounted(() => { if (isServer__default['default']) return; dom.on(trackRef.value, "touchstart", onScrollbarTouchStart); dom.on(thumbRef.value, "touchstart", onThumbMouseDown); }); vue.onBeforeUnmount(() => { dom.off(trackRef.value, "touchstart", onScrollbarTouchStart); detachEvents(); }); return () => { return vue.h("div", { role: "presentation", ref: trackRef, class: "el-virtual-scrollbar", style: trackStyle.value, onMousedown: vue.withModifiers(NOOP, ["stop", "prevent"]) }, vue.h("div", { ref: thumbRef, class: "el-scrollbar__thumb", style: thumbStyle.value, onMousedown: onThumbMouseDown }, null)); }; } }); var __defProp$1 = Object.defineProperty; var __defProps$1 = Object.defineProperties; var __getOwnPropDescs$1 = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols; var __hasOwnProp$1 = Object.prototype.hasOwnProperty; var __propIsEnum$1 = Object.prototype.propertyIsEnumerable; var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues$1 = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp$1.call(b, prop)) __defNormalProp$1(a, prop, b[prop]); if (__getOwnPropSymbols$1) for (var prop of __getOwnPropSymbols$1(b)) { if (__propIsEnum$1.call(b, prop)) __defNormalProp$1(a, prop, b[prop]); } return a; }; var __spreadProps$1 = (a, b) => __defProps$1(a, __getOwnPropDescs$1(b)); const createList = ({ name, getOffset, getItemSize, getItemOffset, getEstimatedTotalSize, getStartIndexForOffset, getStopIndexForStartIndex, initCache, clearCache, validateProps }) => { return vue.defineComponent({ name: name != null ? name : "ElVirtualList", props: DefaultListProps, emits: [ITEM_RENDER_EVT, SCROLL_EVT], setup(props, { emit, expose }) { validateProps(props); const instance = vue.getCurrentInstance(); const dynamicSizeCache = vue.ref(initCache(props, instance)); const windowRef = vue.ref(null); const innerRef = vue.ref(null); const scrollbarRef = vue.ref(null); const states = vue.ref({ isScrolling: false, scrollDir: "forward", scrollOffset: util.isNumber(props.initScrollOffset) ? props.initScrollOffset : 0, updateRequested: false, isScrollbarDragging: false }); const itemsToRender = vue.computed(() => { const { total, cache } = props; const { isScrolling, scrollDir, scrollOffset } = util.$(states); if (total === 0) { return [0, 0, 0, 0]; } const startIndex = getStartIndexForOffset(props, scrollOffset, util.$(dynamicSizeCache)); const stopIndex = getStopIndexForStartIndex(props, startIndex, scrollOffset, util.$(dynamicSizeCache)); const cacheBackward = !isScrolling || scrollDir === BACKWARD ? Math.max(1, cache) : 1; const cacheForward = !isScrolling || scrollDir === FORWARD ? Math.max(1, cache) : 1; return [ Math.max(0, startIndex - cacheBackward), Math.max(0, Math.min(total - 1, stopIndex + cacheForward)), startIndex, stopIndex ]; }); const estimatedTotalSize = vue.computed(() => getEstimatedTotalSize(props, util.$(dynamicSizeCache))); const _isHorizontal = vue.computed(() => isHorizontal(props.layout)); const windowStyle = vue.computed(() => [ { position: "relative", overflow: "hidden", WebkitOverflowScrolling: "touch", willChange: "transform" }, __spreadValues$1({ direction: props.direction, height: util.isNumber(props.height) ? `${props.height}px` : props.height, width: util.isNumber(props.width) ? `${props.width}px` : props.width }, props.style) ]); const innerStyle = vue.computed(() => { const size = util.$(estimatedTotalSize); const horizontal = util.$(_isHorizontal); return { height: horizontal ? "100%" : `${size}px`, pointerEvents: util.$(states).isScrolling ? "none" : void 0, width: horizontal ? `${size}px` : "100%" }; }); const clientSize = vue.computed(() => _isHorizontal.value ? props.width : props.height); const { onWheel } = useWheel({ atStartEdge: vue.computed(() => states.value.scrollOffset <= 0), atEndEdge: vue.computed(() => states.value.scrollOffset >= estimatedTotalSize.value), layout: vue.computed(() => props.layout) }, (offset) => { var _a, _b; (_b = (_a = scrollbarRef.value).onMouseUp) == null ? void 0 : _b.call(_a); scrollTo(Math.min(states.value.scrollOffset + offset, estimatedTotalSize.value - clientSize.value)); }); const emitEvents = () => { const { total } = props; if (total > 0) { const [cacheStart, cacheEnd, visibleStart, visibleEnd] = util.$(itemsToRender); emit(ITEM_RENDER_EVT, cacheStart, cacheEnd, visibleStart, visibleEnd); } const { scrollDir, scrollOffset, updateRequested } = util.$(states); emit(SCROLL_EVT, scrollDir, scrollOffset, updateRequested); }; const scrollVertically = (e) => { const { clientHeight, scrollHeight, scrollTop } = e.currentTarget; const _states = util.$(states); if (_states.scrollOffset === scrollTop) { return; } const scrollOffset = Math.max(0, Math.min(scrollTop, scrollHeight - clientHeight)); states.value = __spreadProps$1(__spreadValues$1({}, _states), { isScrolling: true, scrollDir: getScrollDir(_states.scrollOffset, scrollOffset), scrollOffset, updateRequested: false }); vue.nextTick(resetIsScrolling); }; const scrollHorizontally = (e) => { const { clientWidth, scrollLeft, scrollWidth } = e.currentTarget; const _states = util.$(states); if (_states.scrollOffset === scrollLeft) { return; } const { direction } = props; let scrollOffset = scrollLeft; if (direction === RTL) { switch (getRTLOffsetType()) { case RTL_OFFSET_NAG: { scrollOffset = -scrollLeft; break; } case RTL_OFFSET_POS_DESC: { scrollOffset = scrollWidth - clientWidth - scrollLeft; break; } } } scrollOffset = Math.max(0, Math.min(scrollOffset, scrollWidth - clientWidth)); states.value = __spreadProps$1(__spreadValues$1({}, _states), { isScrolling: true, scrollDir: getScrollDir(_states.scrollOffset, scrollOffset), scrollOffset, updateRequested: false }); vue.nextTick(resetIsScrolling); }; const onScroll = (e) => { util.$(_isHorizontal) ? scrollHorizontally(e) : scrollVertically(e); emitEvents(); }; const onScrollbarScroll = (distanceToGo, totalSteps) => { const offset = (estimatedTotalSize.value - clientSize.value) / totalSteps * distanceToGo; scrollTo(Math.min(estimatedTotalSize.value - clientSize.value, offset)); }; const getItemStyleCache = memo__default['default']((_, __, ___) => ({})); const scrollTo = (offset) => { offset = Math.max(offset, 0); if (offset === util.$(states).scrollOffset) { return; } states.value = __spreadProps$1(__spreadValues$1({}, util.$(states)), { scrollOffset: offset, scrollDir: getScrollDir(util.$(states).scrollOffset, offset), updateRequested: true }); vue.nextTick(resetIsScrolling); }; const scrollToItem = (idx, alignment = AUTO_ALIGNMENT) => { const { scrollOffset } = util.$(states); idx = Math.max(0, Math.min(idx, props.total - 1)); scrollTo(getOffset(props, idx, alignment, scrollOffset, util.$(dynamicSizeCache))); }; const getItemStyle = (idx) => { const { direction, itemSize, layout } = props; const itemStyleCache = getItemStyleCache(clearCache && itemSize, clearCache && layout, clearCache && direction); let style; if (hasOwn(itemStyleCache, String(idx))) { style = itemStyleCache[idx]; } else { const offset = getItemOffset(props, idx, util.$(dynamicSizeCache)); const size = getItemSize(props, idx, util.$(dynamicSizeCache)); const horizontal = util.$(_isHorizontal); const isRtl = direction === RTL; const offsetHorizontal = horizontal ? offset : 0; itemStyleCache[idx] = style = { position: "absolute", left: isRtl ? void 0 : `${offsetHorizontal}px`, right: isRtl ? `${offsetHorizontal}px` : void 0, top: !horizontal ? `${offset}px` : 0, height: !horizontal ? `${size}px` : "100%", width: horizontal ? `${size}px` : "100%" }; } return style; }; const resetIsScrolling = () => { states.value.isScrolling = false; vue.nextTick(() => { getItemStyleCache(-1, null, null); }); }; vue.onMounted(() => { if (isServer__default['default']) return; const { initScrollOffset } = props; const windowElement = util.$(windowRef); if (util.isNumber(initScrollOffset) && windowElement !== null) { if (util.$(_isHorizontal)) { windowElement.scrollLeft = initScrollOffset; } else { windowElement.scrollTop = initScrollOffset; } } emitEvents(); }); vue.onUpdated(() => { const { direction, layout } = props; const { scrollOffset, updateRequested } = util.$(states); if (updateRequested && util.$(windowRef) !== null) { const windowElement = util.$(windowRef); if (layout === HORIZONTAL) { if (direction === RTL) { switch (getRTLOffsetType()) { case "negative": { windowElement.scrollLeft = -scrollOffset; break; } case "positive-ascending": { windowElement.scrollLeft = scrollOffset; break; } default: { const { clientWidth, scrollWidth } = windowElement; windowElement.scrollLeft = scrollWidth - clientWidth - scrollOffset; break; } } } else { windowElement.scrollLeft = scrollOffset; } } else { windowElement.scrollTop = scrollOffset; } } }); const api = { clientSize, estimatedTotalSize, windowStyle, windowRef, innerRef, innerStyle, itemsToRender, scrollbarRef, states, getItemStyle, onScroll, onScrollbarScroll, onWheel, scrollTo, scrollToItem }; expose({ windowRef, innerRef, getItemStyleCache, scrollTo, scrollToItem, states }); return api; }, render(ctx) { var _a; const { $slots, className, clientSize, containerElement, data, getItemStyle, innerElement, itemsToRender, innerStyle, layout, total, onScroll, onScrollbarScroll, onWheel, states, useIsScrolling, windowStyle } = ctx; const [start, end] = itemsToRender; const Container = vue.resolveDynamicComponent(containerElement); const Inner = vue.resolveDynamicComponent(innerElement); const children = []; if (total > 0) { for (let i = start; i <= end; i++) { children.push((_a = $slots.default) == null ? void 0 : _a.call($slots, { data, key: i, index: i, isScrolling: useIsScrolling ? states.isScrolling : void 0, style: getItemStyle(i) })); } } const InnerNode = [vue.h(Inner, { style: innerStyle, ref: "innerRef" }, !util.isString(Inner) ? { default: () => children } : children)]; const scrollbar = vue.h(ScrollBar, { ref: "scrollbarRef", clientSize, layout, onScroll: onScrollbarScroll, ratio: clientSize * 100 / this.estimatedTotalSize, scrollFrom: states.scrollOffset / (this.estimatedTotalSize - clientSize), total, visible: true }); const listContainer = vue.h(Container, { class: className, style: windowStyle, onScroll, onWheel, ref: "windowRef", key: 0 }, !util.isString(Container) ? { default: () => [InnerNode] } : [InnerNode]); return vue.h("div", { key: 0, class: "el-vl__wrapper" }, [ listContainer, scrollbar ]); } }); }; const FixedSizeList = createList({ name: "ElFixedSizeList", getItemOffset: ({ itemSize }, index) => index * itemSize, getItemSize: ({ itemSize }) => itemSize, getEstimatedTotalSize: ({ total, itemSize }) => itemSize * total, getOffset: ({ height, total, itemSize, layout, width }, index, alignment, scrollOffset) => { const size = isHorizontal(layout) ? width : height; if (process.env.ENV !== "production" && util.isString(size)) { throwError__default['default']("[ElVirtualList]", ` You should set width/height to number when your layout is horizontal/vertical `); } const lastItemOffset = Math.max(0, total * itemSize - size); const maxOffset = Math.min(lastItemOffset, index * itemSize); const minOffset = Math.max(0, (index + 1) * itemSize - size); if (alignment === SMART_ALIGNMENT) { if (scrollOffset >= minOffset - size && scrollOffset <= maxOffset + size) { alignment = AUTO_ALIGNMENT; } else { alignment = CENTERED_ALIGNMENT; } } switch (alignment) { case START_ALIGNMENT: { return maxOffset; } case END_ALIGNMENT: { return minOffset; } case CENTERED_ALIGNMENT: { const middleOffset = Math.round(minOffset + (maxOffset - minOffset) / 2); if (middleOffset < Math.ceil(size / 2)) { return 0; } else if (middleOffset > lastItemOffset + Math.floor(size / 2)) { return lastItemOffset; } else { return middleOffset; } } case AUTO_ALIGNMENT: default: { if (scrollOffset >= minOffset && scrollOffset <= maxOffset) { return scrollOffset; } else if (scrollOffset < minOffset) { return minOffset; } else { return maxOffset; } } } }, getStartIndexForOffset: ({ total, itemSize }, offset) => Math.max(0, Math.min(total - 1, Math.floor(offset / itemSize))), getStopIndexForStartIndex: ({ height, total, itemSize, layout, width }, startIndex, scrollOffset) => { const offset = startIndex * itemSize; const size = isHorizontal(layout) ? width : height; const numVisibleItems = Math.ceil((size + scrollOffset - offset) / itemSize); return Math.max(0, Math.min(total - 1, startIndex + numVisibleItems - 1)); }, initCache() { return void 0; }, clearCache: true, validateProps() { } }); const SCOPE = "ElDynamicSizeList"; const getItemFromCache = (props, index, listCache) => { const { itemSize } = props; const { items, lastVisitedIndex } = listCache; if (index > lastVisitedIndex) { let offset = 0; if (lastVisitedIndex >= 0) { const item = items[lastVisitedIndex]; offset = item.offset + item.size; } for (let i = lastVisitedIndex + 1; i <= index; i++) { const size = itemSize(i); items[i] = { offset, size }; offset += size; } listCache.lastVisitedIndex = index; } return items[index]; }; const findItem = (props, listCache, offset) => { const { items, lastVisitedIndex } = listCache; const lastVisitedOffset = lastVisitedIndex > 0 ? items[lastVisitedIndex].offset : 0; if (lastVisitedOffset >= offset) { return bs(props, listCache, 0, lastVisitedIndex, offset); } return es(props, listCache, Math.max(0, lastVisitedIndex), offset); }; const bs = (props, listCache, low, high, offset) => { while (low <= high) { const mid = low + Math.floor((high - low) / 2); const currentOffset = getItemFromCache(props, mid, listCache).offset; if (currentOffset === offset) { return mid; } else if (currentOffset < offset) { low = mid + 1; } else if (currentOffset > offset) { high = mid - 1; } } return Math.max(0, low - 1); }; const es = (props, listCache, index, offset) => { const { total } = props; let exponent = 1; while (index < total && getItemFromCache(props, index, listCache).offset < offset) { index += exponent; exponent *= 2; } return bs(props, listCache, Math.floor(index / 2), Math.min(index, total - 1), offset); }; const getEstimatedTotalSize = ({ total }, { items, estimatedItemSize, lastVisitedIndex }) => { let totalSizeOfMeasuredItems = 0; if (lastVisitedIndex >= total) { lastVisitedIndex = total - 1; } if (lastVisitedIndex >= 0) { const item = items[lastVisitedIndex]; totalSizeOfMeasuredItems = item.offset + item.size; } const numUnmeasuredItems = total - lastVisitedIndex - 1; const totalSizeOfUnmeasuredItems = numUnmeasuredItems * estimatedItemSize; return totalSizeOfMeasuredItems + totalSizeOfUnmeasuredItems; }; const DynamicSizeList = createList({ name: "ElDynamicSizeList", getItemOffset: (props, index, listCache) => getItemFromCache(props, index, listCache).offset, getItemSize: (_, index, { items }) => items[index].size, getEstimatedTotalSize, getOffset: (props, index, alignment, scrollOffset, listCache) => { const { height, layout, width } = props; const size = isHorizontal(layout) ? width : height; const item = getItemFromCache(props, index, listCache); const estimatedTotalSize = getEstimatedTotalSize(props, listCache); const maxOffset = Math.max(0, Math.min(estimatedTotalSize - size, item.offset)); const minOffset = Math.max(0, item.offset - size + item.size); if (alignment === SMART_ALIGNMENT) { if (scrollOffset >= minOffset - size && scrollOffset <= maxOffset + size) { alignment = AUTO_ALIGNMENT; } else { alignment = CENTERED_ALIGNMENT; } } switch (alignment) { case START_ALIGNMENT: { return maxOffset; } case END_ALIGNMENT: { return minOffset; } case CENTERED_ALIGNMENT: { return Math.round(minOffset + (maxOffset - minOffset) / 2); } case AUTO_ALIGNMENT: default: { if (scrollOffset >= minOffset && scrollOffset <= maxOffset) { return scrollOffset; } else if (scrollOffset < minOffset) { return minOffset; } else { return maxOffset; } } } }, getStartIndexForOffset: (props, offset, listCache) => findItem(props, listCache, offset), getStopIndexForStartIndex: (props, startIndex, scrollOffset, listCache) => { const { height, total, layout, width } = props; const size = isHorizontal(layout) ? width : height; const item = getItemFromCache(props, startIndex, listCache); const maxOffset = scrollOffset + size; let offset = item.offset + item.size; let stopIndex = startIndex; while (stopIndex < total - 1 && offset < maxOffset) { stopIndex++; offset += getItemFromCache(props, stopIndex, listCache).size; } return stopIndex; }, initCache({ estimatedItemSize = DEFAULT_DYNAMIC_LIST_ITEM_SIZE }, instance) { const cache = { items: {}, estimatedItemSize, lastVisitedIndex: -1 }; cache.clearCacheAfterIndex = (index, forceUpdate = true) => { cache.lastVisitedIndex = Math.min(cache.lastVisitedIndex, index - 1); instance.exposed.getItemStyleCache(-1); if (forceUpdate) { instance.proxy.$forceUpdate(); } }; return cache; }, clearCache: false, validateProps: ({ itemSize }) => { if (process.env.NODE_ENV !== "production") { if (typeof itemSize !== "function") { throwError__default['default'](SCOPE, ` itemSize is required as function, but the given value was ${typeof itemSize} `); } } } }); var __defProp$2 = Object.defineProperty; var __defProps$2 = Object.defineProperties; var __getOwnPropDescs$2 = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols$2 = Object.getOwnPropertySymbols; var __hasOwnProp$2 = Object.prototype.hasOwnProperty; var __propIsEnum$2 = Object.prototype.propertyIsEnumerable; var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues$2 = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp$2.call(b, prop)) __defNormalProp$2(a, prop, b[prop]); if (__getOwnPropSymbols$2) for (var prop of __getOwnPropSymbols$2(b)) { if (__propIsEnum$2.call(b, prop)) __defNormalProp$2(a, prop, b[prop]); } return a; }; var __spreadProps$2 = (a, b) => __defProps$2(a, __getOwnPropDescs$2(b)); const createGrid = ({ name, clearCache, getColumnPosition, getColumnStartIndexForOffset, getColumnStopIndexForStartIndex, getEstimatedTotalHeight, getEstimatedTotalWidth, getColumnOffset, getRowOffset, getRowPosition, getRowStartIndexForOffset, getRowStopIndexForStartIndex, initCache, validateProps }) => { return vue.defineComponent({ name: name != null ? name : "ElVirtualList", props: DefaultGridProps, emits: [ITEM_RENDER_EVT, SCROLL_EVT], setup(props, { emit, expose }) { validateProps(props); const instance = vue.getCurrentInstance(); const cache = vue.ref(initCache(props, instance)); const windowRef = vue.ref(null); const innerRef = vue.ref(null); const states = vue.ref({ isScrolling: false, scrollLeft: util.isNumber(props.initScrollLeft) ? props.initScrollLeft : 0, scrollTop: util.isNumber(props.initScrollTop) ? props.initScrollTop : 0, updateRequested: false, xAxisScrollDir: FORWARD, yAxisScrollDir: FORWARD }); const columnsToRender = vue.computed(() => { const { totalColumn, totalRow, columnCache } = props; const { isScrolling, xAxisScrollDir, scrollLeft } = util.$(states); if (totalColumn === 0 || totalRow === 0) { return [0, 0, 0, 0]; } const startIndex = getColumnStartIndexForOffset(props, scrollLeft, util.$(cache)); const stopIndex = getColumnStopIndexForStartIndex(props, startIndex, scrollLeft, util.$(cache)); const cacheBackward = !isScrolling || xAxisScrollDir === BACKWARD ? Math.max(1, columnCache) : 1; const cacheForward = !isScrolling || xAxisScrollDir === FORWARD ? Math.max(1, columnCache) : 1; return [ Math.max(0, startIndex - cacheBackward), Math.max(0, Math.min(totalColumn - 1, stopIndex + cacheForward)), startIndex, stopIndex ]; }); const rowsToRender = vue.computed(() => { const { totalColumn, totalRow, rowCache } = props; const { isScrolling, yAxisScrollDir, scrollTop } = util.$(states); if (totalColumn === 0 || totalRow === 0) { return [0, 0, 0, 0]; } const startIndex = getRowStartIndexForOffset(props, scrollTop, util.$(cache)); const stopIndex = getRowStopIndexForStartIndex(props, startIndex, scrollTop, util.$(cache)); const cacheBackward = !isScrolling || yAxisScrollDir === BACKWARD ? Math.max(1, rowCache) : 1; const cacheForward = !isScrolling || yAxisScrollDir === FORWARD ? Math.max(1, rowCache) : 1; return [ Math.max(0, startIndex - cacheBackward), Math.max(0, Math.min(totalRow - 1, stopIndex + cacheForward)), startIndex, stopIndex ]; }); const estimatedTotalHeight = vue.computed(() => getEstimatedTotalHeight(props, util.$(cache))); const estimatedTotalWidth = vue.computed(() => getEstimatedTotalWidth(props, util.$(cache))); const windowStyle = vue.computed(() => [ { position: "relative", overflow: "auto", WebkitOverflowScrolling: "touch", willChange: "transform" }, __spreadValues$2({ direction: props.direction, height: util.isNumber(props.height) ? `${props.height}px` : props.height, width: util.isNumber(props.width) ? `${props.width}px` : props.width }, props.style) ]); const innerStyle = vue.computed(() => { const width = `${util.$(estimatedTotalWidth)}px`; const height = `${util.$(estimatedTotalHeight)}px`; return { height, pointerEvents: util.$(states).isScrolling ? "none" : void 0, width }; }); const emitEvents = () => { const { totalColumn, totalRow } = props; if (totalColumn > 0 && totalRow > 0) { const [columnCacheStart, columnCacheEnd, columnVisibleStart, columnVisibleEnd] = util.$(columnsToRender); const [rowCacheStart, rowCacheEnd, rowVisibleStart, rowVisibleEnd] = util.$(rowsToRender); emit(ITEM_RENDER_EVT, columnCacheStart, columnCacheEnd, rowCacheStart, rowCacheEnd, columnVisibleStart, columnVisibleEnd, rowVisibleStart, rowVisibleEnd); } const { scrollLeft, scrollTop, updateRequested, xAxisScrollDir, yAxisScrollDir } = util.$(states); emit(SCROLL_EVT, xAxisScrollDir, scrollLeft, yAxisScrollDir, scrollTop, updateRequested); }; const onScroll = (e) => { const { clientHeight, clientWidth, scrollHeight, scrollLeft, scrollTop, scrollWidth } = e.currentTarget; const _states = util.$(states); if (_states.scrollTop === scrollTop && _states.scrollLeft === scrollLeft) { return; } let _scrollLeft = scrollLeft; if (isRTL(props.direction)) { switch (getRTLOffsetType()) { case RTL_OFFSET_NAG: _scrollLeft = -scrollLeft; break; case RTL_OFFSET_POS_DESC: _scrollLeft = scrollWidth - clientWidth - scrollLeft; break; } } states.value = __spreadProps$2(__spreadValues$2({}, _states), { isScrolling: true, scrollLeft: _scrollLeft, scrollTop: Math.max(0, Math.min(scrollTop, scrollHeight - clientHeight)), updateRequested: false, xAxisScrollDir: getScrollDir(_states.scrollLeft, _scrollLeft), yAxisScrollDir: getScrollDir(_states.scrollTop, scrollTop) }); vue.nextTick(resetIsScrolling); emitEvents(); }; const getItemStyleCache = memo__default['default']((_, __, ___) => ({})); const scrollTo = ({ scrollLeft, scrollTop }) => { scrollLeft = Math.max(scrollLeft, 0); scrollTop = Math.max(scrollTop, 0); const _states = util.$(states); if (scrollTop === _states.scrollTop && scrollLeft === _states.scrollLeft) { return; } states.value = __spreadProps$2(__spreadValues$2({}, _states), { xAxisScrollDir: getScrollDir(_states.scrollLeft, scrollLeft), yAxisScrollDir: getScrollDir(_states.scrollTop, scrollTop), scrollLeft, scrollTop, updateRequested: true }); vue.nextTick(resetIsScrolling); }; const scrollToItem = (rowIndex = 0, columnIdx = 0, alignment = AUTO_ALIGNMENT) => { const _states = util.$(states); columnIdx = Math.max(0, Math.min(columnIdx, props.totalColumn - 1)); rowIndex = Math.max(0, Math.min(rowIndex, props.totalRow - 1)); const scrollBarWidth = getScrollBarWidth__default['default'](); const _cache = util.$(cache); const estimatedHeight = getEstimatedTotalHeight(props, _cache); const estimatedWidth = getEstimatedTotalWidth(props, _cache); scrollTo({ scrollLeft: getColumnOffset(props, columnIdx, alignment, _states.scrollLeft, _cache, estimatedWidth > props.width ? scrollBarWidth : 0), scrollTop: getRowOffset(props, rowIndex, alignment, _states.scrollTop, _cache, estimatedHeight > props.height ? scrollBarWidth : 0) }); }; const getItemStyle = (rowIndex, columnIndex) => { const { columnWidth, direction, rowHeight } = props; const itemStyleCache = getItemStyleCache(clearCache && columnWidth, clearCache && rowHeight, clearCache && direction); const key = `${rowIndex},${columnIndex}`; if (hasOwn(itemStyleCache, key)) { return itemStyleCache[key]; } else { const [, left] = getColumnPosition(props, columnIndex, util.$(cache)); const _cache = util.$(cache); const rtl = isRTL(direction); const [height, top] = getRowPosition(props, rowIndex, _cache); const [width] = getColumnPosition(props, columnIndex, _cache); itemStyleCache[key] = { position: "absolute", left: rtl ? void 0 : `${left}px`, right: rtl ? `${left}px` : void 0, top: `${top}px`, height: `${height}px`, width: `${width}px` }; return itemStyleCache[key]; } }; const resetIsScrolling = () => { states.value.isScrolling = false; vue.nextTick(() => { getItemStyleCache(-1, null, null); }); }; vue.onMounted(() => { if (isServer__default['default']) return; const { initScrollLeft, initScrollTop } = props; const windowElement = util.$(windowRef); if (windowElement !== null) { if (util.isNumber(initScrollLeft)) { windowElement.scrollLeft = initScrollLeft; } if (util.isNumber(initScrollTop)) { windowElement.scrollTop = initScrollTop; } } emitEvents(); }); vue.onUpdated(() => { const { direction } = props; const { scrollLeft, scrollTop, updateRequested } = util.$(states); if (updateRequested && util.$(windowRef) !== null) { const windowElement = util.$(windowRef); if (direction === RTL) { switch (getRTLOffsetType()) { case RTL_OFFSET_NAG: { windowElement.scrollLeft = -scrollLeft; break; } case RTL_OFFSET_POS_ASC: { windowElement.scrollLeft = scrollLeft; break; } default: { const { clientWidth, scrollWidth } = windowElement; windowElement.scrollLeft = scrollWidth - clientWidth - scrollLeft; break; } } } else { windowElement.scrollLeft = Math.max(0, scrollLeft); } windowElement.scrollTop = Math.max(0, scrollTop); } }); const api = { windowStyle, windowRef, columnsToRender, innerRef, innerStyle, states, rowsToRender, getItemStyle, onScroll, scrollTo, scrollToItem }; expose({ windowRef, innerRef, getItemStyleCache, scrollTo, scrollToItem, states }); return api; }, render(ctx) { var _a; const { $slots, className, containerElement, columnsToRender, data, getItemStyle, innerElement, innerStyle, rowsToRender, onScroll, states, useIsScrolling, windowStyle, totalColumn, totalRow } = ctx; const [columnStart, columnEnd] = columnsToRender; const [rowStart, rowEnd] = rowsToRender; const Container = vue.resolveDynamicComponent(containerElement); const Inner = vue.resolveDynamicComponent(innerElement); const children = []; if (totalRow > 0 && totalColumn > 0) { for (let row = rowStart; row <= rowEnd; row++) { for (let column = columnStart; column <= columnEnd; column++) { children.push((_a = $slots.default) == null ? void 0 : _a.call($slots, { columnIndex: column, data, key: column, isScrolling: useIsScrolling ? states.isScrolling : void 0, style: getItemStyle(row, column), rowIndex: row })); } } } const InnerNode = [vue.h(Inner, { style: innerStyle, ref: "innerRef" }, !util.isString(Inner) ? { default: () => children } : children)]; return vue.h(Container, { class: className, style: windowStyle, onScroll, ref: "windowRef" }, !util.isString(Container) ? { default: () => InnerNode } : InnerNode); } }); }; const SCOPE$1 = "ElFixedSizeGrid"; const FixedSizeGrid = createGrid({ name: "ElFixedSizeGrid", getColumnPosition: ({ columnWidth }, index) => [ columnWidth, index * columnWidth ], getRowPosition: ({ rowHeight }, index) => [ rowHeight, index * rowHeight ], getEstimatedTotalHeight: ({ totalRow, rowHeight }) => rowHeight * totalRow, getEstimatedTotalWidth: ({ totalColumn, columnWidth }) => columnWidth * totalColumn, getColumnOffset: ({ totalColumn, columnWidth, width }, columnIndex, alignment, scrollLeft, _, scrollBarWidth) => { width = Number(width); const lastColumnOffset = Math.max(0, totalColumn * columnWidth - width); const maxOffset = Math.min(lastColumnOffset, columnIndex * columnWidth); const minOffset = Math.max(0, columnIndex * columnWidth - width + scrollBarWidth + columnWidth); if (alignment === "smart") { if (scrollLeft >= minOffset - width && scrollLeft <= maxOffset + width) { alignment = AUTO_ALIGNMENT; } else { alignment = CENTERED_ALIGNMENT; } } switch (alignment) { case START_ALIGNMENT: return maxOffset; case END_ALIGNMENT: return minOffset; case CENTERED_ALIGNMENT: const middleOffset = Math.round(minOffset + (maxOffset - minOffset) / 2); if (middleOffset < Math.ceil(width / 2)) { return 0; } else if (middleOffset > lastColumnOffset + Math.floor(width / 2)) { return lastColumnOffset; } else { return middleOffset; } case AUTO_ALIGNMENT: default: if (scrollLeft >= minOffset && scrollLeft <= maxOffset) { return scrollLeft; } else if (minOffset > maxOffset) { return minOffset; } else if (scrollLeft < minOffset) { return minOffset; } else { return maxOffset; } } }, getRowOffset: ({ rowHeight, height, totalRow }, rowIndex, align, scrollTop, _, scrollBarWidth) => { height = Number(height); const lastRowOffset = Math.max(0, totalRow * rowHeight - height); const maxOffset = Math.min(lastRowOffset, rowIndex * rowHeight); const minOffset = Math.max(0, rowIndex * rowHeight - height + scrollBarWidth + rowHeight); if (align === SMART_ALIGNMENT) { if (scrollTop >= minOffset - height && scrollTop <= maxOffset + height) { align = AUTO_ALIGNMENT; } else { align = CENTERED_ALIGNMENT; } } switch (align) { case START_ALIGNMENT: return maxOffset; case END_ALIGNMENT: return minOffset; case CENTERED_ALIGNMENT: const middleOffset = Math.round(minOffset + (maxOffset - minOffset) / 2); if (middleOffset < Math.ceil(height / 2)) { return 0; } else if (middleOffset > lastRowOffset + Math.floor(height / 2)) { return lastRowOffset; } else { return middleOffset; } case AUTO_ALIGNMENT: default: if (scrollTop >= minOffset && scrollTop <= maxOffset) { return scrollTop; } else if (minOffset > maxOffset) { return minOffset; } else if (scrollTop < minOffset) { return minOffset; } else { return maxOffset; } } }, getColumnStartIndexForOffset: ({ columnWidth, totalColumn }, scrollLeft) => Math.max(0, Math.min(totalColumn - 1, Math.floor(scrollLeft / columnWidth))), getColumnStopIndexForStartIndex: ({ columnWidth, totalColumn, width }, startIndex, scrollLeft) => { const left = startIndex * columnWidth; const visibleColumnsCount = Math.ceil((width + scrollLeft - left) / columnWidth); return Math.max(0, Math.min(totalColumn - 1, startIndex + visibleColumnsCount - 1)); }, getRowStartIndexForOffset: ({ rowHeight, totalRow }, scrollTop) => Math.max(0, Math.min(totalRow - 1, Math.floor(scrollTop / rowHeight))), getRowStopIndexForStartIndex: ({ rowHeight, totalRow, height }, startIndex, scrollTop) => { const top = startIndex * rowHeight; const numVisibleRows = Math.ceil((height + scrollTop - top) / rowHeight); return Math.max(0, Math.min(totalRow - 1, startIndex + numVisibleRows - 1)); }