Skip to content

Commit

Permalink
Statically type vega.ts
Browse files Browse the repository at this point in the history
  • Loading branch information
mattpap committed Feb 12, 2024
1 parent 41aecdb commit ec90fe4
Showing 1 changed file with 69 additions and 69 deletions.
138 changes: 69 additions & 69 deletions panel/models/vega.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
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"

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<any, any> | Record<any, any> | any[]): any {
if (value instanceof Map) {
const obj: Record<any, any> = {}
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<any, any> = {}
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
}
Expand All @@ -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<typeof debounce>
_resize: ReturnType<typeof debounce>
_rendered: boolean = false

connect_signals(): void {
Expand All @@ -66,33 +65,30 @@ 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 = []
this._connect_sources()
}

_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)
}
}
}

override remove(): void {
if (this.vega_view) {
this.vega_view.finalize()
}
this.vega_view?.finalize()
super.remove()
}

Expand All @@ -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<PlainObject<unknown>[]> {
const datasets: PlainObject<PlainObject<unknown>[]> = {}
for (const [ds, cds] of entries(this.model.data_sources)) {
const data: PlainObject<unknown>[] = []
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<unknown> = {}
for (const column of columns) {
item[column] = cds.data[column][i]
item[column] = cds.get(column)[i]
}
data.push(item)
}
Expand All @@ -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))
}
})
}
Expand All @@ -210,12 +210,12 @@ export class VegaPlotView extends LayoutDOMView {
export namespace VegaPlot {
export type Attrs = p.AttrsOf<Props>
export type Props = LayoutDOM.Props & {
data: p.Property<any>
data_sources: p.Property<any>
data: p.Property<Dict<unknown> | null>
data_sources: p.Property<Dict<ColumnDataSource>>
events: p.Property<string[]>
show_actions: p.Property<boolean>
theme: p.Property<string | null>
throttle: p.Property<any>
theme: p.Property<Theme | null>
throttle: p.Property<Dict<number>>
}
}

Expand Down

0 comments on commit ec90fe4

Please sign in to comment.