Skip to content
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

[charts] Document plugins for internal use #16504

Merged
merged 4 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 0 additions & 34 deletions packages/x-charts/src/hooks/useChartDimensions.ts

This file was deleted.

90 changes: 84 additions & 6 deletions packages/x-charts/src/internals/plugins/models/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,34 @@ import { ChartStore } from '../utils/ChartStore';
import { ChartSeriesConfig } from './seriesConfig';

export interface ChartPluginOptions<TSignature extends ChartAnyPluginSignature> {
/**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you can add those to the tree view after this PR is approved it would be awesome 🙏

* An imperative api available for internal use.
*/
instance: ChartUsedInstance<TSignature>;
/**
* The parameters after being processed with the default values.
*/
params: ChartUsedDefaultizedParams<TSignature>;
/**
* The store of controlled properties.
* If they are not controlled by the user, they will be initialized by the plugin.
*/
models: ChartUsedControlModels<TSignature>;
/**
* The store that can be used to access the state of other plugins.
*/
store: ChartUsedStore<TSignature>;
/**
* Reference to the main svg element.
*/
svgRef: React.RefObject<SVGSVGElement | null>;
/**
* All the plugins that are used in the chart.
*/
plugins: ChartPlugin<ChartAnyPluginSignature>[];
/**
* All the series configurations that are currently loaded.
*/
seriesConfig: ChartSeriesConfig<any>;
}

Expand Down Expand Up @@ -40,34 +62,60 @@ export type ChartPluginSignature<
optionalDependencies?: readonly ChartAnyPluginSignature[];
},
> = {
/**
* The raw properties that can be passed to the plugin.
*/
params: T extends { params: {} } ? T['params'] : {};
/**
* The params after being processed with the default values.
*/
defaultizedParams: T extends { defaultizedParams: {} } ? T['defaultizedParams'] : {};
/**
* An imperative api available for internal use.
*/
instance: T extends { instance: {} } ? T['instance'] : {};
/**
* The state is the mutable data that will actually be stored in the plugin state and can be accessed by other plugins.
*/
state: T extends { state: {} } ? T['state'] : {};
/**
* The public imperative API that will be exposed to the user.
* Accessed through the `apiRef` property of the plugin.
*/
publicAPI: T extends { publicAPI: {} } ? T['publicAPI'] : {};
/**
* A helper for controlled properties.
* Properties defined here can be controlled by the user. If they are not controlled, they will be initialized by the plugin.
*/
models: T extends { defaultizedParams: {}; modelNames: keyof T['defaultizedParams'] }
? {
[TControlled in T['modelNames']]-?: ChartControlModel<
Exclude<T['defaultizedParams'][TControlled], undefined>
>;
}
: {};
/**
* Any plugins that this plugin depends on.
*/
dependencies: T extends { dependencies: Array<any> } ? T['dependencies'] : [];
/**
* Same as dependencies but the plugin might not have been initialized.
*/
optionalDependencies: T extends { optionalDependencies: Array<any> }
? T['optionalDependencies']
: [];
};

export type ChartAnyPluginSignature = {
state: any;
instance: any;
export type ChartAnyPluginSignature = ChartPluginSignature<{
params: any;
models: any;
defaultizedParams: any;
instance: any;
publicAPI: any;
state: any;
modelNames: any;
dependencies: any;
optionalDependencies: any;
publicAPI: any;
};
}>;

type ChartRequiredPlugins<TSignature extends ChartAnyPluginSignature> = [
...ChartCorePluginSignatures,
Expand Down Expand Up @@ -118,14 +166,44 @@ export type ChartUsedStore<TSignature extends ChartAnyPluginSignature> = ChartSt
>;

export type ChartPlugin<TSignature extends ChartAnyPluginSignature> = {
/**
* The main function of the plugin that will be executed by the chart.
*
* This should be a valid React `use` function, as it will be executed in the render phase and can contain hooks.
*/
(options: ChartPluginOptions<TSignature>): ChartResponse<TSignature>;
/**
* The initial state is computed after the default values are applied.
* It set up the state for the first render.
* Other state modifications have to be done in effects and so could not be applied on the initial render.
*
* @param {ChartUsedDefaultizedParams<TSignature>} params The parameters after being processed with the default values.
* @param {MergeSignaturesProperty<ChartRequiredPlugins<TSignature>, 'state'>} currentState The current state of the chart.
* @param {ChartSeriesConfig<any>} seriesConfig The series configuration.
*
* @returns {TSignature['state']} The initial state of the plugin.
*/
getInitialState?: (
params: ChartUsedDefaultizedParams<TSignature>,
currentState: MergeSignaturesProperty<ChartRequiredPlugins<TSignature>, 'state'>,
seriesConfig: ChartSeriesConfig<any>,
) => TSignature['state'];
/**
* The configuration of properties that can be controlled by the user.
* If they are not controlled, they will be initialized by the plugin.
*/
models?: ChartControlModelsInitializer<TSignature>;
/**
* An object where each property used by the plugin is set to `true`.
*/
params: Record<keyof TSignature['params'], true>;
/**
* A function that receives the parameters and returns the parameters after being processed with the default values.
*
* @param {ChartUsedParams<TSignature>} options The options object.
* @param {ChartUsedParams<TSignature>['params']} options.params The parameters before being processed with the default values.
* @returns {TSignature['defaultizedParams']} The parameters after being processed with the default values.
*/
getDefaultizedParams?: (options: {
params: ChartUsedParams<TSignature>;
}) => TSignature['defaultizedParams'];
Expand Down
41 changes: 25 additions & 16 deletions packages/x-charts/src/internals/store/useCharts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,17 @@ import { ChartSeriesType } from '../../models/seriesType/config';
import { ChartSeriesConfig } from '../plugins/models/seriesConfig';
import { useChartModels } from './useChartModels';

export function useChartApiInitialization<T>(
inputApiRef: React.RefObject<T | undefined> | undefined,
): T {
const fallbackPublicApiRef = React.useRef({}) as React.RefObject<T>;

if (inputApiRef) {
if (inputApiRef.current == null) {
// eslint-disable-next-line react-compiler/react-compiler
inputApiRef.current = {} as T;
}
return inputApiRef.current;
}

return fallbackPublicApiRef.current;
}

let globalId = 0;

/**
* This is the main hook that setups the plugin system for the chart.
*
* It manages the data used to create the charts.
*
* @param inPlugins All the plugins that will be used in the chart.
* @param props The props passed to the chart.
* @param seriesConfig The set of helpers used for series-specific computation.
*/
export function useCharts<
TSeriesType extends ChartSeriesType,
TSignatures extends readonly ChartAnyPluginSignature[],
Expand Down Expand Up @@ -131,3 +124,19 @@ export function useCharts<

return { contextValue };
}

export function useChartApiInitialization<T>(
inputApiRef: React.RefObject<T | undefined> | undefined,
): T {
const fallbackPublicApiRef = React.useRef({}) as React.RefObject<T>;

if (inputApiRef) {
if (inputApiRef.current == null) {
// eslint-disable-next-line react-compiler/react-compiler
inputApiRef.current = {} as T;
}
return inputApiRef.current;
}

return fallbackPublicApiRef.current;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
const is = Object.is;

/**
* Fast shallow compare for objects.
* @returns true if objects are equal.
*/
export function fastObjectShallowCompare<T extends Record<string, any> | null>(a: T, b: T) {
if (a === b) {
return true;
Expand Down