From 1c268a1e5d556f15473e661c5e6ad413da37ef4a Mon Sep 17 00:00:00 2001 From: Simon Babay Date: Mon, 1 Aug 2016 15:23:34 +0200 Subject: [PATCH 1/8] (select-one) search = false, focus and keyboard navigation --- assets/styles/css/choices.css | 2 ++ assets/styles/scss/choices.scss | 3 +++ 2 files changed, 5 insertions(+) diff --git a/assets/styles/css/choices.css b/assets/styles/css/choices.css index c5e1c4f..cc07b2b 100644 --- a/assets/styles/css/choices.css +++ b/assets/styles/css/choices.css @@ -15,6 +15,8 @@ user-select: none; } .choices.is-disabled .choices__item { cursor: not-allowed; } + .choices:focus { + outline: none; } .choices[data-type*="select-one"] .choices__inner { cursor: pointer; diff --git a/assets/styles/scss/choices.scss b/assets/styles/scss/choices.scss index 0b03251..648427e 100644 --- a/assets/styles/scss/choices.scss +++ b/assets/styles/scss/choices.scss @@ -24,6 +24,9 @@ $choices-button-icon-path: '../../icons/cross.svg'; } .choices__item { cursor: not-allowed; } } + &:focus{ + outline: none; + } } .choices[data-type*="select-one"] { From 994e20a4c3ef550cb41037a969fabc16c8e99982 Mon Sep 17 00:00:00 2001 From: Simon Babay Date: Mon, 1 Aug 2016 15:35:49 +0200 Subject: [PATCH 2/8] onKeyDown on containerOuter to enable keyboard navigation --- assets/scripts/src/choices.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/scripts/src/choices.js b/assets/scripts/src/choices.js index 73ca652..5a55d61 100644 --- a/assets/scripts/src/choices.js +++ b/assets/scripts/src/choices.js @@ -629,7 +629,7 @@ export class Choices { * @return */ _onKeyDown(e) { - if(e.target !== this.input) return; + if(e.target !== this.input && e.target !== this.containerOuter) return; const ctrlDownKey = e.ctrlKey || e.metaKey; const backKey = 46; From 611480ab3bc4962dfa5700f305ce79f37c972d2b Mon Sep 17 00:00:00 2001 From: Simon Babay Date: Mon, 1 Aug 2016 15:36:29 +0200 Subject: [PATCH 3/8] (select-one) search = false, on blur hide dropdown --- assets/scripts/src/choices.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/assets/scripts/src/choices.js b/assets/scripts/src/choices.js index 5a55d61..773ebf5 100644 --- a/assets/scripts/src/choices.js +++ b/assets/scripts/src/choices.js @@ -1600,6 +1600,10 @@ export class Choices { if(this.passedElement.type && this.passedElement.type === 'select-one') { this.containerOuter.addEventListener('focus', this._onFocus); + + if(!this.canSearch) { + this.containerOuter.addEventListener('blur', this._onBlur); + } } this.input.addEventListener('input', this._onInput); @@ -1621,6 +1625,10 @@ export class Choices { if(this.passedElement.type && this.passedElement.type === 'select-one') { this.containerOuter.removeEventListener('focus', this._onFocus); + + if(!this.canSearch) { + this.containerOuter.removeEventListener('blur', this._onBlur); + } } this.input.removeEventListener('input', this._onInput); From 8d3648e76383a846c1b481c958dfcc3179ab2311 Mon Sep 17 00:00:00 2001 From: Simon Babay Date: Mon, 1 Aug 2016 15:44:01 +0200 Subject: [PATCH 4/8] (select-one) search = false, click & show dropdown give focus --- assets/scripts/src/choices.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assets/scripts/src/choices.js b/assets/scripts/src/choices.js index 773ebf5..6a18a99 100644 --- a/assets/scripts/src/choices.js +++ b/assets/scripts/src/choices.js @@ -860,6 +860,10 @@ export class Choices { if(this.passedElement.type !== 'text' && !this.dropdown.classList.contains(this.config.classNames.activeState)) { // For select inputs we always want to show the dropdown if it isn't already showing this.showDropdown(); + + if(this.passedElement.type === 'select-one' && !this.canSearch){ + this.containerOuter.focus(); + } } // If input is not in focus, it ought to be From c18b9f81e4ba1d40d042448ac079b7f7fe0ac47c Mon Sep 17 00:00:00 2001 From: Simon Babay Date: Mon, 1 Aug 2016 16:48:40 +0200 Subject: [PATCH 5/8] (select-one) fix : keep focus after item is selected --- assets/scripts/src/choices.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/assets/scripts/src/choices.js b/assets/scripts/src/choices.js index e4b4f2b..bf5d460 100644 --- a/assets/scripts/src/choices.js +++ b/assets/scripts/src/choices.js @@ -554,6 +554,11 @@ export class Choices { console.error('callbackOnChange: Callback is not a function'); } } + + // Keep focus on select-one element + if(this.passedElement.type === 'select-one'){ + this.containerOuter.focus(); + } } /** From c6f9d9535937467e764c25d56c841610f78a37d7 Mon Sep 17 00:00:00 2001 From: Simon Babay Date: Mon, 1 Aug 2016 21:26:31 +0200 Subject: [PATCH 6/8] (select-one) Focus and hidden dropdown with keyboard navigation --- assets/scripts/src/choices.js | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/assets/scripts/src/choices.js b/assets/scripts/src/choices.js index bf5d460..20c2511 100644 --- a/assets/scripts/src/choices.js +++ b/assets/scripts/src/choices.js @@ -117,6 +117,9 @@ export class Choices { this._onMouseOver = this._onMouseOver.bind(this); this._onPaste = this._onPaste.bind(this); this._onInput = this._onInput.bind(this); + + // Focus containerOuter but not show dropdown if true + this._focusAndHideDropdown = false; // Cutting the mustard const cuttingTheMustard = 'querySelector' in document && 'addEventListener' in document && 'classList' in document.createElement("div"); @@ -557,6 +560,7 @@ export class Choices { // Keep focus on select-one element if(this.passedElement.type === 'select-one'){ + this._focusAndHideDropdown = true; this.containerOuter.focus(); } } @@ -707,7 +711,16 @@ export class Choices { case downKey: case upKey: // If up or down key is pressed, traverse through options - if(hasActiveDropdown) { + if(hasActiveDropdown || this.passedElement.type === 'select-one') { + + // Show dropdown if focus + if(!hasActiveDropdown){ + this.showDropdown(); + if(this.canSearch) { + this.input.focus(); + } + } + const currentEl = this.dropdown.querySelector(`.${this.config.classNames.highlightedState}`); const directionInt = e.keyCode === downKey ? 1 : -1; let nextEl; @@ -867,12 +880,16 @@ export class Choices { if(this.passedElement.type !== 'text') { // For select inputs we always want to show the dropdown if it isn't already showing this.showDropdown(); + if(this.canSearch){ + this.input.focus(); + } + }else{ + // If input is not in focus, it ought to be + if(this.input !== document.activeElement) { + this.input.focus(); + } } - - // If input is not in focus, it ought to be - if(this.input !== document.activeElement) { - this.input.focus(); - } + } else if(this.passedElement.type === 'select-one' && this.dropdown.classList.contains(this.config.classNames.activeState) && e.target === this.containerInner) { this.hideDropdown(); } @@ -986,9 +1003,11 @@ export class Choices { } else if(this.passedElement.type === 'select-one' && e.target === this.containerOuter && !hasActiveDropdown) { this.containerOuter.classList.add(this.config.classNames.focusState); this.showDropdown(); - if(this.canSearch) { + + if(!this._focusAndHideDropdown){ this.input.focus(); } + this._focusAndHideDropdown = false; } } From 1cb566a0f7694b99f42aae8817abf53a6e2b61af Mon Sep 17 00:00:00 2001 From: Simon Babay Date: Mon, 1 Aug 2016 21:30:31 +0200 Subject: [PATCH 7/8] Merge fix --- assets/scripts/src/choices.js | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/assets/scripts/src/choices.js b/assets/scripts/src/choices.js index c157296..b6d6f38 100644 --- a/assets/scripts/src/choices.js +++ b/assets/scripts/src/choices.js @@ -874,28 +874,20 @@ export class Choices { const hasShiftKey = e.shiftKey ? true : false; - if(!this.dropdown.classList.contains(this.config.classNames.activeState)) { - if(this.passedElement.type !== 'text') { - // For select inputs we always want to show the dropdown if it isn't already showing - this.showDropdown(); - if(this.canSearch){ - this.input.focus(); - } - }else{ - // If input is not in focus, it ought to be - if(this.input !== document.activeElement) { - this.input.focus(); - } + if(!this.dropdown.classList.contains(this.config.classNames.activeState)) { + if(this.passedElement.type !== 'text') { + // For select inputs we always want to show the dropdown if it isn't already showing + this.showDropdown(); + if(this.canSearch) { + this.input.focus(); } + }else{ + // If input is not in focus, it ought to be + if(this.input !== document.activeElement) { + this.input.focus(); + } + } - } else if(this.passedElement.type === 'select-one' && this.dropdown.classList.contains(this.config.classNames.activeState) && e.target === this.containerInner) { - this.hideDropdown(); - } - - // If input is not in focus, it ought to be - if(this.input !== document.activeElement) { - this.input.focus(); - } } else if(this.passedElement.type === 'select-one' && this.dropdown.classList.contains(this.config.classNames.activeState) && e.target === this.containerInner) { this.hideDropdown(); } From 6fadbde79e1a5e29a5e60eb420ed5af430fcb38b Mon Sep 17 00:00:00 2001 From: Josh Johnson Date: Tue, 2 Aug 2016 13:48:17 +0100 Subject: [PATCH 8/8] Ensure enter key can also open single select dropdown --- assets/scripts/src/choices.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/assets/scripts/src/choices.js b/assets/scripts/src/choices.js index 331089c..a470163 100644 --- a/assets/scripts/src/choices.js +++ b/assets/scripts/src/choices.js @@ -709,9 +709,18 @@ export class Choices { this._handleEnter(activeItems, value); } + // Show dropdown if focus + if(!hasActiveDropdown && this.passedElement.type === 'select-one'){ + e.preventDefault(); + this.showDropdown(); + if(this.canSearch) { + this.input.focus(); + } + } + if(hasActiveDropdown) { const highlighted = this.dropdown.querySelector(`.${this.config.classNames.highlightedState}`); - + if(highlighted) { const value = highlighted.getAttribute('data-value'); const label = highlighted.innerHTML; @@ -1030,6 +1039,7 @@ export class Choices { if(!this._focusAndHideDropdown){ this.input.focus(); } + this._focusAndHideDropdown = false; } } @@ -1064,7 +1074,8 @@ export class Choices { */ _regexFilter(value) { if(!value) return; - const expression = new RegExp(this.config.regexFilter, 'i'); + const regex = this.config.regexFilter; + const expression = new RegExp(regex.source, 'i'); return expression.test(value); }