mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Use css grid for infinite lists
This commit is contained in:
parent
0e0bb17ad9
commit
eb351f9bed
@ -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",
|
||||
|
@ -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": {
|
||||
|
@ -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) => {
|
||||
|
@ -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>>;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -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 })
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user