-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
lmhcoding
committed
Sep 19, 2020
1 parent
d06332a
commit 8db2731
Showing
4 changed files
with
252 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
# ``useEvent`` | ||
--- | ||
|
||
Sensor hook that subscribes a ``handler`` to events | ||
|
||
## Usage | ||
|
||
1. 当不传target时,target 默认为 ``window`` | ||
|
||
```vue | ||
import { useEvent } from 'composition-fn' | ||
export default { | ||
setup () { | ||
useEvent('resize', () => { | ||
console.log('window resize') | ||
}) | ||
} | ||
} | ||
``` | ||
|
||
2. target 可为 CSS 选择器 | ||
|
||
```vue | ||
<template> | ||
<div id="test">test</test> | ||
</template> | ||
<script> | ||
import { useEvent } from 'composition-fn' | ||
export default { | ||
setup () { | ||
useEvent('click', () => { | ||
console.log('window resize') | ||
}, true, '#test') | ||
} | ||
} | ||
</script> | ||
``` | ||
|
||
3. target 为 Ref<EventTarget> | ||
|
||
```vue | ||
<template> | ||
<div ref="target">test</test> | ||
</template> | ||
<script> | ||
import { ref } from 'vue' | ||
import { useEvent } from 'composition-fn' | ||
export default { | ||
setup () { | ||
const target = ref(null) | ||
useEvent('click', () => { | ||
console.log('window resize') | ||
}, true, '#test') | ||
return { | ||
target | ||
} | ||
} | ||
} | ||
</script> | ||
``` | ||
|
||
4. target 为 EventTarget | ||
|
||
```vue | ||
<template> | ||
<div id="target">test</test> | ||
</template> | ||
<script> | ||
import { ref, onMounted } from 'vue' | ||
import { useEvent } from 'composition-fn' | ||
export default { | ||
setup () { | ||
const target = ref(null) | ||
onMounted(() => { | ||
useEvent('click', () => { | ||
console.log('window resize') | ||
}, true, document.querySelector('#target)) | ||
}) | ||
return { | ||
target | ||
} | ||
} | ||
} | ||
</script> | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from './useTitle' | ||
export * from './useEvent' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
/* eslint-disable no-redeclare */ | ||
import { Ref, onMounted, onUnmounted, isRef, getCurrentInstance } from 'vue' | ||
|
||
type Target = Ref<EventTarget> | EventTarget | string | ||
|
||
interface WindowEventHandler<T extends keyof WindowEventMap> { | ||
(this: Window, e: WindowEventMap[T]): any | ||
} | ||
|
||
interface DocumentEventHandler<T extends keyof DocumentEventMap> { | ||
(this: Document, e: DocumentEventMap[T]): any | ||
} | ||
|
||
type HandlerOptions = boolean | AddEventListenerOptions | ||
type DocumentEvents = keyof DocumentEventMap | ||
type WindowEvents = keyof WindowEventMap | ||
|
||
function getTarget(target: Target): EventTarget | null { | ||
if (!target) { | ||
return window | ||
} | ||
if (typeof target === 'string') { | ||
const dom = document.querySelector(target) | ||
if (!dom && process.env.NODE_ENV !== 'production') { | ||
console.error('target is not found') | ||
return null | ||
} | ||
return dom | ||
} | ||
if (isRef(target)) { | ||
return target.value | ||
} | ||
return target | ||
} | ||
|
||
function registerEvent( | ||
target: Target, | ||
event: string, | ||
cb: EventListenerOrEventListenerObject, | ||
options?: HandlerOptions | ||
) { | ||
const eventTarget = getTarget(target) | ||
if (eventTarget) { | ||
eventTarget.addEventListener(event, cb, options) | ||
} | ||
return eventTarget | ||
} | ||
|
||
export function useEvent<T extends WindowEvents>( | ||
event: T, | ||
handler: WindowEventHandler<T>, | ||
options?: HandlerOptions | ||
): void | ||
export function useEvent<T extends DocumentEvents>( | ||
event: T, | ||
handler: DocumentEventHandler<T>, | ||
options?: HandlerOptions, | ||
target?: Target | ||
): void | ||
export function useEvent<T extends DocumentEvents>( | ||
event: T, | ||
handler: DocumentEventHandler<T>, | ||
options?: HandlerOptions, | ||
target?: string | ||
): void | ||
export function useEvent( | ||
event: string, | ||
cb: EventListenerOrEventListenerObject, | ||
options?: HandlerOptions, | ||
target: Target = window | ||
) { | ||
if (!event || !cb) { | ||
return | ||
} | ||
let eventTarget: EventTarget | null | ||
function register() { | ||
eventTarget = registerEvent(target, event, cb, options) | ||
} | ||
const currentInstance = getCurrentInstance() | ||
if (currentInstance) { | ||
if (currentInstance.isMounted) { | ||
register() | ||
} else { | ||
onMounted(() => { | ||
register() | ||
}) | ||
} | ||
onUnmounted(() => { | ||
if (eventTarget) { | ||
eventTarget.removeEventListener(event, cb) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { mount, VueWrapper } from '@vue/test-utils' | ||
import { defineComponent, ref, Ref } from 'vue' | ||
import { useEvent } from '../src/useEvent' | ||
|
||
const props = { | ||
name: 'resize' as 'resize', | ||
handler: jest.fn(), | ||
target: { | ||
addEventListener: jest.fn(), | ||
removeEventListener: jest.fn() | ||
} as unknown as EventTarget | ||
} | ||
|
||
const props1 = { | ||
...props, | ||
target: ref({ | ||
addEventListener: jest.fn(), | ||
removeEventListener: jest.fn() | ||
}) as unknown as Ref<EventTarget> | ||
} | ||
|
||
describe('test useEvent when target is an EventTarget', () => { | ||
let wrapper: VueWrapper<any> | ||
beforeEach(() => { | ||
const comp = defineComponent({ | ||
template: '<div>test</div>', | ||
setup () { | ||
useEvent(props.name, props.handler, true, props.target) | ||
} | ||
}) | ||
wrapper = mount(comp) | ||
}) | ||
test('should call addEventListener on mount', () => { | ||
expect(props.target.addEventListener).toHaveBeenCalledTimes(1) | ||
expect(props.target.addEventListener).toHaveBeenCalledWith(props.name, props.handler, true) | ||
expect((props.target.addEventListener as any).mock.instances).toEqual([props.target]) | ||
}) | ||
test('should call removeEventListener on unmount', () => { | ||
wrapper.unmount() | ||
expect(props.target.removeEventListener).toHaveBeenCalledTimes(1) | ||
expect(props.target.removeEventListener).toHaveBeenCalledWith(props.name, props.handler) | ||
expect((props.target.removeEventListener as any).mock.instances).toEqual([props.target]) | ||
}) | ||
}) | ||
|
||
describe('test useEvent when target is a Ref', () => { | ||
let wrapper: VueWrapper<any> | ||
beforeEach(() => { | ||
const comp = defineComponent({ | ||
template: '<div>test</div>', | ||
setup () { | ||
useEvent(props1.name, props1.handler, true, props1.target) | ||
} | ||
}) | ||
wrapper = mount(comp) | ||
}) | ||
test('should call addEventListener on mount', () => { | ||
expect(props1.target.value.addEventListener).toHaveBeenCalledTimes(1) | ||
expect(props1.target.value.addEventListener).toHaveBeenCalledWith(props1.name, props1.handler, true) | ||
expect((props1.target.value.addEventListener as any).mock.instances).toEqual([props1.target.value]) | ||
}) | ||
test('should call removeEventListener on unmount', () => { | ||
wrapper.unmount() | ||
expect(props1.target.value.removeEventListener).toHaveBeenCalledTimes(1) | ||
expect(props1.target.value.removeEventListener).toHaveBeenCalledWith(props1.name, props1.handler) | ||
expect((props1.target.value.removeEventListener as any).mock.instances).toEqual([props1.target.value]) | ||
}) | ||
}) |