Refactor board inline-cell input (#13928)
Makes board use the Input portals for Inline Cells
This commit is contained in:
+6
-1
@@ -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,
|
||||
}),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+44
@@ -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 (
|
||||
<RecordInlineCellAnchoredPortal
|
||||
position={editModePosition}
|
||||
fieldMetadataItem={editedFieldMetadataItem}
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
recordId={recordId}
|
||||
instanceIdPrefix={RECORD_BOARD_CARD_INPUT_ID_PREFIX}
|
||||
>
|
||||
<RecordBoardCardInputContextProvider>
|
||||
<RecordInlineCellEditMode>
|
||||
<FieldInput />
|
||||
</RecordInlineCellEditMode>
|
||||
</RecordBoardCardInputContextProvider>
|
||||
</RecordInlineCellAnchoredPortal>
|
||||
);
|
||||
};
|
||||
+41
@@ -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 (
|
||||
<RecordInlineCellAnchoredPortal
|
||||
position={hoverPosition}
|
||||
fieldMetadataItem={hoveredFieldMetadataItem}
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
recordId={recordId}
|
||||
instanceIdPrefix={RECORD_BOARD_CARD_INPUT_ID_PREFIX}
|
||||
>
|
||||
<RecordBoardCardInputContextProvider>
|
||||
<RecordBoardCardCellHoveredPortalContent />
|
||||
</RecordBoardCardInputContextProvider>
|
||||
</RecordInlineCellAnchoredPortal>
|
||||
);
|
||||
};
|
||||
+70
@@ -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 (
|
||||
<RecordInlineCellHoveredPortalContent
|
||||
readonly={isRecordFieldReadOnly}
|
||||
isCentered={isCentered}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<RecordInlineCellDisplayMode isHovered={true} onClick={handleClick}>
|
||||
{editModeContentOnly ? <FieldInput /> : <FieldDisplay />}
|
||||
</RecordInlineCellDisplayMode>
|
||||
</RecordInlineCellHoveredPortalContent>
|
||||
);
|
||||
};
|
||||
+126
@@ -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 (
|
||||
<FieldInputEventContext.Provider
|
||||
value={{
|
||||
onCancel: handleCancel,
|
||||
onEnter: handleEnter,
|
||||
onEscape: handleEscape,
|
||||
onClickOutside: handleClickOutside,
|
||||
onShiftTab: handleShiftTab,
|
||||
onSubmit: handleSubmit,
|
||||
onTab: handleTab,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</FieldInputEventContext.Provider>
|
||||
);
|
||||
};
|
||||
+52
-35
@@ -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 (
|
||||
<StyledBoardCardWrapper
|
||||
data-click-outside-id={RECORD_BOARD_CARD_CLICK_OUTSIDE_ID}
|
||||
onContextMenu={handleContextMenuOpen}
|
||||
<RecordBoardCardComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: `record-board-card-${recordId}`,
|
||||
}}
|
||||
>
|
||||
<InView>
|
||||
<StyledCardContainer isPrimaryMultiDrag={isPrimaryMultiDrag}>
|
||||
{isPrimaryMultiDrag &&
|
||||
Array.from({
|
||||
length: Math.min(5, multiDragState.originalSelection.length - 1),
|
||||
}).map((_, index) => (
|
||||
<StyledRecordBoardCardStackCard key={index} offset={index + 1} />
|
||||
))}
|
||||
<StyledBoardCardWrapper
|
||||
data-click-outside-id={RECORD_BOARD_CARD_CLICK_OUTSIDE_ID}
|
||||
onContextMenu={handleContextMenuOpen}
|
||||
>
|
||||
<InView>
|
||||
<StyledCardContainer isPrimaryMultiDrag={isPrimaryMultiDrag}>
|
||||
{isPrimaryMultiDrag &&
|
||||
Array.from({
|
||||
length: Math.min(
|
||||
5,
|
||||
multiDragState.originalSelection.length - 1,
|
||||
),
|
||||
}).map((_, index) => (
|
||||
<StyledRecordBoardCardStackCard
|
||||
key={index}
|
||||
offset={index + 1}
|
||||
/>
|
||||
))}
|
||||
|
||||
<StyledBoardCard
|
||||
ref={cardRef}
|
||||
data-selected={isCurrentCardSelected}
|
||||
data-focused={isCurrentCardFocused}
|
||||
data-active={isCurrentCardActive}
|
||||
onMouseLeave={onMouseLeaveBoard}
|
||||
onClick={handleCardClick}
|
||||
isPrimaryMultiDrag={isPrimaryMultiDrag}
|
||||
isSecondaryDragged={isSecondaryDragged}
|
||||
>
|
||||
<RecordBoardCardHeader
|
||||
isCardExpanded={isCardExpanded}
|
||||
setIsCardExpanded={setIsCardExpanded}
|
||||
/>
|
||||
<AnimatedEaseInOut
|
||||
isOpen={isCardExpanded || !isCompactModeActive}
|
||||
initial={false}
|
||||
<StyledBoardCard
|
||||
ref={cardRef}
|
||||
data-selected={isCurrentCardSelected}
|
||||
data-focused={isCurrentCardFocused}
|
||||
data-active={isCurrentCardActive}
|
||||
onMouseLeave={onMouseLeaveBoard}
|
||||
onClick={handleCardClick}
|
||||
isPrimaryMultiDrag={isPrimaryMultiDrag}
|
||||
isSecondaryDragged={isSecondaryDragged}
|
||||
>
|
||||
<RecordBoardCardBody
|
||||
fieldDefinitions={visibleFieldDefinitionsFiltered}
|
||||
<RecordBoardCardHeader
|
||||
isCardExpanded={isCardExpanded}
|
||||
setIsCardExpanded={setIsCardExpanded}
|
||||
/>
|
||||
</AnimatedEaseInOut>
|
||||
</StyledBoardCard>
|
||||
</StyledCardContainer>
|
||||
</InView>
|
||||
</StyledBoardCardWrapper>
|
||||
<AnimatedEaseInOut
|
||||
isOpen={isCardExpanded || !isCompactModeActive}
|
||||
initial={false}
|
||||
>
|
||||
<RecordBoardCardBody
|
||||
fieldDefinitions={visibleFieldDefinitionsFiltered}
|
||||
/>
|
||||
</AnimatedEaseInOut>
|
||||
</StyledBoardCard>
|
||||
</StyledCardContainer>
|
||||
<RecordBoardCardCellHoveredPortal />
|
||||
<RecordBoardCardCellEditModePortal />
|
||||
</InView>
|
||||
</StyledBoardCardWrapper>
|
||||
</RecordBoardCardComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
+13
-1
@@ -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 (
|
||||
<RecordBoardCardBodyContainer>
|
||||
{fieldDefinitionsWithReadOnly.map((fieldDefinition) => (
|
||||
{fieldDefinitionsWithReadOnly.map((fieldDefinition, index) => (
|
||||
<StopPropagationContainer key={fieldDefinition.fieldMetadataId}>
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
@@ -79,6 +89,8 @@ export const RecordBoardCardBody = ({
|
||||
useUpdateRecord: useUpdateOneRecordHook,
|
||||
isDisplayModeFixHeight: true,
|
||||
triggerEvent: 'CLICK',
|
||||
anchorId: `${RECORD_BOARD_CARD_INPUT_ID_PREFIX}-${recordId}-${fieldDefinition.metadata.fieldName}`,
|
||||
onMouseEnter: () => handleMouseEnter(index),
|
||||
}}
|
||||
>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
||||
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 { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useContext } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useRecordBoardCardMetadataFromPosition = () => {
|
||||
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,
|
||||
};
|
||||
};
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext';
|
||||
|
||||
export const RecordBoardCardComponentInstanceContext =
|
||||
createComponentInstanceContext();
|
||||
+9
@@ -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<number | null>({
|
||||
key: 'recordBoardCardEditModePositionComponentState',
|
||||
defaultValue: null,
|
||||
componentInstanceContext: RecordBoardCardComponentInstanceContext,
|
||||
});
|
||||
+10
@@ -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,
|
||||
});
|
||||
+6
-3
@@ -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}
|
||||
>
|
||||
<RecordFieldListInputContextProvider>
|
||||
<RecordFieldListCellEditModePortalContent />
|
||||
<RecordInlineCellEditMode>
|
||||
<FieldInput />
|
||||
</RecordInlineCellEditMode>
|
||||
</RecordFieldListInputContextProvider>
|
||||
</RecordInlineCellAnchoredPortal>
|
||||
);
|
||||
|
||||
-10
@@ -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 (
|
||||
<RecordInlineCellEditMode>
|
||||
<FieldInput />
|
||||
</RecordInlineCellEditMode>
|
||||
);
|
||||
};
|
||||
+1
-1
@@ -41,7 +41,7 @@ export const RecordFieldListCellHoveredPortal = ({
|
||||
fieldMetadataItem={hoveredFieldMetadataItem}
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
recordId={recordId}
|
||||
anchorIdPrefix={instanceId}
|
||||
instanceIdPrefix={instanceId}
|
||||
>
|
||||
<RecordFieldListInputContextProvider>
|
||||
<RecordFieldListCellHoveredPortalContent />
|
||||
|
||||
+10
-4
@@ -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,
|
||||
})}`,
|
||||
}}
|
||||
>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
|
||||
+2
-18
@@ -15,13 +15,10 @@ import {
|
||||
type FieldInputEvent,
|
||||
} from '@/object-record/record-field/ui/contexts/FieldInputEventContext';
|
||||
import { usePersistFieldFromFieldInputContext } from '@/object-record/record-field/ui/hooks/usePersistFieldFromFieldInputContext';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
|
||||
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 { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
import { RecordInlineCellContainer } from './RecordInlineCellContainer';
|
||||
import {
|
||||
@@ -82,23 +79,10 @@ export const RecordInlineCell = ({
|
||||
const { goBackToPreviousDropdownFocusId } =
|
||||
useGoBackToPreviousDropdownFocusId();
|
||||
|
||||
const recordFieldComponentInstanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
const setIsInlineCellInEditMode = useSetRecoilState(
|
||||
isInlineCellInEditModeFamilyState(recordFieldComponentInstanceId),
|
||||
);
|
||||
|
||||
const closeInlineCell = useCallback(() => {
|
||||
onCloseEditMode();
|
||||
setIsInlineCellInEditMode(false);
|
||||
goBackToPreviousDropdownFocusId();
|
||||
}, [
|
||||
onCloseEditMode,
|
||||
setIsInlineCellInEditMode,
|
||||
goBackToPreviousDropdownFocusId,
|
||||
]);
|
||||
}, [onCloseEditMode, goBackToPreviousDropdownFocusId]);
|
||||
|
||||
const { persistFieldFromFieldInputContext } =
|
||||
usePersistFieldFromFieldInputContext();
|
||||
|
||||
+12
-9
@@ -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<HTMLAnchorElement>(
|
||||
`#${anchorIdPrefix}-${position}`,
|
||||
`#${fieldInstanceId}`,
|
||||
);
|
||||
|
||||
const isRecordFieldReadOnly = useIsRecordFieldReadOnly({
|
||||
@@ -88,16 +94,13 @@ export const RecordInlineCellAnchoredPortal = ({
|
||||
{createPortal(
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: fieldMetadataItem.name,
|
||||
prefix: anchorIdPrefix,
|
||||
}),
|
||||
instanceId: fieldInstanceId,
|
||||
}}
|
||||
>
|
||||
<RecordInlineCellAnchoredPortalContext>
|
||||
{children}
|
||||
</RecordInlineCellAnchoredPortalContext>
|
||||
<RecordInlineCellCloseOnCommandMenuOpeningEffect />
|
||||
</RecordFieldComponentInstanceContext.Provider>,
|
||||
anchorElement,
|
||||
)}
|
||||
|
||||
+3
-19
@@ -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) && (
|
||||
<StyledLabelAndIconContainer id={labelId}>
|
||||
@@ -164,9 +150,7 @@ export const RecordInlineCellContainer = () => {
|
||||
)}
|
||||
</StyledLabelAndIconContainer>
|
||||
)}
|
||||
{isInlineCellInEditMode && (
|
||||
<RecordInlineCellCloseOnCommandMenuOpeningEffect />
|
||||
)}
|
||||
|
||||
<StyledValueContainer readonly={readonly ?? false} id={anchorId}>
|
||||
<RecordInlineCellValue />
|
||||
</StyledValueContainer>
|
||||
|
||||
-9
@@ -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,
|
||||
};
|
||||
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
import { createFamilyState } from '@/ui/utilities/state/utils/createFamilyState';
|
||||
|
||||
export const isInlineCellInEditModeFamilyState = createFamilyState<
|
||||
boolean,
|
||||
string
|
||||
>({
|
||||
key: 'isInlineCellInEditModeScopedState',
|
||||
defaultValue: false,
|
||||
});
|
||||
+11
-4
@@ -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 {
|
||||
|
||||
+18
-11
@@ -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 = ({
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<FieldFocusContextProvider>
|
||||
<RecordTitleCellContext.Provider value={recordTitleCellContextValue}>
|
||||
<RecordTitleCellContainer />
|
||||
</RecordTitleCellContext.Provider>
|
||||
</FieldFocusContextProvider>
|
||||
<RecordTitleCellComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
prefix: containerType,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<FieldFocusContextProvider>
|
||||
<RecordTitleCellContext.Provider value={recordTitleCellContextValue}>
|
||||
<RecordTitleCellContainer />
|
||||
</RecordTitleCellContext.Provider>
|
||||
</FieldFocusContextProvider>
|
||||
</RecordTitleCellComponentInstanceContext.Provider>
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
+6
-3
@@ -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}</>;
|
||||
};
|
||||
|
||||
+6
-1
@@ -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,
|
||||
}),
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
+55
-49
@@ -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 {
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext';
|
||||
|
||||
export const RecordTitleCellComponentInstanceContext =
|
||||
createComponentInstanceContext();
|
||||
+9
@@ -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<boolean>({
|
||||
key: 'isTitleCellInEditModeComponentState',
|
||||
defaultValue: false,
|
||||
componentInstanceContext: RecordTitleCellComponentInstanceContext,
|
||||
});
|
||||
Reference in New Issue
Block a user