diff --git a/front/apps/web/src/pages/_app.tsx b/front/apps/web/src/pages/_app.tsx
index 97d5fd94..2c097bb2 100755
--- a/front/apps/web/src/pages/_app.tsx
+++ b/front/apps/web/src/pages/_app.tsx
@@ -66,6 +66,10 @@ const GlobalCssTheme = () => {
*:hover::-webkit-scrollbar-thumb {
background-color: rgb(134, 127, 127);
}
+
+ #__next {
+ height: 100vh;
+ }
`}
diff --git a/front/packages/primitives/src/index.ts b/front/packages/primitives/src/index.ts
index bb9b44da..a40a5892 100644
--- a/front/packages/primitives/src/index.ts
+++ b/front/packages/primitives/src/index.ts
@@ -28,6 +28,7 @@ export * from "./image";
export * from "./skeleton";
export * from "./tooltip";
+export * from "./utils/breakpoints";
export * from "./utils/nojs";
import { Dimensions } from "react-native";
diff --git a/front/packages/primitives/src/utils/breakpoints.ts b/front/packages/primitives/src/utils/breakpoints.ts
new file mode 100644
index 00000000..3f2faf12
--- /dev/null
+++ b/front/packages/primitives/src/utils/breakpoints.ts
@@ -0,0 +1,62 @@
+/*
+ * Kyoo - A portable and vast media library solution.
+ * Copyright (c) Kyoo.
+ *
+ * See AUTHORS.md and LICENSE file in the project root for full license information.
+ *
+ * Kyoo is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * Kyoo is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Kyoo. If not, see .
+ */
+
+import { useWindowDimensions } from "react-native";
+import { AtLeastOne, Breakpoints as YoshikiBreakpoint } from "yoshiki/dist/type";
+import { isBreakpoints } from "yoshiki/dist/utils";
+import { breakpoints } from "yoshiki/native";
+
+export type Breakpoint = T | AtLeastOne>;
+
+// copied from yoshiki.
+const useBreakpoint = () => {
+ const { width } = useWindowDimensions();
+ const idx = Object.values(breakpoints).findIndex((x) => width <= x);
+ if (idx === -1) return 0;
+ return idx - 1;
+};
+
+const getBreakpointValue = (value: Breakpoint, breakpoint: number): T => {
+ if (!isBreakpoints(value)) return value;
+ const bpKeys = Object.keys(breakpoints) as Array>;
+ for (let i = breakpoint; i >= 0; i--) {
+ if (bpKeys[i] in value) {
+ const val = value[bpKeys[i]];
+ if (val) return val;
+ }
+ }
+ // This should never be reached.
+ return undefined!;
+};
+
+export const useBreakpointValue = (value: Breakpoint): T => {
+ const breakpoint = useBreakpoint();
+ return getBreakpointValue(value, breakpoint);
+};
+
+export const useBreakpointMap = >(
+ value: T,
+): { [key in keyof T]: T[key] extends Breakpoint ? V : T } => {
+ const breakpoint = useBreakpoint();
+ // @ts-ignore
+ return Object.fromEntries(
+ Object.entries(value).map(([key, val]) => [key, getBreakpointValue(val, breakpoint)]),
+ );
+};
diff --git a/front/packages/ui/src/browse/grid.tsx b/front/packages/ui/src/browse/grid.tsx
index 047a1382..560f599b 100644
--- a/front/packages/ui/src/browse/grid.tsx
+++ b/front/packages/ui/src/browse/grid.tsx
@@ -21,7 +21,7 @@
import { Link, Skeleton, Poster, ts, P, SubP } from "@kyoo/primitives";
import { Platform } from "react-native";
import { percent, px, Stylable, useYoshiki } from "yoshiki/native";
-import { WithLoading } from "../fetch";
+import { Layout, WithLoading } from "../fetch";
export const ItemGrid = ({
href,
@@ -85,4 +85,7 @@ export const ItemGrid = ({
);
};
-ItemGrid.height = px(250);
+ItemGrid.layout = {
+ size: px(150),
+ numColumns: { xs: 3, md: 5, xl: 7 },
+} satisfies Layout;
diff --git a/front/packages/ui/src/browse/index.tsx b/front/packages/ui/src/browse/index.tsx
index 72a7bc47..aa1027ab 100644
--- a/front/packages/ui/src/browse/index.tsx
+++ b/front/packages/ui/src/browse/index.tsx
@@ -31,6 +31,7 @@ import { DefaultLayout } from "../layout";
import { WithLoading } from "../fetch";
import { InfiniteFetch } from "../fetch-infinite";
import { ItemGrid } from "./grid";
+import { ItemList } from "./list";
import { SortBy, SortOrd, Layout } from "./types";
const itemMap = (item: WithLoading): WithLoading> => {
@@ -71,6 +72,8 @@ export const BrowsePage: QueryPage<{ slug?: string }> = ({ slug }) => {
const [sortOrd, setSortOrd] = useState(SortOrd.Asc);
const [layout, setLayout] = useState(Layout.Grid);
+ const LayoutComponent = layout === Layout.Grid ? ItemGrid : ItemList;
+
return (
<>
{/* = ({ slug }) => {
- {(item, key) => }
+ {(item, key) => }
>
);
diff --git a/front/packages/ui/src/fetch-infinite.tsx b/front/packages/ui/src/fetch-infinite.tsx
index d09adf4e..59e727d4 100644
--- a/front/packages/ui/src/fetch-infinite.tsx
+++ b/front/packages/ui/src/fetch-infinite.tsx
@@ -19,30 +19,30 @@
*/
import { Page, QueryIdentifier, useInfiniteFetch } from "@kyoo/models";
+import { useBreakpointMap } from "@kyoo/primitives";
import { FlashList } from "@shopify/flash-list";
import { ReactElement } from "react";
-import { ErrorView, WithLoading } from "./fetch";
+import { ErrorView, Layout, WithLoading } from "./fetch";
export const InfiniteFetch = ({
query,
placeholderCount = 15,
children,
- size,
- numColumns,
+ layout,
...props
}: {
query: QueryIdentifier;
placeholderCount?: number;
- numColumns: number;
+ layout: Layout;
children: (
item: Data extends Page ? WithLoading- : WithLoading,
key: string | undefined,
i: number,
) => ReactElement | null;
- size: number;
}): JSX.Element | null => {
if (!query.infinite) console.warn("A non infinite query was passed to an InfiniteFetch.");
+ const { numColumns, size } = useBreakpointMap(layout);
const { items, error, fetchNextPage, hasNextPage, refetch, isRefetching } =
useInfiniteFetch(query);
diff --git a/front/packages/ui/src/fetch-infinite.web.tsx b/front/packages/ui/src/fetch-infinite.web.tsx
index 5a0c6999..f1d2a5e3 100644
--- a/front/packages/ui/src/fetch-infinite.web.tsx
+++ b/front/packages/ui/src/fetch-infinite.web.tsx
@@ -19,19 +19,22 @@
*/
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 { ErrorView, WithLoading } from "./fetch";
+import { ErrorView, Layout, WithLoading } from "./fetch";
export const InfiniteFetch = ({
query,
placeholderCount = 15,
children,
+ layout,
...props
}: {
query: QueryIdentifier;
placeholderCount?: number;
+ layout: Layout;
children: (
item: Data extends Page ? WithLoading
- : WithLoading,
key: string | undefined,
@@ -41,23 +44,33 @@ export const InfiniteFetch = ({
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();
if (error) return ;
return (
children({ isLoading: true } as any, i.toString(), i))}
{...css(
- {
- display: "flex",
- flexWrap: "wrap",
- alignItems: "flex-start",
- justifyContent: "center",
- },
+ [
+ {
+ display: "flex",
+ alignItems: "flex-start",
+ justifyContent: "center",
+ },
+ numColumns === 1 && {
+ flexDirection: "column",
+ },
+ numColumns !== 1 && {
+ flexWrap: "wrap",
+ },
+ ],
+
props,
)}
>
diff --git a/front/packages/ui/src/fetch.tsx b/front/packages/ui/src/fetch.tsx
index 6ec4a2ca..49f4a074 100644
--- a/front/packages/ui/src/fetch.tsx
+++ b/front/packages/ui/src/fetch.tsx
@@ -19,10 +19,12 @@
*/
import { Page, QueryIdentifier, useFetch, KyooErrors } from "@kyoo/models";
-import { P } from "@kyoo/primitives";
+import { Breakpoint, P } from "@kyoo/primitives";
import { View } from "react-native";
import { useYoshiki } from "yoshiki/native";
+export type Layout = { numColumns: Breakpoint; size: Breakpoint };
+
export type WithLoading
- =
| (Item & { isLoading: false })
| (Partial
- & { isLoading: true });
diff --git a/front/packages/ui/src/layout.tsx b/front/packages/ui/src/layout.tsx
index ff8c6e04..3ff4e57a 100644
--- a/front/packages/ui/src/layout.tsx
+++ b/front/packages/ui/src/layout.tsx
@@ -28,7 +28,9 @@ export const DefaultLayout = (page: ReactElement) => {
return (
<>
- {page}
+
+ {page}
+
>
);
};