Skip to content

Commit

Permalink
feat(site): hook to observe bounds of an element
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelgerber committed Jul 24, 2024
1 parent 3285abf commit 7df03ae
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 6 deletions.
72 changes: 70 additions & 2 deletions site/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import { useEffect, RefObject, useState, useRef } from "react"
import {
useEffect,
RefObject,
useState,
useRef,
useCallback,
useMemo,
} from "react"
import { MultiEmbedderSingleton } from "./multiembedder/MultiEmbedder.js"
import { debounce, throttle } from "@ourworldindata/utils"
import {
Bounds,
debounce,
DEFAULT_BOUNDS,
throttle,
} from "@ourworldindata/utils"

export const useTriggerWhenClickOutside = (
container: RefObject<HTMLElement>,
Expand Down Expand Up @@ -86,3 +98,59 @@ export const useTriggerOnEscape = (trigger: VoidFunction) => {
}
}, [trigger])
}

// Auto-updating Grapher bounds based on ResizeObserver
// Optionally debounces the bounds updates
export const useObservedBounds = (
ref: RefObject<HTMLElement>,
initialValue: Bounds = DEFAULT_BOUNDS,
debounceTime: number | undefined = 100
) => {
const [bounds, setBounds] = useState<Bounds>(initialValue)

const computeAndUpdateBoundsImmediately = useCallback(
(target: HTMLElement) =>
setBounds(Bounds.fromRect(target.getBoundingClientRect())),
[]
)

const computeAndUpdateBoundsDebounced = useMemo(
() =>
debounceTime !== undefined
? debounce(
(target: HTMLElement) =>
computeAndUpdateBoundsImmediately(target),
debounceTime,

// We use `leading` because, in many cases, there is only a single resize event (e.g. phone screen
// orientation change), and we want to optimize for a fast response time in that case
{ leading: true }
)
: computeAndUpdateBoundsImmediately,
[debounceTime, computeAndUpdateBoundsImmediately]
)

// Ensure bounds are computed on mount
useEffect(() => {
if (ref.current) computeAndUpdateBoundsImmediately(ref.current)
}, [ref, computeAndUpdateBoundsImmediately])

// Set up the actual ResizeObserver
useEffect(() => {
if (!ref.current) return

const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
computeAndUpdateBoundsDebounced(entry.target as HTMLElement)
}
})
if (ref.current) {
observer.observe(ref.current)
}
return () => {
observer.disconnect()
}
}, [ref, computeAndUpdateBoundsDebounced])

return bounds
}
12 changes: 8 additions & 4 deletions site/multiDim/MultiDimDataPageContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
stateToQueryStr,
} from "./MultiDimUrl.js"
import { reaction } from "mobx"
import { useObservedBounds } from "../hooks.js"
declare global {
interface Window {
_OWID_MULTI_DIM_CONFIG: MultiDimDataPageConfigType
Expand Down Expand Up @@ -349,6 +350,8 @@ export const MultiDimDataPageContent = ({
imageMetadata: Record<string, ImageMetadata>
initialQueryStr?: string
}) => {
const grapherFigureRef = useRef<HTMLDivElement>(null)

const [initialChoices] = useState(() =>
initialQueryStr
? extractDimensionChoicesFromQueryStr(initialQueryStr, config)
Expand Down Expand Up @@ -437,6 +440,8 @@ export const MultiDimDataPageContent = ({
[grapherInst]
)

const bounds = useObservedBounds(grapherFigureRef)

// TEMPORARY, only while we are using `url` as part of the query string
const urlQueryParam = getWindowQueryParams().url

Expand Down Expand Up @@ -466,13 +471,12 @@ export const MultiDimDataPageContent = ({
dimensions: dimensionsConfig,
isEmbeddedInADataPage: true,

// TODO temp
bounds: new Bounds(0, 0, 1000, 600),
bounds,

// Keep the tab we last had
tab: grapherInst?.tab ?? GrapherTabOption.chart,
} as GrapherProgrammaticInterface
}, [currentView, dimensionsConfig, grapherInst])
}, [currentView, dimensionsConfig, grapherInst, bounds])

// const grapher = useMemo(() => {
// const grapher = new Grapher({ ...grapherConfigComputed, queryStr })
Expand Down Expand Up @@ -551,7 +555,7 @@ export const MultiDimDataPageContent = ({
id="explore-the-data"
className="GrapherWithFallback full-width-on-mobile"
>
<figure data-grapher-src>
<figure data-grapher-src ref={grapherFigureRef}>
<Grapher
{...grapherConfigComputed}
queryStr={queryStr}
Expand Down

0 comments on commit 7df03ae

Please sign in to comment.