Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
697d062
build(deps): update project to Expo 54
Glazzes Oct 6, 2025
6788d83
fix(example): update cropzoom examples for modern devices
Glazzes Oct 6, 2025
be6f3e9
refactor: remove clamp function and runOnJS for react native worklets
Glazzes Oct 6, 2025
bdbbcc3
fix(test): add testing dependencies
Glazzes Oct 6, 2025
24ff75c
docs: update installation guide
Glazzes Oct 6, 2025
eb7d3a2
feature: useZoomCallbacks
Glazzes Oct 8, 2025
f43fb11
refactor: change 'resumable' for 'common' on useTransformation state …
Glazzes Oct 8, 2025
9aea1f6
fix: appbar height
Glazzes Oct 29, 2025
6055e4b
chore: remove type SharedRumber for SharedValue<number>
Glazzes Oct 29, 2025
f98eba4
feature: rtl support
Glazzes Oct 29, 2025
120c701
chore: export useZoomCallbacks hooks
Glazzes Oct 29, 2025
9c42a05
refactor: make timingConfig a common parameter
Glazzes Oct 30, 2025
242c5bc
refactor: rename SizeVector type to Size
Glazzes Oct 30, 2025
917616f
docs: rtl feature
Glazzes Oct 30, 2025
d18cfc1
feature(gallery): add snapTransitionConfig property
Glazzes Oct 30, 2025
d62a5c1
docs: add snapTimingConfig entry to gallery
Glazzes Oct 30, 2025
1404746
feature(snapback): add minScale and maxScale
Glazzes Oct 31, 2025
a8df1db
docs(snapback): add minScale and maxScale entries
Glazzes Oct 31, 2025
7b33ba6
fix: linter issues
Glazzes Oct 31, 2025
7b1764f
build: bump node version to 24
Glazzes Oct 31, 2025
fe7dbe4
docs: add useZoomCallbacks entry
Glazzes Oct 31, 2025
bb13908
refactor: useZoomCallbacks
Glazzes Oct 31, 2025
271f1c3
fix(gallery): reset zoom state when setting a new index
Glazzes Nov 10, 2025
2c91f08
fix: sudden jump on iOS 26.0.1 and 26.1
Glazzes Nov 19, 2025
c6ece47
chore: update gitignore
Glazzes Nov 20, 2025
53dd38f
feat: add velocity to onVerticalPull
Glazzes Nov 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ project.xcworkspace
local.properties
android.iml

# Cocoapods
#
example/ios/Pods
# Native files
example/ios/
example/android/

