Skip to content

Commit

Permalink
switch api to transformers
Browse files Browse the repository at this point in the history
  • Loading branch information
myandrienko committed Mar 12, 2024
1 parent 5167bb7 commit 3851f96
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 75 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,12 +311,18 @@ Window and Linux usually favor the Ctrl key. To provide familiar experience on
all platforms, enable the Mac compatibility mode:

```js
import { hotkeyKeyUX, startKeyUX, MAC_COMPAT } from 'keyux'
import { macCompat } from 'keyux'

startKeyUX(window, [
hotkeyKeyUX({ ...MAC_COMPAT })
])
startKeyUX(window, [hotkeyKeyUX(overrides, [macCompat()])])
```

Hotkeys pressed with the Meta modifier will work as if the Ctrl modifier was
pressed.

This also works for getting hotkey hints:

```js
import { macCompat } from 'keyux'

getHotKeyHint(window, 'ctrl+b', overrides, [macCompat()])
```
22 changes: 9 additions & 13 deletions compat.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
const MAC_COMPAT_KEY = Symbol('macCompat')
export const MAC_COMPAT = { [MAC_COMPAT_KEY]: true }

export function applyCompat(code, window, overrides, dir = 'forward') {
if (
overrides[MAC_COMPAT_KEY] &&
window.navigator.platform.indexOf('Mac') === 0 &&
code.indexOf('meta+ctrl') === -1
) {
return code.replace(
...(dir === 'reverse' ? ['ctrl', 'meta'] : ['meta', 'ctrl'])
)
export function macCompat() {
return (code, window, dir) => {
if (
window.navigator.platform.indexOf('Mac') === 0 &&
code.indexOf('meta+ctrl') === -1
) {
return code.replace(...(dir ? ['ctrl', 'meta'] : ['meta', 'ctrl']))
}
return code
}
return code
}
15 changes: 7 additions & 8 deletions hotkey.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { applyCompat } from './compat.js'

const NON_ENGLISH_LAYOUT = /^[^\x00-\x7F]$/

const IGNORE_INPUTS = {
Expand Down Expand Up @@ -61,38 +59,39 @@ function checkHotkey(where, code, overrides) {
)
}

function findHotKey(event, window, where, overrides) {
function findHotKey(event, window, overrides, transformers) {
let prefix = ''
if (event.metaKey) prefix += 'meta+'
if (event.ctrlKey) prefix += 'ctrl+'
if (event.altKey) prefix += 'alt+'
if (event.shiftKey) prefix += 'shift+'

let code = applyCompat(prefix, window, overrides)
let code = prefix
if (event.key === '+') {
code += 'plus'
} else {
code += event.key.toLowerCase()
}

let hotkey = checkHotkey(where, code, overrides)
transformers.forEach(transform => (code = transform(code, window)))
let hotkey = checkHotkey(window.document, code, overrides)
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.document, prefix + enKey, overrides)
}

return hotkey
}

export function hotkeyKeyUX(overrides = {}) {
export function hotkeyKeyUX(overrides = {}, transformers = []) {
return window => {
function keyDown(event) {
if (ignoreHotkeysIn(event.target)) return
let press = findHotKey(event, window, window.document, overrides)
let press = findHotKey(event, window, overrides, transformers)
if (press) press.click()
}

Expand Down
21 changes: 15 additions & 6 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ export interface MenuKeyUXOptions {

export type HotkeyOverride = Record<string, string>

export type Tranformer = (
code: string,
window: MinimalWindow,
dir: 'r' | undefined
) => string

/**
* Press button/a according to `aria-keyshortcuts`.
*
Expand All @@ -49,7 +55,10 @@ export type HotkeyOverride = Record<string, string>
* ])
* ```
*/
export function hotkeyKeyUX(overrides?: HotkeyOverride): KeyUXModule
export function hotkeyKeyUX(
overrides?: HotkeyOverride,
transformers?: Tranformer[]
): KeyUXModule

/**
* Add arrow-navigation on `role="menu"`.
Expand Down Expand Up @@ -158,20 +167,20 @@ export function likelyWithKeyboard(window: MinimalWindow): boolean
export function getHotKeyHint(
window: MinimalWindow,
code: string,
overrides?: HotkeyOverride
overrides?: HotkeyOverride,
transformers?: Tranformer[]
): string

/**
* Enables Mac compatibility mode, when hot keys with Meta modifier are treated
* as if Ctrl modifier was used.
*
* ```js
* import { startKeyUX, hotkeyKeyUX, MAC_COMPAT } from 'keyux'
* import { macCompat } from 'keyux'
*
* startKeyUX(window, [
* hotkeyKeyUX({ ...MAC_COMPAT })
* hotkeyKeyUX({}, [macCompat()])
* ])
* ```
*/
declare const MAC_COMPAT_KEY: unique symbol
export const MAC_COMPAT: { [MAC_COMPAT_KEY]: true }
export const macCompat: Tranformer
11 changes: 6 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { applyCompat, MAC_COMPAT } from './compat.js'

export * from './hotkey.js'
export * from './hidden.js'
export * from './press.js'
export * from './menu.js'
export * from './jump.js'
export { MAC_COMPAT }
export * from './compat.js'

export function startKeyUX(window, plugins) {
let unbinds = plugins.map(plugin => plugin(window))
Expand All @@ -19,15 +17,18 @@ 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, overrides = {}, transformers = []) {
let realCode = code
for (let i in overrides) {
if (overrides[i] === code) {
realCode = i
break
}
}
let prettyParts = applyCompat(realCode, window, overrides, 'reverse')
transformers.forEach(
transform => (realCode = transform(realCode, window, 'r'))
)
let prettyParts = realCode
.split('+')
.map(part => part[0].toUpperCase() + part.slice(1))
if (window.navigator.platform.indexOf('Mac') === 0) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
"import": {
"./index.js": "{ startKeyUX, hotkeyKeyUX, pressKeyUX, menuKeyUX, jumpKeyUX, hiddenKeyUX, likelyWithKeyboard, getHotKeyHint }"
},
"limit": "1737 B"
"limit": "1768 B"
}
],
"clean-publish": {
Expand Down
43 changes: 9 additions & 34 deletions test/compat.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { JSDOM } from 'jsdom'
import { equal } from 'node:assert'
import { test } from 'node:test'

import { applyCompat, MAC_COMPAT } from '../compat.js'
import { macCompat } from '../compat.js'
import type { MinimalWindow } from '../index.js'

const MAC_WINDOW = {
Expand All @@ -12,42 +12,17 @@ const MAC_WINDOW = {
}
} as MinimalWindow

const COMPAT_OVERRIDES = { ...MAC_COMPAT }

test('applies compatibility for Mac platform', () => {
equal(
applyCompat('meta+shift+b', MAC_WINDOW, COMPAT_OVERRIDES),
'ctrl+shift+b'
)
equal(
applyCompat('meta+ctrl+shift+b', MAC_WINDOW, COMPAT_OVERRIDES),
'meta+ctrl+shift+b'
)
test('applies hotkey compatibility for Mac platform', () => {
equal(macCompat()('meta+shift+b', MAC_WINDOW), 'ctrl+shift+b')
equal(macCompat()('meta+ctrl+shift+b', MAC_WINDOW), 'meta+ctrl+shift+b')
})

test('applies reverse compatibility for Mac platform', () => {
equal(
applyCompat('ctrl+shift+b', MAC_WINDOW, COMPAT_OVERRIDES, 'reverse'),
'meta+shift+b'
)
equal(
applyCompat('meta+ctrl+shift+b', MAC_WINDOW, COMPAT_OVERRIDES, 'reverse'),
'meta+ctrl+shift+b'
)
test('applies hint compatibility for Mac platform', () => {
equal(macCompat()('ctrl+shift+b', MAC_WINDOW, 'r'), 'meta+shift+b')
equal(macCompat()('meta+ctrl+shift+b', MAC_WINDOW, 'r'), 'meta+ctrl+shift+b')
})

test('does nothing for non-Mac platform', () => {
equal(
applyCompat('meta+shift+b', new JSDOM().window, COMPAT_OVERRIDES),
'meta+shift+b'
)
equal(
applyCompat(
'ctrl+shift+b',
new JSDOM().window,
COMPAT_OVERRIDES,
'reverse'
),
'ctrl+shift+b'
)
equal(macCompat()('meta+shift+b', new JSDOM().window), 'meta+shift+b')
equal(macCompat()('ctrl+shift+b', new JSDOM().window, 'r'), 'ctrl+shift+b')
})
9 changes: 5 additions & 4 deletions test/demo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ import {
pressKeyUX,
startKeyUX
} from '../../index.js'
import { macCompat } from '../../compat.js'

let overrides: HotkeyOverride = {}

startKeyUX(window, [
hotkeyKeyUX(overrides),
hotkeyKeyUX(overrides, [macCompat()]),
menuKeyUX(),
pressKeyUX('is-pressed'),
jumpKeyUX(),
Expand All @@ -25,7 +26,7 @@ startKeyUX(window, [

const HotKeyHint: FC<{ hotkey: string }> = ({ hotkey }) => {
return likelyWithKeyboard(window) ? (
<kbd>{getHotKeyHint(window, hotkey, overrides)}</kbd>
<kbd>{getHotKeyHint(window, hotkey, overrides, [macCompat()])}</kbd>
) : null
}

Expand Down Expand Up @@ -63,13 +64,13 @@ const Counter: FC = () => {
let [clicked, setClicked] = useState(0)
return (
<button
aria-keyshortcuts="alt+b"
aria-keyshortcuts="ctrl+b"
onClick={() => {
setClicked(clicked + 1)
}}
>
Clicked <strong>{clicked}</strong>
<HotKeyHint hotkey="alt+b" />
<HotKeyHint hotkey="ctrl+b" />
</button>
)
}
Expand Down

0 comments on commit 3851f96

Please sign in to comment.