diff --git a/app/(app)/(authorized)/(tabs)/example/skia-components.tsx b/app/(app)/(authorized)/(tabs)/example/skia-components.tsx new file mode 100644 index 00000000..0a4ef082 --- /dev/null +++ b/app/(app)/(authorized)/(tabs)/example/skia-components.tsx @@ -0,0 +1,3 @@ +import { SkiaComponentsScreen } from '@baca/screens' + +export default SkiaComponentsScreen diff --git a/package.json b/package.json index 0a76f229..8f378d2a 100644 --- a/package.json +++ b/package.json @@ -106,6 +106,7 @@ "@react-navigation/native": "^6.1.9", "@react-navigation/native-stack": "^6.9.17", "@react-navigation/stack": "^6.3.20", + "@shopify/react-native-skia": "^1.3.6", "@tanstack/react-query": "^4.29.19", "axios": "^1.7.2", "core-js": "^3.37.1", diff --git a/src/components/skia/ProgressBar.tsx b/src/components/skia/ProgressBar.tsx new file mode 100644 index 00000000..7a2210f1 --- /dev/null +++ b/src/components/skia/ProgressBar.tsx @@ -0,0 +1,78 @@ +import { + Canvas, + LinearGradient, + Path, + Skia, + Text, + useFont, + useTouchHandler, + vec, +} from '@shopify/react-native-skia' +import { useEffect } from 'react' +import { useDerivedValue, useSharedValue, withTiming } from 'react-native-reanimated' + +type ProgressBarProps = { + initialProgress: number + height?: number + width?: number +} + +const FONT_SIZE = 24 + +export const ProgressBar = ({ height = 32, initialProgress, width = 200 }: ProgressBarProps) => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const font = useFont(require('@baca/assets/fonts/Inter-Bold.ttf'), FONT_SIZE) + + const path = Skia.Path.Make() + path.moveTo(0, height / 2) + path.lineTo(width, height / 2) + + const progress = useSharedValue(0) + + useEffect(() => { + progress.value = withTiming(initialProgress / 100, { duration: 1000 }) + }, [initialProgress]) + + const onTouch = useTouchHandler({ + onStart: ({ x }) => setNewProgress(x), + onActive: ({ x }) => setNewProgress(x), + }) + + const setNewProgress = (x: number) => { + let newProgress + if (x >= width) { + newProgress = 1 + } else if (x <= 0) { + newProgress = 0 + } else { + newProgress = x / width + } + progress.value = newProgress + } + + const text = useDerivedValue(() => `${Math.floor(progress.value * 100)}%`) + + const textX = useDerivedValue(() => { + const size = font?.measureText(text.value)?.width + + return width / 2 - size! / 2 + }) + + return ( + <> + + + + + + + + + + + ) +} diff --git a/src/i18n/translations/en.json b/src/i18n/translations/en.json index 26a6c44a..ddbf45aa 100644 --- a/src/i18n/translations/en.json +++ b/src/i18n/translations/en.json @@ -188,6 +188,7 @@ "go_to_screen_test_form": "Go to test form", "go_to_screen_with_BEdata": "Go to screen with data from BE", "go_to_settings": "Go to Settings", + "go_to_skia_components": "Go to skia components", "go_to_typography": "Go to Typography", "go_to_user_session": "Go to user session", "header": "This is Example screen" diff --git a/src/i18n/translations/pl.json b/src/i18n/translations/pl.json index c9ebd6af..c8e0aa10 100644 --- a/src/i18n/translations/pl.json +++ b/src/i18n/translations/pl.json @@ -188,6 +188,7 @@ "go_to_screen_test_form": "Idź do formularza testowego", "go_to_screen_with_BEdata": "Idź do widoku z danymi z backend-u", "go_to_settings": "Idź do Ustawień", + "go_to_skia_components": "Idź do Komponentów Skia", "go_to_typography": "Idź do Typografii", "go_to_user_session": "Idź do sesji użytkowania", "header": "To jest przykładowy widok" diff --git a/src/screens/ExamplesScreen.tsx b/src/screens/ExamplesScreen.tsx index caba6d66..c7a49efd 100644 --- a/src/screens/ExamplesScreen.tsx +++ b/src/screens/ExamplesScreen.tsx @@ -24,6 +24,8 @@ export const ExamplesScreen = () => { const goToHomeStackDetails = useCallback(() => push('/home/details'), [push]) + const goToSkiaComponents = useCallback(() => push('/example/skia-components'), [push]) + return ( + ) } diff --git a/src/screens/SkiaComponentsScreen.tsx b/src/screens/SkiaComponentsScreen.tsx new file mode 100644 index 00000000..0da93509 --- /dev/null +++ b/src/screens/SkiaComponentsScreen.tsx @@ -0,0 +1,18 @@ +import { ProgressBar } from '@baca/components/skia/ProgressBar' +import { StyleSheet, View } from 'react-native' + +export const SkiaComponentsScreen = (): JSX.Element => { + return ( + + + + ) +} + +const style = StyleSheet.create({ + container: { + alignItems: 'center', + flex: 1, + justifyContent: 'center', + }, +}) diff --git a/src/screens/index.ts b/src/screens/index.ts index 8eff38bd..694e3893 100644 --- a/src/screens/index.ts +++ b/src/screens/index.ts @@ -12,6 +12,7 @@ export * from './NotFoundScreen' export * from './ProfileScreen' export * from './PushNotificationsHelpersScreen' export * from './SettingsScreen' +export * from './SkiaComponentsScreen' export * from './TestFormScreen' export * from './TypographyScreen' export * from './UserSessionScreen' diff --git a/yarn.lock b/yarn.lock index 6f9b7985..1bfc1b20 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2645,6 +2645,14 @@ component-type "^1.2.1" join-component "^1.1.0" +"@shopify/react-native-skia@^1.3.6": + version "1.3.6" + resolved "https://registry.yarnpkg.com/@shopify/react-native-skia/-/react-native-skia-1.3.6.tgz#074c419e87a1bfb65310b3ebfa74a342c2a07e06" + integrity sha512-AT+yxt0UiAakcqUzbaLCL4kj/9cDlerhuUw2z/U+K7xeudLcox6mbLse98i9E+fUdIctxNFa50ltCo+9hQOPkg== + dependencies: + canvaskit-wasm "0.39.1" + react-reconciler "0.27.0" + "@sideway/address@^4.1.5": version "4.1.5" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" @@ -3625,6 +3633,11 @@ "@webassemblyjs/ast" "1.12.1" "@xtuc/long" "4.2.2" +"@webgpu/types@0.1.21": + version "0.1.21" + resolved "https://registry.yarnpkg.com/@webgpu/types/-/types-0.1.21.tgz#b181202daec30d66ccd67264de23814cfd176d3a" + integrity sha512-pUrWq3V5PiSGFLeLxoGqReTZmiiXwY3jRkIG5sLLKjyqNxrwm/04b4nw7LSmGWJcKk59XOM/YRTUwOzo4MMlow== + "@welldone-software/why-did-you-render@^7.0.1": version "7.0.1" resolved "https://registry.yarnpkg.com/@welldone-software/why-did-you-render/-/why-did-you-render-7.0.1.tgz#09f487d84844bd8e66435843c2e0305702e61efb" @@ -4530,6 +4543,13 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001587: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001621.tgz#4adcb443c8b9c8303e04498318f987616b8fea2e" integrity sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA== +canvaskit-wasm@0.39.1: + version "0.39.1" + resolved "https://registry.yarnpkg.com/canvaskit-wasm/-/canvaskit-wasm-0.39.1.tgz#c3c8f3962cbabbedf246f7bcf90e859013c7eae9" + integrity sha512-Gy3lCmhUdKq+8bvDrs9t8+qf7RvcjuQn+we7vTVVyqgOVO1UVfHpsnBxkTZw+R4ApEJ3D5fKySl9TU11hmjl/A== + dependencies: + "@webgpu/types" "0.1.21" + chalk@5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" @@ -11125,6 +11145,14 @@ react-native@0.74.1: ws "^6.2.2" yargs "^17.6.2" +react-reconciler@0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.27.0.tgz#360124fdf2d76447c7491ee5f0e04503ed9acf5b" + integrity sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.21.0" + react-refresh@^0.14.0, react-refresh@^0.14.2: version "0.14.2" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9" @@ -11584,6 +11612,13 @@ scheduler@0.24.0-canary-efb381bbf-20230505: dependencies: loose-envify "^1.1.0" +scheduler@^0.21.0: + version "0.21.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0.tgz#6fd2532ff5a6d877b6edb12f00d8ab7e8f308820" + integrity sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ== + dependencies: + loose-envify "^1.1.0" + scheduler@^0.23.0, scheduler@^0.23.2: version "0.23.2" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3"