Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix pressing functional keys and add missed chars #185

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/dry-ducks-remain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@interactors/globals": patch
"@interactors/keyboard": patch
---

Improve keyboard layout, by adding missing chars and fix pressing functional keys
2 changes: 1 addition & 1 deletion packages/globals/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './globals'
export { KeyboardLayout, KeyCode } from './keyboard-layout'
export * from './keyboard-layout'
55 changes: 52 additions & 3 deletions packages/globals/src/keyboard-layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,60 @@ export type KeyCode =
| "Slash"
| "Backquote"
| "BracketLeft"
| "Backslack"
| "Backslash"
Comment on lines -102 to +454
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤣

| "BracketRight"
| "Quote";

export const FunctionalKeys: KeyCode[] = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just go ahead and import all of the different key types from MDN https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values and name them after what is found there.

This way, we will be sure and have an exhaustive list, and the category of key will be aligned with what is found there.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to make each key type in its own array so that we can filter accordingly? I'm not sure what "FunctionalKeys" means or if maybe it should be a combination of smaller categories of key code

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two different properties of a KeyboardEvent: key and code. Here for FunctionalKeys and KeyCode we described codes and real keys defined in layouts (like for example in defaultKeyMap for US layout). But for functional keys key and code almost the same and I need them to filter as non-printable characters.

In reality it's more complicated, like backspace removes the last character, but we don't have such functionality.

So what about KeyValues they are described key property, but I don't know what code they should have. And I think I got your point. We don't filter all these key values as non-printable characters.

"Backspace",
"Tab",
"Enter",
"ShiftLeft",
"ShiftRight",
"ControlLeft",
"ControlRight",
"AltLeft",
"AltRight",
"Pause",
"CapsLock",
"Escape",
"PageUp",
"PageDown",
"End",
"Home",
"ArrowLeft",
"ArrowUp",
"ArrowRight",
"ArrowDown",
"PrintScreen",
"Insert",
"Delete",
"MetaLeft",
"MetaRight",
"ContextMenu",
"F1",
"F2",
"F3",
"F4",
"F5",
"F6",
"F7",
"F8",
"F9",
"F10",
"F11",
"F12",
"NumLock",
"ScrollLock",
];

export interface Key {
code: KeyCode;
key: string;
shiftKey?: true;
}

export interface KeyboardLayout {
getKey(code: KeyCode): string | undefined;
getCode(key: string): KeyCode | undefined;
getByCode(code: KeyCode): Key | undefined;
getByKey(key: string): Key | undefined;
}
9 changes: 5 additions & 4 deletions packages/keyboard/src/keyboard.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { globals, KeyCode } from '@interactors/globals';
import { FunctionalKeys, globals, KeyCode } from '@interactors/globals';
import { createInteractor } from '@interactors/core';
import { dispatchInput, dispatchKeyDown, dispatchKeyUp } from './dispatch';

