Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved handling of ReactiveHTML resources #2397

Merged
merged 2 commits into from
Jun 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 58 additions & 1 deletion examples/user_guide/Custom_Components.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,15 @@
"import param\n",
"import panel as pn\n",
"\n",
"pn.extension()"
"js_files = {\n",
" 'mdc': 'https://unpkg.com/material-components-web@latest/dist/material-components-web.min.js'\n",
"}\n",
" \n",
"css_files = [\n",
" 'https://unpkg.com/material-components-web@latest/dist/material-components-web.min.css'\n",
"]\n",
"\n",
"pn.extension(js_files=js_files, css_files=css_files)"
]
},
{
Expand Down Expand Up @@ -411,6 +419,55 @@
"3. `'line_width'` and `'color'` are invoked when the parameters change (i.e. when a widget is updated)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## External dependencies\n",
"\n",
"Often you will want to wrap a component with some external Javascript or CSS dependencies. To make this possible `ReactiveHTML` components may declare `__javascript__`, `__javascript_modules__` and `__css__` attributes, specifying the external dependencies to load. Note that in a notebook the component must be declared before `pn.extension` is called, otherwise the libraries won't be loaded (in this case we explicitly loaded them above).\n",
"\n",
"Below we will create a Material UI text field and declare the Javascript and CSS components to load:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class MaterialTextField(ReactiveHTML):\n",
" \n",
" value = param.String(default='')\n",
" \n",
" _template = \"\"\"\n",
" <label id=\"text-field\" class=\"mdc-text-field mdc-text-field--filled\">\n",
" <span class=\"mdc-text-field__ripple\"></span>\n",
" <span class=\"mdc-floating-label\">Label</span>\n",
" <input id=\"text-input\" type=\"text\" class=\"mdc-text-field__input\" aria-labelledby=\"my-label\" value=\"${value}\"></input>\n",
" <span class=\"mdc-line-ripple\"></span>\n",
" </label>\n",
" \"\"\"\n",
" \n",
" _dom_events = {'text-input': ['change']}\n",
" \n",
" _scripts = {\n",
" 'render': \"mdc.textField.MDCTextField.attachTo(text_field);\"\n",
" }\n",
" \n",
" __javascript__ = [\n",
" 'https://unpkg.com/material-components-web@latest/dist/material-components-web.min.js'\n",
" ]\n",
" \n",
" __css__ = [\n",
" 'https://unpkg.com/material-components-web@latest/dist/material-components-web.min.css'\n",
" ]\n",
" \n",
"text_field = MaterialTextField()\n",
"\n",
"text_field"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down
35 changes: 28 additions & 7 deletions panel/io/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,12 @@ def bundle_resources(resources):
if ext is not None:
js_raw.append(ext)

return Bundle.of(js_files, js_raw, css_files, css_raw, js_resources.hashes if js_resources else {})
hashes = js_resources.hashes if js_resources else {}

return Bundle(
js_files=js_files, js_raw=js_raw, css_files=css_files,
css_raw=css_raw, hashes=hashes
)


class Resources(BkResources):
Expand Down Expand Up @@ -167,7 +172,9 @@ def js_files(self):

for model in param.concrete_descendents(ReactiveHTML).values():
if hasattr(model, '__javascript__'):
files += model.__javascript__
for jsfile in model.__javascript__:
if jsfile not in files:
files.append(jsfile)

js_files = []
for js_file in files:
Expand Down Expand Up @@ -200,7 +207,14 @@ def js_files(self):
@property
def js_modules(self):
from ..config import config
return list(config.js_modules.values())
from ..reactive import ReactiveHTML
modules = list(config.js_modules.values())
for model in param.concrete_descendents(ReactiveHTML).values():
if hasattr(model, '__javascript_modules__'):
for jsmodule in model.__javascript_modules__:
if jsmodule not in modules:
modules.append(jsmodule)
return modules

@property
def css_files(self):
Expand All @@ -211,7 +225,9 @@ def css_files(self):

for model in param.concrete_descendents(ReactiveHTML).values():
if hasattr(model, '__css__'):
files += model.__css__
for css_file in model.__css__:
if css_file not in files:
files.append(css_file)

for cssf in config.css_files:
if os.path.isfile(cssf) or cssf in files:
Expand Down Expand Up @@ -242,15 +258,20 @@ class Bundle(BkBundle):

def __init__(self, **kwargs):
from ..config import config
self.js_modules = kwargs.pop("js_modules", list(config.js_modules.values()))
from ..reactive import ReactiveHTML
js_modules = list(config.js_modules.values())
for model in param.concrete_descendents(ReactiveHTML).values():
if hasattr(model, '__javascript_modules__'):
for js_module in model.__javascript_modules__:
if js_module not in js_modules:
js_modules.append(js_module)
self.js_modules = kwargs.pop("js_modules", js_modules)
super().__init__(**kwargs)

@classmethod
def from_bokeh(cls, bk_bundle):
from ..config import config
return cls(
js_files=bk_bundle.js_files,
js_modules=list(config.js_modules.values()),
js_raw=bk_bundle.js_raw,
css_files=bk_bundle.css_files,
css_raw=bk_bundle.css_raw,
Expand Down
2 changes: 2 additions & 0 deletions panel/models/reactive_html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ export class ReactiveHTMLView extends PanelHTMLBoxView {
}

resize_layout(): void {
if (this.layout == null)
this.update_layout()
super.resize_layout()
if (this._parent != null && this._parent !== this)
this._parent.resize_layout()
Expand Down