diff --git a/src/panel_material_ui/base.py b/src/panel_material_ui/base.py
index c6381c4d..1e6273c8 100644
--- a/src/panel_material_ui/base.py
+++ b/src/panel_material_ui/base.py
@@ -29,7 +29,7 @@
from panel.models import ReactComponent as BkReactComponent
from panel.param import Param
from panel.util import base_version, classproperty
-from panel.viewable import Viewable
+from panel.viewable import Children, Viewable
from .__version import __version__ # noqa
from .theme import MaterialDesign
@@ -142,6 +142,10 @@ class MaterialComponent(ReactComponent):
the JS dependencies and theming support via the ThemedTransform.
"""
+ attached = Children(doc="""
+ Elements that are attached to this object but are not direct
+ children.""")
+
dark_theme = param.Boolean(doc="""
Whether to use dark theme. If not specified, will default to Panel's
global theme setting.""")
@@ -174,7 +178,8 @@ class MaterialComponent(ReactComponent):
"notistack": "https://esm.sh/notistack@3.0.2"
}
}
- _rename = {'loading': 'loading'}
+ _rename = {'loading': 'loading', 'attached': 'elements'}
+ _source_transforms = {'attached': None}
__abstract = True
@@ -235,11 +240,17 @@ def _render_esm(cls, compiled: bool | Literal['compiling'] = True, server: bool
return CDN_DIST
return super()._render_esm(compiled=True, server=server)
+ def _get_children(self, data_model, doc, root, parent, comm):
+ children, old_models = super()._get_children(data_model, doc, root, parent, comm)
+ children.pop('attached', None)
+ return children, old_models
+
def _get_model(
self, doc: Document, root: Model | None = None,
parent: Model | None = None, comm: Comm | None = None
) -> Model:
model = super()._get_model(doc, root, parent, comm)
+ model.elements, _ = self._get_child_model(self.attached, doc, model or root, parent, comm)
# Ensure model loads ESM and CSS bundles from CDN
# if requested or if in notebook
if (
@@ -266,6 +277,7 @@ def _set_on_model(self, msg: Mapping[str, Any], root: Model, model: Model) -> No
def _get_properties(self, doc: Document | None) -> dict[str, Any]:
props = super()._get_properties(doc)
+ props.pop('elements', None)
props.pop('loading', None)
props['data'].loading = self.loading
return props
diff --git a/src/panel_material_ui/layout/Drawer.jsx b/src/panel_material_ui/layout/Drawer.jsx
new file mode 100644
index 00000000..133172c7
--- /dev/null
+++ b/src/panel_material_ui/layout/Drawer.jsx
@@ -0,0 +1,14 @@
+import Drawer from "@mui/material/Drawer"
+
+export function render({model, view}) {
+ const [open, setOpen] = model.useState("open")
+ const objects = model.get_child("objects")
+
+ const anchorEl = view.parent.element_views.includes(view) ? view.parent.el : null
+
+ return (
+ setOpen(true)} onClose={() => setOpen(false)}>
+ {objects}
+
+ )
+}
diff --git a/src/panel_material_ui/layout/base.py b/src/panel_material_ui/layout/base.py
index bd28a52a..f86d08c4 100644
--- a/src/panel_material_ui/layout/base.py
+++ b/src/panel_material_ui/layout/base.py
@@ -465,6 +465,28 @@ class Dialog(MaterialListLike):
_esm_base = "Dialog.jsx"
+class Drawer(MaterialListLike):
+ """
+ The `Drawer` component can be used to display important content in a modal-like overlay that requires
+ user interaction. It is often used for tasks such as confirmations, forms, or displaying
+ additional information.
+
+ Reference: https://mui.com/material-ui/react-drawer/
+
+ :Example:
+ >>> close = Button(on_click=lambda _: drawer.param.update(open=False), label='Close') # type: ignore
+ >>> drawer = Drawer("This is a drawer", close)
+ >>> button = Button(on_click=lambda _: drawer.param.update(open=True), label=f'Open {Drawer.name}')
+ >>> pn.Column(button, drawer).servable()
+ """
+
+ open = param.Boolean(default=False, doc="""
+ Whether the drawer is open.""")
+
+ _esm_base = "Drawer.jsx"
+
+
+
__all__ = [
"Paper",
"Container",
@@ -475,5 +497,6 @@ class Dialog(MaterialListLike):
"Divider",
"Alert",
"Backdrop",
- "Dialog"
+ "Dialog",
+ "Drawer"
]
diff --git a/src/panel_material_ui/widgets/Menu.jsx b/src/panel_material_ui/widgets/Menu.jsx
new file mode 100644
index 00000000..0ce40148
--- /dev/null
+++ b/src/panel_material_ui/widgets/Menu.jsx
@@ -0,0 +1,39 @@
+import Divider from "@mui/material/Divider"
+import Menu from "@mui/material/Menu"
+import MenuItem from "@mui/material/MenuItem"
+
+export function render({model, view}) {
+ const [dense] = model.useState("dense")
+ const [items] = model.useState("items")
+ const [open, setOpen] = model.useState("open")
+ const [sx] = model.useState("sx")
+
+ const keys = Array.isArray(items) ? items.map((_, index) => index) : Object.keys(items)
+ const anchorEl = view.parent?.element_views.includes(view) ? view.parent.el : null
+
+ return (
+
+ )
+}
diff --git a/src/panel_material_ui/widgets/menus.py b/src/panel_material_ui/widgets/menus.py
index 45889006..f7b2f604 100644
--- a/src/panel_material_ui/widgets/menus.py
+++ b/src/panel_material_ui/widgets/menus.py
@@ -11,8 +11,8 @@
from panel.layout.base import ListLike
from panel.models.reactive_html import DOMEvent
-from ..base import COLORS
-from .base import MaterialWidget
+from ..base import COLORS, LoadingTransform, ThemedTransform
+from .base import MaterialWidget, TooltipTransform
from .button import _ButtonBase
@@ -263,6 +263,27 @@ def remove_on_action(self, action: str, callback: Callable[[DOMEvent], None]):
self._on_action_callbacks[action].remove(callback)
+class Menu(MenuBase):
+ """
+ The `Menu` component is a menu component that allows selecting from a list of items.
+
+ Menu items can be strings or objects with properties:
+ - label: The label of the menu item (required)
+ - icon: The icon of the menu item (optional)
+ - color: The color of the menu item (optional)
+
+ Reference: https://mui.com/material-ui/react-menu/
+ """
+
+ dense = param.Boolean(default=False, doc="Whether to show the menu items in a dense format.")
+
+ open = param.Boolean(default=False, doc="Whether the menu is open.")
+
+ _esm_base = "Menu.jsx"
+
+ _item_keys = ['label', 'icon', 'color', 'items']
+
+
class MenuButton(MenuBase, _ButtonBase):
"""
The `MenuButton` component is a button component that allows selecting from a list of items.
@@ -278,6 +299,7 @@ class MenuButton(MenuBase, _ButtonBase):
margin = Margin(default=5)
_esm_base = "MenuButton.jsx"
+ _esm_transforms = [LoadingTransform, TooltipTransform, ThemedTransform]
_source_transforms = {
"attached": None,
"button_type": None,
@@ -311,7 +333,7 @@ class Pagination(MaterialWidget):
value = param.Integer(default=None, doc="The current zero-indexed page number.")
- variant = param.Selector(default="outlined", objects=["outlined", "text"], doc="The variant of the pagination.")
+ variant = param.Selector(default="text", objects=["outlined", "text"], doc="The variant of the pagination.")
width = param.Integer(default=None, doc="The width of the pagination.")