mirror of
https://github.com/Choices-js/Choices.git
synced 2024-06-01 05:22:21 +02:00
Open dropdown if typing
This commit is contained in:
parent
17e00f10fb
commit
2ccac3083d
4
assets/scripts/dist/bundle.js
vendored
4
assets/scripts/dist/bundle.js
vendored
File diff suppressed because one or more lines are too long
|
@ -10,7 +10,6 @@ import Sifter from 'sifter';
|
||||||
* Choices
|
* Choices
|
||||||
*
|
*
|
||||||
* To do:
|
* To do:
|
||||||
* - Dispatch events
|
|
||||||
* - Remove item by clicking a target
|
* - Remove item by clicking a target
|
||||||
* - Set input width based on the size of the contents
|
* - Set input width based on the size of the contents
|
||||||
* - Single select input support
|
* - Single select input support
|
||||||
|
@ -20,9 +19,8 @@ export class Choices {
|
||||||
constructor(element = '[data-choice]', userOptions = {}) {
|
constructor(element = '[data-choice]', userOptions = {}) {
|
||||||
|
|
||||||
// Cutting the mustard
|
// Cutting the mustard
|
||||||
const fakeEl = document.createElement("fakeel");
|
const cuttingTheMustard = 'querySelector' in document && 'addEventListener' in document && 'classList' in document.createElement("div");
|
||||||
const cuttingTheMustard = 'querySelector' in document && 'addEventListener' in document && 'classList' in fakeEl;
|
if (!cuttingTheMustard) console.error('init: Your browser doesn\'t support Choices');
|
||||||
if (!cuttingTheMustard) console.error('init: Your browser doesn\'nt support Choices');
|
|
||||||
|
|
||||||
// If there are multiple elements, create a new instance
|
// If there are multiple elements, create a new instance
|
||||||
// for each element besides the first one (as that already has an instance)
|
// for each element besides the first one (as that already has an instance)
|
||||||
|
@ -104,7 +102,8 @@ export class Choices {
|
||||||
this.init = this.init.bind(this);
|
this.init = this.init.bind(this);
|
||||||
this.render = this.render.bind(this);
|
this.render = this.render.bind(this);
|
||||||
this.destroy = this.destroy.bind(this);
|
this.destroy = this.destroy.bind(this);
|
||||||
|
|
||||||
|
// Bind event handlers
|
||||||
this.onFocus = this.onFocus.bind(this);
|
this.onFocus = this.onFocus.bind(this);
|
||||||
this.onBlur = this.onBlur.bind(this);
|
this.onBlur = this.onBlur.bind(this);
|
||||||
this.onKeyUp = this.onKeyUp.bind(this);
|
this.onKeyUp = this.onKeyUp.bind(this);
|
||||||
|
@ -113,7 +112,6 @@ export class Choices {
|
||||||
this.onPaste = this.onPaste.bind(this);
|
this.onPaste = this.onPaste.bind(this);
|
||||||
|
|
||||||
const classNames = this.options.classNames;
|
const classNames = this.options.classNames;
|
||||||
|
|
||||||
this.templates = {
|
this.templates = {
|
||||||
containerOuter: () => {
|
containerOuter: () => {
|
||||||
return strToEl(`<div class="${ classNames.containerOuter }"></div>`);
|
return strToEl(`<div class="${ classNames.containerOuter }"></div>`);
|
||||||
|
@ -258,9 +256,8 @@ export class Choices {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
onKeyDown(e) {
|
onKeyDown(e) {
|
||||||
const activeItems = this.getItemsFilteredByActive();
|
if(e.target !== this.input) return;
|
||||||
const activeOptions = this.getOptionsFilteredByActive();
|
|
||||||
const inputIsFocussed = this.input === document.activeElement;
|
|
||||||
const ctrlDownKey = e.ctrlKey || e.metaKey;
|
const ctrlDownKey = e.ctrlKey || e.metaKey;
|
||||||
const backKey = 46;
|
const backKey = 46;
|
||||||
const deleteKey = 8;
|
const deleteKey = 8;
|
||||||
|
@ -269,89 +266,95 @@ export class Choices {
|
||||||
const escapeKey = 27;
|
const escapeKey = 27;
|
||||||
const upKey = 38;
|
const upKey = 38;
|
||||||
const downKey = 40;
|
const downKey = 40;
|
||||||
const hasActiveDropDown = this.dropdown && this.dropdown.classList.contains(this.options.classNames.activeState);
|
|
||||||
|
const activeItems = this.getItemsFilteredByActive();
|
||||||
|
const activeOptions = this.getOptionsFilteredByActive();
|
||||||
|
const hasFocussedInput = this.input === document.activeElement;
|
||||||
|
const hasActiveDropdown = this.dropdown && this.dropdown.classList.contains(this.options.classNames.activeState);
|
||||||
const hasItems = this.list && this.list.children;
|
const hasItems = this.list && this.list.children;
|
||||||
|
const keyString = String.fromCharCode(event.keyCode);
|
||||||
|
|
||||||
// If we are typing in the input
|
// If a user is typing and the dropdown is not active
|
||||||
if(e.target === this.input) {
|
if(/[a-zA-Z0-9-_ ]/.test(keyString) && this.dropdown && !hasActiveDropdown) {
|
||||||
// this.input.style.width = getWidthOfInput(this.input);
|
this.toggleDropdown();
|
||||||
switch (e.keyCode) {
|
}
|
||||||
case aKey:
|
|
||||||
// If CTRL + A or CMD + A have been pressed and there are items to select
|
switch (e.keyCode) {
|
||||||
if(ctrlDownKey && hasItems) {
|
case aKey:
|
||||||
if(this.options.removeItems && !this.input.value && this.options.selectAll && this.input === document.activeElement) {
|
// If CTRL + A or CMD + A have been pressed and there are items to select
|
||||||
this.selectAll(this.list.children);
|
if(ctrlDownKey && hasItems) {
|
||||||
}
|
if(this.options.removeItems && !this.input.value && this.options.selectAll && this.input === document.activeElement) {
|
||||||
|
this.selectAll(this.list.children);
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case enterKey:
|
break;
|
||||||
// If enter key is pressed and the input has a value
|
case enterKey:
|
||||||
if(e.target.value && this.passedElement.type === 'text') {
|
// If enter key is pressed and the input has a value
|
||||||
const value = this.input.value;
|
if(e.target.value && this.passedElement.type === 'text') {
|
||||||
this.handleEnter(activeItems, value);
|
const value = this.input.value;
|
||||||
|
this.handleEnter(activeItems, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.passedElement.type === 'select-multiple' && hasActiveDropdown) {
|
||||||
|
const highlighted = this.dropdown.querySelector(`.${this.options.classNames.highlightedState}`);
|
||||||
|
|
||||||
|
if(highlighted) {
|
||||||
|
const value = highlighted.getAttribute('data-choice-value');
|
||||||
|
const label = highlighted.innerHTML;
|
||||||
|
const id = highlighted.getAttribute('data-choice-id');
|
||||||
|
this.addItem(value, label, id);
|
||||||
|
this.input.value = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case escapeKey:
|
||||||
|
if(this.passedElement.type === 'select-multiple' && hasActiveDropdown) {
|
||||||
|
this.toggleDropdown();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case downKey:
|
||||||
|
case upKey:
|
||||||
|
// If up or down key is pressed, traverse through options
|
||||||
|
if(this.passedElement.type === 'select-multiple' && hasActiveDropdown) {
|
||||||
|
const selectableOptions = activeOptions.filter((option) => {
|
||||||
|
return !option.selected;
|
||||||
|
});
|
||||||
|
|
||||||
|
let canHighlight = true;
|
||||||
|
|
||||||
|
if(e.keyCode === downKey) {
|
||||||
|
this.highlightPosition < (selectableOptions.length - 1) ? this.highlightPosition++ : canHighlight = false;
|
||||||
|
} else if(e.keyCode === upKey) {
|
||||||
|
this.highlightPosition > 0 ? this.highlightPosition-- : canHighlight = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.passedElement.type === 'select-multiple' && hasActiveDropDown) {
|
if(canHighlight) {
|
||||||
const highlighted = this.dropdown.querySelector(`.${this.options.classNames.highlightedState}`);
|
const option = selectableOptions[this.highlightPosition];
|
||||||
|
if(option) {
|
||||||
if(highlighted) {
|
const previousElement = this.dropdown.querySelector(`.${this.options.classNames.highlightedState}`);
|
||||||
const value = highlighted.getAttribute('data-choice-value');
|
const currentElement = this.dropdown.querySelector(`[data-choice-id="${option.id}"]`);
|
||||||
const label = highlighted.innerHTML;
|
|
||||||
const id = highlighted.getAttribute('data-choice-id');
|
|
||||||
this.addItem(value, label, id);
|
|
||||||
this.input.value = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case escapeKey:
|
|
||||||
if(this.passedElement.type === 'select-multiple' && hasActiveDropDown) {
|
|
||||||
this.toggleDropdown();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case downKey:
|
|
||||||
case upKey:
|
|
||||||
// If up or down key is pressed, traverse through options
|
|
||||||
if(this.passedElement.type === 'select-multiple' && hasActiveDropDown) {
|
|
||||||
const selectableOptions = activeOptions.filter((option) => {
|
|
||||||
return !option.selected;
|
|
||||||
});
|
|
||||||
|
|
||||||
let canHighlight = true;
|
if(previousElement) {
|
||||||
|
previousElement.classList.remove(this.options.classNames.highlightedState);
|
||||||
|
}
|
||||||
|
|
||||||
if(e.keyCode === downKey) {
|
if(currentElement) {
|
||||||
this.highlightPosition < (selectableOptions.length - 1) ? this.highlightPosition++ : canHighlight = false;
|
currentElement.classList.add(this.options.classNames.highlightedState);
|
||||||
} else if(e.keyCode === upKey) {
|
|
||||||
this.highlightPosition > 0 ? this.highlightPosition-- : canHighlight = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(canHighlight) {
|
|
||||||
const option = selectableOptions[this.highlightPosition];
|
|
||||||
if(option) {
|
|
||||||
const previousElement = this.dropdown.querySelector(`.${this.options.classNames.highlightedState}`);
|
|
||||||
const currentElement = this.dropdown.querySelector(`[data-choice-id="${option.id}"]`);
|
|
||||||
|
|
||||||
if(previousElement) {
|
|
||||||
previousElement.classList.remove(this.options.classNames.highlightedState);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(currentElement) {
|
|
||||||
currentElement.classList.add(this.options.classNames.highlightedState);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
}
|
||||||
case backKey:
|
break
|
||||||
case deleteKey:
|
case backKey:
|
||||||
// If backspace or delete key is pressed and the input has no value
|
case deleteKey:
|
||||||
if(inputIsFocussed && !e.target.value) {
|
// If backspace or delete key is pressed and the input has no value
|
||||||
this.handleBackspaceKey(activeItems);
|
if(hasFocussedInput && !e.target.value) {
|
||||||
e.preventDefault();
|
this.handleBackspaceKey(activeItems);
|
||||||
}
|
e.preventDefault();
|
||||||
break;
|
}
|
||||||
default:
|
break;
|
||||||
break;
|
default:
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,31 +364,35 @@ export class Choices {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
onKeyUp(e) {
|
onKeyUp(e) {
|
||||||
if(e.target === this.input) {
|
if(e.target !== this.input) return;
|
||||||
if(this.passedElement.type === 'select-multiple' && this.options.allowSearch) {
|
|
||||||
const charStr = String.fromCharCode(e.keyCode);
|
if(this.passedElement.type === 'select-multiple' && this.options.allowSearch) {
|
||||||
if(this.input === document.activeElement && /[a-z0-9]/i.test(charStr)) {
|
const options = this.getOptions();
|
||||||
if(this.input.value) {
|
const hasUnactiveOptions = options.some((option) => {
|
||||||
// If we have a value, filter options based on it
|
return option.active !== true;
|
||||||
const handleFilter = debounce(() => {
|
});
|
||||||
const options = this.getOptionsFiltedBySelectable();
|
|
||||||
const sifter = new Sifter(options);
|
if(this.input === document.activeElement) {
|
||||||
const results = sifter.search(this.input.value, {
|
if(this.input.value) {
|
||||||
fields: ['label', 'value'],
|
// If we have a value, filter options based on it
|
||||||
sort: [{field: 'value', direction: 'asc'}],
|
const handleFilter = debounce(() => {
|
||||||
limit: 10
|
const options = this.getOptionsFiltedBySelectable();
|
||||||
});
|
const sifter = new Sifter(options);
|
||||||
this.store.dispatch(filterOptions(results));
|
const results = sifter.search(this.input.value, {
|
||||||
}, 100)
|
fields: ['label', 'value'],
|
||||||
|
sort: [{field: 'value', direction: 'asc'}],
|
||||||
handleFilter();
|
limit: 10
|
||||||
} else {
|
});
|
||||||
// Otherwise reset options to active
|
this.store.dispatch(filterOptions(results));
|
||||||
this.store.dispatch(activateOptions());
|
}, 100)
|
||||||
}
|
|
||||||
|
handleFilter();
|
||||||
|
} else if(hasUnactiveOptions) {
|
||||||
|
// Otherwise reset options to active
|
||||||
|
this.store.dispatch(activateOptions());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue