From 89e26d077e58565afbb4a5d98ecbcd1af53dbed7 Mon Sep 17 00:00:00 2001 From: Marshall Polaris Date: Fri, 30 Sep 2022 16:57:48 -0700 Subject: [PATCH] Clean up chart sizing code (#977) * Clean up chart sizing code * Do all the chart sizing work in same batch --- web/components/charts/contract/binary.tsx | 40 ++++------ web/components/charts/contract/choice.tsx | 39 ++++----- web/components/charts/contract/index.tsx | 3 +- web/components/charts/contract/numeric.tsx | 37 ++++----- .../charts/contract/pseudo-numeric.tsx | 44 ++++------ web/components/contract/contract-overview.tsx | 80 +++++++++++++++---- web/hooks/use-element-width.tsx | 17 ---- web/pages/embed/[username]/[contractSlug].tsx | 11 ++- 8 files changed, 136 insertions(+), 135 deletions(-) delete mode 100644 web/hooks/use-element-width.tsx diff --git a/web/components/charts/contract/binary.tsx b/web/components/charts/contract/binary.tsx index 564ef68c..7e192767 100644 --- a/web/components/charts/contract/binary.tsx +++ b/web/components/charts/contract/binary.tsx @@ -1,4 +1,4 @@ -import { useMemo, useRef } from 'react' +import { useMemo } from 'react' import { last, sortBy } from 'lodash' import { scaleTime, scaleLinear } from 'd3-scale' @@ -6,7 +6,6 @@ import { Bet } from 'common/bet' import { getProbability, getInitialProbability } from 'common/calculate' import { BinaryContract } from 'common/contract' import { DAY_MS } from 'common/util/time' -import { useIsMobile } from 'web/hooks/use-is-mobile' import { TooltipProps, MARGIN_X, @@ -17,7 +16,6 @@ import { formatPct, } from '../helpers' import { HistoryPoint, SingleValueHistoryChart } from '../generic-charts' -import { useElementWidth } from 'web/hooks/use-element-width' import { Row } from 'web/components/layout/row' import { Avatar } from 'web/components/avatar' @@ -45,10 +43,11 @@ const BinaryChartTooltip = (props: TooltipProps>) => { export const BinaryContractChart = (props: { contract: BinaryContract bets: Bet[] - height?: number + width: number + height: number onMouseOver?: (p: HistoryPoint | undefined) => void }) => { - const { contract, bets, onMouseOver } = props + const { contract, bets, width, height, onMouseOver } = props const [start, end] = getDateRange(contract) const startP = getInitialProbability(contract) const endP = getProbability(contract) @@ -67,28 +66,19 @@ export const BinaryContractChart = (props: { Date.now() ) const visibleRange = [start, rightmostDate] - const isMobile = useIsMobile(800) - const containerRef = useRef(null) - const width = useElementWidth(containerRef) ?? 0 - const height = props.height ?? (isMobile ? 150 : 250) const xScale = scaleTime(visibleRange, [0, width - MARGIN_X]) const yScale = scaleLinear([0, 1], [height - MARGIN_Y, 0]) - return ( -
- {width > 0 && ( - - )} -
+ ) } diff --git a/web/components/charts/contract/choice.tsx b/web/components/charts/contract/choice.tsx index 665a01cd..65279b70 100644 --- a/web/components/charts/contract/choice.tsx +++ b/web/components/charts/contract/choice.tsx @@ -1,4 +1,4 @@ -import { useMemo, useRef } from 'react' +import { useMemo } from 'react' import { last, sum, sortBy, groupBy } from 'lodash' import { scaleTime, scaleLinear } from 'd3-scale' @@ -6,7 +6,6 @@ import { Bet } from 'common/bet' import { Answer } from 'common/answer' import { FreeResponseContract, MultipleChoiceContract } from 'common/contract' import { getOutcomeProbability } from 'common/calculate' -import { useIsMobile } from 'web/hooks/use-is-mobile' import { DAY_MS } from 'common/util/time' import { TooltipProps, @@ -18,7 +17,6 @@ import { formatDateInRange, } from '../helpers' import { MultiPoint, MultiValueHistoryChart } from '../generic-charts' -import { useElementWidth } from 'web/hooks/use-element-width' import { Row } from 'web/components/layout/row' import { Avatar } from 'web/components/avatar' @@ -146,10 +144,11 @@ const Legend = (props: { className?: string; items: LegendItem[] }) => { export const ChoiceContractChart = (props: { contract: FreeResponseContract | MultipleChoiceContract bets: Bet[] - height?: number + width: number + height: number onMouseOver?: (p: MultiPoint | undefined) => void }) => { - const { contract, bets, onMouseOver } = props + const { contract, bets, width, height, onMouseOver } = props const [start, end] = getDateRange(contract) const answers = useMemo( () => getTrackedAnswers(contract, CATEGORY_COLORS.length), @@ -173,10 +172,6 @@ export const ChoiceContractChart = (props: { Date.now() ) const visibleRange = [start, rightmostDate] - const isMobile = useIsMobile(800) - const containerRef = useRef(null) - const width = useElementWidth(containerRef) ?? 0 - const height = props.height ?? (isMobile ? 250 : 350) const xScale = scaleTime(visibleRange, [0, width - MARGIN_X]) const yScale = scaleLinear([0, 1], [height - MARGIN_Y, 0]) @@ -212,20 +207,16 @@ export const ChoiceContractChart = (props: { ) return ( -
- {width > 0 && ( - - )} -
+ ) } diff --git a/web/components/charts/contract/index.tsx b/web/components/charts/contract/index.tsx index 1f580bae..1efe1eb4 100644 --- a/web/components/charts/contract/index.tsx +++ b/web/components/charts/contract/index.tsx @@ -8,7 +8,8 @@ import { NumericContractChart } from './numeric' export const ContractChart = (props: { contract: Contract bets: Bet[] - height?: number + width: number + height: number }) => { const { contract } = props switch (contract.outcomeType) { diff --git a/web/components/charts/contract/numeric.tsx b/web/components/charts/contract/numeric.tsx index f8051308..2d62cb11 100644 --- a/web/components/charts/contract/numeric.tsx +++ b/web/components/charts/contract/numeric.tsx @@ -1,4 +1,4 @@ -import { useMemo, useRef } from 'react' +import { useMemo } from 'react' import { range } from 'lodash' import { scaleLinear } from 'd3-scale' @@ -6,10 +6,8 @@ import { formatLargeNumber } from 'common/util/format' import { getDpmOutcomeProbabilities } from 'common/calculate-dpm' import { NumericContract } from 'common/contract' import { NUMERIC_GRAPH_COLOR } from 'common/numeric-constants' -import { useIsMobile } from 'web/hooks/use-is-mobile' import { TooltipProps, MARGIN_X, MARGIN_Y, formatPct } from '../helpers' import { DistributionPoint, DistributionChart } from '../generic-charts' -import { useElementWidth } from 'web/hooks/use-element-width' const getNumericChartData = (contract: NumericContract) => { const { totalShares, bucketCount, min, max } = contract @@ -36,33 +34,26 @@ const NumericChartTooltip = ( export const NumericContractChart = (props: { contract: NumericContract - height?: number + width: number + height: number onMouseOver?: (p: DistributionPoint | undefined) => void }) => { - const { contract, onMouseOver } = props + const { contract, width, height, onMouseOver } = props const { min, max } = contract const data = useMemo(() => getNumericChartData(contract), [contract]) - const isMobile = useIsMobile(800) - const containerRef = useRef(null) - const width = useElementWidth(containerRef) ?? 0 - const height = props.height ?? (isMobile ? 150 : 250) const maxY = Math.max(...data.map((d) => d.y)) const xScale = scaleLinear([min, max], [0, width - MARGIN_X]) const yScale = scaleLinear([0, maxY], [height - MARGIN_Y, 0]) return ( -
- {width > 0 && ( - - )} -
+ ) } diff --git a/web/components/charts/contract/pseudo-numeric.tsx b/web/components/charts/contract/pseudo-numeric.tsx index 04b1fafb..e03d4ad9 100644 --- a/web/components/charts/contract/pseudo-numeric.tsx +++ b/web/components/charts/contract/pseudo-numeric.tsx @@ -1,4 +1,4 @@ -import { useMemo, useRef } from 'react' +import { useMemo } from 'react' import { last, sortBy } from 'lodash' import { scaleTime, scaleLog, scaleLinear } from 'd3-scale' @@ -8,7 +8,6 @@ import { getInitialProbability, getProbability } from 'common/calculate' import { formatLargeNumber } from 'common/util/format' import { PseudoNumericContract } from 'common/contract' import { NUMERIC_GRAPH_COLOR } from 'common/numeric-constants' -import { useIsMobile } from 'web/hooks/use-is-mobile' import { TooltipProps, MARGIN_X, @@ -18,7 +17,6 @@ import { formatDateInRange, } from '../helpers' import { HistoryPoint, SingleValueHistoryChart } from '../generic-charts' -import { useElementWidth } from 'web/hooks/use-element-width' import { Row } from 'web/components/layout/row' import { Avatar } from 'web/components/avatar' @@ -59,10 +57,11 @@ const PseudoNumericChartTooltip = ( export const PseudoNumericContractChart = (props: { contract: PseudoNumericContract bets: Bet[] - height?: number + width: number + height: number onMouseOver?: (p: HistoryPoint | undefined) => void }) => { - const { contract, bets, onMouseOver } = props + const { contract, bets, width, height, onMouseOver } = props const { min, max, isLogScale } = contract const [start, end] = getDateRange(contract) const scaleP = useMemo( @@ -86,30 +85,21 @@ export const PseudoNumericContractChart = (props: { Date.now() ) const visibleRange = [start, rightmostDate] - const isMobile = useIsMobile(800) - const containerRef = useRef(null) - const width = useElementWidth(containerRef) ?? 0 - const height = props.height ?? (isMobile ? 150 : 250) - const xScale = scaleTime(visibleRange, [0, width - MARGIN_X]) + const xScale = scaleTime(visibleRange, [0, width ?? 0 - MARGIN_X]) // clamp log scale to make sure zeroes go to the bottom const yScale = isLogScale - ? scaleLog([Math.max(min, 1), max], [height - MARGIN_Y, 0]).clamp(true) - : scaleLinear([min, max], [height - MARGIN_Y, 0]) - + ? scaleLog([Math.max(min, 1), max], [height ?? 0 - MARGIN_Y, 0]).clamp(true) + : scaleLinear([min, max], [height ?? 0 - MARGIN_Y, 0]) return ( -
- {width > 0 && ( - - )} -
+ ) } diff --git a/web/components/contract/contract-overview.tsx b/web/components/contract/contract-overview.tsx index add9ba48..3e5f22b2 100644 --- a/web/components/contract/contract-overview.tsx +++ b/web/components/contract/contract-overview.tsx @@ -1,13 +1,8 @@ -import React from 'react' +import React, { useEffect, useRef, useState } from 'react' import { tradingAllowed } from 'web/lib/firebase/contracts' import { Col } from '../layout/col' -import { - BinaryContractChart, - NumericContractChart, - PseudoNumericContractChart, - ChoiceContractChart, -} from 'web/components/charts/contract' +import { ContractChart } from 'web/components/charts/contract' import { useUser } from 'web/hooks/use-user' import { Row } from '../layout/row' import { Linkify } from '../linkify' @@ -48,8 +43,43 @@ const BetWidget = (props: { contract: CPMMContract }) => { ) } -const NumericOverview = (props: { contract: NumericContract }) => { - const { contract } = props +const SizedContractChart = (props: { + contract: Contract + bets: Bet[] + fullHeight: number + mobileHeight: number +}) => { + const { contract, bets, fullHeight, mobileHeight } = props + const containerRef = useRef(null) + const [chartWidth, setChartWidth] = useState() + const [chartHeight, setChartHeight] = useState() + useEffect(() => { + const handleResize = () => { + setChartHeight(window.innerWidth < 800 ? mobileHeight : fullHeight) + setChartWidth(containerRef.current?.clientWidth) + } + handleResize() + window.addEventListener('resize', handleResize) + return () => { + window.removeEventListener('resize', handleResize) + } + }, [fullHeight, mobileHeight]) + return ( +
+ {chartWidth != null && chartHeight != null && ( + + )} +
+ ) +} + +const NumericOverview = (props: { contract: NumericContract; bets: Bet[] }) => { + const { contract, bets } = props return ( @@ -66,7 +96,12 @@ const NumericOverview = (props: { contract: NumericContract }) => { contract={contract} /> - + ) } @@ -86,7 +121,12 @@ const BinaryOverview = (props: { contract: BinaryContract; bets: Bet[] }) => { /> - + {tradingAllowed(contract) && ( @@ -111,9 +151,12 @@ const ChoiceOverview = (props: { )} - - - + ) } @@ -139,7 +182,12 @@ const PseudoNumericOverview = (props: { {tradingAllowed(contract) && } - + ) } @@ -153,7 +201,7 @@ export const ContractOverview = (props: { case 'BINARY': return case 'NUMERIC': - return + return case 'PSEUDO_NUMERIC': return case 'FREE_RESPONSE': diff --git a/web/hooks/use-element-width.tsx b/web/hooks/use-element-width.tsx deleted file mode 100644 index 1c373839..00000000 --- a/web/hooks/use-element-width.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { RefObject, useState, useEffect } from 'react' - -// todo: consider consolidation with use-measure-size -export const useElementWidth = (ref: RefObject) => { - const [width, setWidth] = useState() - useEffect(() => { - const handleResize = () => { - setWidth(ref.current?.clientWidth) - } - handleResize() - window.addEventListener('resize', handleResize) - return () => { - window.removeEventListener('resize', handleResize) - } - }, [ref]) - return width -} diff --git a/web/pages/embed/[username]/[contractSlug].tsx b/web/pages/embed/[username]/[contractSlug].tsx index e925a1f6..cc4bc09d 100644 --- a/web/pages/embed/[username]/[contractSlug].tsx +++ b/web/pages/embed/[username]/[contractSlug].tsx @@ -79,7 +79,7 @@ export function ContractEmbed(props: { contract: Contract; bets: Bet[] }) { const href = `https://${DOMAIN}${contractPath(contract)}` - const { setElem, height: graphHeight } = useMeasureSize() + const { setElem, width: graphWidth, height: graphHeight } = useMeasureSize() const [betPanelOpen, setBetPanelOpen] = useState(false) @@ -132,7 +132,14 @@ export function ContractEmbed(props: { contract: Contract; bets: Bet[] }) { )}
- + {graphWidth != null && graphHeight != null && ( + + )}
)