Skip to content

Commit 3d7418f

Browse files
committed
Add intersection hook
1 parent 75d38a8 commit 3d7418f

File tree

20 files changed

+306
-77
lines changed

20 files changed

+306
-77
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
"styled-components": "^4.1.2"
1616
},
1717
"devDependencies": {
18+
"@babel/plugin-syntax-jsx": "^7.2.0",
19+
"@storybook/addon-actions": "^4.1.1",
1820
"@storybook/core": "^4.1.1",
1921
"@storybook/react": "^4.1.1",
2022
"@types/react": "16.7",
@@ -28,7 +30,7 @@
2830
"eslint-plugin-react": "^7.11.1",
2931
"ncc": "^0.3.6",
3032
"prettier": "^1.15.3",
31-
"react-hot-loader": "^4.6.0",
33+
"shx": "^0.3.2",
3234
"typescript": "^3.2.2",
3335
"typescript-eslint-parser": "^21.0.2"
3436
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import "@storybook/addon-actions/register";
Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { configure } from "@storybook/react";
2-
import { setConfig } from 'react-hot-loader';
2+
import { setConfig } from "react-hot-loader";
33

44
setConfig({ pureSFC: true });
55

@@ -8,15 +8,3 @@ function loadStories() {
88
}
99

1010
configure(loadStories, module);
11-
12-
/*
13-
import { configure } from "@storybook/react";
14-
15-
const req = require.context("../src", true, /story.js$/);
16-
17-
function loadStories() {
18-
req.keys().forEach(req);
19-
}
20-
21-
configure(loadStories, module);
22-
*/

packages/focus-hook/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
"main": "dist/index.js",
55
"license": "MIT",
66
"scripts": {
7-
"build": "ncc build -e react -e styled-components src/index.tsx",
8-
"storybook": "start-storybook -p 6006 -c .storybook"
7+
"build": "ncc build -e react src/index.tsx",
8+
"prestorybook": "shx rm -rf \"../../node_modules/@storybook/react/node_modules/react*\"",
9+
"storybook": "start-storybook -p 6006 -c .storybook",
10+
"clean": "shx rm -rf dist"
911
},
1012
"peerDependencies": {
1113
"react": "^16.7.0-alpha.2",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import "@storybook/addon-actions/register";
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { configure } from "@storybook/react";
2+
3+
function loadStories() {
4+
require("../src/story.tsx");
5+
}
6+
7+
configure(loadStories, module);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// import { setConfig } from 'react-hot-loader';
2+
// setConfig({ pureSFC: true });
3+
4+
module.exports = (baseConfig, env, config) => {
5+
config.module.rules.push({
6+
test: /\.(ts|tsx)$/,
7+
loader: require.resolve("awesome-typescript-loader")
8+
});
9+
// config.plugins.push(new TSDocgenPlugin()); // optional
10+
config.resolve.extensions.push(".ts", ".tsx");
11+
return config;
12+
};
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "@tuxsudo/intersection-hook",
3+
"version": "1.0.0",
4+
"main": "dist/index.js",
5+
"license": "MIT",
6+
"scripts": {
7+
"build": "ncc build -e react src/index.tsx",
8+
"prestorybook": "shx rm -rf \"../../node_modules/@storybook/react/node_modules/react*\"",
9+
"storybook": "start-storybook -p 6006 -c .storybook",
10+
"clean": "shx rm -rf dist"
11+
},
12+
"peerDependencies": {
13+
"react": "^16.7.0-alpha.2",
14+
"react-dom": "^16.7.0-alpha.2"
15+
},
16+
"dependencies": {
17+
"resize-observer-polyfill": "^1.5.1"
18+
}
19+
}

packages/intersection-hook/readme.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# React Intersect Observer Hook
2+
3+
```jsx
4+
import useIntersection from "@tuxsudo/intersection-hook";
5+
6+
export function Elm() {
7+
const { ref, entry } = useIntersection();
8+
9+
return (
10+
<div ref={ref}>
11+
{entry && entry.isIntersecting ? "can see me" : "can't see me"}
12+
</div>
13+
);
14+
}
15+
```
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { useState, useEffect, useRef, MutableRefObject } from "react";
2+
import ResizeObserver from "resize-observer-polyfill";
3+
4+
/* https://github.com/Microsoft/TypeScript/issues/16255 */
5+
declare global {
6+
interface Window {
7+
IntersectionObserver: typeof IntersectionObserver;
8+
}
9+
}
10+
11+
export interface IntersectionHandler {
12+
(entry: ResizeObserverEntry): void;
13+
}
14+
15+
export interface UseIntersectionArgs {
16+
root?: HTMLElement;
17+
rootMargin?: string;
18+
threshold?: number;
19+
onChange?: (entry: IntersectionObserverEntry) => void;
20+
}
21+
22+
export default function useIntersection(
23+
args: UseIntersectionArgs
24+
): {
25+
entry: IntersectionObserverEntry | null;
26+
ref: MutableRefObject<any>;
27+
} {
28+
const ref = useRef(null);
29+
const [entry, setEntry] = useState(null);
30+
31+
const handleIntersection = ([newEntry]) => {
32+
setEntry(newEntry);
33+
34+
if (args.onChange) {
35+
args.onChange(newEntry);
36+
}
37+
};
38+
39+
const observer = useRef(
40+
window && window.IntersectionObserver
41+
? new window.IntersectionObserver(handleIntersection, {
42+
root: args.root,
43+
rootMargin: args.rootMargin,
44+
threshold: args.threshold
45+
})
46+
: null
47+
);
48+
49+
useEffect(
50+
() => {
51+
if (ref && window && window.IntersectionObserver) {
52+
observer.current.observe(ref.current);
53+
}
54+
return () => observer.current.disconnect();
55+
},
56+
[ref]
57+
);
58+
59+
return { entry, ref };
60+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import React from "react";
2+
import { storiesOf } from "@storybook/react";
3+
import { action } from "@storybook/addon-actions";
4+
5+
import styled from "styled-components";
6+
7+
import useIntersection from "./index";
8+
9+
const story = storiesOf("Intersection Hook", module);
10+
11+
const Hud = styled.div`
12+
background: white;
13+
border-top: 1px solid #3e3e3e;
14+
bottom: 0;
15+
font-size: 2rem;
16+
left: 0;
17+
right: 0;
18+
opacity: 0.95;
19+
padding: 1rem;
20+
position: fixed;
21+
`;
22+
23+
const Filler = styled.p`
24+
margin: 0;
25+
font-size: 4rem;
26+
27+
& > :not(:last-child) {
28+
margin-bottom: 1rem;
29+
}
30+
`;
31+
32+
const Pre = styled.pre`
33+
padding: 1rem;
34+
background: #c0c0c0;
35+
`;
36+
37+
function Demo({
38+
disableLogging,
39+
disableHud
40+
}: {
41+
disableLogging?: boolean;
42+
disableHud?: boolean;
43+
}) {
44+
const { ref, entry } = useIntersection({
45+
onChange: !disableLogging
46+
? entry =>
47+
action("Intersect")({
48+
boundingClientRect: entry.boundingClientRect,
49+
isIntersecting: entry.isIntersecting
50+
})
51+
: undefined
52+
});
53+
54+
return (
55+
<React.Fragment>
56+
{!disableHud && (
57+
<Hud>
58+
{entry && entry.isIntersecting
59+
? "✅ is in view"
60+
: "🚫 is not in view"}
61+
</Hud>
62+
)}
63+
<Filler>
64+
Pariatur laboris velit pariatur cupidatat consequat officia proident
65+
dolor. Pariatur non nulla excepteur esse elit labore consequat irure
66+
voluptate sint labore. Anim cupidatat cupidatat sint ut sunt esse
67+
consectetur cillum elit anim. Fugiat ut non est incididunt in anim
68+
nostrud ullamco.
69+
</Filler>
70+
<Filler>
71+
Ex nulla non ex id officia dolor. Voluptate duis ad excepteur
72+
adipisicing. Est nostrud commodo adipisicing ullamco anim consequat
73+
eiusmod. Minim nulla Lorem cillum sint duis in laboris nostrud
74+
voluptate. Cillum irure culpa mollit sit.
75+
</Filler>
76+
<Pre ref={ref}>
77+
{entry && JSON.stringify(entry.boundingClientRect, null, 2)}
78+
</Pre>
79+
<Filler>
80+
Pariatur laboris velit pariatur cupidatat consequat officia proident
81+
dolor. Pariatur non nulla excepteur esse elit labore consequat irure
82+
voluptate sint labore. Anim cupidatat cupidatat sint ut sunt esse
83+
consectetur cillum elit anim. Fugiat ut non est incididunt in anim
84+
nostrud ullamco.
85+
</Filler>
86+
<Filler>
87+
Ex nulla non ex id officia dolor. Voluptate duis ad excepteur
88+
adipisicing. Est nostrud commodo adipisicing ullamco anim consequat
89+
eiusmod. Minim nulla Lorem cillum sint duis in laboris nostrud
90+
voluptate. Cillum irure culpa mollit sit.
91+
</Filler>
92+
</React.Fragment>
93+
);
94+
}
95+
96+
story.add("hud", () => <Demo disableLogging />);
97+
story.add("logger", () => <Demo disableHud />);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import "@storybook/addon-actions/register";
Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,7 @@
11
import { configure } from "@storybook/react";
2-
import { setConfig } from 'react-hot-loader';
3-
4-
setConfig({ pureSFC: true });
52

63
function loadStories() {
74
require("../src/story.tsx");
85
}
96

107
configure(loadStories, module);
11-
12-
/*
13-
import { configure } from "@storybook/react";
14-
15-
const req = require.context("../src", true, /story.js$/);
16-
17-
function loadStories() {
18-
req.keys().forEach(req);
19-
}
20-
21-
configure(loadStories, module);
22-
*/

packages/resize-hook/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
"main": "dist/index.js",
55
"license": "MIT",
66
"scripts": {
7-
"build": "ncc build -e react -e styled-components src/index.tsx",
8-
"storybook": "start-storybook -p 6006 -c .storybook"
7+
"build": "ncc build -e react src/index.tsx",
8+
"prestorybook": "shx rm -rf \"../../node_modules/@storybook/react/node_modules/react*\"",
9+
"storybook": "start-storybook -p 6006 -c .storybook",
10+
"clean": "shx rm -rf dist"
911
},
1012
"peerDependencies": {
1113
"react": "^16.7.0-alpha.2",

packages/resize-hook/readme.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,8 @@
44
import useResize from "@tuxsudo/resize-hook";
55

66
export function Elm() {
7-
const { ref, entry } = useResize();
7+
const { ref, entry } = useResize({ onResize: console.log });
88

9-
return (
10-
<div ref={ref}>
11-
{entry && <span>{entry.contentRect.width}</span>}
12-
</div>
13-
);
9+
return <div ref={ref}>{entry && <span>{entry.contentRect.width}</span>}</div>;
1410
}
1511
```

packages/resize-hook/src/index.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import { useState, useEffect, useRef, MutableRefObject } from "react";
22
import ResizeObserver from "resize-observer-polyfill";
33

4-
export default function useResize(): {
4+
export interface ResizeHandler {
5+
(entry: ResizeObserverEntry): void;
6+
}
7+
8+
export default function useResize({
9+
onResize
10+
}: { onResize?: ResizeHandler } = {}): {
511
entry: ResizeObserverEntry | null;
612
ref: MutableRefObject<any>;
713
} {
@@ -10,6 +16,9 @@ export default function useResize(): {
1016

1117
const handleResize = ([entry]) => {
1218
setEntry(entry);
19+
if (onResize) {
20+
onResize(entry);
21+
}
1322
};
1423

1524
const observer = useRef(new ResizeObserver(handleResize));

packages/resize-hook/src/story.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
import React from "react";
22
import { storiesOf } from "@storybook/react";
3+
import { action } from "@storybook/addon-actions";
34

45
import useResize from "./index";
56

7+
const story = storiesOf("Resize Hook", module);
8+
69
function Demo() {
7-
const { ref, entry } = useResize();
10+
const { ref, entry } = useResize({
11+
onResize: entry => action("resize")(entry.contentRect)
12+
});
813

914
return (
10-
<pre ref={ref}>
11-
{entry ? entry.contentRect.width : "?"}x
12-
{entry ? entry.contentRect.height : "?"}
13-
</pre>
15+
<pre ref={ref}>{entry && JSON.stringify(entry.contentRect, null, 2)}</pre>
1416
);
1517
}
1618

17-
storiesOf("Resize Hook", module).add("default", () => <Demo />);
19+
story.add("default", () => <Demo />);

0 commit comments

Comments
 (0)