vxe-table
Version:
A PC-end table component based on Vxe UI, supporting copy-paste, data pivot table, and high-performance virtual list table solution.
413 lines (412 loc) • 14.5 kB
JavaScript
import XEUtils from 'xe-utils';
const reClsMap = {};
let tpImgEl;
export function initTpImg() {
if (!tpImgEl) {
tpImgEl = new Image();
tpImgEl.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
}
return tpImgEl;
}
export function getTpImg() {
if (!tpImgEl) {
return initTpImg();
}
return tpImgEl;
}
export function getPropClass(property, params) {
return property ? (XEUtils.isFunction(property) ? property(params) : property) || '' : '';
}
function getClsRE(cls) {
if (!reClsMap[cls]) {
reClsMap[cls] = new RegExp(`(?:^|\\s)${cls}(?!\\S)`, 'g');
}
return reClsMap[cls];
}
function getNodeOffset(elem, container, rest) {
if (elem) {
const parentElem = elem.parentNode;
rest.top += elem.offsetTop;
rest.left += elem.offsetLeft;
if (parentElem && parentElem !== document.documentElement && parentElem !== document.body) {
rest.top -= parentElem.scrollTop;
rest.left -= parentElem.scrollLeft;
}
if (container && (elem === container || elem.offsetParent === container) ? 0 : elem.offsetParent) {
return getNodeOffset(elem.offsetParent, container, rest);
}
}
return rest;
}
export function isPx(val) {
return val && /^\d+(\.\d+)?(px)?$/.test(val);
}
export function isScale(val) {
return val && /^\d+(\.\d+)?%$/.test(val);
}
export function hasClass(elem, cls) {
if (elem) {
if (elem.classList) {
return elem.classList.contains(cls);
}
return !!(elem.className && elem.className.match && elem.className.match(getClsRE(cls)));
}
return false;
}
export function removeClass(elem, cls) {
if (elem && hasClass(elem, cls)) {
elem.className = elem.className.replace(getClsRE(cls), '');
}
}
export function addClass(elem, cls) {
if (elem && !hasClass(elem, cls)) {
removeClass(elem, cls);
elem.className = `${elem.className} ${cls}`;
}
}
export function hasControlKey(evnt) {
return evnt.ctrlKey || evnt.metaKey;
}
export function toCssUnit(val, unit = 'px') {
if (XEUtils.isNumber(val) || /^\d+$/.test(`${val}`)) {
return `${val}${unit}`;
}
return `${val || ''}`;
}
export function queryElement(elem, selector) {
if (elem) {
return elem.querySelector(selector);
}
return null;
}
export function getDomNode() {
const documentElement = document.documentElement;
const bodyElem = document.body;
return {
scrollTop: documentElement.scrollTop || bodyElem.scrollTop,
scrollLeft: documentElement.scrollLeft || bodyElem.scrollLeft,
visibleHeight: documentElement.clientHeight || bodyElem.clientHeight,
visibleWidth: documentElement.clientWidth || bodyElem.clientWidth
};
}
export function getOffsetHeight(elem) {
return elem ? elem.offsetHeight : 0;
}
export function getPaddingTopBottomSize(elem) {
if (elem) {
const computedStyle = getComputedStyle(elem);
const paddingTop = XEUtils.toNumber(computedStyle.paddingTop);
const paddingBottom = XEUtils.toNumber(computedStyle.paddingBottom);
return paddingTop + paddingBottom;
}
return 0;
}
export function setScrollTop(elem, scrollTop) {
if (elem) {
elem.scrollTop = scrollTop;
}
}
export function setScrollLeft(elem, scrollLeft) {
if (elem) {
elem.scrollLeft = scrollLeft;
}
}
// export function setScrollLeftAndTop (elem: HTMLElement | null, scrollLeft: number, scrollTop: number) {
// if (elem) {
// elem.scrollLeft = scrollLeft
// elem.scrollTop = scrollTop
// }
// }
export function updateCellTitle(overflowElem, column) {
const content = column.type === 'html' ? overflowElem.innerText : overflowElem.textContent;
if (overflowElem.getAttribute('title') !== content) {
overflowElem.setAttribute('title', content);
}
}
export function checkTargetElement(target, exEls, endEl) {
let targetEl = target;
if (!exEls || !exEls.length) {
return false;
}
const [exEl1, exEl2, exEl3] = exEls;
while (targetEl) {
if (exEl1 === targetEl || (exEl2 && targetEl === exEl2) || (exEl3 && targetEl === exEl3)) {
return true;
}
if (endEl && targetEl === endEl) {
return false;
}
targetEl = targetEl.parentElement;
}
return false;
}
/**
* 检查触发源是否属于目标节点
*/
export function getEventTargetNode(evnt, container, queryCls, queryMethod) {
let targetElem;
let target = (evnt.target.shadowRoot && evnt.composed) ? (evnt.composedPath()[0] || evnt.target) : evnt.target;
while (target && target.nodeType && target !== document) {
if (queryCls && hasClass(target, queryCls) && (!queryMethod || queryMethod(target))) {
targetElem = target;
}
else if (target === container) {
return { flag: queryCls ? !!targetElem : true, container, targetElem: targetElem };
}
target = target.parentNode;
}
return { flag: false };
}
/**
* 获取元素相对于 document 的位置
*/
export function getOffsetPos(elem, container) {
return getNodeOffset(elem, container, { left: 0, top: 0 });
}
export function getAbsolutePos(elem) {
const bounding = elem.getBoundingClientRect();
const boundingTop = bounding.top;
const boundingLeft = bounding.left;
const { scrollTop, scrollLeft, visibleHeight, visibleWidth } = getDomNode();
return { boundingTop, top: scrollTop + boundingTop, boundingLeft, left: scrollLeft + boundingLeft, visibleHeight, visibleWidth };
}
export function hasEventInputTarget(target) {
const tagName = target ? target.tagName : '';
return tagName && ['input', 'textarea'].includes((tagName.toLowerCase()));
}
/**
* 判断是否由内部可复用控件触发,输入框带有 vxe-reusekeep-control
*/
function hasReusekeepControl(elem) {
if (elem) {
return hasClass(elem, 'vxe-reusekeep-control');
}
return false;
}
export function hasAxternalInputTarget(target) {
return hasEventInputTarget(target) ? !hasReusekeepControl(target) : false;
}
const scrollIntoViewIfNeeded = 'scrollIntoViewIfNeeded';
const scrollIntoView = 'scrollIntoView';
export function scrollToView(elem) {
if (elem) {
if (elem[scrollIntoViewIfNeeded]) {
elem[scrollIntoViewIfNeeded]();
}
else if (elem[scrollIntoView]) {
elem[scrollIntoView]();
}
}
}
export function triggerEvent(targetElem, type) {
if (targetElem) {
targetElem.dispatchEvent(new Event(type));
}
}
export function isNodeElement(elem) {
return elem && elem.nodeType === 1;
}
export function scrollTopTo(diffNum, cb) {
const duration = Math.abs(diffNum);
const startTime = performance.now();
let countTop = 0;
const step = (timestamp) => {
let progress = (timestamp - startTime) / duration;
if (progress < 0) {
progress = 0;
}
else if (progress > 1) {
progress = 1;
}
const easedProgress = Math.pow(progress, 2);
const offsetTop = Math.floor((diffNum * easedProgress)) - countTop;
countTop += offsetTop;
cb(offsetTop);
if (progress < 1) {
requestAnimationFrame(step);
}
};
requestAnimationFrame(step);
}
let wtlFrame;
export function wheelScrollLeftTo(scrollLeft, cb) {
if (wtlFrame) {
cancelAnimationFrame(wtlFrame);
}
wtlFrame = requestAnimationFrame(() => {
cb(scrollLeft);
wtlFrame = null;
});
}
let wtaFrame;
export function wheelScrollTopTo(diffNum, cb) {
if (wtaFrame) {
cancelAnimationFrame(wtaFrame);
}
wtaFrame = requestAnimationFrame(() => {
const offsetTop = diffNum;
cb(offsetTop);
wtaFrame = null;
});
}
/**
* 通用定位计算
*/
export function updatePanelPlacement(targetElem, panelElem, options) {
const { defaultTop, defaultLeft, placement, defaultPlacement, teleportTo, marginSize, isMinWidth } = Object.assign({
teleportTo: false,
marginSize: 18,
isMinWidth: true
}, options);
let panelPlacement = 'bottom';
let top = '';
let bottom = '';
let left = 0;
let minWidth = '';
let arrowLeft = '';
const stys = {};
if (panelElem && targetElem) {
const bodyEl = document.body;
const parentWrapperEl = getPopupWrapperElement(panelElem);
if (parentWrapperEl) {
const targetWidth = targetElem.offsetWidth;
const targetHeight = targetElem.offsetHeight;
const panelWidth = panelElem.offsetWidth;
const panelHeight = panelElem.offsetHeight;
const parentWrapperRect = parentWrapperEl.getBoundingClientRect();
const panelRect = panelElem.getBoundingClientRect();
const targetRect = targetElem.getBoundingClientRect();
const visibleHeight = bodyEl.clientHeight;
const visibleWidth = bodyEl.clientWidth;
const offsetLeft = parentWrapperRect.left;
const offsetTop = parentWrapperRect.top;
const targetLeft = targetRect.left;
const targetTop = targetRect.top;
minWidth = targetElem.offsetWidth;
if (teleportTo) {
left = defaultLeft || (targetLeft - (panelWidth - targetWidth) / 2);
top = defaultTop || (targetTop + targetHeight);
if (placement === 'top') {
panelPlacement = 'top';
top = targetTop - panelHeight;
}
else if (!placement) {
if (defaultPlacement === 'top') {
panelPlacement = 'top';
if (!defaultTop) {
top = targetTop - panelHeight;
}
// 如果上面不够放,则向下
if (top < marginSize) {
panelPlacement = 'bottom';
top = targetTop + targetHeight;
}
// 如果下面不够放,则向上(优先)
if (top + panelHeight + marginSize > visibleHeight) {
panelPlacement = 'top';
top = targetTop - panelHeight;
}
}
else {
// 如果下面不够放,则向上
if (top + panelHeight + marginSize > visibleHeight) {
panelPlacement = 'top';
top = targetTop - panelHeight;
}
// 如果上面不够放,则向下(优先)
if (top < marginSize) {
panelPlacement = 'bottom';
top = targetTop + targetHeight;
}
}
}
// 如果溢出右边
if (left + panelWidth + marginSize > visibleWidth) {
left -= left + panelWidth + marginSize - visibleWidth;
}
// 如果溢出左边
if (left < marginSize) {
left = marginSize;
}
// 偏移
top -= offsetTop;
left -= offsetLeft;
}
else {
if (placement === 'top') {
panelPlacement = 'top';
bottom = targetHeight;
}
else if (!placement) {
// 如果下面不够放,则向上
top = targetHeight;
if (targetTop + targetHeight + panelHeight + marginSize > visibleHeight) {
// 如果上面不够放,则向下(优先)
if (targetTop - targetHeight - panelHeight > marginSize) {
panelPlacement = 'top';
top = '';
bottom = targetHeight;
}
}
}
// 是否超出右侧
if (panelRect.left + panelRect.width + marginSize > visibleWidth) {
left = -(panelRect.left + panelRect.width + marginSize - visibleWidth);
}
}
if (XEUtils.isNumber(top)) {
stys.top = toCssUnit(top);
}
if (XEUtils.isNumber(bottom)) {
stys.bottom = toCssUnit(bottom);
}
if (XEUtils.isNumber(left)) {
stys.left = toCssUnit(left);
}
if (isMinWidth && XEUtils.isNumber(minWidth)) {
stys.minWidth = toCssUnit(minWidth);
}
// 箭头
if (left === targetLeft) {
if (targetWidth <= panelWidth) {
arrowLeft = targetWidth / 2;
}
}
else if (left < targetLeft) {
if (left + panelWidth > targetLeft + targetWidth) {
arrowLeft = (targetLeft - left) + targetWidth / 2;
}
else {
arrowLeft = (targetLeft - left) + (panelWidth - (targetLeft - left)) / 2;
}
}
}
}
return {
top: top || 0,
bottom: bottom || 0,
left: left || 0,
arrowLeft: arrowLeft || 0,
style: stys,
placement: panelPlacement
};
}
export function getPopupWrapperElement(panelElem) {
if (!panelElem) {
return null;
}
const bodyEl = document.body;
const parentEl = panelElem.parentElement;
return (parentEl === bodyEl ? document.documentElement : parentEl) || null;
}
export function getPopupContainer(appendTo) {
const selectElem = appendTo && XEUtils.isFunction(appendTo) ? appendTo({}) : appendTo;
return selectElem || 'body';
}
export function getPopupAppendElement(appendTo) {
const selectElem = getPopupContainer(appendTo);
if (XEUtils.isString(selectElem)) {
return document.querySelector(selectElem) || document.body;
}
return selectElem;
}