ソースを参照

Merge branch 'develop' into docs/get_resource

namnguyen 5 ヶ月 前
コミット
ce452911a6

+ 52 - 0
doc/gui/examples/async_callback.py

@@ -0,0 +1,52 @@
+# Copyright 2021-2024 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+# -----------------------------------------------------------------------------------------
+# To execute this script, make sure that the taipy-gui package is installed in your
+# Python environment and run:
+#     python <script>
+# -----------------------------------------------------------------------------------------
+# Demonstrate how to update the value of a variable across multiple clients.
+# This application creates a thread that sets a variable to the current time.
+# The value is updated for every client when Gui.broadcast_change() is invoked.
+# -----------------------------------------------------------------------------------------
+import asyncio
+
+import taipy.gui.builder as tgb
+from taipy.gui import Gui, State
+
+
+# This callback is invoked inside a separate thread
+# it can access the state but cannot return a value
+async def heavy_function(state: State):
+    state.logs = "Starting...\n"
+    state.logs += "Searching documents\n"
+    await asyncio.sleep(5)
+    state.logs += "Responding to user\n"
+    await asyncio.sleep(5)
+    state.logs += "Fact Checking\n"
+    await asyncio.sleep(5)
+    state.result = "Done!"
+
+logs = ""
+result = "No response yet"
+
+with tgb.Page() as main_page:
+    # the async callback is used as any other callback
+    tgb.button("Respond", on_action=heavy_function)
+    with tgb.part("card"):
+        tgb.text("{logs}", mode="pre")
+
+    tgb.text("# Result", mode="md")
+    tgb.text("{result}")
+
+
+if __name__ == "__main__":
+    Gui(main_page).run(title="Async - Callback")

+ 4 - 4
doc/gui/extension/table_chess_game.py

@@ -13,14 +13,14 @@ from example_library import ExampleLibrary
 from taipy.gui import Gui
 from taipy.gui import Gui
 
 
 data = [
 data = [
-    ["♖", "♘", "♗", "♕", "♔", "♗", "♘", "♖"],
-    ["♙", "♙", "♙", "♙", "♙", "♙", "♙", "♙"],
+    ["♜", "♞", "♝", "♛", "♚", "♝", "♞", "♜"],
+    ["♟", "♟", "♟", "♟", "♟", "♟", "♟", "♟"],
     ["", "", "", "", "", "", "", ""],
     ["", "", "", "", "", "", "", ""],
     ["", "", "", "", "", "", "", ""],
     ["", "", "", "", "", "", "", ""],
     ["", "", "", "", "", "", "", ""],
     ["", "", "", "", "", "", "", ""],
     ["", "", "", "", "", "", "", ""],
     ["", "", "", "", "", "", "", ""],
-    ["♟", "♟", "♟", "♟", "♟", "♟", "♟", "♟"],
-    ["♜", "♞", "♝", "♛", "♚", "♝", "♞", "♜"]
+    ["♙", "♙", "♙", "♙", "♙", "♙", "♙", "♙"],
+    ["♖", "♘", "♗", "♕", "♔", "♗", "♘", "♖"]
 ]
 ]
 
 
 page = """
 page = """

+ 73 - 50
frontend/taipy-gui/src/components/Taipy/Input.tsx

@@ -31,9 +31,9 @@ const getActionKeys = (keys?: string): string[] => {
     const ak = (
     const ak = (
         keys
         keys
             ? keys
             ? keys
-                  .split(";")
-                  .map((v) => v.trim().toLowerCase())
-                  .filter((v) => AUTHORIZED_KEYS.some((k) => k.toLowerCase() === v))
+                .split(";")
+                .map((v) => v.trim().toLowerCase())
+                .filter((v) => AUTHORIZED_KEYS.some((k) => k.toLowerCase() === v))
             : []
             : []
     ).map((v) => AUTHORIZED_KEYS.find((k) => k.toLowerCase() == v) as string);
     ).map((v) => AUTHORIZED_KEYS.find((k) => k.toLowerCase() == v) as string);
     return ak.length > 0 ? ak : [AUTHORIZED_KEYS[0]];
     return ak.length > 0 ? ak : [AUTHORIZED_KEYS[0]];
@@ -63,6 +63,7 @@ const Input = (props: TaipyInputProps) => {
         onAction,
         onAction,
         onChange,
         onChange,
         multiline = false,
         multiline = false,
+        actionOnBlur = false,
         linesShown = 5,
         linesShown = 5,
     } = props;
     } = props;
 
 
@@ -85,9 +86,9 @@ const Input = (props: TaipyInputProps) => {
         () =>
         () =>
             props.width
             props.width
                 ? {
                 ? {
-                      ...numberSx,
-                      maxWidth: getCssSize(props.width),
-                  }
+                    ...numberSx,
+                    maxWidth: getCssSize(props.width),
+                }
                 : numberSx,
                 : numberSx,
         [props.width]
         [props.width]
     );
     );
@@ -138,6 +139,27 @@ const Input = (props: TaipyInputProps) => {
         [changeDelay, dispatch, updateVarName, module, onChange, propagate]
         [changeDelay, dispatch, updateVarName, module, onChange, propagate]
     );
     );
 
 
