/
githubmirror
/
components
Обзор
Документация
Войти
/
githubmirror
/
components
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/material/checkbox/_checkbox-common.scss
563 строки
17 KB
Karl Seamon
perf(material/checkbox): Use > combinator for hover selector (#32586)
26 дек 2025, 20:10
Не верифицирован
26 дек 2025, 20:10
8cff9c5
Код
Авторство
О чём код?
@use 'sass:math'; @use '@angular/cdk'; @use './m3-checkbox'; @use '../core/tokens/token-utils'; @use '../core/style/vendor-prefixes'; $_path-length: 29.7833385; $_transition-duration: 90ms; $_icon-size: 18px; $_mark-stroke-size: math.div(2, 15) * $_icon-size; $_indeterminate-checked-curve: cubic-bezier(0.14, 0, 0, 1); $_indeterminate-change-duration: 500ms; $_enter-curve: cubic-bezier(0, 0, 0.2, 1); $_exit-curve: cubic-bezier(0.4, 0, 0.6, 1); $_fallback-size: 40px; // Structural styles for a checkbox. Shared with the selection list. @mixin checkbox-structure($include-state-layer-styles) { $fallbacks: m3-checkbox.get-tokens(); .mdc-checkbox { display: inline-block; position: relative; flex: 0 0 $_icon-size; box-sizing: content-box; width: $_icon-size; height: $_icon-size; line-height: 0; white-space: nowrap; cursor: pointer; vertical-align: bottom; $layer-size: token-utils.slot( checkbox-state-layer-size, $fallbacks, $fallback: $_fallback-size ); padding: calc((#{$layer-size} - #{$_icon-size}) / 2); margin: calc((#{$layer-size} - #{$layer-size}) / 2); @if ($include-state-layer-styles) { @include _state-layer-styles($fallbacks); } // These styles have to be nested in order to override overly-broad // user selectors like `input[type='checkbox']`. .mdc-checkbox__native-control { position: absolute; margin: 0; padding: 0; opacity: 0; cursor: inherit; z-index: 1; $layer-size: token-utils.slot( checkbox-state-layer-size, $fallbacks, $fallback: $_fallback-size ); $offset: calc((#{$layer-size} - #{$layer-size}) / 2); width: $layer-size; height: $layer-size; top: $offset; right: $offset; left: $offset; } } .mdc-checkbox--disabled { cursor: default; pointer-events: none; } .mdc-checkbox__background { display: inline-flex; position: absolute; align-items: center; justify-content: center; box-sizing: border-box; width: $_icon-size; height: $_icon-size; border: 2px solid currentColor; border-radius: 2px; background-color: transparent; pointer-events: none; will-change: background-color, border-color; transition: background-color $_transition-duration $_exit-curve, border-color $_transition-duration $_exit-curve; // Force browser to show background-color when using the print function @include vendor-prefixes.color-adjust(exact); $layer-size: token-utils.slot( checkbox-state-layer-size, $fallbacks, $fallback: $_fallback-size ); $offset: calc((#{$layer-size} - #{$_icon-size}) / 2); border-color: token-utils.slot(checkbox-unselected-icon-color, $fallbacks); top: $offset; left: $offset; } // These can't be under `.mdc-checkbox__background` because // the selectors will break when the mixin is nested. .mdc-checkbox__native-control:enabled:checked ~ .mdc-checkbox__background, .mdc-checkbox__native-control:enabled:indeterminate ~ .mdc-checkbox__background { border-color: token-utils.slot(checkbox-selected-icon-color, $fallbacks); background-color: token-utils.slot(checkbox-selected-icon-color, $fallbacks); } .mdc-checkbox--disabled .mdc-checkbox__background { border-color: token-utils.slot(checkbox-disabled-unselected-icon-color, $fallbacks); @include cdk.high-contrast { border-color: GrayText; } } .mdc-checkbox__native-control:disabled:checked ~ .mdc-checkbox__background, .mdc-checkbox__native-control:disabled:indeterminate ~ .mdc-checkbox__background { background-color: token-utils.slot(checkbox-disabled-selected-icon-color, $fallbacks); border-color: transparent; @include cdk.high-contrast { border-color: GrayText; } } // stylelint-disable selector-combinator-space-before .mdc-checkbox:hover > .mdc-checkbox__native-control:not(:checked) ~ .mdc-checkbox__background, .mdc-checkbox:hover > .mdc-checkbox__native-control:not(:indeterminate) ~ .mdc-checkbox__background { border-color: token-utils.slot(checkbox-unselected-hover-icon-color, $fallbacks); background-color: transparent; } // stylelint-enable selector-combinator-space-before .mdc-checkbox:hover > .mdc-checkbox__native-control:checked ~ .mdc-checkbox__background, .mdc-checkbox:hover > .mdc-checkbox__native-control:indeterminate ~ .mdc-checkbox__background { border-color: token-utils.slot(checkbox-selected-hover-icon-color, $fallbacks); background-color: token-utils.slot(checkbox-selected-hover-icon-color, $fallbacks); } // Note: this must be more specific than the hover styles above. // Double :focus is added for increased specificity. .mdc-checkbox__native-control:focus:focus:not(:checked) ~ .mdc-checkbox__background, .mdc-checkbox__native-control:focus:focus:not(:indeterminate) ~ .mdc-checkbox__background { border-color: token-utils.slot(checkbox-unselected-focus-icon-color, $fallbacks); } .mdc-checkbox__native-control:focus:focus:checked ~ .mdc-checkbox__background, .mdc-checkbox__native-control:focus:focus:indeterminate ~ .mdc-checkbox__background { border-color: token-utils.slot(checkbox-selected-focus-icon-color, $fallbacks); background-color: token-utils.slot(checkbox-selected-focus-icon-color, $fallbacks); } // Needs extra specificity to override the focus, hover, active states. .mdc-checkbox--disabled.mat-mdc-checkbox-disabled-interactive { .mdc-checkbox:hover > .mdc-checkbox__native-control ~ .mdc-checkbox__background, .mdc-checkbox .mdc-checkbox__native-control:focus ~ .mdc-checkbox__background, .mdc-checkbox__background { border-color: token-utils.slot(checkbox-disabled-unselected-icon-color, $fallbacks); @include cdk.high-contrast { border-color: GrayText; } } .mdc-checkbox__native-control:checked ~ .mdc-checkbox__background, .mdc-checkbox__native-control:indeterminate ~ .mdc-checkbox__background { background-color: token-utils.slot(checkbox-disabled-selected-icon-color, $fallbacks); border-color: transparent; } } .mdc-checkbox__checkmark { position: absolute; top: 0; right: 0; bottom: 0; left: 0; width: 100%; opacity: 0; transition: opacity $_transition-duration * 2 $_exit-curve; // Always apply the color since the element becomes `opacity: 0` // when unchecked. This makes the animation look better. color: token-utils.slot(checkbox-selected-checkmark-color, $fallbacks); @include cdk.high-contrast { color: CanvasText; } } .mdc-checkbox--disabled { &, &.mat-mdc-checkbox-disabled-interactive { .mdc-checkbox__checkmark { color: token-utils.slot(checkbox-disabled-selected-checkmark-color, $fallbacks); @include cdk.high-contrast { color: GrayText; } } } } .mdc-checkbox__checkmark-path { transition: stroke-dashoffset $_transition-duration * 2 $_exit-curve; stroke: currentColor; stroke-width: $_mark-stroke-size * 1.3; stroke-dashoffset: $_path-length; stroke-dasharray: $_path-length; } .mdc-checkbox__mixedmark { width: 100%; height: 0; transform: scaleX(0) rotate(0deg); border-width: math.div(math.floor($_mark-stroke-size), 2); border-style: solid; opacity: 0; transition: opacity $_transition-duration $_exit-curve, transform $_transition-duration $_exit-curve; // Always apply the color since the element becomes `opacity: 0` // when unchecked. This makes the animation look better. border-color: token-utils.slot(checkbox-selected-checkmark-color, $fallbacks); @include cdk.high-contrast { margin: 0 1px; } } .mdc-checkbox--disabled { &, &.mat-mdc-checkbox-disabled-interactive { .mdc-checkbox__mixedmark { border-color: token-utils.slot(checkbox-disabled-selected-checkmark-color, $fallbacks); @include cdk.high-contrast { border-color: GrayText; } } } } .mdc-checkbox--anim-unchecked-checked, .mdc-checkbox--anim-unchecked-indeterminate, .mdc-checkbox--anim-checked-unchecked, .mdc-checkbox--anim-indeterminate-unchecked { .mdc-checkbox__background { animation-duration: $_transition-duration * 2; animation-timing-function: linear; } } .mdc-checkbox--anim-unchecked-checked { .mdc-checkbox__checkmark-path { animation: mdc-checkbox-unchecked-checked-checkmark-path $_transition-duration * 2 linear; transition: none; } } .mdc-checkbox--anim-unchecked-indeterminate { .mdc-checkbox__mixedmark { animation: mdc-checkbox-unchecked-indeterminate-mixedmark $_transition-duration linear; transition: none; } } .mdc-checkbox--anim-checked-unchecked { .mdc-checkbox__checkmark-path { animation: mdc-checkbox-checked-unchecked-checkmark-path $_transition-duration linear; transition: none; } } .mdc-checkbox--anim-checked-indeterminate { .mdc-checkbox__checkmark { animation: mdc-checkbox-checked-indeterminate-checkmark $_transition-duration linear; transition: none; } .mdc-checkbox__mixedmark { animation: mdc-checkbox-checked-indeterminate-mixedmark $_transition-duration linear; transition: none; } } .mdc-checkbox--anim-indeterminate-checked { .mdc-checkbox__checkmark { // stylelint-disable-next-line max-line-length animation: mdc-checkbox-indeterminate-checked-checkmark $_indeterminate-change-duration linear; transition: none; } .mdc-checkbox__mixedmark { // stylelint-disable-next-line max-line-length animation: mdc-checkbox-indeterminate-checked-mixedmark $_indeterminate-change-duration linear; transition: none; } } .mdc-checkbox--anim-indeterminate-unchecked { .mdc-checkbox__mixedmark { animation: mdc-checkbox-indeterminate-unchecked-mixedmark $_indeterminate-change-duration * 0.6 linear; transition: none; } } .mdc-checkbox__native-control:checked ~ .mdc-checkbox__background, .mdc-checkbox__native-control:indeterminate ~ .mdc-checkbox__background { transition: border-color $_transition-duration $_enter-curve, background-color $_transition-duration $_enter-curve; > .mdc-checkbox__checkmark > .mdc-checkbox__checkmark-path { stroke-dashoffset: 0; } } .mdc-checkbox__native-control:checked ~ .mdc-checkbox__background { > .mdc-checkbox__checkmark { transition: opacity $_transition-duration * 2 $_enter-curve, transform $_transition-duration * 2 $_enter-curve; opacity: 1; } > .mdc-checkbox__mixedmark { transform: scaleX(1) rotate(-45deg); } } .mdc-checkbox__native-control:indeterminate ~ .mdc-checkbox__background { > .mdc-checkbox__checkmark { transform: rotate(45deg); opacity: 0; transition: opacity $_transition-duration $_exit-curve, transform $_transition-duration $_exit-curve; } > .mdc-checkbox__mixedmark { transform: scaleX(1) rotate(0deg); opacity: 1; } } @keyframes mdc-checkbox-unchecked-checked-checkmark-path { 0%, 50% { stroke-dashoffset: $_path-length; } 50% { animation-timing-function: $_enter-curve; } 100% { stroke-dashoffset: 0; } } @keyframes mdc-checkbox-unchecked-indeterminate-mixedmark { 0%, 68.2% { transform: scaleX(0); } 68.2% { animation-timing-function: cubic-bezier(0, 0, 0, 1); } 100% { transform: scaleX(1); } } @keyframes mdc-checkbox-checked-unchecked-checkmark-path { from { animation-timing-function: cubic-bezier(0.4, 0, 1, 1); opacity: 1; stroke-dashoffset: 0; } to { opacity: 0; stroke-dashoffset: $_path-length * -1; } } @keyframes mdc-checkbox-checked-indeterminate-checkmark { from { animation-timing-function: $_enter-curve; transform: rotate(0deg); opacity: 1; } to { transform: rotate(45deg); opacity: 0; } } @keyframes mdc-checkbox-indeterminate-checked-checkmark { from { animation-timing-function: $_indeterminate-checked-curve; transform: rotate(45deg); opacity: 0; } to { transform: rotate(360deg); opacity: 1; } } @keyframes mdc-checkbox-checked-indeterminate-mixedmark { from { animation-timing-function: $_enter-curve; transform: rotate(-45deg); opacity: 0; } to { transform: rotate(0deg); opacity: 1; } } @keyframes mdc-checkbox-indeterminate-checked-mixedmark { from { animation-timing-function: $_indeterminate-checked-curve; transform: rotate(0deg); opacity: 1; } to { transform: rotate(315deg); opacity: 0; } } @keyframes mdc-checkbox-indeterminate-unchecked-mixedmark { 0% { animation-timing-function: linear; transform: scaleX(1); opacity: 1; } 32.8%, 100% { transform: scaleX(0); opacity: 0; } } } // Conditionally disables the animations of the checkbox. @mixin checkbox-noop-animations() { &._mat-animation-noopable > .mat-internal-form-field > .mdc-checkbox { @include checkbox-noop-animations-internal; } } @mixin checkbox-noop-animations-internal() { > .mat-mdc-checkbox-touch-target, > .mdc-checkbox__native-control, > .mdc-checkbox__ripple, > .mat-mdc-checkbox-ripple::before, > .mdc-checkbox__background, > .mdc-checkbox__background > .mdc-checkbox__checkmark, > .mdc-checkbox__background > .mdc-checkbox__checkmark > .mdc-checkbox__checkmark-path, > .mdc-checkbox__background > .mdc-checkbox__mixedmark { transition: none !important; animation: none !important; } } @mixin _state-layer-styles($fallbacks) { // MDC expects `.mdc-checkbox__ripple::before` to be the state layer, but we use // `.mdc-checkbox__ripple` instead, so we emit the state layer slots ourselves. &:hover { > .mdc-checkbox__ripple { opacity: token-utils.slot(checkbox-unselected-hover-state-layer-opacity, $fallbacks); background-color: token-utils.slot(checkbox-unselected-hover-state-layer-color, $fallbacks); } > .mat-mdc-checkbox-ripple > .mat-ripple-element { background-color: token-utils.slot(checkbox-unselected-hover-state-layer-color, $fallbacks); } } .mdc-checkbox__native-control:focus { & + .mdc-checkbox__ripple { opacity: token-utils.slot(checkbox-unselected-focus-state-layer-opacity, $fallbacks); background-color: token-utils.slot(checkbox-unselected-focus-state-layer-color, $fallbacks); } & ~ .mat-mdc-checkbox-ripple .mat-ripple-element { background-color: token-utils.slot(checkbox-unselected-focus-state-layer-color, $fallbacks); } } &:active > .mdc-checkbox__native-control { & + .mdc-checkbox__ripple { opacity: token-utils.slot(checkbox-unselected-pressed-state-layer-opacity, $fallbacks); background-color: token-utils.slot(checkbox-unselected-pressed-state-layer-color, $fallbacks); } & ~ .mat-mdc-checkbox-ripple .mat-ripple-element { background-color: token-utils.slot(checkbox-unselected-pressed-state-layer-color, $fallbacks); } } &:hover > .mdc-checkbox__native-control:checked { & + .mdc-checkbox__ripple { opacity: token-utils.slot(checkbox-selected-hover-state-layer-opacity, $fallbacks); background-color: token-utils.slot(checkbox-selected-hover-state-layer-color, $fallbacks); } & ~ .mat-mdc-checkbox-ripple .mat-ripple-element { background-color: token-utils.slot(checkbox-selected-hover-state-layer-color, $fallbacks); } } .mdc-checkbox__native-control:focus:checked { & + .mdc-checkbox__ripple { opacity: token-utils.slot(checkbox-selected-focus-state-layer-opacity, $fallbacks); background-color: token-utils.slot(checkbox-selected-focus-state-layer-color, $fallbacks); } & ~ .mat-mdc-checkbox-ripple .mat-ripple-element { background-color: token-utils.slot(checkbox-selected-focus-state-layer-color, $fallbacks); } } &:active > .mdc-checkbox__native-control:checked { & + .mdc-checkbox__ripple { opacity: token-utils.slot(checkbox-selected-pressed-state-layer-opacity, $fallbacks); background-color: token-utils.slot(checkbox-selected-pressed-state-layer-color, $fallbacks); } & ~ .mat-mdc-checkbox-ripple .mat-ripple-element { background-color: token-utils.slot(checkbox-selected-pressed-state-layer-color, $fallbacks); } } // Needs extra specificity to override the focus, hover, active states. .mdc-checkbox--disabled.mat-mdc-checkbox-disabled-interactive & { .mdc-checkbox__native-control ~ .mat-mdc-checkbox-ripple .mat-ripple-element, .mdc-checkbox__native-control + .mdc-checkbox__ripple { background-color: token-utils.slot(checkbox-unselected-hover-state-layer-color, $fallbacks); } } }