Rewrite infinite lists to support horizontal

This commit is contained in:
Zoe Roux 2022-12-14 01:02:47 +09:00
parent 26f9cf646b
commit 894cbb3c9d
7 changed files with 108 additions and 63 deletions

View File

@ -31,12 +31,10 @@
"next": "13.0.5",
"next-fonts": "^1.5.1",
"next-translate": "^1.6.0",
"next-transpile-modules": "^10.0.0",
"raf": "^3.4.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-i18next": "^12.0.0",
"react-infinite-scroll-component": "^6.1.0",
"react-native-reanimated": "^2.13.0",
"react-native-web": "^0.18.10",
"solito": "^2.0.5",

View File

@ -78,7 +78,7 @@ export const Link = ({
},
default: {},
});
const Wrapper = radiusStyle ? View : Fragment;
const Wrapper = radiusStyle.style ? View : Fragment;
return (
<Wrapper {...radiusStyle}>

View File

@ -9,6 +9,7 @@
"react-native-svg": "^13.6.0"
},
"devDependencies": {
"@shopify/flash-list": "^1.4.0",
"@types/react": "^18.0.25",
"typescript": "^4.9.3"
},

View File

@ -27,6 +27,7 @@ import { ErrorView, Layout, WithLoading } from "./fetch";
export const InfiniteFetch = <Data,>({
query,
placeholderCount = 15,
horizontal = false,
children,
layout,
...props
@ -34,6 +35,7 @@ export const InfiniteFetch = <Data,>({
query: QueryIdentifier<Data>;
placeholderCount?: number;
layout: Layout;
horizontal?: boolean;
children: (
item: Data extends Page<infer Item> ? WithLoading<Item> : WithLoading<Data>,
key: string | undefined,
@ -65,6 +67,7 @@ export const InfiniteFetch = <Data,>({
]
: items
}
horizontal={horizontal}
keyExtractor={(item: any) => item.id?.toString()}
numColumns={numColumns}
estimatedItemSize={size}

View File

@ -19,22 +19,84 @@
*/
import { Page, QueryIdentifier, useInfiniteFetch } from "@kyoo/models";
import { useBreakpointValue } from "@kyoo/primitives";
import { ReactElement } from "react";
import InfiniteScroll from "react-infinite-scroll-component";
import { useYoshiki } from "yoshiki";
import { ReactElement, useRef } from "react";
import { Stylable, useYoshiki } from "yoshiki";
import { ErrorView, Layout, WithLoading } from "./fetch";
const InfiniteScroll = ({
children,
loader,
layout = "vertical",
loadMore,
hasMore,
isFetching,
...props
}: {
children?: ReactElement | (ReactElement | null)[] | null;
loader?: (ReactElement | null)[];
layout?: "vertical" | "horizontal" | "grid";
loadMore: () => void;
hasMore: boolean;
isFetching: boolean;
} & Stylable) => {
const ref = useRef<HTMLDivElement>(null);
const { css } = useYoshiki();
return (
<div
ref={ref}
onScroll={() => {
if (!ref.current || !hasMore || isFetching) return;
const scroll =
layout === "horizontal"
? ref.current.scrollWidth - ref.current.scrollLeft
: ref.current.scrollHeight - ref.current.scrollTop;
const offset = layout === "horizontal" ? ref.current.offsetWidth : ref.current.offsetHeight;
if (scroll <= offset * 1.2) loadMore();
}}
{...css(
[
{
display: "flex",
alignItems: "flex-start",
overflow: "overlay",
},
layout == "vertical" && {
flexDirection: "column",
alignItems: "stretch",
},
layout == "horizontal" && {
flexDirection: "row",
alignItems: "stretch",
},
layout === "grid" && {
flexWrap: "wrap",
justifyContent: "center",
},
],
props,
)}
>
{children}
{hasMore && isFetching && loader}
</div>
);
};
export const InfiniteFetch = <Data,>({
query,
placeholderCount = 15,
children,
layout,
horizontal = false,
...props
}: {
query: QueryIdentifier<Data>;
placeholderCount?: number;
layout: Layout;
horizontal?: boolean;
children: (
item: Data extends Page<infer Item> ? WithLoading<Item> : WithLoading<Data>,
key: string | undefined,
@ -43,38 +105,19 @@ export const InfiniteFetch = <Data,>({
}): JSX.Element | null => {
if (!query.infinite) console.warn("A non infinite query was passed to an InfiniteFetch.");
const { items, error, fetchNextPage, hasNextPage } = useInfiniteFetch(query);
const { numColumns } = useBreakpointValue(layout);
const { css } = useYoshiki();
const { items, error, fetchNextPage, hasNextPage, isFetching } = useInfiniteFetch(query);
const grid = layout.numColumns !== 1;
if (error) return <ErrorView error={error} />;
return (
<InfiniteScroll
scrollableTarget="main" // Default to the main element for the scroll.
dataLength={items?.length ?? 0}
next={fetchNextPage}
layout={grid ? "grid" : horizontal ? "horizontal" : "vertical"}
loadMore={fetchNextPage}
hasMore={hasNextPage!}
isFetching={isFetching}
loader={[...Array(12)].map((_, i) => children({ isLoading: true } as any, i.toString(), i))}
{...css(
[
{
display: "flex",
alignItems: "flex-start",
justifyContent: "center",
overflow: "unset !important",
},
numColumns === 1 && {
flexDirection: "column",
alignItems: "stretch",
},
numColumns !== 1 && {
flexWrap: "wrap",
},
],
props,
)}
{...props}
>
{items?.map((item, i) =>
children({ ...item, isLoading: false } as any, (item as any).id?.toString(), i),

View File

@ -48,12 +48,12 @@ export const Fetch = <Data,>({
const { data, error } = useFetch(query);
if (error) return <ErrorView error={error} />;
if (placeholderCount === 1 || !isPage<object>(data))
return children(data ? { ...data, isLoading: false } : ({ isLoading: true } as any), 0);
if (!data)
return (
<>{[...Array(placeholderCount)].map((_, i) => children({ isLoading: true } as any, i))}</>
);
if (!isPage<object>(data))
return children(data ? { ...data, isLoading: false } : ({ isLoading: true } as any), 0);
return <>{data.items.map((item, i) => children({ ...item, isLoading: false } as any, i))}</>;
};

View File

@ -2322,6 +2322,7 @@ __metadata:
dependencies:
"@kyoo/models": "workspace:^"
"@kyoo/primitives": "workspace:^"
"@shopify/flash-list": ^1.4.0
"@types/react": ^18.0.25
react-native-svg: ^13.6.0
typescript: ^4.9.3
@ -3156,6 +3157,20 @@ __metadata:
languageName: node
linkType: hard
"@shopify/flash-list@npm:^1.4.0":
version: 1.4.0
resolution: "@shopify/flash-list@npm:1.4.0"
dependencies:
recyclerlistview: 4.2.0
tslib: 2.4.0
peerDependencies:
"@babel/runtime": "*"
react: "*"
react-native: "*"
checksum: c6510b0d6ae6404fe92ede0c918ba184bc2b27ed39c627eebad16a6542792cb34e750e2004e1a9ce165f9d729f1af0555cba1e4c224fd52bfd2a600fdc9e2a65
languageName: node
linkType: hard
"@sideway/address@npm:^4.1.3":
version: 4.1.4
resolution: "@sideway/address@npm:4.1.4"
@ -9834,15 +9849,6 @@ __metadata:
languageName: node
linkType: hard
"next-transpile-modules@npm:^10.0.0":
version: 10.0.0
resolution: "next-transpile-modules@npm:10.0.0"
dependencies:
enhanced-resolve: ^5.10.0
checksum: 3300fc7081f63b2c9487588db7cbe718f209dfd2111adec22d9c8af0e3c8ade2d95fd45f91e045546d78d98cafc78a49431de9a623360d33831b5e694bf007c9
languageName: node
linkType: hard
"next@npm:13.0.5":
version: 13.0.5
resolution: "next@npm:13.0.5"
@ -10923,17 +10929,6 @@ __metadata:
languageName: node
linkType: hard
"react-infinite-scroll-component@npm:^6.1.0":
version: 6.1.0
resolution: "react-infinite-scroll-component@npm:6.1.0"
dependencies:
throttle-debounce: ^2.1.0
peerDependencies:
react: ">=16.0.0"
checksum: 3708398934366df907dbad215247ebc1033221957ce7e32289ea31750cce70aa16513e2d03743e06c8b868ac7c542d12d5dbb6c830fd408433a4762f3cb5ecfb
languageName: node
linkType: hard
"react-is@npm:^16.12.0 || ^17.0.0 || ^18.0.0, react-is@npm:^18.2.0":
version: 18.2.0
resolution: "react-is@npm:18.2.0"
@ -11234,6 +11229,20 @@ __metadata:
languageName: node
linkType: hard
"recyclerlistview@npm:4.2.0":
version: 4.2.0
resolution: "recyclerlistview@npm:4.2.0"
dependencies:
lodash.debounce: 4.0.8
prop-types: 15.8.1
ts-object-utils: 0.0.5
peerDependencies:
react: ">= 15.2.1"
react-native: ">= 0.30.0"
checksum: 6cba6a99fb487067c509112b94e3d4d3905d782bbcb7af2cffbd57c601a4650d670e4eee5fec18d195d58ff6ec01a47288c5510379a2f37da3c5fc0a58860441
languageName: node
linkType: hard
"regenerate-unicode-properties@npm:^10.1.0":
version: 10.1.0
resolution: "regenerate-unicode-properties@npm:10.1.0"
@ -12624,13 +12633,6 @@ __metadata:
languageName: node
linkType: hard
"throttle-debounce@npm:^2.1.0":
version: 2.3.0
resolution: "throttle-debounce@npm:2.3.0"
checksum: 6d90aa2ddb294f8dad13d854a1cfcd88fdb757469669a096a7da10f515ee466857ac1e750649cb9da931165c6f36feb448318e7cb92570f0a3679d20e860a925
languageName: node
linkType: hard
"through2@npm:^2.0.1":
version: 2.0.5
resolution: "through2@npm:2.0.5"
@ -13307,12 +13309,10 @@ __metadata:
next: 13.0.5
next-fonts: ^1.5.1
next-translate: ^1.6.0
next-transpile-modules: ^10.0.0
raf: ^3.4.1
react: 18.2.0
react-dom: 18.2.0
react-i18next: ^12.0.0
react-infinite-scroll-component: ^6.1.0
react-native-reanimated: ^2.13.0
react-native-web: ^0.18.10
solito: ^2.0.5