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}}
-
-
+
+
);
}