chore: Simpler autofocus of function call (#39946)
## Description Improves the autofocus logic as per https://github.com/appsmithorg/appsmith/pull/39916#discussion_r2014507020 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - When adding a new function field, it now automatically receives focus, making the configuration process smoother and more intuitive. - The transition for adding fields has been streamlined to improve user interaction without distractions from unnecessary scrolling behavior. - New state management for tracking the most recently added function field enhances user experience. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
afad67b434
commit
3f696facef
|
|
@ -1,5 +1,5 @@
|
||||||
import { Button, Text } from "@appsmith/ads";
|
import { Button, Text } from "@appsmith/ads";
|
||||||
import { default as React, useCallback, useRef, useEffect } from "react";
|
import React, { useCallback, useState } from "react";
|
||||||
import type { FieldArrayFieldsProps } from "redux-form";
|
import type { FieldArrayFieldsProps } from "redux-form";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { v4 as uuid } from "uuid";
|
import { v4 as uuid } from "uuid";
|
||||||
|
|
@ -29,17 +29,19 @@ export const FunctionCallingConfigForm = ({
|
||||||
fields,
|
fields,
|
||||||
formName,
|
formName,
|
||||||
}: FunctionCallingConfigFormProps) => {
|
}: FunctionCallingConfigFormProps) => {
|
||||||
const latestFieldRef = useRef<HTMLDivElement>(null);
|
const [newlyAddedId, setNewlyAddedId] = useState<string | null>(null);
|
||||||
const previousFieldsLength = useRef(fields.length);
|
|
||||||
|
|
||||||
const handleAddFunctionButtonClick = useCallback(() => {
|
const handleAddFunctionButtonClick = useCallback(() => {
|
||||||
|
const id = uuid();
|
||||||
|
|
||||||
fields.push({
|
fields.push({
|
||||||
id: uuid(),
|
id,
|
||||||
description: "",
|
description: "",
|
||||||
entityId: "",
|
entityId: "",
|
||||||
isApprovalRequired: false,
|
isApprovalRequired: false,
|
||||||
entityType: "Query",
|
entityType: "Query",
|
||||||
});
|
});
|
||||||
|
setNewlyAddedId(id);
|
||||||
}, [fields]);
|
}, [fields]);
|
||||||
|
|
||||||
const handleRemoveToolButtonClick = useCallback(
|
const handleRemoveToolButtonClick = useCallback(
|
||||||
|
|
@ -49,38 +51,6 @@ export const FunctionCallingConfigForm = ({
|
||||||
[fields],
|
[fields],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(
|
|
||||||
function handleAddFunction() {
|
|
||||||
// Only scroll and focus if a new field was added (length increased)
|
|
||||||
if (
|
|
||||||
fields.length > previousFieldsLength.current &&
|
|
||||||
latestFieldRef.current
|
|
||||||
) {
|
|
||||||
// Scroll the new field into view
|
|
||||||
latestFieldRef.current.scrollIntoView({
|
|
||||||
behavior: "smooth",
|
|
||||||
block: "center",
|
|
||||||
});
|
|
||||||
|
|
||||||
// Focus the menu button in the latest field
|
|
||||||
// The menu button is rendered by the Menu component from @appsmith/ads
|
|
||||||
const menuButton = latestFieldRef.current.querySelector(
|
|
||||||
"button.rc-select-selector",
|
|
||||||
);
|
|
||||||
|
|
||||||
if (menuButton) {
|
|
||||||
// Use setTimeout to ensure the button is fully rendered
|
|
||||||
setTimeout(() => {
|
|
||||||
(menuButton as HTMLButtonElement).focus();
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
previousFieldsLength.current = fields.length;
|
|
||||||
},
|
|
||||||
[fields.length],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header>
|
<Header>
|
||||||
|
|
@ -108,14 +78,21 @@ export const FunctionCallingConfigForm = ({
|
||||||
) : (
|
) : (
|
||||||
<ConfigItems>
|
<ConfigItems>
|
||||||
{fields.map((field, index) => {
|
{fields.map((field, index) => {
|
||||||
const isLastField = index === fields.length - 1;
|
const fieldValue = fields.get(index);
|
||||||
|
const isNewlyAdded = fieldValue.id === newlyAddedId;
|
||||||
|
|
||||||
|
// Reset the newly added ID after rendering to ensure focus only happens once
|
||||||
|
if (isNewlyAdded) {
|
||||||
|
setTimeout(() => setNewlyAddedId(null), 100);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={field} ref={isLastField ? latestFieldRef : undefined}>
|
<div key={field}>
|
||||||
<FunctionCallingConfigToolField
|
<FunctionCallingConfigToolField
|
||||||
fieldPath={field}
|
fieldPath={field}
|
||||||
formName={formName}
|
formName={formName}
|
||||||
index={index}
|
index={index}
|
||||||
|
isLastAdded={isNewlyAdded}
|
||||||
onRemove={handleRemoveToolButtonClick}
|
onRemove={handleRemoveToolButtonClick}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ interface FunctionCallingConfigToolFieldProps {
|
||||||
formName: string;
|
formName: string;
|
||||||
index: number;
|
index: number;
|
||||||
onRemove: (index: number) => void;
|
onRemove: (index: number) => void;
|
||||||
|
isLastAdded?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConfigItemRoot = styled.div`
|
const ConfigItemRoot = styled.div`
|
||||||
|
|
@ -39,6 +40,7 @@ const ConfigItemSelectWrapper = styled.div`
|
||||||
|
|
||||||
export const FunctionCallingConfigToolField = ({
|
export const FunctionCallingConfigToolField = ({
|
||||||
index,
|
index,
|
||||||
|
isLastAdded,
|
||||||
onRemove,
|
onRemove,
|
||||||
...props
|
...props
|
||||||
}: FunctionCallingConfigToolFieldProps) => {
|
}: FunctionCallingConfigToolFieldProps) => {
|
||||||
|
|
@ -100,6 +102,7 @@ export const FunctionCallingConfigToolField = ({
|
||||||
<ConfigItemRow>
|
<ConfigItemRow>
|
||||||
<ConfigItemSelectWrapper>
|
<ConfigItemSelectWrapper>
|
||||||
<FunctionCallingMenuField
|
<FunctionCallingMenuField
|
||||||
|
autoFocus={isLastAdded}
|
||||||
name={`${props.fieldPath}.entityId`}
|
name={`${props.fieldPath}.entityId`}
|
||||||
onValueChange={handleFunctionChange}
|
onValueChange={handleFunctionChange}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import React, { useCallback, useMemo } from "react";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Menu,
|
Menu,
|
||||||
|
|
@ -12,7 +13,6 @@ import {
|
||||||
MenuSubTrigger,
|
MenuSubTrigger,
|
||||||
MenuSubContent,
|
MenuSubContent,
|
||||||
} from "@appsmith/ads";
|
} from "@appsmith/ads";
|
||||||
import React, { useCallback, useMemo } from "react";
|
|
||||||
import { Field, type WrappedFieldProps, type BaseFieldProps } from "redux-form";
|
import { Field, type WrappedFieldProps, type BaseFieldProps } from "redux-form";
|
||||||
import type {
|
import type {
|
||||||
FunctionCallingEntityType,
|
FunctionCallingEntityType,
|
||||||
|
|
@ -35,6 +35,7 @@ interface FunctionCallingMenuFieldProps {
|
||||||
value: string,
|
value: string,
|
||||||
entityType: FunctionCallingEntityType,
|
entityType: FunctionCallingEntityType,
|
||||||
) => void;
|
) => void;
|
||||||
|
autoFocus?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FieldRenderProps
|
interface FieldRenderProps
|
||||||
|
|
@ -82,7 +83,7 @@ const getSelectedValueInfo = (
|
||||||
};
|
};
|
||||||
|
|
||||||
const FunctionCallingMenuFieldRender = (props: FieldRenderProps) => {
|
const FunctionCallingMenuFieldRender = (props: FieldRenderProps) => {
|
||||||
const { children, disabled, input, onValueChange } = props;
|
const { autoFocus, children, disabled, input, onValueChange } = props;
|
||||||
const options = useSelector(selectEntityOptions);
|
const options = useSelector(selectEntityOptions);
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
@ -154,6 +155,7 @@ const FunctionCallingMenuFieldRender = (props: FieldRenderProps) => {
|
||||||
<Menu>
|
<Menu>
|
||||||
<MenuTrigger>
|
<MenuTrigger>
|
||||||
<Button
|
<Button
|
||||||
|
autoFocus={autoFocus}
|
||||||
className="rc-select-selector"
|
className="rc-select-selector"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
endIcon="arrow-down-s-line"
|
endIcon="arrow-down-s-line"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user