+ */
+var Choices = /** @class */ (function () {
+ function Choices(element, userConfig) {
+ if (element === void 0) { element = '[data-choice]'; }
+ if (userConfig === void 0) { userConfig = {}; }
+ var _this = this;
+ this.initialisedOK = undefined;
+ this._hasNonChoicePlaceholder = false;
+ this._lastAddedChoiceId = 0;
+ this._lastAddedGroupId = 0;
+ var defaults = Choices.defaults;
+ this.config = __assign(__assign(__assign({}, defaults.allOptions), defaults.options), userConfig);
+ ObjectsInConfig.forEach(function (key) {
+ _this.config[key] = __assign(__assign(__assign({}, defaults.allOptions[key]), defaults.options[key]), userConfig[key]);
+ });
+ var config = this.config;
+ if (!config.silent) {
+ this._validateConfig();
+ }
+ var docRoot = config.shadowRoot || document.documentElement;
+ this._docRoot = docRoot;
+ var passedElement = typeof element === 'string' ? docRoot.querySelector(element) : element;
+ if (!passedElement ||
+ typeof passedElement !== 'object' ||
+ !(isHtmlInputElement(passedElement) || isHtmlSelectElement(passedElement))) {
+ if (!passedElement && typeof element === 'string') {
+ throw TypeError("Selector ".concat(element, " failed to find an element"));
+ }
+ throw TypeError("Expected one of the following types text|select-one|select-multiple");
+ }
+ this._elementType = passedElement.type;
+ this._isTextElement = this._elementType === TEXT_TYPE;
+ if (this._isTextElement || config.maxItemCount !== 1) {
+ config.singleModeForMultiSelect = false;
+ }
+ if (config.singleModeForMultiSelect) {
+ this._elementType = SELECT_MULTIPLE_TYPE;
+ }
+ this._isSelectOneElement = this._elementType === SELECT_ONE_TYPE;
+ this._isSelectMultipleElement = this._elementType === SELECT_MULTIPLE_TYPE;
+ this._isSelectElement = this._isSelectOneElement || this._isSelectMultipleElement;
+ this._canAddUserChoices = (this._isTextElement && config.addItems) || (this._isSelectElement && config.addChoices);
+ if (!['auto', 'always'].includes("".concat(config.renderSelectedChoices))) {
+ config.renderSelectedChoices = 'auto';
+ }
+ if (config.closeDropdownOnSelect === 'auto') {
+ config.closeDropdownOnSelect = this._isTextElement || this._isSelectOneElement || config.singleModeForMultiSelect;
+ }
+ else {
+ config.closeDropdownOnSelect = coerceBool(config.closeDropdownOnSelect);
+ }
+ if (config.placeholder) {
+ if (config.placeholderValue) {
+ this._hasNonChoicePlaceholder = true;
+ }
+ else if (passedElement.dataset.placeholder) {
+ this._hasNonChoicePlaceholder = true;
+ config.placeholderValue = passedElement.dataset.placeholder;
+ }
+ }
+ if (userConfig.addItemFilter && typeof userConfig.addItemFilter !== 'function') {
+ var re = userConfig.addItemFilter instanceof RegExp ? userConfig.addItemFilter : new RegExp(userConfig.addItemFilter);
+ config.addItemFilter = re.test.bind(re);
+ }
+ if (this._isTextElement) {
+ this.passedElement = new WrappedInput({
+ element: passedElement,
+ classNames: config.classNames,
+ });
+ }
+ else {
+ var selectEl = passedElement;
+ this.passedElement = new WrappedSelect({
+ element: selectEl,
+ classNames: config.classNames,
+ template: function (data) { return _this._templates.option(data); },
+ extractPlaceholder: config.placeholder && !this._hasNonChoicePlaceholder,
+ });
+ }
+ this.initialised = false;
+ this._store = new Store();
+ this._currentValue = '';
+ config.searchEnabled = (!this._isTextElement && config.searchEnabled) || this._elementType === SELECT_MULTIPLE_TYPE;
+ this._canSearch = config.searchEnabled;
+ this._isScrollingOnIe = false;
+ this._highlightPosition = 0;
+ this._wasTap = true;
+ this._placeholderValue = this._generatePlaceholderValue();
+ this._baseId = generateId(passedElement, 'choices-');
+ /**
+ * setting direction in cases where it's explicitly set on passedElement
+ * or when calculated direction is different from the document
+ */
+ this._direction = this.passedElement.dir;
+ if (!this._direction) {
+ var elementDirection = window.getComputedStyle(this.passedElement.element).direction;
+ var documentDirection = window.getComputedStyle(document.documentElement).direction;
+ if (elementDirection !== documentDirection) {
+ this._direction = elementDirection;
+ }
+ }
+ this._idNames = {
+ itemChoice: 'item-choice',
+ };
+ this._templates = defaults.templates;
+ this._render = this._render.bind(this);
+ this._onFocus = this._onFocus.bind(this);
+ this._onBlur = this._onBlur.bind(this);
+ this._onKeyUp = this._onKeyUp.bind(this);
+ this._onKeyDown = this._onKeyDown.bind(this);
+ this._onInput = this._onInput.bind(this);
+ this._onClick = this._onClick.bind(this);
+ this._onTouchMove = this._onTouchMove.bind(this);
+ this._onTouchEnd = this._onTouchEnd.bind(this);
+ this._onMouseDown = this._onMouseDown.bind(this);
+ this._onMouseOver = this._onMouseOver.bind(this);
+ this._onFormReset = this._onFormReset.bind(this);
+ this._onSelectKey = this._onSelectKey.bind(this);
+ this._onEnterKey = this._onEnterKey.bind(this);
+ this._onEscapeKey = this._onEscapeKey.bind(this);
+ this._onDirectionKey = this._onDirectionKey.bind(this);
+ this._onDeleteKey = this._onDeleteKey.bind(this);
+ // If element has already been initialised with Choices, fail silently
+ if (this.passedElement.isActive) {
+ if (!config.silent) {
+ console.warn('Trying to initialise Choices on element already initialised', { element: element });
+ }
+ this.initialised = true;
+ this.initialisedOK = false;
+ return;
+ }
+ // Let's go
+ this.init();
+ // preserve the selected item list after setup for form reset
+ this._initialItems = this._store.items.map(function (choice) { return choice.value; });
+ }
+ Object.defineProperty(Choices, "defaults", {
+ get: function () {
+ return Object.preventExtensions({
+ get options() {
+ return USER_DEFAULTS;
+ },
+ get allOptions() {
+ return DEFAULT_CONFIG;
+ },
+ get templates() {
+ return templates;
+ },
+ });
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Choices.prototype.init = function () {
+ if (this.initialised || this.initialisedOK !== undefined) {
+ return;
+ }
+ this._searcher = getSearcher(this.config);
+ this._loadChoices();
+ this._createTemplates();
+ this._createElements();
+ this._createStructure();
+ if ((this._isTextElement && !this.config.addItems) ||
+ this.passedElement.element.hasAttribute('disabled') ||
+ !!this.passedElement.element.closest('fieldset:disabled')) {
+ this.disable();
+ }
+ else {
+ this.enable();
+ this._addEventListeners();
+ }
+ // should be triggered **after** disabled state to avoid additional re-draws
+ this._initStore();
+ this.initialised = true;
+ this.initialisedOK = true;
+ var callbackOnInit = this.config.callbackOnInit;
+ // Run callback if it is a function
+ if (callbackOnInit && typeof callbackOnInit === 'function') {
+ callbackOnInit.call(this);
+ }
+ };
+ Choices.prototype.destroy = function () {
+ if (!this.initialised) {
+ return;
+ }
+ this._removeEventListeners();
+ this.passedElement.reveal();
+ this.containerOuter.unwrap(this.passedElement.element);
+ this._store._listeners = []; // prevents select/input value being wiped
+ this.clearStore();
+ this._stopSearch();
+ this._templates = Choices.defaults.templates;
+ this.initialised = false;
+ this.initialisedOK = undefined;
+ };
+ Choices.prototype.enable = function () {
+ var _a = this, passedElement = _a.passedElement, containerOuter = _a.containerOuter;
+ if (passedElement.isDisabled) {
+ passedElement.enable();
+ }
+ if (containerOuter.isDisabled) {
+ this._addEventListeners();
+ this.input.enable();
+ containerOuter.enable();
+ this._render();
+ }
+ return this;
+ };
+ Choices.prototype.disable = function () {
+ var _a = this, passedElement = _a.passedElement, containerOuter = _a.containerOuter;
+ if (!passedElement.isDisabled) {
+ passedElement.disable();
+ }
+ if (!containerOuter.isDisabled) {
+ this._removeEventListeners();
+ this.input.disable();
+ containerOuter.disable();
+ this._render();
+ }
+ return this;
+ };
+ Choices.prototype.highlightItem = function (item, runEvent) {
+ if (runEvent === void 0) { runEvent = true; }
+ if (!item || !item.id) {
+ return this;
+ }
+ var choice = this._store.choices.find(function (c) { return c.id === item.id; });
+ if (!choice || choice.highlighted) {
+ return this;
+ }
+ this._store.dispatch(highlightItem(choice, true));
+ if (runEvent) {
+ this.passedElement.triggerEvent("highlightItem" /* EventType.highlightItem */, this._getChoiceForOutput(choice));
+ }
+ return this;
+ };
+ Choices.prototype.unhighlightItem = function (item, runEvent) {
+ if (runEvent === void 0) { runEvent = true; }
+ if (!item || !item.id) {
+ return this;
+ }
+ var choice = this._store.choices.find(function (c) { return c.id === item.id; });
+ if (!choice || !choice.highlighted) {
+ return this;
+ }
+ this._store.dispatch(highlightItem(choice, false));
+ if (runEvent) {
+ this.passedElement.triggerEvent("highlightItem" /* EventType.highlightItem */, this._getChoiceForOutput(choice));
+ }
+ return this;
+ };
+ Choices.prototype.highlightAll = function () {
+ var _this = this;
+ this._store.withTxn(function () {
+ _this._store.items.forEach(function (item) { return _this.highlightItem(item); });
+ });
+ return this;
+ };
+ Choices.prototype.unhighlightAll = function () {
+ var _this = this;
+ this._store.withTxn(function () {
+ _this._store.items.forEach(function (item) { return _this.unhighlightItem(item); });
+ });
+ return this;
+ };
+ Choices.prototype.removeActiveItemsByValue = function (value) {
+ var _this = this;
+ this._store.withTxn(function () {
+ _this._store.items.filter(function (item) { return item.value === value; }).forEach(function (item) { return _this._removeItem(item); });
+ });
+ return this;
+ };
+ Choices.prototype.removeActiveItems = function (excludedId) {
+ var _this = this;
+ this._store.withTxn(function () {
+ _this._store.items.filter(function (_a) {
+ var id = _a.id;
+ return id !== excludedId;
+ }).forEach(function (item) { return _this._removeItem(item); });
+ });
+ return this;
+ };
+ Choices.prototype.removeHighlightedItems = function (runEvent) {
+ var _this = this;
+ if (runEvent === void 0) { runEvent = false; }
+ this._store.withTxn(function () {
+ _this._store.highlightedActiveItems.forEach(function (item) {
+ _this._removeItem(item);
+ // If this action was performed by the user
+ // trigger the event
+ if (runEvent) {
+ _this._triggerChange(item.value);
+ }
+ });
+ });
+ return this;
+ };
+ Choices.prototype.showDropdown = function (preventInputFocus) {
+ var _this = this;
+ if (this.dropdown.isActive) {
+ return this;
+ }
+ requestAnimationFrame(function () {
+ _this.dropdown.show();
+ _this.containerOuter.open(_this.dropdown.distanceFromTopWindow);
+ if (!preventInputFocus && _this._canSearch) {
+ _this.input.focus();
+ }
+ _this.passedElement.triggerEvent("showDropdown" /* EventType.showDropdown */);
+ });
+ return this;
+ };
+ Choices.prototype.hideDropdown = function (preventInputBlur) {
+ var _this = this;
+ if (!this.dropdown.isActive) {
+ return this;
+ }
+ requestAnimationFrame(function () {
+ _this.dropdown.hide();
+ _this.containerOuter.close();
+ if (!preventInputBlur && _this._canSearch) {
+ _this.input.removeActiveDescendant();
+ _this.input.blur();
+ }
+ _this.passedElement.triggerEvent("hideDropdown" /* EventType.hideDropdown */);
+ });
+ return this;
+ };
+ Choices.prototype.getValue = function (valueOnly) {
+ var _this = this;
+ if (valueOnly === void 0) { valueOnly = false; }
+ var values = this._store.items.reduce(function (selectedItems, item) {
+ var itemValue = valueOnly ? item.value : _this._getChoiceForOutput(item);
+ selectedItems.push(itemValue);
+ return selectedItems;
+ }, []);
+ return this._isSelectOneElement || this.config.singleModeForMultiSelect ? values[0] : values;
+ };
+ Choices.prototype.setValue = function (items) {
+ var _this = this;
+ if (!this.initialisedOK) {
+ this._warnChoicesInitFailed('setValue');
+ return this;
+ }
+ this._store.withTxn(function () {
+ items.forEach(function (value) {
+ if (value) {
+ _this._addChoice(mapInputToChoice(value, false));
+ }
+ });
+ });
+ // @todo integrate with Store
+ this._searcher.reset();
+ return this;
+ };
+ Choices.prototype.setChoiceByValue = function (value) {
+ var _this = this;
+ if (!this.initialisedOK) {
+ this._warnChoicesInitFailed('setChoiceByValue');
+ return this;
+ }
+ if (this._isTextElement) {
+ return this;
+ }
+ this._store.withTxn(function () {
+ // If only one value has been passed, convert to array
+ var choiceValue = Array.isArray(value) ? value : [value];
+ // Loop through each value and
+ choiceValue.forEach(function (val) { return _this._findAndSelectChoiceByValue(val); });
+ });
+ // @todo integrate with Store
+ this._searcher.reset();
+ return this;
+ };
+ /**
+ * Set choices of select input via an array of objects (or function that returns array of object or promise of it),
+ * a value field name and a label field 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).
+ *
+ * **Input types affected:** select-one, select-multiple
+ *
+ * @example
+ * ```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
+ * ```js
+ * const example = new Choices(element);
+ *
+ * example.setChoices(async () => {
+ * try {
+ * const items = await fetch('/items');
+ * return items.json()
+ * } catch(err) {
+ * console.error(err)
+ * }
+ * });
+ * ```
+ *
+ * @example
+ * ```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);
+ * ```
+ */
+ Choices.prototype.setChoices = function (choicesArrayOrFetcher, value, label, replaceChoices) {
+ var _this = this;
+ if (choicesArrayOrFetcher === void 0) { choicesArrayOrFetcher = []; }
+ if (value === void 0) { value = 'value'; }
+ if (label === void 0) { label = 'label'; }
+ if (replaceChoices === void 0) { replaceChoices = false; }
+ if (!this.initialisedOK) {
+ this._warnChoicesInitFailed('setChoices');
+ return this;
+ }
+ if (!this._isSelectElement) {
+ throw new TypeError("setChoices can't be used with INPUT based Choices");
+ }
+ if (typeof value !== 'string' || !value) {
+ throw new TypeError("value parameter must be a name of 'value' field in passed objects");
+ }
+ // Clear choices if needed
+ if (replaceChoices) {
+ this.clearChoices();
+ }
+ if (typeof choicesArrayOrFetcher === 'function') {
+ // it's a choices fetcher function
+ var fetcher_1 = choicesArrayOrFetcher(this);
+ if (typeof Promise === 'function' && fetcher_1 instanceof Promise) {
+ // that's a promise
+ // eslint-disable-next-line no-promise-executor-return
+ return new Promise(function (resolve) { return requestAnimationFrame(resolve); })
+ .then(function () { return _this._handleLoadingState(true); })
+ .then(function () { return fetcher_1; })
+ .then(function (data) { return _this.setChoices(data, value, label, replaceChoices); })
+ .catch(function (err) {
+ if (!_this.config.silent) {
+ console.error(err);
+ }
+ })
+ .then(function () { return _this._handleLoadingState(false); })
+ .then(function () { return _this; });
+ }
+ // function returned something else than promise, let's check if it's an array of choices
+ if (!Array.isArray(fetcher_1)) {
+ throw new TypeError(".setChoices first argument function must return either array of choices or Promise, got: ".concat(typeof fetcher_1));
+ }
+ // recursion with results, it's sync and choices were cleared already
+ return this.setChoices(fetcher_1, value, label, false);
+ }
+ if (!Array.isArray(choicesArrayOrFetcher)) {
+ throw new TypeError(".setChoices must be called either with array of choices with a function resulting into Promise of array of choices");
+ }
+ this.containerOuter.removeLoadingState();
+ this._store.withTxn(function () {
+ var isDefaultValue = value === 'value';
+ var isDefaultLabel = label === 'label';
+ choicesArrayOrFetcher.forEach(function (groupOrChoice) {
+ if ('choices' in groupOrChoice) {
+ var group = groupOrChoice;
+ if (!isDefaultLabel) {
+ group = __assign(__assign({}, group), { label: group[label] });
+ }
+ _this._addGroup(mapInputToChoice(group, true));
+ }
+ else {
+ var choice = groupOrChoice;
+ if (!isDefaultLabel || !isDefaultValue) {
+ choice = __assign(__assign({}, choice), { value: choice[value], label: choice[label] });
+ }
+ _this._addChoice(mapInputToChoice(choice, false));
+ }
+ });
+ });
+ // @todo integrate with Store
+ this._searcher.reset();
+ return this;
+ };
+ Choices.prototype.refresh = function (withEvents, selectFirstOption, deselectAll) {
+ var _this = this;
+ if (withEvents === void 0) { withEvents = false; }
+ if (selectFirstOption === void 0) { selectFirstOption = false; }
+ if (deselectAll === void 0) { deselectAll = false; }
+ if (!this._isSelectElement) {
+ if (!this.config.silent) {
+ console.warn('refresh method can only be used on choices backed by a
+
+
Below is an example of how you could have two select inputs depend on
@@ -520,6 +522,7 @@
Form interaction
Change the values and press reset to restore to initial state.