mirror of
https://github.com/lingble/twenty.git
synced 2025-10-30 20:27:55 +00:00
fix: Improve Usability of Adding Options via Return Key for Multi-Select Field (#7450)
Fixes #6602 This is the approach that I followed based on these comments https://github.com/twentyhq/twenty/issues/6602#issuecomment-2356870311, https://github.com/twentyhq/twenty/issues/6602#issuecomment-2330737907 - Create forward ref in `<TextInput>` component - Create ref to select text in parent component `<SettingsDataModelFieldSelectFormOptionRow>` and pass it to `<TextInput>` --------- Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { DropResult } from '@hello-pangea/dnd';
|
import { DropResult } from '@hello-pangea/dnd';
|
||||||
import { useState } from 'react';
|
|
||||||
import { Controller, useFormContext } from 'react-hook-form';
|
import { Controller, useFormContext } from 'react-hook-form';
|
||||||
import { IconPlus } from 'twenty-ui';
|
import { IconPlus } from 'twenty-ui';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
@@ -79,7 +78,6 @@ const StyledButton = styled(LightButton)`
|
|||||||
export const SettingsDataModelFieldSelectForm = ({
|
export const SettingsDataModelFieldSelectForm = ({
|
||||||
fieldMetadataItem,
|
fieldMetadataItem,
|
||||||
}: SettingsDataModelFieldSelectFormProps) => {
|
}: SettingsDataModelFieldSelectFormProps) => {
|
||||||
const [focusedOptionId, setFocusedOptionId] = useState('');
|
|
||||||
const { initialDefaultValue, initialOptions } =
|
const { initialDefaultValue, initialOptions } =
|
||||||
useSelectSettingsFormInitialValues({ fieldMetadataItem });
|
useSelectSettingsFormInitialValues({ fieldMetadataItem });
|
||||||
|
|
||||||
@@ -190,10 +188,6 @@ export const SettingsDataModelFieldSelectForm = ({
|
|||||||
const newOptions = getOptionsWithNewOption();
|
const newOptions = getOptionsWithNewOption();
|
||||||
|
|
||||||
setFormValue('options', newOptions);
|
setFormValue('options', newOptions);
|
||||||
|
|
||||||
const lastOptionId = newOptions[newOptions.length - 1].id;
|
|
||||||
|
|
||||||
setFocusedOptionId(lastOptionId);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -227,7 +221,7 @@ export const SettingsDataModelFieldSelectForm = ({
|
|||||||
<SettingsDataModelFieldSelectFormOptionRow
|
<SettingsDataModelFieldSelectFormOptionRow
|
||||||
key={option.id}
|
key={option.id}
|
||||||
option={option}
|
option={option}
|
||||||
focused={focusedOptionId === option.id}
|
isNewRow={index === options.length - 1}
|
||||||
onChange={(nextOption) => {
|
onChange={(nextOption) => {
|
||||||
const nextOptions = toSpliced(
|
const nextOptions = toSpliced(
|
||||||
options,
|
options,
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ type SettingsDataModelFieldSelectFormOptionRowProps = {
|
|||||||
onRemoveAsDefault?: () => void;
|
onRemoveAsDefault?: () => void;
|
||||||
onInputEnter?: () => void;
|
onInputEnter?: () => void;
|
||||||
option: FieldMetadataItemOption;
|
option: FieldMetadataItemOption;
|
||||||
focused?: boolean;
|
isNewRow?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledRow = styled.div`
|
const StyledRow = styled.div`
|
||||||
@@ -67,7 +67,7 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
|
|||||||
onRemoveAsDefault,
|
onRemoveAsDefault,
|
||||||
onInputEnter,
|
onInputEnter,
|
||||||
option,
|
option,
|
||||||
focused,
|
isNewRow,
|
||||||
}: SettingsDataModelFieldSelectFormOptionRowProps) => {
|
}: SettingsDataModelFieldSelectFormOptionRowProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
@@ -129,10 +129,11 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
|
|||||||
value: getOptionValueFromLabel(label),
|
value: getOptionValueFromLabel(label),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
focused={focused}
|
|
||||||
RightIcon={isDefault ? IconCheck : undefined}
|
RightIcon={isDefault ? IconCheck : undefined}
|
||||||
maxLength={OPTION_VALUE_MAXIMUM_LENGTH}
|
maxLength={OPTION_VALUE_MAXIMUM_LENGTH}
|
||||||
onInputEnter={handleInputEnter}
|
onInputEnter={handleInputEnter}
|
||||||
|
autoFocusOnMount={isNewRow}
|
||||||
|
autoSelectOnMount={isNewRow}
|
||||||
/>
|
/>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={dropdownIds.actions}
|
dropdownId={dropdownIds.actions}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { H2Title } from 'twenty-ui';
|
|
||||||
import { Section } from '@/ui/layout/section/components/Section';
|
|
||||||
import { TextInput } from '@/ui/input/components/TextInput';
|
|
||||||
import { TextArea } from '@/ui/input/components/TextArea';
|
|
||||||
import styled from '@emotion/styled';
|
|
||||||
import { ServerlessFunctionNewFormValues } from '@/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState';
|
import { ServerlessFunctionNewFormValues } from '@/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState';
|
||||||
|
import { TextArea } from '@/ui/input/components/TextArea';
|
||||||
|
import { TextInput } from '@/ui/input/components/TextInput';
|
||||||
|
import { Section } from '@/ui/layout/section/components/Section';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { H2Title } from 'twenty-ui';
|
||||||
|
|
||||||
const StyledInputsContainer = styled.div`
|
const StyledInputsContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -25,7 +25,7 @@ export const SettingsServerlessFunctionNewForm = ({
|
|||||||
<TextInput
|
<TextInput
|
||||||
placeholder="Name"
|
placeholder="Name"
|
||||||
fullWidth
|
fullWidth
|
||||||
focused
|
autoFocusOnMount
|
||||||
value={formValues.name}
|
value={formValues.name}
|
||||||
onChange={onChange('name')}
|
onChange={onChange('name')}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ export type TextInputProps = TextInputV2ComponentProps & {
|
|||||||
disableHotkeys?: boolean;
|
disableHotkeys?: boolean;
|
||||||
onInputEnter?: () => void;
|
onInputEnter?: () => void;
|
||||||
dataTestId?: string;
|
dataTestId?: string;
|
||||||
focused?: boolean;
|
autoFocusOnMount?: boolean;
|
||||||
|
autoSelectOnMount?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TextInput = ({
|
export const TextInput = ({
|
||||||
@@ -22,7 +23,8 @@ export const TextInput = ({
|
|||||||
onBlur,
|
onBlur,
|
||||||
onInputEnter,
|
onInputEnter,
|
||||||
disableHotkeys = false,
|
disableHotkeys = false,
|
||||||
focused,
|
autoFocusOnMount,
|
||||||
|
autoSelectOnMount,
|
||||||
dataTestId,
|
dataTestId,
|
||||||
...props
|
...props
|
||||||
}: TextInputProps) => {
|
}: TextInputProps) => {
|
||||||
@@ -31,11 +33,17 @@ export const TextInput = ({
|
|||||||
const [isFocused, setIsFocused] = useState(false);
|
const [isFocused, setIsFocused] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (focused === true) {
|
if (autoFocusOnMount === true) {
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
setIsFocused(true);
|
setIsFocused(true);
|
||||||
}
|
}
|
||||||
}, [focused]);
|
}, [autoFocusOnMount]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (autoSelectOnMount === true) {
|
||||||
|
inputRef.current?.select();
|
||||||
|
}
|
||||||
|
}, [autoSelectOnMount]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
goBackToPreviousHotkeyScope,
|
goBackToPreviousHotkeyScope,
|
||||||
|
|||||||
Reference in New Issue
Block a user