-
-
Notifications
You must be signed in to change notification settings - Fork 529
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a standalone Modal layout (#7083)
- Loading branch information
Showing
11 changed files
with
567 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "5c557435-c052-4622-8023-81ed6f63f231", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"import panel as pn\n", | ||
"pn.extension('modal')" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "a44c5eae-9c73-4626-96ff-019cf959d647", | ||
"metadata": {}, | ||
"source": [ | ||
"The `Modal` layout provides a dialog windows on top of the layout. It is built on-top of [a11y-dialog](https://a11y-dialog.netlify.app/). It has a list-like API with methods to `append`, `extend`, `clear`, `insert`, `pop`, `remove` and `__setitem__`, which make it possible to interactively update and modify the layout. Components inside it are laid out like a `Column`.\n", | ||
"\n", | ||
"#### Parameters:\n", | ||
"\n", | ||
"* **`open`** (boolean): Whether to open the modal.\n", | ||
"* **`show_close_button`** (boolean): Whether to show a close button in the modal.\n", | ||
"* **`background_close`** (boolean): Whether to enable closing the modal when clicking outside the modal.\n", | ||
"\n", | ||
"#### Methods:\n", | ||
"\n", | ||
"* **`show`**: Show the modal.\n", | ||
"* **`hide`**: Hide the modal.\n", | ||
"* **`toggle`**: toggle the modal.\n", | ||
"* **`create_button`**: Create a button which can either show, hide, or toggle the modal.\n", | ||
"\n" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "2b3a8784-7e78-4414-a1a5-600b0ff14664", | ||
"metadata": {}, | ||
"source": [ | ||
"A `Modal` layout can either be instantiated as empty and populated after the fact or using a list of objects provided as positional arguments. If the objects are not already panel components they will each be converted to one using the `pn.panel` conversion method." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "9fd46bf9-a63e-457c-bcc2-a1578b8f9bda", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"w1 = pn.widgets.TextInput(name='Text:')\n", | ||
"w2 = pn.widgets.FloatSlider(name='Slider')\n", | ||
"\n", | ||
"modal = pn.Modal(w1, w2, name='Basic FloatPanel', margin=20)\n", | ||
"toggle_button = modal.create_button(\"toggle\", name=\"Toggle modal\")\n", | ||
"\n", | ||
"pn.Column('**Example: Basic `Modal`**', toggle_button, modal)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "558852dd-9a11-4099-bae3-6da3e38f79f9", | ||
"metadata": {}, | ||
"source": [ | ||
"### Controls\n", | ||
"\n", | ||
"The `Modal` widget exposes a number of options which can be changed from both Python and Javascript. Try out the effect of these parameters interactively:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "b49ed4d6-b06b-47e1-a917-88ff3141936b", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"modal.controls(jslink=True)" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"language_info": { | ||
"name": "python", | ||
"pygments_lexer": "ipython3" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 5 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
from __future__ import annotations | ||
|
||
from typing import ( | ||
TYPE_CHECKING, ClassVar, Literal, Mapping, Optional, | ||
) | ||
|
||
import param | ||
|
||
from pyviz_comms import JupyterComm | ||
|
||
from ..models.modal import ModalDialogEvent | ||
from ..util import lazy_load | ||
from ..util.warnings import PanelUserWarning, warn | ||
from .base import ListPanel | ||
|
||
if TYPE_CHECKING: | ||
from bokeh.document import Document | ||
from bokeh.model import Model | ||
from pyviz_comms import Comm | ||
|
||
|
||
class Modal(ListPanel): | ||
"""Create a modal dialog that can be opened and closed.""" | ||
|
||
open = param.Boolean(default=False, doc="Whether to open the modal.") | ||
|
||
show_close_button = param.Boolean(default=True, doc="Whether to show a close button in the modal.") | ||
|
||
background_close = param.Boolean(default=True, doc="Whether to enable closing the modal when clicking the background.") | ||
|
||
_rename: ClassVar[Mapping[str, str | None]] = {} | ||
|
||
_source_transforms: ClassVar[Mapping[str, str | None]] = {'objects': None} | ||
|
||
def _get_model( | ||
self, doc: Document, root: Optional[Model] = None, | ||
parent: Optional[Model] = None, comm: Optional[Comm] = None | ||
) -> Model: | ||
Modal._bokeh_model = lazy_load( | ||
'panel.models.modal', 'Modal', isinstance(comm, JupyterComm), root | ||
) | ||
return super()._get_model(doc, root, parent, comm) | ||
|
||
def show(self): | ||
self.open = True | ||
|
||
def hide(self): | ||
self.open = False | ||
|
||
def toggle(self): | ||
self.open = not self.open | ||
|
||
@param.depends("open", watch=True) | ||
def _open(self): | ||
if not self._models: | ||
msg = "To use the Modal, you must use '.servable' in a server setting or output the Modal in Jupyter Notebook." | ||
warn(msg, category=PanelUserWarning) | ||
self._send_event(ModalDialogEvent, open=self.open) | ||
|
||
def create_button(self, action: Literal["show", "hide", "toggle"], **kwargs): | ||
"""Create a button to show, hide or toggle the modal.""" | ||
from panel.widgets import Button | ||
|
||
button = Button(**kwargs) | ||
match action: | ||
case "show": | ||
button.on_click(lambda *e: self.show()) | ||
case "hide": | ||
button.on_click(lambda *e: self.hide()) | ||
case "toggle": | ||
button.on_click(lambda *e: self.toggle()) | ||
case _: | ||
raise TypeError(f"Invalid action: {action}") | ||
|
||
return button |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
from typing import Any | ||
|
||
from bokeh.core.properties import Bool | ||
from bokeh.events import ModelEvent | ||
from bokeh.model import Model | ||
|
||
from ..config import config | ||
from ..io.resources import bundled_files | ||
from ..util import classproperty | ||
from .layout import Column | ||
|
||
__all__ = ( | ||
"Modal", | ||
"ModalDialogEvent", | ||
) | ||
|
||
|
||
class Modal(Column): | ||
|
||
__javascript_raw__ = [ | ||
f"{config.npm_cdn}/a11y-dialog@7/dist/a11y-dialog.min.js" | ||
] | ||
|
||
@classproperty | ||
def __javascript__(cls): | ||
return bundled_files(cls) | ||
|
||
@classproperty | ||
def __js_skip__(cls): | ||
return {'A11yDialog': cls.__javascript__[:1]} | ||
|
||
__js_require__ = { | ||
'paths': { | ||
'a11y-dialog': f"{config.npm_cdn}/a11y-dialog@7/dist/a11y-dialog.min", | ||
}, | ||
'exports': { | ||
'A11yDialog': 'a11y-dialog', | ||
} | ||
} | ||
|
||
open = Bool(default=False, help="Whether or not the modal is open.") | ||
show_close_button = Bool(True, help="Whether to show a close button in the modal.") | ||
background_close = Bool(True, help="Whether to enable closing the modal when clicking the background.") | ||
|
||
|
||
class ModalDialogEvent(ModelEvent): | ||
event_name = 'modal-dialog-event' | ||
|
||
def __init__(self, model: Model | None, open: bool): | ||
self.open = open | ||
super().__init__(model=model) | ||
|
||
def event_values(self) -> dict[str, Any]: | ||
return dict(super().event_values(), open=self.open) |
Oops, something went wrong.