Skip to content

Commit dd03ff2

Browse files
committed
feat: add simplified checkbox component for usage in other components
Adds the `md-pseudo-checkbox`, which is a simplified version of `md-checkbox`, that doesn't have the expensive SVG animations or the form control logic. This will be useful for multiple selection in `md-select`, as well as other components in the future. Relates to #2412.
1 parent 651440f commit dd03ff2

File tree

9 files changed

+198
-26
lines changed

9 files changed

+198
-26
lines changed

src/lib/checkbox/checkbox.scss

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,21 @@
11
@import '../core/theming/theming';
22
@import '../core/style/elevation';
3-
@import '../core/style/variables';
3+
@import '../core/style/checkbox-variables';
44
@import '../core/ripple/ripple';
55

6-
7-
// The width/height of the checkbox element.
8-
$md-checkbox-size: $md-toggle-size !default;
9-
// The width of the line used to draw the checkmark / mixedmark.
10-
$md-checkbox-mark-stroke-size: 2/15 * $md-checkbox-size !default;
11-
// The width of the checkbox border shown when the checkbox is unchecked.
12-
$md-checkbox-border-width: 2px;
13-
// The base duration used for the majority of transitions for the checkbox.
14-
$md-checkbox-transition-duration: 90ms;
15-
// The amount of spacing between the checkbox and its label.
16-
$md-checkbox-item-spacing: $md-toggle-padding;
17-
186
// Manual calculation done on SVG
197
$_md-checkbox-mark-path-length: 22.910259;
208
$_md-checkbox-indeterminate-checked-easing-function: cubic-bezier(0.14, 0, 0, 1);
219

2210
// The ripple size of the checkbox
23-
$md-checkbox-ripple-size: 15px;
11+
$_md-checkbox-ripple-size: 15px;
12+
13+
// The amount of spacing between the checkbox and its label.
14+
$_md-checkbox-item-spacing: $md-toggle-padding;
15+
16+
// The width of the line used to draw the checkmark / mixedmark.
17+
$_md-checkbox-mark-stroke-size: 2 / 15 * $md-checkbox-size !default;
18+
2419

