mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
update(ripple): remove deprecated speed factor option (#12258)
* update(ripple): remove deprecated speed factor option. * Removes the deprecated `[matRippleSpeedFactor]` and global `baseSpeedFactor` option. * Adds update rules that can automatically switch to the new API (as much as possible) and even calculate the new durations based on the previously specified speed factor. BREAKING CHANGE: deprecated `[matRippleSpeedFactor]` and `baseSpeedFactor` for the ripples have been removed. Use the new animation config instead. * Add test case; fix NodeJS version target circular dependency; Support for rule version constraints
This commit is contained in:
committed by
mmalerba
parent
e4c6cc98bf
commit
a6c91bce3c
@@ -234,10 +234,9 @@ export class MatChip extends _MatChipMixinBase implements FocusableOption, OnDes
|
||||
this._chipRipple.setupTriggerEvents(_elementRef.nativeElement);
|
||||
|
||||
if (globalOptions) {
|
||||
// TODO(paul): Do not copy each option manually. Allow dynamic global option changes: #9729
|
||||
this._ripplesGloballyDisabled = !!globalOptions.disabled;
|
||||
// TODO(paul): Once the speedFactor is removed, we no longer need to copy each single option.
|
||||
this.rippleConfig = {
|
||||
speedFactor: globalOptions.baseSpeedFactor,
|
||||
animation: globalOptions.animation,
|
||||
terminateOnPointerUp: globalOptions.terminateOnPointerUp,
|
||||
};
|
||||
|
||||
@@ -17,11 +17,6 @@ export type RippleConfig = {
|
||||
persistent?: boolean;
|
||||
animation?: RippleAnimationConfig;
|
||||
terminateOnPointerUp?: boolean;
|
||||
/**
|
||||
* @deprecated Use the `animation` property instead.
|
||||
* @breaking-change 7.0.0
|
||||
*/
|
||||
speedFactor?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -140,7 +135,7 @@ export class RippleRenderer {
|
||||
const radius = config.radius || distanceToFurthestCorner(x, y, containerRect);
|
||||
const offsetX = x - containerRect.left;
|
||||
const offsetY = y - containerRect.top;
|
||||
const duration = animationConfig.enterDuration / (config.speedFactor || 1);
|
||||
const duration = animationConfig.enterDuration;
|
||||
|
||||
const ripple = document.createElement('div');
|
||||
ripple.classList.add('mat-ripple-element');
|
||||
|
||||
@@ -62,7 +62,7 @@ describe('MatRipple', () => {
|
||||
fixture = TestBed.createComponent(BasicRippleContainer);
|
||||
fixture.detectChanges();
|
||||
|
||||
rippleTarget = fixture.nativeElement.querySelector('[mat-ripple]');
|
||||
rippleTarget = fixture.nativeElement.querySelector('.mat-ripple');
|
||||
rippleDirective = fixture.componentInstance.ripple;
|
||||
});
|
||||
|
||||
@@ -262,7 +262,7 @@ describe('MatRipple', () => {
|
||||
fixture = TestBed.createComponent(RippleContainerWithNgIf);
|
||||
fixture.detectChanges();
|
||||
|
||||
rippleTarget = fixture.debugElement.nativeElement.querySelector('[mat-ripple]');
|
||||
rippleTarget = fixture.debugElement.nativeElement.querySelector('.mat-ripple');
|
||||
|
||||
fixture.componentInstance.isDestroyed = true;
|
||||
fixture.detectChanges();
|
||||
@@ -377,7 +377,7 @@ describe('MatRipple', () => {
|
||||
fixture = TestBed.createComponent(BasicRippleContainer);
|
||||
fixture.detectChanges();
|
||||
|
||||
rippleTarget = fixture.nativeElement.querySelector('[mat-ripple]');
|
||||
rippleTarget = fixture.nativeElement.querySelector('.mat-ripple');
|
||||
rippleDirective = fixture.componentInstance.ripple;
|
||||
});
|
||||
|
||||
@@ -492,7 +492,7 @@ describe('MatRipple', () => {
|
||||
fixture = TestBed.createComponent(testComponent);
|
||||
fixture.detectChanges();
|
||||
|
||||
rippleTarget = fixture.nativeElement.querySelector('[mat-ripple]');
|
||||
rippleTarget = fixture.nativeElement.querySelector('.mat-ripple');
|
||||
rippleDirective = fixture.componentInstance.ripple;
|
||||
}
|
||||
|
||||
@@ -534,41 +534,6 @@ describe('MatRipple', () => {
|
||||
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should support changing the baseSpeedFactor', fakeAsync(() => {
|
||||
createTestComponent({ baseSpeedFactor: 0.5 });
|
||||
|
||||
dispatchMouseEvent(rippleTarget, 'mousedown');
|
||||
dispatchMouseEvent(rippleTarget, 'mouseup');
|
||||
|
||||
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
|
||||
|
||||
// Calculates the speedFactor for the duration. Those factors needs to be inverted, because
|
||||
// a lower speed factor, will make the duration longer. For example: 0.5 => 2x duration.
|
||||
let fadeInFactor = 1 / 0.5;
|
||||
|
||||
// Calculates the duration for fading-in and fading-out the ripple.
|
||||
tick(enterDuration * fadeInFactor + exitDuration);
|
||||
|
||||
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
|
||||
}));
|
||||
|
||||
it('should combine individual speed factor with baseSpeedFactor', fakeAsync(() => {
|
||||
createTestComponent({ baseSpeedFactor: 0.5 });
|
||||
|
||||
rippleDirective.launch(0, 0, { speedFactor: 1.5 });
|
||||
|
||||
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
|
||||
|
||||
// Calculates the speedFactor for the duration. Those factors needs to be inverted, because
|
||||
// a lower speed factor, will make the duration longer. For example: 0.5 => 2x duration.
|
||||
let fadeInFactor = 1 / (0.5 * 1.5);
|
||||
|
||||
// Calculates the duration for fading-in and fading-out the ripple.
|
||||
tick(enterDuration * fadeInFactor + exitDuration);
|
||||
|
||||
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
|
||||
}));
|
||||
|
||||
it('should support changing the animation duration', fakeAsync(() => {
|
||||
createTestComponent({
|
||||
animation: {enterDuration: 100, exitDuration: 100}
|
||||
@@ -619,7 +584,7 @@ describe('MatRipple', () => {
|
||||
fixture = TestBed.createComponent(BasicRippleContainer);
|
||||
fixture.detectChanges();
|
||||
|
||||
rippleTarget = fixture.nativeElement.querySelector('[mat-ripple]');
|
||||
rippleTarget = fixture.nativeElement.querySelector('.mat-ripple');
|
||||
rippleDirective = fixture.componentInstance.ripple;
|
||||
});
|
||||
|
||||
@@ -637,7 +602,7 @@ describe('MatRipple', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
controller = fixture.debugElement.componentInstance;
|
||||
rippleTarget = fixture.debugElement.nativeElement.querySelector('[mat-ripple]');
|
||||
rippleTarget = fixture.debugElement.nativeElement.querySelector('.mat-ripple');
|
||||
});
|
||||
|
||||
it('sets ripple color', () => {
|
||||
@@ -757,7 +722,7 @@ describe('MatRipple', () => {
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<div id="container" #ripple="matRipple" mat-ripple [matRippleSpeedFactor]="0"
|
||||
<div id="container" #ripple="matRipple" matRipple
|
||||
style="position: relative; width:300px; height:200px;">
|
||||
</div>
|
||||
`,
|
||||
@@ -769,8 +734,7 @@ class BasicRippleContainer {
|
||||
@Component({
|
||||
template: `
|
||||
<div id="container" style="position: relative; width:300px; height:200px;"
|
||||
mat-ripple
|
||||
[matRippleSpeedFactor]="0"
|
||||
matRipple
|
||||
[matRippleTrigger]="trigger"
|
||||
[matRippleCentered]="centered"
|
||||
[matRippleRadius]="radius"
|
||||
@@ -792,11 +756,11 @@ class RippleContainerWithInputBindings {
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `<div id="container" #ripple="matRipple" mat-ripple></div>`,
|
||||
template: `<div id="container" #ripple="matRipple" matRipple></div>`,
|
||||
})
|
||||
class RippleContainerWithoutBindings {}
|
||||
|
||||
@Component({ template: `<div id="container" mat-ripple [matRippleSpeedFactor]="0"
|
||||
@Component({ template: `<div id="container" matRipple
|
||||
*ngIf="!isDestroyed"></div>` })
|
||||
class RippleContainerWithNgIf {
|
||||
@ViewChild(MatRipple) ripple: MatRipple;
|
||||
|
||||
@@ -37,15 +37,6 @@ export interface RippleGlobalOptions {
|
||||
*/
|
||||
animation?: RippleAnimationConfig;
|
||||
|
||||
/**
|
||||
* If set, the default duration of the fade-in animation is divided by this value. For example,
|
||||
* setting it to 0.5 will cause the ripple fade-in animation to take twice as long.
|
||||
* A changed speedFactor will not affect the fade-out duration of the ripples.
|
||||
* @deprecated Use the `animation` global option instead.
|
||||
* @breaking-change 7.0.0
|
||||
*/
|
||||
baseSpeedFactor?: number;
|
||||
|
||||
/**
|
||||
* Whether ripples should start fading out immediately after the mouse our touch is released. By
|
||||
* default, ripples will wait for the enter animation to complete and for mouse or touch release.
|
||||
@@ -86,15 +77,6 @@ export class MatRipple implements OnInit, OnDestroy, RippleTarget {
|
||||
*/
|
||||
@Input('matRippleRadius') radius: number = 0;
|
||||
|
||||
/**
|
||||
* If set, the normal duration of ripple animations is divided by this value. For example,
|
||||
* setting it to 0.5 will cause the animations to take twice as long.
|
||||
* A changed speedFactor will not modify the fade-out duration of the ripples.
|
||||
* @deprecated Use the [matRippleAnimation] binding instead.
|
||||
* @breaking-change 7.0.0
|
||||
*/
|
||||
@Input('matRippleSpeedFactor') speedFactor: number = 1;
|
||||
|
||||
/**
|
||||
* Configuration for the ripple animation. Allows modifying the enter and exit animation
|
||||
* duration of the ripples. The animation durations will be overwritten if the
|
||||
@@ -174,7 +156,6 @@ export class MatRipple implements OnInit, OnDestroy, RippleTarget {
|
||||
color: this.color,
|
||||
animation: {...this._globalOptions.animation, ...this.animation},
|
||||
terminateOnPointerUp: this._globalOptions.terminateOnPointerUp,
|
||||
speedFactor: this.speedFactor * (this._globalOptions.baseSpeedFactor || 1),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -7,14 +7,9 @@
|
||||
*/
|
||||
|
||||
import {Rule} from '@angular-devkit/schematics';
|
||||
import {TargetVersion} from './target-version';
|
||||
import {createUpdateRule} from './update';
|
||||
|
||||
/** Possible versions that can be automatically migrated by `ng update`. */
|
||||
export enum TargetVersion {
|
||||
V6,
|
||||
V7,
|
||||
}
|
||||
|
||||
/** Entry point for the migration schematics with target of Angular Material 6.0.0 */
|
||||
export function updateToV6(): Rule {
|
||||
return createUpdateRule(TargetVersion.V6);
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {TargetVersion} from '../../target-version';
|
||||
import {VersionChanges} from '../transform-change-data';
|
||||
import {TargetVersion} from '../../index';
|
||||
|
||||
export interface MaterialAttributeSelectorData {
|
||||
/** The attribute name to replace. */
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {TargetVersion} from '../../index';
|
||||
import {TargetVersion} from '../../target-version';
|
||||
import {VersionChanges} from '../transform-change-data';
|
||||
|
||||
export interface MaterialClassNameData {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {TargetVersion} from '../../index';
|
||||
import {TargetVersion} from '../../target-version';
|
||||
import {VersionChanges} from '../transform-change-data';
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {TargetVersion} from '../../index';
|
||||
import {TargetVersion} from '../../target-version';
|
||||
import {VersionChanges} from '../transform-change-data';
|
||||
|
||||
export interface MaterialCssSelectorData {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {TargetVersion} from '../../index';
|
||||
import {TargetVersion} from '../../target-version';
|
||||
import {VersionChanges} from '../transform-change-data';
|
||||
|
||||
export interface MaterialElementSelectorData {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {TargetVersion} from '../../index';
|
||||
import {TargetVersion} from '../../target-version';
|
||||
import {VersionChanges} from '../transform-change-data';
|
||||
|
||||
export interface MaterialInputNameData {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {TargetVersion} from '../../index';
|
||||
import {TargetVersion} from '../../target-version';
|
||||
import {VersionChanges} from '../transform-change-data';
|
||||
|
||||
export interface MaterialMethodCallData {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {TargetVersion} from '../../index';
|
||||
import {TargetVersion} from '../../target-version';
|
||||
import {VersionChanges} from '../transform-change-data';
|
||||
|
||||
export interface MaterialOutputNameData {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {TargetVersion} from '../../index';
|
||||
import {TargetVersion} from '../../target-version';
|
||||
import {VersionChanges} from '../transform-change-data';
|
||||
|
||||
export interface MaterialPropertyNameData {
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {TargetVersion} from '../index';
|
||||
|
||||
import {TargetVersion} from '../target-version';
|
||||
|
||||
export type VersionChanges<T> = {
|
||||
[target in TargetVersion]?: ReadableChange<T>[];
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
/** Converts the specified speed factor into the exact static enter duration. */
|
||||
export function convertSpeedFactorToDuration(factor: number) {
|
||||
// Based on the numeric speed factor value that only affected the `enterDuration` we can
|
||||
// now calculate the exact `enterDuration`. 450ms is the enter duration without factor.
|
||||
return 450 / (factor || 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a runtime TypeScript expression that can be used in order to calculate the duration
|
||||
* from the speed factor expression that couldn't be statically analyzed.
|
||||
*
|
||||
* @param speedFactorValue Speed factor expression that couldn't be statically analyzed.
|
||||
*/
|
||||
export function createSpeedFactorConvertExpression(speedFactorValue: string): string {
|
||||
// To be sure that the speed factor value expression is calculated properly, we need to add
|
||||
// the according parenthesis.
|
||||
return `450 / (${speedFactorValue})`;
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {bold, red} from 'chalk';
|
||||
import {ProgramAwareRuleWalker, RuleFailure, Rules} from 'tslint';
|
||||
import * as ts from 'typescript';
|
||||
import {
|
||||
convertSpeedFactorToDuration,
|
||||
createSpeedFactorConvertExpression,
|
||||
} from './ripple-speed-factor';
|
||||
|
||||
/**
|
||||
* Note that will be added whenever a speed factor expression has been converted to calculate
|
||||
* the according duration. This note should encourage people to clean up their code by switching
|
||||
* away from the speed factors to explicit durations.
|
||||
*/
|
||||
const removeNote = `TODO: Cleanup duration calculation.`;
|
||||
|
||||
/**
|
||||
* Rule that walks through every property assignment and switches the global `baseSpeedFactor`
|
||||
* ripple option to the new global animation config. Also updates every class member assignment
|
||||
* that refers to MatRipple#speedFactor.
|
||||
*/
|
||||
export class Rule extends Rules.TypedRule {
|
||||
applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[] {
|
||||
return this.applyWithWalker(new Walker(sourceFile, this.getOptions(), program));
|
||||
}
|
||||
}
|
||||
|
||||
export class Walker extends ProgramAwareRuleWalker {
|
||||
|
||||
/** Switches binary expressions (e.g. myRipple.speedFactor = 0.5) to the new animation config. */
|
||||
visitBinaryExpression(expression: ts.BinaryExpression) {
|
||||
if (!ts.isPropertyAccessExpression(expression.left)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Left side expression consists of target object and property name (e.g. myInstance.val)
|
||||
const leftExpression = expression.left as ts.PropertyAccessExpression;
|
||||
const targetTypeNode = this.getTypeChecker().getTypeAtLocation(leftExpression.expression);
|
||||
|
||||
if (!targetTypeNode.symbol) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetTypeName = targetTypeNode.symbol.getName();
|
||||
const propertyName = leftExpression.name.getText();
|
||||
|
||||
if (targetTypeName === 'MatRipple' && propertyName === 'speedFactor') {
|
||||
if (ts.isNumericLiteral(expression.right)) {
|
||||
const numericValue = parseFloat(expression.right.text);
|
||||
const newEnterDurationValue = convertSpeedFactorToDuration(numericValue);
|
||||
|
||||
// Replace the `speedFactor` property name with `animation`.
|
||||
const propertyNameReplacement = this.createReplacement(leftExpression.name.getStart(),
|
||||
leftExpression.name.getWidth(), 'animation');
|
||||
|
||||
// Replace the value assignment with the new animation config.
|
||||
const rightExpressionReplacement = this.createReplacement(expression.right.getStart(),
|
||||
expression.right.getWidth(), `{enterDuration: ${newEnterDurationValue}}`);
|
||||
|
||||
this.addFailureAtNode(expression,
|
||||
`Found deprecated variable assignment for "${bold('MatRipple')}#${red('speedFactor')}"`,
|
||||
[propertyNameReplacement, rightExpressionReplacement]);
|
||||
} else {
|
||||
// Handle the right expression differently if the previous speed factor value can't
|
||||
// be resolved statically. In that case, we just create a TypeScript expression that
|
||||
// calculates the explicit duration based on the non-static speed factor expression.
|
||||
const newExpression = createSpeedFactorConvertExpression(expression.right.getText());
|
||||
|
||||
// Replace the `speedFactor` property name with `animation`.
|
||||
const propertyNameReplacement = this.createReplacement(leftExpression.name.getStart(),
|
||||
leftExpression.name.getWidth(), 'animation');
|
||||
|
||||
// Replace the value assignment with the new animation config and remove TODO.
|
||||
const rightExpressionReplacement = this.createReplacement(expression.right.getStart(),
|
||||
expression.right.getWidth(), `/** ${removeNote} */ {enterDuration: ${newExpression}}`);
|
||||
|
||||
this.addFailureAtNode(expression,
|
||||
`Found deprecated variable assignment for "${bold('MatRipple')}#${red('speedFactor')}"`,
|
||||
[propertyNameReplacement, rightExpressionReplacement]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches a potential global option `baseSpeedFactor` to the new animation config. For this
|
||||
* we assume that the `baseSpeedFactor` is not used in combination with individual speed factors.
|
||||
*/
|
||||
visitPropertyAssignment(assignment: ts.PropertyAssignment) {
|
||||
// For switching the `baseSpeedFactor` global option we expect the property assignment
|
||||
// to be inside of a normal object literal. Custom ripple global options cannot be switched
|
||||
// automatically.
|
||||
if (!ts.isObjectLiteralExpression(assignment.parent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The assignment consists of a name (key) and initializer (value).
|
||||
if (assignment.name.getText() !== 'baseSpeedFactor') {
|
||||
return;
|
||||
}
|
||||
|
||||
// We could technically lazily check for the MAT_RIPPLE_GLOBAL_OPTIONS injection token to
|
||||
// be present, but it's not right to assume that everyone sets the ripple global options
|
||||
// immediately in the provider object (e.g. it can happen that someone just imports the
|
||||
// config from a separate file).
|
||||
|
||||
const {initializer, name} = assignment;
|
||||
|
||||
if (ts.isNumericLiteral(initializer)) {
|
||||
const numericValue = parseFloat(initializer.text);
|
||||
const newEnterDurationValue = convertSpeedFactorToDuration(numericValue);
|
||||
|
||||
const keyNameReplacement = this.createReplacement(name.getStart(),
|
||||
assignment.name.getWidth(), `animation`);
|
||||
|
||||
const initializerReplacement = this.createReplacement(initializer.getStart(),
|
||||
initializer.getWidth(), `{enterDuration: ${newEnterDurationValue}}`);
|
||||
|
||||
this.addFailureAtNode(assignment,
|
||||
`Found deprecated property assignment for "${bold('MAT_RIPPLE_GLOBAL_OPTIONS')}:` +
|
||||
`${red('baseSpeedFactor')}"`,
|
||||
[keyNameReplacement, initializerReplacement]);
|
||||
} else {
|
||||
// Handle the right expression differently if the previous speed factor value can't
|
||||
// be resolved statically. In that case, we just create a TypeScript expression that
|
||||
// calculates the explicit duration based on the non-static speed factor expression.
|
||||
const newExpression = createSpeedFactorConvertExpression(initializer.getText());
|
||||
|
||||
// Replace the `baseSpeedFactor` property name with `animation`.
|
||||
const propertyNameReplacement = this.createReplacement(name.getStart(),
|
||||
name.getWidth(), 'animation');
|
||||
|
||||
// Replace the value assignment with the new animation config and remove TODO.
|
||||
const rightExpressionReplacement = this.createReplacement(initializer.getStart(),
|
||||
initializer.getWidth(), `/** ${removeNote} */ {enterDuration: ${newExpression}}`);
|
||||
|
||||
this.addFailureAtNode(assignment,
|
||||
`Found a deprecated property assignment for "${bold('MAT_RIPPLE_GLOBAL_OPTIONS')}:` +
|
||||
`${red('baseSpeedFactor')}.`,
|
||||
[propertyNameReplacement, rightExpressionReplacement]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Replacement, RuleFailure, Rules} from 'tslint';
|
||||
import * as ts from 'typescript';
|
||||
import {ExternalResource} from '../../tslint/component-file';
|
||||
import {ComponentWalker} from '../../tslint/component-walker';
|
||||
import {
|
||||
addFailureAtReplacement,
|
||||
createExternalReplacementFailure,
|
||||
} from '../../tslint/rule-failures';
|
||||
import {
|
||||
convertSpeedFactorToDuration,
|
||||
createSpeedFactorConvertExpression,
|
||||
} from './ripple-speed-factor';
|
||||
|
||||
/** Regular expression that matches [matRippleSpeedFactor]="$NUMBER" in templates. */
|
||||
const speedFactorNumberRegex = /\[matRippleSpeedFactor]="(\d+(?:\.\d+)?)"/g;
|
||||
|
||||
/** Regular expression that matches [matRippleSpeedFactor]="$NOT_A_NUMBER" in templates. */
|
||||
const speedFactorNotParseable = /\[matRippleSpeedFactor]="(?!\d+(?:\.\d+)?")(.*)"/g;
|
||||
|
||||
/** Failure message that will be shown if a speed factor is set to a readable number. */
|
||||
const failureMessageReadableNumber =
|
||||
'Detected deprecated [matRippleSpeedFactor] input binding with readable number.';
|
||||
|
||||
/** Failure message that will be shown if a speed factor is set to a non-parseable value. */
|
||||
const failureMessageNonParseableValue =
|
||||
'Detected deprecated [matRippleSpeedFactor] input binding with non-parseable value.';
|
||||
|
||||
/**
|
||||
* Rule that walks through every inline or external template and updates the deprecated
|
||||
* [matRippleSpeedFactor] to [matRippleAnimation].
|
||||
*/
|
||||
export class Rule extends Rules.AbstractRule {
|
||||
apply(sourceFile: ts.SourceFile): RuleFailure[] {
|
||||
return this.applyWithWalker(new Walker(sourceFile, this.getOptions()));
|
||||
}
|
||||
}
|
||||
|
||||
export class Walker extends ComponentWalker {
|
||||
|
||||
visitInlineTemplate(template: ts.StringLiteralLike) {
|
||||
this._createReplacementsForContent(template, template.getText())
|
||||
.forEach(data => addFailureAtReplacement(this, data.failureMessage, data.replacement));
|
||||
}
|
||||
|
||||
visitExternalTemplate(template: ExternalResource) {
|
||||
this._createReplacementsForContent(template, template.getFullText())
|
||||
.map(data => createExternalReplacementFailure(template, data.failureMessage,
|
||||
this.getRuleName(), data.replacement))
|
||||
.forEach(failure => this.addFailure(failure));
|
||||
}
|
||||
|
||||
private _createReplacementsForContent(node: ts.Node, templateText: string) {
|
||||
const replacements: {failureMessage: string, replacement: Replacement}[] = [];
|
||||
const startPos = node.getStart();
|
||||
|
||||
let match: RegExpMatchArray | null;
|
||||
|
||||
while ((match = speedFactorNumberRegex.exec(templateText)) !== null) {
|
||||
const newEnterDuration = convertSpeedFactorToDuration(parseFloat(match[1]));
|
||||
const fix = this.createReplacement(startPos + match.index!, match[0].length,
|
||||
`[matRippleAnimation]="{enterDuration: ${newEnterDuration}}"`);
|
||||
|
||||
replacements.push({
|
||||
replacement: fix,
|
||||
failureMessage: failureMessageReadableNumber,
|
||||
});
|
||||
}
|
||||
|
||||
while ((match = speedFactorNotParseable.exec(templateText)) !== null) {
|
||||
const newDurationExpression = createSpeedFactorConvertExpression(match[1]);
|
||||
const fix = this.createReplacement(startPos + match.index!, match[0].length,
|
||||
`[matRippleAnimation]="{enterDuration: (${newDurationExpression})}"`);
|
||||
|
||||
replacements.push({
|
||||
replacement: fix,
|
||||
failureMessage: failureMessageNonParseableValue,
|
||||
});
|
||||
}
|
||||
|
||||
return replacements;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
/** Possible versions that can be automatically migrated by `ng update`. */
|
||||
export enum TargetVersion {
|
||||
V6,
|
||||
V7,
|
||||
}
|
||||
@@ -10,7 +10,8 @@ describe('v7 upgrade test cases', () => {
|
||||
* a developers application.
|
||||
*/
|
||||
const testCases = [
|
||||
'v7/property-names'
|
||||
'v7/property-names',
|
||||
'v7/ripple-speed-factor',
|
||||
];
|
||||
|
||||
// Iterates through every test case directory and generates a jasmine test block that will
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
class MatRipple {
|
||||
speedFactor: number;
|
||||
}
|
||||
|
||||
class A {
|
||||
self = {me: this.ripple};
|
||||
|
||||
constructor(protected ripple: MatRipple) {}
|
||||
|
||||
onClick() {
|
||||
this.ripple.animation = {enterDuration: 900};
|
||||
this.self.me.animation = {enterDuration: 300};
|
||||
}
|
||||
}
|
||||
|
||||
const b = new MatRipple();
|
||||
const myConstant = 1;
|
||||
|
||||
b.animation = /** TODO: Cleanup duration calculation. */ {enterDuration: 450 / (0.5 + myConstant)};
|
||||
|
||||
@Component({
|
||||
template: `<div matRipple [matRippleAnimation]="{enterDuration: 900}"></div>`
|
||||
})
|
||||
class C {}
|
||||
|
||||
@Component({
|
||||
template: `<div matRipple [matRippleAnimation]="{enterDuration: (450 / (myValue))}"></div>`
|
||||
})
|
||||
class D {
|
||||
myValue = 1.5;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
class MatRipple {
|
||||
speedFactor: number;
|
||||
}
|
||||
|
||||
class A {
|
||||
self = {me: this.ripple};
|
||||
|
||||
constructor(protected ripple: MatRipple) {}
|
||||
|
||||
onClick() {
|
||||
this.ripple.speedFactor = 0.5;
|
||||
this.self.me.speedFactor = 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
const b = new MatRipple();
|
||||
const myConstant = 1;
|
||||
|
||||
b.speedFactor = 0.5 + myConstant;
|
||||
|
||||
@Component({
|
||||
template: `<div matRipple [matRippleSpeedFactor]="0.5"></div>`
|
||||
})
|
||||
class C {}
|
||||
|
||||
@Component({
|
||||
template: `<div matRipple [matRippleSpeedFactor]="myValue"></div>`
|
||||
})
|
||||
class D {
|
||||
myValue = 1.5;
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {sync as globSync} from 'glob';
|
||||
import {TargetVersion} from './index';
|
||||
import {TargetVersion} from './target-version';
|
||||
|
||||
/** List of rules that need to be enabled when running the TSLint fix task. */
|
||||
const upgradeRules = [
|
||||
@@ -52,6 +52,10 @@ const upgradeRules = [
|
||||
// Additional misc rules.
|
||||
'check-import-misc',
|
||||
'check-template-misc',
|
||||
|
||||
// Ripple misc V7
|
||||
['ripple-speed-factor-assignment', TargetVersion.V7],
|
||||
['ripple-speed-factor-template', TargetVersion.V7],
|
||||
];
|
||||
|
||||
/** List of absolute paths that refer to directories that contain the upgrade rules. */
|
||||
@@ -66,8 +70,14 @@ const rulesDirectory = globSync('rules/**/', {cwd: __dirname, absolute: true});
|
||||
* walker is not able to detect stylesheets which are not referenced by Angular.
|
||||
*/
|
||||
export function createTslintConfig(target: TargetVersion, extraStyleFiles: string[]) {
|
||||
const rules = upgradeRules.reduce((result, ruleName) => {
|
||||
result[ruleName] = [true, target, extraStyleFiles];
|
||||
const rules = upgradeRules.reduce((result, data) => {
|
||||
const ruleName = data instanceof Array ? data[0] : data;
|
||||
const versionConstraints = data instanceof Array ? data.slice(1) : null;
|
||||
|
||||
if (!versionConstraints || versionConstraints.includes(target)) {
|
||||
result[ruleName] = [true, target, extraStyleFiles];
|
||||
}
|
||||
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
import {Rule, SchematicContext, TaskId, Tree} from '@angular-devkit/schematics';
|
||||
import {RunSchematicTask, TslintFixTask} from '@angular-devkit/schematics/tasks';
|
||||
import {sync as globSync} from 'glob';
|
||||
import {TargetVersion} from './index';
|
||||
import {getProjectTsConfigPaths} from './project-tsconfig-paths';
|
||||
import {TargetVersion} from './target-version';
|
||||
import {createTslintConfig} from './tslint-config';
|
||||
|
||||
/** Entry point for `ng update` from Angular CLI. */
|
||||
|
||||
@@ -239,11 +239,10 @@ export class MatTabLink extends _MatTabLinkMixinBase
|
||||
this.tabIndex = parseInt(tabIndex) || 0;
|
||||
|
||||
if (globalOptions) {
|
||||
// TODO(paul): Do not copy each option manually. Allow dynamic global option changes: #9729
|
||||
this._ripplesGloballyDisabled = !!globalOptions.disabled;
|
||||
// TODO(paul): Once the speedFactor is removed, we no longer need to copy each single option.
|
||||
this.rippleConfig = {
|
||||
terminateOnPointerUp: globalOptions.terminateOnPointerUp,
|
||||
speedFactor: globalOptions.baseSpeedFactor,
|
||||
animation: globalOptions.animation,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user