packages/shared/src/element.ts (136 lines of code) (raw):

import { Point } from './coordinate' const InlineLayoutTagNames = new Set([ 'A', 'ABBR', 'ACRONYM', 'AUDIO', 'B', 'BDI', 'BDO', 'BIG', 'BR', 'BUTTON', 'CANVAS', 'CITE', 'CODE', 'DATA', 'DATALIST', 'DEL', 'DFN', 'EM', 'EMBED', 'I', 'IFRAME', 'IMG', 'INS', 'KBD', 'LABEL', 'MAP', 'MARK', 'METER', 'NOSCRIPT', 'OBJECT', 'OUTPUT', 'PICTURE', 'PROGRESS', 'Q', 'RUBY', 'S', 'SAMP', 'SELECT', 'SLOT', 'SMALL', 'STRONG', 'SUB', 'SUP', 'SVG', 'TEMPLATE', 'TEXTAREA', 'TIME', 'U', 'TT', 'VAR', 'VIDEO', 'WBR', 'INPUT', 'SPAN', ]) export const calcElementOuterWidth = ( innerWidth: number, style: CSSStyleDeclaration ) => { return ( innerWidth + parseFloat(style.marginLeft) + parseFloat(style.marginRight) + parseFloat(style.paddingLeft) + parseFloat(style.paddingRight) + parseFloat(style.borderLeftWidth) + parseFloat(style.borderRightWidth) ) } export const calcElementLayout = (element: Element) => { if (!element) return 'vertical' const parent = element.parentElement if (!parent) return 'vertical' const tagName = element.tagName const parentTagName = parent.tagName const style = getComputedStyle(element) const parentStyle = getComputedStyle(parent) const isNotFullWidth = () => { const innerWidth = element.getBoundingClientRect().width const outerWidth = calcElementOuterWidth(innerWidth, style) const parentInnerWidth = parent.getBoundingClientRect().width return outerWidth.toFixed(0) < parentInnerWidth.toFixed(0) } if (tagName === 'TH' || tagName === 'TD') { if (parentTagName === 'TR') return 'horizontal' } if (parentStyle.display === 'flex' && parentStyle.flexDirection === 'row') return 'horizontal' if (parentStyle.display === 'grid') { if (isNotFullWidth()) { return 'horizontal' } } if (InlineLayoutTagNames.has(tagName)) { if (style.display === 'block') { if (style.float === 'left' || style.float === 'right') { if (isNotFullWidth()) { return 'horizontal' } } return 'vertical' } return 'horizontal' } } export const calcElementTranslate = (element: HTMLElement) => { const transform = element?.style?.transform if (transform) { const [x, y] = transform .match( /translate(?:3d)?\(\s*([-\d.]+)[a-z]+?[\s,]+([-\d.]+)[a-z]+?(?:[\s,]+([-\d.]+))?[a-z]+?\s*\)/ ) ?.slice(1, 3) ?? [0, 0] return new Point(Number(x), Number(y)) } else { return new Point(Number(element.offsetLeft), Number(element.offsetTop)) } } export const calcElementRotate = (element: HTMLElement) => { const transform = element?.style?.transform if (transform) { return Number(transform.match(/rotate\(\s*([-\d.]+)/)?.[1] ?? 0) } else { return 0 } } export const calcElementScale = (element: HTMLElement) => { const transform = element?.style?.transform if (transform) { return Number(transform.match(/scale\(\s*([-\d.]+)/)?.[1] ?? 0) } else { return 0 } }