diff --git a/frontend/taipy-gui/src/components/Taipy/Input.tsx b/frontend/taipy-gui/src/components/Taipy/Input.tsx index 436669193c..af50d44379 100644 --- a/frontend/taipy-gui/src/components/Taipy/Input.tsx +++ b/frontend/taipy-gui/src/components/Taipy/Input.tsx @@ -31,9 +31,9 @@ const getActionKeys = (keys?: string): string[] => { const ak = ( 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); return ak.length > 0 ? ak : [AUTHORIZED_KEYS[0]]; @@ -63,6 +63,7 @@ const Input = (props: TaipyInputProps) => { onAction, onChange, multiline = false, + actionOnBlur = false, linesShown = 5, } = props; @@ -85,9 +86,9 @@ const Input = (props: TaipyInputProps) => { () => props.width ? { - ...numberSx, - maxWidth: getCssSize(props.width), - } + ...numberSx, + maxWidth: getCssSize(props.width), + } : numberSx, [props.width] ); @@ -138,6 +139,27 @@ const Input = (props: TaipyInputProps) => { [changeDelay, dispatch, updateVarName, module, onChange, propagate] ); + const handleBlur = useCallback( + (evt: React.FocusEvent) => { + 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( (evt: KeyboardEvent) => { if (evt.shiftKey && type === "number") { @@ -265,51 +287,51 @@ const Input = (props: TaipyInputProps) => { () => type == "number" ? { - htmlInput: { - step: step ? step : 1, - min: min, - max: max, - }, - input: { - endAdornment: ( -
- - - - - - -
- ), - }, - } + htmlInput: { + step: step ? step : 1, + min: min, + max: max, + }, + input: { + endAdornment: ( +
+ + + + + + +
+ ), + }, + } : type == "password" - ? { - htmlInput: { autoComplete: "current-password" }, - input: { - endAdornment: ( - - {showPassword ? : } - - ), - }, - } - : undefined, + ? { + htmlInput: { autoComplete: "current-password" }, + input: { + endAdornment: ( + + {showPassword ? : } + + ), + }, + } + : undefined, [ active, type, @@ -344,6 +366,7 @@ const Input = (props: TaipyInputProps) => { slotProps={inputProps} label={props.label} onChange={handleInput} + onBlur={actionOnBlur ? handleBlur : undefined} disabled={!active} onKeyDown={handleAction} multiline={multiline} diff --git a/frontend/taipy-gui/src/components/Taipy/utils.ts b/frontend/taipy-gui/src/components/Taipy/utils.ts index 7d5f79e455..a714cbbea7 100644 --- a/frontend/taipy-gui/src/components/Taipy/utils.ts +++ b/frontend/taipy-gui/src/components/Taipy/utils.ts @@ -62,6 +62,7 @@ export interface TaipyInputProps extends TaipyActiveProps, TaipyChangeProps, Tai changeDelay?: number; onAction?: string; actionKeys?: string; + actionOnBlur?: boolean; multiline?: boolean; linesShown?: number; width?: string | number; diff --git a/taipy/gui/_renderers/factory.py b/taipy/gui/_renderers/factory.py index 400b3586fc..4945d5fb09 100644 --- a/taipy/gui/_renderers/factory.py +++ b/taipy/gui/_renderers/factory.py @@ -326,6 +326,7 @@ class _Factory: ("action_keys",), ("label",), ("change_delay", PropertyType.number, gui._get_config("change_delay", None)), + ("action_on_blur", PropertyType.boolean, False), ("multiline", PropertyType.boolean, False), ("lines_shown", PropertyType.number, 5), ("width", PropertyType.string_or_number), @@ -434,6 +435,7 @@ class _Factory: ("on_action", PropertyType.function), ("label",), ("change_delay", PropertyType.number, gui._get_config("change_delay", None)), + ("action_on_blur", PropertyType.boolean, False), ("width", PropertyType.string_or_number), ] ), diff --git a/taipy/gui/gui.py b/taipy/gui/gui.py index e5257a62df..e38ba4a603 100644 --- a/taipy/gui/gui.py +++ b/taipy/gui/gui.py @@ -1155,9 +1155,9 @@ def set_unsupported_data_converter(converter: t.Optional[t.Callable[[t.Any], t.A Arguments: 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).
- 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).
+ If set to `None`, it removes any existing converter. """ Gui.__unsupported_data_converter = converter diff --git a/taipy/gui/viselements.json b/taipy/gui/viselements.json index 709a5451fa..f87c8bb11c 100644 --- a/taipy/gui/viselements.json +++ b/taipy/gui/viselements.json @@ -23,7 +23,7 @@ { "name": "mode", "type": "str", - "doc": "Define the way the text is processed:\n