23 changed files with 52 additions and 394 deletions
@ -1,6 +0,0 @@ |
|||
export const enum KeyCodeEnum { |
|||
UP = 38, |
|||
DOWN = 40, |
|||
ENTER = 13, |
|||
ESC = 27, |
|||
} |
|||
@ -1,39 +0,0 @@ |
|||
export interface DebounceAndThrottleOptions { |
|||
// 立即执行
|
|||
immediate?: boolean; |
|||
|
|||
// 是否为debounce
|
|||
debounce?: boolean; |
|||
// 只执行一次
|
|||
once?: boolean; |
|||
} |
|||
export type CancelFn = () => void; |
|||
|
|||
export type DebounceAndThrottleProcedure<T extends unknown[]> = (...args: T) => unknown; |
|||
|
|||
export type DebounceAndThrottleProcedureResult<T extends unknown[]> = [ |
|||
DebounceAndThrottleProcedure<T>, |
|||
CancelFn |
|||
]; |
|||
|
|||
import { |
|||
// throttle,
|
|||
useThrottle, |
|||
} from './useThrottle'; |
|||
|
|||
/** |
|||
* @description: Applicable in components |
|||
*/ |
|||
export function useDebounce<T extends unknown[]>( |
|||
handle: DebounceAndThrottleProcedure<T>, |
|||
wait: number, |
|||
options: DebounceAndThrottleOptions = {} |
|||
): DebounceAndThrottleProcedureResult<T> { |
|||
return useThrottle( |
|||
handle, |
|||
wait, |
|||
Object.assign(options, { |
|||
debounce: true, |
|||
}) |
|||
); |
|||
} |
|||
@ -1,84 +0,0 @@ |
|||
export interface DebounceAndThrottleOptions { |
|||
// 立即执行
|
|||
immediate?: boolean; |
|||
|
|||
// 是否为debounce
|
|||
debounce?: boolean; |
|||
// 只执行一次
|
|||
once?: boolean; |
|||
} |
|||
export type CancelFn = () => void; |
|||
|
|||
export type DebounceAndThrottleProcedure<T extends unknown[]> = (...args: T) => unknown; |
|||
|
|||
export type DebounceAndThrottleProcedureResult<T extends unknown[]> = [ |
|||
DebounceAndThrottleProcedure<T>, |
|||
CancelFn |
|||
]; |
|||
|
|||
import { isFunction } from '/@/utils/is'; |
|||
export function throttle<T extends unknown[]>( |
|||
handle: DebounceAndThrottleProcedure<T>, |
|||
wait: number, |
|||
options: DebounceAndThrottleOptions = {} |
|||
): DebounceAndThrottleProcedureResult<T> { |
|||
if (!isFunction(handle)) { |
|||
throw new Error('handle is not Function!'); |
|||
} |
|||
let { immediate = false } = options; |
|||
const { once = false, debounce = false } = options; |
|||
let timeoutId: Nullable<TimeoutHandle>; |
|||
// Has it been cancelled
|
|||
let cancelled: boolean | null = false; |
|||
/** |
|||
* @description: clear timer |
|||
*/ |
|||
function clearTimer() { |
|||
if (timeoutId) { |
|||
window.clearTimeout(timeoutId); |
|||
timeoutId = null; |
|||
} |
|||
} |
|||
/** cancel exec */ |
|||
function cancel() { |
|||
clearTimer(); |
|||
cancelled = true; |
|||
} |
|||
// If once is true, only execute once
|
|||
function cancelExec(): void { |
|||
once && cancel(); |
|||
} |
|||
function fn(this: unknown, ...args: T) { |
|||
// If it has been cancelled, it will not be executed
|
|||
if (cancelled) { |
|||
return; |
|||
} |
|||
const exec = () => { |
|||
!debounce && clearTimer(); |
|||
handle.apply(this, args); |
|||
cancelExec(); |
|||
}; |
|||
if (immediate) { |
|||
immediate = false; |
|||
const callNow = !timeoutId; |
|||
if (callNow) { |
|||
exec(); |
|||
timeoutId = null; |
|||
} |
|||
} else { |
|||
debounce && clearTimer(); |
|||
if (!timeoutId || debounce) { |
|||
timeoutId = setTimeout(exec, wait); |
|||
} |
|||
} |
|||
} |
|||
return [fn, cancel]; |
|||
} |
|||
|
|||
export function useThrottle<T extends unknown[]>( |
|||
handle: DebounceAndThrottleProcedure<T>, |
|||
wait: number, |
|||
options: DebounceAndThrottleOptions = {} |
|||
): DebounceAndThrottleProcedureResult<T> { |
|||
return throttle(handle, wait, options); |
|||
} |
|||
@ -1,30 +0,0 @@ |
|||
import { useDebounce } from '/@/hooks/core/useDebounce'; |
|||
import { addResizeListener, removeResizeListener } from '/@/utils/event'; |
|||
|
|||
interface WindowSizeOptions { |
|||
once?: boolean; |
|||
immediate?: boolean; |
|||
} |
|||
|
|||
export function useElResize<T>( |
|||
el: Element | typeof window, |
|||
fn: Fn<T>, |
|||
wait = 100, |
|||
options?: WindowSizeOptions |
|||
) { |
|||
let handler = () => { |
|||
fn(); |
|||
}; |
|||
const [handleSize, cancel] = useDebounce(handler, wait, options); |
|||
handler = wait ? handleSize : handler; |
|||
|
|||
function start() { |
|||
addResizeListener(el, handler); |
|||
} |
|||
function stop() { |
|||
removeResizeListener(el, handler); |
|||
cancel(); |
|||
} |
|||
|
|||
return [start, stop]; |
|||
} |
|||
@ -1,164 +0,0 @@ |
|||
// https://ahooks.js.org/zh-CN/hooks/dom/use-key-press
|
|||
|
|||
import type { Ref } from 'vue'; |
|||
import { onBeforeUnmount, onMounted, unref } from 'vue'; |
|||
import { noop } from '/@/utils'; |
|||
import { isFunction, isString, isNumber, isArray } from '/@/utils/is'; |
|||
|
|||
export type KeyPredicate = (event: KeyboardEvent) => boolean; |
|||
export type keyType = KeyboardEvent['keyCode'] | KeyboardEvent['key']; |
|||
export type KeyFilter = keyType | keyType[] | ((event: KeyboardEvent) => boolean); |
|||
export type EventHandler = (event: KeyboardEvent) => void; |
|||
|
|||
export type keyEvent = 'keydown' | 'keyup'; |
|||
|
|||
export type TargetElement = HTMLElement | Element | Document | Window; |
|||
export type Target = Ref<TargetElement>; |
|||
|
|||
export type EventOption = { |
|||
events?: keyEvent[]; |
|||
target?: Target; |
|||
}; |
|||
|
|||
const defaultEvents: keyEvent[] = ['keydown']; |
|||
|
|||
// 键盘事件 keyCode 别名
|
|||
const aliasKeyCodeMap: Recordable<number | number[]> = { |
|||
esc: 27, |
|||
tab: 9, |
|||
enter: 13, |
|||
space: 32, |
|||
up: 38, |
|||
left: 37, |
|||
right: 39, |
|||
down: 40, |
|||
delete: [8, 46], |
|||
}; |
|||
|
|||
// 键盘事件 key 别名
|
|||
const aliasKeyMap: Recordable<string | string[]> = { |
|||
esc: 'Escape', |
|||
tab: 'Tab', |
|||
enter: 'Enter', |
|||
space: ' ', |
|||
// IE11 uses key names without `Arrow` prefix for arrow keys.
|
|||
up: ['Up', 'ArrowUp'], |
|||
left: ['Left', 'ArrowLeft'], |
|||
right: ['Right', 'ArrowRight'], |
|||
down: ['Down', 'ArrowDown'], |
|||
delete: ['Backspace', 'Delete'], |
|||
}; |
|||
|
|||
// 修饰键
|
|||
const modifierKey: Recordable<(event: KeyboardEvent) => boolean> = { |
|||
ctrl: (event: KeyboardEvent) => event.ctrlKey, |
|||
shift: (event: KeyboardEvent) => event.shiftKey, |
|||
alt: (event: KeyboardEvent) => event.altKey, |
|||
meta: (event: KeyboardEvent) => event.metaKey, |
|||
}; |
|||
|
|||
/** |
|||
* 判断按键是否激活 |
|||
* @param [event: KeyboardEvent]键盘事件 |
|||
* @param [keyFilter: any] 当前键 |
|||
* @returns Boolean |
|||
*/ |
|||
function genFilterKey(event: any, keyFilter: any) { |
|||
// 浏览器自动补全 input 的时候,会触发 keyDown、keyUp 事件,但此时 event.key 等为空
|
|||
if (!event.key) { |
|||
return false; |
|||
} |
|||
|
|||
// 数字类型直接匹配事件的 keyCode
|
|||
if (isNumber(keyFilter)) { |
|||
return event.keyCode === keyFilter; |
|||
} |
|||
// 字符串依次判断是否有组合键
|
|||
const genArr = keyFilter.split('.'); |
|||
let genLen = 0; |
|||
for (const key of genArr) { |
|||
// 组合键
|
|||
const genModifier = modifierKey[key]; |
|||
// key 别名
|
|||
const aliasKey = aliasKeyMap[key]; |
|||
// keyCode 别名
|
|||
const aliasKeyCode = aliasKeyCodeMap[key]; |
|||
/** |
|||
* 满足以上规则 |
|||
* 1. 自定义组合键别名 |
|||
* 2. 自定义 key 别名 |
|||
* 3. 自定义 keyCode 别名 |
|||
* 4. 匹配 key 或 keyCode |
|||
*/ |
|||
if ( |
|||
(genModifier && genModifier(event)) || |
|||
(aliasKey && isArray(aliasKey) ? aliasKey.includes(event.key) : aliasKey === event.key) || |
|||
(aliasKeyCode && isArray(aliasKeyCode) |
|||
? aliasKeyCode.includes(event.keyCode) |
|||
: aliasKeyCode === event.keyCode) || |
|||
event.key.toUpperCase() === key.toUpperCase() |
|||
) { |
|||
genLen++; |
|||
} |
|||
} |
|||
return genLen === genArr.length; |
|||
} |
|||
|
|||
/** |
|||
* 键盘输入预处理方法 |
|||
*/ |
|||
function genKeyFormat(keyFilter: any): KeyPredicate { |
|||
if (isFunction(keyFilter)) { |
|||
return keyFilter; |
|||
} |
|||
if (isString(keyFilter) || isNumber(keyFilter)) { |
|||
return (event: KeyboardEvent) => genFilterKey(event, keyFilter); |
|||
} |
|||
if (isArray(keyFilter)) { |
|||
return (event: KeyboardEvent) => keyFilter.some((item: any) => genFilterKey(event, item)); |
|||
} |
|||
return keyFilter ? () => true : () => false; |
|||
} |
|||
|
|||
export function useKeyPress( |
|||
keyFilter: KeyFilter, |
|||
eventHandler: EventHandler = noop, |
|||
option: EventOption = {} |
|||
) { |
|||
const { events = defaultEvents, target } = option; |
|||
|
|||
let el: TargetElement | null | undefined; |
|||
|
|||
function handler(event: any) { |
|||
const genGuard: KeyPredicate = genKeyFormat(keyFilter); |
|||
if (genGuard(event)) { |
|||
return eventHandler(event); |
|||
} |
|||
} |
|||
|
|||
onMounted(() => { |
|||
el = getTargetElement(target, window); |
|||
if (!el) return; |
|||
|
|||
for (const eventName of events) { |
|||
el.addEventListener(eventName, handler); |
|||
} |
|||
}); |
|||
|
|||
onBeforeUnmount(() => { |
|||
if (!el) return; |
|||
for (const eventName of events) { |
|||
el.removeEventListener(eventName, handler); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
export function getTargetElement( |
|||
target?: Target, |
|||
defaultElement?: TargetElement |
|||
): TargetElement | undefined | null { |
|||
if (!target) { |
|||
return defaultElement; |
|||
} |
|||
return isFunction(target) ? target() : unref(target); |
|||
} |
|||
Loading…
Reference in new issue