chore(): migrate vue to typescript (#15928)

This commit is contained in:
Modus Create
2018-10-24 09:22:36 -04:00
committed by Mike Hartington
parent d800c48734
commit e251ca71b4
61 changed files with 1339 additions and 7860 deletions

View File

@ -1,15 +0,0 @@
// A proxy method that initializes the controller and calls requested method
export function proxyMethod(tag, method, ...opts) {
return initController(tag).then(ctrl => ctrl[method].apply(ctrl, opts))
}
// Initialize an Ionic controller and append it to DOM
export function initController(tag) {
let element = document.querySelector(tag)
if (element) {
return element.componentOnReady()
}
return document.body.appendChild(document.createElement(tag)).componentOnReady()
}

17
vue/src/api-utils.ts Normal file
View File

@ -0,0 +1,17 @@
import { HTMLStencilElement } from './interfaces';
// A proxy method that initializes the controller and calls requested method
export function proxyMethod(tag: string, method: string, ...opts: any[]): Promise<any> {
return initController(tag).then((ctrl: any) => ctrl[method].apply(ctrl, opts));
}
// Initialize an Ionic controller and append it to DOM
export function initController(tag: string): Promise<HTMLStencilElement> {
let element = document.querySelector(tag) as HTMLElement;
if (!element) {
element = document.body.appendChild(document.createElement(tag));
}
return (element as HTMLStencilElement).componentOnReady();
}

View File

@ -1,103 +0,0 @@
import Delegate from './framework-delegate'
import ProxyController from './proxy-controller'
import ProxyMenuController from './proxy-menu-controller'
import ProxyDelegateController from './proxy-delegate-controller'
let _Vue, _Delegate
export default class Api {
// Create or return a ActionSheetController instance
get actionSheetController() {
return getOrCreateController('ion-action-sheet-controller')
}
// Create or return an AlertController instance
get alertController() {
return getOrCreateController('ion-alert-controller')
}
// Create or return a LoadingController instance
get loadingController() {
return getOrCreateController('ion-loading-controller')
}
// Create or return a MenuController instance
get menuController() {
return getOrCreateMenuController('ion-menu-controller')
}
// Create or return a ModalController instance
get modalController() {
return getOrCreateDelegatedController('ion-modal-controller')
}
// Create or return a PopoverController instance
get popoverController() {
return getOrCreateDelegatedController('ion-popover-controller')
}
// Create or return a ToastController instance
get toastController() {
return getOrCreateController('ion-toast-controller')
}
}
// Cached controllers
Api.cache = {
'ion-action-sheet-controller': null,
'ion-alert-controller': null,
'ion-loading-controller': null,
'ion-menu-controller': null,
'ion-modal-controller': null,
'ion-popover-controller': null,
'ion-toast-controller': null,
}
Api.install = function(Vue) {
// If installed - skip
if (Api.install.installed && _Vue === Vue) {
return
}
_Vue = Vue
_Delegate = new Delegate(Vue)
Api.install.installed = true
// Ignore Ionic custom elements
Vue.config.ignoredElements.push(/^ion-/)
// Give access to the API methods
Object.defineProperty(Vue.prototype, '$ionic', {
get() {
return new Api()
},
})
}
// Get existing Base controller instance or initialize a new one
function getOrCreateController(tag) {
if (!Api.cache[tag]) {
Api.cache[tag] = new ProxyController(tag)
}
return Api.cache[tag]
}
// Get existing Menu controller instance or initialize a new one
function getOrCreateMenuController(tag) {
if (!Api.cache[tag]) {
Api.cache[tag] = new ProxyMenuController(tag)
}
return Api.cache[tag]
}
// Get existing Delegated controller instance or initialize a new one
function getOrCreateDelegatedController(tag) {
if (!Api.cache[tag]) {
Api.cache[tag] = new ProxyDelegateController(tag, _Delegate)
}
return Api.cache[tag]
}

109
vue/src/api.ts Normal file
View File

@ -0,0 +1,109 @@
import Vue, { PluginFunction } from 'vue';
import { ApiCache, FrameworkDelegate } from './interfaces';
import Delegate from './framework-delegate';
import ProxyController from './proxy-controller';
import ProxyMenuController from './proxy-menu-controller';
import ProxyDelegateController from './proxy-delegate-controller';
let _Vue: typeof Vue, _Delegate: FrameworkDelegate;
export default class Api {
static cache: ApiCache;
static installed = false;
static install: PluginFunction<never>;
// Create or return a ActionSheetController instance
get actionSheetController(): ProxyController {
return getOrCreateController('ion-action-sheet-controller');
}
// Create or return an AlertController instance
get alertController(): ProxyController {
return getOrCreateController('ion-alert-controller');
}
// Create or return a LoadingController instance
get loadingController(): ProxyController {
return getOrCreateController('ion-loading-controller');
}
// Create or return a MenuController instance
get menuController(): ProxyMenuController {
return getOrCreateMenuController('ion-menu-controller');
}
// Create or return a ModalController instance
get modalController(): ProxyDelegateController {
return getOrCreateDelegatedController('ion-modal-controller');
}
// Create or return a PopoverController instance
get popoverController(): ProxyDelegateController {
return getOrCreateDelegatedController('ion-popover-controller');
}
// Create or return a ToastController instance
get toastController(): ProxyController {
return getOrCreateController('ion-toast-controller');
}
}
// Cached controllers
Api.cache = {
'ion-action-sheet-controller': null,
'ion-alert-controller': null,
'ion-loading-controller': null,
'ion-menu-controller': null,
'ion-modal-controller': null,
'ion-popover-controller': null,
'ion-toast-controller': null,
};
Api.install = (Vue): void => {
// If installed - skip
if (Api.installed && _Vue === Vue) {
return;
}
_Vue = Vue;
_Delegate = new Delegate(Vue);
Api.installed = true;
// Ignore Ionic custom elements
Vue.config.ignoredElements.push(/^ion-/);
// Give access to the API methods
Object.defineProperty(Vue.prototype, '$ionic', {
get() {
return new Api();
},
});
};
// Get existing Base controller instance or initialize a new one
function getOrCreateController(tag: string): ProxyController {
if (!Api.cache[tag]) {
Api.cache[tag] = new ProxyController(tag);
}
return Api.cache[tag];
}
// Get existing Menu controller instance or initialize a new one
function getOrCreateMenuController(tag: string): ProxyMenuController {
if (!Api.cache[tag]) {
Api.cache[tag] = new ProxyMenuController(tag);
}
return Api.cache[tag];
}
// Get existing Delegated controller instance or initialize a new one
function getOrCreateDelegatedController(tag: string): ProxyDelegateController {
if (!Api.cache[tag]) {
Api.cache[tag] = new ProxyDelegateController(tag, _Delegate);
}
return Api.cache[tag];
}

View File

@ -4,18 +4,13 @@
</div>
</template>
<script>
import catchIonicGoBack from '../mixins/catch-ionic-go-back.js'
<script lang="ts">
import { Prop } from 'vue-property-decorator';
import Component, { mixins } from 'vue-class-component';
import CatchIonicGoBack from '../mixins/catch-ionic-go-back';
export default {
name: 'IonVueRouter',
mixins: [catchIonicGoBack],
props: {
// A name to call "named views" by
name: {
type: String,
default: 'default',
},
},
@Component
export default class IonVueRouter extends mixins(CatchIonicGoBack) {
@Prop({ default: 'default'}) name!: string;
}
</script>

View File

@ -23,123 +23,112 @@
</ion-router-outlet>
</template>
<script>
import catchIonicGoBack from '../mixins/catch-ionic-go-back.js'
<script lang="ts">
import { Prop } from 'vue-property-decorator';
import Component, { mixins } from 'vue-class-component';
import CatchIonicGoBack from '../mixins/catch-ionic-go-back';
import { IonRouterOutlet } from '../interfaces';
export default {
name: 'IonVueRouter',
mixins: [catchIonicGoBack],
props: {
// A name to call "named views" by
name: {
type: String,
default: 'default',
},
// Set CSS classes during transitions
bindCSS: {
type: Boolean,
default: false,
},
// Animate transitions or not
animated: {
type: Boolean,
default: true,
},
},
data() {
return {
// Currently visible component
leavingEl: null,
@Component
export default class IonVueRouter extends mixins(CatchIonicGoBack) {
@Prop({ default: 'default'}) name!: string;
@Prop({ default: false }) bindCSS!: boolean;
@Prop({ default: true }) animated!: boolean;
// Component to be rendered
enteringEl: null,
// Currently visible component
leavingEl: HTMLElement;
// Component to be rendered
enteringEl: HTMLElement;
// Flag to see if we're still in a transition
inTransition = false;
customTransition = false;
// Flag to see if we're still in a transition
inTransition: false,
customTransition: false,
}
},
created() {
// Cancel navigation if there's a running transition
this.$router.beforeEach((to, from, next) => {
this.$router.beforeEach((to, _from, next) => {
this.customTransition = to.meta.customTransition || false
return this.$nextTick(() => {
return next(!this.inTransition)
return next(!this.inTransition as false);
})
})
},
methods: {
transition(enteringEl, leavingEl) {
// Get the reference to the Ionic component handling the transitions
const ionRouterOutlet = this.$refs.ionRouterOutlet
}
// The Ionic framework didn't load - skip animations
if (typeof ionRouterOutlet.componentOnReady === 'undefined') {
return
}
transition(enteringEl: HTMLElement, leavingEl: HTMLElement) {
// Get the reference to the Ionic component handling the transitions
const ionRouterOutlet = this.$refs.ionRouterOutlet as IonRouterOutlet;
// 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
}
// The Ionic framework didn't load - skip animations
if (typeof ionRouterOutlet.componentOnReady === 'undefined') {
return;
}
// Add the proper Ionic classes, important for smooth transitions
enteringEl.classList.add('ion-page', 'ion-page-invisible')
// 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;
}
// Commit to the transition as soon as the Ionic Router Outlet is ready
return ionRouterOutlet.componentOnReady().then(el => {
return el.commit(enteringEl, leavingEl, {
deepWait: true,
duration: this.getDuration(),
direction: this.getDirection(),
showGoBack: this.$router.canGoBack(),
})
})
},
// Instant transition if we don't want to animate
getDuration() {
return !this.animated ? 0 : undefined
},
// Get the navigation direction from the router
getDirection() {
return this.$router.direction === 1 ? 'forward' : 'back'
},
// Set the component to be rendered before we render the new route
beforeEnter(el) {
this.enteringEl = el
},
// Remember the current component before we leave the route
beforeLeave(el) {
this.leavingEl = el
},
// Transition when we leave the route
leave(el, done) {
const promise = this.transition(this.enteringEl, el)
// Add the proper Ionic classes, important for smooth transitions
enteringEl.classList.add('ion-page', 'ion-page-invisible')
this.inTransition = true
// Commit to the transition as soon as the Ionic Router Outlet is ready
return ionRouterOutlet.componentOnReady().then(el => {
return el.commit(enteringEl, leavingEl, {
deepWait: true,
duration: this.getDuration(),
direction: this.getDirection(),
showGoBack: this.$router.canGoBack(),
});
});
}
// Skip any transition if we don't get back a Promise
if (!promise) {
this.inTransition = false
return done()
}
// Instant transition if we don't want to animate
getDuration() {
return !this.animated ? 0 : undefined;
}
// Perform navigation once the transition was finished
return promise.then(() => {
this.inTransition = false
return done(true)
})
},
// Enter the new route
enter(el, done) {
done()
},
afterEnter(/* el */) {},
enterCancelled(/* el */) {},
afterLeave(/* el */) {},
leaveCancelled(/* el */) {},
},
// Get the navigation direction from the router
getDirection() {
return this.$router.direction === 1 ? 'forward' : 'back';
}
// Set the component to be rendered before we render the new route
beforeEnter(el: HTMLElement) {
this.enteringEl = el;
}
// Remember the current component before we leave the route
beforeLeave(el: HTMLElement) {
this.leavingEl = el;
}
// Transition when we leave the route
leave(el: HTMLElement, done: (opts?: boolean) => void): void {
const promise = this.transition(this.enteringEl, el);
this.inTransition = true;
// Skip any transition if we don't get back a Promise
if (!promise) {
this.inTransition = false;
return done();
}
// Perform navigation once the transition was finished
promise.then(() => {
this.inTransition = false;
return done(true);
});
}
// Enter the new route
enter(_el: HTMLElement, done: () => void): void {
done();
}
afterEnter(/* el */) {}
enterCancelled(/* el */) {}
afterLeave(/* el */) {}
leaveCancelled(/* el */) {}
}
</script>

View File

@ -1,74 +0,0 @@
export default class Delegate {
constructor(Vue) {
this.Vue = Vue
}
// Attach the passed Vue component to DOM
attachViewToDom(parentElement, component, opts, classes) {
// Handle HTML elements
if (isElement(component)) {
// Add any classes to the element
addClasses(component, classes)
// Append the element to DOM
parentElement.appendChild(component)
return Promise.resolve(component)
}
// Get the Vue controller
return this.vueController(component).then(controller => {
const vueComponent = this.vueComponent(controller, opts)
// Add any classes to the Vue component's root element
addClasses(vueComponent.$el, classes)
// Append the Vue component to DOM
parentElement.appendChild(vueComponent.$el)
return vueComponent.$el
})
}
// Remove the earlier created Vue component from DOM
removeViewFromDom(parentElement, childElement) {
// Destroy the Vue component instance
if (childElement.__vue__) {
childElement.__vue__.$destroy()
}
return Promise.resolve()
}
// Handle creation of sync and async components
vueController(component) {
return Promise.resolve(
typeof component === 'function' && component.cid === undefined
? component().then(c => this.Vue.extend(isESModule(c) ? c.default : c))
: this.Vue.extend(component)
)
}
// Create a new instance of the Vue component
vueComponent(controller, opts) {
return new controller(opts).$mount()
}
}
// Check Symbol support
const hasSymbol = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol'
// Check if object is an ES module
function isESModule(obj) {
return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module')
}
// Check if value is an Element
function isElement(el) {
return typeof Element !== 'undefined' && el instanceof Element
}
// Add an array of classes to an element
function addClasses(element, classes = []) {
for (const cls of classes) {
element.classList.add(cls)
}
}

View File

@ -0,0 +1,75 @@
import { VueConstructor } from 'vue';
import { EsModule, FrameworkDelegate, HTMLVueElement, WebpackFunction } from './interfaces';
export default class Delegate implements FrameworkDelegate {
constructor(public vue: VueConstructor) {}
// Attach the passed Vue component to DOM
attachViewToDom(parentElement: HTMLElement, component: HTMLElement | WebpackFunction | object | VueConstructor, opts?: object, classes?: string[]): Promise<HTMLElement> {
// Handle HTML elements
if (isElement(component)) {
// Add any classes to the element
addClasses(component as HTMLElement, classes);
// Append the element to DOM
parentElement.appendChild(component as HTMLElement);
return Promise.resolve(component as HTMLElement);
}
// Get the Vue controller
return this.vueController(component).then((controller: VueConstructor) => {
const vueComponent = this.vueComponent(controller, opts);
// Add any classes to the Vue component's root element
addClasses(vueComponent.$el, classes);
// Append the Vue component to DOM
parentElement.appendChild(vueComponent.$el);
return vueComponent.$el;
});
}
// Remove the earlier created Vue component from DOM
removeViewFromDom(_parentElement: HTMLElement, childElement: HTMLVueElement): Promise<void> {
// Destroy the Vue component instance
if (childElement.__vue__) {
childElement.__vue__.$destroy();
}
return Promise.resolve();
}
// Handle creation of sync and async components
vueController(component: WebpackFunction | object | VueConstructor): Promise<VueConstructor> {
return Promise.resolve(
typeof component === 'function' && (component as WebpackFunction).cid === undefined
? (component as WebpackFunction)().then((cmp: any) => this.vue.extend(isESModule(cmp) ? cmp.default : cmp))
: this.vue.extend(component)
);
}
// Create a new instance of the Vue component
vueComponent(controller: VueConstructor, opts?: object) {
return new controller(opts).$mount();
}
}
// Check Symbol support
const hasSymbol = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
// Check if object is an ES module
function isESModule(obj: EsModule) {
return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module');
}
// Check if value is an Element
function isElement(el: any) {
return typeof Element !== 'undefined' && el instanceof Element;
}
// Add an array of classes to an element
function addClasses(element: HTMLElement, classes: string[] = []) {
for (const cls of classes) {
element.classList.add(cls);
}
}

View File

@ -1,3 +0,0 @@
export { default as Ionic } from './ionic'
export { default as IonicAPI } from './api'
export { default as IonicVueRouter } from './router'

3
vue/src/index.ts Normal file
View File

@ -0,0 +1,3 @@
export { default as Ionic } from './ionic';
export { default as IonicAPI } from './api';
export { default as IonicVueRouter } from './router';

89
vue/src/interfaces.ts Normal file
View File

@ -0,0 +1,89 @@
import Vue from 'vue';
import IonicApi from './api';
import VueRouter from 'vue-router';
import { RouterOptions } from 'vue-router/types/router';
declare module 'vue/types/vue' {
interface Vue {
$ionic: IonicApi;
}
}
declare module 'vue-router/types/router' {
interface VueRouter {
direction: number;
directionOverride: number | null;
canGoBack(): boolean;
}
}
export interface HTMLVueElement extends HTMLElement {
__vue__: Vue;
}
export interface VueWindow extends Window {
Vue: typeof Vue;
VueRouter: typeof VueRouter;
disableIonicTransitions: boolean;
}
export interface WebpackFunction extends Function {
cid: number;
}
export interface EsModule extends Object {
__esModule?: boolean;
[Symbol.toStringTag]: string;
}
export interface HTMLStencilElement extends HTMLElement {
componentOnReady(): Promise<this>;
componentOnReady(done: (el?: this) => void): void;
forceUpdate(): void;
}
export interface FrameworkDelegate {
attachViewToDom(parentElement: HTMLElement, component: HTMLElement | WebpackFunction | object | Vue, opts?: object, classes?: string[]): Promise<HTMLElement>;
removeViewFromDom(parentElement: HTMLElement, childElement: HTMLVueElement): Promise<void>;
}
export interface IonBackButton extends HTMLStencilElement {
defaultHref?: string;
}
export interface IonRouterOutlet extends HTMLStencilElement {
commit(enterinEl: HTMLElement, leavingEl: HTMLElement | undefined, opts?: object | undefined): Promise<boolean>;
}
export interface ApiCache {
[key: string]: any;
}
export interface RouterArgs extends RouterOptions {
direction: number;
viewCount: number;
}
export interface ProxyControllerInterface {
create(opts: object): Promise<HTMLElement>;
dismiss(): Promise<void>;
getTop(): Promise<HTMLElement>;
}
export interface ProxyDelegateOptions extends Object {
[key: string]: any;
delegate?: FrameworkDelegate;
}
export interface ProxyMenuControllerInterface {
open(menuId?: string): Promise<boolean>;
close(menuId?: string): Promise<boolean>;
toggle(menuId?: string): Promise<boolean>;
enable(shouldEnable: boolean, menuId?: string): Promise<HTMLElement>;
swipeEnable(shouldEnable: boolean, menuId?: string): Promise<HTMLElement>;
isOpen(menuId?: string): Promise<boolean>;
isEnabled(menuId?: string): Promise<boolean>;
get(menuId?: string): Promise<HTMLElement>;
getOpen(): Promise<HTMLElement>;
getMenus(): Promise<HTMLElement>;
}

View File

@ -1,10 +0,0 @@
import '@ionic/core/css/ionic.bundle.css'
import 'ionicons/dist/collection/icon/icon.css'
import '@ionic/core/dist/ionic/svg'
import { defineCustomElements } from '@ionic/core/loader'
export default {
init(opts = {}) {
defineCustomElements(window, opts)
},
}

10
vue/src/ionic.ts Normal file
View File

@ -0,0 +1,10 @@
import '@ionic/core/css/ionic.bundle.css';
import 'ionicons/dist/collection/icon/icon.css';
import '@ionic/core/dist/ionic/svg';
import { defineCustomElements } from '@ionic/core/loader';
export default {
init(): void {
defineCustomElements(window);
},
};

View File

@ -1,27 +0,0 @@
export default {
methods: {
// Catch the bubbled-up event from the Ionic's back button
catchIonicGoBack(event) {
// We only care for the event coming from Ionic's back button
const backButton = event.target && event.target.closest('ion-back-button')
if (!backButton) return
let defaultHref
// Explicitly override router direction
// This will always trigger a back transition
this.$router.directionOverride = -1
// If we can go back - do so
// otherwise if there's a default fall-back - use it
// else - skip
if (this.$router.canGoBack()) {
event.preventDefault()
this.$router.back()
} else if (undefined !== (defaultHref = backButton.defaultHref)) {
event.preventDefault()
this.$router.push(defaultHref)
}
},
},
}

View File

@ -0,0 +1,36 @@
import Vue from 'vue';
import Router from '../router';
import Component from 'vue-class-component';
import { IonBackButton } from '../interfaces';
@Component
export default class CatchIonicGoBack extends Vue {
// Catch the bubbled-up event from the Ionic's back button
catchIonicGoBack(event: Event): void {
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 IonBackButton;
if (!backButton) return;
const $router = this.$router as Router;
let defaultHref: string;
// Explicitly override router direction to always trigger a back transition
$router.directionOverride = -1;
// 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);
}
}
}

View File

@ -1,20 +0,0 @@
import * as apiUtils from './api-utils'
// A proxy class that allows early access to controller methods
export default class ProxyController {
constructor(tag) {
this.tag = tag
}
create(opts = {}) {
return apiUtils.proxyMethod(this.tag, 'create', opts)
}
dismiss() {
return apiUtils.proxyMethod(this.tag, 'dismiss')
}
getTop() {
return apiUtils.proxyMethod(this.tag, 'getTop')
}
}

View File

@ -0,0 +1,19 @@
import * as apiUtils from './api-utils';
import { ProxyControllerInterface } from './interfaces';
// A proxy class that allows early access to controller methods
export default class ProxyController implements ProxyControllerInterface {
constructor(public tag: string) {}
create(opts: object = {}): Promise<HTMLElement> {
return apiUtils.proxyMethod(this.tag, 'create', opts);
}
dismiss(): Promise<void> {
return apiUtils.proxyMethod(this.tag, 'dismiss');
}
getTop(): Promise<HTMLElement> {
return apiUtils.proxyMethod(this.tag, 'getTop');
}
}

View File

@ -1,17 +0,0 @@
import ProxyController from './proxy-controller'
// A proxy class that allows early access to controller methods
export default class ProxyDelegateController extends ProxyController {
constructor(tag, delegate) {
super(tag)
if (!ProxyDelegateController.delegate) {
ProxyDelegateController.delegate = delegate
}
}
create(opts = {}) {
opts.delegate = ProxyDelegateController.delegate
return super.create(opts)
}
}

View File

@ -0,0 +1,20 @@
import ProxyController from './proxy-controller';
import { FrameworkDelegate, ProxyDelegateOptions } from './interfaces';
// A proxy class that allows early access to controller methods
export default class ProxyDelegateController extends ProxyController {
static delegate: FrameworkDelegate;
constructor(public tag: string, delegate: FrameworkDelegate) {
super(tag);
if (!ProxyDelegateController.delegate) {
ProxyDelegateController.delegate = delegate;
}
}
create(opts: ProxyDelegateOptions = {} as ProxyDelegateOptions) {
opts.delegate = ProxyDelegateController.delegate;
return super.create(opts);
}
}

View File

@ -1,58 +0,0 @@
import * as apiUtils from './api-utils'
// A proxy class that allows early access to controller methods
export default class ProxyMenuController {
constructor(tag) {
this.tag = tag
}
// Open a menu
open(menuId) {
return apiUtils.proxyMethod(this.tag, 'open', menuId)
}
// Close a menu
close(menuId) {
return apiUtils.proxyMethod(this.tag, 'close', menuId)
}
// Toggle a menu
toggle(menuId) {
return apiUtils.proxyMethod(this.tag, 'toggle', menuId)
}
// Enable or disable a menu
enable(shouldEnable, menuId) {
return apiUtils.proxyMethod(this.tag, 'enable', shouldEnable, menuId)
}
// Enable or disable the ability to swipe open the menu
swipeEnable(shouldEnable, menuId) {
return apiUtils.proxyMethod(this.tag, 'swipeEnable', shouldEnable, menuId)
}
// Check if specific or any menu is open
isOpen(menuId) {
return apiUtils.proxyMethod(this.tag, 'isOpen', menuId)
}
// Check is certain menu is enabled
isEnabled(menuId) {
return apiUtils.proxyMethod(this.tag, 'isEnabled', menuId)
}
// Get specific or first menu instance
get(menuId) {
return apiUtils.proxyMethod(this.tag, 'get', menuId)
}
// Get an instance of an open menu
getOpen() {
return apiUtils.proxyMethod(this.tag, 'getOpen')
}
// Get an array of all menus
getMenus() {
return apiUtils.proxyMethod(this.tag, 'getMenus')
}
}

View File

@ -0,0 +1,57 @@
import * as apiUtils from './api-utils';
import { ProxyMenuControllerInterface } from './interfaces';
// A proxy class that allows early access to controller methods
export default class ProxyMenuController implements ProxyMenuControllerInterface {
constructor(public tag: string) {}
// Open a menu
open(menuId?: string): Promise<boolean> {
return apiUtils.proxyMethod(this.tag, 'open', menuId);
}
// Close a menu
close(menuId?: string): Promise<boolean> {
return apiUtils.proxyMethod(this.tag, 'close', menuId);
}
// Toggle a menu
toggle(menuId?: string): Promise<boolean> {
return apiUtils.proxyMethod(this.tag, 'toggle', menuId);
}
// Enable or disable a menu
enable(shouldEnable: boolean, menuId?: string): Promise<HTMLElement> {
return apiUtils.proxyMethod(this.tag, 'enable', shouldEnable, menuId);
}
// Enable or disable the ability to swipe open the menu
swipeEnable(shouldEnable: boolean, menuId?: string): Promise<HTMLElement> {
return apiUtils.proxyMethod(this.tag, 'swipeEnable', shouldEnable, menuId);
}
// Check if specific or any menu is open
isOpen(menuId?: string): Promise<boolean> {
return apiUtils.proxyMethod(this.tag, 'isOpen', menuId);
}
// Check is certain menu is enabled
isEnabled(menuId?: string): Promise<boolean> {
return apiUtils.proxyMethod(this.tag, 'isEnabled', menuId);
}
// Get specific or first menu instance
get(menuId?: string): Promise<HTMLElement> {
return apiUtils.proxyMethod(this.tag, 'get', menuId);
}
// Get an instance of an open menu
getOpen(): Promise<HTMLElement> {
return apiUtils.proxyMethod(this.tag, 'getOpen');
}
// Get an array of all menus
getMenus(): Promise<HTMLElement> {
return apiUtils.proxyMethod(this.tag, 'getMenus');
}
}

View File

@ -1,101 +0,0 @@
import VueRouter from 'vue-router'
import IonVueRouter from './components/ion-vue-router.vue'
import IonVueRouterTransitionless from './components/ion-vue-router-transitionless.vue'
const inBrowser = typeof window !== 'undefined'
// Detect environment (browser, module, etc.)
const _VueRouter = inBrowser && window.VueRouter ? window.VueRouter : VueRouter
// Extend the official VueRouter
export default class Router extends _VueRouter {
constructor(...args) {
super(...args)
// The direction user navigates in
this.direction = args.direction || 1
// Override normal direction
this.directionOverride = null
// Number of views navigated
this.viewCount = args.viewCount || 0
// Stack of previous routes
this.prevRouteStack = []
// Extend the existing history object
this.extendHistory()
}
extendHistory() {
// Save a reference to the original method
this.history._updateRoute = this.history.updateRoute
this.history.updateRoute = nextRoute => {
// Guesstimate the direction of the next route
this.direction = this.guessDirection(nextRoute)
// Override the direction
if (this.directionOverride) {
this.direction = this.directionOverride
}
// Increment or decrement the view count
this.viewCount += this.direction
// Call the original method
this.history._updateRoute(nextRoute)
// Reset direction for overrides
this.directionOverride = null
}
}
canGoBack() {
// We can display the back button if we're not on /
// or there were more than 1 views rendered
return this.viewCount > 1 && this.currentRoute.fullPath.length > 1
}
guessDirection(nextRoute) {
if (this.prevRouteStack.length !== 0) {
const prevRoute = this.prevRouteStack[this.prevRouteStack.length - 1]
// Last route is the same as the next one - go back
// If we're going to / reset the stack otherwise pop a route
if (prevRoute.fullPath === nextRoute.fullPath) {
if (prevRoute.fullPath.length === 1) {
this.prevRouteStack = []
} else {
this.prevRouteStack.pop()
}
return -1
}
}
// Forward movement, push next route to stack
if (this.history.current.fullPath !== nextRoute.fullPath) {
this.prevRouteStack.push(this.history.current)
}
return 1
}
}
Router.install = function(Vue, { disableIonicTransitions } = {}) {
// If already installed - skip
if (Router.install.installed) {
return
}
Router.install.installed = true
// Install the official VueRouter
_VueRouter.install(Vue)
// Register the IonVueRouter component globally
// either with default Ionic transitions turned on or off
Vue.component('IonVueRouter', disableIonicTransitions ? IonVueRouterTransitionless : IonVueRouter)
}
// Auto-install when Vue is found (i.e. in browser via <script> tag)
if (inBrowser && window.Vue) {
window.Vue.use(Router, { disableIonicTransitions: window.disableIonicTransitions })
}

115
vue/src/router.ts Normal file
View File

@ -0,0 +1,115 @@
import VueRouter, { Route } from 'vue-router';
import { PluginFunction } from 'vue';
import { RouterArgs, VueWindow } from './interfaces';
import IonVueRouter from './components/ion-vue-router.vue';
import IonVueRouterTransitionless from './components/ion-vue-router-transitionless.vue';
const vueWindow = window as VueWindow;
const inBrowser: boolean = typeof window !== 'undefined';
// Detect environment (browser, module, etc.)
const _VueRouter: typeof VueRouter = inBrowser && vueWindow.VueRouter ? vueWindow.VueRouter : VueRouter;
// Extend the official VueRouter
export default class Router extends _VueRouter {
direction: number;
directionOverride: number | null;
viewCount: number;
prevRouteStack: Route[];
history: any;
static installed: boolean;
static install: PluginFunction<never>;
constructor(args: RouterArgs = {} as RouterArgs) {
super(args);
// The direction user navigates in
this.direction = args.direction || 1;
// Override normal direction
this.directionOverride = null;
// Number of views navigated
this.viewCount = args.viewCount || 0;
// Stack of previous routes
this.prevRouteStack = [];
// Extend the existing history object
this.extendHistory();
}
extendHistory(): void {
// Save a reference to the original method
this.history._updateRoute = this.history.updateRoute;
this.history.updateRoute = (nextRoute: Route) => {
// Guesstimate the direction of the next route
this.direction = this.guessDirection(nextRoute);
// Override the direction
if (this.directionOverride) {
this.direction = this.directionOverride;
}
// Increment or decrement the view count
this.viewCount += this.direction;
// Call the original method
this.history._updateRoute(nextRoute);
// Reset direction for overrides
this.directionOverride = null;
};
}
canGoBack(): boolean {
// We can display the back button if we're not on /
// or there were more than 1 views rendered
return this.viewCount > 1 && this.currentRoute.fullPath.length > 1;
}
guessDirection(nextRoute: Route): number {
if (this.prevRouteStack.length !== 0) {
const prevRoute: Route = this.prevRouteStack[this.prevRouteStack.length - 1];
// Last route is the same as the next one - go back
// If we're going to / reset the stack otherwise pop a route
if (prevRoute.fullPath === nextRoute.fullPath) {
if (prevRoute.fullPath.length === 1) {
this.prevRouteStack = [];
} else {
this.prevRouteStack.pop();
}
return -1;
}
}
// Forward movement, push next route to stack
if (this.history.current.fullPath !== nextRoute.fullPath) {
this.prevRouteStack.push(this.history.current);
}
return 1;
}
}
Router.install = (Vue, { disableIonicTransitions = false }: { disableIonicTransitions?: boolean } = {}): void => {
// If already installed - skip
if (Router.installed) {
return;
}
Router.installed = true;
// Install the official VueRouter
_VueRouter.install(Vue);
// Register the IonVueRouter component globally
// either with default Ionic transitions turned on or off
Vue.component('IonVueRouter', disableIonicTransitions ? IonVueRouterTransitionless : IonVueRouter);
};
// Auto-install when Vue is found (i.e. in browser via <script> tag)
if (inBrowser && vueWindow.Vue) {
vueWindow.Vue.use(Router, { disableIonicTransitions: vueWindow.disableIonicTransitions });
}

4
vue/src/sfc.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
declare module '*.vue' {
import Vue from 'vue';
export default Vue;
}