441/fix/clear cell while opening it by typing and delete value when I hit delete / backspace. (#2021)

- Use initial values when opening table cells and pass them to fields

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
Abhishek Thory
2023-11-03 21:13:54 +05:30
committed by GitHub
parent 60b1024efb
commit c397619100
39 changed files with 339 additions and 70 deletions

View File

@@ -11,16 +11,24 @@ export type CompanyPickerProps = {
companyId: string | null;
onSubmit: (newCompanyId: EntityForSelect | null) => void;
onCancel?: () => void;
initialSearchFilter?: string | null;
};
export const CompanyPicker = ({
companyId,
onSubmit,
onCancel,
initialSearchFilter,
}: CompanyPickerProps) => {
const [relationPickerSearchFilter, setRelationPickerSearchFilter] =
useRecoilScopedState(relationPickerSearchFilterScopedState);
useEffect(() => {
if (initialSearchFilter) {
setRelationPickerSearchFilter(initialSearchFilter);
}
}, [initialSearchFilter, setRelationPickerSearchFilter]);
const companies = useFilteredSearchCompanyQuery({
searchFilter: relationPickerSearchFilter,
selectedIds: companyId ? [companyId] : [],
@@ -32,10 +40,6 @@ export const CompanyPicker = ({
onSubmit(selectedCompany ?? null);
};
useEffect(() => {
setRelationPickerSearchFilter('');
}, [setRelationPickerSearchFilter]);
return (
<SingleEntitySelect
entitiesToSelect={companies.entitiesToSelect}

View File

@@ -1,3 +1,5 @@
import { useEffect } from 'react';
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
@@ -13,6 +15,7 @@ export type PeoplePickerProps = {
onCancel?: () => void;
onCreate?: () => void;
excludePersonIds?: string[];
initialSearchFilter?: string | null;
};
export type PersonForSelect = EntityForSelect & {
@@ -26,10 +29,14 @@ export const PeoplePicker = ({
onCancel,
onCreate,
excludePersonIds,
initialSearchFilter,
}: PeoplePickerProps) => {
const [relationPickerSearchFilter] = useRecoilScopedState(
relationPickerSearchFilterScopedState,
);
const [relationPickerSearchFilter, setRelationPickerSearchFilter] =
useRecoilScopedState(relationPickerSearchFilterScopedState);
useEffect(() => {
setRelationPickerSearchFilter(initialSearchFilter ?? '');
}, [initialSearchFilter, setRelationPickerSearchFilter]);
const queryFilters = [
{

View File

@@ -1,4 +1,3 @@
import { useEffect } from 'react';
import debounce from 'lodash.debounce';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
@@ -31,10 +30,6 @@ export const useEntitySelectSearch = () => {
setRelationPickerPreselectedId('');
};
useEffect(() => {
setRelationPickerSearchFilter('');
}, [setRelationPickerSearchFilter]);
return {
searchFilter: relationPickerSearchFilter,
handleSearchFilterChange,

View File

@@ -0,0 +1,18 @@
import { useContext } from 'react';
import { useRecoilValue } from 'recoil';
import { FieldContext } from '../contexts/FieldContext';
import { entityFieldInitialValueFamilyState } from '../states/entityFieldInitialValueFamilyState';
export const useFieldInitialValue = () => {
const { entityId, fieldDefinition } = useContext(FieldContext);
const fieldInitialValue = useRecoilValue(
entityFieldInitialValueFamilyState({
fieldId: fieldDefinition.fieldId,
entityId,
}),
);
return fieldInitialValue;
};

View File

@@ -11,6 +11,9 @@ import { isFieldURL } from '../types/guards/isFieldURL';
export const useGetButtonIcon = (): IconComponent | undefined => {
const { fieldDefinition } = useContext(FieldContext);
if (!fieldDefinition) return undefined;
if (
isFieldURL(fieldDefinition) ||
isFieldEmail(fieldDefinition) ||

View File

@@ -2,6 +2,7 @@ import { useContext } from 'react';
import { useRecoilState } from 'recoil';
import { FieldContext } from '../../contexts/FieldContext';
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
import { isFieldChip } from '../../types/guards/isFieldChip';
@@ -30,11 +31,25 @@ export const useChipField = () => {
const entityType = fieldDefinition.metadata.relationType;
const fieldInitialValue = useFieldInitialValue();
const initialContentValue = fieldInitialValue?.isEmpty
? ''
: fieldInitialValue?.value ?? contentFieldValue;
const initialAvatarValue = fieldInitialValue?.isEmpty
? ''
: fieldInitialValue?.value
? ''
: avatarFieldValue;
return {
fieldDefinition,
contentFieldValue,
initialContentValue,
setContentFieldValue,
avatarFieldValue,
initialAvatarValue,
setAvatarFieldValue,
entityType,
entityId,

View File

@@ -2,6 +2,7 @@ import { useContext } from 'react';
import { useRecoilState } from 'recoil';
import { FieldContext } from '../../contexts/FieldContext';
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
import { isFieldDoubleTextChip } from '../../types/guards/isFieldDoubleTextChip';
@@ -40,6 +41,24 @@ export const useDoubleTextChipField = () => {
const entityType = fieldDefinition.metadata.entityType;
const fieldInitialValue = useFieldInitialValue();
const initialFirstValue = fieldInitialValue?.isEmpty
? ''
: fieldInitialValue?.value ?? firstValue;
const initialSecondValue = fieldInitialValue?.isEmpty
? ''
: fieldInitialValue?.value
? ''
: secondValue;
const initialAvatarUrl = fieldInitialValue?.isEmpty
? ''
: fieldInitialValue?.value
? ''
: avatarUrl;
return {
fieldDefinition,
avatarUrl,
@@ -52,5 +71,8 @@ export const useDoubleTextChipField = () => {
entityType,
entityId,
hotkeyScope,
initialAvatarUrl,
initialFirstValue,
initialSecondValue,
};
};

View File

@@ -2,6 +2,7 @@ import { useContext } from 'react';
import { useRecoilState } from 'recoil';
import { FieldContext } from '../../contexts/FieldContext';
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
import { isFieldDoubleText } from '../../types/guards/isFieldDoubleText';
@@ -25,6 +26,18 @@ export const useDoubleTextField = () => {
}),
);
const fieldInitialValue = useFieldInitialValue();
const initialFirstValue = fieldInitialValue?.isEmpty
? ''
: fieldInitialValue?.value ?? firstValue;
const initialSecondValue = fieldInitialValue?.isEmpty
? ''
: fieldInitialValue?.value
? ''
: secondValue;
const fullValue = [firstValue, secondValue].filter(Boolean).join(' ');
return {
@@ -32,6 +45,8 @@ export const useDoubleTextField = () => {
secondValue,
setSecondValue,
firstValue,
initialFirstValue,
initialSecondValue,
setFirstValue,
fullValue,
hotkeyScope,

View File

@@ -2,6 +2,7 @@ import { useContext } from 'react';
import { useRecoilState } from 'recoil';
import { FieldContext } from '../../contexts/FieldContext';
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
import { isFieldEmail } from '../../types/guards/isFieldEmail';
@@ -20,9 +21,16 @@ export const useEmailField = () => {
}),
);
const fieldInitialValue = useFieldInitialValue();
const initialValue = fieldInitialValue?.isEmpty
? ''
: fieldInitialValue?.value ?? fieldValue;
return {
fieldDefinition,
fieldValue,
initialValue,
setFieldValue,
hotkeyScope,
};

View File

@@ -2,6 +2,7 @@ import { useContext } from 'react';
import { useRecoilState } from 'recoil';
import { FieldContext } from '../../contexts/FieldContext';
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
import { usePersistField } from '../../hooks/usePersistField';
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
import { FieldMoneyAmountV2Value } from '../../types/FieldMetadata';
@@ -33,9 +34,18 @@ export const useMoneyAmountV2Field = () => {
persistField(newValue);
};
const fieldInitialValue = useFieldInitialValue();
const initialValue: FieldMoneyAmountV2Value = fieldInitialValue?.isEmpty
? { amount: 0, currency: '' }
: !isNaN(Number(fieldInitialValue?.value))
? { amount: Number(fieldInitialValue?.value), currency: '' }
: { amount: 0, currency: '' } ?? fieldValue;
return {
fieldDefinition,
fieldValue,
initialValue,
setFieldValue,
hotkeyScope,
persistMoneyAmountV2Field,

View File

@@ -7,6 +7,7 @@ import {
} from '~/utils/cast-as-integer-or-null';
import { FieldContext } from '../../contexts/FieldContext';
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
import { usePersistField } from '../../hooks/usePersistField';
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
@@ -38,9 +39,18 @@ export const useMoneyField = () => {
persistField(castedValue);
};
const fieldInitialValue = useFieldInitialValue();
const initialValue = fieldInitialValue?.isEmpty
? null
: !isNaN(Number(fieldInitialValue?.value))
? Number(fieldInitialValue?.value)
: null ?? fieldValue;
return {
fieldDefinition,
fieldValue,
initialValue,
setFieldValue,
hotkeyScope,
persistMoneyField,

View File

@@ -7,6 +7,7 @@ import {
} from '~/utils/cast-as-integer-or-null';
import { FieldContext } from '../../contexts/FieldContext';
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
import { usePersistField } from '../../hooks/usePersistField';
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
@@ -38,9 +39,18 @@ export const useNumberField = () => {
persistField(castedValue);
};
const fieldInitialValue = useFieldInitialValue();
const initialValue = fieldInitialValue?.isEmpty
? null
: !isNaN(Number(fieldInitialValue?.value))
? Number(fieldInitialValue?.value)
: null ?? fieldValue;
return {
fieldDefinition,
fieldValue,
initialValue,
setFieldValue,
hotkeyScope,
persistNumberField,

View File

@@ -3,6 +3,7 @@ import { isPossiblePhoneNumber } from 'libphonenumber-js';
import { useRecoilState } from 'recoil';
import { FieldContext } from '../../contexts/FieldContext';
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
import { usePersistField } from '../../hooks/usePersistField';
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
@@ -30,9 +31,16 @@ export const usePhoneField = () => {
persistField(newPhoneValue);
};
const fieldInitialValue = useFieldInitialValue();
const initialValue = fieldInitialValue?.isEmpty
? ''
: fieldInitialValue?.value ?? fieldValue;
return {
fieldDefinition,
fieldValue,
initialValue,
setFieldValue,
hotkeyScope,
persistPhoneField,

View File

@@ -2,6 +2,7 @@ import { useContext } from 'react';
import { useRecoilState } from 'recoil';
import { FieldContext } from '../../contexts/FieldContext';
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
import { isFieldRelation } from '../../types/guards/isFieldRelation';
@@ -21,9 +22,19 @@ export const useRelationField = () => {
}),
);
const fieldInitialValue = useFieldInitialValue();
const initialSearchValue = fieldInitialValue?.isEmpty
? null
: fieldInitialValue?.value;
const initialValue = fieldInitialValue?.isEmpty ? null : fieldValue;
return {
fieldDefinition,
fieldValue,
initialValue,
initialSearchValue,
setFieldValue,
};
};

View File

@@ -2,6 +2,7 @@ import { useContext } from 'react';
import { useRecoilState } from 'recoil';
import { FieldContext } from '../../contexts/FieldContext';
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
import { isFieldText } from '../../types/guards/isFieldText';
@@ -20,9 +21,16 @@ export const useTextField = () => {
}),
);
const fieldInitialValue = useFieldInitialValue();
const initialValue = fieldInitialValue?.isEmpty
? ''
: fieldInitialValue?.value ?? fieldValue;
return {
fieldDefinition,
fieldValue,
initialValue,
setFieldValue,
hotkeyScope,
};

View File

@@ -4,6 +4,7 @@ import { useRecoilState } from 'recoil';
import { isURL } from '~/utils/is-url';
import { FieldContext } from '../../contexts/FieldContext';
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
import { usePersistField } from '../../hooks/usePersistField';
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
@@ -23,6 +24,12 @@ export const useURLField = () => {
}),
);
const fieldInitialValue = useFieldInitialValue();
const initialValue = fieldInitialValue?.isEmpty
? ''
: fieldInitialValue?.value ?? fieldValue;
const persistField = usePersistField();
const persistURLField = (newValue: string) => {
@@ -36,6 +43,7 @@ export const useURLField = () => {
return {
fieldDefinition,
fieldValue,
initialValue,
setFieldValue,
hotkeyScope,
persistURLField,

View File

@@ -2,6 +2,7 @@ import { useContext } from 'react';
import { useRecoilState } from 'recoil';
import { FieldContext } from '../../contexts/FieldContext';
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
import { usePersistField } from '../../hooks/usePersistField';
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
import { FieldURLV2Value } from '../../types/FieldMetadata';
@@ -23,6 +24,12 @@ export const useURLV2Field = () => {
}),
);
const fieldInitialValue = useFieldInitialValue();
const initialValue: FieldURLV2Value = fieldInitialValue?.isEmpty
? { link: '', text: '' }
: { link: fieldInitialValue?.value ?? '', text: '' } ?? fieldValue;
const persistField = usePersistField();
const persistURLField = (newValue: FieldURLV2Value) => {
@@ -36,6 +43,7 @@ export const useURLV2Field = () => {
return {
fieldDefinition,
fieldValue,
initialValue,
setFieldValue,
hotkeyScope,
persistURLField,

View File

@@ -21,7 +21,7 @@ export const ChipFieldInput = ({
onTab,
onShiftTab,
}: ChipFieldInputProps) => {
const { fieldDefinition, contentFieldValue, hotkeyScope } = useChipField();
const { fieldDefinition, initialContentValue, hotkeyScope } = useChipField();
const persistField = usePersistField();
@@ -53,7 +53,7 @@ export const ChipFieldInput = ({
<TextInput
placeholder={fieldDefinition.metadata.placeHolder}
autoFocus
value={contentFieldValue ?? ''}
value={initialContentValue}
onClickOutside={handleClickOutside}
onEnter={handleEnter}
onEscape={handleEscape}

View File

@@ -22,8 +22,12 @@ export const DoubleTextChipFieldInput = ({
onTab,
onShiftTab,
}: DoubleTextChipFieldInputProps) => {
const { fieldDefinition, firstValue, secondValue, hotkeyScope } =
useDoubleTextChipField();
const {
fieldDefinition,
initialFirstValue,
initialSecondValue,
hotkeyScope,
} = useDoubleTextChipField();
const persistField = usePersistField();
@@ -53,8 +57,8 @@ export const DoubleTextChipFieldInput = ({
return (
<FieldInputOverlay>
<DoubleTextInput
firstValue={firstValue ?? ''}
secondValue={secondValue ?? ''}
firstValue={initialFirstValue}
secondValue={initialSecondValue}
firstValuePlaceholder={fieldDefinition.metadata.firstValuePlaceholder}
secondValuePlaceholder={fieldDefinition.metadata.secondValuePlaceholder}
onClickOutside={handleClickOutside}

View File

@@ -22,8 +22,12 @@ export const DoubleTextFieldInput = ({
onTab,
onShiftTab,
}: DoubleTextFieldInputProps) => {
const { fieldDefinition, firstValue, secondValue, hotkeyScope } =
useDoubleTextField();
const {
fieldDefinition,
initialFirstValue,
initialSecondValue,
hotkeyScope,
} = useDoubleTextField();
const persistField = usePersistField();
@@ -53,8 +57,8 @@ export const DoubleTextFieldInput = ({
return (
<FieldInputOverlay>
<DoubleTextInput
firstValue={firstValue ?? ''}
secondValue={secondValue ?? ''}
firstValue={initialFirstValue}
secondValue={initialSecondValue}
firstValuePlaceholder={fieldDefinition.metadata.firstValuePlaceholder}
secondValuePlaceholder={fieldDefinition.metadata.secondValuePlaceholder}
onClickOutside={handleClickOutside}

View File

@@ -21,7 +21,7 @@ export const EmailFieldInput = ({
onTab,
onShiftTab,
}: EmailFieldInputProps) => {
const { fieldDefinition, fieldValue, hotkeyScope } = useEmailField();
const { fieldDefinition, initialValue, hotkeyScope } = useEmailField();
const persistField = usePersistField();
@@ -53,7 +53,7 @@ export const EmailFieldInput = ({
<TextInput
placeholder={fieldDefinition.metadata.placeHolder}
autoFocus
value={fieldValue ?? ''}
value={initialValue}
onClickOutside={handleClickOutside}
onEnter={handleEnter}
onEscape={handleEscape}

View File

@@ -22,7 +22,7 @@ export const MoneyAmountV2FieldInput = ({
onTab,
onShiftTab,
}: MoneyAmountV2FieldInputProps) => {
const { fieldValue, hotkeyScope } = useMoneyAmountV2Field();
const { hotkeyScope, initialValue } = useMoneyAmountV2Field();
const persistField = usePersistField();
@@ -77,8 +77,8 @@ export const MoneyAmountV2FieldInput = ({
return (
<FieldInputOverlay>
<DoubleTextInput
firstValue={fieldValue.amount?.toString() ?? ''}
secondValue={fieldValue.currency ?? ''}
firstValue={initialValue.amount?.toString() ?? ''}
secondValue={initialValue.currency ?? ''}
firstValuePlaceholder="Amount"
secondValuePlaceholder="Currency"
onClickOutside={handleClickOutside}

View File

@@ -21,7 +21,7 @@ export const MoneyFieldInput = ({
onTab,
onShiftTab,
}: MoneyFieldInputProps) => {
const { fieldDefinition, fieldValue, hotkeyScope, persistMoneyField } =
const { fieldDefinition, hotkeyScope, persistMoneyField, initialValue } =
useMoneyField();
const handleEnter = (newText: string) => {
@@ -52,7 +52,7 @@ export const MoneyFieldInput = ({
<TextInput
placeholder={fieldDefinition.metadata.placeHolder}
autoFocus
value={fieldValue?.toLocaleString() ?? ''}
value={initialValue?.toLocaleString() ?? ''}
onClickOutside={handleClickOutside}
onEnter={handleEnter}
onEscape={handleEscape}

View File

@@ -21,7 +21,7 @@ export const NumberFieldInput = ({
onTab,
onShiftTab,
}: NumberFieldInputProps) => {
const { fieldDefinition, fieldValue, hotkeyScope, persistNumberField } =
const { fieldDefinition, initialValue, hotkeyScope, persistNumberField } =
useNumberField();
const handleEnter = (newText: string) => {
@@ -52,7 +52,7 @@ export const NumberFieldInput = ({
<TextInput
placeholder={fieldDefinition.metadata.placeHolder}
autoFocus
value={fieldValue?.toString() ?? ''}
value={initialValue?.toString() ?? ''}
onClickOutside={handleClickOutside}
onEnter={handleEnter}
onEscape={handleEscape}

View File

@@ -20,7 +20,7 @@ export const PhoneFieldInput = ({
onTab,
onShiftTab,
}: PhoneFieldInputProps) => {
const { fieldDefinition, fieldValue, hotkeyScope, persistPhoneField } =
const { fieldDefinition, initialValue, hotkeyScope, persistPhoneField } =
usePhoneField();
const handleEnter = (newText: string) => {
@@ -51,7 +51,7 @@ export const PhoneFieldInput = ({
<PhoneInput
placeholder={fieldDefinition.metadata.placeHolder}
autoFocus
value={fieldValue ?? ''}
value={initialValue}
onClickOutside={handleClickOutside}
onEnter={handleEnter}
onEscape={handleEscape}

View File

@@ -1,3 +1,4 @@
import { useEffect } from 'react';
import styled from '@emotion/styled';
import { CompanyPicker } from '@/companies/components/CompanyPicker';
@@ -26,7 +27,8 @@ export const RelationFieldInput = ({
onSubmit,
onCancel,
}: RelationFieldInputProps) => {
const { fieldDefinition, fieldValue } = useRelationField();
const { fieldDefinition, initialValue, initialSearchValue } =
useRelationField();
const persistField = usePersistField();
@@ -34,26 +36,31 @@ export const RelationFieldInput = ({
onSubmit?.(() => persistField(newEntity?.originalEntity ?? null));
};
useEffect(() => {}, [initialSearchValue]);
return (
<StyledRelationPickerContainer>
{fieldDefinition.metadata.relationType === Entity.Person ? (
<PeoplePicker
personId={fieldValue?.id ?? ''}
companyId={fieldValue?.companyId ?? ''}
personId={initialValue?.id ?? ''}
companyId={initialValue?.companyId ?? ''}
onSubmit={handleSubmit}
onCancel={onCancel}
initialSearchFilter={initialSearchValue}
/>
) : fieldDefinition.metadata.relationType === Entity.User ? (
<UserPicker
userId={fieldValue?.id ?? ''}
userId={initialValue?.id ?? ''}
onSubmit={handleSubmit}
onCancel={onCancel}
initialSearchFilter={initialSearchValue}
/>
) : fieldDefinition.metadata.relationType === Entity.Company ? (
<CompanyPicker
companyId={fieldValue?.id ?? ''}
companyId={initialValue?.id ?? ''}
onSubmit={handleSubmit}
onCancel={onCancel}
initialSearchFilter={initialSearchValue}
/>
) : null}
</StyledRelationPickerContainer>

View File

@@ -21,7 +21,7 @@ export const TextFieldInput = ({
onTab,
onShiftTab,
}: TextFieldInputProps) => {
const { fieldDefinition, fieldValue, hotkeyScope } = useTextField();
const { fieldDefinition, initialValue, hotkeyScope } = useTextField();
const persistField = usePersistField();
@@ -53,7 +53,7 @@ export const TextFieldInput = ({
<TextInput
placeholder={fieldDefinition.metadata.placeHolder}
autoFocus
value={fieldValue ?? ''}
value={initialValue}
onClickOutside={handleClickOutside}
onEnter={handleEnter}
onEscape={handleEscape}

View File

@@ -20,7 +20,7 @@ export const URLFieldInput = ({
onTab,
onShiftTab,
}: URLFieldInputProps) => {
const { fieldDefinition, fieldValue, hotkeyScope, persistURLField } =
const { fieldDefinition, initialValue, hotkeyScope, persistURLField } =
useURLField();
const handleEnter = (newText: string) => {
@@ -51,7 +51,7 @@ export const URLFieldInput = ({
<TextInput
placeholder={fieldDefinition.metadata.placeHolder}
autoFocus
value={fieldValue ?? ''}
value={initialValue}
onClickOutside={handleClickOutside}
onEnter={handleEnter}
onEscape={handleEscape}

View File

@@ -20,7 +20,7 @@ export const URLV2FieldInput = ({
onTab,
onShiftTab,
}: URLV2FieldInputProps) => {
const { fieldValue, hotkeyScope, persistURLField } = useURLV2Field();
const { initialValue, hotkeyScope, persistURLField } = useURLV2Field();
const handleEnter = (newURL: FieldDoubleText) => {
onEnter?.(() =>
@@ -73,8 +73,8 @@ export const URLV2FieldInput = ({
return (
<FieldInputOverlay>
<DoubleTextInput
firstValue={fieldValue.link}
secondValue={fieldValue.text}
firstValue={initialValue.link}
secondValue={initialValue.text}
firstValuePlaceholder={'Link'}
secondValuePlaceholder={'Label'}
hotkeyScope={hotkeyScope}

View File

@@ -0,0 +1,11 @@
import { atomFamily } from 'recoil';
import { FieldInitialValue } from '../types/FieldInitialValue';
export const entityFieldInitialValueFamilyState = atomFamily<
FieldInitialValue | undefined,
{ entityId: string; fieldId: string }
>({
key: 'entityFieldInitialValueFamilyState',
default: undefined,
});

View File

@@ -2,6 +2,7 @@ import { selectorFamily } from 'recoil';
import { FieldDefinition } from '../../types/FieldDefinition';
import { FieldMetadata } from '../../types/FieldMetadata';
import { isFieldBoolean } from '../../types/guards/isFieldBoolean';
import { isFieldChip } from '../../types/guards/isFieldChip';
import { isFieldDate } from '../../types/guards/isFieldDate';
import { isFieldDoubleTextChip } from '../../types/guards/isFieldDoubleTextChip';
@@ -35,6 +36,7 @@ export const isEntityFieldEmptyFamilySelector = selectorFamily({
isFieldNumber(fieldDefinition) ||
isFieldMoney(fieldDefinition) ||
isFieldEmail(fieldDefinition) ||
isFieldBoolean(fieldDefinition) ||
isFieldPhone(fieldDefinition)
) {
const fieldName = fieldDefinition.metadata.fieldName;
@@ -88,6 +90,10 @@ export const isEntityFieldEmptyFamilySelector = selectorFamily({
contentFieldSecondValue === undefined ||
contentFieldSecondValue === '')
);
} else {
throw new Error(
`Entity field type not supported in isEntityFieldEmptyFamilySelector : ${fieldDefinition.type}}`,
);
}
return false;

View File

@@ -0,0 +1,4 @@
export type FieldInitialValue = {
isEmpty?: boolean;
value?: string;
};

View File

@@ -0,0 +1,12 @@
import { atomFamily } from 'recoil';
import { TableCellInitialValue } from '../types/TableCellInitialValue';
import { TableCellPosition } from '../types/TableCellPosition';
export const tableCellInitialValueFamilyState = atomFamily<
TableCellInitialValue | undefined,
TableCellPosition
>({
key: 'tableCellInitialValueFamilyState',
default: undefined,
});

View File

@@ -1,13 +1,8 @@
import { useContext } from 'react';
import { IconArrowUpRight } from '@/ui/display/icon';
import { FieldDisplay } from '@/ui/object/field/components/FieldDisplay';
import { FieldInput } from '@/ui/object/field/components/FieldInput';
import { useGetButtonIcon } from '@/ui/object/field/hooks/useGetButtonIcon';
import { FieldInputEvent } from '@/ui/object/field/types/FieldInputEvent';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { ColumnIndexContext } from '../../contexts/ColumnIndexContext';
import { useMoveSoftFocus } from '../../hooks/useMoveSoftFocus';
import { useTableCell } from '../hooks/useTableCell';
@@ -18,10 +13,6 @@ export const TableCell = ({
}: {
customHotkeyScope: HotkeyScope;
}) => {
const isFirstColumn = useContext(ColumnIndexContext) === 0;
const buttonIcon = useGetButtonIcon();
const { closeTableCell } = useTableCell();
const { moveLeft, moveRight, moveDown } = useMoveSoftFocus();
@@ -72,7 +63,6 @@ export const TableCell = ({
/>
}
nonEditModeContent={<FieldDisplay />}
buttonIcon={isFirstColumn ? IconArrowUpRight : buttonIcon}
></TableCellContainer>
);
};

View File

@@ -1,7 +1,8 @@
import { ReactElement, useContext, useState } from 'react';
import styled from '@emotion/styled';
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
import { IconArrowUpRight } from '@/ui/display/icon';
import { useGetButtonIcon } from '@/ui/object/field/hooks/useGetButtonIcon';
import { useIsFieldEmpty } from '@/ui/object/field/hooks/useIsFieldEmpty';
import { useIsFieldInputOnly } from '@/ui/object/field/hooks/useIsFieldInputOnly';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
@@ -39,7 +40,6 @@ export type TableCellContainerProps = {
editHotkeyScope?: HotkeyScope;
transparent?: boolean;
maxContentWidth?: number;
buttonIcon?: IconComponent;
onSubmit?: () => void;
onCancel?: () => void;
};
@@ -54,7 +54,6 @@ export const TableCellContainer = ({
editModeContent,
nonEditModeContent,
editHotkeyScope,
buttonIcon,
}: TableCellContainerProps) => {
const { isCurrentTableCellInEditMode } = useCurrentTableCellEditMode();
@@ -95,6 +94,12 @@ export const TableCellContainer = ({
const isEmpty = useIsFieldEmpty();
const isFirstColumn = useContext(ColumnIndexContext) === 0;
const customButtonIcon = useGetButtonIcon();
const buttonIcon = isFirstColumn ? IconArrowUpRight : customButtonIcon;
const showButton =
!!buttonIcon &&
hasSoftFocus &&

View File

@@ -1,4 +1,5 @@
import { PropsWithChildren, useEffect, useRef } from 'react';
import { Key } from 'ts-key-enum';
import { useIsFieldInputOnly } from '@/ui/object/field/hooks/useIsFieldInputOnly';
import { useToggleEditOnlyInput } from '@/ui/object/field/hooks/useToggleEditOnlyInput';
@@ -26,7 +27,25 @@ export const TableCellSoftFocusMode = ({
}, []);
useScopedHotkeys(
'enter',
[Key.Backspace, Key.Delete],
() => {
if (!isFieldInputOnly) {
openTableCell({
initialValue: {
isEmpty: true,
},
});
}
},
TableHotkeyScope.TableSoftFocus,
[openTableCell],
{
enabled: !isFieldInputOnly,
},
);
useScopedHotkeys(
Key.Enter,
() => {
if (!isFieldInputOnly) {
openTableCell();
@@ -42,6 +61,10 @@ export const TableCellSoftFocusMode = ({
'*',
(keyboardEvent) => {
if (!isFieldInputOnly) {
keyboardEvent.preventDefault();
keyboardEvent.stopPropagation();
keyboardEvent.stopImmediatePropagation();
const isWritingText =
!isNonTextWritingKey(keyboardEvent.key) &&
!keyboardEvent.ctrlKey &&
@@ -51,7 +74,11 @@ export const TableCellSoftFocusMode = ({
return;
}
openTableCell();
openTableCell({
initialValue: {
value: keyboardEvent.key,
},
});
}
},
TableHotkeyScope.TableSoftFocus,

View File

@@ -0,0 +1,17 @@
import { useRecoilState } from 'recoil';
import { tableCellInitialValueFamilyState } from '../../states/tableCellInitialValueFamilyState';
import { useCurrentTableCellPosition } from './useCurrentCellPosition';
export const useCurrentTableCellInitialValue = () => {
const currentTableCellPosition = useCurrentTableCellPosition();
const [currentTableCellInitialValue, setCurrentTableCellInitialValue] =
useRecoilState(tableCellInitialValueFamilyState(currentTableCellPosition));
return {
currentTableCellInitialValue,
setCurrentTableCellInitialValue,
};
};

View File

@@ -1,8 +1,11 @@
import { useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import { useRecoilState } from 'recoil';
import { FieldContext } from '@/ui/object/field/contexts/FieldContext';
import { useIsFieldEmpty } from '@/ui/object/field/hooks/useIsFieldEmpty';
import { entityFieldInitialValueFamilyState } from '@/ui/object/field/states/entityFieldInitialValueFamilyState';
import { FieldInitialValue } from '@/ui/object/field/types/FieldInitialValue';
import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
@@ -28,12 +31,6 @@ export const useTableCell = () => {
const customCellHotkeyScope = useContext(CellHotkeyScopeContext);
const closeTableCell = () => {
setDragSelectionStartEnabled(true);
closeCurrentTableCellInEditMode();
setHotkeyScope(TableHotkeyScope.TableSoftFocus);
};
const navigate = useNavigate();
const isFirstColumnCell = useContext(ColumnIndexContext) === 0;
@@ -42,7 +39,14 @@ export const useTableCell = () => {
const { entityId, fieldDefinition } = useContext(FieldContext);
const openTableCell = () => {
const [, setFieldInitialValue] = useRecoilState(
entityFieldInitialValueFamilyState({
entityId,
fieldId: fieldDefinition.fieldId,
}),
);
const openTableCell = (options?: { initialValue?: FieldInitialValue }) => {
if (isFirstColumnCell && !isEmpty && fieldDefinition.basePathToShowPage) {
navigate(`${fieldDefinition.basePathToShowPage}${entityId}`);
return;
@@ -51,6 +55,10 @@ export const useTableCell = () => {
setDragSelectionStartEnabled(false);
setCurrentTableCellInEditMode();
if (options?.initialValue) {
setFieldInitialValue(options.initialValue);
}
if (customCellHotkeyScope) {
setHotkeyScope(
customCellHotkeyScope.scope,
@@ -61,6 +69,13 @@ export const useTableCell = () => {
}
};
const closeTableCell = () => {
setDragSelectionStartEnabled(true);
closeCurrentTableCellInEditMode();
setFieldInitialValue(undefined);
setHotkeyScope(TableHotkeyScope.TableSoftFocus);
};
return {
closeTableCell,
openTableCell,

View File

@@ -1,3 +1,5 @@
import { useEffect } from 'react';
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
import { IconUserCircle } from '@/ui/display/icon';
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
@@ -12,6 +14,7 @@ export type UserPickerProps = {
onSubmit: (newUser: EntityForSelect | null) => void;
onCancel?: () => void;
width?: number;
initialSearchFilter?: string | null;
};
type UserForSelect = EntityForSelect & {
@@ -23,10 +26,14 @@ export const UserPicker = ({
onSubmit,
onCancel,
width,
initialSearchFilter,
}: UserPickerProps) => {
const [relationPickerSearchFilter] = useRecoilScopedState(
relationPickerSearchFilterScopedState,
);
const [relationPickerSearchFilter, setRelationPickerSearchFilter] =
useRecoilScopedState(relationPickerSearchFilterScopedState);
useEffect(() => {
setRelationPickerSearchFilter(initialSearchFilter ?? '');
}, [initialSearchFilter, setRelationPickerSearchFilter]);
const users = useFilteredSearchEntityQuery({
queryHook: useSearchUserQuery,