UNPKG

vue-golden-layout

Version:
124 lines (112 loc) 4.46 kB
import { Route } from 'vue-router' import goldenLayout, { registerGlobalComponent } from '../golden.vue' import { Dictionary, xInstanceOf } from '../utils' import { goldenItem } from '../roles' import Vue, { ComponentOptions, Component, AsyncComponent, VueConstructor } from 'vue' export function defaultTitler(route: Route): string { //The last case is to warn the programmer who would have forgotten that detail return route ? ((route.meta && route.meta.title) || 'set $route.meta.title') : ''; } export const RouteComponentName = '$router-route'; function freezeValue(object: Dictionary, path: string, value?: any) { const props = path.split('.'), forced = props.pop()!; for(let property of props) Object.defineProperty(object, property, { value: object = Object.create(object[property]), writable: false }); Object.defineProperty(object, forced, { value, writable: false }); } export function freezeRoute(component: Vue, route: Route) { //Simulate a _routerRoot object so that all children have a $route object set to this route object var routerRoot = (<any>component)._routerRoot = Object.create((<any>component)._routerRoot); freezeValue(routerRoot, '_route', route); freezeValue(routerRoot, '_router.history.current', route); } interface RouterSpec { template: any parent: any } function routeParent(parent: any, route: Route): RouterSpec { var template: any; if(parent._glRouter) template = parent.$scopedSlots.route ? parent.$scopedSlots.route(route) : parent.$slots.route; return {template, parent}; } type ComponentSpec = Component<any, any, any, any> | AsyncComponent<any, any, any, any>; async function vueComponent(comp: ComponentSpec|string, namedComponents: Dictionary<ComponentSpec>): Promise<VueConstructor> { var component: ComponentSpec = 'string'=== typeof comp?namedComponents[<string>comp]:<ComponentSpec>comp; function componentIsVueConstructor() { return (<any>component).prototype instanceof Vue; } console.assert(`Component registered : "${comp}".`); if('function'=== typeof component && !componentIsVueConstructor) //AsyncComponentFactory<any, any, any, any> | FunctionalComponentOptions<any, PropsDefinition<any>> component = (<()=> ComponentSpec>component)(); if(component instanceof Promise) component = await (<Promise<ComponentSpec>>component); return componentIsVueConstructor() ? <VueConstructor>component : Vue.extend(<ComponentOptions<Vue>>component); } function createRouteComponent(comp: VueConstructor, routerSpec: RouterSpec, route: Route) : Vue { const {parent, template} = routerSpec; var itr; for(itr = comp; itr && itr != goldenItem; itr = (<any>itr).super); if(itr) { return new comp({ parent, propsData: route }); } const component = template ? new Vue({ render(ce) { // `instanceof Array` fails in popouts: `template` is a `window.opener.Array` then return xInstanceOf(template, 'Array') ? ce('div', {class: 'glComponent'}, template) : template; }, mounted() { new comp({ el: component.$el.querySelector('main') || undefined, parent: component }); }, parent }) : new comp({parent}); return component; } function renderInContainer(container: any, component: Vue) { //TODO: document why we don't use simply component.$mount(container.getElement()); var el = document.createElement('div'); container.getElement().append(el); component.$mount(el); } export async function getRouteComponent(gl: goldenLayout, router: any, path: string) { var route = gl.$router.resolve({path}).route, compSpec = gl.$router.getMatchedComponents({path})[0], component: Vue; console.assert(compSpec, `Path resolves to a component: ${path}`); component = createRouteComponent( await vueComponent(compSpec!, gl.$options.components || {}), routeParent(router, route), route); //freezeRoute(component, route); return component; } async function renderRoute(gl: goldenLayout, container: any, state: any) { var parent = container.parent, _glRouter = parent.vueObject._glRouter; if(_glRouter) parent = _glRouter; else { while(!parent.vueObject || !parent.vueObject._isVue) parent = parent.parent; parent = parent.vueObject; } renderInContainer(container, await getRouteComponent(gl, parent, state.path)); } export function UsingRoutes(target: any) { //This function should be called once and only once if we are using the router registerGlobalComponent(RouteComponentName, renderRoute); }