Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 187 additions & 0 deletions src/material/schematics/ng-generate/theming-api/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/**
* @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
*/

/** Mapping of Material mixins that should be renamed. */
export const materialMixins: Record<string, string> = {
'mat-core': 'core',
'mat-core-color': 'core-color',
'mat-core-theme': 'core-theme',
'angular-material-theme': 'all-component-themes',
'angular-material-typography': 'all-component-typographies',
'angular-material-color': 'all-component-colors',
'mat-base-typography': 'typography-hierarchy',
'mat-typography-level-to-styles': 'typography-level',
'mat-elevation': 'elevation',
'mat-overridable-elevation': 'overridable-elevation',
'mat-elevation-transition': 'elevation-transition',
'mat-ripple': 'ripple',
'mat-ripple-color': 'ripple-color',
'mat-ripple-theme': 'ripple-theme',
'mat-strong-focus-indicators': 'strong-focus-indicators',
'mat-strong-focus-indicators-color': 'strong-focus-indicators-color',
'mat-strong-focus-indicators-theme': 'strong-focus-indicators-theme',
'mat-font-shorthand': 'font-shorthand',
// The expansion panel is a special case, because the package is called `expansion`, but the
// mixins were prefixed with `expansion-panel`. This was corrected by the Sass module migration.
'mat-expansion-panel-theme': 'expansion-theme',
'mat-expansion-panel-color': 'expansion-color',
'mat-expansion-panel-typography': 'expansion-typography',
};

// The component themes all follow the same pattern so we can spare ourselves some typing.
[
'option', 'optgroup', 'pseudo-checkbox', 'autocomplete', 'badge', 'bottom-sheet', 'button',
'button-toggle', 'card', 'checkbox', 'chips', 'divider', 'table', 'datepicker', 'dialog',
'grid-list', 'icon', 'input', 'list', 'menu', 'paginator', 'progress-bar', 'progress-spinner',
'radio', 'select', 'sidenav', 'slide-toggle', 'slider', 'stepper', 'sort', 'tabs', 'toolbar',
'tooltip', 'snack-bar', 'form-field', 'tree'
].forEach(name => {
materialMixins[`mat-${name}-theme`] = `${name}-theme`;
materialMixins[`mat-${name}-color`] = `${name}-color`;
materialMixins[`mat-${name}-typography`] = `${name}-typography`;
});

/** Mapping of Material functions that should be renamed. */
export const materialFunctions: Record<string, string> = {
'mat-color': 'get-color-from-palette',
'mat-contrast': 'get-contrast-color-from-palette',
'mat-palette': 'define-palette',
'mat-dark-theme': 'define-dark-theme',
'mat-light-theme': 'define-light-theme',
'mat-typography-level': 'define-typography-level',
'mat-typography-config': 'define-typography-config',
'mat-font-size': 'font-size',
'mat-line-height': 'line-height',
'mat-font-weight': 'font-weight',
'mat-letter-spacing': 'letter-spacing',
'mat-font-family': 'font-family',
};

/** Mapping of Material variables that should be renamed. */
export const materialVariables: Record<string, string> = {
'mat-light-theme-background': 'light-theme-background-palette',
'mat-dark-theme-background': 'dark-theme-background-palette',
'mat-light-theme-foreground': 'light-theme-foreground-palette',
'mat-dark-theme-foreground': 'dark-theme-foreground-palette',
};

// The palettes all follow the same pattern.
[
'red', 'pink', 'indigo', 'purple', 'deep-purple', 'blue', 'light-blue', 'cyan', 'teal', 'green',
'light-green', 'lime', 'yellow', 'amber', 'orange', 'deep-orange', 'brown', 'grey', 'gray',
'blue-grey', 'blue-gray'
].forEach(name => materialVariables[`mat-${name}`] = `${name}-palette`);

/** Mapping of CDK variables that should be renamed. */
export const cdkVariables: Record<string, string> = {
'cdk-z-index-overlay-container': 'overlay-container-z-index',
'cdk-z-index-overlay': 'overlay-z-index',
'cdk-z-index-overlay-backdrop': 'overlay-backdrop-z-index',
'cdk-overlay-dark-backdrop-background': 'overlay-backdrop-color',
};

/** Mapping of CDK mixins that should be renamed. */
export const cdkMixins: Record<string, string> = {
'cdk-overlay': 'overlay',
'cdk-a11y': 'a11y-visually-hidden',
'cdk-high-contrast': 'high-contrast',
'cdk-text-field-autofill-color': 'text-field-autofill-color',
// This one was split up into two mixins which is trickier to
// migrate so for now we forward to the deprecated variant.
'cdk-text-field': 'text-field',
};

