forked from rc/aircox
update assets dependencies; still work to be done to solve it all
This commit is contained in:
137
assets/public/appBuilder.js
Normal file
137
assets/public/appBuilder.js
Normal file
@ -0,0 +1,137 @@
|
||||
import {createApp} from 'vue'
|
||||
|
||||
/**
|
||||
* Utility class used to handle Vue applications. It provides way to load
|
||||
* remote application and update history.
|
||||
*/
|
||||
export default class Builder {
|
||||
constructor(config={}) {
|
||||
this.config = config
|
||||
this.title = null
|
||||
this.app = null
|
||||
this.vm = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch app from remote and mount application.
|
||||
*/
|
||||
fetch(url, {el='app', ...options}={}) {
|
||||
return fetch(url, options).then(response => response.text())
|
||||
.then(content => {
|
||||
let doc = new DOMParser().parseFromString(content, 'text/html')
|
||||
let app = doc.getElementById('app')
|
||||
content = app ? app.innerHTML : content
|
||||
return this.mount({content, title: doc.title, reset:true, url })
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount application, using `create_app` if required.
|
||||
*
|
||||
* @param {String} options.content: replace app container content with it
|
||||
* @param {String} options.title: set DOM document title.
|
||||
* @param {String} [options.el=this.config.el]: mount application on this element (querySelector argument)
|
||||
* @param {Boolean} [reset=False]: if True, force application recreation.
|
||||
* @return `app.mount`'s result.
|
||||
*/
|
||||
mount({content=null, title=null, el=null, reset=false}={}) {
|
||||
try {
|
||||
this.unmount()
|
||||
|
||||
let config = this.config
|
||||
if(el === null)
|
||||
el = config.el
|
||||
|
||||
if(reset || !this.app)
|
||||
this.app = this.createApp({title,content,el,...config})
|
||||
this.vm = this.app.mount(el)
|
||||
window.scroll(0, 0)
|
||||
return this.vm
|
||||
} catch(error) {
|
||||
this.unmount()
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
createApp({el, title=null, content=null, ...config}) {
|
||||
const container = document.querySelector(el)
|
||||
if(!container)
|
||||
throw `Error: can't get element ${el}`
|
||||
if(content)
|
||||
container.innerHTML = content
|
||||
if(title)
|
||||
document.title = title
|
||||
return createApp(config)
|
||||
}
|
||||
|
||||
unmount() {
|
||||
this.app && this.app.unmount()
|
||||
this.app = null
|
||||
this.vm = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable hot reload: catch page change in order to fetch them and
|
||||
* load page without actually leaving current one.
|
||||
*/
|
||||
enableHotReload(node=null, historySave=true) {
|
||||
if(historySave)
|
||||
this.historySave(document.location, true)
|
||||
node.addEventListener('click', event => this._onPageChange(event), true)
|
||||
node.addEventListener('submit', event => this._onPageChange(event), true)
|
||||
node.addEventListener('popstate', event => this._onPopState(event), true)
|
||||
}
|
||||
|
||||
_onPageChange(event) {
|
||||
let submit = event.type == 'submit';
|
||||
let target = submit || event.target.tagName == 'A'
|
||||
? event.target : event.target.closest('a');
|
||||
if(!target || target.hasAttribute('target'))
|
||||
return;
|
||||
|
||||
let url = submit ? target.getAttribute('action') || ''
|
||||
: target.getAttribute('href');
|
||||
if(url===null || !(url === '' || url.startsWith('/') || url.startsWith('?')))
|
||||
return;
|
||||
|
||||
let options = {};
|
||||
if(submit) {
|
||||
let formData = new FormData(event.target);
|
||||
if(target.method == 'get')
|
||||
url += '?' + (new URLSearchParams(formData)).toString();
|
||||
else
|
||||
options = {...options, method: target.method, body: formData}
|
||||
}
|
||||
this.fetch(url, options).then(_ => this.historySave(url))
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
_onPopState(event) {
|
||||
if(event.state && event.state.content)
|
||||
// document.title = this.title;
|
||||
this.historyLoad(event.state);
|
||||
}
|
||||
|
||||
/// Save application state into browser history
|
||||
historySave(url,replace=false) {
|
||||
const el = document.querySelector(this.config.el)
|
||||
const state = {
|
||||
content: el.innerHTML,
|
||||
title: document.title,
|
||||
}
|
||||
|
||||
if(replace)
|
||||
history.replaceState(state, '', url)
|
||||
else
|
||||
history.pushState(state, '', url)
|
||||
}
|
||||
|
||||
/// Load application from browser history's state
|
||||
historyLoad(state) {
|
||||
return this.mount({ content: state.content, title: state.title })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user