Version 2.6.0

This commit is contained in:
Josh Johnson 2017-01-01 15:32:09 +00:00
parent b2d93572ae
commit ec0d336072
7 changed files with 329 additions and 285 deletions

115
README.md
View file

@ -18,11 +18,14 @@ With [NPM](https://www.npmjs.com/package/choices.js):
```zsh ```zsh
npm install choices.js --save npm install choices.js --save
``` ```
With [Bower](https://bower.io/): With [Bower](https://bower.io/):
```zsh ```zsh
bower install choices.js --save bower install choices.js --save
``` ```
Or include Choices directly: Or include Choices directly:
```html ```html
<!-- Include base CSS (optional) --> <!-- Include base CSS (optional) -->
<link rel="stylesheet" href="assets/styles/css/base.min.css"> <link rel="stylesheet" href="assets/styles/css/base.min.css">
@ -106,13 +109,7 @@ Or include Choices directly:
loadingState: 'is-loading', loadingState: 'is-loading',
}, },
callbackOnInit: null, callbackOnInit: null,
callbackOnAddItem: null,
callbackOnRemoveItem: null,
callbackOnHighlightItem: null,
callbackOnUnhighlightItem: null,
callbackOnCreateTemplates: null, callbackOnCreateTemplates: null,
callbackOnChange: null,
callbackOnSearch: null,
}); });
``` ```
@ -406,44 +403,6 @@ classNames: {
**Usage:** Function to run once Choices initialises. **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 ### callbackOnCreateTemplates
**Type:** `Function` **Default:** `null` **Arguments:** `template` **Type:** `Function` **Default:** `null` **Arguments:** `template`
@ -477,19 +436,73 @@ const example = new Choices(element, {
}); });
``` ```
### callbackOnChange ## Events
**Type:** `Function` **Default:** `null` **Arguments:** `value` **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` **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 ### removeItem
**Type:** `Function` **Default:** `null` **Arguments:** `value` **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
Methods can be called either directly or by chaining: Methods can be called either directly or by chaining:

View file

@ -23,6 +23,7 @@ import {
getWidthOfInput, getWidthOfInput,
sortByAlpha, sortByAlpha,
sortByScore, sortByScore,
triggerEvent,
} }
from './lib/utils.js'; from './lib/utils.js';
import './lib/polyfills.js'; import './lib/polyfills.js';
@ -105,13 +106,7 @@ class Choices {
loadingState: 'is-loading', loadingState: 'is-loading',
}, },
callbackOnInit: null, callbackOnInit: null,
callbackOnAddItem: null,
callbackOnRemoveItem: null,
callbackOnHighlightItem: null,
callbackOnUnhighlightItem: null,
callbackOnCreateTemplates: null, callbackOnCreateTemplates: null,
callbackOnChange: null,
callbackOnSearch: null,
}; };
// Merge options with user options // Merge options with user options
@ -223,8 +218,6 @@ class Choices {
if (callback) { if (callback) {
if (isType('Function', callback)) { if (isType('Function', callback)) {
callback.call(this); callback.call(this);
} else {
console.error('callbackOnInit: Callback is not a function');
} }
} }
} }
@ -449,24 +442,26 @@ class Choices {
* @return {Object} Class instance * @return {Object} Class instance
* @public * @public
*/ */
highlightItem(item) { highlightItem(item, runEvent = true) {
if (!item) return; if (!item) return;
const id = item.id; const id = item.id;
const groupId = item.groupId; const groupId = item.groupId;
const callback = this.config.callbackOnHighlightItem; const group = groupId >= 0 ? this.store.getGroupById(groupId) : null;
this.store.dispatch(highlightItem(id, true)); this.store.dispatch(highlightItem(id, true));
// Run callback if it is a function if (runEvent) {
if (callback) { if(group && group.value) {
if (isType('Function', callback)) { triggerEvent(this.passedElement, 'highlightItem', {
const group = groupId >= 0 ? this.store.getGroupById(groupId) : null; id,
if(group && group.value) { value: item.value,
callback.call(this, id, item.value, group.value); groupValue: group.value
} else { });
callback.call(this, id, item.value)
}
} else { } 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; if (!item) return;
const id = item.id; const id = item.id;
const groupId = item.groupId; const groupId = item.groupId;
const callback = this.config.callbackOnUnhighlightItem; const group = groupId >= 0 ? this.store.getGroupById(groupId) : null;
this.store.dispatch(highlightItem(id, false)); this.store.dispatch(highlightItem(id, false));
// Run callback if it is a function if(group && group.value) {
if (callback) { triggerEvent(this.passedElement, 'unhighlightItem', {
if (isType('Function', callback)) { id,
const group = groupId >= 0 ? this.store.getGroupById(groupId) : null; value: item.value,
if(group && group.value) { groupValue: group.value
callback.call(this, id, item.value, group.value); });
} else { } else {
callback.call(this, id, item.value) triggerEvent(this.passedElement, 'unhighlightItem', {
} id,
} else { value: item.value,
console.error('callbackOnUnhighlightItem: Callback is not a function'); });
}
} }
return this; return this;
@ -580,15 +574,15 @@ class Choices {
* @return {Object} Class instance * @return {Object} Class instance
* @public * @public
*/ */
removeHighlightedItems(runCallback = false) { removeHighlightedItems(runEvent = false) {
const items = this.store.getItemsFilteredByActive(); const items = this.store.getItemsFilteredByActive();
items.forEach((item) => { items.forEach((item) => {
if (item.highlighted && item.active) { if (item.highlighted && item.active) {
this._removeItem(item); this._removeItem(item);
// If this action was performed by the user // If this action was performed by the user
// run the callback // trigger the event
if (runCallback) { if (runEvent) {
this._triggerChange(item.value); this._triggerChange(item.value);
} }
} }
@ -895,16 +889,10 @@ class Choices {
*/ */
_triggerChange(value) { _triggerChange(value) {
if (!value) return; if (!value) return;
const callback = this.config.callbackOnChange;
// Run callback if it is a function triggerEvent(this.passedElement, 'change', {
if (callback) { value
if (isType('Function', callback)) { });
callback.call(this, value);
} else {
console.error('callbackOnChange: Callback is not a function');
}
}
} }
/** /**
@ -1023,7 +1011,7 @@ class Choices {
this._triggerChange(lastItem.value); this._triggerChange(lastItem.value);
} else { } else {
if (!hasHighlightedItems) { if (!hasHighlightedItems) {
this.highlightItem(lastItem); this.highlightItem(lastItem, false);
} }
this.removeHighlightedItems(true); this.removeHighlightedItems(true);
} }
@ -1173,7 +1161,6 @@ class Choices {
if (!value) return; if (!value) return;
const choices = this.store.getChoices(); const choices = this.store.getChoices();
const hasUnactiveChoices = choices.some((option) => option.active !== true); const hasUnactiveChoices = choices.some((option) => option.active !== true);
const callback = this.config.callbackOnSearch;
// Run callback if it is a function // Run callback if it is a function
if (this.input === document.activeElement) { if (this.input === document.activeElement) {
@ -1181,14 +1168,10 @@ class Choices {
if (value && value.length > this.config.searchFloor) { if (value && value.length > this.config.searchFloor) {
// Filter available choices // Filter available choices
this._searchChoices(value); this._searchChoices(value);
// Run callback if it is a function // Trigger search event
if (callback) { triggerEvent(this.passedElement, 'search', {
if (isType('Function', callback)) { value,
callback.call(this, value); });
} else {
console.error('callbackOnSearch: Callback is not a function');
}
}
} else if (hasUnactiveChoices) { } else if (hasUnactiveChoices) {
// Otherwise reset choices to active // Otherwise reset choices to active
this.isSearching = false; this.isSearching = false;
@ -1853,7 +1836,12 @@ class Choices {
const items = this.store.getItems(); const items = this.store.getItems();
const passedLabel = label || passedValue; const passedLabel = label || passedValue;
const passedOptionId = parseInt(choiceId, 10) || -1; 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 a prepended value has been passed, prepend it
if (this.config.prependValue) { if (this.config.prependValue) {
@ -1865,27 +1853,24 @@ class Choices {
passedValue += this.config.appendValue.toString(); passedValue += this.config.appendValue.toString();
} }
// Generate unique id
const id = items ? items.length + 1 : 1;
this.store.dispatch(addItem(passedValue, passedLabel, id, passedOptionId, groupId)); this.store.dispatch(addItem(passedValue, passedLabel, id, passedOptionId, groupId));
if (this.passedElement.type === 'select-one') { if (this.passedElement.type === 'select-one') {
this.removeActiveItems(id); this.removeActiveItems(id);
} }
// Run callback if it is a function // Trigger change event
if (callback) { if(group && group.value) {
const group = groupId >= 0 ? this.store.getGroupById(groupId) : null; triggerEvent(this.passedElement, 'addItem', {
if (isType('Function', callback)) { id,
if(group && group.value) { value: passedValue,
callback.call(this, id, passedValue, group.value); groupValue: group.value,
} else { });
callback.call(this, id, passedValue); } else {
} triggerEvent(this.passedElement, 'addItem', {
} else { id,
console.error('callbackOnAddItem: Callback is not a function'); value: passedValue,
} });
} }
return this; return this;
@ -1908,22 +1893,21 @@ class Choices {
const value = item.value; const value = item.value;
const choiceId = item.choiceId; const choiceId = item.choiceId;
const groupId = item.groupId; const groupId = item.groupId;
const callback = this.config.callbackOnRemoveItem; const group = groupId >= 0 ? this.store.getGroupById(groupId) : null;
this.store.dispatch(removeItem(id, choiceId)); this.store.dispatch(removeItem(id, choiceId));
// Run callback if(group && group.value) {
if (callback) { triggerEvent(this.passedElement, 'removeItem', {
if (isType('Function', callback)) { id,
const group = groupId >= 0 ? this.store.getGroupById(groupId) : null; value,
if(group && group.value) { groupValue: group.value,
callback.call(this, id, value, group.value); });
} else { } else {
callback.call(this, id, value); triggerEvent(this.passedElement, 'removeItem', {
} id,
} else { value,
console.error('callbackOnRemoveItem: Callback is not a function'); });
}
} }
return this; return this;
@ -2100,6 +2084,7 @@ class Choices {
if (callbackTemplate && isType('Function', callbackTemplate)) { if (callbackTemplate && isType('Function', callbackTemplate)) {
userTemplates = callbackTemplate.call(this, strToEl); userTemplates = callbackTemplate.call(this, strToEl);
} }
this.config.templates = extend(templates, userTemplates); this.config.templates = extend(templates, userTemplates);
} }

View file

@ -1,112 +1,129 @@
/* eslint-disable */ /* eslint-disable */
// Production steps of ECMA-262, Edition 6, 22.1.2.1 (function () {
// Reference: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from // Production steps of ECMA-262, Edition 6, 22.1.2.1
if (!Array.from) { // Reference: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from
Array.from = (function() { if (!Array.from) {
var toStr = Object.prototype.toString; Array.from = (function() {
var toStr = Object.prototype.toString;
var isCallable = function(fn) { var isCallable = function(fn) {
return typeof fn === 'function' || toStr.call(fn) === '[object Function]'; return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
}; };
var toInteger = function(value) { var toInteger = function(value) {
var number = Number(value); var number = Number(value);
if (isNaN(number)) { if (isNaN(number)) {
return 0; return 0;
} }
if (number === 0 || !isFinite(number)) { if (number === 0 || !isFinite(number)) {
return number; return number;
} }
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(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 toLength = function(value) {
var len = toInteger(value); var len = toInteger(value);
return Math.min(Math.max(len, 0), maxSafeInteger); return Math.min(Math.max(len, 0), maxSafeInteger);
}; };
// The length property of the from method is 1. // The length property of the from method is 1.
return function from(arrayLike /*, mapFn, thisArg */ ) { return function from(arrayLike /*, mapFn, thisArg */ ) {
// 1. Let C be the this value. // 1. Let C be the this value.
var C = this; var C = this;
// 2. Let items be ToObject(arrayLike). // 2. Let items be ToObject(arrayLike).
var items = Object(arrayLike); var items = Object(arrayLike);
// 3. ReturnIfAbrupt(items). // 3. ReturnIfAbrupt(items).
if (arrayLike == null) { if (arrayLike == null) {
throw new TypeError("Array.from requires an array-like object - not null or undefined"); 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');
} }
// 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined. // 4. If mapfn is undefined, then let mapping be false.
if (arguments.length > 2) { var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
T = arguments[2]; 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;
} }
} }
return undefined;
// 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 function CustomEvent (event, params) {
if (!Array.prototype.find) { params = params || {
Array.prototype.find = function(predicate) { bubbles: false,
'use strict'; cancelable: false,
if (this == null) { detail: undefined
throw new TypeError('Array.prototype.find called on null or undefined'); };
} var evt = document.createEvent('CustomEvent');
if (typeof predicate !== 'function') { evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
throw new TypeError('predicate must be a function'); return evt;
} }
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) { CustomEvent.prototype = window.Event.prototype;
value = list[i];
if (predicate.call(thisArg, value, i, list)) { window.CustomEvent = CustomEvent;
return value; })();
}
}
return undefined;
};
}

View file

@ -30,7 +30,7 @@ export const isNode = (o) => {
return ( return (
typeof Node === "object" ? o instanceof Node : typeof Node === "object" ? o instanceof Node :
o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName === "string" o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName === "string"
); );
}; };
/** /**
@ -42,7 +42,7 @@ export const isElement = (o) => {
return ( return (
typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2 typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2
o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string" 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 * Merge one object into another
* @param {Object} obj Object to merge into extended object * @param {Object} obj Object to merge into extended object
*/ */
let merge = function(obj) { let merge = function(obj) {
for (let prop in obj) { for (let prop in obj) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) { if (Object.prototype.hasOwnProperty.call(obj, prop)) {
// If deep merge and property is an object, merge properties // If deep merge and property is an object, merge properties
@ -91,7 +91,7 @@ export const extend = function() {
*/ */
export const whichTransitionEvent = function() { export const whichTransitionEvent = function() {
var t, var t,
el = document.createElement("fakeelement"); el = document.createElement("fakeelement");
var transitions = { var transitions = {
"transition": "transitionend", "transition": "transitionend",
@ -113,7 +113,7 @@ export const whichTransitionEvent = function() {
*/ */
export const whichAnimationEvent = function() { export const whichAnimationEvent = function() {
var t, var t,
el = document.createElement('fakeelement'); el = document.createElement('fakeelement');
var animations = { var animations = {
'animation': 'animationend', 'animation': 'animationend',
@ -259,7 +259,7 @@ export const debounce = function(func, wait, immediate) {
var timeout; var timeout;
return function() { return function() {
var context = this, var context = this,
args = arguments; args = arguments;
var later = function() { var later = function() {
timeout = null; timeout = null;
if (!immediate) func.apply(context, args); if (!immediate) func.apply(context, args);
@ -459,6 +459,14 @@ export const getWidthOfInput = (input) => {
return `${width}px`; 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) => { export const sortByAlpha = (a, b) => {
const labelA = (a.label || a.value).toLowerCase(); const labelA = (a.label || a.value).toLowerCase();
const labelB = (b.label || b.value).toLowerCase(); const labelB = (b.label || b.value).toLowerCase();
@ -468,6 +476,31 @@ export const sortByAlpha = (a, b) => {
return 0; 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) => { export const sortByScore = (a, b) => {
return a.score - b.score; 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);
};

View file

@ -15,7 +15,7 @@
<meta name="theme-color" content="#ffffff"> <meta name="theme-color" content="#ffffff">
<!-- Ignore these --> <!-- 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 --> <!-- End ignore these -->
<!-- Optional includes --> <!-- Optional includes -->
@ -23,8 +23,8 @@
<!-- End optional includes --> <!-- End optional includes -->
<!-- Choices includes --> <!-- Choices includes -->
<link rel="stylesheet" href="assets/styles/css/choices.min.css?version=2.5.2"> <link rel="stylesheet" href="assets/styles/css/choices.min.css?version=2.6.0">
<script src="assets/scripts/dist/choices.min.js?version=2.5.2"></script> <script src="assets/scripts/dist/choices.min.js?version=2.6.0"></script>
<!-- End Choices includes --> <!-- End Choices includes -->
<!--[if lt IE 9]> <!--[if lt IE 9]>
@ -265,12 +265,6 @@
editItems: true, editItems: true,
maxItemCount: 5, maxItemCount: 5,
removeItemButton: true, removeItemButton: true,
callbackOnHighlightItem: function(id, value) {
console.log(value);
},
callbackOnUnhighlightItem: function(id, value) {
console.log(value);
},
}); });
var textUniqueVals = new Choices('#choices-text-unique-values', { 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' } ], items: ['josh@joshuajohnson.co.uk', { value: 'joe@bloggs.co.uk', label: 'Joe Bloggs' } ],
}); });
var multipleDefault = new Choices('#choices-multiple-groups', { var multipleDefault = new Choices(document.getElementById('choices-multiple-groups'));
callbackOnAddItem: function(id, value, groupValue) {
console.log(arguments);
},
callbackOnRemoveItem: function(id, value, groupValue) {
console.log(arguments);
},
});
var multipleFetch = new Choices('#choices-multiple-remote-fetch', { var multipleFetch = new Choices('#choices-multiple-remote-fetch', {
placeholder: true, placeholder: true,
placeholderValue: 'Pick an Strokes record', placeholderValue: 'Pick an Strokes record',
maxItemCount: 5, maxItemCount: 5,
callbackOnChange: function(value) {
console.log(value)
}
}).ajax(function(callback) { }).ajax(function(callback) {
fetch('https://api.discogs.com/artists/55980/releases?token=QBRmstCkwXEvCjTclCpumbtNwvVkEzGAdELXyRyW') fetch('https://api.discogs.com/artists/55980/releases?token=QBRmstCkwXEvCjTclCpumbtNwvVkEzGAdELXyRyW')
.then(function(response) { .then(function(response) {
@ -335,7 +319,7 @@
}); });
}) })
.catch(function(error) { .catch(function(error) {
// console.error(error); console.error(error);
}); });
}); });
@ -355,7 +339,7 @@
}); });
}) })
.catch(function(error) { .catch(function(error) {
// console.error(error); console.error(error);
}); });
}); });
@ -374,7 +358,7 @@
callback(data.releases, 'title', 'title'); callback(data.releases, 'title', 'title');
singleXhrRemove.setValueByChoice('How Soon Is Now?'); singleXhrRemove.setValueByChoice('How Soon Is Now?');
} else { } else {
// console.error(status); console.error(status);
} }
} }
} }
@ -434,13 +418,13 @@
shouldSort: false, shouldSort: false,
}); });
var states = new Choices(document.getElementById('states'), { var states = new Choices(document.getElementById('states'));
callbackOnChange: function(value) {
if(value === 'New York') { states.passedElement.addEventListener('change', function(e) {
boroughs.enable(); if(e.detail.value === 'New York') {
} else { boroughs.enable();
boroughs.disable(); } else {
} boroughs.disable();
} }
}); });

View file

@ -1,6 +1,6 @@
{ {
"name": "choices.js", "name": "choices.js",
"version": "2.5.2", "version": "2.6.0",
"description": "A vanilla JS customisable text input/select box plugin", "description": "A vanilla JS customisable text input/select box plugin",
"main": "./assets/scripts/dist/choices.min.js", "main": "./assets/scripts/dist/choices.min.js",
"scripts": { "scripts": {

View file

@ -12,7 +12,7 @@ describe('Choices', () => {
beforeEach(function() { beforeEach(function() {
this.input = document.createElement('input'); this.input = document.createElement('input');
this.input.type = "text"; this.input.type = 'text';
this.input.className = 'js-choices'; this.input.className = 'js-choices';
document.body.appendChild(this.input); document.body.appendChild(this.input);
@ -67,12 +67,7 @@ describe('Choices', () => {
expect(this.choices.config.itemSelectText).toEqual(jasmine.any(String)); expect(this.choices.config.itemSelectText).toEqual(jasmine.any(String));
expect(this.choices.config.classNames).toEqual(jasmine.any(Object)); expect(this.choices.config.classNames).toEqual(jasmine.any(Object));
expect(this.choices.config.callbackOnInit).toEqual(null); expect(this.choices.config.callbackOnInit).toEqual(null);
expect(this.choices.config.callbackOnAddItem).toEqual(null); expect(this.choices.config.callbackOnCreateTemplates).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);
}); });
it('should expose public methods', function() { it('should expose public methods', function() {
@ -127,15 +122,15 @@ describe('Choices', () => {
expect(this.choices.input).toEqual(jasmine.any(HTMLElement)); expect(this.choices.input).toEqual(jasmine.any(HTMLElement));
}); });
// it('should create a dropdown', function() { it('should create a dropdown', function() {
// expect(this.choices.dropdown).toEqual(jasmine.any(HTMLElement)); expect(this.choices.dropdown).toEqual(jasmine.any(HTMLElement));
// }); });
}); });
describe('should accept text inputs', function() { describe('should accept text inputs', function() {
beforeEach(function() { beforeEach(function() {
this.input = document.createElement('input'); this.input = document.createElement('input');
this.input.type = "text"; this.input.type = 'text';
this.input.className = 'js-choices'; this.input.className = 'js-choices';
this.input.placeholder = 'Placeholder text'; this.input.placeholder = 'Placeholder text';
@ -301,9 +296,16 @@ describe('Choices', () => {
expect(this.choices.currentState.items.length).toBe(2); 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); 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(); this.choices.input.focus();
// Key down to second choice // Key down to second choice
@ -322,7 +324,10 @@ describe('Choices', () => {
preventDefault: () => {} 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() { it('should open the dropdown on click', function() {
@ -358,6 +363,12 @@ describe('Choices', () => {
it('should filter choices when searching', function() { it('should filter choices when searching', function() {
this.choices = new Choices(this.input); 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.focus();
this.choices.input.value = 'Value 3'; this.choices.input.value = 'Value 3';
@ -371,6 +382,7 @@ describe('Choices', () => {
const mostAccurateResult = this.choices.currentState.choices[0]; const mostAccurateResult = this.choices.currentState.choices[0];
expect(this.choices.isSearching && mostAccurateResult.value === 'Value 3').toBeTruthy; expect(this.choices.isSearching && mostAccurateResult.value === 'Value 3').toBeTruthy;
expect(searchSpy).toHaveBeenCalled();
}); });
it('shouldn\'t sort choices if shouldSort is false', function() { 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() { describe('should handle public methods on text input types', function() {
beforeEach(function() { beforeEach(function() {
this.input = document.createElement('input'); this.input = document.createElement('input');
this.input.type = "text"; this.input.type = 'text';
this.input.className = 'js-choices'; 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); document.body.appendChild(this.input);
this.choices = new Choices(this.input); this.choices = new Choices(this.input);