Use backspace for clearing record table cell. (#4299)
* Use backspace for clearing record table cell.
This commit is contained in:
@ -0,0 +1,62 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
|
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||||
|
import { generateEmptyFieldValue } from '@/object-record/utils/generateEmptyFieldValue';
|
||||||
|
|
||||||
|
import { FieldContext } from '../contexts/FieldContext';
|
||||||
|
|
||||||
|
export const useClearField = () => {
|
||||||
|
const {
|
||||||
|
entityId,
|
||||||
|
fieldDefinition,
|
||||||
|
useUpdateRecord = () => [],
|
||||||
|
} = useContext(FieldContext);
|
||||||
|
|
||||||
|
const [updateRecord] = useUpdateRecord();
|
||||||
|
|
||||||
|
const clearField = useRecoilCallback(
|
||||||
|
({ snapshot, set }) =>
|
||||||
|
() => {
|
||||||
|
const objectMetadataItems = snapshot
|
||||||
|
.getLoadable(objectMetadataItemsState())
|
||||||
|
.getValue();
|
||||||
|
|
||||||
|
const foundObjectMetadataItem = objectMetadataItems.find(
|
||||||
|
(item) =>
|
||||||
|
item.nameSingular ===
|
||||||
|
fieldDefinition.metadata.objectMetadataNameSingular,
|
||||||
|
);
|
||||||
|
|
||||||
|
const foundFieldMetadataItem = foundObjectMetadataItem?.fields.find(
|
||||||
|
(field) => field.name === fieldDefinition.metadata.fieldName,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!foundObjectMetadataItem || !foundFieldMetadataItem) {
|
||||||
|
throw new Error('Field metadata item cannot be found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldName = fieldDefinition.metadata.fieldName;
|
||||||
|
|
||||||
|
const emptyFieldValue = generateEmptyFieldValue(foundFieldMetadataItem);
|
||||||
|
|
||||||
|
set(
|
||||||
|
recordStoreFamilySelector({ recordId: entityId, fieldName }),
|
||||||
|
emptyFieldValue,
|
||||||
|
);
|
||||||
|
|
||||||
|
updateRecord?.({
|
||||||
|
variables: {
|
||||||
|
where: { id: entityId },
|
||||||
|
updateOneRecordInput: {
|
||||||
|
[fieldName]: emptyFieldValue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[entityId, fieldDefinition, updateRecord],
|
||||||
|
);
|
||||||
|
|
||||||
|
return clearField;
|
||||||
|
};
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
|
||||||
|
import { isFieldDateTime } from '@/object-record/record-field/types/guards/isFieldDateTime';
|
||||||
|
|
||||||
|
import { FieldContext } from '../contexts/FieldContext';
|
||||||
|
|
||||||
|
// TODO: have a better clearable settings in metadata ?
|
||||||
|
// We might want to define what's clearable in the metadata
|
||||||
|
// Instead of passing it in the context
|
||||||
|
// See: https://github.com/twentyhq/twenty/issues/4403
|
||||||
|
export const useIsFieldClearable = (): boolean => {
|
||||||
|
const { clearable, isLabelIdentifier, fieldDefinition } =
|
||||||
|
useContext(FieldContext);
|
||||||
|
|
||||||
|
const isDateField = isFieldDateTime(fieldDefinition);
|
||||||
|
|
||||||
|
const fieldCanBeCleared =
|
||||||
|
!isLabelIdentifier && !isDateField && clearable !== false;
|
||||||
|
|
||||||
|
return fieldCanBeCleared;
|
||||||
|
};
|
||||||
@ -15,4 +15,6 @@ export type FieldType =
|
|||||||
| 'SELECT'
|
| 'SELECT'
|
||||||
| 'TEXT'
|
| 'TEXT'
|
||||||
| 'URL'
|
| 'URL'
|
||||||
| 'UUID';
|
| 'UUID'
|
||||||
|
| 'MULTI_SELECT'
|
||||||
|
| 'NUMERIC';
|
||||||
|
|||||||
@ -2,6 +2,8 @@ import { PropsWithChildren, useEffect, useRef } from 'react';
|
|||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { Key } from 'ts-key-enum';
|
import { Key } from 'ts-key-enum';
|
||||||
|
|
||||||
|
import { useClearField } from '@/object-record/record-field/hooks/useClearField';
|
||||||
|
import { useIsFieldClearable } from '@/object-record/record-field/hooks/useIsFieldClearable';
|
||||||
import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly';
|
import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly';
|
||||||
import { useToggleEditOnlyInput } from '@/object-record/record-field/hooks/useToggleEditOnlyInput';
|
import { useToggleEditOnlyInput } from '@/object-record/record-field/hooks/useToggleEditOnlyInput';
|
||||||
import { useOpenRecordTableCell } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell';
|
import { useOpenRecordTableCell } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell';
|
||||||
@ -21,10 +23,14 @@ export const RecordTableCellSoftFocusMode = ({
|
|||||||
const { openTableCell } = useOpenRecordTableCell();
|
const { openTableCell } = useOpenRecordTableCell();
|
||||||
|
|
||||||
const isFieldInputOnly = useIsFieldInputOnly();
|
const isFieldInputOnly = useIsFieldInputOnly();
|
||||||
|
|
||||||
|
const isFieldClearable = useIsFieldClearable();
|
||||||
|
|
||||||
const toggleEditOnlyInput = useToggleEditOnlyInput();
|
const toggleEditOnlyInput = useToggleEditOnlyInput();
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const isSoftFocusUsingMouse = useRecoilValue(isSoftFocusUsingMouseState());
|
const isSoftFocusUsingMouse = useRecoilValue(isSoftFocusUsingMouseState());
|
||||||
|
const clearField = useClearField();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isSoftFocusUsingMouse) {
|
if (!isSoftFocusUsingMouse) {
|
||||||
@ -35,12 +41,12 @@ export const RecordTableCellSoftFocusMode = ({
|
|||||||
useScopedHotkeys(
|
useScopedHotkeys(
|
||||||
[Key.Backspace, Key.Delete],
|
[Key.Backspace, Key.Delete],
|
||||||
() => {
|
() => {
|
||||||
if (!isFieldInputOnly) {
|
if (!isFieldInputOnly && isFieldClearable) {
|
||||||
openTableCell();
|
clearField();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
TableHotkeyScope.TableSoftFocus,
|
TableHotkeyScope.TableSoftFocus,
|
||||||
[openTableCell],
|
[clearField, isFieldClearable, isFieldInputOnly],
|
||||||
{
|
{
|
||||||
enabled: !isFieldInputOnly,
|
enabled: !isFieldInputOnly,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -17,14 +17,12 @@ export const generateEmptyFieldValue = (
|
|||||||
return {
|
return {
|
||||||
label: '',
|
label: '',
|
||||||
url: '',
|
url: '',
|
||||||
__typename: 'Link',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case FieldMetadataType.FullName: {
|
case FieldMetadataType.FullName: {
|
||||||
return {
|
return {
|
||||||
firstName: '',
|
firstName: '',
|
||||||
lastName: '',
|
lastName: '',
|
||||||
__typename: 'FullName',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case FieldMetadataType.DateTime: {
|
case FieldMetadataType.DateTime: {
|
||||||
@ -43,6 +41,8 @@ export const generateEmptyFieldValue = (
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case FieldMetadataType.Relation: {
|
case FieldMetadataType.Relation: {
|
||||||
|
// TODO: refactor with relationDefiniton once the PR is merged : https://github.com/twentyhq/twenty/pull/4378
|
||||||
|
// so we can directly check the relation type from this field point of view.
|
||||||
if (
|
if (
|
||||||
!isNonEmptyString(
|
!isNonEmptyString(
|
||||||
fieldMetadataItem.fromRelationMetadata?.toObjectMetadata
|
fieldMetadataItem.fromRelationMetadata?.toObjectMetadata
|
||||||
@ -63,7 +63,6 @@ export const generateEmptyFieldValue = (
|
|||||||
return {
|
return {
|
||||||
amountMicros: null,
|
amountMicros: null,
|
||||||
currencyCode: null,
|
currencyCode: null,
|
||||||
__typename: 'Currency',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case FieldMetadataType.Select: {
|
case FieldMetadataType.Select: {
|
||||||
|
|||||||
Reference in New Issue
Block a user