├── .eslintrc.js
├── .github
├── FUNDING.yml
└── workflows
│ └── release.yml
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── demo
├── public
│ ├── unsplash-large.jpg
│ ├── unsplash.jpg
│ ├── unsplash2-large.jpg
│ ├── unsplash2.jpg
│ ├── unsplash3-large.jpg
│ └── unsplash3.jpg
└── src
│ └── index.js
├── nwb.config.js
├── package-lock.json
├── package.json
├── src
├── InnerImageZoom
│ ├── InnerImageZoom.js
│ ├── components
│ │ ├── FullscreenPortal.js
│ │ ├── Image.js
│ │ └── ZoomImage.js
│ ├── index.js
│ ├── styles.css
│ └── styles.min.css
└── index.js
└── tests
├── constants
└── srcs.js
└── index.spec.js
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | browser: true,
5 | node: true
6 | },
7 | parserOptions: {
8 | ecmaVersion: 2020,
9 | sourceType: 'module',
10 | ecmaFeatures: {
11 | jsx: true
12 | }
13 | },
14 | settings: {
15 | react: {
16 | version: 'detect'
17 | }
18 | },
19 | extends: [
20 | 'eslint:recommended',
21 | 'plugin:react/recommended',
22 | 'plugin:react-hooks/recommended',
23 | 'plugin:prettier/recommended'
24 | ],
25 | rules: {
26 | 'prettier/prettier': [
27 | 'error',
28 | {
29 | singleQuote: true,
30 | jsxBracketSameLine: false,
31 | trailingComma: 'none',
32 | printWidth: 120,
33 | endOfLine: 'auto'
34 | }
35 | ]
36 | },
37 | ignorePatterns: ['**/dist/**', '**/es/**', '**/lib/**'],
38 | overrides: [
39 | {
40 | files: ['*.spec.js'],
41 | env: {
42 | mocha: true
43 | }
44 | }
45 | ]
46 | };
47 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: ['laurenashpole']
4 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - main
8 | pull_request:
9 |
10 | jobs:
11 | release:
12 | name: Release
13 | runs-on: ${{ matrix.os }}
14 | strategy:
15 | matrix:
16 | os: [ubuntu-latest, macos-latest, windows-latest]
17 | node-version: [12.x, 14.x, 16.x]
18 | include:
19 | - os: ubuntu-latest
20 | node-version: 16.x
21 | publish: true
22 | steps:
23 | - name: Checkout repo
24 | uses: actions/checkout@v2
25 | with:
26 | fetch-depth: 0
27 | - name: Setup Node.js ${{ matrix.node-version }}
28 | uses: actions/setup-node@v2
29 | with:
30 | node-version: ${{ matrix.node-version }}
31 | - name: Cache modules
32 | uses: actions/cache@v2
33 | with:
34 | path: ~/.npm
35 | key: v1-npm-deps-${{ hashFiles('**/package-lock.json') }}
36 | restore-keys: v1-npm-deps-
37 | - name: Install modules
38 | run: npm install
39 | - name: Run tests
40 | run: npm test
41 | - name: Publish
42 | if: matrix.publish && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main')
43 | env:
44 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
45 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
46 | run: |
47 | npm run build
48 | npm run semantic-release
49 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | /demo/dist
3 | /es
4 | /lib
5 | /node_modules
6 | /umd
7 | npm-debug.log*
8 | .DS_Store
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [3.0.2](https://github.com/laurenashpole/react-inner-image-zoom/compare/v3.0.1...v3.0.2) (2022-07-22)
4 |
5 |
6 | ### Fixed
7 |
8 | - A bug re-zooming after clicking the close button on non-touch devices when `zoomPreload` is false.
9 |
10 | 🎉🎉🎉 Special thanks to [MaxDAyala](https://github.com/MaxdAyala) for tackling the following:
11 |
12 | - A Firefox error when the zoomed image is dragged to the far left of the container.
13 | - The timing of the fade out `visibility` and `opacity` transitions.
14 | - An intermittent issue where zooming became disabled by panning in and out at a fast speed.
15 |
16 | ## [3.0.1](https://github.com/laurenashpole/react-inner-image-zoom/compare/v3.0.0...v3.0.1) (2022-06-12)
17 |
18 | ### Fixed
19 |
20 | - Added `prop-types` to the `peerDependencies`.
21 |
22 | ## [3.0.0](https://github.com/laurenashpole/react-inner-image-zoom/compare/v2.1.0...v3.0.0) (2022-01-03)
23 |
24 | ### Changed
25 |
26 | - Replaced `srcSet`, `sizes`, `alt`, and `title` props with `imgAttributes` to set the original image's attributes.
27 | - Show close button when moveType is set to "drag" on all breakpoints.
28 | - Switched from `setTimeout` to `onTransitionEnd` to check that zoomed image has finished fading out.
29 |
30 | ### Added
31 |
32 | - This handy CHANGELOG.
33 |
34 | ### Fixed
35 |
36 | - Added `stopPropagation` on touchmove to prevent events below fullscreen modal.
37 |
38 | ## [2.1.0](https://github.com/laurenashpole/react-inner-image-zoom/compare/v2.0.3...v2.1.0) (2021-08-30)
39 |
40 | ### Added
41 |
42 | - `title` prop to add attribute to original image.
43 |
44 | ## [2.0.3](https://github.com/laurenashpole/react-inner-image-zoom/compare/v2.0.2...v2.0.3) (2021-08-05)
45 |
46 | ### Changed
47 |
48 | - Use `touch-action` CSS property instead of `preventDefault` to prevent scroll on touchmove and drag.
49 |
50 | ### Fixed
51 |
52 | - Sporadic missing zoom image in fullscreen modal caused by missing dimensions and incorrect positioning.
53 |
54 | ## [2.0.2](https://github.com/laurenashpole/react-inner-image-zoom/compare/v2.0.1...v2.0.2) (2021-06-15)
55 |
56 | ### Fixed
57 |
58 | - Incorrect initial zoom position in fullscreen modal.
59 | - Persist the zoomed image after zoom out if `zoomPreload` is true.
60 |
61 | ## [2.0.1](https://github.com/laurenashpole/react-inner-image-zoom/compare/v2.0.0...v2.0.1) (2021-03-12)
62 |
63 | ### Fixed
64 |
65 | - Set the scaled image size based on `naturalWidth` and `naturalHeight` instead of `offsetWidth` and `offsetHeight`.
66 |
67 | ## [2.0.0](https://github.com/laurenashpole/react-inner-image-zoom/compare/v1.3.0...v2.0.0) (2021-03-03)
68 |
69 | ### Changed
70 |
71 | - Refactored using React hooks. All versions after 2.0.0 require React v16.8.0 or above.
72 | - Renamed `startsActive` to `zoomPreload`
73 |
74 | ### Added
75 |
76 | - `hideHint` prop to hide the magnifying glass icon.
77 | - `hideCloseButton` prop to hide the close button on touch devices.
78 | - `width`, `height`, and `hasSpacer` props to set the original image's width and height attributes and optionally generate a spacer based on those values to avoid cumulative layout shift.
79 | - CONTRIBUTING guide.
80 | - ESLint and Prettier formatting.
81 |
82 | ## [1.3.0](https://github.com/laurenashpole/react-inner-image-zoom/compare/v1.2.0...v1.3.0) (2020-11-24)
83 |
84 | ### Added
85 |
86 | - `zoomScale` prop to set the size of the zoomed image.
87 | - `startsActive` prop to load the zoomed image on render.
88 |
89 | ## [1.2.0](https://github.com/laurenashpole/react-inner-image-zoom/compare/v1.1.1...v1.2.0) (2020-11-21)
90 |
91 | ### Added
92 |
93 | - `zoomType` prop with "hover" option to trigger zoom on hover.
94 |
95 | ## [1.1.1](https://github.com/laurenashpole/react-inner-image-zoom/compare/v1.1.0...v1.1.1) (2020-07-13)
96 |
97 | ### Fixed
98 |
99 | - Removed unnecessary dragend events when image is not zoomed.
100 |
101 | ## [1.1.0](https://github.com/laurenashpole/react-inner-image-zoom/compare/v1.0.6...v1.1.0) (2020-07-12)
102 |
103 | ### Added
104 |
105 | - `moveType` prop with "drag" option for drag to move functionality on non-touch devices.
106 |
107 | ## [1.0.6](https://github.com/laurenashpole/react-inner-image-zoom/compare/v1.0.5...v1.0.6) (2020-05-22)
108 |
109 | ### Fixed
110 |
111 | - Hide original image on zoom to support transparent zoom images.
112 |
113 | ## [1.0.5](https://github.com/laurenashpole/react-inner-image-zoom/compare/v1.0.0...v1.0.5) (2019-10-15)
114 |
115 | ### Changed
116 |
117 | - Removed `styles.css` import from React component to allow for a greater variety of build approaches.
118 |
119 | ### Added
120 |
121 | - Minified CSS file `styles.min.css`.
122 | - "Styling" section in README file.
123 |
124 | ## [1.0.0](https://github.com/laurenashpole/react-inner-image-zoom/compare/e8e458231a32831a4332b4c009e7df2d68535ada...v1.0.0) (2019-06-19)
125 |
126 | ### Added
127 |
128 | - InnerImageZoom React component.
129 | - README and LICENSE.
130 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | First, thanks for your interest in contributing to React Inner Image Zoom! I didn't expect the enthusiasm for it so that's been pretty cool to see.
4 |
5 | If you're looking for something to work on or want to talk through an idea before you start coding, visit the [issues page](https://github.com/laurenashpole/react-inner-image-zoom/issues).
6 |
7 | ## Getting Started
8 |
9 | This component was bootstrapped using [nwb](https://github.com/insin/nwb)'s `react-component` command to speed through setting up demos, testing, and the basic build process.
10 |
11 | Commits to this repo should follow the forking workflow. For an overview, check out this [tutorial](https://www.atlassian.com/git/tutorials/comparing-workflows/forking-workflow) and use their instructions for copying your personal repo. Once that's done, install your node modules with:
12 |
13 | ```
14 | npm install
15 | ```
16 |
17 | and then run:
18 |
19 | ```
20 | npm start
21 | ```
22 |
23 | to start your demo app at [http://localhost:3000](http://localhost:3000).
24 |
25 | ## Development
26 |
27 | The basic file structure in your new repo will be:
28 |
29 | - `demo` demo app files.
30 | - `src` component source files.
31 | - `tests` tests and testing data.
32 |
33 | Changes in the `src` directory will be reflected in the published package. When you've written your code and feel ready to commit, please use the [Angular Commit Message Conventions](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#-git-commit-guidelines) when writing your commit messages (feel free to just use `*` for scope). This package uses [Semantic Release](https://github.com/semantic-release/semantic-release) for releases and versioning so that helps keep everything up to date.
34 |
35 | If you're adding a new prop, don't forget to include a short description in the props table in the `README.md` file.
36 |
37 | ## Testing
38 |
39 | nwb comes with [Karma](https://github.com/karma-runner/karma) built-in so that's the test runner of choice here. Since accurately testing this component requires actually loading image files, the tests are written using the [ReactDOM testing utilities](https://reactjs.org/docs/test-utils.html).
40 |
41 | The following commands are available for testing:
42 |
43 | - `npm test` will run the tests once.
44 | - `npm run test:coverage` will run the tests and produce a coverage report in `coverage/`.
45 | - `npm run test:watch` will run the tests on every change.
46 |
47 | Each command will also run [ESLint](https://github.com/eslint/eslint) on the component source files.
48 |
49 | If you can, try to include new tests with your changes. Otherwise, just make sure to run `npm test` to check that the existing tests still pass before opening a pull request.
50 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Lauren Ashpole
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MOVED
2 |
3 | This package has moved to the [inner-image-zoom](https://github.com/laurenashpole/inner-image-zoom) repo. For React specific docs, click [here](https://github.com/laurenashpole/inner-image-zoom/tree/main/packages/react) or check out the new demos site [here](https://innerimagezoom.com/). Issues and PRs have been left open for reference/consideration in future releases in that repo.
4 |
5 | ---
6 |
7 | # react-inner-image-zoom
8 |
9 | [Demos](https://laurenashpole.github.io/react-inner-image-zoom)
10 |
11 | ![GitHub Actions][build-badge] [![npm package][npm-badge]][npm] [![TypeScript definitions on DefinitelyTyped][dt-badge]][dt]
12 |
13 | A React component for magnifying an image within its original container. Zoom behavior can be triggered on click or hover and the zoomed image can be moved by dragging on touch devices and either dragging or pan on hover on non-touch devices. The component supports responsive images, loading placeholders, optional fullscreen zoom on mobile, and more.
14 |
15 | ## Installation
16 |
17 | **Note:** Version 2.0.0 introduces React hooks and requires React v16.8.0 or above. To use this package with older versions of React, install with `npm install react-inner-image-zoom@1.3.0` or `yarn add react-inner-image-zoom@1.3.0` instead of the instructions below.
18 |
19 | ### NPM
20 | ```
21 | npm install react-inner-image-zoom
22 | ```
23 |
24 | ### Yarn
25 | ```
26 | yarn add react-inner-image-zoom
27 | ```
28 |
29 | ### TypeScript
30 |
31 | For TypeScript users, type definitions are available through [DefinitelyTyped](https://definitelytyped.org/) and can be installed with:
32 |
33 | ```
34 | npm install --save-dev @types/react-inner-image-zoom
35 | ```
36 |
37 | ### Styling
38 |
39 | I was originally importing the CSS directly into the component but I've recently realized that makes too many assumptions about the wider build process. You can now download the raw CSS file at:
40 |
41 | [/src/InnerImageZoom/styles.css](https://raw.githubusercontent.com/laurenashpole/react-inner-image-zoom/master/src/InnerImageZoom/styles.css)
42 |
43 | or the minified raw minified version at:
44 |
45 | [/src/InnerImageZoom/styles.min.css](https://raw.githubusercontent.com/laurenashpole/react-inner-image-zoom/master/src/InnerImageZoom/styles.min.css)
46 |
47 | to include however you see fit. Or, if your setup supports it, import the files directory from your `node_modules` using:
48 |
49 | ```javascript
50 | import 'react-inner-image-zoom/lib/InnerImageZoom/styles.css';
51 | ```
52 |
53 | or:
54 |
55 | ```javascript
56 | import 'react-inner-image-zoom/lib/InnerImageZoom/styles.min.css';
57 | ```
58 |
59 | ## Usage
60 |
61 | Import and render the component:
62 | ```javascript
63 | import InnerImageZoom from 'react-inner-image-zoom';
64 |
65 | ...
66 |
67 |
68 | ```
69 |
70 | This is the simplest usage. For additional examples, visit the [demo page](https://laurenashpole.github.io/react-inner-image-zoom).
71 |
72 |
73 | ## Props
74 |
75 | Prop | Type | Default | Description
76 | --- | --- | --- | ---
77 | src | String | | (Required) URL for the original image.
78 | sources | Array | | A list of image sources for using the picture tag to serve the appropriate original image (see below for more details).
79 | width | Number | | Width attribute for original image.
80 | height | Number | | Height attribute for original image.
81 | hasSpacer | Boolean | false | If true, gets the original image's aspect ratio based on the width and height props and creates a spacer to prevent cumulative layout shift.
82 | imgAttributes | Object | | [Img](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attributes) and [global](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes) attributes for the original image (excluding `src`, `width`, `height`, and `style` which are set elsewhere). The imgAttributes keys should follow the [React DOM element](https://reactjs.org/docs/dom-elements.html) naming conventions.
83 | zoomSrc | String | | URL for the larger zoom image. Falls back to original image src if not defined.
84 | zoomScale | Number | 1 | Multiplied against the natural width and height of the zoomed image. This will generally be a decimal (example, 0.9 for 90%).
85 | zoomPreload | Boolean | false | If set to true, preloads the zoom image instead of waiting for mouseenter and (unless on a touch device) persists the image on mouseleave.
86 | moveType | String | pan | `pan` or `drag`. The user behavior for moving zoomed images on non-touch devices.
87 | zoomType | String | click | `click` or `hover`. The user behavior for triggering zoom. When using `hover`, combine with `zoomPreload` to avoid flickering on rapid mouse movements.
88 | fadeDuration | Number | 150 | Fade transition time in milliseconds. If zooming in on transparent images, set this to `0` for best results.
89 | fullscreenOnMobile | Boolean | false | Enables fullscreen zoomed image on touch devices below a specified breakpoint.
90 | mobileBreakpoint | Number | 640 | The maximum breakpoint for fullscreen zoom image when fullscreenOnMobile is true.
91 | hideCloseButton | Boolean | false | Hides the close button on touch devices. If set to true, zoom out is triggered by tap.
92 | hideHint | Boolean | false | Hides the magnifying glass hint.
93 | className | String | | Custom classname for styling the component.
94 | afterZoomIn | Function | | Function to be called after zoom in.
95 | afterZoomOut | Function | | Function to be called after zoom out.
96 |
97 | ### Sources
98 |
99 | This prop accepts an array of objects which it uses to create a picture tag and source elements. The component looks for the following optional properties and you can find additional details on responsive images [here](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images):
100 |
101 | Prop | Type | Default | Description
102 | --- | --- | --- | ---
103 | srcSet | String | | Srcset attribute for source tag.
104 | sizes | String | | Sizes attribute for source tag.
105 | media | String | | An attribute containing a media condition for use with the srcset.
106 | type | String | | An image MIME type. This is useful for using newer formats like WebP.
107 |
108 | ## Issues
109 |
110 | Please submit issues or requests [here](https://github.com/laurenashpole/react-inner-image-zoom/issues).
111 |
112 | Most of the implementation choices for this component are based on use cases I've encountered in the past. For example, I chose click to zoom as the default because it's been the most requested on product detail pages I've worked on. If there's a demand for additional triggers or other functionality, I'd be open to looking into it so feel free to ask. And if you want to talk through ideas first, check out the [discussions page](https://github.com/laurenashpole/react-inner-image-zoom/discussions).
113 |
114 | If you're interested in contributing, check out the guidelines [here](https://github.com/laurenashpole/react-inner-image-zoom/blob/master/CONTRIBUTING.md).
115 |
116 | ## License
117 |
118 | [MIT](https://github.com/laurenashpole/react-inner-image-zoom/blob/master/LICENSE)
119 |
120 | [build-badge]: https://github.com/laurenashpole/react-inner-image-zoom/actions/workflows/release.yml/badge.svg
121 |
122 | [npm-badge]: http://img.shields.io/npm/v/react-inner-image-zoom.svg?style=flat
123 | [npm]: https://www.npmjs.com/package/react-inner-image-zoom
124 |
125 | [dt-badge]: https://definitelytyped.org/badges/standard-flat.svg
126 | [dt]: http://definitelytyped.org
127 |
--------------------------------------------------------------------------------
/demo/public/unsplash-large.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/laurenashpole/react-inner-image-zoom/fed33fbd40236757cd5a24948493d44dcc35fcc2/demo/public/unsplash-large.jpg
--------------------------------------------------------------------------------
/demo/public/unsplash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/laurenashpole/react-inner-image-zoom/fed33fbd40236757cd5a24948493d44dcc35fcc2/demo/public/unsplash.jpg
--------------------------------------------------------------------------------
/demo/public/unsplash2-large.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/laurenashpole/react-inner-image-zoom/fed33fbd40236757cd5a24948493d44dcc35fcc2/demo/public/unsplash2-large.jpg
--------------------------------------------------------------------------------
/demo/public/unsplash2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/laurenashpole/react-inner-image-zoom/fed33fbd40236757cd5a24948493d44dcc35fcc2/demo/public/unsplash2.jpg
--------------------------------------------------------------------------------
/demo/public/unsplash3-large.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/laurenashpole/react-inner-image-zoom/fed33fbd40236757cd5a24948493d44dcc35fcc2/demo/public/unsplash3-large.jpg
--------------------------------------------------------------------------------
/demo/public/unsplash3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/laurenashpole/react-inner-image-zoom/fed33fbd40236757cd5a24948493d44dcc35fcc2/demo/public/unsplash3.jpg
--------------------------------------------------------------------------------
/demo/src/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { render } from 'react-dom';
3 | import InnerImageZoom from '../../src';
4 | import '../../src/InnerImageZoom/styles.css';
5 |
6 | class Demo extends Component {
7 | render() {
8 | return (
9 |