6 changed files with 314 additions and 10 deletions
@ -0,0 +1,228 @@ |
|||
const frameHtml = `<!doctype html>
|
|||
<html> |
|||
<head> |
|||
<title>Frame</title> |
|||
<style> |
|||
html, body, iframe { |
|||
margin: 0; |
|||
padding: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
iframe { |
|||
border: none; |
|||
display: block; |
|||
} |
|||
html { |
|||
box-sizing: border-box; |
|||
} |
|||
*, *:before, *:after { |
|||
box-sizing: inherit; |
|||
} |
|||
</style> |
|||
</head> |
|||
<body> |
|||
${'<'}script type="module"> |
|||
let frame = undefined |
|||
addEventListener('message', event => { |
|||
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, '*') |
|||
} |
|||
} |
|||
}) |
|||
${'</'}script> |
|||
</body> |
|||
</html>` |
|||
|
|||
export class FileGroupPage extends HTMLElement { |
|||
constructor() { |
|||
super() |
|||
this.attachShadow({mode: 'open'}) |
|||
this.csp = "default-src 'self' 'unsafe-inline' 'unsafe-eval'" |
|||
} |
|||
|
|||
connectedCallback() { |
|||
const style = document.createElement('style') |
|||
style.textContent = ` |
|||
:host { |
|||
display: grid; |
|||
grid-template-columns: 1fr; |
|||
grid-template-rows: 1fr; |
|||
grid-template-areas: "main"; |
|||
flex-direction: column; |
|||
align-items: stretch; |
|||
} |
|||
iframe { |
|||
border: none; |
|||
margin: 0; |
|||
padding: 0; |
|||
grid-area: main; |
|||
width: 100%; |
|||
} |
|||
:host(.editing) iframe.view { |
|||
display: none; |
|||
} |
|||
:host(.viewing) iframe.edit { |
|||
display: none; |
|||
} |
|||
` |
|||
this.shadowRoot.append(style) |
|||
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( |
|||
'/-/frame', location.href |
|||
) |
|||
url.searchParams.set('csp', this.csp) |
|||
url.searchParams.set('html', frameHtml) |
|||
frame.src = url.href |
|||
} else { |
|||
frame.sandbox = "allow-scripts allow-top-navigation" |
|||
frame.srcdoc = frameHtml |
|||
} |
|||
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( |
|||
'/-/frame', location.href |
|||
) |
|||
url.searchParams.set('csp', this.csp) |
|||
url.searchParams.set('html', frameHtml) |
|||
frame.src = url.href |
|||
} else { |
|||
frame.sandbox = "allow-scripts allow-top-navigation" |
|||
frame.srcdoc = frameHtml |
|||
} |
|||
frame.addEventListener('load', () => { |
|||
this.displayView() |
|||
}) |
|||
this.viewFrame = frame |
|||
this.shadowRoot.append(frame) |
|||
} |
|||
|
|||
displayView(doc) { |
|||
const msg = ['srcdoc', doc] |
|||
this.viewFrame.contentWindow.postMessage( |
|||
msg, '*' |
|||
) |
|||
} |
|||
|
|||
async displayEdit() { |
|||
const doc = await this.editorBuild.build() |
|||
const msg = ['srcdoc', doc] |
|||
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) |
|||
} catch (err) { |
|||
console.error(err) |
|||
} |
|||
} |
|||
|
|||
get body() { |
|||
try { |
|||
return localStorage.getItem(this.path) |
|||
} catch (err) { |
|||
console.error(err) |
|||
return '' |
|||
} |
|||
} |
|||
|
|||
set editing(value) { |
|||
this._editing = value |
|||
if (this.shadowRoot.host) { |
|||
const classes = this.shadowRoot.host.classList |
|||
if (this.editing) { |
|||
classes.add('editing') |
|||
classes.remove('viewing') |
|||
} else { |
|||
classes.add('viewing') |
|||
classes.remove('editing') |
|||
if (this.editFrame) { |
|||
this.editFrame.contentWindow.postMessage( |
|||
['request-html'], '*' |
|||
) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
get editing() { |
|||
return this._editing |
|||
} |
|||
} |
|||
Loading…
Reference in new issue