mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(select): only animate placeholder when no selection (#2054)
This commit is contained in:
@@ -20,14 +20,15 @@ import {
|
||||
* depending on the text direction of the application.
|
||||
*/
|
||||
export const transformPlaceholder: AnimationEntryMetadata = trigger('transformPlaceholder', [
|
||||
state('normal', style({
|
||||
transform: `translate3d(0, 0, 0) scale(1)`
|
||||
})),
|
||||
state('floating-ltr', style({
|
||||
transform: `translate3d(-2px, -22px, 0) scale(0.75)`
|
||||
top: '-22px',
|
||||
left: '-2px',
|
||||
transform: `scale(0.75)`
|
||||
})),
|
||||
state('floating-rtl', style({
|
||||
transform: `translate3d(2px, -22px, 0) scale(0.75)`
|
||||
top: '-22px',
|
||||
left: '2px',
|
||||
transform: `scale(0.75)`
|
||||
})),
|
||||
transition('* => *', animate(`400ms cubic-bezier(0.25, 0.8, 0.25, 1)`))
|
||||
]);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<div class="md-select-trigger" overlay-origin (click)="toggle()" #origin="overlayOrigin" #trigger>
|
||||
<span class="md-select-placeholder" [@transformPlaceholder]="_getPlaceholderState()"> {{ placeholder }} </span>
|
||||
<span class="md-select-placeholder" [class.md-floating-placeholder]="this.selected"
|
||||
[@transformPlaceholder]="_placeholderState"> {{ placeholder }} </span>
|
||||
<span class="md-select-value" *ngIf="selected"> {{ selected?.viewValue }} </span>
|
||||
<span class="md-select-arrow"></span>
|
||||
</div>
|
||||
|
||||
@@ -32,11 +32,26 @@ md-select {
|
||||
}
|
||||
|
||||
.md-select-placeholder {
|
||||
position: relative;
|
||||
padding: 0 2px;
|
||||
transform-origin: left top;
|
||||
|
||||
// These values are duplicated from animation code in order to
|
||||
// allow placeholders to sometimes float without animating,
|
||||
// for example when the value is set programmatically.
|
||||
// TODO(kara): Change when animations API supports skipping animation.
|
||||
&.md-floating-placeholder {
|
||||
top: -22px;
|
||||
left: -2px;
|
||||
transform: scale(0.75);
|
||||
}
|
||||
|
||||
[dir='rtl'] & {
|
||||
transform-origin: right top;
|
||||
|
||||
&.md-floating-placeholder {
|
||||
left: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Double-check accessibility of this style
|
||||
|
||||
@@ -456,30 +456,35 @@ describe('MdSelect', () => {
|
||||
trigger = fixture.debugElement.query(By.css('.md-select-trigger')).nativeElement;
|
||||
});
|
||||
|
||||
it('should float the placeholder when the panel is open', () => {
|
||||
expect(fixture.componentInstance.select._getPlaceholderState()).toEqual('normal');
|
||||
it('should float the placeholder when the panel is open and unselected', () => {
|
||||
expect(fixture.componentInstance.select._placeholderState)
|
||||
.toEqual('', 'Expected placeholder to initially have a normal position.');
|
||||
|
||||
trigger.click();
|
||||
fixture.detectChanges();
|
||||
expect(fixture.componentInstance.select._getPlaceholderState()).toEqual('floating-ltr');
|
||||
expect(fixture.componentInstance.select._placeholderState)
|
||||
.toEqual('floating-ltr', 'Expected placeholder to animate up to floating position.');
|
||||
|
||||
const backdrop =
|
||||
overlayContainerElement.querySelector('.md-overlay-backdrop') as HTMLElement;
|
||||
backdrop.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.select._getPlaceholderState()).toEqual('normal');
|
||||
expect(fixture.componentInstance.select._placeholderState)
|
||||
.toEqual('', 'Expected placeholder to animate back down to normal position.');
|
||||
});
|
||||
|
||||
it('should float the placeholder when there is a selection', () => {
|
||||
trigger.click();
|
||||
it('should float the placeholder without animation when value is set', () => {
|
||||
fixture.componentInstance.control.setValue('pizza-1');
|
||||
fixture.detectChanges();
|
||||
|
||||
const option = overlayContainerElement.querySelector('md-option') as HTMLElement;
|
||||
option.click();
|
||||
fixture.detectChanges();
|
||||
const placeholderEl =
|
||||
fixture.debugElement.query(By.css('.md-select-placeholder')).nativeElement;
|
||||
|
||||
expect(fixture.componentInstance.select._getPlaceholderState()).toEqual('floating-ltr');
|
||||
expect(placeholderEl.classList)
|
||||
.toContain('md-floating-placeholder', 'Expected placeholder to display as floating.');
|
||||
expect(fixture.componentInstance.select._placeholderState)
|
||||
.toEqual('', 'Expected animation state to be empty to avoid animation.');
|
||||
});
|
||||
|
||||
it('should use the floating-rtl state when the dir is rtl', () => {
|
||||
@@ -487,7 +492,7 @@ describe('MdSelect', () => {
|
||||
|
||||
trigger.click();
|
||||
fixture.detectChanges();
|
||||
expect(fixture.componentInstance.select._getPlaceholderState()).toEqual('floating-rtl');
|
||||
expect(fixture.componentInstance.select._placeholderState).toEqual('floating-rtl');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
+13
-14
@@ -5,7 +5,6 @@ import {
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
Input,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
Optional,
|
||||
Output,
|
||||
@@ -115,6 +114,9 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
|
||||
/** The scroll position of the overlay panel, calculated to center the selected option. */
|
||||
private _scrollTop = 0;
|
||||
|
||||
/** The animation state of the placeholder. */
|
||||
_placeholderState = '';
|
||||
|
||||
/** Manages keyboard events for options in the panel. */
|
||||
_keyManager: ListKeyManager;
|
||||
|
||||
@@ -193,8 +195,8 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
|
||||
@Output() onClose = new EventEmitter();
|
||||
|
||||
constructor(private _element: ElementRef, private _renderer: Renderer,
|
||||
private _ngZone: NgZone, private _viewportRuler: ViewportRuler,
|
||||
@Optional() private _dir: Dir, @Optional() public _control: NgControl) {
|
||||
private _viewportRuler: ViewportRuler, @Optional() private _dir: Dir,
|
||||
@Optional() public _control: NgControl) {
|
||||
if (this._control) {
|
||||
this._control.valueAccessor = this;
|
||||
}
|
||||
@@ -223,12 +225,16 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
|
||||
return;
|
||||
}
|
||||
this._calculateOverlayPosition();
|
||||
this._placeholderState = this._isRtl() ? 'floating-rtl' : 'floating-ltr';
|
||||
this._panelOpen = true;
|
||||
}
|
||||
|
||||
/** Closes the overlay panel and focuses the host element. */
|
||||
close(): void {
|
||||
this._panelOpen = false;
|
||||
if (!this._selected) {
|
||||
this._placeholderState = '';
|
||||
}
|
||||
this._focusHost();
|
||||
}
|
||||
|
||||
@@ -242,7 +248,7 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
|
||||
// the select's child options have been created. It's necessary to call
|
||||
// writeValue() again after the options have been created to ensure any
|
||||
// initial view value is set.
|
||||
this._ngZone.onStable.first().subscribe(() => this.writeValue(value));
|
||||
Promise.resolve(null).then(() => this.writeValue(value));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -300,15 +306,6 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
|
||||
return this._getTriggerRect().width;
|
||||
}
|
||||
|
||||
/** The animation state of the placeholder. */
|
||||
_getPlaceholderState(): string {
|
||||
if (this.panelOpen || this.selected) {
|
||||
return this._isRtl() ? 'floating-rtl' : 'floating-ltr';
|
||||
} else {
|
||||
return 'normal';
|
||||
}
|
||||
}
|
||||
|
||||
/** Ensures the panel opens if activated by the keyboard. */
|
||||
_handleKeydown(event: KeyboardEvent): void {
|
||||
if (event.keyCode === ENTER || event.keyCode === SPACE) {
|
||||
@@ -403,7 +400,9 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
|
||||
private _onSelect(option: MdOption): void {
|
||||
this._selected = option;
|
||||
this._updateOptions();
|
||||
this.close();
|
||||
if (this.panelOpen) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
/** Deselect each option that doesn't match the current selection. */
|
||||
|
||||
Reference in New Issue
Block a user