Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support providing input element for autocomplete ui #110

Merged
merged 1 commit into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading