refactor(view):调整无限列表功能所使用的库。
This commit is contained in:
		| @@ -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" | ||||
|   | ||||
							
								
								
									
										42
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										42
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @@ -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'} | ||||
|   | ||||
| @@ -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<EventEmitter>(EventBusContext); | ||||
|   const virtualizer = useVirtualizer({ | ||||
|     count: fileCount, | ||||
|     getScrollElement: () => parentRef.current, | ||||
|     estimateSize: () => 100 | ||||
|   }); | ||||
|   const items = virtualizer.getVirtualItems(); | ||||
|   const virtualListRef = useRef<VariableSizeList | null>(); | ||||
|   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 ( | ||||
|     <div style={{ overflow: 'auto', contain: 'strict', height: '100%' }} ref={parentRef}> | ||||
|     <div | ||||
|       style={{ | ||||
|         overflow: 'auto', | ||||
|         contain: 'strict', | ||||
|         height: '100%' | ||||
|       }} | ||||
|     > | ||||
|       {!isEmpty(files) && ( | ||||
|         <Box pos="relative" w="100%" h={virtualizer.getTotalSize()}> | ||||
|           <Stack | ||||
|             pos="absolute" | ||||
|             top={0} | ||||
|             left={0} | ||||
|             w="100%" | ||||
|             justify="start" | ||||
|             align="center" | ||||
|             spacing={0} | ||||
|             style={{ | ||||
|               transform: `translateY(${items[0].start}px)` | ||||
|             }} | ||||
|           > | ||||
|             {items.map(row => ( | ||||
|               <img | ||||
|                 key={files[row.index].filename} | ||||
|                 src={files[row.index].path} | ||||
|                 ref={virtualizer.measureElement} | ||||
|                 data-index={row.index} | ||||
|                 style={{ width: `${zoom}%` }} | ||||
|               /> | ||||
|             ))} | ||||
|           </Stack> | ||||
|         </Box> | ||||
|         <VariableSizeList | ||||
|           itemData={files} | ||||
|           itemCount={fileCount} | ||||
|           itemSize={index => files[index].height * (zoom / 100)} | ||||
|           height={viewHeight} | ||||
|           width="100%" | ||||
|           ref={virtualListRef} | ||||
|           onItemsRendered={handleOnRenderAction} | ||||
|         > | ||||
|           {({ index, style, data }) => ( | ||||
|             <div | ||||
|               style={mergeLeft(style, { | ||||
|                 display: 'flex', | ||||
|                 flexDirection: 'row', | ||||
|                 justifyContent: 'center', | ||||
|                 alignItems: 'flex-start' | ||||
|               })} | ||||
|             > | ||||
|               <img src={data[index].path} style={{ width: data[index].width * (zoom / 100) }} /> | ||||
|             </div> | ||||
|           )} | ||||
|         </VariableSizeList> | ||||
|       )} | ||||
|     </div> | ||||
|   ); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user