()
| 147 | * current element (e.g. for live measurements in event handlers). |
| 148 | */ |
| 149 | export function useIsOverflowing<T extends HTMLElement = HTMLElement>(): { |
| 150 | ref: (node: T | null) => void |
| 151 | node: React.RefObject<T | null> |
| 152 | isOverflowing: boolean |
| 153 | } { |
| 154 | const [isOverflowing, setIsOverflowing] = React.useState(false) |
| 155 | const nodeRef = React.useRef<T | null>(null) |
| 156 | const observerRef = React.useRef<ResizeObserver | null>(null) |
| 157 | |
| 158 | const measure = React.useCallback(() => { |
| 159 | const element = nodeRef.current |
| 160 | if (element) setIsOverflowing(isTextClipped(element)) |
| 161 | }, []) |
| 162 | |
| 163 | const ref = React.useCallback( |
| 164 | (node: T | null) => { |
| 165 | observerRef.current?.disconnect() |
| 166 | observerRef.current = null |
| 167 | nodeRef.current = node |
| 168 | if (!node) return |
| 169 | |
| 170 | measure() |
| 171 | const observer = new ResizeObserver(measure) |
| 172 | observer.observe(node) |
| 173 | observerRef.current = observer |
| 174 | }, |
| 175 | [measure] |
| 176 | ) |
| 177 | |
| 178 | React.useEffect(() => { |
| 179 | window.addEventListener('resize', measure) |
| 180 | return () => { |
| 181 | window.removeEventListener('resize', measure) |
| 182 | observerRef.current?.disconnect() |
| 183 | } |
| 184 | }, [measure]) |
| 185 | |
| 186 | return { ref, node: nodeRef, isOverflowing } |
| 187 | } |
| 188 | |
| 189 | /** Whether an element's content is wider than its visible box. */ |
| 190 | export function isTextClipped(element: HTMLElement): boolean { |
no test coverage detected