@fe6/water-pro
Version:
An enterprise-class UI design language and Vue-based implementation
266 lines (235 loc) • 7.21 kB
text/typescript
/** @format */
import { firstCapitalize } from '@fe6/shared';
import warning from './warning';
import ResizeObserver from 'resize-observer-polyfill';
export interface ViewportOffsetResult {
left: number;
top: number;
right: number;
bottom: number;
rightIncludeBody: number;
bottomIncludeBody: number;
}
export function getBoundingClientRect(element: Element): DOMRect | number {
if (!element || !element.getBoundingClientRect) {
return 0;
}
return element.getBoundingClientRect();
}
const trim = function (string: string) {
return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '');
};
/* istanbul ignore next */
export function hasClass(el: Element, cls: string) {
if (!el || !cls) {
return false;
}
if (cls.includes(' ')) {
throw warning('className 不应包含空格');
}
if (el.classList) {
return el.classList.contains(cls);
} else {
return ` ${el.className} `.includes(` ${cls} `);
}
}
/* istanbul ignore next */
export function addClass(el: Element, cls: string) {
if (!el) {
return;
}
let curClass = el.className;
const classes = (cls || '').split(' ');
for (let i = 0, j = classes.length; i < j; i++) {
const clsName = classes[i];
if (!clsName) {
continue;
}
if (el.classList) {
el.classList.add(clsName);
} else if (!hasClass(el, clsName)) {
curClass += ` ${clsName}`;
}
}
if (!el.classList) {
el.className = curClass;
}
}
/* istanbul ignore next */
export function removeClass(el: Element, cls: string) {
if (!el || !cls) {
return;
}
const classes = cls.split(' ');
let curClass = ` ${el.className} `;
for (let i = 0, j = classes.length; i < j; i++) {
const clsName = classes[i];
if (!clsName) {
continue;
}
if (el.classList) {
el.classList.remove(clsName);
} else if (hasClass(el, clsName)) {
curClass = curClass.replace(` ${clsName} `, ' ');
}
}
if (!el.classList) {
el.className = trim(curClass);
}
}
/**
* Get the left and top offset of the current element
* left: the distance between the leftmost element and the left side of the document
* top: the distance from the top of the element to the top of the document
* right: the distance from the far right of the element to the right of the document
* bottom: the distance from the bottom of the element to the bottom of the document
* rightIncludeBody: the distance between the leftmost element and the right side of the document
* bottomIncludeBody: the distance from the bottom of the element to the bottom of the document
*
* @description:
*/
export function getViewportOffset(element: Element): ViewportOffsetResult {
const doc = document.documentElement;
const docScrollLeft = doc.scrollLeft;
const docScrollTop = doc.scrollTop;
const docClientLeft = doc.clientLeft;
const docClientTop = doc.clientTop;
const pageXOffset = window.pageXOffset;
const pageYOffset = window.pageYOffset;
const box = getBoundingClientRect(element);
const { left: retLeft, top: rectTop, width: rectWidth, height: rectHeight } = box as DOMRect;
const scrollLeft = (pageXOffset || docScrollLeft) - (docClientLeft || 0);
const scrollTop = (pageYOffset || docScrollTop) - (docClientTop || 0);
const offsetLeft = retLeft + pageXOffset;
const offsetTop = rectTop + pageYOffset;
const left = offsetLeft - scrollLeft;
const top = offsetTop - scrollTop;
const clientWidth = window.document.documentElement.clientWidth;
const clientHeight = window.document.documentElement.clientHeight;
return {
left,
top,
right: clientWidth - rectWidth - left,
bottom: clientHeight - rectHeight - top,
rightIncludeBody: clientWidth - left,
bottomIncludeBody: clientHeight - top,
};
}
export function hackCss(attr: string, value: string) {
const prefix: string[] = ['webkit', 'Moz', 'ms', 'OT'];
const styleObj: any = {};
prefix.forEach((item) => {
styleObj[`${item}${firstCapitalize(attr)}`] = value;
});
return {
...styleObj,
[attr]: value,
};
}
/* istanbul ignore next */
export function on(
element: Element | HTMLElement | Document | Window,
event: string,
handler: EventListenerOrEventListenerObject,
): void {
if (element && event && handler) {
element.addEventListener(event, handler, false);
}
}
/* istanbul ignore next */
export function off(
element: Element | HTMLElement | Document | Window,
event: string,
handler: Fn,
): void {
if (element && event && handler) {
element.removeEventListener(event, handler, false);
}
}
/* istanbul ignore next */
export function once(el: HTMLElement, event: string, fn: EventListener): void {
const listener = function (this: any, ...args: unknown[]) {
if (fn) {
fn.apply(this, args as any);
}
off(el, event, listener);
};
on(el, event, listener);
}
/**
* triggter window.resize
*/
export function triggerWindowResize() {
const event = document.createEvent('HTMLEvents');
event.initEvent('resize', true, true);
(event as any).eventType = 'message';
window.dispatchEvent(event);
}
/**
* @description: Set ui mount node
*/
export function getPopupContainer(node?: HTMLElement): HTMLElement {
return (node?.parentNode as HTMLElement) ?? document.body;
}
export const isServer = typeof window === 'undefined';
/* istanbul ignore next */
function resizeHandler(entries: any[]) {
for (const entry of entries) {
const listeners = entry.target.__resizeListeners__ || [];
if (listeners.length) {
listeners.forEach((fn: () => any) => {
fn();
});
}
}
}
/* istanbul ignore next */
export function addResizeListener(element: any, fn: () => any) {
if (isServer) {
return;
}
if (!element.__resizeListeners__) {
element.__resizeListeners__ = [];
element.__ro__ = new ResizeObserver(resizeHandler);
element.__ro__.observe(element);
}
element.__resizeListeners__.push(fn);
}
/* istanbul ignore next */
export function removeResizeListener(element: any, fn: () => any) {
if (!element || !element.__resizeListeners__) {
return;
}
element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1);
if (!element.__resizeListeners__.length) {
element.__ro__.disconnect();
}
}
export const getCursortPositionFormTextArea = (ctrl: any) => {
const CaretPos = {
start: 0,
end: 0,
};
if (ctrl.selectionStart) {
// Firefox support
CaretPos.start = ctrl.selectionStart;
}
if (ctrl.selectionEnd) {
CaretPos.end = ctrl.selectionEnd;
}
return CaretPos;
};
// 设置光标位置
export function setCaretPosition(ctrl: any, pos: number) {
// 设置光标位置函数
if (ctrl.setSelectionRange) {
ctrl.focus(); // 获取焦点
ctrl.setSelectionRange(pos, pos); // 设置选定区的开始和结束点
} else if (ctrl.createTextRange) {
const range = ctrl.createTextRange(); // 创建选定区
range.collapse(true); // 设置为折叠,即光标起点和结束点重叠在一起
range.moveEnd('character', pos); // 移动结束点
range.moveStart('character', pos); // 移动开始点
range.select(); // 选定当前区域
}
}