Skip to content

Commit

Permalink
🔨 drop manager pattern for vertical color legend
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiamersmann committed Dec 20, 2024
1 parent cfce1bb commit 49d4482
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,7 @@ import {
ConnectedScatterLegend,
ConnectedScatterLegendManager,
} from "./ConnectedScatterLegend"
import {
VerticalColorLegend,
VerticalColorLegendManager,
} from "../verticalColorLegend/VerticalColorLegend"
import { VerticalColorLegend } from "../verticalColorLegend/VerticalColorLegend"
import { DualAxisComponent } from "../axis/AxisViews"
import { DualAxis, HorizontalAxis, VerticalAxis } from "../axis/Axis"

Expand Down Expand Up @@ -127,7 +124,6 @@ export class ScatterPlotChart
ConnectedScatterLegendManager,
ScatterSizeLegendManager,
ChartInterface,
VerticalColorLegendManager,
ColorScaleManager
{
// currently hovered legend color
Expand Down Expand Up @@ -510,8 +506,16 @@ export class ScatterPlotChart
return this.tooltipState.target?.series
}

@computed private get legendDimensions(): VerticalColorLegend {
return new VerticalColorLegend({ manager: this })
@computed private get verticalColorLegend(): {
width: number
height: number
} {
return VerticalColorLegend.dimensions({
maxLegendWidth: this.maxLegendWidth,
fontSize: this.fontSize,
legendItems: this.legendItems,
legendTitle: this.legendTitle,
})
}

@computed get maxLegendWidth(): number {
Expand All @@ -527,10 +531,10 @@ export class ScatterPlotChart
}

@computed.struct get sidebarWidth(): number {
const { legendDimensions, sidebarMinWidth, sidebarMaxWidth } = this
const { verticalColorLegend, sidebarMinWidth, sidebarMaxWidth } = this

return Math.max(
Math.min(legendDimensions.width, sidebarMaxWidth),
Math.min(verticalColorLegend.width, sidebarMaxWidth),
sidebarMinWidth
)
}
Expand Down Expand Up @@ -762,12 +766,12 @@ export class ScatterPlotChart
sizeLegend,
sidebarWidth,
comparisonLines,
legendDimensions,
verticalColorLegend,
} = this