2520
// Fades in the background of the checkbox when it goes from unchecked -> {checked,indeterminate}.
2621
@keyframes md-checkbox-fade-in-background {
@@ -213,7 +208,7 @@ md-checkbox {
213208
height: $md-checkbox-size;
214209
line-height: 0;
215210
margin: auto;
216-
margin-right: $md-checkbox-item-spacing;
211+
margin-right: $_md-checkbox-item-spacing;
217212
order: 0;
218213
position: relative;
219214
vertical-align: middle;
@@ -223,7 +218,7 @@ md-checkbox {
223218

224219
[dir='rtl'] & {
225220
margin: {
226-
left: $md-checkbox-item-spacing;
221+
left: $_md-checkbox-item-spacing;
227222
right: auto;
228223
}
229224
}
@@ -266,14 +261,14 @@ md-checkbox {
266261
stroke: {
267262
dashoffset: $_md-checkbox-mark-path-length;
268263
dasharray: $_md-checkbox-mark-path-length;
269-
width: $md-checkbox-mark-stroke-size;
264+
width: $_md-checkbox-mark-stroke-size;
270265
}
271266
}
272267

273268
.md-checkbox-mixedmark {
274269
@extend %md-checkbox-mark;
275270

276-
height: floor($md-checkbox-mark-stroke-size);
271+
height: floor($_md-checkbox-mark-stroke-size);
277272
opacity: 0;
278273
transform: scaleX(0) rotate(0deg);
279274
}
@@ -282,14 +277,14 @@ md-checkbox {
282277
.md-checkbox-inner-container {
283278
order: 1;
284279
margin: {
285-
left: $md-checkbox-item-spacing;
280+
left: $_md-checkbox-item-spacing;
286281
right: auto;
287282
}
288283

289284
[dir='rtl'] & {
290285
margin: {
291286
left: auto;
292-
right: $md-checkbox-item-spacing;
287+
right: $_md-checkbox-item-spacing;
293288
}
294289
}
295290
}
@@ -421,10 +416,10 @@ md-checkbox {
421416

422417
.md-checkbox-ripple {
423418
position: absolute;
424-
left: -$md-checkbox-ripple-size;
425-
top: -$md-checkbox-ripple-size;
426-
right: -$md-checkbox-ripple-size;
427-
bottom: -$md-checkbox-ripple-size;
419+
left: -$_md-checkbox-ripple-size;
420+
top: -$_md-checkbox-ripple-size;
421+
right: -$_md-checkbox-ripple-size;
422+
bottom: -$_md-checkbox-ripple-size;
428423
border-radius: 50%;
429424
z-index: 1;
430425
pointer-events: none;

src/lib/core/_core.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
@import 'ripple/ripple';
66
@import 'option/option';
77
@import 'option/option-theme';
8+
@import 'selection/pseudo-checkbox/pseudo-checkbox-theme';
89

910
// Mixin that renders all of the core styles that are not theme-dependent.
1011
@mixin md-core() {
@@ -27,6 +28,7 @@
2728
@mixin md-core-theme($theme) {
2829
@include md-ripple-theme($theme);
2930
@include md-option-theme($theme);
31+
@include md-pseudo-checkbox-theme($theme);
3032

3133
// Wrapper element that provides the theme background when the
3234
// user's content isn't inside of a `md-sidenav-container`.

src/lib/core/core.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {MdRippleModule} from './ripple/ripple';
77
import {PortalModule} from './portal/portal-directives';
88
import {OverlayModule} from './overlay/overlay-directives';
99
import {A11yModule} from './a11y/index';
10+
import {MdSelectionModule} from './selection/index';
1011

1112

1213
// RTL
@@ -114,6 +115,9 @@ export * from './compatibility/default-mode';
114115
// Animation
115116
export * from './animation/animation';
116117

118+
// Selection
119+
export * from './selection/index';
120+
117121
// Coercion
118122
export {coerceBooleanProperty} from './coercion/boolean-property';
119123
export {coerceNumberProperty} from './coercion/number-property';
@@ -132,7 +136,8 @@ export {NoConflictStyleCompatibilityMode} from './compatibility/no-conflict-mode
132136
PortalModule,
133137
OverlayModule,
134138
A11yModule,
135-
MdOptionModule
139+
MdOptionModule,
140+
MdSelectionModule
136141
],
137142
exports: [
138143
MdLineModule,
@@ -142,7 +147,8 @@ export {NoConflictStyleCompatibilityMode} from './compatibility/no-conflict-mode
142147
PortalModule,
143148
OverlayModule,
144149
A11yModule,
145-
MdOptionModule
150+
MdOptionModule,
151+
MdSelectionModule
146152
],
147153
})
148154
export class MdCoreModule {

src/lib/core/selection/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import {NgModule} from '@angular/core';
2+
import {MdPseudoCheckbox} from './pseudo-checkbox/pseudo-checkbox';
3+
4+
export * from './pseudo-checkbox/pseudo-checkbox';
5+
6+
@NgModule({
7+
exports: [MdPseudoCheckbox],
8+
declarations: [MdPseudoCheckbox]
9+
})
10+
export class MdSelectionModule { }
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
@import '../../theming/theming';
2+
3+
4+
@mixin md-pseudo-checkbox-theme($theme) {
5+
$is-dark-theme: map-get($theme, is-dark);
6+
$primary: map-get($theme, primary);
7+
$accent: map-get($theme, accent);
8+
$warn: map-get($theme, warn);
9+
$background: map-get($theme, background);
10+
11+
// The color of the checkbox's checkmark / mixedmark.
12+
$checkbox-mark-color: md-color($background, background);
13+
14+
// NOTE(traviskaufman): While the spec calls for translucent blacks/whites for disabled colors,
15+
// this does not work well with elements layered on top of one another. To get around this we
16+
// blend the colors together based on the base color and the theme background.
17+
$white-30pct-opacity-on-dark: #686868;
18+
$black-26pct-opacity-on-light: #b0b0b0;
19+
$disabled-color: if($is-dark-theme, $white-30pct-opacity-on-dark, $black-26pct-opacity-on-light);
20+
21+
md-pseudo-checkbox::after {
22+
color: $checkbox-mark-color;
23+
}
24+
25+
.md-pseudo-checkbox-checked, .md-pseudo-checkbox-indeterminate {
26+
border: none;
27+
28+
&.md-primary {
29+
background: md-color($primary, 500);
30+
}
31+
32+
&.md-accent {
33+
background: md-color($accent, 500);
34+
}
35+
36+
&.md-warn {
37+
background: md-color($warn, 500);
38+
}
39+
40+
&.md-pseudo-checkbox-disabled {
41+
background: $disabled-color;
42+
}
43+
}
44+
}

src/lib/core/selection/pseudo-checkbox/pseudo-checkbox.html

Whitespace-only changes.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
@import '../../style/checkbox-variables';
2+
3+
// Padding inside of a pseudo checkbox.
4+
$_md-pseudo-checkbox-padding: $md-checkbox-border-width * 2;
5+
6+
// Size of the checkmark in a pseudo checkbox.
7+
$_md-pseudo-checkmark-size: $md-checkbox-size - (2 * $_md-pseudo-checkbox-padding);
8+
9+
10+
md-pseudo-checkbox {
11+
width: $md-checkbox-size;
12+
height: $md-checkbox-size;
13+
border: $md-checkbox-border-width solid;
14+
border-radius: 2px;
15+
cursor: pointer;
16+
display: inline-block;
17+
vertical-align: middle;
18+
box-sizing: border-box;
19+
position: relative;
20+
transition:
21+
border-color $md-checkbox-transition-duration $md-linear-out-slow-in-timing-function,
22+
background-color $md-checkbox-transition-duration $md-linear-out-slow-in-timing-function;
23+
24+
&::after {
25+
position: absolute;
26+
opacity: 0;
27+
content: "";
28+
border-bottom: $md-checkbox-border-width solid currentColor;
29+
transition: opacity $md-checkbox-transition-duration $md-linear-out-slow-in-timing-function;
30+
}
31+
}
32+
33+
.md-pseudo-checkbox-disabled {
34+
cursor: default;
35+
}
36+
37+
.md-pseudo-checkbox-indeterminate::after {
38+
top: ($md-checkbox-size - $md-checkbox-border-width) / 2;
39+
left: $md-checkbox-border-width;
40+
width: $md-checkbox-size - ($md-checkbox-border-width * 2);
41+
opacity: 1;
42+
}
43+
44+
.md-pseudo-checkbox-checked::after {
45+
top: ($md-checkbox-size / 2) - ($_md-pseudo-checkmark-size / 4) - ($md-checkbox-size / 10);
46+
left: $_md-pseudo-checkbox-padding - $md-checkbox-border-width / 2;
47+
width: $_md-pseudo-checkmark-size;
48+
height: ($_md-pseudo-checkmark-size - $md-checkbox-border-width) / 2;
49+
border-left: $md-checkbox-border-width solid currentColor;
50+
transform: rotate(-45deg);
51+
opacity: 1;
52+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import {
2+
Component,
3+
ViewEncapsulation,
4+
Input,
5+
ElementRef,
6+
Renderer,
7+
} from '@angular/core';
8+
9+
export type MdPseudoCheckboxState = 'unchecked' | 'checked' | 'indeterminate';
10+
11+
/**
12+
* Simplified checkbox without any of the underlying form control logic
13+
* and fancy animations. To be used when composing other components.
14+
* @docs-private
15+
*/
16+
@Component({
17+
moduleId: module.id,
18+
encapsulation: ViewEncapsulation.None,
19+
selector: 'md-pseudo-checkbox',
20+
styleUrls: ['pseudo-checkbox.css'],
21+
templateUrl: 'pseudo-checkbox.html',
22+
host: {
23+
'[class.md-pseudo-checkbox-indeterminate]': 'state === "indeterminate"',
24+
'[class.md-pseudo-checkbox-checked]': 'state === "checked"',
25+
'[class.md-pseudo-checkbox-disabled]': 'disabled',
26+
},
27+
})
28+
export class MdPseudoCheckbox {
29+
/** Display state of the checkbox. */
30+
@Input() state: MdPseudoCheckboxState = 'unchecked';
31+
32+
/** Whether the checkbox is disabled. */
33+
@Input() disabled: boolean = false;
34+
35+
/** Color of the checkbox. */
36+
@Input()
37+
get color(): string { return this._color; };
38+
set color(value: string) {
39+
if (value) {
40+
let nativeElement = this._elementRef.nativeElement;
41+
42+
this._renderer.setElementClass(nativeElement, `md-${this.color}`, false);
43+
this._renderer.setElementClass(nativeElement, `md-${value}`, true);
44+
this._color = value;
45+
}
46+
}
47+
48+
private _color: string;
49+
50+
constructor(private _elementRef: ElementRef, private _renderer: Renderer) {
51+
this.color = 'accent';
52+
}
53+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@import './variables';
2+
3+
// The width/height of the checkbox element.
4+
$md-checkbox-size: $md-toggle-size !default;
5+
6+
// The width of the checkbox border shown when the checkbox is unchecked.
7+
$md-checkbox-border-width: 2px;
8+
9+
// The base duration used for the majority of transitions for the checkbox.
10+
$md-checkbox-transition-duration: 90ms;

0 commit comments

Comments
 (0)