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:
@ -3,8 +3,8 @@ import { DEFAULT_QUERY_PAGE_SIZE } from '@/object-record/constants/DefaultQueryP
|
|||||||
import { UseFindManyRecordsParams } from '@/object-record/hooks/useFetchMoreRecordsWithPagination';
|
import { UseFindManyRecordsParams } from '@/object-record/hooks/useFetchMoreRecordsWithPagination';
|
||||||
import { useLazyFindManyRecords } from '@/object-record/hooks/useLazyFindManyRecords';
|
import { useLazyFindManyRecords } from '@/object-record/hooks/useLazyFindManyRecords';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { sleep } from '~/utils/sleep';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { sleep } from '~/utils/sleep';
|
||||||
|
|
||||||
type UseLazyFetchAllRecordIdsParams<T> = Omit<
|
type UseLazyFetchAllRecordIdsParams<T> = Omit<
|
||||||
UseFindManyRecordsParams<T>,
|
UseFindManyRecordsParams<T>,
|
||||||
|
|||||||
@ -14,7 +14,6 @@ export const FieldContextProvider = ({
|
|||||||
fieldMetadataName,
|
fieldMetadataName,
|
||||||
fieldPosition,
|
fieldPosition,
|
||||||
isLabelIdentifier = false,
|
isLabelIdentifier = false,
|
||||||
isLabelHidden,
|
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
objectRecordId,
|
objectRecordId,
|
||||||
customUseUpdateOneObjectHook,
|
customUseUpdateOneObjectHook,
|
||||||
@ -25,7 +24,6 @@ export const FieldContextProvider = ({
|
|||||||
fieldMetadataName: string;
|
fieldMetadataName: string;
|
||||||
fieldPosition: number;
|
fieldPosition: number;
|
||||||
isLabelIdentifier?: boolean;
|
isLabelIdentifier?: boolean;
|
||||||
isLabelHidden?: boolean;
|
|
||||||
objectNameSingular: string;
|
objectNameSingular: string;
|
||||||
objectRecordId: string;
|
objectRecordId: string;
|
||||||
customUseUpdateOneObjectHook?: RecordUpdateHook;
|
customUseUpdateOneObjectHook?: RecordUpdateHook;
|
||||||
@ -65,7 +63,6 @@ export const FieldContextProvider = ({
|
|||||||
value={{
|
value={{
|
||||||
recordId: objectRecordId,
|
recordId: objectRecordId,
|
||||||
isLabelIdentifier,
|
isLabelIdentifier,
|
||||||
isLabelHidden,
|
|
||||||
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
|
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
|
||||||
field: fieldMetadataItem,
|
field: fieldMetadataItem,
|
||||||
showLabel: true,
|
showLabel: true,
|
||||||
|
|||||||
@ -35,7 +35,6 @@ export type GenericFieldContextType = {
|
|||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
onOpenEditMode?: () => void;
|
onOpenEditMode?: () => void;
|
||||||
onCloseEditMode?: () => void;
|
onCloseEditMode?: () => void;
|
||||||
isLabelHidden?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FieldContext = createContext<GenericFieldContextType>(
|
export const FieldContext = createContext<GenericFieldContextType>(
|
||||||
|
|||||||
@ -4,12 +4,13 @@ import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlur
|
|||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { ChipFieldDisplay } from '@/object-record/record-field/meta-types/display/components/ChipFieldDisplay';
|
import { ChipFieldDisplay } from '@/object-record/record-field/meta-types/display/components/ChipFieldDisplay';
|
||||||
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
|
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 { ChipGeneratorsDecorator } from '~/testing/decorators/ChipGeneratorsDecorator';
|
||||||
import { getFieldDecorator } from '~/testing/decorators/getFieldDecorator';
|
|
||||||
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
|
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
|
||||||
|
import { getFieldDecorator } from '~/testing/decorators/getFieldDecorator';
|
||||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
import { getProfilingStory } from '~/testing/profiling/utils/getProfilingStory';
|
import { getProfilingStory } from '~/testing/profiling/utils/getProfilingStory';
|
||||||
import { ComponentDecorator } from 'twenty-ui/testing';
|
|
||||||
|
|
||||||
const meta: Meta = {
|
const meta: Meta = {
|
||||||
title: 'UI/Data/Field/Display/ChipFieldDisplay',
|
title: 'UI/Data/Field/Display/ChipFieldDisplay',
|
||||||
@ -22,18 +23,25 @@ const meta: Meta = {
|
|||||||
)!;
|
)!;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RecordIndexContextProvider
|
<RecordTableComponentInstanceContext.Provider
|
||||||
value={{
|
value={{
|
||||||
indexIdentifierUrl: () => '',
|
instanceId,
|
||||||
onIndexRecordsLoaded: () => {},
|
onColumnsChange: () => {},
|
||||||
objectNamePlural: CoreObjectNamePlural.Company,
|
|
||||||
objectNameSingular: CoreObjectNameSingular.Company,
|
|
||||||
objectMetadataItem: companyObjectMetadataItem,
|
|
||||||
recordIndexId: instanceId,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Story />
|
<RecordIndexContextProvider
|
||||||
</RecordIndexContextProvider>
|
value={{
|
||||||
|
indexIdentifierUrl: () => '',
|
||||||
|
onIndexRecordsLoaded: () => {},
|
||||||
|
objectNamePlural: CoreObjectNamePlural.Company,
|
||||||
|
objectNameSingular: CoreObjectNameSingular.Company,
|
||||||
|
objectMetadataItem: companyObjectMetadataItem,
|
||||||
|
recordIndexId: instanceId,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Story />
|
||||||
|
</RecordIndexContextProvider>
|
||||||
|
</RecordTableComponentInstanceContext.Provider>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
MemoryRouterDecorator,
|
MemoryRouterDecorator,
|
||||||
|
|||||||
@ -8,17 +8,23 @@ import { isFieldText } from '@/object-record/record-field/types/guards/isFieldTe
|
|||||||
import { useRecordValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
|
import { useRecordValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
|
||||||
|
|
||||||
import { isFieldActor } from '@/object-record/record-field/types/guards/isFieldActor';
|
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 { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { useIsMobile } from 'twenty-ui/utilities';
|
||||||
|
import { FieldContext } from '../../contexts/FieldContext';
|
||||||
|
|
||||||
export const useChipFieldDisplay = () => {
|
export const useChipFieldDisplay = () => {
|
||||||
const {
|
const { recordId, fieldDefinition, isLabelIdentifier, labelIdentifierLink } =
|
||||||
recordId,
|
useContext(FieldContext);
|
||||||
fieldDefinition,
|
|
||||||
isLabelIdentifier,
|
const isMobile = useIsMobile();
|
||||||
labelIdentifierLink,
|
const isRecordTableScrolledLeftComponent = useRecoilComponentValueV2(
|
||||||
isLabelHidden,
|
isRecordTableScrolledLeftComponentState,
|
||||||
} = useContext(FieldContext);
|
);
|
||||||
|
|
||||||
|
const isLabelHidden =
|
||||||
|
isMobile && isLabelIdentifier && !isRecordTableScrolledLeftComponent;
|
||||||
|
|
||||||
const { chipGeneratorPerObjectPerField } = useContext(
|
const { chipGeneratorPerObjectPerField } = useContext(
|
||||||
PreComputedChipGeneratorsContext,
|
PreComputedChipGeneratorsContext,
|
||||||
|
|||||||
@ -52,7 +52,6 @@ export const RecordTableRecordGroupRows = () => {
|
|||||||
recordId={recordId}
|
recordId={recordId}
|
||||||
rowIndexForFocus={rowIndex}
|
rowIndexForFocus={rowIndex}
|
||||||
rowIndexForDrag={rowIndexInGroup}
|
rowIndexForDrag={rowIndexInGroup}
|
||||||
isPendingRow={!isRecordGroupTableSectionToggled}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@ -24,6 +24,7 @@ const StyledTableContainer = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type RecordTableWithWrappersProps = {
|
type RecordTableWithWrappersProps = {
|
||||||
|
|||||||
@ -23,9 +23,9 @@ import { RecordTableContextProvider } from '@/object-record/record-table/context
|
|||||||
import { RecordTableRowContextProvider } from '@/object-record/record-table/contexts/RecordTableRowContext';
|
import { RecordTableRowContextProvider } from '@/object-record/record-table/contexts/RecordTableRowContext';
|
||||||
import { RecordTableRowDraggableContextProvider } from '@/object-record/record-table/contexts/RecordTableRowDraggableContext';
|
import { RecordTableRowDraggableContextProvider } from '@/object-record/record-table/contexts/RecordTableRowDraggableContext';
|
||||||
import { RecordTableCellFieldContextWrapper } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldContextWrapper';
|
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 { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||||
import { mockPerformance } from './mock';
|
import { mockPerformance } from './mock';
|
||||||
import { ComponentDecorator } from 'twenty-ui/testing';
|
|
||||||
|
|
||||||
const RelationFieldValueSetterEffect = () => {
|
const RelationFieldValueSetterEffect = () => {
|
||||||
const setEntity = useSetRecoilState(
|
const setEntity = useSetRecoilState(
|
||||||
@ -110,7 +110,6 @@ const meta: Meta = {
|
|||||||
mockPerformance.entityValue.__typename.toLocaleLowerCase(),
|
mockPerformance.entityValue.__typename.toLocaleLowerCase(),
|
||||||
}) + mockPerformance.recordId,
|
}) + mockPerformance.recordId,
|
||||||
isSelected: false,
|
isSelected: false,
|
||||||
isPendingRow: false,
|
|
||||||
inView: true,
|
inView: true,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -7,7 +7,6 @@ export type RecordTableRowContextValue = {
|
|||||||
rowIndex: number;
|
rowIndex: number;
|
||||||
isSelected: boolean;
|
isSelected: boolean;
|
||||||
inView: boolean;
|
inView: boolean;
|
||||||
isPendingRow?: boolean;
|
|
||||||
isReadOnly?: boolean;
|
isReadOnly?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -33,6 +33,8 @@ export const RecordTableBodyLoading = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RecordTableTr
|
<RecordTableTr
|
||||||
|
recordId={`${rowIndex}`}
|
||||||
|
focusIndex={0}
|
||||||
isDragging={false}
|
isDragging={false}
|
||||||
data-testid={`row-id-${rowIndex}`}
|
data-testid={`row-id-${rowIndex}`}
|
||||||
data-selectable-id={`row-id-${rowIndex}`}
|
data-selectable-id={`row-id-${rowIndex}`}
|
||||||
|
|||||||
@ -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 { RecordTableCellSoftFocusMode } from '@/object-record/record-table/record-table-cell/components/RecordTableCellSoftFocusMode';
|
||||||
|
|
||||||
import { RecordTableCellSoftFocusModeHotkeysSetterEffect } from '@/object-record/record-table/record-table-cell/components/RecordTableCellSoftFocusModeHotkeysSetterEffect';
|
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 { RecordTableCellDisplayMode } from './RecordTableCellDisplayMode';
|
||||||
import { RecordTableCellEditMode } from './RecordTableCellEditMode';
|
import { RecordTableCellEditMode } from './RecordTableCellEditMode';
|
||||||
|
|
||||||
@ -26,17 +23,13 @@ export const RecordTableCellContainer = ({
|
|||||||
}: RecordTableCellContainerProps) => {
|
}: RecordTableCellContainerProps) => {
|
||||||
const { hasSoftFocus, isInEditMode } = useContext(RecordTableCellContext);
|
const { hasSoftFocus, isInEditMode } = useContext(RecordTableCellContext);
|
||||||
|
|
||||||
const currentHotkeyScope = useRecoilValue(currentHotkeyScopeState);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RecordTableCellBaseContainer>
|
<RecordTableCellBaseContainer>
|
||||||
{isInEditMode ? (
|
{isInEditMode ? (
|
||||||
<RecordTableCellEditMode>{editModeContent}</RecordTableCellEditMode>
|
<RecordTableCellEditMode>{editModeContent}</RecordTableCellEditMode>
|
||||||
) : hasSoftFocus ? (
|
) : hasSoftFocus ? (
|
||||||
<>
|
<>
|
||||||
{currentHotkeyScope.scope === TableHotkeyScope.TableSoftFocus && (
|
<RecordTableCellSoftFocusModeHotkeysSetterEffect />
|
||||||
<RecordTableCellSoftFocusModeHotkeysSetterEffect />
|
|
||||||
)}
|
|
||||||
<RecordTableCellSoftFocusMode
|
<RecordTableCellSoftFocusMode
|
||||||
editModeContent={editModeContent}
|
editModeContent={editModeContent}
|
||||||
nonEditModeContent={nonEditModeContent}
|
nonEditModeContent={nonEditModeContent}
|
||||||
|
|||||||
@ -14,11 +14,8 @@ import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/co
|
|||||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||||
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
|
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
|
||||||
import { SelectFieldHotkeyScope } from '@/object-record/select/types/SelectFieldHotkeyScope';
|
import { SelectFieldHotkeyScope } from '@/object-record/select/types/SelectFieldHotkeyScope';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
|
||||||
import { ReactNode, useContext } from 'react';
|
import { ReactNode, useContext } from 'react';
|
||||||
import { useIsMobile } from 'twenty-ui/utilities';
|
|
||||||
import { RelationDefinitionType } from '~/generated-metadata/graphql';
|
import { RelationDefinitionType } from '~/generated-metadata/graphql';
|
||||||
import { isRecordTableScrolledLeftComponentState } from '../../states/isRecordTableScrolledLeftComponentState';
|
|
||||||
|
|
||||||
export const RecordTableCellFieldContext = ({
|
export const RecordTableCellFieldContext = ({
|
||||||
children,
|
children,
|
||||||
@ -31,16 +28,6 @@ export const RecordTableCellFieldContext = ({
|
|||||||
const { recordId, isReadOnly: isTableRowReadOnly } =
|
const { recordId, isReadOnly: isTableRowReadOnly } =
|
||||||
useRecordTableRowContextOrThrow();
|
useRecordTableRowContextOrThrow();
|
||||||
const updateRecord = useContext(RecordUpdateContext);
|
const updateRecord = useContext(RecordUpdateContext);
|
||||||
const isMobile = useIsMobile();
|
|
||||||
|
|
||||||
const isRecordTableScrolledLeft = useRecoilComponentValueV2(
|
|
||||||
isRecordTableScrolledLeftComponentState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const isLabelHidden =
|
|
||||||
isMobile &&
|
|
||||||
columnDefinition?.isLabelIdentifier &&
|
|
||||||
!isRecordTableScrolledLeft;
|
|
||||||
|
|
||||||
const computedHotkeyScope = (
|
const computedHotkeyScope = (
|
||||||
columnDefinition: ColumnDefinition<FieldMetadata>,
|
columnDefinition: ColumnDefinition<FieldMetadata>,
|
||||||
@ -93,7 +80,6 @@ export const RecordTableCellFieldContext = ({
|
|||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
}),
|
}),
|
||||||
displayedMaxRows: 1,
|
displayedMaxRows: 1,
|
||||||
isLabelHidden,
|
|
||||||
isReadOnly: isFieldReadOnly,
|
isReadOnly: isFieldReadOnly,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -1,27 +1,21 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext';
|
|
||||||
import { useRecordTableRowDraggableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowDraggableContext';
|
import { useRecordTableRowDraggableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowDraggableContext';
|
||||||
import { RecordTableTd } from '@/object-record/record-table/record-table-cell/components/RecordTableTd';
|
import { RecordTableTd } from '@/object-record/record-table/record-table-cell/components/RecordTableTd';
|
||||||
import { css } from '@emotion/react';
|
|
||||||
import { IconListViewGrip } from 'twenty-ui/input';
|
import { IconListViewGrip } from 'twenty-ui/input';
|
||||||
|
|
||||||
export const TABLE_CELL_GRIP_WIDTH = '16px';
|
export const TABLE_CELL_GRIP_WIDTH = '16px';
|
||||||
|
|
||||||
const StyledContainer = styled.div<{ isPendingRow?: boolean }>`
|
const StyledContainer = styled.div`
|
||||||
height: 32px;
|
height: 32px;
|
||||||
width: ${TABLE_CELL_GRIP_WIDTH};
|
width: ${TABLE_CELL_GRIP_WIDTH};
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
display: flex;
|
display: flex;
|
||||||
${({ isPendingRow }) =>
|
|
||||||
!isPendingRow
|
&:hover .icon {
|
||||||
? css`
|
opacity: 1;
|
||||||
&:hover .icon {
|
}
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
: ''};
|
|
||||||
|
|
||||||
z-index: 200;
|
z-index: 200;
|
||||||
`;
|
`;
|
||||||
@ -32,8 +26,6 @@ const StyledIconWrapper = styled.div<{ isDragging: boolean }>`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const RecordTableCellGrip = () => {
|
export const RecordTableCellGrip = () => {
|
||||||
const { isPendingRow } = useRecordTableRowContextOrThrow();
|
|
||||||
|
|
||||||
const { dragHandleProps, isDragging } =
|
const { dragHandleProps, isDragging } =
|
||||||
useRecordTableRowDraggableContextOrThrow();
|
useRecordTableRowDraggableContextOrThrow();
|
||||||
|
|
||||||
@ -45,7 +37,7 @@ export const RecordTableCellGrip = () => {
|
|||||||
hasRightBorder={false}
|
hasRightBorder={false}
|
||||||
hasBottomBorder={false}
|
hasBottomBorder={false}
|
||||||
>
|
>
|
||||||
<StyledContainer isPendingRow={isPendingRow}>
|
<StyledContainer>
|
||||||
<StyledIconWrapper className="icon" isDragging={isDragging}>
|
<StyledIconWrapper className="icon" isDragging={isDragging}>
|
||||||
<IconListViewGrip />
|
<IconListViewGrip />
|
||||||
</StyledIconWrapper>
|
</StyledIconWrapper>
|
||||||
|
|||||||
@ -12,9 +12,12 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
|||||||
import { isNonTextWritingKey } from '@/ui/utilities/hotkey/utils/isNonTextWritingKey';
|
import { isNonTextWritingKey } from '@/ui/utilities/hotkey/utils/isNonTextWritingKey';
|
||||||
|
|
||||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||||
|
import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState';
|
||||||
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
||||||
|
|
||||||
export const RecordTableCellSoftFocusModeHotkeysSetterEffect = () => {
|
export const RecordTableCellSoftFocusModeHotkeysSetterEffect = () => {
|
||||||
|
const currentHotkeyScope = useRecoilValue(currentHotkeyScopeState);
|
||||||
|
|
||||||
const { openTableCell } = useOpenRecordTableCellFromCell();
|
const { openTableCell } = useOpenRecordTableCellFromCell();
|
||||||
const { isReadOnly } = useContext(FieldContext);
|
const { isReadOnly } = useContext(FieldContext);
|
||||||
|
|
||||||
@ -29,10 +32,14 @@ export const RecordTableCellSoftFocusModeHotkeysSetterEffect = () => {
|
|||||||
const clearField = useClearField();
|
const clearField = useClearField();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (currentHotkeyScope.scope !== TableHotkeyScope.TableSoftFocus) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isSoftFocusUsingMouse) {
|
if (!isSoftFocusUsingMouse) {
|
||||||
scrollRef.current?.scrollIntoView({ block: 'nearest' });
|
scrollRef.current?.scrollIntoView({ block: 'nearest' });
|
||||||
}
|
}
|
||||||
}, [isSoftFocusUsingMouse]);
|
}, [currentHotkeyScope.scope, isSoftFocusUsingMouse]);
|
||||||
|
|
||||||
useScopedHotkeys(
|
useScopedHotkeys(
|
||||||
[Key.Backspace, Key.Delete],
|
[Key.Backspace, Key.Delete],
|
||||||
|
|||||||
@ -9,7 +9,6 @@ export const recordTableRowContextValue: RecordTableRowContextValue = {
|
|||||||
recordId: 'recordId',
|
recordId: 'recordId',
|
||||||
pathToShowPage: '/',
|
pathToShowPage: '/',
|
||||||
objectNameSingular: 'objectNameSingular',
|
objectNameSingular: 'objectNameSingular',
|
||||||
isPendingRow: false,
|
|
||||||
inView: true,
|
inView: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -65,8 +65,9 @@ export const RecordTableActionRow = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledRecordTableDraggableTr
|
<StyledRecordTableDraggableTr
|
||||||
draggableId={draggableId}
|
recordId={draggableId}
|
||||||
draggableIndex={draggableIndex}
|
draggableIndex={draggableIndex}
|
||||||
|
focusIndex={draggableIndex}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
isDragDisabled
|
isDragDisabled
|
||||||
>
|
>
|
||||||
|
|||||||
@ -1,55 +1,44 @@
|
|||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import { Draggable, DraggableId } from '@hello-pangea/dnd';
|
import { Draggable } from '@hello-pangea/dnd';
|
||||||
import { ReactNode, forwardRef } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
import { RecordTableRowDraggableContextProvider } from '@/object-record/record-table/contexts/RecordTableRowDraggableContext';
|
import { RecordTableRowDraggableContextProvider } from '@/object-record/record-table/contexts/RecordTableRowDraggableContext';
|
||||||
import { RecordTableTr } from '@/object-record/record-table/record-table-row/components/RecordTableTr';
|
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 = {
|
type RecordTableDraggableTrProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
draggableId: DraggableId;
|
recordId: string;
|
||||||
draggableIndex: number;
|
draggableIndex: number;
|
||||||
|
focusIndex: number;
|
||||||
isDragDisabled?: boolean;
|
isDragDisabled?: boolean;
|
||||||
onClick?: (event: React.MouseEvent<HTMLTableRowElement>) => void;
|
onClick?: (event: React.MouseEvent<HTMLTableRowElement>) => void;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledAbsoluteInViewContainer = styled.td`
|
export const RecordTableDraggableTr = ({
|
||||||
position: absolute;
|
className,
|
||||||
top: 0;
|
recordId,
|
||||||
left: 0;
|
draggableIndex,
|
||||||
right: 0;
|
focusIndex,
|
||||||
bottom: 0;
|
isDragDisabled,
|
||||||
pointer-events: none;
|
onClick,
|
||||||
z-index: -1;
|
children,
|
||||||
`;
|
}: RecordTableDraggableTrProps) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
export const RecordTableDraggableTr = forwardRef<
|
return (
|
||||||
HTMLTableCellElement,
|
<Draggable
|
||||||
RecordTableDraggableTrProps
|
draggableId={recordId}
|
||||||
>(
|
index={draggableIndex}
|
||||||
(
|
isDragDisabled={isDragDisabled}
|
||||||
{
|
>
|
||||||
className,
|
{(draggableProvided, draggableSnapshot) => (
|
||||||
draggableId,
|
<>
|
||||||
draggableIndex,
|
<RecordTableTrEffect recordId={recordId} />
|
||||||
isDragDisabled,
|
|
||||||
onClick,
|
|
||||||
children,
|
|
||||||
},
|
|
||||||
ref,
|
|
||||||
) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Draggable
|
|
||||||
draggableId={draggableId}
|
|
||||||
index={draggableIndex}
|
|
||||||
isDragDisabled={isDragDisabled}
|
|
||||||
>
|
|
||||||
{(draggableProvided, draggableSnapshot) => (
|
|
||||||
<RecordTableTr
|
<RecordTableTr
|
||||||
|
recordId={recordId}
|
||||||
|
focusIndex={focusIndex}
|
||||||
ref={draggableProvided.innerRef}
|
ref={draggableProvided.innerRef}
|
||||||
className={className}
|
className={className}
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
@ -64,8 +53,9 @@ export const RecordTableDraggableTr = forwardRef<
|
|||||||
: 'transparent',
|
: 'transparent',
|
||||||
}}
|
}}
|
||||||
isDragging={draggableSnapshot.isDragging}
|
isDragging={draggableSnapshot.isDragging}
|
||||||
data-testid={`row-id-${draggableId}`}
|
data-testid={`row-id-${recordId}`}
|
||||||
data-selectable-id={draggableId}
|
data-virtualized-id={recordId}
|
||||||
|
data-selectable-id={recordId}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
<RecordTableRowDraggableContextProvider
|
<RecordTableRowDraggableContextProvider
|
||||||
@ -75,13 +65,10 @@ export const RecordTableDraggableTr = forwardRef<
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<StyledAbsoluteInViewContainer
|
|
||||||
ref={ref}
|
|
||||||
></StyledAbsoluteInViewContainer>
|
|
||||||
</RecordTableRowDraggableContextProvider>
|
</RecordTableRowDraggableContextProvider>
|
||||||
</RecordTableTr>
|
</RecordTableTr>
|
||||||
)}
|
</>
|
||||||
</Draggable>
|
)}
|
||||||
);
|
</Draggable>
|
||||||
},
|
);
|
||||||
);
|
};
|
||||||
|
|||||||
@ -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 { RecordTableCellGrip } from '@/object-record/record-table/record-table-cell/components/RecordTableCellGrip';
|
||||||
import { RecordTableLastEmptyCell } from '@/object-record/record-table/record-table-cell/components/RecordTableLastEmptyCell';
|
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 { 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 = {
|
type RecordTableRowProps = {
|
||||||
recordId: string;
|
recordId: string;
|
||||||
rowIndexForFocus: number;
|
rowIndexForFocus: number;
|
||||||
rowIndexForDrag: number;
|
rowIndexForDrag: number;
|
||||||
isPendingRow?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RecordTableRow = ({
|
export const RecordTableRow = ({
|
||||||
recordId,
|
recordId,
|
||||||
rowIndexForFocus,
|
rowIndexForFocus,
|
||||||
rowIndexForDrag,
|
rowIndexForDrag,
|
||||||
isPendingRow,
|
|
||||||
}: RecordTableRowProps) => {
|
}: RecordTableRowProps) => {
|
||||||
return (
|
return (
|
||||||
<RecordTableRowWrapper
|
<RecordTableDraggableTr
|
||||||
recordId={recordId}
|
recordId={recordId}
|
||||||
rowIndexForFocus={rowIndexForFocus}
|
draggableIndex={rowIndexForDrag}
|
||||||
rowIndexForDrag={rowIndexForDrag}
|
focusIndex={rowIndexForFocus}
|
||||||
isPendingRow={isPendingRow}
|
|
||||||
>
|
>
|
||||||
<RecordTableCellGrip />
|
<RecordTableCellGrip />
|
||||||
<RecordTableCellCheckbox />
|
<RecordTableCellCheckbox />
|
||||||
<RecordTableCells />
|
<RecordTableCells />
|
||||||
<RecordTableLastEmptyCell />
|
<RecordTableLastEmptyCell />
|
||||||
<RecordValueSetterEffect recordId={recordId} />
|
<RecordValueSetterEffect recordId={recordId} />
|
||||||
</RecordTableRowWrapper>
|
</RecordTableDraggableTr>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -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 styled from '@emotion/styled';
|
||||||
|
import { ReactNode, forwardRef } from 'react';
|
||||||
|
|
||||||
const StyledTr = styled.tr<{ isDragging: boolean }>`
|
const StyledTr = styled.tr<{ isDragging: boolean }>`
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -9,4 +16,51 @@ const StyledTr = styled.tr<{ isDragging: boolean }>`
|
|||||||
transition: border-left-color 0.2s ease-in-out;
|
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>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|||||||
@ -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 <></>;
|
||||||
|
};
|
||||||
@ -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,
|
||||||
|
});
|
||||||
@ -20,7 +20,6 @@ import { flushSync } from 'react-dom';
|
|||||||
import { Keys } from 'react-hotkeys-hook';
|
import { Keys } from 'react-hotkeys-hook';
|
||||||
import { useRecoilCallback } from 'recoil';
|
import { useRecoilCallback } from 'recoil';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
import { sleep } from '~/utils/sleep';
|
|
||||||
import { useDropdown } from '../hooks/useDropdown';
|
import { useDropdown } from '../hooks/useDropdown';
|
||||||
|
|
||||||
const StyledDropdownFallbackAnchor = styled.div`
|
const StyledDropdownFallbackAnchor = styled.div`
|
||||||
@ -115,8 +114,6 @@ export const Dropdown = ({
|
|||||||
dropdownHotkeyScope,
|
dropdownHotkeyScope,
|
||||||
);
|
);
|
||||||
|
|
||||||
await sleep(100);
|
|
||||||
|
|
||||||
toggleDropdown();
|
toggleDropdown();
|
||||||
onClickOutside?.();
|
onClickOutside?.();
|
||||||
},
|
},
|
||||||
|
|||||||
@ -14,6 +14,8 @@ const StyledScrollWrapper = styled.div`
|
|||||||
&.scroll-wrapper-y-enabled {
|
&.scroll-wrapper-y-enabled {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
Reference in New Issue
Block a user