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

@ -28,7 +28,7 @@ export class ModalController implements OverlayController {
removeLastOverlay(this.modals); removeLastOverlay(this.modals);
} }
/* /**
* Create a modal overlay with modal options. * Create a modal overlay with modal options.
*/ */
@Method() @Method()
@ -36,7 +36,7 @@ export class ModalController implements OverlayController {
return createOverlay(this.doc.createElement('ion-modal'), opts); return createOverlay(this.doc.createElement('ion-modal'), opts);
} }
/* /**
* Dismiss the open modal overlay. * Dismiss the open modal overlay.
*/ */
@Method() @Method()
@ -44,7 +44,7 @@ export class ModalController implements OverlayController {
return dismissOverlay(data, role, this.modals, modalId); return dismissOverlay(data, role, this.modals, modalId);
} }
/* /**
* Get the most recently opened modal overlay. * Get the most recently opened modal overlay.
*/ */
@Method() @Method()

View File

@ -3,41 +3,6 @@
Modal controllers programmatically control the modal component. Modals can be created and dismissed from the modal controller. View the [Modal](../../modal/Modal) documentation for a full list of options to pass upon creation. Modal controllers programmatically control the modal component. Modals can be created and dismissed from the modal controller. View the [Modal](../../modal/Modal) documentation for a full list of options to pass upon creation.
```javascript
async function presentModal() {
// initialize controller
const modalController = document.querySelector('ion-modal-controller');
await modalController.componentOnReady();
// create component to open
const element = document.createElement('div');
element.innerHTML = `
<ion-header>
<ion-toolbar>
<ion-title>Super Modal</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<h1>Content of doom</h1>
<div>Here's some more content</div>
<ion-button class="dismiss">Dismiss Modal</ion-button>
</ion-content>
`;
// listen for close event
const button = element.querySelector('ion-button');
button.addEventListener('click', () => {
modalController.dismiss();
});
// present the modal
const modalElement = await modalController.create({
component: element
});
modalElement.present();
}
```
<!-- Auto Generated Below --> <!-- Auto Generated Below -->

View File

@ -7,16 +7,16 @@ async function presentModal() {
// create component to open // create component to open
const element = document.createElement('div'); const element = document.createElement('div');
element.innerHTML = ` element.innerHTML = `
<ion-header> &lt;ion-header&gt;
<ion-toolbar> &lt;ion-toolbar&gt;
<ion-title>Super Modal</ion-title> &lt;ion-title&gt;Super Modal&lt;/ion-title&gt;
</ion-toolbar> &lt;/ion-toolbar&gt;
</ion-header> &lt;/ion-header&gt;
<ion-content> &lt;ion-content&gt;
<h1>Content of doom</h1> &lt;h1&gt;Content of doom&lt;/h1&gt;
<div>Here's some more content</div> &lt;div&gt;Here's some more content&lt;/div&gt;
<ion-button class="dismiss">Dismiss Modal</ion-button> &lt;ion-button class="dismiss"&gt;Dismiss Modal&lt;/ion-button&gt;
</ion-content> &lt;/ion-content&gt;
`; `;
// listen for close event // listen for close event

View File

@ -1,5 +1,6 @@
# ion-nav-pop # ion-nav-pop
`NavPop` is a component used the automatically go back in navigation. It is the element from of `NavController.pop()`
<!-- Auto Generated Below --> <!-- Auto Generated Below -->

View File

@ -2,13 +2,19 @@ import { Component, Element, Listen, Prop } from '@stencil/core';
import { ComponentProps, NavComponent } from '../../interface'; import { ComponentProps, NavComponent } from '../../interface';
@Component({ @Component({
tag: 'ion-nav-push', tag: 'ion-nav-push'
}) })
export class NavPush { export class NavPush {
@Element() el!: HTMLElement; @Element() el!: HTMLElement;
/**
* Component to navigate to
*/
@Prop() component?: NavComponent; @Prop() component?: NavComponent;
/**
* Data you want to pass to the component as props
*/
@Prop() componentProps?: ComponentProps; @Prop() componentProps?: ComponentProps;
@Listen('child:click') @Listen('child:click')
@ -20,5 +26,4 @@ export class NavPush {
} }
return Promise.resolve(null); return Promise.resolve(null);
} }
} }

View File

@ -1,5 +1,7 @@
# ion-nav-push # ion-nav-push
`NavPush` is a component used to navigate to the specified component.
It is the element from of `NavController.push()`
<!-- Auto Generated Below --> <!-- Auto Generated Below -->

View File

