Skip to content
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

Align component APIs with Panel #50

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
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
25 changes: 23 additions & 2 deletions src/panel_material_ui/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,30 @@
const [defaultTheme] = props.model.useState('theme')

const theme = createTheme({{
cssVariables: {{
rootSelector: ':host',
colorSchemeSelector: 'class',
}},
colorSchemes: {{
dark: defaultTheme === "dark",
}},
components: {{
MuiPopover: {{
defaultProps: {{
container: props.view.container,
}},
}},
MuiPopper: {{
defaultProps: {{
container: props.view.container,
}},
}},
MuiModal: {{
defaultProps: {{
container: props.view.container,
}},
}},
}}
}});

return (
Expand All @@ -44,8 +65,8 @@ class MaterialComponent(ReactComponent):

_importmap = {
"imports": {
"@mui/material/": "https://esm.sh/@mui/material@6.1.7/",
"@mui/icons-material/": "https://esm.sh/@mui/icons-material@6.1.7/",
"@mui/material/": "https://esm.sh/@mui/material@6.4.0/",
"@mui/icons-material/": "https://esm.sh/@mui/icons-material@6.4.0/",
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/panel_material_ui/chat/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class ChatAreaInput(TextAreaInput):

rows = param.Integer(default=1)

_esm = "ChatArea.jsx"
_esm_base = "ChatArea.jsx"

def _handle_msg(self, msg) -> None:
"""
Expand Down
6 changes: 1 addition & 5 deletions src/panel_material_ui/layout/Dialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,8 @@ export function render({model, view}) {
const [title] = model.useState("title");
const objects = model.get_child("objects");

model.on("after_render", () => {
document.head.append(...view.style_cache.children)
})

return (
<Dialog open={open} fullScreen={full_screen}>
<Dialog open={open} fullScreen={full_screen} container={view.container}>
<DialogTitle>
{title}
</DialogTitle>
Expand Down
16 changes: 8 additions & 8 deletions src/panel_material_ui/layout/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class Paper(MaterialListLike):

elevation = param.Integer(default=1, bounds=(0, None))

_esm = "Paper.jsx"
_esm_base = "Paper.jsx"


class Card(MaterialListLike):
Expand Down Expand Up @@ -101,7 +101,7 @@ class Card(MaterialListLike):

outlined = param.Boolean(default=False)

_esm = "Card.jsx"
_esm_base = "Card.jsx"

def select(self, selector: type | Callable[[Viewable], bool] | None = None) -> list[Viewable]:
return ([] if self.header is None else self.header.select(selector)) + super().select(selector)
Expand Down Expand Up @@ -142,7 +142,7 @@ class Accordion(MaterialNamedListLike):

_names = param.List(default=[])

_esm = "Accordion.jsx"
_esm_base = "Accordion.jsx"

def __init__(self, *objects, **params):
if "objects" not in params:
Expand Down Expand Up @@ -191,7 +191,7 @@ class Tabs(MaterialNamedListLike):

_names = param.List(default=[])

_esm = "Tabs.jsx"
_esm_base = "Tabs.jsx"

def __init__(self, *objects, **params):
if "objects" not in params:
Expand Down Expand Up @@ -233,7 +233,7 @@ class Divider(MaterialListLike):

variant = param.Selector(default="fullWidth", objects=["fullWidth", "inset", "middle"])

_esm = "Divider.jsx"
_esm_base = "Divider.jsx"


class Alert(MaterialListLike):
Expand All @@ -250,7 +250,7 @@ class Alert(MaterialListLike):

variant = param.Selector(default="filled", objects=["filled", "outlined"])

_esm = "Alert.jsx"
_esm_base = "Alert.jsx"


class Backdrop(MaterialListLike):
Expand All @@ -271,7 +271,7 @@ class Backdrop(MaterialListLike):

open = param.Boolean(default=False)

_esm = "Backdrop.jsx"
_esm_base = "Backdrop.jsx"


class Dialog(MaterialListLike):
Expand All @@ -296,4 +296,4 @@ class Dialog(MaterialListLike):

title = param.String(default="")

_esm = "Dialog.jsx"
_esm_base = "Dialog.jsx"
6 changes: 3 additions & 3 deletions src/panel_material_ui/pane/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Chip(MaterialComponent):

variant = param.Selector(objects=["filled", "outlined"], default="filled")

_esm = "Chip.jsx"
_esm_base = "Chip.jsx"

_stylesheets = ["https://fonts.googleapis.com/icon?family=Material+Icons"]

Expand All @@ -42,7 +42,7 @@ class Avatar(MaterialComponent):

variant = param.Selector(objects=["rounded", "square"], default="rounded")

_esm = "Avatar.jsx"
_esm_base = "Avatar.jsx"


class Skeleton(MaterialComponent):
Expand All @@ -52,4 +52,4 @@ class Skeleton(MaterialComponent):

width = param.Integer(default=0)

_esm = "Skeleton.jsx"
_esm_base = "Skeleton.jsx"
2 changes: 1 addition & 1 deletion src/panel_material_ui/template/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ class AppBar(MaterialListLike):

position = param.Selector(default="static", objects=["fixed", "static", "sticky"])

_esm = "AppBar.jsx"
_esm_base = "AppBar.jsx"

_stylesheets = ["https://fonts.googleapis.com/icon?family=Material+Icons"]
66 changes: 66 additions & 0 deletions src/panel_material_ui/widgets/MultiChoice.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import Box from "@mui/material/Box";
import OutlinedInput from "@mui/material/OutlinedInput";
import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem";
import FormControl from "@mui/material/FormControl";
import Select from "@mui/material/Select";
import Chip from "@mui/material/Chip";

const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;

const MenuProps = {
disablePortal: true, // Ensures menu doesn't render outside of shadow DOM
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
};

export function render({model, view}) {
const [disabled] = model.useState("disabled");
const [label] = model.useState("label");
const [options] = model.useState("options");
const [value, setValue] = model.useState("value");

const handleChange = (event) => {
const {
target: { value },
} = event;
setValue(
// On autofill we get a stringified value.
typeof value === "string" ? value.split(",") : value,
);
};

return (
<FormControl sx={{ m: 1, width: 300 }}>
<InputLabel>{label}</InputLabel>
<Select
multiple
value={value}
onChange={handleChange}
input={<OutlinedInput label={label} />}
renderValue={(selected) => (
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}>
{selected.map((value) => (
<Chip key={value} label={value} />
))}
</Box>
)}
MenuProps={MenuProps}
>
{options.map((name) => (
<MenuItem
key={name}
value={name}
>
{name}
</MenuItem>
))}
</Select>
</FormControl>
);
}
21 changes: 18 additions & 3 deletions src/panel_material_ui/widgets/Slider.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@ import Slider from "@mui/material/Slider"
import Typography from "@mui/material/Typography"

export function render({model}) {
const [bar_color] = model.useState("bar_color")
const [color] = model.useState("color")
const [disabled] = model.useState("disabled")
const [end] = model.useState("end")
const [format] = model.useState("format")
const [label] = model.useState("label")
const [orientation] = model.useState("orientation")
const [show_value] = model.useState("show_value")
const [start] = model.useState("start")
const [step] = model.useState("step")
const [tooltips] = model.useState("tooltips")
const [track] = model.useState("track")
const [value, setValue] = model.useState("value")
const [value_throttled, setValueThrottled] = model.useState("value_throttled")

const [value_label, setValueLabel] = React.useState()

Expand All @@ -33,9 +36,11 @@ export function render({model}) {
<Box sx={{height: "100%"}}>
<Typography variant="body1">
{label && `${label}: `}
<strong>
{value_label}
</strong>
{ show_value &&
<strong>
{value_label}
</strong>
}
</Typography>
<Slider
value={value}
Expand All @@ -48,6 +53,16 @@ export function render({model}) {
orientation={orientation}
valueLabelDisplay={tooltips ? "auto" : "off"}
onChange={(event, newValue) => setValue(newValue)}
onChangeCommitted={(event, newValue) => setValueThrottled(newValue)}
sx={{
"& .MuiSlider-track": {
backgroundColor: bar_color,
borderColor: bar_color
},
"& .MuiSlider-rail": {
backgroundColor: bar_color,
},
}}
/>
</Box>
)
Expand Down
2 changes: 2 additions & 0 deletions src/panel_material_ui/widgets/ToggleIcon.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export function render({model, el}) {
const [active_icon] = model.useState("active_icon")
const [color] = model.useState("color")
const [description] = model.useState("description")
const [description_delay] = model.useState("description_delay")
const [disabled] = model.useState("disabled")
const [icon] = model.useState("icon")
const [size] = model.useState("size")
Expand All @@ -29,6 +30,7 @@ export function render({model, el}) {
<Tooltip
title={description}
arrow
enterDelay={description_delay}
placement="right"
PopperProps={{
container: el
Expand Down
71 changes: 2 additions & 69 deletions src/panel_material_ui/widgets/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,6 @@ class Button(_ButtonBase, _ClickButton):
It also provides an additional `clicks` parameter, that can be
watched to subscribe to click events.

Some missing and extra features (if any) when comparing with the corresponding
panel Button widget [panel.widgets.Button](https://panel.holoviz.org/reference/widgets/Button.html):
- Missing features: description_delay
- Extra features: label, on_event, on_msg, tooltip (work in progress), theme

:Example:

>>> Button(name='Click me', icon='caret-right', button_type='primary')
Expand All @@ -87,7 +82,7 @@ class Button(_ButtonBase, _ClickButton):

value = param.Event(doc="Toggles from False to True while the event is being processed.")

_esm = "Button.jsx"
_esm_base = "Button.jsx"

_rename: ClassVar[Mapping[str, str | None]] = {"label": "label", "button_style": "button_style"}

Expand Down Expand Up @@ -133,12 +128,6 @@ class Toggle(_ButtonBase):

This widget is interchangeable with the `Checkbox` widget.

Some missing and extra features (if any) when comparing with the corresponding
panel Toggle widget [panel.widgets.Toggle](https://panel.holoviz.org/reference/widgets/Toggle.html):
- No missing features
- Extra features: clicks, description, label, on_event, on_msg, theme


:Example:

>>> Toggle(name='Toggle', button_type='success')
Expand All @@ -152,60 +141,4 @@ class Toggle(_ButtonBase):

value = param.Boolean(default=False)

_esm = "ToggleButton.jsx"


class ButtonIcon(_ButtonBase):
"""
The `ButtonIcon` widget facilitates event triggering upon button clicks.

This widget displays a default `icon` initially. Upon being clicked, an `active_icon` appears
for a specified `toggle_duration`.

For instance, the `ButtonIcon` can be effectively utilized to implement a feature akin to
ChatGPT's copy-to-clipboard button.

The button incorporates a `value` attribute, which alternates between `False` and `True` as the
click event is processed.

Furthermore, it includes an `clicks` attribute, enabling subscription to click events for
further actions or monitoring.

Some missing and extra features (if any) when comparing with the corresponding
panel ButtonIcon widget [panel.widgets.ButtonIcon](https://panel.holoviz.org/reference/widgets/ButtonIcon.html):
- Missing features: description_delay, js_on_click, on_click
- Extra features: button_style, button_type, edge, label, on_event, on_msg, theme

:Example:

>>> button_icon = ButtonIcon(
... icon='favorite',
... active_icon='check',
... description='Copy',
... toggle_duration=2000
... )
"""

active_icon = param.String(
default="",
doc="""
The name of the icon to display when toggled from
[tabler-icons.io](https://tabler-icons.io)/ or an SVG.""",
)

edge = param.Selector(objects=["start", "end", False], default=False)

size = param.String(
default="1em",
doc="""
Size of the icon as a string, e.g. 12px or 1em.""",
)

toggle_duration = param.Integer(
default=75,
doc="""
The number of milliseconds the active_icon should be shown for
and how long the button should be disabled for.""",
)

_esm = "IconButton.jsx"
_esm_base = "ToggleButton.jsx"
Loading
Loading