From d6602cc424472bf4aaa797dd24268cf50862b275 Mon Sep 17 00:00:00 2001 From: Ovilia Date: Fri, 24 Jan 2025 19:05:47 +0800 Subject: [PATCH 1/2] feat(token): add token API --- src/core/echarts.ts | 137 +++++++++++++++++++++++++++++++++++++++++++- src/util/types.ts | 36 ++++++++++++ 2 files changed, 171 insertions(+), 2 deletions(-) diff --git a/src/core/echarts.ts b/src/core/echarts.ts index 0d5a5087f6..0cb6ad3d7c 100644 --- a/src/core/echarts.ts +++ b/src/core/echarts.ts @@ -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'; @@ -212,6 +213,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; @@ -398,6 +405,8 @@ class ECharts extends Eventful { private [CONNECT_STATUS_KEY]: ConnectStatus; private [STATUS_NEEDS_UPDATE_KEY]: boolean; + private _tokens: TokenOption; + constructor( dom: HTMLElement, // Theme name or themeOption. @@ -589,6 +598,123 @@ class ECharts extends Eventful { 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; + let replaceMerge; + 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)[pathParts[i]]; + } + + return result; + } + /** * Usage: * chart.setOption(option, notMerge, lazyUpdate); @@ -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; } diff --git a/src/util/types.ts b/src/util/types.ts index 1d085c198f..c37a4f83a0 100644 --- a/src/util/types.ts +++ b/src/util/types.ts @@ -486,6 +486,42 @@ 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 + } +} + /** * [ECUnitOption]: * An object that contains definitions of components From 0b38268dfe34840c7136338ef543d8ecca5929a2 Mon Sep 17 00:00:00 2001 From: Ovilia Date: Wed, 5 Feb 2025 15:35:04 +0800 Subject: [PATCH 2/2] feat: add default tokens --- src/core/echarts.ts | 6 +++--- src/theme/tokens.ts | 37 +++++++++++++++++++++++++++++++++++++ src/util/types.ts | 20 ++++++++++++++++++++ 3 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 src/theme/tokens.ts diff --git a/src/core/echarts.ts b/src/core/echarts.ts index 0cb6ad3d7c..65f5adbff2 100644 --- a/src/core/echarts.ts +++ b/src/core/echarts.ts @@ -135,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; @@ -616,7 +617,6 @@ class ECharts extends Eventful { } let silent; - let replaceMerge; if (isObject(notMerge)) { lazyUpdate = notMerge.lazyUpdate; silent = notMerge.silent; @@ -3267,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. diff --git a/src/theme/tokens.ts b/src/theme/tokens.ts new file mode 100644 index 0000000000..47dfc73f32 --- /dev/null +++ b/src/theme/tokens.ts @@ -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; diff --git a/src/util/types.ts b/src/util/types.ts index c37a4f83a0..66e7165c2c 100644 --- a/src/util/types.ts +++ b/src/util/types.ts @@ -519,6 +519,26 @@ export interface TokenOption { 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; } }