Expand All @@ -19,12 +19,13 @@ const KeyboardInteractor = createInteractor('Keyboard')
await interactor.perform((element) => {
let activeElement = (element.ownerDocument.activeElement || element.ownerDocument.body) as HTMLElement;
if(options.key && !options.code) {
options.code = globals.keyboardLayout.getCode(options.key);
options = { ...options, ...globals.keyboardLayout.getByKey(options.key) };
}
if(options.code && !options.key) {
options.key = globals.keyboardLayout.getKey(options.code);
options = { ...options, ...globals.keyboardLayout.getByCode(options.code) };
}
if(dispatchKeyDown(activeElement, options) && isTextElement(activeElement)) {
let isFunctionalKey = options.code ? FunctionalKeys.includes(options.code) : false;
if(dispatchKeyDown(activeElement, options) && isTextElement(activeElement) && !isFunctionalKey) {
// don't change the value if the keydown event was stopped
setValue(activeElement, activeElement.value + options.key);
// input is not dispatched if the keydown event was stopped
Expand Down
202 changes: 88 additions & 114 deletions packages/keyboard/src/layout.ts
Original file line number Diff line number Diff line change
@@ -1,122 +1,96 @@
import { KeyCode, KeyboardLayout } from '@interactors/globals';
import { Key, KeyCode, KeyboardLayout } from "@interactors/globals";

export function createKeyboardLayout(map: Iterable<[KeyCode, string]>): KeyboardLayout {
let keyMap = new Map(map);
let codeMap = new Map(Array.from(map).map(([key, value]) => [value, key]));
export const defaultKeyMap = [
..."0123456789".split("").map((digit) => ({ code: `Digit${digit}`, key: digit })),
...")!@#$%^&*(".split("").map((key, digit) => ({ code: `Digit${digit}`, key, shiftKey: true })),
..."abcdefghijklmnopqrstuvwxyz".split("").map((key) => ({ code: `Key${key.toUpperCase()}`, key })),
..."ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("").map((key) => ({ code: `Key${key}`, key, shiftKey: true })),
...Array.from({ length: 12 })
.map((_, digit) => `F${digit + 1}`)
.map((key) => ({ code: key, key })),
...[
"Backspace",
"Tab",
"Enter",
"Pause",
"CapsLock",
"Escape",
"PageUp",
"PageDown",
"End",
"Home",
"ArrowLeft",
"ArrowUp",
"ArrowRight",
"ArrowDown",
"PrintScreen",
"Insert",
"Delete",
"ContextMenu",
"NumLock",
"ScrollLock",
].map((code) => ({ code, key: code })),

{ code: "Space", key: " " },
{ code: "AltLeft", key: "Alt" },
{ code: "AltRight", key: "Alt" },
{ code: "ShiftLeft", key: "Shift" },
{ code: "ShiftRight", key: "Shift" },
{ code: "ControlLeft", key: "Control" },
{ code: "ControlRight", key: "Control" },
{ code: "MetaLeft", key: "Meta" },
{ code: "MetaRight", key: "Meta" },

{ code: "Semicolon", key: ";" },
{ code: "Equal", key: "=" },
{ code: "Comma", key: "," },
{ code: "Minus", key: "-" },
{ code: "Period", key: "." },
{ code: "Slash", key: "/" },
{ code: "Backquote", key: "`" },
{ code: "BracketLeft", key: "[" },
{ code: "Backslash", key: "\\" },
{ code: "BracketRight", key: "]" },
{ code: "Quote", key: "'" },

{ code: "Semicolon", key: ":", shiftKey: true },
{ code: "Equal", key: "+", shiftKey: true },
{ code: "Comma", key: "<", shiftKey: true },
{ code: "Minus", key: "_", shiftKey: true },
{ code: "Period", key: ">", shiftKey: true },
{ code: "Slash", key: "?", shiftKey: true },
{ code: "Backquote", key: "~", shiftKey: true },
{ code: "BracketLeft", key: "{", shiftKey: true },
{ code: "Backslash", key: "|", shiftKey: true },
{ code: "BracketRight", key: "}", shiftKey: true },
{ code: "Quote", key: '"', shiftKey: true },

..."0123456789".split("").map((digit) => ({ code: `Numpad${digit}`, key: digit })),
{ code: "NumpadMultiply", key: "*" },
{ code: "NumpadAdd", key: "+" },
{ code: "NumpadSubtract", key: "-" },
{ code: "NumpadDecimal", key: "." },
{ code: "NumpadDivide", key: "/" },
] as Key[];

export function createKeyboardLayout(override: Key[] = []): KeyboardLayout {
let codeMap = new Map<KeyCode, Key[]>();
let keyMap = new Map<string, Key[]>();

[...override, ...defaultKeyMap].forEach((key) => {
codeMap.get(key.code)?.push(key) ?? codeMap.set(key.code, [key]);
keyMap.get(key.key)?.push(key) ?? keyMap.set(key.key, [key]);
});

return {
getKey(code: KeyCode): string | undefined {
return keyMap.get(code);
getByCode(code: KeyCode): Key | undefined {
return codeMap.get(code)?.[0];
},

getCode(key: string): KeyCode | undefined {
return codeMap.get(key);
getByKey(key: string): Key | undefined {
return keyMap.get(key)?.[0];
},
}
};
}

export const US_INTERNATIONAL = createKeyboardLayout([
["Backspace", "Backspace"],
["Tab", "Tab"],
["Enter", "Enter"],
["ShiftLeft", "Shift"],
["ShiftRight", "Shift"],
["ControlLeft", "Control"],
["ControlRight", "Control"],
["AltLeft", "Alt"],
["AltRight", "Alt"],
["Pause", "Pause"],
["CapsLock", "CapsLock"],
["Escape", "Escape"],
["Space", " "],
["PageUp", "PageUp"],
["PageDown", "PageDown"],
["End", "End"],
["Home", "Home"],
["ArrowLeft", "ArrowLeft"],
["ArrowUp", "ArrowUp"],
["ArrowRight", "ArrowRight"],
["ArrowDown", "ArrowDown"],
["PrintScreen", "PrintScreen"],
["Insert", "Insert"],
["Delete", "Delete"],
["Digit0", "0"],
["Digit1", "1"],
["Digit2", "2"],
["Digit3", "3"],
["Digit4", "4"],
["Digit5", "5"],
["Digit6", "6"],
["Digit7", "7"],
["Digit8", "8"],
["Digit9", "9"],
["KeyA", "a"],
["KeyB", "b"],
["KeyC", "c"],
["KeyD", "d"],
["KeyE", "e"],
["KeyF", "f"],
["KeyG", "g"],
["KeyH", "h"],
["KeyI", "i"],
["KeyJ", "j"],
["KeyK", "k"],
["KeyL", "l"],
["KeyM", "m"],
["KeyN", "n"],
["KeyO", "o"],
["KeyP", "p"],
["KeyQ", "q"],
["KeyR", "r"],
["KeyS", "s"],
["KeyT", "t"],
["KeyU", "u"],
["KeyV", "v"],
["KeyW", "w"],
["KeyX", "x"],
["KeyY", "y"],
["KeyZ", "z"],
["MetaLeft", "Meta"],
["MetaRight", "Meta"],
["ContextMenu", "ContextMenu"],
["Numpad0", "0"],
["Numpad1", "1"],
["Numpad2", "2"],
["Numpad3", "3"],
["Numpad4", "4"],
["Numpad5", "5"],
["Numpad6", "6"],
["Numpad7", "7"],
["Numpad8", "8"],
["Numpad9", "9"],
["NumpadMultiply", "*"],
["NumpadAdd", "+"],
["NumpadSubtract", "-"],
["NumpadDecimal", "."],
["NumpadDivide", "/"],
["F1", "F1"],
["F2", "F2"],
["F3", "F3"],
["F4", "F4"],
["F5", "F5"],
["F6", "F6"],
["F7", "F7"],
["F8", "F8"],
["F9", "F9"],
["F10", "F10"],
["F11", "F11"],
["F12", "F12"],
["NumLock", "NumLock"],
["ScrollLock", "ScrollLock"],
["Semicolon", ";"],
["Equal", "="],
["Comma", ","],
["Minus", "-"],
["Period", "."],
["Slash", "/"],
["Backquote", "`"],
["BracketLeft", "["],
["Backslack", "\\"],
["BracketRight", "]"],
["Quote", "'"],
]);
export const US_INTERNATIONAL = createKeyboardLayout();
Loading