From c61c9536739067ac3b73263ca7964008ffd2c552 Mon Sep 17 00:00:00 2001 From: Matvei Andrienko Date: Thu, 4 Apr 2024 15:35:00 +0200 Subject: [PATCH 01/11] add transformers api --- README.md | 37 ++++++++++++++++++++++++++++--------- hotkey.js | 21 ++++++++++++--------- index.d.ts | 41 ++++++++++++++++++++++++++++++++++++++--- index.js | 10 ++++------ overrides.js | 18 ++++++++++++++++++ package.json | 2 +- test/hotkey.test.ts | 10 +++++----- test/index.test.ts | 4 +++- 8 files changed, 109 insertions(+), 34 deletions(-) create mode 100644 overrides.js diff --git a/README.md b/README.md index 5549d1b..a653cf1 100644 --- a/README.md +++ b/README.md @@ -61,14 +61,15 @@ import { hotkeyKeyUX, jumpKeyUX, focusGroupKeyUX, + overrides, pressKeyUX, startKeyUX } from 'keyux' -const overrides = {} +const overridesConfig = {} startKeyUX(window, [ - hotkeyKeyUX(overrides), + hotkeyKeyUX([overrides(overridesConfig)]), focusGroupKeyUX(), pressKeyUX('is-pressed'), jumpKeyUX(), @@ -127,6 +128,25 @@ external keyboard). For instance, for `alt+b` it will return `Alt + B` on Windows/Linux or `⌥ B` on Mac. +If you're using overrides, pass the same override config both to `hotkeyKeyUX` +and `getHotKeyHint` for accurate hints: + +```js +import { + getHotKeyHint, + hintOverrides, + hotkeyKeyUX, + overrides, + startKeyUX +} from 'keyux' + +let config = { 'alt+b': 'b' } + +startKeyUX(window, [ + hotkeyKeyUX([overrides(config)]) // Override B to Alt + B +]) +getHotKeyHint(window, 'b', [hintOverrides(config)]) // Alt + B +``` ### Pressed State @@ -161,21 +181,20 @@ to automatically add class for every `:active` state in your CSS. Many users want to override hotkeys because your hotkeys can conflict with their browser’s extensions, system, or screen reader. -KeyUX allows overriding hotkeys using the `overrides` object. -Both `hotkeyKeyUX()` and `getHotKeyHint()` accept it as an argument. +KeyUX allows to override hotkeys using tranforms. Use the `overrides` tranformer +with `hotkeyKeyUX()`, and `hintOverrides` with `getHotKeyHint()`. You will need to create some UI for users to fill this object like: ```js -const overrides = { +const overridesConfig = { 'alt+b': 'b' // Override B to Alt + B } ``` -Then KeyUX will click on `aria-keyshortcuts="b"` when -Alt+B is pressed, and -`getHotKeyHint(window, 'b', overrides)` will return `Alt + B`/`⌥ B`. - +Then KeyUX will click on `aria-keyshortcuts="b"` when Alt+B +is pressed, and `getHotKeyHint(window, 'b', [hintOverrides(overrideConfig)])` +will return `Alt + B`/`⌥ B`. ### Hotkeys for List diff --git a/hotkey.js b/hotkey.js index ff3b3f2..3709216 100644 --- a/hotkey.js +++ b/hotkey.js @@ -31,12 +31,15 @@ function findNonIgnored(activeElement, elements) { } } -function checkHotkey(where, code, overrides) { - let codeOverride = overrides[code] - if (Object.values(overrides).includes(code) && !codeOverride) return false +function checkHotkey(window, code, transformers) { + let actualCode = code + for (let transform of transformers) { + actualCode = transform(code, window) + if (!actualCode) return false + } + let where = window.document let activeElement = where.activeElement - let actualCode = codeOverride || code let areaId = activeElement.getAttribute('data-keyux-hotkeys') if (areaId) { @@ -59,7 +62,7 @@ function checkHotkey(where, code, overrides) { ) } -function findHotKey(event, where, overrides) { +function findHotKey(event, window, transformers) { let prefix = '' if (event.metaKey) prefix += 'meta+' if (event.ctrlKey) prefix += 'ctrl+' @@ -73,24 +76,24 @@ function findHotKey(event, where, overrides) { code += event.key.toLowerCase() } - let hotkey = checkHotkey(where, code, overrides) + let hotkey = checkHotkey(window, code, transformers) if ( !hotkey && NON_ENGLISH_LAYOUT.test(event.key) && /^Key.$/.test(event.code) ) { let enKey = event.code.replace(/^Key/, '').toLowerCase() - hotkey = checkHotkey(where, prefix + enKey, overrides) + hotkey = checkHotkey(window, prefix + enKey, transformers) } return hotkey } -export function hotkeyKeyUX(overrides = {}) { +export function hotkeyKeyUX(transformers = []) { return window => { function keyDown(event) { if (ignoreHotkeysIn(event.target)) return - let press = findHotKey(event, window.document, overrides) + let press = findHotKey(event, window, transformers) if (press) press.click() } diff --git a/index.d.ts b/index.d.ts index 049c131..232ec9a 100644 --- a/index.d.ts +++ b/index.d.ts @@ -38,6 +38,10 @@ export interface FocusGroupKeyUXOptions { export type HotkeyOverride = Record +export interface Transformer { + (code: string, window: MinimalWindow): false | string +} + /** * Press button/a according to `aria-keyshortcuts`. * @@ -49,7 +53,7 @@ export type HotkeyOverride = Record * ]) * ``` */ -export function hotkeyKeyUX(overrides?: HotkeyOverride): KeyUXModule +export function hotkeyKeyUX(transformers?: Transformer[]): KeyUXModule /** * Add arrow-navigation on `role="menu"`. @@ -142,7 +146,7 @@ export function likelyWithKeyboard(window: MinimalWindow): boolean /** * Return text for `` element with hint for hot key. * - * It replaced `Ctrl` with `⌘` on Mac and respects overrides. + * It replaces `Cmd` with `⌘` etc on Mac. * * ```js * import { getHotKeyHint, likelyWithKeyboard } from 'keyux' @@ -158,5 +162,36 @@ export function likelyWithKeyboard(window: MinimalWindow): boolean export function getHotKeyHint( window: MinimalWindow, code: string, - overrides?: HotkeyOverride + transformers?: Transformer[] ): string + +/** + * Provides a transformer for hotkey overrides that can be used + * with `hotkeyKeyUX`. + * + * ```js + * import { startKeyUX, hotkeyKeyUX, overrides } from 'keyux' + * + * startKeyUX(window, [ + * hotkeyKeyUX([overrides({ + * 'alt+b': 'b' // Override B to Alt + B + * })]) + * ]) + * ``` + */ +export function overrides(config: HotkeyOverride): Transformer + +/** + * Provides a transformer for hotkey overrides that can be used + * with `getHotKeyHint`. Use the same override config in `hotkeyKeyUX` + * and `getHotKeyHint` to get accurate hints. + * + * ```js + * import { getHotKeyHint, hintOverrides, hotkeyKeyUX, overrides } from "keyux" + * + * let config = { 'alt+b': 'b' } + * hotkeyKeyUX([overrides(config)]) // Override B to Alt + B + * getHotKeyHint(window, 'b', [hintOverrides(config)]) // Alt + B + * ``` + */ +export function hintOverrides(config: HotkeyOverride): Transformer diff --git a/index.js b/index.js index f97b57f..7056390 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,7 @@ export * from './hotkey.js' export * from './hidden.js' export * from './press.js' export * from './jump.js' +export * from './overrides.js' export function startKeyUX(window, plugins) { let unbinds = plugins.map(plugin => plugin(window)) @@ -16,13 +17,10 @@ export function likelyWithKeyboard(window = globalThis) { return !['iphone', 'ipad', 'android'].some(device => agent.includes(device)) } -export function getHotKeyHint(window, code, overrides = {}) { +export function getHotKeyHint(window, code, transformers = []) { let realCode = code - for (let i in overrides) { - if (overrides[i] === code) { - realCode = i - break - } + for (let transform of transformers) { + realCode = transform(realCode, window) } let prettyParts = realCode .split('+') diff --git a/overrides.js b/overrides.js new file mode 100644 index 0000000..7d98b05 --- /dev/null +++ b/overrides.js @@ -0,0 +1,18 @@ +export function overrides(config) { + return code => { + let codeOverride = config[code] + if (Object.values(config).includes(code) && !codeOverride) return false + return codeOverride || code + } +} + +export function hintOverrides(config) { + return code => { + for (let i in config) { + if (config[i] === code) { + return i + } + } + return code + } +} diff --git a/package.json b/package.json index 565ce9d..2d5d1cb 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "import": { "./index.js": "{ startKeyUX, hotkeyKeyUX, pressKeyUX, focusGroupKeyUX, jumpKeyUX, hiddenKeyUX, likelyWithKeyboard, getHotKeyHint }" }, - "limit": "1967 B" + "limit": "1943 B" } ], "clean-publish": { diff --git a/test/hotkey.test.ts b/test/hotkey.test.ts index e6dbf10..b9a76aa 100644 --- a/test/hotkey.test.ts +++ b/test/hotkey.test.ts @@ -3,7 +3,7 @@ import { equal } from 'node:assert' import { test } from 'node:test' import type { HotkeyOverride } from '../index.js' -import { hotkeyKeyUX, startKeyUX } from '../index.js' +import { hotkeyKeyUX, overrides, startKeyUX } from '../index.js' function press( window: DOMWindow, @@ -124,8 +124,8 @@ test('supports non-English keyboard layouts', () => { test('allows to override hotkeys', () => { let window = new JSDOM().window - let overrides: HotkeyOverride = {} - startKeyUX(window, [hotkeyKeyUX(overrides)]) + let overridesConfig: HotkeyOverride = {} + startKeyUX(window, [hotkeyKeyUX([overrides(overridesConfig)])]) window.document.body.innerHTML = '' + '' @@ -137,8 +137,8 @@ test('allows to override hotkeys', () => { }) } - overrides.q = 'b' - overrides.a = 'q' + overridesConfig.q = 'b' + overridesConfig.a = 'q' press(window, { key: 'b' }) equal(clicked, '') diff --git a/test/index.test.ts b/test/index.test.ts index 53c6c5a..4d83c30 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -4,6 +4,7 @@ import { test } from 'node:test' import { getHotKeyHint, + hintOverrides, likelyWithKeyboard, type MinimalWindow } from '../index.js' @@ -34,7 +35,8 @@ test('makes hotkey hint prettier', () => { getHotKeyHint(window, 'meta+ctrl+shift+alt+b'), 'Meta + Ctrl + Shift + Alt + B' ) - equal(getHotKeyHint(window, 'alt+b', { b: 'alt+b' }), 'B') + equal(getHotKeyHint(window, 'alt+b', [hintOverrides({ b: 'alt+b' })]), 'B') + equal(getHotKeyHint(window, 'q', [hintOverrides({ b: 'alt+b' })]), 'Q') }) test('makes mac hotkey hint prettier', () => { From 8b4338c7ced9aa2bc9bd747555e89476249a2aa4 Mon Sep 17 00:00:00 2001 From: Matvei Andrienko Date: Thu, 4 Apr 2024 15:55:21 +0200 Subject: [PATCH 02/11] update demo --- test/demo/index.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/demo/index.tsx b/test/demo/index.tsx index 8846a17..1c10ce8 100644 --- a/test/demo/index.tsx +++ b/test/demo/index.tsx @@ -6,17 +6,19 @@ import { focusGroupKeyUX, getHotKeyHint, hiddenKeyUX, + hintOverrides, hotkeyKeyUX, jumpKeyUX, likelyWithKeyboard, + overrides, pressKeyUX, startKeyUX } from '../../index.js' -let overrides: HotkeyOverride = {} +let overridesConfig: HotkeyOverride = {} startKeyUX(window, [ - hotkeyKeyUX(overrides), + hotkeyKeyUX([overrides(overridesConfig)]), focusGroupKeyUX(), pressKeyUX('is-pressed'), jumpKeyUX(), @@ -25,7 +27,7 @@ startKeyUX(window, [ const HotKeyHint: FC<{ hotkey: string }> = ({ hotkey }) => { return likelyWithKeyboard(window) ? ( - {getHotKeyHint(window, hotkey, overrides)} + {getHotKeyHint(window, hotkey, [hintOverrides(overridesConfig)])} ) : null } @@ -246,15 +248,15 @@ const Page: FC<{ content = (