sanity
Version:
Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches
58 lines (45 loc) • 1.45 kB
text/typescript
import {useCallback, useEffect, useMemo, useState} from 'react'
const EVENT_LISTENER_OPTIONS: AddEventListenerOptions = {passive: true}
interface CursorElementHookOptions {
disabled: boolean
rootElement: HTMLElement | null
}
export function useCursorElement(opts: CursorElementHookOptions): HTMLElement | null {
const {disabled, rootElement} = opts
const [cursorRect, setCursorRect] = useState<DOMRect | null>(null)
const cursorElement = useMemo(() => {
if (!cursorRect) {
return null
}
return {
getBoundingClientRect: () => {
return cursorRect
},
} as HTMLElement
}, [cursorRect])
const handleSelectionChange = useCallback(() => {
if (disabled) {
setCursorRect(null)
return
}
const sel = window.getSelection()
if (!sel || !sel.isCollapsed || sel.rangeCount === 0) return
const range = sel.getRangeAt(0)
const isWithinRoot = rootElement?.contains(range.commonAncestorContainer)
if (!isWithinRoot) {
setCursorRect(null)
return
}
const rect = range?.getBoundingClientRect()
if (rect) {
setCursorRect(rect)
}
}, [disabled, rootElement])
useEffect(() => {
document.addEventListener('selectionchange', handleSelectionChange, EVENT_LISTENER_OPTIONS)
return () => {
document.removeEventListener('selectionchange', handleSelectionChange)
}
}, [handleSelectionChange])
return cursorElement
}