1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 | @use 'sass:color';
|
27 | @use 'sass:map';
|
28 | @use '@material/animation/functions' as functions2;
|
29 | @use '@material/animation/variables' as variables2;
|
30 | @use '@material/base/mixins' as base-mixins;
|
31 | @use '@material/feature-targeting/feature-targeting';
|
32 | @use '@material/theme/css';
|
33 | @use '@material/theme/custom-properties';
|
34 | @use '@material/theme/theme';
|
35 | @use '@material/theme/keys';
|
36 | @use '@material/theme/shadow-dom';
|
37 | @use '@material/theme/theme-color';
|
38 |
|
39 | $custom-property-prefix: 'ripple';
|
40 |
|
41 | $fade-in-duration: 75ms !default;
|
42 | $fade-out-duration: 150ms !default;
|
43 | $translate-duration: 225ms !default;
|
44 | $states-wash-duration: 15ms !default;
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 | $dark-ink-opacities: (
|
52 | hover: 0.04,
|
53 | focus: 0.12,
|
54 | press: 0.12,
|
55 | selected: 0.08,
|
56 | activated: 0.12,
|
57 | ) !default;
|
58 |
|
59 | $light-ink-opacities: (
|
60 | hover: 0.08,
|
61 | focus: 0.24,
|
62 | press: 0.24,
|
63 | selected: 0.16,
|
64 | activated: 0.24,
|
65 | ) !default;
|
66 |
|
67 |
|
68 |
|
69 | $pressed-dark-ink-opacity: 0.16 !default;
|
70 | $pressed-light-ink-opacity: 0.32 !default;
|
71 |
|
72 |
|
73 | $_hover-selector: '&:hover';
|
74 | $_focus-selector: '&.mdc-ripple-upgraded--background-focused, &:not(.mdc-ripple-upgraded):focus';
|
75 | $_active-selector: '&:not(:disabled):active';
|
76 |
|
77 | $light-theme: (
|
78 | focus-state-layer-color: theme-color.$on-surface,
|
79 | focus-state-layer-opacity: map.get($dark-ink-opacities, focus),
|
80 | hover-state-layer-color: theme-color.$on-surface,
|
81 | hover-state-layer-opacity: map.get($dark-ink-opacities, hover),
|
82 | pressed-state-layer-color: theme-color.$on-surface,
|
83 | pressed-state-layer-opacity: map.get($dark-ink-opacities, press),
|
84 | );
|
85 |
|
86 | @mixin theme($theme) {
|
87 | @include keys.declare-custom-properties(
|
88 | $theme,
|
89 | $prefix: $custom-property-prefix
|
90 | );
|
91 |
|
92 | @if shadow-dom.$css-selector-fallback-declarations {
|
93 | .mdc-ripple-surface {
|
94 | @include theme-styles($theme);
|
95 | }
|
96 | }
|
97 | }
|
98 |
|
99 | $_ripple-theme: (
|
100 | hover-state-layer-color: null,
|
101 | focus-state-layer-color: null,
|
102 | pressed-state-layer-color: null,
|
103 | hover-state-layer-opacity: null,
|
104 | focus-state-layer-opacity: null,
|
105 | pressed-state-layer-opacity: null,
|
106 | );
|
107 |
|
108 | @mixin theme-styles($theme, $ripple-target: '&') {
|
109 | $theme: keys.create-theme-properties(
|
110 | $theme,
|
111 | $prefix: $custom-property-prefix
|
112 | );
|
113 |
|
114 |
|
115 |
|
116 | @include internal-theme-styles($theme, $ripple-target);
|
117 | }
|
118 |
|
119 | @mixin internal-theme-styles($theme, $ripple-target: '&') {
|
120 | @include theme.validate-theme-keys($_ripple-theme, $theme);
|
121 |
|
122 | @include states-base-color(
|
123 | map.get($theme, hover-state-layer-color),
|
124 | $ripple-target: $ripple-target
|
125 | );
|
126 | @include states-hover-opacity(
|
127 | map.get($theme, hover-state-layer-opacity),
|
128 | $ripple-target: $ripple-target
|
129 | );
|
130 | @include states-focus-opacity(
|
131 | map.get($theme, focus-state-layer-opacity),
|
132 | $ripple-target: $ripple-target
|
133 | );
|
134 | @include states-press-opacity(
|
135 | map.get($theme, pressed-state-layer-opacity),
|
136 | $ripple-target: $ripple-target
|
137 | );
|
138 | }
|
139 |
|
140 | @mixin states-base-color(
|
141 | $color,
|
142 | $query: feature-targeting.all(),
|
143 | $ripple-target: '&'
|
144 | ) {
|
145 | $feat-color: feature-targeting.create-target($query, color);
|
146 |
|
147 | @if $color {
|
148 | @if not custom-properties.is-custom-prop($color) {
|
149 | $color: custom-properties.create(
|
150 | ripple-color,
|
151 | theme-color.get-custom-property($color)
|
152 | );
|
153 | }
|
154 |
|
155 | #{$ripple-target}::before,
|
156 | #{$ripple-target}::after {
|
157 | @include feature-targeting.targets($feat-color) {
|
158 | @include theme.property(background-color, $color);
|
159 | }
|
160 | }
|
161 | }
|
162 | }
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 | @mixin states-opacities(
|
171 | $opacity-map: (),
|
172 | $has-nested-focusable-element: false,
|
173 | $ripple-target: '&',
|
174 | $query: feature-targeting.all()
|
175 | ) {
|
176 |
|
177 | @if map.get($opacity-map, hover) {
|
178 | @include states-hover-opacity(
|
179 | map.get($opacity-map, hover),
|
180 | $ripple-target: $ripple-target,
|
181 | $query: $query
|
182 | );
|
183 | }
|
184 |
|
185 | @if map.get($opacity-map, focus) {
|
186 | @include states-focus-opacity(
|
187 | map.get($opacity-map, focus),
|
188 | $ripple-target: $ripple-target,
|
189 | $has-nested-focusable-element: $has-nested-focusable-element,
|
190 | $query: $query
|
191 | );
|
192 | }
|
193 |
|
194 | @if map.get($opacity-map, press) {
|
195 | @include states-press-opacity(
|
196 | map.get($opacity-map, press),
|
197 | $ripple-target: $ripple-target,
|
198 | $query: $query
|
199 | );
|
200 | }
|
201 | }
|
202 |
|
203 | @mixin states-hover-opacity(
|
204 | $opacity,
|
205 | $query: feature-targeting.all(),
|
206 | $ripple-target: '&'
|
207 | ) {
|
208 | $feat-color: feature-targeting.create-target($query, color);
|
209 |
|
210 | @if $opacity and not custom-properties.is-custom-prop($opacity) {
|
211 | $opacity: custom-properties.create(ripple-hover-opacity, $opacity);
|
212 | }
|
213 |
|
214 |
|
215 | &:hover,
|
216 | &.mdc-ripple-surface--hover {
|
217 | @include states-background-selector($ripple-target) {
|
218 |
|
219 | @include feature-targeting.targets($feat-color) {
|
220 | @include theme.property(opacity, $opacity);
|
221 | }
|
222 | }
|
223 | }
|
224 | }
|
225 |
|
226 | @mixin states-focus-opacity(
|
227 | $opacity,
|
228 | $has-nested-focusable-element: false,
|
229 | $query: feature-targeting.all(),
|
230 | $ripple-target: '&'
|
231 | ) {
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 | @if $has-nested-focusable-element {
|
238 |
|
239 | &.mdc-ripple-upgraded--background-focused,
|
240 | &.mdc-ripple-upgraded:focus-within,
|
241 |
|
242 | &:not(.mdc-ripple-upgraded):focus,
|
243 | &:not(.mdc-ripple-upgraded):focus-within {
|
244 | @include states-background-selector($ripple-target) {
|
245 | @include states-focus-opacity-properties_(
|
246 | $opacity: $opacity,
|
247 | $query: $query
|
248 | );
|
249 | }
|
250 | }
|
251 | } @else {
|
252 |
|
253 | &.mdc-ripple-upgraded--background-focused,
|
254 |
|
255 | &:not(.mdc-ripple-upgraded):focus {
|
256 | @include states-background-selector($ripple-target) {
|
257 | @include states-focus-opacity-properties_(
|
258 | $opacity: $opacity,
|
259 | $query: $query
|
260 | );
|
261 | }
|
262 | }
|
263 | }
|
264 | }
|
265 |
|
266 | @mixin states-focus-opacity-properties_($opacity, $query) {
|
267 | $feat-animation: feature-targeting.create-target($query, animation);
|
268 |
|
269 | $feat-color: feature-targeting.create-target($query, color);
|
270 |
|
271 | @if $opacity {
|
272 | @if not custom-properties.is-custom-prop($opacity) {
|
273 | $opacity: custom-properties.create(ripple-focus-opacity, $opacity);
|
274 | }
|
275 |
|
276 |
|
277 | @include feature-targeting.targets($feat-animation) {
|
278 | transition-duration: 75ms;
|
279 | }
|
280 |
|
281 | @include feature-targeting.targets($feat-color) {
|
282 | @include theme.property(opacity, $opacity);
|
283 | }
|
284 | }
|
285 | }
|
286 |
|
287 | @mixin states-press-opacity(
|
288 | $opacity,
|
289 | $query: feature-targeting.all(),
|
290 | $ripple-target: '&'
|
291 | ) {
|
292 | $feat-animation: feature-targeting.create-target($query, animation);
|
293 | $feat-color: feature-targeting.create-target($query, color);
|
294 |
|
295 |
|
296 |
|
297 | @if $opacity {
|
298 | @if not custom-properties.is-custom-prop($opacity) {
|
299 | $opacity: custom-properties.create(ripple-press-opacity, $opacity);
|
300 | }
|
301 |
|
302 | &:not(.mdc-ripple-upgraded) {
|
303 |
|
304 | #{$ripple-target}::after {
|
305 | @include feature-targeting.targets($feat-animation) {
|
306 | transition: opacity $fade-out-duration linear;
|
307 | }
|
308 | }
|
309 |
|
310 | &:active {
|
311 | #{$ripple-target}::after {
|
312 | @include feature-targeting.targets($feat-animation) {
|
313 | transition-duration: $fade-in-duration;
|
314 | }
|
315 |
|
316 |
|
317 | @include feature-targeting.targets($feat-color) {
|
318 | @include theme.property(opacity, $opacity);
|
319 | }
|
320 | }
|
321 | }
|
322 | }
|
323 |
|
324 | &.mdc-ripple-upgraded {
|
325 | @include feature-targeting.targets($feat-color) {
|
326 |
|
327 |
|
328 |
|
329 | @include custom-properties.configure($emit-custom-properties: true) {
|
330 | @include theme.property(
|
331 | custom-properties.create(ripple-fg-opacity, $opacity)
|
332 | );
|
333 | }
|
334 | }
|
335 | }
|
336 | }
|
337 | }
|
338 |
|
339 |
|
340 |
|
341 | @mixin states(
|
342 | $color: theme-color.prop-value(on-surface),
|
343 | $has-nested-focusable-element: false,
|
344 | $query: feature-targeting.all(),
|
345 | $ripple-target: '&',
|
346 | $opacity-map: null
|
347 | ) {
|
348 | @include states-interactions_(
|
349 | $color: $color,
|
350 | $has-nested-focusable-element: $has-nested-focusable-element,
|
351 | $query: $query,
|
352 | $ripple-target: $ripple-target,
|
353 | $opacity-map: $opacity-map
|
354 | );
|
355 | }
|
356 |
|
357 |
|
358 |
|
359 | @mixin states-activated(
|
360 | $color,
|
361 | $has-nested-focusable-element: false,
|
362 | $query: feature-targeting.all(),
|
363 | $ripple-target: '&'
|
364 | ) {
|
365 | $feat-color: feature-targeting.create-target($query, color);
|
366 | $activated-opacity: states-opacity($color, activated);
|
367 |
|
368 | &--activated {
|
369 |
|
370 | @include states-background-selector($ripple-target) {
|
371 |
|
372 | @include feature-targeting.targets($feat-color) {
|
373 | @include theme.property(
|
374 | opacity,
|
375 | custom-properties.create(
|
376 | --mdc-ripple-activated-opacity,
|
377 | $activated-opacity
|
378 | )
|
379 | );
|
380 | }
|
381 | }
|
382 |
|
383 | @include states-interactions_(
|
384 | $color: $color,
|
385 | $has-nested-focusable-element: $has-nested-focusable-element,
|
386 | $opacity-modifier: $activated-opacity,
|
387 | $query: $query,
|
388 | $ripple-target: $ripple-target
|
389 | );
|
390 | }
|
391 | }
|
392 |
|
393 |
|
394 |
|
395 | @mixin states-selected(
|
396 | $color,
|
397 | $has-nested-focusable-element: false,
|
398 | $query: feature-targeting.all(),
|
399 | $ripple-target: '&'
|
400 | ) {
|
401 | $feat-color: feature-targeting.create-target($query, color);
|
402 | $selected-opacity: states-opacity($color, selected);
|
403 |
|
404 | &--selected {
|
405 | @include states-background-selector($ripple-target) {
|
406 |
|
407 | @include feature-targeting.targets($feat-color) {
|
408 | @include theme.property(
|
409 | opacity,
|
410 | custom-properties.create(
|
411 | --mdc-ripple-selected-opacity,
|
412 | $selected-opacity
|
413 | )
|
414 | );
|
415 | }
|
416 | }
|
417 |
|
418 | @include states-interactions_(
|
419 | $color: $color,
|
420 | $has-nested-focusable-element: $has-nested-focusable-element,
|
421 | $opacity-modifier: $selected-opacity,
|
422 | $query: $query,
|
423 | $ripple-target: $ripple-target
|
424 | );
|
425 | }
|
426 | }
|
427 |
|
428 | @mixin states-interactions_(
|
429 | $color,
|
430 | $has-nested-focusable-element,
|
431 | $opacity-modifier: 0,
|
432 | $query: feature-targeting.all(),
|
433 | $ripple-target: '&',
|
434 | $opacity-map: null
|
435 | ) {
|
436 | @include target-selector($ripple-target) {
|
437 | @include states-base-color($color, $query);
|
438 | }
|
439 |
|
440 | @if $opacity-map == null {
|
441 | $opacity-map: (
|
442 | hover: states-opacity($color, hover) + $opacity-modifier,
|
443 | focus: states-opacity($color, focus) + $opacity-modifier,
|
444 | press: states-opacity($color, press) + $opacity-modifier,
|
445 | );
|
446 | }
|
447 |
|
448 | @include states-opacities(
|
449 | $opacity-map,
|
450 | $has-nested-focusable-element: $has-nested-focusable-element,
|
451 | $ripple-target: $ripple-target,
|
452 | $query: $query
|
453 | );
|
454 | }
|
455 |
|
456 |
|
457 | @mixin target-selector($ripple-target: '&') {
|
458 | @if $ripple-target == '&' {
|
459 | @content;
|
460 | } @else {
|
461 | #{$ripple-target} {
|
462 | @content;
|
463 | }
|
464 | }
|
465 | }
|
466 |
|
467 |
|
468 | @mixin states-selector() {
|
469 | #{$_hover-selector},
|
470 | #{$_focus-selector},
|
471 | #{$_active-selector} {
|
472 | @content;
|
473 | }
|
474 | }
|
475 |
|
476 | @mixin hover() {
|
477 | #{$_hover-selector} {
|
478 | @content;
|
479 | }
|
480 | }
|
481 |
|
482 |
|
483 |
|
484 |
|
485 | @mixin focus() {
|
486 | #{$_focus-selector} {
|
487 | @content;
|
488 | }
|
489 | }
|
490 |
|
491 |
|
492 | @mixin pressed() {
|
493 | #{$_active-selector} {
|
494 | @content;
|
495 | }
|
496 | }
|
497 |
|
498 |
|
499 | @mixin active() {
|
500 | @include pressed() {
|
501 | @content;
|
502 | }
|
503 | }
|
504 |
|
505 |
|
506 | @mixin behind-content(
|
507 | $ripple-target,
|
508 | $content-root-selector: '&',
|
509 | $query: feature-targeting.all()
|
510 | ) {
|
511 |
|
512 |
|
513 | $feat-structure: feature-targeting.create-target($query, structure);
|
514 |
|
515 | #{$content-root-selector} {
|
516 | @include feature-targeting.targets($feat-structure) {
|
517 | z-index: 0;
|
518 | }
|
519 | }
|
520 |
|
521 | #{$ripple-target}::before,
|
522 | #{$ripple-target}::after {
|
523 | @include feature-targeting.targets($feat-structure) {
|
524 | @include theme.property(
|
525 | z-index,
|
526 | custom-properties.create(--mdc-ripple-z-index, -1)
|
527 | );
|
528 | }
|
529 | }
|
530 | }
|
531 |
|
532 | @function states-opacity($color, $state) {
|
533 | $color-value: theme-color.prop-value($color);
|
534 | $opacity-map: if(
|
535 | theme-color.tone($color-value) == 'light',
|
536 | $light-ink-opacities,
|
537 | $dark-ink-opacities
|
538 | );
|
539 |
|
540 | @if not map.has-key($opacity-map, $state) {
|
541 | @error "Invalid state: '#{$state}'. Choose one of: #{map.keys($opacity-map)}";
|
542 | }
|
543 |
|
544 | @return map.get($opacity-map, $state);
|
545 | }
|
546 |
|
547 | @mixin states-background-selector($ripple-target) {
|
548 | #{$ripple-target}::before {
|
549 | @content;
|
550 | }
|
551 | }
|