init: 初始化模板项目
This commit is contained in:
3
packages/app/.gitignore
vendored
Normal file
3
packages/app/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
.DS_Store
|
||||
THUMBS_DB
|
||||
127
packages/app/features/home/screen.tsx
Normal file
127
packages/app/features/home/screen.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
import {
|
||||
Anchor,
|
||||
Button,
|
||||
H1,
|
||||
Paragraph,
|
||||
Separator,
|
||||
Sheet,
|
||||
SwitchRouterButton,
|
||||
SwitchThemeButton,
|
||||
useToastController,
|
||||
XStack,
|
||||
YStack
|
||||
} from '@my/ui'
|
||||
import { ChevronDown, ChevronUp } from '@tamagui/lucide-icons'
|
||||
import { useState } from 'react'
|
||||
import { Platform } from 'react-native'
|
||||
import { useLink } from 'solito/navigation'
|
||||
|
||||
export function HomeScreen({ pagesMode = false }: { pagesMode?: boolean }) {
|
||||
const linkTarget = pagesMode ? '/pages-example-user' : '/user'
|
||||
const linkProps = useLink({
|
||||
href: `${linkTarget}/nate`,
|
||||
})
|
||||
|
||||
return (
|
||||
<YStack flex={1} justify="center" items="center" gap="$8" p="$4" bg="$background">
|
||||
<XStack
|
||||
position="absolute"
|
||||
width="100%"
|
||||
t="$6"
|
||||
gap="$6"
|
||||
justify="center"
|
||||
flexWrap="wrap"
|
||||
$sm={{ position: 'relative', t: 0 }}
|
||||
>
|
||||
{Platform.OS === 'web' && (
|
||||
<>
|
||||
<SwitchRouterButton pagesMode={pagesMode} />
|
||||
<SwitchThemeButton />
|
||||
</>
|
||||
)}
|
||||
</XStack>
|
||||
|
||||
<YStack gap="$4">
|
||||
<H1 text="center" color="$color12">
|
||||
Welcome to Tamagui.
|
||||
</H1>
|
||||
<Paragraph color="$color10" text="center">
|
||||
Here's a basic starter to show navigating from one screen to another.
|
||||
</Paragraph>
|
||||
<Separator />
|
||||
<Paragraph text="center">
|
||||
This screen uses the same code on Next.js and React Native.
|
||||
</Paragraph>
|
||||
<Separator />
|
||||
</YStack>
|
||||
|
||||
<Button {...linkProps}>Link to user</Button>
|
||||
|
||||
<SheetDemo />
|
||||
</YStack>
|
||||
)
|
||||
}
|
||||
|
||||
function SheetDemo() {
|
||||
const toast = useToastController()
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
const [position, setPosition] = useState(0)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
size="$6"
|
||||
icon={open ? ChevronDown : ChevronUp}
|
||||
circular
|
||||
onPress={() => setOpen((x) => !x)}
|
||||
/>
|
||||
<Sheet
|
||||
modal
|
||||
animation="medium"
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
snapPoints={[80]}
|
||||
position={position}
|
||||
onPositionChange={setPosition}
|
||||
dismissOnSnapToBottom
|
||||
>
|
||||
<Sheet.Overlay
|
||||
bg="$shadow4"
|
||||
animation="lazy"
|
||||
enterStyle={{ opacity: 0 }}
|
||||
exitStyle={{ opacity: 0 }}
|
||||
/>
|
||||
<Sheet.Handle bg="$color8" />
|
||||
<Sheet.Frame items="center" justify="center" gap="$10" bg="$color2">
|
||||
<XStack gap="$2">
|
||||
<Paragraph text="center">Made by</Paragraph>
|
||||
<Anchor color="$blue10" href="https://twitter.com/natebirdman" target="_blank">
|
||||
@natebirdman,
|
||||
</Anchor>
|
||||
<Anchor
|
||||
color="$blue10"
|
||||
href="https://github.com/tamagui/tamagui"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
give it a ⭐️
|
||||
</Anchor>
|
||||
</XStack>
|
||||
|
||||
<Button
|
||||
size="$6"
|
||||
circular
|
||||
icon={ChevronDown}
|
||||
onPress={() => {
|
||||
setOpen(false)
|
||||
toast.show('Sheet closed!', {
|
||||
message: 'Just showing how toast works...',
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</Sheet.Frame>
|
||||
</Sheet>
|
||||
</>
|
||||
)
|
||||
}
|
||||
18
packages/app/features/user/detail-screen.tsx
Normal file
18
packages/app/features/user/detail-screen.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Button, Paragraph, YStack } from '@my/ui'
|
||||
import { ChevronLeft } from '@tamagui/lucide-icons'
|
||||
import { useRouter } from 'solito/navigation'
|
||||
|
||||
export function UserDetailScreen({ id }: { id: string }) {
|
||||
const router = useRouter()
|
||||
if (!id) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<YStack flex={1} justify="center" items="center" gap="$4" bg="$background">
|
||||
<Paragraph text="center" fontWeight="700" color="$blue10">{`User ID: ${id}`}</Paragraph>
|
||||
<Button icon={ChevronLeft} onPress={() => router.back()}>
|
||||
Go Home
|
||||
</Button>
|
||||
</YStack>
|
||||
)
|
||||
}
|
||||
4
packages/app/index.ts
Normal file
4
packages/app/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
// leave this blank
|
||||
// don't re-export files from this workspace. it'll break next.js tree shaking
|
||||
// https://github.com/vercel/next.js/issues/12557
|
||||
export {}
|
||||
28
packages/app/package.json
Normal file
28
packages/app/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"version": "0.0.0",
|
||||
"name": "app",
|
||||
"main": "index.ts",
|
||||
"private": true,
|
||||
"sideEffects": [
|
||||
"*.css"
|
||||
],
|
||||
"dependencies": {
|
||||
"@my/ui": "0.0.1",
|
||||
"@react-navigation/native": "^6.1.17",
|
||||
"@tamagui/animations-react-native": "^1.132.18",
|
||||
"@tamagui/colors": "^1.132.18",
|
||||
"@tamagui/font-inter": "^1.132.18",
|
||||
"@tamagui/lucide-icons": "^1.132.18",
|
||||
"@tamagui/shorthands": "^1.132.18",
|
||||
"@tamagui/themes": "^1.132.18",
|
||||
"burnt": "^0.12.2",
|
||||
"expo-constants": "~17.1.6",
|
||||
"expo-linking": "~7.1.4",
|
||||
"react-native-safe-area-context": "5.4.0",
|
||||
"solito": "^4.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "~19.0.10",
|
||||
"@types/react-native": "^0.73.0"
|
||||
}
|
||||
}
|
||||
64
packages/app/provider/NextTamaguiProvider.tsx
Normal file
64
packages/app/provider/NextTamaguiProvider.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
'use client'
|
||||
|
||||
import '@tamagui/core/reset.css'
|
||||
import '@tamagui/font-inter/css/400.css'
|
||||
import '@tamagui/font-inter/css/700.css'
|
||||
import '@tamagui/polyfill-dev'
|
||||
|
||||
import type { ReactNode } from 'react'
|
||||
import { useServerInsertedHTML } from 'next/navigation'
|
||||
import { NextThemeProvider, useRootTheme } from '@tamagui/next-theme'
|
||||
import { config } from '@my/ui'
|
||||
import { Provider } from 'app/provider'
|
||||
import { StyleSheet } from 'react-native'
|
||||
|
||||
export const NextTamaguiProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [theme, setTheme] = useRootTheme()
|
||||
|
||||
useServerInsertedHTML(() => {
|
||||
// @ts-ignore
|
||||
const rnwStyle = StyleSheet.getSheet()
|
||||
return (
|
||||
<>
|
||||
<link rel="stylesheet" href="/tamagui.css" />
|
||||
<style dangerouslySetInnerHTML={{ __html: rnwStyle.textContent }} id={rnwStyle.id} />
|
||||
<style
|
||||
dangerouslySetInnerHTML={{
|
||||
// the first time this runs you'll get the full CSS including all themes
|
||||
// after that, it will only return CSS generated since the last call
|
||||
__html: config.getNewCSS(),
|
||||
}}
|
||||
/>
|
||||
|
||||
<style
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: config.getCSS({
|
||||
exclude: process.env.NODE_ENV === 'production' ? 'design-system' : null,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
// avoid flash of animated things on enter:
|
||||
__html: `document.documentElement.classList.add('t_unmounted')`,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<NextThemeProvider
|
||||
skipNextHead
|
||||
defaultTheme="light"
|
||||
onChangeTheme={(next) => {
|
||||
setTheme(next as any)
|
||||
}}
|
||||
>
|
||||
<Provider disableRootThemeClass defaultTheme={theme || 'light'}>
|
||||
{children}
|
||||
</Provider>
|
||||
</NextThemeProvider>
|
||||
)
|
||||
}
|
||||
11
packages/app/provider/ToastViewport.tsx
Normal file
11
packages/app/provider/ToastViewport.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import { ToastViewport as ToastViewportOg } from '@my/ui'
|
||||
|
||||
export const ToastViewport = () => {
|
||||
return (
|
||||
<ToastViewportOg
|
||||
top="$8"
|
||||
left={0}
|
||||
right={0}
|
||||
/>
|
||||
)
|
||||
}
|
||||
11
packages/app/provider/ToastViewport.web.tsx
Normal file
11
packages/app/provider/ToastViewport.web.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import { ToastViewport as ToastViewportOg } from '@my/ui'
|
||||
|
||||
export const ToastViewport = () => {
|
||||
return (
|
||||
<ToastViewportOg
|
||||
left={0}
|
||||
right={0}
|
||||
top={10}
|
||||
/>
|
||||
)
|
||||
}
|
||||
29
packages/app/provider/index.tsx
Normal file
29
packages/app/provider/index.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { useColorScheme } from 'react-native'
|
||||
import {
|
||||
CustomToast,
|
||||
TamaguiProvider,
|
||||
type TamaguiProviderProps,
|
||||
ToastProvider,
|
||||
config,
|
||||
isWeb,
|
||||
} from '@my/ui'
|
||||
import { ToastViewport } from './ToastViewport'
|
||||
|
||||
export function Provider({
|
||||
children,
|
||||
defaultTheme = 'light',
|
||||
...rest
|
||||
}: Omit<TamaguiProviderProps, 'config'> & { defaultTheme?: string }) {
|
||||
const colorScheme = useColorScheme()
|
||||
const theme = defaultTheme || (colorScheme === 'dark' ? 'dark' : 'light')
|
||||
|
||||
return (
|
||||
<TamaguiProvider config={config} defaultTheme={theme} {...rest}>
|
||||
<ToastProvider swipeDirection="horizontal" duration={6000} native={isWeb ? [] : ['mobile']}>
|
||||
{children}
|
||||
<CustomToast />
|
||||
<ToastViewport />
|
||||
</ToastProvider>
|
||||
</TamaguiProvider>
|
||||
)
|
||||
}
|
||||
1
packages/app/provider/safe-area/index.tsx
Normal file
1
packages/app/provider/safe-area/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { SafeAreaProvider as SafeArea } from 'react-native-safe-area-context'
|
||||
10
packages/app/provider/safe-area/index.web.tsx
Normal file
10
packages/app/provider/safe-area/index.web.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
// on Web, we don't use React Navigation, so we are going to avoid the safe area provider
|
||||
// instead, we just have a no-op here
|
||||
// for more, see: https://solito.dev/recipes/tree-shaking
|
||||
|
||||
// if you need safe area hooks yourself, you can implement this yourself
|
||||
// however, you may be better off using the CSS selector for env(safe-area-inset-top) on Web
|
||||
|
||||
// for more, see the `./use-safe-area.web.ts` file
|
||||
|
||||
export const SafeArea = ({ children }: { children: React.ReactElement }) => <>{children}</>
|
||||
6
packages/app/provider/safe-area/use-safe-area.ts
Normal file
6
packages/app/provider/safe-area/use-safe-area.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||
|
||||
const useSafeArea = useSafeAreaInsets
|
||||
|
||||
// `export { useSafeAreaInsets as useSafeArea }` breaks autoimport, so do this instead
|
||||
export { useSafeArea }
|
||||
31
packages/app/provider/safe-area/use-safe-area.web.ts
Normal file
31
packages/app/provider/safe-area/use-safe-area.web.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
// I don't use the real useSafeAreaInsets() hook, since
|
||||
// 1) the SafeAreaProvider forces you to render null on Web until it measures
|
||||
// 2) you might not need to support it, unless you're doing landscape stuff
|
||||
// 3) react-native-safe-area-context has a massive import on Web
|
||||
// see: https://github.com/th3rdwave/react-native-safe-area-context/pull/189#issuecomment-815274313
|
||||
// 4) most importantly, I think you can just use the env(safe-area-inset-bottom) CSS variable instead
|
||||
// after all, safe area code is few-and-far-between, so if you have to write some platform-speciifc code for it,
|
||||
// that is probably better than a massive bundle size for little benefit
|
||||
|
||||
import type { useSafeArea as nativeHook } from './use-safe-area'
|
||||
|
||||
const area = {
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
|
||||
// you could also use CSS env variables like below:
|
||||
// but you'll have to be sure to override the types for `useSafeArea`
|
||||
// and make sure to never add numbers and strings when you consue useSafeArea
|
||||
// just keep in mind that the env() doesn't work on older browsers I think
|
||||
|
||||
// top: `env(safe-area-inset-top)`,
|
||||
// right: `env(safe-area-inset-right)`,
|
||||
// bottom: `env(safe-area-inset-bottom)`,
|
||||
// left: `env(safe-area-inset-left)`,
|
||||
}
|
||||
|
||||
export function useSafeArea(): ReturnType<typeof nativeHook> {
|
||||
return area
|
||||
}
|
||||
13
packages/app/tsconfig.json
Normal file
13
packages/app/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base",
|
||||
"include": ["**/*.ts", "**/*.tsx"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"jsx": "react-jsx",
|
||||
"paths": {
|
||||
"app/*": ["./packages/app/*"],
|
||||
"@my/ui/*": ["./packages/ui/*"]
|
||||
}
|
||||
},
|
||||
"references": []
|
||||
}
|
||||
7
packages/app/types.d.ts
vendored
Normal file
7
packages/app/types.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import { config } from '@my/config'
|
||||
|
||||
export type Conf = typeof config
|
||||
|
||||
declare module '@my/ui' {
|
||||
interface TamaguiCustomConfig extends Conf {}
|
||||
}
|
||||
28
packages/config/package.json
Normal file
28
packages/config/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "@my/config",
|
||||
"version": "0.0.1",
|
||||
"sideEffects": [
|
||||
"*.css"
|
||||
],
|
||||
"private": true,
|
||||
"types": "./src",
|
||||
"main": "src/index.ts",
|
||||
"files": [
|
||||
"types",
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tamagui-build --skip-types",
|
||||
"watch": "tamagui-build --skip-types --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tamagui/animations-react-native": "^1.132.18",
|
||||
"@tamagui/font-inter": "^1.132.18",
|
||||
"@tamagui/shorthands": "^1.132.18",
|
||||
"@tamagui/themes": "^1.132.18",
|
||||
"tamagui": "^1.132.18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tamagui/build": "^1.132.18"
|
||||
}
|
||||
}
|
||||
36
packages/config/src/animations.ts
Normal file
36
packages/config/src/animations.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { createAnimations } from '@tamagui/animations-react-native'
|
||||
|
||||
export const animations = createAnimations({
|
||||
'100ms': {
|
||||
type: 'timing',
|
||||
duration: 100,
|
||||
},
|
||||
bouncy: {
|
||||
damping: 9,
|
||||
mass: 0.9,
|
||||
stiffness: 150,
|
||||
},
|
||||
lazy: {
|
||||
damping: 18,
|
||||
stiffness: 50,
|
||||
},
|
||||
medium: {
|
||||
damping: 15,
|
||||
stiffness: 120,
|
||||
mass: 1,
|
||||
},
|
||||
slow: {
|
||||
damping: 15,
|
||||
stiffness: 40,
|
||||
},
|
||||
quick: {
|
||||
damping: 20,
|
||||
mass: 1.2,
|
||||
stiffness: 250,
|
||||
},
|
||||
tooltip: {
|
||||
damping: 10,
|
||||
mass: 0.9,
|
||||
stiffness: 100,
|
||||
},
|
||||
})
|
||||
45
packages/config/src/fonts.ts
Normal file
45
packages/config/src/fonts.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { createInterFont } from '@tamagui/font-inter'
|
||||
|
||||
export const headingFont = createInterFont({
|
||||
size: {
|
||||
6: 15,
|
||||
},
|
||||
transform: {
|
||||
6: 'uppercase',
|
||||
7: 'none',
|
||||
},
|
||||
weight: {
|
||||
6: '400',
|
||||
7: '700',
|
||||
},
|
||||
color: {
|
||||
6: '$colorFocus',
|
||||
7: '$color',
|
||||
},
|
||||
letterSpacing: {
|
||||
5: 2,
|
||||
6: 1,
|
||||
7: 0,
|
||||
8: -1,
|
||||
9: -2,
|
||||
10: -3,
|
||||
12: -4,
|
||||
14: -5,
|
||||
15: -6,
|
||||
},
|
||||
face: {
|
||||
700: { normal: 'InterBold' },
|
||||
},
|
||||
})
|
||||
|
||||
export const bodyFont = createInterFont(
|
||||
{
|
||||
face: {
|
||||
700: { normal: 'InterBold' },
|
||||
},
|
||||
},
|
||||
{
|
||||
sizeSize: (size) => Math.round(size * 1.1),
|
||||
sizeLineHeight: (size) => Math.round(size * 1.1 + (size > 20 ? 10 : 10)),
|
||||
}
|
||||
)
|
||||
1
packages/config/src/index.ts
Normal file
1
packages/config/src/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './tamagui.config'
|
||||
17
packages/config/src/tamagui.config.ts
Normal file
17
packages/config/src/tamagui.config.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { defaultConfig } from '@tamagui/config/v4'
|
||||
import { createTamagui } from 'tamagui'
|
||||
import { bodyFont, headingFont } from './fonts'
|
||||
import { animations } from './animations'
|
||||
|
||||
export const config = createTamagui({
|
||||
...defaultConfig,
|
||||
animations,
|
||||
fonts: {
|
||||
body: bodyFont,
|
||||
heading: headingFont,
|
||||
},
|
||||
settings:{
|
||||
...defaultConfig.settings,
|
||||
onlyAllowShorthands: false
|
||||
}
|
||||
})
|
||||
5
packages/ui/.gitignore
vendored
Normal file
5
packages/ui/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
dist/
|
||||
.DS_Store
|
||||
THUMBS_DB
|
||||
node_modules/
|
||||
types/
|
||||
27
packages/ui/package.json
Normal file
27
packages/ui/package.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "@my/ui",
|
||||
"version": "0.0.1",
|
||||
"sideEffects": [
|
||||
"*.css"
|
||||
],
|
||||
"private": true,
|
||||
"types": "./src",
|
||||
"main": "src/index.tsx",
|
||||
"module:jsx": "src",
|
||||
"files": [
|
||||
"types",
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tamagui-build --skip-types",
|
||||
"watch": "tamagui-build --skip-types --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@my/config": "0.0.1",
|
||||
"@tamagui/toast": "^1.132.18",
|
||||
"tamagui": "^1.132.18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tamagui/build": "^1.132.18"
|
||||
}
|
||||
}
|
||||
11
packages/ui/src/CustomToast.tsx
Normal file
11
packages/ui/src/CustomToast.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import Constants, { ExecutionEnvironment } from 'expo-constants'
|
||||
import { NativeToast as Toast } from './NativeToast'
|
||||
|
||||
const isExpo = Constants.executionEnvironment === ExecutionEnvironment.StoreClient
|
||||
|
||||
export const CustomToast = () => {
|
||||
if (isExpo) {
|
||||
return null
|
||||
}
|
||||
return <Toast />
|
||||
}
|
||||
14
packages/ui/src/MyComponent.tsx
Normal file
14
packages/ui/src/MyComponent.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { YStack, styled } from 'tamagui'
|
||||
|
||||
export const MyComponent = styled(YStack, {
|
||||
name: 'MyComponent',
|
||||
bg: 'red',
|
||||
|
||||
variants: {
|
||||
blue: {
|
||||
true: {
|
||||
bg: 'blue',
|
||||
},
|
||||
},
|
||||
} as const,
|
||||
})
|
||||
29
packages/ui/src/NativeToast.tsx
Normal file
29
packages/ui/src/NativeToast.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Toast, useToastState } from '@tamagui/toast'
|
||||
import { YStack } from 'tamagui'
|
||||
|
||||
export const NativeToast = () => {
|
||||
const currentToast = useToastState()
|
||||
|
||||
if (!currentToast || currentToast.isHandledNatively) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Toast
|
||||
key={currentToast.id}
|
||||
duration={currentToast.duration}
|
||||
viewportName={currentToast.viewportName}
|
||||
enterStyle={{ opacity: 0, scale: 0.5, y: -25 }}
|
||||
exitStyle={{ opacity: 0, scale: 1, y: -20 }}
|
||||
y={0}
|
||||
opacity={1}
|
||||
scale={1}
|
||||
animation="quick"
|
||||
>
|
||||
<YStack py="$1.5" px="$2">
|
||||
<Toast.Title lineHeight="$1">{currentToast.title}</Toast.Title>
|
||||
{!!currentToast.message && <Toast.Description>{currentToast.message}</Toast.Description>}
|
||||
</YStack>
|
||||
</Toast>
|
||||
)
|
||||
}
|
||||
9
packages/ui/src/SwitchRouterButton.tsx
Normal file
9
packages/ui/src/SwitchRouterButton.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Anchor, Button } from 'tamagui'
|
||||
|
||||
export const SwitchRouterButton = ({ pagesMode = false }: { pagesMode?: boolean }) => {
|
||||
return (
|
||||
<Anchor text="center" color="$color12" href={pagesMode ? '/' : '/pages-example'}>
|
||||
<Button>Change router: {pagesMode ? 'pages' : 'app'}</Button>
|
||||
</Anchor>
|
||||
)
|
||||
}
|
||||
16
packages/ui/src/SwitchThemeButton.tsx
Normal file
16
packages/ui/src/SwitchThemeButton.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { useState } from 'react'
|
||||
import { Button, useIsomorphicLayoutEffect } from 'tamagui'
|
||||
import { useThemeSetting, useRootTheme } from '@tamagui/next-theme'
|
||||
|
||||
export const SwitchThemeButton = () => {
|
||||
const themeSetting = useThemeSetting()
|
||||
const [theme] = useRootTheme()
|
||||
|
||||
const [clientTheme, setClientTheme] = useState<string | undefined>('light')
|
||||
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
setClientTheme(themeSetting.forcedTheme || themeSetting.current || theme)
|
||||
}, [themeSetting.current, themeSetting.resolvedTheme])
|
||||
|
||||
return <Button onPress={themeSetting.toggle}>Change theme: {clientTheme}</Button>
|
||||
}
|
||||
7
packages/ui/src/index.tsx
Normal file
7
packages/ui/src/index.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
export * from 'tamagui'
|
||||
export * from '@tamagui/toast'
|
||||
export * from './MyComponent'
|
||||
export { config } from '@my/config'
|
||||
export * from './CustomToast'
|
||||
export * from './SwitchThemeButton'
|
||||
export * from './SwitchRouterButton'
|
||||
7
packages/ui/src/types.d.ts
vendored
Normal file
7
packages/ui/src/types.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import { config } from '@my/config'
|
||||
|
||||
export type Conf = typeof config
|
||||
|
||||
declare module 'tamagui' {
|
||||
interface TamaguiCustomConfig extends Conf {}
|
||||
}
|
||||
9
packages/ui/tsconfig.json
Normal file
9
packages/ui/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base",
|
||||
"include": ["**/*.ts", "**/*.tsx", "../config/src/tamagui.config.ts"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"references": []
|
||||
}
|
||||
Reference in New Issue
Block a user