+    const handleBlur = useCallback(
+        (evt: React.FocusEvent<HTMLInputElement>) => {
+            const val = (type === "number")
+                ? Number(evt.currentTarget.querySelector("input")?.value)
+                : (multiline
+                    ? evt.currentTarget.querySelector("textarea")?.value
+                    : evt.currentTarget.querySelector("input")?.value)
+                ;
+            if (delayCall.current > 0) {
+                if (changeDelay > 0) {
+                    clearTimeout(delayCall.current);
+                    delayCall.current = -1;
+                }
+                dispatch(createSendUpdateAction(updateVarName, val, module, onChange, propagate));
+            }
+            onAction && dispatch(createSendActionNameAction(id, module, onAction, "Tab", updateVarName, val));
+            evt.preventDefault();
+        },
+        [dispatch, type, updateVarName, module, onChange, propagate, changeDelay, id, multiline, onAction]
+    );
+
     const handleAction = useCallback(
     const handleAction = useCallback(
         (evt: KeyboardEvent<HTMLDivElement>) => {
         (evt: KeyboardEvent<HTMLDivElement>) => {
             if (evt.shiftKey && type === "number") {
             if (evt.shiftKey && type === "number") {
@@ -265,51 +287,51 @@ const Input = (props: TaipyInputProps) => {
         () =>
         () =>
             type == "number"
             type == "number"
                 ? {
                 ? {
-                      htmlInput: {
-                          step: step ? step : 1,
-                          min: min,
-                          max: max,
-                      },
-                      input: {
-                          endAdornment: (
-                              <div style={verticalDivStyle}>
-                                  <IconButton
-                                      aria-label="Increment value"
-                                      size="small"
-                                      onMouseDown={handleUpStepperMouseDown}
-                                      disabled={!active}
-                                  >
-                                      <ArrowDropUpIcon fontSize="inherit" />
-                                  </IconButton>
-                                  <IconButton
-                                      aria-label="Decrement value"
-                                      size="small"
-                                      onMouseDown={handleDownStepperMouseDown}
-                                      disabled={!active}
-                                  >
-                                      <ArrowDropDownIcon fontSize="inherit" />
-                                  </IconButton>
-                              </div>
-                          ),
-                      },
-                  }
+                    htmlInput: {
+                        step: step ? step : 1,
+                        min: min,
+                        max: max,
+                    },
+                    input: {
+                        endAdornment: (
+                            <div style={verticalDivStyle}>
+                                <IconButton
+                                    aria-label="Increment value"
+                                    size="small"
+                                    onMouseDown={handleUpStepperMouseDown}
+                                    disabled={!active}
+                                >
+                                    <ArrowDropUpIcon fontSize="inherit" />
+                                </IconButton>
+                                <IconButton
+                                    aria-label="Decrement value"
+                                    size="small"
+                                    onMouseDown={handleDownStepperMouseDown}
+                                    disabled={!active}
+                                >
+                                    <ArrowDropDownIcon fontSize="inherit" />
+                                </IconButton>
+                            </div>
+                        ),
+                    },
+                }
                 : type == "password"
                 : type == "password"
-                ? {
-                      htmlInput: { autoComplete: "current-password" },
-                      input: {
-                          endAdornment: (
-                              <IconButton
-                                  aria-label="toggle password visibility"
-                                  onClick={handleClickShowPassword}
-                                  onMouseDown={handleMouseDownPassword}
-                                  edge="end"
-                              >
-                                  {showPassword ? <VisibilityOff /> : <Visibility />}
-                              </IconButton>
-                          ),
-                      },
-                  }
-                : undefined,
+                    ? {
+                        htmlInput: { autoComplete: "current-password" },
+                        input: {
+                            endAdornment: (
+                                <IconButton
+                                    aria-label="toggle password visibility"
+                                    onClick={handleClickShowPassword}
+                                    onMouseDown={handleMouseDownPassword}
+                                    edge="end"
+                                >
+                                    {showPassword ? <VisibilityOff /> : <Visibility />}
+                                </IconButton>
+                            ),
+                        },
+                    }
+                    : undefined,
         [
         [
             active,
             active,
             type,
             type,
@@ -344,6 +366,7 @@ const Input = (props: TaipyInputProps) => {
                     slotProps={inputProps}
                     slotProps={inputProps}
                     label={props.label}
                     label={props.label}
                     onChange={handleInput}
                     onChange={handleInput}
+                    onBlur={actionOnBlur ? handleBlur : undefined}
                     disabled={!active}
                     disabled={!active}
                     onKeyDown={handleAction}
                     onKeyDown={handleAction}
                     multiline={multiline}
                     multiline={multiline}

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

@@ -62,6 +62,7 @@ export interface TaipyInputProps extends TaipyActiveProps, TaipyChangeProps, Tai
     changeDelay?: number;
     changeDelay?: number;
     onAction?: string;
     onAction?: string;
     actionKeys?: string;
     actionKeys?: string;
+    actionOnBlur?: boolean;
     multiline?: boolean;
     multiline?: boolean;
     linesShown?: number;
     linesShown?: number;
     width?: string | number;
     width?: string | number;

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

@@ -326,6 +326,7 @@ class _Factory:
                 ("action_keys",),
                 ("action_keys",),
                 ("label",),
                 ("label",),
                 ("change_delay", PropertyType.number, gui._get_config("change_delay", None)),
                 ("change_delay", PropertyType.number, gui._get_config("change_delay", None)),
+                ("action_on_blur", PropertyType.boolean, False),
                 ("multiline", PropertyType.boolean, False),
                 ("multiline", PropertyType.boolean, False),
                 ("lines_shown", PropertyType.number, 5),
                 ("lines_shown", PropertyType.number, 5),
                 ("width", PropertyType.string_or_number),
                 ("width", PropertyType.string_or_number),
@@ -434,6 +435,7 @@ class _Factory:
                 ("on_action", PropertyType.function),
                 ("on_action", PropertyType.function),
                 ("label",),
                 ("label",),
                 ("change_delay", PropertyType.number, gui._get_config("change_delay", None)),
                 ("change_delay", PropertyType.number, gui._get_config("change_delay", None)),
+                ("action_on_blur", PropertyType.boolean, False),
                 ("width", PropertyType.string_or_number),
                 ("width", PropertyType.string_or_number),
             ]
             ]
         ),
         ),

+ 16 - 8
taipy/gui/gui.py

@@ -25,7 +25,7 @@ import uuid
 import warnings
 import warnings
 from importlib import metadata, util
 from importlib import metadata, util
 from importlib.util import find_spec
 from importlib.util import find_spec
-from inspect import currentframe, getabsfile, ismethod, ismodule
+from inspect import currentframe, getabsfile, iscoroutinefunction, ismethod, ismodule
 from pathlib import Path
 from pathlib import Path
 from threading import Thread, Timer
 from threading import Thread, Timer
 from types import FrameType, FunctionType, LambdaType, ModuleType, SimpleNamespace
 from types import FrameType, FunctionType, LambdaType, ModuleType, SimpleNamespace
@@ -73,7 +73,7 @@ from .extension.library import Element, ElementLibrary
 from .page import Page
 from .page import Page
 from .partial import Partial
 from .partial import Partial
 from .server import _Server
 from .server import _Server
-from .state import State, _GuiState
+from .state import State, _AsyncState, _GuiState
 from .types import _WsType
 from .types import _WsType
 from .utils import (
 from .utils import (
     _delscopeattr,
     _delscopeattr,
@@ -115,6 +115,7 @@ from .utils._evaluator import _Evaluator
 from .utils._variable_directory import _is_moduled_variable, _VariableDirectory
 from .utils._variable_directory import _is_moduled_variable, _VariableDirectory
 from .utils.chart_config_builder import _build_chart_config
 from .utils.chart_config_builder import _build_chart_config
 from .utils.table_col_builder import _enhance_columns
 from .utils.table_col_builder import _enhance_columns
+from .utils.threads import _invoke_async_callback
 
 
 
 
 class Gui:
 class Gui:
@@ -1143,7 +1144,6 @@ class Gui:
             for var, val in state_context.items():
             for var, val in state_context.items():
                 self._update_var(var, val, True, forward=False)
                 self._update_var(var, val, True, forward=False)
 
 
-
     @staticmethod
     @staticmethod
     def set_unsupported_data_converter(converter: t.Optional[t.Callable[[t.Any], t.Any]]) -> None:
     def set_unsupported_data_converter(converter: t.Optional[t.Callable[[t.Any], t.Any]]) -> None:
         """Set a custom converter for unsupported data types.
         """Set a custom converter for unsupported data types.
@@ -1155,9 +1155,9 @@ class Gui:
 
 
         Arguments:
         Arguments:
             converter: A function that converts a value with an unsupported data type (the only
             converter: A function that converts a value with an unsupported data type (the only
-              parameter to the function) into data with a supported data type (the returned value
-              from the function).</br>
-              If set to `None`, it removes any existing converter.
+                parameter to the function) into data with a supported data type (the returned value
+                from the function).</br>
+                If set to `None`, it removes any existing converter.
         """
         """
         Gui.__unsupported_data_converter = converter
         Gui.__unsupported_data_converter = converter
 
 
@@ -1588,7 +1588,12 @@ class Gui:
 
 
     def _call_function_with_state(self, user_function: t.Callable, args: t.Optional[t.List[t.Any]] = None) -> t.Any:
     def _call_function_with_state(self, user_function: t.Callable, args: t.Optional[t.List[t.Any]] = None) -> t.Any:
         cp_args = [] if args is None else args.copy()
         cp_args = [] if args is None else args.copy()
-        cp_args.insert(0, self.__get_state())
+        cp_args.insert(
+            0,
+            _AsyncState(t.cast(_GuiState, self.__get_state()))
+            if iscoroutinefunction(user_function)
+            else self.__get_state(),
+        )
         argcount = user_function.__code__.co_argcount
         argcount = user_function.__code__.co_argcount
         if argcount > 0 and ismethod(user_function):
         if argcount > 0 and ismethod(user_function):
             argcount -= 1
             argcount -= 1
@@ -1597,7 +1602,10 @@ class Gui:
         else:
         else:
             cp_args = cp_args[:argcount]
             cp_args = cp_args[:argcount]
         with self.__event_manager:
         with self.__event_manager:
-            return user_function(*cp_args)
+            if iscoroutinefunction(user_function):
+                return _invoke_async_callback(user_function, cp_args)
+            else:
+                return user_function(*cp_args)
 
 
     def _set_module_context(self, module_context: t.Optional[str]) -> t.ContextManager[None]:
     def _set_module_context(self, module_context: t.Optional[str]) -> t.ContextManager[None]:
         return self._set_locals_context(module_context) if module_context is not None else contextlib.nullcontext()
         return self._set_locals_context(module_context) if module_context is not None else contextlib.nullcontext()

+ 9 - 7
taipy/gui/gui_actions.py

@@ -15,6 +15,7 @@ import typing as t
 from ._warnings import _warn
 from ._warnings import _warn
 from .gui import Gui
 from .gui import Gui
 from .state import State
 from .state import State
+from .utils.callable import _is_function
 
 
 
 
 def download(
 def download(
@@ -382,19 +383,20 @@ def invoke_long_callback(
     """
     """
     if not state or not isinstance(state._gui, Gui):
     if not state or not isinstance(state._gui, Gui):
         _warn("'invoke_long_callback()' must be called in the context of a callback.")
         _warn("'invoke_long_callback()' must be called in the context of a callback.")
+        return
 
 
     if user_status_function_args is None:
     if user_status_function_args is None:
         user_status_function_args = []
         user_status_function_args = []
     if user_function_args is None:
     if user_function_args is None:
         user_function_args = []
         user_function_args = []
 
 
-    state_id = get_state_id(state)
-    module_context = get_module_context(state)
+    this_gui = state.get_gui()
+
+    state_id = this_gui._get_client_id()
+    module_context = this_gui._get_locals_context()
     if not isinstance(state_id, str) or not isinstance(module_context, str):
     if not isinstance(state_id, str) or not isinstance(module_context, str):
         return
         return
 
 
-    this_gui = state._gui
-
     def callback_on_exception(state: State, function_name: str, e: Exception):
     def callback_on_exception(state: State, function_name: str, e: Exception):
         if not this_gui._call_on_exception(function_name, e):
         if not this_gui._call_on_exception(function_name, e):
             _warn(f"invoke_long_callback(): Exception raised in function {function_name}()", e)
             _warn(f"invoke_long_callback(): Exception raised in function {function_name}()", e)
@@ -405,10 +407,10 @@ def invoke_long_callback(
         function_name: t.Optional[str] = None,
         function_name: t.Optional[str] = None,
         function_result: t.Optional[t.Any] = None,
         function_result: t.Optional[t.Any] = None,
     ):
     ):
-        if callable(user_status_function):
+        if _is_function(user_status_function):
             this_gui.invoke_callback(
             this_gui.invoke_callback(
                 str(state_id),
                 str(state_id),
-                user_status_function,
+                t.cast(t.Callable, user_status_function),
                 [status] + list(user_status_function_args) + [function_result],  # type: ignore
                 [status] + list(user_status_function_args) + [function_result],  # type: ignore
                 str(module_context),
                 str(module_context),
             )
             )
@@ -438,5 +440,5 @@ def invoke_long_callback(
 
 
     thread = threading.Thread(target=user_function_in_thread, args=user_function_args)
     thread = threading.Thread(target=user_function_in_thread, args=user_function_args)
     thread.start()
     thread.start()
-    if isinstance(period, int) and period >= 500 and callable(user_status_function):
+    if isinstance(period, int) and period >= 500 and _is_function(user_status_function):
         thread_status(thread.name, period / 1000.0, 0)
         thread_status(thread.name, period / 1000.0, 0)

+ 25 - 4
taipy/gui/state.py

@@ -171,10 +171,7 @@ class _GuiState(State):
         "_get_placeholder_attrs",
         "_get_placeholder_attrs",
         "_add_attribute",
         "_add_attribute",
     )
     )
-    __placeholder_attrs = (
-        "_taipy_p1",
-        "_current_context",
-    )
+    __placeholder_attrs = ("_taipy_p1", "_current_context", "__state_id")
     __excluded_attrs = __attrs + __methods + __placeholder_attrs
     __excluded_attrs = __attrs + __methods + __placeholder_attrs
 
 
     def __init__(self, gui: "Gui", var_list: t.Iterable[str], context_list: t.Iterable[str]) -> None:
     def __init__(self, gui: "Gui", var_list: t.Iterable[str], context_list: t.Iterable[str]) -> None:
@@ -278,3 +275,27 @@ class _GuiState(State):
             gui = super().__getattribute__(_GuiState.__gui_attr)
             gui = super().__getattribute__(_GuiState.__gui_attr)
             return gui._bind_var_val(name, default_value)
             return gui._bind_var_val(name, default_value)
         return False
         return False
+
+
+class _AsyncState(_GuiState):
+    def __init__(self, state: State) -> None:
+        super().__init__(state.get_gui(), [], [])
+        self._set_placeholder("__state_id", state.get_gui()._get_client_id())
+
+    @staticmethod
+    def __set_var_in_state(state: State, var_name: str, value: t.Any):
+        setattr(state, var_name, value)
+
+    @staticmethod
+    def __get_var_from_state(state: State, var_name: str):
+        return getattr(state, var_name)
+
+    def __setattr__(self, var_name: str, var_value: t.Any) -> None:
+        self.get_gui().invoke_callback(
+            t.cast(str, self._get_placeholder("__state_id")), _AsyncState.__set_var_in_state, [var_name, var_value]
+        )
+
+    def __getattr__(self, var_name: str) -> t.Any:
+        return self.get_gui().invoke_callback(
+            t.cast(str, self._get_placeholder("__state_id")), _AsyncState.__get_var_from_state, [var_name]
+        )

+ 22 - 0
taipy/gui/utils/threads.py

@@ -0,0 +1,22 @@
+# 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 asyncio
+import threading
+import typing as t
+
+
+def _thread_async_target(user_function, args: t.List[t.Any]):
+    asyncio.run(user_function(*args))
+
+
+def _invoke_async_callback(user_function, args: t.List[t.Any]):
+    thread = threading.Thread(target=_thread_async_target, args=[user_function, args])
+    thread.start()

+ 68 - 56
taipy/gui/viselements.json

@@ -23,7 +23,7 @@
                     {
                     {
                         "name": "mode",
                         "name": "mode",
                         "type": "str",
                         "type": "str",
-                        "doc": "Define the way the text is processed:\n<ul><li>&quot;raw&quot;: synonym for setting the <i>raw</i> 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</li><li>&quot;latex&quot;: LaTe&chi; support</li>"
+                        "doc": "Define the way the text is processed:\n<ul><li>&quot;raw&quot;: synonym for setting the <i>raw</i> 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</li><li>&quot;latex&quot;: LaTe&chi; support</li></ul>"
                     },
                     },
                     {
                     {
                         "name": "format",
                         "name": "format",
@@ -34,7 +34,7 @@
                         "name": "width",
                         "name": "width",
                         "type": "Union[str,int]",
                         "type": "Union[str,int]",
                         "default_value": "None",
                         "default_value": "None",
-                        "doc": "The width of the element."
+                        "doc": "The width of the text element, in CSS units."
                     }
                     }
                 ]
                 ]
             }
             }
@@ -114,31 +114,37 @@
                         "name": "password",
                         "name": "password",
                         "type": "bool",
                         "type": "bool",
                         "default_value": "False",
                         "default_value": "False",
-                        "doc": "If True, the text is obscured: all input characters are displayed as an asterisk ('*')."
+                        "doc": "If True, the text is obscured, and all characters are displayed as asterisks ('*').<br/>This can be useful for sensitive information such as passwords."
                     },
                     },
                     {
                     {
                         "name": "label",
                         "name": "label",
                         "type": "str",
                         "type": "str",
                         "default_value": "None",
                         "default_value": "None",
-                        "doc": "The label associated with the input."
+                        "doc": "The label associated with the input field.<br/>This provides context to the user and improves accessibility."
                     },
                     },
                     {
                     {
                         "name": "multiline",
                         "name": "multiline",
                         "type": "bool",
                         "type": "bool",
                         "default_value": "False",
                         "default_value": "False",
-                        "doc": "If True, the text is presented as a multi line input."
+                        "doc": "If True, the input is rendered as a multi-line text area<br/>The default behavior is a single-line input."
                     },
                     },
                     {
                     {
                         "name": "lines_shown",
                         "name": "lines_shown",
                         "type": "int",
                         "type": "int",
                         "default_value": "5",
                         "default_value": "5",
-                        "doc": "The number of lines shown in the input control, when multiline is True."
+                        "doc": "The number of lines displayed in the input control when multiline is True."
                     },
                     },
                     {
                     {
                         "name": "type",
                         "name": "type",
                         "type": "str",
                         "type": "str",
                         "default_value": "\"text\"",
                         "default_value": "\"text\"",
-                        "doc": "The type of generated input HTML element, as defined in [HTML input types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types).<br/>This value forces certain values to be entered and can be set to \"text\", \"tel\", \"email\", \"url\"..., among other choices."
+                        "doc": "The type of input element, as per <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types\">HTML input types</a>.<br/>This property enforces specific input formats where applicable. Supported values include \"text\", \"tel\", \"email\", \"url\", etc."
+                    },
+                    {
+                        "name": "action_on_blur",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If True, the <code>on_action</code> callback is triggered when the input control looses keyboard focus (e.g., when the user presses the Tab key). When this happens, the key name for the event (set in the <i>args</i> property of the <i>payload</i> parameter to the callback function) is set to \"Tab\"."
                     }
                     }
                 ]
                 ]
             }
             }
