├── index.js ├── dist ├── bundle.css.map ├── index.html ├── bundle.css └── components │ └── index.js ├── .eslintignore ├── mocha.opts ├── test └── tools │ ├── cssNullCompiler.js │ ├── renderPage.js │ └── setup.js ├── .editorconfig ├── .travis.yml ├── CHANGELOG.md ├── .babelrc ├── src ├── index.html ├── index.js ├── style.scss └── components │ └── index.js ├── .gitignore ├── webpack.config.js ├── LICENSE.md ├── .eslintrc ├── README.md └── package.json /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist/components/index') 2 | -------------------------------------------------------------------------------- /dist/bundle.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"bundle.css","sourceRoot":""} -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/** 2 | dist/** 3 | webpack* 4 | src/public/* 5 | src/tracking/omniture.js 6 | src/bundles/click/index.js 7 | -------------------------------------------------------------------------------- /mocha.opts: -------------------------------------------------------------------------------- 1 | --compilers js:babel-register,css:test/tools/cssNullCompiler.js 2 | --recursive 3 | --require ./test/tools/setup.js 4 | --timeout 15000 5 | --slow 150 6 | -------------------------------------------------------------------------------- /test/tools/cssNullCompiler.js: -------------------------------------------------------------------------------- 1 | /* This will effectively ignore any require() or import() for css or png files */ 2 | function doNothing () { 3 | return null 4 | } 5 | 6 | require.extensions['.scss'] = doNothing 7 | require.extensions['.css'] = doNothing 8 | -------------------------------------------------------------------------------- /test/tools/renderPage.js: -------------------------------------------------------------------------------- 1 | import { mount } from 'enzyme' 2 | import React from 'react' 3 | 4 | function renderPage (component) { 5 | return mount( 6 |
7 | {component} 8 |
9 | ) 10 | } 11 | 12 | module.exports = renderPage 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - stable 5 | 6 | install: 7 | - npm install 8 | 9 | script: 10 | - npm run cover 11 | 12 | # Send coverage data to Coveralls 13 | after_script: "cat coverage/lcov.info | node_modules/coveralls/bin/coveralls.js" 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v0.1.0 2 | - `ToggleIcon` now tages an object just like onProps instead of the previous version where `ToggleButton` could have been a string or a react component. 3 | - color and backgroundColor can not be added to `ToggleIcon` 4 | 5 | ## v0.1.6 6 | - Change `IosToggle` default off `backgroundColor` 7 | 8 | ## v0.2.0 9 | - Comes with inbuild css files that can be used instead of inline css 10 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "stage-0", 5 | "react" 6 | ], 7 | "plugins": [ 8 | "babel-plugin-add-module-exports", 9 | "babel-plugin-transform-runtime" 10 | ], 11 | "env": { 12 | "test": { 13 | "presets": [ 14 | "es2015", 15 | "stage-0", 16 | "react" 17 | ], 18 | "plugins": [ "babel-plugin-istanbul" ] 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | react-toggle 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | react-toggle 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/tools/setup.js: -------------------------------------------------------------------------------- 1 | global.PAGE_TIMEOUT = 1000 2 | 3 | let jsdom = require('jsdom') 4 | global.document = jsdom.jsdom({src: ''}) 5 | global.window = document.defaultView 6 | Object.keys(document.defaultView).forEach((property) => { 7 | if (typeof global[property] === 'undefined') { 8 | global[property] = document.defaultView[property] 9 | } 10 | }) 11 | global.navigator = {userAgent: 'Mocha Test node.js'} 12 | 13 | export function consoleError (error) { 14 | this.log(error) 15 | throw new Error(error) 16 | } 17 | 18 | console.error = consoleError // eslint-disable-line no-console 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | 29 | #build folder 30 | build 31 | 32 | #Webstorm metadata 33 | .idea 34 | 35 | # Mac files 36 | .DS_Store 37 | 38 | # dotenv file 39 | .env 40 | 41 | 42 | #nyc 43 | .nyc_output/ 44 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { render } from 'react-dom' 3 | import classnames from 'classnames' 4 | import HoverIntent from './components/index' 5 | 6 | require('./style.scss') 7 | 8 | class App extends Component { 9 | constructor() { 10 | super() 11 | this.state = { mouseover: false } 12 | } 13 | onMouseOver = () => { 14 | this.setState({ mouseover: true }) 15 | } 16 | onMouseOut = () => { 17 | this.setState({ mouseover: false }) 18 | } 19 | render() { 20 | const { mouseover } = this.state 21 | return ( 22 |
23 | 27 | 33 | 34 |
35 | ) 36 | } 37 | } 38 | 39 | render(, document.getElementById('root')) 40 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var CopyWebpackPlugin = require('copy-webpack-plugin') 2 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 3 | var path = require('path') 4 | 5 | module.exports = { 6 | entry: './src/index.js', 7 | devtool: 'source-map', 8 | 9 | output: { 10 | path: path.join(__dirname, 'dist'), 11 | filename: 'bundle.js' 12 | }, 13 | module: { 14 | loaders: [ 15 | { 16 | test: /\.js$/, 17 | exclude: /node_modules/, 18 | loader: 'babel-loader', 19 | }, 20 | { 21 | test: /\.(scss|css)$/, 22 | loader: ExtractTextPlugin.extract("style-loader", ["css-loader", "postcss-loader", "sass-loader"]), 23 | }, 24 | ], 25 | }, 26 | postcss: () => { 27 | return [ 28 | require('autoprefixer') 29 | ]; 30 | }, 31 | devServer: { 32 | port: 4000, 33 | host: 'localhost', 34 | inline: true, 35 | info: false, 36 | }, 37 | plugins: [ 38 | new CopyWebpackPlugin([ 39 | { from: path.join(__dirname, 'src/index.html') }, 40 | ]), 41 | new ExtractTextPlugin('bundle.css', { allChunks: true }), 42 | ], 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License (MIT) 2 | ===================== 3 | 4 | Copyright © 2017 Yatin Gera 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy of this software and associated documentation 8 | files (the “Software”), to deal in the Software without 9 | restriction, including without limitation the rights to use, 10 | copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | -------------------------------------------------------------------------------- /src/style.scss: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Bitter'); 2 | 3 | .container { 4 | padding-top: 40px; 5 | } 6 | 7 | body { 8 | background: 9 | linear-gradient(limegreen, transparent), linear-gradient(90deg, skyblue, transparent), linear-gradient(-90deg, coral, transparent); 10 | height: 100vh; 11 | background-blend-mode: screen; 12 | font: bold 18px/24px 'Bitter', serif; 13 | color: #000; 14 | margin:0; 15 | padding:0; 16 | overflow-x: hidden; 17 | -webkit-font-smoothing: antialiased; 18 | } 19 | ul { 20 | margin:0; 21 | padding:0; 22 | list-style:none; 23 | } 24 | li { 25 | position: relative; 26 | } 27 | 28 | .popover { 29 | display:none; 30 | position: absolute; 31 | border:1px solid #101010; 32 | background:#fff; 33 | z-index:1000; 34 | position:absolute; 35 | width:170px; 36 | margin-left:-105px; 37 | border:none; 38 | padding:20px; 39 | font-size:22px; 40 | animation-duration:500ms; 41 | animation-fill-mode:both; 42 | box-shadow:0 4px 8px rgba(0,0,0,0.25); 43 | border-radius:4px; 44 | } 45 | .popover.open { 46 | display:block; 47 | top: 25px; 48 | left: 40px; 49 | animation-name:fadeInUp; 50 | } 51 | 52 | @keyframes fadeInUp { 53 | 0% { opacity:0; transform:translateY(5px); } 54 | 100% { opacity:1; transform:translateY(0); } 55 | } -------------------------------------------------------------------------------- /dist/bundle.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=Bitter);.container { 2 | padding-top: 40px; } 3 | 4 | body { 5 | background: linear-gradient(limegreen, transparent), linear-gradient(90deg, skyblue, transparent), linear-gradient(-90deg, coral, transparent); 6 | height: 100vh; 7 | background-blend-mode: screen; 8 | font: bold 18px/24px 'Bitter', serif; 9 | color: #000; 10 | margin: 0; 11 | padding: 0; 12 | overflow-x: hidden; 13 | -webkit-font-smoothing: antialiased; } 14 | 15 | ul { 16 | margin: 0; 17 | padding: 0; 18 | list-style: none; } 19 | 20 | li { 21 | position: relative; } 22 | 23 | .popover { 24 | display: none; 25 | position: absolute; 26 | border: 1px solid #101010; 27 | background: #fff; 28 | z-index: 1000; 29 | position: absolute; 30 | width: 170px; 31 | margin-left: -105px; 32 | border: none; 33 | padding: 20px; 34 | font-size: 22px; 35 | animation-duration: 500ms; 36 | animation-fill-mode: both; 37 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.25); 38 | border-radius: 4px; } 39 | 40 | .popover.open { 41 | display: block; 42 | top: 25px; 43 | left: 40px; 44 | animation-name: fadeInUp; } 45 | 46 | @keyframes fadeInUp { 47 | 0% { 48 | opacity: 0; 49 | transform: translateY(5px); } 50 | 100% { 51 | opacity: 1; 52 | transform: translateY(0); } } 53 | 54 | /*# sourceMappingURL=bundle.css.map*/ -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "standard-react", 6 | "eslint:recommended", 7 | "plugin:react/recommended" 8 | ], 9 | "plugins": [ 10 | "react", 11 | "babel", 12 | "mocha", 13 | "import" 14 | ], 15 | "parserOptions": { 16 | "ecmaVersion": 6, 17 | "sourceType": "module", 18 | "ecmaFeatures": { 19 | "jsx": true 20 | } 21 | }, 22 | "globals": { 23 | "__DEV__": true 24 | }, 25 | "env": { 26 | "es6": true, 27 | "browser": true, 28 | "node": true, 29 | "jquery": true, 30 | "mocha": true 31 | }, 32 | "rules": { 33 | "quotes": 0, 34 | "no-console": 1, 35 | "no-debugger": 1, 36 | "no-var": 1, 37 | "semi": [1, "never"], 38 | "no-trailing-spaces": 0, 39 | "eol-last": 0, 40 | "no-unused-vars": 0, 41 | "no-underscore-dangle": 0, 42 | "no-alert": 0, 43 | "no-lone-blocks": 0, 44 | "jsx-quotes": 1, 45 | "import/extensions": 1, 46 | "comma-dangle": ["warn", "always-multiline"], 47 | "react/display-name": [ 1, {"ignoreTranspilerName": false }], 48 | "react/forbid-prop-types": [1, {"forbid": ["any"]}], 49 | "react/jsx-boolean-value": 1, 50 | "react/jsx-closing-bracket-location": 0, 51 | "react/jsx-curly-spacing": 1, 52 | "react/jsx-indent-props": 0, 53 | "react/jsx-key": 1, 54 | "react/jsx-max-props-per-line": 0, 55 | "react/jsx-no-bind": 1, 56 | "react/jsx-no-duplicate-props": 1, 57 | "react/jsx-no-literals": 0, 58 | "react/jsx-no-undef": 1, 59 | "react/jsx-pascal-case": 1, 60 | "react/jsx-sort-prop-types": 0, 61 | "react/jsx-sort-props": 0, 62 | "react/jsx-uses-react": 1, 63 | "react/jsx-uses-vars": 1, 64 | "react/no-danger": 1, 65 | "react/no-did-mount-set-state": 1, 66 | "react/no-did-update-set-state": 1, 67 | "react/no-direct-mutation-state": 1, 68 | "react/no-multi-comp": 1, 69 | "react/no-set-state": 0, 70 | "react/no-unknown-property": 1, 71 | "react/prefer-es6-class": 1, 72 | "react/prop-types": 1, 73 | "react/react-in-jsx-scope": 1, 74 | "react/self-closing-comp": 1, 75 | "react/sort-comp": 1, 76 | "react/jsx-wrap-multilines": 1 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | class HoverIntent extends Component { 5 | constructor() { 6 | super() 7 | this.x = 0 8 | this.y = 0 9 | this.pX = 0 10 | this.pY = 0 11 | this.status = 0 12 | this.timer = 0 13 | } 14 | componentDidMount() { 15 | this.element.addEventListener('mouseover', this.dispatchOver, false) 16 | this.element.addEventListener('mouseout', this.dispatchOut, false) 17 | } 18 | componentWillUnmount() { 19 | this.element.removeEventListener('mouseover', this.dispatchOver, false) 20 | this.element.removeEventListener('mouseout', this.dispatchOut, false) 21 | } 22 | delay = (e) => { 23 | if (this.timer) { this.timer = clearTimeout(this.timer) } 24 | this.status = 0 25 | return this.props.onMouseOut.call(this.element, e) 26 | } 27 | tracker = (e) => { 28 | this.x = e.clientX 29 | this.y = e.clientY 30 | } 31 | compare = (e) => { 32 | if (this.timer) { this.timer = clearTimeout(this.timer) } 33 | if ((Math.abs(this.pX - this.x) + Math.abs(this.pY - this.y)) < this.props.sensitivity) { 34 | this.status = 1 35 | return this.props.onMouseOver.call(this.element, e) 36 | } else { 37 | this.pX = this.x 38 | this.pY = this.y 39 | this.timer = setTimeout(() => this.compare(e), this.props.interval) 40 | } 41 | } 42 | dispatchOver = (e) => { 43 | if (this.timer) { this.timer = clearTimeout(this.timer) } 44 | this.element.removeEventListener('mousemove', this.tracker, false) 45 | if (this.status !== 1) { 46 | this.pX = e.clientX 47 | this.pY = e.clientY 48 | this.element.addEventListener('mousemove', this.tracker, false) 49 | this.timer = setTimeout(() => this.compare(e), this.props.interval) 50 | } 51 | } 52 | dispatchOut = (e) => { 53 | if (this.timer) { this.timer = clearTimeout(this.timer) } 54 | this.element.removeEventListener('mousemove', this.tracker, false) 55 | if (this.status === 1) { 56 | this.timer = setTimeout(() => this.delay(e), this.props.timeout) 57 | } 58 | } 59 | render() { 60 | return React.cloneElement(this.props.children, { 61 | ref: (element) => { this.element = element }, 62 | }) 63 | } 64 | } 65 | 66 | HoverIntent.defaultProps = { 67 | sensitivity: 7, 68 | interval: 100, 69 | timeout: 0, 70 | } 71 | 72 | HoverIntent.propTypes = { 73 | sensitivity: PropTypes.number, 74 | interval: PropTypes.number, 75 | timeout: PropTypes.number, 76 | onMouseOver: PropTypes.func, 77 | onMouseOut: PropTypes.func, 78 | children: PropTypes.node, 79 | } 80 | 81 | export default HoverIntent 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## React Hoverintent 2 | 3 | 4 | react-hoverintent is a react wrapper over jquery-hoverintent plugin. 5 | 6 | "hoverIntent is a plug-in that attempts to determine the user's intent... like a crystal ball, only with mouse movement! It is similar to jQuery's hover method. However, instead of calling the handlerIn function immediately, hoverIntent waits until the user's mouse slows down enough before making the call." 7 | 8 | [jquery-hoverintent](https://github.com/briancherne/jquery-hoverIntent). 9 | 10 | 11 | ## Installation 12 | ```npm install react-hoverintent``` 13 | 14 | ## Demo 15 | 16 | The demo with live examples can be viewed [here](https://vijayranghar.github.io/). 17 | 18 | 19 | ## Props 20 | 21 | ```onMouseOver``` : The callback to fire on mouse over event 22 | 23 | ```onMouseOut``` : The callback to fire on mouse out event 24 | 25 | ```timeout``` : 26 | A simple delay, in milliseconds, before the ```onMouseOut``` callback is fired. If the user mouses back over the element before the timeout has expired the ```onMouseOut``` callback will not be called (nor will the ```onMouseOver``` callback be called). This is primarily to protect against sloppy/human mousing trajectories that temporarily (and unintentionally) take the user off of the target element... giving them time to return. 27 | 28 | Default ```timeout: 0``` 29 | 30 | ```sensitivity``` : 31 | If the mouse travels fewer than this number of pixels between polling intervals, then the ```onMouseOver``` callback will be called. With the minimum sensitivity threshold of 1, the mouse must not move between polling intervals. With higher sensitivity thresholds you are more likely to receive a false positive. 32 | 33 | Default ```sensitivity: 7``` 34 | 35 | ```interval``` : 36 | The number of milliseconds hoverIntent waits between reading/comparing mouse coordinates. When the user's mouse first enters the element its coordinates are recorded. The soonest the ```onMouseOut``` callback can be called is after a single polling interval. Setting the polling interval higher will increase the delay before the first possible ```onMouseOver``` call, but also increases the time to the next point of comparison. 37 | 38 | Default ```interval: 700``` 39 | 40 | 41 | ## Use 42 | 43 | ```javascript 44 | import HoverIntet from 'react-hoverintent' 45 | import classnames from 'classnames' 46 | 47 | class App extends Component { 48 | constructor () { 49 | super() 50 | this.state = { mouseover: false } 51 | this.onMouseOver = this.onMouseOver.bind(this) 52 | this.onMouseOut = this.onMouseOut.bind(this) 53 | } 54 | onMouseOver () { 55 | this.setState({mouseover: true}) 56 | } 57 | onMouseOut () { 58 | this.setState({mouseover: false}) 59 | } 60 | render () { 61 | let {mouseover} = this.state 62 | return ( 63 |
64 | 71 | 72 | 73 | Hover 74 |
75 | ) 76 | } 77 | } 78 | ``` 79 | 80 | 81 | 82 | ## Repo 83 | https://github.com/nerdchacha/react-hoverintent 84 | 85 | 86 | 87 | ## Authors 88 | **Yatin Gera** 89 | 90 | **Vijay Mohan Singh Ranghar** 91 | 92 | ## Credits 93 | 94 | **[briancherne](https://github.com/briancherne/jquery-hoverIntent)** - initial work and setup 95 | 96 | 97 | ## Issue tracker 98 | https://github.com/nerdchacha/react-hoverintent/issues 99 | 100 | 101 | ## License 102 | 103 | This project is licensed under the MIT License 104 | 105 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-hoverintent", 3 | "version": "0.0.10", 4 | "description": "A simple react implementation over jquery-hoverintent", 5 | "scripts": { 6 | "lint": "eslint src", 7 | "lint:changed": "LIST=`git diff-index --name-only HEAD | grep '.jsx\\{0,1\\}'$`; if [ \"$LIST\" ]; then eslint $LIST; fi", 8 | "lint:fix": "npm run lint -- --fix", 9 | "lint:changed:fix": "LIST=`git diff-index --name-only HEAD | grep '.jsx\\{0,1\\}'$`; if [ \"$LIST\" ]; then eslint $LIST --fix; fi", 10 | "lint:changed-PR": "LIST=`git diff-index --name-only HEAD^ | grep '.jsx\\{0,1\\}'$`; if [ \"$LIST\" ]; then eslint $LIST; fi", 11 | "test": "npm run test:local && npm run build:component", 12 | "test:local": "env NODE_ENV=test mocha --opts mocha.opts", 13 | "build:component": "babel src/components --out-dir dist/components", 14 | "add:component": "git add dist/", 15 | "start": "webpack-dev-server", 16 | "build:bundle": "webpack", 17 | "cover": "cross-env NODE_ENV=test nyc --reporter=lcov --reporter=text mocha --opts mocha.opts" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/nerdchacha/react-hoverintent" 22 | }, 23 | "author": { 24 | "name": "Yatin Gera", 25 | "email": "yatin.gera5@gmail.com" 26 | }, 27 | "bugs": { 28 | "url": "https://github.com/nerdchacha/react-hoverintent/issues" 29 | }, 30 | "main": "index.js", 31 | "keywords": [ 32 | "hover", 33 | "hover intent", 34 | "delay" 35 | ], 36 | "license": "MIT", 37 | "precommit": { 38 | "run": [ 39 | "build:component", 40 | "build:bundle", 41 | "add:component", 42 | "lint:changed" 43 | ] 44 | }, 45 | "dependencies": { 46 | "prop-types": "^15.5.10", 47 | "react": "^15.4.1", 48 | "react-dom": "^15.4.1" 49 | }, 50 | "devDependencies": { 51 | "autoprefixer-loader": "^3.2.0", 52 | "babel-cli": "^6.8.0", 53 | "babel-core": "^6.8.0", 54 | "babel-eslint": "^7.1.1", 55 | "babel-istanbul": "^0.12.2", 56 | "babel-loader": "^6.2.10", 57 | "babel-plugin-add-module-exports": "^0.2.1", 58 | "babel-plugin-istanbul": "^4.0.0", 59 | "babel-plugin-react-display-name": "2.0.0", 60 | "babel-plugin-react-transform": "^2.0.2", 61 | "babel-plugin-transform-imports": "^1.1.0", 62 | "babel-plugin-transform-runtime": "^6.15.0", 63 | "babel-polyfill": "^6.20.0", 64 | "babel-preset-es2015": "^6.6.0", 65 | "babel-preset-react": "^6.5.0", 66 | "babel-preset-react-hmre": "1.1.1", 67 | "babel-preset-stage-0": "^6.16.0", 68 | "babel-register": "^6.8.0", 69 | "babel-runtime": "^6.20.0", 70 | "browser-sync": "^2.18.5", 71 | "chai": "^3.5.0", 72 | "classnames": "^2.2.5", 73 | "copy-webpack-plugin": "^4.0.1", 74 | "coveralls": "^2.12.0", 75 | "cross-env": "^3.2.4", 76 | "css-loader": "^0.27.3", 77 | "enzyme": "2.2.0", 78 | "eslint": "^3.12.2", 79 | "eslint-config-standard": "^6.2.1", 80 | "eslint-config-standard-react": "^4.2.0", 81 | "eslint-loader": "^1.5.0", 82 | "eslint-plugin-babel": "^4.0.0", 83 | "eslint-plugin-import": "^2.2.0", 84 | "eslint-plugin-mocha": "^4.3.0", 85 | "eslint-plugin-promise": "^3.4.0", 86 | "eslint-plugin-react": "^6.8.0", 87 | "eslint-plugin-standard": "^2.0.0", 88 | "eventsource-polyfill": "0.9.6", 89 | "expect": "1.19.0", 90 | "extract-text-webpack-plugin": "^1.0.1", 91 | "jsdom": "8.5.0", 92 | "mocha": "2.4.5", 93 | "ncp": "^2.0.0", 94 | "node-sass": "^4.5.0", 95 | "nyc": "^10.1.2", 96 | "postcss-loader": "^1.3.3", 97 | "pre-commit": "^1.2.2", 98 | "react-addons-test-utils": "^15.0.2", 99 | "react-bootstrap": "^0.30.8", 100 | "react-fontawesome": "^1.5.0", 101 | "sass-loader": "^6.0.2", 102 | "scss-loader": "0.0.1", 103 | "style-loader": "^0.13.2", 104 | "webpack": "^1.13.2", 105 | "webpack-dev-middleware": "1.6.1", 106 | "webpack-dev-server": "^1.16.0", 107 | "webpack-hot-middleware": "^2.10.0", 108 | "webpack-middleware": "^1.5.1" 109 | }, 110 | "nyc": { 111 | "sourceMap": false, 112 | "instrument": false 113 | } 114 | } -------------------------------------------------------------------------------- /dist/components/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); 8 | 9 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 10 | 11 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 12 | 13 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 14 | 15 | var _createClass2 = require('babel-runtime/helpers/createClass'); 16 | 17 | var _createClass3 = _interopRequireDefault(_createClass2); 18 | 19 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); 20 | 21 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 22 | 23 | var _inherits2 = require('babel-runtime/helpers/inherits'); 24 | 25 | var _inherits3 = _interopRequireDefault(_inherits2); 26 | 27 | var _react = require('react'); 28 | 29 | var _react2 = _interopRequireDefault(_react); 30 | 31 | var _propTypes = require('prop-types'); 32 | 33 | var _propTypes2 = _interopRequireDefault(_propTypes); 34 | 35 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 36 | 37 | var HoverIntent = function (_Component) { 38 | (0, _inherits3.default)(HoverIntent, _Component); 39 | 40 | function HoverIntent() { 41 | (0, _classCallCheck3.default)(this, HoverIntent); 42 | 43 | var _this = (0, _possibleConstructorReturn3.default)(this, (HoverIntent.__proto__ || (0, _getPrototypeOf2.default)(HoverIntent)).call(this)); 44 | 45 | _this.delay = function (e) { 46 | if (_this.timer) { 47 | _this.timer = clearTimeout(_this.timer); 48 | } 49 | _this.status = 0; 50 | return _this.props.onMouseOut.call(_this.element, e); 51 | }; 52 | 53 | _this.tracker = function (e) { 54 | _this.x = e.clientX; 55 | _this.y = e.clientY; 56 | }; 57 | 58 | _this.compare = function (e) { 59 | if (_this.timer) { 60 | _this.timer = clearTimeout(_this.timer); 61 | } 62 | if (Math.abs(_this.pX - _this.x) + Math.abs(_this.pY - _this.y) < _this.props.sensitivity) { 63 | _this.status = 1; 64 | return _this.props.onMouseOver.call(_this.element, e); 65 | } else { 66 | _this.pX = _this.x; 67 | _this.pY = _this.y; 68 | _this.timer = setTimeout(function () { 69 | return _this.compare(e); 70 | }, _this.props.interval); 71 | } 72 | }; 73 | 74 | _this.dispatchOver = function (e) { 75 | if (_this.timer) { 76 | _this.timer = clearTimeout(_this.timer); 77 | } 78 | _this.element.removeEventListener('mousemove', _this.tracker, false); 79 | if (_this.status !== 1) { 80 | _this.pX = e.clientX; 81 | _this.pY = e.clientY; 82 | _this.element.addEventListener('mousemove', _this.tracker, false); 83 | _this.timer = setTimeout(function () { 84 | return _this.compare(e); 85 | }, _this.props.interval); 86 | } 87 | }; 88 | 89 | _this.dispatchOut = function (e) { 90 | if (_this.timer) { 91 | _this.timer = clearTimeout(_this.timer); 92 | } 93 | _this.element.removeEventListener('mousemove', _this.tracker, false); 94 | if (_this.status === 1) { 95 | _this.timer = setTimeout(function () { 96 | return _this.delay(e); 97 | }, _this.props.timeout); 98 | } 99 | }; 100 | 101 | _this.x = 0; 102 | _this.y = 0; 103 | _this.pX = 0; 104 | _this.pY = 0; 105 | _this.status = 0; 106 | _this.timer = 0; 107 | return _this; 108 | } 109 | 110 | (0, _createClass3.default)(HoverIntent, [{ 111 | key: 'componentDidMount', 112 | value: function componentDidMount() { 113 | this.element.addEventListener('mouseover', this.dispatchOver, false); 114 | this.element.addEventListener('mouseout', this.dispatchOut, false); 115 | } 116 | }, { 117 | key: 'componentWillUnmount', 118 | value: function componentWillUnmount() { 119 | this.element.removeEventListener('mouseover', this.dispatchOver, false); 120 | this.element.removeEventListener('mouseout', this.dispatchOut, false); 121 | } 122 | }, { 123 | key: 'render', 124 | value: function render() { 125 | var _this2 = this; 126 | 127 | return _react2.default.cloneElement(this.props.children, { 128 | ref: function ref(element) { 129 | _this2.element = element; 130 | } 131 | }); 132 | } 133 | }]); 134 | return HoverIntent; 135 | }(_react.Component); 136 | 137 | HoverIntent.defaultProps = { 138 | sensitivity: 7, 139 | interval: 100, 140 | timeout: 0 141 | }; 142 | 143 | HoverIntent.propTypes = { 144 | sensitivity: _propTypes2.default.number, 145 | interval: _propTypes2.default.number, 146 | timeout: _propTypes2.default.number, 147 | onMouseOver: _propTypes2.default.func, 148 | onMouseOut: _propTypes2.default.func, 149 | children: _propTypes2.default.node 150 | }; 151 | 152 | exports.default = HoverIntent; 153 | module.exports = exports['default']; --------------------------------------------------------------------------------