Scroll behavior part 2 (#1304)

* Fix layout issues introduced by scroll behavior

* Complete scrollbar work
This commit is contained in:
Charles Bochet
2023-08-25 12:38:45 +02:00
committed by GitHub
parent 0d210244db
commit de569f4c06
11 changed files with 162 additions and 92 deletions

View File

@ -1,12 +1,10 @@
import React from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { ActivityCreateButton } from '@/activities/components/ActivityCreateButton';
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import { ActivityForDrawer } from '@/activities/types/ActivityForDrawer';
import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity';
import { IconCircleDot } from '@/ui/icon';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import {
ActivityType,
@ -14,7 +12,7 @@ import {
useGetActivitiesByTargetsQuery,
} from '~/generated/graphql';
import { TimelineActivity } from './TimelineActivity';
import { TimelineItemsContainer } from './TimelineItemsContainer';
const StyledMainContainer = styled.div`
align-items: flex-start;
@ -22,26 +20,12 @@ const StyledMainContainer = styled.div`
border-top: ${({ theme }) =>
useIsMobile() ? `1px solid ${theme.border.color.medium}` : 'none'};
display: flex;
flex: 1 0 0;
flex-direction: column;
height: 100%;
justify-content: center;
`;
const StyledTimelineContainer = styled.div`
align-items: center;
align-self: stretch;
display: flex;
flex: 1 0 0;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(1)};
justify-content: flex-start;
overflow-y: ${() => (useIsMobile() ? 'none' : 'auto')};
padding: ${({ theme }) => theme.spacing(3)} ${({ theme }) => theme.spacing(4)};
`;
const StyledTimelineEmptyContainer = styled.div`
align-items: center;
align-self: stretch;
@ -85,17 +69,7 @@ const StyledTopActionBar = styled.div`
top: 0px;
`;
const StyledStartIcon = styled.div`
align-self: flex-start;
color: ${({ theme }) => theme.font.color.tertiary};
display: flex;
height: 20px;
width: 20px;
`;
export function Timeline({ entity }: { entity: ActivityTargetableEntity }) {
const theme = useTheme();
const { data: queryResult, loading } = useGetActivitiesByTargetsQuery({
variables: {
activityTargetIds: [entity.id],
@ -136,14 +110,7 @@ export function Timeline({ entity }: { entity: ActivityTargetableEntity }) {
onTaskClick={() => openCreateActivity(ActivityType.Task, [entity])}
/>
</StyledTopActionBar>
<StyledTimelineContainer>
{activities.map((activity) => (
<TimelineActivity key={activity.id} activity={activity} />
))}
<StyledStartIcon>
<IconCircleDot size={theme.icon.size.lg} />
</StyledStartIcon>
</StyledTimelineContainer>
<TimelineItemsContainer activities={activities} />
</StyledMainContainer>
);
}

View File

@ -0,0 +1,54 @@
import React from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { ActivityForDrawer } from '@/activities/types/ActivityForDrawer';
import { IconCircleDot } from '@/ui/icon';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { TimelineActivity } from './TimelineActivity';
const StyledTimelineContainer = styled.div`
align-items: center;
align-self: stretch;
display: flex;
flex: 1 0 0;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(1)};
justify-content: flex-start;
padding: ${({ theme }) => theme.spacing(3)} ${({ theme }) => theme.spacing(4)};
`;
const StyledStartIcon = styled.div`
align-self: flex-start;
color: ${({ theme }) => theme.font.color.tertiary};
display: flex;
height: 20px;
width: 20px;
`;
const StyledScrollWrapper = styled(ScrollWrapper)``;
export type TimelineItemsContainerProps = {
activities: ActivityForDrawer[];
};
export function TimelineItemsContainer({
activities,
}: TimelineItemsContainerProps) {
const theme = useTheme();
return (
<StyledScrollWrapper>
<StyledTimelineContainer>
{activities.map((activity) => (
<TimelineActivity key={activity.id} activity={activity} />
))}
<StyledStartIcon>
<IconCircleDot size={theme.icon.size.lg} />
</StyledStartIcon>
</StyledTimelineContainer>
</StyledScrollWrapper>
);
}

View File

@ -29,6 +29,7 @@ const StyledListContainer = styled.div`
align-items: flex-start;
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-radius: ${({ theme }) => theme.spacing(1)};
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: auto;

View File

@ -14,8 +14,7 @@ import { BoardColumnIdContext } from '@/ui/board/contexts/BoardColumnIdContext';
import { SelectedSortType } from '@/ui/filter-n-sort/types/interface';
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { StyledScrollWrapper } from '@/ui/utilities/scroll/components/StyledScrollWrapper';
import { useListenScroll } from '@/ui/utilities/scroll/hooks/useListenScroll';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import {
PipelineProgress,
PipelineProgressOrderByWithRelationInput,
@ -31,7 +30,7 @@ import { BoardOptions } from '../types/BoardOptions';
import { EntityBoardColumn } from './EntityBoardColumn';
const StyledCustomScrollWrapper = styled(StyledScrollWrapper)`
const StyledCustomScrollWrapper = styled(ScrollWrapper)`
flex-direction: column;
`;
@ -103,15 +102,10 @@ export function EntityBoard({
return a.index - b.index;
});
const scrollableRef = useRef<HTMLDivElement>(null);
const boardRef = useRef<HTMLDivElement>(null);
useListenScroll({
scrollableRef,
});
return (boardColumns?.length ?? 0) > 0 ? (
<StyledCustomScrollWrapper ref={scrollableRef}>
<StyledCustomScrollWrapper>
<BoardHeader
viewName="All opportunities"
viewIcon={<IconList size={theme.icon.size.md} />}

View File

@ -2,14 +2,26 @@ import { ReactElement } from 'react';
import styled from '@emotion/styled';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
export const StyledShowPageContainer = styled.div`
const StyledOuterContainer = styled.div`
display: flex;
flex-direction: ${() => (useIsMobile() ? 'column' : 'row')};
gap: ${({ theme }) => (useIsMobile() ? theme.spacing(3) : '0')};
height: ${() => (useIsMobile() ? '100%' : 'auto')};
overflow-x: ${() => (useIsMobile() ? 'hidden' : 'auto')};
width: ${() => (useIsMobile() ? `calc(100% - 2px);` : '100%')};
width: 100%;
`;
const StyledInnerContainer = styled.div`
display: flex;
flex-direction: ${() => (useIsMobile() ? 'column' : 'row')};
width: 100%;
`;
const StyledScrollWrapper = styled(ScrollWrapper)`
background-color: ${({ theme }) => theme.background.secondary};
border-radius: ${({ theme }) => theme.border.radius.md};
`;
export type ShowPageContainerProps = {
@ -17,5 +29,16 @@ export type ShowPageContainerProps = {
};
export function ShowPageContainer({ children }: ShowPageContainerProps) {
return <StyledShowPageContainer>{children} </StyledShowPageContainer>;
const isMobile = useIsMobile();
return isMobile ? (
<StyledOuterContainer>
<StyledScrollWrapper>
<StyledInnerContainer>{children}</StyledInnerContainer>
</StyledScrollWrapper>
</StyledOuterContainer>
) : (
<StyledOuterContainer>
<StyledInnerContainer>{children}</StyledInnerContainer>
</StyledOuterContainer>
);
}

View File

@ -2,27 +2,33 @@ import { ReactElement } from 'react';
import styled from '@emotion/styled';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
const StyledShowPageLeftContainer = styled.div`
const StyledOuterContainer = styled.div`
background: ${({ theme }) => theme.background.secondary};
border-bottom-left-radius: 8px;
border-right: 1px solid
${({ theme }) => {
const isMobile = useIsMobile();
return !isMobile ? theme.border.color.medium : 0;
}};
border-right: ${({ theme }) => {
const isMobile = useIsMobile();
return !isMobile ? `1px solid ${theme.border.color.medium}` : 'none';
}};
border-top-left-radius: 8px;
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(3)};
padding: 0px ${({ theme }) => theme.spacing(3)};
z-index: 10;
`;
const StyledInnerContainer = styled.div`
display: flex;
flex-direction: column;
padding: 0px ${({ theme }) => theme.spacing(2)} 0px
${({ theme }) => theme.spacing(3)};
width: ${({ theme }) => {
const isMobile = useIsMobile();
return isMobile ? `calc(100% - ${theme.spacing(6)})` : '320px';
return isMobile ? `calc(100% - ${theme.spacing(5)})` : '320px';
}};
z-index: 10;
`;
export type ShowPageLeftContainerProps = {
@ -32,5 +38,16 @@ export type ShowPageLeftContainerProps = {
export function ShowPageLeftContainer({
children,
}: ShowPageLeftContainerProps) {
return <StyledShowPageLeftContainer>{children} </StyledShowPageLeftContainer>;
const isMobile = useIsMobile();
return isMobile ? (
<StyledOuterContainer>
<StyledInnerContainer>{children}</StyledInnerContainer>
</StyledOuterContainer>
) : (
<StyledOuterContainer>
<ScrollWrapper>
<StyledInnerContainer>{children}</StyledInnerContainer>
</ScrollWrapper>
</StyledOuterContainer>
);
}

View File

@ -9,11 +9,7 @@ export const StyledShowPageRightContainer = styled.div`
flex-direction: column;
justify-content: center;
overflow: ${() => (useIsMobile() ? 'none' : 'hidden')};
width: ${({ theme }) => {
const isMobile = useIsMobile();
return isMobile ? `calc(100% - ${theme.spacing(6)})` : 'auto';
}};
width: calc(100% + 4px);
`;
export type ShowPageRightContainerProps = {

View File

@ -31,7 +31,7 @@ const StyledContainer = styled(motion.div)`
right: 0;
top: 0;
z-index: 2;
z-index: 100;
`;
const StyledRightDrawer = styled.div`

View File

@ -8,8 +8,7 @@ import type {
import { SortType } from '@/ui/filter-n-sort/types/interface';
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { StyledScrollWrapper } from '@/ui/utilities/scroll/components/StyledScrollWrapper';
import { useListenScroll } from '@/ui/utilities/scroll/hooks/useListenScroll';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { EntityUpdateMutationContext } from '../contexts/EntityUpdateMutationHookContext';
import { useLeaveTableFocus } from '../hooks/useLeaveTableFocus';
@ -125,12 +124,6 @@ export function EntityTable<SortField>({
},
});
const scrollableRef = useRef<HTMLDivElement>(null);
useListenScroll({
scrollableRef,
});
return (
<EntityUpdateMutationContext.Provider value={updateEntityMutation}>
<StyledTableWithHeader>
@ -143,12 +136,14 @@ export function EntityTable<SortField>({
onViewSubmit={onViewSubmit}
onImport={onImport}
/>
<StyledScrollWrapper ref={scrollableRef}>
<StyledTable>
<EntityTableHeader onColumnsChange={onColumnsChange} />
<EntityTableBody />
</StyledTable>
</StyledScrollWrapper>
<ScrollWrapper>
<div>
<StyledTable>
<EntityTableHeader onColumnsChange={onColumnsChange} />
<EntityTableBody />
</StyledTable>
</div>
</ScrollWrapper>
<DragSelect
dragSelectable={tableBodyRef}
onDragSelectionStart={resetTableRowSelection}

View File

@ -0,0 +1,35 @@
import { useRef } from 'react';
import styled from '@emotion/styled';
import { useListenScroll } from '../hooks/useListenScroll';
const StyledScrollWrapper = styled.div`
display: flex;
height: 100%;
overflow: scroll;
scrollbar-gutter: stable;
width: 100%;
&.scrolling::-webkit-scrollbar-thumb {
background-color: ${({ theme }) => theme.border.color.medium};
}
`;
export type ScrollWrapperProps = {
children: React.ReactNode;
className?: string;
};
export function ScrollWrapper({ children, className }: ScrollWrapperProps) {
const scrollableRef = useRef<HTMLDivElement>(null);
useListenScroll({
scrollableRef,
});
return (
<StyledScrollWrapper ref={scrollableRef} className={className}>
{children}
</StyledScrollWrapper>
);
}

View File

@ -1,12 +0,0 @@
import styled from '@emotion/styled';
export const StyledScrollWrapper = styled.div`
display: flex;
height: 100%;
overflow: auto;
width: 100%;
&.scrolling::-webkit-scrollbar-thumb {
background-color: ${({ theme }) => theme.border.color.medium};
}
`;