/**
* Material variables that have been removed from the public API
* and which should be replaced with their values.
*/
export const removedMaterialVariables: Record<string, string> = {
// Note: there's also a usage of a variable called `$pi`, but the name is short enough that
// it matches things like `$mat-pink`. Don't migrate it since it's unlikely to be used.
'mat-xsmall': `'max-width: 599px'`,
'mat-small': `'max-width: 959px'`,
'mat-toggle-padding': '8px',
'mat-toggle-size': '20px',
'mat-linear-out-slow-in-timing-function': 'cubic-bezier(0, 0, 0.2, 0.1)',
'mat-fast-out-slow-in-timing-function': 'cubic-bezier(0.4, 0, 0.2, 1)',
'mat-fast-out-linear-in-timing-function': 'cubic-bezier(0.4, 0, 1, 1)',
'mat-elevation-transition-duration': '280ms',
'mat-elevation-transition-timing-function': 'cubic-bezier(0.4, 0, 0.2, 1)',
'mat-elevation-color': '#000',
'mat-elevation-opacity': '1',
'mat-elevation-prefix': `'mat-elevation-z'`,
'mat-ripple-color-opacity': '0.1',
'mat-badge-font-size': '12px',
'mat-badge-font-weight': '600',
'mat-badge-default-size': '22px',
'mat-badge-small-size': '16px',
'mat-badge-large-size': '28px',
'mat-button-toggle-standard-height': '48px',
'mat-button-toggle-standard-minimum-height': '24px',
'mat-button-toggle-standard-maximum-height': '48px',
'mat-chip-remove-font-size': '18px',
'mat-datepicker-selected-today-box-shadow-width': '1px',
'mat-datepicker-selected-fade-amount': '0.6',
'mat-datepicker-range-fade-amount': '0.2',
'mat-datepicker-today-fade-amount': '0.2',
'mat-calendar-body-font-size': '13px',
'mat-calendar-weekday-table-font-size': '11px',
'mat-expansion-panel-header-collapsed-height': '48px',
'mat-expansion-panel-header-collapsed-minimum-height': '36px',
'mat-expansion-panel-header-collapsed-maximum-height': '48px',
'mat-expansion-panel-header-expanded-height': '64px',
'mat-expansion-panel-header-expanded-minimum-height': '48px',
'mat-expansion-panel-header-expanded-maximum-height': '64px',
'mat-expansion-panel-header-transition': '225ms cubic-bezier(0.4, 0, 0.2, 1)',
'mat-paginator-height': '56px',
'mat-paginator-minimum-height': '40px',
'mat-paginator-maximum-height': '56px',
'mat-stepper-header-height': '72px',
'mat-stepper-header-minimum-height': '42px',
'mat-stepper-header-maximum-height': '72px',
'mat-stepper-label-header-height': '24px',
'mat-stepper-label-position-bottom-top-gap': '16px',
'mat-stepper-label-min-width': '50px',
'mat-vertical-stepper-content-margin': '36px',
'mat-stepper-side-gap': '24px',
'mat-stepper-line-width': '1px',
'mat-stepper-line-gap': '8px',
'mat-step-sub-label-font-size': '12px',
'mat-step-header-icon-size': '16px',
'mat-toolbar-minimum-height': '44px',
'mat-toolbar-height-desktop': '64px',
'mat-toolbar-maximum-height-desktop': '64px',
'mat-toolbar-minimum-height-desktop': '44px',
'mat-toolbar-height-mobile': '56px',
'mat-toolbar-maximum-height-mobile': '56px',
'mat-toolbar-minimum-height-mobile': '44px',
'mat-tooltip-target-height': '22px',
'mat-tooltip-font-size': '10px',
'mat-tooltip-vertical-padding': '6px',
'mat-tooltip-handset-target-height': '30px',
'mat-tooltip-handset-font-size': '14px',
'mat-tooltip-handset-vertical-padding': '8px',
'mat-tree-node-height': '48px',
'mat-tree-node-minimum-height': '24px',
'mat-tree-node-maximum-height': '48px',
'z-index-fab': '20',
'z-index-drawer': '100',
'ease-in-out-curve-function': 'cubic-bezier(0.35, 0, 0.25, 1)',
'swift-ease-out-duration': '400ms',
'swift-ease-out-timing-function': 'cubic-bezier(0.25, 0.8, 0.25, 1)',
'swift-ease-out': 'all 400ms cubic-bezier(0.25, 0.8, 0.25, 1)',
'swift-ease-in-duration': '300ms',
'swift-ease-in-timing-function': 'cubic-bezier(0.55, 0, 0.55, 0.2)',
'swift-ease-in': 'all 300ms cubic-bezier(0.55, 0, 0.55, 0.2)',
'swift-ease-in-out-duration': '500ms',
'swift-ease-in-out-timing-function': 'cubic-bezier(0.35, 0, 0.25, 1)',
'swift-ease-in-out': 'all 500ms cubic-bezier(0.35, 0, 0.25, 1)',
'swift-linear-duration': '80ms',
'swift-linear-timing-function': 'linear',
'swift-linear': 'all 80ms linear'
};
65 changes: 65 additions & 0 deletions src/material/schematics/ng-generate/theming-api/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,5 +505,70 @@ describe('Material theming API schematic', () => {
]);
});

