Skip to content

Commit 1c62bb9

Browse files
committed
refactor(Autocomplete): improve class names, clearMenus, input group support
1 parent cffb26c commit 1c62bb9

File tree

6 files changed

+95
-73
lines changed

6 files changed

+95
-73
lines changed

docs/content/forms/autocomplete.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,31 +180,36 @@ You may also choose from small and large auto completes to match our similarly s
180180

181181
{{< example class="d-flex flex-column gap-3" stackblitz_pro="true" >}}
182182
<div
183-
class="autocomplete autocomplete-lg"
183+
class="autocomplete-lg"
184184
data-coreui-cleaner="true"
185185
data-coreui-highlight-options-on-search="true"
186+
data-coreui-indicator="true"
186187
data-coreui-options="Angular, Bootstrap, React.js, Vue.js"
187188
data-coreui-placeholder="Large autocomplete..."
188189
data-coreui-search="global"
189190
data-coreui-show-hints="true"
191+
data-coreui-toggle="autocomplete"
190192
></div>
191193
<div
192-
data-coreui-toggle="autocomplete"
193194
data-coreui-cleaner="true"
194195
data-coreui-highlight-options-on-search="true"
196+
data-coreui-indicator="true"
195197
data-coreui-options="Angular, Bootstrap, React.js, Vue.js"
196198
data-coreui-placeholder="Normal autocomplete..."
197199
data-coreui-search="global"
198200
data-coreui-show-hints="true"
201+
data-coreui-toggle="autocomplete"
199202
></div>
200203
<div
201-
class="autocomplete autocomplete-sm"
204+
class="autocomplete-sm"
202205
data-coreui-cleaner="true"
203206
data-coreui-highlight-options-on-search="true"
207+
data-coreui-indicator="true"
204208
data-coreui-options="Angular, Bootstrap, React.js, Vue.js"
205209
data-coreui-placeholder="Small autocomplete..."
206210
data-coreui-search="global"
207211
data-coreui-show-hints="true"
212+
data-coreui-toggle="autocomplete"
208213
></div>
209214
{{< /example >}}
210215

js/src/autocomplete.js

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,13 @@ const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}`
5555
const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`
5656

5757
const CLASS_NAME_AUTOCOMPLETE = 'autocomplete'
58-
const CLASS_NAME_AUTOCOMPLETE_DROPDOWN = 'autocomplete-dropdown'
58+
const CLASS_NAME_BUTTONS = 'autocomplete-buttons'
5959
const CLASS_NAME_CLEANER = 'autocomplete-cleaner'
6060
const CLASS_NAME_DISABLED = 'disabled'
61+
const CLASS_NAME_DROPDOWN = 'autocomplete-dropdown'
62+
const CLASS_NAME_INDICATOR = 'autocomplete-indicator'
63+
const CLASS_NAME_INPUT = 'autocomplete-input'
64+
const CLASS_NAME_INPUT_HINT = 'autocomplete-input-hint'
6165
const CLASS_NAME_INPUT_GROUP = 'autocomplete-input-group'
6266
const CLASS_NAME_LABEL = 'label'
6367
const CLASS_NAME_OPTGROUP = 'autocomplete-optgroup'
@@ -69,7 +73,8 @@ const CLASS_NAME_SELECTED = 'selected'
6973
const CLASS_NAME_SHOW = 'show'
7074

