mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-31 20:24:27 -04:00
Add focus handling for the grid
This commit is contained in:
parent
35a3c4c4bf
commit
a8a8b45f4a
@ -46,7 +46,7 @@
|
||||
"react-native-screens": "~3.18.0",
|
||||
"react-native-svg": "13.4.0",
|
||||
"react-native-video": "alpha",
|
||||
"yoshiki": "0.4.5"
|
||||
"yoshiki": "1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.19.3",
|
||||
|
@ -36,7 +36,7 @@
|
||||
"react-native-web": "^0.18.10",
|
||||
"solito": "^2.0.5",
|
||||
"superjson": "^1.11.0",
|
||||
"yoshiki": "0.4.5",
|
||||
"yoshiki": "1.2.0",
|
||||
"zod": "^3.19.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -19,7 +19,7 @@
|
||||
*/
|
||||
|
||||
import { forwardRef, ReactNode } from "react";
|
||||
import { Pressable, TextProps, View, PressableProps } from "react-native";
|
||||
import { Pressable, TextProps, View, PressableProps, Platform } from "react-native";
|
||||
import { LinkCore, TextLink } from "solito/link";
|
||||
import { useTheme, useYoshiki } from "yoshiki/native";
|
||||
import { alpha } from "./themes";
|
||||
@ -59,7 +59,10 @@ export const PressableFeedback = forwardRef<View, PressableProps>(function _Feed
|
||||
return (
|
||||
<Pressable
|
||||
ref={ref}
|
||||
android_ripple={{ foreground: true, color: alpha(theme.contrast, 0.5) as any }}
|
||||
// TODO: Enable ripple on tv. Waiting for https://github.com/react-native-tvos/react-native-tvos/issues/440
|
||||
{...(Platform.isTV
|
||||
? {}
|
||||
: { android_ripple: { foreground: true, color: alpha(theme.contrast, 0.5) as any } })}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
|
@ -19,10 +19,11 @@
|
||||
*/
|
||||
|
||||
import { useWindowDimensions } from "react-native";
|
||||
import { AtLeastOne, Breakpoints as YoshikiBreakpoint } from "yoshiki/dist/type";
|
||||
import { Breakpoints as YoshikiBreakpoint } from "yoshiki/dist/type";
|
||||
import { isBreakpoints } from "yoshiki/dist/utils";
|
||||
import { 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>>;
|
||||
|
||||
// copied from yoshiki.
|
||||
|
@ -18,8 +18,16 @@
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Platform } from "react-native";
|
||||
import { px } from "yoshiki/native";
|
||||
|
||||
export const ts = (spacing: number) => {
|
||||
return px(spacing * 8);
|
||||
};
|
||||
|
||||
export const focusReset: object =
|
||||
Platform.OS === "web"
|
||||
? {
|
||||
boxShadow: "unset",
|
||||
}
|
||||
: {};
|
||||
|
@ -8,7 +8,7 @@
|
||||
"@kyoo/primitives": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@shopify/flash-list": "^1.4.0",
|
||||
"@shopify/flash-list": "1.3.1",
|
||||
"@types/react": "^18.0.25",
|
||||
"typescript": "^4.9.3"
|
||||
},
|
||||
|
@ -18,7 +18,7 @@
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Link, Skeleton, Poster, ts, P, SubP } from "@kyoo/primitives";
|
||||
import { Link, Skeleton, Poster, ts, focusReset, P, SubP } from "@kyoo/primitives";
|
||||
import { Platform } from "react-native";
|
||||
import { percent, px, Stylable, useYoshiki } from "yoshiki/native";
|
||||
import { Layout, WithLoading } from "../fetch";
|
||||
@ -29,25 +29,49 @@ export const ItemGrid = ({
|
||||
subtitle,
|
||||
poster,
|
||||
isLoading,
|
||||
hasTVPreferredFocus,
|
||||
...props
|
||||
}: WithLoading<{
|
||||
href: string;
|
||||
name: string;
|
||||
subtitle?: string;
|
||||
poster?: string | null;
|
||||
hasTVPreferredFocus?: boolean;
|
||||
}> &
|
||||
Stylable<"text">) => {
|
||||
const { css } = useYoshiki();
|
||||
const { css } = useYoshiki("grid");
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={href ?? ""}
|
||||
focusable={hasTVPreferredFocus || !isLoading}
|
||||
accessible={hasTVPreferredFocus || !isLoading}
|
||||
{...(Platform.isTV
|
||||
? {
|
||||
hasTVPreferredFocus: hasTVPreferredFocus,
|
||||
}
|
||||
: {})}
|
||||
{...css(
|
||||
[
|
||||
{
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
m: { xs: ts(1), sm: ts(2) },
|
||||
m: { xs: ts(1), sm: ts(4) },
|
||||
child: {
|
||||
poster: {
|
||||
borderColor: "transparent",
|
||||
borderWidth: px(4),
|
||||
},
|
||||
},
|
||||
fover: {
|
||||
self: focusReset,
|
||||
poster: {
|
||||
borderColor: (theme) => theme.appbar,
|
||||
},
|
||||
title: {
|
||||
textDecorationLine: "underline",
|
||||
},
|
||||
},
|
||||
},
|
||||
// We leave no width on native to fill the list's grid.
|
||||
Platform.OS === "web" && {
|
||||
@ -59,10 +83,16 @@ export const ItemGrid = ({
|
||||
props,
|
||||
)}
|
||||
>
|
||||
<Poster src={poster} alt={name} isLoading={isLoading} layout={{ width: percent(100) }} />
|
||||
<Poster
|
||||
src={poster}
|
||||
alt={name}
|
||||
isLoading={isLoading}
|
||||
layout={{ width: percent(100) }}
|
||||
{...css("poster")}
|
||||
/>
|
||||
<Skeleton>
|
||||
{isLoading || (
|
||||
<P numberOfLines={1} {...css({ marginY: 0, textAlign: "center" })}>
|
||||
<P numberOfLines={1} {...css([{ marginY: 0, textAlign: "center" }, "title"])}>
|
||||
{name}
|
||||
</P>
|
||||
)}
|
||||
|
@ -93,7 +93,7 @@ export const BrowsePage: QueryPage<{ slug?: string }> = ({ slug }) => {
|
||||
placeholderCount={15}
|
||||
layout={LayoutComponent.layout}
|
||||
>
|
||||
{(item) => <LayoutComponent {...itemMap(item)} />}
|
||||
{(item, i) => <LayoutComponent {...itemMap(item)} hasTVPreferredFocus={i === 0} />}
|
||||
</InfiniteFetch>
|
||||
</>
|
||||
);
|
||||
|
@ -66,9 +66,10 @@ export const InfiniteFetch = <Data,>({
|
||||
return <EmptyView message={empty} />;
|
||||
}
|
||||
|
||||
const placeholders = [
|
||||
...Array(items ? numColumns - (items.length % numColumns) + numColumns : placeholderCount),
|
||||
].map((_, i) => ({ id: `gen${i}`, isLoading: true } as Data));
|
||||
const count = items ? numColumns - (items.length % numColumns) : placeholderCount;
|
||||
const placeholders = [...Array(count === 0 ? numColumns : count)].map(
|
||||
(_, i) => ({ id: `gen${i}`, isLoading: true } as Data),
|
||||
);
|
||||
|
||||
return (
|
||||
<FlashList
|
||||
|
@ -2251,7 +2251,7 @@ __metadata:
|
||||
dependencies:
|
||||
"@kyoo/models": "workspace:^"
|
||||
"@kyoo/primitives": "workspace:^"
|
||||
"@shopify/flash-list": ^1.4.0
|
||||
"@shopify/flash-list": 1.3.1
|
||||
"@types/react": ^18.0.25
|
||||
typescript: ^4.9.3
|
||||
peerDependencies:
|
||||
@ -3227,20 +3227,6 @@ __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"
|
||||
@ -10416,7 +10402,7 @@ __metadata:
|
||||
react-native-svg-transformer: ^1.0.0
|
||||
react-native-video: alpha
|
||||
typescript: ^4.6.3
|
||||
yoshiki: 0.4.5
|
||||
yoshiki: 1.2.0
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
@ -11996,20 +11982,6 @@ __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"
|
||||
@ -14172,7 +14144,7 @@ __metadata:
|
||||
superjson: ^1.11.0
|
||||
typescript: ^4.9.3
|
||||
webpack: ^5.75.0
|
||||
yoshiki: 0.4.5
|
||||
yoshiki: 1.2.0
|
||||
zod: ^3.19.1
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
@ -14547,9 +14519,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yoshiki@npm:0.4.5":
|
||||
version: 0.4.5
|
||||
resolution: "yoshiki@npm:0.4.5"
|
||||
"yoshiki@npm:1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "yoshiki@npm:1.2.0"
|
||||
dependencies:
|
||||
"@types/node": 18.x.x
|
||||
"@types/react": 18.x.x
|
||||
@ -14564,7 +14536,7 @@ __metadata:
|
||||
optional: true
|
||||
react-native-web:
|
||||
optional: true
|
||||
checksum: 0b2e6576ab0ddf8730da7b38feaa84943728f3c28e07d7472e080da5a9941513a6624dac529918f0efab2bd4bdaac9e4f3dba002f98253a3e26c6be5b32c4e63
|
||||
checksum: 1ef4bc33563bcf344689a5bfbdc4da1636b99552fcff041ada8fa79224c6c3fac2530a890bf6980981fb7aed9cc4e31b89feb1d0bde920179039fc573935ab42
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user