diff --git a/ionic/components/checkbox/checkbox.scss b/ionic/components/checkbox/checkbox.scss
index 77c371ba02..00bc71b453 100644
--- a/ionic/components/checkbox/checkbox.scss
+++ b/ionic/components/checkbox/checkbox.scss
@@ -4,19 +4,10 @@
.checkbox {
- position: relative;
cursor: pointer;
@include user-select-none();
}
-.checkbox input {
- display: none;
-}
-
-.checkbox .input-label {
- max-width: 100%;
-}
-
.checkbox[aria-disabled=true] {
pointer-events: none;
opacity: 0.5;
diff --git a/ionic/components/checkbox/test/basic/e2e.ts b/ionic/components/checkbox/test/basic/e2e.ts
index cd8174623b..d666104663 100644
--- a/ionic/components/checkbox/test/basic/e2e.ts
+++ b/ionic/components/checkbox/test/basic/e2e.ts
@@ -4,7 +4,8 @@ it('should check apple via checkbox element click', function() {
});
-it('should enable/check grape via buttons', function() {
+it('should enable/check grape via buttons and submit form', function() {
element(by.css('#e2eGrapeDisabled')).click();
element(by.css('#e2eGrapeChecked')).click();
+ element(by.css('#e2eSubmit')).click();
});
diff --git a/ionic/components/checkbox/test/basic/main.html b/ionic/components/checkbox/test/basic/main.html
index eb7cfc0e0b..b0721e47e8 100644
--- a/ionic/components/checkbox/test/basic/main.html
+++ b/ionic/components/checkbox/test/basic/main.html
@@ -31,7 +31,7 @@
-
+
diff --git a/ionic/components/icon/icon.ts b/ionic/components/icon/icon.ts
index f47e6437f7..4d9dc4637b 100644
--- a/ionic/components/icon/icon.ts
+++ b/ionic/components/icon/icon.ts
@@ -69,7 +69,7 @@ export class IconDirective {
@Optional() @Ancestor() AncestorButton: Button,
@Attribute('forward') forward: string,
config: IonicConfig,
- private _renderer: Renderer
+ private renderer: Renderer
) {
let ele = this.ele = _elementRef.nativeElement;
@@ -109,7 +109,7 @@ export class IconDirective {
if (!this.name) return;
// add the css class to show the icon font
- this._renderer.setElementClass(this._elementRef, this.name, true);
+ this.renderer.setElementClass(this._elementRef, this.name, true);
// hide the icon when it's within a button
// and the button isn't an icon only button
diff --git a/ionic/components/radio/radio.scss b/ionic/components/radio/radio.scss
index bdca6570ac..07fd5f1456 100644
--- a/ionic/components/radio/radio.scss
+++ b/ionic/components/radio/radio.scss
@@ -3,26 +3,10 @@
// --------------------------------------------------
.radio {
- position: relative;
cursor: pointer;
@include user-select-none();
}
-.radio input {
- position: absolute;
- width: 0;
- height: 0;
- margin: 0;
- padding: 0;
- opacity: 0;
- border: none;
- @include appearance(none);
-}
-
-.radio .input-label {
- max-width: 100%;
-}
-
.radio[aria-disabled=true] {
opacity: 0.5;
color: gray;
diff --git a/ionic/components/switch/extensions/ios.scss b/ionic/components/switch/extensions/ios.scss
index fab87b26ad..107ba4a4d6 100644
--- a/ionic/components/switch/extensions/ios.scss
+++ b/ionic/components/switch/extensions/ios.scss
@@ -3,7 +3,7 @@
// --------------------------------------------------
$switch-ios-width: 51px !default;
-$switch-ios-height: 31px !default;
+$switch-ios-height: 32px !default;
$switch-ios-border-width: 2px !default;
$switch-ios-border-radius: 30px !default;
@@ -22,57 +22,72 @@ $switch-ios-handle-box-shadow: 0 3px 12px rgba(0, 0, 0, 0.16), 0 3px 1px
$switch-ios-handle-off-bg-color: #fff !default;
$switch-ios-handle-on-bg-color: #fff !default;
-$switch-ios-hit-area-expansion: 5px !default;
+$switch-ios-transition-duration: 200ms !default;
.switch[mode="ios"] {
- .media-switch {
- margin-top: 5px;
- margin-bottom: 5px;
+ .item-media {
+ margin: 0;
+ padding: $item-ios-padding-media-top $item-ios-padding-right $item-ios-padding-media-bottom $item-ios-padding-left;
}
- .switch-track {
- /*@include transition-timing-function(ease-in-out);
- @include transition-duration($switch-ios-transition-duration);
- @include transition-property((background-color, border));*/
+ .switch-icon {
position: relative;
width: $switch-ios-width;
height: $switch-ios-height;
+ border-radius: $switch-ios-border-radius;
+ pointer-events: none;
+ background-color: $switch-ios-off-bg-color;
+ transition: background-color $switch-ios-transition-duration;
+ }
+
+ .switch-track {
+ background-color: $switch-ios-off-bg-color;
border: solid $switch-ios-border-width $switch-ios-off-border-color;
border-radius: $switch-ios-border-radius;
- background-color: $switch-ios-off-bg-color;
- content: ' ';
- cursor: pointer;
- pointer-events: none;
+ width: 100%;
+ height: 100%;
+
+ will-change: border-width, opacity;
+ transition-property: border-width, opacity;
+ transition-duration: $switch-ios-transition-duration + 50ms;
+ transition-delay: 100ms;
}
.switch-handle {
- //@include transition($switch-ios-transition-duration cubic-bezier(0, 1.1, 1, 1.1));
- //@include transition-property((background-color, transform));
-
position: absolute;
+ top: $switch-ios-border-width;
+ left: $switch-ios-border-width;
width: $switch-ios-handle-width;
height: $switch-ios-handle-height;
+
border-radius: $switch-ios-handle-radius;
background-color: $switch-ios-handle-off-bg-color;
- top: 0;
- left: 0;
box-shadow: $switch-ios-handle-box-shadow;
- &:before {
- position: absolute;
- top: -4px;
- left: ( ($switch-ios-handle-width / 2) * -1) - 8;
- padding: ($switch-ios-handle-height / 2) + 5 ($switch-ios-handle-width + 7);
- content: '';
- }
+ will-change: width, left, transform;
+
+ transition-property: width, left, transform;
+ transition-duration: $switch-ios-transition-duration;
+ }
+
+ .activated .switch-handle {
+ width: $switch-ios-handle-width + 6;
+ }
+
+ &[aria-checked=true] .switch-icon {
+ background-color: $switch-ios-on-bg-color;
+ }
+
+ .activated .switch-track,
+ &[aria-checked=true] .switch-track {
+ border-width: $switch-ios-height / 2;
}
&[aria-checked=true] .switch-track {
- background-color: $switch-ios-on-bg-color;
- border-color: $switch-ios-on-border-color;
+ opacity: 0;
}
&[aria-checked=true] .switch-handle {
@@ -80,8 +95,8 @@ $switch-ios-hit-area-expansion: 5px !default;
transform: translate3d($switch-ios-width - $switch-ios-handle-width - ($switch-ios-border-width * 2), 0, 0);
}
- .input-label {
- color: inherit;
+ &[aria-checked=true] .activated .switch-handle {
+ left: $switch-ios-border-width - 6;
}
}
diff --git a/ionic/components/switch/switch.scss b/ionic/components/switch/switch.scss
index 4a418bc61c..5d83cd53d0 100644
--- a/ionic/components/switch/switch.scss
+++ b/ionic/components/switch/switch.scss
@@ -4,27 +4,16 @@
.switch {
- position: relative;
- cursor: pointer;
@include user-select-none();
}
-.switch input {
- position: absolute;
- width: 0;
- height: 0;
+.switch .media-switch {
+ cursor: pointer;
margin: 0;
- padding: 0;
- opacity: 0;
- border: none;
- @include appearance(none);
-}
-
-.switch .input-label {
- max-width: 100%;
}
.switch[aria-disabled=true] {
+ pointer-events: none;
opacity: 0.5;
color: gray;
}
diff --git a/ionic/components/switch/switch.ts b/ionic/components/switch/switch.ts
index 1a9275efdf..a91872af6d 100644
--- a/ionic/components/switch/switch.ts
+++ b/ionic/components/switch/switch.ts
@@ -2,23 +2,87 @@ import {
View,
Directive,
ElementRef,
- Renderer,
+ Ancestor,
Optional,
- NgControl
+ NgControl,
+ Renderer,
+ Inject,
+ forwardRef
} from 'angular2/angular2';
import {Ion} from '../ion';
import {IonInputItem} from '../form/input';
import {IonicConfig} from '../../config/config';
import {IonicComponent, IonicView} from '../../config/annotations';
-import {Icon} from '../icon/icon';
+import * as dom from '../../util/dom';
+
+
+@Directive({
+ selector: '.media-switch'
+})
+class MediaSwitch {
+ constructor(
+ @Ancestor() @Inject(forwardRef(() => Switch)) swtch: Switch,
+ elementRef: ElementRef,
+ renderer: Renderer,
+ config: IonicConfig
+ ) {
+ let element = elementRef.nativeElement;
+ let touchEnabled = config.setting('touchEnabled');
+ let startCoord = null;
+
+ function pointerDown(ev) {
+ startCoord = dom.pointerCoord(ev);
+ renderer.setElementClass(elementRef, ACTIVATED, true);
+ element.removeEventListener(touchEnabled ? TOUCHMOVE : MOUSEMOVE, pointerMove);
+ element.addEventListener(touchEnabled ? TOUCHMOVE : MOUSEMOVE, pointerMove);
+ }
+
+ function pointerMove(ev) {
+ let moveCoord = dom.pointerCoord(ev);
+ console.log('pointerMove', moveCoord);
+ }
+
+ function pointerUp(ev) {
+ let endCoord = dom.pointerCoord(ev);
+ renderer.setElementClass(elementRef, ACTIVATED, false);
+ element.removeEventListener(touchEnabled ? TOUCHMOVE : MOUSEMOVE, pointerMove);
+
+ swtch.toggle();
+ }
+
+ element.addEventListener(touchEnabled ? TOUCHSTART : MOUSEDOWN, pointerDown);
+ element.addEventListener(touchEnabled ? TOUCHEND : MOUSEUP, pointerUp);
+
+ this.dereg = function() {
+ element.removeEventListener(touchEnabled ? TOUCHSTART : MOUSEDOWN, pointerDown);
+ element.removeEventListener(touchEnabled ? TOUCHEND : MOUSEUP, pointerUp);
+ element.removeEventListener(touchEnabled ? TOUCHMOVE : MOUSEMOVE, pointerMove);
+ }
+ }
+
+ onDestroy() {
+ this.dereg();
+ }
+
+}
@IonicComponent({
selector: 'ion-switch',
+ properties: [
+ 'value',
+ 'checked',
+ 'disabled',
+ 'id'
+ ],
host: {
'class': 'item',
- //'[attr.aria-checked]': 'input.checked'
+ 'role': 'checkbox',
+ '[attr.tab-index]': 'tabIndex',
+ '[attr.aria-checked]': 'checked',
+ '[attr.aria-disabled]': 'disabled',
+ '[attr.aria-labelledby]': 'labelId'
}
})
@IonicView({
@@ -27,95 +91,50 @@ import {Icon} from '../icon/icon';
'' +
'' +
'
',
+ directives: [MediaSwitch]
})
export class Switch extends IonInputItem {
constructor(
- @Optional() cd: NgControl,
- renderer: Renderer,
elementRef: ElementRef,
- config: IonicConfig
+ config: IonicConfig,
+ @Optional() private cd: NgControl
) {
super(elementRef, config);
+ this.tabIndex = 0;
+
this.onChange = (_) => {};
this.onTouched = (_) => {};
- this.renderer = renderer;
- this.elementRef = elementRef;
- this.cd = cd;
- if(cd) cd.valueAccessor = this;
+ if (cd) cd.valueAccessor = this;
}
onInit() {
super.onInit();
- console.log("switch onInit")
+ this.labelId = 'label-' + this.id;
}
- onAllChangesDone() {
- return
- console.log("switch onAllChangesDone")
- if (this._checked !== void 0 && this.input.checked != this._checked) {
- if (this.input.checked !== void 0) {
- console.warn("switch checked is set in view template and Control declaration.\n" +
- "Value: " + !!this._checked + " from Control takes precedence");
- }
- this.input.checked = !!this._checked;
- }
- if (this._value !== void 0 && this.input.value != this._value) {
- if (this.input.value !== void 0) {
- console.warn("switch value is set in view template and Control declaration.\n" +
- "Value: " + this._value + " from Control takes precedence");
- }
- this.input.value = this._value;
- }
- if (this.input.value === void 0) {
- this.input.value = "on";
- }
- if (this.input.checked === void 0) {
- this.input.checked = false;
- }
- //TODO check validity
- this.cd.control._value = {"checked": !!this.input.checked, "value": this.input.value};
-
- //TODO only want to call this once, we want to set input.checked directly on subsequent
- // writeValue's
- this.onAllChangesDone = () => {};
- // this.onChange({"checked": this.input.checked, "value": this.input.value});
+ check(value) {
+ this.checked = !!value;
+ this.onChange(this.checked);
}
- //from clicking the label or selecting with keyboard
- //view -> model (Control)
toggle() {
- this.input.checked = this._checked = !this.input.checked;
- this.onChange({"checked": this.input.checked, "value": this.input.value});
+ this.check(!this.checked);
}
- // Called by the model (Control) to update the view
- writeValue(modelValue) {
- let type = typeof modelValue;
- switch (type) {
- case "boolean":
- // don't set input.value here, do it in onAllChangesDone
- // because they might have set it in the view
- this._checked = modelValue; break;
- case "object":
- if (modelValue.checked !== void 0) this._checked = !!modelValue.checked;
- if (modelValue.value !== void 0) this._value = modelValue.value.toString();
- break;
- default:
- // don't set input.checked here, do it in onAllChangesDone
- // because they might have set it in the view
- this._value = modelValue.toString();
- }
+ click(ev) {
+ ev.preventDefault();
+ ev.stopPropagation();
+ this.toggle();
+ }
- //TODO we want to set input.checked directly after the first time
- console.log("writeValue, " + this.input.id + " checked: " + this._checked);
- console.log("writeValue " + this.input.id + " value: " + this._value);
-
- // this.cd.control._value = {"checked": this.input.checked, "value": this.input.value};
+ writeValue(value) {
+ this.checked = value;
}
// Used by the view to update the model (Control)
@@ -124,3 +143,11 @@ export class Switch extends IonInputItem {
registerOnTouched(fn) { this.onTouched = fn; }
}
+
+const ACTIVATED = 'activated';
+const MOUSEDOWN = 'mousedown';
+const MOUSEMOVE = 'mousemove';
+const MOUSEUP = 'mouseup';
+const TOUCHSTART = 'touchstart';
+const TOUCHMOVE = 'touchmove';
+const TOUCHEND = 'touchend';
diff --git a/ionic/components/switch/test/basic/e2e.ts b/ionic/components/switch/test/basic/e2e.ts
index e69de29bb2..d666104663 100644
--- a/ionic/components/switch/test/basic/e2e.ts
+++ b/ionic/components/switch/test/basic/e2e.ts
@@ -0,0 +1,11 @@
+
+it('should check apple via checkbox element click', function() {
+ element(by.css('#e2eAppleCheckbox')).click();
+});
+
+
+it('should enable/check grape via buttons and submit form', function() {
+ element(by.css('#e2eGrapeDisabled')).click();
+ element(by.css('#e2eGrapeChecked')).click();
+ element(by.css('#e2eSubmit')).click();
+});
diff --git a/ionic/components/switch/test/basic/index.ts b/ionic/components/switch/test/basic/index.ts
index e906bafacf..40e35c35b2 100644
--- a/ionic/components/switch/test/basic/index.ts
+++ b/ionic/components/switch/test/basic/index.ts
@@ -18,15 +18,27 @@ import {
class IonicApp {
constructor() {
this.fruitsForm = new ControlGroup({
- "appleCtrl": new Control({"checked": false, "value": "apple"}),
+ "appleCtrl": new Control(),
"bananaCtrl": new Control(true),
- "cherryCtrl": new Control({"checked": false, "value": 12}),
- "grapeCtrl": new Control("grape")
+ "cherryCtrl": new Control(false),
+ "grapeCtrl": new Control(true)
});
+
+ this.grapeDisabled = true;
+ this.grapeChecked = true;
+ }
+
+ toggleGrapeChecked() {
+ this.grapeChecked = !this.grapeChecked;
+ }
+
+ toggleGrapeDisabled() {
+ this.grapeDisabled = !this.grapeDisabled;
}
doSubmit(ev) {
console.log('Submitting form', this.fruitsForm.value);
- event.preventDefault();
+ this.formResults = JSON.stringify(this.fruitsForm.value);
+ ev.preventDefault();
}
}
diff --git a/ionic/components/switch/test/basic/main.html b/ionic/components/switch/test/basic/main.html
index 494af7bb30..2f9fbaa6fa 100644
--- a/ionic/components/switch/test/basic/main.html
+++ b/ionic/components/switch/test/basic/main.html
@@ -4,32 +4,95 @@
-
+
+
+
+
+
+
+
+ appleCtrl.dirty: {{fruitsForm.controls.appleCtrl.dirty}}
+ appleCtrl.value: {{fruitsForm.controls.appleCtrl.value}}
+ bananaCtrl.dirty: {{fruitsForm.controls.bananaCtrl.dirty}}
+ bananaCtrl.value: {{fruitsForm.controls.bananaCtrl.value}}
+ cherry.dirty: {{fruitsForm.controls.cherryCtrl.dirty}}
+ cherry.value: {{fruitsForm.controls.cherryCtrl.value}}
+ grape.dirty: {{fruitsForm.controls.grapeCtrl.dirty}}
+ grape.value: {{fruitsForm.controls.grapeCtrl.value}}
+
+
+ {{formResults}}
+
diff --git a/ionic/platform/registry.ts b/ionic/platform/registry.ts
index 0b43d39b6a..fa7bf733e1 100644
--- a/ionic/platform/registry.ts
+++ b/ionic/platform/registry.ts
@@ -63,7 +63,8 @@ Platform.register({
mdRipple: true,
tabBarPlacement: 'top',
navTitleAlign: 'left',
- viewTransition: 'md'
+ viewTransition: 'md',
+ touchEnabled: true
},
isMatch(p) {
// "silk" is kindle fire
@@ -94,6 +95,7 @@ Platform.register({
// and not just an a spoofed user-agent string
return /iphone|ipad|ipod/i.test(Platform.navigatorPlatform());
},
+ touchEnabled: true,
keyboardScrollAssist: true,
viewTransition: 'ios',
navTitleAlign: 'center',