docs(): update examples

This commit is contained in:
mhartington
2018-06-14 11:46:38 -04:00
parent ad3538d089
commit 04e807f459
29 changed files with 746 additions and 400 deletions

View File

@ -1,19 +1,49 @@
import { Build, Component, Element, Event, EventEmitter, Method, Prop, Watch } from '@stencil/core';
import {
Build,
Component,
Element,
Event,
EventEmitter,
Method,
Prop,
Watch
} from '@stencil/core';
import { ViewLifecycle } from '../..';
import {
Animation, ComponentProps, Config, FrameworkDelegate, GestureDetail, Mode,
NavComponent, NavOptions, NavOutlet, NavResult, QueueController, RouteID,
RouteWrite, TransitionDoneFn, TransitionInstruction } from '../../interface';
Animation,
ComponentProps,
Config,
FrameworkDelegate,
GestureDetail,
Mode,
NavComponent,
NavOptions,
NavOutlet,
NavResult,
QueueController,
RouteID,
RouteWrite,
TransitionDoneFn,
TransitionInstruction
} from '../../interface';
import { assert } from '../../utils/helpers';
import { TransitionOptions, lifecycle, transition } from '../../utils/transition';
import {
TransitionOptions,
lifecycle,
transition
} from '../../utils/transition';
import { RouterIntent } from '../router/utils/constants';
import { ViewController, ViewState, convertToViews, matches } from './view-controller';
import {
ViewController,
ViewState,
convertToViews,
matches
} from './view-controller';
@Component({
tag: 'ion-nav',
tag: 'ion-nav'
})
export class Nav implements NavOutlet {
private transInstr: TransitionInstruction[] = [];
private sbTrns?: Animation;
private useRouter = false;
@ -25,16 +55,41 @@ export class Nav implements NavOutlet {
@Element() el!: HTMLElement;
@Prop({ context: 'queue' }) queue!: QueueController;
@Prop({ context: 'config' }) config!: Config;
@Prop({ context: 'window' }) win!: Window;
@Prop({ context: 'queue' })
queue!: QueueController;
@Prop({ connect: 'ion-animation-controller' }) animationCtrl!: HTMLIonAnimationControllerElement;
@Prop({ mutable: true }) swipeBackEnabled?: boolean;
@Prop({ mutable: true }) animated?: boolean;
@Prop({ context: 'config' })
config!: Config;
@Prop({ context: 'window' })
win!: Window;
@Prop({ connect: 'ion-animation-controller' })
animationCtrl!: HTMLIonAnimationControllerElement;
/**
* If the nav component should allow for swipe-to-go-back
*/
@Prop({ mutable: true })
swipeBackEnabled?: boolean;
/**
* If the nav should animate the components or not
*/
@Prop({ mutable: true })
animated?: boolean;
/** @hidden */
@Prop() delegate?: FrameworkDelegate;
/**
* Any parameters for the root component
*/
@Prop() rootParams?: ComponentProps;
/**
* Root NavComponent to load
*/
@Prop() root?: NavComponent;
@Watch('root')
rootChanged() {
const isDev = Build.isDev;
@ -42,19 +97,35 @@ export class Nav implements NavOutlet {
if (!this.useRouter) {
this.setRoot(this.root, this.rootParams);
} else if (isDev) {
console.warn('<ion-nav> does not support a root attribute when using ion-router.');
console.warn(
'<ion-nav> does not support a root attribute when using ion-router.'
);
}
}
}
/**
* Event fired when Nav will load a component
*/
@Event() ionNavWillLoad!: EventEmitter<void>;
/**
* Event fired when the nav will components
*/
@Event() ionNavWillChange!: EventEmitter<void>;
/**
* Event fired when the nav has changed components
*/
@Event() ionNavDidChange!: EventEmitter<void>;
componentWillLoad() {
this.useRouter = !!this.win.document.querySelector('ion-router') && !this.el.closest('[no-router]');
this.useRouter =
!!this.win.document.querySelector('ion-router') &&
!this.el.closest('[no-router]');
if (this.swipeBackEnabled === undefined) {
this.swipeBackEnabled = this.config.getBoolean('swipeBackEnabled', this.mode === 'ios');
this.swipeBackEnabled = this.config.getBoolean(
'swipeBackEnabled',
this.mode === 'ios'
);
}
if (this.animated === undefined) {
this.animated = this.config.getBoolean('animate', true);
@ -79,44 +150,91 @@ export class Nav implements NavOutlet {
this.destroyed = true;
}
/**
* Push a new component onto the current navigation stack. Pass any aditional information along as an object. This additional information is accessible through NavParams
*/
@Method()
push(component: NavComponent, componentProps?: ComponentProps|null, opts?: NavOptions|null, done?: TransitionDoneFn): Promise<boolean> {
return this.queueTrns({
insertStart: -1,
insertViews: [{ page: component, params: componentProps }],
opts: opts,
}, done);
push(
component: NavComponent,
componentProps?: ComponentProps | null,
opts?: NavOptions | null,
done?: TransitionDoneFn
): Promise<boolean> {
return this.queueTrns(
{
insertStart: -1,
insertViews: [{ page: component, params: componentProps }],
opts: opts
},
done
);
}
/**
* Inserts a component into the nav stack at the specified index. This is useful if you need to add a component at any point in your navigation stack.
*/
@Method()
insert(insertIndex: number, component: NavComponent, componentProps?: ComponentProps|null, opts?: NavOptions|null, done?: TransitionDoneFn): Promise<boolean> {
return this.queueTrns({
insertStart: insertIndex,
insertViews: [{ page: component, params: componentProps }],
opts: opts,
}, done);
insert(
insertIndex: number,
component: NavComponent,
componentProps?: ComponentProps | null,
opts?: NavOptions | null,
done?: TransitionDoneFn
): Promise<boolean> {
return this.queueTrns(
{
insertStart: insertIndex,
insertViews: [{ page: component, params: componentProps }],
opts: opts
},
done
);
}
/**
* Inserts an array of components into the nav stack at the specified index. The last component in the array will become instantiated as a view, and animate in to become the active view.
*/
@Method()
insertPages(insertIndex: number, insertComponents: NavComponent[], opts?: NavOptions|null, done?: TransitionDoneFn): Promise<boolean> {
return this.queueTrns({
insertStart: insertIndex,
insertViews: insertComponents,
opts: opts,
}, done);
insertPages(
insertIndex: number,
insertComponents: NavComponent[],
opts?: NavOptions | null,
done?: TransitionDoneFn
): Promise<boolean> {
return this.queueTrns(
{
insertStart: insertIndex,
insertViews: insertComponents,
opts: opts
},
done
);
}
/**
* Call to navigate back from a current component. Similar to push(), you can also pass navigation options.
*/
@Method()
pop(opts?: NavOptions|null, done?: TransitionDoneFn): Promise<boolean> {
return this.queueTrns({
removeStart: -1,
removeCount: 1,
opts: opts,
}, done);
pop(opts?: NavOptions | null, done?: TransitionDoneFn): Promise<boolean> {
return this.queueTrns(
{
removeStart: -1,
removeCount: 1,
opts: opts
},
done
);
}
/**
* Pop to a specific index in the navigation stack
*/
@Method()
popTo(indexOrViewCtrl: number | ViewController, opts?: NavOptions|null, done?: TransitionDoneFn): Promise<boolean> {
popTo(
indexOrViewCtrl: number | ViewController,
opts?: NavOptions | null,
done?: TransitionDoneFn
): Promise<boolean> {
const config: TransitionInstruction = {
removeStart: -1,
removeCount: -1,
@ -131,31 +249,70 @@ export class Nav implements NavOutlet {
return this.queueTrns(config, done);
}
/**
* Navigate back to the root of the stack, no matter how far back that is.
*/
@Method()
popToRoot(opts?: NavOptions|null, done?: TransitionDoneFn): Promise<boolean> {
return this.queueTrns({
removeStart: 1,
removeCount: -1,
opts: opts,
}, done);
popToRoot(
opts?: NavOptions | null,
done?: TransitionDoneFn
): Promise<boolean> {
return this.queueTrns(
{
removeStart: 1,
removeCount: -1,
opts: opts
},
done
);
}
/**
* Removes a page from the nav stack at the specified index.
*/
@Method()
removeIndex(startIndex: number, removeCount = 1, opts?: NavOptions|null, done?: TransitionDoneFn): Promise<boolean> {
return this.queueTrns({
removeStart: startIndex,
removeCount: removeCount,
opts: opts,
}, done);
removeIndex(
startIndex: number,
removeCount = 1,
opts?: NavOptions | null,
done?: TransitionDoneFn
): Promise<boolean> {
return this.queueTrns(
{
removeStart: startIndex,
removeCount: removeCount,
opts: opts
},
done
);
}
/**
* Set the root for the current navigation stack.
*/
@Method()
setRoot(component: NavComponent, componentProps?: ComponentProps|null, opts?: NavOptions|null, done?: TransitionDoneFn): Promise<boolean> {
return this.setPages([{ page: component, params: componentProps }], opts, done);
setRoot(
component: NavComponent,
componentProps?: ComponentProps | null,
opts?: NavOptions | null,
done?: TransitionDoneFn
): Promise<boolean> {
return this.setPages(
[{ page: component, params: componentProps }],
opts,
done
);
}
/**
* Set the views of the current navigation stack and navigate to the last view. By default animations are disabled, but they can be enabled by passing options to the navigation controller.You can also pass any navigation params to the individual pages in the array.
*/
@Method()
setPages(views: any[], opts?: NavOptions|null, done?: TransitionDoneFn): Promise<boolean> {
setPages(
views: any[],
opts?: NavOptions | null,
done?: TransitionDoneFn
): Promise<boolean> {
if (!opts) {
opts = {};
}
@ -163,17 +320,25 @@ export class Nav implements NavOutlet {
if (opts.animated !== true) {
opts.animated = false;
}
return this.queueTrns({
insertStart: 0,
insertViews: views,
removeStart: 0,
removeCount: -1,
opts: opts
}, done);
return this.queueTrns(
{
insertStart: 0,
insertViews: views,
removeStart: 0,
removeCount: -1,
opts: opts
},
done
);
}
/** @hidden */
@Method()
setRouteId(id: string, params: any, direction: RouterIntent): Promise<RouteWrite> {
setRouteId(
id: string,
params: any,
direction: RouterIntent
): Promise<RouteWrite> {
const active = this.getActive();
if (matches(active, id, params)) {
return Promise.resolve({
@ -183,13 +348,13 @@ export class Nav implements NavOutlet {
}
let resolve: (result: RouteWrite) => void;
const promise = new Promise<RouteWrite>((r) => resolve = r);
const promise = new Promise<RouteWrite>(r => (resolve = r));
let finish: Promise<boolean>;
const commonOpts: NavOptions = {
updateURL: false,
viewIsReady: (enteringEl) => {
viewIsReady: enteringEl => {
let mark: Function;
const p = new Promise(r => mark = r);
const p = new Promise(r => (mark = r));
resolve({
changed: true,
element: enteringEl,
@ -208,51 +373,76 @@ export class Nav implements NavOutlet {
const viewController = this.views.find(v => matches(v, id, params));
if (viewController) {
finish = this.popTo(viewController, {...commonOpts, direction: 'back'});
finish = this.popTo(viewController, {
...commonOpts,
direction: 'back'
});
} else if (direction === 1) {
finish = this.push(id, params, commonOpts);
} else if (direction === -1) {
finish = this.setRoot(id, params, {...commonOpts, direction: 'back', animated: true});
finish = this.setRoot(id, params, {
...commonOpts,
direction: 'back',
animated: true
});
}
}
return promise;
}
/** @hidden */
@Method()
getRouteId(): RouteID|undefined {
getRouteId(): RouteID | undefined {
const active = this.getActive();
return active ? {
id: active.element!.tagName,
params: active.params,
element: active.element
} : undefined;
return active
? {
id: active.element!.tagName,
params: active.params,
element: active.element
}
: undefined;
}
/**
* Returns true or false if the current view can go back
*/
@Method()
canGoBack(view = this.getActive()): boolean {
return !!(view && this.getPrevious(view));
}
/**
* Gets the active view
*/
@Method()
getActive(): ViewController|undefined {
getActive(): ViewController | undefined {
return this.views[this.views.length - 1];
}
/**
* Returns the view at the index
*/
@Method()
getByIndex(index: number): ViewController|undefined {
getByIndex(index: number): ViewController | undefined {
return this.views[index];
}
/**
* Gets the previous view
*/
@Method()
getPrevious(view = this.getActive()): ViewController|undefined {
getPrevious(view = this.getActive()): ViewController | undefined {
if (!view) {
return undefined;
}
const views = this.views;
const index = views.indexOf(view);
return (index > 0) ? views[index - 1] : undefined;
return index > 0 ? views[index - 1] : undefined;
}
/**
* Returns the length of navigation stack
*/
@Method()
length() {
return this.views.length;
@ -268,7 +458,10 @@ export class Nav implements NavOutlet {
// 7. _transitionStart(): called once the transition actually starts, it initializes the Animation underneath.
// 8. _transitionFinish(): called once the transition finishes
// 9. _cleanup(): syncs the navigation internal state with the DOM. For example it removes the pages from the DOM or hides/show them.
private queueTrns(ti: TransitionInstruction, done: TransitionDoneFn|undefined): Promise<boolean> {
private queueTrns(
ti: TransitionInstruction,
done: TransitionDoneFn | undefined
): Promise<boolean> {
const promise = new Promise<boolean>((resolve, reject) => {
ti.resolve = resolve;
ti.reject = reject;
@ -310,9 +503,7 @@ export class Nav implements NavOutlet {
if (ti.opts!.updateURL !== false && this.useRouter) {
const router = this.win.document.querySelector('ion-router');
if (router) {
const direction = (result.direction === 'back')
? -1
: 1;
const direction = result.direction === 'back' ? -1 : 1;
router.navChanged(direction);
}
@ -377,17 +568,19 @@ export class Nav implements NavOutlet {
this.postViewInit(enteringView, leavingView, ti);
// Needs transition?
const requiresTransition = (ti.enteringRequiresTransition || ti.leavingRequiresTransition) && enteringView !== leavingView;
const requiresTransition =
(ti.enteringRequiresTransition || ti.leavingRequiresTransition) &&
enteringView !== leavingView;
const result = requiresTransition
? await this.transition(enteringView!, leavingView, ti)
: {
// transition is not required, so we are already done!
// they're inserting/removing the views somewhere in the middle or
// beginning, so visually nothing needs to animate/transition
// resolve immediately because there's no animation that's happening
hasCompleted: true,
requiresTransition: false
};
// transition is not required, so we are already done!
// they're inserting/removing the views somewhere in the middle or
// beginning, so visually nothing needs to animate/transition
// resolve immediately because there's no animation that's happening
hasCompleted: true,
requiresTransition: false
};
this.success(result, ti);
this.ionNavDidChange.emit();
@ -418,12 +611,13 @@ export class Nav implements NavOutlet {
}
if (ti.removeStart != null) {
if (ti.removeStart < 0) {
ti.removeStart = (viewsLength - 1);
ti.removeStart = viewsLength - 1;
}
if (ti.removeCount! < 0) {
ti.removeCount = (viewsLength - ti.removeStart);
ti.removeCount = viewsLength - ti.removeStart;
}
ti.leavingRequiresTransition = (ti.removeCount! > 0) && ((ti.removeStart + ti.removeCount!) === viewsLength);
ti.leavingRequiresTransition =
ti.removeCount! > 0 && ti.removeStart + ti.removeCount! === viewsLength;
}
if (ti.insertViews) {
@ -432,7 +626,7 @@ export class Nav implements NavOutlet {
if (ti.insertStart! < 0 || ti.insertStart! > viewsLength) {
ti.insertStart = viewsLength;
}
ti.enteringRequiresTransition = (ti.insertStart === viewsLength);
ti.enteringRequiresTransition = ti.insertStart === viewsLength;
}
const insertViews = ti.insertViews;
@ -460,7 +654,10 @@ export class Nav implements NavOutlet {
ti.insertViews = viewControllers;
}
private getEnteringView(ti: TransitionInstruction, leavingView: ViewController|undefined): ViewController|undefined {
private getEnteringView(
ti: TransitionInstruction,
leavingView: ViewController | undefined
): ViewController | undefined {
const insertViews = ti.insertViews;
if (insertViews) {
// grab the very last view of the views to be inserted
@ -482,8 +679,15 @@ export class Nav implements NavOutlet {
return undefined;
}
private postViewInit(enteringView: ViewController|undefined, leavingView: ViewController|undefined, ti: TransitionInstruction) {
assert(leavingView || enteringView, 'Both leavingView and enteringView are null');
private postViewInit(
enteringView: ViewController | undefined,
leavingView: ViewController | undefined,
ti: TransitionInstruction
) {
assert(
leavingView || enteringView,
'Both leavingView and enteringView are null'
);
assert(ti.resolve, 'resolve must be valid');
assert(ti.reject, 'reject must be valid');
@ -491,7 +695,7 @@ export class Nav implements NavOutlet {
const insertViews = ti.insertViews;
const removeStart = ti.removeStart;
const removeCount = ti.removeCount;
let destroyQueue: ViewController[]|undefined = undefined;
let destroyQueue: ViewController[] | undefined = undefined;
// there are views to remove
if (removeStart != null && removeCount != null) {
@ -509,11 +713,17 @@ export class Nav implements NavOutlet {
opts.direction = opts.direction || 'back';
}
const finalBalance = this.views.length + (insertViews ? insertViews.length : 0) - (removeCount ? removeCount : 0);
const finalBalance =
this.views.length +
(insertViews ? insertViews.length : 0) -
(removeCount ? removeCount : 0);
assert(finalBalance >= 0, 'final balance can not be negative');
if (finalBalance === 0) {
console.warn(`You can't remove all the pages in the navigation stack. nav.pop() is probably called too many times.`,
this, this.el);
console.warn(
`You can't remove all the pages in the navigation stack. nav.pop() is probably called too many times.`,
this,
this.el
);
throw new Error('navigation stack needs at least one root page');
}
@ -553,7 +763,11 @@ export class Nav implements NavOutlet {
}
}
private async transition(enteringView: ViewController, leavingView: ViewController|undefined, ti: TransitionInstruction): Promise<NavResult> {
private async transition(
enteringView: ViewController,
leavingView: ViewController | undefined,
ti: TransitionInstruction
): Promise<NavResult> {
if (this.sbTrns) {
this.sbTrns.destroy();
this.sbTrns = undefined;
@ -564,7 +778,9 @@ export class Nav implements NavOutlet {
const opts = ti.opts!;
const progressCallback = opts.progressAnimation
? (animation: Animation) => { this.sbTrns = animation; }
? (animation: Animation) => {
this.sbTrns = animation;
}
: undefined;
const enteringEl = enteringView.element!;
@ -586,7 +802,12 @@ export class Nav implements NavOutlet {
return this.transitionFinish(trns, enteringView, leavingView, opts);
}
private transitionFinish(transition: Animation|null, enteringView: ViewController, leavingView: ViewController | undefined, opts: NavOptions): NavResult {
private transitionFinish(
transition: Animation | null,
enteringView: ViewController,
leavingView: ViewController | undefined,
opts: NavOptions
): NavResult {
const hasCompleted = transition ? transition.hasCompleted : true;
const cleanupView = hasCompleted ? enteringView : leavingView;
@ -627,7 +848,10 @@ export class Nav implements NavOutlet {
}
private removeView(view: ViewController) {
assert(view.state === ViewState.Attached || view.state === ViewState.Destroyed, 'view state should be loaded or destroyed');
assert(
view.state === ViewState.Attached || view.state === ViewState.Destroyed,
'view state should be loaded or destroyed'
);
const views = this.views;
const index = views.indexOf(view);
@ -662,7 +886,6 @@ export class Nav implements NavOutlet {
// let's unload it
lifecycle(this.win, view.element, ViewLifecycle.WillUnload);
this.destroyView(view);
} else if (i < activeViewIndex) {
// this view comes before the active view
// and it is not a portal then ensure it is hidden
@ -682,11 +905,14 @@ export class Nav implements NavOutlet {
progressAnimation: true
};
this.queueTrns({
removeStart: -1,
removeCount: 1,
opts: opts,
}, undefined);
this.queueTrns(
{
removeStart: -1,
removeCount: 1,
opts: opts
},
undefined
);
}
private swipeBackProgress(detail: GestureDetail) {
@ -710,8 +936,8 @@ export class Nav implements NavOutlet {
const stepValue = delta / width;
const velocity = detail.velocityX;
const z = width / 2.0;
const shouldComplete = (velocity >= 0)
&& (velocity > 0.2 || detail.deltaX > z);
const shouldComplete =
velocity >= 0 && (velocity > 0.2 || detail.deltaX > z);
const missing = shouldComplete ? 1 - stepValue : stepValue;
const missingDistance = missing * width;
@ -726,16 +952,12 @@ export class Nav implements NavOutlet {
}
private canSwipeBack(): boolean {
return (
!!this.swipeBackEnabled &&
!this.isTransitioning &&
this.canGoBack()
);
return !!this.swipeBackEnabled && !this.isTransitioning && this.canGoBack();
}
render() {
return [
this.swipeBackEnabled &&
this.swipeBackEnabled && (
<ion-gesture
canStart={this.canSwipeBack.bind(this)}
onStart={this.swipeBackStart.bind(this)}
@ -745,9 +967,11 @@ export class Nav implements NavOutlet {
gesturePriority={10}
direction="x"
threshold={10}
attachTo="body"/>,
this.mode === 'ios' && <div class="nav-decor"/>,
<slot></slot>
attachTo="body"
/>
),
this.mode === 'ios' && <div class="nav-decor" />,
<slot />
];
}
}