commit
c7b05482f3
14 changed files with 306 additions and 75 deletions
@ -0,0 +1,86 @@ |
|||||||
|
const modifierdKeyName = (name) => { |
||||||
|
if (name.length === 1) { |
||||||
|
return name; |
||||||
|
} else if (name === 'Escape') { |
||||||
|
return 'Esc'; |
||||||
|
} |
||||||
|
return name; |
||||||
|
}; |
||||||
|
|
||||||
|
const fromKeyboardEvent = (e) => { |
||||||
|
let key = modifierdKeyName(e.key); |
||||||
|
let shift = e.shiftKey; |
||||||
|
if (key.length === 1 && key.toUpperCase() === key.toLowerCase()) { |
||||||
|
// make shift false for symbols to enable key bindings by symbold keys.
|
||||||
|
// But this limits key bindings by symbol keys with Shift (such as Shift+$>.
|
||||||
|
shift = false; |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
key: modifierdKeyName(e.key), |
||||||
|
shiftKey: shift, |
||||||
|
ctrlKey: e.ctrlKey, |
||||||
|
altKey: e.altKey, |
||||||
|
metaKey: e.metaKey, |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
const fromMapKey = (key) => { |
||||||
|
if (key.startsWith('<') && key.endsWith('>')) { |
||||||
|
let inner = key.slice(1, -1); |
||||||
|
let shift = inner.includes('S-'); |
||||||
|
let base = inner.slice(inner.lastIndexOf('-') + 1); |
||||||
|
if (shift && base.length === 1) { |
||||||
|
base = base.toUpperCase(); |
||||||
|
} else if (!shift && base.length === 1) { |
||||||
|
base = base.toLowerCase(); |
||||||
|
} |
||||||
|
return { |
||||||
|
key: base, |
||||||
|
shiftKey: inner.includes('S-'), |
||||||
|
ctrlKey: inner.includes('C-'), |
||||||
|
altKey: inner.includes('A-'), |
||||||
|
metaKey: inner.includes('M-'), |
||||||
|
}; |
||||||
|
} |
||||||
|
return { |
||||||
|
key: key, |
||||||
|
shiftKey: key.toLowerCase() !== key, |
||||||
|
ctrlKey: false, |
||||||
|
altKey: false, |
||||||
|
metaKey: false, |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
const fromMapKeys = (keys) => { |
||||||
|
const fromMapKeysRecursive = (remainings, mappedKeys) => { |
||||||
|
if (remainings.length === 0) { |
||||||
|
return mappedKeys; |
||||||
|
} |
||||||
|
|
||||||
|
let nextPos = 1; |
||||||
|
if (remainings.startsWith('<')) { |
||||||
|
let ltPos = remainings.indexOf('>'); |
||||||
|
if (ltPos > 0) { |
||||||
|
nextPos = ltPos + 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return fromMapKeysRecursive( |
||||||
|
remainings.slice(nextPos), |
||||||
|
mappedKeys.concat([fromMapKey(remainings.slice(0, nextPos))]) |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
return fromMapKeysRecursive(keys, []); |
||||||
|
}; |
||||||
|
|
||||||
|
const equals = (e1, e2) => { |
||||||
|
return e1.key === e2.key && |
||||||
|
e1.ctrlKey === e2.ctrlKey && |
||||||
|
e1.metaKey === e2.metaKey && |
||||||
|
e1.altKey === e2.altKey && |
||||||
|
e1.shiftKey === e2.shiftKey; |
||||||
|
}; |
||||||
|
|
||||||
|
export { fromKeyboardEvent, fromMapKey, fromMapKeys, equals }; |
@ -0,0 +1,145 @@ |
|||||||
|
import { expect } from 'chai'; |
||||||
|
import * as keys from 'shared/utils/keys'; |
||||||
|
|
||||||
|
describe("keys util", () => { |
||||||
|
describe('fromKeyboardEvent', () => { |
||||||
|
it('returns from keyboard input Ctrl+X', () => { |
||||||
|
let k = keys.fromKeyboardEvent({ |
||||||
|
key: 'x', shiftKey: false, ctrlKey: true, altKey: false, metaKey: true |
||||||
|
}); |
||||||
|
expect(k.key).to.equal('x'); |
||||||
|
expect(k.shiftKey).to.be.false; |
||||||
|
expect(k.ctrlKey).to.be.true; |
||||||
|
expect(k.altKey).to.be.false; |
||||||
|
expect(k.metaKey).to.be.true; |
||||||
|
}); |
||||||
|
|
||||||
|
it('returns from keyboard input Shift+Esc', () => { |
||||||
|
let k = keys.fromKeyboardEvent({ |
||||||
|
key: 'Escape', shiftKey: true, ctrlKey: false, altKey: false, metaKey: true |
||||||
|
}); |
||||||
|
expect(k.key).to.equal('Esc'); |
||||||
|
expect(k.shiftKey).to.be.true; |
||||||
|
expect(k.ctrlKey).to.be.false; |
||||||
|
expect(k.altKey).to.be.false; |
||||||
|
expect(k.metaKey).to.be.true; |
||||||
|
}); |
||||||
|
|
||||||
|
it('returns from keyboard input Ctrl+$', () => { |
||||||
|
// $ required shift pressing on most keyboards
|
||||||
|
let k = keys.fromKeyboardEvent({ |
||||||
|
key: '$', shiftKey: true, ctrlKey: true, altKey: false, metaKey: false |
||||||
|
}); |
||||||
|
expect(k.key).to.equal('$'); |
||||||
|
expect(k.shiftKey).to.be.false; |
||||||
|
expect(k.ctrlKey).to.be.true; |
||||||
|
expect(k.altKey).to.be.false; |
||||||
|
expect(k.metaKey).to.be.false; |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('fromMapKey', () => { |
||||||
|
it('return for X', () => { |
||||||
|
let key = keys.fromMapKey('x'); |
||||||
|
expect(key.key).to.equal('x'); |
||||||
|
expect(key.shiftKey).to.be.false; |
||||||
|
expect(key.ctrlKey).to.be.false; |
||||||
|
expect(key.altKey).to.be.false; |
||||||
|
expect(key.metaKey).to.be.false; |
||||||
|
}); |
||||||
|
|
||||||
|
it('return for Shift+X', () => { |
||||||
|
let key = keys.fromMapKey('X'); |
||||||
|
expect(key.key).to.equal('X'); |
||||||
|
expect(key.shiftKey).to.be.true; |
||||||
|
expect(key.ctrlKey).to.be.false; |
||||||
|
expect(key.altKey).to.be.false; |
||||||
|
expect(key.metaKey).to.be.false; |
||||||
|
}); |
||||||
|
|
||||||
|
it('return for Ctrl+X', () => { |
||||||
|
let key = keys.fromMapKey('<C-X>'); |
||||||
|
expect(key.key).to.equal('x'); |
||||||
|
expect(key.shiftKey).to.be.false; |
||||||
|
expect(key.ctrlKey).to.be.true; |
||||||
|
expect(key.altKey).to.be.false; |
||||||
|
expect(key.metaKey).to.be.false; |
||||||
|
}); |
||||||
|
|
||||||
|
it('returns for Ctrl+Meta+X', () => { |
||||||
|
let key = keys.fromMapKey('<C-M-X>'); |
||||||
|
expect(key.key).to.equal('x'); |
||||||
|
expect(key.shiftKey).to.be.false; |
||||||
|
expect(key.ctrlKey).to.be.true; |
||||||
|
expect(key.altKey).to.be.false; |
||||||
|
expect(key.metaKey).to.be.true; |
||||||
|
}); |
||||||
|
|
||||||
|
it('returns for Ctrl+Shift+x', () => { |
||||||
|
let key = keys.fromMapKey('<C-S-x>'); |
||||||
|
expect(key.key).to.equal('X'); |
||||||
|
expect(key.shiftKey).to.be.true; |
||||||
|
expect(key.ctrlKey).to.be.true; |
||||||
|
expect(key.altKey).to.be.false; |
||||||
|
expect(key.metaKey).to.be.false; |
||||||
|
}); |
||||||
|
|
||||||
|
it('returns for Shift+Esc', () => { |
||||||
|
let key = keys.fromMapKey('<S-Esc>'); |
||||||
|
expect(key.key).to.equal('Esc'); |
||||||
|
expect(key.shiftKey).to.be.true; |
||||||
|
expect(key.ctrlKey).to.be.false; |
||||||
|
expect(key.altKey).to.be.false; |
||||||
|
expect(key.metaKey).to.be.false; |
||||||
|
}); |
||||||
|
|
||||||
|
it('returns for Ctrl+Esc', () => { |
||||||
|
let key = keys.fromMapKey('<C-Esc>'); |
||||||
|
expect(key.key).to.equal('Esc'); |
||||||
|
expect(key.shiftKey).to.be.false; |
||||||
|
expect(key.ctrlKey).to.be.true; |
||||||
|
expect(key.altKey).to.be.false; |
||||||
|
expect(key.metaKey).to.be.false; |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('fromMapKeys', () => { |
||||||
|
it('returns mapped keys for Shift+Esc', () => { |
||||||
|
let keyArray = keys.fromMapKeys('<S-Esc>'); |
||||||
|
expect(keyArray).to.have.lengthOf(1); |
||||||
|
expect(keyArray[0].key).to.equal('Esc'); |
||||||
|
expect(keyArray[0].shiftKey).to.be.true; |
||||||
|
}); |
||||||
|
|
||||||
|
it('returns mapped keys for a<C-B><A-C>d<M-e>', () => { |
||||||
|
let keyArray = keys.fromMapKeys('a<C-B><A-C>d<M-e>'); |
||||||
|
expect(keyArray).to.have.lengthOf(5); |
||||||
|
expect(keyArray[0].key).to.equal('a'); |
||||||
|
expect(keyArray[1].ctrlKey).to.be.true; |
||||||
|
expect(keyArray[1].key).to.equal('b'); |
||||||
|
expect(keyArray[2].altKey).to.be.true; |
||||||
|
expect(keyArray[2].key).to.equal('c'); |
||||||
|
expect(keyArray[3].key).to.equal('d'); |
||||||
|
expect(keyArray[4].metaKey).to.be.true; |
||||||
|
expect(keyArray[4].key).to.equal('e'); |
||||||
|
}); |
||||||
|
}) |
||||||
|
|
||||||
|
describe('equals', () => { |
||||||
|
expect(keys.equals({ |
||||||
|
key: 'x', |
||||||
|
ctrlKey: true, |
||||||
|
}, { |
||||||
|
key: 'x', |
||||||
|
ctrlKey: true, |
||||||
|
})).to.be.true; |
||||||
|
|
||||||
|
expect(keys.equals({ |
||||||
|
key: 'X', |
||||||
|
shiftKey: true, |
||||||
|
}, { |
||||||
|
key: 'x', |
||||||
|
ctrlKey: true, |
||||||
|
})).to.be.false; |
||||||
|
}); |
||||||
|
}); |
Reference in new issue