mirror of
https://github.com/Choices-js/Choices.git
synced 2024-06-03 06:22:25 +02:00
Ability to disable sorting of choices/groups
This commit is contained in:
parent
59977af5d3
commit
aff165e4a1
|
@ -41,6 +41,7 @@ A vanilla, lightweight (~15kb gzipped 🎉), configurable select box/text input
|
|||
search: true,
|
||||
flip: true,
|
||||
regexFilter: null,
|
||||
shouldSort: true,
|
||||
sortFilter: sortByAlpha,
|
||||
sortFields: ['label', 'value'],
|
||||
placeholder: true,
|
||||
|
@ -225,6 +226,13 @@ Pass an array of objects:
|
|||
|
||||
**Usage:** A filter that will need to pass for a user to successfully add an item.
|
||||
|
||||
### shouldSort
|
||||
**Type:** `Boolean` **Default:** `true`
|
||||
|
||||
**Input types affected:** `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** Whether choices should be sorted. If false, choices will appear in the order they were given.
|
||||
|
||||
### sortFilter
|
||||
**Type:** `Function` **Default:** sortByAlpha
|
||||
|
||||
|
|
86
assets/scripts/dist/choices.js
vendored
86
assets/scripts/dist/choices.js
vendored
|
@ -104,6 +104,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Retrieve triggering element (i.e. element with 'data-choice' trigger)
|
||||
this.passedElement = (0, _utils.isType)('String', element) ? document.querySelector(element) : element;
|
||||
|
||||
// If element has already been initalised with Choices, return it silently
|
||||
if (this.passedElement.getAttribute('data-choice') === 'active') return;
|
||||
|
||||
var defaultConfig = {
|
||||
items: [],
|
||||
choices: [],
|
||||
|
@ -118,6 +124,7 @@
|
|||
search: true,
|
||||
flip: true,
|
||||
regexFilter: null,
|
||||
shouldSort: true,
|
||||
sortFilter: _utils.sortByAlpha,
|
||||
sortFields: ['label', 'value'],
|
||||
placeholder: true,
|
||||
|
@ -172,12 +179,14 @@
|
|||
this.currentState = {};
|
||||
this.prevState = {};
|
||||
this.currentValue = '';
|
||||
|
||||
// Retrieve triggering element (i.e. element with 'data-choice' trigger)
|
||||
this.passedElement = (0, _utils.isType)('String', element) ? document.querySelector(element) : element;
|
||||
|
||||
this.highlightPosition = 0;
|
||||
|
||||
// Track searching
|
||||
this.canSearch = this.config.search;
|
||||
// Track tapping
|
||||
this.wasTap = true;
|
||||
// Focus containerOuter but not show dropdown if true
|
||||
this.focusAndHideDropdown = false;
|
||||
|
||||
// Assing preset choices from passed object
|
||||
this.presetChoices = this.config.choices;
|
||||
|
@ -209,25 +218,17 @@
|
|||
this._onPaste = this._onPaste.bind(this);
|
||||
this._onInput = this._onInput.bind(this);
|
||||
|
||||
// Focus containerOuter but not show dropdown if true
|
||||
this.focusAndHideDropdown = false;
|
||||
|
||||
// Monitor touch taps/scrolls
|
||||
this.wasTap = true;
|
||||
|
||||
// Cutting the mustard
|
||||
var cuttingTheMustard = 'querySelector' in document && 'addEventListener' in document && 'classList' in document.createElement('div');
|
||||
if (!cuttingTheMustard) console.error('Choices: Your browser doesn\'t support Choices');
|
||||
|
||||
// Input type check
|
||||
var canInit = this.passedElement && (0, _utils.isElement)(this.passedElement) && ['select-one', 'select-multiple', 'text'].some(function (type) {
|
||||
var isValidElement = this.passedElement && (0, _utils.isElement)(this.passedElement);
|
||||
var isValidType = ['select-one', 'select-multiple', 'text'].some(function (type) {
|
||||
return type === _this.passedElement.type;
|
||||
});
|
||||
|
||||
if (canInit) {
|
||||
// If element has already been initalised with Choices
|
||||
if (this.passedElement.getAttribute('data-choice') === 'active') return;
|
||||
|
||||
if (isValidElement && isValidType) {
|
||||
// Let's go
|
||||
this.init();
|
||||
} else {
|
||||
|
@ -247,7 +248,8 @@
|
|||
value: function init() {
|
||||
var callback = arguments.length <= 0 || arguments[0] === undefined ? this.config.callbackOnInit : arguments[0];
|
||||
|
||||
if (this.initialised !== true) {
|
||||
if (this.initialised === true) return;
|
||||
|
||||
// Set initialise flag
|
||||
this.initialised = true;
|
||||
|
||||
|
@ -274,7 +276,6 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy Choices and nullify values
|
||||
|
@ -285,6 +286,8 @@
|
|||
}, {
|
||||
key: 'destroy',
|
||||
value: function destroy() {
|
||||
if (this.initialised !== true) return;
|
||||
|
||||
this._removeEventListeners();
|
||||
|
||||
this.passedElement.classList.remove(this.config.classNames.input, this.config.classNames.hiddenState);
|
||||
|
@ -600,7 +603,8 @@
|
|||
value: function setValue(args) {
|
||||
var _this8 = this;
|
||||
|
||||
if (this.initialised === true) {
|
||||
if (this.initialised !== true) return;
|
||||
|
||||
// Convert args to an itterable array
|
||||
var values = [].concat(_toConsumableArray(args));
|
||||
|
||||
|
@ -622,7 +626,6 @@
|
|||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -639,9 +642,9 @@
|
|||
value: function setValueByChoice(value) {
|
||||
var _this9 = this;
|
||||
|
||||
if (this.passedElement.type !== 'text') {
|
||||
(function () {
|
||||
var choices = _this9.store.getChoices();
|
||||
if (this.passedElement.type === 'text') return;
|
||||
|
||||
var choices = this.store.getChoices();
|
||||
// If only one value has been passed, convert to array
|
||||
var choiceValue = (0, _utils.isType)('Array', value) ? value : [value];
|
||||
|
||||
|
@ -662,8 +665,6 @@
|
|||
console.warn('Attempting to select choice that does not exist');
|
||||
}
|
||||
});
|
||||
})();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -681,7 +682,8 @@
|
|||
value: function setChoices(choices, value, label) {
|
||||
var _this10 = this;
|
||||
|
||||
if (this.initialised === true) {
|
||||
if (this.initialised !== true) return;
|
||||
|
||||
if (this.passedElement.type === 'select-one' || this.passedElement.type === 'select-multiple') {
|
||||
if (!(0, _utils.isType)('Array', choices) || !value) return;
|
||||
|
||||
|
@ -696,7 +698,6 @@
|
|||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -785,7 +786,8 @@
|
|||
value: function ajax(fn) {
|
||||
var _this11 = this;
|
||||
|
||||
if (this.initialised === true) {
|
||||
if (this.initialised !== true) return;
|
||||
|
||||
if (this.passedElement.type === 'select-one' || this.passedElement.type === 'select-multiple') {
|
||||
this.containerOuter.classList.add(this.config.classNames.loadingState);
|
||||
this.containerOuter.setAttribute('aria-busy', 'true');
|
||||
|
@ -801,6 +803,7 @@
|
|||
if (results && results.length) {
|
||||
// Remove loading states/text
|
||||
_this11.containerOuter.classList.remove(_this11.config.classNames.loadingState);
|
||||
|
||||
if (_this11.passedElement.type === 'select-multiple') {
|
||||
var placeholder = _this11.config.placeholder ? _this11.config.placeholderValue || _this11.passedElement.getAttribute('placeholder') : false;
|
||||
if (placeholder) {
|
||||
|
@ -810,19 +813,14 @@
|
|||
|
||||
// Add each result as a choice
|
||||
results.forEach(function (result, index) {
|
||||
// Select first choice in list if single select input
|
||||
if (index === 0 && _this11.passedElement.type === 'select-one') {
|
||||
_this11._addChoice(true, false, result[value], result[label]);
|
||||
} else {
|
||||
_this11._addChoice(false, false, result[value], result[label]);
|
||||
}
|
||||
});
|
||||
}
|
||||
_this11.containerOuter.removeAttribute('aria-busy');
|
||||
};
|
||||
|
||||
fn(callback);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -1064,6 +1062,7 @@
|
|||
var _this14 = this;
|
||||
|
||||
if (!value) return;
|
||||
|
||||
if (this.input === document.activeElement) {
|
||||
var choices = this.store.getChoices();
|
||||
var hasUnactiveChoices = choices.some(function (option) {
|
||||
|
@ -1086,7 +1085,6 @@
|
|||
include: 'score'
|
||||
});
|
||||
var results = fuse.search(needle);
|
||||
|
||||
_this14.currentValue = newValue;
|
||||
_this14.highlightPosition = 0;
|
||||
_this14.isSearching = true;
|
||||
|
@ -2157,6 +2155,7 @@
|
|||
|
||||
// Join choices with preset choices and add them
|
||||
allChoices.concat(_this19.presetChoices).forEach(function (o, index) {
|
||||
// Pre-select first choice if it's a single select
|
||||
if (index === 0 && _this19.passedElement.type === 'select-one') {
|
||||
_this19._addChoice(true, o.disabled ? o.disabled : false, o.value, o.label);
|
||||
} else {
|
||||
|
@ -2195,7 +2194,12 @@
|
|||
var groupFragment = fragment || document.createDocumentFragment();
|
||||
var filter = this.config.sortFilter;
|
||||
|
||||
groups.sort(filter).forEach(function (group) {
|
||||
// If sorting is enabled, filter groups
|
||||
if (this.config.shouldSort) {
|
||||
groups.sort(filter);
|
||||
}
|
||||
|
||||
groups.forEach(function (group) {
|
||||
// Grab options that are children of this group
|
||||
var groupChoices = choices.filter(function (choice) {
|
||||
if (_this20.passedElement.type === 'select-one') {
|
||||
|
@ -2233,11 +2237,15 @@
|
|||
var choicesFragment = fragment || document.createDocumentFragment();
|
||||
var filter = this.isSearching ? _utils.sortByScore : this.config.sortFilter;
|
||||
|
||||
choices.sort(filter).forEach(function (choice) {
|
||||
// If sorting is enabled or the user is searching, filter choices
|
||||
if (this.config.shouldSort || this.isSearching) {
|
||||
choices.sort(filter);
|
||||
}
|
||||
|
||||
choices.forEach(function (choice) {
|
||||
var dropdownItem = _this21._getTemplate('choice', choice);
|
||||
if (_this21.passedElement.type === 'select-one') {
|
||||
choicesFragment.appendChild(dropdownItem);
|
||||
} else if (!choice.selected) {
|
||||
var shouldRender = _this21.passedElement.type === 'select-one' || !choice.selected;
|
||||
if (shouldRender) {
|
||||
choicesFragment.appendChild(dropdownItem);
|
||||
}
|
||||
});
|
||||
|
|
2
assets/scripts/dist/choices.js.map
vendored
2
assets/scripts/dist/choices.js.map
vendored
File diff suppressed because one or more lines are too long
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
|
@ -42,6 +42,12 @@ export default class Choices {
|
|||
}
|
||||
}
|
||||
|
||||
// Retrieve triggering element (i.e. element with 'data-choice' trigger)
|
||||
this.passedElement = isType('String', element) ? document.querySelector(element) : element;
|
||||
|
||||
// If element has already been initalised with Choices, return it silently
|
||||
if (this.passedElement.getAttribute('data-choice') === 'active') return;
|
||||
|
||||
const defaultConfig = {
|
||||
items: [],
|
||||
choices: [],
|
||||
|
@ -111,12 +117,14 @@ export default class Choices {
|
|||
this.currentState = {};
|
||||
this.prevState = {};
|
||||
this.currentValue = '';
|
||||
|
||||
// Retrieve triggering element (i.e. element with 'data-choice' trigger)
|
||||
this.passedElement = isType('String', element) ? document.querySelector(element) : element;
|
||||
|
||||
this.highlightPosition = 0;
|
||||
|
||||
// Track searching
|
||||
this.canSearch = this.config.search;
|
||||
// Track tapping
|
||||
this.wasTap = true;
|
||||
// Focus containerOuter but not show dropdown if true
|
||||
this.focusAndHideDropdown = false;
|
||||
|
||||
// Assing preset choices from passed object
|
||||
this.presetChoices = this.config.choices;
|
||||
|
@ -148,23 +156,15 @@ export default class Choices {
|
|||
this._onPaste = this._onPaste.bind(this);
|
||||
this._onInput = this._onInput.bind(this);
|
||||
|
||||
// Focus containerOuter but not show dropdown if true
|
||||
this.focusAndHideDropdown = false;
|
||||
|
||||
// Monitor touch taps/scrolls
|
||||
this.wasTap = true;
|
||||
|
||||
// Cutting the mustard
|
||||
const cuttingTheMustard = 'querySelector' in document && 'addEventListener' in document && 'classList' in document.createElement('div');
|
||||
if (!cuttingTheMustard) console.error('Choices: Your browser doesn\'t support Choices');
|
||||
|
||||
// Input type check
|
||||
const canInit = this.passedElement && isElement(this.passedElement) && ['select-one', 'select-multiple', 'text'].some(type => type === this.passedElement.type);
|
||||
|
||||
if (canInit) {
|
||||
// If element has already been initalised with Choices
|
||||
if (this.passedElement.getAttribute('data-choice') === 'active') return;
|
||||
const isValidElement = this.passedElement && isElement(this.passedElement);
|
||||
const isValidType = ['select-one', 'select-multiple', 'text'].some(type => type === this.passedElement.type);
|
||||
|
||||
if (isValidElement && isValidType) {
|
||||
// Let's go
|
||||
this.init();
|
||||
} else {
|
||||
|
@ -178,7 +178,8 @@ export default class Choices {
|
|||
* @public
|
||||
*/
|
||||
init(callback = this.config.callbackOnInit) {
|
||||
if (this.initialised !== true) {
|
||||
if (this.initialised === true) return;
|
||||
|
||||
// Set initialise flag
|
||||
this.initialised = true;
|
||||
|
||||
|
@ -205,7 +206,6 @@ export default class Choices {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy Choices and nullify values
|
||||
|
@ -213,6 +213,8 @@ export default class Choices {
|
|||
* @public
|
||||
*/
|
||||
destroy() {
|
||||
if (this.initialised !== true) return;
|
||||
|
||||
this._removeEventListeners();
|
||||
|
||||
this.passedElement.classList.remove(this.config.classNames.input, this.config.classNames.hiddenState);
|
||||
|
@ -472,7 +474,8 @@ export default class Choices {
|
|||
* @public
|
||||
*/
|
||||
setValue(args) {
|
||||
if (this.initialised === true) {
|
||||
if (this.initialised !== true) return;
|
||||
|
||||
// Convert args to an itterable array
|
||||
const values = [...args];
|
||||
|
||||
|
@ -494,7 +497,6 @@ export default class Choices {
|
|||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -506,7 +508,8 @@ export default class Choices {
|
|||
* @public
|
||||
*/
|
||||
setValueByChoice(value) {
|
||||
if (this.passedElement.type !== 'text') {
|
||||
if (this.passedElement.type === 'text') return;
|
||||
|
||||
const choices = this.store.getChoices();
|
||||
// If only one value has been passed, convert to array
|
||||
const choiceValue = isType('Array', value) ? value : [value];
|
||||
|
@ -528,7 +531,6 @@ export default class Choices {
|
|||
console.warn('Attempting to select choice that does not exist');
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -541,7 +543,8 @@ export default class Choices {
|
|||
* @public
|
||||
*/
|
||||
setChoices(choices, value, label) {
|
||||
if (this.initialised === true) {
|
||||
if (this.initialised !== true) return;
|
||||
|
||||
if (this.passedElement.type === 'select-one' || this.passedElement.type === 'select-multiple') {
|
||||
if (!isType('Array', choices) || !value) return;
|
||||
|
||||
|
@ -556,7 +559,6 @@ export default class Choices {
|
|||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -628,7 +630,8 @@ export default class Choices {
|
|||
* @public
|
||||
*/
|
||||
ajax(fn) {
|
||||
if (this.initialised === true) {
|
||||
if (this.initialised !== true) return;
|
||||
|
||||
if (this.passedElement.type === 'select-one' || this.passedElement.type === 'select-multiple') {
|
||||
this.containerOuter.classList.add(this.config.classNames.loadingState);
|
||||
this.containerOuter.setAttribute('aria-busy', 'true');
|
||||
|
@ -644,6 +647,7 @@ export default class Choices {
|
|||
if (results && results.length) {
|
||||
// Remove loading states/text
|
||||
this.containerOuter.classList.remove(this.config.classNames.loadingState);
|
||||
|
||||
if (this.passedElement.type === 'select-multiple') {
|
||||
const placeholder = this.config.placeholder ? this.config.placeholderValue || this.passedElement.getAttribute('placeholder') : false;
|
||||
if (placeholder) {
|
||||
|
@ -653,19 +657,14 @@ export default class Choices {
|
|||
|
||||
// Add each result as a choice
|
||||
results.forEach((result, index) => {
|
||||
// Select first choice in list if single select input
|
||||
if (index === 0 && this.passedElement.type === 'select-one') {
|
||||
this._addChoice(true, false, result[value], result[label]);
|
||||
} else {
|
||||
this._addChoice(false, false, result[value], result[label]);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.containerOuter.removeAttribute('aria-busy');
|
||||
};
|
||||
|
||||
fn(callback);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -867,6 +866,7 @@ export default class Choices {
|
|||
*/
|
||||
_searchChoices(value) {
|
||||
if (!value) return;
|
||||
|
||||
if (this.input === document.activeElement) {
|
||||
const choices = this.store.getChoices();
|
||||
const hasUnactiveChoices = choices.some((option) => option.active !== true);
|
||||
|
@ -1880,7 +1880,6 @@ export default class Choices {
|
|||
const passedOptions = Array.from(this.passedElement.options);
|
||||
const allChoices = [];
|
||||
|
||||
|
||||
// Create array of options from option elements
|
||||
passedOptions.forEach((o) => {
|
||||
allChoices.push({
|
||||
|
@ -1895,6 +1894,7 @@ export default class Choices {
|
|||
allChoices
|
||||
.concat(this.presetChoices)
|
||||
.forEach((o, index) => {
|
||||
// Pre-select first choice if it's a single select
|
||||
if (index === 0 && this.passedElement.type === 'select-one') {
|
||||
this._addChoice(true, o.disabled ? o.disabled : false, o.value, o.label);
|
||||
} else {
|
||||
|
@ -1927,9 +1927,12 @@ export default class Choices {
|
|||
const groupFragment = fragment || document.createDocumentFragment();
|
||||
const filter = this.config.sortFilter;
|
||||
|
||||
groups
|
||||
.sort(filter)
|
||||
.forEach((group) => {
|
||||
// If sorting is enabled, filter groups
|
||||
if (this.config.shouldSort) {
|
||||
groups.sort(filter);
|
||||
}
|
||||
|
||||
groups.forEach((group) => {
|
||||
// Grab options that are children of this group
|
||||
const groupChoices = choices.filter((choice) => {
|
||||
if (this.passedElement.type === 'select-one') {
|
||||
|
@ -1960,17 +1963,17 @@ export default class Choices {
|
|||
renderChoices(choices, fragment) {
|
||||
// Create a fragment to store our list items (so we don't have to update the DOM for each item)
|
||||
const choicesFragment = fragment || document.createDocumentFragment();
|
||||
|
||||
if (this.config.shouldSort || this.isSearching) {
|
||||
const filter = this.isSearching ? sortByScore : this.config.sortFilter;
|
||||
|
||||
// If sorting is enabled or the user is searching, filter choices
|
||||
if (this.config.shouldSort || this.isSearching) {
|
||||
choices.sort(filter);
|
||||
}
|
||||
choices
|
||||
.forEach((choice) => {
|
||||
|
||||
choices.forEach((choice) => {
|
||||
const dropdownItem = this._getTemplate('choice', choice);
|
||||
if (this.passedElement.type === 'select-one') {
|
||||
choicesFragment.appendChild(dropdownItem);
|
||||
} else if (!choice.selected) {
|
||||
const shouldRender = this.passedElement.type === 'select-one' || !choice.selected;
|
||||
if (shouldRender) {
|
||||
choicesFragment.appendChild(dropdownItem);
|
||||
}
|
||||
});
|
||||
|
|
28
index.html
28
index.html
|
@ -41,7 +41,9 @@
|
|||
<h1 class="visible-ie">Choices.js</h1>
|
||||
<p>Choices.js is a lightweight, configurable select box/text input plugin. Similar to Select2 and Selectize but without the jQuery dependency.</p>
|
||||
<p>For all config options, visit the <a href="https://github.com/jshjohnson/Choices">GitHub repo</a>.</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Text inputs</h2>
|
||||
<label for="choices-1">Limited to 5 values with remove button</label>
|
||||
<input class="form-control" id="choices-1" type="text" value="preset-1,preset-2" placeholder="Enter something">
|
||||
|
@ -170,6 +172,28 @@
|
|||
<label for="choices-17">Option selected via config</label>
|
||||
<select class="form-control" name="choices-17" id="choices-17" placeholder="This is a placeholder"></select>
|
||||
|
||||
<label for="choices-18">Options without sorting</label>
|
||||
<select class="form-control" name="choices-18" id="choices-18" placeholder="This is a placeholder">
|
||||
<option value="Madrid">Madrid</option>
|
||||
<option value="Toronto">Toronto</option>
|
||||
<option value="Vancouver">Vancouver</option>
|
||||
<option value="London">London</option>
|
||||
<option value="Manchester">Manchester</option>
|
||||
<option value="Liverpool">Liverpool</option>
|
||||
<option value="Paris">Paris</option>
|
||||
<option value="Malaga">Malaga</option>
|
||||
<option value="Washington" disabled>Washington</option>
|
||||
<option value="Lyon">Lyon</option>
|
||||
<option value="Marseille">Marseille</option>
|
||||
<option value="Hamburg">Hamburg</option>
|
||||
<option value="Munich">Munich</option>
|
||||
<option value="Barcelona">Barcelona</option>
|
||||
<option value="Berlin">Berlin</option>
|
||||
<option value="Montreal">Montreal</option>
|
||||
<option value="New York">New York</option>
|
||||
<option value="Michigan">Michigan</option>
|
||||
</select>
|
||||
|
||||
<p>Below is an example of how you could have two select inputs depend on eachother. 'Boroughs' will only be enabled if the value of 'States' is 'New York'</p>
|
||||
<label for="cities">States</label>
|
||||
<select class="form-control" name="cities" id="cities" placeholder="Choose a state">
|
||||
|
@ -335,6 +359,10 @@
|
|||
],
|
||||
}).setValueByChoice('Two');
|
||||
|
||||
var example16 = new Choices('#choices-18', {
|
||||
shouldSort: false,
|
||||
});
|
||||
|
||||
var cities = new Choices(document.getElementById('cities'), {
|
||||
callbackOnChange: function(value) {
|
||||
if(value === 'New York') {
|
||||
|
|
Loading…
Reference in a new issue