Skip to content

Commit

Permalink
Improve rendering of ReactiveHTML literal children (#2383)
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr authored Jun 14, 2021
1 parent b3d5aae commit 23dc729
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 31 deletions.
7 changes: 4 additions & 3 deletions panel/models/reactive_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,15 +159,16 @@ def find_attrs(html):
}


def construct_data_model(parameterized, name=None, ignore=[]):
def construct_data_model(parameterized, name=None, ignore=[], types={}):
properties = {}
for pname in parameterized.param:
if pname in ignore:
continue
p = parameterized.param[pname]
if p.precedence and p.precedence < 0:
continue
prop = PARAM_MAPPING.get(type(p))
ptype = types.get(pname, type(p))
prop = PARAM_MAPPING.get(ptype)
pname = parameterized._rename.get(pname, pname)
if pname == 'name' or pname is None:
continue
Expand Down Expand Up @@ -199,7 +200,7 @@ class ReactiveHTML(HTMLBox):

callbacks = bp.Dict(bp.String, bp.List(bp.Tuple(bp.String, bp.String)))

children = bp.Dict(bp.String, bp.List(bp.Either(bp.Instance(LayoutDOM), bp.String)))
children = bp.Dict(bp.String, bp.Either(bp.List(bp.Either(bp.Instance(LayoutDOM), bp.String)), bp.String))

data = bp.Instance(DataModel)

Expand Down
62 changes: 38 additions & 24 deletions panel/models/reactive_html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,21 @@ export class ReactiveHTMLView extends PanelHTMLBoxView {
this.connect(this.model.properties.children.change, async () => {
this.html = htmlDecode(this.model.html) || this.model.html
await this.rebuild()
if (this._parent != null)
this._parent.invalidate_layout()
this.invalidate_layout()
})
for (const prop in this.model.data.properties) {
this.connect(this.model.data.properties[prop].change, () => {
if (!this._changing) {
this._update(prop)
this.invalidate_layout()
for (const node in this.model.children) {
if (this.model.children[node] == prop) {
let children = this.model.data[prop]
if (!isArray(children))
children = [children]
this._render_node(node, children)
this.invalidate_layout()
} else if (!this._changing) {
this._update(prop)
this.invalidate_layout()
}
}
})
}
Expand Down Expand Up @@ -195,35 +202,42 @@ export class ReactiveHTMLView extends PanelHTMLBoxView {

private _render_child(model: any, el: Element): void {
const view: any = this._child_views.get(model)
view._parent = this
if (view == null)
el.innerHTML = model
else
else {
view._parent = view
view.renderTo(el)
}
}

private _render_children(): void {
_render_node(node: any, children: any[]): void {
const id = this.model.data.id
for (const node in this.model.children) {
const children = this.model.children[node]
if (this.model.looped.indexOf(node) > -1) {
for (let i = 0; i < children.length; i++) {
let el: any = document.getElementById(`${node}-${i}-${id}`)
if (el == null) {
console.warn(`DOM node '${node}-${i}-${id}' could not be found. Cannot render children.`)
continue
}
this._render_child(children[i], el)
}
} else {
let el: any = document.getElementById(`${node}-${id}`)
if (this.model.looped.indexOf(node) > -1) {
for (let i = 0; i < children.length; i++) {
let el: any = document.getElementById(`${node}-${i}-${id}`)
if (el == null) {
console.warn(`DOM node '${node}-${id}' could not be found. Cannot render children.`)
console.warn(`DOM node '${node}-${i}-${id}' could not be found. Cannot render children.`)
continue
}
for (const child of children)
this._render_child(child, el)
this._render_child(children[i], el)
}
} else {
let el: any = document.getElementById(`${node}-${id}`)
if (el == null) {
console.warn(`DOM node '${node}-${id}' could not be found. Cannot render children.`)
return
}
for (const child of children)
this._render_child(child, el)
}
}

private _render_children(): void {
for (const node in this.model.children) {
let children = this.model.data[prop]
if (!isArray(children))
children = [children]
this._render_node(node, children)
}
}

Expand Down
22 changes: 18 additions & 4 deletions panel/reactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -996,13 +996,21 @@ def __init__(mcs, name, bases, dict_):
if node not in mcs._attrs:
mcs._attrs[node] = []
mcs._attrs[node].append((attr, param_attrs, template))
ignored = list(Reactive.param)+list(mcs._parser.children.values())
ignored = list(Reactive.param)
types = {}
for child in mcs._parser.children.values():
if mcs._child_config.get(child) == 'literal':
types[child] = param.String
else:
ignored.append(child)
ignored.remove('name')

# Create model with unique name
ReactiveHTMLMetaclass._name_counter[name] += 1
model_name = f'{name}{ReactiveHTMLMetaclass._name_counter[name]}'
mcs._data_model = construct_data_model(mcs, name=model_name, ignore=ignored)
mcs._data_model = construct_data_model(
mcs, name=model_name, ignore=ignored, types=types
)



Expand Down Expand Up @@ -1174,7 +1182,10 @@ def _process_children(self, doc, root, model, comm, children):
return children

def _init_params(self):
ignored = list(Reactive.param)+list(self._parser.children.values())
ignored = list(Reactive.param)
for child in self._parser.children.values():
if self._child_config.get(child) != 'literal':
ignored.append(child)
params = {
p : getattr(self, p) for p in list(Layoutable.param)
if getattr(self, p) is not None and p != 'name'
Expand Down Expand Up @@ -1223,6 +1234,7 @@ def _get_children(self, doc, root, model, comm, old_children=None):
for parent, children_param in self._parser.children.items():
mode = self._child_config.get(children_param, 'model')
if mode == 'literal':
new_panes[parent] = None
continue
panes = getattr(self, children_param)
if isinstance(panes, dict):
Expand Down Expand Up @@ -1256,7 +1268,7 @@ def _get_children(self, doc, root, model, comm, old_children=None):
child_panes = child_panes.values()
mode = self._child_config.get(children_param, 'model')
if mode == 'literal':
new_models[parent] = child_panes
new_models[parent] = children_param
elif children_param in old_children:
# Find existing models
old_panes = old_children[children_param]
Expand Down Expand Up @@ -1397,6 +1409,8 @@ def _update_model(self, events, msg, root, model, doc, comm):
for prop, v in list(msg.items()):
if prop in child_params:
new_children[prop] = prop
if self._child_config.get(prop) == 'literal':
data_msg[prop] = bleach.clean(v)
elif prop in list(Reactive.param)+['events']:
model_msg[prop] = v
elif prop in self.param and (self.param[prop].precedence or 0) < 0:
Expand Down

0 comments on commit 23dc729

Please sign in to comment.