diff --git a/src/components/form-tags/form-tags.js b/src/components/form-tags/form-tags.js index 9ee8593cb37..d23d5c265ee 100644 --- a/src/components/form-tags/form-tags.js +++ b/src/components/form-tags/form-tags.js @@ -6,7 +6,15 @@ import identity from '../../utils/identity' import looseEqual from '../../utils/loose-equal' import { arrayIncludes, concat } from '../../utils/array' import { getComponentConfig } from '../../utils/config' -import { attemptBlur, attemptFocus, matches, requestAF, select } from '../../utils/dom' +import { + attemptBlur, + attemptFocus, + closest, + isActiveElement, + matches, + requestAF, + select +} from '../../utils/dom' import { isEvent, isFunction, isString } from '../../utils/inspect' import { escapeRegExp, toString, trim, trimLeft } from '../../utils/string' import idMixin from '../../mixins/id' @@ -178,6 +186,12 @@ export const BFormTags = /*#__PURE__*/ Vue.extend({ type: Boolean, default: false }, + ignoreInputFocusSelector: { + // Disable the input focus behavior when clicking + // on element matching the selector (or selectors) + type: [Array, String], + default: () => ['.b-form-tag', 'button', 'input', 'select'] + }, value: { // The v-model prop type: Array, @@ -245,6 +259,13 @@ export const BFormTags = /*#__PURE__*/ Vue.extend({ const joiner = this.computedSeparator.charAt(0) return joiner !== ' ' ? `${joiner} ` : joiner }, + computeIgnoreInputFocusSelector() { + // Normalize to an single selector with selectors separated by `,` + return concat(this.ignoreInputFocusSelector) + .filter(identity) + .join(',') + .trim() + }, disableAddButton() { // If 'Add' button should be disabled // If the input contains at least one tag that can @@ -416,7 +437,13 @@ export const BFormTags = /*#__PURE__*/ Vue.extend({ }, // --- Wrapper event handlers --- onClick(evt) { - if (!this.disabled && isEvent(evt) && evt.target === evt.currentTarget) { + const ignoreFocusSelector = this.computeIgnoreInputFocusSelector + const { target } = evt + if ( + !this.disabled && + !isActiveElement(target) && + (!ignoreFocusSelector || !closest(ignoreFocusSelector, target, true)) + ) { this.$nextTick(() => { this.focus() }) @@ -630,8 +657,7 @@ export const BFormTags = /*#__PURE__*/ Vue.extend({ staticClass: 'list-unstyled mt-n1 mb-0 d-flex flex-wrap align-items-center', attrs: { id: tagListId } }, - // `concat()` is faster than array spread when args are known to be arrays - concat($tags, $field) + [$tags, $field] ) // Assemble the feedback @@ -792,12 +818,12 @@ export const BFormTags = /*#__PURE__*/ Vue.extend({ 'aria-describedby': this.safeId('_selected_') }, on: { + click: this.onClick, focusin: this.onFocusin, - focusout: this.onFocusout, - click: this.onClick + focusout: this.onFocusout } }, - concat($output, $removed, $content, $hidden) + [$output, $removed, $content, $hidden] ) } }) diff --git a/src/components/form-tags/package.json b/src/components/form-tags/package.json index a41a762951b..43021122273 100644 --- a/src/components/form-tags/package.json +++ b/src/components/form-tags/package.json @@ -110,6 +110,11 @@ { "prop": "noOuterFocus", "description": "When set, disables the focus styling of the component root element" + }, + { + "prop": "ignoreInputFocusSelector", + "version": "2.16.0", + "description": "Ignore certain elements from the click to focus input routine, specified by css selector(s)" } ], "slots": [