diff --git a/file-group.js b/file-group.js new file mode 100644 index 0000000..a1132a3 --- /dev/null +++ b/file-group.js @@ -0,0 +1,88 @@ +export class FileGroup extends HTMLElement { + textEn = { + addFile: 'Add File', + } + + textEs = { + addFile: 'AƱadir archivo', + } + + constructor() { + super() + this.language = navigator.language + this.attachShadow({mode: 'open'}) + this.headerEl = document.createElement('div') + this.headerEl.classList.add('header') + this.contentEl = document.createElement('div') + this.contentEl.classList.add('content') + this.shadowRoot.appendChild(this.headerEl) + this.shadowRoot.appendChild(this.contentEl) + const bGroup = document.createElement( + 'm-forms-button-group' + ) + bGroup.addPrimary(this.text.addFile, () => { + this.addFile() + const btn = bGroup.primary + if (btn.scrollIntoViewIfNeeded) { + btn.scrollIntoViewIfNeeded() + } else { + btn.scrollIntoView() + } + }) + this.shadowRoot.appendChild(bGroup) + } + + connectedCallback() { + const style = document.createElement('style') + style.textContent = ` + :host { + display: flex; + flex-direction: column; + align-items: stretch; + } + div.header { + display: flex; + flex-direction: row; + } + div.files { + display: flex; + flex-direction: column; + flex-grow: 1; + overflow-y: auto; + } + ` + this.shadowRoot.appendChild(style) + if (this.contentEl.childNodes.length === 0) { + this.addFile() + } + } + + addFile({name, data} = {}) { + const el = document.createElement('m-editor-file-view') + if (name !== undefined) { + el.name = name + } + if (data !== undefined) { + el.data = data + } + this.contentEl.appendChild(el) + return el + } + + get language() { + return this._language + } + + set language(language) { + this._language = language + this.text = this.langEs ? this.textEs : this.textEn + } + + get langEs() { + return /^es\b/.test(this.language) + } + + get files() { + return [...this.contentEl.children] + } +} \ No newline at end of file diff --git a/file-view.js b/file-view.js new file mode 100644 index 0000000..1522f0a --- /dev/null +++ b/file-view.js @@ -0,0 +1,120 @@ +export class FileView extends HTMLElement { + icons = { + menu: ` + + + + `, + } + + textEn = { + delete: 'Delete', + } + + textEs = { + delete: 'Borrar', + } + + constructor() { + super() + this.language = navigator.language + this.attachShadow({mode: 'open'}) + this.headerEl = document.createElement('div') + this.headerEl.classList.add('header') + this.contentEl = document.createElement('div') + this.contentEl.classList.add('content') + this.shadowRoot.appendChild(this.headerEl) + this.shadowRoot.appendChild(this.contentEl) + this.nameEl = document.createElement('input') + this.nameEl.classList.add('name') + this.headerEl.appendChild(this.nameEl) + this.editEl = document.createElement('m-editor-text-edit') + this.contentEl.appendChild(this.editEl) + this.menuBtn = document.createElement('button') + this.menuBtn.innerHTML = this.icons.menu + this.menuBtn.addEventListener('click', () => { + this.menu.open(this.menuBtn) + }) + this.headerEl.appendChild(this.menuBtn) + this.menu = document.createElement( + 'm-menu-dropdown' + ) + this.menu.add('Borrar', () => { + this.remove() + }) + this.shadowRoot.appendChild(this.menu) + } + + connectedCallback() { + const style = document.createElement('style') + style.textContent = ` + :host { + display: flex; + flex-direction: column; + align-items: stretch; + } + div.header { + display: flex; + flex-direction: row; + align-items: stretch; + background-color: #111; + color: #ddd; + padding: 3px 0; + } + div.header > * { + background: inherit; + color: inherit; + border: none; + } + .name { + flex-grow: 1; + padding: 0 5px; + font: inherit; + font-family: monospace; + outline: none; + } + div.header button svg { + margin-bottom: -3px; + } + div.content { + display: flex; + flex-direction: column; + align-items: stretch; + } + svg { + height: 20px; + width: 20px; + } + ` + this.shadowRoot.appendChild(style) + } + + set name(name) { + this.nameEl.value = name + } + + get name() { + return this.nameEl.value + } + + set data(data) { + this.editEl.value = data + } + + get data() { + return this.editEl.value + } + + get language() { + return this._language + } + + set language(language) { + this._language = language + this.text = this.langEs ? this.textEs : this.textEn + } + + get langEs() { + return /^es\b/.test(this.language) + } +} \ No newline at end of file diff --git a/text-edit.js b/text-edit.js new file mode 100644 index 0000000..f32db0b --- /dev/null +++ b/text-edit.js @@ -0,0 +1,58 @@ +export class TextEdit extends HTMLElement { + constructor() { + super() + this.attachShadow({mode: 'open'}) + this.stackEl = document.createElement('div') + this.stackEl.classList.add('stack') + this.textEl = document.createElement('textarea') + this.textEl.classList.add('text') + this.textEl.rows = 1 + this.stackEl.appendChild(this.textEl) + this.shadowRoot.appendChild(this.stackEl) + this.textEl.addEventListener('input', () => { + this.stackEl.dataset.copy = this.textEl.value + }) + } + + connectedCallback() { + // https://css-tricks.com/the-cleanest-trick-for-autogrowing-textareas/ + const style = document.createElement('style') + style.textContent = ` + :host { + display: flex; + flex-direction: column; + align-items: stretch; + margin: 5px 0; + } + div.stack { + display: grid; + } + div.stack::after { + content: attr(data-copy) " "; + visibility: hidden; + overflow: hidden; + } + div.stack::after, div.stack > textarea { + white-space: pre-wrap; + border: 1px solid #888; + padding: 3px; + font: inherit; + font-family: monospace; + grid-area: 1 / 1 / 2 / 2; + min-height: 1em; + border-radius: 2px; + resize: none; + } + ` + this.shadowRoot.appendChild(style) + } + + set value(value) { + this.textEl.value = value + this.stackEl.dataset.copy = this.textEl.value + } + + get value() { + return this.textEl.value + } +} \ No newline at end of file