Skip to content

Commit 6173633

Browse files
committed
[charts] Support focus on map shapes
1 parent a1a66fc commit 6173633

5 files changed

Lines changed: 83 additions & 0 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
'use client';
2+
import * as React from 'react';
3+
import { styled } from '@mui/material/styles';
4+
import { useFocusedItem } from '../hooks';
5+
import { useGeoData } from '../hooks/useGeoData';
6+
import { useGeoPath } from '../hooks/useGeoPath';
7+
import { useGeoFeatureIndexesByName } from '../hooks/useGeoFeatureIndexesByName';
8+
import { useMapShapeSeries } from '../hooks/useMapShapeSeries';
9+
10+
const FocusedMapShapeRoot = styled('path', {
11+
name: 'MuiMapShape',
12+
slot: 'Focused',
13+
})(({ theme }) => ({
14+
fill: 'none',
15+
stroke: (theme.vars ?? theme).palette.text.primary,
16+
strokeWidth: 2,
17+
pointerEvents: 'none',
18+
}));
19+
20+
/**
21+
* Renders an outline around the map shape currently focused through keyboard navigation.
22+
*/
23+
export function FocusedMapShape() {
24+
const focusedItem = useFocusedItem();
25+
const geoData = useGeoData();
26+
const path = useGeoPath();
27+
const featureIndexesByName = useGeoFeatureIndexesByName();
28+
const series = useMapShapeSeries();
29+
30+
if (focusedItem?.type !== 'mapShape' || !geoData || !path) {
31+
return null;
32+
}
33+
34+
const seriesItem = series.find((item) => item.id === focusedItem.seriesId);
35+
if (!seriesItem || seriesItem.hidden) {
36+
return null;
37+
}
38+
39+
const dataItem = seriesItem.data[focusedItem.dataIndex];
40+
if (!dataItem || dataItem.hidden) {
41+
return null;
42+
}
43+
44+
const featureIndexes = featureIndexesByName.get(dataItem.name);
45+
if (featureIndexes === undefined || featureIndexes.length === 0) {
46+
return null;
47+
}
48+
49+
return (
50+
<g aria-hidden>
51+
{featureIndexes.map((featureIndex) => {
52+
const feature = geoData.features[featureIndex];
53+
const d = path(feature);
54+
if (!d) {
55+
return null;
56+
}
57+
return <FocusedMapShapeRoot key={featureIndex} d={d} />;
58+
})}
59+
</g>
60+
);
61+
}

packages/x-charts-premium/src/Map/MapShapePlot.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useGeoPath } from '../hooks/useGeoPath';
66
import { useMapShapeSeries } from '../hooks/useMapShapeSeries';
77
import { useGeoFeatureIndexesByName } from '../hooks/useGeoFeatureIndexesByName';
88
import { MapShape } from './MapShape';
9+
import { FocusedMapShape } from './FocusedMapShape';
910
import { mapShapeSeriesConfig } from './seriesConfig';
1011

1112
export interface MapShapePlotProps {
@@ -93,6 +94,7 @@ export function MapShapePlot(props: MapShapePlotProps) {
9394
</g>
9495
);
9596
})}
97+
<FocusedMapShape />
9698
</g>
9799
);
98100
}

packages/x-charts-premium/src/Map/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from './GeoDataPlot';
22
export * from './MapShapePlot';
33
export * from './MapShape';
4+
export * from './FocusedMapShape';
45
export * from './Graticule';
56

67
// Types useful to the public API of the Map charts

packages/x-charts-premium/src/Map/seriesConfig/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ import legendGetter from './legend';
1111
import tooltipGetter from './tooltip';
1212
import getSeriesWithDefaultValues from './getSeriesWithDefaultValues';
1313
import descriptionGetter from './descriptionGetter';
14+
import keyboardFocusHandler from './keyboardFocusHandler';
1415

1516
export const mapShapeSeriesConfig: ChartSeriesTypeConfig<'mapShape'> = {
1617
seriesProcessor,
1718
colorProcessor: getColor,
1819
legendGetter,
1920
tooltipGetter,
2021
getSeriesWithDefaultValues,
22+
keyboardFocusHandler,
2123
identifierSerializer: identifierSerializerSeriesIdDataIndex,
2224
identifierCleaner: identifierCleanerSeriesIdDataIndex,
2325
descriptionGetter,
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import {
2+
createCommonKeyboardFocusHandler,
3+
type KeyboardFocusHandler,
4+
} from '@mui/x-charts/internals';
5+
6+
const mapShapeSeriesTypes = new Set(['mapShape'] as const);
7+
8+
/**
9+
* Move the focus across the shapes of the map series.
10+
*
11+
* `ArrowRight`/`ArrowLeft` move to the next/previous shape of the focused series,
12+
* while `ArrowUp`/`ArrowDown` move between series.
13+
*/
14+
const keyboardFocusHandler: KeyboardFocusHandler<'mapShape', 'mapShape'> =
15+
createCommonKeyboardFocusHandler(mapShapeSeriesTypes);
16+
17+
export default keyboardFocusHandler;

0 commit comments

Comments
 (0)