@@ -162,29 +168,35 @@
                         "name": "label",
                         "name": "label",
                         "type": "str",
                         "type": "str",
                         "default_value": "None",
                         "default_value": "None",
-                        "doc": "The label associated with the input."
+                        "doc": "The label associated with the number field.<br/>This provides context to the user and improves accessibility."
                     },
                     },
                     {
                     {
                         "name": "step",
                         "name": "step",
                         "type": "dynamic(Union[int,float])",
                         "type": "dynamic(Union[int,float])",
                         "default_value": "1",
                         "default_value": "1",
-                        "doc": "The amount by which the value is incremented or decremented when the user clicks one of the arrow buttons."
+                        "doc": "The increment or decrement applied to the value when the user clicks the arrow buttons."
                     },
                     },
                     {
                     {
                         "name": "step_multiplier",
                         "name": "step_multiplier",
                         "type": "dynamic(Union[int,float])",
                         "type": "dynamic(Union[int,float])",
                         "default_value": "10",
                         "default_value": "10",
-                        "doc": "A factor that multiplies <i>step</i> when the user presses the Shift key while clicking one of the arrow buttons."
+                        "doc": "The factor by which the step value is multiplied when the user holds the Shift key while clicking the arrow buttons."
                     },
                     },
                     {
                     {
                         "name": "min",
                         "name": "min",
                         "type": "dynamic(Union[int,float])",
                         "type": "dynamic(Union[int,float])",
-                        "doc": "The minimum value to accept for this input."
+                        "doc": "The minimum acceptable value.<br/>Values below this threshold are invalid."
                     },
                     },
                     {
                     {
                         "name": "max",
                         "name": "max",
                         "type": "dynamic(Union[int,float])",
                         "type": "dynamic(Union[int,float])",
-                        "doc": "The maximum value to accept for this input."
+                        "doc": "The maximum acceptable value.<br/>Values above this threshold are invalid."
+                    },
+                    {
+                        "name": "action_on_blur",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "If True, the <code>on_action</code> callback is triggered when the number control looses keyboard focus (e.g., when the user presses the Tab key). When this happens, the key name for the event (set in the <i>args</i> property of the <i>payload</i> parameter to the callback function) is set to \"Tab\"."
                     }
                     }
                 ]
                 ]
             }
             }
