From 0614eba231f3007157cb579f22e8ff3638c39baa Mon Sep 17 00:00:00 2001 From: Dominic Carretto Date: Wed, 5 Dec 2018 17:34:36 -0500 Subject: [PATCH] feat(notched-outline): Change notched outline to use 3 divs (#1581) As per material-components/material-components-web#4035 --- packages/floating-label/floating-label.ts | 4 +- packages/line-ripple/line-ripple.ts | 4 +- .../notched-outline/notched-outline-module.ts | 2 + packages/notched-outline/notched-outline.ts | 56 ++++++------- packages/textfield/text-field.ts | 81 ++++++++++--------- packages/textfield/textarea.ts | 3 +- 6 files changed, 82 insertions(+), 68 deletions(-) diff --git a/packages/floating-label/floating-label.ts b/packages/floating-label/floating-label.ts index 4649a355c..f5735c84d 100644 --- a/packages/floating-label/floating-label.ts +++ b/packages/floating-label/floating-label.ts @@ -25,7 +25,7 @@ export class MdcFloatingLabel implements AfterContentInit, OnDestroy { @Input() for?: string; - createAdapter() { + private _createAdapter() { return { addClass: (className: string) => this._getHostElement().classList.add(className), removeClass: (className: string) => this._getHostElement().classList.remove(className), @@ -45,7 +45,7 @@ export class MdcFloatingLabel implements AfterContentInit, OnDestroy { public elementRef: ElementRef) { } ngAfterContentInit(): void { - this._foundation = new MDCFloatingLabelFoundation(this.createAdapter()); + this._foundation = new MDCFloatingLabelFoundation(this._createAdapter()); this._loadListeners(); } diff --git a/packages/line-ripple/line-ripple.ts b/packages/line-ripple/line-ripple.ts index 05cfcbdd8..648495491 100644 --- a/packages/line-ripple/line-ripple.ts +++ b/packages/line-ripple/line-ripple.ts @@ -19,7 +19,7 @@ export class MdcLineRipple implements OnInit, OnDestroy { /** Emits whenever the component is destroyed. */ private _destroy = new Subject(); - createAdapter() { + private _createAdapter() { return { addClass: (className: string) => this._getHostElement().classList.add(className), removeClass: (className: string) => this._getHostElement().classList.remove(className), @@ -33,7 +33,7 @@ export class MdcLineRipple implements OnInit, OnDestroy { deactivate(): void, setRippleCenter(xCoordinate: number): void, handleTransitionEnd(evt: TransitionEvent): void - } = new MDCLineRippleFoundation(this.createAdapter()); + } = new MDCLineRippleFoundation(this._createAdapter()); constructor( private _ngZone: NgZone, diff --git a/packages/notched-outline/notched-outline-module.ts b/packages/notched-outline/notched-outline-module.ts index ca5127771..247f62d17 100644 --- a/packages/notched-outline/notched-outline-module.ts +++ b/packages/notched-outline/notched-outline-module.ts @@ -1,8 +1,10 @@ import { NgModule } from '@angular/core'; +import { MdcFloatingLabelModule } from '@angular-mdc/web/floating-label'; import { MdcNotchedOutline } from './notched-outline'; @NgModule({ + imports: [MdcFloatingLabelModule], exports: [MdcNotchedOutline], declarations: [MdcNotchedOutline] }) diff --git a/packages/notched-outline/notched-outline.ts b/packages/notched-outline/notched-outline.ts index 5fa2fa6e7..f62bf7f20 100644 --- a/packages/notched-outline/notched-outline.ts +++ b/packages/notched-outline/notched-outline.ts @@ -2,10 +2,12 @@ import { ChangeDetectionStrategy, Component, ElementRef, + Input, ViewChild, ViewEncapsulation } from '@angular/core'; -import { Platform } from '@angular-mdc/web/common'; + +import { MdcFloatingLabel } from '@angular-mdc/web/floating-label'; import { MDCNotchedOutlineFoundation } from '@material/notched-outline/index'; @@ -13,50 +15,50 @@ import { MDCNotchedOutlineFoundation } from '@material/notched-outline/index'; moduleId: module.id, selector: '[mdcNotchedOutline], mdc-notched-outline', exportAs: 'mdcNotchedOutline', + host: { + 'class': 'mdc-notched-outline', + '[class.mdc-notched-outline--upgraded]': 'label', + '[class.mdc-notched-outline--no-label]': '!label' + }, template: ` -
- - - +
+
+
-
+
`, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None }) export class MdcNotchedOutline { - @ViewChild('notchOutline') _notchOutline!: ElementRef; - @ViewChild('svgpath') _svgpath!: ElementRef; - @ViewChild('notchIdle') _notchIdle!: ElementRef; + @Input() label?: string; + @Input() for?: string; + @ViewChild('notch') _notchElement!: ElementRef; + @ViewChild(MdcFloatingLabel) floatingLabel!: MdcFloatingLabel; - createAdapter() { + private _createAdapter() { return { - getWidth: () => this._notchOutline.nativeElement.offsetWidth, - getHeight: () => this._notchOutline.nativeElement.offsetHeight, - addClass: (className: string) => this._notchOutline.nativeElement.classList.add(className), - removeClass: (className: string) => this._notchOutline.nativeElement.classList.remove(className), - setOutlinePathAttr: (value: string) => this._svgpath.nativeElement.setAttribute('d', value), - getIdleOutlineStyleValue: (propertyName: string) => - this._platform.isBrowser ? window.getComputedStyle(this._notchIdle.nativeElement).getPropertyValue(propertyName) : '' + addClass: (className: string) => this.elementRef.nativeElement.classList.add(className), + removeClass: (className: string) => + this.elementRef.nativeElement.classList.remove(className), + setNotchWidthProperty: (width: number) => + this._notchElement.nativeElement.style.setProperty('width', width > 0 ? width + 'px' : '0') }; } private _foundation: { - notch(notchWidth: number, isRtl: boolean): void, + notch(notchWidth: number): void, closeNotch(): void - } = new MDCNotchedOutlineFoundation(this.createAdapter()); + } = new MDCNotchedOutlineFoundation(this._createAdapter()); - constructor( - private _platform: Platform, - public elementRef: ElementRef) { } + constructor(public elementRef: ElementRef) { } - /** Updates outline selectors and SVG path to open notch. */ - notch(notchWidth: number, isRtl: boolean): void { - this._foundation.notch(notchWidth, isRtl); + /** Updates notched outline to open notch in outline path. */ + notch(notchWidth: number): void { + this._foundation.notch(notchWidth); } - /** Updates the outline selectors to close notch and return it to idle state. */ + /** Updates the notched outline to close notch in outline path. */ closeNotch(): void { this._foundation.closeNotch(); } diff --git a/packages/textfield/text-field.ts b/packages/textfield/text-field.ts index 88c7953b1..66e0c9abd 100644 --- a/packages/textfield/text-field.ts +++ b/packages/textfield/text-field.ts @@ -91,9 +91,9 @@ let nextUniqueId = 0; (change)="onChange($event)" (blur)="onBlur()" /> - + - + `, providers: [ MdcRipple, @@ -138,7 +138,6 @@ export class MdcTextField extends _MdcTextFieldMixinBase implements AfterViewIni const newValue = toBoolean(value); if (newValue !== this._outlined) { this._outlined = toBoolean(newValue); - this._reinitialize(); this.layout(); } } @@ -227,9 +226,9 @@ export class MdcTextField extends _MdcTextFieldMixinBase implements AfterViewIni @Output() readonly blur = new EventEmitter(); @ViewChild('input') _input!: ElementRef; - @ViewChild(MdcFloatingLabel) _floatingLabel!: MdcFloatingLabel; @ViewChild(MdcLineRipple) _lineRipple!: MdcLineRipple; @ViewChild(MdcNotchedOutline) _notchedOutline!: MdcNotchedOutline; + @ViewChild(MdcFloatingLabel) _floatingLabel!: MdcFloatingLabel; @ContentChildren(MdcTextFieldIcon, { descendants: true }) _icons!: QueryList; /** View -> model callback called when value changes */ @@ -248,9 +247,7 @@ export class MdcTextField extends _MdcTextFieldMixinBase implements AfterViewIni addClass: (className: string) => this._getHostElement().classList.add(className), removeClass: (className: string) => this._getHostElement().classList.remove(className), hasClass: (className: string) => this._getHostElement().classList.contains(className), - isFocused: () => this._platform.isBrowser ? document.activeElement! === this._getInputElement() : false, - isRtl: () => - this._platform.isBrowser ? window.getComputedStyle(this._getHostElement()).getPropertyValue('direction') === 'rtl' : false + isFocused: () => this._platform.isBrowser ? document.activeElement! === this._getInputElement() : false }, this._getInputAdapterMethods(), this._getLabelAdapterMethods(), @@ -267,7 +264,7 @@ export class MdcTextField extends _MdcTextFieldMixinBase implements AfterViewIni value: this._value, disabled: this._disabled, validity: { - valid: this._valid ? this._valid : this._platform.isBrowser ? this._input.nativeElement.validity.valid : true, + valid: this._hasErrorState(), badInput: this._platform.isBrowser ? this._input.nativeElement.validity.badInput : false } }; @@ -277,10 +274,10 @@ export class MdcTextField extends _MdcTextFieldMixinBase implements AfterViewIni private _getLabelAdapterMethods() { return { - shakeLabel: (shouldShake: boolean) => this._floatingLabel.shake(shouldShake), - floatLabel: (shouldFloat: boolean) => this._floatingLabel.float(shouldFloat), - hasLabel: () => this._floatingLabel, - getLabelWidth: () => this._floatingLabel ? this._floatingLabel.getWidth() : 0 + shakeLabel: (shouldShake: boolean) => this._getFloatingLabel().shake(shouldShake), + floatLabel: (shouldFloat: boolean) => this._getFloatingLabel().float(shouldFloat), + hasLabel: () => this._hasFloatingLabel(), + getLabelWidth: () => this._hasFloatingLabel() ? this._getFloatingLabel().getWidth() : 0 }; } @@ -307,7 +304,7 @@ export class MdcTextField extends _MdcTextFieldMixinBase implements AfterViewIni private _getOutlineAdapterMethods() { return { hasOutline: () => this._notchedOutline, - notchOutline: (labelWidth: number, isRtl: boolean) => this._notchedOutline.notch(labelWidth, isRtl), + notchOutline: (labelWidth: number) => this._notchedOutline.notch(labelWidth), closeOutline: () => this._notchedOutline.closeNotch() }; } @@ -328,7 +325,7 @@ export class MdcTextField extends _MdcTextFieldMixinBase implements AfterViewIni readonly shouldFloat: boolean, notchOutline(openNotch: boolean): void, setUseNativeValidation(useNativeValidation: boolean): void, - setTransformOrigin(evt: Event): void, + setTransformOrigin(evt: Event | TouchEvent): void, handleTextFieldInteraction(): void, activateFocus(): void, deactivateFocus(): void, @@ -336,10 +333,10 @@ export class MdcTextField extends _MdcTextFieldMixinBase implements AfterViewIni } = new MDCTextFieldFoundation(); constructor( - public _defaultErrorStateMatcher: ErrorStateMatcher, private _platform: Platform, private _changeDetectorRef: ChangeDetectorRef, public elementRef: ElementRef, + public _defaultErrorStateMatcher: ErrorStateMatcher, @Optional() private _parentFormField: MdcFormField, @Optional() private _ripple: MdcRipple, @Self() @Optional() public ngControl: NgControl, @@ -367,10 +364,10 @@ export class MdcTextField extends _MdcTextFieldMixinBase implements AfterViewIni } ngOnDestroy(): void { - this._destroyTextField(); + this._destroy(); } - ngDoCheck() { + ngDoCheck(): void { if (this.ngControl) { // We need to re-evaluate this on every change detection cycle, because there are some // error triggers that we can't subscribe to (e.g. parent form submissions). This means @@ -475,6 +472,22 @@ export class MdcTextField extends _MdcTextFieldMixinBase implements AfterViewIni } } + /** Initializes Text Field's internal state based on the environment state */ + private layout(): void { + this._destroy(); + this.init(); + this._changeDetectorRef.markForCheck(); + + setTimeout(() => { + if (this._outlined) { + this._foundation.notchOutline(this._foundation.shouldFloat); + } + if (this._hasFloatingLabel()) { + this._getFloatingLabel().float(this._foundation.shouldFloat); + } + }); + } + /** Implemented as part of ControlValueAccessor. */ setDisabledState(isDisabled: boolean) { const newValue = toBoolean(isDisabled); @@ -486,19 +499,6 @@ export class MdcTextField extends _MdcTextFieldMixinBase implements AfterViewIni this._changeDetectorRef.markForCheck(); } - /** Recomputes the outline SVG path for the outline element. */ - layout(): void { - setTimeout(() => { - if (this._outlined) { - this._foundation.notchOutline(this._foundation.shouldFloat); - } - - if (this._floatingLabel) { - this._floatingLabel.float(this._foundation.shouldFloat); - } - }); - } - private _checkCustomValidity(): void { Promise.resolve().then(() => { if (this._valid !== undefined) { @@ -533,7 +533,7 @@ export class MdcTextField extends _MdcTextFieldMixinBase implements AfterViewIni } } - private _destroyTextField(): void { + private _destroy(): void { if (this._lineRipple) { this._lineRipple.destroy(); } @@ -543,12 +543,21 @@ export class MdcTextField extends _MdcTextFieldMixinBase implements AfterViewIni this._foundation.destroy(); } - private _reinitialize(): void { - if (this._initialized) { - this._destroyTextField(); - this.init(); - this._changeDetectorRef.markForCheck(); + private _hasErrorState(): boolean { + if (this.ngControl) { + return !this.errorState; } + + return this._valid ? this._valid : this._platform.isBrowser ? + this._input.nativeElement.validity.valid : true; + } + + private _hasFloatingLabel(): boolean { + return this.label && (this._floatingLabel || this._notchedOutline) ? true : false; + } + + private _getFloatingLabel(): MdcFloatingLabel { + return this._floatingLabel || this._notchedOutline.floatingLabel; } private _getInputElement(): HTMLInputElement | HTMLTextAreaElement { diff --git a/packages/textfield/textarea.ts b/packages/textfield/textarea.ts index 73258f31c..7e4b10871 100644 --- a/packages/textfield/textarea.ts +++ b/packages/textfield/textarea.ts @@ -15,6 +15,7 @@ import { MdcTextField } from './text-field'; 'class': 'mdc-text-field', '[class.mdc-text-field--textarea]': 'true', '[class.mdc-text-field--dense]': 'dense', + '[class.mdc-text-field--invalid]': 'errorState' }, template: ` - + `, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None