From cb536183608768274b0ae829619166fa61795bef Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Wed, 18 May 2022 09:31:42 +0200 Subject: [PATCH] refactor(material-experimental/mdc-slide-toggle): remove usage of MDC adapter Bases the MDC slide toggle on top of the non-MDC one, instead of the MDC adapter. --- .../mdc-slide-toggle/slide-toggle.html | 5 +- .../mdc-slide-toggle/slide-toggle.spec.ts | 2 +- .../mdc-slide-toggle/slide-toggle.ts | 216 ++---------------- src/material/slide-toggle/slide-toggle.html | 4 +- src/material/slide-toggle/slide-toggle.ts | 206 ++++++++++------- .../public_api_guard/material/slide-toggle.md | 54 +++-- 6 files changed, 191 insertions(+), 296 deletions(-) diff --git a/src/material-experimental/mdc-slide-toggle/slide-toggle.html b/src/material-experimental/mdc-slide-toggle/slide-toggle.html index 3d3f69102758..ec29dc3c9c4b 100644 --- a/src/material-experimental/mdc-slide-toggle/slide-toggle.html +++ b/src/material-experimental/mdc-slide-toggle/slide-toggle.html @@ -6,6 +6,8 @@ type="button" [class.mdc-switch--selected]="checked" [class.mdc-switch--unselected]="!checked" + [class.mdc-switch--checked]="checked" + [class.mdc-switch--disabled]="disabled" [tabIndex]="tabIndex" [disabled]="disabled" [attr.id]="buttonId" @@ -14,7 +16,8 @@ [attr.aria-labelledby]="_getAriaLabelledBy()" [attr.aria-describedby]="ariaDescribedby" [attr.aria-required]="required || null" - (click)="_handleClick($event)" + [attr.aria-checked]="checked" + (click)="_handleClick()" #switch>
diff --git a/src/material-experimental/mdc-slide-toggle/slide-toggle.spec.ts b/src/material-experimental/mdc-slide-toggle/slide-toggle.spec.ts index 2506e20db667..f24cb8dc54a9 100644 --- a/src/material-experimental/mdc-slide-toggle/slide-toggle.spec.ts +++ b/src/material-experimental/mdc-slide-toggle/slide-toggle.spec.ts @@ -220,7 +220,7 @@ describe('MDC-based MatSlideToggle without forms', () => { // We fall back to pointing to the label if a value isn't provided. expect(buttonElement.getAttribute('aria-labelledby')).toMatch( - /mat-mdc-slide-toggle-label-\d+/, + /mat-mdc-slide-toggle-\d+-label/, ); })); diff --git a/src/material-experimental/mdc-slide-toggle/slide-toggle.ts b/src/material-experimental/mdc-slide-toggle/slide-toggle.ts index ee18a8d3c1fb..df8401a5630e 100644 --- a/src/material-experimental/mdc-slide-toggle/slide-toggle.ts +++ b/src/material-experimental/mdc-slide-toggle/slide-toggle.ts @@ -10,40 +10,25 @@ import { ChangeDetectionStrategy, Component, ViewEncapsulation, - AfterViewInit, - OnDestroy, forwardRef, ViewChild, ElementRef, - Input, - Output, - EventEmitter, ChangeDetectorRef, Attribute, Inject, Optional, } from '@angular/core'; -import {deprecated} from '@material/switch'; -import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; -import { - BooleanInput, - coerceBooleanProperty, - coerceNumberProperty, - NumberInput, -} from '@angular/cdk/coercion'; +import {NG_VALUE_ACCESSOR} from '@angular/forms'; import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; -import {ThemePalette} from '@angular/material-experimental/mdc-core'; import {FocusMonitor} from '@angular/cdk/a11y'; +import {_MatSlideToggleBase} from '@angular/material/slide-toggle'; import { MAT_SLIDE_TOGGLE_DEFAULT_OPTIONS, MatSlideToggleDefaultOptions, } from './slide-toggle-config'; -// Increasing integer for generating unique ids for slide-toggle components. -let nextUniqueId = 0; - /** @docs-private */ -export const MAT_SLIDE_TOGGLE_VALUE_ACCESSOR: any = { +export const MAT_SLIDE_TOGGLE_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MatSlideToggle), multi: true, @@ -63,6 +48,7 @@ export class MatSlideToggleChange { selector: 'mat-slide-toggle', templateUrl: 'slide-toggle.html', styleUrls: ['slide-toggle.css'], + inputs: ['disabled', 'disableRipple', 'color', 'tabIndex'], host: { 'class': 'mat-mdc-slide-toggle', '[id]': 'id', @@ -71,9 +57,6 @@ export class MatSlideToggleChange { '[attr.aria-label]': 'null', '[attr.name]': 'null', '[attr.aria-labelledby]': 'null', - '[class.mat-primary]': 'color === "primary"', - '[class.mat-accent]': 'color !== "primary" && color !== "warn"', - '[class.mat-warn]': 'color === "warn"', '[class.mat-mdc-slide-toggle-focused]': '_focused', '[class.mat-mdc-slide-toggle-checked]': 'checked', '[class._mat-animation-noopable]': '_noopAnimations', @@ -83,116 +66,9 @@ export class MatSlideToggleChange { changeDetection: ChangeDetectionStrategy.OnPush, providers: [MAT_SLIDE_TOGGLE_VALUE_ACCESSOR], }) -export class MatSlideToggle implements ControlValueAccessor, AfterViewInit, OnDestroy { - private _onChange = (_: any) => {}; - private _onTouched = () => {}; - - private _uniqueId: string = `mat-mdc-slide-toggle-${++nextUniqueId}`; - private _required: boolean = false; - private _checked: boolean = false; - private _foundation: deprecated.MDCSwitchFoundation; - private _adapter: deprecated.MDCSwitchAdapter = { - addClass: className => this._switchElement.nativeElement.classList.add(className), - removeClass: className => this._switchElement.nativeElement.classList.remove(className), - setNativeControlChecked: checked => (this._checked = checked), - setNativeControlDisabled: disabled => (this._disabled = disabled), - setNativeControlAttr: (name, value) => { - this._switchElement.nativeElement.setAttribute(name, value); - }, - }; - - /** Whether the slide toggle is currently focused. */ - _focused: boolean; - - /** Whether noop animations are enabled. */ - _noopAnimations: boolean; - +export class MatSlideToggle extends _MatSlideToggleBase { /** Unique ID for the label element. */ - _labelId = `mat-mdc-slide-toggle-label-${++nextUniqueId}`; - - /** The color palette for this slide toggle. */ - @Input() color: ThemePalette; - - /** Name value will be applied to the button element if present. */ - @Input() name: string | null = null; - - /** A unique id for the slide-toggle button. If none is supplied, it will be auto-generated. */ - @Input() id: string = this._uniqueId; - - /** Tabindex for the input element. */ - @Input() - get tabIndex(): number { - return this._tabIndex; - } - set tabIndex(value: NumberInput) { - this._tabIndex = coerceNumberProperty(value); - } - private _tabIndex: number; - - /** Whether the label should appear after or before the slide-toggle. Defaults to 'after'. */ - @Input() labelPosition: 'before' | 'after' = 'after'; - - /** Used to set the aria-label attribute on the underlying button element. */ - @Input('aria-label') ariaLabel: string | null = null; - - /** Used to set the aria-labelledby attribute on the underlying button element. */ - @Input('aria-labelledby') ariaLabelledby: string | null = null; - - /** Used to set the aria-describedby attribute on the underlying button element. */ - @Input('aria-describedby') ariaDescribedby: string; - - /** Whether the slide-toggle is required. */ - @Input() - get required(): boolean { - return this._required; - } - set required(value: BooleanInput) { - this._required = coerceBooleanProperty(value); - } - - /** Whether the slide-toggle element is checked or not. */ - @Input() - get checked(): boolean { - return this._checked; - } - set checked(value: BooleanInput) { - this._checked = coerceBooleanProperty(value); - - if (this._foundation) { - this._foundation.setChecked(this._checked); - } - } - - /** Whether to disable the ripple on this checkbox. */ - @Input() - get disableRipple(): boolean { - return this._disableRipple; - } - set disableRipple(disableRipple: BooleanInput) { - this._disableRipple = coerceBooleanProperty(disableRipple); - } - private _disableRipple = false; - - /** Whether the slide toggle is disabled. */ - @Input() - get disabled(): boolean { - return this._disabled; - } - set disabled(disabled: BooleanInput) { - this._disabled = coerceBooleanProperty(disabled); - - if (this._foundation) { - this._foundation.setDisabled(this._disabled); - } - } - private _disabled = false; - - /** An event will be dispatched each time the slide-toggle changes its value. */ - @Output() readonly change: EventEmitter = - new EventEmitter(); - - /** Event will be dispatched each time the slide-toggle input is toggled. */ - @Output() readonly toggleChange: EventEmitter = new EventEmitter(); + _labelId: string; /** Returns the unique id for the visual hidden button. */ get buttonId(): string { @@ -203,51 +79,29 @@ export class MatSlideToggle implements ControlValueAccessor, AfterViewInit, OnDe @ViewChild('switch') _switchElement: ElementRef; constructor( - private _elementRef: ElementRef, - private _focusMonitor: FocusMonitor, - private _changeDetectorRef: ChangeDetectorRef, + elementRef: ElementRef, + focusMonitor: FocusMonitor, + changeDetectorRef: ChangeDetectorRef, @Attribute('tabindex') tabIndex: string, @Inject(MAT_SLIDE_TOGGLE_DEFAULT_OPTIONS) - public defaults: MatSlideToggleDefaultOptions, + defaults: MatSlideToggleDefaultOptions, @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string, ) { - this.tabIndex = parseInt(tabIndex) || 0; - this.color = defaults.color || 'accent'; - this._noopAnimations = animationMode === 'NoopAnimations'; - } - - ngAfterViewInit() { - const foundation = (this._foundation = new deprecated.MDCSwitchFoundation(this._adapter)); - foundation.setDisabled(this.disabled); - foundation.setChecked(this.checked); - - this._focusMonitor.monitor(this._elementRef, true).subscribe(focusOrigin => { - if (focusOrigin === 'keyboard' || focusOrigin === 'program') { - this._focused = true; - } else if (!focusOrigin) { - // When a focused element becomes disabled, the browser *immediately* fires a blur event. - // Angular does not expect events to be raised during change detection, so any state - // change (such as a form control's ng-touched) will cause a changed-after-checked error. - // See https://github.com/angular/angular/issues/17793. To work around this, we defer - // telling the form control it has been touched until the next tick. - Promise.resolve().then(() => { - this._focused = false; - this._onTouched(); - this._changeDetectorRef.markForCheck(); - }); - } - }); - } - - ngOnDestroy() { - this._focusMonitor.stopMonitoring(this._elementRef); - this._foundation?.destroy(); + super( + elementRef, + focusMonitor, + changeDetectorRef, + tabIndex, + defaults, + animationMode, + 'mat-mdc-slide-toggle-', + ); + this._labelId = this._uniqueId + '-label'; } /** Method being called whenever the underlying button is clicked. */ - _handleClick(event: Event) { + _handleClick() { this.toggleChange.emit(); - this._foundation.handleChange(event); if (!this.defaults.disableToggleValue) { this.checked = !this.checked; @@ -256,37 +110,13 @@ export class MatSlideToggle implements ControlValueAccessor, AfterViewInit, OnDe } } - /** Implemented as part of ControlValueAccessor. */ - writeValue(value: any): void { - this.checked = !!value; - this._changeDetectorRef.markForCheck(); - } - - /** Implemented as part of ControlValueAccessor. */ - registerOnChange(fn: any): void { - this._onChange = fn; - } - - /** Implemented as part of ControlValueAccessor. */ - registerOnTouched(fn: any): void { - this._onTouched = fn; - } - - /** Implemented as a part of ControlValueAccessor. */ - setDisabledState(isDisabled: boolean): void { - this.disabled = isDisabled; - this._changeDetectorRef.markForCheck(); - } - /** Focuses the slide-toggle. */ focus(): void { this._switchElement.nativeElement.focus(); } - /** Toggles the checked state of the slide-toggle. */ - toggle(): void { - this.checked = !this.checked; - this._onChange(this.checked); + protected _createChangeEvent(isChecked: boolean) { + return new MatSlideToggleChange(this, isChecked); } _getAriaLabelledBy() { diff --git a/src/material/slide-toggle/slide-toggle.html b/src/material/slide-toggle/slide-toggle.html index c00d1406ea3f..c7038930c12a 100644 --- a/src/material/slide-toggle/slide-toggle.html +++ b/src/material/slide-toggle/slide-toggle.html @@ -1,5 +1,5 @@