import type * as p from "@bokehjs/core/properties" import type {Transform} from "sucrase" import { ReactiveESM, ReactiveESMView, model_getter, model_setter, } from "./reactive_esm" export class ReactComponentView extends ReactiveESMView { declare model: ReactComponent declare style_cache: HTMLHeadElement model_getter = model_getter model_setter = model_setter override render_esm(): void { if (this.model.usesMui) { this.style_cache = document.createElement("head") this.shadow_el.insertBefore(this.style_cache, this.container) } super.render_esm() } override after_rendered(): void { const handlers = (this._lifecycle_handlers.get("after_render") || []) for (const cb of handlers) { cb() } if (!this._rendered) { for (const cb of (this._lifecycle_handlers.get("after_layout") || [])) { cb() } } this._rendered = true } protected override _render_code(): string { let render_code = ` if (rendered && view.model.usesReact) { view._changing = true const root = createRoot(view.container) try { root.render(rendered) } catch(e) { view.render_error(e) } }` let import_code if (this.model.bundle) { import_code = ` const ns = await view._module_cache.get(view.model.bundle) const {React, createRoot} = ns.default` } else { import_code = ` import * as React from "react" import { createRoot } from "react-dom/client"` } if (this.model.usesMui) { if (this.model.bundle) { import_code = ` const ns = await view._module_cache.get(view.model.bundle) const {CacheProvider, React, createCache, createRoot} = ns.default` } else { import_code = ` ${import_code} import createCache from "@emotion/cache" import { CacheProvider } from "@emotion/react"` } render_code = ` if (rendered) { const cache = createCache({ key: 'css-${this.model.id}', prepend: true, container: view.style_cache, }) rendered = React.createElement(CacheProvider, {value: cache}, rendered) } ${render_code}` } return ` const view = Bokeh.index.find_one_by_id('${this.model.id}') ${import_code} class Child extends React.Component { get view() { const model = this.props.parent.model.data[this.props.name] const models = Array.isArray(model) ? model : [model] return this.props.parent.get_child_view(models[this.props.index]) } get element() { const view = this.view return view == null ? null : view.el } componentDidMount() { this.view.render() this.view.after_render() this.props.parent.on_child_render(this.props.name, (new_views) => { this.forceUpdate() const view = this.view if (new_views.includes(view)) { view.render() view.after_render() } }) } append_child(ref) { if (ref != null) { const view = this.view if (view != null) { ref.appendChild(this.element) } } } render() { return React.createElement('div', {className: "child-wrapper", ref: (ref) => this.append_child(ref)}) } } function react_getter(target, name) { if (name == "useState") { return (prop) => { const data_model = target.model.data if (Reflect.has(data_model, prop)) { const [value, setValue] = React.useState(data_model.attributes[prop]); react_proxy.on(prop, () => setValue(data_model.attributes[prop])) React.useEffect(() => data_model.setv({[prop]: value}), [value]) return [value, setValue] } return undefined } } else if (name === "get_child") { return (child) => { const data_model = target.model.data const value = data_model.attributes[child] if (Array.isArray(value)) { const children = [] for (let i = 0; i { const value = data_model.attributes[child] if (new_views.length && children_state.length !== value.length) { const children = [] for (let i = 0; i export type Props = ReactiveESM.Props } export interface ReactComponent extends ReactComponent.Attrs {} export class ReactComponent extends ReactiveESM { declare properties: ReactComponent.Props override sucrase_transforms: Transform[] = ["typescript", "jsx"] constructor(attrs?: Partial) { super(attrs) } get usesMui(): boolean { if (this.importmap?.imports) { return Object.keys(this.importmap?.imports).some(k => k.startsWith("@mui")) } return false } get usesReact(): boolean { return this.compiled !== null && this.compiled.includes("React") } override compile(): string | null { const compiled = super.compile() if (this.bundle) { return compiled } else if (compiled === null || !compiled.includes("React")) { return compiled } return ` import * as React from "react" ${compiled}` } static override __module__ = "panel.models.esm" static { this.prototype.default_view = ReactComponentView } }