mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-16 11:47:54 +01:00
web: replace react-virtual with react-virtuouso
This commit is contained in:
19
apps/web/package-lock.json
generated
19
apps/web/package-lock.json
generated
@@ -33,7 +33,7 @@
|
||||
"@react-pdf-viewer/core": "^3.12.0",
|
||||
"@react-pdf-viewer/toolbar": "^3.12.0",
|
||||
"@tanstack/react-query": "^4.29.19",
|
||||
"@tanstack/react-virtual": "^3.0.0-beta.68",
|
||||
"@tanstack/react-virtual": "^3.0.1",
|
||||
"@theme-ui/color": "^0.16.1",
|
||||
"@theme-ui/components": "^0.16.1",
|
||||
"@theme-ui/core": "^0.16.1",
|
||||
@@ -72,6 +72,7 @@
|
||||
"react-modal": "3.16.1",
|
||||
"react-qrcode-logo": "^2.9.0",
|
||||
"react-scroll-sync": "^0.11.2",
|
||||
"react-virtuoso": "^4.6.2",
|
||||
"timeago.js": "4.0.2",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"w3c-keyname": "^2.2.6",
|
||||
@@ -37715,6 +37716,8 @@
|
||||
},
|
||||
"node_modules/@tanstack/react-virtual": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.0.1.tgz",
|
||||
"integrity": "sha512-IFOFuRUTaiM/yibty9qQ9BfycQnYXIDHGP2+cU+0LrFFGNhVxCXSQnaY6wkX8uJVteFEBjUondX0Hmpp7TNcag==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/virtual-core": "3.1.3"
|
||||
@@ -37730,6 +37733,8 @@
|
||||
},
|
||||
"node_modules/@tanstack/virtual-core": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.0.0.tgz",
|
||||
"integrity": "sha512-SYXOBTjJb05rXa2vl55TTwO40A6wKu0R5i1qQwhJYNDIqaIGF7D0HsLw+pJAyi2OvntlEIVusx3xtbbgSUi6zg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -43449,6 +43454,18 @@
|
||||
"react-dom": "16.x || 17.x || 18.x"
|
||||
}
|
||||
},
|
||||
"node_modules/react-virtuoso": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.6.2.tgz",
|
||||
"integrity": "sha512-vvlqvzPif+MvBrJ09+hJJrVY0xJK9yran+A+/1iwY78k0YCVKsyoNPqoLxOxzYPggspNBNXqUXEcvckN29OxyQ==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16 || >=17 || >= 18",
|
||||
"react-dom": ">=16 || >=17 || >= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"@react-pdf-viewer/core": "^3.12.0",
|
||||
"@react-pdf-viewer/toolbar": "^3.12.0",
|
||||
"@tanstack/react-query": "^4.29.19",
|
||||
"@tanstack/react-virtual": "^3.0.0-beta.68",
|
||||
"@tanstack/react-virtual": "^3.0.1",
|
||||
"@theme-ui/color": "^0.16.1",
|
||||
"@theme-ui/components": "^0.16.1",
|
||||
"@theme-ui/core": "^0.16.1",
|
||||
@@ -71,6 +71,7 @@
|
||||
"react-modal": "3.16.1",
|
||||
"react-qrcode-logo": "^2.9.0",
|
||||
"react-scroll-sync": "^0.11.2",
|
||||
"react-virtuoso": "^4.6.2",
|
||||
"timeago.js": "4.0.2",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"w3c-keyname": "^2.2.6",
|
||||
|
||||
@@ -17,18 +17,18 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Flex, Button } from "@theme-ui/components";
|
||||
import { forwardRef, useEffect, useRef, useState } from "react";
|
||||
import { Flex, Button, Box } from "@theme-ui/components";
|
||||
import { Plus } from "../icons";
|
||||
import {
|
||||
useStore as useSelectionStore,
|
||||
store as selectionStore
|
||||
} from "../../stores/selection-store";
|
||||
import GroupHeader from "../group-header";
|
||||
import { ListItemWrapper } from "./list-profiles";
|
||||
import { DEFAULT_ITEM_HEIGHT, ListItemWrapper } from "./list-profiles";
|
||||
import Announcements from "../announcements";
|
||||
import { ListLoader } from "../loaders/list-loader";
|
||||
import { FlexScrollContainer } from "../scroll-container";
|
||||
import ScrollContainer from "../scroll-container";
|
||||
import { useKeyboardListNavigation } from "../../hooks/use-keyboard-list-navigation";
|
||||
import { Context } from "./types";
|
||||
import {
|
||||
@@ -37,9 +37,29 @@ import {
|
||||
Item,
|
||||
isGroupHeader
|
||||
} from "@notesnook/core";
|
||||
import { VirtualizedList } from "../virtualized-list";
|
||||
import { ScrollToOptions, Virtualizer } from "@tanstack/react-virtual";
|
||||
import { useResolvedItem } from "./resolved-item";
|
||||
import {
|
||||
ItemProps,
|
||||
ScrollerProps,
|
||||
Virtuoso,
|
||||
VirtuosoHandle
|
||||
} from "react-virtuoso";
|
||||
import Skeleton from "react-loading-skeleton";
|
||||
|
||||
export const CustomScrollbarsVirtualList = forwardRef<
|
||||
HTMLDivElement,
|
||||
ScrollerProps
|
||||
>(function CustomScrollbarsVirtualList(props, ref) {
|
||||
return (
|
||||
<ScrollContainer
|
||||
{...props}
|
||||
forwardedRef={(sRef) => {
|
||||
if (typeof ref === "function") ref(sRef);
|
||||
else if (ref) ref.current = sRef;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type ListContainerProps = {
|
||||
group?: GroupingKey;
|
||||
@@ -68,7 +88,8 @@ function ListContainer(props: ListContainerProps) {
|
||||
(store) => store.toggleSelectionMode
|
||||
);
|
||||
|
||||
const listRef = useRef<Virtualizer<Element, Element>>();
|
||||
const listRef = useRef<VirtuosoHandle>(null);
|
||||
const listContainerRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
@@ -119,25 +140,24 @@ function ListContainer(props: ListContainerProps) {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<FlexScrollContainer
|
||||
style={{ display: "flex", flexDirection: "column", flex: 1 }}
|
||||
<Flex
|
||||
ref={listContainerRef}
|
||||
variant="columnFill"
|
||||
data-test-id={`${group}-list`}
|
||||
>
|
||||
{header ? header : <Announcements />}
|
||||
<VirtualizedList
|
||||
virtualizerRef={listRef}
|
||||
estimatedSize={50}
|
||||
getItemKey={(index) => items.getKey(index)}
|
||||
items={items.ids}
|
||||
mode="dynamic"
|
||||
tabIndex={-1}
|
||||
overscan={10}
|
||||
<Virtuoso
|
||||
ref={listRef}
|
||||
computeItemKey={(index) => items.getKey(index)}
|
||||
defaultItemHeight={DEFAULT_ITEM_HEIGHT}
|
||||
totalCount={items.ids.length}
|
||||
onBlur={() => setFocusedGroupIndex(-1)}
|
||||
onKeyDown={(e) => onKeyDown(e.nativeEvent)}
|
||||
itemWrapperProps={(_, index) => ({
|
||||
onFocus: () => onFocus(index),
|
||||
onMouseDown: (e) => onMouseDown(e.nativeEvent, index)
|
||||
})}
|
||||
components={{
|
||||
Scroller: CustomScrollbarsVirtualList,
|
||||
Item: VirtuosoItem,
|
||||
Header: () => (header ? header : <Announcements />)
|
||||
}}
|
||||
increaseViewportBy={{ top: 10, bottom: 10 }}
|
||||
context={{
|
||||
items,
|
||||
group,
|
||||
@@ -147,11 +167,15 @@ function ListContainer(props: ListContainerProps) {
|
||||
scrollToIndex: listRef.current?.scrollToIndex,
|
||||
focusGroup: setFocusedGroupIndex,
|
||||
context,
|
||||
compact
|
||||
compact,
|
||||
onMouseDown,
|
||||
onFocus
|
||||
}}
|
||||
renderItem={ItemRenderer}
|
||||
itemContent={(index, _data, context) => (
|
||||
<ItemRenderer context={context} index={index} />
|
||||
)}
|
||||
/>
|
||||
</FlexScrollContainer>
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
{button && (
|
||||
@@ -194,6 +218,9 @@ type ListContext = {
|
||||
focusGroup: (index: number) => void;
|
||||
context?: Context;
|
||||
compact?: boolean;
|
||||
|
||||
onMouseDown: (e: MouseEvent, itemIndex: number) => void;
|
||||
onFocus: (itemIndex: number) => void;
|
||||
};
|
||||
function ItemRenderer({
|
||||
index,
|
||||
@@ -214,7 +241,37 @@ function ItemRenderer({
|
||||
compact
|
||||
} = context;
|
||||
const resolvedItem = useResolvedItem({ index, items });
|
||||
if (!resolvedItem) return <div style={{ height: 50, width: "100%" }}></div>;
|
||||
if (!resolvedItem || !resolvedItem.item)
|
||||
return (
|
||||
<Box key="list-item-skeleton" sx={{ py: 2, px: 1 }}>
|
||||
<Skeleton
|
||||
enableAnimation={false}
|
||||
height={16}
|
||||
width={`50%`}
|
||||
style={{ marginBottom: 5 }}
|
||||
/>
|
||||
<Skeleton height={12} count={2} />
|
||||
<Flex>
|
||||
<Skeleton enableAnimation={false} height={10} inline width={50} />
|
||||
<Skeleton
|
||||
enableAnimation={false}
|
||||
height={10}
|
||||
inline
|
||||
width={10}
|
||||
circle
|
||||
style={{ marginLeft: 5 }}
|
||||
/>
|
||||
<Skeleton
|
||||
enableAnimation={false}
|
||||
height={10}
|
||||
inline
|
||||
width={10}
|
||||
circle
|
||||
style={{ marginLeft: 5 }}
|
||||
/>
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -241,7 +298,7 @@ function ItemRenderer({
|
||||
groups={async () => (items.groups ? items.groups() : [])}
|
||||
onJump={(index) => {
|
||||
scrollToIndex?.(index, {
|
||||
align: "center",
|
||||
// align: "center",
|
||||
behavior: "auto"
|
||||
});
|
||||
focusGroup(index);
|
||||
@@ -260,6 +317,26 @@ function ItemRenderer({
|
||||
);
|
||||
}
|
||||
|
||||
function VirtuosoItem({
|
||||
item: _item,
|
||||
context,
|
||||
...props
|
||||
}: ItemProps<string> & {
|
||||
context?: ListContext;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
{...props}
|
||||
onFocus={() => context?.onFocus(props["data-item-index"])}
|
||||
onMouseDown={(e) =>
|
||||
context?.onMouseDown(e.nativeEvent, props["data-item-index"])
|
||||
}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll the element at the specified index into view and
|
||||
* wait until it renders into the DOM. This function keeps
|
||||
@@ -268,15 +345,16 @@ function ItemRenderer({
|
||||
* 50ms interval.
|
||||
*/
|
||||
function waitForElement(
|
||||
list: Virtualizer<Element, Element>,
|
||||
list: VirtuosoHandle,
|
||||
index: number,
|
||||
elementId: string,
|
||||
callback: (element: HTMLElement) => void
|
||||
) {
|
||||
let waitInterval = 0;
|
||||
let maxAttempts = 3;
|
||||
list.scrollToIndex(index);
|
||||
function scrollDone() {
|
||||
list.scrollIntoView({
|
||||
index,
|
||||
done: function scrollDone() {
|
||||
if (!maxAttempts) return;
|
||||
clearTimeout(waitInterval);
|
||||
|
||||
@@ -291,5 +369,5 @@ function waitForElement(
|
||||
|
||||
callback(element);
|
||||
}
|
||||
scrollDone();
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user