diff --git a/source/component/ECharts/Chart.tsx b/source/component/ECharts/Chart.tsx index ba82c08..dd86ff7 100644 --- a/source/component/ECharts/Chart.tsx +++ b/source/component/ECharts/Chart.tsx @@ -9,6 +9,8 @@ import { ChartType, ECChartOptionName, ECComponentOptionName, + ZRElementEventHandler, + ZRElementEventName, loadChart, loadComponent, loadRenderer, @@ -19,7 +21,9 @@ export class EChartsElement extends HTMLElement implements CustomElement { #data: EChartsOption = {}; #type: ChartType; #core?: ECharts; - #buffer = []; + #eventHandlerBuffer: [ZRElementEventName, string, ZRElementEventHandler][] = + []; + #eventDataBuffer = []; toJSON() { return this.#core?.getOption(); @@ -60,14 +64,19 @@ export class EChartsElement extends HTMLElement implements CustomElement { this.setOption(this.#data); - for (const option of this.#buffer) this.setOption(option); + for (const [event, selector, handler] of this.#eventHandlerBuffer) + this.onChild(event, selector, handler); - this.#buffer.length = 0; + this.#eventHandlerBuffer.length = 0; + + for (const option of this.#eventDataBuffer) this.setOption(option); + + this.#eventDataBuffer.length = 0; } async setOption(data: EChartsOption) { if (!this.#core) { - this.#buffer.push(data); + this.#eventDataBuffer.push(data); return; } @@ -98,6 +107,15 @@ export class EChartsElement extends HTMLElement implements CustomElement { this.setOption(this.#data); } + + onChild( + event: ZRElementEventName, + selector: string, + handler: ZRElementEventHandler + ) { + if (this.#core) this.#core.on(event, selector, handler); + else this.#eventHandlerBuffer.push([event, selector, handler]); + } } customElements.define('ec-chart', EChartsElement); diff --git a/source/component/ECharts/Option.tsx b/source/component/ECharts/Option.tsx index 13c8ca3..8d96d65 100644 --- a/source/component/ECharts/Option.tsx +++ b/source/component/ECharts/Option.tsx @@ -1,11 +1,17 @@ import { JsxProps } from 'dom-renderer'; import { EChartsOption } from 'echarts'; +import { ECElementEvent } from 'echarts/core'; import { CustomElement, toCamelCase, toHyphenCase } from 'web-utility'; + +import { EChartsElement } from './Chart'; import { BUILTIN_CHARTS_MAP, BUITIN_COMPONENTS_MAP, ECChartOptionName, ECComponentOptionName, + EventKeyPattern, + ZRElementEventHandler, + ZRElementEventName, proxyPrototype } from './utility'; @@ -36,6 +42,12 @@ export abstract class ECOptionElement } connectedCallback() { + for (const [key, value] of Object.entries(this.#data)) + if (EventKeyPattern.test(key) && typeof value === 'function') + this.on( + key.slice(2) as ZRElementEventName, + value as ZRElementEventHandler + ); this.update(); } @@ -50,6 +62,10 @@ export abstract class ECOptionElement if (value) super.setAttribute(key, key + ''); else super.removeAttribute(key); break; + case 'function': + if (EventKeyPattern.test(key)) + this.on(key.slice(2) as ECElementEvent['type'], value); + break; default: super.setAttribute(key, value + ''); } @@ -71,6 +87,15 @@ export abstract class ECOptionElement ); } + on(event: ZRElementEventName, handler: ZRElementEventHandler) { + if (this.isConnected) + this.closest('ec-chart')?.onChild( + event, + [this.chartTagName, this['type']].filter(Boolean).join('.'), + handler + ); + } + setAttribute(key: string, value: string) { super.setAttribute(key, value); @@ -89,12 +114,15 @@ for (const name of Object.keys({ class extends ECOptionElement {} ); +type HyphenCase = T extends `${infer L}${infer R}` + ? `${L extends Uppercase ? `-${Lowercase}` : L}${HyphenCase}` + : T; type PickSingle = T extends infer S | (infer S)[] ? S : T; type ECOptionElements = { [K in | ECComponentOptionName - | ECChartOptionName as `ec-${K}`]: JsxProps & + | ECChartOptionName as `ec-${HyphenCase}`]: JsxProps & PickSingle; }; diff --git a/source/component/ECharts/utility.ts b/source/component/ECharts/utility.ts index 9d1a8fc..0c4c609 100644 --- a/source/component/ECharts/utility.ts +++ b/source/component/ECharts/utility.ts @@ -1,4 +1,4 @@ -import { use } from 'echarts/core'; +import { ECElementEvent, use } from 'echarts/core'; import memoize from 'lodash.memoize'; import { IndexKey } from 'web-utility'; @@ -27,6 +27,11 @@ export function proxyPrototype( Object.setPrototypeOf(target, prototypeProxy); } +export const EventKeyPattern = /^on(\w+)/; + +export type ZRElementEventName = ECElementEvent['type']; +export type ZRElementEventHandler = (event: ECElementEvent) => boolean | void; + /** * @see {@link https://github.com/apache/echarts/blob/031a908fafaa57e2277b2f720087195925ec38cf/src/model/Global.ts#L83-L111} */ diff --git a/source/page/Map/index.tsx b/source/page/Map/index.tsx index 35fe267..aba8581 100644 --- a/source/page/Map/index.tsx +++ b/source/page/Map/index.tsx @@ -58,8 +58,8 @@ export default class MapsPage extends HTMLElement implements CustomElement { const { loading, virusData } = this; return ( - - + + ); }