(function(window) { /*jshint maxlen:false*/ 'use strict'; var proto = Object.create(HTMLElement.prototype); proto.HEIGHT = 52; // 12px margin + 40px height // #form height attribute is the value of proto.HEIGHT proto.createdCallback = function() { var shadowRoot = this.createShadowRoot(); shadowRoot.innerHTML = `
`; var $id = shadowRoot.getElementById.bind(shadowRoot); this.els = { inner: $id('inner'), input: $id('input'), close: $id('close') }; this.isOpen = false; this.els.results = document.querySelector(this.getAttribute('results')); var onSearch = debounce(() => { this.emit('search', this.els.input.value); }, 500); this.els.input.addEventListener('focus', () => this.open()); this.els.close.addEventListener('click', () => this.close()); this.els.input.addEventListener('input', onSearch); this.addEventListener('touchmove', e => e.preventDefault()); /** * Localizes the component's DOM. * * We can't use data-l10n-id attributes * here as l10n.js doesn't support putting * `placeholder` attribute on any element * other than ``. * * Because we have to use `.formatValues()` it * makes sense to localize `search-close` in * the same way. * * @private */ this.localize = () => { document.l10n.formatValues('search', 'search-close') .then(([search, close]) => { this.els.input.placeholder = search; this.els.close.textContent = close; }); }; }; proto.attachedCallback = function() { document.addEventListener('DOMRetranslated', this.localize); this.localize(); }; proto.detachedCallback = function() { document.removeEventListener('DOMRetranslated', this.localize); }; proto.attributeChangedCallback = function(attr, oldVal, newVal) { switch (attr) { case 'results': this.els.results = document.querySelector(newVal); break; } }; proto.clear = function() { requestAnimationFrame(() => { this.els.input.clear(); }); }; proto.focus = function() { requestAnimationFrame(() => { this.els.input.focus(); }); }; proto.open = function() { if (this.isOpen) { return; } this.isOpen = true; this.els.inner.classList.add('open'); this.focus(); this.emit('open'); if (this.els.results) { this.els.results.open(); } }; proto.close = function() { if (!this.isOpen) { return; } this.isOpen = false; this.els.inner.classList.remove('open'); this.clear(); this.emit('close'); if (this.els.results) { this.els.results.close(); } }; proto.emit = function(name, detail) { var evt = new CustomEvent(name, { detail: detail }); this.dispatchEvent(evt); }; Object.defineProperty(proto, 'value', { get: function() { return this.els.input.value; }, set: function(value) { this.els.input.value = value; } }); Object.defineProperty(proto, 'results', { get: function() { return this.els.results; }, set: function(value) { this.els.results = value; } }); window.MusicSearchBox = document.registerElement('music-search-box', { prototype: proto }); /** * Utils */ function debounce(fn, ms) { var timeout; return () => { var args = arguments; clearTimeout(timeout); timeout = setTimeout(() => fn.apply(this, args), ms); }; } })(window);