mirror of
https://github.com/Choices-js/Choices.git
synced 2024-05-20 22:46:33 +02:00
Mousedown event instead of click for reacting before blur triggers
This commit is contained in:
parent
ec8c324383
commit
c76088fc8f
4
assets/scripts/dist/choices.min.js
vendored
4
assets/scripts/dist/choices.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -109,7 +109,7 @@ export class Choices {
|
||||||
this.onBlur = this.onBlur.bind(this);
|
this.onBlur = this.onBlur.bind(this);
|
||||||
this.onKeyUp = this.onKeyUp.bind(this);
|
this.onKeyUp = this.onKeyUp.bind(this);
|
||||||
this.onKeyDown = this.onKeyDown.bind(this);
|
this.onKeyDown = this.onKeyDown.bind(this);
|
||||||
this.onClick = this.onClick.bind(this);
|
this.onMouseDown = this.onMouseDown.bind(this);
|
||||||
this.onPaste = this.onPaste.bind(this);
|
this.onPaste = this.onPaste.bind(this);
|
||||||
this.onMouseOver = this.onMouseOver.bind(this);
|
this.onMouseOver = this.onMouseOver.bind(this);
|
||||||
|
|
||||||
|
@ -216,7 +216,7 @@ export class Choices {
|
||||||
const activeItems = this.store.getItemsFilteredByActive();
|
const activeItems = this.store.getItemsFilteredByActive();
|
||||||
const activeOptions = this.store.getOptionsFilteredByActive();
|
const activeOptions = this.store.getOptionsFilteredByActive();
|
||||||
|
|
||||||
const hasFocussedInput = this.input === document.activeElement;
|
const hasFocusedInput = this.input === document.activeElement;
|
||||||
const hasActiveDropdown = this.dropdown.classList.contains(this.options.classNames.activeState);
|
const hasActiveDropdown = 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);
|
const keyString = String.fromCharCode(event.keyCode);
|
||||||
|
@ -295,7 +295,7 @@ export class Choices {
|
||||||
case backKey:
|
case backKey:
|
||||||
case deleteKey:
|
case deleteKey:
|
||||||
// If backspace or delete key is pressed and the input has no value
|
// If backspace or delete key is pressed and the input has no value
|
||||||
if(hasFocussedInput && !e.target.value) {
|
if(hasFocusedInput && !e.target.value) {
|
||||||
this.handleBackspace(activeItems);
|
this.handleBackspace(activeItems);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
|
@ -322,13 +322,16 @@ export class Choices {
|
||||||
if (this.options.maxItems && this.options.maxItems <= this.list.children.length) {
|
if (this.options.maxItems && this.options.maxItems <= this.list.children.length) {
|
||||||
dropdownItem = this.getTemplate('notice', `Only ${ this.options.maxItems } options can be selected.`);
|
dropdownItem = this.getTemplate('notice', `Only ${ this.options.maxItems } options can be selected.`);
|
||||||
} else {
|
} else {
|
||||||
dropdownItem = this.getTemplate('notice', `Add "${ this.input.value }"`);
|
dropdownItem = this.getTemplate('notice', `Add "${ this.input.value }"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dropdown.innerHTML = dropdownItem.outerHTML;
|
if((this.options.regexFilter && this.regexFilter(this.input.value)) || !this.options.regexFilter) {
|
||||||
if(!this.dropdown.classList.contains(this.options.classNames.activeState)) {
|
this.dropdown.innerHTML = dropdownItem.outerHTML;
|
||||||
this.showDropdown();
|
if(!this.dropdown.classList.contains(this.options.classNames.activeState)) {
|
||||||
|
this.showDropdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if(this.dropdown.classList.contains(this.options.classNames.activeState)) {
|
if(this.dropdown.classList.contains(this.options.classNames.activeState)) {
|
||||||
this.hideDropdown();
|
this.hideDropdown();
|
||||||
|
@ -378,12 +381,17 @@ export class Choices {
|
||||||
* @param {Object} e Event
|
* @param {Object} e Event
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
onClick(e) {
|
onMouseDown(e) {
|
||||||
const activeItems = this.store.getItemsFilteredByActive();
|
const activeItems = this.store.getItemsFilteredByActive();
|
||||||
const hasShiftKey = e.shiftKey ? true : false;
|
|
||||||
|
|
||||||
// If click is affecting a child node of our element
|
// If click is affecting a child node of our element
|
||||||
if(this.containerOuter.contains(e.target)) {
|
if(this.containerOuter.contains(e.target)) {
|
||||||
|
// Prevent blur event triggering causing dropdown to close
|
||||||
|
// in a race condition
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const hasShiftKey = e.shiftKey ? true : false;
|
||||||
|
|
||||||
// If input is not in focus, it ought to be
|
// If input is not in focus, it ought to be
|
||||||
if(this.input !== document.activeElement) {
|
if(this.input !== document.activeElement) {
|
||||||
this.input.focus();
|
this.input.focus();
|
||||||
|
@ -427,17 +435,16 @@ export class Choices {
|
||||||
} else {
|
} else {
|
||||||
// Click is outside of our element so close dropdown and de-select items
|
// Click is outside of our element so close dropdown and de-select items
|
||||||
const hasSelectedItems = activeItems.some((item) => item.selected === true);
|
const hasSelectedItems = activeItems.some((item) => item.selected === true);
|
||||||
|
const hasActiveDropdown = this.dropdown.classList.contains(this.options.classNames.activeState);
|
||||||
|
|
||||||
if(hasSelectedItems) {
|
// Deselect items
|
||||||
this.deselectAll();
|
if(hasSelectedItems) this.deselectAll();
|
||||||
}
|
|
||||||
|
// Remove focus state
|
||||||
this.containerOuter.classList.remove(this.options.classNames.focusState);
|
this.containerOuter.classList.remove(this.options.classNames.focusState);
|
||||||
|
|
||||||
// Close all other dropodowns
|
// Close all other dropdowns
|
||||||
if(this.dropdown.classList.contains(this.options.classNames.activeState)) {
|
if(hasActiveDropdown) this.toggleDropdown();
|
||||||
this.toggleDropdown();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -580,6 +587,11 @@ export class Choices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlight option element
|
||||||
|
* @param {HTMLElement} el Element to highlight
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
highlightOption(el) {
|
highlightOption(el) {
|
||||||
// Highlight first element in dropdown
|
// Highlight first element in dropdown
|
||||||
const options = Array.from(this.dropdown.querySelectorAll('[data-option-selectable]'));
|
const options = Array.from(this.dropdown.querySelectorAll('[data-option-selectable]'));
|
||||||
|
@ -776,6 +788,10 @@ export class Choices {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
addOption(option, value, label, groupId = -1) {
|
addOption(option, value, label, groupId = -1) {
|
||||||
|
if(!value) return
|
||||||
|
|
||||||
|
if(!label) { label = value; }
|
||||||
|
|
||||||
// Generate unique id
|
// Generate unique id
|
||||||
const options = this.store.getOptions();
|
const options = this.store.getOptions();
|
||||||
const id = options.length + 1;
|
const id = options.length + 1;
|
||||||
|
@ -852,11 +868,7 @@ export class Choices {
|
||||||
toggleDropdown() {
|
toggleDropdown() {
|
||||||
const isActive = this.dropdown.classList.contains(this.options.classNames.activeState);
|
const isActive = this.dropdown.classList.contains(this.options.classNames.activeState);
|
||||||
|
|
||||||
if(isActive) {
|
isActive ? this.hideDropdown() : this.showDropdown();
|
||||||
this.hideDropdown();
|
|
||||||
} else {
|
|
||||||
this.showDropdown();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -871,12 +883,25 @@ export class Choices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populate options via ajax callback
|
||||||
|
* @param {Function} fn Passed
|
||||||
|
* @return {[type]} [description]
|
||||||
|
*/
|
||||||
ajax(fn) {
|
ajax(fn) {
|
||||||
const callback = (results, title, label) => {
|
this.containerOuter.classList.add('is-loading');
|
||||||
|
this.input.placeholder = "Loading...";
|
||||||
|
|
||||||
|
const callback = (results, value, label) => {
|
||||||
if(results && results.length) {
|
if(results && results.length) {
|
||||||
results.forEach((result) => {
|
this.containerOuter.classList.remove('is-loading');
|
||||||
|
this.input.placeholder = "";
|
||||||
|
results.forEach((result, index) => {
|
||||||
// Add each result to option dropdown
|
// Add each result to option dropdown
|
||||||
this.addOption(null, result[title], result[label]);
|
if(index === 0) {
|
||||||
|
this.addItem(result[value], result[label], index);
|
||||||
|
}
|
||||||
|
this.addOption(null, result[value], result[label]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -978,9 +1003,12 @@ export class Choices {
|
||||||
wrap(containerInner, containerOuter);
|
wrap(containerInner, containerOuter);
|
||||||
|
|
||||||
// If placeholder has been enabled and we have a value
|
// If placeholder has been enabled and we have a value
|
||||||
if (this.options.placeholder && this.options.placeholderValue) {
|
if (this.options.placeholder && (this.options.placeholderValue || this.passedElement.placeholder)) {
|
||||||
input.placeholder = this.options.placeholderValue;
|
if(this.passedElement.type !== 'select-one') {
|
||||||
input.style.width = getWidthOfInput(input);
|
const placeholder = this.options.placeholderValue || this.passedElement.placeholder;
|
||||||
|
input.placeholder = placeholder;
|
||||||
|
input.style.width = getWidthOfInput(input);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!this.options.addItems) {
|
if(!this.options.addItems) {
|
||||||
|
@ -999,7 +1027,7 @@ export class Choices {
|
||||||
|
|
||||||
this.isSearching = false;
|
this.isSearching = false;
|
||||||
|
|
||||||
if(passedGroups.length) {
|
if(passedGroups && passedGroups.length) {
|
||||||
passedGroups.forEach((group, index) => {
|
passedGroups.forEach((group, index) => {
|
||||||
const isFirst = index === 0 ? true : false;
|
const isFirst = index === 0 ? true : false;
|
||||||
this.addGroup(group, index, isFirst);
|
this.addGroup(group, index, isFirst);
|
||||||
|
@ -1144,10 +1172,10 @@ export class Choices {
|
||||||
addEventListeners() {
|
addEventListeners() {
|
||||||
document.addEventListener('keyup', this.onKeyUp);
|
document.addEventListener('keyup', this.onKeyUp);
|
||||||
document.addEventListener('keydown', this.onKeyDown);
|
document.addEventListener('keydown', this.onKeyDown);
|
||||||
document.addEventListener('click', this.onClick);
|
document.addEventListener('mousedown', this.onMouseDown);
|
||||||
document.addEventListener('paste', this.onPaste);
|
|
||||||
document.addEventListener('mouseover', this.onMouseOver);
|
document.addEventListener('mouseover', this.onMouseOver);
|
||||||
|
|
||||||
|
this.input.addEventListener('paste', this.onPaste);
|
||||||
this.input.addEventListener('focus', this.onFocus);
|
this.input.addEventListener('focus', this.onFocus);
|
||||||
this.input.addEventListener('blur', this.onBlur);
|
this.input.addEventListener('blur', this.onBlur);
|
||||||
}
|
}
|
||||||
|
@ -1159,10 +1187,10 @@ export class Choices {
|
||||||
removeEventListeners() {
|
removeEventListeners() {
|
||||||
document.removeEventListener('keyup', this.onKeyUp);
|
document.removeEventListener('keyup', this.onKeyUp);
|
||||||
document.removeEventListener('keydown', this.onKeyDown);
|
document.removeEventListener('keydown', this.onKeyDown);
|
||||||
document.removeEventListener('click', this.onClick);
|
document.removeEventListener('mousedown', this.onMouseDown);
|
||||||
document.removeEventListener('paste', this.onPaste);
|
|
||||||
document.removeEventListener('mouseover', this.onMouseOver);
|
document.removeEventListener('mouseover', this.onMouseOver);
|
||||||
|
|
||||||
|
this.input.removeEventListener('paste', this.onPaste);
|
||||||
this.input.removeEventListener('focus', this.onFocus);
|
this.input.removeEventListener('focus', this.onFocus);
|
||||||
this.input.removeEventListener('blur', this.onBlur);
|
this.input.removeEventListener('blur', this.onBlur);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<label for="choices-1">Limited to 5</label>
|
<label for="choices-1">Limited to 5</label>
|
||||||
<input id="choices-1" type="text" value="preset-1 preset-2">
|
<input id="choices-1" type="text" value="preset-1 preset-2">
|
||||||
|
|
||||||
<label for="choices-2">Unique values only</label>
|
<label for="choices-2">Unique values only, no pasting</label>
|
||||||
<input id="choices-2" type="text" value="preset-1, preset-2" placeholder="This is a placeholder" class="custom class">
|
<input id="choices-2" type="text" value="preset-1, preset-2" placeholder="This is a placeholder" class="custom class">
|
||||||
|
|
||||||
<label for="choices-3">Email addresses only</label>
|
<label for="choices-3">Email addresses only</label>
|
||||||
|
|
Loading…
Reference in a new issue