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