Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
ce1a753
Sorting imports (#147)
thomasttvo Dec 22, 2025
fa598a6
reanimated
Dec 24, 2025
d1c21ee
set up physical device test
Dec 24, 2025
58f450c
Upgrade example app to react-native 79 (#148)
thomasttvo Dec 24, 2025
9945d2b
Merge branch 'thomas/no-use-before-define' into thomas/reanimated
Dec 24, 2025
7c76154
doc fix
Dec 24, 2025
fac493f
fix crashes
Dec 24, 2025
8604c3c
onTransformWorklet works
Dec 24, 2025
6c2adfe
fix pageSheet modal breaking coordinates by removing originalPageX/Y …
Dec 25, 2025
b7b10af
fix missing gesture center logic
Dec 25, 2025
4427fcb
fix dx, dy
Dec 25, 2025
ebcb7b8
fix accessing props from worklets
Dec 25, 2025
77c9c80
refactor: clean up unused imports and simplify StaticPin component
Dec 25, 2025
d4fe8c0
fix longPress getting called on pinch
Dec 25, 2025
490ae19
add test case for modal mode
Dec 25, 2025
ff5b6c3
Zoomable context
elliottkember Dec 25, 2025
8809509
Woo - no animated value in the consumer
elliottkember Dec 25, 2025
3601884
Add a nice inverted zoom style
elliottkember Dec 26, 2025
fbab873
ConstantSizeMarker
elliottkember Dec 26, 2025
44f0a85
Rename
elliottkember Dec 26, 2025
eea8cb1
unzoomStyle
elliottkember Dec 26, 2025
1019845
pan responder hooks
Dec 29, 2025
341414d
runonjs (#154)
elliottkember Dec 29, 2025
f893a93
ref export
Dec 29, 2025
71fdaf9
Merge remote-tracking branch 'origin/thomas/reanimated' into thomas/r…
Dec 29, 2025
efdd4aa
unnecessary useAnimatedStyle
Dec 29, 2025
7846d04
onZoomEnd
Dec 30, 2025
74070c3
FixedSize
elliottkember Jan 4, 2026
e37a705
Revert math.max change in favour of separate PR
elliottkember Jan 5, 2026
8c6b27f
Just expose inverseZoom, we don't need an animated style for a transform
elliottkember Jan 5, 2026
86f67ca
inverseZoomStyle
elliottkember Jan 5, 2026
4a2d93f
Separate context file
elliottkember Jan 5, 2026
a327775
Merge branch 'thomas/reanimated' into elliott/reanimated-context
elliottkember Jan 5, 2026
591db01
Bugbot
elliottkember Jan 6, 2026
d23d301
Forgot a file
elliottkember Jan 6, 2026
5193634
Merge pull request #153 from openspacelabs/elliott/reanimated-context
elliottkember Jan 6, 2026
4545946
useZoomableViewContext
elliottkember Jan 6, 2026
f61f498
Forgot to export
elliottkember Jan 6, 2026
678b1c2
Try it without the -worklet suffix and with a Worklet type (which tru…
elliottkember Jan 6, 2026
1540cb2
Bump version
elliottkember Jan 6, 2026
5c0ce1f
CI: move build to release workflow (#158)
thomasttvo Apr 13, 2026
bcb7bba
Merge remote-tracking branch 'origin/master' into thomas/reanimated
Apr 13, 2026
468bd2d
Strip lib/ build artifacts made redundant by #158
Apr 13, 2026
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
24 changes: 18 additions & 6 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
name: Checks
on: push
on:
push:
branches:
- master
pull_request:

jobs:
build:
checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install modules
run: yarn
- name: Run tsc
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'yarn'
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Run TypeScript
run: yarn run typescript
- name: Run ESLint
run: yarn run lint
- name: Build
run: yarn build

35 changes: 35 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Release

on:
release:
types: [published]

jobs:
publish:
if: ${{ !github.event.release.prerelease }}
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'
cache: 'yarn'

- name: Verify npm version
run: npm --version

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Publish to npm
# --access public: Scoped packages (@openspacelabs/*) default to private, this makes it public
# id-token: write (above) enables --provenance for signed attestations, not registry auth
run: npm publish --access public --provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ npm-debug.log
yarn-debug.log
yarn-error.log

# Build output
/lib/

# BUCK
buck-out/
\.buckd/
Expand Down
25 changes: 19 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,30 @@ Our pre-commit hooks verify that your commit message matches this format when co

We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing.

Our pre-commit hooks verify that the linter and tests pass when committing.
Our pre-commit hooks verify that the linter and type checks pass when committing.

### Build Process

The library source code in `src/` is built to `lib/` for distribution. The `lib/` directory is not tracked in git.

- **Local development**: No build needed - the example app reads directly from `src/`
- **Pull requests**: CI automatically builds to verify there are no build errors
- **Releases**: GitHub Actions automatically builds and publishes to npm

You don't need to run build commands locally unless testing the built output.

### Publishing to npm

We use [release-it](https://github.com/release-it/release-it) to make it easier to publish new versions. It handles common tasks like bumping version based on semver, creating tags and releases etc.
We use [release-it](https://github.com/release-it/release-it) to create releases and [GitHub Actions](.github/workflows/release.yml) to build and publish to npm.

To publish new versions, run the following:
To publish new versions:

```sh
yarn release
```
1. Run `yarn release` locally (creates git tag and GitHub release)
2. GitHub Actions automatically builds and publishes to npm

Only maintainers with permission to create releases can publish. The repository must have an `NPM_TOKEN` secret configured (Settings → Secrets and variables → Actions) for CI to publish to npm.

> **Note:** GitHub pre-releases (e.g. `v3.0.0-beta.1`) intentionally skip the npm publish step to prevent beta versions from being tagged as `latest`. The CI job will show as "Skipped" — this is expected. To publish a pre-release manually, build locally and run `npm publish --tag beta --access public`.

### Scripts

Expand Down
44 changes: 18 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ These options can be used to limit and change the zoom behavior.
| zoomStep | number | How much zoom should be applied on double tap | 0.5 |
| pinchToZoomInSensitivity | number | the level of resistance (sensitivity) to zoom in (0 - 10) - higher is less sensitive | 3 |
| pinchToZoomOutSensitivity | number | the level of resistance (sensitivity) to zoom out (0 - 10) - higher is less sensitive | 1 |
| movementSensibility | number | how resistant should shifting the view around be? (0.5 - 5) - higher is less sensitive | 1.9 |
| movementSensitivity | number | how resistant should shifting the view around be? (0.5 - 5) - higher is less sensitive | 1.9 |
| initialOffsetX | number | The horizontal offset the image should start at | 0 |
| initialOffsetY | number | The vertical offset the image should start at | 0 |
| contentHeight | number | Specify if you want to treat the height of the **centered** content inside the zoom subject as the zoom subject's height | undefined |
Expand All @@ -182,19 +182,15 @@ These optional props can be used to keep a "static" pin in the centre of the scr

These events can be used to work with data after specific events.

| name | description | params | expected return |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| onTransform | Will be called when the transformation configuration (zoom level and offset) changes | zoomableViewEventObject | void |
| onDoubleTapBefore | Will be called at the start of a double tap | event, gestureState, zoomableViewEventObject | void |
| onDoubleTapAfter | Will be called at the end of a double tap | event, gestureState, zoomableViewEventObject | void |
| onShiftingBefore | Will be called when user taps and moves the view, but before our view movement work kicks in (so this is the place to interrupt movement, if you need to) | event, gestureState, zoomableViewEventObject | {boolean} if this returns true, ZoomableView will not process the shift, otherwise it will |
| onShiftingAfter | Will be called when user taps and moves the view, but after the values have changed already | event, gestureState, zoomableViewEventObject | void |
| onShiftingEnd | Will be called when user stops a tap and move gesture | event, gestureState, zoomableViewEventObject | void |
| onZoomBefore | Will be called while the user pinches the screen, but before our zoom work kicks in (so this is the place to interrupt zooming, if you need to) | event, gestureState, zoomableViewEventObject | {boolean} if this returns true, ZoomableView will not process the pinch, otherwise it will |
| onZoomAfter | Will be called while the user pinches the screen, but after the values have changed already | event, gestureState, zoomableViewEventObject | {boolean} if this returns true, ZoomableView will not process the pinch, otherwise it will |
| onZoomEnd | Will be called after pinchzooming has ended | event, gestureState, zoomableViewEventObject | {boolean} if this returns true, ZoomableView will not process the pinch, otherwise it will |
| onLongPress | Will be called after the user pressed on the image for a while | event, gestureState | void |
| onLayout | Like `View`'s `onLayout`, but different in that it syncs with this component's internal state and returns a fake sythentic event | Like `View`'s `onLayout` but the synthetic event is fake | void |
| name | description | params | expected return |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | --------------- |
| onTransform | Will be called when the transformation configuration (zoom level and offset) changes | zoomableViewEventObject | void |
| onDoubleTapBefore | Will be called at the start of a double tap | event, zoomableViewEventObject | void |
| onDoubleTapAfter | Will be called at the end of a double tap | event, zoomableViewEventObject | void |
| onShiftingEnd | Will be called when user stops a tap and move gesture | event, zoomableViewEventObject | void |
| onZoomEnd | Will be called after pinchzooming has ended | event, zoomableViewEventObject | void |
| onLongPress | Will be called after the user pressed on the image for a while | event | void |
| onLayout | Like `View`'s `onLayout`, but different in that it syncs with this component's internal state and returns a fake sythentic event | Like `View`'s `onLayout` but the synthetic event is fake | void |

#### Methods

Expand Down Expand Up @@ -259,17 +255,15 @@ export default function App() {

#### Pan Responder Hooks

Sometimes you need to change deeper level behavior, so we prepared these panresponder hooks for you.
`react-native-gesture-handler` is now used instead of the built-in PanResponder. As such, we have removed some hooks that
are no longer supported and made the rest backward compatible.

| name | description | params | expected return |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | --------------------------------------------------- |
| onStartShouldSetPanResponder | description | event, gestureState, zoomableViewEventObject, baseComponentResult | {boolean} whether panresponder should be set or not |
| onPanResponderGrant | description | event, gestureState, zoomableViewEventObject | void |
| onPanResponderEnd | Will be called when gesture ends (more accurately, on pan responder "release") | event, gestureState, zoomableViewEventObject | void |
| onPanResponderTerminate | Will be called when the gesture is force-interrupted by another handler | event, gestureState, zoomableViewEventObject | void |
| onPanResponderTerminationRequest | Callback asking whether the gesture should be interrupted by another handler (**iOS only** due to https://github.com/facebook/react-native/issues/27778, https://github.com/facebook/react-native/issues/5696, ...) | event, gestureState, zoomableViewEventObject | void |
| onPanResponderMove | Will be called when user moves while touching | event, gestureState, zoomableViewEventObject | void |
| onShouldBlockNativeResponder | Returns whether this component should block native components from becoming the JS responder | event, gestureState, zoomableViewEventObject | boolean |
| name | description | params | expected return |
| ------------------------- | ------------------------------------------------------------------------------ | ------------------------------ | ------------------------------------------------------------------------------- |
| onPanResponderGrant | description | event, zoomableViewEventObject | void |
| onPanResponderEnd | Will be called when gesture ends (more accurately, on pan responder "release") | event, zoomableViewEventObject | void |
| onPanResponderTerminate | Will be called when the gesture is force-interrupted by another handler | event, zoomableViewEventObject | void |
| onPanResponderMoveWorklet | Will be called when user moves while touching | event, zoomableViewEventObject | {boolean} if true is returned, pinch and shift operations will not be processed |

### zoomableViewEventObject

Expand All @@ -282,8 +276,6 @@ The zoomableViewEventObject object is attached to every event and represents the
offsetY: number, // current offset top
originalHeight: number, // original height of the zoom subject
originalWidth: number, // original width of the zoom subject
originalPageX: number, // original absolute X of the zoom subject
originalPageY: number, // original absolite Y of the zoom subject
}
```

Expand Down
Loading