@@ -236,30 +248,30 @@
                         "name": "continuous",
                         "name": "continuous",
                         "type": "bool",
                         "type": "bool",
                         "default_value": "True",
                         "default_value": "True",
-                        "doc": "If set to False, the control emits an <tt>on_change</tt> 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."
+                        "doc": "If set to False, the control emits an <code>on_change</code> 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",
                         "name": "change_delay",
                         "type": "int",
                         "type": "int",
                         "default_value": "<i>App config</i>",
                         "default_value": "<i>App config</i>",
-                        "doc": "Minimum time between triggering two <tt>on_change</tt> callbacks.<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."
+                        "doc": "Minimum time between triggering two <code>on_change</code> callbacks.<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",
                         "name": "width",
                         "type": "str",
                         "type": "str",
                         "default_value": "\"300px\"",
                         "default_value": "\"300px\"",
-                        "doc": "The width of this slider, in CSS units."
+                        "doc": "The width of the slider, in CSS units."
                     },
                     },
                     {
                     {
                         "name": "height",
                         "name": "height",
                         "type": "str",
                         "type": "str",
-                        "doc": "The height of this slider, in CSS units.<br/>It defaults to the value of <i>width</i> when using the vertical orientation."
+                        "doc": "The height of the slider, in CSS units.<br/>It defaults to the value of <i>width</i> when using the vertical orientation."
                     },
                     },
                     {
                     {
                         "name": "orientation",
                         "name": "orientation",
                         "type": "str",
                         "type": "str",
                         "default_value": "\"horizontal\"",
                         "default_value": "\"horizontal\"",
-                        "doc": "The orientation of this slider.<br/>Valid values are \"horizontal\" or \"vertical\"."
+                        "doc": "The orientation of the slider.<br/>Valid values are \"horizontal\" or \"vertical\"."
                     }
                     }
                 ]
                 ]
             }
             }
