Skip to content

Commit 3b6cab0

Browse files
crisbetoandrewseguin
authored andcommitted
feat: add simplified checkbox component for usage in other components (#2619)
* 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. * Remove unnecessary template. * Fix linter error. * Address PR feedback. * Trailing comma.
1 parent 408c58d commit 3b6cab0

File tree

8 files changed

+205
-26
lines changed

8 files changed

+205
-26
lines changed

src/lib/checkbox/checkbox.scss

+19-24
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-common';
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
}
@@ -264,14 +259,14 @@ md-checkbox {
264259
stroke: {
265260
dashoffset: $_md-checkbox-mark-path-length;
266261
dasharray: $_md-checkbox-mark-path-length;
267-
width: $md-checkbox-mark-stroke-size;
262+
width: $_md-checkbox-mark-stroke-size;
268263
}
269264
}
270265

271266
.md-checkbox-mixedmark {
272267
@extend %md-checkbox-mark;
273268

274-
height: floor($md-checkbox-mark-stroke-size);
269+
height: floor($_md-checkbox-mark-stroke-size);
275270
opacity: 0;
276271
transform: scaleX(0) rotate(0deg);
277272
}
@@ -280,14 +275,14 @@ md-checkbox {
280275
.md-checkbox-inner-container {
281276
order: 1;
282277
margin: {
283-
left: $md-checkbox-item-spacing;
278+
left: $_md-checkbox-item-spacing;
284279
right: auto;
285280
}
286281

287282
[dir='rtl'] & {
288283
margin: {
289284
left: auto;
290-
right: $md-checkbox-item-spacing;
285+
right: $_md-checkbox-item-spacing;
291286
}
292287
}
293288
}
@@ -419,10 +414,10 @@ md-checkbox {
419414

420415
.md-checkbox-ripple {
421416
position: absolute;
422-
left: -$md-checkbox-ripple-size;
423-
top: -$md-checkbox-ripple-size;
424-
right: -$md-checkbox-ripple-size;
425-
bottom: -$md-checkbox-ripple-size;
417+
left: -$_md-checkbox-ripple-size;
418+
top: -$_md-checkbox-ripple-size;
419+
right: -$_md-checkbox-ripple-size;
420+
bottom: -$_md-checkbox-ripple-size;
426421
border-radius: 50%;
427422
z-index: 1;
428423
pointer-events: none;

src/lib/core/_core.scss

+2
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

+8-2
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/compatibility';
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';
@@ -131,7 +135,8 @@ export {CompatibilityModule, NoConflictStyleCompatibilityMode} from './compatibi
131135
PortalModule,
132136
OverlayModule,
133137
A11yModule,
134-
MdOptionModule
138+
MdOptionModule,
139+
MdSelectionModule,
135140
],
136141
exports: [
137142
MdLineModule,
@@ -141,7 +146,8 @@ export {CompatibilityModule, NoConflictStyleCompatibilityMode} from './compatibi
141146
PortalModule,
142147
OverlayModule,
143148
A11yModule,
144-
MdOptionModule
149+
MdOptionModule,
150+
MdSelectionModule,
145151
],
146152
})
147153
export class MdCoreModule {

src/lib/core/selection/index.ts

+10
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 { }
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
@import '../../style/checkbox-common';
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+
// Used to render the checkmark/mixedmark inside of the box.
25+
&::after {
26+
position: absolute;
27+
opacity: 0;
28+
content: '';
29+
border-bottom: $md-checkbox-border-width solid currentColor;
30+
transition: opacity $md-checkbox-transition-duration $md-linear-out-slow-in-timing-function;
31+
}
32+
}
33+
34+
.md-pseudo-checkbox-disabled {
35+
cursor: default;
36+
}
37+
38+
.md-pseudo-checkbox-indeterminate::after {
39+
top: ($md-checkbox-size - $md-checkbox-border-width) / 2;
40+
left: $md-checkbox-border-width;
41+
width: $md-checkbox-size - ($md-checkbox-border-width * 2);
42+
opacity: 1;
43+
}
44+
45+
.md-pseudo-checkbox-checked::after {
46+
top: ($md-checkbox-size / 2) - ($_md-pseudo-checkmark-size / 4) - ($md-checkbox-size / 10);
47+
left: $_md-pseudo-checkbox-padding - $md-checkbox-border-width / 2;
48+
width: $_md-pseudo-checkmark-size;
49+
height: ($_md-pseudo-checkmark-size - $md-checkbox-border-width) / 2;
50+
border-left: $md-checkbox-border-width solid currentColor;
51+
transform: rotate(-45deg);
52+
opacity: 1;
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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+
* Component that shows a simplified checkbox without including any kind of "real" checkbox.
13+
* Meant to be used when the checkbox is purely decorative and a large number of them will be
14+
* included, such as for the options in a multi-select. Uses no SVGs or complex animations.
15+
*
16+
* Note that this component will be completely invisible to screen-reader users. This is *not*
17+
* interchangeable with <md-checkbox> and should *not* be used if the user would directly interact
18+
* with the checkbox. The pseudo-checkbox should only be used as an implementation detail of
19+
* more complex components that appropriately handle selected / checked state.
20+
* @docs-private
21+
*/
22+
@Component({
23+
moduleId: module.id,
24+
encapsulation: ViewEncapsulation.None,
25+
selector: 'md-pseudo-checkbox',
26+
styleUrls: ['pseudo-checkbox.css'],
27+
template: '',
28+
host: {
29+
'[class.md-pseudo-checkbox-indeterminate]': 'state === "indeterminate"',
30+
'[class.md-pseudo-checkbox-checked]': 'state === "checked"',
31+
'[class.md-pseudo-checkbox-disabled]': 'disabled',
32+
},
33+
})
34+
export class MdPseudoCheckbox {
35+
/** Display state of the checkbox. */
36+
@Input() state: MdPseudoCheckboxState = 'unchecked';
37+
38+
/** Whether the checkbox is disabled. */
39+
@Input() disabled: boolean = false;
40+
41+
/** Color of the checkbox. */
42+
@Input()
43+
get color(): string { return this._color; };
44+
set color(value: string) {
45+
if (value) {
46+
let nativeElement = this._elementRef.nativeElement;
47+
48+
this._renderer.setElementClass(nativeElement, `md-${this.color}`, false);
49+
this._renderer.setElementClass(nativeElement, `md-${value}`, true);
50+
this._color = value;
51+
}
52+
}
53+
54+
private _color: string;
55+
56+
constructor(private _elementRef: ElementRef, private _renderer: Renderer) {
57+
this.color = 'accent';
58+
}
59+
}
+10
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)