UNPKG

seitu

Version:
89 lines (88 loc) • 2.68 kB
import { createReadableSubscription, createSubscription } from "../../core/subscription/index.mjs"; //#region src/web/element-size/index.ts const emptySize = { width: 0, height: 0 }; /** * Tracks an element's width and height through `ResizeObserver`. Sizes ignore * CSS transforms. A null element and the server snapshot are `0 × 0`. * * @example Vanilla * ```ts twoslash * import { createElementSize } from 'seitu/web' * * const size = createElementSize({ * element: document.querySelector('.container'), * }) * * size.subscribe(({ width, height }) => { * console.log(width, height) * }) * * const { width } = size.get() * console.log(width) * ``` * * @example React (callback ref) * ```tsx twoslash title="page.tsx" * 'use client' * * import * as React from 'react' * import { createElementSize } from 'seitu/web' * import { useSubscription } from 'seitu/react' * * function Panel() { * const [ref, setRef] = React.useState<HTMLDivElement | null>(null) * const size = useSubscription(() => createElementSize({ * element: ref, * }), { deps: [ref] }) * * return ( * <div ref={setRef}> * {size.width} × {size.height} * </div> * ) * } * ``` */ function createElementSize(options) { const { box = "border-box" } = options; const resolveElement = () => typeof options.element === "function" ? options.element() : options.element; const get = () => { const element = resolveElement(); if (!element) return emptySize; const style = getComputedStyle(element); const px = (value) => Number.parseFloat(value) || 0; const extraX = px(style.paddingLeft) + px(style.paddingRight) + px(style.borderLeftWidth) + px(style.borderRightWidth); const extraY = px(style.paddingTop) + px(style.paddingBottom) + px(style.borderTopWidth) + px(style.borderBottomWidth); const width = px(style.width); const height = px(style.height); const isBorderBox = style.boxSizing === "border-box"; if (!width && !height) return emptySize; if (box === "border-box") return isBorderBox ? { width, height } : { width: width + extraX, height: height + extraY }; return isBorderBox ? { width: Math.max(0, width - extraX), height: Math.max(0, height - extraY) } : { width, height }; }; const { subscribe, notify } = createSubscription({ onFirstSubscribe() { const element = resolveElement(); if (!element || typeof ResizeObserver === "undefined") return; const observer = new ResizeObserver(() => notify()); observer.observe(element, { box }); return () => observer.disconnect(); } }); return createReadableSubscription(get, subscribe, notify, () => emptySize); } //#endregion export { createElementSize };