>(() => {
- return {
- isInitialized: true,
- data: {
- highlightScope,
- highlightedItem,
- setHighlighted: (itemData) => {
- setHighlightedItem(itemData);
- onHighlightChange?.(itemData);
- },
- clearHighlighted: () => {
- setHighlightedItem(null);
- onHighlightChange?.(null);
- },
- isHighlighted: createIsHighlighted(highlightScope, highlightedItem),
- isFaded: createIsFaded(highlightScope, highlightedItem),
- },
- };
- }, [highlightedItem, highlightScope, setHighlightedItem, onHighlightChange]);
-
- return (
- {children}
- );
-}
-
-HighlightedProvider.propTypes = {
- // ----------------------------- Warning --------------------------------
- // | These PropTypes are generated from the TypeScript type definitions |
- // | To update them edit the TypeScript types and run "pnpm proptypes" |
- // ----------------------------------------------------------------------
- children: PropTypes.node,
- /**
- * The item currently highlighted. Turns highlighting into a controlled prop.
- */
- highlightedItem: PropTypes.shape({
- dataIndex: PropTypes.number,
- seriesId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
- }),
- /**
- * The callback fired when the highlighted item changes.
- *
- * @param {HighlightItemData | null} highlightedItem The newly highlighted item.
- */
- onHighlightChange: PropTypes.func,
-} as any;
-
-export { HighlightedProvider };
diff --git a/packages/x-charts/src/context/HighlightedProvider/createIsFaded.ts b/packages/x-charts/src/context/HighlightedProvider/createIsFaded.ts
deleted file mode 100644
index 8ec92655fcacb..0000000000000
--- a/packages/x-charts/src/context/HighlightedProvider/createIsFaded.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { HighlightItemData, HighlightScope } from './HighlightedContext';
-
-export const createIsFaded =
- (highlightScope: HighlightScope | null | undefined, highlightedItem: HighlightItemData | null) =>
- (input: HighlightItemData): boolean => {
- if (!highlightScope) {
- return false;
- }
-
- if (highlightScope.fade === 'series') {
- return (
- input.seriesId === highlightedItem?.seriesId &&
- input.dataIndex !== highlightedItem?.dataIndex
- );
- }
-
- if (highlightScope.fade === 'global') {
- return (
- input.seriesId !== highlightedItem?.seriesId ||
- input.dataIndex !== highlightedItem?.dataIndex
- );
- }
-
- return false;
- };
diff --git a/packages/x-charts/src/context/HighlightedProvider/createIsHighlighted.ts b/packages/x-charts/src/context/HighlightedProvider/createIsHighlighted.ts
deleted file mode 100644
index 3d94cd023f6fc..0000000000000
--- a/packages/x-charts/src/context/HighlightedProvider/createIsHighlighted.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { HighlightItemData, HighlightScope } from './HighlightedContext';
-
-export const createIsHighlighted =
- (highlightScope: HighlightScope | null | undefined, highlightedItem: HighlightItemData | null) =>
- (input: HighlightItemData): boolean => {
- if (!highlightScope) {
- return false;
- }
-
- if (highlightScope.highlight === 'series') {
- return input.seriesId === highlightedItem?.seriesId;
- }
-
- if (highlightScope.highlight === 'item') {
- return (
- input.dataIndex === highlightedItem?.dataIndex &&
- input.seriesId === highlightedItem?.seriesId
- );
- }
-
- return false;
- };
diff --git a/packages/x-charts/src/context/HighlightedProvider/index.ts b/packages/x-charts/src/context/HighlightedProvider/index.ts
deleted file mode 100644
index c1186b1179d21..0000000000000
--- a/packages/x-charts/src/context/HighlightedProvider/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from './HighlightedProvider';
-export * from './HighlightedContext';
-export * from './useHighlighted';
-export * from './useItemHighlighted';
diff --git a/packages/x-charts/src/context/HighlightedProvider/useHighlighted.test.tsx b/packages/x-charts/src/context/HighlightedProvider/useHighlighted.test.tsx
deleted file mode 100644
index b16a625f62b1b..0000000000000
--- a/packages/x-charts/src/context/HighlightedProvider/useHighlighted.test.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import * as React from 'react';
-import { expect } from 'chai';
-import { ErrorBoundary, createRenderer, screen, reactMajor } from '@mui/internal-test-utils';
-import { testSkipIf, isJSDOM } from 'test/utils/skipIf';
-import { useHighlighted } from './useHighlighted';
-import { HighlightedProvider } from './HighlightedProvider';
-import { ChartProvider } from '../ChartProvider';
-
-function UseHighlighted() {
- const { highlightedItem } = useHighlighted();
- return {highlightedItem?.seriesId}
;
-}
-
-describe('useHighlighted', () => {
- const { render } = createRenderer();
-
- // can't catch render errors in the browser for unknown reason
- // tried try-catch + error boundary + window onError preventDefault
- testSkipIf(!isJSDOM)('should throw an error when parent context not present', () => {
- const errorRef = React.createRef();
-
- const errorMessage1 = 'MUI X: Could not find the highlighted ref context.';
- const errorMessage2 =
- 'It looks like you rendered your component outside of a ChartsContainer parent component.';
- const errorMessage3 = 'The above error occurred in the component:';
- const expectedError =
- reactMajor < 19
- ? [errorMessage1, errorMessage2, errorMessage3]
- : `${errorMessage1}\n${errorMessage2}`;
-
- expect(() =>
- render(
-
-
- ,
- ),
- ).toErrorDev(expectedError);
-
- expect((errorRef.current as any).errors).to.have.length(1);
- expect((errorRef.current as any).errors[0].toString()).to.include(
- 'MUI X: Could not find the highlighted ref context.',
- );
- });
-
- it('should not throw an error when parent context is present', () => {
- render(
-
-
-
-
- ,
- );
-
- expect(screen.getByText('test-id')).toBeVisible();
- });
-});
diff --git a/packages/x-charts/src/context/HighlightedProvider/useHighlighted.ts b/packages/x-charts/src/context/HighlightedProvider/useHighlighted.ts
deleted file mode 100644
index ab584861dded7..0000000000000
--- a/packages/x-charts/src/context/HighlightedProvider/useHighlighted.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-'use client';
-import * as React from 'react';
-import { HighlightedContext, HighlightedState } from './HighlightedContext';
-
-/**
- * A hook to get the highlighted state of the chart.
- *
- * Please consider using the `useItemHighlighted` hook if you need to check the state of a specific item.
- *
- * @returns {HighlightedState} the state of the chart
- */
-export function useHighlighted(): HighlightedState {
- const { isInitialized, data } = React.useContext(HighlightedContext);
-
- if (!isInitialized) {
- throw new Error(
- [
- 'MUI X: Could not find the highlighted ref context.',
- 'It looks like you rendered your component outside of a ChartsContainer parent component.',
- ].join('\n'),
- );
- }
-
- return data;
-}
diff --git a/packages/x-charts/src/context/HighlightedProvider/useItemHighlighted.ts b/packages/x-charts/src/context/HighlightedProvider/useItemHighlighted.ts
deleted file mode 100644
index 721d7f902017e..0000000000000
--- a/packages/x-charts/src/context/HighlightedProvider/useItemHighlighted.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-'use client';
-import { HighlightItemData } from './HighlightedContext';
-import { useHighlighted } from './useHighlighted';
-
-export type ItemHighlightedState = {
- /**
- * Whether the item is highlighted.
- */
- isHighlighted: boolean;
- /**
- * Whether the item is faded.
- */
- isFaded: boolean;
-};
-
-/**
- * A hook to check the highlighted state of the item.
- * This function already calculates that an item is not faded if it is highlighted.
- *
- * If you need fine control over the state, use the `useHighlighted` hook instead.
- *
- * @param {HighlightItemData} item is the item to check
- * @returns {ItemHighlightedState} the state of the item
- */
-export function useItemHighlighted(item: HighlightItemData | null): ItemHighlightedState {
- const highlighted = useHighlighted();
-
- if (!item) {
- return {
- isHighlighted: false,
- isFaded: false,
- };
- }
-
- const isHighlighted = highlighted.isHighlighted(item);
- const isFaded = !isHighlighted && highlighted.isFaded(item);
-
- return { isHighlighted, isFaded };
-}
diff --git a/packages/x-charts/src/context/InteractionSelectors.ts b/packages/x-charts/src/context/InteractionSelectors.ts
deleted file mode 100644
index 7df8523f6f9b6..0000000000000
--- a/packages/x-charts/src/context/InteractionSelectors.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { UseChartInteractionSignature } from '../internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.types';
-import { ChartState } from '../internals/plugins/models';
-import { createSelector } from '../internals/plugins/utils/selectors';
-
-function selectInteraction(state: ChartState<[UseChartInteractionSignature]>) {
- return state.interaction;
-}
-
-export const selectorChartsInteractionItem = createSelector(
- selectInteraction,
- (interaction) => interaction.item,
-);
-
-export const selectorChartsInteractionAxis = createSelector(
- selectInteraction,
- (interaction) => interaction.axis,
-);
-
-export const selectorChartsInteractionXAxis = createSelector(
- selectInteraction,
- (interaction) => interaction.axis.x,
-);
-
-export const selectorChartsInteractionYAxis = createSelector(
- selectInteraction,
- (interaction) => interaction.axis.y,
-);
-
-export const selectorChartsInteractionItemIsDefined = createSelector(
- selectorChartsInteractionItem,
- (item) => item !== null,
-);
-
-export const selectorChartsInteractionXAxisIsDefined = createSelector(
- selectorChartsInteractionXAxis,
- (x) => x !== null,
-);
-
-export const selectorChartsInteractionYAxisIsDefined = createSelector(
- selectorChartsInteractionYAxis,
- (y) => y !== null,
-);
-
-export const selectorChartsInteractionIsVoronoiEnabled = createSelector(
- selectInteraction,
- (interaction) => interaction.isVoronoiEnabled,
-);
diff --git a/packages/x-charts/src/context/index.ts b/packages/x-charts/src/context/index.ts
index da6326f1f9407..45f950c838a7b 100644
--- a/packages/x-charts/src/context/index.ts
+++ b/packages/x-charts/src/context/index.ts
@@ -1 +1,6 @@
-export * from './HighlightedProvider';
+export type {
+ HighlightScope,
+ FadeOptions,
+ HighlightItemData,
+ HighlightOptions,
+} from '../internals/plugins/featurePlugins/useChartHighlight';
diff --git a/packages/x-charts/src/hooks/index.ts b/packages/x-charts/src/hooks/index.ts
index 9bbe0ac753cef..87ee20d63333e 100644
--- a/packages/x-charts/src/hooks/index.ts
+++ b/packages/x-charts/src/hooks/index.ts
@@ -5,6 +5,8 @@ export * from './useAxis';
export * from './useZAxis';
export * from './useColorScale';
export * from './useSvgRef';
+export * from './useItemHighlighted';
+export * from './useItemHighlightedGetter';
export {
useSeries as unstable_useSeries,
usePieSeries as unstable_usePieSeries,
diff --git a/packages/x-charts/src/hooks/useInteractionItemProps.ts b/packages/x-charts/src/hooks/useInteractionItemProps.ts
index d1230d3fbb237..feb1fafda8540 100644
--- a/packages/x-charts/src/hooks/useInteractionItemProps.ts
+++ b/packages/x-charts/src/hooks/useInteractionItemProps.ts
@@ -1,13 +1,13 @@
'use client';
import * as React from 'react';
import { SeriesItemIdentifier } from '../models';
-import { useHighlighted } from '../context';
-import { useStore } from '../internals/store/useStore';
+import { useChartContext } from '../context/ChartProvider';
+import { UseChartHighlightSignature } from '../internals/plugins/featurePlugins/useChartHighlight';
+import { UseChartInteractionSignature } from '../internals/plugins/featurePlugins/useChartInteraction';
export const useInteractionItemProps = (skip?: boolean) => {
- const store = useStore();
-
- const { setHighlighted, clearHighlighted } = useHighlighted();
+ const { instance } =
+ useChartContext<[UseChartInteractionSignature, UseChartHighlightSignature]>();
if (skip) {
return () => ({});
@@ -19,14 +19,8 @@ export const useInteractionItemProps = (skip?: boolean) => {
}
};
const onPointerEnter = () => {
- store.update((prev) => ({
- ...prev,
- interaction: {
- ...prev.interaction,
- item: data,
- },
- }));
- setHighlighted({
+ instance.setItemInteraction(data);
+ instance.setHighlight({
seriesId: data.seriesId,
dataIndex: data.dataIndex,
});
@@ -36,26 +30,8 @@ export const useInteractionItemProps = (skip?: boolean) => {
event.currentTarget.releasePointerCapture(event.pointerId);
}
- store.update((prev) => {
- const prevItem = prev.interaction.item;
- if (
- prevItem === null ||
- Object.keys(data).some(
- (key) => data[key as keyof typeof data] !== prevItem[key as keyof typeof prevItem],
- )
- ) {
- // The item is already something else, no need to clean it.
- return prev;
- }
- return {
- ...prev,
- interaction: {
- ...prev.interaction,
- item: null,
- },
- };
- });
- clearHighlighted();
+ instance.removeItemInteraction(data);
+ instance.clearHighlight();
};
return {
onPointerEnter,
diff --git a/packages/x-charts/src/hooks/useItemHighlighted.ts b/packages/x-charts/src/hooks/useItemHighlighted.ts
new file mode 100644
index 0000000000000..df17cfcf9738b
--- /dev/null
+++ b/packages/x-charts/src/hooks/useItemHighlighted.ts
@@ -0,0 +1,43 @@
+'use client';
+
+import { useStore } from '../internals/store/useStore';
+import { useSelector } from '../internals/store/useSelector';
+import {
+ selectorChartsIsFaded,
+ selectorChartsIsHighlighted,
+} from '../internals/plugins/featurePlugins/useChartHighlight';
+import {
+ HighlightItemData,
+ UseChartHighlightSignature,
+} from '../internals/plugins/featurePlugins/useChartHighlight/useChartHighlight.types';
+
+type UseItemHighlightedReturnType = {
+ /**
+ * Whether the item is highlighted.
+ */
+ isHighlighted: boolean;
+ /**
+ * Whether the item is faded.
+ */
+ isFaded: boolean;
+};
+
+type UseItemHighlightedParams = HighlightItemData | null;
+
+/**
+ * A hook to check the highlighted state of the item.
+ * This function already calculates that an item is not faded if it is highlighted.
+ *
+ * If you need fine control over the state, use the `useItemHighlightedGetter` hook instead.
+ *
+ * @param {HighlightItemData | null} item is the item to check
+ * @returns {UseItemHighlightedReturnType} the state of the item
+ */
+export function useItemHighlighted(item: UseItemHighlightedParams): UseItemHighlightedReturnType {
+ const store = useStore<[UseChartHighlightSignature]>();
+
+ const isHighlighted = useSelector(store, selectorChartsIsHighlighted, item);
+ const isFaded = useSelector(store, selectorChartsIsFaded, item);
+
+ return { isHighlighted, isFaded: !isHighlighted && isFaded };
+}
diff --git a/packages/x-charts/src/hooks/useItemHighlightedGetter.tsx b/packages/x-charts/src/hooks/useItemHighlightedGetter.tsx
new file mode 100644
index 0000000000000..715493085753b
--- /dev/null
+++ b/packages/x-charts/src/hooks/useItemHighlightedGetter.tsx
@@ -0,0 +1,26 @@
+'use client';
+import { useSelector } from '../internals/store/useSelector';
+import { useStore } from '../internals/store/useStore';
+import {
+ selectorChartsIsFadedCallback,
+ selectorChartsIsHighlightedCallback,
+} from '../internals/plugins/featurePlugins/useChartHighlight/useChartHighlight.selectors';
+
+/**
+ * A hook to check the highlighted state of multiple items.
+ * If you're interested by a single one, consider using `useItemHighlighted`.
+ *
+ * Warning: highlighted and faded can both be true at the same time.
+ * We recommend to first test if item is highlighted: `const faded = !highlighted && isFaded(item)`
+ * @returns {{ isHighlighted, isFaded }} callbacks to get the state of the item.
+ */
+export function useItemHighlightedGetter() {
+ const store = useStore();
+
+ const isHighlighted = useSelector(store, selectorChartsIsHighlightedCallback);
+ const isFaded = useSelector(store, selectorChartsIsFadedCallback);
+ return {
+ isHighlighted,
+ isFaded,
+ };
+}
diff --git a/packages/x-charts/src/internals/index.ts b/packages/x-charts/src/internals/index.ts
index bd70fa0195249..4076fbcbc3ee7 100644
--- a/packages/x-charts/src/internals/index.ts
+++ b/packages/x-charts/src/internals/index.ts
@@ -20,6 +20,7 @@ export * from './plugins/corePlugins/useChartDimensions';
export * from './plugins/featurePlugins/useChartZAxis';
export * from './plugins/featurePlugins/useChartCartesianAxis';
export * from './plugins/featurePlugins/useChartInteraction';
+export * from './plugins/featurePlugins/useChartHighlight';
export * from './plugins/utils/selectors';
export * from './store/useCharts';
diff --git a/packages/x-charts/src/internals/plugins/allPlugins.ts b/packages/x-charts/src/internals/plugins/allPlugins.ts
index e5af78e20ddc1..4ee618225c0b7 100644
--- a/packages/x-charts/src/internals/plugins/allPlugins.ts
+++ b/packages/x-charts/src/internals/plugins/allPlugins.ts
@@ -4,6 +4,8 @@ import {
useChartCartesianAxis,
UseChartCartesianAxisSignature,
} from './featurePlugins/useChartCartesianAxis';
+import { UseChartHighlightSignature } from './featurePlugins/useChartHighlight';
+import { useChartHighlight } from './featurePlugins/useChartHighlight/useChartHighlight';
import {
useChartInteraction,
UseChartInteractionSignature,
@@ -15,9 +17,15 @@ export type AllPluginSignatures,
UseChartInteractionSignature,
+ UseChartHighlightSignature,
];
export type AllPluginsType =
ConvertSignaturesIntoPlugins>;
-export const ALL_PLUGINS = [useChartZAxis, useChartCartesianAxis, useChartInteraction];
+export const ALL_PLUGINS = [
+ useChartZAxis,
+ useChartCartesianAxis,
+ useChartInteraction,
+ useChartHighlight,
+];
diff --git a/packages/x-charts/src/context/HighlightedProvider/createIsFaded.test.ts b/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/createIsFaded.test.ts
similarity index 85%
rename from packages/x-charts/src/context/HighlightedProvider/createIsFaded.test.ts
rename to packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/createIsFaded.test.ts
index b333d94442805..d64cc199d8145 100644
--- a/packages/x-charts/src/context/HighlightedProvider/createIsFaded.test.ts
+++ b/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/createIsFaded.test.ts
@@ -1,10 +1,12 @@
import { expect } from 'chai';
import { createIsFaded } from './createIsFaded';
+const seriesId = 'id1';
+const dataIndex = 1;
+
const itemData = {
- seriesId: '1s',
- dataIndex: 1,
- value: '1v',
+ seriesId,
+ dataIndex,
};
describe('createIsFaded', () => {
@@ -20,7 +22,7 @@ describe('createIsFaded', () => {
});
it('should return false when input series is different than highlighted', () => {
- expect(isFadedSameSeries({ ...itemData, seriesId: '2' })).to.equal(false);
+ expect(isFadedSameSeries({ ...itemData, seriesId: 'id2' })).to.equal(false);
});
});
@@ -36,7 +38,7 @@ describe('createIsFaded', () => {
});
it('should return true when series is different than highlighted', () => {
- expect(isFadedGlobal({ ...itemData, seriesId: '2' })).to.equal(true);
+ expect(isFadedGlobal({ ...itemData, seriesId: 'id2' })).to.equal(true);
});
});
diff --git a/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/createIsFaded.ts b/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/createIsFaded.ts
new file mode 100644
index 0000000000000..0d6ca20790725
--- /dev/null
+++ b/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/createIsFaded.ts
@@ -0,0 +1,24 @@
+import { HighlightScope } from './highlightConfig.types';
+import { HighlightItemData } from './useChartHighlight.types';
+
+export const createIsFaded =
+ (highlightScope: HighlightScope | null | undefined, highlightedItem: HighlightItemData | null) =>
+ (item: HighlightItemData | null): boolean => {
+ if (!highlightScope || !highlightedItem || !item) {
+ return false;
+ }
+
+ if (highlightScope.fade === 'series') {
+ return (
+ item.seriesId === highlightedItem.seriesId && item.dataIndex !== highlightedItem.dataIndex
+ );
+ }
+
+ if (highlightScope.fade === 'global') {
+ return (
+ item.seriesId !== highlightedItem.seriesId || item.dataIndex !== highlightedItem.dataIndex
+ );
+ }
+
+ return false;
+ };
diff --git a/packages/x-charts/src/context/HighlightedProvider/createIsHighlighted.test.ts b/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/createIsHighlighted.test.ts
similarity index 87%
rename from packages/x-charts/src/context/HighlightedProvider/createIsHighlighted.test.ts
rename to packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/createIsHighlighted.test.ts
index 3670226150a8d..a8b8bb2da7c49 100644
--- a/packages/x-charts/src/context/HighlightedProvider/createIsHighlighted.test.ts
+++ b/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/createIsHighlighted.test.ts
@@ -1,10 +1,12 @@
import { expect } from 'chai';
import { createIsHighlighted } from './createIsHighlighted';
+const seriesId = 'id1';
+const dataIndex = 1;
+
const itemData = {
- seriesId: '1s',
- dataIndex: 1,
- value: '1v',
+ seriesId,
+ dataIndex,
};
describe('createIsHighlighted', () => {
@@ -20,7 +22,7 @@ describe('createIsHighlighted', () => {
});
it('should return false when input series is different than highlighted', () => {
- expect(isHighlightedSameSeries({ ...itemData, seriesId: '2' })).to.equal(false);
+ expect(isHighlightedSameSeries({ ...itemData, seriesId: 'id2' })).to.equal(false);
});
it('should return true when input item is different than highlighted', () => {
@@ -40,7 +42,7 @@ describe('createIsHighlighted', () => {
});
it('should return false when input series is different than highlighted', () => {
- expect(isHighlightedItem({ ...itemData, seriesId: '2' })).to.equal(false);
+ expect(isHighlightedItem({ ...itemData, seriesId: 'id2' })).to.equal(false);
});
});
diff --git a/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/createIsHighlighted.ts b/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/createIsHighlighted.ts
new file mode 100644
index 0000000000000..d9ecb5fc562ee
--- /dev/null
+++ b/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/createIsHighlighted.ts
@@ -0,0 +1,22 @@
+import { HighlightScope } from './highlightConfig.types';
+import { HighlightItemData } from './useChartHighlight.types';
+
+export const createIsHighlighted =
+ (highlightScope: HighlightScope | null | undefined, highlightedItem: HighlightItemData | null) =>
+ (item: HighlightItemData | null): boolean => {
+ if (!highlightScope || !highlightedItem || !item) {
+ return false;
+ }
+
+ if (highlightScope.highlight === 'series') {
+ return item.seriesId === highlightedItem.seriesId;
+ }
+
+ if (highlightScope.highlight === 'item') {
+ return (
+ item.dataIndex === highlightedItem.dataIndex && item.seriesId === highlightedItem.seriesId
+ );
+ }
+
+ return false;
+ };
diff --git a/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/highlightConfig.types.ts b/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/highlightConfig.types.ts
new file mode 100644
index 0000000000000..f50bb56d9e205
--- /dev/null
+++ b/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/highlightConfig.types.ts
@@ -0,0 +1,22 @@
+export type HighlightOptions = 'none' | 'item' | 'series';
+
+export type FadeOptions = 'none' | 'series' | 'global';
+
+export type HighlightScope = {
+ /**
+ * The scope of highlighted elements.
+ * - 'none': no highlight.
+ * - 'item': only highlight the item.
+ * - 'series': highlight all elements of the same series.
+ * @default 'none'
+ */
+ highlight?: HighlightOptions;
+ /**
+ * The scope of faded elements.
+ * - 'none': no fading.
+ * - 'series': only fade element of the same series.
+ * - 'global': fade all elements that are not highlighted.
+ * @default 'none'
+ */
+ fade?: FadeOptions;
+};
diff --git a/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/index.ts b/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/index.ts
new file mode 100644
index 0000000000000..e42914a8d67fc
--- /dev/null
+++ b/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/index.ts
@@ -0,0 +1,4 @@
+export { useChartHighlight } from './useChartHighlight';
+export * from './useChartHighlight.selectors';
+export type { UseChartHighlightSignature, HighlightItemData } from './useChartHighlight.types';
+export type * from './highlightConfig.types';
diff --git a/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/useChartHighlight.selectors.ts b/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/useChartHighlight.selectors.ts
new file mode 100644
index 0000000000000..65874fa688615
--- /dev/null
+++ b/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/useChartHighlight.selectors.ts
@@ -0,0 +1,69 @@
+import { SeriesId } from '../../../../models/seriesType/common';
+import { ChartSeriesType } from '../../../../models/seriesType/config';
+import { UseChartSeriesSignature } from '../../corePlugins/useChartSeries/useChartSeries.types';
+import { ChartRootSelector, createSelector } from '../../utils/selectors';
+import { HighlightItemData, UseChartHighlightSignature } from './useChartHighlight.types';
+import { HighlightScope } from './highlightConfig.types';
+import { createIsHighlighted } from './createIsHighlighted';
+import { createIsFaded } from './createIsFaded';
+
+const selectHighlight: ChartRootSelector = (state) => state.highlight;
+
+const selectSeries: ChartRootSelector = (state) => state.series;
+
+export const selectorChartsHighlightScopePerSeriesId = createSelector(
+ selectSeries,
+ (series): Map | undefined> => {
+ const map = new Map | undefined>();
+
+ Object.keys(series.processedSeries).forEach((seriesType) => {
+ const seriesData = series.processedSeries[seriesType as ChartSeriesType];
+ Object.keys(seriesData?.series ?? {}).forEach((seriesId) => {
+ const seriesItem = seriesData?.series[seriesId];
+ map.set(seriesId, seriesItem?.highlightScope);
+ });
+ });
+ return map;
+ },
+);
+
+export const selectorChartsHighlightedItem = createSelector(
+ selectHighlight,
+ (highlight) => highlight.item,
+);
+
+export const selectorChartsHighlightScope = createSelector(
+ [selectorChartsHighlightScopePerSeriesId, selectorChartsHighlightedItem],
+ (seriesIdToHighlightScope, highlightedItem) => {
+ if (!highlightedItem) {
+ return null;
+ }
+ const highlightScope = seriesIdToHighlightScope.get(highlightedItem.seriesId);
+
+ if (highlightScope === undefined) {
+ return null;
+ }
+
+ return highlightScope;
+ },
+);
+
+export const selectorChartsIsHighlightedCallback = createSelector(
+ [selectorChartsHighlightScope, selectorChartsHighlightedItem],
+ createIsHighlighted,
+);
+
+export const selectorChartsIsFadedCallback = createSelector(
+ [selectorChartsHighlightScope, selectorChartsHighlightedItem],
+ createIsFaded,
+);
+
+export const selectorChartsIsHighlighted = createSelector(
+ [selectorChartsIsHighlightedCallback, (_, item: HighlightItemData | null) => item],
+ (callback, item) => callback(item),
+);
+
+export const selectorChartsIsFaded = createSelector(
+ [selectorChartsIsFadedCallback, (_, item: HighlightItemData | null) => item],
+ (callback, item) => callback(item),
+);
diff --git a/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/useChartHighlight.ts b/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/useChartHighlight.ts
new file mode 100644
index 0000000000000..4c23e6eb08213
--- /dev/null
+++ b/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/useChartHighlight.ts
@@ -0,0 +1,52 @@
+import useEventCallback from '@mui/utils/useEventCallback';
+import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
+import { ChartPlugin } from '../../models';
+import { HighlightItemData, UseChartHighlightSignature } from './useChartHighlight.types';
+
+export const useChartHighlight: ChartPlugin = ({
+ store,
+ params,
+ models,
+}) => {
+ useEnhancedEffect(() => {
+ store.update((prevState) => ({
+ ...prevState,
+ highlight: {
+ ...prevState.highlight,
+ item: models.highlightedItem.value,
+ },
+ }));
+ }, [store, models.highlightedItem.value]);
+
+ const clearHighlight = useEventCallback(() => {
+ params.onHighlightChange?.(null);
+ models.highlightedItem.setControlledValue(null);
+ });
+
+ const setHighlight = useEventCallback((newItem: HighlightItemData) => {
+ params.onHighlightChange?.(newItem);
+ models.highlightedItem.setControlledValue(newItem);
+ });
+
+ return {
+ instance: {
+ clearHighlight,
+ setHighlight,
+ },
+ };
+};
+
+useChartHighlight.models = {
+ highlightedItem: {
+ getDefaultValue: () => null,
+ },
+};
+
+useChartHighlight.getInitialState = (params) => ({
+ highlight: { item: params.highlightedItem ?? null },
+});
+
+useChartHighlight.params = {
+ highlightedItem: true,
+ onHighlightChange: true,
+};
diff --git a/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/useChartHighlight.types.ts b/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/useChartHighlight.types.ts
new file mode 100644
index 0000000000000..e97243c4b7636
--- /dev/null
+++ b/packages/x-charts/src/internals/plugins/featurePlugins/useChartHighlight/useChartHighlight.types.ts
@@ -0,0 +1,80 @@
+import { DefaultizedProps } from '@mui/x-internals/types';
+import { ChartPluginSignature } from '../../models';
+import { SeriesId } from '../../../../models/seriesType/common';
+import { UseChartSeriesSignature } from '../../corePlugins/useChartSeries';
+
+/**
+ * The data of the highlighted item.
+ * To highlight an item, you need to provide the series id and the item id.
+ * If targeting the whole series, you can omit the item id.
+ * To clear the highlight, set the value to an empty object.
+ *
+ * @example
+ * // Highlight the item with the series id 'london' and the item id 0.
+ * { seriesId: 'london', dataIndex: 0 }
+ *
+ * // Highlight the whole series with the series id 'london'.
+ * { seriesId: 'london' }
+ *
+ * // Clear the highlight.
+ * {}
+ */
+export type HighlightItemData = {
+ /**
+ * The series id of the highlighted item.
+ */
+ seriesId: SeriesId;
+ /**
+ * The index of the item in series data.
+ */
+ dataIndex?: number;
+};
+
+export interface UseChartHighlightInstance {
+ /**
+ * Remove all highlight.
+ */
+ clearHighlight: () => void;
+ /**
+ * Set the highlighted item.
+ * @param {HighlightItemData} item The item to highlight.
+ */
+ setHighlight: (item: HighlightItemData) => void;
+}
+
+export interface UseChartHighlightParameters {
+ /**
+ * The highlighted item.
+ * Used when the highlight is controlled.
+ */
+ highlightedItem?: HighlightItemData | null;
+ /**
+ * The callback fired when the highlighted item changes.
+ *
+ * @param {HighlightItemData | null} highlightedItem The newly highlighted item.
+ */
+ onHighlightChange?: (highlightedItem: HighlightItemData | null) => void;
+}
+
+export type UseChartHighlightDefaultizedParameters = DefaultizedProps<
+ UseChartHighlightParameters,
+ 'highlightedItem'
+>;
+
+export interface UseChartHighlightState {
+ highlight: {
+ /**
+ * The item currently highlighted.
+ */
+ item: HighlightItemData | null;
+ };
+}
+
+export type UseChartHighlightSignature = ChartPluginSignature<{
+ instance: UseChartHighlightInstance;
+ state: UseChartHighlightState;
+ params: UseChartHighlightParameters;
+ defaultizedParams: UseChartHighlightDefaultizedParameters;
+ modelNames: 'highlightedItem';
+ dependencies: [UseChartSeriesSignature];
+}>;
diff --git a/packages/x-charts/src/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.ts b/packages/x-charts/src/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.ts
index 2dca0b61cbbe5..a57cd95ac49b8 100644
--- a/packages/x-charts/src/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.ts
+++ b/packages/x-charts/src/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.ts
@@ -5,12 +5,41 @@ import { ChartItemIdentifier, ChartSeriesType } from '../../../../models/seriesT
export const useChartInteraction: ChartPlugin = ({ store }) => {
const cleanInteraction = useEventCallback(() => {
- store.update((prev) => ({
- ...prev,
- interaction: { ...prev.interaction, axis: { x: null, y: null }, item: null },
- }));
+ store.update((prev) => {
+ return {
+ ...prev,
+ interaction: { ...prev.interaction, axis: { x: null, y: null }, item: null },
+ };
+ });
});
+ const removeItemInteraction = useEventCallback(
+ (itemToRemove: ChartItemIdentifier) => {
+ store.update((prev) => {
+ const prevItem = prev.interaction.item;
+ if (
+ prevItem === null ||
+ Object.keys(itemToRemove).some(
+ (key) =>
+ itemToRemove[key as keyof typeof itemToRemove] !==
+ prevItem[key as keyof typeof prevItem],
+ )
+ ) {
+ // The item is already something else, no need to clean it.
+ return prev;
+ }
+
+ return {
+ ...prev,
+ interaction: {
+ ...prev.interaction,
+ item: null,
+ },
+ };
+ });
+ },
+ );
+
const setItemInteraction = useEventCallback((newItem: ChartItemIdentifier) => {
store.update((prev) => ({
...prev,
@@ -69,6 +98,7 @@ export const useChartInteraction: ChartPlugin = ({
instance: {
cleanInteraction,
setItemInteraction,
+ removeItemInteraction,
setAxisInteraction,
enableVoronoid,
disableVoronoid,
diff --git a/packages/x-charts/src/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.types.ts b/packages/x-charts/src/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.types.ts
index 76c97c2c803c8..ed5befb91a0f5 100644
--- a/packages/x-charts/src/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.types.ts
+++ b/packages/x-charts/src/internals/plugins/featurePlugins/useChartInteraction/useChartInteraction.types.ts
@@ -11,6 +11,11 @@ export interface UseChartInteractionInstance {
* @param {ChartItemIdentifier} newItem The identifier of the item.
*/
setItemInteraction: (newItem: ChartItemIdentifier) => void;
+ /**
+ * Remove item interaction if the current if the provided item is still the one interacting.
+ * @param {ChartItemIdentifier} itemToRemove The identifier of the item.
+ */
+ removeItemInteraction: (itemToRemove: ChartItemIdentifier) => void;
/**
* Set the new axis the user is interacting with.
* @param {Partial} newAxis The new axis identifier.
diff --git a/packages/x-charts/src/internals/store/useStore.ts b/packages/x-charts/src/internals/store/useStore.ts
index 9a82698b4b2ce..a86d046f69753 100644
--- a/packages/x-charts/src/internals/store/useStore.ts
+++ b/packages/x-charts/src/internals/store/useStore.ts
@@ -1,11 +1,12 @@
import { useChartContext } from '../../context/ChartProvider';
import { ChartStore } from '../plugins/utils/ChartStore';
-import { UseChartInteractionSignature } from '../plugins/featurePlugins/useChartInteraction/useChartInteraction.types';
+import { UseChartInteractionSignature } from '../plugins/featurePlugins/useChartInteraction';
+import { UseChartHighlightSignature } from '../plugins/featurePlugins/useChartHighlight';
import { ChartAnyPluginSignature } from '../plugins/models';
// This hook should be removed because user and us should not interact with the store directly, but with public/private APIs
export function useStore(): ChartStore<
- [...TSignatures, UseChartInteractionSignature]
+ [...TSignatures, UseChartInteractionSignature, UseChartHighlightSignature]
> {
const context = useChartContext();
diff --git a/packages/x-charts/src/models/seriesType/common.ts b/packages/x-charts/src/models/seriesType/common.ts
index 084d3d302dd1a..c3835bad2ff3b 100644
--- a/packages/x-charts/src/models/seriesType/common.ts
+++ b/packages/x-charts/src/models/seriesType/common.ts
@@ -1,5 +1,5 @@
import type { ChartsLabelMarkProps } from '../../ChartsLabel';
-import type { HighlightScope } from '../../context';
+import { HighlightScope } from '../../internals/plugins/featurePlugins/useChartHighlight/highlightConfig.types';
import type { StackOffsetType, StackOrderType } from '../stacking';
export type SeriesId = number | string;
diff --git a/scripts/x-charts-pro.exports.json b/scripts/x-charts-pro.exports.json
index a68e0179c5c42..6c4e484f3ca1b 100644
--- a/scripts/x-charts-pro.exports.json
+++ b/scripts/x-charts-pro.exports.json
@@ -184,17 +184,12 @@
{ "name": "HeatmapPlot", "kind": "Function" },
{ "name": "HeatmapTooltip", "kind": "Function" },
{ "name": "HeatmapTooltipProps", "kind": "Interface" },
- { "name": "HighlightedContext", "kind": "Variable" },
- { "name": "HighlightedProvider", "kind": "Function" },
- { "name": "HighlightedProviderProps", "kind": "TypeAlias" },
- { "name": "HighlightedState", "kind": "TypeAlias" },
{ "name": "HighlightElementClassKey", "kind": "TypeAlias" },
{ "name": "HighlightItemData", "kind": "TypeAlias" },
{ "name": "HighlightOptions", "kind": "TypeAlias" },
{ "name": "HighlightScope", "kind": "TypeAlias" },
{ "name": "isBarSeries", "kind": "Function" },
{ "name": "isDefaultizedBarSeries", "kind": "Function" },
- { "name": "ItemHighlightedState", "kind": "TypeAlias" },
{ "name": "labelClasses", "kind": "Variable" },
{ "name": "labelGradientClasses", "kind": "Variable" },
{ "name": "labelMarkClasses", "kind": "Variable" },
@@ -320,8 +315,8 @@
{ "name": "useChartId", "kind": "Function" },
{ "name": "useDrawingArea", "kind": "Function" },
{ "name": "useGaugeState", "kind": "Function" },
- { "name": "useHighlighted", "kind": "Function" },
{ "name": "useItemHighlighted", "kind": "Function" },
+ { "name": "useItemHighlightedGetter", "kind": "Function" },
{ "name": "useItemTooltip", "kind": "Function" },
{ "name": "UseItemTooltipReturnValue", "kind": "Interface" },
{ "name": "useLegend", "kind": "Function" },
diff --git a/scripts/x-charts.exports.json b/scripts/x-charts.exports.json
index d23e021e2ab54..870e8f14743fc 100644
--- a/scripts/x-charts.exports.json
+++ b/scripts/x-charts.exports.json
@@ -172,17 +172,12 @@
{ "name": "getPieCoordinates", "kind": "Function" },
{ "name": "getReferenceLineUtilityClass", "kind": "Function" },
{ "name": "getValueToPositionMapper", "kind": "Function" },
- { "name": "HighlightedContext", "kind": "Variable" },
- { "name": "HighlightedProvider", "kind": "Function" },
- { "name": "HighlightedProviderProps", "kind": "TypeAlias" },
- { "name": "HighlightedState", "kind": "TypeAlias" },
{ "name": "HighlightElementClassKey", "kind": "TypeAlias" },
{ "name": "HighlightItemData", "kind": "TypeAlias" },
{ "name": "HighlightOptions", "kind": "TypeAlias" },
{ "name": "HighlightScope", "kind": "TypeAlias" },
{ "name": "isBarSeries", "kind": "Function" },
{ "name": "isDefaultizedBarSeries", "kind": "Function" },
- { "name": "ItemHighlightedState", "kind": "TypeAlias" },
{ "name": "labelClasses", "kind": "Variable" },
{ "name": "labelGradientClasses", "kind": "Variable" },
{ "name": "labelMarkClasses", "kind": "Variable" },
@@ -304,8 +299,8 @@
{ "name": "useChartId", "kind": "Function" },
{ "name": "useDrawingArea", "kind": "Function" },
{ "name": "useGaugeState", "kind": "Function" },
- { "name": "useHighlighted", "kind": "Function" },
{ "name": "useItemHighlighted", "kind": "Function" },
+ { "name": "useItemHighlightedGetter", "kind": "Function" },
{ "name": "useItemTooltip", "kind": "Function" },
{ "name": "UseItemTooltipReturnValue", "kind": "Interface" },
{ "name": "useLegend", "kind": "Function" },