mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
feat(toggle): boolean input directive
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
_inputCheckHasValue() {}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
_inputNormalize(val: any): boolean {
|
||||
return isTrueProperty(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
_onDragMove(currentX: number) {
|
||||
if (!this._startX) {
|
||||
assert(false, '_startX must be valid');
|
||||
return;
|
||||
}
|
||||
|
||||
let dirty = false;
|
||||
let value: boolean;
|
||||
let activated: boolean;
|
||||
|
||||
if (this._value) {
|
||||
if (currentX + 15 < this._startX) {
|
||||
dirty = true;
|
||||
value = false;
|
||||
activated = true;
|
||||
onDragMove(detail: GestureDetail) {
|
||||
if (this.checked) {
|
||||
if (detail.currentX + 15 < this.startX) {
|
||||
this.checked = false;
|
||||
this.activated = true;
|
||||
this.startX = detail.currentX;
|
||||
}
|
||||
|
||||
} else if (currentX - 15 > this._startX) {
|
||||
dirty = true;
|
||||
value = true;
|
||||
activated = (currentX < this._startX + 5);
|
||||
} else if (detail.currentX - 15 > this.startX) {
|
||||
this.checked = true;
|
||||
this.activated = (detail.currentX < this.startX + 5);
|
||||
this.startX = detail.currentX;
|
||||
}
|
||||
|
||||
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();
|
||||
onDragEnd(detail: GestureDetail) {
|
||||
if (this.checked) {
|
||||
if (detail.startX + 4 > detail.currentX) {
|
||||
this.checked = false;
|
||||
}
|
||||
|
||||
} else if (detail.startX - 4 < detail.currentX) {
|
||||
this.checked = true;
|
||||
}
|
||||
|
||||
this.activated = false;
|
||||
this.fireBlur();
|
||||
this.startX = null;
|
||||
}
|
||||
|
||||
|
||||
@Listen('keydown.space')
|
||||
onSpace(ev: KeyboardEvent) {
|
||||
this.toggle();
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
|
||||
toggle() {
|
||||
if (!this.disabled) {
|
||||
this.checked = !this.checked;
|
||||
this.fireFocus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fireFocus() {
|
||||
if (!this.hasFocus) {
|
||||
this.hasFocus = true;
|
||||
Ionic.emit(this, 'ionFocus');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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'
|
||||
}
|
||||
|
||||
} 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();
|
||||
ev.stopPropagation();
|
||||
this.value = !this.value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
initFocus() {
|
||||
this._elementRef.nativeElement.querySelector('button').focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
ngOnDestroy() {
|
||||
super.ngOnDestroy();
|
||||
this._gesture && this._gesture.destroy();
|
||||
}),
|
||||
[
|
||||
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
|
||||
// --------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user