mirror of
https://github.com/Choices-js/Choices.git
synced 2024-05-21 15:06:45 +02:00
Terminology updates (options -> choices) && documentation
This commit is contained in:
parent
2fa45b2eee
commit
a2e45209a7
173
README.md
173
README.md
|
@ -1,4 +1,4 @@
|
||||||
# Choices.js - in development
|
# Choices.js - beta
|
||||||
A lightweight, configurable select box/text input plugin. Similar to Select2 and Selectize but without the jQuery dependency.
|
A lightweight, configurable select box/text input plugin. Similar to Select2 and Selectize but without the jQuery dependency.
|
||||||
|
|
||||||
Coming soon.
|
Coming soon.
|
||||||
|
@ -17,35 +17,70 @@ Coming soon.
|
||||||
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 (with default options)
|
||||||
var choices = new Choices(elements, {
|
var choices = new Choices(elements, {
|
||||||
items: [],
|
items: [],
|
||||||
|
maxItemCount: -1,
|
||||||
addItems: true,
|
addItems: true,
|
||||||
removeItems: true,
|
removeItems: true,
|
||||||
removeButton: false,
|
removeItemButton: false,
|
||||||
editItems: false,
|
editItems: false,
|
||||||
maxItems: false,
|
duplicateItems: true,
|
||||||
delimiter: ',',
|
delimiter: ',',
|
||||||
allowDuplicates: true,
|
paste: true,
|
||||||
allowPaste: true,
|
searchOptions: true,
|
||||||
allowSearch: true,
|
regexFilter: null,
|
||||||
regexFilter: false,
|
|
||||||
placeholder: true,
|
placeholder: true,
|
||||||
placeholderValue: '',
|
placeholderValue: null,
|
||||||
prependValue: false,
|
prependValue: null,
|
||||||
appendValue: false,
|
appendValue: null,
|
||||||
highlightAll: true,
|
|
||||||
loadingText: 'Loading...',
|
loadingText: 'Loading...',
|
||||||
|
templates: {},
|
||||||
|
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',
|
||||||
|
},
|
||||||
|
callbackOnInit: () => {},
|
||||||
|
callbackOnAddItem: (id, value, passedInput) => {},
|
||||||
|
callbackOnRemoveItem: (id, value, passedInput) => {},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
To install via NPM, run `npm install --save-dev choices.js`
|
To install via NPM, run `npm install --save-dev choices.js`
|
||||||
|
|
||||||
|
## Terminology
|
||||||
|
| Word | Definition |
|
||||||
|
| ------ | ---------- |
|
||||||
|
| Choice | A choice is a value a user can select. A choice would be equivelant to the `<option></option>` element within a select input. |
|
||||||
|
| Group | A group is a collection of choices. A group should be seen as equivalent to a `<optgroup></optgroup>` element within a select input.|
|
||||||
|
| Item | An item is an inputted value (if you are using Choices with a text input) or a selected choice (if you are using Choices with a select element). |
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
#### items
|
### items
|
||||||
<strong>Type:</strong> `Array` <strong>Default:</strong> `[]`
|
<strong>Type:</strong> <strong>Default:</strong> `[]`
|
||||||
|
|
||||||
<strong>Usage:</strong> Add pre-selected items to input.
|
<strong>Usage:</strong> Add pre-selected items to input.
|
||||||
|
|
||||||
|
@ -68,87 +103,89 @@ Pass an array of objects:
|
||||||
}]
|
}]
|
||||||
```
|
```
|
||||||
|
|
||||||
#### addItems
|
### maxItemCount
|
||||||
|
<strong>Type:</strong> `Number` <strong>Default:</strong>`-1`
|
||||||
|
|
||||||
|
<strong>Usage:</strong> The amount of items a user can input/select ("-1" indicates no limit).
|
||||||
|
|
||||||
|
### addItems
|
||||||
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
||||||
|
|
||||||
<strong>Usage:</strong> Whether a user can add items.
|
<strong>Usage:</strong> Whether a user can add items to the passed input's value.
|
||||||
|
|
||||||
#### removeItems
|
### removeItems
|
||||||
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
||||||
|
|
||||||
<strong>Usage:</strong> Whether a user can remove items (only affects text and multiple select input types).
|
<strong>Usage:</strong> Whether a user can remove items (only affects text and multiple select input types).
|
||||||
|
|
||||||
#### removeButton
|
### removeButton
|
||||||
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`false`
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`false`
|
||||||
|
|
||||||
<strong>Usage:</strong> Whether a button should show that, when clicked, will remove an item (only affects text and multiple select input types).
|
<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
|
||||||
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`false`
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`false`
|
||||||
|
|
||||||
<strong>Usage:</strong> Whether a user can edit selected items (only affects text input types).
|
<strong>Usage:</strong> Whether a user can edit selected items (only affects text input types).
|
||||||
|
|
||||||
#### maxItems
|
<strong>Usage:</strong> Optionally set an item limit (`-1` indicates no limit).
|
||||||
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`null`
|
|
||||||
|
|
||||||
<strong>Usage:</strong> Optionally set an item limit.
|
### delimiter
|
||||||
|
|
||||||
#### delimiter
|
|
||||||
<strong>Type:</strong> `String` <strong>Default:</strong>`,`
|
<strong>Type:</strong> `String` <strong>Default:</strong>`,`
|
||||||
|
|
||||||
<strong>Usage:</strong> What divides each value (only affects text input types).
|
<strong>Usage:</strong> What divides each value (only affects text input types).
|
||||||
|
|
||||||
#### allowDuplicates
|
### allowDuplicates
|
||||||
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
||||||
|
|
||||||
<strong>Usage:</strong> Whether a user can input a duplicate item (only affects text input types).
|
<strong>Usage:</strong> Whether a user can input a duplicate item (only affects text input types).
|
||||||
|
|
||||||
#### allowPaste
|
### allowPaste
|
||||||
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
||||||
|
|
||||||
<strong>Usage:</strong> Whether a user can paste into the input.
|
<strong>Usage:</strong> Whether a user can paste into the input.
|
||||||
|
|
||||||
#### allowSearch
|
### allowSearch
|
||||||
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
||||||
|
|
||||||
<strong>Usage:</strong> Whether a user can filter options by searching (only affects select input types).
|
<strong>Usage:</strong> Whether a user can filter options by searching (only affects select input types).
|
||||||
|
|
||||||
#### regexFilter
|
### regexFilter
|
||||||
<strong>Type:</strong> `Regex` <strong>Default:</strong>`null`
|
<strong>Type:</strong> `Regex` <strong>Default:</strong>`null`
|
||||||
|
|
||||||
<strong>Usage:</strong> A filter that will need to pass for a user to successfully add an item (only affects text input types).
|
<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
|
||||||
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
||||||
|
|
||||||
<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.
|
<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
|
||||||
<strong>Type:</strong> `String` <strong>Default:</strong>`null`
|
<strong>Type:</strong> `String` <strong>Default:</strong>`null`
|
||||||
|
|
||||||
<strong>Usage:</strong> The value of the inputs placeholder.
|
<strong>Usage:</strong> The value of the inputs placeholder.
|
||||||
|
|
||||||
#### prependValue
|
### prependValue
|
||||||
<strong>Type:</strong> `String` <strong>Default:</strong>`null`
|
<strong>Type:</strong> `String` <strong>Default:</strong>`null`
|
||||||
|
|
||||||
<strong>Usage:</strong> Prepend a value to each item added to input (only affects text input types).
|
<strong>Usage:</strong> Prepend a value to each item added to input (only affects text input types).
|
||||||
|
|
||||||
#### appendValue
|
### appendValue
|
||||||
<strong>Type:</strong> `String` <strong>Default:</strong>`null`
|
<strong>Type:</strong> `String` <strong>Default:</strong>`null`
|
||||||
|
|
||||||
<strong>Usage:</strong> Append a value to each item added to input (only affects text input types).
|
<strong>Usage:</strong> Append a value to each item added to input (only affects text input types).
|
||||||
|
|
||||||
#### highlightAll
|
### highlightAll
|
||||||
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
<strong>Type:</strong> `Boolean` <strong>Default:</strong>`true`
|
||||||
|
|
||||||
<strong>Usage:</strong> Whether a user can highlight items.
|
<strong>Usage:</strong> Whether a user can highlight items.
|
||||||
|
|
||||||
#### loadingText
|
### loadingText
|
||||||
<strong>Type:</strong> `String` <strong>Default:</strong>`Loading...`
|
<strong>Type:</strong> `String` <strong>Default:</strong>`Loading...`
|
||||||
|
|
||||||
<strong>Usage:</strong> The loading text that is shown when options are populated via an AJAX callback.
|
<strong>Usage:</strong> The loading text that is shown when options are populated via an AJAX callback.
|
||||||
|
|
||||||
#### classNames
|
### classNames
|
||||||
<strong>Type:</strong> `Object` <strong>Default:</strong>
|
<strong>Type:</strong> `Object` <strong>Default:</strong>
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -181,72 +218,86 @@ classNames: {
|
||||||
|
|
||||||
<strong>Usage:</strong> Classes added to HTML generated by Choices.
|
<strong>Usage:</strong> Classes added to HTML generated by Choices.
|
||||||
|
|
||||||
#### callbackOnInit
|
### callbackOnInit
|
||||||
<strong>Type:</strong> `Function` <strong>Default:</strong>`() => {}`
|
<strong>Type:</strong> `Function` <strong>Default:</strong>`() => {}`
|
||||||
|
|
||||||
<strong>Usage:</strong> Function to run once Choices initialises.
|
<strong>Usage:</strong> Function to run once Choices initialises.
|
||||||
|
|
||||||
#### callbackOnAddItem
|
### callbackOnAddItem
|
||||||
<strong>Type:</strong> `Function` <strong>Default:</strong>`(id, value, passedInput) => {}`
|
<strong>Type:</strong> `Function` <strong>Default:</strong>`(id, value, passedInput) => {}`
|
||||||
|
|
||||||
<strong>Usage:</strong> Function to run each time an item is added.
|
<strong>Usage:</strong> Function to run each time an item is added.
|
||||||
|
|
||||||
#### callbackOnRemoveItem
|
### callbackOnRemoveItem
|
||||||
<strong>Type:</strong> `Function` <strong>Default:</strong>`(id, value, passedInput) => {}`
|
<strong>Type:</strong> `Function` <strong>Default:</strong>`(id, value, passedInput) => {}`
|
||||||
|
|
||||||
<strong>Usage:</strong> Function to run each time an item is removed.
|
<strong>Usage:</strong> Function to run each time an item is removed.
|
||||||
|
|
||||||
|
|
||||||
## Methods
|
## Methods
|
||||||
#### highlightAll();
|
Methods can be called either directly or by chaining:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Calling a method by chaining
|
||||||
|
const choices = new Choices(element, {
|
||||||
|
addItems: false,
|
||||||
|
removeItems: false,
|
||||||
|
}).setValue(['Set value 1', 'Set value 2']).disable();
|
||||||
|
|
||||||
|
// Calling a method directly
|
||||||
|
choices.setValue(['Set value 1', 'Set value 2'])
|
||||||
|
choices.disable();
|
||||||
|
```
|
||||||
|
|
||||||
|
### highlightAll();
|
||||||
<strong>Usage:</strong> Highlight each chosen item (selected items can be removed).
|
<strong>Usage:</strong> Highlight each chosen item (selected items can be removed).
|
||||||
|
|
||||||
|
|
||||||
#### unhighlightAll();
|
### unhighlightAll();
|
||||||
<strong>Usage:</strong> Un-highlight each chosen item.
|
<strong>Usage:</strong> Un-highlight each chosen item.
|
||||||
|
|
||||||
|
|
||||||
#### removeItemsByValue(value);
|
### removeItemsByValue(value);
|
||||||
<strong>Usage:</strong> Remove each item by a given value.
|
<strong>Usage:</strong> Remove each item by a given value.
|
||||||
|
|
||||||
|
|
||||||
#### removeActiveItems(excludedId);
|
### removeActiveItems(excludedId);
|
||||||
<strong>Usage:</strong> Remove each selectable item.
|
<strong>Usage:</strong> Remove each selectable item.
|
||||||
|
|
||||||
|
|
||||||
#### removeSelectedItems();
|
### removeSelectedItems();
|
||||||
<strong>Usage:</strong> Remove each item the user has selected.
|
<strong>Usage:</strong> Remove each item the user has selected.
|
||||||
|
|
||||||
|
|
||||||
#### showDropdown();
|
### showDropdown();
|
||||||
<strong>Usage:</strong> Show option list dropdown.
|
<strong>Usage:</strong> Show option list dropdown (only affects select inputs).
|
||||||
|
|
||||||
|
|
||||||
#### hideDropdown();
|
### hideDropdown();
|
||||||
<strong>Usage:</strong> Hide option list dropdown.
|
<strong>Usage:</strong> Hide option list dropdown (only affects select inputs).
|
||||||
|
|
||||||
|
|
||||||
#### toggleDropdown();
|
### toggleDropdown();
|
||||||
<strong>Usage:</strong> Toggle dropdown between showing/hidden.
|
<strong>Usage:</strong> Toggle dropdown between showing/hidden.
|
||||||
|
|
||||||
|
|
||||||
#### setValue(args);
|
### setValue(args);
|
||||||
<strong>Usage:</strong> Set value of input based on an array of objects or strings.
|
<strong>Usage:</strong> Set value of input based on an array of objects or strings. This behaves exactly the same as passing items via the `items` option but can be called after initialising Choices on an text input (only affects text inputs).
|
||||||
|
|
||||||
|
|
||||||
#### clearValue();
|
### clearValue();
|
||||||
<strong>Usage:</strong> Clear value of input.
|
<strong>Usage:</strong> Clear value of input.
|
||||||
|
|
||||||
|
|
||||||
#### clearInput();
|
### clearInput();
|
||||||
<strong>Usage:</strong> Clear input.
|
<strong>Usage:</strong> Clear input of any user inputted text (only affects text inputs).
|
||||||
|
|
||||||
|
|
||||||
#### disable();
|
### disable();
|
||||||
<strong>Usage:</strong> Disable input from selecting further options.
|
<strong>Usage:</strong> Disable input from selecting further options.
|
||||||
|
|
||||||
|
|
||||||
#### ajax(fn);
|
### ajax(fn);
|
||||||
<strong>Usage:</strong> Populate options via a callback.
|
<strong>Usage:</strong> Populate options via a callback.
|
||||||
|
|
||||||
|
|
||||||
|
@ -259,10 +310,12 @@ To setup a local environment: clone this repo, navigate into it's directory in a
|
||||||
```npm install```
|
```npm install```
|
||||||
|
|
||||||
### NPM tasks
|
### NPM tasks
|
||||||
* ```npm start```
|
| Task | Usage |
|
||||||
* ```npm run js:build```
|
| ------------------- | ------------------------------------------------------------ |
|
||||||
* ```npm run css:watch```
|
| `npm start` | Fire up local server for development |
|
||||||
* ```npm run css:build```
|
| `npm run js:build` | Compile Choices to an uglified JavaScript file |
|
||||||
|
| `npm run css:watch` | Watch SCSS files for changes. On a change, run build process |
|
||||||
|
| `npm run css:build` | Compile, minify and prefix SCSS files to CSS |
|
||||||
|
|
||||||
## 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 npm scripts...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
|
||||||
|
|
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
|
@ -1,18 +1,18 @@
|
||||||
export const addItem = (value, label, id, optionId) => {
|
export const addItem = (value, label, id, choiceId) => {
|
||||||
return {
|
return {
|
||||||
type: 'ADD_ITEM',
|
type: 'ADD_ITEM',
|
||||||
value,
|
value,
|
||||||
label,
|
label,
|
||||||
id,
|
id,
|
||||||
optionId,
|
choiceId,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const removeItem = (id, optionId) => {
|
export const removeItem = (id, choiceId) => {
|
||||||
return {
|
return {
|
||||||
type: 'REMOVE_ITEM',
|
type: 'REMOVE_ITEM',
|
||||||
id,
|
id,
|
||||||
optionId,
|
choiceId,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,9 +24,9 @@ export const selectItem = (id, selected) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const addOption = (value, label, id, groupId, disabled) => {
|
export const addChoice = (value, label, id, groupId, disabled) => {
|
||||||
return {
|
return {
|
||||||
type: 'ADD_OPTION',
|
type: 'ADD_CHOICE',
|
||||||
value,
|
value,
|
||||||
label,
|
label,
|
||||||
id,
|
id,
|
||||||
|
@ -35,14 +35,14 @@ export const addOption = (value, label, id, groupId, disabled) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const filterOptions = (results) => {
|
export const filterChoices = (results) => {
|
||||||
return {
|
return {
|
||||||
type: 'FILTER_OPTIONS',
|
type: 'FILTER_CHOICES',
|
||||||
results,
|
results,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const activateOptions = (active = true) => {
|
export const activateChoices = (active = true) => {
|
||||||
return {
|
return {
|
||||||
type: 'ACTIVATE_OPTIONS',
|
type: 'ACTIVATE_OPTIONS',
|
||||||
active,
|
active,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { addItem, removeItem, selectItem, addOption, filterOptions, activateOptions, addGroup, clearAll } from './actions/index';
|
import { addItem, removeItem, selectItem, addChoice, filterChoices, activateChoices, addGroup, clearAll } from './actions/index';
|
||||||
import { isScrolledIntoView, getAdjacentEl, findAncestor, wrap, isType, strToEl, extend, getWidthOfInput, debounce } from './lib/utils.js';
|
import { isScrolledIntoView, getAdjacentEl, findAncestor, wrap, isType, strToEl, extend, getWidthOfInput, debounce } from './lib/utils.js';
|
||||||
import Fuse from 'fuse.js';
|
import Fuse from 'fuse.js';
|
||||||
import Store from './store/index.js';
|
import Store from './store/index.js';
|
||||||
|
@ -29,21 +29,20 @@ export class Choices {
|
||||||
|
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
items: [],
|
items: [],
|
||||||
|
maxItemCount: -1,
|
||||||
addItems: true,
|
addItems: true,
|
||||||
removeItems: true,
|
removeItems: true,
|
||||||
removeButton: false,
|
removeItemButton: false,
|
||||||
editItems: false,
|
editItems: false,
|
||||||
maxItems: null,
|
duplicateItems: true,
|
||||||
delimiter: ',',
|
delimiter: ',',
|
||||||
allowDuplicates: true,
|
paste: true,
|
||||||
allowPaste: true,
|
searchOptions: true,
|
||||||
allowSearch: true,
|
|
||||||
regexFilter: null,
|
regexFilter: null,
|
||||||
placeholder: true,
|
placeholder: true,
|
||||||
placeholderValue: null,
|
placeholderValue: null,
|
||||||
prependValue: null,
|
prependValue: null,
|
||||||
appendValue: null,
|
appendValue: null,
|
||||||
highlightAll: true,
|
|
||||||
loadingText: 'Loading...',
|
loadingText: 'Loading...',
|
||||||
templates: {},
|
templates: {},
|
||||||
classNames: {
|
classNames: {
|
||||||
|
@ -72,8 +71,8 @@ export class Choices {
|
||||||
selectedState: 'is-selected',
|
selectedState: 'is-selected',
|
||||||
},
|
},
|
||||||
callbackOnInit: () => {},
|
callbackOnInit: () => {},
|
||||||
callbackOnRemoveItem: () => {},
|
callbackOnAddItem: (id, value, passedInput) => {},
|
||||||
callbackOnAddItem: () => {}
|
callbackOnRemoveItem: (id, value, passedInput) => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Merge options with user options
|
// Merge options with user options
|
||||||
|
@ -92,7 +91,7 @@ export class Choices {
|
||||||
this.passedElement = isType('String', element) ? document.querySelector(element) : element;
|
this.passedElement = isType('String', element) ? document.querySelector(element) : element;
|
||||||
|
|
||||||
this.highlightPosition = 0;
|
this.highlightPosition = 0;
|
||||||
this.canSearch = this.options.allowSearch;
|
this.canSearch = this.options.searchOptions;
|
||||||
|
|
||||||
// Assign preset items from passed object first
|
// Assign preset items from passed object first
|
||||||
this.presetItems = this.options.items;
|
this.presetItems = this.options.items;
|
||||||
|
@ -371,13 +370,13 @@ export class Choices {
|
||||||
// If we are dealing with a select input, we need to create an option first
|
// If we are dealing with a select input, we need to create an option first
|
||||||
// that is then selected. For text inputs we can just add items normally.
|
// that is then selected. For text inputs we can just add items normally.
|
||||||
if(this.passedElement.type !== 'text') {
|
if(this.passedElement.type !== 'text') {
|
||||||
this._addOption(true, false, item.value, item.label, -1);
|
this._addChoice(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._addChoice(true, false, item, item, -1);
|
||||||
} else {
|
} else {
|
||||||
this._addItem(item);
|
this._addItem(item);
|
||||||
}
|
}
|
||||||
|
@ -433,7 +432,7 @@ export class Choices {
|
||||||
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._addChoice(false, false, result[value], result[label]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -466,11 +465,11 @@ export class Choices {
|
||||||
let canUpdate = true;
|
let canUpdate = true;
|
||||||
|
|
||||||
if(this.options.addItems) {
|
if(this.options.addItems) {
|
||||||
if (this.options.maxItems && this.options.maxItems <= this.itemList.children.length) {
|
if (this.options.maxItemCount && this.options.maxItemCount > 0 && this.options.maxItemCount <= this.itemList.children.length) {
|
||||||
// If there is a max entry limit and we have reached that limit
|
// If there is a max entry limit and we have reached that limit
|
||||||
// don't update
|
// don't update
|
||||||
canUpdate = false;
|
canUpdate = false;
|
||||||
} else if(this.options.allowDuplicates === false && this.passedElement.value) {
|
} else if(this.options.duplicateItems === false && this.passedElement.value) {
|
||||||
// If no duplicates are allowed, and the value already exists
|
// If no duplicates are allowed, and the value already exists
|
||||||
// in the array, don't update
|
// in the array, don't update
|
||||||
canUpdate = !activeItems.some((item) => item.value === value );
|
canUpdate = !activeItems.some((item) => item.value === value );
|
||||||
|
@ -539,7 +538,7 @@ export class Choices {
|
||||||
const downKey = 40;
|
const downKey = 40;
|
||||||
|
|
||||||
const activeItems = this.store.getItemsFilteredByActive();
|
const activeItems = this.store.getItemsFilteredByActive();
|
||||||
const activeOptions = this.store.getOptionsFilteredByActive();
|
const activeOptions = this.store.getChoicesFilteredByActive();
|
||||||
|
|
||||||
const hasFocusedInput = this.input === document.activeElement;
|
const hasFocusedInput = this.input === document.activeElement;
|
||||||
const hasActiveDropdown = this.dropdown.classList.contains(this.options.classNames.activeState);
|
const hasActiveDropdown = this.dropdown.classList.contains(this.options.classNames.activeState);
|
||||||
|
@ -551,14 +550,14 @@ export class Choices {
|
||||||
this.showDropdown();
|
this.showDropdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.canSearch = this.options.allowSearch;
|
this.canSearch = this.options.searchOptions;
|
||||||
|
|
||||||
switch (e.keyCode) {
|
switch (e.keyCode) {
|
||||||
case aKey:
|
case aKey:
|
||||||
// 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.highlightAll && this.input === document.activeElement) {
|
if(this.options.removeItems && !this.input.value && this.input === document.activeElement) {
|
||||||
// Highlight items
|
// Highlight items
|
||||||
this.highlightAll(this.itemList.children);
|
this.highlightAll(this.itemList.children);
|
||||||
}
|
}
|
||||||
|
@ -584,7 +583,7 @@ export class Choices {
|
||||||
|
|
||||||
if(this.passedElement.type === 'select-one') {
|
if(this.passedElement.type === 'select-one') {
|
||||||
this.isSearching = false;
|
this.isSearching = false;
|
||||||
this.store.dispatch(activateOptions());
|
this.store.dispatch(activateChoices());
|
||||||
this.toggleDropdown();
|
this.toggleDropdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -616,10 +615,10 @@ export class Choices {
|
||||||
if(nextEl) {
|
if(nextEl) {
|
||||||
// We prevent default to stop the cursor moving
|
// We prevent default to stop the cursor moving
|
||||||
// when pressing the arrow
|
// when pressing the arrow
|
||||||
if(!isScrolledIntoView(nextEl, this.optionList, directionInt)) {
|
if(!isScrolledIntoView(nextEl, this.choiceList, directionInt)) {
|
||||||
this._scrollToOption(nextEl, directionInt);
|
this._scrollToChoice(nextEl, directionInt);
|
||||||
}
|
}
|
||||||
this._highlightOption(nextEl);
|
this._highlightChoice(nextEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent default to maintain cursor position whilst
|
// Prevent default to maintain cursor position whilst
|
||||||
|
@ -661,9 +660,9 @@ export class Choices {
|
||||||
const activeItems = this.store.getItemsFilteredByActive();
|
const activeItems = this.store.getItemsFilteredByActive();
|
||||||
const isUnique = !activeItems.some((item) => item.value === this.input.value);
|
const isUnique = !activeItems.some((item) => item.value === this.input.value);
|
||||||
|
|
||||||
if (this.options.maxItems && this.options.maxItems <= this.itemList.children.length) {
|
if (this.options.maxItemCount && this.options.maxItemCount > 0 && this.options.maxItemCount <= this.itemList.children.length) {
|
||||||
dropdownItem = this._getTemplate('notice', `Only ${ this.options.maxItems } options can be added.`);
|
dropdownItem = this._getTemplate('notice', `Only ${ this.options.maxItemCount } options can be added.`);
|
||||||
} else if(!this.options.allowDuplicates && !isUnique) {
|
} else if(!this.options.duplicateItems && !isUnique) {
|
||||||
dropdownItem = this._getTemplate('notice', `Only unique values can be added.`);
|
dropdownItem = this._getTemplate('notice', `Only unique values can be added.`);
|
||||||
} else {
|
} else {
|
||||||
dropdownItem = this._getTemplate('notice', `Add "${ this.input.value }"`);
|
dropdownItem = this._getTemplate('notice', `Add "${ this.input.value }"`);
|
||||||
|
@ -684,7 +683,7 @@ export class Choices {
|
||||||
// If we have enabled text search
|
// If we have enabled text search
|
||||||
if(this.canSearch) {
|
if(this.canSearch) {
|
||||||
if(this.input === document.activeElement) {
|
if(this.input === document.activeElement) {
|
||||||
const options = this.store.getOptions();
|
const options = this.store.getChoices();
|
||||||
const hasUnactiveOptions = options.some((option) => option.active !== true);
|
const hasUnactiveOptions = options.some((option) => option.active !== true);
|
||||||
|
|
||||||
// Check that we have a value to search and the input was an alphanumeric character
|
// Check that we have a value to search and the input was an alphanumeric character
|
||||||
|
@ -694,7 +693,7 @@ export class Choices {
|
||||||
const currentValue = this.currentValue.trim();
|
const currentValue = this.currentValue.trim();
|
||||||
|
|
||||||
if(newValue.length >= 1 && newValue !== currentValue + ' ') {
|
if(newValue.length >= 1 && newValue !== currentValue + ' ') {
|
||||||
const haystack = this.store.getOptionsFiltedBySelectable();
|
const haystack = this.store.getChoicesFiltedBySelectable();
|
||||||
const needle = newValue;
|
const needle = newValue;
|
||||||
const fuse = new Fuse(haystack, {
|
const fuse = new Fuse(haystack, {
|
||||||
keys: ['label', 'value'],
|
keys: ['label', 'value'],
|
||||||
|
@ -706,7 +705,7 @@ export class Choices {
|
||||||
this.currentValue = newValue;
|
this.currentValue = newValue;
|
||||||
this.highlightPosition = 0;
|
this.highlightPosition = 0;
|
||||||
this.isSearching = true;
|
this.isSearching = true;
|
||||||
this.store.dispatch(filterOptions(results));
|
this.store.dispatch(filterChoices(results));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -714,7 +713,7 @@ export class Choices {
|
||||||
} else if(hasUnactiveOptions) {
|
} else if(hasUnactiveOptions) {
|
||||||
// Otherwise reset options to active
|
// Otherwise reset options to active
|
||||||
this.isSearching = false;
|
this.isSearching = false;
|
||||||
this.store.dispatch(activateOptions());
|
this.store.dispatch(activateChoices());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -763,7 +762,7 @@ export class Choices {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(e.target.hasAttribute('data-button')) {
|
if(e.target.hasAttribute('data-button')) {
|
||||||
if(this.options.removeItems && this.options.removeButton) {
|
if(this.options.removeItems && this.options.removeItemButton) {
|
||||||
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);
|
||||||
|
@ -786,7 +785,7 @@ export class Choices {
|
||||||
}
|
}
|
||||||
} else if(e.target.hasAttribute('data-option')) {
|
} else if(e.target.hasAttribute('data-option')) {
|
||||||
// If we are clicking on an option
|
// If we are clicking on an option
|
||||||
const options = this.store.getOptionsFilteredByActive();
|
const options = this.store.getChoicesFilteredByActive();
|
||||||
const id = e.target.getAttribute('data-id');
|
const id = e.target.getAttribute('data-id');
|
||||||
const option = options.find((option) => option.id === parseInt(id));
|
const option = options.find((option) => option.id === parseInt(id));
|
||||||
|
|
||||||
|
@ -795,7 +794,7 @@ export class Choices {
|
||||||
if(this.passedElement.type === 'select-one') {
|
if(this.passedElement.type === 'select-one') {
|
||||||
this.input.value = "";
|
this.input.value = "";
|
||||||
this.isSearching = false;
|
this.isSearching = false;
|
||||||
this.store.dispatch(activateOptions(true));
|
this.store.dispatch(activateChoices(true));
|
||||||
this.toggleDropdown();
|
this.toggleDropdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -828,7 +827,7 @@ export class Choices {
|
||||||
// If the dropdown is either the target or one of its children is the target
|
// If the dropdown is either the target or one of its children is the target
|
||||||
if((e.target === this.dropdown || findAncestor(e.target, this.options.classNames.listDropdown))) {
|
if((e.target === this.dropdown || findAncestor(e.target, this.options.classNames.listDropdown))) {
|
||||||
if(e.target.hasAttribute('data-option')) {
|
if(e.target.hasAttribute('data-option')) {
|
||||||
this._highlightOption(e.target);
|
this._highlightChoice(e.target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -842,7 +841,7 @@ export class Choices {
|
||||||
_onPaste(e) {
|
_onPaste(e) {
|
||||||
if(e.target !== this.input) return;
|
if(e.target !== this.input) return;
|
||||||
// Disable pasting into the input if option has been set
|
// Disable pasting into the input if option has been set
|
||||||
if(!this.options.allowPaste) {
|
if(!this.options.paste) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -899,20 +898,20 @@ export class Choices {
|
||||||
* @return
|
* @return
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_scrollToOption(option, direction) {
|
_scrollToChoice(option, direction) {
|
||||||
if(!option) return;
|
if(!option) return;
|
||||||
|
|
||||||
const dropdownHeight = this.optionList.offsetHeight;
|
const dropdownHeight = this.choiceList.offsetHeight;
|
||||||
const optionHeight = option.offsetHeight;
|
const optionHeight = option.offsetHeight;
|
||||||
|
|
||||||
// Distance from bottom of element to top of parent
|
// Distance from bottom of element to top of parent
|
||||||
const optionPos = option.offsetTop + optionHeight;
|
const choicePos = option.offsetTop + optionHeight;
|
||||||
|
|
||||||
// Scroll position of dropdown
|
// Scroll position of dropdown
|
||||||
const containerScrollPos = this.optionList.scrollTop + dropdownHeight;
|
const containerScrollPos = this.choiceList.scrollTop + dropdownHeight;
|
||||||
|
|
||||||
// Difference between the option and scroll position
|
// Difference between the option and scroll position
|
||||||
let endPoint = direction > 0 ? ((this.optionList.scrollTop + optionPos) - containerScrollPos) : option.offsetTop;
|
let endPoint = direction > 0 ? ((this.choiceList.scrollTop + choicePos) - containerScrollPos) : option.offsetTop;
|
||||||
|
|
||||||
const animateScroll = (time, endPoint, direction) => {
|
const animateScroll = (time, endPoint, direction) => {
|
||||||
let continueAnimation = false;
|
let continueAnimation = false;
|
||||||
|
@ -920,19 +919,19 @@ export class Choices {
|
||||||
const strength = 4;
|
const strength = 4;
|
||||||
|
|
||||||
if(direction > 0) {
|
if(direction > 0) {
|
||||||
easing = (endPoint - this.optionList.scrollTop)/strength;
|
easing = (endPoint - this.choiceList.scrollTop)/strength;
|
||||||
distance = easing > 1 ? easing : 1;
|
distance = easing > 1 ? easing : 1;
|
||||||
|
|
||||||
this.optionList.scrollTop = this.optionList.scrollTop + distance;
|
this.choiceList.scrollTop = this.choiceList.scrollTop + distance;
|
||||||
if(this.optionList.scrollTop < endPoint) {
|
if(this.choiceList.scrollTop < endPoint) {
|
||||||
continueAnimation = true;
|
continueAnimation = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
easing = (this.optionList.scrollTop - endPoint)/strength;
|
easing = (this.choiceList.scrollTop - endPoint)/strength;
|
||||||
distance = easing > 1 ? easing : 1;
|
distance = easing > 1 ? easing : 1;
|
||||||
|
|
||||||
this.optionList.scrollTop = this.optionList.scrollTop - distance;
|
this.choiceList.scrollTop = this.choiceList.scrollTop - distance;
|
||||||
if(this.optionList.scrollTop > endPoint) {
|
if(this.choiceList.scrollTop > endPoint) {
|
||||||
continueAnimation = true;
|
continueAnimation = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -955,11 +954,11 @@ export class Choices {
|
||||||
* @return
|
* @return
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_highlightOption(el) {
|
_highlightChoice(el) {
|
||||||
// Highlight first element in dropdown
|
// Highlight first element in dropdown
|
||||||
const options = Array.from(this.dropdown.querySelectorAll('[data-option-selectable]'));
|
const options = Array.from(this.dropdown.querySelectorAll('[data-option-selectable]'));
|
||||||
|
|
||||||
if(options.length) {
|
if(options && options.length) {
|
||||||
const highlightedOptions = Array.from(this.dropdown.querySelectorAll(`.${this.options.classNames.highlightedState}`));
|
const highlightedOptions = Array.from(this.dropdown.querySelectorAll(`.${this.options.classNames.highlightedState}`));
|
||||||
|
|
||||||
// Remove any highlighted options
|
// Remove any highlighted options
|
||||||
|
@ -996,11 +995,11 @@ export class Choices {
|
||||||
* @return {Object} Class instance
|
* @return {Object} Class instance
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
_addItem(value, label, optionId = -1, callback = this.options.callbackOnAddItem) {
|
_addItem(value, label, choiceId = -1, callback = this.options.callbackOnAddItem) {
|
||||||
const items = this.store.getItems();
|
const items = this.store.getItems();
|
||||||
let passedValue = value.trim();
|
let passedValue = value.trim();
|
||||||
let passedLabel = label || passedValue;
|
let passedLabel = label || passedValue;
|
||||||
let passedOptionId = optionId || -1;
|
let passedOptionId = choiceId || -1;
|
||||||
|
|
||||||
// If a prepended value has been passed, prepend it
|
// If a prepended value has been passed, prepend it
|
||||||
if(this.options.prependValue) {
|
if(this.options.prependValue) {
|
||||||
|
@ -1047,9 +1046,9 @@ export class Choices {
|
||||||
|
|
||||||
const id = item.id;
|
const id = item.id;
|
||||||
const value = item.value;
|
const value = item.value;
|
||||||
const optionId = item.optionId;
|
const choiceId = item.choiceId;
|
||||||
|
|
||||||
this.store.dispatch(removeItem(id, optionId));
|
this.store.dispatch(removeItem(id, choiceId));
|
||||||
|
|
||||||
// Run callback
|
// Run callback
|
||||||
if(callback){
|
if(callback){
|
||||||
|
@ -1061,22 +1060,20 @@ export class Choices {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add option to dropdown
|
* Add choice to dropdoww
|
||||||
* @param {Object} option Option to add
|
|
||||||
* @param {Number} groupId ID of the options group
|
|
||||||
* @return
|
* @return
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_addOption(isSelected, isDisabled, value, label, groupId = -1) {
|
_addChoice(isSelected, isDisabled, value, label, groupId = -1) {
|
||||||
if(!value) return
|
if(!value) return
|
||||||
|
|
||||||
if(!label) { label = value; }
|
if(!label) { label = value; }
|
||||||
|
|
||||||
// Generate unique id
|
// Generate unique id
|
||||||
const options = this.store.getOptions();
|
const choices = this.store.getChoices();
|
||||||
const id = options.length + 1;
|
const id = choices ? choices.length + 1 : 1;
|
||||||
|
|
||||||
this.store.dispatch(addOption(value, label, id, groupId, isDisabled));
|
this.store.dispatch(addChoice(value, label, id, groupId, isDisabled));
|
||||||
|
|
||||||
if(isSelected && !isDisabled) {
|
if(isSelected && !isDisabled) {
|
||||||
this._addItem(value, label, id);
|
this._addItem(value, label, id);
|
||||||
|
@ -1098,7 +1095,7 @@ export class Choices {
|
||||||
this.store.dispatch(addGroup(group.label, groupId, true, group.disabled));
|
this.store.dispatch(addGroup(group.label, groupId, true, group.disabled));
|
||||||
groupOptions.forEach((option, optionIndex) => {
|
groupOptions.forEach((option, optionIndex) => {
|
||||||
const isDisabled = option.disabled || option.parentNode.disabled;
|
const isDisabled = option.disabled || option.parentNode.disabled;
|
||||||
this._addOption(option.selected, isDisabled, option.value, option.innerHTML, groupId);
|
this._addChoice(option.selected, isDisabled, option.value, option.innerHTML, groupId);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.store.dispatch(addGroup(group.label, group.id, false, group.disabled));
|
this.store.dispatch(addGroup(group.label, group.id, false, group.disabled));
|
||||||
|
@ -1135,7 +1132,7 @@ export class Choices {
|
||||||
itemList: () => {
|
itemList: () => {
|
||||||
return strToEl(`<div class="${ classNames.list } ${ this.passedElement.type === 'select-one' ? classNames.listSingle : classNames.listItems }"></div>`);
|
return strToEl(`<div class="${ classNames.list } ${ this.passedElement.type === 'select-one' ? classNames.listSingle : classNames.listItems }"></div>`);
|
||||||
},
|
},
|
||||||
optionList: () => {
|
choiceList: () => {
|
||||||
return strToEl(`<div class="${ classNames.list }"></div>`);
|
return strToEl(`<div class="${ classNames.list }"></div>`);
|
||||||
},
|
},
|
||||||
input: () => {
|
input: () => {
|
||||||
|
@ -1165,7 +1162,7 @@ export class Choices {
|
||||||
`);
|
`);
|
||||||
},
|
},
|
||||||
item: (data) => {
|
item: (data) => {
|
||||||
if(this.options.removeButton && this.passedElement.type !== 'select-one') {
|
if(this.options.removeItemButton && 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 }
|
||||||
|
@ -1194,14 +1191,14 @@ export class Choices {
|
||||||
const containerOuter = this._getTemplate('containerOuter');
|
const containerOuter = this._getTemplate('containerOuter');
|
||||||
const containerInner = this._getTemplate('containerInner');
|
const containerInner = this._getTemplate('containerInner');
|
||||||
const itemList = this._getTemplate('itemList');
|
const itemList = this._getTemplate('itemList');
|
||||||
const optionList = this._getTemplate('optionList');
|
const choiceList = this._getTemplate('choiceList');
|
||||||
const input = this._getTemplate('input');
|
const input = this._getTemplate('input');
|
||||||
const dropdown = this._getTemplate('dropdown');
|
const dropdown = this._getTemplate('dropdown');
|
||||||
|
|
||||||
this.containerOuter = containerOuter;
|
this.containerOuter = containerOuter;
|
||||||
this.containerInner = containerInner;
|
this.containerInner = containerInner;
|
||||||
this.input = input;
|
this.input = input;
|
||||||
this.optionList = optionList;
|
this.choiceList = choiceList;
|
||||||
this.itemList = itemList;
|
this.itemList = itemList;
|
||||||
this.dropdown = dropdown;
|
this.dropdown = dropdown;
|
||||||
|
|
||||||
|
@ -1232,11 +1229,11 @@ export class Choices {
|
||||||
containerOuter.appendChild(containerInner);
|
containerOuter.appendChild(containerInner);
|
||||||
containerOuter.appendChild(dropdown);
|
containerOuter.appendChild(dropdown);
|
||||||
containerInner.appendChild(itemList);
|
containerInner.appendChild(itemList);
|
||||||
dropdown.appendChild(optionList);
|
dropdown.appendChild(choiceList);
|
||||||
|
|
||||||
if(this.passedElement.type === 'select-multiple' || this.passedElement.type === 'text') {
|
if(this.passedElement.type === 'select-multiple' || this.passedElement.type === 'text') {
|
||||||
containerInner.appendChild(input);
|
containerInner.appendChild(input);
|
||||||
} else if(this.options.allowSearch) {
|
} else if(this.options.searchOptions) {
|
||||||
dropdown.insertBefore(input, dropdown.firstChild);
|
dropdown.insertBefore(input, dropdown.firstChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1256,7 +1253,7 @@ export class Choices {
|
||||||
const passedOptions = Array.from(this.passedElement.options);
|
const passedOptions = Array.from(this.passedElement.options);
|
||||||
passedOptions.forEach((option) => {
|
passedOptions.forEach((option) => {
|
||||||
const isDisabled = option.disabled || option.parentNode.disabled;
|
const isDisabled = option.disabled || option.parentNode.disabled;
|
||||||
this._addOption(option.selected, isDisabled, option.value, option.innerHTML);
|
this._addChoice(option.selected, isDisabled, option.value, option.innerHTML);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1391,12 +1388,12 @@ export class Choices {
|
||||||
if(this.passedElement.type === 'select-multiple' || this.passedElement.type === 'select-one') {
|
if(this.passedElement.type === 'select-multiple' || this.passedElement.type === 'select-one') {
|
||||||
// Get active groups/options
|
// Get active groups/options
|
||||||
const activeGroups = this.store.getGroupsFilteredByActive();
|
const activeGroups = this.store.getGroupsFilteredByActive();
|
||||||
const activeOptions = this.store.getOptionsFilteredByActive();
|
const activeOptions = this.store.getChoicesFilteredByActive();
|
||||||
|
|
||||||
let optListFragment = document.createDocumentFragment();
|
let optListFragment = document.createDocumentFragment();
|
||||||
|
|
||||||
// Clear options
|
// Clear options
|
||||||
this.optionList.innerHTML = '';
|
this.choiceList.innerHTML = '';
|
||||||
|
|
||||||
// If we have grouped options
|
// If we have grouped options
|
||||||
if(activeGroups.length >= 1 && this.isSearching !== true) {
|
if(activeGroups.length >= 1 && this.isSearching !== true) {
|
||||||
|
@ -1408,12 +1405,12 @@ export class Choices {
|
||||||
if(optListFragment.children.length) {
|
if(optListFragment.children.length) {
|
||||||
// If we actually have anything to add to our dropdown
|
// If we actually have anything to add to our dropdown
|
||||||
// append it and highlight the first option
|
// append it and highlight the first option
|
||||||
this.optionList.appendChild(optListFragment);
|
this.choiceList.appendChild(optListFragment);
|
||||||
this._highlightOption();
|
this._highlightChoice();
|
||||||
} else {
|
} else {
|
||||||
// Otherwise show a notice
|
// Otherwise show a notice
|
||||||
const dropdownItem = this.isSearching ? this._getTemplate('notice', 'No results found') : this._getTemplate('notice', 'No options to select');
|
const dropdownItem = this.isSearching ? this._getTemplate('notice', 'No results found') : this._getTemplate('notice', 'No options to select');
|
||||||
this.optionList.appendChild(dropdownItem);
|
this.choiceList.appendChild(dropdownItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const options = (state = [], action) => {
|
const choices = (state = [], action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'ADD_OPTION':
|
case 'ADD_CHOICE':
|
||||||
return [...state, {
|
return [...state, {
|
||||||
id: action.id,
|
id: action.id,
|
||||||
groupId: action.groupId,
|
groupId: action.groupId,
|
||||||
|
@ -15,9 +15,9 @@ const options = (state = [], action) => {
|
||||||
case 'ADD_ITEM':
|
case 'ADD_ITEM':
|
||||||
// When an item is added and it has an associated option,
|
// When an item is added and it has an associated option,
|
||||||
// we want to disable it so it can't be chosen again
|
// we want to disable it so it can't be chosen again
|
||||||
if(action.optionId > -1) {
|
if(action.choiceId > -1) {
|
||||||
return state.map((option) => {
|
return state.map((option) => {
|
||||||
if(option.id === parseInt(action.optionId)) {
|
if(option.id === parseInt(action.choiceId)) {
|
||||||
option.selected = true;
|
option.selected = true;
|
||||||
}
|
}
|
||||||
return option;
|
return option;
|
||||||
|
@ -29,9 +29,9 @@ const options = (state = [], action) => {
|
||||||
case 'REMOVE_ITEM':
|
case 'REMOVE_ITEM':
|
||||||
// When an item is removed and it has an associated option,
|
// When an item is removed and it has an associated option,
|
||||||
// we want to re-enable it so it can be chosen again
|
// we want to re-enable it so it can be chosen again
|
||||||
if(action.optionId > -1) {
|
if(action.choiceId > -1) {
|
||||||
return state.map((option) => {
|
return state.map((option) => {
|
||||||
if(option.id === parseInt(action.optionId)) {
|
if(option.id === parseInt(action.choiceId)) {
|
||||||
option.selected = false;
|
option.selected = false;
|
||||||
}
|
}
|
||||||
return option;
|
return option;
|
||||||
|
@ -40,7 +40,7 @@ const options = (state = [], action) => {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'FILTER_OPTIONS':
|
case 'FILTER_CHOICES':
|
||||||
const filteredResults = action.results;
|
const filteredResults = action.results;
|
||||||
const filteredState = state.map((option, index) => {
|
const filteredState = state.map((option, index) => {
|
||||||
// Set active state based on whether option is
|
// Set active state based on whether option is
|
||||||
|
@ -60,7 +60,7 @@ const options = (state = [], action) => {
|
||||||
|
|
||||||
return filteredState;
|
return filteredState;
|
||||||
|
|
||||||
case 'ACTIVATE_OPTIONS':
|
case 'ACTIVATE_CHOICES':
|
||||||
return state.map((option) => {
|
return state.map((option) => {
|
||||||
option.active = action.active;
|
option.active = action.active;
|
||||||
|
|
||||||
|
@ -73,4 +73,4 @@ const options = (state = [], action) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default options;
|
export default choices;
|
|
@ -1,12 +1,12 @@
|
||||||
import { combineReducers } from 'redux';
|
import { combineReducers } from 'redux';
|
||||||
import items from './items';
|
import items from './items';
|
||||||
import groups from './groups';
|
import groups from './groups';
|
||||||
import options from './options';
|
import choices from './choices';
|
||||||
|
|
||||||
const appReducer = combineReducers({
|
const appReducer = combineReducers({
|
||||||
items,
|
items,
|
||||||
groups,
|
groups,
|
||||||
options
|
choices
|
||||||
});
|
});
|
||||||
|
|
||||||
const rootReducer = (state, action) => {
|
const rootReducer = (state, action) => {
|
||||||
|
|
|
@ -4,7 +4,7 @@ const items = (state = [], action) => {
|
||||||
// Add object to items array
|
// Add object to items array
|
||||||
let newState = [...state, {
|
let newState = [...state, {
|
||||||
id: action.id,
|
id: action.id,
|
||||||
optionId: action.optionId,
|
choiceId: action.choiceId,
|
||||||
value: action.value,
|
value: action.value,
|
||||||
label: action.label,
|
label: action.label,
|
||||||
active: true,
|
active: true,
|
||||||
|
|
|
@ -75,35 +75,35 @@ export class Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get options from store
|
* Get choices from store
|
||||||
* @return {Array} Option objects
|
* @return {Array} Option objects
|
||||||
*/
|
*/
|
||||||
getOptions() {
|
getChoices() {
|
||||||
const state = this.store.getState();
|
const state = this.store.getState();
|
||||||
return state.options;
|
return state.choices;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get active options from store
|
* Get active choices from store
|
||||||
* @return {Array} Option objects
|
* @return {Array} Option objects
|
||||||
*/
|
*/
|
||||||
getOptionsFilteredByActive() {
|
getChoicesFilteredByActive() {
|
||||||
const options = this.getOptions();
|
const choices = this.getChoices();
|
||||||
const values = options.filter((option) => {
|
const values = choices.filter((choice) => {
|
||||||
return option.active === true;
|
return choice.active === true;
|
||||||
},[]);
|
},[]);
|
||||||
|
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get selectable options from store
|
* Get selectable choices from store
|
||||||
* @return {Array} Option objects
|
* @return {Array} Option objects
|
||||||
*/
|
*/
|
||||||
getOptionsFiltedBySelectable() {
|
getChoicesFiltedBySelectable() {
|
||||||
const options = this.getOptions();
|
const choices = this.getChoices();
|
||||||
const values = options.filter((option) => {
|
const values = choices.filter((choice) => {
|
||||||
return option.selected === false && option.disabled !== true;
|
return choice.selected === false && choice.disabled !== true;
|
||||||
},[]);
|
},[]);
|
||||||
|
|
||||||
return values;
|
return values;
|
||||||
|
@ -124,11 +124,11 @@ export class Store {
|
||||||
*/
|
*/
|
||||||
getGroupsFilteredByActive() {
|
getGroupsFilteredByActive() {
|
||||||
const groups = this.getGroups();
|
const groups = this.getGroups();
|
||||||
const options = this.getOptions();
|
const choices = this.getChoices();
|
||||||
|
|
||||||
const values = groups.filter((group) => {
|
const values = groups.filter((group) => {
|
||||||
const isActive = group.active === true && group.disabled === false;
|
const isActive = group.active === true && group.disabled === false;
|
||||||
const hasActiveOptions = options.some((option) => {
|
const hasActiveOptions = choices.some((option) => {
|
||||||
return option.active === true && option.disabled === false;
|
return option.active === true && option.disabled === false;
|
||||||
});
|
});
|
||||||
return isActive && hasActiveOptions ? true : false;
|
return isActive && hasActiveOptions ? true : false;
|
||||||
|
|
|
@ -141,13 +141,13 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
const choices2 = new Choices('#choices-2', {
|
const choices2 = new Choices('#choices-2', {
|
||||||
allowPaste: false,
|
paste: false,
|
||||||
allowDuplicates: false,
|
duplicateItems: false,
|
||||||
editItems: true,
|
editItems: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const choices3 = new Choices('#choices-3', {
|
const choices3 = new Choices('#choices-3', {
|
||||||
allowDuplicates: false,
|
duplicates: false,
|
||||||
editItems: true,
|
editItems: true,
|
||||||
regexFilter: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
|
regexFilter: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
|
||||||
});
|
});
|
||||||
|
@ -166,7 +166,7 @@
|
||||||
items: ['josh@joshuajohnson.co.uk', { value: 'joe@bloggs.co.uk', label: 'Joe Bloggs' } ],
|
items: ['josh@joshuajohnson.co.uk', { value: 'joe@bloggs.co.uk', label: 'Joe Bloggs' } ],
|
||||||
});
|
});
|
||||||
|
|
||||||
const choices7 = new Choices('#choices-7', { allowSearch: false }).setValue(['Set value 1', 'Set value 2']);
|
const choices7 = new Choices('#choices-7', { Search: false }).setValue(['Set value 1', 'Set value 2']);
|
||||||
|
|
||||||
const choicesAjax = new Choices('#choices-12').ajax((callback) => {
|
const choicesAjax = new Choices('#choices-12').ajax((callback) => {
|
||||||
fetch('https://api.discogs.com/artists/391170/releases?token=QBRmstCkwXEvCjTclCpumbtNwvVkEzGAdELXyRyW')
|
fetch('https://api.discogs.com/artists/391170/releases?token=QBRmstCkwXEvCjTclCpumbtNwvVkEzGAdELXyRyW')
|
||||||
|
|
Loading…
Reference in a new issue