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

feat(theme): support design token #20717

Draft
wants to merge 2 commits into
base: v6
Choose a base branch
from
Draft
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
141 changes: 137 additions & 4 deletions src/core/echarts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ import {
ScaleDataValue,
ZRElementEventName,
ECElementEvent,
AnimationOption
AnimationOption,
TokenOption
} from '../util/types';
import Displayable from 'zrender/src/graphic/Displayable';
import { seriesSymbolTask, dataSymbolTask } from '../visual/symbol';
Expand All @@ -134,6 +135,7 @@ import { platformApi, setPlatformAPI } from 'zrender/src/core/platform';
import { getImpl } from './impl';
import type geoSourceManager from '../coord/geo/geoSourceManager';
import {registerCustomSeries as registerCustom} from '../chart/custom/customSeriesRegister';
import { DEFAULT_TOKENS } from '../theme/tokens';

declare let global: any;

Expand Down Expand Up @@ -212,6 +214,12 @@ type ConnectStatus =
| typeof CONNECT_STATUS_UPDATING
| typeof CONNECT_STATUS_UPDATED;

export interface SetTokenOpts {
notMerge?: boolean;
lazyUpdate?: boolean;
silent?: boolean;
}

export type SetOptionTransitionOpt = UpdateLifecycleTransitionOpt;
export type SetOptionTransitionOptItem = UpdateLifecycleTransitionItem;

Expand Down Expand Up @@ -398,6 +406,8 @@ class ECharts extends Eventful<ECEventDefinition> {
private [CONNECT_STATUS_KEY]: ConnectStatus;
private [STATUS_NEEDS_UPDATE_KEY]: boolean;

private _tokens: TokenOption;

constructor(
dom: HTMLElement,
// Theme name or themeOption.
Expand Down Expand Up @@ -589,6 +599,122 @@ class ECharts extends Eventful<ECEventDefinition> {
return this._ssr;
}

setToken(tokens: TokenOption): void;
setToken(tokens: TokenOption, notMerge?: boolean, lazyUpdate?: boolean): void;
setToken(tokens: TokenOption, opts?: SetTokenOpts): void;
/* eslint-disable-next-line */
setToken(tokens: TokenOption, notMerge?: boolean | SetTokenOpts, lazyUpdate?: boolean): void {
if (this[IN_MAIN_PROCESS_KEY]) {
if (__DEV__) {
error('`setToken` should not be called during main process.');
}
return;
}

if (this._disposed) {
disposedWarning(this.id);
return;
}

let silent;
if (isObject(notMerge)) {
lazyUpdate = notMerge.lazyUpdate;
silent = notMerge.silent;
notMerge = notMerge.notMerge;
}

this[IN_MAIN_PROCESS_KEY] = true;

if (!this._model || notMerge) {
this._tokens = tokens;
this[IN_MAIN_PROCESS_KEY] = false;
return;
}

try {
if (notMerge) {
this._tokens = tokens;
}
else {
// Merge tokens
this._tokens = this._tokens || {};
each(tokens, (value, key) => {
if (isObject(value)) {
this._tokens[key] = this._tokens[key] || {};
extend(this._tokens[key], value);
}
else {
this._tokens[key] = value;
}
});
}
}
catch (e) {
this[IN_MAIN_PROCESS_KEY] = false;
throw e;
}

const updateParams = {
optionChanged: false
} as UpdateLifecycleParams;

if (lazyUpdate) {
this[PENDING_UPDATE] = {
silent: silent,
updateParams: updateParams
};
this[IN_MAIN_PROCESS_KEY] = false;

this.getZr().wakeUp();
}
else {
try {
prepare(this);
updateMethods.update.call(this, null, updateParams);
}
catch (e) {
this[PENDING_UPDATE] = null;
this[IN_MAIN_PROCESS_KEY] = false;
throw e;
}
}

if (!this._ssr) {
this._zr.flush();
}

this[PENDING_UPDATE] = null;
this[IN_MAIN_PROCESS_KEY] = false;

flushPendingActions.call(this, silent);
triggerUpdatedEvent.call(this, silent);
}

getToken(): TokenOption;
getToken(path: string): unknown;
getToken(path?: string): TokenOption | unknown {
if (this._disposed) {
disposedWarning(this.id);
return;
}

if (!path) {
return clone(this._tokens || {});
}

// Use type assertion for the nested access
let result: unknown = this._tokens;
const pathParts = path.split('.');
for (let i = 0; i < pathParts.length; i++) {
if (!result || typeof result !== 'object') {
return;
}
result = (result as Record<string, unknown>)[pathParts[i]];
}

return result;
}

/**
* Usage:
* chart.setOption(option, notMerge, lazyUpdate);
Expand Down Expand Up @@ -2832,7 +2958,14 @@ export function getInstanceById(key: string): EChartsType | undefined {
/**
* Register theme
*/
export function registerTheme(name: string, theme: ThemeOption): void {
export function registerTheme(
name: string,
theme: ThemeOption,
tokens?: TokenOption
): void {
if (tokens) {
theme.designTokens = tokens;
}
themeStorage[name] = theme;
}

Expand Down Expand Up @@ -3134,8 +3267,8 @@ registerAction({
}, noop);

// Default theme
registerTheme('light', lightTheme);
registerTheme('dark', darkTheme);
registerTheme('light', lightTheme, DEFAULT_TOKENS);
registerTheme('dark', darkTheme, DEFAULT_TOKENS);

// For backward compatibility, where the namespace `dataTool` will
// be mounted on `echarts` is the extension `dataTool` is imported.
Expand Down
37 changes: 37 additions & 0 deletions src/theme/tokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { TokenOption } from '../util/types';

/**
* Default design tokens
*/
export const DEFAULT_TOKENS: TokenOption = {
// Colors
color: {
// Testing colors
border: '#f00',
},

// Spacing
spacing: {
base: 4,
small: 8,
medium: 16,
large: 24
},

// Border radius
borderRadius: {
small: 2,
base: 4,
large: 8
},

// Font sizes
fontSize: {
sm: 12,
base: 14,
lg: 16,
xl: 20
}
};

export type DesignTokens = typeof DEFAULT_TOKENS;
56 changes: 56 additions & 0 deletions src/util/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,62 @@ export type SeriesDataType = 'main' | 'node' | 'edge';
// echarts option types (base and common part)
// --------------------------------------------

export interface TokenOption {
color?: {
// General colors
accent0?: string;
accent1?: string;
accent2?: string;
accent3?: string;
accent4?: string;
accent5?: string;
accent6?: string;
accent7?: string;
accent8?: string;

border?: string;
borderTint?: string;
borderShade?: string;

// Chart colors
chartBg?: string;

// Component colors
axisLine?: string;
axisTick?: string;
axisLabel?: string;
splitLine?: string;
splitArea?: string;
titleText?: string;
markText?: string;
tooltipBg?: string;
tooltipBorder?: string;
tooltipText?: string;

// Series colors
},

spacing?: {
base?: number;
small?: number;
medium?: number;
large?: number;
},

borderRadius?: {
small?: number;
base?: number;
large?: number;
},

fontSize?: {
sm?: number;
base?: number;
lg?: number;
xl?: number;
}
}

/**
* [ECUnitOption]:
* An object that contains definitions of components
Expand Down