const textField = document.createElement('template') textField.innerHTML = `
` customElements.define('text-field', class extends HTMLElement { constructor() { super() this.attachShadow({ mode: 'open' }).append(textField.content.cloneNode(true)) this.textField = this.shadowRoot.querySelector('.text-field') this.textContainer = this.textField.children[0] this.editButton = this.textField.querySelector('.edit-button') this.isTextEditable = false this.isDisabled = false this.setEditable = this.setEditable.bind(this) this.setNonEditable = this.setNonEditable.bind(this) this.toggleEditable = this.toggleEditable.bind(this) this.revert = this.revert.bind(this) } static get observedAttributes() { return ['disable'] } get value() { return this.text } set value(val) { this.text = val this.textContainer.textContent = val this.setAttribute('value', val) } set disabled(val) { this.isDisabled = val if (this.isDisabled) this.setAttribute('disable', '') else this.removeAttribute('disable') } setEditable() { if (this.isTextEditable) return this.textContainer.contentEditable = true this.setAttribute('editable', '') this.textContainer.focus() document.execCommand('selectAll', false, null); this.editButton.textContent = 'Done' this.isTextEditable = true } setNonEditable() { if (!this.isTextEditable) return this.textContainer.contentEditable = false this.removeAttribute('editable') if (this.text !== this.textContainer.textContent.trim()) { this.setAttribute('value', this.textContainer.textContent) this.text = this.textContainer.textContent.trim() this.dispatchEvent(new CustomEvent('change', { bubbles: true, cancelable: true, composed: true })); } this.editButton.textContent = 'Edit' this.isTextEditable = false } toggleEditable() { if (this.isTextEditable) this.setNonEditable() else this.setEditable() } revert() { if (this.textContainer.isContentEditable) { this.value = this.text this.setNonEditable() } } connectedCallback() { this.text if (this.hasAttribute('value')) { this.text = this.getAttribute('value') this.textContainer.textContent = this.text } if (this.hasAttribute('disable')) this.isDisabled = true else this.isDisabled = false if (!this.isDisabled) { this.textContainer.addEventListener('dblclick', this.setEditable) this.editButton.addEventListener('click', this.toggleEditable) } } attributeChangedCallback(name) { if (name === 'disable') { if (this.hasAttribute('disable')) { this.editButton.classList.add('hide') this.textContainer.removeEventListener('dblclick', this.setEditable) this.editButton.removeEventListener('click', this.toggleEditable) this.revert() } else { this.editButton.classList.remove('hide') this.textContainer.addEventListener('dblclick', this.setEditable) this.editButton.addEventListener('click', this.toggleEditable) } } } disconnectedCallback() { this.textContainer.removeEventListener('dblclick', this.setEditable) this.editButton.removeEventListener('click', this.toggleEditable) } })