Rework chip to support links and skeletons

This commit is contained in:
Zoe Roux 2023-12-11 01:20:43 +01:00
parent 0272b82166
commit 9304c7ad2f
7 changed files with 77 additions and 47 deletions

View File

@ -28,7 +28,7 @@ export const Button = ({
text,
...props
}: { text: string } & ComponentProps<typeof PressableFeedback>) => {
const { css } = useYoshiki();
const { css } = useYoshiki("button");
return (
<PressableFeedback

View File

@ -18,54 +18,88 @@
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
import { px, rem, Stylable, Theme, useYoshiki } from "yoshiki/native";
import { px, rem, Theme, useYoshiki } from "yoshiki/native";
import { Link } from "./links";
import { P } from "./text";
import { ts } from "./utils";
import { A } from "./links";
import { ComponentType } from "react";
import { capitalize, ts } from "./utils";
import { EnhancedStyle } from "yoshiki/src/native/type";
import { TextProps, TextStyle, ViewStyle } from "react-native";
import { Skeleton } from "./skeleton";
export const Chip = <AsProps = { label: string },>({
export const Chip = ({
color,
size = "medium",
outline = false,
as,
label,
href,
replace,
target,
textProps,
...props
}: {
color?: string;
size?: "small" | "medium" | "large";
outline?: boolean;
as?: ComponentType<AsProps>;
} & AsProps) => {
const { css } = useYoshiki();
label?: string;
href?: string;
replace?: boolean;
target?: string;
textProps?: TextProps;
}) => {
const { css } = useYoshiki("chip");
textProps ??= {};
const sizeMult = size == "medium" ? 1 : size == "small" ? 0.5 : 1.5;
const As = as ?? (P as any);
// @ts-ignore backward compatibilty
if (!as && props.label) props.children = props.label;
return (
<As
<Link
href={href}
replace={replace}
target={target}
{...css(
[
{
pY: ts(1 * sizeMult),
pX: ts(2.5 * sizeMult),
borderRadius: ts(3),
fontSize: rem(0.8),
},
!outline && {
color: (theme: Theme) => theme.alternate.contrast,
bg: color ?? ((theme: Theme) => theme.accent),
overflow: "hidden",
},
outline && {
borderColor: color ?? ((theme: Theme) => theme.accent),
borderStyle: "solid",
borderWidth: px(1),
fover: {
self: {
bg: (theme: Theme) => theme.accent,
},
text: {
color: (theme: Theme) => theme.alternate.contrast,
},
},
},
!outline && {
bg: color ?? ((theme: Theme) => theme.accent),
},
],
props,
)}
/>
>
<P
{...css(
[
"text",
{
marginVertical: 0,
fontSize: rem(0.8),
color: (theme: Theme) => (outline ? theme.contrast : theme.alternate.contrast),
},
],
textProps,
)}
>
{label ? capitalize(label) : <Skeleton {...css({ width: rem(3) })} />}
</P>
</Link>
);
};

View File

@ -19,7 +19,7 @@
*/
import { forwardRef, ReactNode } from "react";
import { Platform, Pressable, TextProps, View, PressableProps } from "react-native";
import { Platform, Pressable, TextProps, View, PressableProps, Linking } from "react-native";
import { TextLink, useLink } from "solito/link";
import { useTheme, useYoshiki } from "yoshiki/native";
import { alpha } from "./themes";
@ -107,7 +107,16 @@ export const Link = ({
// @ts-ignore Missing hrefAttrs type definition.
linkProps.hrefAttrs = { ...linkProps.hrefAttrs, target };
return (
<PressableFeedback {...linkProps} {...props}>
<PressableFeedback
{...linkProps}
{...props}
onPress={(e?: any) => {
props?.onPress?.(e);
if (e?.defaultPrevented) return;
if (Platform.OS !== "web" && href?.includes("://")) Linking.openURL(href);
else linkProps.onPress(e);
}}
>
{children}
</PressableFeedback>
);

View File

@ -30,7 +30,7 @@ export const Rating = ({ rating, color }: { rating?: number; color: Breakpoint<s
<View {...css({ flexDirection: "row", alignItems: "center" })}>
<Icon icon={Star} color={color} {...css({ marginRight: ts(0.5) })} />
<Skeleton {...css({ width: rem(2) })}>
{rating !== undefined && <P {...css({ color })}>{rating ? rating / 10 : "??"} / 10</P>}
{rating !== undefined && <P {...css({ color, verticalAlign: "middle" })}>{rating ? rating / 10 : "??"} / 10</P>}
</Skeleton>
</View>
);

View File

@ -172,7 +172,7 @@ export const EpisodeLine = ({
watchedStatus: WatchStatusV | null;
}> &
Stylable) => {
const { css } = useYoshiki();
const { css } = useYoshiki("episode-line");
const { t } = useTranslation();
return (

View File

@ -52,7 +52,7 @@ import {
} from "@kyoo/primitives";
import { Fragment } from "react";
import { useTranslation } from "react-i18next";
import { ImageStyle, Platform, View } from "react-native";
import { ImageStyle, Platform, Text, View } from "react-native";
import {
Theme,
md,
@ -458,28 +458,17 @@ export const Header = ({
<P {...css({ marginRight: ts(0.5), textAlign: "center" })}>{t("show.links")}:</P>
{(!isLoading
? Object.entries(data.externalId!).filter(([_, data]) => data.link)
: [...Array(3)].map((_, i) => [i, undefined] as const)
).map(([name, data]) => (
: [...Array(3)].map((_) => [undefined, undefined] as const)
).map(([name, data], i) => (
<Chip
key={name}
as={A}
href={data?.link}
key={name ?? i}
label={name}
href={data?.link || undefined}
target="_blank"
size="small"
outline
{...(css({
m: ts(0.5),
color: (theme: Theme) => theme.contrast,
fover: {
self: {
color: (theme: Theme) => theme.alternate.contrast,
bg: (theme: Theme) => theme.accent,
},
},
}) as any)}
>
{data ? capitalize(name) : <Skeleton {...css({ width: rem(3) })} />}
</Chip>
{...css({ m: ts(0.5) })}
/>
))}
</Container>
{type === "show" && (data.watchStatus as ShowWatchStatus)?.nextEpisode && (

View File

@ -166,9 +166,7 @@ export const ItemDetails = ({
{(isLoading || genres) && (
<ScrollView horizontal {...css({ alignItems: "center" })}>
{(genres || [...Array(3)])?.map((x, i) => (
<Chip key={x ?? i} size="small" {...css({ mX: ts(0.5) })}>
{x ?? <Skeleton {...css({ width: rem(3), height: rem(0.8) })} />}
</Chip>
<Chip key={x ?? i} label={x} size="small" {...css({ mX: ts(0.5) })} />
))}
</ScrollView>
)}