WIP: Design #135
@ -8039,6 +8039,10 @@ input.half-field:not(:active):not(:hover) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
.blink {
 | 
			
		||||
  animation: 1s ease-in-out 2s infinite alternate blink;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.loading {
 | 
			
		||||
  animation: 1s ease-in-out 3s infinite alternate blink;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -3,6 +3,7 @@ import components from './components'
 | 
			
		||||
 | 
			
		||||
import { Carousel, Pagination, Navigation, Slide } from 'vue3-carousel'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const App = {
 | 
			
		||||
    el: '#app',
 | 
			
		||||
    delimiters: ['[[', ']]'],
 | 
			
		||||
@ -21,31 +22,6 @@ const App = {
 | 
			
		||||
    computed: {
 | 
			
		||||
        player() { return window.aircox.player; },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            carouselBreakpoints: {
 | 
			
		||||
                400: {
 | 
			
		||||
                    itemsToShow: 1
 | 
			
		||||
                },
 | 
			
		||||
                600: {
 | 
			
		||||
                    itemsToShow: 1.75
 | 
			
		||||
                },
 | 
			
		||||
                800: {
 | 
			
		||||
                    itemsToShow: 3
 | 
			
		||||
                },
 | 
			
		||||
                1024: {
 | 
			
		||||
                    itemsToShow: 4
 | 
			
		||||
                },
 | 
			
		||||
                1280: {
 | 
			
		||||
                    itemsToShow: 4
 | 
			
		||||
                },
 | 
			
		||||
                1380: {
 | 
			
		||||
                    itemsToShow: 5
 | 
			
		||||
                },
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const PlayerApp = {
 | 
			
		||||
 | 
			
		||||
@ -1,144 +0,0 @@
 | 
			
		||||
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', historySave=true, ...options}={}) {
 | 
			
		||||
        const fut = fetch(url, options).then(response => response.text())
 | 
			
		||||
            .then(content => {
 | 
			
		||||
                let doc = new DOMParser().parseFromString(content, 'text/html')
 | 
			
		||||
                let app = doc.querySelector(el)
 | 
			
		||||
                content = app ? app.innerHTML : content
 | 
			
		||||
                return this.mount({content, title: doc.title, reset:true, url })
 | 
			
		||||
            })
 | 
			
		||||
        if(historySave)
 | 
			
		||||
            fut.then(() => this.historySave(url))
 | 
			
		||||
        return fut
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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, props=null}={}) {
 | 
			
		||||
        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}, props)
 | 
			
		||||
 | 
			
		||||
            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}, props) {
 | 
			
		||||
        const container = document.querySelector(el)
 | 
			
		||||
        if(!container)
 | 
			
		||||
            return
 | 
			
		||||
        if(content)
 | 
			
		||||
            container.innerHTML = content
 | 
			
		||||
        if(title)
 | 
			
		||||
            document.title = title
 | 
			
		||||
        const app = createApp(config, props)
 | 
			
		||||
        app.config.globalProperties.window = window
 | 
			
		||||
        return app
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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.pageChanged(event), true)
 | 
			
		||||
        node.addEventListener('submit', event => this.pageChanged(event), true)
 | 
			
		||||
        node.addEventListener('popstate', event => this.statePopped(event), true)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pageChanged(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');
 | 
			
		||||
        let domain = window.location.protocol + '//' + window.location.hostname
 | 
			
		||||
        let stay = (url === '' || url.startsWith('/') || url.startsWith('?') ||
 | 
			
		||||
                    url.startsWith(domain)) && url.indexOf('wp-admin') == -1
 | 
			
		||||
        if(url===null || !stay) {
 | 
			
		||||
            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)
 | 
			
		||||
        event.preventDefault();
 | 
			
		||||
        event.stopPropagation();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    statePopped(event) {
 | 
			
		||||
        const state = event.state
 | 
			
		||||
        if(state && state.content)
 | 
			
		||||
            // document.title = this.title;
 | 
			
		||||
            this.historyLoad(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 })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -109,6 +109,10 @@ input.half-field:not(:active):not(:hover) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.blink {
 | 
			
		||||
    animation: 1s ease-in-out 2s infinite alternate blink;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.loading {
 | 
			
		||||
    animation: 1s ease-in-out 3s infinite alternate blink;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ import '@fortawesome/fontawesome-free/css/all.min.css';
 | 
			
		||||
 | 
			
		||||
//-- aircox
 | 
			
		||||
import App, {PlayerApp} from './app'
 | 
			
		||||
import Builder from './appBuilder'
 | 
			
		||||
import VueLoader from './vueLoader'
 | 
			
		||||
import Sound from './sound'
 | 
			
		||||
import {Set} from './model'
 | 
			
		||||
 | 
			
		||||
@ -17,13 +17,13 @@ import './assets/styles.scss'
 | 
			
		||||
 | 
			
		||||
window.aircox = {
 | 
			
		||||
    // main application
 | 
			
		||||
    builder: new Builder(App),
 | 
			
		||||
    get app() { return this.builder.app  },
 | 
			
		||||
    loader: null,
 | 
			
		||||
    get app() { return this.loader.app  },
 | 
			
		||||
 | 
			
		||||
    // player application
 | 
			
		||||
    playerBuilder: new Builder(PlayerApp),
 | 
			
		||||
    get playerApp() { return this.playerBuilder && this.playerBuilder.app },
 | 
			
		||||
    get player() { return this.playerBuilder.vm && this.playerBuilder.vm.$refs.player },
 | 
			
		||||
    playerLoader: null,
 | 
			
		||||
    get playerApp() { return this.playerLoader && this.playerLoader.app },
 | 
			
		||||
    get player() { return this.playerLoader.vm && this.playerLoader.vm.$refs.player },
 | 
			
		||||
 | 
			
		||||
    Set, Sound,
 | 
			
		||||
 | 
			
		||||
@ -31,27 +31,23 @@ window.aircox = {
 | 
			
		||||
    /**
 | 
			
		||||
     * Initialize main application and player.
 | 
			
		||||
     */
 | 
			
		||||
    init(props=null, {config=null, builder=null, initBuilder=true,
 | 
			
		||||
                      initPlayer=true, hotReload=false, el=null}={})
 | 
			
		||||
    init(props=null, {hotReload=false, el=null,
 | 
			
		||||
                      config=null, playerConfig=null,
 | 
			
		||||
                      initApp=true, initPlayer=true,
 | 
			
		||||
                      loader=null, playerLoader=null}={})
 | 
			
		||||
    {
 | 
			
		||||
        if(initPlayer) {
 | 
			
		||||
            let playerBuilder = this.playerBuilder
 | 
			
		||||
            playerBuilder.mount()
 | 
			
		||||
            playerConfig = playerConfig || PlayerApp
 | 
			
		||||
            playerLoader = playerLoader || new VueLoader(playerConfig)
 | 
			
		||||
            playerLoader.enable(false)
 | 
			
		||||
            this.playerLoader = playerLoader
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(initBuilder) {
 | 
			
		||||
            builder = builder || this.builder
 | 
			
		||||
            this.builder = builder
 | 
			
		||||
            if(config || window.App)
 | 
			
		||||
                builder.config = config || window.App
 | 
			
		||||
            if(el)
 | 
			
		||||
                builder.config.el = el
 | 
			
		||||
 | 
			
		||||
            builder.title = document.title
 | 
			
		||||
            builder.mount({props})
 | 
			
		||||
 | 
			
		||||
            if(hotReload)
 | 
			
		||||
                builder.enableHotReload(hotReload)
 | 
			
		||||
        if(initApp) {
 | 
			
		||||
            config = config || window.App || App
 | 
			
		||||
            loader = loader || new VueLoader({el, props, ...config})
 | 
			
		||||
            loader.enable(hotReload)
 | 
			
		||||
            this.loader = loader
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										174
									
								
								assets/src/pageLoad.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								assets/src/pageLoad.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,174 @@
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Load page without leaving current one (hot-reload).
 | 
			
		||||
 */
 | 
			
		||||
export default class PageLoad {
 | 
			
		||||
    constructor(el, {loadingClass="loading", append=false}={}) {
 | 
			
		||||
        this.el = el
 | 
			
		||||
        this.append = append
 | 
			
		||||
        this.loadingClass = loadingClass
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get target() {
 | 
			
		||||
        if(!this._target)
 | 
			
		||||
            this._target = document.querySelector(this.el)
 | 
			
		||||
        return this._target
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    reset() {
 | 
			
		||||
        this._target = null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Enable hot reload: catch page change in order to fetch them and
 | 
			
		||||
     * load page without actually leaving current one.
 | 
			
		||||
     */
 | 
			
		||||
    enable(target=null) {
 | 
			
		||||
        if(this._pageChanged)
 | 
			
		||||
            throw "Already enabled, please disable me"
 | 
			
		||||
 | 
			
		||||
        if(!target)
 | 
			
		||||
            target = this.target || document.body
 | 
			
		||||
        this.historySave(document.location, true)
 | 
			
		||||
 | 
			
		||||
        this._pageChanged = event => this.pageChanged(event)
 | 
			
		||||
        this._statePopped = event => this.statePopped(event)
 | 
			
		||||
 | 
			
		||||
        target.addEventListener('click', this._pageChanged, true)
 | 
			
		||||
        target.addEventListener('submit', this._pageChanged, true)
 | 
			
		||||
        window.addEventListener('popstate', this._statePopped, true)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Disable hot reload, remove listeners.
 | 
			
		||||
     */
 | 
			
		||||
    disable() {
 | 
			
		||||
        this.target.removeEventListener('click', this._pageChanged, true)
 | 
			
		||||
        this.target.removeEventListener('submit', this._pageChanged, true)
 | 
			
		||||
        window.removeEventListener('popstate', this._statePopped, true)
 | 
			
		||||
 | 
			
		||||
        this._pageChanged = null
 | 
			
		||||
        this._statePopped = null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
    * Fetch url, return promise, similar to standard Fetch API.
 | 
			
		||||
    * Default implementation just forward argument to it.
 | 
			
		||||
    */
 | 
			
		||||
    fetch(url, options) {
 | 
			
		||||
        return fetch(url, options)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetch app from remote and mount application.
 | 
			
		||||
     */
 | 
			
		||||
    load(url, {mount=true,  scroll=[0,0], ...options}={}) {
 | 
			
		||||
        if(this.loadingClass)
 | 
			
		||||
            this.target.classList.add(this.loadingClass)
 | 
			
		||||
 | 
			
		||||
        if(this.onLoad)
 | 
			
		||||
            this.onLoad({url, el: this.el, options})
 | 
			
		||||
        if(scroll)
 | 
			
		||||
            window.scroll(...scroll)
 | 
			
		||||
        return this.fetch(url, options).then(response => response.text())
 | 
			
		||||
            .then(content => {
 | 
			
		||||
                if(this.loadingClass)
 | 
			
		||||
                    this.target.classList.remove(this.loadingClass)
 | 
			
		||||
 | 
			
		||||
                var doc = new DOMParser().parseFromString(content, 'text/html')
 | 
			
		||||
                var dom = doc.querySelectorAll(this.el)
 | 
			
		||||
                var result = {url,
 | 
			
		||||
                              content: dom || [document.createTextNode(content)],
 | 
			
		||||
                              title: doc.title,
 | 
			
		||||
                              append: this.append}
 | 
			
		||||
                mount && this.mount(result)
 | 
			
		||||
                return result
 | 
			
		||||
            })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
    * Mount the page on provided target element
 | 
			
		||||
    */
 | 
			
		||||
    mount({content, title=null, ...options}={}) {
 | 
			
		||||
        if(this.onPreMount)
 | 
			
		||||
            this.onPreMount({target: this.target, content, items, title})
 | 
			
		||||
        var items = null;
 | 
			
		||||
        if(content)
 | 
			
		||||
            items = this.mountContent(content, options)
 | 
			
		||||
        if(title)
 | 
			
		||||
            document.title = title
 | 
			
		||||
        if(this.onMount)
 | 
			
		||||
            this.onMount({target: this.target, content, items, title})
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
    * Mount page content
 | 
			
		||||
    */
 | 
			
		||||
    mountContent(content, {append=false}={}) {
 | 
			
		||||
        if(typeof content == "string") {
 | 
			
		||||
            this.target.innerHTML = append ? this.target.innerHTML + content
 | 
			
		||||
                                           : content;
 | 
			
		||||
            // TODO
 | 
			
		||||
            return []
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(!append)
 | 
			
		||||
            this.target.innerHTML = ""
 | 
			
		||||
 | 
			
		||||
        var fragment = document.createDocumentFragment()
 | 
			
		||||
        var items = []
 | 
			
		||||
        for(var node of content)
 | 
			
		||||
            while(node.firstChild) {
 | 
			
		||||
                items.push(node.firstChild)
 | 
			
		||||
                fragment.appendChild(node.firstChild)
 | 
			
		||||
            }
 | 
			
		||||
        this.target.append(fragment)
 | 
			
		||||
        return items
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Save application state into browser history
 | 
			
		||||
    historySave(url,replace=false) {
 | 
			
		||||
        const state = { content: this.target.innerHTML,
 | 
			
		||||
                        title: document.title, }
 | 
			
		||||
        if(replace)
 | 
			
		||||
            history.replaceState(state, '', url)
 | 
			
		||||
        else
 | 
			
		||||
            history.pushState(state, '', url)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // --- events
 | 
			
		||||
    pageChanged(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');
 | 
			
		||||
        let domain = window.location.protocol + '//' + window.location.hostname
 | 
			
		||||
        let stay = (url === '' || url.startsWith('/') || url.startsWith('?') ||
 | 
			
		||||
                    url.startsWith(domain)) && url.indexOf('wp-admin') == -1
 | 
			
		||||
        if(url===null || !stay) {
 | 
			
		||||
            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.load(url, options).then(() => this.historySave(url))
 | 
			
		||||
        event.preventDefault();
 | 
			
		||||
        event.stopPropagation();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    statePopped(event) {
 | 
			
		||||
        const state = event.state
 | 
			
		||||
        if(state && state.content)
 | 
			
		||||
            this.mount({ content: state.content, title: state.title });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								assets/src/vueLoader.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								assets/src/vueLoader.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
			
		||||
import {createApp} from 'vue'
 | 
			
		||||
 | 
			
		||||
import PageLoad from './pageLoad'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handles loading Vue js app on page load.
 | 
			
		||||
 */
 | 
			
		||||
export default class VueLoader {
 | 
			
		||||
    constructor({el=null, props={}, ...appConfig}={}, loaderOptions={}) {
 | 
			
		||||
        this.appConfig = appConfig;
 | 
			
		||||
        this.props = props
 | 
			
		||||
        this.pageLoad = new PageLoad(el, loaderOptions)
 | 
			
		||||
 | 
			
		||||
        this.pageLoad.onPreMount = event => this.onPreMount(event)
 | 
			
		||||
        this.pageLoad.onMount = event => this.onMount(event)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    enable(hotReload=true) {
 | 
			
		||||
        hotReload && this.pageLoad.enable()
 | 
			
		||||
        this.mount()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mount() {
 | 
			
		||||
        if(this.app)
 | 
			
		||||
            this.unmount()
 | 
			
		||||
 | 
			
		||||
        const app = createApp(this.appConfig, this.props)
 | 
			
		||||
        app.config.globalProperties.window = window
 | 
			
		||||
        this.vm = app.mount(this.pageLoad.el)
 | 
			
		||||
        this.app = app
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    unmount() {
 | 
			
		||||
        if(!this.app)
 | 
			
		||||
            return
 | 
			
		||||
        try { this.app.unmount() }
 | 
			
		||||
        catch(_) { null }
 | 
			
		||||
        this.app = null
 | 
			
		||||
        this.vm = null
 | 
			
		||||
        this.pageLoad.reset()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onPreMount() { this.unmount() }
 | 
			
		||||
    onMount() { this.mount() }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user