u
This commit is contained in:
1 parent
f384a2a6e9
commit
cd191fdc60
25 files changed
+4267
-32
No files matched your search
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
||||
"plugins": [
|
||||
"typescript",
|
||||
"unicorn",
|
||||
"oxc"
|
||||
],
|
||||
"categories": {
|
||||
"correctness": "error"
|
||||
},
|
||||
"rules": {},
|
||||
"env": {
|
||||
"builtin": true
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,13 @@
|
||||
"backgroundImage": "./assets/images/android-icon-background.png",
|
||||
"monochromeImage": "./assets/images/android-icon-monochrome.png"
|
||||
},
|
||||
"predictiveBackGestureEnabled": false
|
||||
"predictiveBackGestureEnabled": false,
|
||||
"package": "com.anonymous.apprn4"
|
||||
},
|
||||
"web": {
|
||||
"output": "static",
|
||||
"favicon": "./assets/images/favicon.png"
|
||||
"favicon": "./assets/images/favicon.png",
|
||||
"bundler": "metro"
|
||||
},
|
||||
"plugins": [
|
||||
"expo-router",
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
module.exports = function (api) {
|
||||
api.cache(true);
|
||||
return {
|
||||
presets: ['babel-preset-expo'],
|
||||
presets: [
|
||||
["babel-preset-expo", { jsxImportSource: "nativewind" }],
|
||||
"nativewind/babel",
|
||||
],
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
import { View, type ViewProps } from 'react-native';
|
||||
import { View, type ViewProps } from "react-native";
|
||||
|
||||
import { ThemeColor } from '@/constants/theme';
|
||||
import { useTheme } from '@/hooks/use-theme';
|
||||
import { ThemeColor } from "@/constants/theme";
|
||||
import { useTheme } from "@/hooks/use-theme";
|
||||
|
||||
export type ThemedViewProps = ViewProps & {
|
||||
lightColor?: string;
|
||||
@@ -12,5 +12,5 @@ export type ThemedViewProps = ViewProps & {
|
||||
export function ThemedView({ style, lightColor, darkColor, type, ...otherProps }: ThemedViewProps) {
|
||||
const theme = useTheme();
|
||||
|
||||
return <View style={[{ backgroundColor: theme[type ?? 'background'] }, style]} {...otherProps} />;
|
||||
return <View style={[{ backgroundColor: theme[type ?? "background"] }, style]} {...otherProps} />;
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
// Learn more https://docs.expo.io/guides/customizing-metro
|
||||
const { getDefaultConfig } = require('expo/metro-config');
|
||||
const { withNativeWind } = require('nativewind/metro');
|
||||
|
||||
/** @type {import('expo/metro-config').MetroConfig} */
|
||||
const config = getDefaultConfig(__dirname);
|
||||
|
||||
module.exports = config;
|
||||
module.exports = withNativeWind(config, { input: './src/styles/global.css' })
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
/// <reference types="nativewind/types" />
|
||||
|
||||
// NOTE: This file should not be edited and should be committed with your source code. It is generated by NativeWind.
|
||||
@@ -5,12 +5,15 @@
|
||||
"scripts": {
|
||||
"start": "expo start",
|
||||
"reset-project": "node ./scripts/reset-project.js",
|
||||
"android": "expo start --android",
|
||||
"ios": "expo start --ios",
|
||||
"android": "expo run:android",
|
||||
"ios": "expo run:ios",
|
||||
"web": "expo start --web",
|
||||
"lint": "expo lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@gluestack-ui/core": "^3.0.16",
|
||||
"@gluestack-ui/utils": "^3.0.19",
|
||||
"@gluestack/ui-next-adapter": "^3.0.3",
|
||||
"@react-navigation/bottom-tabs": "^7.15.5",
|
||||
"@react-navigation/elements": "^2.9.10",
|
||||
"@react-navigation/native": "^7.1.33",
|
||||
@@ -29,6 +32,7 @@
|
||||
"expo-symbols": "~55.0.7",
|
||||
"expo-system-ui": "~55.0.15",
|
||||
"expo-web-browser": "~55.0.14",
|
||||
"nativewind": "^4.2.3",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"react-native": "0.83.4",
|
||||
@@ -36,12 +40,18 @@
|
||||
"react-native-reanimated": "4.2.1",
|
||||
"react-native-safe-area-context": "~5.6.2",
|
||||
"react-native-screens": "~4.23.0",
|
||||
"react-native-svg": "15.15.3",
|
||||
"react-native-web": "~0.21.0",
|
||||
"react-native-worklets": "0.7.2"
|
||||
"react-native-worklets": "0.7.2",
|
||||
"zustand": "^5.0.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "~19.2.2",
|
||||
"babel-preset-expo": "^55.0.16",
|
||||
"oxfmt": "^0.44.0",
|
||||
"oxlint": "^1.59.0",
|
||||
"prettier-plugin-tailwindcss": "^0.5.14",
|
||||
"tailwindcss": "^3.4.19",
|
||||
"typescript": "~5.9.2"
|
||||
},
|
||||
"private": true
|
||||
|
||||
Generated
+2785
-13
File diff suppressed because it is too large.
Load diff
@@ -1,5 +1,22 @@
|
||||
import { Stack } from "expo-router";
|
||||
import { useIsDark } from "@/hooks/use-color-scheme";
|
||||
import { DarkTheme, DefaultTheme, ThemeProvider } from "@react-navigation/native";
|
||||
import { StatusBar } from "expo-status-bar";
|
||||
import "@/styles/global.css";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default function RootLayout() {
|
||||
return <Stack />;
|
||||
const isDark = useIsDark();
|
||||
|
||||
useEffect(() => {
|
||||
console.log(isDark);
|
||||
}, [isDark]);
|
||||
|
||||
return (
|
||||
<ThemeProvider value={isDark ? DarkTheme : DefaultTheme}>
|
||||
<StatusBar style={isDark ? "light" : "dark"} />
|
||||
|
||||
<Stack screenOptions={{ headerShown: false }} />
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,144 @@
|
||||
import React from "react";
|
||||
import { StyleSheet, View } from "react-native";
|
||||
import { BlurTargetView, BlurView } from "expo-blur";
|
||||
import React, { useRef } from "react";
|
||||
import { ScrollView, StyleSheet, Text, View } from "react-native";
|
||||
|
||||
export default function OnlyBlurDemo() {
|
||||
return <View style={{ flex: 1, backgroundColor: "#fff" }}></View>;
|
||||
const targetRef = useRef(null);
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
{/* 👇 Blur 目标区域 */}
|
||||
<BlurTargetView ref={targetRef} style={{ flex: 1 }}>
|
||||
<ScrollView
|
||||
contentContainerStyle={{ padding: 16, paddingBottom: 120 }}
|
||||
removeClippedSubviews={false}
|
||||
scrollEventThrottle={16}
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
<View style={[styles.card, { backgroundColor: "#4EA0FF" }]}>
|
||||
<Text style={styles.cardText}>蓝色卡片</Text>
|
||||
</View>
|
||||
|
||||
<View style={[styles.card, { backgroundColor: "#FF9500" }]}>
|
||||
<Text style={styles.cardText}>橙色卡片</Text>
|
||||
</View>
|
||||
|
||||
<View style={[styles.card, { backgroundColor: "#34C759" }]}>
|
||||
<Text style={styles.cardText}>绿色卡片</Text>
|
||||
</View>
|
||||
|
||||
{Array.from({ length: 10 }).map((_, i) => (
|
||||
<View
|
||||
key={i}
|
||||
style={[
|
||||
styles.listItem,
|
||||
{
|
||||
backgroundColor: i % 2 === 0 ? "#FFFFFF" : "#F2F2F7",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Text style={styles.listText}>列表项 {i + 1}</Text>
|
||||
</View>
|
||||
))}
|
||||
|
||||
<View style={[styles.card, { backgroundColor: "#AF52DE" }]}>
|
||||
<Text style={styles.cardText}>紫色区域</Text>
|
||||
</View>
|
||||
|
||||
<View style={[styles.card, { backgroundColor: "#FF2D55" }]}>
|
||||
<Text style={styles.cardText}>红色区域</Text>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</BlurTargetView>
|
||||
|
||||
{/* 👇 毛玻璃底部栏(稳定版) */}
|
||||
<BlurView
|
||||
intensity={60}
|
||||
tint="light"
|
||||
blurMethod="dimezisBlurView"
|
||||
blurTarget={targetRef}
|
||||
style={styles.blur}
|
||||
>
|
||||
{/* 用 overlay 替代 opacity(避免闪烁) */}
|
||||
<View style={styles.overlay} />
|
||||
|
||||
<View style={styles.tabContent}>
|
||||
<Text style={styles.tabText}>TabBar</Text>
|
||||
</View>
|
||||
</BlurView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({});
|
||||
const styles = StyleSheet.create({
|
||||
blur: {
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
height: 56,
|
||||
overflow: "hidden",
|
||||
borderRadius: 999,
|
||||
margin: 16,
|
||||
|
||||
// ✨ 关键:只保留极轻“环境感”
|
||||
shadowColor: "#000",
|
||||
shadowOffset: { width: 0, height: 10 },
|
||||
shadowOpacity: 0.03,
|
||||
shadowRadius: 20,
|
||||
|
||||
// Android 基本关闭
|
||||
elevation: 0,
|
||||
|
||||
// ✨ 玻璃底(很关键)
|
||||
backgroundColor: "rgba(255,255,255,0.10)",
|
||||
|
||||
// ✨ 极弱边缘,不要存在感
|
||||
borderWidth: 0.5,
|
||||
borderColor: "rgba(255,255,255,0.12)",
|
||||
},
|
||||
|
||||
overlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
backgroundColor: "rgba(249,249,249,0.4)",
|
||||
},
|
||||
|
||||
tabContent: {
|
||||
flex: 1,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
|
||||
tabText: {
|
||||
fontSize: 14,
|
||||
fontWeight: "600",
|
||||
color: "#333",
|
||||
},
|
||||
|
||||
card: {
|
||||
height: 120,
|
||||
borderRadius: 16,
|
||||
marginBottom: 16,
|
||||
padding: 16,
|
||||
justifyContent: "flex-end",
|
||||
},
|
||||
|
||||
cardText: {
|
||||
color: "#FFFFFF",
|
||||
fontSize: 18,
|
||||
fontWeight: "600",
|
||||
},
|
||||
|
||||
listItem: {
|
||||
height: 60,
|
||||
borderRadius: 12,
|
||||
marginBottom: 12,
|
||||
paddingHorizontal: 16,
|
||||
justifyContent: "center",
|
||||
},
|
||||
|
||||
listText: {
|
||||
fontSize: 16,
|
||||
color: "#333",
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,434 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import { createButton } from '@gluestack-ui/core/button/creator';
|
||||
import {
|
||||
tva,
|
||||
withStyleContext,
|
||||
useStyleContext,
|
||||
type VariantProps,
|
||||
} from '@gluestack-ui/utils/nativewind-utils';
|
||||
import { cssInterop } from 'nativewind';
|
||||
import { ActivityIndicator, Pressable, Text, View } from 'react-native';
|
||||
import { PrimitiveIcon, UIIcon } from '@gluestack-ui/core/icon/creator';
|
||||
|
||||
const SCOPE = 'BUTTON';
|
||||
|
||||
const Root = withStyleContext(Pressable, SCOPE);
|
||||
|
||||
const UIButton = createButton({
|
||||
Root: Root,
|
||||
Text,
|
||||
Group: View,
|
||||
Spinner: ActivityIndicator,
|
||||
Icon: UIIcon,
|
||||
});
|
||||
|
||||
cssInterop(PrimitiveIcon, {
|
||||
className: {
|
||||
target: 'style',
|
||||
nativeStyleToProp: {
|
||||
height: true,
|
||||
width: true,
|
||||
fill: true,
|
||||
color: 'classNameColor',
|
||||
stroke: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const buttonStyle = tva({
|
||||
base: 'group/button rounded bg-primary-500 flex-row items-center justify-center data-[focus-visible=true]:web:outline-none data-[focus-visible=true]:web:ring-2 data-[disabled=true]:opacity-40 gap-2',
|
||||
variants: {
|
||||
action: {
|
||||
primary:
|
||||
'bg-primary-500 data-[hover=true]:bg-primary-600 data-[active=true]:bg-primary-700 border-primary-300 data-[hover=true]:border-primary-400 data-[active=true]:border-primary-500 data-[focus-visible=true]:web:ring-indicator-info',
|
||||
secondary:
|
||||
'bg-secondary-500 border-secondary-300 data-[hover=true]:bg-secondary-600 data-[hover=true]:border-secondary-400 data-[active=true]:bg-secondary-700 data-[active=true]:border-secondary-700 data-[focus-visible=true]:web:ring-indicator-info',
|
||||
positive:
|
||||
'bg-success-500 border-success-300 data-[hover=true]:bg-success-600 data-[hover=true]:border-success-400 data-[active=true]:bg-success-700 data-[active=true]:border-success-500 data-[focus-visible=true]:web:ring-indicator-info',
|
||||
negative:
|
||||
'bg-error-500 border-error-300 data-[hover=true]:bg-error-600 data-[hover=true]:border-error-400 data-[active=true]:bg-error-700 data-[active=true]:border-error-500 data-[focus-visible=true]:web:ring-indicator-info',
|
||||
default:
|
||||
'bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent',
|
||||
},
|
||||
variant: {
|
||||
link: 'px-0',
|
||||
outline:
|
||||
'bg-transparent border data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent',
|
||||
solid: '',
|
||||
},
|
||||
|
||||
size: {
|
||||
xs: 'px-3.5 h-8',
|
||||
sm: 'px-4 h-9',
|
||||
md: 'px-5 h-10',
|
||||
lg: 'px-6 h-11',
|
||||
xl: 'px-7 h-12',
|
||||
},
|
||||
},
|
||||
compoundVariants: [
|
||||
{
|
||||
action: 'primary',
|
||||
variant: 'link',
|
||||
class:
|
||||
'px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent',
|
||||
},
|
||||
{
|
||||
action: 'secondary',
|
||||
variant: 'link',
|
||||
class:
|
||||
'px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent',
|
||||
},
|
||||
{
|
||||
action: 'positive',
|
||||
variant: 'link',
|
||||
class:
|
||||
'px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent',
|
||||
},
|
||||
{
|
||||
action: 'negative',
|
||||
variant: 'link',
|
||||
class:
|
||||
'px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent',
|
||||
},
|
||||
{
|
||||
action: 'primary',
|
||||
variant: 'outline',
|
||||
class:
|
||||
'bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent',
|
||||
},
|
||||
{
|
||||
action: 'secondary',
|
||||
variant: 'outline',
|
||||
class:
|
||||
'bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent',
|
||||
},
|
||||
{
|
||||
action: 'positive',
|
||||
variant: 'outline',
|
||||
class:
|
||||
'bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent',
|
||||
},
|
||||
{
|
||||
action: 'negative',
|
||||
variant: 'outline',
|
||||
class:
|
||||
'bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const buttonTextStyle = tva({
|
||||
base: 'text-typography-0 font-semibold web:select-none',
|
||||
parentVariants: {
|
||||
action: {
|
||||
primary:
|
||||
'text-primary-600 data-[hover=true]:text-primary-600 data-[active=true]:text-primary-700',
|
||||
secondary:
|
||||
'text-typography-500 data-[hover=true]:text-typography-600 data-[active=true]:text-typography-700',
|
||||
positive:
|
||||
'text-success-600 data-[hover=true]:text-success-600 data-[active=true]:text-success-700',
|
||||
negative:
|
||||
'text-error-600 data-[hover=true]:text-error-600 data-[active=true]:text-error-700',
|
||||
},
|
||||
variant: {
|
||||
link: 'data-[hover=true]:underline data-[active=true]:underline',
|
||||
outline: '',
|
||||
solid:
|
||||
'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0',
|
||||
},
|
||||
size: {
|
||||
xs: 'text-xs',
|
||||
sm: 'text-sm',
|
||||
md: 'text-base',
|
||||
lg: 'text-lg',
|
||||
xl: 'text-xl',
|
||||
},
|
||||
},
|
||||
parentCompoundVariants: [
|
||||
{
|
||||
variant: 'solid',
|
||||
action: 'primary',
|
||||
class:
|
||||
'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0',
|
||||
},
|
||||
{
|
||||
variant: 'solid',
|
||||
action: 'secondary',
|
||||
class:
|
||||
'text-typography-800 data-[hover=true]:text-typography-800 data-[active=true]:text-typography-800',
|
||||
},
|
||||
{
|
||||
variant: 'solid',
|
||||
action: 'positive',
|
||||
class:
|
||||
'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0',
|
||||
},
|
||||
{
|
||||
variant: 'solid',
|
||||
action: 'negative',
|
||||
class:
|
||||
'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0',
|
||||
},
|
||||
{
|
||||
variant: 'outline',
|
||||
action: 'primary',
|
||||
class:
|
||||
'text-primary-500 data-[hover=true]:text-primary-500 data-[active=true]:text-primary-500',
|
||||
},
|
||||
{
|
||||
variant: 'outline',
|
||||
action: 'secondary',
|
||||
class:
|
||||
'text-typography-500 data-[hover=true]:text-primary-600 data-[active=true]:text-typography-700',
|
||||
},
|
||||
{
|
||||
variant: 'outline',
|
||||
action: 'positive',
|
||||
class:
|
||||
'text-primary-500 data-[hover=true]:text-primary-500 data-[active=true]:text-primary-500',
|
||||
},
|
||||
{
|
||||
variant: 'outline',
|
||||
action: 'negative',
|
||||
class:
|
||||
'text-primary-500 data-[hover=true]:text-primary-500 data-[active=true]:text-primary-500',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const buttonIconStyle = tva({
|
||||
base: 'fill-none',
|
||||
parentVariants: {
|
||||
variant: {
|
||||
link: 'data-[hover=true]:underline data-[active=true]:underline',
|
||||
outline: '',
|
||||
solid:
|
||||
'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0',
|
||||
},
|
||||
size: {
|
||||
xs: 'h-3.5 w-3.5',
|
||||
sm: 'h-4 w-4',
|
||||
md: 'h-[18px] w-[18px]',
|
||||
lg: 'h-[18px] w-[18px]',
|
||||
xl: 'h-5 w-5',
|
||||
},
|
||||
action: {
|
||||
primary:
|
||||
'text-primary-600 data-[hover=true]:text-primary-600 data-[active=true]:text-primary-700',
|
||||
secondary:
|
||||
'text-typography-500 data-[hover=true]:text-typography-600 data-[active=true]:text-typography-700',
|
||||
positive:
|
||||
'text-success-600 data-[hover=true]:text-success-600 data-[active=true]:text-success-700',
|
||||
|
||||
negative:
|
||||
'text-error-600 data-[hover=true]:text-error-600 data-[active=true]:text-error-700',
|
||||
},
|
||||
},
|
||||
parentCompoundVariants: [
|
||||
{
|
||||
variant: 'solid',
|
||||
action: 'primary',
|
||||
class:
|
||||
'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0',
|
||||
},
|
||||
{
|
||||
variant: 'solid',
|
||||
action: 'secondary',
|
||||
class:
|
||||
'text-typography-800 data-[hover=true]:text-typography-800 data-[active=true]:text-typography-800',
|
||||
},
|
||||
{
|
||||
variant: 'solid',
|
||||
action: 'positive',
|
||||
class:
|
||||
'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0',
|
||||
},
|
||||
{
|
||||
variant: 'solid',
|
||||
action: 'negative',
|
||||
class:
|
||||
'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const buttonGroupStyle = tva({
|
||||
base: '',
|
||||
variants: {
|
||||
space: {
|
||||
'xs': 'gap-1',
|
||||
'sm': 'gap-2',
|
||||
'md': 'gap-3',
|
||||
'lg': 'gap-4',
|
||||
'xl': 'gap-5',
|
||||
'2xl': 'gap-6',
|
||||
'3xl': 'gap-7',
|
||||
'4xl': 'gap-8',
|
||||
},
|
||||
isAttached: {
|
||||
true: 'gap-0',
|
||||
},
|
||||
flexDirection: {
|
||||
'row': 'flex-row',
|
||||
'column': 'flex-col',
|
||||
'row-reverse': 'flex-row-reverse',
|
||||
'column-reverse': 'flex-col-reverse',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
type IButtonProps = Omit<
|
||||
React.ComponentPropsWithoutRef<typeof UIButton>,
|
||||
'context'
|
||||
> &
|
||||
VariantProps<typeof buttonStyle> & { className?: string };
|
||||
|
||||
const Button = React.forwardRef<
|
||||
React.ElementRef<typeof UIButton>,
|
||||
IButtonProps
|
||||
>(
|
||||
(
|
||||
{ className, variant = 'solid', size = 'md', action = 'primary', ...props },
|
||||
ref
|
||||
) => {
|
||||
return (
|
||||
<UIButton
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={buttonStyle({ variant, size, action, class: className })}
|
||||
context={{ variant, size, action }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
type IButtonTextProps = React.ComponentPropsWithoutRef<typeof UIButton.Text> &
|
||||
VariantProps<typeof buttonTextStyle> & { className?: string };
|
||||
|
||||
const ButtonText = React.forwardRef<
|
||||
React.ElementRef<typeof UIButton.Text>,
|
||||
IButtonTextProps
|
||||
>(({ className, variant, size, action, ...props }, ref) => {
|
||||
const {
|
||||
variant: parentVariant,
|
||||
size: parentSize,
|
||||
action: parentAction,
|
||||
} = useStyleContext(SCOPE);
|
||||
|
||||
return (
|
||||
<UIButton.Text
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={buttonTextStyle({
|
||||
parentVariants: {
|
||||
variant: parentVariant,
|
||||
size: parentSize,
|
||||
action: parentAction,
|
||||
},
|
||||
variant,
|
||||
size,
|
||||
action,
|
||||
class: className,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const ButtonSpinner = UIButton.Spinner;
|
||||
|
||||
type IButtonIcon = React.ComponentPropsWithoutRef<typeof UIButton.Icon> &
|
||||
VariantProps<typeof buttonIconStyle> & {
|
||||
className?: string | undefined;
|
||||
as?: React.ElementType;
|
||||
height?: number;
|
||||
width?: number;
|
||||
};
|
||||
|
||||
const ButtonIcon = React.forwardRef<
|
||||
React.ElementRef<typeof UIButton.Icon>,
|
||||
IButtonIcon
|
||||
>(({ className, size, ...props }, ref) => {
|
||||
const {
|
||||
variant: parentVariant,
|
||||
size: parentSize,
|
||||
action: parentAction,
|
||||
} = useStyleContext(SCOPE);
|
||||
|
||||
if (typeof size === 'number') {
|
||||
return (
|
||||
<UIButton.Icon
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={buttonIconStyle({ class: className })}
|
||||
size={size}
|
||||
/>
|
||||
);
|
||||
} else if (
|
||||
(props.height !== undefined || props.width !== undefined) &&
|
||||
size === undefined
|
||||
) {
|
||||
return (
|
||||
<UIButton.Icon
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={buttonIconStyle({ class: className })}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<UIButton.Icon
|
||||
{...props}
|
||||
className={buttonIconStyle({
|
||||
parentVariants: {
|
||||
size: parentSize,
|
||||
variant: parentVariant,
|
||||
action: parentAction,
|
||||
},
|
||||
size,
|
||||
class: className,
|
||||
})}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type IButtonGroupProps = React.ComponentPropsWithoutRef<typeof UIButton.Group> &
|
||||
VariantProps<typeof buttonGroupStyle>;
|
||||
|
||||
const ButtonGroup = React.forwardRef<
|
||||
React.ElementRef<typeof UIButton.Group>,
|
||||
IButtonGroupProps
|
||||
>(
|
||||
(
|
||||
{
|
||||
className,
|
||||
space = 'md',
|
||||
isAttached = false,
|
||||
flexDirection = 'column',
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
return (
|
||||
<UIButton.Group
|
||||
className={buttonGroupStyle({
|
||||
class: className,
|
||||
space,
|
||||
isAttached,
|
||||
flexDirection,
|
||||
})}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Button.displayName = 'Button';
|
||||
ButtonText.displayName = 'ButtonText';
|
||||
ButtonSpinner.displayName = 'ButtonSpinner';
|
||||
ButtonIcon.displayName = 'ButtonIcon';
|
||||
ButtonGroup.displayName = 'ButtonGroup';
|
||||
|
||||
export { Button, ButtonText, ButtonSpinner, ButtonIcon, ButtonGroup };
|
||||
@@ -0,0 +1,309 @@
|
||||
'use client'
|
||||
import { vars } from 'nativewind'
|
||||
|
||||
export const config = {
|
||||
light: vars({
|
||||
'--color-primary-0': '240 249 255',
|
||||
'--color-primary-50': '224 242 254',
|
||||
'--color-primary-100': '186 230 253',
|
||||
'--color-primary-200': '125 211 252',
|
||||
'--color-primary-300': '56 189 248',
|
||||
'--color-primary-400': '14 165 233',
|
||||
'--color-primary-500': '2 132 199',
|
||||
'--color-primary-600': '3 105 161',
|
||||
'--color-primary-700': '7 89 133',
|
||||
'--color-primary-800': '12 74 110',
|
||||
'--color-primary-900': '8 47 73',
|
||||
'--color-primary-950': '2 22 36',
|
||||
|
||||
/* Secondary */
|
||||
'--color-secondary-0': '253 253 253',
|
||||
'--color-secondary-50': '251 251 251',
|
||||
'--color-secondary-100': '246 246 246',
|
||||
'--color-secondary-200': '242 242 242',
|
||||
'--color-secondary-300': '237 237 237',
|
||||
'--color-secondary-400': '230 230 231',
|
||||
'--color-secondary-500': '217 217 219',
|
||||
'--color-secondary-600': '198 199 199',
|
||||
'--color-secondary-700': '189 189 189',
|
||||
'--color-secondary-800': '177 177 177',
|
||||
'--color-secondary-900': '165 164 164',
|
||||
'--color-secondary-950': '157 157 157',
|
||||
|
||||
/* Tertiary */
|
||||
'--color-tertiary-0': '255 250 245',
|
||||
'--color-tertiary-50': '255 242 229',
|
||||
'--color-tertiary-100': '255 233 213',
|
||||
'--color-tertiary-200': '254 209 170',
|
||||
'--color-tertiary-300': '253 180 116',
|
||||
'--color-tertiary-400': '251 157 75',
|
||||
'--color-tertiary-500': '231 129 40',
|
||||
'--color-tertiary-600': '215 117 31',
|
||||
'--color-tertiary-700': '180 98 26',
|
||||
'--color-tertiary-800': '130 73 23',
|
||||
'--color-tertiary-900': '108 61 19',
|
||||
'--color-tertiary-950': '84 49 18',
|
||||
|
||||
/* Error */
|
||||
'--color-error-0': '254 233 233',
|
||||
'--color-error-50': '254 226 226',
|
||||
'--color-error-100': '254 202 202',
|
||||
'--color-error-200': '252 165 165',
|
||||
'--color-error-300': '248 113 113',
|
||||
'--color-error-400': '239 68 68',
|
||||
'--color-error-500': '230 53 53',
|
||||
'--color-error-600': '220 38 38',
|
||||
'--color-error-700': '185 28 28',
|
||||
'--color-error-800': '153 27 27',
|
||||
'--color-error-900': '127 29 29',
|
||||
'--color-error-950': '83 19 19',
|
||||
|
||||
/* Success */
|
||||
'--color-success-0': '228 255 244',
|
||||
'--color-success-50': '202 255 232',
|
||||
'--color-success-100': '162 241 192',
|
||||
'--color-success-200': '132 211 162',
|
||||
'--color-success-300': '102 181 132',
|
||||
'--color-success-400': '72 151 102',
|
||||
'--color-success-500': '52 131 82',
|
||||
'--color-success-600': '42 121 72',
|
||||
'--color-success-700': '32 111 62',
|
||||
'--color-success-800': '22 101 52',
|
||||
'--color-success-900': '20 83 45',
|
||||
'--color-success-950': '27 50 36',
|
||||
|
||||
/* Warning */
|
||||
'--color-warning-0': '255 249 245',
|
||||
'--color-warning-50': '255 244 236',
|
||||
'--color-warning-100': '255 231 213',
|
||||
'--color-warning-200': '254 205 170',
|
||||
'--color-warning-300': '253 173 116',
|
||||
'--color-warning-400': '251 149 75',
|
||||
'--color-warning-500': '231 120 40',
|
||||
'--color-warning-600': '215 108 31',
|
||||
'--color-warning-700': '180 90 26',
|
||||
'--color-warning-800': '130 68 23',
|
||||
'--color-warning-900': '108 56 19',
|
||||
'--color-warning-950': '84 45 18',
|
||||
|
||||
/* Info */
|
||||
'--color-info-0': '236 248 254',
|
||||
'--color-info-50': '199 235 252',
|
||||
'--color-info-100': '162 221 250',
|
||||
'--color-info-200': '124 207 248',
|
||||
'--color-info-300': '87 194 246',
|
||||
'--color-info-400': '50 180 244',
|
||||
'--color-info-500': '13 166 242',
|
||||
'--color-info-600': '11 141 205',
|
||||
'--color-info-700': '9 115 168',
|
||||
'--color-info-800': '7 90 131',
|
||||
'--color-info-900': '5 64 93',
|
||||
'--color-info-950': '3 38 56',
|
||||
|
||||
/* Typography */
|
||||
'--color-typography-0': '254 254 255',
|
||||
'--color-typography-50': '245 245 245',
|
||||
'--color-typography-100': '229 229 229',
|
||||
'--color-typography-200': '219 219 220',
|
||||
'--color-typography-300': '212 212 212',
|
||||
'--color-typography-400': '163 163 163',
|
||||
'--color-typography-500': '140 140 140',
|
||||
'--color-typography-600': '115 115 115',
|
||||
'--color-typography-700': '82 82 82',
|
||||
'--color-typography-800': '64 64 64',
|
||||
'--color-typography-900': '38 38 39',
|
||||
'--color-typography-950': '23 23 23',
|
||||
|
||||
/* Outline */
|
||||
'--color-outline-0': '253 254 254',
|
||||
'--color-outline-50': '243 243 243',
|
||||
'--color-outline-100': '230 230 230',
|
||||
'--color-outline-200': '221 220 219',
|
||||
'--color-outline-300': '211 211 211',
|
||||
'--color-outline-400': '165 163 163',
|
||||
'--color-outline-500': '140 141 141',
|
||||
'--color-outline-600': '115 116 116',
|
||||
'--color-outline-700': '83 82 82',
|
||||
'--color-outline-800': '65 65 65',
|
||||
'--color-outline-900': '39 38 36',
|
||||
'--color-outline-950': '26 23 23',
|
||||
|
||||
/* Background */
|
||||
'--color-background-0': '255 255 255',
|
||||
'--color-background-50': '246 246 246',
|
||||
'--color-background-100': '242 241 241',
|
||||
'--color-background-200': '220 219 219',
|
||||
'--color-background-300': '213 212 212',
|
||||
'--color-background-400': '162 163 163',
|
||||
'--color-background-500': '142 142 142',
|
||||
'--color-background-600': '116 116 116',
|
||||
'--color-background-700': '83 82 82',
|
||||
'--color-background-800': '65 64 64',
|
||||
'--color-background-900': '39 38 37',
|
||||
'--color-background-950': '18 18 18',
|
||||
|
||||
/* Background Special */
|
||||
'--color-background-error': '254 241 241',
|
||||
'--color-background-warning': '255 243 234',
|
||||
'--color-background-success': '237 252 242',
|
||||
'--color-background-muted': '247 248 247',
|
||||
'--color-background-info': '235 248 254',
|
||||
|
||||
/* Focus Ring Indicator */
|
||||
'--color-indicator-primary': '55 55 55',
|
||||
'--color-indicator-info': '83 153 236',
|
||||
'--color-indicator-error': '185 28 28',
|
||||
}),
|
||||
dark: vars({
|
||||
'--color-primary-0': '2 22 36',
|
||||
'--color-primary-50': '8 47 73',
|
||||
'--color-primary-100': '12 74 110',
|
||||
'--color-primary-200': '7 89 133',
|
||||
'--color-primary-300': '3 105 161',
|
||||
'--color-primary-400': '2 132 199',
|
||||
'--color-primary-500': '56 189 248',
|
||||
'--color-primary-600': '125 211 252',
|
||||
'--color-primary-700': '186 230 253',
|
||||
'--color-primary-800': '224 242 254',
|
||||
'--color-primary-900': '240 249 255',
|
||||
'--color-primary-950': '248 252 255',
|
||||
|
||||
/* Secondary */
|
||||
'--color-secondary-0': '20 20 20',
|
||||
'--color-secondary-50': '23 23 23',
|
||||
'--color-secondary-100': '31 31 31',
|
||||
'--color-secondary-200': '39 39 39',
|
||||
'--color-secondary-300': '44 44 44',
|
||||
'--color-secondary-400': '56 57 57',
|
||||
'--color-secondary-500': '63 64 64',
|
||||
'--color-secondary-600': '86 86 86',
|
||||
'--color-secondary-700': '110 110 110',
|
||||
'--color-secondary-800': '135 135 135',
|
||||
'--color-secondary-900': '150 150 150',
|
||||
'--color-secondary-950': '164 164 164',
|
||||
|
||||
/* Tertiary */
|
||||
'--color-tertiary-0': '84 49 18',
|
||||
'--color-tertiary-50': '108 61 19',
|
||||
'--color-tertiary-100': '130 73 23',
|
||||
'--color-tertiary-200': '180 98 26',
|
||||
'--color-tertiary-300': '215 117 31',
|
||||
'--color-tertiary-400': '231 129 40',
|
||||
'--color-tertiary-500': '251 157 75',
|
||||
'--color-tertiary-600': '253 180 116',
|
||||
'--color-tertiary-700': '254 209 170',
|
||||
'--color-tertiary-800': '255 233 213',
|
||||
'--color-tertiary-900': '255 242 229',
|
||||
'--color-tertiary-950': '255 250 245',
|
||||
|
||||
/* Error */
|
||||
'--color-error-0': '83 19 19',
|
||||
'--color-error-50': '127 29 29',
|
||||
'--color-error-100': '153 27 27',
|
||||
'--color-error-200': '185 28 28',
|
||||
'--color-error-300': '220 38 38',
|
||||
'--color-error-400': '230 53 53',
|
||||
'--color-error-500': '239 68 68',
|
||||
'--color-error-600': '249 97 96',
|
||||
'--color-error-700': '229 91 90',
|
||||
'--color-error-800': '254 202 202',
|
||||
'--color-error-900': '254 226 226',
|
||||
'--color-error-950': '254 233 233',
|
||||
|
||||
/* Success */
|
||||
'--color-success-0': '27 50 36',
|
||||
'--color-success-50': '20 83 45',
|
||||
'--color-success-100': '22 101 52',
|
||||
'--color-success-200': '32 111 62',
|
||||
'--color-success-300': '42 121 72',
|
||||
'--color-success-400': '52 131 82',
|
||||
'--color-success-500': '72 151 102',
|
||||
'--color-success-600': '102 181 132',
|
||||
'--color-success-700': '132 211 162',
|
||||
'--color-success-800': '162 241 192',
|
||||
'--color-success-900': '202 255 232',
|
||||
'--color-success-950': '228 255 244',
|
||||
|
||||
/* Warning */
|
||||
'--color-warning-0': '84 45 18',
|
||||
'--color-warning-50': '108 56 19',
|
||||
'--color-warning-100': '130 68 23',
|
||||
'--color-warning-200': '180 90 26',
|
||||
'--color-warning-300': '215 108 31',
|
||||
'--color-warning-400': '231 120 40',
|
||||
'--color-warning-500': '251 149 75',
|
||||
'--color-warning-600': '253 173 116',
|
||||
'--color-warning-700': '254 205 170',
|
||||
'--color-warning-800': '255 231 213',
|
||||
'--color-warning-900': '255 244 237',
|
||||
'--color-warning-950': '255 249 245',
|
||||
|
||||
/* Info */
|
||||
'--color-info-0': '3 38 56',
|
||||
'--color-info-50': '5 64 93',
|
||||
'--color-info-100': '7 90 131',
|
||||
'--color-info-200': '9 115 168',
|
||||
'--color-info-300': '11 141 205',
|
||||
'--color-info-400': '13 166 242',
|
||||
'--color-info-500': '50 180 244',
|
||||
'--color-info-600': '87 194 246',
|
||||
'--color-info-700': '124 207 248',
|
||||
'--color-info-800': '162 221 250',
|
||||
'--color-info-900': '199 235 252',
|
||||
'--color-info-950': '236 248 254',
|
||||
|
||||
/* Typography */
|
||||
'--color-typography-0': '23 23 23',
|
||||
'--color-typography-50': '38 38 39',
|
||||
'--color-typography-100': '64 64 64',
|
||||
'--color-typography-200': '82 82 82',
|
||||
'--color-typography-300': '115 115 115',
|
||||
'--color-typography-400': '140 140 140',
|
||||
'--color-typography-500': '163 163 163',
|
||||
'--color-typography-600': '212 212 212',
|
||||
'--color-typography-700': '219 219 220',
|
||||
'--color-typography-800': '229 229 229',
|
||||
'--color-typography-900': '245 245 245',
|
||||
'--color-typography-950': '254 254 255',
|
||||
|
||||
/* Outline */
|
||||
'--color-outline-0': '26 23 23',
|
||||
'--color-outline-50': '39 38 36',
|
||||
'--color-outline-100': '65 65 65',
|
||||
'--color-outline-200': '83 82 82',
|
||||
'--color-outline-300': '115 116 116',
|
||||
'--color-outline-400': '140 141 141',
|
||||
'--color-outline-500': '165 163 163',
|
||||
'--color-outline-600': '211 211 211',
|
||||
'--color-outline-700': '221 220 219',
|
||||
'--color-outline-800': '230 230 230',
|
||||
'--color-outline-900': '243 243 243',
|
||||
'--color-outline-950': '253 254 254',
|
||||
|
||||
/* Background */
|
||||
'--color-background-0': '18 18 18',
|
||||
'--color-background-50': '39 38 37',
|
||||
'--color-background-100': '65 64 64',
|
||||
'--color-background-200': '83 82 82',
|
||||
'--color-background-300': '116 116 116',
|
||||
'--color-background-400': '142 142 142',
|
||||
'--color-background-500': '162 163 163',
|
||||
'--color-background-600': '213 212 212',
|
||||
'--color-background-700': '229 228 228',
|
||||
'--color-background-800': '242 241 241',
|
||||
'--color-background-900': '246 246 246',
|
||||
'--color-background-950': '255 255 255',
|
||||
|
||||
/* Background Special */
|
||||
'--color-background-error': '66 43 43',
|
||||
'--color-background-warning': '65 47 35',
|
||||
'--color-background-success': '28 43 33',
|
||||
'--color-background-muted': '51 51 51',
|
||||
'--color-background-info': '26 40 46',
|
||||
|
||||
/* Focus Ring Indicator */
|
||||
'--color-indicator-primary': '247 247 247',
|
||||
'--color-indicator-info': '161 199 245',
|
||||
'--color-indicator-error': '232 70 69',
|
||||
}),
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {},
|
||||
"devDependencies": {},
|
||||
"components": []
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// This is a Next.js 15 compatible version of the GluestackUIProvider
|
||||
'use client';
|
||||
import React, { useEffect, useLayoutEffect } from 'react';
|
||||
import { config } from './config';
|
||||
import { OverlayProvider } from '@gluestack-ui/core/overlay/creator';
|
||||
import { ToastProvider } from '@gluestack-ui/core/toast/creator';
|
||||
import { setFlushStyles } from '@gluestack-ui/utils/nativewind-utils';
|
||||
import { script } from './script';
|
||||
|
||||
const variableStyleTagId = 'nativewind-style';
|
||||
const createStyle = (styleTagId: string) => {
|
||||
const style = document.createElement('style');
|
||||
style.id = styleTagId;
|
||||
style.appendChild(document.createTextNode(''));
|
||||
return style;
|
||||
};
|
||||
|
||||
export const useSafeLayoutEffect =
|
||||
typeof window !== 'undefined' ? useLayoutEffect : useEffect;
|
||||
|
||||
export function GluestackUIProvider({
|
||||
mode = 'light',
|
||||
...props
|
||||
}: {
|
||||
mode?: 'light' | 'dark' | 'system';
|
||||
children?: React.ReactNode;
|
||||
}) {
|
||||
let cssVariablesWithMode = ``;
|
||||
Object.keys(config).forEach((configKey) => {
|
||||
cssVariablesWithMode +=
|
||||
configKey === 'dark' ? `\n .dark {\n ` : `\n:root {\n`;
|
||||
const cssVariables = Object.keys(
|
||||
config[configKey as keyof typeof config]
|
||||
).reduce((acc: string, curr: string) => {
|
||||
acc += `${curr}:${config[configKey as keyof typeof config][curr]}; `;
|
||||
return acc;
|
||||
}, '');
|
||||
cssVariablesWithMode += `${cssVariables} \n}`;
|
||||
});
|
||||
|
||||
setFlushStyles(cssVariablesWithMode);
|
||||
|
||||
const handleMediaQuery = React.useCallback((e: MediaQueryListEvent) => {
|
||||
script(e.matches ? 'dark' : 'light');
|
||||
}, []);
|
||||
|
||||
useSafeLayoutEffect(() => {
|
||||
if (mode !== 'system') {
|
||||
const documentElement = document.documentElement;
|
||||
if (documentElement) {
|
||||
documentElement.classList.add(mode);
|
||||
documentElement.classList.remove(mode === 'light' ? 'dark' : 'light');
|
||||
documentElement.style.colorScheme = mode;
|
||||
}
|
||||
}
|
||||
}, [mode]);
|
||||
|
||||
useSafeLayoutEffect(() => {
|
||||
if (mode !== 'system') return;
|
||||
const media = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
media.addListener(handleMediaQuery);
|
||||
|
||||
return () => media.removeListener(handleMediaQuery);
|
||||
}, [handleMediaQuery]);
|
||||
|
||||
useSafeLayoutEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const documentElement = document.documentElement;
|
||||
if (documentElement) {
|
||||
const head = documentElement.querySelector('head');
|
||||
let style = head?.querySelector(`[id='${variableStyleTagId}']`);
|
||||
if (!style) {
|
||||
style = createStyle(variableStyleTagId);
|
||||
style.innerHTML = cssVariablesWithMode;
|
||||
if (head) head.appendChild(style);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<OverlayProvider>
|
||||
<ToastProvider>{props.children}</ToastProvider>
|
||||
</OverlayProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { ViewProps } from 'react-native'
|
||||
import { OverlayProvider } from '@gluestack-ui/core/overlay/creator'
|
||||
import { ToastProvider } from '@gluestack-ui/core/toast/creator'
|
||||
import { useColorScheme } from 'nativewind'
|
||||
import * as React from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import { View } from 'react-native'
|
||||
import { config } from './config'
|
||||
|
||||
export type ModeType = 'light' | 'dark' | 'system'
|
||||
|
||||
export function GluestackUIProvider({
|
||||
mode = 'light',
|
||||
...props
|
||||
}: {
|
||||
mode?: ModeType
|
||||
children?: React.ReactNode
|
||||
style?: ViewProps['style']
|
||||
}) {
|
||||
const { colorScheme, setColorScheme } = useColorScheme()
|
||||
|
||||
useEffect(() => {
|
||||
setColorScheme(mode)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [mode])
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
config[colorScheme!],
|
||||
{ flex: 1, height: '100%', width: '100%' },
|
||||
props.style,
|
||||
]}
|
||||
>
|
||||
<OverlayProvider>
|
||||
<ToastProvider>{props.children}</ToastProvider>
|
||||
</OverlayProvider>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
'use client';
|
||||
import React, { useEffect, useLayoutEffect } from 'react';
|
||||
import { config } from './config';
|
||||
import { OverlayProvider } from '@gluestack-ui/core/overlay/creator';
|
||||
import { ToastProvider } from '@gluestack-ui/core/toast/creator';
|
||||
import { setFlushStyles } from '@gluestack-ui/utils/nativewind-utils';
|
||||
import { script } from './script';
|
||||
|
||||
export type ModeType = 'light' | 'dark' | 'system';
|
||||
|
||||
const variableStyleTagId = 'nativewind-style';
|
||||
const createStyle = (styleTagId: string) => {
|
||||
const style = document.createElement('style');
|
||||
style.id = styleTagId;
|
||||
style.appendChild(document.createTextNode(''));
|
||||
return style;
|
||||
};
|
||||
|
||||
export const useSafeLayoutEffect =
|
||||
typeof window !== 'undefined' ? useLayoutEffect : useEffect;
|
||||
|
||||
export function GluestackUIProvider({
|
||||
mode = 'light',
|
||||
...props
|
||||
}: {
|
||||
mode?: ModeType;
|
||||
children?: React.ReactNode;
|
||||
}) {
|
||||
let cssVariablesWithMode = ``;
|
||||
Object.keys(config).forEach((configKey) => {
|
||||
cssVariablesWithMode +=
|
||||
configKey === 'dark' ? `\n .dark {\n ` : `\n:root {\n`;
|
||||
const cssVariables = Object.keys(
|
||||
config[configKey as keyof typeof config]
|
||||
).reduce((acc: string, curr: string) => {
|
||||
acc += `${curr}:${config[configKey as keyof typeof config][curr]}; `;
|
||||
return acc;
|
||||
}, '');
|
||||
cssVariablesWithMode += `${cssVariables} \n}`;
|
||||
});
|
||||
|
||||
setFlushStyles(cssVariablesWithMode);
|
||||
|
||||
const handleMediaQuery = React.useCallback((e: MediaQueryListEvent) => {
|
||||
script(e.matches ? 'dark' : 'light');
|
||||
}, []);
|
||||
|
||||
useSafeLayoutEffect(() => {
|
||||
if (mode !== 'system') {
|
||||
const documentElement = document.documentElement;
|
||||
if (documentElement) {
|
||||
documentElement.classList.add(mode);
|
||||
documentElement.classList.remove(mode === 'light' ? 'dark' : 'light');
|
||||
documentElement.style.colorScheme = mode;
|
||||
}
|
||||
}
|
||||
}, [mode]);
|
||||
|
||||
useSafeLayoutEffect(() => {
|
||||
if (mode !== 'system') return;
|
||||
const media = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
media.addListener(handleMediaQuery);
|
||||
|
||||
return () => media.removeListener(handleMediaQuery);
|
||||
}, [handleMediaQuery]);
|
||||
|
||||
useSafeLayoutEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const documentElement = document.documentElement;
|
||||
if (documentElement) {
|
||||
const head = documentElement.querySelector('head');
|
||||
let style = head?.querySelector(`[id='${variableStyleTagId}']`);
|
||||
if (!style) {
|
||||
style = createStyle(variableStyleTagId);
|
||||
style.innerHTML = cssVariablesWithMode;
|
||||
if (head) head.appendChild(style);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<script
|
||||
suppressHydrationWarning
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `(${script.toString()})('${mode}')`,
|
||||
}}
|
||||
/>
|
||||
<OverlayProvider>
|
||||
<ToastProvider>{props.children}</ToastProvider>
|
||||
</OverlayProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
export const script = (mode: string) => {
|
||||
const documentElement = document.documentElement;
|
||||
|
||||
function getSystemColorMode() {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? 'dark'
|
||||
: 'light';
|
||||
}
|
||||
|
||||
try {
|
||||
const isSystem = mode === 'system';
|
||||
const theme = isSystem ? getSystemColorMode() : mode;
|
||||
documentElement.classList.remove(theme === 'light' ? 'dark' : 'light');
|
||||
documentElement.classList.add(theme);
|
||||
documentElement.style.colorScheme = theme;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
import { useColorScheme } from "react-native";
|
||||
import { useAppStore } from "@/store/app";
|
||||
|
||||
function useIsDark() {
|
||||
const systemScheme = useColorScheme(); // 'light' | 'dark'
|
||||
const themeScheme = useAppStore((state) => state.themeScheme);
|
||||
|
||||
if (themeScheme === "system") {
|
||||
return systemScheme === "dark";
|
||||
}
|
||||
|
||||
return themeScheme === "dark";
|
||||
}
|
||||
|
||||
export { useIsDark };
|
||||
@@ -0,0 +1,21 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useColorScheme as useRNColorScheme } from 'react-native';
|
||||
|
||||
/**
|
||||
* To support static rendering, this value needs to be re-calculated on the client side for web
|
||||
*/
|
||||
export function useColorScheme() {
|
||||
const [hasHydrated, setHasHydrated] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setHasHydrated(true);
|
||||
}, []);
|
||||
|
||||
const colorScheme = useRNColorScheme();
|
||||
|
||||
if (hasHydrated) {
|
||||
return colorScheme;
|
||||
}
|
||||
|
||||
return 'light';
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Learn more about light and dark modes:
|
||||
* https://docs.expo.dev/guides/color-schemes/
|
||||
*/
|
||||
|
||||
import { Colors } from "@/constants/theme";
|
||||
import { useColorScheme } from "@/hooks/use-color-scheme";
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { create } from "zustand";
|
||||
|
||||
export type ThemeScheme = "light" | "dark" | "system";
|
||||
|
||||
interface AppState {
|
||||
themeScheme: ThemeScheme;
|
||||
setThemeScheme: (theme: ThemeScheme) => void;
|
||||
}
|
||||
|
||||
export const useAppStore = create<AppState>((set) => ({
|
||||
themeScheme: "system",
|
||||
setThemeScheme: (theme) => set({ themeScheme: theme }),
|
||||
}));
|
||||
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
export {}
|
||||
|
||||
declare global {
|
||||
type ThemeScheme = 'light' | 'dark' | 'system'
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
darkMode: process.env.DARK_MODE ? process.env.DARK_MODE : "class",
|
||||
content: [
|
||||
"./app/**/*.{html,js,jsx,ts,tsx,mdx}",
|
||||
"./components/**/*.{html,js,jsx,ts,tsx,mdx}",
|
||||
"./utils/**/*.{html,js,jsx,ts,tsx,mdx}",
|
||||
"./*.{html,js,jsx,ts,tsx,mdx}",
|
||||
"./src/**/*.{html,js,jsx,ts,tsx,mdx}",
|
||||
],
|
||||
presets: [require("nativewind/preset")],
|
||||
important: "html",
|
||||
safelist: [
|
||||
{
|
||||
pattern:
|
||||
// eslint-disable-next-line regexp/no-dupe-disjunctions
|
||||
/(bg|border|text|stroke|fill)-(primary|secondary|tertiary|error|success|warning|info|typography|outline|background|indicator)-(0|50|100|200|300|400|500|600|700|800|900|950|white|gray|black|error|warning|muted|success|info|light|dark|primary)/,
|
||||
},
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
0: "rgb(var(--color-primary-0)/<alpha-value>)",
|
||||
50: "rgb(var(--color-primary-50)/<alpha-value>)",
|
||||
100: "rgb(var(--color-primary-100)/<alpha-value>)",
|
||||
200: "rgb(var(--color-primary-200)/<alpha-value>)",
|
||||
300: "rgb(var(--color-primary-300)/<alpha-value>)",
|
||||
400: "rgb(var(--color-primary-400)/<alpha-value>)",
|
||||
500: "rgb(var(--color-primary-500)/<alpha-value>)",
|
||||
600: "rgb(var(--color-primary-600)/<alpha-value>)",
|
||||
700: "rgb(var(--color-primary-700)/<alpha-value>)",
|
||||
800: "rgb(var(--color-primary-800)/<alpha-value>)",
|
||||
900: "rgb(var(--color-primary-900)/<alpha-value>)",
|
||||
950: "rgb(var(--color-primary-950)/<alpha-value>)",
|
||||
},
|
||||
secondary: {
|
||||
0: "rgb(var(--color-secondary-0)/<alpha-value>)",
|
||||
50: "rgb(var(--color-secondary-50)/<alpha-value>)",
|
||||
100: "rgb(var(--color-secondary-100)/<alpha-value>)",
|
||||
200: "rgb(var(--color-secondary-200)/<alpha-value>)",
|
||||
300: "rgb(var(--color-secondary-300)/<alpha-value>)",
|
||||
400: "rgb(var(--color-secondary-400)/<alpha-value>)",
|
||||
500: "rgb(var(--color-secondary-500)/<alpha-value>)",
|
||||
600: "rgb(var(--color-secondary-600)/<alpha-value>)",
|
||||
700: "rgb(var(--color-secondary-700)/<alpha-value>)",
|
||||
800: "rgb(var(--color-secondary-800)/<alpha-value>)",
|
||||
900: "rgb(var(--color-secondary-900)/<alpha-value>)",
|
||||
950: "rgb(var(--color-secondary-950)/<alpha-value>)",
|
||||
},
|
||||
tertiary: {
|
||||
50: "rgb(var(--color-tertiary-50)/<alpha-value>)",
|
||||
100: "rgb(var(--color-tertiary-100)/<alpha-value>)",
|
||||
200: "rgb(var(--color-tertiary-200)/<alpha-value>)",
|
||||
300: "rgb(var(--color-tertiary-300)/<alpha-value>)",
|
||||
400: "rgb(var(--color-tertiary-400)/<alpha-value>)",
|
||||
500: "rgb(var(--color-tertiary-500)/<alpha-value>)",
|
||||
600: "rgb(var(--color-tertiary-600)/<alpha-value>)",
|
||||
700: "rgb(var(--color-tertiary-700)/<alpha-value>)",
|
||||
800: "rgb(var(--color-tertiary-800)/<alpha-value>)",
|
||||
900: "rgb(var(--color-tertiary-900)/<alpha-value>)",
|
||||
950: "rgb(var(--color-tertiary-950)/<alpha-value>)",
|
||||
},
|
||||
error: {
|
||||
0: "rgb(var(--color-error-0)/<alpha-value>)",
|
||||
50: "rgb(var(--color-error-50)/<alpha-value>)",
|
||||
100: "rgb(var(--color-error-100)/<alpha-value>)",
|
||||
200: "rgb(var(--color-error-200)/<alpha-value>)",
|
||||
300: "rgb(var(--color-error-300)/<alpha-value>)",
|
||||
400: "rgb(var(--color-error-400)/<alpha-value>)",
|
||||
500: "rgb(var(--color-error-500)/<alpha-value>)",
|
||||
600: "rgb(var(--color-error-600)/<alpha-value>)",
|
||||
700: "rgb(var(--color-error-700)/<alpha-value>)",
|
||||
800: "rgb(var(--color-error-800)/<alpha-value>)",
|
||||
900: "rgb(var(--color-error-900)/<alpha-value>)",
|
||||
950: "rgb(var(--color-error-950)/<alpha-value>)",
|
||||
},
|
||||
success: {
|
||||
0: "rgb(var(--color-success-0)/<alpha-value>)",
|
||||
50: "rgb(var(--color-success-50)/<alpha-value>)",
|
||||
100: "rgb(var(--color-success-100)/<alpha-value>)",
|
||||
200: "rgb(var(--color-success-200)/<alpha-value>)",
|
||||
300: "rgb(var(--color-success-300)/<alpha-value>)",
|
||||
400: "rgb(var(--color-success-400)/<alpha-value>)",
|
||||
500: "rgb(var(--color-success-500)/<alpha-value>)",
|
||||
600: "rgb(var(--color-success-600)/<alpha-value>)",
|
||||
700: "rgb(var(--color-success-700)/<alpha-value>)",
|
||||
800: "rgb(var(--color-success-800)/<alpha-value>)",
|
||||
900: "rgb(var(--color-success-900)/<alpha-value>)",
|
||||
950: "rgb(var(--color-success-950)/<alpha-value>)",
|
||||
},
|
||||
warning: {
|
||||
0: "rgb(var(--color-warning-0)/<alpha-value>)",
|
||||
50: "rgb(var(--color-warning-50)/<alpha-value>)",
|
||||
100: "rgb(var(--color-warning-100)/<alpha-value>)",
|
||||
200: "rgb(var(--color-warning-200)/<alpha-value>)",
|
||||
300: "rgb(var(--color-warning-300)/<alpha-value>)",
|
||||
400: "rgb(var(--color-warning-400)/<alpha-value>)",
|
||||
500: "rgb(var(--color-warning-500)/<alpha-value>)",
|
||||
600: "rgb(var(--color-warning-600)/<alpha-value>)",
|
||||
700: "rgb(var(--color-warning-700)/<alpha-value>)",
|
||||
800: "rgb(var(--color-warning-800)/<alpha-value>)",
|
||||
900: "rgb(var(--color-warning-900)/<alpha-value>)",
|
||||
950: "rgb(var(--color-warning-950)/<alpha-value>)",
|
||||
},
|
||||
info: {
|
||||
0: "rgb(var(--color-info-0)/<alpha-value>)",
|
||||
50: "rgb(var(--color-info-50)/<alpha-value>)",
|
||||
100: "rgb(var(--color-info-100)/<alpha-value>)",
|
||||
200: "rgb(var(--color-info-200)/<alpha-value>)",
|
||||
300: "rgb(var(--color-info-300)/<alpha-value>)",
|
||||
400: "rgb(var(--color-info-400)/<alpha-value>)",
|
||||
500: "rgb(var(--color-info-500)/<alpha-value>)",
|
||||
600: "rgb(var(--color-info-600)/<alpha-value>)",
|
||||
700: "rgb(var(--color-info-700)/<alpha-value>)",
|
||||
800: "rgb(var(--color-info-800)/<alpha-value>)",
|
||||
900: "rgb(var(--color-info-900)/<alpha-value>)",
|
||||
950: "rgb(var(--color-info-950)/<alpha-value>)",
|
||||
},
|
||||
typography: {
|
||||
0: "rgb(var(--color-typography-0)/<alpha-value>)",
|
||||
50: "rgb(var(--color-typography-50)/<alpha-value>)",
|
||||
100: "rgb(var(--color-typography-100)/<alpha-value>)",
|
||||
200: "rgb(var(--color-typography-200)/<alpha-value>)",
|
||||
300: "rgb(var(--color-typography-300)/<alpha-value>)",
|
||||
400: "rgb(var(--color-typography-400)/<alpha-value>)",
|
||||
500: "rgb(var(--color-typography-500)/<alpha-value>)",
|
||||
600: "rgb(var(--color-typography-600)/<alpha-value>)",
|
||||
700: "rgb(var(--color-typography-700)/<alpha-value>)",
|
||||
800: "rgb(var(--color-typography-800)/<alpha-value>)",
|
||||
900: "rgb(var(--color-typography-900)/<alpha-value>)",
|
||||
950: "rgb(var(--color-typography-950)/<alpha-value>)",
|
||||
white: "#FFFFFF",
|
||||
gray: "#D4D4D4",
|
||||
black: "#181718",
|
||||
},
|
||||
outline: {
|
||||
0: "rgb(var(--color-outline-0)/<alpha-value>)",
|
||||
50: "rgb(var(--color-outline-50)/<alpha-value>)",
|
||||
100: "rgb(var(--color-outline-100)/<alpha-value>)",
|
||||
200: "rgb(var(--color-outline-200)/<alpha-value>)",
|
||||
300: "rgb(var(--color-outline-300)/<alpha-value>)",
|
||||
400: "rgb(var(--color-outline-400)/<alpha-value>)",
|
||||
500: "rgb(var(--color-outline-500)/<alpha-value>)",
|
||||
600: "rgb(var(--color-outline-600)/<alpha-value>)",
|
||||
700: "rgb(var(--color-outline-700)/<alpha-value>)",
|
||||
800: "rgb(var(--color-outline-800)/<alpha-value>)",
|
||||
900: "rgb(var(--color-outline-900)/<alpha-value>)",
|
||||
950: "rgb(var(--color-outline-950)/<alpha-value>)",
|
||||
},
|
||||
background: {
|
||||
0: "rgb(var(--color-background-0)/<alpha-value>)",
|
||||
50: "rgb(var(--color-background-50)/<alpha-value>)",
|
||||
100: "rgb(var(--color-background-100)/<alpha-value>)",
|
||||
200: "rgb(var(--color-background-200)/<alpha-value>)",
|
||||
300: "rgb(var(--color-background-300)/<alpha-value>)",
|
||||
400: "rgb(var(--color-background-400)/<alpha-value>)",
|
||||
500: "rgb(var(--color-background-500)/<alpha-value>)",
|
||||
600: "rgb(var(--color-background-600)/<alpha-value>)",
|
||||
700: "rgb(var(--color-background-700)/<alpha-value>)",
|
||||
800: "rgb(var(--color-background-800)/<alpha-value>)",
|
||||
900: "rgb(var(--color-background-900)/<alpha-value>)",
|
||||
950: "rgb(var(--color-background-950)/<alpha-value>)",
|
||||
error: "rgb(var(--color-background-error)/<alpha-value>)",
|
||||
warning: "rgb(var(--color-background-warning)/<alpha-value>)",
|
||||
muted: "rgb(var(--color-background-muted)/<alpha-value>)",
|
||||
success: "rgb(var(--color-background-success)/<alpha-value>)",
|
||||
info: "rgb(var(--color-background-info)/<alpha-value>)",
|
||||
light: "#FBFBFB",
|
||||
dark: "#181719",
|
||||
},
|
||||
indicator: {
|
||||
primary: "rgb(var(--color-indicator-primary)/<alpha-value>)",
|
||||
info: "rgb(var(--color-indicator-info)/<alpha-value>)",
|
||||
error: "rgb(var(--color-indicator-error)/<alpha-value>)",
|
||||
},
|
||||
},
|
||||
fontFamily: {
|
||||
heading: undefined,
|
||||
body: undefined,
|
||||
mono: undefined,
|
||||
jakarta: ["var(--font-plus-jakarta-sans)"],
|
||||
roboto: ["var(--font-roboto)"],
|
||||
code: ["var(--font-source-code-pro)"],
|
||||
inter: ["var(--font-inter)"],
|
||||
"space-mono": ["var(--font-space-mono)"],
|
||||
},
|
||||
fontWeight: {
|
||||
extrablack: "950",
|
||||
},
|
||||
fontSize: {
|
||||
"2xs": "10px",
|
||||
},
|
||||
boxShadow: {
|
||||
"hard-1": "-2px 2px 8px 0px rgba(38, 38, 38, 0.20)",
|
||||
"hard-2": "0px 3px 10px 0px rgba(38, 38, 38, 0.20)",
|
||||
"hard-3": "2px 2px 8px 0px rgba(38, 38, 38, 0.20)",
|
||||
"hard-4": "0px -3px 10px 0px rgba(38, 38, 38, 0.20)",
|
||||
"hard-5": "0px 2px 10px 0px rgba(38, 38, 38, 0.10)",
|
||||
"soft-1": "0px 0px 10px rgba(38, 38, 38, 0.1)",
|
||||
"soft-2": "0px 0px 20px rgba(38, 38, 38, 0.2)",
|
||||
"soft-3": "0px 0px 30px rgba(38, 38, 38, 0.1)",
|
||||
"soft-4": "0px 0px 40px rgba(38, 38, 38, 0.1)",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -15,6 +15,7 @@
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".expo/types/**/*.ts",
|
||||
"expo-env.d.ts"
|
||||
"expo-env.d.ts",
|
||||
"nativewind-env.d.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user