feishin/src/renderer/components/scroll-area/index.tsx

154 lines
4.8 KiB
TypeScript
Raw Normal View History

2022-12-19 15:59:14 -08:00
import type { ScrollAreaProps as MantineScrollAreaProps } from '@mantine/core';
import { ScrollArea as MantineScrollArea } from '@mantine/core';
import { useMergedRef } from '@mantine/hooks';
import { useInView } from 'framer-motion';
import { useOverlayScrollbars } from 'overlayscrollbars-react';
import { forwardRef, Ref, useEffect, useRef, useState } from 'react';
2022-12-19 15:59:14 -08:00
import styled from 'styled-components';
2023-01-02 16:59:21 -08:00
import { PageHeader, PageHeaderProps } from '/@/renderer/components/page-header';
2023-03-30 06:44:33 -07:00
import { useWindowSettings } from '/@/renderer/store/settings.store';
2023-03-28 23:59:51 -07:00
import { Platform } from '/@/renderer/types';
2022-12-19 15:59:14 -08:00
interface ScrollAreaProps extends MantineScrollAreaProps {
2023-07-01 19:10:05 -07:00
children: React.ReactNode;
2022-12-19 15:59:14 -08:00
}
const StyledScrollArea = styled(MantineScrollArea)`
2023-07-01 19:10:05 -07:00
& .mantine-ScrollArea-thumb {
background: var(--scrollbar-thumb-bg);
border-radius: 0;
}
& .mantine-ScrollArea-scrollbar {
padding: 0;
background: var(--scrollbar-track-bg);
}
& .mantine-ScrollArea-viewport > div {
display: block !important;
}
2022-12-19 15:59:14 -08:00
`;
2023-03-28 23:59:51 -07:00
const StyledNativeScrollArea = styled.div<{ scrollBarOffset?: string; windowBarStyle?: Platform }>`
2023-07-01 19:10:05 -07:00
height: 100%;
2023-01-02 16:59:21 -08:00
`;
2022-12-31 16:50:20 -08:00
export const ScrollArea = forwardRef(({ children, ...props }: ScrollAreaProps, ref: Ref<any>) => {
2023-07-01 19:10:05 -07:00
return (
<StyledScrollArea
ref={ref}
scrollbarSize={12}
{...props}
>
{children}
</StyledScrollArea>
);
2022-12-31 16:50:20 -08:00
});
2023-01-02 16:59:21 -08:00
interface NativeScrollAreaProps {
2023-07-01 19:10:05 -07:00
children: React.ReactNode;
debugScrollPosition?: boolean;
noHeader?: boolean;
pageHeaderProps?: PageHeaderProps & { offset?: any; target?: any };
scrollBarOffset?: string;
scrollHideDelay?: number;
style?: React.CSSProperties;
2023-01-02 16:59:21 -08:00
}
export const NativeScrollArea = forwardRef(
2023-07-01 19:10:05 -07:00
(
{
children,
pageHeaderProps,
debugScrollPosition,
scrollBarOffset,
scrollHideDelay,
noHeader,
...props
}: NativeScrollAreaProps,
ref: Ref<HTMLDivElement>,
) => {
const { windowBarStyle } = useWindowSettings();
const containerRef = useRef(null);
const [isPastOffset, setIsPastOffset] = useState(false);
// useInView initializes as false, so we need to track this to properly render the header
const isInViewInitializedRef = useRef<boolean | null>(null);
2023-07-01 19:10:05 -07:00
const isInView = useInView({
current: pageHeaderProps?.target?.current,
2023-07-01 19:10:05 -07:00
});
useEffect(() => {
if (!isInViewInitializedRef.current && isInView) {
isInViewInitializedRef.current = true;
}
}, [isInView]);
const [initialize] = useOverlayScrollbars({
defer: true,
events: {
scroll: (_instance, e) => {
if (!pageHeaderProps?.offset) {
return;
}
const offset = pageHeaderProps?.offset;
const scrollTop = (e?.target as HTMLDivElement)?.scrollTop;
if (scrollTop > offset && isPastOffset === false) {
setIsPastOffset(true);
} else if (scrollTop <= offset && isPastOffset === true) {
setIsPastOffset(false);
}
},
},
options: {
overflow: { x: 'hidden', y: 'scroll' },
scrollbars: {
autoHide: 'move',
autoHideDelay: 500,
pointers: ['mouse', 'pen', 'touch'],
theme: 'feishin',
visibility: 'visible',
},
},
});
2023-07-01 19:10:05 -07:00
useEffect(() => {
if (containerRef.current) {
initialize(containerRef.current as HTMLDivElement);
}
}, [initialize]);
2023-07-01 19:10:05 -07:00
// console.log('isPastOffsetRef.current', isPastOffsetRef.current);
2023-07-01 19:10:05 -07:00
const mergedRef = useMergedRef(ref, containerRef);
2023-07-01 19:10:05 -07:00
const shouldShowHeader =
!noHeader && (isPastOffset || (isInViewInitializedRef.current && !isInView));
2023-07-01 19:10:05 -07:00
return (
<>
{shouldShowHeader && (
2023-07-01 19:10:05 -07:00
<PageHeader
animated
isHidden={false}
2023-07-01 19:10:05 -07:00
position="absolute"
{...pageHeaderProps}
/>
)}
<StyledNativeScrollArea
ref={mergedRef}
scrollBarOffset={scrollBarOffset}
windowBarStyle={windowBarStyle}
{...props}
>
{children}
</StyledNativeScrollArea>
</>
);
},
2023-01-02 16:59:21 -08:00
);