@@ -588,7 +600,7 @@
                     {
                     {
                         "name": "title",
                         "name": "title",
                         "type": "str",
                         "type": "str",
-                        "doc": "The title of this chart control."
+                        "doc": "The title of the chart control."
                     },
                     },
                     {
                     {
                         "name": "render",
                         "name": "render",
@@ -659,7 +671,7 @@
                     {
                     {
                         "name": "selected_marker",
                         "name": "selected_marker",
                         "type": "indexed(dict[str, Any])",
                         "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 more details."
+                        "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 more details.</a>"
                     },
                     },
                     {
                     {
                         "name": "layout",
                         "name": "layout",
@@ -700,12 +712,12 @@
                         "name": "width",
                         "name": "width",
                         "type": "Union[str,int,float]",
                         "type": "Union[str,int,float]",
                         "default_value": "\"100%\"",
                         "default_value": "\"100%\"",
-                        "doc": "The width of this chart, in CSS units."
+                        "doc": "The width of the chart, in CSS units."
                     },
                     },
                     {
                     {
                         "name": "height",
                         "name": "height",
                         "type": "Union[str,int,float]",
                         "type": "Union[str,int,float]",
-                        "doc": "The height of this chart, in CSS units."
+                        "doc": "The height of the chart, in CSS units."
                     },
                     },
                     {
                     {
                         "name": "template",
                         "name": "template",
@@ -895,13 +907,13 @@
                         "name": "width",
                         "name": "width",
                         "type": "str",
                         "type": "str",
                         "default_value": "\"100%\"",
                         "default_value": "\"100%\"",
-                        "doc": "The width of this table control, in CSS units."
+                        "doc": "The width of the table control, in CSS units."
                     },
                     },
                     {
                     {
                         "name": "height",
                         "name": "height",
                         "type": "str",
                         "type": "str",
                         "default_value": "\"80vh\"",
                         "default_value": "\"80vh\"",
-                        "doc": "The height of this table control, in CSS units."
+                        "doc": "The height of the table control, in CSS units."
                     },
                     },
                     {
                     {
                         "name": "filter",
                         "name": "filter",
@@ -943,7 +955,7 @@
                         "name": "on_edit",
                         "name": "on_edit",
                         "type": "Union[bool, Callable]",
                         "type": "Union[bool, Callable]",
                         "default_value": "<i>default implementation</i>",
                         "default_value": "<i>default implementation</i>",
-                        "doc": "A function or the name of a function triggered when an edited cell is validated.<br/>This function is invoked with the following parameters:<ul><li><i>state</i> (<code>State^</code>): the state instance.</li><li><i>var_name</i> (str): the name of the tabular data variable.</li><li><i>payload</i> (dict): a dictionary containing details about the callback invocation, with the following keys:<ul><li><i>index</i> (int): the row index.</li><li><i>col</i> (str): the column name.</li><li><i>value</i> (Any): the new cell value, cast to the column's data type.</li><li><i>user_value</i> (str): the new cell value, as entered by the user.</li><li><i>tz</i> (str): the timezone, if the column type is <tt>date</tt>.</li></ul></li></ul>If this property is set to False, the table does not provide the cell editing functionality.<br/>If this property is not set, the table will use the default implementation for editing cells.",
+                        "doc": "A function or the name of a function triggered when an edited cell is validated.<br/>This function is invoked with the following parameters:<ul><li><i>state</i> (<code>State^</code>): the state instance.</li><li><i>var_name</i> (str): the name of the tabular data variable.</li><li><i>payload</i> (dict): a dictionary containing details about the callback invocation, with the following keys:<ul><li><i>index</i> (int): the row index.</li><li><i>col</i> (str): the column name.</li><li><i>value</i> (Any): the new cell value, cast to the column's data type.</li><li><i>user_value</i> (str): the new cell value, as entered by the user.</li><li><i>tz</i> (str): the timezone, if the column type is <code>date</code>.</li></ul></li></ul>If this property is set to False, the table does not provide the cell editing functionality.<br/>If this property is not set, the table will use the default implementation for editing cells.",
                         "signature": [
                         "signature": [
                             [
                             [
                                 "state",
                                 "state",
@@ -1085,7 +1097,7 @@
                     {
                     {
                         "name": "mode",
                         "name": "mode",
                         "type": "str",
                         "type": "str",
-                        "doc": "Define the way the selector is displayed:\n<ul><li>&quot;radio&quot;: as a list of radio buttons</li><li>&quot;check&quot;: as a list of check boxes</li><li>any other value: a plain list."
+                        "doc": "Define the way the selector is displayed:\n<ul><li>&quot;radio&quot;: as a list of radio buttons</li><li>&quot;check&quot;: as a list of check boxes</li><li>any other value: a plain list.</ul>"
                     },
                     },
                     {
                     {
                         "name": "dropdown",
                         "name": "dropdown",
@@ -1109,12 +1121,12 @@
                         "name": "width",
                         "name": "width",
                         "type": "Union[str,int]",
                         "type": "Union[str,int]",
                         "default_value": "\"360px\"",
                         "default_value": "\"360px\"",
-                        "doc": "The width of this selector, in CSS units."
+                        "doc": "The width of the selector, in CSS units."
                     },
                     },
                     {
                     {
                         "name": "height",
                         "name": "height",
                         "type": "Union[str,int]",
                         "type": "Union[str,int]",
-                        "doc": "The height of this selector, in CSS units."
+                        "doc": "The height of the selector, in CSS units."
                     }
                     }
                 ]
                 ]
             }
             }
@@ -1308,12 +1320,12 @@
                         "name": "width",
                         "name": "width",
                         "type": "Union[str,int,float]",
                         "type": "Union[str,int,float]",
                         "default_value": "\"300px\"",
                         "default_value": "\"300px\"",
-                        "doc": "The width of this image control, in CSS units."
+                        "doc": "The width of the image control, in CSS units."
                     },
                     },
                     {
                     {
                         "name": "height",
                         "name": "height",
                         "type": "Union[str,int,float]",
                         "type": "Union[str,int,float]",
-                        "doc": "The height of this image control, in CSS units."
+                        "doc": "The height of the image control, in CSS units."
                     }
                     }
                 ]
                 ]
             }
             }
