import { randomString, create } from './utils.js'; import { Values } from './values.js'; export class TagInput { constructor(original, options = {}) { this.original = original; this.id = options.id || randomString(8); this.inputClass = options.inputClass || original.dataset.tagsInputClass; this.valueClass = options.valueClass || original.dataset.tagsValueClass; this.valueFocusClass = options.valueFocusClass || original.dataset.tagsValueFocusClass; this.separators = options.separators || (original.dataset.tagsSeparators || 'Enter').split(/\s+/); this.createElements(); original.hidden = true; original.before(this.wrapper); this.updateValue(); } createElements() { this.wrapper = create('
'); this.inputWrapper = create('
'); this.wrapper.append(this.inputWrapper); this.input = document.createElement('input'); this.input.className = this.inputClass || ''; var labels = Array.from(this.original.labels).map(label => { label.id = label.id || randomString(8); return label.id; }).join(' '); this.input.setAttribute('aria-labelledby', labels); this.inputWrapper.append(this.input); this.values = new Values(this.input, `${this.id}-values`, this.valueClass, this.valueFocusClass); this.input.setAttribute('aria-controls', this.values.el.id); this.datalist = document.createElement('datalist'); this.datalist.innerHTML = this.original.innerHTML; this.datalist.id = `${this.id}-list`; this.input.setAttribute('list', this.datalist.id); this.inputWrapper.append(this.datalist); this.input.disabled = this.original.disabled; this.input.onkeydown = this.onkeydown.bind(this); this.input.onchange = this.onchange.bind(this); this.original.oninvalid = () => { this.input.setCustomValidity(this.original.validationMessage); this.input.reportValiditiy(); }; this.original.onchange = () => { this.input.setCustomValidity(this.original.validationMessage); }; } updateValue() { this.input.value = ''; this.values.update(this.original, () => { this.updateValue(); this.input.focus(); }); } setValue(value) { var option = Array.from(this.original.options).find(op => op.value === value); if (!option) { option = document.createElement('option'); option.setAttribute('data-tag-custom', ''); option.value = value; option.label = value; this.original.append(option); } option.selected = true; this.original.dispatchEvent(new Event('change')); this.updateValue(); } onkeydown(event) { if (this.input.value) { if (this.separators.includes(event.key)) { this.onchange(event); } } else { if (this.values.onkeydown(event)) { // nothing to do } else if (event.key === 'Backspace') { event.preventDefault(); var n = this.original.selectedOptions.length; if (n) { var op = this.original.selectedOptions[n - 1]; op.selected = false; this.updateValue(); this.input.value = op.value; this.input.dispatchEvent(new Event('input')); } } } } onchange(event) { if (this.input.value.trim()) { event.preventDefault(); this.setValue(this.input.value.trim()); } } } Array.from(document.querySelectorAll('[data-tags]')).forEach(el => { new TagInput(el); });