mirror of
https://github.com/Choices-js/Choices.git
synced 2024-05-09 17:16:36 +02:00
Version 2.6.0
This commit is contained in:
parent
b2d93572ae
commit
ec0d336072
115
README.md
115
README.md
|
@ -18,11 +18,14 @@ With [NPM](https://www.npmjs.com/package/choices.js):
|
|||
```zsh
|
||||
npm install choices.js --save
|
||||
```
|
||||
|
||||
With [Bower](https://bower.io/):
|
||||
```zsh
|
||||
bower install choices.js --save
|
||||
```
|
||||
|
||||
Or include Choices directly:
|
||||
|
||||
```html
|
||||
<!-- Include base CSS (optional) -->
|
||||
<link rel="stylesheet" href="assets/styles/css/base.min.css">
|
||||
|
@ -106,13 +109,7 @@ Or include Choices directly:
|
|||
loadingState: 'is-loading',
|
||||
},
|
||||
callbackOnInit: null,
|
||||
callbackOnAddItem: null,
|
||||
callbackOnRemoveItem: null,
|
||||
callbackOnHighlightItem: null,
|
||||
callbackOnUnhighlightItem: null,
|
||||
callbackOnCreateTemplates: null,
|
||||
callbackOnChange: null,
|
||||
callbackOnSearch: null,
|
||||
});
|
||||
```
|
||||
|
||||
|
@ -406,44 +403,6 @@ classNames: {
|
|||
|
||||
**Usage:** Function to run once Choices initialises.
|
||||
|
||||
### callbackOnAddItem
|
||||
**Type:** `Function` **Default:** `null` **Arguments:** `id, value, groupValue`
|
||||
|
||||
**Input types affected:** `text`, `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** Function to run each time an item is added (programmatically or by the user).
|
||||
|
||||
**Example:**
|
||||
|
||||
```js
|
||||
const example = new Choices(element, {
|
||||
callbackOnAddItem: (id, value, groupValue) => {
|
||||
// do something creative here...
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### callbackOnRemoveItem
|
||||
**Type:** `Function` **Default:** `null` **Arguments:** `id, value, groupValue`
|
||||
|
||||
**Input types affected:** `text`, `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** Function to run each time an item is removed (programmatically or by the user).
|
||||
|
||||
### callbackOnHighlightItem
|
||||
**Type:** `Function` **Default:** `null` **Arguments:** `id, value, groupValue`
|
||||
|
||||
**Input types affected:** `text`, `select-multiple`
|
||||
|
||||
**Usage:** Function to run each time an item is highlighted.
|
||||
|
||||
### callbackOnUnhighlightItem
|
||||
**Type:** `Function` **Default:** `null` **Arguments:** `id, value, groupValue`
|
||||
|
||||
**Input types affected:** `text`, `select-multiple`
|
||||
|
||||
**Usage:** Function to run each time an item is unhighlighted.
|
||||
|
||||
### callbackOnCreateTemplates
|
||||
**Type:** `Function` **Default:** `null` **Arguments:** `template`
|
||||
|
||||
|
@ -477,19 +436,73 @@ const example = new Choices(element, {
|
|||
});
|
||||
```
|
||||
|
||||
### callbackOnChange
|
||||
**Type:** `Function` **Default:** `null` **Arguments:** `value`
|
||||
## Events
|
||||
**Note:** Events fired by Choices behave the same as standard events. Each event is triggered on the element passed to Choices (accessible via `this.passedElement`. Arguments are accessible within the `event.detail` object.
|
||||
|
||||
**Example:**
|
||||
|
||||
```js
|
||||
const element = document.getElementById('example');
|
||||
const example = new Choices(element);
|
||||
|
||||
element.addEventListener('addItem', function(event) {
|
||||
// do something creative here...
|
||||
console.log(event.detail.id);
|
||||
console.log(event.detail.value);
|
||||
console.log(event.detail.groupValue);
|
||||
}, false);
|
||||
|
||||
// or
|
||||
const example = new Choices(document.getElementById('example'));
|
||||
|
||||
example.passedElement.addEventListener('addItem', function(event) {
|
||||
// do something creative here...
|
||||
console.log(event.detail.id);
|
||||
console.log(event.detail.value);
|
||||
console.log(event.detail.groupValue);
|
||||
}, false);
|
||||
|
||||
```
|
||||
|
||||
### addItem
|
||||
**Arguments:** `id, value, groupValue`
|
||||
|
||||
**Input types affected:** `text`, `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** Function to run each time an item is added/removed by a user.
|
||||
**Usage:** Triggered each time an item is added (programmatically or by the user).
|
||||
|
||||
### callbackOnSearch
|
||||
**Type:** `Function` **Default:** `null` **Arguments:** `value`
|
||||
### removeItem
|
||||
**Arguments:** `id, value, groupValue`
|
||||
|
||||
**Input types affected:** `select-one`, `select-multiple`
|
||||
**Input types affected:** `text`, `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** Function to run when a user types into an input to search choices.
|
||||
**Usage:** Triggered each time an item is removed (programmatically or by the user).
|
||||
|
||||
### highlightItem
|
||||
**Arguments:** `id, value, groupValue`
|
||||
|
||||
**Input types affected:** `text`, `select-multiple`
|
||||
|
||||
**Usage:** Triggered each time an item is highlighted.
|
||||
|
||||
### unhighlightItem
|
||||
**Arguments:** `id, value, groupValue`
|
||||
|
||||
**Input types affected:** `text`, `select-multiple`
|
||||
|
||||
**Usage:** Triggered each time an item is unhighlighted.
|
||||
|
||||
### change
|
||||
**Arguments:** `value`
|
||||
|
||||
**Input types affected:** `text`, `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** Triggered each time an item is added/removed **by a user**.
|
||||
|
||||
### search
|
||||
**Arguments:** `value` **Input types affected:** `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** Triggered when a user types into an input to search choices.
|
||||
|
||||
## Methods
|
||||
Methods can be called either directly or by chaining:
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
getWidthOfInput,
|
||||
sortByAlpha,
|
||||
sortByScore,
|
||||
triggerEvent,
|
||||
}
|
||||
from './lib/utils.js';
|
||||
import './lib/polyfills.js';
|
||||
|
@ -105,13 +106,7 @@ class Choices {
|
|||
loadingState: 'is-loading',
|
||||
},
|
||||
callbackOnInit: null,
|
||||
callbackOnAddItem: null,
|
||||
callbackOnRemoveItem: null,
|
||||
callbackOnHighlightItem: null,
|
||||
callbackOnUnhighlightItem: null,
|
||||
callbackOnCreateTemplates: null,
|
||||
callbackOnChange: null,
|
||||
callbackOnSearch: null,
|
||||
};
|
||||
|
||||
// Merge options with user options
|
||||
|
@ -223,8 +218,6 @@ class Choices {
|
|||
if (callback) {
|
||||
if (isType('Function', callback)) {
|
||||
callback.call(this);
|
||||
} else {
|
||||
console.error('callbackOnInit: Callback is not a function');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -449,24 +442,26 @@ class Choices {
|
|||
* @return {Object} Class instance
|
||||
* @public
|
||||
*/
|
||||
highlightItem(item) {
|
||||
highlightItem(item, runEvent = true) {
|
||||
if (!item) return;
|
||||
const id = item.id;
|
||||
const groupId = item.groupId;
|
||||
const callback = this.config.callbackOnHighlightItem;
|
||||
const group = groupId >= 0 ? this.store.getGroupById(groupId) : null;
|
||||
|
||||
this.store.dispatch(highlightItem(id, true));
|
||||
|
||||
// Run callback if it is a function
|
||||
if (callback) {
|
||||
if (isType('Function', callback)) {
|
||||
const group = groupId >= 0 ? this.store.getGroupById(groupId) : null;
|
||||
if(group && group.value) {
|
||||
callback.call(this, id, item.value, group.value);
|
||||
} else {
|
||||
callback.call(this, id, item.value)
|
||||
}
|
||||
if (runEvent) {
|
||||
if(group && group.value) {
|
||||
triggerEvent(this.passedElement, 'highlightItem', {
|
||||
id,
|
||||
value: item.value,
|
||||
groupValue: group.value
|
||||
});
|
||||
} else {
|
||||
console.error('callbackOnHighlightItem: Callback is not a function');
|
||||
triggerEvent(this.passedElement, 'highlightItem', {
|
||||
id,
|
||||
value: item.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -483,22 +478,21 @@ class Choices {
|
|||
if (!item) return;
|
||||
const id = item.id;
|
||||
const groupId = item.groupId;
|
||||
const callback = this.config.callbackOnUnhighlightItem;
|
||||
const group = groupId >= 0 ? this.store.getGroupById(groupId) : null;
|
||||
|
||||
this.store.dispatch(highlightItem(id, false));
|
||||
|
||||
// Run callback if it is a function
|
||||
if (callback) {
|
||||
if (isType('Function', callback)) {
|
||||
const group = groupId >= 0 ? this.store.getGroupById(groupId) : null;
|
||||
if(group && group.value) {
|
||||
callback.call(this, id, item.value, group.value);
|
||||
} else {
|
||||
callback.call(this, id, item.value)
|
||||
}
|
||||
} else {
|
||||
console.error('callbackOnUnhighlightItem: Callback is not a function');
|
||||
}
|
||||
if(group && group.value) {
|
||||
triggerEvent(this.passedElement, 'unhighlightItem', {
|
||||
id,
|
||||
value: item.value,
|
||||
groupValue: group.value
|
||||
});
|
||||
} else {
|
||||
triggerEvent(this.passedElement, 'unhighlightItem', {
|
||||
id,
|
||||
value: item.value,
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
|
@ -580,15 +574,15 @@ class Choices {
|
|||
* @return {Object} Class instance
|
||||
* @public
|
||||
*/
|
||||
removeHighlightedItems(runCallback = false) {
|
||||
removeHighlightedItems(runEvent = false) {
|
||||
const items = this.store.getItemsFilteredByActive();
|
||||
|
||||
items.forEach((item) => {
|
||||
if (item.highlighted && item.active) {
|
||||
this._removeItem(item);
|
||||
// If this action was performed by the user
|
||||
// run the callback
|
||||
if (runCallback) {
|
||||
// trigger the event
|
||||
if (runEvent) {
|
||||
this._triggerChange(item.value);
|
||||
}
|
||||
}
|
||||
|
@ -895,16 +889,10 @@ class Choices {
|
|||
*/
|
||||
_triggerChange(value) {
|
||||
if (!value) return;
|
||||
const callback = this.config.callbackOnChange;
|
||||
|
||||
// Run callback if it is a function
|
||||
if (callback) {
|
||||
if (isType('Function', callback)) {
|
||||
callback.call(this, value);
|
||||
} else {
|
||||
console.error('callbackOnChange: Callback is not a function');
|
||||
}
|
||||
}
|
||||
triggerEvent(this.passedElement, 'change', {
|
||||
value
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1023,7 +1011,7 @@ class Choices {
|
|||
this._triggerChange(lastItem.value);
|
||||
} else {
|
||||
if (!hasHighlightedItems) {
|
||||
this.highlightItem(lastItem);
|
||||
this.highlightItem(lastItem, false);
|
||||
}
|
||||
this.removeHighlightedItems(true);
|
||||
}
|
||||
|
@ -1173,7 +1161,6 @@ class Choices {
|
|||
if (!value) return;
|
||||
const choices = this.store.getChoices();
|
||||
const hasUnactiveChoices = choices.some((option) => option.active !== true);
|
||||
const callback = this.config.callbackOnSearch;
|
||||
|
||||
// Run callback if it is a function
|
||||
if (this.input === document.activeElement) {
|
||||
|
@ -1181,14 +1168,10 @@ class Choices {
|
|||
if (value && value.length > this.config.searchFloor) {
|
||||
// Filter available choices
|
||||
this._searchChoices(value);
|
||||
// Run callback if it is a function
|
||||
if (callback) {
|
||||
if (isType('Function', callback)) {
|
||||
callback.call(this, value);
|
||||
} else {
|
||||
console.error('callbackOnSearch: Callback is not a function');
|
||||
}
|
||||
}
|
||||
// Trigger search event
|
||||
triggerEvent(this.passedElement, 'search', {
|
||||
value,
|
||||
});
|
||||
} else if (hasUnactiveChoices) {
|
||||
// Otherwise reset choices to active
|
||||
this.isSearching = false;
|
||||
|
@ -1853,7 +1836,12 @@ class Choices {
|
|||
const items = this.store.getItems();
|
||||
const passedLabel = label || passedValue;
|
||||
const passedOptionId = parseInt(choiceId, 10) || -1;
|
||||
const callback = this.config.callbackOnAddItem;
|
||||
|
||||
// Get group if group ID passed
|
||||
const group = groupId >= 0 ? this.store.getGroupById(groupId) : null;
|
||||
|
||||
// Generate unique id
|
||||
const id = items ? items.length + 1 : 1;
|
||||
|
||||
// If a prepended value has been passed, prepend it
|
||||
if (this.config.prependValue) {
|
||||
|
@ -1865,27 +1853,24 @@ class Choices {
|
|||
passedValue += this.config.appendValue.toString();
|
||||
}
|
||||
|
||||
// Generate unique id
|
||||
const id = items ? items.length + 1 : 1;
|
||||
|
||||
this.store.dispatch(addItem(passedValue, passedLabel, id, passedOptionId, groupId));
|
||||
|
||||
if (this.passedElement.type === 'select-one') {
|
||||
this.removeActiveItems(id);
|
||||
}
|
||||
|
||||
// Run callback if it is a function
|
||||
if (callback) {
|
||||
const group = groupId >= 0 ? this.store.getGroupById(groupId) : null;
|
||||
if (isType('Function', callback)) {
|
||||
if(group && group.value) {
|
||||
callback.call(this, id, passedValue, group.value);
|
||||
} else {
|
||||
callback.call(this, id, passedValue);
|
||||
}
|
||||
} else {
|
||||
console.error('callbackOnAddItem: Callback is not a function');
|
||||
}
|
||||
// Trigger change event
|
||||
if(group && group.value) {
|
||||
triggerEvent(this.passedElement, 'addItem', {
|
||||
id,
|
||||
value: passedValue,
|
||||
groupValue: group.value,
|
||||
});
|
||||
} else {
|
||||
triggerEvent(this.passedElement, 'addItem', {
|
||||
id,
|
||||
value: passedValue,
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
|
@ -1908,22 +1893,21 @@ class Choices {
|
|||
const value = item.value;
|
||||
const choiceId = item.choiceId;
|
||||
const groupId = item.groupId;
|
||||
const callback = this.config.callbackOnRemoveItem;
|
||||
const group = groupId >= 0 ? this.store.getGroupById(groupId) : null;
|
||||
|
||||
this.store.dispatch(removeItem(id, choiceId));
|
||||
|
||||
// Run callback
|
||||
if (callback) {
|
||||
if (isType('Function', callback)) {
|
||||
const group = groupId >= 0 ? this.store.getGroupById(groupId) : null;
|
||||
if(group && group.value) {
|
||||
callback.call(this, id, value, group.value);
|
||||
} else {
|
||||
callback.call(this, id, value);
|
||||
}
|
||||
} else {
|
||||
console.error('callbackOnRemoveItem: Callback is not a function');
|
||||
}
|
||||
if(group && group.value) {
|
||||
triggerEvent(this.passedElement, 'removeItem', {
|
||||
id,
|
||||
value,
|
||||
groupValue: group.value,
|
||||
});
|
||||
} else {
|
||||
triggerEvent(this.passedElement, 'removeItem', {
|
||||
id,
|
||||
value,
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
|
@ -2100,6 +2084,7 @@ class Choices {
|
|||
if (callbackTemplate && isType('Function', callbackTemplate)) {
|
||||
userTemplates = callbackTemplate.call(this, strToEl);
|
||||
}
|
||||
|
||||
this.config.templates = extend(templates, userTemplates);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,112 +1,129 @@
|
|||
/* eslint-disable */
|
||||
// Production steps of ECMA-262, Edition 6, 22.1.2.1
|
||||
// Reference: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from
|
||||
if (!Array.from) {
|
||||
Array.from = (function() {
|
||||
var toStr = Object.prototype.toString;
|
||||
(function () {
|
||||
// Production steps of ECMA-262, Edition 6, 22.1.2.1
|
||||
// Reference: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from
|
||||
if (!Array.from) {
|
||||
Array.from = (function() {
|
||||
var toStr = Object.prototype.toString;
|
||||
|
||||
var isCallable = function(fn) {
|
||||
return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
|
||||
};
|
||||
var isCallable = function(fn) {
|
||||
return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
|
||||
};
|
||||
|
||||
var toInteger = function(value) {
|
||||
var number = Number(value);
|
||||
if (isNaN(number)) {
|
||||
return 0;
|
||||
}
|
||||
if (number === 0 || !isFinite(number)) {
|
||||
return number;
|
||||
}
|
||||
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
|
||||
};
|
||||
var toInteger = function(value) {
|
||||
var number = Number(value);
|
||||
if (isNaN(number)) {
|
||||
return 0;
|
||||
}
|
||||
if (number === 0 || !isFinite(number)) {
|
||||
return number;
|
||||
}
|
||||
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
|
||||
};
|
||||
|
||||
var maxSafeInteger = Math.pow(2, 53) - 1;
|
||||
var maxSafeInteger = Math.pow(2, 53) - 1;
|
||||
|
||||
var toLength = function(value) {
|
||||
var len = toInteger(value);
|
||||
return Math.min(Math.max(len, 0), maxSafeInteger);
|
||||
};
|
||||
var toLength = function(value) {
|
||||
var len = toInteger(value);
|
||||
return Math.min(Math.max(len, 0), maxSafeInteger);
|
||||
};
|
||||
|
||||
// The length property of the from method is 1.
|
||||
return function from(arrayLike /*, mapFn, thisArg */ ) {
|
||||
// 1. Let C be the this value.
|
||||
var C = this;
|
||||
// The length property of the from method is 1.
|
||||
return function from(arrayLike /*, mapFn, thisArg */ ) {
|
||||
// 1. Let C be the this value.
|
||||
var C = this;
|
||||
|
||||
// 2. Let items be ToObject(arrayLike).
|
||||
var items = Object(arrayLike);
|
||||
// 2. Let items be ToObject(arrayLike).
|
||||
var items = Object(arrayLike);
|
||||
|
||||
// 3. ReturnIfAbrupt(items).
|
||||
if (arrayLike == null) {
|
||||
throw new TypeError("Array.from requires an array-like object - not null or undefined");
|
||||
}
|
||||
|
||||
// 4. If mapfn is undefined, then let mapping be false.
|
||||
var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
|
||||
var T;
|
||||
if (typeof mapFn !== 'undefined') {
|
||||
// 5. else
|
||||
// 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
|
||||
if (!isCallable(mapFn)) {
|
||||
throw new TypeError('Array.from: when provided, the second argument must be a function');
|
||||
// 3. ReturnIfAbrupt(items).
|
||||
if (arrayLike == null) {
|
||||
throw new TypeError("Array.from requires an array-like object - not null or undefined");
|
||||
}
|
||||
|
||||
// 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
||||
if (arguments.length > 2) {
|
||||
T = arguments[2];
|
||||
// 4. If mapfn is undefined, then let mapping be false.
|
||||
var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
|
||||
var T;
|
||||
if (typeof mapFn !== 'undefined') {
|
||||
// 5. else
|
||||
// 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
|
||||
if (!isCallable(mapFn)) {
|
||||
throw new TypeError('Array.from: when provided, the second argument must be a function');
|
||||
}
|
||||
|
||||
// 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
||||
if (arguments.length > 2) {
|
||||
T = arguments[2];
|
||||
}
|
||||
}
|
||||
|
||||
// 10. Let lenValue be Get(items, "length").
|
||||
// 11. Let len be ToLength(lenValue).
|
||||
var len = toLength(items.length);
|
||||
|
||||
// 13. If IsConstructor(C) is true, then
|
||||
// 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len.
|
||||
// 14. a. Else, Let A be ArrayCreate(len).
|
||||
var A = isCallable(C) ? Object(new C(len)) : new Array(len);
|
||||
|
||||
// 16. Let k be 0.
|
||||
var k = 0;
|
||||
// 17. Repeat, while k < len… (also steps a - h)
|
||||
var kValue;
|
||||
while (k < len) {
|
||||
kValue = items[k];
|
||||
if (mapFn) {
|
||||
A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
|
||||
} else {
|
||||
A[k] = kValue;
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
// 18. Let putStatus be Put(A, "length", len, true).
|
||||
A.length = len;
|
||||
// 20. Return A.
|
||||
return A;
|
||||
};
|
||||
}());
|
||||
}
|
||||
|
||||
// Reference: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/find
|
||||
if (!Array.prototype.find) {
|
||||
Array.prototype.find = function(predicate) {
|
||||
'use strict';
|
||||
if (this == null) {
|
||||
throw new TypeError('Array.prototype.find called on null or undefined');
|
||||
}
|
||||
if (typeof predicate !== 'function') {
|
||||
throw new TypeError('predicate must be a function');
|
||||
}
|
||||
var list = Object(this);
|
||||
var length = list.length >>> 0;
|
||||
var thisArg = arguments[1];
|
||||
var value;
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
value = list[i];
|
||||
if (predicate.call(thisArg, value, i, list)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// 10. Let lenValue be Get(items, "length").
|
||||
// 11. Let len be ToLength(lenValue).
|
||||
var len = toLength(items.length);
|
||||
|
||||
// 13. If IsConstructor(C) is true, then
|
||||
// 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len.
|
||||
// 14. a. Else, Let A be ArrayCreate(len).
|
||||
var A = isCallable(C) ? Object(new C(len)) : new Array(len);
|
||||
|
||||
// 16. Let k be 0.
|
||||
var k = 0;
|
||||
// 17. Repeat, while k < len… (also steps a - h)
|
||||
var kValue;
|
||||
while (k < len) {
|
||||
kValue = items[k];
|
||||
if (mapFn) {
|
||||
A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
|
||||
} else {
|
||||
A[k] = kValue;
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
// 18. Let putStatus be Put(A, "length", len, true).
|
||||
A.length = len;
|
||||
// 20. Return A.
|
||||
return A;
|
||||
return undefined;
|
||||
};
|
||||
}());
|
||||
}
|
||||
}
|
||||
|
||||
// Reference: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/find
|
||||
if (!Array.prototype.find) {
|
||||
Array.prototype.find = function(predicate) {
|
||||
'use strict';
|
||||
if (this == null) {
|
||||
throw new TypeError('Array.prototype.find called on null or undefined');
|
||||
}
|
||||
if (typeof predicate !== 'function') {
|
||||
throw new TypeError('predicate must be a function');
|
||||
}
|
||||
var list = Object(this);
|
||||
var length = list.length >>> 0;
|
||||
var thisArg = arguments[1];
|
||||
var value;
|
||||
function CustomEvent (event, params) {
|
||||
params = params || {
|
||||
bubbles: false,
|
||||
cancelable: false,
|
||||
detail: undefined
|
||||
};
|
||||
var evt = document.createEvent('CustomEvent');
|
||||
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
|
||||
return evt;
|
||||
}
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
value = list[i];
|
||||
if (predicate.call(thisArg, value, i, list)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
}
|
||||
CustomEvent.prototype = window.Event.prototype;
|
||||
|
||||
window.CustomEvent = CustomEvent;
|
||||
})();
|
||||
|
|
|
@ -30,7 +30,7 @@ export const isNode = (o) => {
|
|||
return (
|
||||
typeof Node === "object" ? o instanceof Node :
|
||||
o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName === "string"
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -42,7 +42,7 @@ export const isElement = (o) => {
|
|||
return (
|
||||
typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2
|
||||
o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string"
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -58,7 +58,7 @@ export const extend = function() {
|
|||
* Merge one object into another
|
||||
* @param {Object} obj Object to merge into extended object
|
||||
*/
|
||||
let merge = function(obj) {
|
||||
let merge = function(obj) {
|
||||
for (let prop in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
|
||||
// If deep merge and property is an object, merge properties
|
||||
|
@ -91,7 +91,7 @@ export const extend = function() {
|
|||
*/
|
||||
export const whichTransitionEvent = function() {
|
||||
var t,
|
||||
el = document.createElement("fakeelement");
|
||||
el = document.createElement("fakeelement");
|
||||
|
||||
var transitions = {
|
||||
"transition": "transitionend",
|
||||
|
@ -113,7 +113,7 @@ export const whichTransitionEvent = function() {
|
|||
*/
|
||||
export const whichAnimationEvent = function() {
|
||||
var t,
|
||||
el = document.createElement('fakeelement');
|
||||
el = document.createElement('fakeelement');
|
||||
|
||||
var animations = {
|
||||
'animation': 'animationend',
|
||||
|
@ -259,7 +259,7 @@ export const debounce = function(func, wait, immediate) {
|
|||
var timeout;
|
||||
return function() {
|
||||
var context = this,
|
||||
args = arguments;
|
||||
args = arguments;
|
||||
var later = function() {
|
||||
timeout = null;
|
||||
if (!immediate) func.apply(context, args);
|
||||
|
@ -459,6 +459,14 @@ export const getWidthOfInput = (input) => {
|
|||
return `${width}px`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sorting function for current and previous string
|
||||
* @param {String} a Current value
|
||||
* @param {String} b Next value
|
||||
* @return {Number} -1 for after previous,
|
||||
* 1 for before,
|
||||
* 0 for same location
|
||||
*/
|
||||
export const sortByAlpha = (a, b) => {
|
||||
const labelA = (a.label || a.value).toLowerCase();
|
||||
const labelB = (b.label || b.value).toLowerCase();
|
||||
|
@ -468,6 +476,31 @@ export const sortByAlpha = (a, b) => {
|
|||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sort by numeric score
|
||||
* @param {Object} a Current value
|
||||
* @param {Object} b Next value
|
||||
* @return {Number} -1 for after previous,
|
||||
* 1 for before,
|
||||
* 0 for same location
|
||||
*/
|
||||
export const sortByScore = (a, b) => {
|
||||
return a.score - b.score;
|
||||
};
|
||||
|
||||
/**
|
||||
* Trigger native event
|
||||
* @param {NodeElement} element Element to trigger event on
|
||||
* @param {String} type Type of event to trigger
|
||||
* @param {Object} customArgs Data to pass with event
|
||||
* @return {Object} Triggered event
|
||||
*/
|
||||
export const triggerEvent = (element, type, customArgs = null) => {
|
||||
const event = new CustomEvent(type, {
|
||||
detail: customArgs,
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
});
|
||||
|
||||
return element.dispatchEvent(event);
|
||||
};
|
||||
|
|
44
index.html
44
index.html
|
@ -15,7 +15,7 @@
|
|||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<!-- Ignore these -->
|
||||
<link rel="stylesheet" href="assets/styles/css/base.min.css?version=2.5.2">
|
||||
<link rel="stylesheet" href="assets/styles/css/base.min.css?version=2.6.0">
|
||||
<!-- End ignore these -->
|
||||
|
||||
<!-- Optional includes -->
|
||||
|
@ -23,8 +23,8 @@
|
|||
<!-- End optional includes -->
|
||||
|
||||
<!-- Choices includes -->
|
||||
<link rel="stylesheet" href="assets/styles/css/choices.min.css?version=2.5.2">
|
||||
<script src="assets/scripts/dist/choices.min.js?version=2.5.2"></script>
|
||||
<link rel="stylesheet" href="assets/styles/css/choices.min.css?version=2.6.0">
|
||||
<script src="assets/scripts/dist/choices.min.js?version=2.6.0"></script>
|
||||
<!-- End Choices includes -->
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
|
@ -265,12 +265,6 @@
|
|||
editItems: true,
|
||||
maxItemCount: 5,
|
||||
removeItemButton: true,
|
||||
callbackOnHighlightItem: function(id, value) {
|
||||
console.log(value);
|
||||
},
|
||||
callbackOnUnhighlightItem: function(id, value) {
|
||||
console.log(value);
|
||||
},
|
||||
});
|
||||
|
||||
var textUniqueVals = new Choices('#choices-text-unique-values', {
|
||||
|
@ -311,22 +305,12 @@
|
|||
items: ['josh@joshuajohnson.co.uk', { value: 'joe@bloggs.co.uk', label: 'Joe Bloggs' } ],
|
||||
});
|
||||
|
||||
var multipleDefault = new Choices('#choices-multiple-groups', {
|
||||
callbackOnAddItem: function(id, value, groupValue) {
|
||||
console.log(arguments);
|
||||
},
|
||||
callbackOnRemoveItem: function(id, value, groupValue) {
|
||||
console.log(arguments);
|
||||
},
|
||||
});
|
||||
var multipleDefault = new Choices(document.getElementById('choices-multiple-groups'));
|
||||
|
||||
var multipleFetch = new Choices('#choices-multiple-remote-fetch', {
|
||||
placeholder: true,
|
||||
placeholderValue: 'Pick an Strokes record',
|
||||
maxItemCount: 5,
|
||||
callbackOnChange: function(value) {
|
||||
console.log(value)
|
||||
}
|
||||
}).ajax(function(callback) {
|
||||
fetch('https://api.discogs.com/artists/55980/releases?token=QBRmstCkwXEvCjTclCpumbtNwvVkEzGAdELXyRyW')
|
||||
.then(function(response) {
|
||||
|
@ -335,7 +319,7 @@
|
|||
});
|
||||
})
|
||||
.catch(function(error) {
|
||||
// console.error(error);
|
||||
console.error(error);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -355,7 +339,7 @@
|
|||
});
|
||||
})
|
||||
.catch(function(error) {
|
||||
// console.error(error);
|
||||
console.error(error);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -374,7 +358,7 @@
|
|||
callback(data.releases, 'title', 'title');
|
||||
singleXhrRemove.setValueByChoice('How Soon Is Now?');
|
||||
} else {
|
||||
// console.error(status);
|
||||
console.error(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -434,13 +418,13 @@
|
|||
shouldSort: false,
|
||||
});
|
||||
|
||||
var states = new Choices(document.getElementById('states'), {
|
||||
callbackOnChange: function(value) {
|
||||
if(value === 'New York') {
|
||||
boroughs.enable();
|
||||
} else {
|
||||
boroughs.disable();
|
||||
}
|
||||
var states = new Choices(document.getElementById('states'));
|
||||
|
||||
states.passedElement.addEventListener('change', function(e) {
|
||||
if(e.detail.value === 'New York') {
|
||||
boroughs.enable();
|
||||
} else {
|
||||
boroughs.disable();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "choices.js",
|
||||
"version": "2.5.2",
|
||||
"version": "2.6.0",
|
||||
"description": "A vanilla JS customisable text input/select box plugin",
|
||||
"main": "./assets/scripts/dist/choices.min.js",
|
||||
"scripts": {
|
||||
|
|
|
@ -12,7 +12,7 @@ describe('Choices', () => {
|
|||
|
||||
beforeEach(function() {
|
||||
this.input = document.createElement('input');
|
||||
this.input.type = "text";
|
||||
this.input.type = 'text';
|
||||
this.input.className = 'js-choices';
|
||||
|
||||
document.body.appendChild(this.input);
|
||||
|
@ -67,12 +67,7 @@ describe('Choices', () => {
|
|||
expect(this.choices.config.itemSelectText).toEqual(jasmine.any(String));
|
||||
expect(this.choices.config.classNames).toEqual(jasmine.any(Object));
|
||||
expect(this.choices.config.callbackOnInit).toEqual(null);
|
||||
expect(this.choices.config.callbackOnAddItem).toEqual(null);
|
||||
expect(this.choices.config.callbackOnRemoveItem).toEqual(null);
|
||||
expect(this.choices.config.callbackOnHighlightItem).toEqual(null);
|
||||
expect(this.choices.config.callbackOnUnhighlightItem).toEqual(null);
|
||||
expect(this.choices.config.callbackOnChange).toEqual(null);
|
||||
expect(this.choices.config.callbackOnSearch).toEqual(null);
|
||||
expect(this.choices.config.callbackOnCreateTemplates).toEqual(null);
|
||||
});
|
||||
|
||||
it('should expose public methods', function() {
|
||||
|
@ -127,15 +122,15 @@ describe('Choices', () => {
|
|||
expect(this.choices.input).toEqual(jasmine.any(HTMLElement));
|
||||
});
|
||||
|
||||
// it('should create a dropdown', function() {
|
||||
// expect(this.choices.dropdown).toEqual(jasmine.any(HTMLElement));
|
||||
// });
|
||||
it('should create a dropdown', function() {
|
||||
expect(this.choices.dropdown).toEqual(jasmine.any(HTMLElement));
|
||||
});
|
||||
});
|
||||
|
||||
describe('should accept text inputs', function() {
|
||||
beforeEach(function() {
|
||||
this.input = document.createElement('input');
|
||||
this.input.type = "text";
|
||||
this.input.type = 'text';
|
||||
this.input.className = 'js-choices';
|
||||
this.input.placeholder = 'Placeholder text';
|
||||
|
||||
|
@ -301,9 +296,16 @@ describe('Choices', () => {
|
|||
expect(this.choices.currentState.items.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should trigger a change callback on selection', function() {
|
||||
it('should trigger add/change event on selection', function() {
|
||||
this.choices = new Choices(this.input);
|
||||
spyOn(this.choices.config, 'callbackOnChange');
|
||||
|
||||
const changeSpy = jasmine.createSpy('changeSpy');
|
||||
const addSpy = jasmine.createSpy('addSpy');
|
||||
const passedElement = this.choices.passedElement;
|
||||
|
||||
passedElement.addEventListener('change', changeSpy);
|
||||
passedElement.addEventListener('addItem', addSpy);
|
||||
|
||||
this.choices.input.focus();
|
||||
|
||||
// Key down to second choice
|
||||
|
@ -322,7 +324,10 @@ describe('Choices', () => {
|
|||
preventDefault: () => {}
|
||||
});
|
||||
|
||||
expect(this.choices.config.callbackOnChange).toHaveBeenCalledWith(jasmine.any(String));
|
||||
const returnValue = changeSpy.calls.mostRecent().args[0].detail.value;
|
||||
expect(returnValue).toEqual(jasmine.any(String));
|
||||
expect(changeSpy).toHaveBeenCalled();
|
||||
expect(addSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should open the dropdown on click', function() {
|
||||
|
@ -358,6 +363,12 @@ describe('Choices', () => {
|
|||
|
||||
it('should filter choices when searching', function() {
|
||||
this.choices = new Choices(this.input);
|
||||
|
||||
const searchSpy = jasmine.createSpy('searchSpy');
|
||||
const passedElement = this.choices.passedElement;
|
||||
|
||||
passedElement.addEventListener('search', searchSpy);
|
||||
|
||||
this.choices.input.focus();
|
||||
this.choices.input.value = 'Value 3';
|
||||
|
||||
|
@ -371,6 +382,7 @@ describe('Choices', () => {
|
|||
const mostAccurateResult = this.choices.currentState.choices[0];
|
||||
|
||||
expect(this.choices.isSearching && mostAccurateResult.value === 'Value 3').toBeTruthy;
|
||||
expect(searchSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('shouldn\'t sort choices if shouldSort is false', function() {
|
||||
|
@ -701,9 +713,9 @@ describe('Choices', () => {
|
|||
describe('should handle public methods on text input types', function() {
|
||||
beforeEach(function() {
|
||||
this.input = document.createElement('input');
|
||||
this.input.type = "text";
|
||||
this.input.type = 'text';
|
||||
this.input.className = 'js-choices';
|
||||
this.input.value = "Value 1, Value 2, Value 3, Value 4";
|
||||
this.input.value = 'Value 1, Value 2, Value 3, Value 4';
|
||||
|
||||
document.body.appendChild(this.input);
|
||||
this.choices = new Choices(this.input);
|
||||
|
|
Loading…
Reference in a new issue