From 89a86989165c5db07af89254420e4933622cc305 Mon Sep 17 00:00:00 2001 From: Sophia Mersmann Date: Mon, 9 Dec 2024 12:12:49 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A7=20make=20interaction=20state=20int?= =?UTF-8?q?o=20record?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../grapher/src/chart/ChartUtils.tsx | 12 ++--- .../grapher/src/lineCharts/LineChart.tsx | 47 +++++++++---------- .../grapher/src/lineLegend/LineLegend.tsx | 19 +------- .../grapher/src/selection/InteractionArray.ts | 7 +-- .../grapher/src/slopeCharts/SlopeChart.tsx | 3 +- .../src/stackedCharts/StackedAreaChart.tsx | 7 ++- .../types/src/grapherTypes/GrapherTypes.ts | 6 +-- packages/@ourworldindata/types/src/index.ts | 2 +- 8 files changed, 38 insertions(+), 65 deletions(-) diff --git a/packages/@ourworldindata/grapher/src/chart/ChartUtils.tsx b/packages/@ourworldindata/grapher/src/chart/ChartUtils.tsx index 0d0a1ee1ca4..859d51b1a5c 100644 --- a/packages/@ourworldindata/grapher/src/chart/ChartUtils.tsx +++ b/packages/@ourworldindata/grapher/src/chart/ChartUtils.tsx @@ -197,15 +197,9 @@ export function findValidChartTypeCombination( export function byInteractionState(series: { hover: InteractionState }): number { - // background series first, - // then series in their default state, - // then active series - switch (series.hover) { - case InteractionState.background: - return 1 - case InteractionState.foreground: - return 2 - } + if (series.hover.background) return 1 + if (series.hover.active) return 3 + return 2 } export function findSeriesNamesContainedInBin( diff --git a/packages/@ourworldindata/grapher/src/lineCharts/LineChart.tsx b/packages/@ourworldindata/grapher/src/lineCharts/LineChart.tsx index a41690d8a3e..e3e9b2555c9 100644 --- a/packages/@ourworldindata/grapher/src/lineCharts/LineChart.tsx +++ b/packages/@ourworldindata/grapher/src/lineCharts/LineChart.tsx @@ -53,7 +53,6 @@ import { ColorScaleConfigInterface, ColorSchemeName, VerticalAlign, - InteractionState, } from "@ourworldindata/types" import { GRAPHER_AXIS_LINE_WIDTH_THICK, @@ -1283,7 +1282,7 @@ class Lines extends React.Component { if (this.props.hidePoints) return false const totalPoints = sum( this.props.series - .filter((s) => s.hover !== InteractionState.background) + .filter((series) => this.seriesHasMarkers(series)) .map((series) => series.placedPoints.length) ) return totalPoints < 500 @@ -1314,24 +1313,17 @@ class Lines extends React.Component { } private seriesHasMarkers(series: RenderLineChartSeries): boolean { - // Don't show markers for lines in the background - if (series.hover === InteractionState.background) return false - - // If the series only contains one point, then we will always want to - // show a marker/circle because we can't draw a line. - return ( - (this.hasMarkers || series.placedPoints.length === 1) && - !series.isProjection - ) + return !series.hover.background } renderLine(series: RenderLineChartSeries): React.ReactElement { + const { hover } = series + const color = series.placedPoints[0]?.color ?? DEFAULT_LINE_COLOR const strokeDasharray = series.isProjection ? "2,3" : undefined - const nonHovered = series.hover === InteractionState.background - const strokeWidth = nonHovered ? 1 : this.strokeWidth - const strokeOpacity = nonHovered ? 0.3 : 1 + const strokeWidth = hover.background ? 1 : this.strokeWidth + const strokeOpacity = hover.background ? 0.3 : 1 const outline = this.renderPathForSeries(series, { id: makeIdForHumanConsumption("outline", series.seriesName), @@ -1374,11 +1366,17 @@ class Lines extends React.Component { renderLineMarkers( series: RenderLineChartSeries ): React.ReactElement | void { - if (!this.seriesHasMarkers(series)) return + // If the series only contains one point, then we will always want to + // show a marker/circle because we can't draw a line. + const forceMarkers = series.placedPoints.length === 1 + + // check if we should hide markers on the chart and series level + const hideMarkers = !this.hasMarkers || !this.seriesHasMarkers(series) + + if (hideMarkers && !forceMarkers) return const { horizontalAxis } = this.props.dualAxis - const nonHovered = series.hover === InteractionState.background - const opacity = nonHovered ? 0.3 : 1 + const opacity = series.hover.background ? 0.3 : 1 return ( { renderLines(): React.ReactElement { return ( <> - {this.props.series.map((series) => { - const showMarkers = this.seriesHasMarkers(series) - return ( - - {this.renderLine(series)} - {showMarkers && this.renderLineMarkers(series)} - - ) - })} + {this.props.series.map((series) => ( + + {this.renderLine(series)} + {this.renderLineMarkers(series)} + + ))} ) } diff --git a/packages/@ourworldindata/grapher/src/lineLegend/LineLegend.tsx b/packages/@ourworldindata/grapher/src/lineLegend/LineLegend.tsx index 24b617d0d14..030189ea0e3 100644 --- a/packages/@ourworldindata/grapher/src/lineLegend/LineLegend.tsx +++ b/packages/@ourworldindata/grapher/src/lineLegend/LineLegend.tsx @@ -116,7 +116,7 @@ class LineLabels extends React.Component<{ } private textOpacityForSeries(series: PlacedSeries): number { - return series.hover === InteractionState.background ? 0.6 : 1 + return series.hover?.background ? 0.6 : 1 } @computed private get markers(): { @@ -225,10 +225,7 @@ class LineLabels extends React.Component<{ const step = (x2 - x1) / (totalLevels + 1) const markerXMid = x1 + step + level * step const d = `M${x1},${leftCenterY} H${markerXMid} V${rightCenterY} H${x2}` - const lineColor = - series.hover === InteractionState.background - ? "#eee" - : "#999" + const lineColor = series.hover?.background ? "#eee" : "#999" return ( { return this.partialInitialSeries.map((series) => series.seriesName) } - @computed private get backgroundSeries(): PlacedSeries[] { - return this.placedSeries.filter( - (series) => series.hover === InteractionState.background - ) - } - - @computed private get focusedSeries(): PlacedSeries[] { - return this.placedSeries.filter( - (series) => series.hover !== InteractionState.background - ) - } - // Does this placement need line markers or is the position of the labels already clear? @computed private get needsLines(): boolean { return this.placedSeries.some((series) => series.totalLevels > 1) diff --git a/packages/@ourworldindata/grapher/src/selection/InteractionArray.ts b/packages/@ourworldindata/grapher/src/selection/InteractionArray.ts index daff9a814df..16829aef32c 100644 --- a/packages/@ourworldindata/grapher/src/selection/InteractionArray.ts +++ b/packages/@ourworldindata/grapher/src/selection/InteractionArray.ts @@ -60,9 +60,10 @@ export class InteractionArray { * isn't currently interacted with. */ state(seriesName: SeriesName): InteractionState { - return this.isInForeground(seriesName) - ? InteractionState.foreground - : InteractionState.background + return { + active: this.isActive(seriesName), + background: this.isInBackground(seriesName), + } } @action.bound private _activate(seriesName: SeriesName): this { diff --git a/packages/@ourworldindata/grapher/src/slopeCharts/SlopeChart.tsx b/packages/@ourworldindata/grapher/src/slopeCharts/SlopeChart.tsx index d10206c4b92..a79424fec35 100644 --- a/packages/@ourworldindata/grapher/src/slopeCharts/SlopeChart.tsx +++ b/packages/@ourworldindata/grapher/src/slopeCharts/SlopeChart.tsx @@ -35,7 +35,6 @@ import { EntityName, VerticalAlign, FacetStrategy, - InteractionState, } from "@ourworldindata/types" import { ChartInterface } from "../chart/ChartInterface" import { ChartManager } from "../chart/ChartManager" @@ -1285,7 +1284,7 @@ function Slope({ }: SlopeProps) { const { seriesName, startPoint, endPoint } = series - const isInBackground = series.hover === InteractionState.background + const isInBackground = series.hover.background const showOutline = !isInBackground const opacity = isInBackground ? 0.3 : 1 diff --git a/packages/@ourworldindata/grapher/src/stackedCharts/StackedAreaChart.tsx b/packages/@ourworldindata/grapher/src/stackedCharts/StackedAreaChart.tsx index 3ca4cc85160..432644699f3 100644 --- a/packages/@ourworldindata/grapher/src/stackedCharts/StackedAreaChart.tsx +++ b/packages/@ourworldindata/grapher/src/stackedCharts/StackedAreaChart.tsx @@ -297,10 +297,9 @@ export class StackedAreaChart extends AbstractStackedChart { private hoverStateForSeries( series: StackedSeries ): InteractionState { - if (!this.focusedSeriesName) return InteractionState.foreground - return this.focusedSeriesName === series.seriesName - ? InteractionState.foreground - : InteractionState.background + const active = this.focusedSeriesName === series.seriesName + const background = !!this.focusedSeriesName && !active + return { active, background } } @computed get lineLegendSeries(): LineLabelSeries[] { diff --git a/packages/@ourworldindata/types/src/grapherTypes/GrapherTypes.ts b/packages/@ourworldindata/types/src/grapherTypes/GrapherTypes.ts index 93d27e98d0d..c1b01085b37 100644 --- a/packages/@ourworldindata/types/src/grapherTypes/GrapherTypes.ts +++ b/packages/@ourworldindata/types/src/grapherTypes/GrapherTypes.ts @@ -95,9 +95,9 @@ export enum DimensionProperty { table = "table", } -export enum InteractionState { - foreground = "foreground", // series is either actively hovered/focused or the chart is not currently being interacted with - background = "background", // another series is actively hovered/focused +export interface InteractionState { + active: boolean // actively hovered or focused + background: boolean // another series is actively hovered or focused } // see CoreTableConstants.ts diff --git a/packages/@ourworldindata/types/src/index.ts b/packages/@ourworldindata/types/src/index.ts index 29ea7d169b2..a9646a86dc4 100644 --- a/packages/@ourworldindata/types/src/index.ts +++ b/packages/@ourworldindata/types/src/index.ts @@ -113,7 +113,7 @@ export { GrapherWindowType, AxisMinMaxValueStr, GrapherTooltipAnchor, - InteractionState, + type InteractionState, } from "./grapherTypes/GrapherTypes.js" export {