Skip to content

Commit

Permalink
feat(hook): add useKeyPress
Browse files Browse the repository at this point in the history
  • Loading branch information
anncwb committed Dec 24, 2020
1 parent 819bcbe commit 3c3e640
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 82 deletions.
15 changes: 5 additions & 10 deletions src/components/Application/src/search/useMenuSearch.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { cloneDeep } from 'lodash-es';
import { ref, onBeforeUnmount, onBeforeMount, unref, Ref } from 'vue';
import { ref, onBeforeMount, unref, Ref } from 'vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { getMenus } from '/@/router/menus';
import type { Menu } from '/@/router/types';
import { filter, forEach } from '/@/utils/helper/treeHelper';
import { useDebounce } from '/@/hooks/core/useDebounce';
import { useGo } from '/@/hooks/web/usePage';
import { useScrollTo } from '/@/hooks/event/useScrollTo';
import { useKeyPress } from '/@/hooks/event/useKeyPress';

export interface SearchResult {
name: string;
Expand Down Expand Up @@ -50,12 +51,6 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
forEach(menuList, (item) => {
item.name = t(item.name);
});

document.addEventListener('keydown', registerKeyDown);
});

onBeforeUnmount(() => {
document.removeEventListener('keydown', registerKeyDown);
});

function search(e: ChangeEvent) {
Expand Down Expand Up @@ -151,8 +146,8 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
emit('close');
}

function registerKeyDown(e: KeyboardEvent) {
const keyCode = window.event ? e.keyCode : e.which;
useKeyPress(['enter', 'up', 'down'], (events) => {
const keyCode = events.keyCode;
switch (keyCode) {
case KeyCodeEnum.UP:
handleUp();
Expand All @@ -167,7 +162,7 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
handleClose();
break;
}
}
});

return { handleSearch, searchResult, keyword, activeIndex, handleMouseenter, handleEnter };
}
10 changes: 6 additions & 4 deletions src/hooks/core/useEffect.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { WatchOptions } from 'vue';
import { watch } from 'vue';
import { isFunction } from '/@/utils/is';

export const useEffect = (effectHandler: Fn, dependencies: any[]) => {
export function useEffect<T extends any = any>(
effectHandler: (deps: T[], prevDeps?: T[]) => () => void,
dependencies: T[]
) {
return watch(
dependencies,
(changedDependencies, prevDependencies, onCleanUp) => {
Expand All @@ -11,6 +13,6 @@ export const useEffect = (effectHandler: Fn, dependencies: any[]) => {
onCleanUp(effectCleaner);
}
},
{ immediate: true, deep: true } as WatchOptions
{ immediate: true, deep: true }
);
};
}
19 changes: 19 additions & 0 deletions src/hooks/core/useLockFn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ref, unref } from 'vue';

export function useLockFn<P extends any[] = any[], V extends any = any>(
fn: (...args: P) => Promise<V>
) {
const lockRef = ref(false);
return async function (...args: P) {
if (unref(lockRef)) return;
lockRef.value = true;
try {
const ret = await fn(...args);
lockRef.value = false;
return ret;
} catch (e) {
lockRef.value = false;
throw e;
}
};
}
47 changes: 0 additions & 47 deletions src/hooks/core/useModel.ts

This file was deleted.

58 changes: 58 additions & 0 deletions src/hooks/core/useState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { isObject } from '@vue/shared';
import { reactive, Ref, ref, readonly } from 'vue';
import { isFunction } from '/@/utils/is';

type State<T> = ((s: T) => T) | T;
type Dispatch<T> = (t: T) => void;

type DispatchState<T> = Dispatch<State<T>>;

type ResultState<T> = Readonly<Ref<T>>;

export function useState<T extends undefined>(
initialState: (() => T) | T
): [ResultState<T>, DispatchState<T>];

export function useState<T extends null>(
initialState: (() => T) | T
): [ResultState<T>, DispatchState<T>];

export function useState<T extends boolean>(
initialState: (() => T) | T
): [ResultState<boolean>, DispatchState<boolean>];

export function useState<T extends string>(
initialState: (() => T) | T
): [ResultState<string>, DispatchState<string>];

export function useState<T extends number>(
initialState: (() => T) | T
): [ResultState<number>, DispatchState<number>];

export function useState<T extends object>(
initialState: (() => T) | T
): [Readonly<T>, DispatchState<T>];

export function useState<T extends any>(
initialState: (() => T) | T
): [Readonly<T>, DispatchState<T>];

export function useState<T>(initialState: (() => T) | T): [ResultState<T> | T, DispatchState<T>] {
if (isFunction(initialState)) {
initialState = (initialState as Fn)();
}

if (isObject(initialState)) {
const state = reactive({ data: initialState }) as any;
const setState = (newState: T) => {
state.data = newState;
};
return [readonly(state), setState];
} else {
const state = ref(initialState) as any;
const setState = (newState: T) => {
state.value = newState;
};
return [readonly(state), setState];
}
}
20 changes: 0 additions & 20 deletions src/hooks/core/useToggle.ts

This file was deleted.

172 changes: 172 additions & 0 deletions src/hooks/event/useKeyPress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// 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: Record<string, number | number[]> = {
esc: 27,
tab: 9,
enter: 13,
space: 32,
up: 38,
left: 37,
right: 39,
down: 40,
delete: [8, 46],
};

// 键盘事件 key 别名
const aliasKeyMap: Record<string, 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: Record<string, (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;
}

let targetElement: TargetElement | undefined | null;

if (isFunction(target)) {
targetElement = target();
} else {
targetElement = unref(target);
}
return targetElement;
}
2 changes: 1 addition & 1 deletion src/hooks/web/useScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function useScript(opts: ScriptOptions) {
isLoading.value = false;
success.value = true;
error.value = false;
resolve();
resolve('');
};

script.onerror = function (err) {
Expand Down

0 comments on commit 3c3e640

Please sign in to comment.