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

chore: add CanDisable mixin to checkbox, radio-group, slide-toggle, and slider #4209

Merged
merged 1 commit into from
Apr 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/lib/button/button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ export const _MdButtonMixinBase = mixinDisabled(MdButtonBase);
host: {
'[disabled]': 'disabled || null',
},
inputs: ['disabled'],
templateUrl: 'button.html',
styleUrls: ['button.css'],
inputs: ['disabled'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
Expand Down
39 changes: 18 additions & 21 deletions src/lib/checkbox/checkbox.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
import {
ChangeDetectorRef,
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
forwardRef,
Input,
OnDestroy,
Output,
Renderer,
ViewEncapsulation,
forwardRef,
ViewChild,
AfterViewInit,
OnDestroy,
ViewEncapsulation,
} from '@angular/core';
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from '@angular/forms';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {coerceBooleanProperty} from '../core/coercion/boolean-property';
import {
MdRipple,
RippleRef,
FocusOriginMonitor,
FocusOrigin,
} from '../core';
import {FocusOrigin, FocusOriginMonitor, MdRipple, RippleRef} from '../core';
import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled';


/** Monotonically increasing integer used to auto-generate unique ids for checkbox components. */
Expand Down Expand Up @@ -60,6 +56,11 @@ export class MdCheckboxChange {
checked: boolean;
}

// Boilerplate for applying mixins to MdCheckbox.
export class MdCheckboxBase { }
export const _MdCheckboxMixinBase = mixinDisabled(MdCheckboxBase);


/**
* A material design checkbox component. Supports all of the functionality of an HTML5 checkbox,
* and exposes a similar API. A MdCheckbox can be either checked, unchecked, indeterminate, or
Expand All @@ -74,17 +75,19 @@ export class MdCheckboxChange {
templateUrl: 'checkbox.html',
styleUrls: ['checkbox.css'],
host: {
'[class.mat-checkbox]': 'true',
'class': 'mat-checkbox',
'[class.mat-checkbox-indeterminate]': 'indeterminate',
'[class.mat-checkbox-checked]': 'checked',
'[class.mat-checkbox-disabled]': 'disabled',
'[class.mat-checkbox-label-before]': 'labelPosition == "before"',
},
providers: [MD_CHECKBOX_CONTROL_VALUE_ACCESSOR],
inputs: ['disabled'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MdCheckbox implements ControlValueAccessor, AfterViewInit, OnDestroy {
export class MdCheckbox extends _MdCheckboxMixinBase
implements ControlValueAccessor, AfterViewInit, OnDestroy, CanDisable {
/**
* Attached to the aria-label attribute of the host element. In most cases, arial-labelledby will
* take precedence so this may be omitted.
Expand Down Expand Up @@ -137,13 +140,6 @@ export class MdCheckbox implements ControlValueAccessor, AfterViewInit, OnDestro
/** Whether the label should appear after or before the checkbox. Defaults to 'after' */
@Input() labelPosition: 'before' | 'after' = 'after';

private _disabled: boolean = false;

/** Whether the checkbox is disabled. */
@Input()
get disabled(): boolean { return this._disabled; }
set disabled(value) { this._disabled = coerceBooleanProperty(value); }

/** Tabindex value that is passed to the underlying input element. */
@Input() tabIndex: number = 0;

Expand Down Expand Up @@ -189,6 +185,7 @@ export class MdCheckbox implements ControlValueAccessor, AfterViewInit, OnDestro
private _elementRef: ElementRef,
private _changeDetectorRef: ChangeDetectorRef,
private _focusOriginMonitor: FocusOriginMonitor) {
super();
this.color = 'accent';
}

Expand Down
24 changes: 10 additions & 14 deletions src/lib/radio/radio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
FocusOrigin,
} from '../core';
import {coerceBooleanProperty} from '../core/coercion/boolean-property';
import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled';


