Skip to content

Commit d0f207c

Browse files
authored
Merge pull request #3 from microcipcip/feature/useIntersection
Feature/use intersection
2 parents aa61984 + 7a93ff1 commit d0f207c

File tree

22 files changed

+559
-16
lines changed

22 files changed

+559
-16
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ Vue.use(VueCompositionAPI);
6161
- Sensors
6262
- [`useHover`](./src/components/useHover/stories/useHover.md) — tracks mouse hover state of a given element.
6363
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usehover--demo)
64+
- [`useIntersection`](./src/components/useIntersection/stories/useIntersection.md) — tracks intersection of target element with an ancestor element.
65+
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-useintersection--demo)
6466
- [`useMedia`](./src/components/useMedia/stories/useMedia.md) — tracks state of a CSS media query.
6567
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usemedia--demo)
6668
[![Demo](https://img.shields.io/badge/advanced_demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usemedia--advanced-demo)
@@ -70,7 +72,7 @@ Vue.use(VueCompositionAPI);
7072
- [`useMouseElement`](./src/components/useMouseElement/stories/useMouseElement.md) — tracks the mouse position relative to given element.
7173
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usemouseelement--demo)
7274
- Animations
73-
- [`useInterval`](./src/components/useInterval/stories/useInterval.md) — updates the `counter` value repeatedly on a fixed time delay.
75+
- [`useInterval`](./src/components/useInterval/stories/useInterval.md) — updates `counter` value repeatedly on a fixed time delay.
7476
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/animations-useinterval--demo)
7577
- [`useIntervalFn`](./src/components/useIntervalFn/stories/useIntervalFn.md) — calls function repeatedly on a fixed time delay.
7678
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/animations-useintervalfn--demo)

src/components/useClickAway/stories/useClickAway.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ Vue function that triggers a callback when the user clicks outside of the target
77
```typescript
88
useClickAway(
99
elRef: Ref<null | Element>,
10-
onClickAway: (e: Event) => void,
10+
callback: (e: Event) => void,
1111
events?: string[]
1212
): void;
1313
```
1414

1515
### Parameters
1616

1717
- `elRef: Ref<null | Element>` the element to check for click away events
18-
- `onClickAway: Function` the callback to run when triggering a click away
18+
- `callback: Function` the callback to run when triggering a click away
1919
- `events: string[]` list of events to listen to, defaults to `['mousedown', 'touchstart']`
2020

2121
## Usage

src/components/useClickAway/useClickAway.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ const defaultEvents = ['mousedown', 'touchstart']
44

