Skip to content

Commit d0f4c3e

Browse files
karajelbourn
authored andcommitted
feat(select): support disabling (#1667)
1 parent aa617f9 commit d0f4c3e

File tree

11 files changed

+311
-38
lines changed

11 files changed

+311
-38
lines changed

src/demo-app/select/select-demo.html

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,30 @@
11
<div class="demo-select">
2-
<md-select placeholder="Food" [formControl]="control" [required]="isRequired">
3-
<md-option *ngFor="let food of foods" [value]="food.value"> {{ food.viewValue }} </md-option>
4-
</md-select>
5-
<p> Value: {{ control.value }} </p>
6-
<p> Touched: {{ control.touched }} </p>
7-
<p> Dirty: {{ control.dirty }} </p>
8-
<p> Status: {{ control.status }} </p>
9-
<button md-button (click)="control.setValue('pizza-1')">SET VALUE</button>
10-
<button md-button (click)="isRequired=!isRequired">TOGGLE REQUIRED</button>
2+
<md-card>
3+
<md-select placeholder="Food" [formControl]="foodControl">
4+
<md-option *ngFor="let food of foods" [value]="food.value"> {{ food.viewValue }} </md-option>
5+
</md-select>
6+
<p> Value: {{ foodControl.value }} </p>
7+
<p> Touched: {{ foodControl.touched }} </p>
8+
<p> Dirty: {{ foodControl.dirty }} </p>
9+
<p> Status: {{ foodControl.status }} </p>
10+
<button md-button (click)="foodControl.setValue('pizza-1')">SET VALUE</button>
11+
<button md-button (click)="toggleDisabled()">TOGGLE DISABLED</button>
12+
</md-card>
13+
14+
<md-card>
15+
<md-select placeholder="Drink" [(ngModel)]="currentDrink" [required]="isRequired" [disabled]="isDisabled"
16+
#drinkControl="ngModel">
17+
<md-option *ngFor="let drink of drinks" [value]="drink.value" [disabled]="drink.disabled">
18+
{{ drink.viewValue }}
19+
</md-option>
20+
</md-select>
21+
<p> Value: {{ currentDrink }} </p>
22+
<p> Touched: {{ drinkControl.touched }} </p>
23+
<p> Dirty: {{ drinkControl.dirty }} </p>
24+
<p> Status: {{ drinkControl.control?.status }} </p>
25+
<button md-button (click)="currentDrink='sprite-1'">SET VALUE</button>
26+
<button md-button (click)="isRequired=!isRequired">TOGGLE REQUIRED</button>
27+
<button md-button (click)="isDisabled=!isDisabled">TOGGLE DISABLED</button>
28+
</md-card>
29+
1130
</div>

src/demo-app/select/select-demo.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,10 @@
11
.demo-select {
2+
display: flex;
3+
flex-flow: row wrap;
4+
5+
md-card {
6+
width: 450px;
7+
margin: 24px;
8+
}
9+
210
}

src/demo-app/select/select-demo.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,24 @@ import {FormControl} from '@angular/forms';
99
})
1010
export class SelectDemo {
1111
isRequired = false;
12+
isDisabled = false;
13+
currentDrink: string;
14+
foodControl = new FormControl('');
1215

1316
foods = [
1417
{value: 'steak-0', viewValue: 'Steak'},
1518
{value: 'pizza-1', viewValue: 'Pizza'},
1619
{value: 'tacos-2', viewValue: 'Tacos'}
1720
];
1821

19-
control = new FormControl('');
22+
drinks = [
23+
{value: 'coke-0', viewValue: 'Coke'},
24+
{value: 'sprite-1', viewValue: 'Sprite', disabled: true},
25+
{value: 'water-2', viewValue: 'Water'}
26+
];
27+
28+
toggleDisabled() {
29+
this.foodControl.enabled ? this.foodControl.disable() : this.foodControl.enable();
30+
}
2031

2132
}

src/lib/core/style/_form-common.scss

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
// Gradient for showing the dashed line when the input is disabled.
3+
// Unlike using a border, a gradient allows us to adjust the spacing of the dotted line
4+
// to match the Material Design spec.
5+
$md-underline-disabled-background-image:
6+
linear-gradient(to right, rgba(0, 0, 0, 0.26) 0%, rgba(0, 0, 0, 0.26) 33%, transparent 0%);
7+
8+
@mixin md-control-disabled-underline {
9+
background-image: $md-underline-disabled-background-image;
10+
background-size: 4px 1px;
11+
background-repeat: repeat-x;
12+
}

src/lib/input/input.scss

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@import '../core/style/variables';
2+
@import '../core/style/form-common';
23

34

45
$md-input-floating-placeholder-scale-factor: 0.75 !default;
@@ -151,11 +152,9 @@ md-input, md-textarea {
151152
border-top-style: solid;
152153

153154
&.md-disabled {
155+
@include md-control-disabled-underline();
154156
border-top: 0;
155-
background-image: $md-input-underline-disabled-background-image;
156157
background-position: 0;
157-
background-size: 4px 1px;
158-
background-repeat: repeat-x;
159158
}
160159

161160
.md-input-ripple {

src/lib/select/_select-theme.scss

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
color: md-color($foreground, hint-text);
1212
border-bottom: 1px solid md-color($foreground, divider);
1313

14-
md-select:focus & {
14+
md-select:focus:not(.md-select-disabled) & {
1515
color: md-color($primary);
1616
border-bottom: 1px solid md-color($primary);
1717
}
1818

19-
.ng-invalid.ng-touched & {
19+
.ng-invalid.ng-touched:not(.md-select-disabled) & {
2020
color: md-color($warn);
2121
border-bottom: 1px solid md-color($warn);
2222
}
@@ -25,11 +25,11 @@
2525
.md-select-arrow {
2626
color: md-color($foreground, hint-text);
2727

28-
md-select:focus & {
28+
md-select:focus:not(.md-select-disabled) & {
2929
color: md-color($primary);
3030
}
3131

32-
.ng-invalid.ng-touched & {
32+
.ng-invalid.ng-touched:not(.md-select-disabled) & {
3333
color: md-color($warn);
3434
}
3535
}
@@ -40,10 +40,14 @@
4040

4141
.md-select-value {
4242
color: md-color($foreground, text);
43+
44+
.md-select-disabled & {
45+
color: md-color($foreground, hint-text);
46+
}
4347
}
4448

4549
md-option {
46-
&:hover, &:focus {
50+
&:hover:not(.md-option-disabled), &:focus:not(.md-option-disabled) {
4751
background: md-color($background, hover);
4852
}
4953

@@ -52,5 +56,9 @@
5256
color: md-color($primary);
5357
}
5458

59+
&.md-option-disabled {
60+
color: md-color($foreground, hint-text);
61+
}
62+
5563
}
5664
}

src/lib/select/option.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
<ng-content></ng-content>
2-
<div class="md-option-ripple" md-ripple md-ripple-background-color="rgba(0,0,0,0)"
2+
<div class="md-option-ripple" *ngIf="!disabled" md-ripple md-ripple-background-color="rgba(0,0,0,0)"
33
[md-ripple-trigger]="_getHostElement()"></div>

src/lib/select/option.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,18 @@ import {
88
ViewEncapsulation
99
} from '@angular/core';
1010
import {ENTER, SPACE} from '../core/keyboard/keycodes';
11+
import {coerceBooleanProperty} from '../core/coersion/boolean-property';
1112

1213
@Component({
1314
moduleId: module.id,
1415
selector: 'md-option',
1516
host: {
1617
'role': 'option',
17-
'tabindex': '0',
18+
'[attr.tabindex]': '_getTabIndex()',
1819
'[class.md-selected]': 'selected',
1920
'[attr.aria-selected]': 'selected.toString()',
21+
'[attr.aria-disabled]': 'disabled.toString()',
22+
'[class.md-option-disabled]': 'disabled',
2023
'(click)': '_selectViaInteraction()',
2124
'(keydown)': '_handleKeydown($event)'
2225
},
@@ -25,11 +28,23 @@ import {ENTER, SPACE} from '../core/keyboard/keycodes';
2528
encapsulation: ViewEncapsulation.None
2629
})
2730
export class MdOption {
28-
private _selected = false;
31+
private _selected: boolean = false;
32+
33+
/** Whether the option is disabled. */
34+
private _disabled: boolean = false;
2935

3036
/** The form value of the option. */
3137
@Input() value: any;
3238

39+
@Input()
40+
get disabled() {
41+
return this._disabled;
42+
}
43+
44+
set disabled(value: any) {
45+
this._disabled = coerceBooleanProperty(value);
46+
}
47+
3348
/** Event emitted when the option is selected. */
3449
@Output() onSelect = new EventEmitter();
3550

@@ -72,13 +87,21 @@ export class MdOption {
7287
}
7388
}
7489

90+
7591
/**
7692
* Selects the option while indicating the selection came from the user. Used to
7793
* determine if the select's view -> model callback should be invoked.
7894
*/
7995
_selectViaInteraction() {
80-
this._selected = true;
81-
this.onSelect.emit(true);
96+
if (!this.disabled) {
97+
this._selected = true;
98+
this.onSelect.emit(true);
99+
}
100+
}
101+
102+
/** Returns the correct tabindex for the option depending on disabled state. */
103+
_getTabIndex() {
104+
return this.disabled ? '-1' : '0';
82105
}
83106

84107
_getHostElement(): HTMLElement {

src/lib/select/select.scss

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@import '../core/style/menu-common';
2+
@import '../core/style/form-common';
23

34
$md-select-trigger-height: 30px !default;
45
$md-select-trigger-min-width: 112px !default;
@@ -16,6 +17,14 @@ md-select {
1617
height: $md-select-trigger-height;
1718
min-width: $md-select-trigger-min-width;
1819
cursor: pointer;
20+
21+
[aria-disabled='true'] & {
22+
@include md-control-disabled-underline();
23+
border-bottom: transparent;
24+
background-position: 0 bottom;
25+
cursor: default;
26+
user-select: none;
27+
}
1928
}
2029

2130
.md-select-placeholder {
@@ -56,6 +65,11 @@ md-option {
5665
position: relative;
5766
cursor: pointer;
5867
outline: none;
68+
69+
&[aria-disabled='true'] {
70+
cursor: default;
71+
user-select: none;
72+
}
5973
}
6074

6175
.md-option-ripple {

0 commit comments

Comments
 (0)