mirror of
https://github.com/Choices-js/Choices.git
synced 2024-05-21 15:06:45 +02:00
Documentation + minor fixes
This commit is contained in:
parent
c9671c4625
commit
6a9f2cb354
210
README.md
210
README.md
|
@ -7,18 +7,18 @@ Coming soon.
|
||||||
```html
|
```html
|
||||||
<script src="/assets/js/dist/choices.min.js"></script>
|
<script src="/assets/js/dist/choices.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
// Pass multiple elements:
|
// Pass multiple elements:
|
||||||
var choices = new Choices(elements);
|
var choices = new Choices(elements);
|
||||||
|
|
||||||
// Pass single element:
|
// Pass single element:
|
||||||
var choice = new Choices(element);
|
var choice = new Choices(element);
|
||||||
|
|
||||||
// Pass reference
|
// Pass reference
|
||||||
var choice = new Choices('[data-choice']);
|
var choice = new Choices('[data-choice']);
|
||||||
var choice = new Choices('.js-choice');
|
var choice = new Choices('.js-choice');
|
||||||
|
|
||||||
// Passing options
|
// Passing options
|
||||||
var choices = new Choices(elements, {
|
var choices = new Choices(elements, {
|
||||||
items: [],
|
items: [],
|
||||||
addItems: true,
|
addItems: true,
|
||||||
removeItems: true,
|
removeItems: true,
|
||||||
|
@ -45,126 +45,206 @@ To install via NPM, run `npm install --save-dev choices.js`
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
#### items
|
#### items
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `Array` <strong>Default:</strong> `[]`
|
||||||
|
|
||||||
Usage:
|
Usage: Add pre-selected items to input.
|
||||||
|
|
||||||
|
Pass an array of strings:
|
||||||
|
|
||||||
|
`['value 1', 'value 2', 'value 3']`
|
||||||
|
|
||||||
|
Pass an array of objects:
|
||||||
|
|
||||||
|
```
|
||||||
|
[{
|
||||||
|
value: 'Value 1',
|
||||||
|
label: 'Label 1',
|
||||||
|
id: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'Value 2',
|
||||||
|
label: 'Label 2',
|
||||||
|
id: 2
|
||||||
|
}]
|
||||||
|
```
|
||||||
|
|
||||||
#### addItems
|
#### addItems
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> Whether a user can add items.
|
||||||
|
|
||||||
#### removeItems
|
#### removeItems
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> Whether a user can remove items (only affects text and multiple select input types).
|
||||||
|
|
||||||
#### removeButton
|
#### removeButton
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`false`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> Whether a button should show that, when clicked, will remove an item (only affects text and multiple select input types).
|
||||||
|
|
||||||
#### editItems
|
#### editItems
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`false`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> Whether a user can edit selected items (only affects text input types).
|
||||||
|
|
||||||
#### maxItems
|
#### maxItems
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`null`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> Optionally set an item limit.
|
||||||
|
|
||||||
#### delimiter
|
#### delimiter
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `String` <strong>Default:</strong>`,`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> What divides each value (only affects text input types).
|
||||||
|
|
||||||
#### allowDuplicates
|
#### allowDuplicates
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> Whether a user can input a duplicate item (only affects text input types).
|
||||||
|
|
||||||
#### allowPaste
|
#### allowPaste
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> Whether a user can paste into the input.
|
||||||
|
|
||||||
#### allowSearch
|
#### allowSearch
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> Whether a user can filter options by searching (only affects select input types).
|
||||||
|
|
||||||
#### regexFilter
|
#### regexFilter
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `Regex` <strong>Default:</strong>`null`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> A filter that will need to pass for a user to successfully add an item (only affects text input types).
|
||||||
|
|
||||||
#### placeholder
|
#### placeholder
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> Whether the input should show a placeholder. Used in conjunction with `placeholderValue`. If `placeholder` is set to true and no value is passed to `placeholderValue`, the passed input's placeholder attribute will be used as the placeholder value.
|
||||||
|
|
||||||
#### placeholderValue
|
#### placeholderValue
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `String` <strong>Default:</strong>`null`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> The value of the inputs placeholder.
|
||||||
|
|
||||||
#### prependValue
|
#### prependValue
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `String` <strong>Default:</strong>`null`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> Prepend a value to each item added to input (only affects text input types).
|
||||||
|
|
||||||
#### appendValue
|
#### appendValue
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `String` <strong>Default:</strong>`null`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> Append a value to each item added to input (only affects text input types).
|
||||||
|
|
||||||
#### selectAll
|
#### highlightAll
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> Whether a user can highlight items.
|
||||||
|
|
||||||
#### loadingText
|
#### loadingText
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `String` <strong>Default:</strong>`Loading...`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> The loading text that is shown when options are populated via an AJAX callback.
|
||||||
|
|
||||||
#### templates
|
|
||||||
Type: `` Default: ``
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
#### classNames
|
#### classNames
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `Object` <strong>Default:</strong>
|
||||||
|
|
||||||
Usage:
|
```
|
||||||
|
classNames: {
|
||||||
|
containerOuter: 'choices',
|
||||||
|
containerInner: 'choices__inner',
|
||||||
|
input: 'choices__input',
|
||||||
|
inputCloned: 'choices__input--cloned',
|
||||||
|
list: 'choices__list',
|
||||||
|
listItems: 'choices__list--multiple',
|
||||||
|
listSingle: 'choices__list--single',
|
||||||
|
listDropdown: 'choices__list--dropdown',
|
||||||
|
item: 'choices__item',
|
||||||
|
itemSelectable: 'choices__item--selectable',
|
||||||
|
itemDisabled: 'choices__item--disabled',
|
||||||
|
itemOption: 'choices__item--option',
|
||||||
|
group: 'choices__group',
|
||||||
|
groupHeading : 'choices__heading',
|
||||||
|
button: 'choices__button',
|
||||||
|
activeState: 'is-active',
|
||||||
|
focusState: 'is-focused',
|
||||||
|
openState: 'is-open',
|
||||||
|
disabledState: 'is-disabled',
|
||||||
|
highlightedState: 'is-highlighted',
|
||||||
|
hiddenState: 'is-hidden',
|
||||||
|
flippedState: 'is-flipped',
|
||||||
|
selectedState: 'is-selected',
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<strong>Usage:</strong> Classes added to HTML generated by Choices.
|
||||||
|
|
||||||
#### callbackOnInit
|
#### callbackOnInit
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `Function` <strong>Default:</strong>`() => {}`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> Function to run once Choices initialises.
|
||||||
|
|
||||||
#### callbackOnAddItem
|
#### callbackOnAddItem
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `Function` <strong>Default:</strong>`(id, value, passedInput) => {}`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> Function to run each time an item is added.
|
||||||
|
|
||||||
#### callbackOnRemoveItem
|
#### callbackOnRemoveItem
|
||||||
Type: `` Default: ``
|
<strong>Type:</strong> `Function` <strong>Default:</strong>`(id, value, passedInput) => {}`
|
||||||
|
|
||||||
Usage:
|
<strong>Usage:</strong> Function to run each time an item is removed.
|
||||||
|
|
||||||
|
|
||||||
## Methods
|
## Methods
|
||||||
#### method();
|
#### `highlightAll();`
|
||||||
Usage:
|
<strong>Usage:</strong> Highlight each chosen item (selected items can be removed).
|
||||||
|
|
||||||
|
#### `unhighlightAll();`
|
||||||
|
<strong>Usage:</strong> Un-highlight each chosen item.
|
||||||
|
|
||||||
|
#### `removeItemsByValue(value);`
|
||||||
|
<strong>Usage:</strong> Remove each item by a given value.
|
||||||
|
|
||||||
|
#### `removeActiveItems(excludedId);`
|
||||||
|
<strong>Usage:</strong> Remove each selectable item.
|
||||||
|
|
||||||
|
#### `removeSelectedItems();`
|
||||||
|
<strong>Usage:</strong> Remove each item the user has selected.
|
||||||
|
|
||||||
|
#### `showDropdown();`
|
||||||
|
<strong>Usage:</strong> Show option list dropdown
|
||||||
|
|
||||||
|
#### `hideDropdown();`
|
||||||
|
<strong>Usage:</strong> Hide option list dropdown
|
||||||
|
|
||||||
|
#### `toggleDropdown();`
|
||||||
|
<strong>Usage:</strong> Toggle dropdown between showing/hidden.
|
||||||
|
|
||||||
|
#### `setValue(args);`
|
||||||
|
<strong>Usage:</strong> Set value of input based on an array of objects or strings.
|
||||||
|
|
||||||
|
#### `clearValue();`
|
||||||
|
<strong>Usage:</strong> Clear value of input.
|
||||||
|
|
||||||
|
#### `clearInput();`
|
||||||
|
<strong>Usage:</strong> Clear input.
|
||||||
|
|
||||||
|
#### `disable();`
|
||||||
|
<strong>Usage:</strong> Disable input from selecting further options.
|
||||||
|
|
||||||
|
#### `ajax(fn);`
|
||||||
|
<strong>Usage:</strong> Populate options via a callback.
|
||||||
|
|
||||||
|
|
||||||
## Browser compatibility
|
## Browser compatibility
|
||||||
Coming soon
|
ES5 browsers and above (http://caniuse.com/#feat=es5).
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
To setup a local environment: clone this repo, navigate into it's directory in a terminal window and run the following command:
|
To setup a local environment: clone this repo, navigate into it's directory in a terminal window and run the following command:
|
||||||
* ```npm install```
|
|
||||||
|
```npm install```
|
||||||
|
|
||||||
### NPM tasks
|
### NPM tasks
|
||||||
* ```npm start```
|
* ```npm start```
|
||||||
|
@ -172,7 +252,7 @@ To setup a local environment: clone this repo, navigate into it's directory in a
|
||||||
* ```npm run css:watch```
|
* ```npm run css:watch```
|
||||||
|
|
||||||
## Contributions
|
## Contributions
|
||||||
In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using Gulp...bla bla bla
|
In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using npm scripts...bla bla bla
|
||||||
|
|
||||||
## License
|
## License
|
||||||
MIT License
|
MIT License
|
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
|
@ -33,17 +33,17 @@ export class Choices {
|
||||||
removeItems: true,
|
removeItems: true,
|
||||||
removeButton: false,
|
removeButton: false,
|
||||||
editItems: false,
|
editItems: false,
|
||||||
maxItems: false,
|
maxItems: null,
|
||||||
delimiter: ',',
|
delimiter: ',',
|
||||||
allowDuplicates: true,
|
allowDuplicates: true,
|
||||||
allowPaste: true,
|
allowPaste: true,
|
||||||
allowSearch: true,
|
allowSearch: true,
|
||||||
_regexFilter: false,
|
regexFilter: null,
|
||||||
placeholder: true,
|
placeholder: true,
|
||||||
placeholderValue: '',
|
placeholderValue: null,
|
||||||
prependValue: false,
|
prependValue: null,
|
||||||
appendValue: false,
|
appendValue: null,
|
||||||
selectAll: true,
|
highlightAll: true,
|
||||||
loadingText: 'Loading...',
|
loadingText: 'Loading...',
|
||||||
templates: {},
|
templates: {},
|
||||||
classNames: {
|
classNames: {
|
||||||
|
@ -80,7 +80,7 @@ export class Choices {
|
||||||
this.options = extend(defaultOptions, userOptions);
|
this.options = extend(defaultOptions, userOptions);
|
||||||
|
|
||||||
// Create data store
|
// Create data store
|
||||||
this.store = new Store(this._render);
|
this.store = new Store(this.render);
|
||||||
|
|
||||||
// State tracking
|
// State tracking
|
||||||
this.initialised = false;
|
this.initialised = false;
|
||||||
|
@ -103,7 +103,7 @@ export class Choices {
|
||||||
|
|
||||||
// Bind methods
|
// Bind methods
|
||||||
this.init = this.init.bind(this);
|
this.init = this.init.bind(this);
|
||||||
this._render = this._render.bind(this);
|
this.render = this.render.bind(this);
|
||||||
this.destroy = this.destroy.bind(this);
|
this.destroy = this.destroy.bind(this);
|
||||||
this.disable = this.disable.bind(this);
|
this.disable = this.disable.bind(this);
|
||||||
|
|
||||||
|
@ -148,10 +148,10 @@ export class Choices {
|
||||||
// Generate input markup
|
// Generate input markup
|
||||||
this._createInput();
|
this._createInput();
|
||||||
|
|
||||||
this.store.subscribe(this._render);
|
this.store.subscribe(this.render);
|
||||||
|
|
||||||
// Render any items
|
// Render any items
|
||||||
this._render();
|
this.render();
|
||||||
|
|
||||||
// Trigger event listeners
|
// Trigger event listeners
|
||||||
this._addEventListeners();
|
this._addEventListeners();
|
||||||
|
@ -217,11 +217,11 @@ export class Choices {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select items within store
|
* Highlight items within store
|
||||||
* @return {Object} Class instance
|
* @return {Object} Class instance
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
selectAll() {
|
highlightAll() {
|
||||||
const items = this.store.getItems();
|
const items = this.store.getItems();
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
this.selectItem(item);
|
this.selectItem(item);
|
||||||
|
@ -244,76 +244,6 @@ export class Choices {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add item to store with correct value
|
|
||||||
* @param {String} value Value to add to store
|
|
||||||
* @return {Object} Class instance
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
addItem(value, label, optionId = -1, callback = this.options.callbackOnAddItem) {
|
|
||||||
const items = this.store.getItems();
|
|
||||||
let passedValue = value.trim();
|
|
||||||
let passedLabel = label || passedValue;
|
|
||||||
let passedOptionId = optionId || -1;
|
|
||||||
|
|
||||||
// If a prepended value has been passed, prepend it
|
|
||||||
if(this.options.prependValue) {
|
|
||||||
passedValue = this.options.prependValue + passedValue.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If an appended value has been passed, append it
|
|
||||||
if(this.options.appendValue) {
|
|
||||||
passedValue = passedValue + this.options.appendValue.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate unique id
|
|
||||||
const id = items ? items.length + 1 : 1;
|
|
||||||
|
|
||||||
this.store.dispatch(addItem(passedValue, passedLabel, id, passedOptionId));
|
|
||||||
|
|
||||||
if(this.passedElement.type === 'select-one') {
|
|
||||||
this.removeActiveItems(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run callback if it is a function
|
|
||||||
if(callback){
|
|
||||||
if(isType('Function', callback)) {
|
|
||||||
callback(id, passedValue, this.passedElement);
|
|
||||||
} else {
|
|
||||||
console.error('callbackOnAddItem: Callback is not a function');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove item from store
|
|
||||||
* @param
|
|
||||||
* @return {Object} Class instance
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
removeItem(item, callback = this.options.callbackOnRemoveItem) {
|
|
||||||
if(!item || !isType('Object', item)) {
|
|
||||||
console.error('removeItem: No item object was passed to be removed');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const id = item.id;
|
|
||||||
const value = item.value;
|
|
||||||
const optionId = item.optionId;
|
|
||||||
|
|
||||||
this.store.dispatch(removeItem(id, optionId));
|
|
||||||
|
|
||||||
// Run callback
|
|
||||||
if(callback){
|
|
||||||
if(!isType('Function', callback)) console.error('callbackOnRemoveItem: Callback is not a function'); return;
|
|
||||||
callback(id, value, this.passedElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove an item from the store by its value
|
* Remove an item from the store by its value
|
||||||
* @param {String} value Value to search for
|
* @param {String} value Value to search for
|
||||||
|
@ -327,7 +257,7 @@ export class Choices {
|
||||||
|
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
if(item.value === value) {
|
if(item.value === value) {
|
||||||
this.removeItem(item);
|
this._removeItem(item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -337,7 +267,7 @@ export class Choices {
|
||||||
/**
|
/**
|
||||||
* Remove all items from store array
|
* Remove all items from store array
|
||||||
* Note: removed items are soft deleted
|
* Note: removed items are soft deleted
|
||||||
* @param {Boolean} selectedOnly Optionally remove only selected items
|
* @param {Number} excludedId Optionally exclude item by ID
|
||||||
* @return {Object} Class instance
|
* @return {Object} Class instance
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
|
@ -346,7 +276,7 @@ export class Choices {
|
||||||
|
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
if(item.active && excludedId !== item.id) {
|
if(item.active && excludedId !== item.id) {
|
||||||
this.removeItem(item);
|
this._removeItem(item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -364,7 +294,7 @@ export class Choices {
|
||||||
|
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
if(item.selected && item.active) {
|
if(item.selected && item.active) {
|
||||||
this.removeItem(item);
|
this._removeItem(item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -443,13 +373,13 @@ export class Choices {
|
||||||
if(this.passedElement.type !== 'text') {
|
if(this.passedElement.type !== 'text') {
|
||||||
this._addOption(true, false, item.value, item.label, -1);
|
this._addOption(true, false, item.value, item.label, -1);
|
||||||
} else {
|
} else {
|
||||||
this.addItem(item.value, item.label, item.id);
|
this._addItem(item.value, item.label, item.id);
|
||||||
}
|
}
|
||||||
} else if(isType('String', item)) {
|
} else if(isType('String', item)) {
|
||||||
if(this.passedElement.type !== 'text') {
|
if(this.passedElement.type !== 'text') {
|
||||||
this._addOption(true, false, item, item, -1);
|
this._addOption(true, false, item, item, -1);
|
||||||
} else {
|
} else {
|
||||||
this.addItem(item);
|
this._addItem(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -501,7 +431,7 @@ export class Choices {
|
||||||
results.forEach((result, index) => {
|
results.forEach((result, index) => {
|
||||||
// Add each result to option dropdown
|
// Add each result to option dropdown
|
||||||
if(index === 0) {
|
if(index === 0) {
|
||||||
this.addItem(result[value], result[label], index);
|
this._addItem(result[value], result[label], index);
|
||||||
}
|
}
|
||||||
this._addOption(false, false, result[value], result[label]);
|
this._addOption(false, false, result[value], result[label]);
|
||||||
});
|
});
|
||||||
|
@ -553,7 +483,7 @@ export class Choices {
|
||||||
let canAddItem = true;
|
let canAddItem = true;
|
||||||
|
|
||||||
// If a user has supplied a regular expression filter
|
// If a user has supplied a regular expression filter
|
||||||
if(this.options._regexFilter) {
|
if(this.options.regexFilter) {
|
||||||
// Determine whether we can update based on whether
|
// Determine whether we can update based on whether
|
||||||
// our regular expression passes
|
// our regular expression passes
|
||||||
canAddItem = this._regexFilter(value);
|
canAddItem = this._regexFilter(value);
|
||||||
|
@ -562,7 +492,7 @@ export class Choices {
|
||||||
// All is good, add
|
// All is good, add
|
||||||
if(canAddItem) {
|
if(canAddItem) {
|
||||||
this.toggleDropdown();
|
this.toggleDropdown();
|
||||||
this.addItem(value);
|
this._addItem(value);
|
||||||
this.clearInput(this.passedElement);
|
this.clearInput(this.passedElement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -583,7 +513,7 @@ export class Choices {
|
||||||
// we can edit the item value. Otherwise if we can remove items, remove all selected items
|
// we can edit the item value. Otherwise if we can remove items, remove all selected items
|
||||||
if(this.options.editItems && !hasSelectedItems && lastItem) {
|
if(this.options.editItems && !hasSelectedItems && lastItem) {
|
||||||
this.input.value = lastItem.value;
|
this.input.value = lastItem.value;
|
||||||
this.removeItem(lastItem);
|
this._removeItem(lastItem);
|
||||||
} else {
|
} else {
|
||||||
if(!hasSelectedItems) { this.selectItem(lastItem); }
|
if(!hasSelectedItems) { this.selectItem(lastItem); }
|
||||||
this.removeSelectedItems();
|
this.removeSelectedItems();
|
||||||
|
@ -628,8 +558,9 @@ export class Choices {
|
||||||
// If CTRL + A or CMD + A have been pressed and there are items to select
|
// If CTRL + A or CMD + A have been pressed and there are items to select
|
||||||
if(ctrlDownKey && hasItems) {
|
if(ctrlDownKey && hasItems) {
|
||||||
this.canSearch = false;
|
this.canSearch = false;
|
||||||
if(this.options.removeItems && !this.input.value && this.options.selectAll && this.input === document.activeElement) {
|
if(this.options.removeItems && !this.input.value && this.options.highlightAll && this.input === document.activeElement) {
|
||||||
this.selectAll(this.itemList.children);
|
// Highlight items
|
||||||
|
this.highlightAll(this.itemList.children);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -648,7 +579,7 @@ export class Choices {
|
||||||
const value = highlighted.getAttribute('data-value');
|
const value = highlighted.getAttribute('data-value');
|
||||||
const label = highlighted.innerHTML;
|
const label = highlighted.innerHTML;
|
||||||
const id = highlighted.getAttribute('data-id');
|
const id = highlighted.getAttribute('data-id');
|
||||||
this.addItem(value, label, id);
|
this._addItem(value, label, id);
|
||||||
this.clearInput(this.passedElement);
|
this.clearInput(this.passedElement);
|
||||||
|
|
||||||
if(this.passedElement.type === 'select-one') {
|
if(this.passedElement.type === 'select-one') {
|
||||||
|
@ -738,7 +669,7 @@ export class Choices {
|
||||||
dropdownItem = this._getTemplate('notice', `Add "${ this.input.value }"`);
|
dropdownItem = this._getTemplate('notice', `Add "${ this.input.value }"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if((this.options._regexFilter && this._regexFilter(this.input.value)) || !this.options._regexFilter) {
|
if((this.options.regexFilter && this._regexFilter(this.input.value)) || !this.options.regexFilter) {
|
||||||
this.dropdown.innerHTML = dropdownItem.outerHTML;
|
this.dropdown.innerHTML = dropdownItem.outerHTML;
|
||||||
if(!this.dropdown.classList.contains(this.options.classNames.activeState)) {
|
if(!this.dropdown.classList.contains(this.options.classNames.activeState)) {
|
||||||
this.showDropdown();
|
this.showDropdown();
|
||||||
|
@ -835,7 +766,7 @@ export class Choices {
|
||||||
if(this.options.removeItems && this.options.removeButton) {
|
if(this.options.removeItems && this.options.removeButton) {
|
||||||
const itemId = e.target.parentNode.getAttribute('data-id');
|
const itemId = e.target.parentNode.getAttribute('data-id');
|
||||||
const itemToRemove = activeItems.find((item) => item.id === parseInt(itemId));
|
const itemToRemove = activeItems.find((item) => item.id === parseInt(itemId));
|
||||||
this.removeItem(itemToRemove);
|
this._removeItem(itemToRemove);
|
||||||
}
|
}
|
||||||
} else if(e.target.hasAttribute('data-item')) {
|
} else if(e.target.hasAttribute('data-item')) {
|
||||||
// If we are clicking on an item
|
// If we are clicking on an item
|
||||||
|
@ -860,7 +791,7 @@ export class Choices {
|
||||||
const option = options.find((option) => option.id === parseInt(id));
|
const option = options.find((option) => option.id === parseInt(id));
|
||||||
|
|
||||||
if(!option.selected && !option.disabled) {
|
if(!option.selected && !option.disabled) {
|
||||||
this.addItem(option.value, option.label, option.id);
|
this._addItem(option.value, option.label, option.id);
|
||||||
if(this.passedElement.type === 'select-one') {
|
if(this.passedElement.type === 'select-one') {
|
||||||
this.input.value = "";
|
this.input.value = "";
|
||||||
this.isSearching = false;
|
this.isSearching = false;
|
||||||
|
@ -875,8 +806,8 @@ export class Choices {
|
||||||
const hasActiveDropdown = this.dropdown.classList.contains(this.options.classNames.activeState);
|
const hasActiveDropdown = this.dropdown.classList.contains(this.options.classNames.activeState);
|
||||||
const hasSelectedItems = activeItems.some((item) => item.selected === true);
|
const hasSelectedItems = activeItems.some((item) => item.selected === true);
|
||||||
|
|
||||||
// De-select any selected items
|
// De-select any highlighted items
|
||||||
if(hasSelectedItems) this.deselectAll();
|
if(hasSelectedItems) this.unhighlightAll();
|
||||||
|
|
||||||
// Remove focus state
|
// Remove focus state
|
||||||
this.containerOuter.classList.remove(this.options.classNames.focusState);
|
this.containerOuter.classList.remove(this.options.classNames.focusState);
|
||||||
|
@ -957,7 +888,7 @@ export class Choices {
|
||||||
*/
|
*/
|
||||||
_regexFilter(value) {
|
_regexFilter(value) {
|
||||||
if(!value) return;
|
if(!value) return;
|
||||||
const expression = new RegExp(this.options._regexFilter, 'i');
|
const expression = new RegExp(this.options.regexFilter, 'i');
|
||||||
return expression.test(value);
|
return expression.test(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1058,6 +989,77 @@ export class Choices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add item to store with correct value
|
||||||
|
* @param {String} value Value to add to store
|
||||||
|
* @return {Object} Class instance
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
_addItem(value, label, optionId = -1, callback = this.options.callbackOnAddItem) {
|
||||||
|
const items = this.store.getItems();
|
||||||
|
let passedValue = value.trim();
|
||||||
|
let passedLabel = label || passedValue;
|
||||||
|
let passedOptionId = optionId || -1;
|
||||||
|
|
||||||
|
// If a prepended value has been passed, prepend it
|
||||||
|
if(this.options.prependValue) {
|
||||||
|
passedValue = this.options.prependValue + passedValue.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an appended value has been passed, append it
|
||||||
|
if(this.options.appendValue) {
|
||||||
|
passedValue = passedValue + this.options.appendValue.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate unique id
|
||||||
|
const id = items ? items.length + 1 : 1;
|
||||||
|
|
||||||
|
this.store.dispatch(addItem(passedValue, passedLabel, id, passedOptionId));
|
||||||
|
|
||||||
|
if(this.passedElement.type === 'select-one') {
|
||||||
|
this.removeActiveItems(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run callback if it is a function
|
||||||
|
if(callback){
|
||||||
|
if(isType('Function', callback)) {
|
||||||
|
callback(id, passedValue, this.passedElement);
|
||||||
|
} else {
|
||||||
|
console.error('callbackOnAddItem: Callback is not a function');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove item from store
|
||||||
|
* @param
|
||||||
|
* @return {Object} Class instance
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
_removeItem(item, callback = this.options.callbackOnRemoveItem) {
|
||||||
|
if(!item || !isType('Object', item)) {
|
||||||
|
console.error('removeItem: No item object was passed to be removed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = item.id;
|
||||||
|
const value = item.value;
|
||||||
|
const optionId = item.optionId;
|
||||||
|
|
||||||
|
this.store.dispatch(removeItem(id, optionId));
|
||||||
|
|
||||||
|
// Run callback
|
||||||
|
if(callback){
|
||||||
|
if(!isType('Function', callback)) console.error('callbackOnRemoveItem: Callback is not a function'); return;
|
||||||
|
callback(id, value, this.passedElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add option to dropdown
|
* Add option to dropdown
|
||||||
* @param {Object} option Option to add
|
* @param {Object} option Option to add
|
||||||
|
@ -1077,7 +1079,7 @@ export class Choices {
|
||||||
this.store.dispatch(addOption(value, label, id, groupId, isDisabled));
|
this.store.dispatch(addOption(value, label, id, groupId, isDisabled));
|
||||||
|
|
||||||
if(isSelected && !isDisabled) {
|
if(isSelected && !isDisabled) {
|
||||||
this.addItem(value, label, id);
|
this._addItem(value, label, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1163,7 +1165,7 @@ export class Choices {
|
||||||
`);
|
`);
|
||||||
},
|
},
|
||||||
item: (data) => {
|
item: (data) => {
|
||||||
if(this.options.removeButton) {
|
if(this.options.removeButton && this.passedElement.type !== 'select-one') {
|
||||||
return strToEl(`
|
return strToEl(`
|
||||||
<div class="${ classNames.item } ${ data.selected ? classNames.selectedState : ''} ${ !data.disabled ? classNames.itemSelectable : '' }" data-item data-id="${ data.id }" data-value="${ data.value }" data-deletable>
|
<div class="${ classNames.item } ${ data.selected ? classNames.selectedState : ''} ${ !data.disabled ? classNames.itemSelectable : '' }" data-item data-id="${ data.id }" data-value="${ data.value }" data-deletable>
|
||||||
${ data.label }
|
${ data.label }
|
||||||
|
@ -1263,9 +1265,9 @@ export class Choices {
|
||||||
this.presetItems.forEach((item) => {
|
this.presetItems.forEach((item) => {
|
||||||
if(isType('Object', item)) {
|
if(isType('Object', item)) {
|
||||||
if(!item.value) return;
|
if(!item.value) return;
|
||||||
this.addItem(item.value, item.label, item.id);
|
this._addItem(item.value, item.label, item.id);
|
||||||
} else if(isType('String', item)) {
|
} else if(isType('String', item)) {
|
||||||
this.addItem(item);
|
this._addItem(item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1279,7 +1281,7 @@ export class Choices {
|
||||||
* @return {DocumentFragment} Populated options fragment
|
* @return {DocumentFragment} Populated options fragment
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_renderGroups(groups, options, fragment) {
|
renderGroups(groups, options, fragment) {
|
||||||
const groupFragment = fragment || document.createDocumentFragment();
|
const groupFragment = fragment || document.createDocumentFragment();
|
||||||
|
|
||||||
groups.forEach((group, i) => {
|
groups.forEach((group, i) => {
|
||||||
|
@ -1297,7 +1299,7 @@ export class Choices {
|
||||||
|
|
||||||
groupFragment.appendChild(dropdownGroup);
|
groupFragment.appendChild(dropdownGroup);
|
||||||
|
|
||||||
this._renderOptions(groupOptions, groupFragment);
|
this.renderOptions(groupOptions, groupFragment);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1311,7 +1313,7 @@ export class Choices {
|
||||||
* @return {DocumentFragment} Populated options fragment
|
* @return {DocumentFragment} Populated options fragment
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_renderOptions(options, fragment) {
|
renderOptions(options, fragment) {
|
||||||
// Create a fragment to store our list items (so we don't have to update the DOM for each item)
|
// Create a fragment to store our list items (so we don't have to update the DOM for each item)
|
||||||
const optsFragment = fragment || document.createDocumentFragment();
|
const optsFragment = fragment || document.createDocumentFragment();
|
||||||
|
|
||||||
|
@ -1335,7 +1337,7 @@ export class Choices {
|
||||||
* @return
|
* @return
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_renderItems(items, fragment) {
|
renderItems(items, fragment) {
|
||||||
// Create fragment to add elements to
|
// Create fragment to add elements to
|
||||||
const itemListFragment = fragment || document.createDocumentFragment();
|
const itemListFragment = fragment || document.createDocumentFragment();
|
||||||
// Simplify store data to just values
|
// Simplify store data to just values
|
||||||
|
@ -1378,10 +1380,10 @@ export class Choices {
|
||||||
* @return
|
* @return
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_render() {
|
render() {
|
||||||
this.currentState = this.store.getState();
|
this.currentState = this.store.getState();
|
||||||
|
|
||||||
// Only _render if our state has actually changed
|
// Only render if our state has actually changed
|
||||||
if(this.currentState !== this.prevState) {
|
if(this.currentState !== this.prevState) {
|
||||||
|
|
||||||
// Options
|
// Options
|
||||||
|
@ -1398,9 +1400,9 @@ export class Choices {
|
||||||
|
|
||||||
// If we have grouped options
|
// If we have grouped options
|
||||||
if(activeGroups.length >= 1 && this.isSearching !== true) {
|
if(activeGroups.length >= 1 && this.isSearching !== true) {
|
||||||
optListFragment = this._renderGroups(activeGroups, activeOptions, optListFragment);
|
optListFragment = this.renderGroups(activeGroups, activeOptions, optListFragment);
|
||||||
} else if(activeOptions.length >= 1) {
|
} else if(activeOptions.length >= 1) {
|
||||||
optListFragment = this._renderOptions(activeOptions, optListFragment);
|
optListFragment = this.renderOptions(activeOptions, optListFragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(optListFragment.children.length) {
|
if(optListFragment.children.length) {
|
||||||
|
@ -1421,7 +1423,7 @@ export class Choices {
|
||||||
const activeItems = this.store.getItemsFilteredByActive();
|
const activeItems = this.store.getItemsFilteredByActive();
|
||||||
if(activeItems) {
|
if(activeItems) {
|
||||||
// Create a fragment to store our list items (so we don't have to update the DOM for each item)
|
// Create a fragment to store our list items (so we don't have to update the DOM for each item)
|
||||||
const itemListFragment = this._renderItems(activeItems);
|
const itemListFragment = this.renderItems(activeItems);
|
||||||
|
|
||||||
// Clear list
|
// Clear list
|
||||||
this.itemList.innerHTML = '';
|
this.itemList.innerHTML = '';
|
||||||
|
|
|
@ -182,6 +182,7 @@
|
||||||
|
|
||||||
const choicesMultiple = new Choices('[data-choice]', {
|
const choicesMultiple = new Choices('[data-choice]', {
|
||||||
placeholderValue: 'This is a placeholder set in the config',
|
placeholderValue: 'This is a placeholder set in the config',
|
||||||
|
removeButton: true
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue