Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/panel_material_ui/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.""")
Expand Down Expand Up @@ -174,7 +178,8 @@ class MaterialComponent(ReactComponent):
"notistack": "https://esm.sh/[email protected]"
}
}
_rename = {'loading': 'loading'}
_rename = {'loading': 'loading', 'attached': 'elements'}
_source_transforms = {'attached': None}

__abstract = True

Expand Down Expand Up @@ -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 (
Expand All @@ -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
Expand Down
14 changes: 14 additions & 0 deletions src/panel_material_ui/layout/Drawer.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<Drawer anchorEl={anchorEl} open={open} onOpen={() => setOpen(true)} onClose={() => setOpen(false)}>
{objects}
</Drawer>
)
}
25 changes: 24 additions & 1 deletion src/panel_material_ui/layout/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -475,5 +497,6 @@ class Dialog(MaterialListLike):
"Divider",
"Alert",
"Backdrop",
"Dialog"
"Dialog",
"Drawer"
]
39 changes: 39 additions & 0 deletions src/panel_material_ui/widgets/Menu.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<Menu
anchorEl={anchorEl}
container={view.container}
dense={dense}
onClose={() => setOpen(false)}
open={open}
sx={sx}
>
{keys.map((name) => {
const item = items[name]
if (item === null || item.label === "---") {
return <Divider />
}
const label = item.label || name
const props = {key: name, onClick: () => { model.send_msg(name) }}
return (
<MenuItem {...props}>
{item.icon ? <Icon>{item.icon}</Icon> : null}
{label}
</MenuItem>
)
})}
</Menu>
)
}
28 changes: 25 additions & 3 deletions src/panel_material_ui/widgets/menus.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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.
Expand All @@ -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,
Expand Down Expand Up @@ -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.")

Expand Down
Loading