mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-20 20:33:32 +08:00
feat(toggle): boolean input directive
This commit is contained in:
124
src/bindings/angular/components/boolean-input.ts
Normal file
124
src/bindings/angular/components/boolean-input.ts
Normal file
@ -0,0 +1,124 @@
|
||||
import { Directive, ElementRef, EventEmitter, HostListener, Input, Output } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'ion-toggle',
|
||||
providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: BooleanInput, multi: true }],
|
||||
})
|
||||
export class BooleanInput {
|
||||
id: string;
|
||||
_labelId: string;
|
||||
_chg: Function;
|
||||
_tch: Function;
|
||||
_dis: Function;
|
||||
|
||||
@Output() ionChange: EventEmitter<any> = new EventEmitter<any>();
|
||||
@Output() ionFocus: EventEmitter<any> = new EventEmitter<any>();
|
||||
@Output() ionBlur: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
|
||||
constructor(public _el: ElementRef) {}
|
||||
|
||||
/**
|
||||
* @input {boolean} If true, the element is selected.
|
||||
*/
|
||||
@Input()
|
||||
get checked(): boolean {
|
||||
return this._el.nativeElement.checked;
|
||||
}
|
||||
set checked(val: boolean) {
|
||||
this._el.nativeElement.checked = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @input {boolean} If true, the user cannot interact with this element.
|
||||
*/
|
||||
@Input()
|
||||
get disabled(): boolean {
|
||||
return this._el.nativeElement.disabled;
|
||||
}
|
||||
set disabled(val: boolean) {
|
||||
this._el.nativeElement.disabled = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @input {string}
|
||||
*/
|
||||
@Input()
|
||||
get value(): string {
|
||||
return this._el.nativeElement.value;
|
||||
}
|
||||
set value(val: string) {
|
||||
this._el.nativeElement.value = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@HostListener('$ionChange', ['$event'])
|
||||
_onChange(ev: any) {
|
||||
ev.stopPropagation();
|
||||
this._chg && this._chg(ev.detail.checked);
|
||||
this._tch && this._tch();
|
||||
this.ionChange.emit(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@HostListener('$ionFocus', ['$event'])
|
||||
_onFocus(ev: any) {
|
||||
ev.stopPropagation();
|
||||
this.ionFocus.emit(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@HostListener('$ionBlur', ['$event'])
|
||||
_onBlur(ev: any) {
|
||||
ev.stopPropagation();
|
||||
this.ionBlur.emit(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
writeValue(val: any) {
|
||||
if (this.checked !== val) {
|
||||
this.checked = val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
registerOnChange(fn: any) {
|
||||
this._chg = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
registerOnTouched(fn: any) {
|
||||
this._tch = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
registerOnDisabledChange(fn: any) {
|
||||
this._dis = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
setDisabledState(isDisabled: boolean) {
|
||||
this.disabled = isDisabled;
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { Config } from '../config/config';
|
||||
import { DomController } from '../platform/dom-controller';
|
||||
import { Config } from '../../../config/config';
|
||||
import { DomController } from '../../../platform/dom-controller';
|
||||
import { NgZone } from '@angular/core';
|
||||
import { Platform } from '../platform/platform';
|
||||
import { Platform } from '../../../platform/platform';
|
||||
|
||||
|
||||
/**
|
||||
@ -27,6 +27,9 @@ export function setupCore(config: Config, plt: Platform, domCtrl: DomController,
|
||||
// keep core and angular dom reads/writes *nsync
|
||||
ionic['domCtrl'] = domCtrl;
|
||||
|
||||
// keep core and angular dom reads/writes *nsync
|
||||
ionic['eventNamePrefix'] = '$';
|
||||
|
||||
// next tick controller created here so that it can
|
||||
// be created to run outside of angular
|
||||
ionic['nextTickCtrl'] = getNextTickController(zone, plt.userAgent().toLowerCase());
|
@ -15,7 +15,7 @@ ion-badge,
|
||||
:host {
|
||||
display: inline-block;
|
||||
visibility: inherit !important;
|
||||
contain: layout content;
|
||||
contain: content;
|
||||
}
|
||||
|
||||
.badge {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component } from '../../util/decorators';
|
||||
import { Component } from '../../index';
|
||||
|
||||
|
||||
@Component({
|
||||
|
3
src/components/card/card-content.ios.scss
Normal file
3
src/components/card/card-content.ios.scss
Normal file
@ -0,0 +1,3 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
@import "./card-content";
|
||||
|
3
src/components/card/card-content.md.scss
Normal file
3
src/components/card/card-content.md.scss
Normal file
@ -0,0 +1,3 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
@import "./card-content";
|
||||
|
9
src/components/card/card-content.scss
Normal file
9
src/components/card/card-content.scss
Normal file
@ -0,0 +1,9 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
|
||||
// Card
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-card-content,
|
||||
:host {
|
||||
display: block;
|
||||
}
|
@ -1,17 +1,12 @@
|
||||
import { Directive, ElementRef, Renderer } from '@angular/core';
|
||||
import { Component } from '../../index';
|
||||
|
||||
import { Config } from '../../config/config';
|
||||
import { Ion } from '../ion';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'ion-card-content'
|
||||
})
|
||||
export class CardContent extends Ion {
|
||||
|
||||
constructor(config: Config, elementRef: ElementRef, renderer: Renderer) {
|
||||
super(config, elementRef, renderer, 'card-content');
|
||||
@Component({
|
||||
tag: 'ion-card-content',
|
||||
styleUrls: {
|
||||
ios: 'card-content.ios.scss',
|
||||
md: 'card-content.md.scss',
|
||||
wp: 'card-content.wp.scss'
|
||||
}
|
||||
}
|
||||
})
|
||||
export class CardContent {}
|
||||
|
3
src/components/card/card-content.wp.scss
Normal file
3
src/components/card/card-content.wp.scss
Normal file
@ -0,0 +1,3 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
@import "./card-content";
|
||||
|
2
src/components/card/card-header.ios.scss
Normal file
2
src/components/card/card-header.ios.scss
Normal file
@ -0,0 +1,2 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
@import "./card-header";
|
2
src/components/card/card-header.md.scss
Normal file
2
src/components/card/card-header.md.scss
Normal file
@ -0,0 +1,2 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
@import "./card-header";
|
14
src/components/card/card-header.scss
Normal file
14
src/components/card/card-header.scss
Normal file
@ -0,0 +1,14 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
|
||||
// Card Header
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
ion-card-header,
|
||||
:host {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
@ -1,17 +1,12 @@
|
||||
import { Directive, ElementRef, Renderer } from '@angular/core';
|
||||
import { Component } from '../../index';
|
||||
|
||||
import { Config } from '../../config/config';
|
||||
import { Ion } from '../ion';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'ion-card-header'
|
||||
})
|
||||
export class CardHeader extends Ion {
|
||||
|
||||
constructor(config: Config, elementRef: ElementRef, renderer: Renderer) {
|
||||
super(config, elementRef, renderer, 'card-header');
|
||||
@Component({
|
||||
tag: 'ion-card-header',
|
||||
styleUrls: {
|
||||
ios: 'card-header.ios.scss',
|
||||
md: 'card-header.md.scss',
|
||||
wp: 'card-header.wp.scss'
|
||||
}
|
||||
}
|
||||
})
|
||||
export class CardHeader {}
|
||||
|
2
src/components/card/card-header.wp.scss
Normal file
2
src/components/card/card-header.wp.scss
Normal file
@ -0,0 +1,2 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
@import "./card-header";
|
@ -1,17 +1,12 @@
|
||||
import { Directive, ElementRef, Renderer } from '@angular/core';
|
||||
import { Component } from '../../index';
|
||||
|
||||
import { Config } from '../../config/config';
|
||||
import { Ion } from '../ion';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'ion-card-title'
|
||||
})
|
||||
export class CardTitle extends Ion {
|
||||
|
||||
constructor(config: Config, elementRef: ElementRef, renderer: Renderer) {
|
||||
super(config, elementRef, renderer, 'card-title');
|
||||
@Component({
|
||||
tag: 'ion-card-title',
|
||||
styleUrls: {
|
||||
ios: 'card.ios.scss',
|
||||
md: 'card.md.scss',
|
||||
wp: 'card.wp.scss'
|
||||
}
|
||||
}
|
||||
})
|
||||
export class CardTitle {}
|
||||
|
@ -4,25 +4,15 @@
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
ion-card {
|
||||
ion-card,
|
||||
:host {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
ion-card img {
|
||||
ion-card img,
|
||||
:host img {
|
||||
display: block;
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
ion-card-header {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
ion-card-content {
|
||||
display: block;
|
||||
}
|
||||
|
@ -1,18 +1,12 @@
|
||||
import { Directive, ElementRef, Renderer } from '@angular/core';
|
||||
|
||||
import { Config } from '../../config/config';
|
||||
import { Ion } from '../ion';
|
||||
import { Component } from '../../index';
|
||||
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'ion-card'
|
||||
})
|
||||
export class Card extends Ion {
|
||||
|
||||
constructor(config: Config, elementRef: ElementRef, renderer: Renderer) {
|
||||
super(config, elementRef, renderer, 'card');
|
||||
@Component({
|
||||
tag: 'ion-card',
|
||||
styleUrls: {
|
||||
ios: 'card.ios.scss',
|
||||
md: 'card.md.scss',
|
||||
wp: 'card.wp.scss'
|
||||
}
|
||||
}
|
||||
})
|
||||
export class Card {}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { IonicPageModule } from '../../../../../..';
|
||||
|
||||
import { RootPage } from './root-page';
|
||||
@ -9,6 +9,9 @@ import { RootPage } from './root-page';
|
||||
],
|
||||
imports: [
|
||||
IonicPageModule.forChild(RootPage)
|
||||
],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
]
|
||||
})
|
||||
export class RootPageModule {}
|
||||
|
173
src/components/gesture/gesture-controller.ts
Normal file
173
src/components/gesture/gesture-controller.ts
Normal file
@ -0,0 +1,173 @@
|
||||
|
||||
|
||||
export class GestureController {
|
||||
private id: number = 0;
|
||||
private requestedStart: { [eventId: number]: number } = {};
|
||||
private disabledGestures: { [eventName: string]: Set<number> } = {};
|
||||
private disabledScroll: Set<number> = new Set<number>();
|
||||
private capturedID: number = null;
|
||||
|
||||
|
||||
createGesture(gestureName: string, gesturePriority: number, disableScroll: boolean): GestureDelegate {
|
||||
return new GestureDelegate(this, ++this.id, gestureName, gesturePriority, disableScroll);
|
||||
}
|
||||
|
||||
start(gestureName: string, id: number, priority: number): boolean {
|
||||
if (!this.canStart(gestureName)) {
|
||||
delete this.requestedStart[id];
|
||||
return false;
|
||||
}
|
||||
|
||||
this.requestedStart[id] = priority;
|
||||
return true;
|
||||
}
|
||||
|
||||
capture(gestureName: string, id: number, priority: number): boolean {
|
||||
if (!this.start(gestureName, id, priority)) {
|
||||
return false;
|
||||
}
|
||||
let requestedStart = this.requestedStart;
|
||||
let maxPriority = -10000;
|
||||
for (let gestureID in requestedStart) {
|
||||
maxPriority = Math.max(maxPriority, requestedStart[gestureID]);
|
||||
}
|
||||
|
||||
if (maxPriority === priority) {
|
||||
this.capturedID = id;
|
||||
this.requestedStart = {};
|
||||
return true;
|
||||
}
|
||||
delete requestedStart[id];
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
release(id: number) {
|
||||
delete this.requestedStart[id];
|
||||
|
||||
if (this.capturedID && id === this.capturedID) {
|
||||
this.capturedID = null;
|
||||
}
|
||||
}
|
||||
|
||||
disableGesture(gestureName: string, id: number) {
|
||||
let set = this.disabledGestures[gestureName];
|
||||
if (!set) {
|
||||
set = new Set<number>();
|
||||
this.disabledGestures[gestureName] = set;
|
||||
}
|
||||
set.add(id);
|
||||
}
|
||||
|
||||
enableGesture(gestureName: string, id: number) {
|
||||
let set = this.disabledGestures[gestureName];
|
||||
if (set) {
|
||||
set.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
disableScroll(id: number) {
|
||||
// let isEnabled = !this.isScrollDisabled();
|
||||
this.disabledScroll.add(id);
|
||||
// if (this._app && isEnabled && this.isScrollDisabled()) {
|
||||
// console.debug('GestureController: Disabling scrolling');
|
||||
// this._app._setDisableScroll(true);
|
||||
// }
|
||||
}
|
||||
|
||||
enableScroll(id: number) {
|
||||
// let isDisabled = this.isScrollDisabled();
|
||||
this.disabledScroll.delete(id);
|
||||
// if (this._app && isDisabled && !this.isScrollDisabled()) {
|
||||
// console.debug('GestureController: Enabling scrolling');
|
||||
// this._app._setDisableScroll(false);
|
||||
// }
|
||||
}
|
||||
|
||||
canStart(gestureName: string): boolean {
|
||||
if (this.capturedID) {
|
||||
// a gesture already captured
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.isDisabled(gestureName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
isCaptured(): boolean {
|
||||
return !!this.capturedID;
|
||||
}
|
||||
|
||||
isScrollDisabled(): boolean {
|
||||
return this.disabledScroll.size > 0;
|
||||
}
|
||||
|
||||
isDisabled(gestureName: string): boolean {
|
||||
let disabled = this.disabledGestures[gestureName];
|
||||
if (disabled && disabled.size > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export class GestureDelegate {
|
||||
|
||||
constructor(
|
||||
private ctrl: GestureController,
|
||||
private id: number,
|
||||
private name: string,
|
||||
private priority: number,
|
||||
private disableScroll: boolean
|
||||
) { }
|
||||
|
||||
canStart(): boolean {
|
||||
if (!this.ctrl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.ctrl.canStart(this.name);
|
||||
}
|
||||
|
||||
start(): boolean {
|
||||
if (!this.ctrl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.ctrl.start(this.name, this.id, this.priority);
|
||||
}
|
||||
|
||||
capture(): boolean {
|
||||
if (!this.ctrl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let captured = this.ctrl.capture(this.name, this.id, this.priority);
|
||||
if (captured && this.disableScroll) {
|
||||
this.ctrl.disableScroll(this.id);
|
||||
}
|
||||
|
||||
return captured;
|
||||
}
|
||||
|
||||
release() {
|
||||
if (this.ctrl) {
|
||||
this.ctrl.release(this.id);
|
||||
|
||||
if (this.disableScroll) {
|
||||
this.ctrl.enableScroll(this.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.release();
|
||||
this.ctrl = null;
|
||||
}
|
||||
|
||||
}
|
365
src/components/gesture/gesture.ts
Normal file
365
src/components/gesture/gesture.ts
Normal file
@ -0,0 +1,365 @@
|
||||
import { applyStyles, getElementReference, pointerCoordX, pointerCoordY } from '../../util/dom';
|
||||
import { Component, Listen, Ionic, Prop } from '../../index';
|
||||
import { GestureCallback, GestureDetail } from '../../util/interfaces';
|
||||
import { GestureController, GestureDelegate } from './gesture-controller';
|
||||
import { PanRecognizer } from './recognizers';
|
||||
|
||||
|
||||
@Component({
|
||||
tag: 'ion-gesture',
|
||||
shadow: false
|
||||
})
|
||||
export class Gesture {
|
||||
private $el: HTMLElement;
|
||||
private detail: GestureDetail = {};
|
||||
private positions: number[] = [];
|
||||
private gesture: GestureDelegate;
|
||||
private lastTouch = 0;
|
||||
private pan: PanRecognizer;
|
||||
private hasCapturedPan = false;
|
||||
private hasPress = false;
|
||||
private hasStartedPan = false;
|
||||
private requiresMove = false;
|
||||
|
||||
@Prop() direction: string = 'x';
|
||||
@Prop() gestureName: string = '';
|
||||
@Prop() gesturePriority: number = 0;
|
||||
@Prop() listenOn: string = 'child';
|
||||
@Prop() maxAngle: number = 40;
|
||||
@Prop() threshold: number = 20;
|
||||
@Prop() type: string = 'pan';
|
||||
|
||||
@Prop() canStart: GestureCallback;
|
||||
@Prop() onStart: GestureCallback;
|
||||
@Prop() onMove: GestureCallback;
|
||||
@Prop() onEnd: GestureCallback;
|
||||
@Prop() onPress: GestureCallback;
|
||||
@Prop() notCaptured: GestureCallback;
|
||||
|
||||
|
||||
ionViewDidLoad() {
|
||||
Ionic.controllers.gesture = (Ionic.controllers.gesture || new GestureController());
|
||||
|
||||
this.gesture = (<GestureController>Ionic.controllers.gesture).createGesture(this.gestureName, this.gesturePriority, false);
|
||||
|
||||
const types = this.type.replace(/\s/g, '').toLowerCase().split(',');
|
||||
|
||||
if (types.indexOf('pan') > -1) {
|
||||
this.pan = new PanRecognizer(this.direction, this.threshold, this.maxAngle);
|
||||
this.requiresMove = true;
|
||||
}
|
||||
this.hasPress = (types.indexOf('press') > -1);
|
||||
|
||||
if (this.pan || this.hasPress) {
|
||||
Ionic.listener.enable(this, 'touchstart', true, this.listenOn);
|
||||
Ionic.listener.enable(this, 'mousedown', true, this.listenOn);
|
||||
|
||||
Ionic.dom.write(() => {
|
||||
applyStyles(getElementReference(this.$el, this.listenOn), GESTURE_INLINE_STYLES);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// DOWN *************************
|
||||
|
||||
@Listen('touchstart', { passive: true, enabled: false })
|
||||
onTouchStart(ev: TouchEvent) {
|
||||
this.lastTouch = now(ev);
|
||||
|
||||
this.enableMouse(false);
|
||||
this.enableTouch(true);
|
||||
|
||||
this.pointerDown(ev, this.lastTouch);
|
||||
}
|
||||
|
||||
|
||||
@Listen('mousedown', { passive: true, enabled: false })
|
||||
onMouseDown(ev: MouseEvent) {
|
||||
const timeStamp = now(ev);
|
||||
|
||||
if (this.lastTouch === 0 || (this.lastTouch + MOUSE_WAIT < timeStamp)) {
|
||||
this.enableMouse(true);
|
||||
this.enableTouch(false);
|
||||
|
||||
this.pointerDown(ev, timeStamp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private pointerDown(ev: UIEvent, timeStamp: number): boolean {
|
||||
if (!this.gesture || this.hasStartedPan) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const detail = this.detail;
|
||||
|
||||
detail.startX = detail.currentX = pointerCoordX(ev);
|
||||
detail.startY = detail.currentY = pointerCoordY(ev);
|
||||
detail.startTimeStamp = detail.timeStamp = timeStamp;
|
||||
detail.velocityX = detail.velocityY = detail.deltaX = detail.deltaY = 0;
|
||||
detail.directionX = detail.directionY = detail.velocityDirectionX = detail.velocityDirectionY = null;
|
||||
detail.event = ev;
|
||||
this.positions.length = 0;
|
||||
|
||||
if (this.canStart && this.canStart(detail) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.positions.push(detail.currentX, detail.currentY, timeStamp);
|
||||
|
||||
// Release fallback
|
||||
this.gesture.release();
|
||||
|
||||
// Start gesture
|
||||
if (!this.gesture.start()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.pan) {
|
||||
this.hasStartedPan = true;
|
||||
this.hasCapturedPan = false;
|
||||
|
||||
this.pan.start(detail.startX, detail.startY);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// MOVE *************************
|
||||
|
||||
@Listen('touchmove', { passive: true, enabled: false })
|
||||
onTouchMove(ev: TouchEvent) {
|
||||
this.lastTouch = this.detail.timeStamp = now(ev);
|
||||
|
||||
this.pointerMove(ev);
|
||||
}
|
||||
|
||||
|
||||
@Listen('document:mousemove', { passive: true, enabled: false })
|
||||
onMoveMove(ev: TouchEvent) {
|
||||
const timeStamp = now(ev);
|
||||
|
||||
if (this.lastTouch === 0 || (this.lastTouch + MOUSE_WAIT < timeStamp)) {
|
||||
this.detail.timeStamp = timeStamp;
|
||||
this.pointerMove(ev);
|
||||
}
|
||||
}
|
||||
|
||||
private pointerMove(ev: UIEvent) {
|
||||
const detail = this.detail;
|
||||
this.calcGestureData(ev);
|
||||
|
||||
if (this.pan) {
|
||||
if (this.hasCapturedPan) {
|
||||
Ionic.dom.write(() => {
|
||||
detail.type = 'pan';
|
||||
|
||||
if (this.onMove) {
|
||||
this.onMove(detail);
|
||||
} else {
|
||||
Ionic.emit(this, 'ionGestureMove', this.detail);
|
||||
}
|
||||
});
|
||||
|
||||
} else if (this.pan.detect(detail.currentX, detail.currentY)) {
|
||||
if (this.pan.isGesture() !== 0) {
|
||||
if (!this.tryToCapturePan(ev)) {
|
||||
this.abortGesture();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private calcGestureData(ev: UIEvent) {
|
||||
const detail = this.detail;
|
||||
detail.currentX = pointerCoordX(ev);
|
||||
detail.currentY = pointerCoordY(ev);
|
||||
detail.deltaX = (detail.currentX - detail.startX);
|
||||
detail.deltaY = (detail.currentY - detail.startY);
|
||||
detail.event = ev;
|
||||
|
||||
// figure out which direction we're movin'
|
||||
detail.directionX = detail.velocityDirectionX = (detail.deltaX > 0 ? 'left' : (detail.deltaX < 0 ? 'right' : null));
|
||||
detail.directionY = detail.velocityDirectionY = (detail.deltaY > 0 ? 'up' : (detail.deltaY < 0 ? 'down' : null));
|
||||
|
||||
const positions = this.positions;
|
||||
positions.push(detail.currentX, detail.currentY, detail.timeStamp);
|
||||
|
||||
var endPos = (positions.length - 1);
|
||||
var startPos = endPos;
|
||||
var timeRange = (detail.timeStamp - 100);
|
||||
|
||||
// move pointer to position measured 100ms ago
|
||||
for (var i = endPos; i > 0 && positions[i] > timeRange; i -= 3) {
|
||||
startPos = i;
|
||||
}
|
||||
|
||||
if (startPos !== endPos) {
|
||||
// compute relative movement between these two points
|
||||
var movedX = (positions[startPos - 2] - positions[endPos - 2]);
|
||||
var movedY = (positions[startPos - 1] - positions[endPos - 1]);
|
||||
var factor = 16 / (positions[endPos] - positions[startPos]);
|
||||
|
||||
// based on XXms compute the movement to apply for each render step
|
||||
detail.velocityX = movedX * factor;
|
||||
detail.velocityY = movedY * factor;
|
||||
|
||||
detail.velocityDirectionX = (detail.velocityX > 0 ? 'left' : (detail.velocityX < 0 ? 'right' : null));
|
||||
detail.velocityDirectionY = (detail.velocityY > 0 ? 'up' : (detail.velocityY < 0 ? 'down' : null));
|
||||
}
|
||||
}
|
||||
|
||||
private tryToCapturePan(ev: UIEvent): boolean {
|
||||
if (this.gesture && !this.gesture.capture()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.detail.event = ev;
|
||||
|
||||
if (this.onStart) {
|
||||
this.onStart(this.detail);
|
||||
} else {
|
||||
Ionic.emit(this, 'ionGestureStart', this.detail);
|
||||
}
|
||||
|
||||
this.hasCapturedPan = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private abortGesture() {
|
||||
this.hasStartedPan = false;
|
||||
this.hasCapturedPan = false;
|
||||
|
||||
this.gesture.release();
|
||||
|
||||
this.enable(false);
|
||||
this.notCaptured(this.detail);
|
||||
}
|
||||
|
||||
|
||||
// END *************************
|
||||
|
||||
@Listen('touchend', { passive: true, enabled: false })
|
||||
onTouchEnd(ev: TouchEvent) {
|
||||
this.lastTouch = this.detail.timeStamp = now(ev);
|
||||
|
||||
this.pointerUp(ev);
|
||||
this.enableTouch(false);
|
||||
}
|
||||
|
||||
|
||||
@Listen('document:mouseup', { passive: true, enabled: false })
|
||||
onMouseUp(ev: TouchEvent) {
|
||||
const timeStamp = now(ev);
|
||||
|
||||
if (this.lastTouch === 0 || (this.lastTouch + MOUSE_WAIT < timeStamp)) {
|
||||
this.detail.timeStamp = timeStamp;
|
||||
this.pointerUp(ev);
|
||||
this.enableMouse(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private pointerUp(ev: UIEvent) {
|
||||
const detail = this.detail;
|
||||
|
||||
this.gesture && this.gesture.release();
|
||||
|
||||
detail.event = ev;
|
||||
|
||||
this.calcGestureData(ev);
|
||||
|
||||
if (this.pan) {
|
||||
if (this.hasCapturedPan) {
|
||||
detail.type = 'pan';
|
||||
if (this.onEnd) {
|
||||
this.onEnd(detail);
|
||||
} else {
|
||||
Ionic.emit(this, 'ionGestureEnd', detail);
|
||||
}
|
||||
|
||||
} else if (this.hasPress) {
|
||||
this.detectPress();
|
||||
|
||||
} else {
|
||||
if (this.notCaptured) {
|
||||
this.notCaptured(detail);
|
||||
} else {
|
||||
Ionic.emit(this, 'ionGestureNotCaptured', detail);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (this.hasPress) {
|
||||
this.detectPress();
|
||||
}
|
||||
|
||||
this.hasCapturedPan = false;
|
||||
this.hasStartedPan = false;
|
||||
}
|
||||
|
||||
|
||||
private detectPress() {
|
||||
const detail = this.detail;
|
||||
|
||||
if (Math.abs(detail.startX - detail.currentX) < 10 && Math.abs(detail.startY - detail.currentY) < 10) {
|
||||
detail.type = 'press';
|
||||
|
||||
if (this.onPress) {
|
||||
this.onPress(detail);
|
||||
} else {
|
||||
Ionic.emit(this, 'ionPress', detail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ENABLE LISTENERS *************************
|
||||
|
||||
private enableMouse(shouldEnable: boolean) {
|
||||
if (this.requiresMove) {
|
||||
Ionic.listener.enable(this, 'document:mousemove', shouldEnable);
|
||||
}
|
||||
Ionic.listener.enable(this, 'document:mouseup', shouldEnable);
|
||||
}
|
||||
|
||||
|
||||
private enableTouch(shouldEnable: boolean) {
|
||||
if (this.requiresMove) {
|
||||
Ionic.listener.enable(this, 'touchmove', shouldEnable);
|
||||
}
|
||||
Ionic.listener.enable(this, 'touchend', shouldEnable);
|
||||
}
|
||||
|
||||
|
||||
private enable(shouldEnable: boolean) {
|
||||
this.enableMouse(shouldEnable);
|
||||
this.enableTouch(shouldEnable);
|
||||
}
|
||||
|
||||
|
||||
ionViewWillUnload() {
|
||||
this.gesture && this.gesture.destroy();
|
||||
this.gesture = this.pan = this.detail = this.detail.event = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
const GESTURE_INLINE_STYLES = {
|
||||
'touch-action': 'none',
|
||||
'user-select': 'none',
|
||||
'-webkit-user-drag': 'none',
|
||||
'-webkit-tap-highlight-color': 'rgba(0,0,0,0)'
|
||||
};
|
||||
|
||||
const MOUSE_WAIT = 2500;
|
||||
|
||||
|
||||
function now(ev: UIEvent) {
|
||||
return ev.timeStamp || Date.now();
|
||||
}
|
||||
|
66
src/components/gesture/recognizers.ts
Normal file
66
src/components/gesture/recognizers.ts
Normal file
@ -0,0 +1,66 @@
|
||||
|
||||
|
||||
export class PanRecognizer {
|
||||
private startX: number;
|
||||
private startY: number;
|
||||
|
||||
private dirty: boolean = false;
|
||||
private threshold: number;
|
||||
private maxCosine: number;
|
||||
|
||||
private angle = 0;
|
||||
private isPan = 0;
|
||||
|
||||
|
||||
constructor(private direction: string, threshold: number, maxAngle: number) {
|
||||
const radians = maxAngle * (Math.PI / 180);
|
||||
this.maxCosine = Math.cos(radians);
|
||||
this.threshold = threshold * threshold;
|
||||
}
|
||||
|
||||
start(x: number, y: number) {
|
||||
this.startX = x;
|
||||
this.startY = y;
|
||||
this.angle = 0;
|
||||
this.isPan = 0;
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
detect(x: number, y: number): boolean {
|
||||
if (!this.dirty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const deltaX = (x - this.startX);
|
||||
const deltaY = (y - this.startY);
|
||||
const distance = deltaX * deltaX + deltaY * deltaY;
|
||||
|
||||
if (distance >= this.threshold) {
|
||||
var angle = Math.atan2(deltaY, deltaX);
|
||||
var cosine = (this.direction === 'y')
|
||||
? Math.sin(angle)
|
||||
: Math.cos(angle);
|
||||
|
||||
this.angle = angle;
|
||||
|
||||
if (cosine > this.maxCosine) {
|
||||
this.isPan = 1;
|
||||
|
||||
} else if (cosine < -this.maxCosine) {
|
||||
this.isPan = -1;
|
||||
|
||||
} else {
|
||||
this.isPan = 0;
|
||||
}
|
||||
|
||||
this.dirty = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
isGesture(): number {
|
||||
return this.isPan;
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule, ModuleWithProviders, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
|
||||
import { IconModule } from '../icon/icon.module';
|
||||
|
||||
import { Tab } from './tab';
|
||||
import { TabButton } from './tab-button';
|
||||
import { TabHighlight } from './tab-highlight';
|
||||
import { Tabs } from './tabs';
|
||||
|
||||
/** @hidden */
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
IconModule
|
||||
],
|
||||
declarations: [
|
||||
Tab,
|
||||
TabButton,
|
||||
TabHighlight,
|
||||
Tabs
|
||||
],
|
||||
exports: [
|
||||
Tab,
|
||||
TabButton,
|
||||
TabHighlight,
|
||||
Tabs
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class TabsModule {
|
||||
public static forRoot(): ModuleWithProviders {
|
||||
return {
|
||||
ngModule: TabsModule, providers: []
|
||||
};
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
|
||||
import { Toggle } from '../../../../../../';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'root-page.html'
|
||||
@ -38,20 +37,20 @@ export class RootPage {
|
||||
this.grapeCtrl.enabled ? this.grapeCtrl.disable() : this.grapeCtrl.enable();
|
||||
}
|
||||
|
||||
appleChange(toggle: Toggle) {
|
||||
appleChange(toggle: any) {
|
||||
console.log('appleChange', toggle);
|
||||
}
|
||||
|
||||
bananaChange(toggle: Toggle) {
|
||||
bananaChange(toggle: any) {
|
||||
console.log('bananaChange', toggle);
|
||||
}
|
||||
|
||||
kiwiChange(toggle: Toggle) {
|
||||
kiwiChange(toggle: any) {
|
||||
console.log('kiwiChange', toggle);
|
||||
this.kiwiValue = toggle.checked;
|
||||
}
|
||||
|
||||
strawberryChange(toggle: Toggle) {
|
||||
strawberryChange(toggle: any) {
|
||||
console.log('strawberryChange', toggle);
|
||||
this.strawberryValue = toggle.checked;
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
|
||||
import { Toggle } from '../toggle';
|
||||
import { mockConfig, mockPlatform, mockHaptic, mockElementRef, mockGestureController, mockRenderer, mockItem, mockForm, mockChangeDetectorRef, mockZone } from '../../../util/mock-providers';
|
||||
import { commonInputTest, BOOLEAN_CORPUS } from '../../../util/input-tester';
|
||||
|
||||
describe('Toggle', () => {
|
||||
|
||||
it('should pass common test', () => {
|
||||
|
||||
const platform = mockPlatform();
|
||||
const config = mockConfig();
|
||||
const elementRef = mockElementRef();
|
||||
const renderer = mockRenderer();
|
||||
const item: any = mockItem();
|
||||
const form = mockForm();
|
||||
const haptic = mockHaptic();
|
||||
const cd = mockChangeDetectorRef();
|
||||
const gesture = mockGestureController();
|
||||
const zone = mockZone();
|
||||
const toggle = new Toggle(form, config, platform, elementRef, renderer, haptic, item, gesture, null, cd, zone);
|
||||
|
||||
commonInputTest(toggle, {
|
||||
defaultValue: false,
|
||||
corpus: BOOLEAN_CORPUS,
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
@ -1,53 +0,0 @@
|
||||
import { GestureController, GESTURE_PRIORITY_TOGGLE, GESTURE_TOGGLE } from '../../gestures/gesture-controller';
|
||||
import { DomController } from '../../platform/dom-controller';
|
||||
import { PanGesture } from '../../gestures/pan-gesture';
|
||||
import { Platform } from '../../platform/platform';
|
||||
import { pointerCoord } from '../../util/dom';
|
||||
import { Toggle } from './toggle';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
export class ToggleGesture extends PanGesture {
|
||||
|
||||
constructor(
|
||||
plt: Platform,
|
||||
public toggle: Toggle,
|
||||
gestureCtrl: GestureController,
|
||||
domCtrl: DomController
|
||||
) {
|
||||
super(
|
||||
plt,
|
||||
toggle.getNativeElement(), {
|
||||
threshold: 0,
|
||||
zone: false,
|
||||
domController: domCtrl,
|
||||
gesture: gestureCtrl.createGesture({
|
||||
name: GESTURE_TOGGLE,
|
||||
priority: GESTURE_PRIORITY_TOGGLE
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
canStart(ev: any): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
onDragStart(ev: any) {
|
||||
ev.preventDefault();
|
||||
|
||||
this.toggle._onDragStart(pointerCoord(ev).x);
|
||||
}
|
||||
|
||||
onDragMove(ev: any) {
|
||||
ev.preventDefault();
|
||||
|
||||
this.toggle._onDragMove(pointerCoord(ev).x);
|
||||
}
|
||||
|
||||
onDragEnd(ev: any) {
|
||||
ev.preventDefault();
|
||||
|
||||
this.toggle._onDragEnd(pointerCoord(ev).x);
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
@import "../../themes/ionic.globals.ios";
|
||||
@import "./toggle";
|
||||
|
||||
|
||||
// iOS Toggle
|
||||
// --------------------------------------------------
|
||||
|
@ -1,4 +1,6 @@
|
||||
@import "../../themes/ionic.globals.md";
|
||||
@import "./toggle";
|
||||
|
||||
|
||||
// Material Design Toggle
|
||||
// --------------------------------------------------
|
||||
|
35
src/components/toggle/toggle.scss
Normal file
35
src/components/toggle/toggle.scss
Normal file
@ -0,0 +1,35 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
|
||||
|
||||
// Toggle
|
||||
// --------------------------------------------------
|
||||
|
||||
:host {
|
||||
display: inline-block;
|
||||
visibility: inherit !important;
|
||||
contain: content;
|
||||
}
|
||||
|
||||
|
||||
.toggle-cover {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
border: 0;
|
||||
|
||||
background: transparent;
|
||||
|
||||
font-family: inherit;
|
||||
font-style: inherit;
|
||||
font-variant: inherit;
|
||||
|
||||
line-height: 1;
|
||||
text-transform: none;
|
||||
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
@ -1,238 +1,147 @@
|
||||
import { NgZone, AfterViewInit, ChangeDetectorRef, Component, ElementRef, HostListener, Input, OnDestroy, Optional, Renderer, ViewEncapsulation } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { BooleanInputComponent, GestureDetail } from '../../util/interfaces';
|
||||
import { Component, h, Ionic, Listen, Prop, Watch } from '../../index';
|
||||
|
||||
import { Config } from '../../config/config';
|
||||
import { DomController } from '../../platform/dom-controller';
|
||||
import { Form, IonicTapInput } from '../../util/form';
|
||||
import { GestureController } from '../../gestures/gesture-controller';
|
||||
import { Haptic } from '../../tap-click/haptic';
|
||||
import { assert, isTrueProperty } from '../../util/util';
|
||||
import { BaseInput } from '../../util/base-input';
|
||||
import { Item } from '../item/item';
|
||||
import { KEY_ENTER, KEY_SPACE } from '../../platform/key';
|
||||
import { Platform } from '../../platform/platform';
|
||||
import { ToggleGesture } from './toggle-gesture';
|
||||
|
||||
/**
|
||||
* @name Toggle
|
||||
* @description
|
||||
* A toggle technically is the same thing as an HTML checkbox input,
|
||||
* except it looks different and is easier to use on a touch device.
|
||||
* Toggles can also have colors assigned to them, by adding any color
|
||||
* attribute.
|
||||
*
|
||||
* See the [Angular 2 Docs](https://angular.io/docs/ts/latest/guide/forms.html)
|
||||
* for more info on forms and inputs.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
*
|
||||
* <ion-list>
|
||||
*
|
||||
* <ion-item>
|
||||
* <ion-label>Pepperoni</ion-label>
|
||||
* <ion-toggle [(ngModel)]="pepperoni"></ion-toggle>
|
||||
* </ion-item>
|
||||
*
|
||||
* <ion-item>
|
||||
* <ion-label>Sausage</ion-label>
|
||||
* <ion-toggle [(ngModel)]="sausage" disabled="true"></ion-toggle>
|
||||
* </ion-item>
|
||||
*
|
||||
* <ion-item>
|
||||
* <ion-label>Mushrooms</ion-label>
|
||||
* <ion-toggle [(ngModel)]="mushrooms"></ion-toggle>
|
||||
* </ion-item>
|
||||
*
|
||||
* </ion-list>
|
||||
* ```
|
||||
*
|
||||
* @demo /docs/demos/src/toggle/
|
||||
* @see {@link /docs/components#toggle Toggle Component Docs}
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ion-toggle',
|
||||
template:
|
||||
'<div class="toggle-icon">' +
|
||||
'<div class="toggle-inner"></div>' +
|
||||
'</div>' +
|
||||
'<button role="checkbox" ' +
|
||||
'type="button" ' +
|
||||
'ion-button="item-cover" ' +
|
||||
'[id]="id" ' +
|
||||
'[attr.aria-checked]="_value" ' +
|
||||
'[attr.aria-labelledby]="_labelId" ' +
|
||||
'[attr.aria-disabled]="_disabled" ' +
|
||||
'class="item-cover" disable-activated>' +
|
||||
'</button>',
|
||||
host: {
|
||||
'[class.toggle-disabled]': '_disabled',
|
||||
'[class.toggle-checked]': '_value',
|
||||
'[class.toggle-activated]': '_activated',
|
||||
},
|
||||
providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: Toggle, multi: true } ],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
tag: 'ion-toggle',
|
||||
styleUrls: {
|
||||
ios: 'toggle.ios.scss',
|
||||
md: 'toggle.md.scss',
|
||||
wp: 'toggle.wp.scss'
|
||||
}
|
||||
})
|
||||
export class Toggle extends BaseInput<boolean> implements IonicTapInput, AfterViewInit, OnDestroy {
|
||||
export class Toggle implements BooleanInputComponent {
|
||||
activated: boolean;
|
||||
hasFocus: boolean;
|
||||
id: string;
|
||||
labelId: string;
|
||||
startX: number;
|
||||
|
||||
_activated: boolean = false;
|
||||
_startX: number;
|
||||
_msPrv: number = 0;
|
||||
_gesture: ToggleGesture;
|
||||
@Prop() checked: boolean;
|
||||
@Prop() disabled: boolean;
|
||||
@Prop() value: string;
|
||||
|
||||
/**
|
||||
* @input {boolean} If true, the element is selected.
|
||||
*/
|
||||
@Input()
|
||||
get checked(): boolean {
|
||||
return this.value;
|
||||
|
||||
@Watch('checked')
|
||||
changed(val: boolean) {
|
||||
Ionic.emit(this, 'ionChange', { checked: val });
|
||||
}
|
||||
|
||||
set checked(val: boolean) {
|
||||
this.value = val;
|
||||
|
||||
canStart() {
|
||||
return !this.disabled;
|
||||
}
|
||||
|
||||
constructor(
|
||||
form: Form,
|
||||
config: Config,
|
||||
private _plt: Platform,
|
||||
elementRef: ElementRef,
|
||||
renderer: Renderer,
|
||||
private _haptic: Haptic,
|
||||
@Optional() item: Item,
|
||||
private _gestureCtrl: GestureController,
|
||||
private _domCtrl: DomController,
|
||||
private _cd: ChangeDetectorRef,
|
||||
private _zone: NgZone,
|
||||
) {
|
||||
super(config, elementRef, renderer, 'toggle', false, form, item, null);
|
||||
|
||||
onDragStart(detail: GestureDetail) {
|
||||
this.startX = detail.startX;
|
||||
this.fireFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
ngAfterViewInit() {
|
||||
this._initialize();
|
||||
this._gesture = new ToggleGesture(this._plt, this, this._gestureCtrl, this._domCtrl);
|
||||
this._gesture.listen();
|
||||
|
||||
onDragMove(detail: GestureDetail) {
|
||||
if (this.checked) {
|
||||
if (detail.currentX + 15 < this.startX) {
|
||||
this.checked = false;
|
||||
this.activated = true;
|
||||
this.startX = detail.currentX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
_inputCheckHasValue() {}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
_inputNormalize(val: any): boolean {
|
||||
return isTrueProperty(val);
|
||||
} else if (detail.currentX - 15 > this.startX) {
|
||||
this.checked = true;
|
||||
this.activated = (detail.currentX < this.startX + 5);
|
||||
this.startX = detail.currentX;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
_onDragStart(startX: number) {
|
||||
assert(startX, 'startX must be valid');
|
||||
console.debug('toggle, _onDragStart', startX);
|
||||
|
||||
this._zone.run(() => {
|
||||
this._startX = startX;
|
||||
this._fireFocus();
|
||||
this._activated = true;
|
||||
});
|
||||
onDragEnd(detail: GestureDetail) {
|
||||
if (this.checked) {
|
||||
if (detail.startX + 4 > detail.currentX) {
|
||||
this.checked = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
_onDragMove(currentX: number) {
|
||||
if (!this._startX) {
|
||||
assert(false, '_startX must be valid');
|
||||
return;
|
||||
} else if (detail.startX - 4 < detail.currentX) {
|
||||
this.checked = true;
|
||||
}
|
||||
|
||||
let dirty = false;
|
||||
let value: boolean;
|
||||
let activated: boolean;
|
||||
|
||||
if (this._value) {
|
||||
if (currentX + 15 < this._startX) {
|
||||
dirty = true;
|
||||
value = false;
|
||||
activated = true;
|
||||
this.activated = false;
|
||||
this.fireBlur();
|
||||
this.startX = null;
|
||||
}
|
||||
|
||||
} else if (currentX - 15 > this._startX) {
|
||||
dirty = true;
|
||||
value = true;
|
||||
activated = (currentX < this._startX + 5);
|
||||
}
|
||||
|
||||
if (dirty) {
|
||||
this._zone.run(() => {
|
||||
this.value = value;
|
||||
this._startX = currentX;
|
||||
this._activated = activated;
|
||||
this._haptic.selection();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
_onDragEnd(endX: number) {
|
||||
if (!this._startX) {
|
||||
assert(false, '_startX must be valid');
|
||||
return;
|
||||
}
|
||||
console.debug('toggle, _onDragEnd', endX);
|
||||
|
||||
this._zone.run(() => {
|
||||
if (this._value) {
|
||||
if (this._startX + 4 > endX) {
|
||||
this.value = false;
|
||||
this._haptic.selection();
|
||||
}
|
||||
|
||||
} else if (this._startX - 4 < endX) {
|
||||
this.value = true;
|
||||
this._haptic.selection();
|
||||
}
|
||||
|
||||
this._activated = false;
|
||||
this._fireBlur();
|
||||
this._startX = null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@HostListener('keyup', ['$event']) _keyup(ev: KeyboardEvent) {
|
||||
if (ev.keyCode === KEY_SPACE || ev.keyCode === KEY_ENTER) {
|
||||
console.debug(`toggle, keyup: ${ev.keyCode}`);
|
||||
ev.preventDefault();
|
||||
@Listen('keydown.space')
|
||||
onSpace(ev: KeyboardEvent) {
|
||||
this.toggle();
|
||||
ev.stopPropagation();
|
||||
this.value = !this.value;
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
|
||||
toggle() {
|
||||
if (!this.disabled) {
|
||||
this.checked = !this.checked;
|
||||
this.fireFocus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
initFocus() {
|
||||
this._elementRef.nativeElement.querySelector('button').focus();
|
||||
|
||||
fireFocus() {
|
||||
if (!this.hasFocus) {
|
||||
this.hasFocus = true;
|
||||
Ionic.emit(this, 'ionFocus');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
ngOnDestroy() {
|
||||
super.ngOnDestroy();
|
||||
this._gesture && this._gesture.destroy();
|
||||
|
||||
fireBlur() {
|
||||
if (this.hasFocus) {
|
||||
this.hasFocus = false;
|
||||
Ionic.emit(this, 'ionBlur');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
return h(this,
|
||||
h('ion-gesture', Ionic.theme(this, 'toggle', {
|
||||
class: {
|
||||
'toggle-activated': this.activated,
|
||||
'toggle-checked': this.checked,
|
||||
'toggle-disabled': this.disabled,
|
||||
},
|
||||
props: {
|
||||
'canStart': this.canStart.bind(this),
|
||||
'onStart': this.onDragStart.bind(this),
|
||||
'onMove': this.onDragMove.bind(this),
|
||||
'onEnd': this.onDragEnd.bind(this),
|
||||
'onPress': this.toggle.bind(this),
|
||||
'gestureName': 'toggle',
|
||||
'gesturePriority': 30,
|
||||
'type': 'pan,press',
|
||||
'direction': 'x',
|
||||
'threshold': 20,
|
||||
'listenOn': 'parent'
|
||||
}
|
||||
}),
|
||||
[
|
||||
h('div.toggle-icon',
|
||||
h('div.toggle-inner')
|
||||
),
|
||||
h('div.toggle-cover', {
|
||||
attrs: {
|
||||
'id': this.id,
|
||||
'aria-checked': this.checked ? 'true' : false,
|
||||
'aria-disabled': this.disabled ? 'true' : false,
|
||||
'aria-labelledby': this.labelId,
|
||||
'role': 'checkbox',
|
||||
'tabindex': 0
|
||||
}
|
||||
})
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
@import "../../themes/ionic.globals.wp";
|
||||
@import "./toggle";
|
||||
|
||||
|
||||
// Windows Toggle
|
||||
// --------------------------------------------------
|
||||
|
27
src/index.ts
27
src/index.ts
@ -12,12 +12,7 @@ export { AlertCmp } from './components/alert/alert-component';
|
||||
export { App } from './components/app/app';
|
||||
export { Avatar } from './components/avatar/avatar';
|
||||
export { Backdrop } from './components/backdrop/backdrop';
|
||||
export { Badge } from './components/badge/badge';
|
||||
export { Button } from './components/button/button';
|
||||
export { Card } from './components/card/card';
|
||||
export { CardContent } from './components/card/card-content';
|
||||
export { CardHeader } from './components/card/card-header';
|
||||
export { CardTitle } from './components/card/card-title';
|
||||
export { Checkbox } from './components/checkbox/checkbox';
|
||||
export { Chip } from './components/chip/chip';
|
||||
export { Content, ScrollEvent } from './components/content/content';
|
||||
@ -102,7 +97,6 @@ export { Toast } from './components/toast/toast';
|
||||
export { ToastCmp } from './components/toast/toast-component';
|
||||
export { ToastController } from './components/toast/toast-controller';
|
||||
export { ToastOptions } from './components/toast/toast-options';
|
||||
export { Toggle } from './components/toggle/toggle';
|
||||
export { Footer } from './components/toolbar/toolbar-footer';
|
||||
export { Header } from './components/toolbar/toolbar-header';
|
||||
export { Toolbar } from './components/toolbar/toolbar';
|
||||
@ -115,6 +109,7 @@ export { VirtualFooter } from './components/virtual-scroll/virtual-footer';
|
||||
export { VirtualHeader } from './components/virtual-scroll/virtual-header';
|
||||
export { VirtualItem } from './components/virtual-scroll/virtual-item';
|
||||
export { VirtualScroll } from './components/virtual-scroll/virtual-scroll';
|
||||
export { BooleanInput } from './bindings/angular/components/boolean-input';
|
||||
|
||||
|
||||
/**
|
||||
@ -130,6 +125,7 @@ export { NavController } from './navigation/nav-controller';
|
||||
export { NavControllerBase } from './navigation/nav-controller-base';
|
||||
export { NavParams } from './navigation/nav-params';
|
||||
export { NavLink, NavOptions, DeepLinkConfig, DeepLinkMetadata, DeepLinkMetadataFactory } from './navigation/nav-util';
|
||||
export { setupCore } from './bindings/angular/providers/ionic-core';
|
||||
export { TapClick, setupTapClick, isActivatable } from './tap-click/tap-click';
|
||||
export { UrlSerializer, DeepLinkConfigToken } from './navigation/url-serializer';
|
||||
export { ViewController } from './navigation/view-controller';
|
||||
@ -169,3 +165,22 @@ export { IonicGestureConfig } from './gestures/gesture-config';
|
||||
|
||||
export { IonicModule, IonicPageModule, provideLocationStrategy } from './module';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Core
|
||||
*/
|
||||
|
||||
import * as interfaces from './util/interfaces';
|
||||
|
||||
export declare const Component: interfaces.ComponentDecorator;
|
||||
|
||||
export declare const h: interfaces.Hyperscript;
|
||||
|
||||
export declare const Ionic: interfaces.Ionic;
|
||||
|
||||
export declare const Listen: interfaces.ListenDecorator;
|
||||
|
||||
export declare const Prop: interfaces.PropDecorator;
|
||||
|
||||
export declare const Watch: interfaces.WatchDecorator;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Import Angular
|
||||
*/
|
||||
import { ANALYZE_FOR_ENTRY_COMPONENTS, APP_INITIALIZER, ComponentFactoryResolver, Inject, Injector, ModuleWithProviders, NgModule, NgZone, Optional } from '@angular/core';
|
||||
import { ANALYZE_FOR_ENTRY_COMPONENTS, APP_INITIALIZER, ComponentFactoryResolver, CUSTOM_ELEMENTS_SCHEMA, Inject, Injector, ModuleWithProviders, NgModule, NgZone, Optional } from '@angular/core';
|
||||
import { APP_BASE_HREF, Location, LocationStrategy, HashLocationStrategy, PathLocationStrategy, PlatformLocation } from '@angular/common';
|
||||
import { DOCUMENT, HAMMER_GESTURE_CONFIG } from '@angular/platform-browser';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
@ -31,6 +31,7 @@ import { ModuleLoader, provideModuleLoader, setupPreloading, LAZY_LOADED_TOKEN }
|
||||
import { NgModuleLoader } from './util/ng-module-loader';
|
||||
import { Platform, setupPlatform } from './platform/platform';
|
||||
import { PlatformConfigToken, providePlatformConfigs } from './platform/platform-registry';
|
||||
import { setupCore } from './bindings/angular/providers/ionic-core';
|
||||
import { TapClick, setupTapClick } from './tap-click/tap-click';
|
||||
import { registerModeConfigs } from './config/mode-registry';
|
||||
import { TransitionController } from './transitions/transition-controller';
|
||||
@ -49,12 +50,7 @@ import { IonicApp } from './components/app/app-root';
|
||||
import { OverlayPortal } from './components/app/overlay-portal';
|
||||
import { Avatar } from './components/avatar/avatar';
|
||||
import { Backdrop } from './components/backdrop/backdrop';
|
||||
import { Badge } from './components/badge/badge';
|
||||
import { Button } from './components/button/button';
|
||||
import { Card } from './components/card/card';
|
||||
import { CardContent } from './components/card/card-content';
|
||||
import { CardHeader } from './components/card/card-header';
|
||||
import { CardTitle } from './components/card/card-title';
|
||||
import { Checkbox } from './components/checkbox/checkbox';
|
||||
import { Chip } from './components/chip/chip';
|
||||
import { Content } from './components/content/content';
|
||||
@ -128,7 +124,6 @@ import { Tabs } from './components/tabs/tabs';
|
||||
import { Thumbnail } from './components/thumbnail/thumbnail';
|
||||
import { ToastCmp } from './components/toast/toast-component';
|
||||
import { ToastController } from './components/toast/toast-controller';
|
||||
import { Toggle } from './components/toggle/toggle';
|
||||
import { Footer } from './components/toolbar/toolbar-footer';
|
||||
import { Header } from './components/toolbar/toolbar-header';
|
||||
import { Toolbar } from './components/toolbar/toolbar';
|
||||
@ -140,6 +135,7 @@ import { VirtualFooter } from './components/virtual-scroll/virtual-footer';
|
||||
import { VirtualHeader } from './components/virtual-scroll/virtual-header';
|
||||
import { VirtualItem } from './components/virtual-scroll/virtual-item';
|
||||
import { VirtualScroll } from './components/virtual-scroll/virtual-scroll';
|
||||
import { BooleanInput } from './bindings/angular/components/boolean-input';
|
||||
|
||||
/**
|
||||
* @name IonicModule
|
||||
@ -190,12 +186,7 @@ import { VirtualScroll } from './components/virtual-scroll/virtual-scroll';
|
||||
OverlayPortal,
|
||||
Avatar,
|
||||
Backdrop,
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
Checkbox,
|
||||
Chip,
|
||||
Col,
|
||||
@ -263,7 +254,6 @@ import { VirtualScroll } from './components/virtual-scroll/virtual-scroll';
|
||||
TextInput,
|
||||
Thumbnail,
|
||||
ToastCmp,
|
||||
Toggle,
|
||||
Footer,
|
||||
Header,
|
||||
Toolbar,
|
||||
@ -274,7 +264,8 @@ import { VirtualScroll } from './components/virtual-scroll/virtual-scroll';
|
||||
VirtualFooter,
|
||||
VirtualHeader,
|
||||
VirtualItem,
|
||||
VirtualScroll
|
||||
VirtualScroll,
|
||||
BooleanInput
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
@ -293,12 +284,7 @@ import { VirtualScroll } from './components/virtual-scroll/virtual-scroll';
|
||||
OverlayPortal,
|
||||
Avatar,
|
||||
Backdrop,
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
Checkbox,
|
||||
Chip,
|
||||
Col,
|
||||
@ -366,7 +352,6 @@ import { VirtualScroll } from './components/virtual-scroll/virtual-scroll';
|
||||
TextInput,
|
||||
Thumbnail,
|
||||
ToastCmp,
|
||||
Toggle,
|
||||
Footer,
|
||||
Header,
|
||||
Toolbar,
|
||||
@ -377,7 +362,8 @@ import { VirtualScroll } from './components/virtual-scroll/virtual-scroll';
|
||||
VirtualFooter,
|
||||
VirtualHeader,
|
||||
VirtualItem,
|
||||
VirtualScroll
|
||||
VirtualScroll,
|
||||
BooleanInput
|
||||
],
|
||||
entryComponents: [
|
||||
ActionSheetCmp,
|
||||
@ -389,7 +375,8 @@ import { VirtualScroll } from './components/virtual-scroll/virtual-scroll';
|
||||
PopoverCmp,
|
||||
SelectPopover,
|
||||
ToastCmp
|
||||
]
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class IonicModule {
|
||||
|
||||
@ -418,6 +405,7 @@ export class IonicModule {
|
||||
|
||||
// useFactory: ionic app initializers
|
||||
{ provide: APP_INITIALIZER, useFactory: registerModeConfigs, deps: [ Config ], multi: true },
|
||||
{ provide: APP_INITIALIZER, useFactory: setupCore, deps: [ Config, Platform, DomController, NgZone ], multi: true },
|
||||
{ provide: APP_INITIALIZER, useFactory: setupProvideEvents, deps: [ Platform, DomController ], multi: true },
|
||||
{ provide: APP_INITIALIZER, useFactory: setupTapClick, deps: [ Config, Platform, DomController, App, NgZone, GestureController ], multi: true },
|
||||
{ provide: APP_INITIALIZER, useFactory: setupPreloading, deps: [ Config, DeepLinkConfigToken, ModuleLoader, NgZone ], multi: true },
|
||||
|
@ -1,19 +0,0 @@
|
||||
|
||||
export const Component: ComponentDecorator = function(opts?: ComponentOptions): (target: any) => any {
|
||||
return function() {};
|
||||
};
|
||||
|
||||
|
||||
export interface ComponentDecorator {
|
||||
(opts?: ComponentOptions): any;
|
||||
}
|
||||
|
||||
|
||||
export interface ComponentOptions {
|
||||
tag: string;
|
||||
styleUrls?: string[] | ModeStyles;
|
||||
}
|
||||
|
||||
export interface ModeStyles {
|
||||
[modeName: string]: string | string[];
|
||||
}
|
@ -113,3 +113,89 @@ export interface PointerCoordinates {
|
||||
x?: number;
|
||||
y?: number;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function pointerCoordX(ev: any): number {
|
||||
// get X coordinates for either a mouse click
|
||||
// or a touch depending on the given event
|
||||
if (ev) {
|
||||
var changedTouches = ev.changedTouches;
|
||||
if (changedTouches && changedTouches.length > 0) {
|
||||
return changedTouches[0].clientX;
|
||||
}
|
||||
if (ev.pageX !== undefined) {
|
||||
return ev.pageX;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
export function pointerCoordY(ev: any): number {
|
||||
// get Y coordinates for either a mouse click
|
||||
// or a touch depending on the given event
|
||||
if (ev) {
|
||||
var changedTouches = ev.changedTouches;
|
||||
if (changedTouches && changedTouches.length > 0) {
|
||||
return changedTouches[0].clientY;
|
||||
}
|
||||
if (ev.pageY !== undefined) {
|
||||
return ev.pageY;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
export function getElementReference(elm: any, ref: string) {
|
||||
if (ref === 'child') {
|
||||
return elm.firstElementChild;
|
||||
}
|
||||
if (ref === 'parent') {
|
||||
if (elm.parentElement ) {
|
||||
// normal element with a parent element
|
||||
return elm.parentElement;
|
||||
}
|
||||
if (elm.parentNode && elm.parentNode.host) {
|
||||
// shadow dom's document fragment
|
||||
return elm.parentNode.host;
|
||||
}
|
||||
}
|
||||
if (ref === 'body') {
|
||||
return elm.ownerDocument.body;
|
||||
}
|
||||
if (ref === 'document') {
|
||||
return elm.ownerDocument;
|
||||
}
|
||||
if (ref === 'window') {
|
||||
return elm.ownerDocument.defaultView;
|
||||
}
|
||||
return elm;
|
||||
}
|
||||
|
||||
|
||||
export function getKeyCodeByName(keyName: string) {
|
||||
if (keyName === 'enter') {
|
||||
return 13;
|
||||
}
|
||||
if (keyName === 'escape') {
|
||||
return 27;
|
||||
}
|
||||
if (keyName === 'space') {
|
||||
return 32;
|
||||
}
|
||||
if (keyName === 'tab') {
|
||||
return 9;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
export function applyStyles(elm: HTMLElement, styles: {[styleProp: string]: string|number}) {
|
||||
const styleProps = Object.keys(styles);
|
||||
|
||||
for (var i = 0; i < styleProps.length; i++) {
|
||||
(<any>elm.style)[styleProps[i]] = styles[styleProps[i]];
|
||||
}
|
||||
}
|
||||
|
424
src/util/interfaces.ts
Normal file
424
src/util/interfaces.ts
Normal file
@ -0,0 +1,424 @@
|
||||
|
||||
export interface Ionic {
|
||||
emit: EventEmit;
|
||||
listener: {
|
||||
enable: EventListenerEnable;
|
||||
};
|
||||
theme: IonicTheme;
|
||||
controllers: {
|
||||
gesture?: any;
|
||||
};
|
||||
dom: DomControllerApi;
|
||||
}
|
||||
|
||||
|
||||
export interface EventEmit {
|
||||
(instance: any, eventName: string, data?: any): void;
|
||||
}
|
||||
|
||||
|
||||
export interface EventListenerEnable {
|
||||
(instance: any, eventName: string, enabled: boolean, listenOn?: string): void;
|
||||
}
|
||||
|
||||
|
||||
export interface EventListenerCallback {
|
||||
(ev?: any): void;
|
||||
}
|
||||
|
||||
|
||||
export interface GestureDetail {
|
||||
type?: string;
|
||||
event?: UIEvent;
|
||||
startX?: number;
|
||||
startY?: number;
|
||||
startTimeStamp?: number;
|
||||
currentX?: number;
|
||||
currentY?: number;
|
||||
velocityX?: number;
|
||||
velocityY?: number;
|
||||
deltaX?: number;
|
||||
deltaY?: number;
|
||||
directionX?: 'left'|'right';
|
||||
directionY?: 'up'|'down';
|
||||
velocityDirectionX?: 'left'|'right';
|
||||
velocityDirectionY?: 'up'|'down';
|
||||
timeStamp?: number;
|
||||
}
|
||||
|
||||
|
||||
export interface GestureCallback {
|
||||
(detail?: GestureDetail): boolean|void;
|
||||
}
|
||||
|
||||
|
||||
export interface IonicGlobal {
|
||||
staticDir?: string;
|
||||
components?: LoadComponents;
|
||||
loadComponents?: {(bundleId: string): void};
|
||||
config?: Object;
|
||||
configCtrl?: ConfigApi;
|
||||
domCtrl?: DomControllerApi;
|
||||
nextTickCtrl?: NextTickApi;
|
||||
eventNamePrefix?: string;
|
||||
}
|
||||
|
||||
|
||||
export interface NextTickApi {
|
||||
nextTick: NextTick;
|
||||
}
|
||||
|
||||
|
||||
export interface NextTick {
|
||||
(cb: Function): void;
|
||||
}
|
||||
|
||||
|
||||
export interface DomRead {
|
||||
(cb: Function): void;
|
||||
}
|
||||
|
||||
|
||||
export interface DomWrite {
|
||||
(cb: Function): void;
|
||||
}
|
||||
|
||||
|
||||
export interface DomControllerApi {
|
||||
read: DomRead;
|
||||
write: DomWrite;
|
||||
}
|
||||
|
||||
|
||||
export interface RafCallback {
|
||||
(timeStamp?: number): void;
|
||||
}
|
||||
|
||||
|
||||
export interface RequestAnimationFrame {
|
||||
(cb: RafCallback): void;
|
||||
}
|
||||
|
||||
|
||||
export interface LoadComponents {
|
||||
[tag: string]: any[];
|
||||
}
|
||||
|
||||
|
||||
export interface ComponentModeData {
|
||||
/**
|
||||
* tag name (ion-badge)
|
||||
*/
|
||||
[0]: string;
|
||||
|
||||
/**
|
||||
* component class name (Badge)
|
||||
*/
|
||||
[1]: string;
|
||||
|
||||
/**
|
||||
* listeners
|
||||
*/
|
||||
[2]: ComponentListenersData[];
|
||||
|
||||
/**
|
||||
* watchers
|
||||
*/
|
||||
[3]: ComponentWatchersData[];
|
||||
|
||||
/**
|
||||
* shadow
|
||||
*/
|
||||
[4]: boolean;
|
||||
|
||||
/**
|
||||
* mode name (ios, md, wp)
|
||||
*/
|
||||
[5]: string;
|
||||
|
||||
/**
|
||||
* component mode styles
|
||||
*/
|
||||
[6]: string;
|
||||
|
||||
/**
|
||||
* import component function
|
||||
*/
|
||||
[7]: ComponentModeImporterFn;
|
||||
}
|
||||
|
||||
|
||||
export interface ComponentListenersData {
|
||||
/**
|
||||
* methodName
|
||||
*/
|
||||
[0]: string;
|
||||
|
||||
/**
|
||||
* eventName
|
||||
*/
|
||||
[1]: string;
|
||||
|
||||
/**
|
||||
* capture
|
||||
*/
|
||||
[2]: number;
|
||||
|
||||
/**
|
||||
* passive
|
||||
*/
|
||||
[3]: number;
|
||||
|
||||
/**
|
||||
* enabled
|
||||
*/
|
||||
[4]: number;
|
||||
}
|
||||
|
||||
|
||||
export interface ComponentWatchersData {
|
||||
[methodName: string]: any;
|
||||
}
|
||||
|
||||
|
||||
export interface ComponentModeImporterFn {
|
||||
(importer: any, h: Hyperscript, Ionic: Ionic): void;
|
||||
}
|
||||
|
||||
|
||||
export interface ComponentDecorator {
|
||||
(opts?: ComponentOptions): any;
|
||||
}
|
||||
|
||||
|
||||
export interface ComponentOptions {
|
||||
tag: string;
|
||||
styleUrls?: string[] | ModeStyles;
|
||||
shadow?: boolean;
|
||||
}
|
||||
|
||||
export interface ModeStyles {
|
||||
[modeName: string]: string | string[];
|
||||
}
|
||||
|
||||
|
||||
export interface PropDecorator {
|
||||
(opts?: PropOptions): any;
|
||||
}
|
||||
|
||||
|
||||
export interface PropOptions {
|
||||
type?: number;
|
||||
}
|
||||
|
||||
|
||||
export interface Props {
|
||||
[propName: string]: PropOptions;
|
||||
}
|
||||
|
||||
|
||||
export interface ListenDecorator {
|
||||
(eventName: string, opts?: ListenOpts): any;
|
||||
}
|
||||
|
||||
|
||||
export interface ComponentMetaListeners {
|
||||
[methodName: string]: ListenOpts;
|
||||
}
|
||||
|
||||
|
||||
export interface ListenOpts {
|
||||
eventName?: string;
|
||||
capture?: boolean;
|
||||
passive?: boolean;
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
|
||||
export interface WatchDecorator {
|
||||
(propName: string): any;
|
||||
}
|
||||
|
||||
|
||||
export interface WatchOpts {
|
||||
fn: string;
|
||||
}
|
||||
|
||||
|
||||
export interface Watchers {
|
||||
[propName: string]: WatchOpts;
|
||||
}
|
||||
|
||||
|
||||
export interface IonicTheme {
|
||||
(instance: any, cssClassName: string, vnodeData: VNodeData): VNodeData;
|
||||
}
|
||||
|
||||
|
||||
export interface ConfigApi {
|
||||
get: (key: string, fallback?: any) => any;
|
||||
getBoolean: (key: string, fallback?: boolean) => boolean;
|
||||
getNumber: (key: string, fallback?: number) => number;
|
||||
}
|
||||
|
||||
|
||||
export interface ComponentMeta {
|
||||
tag?: string;
|
||||
props?: Props;
|
||||
listeners?: ComponentMetaListeners;
|
||||
watchers?: Watchers;
|
||||
shadow?: boolean;
|
||||
obsAttrs?: string[];
|
||||
hostCss?: string;
|
||||
componentModule?: any;
|
||||
modes: {[modeName: string]: ComponentMode};
|
||||
}
|
||||
|
||||
|
||||
export interface ComponentMode {
|
||||
bundleId?: string;
|
||||
styles?: string;
|
||||
styleUrls?: string[];
|
||||
styleElm?: HTMLElement;
|
||||
}
|
||||
|
||||
|
||||
export interface Component {
|
||||
ionViewDidLoad?: {(): void};
|
||||
ionViewWillUnload?: {(): void};
|
||||
|
||||
render?: {(): VNode};
|
||||
|
||||
mode?: string;
|
||||
color?: string;
|
||||
|
||||
$el?: ProxyElement;
|
||||
$meta?: ComponentMeta;
|
||||
$listeners?: ComponentActiveListeners;
|
||||
$root?: HTMLElement | ShadowRoot;
|
||||
$vnode?: VNode;
|
||||
|
||||
[memberName: string]: any;
|
||||
}
|
||||
|
||||
|
||||
export interface ComponentActiveListeners {
|
||||
[eventName: string]: Function;
|
||||
}
|
||||
|
||||
|
||||
export interface BaseInputComponent extends Component {
|
||||
disabled: boolean;
|
||||
hasFocus: boolean;
|
||||
value: string;
|
||||
|
||||
fireFocus: {(): void};
|
||||
fireBlur: {(): void};
|
||||
}
|
||||
|
||||
|
||||
export interface BooleanInputComponent extends BaseInputComponent {
|
||||
checked: boolean;
|
||||
toggle: {(ev: UIEvent): void};
|
||||
}
|
||||
|
||||
|
||||
export interface ComponentModule {
|
||||
new (): Component;
|
||||
}
|
||||
|
||||
|
||||
export interface ComponentRegistry {
|
||||
[tag: string]: ComponentMeta;
|
||||
}
|
||||
|
||||
|
||||
export interface ProxyElement extends HTMLElement {
|
||||
connectedCallback: {(): void};
|
||||
attributeChangedCallback: {(attrName: string, oldVal: string, newVal: string, namespace: string): void};
|
||||
disconnectedCallback: {(): void};
|
||||
|
||||
$queued?: boolean;
|
||||
$instance?: Component;
|
||||
|
||||
[memberName: string]: any;
|
||||
}
|
||||
|
||||
|
||||
export interface RendererApi {
|
||||
(oldVnode: VNode | Element, vnode: VNode): VNode;
|
||||
}
|
||||
|
||||
|
||||
export type Key = string | number;
|
||||
|
||||
|
||||
export interface Hyperscript {
|
||||
(sel: any): VNode;
|
||||
(sel: Node, data: VNodeData): VNode;
|
||||
(sel: any, data: VNodeData): VNode;
|
||||
(sel: any, text: string): VNode;
|
||||
(sel: any, children: Array<any>): VNode;
|
||||
(sel: any, data: VNodeData, text: string): VNode;
|
||||
(sel: any, data: VNodeData, children: Array<any|string>): VNode;
|
||||
(sel: any, data: VNodeData, children: any): VNode;
|
||||
}
|
||||
|
||||
|
||||
export interface VNode {
|
||||
sel: string | undefined;
|
||||
vdata: VNodeData | undefined;
|
||||
vchildren: Array<VNode | string> | undefined;
|
||||
elm: Node | undefined;
|
||||
vtext: string | undefined;
|
||||
vkey: Key;
|
||||
}
|
||||
|
||||
|
||||
export interface VNodeData {
|
||||
props?: any;
|
||||
attrs?: any;
|
||||
class?: any;
|
||||
style?: any;
|
||||
dataset?: any;
|
||||
on?: any;
|
||||
attachData?: any;
|
||||
vkey?: Key;
|
||||
vns?: string; // for SVGs
|
||||
[key: string]: any; // for any other 3rd party module
|
||||
}
|
||||
|
||||
|
||||
export interface PlatformApi {
|
||||
registerComponent: (tag: string, data: any[]) => ComponentMeta;
|
||||
getComponentMeta: (tag: string) => ComponentMeta;
|
||||
loadComponent: (bundleId: string, cb: Function) => void;
|
||||
nextTick: NextTick;
|
||||
domRead: DomRead;
|
||||
domWrite: DomWrite;
|
||||
|
||||
isElement: (node: Node) => node is Element;
|
||||
isText: (node: Node) => node is Text;
|
||||
isComment: (node: Node) => node is Comment;
|
||||
|
||||
$createElement<K extends keyof HTMLElementTagNameMap>(tagName: K): HTMLElementTagNameMap[K];
|
||||
$createElementNS: (namespaceURI: string, qualifiedName: string) => Element;
|
||||
$createTextNode: (text: string) => Text;
|
||||
$createComment: (text: string) => Comment;
|
||||
$insertBefore: (parentNode: Node, newNode: Node, referenceNode: Node | null) => void;
|
||||
$removeChild: (parentNode: Node, childNode: Node) => void;
|
||||
$appendChild: (parentNode: Node, childNode: Node) => void;
|
||||
$parentNode: (node: Node) => Node;
|
||||
$nextSibling: (node: Node) => Node;
|
||||
$tagName: (elm: Element) => string;
|
||||
$setTextContent: (node: Node, text: string | null) => void;
|
||||
$getTextContent: (node: Node) => string | null;
|
||||
$getAttribute: (elm: Element, attrName: string) => string;
|
||||
$attachShadow: (elm: Element, cmpMode: ComponentMode, cmpModeId: string) => ShadowRoot;
|
||||
}
|
||||
|
||||
|
||||
export interface ServerInitConfig {
|
||||
staticDir: string;
|
||||
config?: Object;
|
||||
}
|
Reference in New Issue
Block a user