diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/components/CreateRelatedRecordAction.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/components/CreateRelatedRecordAction.tsx
index 406867c220..329da09050 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/components/CreateRelatedRecordAction.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/components/CreateRelatedRecordAction.tsx
@@ -10,6 +10,7 @@ import { useRecordTitleCell } from '@/object-record/record-title-cell/hooks/useR
import { RecordTitleCellContainerType } from '@/object-record/record-title-cell/types/RecordTitleCellContainerType';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { getForeignKeyNameFromRelationFieldName } from '@/object-record/utils/getForeignKeyNameFromRelationFieldName';
+import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
import { isDefined } from 'twenty-shared/utils';
interface CreateRelatedRecordActionProps {
@@ -106,7 +107,11 @@ export const CreateRelatedRecordAction = ({
openRecordTitleCell({
recordId: createdRecord.id,
fieldName: labelIdentifierFieldMetadataItem.name,
- containerType: RecordTitleCellContainerType.ShowPage,
+ instanceId: getRecordFieldInputInstanceId({
+ recordId: createdRecord.id,
+ fieldName: labelIdentifierFieldMetadataItem.name,
+ prefix: RecordTitleCellContainerType.ShowPage,
+ }),
});
}
};
diff --git a/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx b/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx
index ea6d02ccf4..2c2fd5a1de 100644
--- a/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx
+++ b/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx
@@ -30,7 +30,7 @@ import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRec
import { useRestoreManyRecords } from '@/object-record/hooks/useRestoreManyRecords';
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
import { useIsRecordFieldReadOnly } from '@/object-record/record-field/ui/hooks/read-only/useIsRecordFieldReadOnly';
-import { isInlineCellInEditModeFamilyState } from '@/object-record/record-inline-cell/states/isInlineCellInEditModeFamilyState';
+import { isTitleCellInEditModeComponentState } from '@/object-record/record-title-cell/states/isTitleCellInEditModeComponentState';
import { RecordTitleCellContainerType } from '@/object-record/record-title-cell/types/RecordTitleCellContainerType';
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
import { BlockEditor } from '@/ui/input/editor/components/BlockEditor';
@@ -391,7 +391,11 @@ export const ActivityRichTextEditor = ({
// Because that is how we do in the app, for example with stacked dropdowns, we always close what's open before
// letting the click being captured by a button or input that can capture it.
const isRecordTitleCellOpen = snapshot
- .getLoadable(isInlineCellInEditModeFamilyState(recordTitleCellId))
+ .getLoadable(
+ isTitleCellInEditModeComponentState.atomFamily({
+ instanceId: recordTitleCellId,
+ }),
+ )
.getValue();
if (isRecordTitleCellOpen) {
@@ -417,7 +421,11 @@ export const ActivityRichTextEditor = ({
({ snapshot }) =>
() => {
const isRecordTitleCellOpen = snapshot
- .getLoadable(isInlineCellInEditModeFamilyState(recordTitleCellId))
+ .getLoadable(
+ isTitleCellInEditModeComponentState.atomFamily({
+ instanceId: recordTitleCellId,
+ }),
+ )
.getValue();
if (isRecordTitleCellOpen) {
diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardCellEditModePortal.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardCellEditModePortal.tsx
new file mode 100644
index 0000000000..d00fe91ff9
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardCellEditModePortal.tsx
@@ -0,0 +1,44 @@
+import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
+
+import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
+import { RecordBoardCardInputContextProvider } from '@/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardInputContextProvider';
+import { RECORD_BOARD_CARD_INPUT_ID_PREFIX } from '@/object-record/record-board/record-board-card/constants/RecordBoardCardInputIdPrefix';
+import { RecordBoardCardContext } from '@/object-record/record-board/record-board-card/contexts/RecordBoardCardContext';
+import { useRecordBoardCardMetadataFromPosition } from '@/object-record/record-board/record-board-card/hooks/useRecordBoardCardMetadataFromPosition';
+import { recordBoardCardEditModePositionComponentState } from '@/object-record/record-board/record-board-card/states/recordBoardCardEditModePositionComponentState';
+import { FieldInput } from '@/object-record/record-field/ui/components/FieldInput';
+import { RecordInlineCellAnchoredPortal } from '@/object-record/record-inline-cell/components/RecordInlineCellAnchoredPortal';
+import { RecordInlineCellEditMode } from '@/object-record/record-inline-cell/components/RecordInlineCellEditMode';
+import { useContext } from 'react';
+import { isDefined } from 'twenty-shared/utils';
+
+export const RecordBoardCardCellEditModePortal = () => {
+ const { objectMetadataItem } = useContext(RecordBoardContext);
+ const { recordId } = useContext(RecordBoardCardContext);
+
+ const editModePosition = useRecoilComponentValue(
+ recordBoardCardEditModePositionComponentState,
+ );
+
+ const { editedFieldMetadataItem } = useRecordBoardCardMetadataFromPosition();
+
+ if (!isDefined(editModePosition) || !isDefined(editedFieldMetadataItem)) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardCellHoveredPortal.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardCellHoveredPortal.tsx
new file mode 100644
index 0000000000..87db581e1d
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardCellHoveredPortal.tsx
@@ -0,0 +1,41 @@
+import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
+
+import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
+import { RecordBoardCardCellHoveredPortalContent } from '@/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardCellHoveredPortalContent';
+import { RecordBoardCardInputContextProvider } from '@/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardInputContextProvider';
+import { RECORD_BOARD_CARD_INPUT_ID_PREFIX } from '@/object-record/record-board/record-board-card/constants/RecordBoardCardInputIdPrefix';
+import { RecordBoardCardContext } from '@/object-record/record-board/record-board-card/contexts/RecordBoardCardContext';
+import { useRecordBoardCardMetadataFromPosition } from '@/object-record/record-board/record-board-card/hooks/useRecordBoardCardMetadataFromPosition';
+import { recordBoardCardHoverPositionComponentState } from '@/object-record/record-board/record-board-card/states/recordBoardCardHoverPositionComponentState';
+import { RecordInlineCellAnchoredPortal } from '@/object-record/record-inline-cell/components/RecordInlineCellAnchoredPortal';
+import { useContext } from 'react';
+import { isDefined } from 'twenty-shared/utils';
+
+export const RecordBoardCardCellHoveredPortal = () => {
+ const { objectMetadataItem } = useContext(RecordBoardContext);
+ const { recordId } = useContext(RecordBoardCardContext);
+
+ const hoverPosition = useRecoilComponentValue(
+ recordBoardCardHoverPositionComponentState,
+ );
+
+ const { hoveredFieldMetadataItem } = useRecordBoardCardMetadataFromPosition();
+
+ if (!isDefined(hoverPosition) || !isDefined(hoveredFieldMetadataItem)) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardCellHoveredPortalContent.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardCellHoveredPortalContent.tsx
new file mode 100644
index 0000000000..aa27092557
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardCellHoveredPortalContent.tsx
@@ -0,0 +1,70 @@
+import { RECORD_BOARD_CARD_INPUT_ID_PREFIX } from '@/object-record/record-board/record-board-card/constants/RecordBoardCardInputIdPrefix';
+import { recordBoardCardEditModePositionComponentState } from '@/object-record/record-board/record-board-card/states/recordBoardCardEditModePositionComponentState';
+import { recordBoardCardHoverPositionComponentState } from '@/object-record/record-board/record-board-card/states/recordBoardCardHoverPositionComponentState';
+import { FieldDisplay } from '@/object-record/record-field/ui/components/FieldDisplay';
+import { FieldInput } from '@/object-record/record-field/ui/components/FieldInput';
+import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
+import { useOpenFieldInputEditMode } from '@/object-record/record-field/ui/hooks/useOpenFieldInputEditMode';
+import { useRecordInlineCellContext } from '@/object-record/record-inline-cell/components/RecordInlineCellContext';
+import { RecordInlineCellDisplayMode } from '@/object-record/record-inline-cell/components/RecordInlineCellDisplayMode';
+import { RecordInlineCellHoveredPortalContent } from '@/object-record/record-inline-cell/components/RecordInlineCellHoveredPortalContent';
+import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell';
+import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
+import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
+import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
+import { useContext } from 'react';
+
+export const RecordBoardCardCellHoveredPortalContent = () => {
+ const { editModeContentOnly, isCentered } = useRecordInlineCellContext();
+
+ const { isRecordFieldReadOnly, recordId, fieldDefinition } =
+ useContext(FieldContext);
+
+ const { openInlineCell } = useInlineCell(
+ getRecordFieldInputInstanceId({
+ recordId,
+ fieldName: fieldDefinition.metadata.fieldName,
+ prefix: RECORD_BOARD_CARD_INPUT_ID_PREFIX,
+ }),
+ );
+
+ const shouldContainerBeClickable =
+ !isRecordFieldReadOnly && !editModeContentOnly;
+
+ const [recordBoardCardHoverPosition, setRecordBoardCardHoverPosition] =
+ useRecoilComponentState(recordBoardCardHoverPositionComponentState);
+
+ const setRecordBoardCardEditModePosition = useSetRecoilComponentState(
+ recordBoardCardEditModePositionComponentState,
+ );
+ const { openFieldInput } = useOpenFieldInputEditMode();
+
+ const handleClick = () => {
+ if (shouldContainerBeClickable) {
+ openInlineCell();
+ setRecordBoardCardEditModePosition(recordBoardCardHoverPosition);
+
+ openFieldInput({
+ fieldDefinition,
+ recordId,
+ prefix: RECORD_BOARD_CARD_INPUT_ID_PREFIX,
+ });
+ }
+ };
+
+ const handleMouseLeave = () => {
+ setRecordBoardCardHoverPosition(null);
+ };
+
+ return (
+
+
+ {editModeContentOnly ? : }
+
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardInputContextProvider.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardInputContextProvider.tsx
new file mode 100644
index 0000000000..56a716af8f
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardInputContextProvider.tsx
@@ -0,0 +1,126 @@
+import { usePersistFieldFromFieldInputContext } from '@/object-record/record-field/ui/hooks/usePersistFieldFromFieldInputContext';
+import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
+
+import { recordBoardCardEditModePositionComponentState } from '@/object-record/record-board/record-board-card/states/recordBoardCardEditModePositionComponentState';
+import {
+ FieldInputEventContext,
+ type FieldInputClickOutsideEvent,
+ type FieldInputEvent,
+} from '@/object-record/record-field/ui/contexts/FieldInputEventContext';
+import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell';
+import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector';
+import { useAvailableComponentInstanceId } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceId';
+import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
+import { useCallback } from 'react';
+import { useRecoilCallback } from 'recoil';
+
+type RecordBoardCardInputContextProviderProps = {
+ children: React.ReactNode;
+};
+
+export const RecordBoardCardInputContextProvider = ({
+ children,
+}: RecordBoardCardInputContextProviderProps) => {
+ const { closeInlineCell } = useInlineCell();
+ const setRecordBoardCardEditModePosition = useSetRecoilComponentState(
+ recordBoardCardEditModePositionComponentState,
+ );
+
+ const closeInlineCellAndResetEditModePosition = useCallback(() => {
+ setRecordBoardCardEditModePosition(null);
+ closeInlineCell();
+ }, [closeInlineCell, setRecordBoardCardEditModePosition]);
+
+ const instanceId = useAvailableComponentInstanceId(
+ RecordFieldComponentInstanceContext,
+ );
+
+ const { persistFieldFromFieldInputContext } =
+ usePersistFieldFromFieldInputContext();
+
+ const handleEnter: FieldInputEvent = ({ newValue, skipPersist }) => {
+ if (skipPersist !== true) {
+ persistFieldFromFieldInputContext(newValue);
+ }
+
+ closeInlineCellAndResetEditModePosition();
+ };
+
+ const handleSubmit: FieldInputEvent = ({ newValue, skipPersist }) => {
+ if (skipPersist !== true) {
+ persistFieldFromFieldInputContext(newValue);
+ }
+
+ closeInlineCellAndResetEditModePosition();
+ };
+
+ const handleCancel = () => {
+ closeInlineCellAndResetEditModePosition();
+ };
+
+ const handleClickOutside: FieldInputClickOutsideEvent = useRecoilCallback(
+ ({ snapshot }) =>
+ ({ newValue, event, skipPersist }) => {
+ const currentFocusId = snapshot
+ .getLoadable(currentFocusIdSelector)
+ .getValue();
+
+ if (currentFocusId !== instanceId) {
+ return;
+ }
+ event?.preventDefault();
+ event?.stopImmediatePropagation();
+
+ if (skipPersist !== true) {
+ persistFieldFromFieldInputContext(newValue);
+ }
+
+ closeInlineCellAndResetEditModePosition();
+ },
+ [
+ closeInlineCellAndResetEditModePosition,
+ instanceId,
+ persistFieldFromFieldInputContext,
+ ],
+ );
+
+ const handleEscape: FieldInputEvent = ({ newValue, skipPersist }) => {
+ if (skipPersist !== true) {
+ persistFieldFromFieldInputContext(newValue);
+ }
+
+ closeInlineCellAndResetEditModePosition();
+ };
+
+ const handleTab: FieldInputEvent = ({ newValue, skipPersist }) => {
+ if (skipPersist !== true) {
+ persistFieldFromFieldInputContext(newValue);
+ }
+
+ closeInlineCellAndResetEditModePosition();
+ };
+
+ const handleShiftTab: FieldInputEvent = ({ newValue, skipPersist }) => {
+ if (skipPersist !== true) {
+ persistFieldFromFieldInputContext(newValue);
+ }
+
+ closeInlineCellAndResetEditModePosition();
+ };
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx
index 31815e1186..80e5027f06 100644
--- a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx
@@ -11,9 +11,12 @@ import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-re
import { useActiveRecordBoardCard } from '@/object-record/record-board/hooks/useActiveRecordBoardCard';
import { useFocusedRecordBoardCard } from '@/object-record/record-board/hooks/useFocusedRecordBoardCard';
+import { RecordBoardCardCellEditModePortal } from '@/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardCellEditModePortal';
+import { RecordBoardCardCellHoveredPortal } from '@/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardCellHoveredPortal';
import { RecordBoardCardBody } from '@/object-record/record-board/record-board-card/components/RecordBoardCardBody';
import { RecordBoardCardHeader } from '@/object-record/record-board/record-board-card/components/RecordBoardCardHeader';
import { RECORD_BOARD_CARD_CLICK_OUTSIDE_ID } from '@/object-record/record-board/record-board-card/constants/RecordBoardCardClickOutsideId';
+import { RecordBoardCardComponentInstanceContext } from '@/object-record/record-board/record-board-card/states/contexts/RecordBoardCardComponentInstanceContext';
import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext';
import { useOpenRecordFromIndexView } from '@/object-record/record-index/hooks/useOpenRecordFromIndexView';
import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
@@ -226,44 +229,58 @@ export const RecordBoardCard = () => {
);
return (
-
-
-
- {isPrimaryMultiDrag &&
- Array.from({
- length: Math.min(5, multiDragState.originalSelection.length - 1),
- }).map((_, index) => (
-
- ))}
+
+
+
+ {isPrimaryMultiDrag &&
+ Array.from({
+ length: Math.min(
+ 5,
+ multiDragState.originalSelection.length - 1,
+ ),
+ }).map((_, index) => (
+
+ ))}
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
);
};
diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCardBody.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCardBody.tsx
index 9895b61d08..0f7f564133 100644
--- a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCardBody.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCardBody.tsx
@@ -3,6 +3,7 @@ import { RecordBoardCardBodyContainer } from '@/object-record/record-board/recor
import { StopPropagationContainer } from '@/object-record/record-board/record-board-card/components/StopPropagationContainer';
import { RECORD_BOARD_CARD_INPUT_ID_PREFIX } from '@/object-record/record-board/record-board-card/constants/RecordBoardCardInputIdPrefix';
import { RecordBoardCardContext } from '@/object-record/record-board/record-board-card/contexts/RecordBoardCardContext';
+import { recordBoardCardHoverPositionComponentState } from '@/object-record/record-board/record-board-card/states/recordBoardCardHoverPositionComponentState';
import { type RecordBoardFieldDefinition } from '@/object-record/record-board/types/RecordBoardFieldDefinition';
import {
FieldContext,
@@ -15,6 +16,7 @@ import { type FieldMetadata } from '@/object-record/record-field/ui/types/FieldM
import { getFieldButtonIcon } from '@/object-record/record-field/ui/utils/getFieldButtonIcon';
import { RecordInlineCell } from '@/object-record/record-inline-cell/components/RecordInlineCell';
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
+import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
import { useContext } from 'react';
export const RecordBoardCardBody = ({
@@ -53,9 +55,17 @@ export const RecordBoardCardBody = ({
}),
);
+ const setRecordBoardCardHoverPosition = useSetRecoilComponentState(
+ recordBoardCardHoverPositionComponentState,
+ );
+
+ const handleMouseEnter = (index: number) => {
+ setRecordBoardCardHoverPosition(index);
+ };
+
return (
- {fieldDefinitionsWithReadOnly.map((fieldDefinition) => (
+ {fieldDefinitionsWithReadOnly.map((fieldDefinition, index) => (
handleMouseEnter(index),
}}
>
{
+ const { objectMetadataItem } = useContext(RecordBoardContext);
+
+ const hoverPosition = useRecoilComponentValue(
+ recordBoardCardHoverPositionComponentState,
+ );
+
+ const editModePosition = useRecoilComponentValue(
+ recordBoardCardEditModePositionComponentState,
+ );
+
+ const visibleFieldDefinitions = useRecoilComponentValue(
+ recordBoardVisibleFieldDefinitionsComponentSelector,
+ );
+
+ const visibleFieldDefinitionsFiltered = visibleFieldDefinitions.filter(
+ (boardField) => !boardField.isLabelIdentifier,
+ );
+
+ const hoveredFieldDefinition = isDefined(hoverPosition)
+ ? visibleFieldDefinitionsFiltered.at(hoverPosition)
+ : undefined;
+
+ const editedFieldDefinition = isDefined(editModePosition)
+ ? visibleFieldDefinitionsFiltered.at(editModePosition)
+ : undefined;
+
+ const hoveredFieldMetadataItem = isDefined(hoveredFieldDefinition)
+ ? objectMetadataItem.fields.find(
+ (fieldMetadataItem) =>
+ fieldMetadataItem.id === hoveredFieldDefinition.fieldMetadataId,
+ )
+ : undefined;
+
+ const editedFieldMetadataItem = isDefined(editedFieldDefinition)
+ ? objectMetadataItem.fields.find(
+ (fieldMetadataItem) =>
+ fieldMetadataItem.id === editedFieldDefinition.fieldMetadataId,
+ )
+ : undefined;
+
+ return {
+ hoveredFieldMetadataItem,
+ editedFieldMetadataItem,
+ };
+};
diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/states/contexts/RecordBoardCardComponentInstanceContext.ts b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/states/contexts/RecordBoardCardComponentInstanceContext.ts
new file mode 100644
index 0000000000..b78b7b5a42
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/states/contexts/RecordBoardCardComponentInstanceContext.ts
@@ -0,0 +1,4 @@
+import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext';
+
+export const RecordBoardCardComponentInstanceContext =
+ createComponentInstanceContext();
diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/states/recordBoardCardEditModePositionComponentState.ts b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/states/recordBoardCardEditModePositionComponentState.ts
new file mode 100644
index 0000000000..0b13a60e60
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/states/recordBoardCardEditModePositionComponentState.ts
@@ -0,0 +1,9 @@
+import { RecordBoardCardComponentInstanceContext } from '@/object-record/record-board/record-board-card/states/contexts/RecordBoardCardComponentInstanceContext';
+import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
+
+export const recordBoardCardEditModePositionComponentState =
+ createComponentState({
+ key: 'recordBoardCardEditModePositionComponentState',
+ defaultValue: null,
+ componentInstanceContext: RecordBoardCardComponentInstanceContext,
+ });
diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/states/recordBoardCardHoverPositionComponentState.ts b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/states/recordBoardCardHoverPositionComponentState.ts
new file mode 100644
index 0000000000..aff482cc5e
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/states/recordBoardCardHoverPositionComponentState.ts
@@ -0,0 +1,10 @@
+import { RecordBoardCardComponentInstanceContext } from '@/object-record/record-board/record-board-card/states/contexts/RecordBoardCardComponentInstanceContext';
+import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
+
+export const recordBoardCardHoverPositionComponentState = createComponentState<
+ number | null
+>({
+ key: 'recordBoardCardHoverPositionComponentState',
+ defaultValue: null,
+ componentInstanceContext: RecordBoardCardComponentInstanceContext,
+});
diff --git a/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListCellEditModePortal.tsx b/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListCellEditModePortal.tsx
index c27a07aec3..e703b11874 100644
--- a/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListCellEditModePortal.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListCellEditModePortal.tsx
@@ -1,12 +1,13 @@
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
-import { RecordFieldListCellEditModePortalContent } from '@/object-record/record-field-list/anchored-portal/components/RecordFieldListCellEditModePortalContent';
import { RecordFieldListInputContextProvider } from '@/object-record/record-field-list/anchored-portal/components/RecordFieldListInputContextProvider';
import { useFieldListFieldMetadataFromPosition } from '@/object-record/record-field-list/hooks/useFieldListFieldMetadataFromPosition';
import { RecordFieldListComponentInstanceContext } from '@/object-record/record-field-list/states/contexts/RecordFieldListComponentInstanceContext';
import { recordFieldListCellEditModePositionComponentState } from '@/object-record/record-field-list/states/recordFieldListCellEditModePositionComponentState';
+import { FieldInput } from '@/object-record/record-field/ui/components/FieldInput';
import { RecordInlineCellAnchoredPortal } from '@/object-record/record-inline-cell/components/RecordInlineCellAnchoredPortal';
+import { RecordInlineCellEditMode } from '@/object-record/record-inline-cell/components/RecordInlineCellEditMode';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { isDefined } from 'twenty-shared/utils';
@@ -41,10 +42,12 @@ export const RecordFieldListCellEditModePortal = ({
fieldMetadataItem={editedFieldMetadataItem}
objectMetadataItem={objectMetadataItem}
recordId={recordId}
- anchorIdPrefix={instanceId}
+ instanceIdPrefix={instanceId}
>
-
+
+
+
);
diff --git a/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListCellEditModePortalContent.tsx b/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListCellEditModePortalContent.tsx
deleted file mode 100644
index 9cda5b1757..0000000000
--- a/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListCellEditModePortalContent.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { FieldInput } from '@/object-record/record-field/ui/components/FieldInput';
-import { RecordInlineCellEditMode } from '@/object-record/record-inline-cell/components/RecordInlineCellEditMode';
-
-export const RecordFieldListCellEditModePortalContent = () => {
- return (
-
-
-
- );
-};
diff --git a/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListCellHoveredPortal.tsx b/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListCellHoveredPortal.tsx
index 6753140d38..24d35793a2 100644
--- a/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListCellHoveredPortal.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListCellHoveredPortal.tsx
@@ -41,7 +41,7 @@ export const RecordFieldListCellHoveredPortal = ({
fieldMetadataItem={hoveredFieldMetadataItem}
objectMetadataItem={objectMetadataItem}
recordId={recordId}
- anchorIdPrefix={instanceId}
+ instanceIdPrefix={instanceId}
>
diff --git a/packages/twenty-front/src/modules/object-record/record-field-list/components/RecordFieldList.tsx b/packages/twenty-front/src/modules/object-record/record-field-list/components/RecordFieldList.tsx
index 9701d42810..f1d595d831 100644
--- a/packages/twenty-front/src/modules/object-record/record-field-list/components/RecordFieldList.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field-list/components/RecordFieldList.tsx
@@ -111,7 +111,11 @@ export const RecordFieldList = ({
useUpdateRecord: useUpdateOneObjectRecordMutation,
isDisplayModeFixHeight: true,
onMouseEnter: () => handleMouseEnter(index),
- anchorId: `${instanceId}-${index}`,
+ anchorId: `${getRecordFieldInputInstanceId({
+ recordId: objectRecordId,
+ fieldName: fieldMetadataItem.name,
+ prefix: instanceId,
+ })}`,
isRecordFieldReadOnly: isRecordFieldReadOnly({
isRecordReadOnly,
objectPermissions:
@@ -178,9 +182,11 @@ export const RecordFieldList = ({
handleMouseEnter(
index + (inlineRelationFieldMetadataItems?.length ?? 0),
),
- anchorId: `${instanceId}-${
- index + (inlineRelationFieldMetadataItems?.length ?? 0)
- }`,
+ anchorId: `${getRecordFieldInputInstanceId({
+ recordId: objectRecordId,
+ fieldName: fieldMetadataItem.name,
+ prefix: instanceId,
+ })}`,
}}
>
{
onCloseEditMode();
- setIsInlineCellInEditMode(false);
goBackToPreviousDropdownFocusId();
- }, [
- onCloseEditMode,
- setIsInlineCellInEditMode,
- goBackToPreviousDropdownFocusId,
- ]);
+ }, [onCloseEditMode, goBackToPreviousDropdownFocusId]);
const { persistFieldFromFieldInputContext } =
usePersistFieldFromFieldInputContext();
diff --git a/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCellAnchoredPortal.tsx b/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCellAnchoredPortal.tsx
index ec91c71d50..4bda96376b 100644
--- a/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCellAnchoredPortal.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCellAnchoredPortal.tsx
@@ -11,6 +11,7 @@ import { FieldFocusContextProvider } from '@/object-record/record-field/ui/conte
import { useIsRecordFieldReadOnly } from '@/object-record/record-field/ui/hooks/read-only/useIsRecordFieldReadOnly';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
import { RecordInlineCellAnchoredPortalContext } from '@/object-record/record-inline-cell/components/RecordInlineCellAnchoredPortalContext';
+import { RecordInlineCellCloseOnCommandMenuOpeningEffect } from '@/object-record/record-inline-cell/components/RecordInlineCellCloseOnCommandMenuOpeningEffect';
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
import { createPortal } from 'react-dom';
import { isDefined } from 'twenty-shared/utils';
@@ -23,20 +24,25 @@ type RecordInlineCellAnchoredPortalProps = {
>;
objectMetadataItem: ObjectMetadataItem;
recordId: string;
- anchorIdPrefix: string;
+ instanceIdPrefix: string;
children: React.ReactNode;
};
export const RecordInlineCellAnchoredPortal = ({
- position,
fieldMetadataItem,
objectMetadataItem,
recordId,
- anchorIdPrefix,
+ instanceIdPrefix,
children,
}: RecordInlineCellAnchoredPortalProps) => {
+ const fieldInstanceId = getRecordFieldInputInstanceId({
+ recordId,
+ fieldName: fieldMetadataItem.name,
+ prefix: instanceIdPrefix,
+ });
+
const anchorElement = document.body.querySelector(
- `#${anchorIdPrefix}-${position}`,
+ `#${fieldInstanceId}`,
);
const isRecordFieldReadOnly = useIsRecordFieldReadOnly({
@@ -88,16 +94,13 @@ export const RecordInlineCellAnchoredPortal = ({
{createPortal(
{children}
+
,
anchorElement,
)}
diff --git a/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCellContainer.tsx b/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCellContainer.tsx
index 44d3198d1a..1e38d25140 100644
--- a/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCellContainer.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCellContainer.tsx
@@ -9,8 +9,6 @@ import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFi
import { assertFieldMetadata } from '@/object-record/record-field/ui/types/guards/assertFieldMetadata';
import { isFieldText } from '@/object-record/record-field/ui/types/guards/isFieldText';
-import { RecordInlineCellCloseOnCommandMenuOpeningEffect } from '@/object-record/record-inline-cell/components/RecordInlineCellCloseOnCommandMenuOpeningEffect';
-import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell';
import {
AppTooltip,
OverflowingTextWithTooltip,
@@ -88,23 +86,12 @@ export const StyledSkeletonDiv = styled.div`
`;
export const RecordInlineCellContainer = () => {
- const {
- readonly,
- IconLabel,
- label,
- labelWidth,
- showLabel,
- editModeContentOnly,
- } = useRecordInlineCellContext();
-
- const { isInlineCellInEditMode, openInlineCell } = useInlineCell();
+ const { readonly, IconLabel, label, labelWidth, showLabel } =
+ useRecordInlineCellContext();
const { recordId, fieldDefinition, onMouseEnter, onMouseLeave, anchorId } =
useContext(FieldContext);
- const shouldContainerBeClickable =
- !readonly && !editModeContentOnly && !isInlineCellInEditMode;
-
if (isFieldText(fieldDefinition)) {
assertFieldMetadata(FieldMetadataType.TEXT, isFieldText, fieldDefinition);
}
@@ -136,7 +123,6 @@ export const RecordInlineCellContainer = () => {
readonly={readonly ?? false}
onMouseEnter={handleContainerMouseEnter}
onMouseLeave={handleContainerMouseLeave}
- onClick={shouldContainerBeClickable ? openInlineCell : undefined}
>
{(IconLabel || label) && (
@@ -164,9 +150,7 @@ export const RecordInlineCellContainer = () => {
)}
)}
- {isInlineCellInEditMode && (
-
- )}
+
diff --git a/packages/twenty-front/src/modules/object-record/record-inline-cell/hooks/useInlineCell.ts b/packages/twenty-front/src/modules/object-record/record-inline-cell/hooks/useInlineCell.ts
index ed2e4f610e..596324d6f2 100644
--- a/packages/twenty-front/src/modules/object-record/record-inline-cell/hooks/useInlineCell.ts
+++ b/packages/twenty-front/src/modules/object-record/record-inline-cell/hooks/useInlineCell.ts
@@ -1,12 +1,10 @@
import { useContext } from 'react';
-import { useRecoilState } from 'recoil';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { useInitDraftValue } from '@/object-record/record-field/ui/hooks/useInitDraftValue';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
import { useRecordInlineCellContext } from '@/object-record/record-inline-cell/components/RecordInlineCellContext';
-import { isInlineCellInEditModeFamilyState } from '@/object-record/record-inline-cell/states/isInlineCellInEditModeFamilyState';
import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField';
import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId';
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
@@ -22,10 +20,6 @@ export const useInlineCell = (
recordFieldComponentInstanceIdFromProps,
);
- const [isInlineCellInEditMode, setIsInlineCellInEditMode] = useRecoilState(
- isInlineCellInEditModeFamilyState(recordFieldComponentInstanceId),
- );
-
const { onOpenEditMode, onCloseEditMode } = useRecordInlineCellContext();
const { setActiveDropdownFocusIdAndMemorizePrevious } =
@@ -37,14 +31,12 @@ export const useInlineCell = (
const closeInlineCell = () => {
onCloseEditMode?.();
- setIsInlineCellInEditMode(false);
goBackToPreviousDropdownFocusId();
};
const openInlineCell = () => {
onOpenEditMode?.();
- setIsInlineCellInEditMode(true);
initFieldInputDraftValue({
recordId,
fieldDefinition,
@@ -61,7 +53,6 @@ export const useInlineCell = (
};
return {
- isInlineCellInEditMode,
closeInlineCell,
openInlineCell,
};
diff --git a/packages/twenty-front/src/modules/object-record/record-inline-cell/states/isInlineCellInEditModeFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-inline-cell/states/isInlineCellInEditModeFamilyState.ts
deleted file mode 100644
index d605eda213..0000000000
--- a/packages/twenty-front/src/modules/object-record/record-inline-cell/states/isInlineCellInEditModeFamilyState.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { createFamilyState } from '@/ui/utilities/state/utils/createFamilyState';
-
-export const isInlineCellInEditModeFamilyState = createFamilyState<
- boolean,
- string
->({
- key: 'isInlineCellInEditModeScopedState',
- defaultValue: false,
-});
diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/useCreateNewIndexRecord.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/useCreateNewIndexRecord.ts
index 9247139af2..0e9785e3d7 100644
--- a/packages/twenty-front/src/modules/object-record/record-table/hooks/useCreateNewIndexRecord.ts
+++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/useCreateNewIndexRecord.ts
@@ -8,6 +8,7 @@ import { useRecordTitleCell } from '@/object-record/record-title-cell/hooks/useR
import { RecordTitleCellContainerType } from '@/object-record/record-title-cell/types/RecordTitleCellContainerType';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { canOpenObjectInSidePanel } from '@/object-record/utils/canOpenObjectInSidePanel';
+import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
import { AppPath } from '@/types/AppPath';
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
import { useRecoilCallback } from 'recoil';
@@ -15,11 +16,13 @@ import { isDefined } from 'twenty-shared/utils';
import { v4 } from 'uuid';
import { useNavigateApp } from '~/hooks/useNavigateApp';
+type UseCreateNewIndexRecordProps = {
+ objectMetadataItem: ObjectMetadataItem;
+};
+
export const useCreateNewIndexRecord = ({
objectMetadataItem,
-}: {
- objectMetadataItem: ObjectMetadataItem;
-}) => {
+}: UseCreateNewIndexRecordProps) => {
const { openRecordInCommandMenu } = useOpenRecordInCommandMenu();
const { createOneRecord } = useCreateOneRecord({
@@ -67,7 +70,11 @@ export const useCreateNewIndexRecord = ({
openRecordTitleCell({
recordId,
fieldName: labelIdentifierFieldMetadataItem.name,
- containerType: RecordTitleCellContainerType.ShowPage,
+ instanceId: getRecordFieldInputInstanceId({
+ recordId,
+ fieldName: labelIdentifierFieldMetadataItem.name,
+ prefix: RecordTitleCellContainerType.ShowPage,
+ }),
});
}
} else {
diff --git a/packages/twenty-front/src/modules/object-record/record-title-cell/components/RecordTitleCell.tsx b/packages/twenty-front/src/modules/object-record/record-title-cell/components/RecordTitleCell.tsx
index 0c14ee1602..d8be1cd0f5 100644
--- a/packages/twenty-front/src/modules/object-record/record-title-cell/components/RecordTitleCell.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-title-cell/components/RecordTitleCell.tsx
@@ -19,6 +19,7 @@ import {
import { RecordTitleCellFieldDisplay } from '@/object-record/record-title-cell/components/RecordTitleCellFieldDisplay';
import { RecordTitleCellFieldInput } from '@/object-record/record-title-cell/components/RecordTitleCellFieldInput';
import { useRecordTitleCell } from '@/object-record/record-title-cell/hooks/useRecordTitleCell';
+import { RecordTitleCellComponentInstanceContext } from '@/object-record/record-title-cell/states/contexts/RecordTitleCellComponentInstanceContext';
import { type RecordTitleCellContainerType } from '@/object-record/record-title-cell/types/RecordTitleCellContainerType';
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
import { useRecoilCallback } from 'recoil';
@@ -42,12 +43,8 @@ export const RecordTitleCell = ({
const { closeRecordTitleCell } = useRecordTitleCell();
const closeCell = useCallback(() => {
- closeRecordTitleCell({
- recordId,
- fieldName: fieldDefinition.metadata.fieldName,
- containerType,
- });
- }, [closeRecordTitleCell, containerType, fieldDefinition, recordId]);
+ closeRecordTitleCell();
+ }, [closeRecordTitleCell]);
const { persistFieldFromFieldInputContext } =
usePersistFieldFromFieldInputContext();
@@ -132,11 +129,21 @@ export const RecordTitleCell = ({
}),
}}
>
-
-
-
-
-
+
+
+
+
+
+
+
);
};
diff --git a/packages/twenty-front/src/modules/object-record/record-title-cell/components/RecordTitleCellContainer.tsx b/packages/twenty-front/src/modules/object-record/record-title-cell/components/RecordTitleCellContainer.tsx
index c41d0ca76e..05d438c86f 100644
--- a/packages/twenty-front/src/modules/object-record/record-title-cell/components/RecordTitleCellContainer.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-title-cell/components/RecordTitleCellContainer.tsx
@@ -1,5 +1,6 @@
-import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell';
import { RecordTitleCellContext } from '@/object-record/record-title-cell/components/RecordTitleCellContext';
+import { isTitleCellInEditModeComponentState } from '@/object-record/record-title-cell/states/isTitleCellInEditModeComponentState';
+import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useContext } from 'react';
import { isDefined } from 'twenty-shared/utils';
@@ -8,11 +9,13 @@ export const RecordTitleCellContainer = () => {
RecordTitleCellContext,
);
- const { isInlineCellInEditMode } = useInlineCell();
+ const isTitleCellInEditMode = useRecoilComponentValue(
+ isTitleCellInEditModeComponentState,
+ );
if (isDefined(isReadOnly) && isReadOnly) {
return <>{displayModeContent}>;
}
- return <>{isInlineCellInEditMode ? editModeContent : displayModeContent}>;
+ return <>{isTitleCellInEditMode ? editModeContent : displayModeContent}>;
};
diff --git a/packages/twenty-front/src/modules/object-record/record-title-cell/components/RecordTitleCellTextFieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-title-cell/components/RecordTitleCellTextFieldDisplay.tsx
index 9d983b524a..c90c1e49e8 100644
--- a/packages/twenty-front/src/modules/object-record/record-title-cell/components/RecordTitleCellTextFieldDisplay.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-title-cell/components/RecordTitleCellTextFieldDisplay.tsx
@@ -2,6 +2,7 @@ import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldCont
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useRecordTitleCell } from '@/object-record/record-title-cell/hooks/useRecordTitleCell';
import { type RecordTitleCellContainerType } from '@/object-record/record-title-cell/types/RecordTitleCellContainerType';
+import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
import { withTheme, type Theme } from '@emotion/react';
import styled from '@emotion/styled';
import { useContext } from 'react';
@@ -50,7 +51,11 @@ export const RecordTitleCellSingleTextDisplayMode = ({
openRecordTitleCell({
recordId,
fieldName: fieldDefinition.metadata.fieldName,
- containerType,
+ instanceId: getRecordFieldInputInstanceId({
+ recordId,
+ fieldName: fieldDefinition.metadata.fieldName,
+ prefix: containerType,
+ }),
});
}}
>
diff --git a/packages/twenty-front/src/modules/object-record/record-title-cell/hooks/useRecordTitleCell.tsx b/packages/twenty-front/src/modules/object-record/record-title-cell/hooks/useRecordTitleCell.tsx
index 72bbcbded4..95367ecb2e 100644
--- a/packages/twenty-front/src/modules/object-record/record-title-cell/hooks/useRecordTitleCell.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-title-cell/hooks/useRecordTitleCell.tsx
@@ -1,20 +1,30 @@
import { useContextStoreObjectMetadataItem } from '@/context-store/hooks/useContextStoreObjectMetadataItem';
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
import { useInitDraftValue } from '@/object-record/record-field/ui/hooks/useInitDraftValue';
-import { isInlineCellInEditModeFamilyState } from '@/object-record/record-inline-cell/states/isInlineCellInEditModeFamilyState';
-import { type RecordTitleCellContainerType } from '@/object-record/record-title-cell/types/RecordTitleCellContainerType';
-import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
+import { RecordTitleCellComponentInstanceContext } from '@/object-record/record-title-cell/states/contexts/RecordTitleCellComponentInstanceContext';
+import { isTitleCellInEditModeComponentState } from '@/object-record/record-title-cell/states/isTitleCellInEditModeComponentState';
import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId';
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
+import { useAvailableComponentInstanceId } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceId';
import { useRecoilCallback } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
+type OpenTitleCellFunctionParams = {
+ recordId: string;
+ fieldName: string;
+ instanceId?: string;
+};
+
export const useRecordTitleCell = () => {
const { goBackToPreviousDropdownFocusId } =
useGoBackToPreviousDropdownFocusId();
+ const instanceId = useAvailableComponentInstanceId(
+ RecordTitleCellComponentInstanceContext,
+ );
+
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
const { removeFocusItemFromFocusStackById } =
useRemoveFocusItemFromFocusStackById();
@@ -23,37 +33,32 @@ export const useRecordTitleCell = () => {
const closeRecordTitleCell = useRecoilCallback(
({ set }) =>
- ({
- recordId,
- fieldName,
- containerType,
- }: {
- recordId: string;
- fieldName: string;
- containerType: RecordTitleCellContainerType;
- }) => {
+ (instanceIdFromProps?: string) => {
+ const computedInstanceId = instanceIdFromProps ?? instanceId;
+
+ if (!isDefined(computedInstanceId)) {
+ throw new Error(
+ 'Instance ID is not defined in closeRecordTitleCell this should not happen.',
+ );
+ }
set(
- isInlineCellInEditModeFamilyState(
- getRecordFieldInputInstanceId({
- recordId,
- fieldName,
- prefix: containerType,
- }),
- ),
+ isTitleCellInEditModeComponentState.atomFamily({
+ instanceId: computedInstanceId,
+ }),
false,
);
removeFocusItemFromFocusStackById({
- focusId: getRecordFieldInputInstanceId({
- recordId,
- fieldName,
- prefix: containerType,
- }),
+ focusId: computedInstanceId,
});
goBackToPreviousDropdownFocusId();
},
- [goBackToPreviousDropdownFocusId, removeFocusItemFromFocusStackById],
+ [
+ goBackToPreviousDropdownFocusId,
+ instanceId,
+ removeFocusItemFromFocusStackById,
+ ],
);
const initFieldInputDraftValue = useInitDraftValue();
@@ -63,31 +68,27 @@ export const useRecordTitleCell = () => {
({
recordId,
fieldName,
- containerType,
- }: {
- recordId: string;
- fieldName: string;
- containerType: RecordTitleCellContainerType;
- }) => {
+ instanceId: instanceIdFromProps,
+ }: OpenTitleCellFunctionParams) => {
if (!isDefined(objectMetadataItem)) {
throw new Error(
'Cannot find object metadata item in openRecordTitleCell this should not happen.',
);
}
+ const computedInstanceId = instanceIdFromProps ?? instanceId;
+
+ if (!isDefined(computedInstanceId)) {
+ throw new Error(
+ 'Instance ID is not defined in openRecordTitleCell this should not happen.',
+ );
+ }
+
pushFocusItemToFocusStack({
- focusId: getRecordFieldInputInstanceId({
- recordId,
- fieldName,
- prefix: containerType,
- }),
+ focusId: computedInstanceId,
component: {
type: FocusComponentType.OPENED_FIELD_INPUT,
- instanceId: getRecordFieldInputInstanceId({
- recordId,
- fieldName,
- prefix: containerType,
- }),
+ instanceId: computedInstanceId,
},
globalHotkeysConfig: {
enableGlobalHotkeysConflictingWithKeyboard: false,
@@ -95,12 +96,12 @@ export const useRecordTitleCell = () => {
},
});
- const recordTitleCellId = getRecordFieldInputInstanceId({
- recordId,
- fieldName,
- prefix: containerType,
- });
- set(isInlineCellInEditModeFamilyState(recordTitleCellId), true);
+ set(
+ isTitleCellInEditModeComponentState.atomFamily({
+ instanceId: computedInstanceId,
+ }),
+ true,
+ );
const fieldDefinitions = objectMetadataItem.fields.map(
(fieldMetadataItem, index) =>
@@ -124,10 +125,15 @@ export const useRecordTitleCell = () => {
initFieldInputDraftValue({
recordId,
fieldDefinition,
- fieldComponentInstanceId: recordTitleCellId,
+ fieldComponentInstanceId: computedInstanceId,
});
},
- [initFieldInputDraftValue, pushFocusItemToFocusStack, objectMetadataItem],
+ [
+ objectMetadataItem,
+ instanceId,
+ pushFocusItemToFocusStack,
+ initFieldInputDraftValue,
+ ],
);
return {
diff --git a/packages/twenty-front/src/modules/object-record/record-title-cell/states/contexts/RecordTitleCellComponentInstanceContext.ts b/packages/twenty-front/src/modules/object-record/record-title-cell/states/contexts/RecordTitleCellComponentInstanceContext.ts
new file mode 100644
index 0000000000..5bab6c1ac0
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-title-cell/states/contexts/RecordTitleCellComponentInstanceContext.ts
@@ -0,0 +1,4 @@
+import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext';
+
+export const RecordTitleCellComponentInstanceContext =
+ createComponentInstanceContext();
diff --git a/packages/twenty-front/src/modules/object-record/record-title-cell/states/isTitleCellInEditModeComponentState.ts b/packages/twenty-front/src/modules/object-record/record-title-cell/states/isTitleCellInEditModeComponentState.ts
new file mode 100644
index 0000000000..67422b1e94
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-title-cell/states/isTitleCellInEditModeComponentState.ts
@@ -0,0 +1,9 @@
+import { RecordTitleCellComponentInstanceContext } from '@/object-record/record-title-cell/states/contexts/RecordTitleCellComponentInstanceContext';
+import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
+
+export const isTitleCellInEditModeComponentState =
+ createComponentState({
+ key: 'isTitleCellInEditModeComponentState',
+ defaultValue: false,
+ componentInstanceContext: RecordTitleCellComponentInstanceContext,
+ });