Skip to content

Commit 86c6bf8

Browse files
committed
feat(switch): add switch component and drag service
- Improved Dragging as in Material 1, due Outter Drag - NgModel Support / NgControl - RTL Ported from Material 1
1 parent 249996b commit 86c6bf8

File tree

10 files changed

+492
-2
lines changed

10 files changed

+492
-2
lines changed

ember-cli-build.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ module.exports = function(defaults) {
1616
return mergeTrees([
1717
angularAppTree.toTree(),
1818
componentCssTree,
19-
demoAppCssTree,
19+
demoAppCssTree
2020
]);
2121
};
2222

src/components/switch/switch.html

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<div class="md-container"><div class="md-bar"></div>
2+
<div class="md-thumb-container">
3+
<div class="md-thumb"></div>
4+
</div>
5+
</div>
6+
<div class="md-label">
7+
<ng-content></ng-content>
8+
</div>

src/components/switch/switch.scss

+220
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
@import "variables";
2+
@import "shadows";
3+
@import "mixins";
4+
5+
//TODO Temporary Theme
6+
@import "default-theme";
7+
8+
$switch-width: 36px !default;
9+
$switch-height: 8px * 3 !default;
10+
$switch-bar-height: 14px !default;
11+
$switch-thumb-size: 20px !default;
12+
$switch-margin: 16px !default;
13+
14+
.md-inline-form.md-switch {
15+
margin-top: 18px;
16+
margin-bottom: 19px;
17+
}
18+
19+
md-switch {
20+
margin: $switch-margin 0;
21+
white-space: nowrap;
22+
cursor: pointer;
23+
outline: none;
24+
user-select: none;
25+
height: 30px;
26+
line-height: 28px;
27+
align-items: center;
28+
display: flex;
29+
30+
@include rtl(margin-left, inherit, $switch-margin);
31+
@include rtl(margin-right, $switch-margin, inherit);
32+
33+
&:last-of-type {
34+
@include rtl(margin-left, inherit, 0);
35+
@include rtl(margin-right, 0, inherit);
36+
}
37+
38+
&[disabled] {
39+
cursor: default;
40+
41+
.md-container {
42+
cursor: default;
43+
}
44+
}
45+
46+
.md-container {
47+
cursor: grab;
48+
width: $switch-width;
49+
height: $switch-height;
50+
position: relative;
51+
user-select: none;
52+
margin-right: 8px;
53+
float: left;
54+
}
55+
56+
&:not([disabled]) {
57+
.md-dragging,
58+
&.md-dragging .md-container {
59+
cursor: grabbing;
60+
}
61+
}
62+
63+
&.md-focused:not([disabled]) {
64+
.md-thumb:before {
65+
left: -8px;
66+
top: -8px;
67+
right: -8px;
68+
bottom: -8px;
69+
}
70+
71+
&:not(.md-checked).md-thumb:before {
72+
background-color: rgba(0, 0, 0, 0.12);
73+
}
74+
}
75+
76+
.md-label {
77+
border: 0 transparent;
78+
float: left;
79+
}
80+
81+
.md-bar {
82+
left: 1px;
83+
width: $switch-width - 2px;
84+
top: $switch-height / 2 - $switch-bar-height / 2;
85+
height: $switch-bar-height;
86+
border-radius: 8px;
87+
position: absolute;
88+
}
89+
90+
.md-thumb-container {
91+
top: $switch-height / 2 - $switch-thumb-size / 2;
92+
left: 0;
93+
width: $switch-width - $switch-thumb-size;
94+
position: absolute;
95+
transform: translate3d(0,0,0);
96+
z-index: 1;
97+
}
98+
&.md-checked .md-thumb-container {
99+
transform: translate3d(100%,0,0);
100+
}
101+
102+
.md-thumb {
103+
position: absolute;
104+
margin: 0;
105+
left: 0;
106+
top: 0;
107+
outline: none;
108+
height: $switch-thumb-size;
109+
width: $switch-thumb-size;
110+
border-radius: 50%;
111+
box-shadow: $md-shadow-bottom-z-1;
112+
113+
&:before {
114+
background-color: transparent;
115+
border-radius: 50%;
116+
content: '';
117+
position: absolute;
118+
display: block;
119+
height: auto;
120+
left: 0;
121+
top: 0;
122+
right: 0;
123+
bottom: 0;
124+
transition: all 0.5s;
125+
width: auto;
126+
}
127+
}
128+
129+
&:not(.md-dragging) {
130+
.md-bar,
131+
.md-thumb-container,
132+
.md-thumb {
133+
transition: $swift-linear;
134+
transition-property: transform, background-color;
135+
}
136+
137+
.md-bar,
138+
.md-thumb {
139+
transition-delay: 0.05s;
140+
}
141+
}
142+
143+
// COLOR THEMING
144+
.md-thumb {
145+
background-color: md-color($md-background, 50);
146+
}
147+
.md-bar {
148+
background-color: md-color($md-background, 500);
149+
}
150+
151+
&.md-checked {
152+
.md-ink-ripple {
153+
color: md-color($md-accent);
154+
}
155+
156+
.md-thumb {
157+
background-color: md-color($md-accent);
158+
}
159+
160+
.md-bar {
161+
background-color: md-color($md-accent, 0.5);
162+
}
163+
164+
&.md-focused .md-thumb:before {
165+
background-color: md-color($md-accent, 0.26);
166+
}
167+
168+
&.md-primary {
169+
.md-ink-ripple {
170+
color: md-color($md-primary);
171+
}
172+
.md-thumb {
173+
background-color: md-color($md-primary);
174+
}
175+
.md-bar {
176+
background-color: md-color($md-primary, 0.5);
177+
}
178+
&.md-focused .md-thumb:before {
179+
background-color: md-color($md-primary, 0.26);
180+
}
181+
}
182+
183+
&.md-warn {
184+
.md-ink-ripple {
185+
color: md-color($md-warn);
186+
}
187+
.md-thumb {
188+
background-color: md-color($md-warn);
189+
}
190+
.md-bar {
191+
background-color: md-color($md-warn, 0.5);
192+
}
193+
&.md-focused .md-thumb:before {
194+
background-color: md-color($md-warn, 0.26);
195+
}
196+
}
197+
}
198+
199+
&[disabled] {
200+
.md-thumb {
201+
background-color: md-color($md-background, 400);
202+
}
203+
.md-bar {
204+
background-color: md-color($md-foreground, 'divider');
205+
}
206+
}
207+
}
208+
209+
@media screen and (-ms-high-contrast: active) {
210+
md-switch.md-default-theme .md-bar {
211+
background-color: #666;
212+
}
213+
md-switch.md-default-theme.md-checked .md-bar {
214+
background-color: #9E9E9E;
215+
}
216+
md-switch.md-default-theme .md-thumb {
217+
background-color: #fff;
218+
}
219+
}
220+

