Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add simplified checkbox component for usage in other components #2619

Merged
merged 5 commits into from
Jan 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 19 additions & 24 deletions src/lib/checkbox/checkbox.scss
Original file line number Diff line number Diff line change
@@ -1,26 +1,21 @@
@import '../core/theming/theming';
@import '../core/style/elevation';
@import '../core/style/variables';
@import '../core/style/checkbox-common';
@import '../core/ripple/ripple';


// The width/height of the checkbox element.
$md-checkbox-size: $md-toggle-size !default;
// The width of the line used to draw the checkmark / mixedmark.
$md-checkbox-mark-stroke-size: 2/15 * $md-checkbox-size !default;
// The width of the checkbox border shown when the checkbox is unchecked.
$md-checkbox-border-width: 2px;
// The base duration used for the majority of transitions for the checkbox.
$md-checkbox-transition-duration: 90ms;
// The amount of spacing between the checkbox and its label.
$md-checkbox-item-spacing: $md-toggle-padding;

// Manual calculation done on SVG
$_md-checkbox-mark-path-length: 22.910259;
$_md-checkbox-indeterminate-checked-easing-function: cubic-bezier(0.14, 0, 0, 1);

// The ripple size of the checkbox
$md-checkbox-ripple-size: 15px;
$_md-checkbox-ripple-size: 15px;

// The amount of spacing between the checkbox and its label.
$_md-checkbox-item-spacing: $md-toggle-padding;

// The width of the line used to draw the checkmark / mixedmark.
$_md-checkbox-mark-stroke-size: 2 / 15 * $md-checkbox-size !default;