it('should replace removed variables with their values', async () => {
const app = await createTestApp(runner);
app.create('/theme.scss', [
`@import '~@angular/material/theming';`,
``,
`@include mat-button-toggle-theme();`,
``,

`.my-button-toggle {`,
`height: $mat-button-toggle-standard-height + 10px;`,
`transition: $swift-ease-out;`,
`}`,
``,
`@media ($mat-small) {`,
`.my-button-toggle {`,
`height: $mat-button-toggle-standard-minimum-height;`,
`}`,
`}`
].join('\n'));

const tree = await runner.runSchematicAsync('theming-api', options, app).toPromise();
expect(getFileContent(tree, '/theme.scss').split('\n')).toEqual([
`@use '~@angular/material' as mat;`,
``,
`@include mat.button-toggle-theme();`,
``,

`.my-button-toggle {`,
`height: 48px + 10px;`,
`transition: all 400ms cubic-bezier(0.25, 0.8, 0.25, 1);`,
`}`,
``,
`@media ('max-width: 959px') {`,
`.my-button-toggle {`,
`height: 24px;`,
`}`,
`}`
]);
});

it('should not replace assignments to removed variables', async () => {
const app = await createTestApp(runner);
app.create('/theme.scss', [
`@import '~@angular/material/theming';`,
``,
`$mat-button-toggle-standard-height: 50px;`,
`$mat-button-toggle-standard-minimum-height : 12px;`,
`$mat-toggle-padding:10px;`,
`$mat-toggle-size: 11px;`,
``,
`@include mat-button-toggle-theme();`,
].join('\n'));

const tree = await runner.runSchematicAsync('theming-api', options, app).toPromise();
expect(getFileContent(tree, '/theme.scss').split('\n')).toEqual([
`@use '~@angular/material' as mat;`,
``,
`$mat-button-toggle-standard-height: 50px;`,
`$mat-button-toggle-standard-minimum-height : 12px;`,
`$mat-toggle-padding:10px;`,
`$mat-toggle-size: 11px;`,
``,
`@include mat.button-toggle-theme();`,
]);
});

});
114 changes: 25 additions & 89 deletions src/material/schematics/ng-generate/theming-api/migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,95 +6,14 @@
* found in the LICENSE file at https://angular.io/license
*/

/** Mapping of Material mixins that should be renamed. */
const materialMixins: Record<string, string> = {
'mat-core': 'core',
'mat-core-color': 'core-color',
'mat-core-theme': 'core-theme',
'angular-material-theme': 'all-component-themes',
'angular-material-typography': 'all-component-typographies',
'angular-material-color': 'all-component-colors',
'mat-base-typography': 'typography-hierarchy',
'mat-typography-level-to-styles': 'typography-level',
'mat-elevation': 'elevation',
'mat-overridable-elevation': 'overridable-elevation',
'mat-elevation-transition': 'elevation-transition',
'mat-ripple': 'ripple',
'mat-ripple-color': 'ripple-color',
'mat-ripple-theme': 'ripple-theme',
'mat-strong-focus-indicators': 'strong-focus-indicators',
'mat-strong-focus-indicators-color': 'strong-focus-indicators-color',
'mat-strong-focus-indicators-theme': 'strong-focus-indicators-theme',
'mat-font-shorthand': 'font-shorthand',
// The expansion panel is a special case, because the package is called `expansion`, but the
// mixins were prefixed with `expansion-panel`. This was corrected by the Sass module migration.
'mat-expansion-panel-theme': 'expansion-theme',
'mat-expansion-panel-color': 'expansion-color',
'mat-expansion-panel-typography': 'expansion-typography',
};

