183 lines
5.4 KiB
TypeScript
183 lines
5.4 KiB
TypeScript
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<any>[] = []
|
|
|
|
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<Props>
|
|
export type Props = HTMLBox.Props & {
|
|
data: p.Property<any>
|
|
options: p.Property<any>
|
|
event_config: p.Property<any>
|
|
js_events: p.Property<any>
|
|
renderer: p.Property<string>
|
|
theme: p.Property<string>
|
|
}
|
|
}
|
|
|
|
export interface ECharts extends ECharts.Attrs {}
|
|
|
|
export class ECharts extends HTMLBox {
|
|
declare properties: ECharts.Props
|
|
|
|
constructor(attrs?: Partial<ECharts.Attrs>) {
|
|
super(attrs)
|
|
}
|
|
|
|
static override __module__ = "panel.models.echarts"
|
|
|
|
static {
|
|
this.prototype.default_view = EChartsView
|
|
|
|
this.define<ECharts.Props>(({Any, Str}) => ({
|
|
data: [ Any, {} ],
|
|
options: [ Any, {} ],
|
|
event_config: [ Any, {} ],
|
|
js_events: [ Any, {} ],
|
|
theme: [ Str, "default"],
|
|
renderer: [ Str, "canvas"],
|
|
}))
|
|
}
|
|
}
|