import { ref, reactive, onMounted, onBeforeUnmount } from 'vue'
import { Ref } from 'vue'
import debounce from 'lodash/debounce'
import ResizeObserver from 'resize-observer-polyfill'
// Coalesce resize bursts (e.g. a window/pane drag fires dozens per second) so
// consumers only redraw once the dimensions settle, rather than every frame.
const RESIZE_DEBOUNCE_MS = 100
const NARROW_WIDTH_THRESHOLD = 540
/**
* Observes an element's size and exposes reactive, debounced dimensions.
*
* @param resizableRef - Optional ref to the observed element; when omitted, a fresh ref is created and returned as `resizeRef`.
* @returns An object with `resizeRef` (attach to the element) and reactive `resizeState` holding `dimensions`, `offsetWidth`, and `narrowWidth`.
* @example
*
*
*
*
{{ resizeState.offsetWidth }}px
*
*/
export function useResizeObserver(resizableRef?: Ref) {
const resizeRef: Ref = resizableRef ?? ref()
const resizeState = reactive({
dimensions: {} as DOMRect,
offsetWidth: NARROW_WIDTH_THRESHOLD,
narrowWidth: false
})
const onResize = debounce((entries: ResizeObserverEntry[]) => {
entries.forEach((entry) => {
resizeState.dimensions = entry.contentRect
resizeState.offsetWidth = (entry.target as HTMLElement).offsetWidth
resizeState.narrowWidth = (resizeState.offsetWidth ?? NARROW_WIDTH_THRESHOLD) < NARROW_WIDTH_THRESHOLD
})
}, RESIZE_DEBOUNCE_MS)
const observer = new ResizeObserver(onResize)
onMounted(async () => {
if (resizeRef.value) {
// set initial dimensions right before observing: Element.getBoundingClientRect()
resizeState.dimensions = resizeRef.value.getBoundingClientRect()
resizeState.offsetWidth = resizeRef.value.offsetWidth
resizeState.narrowWidth = false // TODO CD: old default is false but maybe this would work (resizeState.offsetWidth ?? 540) < 540 but would be often true
observer.observe(resizeRef.value)
}
})
onBeforeUnmount(() => {
// Drop any pending trailing call so it can't fire after teardown, then stop
// observing every element (disconnect covers unobserve).
onResize.cancel()
observer.disconnect()
})
return { resizeState, resizeRef }
}