Skip to content

Commit

Permalink
feat: update API to return object of props
Browse files Browse the repository at this point in the history
BREAKING CHANGE: The second item in the returned array is now an object of props.
  • Loading branch information
therealparmesh committed Jan 30, 2020
1 parent a5ec330 commit 189dddf
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 81 deletions.
12 changes: 4 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@ npm install use-hovering
import { useHovering } from 'use-hovering';

export const Example = () => {
const [hovering, getTargetProps] = useHovering();
const [hovering, bind] = useHovering();

return (
<div {...getTargetProps()}>Hover over me!{hovering && ' Hovering!'}</div>
);
return <div {...bind}>Hover over me!{hovering && ' Hovering!'}</div>;
};
```

Expand All @@ -33,13 +31,11 @@ export const Example = () => {
import { useHovering } from 'use-hovering';

export const Example = () => {
const [hovering, getTargetProps] = useHovering({
const [hovering, bind] = useHovering({
enterDelay: 250,
exitDelay: 250,
});

return (
<div {...getTargetProps()}>Hover over me!{hovering && ' Hovering!'}</div>
);
return <div {...bind}>Hover over me!{hovering && ' Hovering!'}</div>;
};
```
5 changes: 0 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 4 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
"repository": "github:therealparmesh/use-hovering",
"license": "MIT",
"author": "Parmesh Krishen",
"main": "dist/index.js",
"module": "dist/index.mjs",
"source": "src/index.js",
"types": "types.d.ts",
"files": [
"src",
"dist",
"types.d.ts"
],
"main": "dist/index.js",
"module": "dist/index.mjs",
"source": "src/index.js",
"types": "types.d.ts",
"scripts": {
"build": "microbundle --format cjs,es",
"prepublishOnly": "npm run-script build",
Expand All @@ -28,9 +28,6 @@
"singleQuote": true,
"trailingComma": "all"
},
"dependencies": {
"memoize-one": "^5.1.1"
},
"devDependencies": {
"husky": "^4.0.10",
"microbundle": "^0.11.0",
Expand Down
96 changes: 44 additions & 52 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import React from 'react';
import memoizeOne from 'memoize-one';

const mergeRefs = memoizeOne((...refs) => {
return value => {
refs.forEach(resolvableRef => {
if (typeof resolvableRef === 'function') {
resolvableRef(value);
} else if (resolvableRef) {
resolvableRef.current = value;
}
});
};
});

export const useHovering = ({ enterDelay, exitDelay } = {}) => {
const ref = React.useRef(null);
const op = React.useRef(null);
const [node, setNode] = React.useState(null);
const [hovering, setHovering] = React.useState(false);

const changeHoverState = React.useCallback(
const ref = React.useCallback(n => {
if (n) {
setNode(n);
}
}, []);

const bind = React.useMemo(() => {
return {
ref,
tabIndex: 0,
};
}, [ref]);

const changeHoveringState = React.useCallback(
value => {
clearTimeout(op.current);

Expand All @@ -36,53 +36,45 @@ export const useHovering = ({ enterDelay, exitDelay } = {}) => {
[enterDelay, exitDelay],
);

const getTargetProps = React.useMemo(() => {
const makeMemo = value => {
return memoizeOne(callback => {
return e => {
changeHoverState(value);
React.useEffect(() => {
if (!node) {
return;
}

if (callback) {
return callback(e);
}
};
});
const on = () => {
changeHoveringState(true);
};

return ({
ref: resolvableRef,
tabIndex,
onMouseEnter,
onMouseLeave,
onMouseMove,
onFocus,
onBlur,
} = {}) => {
return {
ref: resolvableRef ? mergeRefs(ref, resolvableRef) : ref,
tabIndex: tabIndex || 0,
onMouseEnter: makeMemo(true)(onMouseEnter),
onMouseLeave: makeMemo(false)(onMouseLeave),
onMouseMove: makeMemo(true)(onMouseMove),
onFocus: makeMemo(true)(onFocus),
onBlur: makeMemo(false)(onBlur),
};
const off = () => {
changeHoveringState(false);
};
}, [changeHoverState]);

React.useEffect(() => {
const listener = e => {
if (ref.current && !ref.current.contains(e.target)) {
changeHoverState(false);
const outsideOff = e => {
if (node && !node.contains(e.target)) {
changeHoveringState(false);
}
};

document.addEventListener('mousemove', listener);
node.addEventListener('mouseenter', on);
node.addEventListener('mouseleave', off);
node.addEventListener('mousemove', on);
node.addEventListener('focus', on);
node.addEventListener('blur', off);
document.addEventListener('mousemove', outsideOff);

return () => {
document.removeEventListener('mousemove', listener);
if (!node) {
return;
}

node.removeEventListener('mouseenter', on);
node.removeEventListener('mouseleave', off);
node.removeEventListener('mousemove', on);
node.removeEventListener('focus', on);
node.removeEventListener('blur', off);
document.removeEventListener('mousemove', outsideOff);
};
}, [changeHoverState]);
}, [node, changeHoveringState]);

return [hovering, getTargetProps];
return [hovering, bind];
};
11 changes: 2 additions & 9 deletions types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,9 @@ interface Props {
exitDelay?: number;
}

interface TargetProps {
interface Bind {
ref: React.RefObject<any>;
tabIndex: React.HTMLAttributes<any>['tabIndex'];
onMouseEnter: React.MouseEventHandler<any>;
onMouseLeave: React.MouseEventHandler<any>;
onMouseMove: React.MouseEventHandler<any>;
onFocus: React.FocusEventHandler<any>;
onBlur: React.FocusEventHandler<any>;
}

export const useHovering: (
args: Props,
) => [boolean, (args: Partial<TargetProps>) => TargetProps];
export const useHovering: (args: Props) => [boolean, Bind];

0 comments on commit 189dddf

Please sign in to comment.