Browse Source

new requirements

namnguyen 7 months ago
parent
commit
4d77679cf2

+ 26 - 0
doc/gui/examples/controls/menu_inactive.py

@@ -0,0 +1,26 @@
+# Copyright 2021-2024 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+# -----------------------------------------------------------------------------------------
+# To execute this script, make sure that the taipy-gui package is installed in your
+# Python environment and run:
+#     python <script>
+# -----------------------------------------------------------------------------------------
+from taipy.gui import Gui
+
+options = [("a", "Option A"), ("b", "Option B"), ("c", "Option C"), ("d", "Option D")]
+
+
+page = """
+<|menu|lov={options}|not active|>
+"""
+
+if __name__ == "__main__":
+    Gui(page).run(title="Menu - Inactive")

+ 26 - 0
doc/gui/examples/controls/menu_inactive_options.py

@@ -0,0 +1,26 @@
+# Copyright 2021-2024 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+# -----------------------------------------------------------------------------------------
+# To execute this script, make sure that the taipy-gui package is installed in your
+# Python environment and run:
+#     python <script>
+# -----------------------------------------------------------------------------------------
+from taipy.gui import Gui
+
+options = [("a", "Option A"), ("b", "Option B"), ("c", "Option C"), ("d", "Option D")]
+inactive_options = ["b", "d"]
+
+page = """
+<|menu|lov={options}|inactive_ids={inactive_options}|>
+"""
+
+if __name__ == "__main__":
+    Gui(page).run(title="Menu - Inactive options")

+ 26 - 0
doc/gui/examples/controls/menu_label.py

@@ -0,0 +1,26 @@
+# Copyright 2021-2024 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+# -----------------------------------------------------------------------------------------
+# To execute this script, make sure that the taipy-gui package is installed in your
+# Python environment and run:
+#     python <script>
+# -----------------------------------------------------------------------------------------
+from taipy.gui import Gui
+
+options = [("a", "Option A"), ("b", "Option B"), ("c", "Option C"), ("d", "Option D")]
+
+
+page = """
+<|menu|label=menu|lov={options}|>
+"""
+
+if __name__ == "__main__":
+    Gui(page).run(title="Menu - Label")

+ 27 - 0
doc/gui/examples/controls/menu_selected.py

@@ -0,0 +1,27 @@
+# Copyright 2021-2024 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+# -----------------------------------------------------------------------------------------
+# To execute this script, make sure that the taipy-gui package is installed in your
+# Python environment and run:
+#     python <script>
+# -----------------------------------------------------------------------------------------
+from taipy.gui import Gui
+
+options = [("a", "Option A"), ("b", "Option B"), ("c", "Option C"), ("d", "Option D")]
+selected = ["a", "b"]
+
+
+page = """
+<|menu|lov={options}|selected={selected}|>
+"""
+
+if __name__ == "__main__":
+    Gui(page).run(title="Menu - Selected")

+ 26 - 0
doc/gui/examples/controls/menu_simple.py

@@ -0,0 +1,26 @@
+# Copyright 2021-2024 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+# -----------------------------------------------------------------------------------------
+# To execute this script, make sure that the taipy-gui package is installed in your
+# Python environment and run:
+#     python <script>
+# -----------------------------------------------------------------------------------------
+from taipy.gui import Gui
+
+options = [("a", "Option A"), ("b", "Option B"), ("c", "Option C"), ("d", "Option D")]
+
+
+page = """
+<|menu|lov={options}|>
+"""
+
+if __name__ == "__main__":
+    Gui(page).run(title="Menu - Simple")

+ 31 - 0
doc/gui/examples/menu_on_action.py

@@ -0,0 +1,31 @@
+# Copyright 2021-2024 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+# -----------------------------------------------------------------------------------------
+# To execute this script, make sure that the taipy-gui package is installed in your
+# Python environment and run:
+#     python <script>
+# -----------------------------------------------------------------------------------------
+from taipy.gui import Gui
+
+options = [("a", "Option A"), ("b", "Option B"), ("c", "Option C"), ("d", "Option D")]
+selected = ["a", "b"]
+
+def menu_action(_1, _2, payload):
+    selected_options = payload["args"]
+    for option in selected_options:
+        print(f"Selected: {option}") # noqa: F401, T201
+
+page = """
+<|menu|lov={options}|selected={selected}|on_action=menu_action|>
+"""
+
+if __name__ == "__main__":
+    Gui(page).run(title="Menu - On Action")

+ 59 - 28
frontend/taipy-gui/src/components/Taipy/Menu.tsx

@@ -20,20 +20,21 @@ import Avatar from "@mui/material/Avatar";
 import CardHeader from "@mui/material/CardHeader";
 import ListItemAvatar from "@mui/material/ListItemAvatar";
 import Box from "@mui/material/Box";
-import Tooltip from '@mui/material/Tooltip';
+import Tooltip from "@mui/material/Tooltip";
 import { Theme, useTheme } from "@mui/system";
 
-import { SingleItem } from "./lovUtils";
+import { LovImage } from "./lovUtils";
 import { createSendActionNameAction } from "../../context/taipyReducers";
 import { MenuProps } from "../../utils/lov";
 import { useClassNames, useDispatch, useModule } from "../../utils/hooks";
-import { emptyArray } from "../../utils";
+import { emptyArray, getInitials } from "../../utils";
 import { getComponentClassName } from "./TaipyStyle";
 
 const boxDrawerStyle = { overflowX: "hidden" } as CSSProperties;
 const headerSx = { padding: 0 };
 const avatarSx = { bgcolor: (theme: Theme) => theme.palette.text.primary };
 const baseTitleProps = { noWrap: true, variant: "h6" } as const;
+const cardSx = { p: 0 } as CSSProperties;
 
 const Menu = (props: MenuProps) => {
     const { label, onAction = "", lov, width, inactiveIds = emptyArray, active = true } = props;
@@ -49,13 +50,17 @@ const Menu = (props: MenuProps) => {
         (evt: MouseEvent<HTMLElement>) => {
             if (active) {
                 const { id: key = "" } = evt.currentTarget.dataset;
-                setSelectedValue(() => {
-                    dispatch(createSendActionNameAction("menu", module, onAction, key));
-                    return key;
-                });
+                if (props.selected) {
+                    dispatch(createSendActionNameAction("menu", module, onAction, ...props.selected));
+                } else {
+                    setSelectedValue(() => {
+                        dispatch(createSendActionNameAction("menu", module, onAction, key));
+                        return key;
+                    });
+                }
             }
         },
-        [onAction, dispatch, active, module]
+        [active, props.selected, dispatch, module, onAction]
     );
 
     const openHandler = useCallback((evt: MouseEvent<HTMLElement>) => {
@@ -65,21 +70,29 @@ const Menu = (props: MenuProps) => {
 
     const [drawerSx, titleProps] = useMemo(() => {
         const drawerWidth = opened ? width : `calc(${theme.spacing(9)} + 1px)`;
-        const titleWidth = opened ? `calc(${width} - ${theme.spacing(10)})`: undefined;
-        return [{
-            width: drawerWidth,
-            flexShrink: 0,
-            "& .MuiDrawer-paper": {
+        const titleWidth = opened ? `calc(${width} - ${theme.spacing(10)})` : undefined;
+        return [
+            {
                 width: drawerWidth,
-                boxSizing: "border-box",
+                flexShrink: 0,
+                "& .MuiDrawer-paper": {
+                    width: drawerWidth,
+                    boxSizing: "border-box",
+                    transition: "width 0.3s",
+                },
                 transition: "width 0.3s",
             },
-            transition: "width 0.3s",
-        }, {...baseTitleProps, width: titleWidth}];
+            { ...baseTitleProps, width: titleWidth },
+        ];
     }, [opened, width, theme]);
 
     return lov && lov.length ? (
-        <Drawer variant="permanent" anchor="left" sx={drawerSx} className={`${className} ${getComponentClassName(props.children)}`}>
+        <Drawer
+            variant="permanent"
+            anchor="left"
+            sx={drawerSx}
+            className={`${className} ${getComponentClassName(props.children)}`}
+        >
             <Box style={boxDrawerStyle}>
                 <List>
                     <ListItemButton key="taipy_menu_0" onClick={openHandler}>
@@ -87,9 +100,11 @@ const Menu = (props: MenuProps) => {
                             <CardHeader
                                 sx={headerSx}
                                 avatar={
-                                    <Tooltip title={label || false}><Avatar sx={avatarSx}>
-                                        <MenuIco />
-                                    </Avatar></Tooltip>
+                                    <Tooltip title={label || false}>
+                                        <Avatar sx={avatarSx}>
+                                            <MenuIco />
+                                        </Avatar>
+                                    </Tooltip>
                                 }
                                 title={label}
                                 titleTypographyProps={titleProps}
@@ -97,16 +112,32 @@ const Menu = (props: MenuProps) => {
                         </ListItemAvatar>
                     </ListItemButton>
                     {lov.map((elt) => (
-                        <SingleItem
+                        <ListItemButton
                             key={elt.id}
-                            value={elt.id}
-                            item={elt.item}
-                            selectedValue={selectedValue}
-                            clickHandler={clickHandler}
+                            onClick={clickHandler}
+                            data-id={elt.id}
+                            selected={props.selected ? props.selected.includes(elt.id) : selectedValue === elt.id}
                             disabled={!active || inactiveIds.includes(elt.id)}
-                            withAvatar={true}
-                            titleTypographyProps={titleProps}
-                        />
+                        >
+                            {typeof elt.item === "string" ? (
+                                <ListItemAvatar>
+                                    <CardHeader
+                                        sx={cardSx}
+                                        avatar={
+                                            <Tooltip title={elt.item}>
+                                                <Avatar sx={avatarSx}>{getInitials(elt.item)}</Avatar>
+                                            </Tooltip>
+                                        }
+                                        title={elt.item}
+                                        titleTypographyProps={titleProps}
+                                    />
+                                </ListItemAvatar>
+                            ) : (
+                                <ListItemAvatar>
+                                    <LovImage item={elt.item} titleTypographyProps={titleProps} />
+                                </ListItemAvatar>
+                            )}
+                        </ListItemButton>
                     ))}
                 </List>
             </Box>

+ 12 - 9
frontend/taipy-gui/src/components/Taipy/MenuCtl.tsx

@@ -14,7 +14,14 @@
 import React, { useMemo, useEffect } from "react";
 
 import { LovProps, useLovListMemo } from "./lovUtils";
-import { useClassNames, useDispatch, useDispatchRequestUpdateOnFirstRender, useDynamicProperty, useIsMobile, useModule } from "../../utils/hooks";
+import {
+    useClassNames,
+    useDispatch,
+    useDispatchRequestUpdateOnFirstRender,
+    useDynamicProperty,
+    useIsMobile,
+    useModule,
+} from "../../utils/hooks";
 import { createSetMenuAction } from "../../context/taipyReducers";
 import { MenuProps } from "../../utils/lov";
 
@@ -25,17 +32,11 @@ interface MenuCtlProps extends LovProps<string> {
     onAction?: string;
     inactiveIds?: string[];
     defaultInactiveIds?: string;
+    selected?: string[];
 }
 
 const MenuCtl = (props: MenuCtlProps) => {
-    const {
-        id,
-        label,
-        onAction,
-        defaultLov = "",
-        width = "15vw",
-        width_Mobile_ = "85vw",
-    } = props;
+    const { id, label, onAction, defaultLov = "", width = "15vw", width_Mobile_ = "85vw" } = props;
     const dispatch = useDispatch();
     const isMobile = useIsMobile();
     const module = useModule();
@@ -71,6 +72,7 @@ const MenuCtl = (props: MenuCtlProps) => {
                 inactiveIds: inactiveIds,
                 width: isMobile ? width_Mobile_ : width,
                 className: className,
+                selected: props.selected,
             } as MenuProps)
         );
         return () => dispatch(createSetMenuAction({}));
@@ -85,6 +87,7 @@ const MenuCtl = (props: MenuCtlProps) => {
         isMobile,
         className,
         dispatch,
+        props.selected,
     ]);
 
     return <></>;

+ 1 - 0
frontend/taipy-gui/src/utils/lov.ts

@@ -33,4 +33,5 @@ export interface MenuProps extends TaipyBaseProps {
     inactiveIds?: string[];
     lov?: LovItem[];
     active?: boolean;
+    selected?: string[];
 }

+ 5 - 4
taipy/gui/_renderers/factory.py

@@ -335,14 +335,15 @@ class _Factory:
         )
         .set_attributes(
             [
-                ("active", PropertyType.dynamic_boolean, True),
+                ("lov", PropertyType.lov),
                 ("label",),
-                ("width",),
-                ("width[mobile]",),
                 ("on_action", PropertyType.function),
+                ("selected", PropertyType.lov_value, "optional"),
                 ("inactive_ids", PropertyType.dynamic_list),
+                ("active", PropertyType.dynamic_boolean, True),
                 ("hover_text", PropertyType.dynamic_string),
-                ("lov", PropertyType.lov),
+                ("width",),
+                ("width[mobile]",),
             ]
         )
         ._set_propagate(),

+ 35 - 25
taipy/gui/viselements.json

@@ -1395,7 +1395,7 @@
                         "type": "dynamic(str)",
                         "doc": "The title of the progress indicator."
                     },
-                                        {
+                    {
                         "name": "title_anchor",
                         "type": "str",
                         "default_value": "\"bottom\"",
@@ -1490,6 +1490,40 @@
                         "type": "dynamic(Union[str,list[Union[str,Icon,Any]]])",
                         "doc": "The list of menu option values."
                     },
+                    {
+                        "name": "label",
+                        "type": "str",
+                        "doc": "The title of the menu."
+                    },
+                    {
+                        "name": "on_action",
+                        "type": "Union[str, Callable]",
+                        "doc": "A function or the name of a function that is triggered when a menu option is selected.<br/><br/>This function is invoked with the following parameters:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li><li>id (str): the identifier of the button, if it has one.</li><li>payload (dict): a dictionary containing details about the callback invocation, with the following keys:<ul>\n<li>action: the name of the action that triggered this callback.</li><li>args: a list where the first element contains the identifier of the selected option.</li></ul></li></ul>",
+                        "signature": [
+                            [
+                                "state",
+                                "State"
+                            ],
+                            [
+                                "id",
+                                "str"
+                            ],
+                            [
+                                "payload",
+                                "dict"
+                            ]
+                        ]
+                    },
+                    {
+                        "name": "selected",
+                        "type": "dynamic(Union[str,list[str]])",
+                        "doc": "Semicolon (';')-separated list or a list of menu items identifiers that are selected."
+                    },
+                    {
+                        "name": "inactive_ids",
+                        "type": "dynamic(Union[str,list[str]])",
+                        "doc": "Semicolon (';')-separated list or a list of menu items identifiers that are disabled."
+                    },
                     {
                         "name": "adapter",
                         "type": "Union[str, Callable]",
@@ -1507,11 +1541,6 @@
                         "type": "str",
                         "doc": "The title of the menu."
                     },
-                    {
-                        "name": "inactive_ids",
-                        "type": "dynamic(Union[str,list[str]])",
-                        "doc": "Semicolon (';')-separated list or a list of menu items identifiers that are disabled."
-                    },
                     {
                         "name": "width",
                         "type": "str",
@@ -1523,25 +1552,6 @@
                         "type": "str",
                         "default_value": "\"85vw\"",
                         "doc": "The width of the menu when unfolded, in CSS units, when running on a mobile device."
-                    },
-                    {
-                        "name": "on_action",
-                        "type": "Union[str, Callable]",
-                        "doc": "A function or the name of a function that is triggered when a menu option is selected.<br/><br/>This function is invoked with the following parameters:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li><li>id (str): the identifier of the button, if it has one.</li><li>payload (dict): a dictionary containing details about the callback invocation, with the following keys:<ul>\n<li>action: the name of the action that triggered this callback.</li><li>args: a list where the first element contains the identifier of the selected option.</li></ul></li></ul>",
-                        "signature": [
-                            [
-                                "state",
-                                "State"
-                            ],
-                            [
-                                "id",
-                                "str"
-                            ],
-                            [
-                                "payload",
-                                "dict"
-                            ]
-                        ]
                     }
                 ]
             }