/
ion-vue-router.ts
154 lines (129 loc) · 4.4 KB
/
ion-vue-router.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import Vue, { CreateElement, RenderContext, VNodeData } from 'vue';
import { NavDirection } from '@ionic/core';
type TransitionDone = () => void;
interface Props {
name: string;
animated: boolean;
}
// Component entering the view
let enteringEl: HTMLElement;
export default {
name: 'IonVueRouter',
functional: true,
props: {
// Router view name
name: { default: 'default', type: String },
// Disable transitions
animated: { default: true, type: Boolean },
},
render(h: CreateElement, { parent, props, data, children }: RenderContext) {
if (!parent.$router) {
throw new Error('IonTabs requires an instance of either VueRouter or IonicVueRouter');
}
const ionRouterOutletData: VNodeData = {
...data,
ref: 'ionRouterOutlet',
on: { click: (event: Event) => catchIonicGoBack(parent, event) },
};
const routerViewData: VNodeData = { props: { name: props.name } };
const transitionData: VNodeData = {
props: { css: false, mode: 'in-out' },
on: {
leave: (el: HTMLElement, done: TransitionDone) => {
leave(parent, props as Props, el, done);
},
beforeEnter,
enter,
afterEnter,
beforeLeave,
afterLeave,
enterCancelled,
leaveCancelled,
}
};
return h('ion-router-outlet', ionRouterOutletData, [
h('transition', transitionData, [
h('router-view', routerViewData, children)
])
]);
}
};
function catchIonicGoBack(parent: Vue, event: Event): void {
// In case of nested ion-vue-routers run only once
event.stopImmediatePropagation();
if (!event.target) return;
// We only care for the event coming from Ionic's back button
const backButton = (event.target as HTMLElement).closest('ion-back-button') as HTMLIonBackButtonElement;
if (!backButton) return;
const $router = parent.$router;
let defaultHref: string;
// Explicitly override router direction to always trigger a back transition
$router.directionOverride = 'back';
// If we can go back - do so
if ($router.canGoBack()) {
event.preventDefault();
$router.back();
return;
}
// If there's a default fallback - use it
defaultHref = backButton.defaultHref as string;
if (undefined !== defaultHref) {
event.preventDefault();
$router.push(defaultHref);
}
}
// Transition when we leave the route
function leave(parent: Vue, props: Props, el: HTMLElement, done: TransitionDone) {
const promise = transition(parent, props, el);
// Skip any transition if we don't get back a Promise
if (!promise) {
done();
return;
}
// Perform navigation once the transition was finished
parent.$router.transition = new Promise(resolve => {
promise.then(() => {
resolve();
done();
}).catch(console.error);
});
}
// Trigger the ionic/core transitions
function transition(parent: Vue, props: Props, leavingEl: HTMLElement) {
const ionRouterOutlet = parent.$refs.ionRouterOutlet as HTMLIonRouterOutletElement;
// The Ionic framework didn't load - skip animations
if (typeof ionRouterOutlet.componentOnReady === 'undefined') {
return;
}
// Skip animations if there's no component to navigate to
// or the current and the "to-be-rendered" components are the same
if (!enteringEl || enteringEl === leavingEl) {
return;
}
// Add the proper Ionic classes, important for smooth transitions
enteringEl.classList.add('ion-page');
enteringEl.classList.add('ion-page-invisible');
// Commit to the transition as soon as the Ionic Router Outlet is ready
return ionRouterOutlet.componentOnReady().then((el: HTMLIonRouterOutletElement) => {
return el.commit(enteringEl, leavingEl, {
deepWait: true,
duration: !props.animated || parent.$router.direction === 'root' ? 0 : undefined,
direction: parent.$router.direction as NavDirection,
showGoBack: parent.$router.canGoBack(),
});
}).catch(console.error);
}
// Set the component to be rendered before we render the new route
function beforeEnter(el: HTMLElement) {
enteringEl = el;
}
// Enter the new route
function enter(_el: HTMLElement, done: TransitionDone) {
done();
}
// Vue transition stub functions
function afterEnter(_el: HTMLElement) { /* */ }
function afterLeave(_el: HTMLElement) { /* */ }
function beforeLeave(_el: HTMLElement) { /* */ }
function enterCancelled(_el: HTMLElement) { /* */ }
function leaveCancelled(_el: HTMLElement) { /* */ }