7175
const SELECTOR_DATA_TOGGLE = '[data-coreui-toggle="autocomplete"]:not(.disabled)'
72-
const SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE}.${CLASS_NAME_SHOW}`
76+
const SELECTOR_DATA_TOGGLE_SHOWN = `.autocomplete:not(.disabled).${CLASS_NAME_SHOW}`
77+
const SELECTOR_INDICATOR = '.autocomplete-indicator'
7378
const SELECTOR_OPTGROUP = '.autocomplete-optgroup'
7479
const SELECTOR_OPTION = '.autocomplete-option'
7580
const SELECTOR_OPTIONS = '.autocomplete-options'
@@ -196,7 +201,7 @@ class Autocomplete extends BaseComponent {
196201

197202
EventHandler.trigger(this._element, EVENT_SHOW)
198203
this._element.classList.add(CLASS_NAME_SHOW)
199-
this._element.setAttribute('aria-expanded', true)
204+
this._inputElement.setAttribute('aria-expanded', true)
200205

201206
if (this._config.container) {
202207
this._menu.style.minWidth = `${this._element.offsetWidth}px`
@@ -216,7 +221,7 @@ class Autocomplete extends BaseComponent {
216221
}
217222

218223
this._element.classList.remove(CLASS_NAME_SHOW)
219-
this._element.setAttribute('aria-expanded', 'false')
224+
this._inputElement.setAttribute('aria-expanded', 'false')
220225

221226
if (this._config.container) {
222227
this._menu.classList.remove(CLASS_NAME_SHOW)
@@ -278,7 +283,7 @@ class Autocomplete extends BaseComponent {
278283
}
279284

280285
this._deselectOption(option.value)
281-
this._updateSelectionCleaner()
286+
this._updateCleaner()
282287
}
283288
}
284289

@@ -326,8 +331,8 @@ class Autocomplete extends BaseComponent {
326331
// Private
327332

328333
_addEventListeners() {
329-
EventHandler.on(this._element, EVENT_CLICK, () => {
330-
if (!this._config.disabled) {
334+
EventHandler.on(this._element, EVENT_CLICK, event => {
335+
if (!this._config.disabled && !event.target.closest(SELECTOR_INDICATOR)) {
331336
this.show()
332337
}
333338
})
@@ -369,7 +374,7 @@ class Autocomplete extends BaseComponent {
369374

370375
EventHandler.on(this._indicatorElement, EVENT_CLICK, event => {
371376
event.preventDefault()
372-
event.stopPropagation()
377+
// event.stopPropagation()
373378
this.toggle()
374379
})
375380

@@ -451,7 +456,7 @@ class Autocomplete extends BaseComponent {
451456
this._onOptionsClick(event.target)
452457
})
453458

454-
EventHandler.on(this._selectionCleanerElement, EVENT_CLICK, event => {
459+
EventHandler.on(this._cleanerElement, EVENT_CLICK, event => {
455460
if (!this._config.disabled) {
456461
event.preventDefault()
457462
event.stopPropagation()
@@ -536,13 +541,13 @@ class Autocomplete extends BaseComponent {
536541
this._element.classList.add(className)
537542
}
538543

539-
this._createSelection()
544+
this._createInputGroup()
540545
this._createButtons()
541546
this._createOptionsContainer()
542547
this._updateOptionsList()
543548
}
544549

545-
_createSelection() {
550+
_createInputGroup() {
546551
const togglerEl = document.createElement('div')
547552
togglerEl.classList.add(CLASS_NAME_INPUT_GROUP)
548553
this._togglerElement = togglerEl
@@ -553,7 +558,7 @@ class Autocomplete extends BaseComponent {
553558

554559
if (!this._config.disabled && this._config.showHints) {
555560
const inputHintEl = document.createElement('input')
556-
inputHintEl.classList.add('form-control', 'autocomplete-selection', 'autocomplete-selection-hint')
561+
inputHintEl.classList.add(CLASS_NAME_INPUT, CLASS_NAME_INPUT_HINT)
557562
inputHintEl.setAttribute('name', (this._config.name || `${this._uniqueId}-hint`).toString())
558563
inputHintEl.autocomplete = 'off'
559564
inputHintEl.readOnly = true
@@ -565,7 +570,7 @@ class Autocomplete extends BaseComponent {
565570
}
566571

567572
const inputEl = document.createElement('input')
568-
inputEl.classList.add('form-control', 'autocomplete-selection')
573+
inputEl.classList.add(CLASS_NAME_INPUT)
569574
inputEl.id = this._uniqueId
570575
inputEl.setAttribute('name', (this._config.name || this._uniqueId).toString())
571576
inputEl.autocomplete = 'off'
@@ -596,7 +601,7 @@ class Autocomplete extends BaseComponent {
596601
}
597602

598603
const buttons = document.createElement('div')
599-
buttons.classList.add('autocomplete-buttons')
604+
buttons.classList.add(CLASS_NAME_BUTTONS)
600605

601606
if (!this._config.disabled && this._config.cleaner) {
602607
const cleaner = document.createElement('button')
@@ -606,13 +611,13 @@ class Autocomplete extends BaseComponent {
606611
cleaner.setAttribute('aria-label', this._config.ariaCleanerLabel)
607612

608613
buttons.append(cleaner)
609-
this._selectionCleanerElement = cleaner
614+
this._cleanerElement = cleaner
610615
}
611616

612617
if (this._config.indicator) {
613618
const indicator = document.createElement('button')
614619
indicator.type = 'button'
615-
indicator.classList.add('autocomplete-indicator')
620+
indicator.classList.add(CLASS_NAME_INDICATOR)
616621
indicator.setAttribute('aria-label', this._config.ariaIndicatorLabel)
617622

618623
if (this._config.disabled) {
@@ -625,7 +630,7 @@ class Autocomplete extends BaseComponent {
625630
}
626631

627632
this._togglerElement.append(buttons)
628-
this._updateSelectionCleaner()
633+
this._updateCleaner()
629634
}
630635

631636
_createPopper() {
@@ -654,7 +659,7 @@ class Autocomplete extends BaseComponent {
654659

655660
_createOptionsContainer() {
656661
const dropdownDiv = document.createElement('div')
657-
dropdownDiv.classList.add(CLASS_NAME_AUTOCOMPLETE_DROPDOWN)
662+
dropdownDiv.classList.add(CLASS_NAME_DROPDOWN)
658663
dropdownDiv.role = 'listbox'
659664
dropdownDiv.setAttribute('aria-labelledby', this._uniqueId)
660665

@@ -797,7 +802,7 @@ class Autocomplete extends BaseComponent {
797802
}
798803

799804
this._inputElement.focus()
800-
this._updateSelectionCleaner()
805+
this._updateCleaner()
801806
}
802807

803808
_deselectOption(value) {
@@ -814,17 +819,17 @@ class Autocomplete extends BaseComponent {
814819
})
815820
}
816821

817-
_updateSelectionCleaner() {
818-
if (!this._config.cleaner || this._selectionCleanerElement === null) {
822+
_updateCleaner() {
823+
if (!this._config.cleaner || this._cleanerElement === null) {
819824
return
820825
}
821826

822827
if (this._selected.length > 0) {
823-
this._selectionCleanerElement.style.removeProperty('display')
828+
this._cleanerElement.style.removeProperty('display')
824829
return
825830
}
826831

827-
this._selectionCleanerElement.style.display = 'none'
832+
this._cleanerElement.style.display = 'none'
828833
}
829834

830835
_updateOptionsList(options = this._options) {
@@ -946,42 +951,38 @@ class Autocomplete extends BaseComponent {
946951
}
947952

948953
static clearMenus(event) {
949-
if (event && (event.button === RIGHT_MOUSE_BUTTON ||
950-
(event.type === 'keyup' && event.key !== TAB_KEY))) {
954+
if (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY)) {
951955
return
952956
}
953957

954-
const autocompletes = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN)
955-
956-
for (let i = 0, len = autocompletes.length; i < len; i++) {
957-
const context = Data.get(autocompletes[i], DATA_KEY)
958-
const relatedTarget = {
959-
relatedTarget: autocompletes[i]
960-
}
958+
const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN)
961959

962-
if (event && event.type === 'click') {
963-
relatedTarget.clickEvent = event
964-
}
960+
for (const toggle of openToggles) {
961+
const context = Autocomplete.getInstance(toggle)
965962

966963
if (!context) {
967964
continue
968965
}
969966

970-
if (!context._element.classList.contains(CLASS_NAME_SHOW)) {
967+
const composedPath = event.composedPath()
968+
969+
if (
970+
composedPath.includes(context._element)
971+
) {
971972
continue
972973
}
973974

974-
if (context._element.contains(event.target)) {
975-
continue
975+
const relatedTarget = { relatedTarget: context._element }
976+
977+
if (event.type === 'click') {
978+
relatedTarget.clickEvent = event
976979
}
977980

978981
context.hide()
979982
context.search('')
980983
if (context._config.allowOnlyDefinedOptions && context._selected.length === 0) {
981984
context._inputElement.value = ''
982985
}
983-
984-
EventHandler.trigger(context._element, EVENT_HIDDEN)
985986
}
986987
}
987988
}

0 commit comments

Comments
 (0)