diff --git a/package.json b/package.json index cf24cec..51b8f92 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,6 @@ "@mantine/notifications": "^6.0.0", "@mantine/nprogress": "^6.0.0", "@tabler/icons-react": "^2.8.0", - "@tanstack/react-virtual": "3.0.0-beta.35", "@tauri-apps/api": "^1.2.0", "events": "^3.3.0", "framer-motion": "^8.1.1", @@ -30,6 +29,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-use": "^17.4.0", + "react-window": "^1.8.8", "use-immer": "^0.8.1", "zustand": "^4.2.0" }, @@ -41,6 +41,7 @@ "@types/ramda": "^0.28.20", "@types/react": "^18.0.15", "@types/react-dom": "^18.0.6", + "@types/react-window": "^1.8.5", "@vitejs/plugin-react": "^3.0.0", "typescript": "^4.6.4", "vite": "^4.0.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bca3437..0516282 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,7 +12,6 @@ specifiers: '@mantine/notifications': ^6.0.0 '@mantine/nprogress': ^6.0.0 '@tabler/icons-react': ^2.8.0 - '@tanstack/react-virtual': 3.0.0-beta.35 '@tauri-apps/api': ^1.2.0 '@tauri-apps/cli': ^1.2.2 '@types/events': ^3.0.0 @@ -20,6 +19,7 @@ specifiers: '@types/ramda': ^0.28.20 '@types/react': ^18.0.15 '@types/react-dom': ^18.0.6 + '@types/react-window': ^1.8.5 '@vitejs/plugin-react': ^3.0.0 events: ^3.3.0 framer-motion: ^8.1.1 @@ -28,6 +28,7 @@ specifiers: react: ^18.2.0 react-dom: ^18.2.0 react-use: ^17.4.0 + react-window: ^1.8.8 typescript: ^4.6.4 use-immer: ^0.8.1 vite: ^4.0.0 @@ -44,7 +45,6 @@ dependencies: '@mantine/notifications': 6.0.0_uziugpv5zwkk3pqsn64qbjkxrm '@mantine/nprogress': 6.0.0_uziugpv5zwkk3pqsn64qbjkxrm '@tabler/icons-react': 2.8.0_react@18.2.0 - '@tanstack/react-virtual': 3.0.0-beta.35_react@18.2.0 '@tauri-apps/api': 1.2.0 events: 3.3.0 framer-motion: 8.5.5_biqbaboplfbrettd7655fr4n2y @@ -53,6 +53,7 @@ dependencies: react: 18.2.0 react-dom: 18.2.0_react@18.2.0 react-use: 17.4.0_biqbaboplfbrettd7655fr4n2y + react-window: 1.8.8_biqbaboplfbrettd7655fr4n2y use-immer: 0.8.1_immer@9.0.19+react@18.2.0 zustand: 4.3.6_immer@9.0.19+react@18.2.0 @@ -64,6 +65,7 @@ devDependencies: '@types/ramda': 0.28.23 '@types/react': 18.0.28 '@types/react-dom': 18.0.11 + '@types/react-window': 1.8.5 '@vitejs/plugin-react': 3.1.0_vite@4.1.4 typescript: 4.9.5 vite: 4.1.4_@types+node@18.14.6 @@ -988,19 +990,6 @@ packages: resolution: {integrity: sha512-8diABuB3J+NEUtdwIXJF0bJSE5VpnnyzWeiZGFq/XlcTYJNEF+36ijiPNUGpsV/QXY6syesJKmmPkUUwD+LxdA==} dev: false - /@tanstack/react-virtual/3.0.0-beta.35_react@18.2.0: - resolution: {integrity: sha512-x4dicQTGao3p7wA1bGJyKsbtabO6UJKNUJdMWbxzrdc9CvpFl+ASOBtQHReDRjjAy7Z5/4loTLle8RtZu+2c2A==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@tanstack/virtual-core': 3.0.0-beta.35 - react: 18.2.0 - dev: false - - /@tanstack/virtual-core/3.0.0-beta.35: - resolution: {integrity: sha512-p+dNBkN70nz3RzsfJImpmg1eoEHoX3X49lOk1N4I6jtUttKzZ/0U2+LqDmErNa5q/ksWD45zegOU4MnHwKRp2w==} - dev: false - /@tauri-apps/api/1.2.0: resolution: {integrity: sha512-lsI54KI6HGf7VImuf/T9pnoejfgkNoXveP14pVV7XarrQ46rOejIVJLFqHI9sRReJMGdh2YuCoI3cc/yCWCsrw==} engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} @@ -1134,6 +1123,12 @@ packages: '@types/react': 18.0.28 dev: true + /@types/react-window/1.8.5: + resolution: {integrity: sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==} + dependencies: + '@types/react': 18.0.28 + dev: true + /@types/react/18.0.28: resolution: {integrity: sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==} dependencies: @@ -1531,6 +1526,10 @@ packages: resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} dev: false + /memoize-one/5.2.1: + resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + dev: false + /ms/2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true @@ -1751,6 +1750,19 @@ packages: tslib: 2.5.0 dev: false + /react-window/1.8.8_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-D4IiBeRtGXziZ1n0XklnFGu7h9gU684zepqyKzgPNzrsrk7xOCxni+TCckjg2Nr/DiaEEGVVmnhYSlT2rB47dQ==} + engines: {node: '>8.0.0'} + peerDependencies: + react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.21.0 + memoize-one: 5.2.1 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + /react/18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} diff --git a/src/components/ContinuationView.tsx b/src/components/ContinuationView.tsx index 8babe4e..f776aa9 100644 --- a/src/components/ContinuationView.tsx +++ b/src/components/ContinuationView.tsx @@ -1,13 +1,11 @@ -import { Box, Stack } from '@mantine/core'; -import { useVirtualizer } from '@tanstack/react-virtual'; import EventEmitter from 'events'; -import { filter, indexOf, isEmpty, length, map, pluck } from 'ramda'; -import { FC, useContext, useLayoutEffect, useMemo, useRef } from 'react'; +import { indexOf, isEmpty, length, map, mergeLeft, pluck, range } from 'ramda'; +import { FC, useCallback, useContext, useMemo, useRef } from 'react'; import { useLifecycles } from 'react-use'; +import { VariableSizeList } from 'react-window'; import { EventBusContext } from '../EventBus'; import { useFileListStore } from '../states/files'; import { useZoomState } from '../states/zoom'; -import { withinRange } from '../utils/offset_func'; export const ContinuationView: FC = () => { const { files } = useFileListStore(); @@ -15,23 +13,24 @@ export const ContinuationView: FC = () => { const viewHeight = useZoomState.use.viewHeight(); const updateActives = useFileListStore.use.updateActiveFiles(); const fileCount = useMemo(() => length(files), [files]); - const parentRef = useRef(); const ebus = useContext(EventBusContext); - const virtualizer = useVirtualizer({ - count: fileCount, - getScrollElement: () => parentRef.current, - estimateSize: () => 100 - }); - const items = virtualizer.getVirtualItems(); + const virtualListRef = useRef(); + const handleOnRenderAction = useCallback( + ({ visibleStartIndex, visibleStopIndex }) => { + console.log('[debug]on render:', visibleStartIndex, visibleStopIndex); + updateActives(map(i => files[i].filename, range(visibleStartIndex, visibleStopIndex + 1))); + }, + [files] + ); useLifecycles( () => { ebus?.addListener('navigate_offset', ({ filename }) => { let index = indexOf(filename, pluck('filename', files)); - virtualizer.scrollToIndex(index); + virtualListRef.current?.scrollToItem(index); }); ebus?.addListener('reset_views', () => { - virtualizer.scrollToOffset(0); + virtualListRef.current?.scrollTo(0); }); }, () => { @@ -40,43 +39,37 @@ export const ContinuationView: FC = () => { } ); - useLayoutEffect(() => { - let rangeStart = virtualizer.scrollOffset; - let rangeEnd = virtualizer.scrollOffset + viewHeight; - let onShowItems = pluck( - 'index', - filter(item => withinRange(item.start, item.end, rangeStart, rangeEnd), items) - ); - updateActives(map(i => files[i].filename, onShowItems)); - }, [virtualizer.scrollOffset, viewHeight, items]); - return ( -
+
{!isEmpty(files) && ( - - - {items.map(row => ( - - ))} - - + files[index].height * (zoom / 100)} + height={viewHeight} + width="100%" + ref={virtualListRef} + onItemsRendered={handleOnRenderAction} + > + {({ index, style, data }) => ( +
+ +
+ )} +
)}
);