mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Add focus handling for the details page
This commit is contained in:
parent
262a26e3df
commit
2652e558d2
@ -67,7 +67,7 @@ const ThemedStack = ({ onLayout }: { onLayout?: () => void }) => {
|
||||
backgroundColor: theme.background,
|
||||
},
|
||||
headerStyle: {
|
||||
backgroundColor: theme.appbar,
|
||||
backgroundColor: theme.accent,
|
||||
},
|
||||
headerTintColor: theme.colors.white,
|
||||
}}
|
||||
|
@ -22,9 +22,10 @@ import React, { ComponentProps, ComponentType, ForwardedRef, forwardRef } from "
|
||||
import { Platform, PressableProps, ViewStyle } from "react-native";
|
||||
import { SvgProps } from "react-native-svg";
|
||||
import { YoshikiStyle } from "yoshiki/dist/type";
|
||||
import { px, useYoshiki } from "yoshiki/native";
|
||||
import { px, Theme, useYoshiki } from "yoshiki/native";
|
||||
import { PressableFeedback } from "./links";
|
||||
import { ts } from "./utils";
|
||||
import { alpha } from "./themes";
|
||||
import { Breakpoint, ts, useBreakpointValue } from "./utils";
|
||||
|
||||
declare module "react" {
|
||||
function forwardRef<T, P = {}>(
|
||||
@ -34,24 +35,25 @@ declare module "react" {
|
||||
|
||||
type IconProps = {
|
||||
icon: ComponentType<SvgProps>;
|
||||
color?: YoshikiStyle<string>;
|
||||
color?: Breakpoint<string>;
|
||||
size?: YoshikiStyle<number | string>;
|
||||
};
|
||||
|
||||
export const Icon = ({ icon: Icon, color, size = 24, ...props }: IconProps) => {
|
||||
const { css, theme } = useYoshiki();
|
||||
const computed = css(
|
||||
{ width: size, height: size, fill: color ?? theme.contrast } as ViewStyle,
|
||||
props,
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const colorValue = Platform.OS !== "web" ? useBreakpointValue(color) : null;
|
||||
|
||||
return (
|
||||
<Icon
|
||||
{...Platform.select<SvgProps>({
|
||||
web: computed,
|
||||
web: css({ width: size, height: size, fill: color ?? theme.contrast } as ViewStyle, props),
|
||||
default: {
|
||||
height: computed.style?.height,
|
||||
width: computed.style?.width,
|
||||
...computed,
|
||||
height: size,
|
||||
width: size,
|
||||
// @ts-ignore
|
||||
fill: colorValue ?? theme.contrast,
|
||||
...props,
|
||||
},
|
||||
})}
|
||||
/>
|
||||
@ -78,11 +80,17 @@ export const IconButton = forwardRef(function _IconButton<AsProps = PressablePro
|
||||
<Container
|
||||
ref={ref as any}
|
||||
accessibilityRole="button"
|
||||
focusRipple
|
||||
{...(css(
|
||||
{
|
||||
p: ts(1),
|
||||
m: px(2),
|
||||
borderRadius: 9999,
|
||||
fover: {
|
||||
self: {
|
||||
bg: (theme: Theme) => alpha(theme.contrast, 0.5),
|
||||
},
|
||||
},
|
||||
},
|
||||
asProps,
|
||||
) as AsProps)}
|
||||
@ -99,10 +107,16 @@ export const IconFab = <AsProps = PressableProps,>(
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
colors={theme.colors.black}
|
||||
color={theme.colors.black}
|
||||
{...(css(
|
||||
{
|
||||
bg: (theme) => theme.accent,
|
||||
fover: {
|
||||
self: {
|
||||
transform: [{ scale: 1.3 }],
|
||||
bg: (theme: Theme) => theme.accent,
|
||||
},
|
||||
},
|
||||
},
|
||||
props,
|
||||
) as any)}
|
||||
|
@ -50,25 +50,24 @@ export const A = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const PressableFeedback = forwardRef<View, PressableProps>(function _Feedback(
|
||||
{ children, ...props },
|
||||
ref,
|
||||
) {
|
||||
const theme = useTheme();
|
||||
export const PressableFeedback = forwardRef<View, PressableProps>(
|
||||
function _Feedback({ children, ...props }, ref) {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
ref={ref}
|
||||
// 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}
|
||||
</Pressable>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<Pressable
|
||||
ref={ref}
|
||||
// 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}
|
||||
</Pressable>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export const Link = ({
|
||||
href,
|
||||
|
@ -24,13 +24,12 @@ import { ThemeBuilder } from "./theme";
|
||||
export const catppuccin: ThemeBuilder = {
|
||||
light: {
|
||||
// Catppuccin latte
|
||||
appbar: "#e64553",
|
||||
overlay0: "#9ca0b0",
|
||||
overlay1: "#7c7f93",
|
||||
link: "#1e66f5",
|
||||
default: {
|
||||
background: "#eff1f5",
|
||||
accent: "#ea76cb",
|
||||
accent: "#e64553",
|
||||
divider: "#8c8fa1",
|
||||
heading: "#4c4f69",
|
||||
paragraph: "#5c5f77",
|
||||
@ -55,13 +54,12 @@ export const catppuccin: ThemeBuilder = {
|
||||
},
|
||||
dark: {
|
||||
// Catppuccin mocha
|
||||
appbar: "#89b4fa",
|
||||
overlay0: "#6c7086",
|
||||
overlay1: "#9399b2",
|
||||
link: "#89b4fa",
|
||||
default: {
|
||||
background: "#1e1e2e",
|
||||
accent: "#f5c2e7",
|
||||
accent: "#89b4fa",
|
||||
divider: "#7f849c",
|
||||
heading: "#cdd6f4",
|
||||
paragraph: "#bac2de",
|
||||
|
@ -35,7 +35,6 @@ type FontList = Partial<
|
||||
>;
|
||||
|
||||
type Mode = {
|
||||
appbar: Property.Color;
|
||||
overlay0: Property.Color;
|
||||
overlay1: Property.Color;
|
||||
link: Property.Color;
|
||||
|
@ -57,7 +57,7 @@ export const ItemGrid = ({
|
||||
fover: {
|
||||
self: focusReset,
|
||||
poster: {
|
||||
borderColor: (theme) => theme.appbar,
|
||||
borderColor: (theme) => theme.accent,
|
||||
},
|
||||
title: {
|
||||
textDecorationLine: "underline",
|
||||
|
@ -18,11 +18,11 @@
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { H6, Image, Link, P, Skeleton, ts } from "@kyoo/primitives";
|
||||
import { focusReset, H6, Image, Link, P, Skeleton, ts } from "@kyoo/primitives";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View } from "react-native";
|
||||
import { Layout, WithLoading } from "../fetch";
|
||||
import { percent, rem, Stylable, useYoshiki } from "yoshiki/native";
|
||||
import { percent, px, rem, Stylable, Theme, useYoshiki } from "yoshiki/native";
|
||||
|
||||
export const episodeDisplayNumber = (
|
||||
episode: {
|
||||
@ -88,6 +88,22 @@ export const EpisodeLine = ({
|
||||
m: ts(1),
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
child: {
|
||||
poster: {
|
||||
borderColor: "transparent",
|
||||
borderWidth: px(4),
|
||||
},
|
||||
},
|
||||
focus: {
|
||||
self: focusReset,
|
||||
poster: {
|
||||
transform: [{ scale: 1.1 }],
|
||||
borderColor: (theme: Theme) => theme.accent,
|
||||
},
|
||||
title: {
|
||||
textDecorationLine: "underline",
|
||||
},
|
||||
},
|
||||
},
|
||||
props,
|
||||
)}
|
||||
@ -102,11 +118,15 @@ export const EpisodeLine = ({
|
||||
width: percent(18),
|
||||
aspectRatio: 16 / 9,
|
||||
}}
|
||||
{...css({ flexShrink: 0, m: ts(1) })}
|
||||
{...css(["poster", { flexShrink: 0, m: ts(1) }])}
|
||||
/>
|
||||
<View {...css({ flexGrow: 1, flexShrink: 1, m: ts(1) })}>
|
||||
<Skeleton>
|
||||
{isLoading || <H6 aria-level={undefined}>{name ?? t("show.episodeNoMetadata")}</H6>}
|
||||
{isLoading || (
|
||||
<H6 aria-level={undefined} {...css("title")}>
|
||||
{name ?? t("show.episodeNoMetadata")}
|
||||
</H6>
|
||||
)}
|
||||
</Skeleton>
|
||||
<Skeleton>{isLoading || <P numberOfLines={3}>{overview}</P>}</Skeleton>
|
||||
</View>
|
||||
|
@ -37,10 +37,11 @@ import {
|
||||
LI,
|
||||
A,
|
||||
ts,
|
||||
Button,
|
||||
} from "@kyoo/primitives";
|
||||
import { Fragment } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Platform, View } from "react-native";
|
||||
import { Platform, Pressable, PressableProps, View } from "react-native";
|
||||
import {
|
||||
Theme,
|
||||
md,
|
||||
@ -160,7 +161,10 @@ const TitleLine = ({
|
||||
as={Link}
|
||||
href={`/watch/${slug}`}
|
||||
color={{ xs: theme.user.colors.black, md: theme.colors.black }}
|
||||
{...css({ bg: { xs: theme.user.accent, md: theme.accent } })}
|
||||
{...css({
|
||||
bg: theme.user.accent,
|
||||
fover: { self: { bg: theme.user.accent } },
|
||||
})}
|
||||
{...tooltip(t("show.play"))}
|
||||
/>
|
||||
<IconButton
|
||||
@ -227,7 +231,9 @@ const Description = ({
|
||||
const { css } = useYoshiki();
|
||||
|
||||
return (
|
||||
<Container {...css({ flexDirection: { xs: "column", sm: "row" } }, props)}>
|
||||
<Container
|
||||
{...css({ paddingBottom: ts(1), flexDirection: { xs: "column", sm: "row" } }, props)}
|
||||
>
|
||||
<P
|
||||
{...css({
|
||||
display: { xs: "flex", sm: "none" },
|
||||
@ -250,10 +256,22 @@ const Description = ({
|
||||
|
||||
<Skeleton
|
||||
lines={4}
|
||||
{...css({ width: percent(100), flexBasis: 0, flexGrow: 1, paddingTop: ts(4) })}
|
||||
{...css({
|
||||
width: percent(100),
|
||||
flexBasis: 0,
|
||||
flexGrow: 1,
|
||||
paddingTop: ts(4),
|
||||
})}
|
||||
>
|
||||
{isLoading || (
|
||||
<P {...css({ flexBasis: 0, flexGrow: 1, textAlign: "justify", paddingTop: ts(4) })}>
|
||||
<P
|
||||
{...css({
|
||||
flexBasis: 0,
|
||||
flexGrow: 1,
|
||||
textAlign: "justify",
|
||||
paddingTop: ts(4),
|
||||
})}
|
||||
>
|
||||
{overview ?? t("show.noOverview")}
|
||||
</P>
|
||||
)}
|
||||
|
@ -18,8 +18,8 @@
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Episode, EpisodeP, QueryIdentifier, Season } from "@kyoo/models";
|
||||
import { Container, SwitchVariant, ts } from "@kyoo/primitives";
|
||||
import { Episode, EpisodeP, QueryIdentifier } from "@kyoo/models";
|
||||
import { Container } from "@kyoo/primitives";
|
||||
import { Stylable } from "yoshiki/native";
|
||||
import { View } from "react-native";
|
||||
import { InfiniteFetch } from "../fetch-infinite";
|
||||
|
@ -20,9 +20,9 @@
|
||||
|
||||
import { QueryIdentifier, QueryPage, Show, ShowP } from "@kyoo/models";
|
||||
import { Platform, View, ViewProps } from "react-native";
|
||||
import { percent, useYoshiki, vh } from "yoshiki/native";
|
||||
import { percent, useYoshiki } from "yoshiki/native";
|
||||
import { DefaultLayout } from "../layout";
|
||||
import { EpisodeList, SeasonTab } from "./season";
|
||||
import { EpisodeList } from "./season";
|
||||
import { Header } from "./header";
|
||||
import Svg, { Path, SvgProps } from "react-native-svg";
|
||||
import { Container, SwitchVariant } from "@kyoo/primitives";
|
||||
|
@ -23,7 +23,13 @@ import { MeQuery, Navbar } from "./navbar";
|
||||
import { useYoshiki } from "yoshiki/native";
|
||||
import { Main } from "@kyoo/primitives";
|
||||
|
||||
export const DefaultLayout = ({ page, transparent }: { page: ReactElement, transparent?: boolean }) => {
|
||||
export const DefaultLayout = ({
|
||||
page,
|
||||
transparent,
|
||||
}: {
|
||||
page: ReactElement;
|
||||
transparent?: boolean;
|
||||
}) => {
|
||||
const { css } = useYoshiki();
|
||||
|
||||
return (
|
||||
@ -36,6 +42,7 @@ export const DefaultLayout = ({ page, transparent }: { page: ReactElement, trans
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
shadowOpacity: 0,
|
||||
},
|
||||
)}
|
||||
/>
|
||||
|
@ -177,7 +177,7 @@ export const Navbar = (props: Stylable) => {
|
||||
<Header
|
||||
{...css(
|
||||
{
|
||||
backgroundColor: (theme) => theme.appbar,
|
||||
backgroundColor: (theme) => theme.accent,
|
||||
paddingX: ts(2),
|
||||
height: { xs: 48, sm: 64 },
|
||||
flexDirection: "row",
|
||||
|
Loading…
x
Reference in New Issue
Block a user