@ -2,12 +2,20 @@ import { Component, Element, Listen, Prop } from '@stencil/core';
import { ComponentProps, NavComponent } from '../../interface'; import { ComponentProps, NavComponent } from '../../interface';
@Component({ @Component({
tag: 'ion-nav-set-root', tag: 'ion-nav-set-root'
}) })
export class NavSetRoot { export class NavSetRoot {
@Element() el!: HTMLElement; @Element() el!: HTMLElement;
/**
* Component you want to make root for the navigation stack
*
*/
@Prop() component?: NavComponent; @Prop() component?: NavComponent;
/**
* Data you want to pass to the component as props
*/
@Prop() componentProps?: ComponentProps; @Prop() componentProps?: ComponentProps;
@Listen('child:click') @Listen('child:click')
@ -19,5 +27,4 @@ export class NavSetRoot {
} }
return Promise.resolve(null); return Promise.resolve(null);
} }
} }

View File

@ -1,5 +1,7 @@
# ion-nav-set-root # ion-nav-set-root
`NavSetRoot` is an element that allows you to set the root of the current navigation stack.
It is the element form a calling `NavController.setRoot()`
<!-- Auto Generated Below --> <!-- Auto Generated Below -->

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

View File

@ -1,7 +1,6 @@
import { Component, Prop } from '@stencil/core'; import { Component, Prop } from '@stencil/core';
import { Color, Mode } from '../../interface'; import { Color, Mode } from '../../interface';
@Component({ @Component({
tag: 'ion-note', tag: 'ion-note',
styleUrls: { styleUrls: {
@ -13,19 +12,15 @@ import { Color, Mode } from '../../interface';
} }
}) })
export class Note { export class Note {
/** /**
* The color to use from your Sass `$colors` map. * The color to use from your Sass `$colors` map.
* Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. * Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`.
* For more information, see [Theming your App](/docs/theming/theming-your-app).
*/ */
@Prop() color?: Color; @Prop() color?: Color;
/** /**
* The mode determines which platform styles to use. * The mode determines which platform styles to use.
* Possible values are: `"ios"` or `"md"`. * Possible values are: `"ios"` or `"md"`.
* For more information, see [Platform Styles](/docs/theming/platform-specific-styles).
*/ */
@Prop() mode!: Mode; @Prop() mode!: Mode;
} }

View File

@ -2,31 +2,6 @@
Notes are text elements generally used as subtitles that provide more information. Notes are styled to appear grey by default. Notes are text elements generally used as subtitles that provide more information. Notes are styled to appear grey by default.
```html
<!-- Default Note -->
<ion-note>Default Note</ion-note>
<!-- Note Colors -->
<ion-note color="primary">Primary Note</ion-note>
<ion-note color="secondary">Secondary Note</ion-note>
<ion-note color="danger">Danger Note</ion-note>
<ion-note color="light">Light Note</ion-note>
<ion-note color="dark">Dark Note</ion-note>
<!-- Notes in a List -->
<ion-list>
<ion-item>
<ion-label>Note (End)</ion-label>
<ion-note slot="end">On</ion-note>
</ion-item>
<ion-item>
<ion-note slot="start">Off</ion-note>
<ion-label>Note (Start)</ion-label>
</ion-item>
</ion-list>
```
<!-- Auto Generated Below --> <!-- Auto Generated Below -->

View File

@ -0,0 +1,24 @@
```html
<!-- Default Note -->
<ion-note>Default Note</ion-note>
<!-- Note Colors -->
<ion-note color="primary">Primary Note</ion-note>
<ion-note color="secondary">Secondary Note</ion-note>
<ion-note color="danger">Danger Note</ion-note>
<ion-note color="light">Light Note</ion-note>
<ion-note color="dark">Dark Note</ion-note>
<!-- Notes in a List -->
<ion-list>
<ion-item>
<ion-label>Note (End)</ion-label>
<ion-note slot="end">On</ion-note>
</ion-item>
<ion-item>
<ion-note slot="start">Off</ion-note>
<ion-label>Note (Start)</ion-label>
</ion-item>
</ion-list>
```

View File

