139 lines
3.4 KiB
Vue
139 lines
3.4 KiB
Vue
<template lang="pug">
|
|
.pages
|
|
.pages-list
|
|
slot(v-if="pages.error" name="error") {{ pages.error }}
|
|
slot(v-else-if="pages.data" name="list")
|
|
template(v-for="(page, id) in pages.data")
|
|
button(v-if="activePage != id" @click="showPage(id)") {{ page.name }}
|
|
p(v-else) {{ page.name }}
|
|
slot(v-else name="loader") Loading...
|
|
.active-page
|
|
div(v-if="activePage")
|
|
keep-alive
|
|
div(:is="activeComponent")
|
|
</template>
|
|
|
|
<script>
|
|
import PageLoader from './components/loader.vue'
|
|
import PageError from './components/error.vue'
|
|
|
|
export default {
|
|
name: 'pages-router',
|
|
data() {
|
|
return {
|
|
pages: {
|
|
error: null,
|
|
data: null
|
|
},
|
|
activePage: null
|
|
}
|
|
},
|
|
props: {
|
|
loader: {
|
|
type: Object,
|
|
default() {
|
|
return PageLoader
|
|
}
|
|
},
|
|
error: {
|
|
type: Object,
|
|
default() {
|
|
return PageError
|
|
}
|
|
},
|
|
prefix: {
|
|
type: String,
|
|
default: 'page-'
|
|
},
|
|
retry: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
source: {
|
|
required: true
|
|
},
|
|
timeout: {
|
|
type: Number,
|
|
default: 5000
|
|
},
|
|
title: {
|
|
type: String,
|
|
default: 'Router'
|
|
}
|
|
},
|
|
created() {
|
|
this.source.then(data => { // Load pages
|
|
this.pages.data = data
|
|
const loading = () => { // Show page from url or default one
|
|
this.showPage(window.location.hash.length > 0 ?
|
|
window.location.hash.substr(2) :
|
|
Object.keys(this.pages.data)[0])
|
|
}
|
|
this.$nextTick(loading)
|
|
window.onhashchange = loading
|
|
}).catch(error => {
|
|
this.pages.error = error
|
|
})
|
|
},
|
|
computed: {
|
|
activeComponent() {
|
|
return this.prefix + this.activePage
|
|
}
|
|
},
|
|
methods: {
|
|
showPage(id) {
|
|
const key = this.prefix + id
|
|
if (!(id in this.pages.data)) {
|
|
console.error(`Module not found: ${id}`)
|
|
this.$options.components[key] = this.error
|
|
return;
|
|
}
|
|
const mod = this.pages.data[id]
|
|
|
|
if (!(key in this.$options.components)) { // Must be load
|
|
this.$options.components[key] = () => ({
|
|
// Dynamicly load component
|
|
component: new Promise((resolve, reject) => {
|
|
const loaded = () => key in Vue.options.components
|
|
|
|
const load = () => loaded() ?
|
|
resolve(Vue.options.components[key]) : reject()
|
|
|
|
if (loaded() || (!this.retry && document.querySelector(`script[src="${mod.src}"]`))) { // Allready loaded
|
|
load()
|
|
return;
|
|
}
|
|
|
|
const remover = fn => e => {
|
|
if(this.retry) {
|
|
document.head.removeChild(e.target)
|
|
}
|
|
fn()
|
|
}
|
|
|
|
const el = document.createElement('script')
|
|
|
|
el.type = 'text/javascript'
|
|
el.async = true
|
|
el.src = mod.src
|
|
if (mod.integrity != null)
|
|
el.integrity = mod.integrity
|
|
|
|
el.addEventListener('load', remover(load))
|
|
el.addEventListener('error', remover(reject))
|
|
el.addEventListener('abort', remover(reject))
|
|
|
|
document.head.appendChild(el)
|
|
}),
|
|
loading: this.loader,
|
|
error: this.error,
|
|
timeout: this.timeout
|
|
})
|
|
}
|
|
document.title = `${this.title} - ${mod.name}`
|
|
window.history.pushState({}, document.title, "/#/" + id)
|
|
this.activePage = id
|
|
}
|
|
}
|
|
}
|
|
</script> |