Ensure record fields uniqueness across widgets (#16781)

This PR dissociates record inputs so that several inputs for the same
record field can live on the same page.

## Simple values demo


https://github.com/user-attachments/assets/a8c224d8-4cd6-4fe3-9cf8-01f6bdd2fca9

## Relations demo


https://github.com/user-attachments/assets/87267a8b-8ce5-41ce-8a78-cba2384828bb

## Doesn't break Record Table


https://github.com/user-attachments/assets/bcdd2b05-7cb4-4ed2-9093-58be3e04e43e

## Doesn't break Show Page


https://github.com/user-attachments/assets/f9ab9e14-012c-451e-a35f-01a165523f95
This commit is contained in:
Baptiste Devessier
2025-12-24 17:01:10 +01:00
committed by GitHub
parent 6a4974e285
commit 06d0ac13c4
37 changed files with 620 additions and 436 deletions
@@ -9,6 +9,7 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition';
import { useIsRecordReadOnly } from '@/object-record/read-only/hooks/useIsRecordReadOnly';
import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { useFieldListFieldMetadataItems } from '@/object-record/record-field-list/hooks/useFieldListFieldMetadataItems';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
@@ -143,43 +144,47 @@ export const CalendarEventDetails = ({
}),
}}
>
<RecordInlineCell instanceIdPrefix={INPUT_ID_PREFIX} />
<RecordInlineCell />
</RecordFieldComponentInstanceContext.Provider>
</FieldContext.Provider>
</StyledPropertyBox>
);
return (
<StyledContainer>
<StyledEventChip
accent={ChipAccent.TextSecondary}
size={ChipSize.Large}
variant={ChipVariant.Highlighted}
clickable={false}
leftComponent={<IconCalendarEvent size={theme.icon.size.md} />}
label={t`Event`}
/>
<StyledHeader>
<StyledTitle canceled={calendarEvent.isCanceled}>
{calendarEvent.title}
</StyledTitle>
<StyledCreatedAt>
{t`Created`}{' '}
{beautifyPastDateRelativeToNow(
new Date(calendarEvent.externalCreatedAt),
<RecordFieldsScopeContextProvider
value={{ scopeInstanceId: INPUT_ID_PREFIX }}
>
<StyledContainer>
<StyledEventChip
accent={ChipAccent.TextSecondary}
size={ChipSize.Large}
variant={ChipVariant.Highlighted}
clickable={false}
leftComponent={<IconCalendarEvent size={theme.icon.size.md} />}
label={t`Event`}
/>
<StyledHeader>
<StyledTitle canceled={calendarEvent.isCanceled}>
{calendarEvent.title}
</StyledTitle>
<StyledCreatedAt>
{t`Created`}{' '}
{beautifyPastDateRelativeToNow(
new Date(calendarEvent.externalCreatedAt),
)}
</StyledCreatedAt>
</StyledHeader>
<StyledFields>
{standardFields.slice(0, 2).map(renderField)}
{calendarEventParticipants && (
<CalendarEventParticipantsResponseStatus
participants={calendarEventParticipants}
/>
)}
</StyledCreatedAt>
</StyledHeader>
<StyledFields>
{standardFields.slice(0, 2).map(renderField)}
{calendarEventParticipants && (
<CalendarEventParticipantsResponseStatus
participants={calendarEventParticipants}
/>
)}
{standardFields.slice(2).map(renderField)}
{customFields.map(renderField)}
</StyledFields>
</StyledContainer>
{standardFields.slice(2).map(renderField)}
{customFields.map(renderField)}
</StyledFields>
</StyledContainer>
</RecordFieldsScopeContextProvider>
);
};
@@ -1,11 +1,12 @@
import { useContext } from 'react';
import { t } from '@lingui/core/macro';
import { useContext } from 'react';
import { ActivityTargetChips } from '@/activities/components/ActivityTargetChips';
import { useActivityTargetObjectRecords } from '@/activities/hooks/useActivityTargetObjectRecords';
import { useOpenActivityTargetCellEditMode } from '@/activities/inline-cell/hooks/useOpenActivityTargetCellEditMode';
import { useUpdateActivityTargetFromCell } from '@/activities/inline-cell/hooks/useUpdateActivityTargetFromCell';
import { type CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { FieldContextProvider } from '@/object-record/record-field/ui/components/FieldContextProvider';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { FieldFocusContextProvider } from '@/object-record/record-field/ui/contexts/FieldFocusContextProvider';
@@ -54,67 +55,71 @@ export const ActivityTargetsInlineCell = ({
});
return (
<RecordFieldComponentInstanceContext.Provider
value={{
instanceId: componentInstanceId,
}}
<RecordFieldsScopeContextProvider
value={{ scopeInstanceId: componentInstanceId }}
>
<FieldFocusContextProvider>
<FieldContextProvider
objectNameSingular={activityObjectNameSingular}
objectRecordId={activityRecordId}
fieldMetadataName={fieldDefinition.metadata.fieldName}
fieldPosition={3}
overridenIsFieldEmpty={activityTargetObjectRecords.length === 0}
onMouseEnter={onMouseEnter}
anchorId={anchorId}
>
<RecordInlineCellContext.Provider
value={{
buttonIcon: IconPencil,
IconLabel: showLabel ? IconArrowUpRight : undefined,
showLabel: showLabel,
readonly: isReadOnly,
labelWidth: fieldDefinition?.labelWidth,
editModeContent: (
<MultipleRecordPicker
focusId={componentInstanceId}
componentInstanceId={componentInstanceId}
onClickOutside={() => {
closeInlineCell();
}}
onChange={(morphItem) => {
updateActivityTargetFromCell({
recordPickerInstanceId: componentInstanceId,
morphItem,
activityTargetWithTargetRecords:
activityTargetObjectRecords,
});
}}
onSubmit={() => {
closeInlineCell();
}}
/>
),
label: t`Relations`,
displayModeContent: (
<ActivityTargetChips
activityTargetObjectRecords={activityTargetObjectRecords}
maxWidth={maxWidth}
/>
),
onOpenEditMode: () => {
openActivityTargetCellEditMode({
recordPickerInstanceId: componentInstanceId,
activityTargetObjectRecords,
});
},
}}
<RecordFieldComponentInstanceContext.Provider
value={{
instanceId: componentInstanceId,
}}
>
<FieldFocusContextProvider>
<FieldContextProvider
objectNameSingular={activityObjectNameSingular}
objectRecordId={activityRecordId}
fieldMetadataName={fieldDefinition.metadata.fieldName}
fieldPosition={3}
overridenIsFieldEmpty={activityTargetObjectRecords.length === 0}
onMouseEnter={onMouseEnter}
anchorId={anchorId}
>
<RecordInlineCellContainer />
</RecordInlineCellContext.Provider>
</FieldContextProvider>
</FieldFocusContextProvider>
</RecordFieldComponentInstanceContext.Provider>
<RecordInlineCellContext.Provider
value={{
buttonIcon: IconPencil,
IconLabel: showLabel ? IconArrowUpRight : undefined,
showLabel: showLabel,
readonly: isReadOnly,
labelWidth: fieldDefinition?.labelWidth,
editModeContent: (
<MultipleRecordPicker
focusId={componentInstanceId}
componentInstanceId={componentInstanceId}
onClickOutside={() => {
closeInlineCell();
}}
onChange={(morphItem) => {
updateActivityTargetFromCell({
recordPickerInstanceId: componentInstanceId,
morphItem,
activityTargetWithTargetRecords:
activityTargetObjectRecords,
});
}}
onSubmit={() => {
closeInlineCell();
}}
/>
),
label: t`Relations`,
displayModeContent: (
<ActivityTargetChips
activityTargetObjectRecords={activityTargetObjectRecords}
maxWidth={maxWidth}
/>
),
onOpenEditMode: () => {
openActivityTargetCellEditMode({
recordPickerInstanceId: componentInstanceId,
activityTargetObjectRecords,
});
},
}}
>
<RecordInlineCellContainer />
</RecordInlineCellContext.Provider>
</FieldContextProvider>
</FieldFocusContextProvider>
</RecordFieldComponentInstanceContext.Provider>
</RecordFieldsScopeContextProvider>
);
};
@@ -7,6 +7,7 @@ import { type Note } from '@/activities/types/Note';
import { getActivityPreview } from '@/activities/utils/getActivityPreview';
import { useOpenRecordInCommandMenu } from '@/command-menu/hooks/useOpenRecordInCommandMenu';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { FieldContextProvider } from '@/object-record/record-field/ui/components/FieldContextProvider';
const StyledCard = styled.div<{ isSingleNote: boolean }>`
@@ -99,11 +100,17 @@ export const NoteTile = ({
fieldMetadataName="noteTargets"
fieldPosition={0}
>
<ActivityTargetsInlineCell
componentInstanceId={componentInstanceId}
activityRecordId={note.id}
activityObjectNameSingular={CoreObjectNameSingular.Note}
/>
<RecordFieldsScopeContextProvider
value={{
scopeInstanceId: note.id,
}}
>
<ActivityTargetsInlineCell
componentInstanceId={componentInstanceId}
activityRecordId={note.id}
activityObjectNameSingular={CoreObjectNameSingular.Note}
/>
</RecordFieldsScopeContextProvider>
</FieldContextProvider>
</StyledFooter>
</StyledCard>
@@ -12,6 +12,7 @@ import { type Task } from '@/activities/types/Task';
import { useOpenRecordInCommandMenu } from '@/command-menu/hooks/useOpenRecordInCommandMenu';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { StopPropagationContainer } from '@/object-record/record-board/record-board-card/components/StopPropagationContainer';
import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { FieldContextProvider } from '@/object-record/record-field/ui/components/FieldContextProvider';
import { IconCalendar, OverflowingTextWithTooltip } from 'twenty-ui/display';
import { Checkbox, CheckboxShape } from 'twenty-ui/input';
@@ -138,15 +139,21 @@ export const TaskRow = ({ task }: { task: Task }) => {
fieldMetadataName="taskTargets"
fieldPosition={0}
>
<StopPropagationContainer>
<ActivityTargetsInlineCell
activityObjectNameSingular={CoreObjectNameSingular.Task}
activityRecordId={task.id}
showLabel={false}
maxWidth={200}
componentInstanceId={componentInstanceId}
/>
</StopPropagationContainer>
<RecordFieldsScopeContextProvider
value={{
scopeInstanceId: task.id,
}}
>
<StopPropagationContainer>
<ActivityTargetsInlineCell
activityObjectNameSingular={CoreObjectNameSingular.Task}
activityRecordId={task.id}
showLabel={false}
maxWidth={200}
componentInstanceId={componentInstanceId}
/>
</StopPropagationContainer>
</RecordFieldsScopeContextProvider>
</FieldContextProvider>
</StyledActivityTargetsContainer>
}
@@ -14,12 +14,14 @@ import { RecordBoardCardBody } from '@/object-record/record-board/record-board-c
import { RecordBoardCardHeader } from '@/object-record/record-board/record-board-card/components/RecordBoardCardHeader';
import { RecordBoardCardMultiDragStack } from '@/object-record/record-board/record-board-card/components/RecordBoardCardMultiDragStack';
import { RECORD_BOARD_CARD_CLICK_OUTSIDE_ID } from '@/object-record/record-board/record-board-card/constants/RecordBoardCardClickOutsideId';
import { RECORD_BOARD_CARD_INPUT_ID_PREFIX } from '@/object-record/record-board/record-board-card/constants/RecordBoardCardInputIdPrefix';
import { RecordBoardCardComponentInstanceContext } from '@/object-record/record-board/record-board-card/states/contexts/RecordBoardCardComponentInstanceContext';
import { recordBoardCardIsExpandedComponentState } from '@/object-record/record-board/record-board-card/states/recordBoardCardIsExpandedComponentState';
import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext';
import { RecordCard } from '@/object-record/record-card/components/RecordCard';
import { isRecordIdPrimaryDragMultipleComponentFamilyState } from '@/object-record/record-drag/states/isRecordIdPrimaryDragMultipleComponentFamilyState';
import { isRecordIdSecondaryDragMultipleComponentFamilyState } from '@/object-record/record-drag/states/isRecordIdSecondaryDragMultipleComponentFamilyState';
import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { useOpenRecordFromIndexView } from '@/object-record/record-index/hooks/useOpenRecordFromIndexView';
import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
@@ -151,34 +153,40 @@ export const RecordBoardCard = () => {
instanceId: `record-board-card-${recordId}`,
}}
>
<StyledBoardCardWrapper
data-click-outside-id={RECORD_BOARD_CARD_CLICK_OUTSIDE_ID}
onContextMenu={handleContextMenuOpen}
<RecordFieldsScopeContextProvider
value={{ scopeInstanceId: RECORD_BOARD_CARD_INPUT_ID_PREFIX }}
>
<StyledCardContainer isPrimaryMultiDrag={isRecordIdPrimaryDragMultiple}>
{isRecordIdPrimaryDragMultiple && <RecordBoardCardMultiDragStack />}
<RecordCard
data-selected={isCurrentCardSelected}
data-focused={isCurrentCardFocused}
data-active={isCurrentCardActive}
onMouseLeave={onMouseLeaveBoard}
onClick={handleCardClick}
<StyledBoardCardWrapper
data-click-outside-id={RECORD_BOARD_CARD_CLICK_OUTSIDE_ID}
onContextMenu={handleContextMenuOpen}
>
<StyledCardContainer
isPrimaryMultiDrag={isRecordIdPrimaryDragMultiple}
isSecondaryDragged={isRecordIdSecondaryDragMultiple}
isDragging={isDraggingThisCard}
>
<RecordBoardCardHeader />
<AnimatedEaseInOut
isOpen={isCardExpanded || !isCompactModeActive}
initial={false}
{isRecordIdPrimaryDragMultiple && <RecordBoardCardMultiDragStack />}
<RecordCard
data-selected={isCurrentCardSelected}
data-focused={isCurrentCardFocused}
data-active={isCurrentCardActive}
onMouseLeave={onMouseLeaveBoard}
onClick={handleCardClick}
isPrimaryMultiDrag={isRecordIdPrimaryDragMultiple}
isSecondaryDragged={isRecordIdSecondaryDragMultiple}
isDragging={isDraggingThisCard}
>
<RecordBoardCardBody />
</AnimatedEaseInOut>
</RecordCard>
</StyledCardContainer>
<RecordBoardCardCellHoveredPortal />
<RecordBoardCardCellEditModePortal />
</StyledBoardCardWrapper>
<RecordBoardCardHeader />
<AnimatedEaseInOut
isOpen={isCardExpanded || !isCompactModeActive}
initial={false}
>
<RecordBoardCardBody />
</AnimatedEaseInOut>
</RecordCard>
</StyledCardContainer>
<RecordBoardCardCellHoveredPortal />
<RecordBoardCardCellEditModePortal />
</StyledBoardCardWrapper>
</RecordFieldsScopeContextProvider>
</RecordBoardCardComponentInstanceContext.Provider>
);
};
@@ -6,10 +6,12 @@ import { RecordCalendarCardCellHoveredPortal } from '@/object-record/record-cale
import { RecordCalendarCardBody } from '@/object-record/record-calendar/record-calendar-card/components/RecordCalendarCardBody';
import { RecordCalendarCardHeader } from '@/object-record/record-calendar/record-calendar-card/components/RecordCalendarCardHeader';
import { RECORD_CALENDAR_CARD_CLICK_OUTSIDE_ID } from '@/object-record/record-calendar/record-calendar-card/constants/RecordCalendarCardClickOutsideId';
import { RECORD_CALENDAR_CARD_INPUT_ID_PREFIX } from '@/object-record/record-calendar/record-calendar-card/constants/RecordCalendarCardInputIdPrefix';
import { RecordCalendarCardComponentInstanceContext } from '@/object-record/record-calendar/record-calendar-card/states/contexts/RecordCalendarCardComponentInstanceContext';
import { isRecordCalendarCardSelectedComponentFamilyState } from '@/object-record/record-calendar/record-calendar-card/states/isRecordCalendarCardSelectedComponentFamilyState';
import { RecordCalendarComponentInstanceContext } from '@/object-record/record-calendar/states/contexts/RecordCalendarComponentInstanceContext';
import { RecordCard } from '@/object-record/record-card/components/RecordCard';
import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentFamilyState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyState';
@@ -78,22 +80,26 @@ export const RecordCalendarCard = ({ recordId }: RecordCalendarCardProps) => {
instanceId: recordId,
}}
>
<StyledContainer onContextMenu={handleContextMenuOpen}>
<StyledRecordCard
data-selected={isCurrentCardSelected}
data-click-outside-id={RECORD_CALENDAR_CARD_CLICK_OUTSIDE_ID}
>
<RecordCalendarCardHeader recordId={recordId} />
<AnimatedEaseInOut isOpen={!isCompactModeActive} initial={false}>
<RecordCalendarCardBody
recordId={recordId}
isRecordReadOnly={false}
/>
</AnimatedEaseInOut>
</StyledRecordCard>
<RecordCalendarCardCellHoveredPortal recordId={recordId} />
<RecordCalendarCardCellEditModePortal recordId={recordId} />
</StyledContainer>
<RecordFieldsScopeContextProvider
value={{ scopeInstanceId: RECORD_CALENDAR_CARD_INPUT_ID_PREFIX }}
>
<StyledContainer onContextMenu={handleContextMenuOpen}>
<StyledRecordCard
data-selected={isCurrentCardSelected}
data-click-outside-id={RECORD_CALENDAR_CARD_CLICK_OUTSIDE_ID}
>
<RecordCalendarCardHeader recordId={recordId} />
<AnimatedEaseInOut isOpen={!isCompactModeActive} initial={false}>
<RecordCalendarCardBody
recordId={recordId}
isRecordReadOnly={false}
/>
</AnimatedEaseInOut>
</StyledRecordCard>
<RecordCalendarCardCellHoveredPortal recordId={recordId} />
<RecordCalendarCardCellEditModePortal recordId={recordId} />
</StyledContainer>
</RecordFieldsScopeContextProvider>
</RecordCalendarCardComponentInstanceContext.Provider>
);
};
@@ -0,0 +1,12 @@
import { createRequiredContext } from '~/utils/createRequiredContext';
export type RecordFieldsScopeContextValue = {
scopeInstanceId: string;
};
export const [
RecordFieldsScopeContextProvider,
useRecordFieldsScopeContextOrThrow,
] = createRequiredContext<RecordFieldsScopeContextValue>(
'RecordFieldsScopeContext',
);
@@ -1,6 +1,7 @@
import { useContext } from 'react';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { RecordDetailSectionContainer } from '@/object-record/record-field-list/record-detail-section/components/RecordDetailSectionContainer';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import {
@@ -26,6 +27,7 @@ type RecordDetailMorphRelationSectionProps = {
export const RecordDetailMorphRelationSection = ({
loading,
}: RecordDetailMorphRelationSectionProps) => {
const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow();
const { recordId, fieldDefinition } = useContext(FieldContext);
const metadata = fieldDefinition.metadata as FieldMorphRelationMetadata;
@@ -44,6 +46,7 @@ export const RecordDetailMorphRelationSection = ({
const dropdownId = getRecordFieldCardRelationPickerDropdownId({
fieldDefinition,
recordId,
instanceId: scopeInstanceId,
});
const isDropdownOpen = useRecoilComponentValue(
@@ -1,6 +1,7 @@
import { type ReactNode, useCallback, useContext } from 'react';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { useGetMorphRelationRelatedRecordsWithObjectNameSingular } from '@/object-record/record-field-list/record-detail-section/relation/components/hooks/useGetMorphRelationRelatedRecordsWithObjectNameSingular';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts/FieldInputEventContext';
@@ -29,6 +30,7 @@ type RecordDetailMorphRelationSectionDropdownManyToOneProps = {
export const RecordDetailMorphRelationSectionDropdownManyToOne = ({
dropdownTriggerClickableComponent,
}: RecordDetailMorphRelationSectionDropdownManyToOneProps) => {
const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow();
const { recordId, fieldDefinition } = useContext(FieldContext);
assertFieldMetadata(
@@ -58,6 +60,7 @@ export const RecordDetailMorphRelationSectionDropdownManyToOne = ({
const dropdownId = getRecordFieldCardRelationPickerDropdownId({
fieldDefinition,
recordId,
instanceId: scopeInstanceId,
});
const { closeDropdown } = useCloseDropdown();
@@ -1,6 +1,7 @@
import { type ReactNode, useCallback, useContext } from 'react';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { useGetMorphRelationRelatedRecordsWithObjectNameSingular } from '@/object-record/record-field-list/record-detail-section/relation/components/hooks/useGetMorphRelationRelatedRecordsWithObjectNameSingular';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { useUpdateMorphRelationOneToManyFieldInput } from '@/object-record/record-field/ui/meta-types/input/hooks/useUpdateMorphRelationOneToManyFieldInput';
@@ -31,6 +32,7 @@ type RecordDetailMorphRelationSectionDropdownOneToManyProps = {
export const RecordDetailMorphRelationSectionDropdownOneToMany = ({
dropdownTriggerClickableComponent,
}: RecordDetailMorphRelationSectionDropdownOneToManyProps) => {
const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow();
const { recordId, fieldDefinition } = useContext(FieldContext);
assertFieldMetadata(
@@ -61,6 +63,7 @@ export const RecordDetailMorphRelationSectionDropdownOneToMany = ({
const dropdownId = getRecordFieldCardRelationPickerDropdownId({
fieldDefinition,
recordId,
instanceId: scopeInstanceId,
});
const { closeDropdown } = useCloseDropdown();
@@ -12,6 +12,10 @@ import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject';
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
import { RecordFieldList } from '@/object-record/record-field-list/components/RecordFieldList';
import {
RecordFieldsScopeContextProvider,
useRecordFieldsScopeContextOrThrow,
} from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { RecordDetailRecordsListItemContainer } from '@/object-record/record-field-list/record-detail-section/components/RecordDetailRecordsListItemContainer';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts/FieldInputEventContext';
@@ -30,13 +34,13 @@ import { useModal } from '@/ui/layout/modal/hooks/useModal';
import { useIsInRightDrawerOrThrow } from '@/ui/layout/right-drawer/contexts/RightDrawerContext';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
import { t } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { createPortal } from 'react-dom';
import {
computeMorphRelationFieldName,
CustomError,
} from 'twenty-shared/utils';
import { Trans } from '@lingui/react/macro';
import { t } from '@lingui/core/macro';
import {
IconChevronDown,
IconDotsVertical,
@@ -99,6 +103,7 @@ export const RecordDetailRelationRecordsListItem = ({
relationObjectMetadataNameSingular,
relationFieldMetadataId,
}: RecordDetailRelationRecordsListItemProps) => {
const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow();
const {
fieldDefinition,
recordId,
@@ -162,6 +167,7 @@ export const RecordDetailRelationRecordsListItem = ({
const dropdownId = getRecordFieldCardRelationPickerDropdownId({
fieldDefinition,
recordId,
instanceId: scopeInstanceId,
});
const setSingleRecordPickerSelectedId = useSetRecoilComponentState(
singleRecordPickerSelectedIdComponentState,
@@ -284,15 +290,21 @@ export const RecordDetailRelationRecordsListItem = ({
)}
</StyledListItem>
<AnimatedEaseInOut isOpen={isExpanded}>
<RecordFieldList
instanceId={`record-detail-relation-${relationRecord.id}-${isInRightDrawer ? 'right-drawer' : ''}`}
objectNameSingular={relationObjectMetadataNameSingular}
objectRecordId={relationRecord.id}
showDuplicatesSection={false}
showRelationSections={false}
excludeCreatedAtAndUpdatedAt={true}
excludeFieldMetadataIds={[relationFieldMetadataId]}
/>
<RecordFieldsScopeContextProvider
value={{
scopeInstanceId: `record-detail-relation-${relationRecord.id}${isInRightDrawer ? '-right-drawer' : ''}`,
}}
>
<RecordFieldList
instanceId={`record-detail-relation-${relationRecord.id}-${isInRightDrawer ? 'right-drawer' : ''}`}
objectNameSingular={relationObjectMetadataNameSingular}
objectRecordId={relationRecord.id}
showDuplicatesSection={false}
showRelationSections={false}
excludeCreatedAtAndUpdatedAt={true}
excludeFieldMetadataIds={[relationFieldMetadataId]}
/>
</RecordFieldsScopeContextProvider>
</AnimatedEaseInOut>
{createPortal(
<ConfirmationModal
@@ -4,6 +4,7 @@ import { useRecoilValue } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { useAggregateRecords } from '@/object-record/hooks/useAggregateRecords';
import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { RecordDetailSectionContainer } from '@/object-record/record-field-list/record-detail-section/components/RecordDetailSectionContainer';
import { RecordDetailRelationRecordsList } from '@/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationRecordsList';
import { RecordDetailRelationSectionDropdown } from '@/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSectionDropdown';
@@ -46,6 +47,7 @@ export const RecordDetailRelationSection = ({
}: RecordDetailRelationSectionProps) => {
const { t } = useLingui();
const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow();
const { recordId, fieldDefinition } = useContext(FieldContext);
const {
@@ -95,6 +97,7 @@ export const RecordDetailRelationSection = ({
const dropdownId = getRecordFieldCardRelationPickerDropdownId({
fieldDefinition,
recordId,
instanceId: scopeInstanceId,
});
const isDropdownOpen = useRecoilComponentValue(
@@ -4,6 +4,7 @@ import { useRecoilValue } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { getFieldMetadataItemById } from '@/object-metadata/utils/getFieldMetadataItemById';
import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/record-field/ui/meta-types/input/hooks/useAddNewRecordAndOpenRightDrawer';
import { useUpdateRelationOneToManyFieldInput } from '@/object-record/record-field/ui/meta-types/input/hooks/useUpdateRelationOneToManyFieldInput';
@@ -33,6 +34,7 @@ type RecordDetailRelationSectionDropdownToManyProps = {
export const RecordDetailRelationSectionDropdownToMany = ({
dropdownTriggerClickableComponent,
}: RecordDetailRelationSectionDropdownToManyProps) => {
const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow();
const { recordId, fieldDefinition } = useContext(FieldContext);
const { fieldMetadataId } = fieldDefinition;
const {
@@ -78,6 +80,7 @@ export const RecordDetailRelationSectionDropdownToMany = ({
const dropdownId = getRecordFieldCardRelationPickerDropdownId({
fieldDefinition,
recordId,
instanceId: scopeInstanceId,
});
const { closeDropdown } = useCloseDropdown();
@@ -3,6 +3,7 @@ import { useRecoilValue } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts/FieldInputEventContext';
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/record-field/ui/meta-types/input/hooks/useAddNewRecordAndOpenRightDrawer';
@@ -36,6 +37,7 @@ type RecordDetailRelationSectionDropdownToOneProps = {
export const RecordDetailRelationSectionDropdownToOne = ({
dropdownTriggerClickableComponent,
}: RecordDetailRelationSectionDropdownToOneProps) => {
const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow();
const { recordId, fieldDefinition } = useContext(FieldContext);
assertFieldMetadata(
FieldMetadataType.RELATION,
@@ -89,6 +91,7 @@ export const RecordDetailRelationSectionDropdownToOne = ({
const dropdownId = getRecordFieldCardRelationPickerDropdownId({
fieldDefinition,
recordId,
instanceId: scopeInstanceId,
});
const { closeDropdown } = useCloseDropdown();
@@ -10,6 +10,7 @@ import { graphqlMocks } from '~/testing/graphqlMocks';
import { getCompaniesMock } from '~/testing/mock-data/companies';
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { ComponentDecorator } from 'twenty-ui/testing';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { RightDrawerDecorator } from '~/testing/decorators/RightDrawerDecorator';
@@ -49,7 +50,11 @@ const meta: Meta<typeof RecordDetailRelationSection> = {
isRecordFieldReadOnly: false,
}}
>
<Story />
<RecordFieldsScopeContextProvider
value={{ scopeInstanceId: 'mock-instance-id' }}
>
<Story />
</RecordFieldsScopeContextProvider>
</FieldContext.Provider>
</ContextStoreComponentInstanceContext.Provider>
),
@@ -5,6 +5,7 @@ import { useEffect } from 'react';
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { getFieldInputEventContextProviderWithJestMocks } from '@/object-record/record-field/ui/meta-types/input/components/__stories__/utils/getFieldInputEventContextProviderWithJestMocks';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
@@ -90,11 +91,15 @@ const DateFieldInputWithContext = ({
isRecordFieldReadOnly: false,
}}
>
<FieldInputEventContextProviderWithJestMocks>
<StorybookFieldInputDropdownFocusIdSetterEffect />
<DateFieldValueSetterEffect value={value} />
<DateFieldValueGater />
</FieldInputEventContextProviderWithJestMocks>
<RecordFieldsScopeContextProvider
value={{ scopeInstanceId: instanceId }}
>
<FieldInputEventContextProviderWithJestMocks>
<StorybookFieldInputDropdownFocusIdSetterEffect />
<DateFieldValueSetterEffect value={value} />
<DateFieldValueGater />
</FieldInputEventContextProviderWithJestMocks>
</RecordFieldsScopeContextProvider>
</FieldContext.Provider>
<div data-testid="data-field-input-click-outside-div"></div>
</RecordFieldComponentInstanceContext.Provider>
@@ -6,6 +6,7 @@ import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePush
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { getFieldInputEventContextProviderWithJestMocks } from '@/object-record/record-field/ui/meta-types/input/components/__stories__/utils/getFieldInputEventContextProviderWithJestMocks';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
@@ -96,11 +97,15 @@ const NumberFieldInputWithContext = ({
isRecordFieldReadOnly: false,
}}
>
<FieldInputEventContextProviderWithJestMocks>
{isReady && <StorybookFieldInputDropdownFocusIdSetterEffect />}
<NumberFieldValueSetterEffect value={value} />
<NumberFieldInput />
</FieldInputEventContextProviderWithJestMocks>
<RecordFieldsScopeContextProvider
value={{ scopeInstanceId: RECORD_TABLE_CELL_INPUT_ID_PREFIX }}
>
<FieldInputEventContextProviderWithJestMocks>
{isReady && <StorybookFieldInputDropdownFocusIdSetterEffect />}
<NumberFieldValueSetterEffect value={value} />
<NumberFieldInput />
</FieldInputEventContextProviderWithJestMocks>
</RecordFieldsScopeContextProvider>
</FieldContext.Provider>
{isReady && <div data-testid="is-ready-marker" />}
<div data-testid="data-field-input-click-outside-div" />
@@ -9,6 +9,7 @@ import { useGetButtonIcon } from '@/object-record/record-field/ui/hooks/useGetBu
import { useIsFieldInputOnly } from '@/object-record/record-field/ui/hooks/useIsFieldInputOnly';
import { useOpenFieldInputEditMode } from '@/object-record/record-field/ui/hooks/useOpenFieldInputEditMode';
import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import {
FieldInputEventContext,
type FieldInputClickOutsideEvent,
@@ -44,6 +45,7 @@ export const RecordInlineCell = ({
onCloseEditMode: onCloseEditModeFromContext,
isRecordFieldReadOnly: isReadOnly,
} = useContext(FieldContext);
const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow();
const { openFieldInput, closeFieldInput } = useOpenFieldInputEditMode();
@@ -142,11 +144,12 @@ export const RecordInlineCell = ({
.getLoadable(activeDropdownFocusIdState)
.getValue();
const expectedDropdownFocusId = getDropdownFocusIdForRecordField(
const expectedDropdownFocusId = getDropdownFocusIdForRecordField({
recordId,
fieldDefinition.fieldMetadataId,
'inline-cell',
);
fieldMetadataId: fieldDefinition.fieldMetadataId,
componentType: 'inline-cell',
instanceId: scopeInstanceId,
});
if (currentDropdownFocusId !== expectedDropdownFocusId) {
return;
@@ -162,9 +165,10 @@ export const RecordInlineCell = ({
closeInlineCell();
},
[
closeInlineCell,
recordId,
fieldDefinition.fieldMetadataId,
scopeInstanceId,
closeInlineCell,
persistFieldFromFieldInputContext,
],
);
@@ -101,8 +101,9 @@ export const RecordInlineCellAnchoredPortal = ({
>
<RecordInlineCellAnchoredPortalContext>
{children}
<RecordInlineCellCloseOnCommandMenuOpeningEffect />
</RecordInlineCellAnchoredPortalContext>
<RecordInlineCellCloseOnCommandMenuOpeningEffect />
</RecordFieldComponentInstanceContext.Provider>,
anchorElement,
)}
@@ -2,6 +2,7 @@ import { useContext } from 'react';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
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';
@@ -21,6 +22,7 @@ export const useInlineCell = (
);
const { onOpenEditMode, onCloseEditMode } = useRecordInlineCellContext();
const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow();
const { setActiveDropdownFocusIdAndMemorizePrevious } =
useSetActiveDropdownFocusIdAndMemorizePrevious();
@@ -43,13 +45,14 @@ export const useInlineCell = (
fieldComponentInstanceId: recordFieldComponentInstanceId,
});
setActiveDropdownFocusIdAndMemorizePrevious(
getDropdownFocusIdForRecordField(
recordId,
fieldDefinition.fieldMetadataId,
'inline-cell',
),
);
const dropdownId = getDropdownFocusIdForRecordField({
recordId,
fieldMetadataId: fieldDefinition.fieldMetadataId,
componentType: 'inline-cell',
instanceId: scopeInstanceId,
});
setActiveDropdownFocusIdAndMemorizePrevious(dropdownId);
};
return {
@@ -1,4 +1,5 @@
import { RecordFieldList } from '@/object-record/record-field-list/components/RecordFieldList';
import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { type FieldCardConfiguration } from '@/object-record/record-show/types/CardConfiguration';
import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext';
import { useTargetRecord } from '@/ui/layout/contexts/useTargetRecord';
@@ -11,12 +12,16 @@ export const FieldsCard = ({ configuration }: FieldsCardProps) => {
const targetRecord = useTargetRecord();
const { isInRightDrawer } = useLayoutRenderingContext();
const instanceId = `fields-card-${targetRecord.id}${isInRightDrawer ? '-right-drawer' : ''}`;
return (
<RecordFieldList
instanceId={`fields-card-${targetRecord.id}-${isInRightDrawer ? 'right-drawer' : ''}`}
objectNameSingular={targetRecord.targetObjectNameSingular}
objectRecordId={targetRecord.id}
showDuplicatesSection={configuration?.showDuplicatesSection ?? true}
/>
<RecordFieldsScopeContextProvider value={{ scopeInstanceId: instanceId }}>
<RecordFieldList
instanceId={instanceId}
objectNameSingular={targetRecord.targetObjectNameSingular}
objectRecordId={targetRecord.id}
showDuplicatesSection={configuration?.showDuplicatesSection ?? true}
/>
</RecordFieldsScopeContextProvider>
);
};
@@ -4,9 +4,11 @@ import { type FieldMetadata } from '@/object-record/record-field/ui/types/FieldM
type GetRecordFieldCardRelationPickerDropdownIdArgs = {
fieldDefinition: FieldDefinition<FieldMetadata>;
recordId: string;
instanceId: string;
};
export const getRecordFieldCardRelationPickerDropdownId = ({
fieldDefinition,
recordId,
instanceId,
}: GetRecordFieldCardRelationPickerDropdownIdArgs) =>
`record-field-card-relation-picker-${fieldDefinition.fieldMetadataId}-${recordId}`;
`record-field-card-relation-picker:${instanceId}:${fieldDefinition.fieldMetadataId}:${recordId}`;
@@ -4,8 +4,10 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
import { RecordTableContextProvider as RecordTableContextInternalProvider } from '@/object-record/record-table/contexts/RecordTableContext';
import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject';
import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector';
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix';
import { RECORD_TABLE_COLUMN_MIN_WIDTH } from '@/object-record/record-table/constants/RecordTableColumnMinWidth';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
@@ -46,22 +48,26 @@ export const RecordTableContextProvider = ({
: 'MOUSE_DOWN';
return (
<RecordTableContextInternalProvider
value={{
viewBarId,
objectMetadataItem,
recordTableId,
objectNameSingular,
objectPermissions,
visibleRecordFields: visibleRecordFields.map((field) => ({
...field,
size: Math.max(field.size, RECORD_TABLE_COLUMN_MIN_WIDTH),
})),
onRecordIdentifierClick,
triggerEvent,
}}
<RecordFieldsScopeContextProvider
value={{ scopeInstanceId: RECORD_TABLE_CELL_INPUT_ID_PREFIX }}
>
{children}
</RecordTableContextInternalProvider>
<RecordTableContextInternalProvider
value={{
viewBarId,
objectMetadataItem,
recordTableId,
objectNameSingular,
objectPermissions,
visibleRecordFields: visibleRecordFields.map((field) => ({
...field,
size: Math.max(field.size, RECORD_TABLE_COLUMN_MIN_WIDTH),
})),
onRecordIdentifierClick,
triggerEvent,
}}
>
{children}
</RecordTableContextInternalProvider>
</RecordFieldsScopeContextProvider>
);
};
@@ -21,6 +21,7 @@ import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropd
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { useOpenRecordFromIndexView } from '@/object-record/record-index/hooks/useOpenRecordFromIndexView';
import { useActiveRecordTableRow } from '@/object-record/record-table/hooks/useActiveRecordTableRow';
import { useFocusedRecordTableRow } from '@/object-record/record-table/hooks/useFocusedRecordTableRow';
@@ -41,6 +42,8 @@ export type OpenTableCellArgs = {
};
export const useOpenRecordTableCell = (recordTableId: string) => {
const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow();
const clickOutsideListenerIsActivatedState = useRecoilComponentCallbackState(
clickOutsideListenerIsActivatedComponentState,
RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID,
@@ -157,11 +160,12 @@ export const useOpenRecordTableCell = (recordTableId: string) => {
toggleClickOutside(false);
setActiveDropdownFocusIdAndMemorizePrevious(
getDropdownFocusIdForRecordField(
getDropdownFocusIdForRecordField({
recordId,
fieldDefinition.fieldMetadataId,
'table-cell',
),
fieldMetadataId: fieldDefinition.fieldMetadataId,
componentType: 'table-cell',
instanceId: scopeInstanceId,
}),
);
},
[
@@ -175,10 +179,11 @@ export const useOpenRecordTableCell = (recordTableId: string) => {
initDraftValue,
toggleClickOutside,
setActiveDropdownFocusIdAndMemorizePrevious,
scopeInstanceId,
leaveTableFocus,
openRecordFromIndexView,
activateRecordTableRow,
unfocusRecordTableRow,
openRecordFromIndexView,
],
);
@@ -1,7 +1,13 @@
export const getDropdownFocusIdForRecordField = (
recordId: string,
fieldMetadataId: string,
componentType: 'table-cell' | 'inline-cell',
) => {
return `dropdown-${componentType}-record-${recordId}-field-${fieldMetadataId}`;
export const getDropdownFocusIdForRecordField = ({
recordId,
fieldMetadataId,
componentType,
instanceId,
}: {
recordId: string;
fieldMetadataId: string;
componentType: 'table-cell' | 'inline-cell';
instanceId: string;
}) => {
return `dropdown-${instanceId}-${componentType}-record-${recordId}-field-${fieldMetadataId}`;
};
@@ -43,6 +43,25 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
updatedAt: new Date().toISOString(),
deletedAt: null,
},
{
__typename: 'PageLayoutWidget',
id: 'company-widget-fields-2',
pageLayoutTabId: 'company-tab-fields',
title: 'Fields',
type: WidgetType.FIELDS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 3,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
configuration: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
{
__typename: 'PageLayoutWidget',
id: 'company-widget-account-owner',
@@ -112,6 +131,29 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
updatedAt: new Date().toISOString(),
deletedAt: null,
},
{
__typename: 'PageLayoutWidget',
id: 'company-widget-people-2',
pageLayoutTabId: 'company-tab-fields',
title: 'People',
type: WidgetType.FIELD,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 2,
column: 0,
rowSpan: 1,
columnSpan: 12,
},
configuration: {
__typename: 'FieldConfiguration',
fieldMetadataId: 'people',
layout: 'FIELD',
},
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
{
__typename: 'PageLayoutWidget',
id: 'company-widget-name',
@@ -135,6 +177,29 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
updatedAt: new Date().toISOString(),
deletedAt: null,
},
{
__typename: 'PageLayoutWidget',
id: 'company-widget-name-2',
pageLayoutTabId: 'company-tab-fields',
title: 'Name',
type: WidgetType.FIELD,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 2,
column: 0,
rowSpan: 1,
columnSpan: 12,
},
configuration: {
__typename: 'FieldConfiguration',
fieldMetadataId: 'name',
layout: 'FIELD',
},
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
{
__typename: 'PageLayoutWidget',
id: 'company-widget-employees',
@@ -4,6 +4,7 @@ import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/uti
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
import { useIsRecordReadOnly } from '@/object-record/read-only/hooks/useIsRecordReadOnly';
import { isRecordFieldReadOnly } from '@/object-record/read-only/utils/isRecordFieldReadOnly';
import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import {
FieldContext,
type GenericFieldContextType,
@@ -70,21 +71,24 @@ export const WidgetActionFieldEdit = () => {
const isRelationField =
isFieldRelation(fieldDefinition) || isFieldMorphRelation(fieldDefinition);
if (isRelationField) {
return (
<FieldWidgetRelationEditAction
fieldDefinition={fieldDefinition}
recordId={targetRecord.id}
/>
);
}
const instanceId = getFieldWidgetInstanceId({
widgetId: widget.id,
recordId: targetRecord.id,
fieldName: fieldMetadataItem.name,
isInRightDrawer,
});
if (isRelationField) {
return (
<RecordFieldsScopeContextProvider value={{ scopeInstanceId: instanceId }}>
<FieldWidgetRelationEditAction
fieldDefinition={fieldDefinition}
recordId={targetRecord.id}
/>
</RecordFieldsScopeContextProvider>
);
}
const recordFieldInputInstanceId = getRecordFieldInputInstanceId({
recordId: targetRecord.id,
fieldName: fieldMetadataItem.name,
@@ -113,14 +117,16 @@ export const WidgetActionFieldEdit = () => {
} satisfies GenericFieldContextType;
return (
<RecordFieldComponentInstanceContext.Provider
value={{
instanceId: recordFieldInputInstanceId,
}}
>
<FieldContext.Provider value={fieldContextValue}>
<FieldWidgetEditAction />
</FieldContext.Provider>
</RecordFieldComponentInstanceContext.Provider>
<RecordFieldsScopeContextProvider value={{ scopeInstanceId: instanceId }}>
<RecordFieldComponentInstanceContext.Provider
value={{
instanceId: recordFieldInputInstanceId,
}}
>
<FieldContext.Provider value={fieldContextValue}>
<FieldWidgetEditAction />
</FieldContext.Provider>
</RecordFieldComponentInstanceContext.Provider>
</RecordFieldsScopeContextProvider>
);
};
@@ -1,5 +1,6 @@
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
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';
@@ -23,12 +24,14 @@ export const FieldWidgetCellEditModePortal = ({
recordId,
instanceId,
}: FieldWidgetCellEditModePortalProps) => {
const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow();
const activeDropdownFocusId = useRecoilValue(activeDropdownFocusIdState);
const expectedDropdownFocusId = getDropdownFocusIdForRecordField(
const expectedDropdownFocusId = getDropdownFocusIdForRecordField({
recordId,
fieldMetadataItem.id,
'inline-cell',
);
fieldMetadataId: fieldMetadataItem.id,
componentType: 'inline-cell',
instanceId: scopeInstanceId,
});
const isEditing = activeDropdownFocusId === expectedDropdownFocusId;
const setIsHovered = useSetRecoilComponentState(
@@ -1,5 +1,6 @@
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { RecordInlineCellAnchoredPortal } from '@/object-record/record-inline-cell/components/RecordInlineCellAnchoredPortal';
import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField';
import { FieldWidgetCellHoveredContent } from '@/page-layout/widgets/field/components/FieldWidgetCellHoveredContent';
@@ -23,12 +24,14 @@ export const FieldWidgetCellHoveredPortal = ({
isHovered,
onMouseLeave,
}: FieldWidgetCellHoveredPortalProps) => {
const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow();
const activeDropdownFocusId = useRecoilValue(activeDropdownFocusIdState);
const expectedDropdownFocusId = getDropdownFocusIdForRecordField(
const expectedDropdownFocusId = getDropdownFocusIdForRecordField({
recordId,
fieldMetadataItem.id,
'inline-cell',
);
fieldMetadataId: fieldMetadataItem.id,
componentType: 'inline-cell',
instanceId: scopeInstanceId,
});
const isEditing = activeDropdownFocusId === expectedDropdownFocusId;
if (!isHovered || isEditing) {
@@ -3,6 +3,7 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataI
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
import { useIsRecordReadOnly } from '@/object-record/read-only/hooks/useIsRecordReadOnly';
import { isRecordFieldReadOnly } from '@/object-record/read-only/utils/isRecordFieldReadOnly';
import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
import { type FieldDefinition } from '@/object-record/record-field/ui/types/FieldDefinition';
@@ -13,6 +14,7 @@ import { FieldWidgetCellHoveredPortal } from '@/page-layout/widgets/field/compon
import { FieldWidgetInlineCell } from '@/page-layout/widgets/field/components/FieldWidgetInlineCell';
import { fieldWidgetHoverComponentState } from '@/page-layout/widgets/field/states/fieldWidgetHoverComponentState';
import { getFieldWidgetInstanceId } from '@/page-layout/widgets/field/utils/getFieldWidgetInstanceId';
import { useCurrentWidget } from '@/page-layout/widgets/hooks/useCurrentWidget';
import { getObjectPermissionsFromMapByObjectMetadataId } from '@/settings/roles/role-permissions/objects-permissions/utils/getObjectPermissionsFromMapByObjectMetadataId';
import { RightDrawerProvider } from '@/ui/layout/right-drawer/contexts/RightDrawerContext';
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
@@ -38,11 +40,14 @@ export const FieldWidgetDisplay = ({
recordId,
isInRightDrawer,
}: FieldWidgetDisplayProps) => {
const widget = useCurrentWidget();
const [isHovered, setIsHovered] = useRecoilComponentState(
fieldWidgetHoverComponentState,
);
const instanceId = getFieldWidgetInstanceId({
widgetId: widget.id,
recordId,
fieldName: fieldMetadataItem.name,
isInRightDrawer,
@@ -64,64 +69,66 @@ export const FieldWidgetDisplay = ({
const handleMouseLeave = () => setIsHovered(false);
return (
<RightDrawerProvider value={{ isInRightDrawer }}>
<RecordFieldComponentInstanceContext.Provider
value={{
instanceId: getRecordFieldInputInstanceId({
recordId,
fieldName: fieldMetadataItem.name,
prefix: instanceId,
}),
}}
>
<StyledContainer>
<FieldContext.Provider
value={{
<RecordFieldsScopeContextProvider value={{ scopeInstanceId: instanceId }}>
<RightDrawerProvider value={{ isInRightDrawer }}>
<RecordFieldComponentInstanceContext.Provider
value={{
instanceId: getRecordFieldInputInstanceId({
recordId,
maxWidth: 200,
isLabelIdentifier: false,
fieldDefinition,
useUpdateRecord: useUpdateOneObjectRecordMutation,
isDisplayModeFixHeight: false,
isRecordFieldReadOnly: isRecordFieldReadOnly({
isRecordReadOnly,
objectPermissions:
getObjectPermissionsFromMapByObjectMetadataId({
objectPermissionsByObjectMetadataId,
objectMetadataId: objectMetadataItem.id,
}),
fieldMetadataItem: {
id: fieldMetadataItem.id,
isUIReadOnly: fieldMetadataItem.isUIReadOnly ?? false,
},
}),
onMouseEnter: handleMouseEnter,
anchorId: getRecordFieldInputInstanceId({
fieldName: fieldMetadataItem.name,
prefix: instanceId,
}),
}}
>
<StyledContainer>
<FieldContext.Provider
value={{
recordId,
fieldName: fieldMetadataItem.name,
prefix: instanceId,
}),
}}
>
<FieldWidgetInlineCell instanceIdPrefix={instanceId} />
</FieldContext.Provider>
</StyledContainer>
maxWidth: 200,
isLabelIdentifier: false,
fieldDefinition,
useUpdateRecord: useUpdateOneObjectRecordMutation,
isDisplayModeFixHeight: false,
isRecordFieldReadOnly: isRecordFieldReadOnly({
isRecordReadOnly,
objectPermissions:
getObjectPermissionsFromMapByObjectMetadataId({
objectPermissionsByObjectMetadataId,
objectMetadataId: objectMetadataItem.id,
}),
fieldMetadataItem: {
id: fieldMetadataItem.id,
isUIReadOnly: fieldMetadataItem.isUIReadOnly ?? false,
},
}),
onMouseEnter: handleMouseEnter,
anchorId: getRecordFieldInputInstanceId({
recordId,
fieldName: fieldMetadataItem.name,
prefix: instanceId,
}),
}}
>
<FieldWidgetInlineCell instanceIdPrefix={instanceId} />
</FieldContext.Provider>
</StyledContainer>
<FieldWidgetCellHoveredPortal
objectMetadataItem={objectMetadataItem}
fieldMetadataItem={fieldMetadataItem}
recordId={recordId}
instanceId={instanceId}
isHovered={isHovered}
onMouseLeave={handleMouseLeave}
/>
<FieldWidgetCellEditModePortal
objectMetadataItem={objectMetadataItem}
fieldMetadataItem={fieldMetadataItem}
recordId={recordId}
instanceId={instanceId}
/>
</RecordFieldComponentInstanceContext.Provider>
</RightDrawerProvider>
<FieldWidgetCellHoveredPortal
objectMetadataItem={objectMetadataItem}
fieldMetadataItem={fieldMetadataItem}
recordId={recordId}
instanceId={instanceId}
isHovered={isHovered}
onMouseLeave={handleMouseLeave}
/>
<FieldWidgetCellEditModePortal
objectMetadataItem={objectMetadataItem}
fieldMetadataItem={fieldMetadataItem}
recordId={recordId}
instanceId={instanceId}
/>
</RecordFieldComponentInstanceContext.Provider>
</RightDrawerProvider>
</RecordFieldsScopeContextProvider>
);
};
@@ -9,6 +9,7 @@ import { useGetButtonIcon } from '@/object-record/record-field/ui/hooks/useGetBu
import { useIsFieldInputOnly } from '@/object-record/record-field/ui/hooks/useIsFieldInputOnly';
import { useOpenFieldInputEditMode } from '@/object-record/record-field/ui/hooks/useOpenFieldInputEditMode';
import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import {
FieldInputEventContext,
type FieldInputClickOutsideEvent,
@@ -34,6 +35,7 @@ export const FieldWidgetInlineCell = ({
loading,
instanceIdPrefix,
}: FieldWidgetInlineCellProps) => {
const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow();
const {
fieldDefinition,
recordId,
@@ -141,11 +143,12 @@ export const FieldWidgetInlineCell = ({
.getLoadable(activeDropdownFocusIdState)
.getValue();
const expectedDropdownFocusId = getDropdownFocusIdForRecordField(
const expectedDropdownFocusId = getDropdownFocusIdForRecordField({
recordId,
fieldDefinition.fieldMetadataId,
'inline-cell',
);
fieldMetadataId: fieldDefinition.fieldMetadataId,
componentType: 'inline-cell',
instanceId: scopeInstanceId,
});
if (currentDropdownFocusId !== expectedDropdownFocusId) {
return;
@@ -165,6 +168,7 @@ export const FieldWidgetInlineCell = ({
recordId,
fieldDefinition.fieldMetadataId,
persistFieldFromFieldInputContext,
scopeInstanceId,
],
);
@@ -4,7 +4,7 @@ import { useContext } from 'react';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { useFieldFocus } from '@/object-record/record-field/ui/hooks/useFieldFocus';
import { useRecordInlineCellContext } from '@/object-record/record-inline-cell/components/RecordInlineCellContext';
import { FieldWidgetInlineCellValue } from '@/page-layout/widgets/field/components/FieldWidgetInlineCellValue';
import { RecordInlineCellValue } from '@/object-record/record-inline-cell/components/RecordInlineCellValue';
const StyledValueContainer = styled.div<{ readonly: boolean }>`
display: flex;
@@ -52,7 +52,7 @@ export const FieldWidgetInlineCellContainer = () => {
onMouseLeave={handleContainerMouseLeave}
>
<StyledValueContainer readonly={readonly ?? false} id={anchorId}>
<FieldWidgetInlineCellValue />
<RecordInlineCellValue />
</StyledValueContainer>
</StyledInlineCellBaseContainer>
);
@@ -1,45 +0,0 @@
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 { RecordInlineCellSkeletonLoader } from '@/object-record/record-inline-cell/components/RecordInlineCellSkeletonLoader';
const StyledClickableContainer = styled.div<{
readonly?: boolean;
isCentered?: boolean;
}>`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
width: 100%;
${({ isCentered }) =>
isCentered === true &&
`
justify-content: center;
`};
${({ readonly }) =>
!readonly &&
css`
cursor: pointer;
`};
`;
export const FieldWidgetInlineCellValue = () => {
const { readonly, loading, isCentered } = useRecordInlineCellContext();
if (loading === true) {
return <RecordInlineCellSkeletonLoader />;
}
return (
<StyledClickableContainer readonly={readonly} isCentered={isCentered}>
<RecordInlineCellDisplayMode isHovered={false}>
<FieldDisplay />
</RecordInlineCellDisplayMode>
</StyledClickableContainer>
);
};
@@ -1,4 +1,5 @@
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { RecordDetailMorphRelationSectionDropdown } from '@/object-record/record-field-list/record-detail-section/relation/components/RecordDetailMorphRelationSectionDropdown';
import { RecordDetailRelationSectionDropdown } from '@/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSectionDropdown';
import {
@@ -51,6 +52,8 @@ export const FieldWidgetRelationEditAction = ({
fieldDefinition,
recordId,
}: FieldWidgetRelationEditActionProps) => {
const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow();
const { objectMetadataItems } = useObjectMetadataItems();
const objectMetadataItem = objectMetadataItems.find(
(item) =>
@@ -89,6 +92,7 @@ export const FieldWidgetRelationEditAction = ({
getRecordFieldCardRelationPickerDropdownId({
fieldDefinition,
recordId,
instanceId: scopeInstanceId,
});
const isRelationSelectionDropdownOpen = useRecoilComponentValue(
@@ -1,11 +1,13 @@
export const getFieldWidgetInstanceId = ({
widgetId,
recordId,
fieldName,
isInRightDrawer,
}: {
widgetId: string;
recordId: string;
fieldName: string;
isInRightDrawer: boolean;
}): string => {
return `field-widget-${recordId}-${fieldName}${isInRightDrawer ? '-right-drawer' : ''}`;
return `${widgetId}-field-widget-${recordId}-${fieldName}${isInRightDrawer ? '-right-drawer' : ''}`;
};
@@ -3,6 +3,7 @@ import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/uti
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
import { useIsRecordReadOnly } from '@/object-record/read-only/hooks/useIsRecordReadOnly';
import { isRecordFieldReadOnly } from '@/object-record/read-only/utils/isRecordFieldReadOnly';
import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { RecordDetailSectionContainer } from '@/object-record/record-field-list/record-detail-section/components/RecordDetailSectionContainer';
import { RecordFieldListComponentInstanceContext } from '@/object-record/record-field-list/states/contexts/RecordFieldListComponentInstanceContext';
import { recordFieldListHoverPositionComponentState } from '@/object-record/record-field-list/states/recordFieldListHoverPositionComponentState';
@@ -44,11 +45,11 @@ type FieldsWidgetProps = {
widget: PageLayoutWidget;
};
export const FieldsWidget = ({ widget: _widget }: FieldsWidgetProps) => {
export const FieldsWidget = ({ widget }: FieldsWidgetProps) => {
const targetRecord = useTargetRecord();
const { isInRightDrawer } = useLayoutRenderingContext();
const instanceId = `fields-widget-${targetRecord.id}-${isInRightDrawer ? 'right-drawer' : ''}`;
const instanceId = `fields-widget-${widget.id}-${targetRecord.id}${isInRightDrawer ? '-right-drawer' : ''}`;
const { recordLoading, isPrefetchLoading } = useRecordShowContainerData({
objectRecordId: targetRecord.id,
@@ -103,93 +104,97 @@ export const FieldsWidget = ({ widget: _widget }: FieldsWidgetProps) => {
}
return (
<StyledContainer>
<RecordFieldListComponentInstanceContext.Provider
value={{
instanceId,
}}
>
{sectionsWithFieldIndices.map((section) => (
<RecordDetailSectionContainer key={section.id} title={section.title}>
<PropertyBox>
{isPrefetchLoading ? (
<PropertyBoxSkeletonLoader />
) : (
<>
{section.fields.map(
({ field: fieldMetadataItem, globalIndex }) => {
return (
<FieldContext.Provider
key={targetRecord.id + fieldMetadataItem.id}
value={{
recordId: targetRecord.id,
maxWidth: 200,
isLabelIdentifier: false,
fieldDefinition:
formatFieldMetadataItemAsColumnDefinition({
field: fieldMetadataItem,
position: globalIndex,
objectMetadataItem,
showLabel: true,
labelWidth: 90,
}),
useUpdateRecord: useUpdateOneObjectRecordMutation,
isDisplayModeFixHeight: true,
isRecordFieldReadOnly: isRecordFieldReadOnly({
isRecordReadOnly,
objectPermissions:
getObjectPermissionsFromMapByObjectMetadataId({
objectPermissionsByObjectMetadataId,
objectMetadataId: objectMetadataItem.id,
}),
fieldMetadataItem: {
id: fieldMetadataItem.id,
isUIReadOnly:
fieldMetadataItem.isUIReadOnly ?? false,
},
}),
onMouseEnter: () =>
setRecordFieldListHoverPosition(globalIndex),
anchorId: `${getRecordFieldInputInstanceId({
recordId: targetRecord.id,
fieldName: fieldMetadataItem.name,
prefix: instanceId,
})}`,
}}
>
<RecordFieldComponentInstanceContext.Provider
<RecordFieldsScopeContextProvider value={{ scopeInstanceId: instanceId }}>
<StyledContainer>
<RecordFieldListComponentInstanceContext.Provider
value={{
instanceId,
}}
>
{sectionsWithFieldIndices.map((section) => (
<RecordDetailSectionContainer
key={section.id}
title={section.title}
>
<PropertyBox>
{isPrefetchLoading ? (
<PropertyBoxSkeletonLoader />
) : (
<>
{section.fields.map(
({ field: fieldMetadataItem, globalIndex }) => {
return (
<FieldContext.Provider
key={targetRecord.id + fieldMetadataItem.id}
value={{
instanceId: getRecordFieldInputInstanceId({
recordId: targetRecord.id,
maxWidth: 200,
isLabelIdentifier: false,
fieldDefinition:
formatFieldMetadataItemAsColumnDefinition({
field: fieldMetadataItem,
position: globalIndex,
objectMetadataItem,
showLabel: true,
labelWidth: 90,
}),
useUpdateRecord: useUpdateOneObjectRecordMutation,
isDisplayModeFixHeight: true,
isRecordFieldReadOnly: isRecordFieldReadOnly({
isRecordReadOnly,
objectPermissions:
getObjectPermissionsFromMapByObjectMetadataId(
{
objectPermissionsByObjectMetadataId,
objectMetadataId: objectMetadataItem.id,
},
),
fieldMetadataItem: {
id: fieldMetadataItem.id,
isUIReadOnly:
fieldMetadataItem.isUIReadOnly ?? false,
},
}),
onMouseEnter: () =>
setRecordFieldListHoverPosition(globalIndex),
anchorId: `${getRecordFieldInputInstanceId({
recordId: targetRecord.id,
fieldName: fieldMetadataItem.name,
prefix: instanceId,
}),
})}`,
}}
>
<RecordInlineCell
loading={recordLoading}
instanceIdPrefix={instanceId}
/>
</RecordFieldComponentInstanceContext.Provider>
</FieldContext.Provider>
);
},
)}
</>
)}
</PropertyBox>
</RecordDetailSectionContainer>
))}
<RecordFieldComponentInstanceContext.Provider
value={{
instanceId: getRecordFieldInputInstanceId({
recordId: targetRecord.id,
fieldName: fieldMetadataItem.name,
prefix: instanceId,
}),
}}
>
<RecordInlineCell loading={recordLoading} />
</RecordFieldComponentInstanceContext.Provider>
</FieldContext.Provider>
);
},
)}
</>
)}
</PropertyBox>
</RecordDetailSectionContainer>
))}
<FieldsWidgetCellHoveredPortal
objectMetadataItem={objectMetadataItem}
recordId={targetRecord.id}
/>
<FieldsWidgetCellEditModePortal
objectMetadataItem={objectMetadataItem}
recordId={targetRecord.id}
/>
</RecordFieldListComponentInstanceContext.Provider>
</StyledContainer>
<FieldsWidgetCellHoveredPortal
objectMetadataItem={objectMetadataItem}
recordId={targetRecord.id}
/>
<FieldsWidgetCellEditModePortal
objectMetadataItem={objectMetadataItem}
recordId={targetRecord.id}
/>
</RecordFieldListComponentInstanceContext.Provider>
</StyledContainer>
</RecordFieldsScopeContextProvider>
);
};
@@ -1,3 +1,4 @@
import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField';
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
@@ -5,15 +6,17 @@ import { useContext, useEffect } from 'react';
export const StorybookFieldInputDropdownFocusIdSetterEffect = () => {
const { recordId, fieldDefinition } = useContext(FieldContext);
const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow();
const { setActiveDropdownFocusIdAndMemorizePrevious } =
useSetActiveDropdownFocusIdAndMemorizePrevious();
const fieldDropdownFocusIdTableCell = getDropdownFocusIdForRecordField(
const fieldDropdownFocusIdTableCell = getDropdownFocusIdForRecordField({
recordId,
fieldDefinition.fieldMetadataId,
'table-cell',
);
fieldMetadataId: fieldDefinition.fieldMetadataId,
componentType: 'table-cell',
instanceId: scopeInstanceId,
});
useEffect(() => {
setActiveDropdownFocusIdAndMemorizePrevious(fieldDropdownFocusIdTableCell);