@ -1,7 +1,30 @@
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core'; import {
import { Animation, AnimationBuilder, Config, CssClassMap, Mode, PickerButton, PickerColumn } from '../../interface'; Component,
Element,
Event,
EventEmitter,
Listen,
Method,
Prop,
State
} from '@stencil/core';
import {
Animation,
AnimationBuilder,
Config,
CssClassMap,
Mode,
PickerButton,
PickerColumn
} from '../../interface';
import { OverlayEventDetail, OverlayInterface, dismiss, eventMethod, present } from '../../utils/overlays'; import {
OverlayEventDetail,
OverlayInterface,
dismiss,
eventMethod,
present
} from '../../utils/overlays';
import { getClassMap } from '../../utils/theme'; import { getClassMap } from '../../utils/theme';
import { iosEnterAnimation } from './animations/ios.enter'; import { iosEnterAnimation } from './animations/ios.enter';
@ -18,7 +41,6 @@ import { iosLeaveAnimation } from './animations/ios.leave';
} }
}) })
export class Picker implements OverlayInterface { export class Picker implements OverlayInterface {
private durationTimeout: any; private durationTimeout: any;
mode!: Mode; mode!: Mode;
@ -30,9 +52,18 @@ export class Picker implements OverlayInterface {
@State() private showSpinner!: boolean; @State() private showSpinner!: boolean;
@State() private spinner!: string; @State() private spinner!: string;
@Prop({ connect: 'ion-animation-controller' }) animationCtrl!: HTMLIonAnimationControllerElement; @Prop({ connect: 'ion-animation-controller' })
@Prop({ context: 'config' }) config!: Config; animationCtrl!: HTMLIonAnimationControllerElement;
@Prop({ context: 'config' })
config!: Config;
/** @hidden */
@Prop() overlayId!: number; @Prop() overlayId!: number;
/**
* If the keyboard should be able to close the picker. Defaults to true.
*/
@Prop() keyboardClose = true; @Prop() keyboardClose = true;
/** /**
@ -89,32 +120,35 @@ export class Picker implements OverlayInterface {
/** /**
* Emitted after the picker has presented. * Emitted after the picker has presented.
*/ */
@Event({eventName: 'ionPickerDidPresent'}) didPresent!: EventEmitter<void>; @Event({ eventName: 'ionPickerDidPresent' })
didPresent!: EventEmitter<void>;
/** /**
* Emitted before the picker has presented. * Emitted before the picker has presented.
*/ */
@Event({eventName: 'ionPickerWillPresent'}) willPresent!: EventEmitter<void>; @Event({ eventName: 'ionPickerWillPresent' })
willPresent!: EventEmitter<void>;
/** /**
* Emitted before the picker has dismissed. * Emitted before the picker has dismissed.
*/ */
@Event({eventName: 'ionPickerWillDismiss'}) willDismiss!: EventEmitter<OverlayEventDetail>; @Event({ eventName: 'ionPickerWillDismiss' })
willDismiss!: EventEmitter<OverlayEventDetail>;
/** /**
* Emitted after the picker has dismissed. * Emitted after the picker has dismissed.
*/ */
@Event({eventName: 'ionPickerDidDismiss'}) didDismiss!: EventEmitter<OverlayEventDetail>; @Event({ eventName: 'ionPickerDidDismiss' })
didDismiss!: EventEmitter<OverlayEventDetail>;
/** /**
* Emitted after the picker has unloaded. * Emitted after the picker has unloaded.
*/ */
@Event() ionPickerDidUnload!: EventEmitter<void>; @Event() ionPickerDidUnload!: EventEmitter<void>;
componentWillLoad() { componentWillLoad() {
if (!this.spinner) { if (!this.spinner) {
const defaultSpinner = (this.mode === 'ios') ? 'lines' : 'crescent'; const defaultSpinner = this.mode === 'ios' ? 'lines' : 'crescent';
this.spinner = this.config.get('pickerSpinner', defaultSpinner); this.spinner = this.config.get('pickerSpinner', defaultSpinner);
} }
if (this.showSpinner === undefined) { if (this.showSpinner === undefined) {
@ -145,7 +179,13 @@ export class Picker implements OverlayInterface {
*/ */
@Method() @Method()
async present(): Promise<void> { async present(): Promise<void> {
await present(this, 'pickerEnter', iosEnterAnimation, iosEnterAnimation, undefined); await present(
this,
'pickerEnter',
iosEnterAnimation,
iosEnterAnimation,
undefined
);
if (this.duration) { if (this.duration) {
this.durationTimeout = setTimeout(() => this.dismiss(), this.duration); this.durationTimeout = setTimeout(() => this.dismiss(), this.duration);
@ -160,7 +200,14 @@ export class Picker implements OverlayInterface {
if (this.durationTimeout) { if (this.durationTimeout) {
clearTimeout(this.durationTimeout); clearTimeout(this.durationTimeout);
} }
return dismiss(this, data, role, 'pickerLeave', iosLeaveAnimation, iosLeaveAnimation); return dismiss(
this,
data,
role,
'pickerLeave',
iosLeaveAnimation,
iosLeaveAnimation
);
} }
/** /**
@ -169,7 +216,9 @@ export class Picker implements OverlayInterface {
* *
*/ */
@Method() @Method()
onDidDismiss(callback?: (detail: OverlayEventDetail) => void): Promise<OverlayEventDetail> { onDidDismiss(
callback?: (detail: OverlayEventDetail) => void
): Promise<OverlayEventDetail> {
return eventMethod(this.el, 'ionPickerDidDismiss', callback); return eventMethod(this.el, 'ionPickerDidDismiss', callback);
} }
@ -179,25 +228,39 @@ export class Picker implements OverlayInterface {
* *
*/ */
@Method() @Method()
onWillDismiss(callback?: (detail: OverlayEventDetail) => void): Promise<OverlayEventDetail> { onWillDismiss(
callback?: (detail: OverlayEventDetail) => void
): Promise<OverlayEventDetail> {
return eventMethod(this.el, 'ionPickerWillDismiss', callback); return eventMethod(this.el, 'ionPickerWillDismiss', callback);
} }
/**
* Add a new PickerButton to the picker
*/
@Method() @Method()
addButton(button: PickerButton) { addButton(button: PickerButton) {
this.buttons.push(button); this.buttons.push(button);
} }
/**
* Add a new PickerColumn to the picker
*/
@Method() @Method()
addColumn(column: PickerColumn) { addColumn(column: PickerColumn) {
this.columns.push(column); this.columns.push(column);
} }
/**
* Returns the column the matches the specified name
*/
@Method() @Method()
getColumn(name: string): PickerColumn|undefined { getColumn(name: string): PickerColumn | undefined {
return this.columns.find(column => column.name === name); return this.columns.find(column => column.name === name);
} }
/**
* Returns all the PickerColumns
*/
@Method() @Method()
getColumns(): PickerColumn[] { getColumns(): PickerColumn[] {
return this.columns; return this.columns;
@ -226,13 +289,15 @@ export class Picker implements OverlayInterface {
} }
private getSelected() { private getSelected() {
const selected: {[k: string]: any} = {}; const selected: { [k: string]: any } = {};
this.columns.forEach((col, index) => { this.columns.forEach((col, index) => {
const selectedColumn = col.selectedIndex ? col.options[col.selectedIndex] : null; const selectedColumn = col.selectedIndex
? col.options[col.selectedIndex]
: null;
selected[col.name] = { selected[col.name] = {
text: selectedColumn ? selectedColumn.text : null, text: selectedColumn ? selectedColumn.text : null,
value: selectedColumn ? selectedColumn.value : null, value: selectedColumn ? selectedColumn.value : null,
columnIndex: index, columnIndex: index
}; };
}); });
return selected; return selected;
@ -241,7 +306,7 @@ export class Picker implements OverlayInterface {
hostData() { hostData() {
return { return {
style: { style: {
zIndex: 20000 + this.overlayId, zIndex: 20000 + this.overlayId
} }
}; };
} }
@ -249,16 +314,17 @@ export class Picker implements OverlayInterface {
render() { render() {
// TODO: cssClass // TODO: cssClass
const buttons = this.buttons.map(b => { const buttons = this.buttons
if (typeof b === 'string') { .map(b => {
b = { text: b }; if (typeof b === 'string') {
} b = { text: b };
if (!b.cssClass) { }
b.cssClass = ''; if (!b.cssClass) {
} b.cssClass = '';
return b; }
}) return b;
.filter(b => b !== null); })
.filter(b => b !== null);
const columns = this.columns; const columns = this.columns;
@ -292,23 +358,27 @@ export class Picker implements OverlayInterface {
// }); // });
return [ return [
<ion-backdrop visible={this.showBackdrop} tappable={this.enableBackdropDismiss}/>, <ion-backdrop
visible={this.showBackdrop}
tappable={this.enableBackdropDismiss}
/>,
<div class="picker-wrapper" role="dialog"> <div class="picker-wrapper" role="dialog">
<div class="picker-toolbar"> <div class="picker-toolbar">
{buttons.map(b => {buttons.map(b => (
<div class={buttonWrapperClass(b)}> <div class={buttonWrapperClass(b)}>
<button onClick={() => this.buttonClick(b)} class={buttonClass(b)}> <button
onClick={() => this.buttonClick(b)}
class={buttonClass(b)}
>
{b.text} {b.text}
</button> </button>
</div> </div>
)} ))}
</div> </div>
<div class="picker-columns"> <div class="picker-columns">
<div class="picker-above-highlight"></div> <div class="picker-above-highlight" />
{columns.map(c => {columns.map(c => <ion-picker-column col={c} />)}
<ion-picker-column col={c}></ion-picker-column> <div class="picker-below-highlight" />
)}
<div class="picker-below-highlight"></div>
</div> </div>
</div> </div>
]; ];
@ -317,7 +387,7 @@ export class Picker implements OverlayInterface {
function buttonWrapperClass(button: PickerButton): CssClassMap { function buttonWrapperClass(button: PickerButton): CssClassMap {
const buttonClass: CssClassMap = { const buttonClass: CssClassMap = {
'picker-toolbar-button': true, 'picker-toolbar-button': true
}; };
if (button.role) { if (button.role) {
buttonClass[`picker-toolbar-${button.role}`] = true; buttonClass[`picker-toolbar-${button.role}`] = true;

View File

@ -6,44 +6,6 @@ button that belongs to a radio group unchecks any previous checked
radio button within the same group. radio button within the same group.
```html
<ion-list>
<ion-radio-group>
<ion-list-header>
Auto Manufacturers
</ion-list-header>
<ion-item>
<ion-label>Cord</ion-label>
<ion-radio value="cord"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Duesenberg</ion-label>
<ion-radio value="duesenberg"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Hudson</ion-label>
<ion-radio value="hudson"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Packard</ion-label>
<ion-radio value="packard"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Studebaker</ion-label>
<ion-radio value="studebaker"></ion-radio>
</ion-item>
</ion-radio-group>
</ion-list>
```
<!-- Auto Generated Below --> <!-- Auto Generated Below -->

View File

@ -0,0 +1,38 @@
```html
<ion-list>
<ion-radio-group>
<ion-list-header>
Auto Manufacturers
</ion-list-header>
<ion-item>
<ion-label>Cord</ion-label>
<ion-radio value="cord"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Duesenberg</ion-label>
<ion-radio value="duesenberg"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Hudson</ion-label>
<ion-radio value="hudson"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Packard</ion-label>
<ion-radio value="packard"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Studebaker</ion-label>
<ion-radio value="studebaker"></ion-radio>
</ion-item>
</ion-radio-group>
</ion-list>
```

View File

@ -24,14 +24,12 @@ export class Radio implements RadioButtonInput {
/** /**
* The color to use from your Sass `$colors` map. * The color to use from your Sass `$colors` map.
* Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. * Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`.
* For more information, see [Theming your App](/docs/theming/theming-your-app).
*/ */
@Prop() color?: Color; @Prop() color?: Color;
/** /**
* The mode determines which platform styles to use. * The mode determines which platform styles to use.
* Possible values are: `"ios"` or `"md"`. * Possible values are: `"ios"` or `"md"`.
* For more information, see [Platform Styles](/docs/theming/platform-specific-styles).
*/ */
@Prop() mode!: Mode; @Prop() mode!: Mode;

View File

@ -5,30 +5,6 @@ Radios are generally used as a set of related options inside of a group, but the
An `ion-radio-group` can be used to group a set of radios. When radios are inside of a [radio group](../radio-group), only one radio in the group will be checked at any time. Pressing a radio will check it and uncheck the previously selected radio, if there is one. If a radio is not in a group with another radio, then both radios will have the ability to be checked at the same time. An `ion-radio-group` can be used to group a set of radios. When radios are inside of a [radio group](../radio-group), only one radio in the group will be checked at any time. Pressing a radio will check it and uncheck the previously selected radio, if there is one. If a radio is not in a group with another radio, then both radios will have the ability to be checked at the same time.
```html
<ion-list>
<ion-radio-group>
<ion-list-header>
<ion-label>Name</ion-label>
</ion-list-header>
<ion-item>
<ion-label>Biff</ion-label>
<ion-radio slot="start" value="biff" checked></ion-radio>
</ion-item>
<ion-item>
<ion-label>Griff</ion-label>
<ion-radio slot="start" value="griff"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Buford</ion-label>
<ion-radio slot="start" value="buford"></ion-radio>
</ion-item>
</ion-radio-group>
</ion-list>
```
<!-- Auto Generated Below --> <!-- Auto Generated Below -->

View File

@ -0,0 +1,24 @@
```html
<ion-list>
<ion-radio-group>
<ion-list-header>
<ion-label>Name</ion-label>
</ion-list-header>
<ion-item>
<ion-label>Biff</ion-label>
<ion-radio slot="start" value="biff" checked></ion-radio>
</ion-item>
<ion-item>
<ion-label>Griff</ion-label>
<ion-radio slot="start" value="griff"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Buford</ion-label>
<ion-radio slot="start" value="buford"></ion-radio>
</ion-item>
</ion-radio-group>
</ion-list>
```

View File

@ -1,5 +1,6 @@
# ion-range-knob # ion-range-knob
RangeKnob is an internal component of Range.
<!-- Auto Generated Below --> <!-- Auto Generated Below -->

View File

@ -1,5 +1,21 @@
import { Component, Element, Event, EventEmitter, Listen, Prop, State, Watch } from '@stencil/core'; import {
import { BaseInput, Color, GestureDetail, Mode, RangeInputChangeEvent, StyleEvent } from '../../interface'; Component,
Element,
Event,
EventEmitter,
Listen,
Prop,
State,
Watch
} from '@stencil/core';
import {
BaseInput,
Color,
GestureDetail,
Mode,
RangeInputChangeEvent,
StyleEvent
} from '../../interface';
import { clamp, debounceEvent, deferEvent } from '../../utils/helpers'; import { clamp, debounceEvent, deferEvent } from '../../utils/helpers';
import { Knob, RangeEventDetail, RangeValue } from './range-interface'; import { Knob, RangeEventDetail, RangeValue } from './range-interface';
@ -14,7 +30,6 @@ import { Knob, RangeEventDetail, RangeValue } from './range-interface';
} }
}) })
export class Range implements BaseInput { export class Range implements BaseInput {
private noUpdate = false; private noUpdate = false;
private rect!: ClientRect; private rect!: ClientRect;
private hasFocus = false; private hasFocus = false;
@ -28,14 +43,12 @@ export class Range implements BaseInput {
/** /**
* The color to use from your Sass `$colors` map. * The color to use from your Sass `$colors` map.
* Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. * Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`.
* For more information, see [Theming your App](/docs/theming/theming-your-app).
*/ */
@Prop() color?: Color; @Prop() color?: Color;
/** /**
* The mode determines which platform styles to use. * The mode determines which platform styles to use.
* Possible values are: `"ios"` or `"md"`. * Possible values are: `"ios"` or `"md"`.
* For more information, see [Platform Styles](/docs/theming/platform-specific-styles).
*/ */
@Prop() mode!: Mode; @Prop() mode!: Mode;
@ -99,16 +112,16 @@ export class Range implements BaseInput {
/** /**
* the value of the range. * the value of the range.
*/ */
@Prop({ mutable: true }) value: any = 0; @Prop({ mutable: true })
value: any = 0;
@Watch('value') @Watch('value')
protected valueChanged(value: RangeValue) { protected valueChanged(value: RangeValue) {
if (!this.noUpdate) { if (!this.noUpdate) {
this.updateRatio(); this.updateRatio();
} }
this.ionChange.emit({value}); this.ionChange.emit({ value });
} }
/** /**
* Emitted when the value property has changed. * Emitted when the value property has changed.
*/ */
@ -129,7 +142,6 @@ export class Range implements BaseInput {
*/ */
@Event() ionBlur!: EventEmitter<void>; @Event() ionBlur!: EventEmitter<void>;
componentWillLoad() { componentWillLoad() {
this.ionStyle = deferEvent(this.ionStyle); this.ionStyle = deferEvent(this.ionStyle);
@ -162,7 +174,7 @@ export class Range implements BaseInput {
} }
return { return {
lower: 0, lower: 0,
upper: value, upper: value
}; };
} else { } else {
if (typeof value === 'object') { if (typeof value === 'object') {
@ -198,14 +210,16 @@ export class Range implements BaseInput {
this.fireFocus(); this.fireFocus();
const el = this.el.querySelector('.range-slider')!; const el = this.el.querySelector('.range-slider')!;
const rect = this.rect = el.getBoundingClientRect() as any; const rect = (this.rect = el.getBoundingClientRect() as any);
const currentX = detail.currentX; const currentX = detail.currentX;
// figure out which knob they started closer to // figure out which knob they started closer to
const ratio = clamp(0, (currentX - rect.left) / rect.width, 1); const ratio = clamp(0, (currentX - rect.left) / rect.width, 1);
this.pressedKnob = (!this.dualKnobs || (Math.abs(this.ratioA - ratio) < Math.abs(this.ratioB - ratio))) this.pressedKnob =
? Knob.A !this.dualKnobs ||
: Knob.B; Math.abs(this.ratioA - ratio) < Math.abs(this.ratioB - ratio)
? Knob.A
: Knob.B;
// update the active knob's position // update the active knob's position
this.update(currentX); this.update(currentX);
@ -267,7 +281,7 @@ export class Range implements BaseInput {
private updateRatio() { private updateRatio() {
const value = this.getValue() as any; const value = this.getValue() as any;
const {min, max} = this; const { min, max } = this;
if (this.dualKnobs) { if (this.dualKnobs) {
this.ratioA = valueToRatio(value.lower, min, max); this.ratioA = valueToRatio(value.lower, min, max);
this.ratioB = valueToRatio(value.upper, min, max); this.ratioB = valueToRatio(value.upper, min, max);
@ -279,13 +293,13 @@ export class Range implements BaseInput {
private updateValue() { private updateValue() {
this.noUpdate = true; this.noUpdate = true;
const {valA, valB} = this; const { valA, valB } = this;
this.value = (!this.dualKnobs) this.value = !this.dualKnobs
? valA ? valA
: { : {
lower: Math.min(valA, valB), lower: Math.min(valA, valB),
upper: Math.max(valA, valB) upper: Math.max(valA, valB)
}; };
this.noUpdate = false; this.noUpdate = false;
} }
@ -301,7 +315,7 @@ export class Range implements BaseInput {
} }
render() { render() {
const {min, max, step, ratioLower, ratioUpper} = this; const { min, max, step, ratioLower, ratioUpper } = this;
const barL = `${ratioLower * 100}%`; const barL = `${ratioLower * 100}%`;
const barR = `${100 - ratioUpper * 100}%`; const barR = `${100 - ratioUpper * 100}%`;
@ -319,7 +333,7 @@ export class Range implements BaseInput {
} }
return [ return [
<slot name="start"></slot>, <slot name="start" />,
<ion-gesture <ion-gesture
disableScroll={true} disableScroll={true}
onStart={this.onDragStart.bind(this)} onStart={this.onDragStart.bind(this)}
@ -329,18 +343,19 @@ export class Range implements BaseInput {
gestureName="range" gestureName="range"
gesturePriority={30} gesturePriority={30}
direction="x" direction="x"
threshold={0}> threshold={0}
>
<div class="range-slider"> <div class="range-slider">
{ticks.map(t => {ticks.map(t => (
<div <div
style={{ left: t.left }} style={{ left: t.left }}
role="presentation" role="presentation"
class={{ class={{
'range-tick': true, 'range-tick': true,
'range-tick-active': t.active 'range-tick-active': t.active
}}/> }}
)} />
))}
<div class="range-bar" role="presentation" /> <div class="range-bar" role="presentation" />
<div <div
@ -358,9 +373,10 @@ export class Range implements BaseInput {
ratio={this.ratioA} ratio={this.ratioA}
pin={this.pin} pin={this.pin}
min={min} min={min}
max={max}/> max={max}
/>
{ this.dualKnobs && {this.dualKnobs && (
<ion-range-knob <ion-range-knob
knob={Knob.B} knob={Knob.B}
pressed={this.pressedKnob === Knob.B} pressed={this.pressedKnob === Knob.B}
@ -368,17 +384,23 @@ export class Range implements BaseInput {
ratio={this.ratioB} ratio={this.ratioB}
pin={this.pin} pin={this.pin}
min={min} min={min}
max={max} /> } max={max}
/>
)}
</div> </div>
</ion-gesture>, </ion-gesture>,
<slot name="end"></slot> <slot name="end" />
]; ];
} }
} }
export function ratioToValue(
export function ratioToValue(ratio: number, min: number, max: number, step: number): number { ratio: number,
let value = ((max - min) * ratio); min: number,
max: number,
step: number
): number {
let value = (max - min) * ratio;
if (step > 0) { if (step > 0) {
value = Math.round(value / step) * step + min; value = Math.round(value / step) * step + min;
} }

View File

@ -9,39 +9,8 @@ controls the value of the range.
Labels can be placed on either side of the range by adding the Labels can be placed on either side of the range by adding the
`slot="start"` or `slot="end"` to the element. The element doesn't have to `slot="start"` or `slot="end"` to the element. The element doesn't have to
be an `ion-label`, it can be added to any element to place it to the be an `ion-label`, it can be added to any element to place it to the
left or right of the range. See [usage](#usage) below for examples. left or right of the range.
### Usage
```html
<ion-list>
<ion-item>
<ion-range color="danger" pin="true"></ion-range>
</ion-item>
<ion-item>
<ion-range min="-200" max="200" color="secondary">
<ion-label slot="start">-200</ion-label>
<ion-label slot="end">200</ion-label>
</ion-range>
</ion-item>
<ion-item>
<ion-range min="20" max="80" step="2" >
<ion-icon small slot="start" name="sunny"></ion-icon>
<ion-icon slot="end" name="sunny"></ion-icon>
</ion-range>
</ion-item>
<ion-item>
<ion-range min="1000" max="2000" step="100" snaps="true" color="secondary" ></ion-range>
</ion-item>
<ion-item>
<ion-range dualKnobs="true" min="21" max="72" step="3" snaps="true"></ion-range>
</ion-item>
</ion-list>
```
<!-- Auto Generated Below --> <!-- Auto Generated Below -->

View File

@ -0,0 +1,31 @@
```html
<ion-list>
<ion-item>
<ion-range color="danger" pin="true"></ion-range>
</ion-item>
<ion-item>
<ion-range min="-200" max="200" color="secondary">
<ion-label slot="start">-200</ion-label>
<ion-label slot="end">200</ion-label>
</ion-range>
</ion-item>
<ion-item>
<ion-range min="20" max="80" step="2" >
<ion-icon small slot="start" name="sunny"></ion-icon>
<ion-icon slot="end" name="sunny"></ion-icon>
</ion-range>
</ion-item>
<ion-item>
<ion-range min="1000" max="2000" step="100" snaps="true" color="secondary" ></ion-range>
</ion-item>
<ion-item>
<ion-range dualKnobs="true" min="21" max="72" step="3" snaps="true"></ion-range>
</ion-item>
</ion-list>
```

View File

@ -0,0 +1,29 @@
```html
<ion-list>
<ion-item>
<ion-range color="danger" pin="true"></ion-range>
</ion-item>
<ion-item>
<ion-range min="-200" max="200" color="secondary">
<ion-label slot="start">-200</ion-label>
<ion-label slot="end">200</ion-label>
</ion-range>
</ion-item>
<ion-item>
<ion-range min="20" max="80" step="2" >
<ion-icon small slot="start" name="sunny"></ion-icon>
<ion-icon slot="end" name="sunny"></ion-icon>
</ion-range>
</ion-item>
<ion-item>
<ion-range min="1000" max="2000" step="100" snaps="true" color="secondary" ></ion-range>
</ion-item>
<ion-item>
<ion-range dual-knobs="true" min="21" max="72" step="3" snaps="true"></ion-range>
</ion-item>
</ion-list>
```

View File

@ -2,18 +2,6 @@
The refresher content contains the text, icon and spinner to display during a pull-to-refresh. Ionic provides the pulling icon and refreshing spinner based on the platform. However, the default icon, spinner, and text can be customized based on the state of the refresher. The refresher content contains the text, icon and spinner to display during a pull-to-refresh. Ionic provides the pulling icon and refreshing spinner based on the platform. However, the default icon, spinner, and text can be customized based on the state of the refresher.
```html
<ion-content>
<ion-refresher slot="fixed">
<ion-refresher-content
pulling-icon="arrow-dropdown"
pulling-text="Pull to refresh"
refreshing-spinner="circles"
refreshing-text="Refreshing...">
</ion-refresher-content>
</ion-refresher>
</ion-content>
```
<!-- Auto Generated Below --> <!-- Auto Generated Below -->

View File

@ -10,12 +10,6 @@ refresher.
```html ```html
<ion-content>
<ion-refresher slot="fixed">
<ion-refresher-content>
</ion-refresher-content>
</ion-refresher>
</ion-content>
``` ```

View File

@ -0,0 +1,23 @@
```html
<ion-content>
<ion-refresher (ionRefresh)="doRefresh($event)">
<ion-refresher-content></ion-refresher-content>
</ion-refresher>
</ion-content>
```
```typescript
@Component({...})
export class NewsFeedPage {
doRefresh(event) {
console.log('Begin async operation', refresher);
setTimeout(() => {
console.log('Async operation has ended');
event.target.complete();
}, 2000);
}
}
```

View File

@ -0,0 +1,21 @@
```html
<ion-content>
<ion-refresher slot="fixed">
<ion-refresher-content>
</ion-refresher-content>
</ion-refresher>
</ion-content>
<!-- or for custom content -->
<ion-content>
<ion-refresher slot="fixed">
<ion-refresher-content
pulling-icon="arrow-dropdown"
pulling-text="Pull to refresh"
refreshing-spinner="circles"
refreshing-text="Refreshing...">
</ion-refresher-content>
</ion-refresher>
</ion-content>
```

View File

@ -1,8 +1,7 @@
# ion-reorder-group # ion-reorder-group
Item reorder adds the ability to change an item's order in a group. ReorderGroup is a wrapper component for items with the Reorder element.
It can be used within an `ion-list` or `ion-item-group` to provide a
visual drag and drop interface.
## Grouping Items ## Grouping Items

View File

@ -1,5 +1,6 @@
# ion-reorder # ion-reorder
Reorder adds the ability to change an item's order in a group. It can be used within an ion-list or ion-reorder-group to provide a visual drag and drop interface.
<!-- Auto Generated Below --> <!-- Auto Generated Below -->