diff --git a/.changeset/clever-pants-approve.md b/.changeset/clever-pants-approve.md new file mode 100644 index 00000000..897f0a09 --- /dev/null +++ b/.changeset/clever-pants-approve.md @@ -0,0 +1,5 @@ +--- +"react-native-reanimated-carousel": patch +--- + +fix: typescript error where MeasuredDimension can be null diff --git a/.changeset/lazy-tips-wave.md b/.changeset/lazy-tips-wave.md new file mode 100644 index 00000000..a6612a3c --- /dev/null +++ b/.changeset/lazy-tips-wave.md @@ -0,0 +1,5 @@ +--- +"react-native-reanimated-carousel": patch +--- + +Add dot animation. diff --git a/.changeset/many-ads-fry.md b/.changeset/many-ads-fry.md new file mode 100644 index 00000000..6f440d70 --- /dev/null +++ b/.changeset/many-ads-fry.md @@ -0,0 +1,5 @@ +--- +'react-native-reanimated-carousel': patch +--- + +fix: make gesture onStart/onUpdate/onEnd (et al) callbacks run as worklets again diff --git a/.changeset/nine-moose-whisper.md b/.changeset/nine-moose-whisper.md new file mode 100644 index 00000000..ead48680 --- /dev/null +++ b/.changeset/nine-moose-whisper.md @@ -0,0 +1,5 @@ +--- +"react-native-reanimated-carousel": patch +--- + +fix app crash when using "onProgressChange" prop as function diff --git a/.changeset/pre.json b/.changeset/pre.json index ecca2da9..12af7ec0 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -1,8 +1,8 @@ { "mode": "pre", - "tag": "alpha", + "tag": "canary", "initialVersions": { - "react-native-reanimated-carousel": "3.5.1" + "react-native-reanimated-carousel": "4.0.0-alpha.12" }, "changesets": [ "bright-socks-change", diff --git a/.changeset/spotty-melons-return.md b/.changeset/spotty-melons-return.md new file mode 100644 index 00000000..2983a92b --- /dev/null +++ b/.changeset/spotty-melons-return.md @@ -0,0 +1,5 @@ +--- +'react-native-reanimated-carousel': patch +--- + +fix: rework code to avoid possible flicker when starting pan (panOffset race condition) diff --git a/.eslintrc.js b/.eslintrc.js index c3f9206e..1439070c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -2,9 +2,11 @@ module.exports = { extends: "@dohooo", rules: { "@typescript-eslint/no-use-before-define": "off", + "@typescript-eslint/member-delimiter-style": "off", "quotes": "off", "@typescript-eslint/quotes": "error", "operator-linebreak": "off", + "@typescript-eslint/indent": "off", }, - plugins: ["jest"], + plugins: ["jest", "prettier"], }; diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d841de23..2916faa6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ yarn > While it's possible to use [`npm`](https://github.com/npm/cli), the tooling is built around [`yarn`](https://classic.yarnpkg.com/), so you'll have an easier time if you use `yarn` for development. -While developing, you can run the [example app](/exampleExpo/) to test your changes. Any changes you make in your library's JavaScript code will be reflected in the example app without a rebuild. If you change any native code, then you'll need to rebuild the example app. +While developing, you can run the [example app](/example/expo/) to test your changes. Any changes you make in your library's JavaScript code will be reflected in the example app without a rebuild. If you change any native code, then you'll need to rebuild the example app. To start build: @@ -23,19 +23,19 @@ yarn dev To run the example app on Android: ```sh -yarn android +cd example/expo && yarn && yarn android ``` To run the example app on iOS: ```sh -yarn ios +cd example/expo && yarn && yarn ios ``` To run the example app on Web: ```sh -yarn web +cd example/expo && yarn && yarn web ``` Make sure your code passes TypeScript and ESLint. Run the following to verify: diff --git a/example/app/src/pages/parallax/index.tsx b/example/app/src/pages/parallax/index.tsx index 40de402a..3c34860e 100644 --- a/example/app/src/pages/parallax/index.tsx +++ b/example/app/src/pages/parallax/index.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import { View } from "react-native"; -import { useSharedValue } from "react-native-reanimated"; +import { useSharedValue, interpolate, Extrapolation } from "react-native-reanimated"; import Carousel, { ICarouselInstance, Pagination, @@ -156,6 +156,7 @@ function Index() { activeDotStyle={{ borderRadius: 100, overflow: "hidden", + backgroundColor: "rgba(0,0,0,0.2)", }} containerStyle={[ isVertical @@ -166,6 +167,10 @@ function Index() { top: 40, } : undefined, + { + gap: 5, + marginBottom: 10, + }, ]} horizontal={!isVertical} renderItem={(item) => ( @@ -179,6 +184,118 @@ function Index() { onPress={onPressPagination} /> + + progress={progress} + data={colors.map((color) => ({ color }))} + size={20} + dotStyle={{ + borderRadius: 16, + backgroundColor: "rgba(0,0,0,0.2)", + }} + activeDotStyle={{ + borderRadius: 8, + width: 40, + height: 30, + overflow: "hidden", + backgroundColor: 'black', + }} + containerStyle={[ + isVertical + ? { + position: "absolute", + width: 20, + right: 5, + top: 40, + } + : undefined, + { + gap: 5, + marginBottom: 10, + alignItems: "center", + }, + ]} + horizontal={!isVertical} + onPress={onPressPagination} + customReanimatedStyle={(progress, index, length) => { + let val = Math.abs(progress - index); + if (index === 0 && progress > length - 1) { + val = Math.abs(progress - length); + } + + return { + transform: [ + { + translateY: interpolate( + val, + [0, 1], + [10, 0], + Extrapolation.CLAMP, + ), + } + ] + } + }} + /> + + + progress={progress} + data={colors.map((color) => ({ color }))} + size={20} + dotStyle={{ + borderRadius: 16, + backgroundColor: "rgba(0,0,0,0.2)", + }} + activeDotStyle={{ + borderRadius: 8, + width: 40, + height: 30, + overflow: "hidden", + }} + containerStyle={[ + isVertical + ? { + position: "absolute", + width: 20, + right: 5, + top: 40, + } + : undefined, + { + gap: 5, + alignItems: "center", + }, + ]} + horizontal={!isVertical} + onPress={onPressPagination} + customReanimatedStyle={(progress, index, length) => { + let val = Math.abs(progress - index); + if (index === 0 && progress > length - 1) { + val = Math.abs(progress - length); + } + + return { + transform: [ + { + translateY: interpolate( + val, + [0, 1], + [10, 0], + Extrapolation.CLAMP, + ), + } + ] + } + }} + renderItem={(item) => ( + + )} + /> + setAutoPlay(!autoPlay)} >{`${ElementsText.AUTOPLAY}:${autoPlay}`} diff --git a/example/app/src/utils/log.ts b/example/app/src/utils/log.ts index 4bd70e1e..55778739 100644 --- a/example/app/src/utils/log.ts +++ b/example/app/src/utils/log.ts @@ -1,6 +1,6 @@ /** * In worklet - * e.g. runOnJS(lop)(...); + * e.g. runOnJS(log)(...); */ export function log(...msg: any) { console.log(...msg); diff --git a/example/expo/app.config.js b/example/expo/app.config.js index 76b7877d..0071381b 100644 --- a/example/expo/app.config.js +++ b/example/expo/app.config.js @@ -19,6 +19,7 @@ export default () => { ], ios: { supportsTablet: true, + bundleIdentifier: "com.rnrc.exampleExpo", }, android: { adaptiveIcon: { diff --git a/example/expo/package.json b/example/expo/package.json index e4544940..75c985fc 100644 --- a/example/expo/package.json +++ b/example/expo/package.json @@ -56,7 +56,7 @@ "gh-pages": "^3.2.3", "react-error-overlay": "6.0.9", "react-native-web": "~0.19.6", - "typescript": "^5.1.3" + "typescript": "^5.5.4" }, "resolutions": { "@babel/core": "^7.18.0", diff --git a/example/expo/yarn.lock b/example/expo/yarn.lock index 2d944438..9242d111 100644 --- a/example/expo/yarn.lock +++ b/example/expo/yarn.lock @@ -11681,7 +11681,7 @@ __metadata: react-native-safe-area-context: 4.6.3 react-native-screens: ~3.22.0 react-native-web: ~0.19.6 - typescript: ^5.1.3 + typescript: ^5.5.4 languageName: unknown linkType: soft @@ -13512,23 +13512,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.1.3": - version: 5.1.6 - resolution: "typescript@npm:5.1.6" +"typescript@npm:^5.5.4": + version: 5.5.4 + resolution: "typescript@npm:5.5.4" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: b2f2c35096035fe1f5facd1e38922ccb8558996331405eb00a5111cc948b2e733163cc22fab5db46992aba7dd520fff637f2c1df4996ff0e134e77d3249a7350 + checksum: b309040f3a1cd91c68a5a58af6b9fdd4e849b8c42d837b2c2e73f9a4f96a98c4f1ed398a9aab576ee0a4748f5690cf594e6b99dbe61de7839da748c41e6d6ca8 languageName: node linkType: hard -"typescript@patch:typescript@^5.1.3#~builtin": - version: 5.1.6 - resolution: "typescript@patch:typescript@npm%3A5.1.6#~builtin::version=5.1.6&hash=5da071" +"typescript@patch:typescript@^5.5.4#~builtin": + version: 5.5.4 + resolution: "typescript@patch:typescript@npm%3A5.5.4#~builtin::version=5.5.4&hash=14eedb" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: f53bfe97f7c8b2b6d23cf572750d4e7d1e0c5fff1c36d859d0ec84556a827b8785077bc27676bf7e71fae538e517c3ecc0f37e7f593be913d884805d931bc8be + checksum: fc52962f31a5bcb716d4213bef516885e4f01f30cea797a831205fc9ef12b405a40561c40eae3127ab85ba1548e7df49df2bcdee6b84a94bfbe3a0d7eff16b14 languageName: node linkType: hard diff --git a/example/website/package.json b/example/website/package.json index bc2c8e8f..08daf007 100644 --- a/example/website/package.json +++ b/example/website/package.json @@ -2,21 +2,21 @@ "name": "nextra-docs-template", "version": "0.0.1", "description": "Nextra docs template", - "scripts": { - "dev": "next dev", - "build": "node scripts/gen-pages.mjs && next build", - "start": "next start" - }, + "author": "Shu Ding ", + "license": "MIT", + "homepage": "https://github.com/shuding/nextra-docs-template#readme", "repository": { "type": "git", "url": "git+https://github.com/shuding/nextra-docs-template.git" }, - "author": "Shu Ding ", - "license": "MIT", "bugs": { "url": "https://github.com/shuding/nextra-docs-template/issues" }, - "homepage": "https://github.com/shuding/nextra-docs-template#readme", + "scripts": { + "dev": "next dev", + "build": "node scripts/gen-pages.mjs && next build", + "start": "next start" + }, "dependencies": { "@vercel/analytics": "^1.1.1", "next": "^13.0.6", @@ -32,6 +32,6 @@ "postcss": "^8.4.32", "react-native-web": "~0.19.6", "tailwindcss": "^3.3.6", - "typescript": "^4.9.3" + "typescript": "^5.5.4" } } diff --git a/example/website/pages/custom-animations.mdx b/example/website/pages/custom-animations.mdx index 5061b19e..c2a437c5 100644 --- a/example/website/pages/custom-animations.mdx +++ b/example/website/pages/custom-animations.mdx @@ -31,7 +31,7 @@ After some effort, we finally implemented custom animation in v2, now we just ne ### Prepare ``` -type TAnimationStyle = (value: number) => Animated.AnimatedStyleProp; +type TAnimationStyle = (value: number) => ViewStyle; ``` This function will be called in each item and accepts a parameter `value` indicating the position of the current item relative to `window`. The following picture shows the relationship between `value` and position diff --git a/example/website/pages/props.mdx b/example/website/pages/props.mdx index ed01f24c..4697a83a 100644 --- a/example/website/pages/props.mdx +++ b/example/website/pages/props.mdx @@ -286,7 +286,7 @@ Custom animations. For details, see below\[custom animation\]\(.\/custom-animati | type | default | required | | ------ | ------- | -------- | -| \(value: number\) => Animated.AnimatedStyleProp\ | - | ❌ | +| \(value: number\) => ViewStyle | - | ❌ | ### `maxScrollDistancePerSwipe` @@ -409,6 +409,89 @@ Slide direction ## Ref +By using these methods, remember you need to reference the component using [React useRef()](https://react.dev/reference/react/useRef). + +**JavaScript** + +```js +const ref = React.useRef(null) +``` + +If you're using **TypeScript**: + +You need to import: + +```ts +import type { ICarouselInstance } from "react-native-reanimated-carousel"; +``` + +and then: + +```ts +const ref = React.useRef(null); +``` + +Now, you only need to pass the ref to the Carousel component: + +```js +; +``` + +And now you can use these methods throughout your component. Here's an example of implementing a button to go to the next slide: + +```tsx +import React from "react"; +import Carousel from "react-native-reanimated-carousel"; +import type { ICarouselInstance } from "react-native-reanimated-carousel"; +import { Button, Text, View } from "react-native"; + +// 1. Create a data array with the slides +const data = [ + { + title: "Slide 1", + content: "Slide 1 Content", + }, + { + title: "Slide 2", + content: "Slide 2 Content", + }, + { + title: "Slide 3", + content: "Slide 3 Content", + }, +]; + +const Example = () => { + const ref = React.useRef(null); // 2. Create a ref for the Carousel component + + return ( + + {/* 3. Add the Carousel component with the ref */} + ( + + {item.title} + {item.content} + + )} + /> + {/* 5. Add a button to trigger the next slide */} +