// The component themes all follow the same pattern so we can spare ourselves some typing.
[
'option', 'optgroup', 'pseudo-checkbox', 'autocomplete', 'badge', 'bottom-sheet', 'button',
'button-toggle', 'card', 'checkbox', 'chips', 'divider', 'table', 'datepicker', 'dialog',
'grid-list', 'icon', 'input', 'list', 'menu', 'paginator', 'progress-bar', 'progress-spinner',
'radio', 'select', 'sidenav', 'slide-toggle', 'slider', 'stepper', 'sort', 'tabs', 'toolbar',
'tooltip', 'snack-bar', 'form-field', 'tree'
].forEach(name => {
materialMixins[`mat-${name}-theme`] = `${name}-theme`;
materialMixins[`mat-${name}-color`] = `${name}-color`;
materialMixins[`mat-${name}-typography`] = `${name}-typography`;
});

/** Mapping of Material functions that should be renamed. */
const materialFunctions: Record<string, string> = {
'mat-color': 'get-color-from-palette',
'mat-contrast': 'get-contrast-color-from-palette',
'mat-palette': 'define-palette',
'mat-dark-theme': 'define-dark-theme',
'mat-light-theme': 'define-light-theme',
'mat-typography-level': 'define-typography-level',
'mat-typography-config': 'define-typography-config',
'mat-font-size': 'font-size',
'mat-line-height': 'line-height',
'mat-font-weight': 'font-weight',
'mat-letter-spacing': 'letter-spacing',
'mat-font-family': 'font-family',
};

/** Mapping of Material variables that should be renamed. */
const materialVariables: Record<string, string> = {
'mat-light-theme-background': 'light-theme-background-palette',
'mat-dark-theme-background': 'dark-theme-background-palette',
'mat-light-theme-foreground': 'light-theme-foreground-palette',
'mat-dark-theme-foreground': 'dark-theme-foreground-palette',
};

// The palettes all follow the same pattern.
[
'red', 'pink', 'indigo', 'purple', 'deep-purple', 'blue', 'light-blue', 'cyan', 'teal', 'green',
'light-green', 'lime', 'yellow', 'amber', 'orange', 'deep-orange', 'brown', 'grey', 'gray',
'blue-grey', 'blue-gray'
].forEach(name => materialVariables[`mat-${name}`] = `${name}-palette`);

/** Mapping of CDK variables that should be renamed. */
const cdkVariables: Record<string, string> = {
'cdk-z-index-overlay-container': 'overlay-container-z-index',
'cdk-z-index-overlay': 'overlay-z-index',
'cdk-z-index-overlay-backdrop': 'overlay-backdrop-z-index',
'cdk-overlay-dark-backdrop-background': 'overlay-backdrop-color',
};

/** Mapping of CDK mixins that should be renamed. */
const cdkMixins: Record<string, string> = {
'cdk-overlay': 'overlay',
'cdk-a11y': 'a11y-visually-hidden',
'cdk-high-contrast': 'high-contrast',
'cdk-text-field-autofill-color': 'text-field-autofill-color',
// This one was split up into two mixins which is trickier to
// migrate so for now we forward to the deprecated variant.
'cdk-text-field': 'text-field',
};
import {
materialMixins,
materialFunctions,
materialVariables,
cdkMixins,
cdkVariables,
removedMaterialVariables
} from './config';

/**
* Migrates the content of a file to the new theming API. Note that this migration is using plain
Expand Down Expand Up @@ -129,6 +48,7 @@ export function migrateFileContent(content: string,
content = removeStrings(content, materialResults.imports);
content = removeStrings(content, cdkResults.imports);
content = content.replace(/^\s+/, '');
content = replaceRemovedVariables(content, removedMaterialVariables);
}
}

Expand Down Expand Up @@ -346,3 +266,19 @@ function extractNamespaceFromUseStatement(fullImport: string): string {

throw Error(`Could not extract namespace from import "${fullImport}".`);
}

/**
* Replaces variables that have been removed with their values.
* @param content Content of the file to be migrated.
* @param variables Mapping between variable names and their values.
*/
function replaceRemovedVariables(content: string, variables: Record<string, string>): string {
Object.keys(variables).sort(sortLengthDescending).forEach(variableName => {
// Note that the pattern uses a negative lookahead to exclude
// variable assignments, because they can't be migrated.
const regex = new RegExp(`\\$${escapeRegExp(variableName)}(?!\\s+:|:)`, 'g');
content = content.replace(regex, variables[variableName]);
});

return content;
}