mirror of
https://github.com/Choices-js/Choices.git
synced 2024-06-03 14:32:11 +02:00
_canAddItem private function to validate a user can add an item
This commit is contained in:
parent
9da6b57b66
commit
84bec3804e
14
README.md
14
README.md
|
@ -183,6 +183,13 @@ Pass an array of objects:
|
||||||
|
|
||||||
<strong>Usage:</strong> Whether a user can edit items. An items value can be edited by pressing the backspace.
|
<strong>Usage:</strong> Whether a user can edit items. An items value can be edited by pressing the backspace.
|
||||||
|
|
||||||
|
### duplicateItems
|
||||||
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong> `true`
|
||||||
|
|
||||||
|
<strong>Input types affected:</strong> `text`, `select-multiple`
|
||||||
|
|
||||||
|
<strong>Usage:</strong> Whether a user can input/choose a duplicate item.
|
||||||
|
|
||||||
### delimiter
|
### delimiter
|
||||||
<strong>Type:</strong> `String` <strong>Default:</strong> `,`
|
<strong>Type:</strong> `String` <strong>Default:</strong> `,`
|
||||||
|
|
||||||
|
@ -190,13 +197,6 @@ Pass an array of objects:
|
||||||
|
|
||||||
<strong>Usage:</strong> What divides each value. By default the delimited value would be `"Value 1, Value 2, Value 3"`.
|
<strong>Usage:</strong> What divides each value. By default the delimited value would be `"Value 1, Value 2, Value 3"`.
|
||||||
|
|
||||||
### duplicates
|
|
||||||
<strong>Type:</strong> `Boolean` <strong>Default:</strong> `true`
|
|
||||||
|
|
||||||
<strong>Input types affected:</strong> `text`
|
|
||||||
|
|
||||||
<strong>Usage:</strong> Whether a user can input a duplicate item.
|
|
||||||
|
|
||||||
### paste
|
### paste
|
||||||
<strong>Type:</strong> `Boolean` <strong>Default:</strong> `true`
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong> `true`
|
||||||
|
|
||||||
|
|
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
|
@ -668,48 +668,6 @@ export class Choices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Process enter key event
|
|
||||||
* @param {Array} activeItems Items that are currently active
|
|
||||||
* @return
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_handleEnter(activeItems, value) {
|
|
||||||
let canUpdate = true;
|
|
||||||
|
|
||||||
if(this.config.addItems) {
|
|
||||||
if (this.config.maxItemCount && this.config.maxItemCount > 0 && this.config.maxItemCount <= this.itemList.children.length) {
|
|
||||||
// If there is a max entry limit and we have reached that limit
|
|
||||||
// don't update
|
|
||||||
canUpdate = false;
|
|
||||||
} else if(this.config.duplicateItems === false && this.passedElement.value) {
|
|
||||||
// If no duplicates are allowed, and the value already exists
|
|
||||||
// in the array, don't update
|
|
||||||
canUpdate = !activeItems.some((item) => item.value === value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
canUpdate = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canUpdate) {
|
|
||||||
let canAddItem = true;
|
|
||||||
|
|
||||||
// If a user has supplied a regular expression filter
|
|
||||||
if(this.config.regexFilter) {
|
|
||||||
// Determine whether we can update based on whether
|
|
||||||
// our regular expression passes
|
|
||||||
canAddItem = this._regexFilter(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// All is good, add
|
|
||||||
if(canAddItem) {
|
|
||||||
this.toggleDropdown();
|
|
||||||
this._addItem(value);
|
|
||||||
this._triggerChange(value);
|
|
||||||
this.clearInput(this.passedElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process enter/click of an item button
|
* Process enter/click of an item button
|
||||||
|
@ -780,6 +738,7 @@ export class Choices {
|
||||||
|
|
||||||
if(choice && !choice.selected && !choice.disabled) {
|
if(choice && !choice.selected && !choice.disabled) {
|
||||||
|
|
||||||
|
const hasActiveDropdown = this.dropdown.classList.contains(this.config.classNames.activeState);
|
||||||
let canAddItem = true;
|
let canAddItem = true;
|
||||||
|
|
||||||
if(this.config.maxItemCount > 0 && this.config.maxItemCount <= activeItems.length && this.passedElement.type === 'select-multiple') {
|
if(this.config.maxItemCount > 0 && this.config.maxItemCount <= activeItems.length && this.passedElement.type === 'select-multiple') {
|
||||||
|
@ -827,6 +786,50 @@ export class Choices {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates whether an item can be added by a user
|
||||||
|
* @param {Array} activeItems The currently active items
|
||||||
|
* @param {String} value Value of item to add
|
||||||
|
* @return {Object} Response: Whether user can add item
|
||||||
|
* Notice: Notice show in dropdown
|
||||||
|
*/
|
||||||
|
_canAddItem(activeItems, value) {
|
||||||
|
let canAddItem = true;
|
||||||
|
let notice = `Press Enter to add "${ value }"`;
|
||||||
|
|
||||||
|
if(this.passedElement.type === 'select-multiple' || this.passedElement.type === 'text') {
|
||||||
|
if (this.config.maxItemCount > 0 && this.config.maxItemCount <= this.itemList.children.length) {
|
||||||
|
// If there is a max entry limit and we have reached that limit
|
||||||
|
// don't update
|
||||||
|
canAddItem = false;
|
||||||
|
notice = `Only ${ this.config.maxItemCount } values can be added.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.passedElement.type === 'text' && this.config.addItems) {
|
||||||
|
const isUnique = !activeItems.some((item) => item.value === value);
|
||||||
|
|
||||||
|
// If a user has supplied a regular expression filter
|
||||||
|
if(this.config.regexFilter) {
|
||||||
|
// Determine whether we can update based on whether
|
||||||
|
// our regular expression passes
|
||||||
|
canAddItem = this._regexFilter(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no duplicates are allowed, and the value already exists
|
||||||
|
// in the array
|
||||||
|
if(this.config.duplicateItems === false && !isUnique) {
|
||||||
|
canAddItem = false;
|
||||||
|
notice = `Only unique values can be added.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
response: canAddItem,
|
||||||
|
notice: notice,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter choices based on search value
|
* Filter choices based on search value
|
||||||
* @param {String} value Value to filter by
|
* @param {String} value Value to filter by
|
||||||
|
@ -848,14 +851,13 @@ export class Choices {
|
||||||
if(newValue.length >= 1 && newValue !== currentValue + ' ') {
|
if(newValue.length >= 1 && newValue !== currentValue + ' ') {
|
||||||
const haystack = this.store.getChoicesFilteredBySelectable();
|
const haystack = this.store.getChoicesFilteredBySelectable();
|
||||||
const needle = newValue;
|
const needle = newValue;
|
||||||
|
const keys = isType('Array', this.config.sortFields) ? this.config.sortFields : [this.config.sortFields];
|
||||||
const keys = isType('Array', this.config.sortFields) ? this.config.sortFields : [this.config.sortFields];
|
const fuse = new Fuse(haystack, {
|
||||||
const fuse = new Fuse(haystack, {
|
|
||||||
keys: keys,
|
keys: keys,
|
||||||
shouldSort: true,
|
shouldSort: true,
|
||||||
include: 'score',
|
include: 'score',
|
||||||
});
|
});
|
||||||
const results = fuse.search(needle);
|
const results = fuse.search(needle);
|
||||||
|
|
||||||
this.currentValue = newValue;
|
this.currentValue = newValue;
|
||||||
this.highlightPosition = 0;
|
this.highlightPosition = 0;
|
||||||
|
@ -921,9 +923,17 @@ export class Choices {
|
||||||
|
|
||||||
case enterKey:
|
case enterKey:
|
||||||
// If enter key is pressed and the input has a value
|
// If enter key is pressed and the input has a value
|
||||||
if(target.value && this.passedElement.type === 'text') {
|
if(this.passedElement.type === 'text' && target.value) {
|
||||||
const value = this.input.value;
|
const value = this.input.value;
|
||||||
this._handleEnter(activeItems, value);
|
const canAddItem = this._canAddItem(activeItems, value);
|
||||||
|
|
||||||
|
// All is good, add
|
||||||
|
if(canAddItem.response) {
|
||||||
|
this.toggleDropdown();
|
||||||
|
this._addItem(value);
|
||||||
|
this._triggerChange(value);
|
||||||
|
this.clearInput(this.passedElement);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(target.hasAttribute('data-button')) {
|
if(target.hasAttribute('data-button')) {
|
||||||
|
@ -934,17 +944,12 @@ export class Choices {
|
||||||
const highlighted = this.dropdown.querySelector(`.${this.config.classNames.highlightedState}`);
|
const highlighted = this.dropdown.querySelector(`.${this.config.classNames.highlightedState}`);
|
||||||
|
|
||||||
if(highlighted) {
|
if(highlighted) {
|
||||||
let canAddItem = true;
|
const value = highlighted.getAttribute('data-value');
|
||||||
|
const label = highlighted.innerHTML;
|
||||||
if(this.config.maxItemCount > 0 && this.config.maxItemCount <= activeItems.length && this.passedElement.type === 'select-multiple') {
|
const id = highlighted.getAttribute('data-id');
|
||||||
canAddItem = false;
|
const canAddItem = this._canAddItem(activeItems, value);
|
||||||
}
|
|
||||||
|
|
||||||
if(canAddItem) {
|
|
||||||
const value = highlighted.getAttribute('data-value');
|
|
||||||
const label = highlighted.innerHTML;
|
|
||||||
const id = highlighted.getAttribute('data-id');
|
|
||||||
|
|
||||||
|
if(canAddItem.response) {
|
||||||
this._addItem(value, label, id);
|
this._addItem(value, label, id);
|
||||||
this._triggerChange(value);
|
this._triggerChange(value);
|
||||||
this.clearInput(this.passedElement);
|
this.clearInput(this.passedElement);
|
||||||
|
@ -1039,34 +1044,25 @@ export class Choices {
|
||||||
// notice. Otherwise hide the dropdown
|
// notice. Otherwise hide the dropdown
|
||||||
if(this.passedElement.type === 'text') {
|
if(this.passedElement.type === 'text') {
|
||||||
const hasActiveDropdown = this.dropdown.classList.contains(this.config.classNames.activeState);
|
const hasActiveDropdown = this.dropdown.classList.contains(this.config.classNames.activeState);
|
||||||
let dropdownItem;
|
const value = this.input.value;
|
||||||
if(this.input.value) {
|
|
||||||
const activeItems = this.store.getItemsFilteredByActive();
|
|
||||||
const isUnique = !activeItems.some((item) => item.value === this.input.value);
|
|
||||||
let canAddItem = true;
|
|
||||||
|
|
||||||
// If a user has supplied a regular expression filter
|
let dropdownItem;
|
||||||
if(this.config.regexFilter) {
|
|
||||||
// Determine whether we can update based on whether
|
if(value) {
|
||||||
// our regular expression passes
|
const activeItems = this.store.getItemsFilteredByActive();
|
||||||
canAddItem = this._regexFilter(this.input.value);
|
const canAddItem = this._canAddItem(activeItems, value);
|
||||||
}
|
|
||||||
|
if(canAddItem.notice) {
|
||||||
if(this.config.maxItemCount && this.config.maxItemCount > 0 && this.config.maxItemCount <= this.itemList.children.length) {
|
const dropdownItem = this._getTemplate('notice', canAddItem.notice);
|
||||||
dropdownItem = this._getTemplate('notice', `Only ${ this.config.maxItemCount } values can be added.`);
|
|
||||||
} else if(!this.config.duplicateItems && !isUnique) {
|
|
||||||
dropdownItem = this._getTemplate('notice', `Only unique values can be added.`);
|
|
||||||
} else if(canAddItem) {
|
|
||||||
dropdownItem = this._getTemplate('notice', `Press Enter to add "${ this.input.value }"`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(canAddItem !== false) {
|
|
||||||
this.dropdown.innerHTML = dropdownItem.outerHTML;
|
this.dropdown.innerHTML = dropdownItem.outerHTML;
|
||||||
if(!this.dropdown.classList.contains(this.config.classNames.activeState)) {
|
}
|
||||||
|
|
||||||
|
if(canAddItem.response === true) {
|
||||||
|
if(!hasActiveDropdown) {
|
||||||
this.showDropdown();
|
this.showDropdown();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(hasActiveDropdown) {
|
if(!canAddItem.notice && hasActiveDropdown) {
|
||||||
this.hideDropdown();
|
this.hideDropdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1075,6 +1071,7 @@ export class Choices {
|
||||||
this.hideDropdown();
|
this.hideDropdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const backKey = 46;
|
const backKey = 46;
|
||||||
const deleteKey = 8;
|
const deleteKey = 8;
|
||||||
|
@ -1092,6 +1089,7 @@ export class Choices {
|
||||||
this._searchChoices(this.input.value);
|
this._searchChoices(this.input.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
29
index.html
29
index.html
|
@ -58,13 +58,6 @@
|
||||||
<option value="Dropdown item 3" selected>Dropdown item 3</option>
|
<option value="Dropdown item 3" selected>Dropdown item 3</option>
|
||||||
<option value="Dropdown item 4" disabled>Dropdown item 4</option>
|
<option value="Dropdown item 4" disabled>Dropdown item 4</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<label for="choices-8">With pre-selected option</label>
|
|
||||||
<select class="form-control" data-choice name="choices-8" id="choices-8" placeholder="Choose an option" multiple>
|
|
||||||
<option value="Dropdown item 1">Dropdown item 1</option>
|
|
||||||
<option value="Dropdown item 2" selected>Dropdown item 2</option>
|
|
||||||
<option value="Dropdown item 3">Dropdown item 3</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<label for="choices-9">Option groups</label>
|
<label for="choices-9">Option groups</label>
|
||||||
<select class="form-control" data-choice name="choices-9" id="choices-9" placeholder="This is a placeholder" multiple>
|
<select class="form-control" data-choice name="choices-9" id="choices-9" placeholder="This is a placeholder" multiple>
|
||||||
|
@ -199,8 +192,6 @@
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(example1.getValue());
|
|
||||||
|
|
||||||
var example2 = new Choices('#choices-2', {
|
var example2 = new Choices('#choices-2', {
|
||||||
paste: false,
|
paste: false,
|
||||||
duplicateItems: false,
|
duplicateItems: false,
|
||||||
|
@ -208,7 +199,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
var example3 = new Choices('#choices-3', {
|
var example3 = new Choices('#choices-3', {
|
||||||
duplicates: false,
|
duplicateItems: false,
|
||||||
editItems: true,
|
editItems: true,
|
||||||
regexFilter: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
|
regexFilter: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
|
||||||
});
|
});
|
||||||
|
@ -227,7 +218,7 @@
|
||||||
items: ['josh@joshuajohnson.co.uk', { value: 'joe@bloggs.co.uk', label: 'Joe Bloggs' } ],
|
items: ['josh@joshuajohnson.co.uk', { value: 'joe@bloggs.co.uk', label: 'Joe Bloggs' } ],
|
||||||
});
|
});
|
||||||
|
|
||||||
var example8 = new Choices('#choices-10', {
|
var example9 = new Choices('#choices-10', {
|
||||||
placeholder: true,
|
placeholder: true,
|
||||||
placeholderValue: 'Pick an Strokes record',
|
placeholderValue: 'Pick an Strokes record',
|
||||||
maxItemCount: 5,
|
maxItemCount: 5,
|
||||||
|
@ -244,7 +235,7 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
var example9 = new Choices('#choices-12', {
|
var example10 = new Choices('#choices-12', {
|
||||||
placeholder: true,
|
placeholder: true,
|
||||||
placeholderValue: 'Pick an Arctic Monkeys record'
|
placeholderValue: 'Pick an Arctic Monkeys record'
|
||||||
}).ajax(function(callback) {
|
}).ajax(function(callback) {
|
||||||
|
@ -252,7 +243,7 @@
|
||||||
.then(function(response) {
|
.then(function(response) {
|
||||||
response.json().then(function(data) {
|
response.json().then(function(data) {
|
||||||
callback(data.releases, 'title', 'title');
|
callback(data.releases, 'title', 'title');
|
||||||
example9.setValueByChoice('Fake Tales Of San Francisco');
|
example10.setValueByChoice('Fake Tales Of San Francisco');
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(function(error) {
|
.catch(function(error) {
|
||||||
|
@ -260,7 +251,7 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
var example10 = new Choices('#choices-14', {
|
var example11 = new Choices('#choices-14', {
|
||||||
removeItemButton: true,
|
removeItemButton: true,
|
||||||
}).ajax(function(callback) {
|
}).ajax(function(callback) {
|
||||||
var request = new XMLHttpRequest();
|
var request = new XMLHttpRequest();
|
||||||
|
@ -273,7 +264,7 @@
|
||||||
if (status == 200) {
|
if (status == 200) {
|
||||||
data = JSON.parse(request.responseText);
|
data = JSON.parse(request.responseText);
|
||||||
callback(data.releases, 'title', 'title');
|
callback(data.releases, 'title', 'title');
|
||||||
example10.setValueByChoice('How Soon Is Now?');
|
example11.setValueByChoice('How Soon Is Now?');
|
||||||
} else {
|
} else {
|
||||||
console.error(status);
|
console.error(status);
|
||||||
}
|
}
|
||||||
|
@ -282,12 +273,12 @@
|
||||||
request.send();
|
request.send();
|
||||||
});
|
});
|
||||||
|
|
||||||
var example11 = new Choices('[data-choice]', {
|
var example12 = new Choices('[data-choice]', {
|
||||||
placeholderValue: 'This is a placeholder set in the config',
|
placeholderValue: 'This is a placeholder set in the config',
|
||||||
removeButton: true,
|
removeButton: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
var example12 = new Choices('#choices-15', {
|
var example13 = new Choices('#choices-15', {
|
||||||
search: false,
|
search: false,
|
||||||
choices: [
|
choices: [
|
||||||
{value: 'One', label: 'Label One'},
|
{value: 'One', label: 'Label One'},
|
||||||
|
@ -300,7 +291,7 @@
|
||||||
{value: 'Six', label: 'Label Six', selected: true},
|
{value: 'Six', label: 'Label Six', selected: true},
|
||||||
], 'value', 'label');
|
], 'value', 'label');
|
||||||
|
|
||||||
var example13 = new Choices('#choices-16', {
|
var example14 = new Choices('#choices-16', {
|
||||||
placeholder: true,
|
placeholder: true,
|
||||||
}).setChoices([{
|
}).setChoices([{
|
||||||
label: 'Group one',
|
label: 'Group one',
|
||||||
|
@ -323,7 +314,7 @@
|
||||||
]
|
]
|
||||||
}], 'value', 'label');
|
}], 'value', 'label');
|
||||||
|
|
||||||
var example14 = new Choices('#choices-17', {
|
var example15 = new Choices('#choices-17', {
|
||||||
choices: [
|
choices: [
|
||||||
{value: 'One', label: 'Label One'},
|
{value: 'One', label: 'Label One'},
|
||||||
{value: 'Two', label: 'Label Two', disabled: true},
|
{value: 'Two', label: 'Label Two', disabled: true},
|
||||||
|
|
Loading…
Reference in a new issue