@@ -1341,13 +1353,13 @@
                         "name": "min",
                         "name": "min",
                         "type": "Union[int,float]",
                         "type": "Union[int,float]",
                         "default_value": "0",
                         "default_value": "0",
-                        "doc": "The minimum value of this metric control's gauge."
+                        "doc": "The minimum value of the metric control's gauge."
                     },
                     },
                     {
                     {
                         "name": "max",
                         "name": "max",
                         "type": "Union[int,float]",
                         "type": "Union[int,float]",
                         "default_value": "100",
                         "default_value": "100",
-                        "doc": "The maximum value of this metric control's gauge."
+                        "doc": "The maximum value of the metric control's gauge."
                     },
                     },
                     {
                     {
                         "name": "delta",
                         "name": "delta",
@@ -1480,7 +1492,7 @@
                         "name": "width",
                         "name": "width",
                         "type": "Union[str,int]",
                         "type": "Union[str,int]",
                         "default_value": "None",
                         "default_value": "None",
-                        "doc": "The width of this progress indicator, in CSS units."
+                        "doc": "The width of the progress indicator, in CSS units."
                     }
                     }
                 ]
                 ]
             }
             }
@@ -1525,7 +1537,7 @@
                         "name": "orientation",
                         "name": "orientation",
                         "type": "str",
                         "type": "str",
                         "default_value": "\"horizontal\"",
                         "default_value": "\"horizontal\"",
-                        "doc": "The orientation of this slider."
+                        "doc": "The orientation of the indicator."
                     },
                     },
                     {
                     {
                         "name": "width",
                         "name": "width",
@@ -1596,7 +1608,7 @@
                     {
                     {
                         "name": "adapter",
                         "name": "adapter",
                         "type": "Union[str, Callable]",
                         "type": "Union[str, Callable]",
-                        "default_value": "<tt>lambda x: str(x)</tt>",
+                        "default_value": "<code>lambda x: str(x)</code>",
                         "doc": "A function or the name of the function that transforms an element of <i>lov</i> into a <i>tuple(id:str, label:Union[str,Icon])</i>.<br/>The default value is a function that returns the string representation of the <i>lov</i> element."
                         "doc": "A function or the name of the function that transforms an element of <i>lov</i> into a <i>tuple(id:str, label:Union[str,Icon])</i>.<br/>The default value is a function that returns the string representation of the <i>lov</i> element."
                     },
                     },
                     {
                     {
@@ -1801,7 +1813,7 @@
                     {
                     {
                         "name": "height",
                         "name": "height",
                         "type": "Union[str,int,float]",
                         "type": "Union[str,int,float]",
-                        "doc": "The maximum height of this chat control, in CSS units."
+                        "doc": "The maximum height of the chat control, in CSS units."
                     },
                     },
                     {
                     {
                         "name": "show_sender",
                         "name": "show_sender",
@@ -1858,7 +1870,7 @@
                     {
                     {
                         "name": "row_height",
                         "name": "row_height",
                         "type": "str",
                         "type": "str",
-                        "doc": "The height of each row of this tree, in CSS units."
+                        "doc": "The height of each row of the tree, in CSS units."
                     },
                     },
                     {
                     {
                         "name": "mode",
                         "name": "mode",
@@ -1901,7 +1913,7 @@
                     {
                     {
                         "name": "height",
                         "name": "height",
                         "type": "dynamic(str)",
                         "type": "dynamic(str)",
-                        "doc": "The height, in CSS units, of this block."
+                        "doc": "The height of the part, in CSS units."
                     },
                     },
                     {
                     {
                         "name": "content",
                         "name": "content",
@@ -1924,7 +1936,7 @@
                         "name": "title",
                         "name": "title",
                         "default_property": true,
                         "default_property": true,
                         "type": "dynamic(str)",
                         "type": "dynamic(str)",
-                        "doc": "Title of this block element."
+                        "doc": "Title of the expandable block."
                     },
                     },
                     {
                     {
                         "name": "expanded",
                         "name": "expanded",
@@ -1974,22 +1986,22 @@
                         "name": "close_label",
                         "name": "close_label",
                         "type": "str",
                         "type": "str",
                         "default_value": "\"Close\"",
                         "default_value": "\"Close\"",
-                        "doc": "The tooltip of the top-right close icon button. In the <tt>on_action</tt> callback, <i>args</i> will be set to -1."
+                        "doc": "The tooltip of the top-right close icon button. In the <code>on_action</code> callback, <i>args</i> will be set to -1."
                     },
                     },
                     {
                     {
                         "name": "labels",
                         "name": "labels",
                         "type": "Union[str,list[str]]",
                         "type": "Union[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 <tt>on_action</tt> callback (that index is -1 for the <i>close</i> icon)."
+                        "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 <code>on_action</code> callback (that index is -1 for the <i>close</i> icon)."
                     },
                     },
                     {
                     {
                         "name": "width",
                         "name": "width",
                         "type": "Union[str,int,float]",
                         "type": "Union[str,int,float]",
-                        "doc": "The width of this dialog, in CSS units."
+                        "doc": "The width of the dialog, in CSS units."
                     },
                     },
                     {
                     {
                         "name": "height",
                         "name": "height",
                         "type": "Union[str,int,float]",
                         "type": "Union[str,int,float]",
-                        "doc": "The height of this dialog, in CSS units."
+                        "doc": "The height of the dialog, in CSS units."
                     }
                     }
                 ]
                 ]
             }
             }
@@ -2071,13 +2083,13 @@
                         "name": "width",
                         "name": "width",
                         "type": "str",
                         "type": "str",
                         "default_value": "\"30vw\"",
                         "default_value": "\"30vw\"",
-                        "doc": "Width, in CSS units, of this pane.<br/>This is used only if <i>anchor</i> is \"left\" or \"right\"."
+                        "doc": "Width of the pane, in CSS units.<br/>This is used only if <i>anchor</i> is \"left\" or \"right\"."
                     },
                     },
                     {
                     {
                         "name": "height",
                         "name": "height",
                         "type": "str",
                         "type": "str",
                         "default_value": "\"30vh\"",
                         "default_value": "\"30vh\"",
-                        "doc": "Height, in CSS units, of this pane.<br/>This is used only if <i>anchor</i> is \"top\" or \"bottom\"."
+                        "doc": "Height of this pane, in CSS units.<br/>This is used only if <i>anchor</i> is \"top\" or \"bottom\"."
                     },
                     },
                     {
                     {
                         "name": "show_button",
                         "name": "show_button",
@@ -2098,7 +2110,7 @@
                         "name": "active",
                         "name": "active",
                         "type": "dynamic(bool)",
                         "type": "dynamic(bool)",
                         "default_value": "True",
                         "default_value": "True",
-                        "doc": "Indicates if this component is active.<br/>An inactive component allows no user interaction."
+                        "doc": "Indicates if this element is active.<br/>If False, the element is disabled, and user interaction is not allowed."
                     }
                     }
                 ]
                 ]
             }
             }
