From 7d8a87615a5c4c68ecd3e26b261ffad4bf05accb Mon Sep 17 00:00:00 2001 From: James Grugett Date: Thu, 24 Mar 2022 12:03:08 -0500 Subject: [PATCH] Embed market: dynamically adjust graph height --- common/util/array.ts | 2 +- web/components/answers/answers-graph.tsx | 5 +- web/components/contract-prob-graph.tsx | 5 +- web/components/layout/col.tsx | 7 ++- web/hooks/use-measure-size.ts | 63 +++++++++++++++++++ web/pages/embed/[username]/[contractSlug].tsx | 26 ++++++-- 6 files changed, 95 insertions(+), 13 deletions(-) create mode 100644 web/hooks/use-measure-size.ts diff --git a/common/util/array.ts b/common/util/array.ts index fba342aa..d81edba1 100644 --- a/common/util/array.ts +++ b/common/util/array.ts @@ -1,3 +1,3 @@ export function filterDefined(array: (T | null | undefined)[]) { - return array.filter((item) => item) as T[] + return array.filter((item) => item !== null && item !== undefined) as T[] } diff --git a/web/components/answers/answers-graph.tsx b/web/components/answers/answers-graph.tsx index a602a826..5c5afd13 100644 --- a/web/components/answers/answers-graph.tsx +++ b/web/components/answers/answers-graph.tsx @@ -14,8 +14,9 @@ const NUM_LINES = 6 export function AnswersGraph(props: { contract: FullContract bets: Bet[] + height?: number }) { - const { contract } = props + const { contract, height } = props const { createdTime, resolutionTime, closeTime, answers } = contract const bets = useBets(contract.id) ?? props.bets @@ -86,7 +87,7 @@ export function AnswersGraph(props: { return (
= 800 ? 350 : 225 }} + style={{ height: height ?? (!width || width >= 800 ? 350 : 225) }} > bets: Bet[] + height?: number }) { - const { contract } = props + const { contract, height } = props const { resolutionTime, closeTime } = contract const bets = useBetsWithoutAntes(contract, props.bets).filter( @@ -63,7 +64,7 @@ export function ContractProbGraph(props: { return (
= 800 ? 400 : 250 }} + style={{ height: height ?? (!width || width >= 800 ? 400 : 250) }} > }) { - const { children, className, style } = props + const { children, className, style, ref } = props return ( -
+
{children}
) diff --git a/web/hooks/use-measure-size.ts b/web/hooks/use-measure-size.ts new file mode 100644 index 00000000..5e50400b --- /dev/null +++ b/web/hooks/use-measure-size.ts @@ -0,0 +1,63 @@ +import _ from 'lodash' +import { RefObject, useMemo, useLayoutEffect, useRef, useState } from 'react' + +type elem_size = + | { width: number; height: number } + | { width: undefined; height: undefined } + +const getSize = (elem: HTMLElement | null) => + elem + ? { width: elem.offsetWidth, height: elem.offsetHeight } + : { width: undefined, height: undefined } + +export function useListenElemSize( + elemRef: RefObject, + callback: (size: elem_size) => void, + debounceMs: number | undefined = undefined +) { + const handleResize = useMemo(() => { + let updateSize = () => { + if (elemRef.current) callback(getSize(elemRef.current)) + } + + return debounceMs + ? _.debounce(updateSize, debounceMs, { leading: false, trailing: true }) + : updateSize + }, [callback, elemRef, debounceMs]) + + let elem = elemRef.current + + useLayoutEffect(() => { + if (!elemRef.current) return + + const resizeObserver = new ResizeObserver(handleResize) + resizeObserver.observe(elemRef.current) + + return () => resizeObserver.disconnect() + }, [elemRef, elem, handleResize]) +} + +export function useMeasureSize(debounceMs: number | undefined = undefined) { + const elemRef = useRef(null) + const [size, setSize] = useState(() => getSize(null)) + const sizeRef = useRef(size) + + const setSizeIfDifferent = (newSize: typeof size) => { + if (newSize?.height !== size?.height || newSize?.width !== size?.width) { + sizeRef.current = newSize + setSize(newSize) + } + } + + useListenElemSize(elemRef, setSizeIfDifferent, debounceMs) + + const setElem = (elem: HTMLElement | null) => { + elemRef.current = elem + + if (elem) { + setSizeIfDifferent(getSize(elem)) + } + } + + return { setElem, elemRef, sizeRef, ...size } +} diff --git a/web/pages/embed/[username]/[contractSlug].tsx b/web/pages/embed/[username]/[contractSlug].tsx index b5816710..c8d4b046 100644 --- a/web/pages/embed/[username]/[contractSlug].tsx +++ b/web/pages/embed/[username]/[contractSlug].tsx @@ -18,7 +18,9 @@ import { Spacer } from '../../../components/layout/spacer' import { Linkify } from '../../../components/linkify' import { SiteLink } from '../../../components/site-link' import { useContractWithPreload } from '../../../hooks/use-contract' +import { useMeasureSize } from '../../../hooks/use-measure-size' import { fromPropz, usePropz } from '../../../hooks/use-propz' +import { useWindowSize } from '../../../hooks/use-window-size' import { listAllBets } from '../../../lib/firebase/bets' import { contractPath, @@ -85,9 +87,18 @@ function ContractEmbed(props: { contract: Contract; bets: Bet[] }) { const href = `https://${DOMAIN}${contractPath(contract)}` + const { height: windowHeight } = useWindowSize() + const { setElem, height: topSectionHeight } = useMeasureSize() + const paddingBottom = 8 + + const graphHeight = + windowHeight && topSectionHeight + ? windowHeight - topSectionHeight - paddingBottom + : 0 + return ( - - + +
- +
-
+
{isBinary ? ( - + ) : ( } bets={bets} + height={graphHeight} /> )}