-
-
Notifications
You must be signed in to change notification settings - Fork 525
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
Add a standalone Modal layout #7083
base: main
Are you sure you want to change the base?
Changes from all commits
5d211f4
d301572
7dcf020
4220c27
6d1689f
009c57d
6cea115
e8585a9
185d5e2
5a82aca
e3c8e75
1484cc3
e68d42f
7145d8b
8a617aa
256b337
df0140a
d5f8501
09c414d
7f9b12b
94a27fc
fea45f0
c9ada92
06aee3f
cf3414e
1bb3c55
e784495
52454dc
1e1ded7
5e82f98
59bba8a
5914e4e
9d531b1
77e5adc
8235a48
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
:host(.bk-panel-models-modal-Modal) { | ||
width: 0; | ||
height: 0; | ||
} | ||
|
||
.dialog-container, | ||
.dialog-overlay { | ||
position: fixed; | ||
top: 0; | ||
right: 0; | ||
bottom: 0; | ||
left: 0; | ||
} | ||
.dialog-container { | ||
z-index: 100002; | ||
display: flex; | ||
} | ||
.dialog-overlay { | ||
z-index: 100001; | ||
background-color: rgb(43 46 56 / 0.9); | ||
} | ||
.dialog-content { | ||
margin: auto; | ||
z-index: 100002; | ||
position: relative; | ||
background-color: white; | ||
border-radius: 2px; | ||
padding: 10px; | ||
padding-bottom: 20px; | ||
} | ||
fast-design-system-provider .dialog-content { | ||
background-color: var(--background-color); | ||
border-radius: calc(var(--corner-radius) * 1px); | ||
} | ||
@keyframes fade-in { | ||
from { | ||
opacity: 0; | ||
} | ||
} | ||
@keyframes slide-up { | ||
from { | ||
transform: translateY(10%); | ||
} | ||
} | ||
.dialog-overlay { | ||
animation: fade-in 200ms both; | ||
} | ||
.dialog-content { | ||
animation: | ||
fade-in 400ms 200ms both, | ||
slide-up 400ms 200ms both; | ||
} | ||
@media (prefers-reduced-motion: reduce) { | ||
.dialog-overlay, | ||
.dialog-content { | ||
animation: none; | ||
} | ||
} | ||
.pnx-dialog-close { | ||
position: absolute; | ||
top: 0.5em; | ||
right: 0.5em; | ||
border: 0; | ||
padding: 0.25em; | ||
background-color: transparent; | ||
font-size: 1.5em; | ||
width: 1.5em; | ||
height: 1.5em; | ||
text-align: center; | ||
cursor: pointer; | ||
transition: 0.15s; | ||
border-radius: 50%; | ||
z-index: 100003; | ||
} | ||
fast-design-system-provider .pnx-dialog-close { | ||
color: var(--neutral-foreground-rest); | ||
} | ||
.pnx-dialog-close:hover { | ||
background-color: rgb(50 50 0 / 0.15); | ||
} | ||
fast-design-system-provider .pnx-dialog-close:hover { | ||
background-color: var(--neutral-fill-hover); | ||
} | ||
.lm-Widget.p-Widget.lm-TabBar.p-TabBar.lm-DockPanel-tabBar.jp-Activity { | ||
z-index: -1; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
from __future__ import annotations | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One thing to consider is if the Could the api be simplified?The api of import panel as pn
from panel_modal import Modal
pn.extension("modal")
modal = Modal(pn.panel("Hi. I am the Panel Modal!", width=200))
pn.Column(modal.param.open, modal).servable() It requires you define a variable holding the Modal and include both the button ( If you want to customize the button it requires something like Could the api be simplified? Could it be enough to just include the modal? import panel as pn
from panel_modal import Modal
pn.extension("modal")
pn.Column(
Modal(pn.panel("Hi. I am the Panel Modal!", width=200))
).servable() That should show the button. And when clicked the modal should open/ close. That would make it much simpler to drop in as you don't need to define a variable holding the If you want to customize the button you can just do: import panel as pn
from panel_modal import Modal
pn.extension("modal")
pn.Column(
pn.widgets.Button.from_param(
Modal(
pn.panel("Hi. I am the Panel Modal!", width=200),
),
button_style="outline",
)
)
).servable() If you want to trigger the modal from something else than a Button than you should be able to hide the button: import panel as pn
from panel_modal import Modal
pn.extension("modal")
modal = Modal(..., visible=False)
...
def trigger_modal():
modal.open=True
pn.Column(
modal,
.....
).servable() Could the API be generalized?A Modal is actually a part of a general concept for action buttons. You want to be able to trigger an action via a Button. You want to be able to easily set the source or target ( modal_button = ModalButton(object=pn.panel(...))
copy_text_button= CopyToClipboardButton(object="...my code")
copy_dataframe_button= CopyToClipboardButton(object=df) # df is a dataframe
paste_button = PasteFromClipboardButton(object=pn.widgets.Tabulator) # Here the object is the target. Not the source.
link_button = OpenLinkButton(object="https://panel.holoviz.org, target="_blank")
maximize_content = pn.Column(...)
maximize_button = MaximizeButton(object=maximize_content) # Maximizes the object to full window size
... I've had in the back of my head that I wanted to create a @philippjfr. What do you think? |
||
from typing import ( | ||
TYPE_CHECKING, ClassVar, Literal, Mapping, Optional, | ||
) | ||
|
||
import param | ||
|
||
from pyviz_comms import JupyterComm | ||
|
||
from ..io.resources import CDN_DIST | ||
from ..models.modal import ModalDialogEvent | ||
from ..util import lazy_load | ||
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.") | ||
|
||
_stylesheets: ClassVar[list[str]] = [f"{CDN_DIST}css/models/modal.css"] | ||
|
||
_rename: ClassVar[Mapping[str, str | 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): | ||
self._send_event(ModalDialogEvent, open=self.open) | ||
|
||
def create_button(self, button_type: Literal["show", "hide", "toggle"], **kwargs): | ||
"""Create a button to show, hide or toggle the modal.""" | ||
from panel.widgets import Button | ||
|
||
button = Button(**kwargs) | ||
match button_type: | ||
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 button_type: {button_type}") | ||
|
||
return button |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
from typing import Any | ||
|
||
from bokeh.core.properties import Bool | ||
from bokeh.events import ModelEvent | ||
from bokeh.model import Model | ||
|
||
from ..io.resources import bundled_files | ||
from ..util import classproperty | ||
from .layout import Column | ||
|
||
__all__ = ( | ||
"Modal", | ||
"ModalDialogEvent", | ||
) | ||
|
||
|
||
class Modal(Column): | ||
|
||
__javascript_raw__ = [ | ||
"https://cdn.jsdelivr.net/npm/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': "https://cdn.jsdelivr.net/npm/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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When adding documentation please consider if request awesome-panel/panel-modal#3 should be part of the docs. Thx.