src/components/switch/switch.ts

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import {Component, ViewEncapsulation, ElementRef} from 'angular2/core';
2+
import {MdDrag} from '../../core/services/drag/drag';
3+
import {ControlValueAccessor} from "angular2/common";
4+
import {NgControl} from "angular2/common";
5+
import {Optional} from "angular2/core";
6+
7+
@Component({
8+
selector: 'md-switch',
9+
host: {
10+
'(click)': 'onClick()'
11+
},
12+
templateUrl: './components/switch/switch.html',
13+
styleUrls: ['./components/switch/switch.css'],
14+
encapsulation: ViewEncapsulation.None,
15+
})
16+
export class MdSwitch implements ControlValueAccessor {
17+
18+
elementRef: ElementRef;
19+
componentElement: HTMLElement;
20+
switchContainer: HTMLElement;
21+
thumbContainer: HTMLElement;
22+
23+
dragData: any;
24+
dragClick = false;
25+
26+
// Accessor Values
27+
onChange = (_:any) => {};
28+
onTouched = () => {};
29+
30+
// Model Values
31+
value: boolean;
32+
33+
constructor(private _elementRef: ElementRef, @Optional() ngControl: NgControl) {
34+
this.elementRef = _elementRef;
35+
36+
if (ngControl) {
37+
ngControl.valueAccessor = this;
38+
}
39+
}
40+
41+
ngOnInit() {
42+
this.componentElement = this.elementRef.nativeElement;
43+
this.switchContainer = <HTMLElement> this.componentElement.querySelector('.md-container');
44+
this.thumbContainer = <HTMLElement> this.componentElement.querySelector('.md-thumb-container');
45+
46+
MdDrag.register(this.switchContainer);
47+
48+
this.switchContainer.addEventListener('$md.dragstart', (ev: CustomEvent) => this.onDragStart(ev));
49+
this.switchContainer.addEventListener('$md.drag', (ev: CustomEvent) => this.onDrag(ev));
50+
this.switchContainer.addEventListener('$md.dragend', (ev: CustomEvent) => this.onDragEnd(ev));
51+
}
52+
53+
54+
onDragStart(event: CustomEvent) {
55+
this.componentElement.classList.add('md-dragging');
56+
57+
this.dragData = {
58+
width: this.thumbContainer.offsetWidth
59+
};
60+
61+
this.componentElement.classList.remove('transition')
62+
}
63+
64+
onDrag(event: CustomEvent) {
65+
let percent = event.detail.pointer.distanceX / this.dragData.width;
66+
67+
let translate = this.value ? 1 + percent : percent;
68+
translate = Math.max(0, Math.min(1, translate));
69+
70+
this.thumbContainer.style.transform = 'translate3d(' + (100 * translate) + '%,0,0)';
71+
this.dragData.translate = translate;
72+
}
73+
74+
onDragEnd(event: CustomEvent) {
75+
this.componentElement.classList.remove('md-dragging');
76+
this.thumbContainer.style.transform = null;
77+
78+
79+
var isChanged = this.value ? this.dragData.translate < 0.5 : this.dragData.translate > 0.5;
80+
if (isChanged || !this.dragData.translate) {
81+
this.changeValue(!this.value);
82+
}
83+
84+
this.dragData = null;
85+
86+
// Wait for incoming mouseup click
87+
this.dragClick = true;
88+
setTimeout(() => this.dragClick = false, 1);
89+
}
90+
91+
onClick() {
92+
if (!this.dragClick) this.changeValue(!this.value);
93+
}
94+
95+
changeValue(newValue: boolean) {
96+
this.onChange(newValue);
97+
this.writeValue(newValue);
98+
}
99+
100+
writeValue(value: any): void {
101+
this.value = !!value;
102+
103+
// Apply Checked Class
104+
this.componentElement.classList.toggle('md-checked', this.value);
105+
}
106+
107+
registerOnChange(fn: any): void {
108+
this.onChange = fn;
109+
}
110+
111+
registerOnTouched(fn: any): void {
112+
this.onTouched = fn;
113+
}
114+
}

0 commit comments

Comments
 (0)