From 71ef66f83022837a5cf9667d44293df85ec95fcc Mon Sep 17 00:00:00 2001 From: Salvatore Tedde Date: Sun, 16 Feb 2020 02:16:10 +0000 Subject: [PATCH 1/5] feat(useIntersection): Adding useIntersection function --- README.md | 4 +- src/components/useIntersection/index.ts | 1 + .../stories/UseIntersectionDemo.vue | 86 +++++++++++++ .../stories/UseIntersectionElementDemo.vue | 36 ++++++ .../stories/useIntersection.md | 113 ++++++++++++++++++ .../stories/useIntersection.story.ts | 28 +++++ .../useIntersection/useIntersection.spec.ts | 8 ++ .../useIntersection/useIntersection.ts | 38 ++++++ src/vue-use-kit.ts | 1 + 9 files changed, 314 insertions(+), 1 deletion(-) create mode 100755 src/components/useIntersection/index.ts create mode 100755 src/components/useIntersection/stories/UseIntersectionDemo.vue create mode 100755 src/components/useIntersection/stories/UseIntersectionElementDemo.vue create mode 100755 src/components/useIntersection/stories/useIntersection.md create mode 100755 src/components/useIntersection/stories/useIntersection.story.ts create mode 100755 src/components/useIntersection/useIntersection.spec.ts create mode 100755 src/components/useIntersection/useIntersection.ts diff --git a/README.md b/README.md index 321620d..361a6aa 100755 --- a/README.md +++ b/README.md @@ -61,6 +61,8 @@ Vue.use(VueCompositionAPI); - Sensors - [`useHover`](./src/components/useHover/stories/useHover.md) — tracks mouse hover state of a given element. [![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usehover--demo) + - [`useIntersection`](./src/components/useIntersection/stories/useIntersection.md) — tracks intersection of target element with an ancestor element. + [![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-useintersection--demo) - [`useMedia`](./src/components/useMedia/stories/useMedia.md) — tracks state of a CSS media query. [![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usemedia--demo) [![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); - [`useMouseElement`](./src/components/useMouseElement/stories/useMouseElement.md) — tracks the mouse position relative to given element. [![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usemouseelement--demo) - Animations - - [`useInterval`](./src/components/useInterval/stories/useInterval.md) — updates the `counter` value repeatedly on a fixed time delay. + - [`useInterval`](./src/components/useInterval/stories/useInterval.md) — updates `counter` value repeatedly on a fixed time delay. [![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/animations-useinterval--demo) - [`useIntervalFn`](./src/components/useIntervalFn/stories/useIntervalFn.md) — calls function repeatedly on a fixed time delay. [![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/animations-useintervalfn--demo) diff --git a/src/components/useIntersection/index.ts b/src/components/useIntersection/index.ts new file mode 100755 index 0000000..9f8af68 --- /dev/null +++ b/src/components/useIntersection/index.ts @@ -0,0 +1 @@ +export * from './useIntersection' diff --git a/src/components/useIntersection/stories/UseIntersectionDemo.vue b/src/components/useIntersection/stories/UseIntersectionDemo.vue new file mode 100755 index 0000000..2dbb774 --- /dev/null +++ b/src/components/useIntersection/stories/UseIntersectionDemo.vue @@ -0,0 +1,86 @@ + + + + + diff --git a/src/components/useIntersection/stories/UseIntersectionElementDemo.vue b/src/components/useIntersection/stories/UseIntersectionElementDemo.vue new file mode 100755 index 0000000..166d107 --- /dev/null +++ b/src/components/useIntersection/stories/UseIntersectionElementDemo.vue @@ -0,0 +1,36 @@ + + + diff --git a/src/components/useIntersection/stories/useIntersection.md b/src/components/useIntersection/stories/useIntersection.md new file mode 100755 index 0000000..266a9e5 --- /dev/null +++ b/src/components/useIntersection/stories/useIntersection.md @@ -0,0 +1,113 @@ +# useIntersection + +Vue function that tracks the changes in the intersection of a target element with an ancestor element or with a top-level document's viewport. + +It is based on the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API). + +## Reference + +```typescript +useIntersection( + elRef: Ref, + options?: IntersectionObserverInit, + runOnMount?: boolean +): { + observedEntry: Ref; + start: () => void; + stop: () => void; +}; +``` + +### Parameters + +- `elRef: Ref` the element to observe +- `options: IntersectionObserverInit` the [IntersectionObserver options](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver#Properties) +- `runOnMount: boolean` whether to observe the element on mount, `true` by default + +### Returns + +- `observedEntry: Ref` the [observed entry](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry) +- `start: Function` the function used for starting observing +- `stop: Function` the function used for stopping observing + +## Usage + +```html + + + + + +``` diff --git a/src/components/useIntersection/stories/useIntersection.story.ts b/src/components/useIntersection/stories/useIntersection.story.ts new file mode 100755 index 0000000..ffd654f --- /dev/null +++ b/src/components/useIntersection/stories/useIntersection.story.ts @@ -0,0 +1,28 @@ +import { storiesOf } from '@storybook/vue' +import path from 'path' +import StoryTitle from '@src/helpers/StoryTitle.vue' +import UseIntersectionDemo from './UseIntersectionDemo.vue' + +const functionName = 'useIntersection' +const functionPath = path.resolve(__dirname, '..') +const notes = require(`./${functionName}.md`).default + +const basicDemo = () => ({ + components: { StoryTitle, demo: UseIntersectionDemo }, + template: ` +
+ + + + + +
` +}) + +storiesOf('sensors|useIntersection', module) + .addParameters({ notes }) + .add('Demo', basicDemo) diff --git a/src/components/useIntersection/useIntersection.spec.ts b/src/components/useIntersection/useIntersection.spec.ts new file mode 100755 index 0000000..b0c98b0 --- /dev/null +++ b/src/components/useIntersection/useIntersection.spec.ts @@ -0,0 +1,8 @@ +// import { mount } from '@src/helpers/test' +// import { useIntersection } from '@src/vue-use-kit' + +describe('useIntersection', () => { + it('should do something', () => { + // Add test here + }) +}) diff --git a/src/components/useIntersection/useIntersection.ts b/src/components/useIntersection/useIntersection.ts new file mode 100755 index 0000000..b23e065 --- /dev/null +++ b/src/components/useIntersection/useIntersection.ts @@ -0,0 +1,38 @@ +import { ref, onMounted, onUnmounted, Ref } from '@src/api' + +const errorMsg = + 'IntersectionObserver is not supported, please install a polyfill' + +export function useIntersection( + elRef: Ref, + options: IntersectionObserverInit = {}, + runOnMount = true +) { + const observedEntry: Ref = ref(null) + + const handleObserver = (entries: IntersectionObserverEntry[]) => { + observedEntry.value = entries[0] + } + + let observer: IntersectionObserver | null = null + + const start = () => { + if (!('IntersectionObserver' in window)) throw new Error(errorMsg) + + // Do not start if the observer is already initialized + if (observer || !elRef.value) return + observer = new IntersectionObserver(handleObserver, options) + observer.observe(elRef.value) + } + + const stop = () => { + if (!observer) return + observer.disconnect() + observer = null + } + + onMounted(() => runOnMount && start()) + onUnmounted(stop) + + return { observedEntry, start, stop } +} diff --git a/src/vue-use-kit.ts b/src/vue-use-kit.ts index 4ede07a..f15005b 100755 --- a/src/vue-use-kit.ts +++ b/src/vue-use-kit.ts @@ -1,5 +1,6 @@ export * from './components/getQuery' export * from './components/useClickAway' +export * from './components/useIntersection' export * from './components/useIntervalFn' export * from './components/useInterval' export * from './components/useMedia' From fd9de76928f1ccfc7599f87a0c09df615061c5be Mon Sep 17 00:00:00 2001 From: Salvatore Tedde Date: Sun, 16 Feb 2020 03:00:09 +0000 Subject: [PATCH 2/5] docs(useIntersection): Adding demo in the docs for useIntersection --- .../stories/useIntersection.md | 74 ++++++++++--------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/src/components/useIntersection/stories/useIntersection.md b/src/components/useIntersection/stories/useIntersection.md index 266a9e5..3dfd57f 100755 --- a/src/components/useIntersection/stories/useIntersection.md +++ b/src/components/useIntersection/stories/useIntersection.md @@ -32,18 +32,12 @@ useIntersection( ## Usage +First we have to define the intersection component, named `UseIntersectionElDemo.vue`. + ```html @@ -53,7 +47,7 @@ useIntersection( import { useIntersection } from 'vue-use-kit' export default Vue.extend({ - name: 'UseIntersectionDemo', + name: 'UseIntersectionElDemo', setup() { const options = { root: null, @@ -61,42 +55,52 @@ useIntersection( threshold: 1.0 } - const watchClass = (className, observedEntry, isEnabled) => { - if (!isEnabled) return + const elRef = ref(null) + const elClass = ref('') + const { observedEntry } = useIntersection(elRef, options) + watch(() => { + if (!observedEntry.value) return const isVisible = observedEntry.value.intersectionRatio === 1 - className.value = isVisible ? '-is-visible' : '' + elClass.value = isVisible ? '-is-visible' : '' + }) + + return { + elRef, + elClass } + } + }) + +``` - const el1Ref = ref(null) - const el1Class = ref('') - const { observedEntry: el1Ob } = useIntersection(el1Ref, options) - watch(() => watchClass(el1Class, el1Ob, el1Ob.value)) +Then we can call it in a loop in the parent component `UseIntersectionDemo.vue`. - const el2Ref = ref(null) - const el2Class = ref('') - const { observedEntry: el2Ob } = useIntersection(el2Ref, options) - watch(() => watchClass(el2Class, el2Ob, el2Ob.value)) +```html + - const el3Ref = ref(null) - const el3Class = ref('') - const { observedEntry: el3Ob } = useIntersection(el3Ref, options) - watch(() => watchClass(el3Class, el3Ob, el3Ob.value)) + diff --git a/src/components/useIntersection/stories/useIntersection.story.ts b/src/components/useIntersection/stories/useIntersection.story.ts index ffd654f..f1a0217 100755 --- a/src/components/useIntersection/stories/useIntersection.story.ts +++ b/src/components/useIntersection/stories/useIntersection.story.ts @@ -2,6 +2,7 @@ import { storiesOf } from '@storybook/vue' import path from 'path' import StoryTitle from '@src/helpers/StoryTitle.vue' import UseIntersectionDemo from './UseIntersectionDemo.vue' +import UseIntersectionAdvancedDemo from './UseIntersectionAdvancedDemo.vue' const functionName = 'useIntersection' const functionPath = path.resolve(__dirname, '..') @@ -23,6 +24,23 @@ const basicDemo = () => ({ ` }) +const advancedDemo = () => ({ + components: { StoryTitle, demo: UseIntersectionAdvancedDemo }, + template: ` +
+ + + + + +
` +}) + storiesOf('sensors|useIntersection', module) .addParameters({ notes }) .add('Demo', basicDemo) + .add('Advanced Demo', advancedDemo) From 1d410bceaa410739ec7f1a544f60e94b878a9279 Mon Sep 17 00:00:00 2001 From: Salvatore Tedde Date: Sun, 16 Feb 2020 13:22:35 +0000 Subject: [PATCH 4/5] docs(useIntersection): Add useIntersection demo --- .../useClickAway/stories/useClickAway.md | 4 +- src/components/useClickAway/useClickAway.ts | 4 +- .../useHover/stories/UseHoverDemo.vue | 2 +- .../stories/UseIntersectionAdvancedDemo.vue | 48 +++++++++++++------ .../stories/UseIntersectionDemo.vue | 36 +++++++++++--- .../stories/UseIntersectionElementDemo.vue | 36 +++++++++++++- .../stories/useIntersection.story.ts | 20 +++++++- .../useMedia/stories/UseMediaAdvancedDemo.vue | 2 +- .../useMedia/stories/UseMediaDemo.vue | 2 +- .../useMedia/stories/useMedia.story.ts | 4 +- .../useMouse/stories/UseMouseAdvancedDemo.vue | 2 +- .../stories/UseMouseElementDemo.vue | 2 +- src/components/useRaf/stories/UseRafDemo.vue | 2 +- .../useRafFn/stories/UseRafFnAdvancedDemo.vue | 4 +- .../useRafFn/stories/UseRafFnDemo.vue | 2 +- src/helpers/StoryTitle.vue | 8 ++++ 16 files changed, 137 insertions(+), 41 deletions(-) diff --git a/src/components/useClickAway/stories/useClickAway.md b/src/components/useClickAway/stories/useClickAway.md index 2eedc4e..b980963 100755 --- a/src/components/useClickAway/stories/useClickAway.md +++ b/src/components/useClickAway/stories/useClickAway.md @@ -7,7 +7,7 @@ Vue function that triggers a callback when the user clicks outside of the target ```typescript useClickAway( elRef: Ref, - onClickAway: (e: Event) => void, + callback: (e: Event) => void, events?: string[] ): void; ``` @@ -15,7 +15,7 @@ useClickAway( ### Parameters - `elRef: Ref` the element to check for click away events -- `onClickAway: Function` the callback to run when triggering a click away +- `callback: Function` the callback to run when triggering a click away - `events: string[]` list of events to listen to, defaults to `['mousedown', 'touchstart']` ## Usage diff --git a/src/components/useClickAway/useClickAway.ts b/src/components/useClickAway/useClickAway.ts index 2474479..d67611e 100755 --- a/src/components/useClickAway/useClickAway.ts +++ b/src/components/useClickAway/useClickAway.ts @@ -4,12 +4,12 @@ const defaultEvents = ['mousedown', 'touchstart'] export function useClickAway( elRef: Ref, - onClickAway: (e: Event) => void, + callback: (e: Event) => void, events = defaultEvents ) { const handler = (e: Event) => { if (elRef.value && !elRef.value.contains(e.target as Node)) { - onClickAway(e) + callback(e) } } diff --git a/src/components/useHover/stories/UseHoverDemo.vue b/src/components/useHover/stories/UseHoverDemo.vue index ee0254b..e9b3594 100755 --- a/src/components/useHover/stories/UseHoverDemo.vue +++ b/src/components/useHover/stories/UseHoverDemo.vue @@ -22,7 +22,7 @@ export default Vue.extend({ }) - diff --git a/src/components/useIntersection/stories/UseIntersectionDemo.vue b/src/components/useIntersection/stories/UseIntersectionDemo.vue index 2dbb774..c3292ac 100755 --- a/src/components/useIntersection/stories/UseIntersectionDemo.vue +++ b/src/components/useIntersection/stories/UseIntersectionDemo.vue @@ -5,6 +5,7 @@ class="intersection__el" :options="intersectionOpts" @change="handleIntersectionChange" + @paused="handleIntersectionPaused" /> @@ -34,12 +35,21 @@ export default Vue.extend({ target.classList.toggle('-is-active', isVisible) } - return { divElements, intersectionOpts, handleIntersectionChange } + const handleIntersectionPaused = (target: Element, isPaused: boolean) => { + target.classList.toggle('-is-paused', isPaused) + } + + return { + divElements, + intersectionOpts, + handleIntersectionChange, + handleIntersectionPaused + } } }) - diff --git a/src/components/useIntersection/stories/UseIntersectionElementDemo.vue b/src/components/useIntersection/stories/UseIntersectionElementDemo.vue index 166d107..fad3ebd 100755 --- a/src/components/useIntersection/stories/UseIntersectionElementDemo.vue +++ b/src/components/useIntersection/stories/UseIntersectionElementDemo.vue @@ -1,6 +1,22 @@ @@ -21,16 +37,32 @@ export default Vue.extend({ }, setup({ options }, { emit }) { const elRef = ref(null) - const { observedEntry } = useIntersection( + + const { observedEntry, start, stop } = useIntersection( elRef, options as IntersectionObserverInit ) + watch(() => { if (!observedEntry.value) return emit('change', observedEntry.value) }) - return { elRef } + let isObserving = ref(true) + const handleObserverState = () => { + if (!observedEntry.value) return + if (isObserving.value) { + stop() + emit('paused', observedEntry.value.target, true) + isObserving.value = false + } else { + start() + emit('paused', observedEntry.value.target, false) + isObserving.value = true + } + } + + return { handleObserverState, isObserving, elRef } } }) diff --git a/src/components/useIntersection/stories/useIntersection.story.ts b/src/components/useIntersection/stories/useIntersection.story.ts index f1a0217..2346518 100755 --- a/src/components/useIntersection/stories/useIntersection.story.ts +++ b/src/components/useIntersection/stories/useIntersection.story.ts @@ -18,7 +18,15 @@ const basicDemo = () => ({ demo-name="UseIntersectionDemo.vue" > - + ` @@ -34,7 +42,15 @@ const advancedDemo = () => ({ demo-name="UseIntersectionAdvancedDemo.vue" > - + ` diff --git a/src/components/useMedia/stories/UseMediaAdvancedDemo.vue b/src/components/useMedia/stories/UseMediaAdvancedDemo.vue index ebdec12..3576645 100755 --- a/src/components/useMedia/stories/UseMediaAdvancedDemo.vue +++ b/src/components/useMedia/stories/UseMediaAdvancedDemo.vue @@ -78,7 +78,7 @@ export default Vue.extend({ }) - diff --git a/src/components/useRafFn/stories/UseRafFnDemo.vue b/src/components/useRafFn/stories/UseRafFnDemo.vue index 1ac5cf0..3c0c532 100755 --- a/src/components/useRafFn/stories/UseRafFnDemo.vue +++ b/src/components/useRafFn/stories/UseRafFnDemo.vue @@ -91,7 +91,7 @@ export default Vue.extend({ }) -