Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116import type { Attachment } from "svelte/attachments";
export function popoverSingleton() { let openHide: (() => void) | undefined;
return { claim(hide: () => void) { if (openHide && openHide !== hide) openHide(); openHide = hide; }, release(hide: () => void) { if (openHide === hide) openHide = undefined; } };}
export function popoverAnchorName(prefix: string, id: string) { return `--${prefix}-${id.replace(/[^a-zA-Z0-9_-]/g, "-")}`;}
export type PopoverOrigin = "tl" | "tr" | "bl" | "br";
const corners: Record<PopoverOrigin, string> = { tl: "top-left", tr: "top-right", bl: "bottom-left", br: "bottom-right"};
const along = (center: number, start: number, extent: number, near: string, far: string) => { if (extent === 0) return "center"; const fraction = (center - start) / extent; if (fraction < 1 / 3) return near; if (fraction > 2 / 3) return far; return "center";};
export const popoverOrigin = ( anchor: () => HTMLElement | undefined, origin?: () => PopoverOrigin | undefined ): Attachment<HTMLElement> => (node) => { const measure = () => { const fixed = origin?.(); if (fixed) { node.setAttribute("data-popover-origin", corners[fixed]); return; }
const trigger = anchor()?.getBoundingClientRect(); if (!trigger) return; const panel = node.getBoundingClientRect(); if (panel.width === 0 || panel.height === 0) return;
const block = along( trigger.top + trigger.height / 2, panel.top, panel.height, "top", "bottom" ); const inline = along( trigger.left + trigger.width / 2, panel.left, panel.width, "left", "right" ); node.setAttribute("data-popover-origin", `${block}-${inline}`); };
const onToggle = (event: ToggleEvent) => { if (event.newState !== "open") return; const moving = node .getAnimations() .filter((animation) => !(animation instanceof CSSTransition)); if (moving.length === 0) return measure(); Promise.allSettled(moving.map((animation) => animation.finished)).then(measure); };
node.addEventListener("toggle", onToggle); return () => node.removeEventListener("toggle", onToggle); };
export const popoverResize: Attachment<HTMLElement> = (body) => { const card = body.parentElement; if (!card) return; let animation: Animation | undefined; let height = 0;
const observer = new ResizeObserver(() => { const from = animation?.playState === "running" ? parseFloat(getComputedStyle(card).height) : height; animation?.cancel(); const to = (height = card.offsetHeight); if ( !card.matches(":popover-open") || !from || from === to || matchMedia("(prefers-reduced-motion: reduce)").matches ) return; animation = card.animate( { height: [`${from}px`, `${to}px`], overflow: ["clip", "clip"] }, { duration: 120, easing: "ease-out" } ); });
observer.observe(body); return () => { observer.disconnect(); animation?.cancel(); };};