From c9a021057b949a021582051018c651eb6763d88d Mon Sep 17 00:00:00 2001 From: Benjamin Atkin Date: Tue, 11 Apr 2023 12:43:25 -0700 Subject: [PATCH] integrate editor --- components/file-group-page.js | 88 +++++++++++++++++++++++++++-------- components/header.js | 41 +++++++++++++++- components/layout.js | 14 +++++- components/page.js | 2 +- sw.js | 19 ++++---- 5 files changed, 133 insertions(+), 31 deletions(-) diff --git a/components/file-group-page.js b/components/file-group-page.js index 03bd4e2..dab9dcf 100644 --- a/components/file-group-page.js +++ b/components/file-group-page.js @@ -23,22 +23,29 @@ html { ${'<'}script type="module"> - let frame = undefined +let frame = undefined addEventListener('message', event => { - let isNew = false - const d = event.data - if (Array.isArray(d) && d[0] === 'srcdoc') { - isNew = frame === undefined - if (isNew) { - frame = document.createElement('iframe') - frame.sandbox = "allow-scripts allow-top-navigation" + const isChild = ( + frame !== undefined && event.source == frame.contentWindow + ) + if (isChild) { + parent.postMessage(event.data, '*') + } else { + let isNew = false + const d = event.data + if (Array.isArray(d) && d[0] === 'srcdoc') { + isNew = frame === undefined + if (isNew) { + frame = document.createElement('iframe') + frame.sandbox = "allow-scripts allow-top-navigation" + } + frame.srcdoc = d[1] + if (isNew) { + document.body.appendChild(frame) + } + } else if (frame !== undefined) { + frame.contentWindow.postMessage(event.data, '*') } - frame.srcdoc = d[1] - } else if (frame !== undefined) { - frame.postMessage(event.data) - } - if (isNew) { - document.body.appendChild(frame) } }) ${' @@ -81,10 +88,16 @@ export class FileGroupPage extends HTMLElement { this.initEditFrame() this.initViewFrame() this.editing = this.editing + addEventListener('message', this.handleMessage) + } + + disconnectedCallback() { + removeEventListener('message', this.handleMessage) } initEditFrame() { const frame = document.createElement('iframe') + frame.classList.add('edit') if (this.csp !== undefined) { frame.sandbox = "allow-same-origin allow-scripts allow-top-navigation" const url = new URL( @@ -100,12 +113,16 @@ export class FileGroupPage extends HTMLElement { frame.addEventListener('load', () => { this.displayEdit() }) + frame.addEventListener('message', message => { + this.handleEditMessage(message) + }) this.editFrame = frame this.shadowRoot.append(frame) } initViewFrame() { const frame = document.createElement('iframe') + frame.classList.add('view') if (this.csp !== undefined) { frame.sandbox = "allow-same-origin allow-scripts allow-top-navigation" const url = new URL( @@ -125,22 +142,50 @@ export class FileGroupPage extends HTMLElement { this.shadowRoot.append(frame) } - displayView() { - let doc = 'view here' + displayView(doc) { const msg = ['srcdoc', doc] this.viewFrame.contentWindow.postMessage( msg, '*' ) } - displayEdit() { - let doc = 'edit here' + async displayEdit() { + const doc = await this.editorBuild.build() const msg = ['srcdoc', doc] - this.viewFrame.contentWindow.postMessage( + this.editFrame.contentWindow.postMessage( msg, '*' ) } + handleMessage = event => { + const editWin = this.editFrame?.contentWindow + const viewWin = this.viewFrame?.contentWindow + if (editWin && event.source == editWin) { + this.handleEditMessage(event) + } else if (viewWin && event.source == viewWin) { + this.handleViewMessage(event) + } + } + + async handleViewMessage(event) { + } + + async handleEditMessage(event) { + if (Array.isArray(event.data)) { + if (event.data[0] === 'ready') { + this.editFrame.contentWindow.postMessage( + ['doc', this.body], '*' + ) + } else if (event.data[0] === 'html') { + const html = event.data[1] + this.displayView(html) + } else if (event.data[0] === 'save') { + const doc = event.data[1] + this.body = doc + } + } + } + set body(value) { try { localStorage.setItem(this.path, value) @@ -168,6 +213,11 @@ export class FileGroupPage extends HTMLElement { } else { classes.add('viewing') classes.remove('editing') + if (this.editFrame) { + this.editFrame.contentWindow.postMessage( + ['request-html'], '*' + ) + } } } } diff --git a/components/header.js b/components/header.js index 088a30f..04ff05e 100644 --- a/components/header.js +++ b/components/header.js @@ -28,6 +28,9 @@ export class Header extends HTMLElement { cancel: 'Cancel', alreadyExists: 'There is already a page with that name.', createPage: 'Create Page', + htmlCss: 'HTML/CSS', + singleFile: 'Single File', + newPage: 'New Page', } textEs = { @@ -40,6 +43,9 @@ export class Header extends HTMLElement { cancel: 'Cancelar', alreadyExists: 'Ya existe una página con ese nombre.', createPage: 'Crear Página', + htmlCss: 'HTML/CSS', + singleFile: 'Archivo único', + newPage: 'Nueva Página', } constructor() { @@ -310,6 +316,22 @@ export class Header extends HTMLElement { input.value = '/' input.style.minWidth = '300px' dialog.bodyEl.appendChild(input) + const select = document.createElement('select') + const options = ['htmlCss', 'singleFile'] + select.append(...options.map(value => { + const el = document.createElement('option') + el.value = value + el.innerText = this.text[value] + return el + })) + select.value = 'htmlCss' + input.addEventListener('input', e => { + const ext = e.target.value.match(/\.\w+$/) + select.value = ext ? 'singleFile' : 'htmlCss' + }) + select.style.marginTop = '10px' + select.style.marginBottom = '10px' + dialog.bodyEl.appendChild(select) let errorEl const bGroup = document.createElement( 'm-forms-button-group' @@ -328,7 +350,24 @@ export class Header extends HTMLElement { } return } - localStorage.setItem(newPath, '') + const value = ( + select.value === 'singleFile' ? + '' : + JSON.stringify({ + type: 'm-file-group', + files: [ + { + "name": "index.html", + "data": `

${this.text.newPage}

`, + }, + { + "name": "style.css", + "data": 'h1 { color: dodgerblue; }', + }, + ], + }) + ) + localStorage.setItem(newPath, value) location.hash = newPath dialog.close() this.dispatchEvent(new CustomEvent( diff --git a/components/layout.js b/components/layout.js index 2f37667..4468f06 100644 --- a/components/layout.js +++ b/components/layout.js @@ -1,3 +1,5 @@ +import { EditorBuild } from "/loader/editor-build.js" + export class Layout extends HTMLElement { constructor() { super() @@ -19,7 +21,7 @@ export class Layout extends HTMLElement { overflow-y: hidden; position: relative; } - m-page { + m-page, m-file-group-page { flex-grow: 1; } ` @@ -61,6 +63,9 @@ export class Layout extends HTMLElement { isGroup ? 'm-file-group-page' : 'm-page' ) this.page.csp = this.csp + if (isGroup) { + this.page.editorBuild = this.editorBuild + } this.page.path = path this.editing = this.editing if (prevPage !== undefined) { @@ -99,4 +104,11 @@ export class Layout extends HTMLElement { return false } } + + get editorBuild() { + if (this._editorBuild === undefined) { + this._editorBuild = new EditorBuild() + } + return this._editorBuild + } } \ No newline at end of file diff --git a/components/page.js b/components/page.js index 3e1dd58..d77f271 100644 --- a/components/page.js +++ b/components/page.js @@ -35,7 +35,7 @@ addEventListener('message', event => { } frame.srcdoc = d[1] } else if (frame !== undefined) { - frame.postMessage(event.data) + frame.contentWindow.postMessage(event.data, '*') } if (isNew) { document.body.appendChild(frame) diff --git a/sw.js b/sw.js index 19a73c4..326907c 100644 --- a/sw.js +++ b/sw.js @@ -1,21 +1,22 @@ async function initCache() { const cache = await caches.open('v1') await cache.addAll([ - '/', - '/index.html', '/app.js', - '/components/page.js', - '/components/layout.js', + '/components/file-group-page.js', '/components/header.js', + '/components/layout.js', '/components/nav-menu.js', + '/components/page.js', '/dialog/dialog.js', + '/editor/app.js', + '/editor/file-group.js', + '/editor/file-view.js', + '/editor/text-edit.js', '/forms/button-group.js', - '/menu/dropdown.js', + '/index.html', '/loader/builder.js', '/loader/editor-build.js', - '/editor/file-group.js', - '/editor/file-page.js', - '/editor/text-edit.js', + '/menu/dropdown.js', ]) } @@ -52,4 +53,4 @@ self.addEventListener('fetch', event => { self.addEventListener('activate', event => { event.waitUntil(clients.claim()) -}) \ No newline at end of file +})