From 2ac399af277c9d775a327d410b143a2475010b65 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Sun, 24 Mar 2019 16:38:14 +0000 Subject: [PATCH 1/2] Allow defining external JS and CSS resources via config --- panel/compiler.py | 18 +++++++++++++++--- panel/config.py | 10 ++++++++++ panel/io/__init__.py | 1 + panel/io/resources.py | 26 ++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 panel/io/resources.py diff --git a/panel/compiler.py b/panel/compiler.py index eee30bf9f6..0b38111bed 100644 --- a/panel/compiler.py +++ b/panel/compiler.py @@ -74,11 +74,23 @@ def require_components(): Returns JS snippet to load the required dependencies in the classic notebook using REQUIRE JS. """ + from .config import config + configs, requirements, exports = [], [], [] - for model in CUSTOM_MODELS.values(): - if not hasattr(model, '__js_require__'): + js_requires = list(CUSTOM_MODELS.values()) + + for export, js in config.js_files.items(): + name = js.split('/')[-1].replace('.min', '').split('.')[-2] + conf = {'paths': {name: js[:-3]}, 'exports': {name: export}} + js_requires.append(conf) + + for model in js_requires: + if not (hasattr(model, '__js_require__') or isinstance(model, dict)): continue - model_require = model.__js_require__ + if isinstance(model, dict): + model_require = model + else: + model_require = model.__js_require__ model_exports = model_require.pop('exports', {}) configs.append(model_require) for req in model_require.get('paths', []): diff --git a/panel/config.py b/panel/config.py index 94639e0b8e..40ac5ac00e 100644 --- a/panel/config.py +++ b/panel/config.py @@ -37,6 +37,16 @@ class _config(param.Parameterized): os.environ['PANEL_EMBED'] = 'True' """ + css_files = param.List(default=[], doc=""" + External CSS files to load as part of the template.""") + + js_files = param.Dict(default={}, doc=""" + External JS files to load as part of the template. Dictionary + should map from exported name to the URL of the JS file.""") + + raw_css = param.List(default=[], doc=""" + List of raw CSS strings to add to the template.""") + _embed = param.Boolean(default=False, allow_None=True, doc=""" Whether plot data will be embedded.""") diff --git a/panel/io/__init__.py b/panel/io/__init__.py index da5c82fc81..3918b7b67e 100644 --- a/panel/io/__init__.py +++ b/panel/io/__init__.py @@ -6,5 +6,6 @@ from .embed import embed_state # noqa from .state import state # noqa from .model import add_to_doc, remove_root, diff # noqa +from .resources import Resources # noqa from .server import get_server # noqa from .notebook import block_comm, load_notebook, push # noqa diff --git a/panel/io/resources.py b/panel/io/resources.py new file mode 100644 index 0000000000..29436dd484 --- /dev/null +++ b/panel/io/resources.py @@ -0,0 +1,26 @@ +""" +Patches bokeh resources to make it easy to add external JS and CSS +resources via the panel.config object. +""" +from __future__ import absolute_import, division, unicode_literals + +from bokeh.resources import Resources + +def css_raw(self): + from ..config import config + raw = super(Resources, self).css_raw + return raw + config.raw_css + +def js_files(self): + from ..config import config + files = super(Resources, self).js_files + return files + list(config.js_files.values()) + +def css_files(self): + from ..config import config + files = super(Resources, self).css_files + return files + config.css_files + +Resources.css_raw = property(css_raw) +Resources.js_files = property(js_files) +Resources.css_files = property(css_files) From 6ca3904c044b1c738395257df1db3e0783f4fca3 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Sun, 24 Mar 2019 17:05:07 +0000 Subject: [PATCH 2/2] Added docs about loading custom js and css --- examples/user_guide/Deploy_and_Export.ipynb | 35 ++++++++++++--------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/examples/user_guide/Deploy_and_Export.ipynb b/examples/user_guide/Deploy_and_Export.ipynb index fc4cbc8856..d23cd5fbe4 100644 --- a/examples/user_guide/Deploy_and_Export.ipynb +++ b/examples/user_guide/Deploy_and_Export.ipynb @@ -22,9 +22,24 @@ "\n", " jupyter labextension install @pyviz/jupyterlab_pyviz\n", " \n", + "Note that to use certain components such as Vega, LaTeX and Plotly plots in a notebook the models must be loaded using the extension, e.g.:\n", + "\n", + " pn.extension('vega', 'katex')\n", + " \n", + "will ensure that the Vega and LaTeX JS dependencies are loaded. Additionally any external ``css_files``, ``js_files`` and ``raw_css`` should be declared in the extension. The ``js_files`` should be declared as a dictionary mapping from the exported JS module name to the URL containing the JS components, while the ``css_files`` can be defined as a list:\n", + "\n", + " pn.extension(js_files={'deck': https://unpkg.com/deck.gl@~5.2.0/deckgl.min.js},\n", + " css_files=['https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.css'])\n", + "\n", + "Meanwhile ``raw_css`` can be defined as a list of CSS strings. Providing keyword arguments via the ``extension`` is the same as setting them on ``pn.config``, which is the preferred approach outside the notebook, e.g. ``js_files`` and ``css_files`` may be set as follows:\n", + "\n", + " pn.config.js_files = {'deck': https://unpkg.com/deck.gl@~5.2.0/deckgl.min.js}\n", + " pn.config.css_files = ['https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.css']\n", + "\n", + "\n", "#### The repr\n", " \n", - "Once these conditions are met a panel will display itself:" + "Once the extension is loaded panel objects will display themselves if placed at the end of cell:" ] }, { @@ -192,25 +207,15 @@ "\n", "## Exporting\n", "\n", - "In case you don't need an actual server or simply want to export a static snapshot of a panel app you can use the ``save`` method which allows exporting the app to a standalone HTML or PNG file." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ + "In case you don't need an actual server or simply want to export a static snapshot of a panel app you can use the ``save`` method which allows exporting the app to a standalone HTML or PNG file.\n", + "\n", "By default, the HTML file generated will depend on loading JavaScript code for BokehJS from the online ``CDN`` repository, to reduce the file size. If you need to work in an airgapped or no-network environment, you can declare that ``INLINE`` resources should be used instead of ``CDN``:\n", "\n", "```python\n", "from bokeh.resources import INLINE\n", "panel.save('test.html', resources=INLINE)\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ + "```\n", + "\n", "Finally, if a 'png' file extension is specified, the exported plot will be rendered as a PNG, which currently requires Selenium and PhantomJS to be installed:\n", "\n", "```python\n",