diff --git a/README.md b/README.md index b1c596a..23c48d2 100644 --- a/README.md +++ b/README.md @@ -191,11 +191,11 @@ Pass an array of objects: **Usage:** The amount of items a user can input/select ("-1" indicates no limit). ### addItems -**Type:** `Boolean` **Default:** `true` +**Type:** `Boolean` **Default:** `true` (for text inputs) `false` (for select elements) -**Input types affected:** `text` +**Input types affected:** `text`, `select-one`, `select-multiple` -**Usage:** Whether a user can add items. +**Usage:** Whether a user can add items. ### removeItems **Type:** `Boolean` **Default:** `true` diff --git a/assets/scripts/src/choices.js b/assets/scripts/src/choices.js index 04377f4..94de858 100644 --- a/assets/scripts/src/choices.js +++ b/assets/scripts/src/choices.js @@ -116,18 +116,6 @@ class Choices { callbackOnCreateTemplates: null, }; - // Merge options with user options - this.config = extend(defaultConfig, userConfig); - - // Create data store - this.store = new Store(this.render); - - // State tracking - this.initialised = false; - this.currentState = {}; - this.prevState = {}; - this.currentValue = ''; - // Retrieve triggering element (i.e. element with 'data-choice' trigger) this.element = element; this.passedElement = isType('String', element) ? document.querySelector(element) : element; @@ -139,8 +127,14 @@ class Choices { return; } - this.highlightPosition = 0; - this.canSearch = this.config.search; + // It only makes sense for addItems to be true for + // text inputs by default + if (this.isSelectElement) { + defaultConfig.addItems = false; + } + + // Merge options with user options + this.config = extend(defaultConfig, userConfig); // Assing preset choices from passed object this.presetChoices = this.config.choices; @@ -172,7 +166,16 @@ class Choices { this._onPaste = this._onPaste.bind(this); this._onInput = this._onInput.bind(this); - // Monitor touch taps/scrolls + // Create data store + this.store = new Store(this.render); + + // State tracking + this.initialised = false; + this.currentState = {}; + this.prevState = {}; + this.currentValue = ''; + this.highlightPosition = 0; + this.canSearch = this.config.search; this.wasTap = true; // Cutting the mustard @@ -416,7 +419,7 @@ class Choices { const canAddItem = this._canAddItem(activeItems, this.input.value); let dropdownItem = this._getTemplate('notice', this.config.noChoicesText); - if (canAddItem.notice) { + if (this.config.addItems && canAddItem.notice) { dropdownItem = this._getTemplate('notice', canAddItem.notice); } else if (this.isSearching) { dropdownItem = this._getTemplate('notice', this.config.noResultsText); @@ -1082,18 +1085,18 @@ class Choices { let canAddItem = true; let notice = isType('Function', this.config.addItemText) ? this.config.addItemText(value) : this.config.addItemText; - if (this.passedElement.type === 'select-multiple' || this.passedElement.type === 'text') { - if (this.config.maxItemCount > 0 && this.config.maxItemCount <= this.itemList.children.length) { - // If there is a max entry limit and we have reached that limit - // don't update - canAddItem = false; - notice = isType('Function', this.config.maxItemText) ? this.config.maxItemText(this.config.maxItemCount) : this.config.maxItemText; - } - } - if (this.config.addItems) { const isUnique = !activeItems.some((item) => item.value === value.trim()); + if (this.passedElement.type === 'select-multiple' || this.passedElement.type === 'text') { + if (this.config.maxItemCount > 0 && this.config.maxItemCount <= this.itemList.children.length) { + // If there is a max entry limit and we have reached that limit + // don't update + canAddItem = false; + notice = isType('Function', this.config.maxItemText) ? this.config.maxItemText(this.config.maxItemCount) : this.config.maxItemText; + } + } + // If a user has supplied a regular expression filter if (this.config.regexFilter) { // Determine whether we can update based on whether @@ -1356,13 +1359,11 @@ class Choices { }; const onEnterKey = () => { - if (hasActiveDropdown) { - const highlighted = this.dropdown.querySelector(`.${this.config.classNames.highlightedState}`); + const highlighted = this.dropdown.querySelector(`.${this.config.classNames.highlightedState}`); + if (hasActiveDropdown && highlighted) { // If we have a highlighted choice, select it - if (highlighted) { - this._handleChoiceAction(activeItems, highlighted); - } + this._handleChoiceAction(activeItems, highlighted); } else if (passedElementType === 'select-one') { // Open single select dropdown if it's not active if (!hasActiveDropdown) { @@ -1378,19 +1379,24 @@ class Choices { // All is good, add if (canAddItem.response) { - if (hasActiveDropdown) { - this.hideDropdown(); - } + // Track whether we will end up adding an item + const willAddItem = this.isTextElement || (this.isSelectElement && this.config.addItems); - if (this.isTextElement) { - this._addItem(value); - } else { - this._addChoice(true, false, value, value); - this.containerOuter.focus(); - } + if (willAddItem) { + if (hasActiveDropdown) { + this.hideDropdown(); + } - this._triggerChange(value); - this.clearInput(this.passedElement); + if (this.isTextElement) { + this._addItem(value); + } else if(this.config.addItems) { + this._addChoice(true, false, value, value); + this.containerOuter.focus(); + } + + this._triggerChange(value); + this.clearInput(this.passedElement); + } } } @@ -2235,7 +2241,10 @@ class Choices { } } - if (!this.config.addItems) this.disable(); + // Disable text input if no entry allowed + if (!this.config.addItems && this.isTextElement) { + this.disable(); + } containerOuter.appendChild(containerInner); containerOuter.appendChild(dropdown);