({
line,
column,
active,
}: {
line: number
column: number
active: boolean
})
| 23 | * explicitly after render. |
| 24 | */ |
| 25 | export function useDeclaredCursor({ |
| 26 | line, |
| 27 | column, |
| 28 | active, |
| 29 | }: { |
| 30 | line: number |
| 31 | column: number |
| 32 | active: boolean |
| 33 | }): (element: DOMElement | null) => void { |
| 34 | const setCursorDeclaration = useContext(CursorDeclarationContext) |
| 35 | const nodeRef = useRef<DOMElement | null>(null) |
| 36 | |
| 37 | const setNode = useCallback((node: DOMElement | null) => { |
| 38 | nodeRef.current = node |
| 39 | }, []) |
| 40 | |
| 41 | // When active, set unconditionally. When inactive, clear conditionally |
| 42 | // (only if the currently-declared node is ours). The node-identity check |
| 43 | // handles two hazards: |
| 44 | // 1. A memo()ized active instance elsewhere (e.g. the search input in |
| 45 | // a memo'd Footer) doesn't re-render this commit — an inactive |
| 46 | // instance re-rendering here must not clobber it. |
| 47 | // 2. Sibling handoff (menu focus moving between list items) — when |
| 48 | // focus moves opposite to sibling order, the newly-inactive item's |
| 49 | // effect runs AFTER the newly-active item's set. Without the node |
| 50 | // check it would clobber. |
| 51 | // No dep array: must re-declare every commit so the active instance |
| 52 | // re-claims the declaration after another instance's unmount-cleanup or |
| 53 | // sibling handoff nulls it. |
| 54 | useLayoutEffect(() => { |
| 55 | const node = nodeRef.current |
| 56 | if (active && node) { |
| 57 | setCursorDeclaration({ relativeX: column, relativeY: line, node }) |
| 58 | } else { |
| 59 | setCursorDeclaration(null, node) |
| 60 | } |
| 61 | }) |
| 62 | |
| 63 | // Clear on unmount (conditionally — another instance may own by then). |
| 64 | // Separate effect with empty deps so cleanup only fires once — not on |
| 65 | // every line/column change, which would transiently null between commits. |
| 66 | useLayoutEffect(() => { |
| 67 | return () => { |
| 68 | setCursorDeclaration(null, nodeRef.current) |
| 69 | } |
| 70 | }, [setCursorDeclaration]) |
| 71 | |
| 72 | return setNode |
| 73 | } |
| 74 |
no outgoing calls
no test coverage detected