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 { default as React, useCallback, useRef, useEffect } from "react";
|
||||
import React, { useCallback, useState } from "react";
|
||||
import type { FieldArrayFieldsProps } from "redux-form";
|
||||
import styled from "styled-components";
|
||||
import { v4 as uuid } from "uuid";
|
||||
|
|
@ -29,17 +29,19 @@ export const FunctionCallingConfigForm = ({
|
|||
fields,
|
||||
formName,
|
||||
}: FunctionCallingConfigFormProps) => {
|
||||
const latestFieldRef = useRef<HTMLDivElement>(null);
|
||||
const previousFieldsLength = useRef(fields.length);
|
||||
const [newlyAddedId, setNewlyAddedId] = useState<string | null>(null);
|
||||
|
||||
const handleAddFunctionButtonClick = useCallback(() => {
|
||||
const id = uuid();
|
||||
|
||||
fields.push({
|
||||
id: uuid(),
|
||||
id,
|
||||
description: "",
|
||||
entityId: "",
|
||||
isApprovalRequired: false,
|
||||
entityType: "Query",
|
||||
});
|
||||
setNewlyAddedId(id);
|
||||
}, [fields]);
|
||||
|
||||
const handleRemoveToolButtonClick = useCallback(
|
||||
|
|
@ -49,38 +51,6 @@ export const FunctionCallingConfigForm = ({
|
|||
[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 (
|
||||
<>
|
||||
<Header>
|
||||
|
|
@ -108,14 +78,21 @@ export const FunctionCallingConfigForm = ({
|
|||
) : (
|
||||
<ConfigItems>
|
||||
{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 (
|
||||
<div key={field} ref={isLastField ? latestFieldRef : undefined}>
|
||||
<div key={field}>
|
||||
<FunctionCallingConfigToolField
|
||||
fieldPath={field}
|
||||
formName={formName}
|
||||
index={index}
|
||||
isLastAdded={isNewlyAdded}
|
||||
onRemove={handleRemoveToolButtonClick}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ interface FunctionCallingConfigToolFieldProps {
|
|||
formName: string;
|
||||
index: number;
|
||||
onRemove: (index: number) => void;
|
||||
isLastAdded?: boolean;
|
||||
}
|
||||
|
||||
const ConfigItemRoot = styled.div`
|
||||
|
|
@ -39,6 +40,7 @@ const ConfigItemSelectWrapper = styled.div`
|
|||
|
||||
export const FunctionCallingConfigToolField = ({
|
||||
index,
|
||||
isLastAdded,
|
||||
onRemove,
|
||||
...props
|
||||
}: FunctionCallingConfigToolFieldProps) => {
|
||||
|
|
@ -100,6 +102,7 @@ export const FunctionCallingConfigToolField = ({
|
|||
<ConfigItemRow>
|
||||
<ConfigItemSelectWrapper>
|
||||
<FunctionCallingMenuField
|
||||
autoFocus={isLastAdded}
|
||||
name={`${props.fieldPath}.entityId`}
|
||||
onValueChange={handleFunctionChange}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import React, { useCallback, useMemo } from "react";
|
||||
import {
|
||||
Button,
|
||||
Menu,
|
||||
|
|
@ -12,7 +13,6 @@ import {
|
|||
MenuSubTrigger,
|
||||
MenuSubContent,
|
||||
} from "@appsmith/ads";
|
||||
import React, { useCallback, useMemo } from "react";
|
||||
import { Field, type WrappedFieldProps, type BaseFieldProps } from "redux-form";
|
||||
import type {
|
||||
FunctionCallingEntityType,
|
||||
|
|
@ -35,6 +35,7 @@ interface FunctionCallingMenuFieldProps {
|
|||
value: string,
|
||||
entityType: FunctionCallingEntityType,
|
||||
) => void;
|
||||
autoFocus?: boolean;
|
||||
}
|
||||
|
||||
interface FieldRenderProps
|
||||
|
|
@ -82,7 +83,7 @@ const getSelectedValueInfo = (
|
|||
};
|
||||
|
||||
const FunctionCallingMenuFieldRender = (props: FieldRenderProps) => {
|
||||
const { children, disabled, input, onValueChange } = props;
|
||||
const { autoFocus, children, disabled, input, onValueChange } = props;
|
||||
const options = useSelector(selectEntityOptions);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
|
@ -154,6 +155,7 @@ const FunctionCallingMenuFieldRender = (props: FieldRenderProps) => {
|
|||
<Menu>
|
||||
<MenuTrigger>
|
||||
<Button
|
||||
autoFocus={autoFocus}
|
||||
className="rc-select-selector"
|
||||
disabled={disabled}
|
||||
endIcon="arrow-down-s-line"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user