Elizabeth Mitchell 55b4650063 fix(tokens)!: tokens.md-comp-*-values() include custom properties by default
BREAKING CHANGE: Sass component tokens, such as `tokens.md-comp-checkbox-values()`, return `var(--md-<component>, <value>)` instead of just the CSS value. Use `$exclude-custom-properties: true` to remove them.

PiperOrigin-RevId: 652550625
2024-07-15 11:43:52 -07:00

288 lines
7.9 KiB
SCSS

//
// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0
//
// go/keep-sorted start
@use 'sass:list';
// go/keep-sorted end
// go/keep-sorted start
@use '../../tokens';
// go/keep-sorted end
@mixin theme($tokens) {
$supported-tokens: tokens.$md-comp-circular-progress-supported-tokens;
@each $token, $value in $tokens {
@if list.index($supported-tokens, $token) == null {
@error 'Token `#{$token}` is not a supported token.';
}
@if $value {
--md-circular-progress-#{$token}: #{$value};
}
}
}
@mixin styles() {
$tokens: tokens.md-comp-circular-progress-values();
// If changing this value, make sure to change $size-without-padding in the
// circular-progress tokens.
$container-padding: 4px;
// note, these value come from the m2 version but match current gm3 values.
// Constants:
// ARCSIZE = 270 degrees (amount of circle the arc takes up)
// ARCTIME = 1333ms (time it takes to expand and contract arc)
// ARCSTARTROT = 216 degrees (how much the start location of the arc
// should rotate each time, 216 gives us a
// 5 pointed star shape (it's 360/5 * 3).
// For a 7 pointed star, we might do
// 360/7 * 3 = 154.286)
// ARCTIME
$arc-duration: 1333ms;
// 4 * ARCTIME
$cycle-duration: calc(4 * $arc-duration);
// ARCTIME * 360 / (ARCSTARTROT + (360-ARCSIZE))
$linear-rotate-duration: calc($arc-duration * 360 / 306);
$indeterminate-easing: cubic-bezier(0.4, 0, 0.2, 1);
:host {
@each $token, $value in $tokens {
--_#{$token}: #{$value};
}
display: inline-flex;
vertical-align: middle;
width: var(--_size);
height: var(--_size);
position: relative;
align-items: center;
justify-content: center;
// `contain` and `content-visibility` are performance optimizations
// important here because progress indicators are often used when a cpu
// intensive task is underway so it's especially important to minimize
// their cpu consumption.
contain: strict;
content-visibility: auto;
}
.progress {
flex: 1;
align-self: stretch;
margin: $container-padding;
}
.progress,
.spinner,
.left,
.right,
.circle,
svg,
.track,
.active-track {
position: absolute;
inset: 0;
}
svg {
transform: rotate(-90deg);
}
circle {
cx: 50%;
cy: 50%;
r: calc(50% * (1 - var(--_active-indicator-width) / 100));
// match size to indeterminate border width
stroke-width: calc(var(--_active-indicator-width) * 1%);
// note, pathLength is set so this can be normalized
stroke-dasharray: 100;
fill: transparent;
}
.active-track {
// note, these value come from the m2 version but match current gm3 values.
transition: stroke-dashoffset 500ms cubic-bezier(0, 0, 0.2, 1);
stroke: var(--_active-indicator-color);
}
.track {
stroke: transparent;
}
.progress.indeterminate {
animation: linear infinite linear-rotate;
animation-duration: $linear-rotate-duration;
}
.spinner {
animation: infinite both rotate-arc;
animation-duration: $cycle-duration;
animation-timing-function: $indeterminate-easing;
}
.left {
overflow: hidden;
inset: 0 50% 0 0;
}
.right {
overflow: hidden;
inset: 0 0 0 50%;
}
.circle {
box-sizing: border-box;
border-radius: 50%;
// match size to svg stroke width, which is a fraction of the overall
// padding box width.
$_padding-box-width: calc(var(--_size) - 2 * $container-padding);
$_active-indicator-fraction: calc(var(--_active-indicator-width) / 100);
border: solid calc($_active-indicator-fraction * $_padding-box-width);
border-color: var(--_active-indicator-color) var(--_active-indicator-color)
transparent transparent;
animation: expand-arc;
animation-iteration-count: infinite;
animation-fill-mode: both;
animation-duration: $arc-duration, $cycle-duration;
animation-timing-function: $indeterminate-easing;
}
.four-color .circle {
animation-name: expand-arc, four-color;
}
.left .circle {
rotate: 135deg;
inset: 0 -100% 0 0;
}
.right .circle {
rotate: 100deg;
inset: 0 0 0 -100%;
animation-delay: calc(-0.5 * $arc-duration), 0ms;
}
@media (forced-colors: active) {
.active-track {
stroke: CanvasText;
}
.circle {
border-color: CanvasText CanvasText Canvas Canvas;
}
}
// Indeterminate mode is 3 animations composed together:
// 1. expand-arc: an arc is expanded/contracted between 10deg and 270deg.
// 2. rotate-arc: at the same time, the arc is rotated in increments
// of 270deg.
// 3. linear-rotate: that rotating arc is then linearly rotated to produce
// a spinning expanding/contracting arc.
//
// See original implementation:
// https://github.com/PolymerElements/paper-spinner/blob/master/paper-spinner-styles.js.
// 1. expand-arc
// This is used on 2 divs which each represent half the desired
// 270deg arc with one offset by 50%. This creates an arc which expands from
// 10deg to 270deg.
@keyframes expand-arc {
0% {
transform: rotate(265deg);
}
50% {
transform: rotate(130deg);
}
100% {
transform: rotate(265deg);
}
}
// 2. rotate-arc
// The arc seamlessly travels around the circle indefinitely so it needs to
// end at a full rotation of the circle. This rotates the 270 deg
// (270/360 = 3/4) arc 4x (4 * 3/4 = 3) so it ends at
// (3 * 360 = 1080).
// This is sub-divided into increments of 135deg since the arc is rendered
// with 2 divs acting together.
@keyframes rotate-arc {
12.5% {
transform: rotate(135deg);
}
25% {
transform: rotate(270deg);
}
37.5% {
transform: rotate(405deg);
}
50% {
transform: rotate(540deg);
}
62.5% {
transform: rotate(675deg);
}
75% {
transform: rotate(810deg);
}
87.5% {
transform: rotate(945deg);
}
100% {
transform: rotate(1080deg);
}
}
// 3. linear-rotate
// The traveling expanding arc is linearly rotated to produce the spinner
// effect.
@keyframes linear-rotate {
to {
transform: rotate(360deg);
}
}
// This animates between 4 colors which are each shown for 25% of the time.
// Each color is shown solid for 3/5 of that time (3/5 * 25% = 15%) and
// transitions to the next color for 2/5 of that time (2/5 * 25% = 10%).
@keyframes four-color {
0% {
border-top-color: var(--_four-color-active-indicator-one-color);
border-right-color: var(--_four-color-active-indicator-one-color);
}
15% {
border-top-color: var(--_four-color-active-indicator-one-color);
border-right-color: var(--_four-color-active-indicator-one-color);
}
25% {
border-top-color: var(--_four-color-active-indicator-two-color);
border-right-color: var(--_four-color-active-indicator-two-color);
}
40% {
border-top-color: var(--_four-color-active-indicator-two-color);
border-right-color: var(--_four-color-active-indicator-two-color);
}
50% {
border-top-color: var(--_four-color-active-indicator-three-color);
border-right-color: var(--_four-color-active-indicator-three-color);
}
65% {
border-top-color: var(--_four-color-active-indicator-three-color);
border-right-color: var(--_four-color-active-indicator-three-color);
}
75% {
border-top-color: var(--_four-color-active-indicator-four-color);
border-right-color: var(--_four-color-active-indicator-four-color);
}
90% {
border-top-color: var(--_four-color-active-indicator-four-color);
border-right-color: var(--_four-color-active-indicator-four-color);
}
100% {
border-top-color: var(--_four-color-active-indicator-one-color);
border-right-color: var(--_four-color-active-indicator-one-color);
}
}
}