Socket
Socket
Sign inDemoInstall

@github/auto-complete-element

Package Overview
Dependencies
Maintainers
14
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@github/auto-complete-element - npm Package Compare versions

Comparing version 3.0.1 to 3.0.2

dist/auto-complete-element.d.ts

807

dist/bundle.js

@@ -1,383 +0,346 @@

class AutocompleteEvent extends CustomEvent {
constructor(type, init) {
super(type, init);
this.relatedTarget = init.relatedTarget;
}
}
function debounce(callback, wait) {
let timeout;
return function () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
clearTimeout(timeout);
timeout = setTimeout(() => {
clearTimeout(timeout);
callback(...args);
}, wait);
};
}
const requests = new WeakMap();
function fragment(el, url) {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.setRequestHeader('Accept', 'text/fragment+html');
return request(el, xhr);
}
function request(el, xhr) {
const pending = requests.get(el);
if (pending) pending.abort();
requests.set(el, xhr);
const clear = () => requests.delete(el);
const result = send(xhr);
result.then(clear, clear);
return result;
}
function send(xhr) {
return new Promise((resolve, reject) => {
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.responseText);
} else {
reject(new Error(xhr.responseText));
}
};
xhr.onerror = reject;
xhr.send();
});
}
const ctrlBindings = !!navigator.userAgent.match(/Macintosh/);
class Combobox {
constructor(input, list) {
this.input = input;
this.list = list;
this.isComposing = false;
if (!list.id) {
list.id = "combobox-".concat(Math.random().toString().slice(2, 6));
constructor(input, list) {
this.input = input;
this.list = list;
this.isComposing = false;
if (!list.id) {
list.id = `combobox-${Math.random()
.toString()
.slice(2, 6)}`;
}
this.keyboardEventHandler = event => keyboardBindings(event, this);
this.compositionEventHandler = event => trackComposition(event, this);
this.inputHandler = this.clearSelection.bind(this);
input.setAttribute('role', 'combobox');
input.setAttribute('aria-controls', list.id);
input.setAttribute('aria-expanded', 'false');
input.setAttribute('aria-autocomplete', 'list');
input.setAttribute('aria-haspopup', 'listbox');
}
this.keyboardEventHandler = event => keyboardBindings(event, this);
this.compositionEventHandler = event => trackComposition(event, this);
this.inputHandler = this.clearSelection.bind(this);
input.setAttribute('role', 'combobox');
input.setAttribute('aria-controls', list.id);
input.setAttribute('aria-expanded', 'false');
input.setAttribute('aria-autocomplete', 'list');
input.setAttribute('aria-haspopup', 'listbox');
}
destroy() {
this.clearSelection();
this.stop();
this.input.removeAttribute('role');
this.input.removeAttribute('aria-controls');
this.input.removeAttribute('aria-expanded');
this.input.removeAttribute('aria-autocomplete');
this.input.removeAttribute('aria-haspopup');
}
start() {
this.input.setAttribute('aria-expanded', 'true');
this.input.addEventListener('compositionstart', this.compositionEventHandler);
this.input.addEventListener('compositionend', this.compositionEventHandler);
this.input.addEventListener('input', this.inputHandler);
this.input.addEventListener('keydown', this.keyboardEventHandler);
this.list.addEventListener('click', commitWithElement);
}
stop() {
this.clearSelection();
this.input.setAttribute('aria-expanded', 'false');
this.input.removeEventListener('compositionstart', this.compositionEventHandler);
this.input.removeEventListener('compositionend', this.compositionEventHandler);
this.input.removeEventListener('input', this.inputHandler);
this.input.removeEventListener('keydown', this.keyboardEventHandler);
this.list.removeEventListener('click', commitWithElement);
}
navigate() {
let indexDiff = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
const focusEl = Array.from(this.list.querySelectorAll('[aria-selected="true"]')).filter(visible)[0];
const els = Array.from(this.list.querySelectorAll('[role="option"]')).filter(visible);
const focusIndex = els.indexOf(focusEl);
if (focusIndex === els.length - 1 && indexDiff === 1 || focusIndex === 0 && indexDiff === -1) {
this.clearSelection();
this.input.focus();
return;
destroy() {
this.clearSelection();
this.stop();
this.input.removeAttribute('role');
this.input.removeAttribute('aria-controls');
this.input.removeAttribute('aria-expanded');
this.input.removeAttribute('aria-autocomplete');
this.input.removeAttribute('aria-haspopup');
}
let indexOfItem = indexDiff === 1 ? 0 : els.length - 1;
if (focusEl && focusIndex >= 0) {
const newIndex = focusIndex + indexDiff;
if (newIndex >= 0 && newIndex < els.length) indexOfItem = newIndex;
start() {
this.input.setAttribute('aria-expanded', 'true');
this.input.addEventListener('compositionstart', this.compositionEventHandler);
this.input.addEventListener('compositionend', this.compositionEventHandler);
this.input.addEventListener('input', this.inputHandler);
this.input.addEventListener('keydown', this.keyboardEventHandler);
this.list.addEventListener('click', commitWithElement);
}
const target = els[indexOfItem];
if (!target) return;
for (const el of els) {
if (target === el) {
this.input.setAttribute('aria-activedescendant', target.id);
target.setAttribute('aria-selected', 'true');
scrollTo(this.list, target);
} else {
el.setAttribute('aria-selected', 'false');
}
stop() {
this.clearSelection();
this.input.setAttribute('aria-expanded', 'false');
this.input.removeEventListener('compositionstart', this.compositionEventHandler);
this.input.removeEventListener('compositionend', this.compositionEventHandler);
this.input.removeEventListener('input', this.inputHandler);
this.input.removeEventListener('keydown', this.keyboardEventHandler);
this.list.removeEventListener('click', commitWithElement);
}
}
clearSelection() {
this.input.removeAttribute('aria-activedescendant');
for (const el of this.list.querySelectorAll('[aria-selected="true"]')) {
el.setAttribute('aria-selected', 'false');
navigate(indexDiff = 1) {
const focusEl = Array.from(this.list.querySelectorAll('[aria-selected="true"]')).filter(visible)[0];
const els = Array.from(this.list.querySelectorAll('[role="option"]')).filter(visible);
const focusIndex = els.indexOf(focusEl);
if ((focusIndex === els.length - 1 && indexDiff === 1) || (focusIndex === 0 && indexDiff === -1)) {
this.clearSelection();
this.input.focus();
return;
}
let indexOfItem = indexDiff === 1 ? 0 : els.length - 1;
if (focusEl && focusIndex >= 0) {
const newIndex = focusIndex + indexDiff;
if (newIndex >= 0 && newIndex < els.length)
indexOfItem = newIndex;
}
const target = els[indexOfItem];
if (!target)
return;
for (const el of els) {
if (target === el) {
this.input.setAttribute('aria-activedescendant', target.id);
target.setAttribute('aria-selected', 'true');
scrollTo(this.list, target);
}
else {
el.setAttribute('aria-selected', 'false');
}
}
}
}
clearSelection() {
this.input.removeAttribute('aria-activedescendant');
for (const el of this.list.querySelectorAll('[aria-selected="true"]')) {
el.setAttribute('aria-selected', 'false');
}
}
}
function keyboardBindings(event, combobox) {
if (event.shiftKey || event.metaKey || event.altKey) return;
if (!ctrlBindings && event.ctrlKey) return;
if (combobox.isComposing) return;
switch (event.key) {
case 'Enter':
case 'Tab':
if (commit(combobox.input, combobox.list)) {
event.preventDefault();
}
break;
case 'Escape':
combobox.clearSelection();
break;
case 'ArrowDown':
combobox.navigate(1);
event.preventDefault();
break;
case 'ArrowUp':
combobox.navigate(-1);
event.preventDefault();
break;
case 'n':
if (ctrlBindings && event.ctrlKey) {
combobox.navigate(1);
event.preventDefault();
}
break;
case 'p':
if (ctrlBindings && event.ctrlKey) {
combobox.navigate(-1);
event.preventDefault();
}
break;
default:
if (event.ctrlKey) break;
combobox.clearSelection();
}
if (event.shiftKey || event.metaKey || event.altKey)
return;
if (!ctrlBindings && event.ctrlKey)
return;
if (combobox.isComposing)
return;
switch (event.key) {
case 'Enter':
case 'Tab':
if (commit(combobox.input, combobox.list)) {
event.preventDefault();
}
break;
case 'Escape':
combobox.clearSelection();
break;
case 'ArrowDown':
combobox.navigate(1);
event.preventDefault();
break;
case 'ArrowUp':
combobox.navigate(-1);
event.preventDefault();
break;
case 'n':
if (ctrlBindings && event.ctrlKey) {
combobox.navigate(1);
event.preventDefault();
}
break;
case 'p':
if (ctrlBindings && event.ctrlKey) {
combobox.navigate(-1);
event.preventDefault();
}
break;
default:
if (event.ctrlKey)
break;
combobox.clearSelection();
}
}
function commitWithElement(event) {
if (!(event.target instanceof Element)) return;
const target = event.target.closest('[role="option"]');
if (!target) return;
if (target.getAttribute('aria-disabled') === 'true') return;
fireCommitEvent(target);
if (!(event.target instanceof Element))
return;
const target = event.target.closest('[role="option"]');
if (!target)
return;
if (target.getAttribute('aria-disabled') === 'true')
return;
fireCommitEvent(target);
}
function commit(input, list) {
const target = list.querySelector('[aria-selected="true"]');
if (!target) return false;
if (target.getAttribute('aria-disabled') === 'true') return true;
target.click();
return true;
const target = list.querySelector('[aria-selected="true"]');
if (!target)
return false;
if (target.getAttribute('aria-disabled') === 'true')
return true;
target.click();
return true;
}
function fireCommitEvent(target) {
target.dispatchEvent(new CustomEvent('combobox-commit', {
bubbles: true
}));
target.dispatchEvent(new CustomEvent('combobox-commit', { bubbles: true }));
}
function visible(el) {
return !el.hidden && !(el instanceof HTMLInputElement && el.type === 'hidden') && (el.offsetWidth > 0 || el.offsetHeight > 0);
return (!el.hidden &&
!(el instanceof HTMLInputElement && el.type === 'hidden') &&
(el.offsetWidth > 0 || el.offsetHeight > 0));
}
function trackComposition(event, combobox) {
combobox.isComposing = event.type === 'compositionstart';
const list = document.getElementById(combobox.input.getAttribute('aria-controls') || '');
if (!list) return;
combobox.clearSelection();
combobox.isComposing = event.type === 'compositionstart';
const list = document.getElementById(combobox.input.getAttribute('aria-controls') || '');
if (!list)
return;
combobox.clearSelection();
}
function scrollTo(container, target) {
if (!inViewport(container, target)) {
container.scrollTop = target.offsetTop;
}
if (!inViewport(container, target)) {
container.scrollTop = target.offsetTop;
}
}
function inViewport(container, element) {
const scrollTop = container.scrollTop;
const containerBottom = scrollTop + container.clientHeight;
const top = element.offsetTop;
const bottom = top + element.clientHeight;
return top >= scrollTop && bottom <= containerBottom;
const scrollTop = container.scrollTop;
const containerBottom = scrollTop + container.clientHeight;
const top = element.offsetTop;
const bottom = top + element.clientHeight;
return top >= scrollTop && bottom <= containerBottom;
}
class Autocomplete {
constructor(container, input, results) {
this.container = container;
this.input = input;
this.results = results;
this.combobox = new Combobox(input, results);
this.results.hidden = true;
this.input.setAttribute('autocomplete', 'off');
this.input.setAttribute('spellcheck', 'false');
this.interactingWithList = false;
this.onInputChange = debounce(this.onInputChange.bind(this), 300);
this.onResultsMouseDown = this.onResultsMouseDown.bind(this);
this.onInputBlur = this.onInputBlur.bind(this);
this.onInputFocus = this.onInputFocus.bind(this);
this.onKeydown = this.onKeydown.bind(this);
this.onCommit = this.onCommit.bind(this);
this.input.addEventListener('keydown', this.onKeydown);
this.input.addEventListener('focus', this.onInputFocus);
this.input.addEventListener('blur', this.onInputBlur);
this.input.addEventListener('input', this.onInputChange);
this.results.addEventListener('mousedown', this.onResultsMouseDown);
this.results.addEventListener('combobox-commit', this.onCommit);
}
destroy() {
this.input.removeEventListener('keydown', this.onKeydown);
this.input.removeEventListener('focus', this.onInputFocus);
this.input.removeEventListener('blur', this.onInputBlur);
this.input.removeEventListener('input', this.onInputChange);
this.results.removeEventListener('mousedown', this.onResultsMouseDown);
this.results.removeEventListener('combobox-commit', this.onCommit);
}
onKeydown(event) {
if (event.key === 'Escape' && this.container.open) {
this.container.open = false;
event.stopPropagation();
event.preventDefault();
} else if (event.altKey && event.key === 'ArrowUp' && this.container.open) {
this.container.open = false;
event.stopPropagation();
event.preventDefault();
} else if (event.altKey && event.key === 'ArrowDown' && !this.container.open) {
if (!this.input.value.trim()) return;
this.container.open = true;
event.stopPropagation();
event.preventDefault();
class AutocompleteEvent extends CustomEvent {
constructor(type, init) {
super(type, init);
this.relatedTarget = init.relatedTarget;
}
}
}
onInputFocus() {
this.fetchResults();
}
function debounce(callback, wait = 0) {
let timeout;
return function (...Rest) {
clearTimeout(timeout);
timeout = window.setTimeout(() => {
clearTimeout(timeout);
callback(...Rest);
}, wait);
};
}
onInputBlur() {
if (this.interactingWithList) {
this.interactingWithList = false;
return;
}
const requests = new WeakMap();
function fragment(el, url) {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.setRequestHeader('Accept', 'text/fragment+html');
return request(el, xhr);
}
function request(el, xhr) {
const pending = requests.get(el);
if (pending)
pending.abort();
requests.set(el, xhr);
const clear = () => requests.delete(el);
const result = send(xhr);
result.then(clear, clear);
return result;
}
function send(xhr) {
return new Promise((resolve, reject) => {
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.responseText);
}
else {
reject(new Error(xhr.responseText));
}
};
xhr.onerror = reject;
xhr.send();
});
}
this.container.open = false;
}
onCommit(_ref) {
let {
target
} = _ref;
const selected = target;
if (!(selected instanceof HTMLElement)) return;
this.container.open = false;
if (selected instanceof HTMLAnchorElement) return;
const value = selected.getAttribute('data-autocomplete-value') || selected.textContent;
this.container.value = value;
}
onResultsMouseDown() {
this.interactingWithList = true;
}
onInputChange() {
this.container.removeAttribute('value');
this.fetchResults();
}
identifyOptions() {
let id = 0;
for (const el of this.results.querySelectorAll('[role="option"]:not([id])')) {
el.id = "".concat(this.results.id, "-option-").concat(id++);
class Autocomplete {
constructor(container, input, results) {
this.container = container;
this.input = input;
this.results = results;
this.combobox = new Combobox(input, results);
this.results.hidden = true;
this.input.setAttribute('autocomplete', 'off');
this.input.setAttribute('spellcheck', 'false');
this.interactingWithList = false;
this.onInputChange = debounce(this.onInputChange.bind(this), 300);
this.onResultsMouseDown = this.onResultsMouseDown.bind(this);
this.onInputBlur = this.onInputBlur.bind(this);
this.onInputFocus = this.onInputFocus.bind(this);
this.onKeydown = this.onKeydown.bind(this);
this.onCommit = this.onCommit.bind(this);
this.input.addEventListener('keydown', this.onKeydown);
this.input.addEventListener('focus', this.onInputFocus);
this.input.addEventListener('blur', this.onInputBlur);
this.input.addEventListener('input', this.onInputChange);
this.results.addEventListener('mousedown', this.onResultsMouseDown);
this.results.addEventListener('combobox-commit', this.onCommit);
}
}
fetchResults() {
const query = this.input.value.trim();
if (!query) {
this.container.open = false;
return;
destroy() {
this.input.removeEventListener('keydown', this.onKeydown);
this.input.removeEventListener('focus', this.onInputFocus);
this.input.removeEventListener('blur', this.onInputBlur);
this.input.removeEventListener('input', this.onInputChange);
this.results.removeEventListener('mousedown', this.onResultsMouseDown);
this.results.removeEventListener('combobox-commit', this.onCommit);
}
const src = this.container.src;
if (!src) return;
const url = new URL(src, window.location.href);
const params = new URLSearchParams(url.search.slice(1));
params.append('q', query);
url.search = params.toString();
this.container.dispatchEvent(new CustomEvent('loadstart'));
fragment(this.input, url.toString()).then(html => {
this.results.innerHTML = html;
this.identifyOptions();
const hasResults = !!this.results.querySelector('[role="option"]');
this.container.open = hasResults;
this.container.dispatchEvent(new CustomEvent('load'));
this.container.dispatchEvent(new CustomEvent('loadend'));
}).catch(() => {
this.container.dispatchEvent(new CustomEvent('error'));
this.container.dispatchEvent(new CustomEvent('loadend'));
});
}
open() {
if (!this.results.hidden) return;
this.combobox.start();
this.results.hidden = false;
}
close() {
if (this.results.hidden) return;
this.combobox.stop();
this.results.hidden = true;
}
onKeydown(event) {
if (event.key === 'Escape' && this.container.open) {
this.container.open = false;
event.stopPropagation();
event.preventDefault();
}
else if (event.altKey && event.key === 'ArrowUp' && this.container.open) {
this.container.open = false;
event.stopPropagation();
event.preventDefault();
}
else if (event.altKey && event.key === 'ArrowDown' && !this.container.open) {
if (!this.input.value.trim())
return;
this.container.open = true;
event.stopPropagation();
event.preventDefault();
}
}
onInputFocus() {
this.fetchResults();
}
onInputBlur() {
if (this.interactingWithList) {
this.interactingWithList = false;
return;
}
this.container.open = false;
}
onCommit({ target }) {
const selected = target;
if (!(selected instanceof HTMLElement))
return;
this.container.open = false;
if (selected instanceof HTMLAnchorElement)
return;
const value = selected.getAttribute('data-autocomplete-value') || selected.textContent;
this.container.value = value;
}
onResultsMouseDown() {
this.interactingWithList = true;
}
onInputChange() {
this.container.removeAttribute('value');
this.fetchResults();
}
identifyOptions() {
let id = 0;
for (const el of this.results.querySelectorAll('[role="option"]:not([id])')) {
el.id = `${this.results.id}-option-${id++}`;
}
}
fetchResults() {
const query = this.input.value.trim();
if (!query) {
this.container.open = false;
return;
}
const src = this.container.src;
if (!src)
return;
const url = new URL(src, window.location.href);
const params = new URLSearchParams(url.search.slice(1));
params.append('q', query);
url.search = params.toString();
this.container.dispatchEvent(new CustomEvent('loadstart'));
fragment(this.input, url.toString())
.then(html => {
this.results.innerHTML = html;
this.identifyOptions();
const hasResults = !!this.results.querySelector('[role="option"]');
this.container.open = hasResults;
this.container.dispatchEvent(new CustomEvent('load'));
this.container.dispatchEvent(new CustomEvent('loadend'));
})
.catch(() => {
this.container.dispatchEvent(new CustomEvent('error'));
this.container.dispatchEvent(new CustomEvent('loadend'));
});
}
open() {
if (!this.results.hidden)
return;
this.combobox.start();
this.results.hidden = false;
}
close() {
if (this.results.hidden)
return;
this.combobox.stop();
this.results.hidden = true;
}
}

@@ -387,85 +350,75 @@

class AutocompleteElement extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
const listId = this.getAttribute('for');
if (!listId) return;
const input = this.querySelector('input');
const results = document.getElementById(listId);
if (!(input instanceof HTMLInputElement) || !results) return;
state.set(this, new Autocomplete(this, input, results));
results.setAttribute('role', 'listbox');
}
disconnectedCallback() {
const autocomplete = state.get(this);
if (autocomplete) {
autocomplete.destroy();
state.delete(this);
constructor() {
super();
}
}
get src() {
return this.getAttribute('src') || '';
}
set src(url) {
this.setAttribute('src', url);
}
get value() {
return this.getAttribute('value') || '';
}
set value(value) {
this.setAttribute('value', value);
}
get open() {
return this.hasAttribute('open');
}
set open(value) {
if (value) {
this.setAttribute('open', '');
} else {
this.removeAttribute('open');
connectedCallback() {
const listId = this.getAttribute('for');
if (!listId)
return;
const input = this.querySelector('input');
const results = document.getElementById(listId);
if (!(input instanceof HTMLInputElement) || !results)
return;
state.set(this, new Autocomplete(this, input, results));
results.setAttribute('role', 'listbox');
}
}
static get observedAttributes() {
return ['open', 'value'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue === newValue) return;
const autocomplete = state.get(this);
if (!autocomplete) return;
switch (name) {
case 'open':
newValue === null ? autocomplete.close() : autocomplete.open();
break;
case 'value':
if (newValue !== null) {
autocomplete.input.value = newValue;
disconnectedCallback() {
const autocomplete = state.get(this);
if (autocomplete) {
autocomplete.destroy();
state.delete(this);
}
this.dispatchEvent(new AutocompleteEvent('auto-complete-change', {
bubbles: true,
relatedTarget: autocomplete.input
}));
break;
}
}
get src() {
return this.getAttribute('src') || '';
}
set src(url) {
this.setAttribute('src', url);
}
get value() {
return this.getAttribute('value') || '';
}
set value(value) {
this.setAttribute('value', value);
}
get open() {
return this.hasAttribute('open');
}
set open(value) {
if (value) {
this.setAttribute('open', '');
}
else {
this.removeAttribute('open');
}
}
static get observedAttributes() {
return ['open', 'value'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue === newValue)
return;
const autocomplete = state.get(this);
if (!autocomplete)
return;
switch (name) {
case 'open':
newValue === null ? autocomplete.close() : autocomplete.open();
break;
case 'value':
if (newValue !== null) {
autocomplete.input.value = newValue;
}
this.dispatchEvent(new AutocompleteEvent('auto-complete-change', {
bubbles: true,
relatedTarget: autocomplete.input
}));
break;
}
}
}
if (!window.customElements.get('auto-complete')) {
window.AutocompleteElement = AutocompleteElement;
window.customElements.define('auto-complete', AutocompleteElement);
window.AutocompleteElement = AutocompleteElement;
window.customElements.define('auto-complete', AutocompleteElement);
}

@@ -472,0 +425,0 @@

import Combobox from '@github/combobox-nav';
class AutocompleteEvent extends CustomEvent {
constructor(type, init) {
super(type, init);
this.relatedTarget = init.relatedTarget;
}
constructor(type, init) {
super(type, init);
this.relatedTarget = init.relatedTarget;
}
}
function debounce(callback, wait) {
let timeout;
return function () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
clearTimeout(timeout);
timeout = setTimeout(() => {
clearTimeout(timeout);
callback(...args);
}, wait);
};
function debounce(callback, wait = 0) {
let timeout;
return function (...Rest) {
clearTimeout(timeout);
timeout = window.setTimeout(() => {
clearTimeout(timeout);
callback(...Rest);
}, wait);
};
}

@@ -28,167 +23,155 @@

function fragment(el, url) {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.setRequestHeader('Accept', 'text/fragment+html');
return request(el, xhr);
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.setRequestHeader('Accept', 'text/fragment+html');
return request(el, xhr);
}
function request(el, xhr) {
const pending = requests.get(el);
if (pending) pending.abort();
requests.set(el, xhr);
const clear = () => requests.delete(el);
const result = send(xhr);
result.then(clear, clear);
return result;
const pending = requests.get(el);
if (pending)
pending.abort();
requests.set(el, xhr);
const clear = () => requests.delete(el);
const result = send(xhr);
result.then(clear, clear);
return result;
}
function send(xhr) {
return new Promise((resolve, reject) => {
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.responseText);
} else {
reject(new Error(xhr.responseText));
}
};
xhr.onerror = reject;
xhr.send();
});
return new Promise((resolve, reject) => {
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.responseText);
}
else {
reject(new Error(xhr.responseText));
}
};
xhr.onerror = reject;
xhr.send();
});
}
class Autocomplete {
constructor(container, input, results) {
this.container = container;
this.input = input;
this.results = results;
this.combobox = new Combobox(input, results);
this.results.hidden = true;
this.input.setAttribute('autocomplete', 'off');
this.input.setAttribute('spellcheck', 'false');
this.interactingWithList = false;
this.onInputChange = debounce(this.onInputChange.bind(this), 300);
this.onResultsMouseDown = this.onResultsMouseDown.bind(this);
this.onInputBlur = this.onInputBlur.bind(this);
this.onInputFocus = this.onInputFocus.bind(this);
this.onKeydown = this.onKeydown.bind(this);
this.onCommit = this.onCommit.bind(this);
this.input.addEventListener('keydown', this.onKeydown);
this.input.addEventListener('focus', this.onInputFocus);
this.input.addEventListener('blur', this.onInputBlur);
this.input.addEventListener('input', this.onInputChange);
this.results.addEventListener('mousedown', this.onResultsMouseDown);
this.results.addEventListener('combobox-commit', this.onCommit);
}
destroy() {
this.input.removeEventListener('keydown', this.onKeydown);
this.input.removeEventListener('focus', this.onInputFocus);
this.input.removeEventListener('blur', this.onInputBlur);
this.input.removeEventListener('input', this.onInputChange);
this.results.removeEventListener('mousedown', this.onResultsMouseDown);
this.results.removeEventListener('combobox-commit', this.onCommit);
}
onKeydown(event) {
if (event.key === 'Escape' && this.container.open) {
this.container.open = false;
event.stopPropagation();
event.preventDefault();
} else if (event.altKey && event.key === 'ArrowUp' && this.container.open) {
this.container.open = false;
event.stopPropagation();
event.preventDefault();
} else if (event.altKey && event.key === 'ArrowDown' && !this.container.open) {
if (!this.input.value.trim()) return;
this.container.open = true;
event.stopPropagation();
event.preventDefault();
constructor(container, input, results) {
this.container = container;
this.input = input;
this.results = results;
this.combobox = new Combobox(input, results);
this.results.hidden = true;
this.input.setAttribute('autocomplete', 'off');
this.input.setAttribute('spellcheck', 'false');
this.interactingWithList = false;
this.onInputChange = debounce(this.onInputChange.bind(this), 300);
this.onResultsMouseDown = this.onResultsMouseDown.bind(this);
this.onInputBlur = this.onInputBlur.bind(this);
this.onInputFocus = this.onInputFocus.bind(this);
this.onKeydown = this.onKeydown.bind(this);
this.onCommit = this.onCommit.bind(this);
this.input.addEventListener('keydown', this.onKeydown);
this.input.addEventListener('focus', this.onInputFocus);
this.input.addEventListener('blur', this.onInputBlur);
this.input.addEventListener('input', this.onInputChange);
this.results.addEventListener('mousedown', this.onResultsMouseDown);
this.results.addEventListener('combobox-commit', this.onCommit);
}
}
onInputFocus() {
this.fetchResults();
}
onInputBlur() {
if (this.interactingWithList) {
this.interactingWithList = false;
return;
destroy() {
this.input.removeEventListener('keydown', this.onKeydown);
this.input.removeEventListener('focus', this.onInputFocus);
this.input.removeEventListener('blur', this.onInputBlur);
this.input.removeEventListener('input', this.onInputChange);
this.results.removeEventListener('mousedown', this.onResultsMouseDown);
this.results.removeEventListener('combobox-commit', this.onCommit);
}
this.container.open = false;
}
onCommit(_ref) {
let {
target
} = _ref;
const selected = target;
if (!(selected instanceof HTMLElement)) return;
this.container.open = false;
if (selected instanceof HTMLAnchorElement) return;
const value = selected.getAttribute('data-autocomplete-value') || selected.textContent;
this.container.value = value;
}
onResultsMouseDown() {
this.interactingWithList = true;
}
onInputChange() {
this.container.removeAttribute('value');
this.fetchResults();
}
identifyOptions() {
let id = 0;
for (const el of this.results.querySelectorAll('[role="option"]:not([id])')) {
el.id = "".concat(this.results.id, "-option-").concat(id++);
onKeydown(event) {
if (event.key === 'Escape' && this.container.open) {
this.container.open = false;
event.stopPropagation();
event.preventDefault();
}
else if (event.altKey && event.key === 'ArrowUp' && this.container.open) {
this.container.open = false;
event.stopPropagation();
event.preventDefault();
}
else if (event.altKey && event.key === 'ArrowDown' && !this.container.open) {
if (!this.input.value.trim())
return;
this.container.open = true;
event.stopPropagation();
event.preventDefault();
}
}
}
fetchResults() {
const query = this.input.value.trim();
if (!query) {
this.container.open = false;
return;
onInputFocus() {
this.fetchResults();
}
const src = this.container.src;
if (!src) return;
const url = new URL(src, window.location.href);
const params = new URLSearchParams(url.search.slice(1));
params.append('q', query);
url.search = params.toString();
this.container.dispatchEvent(new CustomEvent('loadstart'));
fragment(this.input, url.toString()).then(html => {
this.results.innerHTML = html;
this.identifyOptions();
const hasResults = !!this.results.querySelector('[role="option"]');
this.container.open = hasResults;
this.container.dispatchEvent(new CustomEvent('load'));
this.container.dispatchEvent(new CustomEvent('loadend'));
}).catch(() => {
this.container.dispatchEvent(new CustomEvent('error'));
this.container.dispatchEvent(new CustomEvent('loadend'));
});
}
open() {
if (!this.results.hidden) return;
this.combobox.start();
this.results.hidden = false;
}
close() {
if (this.results.hidden) return;
this.combobox.stop();
this.results.hidden = true;
}
onInputBlur() {
if (this.interactingWithList) {
this.interactingWithList = false;
return;
}
this.container.open = false;
}
onCommit({ target }) {
const selected = target;
if (!(selected instanceof HTMLElement))
return;
this.container.open = false;
if (selected instanceof HTMLAnchorElement)
return;
const value = selected.getAttribute('data-autocomplete-value') || selected.textContent;
this.container.value = value;
}
onResultsMouseDown() {
this.interactingWithList = true;
}
onInputChange() {
this.container.removeAttribute('value');
this.fetchResults();
}
identifyOptions() {
let id = 0;
for (const el of this.results.querySelectorAll('[role="option"]:not([id])')) {
el.id = `${this.results.id}-option-${id++}`;
}
}
fetchResults() {
const query = this.input.value.trim();
if (!query) {
this.container.open = false;
return;
}
const src = this.container.src;
if (!src)
return;
const url = new URL(src, window.location.href);
const params = new URLSearchParams(url.search.slice(1));
params.append('q', query);
url.search = params.toString();
this.container.dispatchEvent(new CustomEvent('loadstart'));
fragment(this.input, url.toString())
.then(html => {
this.results.innerHTML = html;
this.identifyOptions();
const hasResults = !!this.results.querySelector('[role="option"]');
this.container.open = hasResults;
this.container.dispatchEvent(new CustomEvent('load'));
this.container.dispatchEvent(new CustomEvent('loadend'));
})
.catch(() => {
this.container.dispatchEvent(new CustomEvent('error'));
this.container.dispatchEvent(new CustomEvent('loadend'));
});
}
open() {
if (!this.results.hidden)
return;
this.combobox.start();
this.results.hidden = false;
}
close() {
if (this.results.hidden)
return;
this.combobox.stop();
this.results.hidden = true;
}
}

@@ -198,85 +181,75 @@

class AutocompleteElement extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
const listId = this.getAttribute('for');
if (!listId) return;
const input = this.querySelector('input');
const results = document.getElementById(listId);
if (!(input instanceof HTMLInputElement) || !results) return;
state.set(this, new Autocomplete(this, input, results));
results.setAttribute('role', 'listbox');
}
disconnectedCallback() {
const autocomplete = state.get(this);
if (autocomplete) {
autocomplete.destroy();
state.delete(this);
constructor() {
super();
}
}
get src() {
return this.getAttribute('src') || '';
}
set src(url) {
this.setAttribute('src', url);
}
get value() {
return this.getAttribute('value') || '';
}
set value(value) {
this.setAttribute('value', value);
}
get open() {
return this.hasAttribute('open');
}
set open(value) {
if (value) {
this.setAttribute('open', '');
} else {
this.removeAttribute('open');
connectedCallback() {
const listId = this.getAttribute('for');
if (!listId)
return;
const input = this.querySelector('input');
const results = document.getElementById(listId);
if (!(input instanceof HTMLInputElement) || !results)
return;
state.set(this, new Autocomplete(this, input, results));
results.setAttribute('role', 'listbox');
}
}
static get observedAttributes() {
return ['open', 'value'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue === newValue) return;
const autocomplete = state.get(this);
if (!autocomplete) return;
switch (name) {
case 'open':
newValue === null ? autocomplete.close() : autocomplete.open();
break;
case 'value':
if (newValue !== null) {
autocomplete.input.value = newValue;
disconnectedCallback() {
const autocomplete = state.get(this);
if (autocomplete) {
autocomplete.destroy();
state.delete(this);
}
this.dispatchEvent(new AutocompleteEvent('auto-complete-change', {
bubbles: true,
relatedTarget: autocomplete.input
}));
break;
}
}
get src() {
return this.getAttribute('src') || '';
}
set src(url) {
this.setAttribute('src', url);
}
get value() {
return this.getAttribute('value') || '';
}
set value(value) {
this.setAttribute('value', value);
}
get open() {
return this.hasAttribute('open');
}
set open(value) {
if (value) {
this.setAttribute('open', '');
}
else {
this.removeAttribute('open');
}
}
static get observedAttributes() {
return ['open', 'value'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue === newValue)
return;
const autocomplete = state.get(this);
if (!autocomplete)
return;
switch (name) {
case 'open':
newValue === null ? autocomplete.close() : autocomplete.open();
break;
case 'value':
if (newValue !== null) {
autocomplete.input.value = newValue;
}
this.dispatchEvent(new AutocompleteEvent('auto-complete-change', {
bubbles: true,
relatedTarget: autocomplete.input
}));
break;
}
}
}
if (!window.customElements.get('auto-complete')) {
window.AutocompleteElement = AutocompleteElement;
window.customElements.define('auto-complete', AutocompleteElement);
window.AutocompleteElement = AutocompleteElement;
window.customElements.define('auto-complete', AutocompleteElement);
}

@@ -283,0 +256,0 @@

{
"name": "@github/auto-complete-element",
"version": "3.0.1",
"version": "3.0.2",
"description": "Auto-complete input values from server results",

@@ -9,8 +9,8 @@ "repository": "github/auto-complete-element",

"module": "dist/index.js",
"types": "index.d.ts",
"types": "dist/index.d.ts",
"scripts": {
"clean": "rm -rf dist",
"lint": "github-lint",
"lint": "eslint . --ext .ts,.js && tsc --noEmit",
"prebuild": "npm run clean && npm run lint",
"build": "rollup -c && cp src/index.js.flow dist/index.esm.js.flow && cp src/index.js.flow dist/index.umd.js.flow",
"build": "tsc && rollup -c",
"pretest": "npm run build",

@@ -27,4 +27,3 @@ "test": "karma start test/karma.config.cjs",

"files": [
"dist",
"index.d.ts"
"dist"
],

@@ -35,9 +34,6 @@ "dependencies": {

"devDependencies": {
"@babel/core": "^7.7.0",
"@github/prettier-config": "0.0.4",
"babel-preset-github": "^3.2.1",
"chai": "^4.2.0",
"eslint": "^6.6.0",
"eslint-plugin-github": "^3.2.1",
"flow-bin": "^0.111.1",
"eslint-plugin-github": "^4.0.1",
"karma": "^5.0.4",

@@ -49,5 +45,5 @@ "karma-chai": "^0.1.0",

"mocha": "^7.1.2",
"rollup": "^1.26.3",
"rollup-plugin-babel": "^4.3.3",
"rollup-plugin-node-resolve": "^5.2.0"
"rollup": "^2.12.0",
"rollup-plugin-node-resolve": "^5.2.0",
"typescript": "^3.9.3"
},

@@ -54,0 +50,0 @@ "eslintIgnore": [

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc