Przeglądaj źródła

chat control (#1092)

* chat control
resolves #1044

* WiP use data type and pagination for messages

* pagination WiP

* fix tests

* manage new message indicator

* cleaning

* use rowCount

* make it work

* fix js tests

* fix doc

* fix tests

* managing load more

* pagesize in data key

---------

Co-authored-by: Fred Lefévère-Laoide <Fred.Lefevere-Laoide@Taipy.io>
Fred Lefévère-Laoide 1 rok temu
rodzic
commit
3bf293a8fc

+ 2 - 2
frontend/taipy-gui/jest.config.js

@@ -11,11 +11,11 @@
  * specific language governing permissions and limitations under the License.
  */
 
-/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
+/** @type {import('ts-jest/dist/types').JestConfigWithTsJest} */
 module.exports = {
  // testEnvironment: 'jest-environment-jsdom',
   preset: 'ts-jest',
   testEnvironment: 'jsdom',
-  setupFiles: ['./test-config/jest.env.js', './test-config/createObjectUrl.js', './test-config/Canvas.js', './test-config/mockFileUpload.js'],
+  setupFiles: ['./test-config/jest.env.js', './test-config/createObjectUrl.js', './test-config/Canvas.js', './test-config/mockFileUpload.js', './test-config/intersectionObserver.js'],
   coverageReporters: ["json", "html", "text"],
 };

Plik diff jest za duży
+ 216 - 311
frontend/taipy-gui/package-lock.json


+ 3 - 3
frontend/taipy-gui/package.json

@@ -74,7 +74,7 @@
   },
   "devDependencies": {
     "@testing-library/jest-dom": "^6.1.3",
-    "@testing-library/react": "^14.0.0",
+    "@testing-library/react": "^15.0.7",
     "@testing-library/user-event": "^14.2.1",
     "@types/css-mediaquery": "^0.1.1",
     "@types/jest": "^29.0.1",
@@ -94,11 +94,11 @@
     "autoprefixer": "^10.4.0",
     "copy-webpack-plugin": "^12.0.1",
     "cross-env": "^7.0.3",
-    "css-loader": "^6.5.0",
+    "css-loader": "^7.1.0",
     "css-mediaquery": "^0.1.2",
     "dotenv-webpack": "^8.0.0",
     "dts-bundle-generator": "^9.2.1",
-    "eslint": "^8.3.0",
+    "eslint": "^8.57.0",
     "eslint-plugin-react": "^7.26.1",
     "eslint-plugin-react-hooks": "^4.2.0",
     "eslint-plugin-tsdoc": "^0.2.16",

+ 121 - 0
frontend/taipy-gui/src/components/Taipy/Chat.spec.tsx

@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+import React from "react";
+import { render } from "@testing-library/react";
+import "@testing-library/jest-dom";
+import userEvent from "@testing-library/user-event";
+
+import Chat from "./Chat";
+import { INITIAL_STATE, TaipyState } from "../../context/taipyReducers";
+import { TaipyContext } from "../../context/taipyContext";
+import { stringIcon } from "../../utils/icon";
+import { TableValueType } from "./tableUtils";
+
+const valueKey = "Infinite-Entity--asc";
+const messages: TableValueType = {
+    [valueKey]: {
+        data: [
+    ["1", "msg 1", "Fred"],
+    ["2", "msg From Another unknown User", "Fredo"],
+    ["3", "This from the sender User", "taipy"],
+    ["4", "And from another known one", "Fredi"],
+], rowcount: 4, start: 0}};
+const user1: [string, stringIcon] = ["Fred", { path: "/images/favicon.png", text: "Fred.png" }];
+const user2: [string, stringIcon] = ["Fredi", { path: "/images/fred.png", text: "Fredi.png" }];
+const users = [user1, user2];
+
+const searchMsg = messages[valueKey].data[0][1];
+
+describe("Chat Component", () => {
+    it("renders", async () => {
+        const { getByText, getByLabelText } = render(<Chat messages={messages} defaultKey={valueKey} />);
+        const elt = getByText(searchMsg);
+        expect(elt.tagName).toBe("DIV");
+        const input = getByLabelText("message (taipy)");
+        expect(input.tagName).toBe("INPUT");
+    });
+    it("uses the class", async () => {
+        const { getByText } = render(<Chat messages={messages} className="taipy-chat" defaultKey={valueKey} />);
+        const elt = getByText(searchMsg);
+        expect(elt.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass("taipy-chat");
+    });
+    it("can display an avatar", async () => {
+        const { getByAltText } = render(<Chat messages={messages} users={users} defaultKey={valueKey} />);
+        const elt = getByAltText("Fred.png");
+        expect(elt.tagName).toBe("IMG");
+    });
+    it("is disabled", async () => {
+        const { getAllByRole } = render(<Chat messages={messages} active={false} defaultKey={valueKey} />);
+        const elts = getAllByRole("button");
+        elts.forEach((elt) => expect(elt).toHaveClass("Mui-disabled"));
+    });
+    it("is enabled by default", async () => {
+        const { getAllByRole } = render(<Chat messages={messages} defaultKey={valueKey} />);
+        const elts = getAllByRole("button");
+        elts.forEach((elt) => expect(elt).not.toHaveClass("Mui-disabled"));
+    });
+    it("is enabled by active", async () => {
+        const { getAllByRole } = render(<Chat messages={messages} active={true} defaultKey={valueKey} />);
+        const elts = getAllByRole("button");
+        elts.forEach((elt) => expect(elt).not.toHaveClass("Mui-disabled"));
+    });
+    it("can hide input", async () => {
+        render(<Chat messages={messages} withInput={false} className="taipy-chat" defaultKey={valueKey} />);
+        const elt = document.querySelector(".taipy-chat input");
+        expect(elt).toBeNull();
+    });
+    it("dispatch a well formed message by Keyboard", async () => {
+        const dispatch = jest.fn();
+        const state: TaipyState = INITIAL_STATE;
+        const { getByLabelText } = render(
+            <TaipyContext.Provider value={{ state, dispatch }}>
+                <Chat messages={messages} updateVarName="varname" defaultKey={valueKey} />
+            </TaipyContext.Provider>
+        );
+        const elt = getByLabelText("message (taipy)");
+        await userEvent.click(elt);
+        await userEvent.keyboard("new message{Enter}");
+        expect(dispatch).toHaveBeenCalledWith({
+            type: "SEND_ACTION_ACTION",
+            name: "",
+            context: undefined,
+            payload: {
+                action: undefined,
+                args: ["Enter", "varname", "new message", "taipy"],
+            },
+        });
+    });
+    it("dispatch a well formed message by button", async () => {
+        const dispatch = jest.fn();
+        const state: TaipyState = INITIAL_STATE;
+        const { getByLabelText, getByRole } = render(
+            <TaipyContext.Provider value={{ state, dispatch }}>
+                <Chat messages={messages} updateVarName="varname" defaultKey={valueKey} />
+            </TaipyContext.Provider>
+        );
+        const elt = getByLabelText("message (taipy)");
+        await userEvent.click(elt);
+        await userEvent.keyboard("new message");
+        await userEvent.click(getByRole("button"))
+        expect(dispatch).toHaveBeenCalledWith({
+            type: "SEND_ACTION_ACTION",
+            name: "",
+            context: undefined,
+            payload: {
+                action: undefined,
+                args: ["click", "varname", "new message", "taipy"],
+            },
+        });
+    });
+});

+ 413 - 0
frontend/taipy-gui/src/components/Taipy/Chat.tsx

@@ -0,0 +1,413 @@
+/*
+ * 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.
+ */
+
+import React, { useMemo, useCallback, KeyboardEvent, MouseEvent, useState, useRef, useEffect, ReactNode } from "react";
+import { SxProps, Theme, darken, lighten } from "@mui/material/styles";
+import Avatar from "@mui/material/Avatar";
+import Box from "@mui/material/Box";
+import Button from "@mui/material/Button";
+import Chip from "@mui/material/Chip";
+import Grid from "@mui/material/Grid";
+import IconButton from "@mui/material/IconButton";
+import InputAdornment from "@mui/material/InputAdornment";
+import Paper from "@mui/material/Paper";
+import Popper from "@mui/material/Popper";
+import TextField from "@mui/material/TextField";
+import Tooltip from "@mui/material/Tooltip";
+import Send from "@mui/icons-material/Send";
+import ArrowDownward from "@mui/icons-material/ArrowDownward";
+import ArrowUpward from "@mui/icons-material/ArrowUpward";
+
+// import InfiniteLoader from "react-window-infinite-loader";
+
+import { createRequestInfiniteTableUpdateAction, createSendActionNameAction } from "../../context/taipyReducers";
+import { TaipyActiveProps, disableColor, getSuffixedClassNames } from "./utils";
+import { useClassNames, useDispatch, useDynamicProperty, useElementVisible, useModule } from "../../utils/hooks";
+import { LoVElt, useLovListMemo } from "./lovUtils";
+import { IconAvatar, avatarSx } from "../../utils/icon";
+import { getInitials } from "../../utils";
+import { RowType, TableValueType } from "./tableUtils";
+
+interface ChatProps extends TaipyActiveProps {
+    messages?: TableValueType;
+    withInput?: boolean;
+    users?: LoVElt[];
+    defaultUsers?: string;
+    onAction?: string;
+    senderId?: string;
+    height?: string;
+    defaultKey?: string; // for testing purposes only
+    pageSize?: number;
+}
+
+const ENTER_KEY = "Enter";
+
+const indicWidth = 0.7;
+const avatarWidth = 24;
+const chatAvatarSx = { ...avatarSx, width: avatarWidth, height: avatarWidth };
+const avatarColSx = { width: 1.5 * avatarWidth };
+const senderMsgSx = { width: "fit-content", maxWidth: "80%", marginLeft: "auto" };
+const gridSx = { pb: "1em", mt: "unset", flex: 1, overflow: "auto" };
+const loadMoreSx = { width: "fit-content", marginLeft: "auto", marginRight: "auto" };
+const inputSx = { maxWidth: "unset" };
+const nameSx = { fontSize: "0.6em", fontWeight: "bolder" };
+const senderPaperSx = {
+    pr: `${indicWidth}em`,
+    pl: `${indicWidth}em`,
+    mr: `${indicWidth}em`,
+    position: "relative",
+    "&:before": {
+        content: "''",
+        position: "absolute",
+        width: "0",
+        height: "0",
+        borderTopWidth: `${indicWidth}em`,
+        borderTopStyle: "solid",
+        borderTopColor: (theme: Theme) => theme.palette.background.paper,
+        borderLeft: `${indicWidth}em solid transparent`,
+        borderRight: `${indicWidth}em solid transparent`,
+        top: "0",
+        right: `-${indicWidth}em`,
+    },
+} as SxProps<Theme>;
+const otherPaperSx = {
+    position: "relative",
+    pl: `${indicWidth}em`,
+    pr: `${indicWidth}em`,
+    "&:before": {
+        content: "''",
+        position: "absolute",
+        width: "0",
+        height: "0",
+        borderTopWidth: `${indicWidth}em`,
+        borderTopStyle: "solid",
+        borderTopColor: (theme: Theme) => theme.palette.background.paper,
+        borderLeft: `${indicWidth}em solid transparent`,
+        borderRight: `${indicWidth}em solid transparent`,
+        top: "0",
+        left: `-${indicWidth}em`,
+    },
+} as SxProps<Theme>;
+const defaultBoxSx = {
+    pl: `${indicWidth}em`,
+    pr: `${indicWidth}em`,
+    backgroundColor: (theme: Theme) =>
+        theme.palette.mode == "dark"
+            ? lighten(theme.palette.background.paper, 0.05)
+            : darken(theme.palette.background.paper, 0.15),
+} as SxProps<Theme>;
+const noAnchorSx = { overflowAnchor: "none", "& *": { overflowAnchor: "none" } } as SxProps<Theme>;
+const anchorSx = { overflowAnchor: "auto", height: "1px", width: "100%" } as SxProps<Theme>;
+
+interface key2Rows {
+    key: string;
+}
+
+interface ChatRowProps {
+    senderId: string;
+    message: string;
+    name: string;
+    className?: string;
+    getAvatar: (id: string) => ReactNode;
+    index: number;
+}
+
+const ChatRow = (props: ChatRowProps) => {
+    const { senderId, message, name, className, getAvatar, index } = props;
+    return senderId == name ? (
+        <Grid item className={getSuffixedClassNames(className, "-sent")} xs={12} sx={noAnchorSx}>
+            <Box sx={senderMsgSx}>
+                <Paper sx={senderPaperSx} data-idx={index}>
+                    {message}
+                </Paper>
+            </Box>
+        </Grid>
+    ) : (
+        <Grid
+            item
+            container
+            className={getSuffixedClassNames(className, "-received")}
+            rowSpacing={0.2}
+            columnSpacing={1}
+            sx={noAnchorSx}
+        >
+            <Grid item sx={avatarColSx}></Grid>
+            <Grid item sx={nameSx}>
+                {name}
+            </Grid>
+            <Box width="100%" />
+            <Grid item sx={avatarColSx}>
+                {getAvatar(name)}
+            </Grid>
+            <Grid item>
+                <Paper sx={otherPaperSx} data-idx={index}>
+                    {message}
+                </Paper>
+            </Grid>
+        </Grid>
+    );
+};
+
+const getChatKey = (start: number, page: number) => `Chat-${start}-${start+page}`
+
+const Chat = (props: ChatProps) => {
+    const { id, updateVarName, senderId = "taipy", onAction, withInput = true, defaultKey = "", pageSize = 50 } = props;
+    const dispatch = useDispatch();
+    const module = useModule();
+
+    const [rows, setRows] = useState<RowType[]>([]);
+    const page = useRef<key2Rows>({ key: defaultKey });
+    const [rowCount, setRowCount] = useState(0);
+    const [columns, setColumns] = useState<Array<string>>([]);
+    const scrollDivRef = useRef<HTMLDivElement>(null);
+    const anchorDivRef = useRef<HTMLElement>(null);
+    const isAnchorDivVisible = useElementVisible(anchorDivRef);
+    const [showMessage, setShowMessage] = useState(false);
+    const [anchorPopup, setAnchorPopup] = useState<HTMLDivElement | null>(null);
+
+    const className = useClassNames(props.libClassName, props.dynamicClassName, props.className);
+    const active = useDynamicProperty(props.active, props.defaultActive, true);
+    const hover = useDynamicProperty(props.hoverText, props.defaultHoverText, undefined);
+    const users = useLovListMemo(props.users, props.defaultUsers || "");
+
+    const boxSx = useMemo(
+        () =>
+            props.height
+                ? ({
+                      ...defaultBoxSx,
+                      maxHeight: props.height,
+                      display: "flex",
+                      flexDirection: "column",
+                  } as SxProps<Theme>)
+                : defaultBoxSx,
+        [props.height]
+    );
+    const handleAction = useCallback(
+        (evt: KeyboardEvent<HTMLDivElement>) => {
+            if (!evt.shiftKey && !evt.ctrlKey && !evt.altKey && ENTER_KEY == evt.key) {
+                const elt = evt.currentTarget.querySelector("input");
+                if (elt?.value) {
+                    dispatch(
+                        createSendActionNameAction(id, module, onAction, evt.key, updateVarName, elt?.value, senderId)
+                    );
+                    elt.value = "";
+                }
+                evt.preventDefault();
+            }
+        },
+        [updateVarName, onAction, senderId, id, dispatch, module]
+    );
+
+    const handleClick = useCallback(
+        (evt: MouseEvent<HTMLButtonElement>) => {
+            const elt = evt.currentTarget.parentElement?.parentElement?.querySelector("input");
+            if (elt?.value) {
+                dispatch(
+                    createSendActionNameAction(id, module, onAction, "click", updateVarName, elt?.value, senderId)
+                );
+                elt.value = "";
+            }
+            evt.preventDefault();
+        },
+        [updateVarName, onAction, senderId, id, dispatch, module]
+    );
+
+    const avatars = useMemo(() => {
+        return users.reduce((pv, elt) => {
+            if (elt.id) {
+                pv[elt.id] =
+                    typeof elt.item == "string" ? (
+                        <Tooltip title={elt.item}>
+                            <Avatar sx={chatAvatarSx}>{getInitials(elt.item)}</Avatar>
+                        </Tooltip>
+                    ) : (
+                        <IconAvatar img={elt.item} sx={chatAvatarSx} />
+                    );
+            }
+            return pv;
+        }, {} as Record<string, React.ReactNode>);
+    }, [users]);
+
+    const getAvatar = useCallback(
+        (id: string) =>
+            avatars[id] || (
+                <Tooltip title={id}>
+                    <Avatar sx={chatAvatarSx}>{getInitials(id)}</Avatar>
+                </Tooltip>
+            ),
+        [avatars]
+    );
+
+    const loadMoreItems = useCallback(
+        (startIndex: number) => {
+            const key = getChatKey(startIndex, pageSize);
+            page.current = {
+                key: key,
+            };
+            dispatch(
+                createRequestInfiniteTableUpdateAction(
+                    updateVarName,
+                    id,
+                    module,
+                    [],
+                    key,
+                    startIndex,
+                    startIndex + pageSize,
+                    undefined,
+                    undefined,
+                    undefined,
+                    undefined,
+                    undefined,
+                    undefined,
+                    undefined,
+                    undefined,
+                    undefined,
+                    undefined,
+                    undefined,
+                    true // reverse
+                )
+            );
+        },
+        [pageSize, updateVarName, id, dispatch, module]
+    );
+
+    const showBottom = useCallback(() => {
+        anchorDivRef.current?.scrollIntoView();
+        setShowMessage(false);
+    }, []);
+
+    const refresh = typeof props.messages === "number";
+
+    useEffect(() => {
+        if (!refresh && props.messages && page.current.key && props.messages[page.current.key] !== undefined) {
+            const newValue = props.messages[page.current.key];
+            setRowCount(newValue.rowcount);
+            const nr = newValue.data as RowType[];
+            if (Array.isArray(nr) && nr.length > newValue.start && nr[newValue.start]) {
+                setRows((old) => {
+                    old.length && nr.length > old.length && setShowMessage(true);
+                    if (nr.length < old.length) {
+                        return nr.concat(old.slice(nr.length))
+                    }
+                    if (old.length > newValue.start) {
+                        return old.slice(0, newValue.start).concat(nr.slice(newValue.start));
+                    }
+                    return nr;
+                });
+                const cols = Object.keys(nr[newValue.start]);
+                setColumns(cols.length > 2 ? cols : cols.length == 2 ? [...cols, ""] : ["", ...cols, "", ""]);
+            }
+            page.current.key = getChatKey(0, pageSize);
+        }
+    }, [refresh, pageSize, props.messages]);
+
+    useEffect(() => {
+        if (showMessage && !isAnchorDivVisible) {
+            setAnchorPopup(scrollDivRef.current);
+            setTimeout(() => setShowMessage(false), 5000);
+        } else if (!showMessage) {
+            setAnchorPopup(null);
+        }
+    }, [showMessage, isAnchorDivVisible]);
+
+    useEffect(() => {
+        if (refresh) {
+            setTimeout(() => loadMoreItems(0), 1); // So that the state can be changed
+        }
+    }, [refresh, loadMoreItems]);
+
+    useEffect(() => {
+        loadMoreItems(0);
+    }, [loadMoreItems]);
+
+    const loadOlder = useCallback(
+        (evt: MouseEvent<HTMLElement>) => {
+            const { start } = evt.currentTarget.dataset;
+            if (start) {
+                loadMoreItems(parseInt(start));
+            }
+        },
+        [loadMoreItems]
+    );
+
+    return (
+        <Tooltip title={hover || "" || `rowCount: ${rowCount}`}>
+            <Paper className={className} sx={boxSx} id={id}>
+                <Grid container rowSpacing={2} sx={gridSx} ref={scrollDivRef}>
+                    {rows.length && !rows[0] ? (
+                        <Grid item className={getSuffixedClassNames(className, "-load")} xs={12} sx={noAnchorSx}>
+                            <Box sx={loadMoreSx}>
+                                <Button
+                                    endIcon={<ArrowUpward />}
+                                    onClick={loadOlder}
+                                    data-start={rows.length - rows.findIndex((row) => !!row)}
+                                >
+                                    Load More
+                                </Button>
+                            </Box>
+                        </Grid>
+                    ) : null}
+                    {rows.map((row, idx) =>
+                        row ? (
+                            <ChatRow
+                                key={columns[0] ? `${row[columns[0]]}` : `id${idx}`}
+                                senderId={senderId}
+                                message={`${row[columns[1]]}`}
+                                name={columns[2] ? `${row[columns[2]]}` : "Unknown"}
+                                className={className}
+                                getAvatar={getAvatar}
+                                index={idx}
+                            />
+                        ) : null
+                    )}
+                    <Box sx={anchorSx} ref={anchorDivRef} />
+                </Grid>
+                <Popper id={id} open={Boolean(anchorPopup)} anchorEl={anchorPopup} placement="right">
+                    <Chip
+                        label="A new message is available"
+                        variant="outlined"
+                        onClick={showBottom}
+                        icon={<ArrowDownward />}
+                    />
+                </Popper>
+                {withInput ? (
+                    <TextField
+                        margin="dense"
+                        fullWidth
+                        className={getSuffixedClassNames(className, "-input")}
+                        label={`message (${senderId})`}
+                        disabled={!active}
+                        onKeyDown={handleAction}
+                        InputProps={{
+                            endAdornment: (
+                                <InputAdornment position="end">
+                                    <IconButton
+                                        aria-label="send message"
+                                        onClick={handleClick}
+                                        edge="end"
+                                        disabled={!active}
+                                    >
+                                        <Send color={disableColor("primary", !active)} />
+                                    </IconButton>
+                                </InputAdornment>
+                            ),
+                        }}
+                        sx={inputSx}
+                    />
+                ) : null}
+            </Paper>
+        </Tooltip>
+    );
+};
+
+export default Chat;

+ 2 - 0
frontend/taipy-gui/src/components/Taipy/index.ts

@@ -13,6 +13,7 @@
 
 import { ComponentType } from "react";
 import Button from "./Button";
+import Chat from "./Chat";
 import Chart from "./Chart";
 import DateRange from "./DateRange";
 import DateSelector from "./DateSelector";
@@ -46,6 +47,7 @@ export const getRegisteredComponents = () => {
         Object.entries({
             a: Link,
             Button: Button,
+            Chat: Chat,
             Chart: Chart,
             DateRange: DateRange,
             DateSelector: DateSelector,

+ 2 - 0
frontend/taipy-gui/src/components/Taipy/utils.ts

@@ -111,3 +111,5 @@ export const getSuffixedClassNames = (names: string | undefined, suffix: string)
         .join(" ");
 
 export const emptyStyle = {} as CSSProperties;
+
+export const disableColor = <T>(color: T, disabled: boolean) => (disabled ? ("disabled" as T) : color);

+ 50 - 34
frontend/taipy-gui/src/context/taipyReducers.ts

@@ -582,8 +582,8 @@ const ligtenPayload = (payload: Record<string, unknown>) => {
             pv[key] = payload[key];
         }
         return pv;
-    }, {} as typeof payload)
-}
+    }, {} as typeof payload);
+};
 
 export const createRequestTableUpdateAction = (
     name: string | undefined,
@@ -605,21 +605,28 @@ export const createRequestTableUpdateAction = (
     compareDatas?: string,
     stateContext?: Record<string, unknown>
 ): TaipyAction =>
-    createRequestDataUpdateAction(name, id, context, columns, pageKey, ligtenPayload({
-        start: start,
-        end: end,
-        orderby: orderBy,
-        sort: sort,
-        aggregates: aggregates,
-        applies: applies,
-        styles: styles,
-        tooltips: tooltips,
-        handlenan: handleNan,
-        filters: filters,
-        compare: compare,
-        compare_datas: compareDatas,
-        state_context: stateContext,
-    }));
+    createRequestDataUpdateAction(
+        name,
+        id,
+        context,
+        columns,
+        pageKey,
+        ligtenPayload({
+            start: start,
+            end: end,
+            orderby: orderBy,
+            sort: sort,
+            aggregates: aggregates,
+            applies: applies,
+            styles: styles,
+            tooltips: tooltips,
+            handlenan: handleNan,
+            filters: filters,
+            compare: compare,
+            compare_datas: compareDatas,
+            state_context: stateContext,
+        })
+    );
 
 export const createRequestInfiniteTableUpdateAction = (
     name: string | undefined,
@@ -639,24 +646,33 @@ export const createRequestInfiniteTableUpdateAction = (
     filters?: Array<FilterDesc>,
     compare?: string,
     compareDatas?: string,
-    stateContext?: Record<string, unknown>
+    stateContext?: Record<string, unknown>,
+    reverse?: boolean
 ): TaipyAction =>
-    createRequestDataUpdateAction(name, id, context, columns, pageKey, ligtenPayload({
-        infinite: true,
-        start: start,
-        end: end,
-        orderby: orderBy,
-        sort: sort,
-        aggregates: aggregates,
-        applies: applies,
-        styles: styles,
-        tooltips: tooltips,
-        handlenan: handleNan,
-        filters: filters,
-        compare: compare,
-        compare_datas: compareDatas,
-        state_context: stateContext,
-    }));
+    createRequestDataUpdateAction(
+        name,
+        id,
+        context,
+        columns,
+        pageKey,
+        ligtenPayload({
+            infinite: true,
+            start: start,
+            end: end,
+            orderby: orderBy,
+            sort: sort,
+            aggregates: aggregates,
+            applies: applies,
+            styles: styles,
+            tooltips: tooltips,
+            handlenan: handleNan,
+            filters: filters,
+            compare: compare,
+            compare_datas: compareDatas,
+            state_context: stateContext,
+            reverse: !!reverse,
+        })
+    );
 
 /**
  * Create a *request data update* `Action` that will be used to update the `Context`.

+ 2 - 0
frontend/taipy-gui/src/extensions/exports.ts

@@ -27,6 +27,7 @@ import {
     useClassNames,
     useDispatchRequestUpdateOnFirstRender,
     useDispatch,
+    useDynamicJsonProperty,
     useDynamicProperty,
     useModule,
 } from "../utils/hooks";
@@ -53,6 +54,7 @@ export {
     useClassNames,
     useDispatchRequestUpdateOnFirstRender,
     useDispatch,
+    useDynamicJsonProperty,
     useDynamicProperty,
     useLovListMemo,
     useModule,

+ 25 - 4
frontend/taipy-gui/src/utils/hooks.ts

@@ -11,7 +11,7 @@
  * specific language governing permissions and limitations under the License.
  */
 
-import { Dispatch, useContext, useEffect, useMemo, useRef } from "react";
+import { Dispatch, RefObject, useContext, useEffect, useMemo, useRef, useState } from "react";
 import { useMediaQuery, useTheme } from "@mui/material";
 
 import { getUpdateVars } from "../components/Taipy/utils";
@@ -92,7 +92,7 @@ export const useDispatchRequestUpdateOnFirstRender = (
     forceRefresh?: boolean
 ) => {
     useEffect(() => {
-        const updateArray = getUpdateVars(updateVars).filter(uv => !uv.includes(","));
+        const updateArray = getUpdateVars(updateVars).filter((uv) => !uv.includes(","));
         varName && updateArray.push(varName);
         updateArray.length && dispatch(createRequestUpdateAction(id, context, updateArray, forceRefresh));
     }, [updateVars, dispatch, id, context, varName, forceRefresh]);
@@ -157,7 +157,7 @@ export const useClassNames = (libClassName?: string, dynamicClassName?: string,
 export const useWhyDidYouUpdate = (name: string, props: Record<string, unknown>): void => {
     // Get a mutable ref object where we can store props ...
     // ... for comparison next time this hook runs.
-    const previousProps = useRef({} as Record<string, unknown>);
+    const previousProps = useRef<Record<string, unknown>>();
     useEffect(() => {
         if (previousProps.current) {
             // Get all keys from previous and current props
@@ -167,7 +167,7 @@ export const useWhyDidYouUpdate = (name: string, props: Record<string, unknown>)
             // Iterate through keys
             allKeys.forEach((key) => {
                 // If previous is different from current
-                if (previousProps.current[key] !== props[key]) {
+                if (previousProps.current && previousProps.current[key] !== props[key]) {
                     // Add to changesObj
                     changesObj[key] = {
                         from: previousProps.current[key],
@@ -184,3 +184,24 @@ export const useWhyDidYouUpdate = (name: string, props: Record<string, unknown>)
         previousProps.current = props;
     });
 };
+
+export const useElementVisible = (ref: RefObject<HTMLElement>) => {
+    const observerRef = useRef<IntersectionObserver | null>(null);
+    const [isOnScreen, setIsOnScreen] = useState(false);
+
+    useEffect(() => {
+        observerRef.current = new IntersectionObserver(([entry]) => setIsOnScreen(entry.isIntersecting));
+    }, []);
+
+    useEffect(() => {
+        observerRef.current && ref.current && observerRef.current.observe(ref.current);
+
+        return () => {
+            observerRef.current && observerRef.current.disconnect();
+        };
+    }, [ref]);
+
+    return isOnScreen;
+};
+
+export const useUniqueId = (id?: string) => useMemo(() => (id ? id : new Date().toISOString() + Math.random()), [id]);

+ 2 - 2
frontend/taipy-gui/src/utils/icon.tsx

@@ -14,7 +14,7 @@
 import React, { useEffect, useMemo, useRef } from "react";
 import axios from "axios";
 import Avatar from "@mui/material/Avatar";
-import { SxProps, useTheme, Theme } from "@mui/system";
+import { SxProps, useTheme, Theme } from "@mui/material/styles";
 
 /**
  * An Icon representation.
@@ -39,7 +39,7 @@ interface IconProps {
     id?: string;
     img: Icon;
     className?: string;
-    sx?: SxProps;
+    sx?: SxProps<Theme>;
 }
 
 export const avatarSx = { bgcolor: (theme: Theme) => theme.palette.text.primary };

+ 23 - 0
frontend/taipy-gui/test-config/intersectionObserver.js

@@ -0,0 +1,23 @@
+class IntersectionObserver {
+    root = null;
+    rootMargin = "";
+    thresholds = [];
+
+    disconnect() {
+      return null;
+    }
+
+    observe() {
+      return null;
+    }
+
+    takeRecords() {
+      return [];
+    }
+
+    unobserve() {
+      return null;
+    }
+  }
+  window.IntersectionObserver = IntersectionObserver;
+  global.IntersectionObserver = IntersectionObserver;

+ 171 - 202
frontend/taipy/package-lock.json

@@ -43,15 +43,6 @@
     "../../taipy/gui/webapp": {
       "version": "3.2.0"
     },
-    "node_modules/@aashutoshrathi/word-wrap": {
-      "version": "1.2.6",
-      "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
-      "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/@babel/code-frame": {
       "version": "7.24.2",
       "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz",
@@ -84,19 +75,19 @@
       }
     },
     "node_modules/@babel/helper-validator-identifier": {
-      "version": "7.22.20",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
-      "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz",
+      "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==",
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/highlight": {
-      "version": "7.24.2",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz",
-      "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz",
+      "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==",
       "dependencies": {
-        "@babel/helper-validator-identifier": "^7.22.20",
+        "@babel/helper-validator-identifier": "^7.24.5",
         "chalk": "^2.4.2",
         "js-tokens": "^4.0.0",
         "picocolors": "^1.0.0"
@@ -170,9 +161,9 @@
       }
     },
     "node_modules/@babel/runtime": {
-      "version": "7.24.4",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz",
-      "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz",
+      "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==",
       "dependencies": {
         "regenerator-runtime": "^0.14.0"
       },
@@ -181,12 +172,12 @@
       }
     },
     "node_modules/@babel/types": {
-      "version": "7.24.0",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz",
-      "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==",
+      "version": "7.24.5",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz",
+      "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==",
       "dependencies": {
-        "@babel/helper-string-parser": "^7.23.4",
-        "@babel/helper-validator-identifier": "^7.22.20",
+        "@babel/helper-string-parser": "^7.24.1",
+        "@babel/helper-validator-identifier": "^7.24.5",
         "to-fast-properties": "^2.0.0"
       },
       "engines": {
@@ -414,28 +405,28 @@
       }
     },
     "node_modules/@floating-ui/core": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz",
-      "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==",
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.2.tgz",
+      "integrity": "sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==",
       "dependencies": {
-        "@floating-ui/utils": "^0.2.1"
+        "@floating-ui/utils": "^0.2.0"
       }
     },
     "node_modules/@floating-ui/dom": {
-      "version": "1.6.3",
-      "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz",
-      "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==",
+      "version": "1.6.5",
+      "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz",
+      "integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==",
       "dependencies": {
         "@floating-ui/core": "^1.0.0",
         "@floating-ui/utils": "^0.2.0"
       }
     },
     "node_modules/@floating-ui/react-dom": {
-      "version": "2.0.8",
-      "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz",
-      "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==",
+      "version": "2.0.9",
+      "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.9.tgz",
+      "integrity": "sha512-q0umO0+LQK4+p6aGyvzASqKbKOJcAHJ7ycE9CuUvfx3s9zTHWmGJTPOIlM/hmSBfUfg/XfY5YhLBLR/LHwShQQ==",
       "dependencies": {
-        "@floating-ui/dom": "^1.6.1"
+        "@floating-ui/dom": "^1.0.0"
       },
       "peerDependencies": {
         "react": ">=16.8.0",
@@ -443,9 +434,9 @@
       }
     },
     "node_modules/@floating-ui/utils": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz",
-      "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz",
+      "integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw=="
     },
     "node_modules/@humanwhocodes/config-array": {
       "version": "0.11.14",
@@ -652,18 +643,18 @@
       }
     },
     "node_modules/@mui/core-downloads-tracker": {
-      "version": "5.15.15",
-      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.15.tgz",
-      "integrity": "sha512-aXnw29OWQ6I5A47iuWEI6qSSUfH6G/aCsW9KmW3LiFqr7uXZBK4Ks+z8G+qeIub8k0T5CMqlT2q0L+ZJTMrqpg==",
+      "version": "5.15.18",
+      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.18.tgz",
+      "integrity": "sha512-/9pVk+Al8qxAjwFUADv4BRZgMpZM4m5E+2Q/20qhVPuIJWqKp4Ie4tGExac6zu93rgPTYVQGgu+1vjiT0E+cEw==",
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/mui-org"
       }
     },
     "node_modules/@mui/icons-material": {
-      "version": "5.15.15",
-      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.15.tgz",
-      "integrity": "sha512-kkeU/pe+hABcYDH6Uqy8RmIsr2S/y5bP2rp+Gat4CcRjCcVne6KudS1NrZQhUCRysrTDCAhcbcf9gt+/+pGO2g==",
+      "version": "5.15.18",
+      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.18.tgz",
+      "integrity": "sha512-jGhyw02TSLM0NgW+MDQRLLRUD/K4eN9rlK2pTBTL1OtzyZmQ8nB060zK1wA0b7cVrIiG+zyrRmNAvGWXwm2N9Q==",
       "dependencies": {
         "@babel/runtime": "^7.23.9"
       },
@@ -686,13 +677,13 @@
       }
     },
     "node_modules/@mui/material": {
-      "version": "5.15.15",
-      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.15.tgz",
-      "integrity": "sha512-3zvWayJ+E1kzoIsvwyEvkTUKVKt1AjchFFns+JtluHCuvxgKcLSRJTADw37k0doaRtVAsyh8bz9Afqzv+KYrIA==",
+      "version": "5.15.18",
+      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.18.tgz",
+      "integrity": "sha512-n+/dsiqux74fFfcRUJjok+ieNQ7+BEk6/OwX9cLcLvriZrZb+/7Y8+Fd2HlUUbn5N0CDurgAHm0VH1DqyJ9HAw==",
       "dependencies": {
         "@babel/runtime": "^7.23.9",
         "@mui/base": "5.0.0-beta.40",
-        "@mui/core-downloads-tracker": "^5.15.15",
+        "@mui/core-downloads-tracker": "^5.15.18",
         "@mui/system": "^5.15.15",
         "@mui/types": "^7.2.14",
         "@mui/utils": "^5.15.14",
@@ -866,16 +857,16 @@
       }
     },
     "node_modules/@mui/x-date-pickers": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.2.0.tgz",
-      "integrity": "sha512-hsXugZ+n1ZnHRYzf7+PFrjZ44T+FyGZmTreBmH0M2RUaAblgK+A1V3KNLT+r4Y9gJLH+92LwePxQ9xyfR+E51A==",
+      "version": "7.4.0",
+      "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.4.0.tgz",
+      "integrity": "sha512-Xh0LD/PCYIWWSchvtnEHdUfIsnANA0QOppUkCJ+4b8mN7z+TMEBA/LHmzA2+edxo7eanyfJ7L52znxwPP4vX8Q==",
       "dependencies": {
         "@babel/runtime": "^7.24.0",
         "@mui/base": "^5.0.0-beta.40",
         "@mui/system": "^5.15.14",
         "@mui/utils": "^5.15.14",
         "@types/react-transition-group": "^4.4.10",
-        "clsx": "^2.1.0",
+        "clsx": "^2.1.1",
         "prop-types": "^15.8.1",
         "react-transition-group": "^4.4.5"
       },
@@ -891,7 +882,7 @@
         "@emotion/styled": "^11.8.1",
         "@mui/material": "^5.15.14",
         "date-fns": "^2.25.0 || ^3.2.0",
-        "date-fns-jalali": "^2.13.0-0",
+        "date-fns-jalali": "^2.13.0-0 || ^3.2.0-0",
         "dayjs": "^1.10.7",
         "luxon": "^3.0.2",
         "moment": "^2.29.4",
@@ -931,16 +922,16 @@
       }
     },
     "node_modules/@mui/x-tree-view": {
-      "version": "7.3.0",
-      "resolved": "https://registry.npmjs.org/@mui/x-tree-view/-/x-tree-view-7.3.0.tgz",
-      "integrity": "sha512-zPLtY4UP4UrglAdVRphE3Ow2UVUNKo+YkiF5z6VRqMenZBiMY+CkHSC3T+xzlAz2sSiiLZdiYJFqEpjPJI3Fcw==",
+      "version": "7.4.0",
+      "resolved": "https://registry.npmjs.org/@mui/x-tree-view/-/x-tree-view-7.4.0.tgz",
+      "integrity": "sha512-gUAZ21wUbc4cpk5sAsUjZNtdryxIVgVYRYiZsz8OTzDk82JUlGmULF6Tpex93NYI+tykkrz1+/4/Tg9MIIAKUg==",
       "dependencies": {
         "@babel/runtime": "^7.24.0",
         "@mui/base": "^5.0.0-beta.40",
         "@mui/system": "^5.15.14",
         "@mui/utils": "^5.15.14",
         "@types/react-transition-group": "^4.4.10",
-        "clsx": "^2.1.0",
+        "clsx": "^2.1.1",
         "prop-types": "^15.8.1",
         "react-transition-group": "^4.4.5"
       },
@@ -1149,9 +1140,9 @@
       "dev": true
     },
     "node_modules/@types/node": {
-      "version": "20.12.7",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
-      "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==",
+      "version": "20.12.12",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz",
+      "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==",
       "dev": true,
       "dependencies": {
         "undici-types": "~5.26.4"
@@ -1168,9 +1159,9 @@
       "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
     },
     "node_modules/@types/react": {
-      "version": "18.2.79",
-      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.79.tgz",
-      "integrity": "sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==",
+      "version": "18.3.2",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.2.tgz",
+      "integrity": "sha512-Btgg89dAnqD4vV7R3hlwOxgqobUQKgx3MmrQRi0yYbs/P0ym8XozIAlkqVilPqHQwXs4e9Tf63rrCgl58BcO4w==",
       "dependencies": {
         "@types/prop-types": "*",
         "csstype": "^3.0.2"
@@ -1184,12 +1175,6 @@
         "@types/react": "*"
       }
     },
-    "node_modules/@types/semver": {
-      "version": "7.5.8",
-      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
-      "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
-      "dev": true
-    },
     "node_modules/@types/yargs": {
       "version": "17.0.32",
       "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
@@ -1206,21 +1191,19 @@
       "dev": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "7.7.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.7.1.tgz",
-      "integrity": "sha512-KwfdWXJBOviaBVhxO3p5TJiLpNuh2iyXyjmWN0f1nU87pwyvfS0EmjC6ukQVYVFJd/K1+0NWGPDXiyEyQorn0Q==",
+      "version": "7.9.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz",
+      "integrity": "sha512-6e+X0X3sFe/G/54aC3jt0txuMTURqLyekmEHViqyA2VnxhLMpvA6nqmcjIy+Cr9tLDHPssA74BP5Mx9HQIxBEA==",
       "dev": true,
       "dependencies": {
         "@eslint-community/regexpp": "^4.10.0",
-        "@typescript-eslint/scope-manager": "7.7.1",
-        "@typescript-eslint/type-utils": "7.7.1",
-        "@typescript-eslint/utils": "7.7.1",
-        "@typescript-eslint/visitor-keys": "7.7.1",
-        "debug": "^4.3.4",
+        "@typescript-eslint/scope-manager": "7.9.0",
+        "@typescript-eslint/type-utils": "7.9.0",
+        "@typescript-eslint/utils": "7.9.0",
+        "@typescript-eslint/visitor-keys": "7.9.0",
         "graphemer": "^1.4.0",
         "ignore": "^5.3.1",
         "natural-compare": "^1.4.0",
-        "semver": "^7.6.0",
         "ts-api-utils": "^1.3.0"
       },
       "engines": {
@@ -1241,15 +1224,15 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "7.7.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.7.1.tgz",
-      "integrity": "sha512-vmPzBOOtz48F6JAGVS/kZYk4EkXao6iGrD838sp1w3NQQC0W8ry/q641KU4PrG7AKNAf56NOcR8GOpH8l9FPCw==",
+      "version": "7.9.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.9.0.tgz",
+      "integrity": "sha512-qHMJfkL5qvgQB2aLvhUSXxbK7OLnDkwPzFalg458pxQgfxKDfT1ZDbHQM/I6mDIf/svlMkj21kzKuQ2ixJlatQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "7.7.1",
-        "@typescript-eslint/types": "7.7.1",
-        "@typescript-eslint/typescript-estree": "7.7.1",
-        "@typescript-eslint/visitor-keys": "7.7.1",
+        "@typescript-eslint/scope-manager": "7.9.0",
+        "@typescript-eslint/types": "7.9.0",
+        "@typescript-eslint/typescript-estree": "7.9.0",
+        "@typescript-eslint/visitor-keys": "7.9.0",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -1269,13 +1252,13 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "7.7.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.7.1.tgz",
-      "integrity": "sha512-PytBif2SF+9SpEUKynYn5g1RHFddJUcyynGpztX3l/ik7KmZEv19WCMhUBkHXPU9es/VWGD3/zg3wg90+Dh2rA==",
+      "version": "7.9.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz",
+      "integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.7.1",
-        "@typescript-eslint/visitor-keys": "7.7.1"
+        "@typescript-eslint/types": "7.9.0",
+        "@typescript-eslint/visitor-keys": "7.9.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -1286,13 +1269,13 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "7.7.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.7.1.tgz",
-      "integrity": "sha512-ZksJLW3WF7o75zaBPScdW1Gbkwhd/lyeXGf1kQCxJaOeITscoSl0MjynVvCzuV5boUz/3fOI06Lz8La55mu29Q==",
+      "version": "7.9.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.9.0.tgz",
+      "integrity": "sha512-6Qy8dfut0PFrFRAZsGzuLoM4hre4gjzWJB6sUvdunCYZsYemTkzZNwF1rnGea326PHPT3zn5Lmg32M/xfJfByA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "7.7.1",
-        "@typescript-eslint/utils": "7.7.1",
+        "@typescript-eslint/typescript-estree": "7.9.0",
+        "@typescript-eslint/utils": "7.9.0",
         "debug": "^4.3.4",
         "ts-api-utils": "^1.3.0"
       },
@@ -1313,9 +1296,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "7.7.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.7.1.tgz",
-      "integrity": "sha512-AmPmnGW1ZLTpWa+/2omPrPfR7BcbUU4oha5VIbSbS1a1Tv966bklvLNXxp3mrbc+P2j4MNOTfDffNsk4o0c6/w==",
+      "version": "7.9.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz",
+      "integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==",
       "dev": true,
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -1326,13 +1309,13 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "7.7.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.7.1.tgz",
-      "integrity": "sha512-CXe0JHCXru8Fa36dteXqmH2YxngKJjkQLjxzoj6LYwzZ7qZvgsLSc+eqItCrqIop8Vl2UKoAi0StVWu97FQZIQ==",
+      "version": "7.9.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz",
+      "integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.7.1",
-        "@typescript-eslint/visitor-keys": "7.7.1",
+        "@typescript-eslint/types": "7.9.0",
+        "@typescript-eslint/visitor-keys": "7.9.0",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
@@ -1354,18 +1337,15 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "7.7.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.7.1.tgz",
-      "integrity": "sha512-QUvBxPEaBXf41ZBbaidKICgVL8Hin0p6prQDu6bbetWo39BKbWJxRsErOzMNT1rXvTll+J7ChrbmMCXM9rsvOQ==",
+      "version": "7.9.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz",
+      "integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
-        "@types/json-schema": "^7.0.15",
-        "@types/semver": "^7.5.8",
-        "@typescript-eslint/scope-manager": "7.7.1",
-        "@typescript-eslint/types": "7.7.1",
-        "@typescript-eslint/typescript-estree": "7.7.1",
-        "semver": "^7.6.0"
+        "@typescript-eslint/scope-manager": "7.9.0",
+        "@typescript-eslint/types": "7.9.0",
+        "@typescript-eslint/typescript-estree": "7.9.0"
       },
       "engines": {
         "node": "^18.18.0 || >=20.0.0"
@@ -1379,12 +1359,12 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "7.7.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.7.1.tgz",
-      "integrity": "sha512-gBL3Eq25uADw1LQ9kVpf3hRM+DWzs0uZknHYK3hq4jcTPqVCClHGDnB6UUUV2SFeBeA4KWHWbbLqmbGcZ4FYbw==",
+      "version": "7.9.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz",
+      "integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "7.7.1",
+        "@typescript-eslint/types": "7.9.0",
         "eslint-visitor-keys": "^3.4.3"
       },
       "engines": {
@@ -1667,15 +1647,15 @@
       }
     },
     "node_modules/ajv-formats/node_modules/ajv": {
-      "version": "8.12.0",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
-      "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+      "version": "8.13.0",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz",
+      "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==",
       "dev": true,
       "dependencies": {
-        "fast-deep-equal": "^3.1.1",
+        "fast-deep-equal": "^3.1.3",
         "json-schema-traverse": "^1.0.0",
         "require-from-string": "^2.0.2",
-        "uri-js": "^4.2.2"
+        "uri-js": "^4.4.1"
       },
       "funding": {
         "type": "github",
@@ -1997,9 +1977,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001612",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001612.tgz",
-      "integrity": "sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g==",
+      "version": "1.0.30001620",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001620.tgz",
+      "integrity": "sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==",
       "dev": true,
       "funding": [
         {
@@ -2345,15 +2325,15 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.748",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.748.tgz",
-      "integrity": "sha512-VWqjOlPZn70UZ8FTKUOkUvBLeTQ0xpty66qV0yJcAGY2/CthI4xyW9aEozRVtuwv3Kpf5xTesmJUcPwuJmgP4A==",
+      "version": "1.4.772",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.772.tgz",
+      "integrity": "sha512-jFfEbxR/abTTJA3ci+2ok1NTuOBBtB4jH+UT6PUmRN+DY3WSD4FFRsgoVQ+QNIJ0T7wrXwzsWCI2WKC46b++2A==",
       "dev": true
     },
     "node_modules/enhanced-resolve": {
-      "version": "5.16.0",
-      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz",
-      "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==",
+      "version": "5.16.1",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz",
+      "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==",
       "dev": true,
       "dependencies": {
         "graceful-fs": "^4.2.4",
@@ -2364,9 +2344,9 @@
       }
     },
     "node_modules/envinfo": {
-      "version": "7.12.0",
-      "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.12.0.tgz",
-      "integrity": "sha512-Iw9rQJBGpJRd3rwXm9ft/JiGoAZmLxxJZELYDQoPRZ4USVhkKtIcNBPw6U+/K2mBpaqM25JSV6Yl4Az9vO2wJg==",
+      "version": "7.13.0",
+      "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz",
+      "integrity": "sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==",
       "dev": true,
       "bin": {
         "envinfo": "dist/cli.js"
@@ -2490,9 +2470,9 @@
       }
     },
     "node_modules/es-module-lexer": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.0.tgz",
-      "integrity": "sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw==",
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.2.tgz",
+      "integrity": "sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA==",
       "dev": true
     },
     "node_modules/es-object-atoms": {
@@ -2655,9 +2635,9 @@
       }
     },
     "node_modules/eslint-plugin-react-hooks": {
-      "version": "4.6.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
-      "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz",
+      "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==",
       "dev": true,
       "engines": {
         "node": ">=10"
@@ -3201,12 +3181,13 @@
       }
     },
     "node_modules/globalthis": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
-      "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+      "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
       "dev": true,
       "dependencies": {
-        "define-properties": "^1.1.3"
+        "define-properties": "^1.2.1",
+        "gopd": "^1.0.1"
       },
       "engines": {
         "node": ">= 0.4"
@@ -4047,18 +4028,6 @@
         "loose-envify": "cli.js"
       }
     },
-    "node_modules/lru-cache": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
-      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
-      "dev": true,
-      "dependencies": {
-        "yallist": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/merge-stream": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -4276,17 +4245,17 @@
       }
     },
     "node_modules/optionator": {
-      "version": "0.9.3",
-      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
-      "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+      "version": "0.9.4",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+      "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
       "dev": true,
       "dependencies": {
-        "@aashutoshrathi/word-wrap": "^1.2.3",
         "deep-is": "^0.1.3",
         "fast-levenshtein": "^2.0.6",
         "levn": "^0.4.1",
         "prelude-ls": "^1.2.1",
-        "type-check": "^0.4.0"
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.5"
       },
       "engines": {
         "node": ">= 0.8.0"
@@ -4416,9 +4385,9 @@
       }
     },
     "node_modules/picocolors": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
-      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
+      "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew=="
     },
     "node_modules/picomatch": {
       "version": "2.3.1",
@@ -4568,9 +4537,9 @@
       }
     },
     "node_modules/react": {
-      "version": "18.2.0",
-      "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
-      "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
+      "version": "18.3.1",
+      "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+      "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
       "dependencies": {
         "loose-envify": "^1.1.0"
       },
@@ -4579,15 +4548,15 @@
       }
     },
     "node_modules/react-dom": {
-      "version": "18.2.0",
-      "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
-      "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
+      "version": "18.3.1",
+      "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+      "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
       "dependencies": {
         "loose-envify": "^1.1.0",
-        "scheduler": "^0.23.0"
+        "scheduler": "^0.23.2"
       },
       "peerDependencies": {
-        "react": "^18.2.0"
+        "react": "^18.3.1"
       }
     },
     "node_modules/react-fast-compare": {
@@ -4596,9 +4565,9 @@
       "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
     },
     "node_modules/react-is": {
-      "version": "18.2.0",
-      "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
-      "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
+      "version": "18.3.1",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+      "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
     },
     "node_modules/react-transition-group": {
       "version": "4.4.5",
@@ -4834,9 +4803,9 @@
       }
     },
     "node_modules/scheduler": {
-      "version": "0.23.0",
-      "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
-      "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
+      "version": "0.23.2",
+      "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+      "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
       "dependencies": {
         "loose-envify": "^1.1.0"
       }
@@ -4861,15 +4830,15 @@
       }
     },
     "node_modules/schema-utils/node_modules/ajv": {
-      "version": "8.12.0",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
-      "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+      "version": "8.13.0",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz",
+      "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==",
       "dev": true,
       "dependencies": {
-        "fast-deep-equal": "^3.1.1",
+        "fast-deep-equal": "^3.1.3",
         "json-schema-traverse": "^1.0.0",
         "require-from-string": "^2.0.2",
-        "uri-js": "^4.2.2"
+        "uri-js": "^4.4.1"
       },
       "funding": {
         "type": "github",
@@ -4895,13 +4864,10 @@
       "dev": true
     },
     "node_modules/semver": {
-      "version": "7.6.0",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
-      "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+      "version": "7.6.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
+      "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
       "dev": true,
-      "dependencies": {
-        "lru-cache": "^6.0.0"
-      },
       "bin": {
         "semver": "bin/semver.js"
       },
@@ -5178,9 +5144,9 @@
       }
     },
     "node_modules/terser": {
-      "version": "5.30.4",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.4.tgz",
-      "integrity": "sha512-xRdd0v64a8mFK9bnsKVdoNP9GQIKUAaJPTaqEQDL4w/J8WaW4sWXXoMZ+6SimPkfT5bElreXf8m9HnmPc3E1BQ==",
+      "version": "5.31.0",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz",
+      "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==",
       "dev": true,
       "dependencies": {
         "@jridgewell/source-map": "^0.3.3",
@@ -5485,9 +5451,9 @@
       "dev": true
     },
     "node_modules/update-browserslist-db": {
-      "version": "1.0.13",
-      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
-      "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
+      "version": "1.0.16",
+      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz",
+      "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==",
       "dev": true,
       "funding": [
         {
@@ -5504,8 +5470,8 @@
         }
       ],
       "dependencies": {
-        "escalade": "^3.1.1",
-        "picocolors": "^1.0.0"
+        "escalade": "^3.1.2",
+        "picocolors": "^1.0.1"
       },
       "bin": {
         "update-browserslist-db": "cli.js"
@@ -5800,18 +5766,21 @@
       "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==",
       "dev": true
     },
+    "node_modules/word-wrap": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+      "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
       "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
       "dev": true
     },
-    "node_modules/yallist": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
-      "dev": true
-    },
     "node_modules/yaml": {
       "version": "1.10.2",
       "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",

+ 18 - 0
taipy/gui/_renderers/factory.py

@@ -31,6 +31,7 @@ class _Factory:
 
     __CONTROL_DEFAULT_PROP_NAME = {
         "button": "label",
+        "chat": "messages",
         "chart": "data",
         "content": "value",
         "date": "date",
@@ -81,6 +82,23 @@ class _Factory:
                 ("hover_text", PropertyType.dynamic_string),
             ]
         ),
+        "chat": lambda gui, control_type, attrs: _Builder(
+            gui=gui, control_type=control_type, element_name="Chat", attributes=attrs, default_value=None
+        )
+        .set_value_and_default(with_update=True, with_default=False, var_type=PropertyType.data)
+        .set_attributes(
+            [
+                ("id",),
+                ("on_action", PropertyType.function),
+                ("active", PropertyType.dynamic_boolean, True),
+                ("hover_text", PropertyType.dynamic_string),
+                ("with_input", PropertyType.dynamic_boolean, True),
+                ("users", PropertyType.lov),
+                ("sender_id",),
+                ("height",),
+                ("page_size", PropertyType.number, 50),
+            ]
+        ),
         "chart": lambda gui, control_type, attrs: _Builder(
             gui=gui, control_type=control_type, element_name="Chart", attributes=attrs, default_value=None
         )

+ 8 - 0
taipy/gui/data/pandas_data_accessor.py

@@ -313,6 +313,14 @@ class _PandasDataAccessor(_DataAccessor):
                 start = 0
             if end < 0 or end >= rowcount:
                 end = rowcount - 1
+            if payload.get("reverse", False):
+                diff = end - start
+                end = rowcount - 1 - start
+                if end < 0:
+                    end = rowcount - 1
+                start = end - diff
+                if start < 0:
+                    start = 0
             # deal with sort
             order_by = payload.get("orderby")
             if isinstance(order_by, str) and len(order_by):

+ 2 - 2
taipy/gui/gui_actions.py

@@ -322,9 +322,9 @@ def invoke_state_callback(gui: Gui, state_id: str, callback: t.Callable, args: t
 def invoke_long_callback(
     state: State,
     user_function: t.Callable,
-    user_function_args: t.Union[t.Tuple, t.List] = None,
+    user_function_args: t.Optional[t.Union[t.Tuple, t.List]] = None,
     user_status_function: t.Optional[t.Callable] = None,
-    user_status_function_args: t.Union[t.Tuple, t.List] = None,
+    user_status_function_args: t.Optional[t.Union[t.Tuple, t.List]] = None,
     period=0,
 ):
     """Invoke a long running user callback.

+ 1754 - 1482
taipy/gui/viselements.json

@@ -1,1524 +1,1796 @@
 {
-  "controls": [
-    [
-      "text",
-      {
-        "inherits": [
-          "shared"
-        ],
-        "properties": [
-          {
-            "name": "value",
-            "default_property": true,
-            "type": "dynamic(any)",
-            "default_value": "\"\"",
-            "doc": "The value displayed as text by this control."
-          },
-          {
-            "name": "raw",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "If set to True, the component renders as an HTML &lt;span&gt; element without any default style."
-          },
-          {
-            "name": "mode",
-            "type": "str",
-            "doc": "Define the way the text is processed:<ul><li>&quot;raw&quot;: synonym for setting the *raw* property to True</li><li>&quot;pre&quot;: keeps spaces and new lines</li><li>&quot;markdown&quot; or &quot;md&quot;: basic support for Markdown."
-          },
-          {
-            "name": "format",
-            "type": "str",
-            "doc": "The format to apply to the value.<br/>See below."
-          }
-        ]
-      }
-    ],
-    [
-      "button",
-      {
-        "inherits": [
-          "active",
-          "shared"
+    "controls": [
+        [
+            "text",
+            {
+                "inherits": [
+                    "shared"
+                ],
+                "properties": [
+                    {
+                        "name": "value",
+                        "default_property": true,
+                        "type": "dynamic(any)",
+                        "default_value": "\"\"",
+                        "doc": "The value displayed as text by this control."
+                    },
+                    {
+                        "name": "raw",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If set to True, the component renders as an HTML &lt;span&gt; element without any default style."
+                    },
+                    {
+                        "name": "mode",
+                        "type": "str",
+                        "doc": "Define the way the text is processed:<ul><li>&quot;raw&quot;: synonym for setting the *raw* property to True</li><li>&quot;pre&quot;: keeps spaces and new lines</li><li>&quot;markdown&quot; or &quot;md&quot;: basic support for Markdown."
+                    },
+                    {
+                        "name": "format",
+                        "type": "str",
+                        "doc": "The format to apply to the value.<br/>See below."
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "label",
-            "default_property": true,
-            "type": "dynamic(str|Icon)",
-            "default_value": "\"\"",
-            "doc": "The label displayed in the button."
-          },
-          {
-            "name": "on_action",
-            "type": "Callback",
-            "doc": "The name of a function that is triggered when the button is pressed.<br/>The parameters of that function are all optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (optional[str]): the identifier of the button.</li>\n<li>payload (dict): a dictionary that contains the key \"action\" set to the name of the action that triggered this callback.</li>\n</ul>",
-            "signature": [["state", "State"], ["id", "str"], ["payload", "dict"]]
-          }
-        ]
-      }
-    ],
-    [
-      "input",
-      {
-        "inherits": [
-          "sharedInput",
-          "on_change",
-          "propagate"
+        [
+            "button",
+            {
+                "inherits": [
+                    "active",
+                    "shared"
+                ],
+                "properties": [
+                    {
+                        "name": "label",
+                        "default_property": true,
+                        "type": "dynamic(str|Icon)",
+                        "default_value": "\"\"",
+                        "doc": "The label displayed in the button."
+                    },
+                    {
+                        "name": "on_action",
+                        "type": "Callback",
+                        "doc": "The name of a function that is triggered when the button is pressed.<br/>The parameters of that function are all optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (optional[str]): the identifier of the button.</li>\n<li>payload (dict): a dictionary that contains the key \"action\" set to the name of the action that triggered this callback.</li>\n</ul>",
+                        "signature": [
+                            [
+                                "state",
+                                "State"
+                            ],
+                            [
+                                "id",
+                                "str"
+                            ],
+                            [
+                                "payload",
+                                "dict"
+                            ]
+                        ]
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "value",
-            "default_property": true,
-            "type": "dynamic(any)",
-            "default_value": "None",
-            "doc": "The value represented by this control."
-          },
-          {
-            "name": "password",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "If True, the text is obscured: all input characters are displayed as an asterisk ('*')."
-          },
-          {
-            "name": "label",
-            "type": "str",
-            "default_value": "None",
-            "doc": "The label associated with the input."
-          },
-          {
-            "name": "multiline",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "If True, the text is presented as a multi line input."
-          },
-          {
-            "name": "lines_shown",
-            "type": "int",
-            "default_value": "5",
-            "doc": "The height of the displayed element if multiline is True."
-          }
-        ]
-      }
-    ],
-    [
-      "number",
-      {
-        "inherits": [
-          "sharedInput",
-          "on_change",
-          "propagate"
+        [
+            "input",
+            {
+                "inherits": [
+                    "sharedInput",
+                    "on_change",
+                    "propagate"
+                ],
+                "properties": [
+                    {
+                        "name": "value",
+                        "default_property": true,
+                        "type": "dynamic(any)",
+                        "default_value": "None",
+                        "doc": "The value represented by this control."
+                    },
+                    {
+                        "name": "password",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If True, the text is obscured: all input characters are displayed as an asterisk ('*')."
+                    },
+                    {
+                        "name": "label",
+                        "type": "str",
+                        "default_value": "None",
+                        "doc": "The label associated with the input."
+                    },
+                    {
+                        "name": "multiline",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If True, the text is presented as a multi line input."
+                    },
+                    {
+                        "name": "lines_shown",
+                        "type": "int",
+                        "default_value": "5",
+                        "doc": "The height of the displayed element if multiline is True."
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "value",
-            "default_property": true,
-            "type": "dynamic(any)",
-            "doc": "The numerical value represented by this control."
-          },
-          {
-            "name": "label",
-            "type": "str",
-            "default_value": "None",
-            "doc": "The label associated with the input."
-          }
-        ]
-      }
-    ],
-    [
-      "slider",
-      {
-        "inherits": [
-          "lovComp",
-          "propagate"
+        [
+            "number",
+            {
+                "inherits": [
+                    "sharedInput",
+                    "on_change",
+                    "propagate"
+                ],
+                "properties": [
+                    {
+                        "name": "value",
+                        "default_property": true,
+                        "type": "dynamic(any)",
+                        "doc": "The numerical value represented by this control."
+                    },
+                    {
+                        "name": "label",
+                        "type": "str",
+                        "default_value": "None",
+                        "doc": "The label associated with the input."
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "value",
-            "default_property": true,
-            "type": "dynamic(int|float|int[]|float[]|str|str[])",
-            "doc": "The value that is set for this slider.<br/>If this slider is based on a <i>lov</i> then this property can be set to the lov element.<br/>This value can also hold an array of numbers to indicate that the slider reflects a range (within the [<i>min</i>,<i>max</i>] domain) defined by several knobs that the user can set independently.<br/>If this slider is based on a <i>lov</i> then this property can be set to an array of lov elements. The slider is then represented with several knobs, one for each lov value."
-          },
-          {
-            "name": "min",
-            "type": "int|float",
-            "default_value": "0",
-            "doc": "The minimum value.<br/>This is ignored when <i>lov</i> is defined."
-          },
-          {
-            "name": "max",
-            "type": "int|float",
-            "default_value": "100",
-            "doc": "The maximum value.<br/>This is ignored when <i>lov</i> is defined."
-          },
-          {
-            "name": "step",
-            "type": "int|float",
-            "default_value": "1",
-            "doc": "The step value: the gap between two consecutive values the slider set. It is a good practice to have (<i>max</i>-<i>min</i>) being divisible by <i>step</i>.<br/>This property is ignored when <i>lov</i> is defined."
-          },
-          {
-            "name": "text_anchor",
-            "type": "str",
-            "default_value": "\"bottom\"",
-            "doc": "When the <i>lov</i> property is used, this property indicates the location of the label.<br/>Possible values are:\n<ul>\n<li>\"bottom\"</li>\n<li>\"top\"</li>\n<li>\"left\"</li>\n<li>\"right\"</li>\n<li>\"none\" (no label is displayed)</li>\n</ul>"
-          },
-          {
-            "name": "labels",
-            "type": "bool|dict",
-            "doc": "The labels for specific points of the slider.<br/>If set to True, this slider uses the labels of the <i>lov</i> if there are any.<br/>If set to a dictionary, the slider uses the dictionary keys as a <i>lov</i> key or index, and the associated value as the label."
-          },
-          {
-            "name": "continuous",
-            "type": "bool",
-            "default_value": "True",
-            "doc": "If set to False, the control emits an on_change notification only when the mouse button is released, otherwise notifications are emitted during the cursor movements.<br/>If <i>lov</i> is defined, the default value is False."
-          },
-          {
-            "name": "change_delay",
-            "type": "int",
-            "default_value": "<i>App config</i>",
-            "doc": "Minimum time between triggering two <i>on_change</i> calls.<br/>The default value is defined at the application configuration level by the <strong>change_delay</strong> configuration option. if None or 0, there's no delay."
-          },
-          {
-            "name": "width",
-            "type": "str",
-            "default_value": "\"300px\"",
-            "doc": "The width, in CSS units, of this element."
-          },
-          {
-            "name": "height",
-            "type": "str",
-            "doc": "The height, in CSS units, of this element.<br/>It defaults to the <i>width</i> value when using the vertical orientation."
-          },
-          {
-            "name": "orientation",
-            "type": "str",
-            "default_value": "\"horizontal\"",
-            "doc": "The orientation of this slider.<br/>Valid values are \"horizontal\" or \"vertical\"."
-          }
-        ]
-      }
-    ],
-    [
-      "toggle",
-      {
-        "inherits": [
-          "lovComp",
-          "propagate"
+        [
+            "slider",
+            {
+                "inherits": [
+                    "lovComp",
+                    "propagate"
+                ],
+                "properties": [
+                    {
+                        "name": "value",
+                        "default_property": true,
+                        "type": "dynamic(int|float|int[]|float[]|str|str[])",
+                        "doc": "The value that is set for this slider.<br/>If this slider is based on a <i>lov</i> then this property can be set to the lov element.<br/>This value can also hold an array of numbers to indicate that the slider reflects a range (within the [<i>min</i>,<i>max</i>] domain) defined by several knobs that the user can set independently.<br/>If this slider is based on a <i>lov</i> then this property can be set to an array of lov elements. The slider is then represented with several knobs, one for each lov value."
+                    },
+                    {
+                        "name": "min",
+                        "type": "int|float",
+                        "default_value": "0",
+                        "doc": "The minimum value.<br/>This is ignored when <i>lov</i> is defined."
+                    },
+                    {
+                        "name": "max",
+                        "type": "int|float",
+                        "default_value": "100",
+                        "doc": "The maximum value.<br/>This is ignored when <i>lov</i> is defined."
+                    },
+                    {
+                        "name": "step",
+                        "type": "int|float",
+                        "default_value": "1",
+                        "doc": "The step value: the gap between two consecutive values the slider set. It is a good practice to have (<i>max</i>-<i>min</i>) being divisible by <i>step</i>.<br/>This property is ignored when <i>lov</i> is defined."
+                    },
+                    {
+                        "name": "text_anchor",
+                        "type": "str",
+                        "default_value": "\"bottom\"",
+                        "doc": "When the <i>lov</i> property is used, this property indicates the location of the label.<br/>Possible values are:\n<ul>\n<li>\"bottom\"</li>\n<li>\"top\"</li>\n<li>\"left\"</li>\n<li>\"right\"</li>\n<li>\"none\" (no label is displayed)</li>\n</ul>"
+                    },
+                    {
+                        "name": "labels",
+                        "type": "bool|dict",
+                        "doc": "The labels for specific points of the slider.<br/>If set to True, this slider uses the labels of the <i>lov</i> if there are any.<br/>If set to a dictionary, the slider uses the dictionary keys as a <i>lov</i> key or index, and the associated value as the label."
+                    },
+                    {
+                        "name": "continuous",
+                        "type": "bool",
+                        "default_value": "True",
+                        "doc": "If set to False, the control emits an on_change notification only when the mouse button is released, otherwise notifications are emitted during the cursor movements.<br/>If <i>lov</i> is defined, the default value is False."
+                    },
+                    {
+                        "name": "change_delay",
+                        "type": "int",
+                        "default_value": "<i>App config</i>",
+                        "doc": "Minimum time between triggering two <i>on_change</i> calls.<br/>The default value is defined at the application configuration level by the <strong>change_delay</strong> configuration option. if None or 0, there's no delay."
+                    },
+                    {
+                        "name": "width",
+                        "type": "str",
+                        "default_value": "\"300px\"",
+                        "doc": "The width, in CSS units, of this element."
+                    },
+                    {
+                        "name": "height",
+                        "type": "str",
+                        "doc": "The height, in CSS units, of this element.<br/>It defaults to the <i>width</i> value when using the vertical orientation."
+                    },
+                    {
+                        "name": "orientation",
+                        "type": "str",
+                        "default_value": "\"horizontal\"",
+                        "doc": "The orientation of this slider.<br/>Valid values are \"horizontal\" or \"vertical\"."
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "value"
-          },
-          {
-            "name": "theme",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "If set, this toggle control acts as a way to set the application Theme (dark or light)."
-          },
-          {
-            "name": "allow_unselect",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "If set, this allows de-selection and the value is set to unselected_value."
-          },
-          {
-            "name": "mode",
-            "type": "str",
-            "doc": "Define the way the toggle is displayed:<ul><li>&quot;theme&quot;: synonym for setting the *theme* property to True</li></ul>"
-          }
-        ]
-      }
-    ],
-    [
-      "date",
-      {
-        "inherits": [
-          "on_change",
-          "propagate"
+        [
+            "toggle",
+            {
+                "inherits": [
+                    "lovComp",
+                    "propagate"
+                ],
+                "properties": [
+                    {
+                        "name": "value"
+                    },
+                    {
+                        "name": "theme",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If set, this toggle control acts as a way to set the application Theme (dark or light)."
+                    },
+                    {
+                        "name": "allow_unselect",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If set, this allows de-selection and the value is set to unselected_value."
+                    },
+                    {
+                        "name": "mode",
+                        "type": "str",
+                        "doc": "Define the way the toggle is displayed:<ul><li>&quot;theme&quot;: synonym for setting the *theme* property to True</li></ul>"
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "date",
-            "default_property": true,
-            "type": "dynamic(datetime)",
-            "doc": "The date that this control represents and can modify.<br/>It is typically bound to a <code>datetime</code> object."
-          },
-          {
-            "name": "with_time",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "Whether or not to show the time part of the date."
-          },
-          {
-            "name": "format",
-            "type": "str",
-            "doc": "The format to apply to the value. See below."
-          },
-          {
-            "name": "editable",
-            "type": "dynamic(bool)",
-            "default_value": "True",
-            "doc": "Shows the date as a formatted string if not editable."
-          },
-          {
-            "name": "label",
-            "type": "str",
-            "doc": "The label associated with the input."
-          }
-        ]
-      }
-    ],
-    [
-        "date_range",
-        {
-          "inherits": [
-            "on_change",
-            "propagate"
-          ],
-          "properties": [
+        [
+            "date",
             {
-              "name": "dates",
-              "default_property": true,
-              "type": "dynamic(list(datetime))",
-              "doc": "The dates that this control represents and can modify.<br/>It is typically bound to a list of two <code>datetime</code> object."
-            },
+                "inherits": [
+                    "on_change",
+                    "propagate"
+                ],
+                "properties": [
+                    {
+                        "name": "date",
+                        "default_property": true,
+                        "type": "dynamic(datetime)",
+                        "doc": "The date that this control represents and can modify.<br/>It is typically bound to a <code>datetime</code> object."
+                    },
+                    {
+                        "name": "with_time",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "Whether or not to show the time part of the date."
+                    },
+                    {
+                        "name": "format",
+                        "type": "str",
+                        "doc": "The format to apply to the value. See below."
+                    },
+                    {
+                        "name": "editable",
+                        "type": "dynamic(bool)",
+                        "default_value": "True",
+                        "doc": "Shows the date as a formatted string if not editable."
+                    },
+                    {
+                        "name": "label",
+                        "type": "str",
+                        "doc": "The label associated with the input."
+                    }
+                ]
+            }
+        ],
+        [
+            "date_range",
             {
-              "name": "with_time",
-              "type": "bool",
-              "default_value": "False",
-              "doc": "Whether or not to show the time part of the date."
-            },
+                "inherits": [
+                    "on_change",
+                    "propagate"
+                ],
+                "properties": [
+                    {
+                        "name": "dates",
+                        "default_property": true,
+                        "type": "dynamic(list(datetime))",
+                        "doc": "The dates that this control represents and can modify.<br/>It is typically bound to a list of two <code>datetime</code> object."
+                    },
+                    {
+                        "name": "with_time",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "Whether or not to show the time part of the date."
+                    },
+                    {
+                        "name": "format",
+                        "type": "str",
+                        "doc": "The format to apply to the value. See below."
+                    },
+                    {
+                        "name": "editable",
+                        "type": "dynamic(bool)",
+                        "default_value": "True",
+                        "doc": "Shows the date as a formatted string if not editable."
+                    },
+                    {
+                        "name": "label_start",
+                        "type": "str",
+                        "doc": "The label associated with the first input."
+                    },
+                    {
+                        "name": "label_end",
+                        "type": "str",
+                        "doc": "The label associated with the second input."
+                    }
+                ]
+            }
+        ],
+        [
+            "chat",
             {
-              "name": "format",
-              "type": "str",
-              "doc": "The format to apply to the value. See below."
-            },
+                "inherits": [
+                    "active",
+                    "shared"
+                ],
+                "properties": [
+                    {
+                        "name": "messages",
+                        "default_property": true,
+                        "required": true,
+                        "type": "dynamic(list[str])",
+                        "doc": "The list of messages. Each element is a list composed of an id, a message and an user identifier."
+                    },
+                    {
+                        "name": "users",
+                        "type": "dynamic(list[str|Icon])",
+                        "doc": "The list of users. See the <a href=\"../../binding/#list-of-values\">section on List of Values</a> for details."
+                    },
+                    {
+                        "name": "on_action",
+                        "type": "Callback",
+                        "doc": "The name of a function that is triggered when the user enters a new message.<br/>All parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>var_name (str): the name of the messages variable.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>This dictionary has the following keys:\n<ul>\n<li>action: the name of the action that triggered this callback.</li>\n<li>args (list): A list composed of a reason (click or Enter), variable name, message, sender id.</li></ul></li></ul>.",
+                        "signature": [
+                            [
+                                "state",
+                                "State"
+                            ],
+                            [
+                                "var_name",
+                                "str"
+                            ],
+                            [
+                                "payload",
+                                "dict"
+                            ]
+                        ]
+                    },
+                    {
+                        "name": "with_input",
+                        "type": "dynamic(bool)",
+                        "default_value": "True",
+                        "doc": "If True, the input field is visible."
+                    },
+                    {
+                        "name": "sender_id",
+                        "type": "str",
+                        "default_value": "taipy",
+                        "doc": "The user id associated with the message sent from the input"
+                    },
+                    {
+                        "name": "height",
+                        "type": "str|int|float",
+                        "doc": "The maximum height, in CSS units, of this element."
+                    },
+                    {
+                        "name": "page_size",
+                        "type": "int",
+                        "default_value": "50",
+                        "doc": "The number of rows retrieved on the frontend."
+                    }
+                ]
+            }
+        ],
+        [
+            "chart",
             {
-              "name": "editable",
-              "type": "dynamic(bool)",
-              "default_value": "True",
-              "doc": "Shows the date as a formatted string if not editable."
-            },
+                "inherits": [
+                    "on_change",
+                    "propagate"
+                ],
+                "properties": [
+                    {
+                        "name": "data",
+                        "default_property": true,
+                        "required": true,
+                        "type": "dynamic(any)",
+                        "doc": "The data object bound to this chart control.<br/>See the section on the <a href=\"#the-data-property\"><i>data</i> property</a> below for details."
+                    },
+                    {
+                        "name": "type",
+                        "type": "indexed(str)",
+                        "default_value": "scatter",
+                        "doc": "Chart type.<br/>See the Plotly <a href=\"https://plotly.com/javascript/reference/\">chart type</a> documentation for details."
+                    },
+                    {
+                        "name": "mode",
+                        "type": "indexed(str)",
+                        "default_value": "lines+markers",
+                        "doc": "Chart mode.<br/>See the Plotly <a href=\"https://plotly.com/javascript/reference/scatter/#scatter-mode\">chart mode</a> documentation for details."
+                    },
+                    {
+                        "name": "x",
+                        "type": "indexed(str)",
+                        "doc": "Column name for the <i>x</i> axis."
+                    },
+                    {
+                        "name": "y",
+                        "type": "indexed(str)",
+                        "doc": "Column name for the <i>y</i> axis."
+                    },
+                    {
+                        "name": "z",
+                        "type": "indexed(str)",
+                        "doc": "Column name for the <i>z</i> axis."
+                    },
+                    {
+                        "name": "lon",
+                        "type": "indexed(str)",
+                        "doc": "Column name for the <i>longitude</i> value, for 'scattergeo' charts. See <a href=\"https://plotly.com/javascript/reference/scattergeo/#scattergeo-lon\">Plotly Map traces</a>."
+                    },
+                    {
+                        "name": "lat",
+                        "type": "indexed(str)",
+                        "doc": "Column name for the <i>latitude</i> value, for 'scattergeo' charts. See <a href=\"https://plotly.com/javascript/reference/scattergeo/#scattergeo-lat\">Plotly Map traces</a>."
+                    },
+                    {
+                        "name": "r",
+                        "type": "indexed(str)",
+                        "doc": "Column name for the <i>r</i> value, for 'scatterpolar' charts. See <a href=\"https://plotly.com/javascript/polar-chart/\">Plotly Polar charts</a>."
+                    },
+                    {
+                        "name": "theta",
+                        "type": "indexed(str)",
+                        "doc": "Column name for the <i>theta</i> value, for 'scatterpolar' charts. See <a href=\"https://plotly.com/javascript/polar-chart/\">Plotly Polar charts</a>."
+                    },
+                    {
+                        "name": "high",
+                        "type": "indexed(str)",
+                        "doc": "Column name for the <i>high</i> value, for 'candlestick' charts. See <a href=\"https://plotly.com/javascript/reference/candlestick/#candlestick-high\">Plotly Candlestick charts</a>."
+                    },
+                    {
+                        "name": "low",
+                        "type": "indexed(str)",
+                        "doc": "Column name for the <i>low</i> value, for 'candlestick' charts. See <a href=\"https://plotly.com/javascript/reference/candlestick/#candlestick-low\">Ploty Candlestick charts</a>."
+                    },
+                    {
+                        "name": "open",
+                        "type": "indexed(str)",
+                        "doc": "Column name for the <i>open</i> value, for 'candlestick' charts. See <a href=\"https://plotly.com/javascript/reference/candlestick/#candlestick-open\">Plotly Candlestick charts</a>."
+                    },
+                    {
+                        "name": "close",
+                        "type": "indexed(str)",
+                        "doc": "Column name for the <i>close</i> value, for 'candlestick' charts. See <a href=\"https://plotly.com/javascript/reference/candlestick/#candlestick-close\">Plotly Candlestick charts</a>."
+                    },
+                    {
+                        "name": "measure",
+                        "type": "indexed(str)",
+                        "doc": "Column name for the <i>measure</i> value, for 'waterfall' charts. See <a href=\"https://plotly.com/javascript/reference/waterfall/#waterfall-measure\">Plotly Waterfall charts</a>."
+                    },
+                    {
+                        "name": "locations",
+                        "type": "indexed(str)",
+                        "doc": "Column name for the <i>locations</i> value. See <a href=\"https://plotly.com/javascript/choropleth-maps/\">Plotly Choropleth maps</a>."
+                    },
+                    {
+                        "name": "values",
+                        "type": "indexed(str)",
+                        "doc": "Column name for the <i>values</i> value. See <a href=\"https://plotly.com/javascript/reference/pie/#pie-values\">Plotly Pie charts</a> or <a href=\"https://plotly.com/javascript/reference/funnelarea/#funnelarea-values\">Plotly Funnel Area charts</a>."
+                    },
+                    {
+                        "name": "labels",
+                        "type": "indexed(str)",
+                        "doc": "Column name for the <i>labels</i> value. See <a href=\"https://plotly.com/javascript/reference/pie/#pie-labels\">Plotly Pie charts</a>."
+                    },
+                    {
+                        "name": "parents",
+                        "type": "indexed(str)",
+                        "doc": "Column name for the <i>parents</i> value. See <a href=\"https://plotly.com/javascript/reference/treemap/#treemap-parents\">Plotly Treemap charts</a>."
+                    },
+                    {
+                        "name": "text",
+                        "type": "indexed(str)",
+                        "doc": "Column name for the text associated to the point for the indicated trace.<br/>This is meaningful only when <i>mode</i> has the <i>text</i> option."
+                    },
+                    {
+                        "name": "base",
+                        "type": "indexed(str)",
+                        "doc": "Column name for the <i>base</i> value. Used in bar charts only.<br/>See the Plotly <a href=\"https://plotly.com/javascript/reference/bar/#bar-base\">bar chart base</a> documentation for details.\""
+                    },
+                    {
+                        "name": "title",
+                        "type": "str",
+                        "doc": "The title of this chart control."
+                    },
+                    {
+                        "name": "render",
+                        "type": "dynamic(bool)",
+                        "default_value": "True",
+                        "doc": "If True, this chart is visible on the page."
+                    },
+                    {
+                        "name": "on_range_change",
+                        "type": "Callback",
+                        "doc": "The callback function that is invoked when the visible part of the x axis changes.<br/>The function receives three parameters:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (optional[str]): the identifier of the chart control.</li>\n<li>payload (dict[str, any]): the full details on this callback's invocation, as emitted by <a href=\"https://plotly.com/javascript/plotlyjs-events/#update-data\">Plotly</a>.</li>\n</ul>",
+                        "signature": [
+                            [
+                                "state",
+                                "State"
+                            ],
+                            [
+                                "id",
+                                "str"
+                            ],
+                            [
+                                "payload",
+                                "dict"
+                            ]
+                        ]
+                    },
+                    {
+                        "name": "columns",
+                        "type": "str|list[str]|dict[str, dict[str, str]]",
+                        "default_value": "<i>All columns</i>",
+                        "doc": "The list of column names\n<ul>\n<li>str: ;-separated list of column names</li>\n<li>list[str]: list of names</li>\n<li>dict: {\"column_name\": {format: \"format\", index: 1}} if index is specified, it represents the display order of the columns.\nIf not, the list order defines the index</li>\n</ul>"
+                    },
+                    {
+                        "name": "label",
+                        "type": "indexed(str)",
+                        "doc": "The label for the indicated trace.<br/>This is used when the mouse hovers over a trace."
+                    },
+                    {
+                        "name": "name",
+                        "type": "indexed(str)",
+                        "doc": "The name of the indicated trace."
+                    },
+                    {
+                        "name": "selected",
+                        "type": "indexed(dynamic(list[int]|str))",
+                        "doc": "The list of the selected point indices  ."
+                    },
+                    {
+                        "name": "color",
+                        "type": "indexed(str)",
+                        "doc": "The color of the indicated trace (or a column name for scattered)."
+                    },
+                    {
+                        "name": "selected_color",
+                        "type": "indexed(str)",
+                        "doc": "The color of the selected points for the indicated trace."
+                    },
+                    {
+                        "name": "marker",
+                        "type": "indexed(dict[str, any])",
+                        "doc": "The type of markers used for the indicated trace.<br/>See <a href=\"https://plotly.com/javascript/reference/scatter/#scatter-marker\">marker</a> for details.<br/>Color, opacity, size and symbol can be column name."
+                    },
+                    {
+                        "name": "line",
+                        "type": "indexed(str|dict[str, any])",
+                        "doc": "The configuration of the line used for the indicated trace.<br/>See <a href=\"https://plotly.com/javascript/reference/scatter/#scatter-line\">line</a> for details.<br/>If the value is a string, it must be a dash type or pattern (see <a href=\"https://plotly.com/python/reference/scatter/#scatter-line-dash\">dash style of lines</a> for details)."
+                    },
+                    {
+                        "name": "selected_marker",
+                        "type": "indexed(dict[str, any])",
+                        "doc": "The type of markers used for selected points in the indicated trace.<br/>See <a href=\"https://plotly.com/javascript/reference/scatter/#scatter-selected-marker\">selected marker for details."
+                    },
+                    {
+                        "name": "layout",
+                        "type": "dynamic(dict[str, any])",
+                        "doc": "The <i>plotly.js</i> compatible <a href=\"https://plotly.com/javascript/reference/layout/\">layout object</a>."
+                    },
+                    {
+                        "name": "plot_config",
+                        "type": "dict[str, any]",
+                        "doc": "The <i>plotly.js</i> compatible <a href=\"https://plotly.com/javascript/configuration-options/\"> configuration options object</a>."
+                    },
+                    {
+                        "name": "options",
+                        "type": "indexed(dict[str, any])",
+                        "doc": "The <i>plotly.js</i> compatible <a href=\"https://plotly.com/javascript/reference/\">data object where dynamic data will be overridden.</a>."
+                    },
+                    {
+                        "name": "orientation",
+                        "type": "indexed(str)",
+                        "doc": "The orientation of the indicated trace."
+                    },
+                    {
+                        "name": "text_anchor",
+                        "type": "indexed(str)",
+                        "doc": "Position of the text relative to the point.<br/>Valid values are: <i>top</i>, <i>bottom</i>, <i>left</i>, and <i>right</i>."
+                    },
+                    {
+                        "name": "xaxis",
+                        "type": "indexed(str)",
+                        "doc": "The <i>x</i> axis identifier for the indicated trace."
+                    },
+                    {
+                        "name": "yaxis",
+                        "type": "indexed(str)",
+                        "doc": "The <i>y</i> axis identifier for the indicated trace."
+                    },
+                    {
+                        "name": "width",
+                        "type": "str|int|float",
+                        "default_value": "\"100%\"",
+                        "doc": "The width, in CSS units, of this element."
+                    },
+                    {
+                        "name": "height",
+                        "type": "str|int|float",
+                        "doc": "The height, in CSS units, of this element."
+                    },
+                    {
+                        "name": "template",
+                        "type": "dict",
+                        "doc": "The Plotly layout <a href=\"https://plotly.com/javascript/layout-template/\">template</a>."
+                    },
+                    {
+                        "name": "template[dark]",
+                        "type": "dict",
+                        "doc": "The Plotly layout <a href=\"https://plotly.com/javascript/layout-template/\">template</a> applied over the base template when theme is dark."
+                    },
+                    {
+                        "name": "template[light]",
+                        "type": "dict",
+                        "doc": "The Plotly layout <a href=\"https://plotly.com/javascript/layout-template/\">template</a> applied over the base template when theme is not dark."
+                    },
+                    {
+                        "name": "decimator",
+                        "type": "indexed(taipy.gui.data.Decimator)",
+                        "doc": "A decimator instance for the indicated trace that will reduce the size of the data being sent back and forth.<br>If defined as indexed, it will impact only the indicated trace; if not, it will apply only the first trace."
+                    },
+                    {
+                        "name": "rebuild",
+                        "type": "dynamic(bool)",
+                        "default_value": "False",
+                        "doc": "Allows dynamic config refresh if set to True."
+                    },
+                    {
+                        "name": "figure",
+                        "type": "dynamic(plotly.graph_objects.Figure)",
+                        "doc": "A figure as produced by plotly."
+                    }
+                ]
+            }
+        ],
+        [
+            "file_download",
             {
-              "name": "label_start",
-              "type": "str",
-              "doc": "The label associated with the first input."
-            },
+                "inherits": [
+                    "active",
+                    "shared"
+                ],
+                "properties": [
+                    {
+                        "name": "content",
+                        "default_property": true,
+                        "type": "dynamic(path|file|URL|ReadableBuffer|None)",
+                        "doc": "The content to transfer.<br/>If this is a string, a URL, or a file, then the content is read from this source.<br/>If a readable buffer is provided (such as an array of bytes...), and to prevent the bandwidth from being consumed too much, the way the data is transferred depends on the <i>data_url_max_size</i> parameter of the application configuration (which is set to 50kB by default):\n<ul>\n<li>If the buffer size is smaller than this setting, then the raw content is generated as a data URL, encoded using base64 (i.e. <code>\"data:&lt;mimetype&gt;;base64,&lt;data&gt;\"</code>).</li>\n<li>If the buffer size exceeds this setting, then it is transferred through a temporary file.</li>\n</ul>If this property is set to None, that indicates that dynamic content is generated. Please take a look at the examples below for details on dynamic generation."
+                    },
+                    {
+                        "name": "label",
+                        "type": "dynamic(str)",
+                        "doc": "The label of the button."
+                    },
+                    {
+                        "name": "on_action",
+                        "type": "Callback",
+                        "doc": "The name of a function that is triggered when the download is terminated (or on user action if <i>content</i> is None).<br/>All the parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (optional[str]): the identifier of the button.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>\nThis dictionary has two keys:\n<ul>\n<li>action: the name of the action that triggered this callback.</li>\n<li>args: A list of two elements: <i>args[0]</i> reflects the <i>name</i> property and <i>args[1]</i> holds the file URL.</li>\n</ul>\n</li>\n</ul>",
+                        "signature": [
+                            [
+                                "state",
+                                "State"
+                            ],
+                            [
+                                "id",
+                                "str"
+                            ],
+                            [
+                                "payload",
+                                "dict"
+                            ]
+                        ]
+                    },
+                    {
+                        "name": "auto",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If True, the download starts as soon as the page is loaded."
+                    },
+                    {
+                        "name": "render",
+                        "type": "dynamic(bool)",
+                        "default_value": "True",
+                        "doc": "If True, the control is displayed.<br/>If False, the control is not displayed."
+                    },
+                    {
+                        "name": "bypass_preview",
+                        "type": "bool",
+                        "default_value": "True",
+                        "doc": "If False, allows the browser to try to show the content in a different tab.<br/>The file download is always performed."
+                    },
+                    {
+                        "name": "name",
+                        "type": "str",
+                        "doc": "A name proposition for the file to save, that the user can change."
+                    }
+                ]
+            }
+        ],
+        [
+            "file_selector",
             {
-              "name": "label_end",
-              "type": "str",
-              "doc": "The label associated with the second input."
+                "inherits": [
+                    "active",
+                    "shared"
+                ],
+                "properties": [
+                    {
+                        "name": "content",
+                        "default_property": true,
+                        "type": "dynamic(str)",
+                        "doc": "The path or the list of paths of the uploaded files."
+                    },
+                    {
+                        "name": "label",
+                        "type": "str",
+                        "doc": "The label of the button."
+                    },
+                    {
+                        "name": "on_action",
+                        "type": "Callback",
+                        "doc": "The name of the function that will be triggered.<br/>All the parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (optional[str]): the identifier of the button.</li>\n<li>payload (dict): a dictionary that contains the key \"action\" set to the name of the action that triggered this callback.</li>\n</ul>",
+                        "signature": [
+                            [
+                                "state",
+                                "State"
+                            ],
+                            [
+                                "id",
+                                "str"
+                            ],
+                            [
+                                "payload",
+                                "dict"
+                            ]
+                        ]
+                    },
+                    {
+                        "name": "multiple",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If set to True, multiple files can be uploaded."
+                    },
+                    {
+                        "name": "extensions",
+                        "type": "str",
+                        "default_value": "\".csv,.xlsx\"",
+                        "doc": "The list of file extensions that can be uploaded."
+                    },
+                    {
+                        "name": "drop_message",
+                        "type": "str",
+                        "default_value": "\"Drop here to Upload\"",
+                        "doc": "The message that is displayed when the user drags a file above the button."
+                    },
+                    {
+                        "name": "notify",
+                        "type": "bool",
+                        "default_value": "True",
+                        "doc": "If set to False, the user won't be notified of upload finish."
+                    }
+                ]
             }
-          ]
-        }
-      ],
-      [
-      "chart",
-      {
-        "inherits": [
-          "on_change",
-          "propagate"
         ],
-        "properties": [
-          {
-            "name": "data",
-            "default_property": true,
-            "required": true,
-            "type": "dynamic(any)",
-            "doc": "The data object bound to this chart control.<br/>See the section on the <a href=\"#the-data-property\"><i>data</i> property</a> below for details."
-          },
-          {
-            "name": "type",
-            "type": "indexed(str)",
-            "default_value": "scatter",
-            "doc": "Chart type.<br/>See the Plotly <a href=\"https://plotly.com/javascript/reference/\">chart type</a> documentation for details."
-          },
-          {
-            "name": "mode",
-            "type": "indexed(str)",
-            "default_value": "lines+markers",
-            "doc": "Chart mode.<br/>See the Plotly <a href=\"https://plotly.com/javascript/reference/scatter/#scatter-mode\">chart mode</a> documentation for details."
-          },
-          {
-            "name": "x",
-            "type": "indexed(str)",
-            "doc": "Column name for the <i>x</i> axis."
-          },
-          {
-            "name": "y",
-            "type": "indexed(str)",
-            "doc": "Column name for the <i>y</i> axis."
-          },
-          {
-            "name": "z",
-            "type": "indexed(str)",
-            "doc": "Column name for the <i>z</i> axis."
-          },
-          {
-            "name": "lon",
-            "type": "indexed(str)",
-            "doc": "Column name for the <i>longitude</i> value, for 'scattergeo' charts. See <a href=\"https://plotly.com/javascript/reference/scattergeo/#scattergeo-lon\">Plotly Map traces</a>."
-          },
-          {
-            "name": "lat",
-            "type": "indexed(str)",
-            "doc": "Column name for the <i>latitude</i> value, for 'scattergeo' charts. See <a href=\"https://plotly.com/javascript/reference/scattergeo/#scattergeo-lat\">Plotly Map traces</a>."
-          },
-          {
-            "name": "r",
-            "type": "indexed(str)",
-            "doc": "Column name for the <i>r</i> value, for 'scatterpolar' charts. See <a href=\"https://plotly.com/javascript/polar-chart/\">Plotly Polar charts</a>."
-          },
-          {
-            "name": "theta",
-            "type": "indexed(str)",
-            "doc": "Column name for the <i>theta</i> value, for 'scatterpolar' charts. See <a href=\"https://plotly.com/javascript/polar-chart/\">Plotly Polar charts</a>."
-          },
-          {
-            "name": "high",
-            "type": "indexed(str)",
-            "doc": "Column name for the <i>high</i> value, for 'candlestick' charts. See <a href=\"https://plotly.com/javascript/reference/candlestick/#candlestick-high\">Plotly Candlestick charts</a>."
-          },
-          {
-            "name": "low",
-            "type": "indexed(str)",
-            "doc": "Column name for the <i>low</i> value, for 'candlestick' charts. See <a href=\"https://plotly.com/javascript/reference/candlestick/#candlestick-low\">Ploty Candlestick charts</a>."
-          },
-          {
-            "name": "open",
-            "type": "indexed(str)",
-            "doc": "Column name for the <i>open</i> value, for 'candlestick' charts. See <a href=\"https://plotly.com/javascript/reference/candlestick/#candlestick-open\">Plotly Candlestick charts</a>."
-          },
-          {
-            "name": "close",
-            "type": "indexed(str)",
-            "doc": "Column name for the <i>close</i> value, for 'candlestick' charts. See <a href=\"https://plotly.com/javascript/reference/candlestick/#candlestick-close\">Plotly Candlestick charts</a>."
-          },
-          {
-            "name": "measure",
-            "type": "indexed(str)",
-            "doc": "Column name for the <i>measure</i> value, for 'waterfall' charts. See <a href=\"https://plotly.com/javascript/reference/waterfall/#waterfall-measure\">Plotly Waterfall charts</a>."
-          },
-          {
-            "name": "locations",
-            "type": "indexed(str)",
-            "doc": "Column name for the <i>locations</i> value. See <a href=\"https://plotly.com/javascript/choropleth-maps/\">Plotly Choropleth maps</a>."
-          },
-          {
-            "name": "values",
-            "type": "indexed(str)",
-            "doc": "Column name for the <i>values</i> value. See <a href=\"https://plotly.com/javascript/reference/pie/#pie-values\">Plotly Pie charts</a> or <a href=\"https://plotly.com/javascript/reference/funnelarea/#funnelarea-values\">Plotly Funnel Area charts</a>."
-          },
-          {
-            "name": "labels",
-            "type": "indexed(str)",
-            "doc": "Column name for the <i>labels</i> value. See <a href=\"https://plotly.com/javascript/reference/pie/#pie-labels\">Plotly Pie charts</a>."
-          },
-          {
-            "name": "parents",
-            "type": "indexed(str)",
-            "doc": "Column name for the <i>parents</i> value. See <a href=\"https://plotly.com/javascript/reference/treemap/#treemap-parents\">Plotly Treemap charts</a>."
-          },
-          {
-            "name": "text",
-            "type": "indexed(str)",
-            "doc": "Column name for the text associated to the point for the indicated trace.<br/>This is meaningful only when <i>mode</i> has the <i>text</i> option."
-          },
-          {
-            "name": "base",
-            "type": "indexed(str)",
-            "doc": "Column name for the <i>base</i> value. Used in bar charts only.<br/>See the Plotly <a href=\"https://plotly.com/javascript/reference/bar/#bar-base\">bar chart base</a> documentation for details.\""
-          },
-          {
-            "name": "title",
-            "type": "str",
-            "doc": "The title of this chart control."
-          },
-          {
-            "name": "render",
-            "type": "dynamic(bool)",
-            "default_value": "True",
-            "doc": "If True, this chart is visible on the page."
-          },
-          {
-            "name": "on_range_change",
-            "type": "Callback",
-            "doc": "The callback function that is invoked when the visible part of the x axis changes.<br/>The function receives three parameters:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (optional[str]): the identifier of the chart control.</li>\n<li>payload (dict[str, any]): the full details on this callback's invocation, as emitted by <a href=\"https://plotly.com/javascript/plotlyjs-events/#update-data\">Plotly</a>.</li>\n</ul>",
-            "signature": [["state", "State"], ["id", "str"], ["payload", "dict"]]
-
-          },
-          {
-            "name": "columns",
-            "type": "str|list[str]|dict[str, dict[str, str]]",
-            "default_value": "<i>All columns</i>",
-            "doc": "The list of column names\n<ul>\n<li>str: ;-separated list of column names</li>\n<li>list[str]: list of names</li>\n<li>dict: {\"column_name\": {format: \"format\", index: 1}} if index is specified, it represents the display order of the columns.\nIf not, the list order defines the index</li>\n</ul>"
-          },
-          {
-            "name": "label",
-            "type": "indexed(str)",
-            "doc": "The label for the indicated trace.<br/>This is used when the mouse hovers over a trace."
-          },
-          {
-            "name": "name",
-            "type": "indexed(str)",
-            "doc": "The name of the indicated trace."
-          },
-          {
-            "name": "selected",
-            "type": "indexed(dynamic(list[int]|str))",
-            "doc": "The list of the selected point indices  ."
-          },
-          {
-            "name": "color",
-            "type": "indexed(str)",
-            "doc": "The color of the indicated trace (or a column name for scattered)."
-          },
-          {
-            "name": "selected_color",
-            "type": "indexed(str)",
-            "doc": "The color of the selected points for the indicated trace."
-          },
-          {
-            "name": "marker",
-            "type": "indexed(dict[str, any])",
-            "doc": "The type of markers used for the indicated trace.<br/>See <a href=\"https://plotly.com/javascript/reference/scatter/#scatter-marker\">marker</a> for details.<br/>Color, opacity, size and symbol can be column name."
-          },
-          {
-            "name": "line",
-            "type": "indexed(str|dict[str, any])",
-            "doc": "The configuration of the line used for the indicated trace.<br/>See <a href=\"https://plotly.com/javascript/reference/scatter/#scatter-line\">line</a> for details.<br/>If the value is a string, it must be a dash type or pattern (see <a href=\"https://plotly.com/python/reference/scatter/#scatter-line-dash\">dash style of lines</a> for details)."
-          },
-          {
-            "name": "selected_marker",
-            "type": "indexed(dict[str, any])",
-            "doc": "The type of markers used for selected points in the indicated trace.<br/>See <a href=\"https://plotly.com/javascript/reference/scatter/#scatter-selected-marker\">selected marker for details."
-          },
-          {
-            "name": "layout",
-            "type": "dynamic(dict[str, any])",
-            "doc": "The <i>plotly.js</i> compatible <a href=\"https://plotly.com/javascript/reference/layout/\">layout object</a>."
-          },
-          {
-            "name": "plot_config",
-            "type": "dict[str, any]",
-            "doc": "The <i>plotly.js</i> compatible <a href=\"https://plotly.com/javascript/configuration-options/\"> configuration options object</a>."
-          },
-          {
-            "name": "options",
-            "type": "indexed(dict[str, any])",
-            "doc": "The <i>plotly.js</i> compatible <a href=\"https://plotly.com/javascript/reference/\">data object where dynamic data will be overridden.</a>."
-          },
-          {
-            "name": "orientation",
-            "type": "indexed(str)",
-            "doc": "The orientation of the indicated trace."
-          },
-          {
-            "name": "text_anchor",
-            "type": "indexed(str)",
-            "doc": "Position of the text relative to the point.<br/>Valid values are: <i>top</i>, <i>bottom</i>, <i>left</i>, and <i>right</i>."
-          },
-          {
-            "name": "xaxis",
-            "type": "indexed(str)",
-            "doc": "The <i>x</i> axis identifier for the indicated trace."
-          },
-          {
-            "name": "yaxis",
-            "type": "indexed(str)",
-            "doc": "The <i>y</i> axis identifier for the indicated trace."
-          },
-          {
-            "name": "width",
-            "type": "str|int|float",
-            "default_value": "\"100%\"",
-            "doc": "The width, in CSS units, of this element."
-          },
-          {
-            "name": "height",
-            "type": "str|int|float",
-            "doc": "The height, in CSS units, of this element."
-          },
-          {
-            "name": "template",
-            "type": "dict",
-            "doc": "The Plotly layout <a href=\"https://plotly.com/javascript/layout-template/\">template</a>."
-          },
-          {
-            "name": "template[dark]",
-            "type": "dict",
-            "doc": "The Plotly layout <a href=\"https://plotly.com/javascript/layout-template/\">template</a> applied over the base template when theme is dark."
-          },
-          {
-            "name": "template[light]",
-            "type": "dict",
-            "doc": "The Plotly layout <a href=\"https://plotly.com/javascript/layout-template/\">template</a> applied over the base template when theme is not dark."
-          },
-          {
-            "name": "decimator",
-            "type": "indexed(taipy.gui.data.Decimator)",
-            "doc": "A decimator instance for the indicated trace that will reduce the size of the data being sent back and forth.<br>If defined as indexed, it will impact only the indicated trace; if not, it will apply only the first trace."
-          },
-          {
-            "name": "rebuild",
-            "type": "dynamic(bool)",
-            "default_value": "False",
-            "doc": "Allows dynamic config refresh if set to True."
-          },
-          {
-            "name": "figure",
-            "type": "dynamic(plotly.graph_objects.Figure)",
-            "doc": "A figure as produced by plotly."
-          }
-        ]
-      }
-    ],
-    [
-      "file_download",
-      {
-        "inherits": [
-          "active",
-          "shared"
+        [
+            "image",
+            {
+                "inherits": [
+                    "active",
+                    "shared"
+                ],
+                "properties": [
+                    {
+                        "name": "content",
+                        "default_property": true,
+                        "type": "dynamic(path|URL|file|ReadableBuffer)",
+                        "doc": "The image source.<br/>If a buffer is provided (string, array of bytes...), and in order to prevent the bandwidth to be consumed too much, the way the image data is transferred depends on the <i>data_url_max_size</i> parameter of the application configuration (which is set to 50kB by default):\n<ul>\n<li>If the size of the buffer is smaller than this setting, then the raw content is generated as a\n  data URL, encoded using base64 (i.e. <code>\"data:&lt;mimetype&gt;;base64,&lt;data&gt;\"</code>).</li>\n<li>If the size of the buffer is greater than this setting, then it is transferred through a temporary\n  file.</li>\n</ul>"
+                    },
+                    {
+                        "name": "label",
+                        "type": "dynamic(str)",
+                        "doc": "The label for this image."
+                    },
+                    {
+                        "name": "on_action",
+                        "type": "Callback",
+                        "doc": "The name of a function that is triggered when the user clicks on the image.<br/>All the parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (optional[str]): the identifier of the button.</li>\n<li>payload (dict): a dictionary that contains the key \"action\" set to the name of the action that triggered this callback.</li>\n</ul>",
+                        "signature": [
+                            [
+                                "state",
+                                "State"
+                            ],
+                            [
+                                "id",
+                                "str"
+                            ],
+                            [
+                                "payload",
+                                "dict"
+                            ]
+                        ]
+                    },
+                    {
+                        "name": "width",
+                        "type": "str|int|float",
+                        "default_value": "\"300px\"",
+                        "doc": "The width, in CSS units, of this element."
+                    },
+                    {
+                        "name": "height",
+                        "type": "str|int|float",
+                        "doc": "The height, in CSS units, of this element."
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "content",
-            "default_property": true,
-            "type": "dynamic(path|file|URL|ReadableBuffer|None)",
-            "doc": "The content to transfer.<br/>If this is a string, a URL, or a file, then the content is read from this source.<br/>If a readable buffer is provided (such as an array of bytes...), and to prevent the bandwidth from being consumed too much, the way the data is transferred depends on the <i>data_url_max_size</i> parameter of the application configuration (which is set to 50kB by default):\n<ul>\n<li>If the buffer size is smaller than this setting, then the raw content is generated as a data URL, encoded using base64 (i.e. <code>\"data:&lt;mimetype&gt;;base64,&lt;data&gt;\"</code>).</li>\n<li>If the buffer size exceeds this setting, then it is transferred through a temporary file.</li>\n</ul>If this property is set to None, that indicates that dynamic content is generated. Please take a look at the examples below for details on dynamic generation."
-          },
-          {
-            "name": "label",
-            "type": "dynamic(str)",
-            "doc": "The label of the button."
-          },
-          {
-            "name": "on_action",
-            "type": "Callback",
-            "doc": "The name of a function that is triggered when the download is terminated (or on user action if <i>content</i> is None).<br/>All the parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (optional[str]): the identifier of the button.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>\nThis dictionary has two keys:\n<ul>\n<li>action: the name of the action that triggered this callback.</li>\n<li>args: A list of two elements: <i>args[0]</i> reflects the <i>name</i> property and <i>args[1]</i> holds the file URL.</li>\n</ul>\n</li>\n</ul>",
-            "signature": [["state", "State"], ["id", "str"], ["payload", "dict"]]
-          },
-          {
-            "name": "auto",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "If True, the download starts as soon as the page is loaded."
-          },
-          {
-            "name": "render",
-            "type": "dynamic(bool)",
-            "default_value": "True",
-            "doc": "If True, the control is displayed.<br/>If False, the control is not displayed."
-          },
-          {
-            "name": "bypass_preview",
-            "type": "bool",
-            "default_value": "True",
-            "doc": "If False, allows the browser to try to show the content in a different tab.<br/>The file download is always performed."
-          },
-          {
-            "name": "name",
-            "type": "str",
-            "doc": "A name proposition for the file to save, that the user can change."
-          }
-        ]
-      }
-    ],
-    [
-      "file_selector",
-      {
-        "inherits": [
-          "active",
-          "shared"
+        [
+            "indicator",
+            {
+                "inherits": [
+                    "shared"
+                ],
+                "properties": [
+                    {
+                        "name": "display",
+                        "default_property": true,
+                        "type": "dynamic(any)",
+                        "doc": "The label to be displayed.<br/>This can be formatted if it is a numerical value."
+                    },
+                    {
+                        "name": "value",
+                        "type": "dynamic(int,float)",
+                        "default_value": "<i>min</i>",
+                        "doc": "The location of the label on the [<i>min</i>, <i>max</i>] range."
+                    },
+                    {
+                        "name": "min",
+                        "type": "int|float",
+                        "default_value": "0",
+                        "doc": "The minimum value of the range."
+                    },
+                    {
+                        "name": "max",
+                        "type": "int|float",
+                        "default_value": "100",
+                        "doc": "The maximum value of the range."
+                    },
+                    {
+                        "name": "format",
+                        "type": "str",
+                        "doc": "The format to use when displaying the value.<br/>This uses the <code>printf</code> syntax."
+                    },
+                    {
+                        "name": "orientation",
+                        "type": "str",
+                        "default_value": "\"horizontal\"",
+                        "doc": "The orientation of this slider."
+                    },
+                    {
+                        "name": "width",
+                        "type": "str",
+                        "default_value": "None",
+                        "doc": "The width, in CSS units, of the indicator (used when orientation is horizontal)."
+                    },
+                    {
+                        "name": "height",
+                        "type": "str",
+                        "default_value": "None",
+                        "doc": "The height, in CSS units, of the indicator (used when orientation is vertical)."
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "content",
-            "default_property": true,
-            "type": "dynamic(str)",
-            "doc": "The path or the list of paths of the uploaded files."
-          },
-          {
-            "name": "label",
-            "type": "str",
-            "doc": "The label of the button."
-          },
-          {
-            "name": "on_action",
-            "type": "Callback",
-            "doc": "The name of the function that will be triggered.<br/>All the parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (optional[str]): the identifier of the button.</li>\n<li>payload (dict): a dictionary that contains the key \"action\" set to the name of the action that triggered this callback.</li>\n</ul>",
-            "signature": [["state", "State"], ["id", "str"], ["payload", "dict"]]
-          },
-          {
-            "name": "multiple",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "If set to True, multiple files can be uploaded."
-          },
-          {
-            "name": "extensions",
-            "type": "str",
-            "default_value": "\".csv,.xlsx\"",
-            "doc": "The list of file extensions that can be uploaded."
-          },
-          {
-            "name": "drop_message",
-            "type": "str",
-            "default_value": "\"Drop here to Upload\"",
-            "doc": "The message that is displayed when the user drags a file above the button."
-          },
-          {
-            "name": "notify",
-            "type": "bool",
-            "default_value": "True",
-            "doc": "If set to False, the user won't be notified of upload finish."
-          }
-        ]
-      }
-    ],
-    [
-      "image",
-      {
-        "inherits": [
-          "active",
-          "shared"
+        [
+            "login",
+            {
+                "inherits": [
+                    "shared"
+                ],
+                "properties": [
+                    {
+                        "name": "title",
+                        "default_property": true,
+                        "type": "str",
+                        "default_value": "\"Log in\"",
+                        "doc": "The title of the login dialog."
+                    },
+                    {
+                        "name": "on_action",
+                        "type": "Callback",
+                        "doc": "The name of the function that is triggered when the dialog button is pressed.<br/><br/>All the parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (str): the identifier of the button.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>\nThis dictionary has the following keys:\n<ul>\n<li>action: the name of the action that triggered this callback.</li>\n<li>args: a list with three elements:<ul><li>The first element is the username</li><li>The second element is the password</li><li>The third element is the current page name</li></ul></li></li>\n</ul>\n</li>\n</ul><br/>When the button is pressed, and if this property is not set, Taipy will try to find a callback function called <i>on_login()</i> and invoke it with the parameters listed above.",
+                        "signature": [
+                            [
+                                "state",
+                                "State"
+                            ],
+                            [
+                                "id",
+                                "str"
+                            ],
+                            [
+                                "payload",
+                                "dict"
+                            ]
+                        ]
+                    },
+                    {
+                        "name": "message",
+                        "type": "dynamic(str)",
+                        "doc": "The message shown in the dialog."
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "content",
-            "default_property": true,
-            "type": "dynamic(path|URL|file|ReadableBuffer)",
-            "doc": "The image source.<br/>If a buffer is provided (string, array of bytes...), and in order to prevent the bandwidth to be consumed too much, the way the image data is transferred depends on the <i>data_url_max_size</i> parameter of the application configuration (which is set to 50kB by default):\n<ul>\n<li>If the size of the buffer is smaller than this setting, then the raw content is generated as a\n  data URL, encoded using base64 (i.e. <code>\"data:&lt;mimetype&gt;;base64,&lt;data&gt;\"</code>).</li>\n<li>If the size of the buffer is greater than this setting, then it is transferred through a temporary\n  file.</li>\n</ul>"
-          },
-          {
-            "name": "label",
-            "type": "dynamic(str)",
-            "doc": "The label for this image."
-          },
-          {
-            "name": "on_action",
-            "type": "str",
-            "doc": "The name of a function that is triggered when the user clicks on the image.<br/>All the parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (optional[str]): the identifier of the button.</li>\n<li>payload (dict): a dictionary that contains the key \"action\" set to the name of the action that triggered this callback.</li>\n</ul>",
-            "signature": [["state", "State"], ["id", "str"], ["payload", "dict"]]
-          },
-          {
-            "name": "width",
-            "type": "str|int|float",
-            "default_value": "\"300px\"",
-            "doc": "The width, in CSS units, of this element."
-          },
-          {
-            "name": "height",
-            "type": "str|int|float",
-            "doc": "The height, in CSS units, of this element."
-          }
-        ]
-      }
-    ],
-    [
-      "indicator",
-      {
-        "inherits": [
-          "shared"
+        [
+            "menu",
+            {
+                "inherits": [
+                    "active"
+                ],
+                "properties": [
+                    {
+                        "name": "lov",
+                        "default_property": true,
+                        "type": "dynamic(str|list[str|Icon|any])",
+                        "doc": "The list of menu option values."
+                    },
+                    {
+                        "name": "adapter",
+                        "type": "Function",
+                        "default_value": "`\"lambda x: str(x)\"`",
+                        "doc": "The function that transforms an element of <i>lov</i> into a <i>tuple(id:str, label:str|Icon)</i>."
+                    },
+                    {
+                        "name": "type",
+                        "type": "str",
+                        "default_value": "<i>Type of the first lov element</i>",
+                        "doc": "Must be specified if <i>lov</i> contains a non specific type of data (ex: dict).<br/><i>value</i> must be of that type, <i>lov</i> must be an iterable on this type, and the adapter function will receive an object of this type."
+                    },
+                    {
+                        "name": "label",
+                        "type": "str",
+                        "doc": "The title of the menu."
+                    },
+                    {
+                        "name": "width",
+                        "type": "str",
+                        "default_value": "\"15vw\"",
+                        "doc": "The width, in CSS units, of the menu when unfolded.<br/>Note that when running on a mobile device, the property <i>width[active]</i> is used instead."
+                    },
+                    {
+                        "name": "width[mobile]",
+                        "type": "str",
+                        "default_value": "\"85vw\"",
+                        "doc": "The width, in CSS units, of the menu when unfolded, on a mobile device."
+                    },
+                    {
+                        "name": "on_action",
+                        "type": "Callback",
+                        "doc": "The name of the function that is triggered when a menu option is selected.<br/><br/>All the parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (str): the identifier of the button.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>\nThis dictionary has the following keys:\n<ul>\n<li>action: the name of the action that triggered this callback.</li>\n<li>args: List where the first element contains the id of the selected option.</li>\n</ul>\n</li>\n</ul>",
+                        "signature": [
+                            [
+                                "state",
+                                "State"
+                            ],
+                            [
+                                "id",
+                                "str"
+                            ],
+                            [
+                                "payload",
+                                "dict"
+                            ]
+                        ]
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "display",
-            "default_property": true,
-            "type": "dynamic(any)",
-            "doc": "The label to be displayed.<br/>This can be formatted if it is a numerical value."
-          },
-          {
-            "name": "value",
-            "type": "dynamic(int,float)",
-            "default_value": "<i>min</i>",
-            "doc": "The location of the label on the [<i>min</i>, <i>max</i>] range."
-          },
-          {
-            "name": "min",
-            "type": "int|float",
-            "default_value": "0",
-            "doc": "The minimum value of the range."
-          },
-          {
-            "name": "max",
-            "type": "int|float",
-            "default_value": "100",
-            "doc": "The maximum value of the range."
-          },
-          {
-            "name": "format",
-            "type": "str",
-            "doc": "The format to use when displaying the value.<br/>This uses the <code>printf</code> syntax."
-          },
-          {
-            "name": "orientation",
-            "type": "str",
-            "default_value": "\"horizontal\"",
-            "doc": "The orientation of this slider."
-          },
-          {
-            "name": "width",
-            "type": "str",
-            "default_value": "None",
-            "doc": "The width, in CSS units, of the indicator (used when orientation is horizontal)."
-          },
-          {
-            "name": "height",
-            "type": "str",
-            "default_value": "None",
-            "doc": "The height, in CSS units, of the indicator (used when orientation is vertical)."
-          }
-        ]
-      }
-    ],
-    ["login", {
-        "inherits": ["shared"],
-        "properties": [
+        [
+            "navbar",
             {
-                "name": "title",
-                "default_property": true,
-                "type": "str",
-                "default_value": "\"Log in\"",
-                "doc": "The title of the login dialog."
-            },
+                "inherits": [
+                    "active",
+                    "shared"
+                ],
+                "properties": [
+                    {
+                        "name": "lov",
+                        "default_property": true,
+                        "type": "dict[str, any]",
+                        "doc": "The list of pages. The keys should be:\n<ul>\n<li>page id (start with \"/\")</li>\n<li>or full URL</li>\n</ul>\nThe values are labels. See the <a href=\"../../binding/#list-of-values\">section on List of Values</a> for details."
+                    }
+                ]
+            }
+        ],
+        [
+            "selector",
             {
-                "name": "on_action",
-                "type": "Callback",
-                "doc": "The name of the function that is triggered when the dialog button is pressed.<br/><br/>All the parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (str): the identifier of the button.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>\nThis dictionary has the following keys:\n<ul>\n<li>action: the name of the action that triggered this callback.</li>\n<li>args: a list with three elements:<ul><li>The first element is the username</li><li>The second element is the password</li><li>The third element is the current page name</li></ul></li></li>\n</ul>\n</li>\n</ul><br/>When the button is pressed, and if this property is not set, Taipy will try to find a callback function called <i>on_login()</i> and invoke it with the parameters listed above.",
-                "signature": [["state", "State"], ["id", "str"], ["payload", "dict"]]
-              },
-              {
-                "name": "message",
-                "type": "dynamic(str)",
-                "doc": "The message shown in the dialog."
-              }
-
-        ]
-    }],
-    [
-      "menu",
-      {
-        "inherits": [
-          "active"
+                "inherits": [
+                    "lovComp",
+                    "propagate"
+                ],
+                "properties": [
+                    {
+                        "name": "filter",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If True, this control is combined with a filter input area."
+                    },
+                    {
+                        "name": "multiple",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If True, the user can select multiple items."
+                    },
+                    {
+                        "name": "width",
+                        "type": "str|int",
+                        "default_value": "\"360px\"",
+                        "doc": "The width, in CSS units, of this element."
+                    },
+                    {
+                        "name": "height",
+                        "type": "str|int",
+                        "doc": "The height, in CSS units, of this element."
+                    },
+                    {
+                        "name": "dropdown",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If True, the list of items is shown in a dropdown menu.<br/><br/>You cannot use the filter in that situation."
+                    },
+                    {
+                        "name": "label",
+                        "type": "str",
+                        "default_value": "None",
+                        "doc": "The label associated with the selector when in dropdown mode."
+                    },
+                    {
+                        "name": "mode",
+                        "type": "str",
+                        "doc": "Define the way the selector is displayed:<ul><li>&quot;radio&quot;: list of radio buttons</li><li>&quot;check&quot;: list of check buttons</li><li>any other value: selector as usual."
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "lov",
-            "default_property": true,
-            "type": "dynamic(str|list[str|Icon|any])",
-            "doc": "The list of menu option values."
-          },
-          {
-            "name": "adapter",
-            "type": "Function",
-            "default_value": "`\"lambda x: str(x)\"`",
-            "doc": "The function that transforms an element of <i>lov</i> into a <i>tuple(id:str, label:str|Icon)</i>."
-          },
-          {
-            "name": "type",
-            "type": "str",
-            "default_value": "<i>Type of the first lov element</i>",
-            "doc": "Must be specified if <i>lov</i> contains a non specific type of data (ex: dict).<br/><i>value</i> must be of that type, <i>lov</i> must be an iterable on this type, and the adapter function will receive an object of this type."
-          },
-          {
-            "name": "label",
-            "type": "str",
-            "doc": "The title of the menu."
-          },
-          {
-            "name": "width",
-            "type": "str",
-            "default_value": "\"15vw\"",
-            "doc": "The width, in CSS units, of the menu when unfolded.<br/>Note that when running on a mobile device, the property <i>width[active]</i> is used instead."
-          },
-          {
-            "name": "width[mobile]",
-            "type": "str",
-            "default_value": "\"85vw\"",
-            "doc": "The width, in CSS units, of the menu when unfolded, on a mobile device."
-          },
-          {
-            "name": "on_action",
-            "type": "Callback",
-            "doc": "The name of the function that is triggered when a menu option is selected.<br/><br/>All the parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (str): the identifier of the button.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>\nThis dictionary has the following keys:\n<ul>\n<li>action: the name of the action that triggered this callback.</li>\n<li>args: List where the first element contains the id of the selected option.</li>\n</ul>\n</li>\n</ul>",
-            "signature": [["state", "State"], ["id", "str"], ["payload", "dict"]]
-          }
-        ]
-      }
-    ],
-    [
-      "navbar",
-      {
-        "inherits": [
-            "active",
-            "shared"
-          ],
-          "properties": [
+        [
+            "status",
             {
-                "name": "lov",
-                "default_property": true,
-                "type": "dict[str, any]",
-                "doc": "The list of pages. The keys should be:\n<ul>\n<li>page id (start with \"/\")</li>\n<li>or full URL</li>\n</ul>\nThe values are labels. See the <a href=\"../../binding/#list-of-values\">section on List of Values</a> for details."
+                "inherits": [
+                    "shared"
+                ],
+                "properties": [
+                    {
+                        "name": "value",
+                        "default_property": true,
+                        "type": "tuple|dict|list[dict]|list[tuple]",
+                        "doc": "The different status items to represent. See below."
+                    },
+                    {
+                        "name": "without_close",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If True, the user cannot remove the status items from the list."
+                    }
+                ]
             }
-        ]
-      }
-    ],
-    [
-      "selector",
-      {
-        "inherits": [
-          "lovComp",
-          "propagate"
         ],
-        "properties": [
-          {
-            "name": "filter",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "If True, this control is combined with a filter input area."
-          },
-          {
-            "name": "multiple",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "If True, the user can select multiple items."
-          },
-          {
-            "name": "width",
-            "type": "str|int",
-            "default_value": "\"360px\"",
-            "doc": "The width, in CSS units, of this element."
-          },
-          {
-            "name": "height",
-            "type": "str|int",
-            "doc": "The height, in CSS units, of this element."
-          },
-          {
-            "name": "dropdown",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "If True, the list of items is shown in a dropdown menu.<br/><br/>You cannot use the filter in that situation."
-          },
-          {
-            "name": "label",
-            "type": "str",
-            "default_value": "None",
-            "doc": "The label associated with the selector when in dropdown mode."
-          },
-          {
-            "name": "mode",
-            "type": "str",
-            "doc": "Define the way the selector is displayed:<ul><li>&quot;radio&quot;: list of radio buttons</li><li>&quot;check&quot;: list of check buttons</li><li>any other value: selector as usual."
-          }
-        ]
-      }
-    ],
-    [
-      "status",
-      {
-        "inherits": [
-          "shared"
+        [
+            "table",
+            {
+                "inherits": [
+                    "active",
+                    "shared"
+                ],
+                "properties": [
+                    {
+                        "name": "data",
+                        "default_property": true,
+                        "required": true,
+                        "type": "dynamic(any)",
+                        "doc": "The data to be represented in this table. This property can be indexed to define other data for comparison."
+                    },
+                    {
+                        "name": "page_size",
+                        "type": "int",
+                        "default_value": "100",
+                        "doc": "For a paginated table, the number of visible rows."
+                    },
+                    {
+                        "name": "allow_all_rows",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "For a paginated table, adds an option to show all the rows."
+                    },
+                    {
+                        "name": "show_all",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "For a paginated table, show all the rows."
+                    },
+                    {
+                        "name": "auto_loading",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If True, the data will be loaded on demand."
+                    },
+                    {
+                        "name": "width[<i>column_name</i>]",
+                        "type": "str",
+                        "doc": "The width, in CSS units, of the indicated column."
+                    },
+                    {
+                        "name": "selected",
+                        "type": "list[int]|str",
+                        "doc": "The list of the indices of the rows to be displayed as selected."
+                    },
+                    {
+                        "name": "page_size_options",
+                        "type": "list[int]|str",
+                        "default_value": "[50, 100, 500]",
+                        "doc": "The list of available page sizes that users can choose from."
+                    },
+                    {
+                        "name": "columns",
+                        "type": "str|list[str]|dict[str, dict[str, str|int]]",
+                        "default_value": "<i>shows all columns when empty</i>",
+                        "doc": "The list of the column names to display.\n<ul>\n<li>str: Semicolon (';')-separated list of column names.</li>\n<li>list[str]: The list of column names.</li>\n<li>dict: A dictionary with entries matching: {\"col name\": {format: \"format\", index: 1}}.<br/>\nif <i>index</i> is specified, it represents the display order of the columns.\nIf <i>index</i> is not specified, the list order defines the index.<br/>\nIf <i>format</i> is specified, it is used for numbers or dates.</li>\n</ul>"
+                    },
+                    {
+                        "name": "date_format",
+                        "type": "str",
+                        "default_value": "\"MM/dd/yyyy\"",
+                        "doc": "The date format used for all date columns when the format is not specifically defined."
+                    },
+                    {
+                        "name": "number_format",
+                        "type": "str",
+                        "doc": "The number format used for all number columns when the format is not specifically defined."
+                    },
+                    {
+                        "name": "group_by[<i>column_name</i>]",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "Indicates, if True, that the given column can be aggregated.<br/>See <a href=\"#aggregation\">below</a> for details."
+                    },
+                    {
+                        "name": "apply[<i>column_name</i>]",
+                        "type": "str",
+                        "default_value": "\"first\"",
+                        "doc": "The name of the aggregation function to use.<br/>This is used only if <i>group_by[column_name]</i> is set to True.<br/>See <a href=\"#aggregation\">below</a> for details."
+                    },
+                    {
+                        "name": "style",
+                        "type": "str",
+                        "doc": "Allows the styling of table lines.<br/>See <a href=\"#dynamic-styling\">below</a> for details."
+                    },
+                    {
+                        "name": "style[<i>column_name</i>]",
+                        "type": "str",
+                        "doc": "Allows the styling of table cells.<br/>See <a href=\"#dynamic-styling\">below</a> for details."
+                    },
+                    {
+                        "name": "tooltip",
+                        "type": "str",
+                        "doc": "The name of the function that must return a tooltip text for a cell.<br/>See <a href=\"#cell-tooltips\">below</a> for details."
+                    },
+                    {
+                        "name": "tooltip[<i>column_name</i>]",
+                        "type": "str",
+                        "doc": "The name of the function that must return a tooltip text for a cell.<br/>See <a href=\"#cell-tooltips\">below</a> for details."
+                    },
+                    {
+                        "name": "width",
+                        "type": "str",
+                        "default_value": "\"100%\"",
+                        "doc": "The width, in CSS units, of this table control."
+                    },
+                    {
+                        "name": "height",
+                        "type": "str",
+                        "default_value": "\"80vh\"",
+                        "doc": "The height, in CSS units, of this table control."
+                    },
+                    {
+                        "name": "filter",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "Indicates, if True, that all columns can be filtered."
+                    },
+                    {
+                        "name": "filter[<i>column_name</i>]",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "Indicates, if True, that the indicated column can be filtered."
+                    },
+                    {
+                        "name": "nan_value",
+                        "type": "str",
+                        "default_value": "\"\"",
+                        "doc": "The replacement text for NaN (not-a-number) values."
+                    },
+                    {
+                        "name": "nan_value[<i>column_name</i>]",
+                        "type": "str",
+                        "default_value": "\"\"",
+                        "doc": "The replacement text for NaN (not-a-number) values for the indicated column."
+                    },
+                    {
+                        "name": "editable",
+                        "type": "dynamic(bool)",
+                        "default_value": "True",
+                        "doc": "Indicates, if True, that all columns can be edited."
+                    },
+                    {
+                        "name": "editable[<i>column_name</i>]",
+                        "type": "bool",
+                        "default_value": "editable",
+                        "doc": "Indicates, if False, that the indicated column cannot be edited when editable is True."
+                    },
+                    {
+                        "name": "on_edit",
+                        "type": "Callback",
+                        "doc": "The name of a function that is triggered when a cell edition is validated.<br/>All parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>var_name (str): the name of the tabular data variable.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>\nThis dictionary has the following keys:\n<ul>\n<li>index (int): the row index.</li>\n<li>col (str): the column name.</li>\n<li>value (any): the new cell value cast to the type of the column.</li>\n<li>user_value (str): the new cell value, as it was provided by the user.</li>\n<li>tz (str): the timezone if the column type is date.</li>\n</ul>\n</li>\n</ul><br/>If this property is not set, the user cannot edit cells.",
+                        "signature": [
+                            [
+                                "state",
+                                "State"
+                            ],
+                            [
+                                "var_name",
+                                "str"
+                            ],
+                            [
+                                "payload",
+                                "dict"
+                            ]
+                        ]
+                    },
+                    {
+                        "name": "on_delete",
+                        "type": "str",
+                        "doc": "The name of a function that is triggered when a row is deleted.<br/>All parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>var_name (str): the name of the tabular data variable.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>\nThis dictionary has the following keys:\n<ul>\n<li>index (int): the row index.</li>\n</ul>\n</li>\n</ul><br/>If this property is not set, the user cannot delete rows.",
+                        "signature": [
+                            [
+                                "state",
+                                "State"
+                            ],
+                            [
+                                "var_name",
+                                "str"
+                            ],
+                            [
+                                "payload",
+                                "dict"
+                            ]
+                        ]
+                    },
+                    {
+                        "name": "on_add",
+                        "type": "str",
+                        "doc": "The name of a function that is triggered when the user requests a row to be added.<br/>All parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>var_name (str): the name of the tabular data variable.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>This dictionary has the following keys:\n<ul>\n<li>index (int): the row index.</li>\n</ul>\n</li>\n</ul><br/>If this property is not set, the user cannot add rows.",
+                        "signature": [
+                            [
+                                "state",
+                                "State"
+                            ],
+                            [
+                                "var_name",
+                                "str"
+                            ],
+                            [
+                                "payload",
+                                "dict"
+                            ]
+                        ]
+                    },
+                    {
+                        "name": "on_action",
+                        "type": "Callback",
+                        "doc": "The name of a function that is triggered when the user selects a row.<br/>All parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>var_name (str): the name of the tabular data variable.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>This dictionary has the following keys:\n<ul>\n<li>action: the name of the action that triggered this callback.</li>\n<li>index (int): the row index.</li>\n<li>col (str): the column name.</li>\n<li>reason (str): the origin of the action: \"click\", or \"button\" if the cell contains a Markdown link syntax.</li>\n<li>value (str): the *link value* indicated in the cell when using a Markdown link syntax (that is, <i>reason</i> is set to \"button\").</li></ul></li></ul>.",
+                        "signature": [
+                            [
+                                "state",
+                                "State"
+                            ],
+                            [
+                                "var_name",
+                                "str"
+                            ],
+                            [
+                                "payload",
+                                "dict"
+                            ]
+                        ]
+                    },
+                    {
+                        "name": "size",
+                        "type": "str",
+                        "default_value": "\"small\"",
+                        "doc": "The size of the rows.<br/>Valid values are \"small\" and \"medium\"."
+                    },
+                    {
+                        "name": "rebuild",
+                        "type": "dynamic(bool)",
+                        "default_value": "False",
+                        "doc": "If set to True, this allows to dynamically refresh the  columns."
+                    },
+                    {
+                        "name": "lov[<i>column_name</i>]",
+                        "type": "list[str]|str",
+                        "doc": "The list of values of the indicated column."
+                    },
+                    {
+                        "name": "downloadable",
+                        "type": "boolean",
+                        "doc": "If True, a clickable icon is shown so the user can download the data as CSV."
+                    },
+                    {
+                        "name": "on_compare",
+                        "type": "str",
+                        "doc": "A data comparison function that would return a structure that identifies the differences between the different data passed as name. The default implementation compares the default data with the data[1] value.",
+                        "signature": [
+                            [
+                                "state",
+                                "State"
+                            ],
+                            [
+                                "main_data_name",
+                                "str"
+                            ],
+                            [
+                                "compare_data_names",
+                                "list[str]"
+                            ]
+                        ]
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "value",
-            "default_property": true,
-            "type": "tuple|dict|list[dict]|list[tuple]",
-            "doc": "The different status items to represent. See below."
-          },
-          {
-            "name": "without_close",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "If True, the user cannot remove the status items from the list."
-          }
-        ]
-      }
-    ],
-    [
-      "table",
-      {
-        "inherits": [
-          "active",
-          "shared"
+        [
+            "dialog",
+            {
+                "inherits": [
+                    "partial",
+                    "active",
+                    "shared"
+                ],
+                "properties": [
+                    {
+                        "name": "open",
+                        "default_property": true,
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If True, the dialog is visible. If False, it is hidden."
+                    },
+                    {
+                        "name": "on_action",
+                        "type": "Callback",
+                        "doc": "Name of a function triggered when a button is pressed.<br/>The parameters of that function are all optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (str): the identifier of the dialog.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>This dictionary has the following keys:\n<ul>\n<li>action: the name of the action that triggered this callback.</li>\n<li>args: a list where the first element contains the index of the selected label.</li>\n</ul>\n</li>\n</ul>",
+                        "signature": [
+                            [
+                                "state",
+                                "State"
+                            ],
+                            [
+                                "id",
+                                "str"
+                            ],
+                            [
+                                "payload",
+                                "dict"
+                            ]
+                        ]
+                    },
+                    {
+                        "name": "close_label",
+                        "type": "str",
+                        "default_value": "\"Close\"",
+                        "doc": "The tooltip of the top-right close icon button. In the <i>on_action</i> function, args will hold -1."
+                    },
+                    {
+                        "name": "labels",
+                        "type": " str|list[str]",
+                        "doc": "A list of labels to show in a row of buttons at the bottom of the dialog. The index of the button in the list is reported as args in the <i>on_action</i> function (-1 for the close icon)."
+                    },
+                    {
+                        "name": "width",
+                        "type": "str|int|float",
+                        "doc": "The width, in CSS units, of this dialog.<br/>(CSS property)"
+                    },
+                    {
+                        "name": "height",
+                        "type": "str|int|float",
+                        "doc": "The height, in CSS units, of this dialog.<br/>(CSS property)"
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "data",
-            "default_property": true,
-            "required": true,
-            "type": "dynamic(any)",
-            "doc": "The data to be represented in this table. This property can be indexed to define other data for comparison."
-          },
-          {
-            "name": "page_size",
-            "type": "int",
-            "default_value": "100",
-            "doc": "For a paginated table, the number of visible rows."
-          },
-          {
-            "name": "allow_all_rows",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "For a paginated table, adds an option to show all the rows."
-          },
-          {
-            "name": "show_all",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "For a paginated table, show all the rows."
-          },
-          {
-            "name": "auto_loading",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "If True, the data will be loaded on demand."
-          },
-          {
-            "name": "width[<i>column_name</i>]",
-            "type": "str",
-            "doc": "The width, in CSS units, of the indicated column."
-          },
-          {
-            "name": "selected",
-            "type": "list[int]|str",
-            "doc": "The list of the indices of the rows to be displayed as selected."
-          },
-          {
-            "name": "page_size_options",
-            "type": "list[int]|str",
-            "default_value": "[50, 100, 500]",
-            "doc": "The list of available page sizes that users can choose from."
-          },
-          {
-            "name": "columns",
-            "type": "str|list[str]|dict[str, dict[str, str|int]]",
-            "default_value": "<i>shows all columns when empty</i>",
-            "doc": "The list of the column names to display.\n<ul>\n<li>str: Semicolon (';')-separated list of column names.</li>\n<li>list[str]: The list of column names.</li>\n<li>dict: A dictionary with entries matching: {\"col name\": {format: \"format\", index: 1}}.<br/>\nif <i>index</i> is specified, it represents the display order of the columns.\nIf <i>index</i> is not specified, the list order defines the index.<br/>\nIf <i>format</i> is specified, it is used for numbers or dates.</li>\n</ul>"
-          },
-          {
-            "name": "date_format",
-            "type": "str",
-            "default_value": "\"MM/dd/yyyy\"",
-            "doc": "The date format used for all date columns when the format is not specifically defined."
-          },
-          {
-            "name": "number_format",
-            "type": "str",
-            "doc": "The number format used for all number columns when the format is not specifically defined."
-          },
-          {
-            "name": "group_by[<i>column_name</i>]",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "Indicates, if True, that the given column can be aggregated.<br/>See <a href=\"#aggregation\">below</a> for details."
-          },
-          {
-            "name": "apply[<i>column_name</i>]",
-            "type": "str",
-            "default_value": "\"first\"",
-            "doc": "The name of the aggregation function to use.<br/>This is used only if <i>group_by[column_name]</i> is set to True.<br/>See <a href=\"#aggregation\">below</a> for details."
-          },
-          {
-            "name": "style",
-            "type": "str",
-            "doc": "Allows the styling of table lines.<br/>See <a href=\"#dynamic-styling\">below</a> for details."
-          },
-          {
-            "name": "style[<i>column_name</i>]",
-            "type": "str",
-            "doc": "Allows the styling of table cells.<br/>See <a href=\"#dynamic-styling\">below</a> for details."
-          },
-          {
-            "name": "tooltip",
-            "type": "str",
-            "doc": "The name of the function that must return a tooltip text for a cell.<br/>See <a href=\"#cell-tooltips\">below</a> for details."
-          },
-          {
-            "name": "tooltip[<i>column_name</i>]",
-            "type": "str",
-            "doc": "The name of the function that must return a tooltip text for a cell.<br/>See <a href=\"#cell-tooltips\">below</a> for details."
-          },
-          {
-            "name": "width",
-            "type": "str",
-            "default_value": "\"100%\"",
-            "doc": "The width, in CSS units, of this table control."
-          },
-          {
-            "name": "height",
-            "type": "str",
-            "default_value": "\"80vh\"",
-            "doc": "The height, in CSS units, of this table control."
-          },
-          {
-            "name": "filter",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "Indicates, if True, that all columns can be filtered."
-          },
-          {
-            "name": "filter[<i>column_name</i>]",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "Indicates, if True, that the indicated column can be filtered."
-          },
-          {
-            "name": "nan_value",
-            "type": "str",
-            "default_value": "\"\"",
-            "doc": "The replacement text for NaN (not-a-number) values."
-          },
-          {
-            "name": "nan_value[<i>column_name</i>]",
-            "type": "str",
-            "default_value": "\"\"",
-            "doc": "The replacement text for NaN (not-a-number) values for the indicated column."
-          },
-          {
-            "name": "editable",
-            "type": "dynamic(bool)",
-            "default_value": "True",
-            "doc": "Indicates, if True, that all columns can be edited."
-          },
-          {
-            "name": "editable[<i>column_name</i>]",
-            "type": "bool",
-            "default_value": "editable",
-            "doc": "Indicates, if False, that the indicated column cannot be edited when editable is True."
-          },
-          {
-            "name": "on_edit",
-            "type": "Callback",
-            "doc": "The name of a function that is triggered when a cell edition is validated.<br/>All parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>var_name (str): the name of the tabular data variable.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>\nThis dictionary has the following keys:\n<ul>\n<li>index (int): the row index.</li>\n<li>col (str): the column name.</li>\n<li>value (any): the new cell value cast to the type of the column.</li>\n<li>user_value (str): the new cell value, as it was provided by the user.</li>\n<li>tz (str): the timezone if the column type is date.</li>\n</ul>\n</li>\n</ul><br/>If this property is not set, the user cannot edit cells.",
-            "signature": [["state", "State"], ["var_name", "str"], ["payload", "dict"]]
-          },
-          {
-            "name": "on_delete",
-            "type": "str",
-            "doc": "The name of a function that is triggered when a row is deleted.<br/>All parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>var_name (str): the name of the tabular data variable.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>\nThis dictionary has the following keys:\n<ul>\n<li>index (int): the row index.</li>\n</ul>\n</li>\n</ul><br/>If this property is not set, the user cannot delete rows.",
-            "signature": [["state", "State"], ["var_name", "str"], ["payload", "dict"]]
-          },
-          {
-            "name": "on_add",
-            "type": "str",
-            "doc": "The name of a function that is triggered when the user requests a row to be added.<br/>All parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>var_name (str): the name of the tabular data variable.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>This dictionary has the following keys:\n<ul>\n<li>index (int): the row index.</li>\n</ul>\n</li>\n</ul><br/>If this property is not set, the user cannot add rows.",
-            "signature": [["state", "State"], ["var_name", "str"], ["payload", "dict"]]
-          },
-          {
-            "name": "on_action",
-            "type": "str",
-            "doc": "The name of a function that is triggered when the user selects a row.<br/>All parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>var_name (str): the name of the tabular data variable.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>This dictionary has the following keys:\n<ul>\n<li>action: the name of the action that triggered this callback.</li>\n<li>index (int): the row index.</li>\n<li>col (str): the column name.</li>\n<li>reason (str): the origin of the action: \"click\", or \"button\" if the cell contains a Markdown link syntax.</li>\n<li>value (str): the *link value* indicated in the cell when using a Markdown link syntax (that is, <i>reason</i> is set to \"button\").</li></ul></li></ul>.",
-            "signature": [["state", "State"], ["var_name", "str"], ["payload", "dict"]]
-          },
-          {
-            "name": "size",
-            "type": "str",
-            "default_value": "\"small\"",
-            "doc": "The size of the rows.<br/>Valid values are \"small\" and \"medium\"."
-          },
-          {
-            "name": "rebuild",
-            "type": "dynamic(bool)",
-            "default_value": "False",
-            "doc": "If set to True, this allows to dynamically refresh the  columns."
-          },
-          {
-            "name": "lov[<i>column_name</i>]",
-            "type": "list[str]|str",
-            "doc": "The list of values of the indicated column."
-          },
-          {
-            "name": "downloadable",
-            "type": "boolean",
-            "doc": "If True, a clickable icon is shown so the user can download the data as CSV."
-          },
-          {
-            "name": "on_compare",
-            "type": "str",
-            "doc": "A data comparison function that would return a structure that identifies the differences between the different data passed as name. The default implementation compares the default data with the data[1] value.",
-            "signature": [["state", "State"], ["main_data_name", "str"], ["compare_data_names", "list[str]"]]
-          }
+        [
+            "tree",
+            {
+                "inherits": [
+                    "selector"
+                ],
+                "properties": [
+                    {
+                        "name": "expanded",
+                        "type": "dynamic(bool|str[])",
+                        "default_value": "True",
+                        "doc": "If Boolean and False, only one node can be expanded at one given level. Otherwise this should be set to an array of the node identifiers that need to be expanded."
+                    },
+                    {
+                        "name": "multiple",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If True, the user can select multiple items by holding the <code>Ctrl</code> key while clicking on items."
+                    },
+                    {
+                        "name": "select_leafs_only",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If True, the user can only select leaf nodes."
+                    },
+                    {
+                        "name": "row_height",
+                        "type": "str",
+                        "doc": "The height, in CSS units, of each row."
+                    }
+                ]
+            }
         ]
-      }
     ],
-    [
-      "dialog",
-      {
-        "inherits": [
-          "partial",
-          "active",
-          "shared"
+    "blocks": [
+        [
+            "part",
+            {
+                "inherits": [
+                    "partial",
+                    "shared"
+                ],
+                "properties": [
+                    {
+                        "name": "render",
+                        "type": "dynamic(bool)",
+                        "default_value": "True",
+                        "doc": "If True, this part is visible on the page.<br/>If False, the part is hidden and its content is not displayed."
+                    },
+                    {
+                        "name": "class_name",
+                        "default_property": true,
+                        "type": "dynamic(str)",
+                        "doc": "A list of CSS class names, separated by white spaces, that will be associated with the generated HTML Element.<br/>These class names are added to the default <code>taipy-part</code>."
+                    },
+                    {
+                        "name": "page",
+                        "type": "dynamic(str)",
+                        "doc": "The page to show as the content of the block (page name if defined or a URL in an <i>iframe</i>).<br/>This should not be defined if <i>partial</i> is set."
+                    },
+                    {
+                        "name": "height",
+                        "type": "dynamic(str)",
+                        "doc": "The height, in CSS units, of this block."
+                    },
+                    {
+                        "name": "content",
+                        "type": "dynamic(any)",
+                        "doc": "The content provided to the part. See the documentation section on content providers."
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "open",
-            "default_property": true,
-            "type": "bool",
-            "default_value": "False",
-            "doc": "If True, the dialog is visible. If False, it is hidden."
-          },
-          {
-            "name": "on_action",
-            "type": "Callback",
-            "doc": "Name of a function triggered when a button is pressed.<br/>The parameters of that function are all optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (str): the identifier of the dialog.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>This dictionary has the following keys:\n<ul>\n<li>action: the name of the action that triggered this callback.</li>\n<li>args: a list where the first element contains the index of the selected label.</li>\n</ul>\n</li>\n</ul>",
-            "signature": [["state", "State"], ["id", "str"], ["payload", "dict"]]
-          },
-          {
-            "name": "close_label",
-            "type": "str",
-            "default_value": "\"Close\"",
-            "doc": "The tooltip of the top-right close icon button. In the <i>on_action</i> function, args will hold -1."
-          },
-          {
-            "name": "labels",
-            "type": " str|list[str]",
-            "doc": "A list of labels to show in a row of buttons at the bottom of the dialog. The index of the button in the list is reported as args in the <i>on_action</i> function (-1 for the close icon)."
-          },
-          {
-            "name": "width",
-            "type": "str|int|float",
-            "doc": "The width, in CSS units, of this dialog.<br/>(CSS property)"
-          },
-          {
-            "name": "height",
-            "type": "str|int|float",
-            "doc": "The height, in CSS units, of this dialog.<br/>(CSS property)"
-          }
-        ]
-      }
-    ],
-    [
-      "tree",
-      {
-        "inherits": [
-          "selector"
+        [
+            "expandable",
+            {
+                "inherits": [
+                    "partial",
+                    "shared",
+                    "on_change"
+                ],
+                "properties": [
+                    {
+                        "name": "title",
+                        "default_property": true,
+                        "type": "dynamic(str)",
+                        "doc": "Title of this block element."
+                    },
+                    {
+                        "name": "expanded",
+                        "type": "dynamic(bool)",
+                        "default_value": "True",
+                        "doc": "If True, the block is expanded, and the content is displayed.<br/>If False, the block is collapsed and its content is hidden."
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "expanded",
-            "type": "dynamic(bool|str[])",
-            "default_value": "True",
-            "doc": "If Boolean and False, only one node can be expanded at one given level. Otherwise this should be set to an array of the node identifiers that need to be expanded."
-          },
-          {
-            "name": "multiple",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "If True, the user can select multiple items by holding the <code>Ctrl</code> key while clicking on items."
-          },
-          {
-            "name": "select_leafs_only",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "If True, the user can only select leaf nodes."
-          },
-          {
-            "name": "row_height",
-            "type": "str",
-            "doc": "The height, in CSS units, of each row."
-          }
-        ]
-      }
-    ]
-  ],
-  "blocks": [
-    [
-      "part",
-      {
-        "inherits": [
-          "partial",
-          "shared"
+        [
+            "layout",
+            {
+                "inherits": [
+                    "shared"
+                ],
+                "properties": [
+                    {
+                        "name": "columns",
+                        "default_property": true,
+                        "type": "str",
+                        "default_value": "\"1 1\"",
+                        "doc": "The list of weights for each column.<br/>For example, `\"1 2\"` creates a 2 column grid:\n<ul>\n<li>1fr</li>\n<li>2fr</li>\n</ul><br/>The creation of multiple same size columns can be simplified by using the multiply sign eg. \"5*1\" is equivalent to \"1 1 1 1 1\"."
+                    },
+                    {
+                        "name": "columns[mobile]",
+                        "type": "str",
+                        "default_value": "\"1\"",
+                        "doc": "The list of weights for each column, when displayed on a mobile device.<br/>The syntax is the same as for <i>columns</i>."
+                    },
+                    {
+                        "name": "gap",
+                        "type": "str",
+                        "default_value": "\"0.5rem\"",
+                        "doc": "The size of the gap between the columns."
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "render",
-            "type": "dynamic(bool)",
-            "default_value": "True",
-            "doc": "If True, this part is visible on the page.<br/>If False, the part is hidden and its content is not displayed."
-          },
-          {
-            "name": "class_name",
-            "default_property": true,
-            "type": "dynamic(str)",
-            "doc": "A list of CSS class names, separated by white spaces, that will be associated with the generated HTML Element.<br/>These class names are added to the default <code>taipy-part</code>."
-          },
-          {
-            "name": "page",
-            "type": "dynamic(str)",
-            "doc": "The page to show as the content of the block (page name if defined or a URL in an <i>iframe</i>).<br/>This should not be defined if <i>partial</i> is set."
-          },
-          {
-            "name": "height",
-            "type": "dynamic(str)",
-            "doc": "The height, in CSS units, of this block."
-          },
-          {
-            "name": "content",
-            "type": "dynamic(any)",
-            "doc": "The content provided to the part. See the documentation section on content providers."
-          }
+        [
+            "pane",
+            {
+                "inherits": [
+                    "partial",
+                    "on_change",
+                    "active",
+                    "shared"
+                ],
+                "properties": [
+                    {
+                        "name": "open",
+                        "default_property": true,
+                        "type": "dynamic(bool)",
+                        "default_value": "False",
+                        "doc": "If True, this pane is visible on the page.<br/>If False, the pane is hidden."
+                    },
+                    {
+                        "name": "on_close",
+                        "type": "Callback",
+                        "doc": "The name of a function that is triggered when this pane is closed (if the user clicks outside of it or presses the Esc key).<br/>All parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (optional[str]): the identifier of the button.</li>\n</ul><br/>If this property is not set, no function is called when this pane is closed.",
+                        "signature": [
+                            [
+                                "state",
+                                "State"
+                            ],
+                            [
+                                "id",
+                                "str"
+                            ]
+                        ]
+                    },
+                    {
+                        "name": "anchor",
+                        "type": "str",
+                        "default_value": "\"left\"",
+                        "doc": "Anchor side of the pane.<br/>Valid values are \"left\", \"right\", \"top\", or \"bottom\"."
+                    },
+                    {
+                        "name": "width",
+                        "type": "str",
+                        "default_value": "\"30vw\"",
+                        "doc": "Width, in CSS units, of this pane.<br/>This is used only if <i>anchor</i> is \"left\" or \"right\"."
+                    },
+                    {
+                        "name": "height",
+                        "type": "str",
+                        "default_value": "\"30vh\"",
+                        "doc": "Height, in CSS units, of this pane.<br/>This is used only if <i>anchor</i> is \"top\" or \"bottom\"."
+                    },
+                    {
+                        "name": "persistent",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If False, the pane covers the page where it appeared and disappears if the user clicks in the page.<br/>If True, the pane appears next to the page. Note that the parent section of the pane must have the <i>flex</i> display mode set. See below for an example using the <code>persistent</code> property."
+                    }
+                ]
+            }
         ]
-      }
     ],
-    [
-      "expandable",
-      {
-        "inherits": [
-          "partial",
-          "shared",
-          "on_change"
+    "undocumented": [
+        [
+            "active",
+            {
+                "properties": [
+                    {
+                        "name": "active",
+                        "type": "dynamic(bool)",
+                        "default_value": "True",
+                        "doc": "Indicates if this component is active.<br/>An inactive component allows no user interaction."
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "title",
-            "default_property": true,
-            "type": "dynamic(str)",
-            "doc": "Title of this block element."
-          },
-          {
-            "name": "expanded",
-            "type": "dynamic(bool)",
-            "default_value": "True",
-            "doc": "If True, the block is expanded, and the content is displayed.<br/>If False, the block is collapsed and its content is hidden."
-          }
-        ]
-      }
-    ],
-    [
-      "layout",
-      {
-        "inherits": [
-          "shared"
+        [
+            "lovComp",
+            {
+                "inherits": [
+                    "on_change"
+                ],
+                "properties": [
+                    {
+                        "name": "value",
+                        "default_property": true,
+                        "type": "dynamic(any)",
+                        "doc": "Bound to the selection value."
+                    },
+                    {
+                        "name": "lov",
+                        "type": "dict[str, any]",
+                        "doc": "The list of values. See the <a href=\"../../binding/#list-of-values\">section on List of Values</a> for details."
+                    },
+                    {
+                        "name": "adapter",
+                        "type": "Function",
+                        "default_value": "`lambda x: str(x)`",
+                        "doc": "The function that transforms an element of <i>lov</i> into a <i>tuple(id:str, label:str|Icon)</i>."
+                    },
+                    {
+                        "name": "type",
+                        "type": "str",
+                        "default_value": "<i>Type name of the lov elements</i>",
+                        "doc": "Must be specified if <i>lov</i> contains a non-specific type of data (ex: dict).<br/><i>value</i> must be of that type, <i>lov</i> must be an iterable on this type, and the adapter function will receive an object of this type."
+                    },
+                    {
+                        "name": "value_by_id",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If False, the selection value (in <i>value</i>) is the selected element in <i>lov</i>. If set to True, then <i>value</i> is set to the id of the selected element in <i>lov</i>."
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "columns",
-            "default_property": true,
-            "type": "str",
-            "default_value": "\"1 1\"",
-            "doc": "The list of weights for each column.<br/>For example, `\"1 2\"` creates a 2 column grid:\n<ul>\n<li>1fr</li>\n<li>2fr</li>\n</ul><br/>The creation of multiple same size columns can be simplified by using the multiply sign eg. \"5*1\" is equivalent to \"1 1 1 1 1\"."
-          },
-          {
-            "name": "columns[mobile]",
-            "type": "str",
-            "default_value": "\"1\"",
-            "doc": "The list of weights for each column, when displayed on a mobile device.<br/>The syntax is the same as for <i>columns</i>."
-          },
-          {
-            "name": "gap",
-            "type": "str",
-            "default_value": "\"0.5rem\"",
-            "doc": "The size of the gap between the columns."
-          }
-        ]
-      }
-    ],
-    [
-      "pane",
-      {
-        "inherits": [
-          "partial",
-          "on_change",
-          "active",
-          "shared"
+        [
+            "on_change",
+            {
+                "properties": [
+                    {
+                        "name": "on_change",
+                        "type": "Callback",
+                        "doc": "The name of a function that is triggered when the value is updated.<br/>The parameters of that function are all optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>var_name (str): the variable name.</li>\n<li>value (any): the new value.</li>\n</ul>",
+                        "signature": [
+                            [
+                                "state",
+                                "State"
+                            ],
+                            [
+                                "var_name",
+                                "str"
+                            ],
+                            [
+                                "value",
+                                ""
+                            ]
+                        ]
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "open",
-            "default_property": true,
-            "type": "dynamic(bool)",
-            "default_value": "False",
-            "doc": "If True, this pane is visible on the page.<br/>If False, the pane is hidden."
-          },
-          {
-            "name": "on_close",
-            "type": "Callback",
-            "doc": "The name of a function that is triggered when this pane is closed (if the user clicks outside of it or presses the Esc key).<br/>All parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (optional[str]): the identifier of the button.</li>\n</ul><br/>If this property is not set, no function is called when this pane is closed.",
-            "signature": [["state", "State"], ["id", "str"]]
-          },
-          {
-            "name": "anchor",
-            "type": "str",
-            "default_value": "\"left\"",
-            "doc": "Anchor side of the pane.<br/>Valid values are \"left\", \"right\", \"top\", or \"bottom\"."
-          },
-          {
-            "name": "width",
-            "type": "str",
-            "default_value": "\"30vw\"",
-            "doc": "Width, in CSS units, of this pane.<br/>This is used only if <i>anchor</i> is \"left\" or \"right\"."
-          },
-          {
-            "name": "height",
-            "type": "str",
-            "default_value": "\"30vh\"",
-            "doc": "Height, in CSS units, of this pane.<br/>This is used only if <i>anchor</i> is \"top\" or \"bottom\"."
-          },
-          {
-            "name": "persistent",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "If False, the pane covers the page where it appeared and disappears if the user clicks in the page.<br/>If True, the pane appears next to the page. Note that the parent section of the pane must have the <i>flex</i> display mode set. See below for an example using the <code>persistent</code> property."
-          }
-        ]
-      }
-    ]
-  ],
-  "undocumented": [
-    [
-      "active",
-      {
-        "properties": [
-          {
-            "name": "active",
-            "type": "dynamic(bool)",
-            "default_value": "True",
-            "doc": "Indicates if this component is active.<br/>An inactive component allows no user interaction."
-          }
-        ]
-      }
-    ],
-    [
-      "lovComp",
-      {
-        "inherits": [
-          "on_change"
+        [
+            "partial",
+            {
+                "properties": [
+                    {
+                        "name": "partial",
+                        "type": "Partial",
+                        "doc": "A Partial object that holds the content of the block.<br/>This should not be defined if <i>page</i> is set."
+                    },
+                    {
+                        "name": "page",
+                        "type": "str",
+                        "doc": "The page name to show as the content of the block.<br/>This should not be defined if <i>partial</i> is set."
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "value",
-            "default_property": true,
-            "type": "dynamic(any)",
-            "doc": "Bound to the selection value."
-          },
-          {
-            "name": "lov",
-            "type": "dict[str, any]",
-            "doc": "The list of values. See the <a href=\"../../binding/#list-of-values\">section on List of Values</a> for details."
-          },
-          {
-            "name": "adapter",
-            "type": "Function",
-            "default_value": "`lambda x: str(x)`",
-            "doc": "The function that transforms an element of <i>lov</i> into a <i>tuple(id:str, label:str|Icon)</i>."
-          },
-          {
-            "name": "type",
-            "type": "str",
-            "default_value": "<i>Type of first lov element</i>",
-            "doc": "Must be specified if <i>lov</i> contains a non-specific type of data (ex: dict).<br/><i>value</i> must be of that type, <i>lov</i> must be an iterable on this type, and the adapter function will receive an object of this type."
-          },
-          {
-            "name": "value_by_id",
-            "type": "bool",
-            "default_value": "False",
-            "doc": "If False, the selection value (in <i>value</i>) is the selected element in <i>lov</i>. If set to True, then <i>value</i> is set to the id of the selected element in <i>lov</i>."
-          }
-        ]
-      }
-    ],
-    [
-      "on_change",
-      {
-        "properties": [
-          {
-            "name": "on_change",
-            "type": "Callback",
-            "doc": "The name of a function that is triggered when the value is updated.<br/>The parameters of that function are all optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>var_name (str): the variable name.</li>\n<li>value (any): the new value.</li>\n</ul>",
-            "signature": [["state", "State"], ["var_name", "str"], ["value", ""]]
-          }
-        ]
-      }
-    ],
-    [
-      "partial",
-      {
-        "properties": [
-          {
-            "name": "partial",
-            "type": "Partial",
-            "doc": "A Partial object that holds the content of the block.<br/>This should not be defined if <i>page</i> is set."
-          },
-          {
-            "name": "page",
-            "type": "str",
-            "doc": "The page name to show as the content of the block.<br/>This should not be defined if <i>partial</i> is set."
-          }
-        ]
-      }
-    ],
-    [
-      "propagate",
-      {
-        "inherits": [
-          "active",
-          "shared"
+        [
+            "propagate",
+            {
+                "inherits": [
+                    "active",
+                    "shared"
+                ],
+                "properties": [
+                    {
+                        "name": "propagate",
+                        "type": "bool",
+                        "default_value": "<i>App config</i>",
+                        "doc": "Allows the control's main value to be automatically propagated.<br/>The default value is defined at the application configuration level.<br/>If True, any change to the control's value is immediately reflected in the bound application variable."
+                    }
+                ]
+            }
         ],
-        "properties": [
-          {
-            "name": "propagate",
-            "type": "bool",
-            "default_value": "<i>App config</i>",
-            "doc": "Allows the control's main value to be automatically propagated.<br/>The default value is defined at the application configuration level.<br/>If True, any change to the control's value is immediately reflected in the bound application variable."
-          }
-        ]
-      }
-    ],
-    [
-      "sharedInput",
-      {
-        "properties": [
-          {
-            "name": "change_delay",
-            "type": "int",
-            "default_value": "<i>App config</i>",
-            "doc": "Minimum time between triggering two calls to the <i>on_change</i> callback.<br/>The default value is defined at the application configuration level by the <strong>change_delay</strong> configuration option. if None, the delay is set to 300 ms.<br/>If set to -1, the input change is triggered only when the user presses the Enter key."
-          },
-          {
-            "name": "on_action",
-            "type": "Callback",
-            "doc": "Name of a function that is triggered when a specific key is pressed.<br/>The parameters of that function are all optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (str): the identifier of the input.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>\nThis dictionary has the following keys:\n<ul>\n<li>action: the name of the action that triggered this callback.</li>\n<li>args (list):\n<ul><li>key name</li><li>variable name</li><li>current value</li></ul>\n</li>\n</ul>\n</li>\n</ul>",
-            "signature": [["state", "State"], ["id", "str"], ["payload", "dict"]]
-          },
-          {
-            "name": "action_keys",
-            "type": "str",
-            "default_value": "\"Enter\"",
-            "doc": "Semicolon (';')-separated list of supported key names.<br/>Authorized values are Enter, Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12."
-          }
-        ]
-      }
-    ],
-    [
-      "shared",
-      {
-        "properties": [
-          {
-            "name": "id",
-            "type": "str",
-            "doc": "The identifier that will be assigned to the rendered HTML component."
-          },
-          {
-            "name": "properties",
-            "type": "dict[str, any]",
-            "doc": "Bound to a dictionary that contains additional properties for this element."
-          },
-          {
-            "name": "class_name",
-            "type": "dynamic(str)",
-            "doc": "The list of CSS class names that will be associated with the generated HTML Element.<br/>These class names will be added to the default <code>taipy-&lt;element_type&gt;</code>."
-          },
-          {
-            "name": "hover_text",
-            "type": "dynamic(str)",
-            "doc": "The information that is displayed when the user hovers over this element."
-          }
+        [
+            "sharedInput",
+            {
+                "properties": [
+                    {
+                        "name": "change_delay",
+                        "type": "int",
+                        "default_value": "<i>App config</i>",
+                        "doc": "Minimum time between triggering two calls to the <i>on_change</i> callback.<br/>The default value is defined at the application configuration level by the <strong>change_delay</strong> configuration option. if None, the delay is set to 300 ms.<br/>If set to -1, the input change is triggered only when the user presses the Enter key."
+                    },
+                    {
+                        "name": "on_action",
+                        "type": "Callback",
+                        "doc": "Name of a function that is triggered when a specific key is pressed.<br/>The parameters of that function are all optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (str): the identifier of the input.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>\nThis dictionary has the following keys:\n<ul>\n<li>action: the name of the action that triggered this callback.</li>\n<li>args (list):\n<ul><li>key name</li><li>variable name</li><li>current value</li></ul>\n</li>\n</ul>\n</li>\n</ul>",
+                        "signature": [
+                            [
+                                "state",
+                                "State"
+                            ],
+                            [
+                                "id",
+                                "str"
+                            ],
+                            [
+                                "payload",
+                                "dict"
+                            ]
+                        ]
+                    },
+                    {
+                        "name": "action_keys",
+                        "type": "str",
+                        "default_value": "\"Enter\"",
+                        "doc": "Semicolon (';')-separated list of supported key names.<br/>Authorized values are Enter, Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12."
+                    }
+                ]
+            }
+        ],
+        [
+            "shared",
+            {
+                "properties": [
+                    {
+                        "name": "id",
+                        "type": "str",
+                        "doc": "The identifier that will be assigned to the rendered HTML component."
+                    },
+                    {
+                        "name": "properties",
+                        "type": "dict[str, any]",
+                        "doc": "Bound to a dictionary that contains additional properties for this element."
+                    },
+                    {
+                        "name": "class_name",
+                        "type": "dynamic(str)",
+                        "doc": "The list of CSS class names that will be associated with the generated HTML Element.<br/>These class names will be added to the default <code>taipy-&lt;element_type&gt;</code>."
+                    },
+                    {
+                        "name": "hover_text",
+                        "type": "dynamic(str)",
+                        "doc": "The information that is displayed when the user hovers over this element."
+                    }
+                ]
+            }
         ]
-      }
     ]
-  ]
 }

+ 69 - 0
tests/gui/builder/control/test_chat.py

@@ -0,0 +1,69 @@
+# 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.
+
+import taipy.gui.builder as tgb
+from taipy.gui import Gui, Icon
+
+
+def test_chat_builder_1(gui: Gui, test_client, helpers):
+    gui._bind_var_val(
+        "messages",
+        [
+            ["1", "msg 1", "Fred"],
+            ["2", "msg From Another unknown User", "Fredo"],
+            ["3", "This from the sender User", "taipy"],
+            ["4", "And from another known one", "Fredi"],
+        ],
+    )
+
+    gui._bind_var_val(
+        "chat_properties",
+        {"users": [["Fred", Icon("/images/favicon.png", "Fred.png")], ["Fredi", Icon("/images/fred.png", "Fred.png")]]},
+    )
+
+    with tgb.Page(frame=None) as page:
+        tgb.chat(messages="{messages}", properties="{chat_properties}")  # type: ignore[attr-defined]
+    expected_list = [
+        "<Chat",
+        'defaultUsers="[[&quot;Fred&quot;, &#x7B;&quot;path&quot;: &quot;/images/favicon.png&quot;, &quot;text&quot;: &quot;Fred.png&quot;&#x7D;], [&quot;Fredi&quot;, &#x7B;&quot;path&quot;: &quot;/images/fred.png&quot;, &quot;text&quot;: &quot;Fred.png&quot;&#x7D;]]"',  # noqa: E501
+        "messages={tpec_TpExPr_messages_TPMDL_0}",
+        'updateVarName="tpec_TpExPr_messages_TPMDL_0"',
+    ]
+    helpers.test_control_builder(gui, page, expected_list)
+
+
+def test_chat_builder_2(gui: Gui, test_client, helpers):
+    gui._bind_var_val(
+        "messages",
+        [
+            ["1", "msg 1", "Fred"],
+            ["2", "msg From Another unknown User", "Fredo"],
+            ["3", "This from the sender User", "taipy"],
+            ["4", "And from another known one", "Fredi"],
+        ],
+    )
+
+    gui._bind_var_val(
+        "users", [["Fred", Icon("/images/favicon.png", "Fred.png")], ["Fredi", Icon("/images/fred.png", "Fred.png")]]
+    )
+
+    with tgb.Page(frame=None) as page:
+        tgb.chat(messages="{messages}", users="{users}")  # type: ignore[attr-defined]
+    expected_list = [
+        "<Chat",
+        'defaultUsers="[[&quot;Fred&quot;, &#x7B;&quot;path&quot;: &quot;/images/favicon.png&quot;, &quot;text&quot;: &quot;Fred.png&quot;&#x7D;], [&quot;Fredi&quot;, &#x7B;&quot;path&quot;: &quot;/images/fred.png&quot;, &quot;text&quot;: &quot;Fred.png&quot;&#x7D;]]"',  # noqa: E501
+        "messages={tpec_TpExPr_messages_TPMDL_0}",
+        'updateVarName="tpec_TpExPr_messages_TPMDL_0"',
+        "users={_TpL_tpec_TpExPr_users_TPMDL_0}",
+        'updateVars="users=_TpL_tpec_TpExPr_users_TPMDL_0"'
+    ]
+    helpers.test_control_builder(gui, page, expected_list)
+

+ 175 - 0
tests/gui/control/test_chat.py

@@ -0,0 +1,175 @@
+# 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.
+
+from taipy.gui import Gui, Icon
+
+
+def test_chat_md_1(gui: Gui, test_client, helpers):
+    gui._bind_var_val(
+        "messages",
+        [
+            ["1", "msg 1", "Fred"],
+            ["2", "msg From Another unknown User", "Fredo"],
+            ["3", "This from the sender User", "taipy"],
+            ["4", "And from another known one", "Fredi"],
+        ],
+    )
+    gui._bind_var_val(
+        "chat_properties",
+        {
+            "users": [
+                ["Fred", Icon("/images/favicon.png", "Fred.png")],
+                ["Fredi", Icon("/images/fred.png", "Fred.png")],
+            ],
+            "sender_id": "sender",
+            "on_action": "on_action",
+            "with_input": False,
+            "height": "50vh",
+        },
+    )
+    md_string = "<|{messages}|chat|properties=chat_properties|>"
+    expected_list = [
+        "<Chat",
+        'defaultUsers="[[&quot;Fred&quot;, &#x7B;&quot;path&quot;: &quot;/images/favicon.png&quot;, &quot;text&quot;: &quot;Fred.png&quot;&#x7D;], [&quot;Fredi&quot;, &#x7B;&quot;path&quot;: &quot;/images/fred.png&quot;, &quot;text&quot;: &quot;Fred.png&quot;&#x7D;]]"',  # noqa: E501
+        "messages={_TpD_tpec_TpExPr_messages_TPMDL_0}",
+        'updateVarName="_TpD_tpec_TpExPr_messages_TPMDL_0"',
+        'senderId="sender"',
+        'onAction="on_action"',
+        "defaultWithInput={false}",
+        'height="50vh"',
+    ]
+    helpers.test_control_md(gui, md_string, expected_list)
+
+
+def test_chat_md_2(gui: Gui, test_client, helpers):
+    gui._bind_var_val(
+        "messages",
+        [
+            ["1", "msg 1", "Fred"],
+            ["2", "msg From Another unknown User", "Fredo"],
+            ["3", "This from the sender User", "taipy"],
+            ["4", "And from another known one", "Fredi"],
+        ],
+    )
+    gui._bind_var_val(
+        "users", [["Fred", Icon("/images/favicon.png", "Fred.png")], ["Fredi", Icon("/images/fred.png", "Fred.png")]]
+    )
+    gui._bind_var_val("winp", False)
+    md_string = "<|{messages}|chat|users={users}|with_input={winp}|>"
+    expected_list = [
+        "<Chat",
+        'defaultUsers="[[&quot;Fred&quot;, &#x7B;&quot;path&quot;: &quot;/images/favicon.png&quot;, &quot;text&quot;: &quot;Fred.png&quot;&#x7D;], [&quot;Fredi&quot;, &#x7B;&quot;path&quot;: &quot;/images/fred.png&quot;, &quot;text&quot;: &quot;Fred.png&quot;&#x7D;]]"',  # noqa: E501
+        "messages={_TpD_tpec_TpExPr_messages_TPMDL_0}",
+        'updateVarName="_TpD_tpec_TpExPr_messages_TPMDL_0"',
+        'updateVars="users=_TpL_tp_TpExPr_gui_get_adapted_lov_users_list_TPMDL_0_0"',
+        "users={_TpL_tp_TpExPr_gui_get_adapted_lov_users_list_TPMDL_0_0}",
+        "withInput={_TpB_tpec_TpExPr_winp_TPMDL_0}>",
+        "defaultWithInput={false}",
+    ]
+    helpers.test_control_md(gui, md_string, expected_list)
+
+
+def test_chat_html_1_1(gui: Gui, test_client, helpers):
+    gui._bind_var_val(
+        "messages",
+        [
+            ["1", "msg 1", "Fred"],
+            ["2", "msg From Another unknown User", "Fredo"],
+            ["3", "This from the sender User", "taipy"],
+            ["4", "And from another known one", "Fredi"],
+        ],
+    )
+    gui._bind_var_val(
+        "chat_properties",
+        {"users": [["Fred", Icon("/images/favicon.png", "Fred.png")], ["Fredi", Icon("/images/fred.png", "Fred.png")]]},
+    )
+    html_string = '<taipy:chat messages="{messages}" properties="chat_properties"/>'
+    expected_list = [
+        "<Chat",
+        'defaultUsers="[[&quot;Fred&quot;, &#x7B;&quot;path&quot;: &quot;/images/favicon.png&quot;, &quot;text&quot;: &quot;Fred.png&quot;&#x7D;], [&quot;Fredi&quot;, &#x7B;&quot;path&quot;: &quot;/images/fred.png&quot;, &quot;text&quot;: &quot;Fred.png&quot;&#x7D;]]"',  # noqa: E501
+        "messages={_TpD_tpec_TpExPr_messages_TPMDL_0}",
+        'updateVarName="_TpD_tpec_TpExPr_messages_TPMDL_0"',
+    ]
+    helpers.test_control_html(gui, html_string, expected_list)
+
+
+def test_chat_html_1_2(gui: Gui, test_client, helpers):
+    gui._bind_var_val(
+        "messages",
+        [
+            ["1", "msg 1", "Fred"],
+            ["2", "msg From Another unknown User", "Fredo"],
+            ["3", "This from the sender User", "taipy"],
+            ["4", "And from another known one", "Fredi"],
+        ],
+    )
+    gui._bind_var_val(
+        "chat_properties",
+        {"users": [["Fred", Icon("/images/favicon.png", "Fred.png")], ["Fredi", Icon("/images/fred.png", "Fred.png")]]},
+    )
+    html_string = '<taipy:chat properties="chat_properties">{messages}</taipy:chat>'
+    expected_list = [
+        "<Chat",
+        'defaultUsers="[[&quot;Fred&quot;, &#x7B;&quot;path&quot;: &quot;/images/favicon.png&quot;, &quot;text&quot;: &quot;Fred.png&quot;&#x7D;], [&quot;Fredi&quot;, &#x7B;&quot;path&quot;: &quot;/images/fred.png&quot;, &quot;text&quot;: &quot;Fred.png&quot;&#x7D;]]"',  # noqa: E501
+        "messages={_TpD_tpec_TpExPr_messages_TPMDL_0}",
+        'updateVarName="_TpD_tpec_TpExPr_messages_TPMDL_0"',
+    ]
+    helpers.test_control_html(gui, html_string, expected_list)
+
+
+def test_chat_html_2_1(gui: Gui, test_client, helpers):
+    gui._bind_var_val(
+        "messages",
+        [
+            ["1", "msg 1", "Fred"],
+            ["2", "msg From Another unknown User", "Fredo"],
+            ["3", "This from the sender User", "taipy"],
+            ["4", "And from another known one", "Fredi"],
+        ],
+    )
+    gui._bind_var_val(
+        "users", [["Fred", Icon("/images/favicon.png", "Fred.png")], ["Fredi", Icon("/images/fred.png", "Fred.png")]]
+    )
+    html_string = '<taipy:chat messages="{messages}" users="{users}" />'
+    expected_list = [
+        "<Chat",
+        'defaultUsers="[[&quot;Fred&quot;, &#x7B;&quot;path&quot;: &quot;/images/favicon.png&quot;, &quot;text&quot;: &quot;Fred.png&quot;&#x7D;], [&quot;Fredi&quot;, &#x7B;&quot;path&quot;: &quot;/images/fred.png&quot;, &quot;text&quot;: &quot;Fred.png&quot;&#x7D;]]"',  # noqa: E501
+        "messages={_TpD_tpec_TpExPr_messages_TPMDL_0}",
+        'updateVarName="_TpD_tpec_TpExPr_messages_TPMDL_0"',
+        'updateVars="users=_TpL_tp_TpExPr_gui_get_adapted_lov_users_list_TPMDL_0_0"',
+        "users={_TpL_tp_TpExPr_gui_get_adapted_lov_users_list_TPMDL_0_0}",
+    ]
+    helpers.test_control_html(gui, html_string, expected_list)
+
+
+def test_chat_html_2_2(gui: Gui, test_client, helpers):
+    gui._bind_var_val(
+        "messages",
+        [
+            ["1", "msg 1", "Fred"],
+            ["2", "msg From Another unknown User", "Fredo"],
+            ["3", "This from the sender User", "taipy"],
+            ["4", "And from another known one", "Fredi"],
+        ],
+    )
+    gui._bind_var_val(
+        "users", [["Fred", Icon("/images/favicon.png", "Fred.png")], ["Fredi", Icon("/images/fred.png", "Fred.png")]]
+    )
+    html_string = '<taipy:chat users="{users}">{messages}</taipy:chat>'
+    expected_list = [
+        "<Chat",
+        'defaultUsers="[[&quot;Fred&quot;, &#x7B;&quot;path&quot;: &quot;/images/favicon.png&quot;, &quot;text&quot;: &quot;Fred.png&quot;&#x7D;], [&quot;Fredi&quot;, &#x7B;&quot;path&quot;: &quot;/images/fred.png&quot;, &quot;text&quot;: &quot;Fred.png&quot;&#x7D;]]"',  # noqa: E501
+        "messages={_TpD_tpec_TpExPr_messages_TPMDL_0}",
+        'updateVarName="_TpD_tpec_TpExPr_messages_TPMDL_0"',
+        'updateVars="users=_TpL_tp_TpExPr_gui_get_adapted_lov_users_list_TPMDL_0_0"',
+        "users={_TpL_tp_TpExPr_gui_get_adapted_lov_users_list_TPMDL_0_0}",
+    ]
+    helpers.test_control_html(gui, html_string, expected_list)

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików