|
import type { Action } from "svelte/action"; |
|
|
|
export enum ObservedElements { |
|
BottomActions = "bottom-actions", |
|
TokenCountEnd = "token-count-end", |
|
TokenCountStart = "token-count-start", |
|
|
|
} |
|
|
|
type ObservedData = { |
|
rect: { |
|
width: number; |
|
height: number; |
|
top: number; |
|
left: number; |
|
right: number; |
|
bottom: number; |
|
}; |
|
offset: { |
|
width: number; |
|
height: number; |
|
top: number; |
|
left: number; |
|
right: number; |
|
bottom: number; |
|
}; |
|
}; |
|
|
|
export const observed: Record<ObservedElements, ObservedData> = $state( |
|
Object.values(ObservedElements).reduce( |
|
(acc, key) => { |
|
acc[key] = { |
|
rect: { |
|
width: 0, |
|
height: 0, |
|
top: 0, |
|
left: 0, |
|
right: 0, |
|
bottom: 0, |
|
}, |
|
offset: { |
|
width: 0, |
|
height: 0, |
|
top: 0, |
|
left: 0, |
|
right: 0, |
|
bottom: 0, |
|
}, |
|
}; |
|
return acc; |
|
}, |
|
{} as Record<ObservedElements, ObservedData> |
|
) |
|
); |
|
|
|
type ObserveArgs = { |
|
name: ObservedElements; |
|
}; |
|
|
|
function getOffsetPosition(el: HTMLElement) { |
|
let top = 0; |
|
let left = 0; |
|
const width = el.offsetWidth; |
|
const height = el.offsetHeight; |
|
|
|
while (el) { |
|
top += el.offsetTop; |
|
left += el.offsetLeft; |
|
el = el.offsetParent as HTMLElement; |
|
} |
|
|
|
return { top, left, width, height, right: left + width, bottom: top + height }; |
|
} |
|
|
|
export const observe: Action<HTMLElement, ObserveArgs> = (node, args) => { |
|
let resizeObserver: ResizeObserver; |
|
|
|
function setVars(name: ObservedElements) { |
|
|
|
const rect = node.getBoundingClientRect(); |
|
document.documentElement.style.setProperty(`--${name}-width`, `${rect.width}px`); |
|
document.documentElement.style.setProperty(`--${name}-height`, `${rect.height}px`); |
|
document.documentElement.style.setProperty(`--${name}-top`, `${rect.top}px`); |
|
document.documentElement.style.setProperty(`--${name}-left`, `${rect.left}px`); |
|
document.documentElement.style.setProperty(`--${name}-right`, `${rect.right}px`); |
|
document.documentElement.style.setProperty(`--${name}-bottom`, `${rect.bottom}px`); |
|
|
|
|
|
const offset = getOffsetPosition(node); |
|
document.documentElement.style.setProperty(`--${name}-width-offset`, `${offset.width}px`); |
|
document.documentElement.style.setProperty(`--${name}-height-offset`, `${offset.height}px`); |
|
document.documentElement.style.setProperty(`--${name}-top-offset`, `${offset.top}px`); |
|
document.documentElement.style.setProperty(`--${name}-left-offset`, `${offset.left}px`); |
|
document.documentElement.style.setProperty(`--${name}-right-offset`, `${offset.right}px`); |
|
document.documentElement.style.setProperty(`--${name}-bottom-offset`, `${offset.bottom}px`); |
|
|
|
observed[name] = { |
|
rect, |
|
offset, |
|
}; |
|
} |
|
|
|
function update(args: ObserveArgs) { |
|
if (resizeObserver) { |
|
resizeObserver.disconnect(); |
|
} |
|
|
|
resizeObserver = new ResizeObserver(() => { |
|
setVars(args.name); |
|
}); |
|
resizeObserver.observe(node); |
|
|
|
|
|
window.addEventListener("scroll", onWindowChange, true); |
|
window.addEventListener("resize", onWindowChange, true); |
|
|
|
setVars(args.name); |
|
} |
|
|
|
function onWindowChange() { |
|
setVars(args.name); |
|
} |
|
|
|
update(args); |
|
|
|
function destroy() { |
|
resizeObserver.disconnect(); |
|
window.removeEventListener("scroll", onWindowChange, true); |
|
window.removeEventListener("resize", onWindowChange, true); |
|
} |
|
|
|
return { |
|
update, |
|
destroy, |
|
}; |
|
}; |
|
|