Refactor inline-cell inputs (#13914)

Continue the work so that inline-cell do not embed their own input which
is inefficient when we have thousands of inline-cell on the table
This commit is contained in:
Charles Bochet
2025-08-14 11:06:37 +02:00
committed by GitHub
parent 455ce191e4
commit cd0f9aebc0
15 changed files with 279 additions and 218 deletions
@@ -1,118 +0,0 @@
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
import { RecordFieldListCellAnchorPortalContext } from '@/object-record/record-field-list/anchored-portal/components/RecordFieldListCellAnchorPortalContext';
import {
FieldContext,
type RecordUpdateHook,
type RecordUpdateHookParams,
} from '@/object-record/record-field/ui/contexts/FieldContext';
import { useIsRecordFieldReadOnly } from '@/object-record/record-field/ui/hooks/read-only/useIsRecordFieldReadOnly';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
import { RecordInlineCellHoveredPortal } from '@/object-record/record-inline-cell/components/RecordInlineCellHoveredPortal';
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { createPortal } from 'react-dom';
import { isDefined } from 'twenty-shared/utils';
type RecordFieldListCellAnchoredPortalProps = {
position: number;
fieldMetadataItem: Pick<
FieldMetadataItem,
'id' | 'name' | 'type' | 'createdAt' | 'updatedAt' | 'label'
>;
children: React.ReactNode;
};
export const RecordFieldListCellAnchoredPortal = ({
position,
fieldMetadataItem,
children,
}: RecordFieldListCellAnchoredPortalProps) => {
const anchorElement = document.body.querySelector<HTMLAnchorElement>(
`#record-field-list-cell-${position}`,
);
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
const targetedRecordsRuleFromContextStore = useRecoilComponentValue(
contextStoreTargetedRecordsRuleComponentState,
);
let recordId;
if (targetedRecordsRuleFromContextStore.mode === 'selection') {
recordId = targetedRecordsRuleFromContextStore.selectedRecordIds[0];
}
const INPUT_ID_PREFIX = 'fields-card';
const isRecordFieldReadOnly = useIsRecordFieldReadOnly({
fieldMetadataId: fieldMetadataItem.id,
objectMetadataId: objectMetadataItem.id,
recordId: recordId ?? '',
});
const { updateOneRecord } = useUpdateOneRecord({
objectNameSingular: objectMetadataItem.nameSingular,
});
const useUpdateOneObjectRecordMutation: RecordUpdateHook = () => {
const updateEntity = ({ variables }: RecordUpdateHookParams) => {
updateOneRecord?.({
idToUpdate: variables.where.id as string,
updateOneRecordInput: variables.updateOneRecordInput,
});
};
return [updateEntity, { loading: false }];
};
if (!isDefined(anchorElement) || !isDefined(recordId)) {
return null;
}
return (
<FieldContext.Provider
key={recordId + fieldMetadataItem.id}
value={{
recordId,
maxWidth: 200,
isLabelIdentifier: false,
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
field: fieldMetadataItem,
position: 0,
objectMetadataItem,
showLabel: true,
labelWidth: 90,
}),
useUpdateRecord: useUpdateOneObjectRecordMutation,
isDisplayModeFixHeight: true,
isRecordFieldReadOnly,
}}
>
<>
{createPortal(
<RecordFieldComponentInstanceContext.Provider
value={{
instanceId: getRecordFieldInputInstanceId({
recordId,
fieldName: fieldMetadataItem.name,
prefix: INPUT_ID_PREFIX,
}),
}}
>
<RecordFieldListCellAnchorPortalContext>
<RecordInlineCellHoveredPortal>
{children}
</RecordInlineCellHoveredPortal>
</RecordFieldListCellAnchorPortalContext>
</RecordFieldComponentInstanceContext.Provider>,
anchorElement,
)}
</>
</FieldContext.Provider>
);
};
@@ -0,0 +1,45 @@
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 { recordFieldListCellEditModePositionComponentState } from '@/object-record/record-field-list/states/recordFieldListCellEditModePositionComponentState';
import { RecordInlineCellAnchoredPortal } from '@/object-record/record-inline-cell/components/RecordInlineCellAnchoredPortal';
import { isDefined } from 'twenty-shared/utils';
type RecordFieldListCellEditModePortalProps = {
objectMetadataItem: ObjectMetadataItem;
recordId: string;
};
export const RecordFieldListCellEditModePortal = ({
objectMetadataItem,
recordId,
}: RecordFieldListCellEditModePortalProps) => {
const editModePosition = useRecoilComponentValue(
recordFieldListCellEditModePositionComponentState,
);
const { editedFieldMetadataItem } = useFieldListFieldMetadataFromPosition({
objectNameSingular: objectMetadataItem.nameSingular,
});
if (!isDefined(editModePosition) || !isDefined(editedFieldMetadataItem)) {
return null;
}
return (
<RecordInlineCellAnchoredPortal
position={editModePosition}
fieldMetadataItem={editedFieldMetadataItem}
objectMetadataItem={objectMetadataItem}
recordId={recordId}
anchorIdPrefix={'record-field-list-inline-cell'}
>
<RecordFieldListInputContextProvider>
<RecordFieldListCellEditModePortalContent />
</RecordFieldListInputContextProvider>
</RecordInlineCellAnchoredPortal>
);
};
@@ -0,0 +1,10 @@
import { FieldInput } from '@/object-record/record-field/ui/components/FieldInput';
import { RecordInlineCellEditMode } from '@/object-record/record-inline-cell/components/RecordInlineCellEditMode';
export const RecordFieldListCellEditModePortalContent = () => {
return (
<RecordInlineCellEditMode>
<FieldInput />
</RecordInlineCellEditMode>
);
};
@@ -1,18 +1,21 @@
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { RecordFieldListCellAnchoredPortal } from '@/object-record/record-field-list/anchored-portal/components/RecordFieldListCellAnchoredPortal';
import { RecordFieldListCellHoveredPortalContent } from '@/object-record/record-field-list/anchored-portal/components/RecordFieldListCellHoveredPortalContent';
import { RecordFieldListInputContextProvider } from '@/object-record/record-field-list/anchored-portal/components/RecordFieldListInputContextProvider';
import { useFieldListFieldMetadataFromPosition } from '@/object-record/record-field-list/hooks/useFieldListFieldMetadataFromPosition';
import { recordFieldListHoverPositionComponentState } from '@/object-record/record-field-list/states/recordFieldListHoverPositionComponentState';
import { RecordInlineCellAnchoredPortal } from '@/object-record/record-inline-cell/components/RecordInlineCellAnchoredPortal';
import { isDefined } from 'twenty-shared/utils';
type RecordFieldListCellHoveredPortalProps = {
objectMetadataItem: ObjectMetadataItem;
recordId: string;
};
export const RecordFieldListCellHoveredPortal = ({
objectMetadataItem,
recordId,
}: RecordFieldListCellHoveredPortalProps) => {
const hoverPosition = useRecoilComponentValue(
recordFieldListHoverPositionComponentState,
@@ -27,13 +30,16 @@ export const RecordFieldListCellHoveredPortal = ({
}
return (
<RecordFieldListCellAnchoredPortal
<RecordInlineCellAnchoredPortal
position={hoverPosition}
fieldMetadataItem={hoveredFieldMetadataItem}
objectMetadataItem={objectMetadataItem}
recordId={recordId}
anchorIdPrefix={'record-field-list-inline-cell'}
>
<RecordFieldListCellHoveredPortalContent
objectMetadataItem={objectMetadataItem}
/>
</RecordFieldListCellAnchoredPortal>
<RecordFieldListInputContextProvider>
<RecordFieldListCellHoveredPortalContent />
</RecordFieldListInputContextProvider>
</RecordInlineCellAnchoredPortal>
);
};
@@ -1,63 +1,60 @@
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useFieldListFieldMetadataFromPosition } from '@/object-record/record-field-list/hooks/useFieldListFieldMetadataFromPosition';
import { recordFieldListCellEditModePositionComponentState } from '@/object-record/record-field-list/states/recordFieldListCellEditModePositionComponentState';
import { recordFieldListHoverPositionComponentState } from '@/object-record/record-field-list/states/recordFieldListHoverPositionComponentState';
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 { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
import { useContext } from 'react';
import { isDefined } from 'twenty-shared/utils';
type RecordFieldListCellHoveredPortalContentProps = {
objectMetadataItem: ObjectMetadataItem;
};
export const RecordFieldListCellHoveredPortalContent = ({
objectMetadataItem,
}: RecordFieldListCellHoveredPortalContentProps) => {
const hoverPosition = useRecoilComponentValue(
recordFieldListHoverPositionComponentState,
);
export const RecordFieldListCellHoveredPortalContent = () => {
const { editModeContentOnly, isCentered } = useRecordInlineCellContext();
const { hoveredFieldMetadataItem } = useFieldListFieldMetadataFromPosition({
objectNameSingular: objectMetadataItem.nameSingular,
});
const { isRecordFieldReadOnly } = useContext(FieldContext);
const { isRecordFieldReadOnly, recordId, fieldDefinition } =
useContext(FieldContext);
const { openInlineCell } = useInlineCell();
const shouldContainerBeClickable =
!isRecordFieldReadOnly && !editModeContentOnly;
const setRecordFieldListHoverPosition = useSetRecoilComponentState(
recordFieldListHoverPositionComponentState,
'fields-card',
const [recordFieldListHoverPosition, setRecordFieldListHoverPosition] =
useRecoilComponentState(recordFieldListHoverPositionComponentState);
const setRecordFieldListCellEditModePosition = useSetRecoilComponentState(
recordFieldListCellEditModePositionComponentState,
);
const { openFieldInput } = useOpenFieldInputEditMode();
const handleClick = () => {
if (shouldContainerBeClickable) {
openInlineCell();
setRecordFieldListCellEditModePosition(recordFieldListHoverPosition);
openFieldInput({
fieldDefinition,
recordId,
prefix: 'inline-cell',
});
}
};
const handleMouseLeave = () => {
setRecordFieldListHoverPosition(null);
};
if (!isDefined(hoverPosition) || !isDefined(hoveredFieldMetadataItem)) {
return null;
}
return (
<RecordInlineCellHoveredPortalContent
readonly={isRecordFieldReadOnly}
isCentered={isCentered}
onClick={shouldContainerBeClickable ? openInlineCell : undefined}
onClick={handleClick}
onMouseLeave={handleMouseLeave}
>
<RecordInlineCellDisplayMode>
<RecordInlineCellDisplayMode isHovered={true}>
{editModeContentOnly ? <FieldInput /> : <FieldDisplay />}
</RecordInlineCellDisplayMode>
</RecordInlineCellHoveredPortalContent>
@@ -1,20 +1,35 @@
import { FieldInput } from '@/object-record/record-field/ui/components/FieldInput';
import { usePersistFieldFromFieldInputContext } from '@/object-record/record-field/ui/hooks/usePersistFieldFromFieldInputContext';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
import { recordFieldListCellEditModePositionComponentState } from '@/object-record/record-field-list/states/recordFieldListCellEditModePositionComponentState';
import {
FieldInputEventContext,
type FieldInputClickOutsideEvent,
type FieldInputEvent,
} from '@/object-record/record-field/ui/contexts/FieldInputEventContext';
import { useRecordTableBodyContextOrThrow } from '@/object-record/record-table/contexts/RecordTableBodyContext';
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';
export const RecordTableCellFieldInput = () => {
const { onMoveFocus, onCloseTableCell } = useRecordTableBodyContextOrThrow();
type RecordFieldListInputContextProviderProps = {
children: React.ReactNode;
};
export const RecordFieldListInputContextProvider = ({
children,
}: RecordFieldListInputContextProviderProps) => {
const { closeInlineCell } = useInlineCell();
const setRecordFieldListCellEditModePosition = useSetRecoilComponentState(
recordFieldListCellEditModePositionComponentState,
);
const closeInlineCellAndResetEditModePosition = useCallback(() => {
setRecordFieldListCellEditModePosition(null);
closeInlineCell();
}, [closeInlineCell, setRecordFieldListCellEditModePosition]);
const instanceId = useAvailableComponentInstanceId(
RecordFieldComponentInstanceContext,
@@ -28,8 +43,7 @@ export const RecordTableCellFieldInput = () => {
persistFieldFromFieldInputContext(newValue);
}
onCloseTableCell();
onMoveFocus('down');
closeInlineCellAndResetEditModePosition();
};
const handleSubmit: FieldInputEvent = ({ newValue, skipPersist }) => {
@@ -37,11 +51,11 @@ export const RecordTableCellFieldInput = () => {
persistFieldFromFieldInputContext(newValue);
}
onCloseTableCell();
closeInlineCellAndResetEditModePosition();
};
const handleCancel = () => {
onCloseTableCell();
closeInlineCellAndResetEditModePosition();
};
const handleClickOutside: FieldInputClickOutsideEvent = useRecoilCallback(
@@ -61,9 +75,13 @@ export const RecordTableCellFieldInput = () => {
persistFieldFromFieldInputContext(newValue);
}
onCloseTableCell();
closeInlineCellAndResetEditModePosition();
},
[onCloseTableCell, instanceId, persistFieldFromFieldInputContext],
[
closeInlineCellAndResetEditModePosition,
instanceId,
persistFieldFromFieldInputContext,
],
);
const handleEscape: FieldInputEvent = ({ newValue, skipPersist }) => {
@@ -71,7 +89,7 @@ export const RecordTableCellFieldInput = () => {
persistFieldFromFieldInputContext(newValue);
}
onCloseTableCell();
closeInlineCellAndResetEditModePosition();
};
const handleTab: FieldInputEvent = ({ newValue, skipPersist }) => {
@@ -79,8 +97,7 @@ export const RecordTableCellFieldInput = () => {
persistFieldFromFieldInputContext(newValue);
}
onCloseTableCell();
onMoveFocus('right');
closeInlineCellAndResetEditModePosition();
};
const handleShiftTab: FieldInputEvent = ({ newValue, skipPersist }) => {
@@ -88,8 +105,7 @@ export const RecordTableCellFieldInput = () => {
persistFieldFromFieldInputContext(newValue);
}
onCloseTableCell();
onMoveFocus('left');
closeInlineCellAndResetEditModePosition();
};
return (
@@ -104,7 +120,7 @@ export const RecordTableCellFieldInput = () => {
onTab: handleTab,
}}
>
<FieldInput />
{children}
</FieldInputEventContext.Provider>
);
};
@@ -0,0 +1,108 @@
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
import {
FieldContext,
type RecordUpdateHook,
type RecordUpdateHookParams,
} from '@/object-record/record-field/ui/contexts/FieldContext';
import { FieldFocusContextProvider } from '@/object-record/record-field/ui/contexts/FieldFocusContextProvider';
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 { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
import { createPortal } from 'react-dom';
import { isDefined } from 'twenty-shared/utils';
type RecordInlineCellAnchoredPortalProps = {
position: number;
fieldMetadataItem: Pick<
FieldMetadataItem,
'id' | 'name' | 'type' | 'createdAt' | 'updatedAt' | 'label'
>;
objectMetadataItem: ObjectMetadataItem;
recordId: string;
anchorIdPrefix: string;
children: React.ReactNode;
};
export const RecordInlineCellAnchoredPortal = ({
position,
fieldMetadataItem,
objectMetadataItem,
recordId,
anchorIdPrefix,
children,
}: RecordInlineCellAnchoredPortalProps) => {
const anchorElement = document.body.querySelector<HTMLAnchorElement>(
`#${anchorIdPrefix}-${position}`,
);
const isRecordFieldReadOnly = useIsRecordFieldReadOnly({
fieldMetadataId: fieldMetadataItem.id,
objectMetadataId: objectMetadataItem.id,
recordId: recordId ?? '',
});
const { updateOneRecord } = useUpdateOneRecord({
objectNameSingular: objectMetadataItem.nameSingular,
});
const useUpdateOneObjectRecordMutation: RecordUpdateHook = () => {
const updateEntity = ({ variables }: RecordUpdateHookParams) => {
updateOneRecord?.({
idToUpdate: variables.where.id as string,
updateOneRecordInput: variables.updateOneRecordInput,
});
};
return [updateEntity, { loading: false }];
};
if (!isDefined(anchorElement) || !isDefined(recordId)) {
return null;
}
return (
<FieldFocusContextProvider isFocused={true}>
<FieldContext.Provider
key={recordId + fieldMetadataItem.id}
value={{
recordId,
maxWidth: 200,
isLabelIdentifier: false,
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
field: fieldMetadataItem,
position: 0,
objectMetadataItem,
showLabel: true,
labelWidth: 90,
}),
useUpdateRecord: useUpdateOneObjectRecordMutation,
isDisplayModeFixHeight: true,
isRecordFieldReadOnly,
}}
>
<>
{createPortal(
<RecordFieldComponentInstanceContext.Provider
value={{
instanceId: getRecordFieldInputInstanceId({
recordId,
fieldName: fieldMetadataItem.name,
prefix: 'inline-cell',
}),
}}
>
<RecordInlineCellAnchoredPortalContext>
{children}
</RecordInlineCellAnchoredPortalContext>
</RecordFieldComponentInstanceContext.Provider>,
anchorElement,
)}
</>
</FieldContext.Provider>
</FieldFocusContextProvider>
);
};
@@ -10,13 +10,13 @@ import {
import { useContext, type ReactNode } from 'react';
import { useIcons } from 'twenty-ui/display';
type RecordFieldListCellAnchorPortalContextProps = {
type RecordInlineCellAnchoredPortalContextProps = {
children: ReactNode;
};
export const RecordFieldListCellAnchorPortalContext = ({
export const RecordInlineCellAnchoredPortalContext = ({
children,
}: RecordFieldListCellAnchorPortalContextProps) => {
}: RecordInlineCellAnchoredPortalContextProps) => {
const {
isRecordFieldReadOnly,
fieldDefinition,
@@ -47,12 +47,12 @@ const StyledValueContainer = styled.div<{ readonly: boolean }>`
display: flex;
min-width: 0;
position: relative;
width: 100%;
&:hover {
${({ readonly, theme }) =>
readonly &&
`
outline: 1px solid ${theme.border.color.medium};
border-radius: ${theme.border.radius.sm};
${StyledIconContainer}, ${StyledLabelContainer} {
@@ -1,7 +1,6 @@
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { useFieldFocus } from '@/object-record/record-field/ui/hooks/useFieldFocus';
import { useIsFieldEmpty } from '@/object-record/record-field/ui/hooks/useIsFieldEmpty';
import { useIsFieldInputOnly } from '@/object-record/record-field/ui/hooks/useIsFieldInputOnly';
import {
@@ -17,6 +16,9 @@ const StyledRecordInlineCellNormalModeOuterContainer = styled.div<
'isDisplayModeFixHeight' | 'disableHoverEffect'
> & { isHovered?: boolean }
>`
outline: 1px solid
${({ theme, isHovered }) =>
isHovered ? theme.border.color.medium : 'transparent'};
align-items: center;
border-radius: ${({ theme }) => theme.border.radius.sm};
display: flex;
@@ -54,17 +56,16 @@ const StyledRecordInlineCellNormalModeInnerContainer = styled.div`
`;
const StyledEmptyField = styled.div`
color: ${({ theme }) => theme.font.color.light};
height: 20px;
display: flex;
align-items: center;
color: ${({ theme }) => theme.font.color.light};
display: flex;
height: 20px;
`;
export const RecordInlineCellDisplayMode = ({
children,
}: React.PropsWithChildren<unknown>) => {
const { isFocused } = useFieldFocus();
isHovered,
}: React.PropsWithChildren<{ isHovered: boolean }>) => {
const { t } = useLingui();
const { editModeContentOnly, showLabel, label, buttonIcon } =
@@ -73,19 +74,19 @@ export const RecordInlineCellDisplayMode = ({
const isDisplayModeContentEmpty = useIsFieldEmpty();
const showEditButton =
buttonIcon &&
isFocused &&
isHovered &&
!isDisplayModeContentEmpty &&
!editModeContentOnly;
const isFieldInputOnly = useIsFieldInputOnly();
const shouldDisplayEditModeOnFocus = isFocused && isFieldInputOnly;
const shouldDisplayEditModeOnFocus = isHovered && isFieldInputOnly;
const emptyPlaceHolder = showLabel ? t`Empty` : label;
return (
<>
<StyledRecordInlineCellNormalModeOuterContainer isHovered={isFocused}>
<StyledRecordInlineCellNormalModeOuterContainer isHovered={isHovered}>
<StyledRecordInlineCellNormalModeInnerContainer>
{(isDisplayModeContentEmpty && !shouldDisplayEditModeOnFocus) ||
!children ? (
@@ -1,7 +1,7 @@
import styled from '@emotion/styled';
import { AnimatedContainer } from 'twenty-ui/utilities';
import { FloatingIconButton } from 'twenty-ui/input';
import { type IconComponent } from 'twenty-ui/display';
import { FloatingIconButton } from 'twenty-ui/input';
import { AnimatedContainer } from 'twenty-ui/utilities';
const StyledInlineCellButtonContainer = styled.div`
align-items: center;
@@ -1,3 +1,4 @@
import { RecordInlineCellHoveredPortal } from '@/object-record/record-inline-cell/components/RecordInlineCellHoveredPortal';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
@@ -50,17 +51,19 @@ export const RecordInlineCellHoveredPortalContent = ({
onMouseLeave,
}: RecordInlineCellHoveredPortalContentProps) => {
return (
<StyledInlineCellBaseContainer
readonly={readonly}
onClick={onClick}
onMouseLeave={onMouseLeave}
>
<StyledRecordTableCellHoveredPortalContent
isCentered={isCentered}
<RecordInlineCellHoveredPortal>
<StyledInlineCellBaseContainer
readonly={readonly}
onClick={onClick}
onMouseLeave={onMouseLeave}
>
{children}
</StyledRecordTableCellHoveredPortalContent>
</StyledInlineCellBaseContainer>
<StyledRecordTableCellHoveredPortalContent
isCentered={isCentered}
readonly={readonly}
>
{children}
</StyledRecordTableCellHoveredPortalContent>
</StyledInlineCellBaseContainer>
</RecordInlineCellHoveredPortal>
);
};
@@ -1,11 +1,10 @@
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { FieldDisplay } from '@/object-record/record-field/ui/components/FieldDisplay';
import { useRecordInlineCellContext } from '@/object-record/record-inline-cell/components/RecordInlineCellContext';
import { RecordInlineCellDisplayMode } from '@/object-record/record-inline-cell/components/RecordInlineCellDisplayMode';
import { RecordInlineCellEditMode } from '@/object-record/record-inline-cell/components/RecordInlineCellEditMode';
import { RecordInlineCellSkeletonLoader } from '@/object-record/record-inline-cell/components/RecordInlineCellSkeletonLoader';
import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell';
const StyledClickableContainer = styled.div<{
readonly?: boolean;
@@ -30,16 +29,7 @@ const StyledClickableContainer = styled.div<{
`;
export const RecordInlineCellValue = () => {
const {
displayModeContent,
editModeContent,
editModeContentOnly,
readonly,
loading,
isCentered,
} = useRecordInlineCellContext();
const { isInlineCellInEditMode } = useInlineCell();
const { readonly, loading, isCentered } = useRecordInlineCellContext();
if (loading === true) {
return <RecordInlineCellSkeletonLoader />;
@@ -47,12 +37,9 @@ export const RecordInlineCellValue = () => {
return (
<>
{!readonly && isInlineCellInEditMode && (
<RecordInlineCellEditMode>{editModeContent}</RecordInlineCellEditMode>
)}
<StyledClickableContainer readonly={readonly} isCentered={isCentered}>
<RecordInlineCellDisplayMode>
{editModeContentOnly ? editModeContent : displayModeContent}
<RecordInlineCellDisplayMode isHovered={false}>
<FieldDisplay />
</RecordInlineCellDisplayMode>
</StyledClickableContainer>
</>
@@ -6,11 +6,11 @@ import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldCont
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';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { isInlineCellInEditModeFamilyState } from '../states/isInlineCellInEditModeFamilyState';
export const useInlineCell = (
recordFieldComponentInstanceIdFromProps?: string,
@@ -3,6 +3,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
import { type CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
import { RecordFieldListCellEditModePortal } from '@/object-record/record-field-list/anchored-portal/components/RecordFieldListCellEditModePortal';
import { RecordFieldListCellHoveredPortal } from '@/object-record/record-field-list/anchored-portal/components/RecordFieldListCellHoveredPortal';
import { useFieldListFieldMetadataItems } from '@/object-record/record-field-list/hooks/useFieldListFieldMetadataItems';
import { RecordFieldListComponentInstanceContext } from '@/object-record/record-field-list/states/contexts/RecordFieldListComponentInstanceContext';
@@ -60,7 +61,7 @@ export const FieldsCard = ({
const setRecordFieldListHoverPosition = useSetRecoilComponentState(
recordFieldListHoverPositionComponentState,
'fields-card',
`fields-list-${objectRecordId}`,
);
const handleMouseEnter = (index: number) => {
@@ -78,7 +79,7 @@ export const FieldsCard = ({
return (
<RecordFieldListComponentInstanceContext.Provider
value={{
instanceId: 'fields-card',
instanceId: `fields-list-${objectRecordId}`,
}}
>
<PropertyBox>
@@ -171,7 +172,7 @@ export const FieldsCard = ({
handleMouseEnter(
index + (inlineRelationFieldMetadataItems?.length ?? 0),
),
anchorId: `record-field-list-cell-${
anchorId: `record-field-list-inline-cell-${
index + (inlineRelationFieldMetadataItems?.length ?? 0)
}`,
}}
@@ -236,6 +237,11 @@ export const FieldsCard = ({
<RecordFieldListCellHoveredPortal
objectMetadataItem={objectMetadataItem}
recordId={objectRecordId}
/>
<RecordFieldListCellEditModePortal
objectMetadataItem={objectMetadataItem}
recordId={objectRecordId}
/>
</RecordFieldListComponentInstanceContext.Provider>
);