const hasLegendItems = this.legendItems.length > 0
const verticalLegendHeight = hasLegendItems
? legendDimensions.height
? verticalColorLegend.height
: 0
const sizeLegendHeight = sizeLegend?.height ?? 0
const arrowLegendHeight = arrowLegend?.height ?? 0
Expand Down Expand Up @@ -828,7 +832,20 @@ export class ScatterPlotChart
/>
))}
{this.points}
<VerticalColorLegend manager={this} />
<VerticalColorLegend
maxLegendWidth={this.maxLegendWidth}
fontSize={this.fontSize}
legendItems={this.legendItems}
legendTitle={this.legendTitle}
onLegendMouseOver={this.onLegendMouseOver}
onLegendClick={this.onLegendClick}
onLegendMouseLeave={this.onLegendMouseLeave}
legendX={this.legendX}
legendY={this.legendY}
activeColors={this.activeColors}
focusColors={this.focusColors}
isStatic={manager.isStatic}
/>
{sizeLegend && (
<>
{separatorLine(ySizeLegend)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { DualAxisComponent } from "../axis/AxisViews"
import { NoDataModal } from "../noDataModal/NoDataModal"
import {
VerticalColorLegend,
VerticalColorLegendManager,
LegendItem,
} from "../verticalColorLegend/VerticalColorLegend"
import { TooltipFooterIcon } from "../tooltip/TooltipProps.js"
Expand Down Expand Up @@ -144,7 +143,7 @@ class StackedBarSegment extends React.Component<StackedBarSegmentProps> {
@observer
export class StackedBarChart
extends AbstractStackedChart
implements VerticalColorLegendManager, ColorScaleManager
implements ColorScaleManager
{
readonly minBarSpacing = 4

Expand Down Expand Up @@ -318,8 +317,15 @@ export class StackedBarChart
)
}

@computed private get verticalColorLegend(): VerticalColorLegend {
return new VerticalColorLegend({ manager: this })
@computed private get verticalColorLegend(): {
width: number
height: number
} {
return VerticalColorLegend.dimensions({
maxLegendWidth: this.maxLegendWidth,
fontSize: this.fontSize,
legendItems: this.legendItems,
})
}

@computed
Expand Down Expand Up @@ -476,7 +482,17 @@ export class StackedBarChart
return showHorizontalLegend ? (
<HorizontalCategoricalColorLegend manager={this} />
) : (
<VerticalColorLegend manager={this} />
<VerticalColorLegend
legendItems={this.legendItems}
maxLegendWidth={this.maxLegendWidth}
fontSize={this.fontSize}
onLegendMouseOver={this.onLegendMouseOver}
onLegendMouseLeave={this.onLegendMouseLeave}
legendX={this.legendX}
legendY={this.legendY}
activeColors={this.activeColors}
isStatic={this.isStatic}
/>
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React from "react"
import {
VerticalColorLegend,
VerticalColorLegendManager,
VerticalColorLegendProps,
} from "./VerticalColorLegend"

export default {
title: "VerticalColorLegend",
component: VerticalColorLegend,
}

const manager: VerticalColorLegendManager = {
const props: VerticalColorLegendProps = {
maxLegendWidth: 500,
legendTitle: "Legend Title",
legendItems: [
Expand All @@ -28,7 +28,7 @@ const manager: VerticalColorLegendManager = {
export const CategoricalBins = (): React.ReactElement => {
return (
<svg width={600} height={400}>
<VerticalColorLegend manager={manager} />
<VerticalColorLegend {...props} />
</svg>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,26 @@ import {
} from "../core/GrapherConstants"
import { Color } from "@ourworldindata/types"

export interface VerticalColorLegendManager {
export interface VerticalColorLegendProps {
legendItems: LegendItem[]
maxLegendWidth?: number
fontSize?: number
legendItems: LegendItem[]
legendTitle?: string
onLegendMouseOver?: (color: string) => void
onLegendClick?: (color: string) => void
onLegendMouseLeave?: () => void
legendX?: number
legendY?: number
activeColors: Color[]
activeColors?: Color[]
focusColors?: Color[]
isStatic?: boolean
}

type VerticalColorLegendPropsMinimal = Pick<
VerticalColorLegendProps,
"legendItems" | "maxLegendWidth" | "fontSize" | "legendTitle"
>

export interface LegendItem {
label?: string
minText?: string
Expand All @@ -40,37 +45,41 @@ interface SizedLegendSeries {
}

@observer
export class VerticalColorLegend extends React.Component<{
manager: VerticalColorLegendManager
}> {
@computed get manager(): VerticalColorLegendManager {
return this.props.manager
export class VerticalColorLegend extends React.Component<VerticalColorLegendProps> {
private rectPadding = 5
private lineHeight = 5

static dimensions(props: VerticalColorLegendPropsMinimal): {
width: number
height: number
} {
const legend = new VerticalColorLegend(props)
return {
width: legend.width,
height: legend.height,
}
}

@computed private get maxLegendWidth(): number {
return this.manager.maxLegendWidth ?? 100
return this.props.maxLegendWidth ?? 100
}

@computed private get fontSize(): number {
return (
GRAPHER_FONT_SCALE_11_2 * (this.manager.fontSize ?? BASE_FONT_SIZE)
)
return GRAPHER_FONT_SCALE_11_2 * (this.props.fontSize ?? BASE_FONT_SIZE)
}

@computed private get rectSize(): number {
return Math.round(this.fontSize / 1.4)
}

private rectPadding = 5
private lineHeight = 5

@computed private get title(): TextWrap | undefined {
if (!this.manager.legendTitle) return undefined
if (!this.props.legendTitle) return undefined
return new TextWrap({
maxWidth: this.maxLegendWidth,
fontSize: this.fontSize,
fontWeight: 700,
lineHeight: 1,
text: this.manager.legendTitle,
text: this.props.legendTitle,
})
}

Expand All @@ -80,17 +89,11 @@ export class VerticalColorLegend extends React.Component<{
}

@computed private get series(): SizedLegendSeries[] {
const {
manager,
fontSize,
rectSize,
rectPadding,
titleHeight,
lineHeight,
} = this
const { fontSize, rectSize, rectPadding, titleHeight, lineHeight } =
this

let runningYOffset = titleHeight
return manager.legendItems.map((series) => {
return this.props.legendItems.map((series) => {
let label = series.label
// infer label for numeric bins
if (!label && series.minText && series.maxText) {
Expand Down Expand Up @@ -133,16 +136,16 @@ export class VerticalColorLegend extends React.Component<{
}

@computed get legendX(): number {
return this.manager.legendX ?? 0
return this.props.legendX ?? 0
}

@computed get legendY(): number {
return this.manager.legendY ?? 0
return this.props.legendY ?? 0
}

renderLabels(): React.ReactElement {
const { series, manager, rectSize, rectPadding } = this
const { focusColors } = manager
const { series, rectSize, rectPadding } = this
const { focusColors } = this.props

return (
<g id={makeIdForHumanConsumption("labels")}>
Expand Down Expand Up @@ -173,8 +176,8 @@ export class VerticalColorLegend extends React.Component<{
}

renderSwatches(): React.ReactElement {
const { manager, series, rectSize, rectPadding } = this
const { activeColors } = manager
const { series, rectSize, rectPadding } = this
const { activeColors = [] } = this.props

return (
<g>
Expand Down Expand Up @@ -204,8 +207,9 @@ export class VerticalColorLegend extends React.Component<{
}

renderInteractiveElements(): React.ReactElement {
const { series, manager, lineHeight } = this
const { onLegendClick, onLegendMouseOver, onLegendMouseLeave } = manager
const { series, lineHeight } = this
const { onLegendClick, onLegendMouseOver, onLegendMouseLeave } =
this.props
return (
<g>
{series.map((series) => {
Expand Down Expand Up @@ -261,7 +265,7 @@ export class VerticalColorLegend extends React.Component<{
})}
{this.renderLabels()}
{this.renderSwatches()}
{!this.manager.isStatic && this.renderInteractiveElements()}
{!this.props.isStatic && this.renderInteractiveElements()}
</g>
)
}
Expand Down

0 comments on commit 49d4482

Please sign in to comment.