Skip to content

Commit

Permalink
Created a specific scroll wrapper context per scroll wrapper and made…
Browse files Browse the repository at this point in the history
… ScrollTop and ScrollLeft componentStates (#6645)

@lucasbordeau @charlesBochet 

Issue #4826 

Could u review this changes.

Let me know what do you think.

---------

Co-authored-by: Lucas Bordeau <[email protected]>
  • Loading branch information
ehconitin and lucasbordeau authored Aug 22, 2024
1 parent 0a77003 commit 1030ff4
Show file tree
Hide file tree
Showing 26 changed files with 205 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const EventList = ({ events, targetableObject }: EventListProps) => {
const groupedEvents = groupEventsByMonth(filteredEvents);

return (
<ScrollWrapper>
<ScrollWrapper contextProviderName="eventList">
<StyledTimelineContainer>
{groupedEvents.map((group, index) => (
<EventsGroup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ export const CommandMenu = () => {
)}
</StyledInputContainer>
<StyledList>
<ScrollWrapper>
<ScrollWrapper contextProviderName="commandMenu">
<StyledInnerList isMobile={isMobile}>
<SelectableList
selectableListId="command-menu-list"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export const RecordBoard = ({ recordBoardId }: RecordBoardProps) => {
>
<StyledWrapper>
<StyledBoardHeader />
<ScrollWrapper>
<ScrollWrapper contextProviderName="recordBoard">
<StyledContainer ref={boardRef}>
<DragDropContext onDragEnd={onDragEnd}>
{columnIds.map((columnId) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { Checkbox, CheckboxVariant } from '@/ui/input/components/Checkbox';
import { contextMenuIsOpenState } from '@/ui/navigation/context-menu/states/contextMenuIsOpenState';
import { contextMenuPositionState } from '@/ui/navigation/context-menu/states/contextMenuPositionState';
import { AnimatedEaseInOut } from '@/ui/utilities/animation/components/AnimatedEaseInOut';
import { ScrollWrapperContext } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { RecordBoardScrollWrapperContext } from '@/ui/utilities/scroll/contexts/ScrollWrapperContexts';

const StyledBoardCard = styled.div<{ selected: boolean }>`
background-color: ${({ theme, selected }) =>
Expand Down Expand Up @@ -199,10 +199,10 @@ export const RecordBoardCard = () => {
return [updateEntity, { loading: false }];
};

const scrollWrapperRef = useContext(ScrollWrapperContext);
const scrollWrapperRef = useContext(RecordBoardScrollWrapperContext);

const { ref: cardRef, inView } = useInView({
root: scrollWrapperRef.current,
root: scrollWrapperRef?.ref.current,
rootMargin: '1000px',
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const RecordTableWithWrappers = ({

return (
<EntityDeleteContext.Provider value={deleteOneRecord}>
<ScrollWrapper>
<ScrollWrapper contextProviderName="recordTableWithWrappers">
<RecordUpdateContext.Provider value={updateRecordMutation}>
<StyledTableWithHeader>
<StyledTableContainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import { useRecordTableStates } from '@/object-record/record-table/hooks/interna
import { isRecordTableScrolledLeftComponentState } from '@/object-record/record-table/states/isRecordTableScrolledLeftComponentState';
import { isRecordTableScrolledTopComponentState } from '@/object-record/record-table/states/isRecordTableScrolledTopComponentState';
import { isFetchingMoreRecordsFamilyState } from '@/object-record/states/isFetchingMoreRecordsFamilyState';
import { scrollLeftState } from '@/ui/utilities/scroll/states/scrollLeftState';
import { scrollTopState } from '@/ui/utilities/scroll/states/scrollTopState';
import { useScrollLeftValue } from '@/ui/utilities/scroll/hooks/useScrollLeftValue';
import { useScrollTopValue } from '@/ui/utilities/scroll/hooks/useScrollTopValue';
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
import { isNonEmptyString } from '@sniptt/guards';
import { useScrollToPosition } from '~/hooks/useScrollToPosition';
Expand All @@ -38,7 +38,7 @@ export const RecordTableBodyEffect = () => {

const tableLastRowVisible = useRecoilValue(tableLastRowVisibleState);

const scrollTop = useRecoilValue(scrollTopState);
const scrollTop = useScrollTopValue('recordTableWithWrappers');
const setIsRecordTableScrolledTop = useSetRecoilComponentState(
isRecordTableScrolledTopComponentState,
);
Expand All @@ -57,7 +57,7 @@ export const RecordTableBodyEffect = () => {
}
}, [scrollTop, setIsRecordTableScrolledTop]);

const scrollLeft = useRecoilValue(scrollLeftState);
const scrollLeft = useScrollLeftValue('recordTableWithWrappers');

const setIsRecordTableScrolledLeft = useSetRecoilComponentState(
isRecordTableScrolledLeftComponentState,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import styled from '@emotion/styled';
import { useContext } from 'react';
import { useInView } from 'react-intersection-observer';
import styled from '@emotion/styled';
import { useRecoilCallback, useRecoilValue } from 'recoil';
import { GRAY_SCALE } from 'twenty-ui';

import { useLoadRecordIndexTable } from '@/object-record/record-index/hooks/useLoadRecordIndexTable';
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
import { isFetchingMoreRecordsFamilyState } from '@/object-record/states/isFetchingMoreRecordsFamilyState';
import { ScrollWrapperContext } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { RecordTableWithWrappersScrollWrapperContext } from '@/ui/utilities/scroll/contexts/ScrollWrapperContexts';

type RecordTableBodyFetchMoreLoaderProps = {
objectNameSingular: string;
Expand Down Expand Up @@ -40,12 +40,14 @@ export const RecordTableBodyFetchMoreLoader = ({
[setRecordTableLastRowVisible],
);

const scrollWrapperRef = useContext(ScrollWrapperContext);
const scrollWrapperRef = useContext(
RecordTableWithWrappersScrollWrapperContext,
);

const { ref: tbodyRef } = useInView({
onChange: onLastRowVisible,
rootMargin: '1000px',
root: scrollWrapperRef.current?.querySelector(
root: scrollWrapperRef?.ref.current?.querySelector(
'[data-overlayscrollbars-viewport="scrollbarHidden"]',
),
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ const HIDDEN_TABLE_COLUMN_DROPDOWN_HOTKEY_SCOPE_ID =
export const RecordTableHeaderLastColumn = () => {
const { theme } = useContext(ThemeContext);

const scrollWrapper = useScrollWrapperScopedRef();
const scrollWrapper = useScrollWrapperScopedRef('recordTableWithWrappers');

const isTableWiderThanScreen =
(scrollWrapper.current?.clientWidth ?? 0) <
(scrollWrapper.current?.scrollWidth ?? 0);
(scrollWrapper.ref.current?.clientWidth ?? 0) <
(scrollWrapper.ref.current?.scrollWidth ?? 0);

const { hiddenTableColumnsSelector } = useRecordTableStates();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { RecordTableContext } from '@/object-record/record-table/contexts/Record
import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
import { RecordTableTr } from '@/object-record/record-table/record-table-row/components/RecordTableTr';
import { ScrollWrapperContext } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { RecordTableWithWrappersScrollWrapperContext } from '@/ui/utilities/scroll/contexts/ScrollWrapperContexts';

export const RecordTableRowWrapper = ({
recordId,
Expand All @@ -31,10 +31,12 @@ export const RecordTableRowWrapper = ({
const { isRowSelectedFamilyState } = useRecordTableStates();
const currentRowSelected = useRecoilValue(isRowSelectedFamilyState(recordId));

const scrollWrapperRef = useContext(ScrollWrapperContext);
const scrollWrapperRef = useContext(
RecordTableWithWrappersScrollWrapperContext,
);

const { ref: elementRef, inView } = useInView({
root: scrollWrapperRef.current?.querySelector(
root: scrollWrapperRef.ref.current?.querySelector(
'[data-overlayscrollbars-viewport="scrollbarHidden"]',
),
rootMargin: '1000px',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const SettingsPageContainer = ({
}: {
children: ReactNode;
}) => (
<StyledScrollWrapper>
<StyledScrollWrapper contextProviderName="settingsPageContainer">
<StyledSettingsPageContainer>{children}</StyledSettingsPageContainer>
</StyledScrollWrapper>
);
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const DropdownMenuItemsContainer = ({
return (
<StyledDropdownMenuItemsExternalContainer hasMaxHeight={hasMaxHeight}>
{hasMaxHeight ? (
<StyledScrollWrapper>
<StyledScrollWrapper contextProviderName="dropdownMenuItemsContainer">
<StyledDropdownMenuItemsInternalContainer>
{children}
</StyledDropdownMenuItemsInternalContainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const ShowPageContainer = ({ children }: ShowPageContainerProps) => {
const isMobile = useIsMobile();
return isMobile ? (
<StyledOuterContainer>
<StyledScrollWrapper>
<StyledScrollWrapper contextProviderName="showPageContainer">
<StyledInnerContainer>{children}</StyledInnerContainer>
</StyledScrollWrapper>
</StyledOuterContainer>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ReactNode } from 'react';
import styled from '@emotion/styled';
import { ReactNode } from 'react';

import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
Expand Down Expand Up @@ -46,7 +46,7 @@ export const ShowPageLeftContainer = ({
{children}
</StyledInnerContainer>
) : (
<ScrollWrapper>
<ScrollWrapper contextProviderName="showPageLeftContainer">
<StyledIntermediateContainer>
<StyledInnerContainer isMobile={isMobile}>
{children}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import styled from '@emotion/styled';
import * as React from 'react';
import { useRecoilValue } from 'recoil';
import { IconComponent } from 'twenty-ui';

Expand Down Expand Up @@ -53,7 +53,7 @@ export const TabList = ({

return (
<TabListScope tabListScopeId={tabListId}>
<ScrollWrapper hideY>
<ScrollWrapper hideY contextProviderName="tabList">
<StyledContainer className={className}>
{tabs
.filter((tab) => !tab.hide)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import { createContext, RefObject, useEffect, useRef } from 'react';
import styled from '@emotion/styled';
import { OverlayScrollbars } from 'overlayscrollbars';
import { useOverlayScrollbars } from 'overlayscrollbars-react';
import { useRecoilCallback, useSetRecoilState } from 'recoil';
import { useEffect, useRef } from 'react';
import { useSetRecoilState } from 'recoil';

import {
ContextProviderName,
getContextByProviderName,
} from '@/ui/utilities/scroll/contexts/ScrollWrapperContexts';
import { useScrollStates } from '@/ui/utilities/scroll/hooks/internal/useScrollStates';
import { overlayScrollbarsState } from '@/ui/utilities/scroll/states/overlayScrollbarsState';
import { scrollLeftState } from '@/ui/utilities/scroll/states/scrollLeftState';
import { scrollTopState } from '@/ui/utilities/scroll/states/scrollTopState';

import 'overlayscrollbars/overlayscrollbars.css';

export const ScrollWrapperContext = createContext<RefObject<HTMLDivElement>>({
current: null,
});

const StyledScrollWrapper = styled.div`
display: flex;
height: 100%;
Expand All @@ -29,25 +28,29 @@ export type ScrollWrapperProps = {
className?: string;
hideY?: boolean;
hideX?: boolean;
contextProviderName: ContextProviderName;
};

export const ScrollWrapper = ({
children,
className,
hideX,
hideY,
contextProviderName,
}: ScrollWrapperProps) => {
const scrollableRef = useRef<HTMLDivElement>(null);
const Context = getContextByProviderName(contextProviderName);

const handleScroll = useRecoilCallback(
({ set }) =>
(overlayScroll: OverlayScrollbars) => {
const target = overlayScroll.elements().scrollOffsetElement;
set(scrollTopState, target.scrollTop);
set(scrollLeftState, target.scrollLeft);
},
[],
);
const { scrollTopComponentState, scrollLeftComponentState } =
useScrollStates(contextProviderName);
const setScrollTop = useSetRecoilState(scrollTopComponentState);
const setScrollLeft = useSetRecoilState(scrollLeftComponentState);

const handleScroll = (overlayScroll: OverlayScrollbars) => {
const target = overlayScroll.elements().scrollOffsetElement;
setScrollTop(target.scrollTop);
setScrollLeft(target.scrollLeft);
};

const setOverlayScrollbars = useSetRecoilState(overlayScrollbarsState);

Expand Down Expand Up @@ -75,10 +78,10 @@ export const ScrollWrapper = ({
}, [instance, setOverlayScrollbars]);

return (
<ScrollWrapperContext.Provider value={scrollableRef}>
<Context.Provider value={{ ref: scrollableRef, id: contextProviderName }}>
<StyledScrollWrapper ref={scrollableRef} className={className}>
{children}
</StyledScrollWrapper>
</ScrollWrapperContext.Provider>
</Context.Provider>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { createContext, RefObject } from 'react';

type ScrollWrapperContextValue = {
ref: RefObject<HTMLDivElement>;
id: string;
};

export type ContextProviderName =
| 'eventList'
| 'commandMenu'
| 'recordBoard'
| 'recordTableWithWrappers'
| 'settingsPageContainer'
| 'dropdownMenuItemsContainer'
| 'showPageContainer'
| 'showPageLeftContainer'
| 'tabList'
| 'releases'
| 'test';

const createScrollWrapperContext = (id: string) =>
createContext<ScrollWrapperContextValue>({
ref: { current: null },
id,
});

export const EventListScrollWrapperContext =
createScrollWrapperContext('eventList');
export const CommandMenuScrollWrapperContext =
createScrollWrapperContext('commandMenu');
export const RecordBoardScrollWrapperContext =
createScrollWrapperContext('recordBoard');
export const RecordTableWithWrappersScrollWrapperContext =
createScrollWrapperContext('recordTableWithWrappers');
export const SettingsPageContainerScrollWrapperContext =
createScrollWrapperContext('settingsPageContainer');
export const DropdownMenuItemsContainerScrollWrapperContext =
createScrollWrapperContext('dropdownMenuItemsContainer');
export const ShowPageContainerScrollWrapperContext =
createScrollWrapperContext('showPageContainer');
export const ShowPageLeftContainerScrollWrapperContext =
createScrollWrapperContext('showPageLeftContainer');
export const TabListScrollWrapperContext =
createScrollWrapperContext('tabList');
export const ReleasesScrollWrapperContext =
createScrollWrapperContext('releases');
export const TestScrollWrapperContext = createScrollWrapperContext('test');

export const getContextByProviderName = (
contextProviderName: ContextProviderName,
) => {
switch (contextProviderName) {
case 'eventList':
return EventListScrollWrapperContext;
case 'commandMenu':
return CommandMenuScrollWrapperContext;
case 'recordBoard':
return RecordBoardScrollWrapperContext;
case 'recordTableWithWrappers':
return RecordTableWithWrappersScrollWrapperContext;
case 'settingsPageContainer':
return SettingsPageContainerScrollWrapperContext;
case 'dropdownMenuItemsContainer':
return DropdownMenuItemsContainerScrollWrapperContext;
case 'showPageContainer':
return ShowPageContainerScrollWrapperContext;
case 'showPageLeftContainer':
return ShowPageLeftContainerScrollWrapperContext;
case 'tabList':
return TabListScrollWrapperContext;
case 'releases':
return ReleasesScrollWrapperContext;
case 'test':
return TestScrollWrapperContext;
default:
throw new Error('Context Provider not available');
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jest.mock('react', () => {

describe('useScrollWrapperScopedRef', () => {
it('should return the scrollWrapperRef if available', () => {
const { result } = renderHook(() => useScrollWrapperScopedRef());
const { result } = renderHook(() => useScrollWrapperScopedRef('test'));

expect(result.current).toBeDefined();
});
Expand Down
Loading

0 comments on commit 1030ff4

Please sign in to comment.