Add Edit Rich Text functionality to table view (#11390)
Fixes https://github.com/twentyhq/core-team-issues/issues/729 [recording.webm](https://github.com/user-attachments/assets/ea95d67b-64a3-4fef-91ed-b06318099a78) --------- Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
@ -2,6 +2,7 @@ import { CommandMenu } from '@/command-menu/components/CommandMenu';
|
||||
import { CommandMenuCalendarEventPage } from '@/command-menu/pages/calendar-event/components/CommandMenuCalendarEventPage';
|
||||
import { CommandMenuMessageThreadPage } from '@/command-menu/pages/message-thread/components/CommandMenuMessageThreadPage';
|
||||
import { CommandMenuRecordPage } from '@/command-menu/pages/record-page/components/CommandMenuRecordPage';
|
||||
import { CommandMenuEditRichTextPage } from '@/command-menu/pages/rich-text-page/components/CommandMenuEditRichTextPage';
|
||||
import { CommandMenuSearchRecordsPage } from '@/command-menu/pages/search/components/CommandMenuSearchRecordsPage';
|
||||
import { CommandMenuWorkflowSelectAction } from '@/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectAction';
|
||||
import { CommandMenuWorkflowEditStep } from '@/command-menu/pages/workflow/step/edit/components/CommandMenuWorkflowEditStep';
|
||||
@ -18,6 +19,7 @@ export const COMMAND_MENU_PAGES_CONFIG = new Map<
|
||||
[CommandMenuPages.ViewRecord, <CommandMenuRecordPage />],
|
||||
[CommandMenuPages.ViewEmailThread, <CommandMenuMessageThreadPage />],
|
||||
[CommandMenuPages.ViewCalendarEvent, <CommandMenuCalendarEventPage />],
|
||||
[CommandMenuPages.EditRichText, <CommandMenuEditRichTextPage />],
|
||||
[
|
||||
CommandMenuPages.WorkflowStepSelectTriggerType,
|
||||
<CommandMenuWorkflowSelectTriggerType />,
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { viewableRichTextComponentState } from '@/command-menu/pages/rich-text-page/states/viewableRichTextComponentState';
|
||||
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
|
||||
import { useCallback } from 'react';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { IconPencil } from 'twenty-ui/display';
|
||||
|
||||
export const useRichTextCommandMenu = () => {
|
||||
const { navigateCommandMenu, openCommandMenu } = useCommandMenu();
|
||||
|
||||
const openRichTextInCommandMenu = useRecoilCallback(
|
||||
({ set }) =>
|
||||
({
|
||||
activityId,
|
||||
activityObjectNameSingular,
|
||||
}: {
|
||||
activityId: string;
|
||||
activityObjectNameSingular: string;
|
||||
}) => {
|
||||
set(viewableRichTextComponentState, {
|
||||
activityId,
|
||||
activityObjectNameSingular,
|
||||
});
|
||||
|
||||
openCommandMenu();
|
||||
navigateCommandMenu({
|
||||
page: CommandMenuPages.EditRichText,
|
||||
pageTitle: 'Rich Text',
|
||||
pageIcon: IconPencil,
|
||||
});
|
||||
},
|
||||
[navigateCommandMenu, openCommandMenu],
|
||||
);
|
||||
|
||||
const editRichText = useCallback(
|
||||
(activityId: string, activityObjectNameSingular: string) => {
|
||||
openRichTextInCommandMenu({ activityId, activityObjectNameSingular });
|
||||
},
|
||||
[openRichTextInCommandMenu],
|
||||
);
|
||||
|
||||
return {
|
||||
editRichText,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,33 @@
|
||||
import { ActivityRichTextEditor } from '@/activities/components/ActivityRichTextEditor';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { viewableRichTextComponentState } from '../states/viewableRichTextComponentState';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
margin: ${({ theme }) => theme.spacing(4)} ${({ theme }) => theme.spacing(-2)};
|
||||
`;
|
||||
|
||||
export const CommandMenuEditRichTextPage = () => {
|
||||
const { activityId, activityObjectNameSingular } = useRecoilValue(
|
||||
viewableRichTextComponentState,
|
||||
);
|
||||
|
||||
if (
|
||||
activityObjectNameSingular !== CoreObjectNameSingular.Note &&
|
||||
activityObjectNameSingular !== CoreObjectNameSingular.Task
|
||||
) {
|
||||
throw new Error(
|
||||
`Invalid activity object name singular: ${activityObjectNameSingular}`,
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<ActivityRichTextEditor
|
||||
activityId={activityId}
|
||||
activityObjectNameSingular={activityObjectNameSingular}
|
||||
/>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,12 @@
|
||||
import { atom, RecoilState } from 'recoil';
|
||||
|
||||
export const viewableRichTextComponentState: RecoilState<{
|
||||
activityId: string;
|
||||
activityObjectNameSingular: string;
|
||||
}> = atom({
|
||||
key: 'viewableRichTextComponentState',
|
||||
default: {
|
||||
activityId: '',
|
||||
activityObjectNameSingular: '',
|
||||
},
|
||||
});
|
||||
@ -3,6 +3,7 @@ export enum CommandMenuPages {
|
||||
ViewRecord = 'view-record',
|
||||
ViewEmailThread = 'view-email-thread',
|
||||
ViewCalendarEvent = 'view-calendar-event',
|
||||
EditRichText = 'edit-rich-text',
|
||||
Copilot = 'copilot',
|
||||
WorkflowStepSelectTriggerType = 'workflow-step-select-trigger-type',
|
||||
WorkflowStepSelectAction = 'workflow-step-select-action',
|
||||
|
||||
@ -13,7 +13,9 @@ import { SelectFieldInput } from '@/object-record/record-field/meta-types/input/
|
||||
import { isFieldPhones } from '@/object-record/record-field/types/guards/isFieldPhones';
|
||||
import { isFieldRelationFromManyObjects } from '@/object-record/record-field/types/guards/isFieldRelationFromManyObjects';
|
||||
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { ArrayFieldInput } from '@/object-record/record-field/meta-types/input/components/ArrayFieldInput';
|
||||
import { RichTextFieldInput } from '@/object-record/record-field/meta-types/input/components/RichTextFieldInput';
|
||||
import { isFieldAddress } from '@/object-record/record-field/types/guards/isFieldAddress';
|
||||
import { isFieldArray } from '@/object-record/record-field/types/guards/isFieldArray';
|
||||
import { isFieldBoolean } from '@/object-record/record-field/types/guards/isFieldBoolean';
|
||||
@ -28,6 +30,7 @@ import { isFieldNumber } from '@/object-record/record-field/types/guards/isField
|
||||
import { isFieldRating } from '@/object-record/record-field/types/guards/isFieldRating';
|
||||
import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson';
|
||||
import { isFieldRelationToOneObject } from '@/object-record/record-field/types/guards/isFieldRelationToOneObject';
|
||||
import { isFieldRichTextV2 } from '@/object-record/record-field/types/guards/isFieldRichTextV2';
|
||||
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
|
||||
import { FieldContext } from '../contexts/FieldContext';
|
||||
import { BooleanFieldInput } from '../meta-types/input/components/BooleanFieldInput';
|
||||
@ -38,6 +41,7 @@ import { RatingFieldInput } from '../meta-types/input/components/RatingFieldInpu
|
||||
import { RelationToOneFieldInput } from '../meta-types/input/components/RelationToOneFieldInput';
|
||||
import { TextFieldInput } from '../meta-types/input/components/TextFieldInput';
|
||||
import { FieldInputEvent } from '../types/FieldInputEvent';
|
||||
import { FieldRichTextV2Metadata } from '../types/FieldMetadata';
|
||||
import { isFieldText } from '../types/guards/isFieldText';
|
||||
|
||||
type FieldInputProps = {
|
||||
@ -64,7 +68,7 @@ export const FieldInput = ({
|
||||
onClickOutside,
|
||||
isReadOnly,
|
||||
}: FieldInputProps) => {
|
||||
const { fieldDefinition } = useContext(FieldContext);
|
||||
const { fieldDefinition, recordId } = useContext(FieldContext);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -161,6 +165,22 @@ export const FieldInput = ({
|
||||
onCancel={onCancel}
|
||||
onClickOutside={(event) => onClickOutside?.(() => {}, event)}
|
||||
/>
|
||||
) : isFieldRichTextV2(fieldDefinition) ? (
|
||||
<RichTextFieldInput
|
||||
targetableObject={{
|
||||
id: recordId,
|
||||
targetObjectNameSingular: (
|
||||
fieldDefinition as {
|
||||
metadata: FieldRichTextV2Metadata;
|
||||
}
|
||||
).metadata.objectMetadataNameSingular as
|
||||
| CoreObjectNameSingular.Note
|
||||
| CoreObjectNameSingular.Task,
|
||||
}}
|
||||
onCancel={onCancel}
|
||||
onClickOutside={onClickOutside}
|
||||
onEscape={onEscape}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
@ -1,60 +1,89 @@
|
||||
import { BLOCK_SCHEMA } from '@/activities/blocks/constants/Schema';
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { useRichTextField } from '@/object-record/record-field/meta-types/hooks/useRichTextField';
|
||||
import { ActivityRichTextEditor } from '@/activities/components/ActivityRichTextEditor';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { useRichTextCommandMenu } from '@/command-menu/hooks/useRichTextCommandMenu';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useRegisterInputEvents } from '@/object-record/record-field/meta-types/input/hooks/useRegisterInputEvents';
|
||||
import { FieldInputClickOutsideEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import {
|
||||
FieldInputClickOutsideEvent,
|
||||
FieldInputEvent,
|
||||
} from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { BlockEditor } from '@/ui/input/editor/components/BlockEditor';
|
||||
import { BlockEditorComponentInstanceContext } from '@/ui/input/editor/contexts/BlockEditorCompoponeInstanceContext';
|
||||
import { PartialBlock } from '@blocknote/core';
|
||||
import { useCreateBlockNote } from '@blocknote/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { useContext, useRef } from 'react';
|
||||
|
||||
const StyledRichTextContainer = styled.div`
|
||||
height: 400px;
|
||||
width: 500px;
|
||||
|
||||
overflow: auto;
|
||||
`;
|
||||
import { useRef } from 'react';
|
||||
import { IconLayoutSidebarLeftCollapse } from 'twenty-ui/display';
|
||||
import { FloatingIconButton } from 'twenty-ui/input';
|
||||
|
||||
export type RichTextFieldInputProps = {
|
||||
onClickOutside?: FieldInputClickOutsideEvent;
|
||||
onCancel?: () => void;
|
||||
onEnter?: FieldInputEvent;
|
||||
onEscape?: FieldInputEvent;
|
||||
};
|
||||
|
||||
export const RichTextFieldInput = ({
|
||||
onClickOutside,
|
||||
}: RichTextFieldInputProps) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const { recordId } = useContext(FieldContext);
|
||||
const { draftValue, persistRichTextField, fieldDefinition } =
|
||||
useRichTextField();
|
||||
const StyledContainer = styled.div`
|
||||
background-color: ${({ theme }) => theme.background.primary};
|
||||
width: 480px;
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
margin: 0 0 0 ${({ theme }) => theme.spacing(-6)};
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const editor = useCreateBlockNote({
|
||||
initialContent: draftValue,
|
||||
domAttributes: { editor: { class: 'editor' } },
|
||||
schema: BLOCK_SCHEMA,
|
||||
});
|
||||
const StyledCollapseButton = styled.div`
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const RichTextFieldInput = ({
|
||||
targetableObject,
|
||||
onClickOutside,
|
||||
onEscape,
|
||||
}: {
|
||||
targetableObject: Pick<ActivityTargetableObject, 'id'> & {
|
||||
targetObjectNameSingular:
|
||||
| CoreObjectNameSingular.Note
|
||||
| CoreObjectNameSingular.Task;
|
||||
};
|
||||
} & RichTextFieldInputProps) => {
|
||||
const { editRichText } = useRichTextCommandMenu();
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleClickOutside = (event: MouseEvent | TouchEvent) => {
|
||||
onClickOutside?.(() => persistRichTextField(editor.document), event);
|
||||
onClickOutside?.(() => {}, event);
|
||||
};
|
||||
|
||||
useRegisterInputEvents<PartialBlock[]>({
|
||||
const handleEscape = () => {
|
||||
onEscape?.(() => {});
|
||||
};
|
||||
|
||||
useRegisterInputEvents({
|
||||
inputRef: containerRef,
|
||||
inputValue: draftValue,
|
||||
inputValue: null,
|
||||
onClickOutside: handleClickOutside,
|
||||
onEscape: handleEscape,
|
||||
hotkeyScope: DEFAULT_CELL_SCOPE.scope,
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledRichTextContainer ref={containerRef}>
|
||||
<BlockEditorComponentInstanceContext.Provider
|
||||
value={{ instanceId: `${recordId}-${fieldDefinition.fieldMetadataId}` }}
|
||||
>
|
||||
<BlockEditor editor={editor} />
|
||||
</BlockEditorComponentInstanceContext.Provider>
|
||||
</StyledRichTextContainer>
|
||||
<StyledContainer ref={containerRef}>
|
||||
<ActivityRichTextEditor
|
||||
activityId={targetableObject.id}
|
||||
activityObjectNameSingular={targetableObject.targetObjectNameSingular}
|
||||
/>
|
||||
<StyledCollapseButton>
|
||||
<FloatingIconButton
|
||||
Icon={IconLayoutSidebarLeftCollapse}
|
||||
size="small"
|
||||
onClick={() => {
|
||||
onEscape?.(() => {});
|
||||
editRichText(
|
||||
targetableObject.id,
|
||||
targetableObject.targetObjectNameSingular,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</StyledCollapseButton>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1,132 @@
|
||||
import { expect, fn, userEvent, waitFor, within } from '@storybook/test';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { Decorator, Meta, StoryObj } from '@storybook/react';
|
||||
import { FieldMetadataType } from '~/generated/graphql';
|
||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
import { RichTextFieldInput } from '../RichTextFieldInput';
|
||||
|
||||
const clickOutsideJestFn = fn();
|
||||
const escapeJestFn = fn();
|
||||
|
||||
type RichTextFieldInputWithContextProps = {
|
||||
targetableObjectId?: string;
|
||||
onClickOutside?: typeof clickOutsideJestFn;
|
||||
onEscape?: typeof escapeJestFn;
|
||||
};
|
||||
|
||||
const clearMocksDecorator: Decorator = (Story, context) => {
|
||||
if (context.parameters.clearMocks !== false) {
|
||||
clickOutsideJestFn.mockClear();
|
||||
escapeJestFn.mockClear();
|
||||
}
|
||||
return <Story />;
|
||||
};
|
||||
|
||||
const RichTextFieldInputWithContext = ({
|
||||
targetableObjectId = 'test-id',
|
||||
onClickOutside,
|
||||
onEscape,
|
||||
}: RichTextFieldInputWithContextProps) => {
|
||||
const setHotKeyScope = useSetHotkeyScope();
|
||||
|
||||
useEffect(() => {
|
||||
setHotKeyScope(DEFAULT_CELL_SCOPE.scope);
|
||||
}, [setHotKeyScope]);
|
||||
|
||||
return (
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: 'record-field-component-instance-id',
|
||||
}}
|
||||
>
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
recordId: targetableObjectId,
|
||||
fieldDefinition: {
|
||||
fieldMetadataId: 'richText',
|
||||
label: 'Rich Text',
|
||||
type: FieldMetadataType.RICH_TEXT,
|
||||
iconName: 'IconRichText',
|
||||
metadata: {
|
||||
fieldName: 'richText',
|
||||
objectMetadataNameSingular: 'note',
|
||||
},
|
||||
},
|
||||
isLabelIdentifier: false,
|
||||
isReadOnly: false,
|
||||
}}
|
||||
>
|
||||
<RichTextFieldInput
|
||||
targetableObject={{
|
||||
id: targetableObjectId,
|
||||
targetObjectNameSingular: CoreObjectNameSingular.Note,
|
||||
}}
|
||||
onClickOutside={onClickOutside}
|
||||
onEscape={onEscape}
|
||||
/>
|
||||
</FieldContext.Provider>
|
||||
<div data-testid="click-outside-element" />
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const meta: Meta = {
|
||||
title: 'UI/Data/Field/Input/RichTextFieldInput',
|
||||
component: RichTextFieldInputWithContext,
|
||||
args: {
|
||||
targetableObjectId: 'test-id',
|
||||
onClickOutside: clickOutsideJestFn,
|
||||
onEscape: escapeJestFn,
|
||||
},
|
||||
argTypes: {
|
||||
onClickOutside: { control: false },
|
||||
onEscape: { control: false },
|
||||
},
|
||||
decorators: [
|
||||
clearMocksDecorator,
|
||||
SnackBarDecorator,
|
||||
I18nFrontDecorator,
|
||||
ObjectMetadataItemsDecorator,
|
||||
],
|
||||
parameters: {
|
||||
clearMocks: true,
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof RichTextFieldInputWithContext>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
||||
export const Escape: Story = {
|
||||
play: async () => {
|
||||
expect(escapeJestFn).toHaveBeenCalledTimes(0);
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.keyboard('{esc}');
|
||||
expect(escapeJestFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const ClickOutside: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(clickOutsideJestFn).toHaveBeenCalledTimes(0);
|
||||
|
||||
await waitFor(() => {
|
||||
const outsideElement = canvas.getByTestId('click-outside-element');
|
||||
userEvent.click(outsideElement);
|
||||
expect(clickOutsideJestFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
};
|
||||
@ -54,12 +54,12 @@ describe('isFieldValueReadOnly', () => {
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true if fieldType is RICH_TEXT_V2', () => {
|
||||
it('should return false if fieldType is RICH_TEXT_V2', () => {
|
||||
const result = isFieldValueReadOnly({
|
||||
fieldType: FieldMetadataType.RICH_TEXT_V2,
|
||||
});
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true if fieldType is ACTOR', () => {
|
||||
|
||||
@ -2,7 +2,7 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
|
||||
import { isWorkflowSubObjectMetadata } from '@/object-metadata/utils/isWorkflowSubObjectMetadata';
|
||||
import { isFieldActor } from '@/object-record/record-field/types/guards/isFieldActor';
|
||||
import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText';
|
||||
import { isFieldRichTextV2 } from '@/object-record/record-field/types/guards/isFieldRichTextV2';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
@ -61,9 +61,7 @@ export const isFieldValueReadOnly = ({
|
||||
|
||||
if (
|
||||
isDefined(fieldType) &&
|
||||
(isFieldActor({ type: fieldType }) ||
|
||||
isFieldRichText({ type: fieldType }) ||
|
||||
isFieldRichTextV2({ type: fieldType }))
|
||||
(isFieldActor({ type: fieldType }) || isFieldRichText({ type: fieldType }))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -55,11 +55,16 @@ export const FieldsCard = ({
|
||||
);
|
||||
|
||||
const { inlineFieldMetadataItems, relationFieldMetadataItems } = groupBy(
|
||||
availableFieldMetadataItems.filter(
|
||||
(fieldMetadataItem) =>
|
||||
fieldMetadataItem.name !== 'createdAt' &&
|
||||
fieldMetadataItem.name !== 'deletedAt',
|
||||
),
|
||||
availableFieldMetadataItems
|
||||
.filter(
|
||||
(fieldMetadataItem) =>
|
||||
fieldMetadataItem.name !== 'createdAt' &&
|
||||
fieldMetadataItem.name !== 'deletedAt',
|
||||
)
|
||||
.filter(
|
||||
(fieldMetadataItem) =>
|
||||
fieldMetadataItem.type !== FieldMetadataType.RICH_TEXT_V2,
|
||||
),
|
||||
(fieldMetadataItem) =>
|
||||
fieldMetadataItem.type === FieldMetadataType.RELATION
|
||||
? 'relationFieldMetadataItems'
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { FieldInput } from '@/object-record/record-field/components/FieldInput';
|
||||
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import {
|
||||
FieldInputClickOutsideEvent,
|
||||
@ -12,7 +13,6 @@ import { useRecoilCallback } from 'recoil';
|
||||
|
||||
export const RecordTableCellFieldInput = () => {
|
||||
const { onMoveFocus, onCloseTableCell } = useRecordTableBodyContextOrThrow();
|
||||
|
||||
const { isReadOnly } = useContext(FieldContext);
|
||||
|
||||
const handleEnter: FieldInputEvent = (persistField) => {
|
||||
|
||||
Reference in New Issue
Block a user