vxe-pc-ui
Version:
A vue based PC component library
280 lines (254 loc) • 9.18 kB
text/typescript
import XEUtils from 'xe-utils'
let tpImgEl: HTMLImageElement | undefined
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
}
const reClsMap: { [key: string]: any } = {}
function getClsRE (cls: any) {
if (!reClsMap[cls]) {
reClsMap[cls] = new RegExp(`(?:^|\\s)${cls}(?!\\S)`, 'g')
}
return reClsMap[cls]
}
function getNodeOffset (elem: any, container: any, rest: any): any {
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: any) {
return val && /^\d+(\.\d+)?(px)?$/.test(val)
}
export function isScale (val: any) {
return val && /^\d+(\.\d+)?%$/.test(val)
}
export function hasClass (elem: any, cls: any) {
return !!(elem && elem.className && elem.className.match && elem.className.match(getClsRE(cls)))
}
export function removeClass (elem: any, cls: any) {
if (elem && hasClass(elem, cls)) {
elem.className = elem.className.replace(getClsRE(cls), '')
}
}
export function addClass (elem: any, cls: string) {
if (elem && !hasClass(elem, cls)) {
removeClass(elem, cls)
elem.className = `${elem.className} ${cls}`
}
}
export function hasControlKey (evnt: KeyboardEvent | MouseEvent | DragEvent) {
return evnt.ctrlKey || evnt.metaKey
}
export function toCssUnit (val?: number | string | null, unit = 'px') {
if (XEUtils.isNumber(val) || /^\d+$/.test(`${val}`)) {
return `${val}${unit}`
}
return `${val || ''}`
}
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 getEventTargetNode (evnt: any, container: any, queryCls?: string, queryMethod?: (target: Element) => boolean) {
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: any, container: any) {
return getNodeOffset(elem, container, { left: 0, top: 0 })
}
export function getAbsolutePos (elem: any) {
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 getPaddingTopBottomSize (elem: HTMLElement) {
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 getPaddingLeftRightSize (elem: HTMLElement) {
if (elem) {
const computedStyle = getComputedStyle(elem)
const paddingLeft = XEUtils.toNumber(computedStyle.paddingLeft)
const paddingRight = XEUtils.toNumber(computedStyle.paddingRight)
return paddingLeft + paddingRight
}
return 0
}
const scrollIntoViewIfNeeded = 'scrollIntoViewIfNeeded'
const scrollIntoView = 'scrollIntoView'
export function scrollToView (elem: any) {
if (elem) {
if (elem[scrollIntoViewIfNeeded]) {
elem[scrollIntoViewIfNeeded]()
} else if (elem[scrollIntoView]) {
elem[scrollIntoView]()
}
}
}
export function triggerEvent (targetElem: Element, type: string) {
if (targetElem) {
targetElem.dispatchEvent(new Event(type))
}
}
export function isNodeElement (elem: any): elem is HTMLElement {
return elem && elem.nodeType === 1
}
export function updatePanelPlacement (targetElem: HTMLElement | null | undefined, panelElem: HTMLElement | null | undefined, options: {
placement?: '' | 'top' | 'bottom' | null
defaultPlacement?: '' | 'top' | 'bottom' | null
teleportTo?: boolean
marginSize?: number
}) {
const { placement, defaultPlacement, teleportTo, marginSize } = Object.assign({ teleportTo: false, marginSize: 18 }, options)
let panelPlacement: 'top' | 'bottom' = 'bottom'
let top: number | '' = ''
let bottom: number | '' = ''
let left: number | '' = ''
const right: number | '' = ''
let minWidth: number | '' = ''
const stys: Record<string, string> = {}
if (panelElem && targetElem) {
const documentElement = document.documentElement
const bodyElem = document.body
const targetHeight = targetElem.offsetHeight
const panelHeight = panelElem.offsetHeight
const panelWidth = panelElem.offsetWidth
const panelRect = panelElem.getBoundingClientRect()
const targetRect = targetElem.getBoundingClientRect()
const visibleHeight = documentElement.clientHeight || bodyElem.clientHeight
const visibleWidth = documentElement.clientWidth || bodyElem.clientWidth
minWidth = targetElem.offsetWidth
if (teleportTo) {
left = targetRect.left
top = targetRect.top + targetHeight
if (placement === 'top') {
panelPlacement = 'top'
top = targetRect.top - panelHeight
} else if (!placement) {
if (defaultPlacement === 'top') {
panelPlacement = 'top'
top = targetRect.top - panelHeight
// 如果上面不够放,则向下
if (top < marginSize) {
panelPlacement = 'bottom'
top = targetRect.top + targetHeight
}
// 如果下面不够放,则向上(优先)
if (top + panelHeight + marginSize > visibleHeight) {
panelPlacement = 'top'
top = targetRect.top - panelHeight
}
} else {
// 如果下面不够放,则向上
if (top + panelHeight + marginSize > visibleHeight) {
panelPlacement = 'top'
top = targetRect.top - panelHeight
}
// 如果上面不够放,则向下(优先)
if (top < marginSize) {
panelPlacement = 'bottom'
top = targetRect.top + targetHeight
}
}
}
// 如果溢出右边
if (left + panelWidth + marginSize > visibleWidth) {
left -= left + panelWidth + marginSize - visibleWidth
}
// 如果溢出左边
if (left < marginSize) {
left = marginSize
}
} else {
if (placement === 'top') {
panelPlacement = 'top'
bottom = targetHeight
} else if (!placement) {
// 如果下面不够放,则向上
top = targetHeight
if (targetRect.top + targetHeight + panelHeight + marginSize > visibleHeight) {
// 如果上面不够放,则向下(优先)
if (targetRect.top - 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 (XEUtils.isNumber(right)) {
stys.right = toCssUnit(right)
}
if (XEUtils.isNumber(minWidth)) {
stys.minWidth = toCssUnit(minWidth)
}
}
return {
top: top || 0,
bottom: bottom || 0,
left: left || 0,
right: right || 0,
style: stys,
placement: panelPlacement
}
}