chore: improves scrolling performance and loading component for dark theme (#8795)
What's Changed * improve scrolling performance * loading component for dark theme
This commit is contained in:
@@ -15,13 +15,18 @@ import {
|
|||||||
type PDFVirtuosoContext,
|
type PDFVirtuosoContext,
|
||||||
type PDFVirtuosoProps,
|
type PDFVirtuosoProps,
|
||||||
Scroller,
|
Scroller,
|
||||||
|
ScrollSeekPlaceholder,
|
||||||
} from '@affine/core/modules/pdf/views';
|
} from '@affine/core/modules/pdf/views';
|
||||||
import type { AttachmentBlockModel } from '@blocksuite/affine/blocks';
|
import type { AttachmentBlockModel } from '@blocksuite/affine/blocks';
|
||||||
import { CollapseIcon, ExpandIcon } from '@blocksuite/icons/rc';
|
import { CollapseIcon, ExpandIcon } from '@blocksuite/icons/rc';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Virtuoso, type VirtuosoHandle } from 'react-virtuoso';
|
import {
|
||||||
|
type ScrollSeekConfiguration,
|
||||||
|
Virtuoso,
|
||||||
|
type VirtuosoHandle,
|
||||||
|
} from 'react-virtuoso';
|
||||||
|
|
||||||
import * as styles from './styles.css';
|
import * as styles from './styles.css';
|
||||||
import { calculatePageNum } from './utils';
|
import { calculatePageNum } from './utils';
|
||||||
@@ -112,6 +117,13 @@ const PDFViewerInner = ({ pdf, state }: PDFViewerInnerProps) => {
|
|||||||
};
|
};
|
||||||
}, [state, viewportInfo, onPageSelect]);
|
}, [state, viewportInfo, onPageSelect]);
|
||||||
|
|
||||||
|
const scrollSeekConfig = useMemo<ScrollSeekConfiguration>(() => {
|
||||||
|
return {
|
||||||
|
enter: velocity => Math.abs(velocity) > 1024,
|
||||||
|
exit: velocity => Math.abs(velocity) < 10,
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const viewer = viewerRef.current;
|
const viewer = viewerRef.current;
|
||||||
if (!viewer) return;
|
if (!viewer) return;
|
||||||
@@ -142,12 +154,14 @@ const PDFViewerInner = ({ pdf, state }: PDFViewerInnerProps) => {
|
|||||||
Scroller,
|
Scroller,
|
||||||
Header: ListPadding,
|
Header: ListPadding,
|
||||||
Footer: ListPadding,
|
Footer: ListPadding,
|
||||||
|
ScrollSeekPlaceholder,
|
||||||
}}
|
}}
|
||||||
context={{
|
context={{
|
||||||
width: state.meta.width,
|
width: state.meta.width,
|
||||||
height: state.meta.height,
|
height: state.meta.height,
|
||||||
pageClassName: styles.pdfPage,
|
pageClassName: styles.pdfPage,
|
||||||
}}
|
}}
|
||||||
|
scrollSeekConfiguration={scrollSeekConfig}
|
||||||
/>
|
/>
|
||||||
<div className={clsx(['thumbnails', styles.pdfThumbnails])}>
|
<div className={clsx(['thumbnails', styles.pdfThumbnails])}>
|
||||||
<div className={clsx([styles.pdfThumbnailsList, { collapsed }])}>
|
<div className={clsx([styles.pdfThumbnailsList, { collapsed }])}>
|
||||||
@@ -159,9 +173,11 @@ const PDFViewerInner = ({ pdf, state }: PDFViewerInnerProps) => {
|
|||||||
itemContent={pageContent}
|
itemContent={pageContent}
|
||||||
components={{
|
components={{
|
||||||
Item,
|
Item,
|
||||||
Scroller,
|
|
||||||
List: ListWithSmallGap,
|
List: ListWithSmallGap,
|
||||||
|
Scroller,
|
||||||
|
ScrollSeekPlaceholder,
|
||||||
}}
|
}}
|
||||||
|
scrollSeekConfiguration={scrollSeekConfig}
|
||||||
style={thumbnailsConfig.style}
|
style={thumbnailsConfig.style}
|
||||||
context={thumbnailsConfig.context}
|
context={thumbnailsConfig.context}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export const viewer = style({
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
resize: 'both',
|
resize: 'none',
|
||||||
selectors: {
|
selectors: {
|
||||||
'&:before': {
|
'&:before': {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Scrollable } from '@affine/component';
|
import { Scrollable } from '@affine/component';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { type CSSProperties, forwardRef, memo } from 'react';
|
import { type CSSProperties, forwardRef, memo } from 'react';
|
||||||
import type { VirtuosoProps } from 'react-virtuoso';
|
import type { ScrollSeekPlaceholderProps, VirtuosoProps } from 'react-virtuoso';
|
||||||
|
|
||||||
import * as styles from './styles.css';
|
import * as styles from './styles.css';
|
||||||
|
|
||||||
@@ -27,6 +27,26 @@ export const Scroller = forwardRef<HTMLDivElement, PDFVirtuosoProps>(
|
|||||||
|
|
||||||
Scroller.displayName = 'pdf-virtuoso-scroller';
|
Scroller.displayName = 'pdf-virtuoso-scroller';
|
||||||
|
|
||||||
|
export const ScrollSeekPlaceholder = forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
ScrollSeekPlaceholderProps & {
|
||||||
|
context?: PDFVirtuosoContext;
|
||||||
|
}
|
||||||
|
>(({ context }, ref) => {
|
||||||
|
const className = context?.pageClassName;
|
||||||
|
const width = context?.width ?? 537;
|
||||||
|
const height = context?.height ?? 759;
|
||||||
|
const style = { width, aspectRatio: `${width} / ${height}` };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className} style={style} ref={ref}>
|
||||||
|
<LoadingSvg />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ScrollSeekPlaceholder.displayName = 'pdf-virtuoso-scroll-seek-placeholder';
|
||||||
|
|
||||||
export const List = forwardRef<HTMLDivElement, PDFVirtuosoProps>(
|
export const List = forwardRef<HTMLDivElement, PDFVirtuosoProps>(
|
||||||
({ context: _, className, ...props }, ref) => {
|
({ context: _, className, ...props }, ref) => {
|
||||||
return (
|
return (
|
||||||
@@ -63,123 +83,33 @@ export const ListPadding = () => (
|
|||||||
<div style={{ width: '100%', height: '20px' }} />
|
<div style={{ width: '100%', height: '20px' }} />
|
||||||
);
|
);
|
||||||
|
|
||||||
export const LoadingSvg = memo(function LoadingSvg({
|
export const LoadingSvg = memo(
|
||||||
style,
|
({ className, style }: { className?: string; style?: CSSProperties }) => {
|
||||||
className,
|
return (
|
||||||
}: {
|
<svg
|
||||||
style?: CSSProperties;
|
className={clsx([styles.pdfLoading, className])}
|
||||||
className?: string;
|
style={style}
|
||||||
}) {
|
width="16"
|
||||||
return (
|
height="24"
|
||||||
<svg
|
viewBox="0 0 537 759"
|
||||||
className={clsx([styles.pdfLoading, className])}
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
style={style}
|
fill="currentColor"
|
||||||
width="16"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 537 759"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<rect width="537" height="759" fill="white" />
|
|
||||||
<rect
|
|
||||||
x="32"
|
|
||||||
y="82"
|
|
||||||
width="361"
|
|
||||||
height="30"
|
|
||||||
rx="4"
|
|
||||||
fill="black"
|
|
||||||
fillOpacity="0.07"
|
fillOpacity="0.07"
|
||||||
/>
|
>
|
||||||
<rect
|
<rect x="32" y="82" width="361" height="30" rx="4" />
|
||||||
x="32"
|
<rect x="32" y="142" width="444" height="30" rx="4" />
|
||||||
y="142"
|
<rect x="32" y="202" width="387" height="30" rx="4" />
|
||||||
width="444"
|
<rect x="32" y="262" width="461" height="30" rx="4" />
|
||||||
height="30"
|
<rect x="32" y="322" width="282" height="30" rx="4" />
|
||||||
rx="4"
|
<rect x="32" y="382" width="361" height="30" rx="4" />
|
||||||
fill="black"
|
<rect x="32" y="442" width="444" height="30" rx="4" />
|
||||||
fillOpacity="0.07"
|
<rect x="32" y="502" width="240" height="30" rx="4" />
|
||||||
/>
|
<rect x="32" y="562" width="201" height="30" rx="4" />
|
||||||
<rect
|
<rect x="32" y="622" width="224" height="30" rx="4" />
|
||||||
x="32"
|
<rect x="314" y="502" width="191" height="166" rx="4" />
|
||||||
y="202"
|
</svg>
|
||||||
width="387"
|
);
|
||||||
height="30"
|
}
|
||||||
rx="4"
|
);
|
||||||
fill="black"
|
|
||||||
fillOpacity="0.07"
|
LoadingSvg.displayName = 'pdf-loading';
|
||||||
/>
|
|
||||||
<rect
|
|
||||||
x="32"
|
|
||||||
y="262"
|
|
||||||
width="461"
|
|
||||||
height="30"
|
|
||||||
rx="4"
|
|
||||||
fill="black"
|
|
||||||
fillOpacity="0.07"
|
|
||||||
/>
|
|
||||||
<rect
|
|
||||||
x="32"
|
|
||||||
y="322"
|
|
||||||
width="282"
|
|
||||||
height="30"
|
|
||||||
rx="4"
|
|
||||||
fill="black"
|
|
||||||
fillOpacity="0.07"
|
|
||||||
/>
|
|
||||||
<rect
|
|
||||||
x="32"
|
|
||||||
y="382"
|
|
||||||
width="361"
|
|
||||||
height="30"
|
|
||||||
rx="4"
|
|
||||||
fill="black"
|
|
||||||
fillOpacity="0.07"
|
|
||||||
/>
|
|
||||||
<rect
|
|
||||||
x="32"
|
|
||||||
y="442"
|
|
||||||
width="444"
|
|
||||||
height="30"
|
|
||||||
rx="4"
|
|
||||||
fill="black"
|
|
||||||
fillOpacity="0.07"
|
|
||||||
/>
|
|
||||||
<rect
|
|
||||||
x="32"
|
|
||||||
y="502"
|
|
||||||
width="240"
|
|
||||||
height="30"
|
|
||||||
rx="4"
|
|
||||||
fill="black"
|
|
||||||
fillOpacity="0.07"
|
|
||||||
/>
|
|
||||||
<rect
|
|
||||||
x="32"
|
|
||||||
y="562"
|
|
||||||
width="201"
|
|
||||||
height="30"
|
|
||||||
rx="4"
|
|
||||||
fill="black"
|
|
||||||
fillOpacity="0.07"
|
|
||||||
/>
|
|
||||||
<rect
|
|
||||||
x="32"
|
|
||||||
y="622"
|
|
||||||
width="224"
|
|
||||||
height="30"
|
|
||||||
rx="4"
|
|
||||||
fill="black"
|
|
||||||
fillOpacity="0.07"
|
|
||||||
/>
|
|
||||||
<rect
|
|
||||||
x="314"
|
|
||||||
y="502"
|
|
||||||
width="191"
|
|
||||||
height="166"
|
|
||||||
rx="4"
|
|
||||||
fill="black"
|
|
||||||
fillOpacity="0.07"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -7,5 +7,6 @@ export {
|
|||||||
type PDFVirtuosoContext,
|
type PDFVirtuosoContext,
|
||||||
type PDFVirtuosoProps,
|
type PDFVirtuosoProps,
|
||||||
Scroller,
|
Scroller,
|
||||||
|
ScrollSeekPlaceholder,
|
||||||
} from './components';
|
} from './components';
|
||||||
export { PDFPageRenderer } from './page-renderer';
|
export { PDFPageRenderer } from './page-renderer';
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export const pdfPageCanvas = style({
|
|||||||
export const pdfLoading = style({
|
export const pdfLoading = style({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
|
margin: 'auto',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
maxWidth: '537px',
|
maxWidth: '537px',
|
||||||
|
|||||||
Reference in New Issue
Block a user