This commit is contained in:
Kris Reeves 2023-02-06 23:51:02 +00:00 committed by GitHub
commit 9a37556d3f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 85 additions and 23 deletions

View file

@ -1,4 +1,4 @@
/*! choices.js v10.2.0 | © 2022 Josh Johnson | https://github.com/jshjohnson/Choices#readme */ /*! choices.js v10.2.0 | © 2023 Josh Johnson | https://github.com/jshjohnson/Choices#readme */
(function webpackUniversalModuleDefinition(root, factory) { (function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object') if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(); module.exports = factory();
@ -1263,9 +1263,30 @@ var Choices = /** @class */function () {
var hasFocusedInput = this.input.isFocussed; var hasFocusedInput = this.input.isFocussed;
var hasActiveDropdown = this.dropdown.isActive; var hasActiveDropdown = this.dropdown.isActive;
var hasItems = this.itemList.hasChildren(); var hasItems = this.itemList.hasChildren();
var keyString = String.fromCharCode(keyCode); /*
// eslint-disable-next-line no-control-regex See:
var wasPrintableChar = /[^\x00-\x1F]/.test(keyString); https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values
https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF - UTF-16 surrogate pairs
https://stackoverflow.com/a/70866532 - "Unidentified" for mobile
http://www.unicode.org/versions/Unicode5.2.0/ch16.pdf#G19635 - U+FFFF is reserved (Section 16.7)
Logic: when a key event is sent, `event.key` represents its printable value _or_ one
of a large list of special values indicating meta keys/functionality. In addition,
key events for compose functionality contain a value of `Dead` when mid-composition.
I can't quite verify it, but non-English IMEs may also be able to generate key codes
for code points in the surrogate-pair range, which could potentially be seen as having
key.length > 1. Since `Fn` is one of the special keys, we can't distinguish by that
alone.
Here, key.length === 1 means we know for sure the input was printable and not a special
`key` value. When the length is greater than 1, it could be either a printable surrogate
pair or a special `key` value. We can tell the difference by checking if the _character
code_ value (not code point!) is in the "surrogate pair" range or not.
We don't use .codePointAt because an invalid code point would return 65535, which wouldn't
pass the >= 0x10000 check we would otherwise use.
> ...The Unicode Standard sets aside 66 noncharacter code points. The last two code points
> of each plane are noncharacters: U+FFFE and U+FFFF on the BMP...
*/
var wasPrintableChar = event.key.length === 1 || event.key.length === 2 && event.key.charCodeAt(0) >= 0xD800 || event.key === 'Unidentified';
var BACK_KEY = constants_1.KEY_CODES.BACK_KEY, var BACK_KEY = constants_1.KEY_CODES.BACK_KEY,
DELETE_KEY = constants_1.KEY_CODES.DELETE_KEY, DELETE_KEY = constants_1.KEY_CODES.DELETE_KEY,
ENTER_KEY = constants_1.KEY_CODES.ENTER_KEY, ENTER_KEY = constants_1.KEY_CODES.ENTER_KEY,
@ -1275,15 +1296,15 @@ var Choices = /** @class */function () {
DOWN_KEY = constants_1.KEY_CODES.DOWN_KEY, DOWN_KEY = constants_1.KEY_CODES.DOWN_KEY,
PAGE_UP_KEY = constants_1.KEY_CODES.PAGE_UP_KEY, PAGE_UP_KEY = constants_1.KEY_CODES.PAGE_UP_KEY,
PAGE_DOWN_KEY = constants_1.KEY_CODES.PAGE_DOWN_KEY; PAGE_DOWN_KEY = constants_1.KEY_CODES.PAGE_DOWN_KEY;
if (!this._isTextElement && !hasActiveDropdown && wasPrintableChar) { if (!this._isTextElement && !hasActiveDropdown) {
this.showDropdown(); this.showDropdown();
if (!this.input.isFocussed) { if (!this.input.isFocussed && wasPrintableChar) {
/* /*
We update the input value with the pressed key as We update the input value with the pressed key as
the input was not focussed at the time of key press the input was not focussed at the time of key press
therefore does not have the value of the key. therefore does not have the value of the key.
*/ */
this.input.value += event.key.toLowerCase(); this.input.value += event.key;
} }
} }
switch (keyCode) { switch (keyCode) {

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
/*! choices.js v10.2.0 | © 2022 Josh Johnson | https://github.com/jshjohnson/Choices#readme */ /*! choices.js v10.2.0 | © 2023 Josh Johnson | https://github.com/jshjohnson/Choices#readme */

File diff suppressed because one or more lines are too long

View file

@ -2196,16 +2196,17 @@ describe('choices', () => {
describe('direction key', () => { describe('direction key', () => {
const keyCodes = [ const keyCodes = [
KEY_CODES.UP_KEY, [KEY_CODES.UP_KEY, 'ArrowUp'],
KEY_CODES.DOWN_KEY, [KEY_CODES.DOWN_KEY, 'ArrowDown'],
KEY_CODES.PAGE_UP_KEY, [KEY_CODES.PAGE_UP_KEY, 'PageUp'],
KEY_CODES.PAGE_DOWN_KEY, [KEY_CODES.PAGE_DOWN_KEY, 'PageDown'],
]; ];
keyCodes.forEach((keyCode) => { keyCodes.forEach(([keyCode, key]) => {
it(`calls _onDirectionKey with the expected arguments`, () => { it(`calls _onDirectionKey with the expected arguments`, () => {
const event = { const event = {
keyCode, keyCode,
key,
}; };
instance._onKeyDown(event); instance._onKeyDown(event);
@ -2222,6 +2223,7 @@ describe('choices', () => {
it(`calls _onSelectKey with the expected arguments`, () => { it(`calls _onSelectKey with the expected arguments`, () => {
const event = { const event = {
keyCode: KEY_CODES.A_KEY, keyCode: KEY_CODES.A_KEY,
key: 'A',
}; };
instance._onKeyDown(event); instance._onKeyDown(event);
@ -2237,6 +2239,7 @@ describe('choices', () => {
it(`calls _onEnterKey with the expected arguments`, () => { it(`calls _onEnterKey with the expected arguments`, () => {
const event = { const event = {
keyCode: KEY_CODES.ENTER_KEY, keyCode: KEY_CODES.ENTER_KEY,
key: 'Enter',
}; };
instance._onKeyDown(event); instance._onKeyDown(event);
@ -2250,12 +2253,20 @@ describe('choices', () => {
}); });
describe('delete key', () => { describe('delete key', () => {
const keyCodes = [KEY_CODES.DELETE_KEY, KEY_CODES.BACK_KEY]; // this is not an error; the constants are named the reverse of their assigned key names, according
// to their actual values, which appear to conform to the Windows VK mappings:
// 0x08 = 'Backspace', 0x2E = 'Delete'
// https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values#editing_keys
const keyCodes = [
[KEY_CODES.DELETE_KEY, 'Backspace'],
[KEY_CODES.BACK_KEY, 'Delete'],
];
keyCodes.forEach((keyCode) => { keyCodes.forEach(([keyCode, key]) => {
it(`calls _onDeleteKey with the expected arguments`, () => { it(`calls _onDeleteKey with the expected arguments`, () => {
const event = { const event = {
keyCode, keyCode,
key
}; };
instance._onKeyDown(event); instance._onKeyDown(event);

View file

@ -1440,9 +1440,39 @@ class Choices implements Choices {
const hasFocusedInput = this.input.isFocussed; const hasFocusedInput = this.input.isFocussed;
const hasActiveDropdown = this.dropdown.isActive; const hasActiveDropdown = this.dropdown.isActive;
const hasItems = this.itemList.hasChildren(); const hasItems = this.itemList.hasChildren();
const keyString = String.fromCharCode(keyCode); /*
// eslint-disable-next-line no-control-regex See:
const wasPrintableChar = /[^\x00-\x1F]/.test(keyString); https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values
https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF - UTF-16 surrogate pairs
https://stackoverflow.com/a/70866532 - "Unidentified" for mobile
http://www.unicode.org/versions/Unicode5.2.0/ch16.pdf#G19635 - U+FFFF is reserved (Section 16.7)
Logic: when a key event is sent, `event.key` represents its printable value _or_ one
of a large list of special values indicating meta keys/functionality. In addition,
key events for compose functionality contain a value of `Dead` when mid-composition.
I can't quite verify it, but non-English IMEs may also be able to generate key codes
for code points in the surrogate-pair range, which could potentially be seen as having
key.length > 1. Since `Fn` is one of the special keys, we can't distinguish by that
alone.
Here, key.length === 1 means we know for sure the input was printable and not a special
`key` value. When the length is greater than 1, it could be either a printable surrogate
pair or a special `key` value. We can tell the difference by checking if the _character
code_ value (not code point!) is in the "surrogate pair" range or not.
We don't use .codePointAt because an invalid code point would return 65535, which wouldn't
pass the >= 0x10000 check we would otherwise use.
> ...The Unicode Standard sets aside 66 noncharacter code points. The last two code points
> of each plane are noncharacters: U+FFFE and U+FFFF on the BMP...
*/
const wasPrintableChar = (
event.key.length === 1
|| (event.key.length === 2 && event.key.charCodeAt(0) >= 0xD800)
|| event.key === 'Unidentified'
);
const { const {
BACK_KEY, BACK_KEY,
@ -1456,16 +1486,16 @@ class Choices implements Choices {
PAGE_DOWN_KEY, PAGE_DOWN_KEY,
} = KEY_CODES; } = KEY_CODES;
if (!this._isTextElement && !hasActiveDropdown && wasPrintableChar) { if (!this._isTextElement && !hasActiveDropdown) {
this.showDropdown(); this.showDropdown();
if (!this.input.isFocussed) { if (!this.input.isFocussed && wasPrintableChar) {
/* /*
We update the input value with the pressed key as We update the input value with the pressed key as
the input was not focussed at the time of key press the input was not focussed at the time of key press
therefore does not have the value of the key. therefore does not have the value of the key.
*/ */
this.input.value += event.key.toLowerCase(); this.input.value += event.key;
} }
} }