Files
rn-app/components/Themed.tsx
2025-11-06 16:37:01 +08:00

128 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 主题化组件
*
* 提供自动适配主题的 Text 和 View 组件
* 支持从 settingsStore 读取主题设置
*/
import { Text as DefaultText, View as DefaultView, TextStyle } from 'react-native';
import Colors from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useTheme';
type ThemeProps = {
lightColor?: string;
darkColor?: string;
};
export type TextProps = ThemeProps & DefaultText['props'];
export type ViewProps = ThemeProps & DefaultView['props'];
/**
* 获取主题颜色
*
* @param props - 包含 light 和 dark 颜色的对象
* @param colorName - Colors 中定义的颜色名称
* @returns 当前主题对应的颜色值
*/
export function useThemeColor(
props: { light?: string; dark?: string },
colorName: keyof typeof Colors.light & keyof typeof Colors.dark
) {
const theme = useColorScheme() ?? 'light';
const colorFromProps = props[theme];
if (colorFromProps) {
return colorFromProps;
} else {
return Colors[theme][colorName];
}
}
/**
* 主题化 Text 组件
*
* 自动应用当前主题的文本颜色
*/
export function Text(props: TextProps) {
const { style, lightColor, darkColor, ...otherProps } = props;
const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text');
return <DefaultText style={[{ color }, style]} {...otherProps} />;
}
/**
* 主题化 View 组件
*
* 自动应用当前主题的背景颜色
*/
export function View(props: ViewProps) {
const { style, lightColor, darkColor, ...otherProps } = props;
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
return <DefaultView style={[{ backgroundColor }, style]} {...otherProps} />;
}
/**
* 主题化 Text 组件(带类型)
*
* 支持不同的文本类型title, subtitle, defaultSemiBold, link
*/
export type ThemedTextProps = TextProps & {
type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link';
};
export function ThemedText({ style, type = 'default', ...rest }: ThemedTextProps) {
const color = useThemeColor({}, 'text');
const linkColor = useThemeColor({}, 'tint');
const typeStyles: Record<string, TextStyle> = {
default: {
fontSize: 16,
lineHeight: 24,
},
defaultSemiBold: {
fontSize: 16,
lineHeight: 24,
fontWeight: '600',
},
title: {
fontSize: 32,
fontWeight: 'bold',
lineHeight: 40,
},
subtitle: {
fontSize: 20,
fontWeight: '600',
lineHeight: 28,
},
link: {
fontSize: 16,
lineHeight: 24,
color: linkColor,
},
};
return (
<Text
style={[
{ color },
typeStyles[type],
style,
]}
{...rest}
/>
);
}
/**
* 主题化 View 组件
*/
export type ThemedViewProps = ViewProps;
export function ThemedView({ style, ...otherProps }: ThemedViewProps) {
const backgroundColor = useThemeColor({}, 'background');
return <DefaultView style={[{ backgroundColor }, style]} {...otherProps} />;
}