u
No files matched your search
@@ -1,34 +1,13 @@
|
||||
import { useEffect } from 'react'
|
||||
import { Appearance, Platform, StyleSheet, Text, View } from 'react-native'
|
||||
|
||||
import { Button, ButtonIcon, ButtonSpinner, ButtonText } from '@/components/ui/button'
|
||||
import { useAppStore } from '@/store/app'
|
||||
import { View } from 'react-native'
|
||||
import { Button, ButtonText } from '@/components/ui/button'
|
||||
// import { useAppStore } from '@/store/app'
|
||||
|
||||
export default function HomeScreen() {
|
||||
const appStore = useAppStore()
|
||||
|
||||
function handleSwitchMode() {
|
||||
if (appStore.themeScheme === 'dark') {
|
||||
appStore.setThemeScheme('light')
|
||||
}
|
||||
else if (appStore.themeScheme === 'light') {
|
||||
appStore.setThemeScheme('dark')
|
||||
}
|
||||
}
|
||||
|
||||
// const themeScheme = useAppStore(s => s.themeScheme)
|
||||
// const setThemeScheme = useAppStore(s => s.setThemeScheme)
|
||||
|
||||
return (
|
||||
<View className="flex-1 items-center justify-center dark:bg-black">
|
||||
<Button
|
||||
action="primary"
|
||||
size="sm"
|
||||
isDisabled={false}
|
||||
onPress={() => {
|
||||
handleSwitchMode()
|
||||
}}
|
||||
>
|
||||
<ButtonText>Hello World</ButtonText>
|
||||
</Button>
|
||||
<View className="flex-1 items-center justify-center gap-4 dark:bg-black">
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -2,31 +2,26 @@ import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native
|
||||
import { Stack } from 'expo-router'
|
||||
import { StatusBar } from 'expo-status-bar'
|
||||
import { useEffect } from 'react'
|
||||
import { Appearance } from 'react-native'
|
||||
import { GluestackUIProvider } from '@/components/ui/gluestack-ui-provider'
|
||||
import { useIsDark } from '@/hooks/use-color-scheme'
|
||||
import { useColorScheme } from '@/hooks/use-color-scheme'
|
||||
import 'react-native-reanimated'
|
||||
import '@/styles/global.css'
|
||||
|
||||
export default function RootLayout() {
|
||||
const isDark = useIsDark()
|
||||
const colorScheme = useColorScheme()
|
||||
|
||||
useEffect(() => {
|
||||
const sub = Appearance.addChangeListener(({ colorScheme }) => {
|
||||
console.log('system changed:', colorScheme)
|
||||
})
|
||||
|
||||
return () => sub.remove()
|
||||
}, [])
|
||||
console.log(colorScheme)
|
||||
}, [colorScheme])
|
||||
|
||||
return (
|
||||
<GluestackUIProvider mode={isDark ? 'dark' : 'light'}>
|
||||
<ThemeProvider value={isDark ? DarkTheme : DefaultTheme}>
|
||||
<GluestackUIProvider mode={colorScheme === 'dark' ? 'dark' : 'light'}>
|
||||
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
|
||||
<Stack>
|
||||
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="modal" options={{ presentation: 'modal', title: 'Modal' }} />
|
||||
</Stack>
|
||||
<StatusBar style={isDark ? 'light' : 'dark'} />
|
||||
<StatusBar style="auto" />
|
||||
</ThemeProvider>
|
||||
</GluestackUIProvider>
|
||||
)
|
||||
|
||||
@@ -1,16 +1 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Appearance, useColorScheme } from 'react-native'
|
||||
import { useAppStore } from '@/store/app'
|
||||
|
||||
function useIsDark() {
|
||||
const system = useColorScheme()
|
||||
const themeScheme = useAppStore(s => s.themeScheme)
|
||||
|
||||
if (themeScheme === 'auto') {
|
||||
return system === 'dark'
|
||||
}
|
||||
|
||||
return themeScheme === 'dark'
|
||||
}
|
||||
|
||||
export { useColorScheme, useIsDark }
|
||||
export { useColorScheme } from 'react-native';
|
||||
@@ -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';
|
||||
}
|
||||
@@ -2,22 +2,20 @@ import AsyncStorage from '@react-native-async-storage/async-storage'
|
||||
import { create } from 'zustand'
|
||||
import { createJSONStorage, persist } from 'zustand/middleware'
|
||||
|
||||
export type ThemeScheme = 'light' | 'dark' | 'auto'
|
||||
|
||||
interface AppState {
|
||||
themeScheme: ThemeScheme
|
||||
setThemeScheme: (theme: ThemeScheme) => void
|
||||
}
|
||||
|
||||
export const useAppStore = create<AppState>()(
|
||||
persist(
|
||||
set => ({
|
||||
themeScheme: 'auto',
|
||||
setThemeScheme: theme => set({ themeScheme: theme }),
|
||||
}),
|
||||
{
|
||||
name: 'app-storage',
|
||||
storage: createJSONStorage(() => AsyncStorage),
|
||||
},
|
||||
),
|
||||
)
|
||||
// export const useAppStore = create<AppState>()(
|
||||
// persist(
|
||||
// set => ({
|
||||
// themeScheme: 'system',
|
||||
// setThemeScheme: theme => set({ themeScheme: theme }),
|
||||
// }),
|
||||
// {
|
||||
// name: 'app-storage',
|
||||
// storage: createJSONStorage(() => AsyncStorage),
|
||||
// },
|
||||
// ),
|
||||
// )
|
||||
@@ -1,5 +1,5 @@
|
||||
export {}
|
||||
|
||||
declare global {
|
||||
type ThemeScheme = 'light' | 'dark' | 'auto'
|
||||
type ThemeScheme = 'light' | 'dark' | 'system'
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# Expo
|
||||
.expo/
|
||||
dist/
|
||||
web-build/
|
||||
expo-env.d.ts
|
||||
|
||||
# Native
|
||||
.kotlin/
|
||||
*.orig.*
|
||||
*.jks
|
||||
*.p8
|
||||
*.p12
|
||||
*.key
|
||||
*.mobileprovision
|
||||
|
||||
# Metro
|
||||
.metro-health-check*
|
||||
|
||||
# debug
|
||||
npm-debug.*
|
||||
yarn-debug.*
|
||||
yarn-error.*
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
|
||||
app-example
|
||||
|
||||
# generated native folders
|
||||
/ios
|
||||
/android
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
||||
"ignorePatterns": []
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{ "recommendations": ["expo.vscode-expo-tools"] }
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"oxc.fmt.configPath": ".oxfmtrc.json",
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode",
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
# Welcome to your Expo app 👋
|
||||
|
||||
This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app).
|
||||
|
||||
## Get started
|
||||
|
||||
1. Install dependencies
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Start the app
|
||||
|
||||
```bash
|
||||
npx expo start
|
||||
```
|
||||
|
||||
In the output, you'll find options to open the app in a
|
||||
|
||||
- [development build](https://docs.expo.dev/develop/development-builds/introduction/)
|
||||
- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/)
|
||||
- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/)
|
||||
- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo
|
||||
|
||||
You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction).
|
||||
|
||||
## Get a fresh project
|
||||
|
||||
When you're ready, run:
|
||||
|
||||
```bash
|
||||
npm run reset-project
|
||||
```
|
||||
|
||||
This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing.
|
||||
|
||||
## Learn more
|
||||
|
||||
To learn more about developing your project with Expo, look at the following resources:
|
||||
|
||||
- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides).
|
||||
- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
|
||||
|
||||
## Join the community
|
||||
|
||||
Join our community of developers creating universal apps.
|
||||
|
||||
- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute.
|
||||
- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions.
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"expo": {
|
||||
"name": "app-rn2",
|
||||
"slug": "app-rn2",
|
||||
"version": "1.0.0",
|
||||
"orientation": "portrait",
|
||||
"icon": "./assets/images/icon.png",
|
||||
"scheme": "apprn2",
|
||||
"userInterfaceStyle": "automatic",
|
||||
"newArchEnabled": true,
|
||||
"ios": {
|
||||
"supportsTablet": true
|
||||
},
|
||||
"android": {
|
||||
"adaptiveIcon": {
|
||||
"backgroundColor": "#E6F4FE",
|
||||
"foregroundImage": "./assets/images/android-icon-foreground.png",
|
||||
"backgroundImage": "./assets/images/android-icon-background.png",
|
||||
"monochromeImage": "./assets/images/android-icon-monochrome.png"
|
||||
},
|
||||
"edgeToEdgeEnabled": true,
|
||||
"predictiveBackGestureEnabled": false
|
||||
},
|
||||
"web": {
|
||||
"bundler": "metro",
|
||||
"output": "static",
|
||||
"favicon": "./assets/images/favicon.png"
|
||||
},
|
||||
"plugins": [
|
||||
"expo-router",
|
||||
[
|
||||
"expo-splash-screen",
|
||||
{
|
||||
"image": "./assets/images/splash-icon.png",
|
||||
"imageWidth": 200,
|
||||
"resizeMode": "contain",
|
||||
"backgroundColor": "#ffffff",
|
||||
"dark": {
|
||||
"backgroundColor": "#000000"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"experiments": {
|
||||
"typedRoutes": true,
|
||||
"reactCompiler": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useColorScheme } from "@/hooks/use-color-scheme";
|
||||
import { Stack } from "expo-router";
|
||||
import { useEffect } from "react";
|
||||
import "@/styles/global.css";
|
||||
|
||||
export default function RootLayout() {
|
||||
const colorScheme = useColorScheme();
|
||||
|
||||
useEffect(() => {
|
||||
console.log(colorScheme);
|
||||
}, [colorScheme]);
|
||||
|
||||
return <Stack />;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Text, View } from "react-native";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<View className="flex-1 items-center justify-center bg-white dark:bg-black">
|
||||
<Text className="text-xl font-bold text-blue-500 dark:text-white">Welcome to Nativewind!</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 384 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,6 @@
|
||||
module.exports = function (api) {
|
||||
api.cache(true);
|
||||
return {
|
||||
presets: [["babel-preset-expo", { jsxImportSource: "nativewind" }], "nativewind/babel"],
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { useColorScheme } from "react-native";
|
||||
@@ -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,6 @@
|
||||
const { getDefaultConfig } = require("expo/metro-config");
|
||||
const { withNativeWind } = require("nativewind/metro");
|
||||
|
||||
const config = getDefaultConfig(__dirname);
|
||||
|
||||
module.exports = withNativeWind(config, { input: "./styles/global.css" });
|
||||
@@ -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.
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "app-rn2",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"main": "expo-router/entry",
|
||||
"scripts": {
|
||||
"start": "expo start",
|
||||
"reset-project": "node ./scripts/reset-project.js",
|
||||
"android": "expo start --android",
|
||||
"ios": "expo start --ios",
|
||||
"web": "expo start --web",
|
||||
"lint": "expo lint",
|
||||
"fmt": "oxfmt",
|
||||
"fmt:check": "oxfmt --check"
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/vector-icons": "^15.0.3",
|
||||
"@react-navigation/bottom-tabs": "^7.4.0",
|
||||
"@react-navigation/elements": "^2.6.3",
|
||||
"@react-navigation/native": "^7.1.8",
|
||||
"expo": "~54.0.33",
|
||||
"expo-constants": "~18.0.13",
|
||||
"expo-font": "~14.0.11",
|
||||
"expo-haptics": "~15.0.8",
|
||||
"expo-image": "~3.0.11",
|
||||
"expo-linking": "~8.0.11",
|
||||
"expo-router": "~6.0.23",
|
||||
"expo-splash-screen": "~31.0.13",
|
||||
"expo-status-bar": "~3.0.9",
|
||||
"expo-symbols": "~1.0.8",
|
||||
"expo-system-ui": "~6.0.9",
|
||||
"expo-web-browser": "~15.0.10",
|
||||
"nativewind": "^4.2.3",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react-native": "0.81.5",
|
||||
"react-native-css-interop": "^0.2.3",
|
||||
"react-native-gesture-handler": "~2.28.0",
|
||||
"react-native-reanimated": "~4.1.1",
|
||||
"react-native-safe-area-context": "~5.6.0",
|
||||
"react-native-screens": "~4.16.0",
|
||||
"react-native-svg": "15.12.1",
|
||||
"react-native-web": "~0.21.0",
|
||||
"react-native-worklets": "0.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "~19.1.0",
|
||||
"babel-preset-expo": "^55.0.16",
|
||||
"oxfmt": "^0.44.0",
|
||||
"prettier-plugin-tailwindcss": "^0.5.14",
|
||||
"tailwindcss": "^3.4.19",
|
||||
"typescript": "~5.9.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -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)",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": "expo/tsconfig.base",
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".expo/types/**/*.ts",
|
||||
"expo-env.d.ts",
|
||||
"nativewind-env.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# Expo
|
||||
.expo/
|
||||
dist/
|
||||
web-build/
|
||||
expo-env.d.ts
|
||||
|
||||
# Native
|
||||
.kotlin/
|
||||
*.orig.*
|
||||
*.jks
|
||||
*.p8
|
||||
*.p12
|
||||
*.key
|
||||
*.mobileprovision
|
||||
|
||||
# Metro
|
||||
.metro-health-check*
|
||||
|
||||
# debug
|
||||
npm-debug.*
|
||||
yarn-debug.*
|
||||
yarn-error.*
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
|
||||
app-example
|
||||
|
||||
# generated native folders
|
||||
/ios
|
||||
/android
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
||||
"ignorePatterns": []
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{ "recommendations": ["expo.vscode-expo-tools"] }
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"oxc.fmt.configPath": ".oxfmtrc.json",
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode",
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
# Welcome to your Expo app 👋
|
||||
|
||||
This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app).
|
||||
|
||||
## Get started
|
||||
|
||||
1. Install dependencies
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Start the app
|
||||
|
||||
```bash
|
||||
npx expo start
|
||||
```
|
||||
|
||||
In the output, you'll find options to open the app in a
|
||||
|
||||
- [development build](https://docs.expo.dev/develop/development-builds/introduction/)
|
||||
- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/)
|
||||
- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/)
|
||||
- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo
|
||||
|
||||
You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction).
|
||||
|
||||
## Get a fresh project
|
||||
|
||||
When you're ready, run:
|
||||
|
||||
```bash
|
||||
npm run reset-project
|
||||
```
|
||||
|
||||
This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing.
|
||||
|
||||
## Learn more
|
||||
|
||||
To learn more about developing your project with Expo, look at the following resources:
|
||||
|
||||
- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides).
|
||||
- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
|
||||
|
||||
## Join the community
|
||||
|
||||
Join our community of developers creating universal apps.
|
||||
|
||||
- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute.
|
||||
- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions.
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"expo": {
|
||||
"name": "app-rn3",
|
||||
"slug": "app-rn3",
|
||||
"version": "1.0.0",
|
||||
"orientation": "portrait",
|
||||
"icon": "./assets/images/icon.png",
|
||||
"scheme": "apprn3",
|
||||
"userInterfaceStyle": "automatic",
|
||||
"newArchEnabled": true,
|
||||
"ios": {
|
||||
"supportsTablet": true
|
||||
},
|
||||
"android": {
|
||||
"adaptiveIcon": {
|
||||
"backgroundColor": "#E6F4FE",
|
||||
"foregroundImage": "./assets/images/android-icon-foreground.png",
|
||||
"backgroundImage": "./assets/images/android-icon-background.png",
|
||||
"monochromeImage": "./assets/images/android-icon-monochrome.png"
|
||||
},
|
||||
"edgeToEdgeEnabled": true,
|
||||
"predictiveBackGestureEnabled": false
|
||||
},
|
||||
"web": {
|
||||
"bundler": "metro",
|
||||
"output": "static",
|
||||
"favicon": "./assets/images/favicon.png"
|
||||
},
|
||||
"plugins": [
|
||||
"expo-router",
|
||||
[
|
||||
"expo-splash-screen",
|
||||
{
|
||||
"image": "./assets/images/splash-icon.png",
|
||||
"imageWidth": 200,
|
||||
"resizeMode": "contain",
|
||||
"backgroundColor": "#ffffff",
|
||||
"dark": {
|
||||
"backgroundColor": "#000000"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"experiments": {
|
||||
"typedRoutes": true,
|
||||
"reactCompiler": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useColorScheme } from "@/hooks/use-color-scheme";
|
||||
import { Stack } from "expo-router";
|
||||
import { useEffect } from "react";
|
||||
import "@/styles/global.css";
|
||||
|
||||
export default function RootLayout() {
|
||||
const colorScheme = useColorScheme();
|
||||
|
||||
useEffect(() => {
|
||||
console.log(colorScheme);
|
||||
}, [colorScheme]);
|
||||
|
||||
return <Stack />;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import React from "react";
|
||||
import { Button, ButtonText, ButtonSpinner, ButtonIcon } from "@/components/ui/button";
|
||||
import { Text, View } from "react-native";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<View className="flex-1 items-center justify-center bg-white dark:bg-black">
|
||||
<Text className="text-xl font-bold text-blue-500 dark:text-white">
|
||||
Welcome to Nativewind!
|
||||
</Text>
|
||||
<Button action={"primary"} variant={"solid"} size={"lg"} isDisabled={false}>
|
||||
<ButtonText>Hello World</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 384 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,6 @@
|
||||
module.exports = function (api) {
|
||||
api.cache(true);
|
||||
return {
|
||||
presets: [["babel-preset-expo", { jsxImportSource: "nativewind" }], "nativewind/babel"],
|
||||
};
|
||||
};
|
||||
@@ -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 @@
|
||||
export { useColorScheme } from 'react-native';
|
||||
@@ -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,6 @@
|
||||
const { getDefaultConfig } = require("expo/metro-config");
|
||||
const { withNativeWind } = require("nativewind/metro");
|
||||
|
||||
const config = getDefaultConfig(__dirname);
|
||||
|
||||
module.exports = withNativeWind(config, { input: "./styles/global.css" });
|
||||
@@ -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.
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "app-rn3",
|
||||
"main": "expo-router/entry",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"start": "expo start",
|
||||
"reset-project": "node ./scripts/reset-project.js",
|
||||
"android": "expo start --android",
|
||||
"ios": "expo start --ios",
|
||||
"web": "expo start --web",
|
||||
"lint": "expo lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/vector-icons": "^15.0.3",
|
||||
"@gluestack-ui/core": "^3.0.16",
|
||||
"@gluestack-ui/utils": "^3.0.19",
|
||||
"@gluestack/ui-next-adapter": "^3.0.3",
|
||||
"@react-navigation/bottom-tabs": "^7.4.0",
|
||||
"@react-navigation/elements": "^2.6.3",
|
||||
"@react-navigation/native": "^7.1.8",
|
||||
"expo": "~54.0.33",
|
||||
"expo-constants": "~18.0.13",
|
||||
"expo-font": "~14.0.11",
|
||||
"expo-haptics": "~15.0.8",
|
||||
"expo-image": "~3.0.11",
|
||||
"expo-linking": "~8.0.11",
|
||||
"expo-router": "~6.0.23",
|
||||
"expo-splash-screen": "~31.0.13",
|
||||
"expo-status-bar": "~3.0.9",
|
||||
"expo-symbols": "~1.0.8",
|
||||
"expo-system-ui": "~6.0.9",
|
||||
"expo-web-browser": "~15.0.10",
|
||||
"nativewind": "^4.2.3",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react-native": "0.81.5",
|
||||
"react-native-css-interop": "^0.2.3",
|
||||
"react-native-gesture-handler": "~2.28.0",
|
||||
"react-native-reanimated": "~4.1.7",
|
||||
"react-native-safe-area-context": "~5.6.2",
|
||||
"react-native-screens": "~4.16.0",
|
||||
"react-native-svg": "15.12.1",
|
||||
"react-native-web": "~0.21.0",
|
||||
"react-native-worklets": "0.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "~19.1.0",
|
||||
"babel-preset-expo": "^55.0.16",
|
||||
"oxfmt": "^0.44.0",
|
||||
"prettier-plugin-tailwindcss": "^0.5.14",
|
||||
"tailwindcss": "^3.4.19",
|
||||
"typescript": "~5.9.2"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
nodeLinker: hoisted
|
||||
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -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)",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": "expo/tsconfig.base",
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".expo/types/**/*.ts",
|
||||
"expo-env.d.ts",
|
||||
"nativewind-env.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -1 +0,0 @@
|
||||
shamefully-hoist=true
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar"]
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
{
|
||||
// Disable the default formatter, use eslint instead
|
||||
"prettier.enable": false,
|
||||
"editor.formatOnSave": false,
|
||||
|
||||
// Auto fix
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.organizeImports": "never"
|
||||
},
|
||||
|
||||
// Silent the stylistic rules in you IDE, but still auto fix them
|
||||
"eslint.rules.customizations": [
|
||||
{ "rule": "style/*", "severity": "off", "fixable": true },
|
||||
{ "rule": "format/*", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-indent", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-spacing", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-spaces", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-order", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-dangle", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-newline", "severity": "off", "fixable": true },
|
||||
{ "rule": "*quotes", "severity": "off", "fixable": true },
|
||||
{ "rule": "*semi", "severity": "off", "fixable": true }
|
||||
],
|
||||
|
||||
// Enable eslint for all supported languages
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact",
|
||||
"vue",
|
||||
"html",
|
||||
"markdown",
|
||||
"json",
|
||||
"jsonc",
|
||||
"yaml",
|
||||
"toml",
|
||||
"xml",
|
||||
"gql",
|
||||
"graphql",
|
||||
"astro",
|
||||
"svelte",
|
||||
"css",
|
||||
"less",
|
||||
"scss",
|
||||
"pcss",
|
||||
"postcss"
|
||||
]
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"Print Vue3 SFC": {
|
||||
"scope": "vue",
|
||||
"prefix": "v3",
|
||||
"body": [
|
||||
"<script lang=\"ts\" setup>",
|
||||
"",
|
||||
"</script>\n",
|
||||
"<template>",
|
||||
" <view class=\"\">$1</view>",
|
||||
"</template>\n",
|
||||
"<style lang=\"scss\" scoped>",
|
||||
"",
|
||||
"</style>\n"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
VITE_APP_PORT = 3000
|
||||
|
||||
VITE_ALIYUN_BUCKET = 'g3cdcont'
|
||||
@@ -1,26 +0,0 @@
|
||||
// eslint.config.mjs
|
||||
import antfu from '@antfu/eslint-config'
|
||||
|
||||
export default antfu({
|
||||
vue: true,
|
||||
|
||||
stylistic: {
|
||||
indent: 2,
|
||||
quotes: 'single',
|
||||
},
|
||||
|
||||
formatters: {
|
||||
css: true,
|
||||
html: true,
|
||||
markdown: 'prettier',
|
||||
},
|
||||
|
||||
rules: {
|
||||
'no-console': 'off',
|
||||
'prefer-promise-reject-errors': 'off',
|
||||
'unused-imports/no-unused-imports': 'warn',
|
||||
'unused-imports/no-unused-vars': 'warn',
|
||||
'prefer-const': 'off',
|
||||
'no-eval': 'off',
|
||||
},
|
||||
})
|
||||
@@ -1,26 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="shortcut icon" href="/public/logo.png" type="image/x-icon" />
|
||||
<script>
|
||||
const coverSupport =
|
||||
'CSS' in window &&
|
||||
typeof CSS.supports === 'function' &&
|
||||
(CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
|
||||
document.write(
|
||||
`<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0${
|
||||
coverSupport ? ', viewport-fit=cover' : ''
|
||||
}" />`,
|
||||
)
|
||||
</script>
|
||||
<title>App</title>
|
||||
<!--preload-links-->
|
||||
<!--app-context-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"><!--app-html--></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,129 +0,0 @@
|
||||
{
|
||||
"name": "base-uniapp3",
|
||||
"type": "module",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"dev:app": "uni -p app",
|
||||
"dev:app:test": "uni -p app --mode test",
|
||||
"dev:app:prod": "uni -p app --mode production",
|
||||
"dev:app-android": "uni -p app-android",
|
||||
"dev:app-ios": "uni -p app-ios",
|
||||
"dev:custom": "uni -p",
|
||||
"dev": "node --experimental-loader ./scripts/window-path-loader.js node_modules/@dcloudio/vite-plugin-uni/bin/uni.js",
|
||||
"dev:test": "uni --mode test",
|
||||
"dev:prod": "uni --mode production",
|
||||
"dev:h5": "uni",
|
||||
"dev:h5:test": "uni --mode test",
|
||||
"dev:h5:prod": "uni --mode production",
|
||||
"dev:h5:ssr": "uni --ssr",
|
||||
"dev:mp": "uni -p mp-weixin",
|
||||
"dev:mp:test": "uni -p mp-weixin --mode test",
|
||||
"dev:mp:prod": "uni -p mp-weixin --mode production",
|
||||
"dev:mp-alipay": "uni -p mp-alipay",
|
||||
"dev:mp-baidu": "uni -p mp-baidu",
|
||||
"dev:mp-jd": "uni -p mp-jd",
|
||||
"dev:mp-kuaishou": "uni -p mp-kuaishou",
|
||||
"dev:mp-lark": "uni -p mp-lark",
|
||||
"dev:mp-qq": "uni -p mp-qq",
|
||||
"dev:mp-toutiao": "uni -p mp-toutiao",
|
||||
"dev:mp-weixin": "uni -p mp-weixin",
|
||||
"dev:mp-xhs": "uni -p mp-xhs",
|
||||
"dev:quickapp-webview": "uni -p quickapp-webview",
|
||||
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
|
||||
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
|
||||
"build:app": "uni build -p app",
|
||||
"build:app:test": "uni build -p app --mode test",
|
||||
"build:app:prod": "uni build -p app --mode production",
|
||||
"build:app-android": "uni build -p app-android",
|
||||
"build:app-ios": "uni build -p app-ios",
|
||||
"build:custom": "uni build -p",
|
||||
"build:h5": "uni build",
|
||||
"build:h5:test": "uni build --mode test",
|
||||
"build:h5:prod": "uni build --mode production",
|
||||
"build": "uni build",
|
||||
"build:test": "uni build --mode test",
|
||||
"build:prod": "uni build --mode production",
|
||||
"build:h5:ssr": "uni build --ssr",
|
||||
"build:mp-alipay": "uni build -p mp-alipay",
|
||||
"build:mp": "uni build -p mp-weixin",
|
||||
"build:mp:test": "uni build -p mp-weixin --mode test",
|
||||
"build:mp:prod": "uni build -p mp-weixin --mode production",
|
||||
"build:mp-baidu": "uni build -p mp-baidu",
|
||||
"build:mp-jd": "uni build -p mp-jd",
|
||||
"build:mp-kuaishou": "uni build -p mp-kuaishou",
|
||||
"build:mp-lark": "uni build -p mp-lark",
|
||||
"build:mp-qq": "uni build -p mp-qq",
|
||||
"build:mp-toutiao": "uni build -p mp-toutiao",
|
||||
"build:mp-weixin": "uni build -p mp-weixin",
|
||||
"build:mp-xhs": "uni build -p mp-xhs",
|
||||
"build:quickapp-webview": "uni build -p quickapp-webview",
|
||||
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
|
||||
"build:quickapp-webview-union": "uni build -p quickapp-webview-union"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dcloudio/uni-app": "3.0.0-4070620250821001",
|
||||
"@dcloudio/uni-app-harmony": "3.0.0-4070620250821001",
|
||||
"@dcloudio/uni-app-plus": "3.0.0-4070620250821001",
|
||||
"@dcloudio/uni-components": "3.0.0-4070620250821001",
|
||||
"@dcloudio/uni-h5": "3.0.0-4070620250821001",
|
||||
"@dcloudio/uni-mp-alipay": "3.0.0-4070620250821001",
|
||||
"@dcloudio/uni-mp-baidu": "3.0.0-4070620250821001",
|
||||
"@dcloudio/uni-mp-harmony": "3.0.0-4070620250821001",
|
||||
"@dcloudio/uni-mp-jd": "3.0.0-4070620250821001",
|
||||
"@dcloudio/uni-mp-kuaishou": "3.0.0-4070620250821001",
|
||||
"@dcloudio/uni-mp-lark": "3.0.0-4070620250821001",
|
||||
"@dcloudio/uni-mp-qq": "3.0.0-4070620250821001",
|
||||
"@dcloudio/uni-mp-toutiao": "3.0.0-4070620250821001",
|
||||
"@dcloudio/uni-mp-weixin": "3.0.0-4070620250821001",
|
||||
"@dcloudio/uni-mp-xhs": "3.0.0-4070620250821001",
|
||||
"@dcloudio/uni-quickapp-webview": "3.0.0-4070620250821001",
|
||||
"@vitejs/plugin-basic-ssl": "^2.1.4",
|
||||
"abortcontroller-polyfill": "^1.7.8",
|
||||
"dayjs": "^1.11.18",
|
||||
"pinia": "^3.0.3",
|
||||
"pinia-plugin-persistedstate": "^4.5.0",
|
||||
"sard-uniapp": "^1.28.0",
|
||||
"vue": "^3.5.21",
|
||||
"wot-design-uni": "^1.13.0",
|
||||
"z-paging": "^2.8.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^5.2.1",
|
||||
"@dcloudio/types": "^3.4.21",
|
||||
"@dcloudio/uni-automator": "3.0.0-4070620250821001",
|
||||
"@dcloudio/uni-cli-shared": "3.0.0-4070620250821001",
|
||||
"@dcloudio/uni-stacktracey": "3.0.0-4070620250821001",
|
||||
"@dcloudio/vite-plugin-uni": "3.0.0-4070620250821001",
|
||||
"@esbuild/darwin-arm64": "0.20.2",
|
||||
"@esbuild/darwin-x64": "0.20.2",
|
||||
"@rollup/rollup-darwin-x64": "^4.28.0",
|
||||
"@types/node": "^20.17.9",
|
||||
"@uni-helper/plugin-uni": "0.1.0",
|
||||
"@uni-helper/uni-env": "^0.1.8",
|
||||
"@uni-helper/uni-types": "1.0.0-alpha.6",
|
||||
"@uni-helper/vite-plugin-uni-components": "0.2.0",
|
||||
"@uni-helper/vite-plugin-uni-layouts": "0.1.11",
|
||||
"@uni-helper/vite-plugin-uni-manifest": "^0.2.8",
|
||||
"@uni-helper/vite-plugin-uni-platform": "^0.0.5",
|
||||
"@uni-ku/bundle-optimizer": "^1.3.3",
|
||||
"@vitejs/plugin-legacy": "^7.2.1",
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"@vue/tsconfig": "^0.8.1",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"eslint": "^9.34.0",
|
||||
"eslint-plugin-format": "^1.0.1",
|
||||
"miniprogram-api-typings": "^4.1.0",
|
||||
"postcss": "^8.4.49",
|
||||
"postcss-html": "^1.8.0",
|
||||
"postcss-scss": "^4.0.9",
|
||||
"rollup-plugin-visualizer": "^6.0.3",
|
||||
"sass": "1.77.8",
|
||||
"terser": "^5.44.0",
|
||||
"typescript": "~5.8.3",
|
||||
"unplugin-auto-import": "^20.0.0",
|
||||
"vite": "^7.1.7",
|
||||
"vue-tsc": "^3.0.7"
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"setting": {
|
||||
"es6": true,
|
||||
"postcss": true,
|
||||
"minified": true,
|
||||
"uglifyFileName": false,
|
||||
"enhance": true,
|
||||
"packNpmRelationList": [],
|
||||
"babelSetting": {
|
||||
"ignore": [],
|
||||
"disablePlugins": [],
|
||||
"outputPath": ""
|
||||
},
|
||||
"useCompilerPlugins": false,
|
||||
"minifyWXML": true
|
||||
},
|
||||
"compileType": "miniprogram",
|
||||
"simulatorPluginLibVersion": {},
|
||||
"packOptions": {
|
||||
"ignore": [],
|
||||
"include": []
|
||||
},
|
||||
"appid": "wxff43f54d4267a569",
|
||||
"editorSetting": {}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"libVersion": "3.10.2",
|
||||
"projectname": "base-uniapp-main",
|
||||
"setting": {
|
||||
"urlCheck": true,
|
||||
"coverView": true,
|
||||
"lazyloadPlaceholderEnable": false,
|
||||
"skylineRenderEnable": false,
|
||||
"preloadBackgroundData": false,
|
||||
"autoAudits": false,
|
||||
"showShadowRootInWxmlPanel": true,
|
||||
"compileHotReLoad": true
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,35 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { onLaunch, onShow } from '@dcloudio/uni-app'
|
||||
import { navigateToInterceptor } from '@/router/interceptor'
|
||||
|
||||
onLaunch(() => {
|
||||
})
|
||||
|
||||
onShow((options) => {
|
||||
if (options?.path) {
|
||||
navigateToInterceptor.invoke({ url: `/${options.path}`, query: options.query })
|
||||
}
|
||||
else {
|
||||
navigateToInterceptor.invoke({ url: '/' })
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f1f5f9;
|
||||
}
|
||||
|
||||
swiper,
|
||||
scroll-view {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 496 B |
@@ -1,32 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
defineOptions({
|
||||
name: 'OverlayLoading',
|
||||
})
|
||||
defineProps<{
|
||||
visible: boolean
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<sar-overlay :visible="visible">
|
||||
<view class="overlay">
|
||||
<sar-loading
|
||||
vertical
|
||||
color="#fff"
|
||||
text-color="#fff"
|
||||
>
|
||||
加载中...
|
||||
</sar-loading>
|
||||
</view>
|
||||
</sar-overlay>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.overlay {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
@@ -1,104 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import { dateSearchProps } from './types'
|
||||
|
||||
// 定义 props 和 emits
|
||||
const props = defineProps(dateSearchProps)
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: string | number): void
|
||||
}>()
|
||||
|
||||
// 初始化 dateValue,确保它从 props.modelValue 获取初始值
|
||||
const dateValue = ref<string | number>(props.modelValue || 0) // 如果没有 modelValue 默认 ''
|
||||
|
||||
// 监听 props.modelValue 的变化,并更新 dateValue
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue: string | number) => {
|
||||
if (props.type === 'date') {
|
||||
// 如果是字符串类型
|
||||
if (typeof newValue === 'string') {
|
||||
// 尝试将字符串转为时间戳
|
||||
const timestamp = Date.parse(newValue)
|
||||
// 如果是有效的时间戳,则使用它;否则使用 0
|
||||
dateValue.value = Number.isNaN(timestamp) ? 0 : timestamp
|
||||
}
|
||||
else {
|
||||
// 如果是数字类型,直接赋值
|
||||
dateValue.value = newValue
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
const showValue = computed(() => {
|
||||
// 如果 dateValue 有值且不为 0
|
||||
if (props.type === 'date') {
|
||||
if (dateValue.value && dateValue.value !== 0) {
|
||||
// 转换时间戳为 YYYY-MM-DD 格式
|
||||
const date = new Date(dateValue.value)
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0') // 补零
|
||||
const day = date.getDate().toString().padStart(2, '0') // 补零
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
else {
|
||||
// 如果 dateValue 为 0 或空,返回 placeholder
|
||||
return props.placeholder || '请输入'
|
||||
}
|
||||
}
|
||||
else {
|
||||
return dateValue.value || props.placeholder
|
||||
}
|
||||
})
|
||||
|
||||
const showValueClass = computed(() => {
|
||||
return dateValue.value ? 'text_black' : 'text_gray'
|
||||
})
|
||||
|
||||
// 监听 dateValue 变化并通知父组件
|
||||
watch(dateValue, (newValue) => {
|
||||
emit('update:modelValue', newValue) // 将变化同步回父组件
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<wd-datetime-picker v-bind="props" v-model="dateValue" use-default-slot>
|
||||
<view class="datetime-picker-container">
|
||||
<view class="datetime-display" :class="showValueClass">
|
||||
{{ showValue }}
|
||||
</view>
|
||||
</view>
|
||||
</wd-datetime-picker>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 包裹容器,确保内容水平居中 */
|
||||
.datetime-picker-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
/* 输入框样式 */
|
||||
.datetime-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 4px;
|
||||
font-size: 14px;
|
||||
color: #333; /* 默认文本颜色 */
|
||||
text-align: center;
|
||||
background-color: #f5f5f5; /* 背景色 */
|
||||
border-radius: 4px; /* 圆角 */
|
||||
}
|
||||
/* 黑色文本样式 */
|
||||
.text_black {
|
||||
color: black;
|
||||
}
|
||||
/* 灰色文本样式 */
|
||||
.text_gray {
|
||||
color: gray;
|
||||
}
|
||||
</style>
|
||||
@@ -1,10 +0,0 @@
|
||||
import { datetimePickerProps } from 'wot-design-uni/components/wd-datetime-picker/types'
|
||||
|
||||
export const dateSearchProps = {
|
||||
...datetimePickerProps,
|
||||
modelValue: {
|
||||
type: [String, Number] as PropType<string | number>,
|
||||
default: '',
|
||||
required: true,
|
||||
},
|
||||
}
|
||||
@@ -1,236 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="overlay-center" style="pointer-events: none;">
|
||||
<view class="boxes">
|
||||
<view class="box">
|
||||
<view />
|
||||
<view />
|
||||
<view />
|
||||
<view />
|
||||
</view>
|
||||
<view class="box">
|
||||
<view />
|
||||
<view />
|
||||
<view />
|
||||
<view />
|
||||
</view>
|
||||
<view class="box">
|
||||
<view />
|
||||
<view />
|
||||
<view />
|
||||
<view />
|
||||
</view>
|
||||
<view class="box">
|
||||
<view />
|
||||
<view />
|
||||
<view />
|
||||
<view />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.boxes {
|
||||
--size: 32px;
|
||||
--duration: 800ms;
|
||||
position: relative;
|
||||
width: calc(var(--size) * 3);
|
||||
height: calc(var(--size) * 2);
|
||||
margin-top: calc(var(--size) * 1.5 * -1);
|
||||
transform: rotateX(60deg) rotateZ(45deg) rotateY(0deg) translateZ(0px);
|
||||
transform-origin: 50% 50%;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
.overlay-center {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.boxes .box {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
.boxes .box:nth-child(1) {
|
||||
transform: translate(100%, 0);
|
||||
-webkit-animation: box1 var(--duration) linear infinite;
|
||||
animation: box1 var(--duration) linear infinite;
|
||||
}
|
||||
|
||||
.boxes .box:nth-child(2) {
|
||||
transform: translate(0, 100%);
|
||||
-webkit-animation: box2 var(--duration) linear infinite;
|
||||
animation: box2 var(--duration) linear infinite;
|
||||
}
|
||||
|
||||
.boxes .box:nth-child(3) {
|
||||
transform: translate(100%, 100%);
|
||||
-webkit-animation: box3 var(--duration) linear infinite;
|
||||
animation: box3 var(--duration) linear infinite;
|
||||
}
|
||||
|
||||
.boxes .box:nth-child(4) {
|
||||
transform: translate(200%, 0);
|
||||
-webkit-animation: box4 var(--duration) linear infinite;
|
||||
animation: box4 var(--duration) linear infinite;
|
||||
}
|
||||
|
||||
.boxes .box > view {
|
||||
--background: #5c8df6;
|
||||
--top: auto;
|
||||
--right: auto;
|
||||
--bottom: auto;
|
||||
--left: auto;
|
||||
--translateZ: calc(var(--size) / 2);
|
||||
--rotateY: 0deg;
|
||||
--rotateX: 0deg;
|
||||
position: absolute;
|
||||
top: var(--top);
|
||||
right: var(--right);
|
||||
bottom: var(--bottom);
|
||||
left: var(--left);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--background);
|
||||
transform: rotateY(var(--rotateY)) rotateX(var(--rotateX)) translateZ(var(--translateZ));
|
||||
}
|
||||
|
||||
.boxes .box > view:nth-child(1) {
|
||||
--top: 0;
|
||||
--left: 0;
|
||||
}
|
||||
|
||||
.boxes .box > view:nth-child(2) {
|
||||
--background: #145af2;
|
||||
--right: 0;
|
||||
--rotateY: 90deg;
|
||||
}
|
||||
|
||||
.boxes .box > view:nth-child(3) {
|
||||
--background: #447cf5;
|
||||
--rotateX: -90deg;
|
||||
}
|
||||
|
||||
.boxes .box > view:nth-child(4) {
|
||||
--background: #dbe3f4;
|
||||
--top: 0;
|
||||
--left: 0;
|
||||
--translateZ: calc(var(--size) * 3 * -1);
|
||||
}
|
||||
|
||||
@-webkit-keyframes box1 {
|
||||
0%,
|
||||
50% {
|
||||
transform: translate(100%, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(200%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes box1 {
|
||||
0%,
|
||||
50% {
|
||||
transform: translate(100%, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(200%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes box2 {
|
||||
0% {
|
||||
transform: translate(0, 100%);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(100%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes box2 {
|
||||
0% {
|
||||
transform: translate(0, 100%);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(100%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes box3 {
|
||||
0%,
|
||||
50% {
|
||||
transform: translate(100%, 100%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(0, 100%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes box3 {
|
||||
0%,
|
||||
50% {
|
||||
transform: translate(100%, 100%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(0, 100%);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes box4 {
|
||||
0% {
|
||||
transform: translate(200%, 0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate(200%, 100%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(100%, 100%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes box4 {
|
||||
0% {
|
||||
transform: translate(200%, 0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate(200%, 100%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(100%, 100%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,71 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import PickerSelect from '../picker-select/index.vue'
|
||||
|
||||
const props = defineProps({
|
||||
label: { type: String, default: '' },
|
||||
value: { type: null as unknown as PropType<any>, default: undefined },
|
||||
labelField: { type: String, required: true },
|
||||
labelField2: { type: String, required: false },
|
||||
valueField: { type: String, required: true },
|
||||
viewname: { type: String, required: true },
|
||||
field1: { type: String, required: true },
|
||||
field2: { type: String, required: true },
|
||||
placeholder: { type: String, default: '请选择' },
|
||||
searchcondition: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
searchType: {
|
||||
type: String as PropType<'default' | 'ex' | 'single'>,
|
||||
default: 'default',
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: 'update:label', val: string): void
|
||||
(e: 'update:value', val: any): void
|
||||
(e: 'change', value: any): void
|
||||
}>()
|
||||
|
||||
const _visible = ref(false)
|
||||
const _label = ref(props.label)
|
||||
|
||||
// 同步 props.label
|
||||
watch(() => props.label, (val) => {
|
||||
_label.value = val
|
||||
})
|
||||
|
||||
// 选中时只处理 label
|
||||
function handleSelectChange(selectValue: any) {
|
||||
if (!selectValue)
|
||||
return
|
||||
_label.value = selectValue[props.labelField]
|
||||
emits('update:label', _label.value)
|
||||
|
||||
emits('change', selectValue)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<sar-popout-input
|
||||
v-model="_label"
|
||||
:placeholder="props.placeholder"
|
||||
@click="_visible = true"
|
||||
>
|
||||
<PickerSelect
|
||||
v-model:visible="_visible"
|
||||
:value="props.value"
|
||||
:viewname="props.viewname"
|
||||
:field1="props.field1"
|
||||
:field2="props.field2"
|
||||
:label-field="props.labelField"
|
||||
:label-field2="props.labelField2"
|
||||
:value-field="props.valueField"
|
||||
:searchcondition="searchcondition"
|
||||
:search-type="searchType"
|
||||
@update:value="emits('update:value', $event)"
|
||||
@change="handleSelectChange"
|
||||
/>
|
||||
</sar-popout-input>
|
||||
</template>
|
||||
@@ -1,403 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { PropType } from 'vue'
|
||||
import { useIndeterminate } from 'sard-uniapp'
|
||||
import { computed, ref } from 'vue'
|
||||
import { getAutocompleteDataApi, getAutocompleteDataExApi } from '@/request/api'
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
type: {
|
||||
type: String as PropType<'radio' | 'multiple'>,
|
||||
default: 'radio',
|
||||
},
|
||||
value: {
|
||||
type: null as unknown as PropType<any | any[]>,
|
||||
default: undefined,
|
||||
},
|
||||
labelField: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
labelField2: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
valueField: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
viewname: {
|
||||
type: String,
|
||||
},
|
||||
searchcondition: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
userid: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
field1: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
field2: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
searchType: {
|
||||
type: String as PropType<'default' | 'ex' | 'single'>,
|
||||
default: 'default',
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['update:visible', 'update:value', 'change'])
|
||||
|
||||
const _visible = ref(props.visible)
|
||||
|
||||
watch(() => props.visible, val => _visible.value = val)
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
const searchValue = ref('')
|
||||
const pagination = ref({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const isPrevDisabled = computed(() => pagination.value.current <= 1)
|
||||
const isNextDisabled = computed(() =>
|
||||
pagination.value.current >= Math.ceil(pagination.value.total / pagination.value.pageSize),
|
||||
)
|
||||
|
||||
// 内部 value,双向绑定 props.value
|
||||
const _value = computed({
|
||||
get: () => {
|
||||
if (props.type === 'multiple') {
|
||||
return Array.isArray(props.value) ? props.value : []
|
||||
}
|
||||
return props.value ?? null
|
||||
},
|
||||
set: (val) => {
|
||||
emit('update:value', val)
|
||||
},
|
||||
})
|
||||
|
||||
const options = ref<any[]>([])
|
||||
|
||||
const { allChecked, isIndeterminate, onAllChange } = useIndeterminate(
|
||||
_value,
|
||||
options,
|
||||
)
|
||||
|
||||
function select(option, toggle) {
|
||||
if (!option.disabled) {
|
||||
toggle(option.value)
|
||||
if (props.type === 'radio') {
|
||||
_visible.value = false
|
||||
|
||||
const selectedOption = options.value.find(
|
||||
o => o.value === option.value,
|
||||
)
|
||||
|
||||
_visible.value = false
|
||||
emit('update:visible', false)
|
||||
emit('change', selectedOption)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 显示数量
|
||||
const count = computed(() => {
|
||||
if (props.type === 'multiple') {
|
||||
return `${(_value.value).length || 0} / ${options.value.length}`
|
||||
}
|
||||
else {
|
||||
return _value.value != null ? `1 / ${options.value.length}` : `0 / ${options.value.length}`
|
||||
}
|
||||
})
|
||||
|
||||
function handleAfterEnter() {
|
||||
getData()
|
||||
}
|
||||
|
||||
function handlePagationChange(page: number) {
|
||||
pagination.value.current = page
|
||||
|
||||
getData()
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
pagination.value.current = 1
|
||||
pagination.value.total = 0
|
||||
pagination.value.pageSize = 10
|
||||
|
||||
getData()
|
||||
}
|
||||
|
||||
function getData() {
|
||||
if (props.searchType === 'ex') {
|
||||
getAutocompleteDataEx()
|
||||
}
|
||||
else if (props.searchType === 'single') {
|
||||
getAutocompleteDataSingle()
|
||||
}
|
||||
else {
|
||||
getAutocompleteData()
|
||||
}
|
||||
}
|
||||
|
||||
function getAutocompleteData() {
|
||||
loading.value = true
|
||||
|
||||
getAutocompleteDataApi({
|
||||
q: searchValue.value,
|
||||
page: pagination.value.current,
|
||||
limit: pagination.value.pageSize,
|
||||
viewname: props.viewname,
|
||||
field1: props.field1,
|
||||
field2: props.field2,
|
||||
}).then((res) => {
|
||||
pagination.value.total = res.data.total
|
||||
|
||||
options.value = res.data.rows.map((item) => {
|
||||
const label1 = item[props.labelField] ?? ''
|
||||
const label2 = props.labelField2
|
||||
? item[props.labelField2] ?? ''
|
||||
: ''
|
||||
|
||||
return {
|
||||
label: label2 ? `${label1} | ${label2}` : label1,
|
||||
value: item[props.valueField] ?? undefined,
|
||||
disabled: !!item.disabled,
|
||||
...item,
|
||||
}
|
||||
})
|
||||
|
||||
console.log(res.data.rows)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function getAutocompleteDataEx() {
|
||||
loading.value = true
|
||||
|
||||
getAutocompleteDataExApi({
|
||||
q: searchValue.value,
|
||||
page: pagination.value.current,
|
||||
limit: pagination.value.pageSize,
|
||||
viewname: props.viewname,
|
||||
field1: props.field1,
|
||||
field2: props.field2,
|
||||
userid: props.userid,
|
||||
searchcondition: props.searchcondition,
|
||||
}).then((res) => {
|
||||
pagination.value.total = res.data.total
|
||||
|
||||
options.value = res.data.rows.map((item) => {
|
||||
const label1 = item[props.labelField] ?? ''
|
||||
const label2 = props.labelField2
|
||||
? item[props.labelField2] ?? ''
|
||||
: ''
|
||||
|
||||
return {
|
||||
label: label2 ? `${label1} | ${label2}` : label1,
|
||||
value: item[props.valueField] ?? undefined,
|
||||
disabled: !!item.disabled,
|
||||
...item,
|
||||
}
|
||||
})
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function getAutocompleteDataSingle() {
|
||||
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
_visible.value = false
|
||||
emit('update:visible', false)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<sar-popout
|
||||
:visible="_visible"
|
||||
:show-footer="false"
|
||||
title="请选择"
|
||||
@after-enter="handleAfterEnter"
|
||||
@close="handleCancel"
|
||||
>
|
||||
<template v-if="props.type === 'multiple'" #title-prepend>
|
||||
<view class="sar-checkbox-popout__check-all">
|
||||
<sar-checkbox
|
||||
v-model:checked="allChecked"
|
||||
:indeterminate="isIndeterminate"
|
||||
@change="onAllChange"
|
||||
>
|
||||
{{ count }}
|
||||
</sar-checkbox>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template #visible="{ already }">
|
||||
<view v-if="already">
|
||||
<sar-list-item>
|
||||
<sar-input
|
||||
v-model="searchValue"
|
||||
placeholder="请输入搜索的值"
|
||||
clearable
|
||||
@confirm="handleSearch"
|
||||
>
|
||||
<template #prepend>
|
||||
<sar-icon family="sari" name="search" />
|
||||
</template>
|
||||
</sar-input>
|
||||
</sar-list-item>
|
||||
|
||||
<view class="sar-checkbox-popout__container">
|
||||
<scroll-view
|
||||
class="sar-checkbox-popout__scroll_searchable sar-checkbox-popout__scroll"
|
||||
scroll-y
|
||||
trap-scroll
|
||||
:upper-threshold="0"
|
||||
:lower-threshold="0"
|
||||
:throttle="false"
|
||||
>
|
||||
<sar-empty
|
||||
v-if="options.length === 0"
|
||||
:root-style="{ height: '100%' }"
|
||||
/>
|
||||
|
||||
<sar-checkbox-group
|
||||
v-else-if="props.type === 'multiple'"
|
||||
v-model="_value"
|
||||
:validate-event="false"
|
||||
>
|
||||
<template #custom="{ toggle }">
|
||||
<sar-list inlaid>
|
||||
<sar-list-item
|
||||
v-for="option in options"
|
||||
:key="option.value"
|
||||
:title="option.label"
|
||||
@click="select(option, toggle)"
|
||||
>
|
||||
<template #icon>
|
||||
<sar-checkbox
|
||||
readonly
|
||||
:value="option.value"
|
||||
:validate-event="false"
|
||||
/>
|
||||
</template>
|
||||
</sar-list-item>
|
||||
</sar-list>
|
||||
</template>
|
||||
</sar-checkbox-group>
|
||||
|
||||
<sar-radio-group
|
||||
v-else
|
||||
v-model="_value"
|
||||
:validate-event="false"
|
||||
>
|
||||
<template #custom="{ toggle }">
|
||||
<sar-list inlaid>
|
||||
<sar-list-item
|
||||
v-for="option in options"
|
||||
:key="option.value"
|
||||
:title="option.label"
|
||||
:hover="!option.disabled"
|
||||
@click="select(option, toggle)"
|
||||
>
|
||||
<template #icon>
|
||||
<sar-radio
|
||||
:key="option.value"
|
||||
readonly
|
||||
:disabled="option.disabled"
|
||||
:value="option.value"
|
||||
/>
|
||||
</template>
|
||||
</sar-list-item>
|
||||
</sar-list>
|
||||
</template>
|
||||
</sar-radio-group>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<view class="sar-popout__footer">
|
||||
<sar-pagination
|
||||
:current="pagination.current"
|
||||
:total="pagination.total"
|
||||
:page-size="pagination.pageSize"
|
||||
type="simple"
|
||||
root-class="picker-select__pagination-root"
|
||||
@change="handlePagationChange"
|
||||
>
|
||||
<template #prev>
|
||||
<sar-button type="default" :disabled="isPrevDisabled" size="mini" :loading="loading">
|
||||
上一页
|
||||
</sar-button>
|
||||
</template>
|
||||
<template #next>
|
||||
<sar-button type="default" :disabled="isNextDisabled" size="mini" :loading="loading">
|
||||
下一页
|
||||
</sar-button>
|
||||
</template>
|
||||
</sar-pagination>
|
||||
|
||||
<sar-button
|
||||
v-if="props.type === 'multiple'"
|
||||
:root-style="{ width: '100%' }"
|
||||
>
|
||||
确认
|
||||
</sar-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</sar-popout>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.sar-checkbox-popout__check-all {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
left: 32rpx;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
color: var(--sar-tertiary-color);
|
||||
font-size: var(--sar-text-sm);
|
||||
}
|
||||
|
||||
.sar-checkbox-popout__container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sar-checkbox-popout__scroll_searchable {
|
||||
height: var(--sar-checkbox-popout-max-height);
|
||||
}
|
||||
|
||||
.sar-checkbox-popout__scroll {
|
||||
max-height: var(--sar-checkbox-popout-max-height);
|
||||
}
|
||||
|
||||
.sar-popout__footer {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
align-items: center;
|
||||
padding: var(--sar-popout-footer-gap-y) var(--sar-popout-footer-gap-x);
|
||||
}
|
||||
</style>
|
||||
@@ -1,647 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { toast } from 'sard-uniapp'
|
||||
import { loaddataApi, loaddatabysqlApi } from '@/request/api'
|
||||
import { useUserStore } from '@/store'
|
||||
import { jsonToString } from '@/utils/common'
|
||||
|
||||
import { saveObjectHelper } from '@/utils/save'
|
||||
import { getCurrentDateTimeWithSeconds } from '@/utils/time'
|
||||
|
||||
const props = defineProps({
|
||||
componentData: {
|
||||
type: null,
|
||||
},
|
||||
})
|
||||
|
||||
const emits = defineEmits(['success'])
|
||||
|
||||
const visible = defineModel({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
})
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
const formRef = ref()
|
||||
const auditData = ref({
|
||||
showSelect: true,
|
||||
selectSearchCondition: '',
|
||||
toShUsername: '',
|
||||
toShUserid: '',
|
||||
shMemo: '',
|
||||
})
|
||||
|
||||
const shsetdatas = ref<any[]>([])
|
||||
|
||||
const midAuditShData = ref<{
|
||||
state: string
|
||||
shlevel: number
|
||||
rowdata: any
|
||||
}>({
|
||||
state: '',
|
||||
shlevel: 0,
|
||||
rowdata: {},
|
||||
})
|
||||
|
||||
const midAuditBhData = ref<{
|
||||
now_level: number
|
||||
ywownerid: string
|
||||
nowstate: string
|
||||
}>({
|
||||
ywownerid: '',
|
||||
nowstate: '',
|
||||
now_level: 0,
|
||||
})
|
||||
|
||||
async function handleAfterEnter() {
|
||||
console.log('===after enter===')
|
||||
|
||||
loading.value = true
|
||||
|
||||
if (props.componentData.row === null || props.componentData.moduleId === '' || props.componentData.saveTable === '') {
|
||||
toast.fail('传递参数有误,请联系管理人员!')
|
||||
return
|
||||
}
|
||||
|
||||
const req1 = await loaddataApi({
|
||||
viewname: 'sh_b_set',
|
||||
searchcondition: `b_module_id = '${props.componentData.moduleId}'`,
|
||||
orderfield: 'b_level',
|
||||
soption: '',
|
||||
})
|
||||
|
||||
shsetdatas.value = req1.data
|
||||
if (req1.data.length === 0) {
|
||||
handleNoLevelSh()
|
||||
}
|
||||
else {
|
||||
if (props.componentData.state === '审核通过') {
|
||||
handleLevelSh()
|
||||
}
|
||||
else if (props.componentData.state === '审核驳回') {
|
||||
handleLevelBh()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleNoLevelSh() {
|
||||
const { state, saveTable, moduleId, row } = props.componentData
|
||||
let canuse_state = []
|
||||
if (state === '送审') {
|
||||
canuse_state = ['草拟', '审核驳回', '']
|
||||
}
|
||||
if (state === '归档') {
|
||||
canuse_state = ['审核通过', '完成']
|
||||
}
|
||||
|
||||
let subtable = []
|
||||
let subinsertvalue = []
|
||||
let subupdatevalue = []
|
||||
let subdelvalue = []
|
||||
let keyid = []
|
||||
let logvalues = []
|
||||
|
||||
let LogArray = []
|
||||
let updateRows = []
|
||||
let updateFeeData = []
|
||||
|
||||
subtable.push(saveTable)
|
||||
keyid.push('b_id')
|
||||
subdelvalue.push('[]')
|
||||
subinsertvalue.push('[]')
|
||||
|
||||
if (canuse_state.length === 0 || canuse_state.includes(row.b_state)) {
|
||||
if (['cw_zdorder', 'cw_zdorder_f'].includes(moduleId)) {
|
||||
if (state === '审核通过') {
|
||||
// 暂时不做处理
|
||||
}
|
||||
if (state === '审核驳回') {
|
||||
let sql = `select top 1 * from bs_business_cw where mx_zdorderid ='${row.b_id}' and (isnull(mx_invoiceid,'')<>'' or isnull(mx_sumamount,0)<>0)`
|
||||
await loaddatabysqlApi({
|
||||
sql,
|
||||
orderfield: 'subid',
|
||||
soption: '',
|
||||
}).then((res) => {
|
||||
if (res.data.length > 0) {
|
||||
toast.fail('驳回失败!费用项已销账或开票!')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const updateObj = {
|
||||
b_id: row.b_id,
|
||||
b_state: state,
|
||||
}
|
||||
const oldState = row.b_state
|
||||
let OpContent = `审核状态:${oldState}->${state}`
|
||||
let logoldData = `审核状态:${oldState}`
|
||||
let lognewData = `审核状态:${state}`
|
||||
|
||||
updateRows.push(updateObj)
|
||||
|
||||
LogArray.push({
|
||||
OpType: state,
|
||||
ModuleID: moduleId,
|
||||
RecordID: row.b_id,
|
||||
viewsubtable: saveTable,
|
||||
OpContent,
|
||||
modify_b: logoldData,
|
||||
modify_h: lognewData,
|
||||
})
|
||||
}
|
||||
else {
|
||||
toast.fail(`只有${canuse_state.join(',')}的记录才能${state}!`)
|
||||
}
|
||||
if (updateRows.length > 0) {
|
||||
subupdatevalue.push(jsonToString(updateRows))
|
||||
logvalues.push(jsonToString(LogArray))
|
||||
if (updateFeeData.length > 0) {
|
||||
subtable.push('bs_business_cw')
|
||||
keyid.push('subid')
|
||||
subdelvalue.push('[]')
|
||||
subinsertvalue.push('[]')
|
||||
subupdatevalue.push(jsonToString(updateFeeData))
|
||||
}
|
||||
await saveObjectHelper({
|
||||
tables: subtable,
|
||||
insertValues: subinsertvalue,
|
||||
delValues: subdelvalue,
|
||||
updateValues: subupdatevalue,
|
||||
keys: keyid,
|
||||
logValues: logvalues,
|
||||
}).then((data) => {
|
||||
if (data === 'OK') {
|
||||
handleCloseDialog()
|
||||
toast.success(`${state}成功!`)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLevelSh() {
|
||||
const { userid } = userStore.userInfo
|
||||
const { saveTable, moduleId, row } = props.componentData
|
||||
const ywid = row.b_id
|
||||
let rowdata: any = {}
|
||||
|
||||
await loaddataApi({
|
||||
viewname: saveTable,
|
||||
searchcondition: `b_id='${ywid}'`,
|
||||
orderfield: 'b_id',
|
||||
soption: '',
|
||||
}).then((res) => {
|
||||
if (res.data.length === 0) {
|
||||
toast.fail('当前业务单不存在')
|
||||
handleCloseDialog()
|
||||
return
|
||||
}
|
||||
rowdata = res.data[0]
|
||||
})
|
||||
|
||||
let ywownerid = rowdata.b_owner_id
|
||||
|
||||
let shrecords = []
|
||||
await loaddataApi({
|
||||
viewname: 'sh_record',
|
||||
searchcondition: `b_dj_id='${ywid}' and b_module_id='${moduleId}'`,
|
||||
orderfield: 'b_id desc',
|
||||
soption: '',
|
||||
}).then((res) => {
|
||||
if (res.data.length === 0) {
|
||||
toast.fail('没有待审记录')
|
||||
handleCloseDialog()
|
||||
return
|
||||
}
|
||||
shrecords = res.data
|
||||
})
|
||||
|
||||
let now_level = shrecords[0].b_level
|
||||
let shlevel: any = Number.parseInt(now_level) + 1
|
||||
let dsh_useid = shrecords[0].b_tosh_useid
|
||||
|
||||
let can_sh = false
|
||||
let can_nextsh = false
|
||||
let level_userid = []
|
||||
|
||||
if (dsh_useid !== userid) {
|
||||
toast.fail('你没有该票业务的审批权限')
|
||||
handleCloseDialog()
|
||||
return
|
||||
}
|
||||
|
||||
for (const item of shsetdatas.value) {
|
||||
if (Number(item.b_level) === shlevel && item.b_user_id === dsh_useid) {
|
||||
can_sh = true
|
||||
}
|
||||
|
||||
if (Number(item.b_level) === Number.parseInt(shlevel) + 1) {
|
||||
can_nextsh = true
|
||||
level_userid.push(item.b_user_id)
|
||||
}
|
||||
}
|
||||
|
||||
if (!can_sh) {
|
||||
toast.fail('你没有该票业务的审批权限')
|
||||
handleCloseDialog()
|
||||
return
|
||||
}
|
||||
|
||||
midAuditShData.value.shlevel = shlevel
|
||||
|
||||
let sh_state = '审核通过'
|
||||
let xxx = ['一', '二', '三', '四', '五', '六', '七', '八']
|
||||
if (can_nextsh) {
|
||||
sh_state = `${xxx[now_level]}审通过`
|
||||
}
|
||||
|
||||
midAuditShData.value.state = sh_state
|
||||
midAuditShData.value.rowdata = rowdata
|
||||
let str_leveluserid = level_userid.map(item => `'${item}'`).join(',')
|
||||
let searchcondition = `b_id in (${str_leveluserid})`
|
||||
|
||||
auditData.value.selectSearchCondition = searchcondition
|
||||
|
||||
if (level_userid.length === 1) {
|
||||
auditData.value.toShUserid = level_userid[0]
|
||||
auditData.value.showSelect = false
|
||||
await loadShUserName(level_userid[0])
|
||||
}
|
||||
if (level_userid.length === 0) {
|
||||
auditData.value.toShUserid = ywownerid
|
||||
auditData.value.showSelect = false
|
||||
|
||||
await loadShUserName(ywownerid)
|
||||
}
|
||||
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
async function loadShUserName(userid) {
|
||||
if (!userid)
|
||||
return
|
||||
|
||||
try {
|
||||
const res = await loaddatabysqlApi({
|
||||
sql: `select b_name from b_user where b_id = '${userid}'`,
|
||||
soption: '',
|
||||
orderfield: '',
|
||||
})
|
||||
|
||||
const name = res?.data?.[0]?.b_name
|
||||
|
||||
auditData.value.toShUsername = name || userid
|
||||
}
|
||||
catch (err) {
|
||||
console.error('获取审核人名称失败:', err)
|
||||
auditData.value.toShUsername = userid
|
||||
}
|
||||
}
|
||||
// 驳回
|
||||
async function handleLevelBh() {
|
||||
const { userid } = userStore.userInfo
|
||||
const { saveTable, moduleId, row } = props.componentData
|
||||
const ywid = row.b_id
|
||||
|
||||
let rowdata: any = {}
|
||||
|
||||
await loaddataApi({
|
||||
viewname: saveTable,
|
||||
searchcondition: `b_id='${ywid}'`,
|
||||
orderfield: 'b_id',
|
||||
soption: '',
|
||||
}).then((res) => {
|
||||
if (res.data.length === 0) {
|
||||
toast.fail('当前业务单不存在')
|
||||
handleCloseDialog()
|
||||
return
|
||||
}
|
||||
rowdata = res.data[0]
|
||||
})
|
||||
let ywownerid = rowdata.b_owner_id
|
||||
let nowstate = rowdata.b_state
|
||||
if (nowstate === '审核驳回') {
|
||||
toast.fail('已驳回!请检查!')
|
||||
return false
|
||||
}
|
||||
|
||||
let shrecords = []
|
||||
let now_level = 0
|
||||
|
||||
await loaddataApi({
|
||||
viewname: 'sh_record',
|
||||
searchcondition: `b_dj_id='${ywid}' and b_module_id='${moduleId}'`,
|
||||
orderfield: 'b_id',
|
||||
soption: '',
|
||||
}).then((res) => {
|
||||
shrecords = res.data
|
||||
})
|
||||
if (shrecords.length === 0) {
|
||||
await loaddataApi({
|
||||
viewname: 'sh_b_set',
|
||||
searchcondition: `b_module_id='${moduleId}' and b_user_id='${userid}'`,
|
||||
orderfield: 'b_level',
|
||||
soption: '',
|
||||
}).then((res) => {
|
||||
if (res.data.length === 0) {
|
||||
toast.fail('你没有该票业务的驳回权限')
|
||||
handleCloseDialog()
|
||||
}
|
||||
})
|
||||
}
|
||||
else {
|
||||
now_level = shrecords[0].b_level
|
||||
let dsh_useid = shrecords[0].b_tosh_useid
|
||||
let sh_userid = shrecords[0].b_user_id
|
||||
if (dsh_useid !== userid && sh_userid !== userid && now_level > 0) {
|
||||
toast.fail('你没有该票业务的驳回权限')
|
||||
handleCloseDialog()
|
||||
return false
|
||||
}
|
||||
if (nowstate === '送审' && dsh_useid !== userid) {
|
||||
toast.fail('你没有该票业务的驳回权限')
|
||||
handleCloseDialog()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
auditData.value.toShUserid = ywownerid
|
||||
auditData.value.showSelect = false
|
||||
|
||||
await loadShUserName(ywownerid)
|
||||
|
||||
midAuditBhData.value = {
|
||||
ywownerid,
|
||||
nowstate,
|
||||
now_level,
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
async function handleBeforeClose(type: string) {
|
||||
if (type !== 'confirm')
|
||||
return true
|
||||
|
||||
if (props.componentData.state === '审核通过') {
|
||||
return await handleConfirmSh()
|
||||
}
|
||||
else if (props.componentData.state === '审核驳回') {
|
||||
return await handleConfirmBh()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
async function handleConfirmSh() {
|
||||
if (auditData.value.toShUserid === '') {
|
||||
toast.fail('请指定审批人')
|
||||
return false
|
||||
}
|
||||
if (midAuditShData.value.rowdata === undefined) {
|
||||
return false
|
||||
}
|
||||
try {
|
||||
loading.value = true
|
||||
const { userid } = userStore.userInfo
|
||||
const { rowdata, shlevel, state } = midAuditShData.value
|
||||
const { saveTable, moduleId } = props.componentData
|
||||
|
||||
let subtable = []
|
||||
let subinsertvalue = []
|
||||
let subupdatevalue = []
|
||||
let subdelvalue = []
|
||||
let keyid = []
|
||||
let logvalues = []
|
||||
|
||||
let updateRows = []
|
||||
let LogArray = []
|
||||
let insert_sh_records = []
|
||||
|
||||
let inputdatetime = getCurrentDateTimeWithSeconds()
|
||||
|
||||
let oldstate = rowdata.b_state
|
||||
let obj: any = { b_id: rowdata.b_id, b_state: state }
|
||||
if (state === '审核通过') {
|
||||
obj.mx_shdate = inputdatetime
|
||||
obj.mx_shman_id = userid
|
||||
}
|
||||
|
||||
updateRows.push(obj)
|
||||
|
||||
let OpContent = `审核状态:${oldstate}->${state}`
|
||||
let logoldData = `审核状态:${oldstate}`
|
||||
let lognewData = `审核状态:${state}`
|
||||
|
||||
LogArray.push({ OpType: '修改', ModuleID: moduleId, RecordID: rowdata.b_id, viewsubtable: saveTable, OpContent, modify_b: logoldData, modify_h: lognewData })
|
||||
|
||||
insert_sh_records.push({
|
||||
b_module_id: moduleId,
|
||||
b_user_id: userid,
|
||||
b_level: shlevel,
|
||||
b_name: state,
|
||||
b_dj_id: rowdata.b_id,
|
||||
b_sh: '1',
|
||||
b_sh_date: inputdatetime,
|
||||
b_sh_memo: auditData.value.shMemo,
|
||||
b_tosh_useid: auditData.value.toShUserid,
|
||||
})
|
||||
if (updateRows.length > 0) {
|
||||
subtable.length = 0
|
||||
subtable.push(saveTable)
|
||||
keyid.push('b_id')
|
||||
subdelvalue.push('[]')
|
||||
subinsertvalue.push('[]')
|
||||
subupdatevalue.push(jsonToString(updateRows))
|
||||
logvalues.push(jsonToString(LogArray))
|
||||
|
||||
subtable.push('sh_record')
|
||||
keyid.push('b_id')
|
||||
subdelvalue.push('[]')
|
||||
subinsertvalue.push(jsonToString(insert_sh_records))
|
||||
subupdatevalue.push('[]')
|
||||
logvalues.push('[]')
|
||||
|
||||
const sResult = await saveObjectHelper({ tables: subtable, insertValues: subinsertvalue, delValues: subdelvalue, updateValues: subupdatevalue, keys: keyid, logValues: logvalues })
|
||||
if (sResult === 'OK') {
|
||||
toast.success(`${state}成功!`)
|
||||
emits('success')
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
toast.fail('操作失败')
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
async function handleConfirmBh() {
|
||||
const { now_level, ywownerid, nowstate } = midAuditBhData.value
|
||||
const { state, saveTable, moduleId, row } = props.componentData
|
||||
const { userid } = userStore.userInfo
|
||||
const ywid = row.b_id
|
||||
|
||||
let subtable = []
|
||||
let subinsertvalue = []
|
||||
let subupdatevalue = []
|
||||
let subdelvalue = []
|
||||
let keyid = []
|
||||
let logvalues = []
|
||||
|
||||
let updateRows = []
|
||||
let LogArray = []
|
||||
let insert_sh_records = []
|
||||
|
||||
let inputdatetime = getCurrentDateTimeWithSeconds()
|
||||
|
||||
let obj: any = { b_id: ywid, b_state: state }
|
||||
|
||||
if (state === '审核驳回') {
|
||||
obj.mx_shdate = ''
|
||||
obj.mx_shman_id = ''
|
||||
}
|
||||
|
||||
updateRows.push(obj)
|
||||
|
||||
let OpContent = `审核状态:${nowstate}->${state}`
|
||||
let logoldData = `审核状态:${nowstate}`
|
||||
let lognewData = `审核状态:${state}`
|
||||
|
||||
LogArray.push({ OpType: '修改', ModuleID: moduleId, RecordID: ywid, viewsubtable: saveTable, OpContent, modify_b: logoldData, modify_h: lognewData })
|
||||
|
||||
insert_sh_records.push({
|
||||
b_module_id: moduleId,
|
||||
b_user_id: userid,
|
||||
b_level: now_level,
|
||||
b_name: state,
|
||||
b_dj_id: ywid,
|
||||
b_sh: '0',
|
||||
b_sh_date: inputdatetime,
|
||||
b_sh_memo: auditData.value.shMemo,
|
||||
b_tosh_useid: ywownerid,
|
||||
})
|
||||
if (updateRows.length > 0) {
|
||||
subtable.length = 0
|
||||
subtable.push(saveTable)
|
||||
keyid.push('b_id')
|
||||
subdelvalue.push('[]')
|
||||
subinsertvalue.push('[]')
|
||||
subupdatevalue.push(jsonToString(updateRows))
|
||||
logvalues.push(jsonToString(LogArray))
|
||||
|
||||
subtable.push('sh_record')
|
||||
keyid.push('b_id')
|
||||
subdelvalue.push('[]')
|
||||
subinsertvalue.push(jsonToString(insert_sh_records))
|
||||
subupdatevalue.push('[]')
|
||||
logvalues.push('[]')
|
||||
|
||||
const sResult = await saveObjectHelper({
|
||||
tables: subtable,
|
||||
insertValues: subinsertvalue,
|
||||
delValues: subdelvalue,
|
||||
updateValues: subupdatevalue,
|
||||
keys: keyid,
|
||||
logValues: logvalues,
|
||||
})
|
||||
|
||||
if (sResult === 'OK') {
|
||||
toast.success(`${state}成功!`)
|
||||
emits('success')
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleAfterClose() {
|
||||
loading.value = true
|
||||
|
||||
auditData.value = {
|
||||
showSelect: true,
|
||||
selectSearchCondition: '',
|
||||
toShUserid: '',
|
||||
toShUsername: '',
|
||||
shMemo: '',
|
||||
}
|
||||
|
||||
shsetdatas.value = []
|
||||
|
||||
midAuditShData.value = {
|
||||
state: '',
|
||||
shlevel: 0,
|
||||
rowdata: {},
|
||||
}
|
||||
midAuditBhData.value = {
|
||||
ywownerid: '',
|
||||
nowstate: '',
|
||||
now_level: 0,
|
||||
}
|
||||
}
|
||||
|
||||
function handleCloseDialog() {
|
||||
visible.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<sar-dialog
|
||||
v-model:visible="visible"
|
||||
popup-style="top: 50%"
|
||||
:title="props.componentData.state"
|
||||
button-type="text"
|
||||
:before-close="handleBeforeClose"
|
||||
@after-enter="handleAfterEnter"
|
||||
@after-leave="handleAfterClose"
|
||||
>
|
||||
<scroll-view
|
||||
style="max-height: 60vh; margin-top: 1px; margin-bottom: 16rpx"
|
||||
scroll-y
|
||||
>
|
||||
<view v-if="loading" class="dialog-loading">
|
||||
<sar-loading vertical>
|
||||
加载中...
|
||||
</sar-loading>
|
||||
</view>
|
||||
|
||||
<sar-form v-else ref="formRef" :model="auditData" label-width="150rpx" label-align="start" star-position="right">
|
||||
<sar-form-item
|
||||
v-if="auditData.showSelect"
|
||||
name="toShUserid"
|
||||
label="送报审批人"
|
||||
>
|
||||
<picker-select-input
|
||||
v-model:label="auditData.toShUsername"
|
||||
v-model:value="auditData.toShUserid"
|
||||
:searchcondition="auditData.selectSearchCondition"
|
||||
search-type="ex"
|
||||
viewname="b_user"
|
||||
label-field="b_name"
|
||||
value-field="b_id"
|
||||
field1="b_id"
|
||||
field2="b_name"
|
||||
/>
|
||||
</sar-form-item>
|
||||
|
||||
<sar-form-item
|
||||
name="shMemo"
|
||||
label="审核意见"
|
||||
>
|
||||
<sar-input v-model="auditData.shMemo" type="textarea" inlaid placeholder="请输入审核意见" />
|
||||
</sar-form-item>
|
||||
</sar-form>
|
||||
</scroll-view>
|
||||
</sar-dialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dialog-loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 200rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -1,24 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
||||
/// <reference types="vite-svg-loader" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
|
||||
const component: DefineComponent<object, object, any>
|
||||
export default component
|
||||
}
|
||||
|
||||
interface ImportMetaEnv {
|
||||
/** 服务端口号 */
|
||||
readonly VITE_SERVER_PORT: string
|
||||
/** 是否清除console */
|
||||
readonly VITE_DELETE_CONSOLE: string
|
||||
|
||||
readonly VITE_ALIYUN_BUCKET: string
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv
|
||||
}
|
||||
|
||||
declare const __UNI_PLATFORM__: 'app' | 'h5' | 'mp-alipay' | 'mp-baidu' | 'mp-kuaishou' | 'mp-lark' | 'mp-qq' | 'mp-tiktok' | 'mp-weixin' | 'mp-xiaochengxu'
|
||||
@@ -1,38 +0,0 @@
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
export function useDeviceInfo() {
|
||||
const isMobile = ref(false)
|
||||
const screenWidth = ref(0)
|
||||
|
||||
// 判断是否为手机
|
||||
function checkIsMobile() {
|
||||
const ua = navigator.userAgent || ''
|
||||
const isPhoneUA = /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua)
|
||||
const isMobileViewport = window.matchMedia('(max-width: 767px)').matches
|
||||
|
||||
// 平板 / iPad / 电脑都算 PC
|
||||
isMobile.value = isPhoneUA && isMobileViewport
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 首先用 UA + 媒体查询判断
|
||||
checkIsMobile()
|
||||
|
||||
// 兜底:uni.getSystemInfo 获取屏幕宽度
|
||||
if (typeof uni !== 'undefined' && uni.getSystemInfo) {
|
||||
uni.getSystemInfo({
|
||||
success: (res) => {
|
||||
screenWidth.value = res.screenWidth
|
||||
},
|
||||
})
|
||||
}
|
||||
else {
|
||||
screenWidth.value = window.innerWidth
|
||||
}
|
||||
|
||||
// 可选:监听窗口大小变化
|
||||
window.addEventListener('resize', checkIsMobile)
|
||||
})
|
||||
|
||||
return { isMobile, screenWidth }
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view>
|
||||
<slot />
|
||||
<sar-toast-agent />
|
||||
<sar-dialog-agent />
|
||||
<sar-notify-agent />
|
||||
</view>
|
||||
</template>
|
||||