Socket
Socket
Sign inDemoInstall

@github/auto-complete-element

Package Overview
Dependencies
Maintainers
13
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 0.1.2 to 0.2.0

171

dist/index.esm.js

@@ -54,2 +54,16 @@ function debounce(callback, wait) {

function scrollTo(container, target) {
if (!inViewport(container, target)) {
container.scrollTop = target.offsetTop;
}
}
function inViewport(container, element) {
var scrollTop = container.scrollTop;
var containerBottom = scrollTop + container.clientHeight;
var top = element.offsetTop;
var bottom = top + element.clientHeight;
return top >= scrollTop && bottom <= containerBottom;
}
var classCallCheck = function (instance, Constructor) {

@@ -103,4 +117,6 @@ if (!(instance instanceof Constructor)) {

var ctrlBindings = navigator.userAgent.match(/Macintosh/);
var Autocomplete = function () {
function Autocomplete(container, input, results, list) {
function Autocomplete(container, input, results) {
classCallCheck(this, Autocomplete);

@@ -111,3 +127,2 @@

this.results = results;
this.list = list;

@@ -121,2 +136,3 @@ this.results.hidden = true;

this.onInputChange = debounce(this.onInputChange.bind(this), 300);
this.onResultsClick = this.onResultsClick.bind(this);
this.onResultsMouseDown = this.onResultsMouseDown.bind(this);

@@ -132,2 +148,3 @@ this.onInputBlur = this.onInputBlur.bind(this);

this.results.addEventListener('mousedown', this.onResultsMouseDown);
this.results.addEventListener('click', this.onResultsClick);
}

@@ -143,8 +160,83 @@

this.results.removeEventListener('mousedown', this.onResultsMouseDown);
this.results.removeEventListener('click', this.onResultsClick);
}
}, {
key: 'sibling',
value: function sibling(next) {
var options = Array.from(this.results.querySelectorAll('[role="option"]'));
var selected = this.results.querySelector('[aria-selected="true"]');
var index = options.indexOf(selected);
var sibling = next ? options[index + 1] : options[index - 1];
var def = next ? options[0] : options[options.length - 1];
return sibling || def;
}
}, {
key: 'select',
value: function select(target) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = this.results.querySelectorAll('[aria-selected="true"]')[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var el = _step.value;
el.removeAttribute('aria-selected');
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
target.setAttribute('aria-selected', 'true');
this.input.setAttribute('aria-activedescendant', target.id);
scrollTo(this.results, target);
}
}, {
key: 'onKeydown',
value: function onKeydown(event) {
if (event.key === 'Escape') {
this.container.open = false;
switch (event.key) {
case 'Escape':
this.container.open = false;
event.preventDefault();
break;
case 'ArrowDown':
this.select(this.sibling(true));
event.preventDefault();
break;
case 'ArrowUp':
this.select(this.sibling(false));
event.preventDefault();
break;
case 'n':
if (ctrlBindings && event.ctrlKey) {
this.select(this.sibling(true));
event.preventDefault();
}
break;
case 'p':
if (ctrlBindings && event.ctrlKey) {
this.select(this.sibling(false));
event.preventDefault();
}
break;
case 'Enter':
{
var selected = this.results.querySelector('[aria-selected="true"]');
if (selected) {
this.commit(selected);
event.preventDefault();
}
}
break;
}

@@ -164,2 +256,17 @@ }

}, {
key: 'commit',
value: function commit(selected) {
if (selected.getAttribute('aria-disabled') === 'true') return;
var value = selected.getAttribute('data-autocomplete-value') || selected.textContent;
this.container.value = value;
this.container.open = false;
}
}, {
key: 'onResultsClick',
value: function onResultsClick(event) {
if (!(event.target instanceof Element)) return;
var selected = event.target.closest('[role="option"]');
if (selected) this.commit(selected);
}
}, {
key: 'onResultsMouseDown',

@@ -181,2 +288,31 @@ value: function onResultsMouseDown() {

}, {
key: 'identifyOptions',
value: function identifyOptions() {
var id = 0;
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = this.results.querySelectorAll('[role="option"]:not([id])')[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var el = _step2.value;
el.id = this.results.id + '-option-' + id++;
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
}
}, {
key: 'fetchResults',

@@ -202,3 +338,4 @@ value: function fetchResults() {

fragment(this.input, url.toString()).then(function (html) {
_this2.list.innerHTML = html;
_this2.results.innerHTML = html;
_this2.identifyOptions();
var hasResults = !!_this2.results.querySelector('[data-autocomplete-value]');

@@ -218,2 +355,3 @@ _this2.container.open = hasResults;

positionBelow(this.input, this.results);
this.container.setAttribute('aria-expanded', 'true');
this.container.dispatchEvent(new CustomEvent('toggle', { detail: { input: this.input, results: this.results } }));

@@ -226,2 +364,4 @@ }

this.results.hidden = true;
this.input.removeAttribute('aria-activedescendant');
this.container.setAttribute('aria-expanded', 'false');
this.container.dispatchEvent(new CustomEvent('toggle', { detail: { input: this.input, results: this.results } }));

@@ -277,7 +417,18 @@ }

value: function connectedCallback() {
var input = this.querySelector('input[slot="field"]');
var results = this.querySelector('[slot="popup"]');
var list = this.querySelector('[slot="results"]');
if (!(input instanceof HTMLInputElement) || !results || !list) return;
state.set(this, new Autocomplete(this, input, results, list));
var owns = this.getAttribute('aria-owns');
if (!owns) return;
var input = this.querySelector('input');
var results = document.getElementById(owns);
if (!(input instanceof HTMLInputElement) || !results) return;
state.set(this, new Autocomplete(this, input, results));
this.setAttribute('role', 'combobox');
this.setAttribute('aria-haspopup', 'listbox');
this.setAttribute('aria-expanded', 'false');
input.setAttribute('aria-autocomplete', 'list');
input.setAttribute('aria-controls', owns);
results.setAttribute('role', 'listbox');
}

@@ -284,0 +435,0 @@ }, {

@@ -60,2 +60,16 @@ (function (global, factory) {

function scrollTo(container, target) {
if (!inViewport(container, target)) {
container.scrollTop = target.offsetTop;
}
}
function inViewport(container, element) {
var scrollTop = container.scrollTop;
var containerBottom = scrollTop + container.clientHeight;
var top = element.offsetTop;
var bottom = top + element.clientHeight;
return top >= scrollTop && bottom <= containerBottom;
}
var classCallCheck = function (instance, Constructor) {

@@ -109,4 +123,6 @@ if (!(instance instanceof Constructor)) {

var ctrlBindings = navigator.userAgent.match(/Macintosh/);
var Autocomplete = function () {
function Autocomplete(container, input, results, list) {
function Autocomplete(container, input, results) {
classCallCheck(this, Autocomplete);

@@ -117,3 +133,2 @@

this.results = results;
this.list = list;

@@ -127,2 +142,3 @@ this.results.hidden = true;

this.onInputChange = debounce(this.onInputChange.bind(this), 300);
this.onResultsClick = this.onResultsClick.bind(this);
this.onResultsMouseDown = this.onResultsMouseDown.bind(this);

@@ -138,2 +154,3 @@ this.onInputBlur = this.onInputBlur.bind(this);

this.results.addEventListener('mousedown', this.onResultsMouseDown);
this.results.addEventListener('click', this.onResultsClick);
}

@@ -149,8 +166,83 @@

this.results.removeEventListener('mousedown', this.onResultsMouseDown);
this.results.removeEventListener('click', this.onResultsClick);
}
}, {
key: 'sibling',
value: function sibling(next) {
var options = Array.from(this.results.querySelectorAll('[role="option"]'));
var selected = this.results.querySelector('[aria-selected="true"]');
var index = options.indexOf(selected);
var sibling = next ? options[index + 1] : options[index - 1];
var def = next ? options[0] : options[options.length - 1];
return sibling || def;
}
}, {
key: 'select',
value: function select(target) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = this.results.querySelectorAll('[aria-selected="true"]')[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var el = _step.value;
el.removeAttribute('aria-selected');
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
target.setAttribute('aria-selected', 'true');
this.input.setAttribute('aria-activedescendant', target.id);
scrollTo(this.results, target);
}
}, {
key: 'onKeydown',
value: function onKeydown(event) {
if (event.key === 'Escape') {
this.container.open = false;
switch (event.key) {
case 'Escape':
this.container.open = false;
event.preventDefault();
break;
case 'ArrowDown':
this.select(this.sibling(true));
event.preventDefault();
break;
case 'ArrowUp':
this.select(this.sibling(false));
event.preventDefault();
break;
case 'n':
if (ctrlBindings && event.ctrlKey) {
this.select(this.sibling(true));
event.preventDefault();
}
break;
case 'p':
if (ctrlBindings && event.ctrlKey) {
this.select(this.sibling(false));
event.preventDefault();
}
break;
case 'Enter':
{
var selected = this.results.querySelector('[aria-selected="true"]');
if (selected) {
this.commit(selected);
event.preventDefault();
}
}
break;
}

@@ -170,2 +262,17 @@ }

}, {
key: 'commit',
value: function commit(selected) {
if (selected.getAttribute('aria-disabled') === 'true') return;
var value = selected.getAttribute('data-autocomplete-value') || selected.textContent;
this.container.value = value;
this.container.open = false;
}
}, {
key: 'onResultsClick',
value: function onResultsClick(event) {
if (!(event.target instanceof Element)) return;
var selected = event.target.closest('[role="option"]');
if (selected) this.commit(selected);
}
}, {
key: 'onResultsMouseDown',

@@ -187,2 +294,31 @@ value: function onResultsMouseDown() {

}, {
key: 'identifyOptions',
value: function identifyOptions() {
var id = 0;
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = this.results.querySelectorAll('[role="option"]:not([id])')[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var el = _step2.value;
el.id = this.results.id + '-option-' + id++;
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
}
}, {
key: 'fetchResults',

@@ -208,3 +344,4 @@ value: function fetchResults() {

fragment(this.input, url.toString()).then(function (html) {
_this2.list.innerHTML = html;
_this2.results.innerHTML = html;
_this2.identifyOptions();
var hasResults = !!_this2.results.querySelector('[data-autocomplete-value]');

@@ -224,2 +361,3 @@ _this2.container.open = hasResults;

positionBelow(this.input, this.results);
this.container.setAttribute('aria-expanded', 'true');
this.container.dispatchEvent(new CustomEvent('toggle', { detail: { input: this.input, results: this.results } }));

@@ -232,2 +370,4 @@ }

this.results.hidden = true;
this.input.removeAttribute('aria-activedescendant');
this.container.setAttribute('aria-expanded', 'false');
this.container.dispatchEvent(new CustomEvent('toggle', { detail: { input: this.input, results: this.results } }));

@@ -283,7 +423,18 @@ }

value: function connectedCallback() {
var input = this.querySelector('input[slot="field"]');
var results = this.querySelector('[slot="popup"]');
var list = this.querySelector('[slot="results"]');
if (!(input instanceof HTMLInputElement) || !results || !list) return;
state.set(this, new Autocomplete(this, input, results, list));
var owns = this.getAttribute('aria-owns');
if (!owns) return;
var input = this.querySelector('input');
var results = document.getElementById(owns);
if (!(input instanceof HTMLInputElement) || !results) return;
state.set(this, new Autocomplete(this, input, results));
this.setAttribute('role', 'combobox');
this.setAttribute('aria-haspopup', 'listbox');
this.setAttribute('aria-expanded', 'false');
input.setAttribute('aria-autocomplete', 'list');
input.setAttribute('aria-controls', owns);
results.setAttribute('role', 'listbox');
}

@@ -290,0 +441,0 @@ }, {

2

package.json
{
"name": "@github/auto-complete-element",
"version": "0.1.2",
"version": "0.2.0",
"description": "Auto-complete input values from server results",

@@ -5,0 +5,0 @@ "repository": "github/auto-complete-element",

@@ -18,10 +18,17 @@ # &lt;auto-complete&gt; element

```html
<auto-complete src="/users/search">
<input slot="field" type="text" data-autocomplete-autofocus>
<div slot="popup">
<ul slot="results"></ul>
</div>
<auto-complete src="/users/search" aria-owns="users-popup">
<input type="text" data-autocomplete-autofocus>
<ul id="users-popup"></ul>
</auto-complete>
```
The server response should include the items that matched the search query.
```html
<li role="option" data-autocomplete-value="@hubot">Hubot</li>
<li role="option" data-autocomplete-value="@bender">Bender</li>
<li role="option" data-autocomplete-value="@bb-8">BB-8</li>
<li role="option" data-autocomplete-value="@r2d2" aria-disabled="true">R2-D2 (powered down)</li>
```
## Browser support

@@ -28,0 +35,0 @@

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