-
Notifications
You must be signed in to change notification settings - Fork 134
feat(highlight): Add support to configure dimmed (unhighlight) colors for Bar and Partition charts #2774
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat(highlight): Add support to configure dimmed (unhighlight) colors for Bar and Partition charts #2774
Changes from all commits
5abf00d
49dcef6
89aef6f
611ab4a
97a7e76
5b2803d
36fa035
e5a9f9a
97cb8ec
1331670
82b300f
d9fdb76
b59f7de
bceaf5c
c7e926b
3901a6f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,13 +6,16 @@ | |
| * Side Public License, v 1. | ||
| */ | ||
|
|
||
| import type { AnimationState } from './partition'; | ||
| import { colorToRgba, RGBATupleToString } from '../../../../common/color_library_wrappers'; | ||
| import type { Color } from '../../../../common/colors'; | ||
| import { TAU } from '../../../../common/constants'; | ||
| import type { Pixels } from '../../../../common/geometry'; | ||
| import { cssFontShorthand, HorizontalAlignment } from '../../../../common/text_utils'; | ||
| import { renderLayers, withContext } from '../../../../renderers/canvas'; | ||
| import { MIN_STROKE_WIDTH } from '../../../../renderers/canvas/primitives/line'; | ||
| import type { LegendPath } from '../../../../state/actions/legend'; | ||
| import type { ArcSeriesStyle } from '../../../../utils/themes/theme'; | ||
| import type { | ||
| LinkLabelVM, | ||
| OutsideLinksViewModel, | ||
|
|
@@ -22,6 +25,8 @@ import type { | |
| ShapeViewModel, | ||
| TextRow, | ||
| } from '../../layout/types/viewmodel_types'; | ||
| import type { LegendStrategy } from '../../layout/utils/highlighted_geoms'; | ||
| import { highlightedGeoms } from '../../layout/utils/highlighted_geoms'; | ||
| import type { LinkLabelsViewModelSpec } from '../../layout/viewmodel/link_text_layout'; | ||
| import { isSunburst } from '../../layout/viewmodel/viewmodel'; | ||
|
|
||
|
|
@@ -149,22 +154,53 @@ function renderTaperedBorder( | |
| } | ||
| } | ||
|
|
||
| function renderSectors(ctx: CanvasRenderingContext2D, quadViewModel: QuadViewModel[]) { | ||
| function renderSectors( | ||
| ctx: CanvasRenderingContext2D, | ||
| quadViewModel: QuadViewModel[], | ||
| highlightedQuadSet: Set<QuadViewModel>, | ||
| arcSeriesStyle: ArcSeriesStyle, | ||
| ) { | ||
| withContext(ctx, () => { | ||
| ctx.scale(1, -1); // D3 and Canvas2d use a left-handed coordinate system (+y = down) but the ViewModel uses +y = up, so we must locally invert Y | ||
| quadViewModel.forEach((quad: QuadViewModel) => { | ||
| if (quad.x0 !== quad.x1) renderTaperedBorder(ctx, quad); | ||
| // Apply dimmed colors for unhighlighted quads | ||
| const isUnhighlighted = highlightedQuadSet.size > 0 && !highlightedQuadSet.has(quad); | ||
| const dimmed = arcSeriesStyle.arc.dimmed; | ||
| const dimmedFill = dimmed && 'fill' in dimmed ? dimmed.fill : undefined; | ||
| const useDimmedColor = isUnhighlighted && dimmedFill; | ||
|
|
||
| if (useDimmedColor) { | ||
| // Temporarily override fillColor for dimmed state | ||
| const originalFillColor = quad.fillColor; | ||
| quad.fillColor = dimmedFill; | ||
| if (quad.x0 !== quad.x1) renderTaperedBorder(ctx, quad); | ||
| quad.fillColor = originalFillColor; // Restore | ||
|
Comment on lines
+175
to
+177
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if we need to update the color just for that call, I prefer instead passing just the required properties (in case also by revisiting the |
||
| } else { | ||
| if (quad.x0 !== quad.x1) renderTaperedBorder(ctx, quad); | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| function renderRectangles(ctx: CanvasRenderingContext2D, quadViewModel: QuadViewModel[]) { | ||
| function renderRectangles( | ||
| ctx: CanvasRenderingContext2D, | ||
| quadViewModel: QuadViewModel[], | ||
| highlightedQuadSet: Set<QuadViewModel>, | ||
| arcSeriesStyle: ArcSeriesStyle, | ||
| ) { | ||
| withContext(ctx, () => { | ||
| ctx.scale(1, -1); // D3 and Canvas2d use a left-handed coordinate system (+y = down) but the ViewModel uses +y = up, so we must locally invert Y | ||
| quadViewModel.forEach(({ strokeWidth, fillColor, x0, x1, y0px, y1px }) => { | ||
| quadViewModel.forEach((quad) => { | ||
| const { strokeWidth, fillColor, x0, x1, y0px, y1px } = quad; | ||
| // only draw a shape if it would show up at all | ||
| if (x1 - x0 >= 1 && y1px - y0px >= 1) { | ||
| ctx.fillStyle = fillColor; | ||
| // Apply dimmed colors for unhighlighted quads | ||
| const isUnhighlighted = highlightedQuadSet.size > 0 && !highlightedQuadSet.has(quad); | ||
| const dimmed = arcSeriesStyle.arc.dimmed; | ||
| const dimmedFill = dimmed && 'fill' in dimmed ? dimmed.fill : undefined; | ||
| const useDimmedColor = isUnhighlighted && dimmedFill; | ||
|
|
||
| ctx.fillStyle = useDimmedColor ? dimmedFill : fillColor; | ||
| ctx.beginPath(); | ||
| ctx.moveTo(x0, y0px); | ||
| ctx.lineTo(x0, y1px); | ||
|
|
@@ -277,6 +313,12 @@ export function renderPartitionCanvas2d( | |
| panel, | ||
| chartDimensions, | ||
| }: ShapeViewModel, | ||
| _focus: unknown, | ||
| _animationState: AnimationState, | ||
|
Comment on lines
+316
to
+317
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see where this function get called, we probably need to fix there and call the relative functions with just the minimal amount of arguments, living these arguments there doesn't have much sense |
||
| highlightedLegendPath: LegendPath, | ||
| legendStrategy: LegendStrategy | undefined, | ||
| flatLegend: boolean | undefined, | ||
| arcSeriesStyle: ArcSeriesStyle, | ||
| ) { | ||
| const { sectorLineWidth, sectorLineStroke, linkLabel } = style; | ||
|
|
||
|
|
@@ -322,13 +364,24 @@ export function renderPartitionCanvas2d( | |
| ctx.strokeStyle = sectorLineStroke; | ||
| ctx.lineWidth = sectorLineWidth; | ||
|
|
||
| // Calculate which quads are highlighted for legend dimming | ||
| const highlightedQuadSet = new Set<QuadViewModel>(); | ||
| if (highlightedLegendPath.length > 0) { | ||
| // Use highlightedGeoms to determine which quads match the legend path | ||
| const highlighted = highlightedGeoms(legendStrategy, flatLegend, quadViewModel, highlightedLegendPath); | ||
| highlighted.forEach((quad) => highlightedQuadSet.add(quad)); | ||
| } | ||
|
|
||
| // painter's algorithm, like that of SVG: the sequence determines what overdraws what; first element of the array is drawn first | ||
| // (of course, with SVG, it's for ambiguous situations only, eg. when 3D transforms with different Z values aren't used, but | ||
| // unlike SVG and esp. WebGL, Canvas2d doesn't support the 3rd dimension well, see ctx.transform / ctx.setTransform). | ||
| // The layers are callbacks, because of the need to not bake in the `ctx`, it feels more composable and uncoupled this way. | ||
| renderLayers(ctx, [ | ||
| // bottom layer: sectors (pie slices, ring sectors etc.) | ||
| () => (isSunburst(layout) ? renderSectors(ctx, quadViewModel) : renderRectangles(ctx, quadViewModel)), | ||
| () => | ||
| isSunburst(layout) | ||
| ? renderSectors(ctx, quadViewModel, highlightedQuadSet, arcSeriesStyle) | ||
| : renderRectangles(ctx, quadViewModel, highlightedQuadSet, arcSeriesStyle), | ||
|
|
||
| // all the fill-based, potentially multirow text, whether inside or outside the sector | ||
| () => renderRowSets(ctx, rowSets, linkLineColor), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we extract this into a function and reuse it?
Probably we have probably a function that is used everytime we need to pick up the a dimmed or non-dimmed color and reuse it elsewhere also on area/line/point charts