From 139d129a63e1a4c307f2f1d53be4401ebdbed5f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=97=E5=98=89=E7=94=B7?= <574980606@qq.com> Date: Tue, 28 Jan 2025 18:43:17 +0800 Subject: [PATCH 1/3] chore: use @rc-component/resize-observer --- .gitignore | 3 ++- examples/animate.tsx | 12 ++++++------ package.json | 28 ++++++++++++++-------------- src/Filler.tsx | 6 ++++-- src/List.tsx | 30 +++++++++++++++--------------- src/ScrollBar.tsx | 12 ++++++------ src/hooks/useFrameWheel.ts | 2 +- src/hooks/useHeights.tsx | 2 +- src/hooks/useMobileTouchMove.ts | 2 +- src/hooks/useScrollDrag.ts | 2 +- src/hooks/useScrollTo.tsx | 8 ++++---- src/mock.tsx | 4 +++- tests/scroll.test.js | 4 ++-- tests/scrollWidth.test.tsx | 4 ++-- 14 files changed, 62 insertions(+), 57 deletions(-) diff --git a/.gitignore b/.gitignore index 6db48dde..99bd1451 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,8 @@ lib/ ~* yarn.lock package-lock.json -!tests/__mocks__/rc-util/lib +pnpm-lock.yaml +!tests/__mocks__/@rc-component/util/lib bun.lockb # umi diff --git a/examples/animate.tsx b/examples/animate.tsx index daa632f3..773110ab 100644 --- a/examples/animate.tsx +++ b/examples/animate.tsx @@ -5,7 +5,7 @@ import * as React from 'react'; import CSSMotion from 'rc-animate/lib/CSSMotion'; import classNames from 'classnames'; import List, { ListRef } from '../src/List'; -import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect'; +import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect'; import './animate.less'; let uuid = 0; @@ -74,7 +74,7 @@ const MyItem: React.ForwardRefRenderFunction = ( motionName="motion" motionAppear={motionAppear} onAppearStart={getCollapsedHeight} - onAppearActive={node => { + onAppearActive={(node) => { motionRef.current = true; return getMaxHeight(node); }} @@ -135,7 +135,7 @@ const Demo = () => { const [animating, setAnimating] = React.useState(false); const [insertIndex, setInsertIndex] = React.useState(); - const listRef = React.useRef(); + const listRef = React.useRef(null); const onClose = (id: string) => { setCloseMap({ @@ -145,7 +145,7 @@ const Demo = () => { }; const onLeave = (id: string) => { - const newData = data.filter(item => item.id !== id); + const newData = data.filter((item) => item.id !== id); setData(newData); }; @@ -159,14 +159,14 @@ const Demo = () => { } const onInsertBefore = (id: string) => { - const index = data.findIndex(item => item.id === id); + const index = data.findIndex((item) => item.id === id); const newData = [...data.slice(0, index), genItem(), ...data.slice(index)]; setInsertIndex(index); setData(newData); lockForAnimation(); }; const onInsertAfter = (id: string) => { - const index = data.findIndex(item => item.id === id) + 1; + const index = data.findIndex((item) => item.id === id) + 1; const newData = [...data.slice(0, index), genItem(), ...data.slice(index)]; setInsertIndex(index); setData(newData); diff --git a/package.json b/package.json index 6f45d2e2..db983558 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "rc-virtual-list", - "version": "3.18.1", + "name": "@rc-component/virtual-list", + "version": "1.0.0", "description": "React Virtual List Component", "engines": { "node": ">=8.x" @@ -36,9 +36,11 @@ "test": "rc-test", "now-build": "npm run build" }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" + "dependencies": { + "@babel/runtime": "^7.20.0", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/util": "^1.2.0", + "classnames": "^2.2.6" }, "devDependencies": { "@rc-component/father-plugin": "^1.0.2", @@ -47,8 +49,8 @@ "@types/classnames": "^2.2.10", "@types/enzyme": "^3.10.5", "@types/jest": "^25.1.3", - "@types/react": "^18.0.8", - "@types/react-dom": "^18.0.3", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", "@types/warning": "^3.0.0", "cheerio": "1.0.0-rc.12", "cross-env": "^5.2.0", @@ -63,14 +65,12 @@ "np": "^5.0.3", "rc-animate": "^2.9.1", "rc-test": "^7.0.15", - "react": "16.14.0", - "react-dom": "16.14.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", "typescript": "^5.0.0" }, - "dependencies": { - "@babel/runtime": "^7.20.0", - "classnames": "^2.2.6", - "rc-resize-observer": "^1.0.0", - "rc-util": "^5.36.0" + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } } diff --git a/src/Filler.tsx b/src/Filler.tsx index 5e480e25..2c99bc73 100644 --- a/src/Filler.tsx +++ b/src/Filler.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import ResizeObserver from 'rc-resize-observer'; +import ResizeObserver from '@rc-component/resize-observer'; import classNames from 'classnames'; export type InnerProps = Pick, 'role' | 'id'>; @@ -95,6 +95,8 @@ const Filler = React.forwardRef( }, ); -Filler.displayName = 'Filler'; +if (process.env.NODE_ENV !== 'production') { + Filler.displayName = 'Filler'; +} export default Filler; diff --git a/src/List.tsx b/src/List.tsx index 9afe3bfb..a473d366 100644 --- a/src/List.tsx +++ b/src/List.tsx @@ -1,8 +1,8 @@ import classNames from 'classnames'; -import type { ResizeObserverProps } from 'rc-resize-observer'; -import ResizeObserver from 'rc-resize-observer'; -import { useEvent } from 'rc-util'; -import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect'; +import type { ResizeObserverProps } from '@rc-component/resize-observer'; +import ResizeObserver from '@rc-component/resize-observer'; +import { useEvent } from '@rc-component/util'; +import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect'; import * as React from 'react'; import { useRef, useState } from 'react'; import { flushSync } from 'react-dom'; @@ -148,9 +148,9 @@ export function RawList(props: ListProps, ref: React.Ref) { const mergedClassName = classNames(prefixCls, { [`${prefixCls}-rtl`]: isRTL }, className); const mergedData = data || EMPTY_DATA; - const componentRef = useRef(); - const fillerInnerRef = useRef(); - const containerRef = useRef(); + const componentRef = useRef(null); + const fillerInnerRef = useRef(null); + const containerRef = useRef(null); // =============================== Item Key =============================== @@ -190,7 +190,7 @@ export function RawList(props: ListProps, ref: React.Ref) { // Put ref here since the range is generate by follow const rangeRef = useRef({ start: 0, end: mergedData.length }); - const diffItemRef = useRef(); + const diffItemRef = useRef(null); const [diffItem] = useDiffItem(mergedData, getKey); diffItemRef.current = diffItem; @@ -303,8 +303,8 @@ export function RawList(props: ListProps, ref: React.Ref) { }; // Hack on scrollbar to enable flash call - const verticalScrollBarRef = useRef(); - const horizontalScrollBarRef = useRef(); + const verticalScrollBarRef = useRef(null); + const horizontalScrollBarRef = useRef(null); const horizontalScrollBarSpinSize = React.useMemo( () => getSpinSize(size.width, scrollWidth), @@ -587,6 +587,7 @@ export function RawList(props: ListProps, ref: React.Ref) { } const containerProps: React.HTMLAttributes = {}; + if (isRTL) { containerProps.dir = 'rtl'; } @@ -594,10 +595,7 @@ export function RawList(props: ListProps, ref: React.Ref) { return (
(props: ListProps, ref: React.Ref) { const List = React.forwardRef>(RawList); -List.displayName = 'List'; +if (process.env.NODE_ENV !== 'production') { + List.displayName = 'List'; +} export default List as ( props: ListProps & { ref?: React.Ref }, diff --git a/src/ScrollBar.tsx b/src/ScrollBar.tsx index 15a9db25..e729b79a 100644 --- a/src/ScrollBar.tsx +++ b/src/ScrollBar.tsx @@ -1,5 +1,5 @@ import classNames from 'classnames'; -import raf from 'rc-util/lib/raf'; +import raf from '@rc-component/util/lib/raf'; import * as React from 'react'; import { getPageXY } from './hooks/useScrollDrag'; @@ -49,12 +49,12 @@ const ScrollBar = React.forwardRef((props, ref) => const isLTR = !rtl; // ========================= Refs ========================= - const scrollbarRef = React.useRef(); - const thumbRef = React.useRef(); + const scrollbarRef = React.useRef(null); + const thumbRef = React.useRef(null); // ======================= Visible ======================== const [visible, setVisible] = React.useState(showScrollBar); - const visibleTimeoutRef = React.useRef>(); + const visibleTimeoutRef = React.useRef>(null); const delayHidden = () => { if (showScrollBar === true || showScrollBar === false) return; @@ -120,9 +120,9 @@ const ScrollBar = React.forwardRef((props, ref) => }, []); // Pass to effect - const enableScrollRangeRef = React.useRef(); + const enableScrollRangeRef = React.useRef(null); enableScrollRangeRef.current = enableScrollRange; - const enableOffsetRangeRef = React.useRef(); + const enableOffsetRangeRef = React.useRef(null); enableOffsetRangeRef.current = enableOffsetRange; React.useEffect(() => { diff --git a/src/hooks/useFrameWheel.ts b/src/hooks/useFrameWheel.ts index 7c0561d9..7dea2ef5 100644 --- a/src/hooks/useFrameWheel.ts +++ b/src/hooks/useFrameWheel.ts @@ -1,4 +1,4 @@ -import raf from 'rc-util/lib/raf'; +import raf from '@rc-component/util/lib/raf'; import { useRef } from 'react'; import isFF from '../utils/isFirefox'; import useOriginScroll from './useOriginScroll'; diff --git a/src/hooks/useHeights.tsx b/src/hooks/useHeights.tsx index bea8a057..646c15c1 100644 --- a/src/hooks/useHeights.tsx +++ b/src/hooks/useHeights.tsx @@ -1,4 +1,4 @@ -import findDOMNode from 'rc-util/lib/Dom/findDOMNode'; +import findDOMNode from '@rc-component/util/lib/Dom/findDOMNode'; import * as React from 'react'; import { useEffect, useRef } from 'react'; import type { GetKey } from '../interface'; diff --git a/src/hooks/useMobileTouchMove.ts b/src/hooks/useMobileTouchMove.ts index 74945628..3a787f80 100644 --- a/src/hooks/useMobileTouchMove.ts +++ b/src/hooks/useMobileTouchMove.ts @@ -1,4 +1,4 @@ -import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect'; +import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect'; import type * as React from 'react'; import { useRef } from 'react'; diff --git a/src/hooks/useScrollDrag.ts b/src/hooks/useScrollDrag.ts index e901eada..f38c22f3 100644 --- a/src/hooks/useScrollDrag.ts +++ b/src/hooks/useScrollDrag.ts @@ -1,4 +1,4 @@ -import raf from 'rc-util/lib/raf'; +import raf from '@rc-component/util/lib/raf'; import * as React from 'react'; function smoothScrollOffset(offset: number) { diff --git a/src/hooks/useScrollTo.tsx b/src/hooks/useScrollTo.tsx index eadfdf5a..f0df313a 100644 --- a/src/hooks/useScrollTo.tsx +++ b/src/hooks/useScrollTo.tsx @@ -1,10 +1,10 @@ /* eslint-disable no-param-reassign */ import * as React from 'react'; -import raf from 'rc-util/lib/raf'; +import raf from '@rc-component/util/lib/raf'; import type { GetKey } from '../interface'; import type CacheMap from '../utils/CacheMap'; -import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect'; -import { warning } from 'rc-util'; +import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect'; +import { warning } from '@rc-component/util'; const MAX_TIMES = 10; @@ -37,7 +37,7 @@ export default function useScrollTo( syncScrollTop: (newTop: number) => void, triggerFlash: () => void, ): (arg: number | ScrollTarget) => void { - const scrollRef = React.useRef(); + const scrollRef = React.useRef(null); const [syncState, setSyncState] = React.useState<{ times: number; diff --git a/src/mock.tsx b/src/mock.tsx index 716f821c..7b47bbea 100644 --- a/src/mock.tsx +++ b/src/mock.tsx @@ -8,6 +8,8 @@ const List = React.forwardRef((props: ListProps, ref: React.Ref) = props: React.PropsWithChildren> & { ref?: React.Ref }, ) => React.ReactElement; -(List as any).displayName = 'List'; +if (process.env.NODE_ENV !== 'production') { + (List as any).displayName = 'List'; +} export default List; diff --git a/tests/scroll.test.js b/tests/scroll.test.js index 59e2f6ca..c2488367 100644 --- a/tests/scroll.test.js +++ b/tests/scroll.test.js @@ -1,8 +1,8 @@ import '@testing-library/jest-dom'; import { act, createEvent, fireEvent, render } from '@testing-library/react'; import { mount } from 'enzyme'; -import { _rs as onLibResize } from 'rc-resize-observer/lib/utils/observerUtil'; -import { resetWarned } from 'rc-util/lib/warning'; +import { _rs as onLibResize } from '@rc-component/resize-observer/lib/utils/observerUtil'; +import { resetWarned } from '@rc-component/util/lib/warning'; import React from 'react'; import List from '../src'; import { spyElementPrototypes } from './utils/domHook'; diff --git a/tests/scrollWidth.test.tsx b/tests/scrollWidth.test.tsx index 79a141c1..e0f25b67 100644 --- a/tests/scrollWidth.test.tsx +++ b/tests/scrollWidth.test.tsx @@ -1,7 +1,7 @@ import '@testing-library/jest-dom'; import { act, fireEvent, render } from '@testing-library/react'; -import { _rs as onLibResize } from 'rc-resize-observer/lib/utils/observerUtil'; -import { spyElementPrototypes } from 'rc-util/lib/test/domHook'; +import { _rs as onLibResize } from '@rc-component/resize-observer/lib/utils/observerUtil'; +import { spyElementPrototypes } from '@rc-component/util/lib/test/domHook'; import React from 'react'; import type { ListRef } from '../src'; import List, { type ListProps } from '../src'; From 3ccbba1b0ae7a7f167b0a9c7f47b01fbee263daf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=97=E5=98=89=E7=94=B7?= <574980606@qq.com> Date: Tue, 28 Jan 2025 18:49:32 +0800 Subject: [PATCH 2/3] fix: fix list --- .gitignore | 1 + examples/no-virtual.tsx | 17 +++--- examples/switch.tsx | 5 +- src/Filler.tsx | 112 ++++++++++++++++++---------------------- 4 files changed, 62 insertions(+), 73 deletions(-) diff --git a/.gitignore b/.gitignore index 99bd1451..615c9938 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ lib/ yarn.lock package-lock.json pnpm-lock.yaml +!tests/__mocks__/rc-util/lib !tests/__mocks__/@rc-component/util/lib bun.lockb diff --git a/examples/no-virtual.tsx b/examples/no-virtual.tsx index 793ab907..ae81eea4 100644 --- a/examples/no-virtual.tsx +++ b/examples/no-virtual.tsx @@ -5,9 +5,11 @@ import List from '../src/List'; interface Item { id: number; height: number; + ref?: React.Ref; } -const MyItem: React.FC = ({ id, height }, ref) => { +const MyItem: React.FC = (props) => { + const { id, height, ref } = props; return ( { boxSizing: 'border-box', }} > - {item => } + {(item) => }

Less Count

@@ -59,12 +61,9 @@ const Demo = () => { itemHeight={30} height={100} itemKey="id" - style={{ - border: '1px solid red', - boxSizing: 'border-box', - }} + style={{ border: '1px solid red', boxSizing: 'border-box' }} > - {item => } + {(item) => }

Less Item Height

@@ -78,7 +77,7 @@ const Demo = () => { boxSizing: 'border-box', }} > - {item => } + {(item) => }

Without Height

@@ -91,7 +90,7 @@ const Demo = () => { boxSizing: 'border-box', }} > - {item => } + {(item) => }
diff --git a/examples/switch.tsx b/examples/switch.tsx index 5339e887..a638251e 100644 --- a/examples/switch.tsx +++ b/examples/switch.tsx @@ -4,9 +4,10 @@ import List from '../src/List'; interface Item { id: number; + ref?: React.Ref; } -const MyItem: React.FC = ({ id }, ref) => ( +const MyItem: React.FC = ({ id, ref }) => ( { const [height, setHeight] = React.useState(200); const [data, setData] = React.useState(getData(20)); const [fullHeight, setFullHeight] = React.useState(true); - const listRef = React.useRef(); + const listRef = React.useRef(null); return ( diff --git a/src/Filler.tsx b/src/Filler.tsx index 2c99bc73..f111152d 100644 --- a/src/Filler.tsx +++ b/src/Filler.tsx @@ -28,72 +28,60 @@ interface FillerProps { /** * Fill component to provided the scroll content real height. */ -const Filler = React.forwardRef( - ( - { +const Filler = React.forwardRef((props, ref) => { + const { height, offsetY, offsetX, children, prefixCls, onInnerResize, innerProps, rtl, extra } = + props; + + let outerStyle: React.CSSProperties = {}; + + let innerStyle: React.CSSProperties = { + display: 'flex', + flexDirection: 'column', + }; + + if (offsetY !== undefined) { + // Not set `width` since this will break `sticky: right` + outerStyle = { height, - offsetY, - offsetX, - children, - prefixCls, - onInnerResize, - innerProps, - rtl, - extra, - }: FillerProps, - ref: React.Ref, - ) => { - let outerStyle: React.CSSProperties = {}; - - let innerStyle: React.CSSProperties = { - display: 'flex', - flexDirection: 'column', + position: 'relative', + overflow: 'hidden', + }; + + innerStyle = { + ...innerStyle, + transform: `translateY(${offsetY}px)`, + [rtl ? 'marginRight' : 'marginLeft']: -offsetX, + position: 'absolute', + left: 0, + right: 0, + top: 0, }; + } - if (offsetY !== undefined) { - // Not set `width` since this will break `sticky: right` - outerStyle = { - height, - position: 'relative', - overflow: 'hidden', - }; - - innerStyle = { - ...innerStyle, - transform: `translateY(${offsetY}px)`, - [rtl ? 'marginRight' : 'marginLeft']: -offsetX, - position: 'absolute', - left: 0, - right: 0, - top: 0, - }; - } - - return ( -
- { - if (offsetHeight && onInnerResize) { - onInnerResize(); - } - }} + return ( +
+ { + if (offsetHeight && onInnerResize) { + onInnerResize(); + } + }} + > +
-
- {children} - {extra} -
- -
- ); - }, -); + {children} + {extra} +
+
+
+ ); +}); if (process.env.NODE_ENV !== 'production') { Filler.displayName = 'Filler'; From 64111264103f5ea5c887f95e168a6fac3f2bb2ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=97=E5=98=89=E7=94=B7?= <574980606@qq.com> Date: Tue, 28 Jan 2025 18:51:35 +0800 Subject: [PATCH 3/3] fix: fix list --- src/Item.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Item.tsx b/src/Item.tsx index 8a8cb6c5..e9412862 100644 --- a/src/Item.tsx +++ b/src/Item.tsx @@ -6,11 +6,10 @@ export interface ItemProps { } export function Item({ children, setRef }: ItemProps) { - const refFunc = React.useCallback(node => { + const refFunc = React.useCallback((node: HTMLElement) => { setRef(node); }, []); - - return React.cloneElement(children, { + return React.cloneElement(children, { ref: refFunc, }); }