@bokeh/bokehjs
Version:
Interactive, novel data visualization
136 lines • 4.88 kB
JavaScript
import { Renderer, RendererView } from "./renderer";
import { UIElement } from "../ui/ui_element";
import { DOMNode } from "../dom/dom_node";
import { build_views, remove_views } from "../../core/build_views";
import { Ref, Or } from "../../core/kinds";
// TODO UIElement needs to inherit from DOMNode
const ElementLike = Or(Ref(UIElement), Ref(DOMNode));
export class CompositeRendererView extends RendererView {
static __name__ = "CompositeRendererView";
_renderer_views = new Map();
get renderer_views() {
return this.model.renderers.map((renderer) => this._renderer_views.get(renderer));
}
_element_views = new Map();
get element_views() {
return this.model.elements.map((element) => this._element_views.get(element));
}
*children() {
yield* super.children();
yield* this.renderer_views;
yield* this.element_views;
}
async lazy_initialize() {
await super.lazy_initialize();
await this._build_renderers();
await this._build_elements();
}
_computed_renderers = [];
get computed_renderers() {
return [...this.model.renderers, ...this._computed_renderers];
}
get computed_renderer_views() {
return this.computed_renderers.map((item) => this._renderer_views.get(item)).filter((rv) => rv != null);
}
async _build_renderers() {
return await build_views(this._renderer_views, this.computed_renderers, { parent: this.plot_view });
}
_computed_elements = [];
get computed_elements() {
return [...this.model.elements, ...this._computed_elements];
}
get computed_element_views() {
return this.computed_elements.map((item) => this._element_views.get(item)).filter((ev) => ev != null);
}
async _build_elements() {
return await build_views(this._element_views, this.computed_elements, { parent: this.plot_view });
}
async _update_renderers() {
await this._build_renderers();
}
async _update_elements() {
const { created } = await this._build_elements();
const created_elements = new Set(created);
// First remove and then either reattach existing elements or render and
// attach new elements, so that the order of children is consistent, while
// avoiding expensive re-rendering of existing views.
for (const element_view of this.element_views) {
element_view.el.remove();
}
for (const element_view of this.element_views) {
const is_new = created_elements.has(element_view);
const target = element_view.rendering_target() ?? this.shadow_el;
if (is_new) {
element_view.render_to(target);
}
else {
target.append(element_view.el);
}
}
this.r_after_render();
}
remove() {
remove_views(this._renderer_views);
remove_views(this._element_views);
super.remove();
}
connect_signals() {
super.connect_signals();
const { renderers, elements } = this.model.properties;
this.on_change(renderers, async () => {
await this._update_renderers();
});
this.on_change(elements, async () => {
await this._update_elements();
});
}
_has_rendered_elements = false;
paint(ctx) {
if (!this._has_rendered_elements) {
for (const element_view of this.element_views) {
const target = element_view.rendering_target() ?? this.shadow_el;
element_view.render_to(target);
}
this._has_rendered_elements = true;
}
super.paint(ctx);
if (this.displayed && this.is_renderable) {
for (const renderer of this.computed_renderer_views) {
renderer.paint(ctx);
}
}
const { displayed } = this;
for (const element_view of this.element_views) {
element_view.reposition(displayed);
}
}
has_finished() {
if (!super.has_finished()) {
return false;
}
for (const renderer_view of this.renderer_views) {
if (!renderer_view.has_finished()) {
return false;
}
}
for (const element_view of this.element_views) {
if (!element_view.has_finished()) {
return false;
}
}
return true;
}
}
export class CompositeRenderer extends Renderer {
static __name__ = "CompositeRenderer";
constructor(attrs) {
super(attrs);
}
static {
this.define(({ List, Ref }) => ({
renderers: [List(Ref(Renderer)), []],
elements: [List(ElementLike), []],
}));
}
}
//# sourceMappingURL=composite_renderer.js.map