@@ -2124,7 +2136,7 @@
                     {
                     {
                         "name": "adapter",
                         "name": "adapter",
                         "type": "Union[str, Callable]",
                         "type": "Union[str, Callable]",
-                        "default_value": "<tt>lambda x: str(x)</tt>",
+                        "default_value": "<code>lambda x: str(x)</code>",
                         "doc": "A function or the name of the function that transforms an element of <i>lov</i> into a <i>tuple(id:str, label:Union[str,Icon])</i>.<br/>The default value is a function that returns the string representation of the <i>lov</i> element."
                         "doc": "A function or the name of the function that transforms an element of <i>lov</i> into a <i>tuple(id:str, label:Union[str,Icon])</i>.<br/>The default value is a function that returns the string representation of the <i>lov</i> element."
                     },
                     },
                     {
                     {
@@ -2149,7 +2161,7 @@
                     {
                     {
                         "name": "on_change",
                         "name": "on_change",
                         "type": "Union[str, Callable]",
                         "type": "Union[str, Callable]",
-                        "doc": "A function or the name of a function that is triggered when the value is updated.<br/>This function is invoked with the following parameters:<ul>\n<li>state (<code>State^</code>): the state instance.</li><li>var_name (str): the variable name.</li><li>value (Any): the new value.</li></ul>",
+                        "doc": "A function or the name of a function that is triggered when the value changes.<br/>The callback function receives the following parameters:<ul>\n<li>state (<code>State^</code>): the state instance.</li><li>var_name (str): the bound variable name.</li><li>value (Any): the updated value.</li></ul>",
                         "signature": [
                         "signature": [
                             [
                             [
                                 "state",
                                 "state",
@@ -2197,7 +2209,7 @@
                         "name": "propagate",
                         "name": "propagate",
                         "type": "bool",
                         "type": "bool",
                         "default_value": "<i>App config</i>",
                         "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 by the <strong>propagate</strong> configuration option.<br/>If True, any change to the control's value is immediately reflected in the bound application variable."
+                        "doc": "Determines whether the control's value is automatically reflected in the bound application variable.<br/>The default value is defined at the application configuration level by the <strong>propagate</strong> configuration option.<br/>If True, any change to the control's value is immediately reflected in the variable."
                     }
                     }
                 ]
                 ]
             }
             }
@@ -2210,12 +2222,12 @@
                         "name": "change_delay",
                         "name": "change_delay",
                         "type": "int",
                         "type": "int",
                         "default_value": "<i>App config</i>",
                         "default_value": "<i>App config</i>",
-                        "doc": "Minimum interval between two consecutive calls to the <tt>on_change</tt> callback.<br/>The default value is defined at the application configuration level by the <strong>change_delay</strong> configuration option.<br/>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."
+                        "doc": "The minimum interval (in milliseconds) between two consecutive calls to the <code>on_change</code> callback.<br/>The default value is defined at the application configuration level by the <strong>change_delay</strong> configuration option.<br/>if None, the delay is set to 300 ms.<br/>If set to -1, the callback is triggered only when the user presses the Enter key."
                     },
                     },
                     {
                     {
                         "name": "on_action",
                         "name": "on_action",
                         "type": "Union[str, Callable]",
                         "type": "Union[str, Callable]",
-                        "doc": "A function or the name of a function that is triggered when a specific key is pressed.<br/>This function is invoked with the following parameters:<ul>\n<li>state (<code>State^</code>): the state instance.</li><li>id (str): the identifier of the control if it has one.</li><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><li>args (list):\n<ul><li>key name</li><li>variable name</li><li>current value</li></ul></li></ul></li></ul>",
+                        "doc": "A function or the name of a function that is triggered when a specific key is pressed.<br/>The callback function is invoked with the following parameters:<ul>\n<li>state (<code>State^</code>): the state instance.</li><li>id (str): the identifier of the control if it has one.</li><li>payload (dict): the callback details<br/>\nThis dictionary has the following keys:\n<ul>\n<li>action: the name of the action that triggered this callback.</li><li>args (list):\n<ul><li>The key name pressed.</li><li>The variable name.</li><li>The current value of the variable.</li></ul></li></ul></li></ul>",
                         "signature": [
                         "signature": [
                             [
                             [
                                 "state",
                                 "state",
@@ -2235,13 +2247,13 @@
                         "name": "action_keys",
                         "name": "action_keys",
                         "type": "str",
                         "type": "str",
                         "default_value": "\"Enter\"",
                         "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."
+                        "doc": "A semicolon-separated list of keys that can trigger the <code>on_action</code> callback.<br/>Authorized values are Enter, Escape, and function keys F1 to F12."
                     },
                     },
                     {
                     {
                         "name": "width",
                         "name": "width",
                         "type": "Union[str,int]",
                         "type": "Union[str,int]",
                         "default_value": "None",
                         "default_value": "None",
-                        "doc": "The width of the element."
+                        "doc": "The width of the element, in CSS units."
                     }
                     }
                 ]
                 ]
             }
             }
