diff --git a/apps/mobile/babel.config.js b/apps/mobile/babel.config.js index 7f586b895bc..330a13bed64 100644 --- a/apps/mobile/babel.config.js +++ b/apps/mobile/babel.config.js @@ -12,7 +12,10 @@ module.exports = function (api) { require('@tamagui/babel-plugin/dist/cjs/index.native'), { components: ['tamagui'], - config: path.join(__dirname, '../../packages/components/tamagui.config.ts'), + config: path.join( + __dirname, + '../../packages/components/tamagui.config.ts', + ), importsWhitelist: [], logTimings: true, disableExtraction: process.env.NODE_ENV === 'development', diff --git a/packages/components/src/Screen/index.native.tsx b/packages/components/src/Screen/index.native.tsx new file mode 100644 index 00000000000..f1874391126 --- /dev/null +++ b/packages/components/src/Screen/index.native.tsx @@ -0,0 +1,32 @@ +import type { PropsWithChildren } from 'react'; +import { useEffect, useState } from 'react'; + +import { useNavigation } from '@react-navigation/native'; + +import useIsLowPerformanceDevice from '../hooks/useIsLowPerformanceDevice'; +import { Spinner } from '../Spinner'; +import { Stack } from '../Stack'; + +const Loading = () => ( + + + +); + +function LoadingScreen({ children }: PropsWithChildren) { + const [isTransitionEnd, setIsTransitionEnd] = useState(false); + const navigation = useNavigation(); + useEffect(() => { + // 'onTransitionEnd' event is missing in react navigation types + const unsubscribe = navigation.addListener('transitionEnd' as any, () => { + setIsTransitionEnd(true); + }); + return unsubscribe; + }, [navigation]); + return isTransitionEnd ? children : ; +} + +export function Screen({ children }: PropsWithChildren) { + const isLowPerformanceDevice = useIsLowPerformanceDevice(); + return isLowPerformanceDevice ? LoadingScreen : children; +} diff --git a/packages/components/src/Screen/index.tsx b/packages/components/src/Screen/index.tsx new file mode 100644 index 00000000000..f251dc97fa1 --- /dev/null +++ b/packages/components/src/Screen/index.tsx @@ -0,0 +1,5 @@ +import type { PropsWithChildren } from 'react'; + +export function Screen({ children }: PropsWithChildren) { + return children; +} diff --git a/packages/components/src/Skeleton/index.tsx b/packages/components/src/Skeleton/index.tsx new file mode 100644 index 00000000000..5fb6e1626ee --- /dev/null +++ b/packages/components/src/Skeleton/index.tsx @@ -0,0 +1,47 @@ +import { MotiView } from 'moti'; +import { Skeleton as MotiSkeleton } from 'moti/skeleton'; +import { StyleSheet } from 'react-native'; + +import useTheme from '../Provider/hooks/useTheme'; + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: 'center', + }, + padded: { + padding: 16, + }, +}); + +const Spacer = ({ height = 16 }: { height: number }) => ( + +); + +export function Skeleton() { + const { themeVariant } = useTheme(); + return ( + + + + + + + + + + ); +} diff --git a/packages/components/src/hooks/useIsLowPerformanceDevice.ts b/packages/components/src/hooks/useIsLowPerformanceDevice.ts new file mode 100644 index 00000000000..4588fcd64b6 --- /dev/null +++ b/packages/components/src/hooks/useIsLowPerformanceDevice.ts @@ -0,0 +1,6 @@ +// import platformEnv from "@onekeyhq/shared/src/platformEnv"; + +export default function useIsLowPerformanceDevice() { + // return !platformEnv.isDev ? IsLowPerformanceDevice : false; + return false; +} diff --git a/packages/components/src/hooks/useKeyboardHeight.ts b/packages/components/src/hooks/useKeyboardHeight.ts index 4cbbb94a0f0..d891cab8909 100644 --- a/packages/components/src/hooks/useKeyboardHeight.ts +++ b/packages/components/src/hooks/useKeyboardHeight.ts @@ -9,11 +9,11 @@ export default function useKeyboardHeight() { const handleKeyboardWillShow: KeyboardEventListener = useCallback((e) => { setKeyboardHeight(e.endCoordinates.height); - }); + }, []); // const handleKeyboardDidShow: KeyboardEventListener = useCallback((e) => {}); - const handleKeyboardWillHide: KeyboardEventListener = useCallback((e) => { + const handleKeyboardWillHide: KeyboardEventListener = useCallback(() => { setKeyboardHeight(0); - }); + }, []); // const handleKeyboardDidHide: KeyboardEventListener = useCallback((e) => {}); useEffect(() => { @@ -27,7 +27,7 @@ export default function useKeyboardHeight() { return () => { subscriptions.forEach((subscription) => subscription.remove()); }; - }, []); + }, [handleKeyboardWillHide, handleKeyboardWillShow]); return keyboardHeight; } diff --git a/packages/components/src/index.tsx b/packages/components/src/index.tsx index 7c2af56ba21..1d9ec761b04 100644 --- a/packages/components/src/index.tsx +++ b/packages/components/src/index.tsx @@ -38,6 +38,8 @@ export * from './Provider/hooks/useThemeValue'; export * from './Provider/hooks/useIsMounted'; export * from './QRCode'; export * from './CollapsibleTabView'; +export * from './Skeleton'; +export * from './Screen'; export * as DelayedFreeze from './DelayedFreeze'; // Navigation diff --git a/packages/kit/src/routes/Gallery/index.tsx b/packages/kit/src/routes/Gallery/index.tsx index c17af13cfd5..6420dd6d357 100644 --- a/packages/kit/src/routes/Gallery/index.tsx +++ b/packages/kit/src/routes/Gallery/index.tsx @@ -28,6 +28,7 @@ import AlertGallery from '../../views/Components/stories/Alert'; import DividerGallery from '../../views/Components/stories/Divider'; import FormGallery from '../../views/Components/stories/Form'; import QRCodeGallery from '../../views/Components/stories/QRCode'; +import SkeletonGallery from '../../views/Components/stories/Skeleton'; import TextAreaGallery from '../../views/Components/stories/TextArea'; import ThemeGallery from '../../views/Components/stories/Theme'; @@ -39,6 +40,7 @@ export enum GalleryRoutes { ComponentIcon = 'component/icon', ComponentButton = 'component/button', ComponentSelect = 'component/select', + ComponentSkeleton = 'component/skeleton', ComponentIconButton = 'component/iconButton', ComponentBadge = 'component/badge', ComponentDialog = 'component/dialog', @@ -83,6 +85,7 @@ export const stackScreenList = [ { name: GalleryRoutes.ComponentDialog, component: DialogGallery }, { name: GalleryRoutes.ComponentEmpty, component: EmptyGallery }, { name: GalleryRoutes.ComponentRadio, component: RadioGallery }, + { name: GalleryRoutes.ComponentSkeleton, component: SkeletonGallery }, { name: GalleryRoutes.ComponentCheckbox, component: CheckboxGallery }, { name: GalleryRoutes.ComponentToggleGroup, component: ToggleGroupGallery }, { name: GalleryRoutes.ComponentActionList, component: ActionListGallery }, diff --git a/packages/kit/src/views/Components/stories/Radio.tsx b/packages/kit/src/views/Components/stories/Radio.tsx index 9cd671d6b93..0c09d67de05 100644 --- a/packages/kit/src/views/Components/stories/Radio.tsx +++ b/packages/kit/src/views/Components/stories/Radio.tsx @@ -1,7 +1,24 @@ +import { useState } from 'react'; + import { Radio } from '@onekeyhq/components'; import { Layout } from './utils/Layout'; +const RadioExample = () => { + const [radioValue, setRadioValue] = useState(); + return ( + + ); +}; + const RadioGallery = () => ( ( elements={[ { title: 'Default', - element: ( - - ), + element: , }, ]} /> diff --git a/packages/kit/src/views/Components/stories/Skeleton.tsx b/packages/kit/src/views/Components/stories/Skeleton.tsx new file mode 100644 index 00000000000..f7dcf763a00 --- /dev/null +++ b/packages/kit/src/views/Components/stories/Skeleton.tsx @@ -0,0 +1,19 @@ +import { Skeleton } from '@onekeyhq/components'; + +import { Layout } from './utils/Layout'; + +const SelectGallery = () => ( + , + }, + ]} + /> +); + +export default SelectGallery; diff --git a/packages/kit/src/views/Components/stories/utils/Layout.tsx b/packages/kit/src/views/Components/stories/utils/Layout.tsx index fe92b531419..bfe911e4fdc 100644 --- a/packages/kit/src/views/Components/stories/utils/Layout.tsx +++ b/packages/kit/src/views/Components/stories/utils/Layout.tsx @@ -3,6 +3,7 @@ import { ScrollView } from 'tamagui'; import { Button, + Screen, Stack, Text, XStack, @@ -57,90 +58,92 @@ export function Layout({ const dispatch = useDispatch(); const keyboardHeight = useKeyboardHeight(); return ( - - - - - - - {description && ( - - - 使用说明 + + + + + + + + {description && ( + + + 使用说明 + + + + - - + )} + {suggestions && ( + + + 使用建议 + + - - )} - {suggestions && ( - - - 使用建议 + )} + {boundaryConditions?.length > 0 && ( + + + 注意事项 + + - - - )} - {boundaryConditions?.length > 0 && ( + )} - 注意事项 + 组件案例 - - - )} - - - 组件案例 - - - {elements?.map((item, index) => ( - - - {item.title} - {item.description && ( - - {item.description}。 - - )} + + {elements?.map((item, index) => ( + + + {item.title} + {item.description && ( + + {item.description}。 + + )} + + {item.element} - {item.element} - - ))} + ))} + + {children && {children}} - {children && {children}} - - + + ); }