import { generateId } from './utils' ; import { RouteInfo , ViewItem , ViewStacks , } from './types' ; import { RouteLocationMatched , Router } from 'vue-router' ; import { shallowRef } from 'vue' ; export const createViewStacks = ( router ) => { let viewStacks = { } ; const clear = ( outletId ) => { delete viewStacks [ outletId ] ; } ; const getViewStack = ( outletId ) => { return viewStacks [ outletId ] ; } ; const registerIonPage = ( viewItem , ionPage ) => { viewItem . ionPageElement = ionPage ; viewItem . ionRoute = true ; /**
         * This is needed otherwise Vue Router
         * will not consider this component mounted
         * and will not run route guards that
         * are written in the component.
         */ viewItem . matchedRoute . instances = { default : viewItem . vueComponentRef . value } ; } ; const findViewItemByRouteInfo = ( routeInfo , outletId ? ) => { return findViewItemByPath ( routeInfo . pathname , outletId , false ) ; } ; const findLeavingViewItemByRouteInfo = ( routeInfo , outletId ? , mustBeIonRoute = true ) => { return findViewItemByPath ( routeInfo . lastPathname , outletId , mustBeIonRoute ) ; } ; const findViewItemByPathname = ( pathname , outletId ? ) => { return findViewItemByPath ( pathname , outletId , false ) ; } ; const findViewItemInStack = ( path , stack ) : ViewItem | undefined => { return stack . find ( ( viewItem ) => { if ( viewItem . pathname === path ) { return viewItem ; } return undefined ; } ) ; } ; const findViewItemByPath = ( path , outletId ? , mustBeIonRoute = false ) : ViewItem | undefined => { const matchView = ( viewItem ) => { if ( ( mustBeIonRoute && ! viewItem . ionRoute ) || path === '' ) { return false ; } const resolvedPath = router . resolve ( path ) ; const findMatchedRoute = resolvedPath . matched . find ( ( matchedRoute ) => matchedRoute === viewItem . matchedRoute ) ; if ( findMatchedRoute ) { /**
                 * /page/1 and /page/2 should not match
                 * to the same view item otherwise there will
                 * be not page transition and we will need to
                 * explicitly clear out parameters from page 1
                 * so the page 2 params are properly passed
                 * to the developer's app.
                 */ const hasParameter = findMatchedRoute . path . includes ( ':' ) ; if ( hasParameter && path !== viewItem . pathname ) { return false ; } return viewItem ; } return undefined ; } ; if ( outletId ) { const stack = viewStacks [ outletId ] ; if ( ! stack ) return undefined ; const match = ( router ) ? stack . find ( matchView ) : findViewItemInStack ( path , stack ) ; if ( match ) return match ; } else { for ( let outletId in viewStacks ) { const stack = viewStacks [ outletId ] ; const viewItem = findViewItemInStack ( path , stack ) ; if ( viewItem ) { return viewItem ; } } } return undefined ; } ; const createViewItem = ( outletId , vueComponent , matchedRoute , routeInfo , ionPage ? ) : ViewItem => { return { id : generateId ( 'viewItem' ) , pathname : routeInfo . pathname , outletId , matchedRoute , ionPageElement : ionPage , vueComponent , vueComponentRef : shallowRef ( ) , ionRoute : false , mount : false , exact : routeInfo . pathname === matchedRoute . path , params : routeInfo . params , vueComponentData : { } } ; } ; const add = ( viewItem ) : void => { const { outletId } = viewItem ; if ( ! viewStacks [ outletId ] ) { viewStacks [ outletId ] = [ viewItem ] ; } else { viewStacks [ outletId ] . push ( viewItem ) ; } } ; const remove = ( viewItem , outletId ? ) : void => { if ( ! outletId ) { throw Error ( 'outletId required' ) ; } const viewStack = viewStacks [ outletId ] ; if ( viewStack ) { viewStacks [ outletId ] = viewStack . filter ( item => item . id !== viewItem . id ) ; } } ; const getChildrenToRender = ( outletId ) : ViewItem [ ] => { const viewStack = viewStacks [ outletId ] ; if ( viewStack ) { const components = viewStacks [ outletId ] . filter ( v => v . mount ) ; return components ; } return [ ] ; } ; /**
     * When navigating backwards, we need to clean up and
     * leaving pages so that they are re-created if
     * we ever navigate back to them. This is especially
     * important when using router.go and stepping back
     * multiple pages at a time.
     */ const unmountLeavingViews = ( outletId , viewItem , delta = 1 ) => { const viewStack = viewStacks [ outletId ] ; if ( ! viewStack ) return ; const startIndex = viewStack . findIndex ( v => v === viewItem ) ; for ( let i = startIndex + 1 ; i < startIndex - delta ; i ++ ) { const viewItem = viewStack [ i ] ; viewItem . mount = false ; viewItem . ionPageElement = undefined ; viewItem . ionRoute = false ; viewItem . matchedRoute . instances = { } ; } } ; /**
     * When navigating forward it is possible for
     * developers to step forward over multiple views.
     * The intermediary views need to be remounted so that
     * swipe to go back works properly.
     * We need to account for the delta value here too because
     * we do not want to remount an unrelated view.
     * Example:
     * /home --> /page2 --> router.back() --> /page3
     * Going to /page3 would remount /page2 since we do
     * not prune /page2 from the stack. However, /page2
     * needs to remain in the stack.
     * Example:
     * /home --> /page2 --> /page3 --> router.go(-2) --> router.go(2)
     * We would end up on /page3, but users need to be able to swipe
     * to go back to /page2 and /home, so we need both pages mounted
     * in the DOM.
     */ const mountIntermediaryViews = ( outletId , viewItem , delta = 1 ) => { const viewStack = viewStacks [ outletId ] ; if ( ! viewStack ) return ; const startIndex = viewStack . findIndex ( v => v === viewItem ) ; for ( let i = startIndex + 1 ; i < startIndex + delta ; i ++ ) { viewStack [ i ] . mount = true ; } } ; return { unmountLeavingViews , mountIntermediaryViews , clear , findViewItemByRouteInfo , findLeavingViewItemByRouteInfo , findViewItemByPathname , createViewItem , getChildrenToRender , add , remove , registerIonPage , getViewStack } ; } ;