@@ -2253,22 +2265,22 @@
                     {
                     {
                         "name": "id",
                         "name": "id",
                         "type": "str",
                         "type": "str",
-                        "doc": "The identifier that is assigned to the rendered HTML component."
+                        "doc": "The identifier assigned to the rendered HTML component.<br/>This can be used in callbacks or to target the element for styling."
                     },
                     },
                     {
                     {
                         "name": "properties",
                         "name": "properties",
                         "type": "dict[str, Any]",
                         "type": "dict[str, Any]",
-                        "doc": "Bound to a dictionary that contains additional properties for this element."
+                        "doc": "A dictionary of additional properties that can be set to the element."
                     },
                     },
                     {
                     {
                         "name": "class_name",
                         "name": "class_name",
                         "type": "dynamic(str)",
                         "type": "dynamic(str)",
-                        "doc": "The list of CSS class names that are associated with the generated HTML Element.<br/>These class names are added to the default <code>taipy-[element_type]</code> class name."
+                        "doc": "A space-separated list of CSS class names to be applied to the generated HTML element.<br/>These classes are added to the default <code>taipy-[element_type]</code> class."
                     },
                     },
                     {
                     {
                         "name": "hover_text",
                         "name": "hover_text",
                         "type": "dynamic(str)",
                         "type": "dynamic(str)",
-                        "doc": "The information that is displayed when the user hovers over this element."
+                        "doc": "The text that is displayed when the user hovers over the element."
                     }
                     }
                 ]
                 ]
             }
             }

+ 1 - 1
taipy/gui_core/viselements.json

@@ -208,7 +208,7 @@
                     {
                     {
                         "name": "on_submission_change",
                         "name": "on_submission_change",
                         "type": "Union[str, Callable]",
                         "type": "Union[str, Callable]",
-                        "doc": "A function or the name of a function that is triggered when a submission status is changed.<br/><br/>All the parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>submission (Submission): the submission entity containing submission information.</li>\n<li>details (dict): the details on this callback's invocation.<br/>\nThis dictionary has the following keys:\n<ul>\n<li>submission_status (str): the new status of the submission (possible values are: \"SUBMITTED\", \"COMPLETED\", \"CANCELED\", \"FAILED\", \"BLOCKED\", \"WAITING\", or \"RUNNING\").</li>\n<li>job: the Job (if any) that is at the origin of the submission status change.</li>\n<li>submittable_entity (Submittable): the entity (usually a Scenario) that was submitted.</li>\n</ul>",
+                        "doc": "A function or the name of a function that is triggered when a submission status is changed.<br/><br/>All the parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>submission (Submission): the submission entity containing submission information.</li>\n<li>details (dict): the details on this callback's invocation.<br/>\nThis dictionary has the following keys:\n<ul>\n<li>submission_status (str): the new status of the submission (possible values are: \"SUBMITTED\", \"COMPLETED\", \"CANCELED\", \"FAILED\", \"BLOCKED\", \"WAITING\", or \"RUNNING\").</li>\n<li>job: the Job (if any) that is at the origin of the submission status change.</li>\n<li>submittable_entity (Submittable): the entity (usually a Scenario) that was submitted.</li></ul></ul>",
                         "signature": [
                         "signature": [
                             [
                             [
                                 "state",
                                 "state",

+ 83 - 0
tests/gui/e2e/with_action/test_input.py

@@ -0,0 +1,83 @@
+# 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 inspect
+import logging
+from importlib import util
+
+import pytest
+
+if util.find_spec("playwright"):
+    from playwright._impl._page import Page
+
+from taipy.gui import Gui
+
+
+@pytest.mark.teste2e
+def test_input_action(page: "Page", gui: Gui, helpers):
+    page_md = """
+<|{input1_value}|input|on_action=input_action|id=input1|>
+<|{input2_value}|input|on_action=input_action|id=input2|action_on_blur|>
+<|X|button|id=button1|on_action=button_action|>
+<|{input1_action_tracker}|id=input1_tracker|>
+<|{input2_action_tracker}|id=input2_tracker|>
+<|{button_action_tracker}|id=button_tracker|>
+"""
+    input1_value = "init"  # noqa: F841
+    input2_value = "init"  # noqa: F841
+    input1_action_tracker = 0  # noqa: F841
+    input2_action_tracker = 0  # noqa: F841
+    button_action_tracker = 0  # noqa: F841
+
+    def input_action(state, id):
+        if id == "input1":
+            state.input1_action_tracker = state.input1_action_tracker + 1
+        elif id == "input2":
+            state.input2_action_tracker = state.input2_action_tracker + 1
+
+    def button_action(state, id):
+        state.button_action_tracker = state.button_action_tracker + 1
+
+    gui._set_frame(inspect.currentframe())
+    gui.add_page(name="test", page=page_md)
+    helpers.run_e2e(gui)
+    page.goto("./test")
+    page.expect_websocket()
+    page.wait_for_selector("#input1_tracker")
+    assert page.query_selector("#input1").input_value() == "init", "Wrong initial value"
+    page.click("#button1")
+    try:
+        page.wait_for_function("document.querySelector('#button_tracker').innerText !== '0'")
+    except Exception as e:
+        logging.getLogger().debug(f"Function evaluation timeout.\n{e}")
+    assert page.query_selector("#button_tracker").inner_text() == "1"
+    page.click("#input1")
+    page.fill("#input1", "step2")
+    page.click("#button1")
+    try:
+        page.wait_for_function("document.querySelector('#button_tracker').innerText !== '1'")
+    except Exception as e:
+        logging.getLogger().debug(f"Function evaluation timeout.\n{e}")
+    assert page.query_selector("#button_tracker").inner_text() == "2", "Button action should have been invoked"
+    assert (
+        page.query_selector("#input1_tracker").inner_text() == "0"
+    ), "Action should not have been invoked (no action_on_blur)"
+    page.click("#input2")
+    page.fill("#input2", "step2")
+    page.click("#button1")
+    try:
+        page.wait_for_function("document.querySelector('#button_tracker').innerText !== '2'")
+    except Exception as e:
+        logging.getLogger().debug(f"Function evaluation timeout.\n{e}")
+    assert page.query_selector("#button_tracker").inner_text() == "3", "Button action should have been invoked"
+    assert (
+        page.query_selector("#input2_tracker").inner_text() == "1"
+    ), "Action should have been invoked (action_on_blur)"

+ 1 - 0
tests/gui/gui_specific/test_state.py

@@ -54,6 +54,7 @@ def test_state(gui: Gui):
         assert state._get_placeholder_attrs() == (
         assert state._get_placeholder_attrs() == (
             "_taipy_p1",
             "_taipy_p1",
             "_current_context",
             "_current_context",
+            "__state_id"
         )
         )
 
 
         assert get_a(state) == 20
         assert get_a(state) == 20