Remove yoshiki, fix ts issues

This commit is contained in:
Zoe Roux
2026-03-12 10:38:24 +01:00
parent f4be92f190
commit e771ca591f
22 changed files with 201 additions and 587 deletions
-7
View File
@@ -3,18 +3,11 @@ import KyooLogo from "public/icon.svg";
import type { ComponentProps } from "react";
import { type ImageStyle, Platform, View, type ViewProps } from "react-native";
import { withUniwind } from "uniwind";
import type { YoshikiStyle } from "yoshiki/src/type";
import type { KImage } from "~/models";
import { useToken } from "~/providers/account-context";
import { cn } from "~/utils";
import { Skeleton } from "../skeleton";
export type YoshikiEnhanced<Style> = Style extends any
? {
[key in keyof Style]: YoshikiStyle<Style[key]>;
}
: never;
const Img = withUniwind(EImage);
// This should stay in think with `ImageBackground`.
-1
View File
@@ -20,7 +20,6 @@ export * from "./select";
export * from "./skeleton";
export * from "./slider";
export * from "./text";
export * from "./theme";
export * from "./tooltip";
export * from "./utils";
-69
View File
@@ -1,69 +0,0 @@
import type { ThemeBuilder } from "./theme";
// Ref: https://github.com/catppuccin/catppuccin
export const catppuccin: ThemeBuilder = {
light: {
// Catppuccin latte
overlay0: "#9ca0b0",
overlay1: "#7c7f93",
lightOverlay: "#eff1f599",
darkOverlay: "#4c4f6999",
link: "#1e66f5",
default: {
background: "#eff1f5",
accent: "#e64553",
divider: "#8c8fa1",
heading: "#4c4f69",
paragraph: "#5c5f77",
subtext: "#6c6f85",
},
variant: {
background: "#e6e9ef",
accent: "#d20f39",
divider: "#dd7878",
heading: "#4c4f69",
paragraph: "#5c5f77",
subtext: "#6c6f85",
},
colors: {
red: "#d20f39",
green: "#40a02b",
blue: "#1e66f5",
yellow: "#df8e1d",
black: "#4c4f69",
white: "#eff1f5",
},
},
dark: {
// Catppuccin mocha
overlay0: "#6c7086",
overlay1: "#9399b2",
lightOverlay: "#f5f0f899",
darkOverlay: "#11111b99",
link: "#89b4fa",
default: {
background: "#1e1e2e",
accent: "#89b4fa",
divider: "#7f849c",
heading: "#cdd6f4",
paragraph: "#bac2de",
subtext: "#a6adc8",
},
variant: {
background: "#181825",
accent: "#74c7ec",
divider: "#1e1e2e",
heading: "#cdd6f4",
paragraph: "#bac2de",
subtext: "#a6adc8",
},
colors: {
red: "#f38ba8",
green: "#a6e3a1",
blue: "#89b4fa",
yellow: "#f9e2af",
black: "#11111b",
white: "#f5f0f8",
},
},
};
-2
View File
@@ -1,2 +0,0 @@
export * from "./catppuccin";
export * from "./theme";
-215
View File
@@ -1,215 +0,0 @@
import type { Property } from "csstype";
import type { ReactNode } from "react";
import { Platform, type TextStyle } from "react-native";
import {
type Theme,
useAutomaticTheme,
ThemeProvider as WebThemeProvider,
} from "yoshiki";
import "yoshiki";
import { ThemeProvider, useTheme, useYoshiki } from "yoshiki/native";
import "yoshiki/native";
import { catppuccin } from "./catppuccin";
type FontList = Partial<
Record<Exclude<TextStyle["fontWeight"], null | undefined | number>, string>
>;
type Mode = {
mode: "light" | "dark" | "auto";
overlay0: Property.Color;
overlay1: Property.Color;
lightOverlay: Property.Color;
darkOverlay: Property.Color;
themeOverlay: Property.Color;
link: Property.Color;
contrast: Property.Color;
variant: Variant;
colors: {
red: Property.Color;
green: Property.Color;
blue: Property.Color;
yellow: Property.Color;
white: Property.Color;
black: Property.Color;
};
};
type Variant = {
background: Property.Color;
accent: Property.Color;
divider: Property.Color;
heading: Property.Color;
paragraph: Property.Color;
subtext: Property.Color;
};
declare module "yoshiki" {
export interface Theme extends Mode, Variant {
light: Mode & Variant;
dark: Mode & Variant;
user: Mode & Variant;
alternate: Mode & Variant;
font: FontList;
}
}
export type { Theme } from "yoshiki";
export type ThemeBuilder = {
light: Omit<Mode, "contrast" | "mode" | "themeOverlay"> & {
default: Variant;
};
dark: Omit<Mode, "contrast" | "mode" | "themeOverlay"> & { default: Variant };
};
const selectMode = (
theme: ThemeBuilder & { font: FontList },
mode: "light" | "dark" | "auto",
): Theme => {
const { light: lightBuilder, dark: darkBuilder, ...options } = theme;
const light: Mode & Variant = {
...lightBuilder,
...lightBuilder.default,
contrast: lightBuilder.colors.black,
themeOverlay: lightBuilder.lightOverlay,
mode: "light",
};
const dark: Mode & Variant = {
...darkBuilder,
...darkBuilder.default,
contrast: darkBuilder.colors.white,
themeOverlay: darkBuilder.darkOverlay,
mode: "dark",
};
if (Platform.OS !== "web" || mode !== "auto") {
const value = mode === "light" ? light : dark;
const alternate = mode === "light" ? dark : light;
return {
...options,
...value,
light,
dark,
user: value,
alternate,
};
}
// biome-ignore lint/correctness/useHookAtTopLevel: const
const auto = useAutomaticTheme("theme", { light, dark });
// biome-ignore lint/correctness/useHookAtTopLevel: const
const alternate = useAutomaticTheme("alternate", {
dark: light,
light: dark,
});
return {
...options,
...auto,
mode: "auto",
light,
dark,
user: { ...auto, mode: "auto" },
alternate: { ...alternate, mode: "auto" },
};
};
const switchVariant = (theme: Theme) => {
return {
...theme,
...theme.variant,
variant: {
background: theme.background,
accent: theme.accent,
divider: theme.divider,
heading: theme.heading,
paragraph: theme.paragraph,
subtext: theme.subtext,
},
};
};
export const ThemeSelector = ({
children,
theme,
font,
}: {
children: ReactNode;
theme: "light" | "dark" | "auto";
font: FontList;
}) => {
const newTheme = selectMode({ ...catppuccin, font }, theme);
return (
<ThemeProvider theme={newTheme}>
<WebThemeProvider theme={newTheme}>{children as any}</WebThemeProvider>
</ThemeProvider>
);
};
export type YoshikiFunc<T> = (props: ReturnType<typeof useYoshiki>) => T;
const YoshikiProvider = ({
children,
}: {
children: YoshikiFunc<ReactNode>;
}) => {
const yoshiki = useYoshiki();
return <>{children(yoshiki)}</>;
};
export const SwitchVariant = ({
children,
}: {
children: ReactNode | YoshikiFunc<ReactNode>;
}) => {
const theme = useTheme();
return (
<ThemeProvider theme={switchVariant(theme)}>
{typeof children === "function" ? (
<YoshikiProvider>{children}</YoshikiProvider>
) : (
(children as any)
)}
</ThemeProvider>
);
};
export const ContrastArea = ({
children,
mode = "dark",
contrastText,
}: {
children: ReactNode | YoshikiFunc<ReactNode>;
mode?: "light" | "dark" | "user" | "alternate";
contrastText?: boolean;
}) => {
const oldTheme = useTheme();
const theme: Theme = { ...oldTheme, ...oldTheme[mode] };
return (
<ThemeProvider
theme={
contrastText
? {
...theme,
// Keep the same skeletons, it looks weird otherwise.
overlay0: theme.user.overlay0,
overlay1: theme.user.overlay1,
heading: theme.contrast,
paragraph: theme.heading,
}
: theme
}
>
{typeof children === "function" ? (
<YoshikiProvider>{children}</YoshikiProvider>
) : (
(children as any)
)}
</ThemeProvider>
);
};
export const alpha = (color: Property.Color, alpha: number) => {
return color + Math.round(alpha * 255).toString(16);
};
+10 -4
View File
@@ -1,6 +1,6 @@
import type { ComponentProps } from "react";
import { Tooltip as RTooltip } from "react-tooltip";
import { useTheme } from "yoshiki/native";
import { useResolveClassNames } from "uniwind";
export const tooltip = (tooltip: string, up?: boolean) => ({
dataSet: {
@@ -12,14 +12,20 @@ export const tooltip = (tooltip: string, up?: boolean) => ({
});
export const Tooltip = (props: ComponentProps<typeof RTooltip>) => {
const theme = useTheme();
const { color: background } = useResolveClassNames(
"text-color-dark dark:text-color-light",
);
const { color } = useResolveClassNames(
"text-color-light dark:text-color-dark",
);
return (
<RTooltip
id="tooltip"
opacity={0.9}
style={{
background: theme.contrast,
color: theme.alternate.contrast,
background: background as string,
color: color as string,
}}
{...props}
/>
+23 -7
View File
@@ -1,15 +1,31 @@
import { useWindowDimensions } from "react-native";
import {
breakpoints,
isBreakpoints,
type Breakpoints as YoshikiBreakpoint,
} from "yoshiki/native";
export const breakpoints = {
xs: 0,
sm: 600,
md: 900,
lg: 1200,
xl: 1600,
};
type Breakpoints<Property> = {
[key in keyof typeof breakpoints]?: Property;
};
type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = Partial<T> &
U[keyof U];
export type Breakpoint<T> = T | AtLeastOne<YoshikiBreakpoint<T>>;
export type Breakpoint<T> = T | AtLeastOne<Breakpoints<T>>;
// copied from yoshiki.
const isBreakpoints = <T>(value: unknown): value is Breakpoints<T> => {
if (typeof value !== "object" || !value) return false;
for (const v of Object.keys(value)) {
if (!(v in breakpoints)) {
return false;
}
}
return true;
};
const useBreakpoint = () => {
const { width } = useWindowDimensions();
const idx = Object.values(breakpoints).findLastIndex((x) => x <= width);
@@ -19,7 +35,7 @@ const useBreakpoint = () => {
const getBreakpointValue = <T>(value: Breakpoint<T>, breakpoint: number): T => {
if (!isBreakpoints(value)) return value;
const bpKeys = Object.keys(breakpoints) as Array<keyof YoshikiBreakpoint<T>>;
const bpKeys = Object.keys(breakpoints) as Array<keyof Breakpoints<T>>;
for (let i = breakpoint; i >= 0; i--) {
if (bpKeys[i] in value) {
const val = value[bpKeys[i]];