| import type { DOMElement } from "src/ink"; |
| import { |
| type RefObject, |
| useCallback, |
| useEffect, |
| useRef, |
| useState, |
| } from "react"; |
| import { useInkPictureConfig } from "../InkPictureProvider.js"; |
|
|
| export type Position = { |
| |
| |
| |
| col: number; |
| |
| |
| |
| row: number; |
| |
| |
| |
| width: number; |
| |
| |
| |
| height: number; |
| |
| |
| |
| appWidth: number; |
| |
| |
| |
| appHeight: number; |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| const usePosition = ( |
| ref: RefObject<DOMElement | null>, |
| ): Position | undefined => { |
| const [position, setPosition] = useState<Position | undefined>(undefined); |
|
|
| const updatePosition = useCallback(() => { |
| const node = ref.current; |
| if (!node?.yogaNode) { |
| return; |
| } |
|
|
| const { yogaNode } = node; |
|
|
| |
| let absoluteCol = 0; |
| let absoluteRow = 0; |
| let appWidth = 0; |
| let appHeight = 0; |
| let currentNode: DOMElement | undefined = node; |
|
|
| while (currentNode) { |
| if (currentNode.yogaNode) { |
| absoluteCol += currentNode.yogaNode.getComputedLeft(); |
| absoluteRow += currentNode.yogaNode.getComputedTop(); |
| appWidth = currentNode.yogaNode.getComputedWidth(); |
| appHeight = currentNode.yogaNode.getComputedHeight(); |
| } |
|
|
| currentNode = currentNode.parentNode; |
| } |
|
|
| const newPosition: Position = { |
| col: absoluteCol, |
| row: absoluteRow, |
| width: yogaNode.getComputedWidth(), |
| height: yogaNode.getComputedHeight(), |
| appWidth, |
| appHeight, |
| }; |
|
|
| setPosition((previousPosition) => { |
| |
| if ( |
| !previousPosition || |
| previousPosition.col !== newPosition.col || |
| previousPosition.row !== newPosition.row || |
| previousPosition.width !== newPosition.width || |
| previousPosition.height !== newPosition.height || |
| previousPosition.appWidth !== newPosition.appWidth || |
| previousPosition.appHeight !== newPosition.appHeight |
| ) { |
| return newPosition; |
| } |
|
|
| return previousPosition; |
| }); |
| }, [ref]); |
|
|
| |
| |
| useEffect(() => { |
| updatePosition(); |
| }); |
|
|
| |
| |
| const config = useInkPictureConfig(); |
| const configRef = useRef(config); |
| configRef.current = config; |
|
|
| useEffect(() => { |
| let timeout: ReturnType<typeof setTimeout>; |
|
|
| function schedule() { |
| timeout = setTimeout(tick, configRef.current.pollIntervalMs); |
| timeout.unref(); |
| } |
|
|
| function tick() { |
| updatePosition(); |
| schedule(); |
| } |
|
|
| schedule(); |
|
|
| return () => { |
| clearTimeout(timeout); |
| }; |
| }, [updatePosition]); |
|
|
| return position; |
| }; |
|
|
| export default usePosition; |
|
|