Skip to content

Commit

Permalink
support providing input element for autocomplete ui (#110)
Browse files Browse the repository at this point in the history
fix bug when passing DOM object to autocomplete

allow keyboard events to propagate if result list is not open
  • Loading branch information
kochis authored Aug 3, 2023
1 parent 3ba1215 commit bffa81d
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 34 deletions.
2 changes: 1 addition & 1 deletion demo/views/create-autocomplete.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</div>

<div>
<div id="autocomplete" style="max-width: 800px;"></div>
<input id="autocomplete" name="address" type="text" />
</div>

<div id="result-container" class="mt-4" style="display: none;">
Expand Down
84 changes: 51 additions & 33 deletions src/ui/autocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,56 +84,69 @@ class AutocompleteUI {
constructor(options: Partial<RadarAutocompleteUIOptions> = {}) {
this.config = Object.assign({}, defaultAutocompleteOptions, options) as RadarAutocompleteConfig;

// lookup container element
if (typeof this.config.container === 'string') {
const containerEL = document.getElementById(this.config.container);
if (!containerEL) {
throw new RadarAutocompleteContainerNotFound(`Could not find element with id ${this.config.container}`);
}
this.container = containerEL;
} else { // use provided element
this.container = this.config.container; // HTMLElement
}

// state
// setup state
this.isOpen = false;
this.debouncedFetchResults = this.debounce(this.fetchResults, this.config.debounceMS);
this.results = [];
this.highlightedIndex = -1;

// input field element
this.inputField = document.createElement('input');
this.inputField.classList.add(CLASSNAMES.INPUT);
this.inputField.placeholder = this.config.placeholder;
this.inputField.type = 'text';
this.inputField.disabled = this.config.disabled;
// get container element
let containerEL;
if (typeof this.config.container === 'string') { // lookup container element by ID
containerEL = document.getElementById(this.config.container);
} else { // use provided element
containerEL = this.config.container; // HTMLElement
}
if (!containerEL) {
throw new RadarAutocompleteContainerNotFound(`Could not find container element: ${this.config.container}`);
}
this.container = containerEL;

// search icon
const searchIcon = document.createElement('div');
searchIcon.classList.add(CLASSNAMES.SEARCH_ICON);
// create wrapper for input and result list
this.wrapper = document.createElement('div');
this.wrapper.classList.add(CLASSNAMES.WRAPPER);
this.wrapper.style.display = this.config.responsive ? 'block' : 'inline-block';
setWidth(this.wrapper, this.config);

// result list element
this.resultsList = document.createElement('ul');
this.resultsList.classList.add(CLASSNAMES.RESULTS_LIST);

// create wrapper and input field on DOM
this.wrapper = document.createElement('div');
this.wrapper.classList.add(CLASSNAMES.WRAPPER);
this.wrapper.style.display = this.config.responsive ? 'block' : 'inline-block';
setWidth(this.wrapper, this.config);
if (containerEL.nodeName === 'INPUT') {
// if an <input> element is provided, use that as the inputField,
// and append the resultList to it's parent container
this.inputField = containerEL as HTMLInputElement;

this.wrapper.appendChild(this.inputField);
this.wrapper.appendChild(this.resultsList);
this.wrapper.appendChild(searchIcon);
// append to dom
this.wrapper.appendChild(this.resultsList);
(containerEL.parentNode as any).appendChild(this.wrapper);

} else {
// if container is not an input, create new input and append to container

// create new input
this.inputField = document.createElement('input');
this.inputField.classList.add(CLASSNAMES.INPUT);
this.inputField.placeholder = this.config.placeholder;
this.inputField.type = 'text';
this.inputField.disabled = this.config.disabled;

// search icon
const searchIcon = document.createElement('div');
searchIcon.classList.add(CLASSNAMES.SEARCH_ICON);

// append to DOM
this.wrapper.appendChild(this.inputField);
this.wrapper.appendChild(this.resultsList);
this.wrapper.appendChild(searchIcon);
this.container.appendChild(this.wrapper);
}

// event listeners
// setup event listeners
this.inputField.addEventListener('input', this.handleInput.bind(this));
this.inputField.addEventListener('blur', this.close.bind(this), true);
this.inputField.addEventListener('keydown', this.handleKeyboardNavigation.bind(this));

// append to DOM
this.container.appendChild(this.wrapper);

Logger.debug(`AutocompleteUI iniailized with options: ${JSON.stringify(this.config)}`);
}

Expand Down Expand Up @@ -336,6 +349,11 @@ class AutocompleteUI {
// fallback to deprecated "keyCode" if event.code not set
const code = event.code !== undefined ? event.code : event.keyCode;

// allow event to propagate if result list is not open
if (!this.isOpen) {
return;
}

switch (code) {
// Next item
case 'Tab':
Expand Down

0 comments on commit bffa81d

Please sign in to comment.