Use css grid for infinite lists

This commit is contained in:
Zoe Roux 2023-10-17 23:09:15 +02:00
parent 0e0bb17ad9
commit eb351f9bed
10 changed files with 66 additions and 66 deletions

View File

@ -51,7 +51,7 @@
"react-native-svg": "13.9.0",
"react-native-uuid": "^2.0.1",
"react-native-video": "^6.0.0-alpha.7",
"yoshiki": "1.2.7"
"yoshiki": "1.2.9"
},
"devDependencies": {
"@babel/core": "^7.22.10",

View File

@ -38,7 +38,7 @@
"srt-webvtt": "^2.0.0",
"superjson": "^1.13.1",
"sweetalert2": "^11.7.20",
"yoshiki": "1.2.7",
"yoshiki": "1.2.9",
"zod": "^3.21.4"
},
"devDependencies": {

View File

@ -24,7 +24,7 @@ import { useYoshiki, px, Stylable } from "yoshiki/native";
import { Icon } from "./icons";
import { P } from "./text";
import AccountCircle from "@material-symbols/svg-400/rounded/account_circle-fill.svg";
import { YoshikiStyle } from "yoshiki/src/type";
import { YoshikiStyle } from "yoshiki";
import { ComponentType, forwardRef, RefAttributes } from "react";
const stringToColor = (string: string) => {

View File

@ -19,9 +19,7 @@
*/
import { useWindowDimensions } from "react-native";
import { Breakpoints as YoshikiBreakpoint } from "yoshiki/src/type";
import { isBreakpoints } from "yoshiki/src/utils";
import { breakpoints } from "yoshiki/native";
import { Breakpoints as YoshikiBreakpoint, isBreakpoints, breakpoints } from "yoshiki/native";
type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U];
export type Breakpoint<T> = T | AtLeastOne<YoshikiBreakpoint<T>>;

View File

@ -21,7 +21,7 @@
import { KyooImage } from "@kyoo/models";
import { Link, Skeleton, Poster, ts, focusReset, P, SubP } from "@kyoo/primitives";
import { ImageStyle, Platform } from "react-native";
import { percent, px, Stylable, useYoshiki } from "yoshiki/native";
import { percent, px, Stylable, Theme, useYoshiki } from "yoshiki/native";
import { Layout, WithLoading } from "../fetch";
export const ItemGrid = ({
@ -44,35 +44,27 @@ export const ItemGrid = ({
<Link
href={href ?? ""}
{...css(
[
{
flexDirection: "column",
alignItems: "center",
m: { xs: ts(1), sm: ts(4) },
child: {
poster: {
borderColor: (theme) => theme.background,
borderWidth: px(4),
borderStyle: "solid",
},
},
fover: {
self: focusReset,
poster: {
borderColor: (theme) => theme.accent,
},
title: {
textDecorationLine: "underline",
},
{
flexDirection: "column",
alignItems: "center",
width: percent(100),
child: {
poster: {
borderColor: (theme) => theme.background,
borderWidth: px(4),
borderStyle: "solid",
},
},
// We leave no width on native to fill the list's grid.
Platform.OS === "web" && {
width: { xs: percent(18), sm: percent(25) },
minWidth: { xs: px(90), sm: px(120) },
maxWidth: px(168),
fover: {
self: focusReset,
poster: {
borderColor: (theme: Theme) => theme.accent,
},
title: {
textDecorationLine: "underline",
},
},
],
},
props,
)}
>
@ -111,5 +103,7 @@ export const ItemGrid = ({
ItemGrid.layout = {
size: px(150),
numColumns: { xs: 3, sm: 5, xl: 7 },
numColumns: { xs: 3, sm: 4, md: 5, lg: 6, xl: 8 },
gap: { xs: ts(1), sm: ts(2), md: ts(4) },
layout: "grid",
} satisfies Layout;

View File

@ -65,7 +65,6 @@ export const ItemList = ({
flexDirection: "row",
height: ItemList.layout.size,
borderRadius: px(6),
m: ts(1),
})}
>
<View
@ -115,4 +114,4 @@ export const ItemList = ({
);
};
ItemList.layout = { numColumns: 1, size: 300 } satisfies Layout;
ItemList.layout = { numColumns: 1, size: 300, layout: "vertical", gap: ts(2) } satisfies Layout;

View File

@ -28,7 +28,6 @@ export const InfiniteFetch = <Data, Props, _>({
query,
placeholderCount = 15,
incremental = false,
horizontal = false,
children,
layout,
empty,
@ -83,7 +82,7 @@ export const InfiniteFetch = <Data, Props, _>({
<FlashList
renderItem={({ item, index }) => children({ isLoading: false, ...item } as any, index)}
data={hasNextPage !== false ? [...(items || []), ...placeholders] : items}
horizontal={horizontal}
horizontal={layout.layout === "horizontal"}
keyExtractor={(item: any) => item.id?.toString()}
numColumns={numColumns}
estimatedItemSize={size}

View File

@ -29,13 +29,13 @@ import {
useEffect,
useRef,
} from "react";
import { Stylable, useYoshiki } from "yoshiki";
import { Stylable, useYoshiki, ysMap } from "yoshiki";
import { EmptyView, ErrorView, Layout, WithLoading } from "./fetch";
const InfiniteScroll = <Props,>({
children,
loader,
layout = "vertical",
layout,
loadMore,
hasMore = true,
isFetching,
@ -45,7 +45,7 @@ const InfiniteScroll = <Props,>({
}: {
children?: ReactElement | (ReactElement | null)[] | null;
loader?: (ReactElement | null)[];
layout?: "vertical" | "horizontal" | "grid";
layout: Layout;
loadMore: () => void;
hasMore: boolean;
isFetching: boolean;
@ -58,10 +58,11 @@ const InfiniteScroll = <Props,>({
const onScroll = useCallback(() => {
if (!ref.current || !hasMore || isFetching) return;
const scroll =
layout === "horizontal"
layout.layout === "horizontal"
? ref.current.scrollWidth - ref.current.scrollLeft
: ref.current.scrollHeight - ref.current.scrollTop;
const offset = layout === "horizontal" ? ref.current.offsetWidth : ref.current.offsetHeight;
const offset =
layout.layout === "horizontal" ? ref.current.offsetWidth : ref.current.offsetHeight;
if (scroll <= offset * 1.2) loadMore();
}, [hasMore, isFetching, layout, loadMore]);
@ -78,22 +79,30 @@ const InfiniteScroll = <Props,>({
{...css(
[
{
display: "flex",
alignItems: "flex-start",
overflowX: "hidden",
display: "grid",
gridAutoRows: "max-content",
// the as any is due to differencies between css types of native and web (already accounted for in yoshiki)
gridGap: layout.gap as any,
padding: layout.gap as any,
},
layout.layout == "vertical" && {
gridTemplateColumns: "1fr",
alignItems: "stretch",
overflowY: "auto",
},
layout == "vertical" && {
flexDirection: "column",
layout.layout == "horizontal" && {
alignItems: "stretch",
overflowX: "auto",
overflowY: "hidden",
gridAutoFlow: "column",
gridAutoColumns: ysMap(layout.numColumns, (x) => `${100 / x}%`),
gridTemplateRows: "max-content",
},
layout == "horizontal" && {
flexDirection: "row",
alignItems: "stretch",
},
layout === "grid" && {
flexWrap: "wrap",
layout.layout === "grid" && {
gridTemplateColumns: ysMap(layout.numColumns, (x) => `repeat(${x}, 1fr)`),
justifyContent: "center",
alignItems: "flex-start",
overflowY: "auto",
},
],
@ -127,7 +136,6 @@ export const InfiniteFetch = <Data, _, HeaderProps>({
placeholderCount = 15,
children,
layout,
horizontal = false,
empty,
divider: Divider = false,
Header,
@ -139,7 +147,6 @@ export const InfiniteFetch = <Data, _, HeaderProps>({
incremental?: boolean;
placeholderCount?: number;
layout: Layout;
horizontal?: boolean;
children: (
item: Data extends Page<infer Item> ? WithLoading<Item> : WithLoading<Data>,
i: number,
@ -156,8 +163,6 @@ export const InfiniteFetch = <Data, _, HeaderProps>({
const { items, error, fetchNextPage, hasNextPage, isFetching } = useInfiniteFetch(query, {
useErrorBoundary: false,
});
const grid = layout.numColumns !== 1;
if (incremental && items) oldItems.current = items;
if (error) return addHeader(Header, <ErrorView error={error} />, headerProps);
@ -168,7 +173,7 @@ export const InfiniteFetch = <Data, _, HeaderProps>({
return (
<InfiniteScroll
layout={grid ? "grid" : horizontal ? "horizontal" : "vertical"}
layout={layout}
loadMore={fetchNextPage}
hasMore={hasNextPage!}
isFetching={isFetching}

View File

@ -23,7 +23,12 @@ import { Breakpoint, P } from "@kyoo/primitives";
import { View } from "react-native";
import { useYoshiki } from "yoshiki/native";
export type Layout = { numColumns: Breakpoint<number>; size: Breakpoint<number> };
export type Layout = {
numColumns: Breakpoint<number>;
size: Breakpoint<number>;
gap: Breakpoint<number | string>;
layout: "grid" | "horizontal" | "vertical";
};
export type WithLoading<Item> =
| (Item & { isLoading: false })

View File

@ -10573,7 +10573,7 @@ __metadata:
react-native-uuid: ^2.0.1
react-native-video: ^6.0.0-alpha.7
typescript: ^5.1.6
yoshiki: 1.2.7
yoshiki: 1.2.9
languageName: unknown
linkType: soft
@ -14285,7 +14285,7 @@ __metadata:
sweetalert2: ^11.7.20
typescript: ^5.1.6
webpack: ^5.88.2
yoshiki: 1.2.7
yoshiki: 1.2.9
zod: ^3.21.4
languageName: unknown
linkType: soft
@ -14667,9 +14667,9 @@ __metadata:
languageName: node
linkType: hard
"yoshiki@npm:1.2.7":
version: 1.2.7
resolution: "yoshiki@npm:1.2.7"
"yoshiki@npm:1.2.9":
version: 1.2.9
resolution: "yoshiki@npm:1.2.9"
dependencies:
"@types/inline-style-prefixer": ^5.0.0
"@types/node": 18.x.x
@ -14684,7 +14684,7 @@ __metadata:
optional: true
react-native-web:
optional: true
checksum: 2ebe15f481ad347a90fc8d6bc021d17ef03a8f982b31fa00b8700fa65d27e987044faa3d83d69159becb383ad9aa9ea5523eff3888965e25bc457affe147d361
checksum: 1cd681cf9ba241f051c8e09a97b3a67ae5610fbba460ac506127c8d78bbcc069c07c727fb84a15a723a887ad58582bbcf57baba7020642ac6234d38d0c21116f
languageName: node
linkType: hard