55
export function useClickAway(
66
elRef: Ref<null | Element>,
7-
onClickAway: (e: Event) => void,
7+
callback: (e: Event) => void,
88
events = defaultEvents
99
) {
1010
const handler = (e: Event) => {
1111
if (elRef.value && !elRef.value.contains(e.target as Node)) {
12-
onClickAway(e)
12+
callback(e)
1313
}
1414
}
1515

src/components/useHover/stories/UseHoverDemo.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export default Vue.extend({
2222
})
2323
</script>
2424

25-
<style>
25+
<style scoped>
2626
.use-hover {
2727
cursor: default;
2828
display: flex;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './useIntersection'
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<template>
2+
<div class="section">
3+
<div class="intersection" v-for="(item, index) in divElements">
4+
<use-intersection-element-demo
5+
class="intersection__el"
6+
:options="intersectionOpts"
7+
@changed="handleIntersectionChange"
8+
>
9+
<video controls loop>
10+
<source :src="getAlternateVideoUrl(index)" type="video/mp4" />
11+
</video>
12+
</use-intersection-element-demo>
13+
</div>
14+
</div>
15+
</template>
16+
17+
<script lang="ts">
18+
import Vue from 'vue'
19+
import UseIntersectionElementDemo from './UseIntersectionElementDemo.vue'
20+
21+
const getAlternateVideoUrl = (n: number) => {
22+
const videoNumber = n % 2 === 0 ? 1 : 2
23+
return `/demo/video${videoNumber}.mp4`
24+
}
25+
26+
const setVideoPausedValue = ($el: Element, val: string) =>
27+
$el.setAttribute('data-is-paused', val)
28+
29+
const divElements = new Array(10)
30+
31+
export default Vue.extend({
32+
name: 'UseIntersectionDemo',
33+
components: { UseIntersectionElementDemo },
34+
setup() {
35+
const intersectionOpts = {
36+
root: null,
37+
threshold: 1
38+
}
39+
40+
const handleIntersectionChange = ({
41+
target,
42+
intersectionRatio
43+
}: IntersectionObserverEntry) => {
44+
const isVisible = intersectionRatio === 1
45+
const $video = target.querySelector('video')
46+
if (!$video) return
47+
48+
// Video pause logic
49+
if (!isVisible) {
50+
if ($video.paused) return
51+
$video.pause()
52+
setVideoPausedValue($video, '1')
53+
}
54+
55+
// Video play logic
56+
if (isVisible && $video.dataset.isPaused === '1'){
57+
$video.play()
58+
setVideoPausedValue($video, '0')
59+
}
60+
}
61+
62+
return {
63+
divElements,
64+
intersectionOpts,
65+
getAlternateVideoUrl,
66+
handleIntersectionChange
67+
}
68+
}
69+
})
70+
</script>
71+
72+
<style scoped>
73+
.section {
74+
padding: 20px 0;
75+
}
76+
77+
.intersection {
78+
display: flex;
79+
align-items: flex-start;
80+
justify-content: center;
81+
height: 920px;
82+
}
83+
84+
.intersection__el {
85+
position: relative;
86+
}
87+
</style>
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<template>
2+
<div class="section">
3+
<div class="intersection" v-for="el in divElements">
4+
<use-intersection-element-demo
5+
class="intersection__el"
6+
:options="intersectionOpts"
7+
@changed="handleIntersectionChange"
8+
@paused="handleIntersectionPaused"
9+
/>
10+
</div>
11+
</div>
12+
</template>
13+
14+
<script lang="ts">
15+
import Vue from 'vue'
16+
import UseIntersectionElementDemo from './UseIntersectionElementDemo.vue'
17+
18+
export default Vue.extend({
19+
name: 'UseIntersectionDemo',
20+
components: { UseIntersectionElementDemo },
21+
setup() {
22+
const intersectionOpts = {
23+
root: null,
24+
rootMargin: '-40px 0px -40px',
25+
threshold: 1
26+
}
27+
28+
const divElements = new Array(100)
29+
30+
const handleIntersectionChange = ({
31+
target,
32+
intersectionRatio
33+
}: IntersectionObserverEntry) => {
34+
const isVisible = intersectionRatio === 1
35+
target.classList.toggle('-is-active', isVisible)
36+
}
37+
38+
const handleIntersectionPaused = (target: Element, isPaused: boolean) => {
39+
target.classList.toggle('-is-paused', isPaused)
40+
}
41+
42+
return {
43+
divElements,
44+
intersectionOpts,
45+
handleIntersectionChange,
46+
handleIntersectionPaused
47+
}
48+
}
49+
})
50+
</script>
51+
52+
<style scoped>
53+
.section {
54+
padding: 20px 0;
55+
}
56+
57+
.intersection {
58+
display: flex;
59+
align-items: center;
60+
justify-content: center;
61+
height: 120px;
62+
}
63+
64+
/* El */
65+
.intersection__el {
66+
position: relative;
67+
width: 80px;
68+
height: 80px;
69+
}
70+
71+
/* Action buttons */
72+
/deep/ .intersection__el .button {
73+
position: absolute;
74+
top: 50%;
75+
left: 130px;
76+
transform: translateY(-50%);
77+
}
78+
79+
/* Circle */
80+
.intersection__el:after {
81+
content: 'Not intersecting';
82+
display: flex;
83+
justify-content: center;
84+
align-items: center;
85+
position: absolute;
86+
top: 50%;
87+
left: 50%;
88+
width: 100px;
89+
height: 40px;
90+
border-radius: 8px;
91+
background: red;
92+
color: white;
93+
font-size: 12px;
94+
transition: all 0.7s ease-in-out;
95+
transform: translate(-50%, -50%) scale(1);
96+
}
97+
98+
.intersection__el.-is-active:after {
99+
content: 'Intersecting';
100+
background: green;
101+
transform: translate(-50%, -50%) scale(1.4);
102+
}
103+
104+
.intersection__el.-is-paused:after {
105+
content: 'Disabled';
106+
background: orange;
107+
}
108+
</style>
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<template>
2+
<div ref="elRef">
3+
<slot></slot>
4+
<button
5+
class="button is-primary"
6+
@click="handleStart"
7+
v-if="!isObserving"
8+
title="Start observing"
9+
>
10+
<span class="icon"><i class="fas fa-play"></i></span>
11+
</button>
12+
<button
13+
class="button is-warning"
14+
@click="handleStop"
15+
v-else
16+
title="Stop observing"
17+
>
18+
<span class="icon"><i class="fas fa-pause"></i></span>
19+
</button>
20+
</div>
21+
</template>
22+
23+
<script lang="ts">
24+
import Vue from 'vue'
25+
import { ref, watch } from '@src/api'
26+
import { useIntersection } from '@src/vue-use-kit'
27+
export default Vue.extend({
28+
name: 'UseIntersectionElementDemo',
29+
props: {
30+
options: {
31+
default: {
32+
root: null,
33+
rootMargin: '0px',
34+
threshold: 0
35+
}
36+
}
37+
},
38+
setup({ options }, { emit }) {
39+
const elRef = ref(null)
40+
41+
const { observedEntry, start, stop } = useIntersection(
42+
elRef,
43+
options as IntersectionObserverInit
44+
)
45+
46+
watch(() => {
47+
if (!observedEntry.value) return
48+
emit('changed', observedEntry.value)
49+
})
50+
51+
let isObserving = ref(true)
52+
const handleStart = () => {
53+
if (!observedEntry.value) return
54+
start()
55+
emit('paused', observedEntry.value.target, false)
56+
isObserving.value = true
57+
}
58+
59+
const handleStop = () => {
60+
if (!observedEntry.value) return
61+
stop()
62+
emit('paused', observedEntry.value.target, true)
63+
isObserving.value = false
64+
}
65+
66+
return { handleStart, handleStop, isObserving, elRef }
67+
}
68+
})
69+
</script>

0 commit comments

Comments
 (0)