Fix safari broken table (#11417)

In this PR:
- deprecate usage of useInView (react-intersection-observer) for record
table
- fixes #11254
This commit is contained in:
Charles Bochet
2025-04-07 16:33:02 +02:00
committed by GitHub
parent 26504f02a3
commit 59ae978ee3
24 changed files with 223 additions and 203 deletions

View File

@ -3,8 +3,8 @@ import { DEFAULT_QUERY_PAGE_SIZE } from '@/object-record/constants/DefaultQueryP
import { UseFindManyRecordsParams } from '@/object-record/hooks/useFetchMoreRecordsWithPagination';
import { useLazyFindManyRecords } from '@/object-record/hooks/useLazyFindManyRecords';
import { useCallback, useState } from 'react';
import { sleep } from '~/utils/sleep';
import { isDefined } from 'twenty-shared/utils';
import { sleep } from '~/utils/sleep';
type UseLazyFetchAllRecordIdsParams<T> = Omit<
UseFindManyRecordsParams<T>,

View File

@ -14,7 +14,6 @@ export const FieldContextProvider = ({
fieldMetadataName,
fieldPosition,
isLabelIdentifier = false,
isLabelHidden,
objectNameSingular,
objectRecordId,
customUseUpdateOneObjectHook,
@ -25,7 +24,6 @@ export const FieldContextProvider = ({
fieldMetadataName: string;
fieldPosition: number;
isLabelIdentifier?: boolean;
isLabelHidden?: boolean;
objectNameSingular: string;
objectRecordId: string;
customUseUpdateOneObjectHook?: RecordUpdateHook;
@ -65,7 +63,6 @@ export const FieldContextProvider = ({
value={{
recordId: objectRecordId,
isLabelIdentifier,
isLabelHidden,
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
field: fieldMetadataItem,
showLabel: true,

View File

@ -35,7 +35,6 @@ export type GenericFieldContextType = {
isReadOnly: boolean;
onOpenEditMode?: () => void;
onCloseEditMode?: () => void;
isLabelHidden?: boolean;
};
export const FieldContext = createContext<GenericFieldContextType>(

View File

@ -4,12 +4,13 @@ import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlur
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { ChipFieldDisplay } from '@/object-record/record-field/meta-types/display/components/ChipFieldDisplay';
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
import { ComponentDecorator } from 'twenty-ui/testing';
import { ChipGeneratorsDecorator } from '~/testing/decorators/ChipGeneratorsDecorator';
import { getFieldDecorator } from '~/testing/decorators/getFieldDecorator';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
import { getFieldDecorator } from '~/testing/decorators/getFieldDecorator';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
import { getProfilingStory } from '~/testing/profiling/utils/getProfilingStory';
import { ComponentDecorator } from 'twenty-ui/testing';
const meta: Meta = {
title: 'UI/Data/Field/Display/ChipFieldDisplay',
@ -22,18 +23,25 @@ const meta: Meta = {
)!;
return (
<RecordIndexContextProvider
<RecordTableComponentInstanceContext.Provider
value={{
indexIdentifierUrl: () => '',
onIndexRecordsLoaded: () => {},
objectNamePlural: CoreObjectNamePlural.Company,
objectNameSingular: CoreObjectNameSingular.Company,
objectMetadataItem: companyObjectMetadataItem,
recordIndexId: instanceId,
instanceId,
onColumnsChange: () => {},
}}
>
<Story />
</RecordIndexContextProvider>
<RecordIndexContextProvider
value={{
indexIdentifierUrl: () => '',
onIndexRecordsLoaded: () => {},
objectNamePlural: CoreObjectNamePlural.Company,
objectNameSingular: CoreObjectNameSingular.Company,
objectMetadataItem: companyObjectMetadataItem,
recordIndexId: instanceId,
}}
>
<Story />
</RecordIndexContextProvider>
</RecordTableComponentInstanceContext.Provider>
);
},
MemoryRouterDecorator,

View File

@ -8,17 +8,23 @@ import { isFieldText } from '@/object-record/record-field/types/guards/isFieldTe
import { useRecordValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { isFieldActor } from '@/object-record/record-field/types/guards/isFieldActor';
import { FieldContext } from '../../contexts/FieldContext';
import { isRecordTableScrolledLeftComponentState } from '@/object-record/record-table/states/isRecordTableScrolledLeftComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { isDefined } from 'twenty-shared/utils';
import { useIsMobile } from 'twenty-ui/utilities';
import { FieldContext } from '../../contexts/FieldContext';
export const useChipFieldDisplay = () => {
const {
recordId,
fieldDefinition,
isLabelIdentifier,
labelIdentifierLink,
isLabelHidden,
} = useContext(FieldContext);
const { recordId, fieldDefinition, isLabelIdentifier, labelIdentifierLink } =
useContext(FieldContext);
const isMobile = useIsMobile();
const isRecordTableScrolledLeftComponent = useRecoilComponentValueV2(
isRecordTableScrolledLeftComponentState,
);
const isLabelHidden =
isMobile && isLabelIdentifier && !isRecordTableScrolledLeftComponent;
const { chipGeneratorPerObjectPerField } = useContext(
PreComputedChipGeneratorsContext,

View File

@ -52,7 +52,6 @@ export const RecordTableRecordGroupRows = () => {
recordId={recordId}
rowIndexForFocus={rowIndex}
rowIndexForDrag={rowIndexInGroup}
isPendingRow={!isRecordGroupTableSectionToggled}
/>
);
})}

View File

@ -24,6 +24,7 @@ const StyledTableContainer = styled.div`
display: flex;
flex-direction: column;
position: relative;
width: 100%;
`;
type RecordTableWithWrappersProps = {

View File

@ -23,9 +23,9 @@ import { RecordTableContextProvider } from '@/object-record/record-table/context
import { RecordTableRowContextProvider } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { RecordTableRowDraggableContextProvider } from '@/object-record/record-table/contexts/RecordTableRowDraggableContext';
import { RecordTableCellFieldContextWrapper } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldContextWrapper';
import { ComponentDecorator } from 'twenty-ui/testing';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
import { mockPerformance } from './mock';
import { ComponentDecorator } from 'twenty-ui/testing';
const RelationFieldValueSetterEffect = () => {
const setEntity = useSetRecoilState(
@ -110,7 +110,6 @@ const meta: Meta = {
mockPerformance.entityValue.__typename.toLocaleLowerCase(),
}) + mockPerformance.recordId,
isSelected: false,
isPendingRow: false,
inView: true,
}}
>

View File

@ -7,7 +7,6 @@ export type RecordTableRowContextValue = {
rowIndex: number;
isSelected: boolean;
inView: boolean;
isPendingRow?: boolean;
isReadOnly?: boolean;
};

View File

@ -33,6 +33,8 @@ export const RecordTableBodyLoading = () => {
}}
>
<RecordTableTr
recordId={`${rowIndex}`}
focusIndex={0}
isDragging={false}
data-testid={`row-id-${rowIndex}`}
data-selectable-id={`row-id-${rowIndex}`}

View File

@ -5,9 +5,6 @@ import { RecordTableCellBaseContainer } from '@/object-record/record-table/recor
import { RecordTableCellSoftFocusMode } from '@/object-record/record-table/record-table-cell/components/RecordTableCellSoftFocusMode';
import { RecordTableCellSoftFocusModeHotkeysSetterEffect } from '@/object-record/record-table/record-table-cell/components/RecordTableCellSoftFocusModeHotkeysSetterEffect';
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState';
import { useRecoilValue } from 'recoil';
import { RecordTableCellDisplayMode } from './RecordTableCellDisplayMode';
import { RecordTableCellEditMode } from './RecordTableCellEditMode';
@ -26,17 +23,13 @@ export const RecordTableCellContainer = ({
}: RecordTableCellContainerProps) => {
const { hasSoftFocus, isInEditMode } = useContext(RecordTableCellContext);
const currentHotkeyScope = useRecoilValue(currentHotkeyScopeState);
return (
<RecordTableCellBaseContainer>
{isInEditMode ? (
<RecordTableCellEditMode>{editModeContent}</RecordTableCellEditMode>
) : hasSoftFocus ? (
<>
{currentHotkeyScope.scope === TableHotkeyScope.TableSoftFocus && (
<RecordTableCellSoftFocusModeHotkeysSetterEffect />
)}
<RecordTableCellSoftFocusModeHotkeysSetterEffect />
<RecordTableCellSoftFocusMode
editModeContent={editModeContent}
nonEditModeContent={nonEditModeContent}

View File

@ -14,11 +14,8 @@ import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/co
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
import { SelectFieldHotkeyScope } from '@/object-record/select/types/SelectFieldHotkeyScope';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ReactNode, useContext } from 'react';
import { useIsMobile } from 'twenty-ui/utilities';
import { RelationDefinitionType } from '~/generated-metadata/graphql';
import { isRecordTableScrolledLeftComponentState } from '../../states/isRecordTableScrolledLeftComponentState';
export const RecordTableCellFieldContext = ({
children,
@ -31,16 +28,6 @@ export const RecordTableCellFieldContext = ({
const { recordId, isReadOnly: isTableRowReadOnly } =
useRecordTableRowContextOrThrow();
const updateRecord = useContext(RecordUpdateContext);
const isMobile = useIsMobile();
const isRecordTableScrolledLeft = useRecoilComponentValueV2(
isRecordTableScrolledLeftComponentState,
);
const isLabelHidden =
isMobile &&
columnDefinition?.isLabelIdentifier &&
!isRecordTableScrolledLeft;
const computedHotkeyScope = (
columnDefinition: ColumnDefinition<FieldMetadata>,
@ -93,7 +80,6 @@ export const RecordTableCellFieldContext = ({
objectMetadataItem,
}),
displayedMaxRows: 1,
isLabelHidden,
isReadOnly: isFieldReadOnly,
}}
>

View File

@ -1,27 +1,21 @@
import styled from '@emotion/styled';
import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { useRecordTableRowDraggableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowDraggableContext';
import { RecordTableTd } from '@/object-record/record-table/record-table-cell/components/RecordTableTd';
import { css } from '@emotion/react';
import { IconListViewGrip } from 'twenty-ui/input';
export const TABLE_CELL_GRIP_WIDTH = '16px';
const StyledContainer = styled.div<{ isPendingRow?: boolean }>`
const StyledContainer = styled.div`
height: 32px;
width: ${TABLE_CELL_GRIP_WIDTH};
border-color: transparent;
cursor: grab;
display: flex;
${({ isPendingRow }) =>
!isPendingRow
? css`
&:hover .icon {
opacity: 1;
}
`
: ''};
&:hover .icon {
opacity: 1;
}
z-index: 200;
`;
@ -32,8 +26,6 @@ const StyledIconWrapper = styled.div<{ isDragging: boolean }>`
`;
export const RecordTableCellGrip = () => {
const { isPendingRow } = useRecordTableRowContextOrThrow();
const { dragHandleProps, isDragging } =
useRecordTableRowDraggableContextOrThrow();
@ -45,7 +37,7 @@ export const RecordTableCellGrip = () => {
hasRightBorder={false}
hasBottomBorder={false}
>
<StyledContainer isPendingRow={isPendingRow}>
<StyledContainer>
<StyledIconWrapper className="icon" isDragging={isDragging}>
<IconListViewGrip />
</StyledIconWrapper>

View File

@ -12,9 +12,12 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { isNonTextWritingKey } from '@/ui/utilities/hotkey/utils/isNonTextWritingKey';
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState';
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
export const RecordTableCellSoftFocusModeHotkeysSetterEffect = () => {
const currentHotkeyScope = useRecoilValue(currentHotkeyScopeState);
const { openTableCell } = useOpenRecordTableCellFromCell();
const { isReadOnly } = useContext(FieldContext);
@ -29,10 +32,14 @@ export const RecordTableCellSoftFocusModeHotkeysSetterEffect = () => {
const clearField = useClearField();
useEffect(() => {
if (currentHotkeyScope.scope !== TableHotkeyScope.TableSoftFocus) {
return;
}
if (!isSoftFocusUsingMouse) {
scrollRef.current?.scrollIntoView({ block: 'nearest' });
}
}, [isSoftFocusUsingMouse]);
}, [currentHotkeyScope.scope, isSoftFocusUsingMouse]);
useScopedHotkeys(
[Key.Backspace, Key.Delete],

View File

@ -9,7 +9,6 @@ export const recordTableRowContextValue: RecordTableRowContextValue = {
recordId: 'recordId',
pathToShowPage: '/',
objectNameSingular: 'objectNameSingular',
isPendingRow: false,
inView: true,
};

View File

@ -65,8 +65,9 @@ export const RecordTableActionRow = ({
return (
<StyledRecordTableDraggableTr
draggableId={draggableId}
recordId={draggableId}
draggableIndex={draggableIndex}
focusIndex={draggableIndex}
onClick={onClick}
isDragDisabled
>

View File

@ -1,55 +1,44 @@
import { useTheme } from '@emotion/react';
import { Draggable, DraggableId } from '@hello-pangea/dnd';
import { ReactNode, forwardRef } from 'react';
import { Draggable } from '@hello-pangea/dnd';
import { ReactNode } from 'react';
import { RecordTableRowDraggableContextProvider } from '@/object-record/record-table/contexts/RecordTableRowDraggableContext';
import { RecordTableTr } from '@/object-record/record-table/record-table-row/components/RecordTableTr';
import styled from '@emotion/styled';
import { RecordTableTrEffect } from '@/object-record/record-table/record-table-row/components/RecordTableTrEffect';
type RecordTableDraggableTrProps = {
className?: string;
draggableId: DraggableId;
recordId: string;
draggableIndex: number;
focusIndex: number;
isDragDisabled?: boolean;
onClick?: (event: React.MouseEvent<HTMLTableRowElement>) => void;
children: ReactNode;
};
const StyledAbsoluteInViewContainer = styled.td`
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
z-index: -1;
`;
export const RecordTableDraggableTr = ({
className,
recordId,
draggableIndex,
focusIndex,
isDragDisabled,
onClick,
children,
}: RecordTableDraggableTrProps) => {
const theme = useTheme();
export const RecordTableDraggableTr = forwardRef<
HTMLTableCellElement,
RecordTableDraggableTrProps
>(
(
{
className,
draggableId,
draggableIndex,
isDragDisabled,
onClick,
children,
},
ref,
) => {
const theme = useTheme();
return (
<Draggable
draggableId={draggableId}
index={draggableIndex}
isDragDisabled={isDragDisabled}
>
{(draggableProvided, draggableSnapshot) => (
return (
<Draggable
draggableId={recordId}
index={draggableIndex}
isDragDisabled={isDragDisabled}
>
{(draggableProvided, draggableSnapshot) => (
<>
<RecordTableTrEffect recordId={recordId} />
<RecordTableTr
recordId={recordId}
focusIndex={focusIndex}
ref={draggableProvided.innerRef}
className={className}
// eslint-disable-next-line react/jsx-props-no-spreading
@ -64,8 +53,9 @@ export const RecordTableDraggableTr = forwardRef<
: 'transparent',
}}
isDragging={draggableSnapshot.isDragging}
data-testid={`row-id-${draggableId}`}
data-selectable-id={draggableId}
data-testid={`row-id-${recordId}`}
data-virtualized-id={recordId}
data-selectable-id={recordId}
onClick={onClick}
>
<RecordTableRowDraggableContextProvider
@ -75,13 +65,10 @@ export const RecordTableDraggableTr = forwardRef<
}}
>
{children}
<StyledAbsoluteInViewContainer
ref={ref}
></StyledAbsoluteInViewContainer>
</RecordTableRowDraggableContextProvider>
</RecordTableTr>
)}
</Draggable>
);
},
);
</>
)}
</Draggable>
);
};

View File

@ -3,33 +3,30 @@ import { RecordTableCellCheckbox } from '@/object-record/record-table/record-tab
import { RecordTableCellGrip } from '@/object-record/record-table/record-table-cell/components/RecordTableCellGrip';
import { RecordTableLastEmptyCell } from '@/object-record/record-table/record-table-cell/components/RecordTableLastEmptyCell';
import { RecordTableCells } from '@/object-record/record-table/record-table-row/components/RecordTableCells';
import { RecordTableRowWrapper } from '@/object-record/record-table/record-table-row/components/RecordTableRowWrapper';
import { RecordTableDraggableTr } from '@/object-record/record-table/record-table-row/components/RecordTableDraggableTr';
type RecordTableRowProps = {
recordId: string;
rowIndexForFocus: number;
rowIndexForDrag: number;
isPendingRow?: boolean;
};
export const RecordTableRow = ({
recordId,
rowIndexForFocus,
rowIndexForDrag,
isPendingRow,
}: RecordTableRowProps) => {
return (
<RecordTableRowWrapper
<RecordTableDraggableTr
recordId={recordId}
rowIndexForFocus={rowIndexForFocus}
rowIndexForDrag={rowIndexForDrag}
isPendingRow={isPendingRow}
draggableIndex={rowIndexForDrag}
focusIndex={rowIndexForFocus}
>
<RecordTableCellGrip />
<RecordTableCellCheckbox />
<RecordTableCells />
<RecordTableLastEmptyCell />
<RecordValueSetterEffect recordId={recordId} />
</RecordTableRowWrapper>
</RecordTableDraggableTr>
);
};

View File

@ -1,77 +0,0 @@
import { ReactNode, useEffect } from 'react';
import { getBasePathToShowPage } from '@/object-metadata/utils/getBasePathToShowPage';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { RecordTableRowContextProvider } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { RecordTableDraggableTr } from '@/object-record/record-table/record-table-row/components/RecordTableDraggableTr';
import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState';
import { useScrollWrapperElement } from '@/ui/utilities/scroll/hooks/useScrollWrapperElement';
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
import { useInView } from 'react-intersection-observer';
type RecordTableRowWrapperProps = {
recordId: string;
rowIndexForFocus: number;
rowIndexForDrag: number;
isPendingRow?: boolean;
children: ReactNode;
};
export const RecordTableRowWrapper = ({
recordId,
rowIndexForFocus,
rowIndexForDrag,
isPendingRow,
children,
}: RecordTableRowWrapperProps) => {
const { objectMetadataItem } = useRecordTableContextOrThrow();
const currentRowSelected = useRecoilComponentFamilyValueV2(
isRowSelectedComponentFamilyState,
recordId,
);
const { onIndexRecordsLoaded } = useRecordIndexContextOrThrow();
const { scrollWrapperHTMLElement } = useScrollWrapperElement();
const { ref: elementRef, inView } = useInView({
root: scrollWrapperHTMLElement,
rootMargin: '1000px',
});
// TODO: find a better way to emit this event
useEffect(() => {
if (inView) {
onIndexRecordsLoaded?.();
}
}, [inView, onIndexRecordsLoaded]);
return (
<RecordTableDraggableTr
ref={elementRef}
key={recordId}
draggableId={recordId}
draggableIndex={rowIndexForDrag}
isDragDisabled={isPendingRow}
>
<RecordTableRowContextProvider
value={{
recordId,
rowIndex: rowIndexForFocus,
pathToShowPage:
getBasePathToShowPage({
objectNameSingular: objectMetadataItem.nameSingular,
}) + recordId,
objectNameSingular: objectMetadataItem.nameSingular,
isSelected: currentRowSelected,
isPendingRow,
inView,
}}
>
{children}
</RecordTableRowContextProvider>
</RecordTableDraggableTr>
);
};

View File

@ -1,4 +1,11 @@
import { getBasePathToShowPage } from '@/object-metadata/utils/getBasePathToShowPage';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { RecordTableRowContextProvider } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState';
import { isRowVisibleComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowVisibleComponentFamilyState';
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
import styled from '@emotion/styled';
import { ReactNode, forwardRef } from 'react';
const StyledTr = styled.tr<{ isDragging: boolean }>`
position: relative;
@ -9,4 +16,51 @@ const StyledTr = styled.tr<{ isDragging: boolean }>`
transition: border-left-color 0.2s ease-in-out;
`;
export const RecordTableTr = StyledTr;
type RecordTableTrProps = {
children: ReactNode;
recordId: string;
focusIndex: number;
isDragging?: boolean;
} & React.ComponentProps<typeof StyledTr>;
export const RecordTableTr = forwardRef<
HTMLTableRowElement,
RecordTableTrProps
>(({ children, recordId, focusIndex, isDragging = false, ...props }, ref) => {
const { objectMetadataItem } = useRecordTableContextOrThrow();
const currentRowSelected = useRecoilComponentFamilyValueV2(
isRowSelectedComponentFamilyState,
recordId,
);
const isRowVisible = useRecoilComponentFamilyValueV2(
isRowVisibleComponentFamilyState,
recordId,
);
return (
<RecordTableRowContextProvider
value={{
recordId: recordId,
rowIndex: focusIndex,
pathToShowPage:
getBasePathToShowPage({
objectNameSingular: objectMetadataItem.nameSingular,
}) + recordId,
objectNameSingular: objectMetadataItem.nameSingular,
isSelected: currentRowSelected,
inView: isRowVisible,
}}
>
<StyledTr
data-virtualized-id={recordId}
isDragging={isDragging}
ref={ref}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
>
{children}
</StyledTr>
</RecordTableRowContextProvider>
);
});

View File

@ -0,0 +1,61 @@
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { isRowVisibleComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowVisibleComponentFamilyState';
import { useScrollWrapperElement } from '@/ui/utilities/scroll/hooks/useScrollWrapperElement';
import { useSetRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentFamilyStateV2';
import { useEffect } from 'react';
type RecordTableTrEffectProps = {
recordId: string;
};
export const RecordTableTrEffect = ({ recordId }: RecordTableTrEffectProps) => {
const { onIndexRecordsLoaded } = useRecordIndexContextOrThrow();
const { scrollWrapperHTMLElement } = useScrollWrapperElement();
const setIsRowVisible = useSetRecoilComponentFamilyStateV2(
isRowVisibleComponentFamilyState,
recordId,
);
useEffect(() => {
const options = {
root: scrollWrapperHTMLElement,
rootMargin: '1000px',
threshold: 0.1,
};
const callback = (entries: IntersectionObserverEntry[]) => {
entries.forEach((entry) => {
const isIntersecting = entry.isIntersecting;
if (isIntersecting) {
onIndexRecordsLoaded?.();
setIsRowVisible(true);
}
if (!isIntersecting) {
setIsRowVisible(false);
}
});
};
const observer = new IntersectionObserver(callback, options);
observer.observe(
document.querySelector(
`[data-virtualized-id="${recordId}"]`,
) as HTMLElement,
);
return () => {
observer.disconnect();
};
}, [
onIndexRecordsLoaded,
recordId,
scrollWrapperHTMLElement,
setIsRowVisible,
]);
return <></>;
};

View File

@ -0,0 +1,11 @@
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2';
export const isRowVisibleComponentFamilyState = createComponentFamilyStateV2<
boolean,
string
>({
key: 'isRowVisibleComponentFamilyState',
defaultValue: true,
componentInstanceContext: RecordTableComponentInstanceContext,
});

View File

@ -20,7 +20,6 @@ import { flushSync } from 'react-dom';
import { Keys } from 'react-hotkeys-hook';
import { useRecoilCallback } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
import { sleep } from '~/utils/sleep';
import { useDropdown } from '../hooks/useDropdown';
const StyledDropdownFallbackAnchor = styled.div`
@ -115,8 +114,6 @@ export const Dropdown = ({
dropdownHotkeyScope,
);
await sleep(100);
toggleDropdown();
onClickOutside?.();
},

View File

@ -14,6 +14,8 @@ const StyledScrollWrapper = styled.div`
&.scroll-wrapper-y-enabled {
overflow-y: scroll;
}
overflow-x: hidden;
overflow-y: hidden;
display: flex;
width: 100%;
height: 100%;