├── .editorconfig ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .npmignore ├── .release-it.json ├── .size-snapshot.json ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── babel.config.js ├── docs ├── example.png ├── index.html └── main.0322e55f6eb6aaa0d9e3.js ├── jest.config.js ├── package.json ├── prettier.config.js ├── renovate.json ├── rollup.config.js ├── src ├── example │ ├── example.png │ └── index.tsx ├── index.ts └── useVisible.tsx ├── template.html ├── tsconfig.json ├── webpack.config.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_size = 2 6 | end_of_line = lf 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | commonjs: true, 6 | es6: true 7 | }, 8 | extends: ['plugin:react/recommended', 'prettier/@typescript-eslint'], 9 | plugins: ['@typescript-eslint', 'prettier', 'react', 'react-hooks'], 10 | parser: '@typescript-eslint/parser', 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2018, 14 | ecmaFeatures: { 15 | jsx: true 16 | } 17 | }, 18 | settings: { 19 | react: { 20 | createClass: 'createReactClass', 21 | pragma: 'React', // Pragma to use, default to "React" 22 | version: '16.8' 23 | }, 24 | propWrapperFunctions: [ 25 | 'forbidExtraProps', 26 | { property: 'freeze', object: 'Object' }, 27 | { property: 'myFavoriteWrapper' } 28 | ], 29 | linkComponents: ['Hyperlink', { name: 'Link', linkAttribute: 'to' }] 30 | }, 31 | rules: { 32 | // react-config -> https://github.com/yannickcr/eslint-plugin-react/ 33 | 'react/jsx-uses-react': 'error', 34 | 'react/jsx-uses-vars': 'error', 35 | 'react/no-deprecated': 'error', 36 | 'react/display-name': [0], 37 | 'react/prop-types': [0], 38 | 'react-hooks/rules-of-hooks': 'error', 39 | 'react-hooks/exhaustive-deps': 'warn', 40 | strict: 'error', 41 | 'check-constructor': false, 42 | 'no-comma-dangle': false, 43 | 'no-empty-interface': false, 44 | 'no-unused-vars': [0], 45 | 'prettier/prettier': [ 46 | 'error', 47 | { 48 | semi: false, 49 | singleQuote: true, 50 | printWidth: 80, 51 | tabWidth: 2 52 | } 53 | ] 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | lib/* linguist-vendored 2 | *.js linguist-vendored 3 | *.html linguist-vendored 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production,distfile 10 | /build 11 | 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | package-lock.json 16 | .vscode 17 | 18 | # yarn.lock 19 | lib 20 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.env 2 | node_modules 3 | .vscode 4 | template.html 5 | webpack.*.js 6 | renovate.json 7 | .travis.yml 8 | src/example 9 | prettier.config.js 10 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "git": { 3 | "tagName": "v${version}", 4 | "requireUpstream": false, 5 | "requireCleanWorkingDir": false 6 | }, 7 | "github": { 8 | "release": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.size-snapshot.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib/index.min.js": { 3 | "bundled": 906, 4 | "minified": 906, 5 | "gzipped": 540 6 | }, 7 | "lib/index.cjs.js": { 8 | "bundled": 1797, 9 | "minified": 1042, 10 | "gzipped": 543 11 | }, 12 | "lib/index.esm.js": { 13 | "bundled": 1335, 14 | "minified": 752, 15 | "gzipped": 447, 16 | "treeshaked": { 17 | "rollup": { 18 | "code": 14, 19 | "import_statements": 14 20 | }, 21 | "webpack": { 22 | "code": 1036 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 8 5 | - 6 6 | 7 | jobs: 8 | include: 9 | # Define the release stage that runs semantic-release 10 | - stage: release 11 | node_js: lts/* 12 | # Advanced: optionally overwrite your default `script` step to skip the tests 13 | # script: skip 14 | deploy: 15 | provider: script 16 | skip_cleanup: true 17 | script: 18 | - npm i 19 | - npm run release 20 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [v1.0.0](https://github.com/kmkzt/react-hooks-visible/tree/v1.0.0) (2019-05-27) 4 | 5 | 6 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 kmkzt 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 | # react-hooks-visible 2 | 3 | [![npm version](https://badge.fury.io/js/react-hooks-visible.svg)](https://badge.fury.io/js/react-hooks-visible.svg) ![npm](https://img.shields.io/npm/dt/react-hooks-visible.svg) 4 | 5 | `react-hooks-visible` is React Hooks library for element visibility. Uses the intersection observer API. 6 | 7 | **[demo](https://kmkzt.github.io/react-hooks-visible/)** 8 | 9 | ## Get started 10 | 11 | ```shell 12 | yarn add react react-hooks-visible 13 | ``` 14 | 15 | ## How to use 16 | 17 | started 18 | 19 | ```javascript 20 | import React from 'react' 21 | import { useVisible } from 'react-hooks-visible' 22 | 23 | const VisibleComponent = () => { 24 | const [targetRef, visible] = useVisible() 25 | return ( 26 |
This is {Math.floor(visible * 100)} % visible
27 | ) 28 | } 29 | ``` 30 | 31 | Pass a function to an argument, and you can change the return value 32 | 33 | ```javascript 34 | // Percent value. 35 | const [targetRef, percent] = useVisible((vi: number) => Math.floor(vi * 100)) 36 | 37 | // Boolean. This example is 50% visible. 38 | const [targetRef, isVisible] = useVisible((vi: number) => vi > 0.5) 39 | 40 | // CSSProperties. opacity 41 | const [styleExampleRef, visibleStyle] = useVisible((vi: number) => ({ 42 | opacity: vi 43 | })) 44 | ``` 45 | 46 | ### Options. 47 | 48 | This is same as IntersectionObserver Option. 49 | https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Creating_an_intersection_observer 50 | 51 | ```javascript 52 | const [targetRef, visible] = useVisble(Math.floor(visible * 100),{ 53 | root: document.querySelector('wrapper') // Wrap element 54 | rootMargin: '10px', //wrap element margin 55 | threshold: [0.1, 0.2, 0.3, 0.4] 56 | }) 57 | ``` 58 | 59 | [example code](src/example/) 60 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const { BABEL_ENV, NODE_ENV } = process.env 2 | const modules = BABEL_ENV === 'cjs' || NODE_ENV === 'test' ? 'commonjs' : false 3 | 4 | module.exports = api => { 5 | api.cache(false) 6 | return { 7 | presets: [ 8 | [ 9 | '@babel/preset-env', 10 | { 11 | targets: NODE_ENV === 'test' ? { node: 'current' } : undefined, 12 | loose: true, 13 | modules 14 | } 15 | ], 16 | // react only 17 | '@babel/preset-react', 18 | '@babel/preset-typescript' 19 | ], 20 | plugins: [ 21 | // react only 22 | // [ 23 | // 'babel-plugin-transform-react-remove-prop-types', 24 | // { mode: 'unsafe-wrap' } 25 | // ], 26 | ['@babel/plugin-proposal-object-rest-spread', { loose: true }], 27 | ['@babel/plugin-proposal-class-properties', { loose: true }] 28 | // modules === 'commonjs' && 'add-module-exports' 29 | ] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /docs/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kmkzt/react-hooks-visible/de8746aff7598d4e76e010a791fe65f62faa8c38/docs/example.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | react-hooks-visible -------------------------------------------------------------------------------- /docs/main.0322e55f6eb6aaa0d9e3.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function n(r){if(t[r])return t[r].exports;var l=t[r]={i:r,l:!1,exports:{}};return e[r].call(l.exports,l,l.exports,n),l.l=!0,l.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var l in e)n.d(r,l,function(t){return e[t]}.bind(null,l));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=9)}([function(e,t,n){"use strict";e.exports=n(3)},function(e,t,n){"use strict"; 2 | /* 3 | object-assign 4 | (c) Sindre Sorhus 5 | @license MIT 6 | */var r=Object.getOwnPropertySymbols,l=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable;function a(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map(function(e){return t[e]}).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach(function(e){r[e]=e}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var n,o,u=a(e),c=1;cz.length&&z.push(e)}function U(e,t,n){return null==e?0:function e(t,n,r,l){var o=typeof t;"undefined"!==o&&"boolean"!==o||(t=null);var u=!1;if(null===t)u=!0;else switch(o){case"string":case"number":u=!0;break;case"object":switch(t.$$typeof){case i:case a:u=!0}}if(u)return r(l,t,""===n?"."+D(t,0):n),1;if(u=0,n=""===n?".":n+":",Array.isArray(t))for(var c=0;cthis.eventPool.length&&this.eventPool.push(e)}function fe(e){e.eventPool=[],e.getPooled=ce,e.release=se}l(ue.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=ae)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=ae)},persist:function(){this.isPersistent=ae},isPersistent:oe,destructor:function(){var e,t=this.constructor.Interface;for(e in t)this[e]=null;this.nativeEvent=this._targetInst=this.dispatchConfig=null,this.isPropagationStopped=this.isDefaultPrevented=oe,this._dispatchInstances=this._dispatchListeners=null}}),ue.Interface={type:null,target:null,currentTarget:function(){return null},eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null},ue.extend=function(e){function t(){}function n(){return r.apply(this,arguments)}var r=this;t.prototype=r.prototype;var i=new t;return l(i,n.prototype),n.prototype=i,n.prototype.constructor=n,n.Interface=l({},r.Interface,e),n.extend=r.extend,fe(n),n},fe(ue);var de=ue.extend({data:null}),pe=ue.extend({data:null}),me=[9,13,27,32],he=$&&"CompositionEvent"in window,ve=null;$&&"documentMode"in document&&(ve=document.documentMode);var ye=$&&"TextEvent"in window&&!ve,ge=$&&(!he||ve&&8=ve),be=String.fromCharCode(32),ke={beforeInput:{phasedRegistrationNames:{bubbled:"onBeforeInput",captured:"onBeforeInputCapture"},dependencies:["compositionend","keypress","textInput","paste"]},compositionEnd:{phasedRegistrationNames:{bubbled:"onCompositionEnd",captured:"onCompositionEndCapture"},dependencies:"blur compositionend keydown keypress keyup mousedown".split(" ")},compositionStart:{phasedRegistrationNames:{bubbled:"onCompositionStart",captured:"onCompositionStartCapture"},dependencies:"blur compositionstart keydown keypress keyup mousedown".split(" ")},compositionUpdate:{phasedRegistrationNames:{bubbled:"onCompositionUpdate",captured:"onCompositionUpdateCapture"},dependencies:"blur compositionupdate keydown keypress keyup mousedown".split(" ")}},xe=!1;function we(e,t){switch(e){case"keyup":return-1!==me.indexOf(t.keyCode);case"keydown":return 229!==t.keyCode;case"keypress":case"mousedown":case"blur":return!0;default:return!1}}function Te(e){return"object"==typeof(e=e.detail)&&"data"in e?e.data:null}var _e=!1;var Se={eventTypes:ke,extractEvents:function(e,t,n,r){var l=void 0,i=void 0;if(he)e:{switch(e){case"compositionstart":l=ke.compositionStart;break e;case"compositionend":l=ke.compositionEnd;break e;case"compositionupdate":l=ke.compositionUpdate;break e}l=void 0}else _e?we(e,n)&&(l=ke.compositionEnd):"keydown"===e&&229===n.keyCode&&(l=ke.compositionStart);return l?(ge&&"ko"!==n.locale&&(_e||l!==ke.compositionStart?l===ke.compositionEnd&&_e&&(i=ie()):(re="value"in(ne=r)?ne.value:ne.textContent,_e=!0)),l=de.getPooled(l,t,n,r),i?l.data=i:null!==(i=Te(n))&&(l.data=i),H(l),i=l):i=null,(e=ye?function(e,t){switch(e){case"compositionend":return Te(t);case"keypress":return 32!==t.which?null:(xe=!0,be);case"textInput":return(e=t.data)===be&&xe?null:e;default:return null}}(e,n):function(e,t){if(_e)return"compositionend"===e||!he&&we(e,t)?(e=ie(),le=re=ne=null,_e=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1