Folo/packages/internal/utils/src/dom.ts

91 lines
2.9 KiB
TypeScript

export const stopPropagation = <T extends { stopPropagation: () => any }>(e: T) =>
e.stopPropagation()
export const preventDefault = <T extends { preventDefault: () => any }>(e: T) => e.preventDefault()
export const nextFrame = (fn: (...args: any[]) => any) => {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
fn()
})
})
}
export const getElementTop = (element: HTMLElement) => {
let actualTop = element.offsetTop
let current = element.offsetParent as HTMLElement
while (current !== null) {
actualTop += current.offsetTop
current = current.offsetParent as HTMLElement
}
return actualTop
}
export const clearSelection = () => window.getSelection()?.removeAllRanges()
export const findElementInShadowDOM = (selector: string): HTMLElement | null => {
const element = document.querySelector(selector)
if (element) return element as HTMLElement
// find in all shadow roots
const getAllShadowRoots = (root: Document | Element | ShadowRoot): Element[] => {
const hosts = Array.from(root.querySelectorAll("*")).filter((el) => el.shadowRoot)
return hosts.reduce((acc, host) => {
if (host.shadowRoot) {
const shadowElement = host.shadowRoot.querySelector(selector)
if (shadowElement) acc.push(shadowElement)
return [...acc, ...getAllShadowRoots(host.shadowRoot)]
}
return acc
}, [] as Element[])
}
const results = getAllShadowRoots(document)
return (results[0] as HTMLElement) || null
}
export function composeEventHandlers<E>(
originalEventHandler?: ((event: E) => void) | null,
ourEventHandler?: ((event: E) => void) | null,
{ checkForDefaultPrevented = true } = {},
) {
return function handleEvent(event: E) {
originalEventHandler?.(event)
if (checkForDefaultPrevented === false || !(event as unknown as Event).defaultPrevented) {
return ourEventHandler?.(event)
}
}
}
export function getNodeXInScroller(node: Element, scroller: Element) {
const nodeRect = node.getBoundingClientRect()
const scrollerRect = scroller.getBoundingClientRect()
return nodeRect.left - scrollerRect.left + scroller.scrollLeft
}
/**
* Determines whether the node is visible in the horizontal scroll container
*/
export function isNodeVisibleInScroller(node: Element, scroller: Element) {
const nodeRect = node.getBoundingClientRect()
const scrollerRect = scroller.getBoundingClientRect()
const nodeLeft = nodeRect.left - scrollerRect.left + scroller.scrollLeft
const nodeRight = nodeLeft + nodeRect.width
const scrollerLeft = scroller.scrollLeft
const scrollerRight = scrollerLeft + scroller.clientWidth
return nodeRight > scrollerLeft && nodeLeft < scrollerRight
}
export const checkIsEditableElement = (element: HTMLElement) => {
return (
element.isContentEditable ||
element.getAttribute("contenteditable") === "true" ||
(element instanceof HTMLInputElement && element.type === "text") ||
element instanceof HTMLTextAreaElement
)
}