diff --git a/panel/io/handlers.py b/panel/io/handlers.py index 52e408b171..f4c3a0551b 100644 --- a/panel/io/handlers.py +++ b/panel/io/handlers.py @@ -533,7 +533,10 @@ def _render_template(self, doc, path): layouts, outputs, cells = {}, {}, {} for cell_id, out in state._cell_outputs.items(): - spec = state._cell_layouts[self].get(cell_id, {}) + if cell_id in self._layout.get('cells', {}): + spec = self._layout['cells'][cell_id] + else: + spec = state._cell_layouts[self].get(cell_id, {}) if 'width' in spec and 'height' in spec: sizing_mode = 'stretch_both' else: @@ -545,15 +548,19 @@ def _render_template(self, doc, path): for po in pout: po.sizing_mode = sizing_mode outputs[cell_id] = pout - layouts[id(pout)] = state._cell_layouts[self][cell_id] + layouts[id(pout)] = spec cells[cell_id] = id(pout) pout.servable() # Reorder outputs based on notebook metadata import nbformat nb = nbformat.read(self._runner._path, nbformat.NO_CONVERT) + if 'order' in self._layout: + cell_order = self._layout['order'] + else: + cell_order = nb['metadata'].get('panel-cell-order', []) ordered = {} - for cell_id in nb['metadata'].get('panel-cell-order', []): + for cell_id in cell_order: if cell_id not in cells: continue obj_id = cells[cell_id] @@ -568,7 +575,10 @@ def _render_template(self, doc, path): ordered[obj_id] = spec # Set up state - state.template.layout = ordered + state.template.param.update( + layout=ordered, + local_save=not bool(state._jupyter_kernel_context) + ) if persist: state.template.param.watch(self._update_position_metadata, 'layout') state._session_outputs[doc] = outputs diff --git a/panel/pane/vega.py b/panel/pane/vega.py index 09dff84804..6afbccd905 100644 --- a/panel/pane/vega.py +++ b/panel/pane/vega.py @@ -264,6 +264,16 @@ def _get_properties(self, doc, sources={}): data = props['data'] if data is not None: sources = self._get_sources(data, sources) + if self.sizing_mode: + if 'both' in self.sizing_mode: + if 'width' in data: + data['width'] = 'container' + if 'height' in data: + data['height'] = 'container' + elif 'width' in self.sizing_mode and 'width' in data: + data['width'] = 'container' + elif 'height' in self.sizing_mode and 'height' in data: + data['height'] = 'container' dimensions = _get_dimensions(data, props) if data else {} props['data'] = data props['data_sources'] = sources diff --git a/panel/template/editable/__init__.py b/panel/template/editable/__init__.py index 78d3d840f8..d410870af2 100644 --- a/panel/template/editable/__init__.py +++ b/panel/template/editable/__init__.py @@ -83,6 +83,9 @@ class EditableTemplate(VanillaTemplate): The layout definition of the template indexed by the id of each component in the main area.""") + local_save = param.Boolean(default=True, doc=""" + Whether to enable saving to local storage.""") + _css = [ pathlib.Path(__file__).parent.parent / 'vanilla' / "vanilla.css", pathlib.Path(__file__).parent / 'editable.css' @@ -106,6 +109,7 @@ def _update_vars(self): } self._render_variables['muuri_layout'] = list(layout.values()) self._render_variables['editable'] = self.editable + self._render_variables['local_save'] = self.local_save super()._update_vars() def _init_doc( diff --git a/panel/template/editable/editable.html b/panel/template/editable/editable.html index 7f4dbc1a8f..f7776a8827 100644 --- a/panel/template/editable/editable.html +++ b/panel/template/editable/editable.html @@ -4,7 +4,9 @@ {% if editable %}
+ {% if local_save %}
+ {% endif %} {% if busy %}
{{ embed(roots.busy_indicator) | indent(6) }} @@ -123,7 +125,11 @@ } width = Math.min(100, width); item.style.width = `calc( ${width}% - 30px)`; - item.style.height = `${height}px`; + if (height == null) { + item.style.height = ""; + } else { + item.style.height = `${height}px`; + } if (notify) { grid.refreshItems() grid.layout() @@ -195,7 +201,7 @@ const el = item.getElement(); const margin = item.getMargin(); const height = item.isVisible() ? item.getHeight() : 100; - resize_item(el, 100, height-margin.top, false); + resize_item(el, 100, Math.max(height-margin.top, 50), false); } grid.refreshItems(); grid.show(items, {layout: false}); @@ -222,11 +228,13 @@ } } + {% if local_save %} const save_button = document.getElementById("grid-save") save_button.addEventListener("click", function() { const layout = export_layout() saveToLS('grid-layout', layout) }) + {% endif %} window.resizeableGrid = interact('.muuri-grid-item') .resizable({ @@ -241,11 +249,14 @@ const height = event.rect.height-top-bottom; event.target.style.zIndex = 100; resize_item(event.target, width, height, false); + grid.refreshItems() + grid.layout() }, end (event) { event.target.style.removeProperty('z-index') grid.refreshItems() grid.layout() + window.dispatchEvent(new Event('resize')); } }, modifiers: [