Files
coze-studio/frontend/packages/workflow/render/src/utils/shortcuts-utils.ts

210 lines
4.8 KiB
TypeScript

/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const isAppleDevice = /(mac|iphone|ipod|ipad)/i.test(
typeof navigator !== 'undefined' ? navigator?.platform : '',
);
// Keyboard Event keyCode Alias
const aliasKeyCodeMap = {
'0': 48,
'1': 49,
'2': 50,
'3': 51,
'4': 52,
'5': 53,
'6': 54,
'7': 55,
'8': 56,
'9': 57,
backspace: 8,
tab: 9,
enter: 13,
shift: 16,
ctrl: 17,
alt: 18,
pausebreak: 19,
capslock: 20,
esc: 27,
space: 32,
pageup: 33,
pagedown: 34,
end: 35,
home: 36,
leftarrow: 37,
uparrow: 38,
rightarrow: 39,
downarrow: 40,
insert: 45,
delete: 46,
a: 65,
b: 66,
c: 67,
d: 68,
e: 69,
f: 70,
g: 71,
h: 72,
i: 73,
j: 74,
k: 75,
l: 76,
m: 77,
n: 78,
o: 79,
p: 80,
q: 81,
r: 82,
s: 83,
t: 84,
u: 85,
v: 86,
w: 87,
x: 88,
y: 89,
z: 90,
leftwindowkey: 91,
rightwindowkey: 92,
meta: isAppleDevice ? [91, 93] : [91, 92],
selectkey: 93,
numpad0: 96,
numpad1: 97,
numpad2: 98,
numpad3: 99,
numpad4: 100,
numpad5: 101,
numpad6: 102,
numpad7: 103,
numpad8: 104,
numpad9: 105,
multiply: 106,
add: 107,
subtract: 109,
decimalpoint: 110,
divide: 111,
f1: 112,
f2: 113,
f3: 114,
f4: 115,
f5: 116,
f6: 117,
f7: 118,
f8: 119,
f9: 120,
f10: 121,
f11: 122,
f12: 123,
numlock: 144,
scrolllock: 145,
semicolon: 186,
equalsign: 187,
'=': 187,
comma: 188,
dash: 189,
'-': 189,
period: 190,
forwardslash: 191,
graveaccent: 192,
openbracket: 219,
backslash: 220,
closebracket: 221,
singlequote: 222,
};
const modifierKey = {
ctrl: (event: KeyboardEvent) => event.ctrlKey,
shift: (event: KeyboardEvent) => event.shiftKey,
alt: (event: KeyboardEvent) => event.altKey,
meta: (event: KeyboardEvent) => {
if (event.type === 'keyup') {
return aliasKeyCodeMap.meta.includes(event.keyCode);
}
return event.metaKey;
},
};
// Number of activation keys based on event
function countKeyByEvent(event: KeyboardEvent): number {
const countOfModifier = Object.keys(modifierKey).reduce((total, key) => {
if (modifierKey[key](event)) {
return total + 1;
}
return total;
}, 0);
// 16 17 18 91 92 is the keyCode of the modifier key. If keyCode is a modifier key, then the number of activations is the number of modifier keys. If not, then + 1 is required.
return [16, 17, 18, 91, 92].includes(event.keyCode)
? countOfModifier
: countOfModifier + 1;
}
/**
*
* @param event
* @param keyString 'ctrl.s' 'meta.s'
* @param exactMatch
*/
function isKeyStringMatch(
event: KeyboardEvent,
keyString: string,
exactMatch = true,
): boolean {
// When the browser automatically completes the input, keyDown and keyUp events will be triggered, but at this time event.key, etc. are empty
if (!event.key || !keyString) {
return false;
}
// String in turn determines whether there is a key combination
const genArr = keyString.split(/\s+/);
let genLen = 0;
for (const key of genArr) {
// key combination
const genModifier = modifierKey[key];
// keyCode alias
const aliasKeyCode: number | number[] = aliasKeyCodeMap[key.toLowerCase()];
if (
(genModifier && genModifier(event)) ||
(aliasKeyCode && aliasKeyCode === event.keyCode)
) {
genLen++;
}
}
/**
* It is necessary to determine that the triggered key is exactly the same as the monitored key. The judgment method is that the triggered key is equal to the monitored key.
* genLen == genArr.length can determine that there is a listening key among the triggered keys
* countKeyByEvent (event) === genArr.length The number of keys triggered by the judgment is equal to the number of keys listened to
* It is mainly used to prevent the situation that pressing a subset of the key combination will also trigger, such as listening to the event that ctrl + a will trigger listening to the two keys ctrl and a.
*/
if (exactMatch) {
return genLen === genArr.length && countKeyByEvent(event) === genArr.length;
}
return genLen === genArr.length;
}
/**
* Matches the specified shortcut
* @param event
* @param shortcuts
*/
export function isShortcutsMatch(
event: KeyboardEvent,
shortcuts: string[],
): boolean {
return shortcuts.some(keyString => isKeyStringMatch(event, keyString));
}