This commit is contained in:
Konstantin Vyatkin 2019-10-28 15:33:27 -04:00 committed by Josh Johnson
parent dbd15d7823
commit 64407174e3
2 changed files with 248 additions and 125 deletions

335
README.md
View file

@ -1,29 +1,36 @@
# Choices.js [![Actions Status](https://github.com/jshjohnson/Choices/workflows/Unit%20Tests/badge.svg)](https://github.com/jshjohnson/Choices/actions) [![npm](https://img.shields.io/npm/v/choices.js.svg)](https://www.npmjs.com/package/choices.js) [![codebeat badge](https://codebeat.co/badges/55120150-5866-42d8-8010-6aaaff5d3fa1)](https://codebeat.co/projects/github-com-jshjohnson-choices-master)
A vanilla, lightweight (~19kb gzipped 🎉), configurable select box/text input plugin. Similar to Select2 and Selectize but without the jQuery dependency.
[Demo](https://joshuajohnson.co.uk/Choices/)
## TL;DR
* Lightweight
* No jQuery dependency
* Configurable sorting
* Flexible styling
* Fast search/filtering
* Clean API
* Right-to-left support
* Custom templates
----
- Lightweight
- No jQuery dependency
- Configurable sorting
- Flexible styling
- Fast search/filtering
- Clean API
- Right-to-left support
- Custom templates
---
### Interested in writing your own ES6 JavaScript plugins? Check out [ES6.io](https://ES6.io/friend/JOHNSON) for great tutorials! 💪🏼
----
---
## Installation
With [NPM](https://www.npmjs.com/package/choices.js):
```zsh
npm install choices.js
```
With [Yarn](https://yarnpkg.com/):
```zsh
yarn add choices.js
```
@ -34,9 +41,15 @@ From a [CDN](https://www.jsdelivr.com/package/npm/choices.js):
```html
<!-- Include base CSS (optional) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/base.min.css">
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/base.min.css"
/>
<!-- Include Choices CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css">
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css"
/>
<!-- Include Choices JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.min.js"></script>
```
@ -45,12 +58,13 @@ Or include Choices directly:
```html
<!-- Include base CSS (optional) -->
<link rel="stylesheet" href="public/assets/styles/base.min.css">
<link rel="stylesheet" href="public/assets/styles/base.min.css" />
<!-- Include Choices CSS -->
<link rel="stylesheet" href="public/assets/styles/choices.min.css">
<link rel="stylesheet" href="public/assets/styles/choices.min.css" />
<!-- Include Choices JavaScript -->
<script src="/public/assets/scripts/choices.min.js"></script>
```
## Setup
If you pass a selector which targets multiple elements, an array of Choices instances
@ -152,24 +166,26 @@ will be returned. If you target one element, that instance will be returned.
```
## Terminology
| Word | Definition |
| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Choice | A choice is a value a user can select. A choice would be equivalent 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 (text input) or a selected choice (select element). In the context of a select element, an item is equivalent to a selected option element: `<option value="Hello" selected></option>` whereas in the context of a text input an item is equivalent to `<input type="text" value="Hello">` |
## Configuration options
### silent
**Type:** `Boolean` **Default:** `false`
**Type:** `Boolean` **Default:** `false`
**Input types affected:** `text`, `select-single`, `select-multiple`
**Usage:** Optionally suppress console errors and warnings.
### items
**Type:** `Array` **Default:** `[]`
**Type:** `Array` **Default:** `[]`
**Input types affected:** `text`
@ -198,7 +214,8 @@ Pass an array of objects:
```
### choices
**Type:** `Array` **Default:** `[]`
**Type:** `Array` **Default:** `[]`
**Input types affected:** `select-one`, `select-multiple`
@ -226,6 +243,7 @@ Pass an array of objects:
```
### renderChoiceLimit
**Type:** `Number` **Default:** `-1`
**Input types affected:** `select-one`, `select-multiple`
@ -233,6 +251,7 @@ Pass an array of objects:
**Usage:** The amount of choices to be rendered within the dropdown list ("-1" indicates no limit). This is useful if you have a lot of choices where it is easier for a user to use the search area to find a choice.
### maxItemCount
**Type:** `Number` **Default:** `-1`
**Input types affected:** `text`, `select-multiple`
@ -240,6 +259,7 @@ Pass an array of objects:
**Usage:** The amount of items a user can input/select ("-1" indicates no limit).
### addItems
**Type:** `Boolean` **Default:** `true`
**Input types affected:** `text`
@ -247,6 +267,7 @@ Pass an array of objects:
**Usage:** Whether a user can add items.
### removeItems
**Type:** `Boolean` **Default:** `true`
**Input types affected:** `text`, `select-multiple`
@ -254,6 +275,7 @@ Pass an array of objects:
**Usage:** Whether a user can remove items.
### removeItemButton
**Type:** `Boolean` **Default:** `false`
**Input types affected:** `text`, `select-one`, `select-multiple`
@ -261,6 +283,7 @@ Pass an array of objects:
**Usage:** Whether each item should have a remove button.
### editItems
**Type:** `Boolean` **Default:** `false`
**Input types affected:** `text`
@ -268,6 +291,7 @@ Pass an array of objects:
**Usage:** Whether a user can edit items. An item's value can be edited by pressing the backspace.
### duplicateItemsAllowed
**Type:** `Boolean` **Default:** `true`
**Input types affected:** `text`, `select-multiple`
@ -275,6 +299,7 @@ Pass an array of objects:
**Usage:** Whether duplicate inputted/chosen items are allowed
### delimiter
**Type:** `String` **Default:** `,`
**Input types affected:** `text`
@ -282,6 +307,7 @@ Pass an array of objects:
**Usage:** What divides each value. The default delimiter seperates each value with a comma: `"Value 1, Value 2, Value 3"`.
### paste
**Type:** `Boolean` **Default:** `true`
**Input types affected:** `text`, `select-multiple`
@ -289,21 +315,23 @@ Pass an array of objects:
**Usage:** Whether a user can paste into the input.
### searchEnabled
**Type:** `Boolean` **Default:** `true`
**Input types affected:** `select-one`
**Usage:** Whether a search area should be shown. **Note:** Multiple select boxes will *always* show search areas.
**Usage:** Whether a search area should be shown. **Note:** Multiple select boxes will _always_ show search areas.
### searchChoices
**Type:** `Boolean` **Default:** `true`
**Input types affected:** `select-one`
**Usage:** Whether choices should be filtered by input or not. If `false`, the search event will still emit, but choices will not be filtered.
### searchFields
**Type:** `Array/String` **Default:** `['label', 'value']`
**Input types affected:**`select-one`, `select-multiple`
@ -311,6 +339,7 @@ Pass an array of objects:
**Usage:** Specify which fields should be used when a user is searching. If you have added custom properties to your choices, you can add these values thus: `['label', 'value', 'customProperties.example']`.
### searchFloor
**Type:** `Number` **Default:** `1`
**Input types affected:** `select-one`, `select-multiple`
@ -318,6 +347,7 @@ Pass an array of objects:
**Usage:** The minimum length a search value should be before choices are searched.
### searchResultLimit: 4,
**Type:** `Number` **Default:** `4`
**Input types affected:** `select-one`, `select-multiple`
@ -325,6 +355,7 @@ Pass an array of objects:
**Usage:** The maximum amount of search results to show.
### position
**Type:** `String` **Default:** `auto`
**Input types affected:** `select-one`, `select-multiple`
@ -332,6 +363,7 @@ Pass an array of objects:
**Usage:** Whether the dropdown should appear above (`top`) or below (`bottom`) the input. By default, if there is not enough space within the window the dropdown will appear above the input, otherwise below it.
### resetScrollPosition
**Type:** `Boolean` **Default:** `true`
**Input types affected:** `select-multiple`
@ -339,6 +371,7 @@ Pass an array of objects:
**Usage:** Whether the scroll position should reset after adding an item.
### addItemFilterFn
**Type:** `Function` **Default:** `null`
**Input types affected:** `text`
@ -357,6 +390,7 @@ new Choices(element, {
```
### shouldSort
**Type:** `Boolean` **Default:** `true`
**Input types affected:** `select-one`, `select-multiple`
@ -364,6 +398,7 @@ new Choices(element, {
**Usage:** Whether choices and groups should be sorted. If false, choices/groups will appear in the order they were given.
### shouldSortItems
**Type:** `Boolean` **Default:** `false`
**Input types affected:** `text`, `select-multiple`
@ -371,6 +406,7 @@ new Choices(element, {
**Usage:** Whether items should be sorted. If false, items will appear in the order they were selected.
### sortFn
**Type:** `Function` **Default:** sortByAlpha
**Input types affected:** `select-one`, `select-multiple`
@ -389,11 +425,12 @@ const example = new Choices(element, {
```
### placeholder
**Type:** `Boolean` **Default:** `true`
**Input types affected:** `text`, `select-multiple`
**Usage:** 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.
**Usage:** 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.
**Note:** For single select boxes, the recommended way of adding a placeholder is as follows:
@ -409,6 +446,7 @@ const example = new Choices(element, {
For backward compatibility, `<option placeholder>This is a placeholder</option>` is also supported.
### placeholderValue
**Type:** `String` **Default:** `null`
**Input types affected:** `text`, `select-multiple`
@ -416,6 +454,7 @@ For backward compatibility, `<option placeholder>This is a placeholder</option>`
**Usage:** The value of the inputs placeholder.
### searchPlaceholderValue
**Type:** `String` **Default:** `null`
**Input types affected:** `select-one`
@ -423,6 +462,7 @@ For backward compatibility, `<option placeholder>This is a placeholder</option>`
**Usage:** The value of the search inputs placeholder.
### prependValue
**Type:** `String` **Default:** `null`
**Input types affected:** `text`, `select-one`, `select-multiple`
@ -430,6 +470,7 @@ For backward compatibility, `<option placeholder>This is a placeholder</option>`
**Usage:** Prepend a value to each item added/selected.
### appendValue
**Type:** `String` **Default:** `null`
**Input types affected:** `text`, `select-one`, `select-multiple`
@ -437,6 +478,7 @@ For backward compatibility, `<option placeholder>This is a placeholder</option>`
**Usage:** Append a value to each item added/selected.
### renderSelectedChoices
**Type:** `String` **Default:** `auto`
**Input types affected:** `select-multiple`
@ -444,6 +486,7 @@ For backward compatibility, `<option placeholder>This is a placeholder</option>`
**Usage:** Whether selected choices should be removed from the list. By default choices are removed when they are selected in multiple select box. To always render choices pass `always`.
### loadingText
**Type:** `String` **Default:** `Loading...`
**Input types affected:** `select-one`, `select-multiple`
@ -451,6 +494,7 @@ For backward compatibility, `<option placeholder>This is a placeholder</option>`
**Usage:** The text that is shown whilst choices are being populated via AJAX.
### noResultsText
**Type:** `String/Function` **Default:** `No results found`
**Input types affected:** `select-one`, `select-multiple`
@ -458,6 +502,7 @@ For backward compatibility, `<option placeholder>This is a placeholder</option>`
**Usage:** The text that is shown when a user's search has returned no results. Optionally pass a function returning a string.
### noChoicesText
**Type:** `String/Function` **Default:** `No choices to choose from`
**Input types affected:** `select-multiple`
@ -465,6 +510,7 @@ For backward compatibility, `<option placeholder>This is a placeholder</option>`
**Usage:** The text that is shown when a user has selected all possible choices. Optionally pass a function returning a string.
### itemSelectText
**Type:** `String` **Default:** `Press to select`
**Input types affected:** `select-multiple`, `select-one`
@ -472,6 +518,7 @@ For backward compatibility, `<option placeholder>This is a placeholder</option>`
**Usage:** The text that is shown when a user hovers over a selectable choice.
### addItemText
**Type:** `String/Function` **Default:** `Press Enter to add "${value}"`
**Input types affected:** `text`
@ -479,6 +526,7 @@ For backward compatibility, `<option placeholder>This is a placeholder</option>`
**Usage:** The text that is shown when a user has inputted a new item but has not pressed the enter key. To access the current input value, pass a function with a `value` argument (see the [default config](https://github.com/jshjohnson/Choices#setup) for an example), otherwise pass a string.
### maxItemText
**Type:** `String/Function` **Default:** `Only ${maxItemCount} values can be added`
**Input types affected:** `text`
@ -486,6 +534,7 @@ For backward compatibility, `<option placeholder>This is a placeholder</option>`
**Usage:** The text that is shown when a user has focus on the input but has already reached the [max item count](https://github.com/jshjohnson/Choices#maxitemcount). To access the max item count, pass a function with a `maxItemCount` argument (see the [default config](https://github.com/jshjohnson/Choices#setup) for an example), otherwise pass a string.
### itemComparer
**Type:** `Function` **Default:** `strict equality`
**Input types affected:** `select-one`, `select-multiple`
@ -493,6 +542,7 @@ For backward compatibility, `<option placeholder>This is a placeholder</option>`
**Usage:** Compare choice and value in appropriate way (e.g. deep equality for objects). To compare choice and value, pass a function with a `itemComparer` argument (see the [default config](https://github.com/jshjohnson/Choices#setup) for an example).
### classNames
**Type:** `Object` **Default:**
```
@ -527,9 +577,11 @@ classNames: {
**Usage:** Classes added to HTML generated by Choices. By default classnames follow the [BEM](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/) notation.
## Callbacks
**Note:** For each callback, `this` refers to the current instance of Choices. This can be useful if you need access to methods (`this.disable()`) or the config object (`this.config`).
### callbackOnInit
**Type:** `Function` **Default:** `null`
**Input types affected:** `text`, `select-one`, `select-multiple`
@ -537,6 +589,7 @@ classNames: {
**Usage:** Function to run once Choices initialises.
### callbackOnCreateTemplates
**Type:** `Function` **Default:** `null` **Arguments:** `template`
**Input types affected:** `text`, `select-one`, `select-multiple`
@ -547,28 +600,45 @@ classNames: {
```js
const example = new Choices(element, {
callbackOnCreateTemplates: function (template) {
callbackOnCreateTemplates: function(template) {
return {
item: (classNames, data) => {
return template(`
<div class="${classNames.item} ${data.highlighted ? classNames.highlightedState : classNames.itemSelectable} ${data.placeholder ? classNames.placeholder : ''}" data-item data-id="${data.id}" data-value="${data.value}" ${data.active ? 'aria-selected="true"' : ''} ${data.disabled ? 'aria-disabled="true"' : ''}>
<div class="${classNames.item} ${
data.highlighted
? classNames.highlightedState
: classNames.itemSelectable
} ${
data.placeholder ? classNames.placeholder : ''
}" data-item data-id="${data.id}" data-value="${data.value}" ${
data.active ? 'aria-selected="true"' : ''
} ${data.disabled ? 'aria-disabled="true"' : ''}>
<span>&bigstar;</span> ${data.label}
</div>
`);
},
choice: (classNames, data) => {
return template(`
<div class="${classNames.item} ${classNames.itemChoice} ${data.disabled ? classNames.itemDisabled : classNames.itemSelectable}" data-select-text="${this.config.itemSelectText}" data-choice ${data.disabled ? 'data-choice-disabled aria-disabled="true"' : 'data-choice-selectable'} data-id="${data.id}" data-value="${data.value}" ${data.groupId > 0 ? 'role="treeitem"' : 'role="option"'}>
<div class="${classNames.item} ${classNames.itemChoice} ${
data.disabled ? classNames.itemDisabled : classNames.itemSelectable
}" data-select-text="${this.config.itemSelectText}" data-choice ${
data.disabled
? 'data-choice-disabled aria-disabled="true"'
: 'data-choice-selectable'
} data-id="${data.id}" data-value="${data.value}" ${
data.groupId > 0 ? 'role="treeitem"' : 'role="option"'
}>
<span>&bigstar;</span> ${data.label}
</div>
`);
},
};
}
},
});
```
## Events
**Note:** Events fired by Choices behave the same as standard events. Each event is triggered on the element passed to Choices (accessible via `this.passedElement`. Arguments are accessible within the `event.detail` object.
**Example:**
@ -577,29 +647,38 @@ const example = new Choices(element, {
const element = document.getElementById('example');
const example = new Choices(element);
element.addEventListener('addItem', function(event) {
// do something creative here...
console.log(event.detail.id);
console.log(event.detail.value);
console.log(event.detail.label);
console.log(event.detail.customProperties);
console.log(event.detail.groupValue);
}, false);
element.addEventListener(
'addItem',
function(event) {
// do something creative here...
console.log(event.detail.id);
console.log(event.detail.value);
console.log(event.detail.label);
console.log(event.detail.customProperties);
console.log(event.detail.groupValue);
},
false,
);
// or
const example = new Choices(document.getElementById('example'));
example.passedElement.element.addEventListener('addItem', function(event) {
// do something creative here...
console.log(event.detail.id);
console.log(event.detail.value);
console.log(event.detail.label);
console.log(event.detail.customProperties);
console.log(event.detail.groupValue);
}, false);
example.passedElement.element.addEventListener(
'addItem',
function(event) {
// do something creative here...
console.log(event.detail.id);
console.log(event.detail.value);
console.log(event.detail.label);
console.log(event.detail.customProperties);
console.log(event.detail.groupValue);
},
false,
);
```
### addItem
**Arguments:** `id, value, label, groupValue, keyCode`
**Input types affected:** `text`, `select-one`, `select-multiple`
@ -607,6 +686,7 @@ example.passedElement.element.addEventListener('addItem', function(event) {
**Usage:** Triggered each time an item is added (programmatically or by the user).
### removeItem
**Arguments:** `id, value, label, groupValue`
**Input types affected:** `text`, `select-one`, `select-multiple`
@ -614,6 +694,7 @@ example.passedElement.element.addEventListener('addItem', function(event) {
**Usage:** Triggered each time an item is removed (programmatically or by the user).
### highlightItem
**Arguments:** `id, value, label, groupValue`
**Input types affected:** `text`, `select-multiple`
@ -621,6 +702,7 @@ example.passedElement.element.addEventListener('addItem', function(event) {
**Usage:** Triggered each time an item is highlighted.
### unhighlightItem
**Arguments:** `id, value, label, groupValue`
**Input types affected:** `text`, `select-multiple`
@ -628,13 +710,16 @@ example.passedElement.element.addEventListener('addItem', function(event) {
**Usage:** Triggered each time an item is unhighlighted.
### choice
**Arguments:** `value, keyCode`
**Arguments:** `choice`
**Input types affected:** `select-one`, `select-multiple`
**Usage:** Triggered each time a choice is selected **by a user**, regardless if it changes the value of the input.
`choice` is a Choice object here (see terminology or typings file)
### change
**Arguments:** `value`
**Input types affected:** `text`, `select-one`, `select-multiple`
@ -642,6 +727,7 @@ example.passedElement.element.addEventListener('addItem', function(event) {
**Usage:** Triggered each time an item is added/removed **by a user**.
### search
**Arguments:** `value`, `resultCount`
**Input types affected:** `select-one`, `select-multiple`
@ -649,6 +735,7 @@ example.passedElement.element.addEventListener('addItem', function(event) {
**Usage:** Triggered when a user types into an input to search choices.
### showDropdown
**Arguments:** -
**Input types affected:** `select-one`, `select-multiple`
@ -656,6 +743,7 @@ example.passedElement.element.addEventListener('addItem', function(event) {
**Usage:** Triggered when the dropdown is shown.
### hideDropdown
**Arguments:** -
**Input types affected:** `select-one`, `select-multiple`
@ -663,21 +751,24 @@ example.passedElement.element.addEventListener('addItem', function(event) {
**Usage:** Triggered when the dropdown is hidden.
### highlightChoice
**Arguments:** `el`
**Input types affected:** `select-one`, `select-multiple`
**Usage:** Triggered when a choice from the dropdown is highlighted. The `el` argument is the HTML element node object that was affected.
**Usage:** Triggered when a choice from the dropdown is highlighted.
The `el` argument is choices.passedElement object that was affected.
## Methods
Methods can be called either directly or by chaining:
```js
// Calling a method by chaining
const choices = new Choices(element, {
addItems: false,
removeItems: false,
})
addItems: false,
removeItems: false,
})
.setValue(['Set value 1', 'Set value 2'])
.disable();
@ -687,16 +778,18 @@ const choices = new Choices(element, {
removeItems: false,
});
choices.setValue(['Set value 1', 'Set value 2'])
choices.setValue(['Set value 1', 'Set value 2']);
choices.disable();
```
### destroy();
**Input types affected:** `text`, `select-multiple`, `select-one`
**Usage:** Kills the instance of Choices, removes all event listeners and returns passed input to its initial state.
### init();
**Input types affected:** `text`, `select-multiple`, `select-one`
**Usage:** Creates a new instance of Choices, adds event listeners, creates templates and renders a Choices element to the DOM.
@ -704,47 +797,49 @@ choices.disable();
**Note:** This is called implicitly when a new instance of Choices is created. This would be used after a Choices instance had already been destroyed (using `destroy()`).
### highlightAll();
**Input types affected:** `text`, `select-multiple`
**Usage:** Highlight each chosen item (selected items can be removed).
### unhighlightAll();
**Input types affected:** `text`, `select-multiple`
**Usage:** Un-highlight each chosen item.
### removeActiveItemsByValue(value);
**Input types affected:** `text`, `select-multiple`
**Usage:** Remove each item by a given value.
### removeActiveItems(excludedId);
**Input types affected:** `text`, `select-multiple`
**Usage:** Remove each selectable item.
### removeHighlightedItems();
**Input types affected:** `text`, `select-multiple`
**Usage:** Remove each item the user has selected.
### showDropdown();
**Input types affected:** `select-one`, `select-multiple`
**Usage:** Show option list dropdown (only affects select inputs).
### hideDropdown();
**Input types affected:** `text`, `select-multiple`
**Usage:** Hide option list dropdown (only affects select inputs).
### setChoices(choices, value, label, replaceChoices);
**Input types affected:** `select-one`, `select-multiple`
**Usage:** Set choices of select input via an array of objects, a value name and a label name. This behaves the same as passing items via the `choices` option but can be called after initialising Choices. This can also be used to add groups of choices (see example 2); Optionally pass a true `replaceChoices` value to remove any existing choices. Optionally pass a `customProperties` object to add additional data to your choices (useful when searching/filtering etc). Passing an empty array as the first parameter, and a true `replaceChoices` is the same as calling `clearChoices` (see below).
@ -754,11 +849,16 @@ choices.disable();
```js
const example = new Choices(element);
example.setChoices([
{value: 'One', label: 'Label One', disabled: true},
{value: 'Two', label: 'Label Two', selected: true},
{value: 'Three', label: 'Label Three'},
], 'value', 'label', false);
example.setChoices(
[
{ value: 'One', label: 'Label One', disabled: true },
{ value: 'Two', label: 'Label Two', selected: true },
{ value: 'Three', label: 'Label Three' },
],
'value',
'label',
false,
);
```
**Example 2:**
@ -766,37 +866,50 @@ example.setChoices([
```js
const example = new Choices(element);
example.setChoices([{
label: 'Group one',
id: 1,
disabled: false,
choices: [
{value: 'Child One', label: 'Child One', selected: true},
{value: 'Child Two', label: 'Child Two', disabled: true},
{value: 'Child Three', label: 'Child Three'},
]
},
{
label: 'Group two',
id: 2,
disabled: false,
choices: [
{value: 'Child Four', label: 'Child Four', disabled: true},
{value: 'Child Five', label: 'Child Five'},
{value: 'Child Six', label: 'Child Six', customProperties: {
description: 'Custom description about child six',
random: 'Another random custom property'
}},
]
}], 'value', 'label', false);
example.setChoices(
[
{
label: 'Group one',
id: 1,
disabled: false,
choices: [
{ value: 'Child One', label: 'Child One', selected: true },
{ value: 'Child Two', label: 'Child Two', disabled: true },
{ value: 'Child Three', label: 'Child Three' },
],
},
{
label: 'Group two',
id: 2,
disabled: false,
choices: [
{ value: 'Child Four', label: 'Child Four', disabled: true },
{ value: 'Child Five', label: 'Child Five' },
{
value: 'Child Six',
label: 'Child Six',
customProperties: {
description: 'Custom description about child six',
random: 'Another random custom property',
},
},
],
},
],
'value',
'label',
false,
);
```
### clearChoices();
**Input types affected:** `select-one`, `select-multiple`
**Usage:** Clear all choices from select
### getValue(valueOnly)
**Input types affected:** `text`, `select-one`, `select-multiple`
**Usage:** Get value(s) of input (i.e. inputted items (text) or selected choices (select)). Optionally pass an argument of `true` to only return values rather than value objects.
@ -810,6 +923,7 @@ const valueArray = example.getValue(); // returns [{ active: true, choiceId: 1,
```
### setValue(items);
**Input types affected:** `text`
**Usage:** 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.
@ -821,16 +935,17 @@ const example = new Choices(element);
// via an array of objects
example.setValue([
{value: 'One', label: 'Label One'},
{value: 'Two', label: 'Label Two'},
{value: 'Three', label: 'Label Three'},
{ value: 'One', label: 'Label One' },
{ value: 'Two', label: 'Label Two' },
{ value: 'Three', label: 'Label Three' },
]);
// or via an array of strings
example.setValue(['Four','Five','Six']);
example.setValue(['Four', 'Five', 'Six']);
```
### setChoiceByValue(value);
**Input types affected:** `select-one`, `select-multiple`
**Usage:** Set value of input based on existing Choice. `value` can be either a single string or an array of strings
@ -840,9 +955,9 @@ example.setValue(['Four','Five','Six']);
```js
const example = new Choices(element, {
choices: [
{value: 'One', label: 'Label One'},
{value: 'Two', label: 'Label Two', disabled: true},
{value: 'Three', label: 'Label Three'},
{ value: 'One', label: 'Label One' },
{ value: 'Two', label: 'Label Two', disabled: true },
{ value: 'Three', label: 'Label Three' },
],
});
@ -850,29 +965,31 @@ example.setChoiceByValue('Two'); // Choice with value of 'Two' has now been sele
```
### clearStore();
**Input types affected:** `text`, `select-one`, `select-multiple`
**Usage:** Removes all items, choices and groups. Use with caution.
### clearInput();
**Input types affected:** `text`
**Usage:** Clear input of any user inputted text.
### disable();
**Input types affected:** `text`, `select-one`, `select-multiple`
**Usage:** Disables input from accepting new value/selecting further choices.
### enable();
**Input types affected:** `text`, `select-one`, `select-multiple`
**Usage:** Enables input to accept new values/select further choices.
### ajax(fn);
**Input types affected:** `select-one`, `select-multiple`
**Usage:** Populate choices/groups via a callback.
@ -899,7 +1016,7 @@ example.ajax(function(callback) {
If your structure differs from `data.value` and `data.key` structure you can write your own `key` and `value` into the `callback` function. This could be useful when you don't want to transform the given response.
```js
const example = new Choices(element)
const example = new Choices(element);
example.ajax(function(callback) {
fetch(url)
@ -915,6 +1032,7 @@ example.ajax(function(callback) {
```
## Browser compatibility
Choices is compiled using [Babel](https://babeljs.io/) to enable support for [ES5 browsers](http://caniuse.com/#feat=es5). If you need to support a browser that does not support one of the features listed below, I suggest including a polyfill from the very good [polyfill.io](https://cdn.polyfill.io/v2/docs/):
**Polyfill example used for the demo:**
@ -925,25 +1043,27 @@ Choices is compiled using [Babel](https://babeljs.io/) to enable support for [ES
**Features used in Choices:**
* Array.prototype.forEach
* Array.prototype.map
* Array.prototype.find
* Array.prototype.some
* Array.prototype.includes
* Array.from
* Array.prototype.reduce
* Array.prototype.indexOf
* Object.assign
* Element.prototype.classList
* window.requestAnimationFrame
* CustomEvent
- Array.prototype.forEach
- Array.prototype.map
- Array.prototype.find
- Array.prototype.some
- Array.prototype.includes
- Array.from
- Array.prototype.reduce
- Array.prototype.indexOf
- Object.assign
- Element.prototype.classList
- window.requestAnimationFrame
- CustomEvent
## Development
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
| Task | Usage |
| ------------------------- | ------------------------------------------------------------ |
| `npm run start` | Fire up local server for development |
@ -958,10 +1078,13 @@ To setup a local environment: clone this repo, navigate into it's directory in a
| `npm run css:build` | Compile, minify and prefix SCSS files to CSS |
## License
MIT License
## Web component
Want to use Choices as a web component? You're in luck. Adidas have built one for their design system which can be found [here](https://github.com/adidas/choicesjs-stencil).
## Misc
Thanks to [@mikefrancis](https://github.com/mikefrancis/) for [sending me on a hunt](https://twitter.com/_mikefrancis/status/701797835826667520) for a non-jQuery solution for select boxes that eventually led to this being built!

38
types/index.d.ts vendored
View file

@ -13,7 +13,7 @@ import { FuseOptions } from 'fuse.js';
declare namespace Choices {
namespace Types {
type strToEl = (
str: string
str: string,
) => HTMLElement | HTMLInputElement | HTMLOptionElement;
type stringFunction = () => string;
type noticeStringFunction = (value: string) => string;
@ -99,9 +99,9 @@ declare namespace Choices {
*
* **Input types affected:** select-one, select-multiple
*
* Arguments: value, keyCode
* Arguments: choice: Choice
*/
choice: CustomEvent<{ value: string; keyCode: string }>;
choice: CustomEvent<{ choice: Choices.Choice }>;
/**
* Triggered each time an item is added/removed **by a user**.
@ -143,9 +143,9 @@ declare namespace Choices {
* Triggered when a choice from the dropdown is highlighted.
*
* Input types affected: select-one, select-multiple
* Arguments: el is the HTML element node object that was affected.
* Arguments: el is the choice.passedElement that was affected.
*/
highlightChoice: CustomEvent<{ el: HTMLOptionElement }>;
highlightChoice: CustomEvent<{ el: Choices.passedElement }>;
}
interface Group {
@ -168,52 +168,52 @@ declare namespace Choices {
isSelectElement: boolean,
isSelectOneElement: boolean,
searchEnabled: boolean,
passedElementType: passedElement['type']
passedElementType: passedElement['type'],
) => HTMLElement;
containerInner: (this: Choices, classNames: ClassNames) => HTMLElement;
itemList: (
this: Choices,
classNames: ClassNames,
isSelectOneElement: boolean
isSelectOneElement: boolean,
) => HTMLElement;
placeholder: (
this: Choices,
classNames: ClassNames,
value: string
value: string,
) => HTMLElement;
item: (
this: Choices,
classNames: ClassNames,
data: Choice,
removeItemButton: boolean
removeItemButton: boolean,
) => HTMLElement;
choiceList: (
this: Choices,
classNames: ClassNames,
isSelectOneElement: boolean
isSelectOneElement: boolean,
) => HTMLElement;
choiceGroup: (
this: Choices,
classNames: ClassNames,
data: Choice
data: Choice,
) => HTMLElement;
choice: (
this: Choices,
classNames: ClassNames,
data: Choice,
selectText: string
selectText: string,
) => HTMLElement;
input: (
this: Choices,
classNames: ClassNames,
placeholderValue: string
placeholderValue: string,
) => HTMLInputElement;
dropdown: (this: Choices, classNames: ClassNames) => HTMLElement;
notice: (
this: Choices,
classNames: ClassNames,
label: string,
type: '' | 'no-results' | 'no-choices'
type: '' | 'no-results' | 'no-choices',
) => HTMLElement;
option: (data: Choice) => HTMLOptionElement;
}
@ -280,9 +280,9 @@ declare namespace Choices {
type: K,
listener: (
this: HTMLInputElement | HTMLSelectElement,
ev: Choices.EventMap[K]
ev: Choices.EventMap[K],
) => void,
options?: boolean | AddEventListenerOptions
options?: boolean | AddEventListenerOptions,
): void;
};
type: 'text' | 'select-one' | 'select-multiple';
@ -750,7 +750,7 @@ declare namespace Choices {
* @default null
*/
callbackOnCreateTemplates: (
template: Choices.Types.strToEl
template: Choices.Types.strToEl,
) => Partial<Choices.Templates>;
}
}
@ -769,7 +769,7 @@ export default class Choices {
constructor(
selectorOrElement: string | HTMLInputElement | HTMLSelectElement,
userConfig?: Partial<Choices.Options>
userConfig?: Partial<Choices.Options>,
);
/**
@ -942,7 +942,7 @@ export default class Choices {
choices: Choices.Choice[],
value: string,
label: string,
replaceChoices?: boolean
replaceChoices?: boolean,
): this;
/**