/**
Expand All @@ -49,6 +50,11 @@ export class MdRadioChange {
value: any;
}


// Boilerplate for applying mixins to MdRadioGroup.
export class MdRadioGroupBase { }
export const _MdRadioGroupMixinBase = mixinDisabled(MdRadioGroupBase);

/**
* A group of radio buttons. May contain one or more `<md-radio-button>` elements.
*/
Expand All @@ -59,8 +65,10 @@ export class MdRadioChange {
'role': 'radiogroup',
'[class.mat-radio-group]': 'true',
},
inputs: ['disabled'],
})
export class MdRadioGroup implements AfterContentInit, ControlValueAccessor {
export class MdRadioGroup extends _MdRadioGroupMixinBase
implements AfterContentInit, ControlValueAccessor, CanDisable {
/**
* Selected value for group. Should equal the value of the selected radio button if there *is*
* a corresponding radio button with a matching value. If there is *not* such a corresponding
Expand All @@ -72,9 +80,6 @@ export class MdRadioGroup implements AfterContentInit, ControlValueAccessor {
/** The HTML name attribute applied to radio buttons in this group. */
private _name: string = `md-radio-group-${_uniqueIdCounter++}`;

/** Disables all individual radio buttons assigned to this group. */
private _disabled: boolean = false;

/** The currently selected radio button. Should match value. */
private _selected: MdRadioButton = null;

Expand Down Expand Up @@ -127,14 +132,6 @@ export class MdRadioGroup implements AfterContentInit, ControlValueAccessor {
/** Whether the labels should appear after or before the radio-buttons. Defaults to 'after' */
@Input() labelPosition: 'before' | 'after' = 'after';

/** Whether the radio button is disabled. */
@Input()
get disabled(): boolean { return this._disabled; }
set disabled(value) {
// The presence of *any* disabled value makes the component disabled, *except* for false.
this._disabled = (value != null && value !== false) ? true : null;
}

/** Value of the radio button. */
@Input()
get value(): any { return this._value; }
Expand Down Expand Up @@ -369,8 +366,7 @@ export class MdRadioButton implements OnInit, AfterViewInit, OnDestroy {
}

set disabled(value: boolean) {
// The presence of *any* disabled value makes the component disabled, *except* for false.
this._disabled = (value != null && value !== false) ? true : null;
this._disabled = coerceBooleanProperty(value);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we not use the mixin for the individual radio buttons too?

Copy link
Member Author

Choose a reason for hiding this comment

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

The getter for indivual buttons is different (it checks the parent state as well as its own)

}

/**
Expand Down
44 changes: 23 additions & 21 deletions src/lib/slide-toggle/slide-toggle.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
import {
AfterContentInit,
ChangeDetectionStrategy,
Component,
ElementRef,
Renderer,
EventEmitter,
forwardRef,
ChangeDetectionStrategy,
Input,
OnDestroy,
Output,
EventEmitter,
AfterContentInit,
Renderer,
ViewChild,
ViewEncapsulation,
OnDestroy,
ViewEncapsulation
} from '@angular/core';
import {
applyCssTransform,
coerceBooleanProperty,
HammerInput,
FocusOriginMonitor,
FocusOrigin,
FocusOriginMonitor,
HammerInput,
MdRipple,
RippleRef
} from '../core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {Observable} from 'rxjs/Observable';
import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled';


export const MD_SLIDE_TOGGLE_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
Expand All @@ -39,9 +40,13 @@ export class MdSlideToggleChange {
// Increasing integer for generating unique ids for slide-toggle components.
let nextId = 0;

/**
* Two-state control, which can be also called `switch`.
*/


// Boilerplate for applying mixins to MdSlideToggle.
export class MdSlideToggleBase { }
export const _MdSlideToggleMixinBase = mixinDisabled(MdSlideToggleBase);

/** Represents a slidable "switch" toggle that can be moved between on and off. */
@Component({
moduleId: module.id,
selector: 'md-slide-toggle, mat-slide-toggle',
Expand All @@ -54,19 +59,19 @@ let nextId = 0;
templateUrl: 'slide-toggle.html',
styleUrls: ['slide-toggle.css'],
providers: [MD_SLIDE_TOGGLE_VALUE_ACCESSOR],
inputs: ['disabled'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MdSlideToggle implements OnDestroy, AfterContentInit, ControlValueAccessor {

export class MdSlideToggle extends _MdSlideToggleMixinBase
implements OnDestroy, AfterContentInit, ControlValueAccessor, CanDisable {
private onChange = (_: any) => {};
private onTouched = () => {};

// A unique id for the slide-toggle. By default the id is auto-generated.
private _uniqueId = `md-slide-toggle-${++nextId}`;
private _color: string;
private _slideRenderer: SlideToggleRenderer = null;
private _disabled: boolean = false;
private _required: boolean = false;
private _disableRipple: boolean = false;

Expand Down Expand Up @@ -94,11 +99,6 @@ export class MdSlideToggle implements OnDestroy, AfterContentInit, ControlValueA
/** Used to set the aria-labelledby attribute on the underlying input element. */
@Input('aria-labelledby') ariaLabelledby: string = null;

/** Whether the slide-toggle is disabled. */
@Input()
get disabled(): boolean { return this._disabled; }
set disabled(value) { this._disabled = coerceBooleanProperty(value); }

/** Whether the slide-toggle is required. */
@Input()
get required(): boolean { return this._required; }
Expand All @@ -123,7 +123,9 @@ export class MdSlideToggle implements OnDestroy, AfterContentInit, ControlValueA

constructor(private _elementRef: ElementRef,
private _renderer: Renderer,
private _focusOriginMonitor: FocusOriginMonitor) {}
private _focusOriginMonitor: FocusOriginMonitor) {
super();
}

ngAfterContentInit() {
this._slideRenderer = new SlideToggleRenderer(this._elementRef);
Expand Down
20 changes: 12 additions & 8 deletions src/lib/slider/slider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {
UP_ARROW
} from '../core/keyboard/keycodes';
import {FocusOrigin, FocusOriginMonitor} from '../core/style/focus-origin-monitor';
import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled';


/**
* Visually, a 30px separation between tick marks looks best. This is very subjective but it is
Expand Down Expand Up @@ -59,6 +61,11 @@ export class MdSliderChange {
value: number;
}


// Boilerplate for applying mixins to MdSlider.
export class MdSliderBase { }
export const _MdSliderMixinBase = mixinDisabled(MdSliderBase);

/**
* Allows users to select from a range of values by moving the slider thumb. It is similar in
* behavior to the native `<input type="range">` element.
Expand All @@ -68,7 +75,6 @@ export class MdSliderChange {
selector: 'md-slider, mat-slider',
providers: [MD_SLIDER_VALUE_ACCESSOR],
host: {
'[class.mat-slider]': 'true',
'(focus)': '_onFocus()',
'(blur)': '_onBlur()',
'(click)': '_onClick($event)',
Expand All @@ -78,6 +84,7 @@ export class MdSliderChange {
'(slide)': '_onSlide($event)',
'(slideend)': '_onSlideEnd()',
'(slidestart)': '_onSlideStart($event)',
'class': 'mat-slider',
'role': 'slider',
'tabindex': '0',
'[attr.aria-disabled]': 'disabled',
Expand All @@ -99,15 +106,11 @@ export class MdSliderChange {
},
templateUrl: 'slider.html',
styleUrls: ['slider.css'],
inputs: ['disabled'],
encapsulation: ViewEncapsulation.None,
})
export class MdSlider implements ControlValueAccessor, OnDestroy {
/** Whether or not the slider is disabled. */
@Input()
get disabled(): boolean { return this._disabled; }
set disabled(value) { this._disabled = coerceBooleanProperty(value); }
private _disabled: boolean = false;

export class MdSlider extends _MdSliderMixinBase
implements ControlValueAccessor, OnDestroy, CanDisable {
/** Whether the slider is inverted. */
@Input()
get invert() { return this._invert; }
Expand Down Expand Up @@ -379,6 +382,7 @@ export class MdSlider implements ControlValueAccessor, OnDestroy {

constructor(renderer: Renderer, private _elementRef: ElementRef,
private _focusOriginMonitor: FocusOriginMonitor, @Optional() private _dir: Dir) {
super();
this._focusOriginMonitor.monitor(this._elementRef.nativeElement, renderer, true)
.subscribe((origin: FocusOrigin) => this._isActive = !!origin && origin !== 'keyboard');
this._renderer = new SliderRenderer(this._elementRef);
Expand Down