Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | 7x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 28x 18x 18x 18x | import { DataContext } from '@visx/xychart';
import { useContext } from 'react';
import styles from '../bar-chart.module.scss';
import { createGroupScale } from './band-scale';
import { computeSlotRect, getValueScaleBaseline } from './comparison-bars-geometry';
import type { BandScale } from './band-scale';
import type { DataPointDate } from '../../../types';
import type { FC } from 'react';
/** The slot the keyboard has selected. */
export type SelectedBar = {
/** The data of the bars the slot draws at the selected category, which the ring encloses. */
data: DataPointDate[];
/** The selected slot. */
slotKey: string;
/** The thickness of the slot's widest bar, as a share of the slot. */
thickness: number;
};
type AnyScale = ( ( input: unknown ) => number ) &
Partial< Pick< BandScale, 'bandwidth' > > & { range: () => unknown[] };
/**
* Outlines every bar in the keyboard-selected slot.
*
* @param props - Component props.
* @param props.selected - The selected slot.
* @param props.slotKeys - The slots, in the order the band lays them out.
* @param props.groupPadding - The padding between slots.
* @param props.horizontal - Whether the bars run horizontally.
* @param props.xAccessor - Reads a datum's x value.
* @param props.yAccessor - Reads a datum's y value.
* @return The ring, or nothing when the slot has no position.
*/
export const SelectedBarRing: FC< {
selected: SelectedBar;
slotKeys: string[];
groupPadding: number;
horizontal: boolean;
xAccessor: ( d: DataPointDate ) => string | number | Date | undefined;
yAccessor: ( d: DataPointDate ) => number | undefined;
} > = ( { selected, slotKeys, groupPadding, horizontal, xAccessor, yAccessor } ) => {
const context = useContext( DataContext );
const bandScale = ( horizontal ? context?.yScale : context?.xScale ) as AnyScale | undefined;
const valueScale = ( horizontal ? context?.xScale : context?.yScale ) as AnyScale | undefined;
const bandwidth = bandScale?.bandwidth?.() ?? 0;
const [ category ] = selected.data;
Iif ( ! bandScale || ! valueScale || ! bandwidth || ! category ) {
return null;
}
const groupScale = createGroupScale( slotKeys, bandwidth, groupPadding );
const slotOffset = groupScale( selected.slotKey );
const bandPosition = Number(
bandScale( ( horizontal ? yAccessor : xAccessor )( category ) as never )
);
const valuePositions = selected.data
.map( datum =>
Number( valueScale( Number( ( horizontal ? xAccessor : yAccessor )( datum ) ) as never ) )
)
.filter( Number.isFinite );
Iif ( slotOffset == null || ! Number.isFinite( bandPosition ) || ! valuePositions.length ) {
return null;
}
const rect = computeSlotRect( {
horizontal,
bandPosition,
slotOffset,
slotThickness: groupScale.bandwidth(),
valuePositions: [ getValueScaleBaseline( valueScale ), ...valuePositions ],
widthFactor: selected.thickness,
} );
return (
<rect
{ ...rect }
pointerEvents="none"
aria-hidden="true"
className={ styles[ 'selected-ring' ] }
data-testid="bar-chart-selected-ring"
/>
);
};
|