// Fades in the background of the checkbox when it goes from unchecked -> {checked,indeterminate}.
@keyframes md-checkbox-fade-in-background {
Expand Down Expand Up @@ -213,7 +208,7 @@ md-checkbox {
height: $md-checkbox-size;
line-height: 0;
margin: auto;
margin-right: $md-checkbox-item-spacing;
margin-right: $_md-checkbox-item-spacing;
order: 0;
position: relative;
vertical-align: middle;
Expand All @@ -223,7 +218,7 @@ md-checkbox {

[dir='rtl'] & {
margin: {
left: $md-checkbox-item-spacing;
left: $_md-checkbox-item-spacing;
right: auto;
}
}
Expand Down Expand Up @@ -266,14 +261,14 @@ md-checkbox {
stroke: {
dashoffset: $_md-checkbox-mark-path-length;
dasharray: $_md-checkbox-mark-path-length;
width: $md-checkbox-mark-stroke-size;
width: $_md-checkbox-mark-stroke-size;
}
}

.md-checkbox-mixedmark {
@extend %md-checkbox-mark;

height: floor($md-checkbox-mark-stroke-size);
height: floor($_md-checkbox-mark-stroke-size);
opacity: 0;
transform: scaleX(0) rotate(0deg);
}
Expand All @@ -282,14 +277,14 @@ md-checkbox {
.md-checkbox-inner-container {
order: 1;
margin: {
left: $md-checkbox-item-spacing;
left: $_md-checkbox-item-spacing;
right: auto;
}

[dir='rtl'] & {
margin: {
left: auto;
right: $md-checkbox-item-spacing;
right: $_md-checkbox-item-spacing;
}
}
}
Expand Down Expand Up @@ -421,10 +416,10 @@ md-checkbox {

.md-checkbox-ripple {
position: absolute;
left: -$md-checkbox-ripple-size;
top: -$md-checkbox-ripple-size;
right: -$md-checkbox-ripple-size;
bottom: -$md-checkbox-ripple-size;
left: -$_md-checkbox-ripple-size;
top: -$_md-checkbox-ripple-size;
right: -$_md-checkbox-ripple-size;
bottom: -$_md-checkbox-ripple-size;
border-radius: 50%;
z-index: 1;
pointer-events: none;
Expand Down
2 changes: 2 additions & 0 deletions src/lib/core/_core.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@import 'ripple/ripple';
@import 'option/option';
@import 'option/option-theme';
@import 'selection/pseudo-checkbox/pseudo-checkbox-theme';

// Mixin that renders all of the core styles that are not theme-dependent.
@mixin md-core() {
Expand All @@ -27,6 +28,7 @@
@mixin md-core-theme($theme) {
@include md-ripple-theme($theme);
@include md-option-theme($theme);
@include md-pseudo-checkbox-theme($theme);

// Wrapper element that provides the theme background when the
// user's content isn't inside of a `md-sidenav-container`.
Expand Down
10 changes: 8 additions & 2 deletions src/lib/core/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {MdRippleModule} from './ripple/ripple';
import {PortalModule} from './portal/portal-directives';
import {OverlayModule} from './overlay/overlay-directives';
import {A11yModule} from './a11y/index';
import {MdSelectionModule} from './selection/index';


// RTL
Expand Down Expand Up @@ -114,6 +115,9 @@ export * from './compatibility/default-mode';
// Animation
export * from './animation/animation';

// Selection
export * from './selection/index';

// Coercion
export {coerceBooleanProperty} from './coercion/boolean-property';
export {coerceNumberProperty} from './coercion/number-property';
Expand All @@ -132,7 +136,8 @@ export {NoConflictStyleCompatibilityMode} from './compatibility/no-conflict-mode
PortalModule,
OverlayModule,
A11yModule,
MdOptionModule
MdOptionModule,
MdSelectionModule,
],
exports: [
MdLineModule,
Expand All @@ -142,7 +147,8 @@ export {NoConflictStyleCompatibilityMode} from './compatibility/no-conflict-mode
PortalModule,
OverlayModule,
A11yModule,
MdOptionModule
MdOptionModule,
MdSelectionModule,
],
})
export class MdCoreModule {
Expand Down
10 changes: 10 additions & 0 deletions src/lib/core/selection/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {NgModule} from '@angular/core';
import {MdPseudoCheckbox} from './pseudo-checkbox/pseudo-checkbox';

export * from './pseudo-checkbox/pseudo-checkbox';

@NgModule({
exports: [MdPseudoCheckbox],
declarations: [MdPseudoCheckbox]
})
export class MdSelectionModule { }
44 changes: 44 additions & 0 deletions src/lib/core/selection/pseudo-checkbox/_pseudo-checkbox-theme.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
@import '../../theming/theming';


@mixin md-pseudo-checkbox-theme($theme) {
$is-dark-theme: map-get($theme, is-dark);
$primary: map-get($theme, primary);
$accent: map-get($theme, accent);
$warn: map-get($theme, warn);
$background: map-get($theme, background);

// The color of the checkbox's checkmark / mixedmark.
$checkbox-mark-color: md-color($background, background);

// NOTE(traviskaufman): While the spec calls for translucent blacks/whites for disabled colors,
// this does not work well with elements layered on top of one another. To get around this we
// blend the colors together based on the base color and the theme background.
$white-30pct-opacity-on-dark: #686868;
$black-26pct-opacity-on-light: #b0b0b0;
$disabled-color: if($is-dark-theme, $white-30pct-opacity-on-dark, $black-26pct-opacity-on-light);

md-pseudo-checkbox::after {
color: $checkbox-mark-color;
}

.md-pseudo-checkbox-checked, .md-pseudo-checkbox-indeterminate {
border: none;

&.md-primary {
background: md-color($primary, 500);
}

&.md-accent {
background: md-color($accent, 500);
}

&.md-warn {
background: md-color($warn, 500);
}

&.md-pseudo-checkbox-disabled {
background: $disabled-color;
}
}
}
53 changes: 53 additions & 0 deletions src/lib/core/selection/pseudo-checkbox/pseudo-checkbox.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
@import '../../style/checkbox-common';

// Padding inside of a pseudo checkbox.
$_md-pseudo-checkbox-padding: $md-checkbox-border-width * 2;

// Size of the checkmark in a pseudo checkbox.
$_md-pseudo-checkmark-size: $md-checkbox-size - (2 * $_md-pseudo-checkbox-padding);


md-pseudo-checkbox {
width: $md-checkbox-size;
height: $md-checkbox-size;
border: $md-checkbox-border-width solid;
border-radius: 2px;
cursor: pointer;
display: inline-block;
vertical-align: middle;
box-sizing: border-box;
position: relative;
transition:
border-color $md-checkbox-transition-duration $md-linear-out-slow-in-timing-function,
background-color $md-checkbox-transition-duration $md-linear-out-slow-in-timing-function;

// Used to render the checkmark/mixedmark inside of the box.
&::after {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment explaining what the ::after element is used for?

position: absolute;
opacity: 0;
content: '';
border-bottom: $md-checkbox-border-width solid currentColor;
transition: opacity $md-checkbox-transition-duration $md-linear-out-slow-in-timing-function;
}
}

.md-pseudo-checkbox-disabled {
cursor: default;
}

.md-pseudo-checkbox-indeterminate::after {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that the pseudo-checkbox needs an indeterminate state; did you have something in mind for it?

Also same question for disabled

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We definitely need the disabled state since select options can be disabled. Regarding the indeterminate state, we don't need it for the select, but I'd imagine that it would come in handy for other components (e.g. the "select all" functionality in the data table or potentially in the selection list).

top: ($md-checkbox-size - $md-checkbox-border-width) / 2;
left: $md-checkbox-border-width;
width: $md-checkbox-size - ($md-checkbox-border-width * 2);
opacity: 1;
}

.md-pseudo-checkbox-checked::after {
top: ($md-checkbox-size / 2) - ($_md-pseudo-checkmark-size / 4) - ($md-checkbox-size / 10);
left: $_md-pseudo-checkbox-padding - $md-checkbox-border-width / 2;
width: $_md-pseudo-checkmark-size;
height: ($_md-pseudo-checkmark-size - $md-checkbox-border-width) / 2;
border-left: $md-checkbox-border-width solid currentColor;
transform: rotate(-45deg);
opacity: 1;
}
59 changes: 59 additions & 0 deletions src/lib/core/selection/pseudo-checkbox/pseudo-checkbox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
Component,
ViewEncapsulation,
Input,
ElementRef,
Renderer,
} from '@angular/core';

export type MdPseudoCheckboxState = 'unchecked' | 'checked' | 'indeterminate';

/**
* Component that shows a simplified checkbox without including any kind of "real" checkbox.
* Meant to be used when the checkbox is purely decorative and a large number of them will be
* included, such as for the options in a multi-select. Uses no SVGs or complex animations.
*
* Note that this component will be completely invisible to screen-reader users. This is *not*
* interchangeable with <md-checkbox> and should *not* be used if the user would directly interact
* with the checkbox. The pseudo-checkbox should only be used as an implementation detail of
* more complex components that appropriately handle selected / checked state.
* @docs-private
*/
@Component({
moduleId: module.id,
encapsulation: ViewEncapsulation.None,
selector: 'md-pseudo-checkbox',
styleUrls: ['pseudo-checkbox.css'],
template: '',
host: {
'[class.md-pseudo-checkbox-indeterminate]': 'state === "indeterminate"',
'[class.md-pseudo-checkbox-checked]': 'state === "checked"',
'[class.md-pseudo-checkbox-disabled]': 'disabled',
},
})
export class MdPseudoCheckbox {
/** Display state of the checkbox. */
@Input() state: MdPseudoCheckboxState = 'unchecked';

/** Whether the checkbox is disabled. */
@Input() disabled: boolean = false;

/** Color of the checkbox. */
@Input()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of doing an @Input for color and disabled, perhaps we should just include the class directly? My reasoning is that it more clearly communicates that it's not a real checkbox and is only for show.

@kara thoughts?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that it's mainly for consistency. Regarding communication, this is intended for internal usage anyway, it shouldn't be an issue if people look at the readme.

get color(): string { return this._color; };
set color(value: string) {
if (value) {
let nativeElement = this._elementRef.nativeElement;

this._renderer.setElementClass(nativeElement, `md-${this.color}`, false);
this._renderer.setElementClass(nativeElement, `md-${value}`, true);
this._color = value;
}
}

private _color: string;

constructor(private _elementRef: ElementRef, private _renderer: Renderer) {
this.color = 'accent';
}
}
10 changes: 10 additions & 0 deletions src/lib/core/style/_checkbox-common.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@import './variables';

// The width/height of the checkbox element.
$md-checkbox-size: $md-toggle-size !default;

// The width of the checkbox border shown when the checkbox is unchecked.
$md-checkbox-border-width: 2px;

// The base duration used for the majority of transitions for the checkbox.
$md-checkbox-transition-duration: 90ms;