Navigation drawer scroll padding fix (#9141)
closes https://github.com/twentyhq/twenty/issues/9026 fixes #9312 https://github.com/user-attachments/assets/3d7df3ec-8a5e-4308-8993-82c715edc683 --------- Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
@ -19,6 +19,9 @@ import styled from '@emotion/styled';
|
|||||||
const StyledMainSection = styled(NavigationDrawerSection)`
|
const StyledMainSection = styled(NavigationDrawerSection)`
|
||||||
min-height: fit-content;
|
min-height: fit-content;
|
||||||
`;
|
`;
|
||||||
|
const StyledInnerContainer = styled.div`
|
||||||
|
height: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
export const MainNavigationDrawerItems = () => {
|
export const MainNavigationDrawerItems = () => {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
@ -60,12 +63,14 @@ export const MainNavigationDrawerItems = () => {
|
|||||||
contextProviderName="navigationDrawer"
|
contextProviderName="navigationDrawer"
|
||||||
componentInstanceId={`scroll-wrapper-navigation-drawer`}
|
componentInstanceId={`scroll-wrapper-navigation-drawer`}
|
||||||
defaultEnableXScroll={false}
|
defaultEnableXScroll={false}
|
||||||
scrollHide={true}
|
scrollbarVariant="no-padding"
|
||||||
>
|
>
|
||||||
<NavigationDrawerOpenedSection />
|
<StyledInnerContainer>
|
||||||
<CurrentWorkspaceMemberFavoritesFolders />
|
<NavigationDrawerOpenedSection />
|
||||||
<WorkspaceFavorites />
|
<CurrentWorkspaceMemberFavoritesFolders />
|
||||||
<RemoteNavigationDrawerSection />
|
<WorkspaceFavorites />
|
||||||
|
<RemoteNavigationDrawerSection />
|
||||||
|
</StyledInnerContainer>
|
||||||
</ScrollWrapper>
|
</ScrollWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -62,7 +62,21 @@ const StyledTableFoot = styled.thead<{ endOfTableSticky?: boolean }>`
|
|||||||
tr {
|
tr {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
${({ endOfTableSticky }) => endOfTableSticky && `bottom: 0;`}
|
background: ${({ theme }) => theme.background.primary};
|
||||||
|
${({ endOfTableSticky }) =>
|
||||||
|
endOfTableSticky &&
|
||||||
|
`
|
||||||
|
bottom: 10px;
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: -10px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 10px;
|
||||||
|
background: inherit;
|
||||||
|
}
|
||||||
|
`}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
|||||||
import { NAV_DRAWER_WIDTHS } from '@/ui/navigation/navigation-drawer/constants/NavDrawerWidths';
|
import { NAV_DRAWER_WIDTHS } from '@/ui/navigation/navigation-drawer/constants/NavDrawerWidths';
|
||||||
|
|
||||||
import { useIsSettingsDrawer } from '@/navigation/hooks/useIsSettingsDrawer';
|
import { useIsSettingsDrawer } from '@/navigation/hooks/useIsSettingsDrawer';
|
||||||
|
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
|
||||||
import { isNavigationDrawerExpandedState } from '../../states/isNavigationDrawerExpanded';
|
import { isNavigationDrawerExpandedState } from '../../states/isNavigationDrawerExpanded';
|
||||||
import { NavigationDrawerBackButton } from './NavigationDrawerBackButton';
|
import { NavigationDrawerBackButton } from './NavigationDrawerBackButton';
|
||||||
import { NavigationDrawerHeader } from './NavigationDrawerHeader';
|
import { NavigationDrawerHeader } from './NavigationDrawerHeader';
|
||||||
@ -43,7 +44,7 @@ const StyledContainer = styled.div<{
|
|||||||
? theme.spacing(3, 8)
|
? theme.spacing(3, 8)
|
||||||
: theme.spacing(3, 8, 4, 0)
|
: theme.spacing(3, 8, 4, 0)
|
||||||
: theme.spacing(3, 2, 4)};
|
: theme.spacing(3, 2, 4)};
|
||||||
|
padding-right: 0px;
|
||||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
@ -123,7 +124,7 @@ export const NavigationDrawer = ({
|
|||||||
<StyledItemsContainer isSettings={isSettingsDrawer}>
|
<StyledItemsContainer isSettings={isSettingsDrawer}>
|
||||||
{children}
|
{children}
|
||||||
</StyledItemsContainer>
|
</StyledItemsContainer>
|
||||||
{footer}
|
<NavigationDrawerSection>{footer}</NavigationDrawerSection>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
</StyledAnimatedContainer>
|
</StyledAnimatedContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -92,7 +92,7 @@ const StyledItem = styled('button', {
|
|||||||
width: ${(props) =>
|
width: ${(props) =>
|
||||||
!props.isNavigationDrawerExpanded
|
!props.isNavigationDrawerExpanded
|
||||||
? `calc(${NAV_DRAWER_WIDTHS.menu.desktop.collapsed}px - ${props.theme.spacing(5.5)})`
|
? `calc(${NAV_DRAWER_WIDTHS.menu.desktop.collapsed}px - ${props.theme.spacing(5.5)})`
|
||||||
: `calc(100% - ${props.theme.spacing(2)})`};
|
: `calc(100% - ${props.theme.spacing(1.5)})`};
|
||||||
|
|
||||||
${({ isDragging }) =>
|
${({ isDragging }) =>
|
||||||
isDragging &&
|
isDragging &&
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
import { useIsMobile } from 'twenty-ui';
|
||||||
|
|
||||||
const StyledSection = styled.div`
|
const StyledSection = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -9,4 +10,25 @@ const StyledSection = styled.div`
|
|||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export { StyledSection as NavigationDrawerSection };
|
const StyledSectionInnerContainerMinusScrollPadding = styled.div<{
|
||||||
|
isMobile: boolean;
|
||||||
|
}>`
|
||||||
|
width: calc(
|
||||||
|
100% - ${({ isMobile, theme }) => (isMobile ? 0 : theme.spacing(2))}
|
||||||
|
);
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const NavigationDrawerSection = ({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) => {
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
return (
|
||||||
|
<StyledSection>
|
||||||
|
<StyledSectionInnerContainerMinusScrollPadding isMobile={isMobile}>
|
||||||
|
{children}
|
||||||
|
</StyledSectionInnerContainerMinusScrollPadding>
|
||||||
|
</StyledSection>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@ -13,13 +13,14 @@ import { scrollWrapperInstanceComponentState } from '@/ui/utilities/scroll/state
|
|||||||
import { scrollWrapperScrollLeftComponentState } from '@/ui/utilities/scroll/states/scrollWrapperScrollLeftComponentState';
|
import { scrollWrapperScrollLeftComponentState } from '@/ui/utilities/scroll/states/scrollWrapperScrollLeftComponentState';
|
||||||
import { scrollWrapperScrollTopComponentState } from '@/ui/utilities/scroll/states/scrollWrapperScrollTopComponentState';
|
import { scrollWrapperScrollTopComponentState } from '@/ui/utilities/scroll/states/scrollWrapperScrollTopComponentState';
|
||||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||||
|
import { css } from '@emotion/react';
|
||||||
import 'overlayscrollbars/overlayscrollbars.css';
|
import 'overlayscrollbars/overlayscrollbars.css';
|
||||||
|
|
||||||
type HeightMode = 'full' | 'fit-content';
|
type HeightMode = 'full' | 'fit-content';
|
||||||
|
|
||||||
const StyledScrollWrapper = styled.div<{
|
const StyledScrollWrapper = styled.div<{
|
||||||
scrollHide?: boolean;
|
|
||||||
heightMode: HeightMode;
|
heightMode: HeightMode;
|
||||||
|
scrollbarVariant: 'with-padding' | 'no-padding';
|
||||||
}>`
|
}>`
|
||||||
display: flex;
|
display: flex;
|
||||||
height: ${({ heightMode }) => {
|
height: ${({ heightMode }) => {
|
||||||
@ -33,9 +34,38 @@ const StyledScrollWrapper = styled.div<{
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.os-scrollbar-handle {
|
.os-scrollbar-handle {
|
||||||
background-color: ${({ theme, scrollHide }) =>
|
background-color: ${({ theme }) => theme.border.color.strong};
|
||||||
scrollHide ? 'transparent' : theme.border.color.medium};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep horizontal scrollbar always visible
|
||||||
|
.os-scrollbar-horizontal {
|
||||||
|
&.os-scrollbar-auto-hide {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
.os-scrollbar-track {
|
||||||
|
visibility: visible !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-scrollbar {
|
||||||
|
transition:
|
||||||
|
opacity 300ms,
|
||||||
|
visibility 300ms,
|
||||||
|
top 300ms,
|
||||||
|
right 300ms,
|
||||||
|
bottom 300ms,
|
||||||
|
left 300ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
${({ scrollbarVariant }) =>
|
||||||
|
scrollbarVariant === 'no-padding' &&
|
||||||
|
css`
|
||||||
|
.os-scrollbar {
|
||||||
|
--os-size: 6px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledInnerContainer = styled.div`
|
const StyledInnerContainer = styled.div`
|
||||||
@ -49,8 +79,8 @@ export type ScrollWrapperProps = {
|
|||||||
defaultEnableXScroll?: boolean;
|
defaultEnableXScroll?: boolean;
|
||||||
defaultEnableYScroll?: boolean;
|
defaultEnableYScroll?: boolean;
|
||||||
contextProviderName: ContextProviderName;
|
contextProviderName: ContextProviderName;
|
||||||
scrollHide?: boolean;
|
|
||||||
componentInstanceId: string;
|
componentInstanceId: string;
|
||||||
|
scrollbarVariant?: 'with-padding' | 'no-padding';
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ScrollWrapper = ({
|
export const ScrollWrapper = ({
|
||||||
@ -61,7 +91,7 @@ export const ScrollWrapper = ({
|
|||||||
defaultEnableXScroll = true,
|
defaultEnableXScroll = true,
|
||||||
defaultEnableYScroll = true,
|
defaultEnableYScroll = true,
|
||||||
contextProviderName,
|
contextProviderName,
|
||||||
scrollHide = false,
|
scrollbarVariant = 'with-padding',
|
||||||
}: ScrollWrapperProps) => {
|
}: ScrollWrapperProps) => {
|
||||||
const scrollableRef = useRef<HTMLDivElement>(null);
|
const scrollableRef = useRef<HTMLDivElement>(null);
|
||||||
const Context = getContextByProviderName(contextProviderName);
|
const Context = getContextByProviderName(contextProviderName);
|
||||||
@ -94,39 +124,23 @@ export const ScrollWrapper = ({
|
|||||||
autoHideDelay: 500,
|
autoHideDelay: 500,
|
||||||
},
|
},
|
||||||
overflow: {
|
overflow: {
|
||||||
x: defaultEnableXScroll ? 'scroll' : 'hidden',
|
x: defaultEnableXScroll ? undefined : 'hidden',
|
||||||
y: defaultEnableYScroll ? 'scroll' : 'hidden',
|
y: defaultEnableYScroll ? undefined : 'hidden',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
events: {
|
events: {
|
||||||
scroll: (osInstance) => {
|
scroll: (osInstance) => {
|
||||||
const {
|
const { scrollOffsetElement: target, scrollbarVertical } =
|
||||||
scrollOffsetElement: target,
|
osInstance.elements();
|
||||||
scrollbarHorizontal,
|
// Hide vertical scrollbar by default
|
||||||
scrollbarVertical,
|
if (scrollbarVertical !== null) {
|
||||||
} = osInstance.elements();
|
scrollbarVertical.track.style.visibility = 'hidden';
|
||||||
|
}
|
||||||
|
|
||||||
// Hide scrollbars by default
|
// Show vertical scrollbar based on scroll direction
|
||||||
[scrollbarHorizontal, scrollbarVertical].forEach((scrollbar) => {
|
|
||||||
if (scrollbar !== null) {
|
|
||||||
scrollbar.track.style.visibility = 'hidden';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Show appropriate scrollbar based on scroll direction
|
|
||||||
const isHorizontalScroll =
|
|
||||||
target.scrollLeft !== Number(target.dataset.lastScrollLeft || '0');
|
|
||||||
const isVerticalScroll =
|
const isVerticalScroll =
|
||||||
target.scrollTop !== Number(target.dataset.lastScrollTop || '0');
|
target.scrollTop !== Number(target.dataset.lastScrollTop || '0');
|
||||||
|
|
||||||
// Show scrollbar based on scroll direction only with explicit conditions
|
|
||||||
if (
|
|
||||||
isHorizontalScroll === true &&
|
|
||||||
scrollbarHorizontal !== null &&
|
|
||||||
target.scrollWidth > target.clientWidth
|
|
||||||
) {
|
|
||||||
scrollbarHorizontal.track.style.visibility = 'visible';
|
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
isVerticalScroll === true &&
|
isVerticalScroll === true &&
|
||||||
scrollbarVertical !== null &&
|
scrollbarVertical !== null &&
|
||||||
@ -134,9 +148,7 @@ export const ScrollWrapper = ({
|
|||||||
) {
|
) {
|
||||||
scrollbarVertical.track.style.visibility = 'visible';
|
scrollbarVertical.track.style.visibility = 'visible';
|
||||||
}
|
}
|
||||||
|
// Update vertical scroll positions
|
||||||
// Update scroll positions
|
|
||||||
target.dataset.lastScrollLeft = target.scrollLeft.toString();
|
|
||||||
target.dataset.lastScrollTop = target.scrollTop.toString();
|
target.dataset.lastScrollTop = target.scrollTop.toString();
|
||||||
|
|
||||||
handleScroll(osInstance);
|
handleScroll(osInstance);
|
||||||
@ -146,19 +158,16 @@ export const ScrollWrapper = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const currentRef = scrollableRef.current;
|
const currentRef = scrollableRef.current;
|
||||||
|
|
||||||
if (currentRef !== null) {
|
if (currentRef !== null) {
|
||||||
initialize(currentRef);
|
initialize(currentRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
// Reset all component-specific Recoil state
|
// Reset vertical scroll component-specific Recoil state
|
||||||
setScrollTop(0);
|
setScrollTop(0);
|
||||||
setScrollLeft(0);
|
|
||||||
setOverlayScrollbars(null);
|
setOverlayScrollbars(null);
|
||||||
instance()?.destroy();
|
instance()?.destroy();
|
||||||
};
|
};
|
||||||
}, [initialize, instance, setScrollTop, setScrollLeft, setOverlayScrollbars]);
|
}, [initialize, instance, setScrollTop, setOverlayScrollbars]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setOverlayScrollbars(instance());
|
setOverlayScrollbars(instance());
|
||||||
@ -177,8 +186,8 @@ export const ScrollWrapper = ({
|
|||||||
<StyledScrollWrapper
|
<StyledScrollWrapper
|
||||||
ref={scrollableRef}
|
ref={scrollableRef}
|
||||||
className={className}
|
className={className}
|
||||||
scrollHide={scrollHide}
|
|
||||||
heightMode={heightMode}
|
heightMode={heightMode}
|
||||||
|
scrollbarVariant={scrollbarVariant}
|
||||||
>
|
>
|
||||||
<StyledInnerContainer>{children}</StyledInnerContainer>
|
<StyledInnerContainer>{children}</StyledInnerContainer>
|
||||||
</StyledScrollWrapper>
|
</StyledScrollWrapper>
|
||||||
|
|||||||
Reference in New Issue
Block a user