From ec90fe46a3f592c78a36ce9af1c4bfdb2e603f81 Mon Sep 17 00:00:00 2001 From: Mateusz Paprocki Date: Mon, 12 Feb 2024 01:43:02 +0100 Subject: [PATCH] Statically type vega.ts --- panel/models/vega.ts | 138 +++++++++++++++++++++---------------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/panel/models/vega.ts b/panel/models/vega.ts index 7230322b548..b3fde68a050 100644 --- a/panel/models/vega.ts +++ b/panel/models/vega.ts @@ -1,9 +1,12 @@ import {div} from "@bokehjs/core/dom" import * as p from "@bokehjs/core/properties" import {ModelEvent} from "@bokehjs/core/bokeh_events" -import {isArray} from "@bokehjs/core/util/types" +import {DictLike as Dict, PlainObject} from "@bokehjs/core/types" +import {isArray, isPlainObject} from "@bokehjs/core/util/types" import {LayoutDOM, LayoutDOMView} from "@bokehjs/models/layouts/layout_dom" -import {keys, to_object} from "@bokehjs/core/util/object" +import {ColumnDataSource} from "@bokehjs/models/sources/column_data_source" +import {dict, entries, is_empty} from "@bokehjs/core/util/object" +import {Enum} from "@bokehjs/core/kinds" import type {Attrs} from "@bokehjs/core/types" @@ -11,22 +14,18 @@ import {set_size} from "./layout" import {debounce} from "debounce" +const Theme = Enum("excel", "ggplot2", "quartz", "vox", "fivethirtyeight", "dark", "latimes", "urbaninstitute", "googlecharts") +type Theme = typeof Theme["__type__"] -function convert_nested_map_to_object(value: Map | Record | any[]): any { - if (value instanceof Map) { - const obj: Record = {} - value.forEach((v, k) => {obj[k] = convert_nested_map_to_object(v)}) - return obj - } else if (Array.isArray(value)) { - return value.map(convert_nested_map_to_object) - } else if (typeof value === 'object' && value !== null) { - const obj: Record = {} - for (const key in value) { - if (Object.prototype.hasOwnProperty.call(value, key)) { - obj[key] = convert_nested_map_to_object(value[key]) - } +function convert_nested_map_to_object(value: unknown): unknown { + if (value instanceof Map || isPlainObject(value)) { + const obj: PlainObject = {} + for (const [key, val] of entries(value)) { + obj[key] = convert_nested_map_to_object(val) } return obj + } else if (isArray(value)) { + return value.map(convert_nested_map_to_object) } else { return value } @@ -48,12 +47,12 @@ export class VegaEvent extends ModelEvent { export class VegaPlotView extends LayoutDOMView { model: VegaPlot - vega_view: any + vega_view: VegaView | null = null container: HTMLDivElement _callbacks: string[] _connected: string[] - _replot: any - _resize: any + _replot: ReturnType + _resize: ReturnType _rendered: boolean = false connect_signals(): void { @@ -66,12 +65,13 @@ export class VegaPlotView extends LayoutDOMView { this.connect(this.model.properties.data_sources.change, () => this._connect_sources()) this.connect(this.model.properties.events.change, () => { for (const event of this.model.events) { - if (this._callbacks.indexOf(event) > -1) + if (this._callbacks.includes(event)) { continue + } this._callbacks.push(event) - const callback = (name: string, value: any) => this._dispatch_event(name, value) - const timeout = this.model.throttle[event] || 20 - this.vega_view.addSignalListener(event, debounce(callback, timeout, false)) + const callback = (name: string, value: unknown) => this._dispatch_event(name, value) + const timeout = dict(this.model.throttle).get(event) ?? 20 + this.vega_view?.addSignalListener(event, debounce(callback, timeout, false)) } }) this._connected = [] @@ -79,10 +79,8 @@ export class VegaPlotView extends LayoutDOMView { } _connect_sources(): void { - const mds = to_object(this.model.data_sources) - for (const ds in mds) { - const cds = mds[ds] - if (this._connected.indexOf(ds) < 0) { + for (const [ds, cds] of dict(this.model.data_sources)) { + if (!this._connected.includes(ds)) { this.connect(cds.properties.data.change, () => this._replot()) this._connected.push(ds) } @@ -90,9 +88,7 @@ export class VegaPlotView extends LayoutDOMView { } override remove(): void { - if (this.vega_view) { - this.vega_view.finalize() - } + this.vega_view?.finalize() super.remove() } @@ -104,29 +100,26 @@ export class VegaPlotView extends LayoutDOMView { indexes.push(index._vgsid_); } else { // If "_vgsid_" property doesn't exist // Iterate through all properties in the "index" object - for (const key in index) { - if (index.hasOwnProperty(key)) { // To ensure key comes from "index" object itself, not its prototype - indexes.push({[key]: index[key]}); // Push a new object with this key-value pair into the array - } + for (const [key, val] of entries(index)) { + indexes.push({[key]: val}) // Push a new object with this key-value pair into the array } } } value = indexes } - this.model.trigger_event(new VegaEvent({type: name, value: value})) + this.model.trigger_event(new VegaEvent({type: name, value})) } - _fetch_datasets() { - const datasets: any = {} - const mds = to_object(this.model.data_sources) - for (const ds in mds) { - const cds = mds[ds]; - const data: any = [] + _fetch_datasets(): PlainObject[]> { + const datasets: PlainObject[]> = {} + for (const [ds, cds] of entries(this.model.data_sources)) { + const data: PlainObject[] = [] const columns = cds.columns() - for (let i = 0; i < cds.get_length(); i++) { - const item: any = {} + const n = cds.get_length() ?? 0 + for (let i = 0; i < n; i++) { + const item: PlainObject = {} for (const column of columns) { - item[column] = cds.data[column][i] + item[column] = cds.get(column)[i] } data.push(item) } @@ -150,40 +143,47 @@ export class VegaPlotView extends LayoutDOMView { } _plot(): void { - const data = this.model.data - if ((data == null) || !(window as any).vegaEmbed) + const {data: data_, data_sources} = this.model + if (data_ == null || typeof vegaEmbed == "undefined") { return - if (this.model.data_sources && (keys(this.model.data_sources).length > 0)) { - const datasets = this._fetch_datasets() - if ('data' in datasets) { - data.get('data').set('values', datasets['data']) - delete datasets['data'] + } + const data = dict(data_) + if (!is_empty(data_sources)) { + const datasets = dict(this._fetch_datasets()) + if (datasets.has("data")) { + const data_data = data.get("data") + if (isPlainObject(data_data) || data_data instanceof Map) { + dict(data_data).set("values", datasets.get("data")) + } + datasets.delete("data") } if (data.has('data')) { - const data_data = to_object(data.get('data')) + const data_data = convert_nested_map_to_object(data.get('data')) const is_array = isArray(data_data) const data_objs = is_array ? data_data : [data_data] as any for (const d of data_objs) { - if (d.name in datasets) { - d['values'] = datasets[d.name] - delete datasets[d.name] + const val = datasets.get(d.name) + if (val !== undefined) { + d['values'] = val + datasets.delete(d.name) } } - this.model.data.set('data', is_array ? data_objs : data_objs[0]) + data.set('data', is_array ? data_objs : data_objs[0]) } - this.model.data.set('datasets', datasets) + data.set('datasets', datasets) } - const config: any = {actions: this.model.show_actions, theme: this.model.theme}; - const embed_data = convert_nested_map_to_object(this.model.data); - - (window as any).vegaEmbed(this.container, embed_data, config).then((result: any) => { - this.vega_view = result.view - this._resize = debounce(() => this.resize_view(result.view), 50) - const callback = (name: string, value: any) => this._dispatch_event(name, value) + const config = {actions: this.model.show_actions, theme: this.model.theme}; + const embed_data = convert_nested_map_to_object(data); + + vegaEmbed(this.container, embed_data, config).then(({view}) => { + this.vega_view = view + this._resize = debounce(() => this.resize_view(view), 50) + const callback = (name: string, value: unknown) => this._dispatch_event(name, value) + const throttle = dict(this.model.throttle) for (const event of this.model.events) { this._callbacks.push(event) - const timeout = this.model.throttle[event] || 20 - this.vega_view.addSignalListener(event, debounce(callback, timeout, false)) + const timeout = throttle.get(event) ?? 20 + this.vega_view?.addSignalListener(event, debounce(callback, timeout, false)) } }) } @@ -210,12 +210,12 @@ export class VegaPlotView extends LayoutDOMView { export namespace VegaPlot { export type Attrs = p.AttrsOf export type Props = LayoutDOM.Props & { - data: p.Property - data_sources: p.Property + data: p.Property | null> + data_sources: p.Property> events: p.Property show_actions: p.Property - theme: p.Property - throttle: p.Property + theme: p.Property + throttle: p.Property> } }