Skip to content

Commit 92a56cf

Browse files
committed
feat(): add slide-toggle component.
1 parent dfe683b commit 92a56cf

File tree

11 files changed

+456
-0
lines changed

11 files changed

+456
-0
lines changed

src/components/slide-toggle/README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# MdSlideToggle
2+
`MdSlideToggle` is a two-state control, which can be also called `switch`
3+
4+
### Screenshots
5+
![image](https://cloud.githubusercontent.com/assets/4987015/14860895/25cc0dc0-0cab-11e6-9e57-9f6d513444b1.png)
6+
7+
## `<md-slide-toggle>`
8+
### Bound Properties
9+
10+
| Name | Type | Description |
11+
| --- | --- | --- |
12+
| `disabled` | boolean | Disables the `slide-toggle` |
13+
| `color` | `"primary" | "accent" | "warn"` | The color palette of `the slide-toggle` |
14+
15+
### Examples
16+
A basic slide-toggle would have the following markup.
17+
```html
18+
<md-slide-toggle [(ngModel)]="slideToggleModel">
19+
Default Slide Toggle
20+
</md-slide-toggle>
21+
```
22+
23+
Slide toggle can be also disabled.
24+
```html
25+
<md-slide-toggle disabled>
26+
Disabled Slide Toggle
27+
</md-slide-toggle>
28+
```
29+
30+
## Theming
31+
A slide-toggle is default using the `accent` palette for its styling.
32+
33+
Modifiying the color on a `slide-toggle` can be easily done, by using the following classes.
34+
- `md-primary`
35+
- `md-warn`
36+
37+
Here is an example markup, which uses the primary color.
38+
```html
39+
<md-slide-toggle class="md-primary">
40+
Primary Slide Toggle
41+
</md-slide-toggle>
42+
```
43+
44+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<div class="md-slide-toggle-container">
2+
<div class="md-slide-toggle-bar"></div>
3+
<div class="md-slide-toggle-thumb-container">
4+
<div class="md-slide-toggle-thumb"></div>
5+
</div>
6+
</div>
7+
<div class="md-slide-toggle-label">
8+
<ng-content></ng-content>
9+
</div>
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
@import "../../core/style/variables";
2+
@import "../../core/style/mixins";
3+
@import "../../core/style/elevation";
4+
5+
//TODO(): remove the default theme.
6+
@import "../../core/style/default-theme";
7+
8+
$md-slide-toggle-width: 36px !default;
9+
$md-slide-toggle-height: 24px !default;
10+
$md-slide-toggle-bar-height: 14px !default;
11+
$md-slide-toggle-thumb-size: 20px !default;
12+
$md-slide-toggle-margin: 16px !default;
13+
14+
@mixin md-switch-checked($palette) {
15+
.md-slide-toggle-thumb {
16+
background-color: md-color($palette);
17+
}
18+
19+
.md-slide-toggle-bar {
20+
background-color: md-color($palette, 0.5);
21+
}
22+
}
23+
24+
:host {
25+
display: flex;
26+
height: $md-slide-toggle-height;
27+
28+
margin: $md-slide-toggle-margin 0;
29+
line-height: $md-slide-toggle-height;
30+
31+
white-space: nowrap;
32+
cursor: pointer;
33+
user-select: none;
34+
35+
outline: none;
36+
37+
&.md-checked {
38+
@include md-switch-checked($md-accent);
39+
40+
&.md-primary {
41+
@include md-switch-checked($md-primary);
42+
}
43+
44+
&.md-warn {
45+
@include md-switch-checked($md-warn);
46+
}
47+
48+
.md-slide-toggle-thumb-container {
49+
transform: translate3d(100%, 0, 0);
50+
}
51+
}
52+
53+
&[disabled] {
54+
cursor: default;
55+
56+
.md-slide-toggle-container {
57+
cursor: default;
58+
}
59+
60+
.md-slide-toggle-thumb {
61+
background-color: md-color($md-grey, 400);
62+
}
63+
.md-slide-toggle-bar {
64+
background-color: md-color($md-foreground, divider);
65+
}
66+
}
67+
}
68+
69+
// Root container for the composition of the slide-toggle / switch indicator.
70+
.md-slide-toggle-container {
71+
cursor: grab;
72+
width: $md-slide-toggle-width;
73+
height: $md-slide-toggle-height;
74+
75+
position: relative;
76+
user-select: none;
77+
78+
margin-right: 8px;
79+
}
80+
81+
// The thumb container is responsible for the dragging functionality.
82+
// It moves around and holds the actual circle as a thumb.
83+
.md-slide-toggle-thumb-container {
84+
position: absolute;
85+
top: $md-slide-toggle-height / 2 - $md-slide-toggle-thumb-size / 2;
86+
left: 0;
87+
z-index: 1;
88+
89+
width: $md-slide-toggle-width - $md-slide-toggle-thumb-size;
90+
91+
transform: translate3d(0, 0, 0);
92+
93+
transition: $swift-linear;
94+
transition-property: transform;
95+
}
96+
97+
// The thumb will be elevated from the slide-toggle bar.
98+
// Also the thumb is bound to its parent thumb-container, which manages the movement of the thumb.
99+
.md-slide-toggle-thumb {
100+
position: absolute;
101+
margin: 0;
102+
left: 0;
103+
top: 0;
104+
105+
height: $md-slide-toggle-thumb-size;
106+
width: $md-slide-toggle-thumb-size;
107+
border-radius: 50%;
108+
109+
background-color: md-color($md-background, background);
110+
@include md-elevation(1);
111+
}
112+
113+
// Horizontal bar for the slide-toggle.
114+
// The slide-toggle bar is shown behind the thumb container.
115+
.md-slide-toggle-bar {
116+
position: absolute;
117+
left: 1px;
118+
top: $md-slide-toggle-height / 2 - $md-slide-toggle-bar-height / 2;
119+
120+
width: $md-slide-toggle-width - 2px;
121+
height: $md-slide-toggle-bar-height;
122+
123+
background-color: md-color($md-grey, 500);
124+
125+
border-radius: 8px;
126+
}
127+
128+
.md-slide-toggle-bar,
129+
.md-slide-toggle-thumb {
130+
transition: $swift-linear;
131+
transition-property: background-color;
132+
transition-delay: 0.05s;
133+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import {
2+
it,
3+
describe,
4+
expect,
5+
beforeEach,
6+
inject,
7+
async
8+
} from '@angular/core/testing';
9+
import {TestComponentBuilder} from '@angular/compiler/testing';
10+
import {By} from '@angular/platform-browser';
11+
import {Component} from '@angular/core';
12+
import {MdSlideToggle} from './slide-toggle';
13+
14+
export function main() {
15+
describe('MdSlideToggle', () => {
16+
let builder: TestComponentBuilder;
17+
18+
beforeEach(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
19+
builder = tcb;
20+
}));
21+
22+
it('should update the model correctly', async(() => {
23+
return builder.createAsync(SlideToggleTestApp).then((fixture) => {
24+
let testComponent = fixture.debugElement.componentInstance;
25+
let slideToggleEl = fixture.debugElement.query(By.css('md-slide-toggle')).nativeElement;
26+
27+
fixture.detectChanges();
28+
29+
expect(slideToggleEl.classList).not.toContain('md-checked');
30+
31+
testComponent.slideModel = true;
32+
fixture.detectChanges();
33+
34+
expect(slideToggleEl.classList).toContain('md-checked');
35+
});
36+
}));
37+
38+
it('should apply class based on color attribute', async(() => {
39+
return builder.createAsync(SlideToggleTestApp).then(fixture => {
40+
let testComponent = fixture.debugElement.componentInstance;
41+
let slideToggleEl = fixture.debugElement.query(By.css('md-slide-toggle')).nativeElement;
42+
43+
testComponent.slideColor = 'primary';
44+
fixture.detectChanges();
45+
46+
expect(slideToggleEl.classList).toContain('md-primary');
47+
48+
testComponent.slideColor = 'accent';
49+
fixture.detectChanges();
50+
51+
expect(slideToggleEl.classList).toContain('md-accent');
52+
});
53+
}));
54+
55+
it('should correctly update the disabled attribute', async(() => {
56+
return builder.createAsync(SlideToggleTestApp).then((fixture) => {
57+
let testComponent = fixture.debugElement.componentInstance;
58+
let slideToggleEl = fixture.debugElement.query(By.css('md-slide-toggle')).nativeElement;
59+
60+
fixture.detectChanges();
61+
62+
expect(slideToggleEl.getAttribute('disabled')).toBeFalsy();
63+
64+
testComponent.isDisabled = true;
65+
fixture.detectChanges();
66+
67+
expect(slideToggleEl.getAttribute('disabled')).toBeTruthy();
68+
});
69+
}));
70+
71+
it('should correctly update aria-disabled', async(() => {
72+
return builder.createAsync(SlideToggleTestApp).then((fixture) => {
73+
let testComponent = fixture.debugElement.componentInstance;
74+
let slideToggleEl = fixture.debugElement.query(By.css('md-slide-toggle')).nativeElement;
75+
76+
fixture.detectChanges();
77+
78+
expect(slideToggleEl.getAttribute('aria-disabled')).toBe('false');
79+
80+
testComponent.isDisabled = true;
81+
fixture.detectChanges();
82+
83+
expect(slideToggleEl.getAttribute('aria-disabled')).toBe('true');
84+
});
85+
}));
86+
87+
it('should correctly update aria-checked', async(() => {
88+
return builder.createAsync(SlideToggleTestApp).then((fixture) => {
89+
let testComponent = fixture.debugElement.componentInstance;
90+
let slideToggleEl = fixture.debugElement.query(By.css('md-slide-toggle')).nativeElement;
91+
92+
fixture.detectChanges();
93+
94+
expect(slideToggleEl.getAttribute('aria-checked')).toBe('false');
95+
96+
testComponent.slideModel = true;
97+
fixture.detectChanges();
98+
99+
expect(slideToggleEl.getAttribute('aria-checked')).toBe('true');
100+
});
101+
}));
102+
103+
it('should set the toggle to checked on click', async(() => {
104+
return builder.createAsync(SlideToggleTestApp).then((fixture) => {
105+
let slideToggle = fixture.debugElement.query(By.css('md-slide-toggle')).componentInstance;
106+
let slideToggleEl = fixture.debugElement.query(By.css('md-slide-toggle')).nativeElement;
107+
108+
fixture.detectChanges();
109+
110+
expect(slideToggle.checked).toBe(false);
111+
expect(slideToggleEl.classList).not.toContain('md-checked');
112+
113+
slideToggleEl.click();
114+
fixture.detectChanges();
115+
116+
expect(slideToggleEl.classList).toContain('md-checked');
117+
expect(slideToggle.checked).toBe(true);
118+
});
119+
}));
120+
});
121+
}
122+
123+
@Component({
124+
selector: 'slide-toggle-test-app',
125+
template: `
126+
<md-slide-toggle [(ngModel)]="slideModel" [disabled]="isDisabled" [color]="slideColor">
127+
<span>Test Slide Toggle</span>
128+
</md-slide-toggle>
129+
`,
130+
directives: [MdSlideToggle]
131+
})
132+
class SlideToggleTestApp {
133+
isDisabled: boolean = false;
134+
slideModel: boolean = false;
135+
}

0 commit comments

Comments
 (0)