UNPKG

@opentiny/vue-renderless

Version:

An enterprise-class UI component library, support both Vue.js 2 and Vue.js 3, as well as PC and mobile.

295 lines (294 loc) 9.12 kB
import { __spreadValues } from "../chunk-G2ADBYYC.js"; import { calcScrollWidth } from "@opentiny/utils"; const computedBarSize = (props) => () => { let barSize = +props.scrollbarSize; if (isNaN(barSize) || barSize < 0) { barSize = calcScrollWidth(); } return barSize; }; const analyzeScrolling = ({ props, state }) => () => { const width = +props.width; const height = +props.height; const scrollbarSize = state.scrollbarSize; const columnSizes = props.columnSizes; const rowSizes = props.rowSizes; const columnTotal = columnSizes.reduce((p, c) => p + c, 0); const rowTotal = rowSizes.reduce((p, r) => p + r, 0); const result = { isScrollX: false, isScrollY: false, columnTotal, rowTotal }; if (columnTotal > width && rowTotal > height) { result.isScrollX = true; result.isScrollY = true; } else if (columnTotal <= width && rowTotal <= height) { result.isScrollX = false; result.isScrollY = false; } else if (columnTotal <= width && rowTotal > height) { result.isScrollX = columnTotal + scrollbarSize > width; result.isScrollY = true; } else if (columnTotal > width && rowTotal <= height) { result.isScrollX = true; result.isScrollY = rowTotal + scrollbarSize > height; } return result; }; const preprocessVirtualScrollSlice = ({ items, itemSizes, fixedItems }) => { const hasStart = fixedItems[0].length > 0; const hasEnd = fixedItems[1].length > 0; const startInfos = []; const infos = []; const endInfos = []; let startSize = 0; let endSize = 0; items.forEach((item, i) => { const raw = item; const size = itemSizes[i]; const info = { raw, size, start: 0, end: 0, index: i, fixed: void 0, offset: void 0, startLast: void 0, endFirst: void 0, isLast: void 0 }; if (hasStart && fixedItems[0].includes(i)) { startInfos.push(info); startSize += size; info.fixed = "start"; } else if (hasEnd && fixedItems[1].includes(i)) { endInfos.push(info); endSize += size; info.fixed = "end"; } else { infos.push(info); } }); let allInfos = [...infos]; if (hasStart || hasEnd) { allInfos = [...startInfos, ...infos, ...endInfos]; if (hasStart && startInfos[startInfos.length - 1]) { startInfos[startInfos.length - 1].startLast = true; } if (hasEnd && endInfos[0]) { endInfos[0].endFirst = true; } } if (allInfos[allInfos.length - 1]) { allInfos[allInfos.length - 1].isLast = true; } let reverseAllInfos = [...allInfos].reverse(); let total = 0; allInfos.forEach((info) => { info.start = total; total += info.size; }); total = 0; reverseAllInfos.forEach((info) => { info.end = total; total += info.size; }); return { startSize, endSize, startInfos, infos, endInfos }; }; const virtualScrollSlice = ({ size, buffer, startSize, endSize, startInfos, infos, endInfos, pos, isScroll, barSize }) => { const list = []; const viewStart = pos + startSize - buffer; const viewEnd = pos + size - endSize + buffer - (isScroll ? barSize : 0); startInfos.forEach((info) => list.push(info)); let firstInfo, firstOffset; for (let i = 0; i < infos.length; i++) { const info = infos[i]; const itemStart = info.start; const itemEnd = itemStart + info.size; if (itemEnd < viewStart) { continue; } if (itemStart >= viewStart && itemStart < viewEnd || itemEnd > viewStart && itemEnd <= viewEnd) { list.push(info); if (!firstInfo) { firstInfo = info; firstOffset = itemStart - (pos + startSize); } info.offset = firstOffset; } else if (firstInfo) { break; } } endInfos.forEach((info) => list.push(info)); return list; }; const createVirtualScroll = ({ api, props, state }) => () => { let { width, height, columns, columnSizes, rows, rowSizes, columnBuffer, rowBuffer, fixedColumns, fixedRows } = props; let { scrollbarSize } = state; width = +width; height = +height; columnBuffer = +columnBuffer; rowBuffer = +rowBuffer; const { isScrollX, isScrollY, columnTotal, rowTotal } = api.analyzeScrolling(); const boxStyle = { width: `${width}px`, height: `${height}px`, position: "relative", overflow: "auto" }; const xAxisStyle = { width: `${columnTotal}px`, height: "1px" }; const yAxisStyle = { height: `${rowTotal - 1}px`, width: "1px" }; const viewBaseStyle = { width: "100%", height: "100%", position: "absolute", overflow: "clip" }; const { startSize: colStartSize, endSize: colEndSize, startInfos: colStartInfos, infos: colInfos, endInfos: colEndInfos } = preprocessVirtualScrollSlice({ items: columns, itemSizes: columnSizes, fixedItems: fixedColumns }); const { startSize: rowStartSize, endSize: rowEndSize, startInfos: rowStartInfos, infos: rowInfos, endInfos: rowEndInfos } = preprocessVirtualScrollSlice({ items: rows, itemSizes: rowSizes, fixedItems: fixedRows }); return { sliceColumn: (scrollLeft) => virtualScrollSlice({ pos: scrollLeft, size: width, buffer: columnBuffer, startSize: colStartSize, endSize: colEndSize, startInfos: colStartInfos, infos: colInfos, endInfos: colEndInfos, isScroll: isScrollY, barSize: scrollbarSize }), sliceRow: (scrollTop) => virtualScrollSlice({ pos: scrollTop, size: height, buffer: rowBuffer, startSize: rowStartSize, endSize: rowEndSize, startInfos: rowStartInfos, infos: rowInfos, endInfos: rowEndInfos, isScroll: isScrollX, barSize: scrollbarSize }), buildView: ({ slicedCols, slicedRows, scrollLeft, scrollTop }) => { const viewRows = []; const viewCols = []; const result = { isScrollX, isScrollY, boxStyle, xAxisStyle, yAxisStyle, viewStyle: __spreadValues({ left: `${scrollLeft}px`, top: `${scrollTop}px` }, viewBaseStyle), viewRows, viewCols }; let rowWidth = 0; slicedCols.forEach((info) => { rowWidth += info.size; viewCols.push({ info, key: `c-${info.index}`, style: { width: `${info.size}px`, position: info.fixed ? "sticky" : void 0, zIndex: info.fixed ? 1 : void 0, left: info.fixed === "start" ? `${info.start}px` : void 0, right: info.fixed === "end" ? `${info.end}px` : void 0, transform: info.offset === void 0 ? void 0 : `translateX(${info.offset}px)` } }); }); slicedRows.forEach((info) => { viewRows.push({ info, key: `r-${info.index}`, style: { width: `${rowWidth}px`, height: `${info.size}px`, position: info.fixed ? "sticky" : void 0, top: info.fixed === "start" ? `${info.start}px` : void 0, bottom: info.fixed === "end" ? `${info.end}px` : void 0, zIndex: info.fixed ? 2 : void 0, transform: info.offset === void 0 ? void 0 : `translateY(${info.offset}px)` } }); }); return result; } }; }; const onScroll = ({ emit, state }) => (e, force = false) => { const sLeft = e ? e.target.scrollLeft : 0; const sTop = e ? e.target.scrollTop : 0; const rangeThreshold = 5; if (state.scrollLeft !== sLeft || force) { state.scrollLeft = sLeft; state.slicedCols = state.vs.sliceColumn(sLeft); } if (state.scrollTop !== sTop || force) { state.scrollTop = sTop; state.slicedRows = state.vs.sliceRow(sTop); } state.ctx = state.vs.buildView({ slicedCols: state.slicedCols, slicedRows: state.slicedRows, scrollLeft: sLeft, scrollTop: sTop }); if (state.isReady) { state.isTop = sTop < rangeThreshold; state.isBottom = sTop > state.maxTop - rangeThreshold; state.isLeft = sLeft < rangeThreshold; state.isRight = sLeft > state.maxLeft - rangeThreshold; } state.slotParams = { viewRows: state.ctx.viewRows, viewCols: state.ctx.viewCols, isScrollX: state.ctx.isScrollX, isScrollY: state.ctx.isScrollY, isTop: state.isTop, isBottom: state.isBottom, isLeft: state.isLeft, isRight: state.isRight }; emit("change", state.slotParams); }; const refresh = ({ api, state, vm, nextTick }) => (options = {}) => { const { isKeepScrollTop = false, isKeepScrollLeft = false } = options || {}; const lastTop = isKeepScrollTop ? state.scrollTop : 0; const lastLeft = isKeepScrollLeft ? state.scrollLeft : 0; state.vs = api.createVirtualScroll(); api.onScroll({ target: { scrollLeft: lastLeft, scrollTop: lastTop } }, true); nextTick(() => { state.maxTop = vm.$el.scrollHeight - vm.$el.offsetHeight; state.maxLeft = vm.$el.scrollWidth - vm.$el.offsetWidth; vm.$el.scrollTop = lastTop; vm.$el.scrollLeft = lastLeft; }); }; export { analyzeScrolling, computedBarSize, createVirtualScroll, onScroll, refresh };