# Ruby
example/vendor/
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v18
v24
5 changes: 1 addition & 4 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: [
'@babel/plugin-proposal-export-namespace-from',
'react-native-reanimated/plugin',
],
plugins: ['react-native-worklets/plugin'],
};
6 changes: 5 additions & 1 deletion docs/docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default defineConfig({
nav: [
{ text: 'Home', link: '/' },
{
text: '5.0.0',
text: '6.0.0',
items: [
{
text: 'Releases',
Expand Down Expand Up @@ -52,6 +52,10 @@ export default defineConfig({
text: 'useTransformationState',
link: '/utilities/usetransformationstate',
},
{
text: 'useZoomCallbacks',
link: '/utilities/useZoomCallbacks',
},
],
},
{
Expand Down
23 changes: 19 additions & 4 deletions docs/docs/components/gallery.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,21 +176,36 @@ Blank space between items.
| -------- | ------- |
| `number` | `0` |

Sets the initial position of the list.
Set the initial position of the list.

### rtl
| Type | Default |
| --------- | ------- |
| `boolean` | `false` |

Invert the gallery's orientation in horizontal mode, [more info](https://github.com/Glazzes/react-native-zoom-toolkit/issues/99#issuecomment-3029442065).

### vertical

| Type | Default |
| --------- | ------- |
| `boolean` | `false` |

Modifies the orientation of the component to vertical mode.
Modify the orientation of the component to vertical mode.

### snapTimingConfig

| Type | Default | Adittional Info |
| -------- | ------------| --------------- |
| `object` | `undefined` | see [timingConfig](https://docs.swmansion.com/react-native-reanimated/docs/animations/withTiming/#config-) |

Set the timing config used to snap between gallery's elements.

### maxScale

| Type | Default |
| -------------------------------- | ------- |
| `SizeVector<number>[] \| number` | `6` |
| `Size<number>[] \| number` | `6` |

Maximum scale value allowed by the pinch gesture for all elements, expects values bigger than or equals one.

Expand Down Expand Up @@ -498,4 +513,4 @@ Reset all transformations to their initial state.
| `direction` | `vertical \| horizontal` | Direction of the gallery. |
| `isScrolling` | `boolean` | Whether the gallery is actively being scrolled. |
| `scroll` | `number` | Current scroll value. |
| `gallerySize` | `SizeVector<number>` | Width and height of the gallery. |
| `gallerySize` | `Size<number>` | Width and height of the gallery. |
4 changes: 2 additions & 2 deletions docs/docs/components/resumablezoom.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,13 @@ Minimum scale value allowed by the pinch gesture, expects values greater than or

| Type | Default |
| ------------------------------ | ------- |
| `SizeVector<number> \| number` | `6` |
| `Size<number> \| number` | `6` |

Maximum scale value allowed by the pinch gesture, expects values bigger than or equals one.

Alternatively you can pass the resolution of your image/video, for instance `{ width: 1920, height: 1080 }`;
this will instruct the component to calculate `maxScale` in such a way it's a value just before your content
starts to pixelate.
starts pixelating.

### panMode

Expand Down
20 changes: 20 additions & 0 deletions docs/docs/components/snapbackzoom.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,26 @@ All properties for this component are optional.

Increase (Android only) or decrease the gesture detection area around your component in all directions by a given amount in pixels, useful when dealing with small components.

### minScale

| Type | Default |
| -------- | ------- |
| `number` | `0` |

Minimum scale value allowed by the pinch gesture, expects values greater than or equals one.

### maxScale

| Type | Default |
| ------------------------------ | ------- |
| `Size<number> \| number` | `6` |

Maximum scale value allowed by the pinch gesture, expects values bigger than or equals one.

Alternatively you can pass the resolution of your image/video, for instance `{ width: 1920, height: 1080 }`;
this will instruct the component to calculate `maxScale` in such a way it's a value just before your content
starts pixelating.

### timingConfig

| Type | Default | Additional Info |
Expand Down
25 changes: 23 additions & 2 deletions docs/docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,29 @@ This library relies on both Reanimated and Gesture Handler making part of your p

| React Native Version | React Native Zoom Toolkit Version | Gesture Handler version |
| -------------------- | --------------------------------- | ----------------------- |
| `<= 0.76` | `>= 3.0.0` | 2.16.0 and beyond. |
| `>= 0.76` | `>= 4.0.0` | 2.19.0 and beyond. |
| `<= 0.76` | `>= 3.0.0` | 2.16.0 and above. |
| `>= 0.76` | `>= 4.0.0` | 2.19.0 and above. |
| `>= 0.81` | `>= 6.0.0` | 2.19.0 and above. |

For Expo 54 and above.

::: code-group

```sh [npm]
npm install react-native-zoom-toolkit react-native-gesture-handler react-native-reanimated react-native-worklets
```

```sh [yarn]
yarn add react-native-zoom-toolkit react-native-gesture-handler react-native-reanimated react-native-worklets
```

```sh [expo]
npx expo install react-native-zoom-toolkit react-native-gesture-handler react-native-reanimated react-native-worklets
```

:::

For Expo 53 and below.

::: code-group

Expand Down
36 changes: 36 additions & 0 deletions docs/docs/utilities/useZoomCallbacks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
title: useZoomCallbacks hook
outline: deep
---

# useZoomCallbacks hook

A simple utility for `ResumableZoom`, `CropZoom` and `Gallery` components to handle common zoom events such as

| Event Name | Description |
|-------------------|------------------------------------------------------------------------------------|
| onUpdate | Callback triggered every time scale is updated |
| onStart | Callback triggered the very fist time scale value changes |
| onEnd | Callback triggered once the scale value has returned to its base state |
| onMaxScaleReached | Callback triggered once the scale value is greater than equals the max scale value |

## How to use

useZoomCallbacks is dependant on the zoom component's current state, therefore you need to use useTransformState hook in order to get such values.

```tsx {3}
const {state, onUpdate} = useTransformationState("common")
useZoomCallbacks(
state,
{
onUpdate: (scale) => console.log("Current scale value ", scale),
onStart: () => console.log("Zoom started"),
onEnd: () => console.log("Zoom finished"),
onMaxScaleReached: (scale) => console.log("Reached max scale at", scale)
}
)

<ResumableZoom onUpdate={onUpdate}>
<Image />
</ResumableZoom>
```
5 changes: 2 additions & 3 deletions docs/docs/utilities/usetransformationstate.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ your choice, for a hands on example see [How to use with Skia Components](../gui

When calling this hook you will receive the three following properties:

- `onUpdate` is a worklet function which must be passed as a property to the zoom component's onUpdate
- `onUpdate` is a [worklet](https://docs.swmansion.com/react-native-reanimated/docs/guides/worklets/) which must be passed as a property to the zoom component's onUpdate
callback property, this way the zoom component will update transform and state properties.
- `transform` is a shared value describing zoom component's current transformations as an array.
- `state` is an object holding the shared values describing the current transformation state, in case
you need them.
- `state` is an object holding the [Reanimated's shared values](https://docs.swmansion.com/react-native-reanimated/docs/2.x/fundamentals/shared-values/) describing the current transformation state in case you need them.

```tsx{9,21}
import { useTransformationState } from 'react-native-zoom-toolkit';
Expand Down
29 changes: 29 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { fixupConfigRules } from '@eslint/compat';
import { FlatCompat } from '@eslint/eslintrc';
import js from '@eslint/js';
import prettier from 'eslint-plugin-prettier';
import { defineConfig } from 'eslint/config';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all,
});

export default defineConfig([
{
extends: fixupConfigRules(compat.extends('@react-native', 'prettier')),
plugins: { prettier },
rules: {
'react/react-in-jsx-scope': 'off',
'prettier/prettier': 'error',
},
},
{
ignores: ['node_modules/', 'lib/', 'docs/'],
},
]);
2 changes: 0 additions & 2 deletions example/babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ module.exports = function (api) {
},
},
],
'@babel/plugin-proposal-export-namespace-from',
'react-native-reanimated/plugin',
],
};
};
39 changes: 10 additions & 29 deletions example/metro.config.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,24 @@
const path = require('path');
const escape = require('escape-string-regexp');
const { getDefaultConfig } = require('@expo/metro-config');
const exclusionList = require('metro-config/src/defaults/exclusionList');
const pak = require('../package.json');

const root = path.resolve(__dirname, '..');
const modules = Object.keys({ ...pak.peerDependencies });

const defaultConfig = getDefaultConfig(__dirname);

/**
* Metro configuration
* https://facebook.github.io/metro/docs/configuration
*
* @type {import('metro-config').MetroConfig}
*/
const config = {
...defaultConfig,

projectRoot: __dirname,
watchFolders: [root],

// We need to make sure that only one version is loaded for peerDependencies
// So we block them at the root, and alias them to the versions in example's node_modules
resolver: {
...defaultConfig.resolver,
module.exports = (async () => {
// 👇 usamos dynamic import
const { withMetroConfig } = await import('react-native-monorepo-config');

blacklistRE: exclusionList(
modules.map(
(m) =>
new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`)
)
),
const config = withMetroConfig(getDefaultConfig(__dirname), {
root,
dirname: __dirname,
});

extraNodeModules: modules.reduce((acc, name) => {
acc[name] = path.join(__dirname, 'node_modules', name);
return acc;
}, {}),
},
};
config.resolver.unstable_enablePackageExports = true;

module.exports = config;
return config;
})();
46 changes: 24 additions & 22 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,38 @@
"web": "expo start --web"
},
"dependencies": {
"@expo/metro-runtime": "~5.0.4",
"@expo/metro-runtime": "~6.1.2",
"@react-navigation/drawer": "^7.0.0",
"@shopify/react-native-skia": "v2.0.0-next.4",
"expo": "^53.0.9",
"expo-av": "~15.1.4",
"expo-constants": "~17.1.6",
"expo-image": "~2.1.7",
"expo-image-manipulator": "~13.1.7",
"expo-linking": "~7.1.5",
"expo-media-library": "~17.1.6",
"expo-router": "~5.0.7",
"expo-screen-orientation": "~8.1.6",
"expo-status-bar": "~2.2.3",
"@shopify/react-native-skia": "2.2.12",
"expo": "~54.0.10",
"expo-av": "~16.0.7",
"expo-constants": "~18.0.9",
"expo-image": "~3.0.8",
"expo-image-manipulator": "~14.0.7",
"expo-linking": "~8.0.8",
"expo-media-library": "~18.2.0",
"expo-router": "~6.0.8",
"expo-screen-orientation": "~9.0.7",
"expo-status-bar": "~3.0.8",
"fbemitter": "^3.0.0",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-native": "0.79.2",
"react-native-gesture-handler": "~2.24.0",
"react-native-image-viewing": "^0.2.2",
"react-native-reanimated": "~3.17.4",
"react-native-safe-area-context": "5.4.0",
"react-native-screens": "~4.10.0",
"react-native-web": "^0.20.0"
"react": "19.1.0",
"react-dom": "19.1.0",
"react-native": "0.81.4",
"react-native-gesture-handler": "~2.28.0",
"react-native-reanimated": "~4.1.1",
"react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.16.0",
"react-native-web": "^0.21.0",
"react-native-worklets": "0.5.1"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@expo/webpack-config": "~19.0.1",
"@types/fbemitter": "^2.0.35",
"babel-loader": "^8.1.0",
"babel-plugin-module-resolver": "^5.0.0"
"babel-plugin-module-resolver": "^5.0.0",
"react-native-builder-bob": "^0.40.13",
"react-native-monorepo-config": "^0.1.10"
},
"private": true
}
Loading