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:
committed by
GitHub
parent
6a4974e285
commit
06d0ac13c4
+36
-31
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
+66
-61
@@ -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>
|
||||
}
|
||||
|
||||
+32
-24
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
+22
-16
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { createRequiredContext } from '~/utils/createRequiredContext';
|
||||
|
||||
export type RecordFieldsScopeContextValue = {
|
||||
scopeInstanceId: string;
|
||||
};
|
||||
|
||||
export const [
|
||||
RecordFieldsScopeContextProvider,
|
||||
useRecordFieldsScopeContextOrThrow,
|
||||
] = createRequiredContext<RecordFieldsScopeContextValue>(
|
||||
'RecordFieldsScopeContext',
|
||||
);
|
||||
+3
@@ -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(
|
||||
|
||||
+3
@@ -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();
|
||||
|
||||
+3
@@ -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();
|
||||
|
||||
+23
-11
@@ -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
|
||||
|
||||
+3
@@ -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(
|
||||
|
||||
+3
@@ -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
@@ -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();
|
||||
|
||||
+6
-1
@@ -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>
|
||||
),
|
||||
|
||||
+10
-5
@@ -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>
|
||||
|
||||
+10
-5
@@ -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
-5
@@ -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,
|
||||
],
|
||||
);
|
||||
|
||||
+2
-1
@@ -101,8 +101,9 @@ export const RecordInlineCellAnchoredPortal = ({
|
||||
>
|
||||
<RecordInlineCellAnchoredPortalContext>
|
||||
{children}
|
||||
|
||||
<RecordInlineCellCloseOnCommandMenuOpeningEffect />
|
||||
</RecordInlineCellAnchoredPortalContext>
|
||||
<RecordInlineCellCloseOnCommandMenuOpeningEffect />
|
||||
</RecordFieldComponentInstanceContext.Provider>,
|
||||
anchorElement,
|
||||
)}
|
||||
|
||||
+10
-7
@@ -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 {
|
||||
|
||||
+11
-6
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
+3
-1
@@ -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}`;
|
||||
|
||||
+22
-16
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
+10
-5
@@ -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,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
+12
-6
@@ -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}`;
|
||||
};
|
||||
|
||||
+65
@@ -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',
|
||||
|
||||
+24
-18
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
+7
-4
@@ -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(
|
||||
|
||||
+7
-4
@@ -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) {
|
||||
|
||||
+63
-56
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
+8
-4
@@ -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,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
+2
-2
@@ -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>
|
||||
);
|
||||
|
||||
-45
@@ -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>
|
||||
);
|
||||
};
|
||||
+4
@@ -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(
|
||||
|
||||
+3
-1
@@ -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' : ''}`;
|
||||
};
|
||||
|
||||
+88
-83
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
+7
-4
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user