import {ModelEvent} from "@bokehjs/core/bokeh_events" import {div} from "@bokehjs/core/dom" import type * as p from "@bokehjs/core/properties" import type {Attrs} from "@bokehjs/core/types" import {serializeEvent} from "./event-to-object" import {HTMLBox, HTMLBoxView} from "./layout" const mouse_events = [ "click", "dblclick", "mousedown", "mousemove", "mouseup", "mouseover", "mouseout", "globalout", "contextmenu", ] const events = [ "highlight", "downplay", "selectchanged", "legendselectchangedEvent", "legendselected", "legendunselected", "legendselectall", "legendinverseselect", "legendscroll", "datazoom", "datarangeselected", "timelineplaychanged", "restore", "dataviewchanged", "magictypechanged", "geoselectchanged", "geoselected", "geounselected", "axisareaselected", "brush", "brushEnd", "rushselected", "globalcursortaken", "rendered", "finished", ] const all_events = mouse_events.concat(events) export class EChartsEvent extends ModelEvent { constructor(readonly type: string, readonly data: any, readonly query: string) { super() } protected override get event_values(): Attrs { return {model: this.origin, type: this.type, data: this.data, query: this.query} } static { this.prototype.event_name = "echarts_event" } } export class EChartsView extends HTMLBoxView { declare model: ECharts container: Element _chart: any _callbacks: Array[] = [] override connect_signals(): void { super.connect_signals() const {width, height, renderer, theme, event_config, js_events, data} = this.model.properties this.on_change(data, () => this._plot()) this.on_change([width, height], () => this._resize()) this.on_change([theme, renderer], () => { this.render() this._chart.resize() }) this.on_change([event_config, js_events], () => this._subscribe()) } override render(): void { if (this._chart != null) { (window as any).echarts.dispose(this._chart) } super.render() this.container = div({style: {height: "100%", width: "100%"}}) const config = {width: this.model.width, height: this.model.height, renderer: this.model.renderer} this._chart = (window as any).echarts.init( this.container, this.model.theme, config, ) this._plot() this._subscribe() this.shadow_el.append(this.container) } override remove(): void { super.remove() if (this._chart != null) { (window as any).echarts.dispose(this._chart) } } override after_layout(): void { super.after_layout() this._chart.resize() } _plot(): void { if ((window as any).echarts == null) { return } this._chart.setOption(this.model.data, this.model.options) } _resize(): void { this._chart.resize({width: this.model.width, height: this.model.height}) } _subscribe(): void { if ((window as any).echarts == null) { return } for (const [event_type, callback] of this._callbacks) { this._chart.off(event_type, callback) } this._callbacks = [] for (const event_type in this.model.event_config) { if (!all_events.includes(event_type)) { console.warn(`Could not subscribe to unknown Echarts event: ${event_type}.`) continue } const queries = this.model.event_config[event_type] for (const query of queries) { const callback = (event: any) => { const processed = {...event} processed.event = serializeEvent(event.event?.event) const serialized = JSON.parse(JSON.stringify(processed)) this.model.trigger_event(new EChartsEvent(event_type, serialized, query)) } if (query == null) { this._chart.on(event_type, query, callback) } else { this._chart.on(event_type, callback) } this._callbacks.push([event_type, callback]) } } for (const event_type in this.model.js_events) { if (!all_events.includes(event_type)) { console.warn(`Could not subscribe to unknown Echarts event: ${event_type}.`) continue } const handlers = this.model.js_events[event_type] for (const handler of handlers) { const callback = (event: any) => { handler.callback.execute(this._chart, event) } if ("query" in handler) { this._chart.on(event_type, handler.query, callback) } else { this._chart.on(event_type, callback) } this._callbacks.push([event_type, callback]) } } } } export namespace ECharts { export type Attrs = p.AttrsOf export type Props = HTMLBox.Props & { data: p.Property options: p.Property event_config: p.Property js_events: p.Property renderer: p.Property theme: p.Property } } export interface ECharts extends ECharts.Attrs {} export class ECharts extends HTMLBox { declare properties: ECharts.Props constructor(attrs?: Partial) { super(attrs) } static override __module__ = "panel.models.echarts" static { this.prototype.default_view = EChartsView this.define(({Any, Str}) => ({ data: [ Any, {} ], options: [ Any, {} ], event_config: [ Any, {} ], js_events: [ Any, {} ], theme: [ Str, "default"], renderer: [ Str, "canvas"], })) } }