Skip to content

Commit 96f7d46

Browse files
committed
Add the new mat.theme API
1 parent f990c0b commit 96f7d46

File tree

5 files changed

+151
-1
lines changed

5 files changed

+151
-1
lines changed

src/dev-app/theme-token-api.scss

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@use '@angular/material' as mat;
2+
@use '@angular/material-experimental';
23

34
dev-app {
45
&::before {
@@ -15,4 +16,38 @@ dev-app {
1516
}
1617
}
1718

18-
@debug 'Generated M2 tokens:' mat.m2-tokens-from-theme();
19+
.demo-unicorn-dark-theme {
20+
background: black;
21+
color: white;
22+
}
23+
24+
@include mat.core();
25+
26+
$dark-theme: mat.define-dark-theme((
27+
color: (
28+
primary: mat.define-palette(mat.$blue-grey-palette),
29+
accent: mat.define-palette(mat.$amber-palette, A200, A100, A400),
30+
warn: mat.define-palette(mat.$deep-orange-palette),
31+
),
32+
typography: mat.define-typography-config(),
33+
density: 0,
34+
));
35+
36+
// Set up light theme.
37+
38+
@include material-experimental.theme($components: (
39+
material-experimental.card(),
40+
material-experimental.checkbox(),
41+
));
42+
43+
// Set up dark theme.
44+
45+
.demo-unicorn-dark-theme {
46+
@include material-experimental.theme($tokens: mat.m2-tokens-from-theme($dark-theme), $components: (
47+
material-experimental.checkbox((
48+
(mdc, checkbox): (
49+
selected-checkmark-color: red,
50+
)
51+
)),
52+
));
53+
}

src/material-experimental/_index.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@
44
@forward './popover-edit/popover-edit-theme' as popover-edit-* show popover-edit-color,
55
popover-edit-typography, popover-edit-density, popover-edit-theme;
66

7+
// Token-based theming API
8+
@forward './theming/theming' show theme, card, checkbox;
9+
710
// Additional public APIs for individual components
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
@use 'sass:list';
2+
@use 'sass:map';
3+
@use 'sass:meta';
4+
@use 'sass:string';
5+
@use '@angular/material' as mat;
6+
7+
// Whether to throw an error when a required dep is not configured. If false, the dep will be
8+
// automatically configured instead.
9+
$_error-on-missing-dep: false;
10+
11+
// Applies the theme for the given component configuration.
12+
@mixin _apply-theme($tokens, $component) {
13+
$id: map.get($component, id);
14+
$tokens: map.deep-merge($tokens, map.get($component, customizations));
15+
16+
// NOTE: for now we use a hardcoded if-chain, but in the future when first-class mixins are
17+
// supported, the configuration data will contain a reference to its own theme mixin.
18+
@if $id == 'mat.card' {
19+
@include mat.private-apply-card-theme-from-tokens($tokens);
20+
} @else if $id == 'mat.checkbox' {
21+
@include mat.private-apply-checkbox-theme-from-tokens($tokens);
22+
} @else {
23+
@error 'Unrecognized component theme: #{id}';
24+
}
25+
}
26+
27+
// Gets the transitive dependency configurations for the given list of component configurations.
28+
@function _get-transitive-deps($components, $configured: ()) {
29+
// Mark the given components as configured.
30+
@each $component in $components {
31+
$configured: map.set($configured, map.get($component, id), true);
32+
}
33+
$new-deps: ();
34+
35+
// Check each of the given components for new deps.
36+
@each $component in $components {
37+
@each $dep-getter in mat.private-normalize-args-list(map.get($component, deps)) {
38+
$dep: meta.call($dep-getter);
39+
$dep-id: map.get($dep, id);
40+
@if not (map.has-key($configured, $dep-id)) {
41+
@if $_error-on-missing-dep {
42+
@error 'Missing theme: `#{map.get($component, id)}` depends on `#{$dep-id}`.' +
43+
' Please configure the theme for `#{$dep-id}` in your call to `mat.theme`';
44+
} @else {
45+
$configured: map.set($configured, $dep-id, true);
46+
$new-deps: list.append($new-deps, $dep);
47+
}
48+
}
49+
}
50+
}
51+
52+
// Append on the new deps to this list of component configurations and return.
53+
@if list.length($new-deps) > 0 {
54+
$components: list.join($components, _get-transitive-deps($new-deps, $configured));
55+
}
56+
@return $components;
57+
}
58+
59+
@mixin _theme($tokens, $components) {
60+
// Call the theme mixin for each configured component.
61+
@at-root #{& or body} {
62+
@each $component in $components {
63+
@include _apply-theme($tokens, $component);
64+
}
65+
}
66+
}
67+
68+
// Takes the full list of tokens and a list of components to configure, and outputs all theme
69+
// tokens for the configured components.
70+
@mixin theme($tokens: mat.m2-tokens-from-theme(), $components) {
71+
@include _theme($tokens, _get-transitive-deps(mat.private-normalize-args-list($components)));
72+
}
73+
74+
// TODO(mmalerba): What should we call this?
75+
// - update-theme
76+
// - adjust-theme
77+
// - edit-theme
78+
// - override-theme
79+
// - retheme
80+
// Takes a list of components to configure, and outputs only the theme tokens that are explicitly
81+
// customized by the configurations.
82+
@mixin update-theme($components) {
83+
@include _theme((), $components);
84+
}
85+
86+
// Configure the mat-card's theme.
87+
@function card($customizations: ()) {
88+
@return (
89+
id: 'mat.card',
90+
customizations: $customizations,
91+
deps: (),
92+
);
93+
}
94+
95+
// Configure the mat-checkbox's theme.
96+
@function checkbox($customizations: ()) {
97+
@return (
98+
id: 'mat.checkbox',
99+
customizations: $customizations,
100+
deps: (),
101+
);
102+
}

src/material/_index.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
// The form field density mixin needs to be exposed, because the paginator depends on it.
3939
@forward './form-field/form-field-theme' as private-form-field-* show private-form-field-density;
4040
@forward './token-theming' as private-apply-*;
41+
@forward './core/style/sass-utils' as private-*;
4142

4243
// Structural
4344
@forward './core/core' show core;

src/material/core/style/_sass-utils.scss

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
@use 'sass:list';
12
@use 'sass:map';
3+
@use 'sass:meta';
24

35
// A version of the standard `map.deep-merge` function that takes a variable number of arguments.
46
// Each argument is deep-merged into the final result from left to right.
@@ -9,3 +11,10 @@
911
}
1012
@return $result;
1113
}
14+
15+
// Normalizes a list of arguments to ensure it really is a list and not a single arg.
16+
// This should be used when dealing with user-passed lists of args to avoid confusing errors,
17+
// since Sass treats `($x)` as equivalent to `$x`.
18+
@function normalize-args-list($list) {
19+
@return if(meta.type-of($list) != 'list', ($list,), $list);
20+
}

0 commit comments

Comments
 (0)