mirror of
https://github.com/material-components/material-web.git
synced 2024-10-26 21:56:56 +03:00
fix(radio): update rendering and styles
PiperOrigin-RevId: 499587641
This commit is contained in:
parent
b0e87c538a
commit
3aff084297
@ -1 +1,6 @@
|
||||
@forward './lib/radio-theme' show theme, theme-extension;
|
||||
//
|
||||
// Copyright 2022 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
@forward './lib/radio' show theme;
|
||||
|
@ -1,377 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// stylelint-disable selector-class-pattern --
|
||||
// Selector '.md3-*' should only be used in this project.
|
||||
|
||||
@use 'sass:map';
|
||||
@use 'sass:selector';
|
||||
|
||||
@use '../../ripple/ripple';
|
||||
@use '../../sass/theme';
|
||||
@use '../../tokens';
|
||||
|
||||
$light-theme: tokens.md-comp-radio-button-values();
|
||||
$custom-property-prefix: 'radio';
|
||||
|
||||
@mixin theme($theme) {
|
||||
$theme: theme.validate-theme($light-theme, $theme);
|
||||
@include theme.emit-theme-vars(
|
||||
theme.create-theme-vars($theme, $custom-property-prefix)
|
||||
);
|
||||
}
|
||||
|
||||
@mixin theme-styles($theme) {
|
||||
$theme: theme.validate-theme($light-theme, $theme);
|
||||
// Set touch target manually until tokens provide this information.
|
||||
$theme: map.set($theme, _touch-target-size, 48px);
|
||||
$theme: theme.create-theme-vars($theme, $prefix: $custom-property-prefix);
|
||||
|
||||
.md3-radio {
|
||||
@include _disabled-selected-icon-color(
|
||||
map.get($theme, disabled-selected-icon-color)
|
||||
);
|
||||
@include _disabled-selected-icon-opacity(
|
||||
map.get($theme, disabled-selected-icon-opacity)
|
||||
);
|
||||
@include _disabled-unselected-icon-color(
|
||||
map.get($theme, disabled-unselected-icon-color)
|
||||
);
|
||||
@include _disabled-unselected-icon-opacity(
|
||||
map.get($theme, disabled-unselected-icon-opacity)
|
||||
);
|
||||
@include _icon-size(map.get($theme, icon-size));
|
||||
@include _selected-focus-icon-color(
|
||||
map.get($theme, selected-focus-icon-color)
|
||||
);
|
||||
@include _selected-hover-icon-color(
|
||||
map.get($theme, selected-hover-icon-color)
|
||||
);
|
||||
@include _selected-icon-color(map.get($theme, selected-icon-color));
|
||||
@include _selected-pressed-icon-color(
|
||||
map.get($theme, selected-pressed-icon-color)
|
||||
);
|
||||
@include _state-layer-size(map.get($theme, state-layer-size));
|
||||
@include _touch-target($size: map.get($theme, state-layer-size));
|
||||
@include _unselected-focus-icon-color(
|
||||
map.get($theme, unselected-focus-icon-color)
|
||||
);
|
||||
@include _unselected-hover-icon-color(
|
||||
map.get($theme, unselected-hover-icon-color)
|
||||
);
|
||||
@include _unselected-icon-color(map.get($theme, unselected-icon-color));
|
||||
@include _unselected-pressed-icon-color(
|
||||
map.get($theme, unselected-pressed-icon-color)
|
||||
);
|
||||
}
|
||||
|
||||
.md3-radio--touch {
|
||||
@include _touch-target($size: map.get($theme, _touch-target-size));
|
||||
}
|
||||
|
||||
@include ripple.theme(
|
||||
(
|
||||
hover-state-layer-color:
|
||||
map.get($theme, unselected-hover-state-layer-color),
|
||||
focus-state-layer-color:
|
||||
map.get($theme, unselected-focus-state-layer-color),
|
||||
pressed-state-layer-color:
|
||||
map.get($theme, unselected-pressed-state-layer-color),
|
||||
hover-state-layer-opacity:
|
||||
map.get($theme, unselected-hover-state-layer-opacity),
|
||||
focus-state-layer-opacity:
|
||||
map.get($theme, unselected-focus-state-layer-opacity),
|
||||
pressed-state-layer-opacity:
|
||||
map.get($theme, unselected-pressed-state-layer-opacity),
|
||||
)
|
||||
);
|
||||
|
||||
@include _checked-selector() {
|
||||
@include ripple.theme(
|
||||
(
|
||||
hover-state-layer-color:
|
||||
map.get($theme, selected-hover-state-layer-color),
|
||||
focus-state-layer-color:
|
||||
map.get($theme, selected-focus-state-layer-color),
|
||||
pressed-state-layer-color:
|
||||
map.get($theme, selected-pressed-state-layer-color),
|
||||
hover-state-layer-opacity:
|
||||
map.get($theme, selected-hover-state-layer-opacity),
|
||||
focus-state-layer-opacity:
|
||||
map.get($theme, selected-focus-state-layer-opacity),
|
||||
pressed-state-layer-opacity:
|
||||
map.get($theme, selected-pressed-state-layer-opacity),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$_theme-extension-keys: (
|
||||
touch-target-size: null,
|
||||
);
|
||||
|
||||
@mixin theme-extension($theme) {
|
||||
$theme: theme.validate-theme($_theme-extension-keys, $theme);
|
||||
|
||||
.md3-radio {
|
||||
@include _touch-target(map.get($theme, touch-target-size));
|
||||
}
|
||||
}
|
||||
|
||||
@mixin high-contrast-styles() {
|
||||
@include _disabled-selected-icon-color(GrayText);
|
||||
@include _disabled-selected-icon-opacity(1);
|
||||
@include _disabled-unselected-icon-color(GrayText);
|
||||
@include _disabled-unselected-icon-opacity(1);
|
||||
@include _selected-icon-color(CanvasText);
|
||||
@include _selected-hover-icon-color(CanvasText);
|
||||
@include _selected-focus-icon-color(CanvasText);
|
||||
@include _selected-pressed-icon-color(CanvasText);
|
||||
@include _unselected-icon-color(CanvasText);
|
||||
@include _unselected-hover-icon-color(CanvasText);
|
||||
@include _unselected-focus-icon-color(CanvasText);
|
||||
@include _unselected-pressed-icon-color(CanvasText);
|
||||
}
|
||||
|
||||
///
|
||||
/// Sets the stroke color of a checked, disabled radio button.
|
||||
/// @param {Color} $color - The desired stroke color.
|
||||
///
|
||||
@mixin disabled-checked-stroke-color($color) {
|
||||
@include _if-disabled-checked {
|
||||
@include _stroke-color($color);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Sets the stroke color of an unchecked, disabled radio button.
|
||||
/// @param {Color} $color - The desired stroke color.
|
||||
///
|
||||
@mixin disabled-unchecked-stroke-color($color) {
|
||||
@include _if-disabled-unchecked {
|
||||
@include _stroke-color($color);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Sets the ink color of a disabled radio button.
|
||||
/// @param {Color} $color - The desired ink color
|
||||
///
|
||||
@mixin disabled-ink-color($color) {
|
||||
@include _if-disabled {
|
||||
@include _ink-color($color);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _disabled-selected-icon-color($color) {
|
||||
@include disabled-checked-stroke-color($color);
|
||||
@include disabled-ink-color($color);
|
||||
}
|
||||
|
||||
@mixin _disabled-selected-icon-opacity($opacity) {
|
||||
@include _disabled-checked-stroke-opacity($opacity);
|
||||
@include _disabled-ink-opacity($opacity);
|
||||
}
|
||||
|
||||
@mixin _disabled-unselected-icon-color($color) {
|
||||
@include disabled-unchecked-stroke-color($color);
|
||||
}
|
||||
|
||||
@mixin _disabled-unselected-icon-opacity($opacity) {
|
||||
@include _disabled-unchecked-stroke-opacity($opacity);
|
||||
}
|
||||
|
||||
@mixin _icon-size($size) {
|
||||
.md3-radio__background {
|
||||
height: $size;
|
||||
width: $size;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _selected-hover-icon-color($color) {
|
||||
@include _if-input-selected {
|
||||
&:hover + {
|
||||
@include _stroke-color($color);
|
||||
@include _ink-color($color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _selected-focus-icon-color($color) {
|
||||
@include _if-input-selected {
|
||||
&:focus + {
|
||||
@include _stroke-color($color);
|
||||
@include _ink-color($color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _selected-pressed-icon-color($color) {
|
||||
@include _if-input-selected {
|
||||
&:active + {
|
||||
@include _stroke-color($color);
|
||||
@include _ink-color($color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _selected-icon-color($color) {
|
||||
@include _if-input-selected {
|
||||
& + {
|
||||
@include _stroke-color($color);
|
||||
@include _ink-color($color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _unselected-hover-icon-color($color) {
|
||||
@include _if-input-unselected {
|
||||
&:hover + {
|
||||
@include _stroke-color($color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _unselected-focus-icon-color($color) {
|
||||
@include _if-input-unselected {
|
||||
&:focus + {
|
||||
@include _stroke-color($color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _unselected-pressed-icon-color($color) {
|
||||
@include _if-input-unselected {
|
||||
&:active + {
|
||||
@include _stroke-color($color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _unselected-icon-color($color) {
|
||||
@include _if-input-unselected {
|
||||
& + {
|
||||
@include _stroke-color($color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _disabled-unchecked-stroke-opacity($opacity) {
|
||||
@include _if-disabled-unchecked {
|
||||
@include _stroke-opacity($opacity);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _disabled-checked-stroke-opacity($opacity) {
|
||||
@include _if-disabled-checked {
|
||||
@include _stroke-opacity($opacity);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _disabled-ink-opacity($opacity) {
|
||||
@include _if-disabled {
|
||||
@include _ink-opacity($opacity);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _touch-target($size) {
|
||||
block-size: $size;
|
||||
inline-size: $size;
|
||||
}
|
||||
|
||||
@mixin _state-layer-size($size) {
|
||||
.md3-radio__ripple {
|
||||
block-size: $size;
|
||||
inline-size: $size;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Sets the ink color for radio. This is wrapped in a mixin
|
||||
/// that qualifies state such as `_if-enabled`
|
||||
///
|
||||
@mixin _ink-color($color) {
|
||||
.md3-radio__background .md3-radio__inner-circle {
|
||||
background-color: $color;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _ink-opacity($opacity) {
|
||||
.md3-radio__background .md3-radio__inner-circle {
|
||||
opacity: $opacity;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Sets the stroke color for radio. This is wrapped in a mixin
|
||||
/// that qualifies state such as `_if-enabled`
|
||||
///
|
||||
@mixin _stroke-color($color) {
|
||||
.md3-radio__background .md3-radio__outer-circle {
|
||||
border-color: $color;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _stroke-opacity($opacity) {
|
||||
.md3-radio__background .md3-radio__outer-circle {
|
||||
opacity: $opacity;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _checked-selector() {
|
||||
@at-root {
|
||||
:host([checked]) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _if-input-unselected {
|
||||
.md3-radio__native-control:enabled:not(:checked) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _if-input-selected {
|
||||
.md3-radio__native-control:enabled:checked {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Helps select the radio background only when its native control is in the
|
||||
/// disabled state.
|
||||
///
|
||||
@mixin _if-disabled {
|
||||
.md3-radio__native-control:disabled {
|
||||
+ {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Helps select the radio background only when its native control is in the
|
||||
/// disabled & unchecked state.
|
||||
///
|
||||
@mixin _if-disabled-unchecked {
|
||||
.md3-radio__native-control:disabled {
|
||||
&:not(:checked) + {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Helps select the radio background only when its native control is in the
|
||||
/// disabled & checked state.
|
||||
///
|
||||
@mixin _if-disabled-checked {
|
||||
.md3-radio__native-control:disabled {
|
||||
&:checked + {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
@ -3,133 +3,165 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// stylelint-disable selector-class-pattern --
|
||||
// Selector '.md3-*' should only be used in this project.
|
||||
|
||||
@use 'sass:map';
|
||||
@use '../../focus/focus-ring';
|
||||
@use '../../motion/animation';
|
||||
@use '../../ripple/ripple';
|
||||
@use '../../sass/theme';
|
||||
@use '../../tokens';
|
||||
|
||||
@use './radio-theme';
|
||||
$_md-sys-motion: tokens.md-sys-motion-values();
|
||||
|
||||
@mixin theme($tokens) {
|
||||
$tokens: theme.validate-theme(tokens.md-comp-radio-button-values(), $tokens);
|
||||
$tokens: theme.create-theme-vars($tokens, 'radio');
|
||||
|
||||
@include theme.emit-theme-vars($tokens);
|
||||
}
|
||||
|
||||
@mixin styles() {
|
||||
$tokens: tokens.md-comp-radio-button-values();
|
||||
$tokens: theme.create-theme-vars($tokens, 'radio');
|
||||
|
||||
@mixin static-styles() {
|
||||
:host {
|
||||
@each $token, $value in $tokens {
|
||||
--_#{$token}: #{$value};
|
||||
}
|
||||
|
||||
@include ripple.theme(
|
||||
(
|
||||
focus-state-layer-color: var(--_unselected-focus-state-layer-color),
|
||||
focus-state-layer-opacity: var(--_unselected-focus-state-layer-opacity),
|
||||
hover-state-layer-color: var(--_unselected-hover-state-layer-color),
|
||||
hover-state-layer-opacity: var(--_unselected-hover-state-layer-opacity),
|
||||
pressed-state-layer-color: var(--_unselected-pressed-state-layer-color),
|
||||
pressed-state-layer-opacity:
|
||||
var(--_unselected-pressed-state-layer-opacity),
|
||||
)
|
||||
);
|
||||
|
||||
@include focus-ring.theme(
|
||||
(
|
||||
offset-vertical: -2px,
|
||||
offset-horizontal: -2px,
|
||||
)
|
||||
);
|
||||
|
||||
display: inline-flex;
|
||||
height: 48px;
|
||||
position: relative;
|
||||
vertical-align: top; // Fix extra space when placed inside display: block
|
||||
width: 48px;
|
||||
// Remove highlight color for mobile Safari
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.md3-radio {
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
will-change: opacity, transform, border-color, color;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
:host([checked]) {
|
||||
@include ripple.theme(
|
||||
(
|
||||
focus-state-layer-color: var(--_selected-focus-state-layer-color),
|
||||
focus-state-layer-opacity: var(--_selected-focus-state-layer-opacity),
|
||||
hover-state-layer-color: var(--_selected-hover-state-layer-color),
|
||||
hover-state-layer-opacity: var(--_selected-hover-state-layer-opacity),
|
||||
pressed-state-layer-color: var(--_selected-pressed-state-layer-color),
|
||||
pressed-state-layer-opacity:
|
||||
var(--_selected-pressed-state-layer-opacity),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
.md3-radio__background {
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.md3-radio__outer-circle {
|
||||
position: absolute;
|
||||
inset-block-start: 0;
|
||||
inset-inline-start: 0;
|
||||
box-sizing: border-box;
|
||||
block-size: 100%;
|
||||
inline-size: 100%;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
border-radius: 50%;
|
||||
transition: exit(border-color);
|
||||
}
|
||||
|
||||
.md3-radio__inner-circle {
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
block-size: 50%;
|
||||
inline-size: 50%;
|
||||
transform: scale(0);
|
||||
border-radius: 50%;
|
||||
transition: exit(transform), exit(border-color);
|
||||
}
|
||||
|
||||
.md3-radio__ripple {
|
||||
position: absolute;
|
||||
display: inline-flex;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.md3-radio__native-control {
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
opacity: 0;
|
||||
cursor: inherit;
|
||||
z-index: 1;
|
||||
block-size: 100%;
|
||||
inline-size: 100%;
|
||||
input,
|
||||
md-ripple,
|
||||
md-focus-ring,
|
||||
.icon {
|
||||
inset: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.md3-radio__native-control:checked,
|
||||
.md3-radio__native-control:disabled {
|
||||
+ .md3-radio__background {
|
||||
transition: enter(opacity), enter(transform);
|
||||
|
||||
.md3-radio__outer-circle {
|
||||
transition: enter(border-color);
|
||||
}
|
||||
|
||||
.md3-radio__inner-circle {
|
||||
transition: enter(transform), enter(border-color);
|
||||
}
|
||||
}
|
||||
input {
|
||||
appearance: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.md3-radio--disabled {
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
md-ripple {
|
||||
height: var(--_state-layer-size);
|
||||
width: var(--_state-layer-size);
|
||||
}
|
||||
|
||||
.md3-radio__native-control:checked {
|
||||
+ .md3-radio__background {
|
||||
.md3-radio__inner-circle {
|
||||
transform: scale(1);
|
||||
transition: enter(transform), enter(border-color);
|
||||
}
|
||||
}
|
||||
.icon {
|
||||
fill: var(--_unselected-icon-color);
|
||||
height: var(--_icon-size);
|
||||
width: var(--_icon-size);
|
||||
}
|
||||
|
||||
.md3-radio__native-control:disabled,
|
||||
[aria-disabled='true'] .md3-radio__native-control {
|
||||
+ .md3-radio__background {
|
||||
cursor: default;
|
||||
}
|
||||
.inner-circle {
|
||||
opacity: 0;
|
||||
transition-duration: 150ms, 50ms; // Exit duration for scale and opacity.
|
||||
transition-property: transform, opacity;
|
||||
// Exit easing function for scale, linear for opacity.
|
||||
transition-timing-function: map.get(
|
||||
$_md-sys-motion,
|
||||
easing-emphasized-accelerate
|
||||
),
|
||||
linear;
|
||||
transform: scale(0.6);
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
@include focus-ring.theme(
|
||||
(
|
||||
offset-vertical: -2px,
|
||||
offset-horizontal: -2px,
|
||||
)
|
||||
);
|
||||
:host([checked]) .icon {
|
||||
fill: var(--_selected-icon-color);
|
||||
}
|
||||
|
||||
@media (forced-colors: active) {
|
||||
.md3-radio {
|
||||
@include radio-theme.high-contrast-styles();
|
||||
}
|
||||
:host([checked]) .inner-circle {
|
||||
opacity: 1;
|
||||
// Enter duration for scale and opacity.
|
||||
transition-duration: 350ms, 50ms;
|
||||
// Enter easing function for scale, linear for opacity.
|
||||
transition-timing-function: map.get(
|
||||
$_md-sys-motion,
|
||||
easing-emphasized-decelerate
|
||||
),
|
||||
linear;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
// Don't animate when disabled
|
||||
:host([disabled]) .inner-circle {
|
||||
transition-duration: 0s;
|
||||
}
|
||||
|
||||
:host(:hover) .icon {
|
||||
fill: var(--_unselected-hover-icon-color);
|
||||
}
|
||||
|
||||
:host(:focus-within) .icon {
|
||||
fill: var(--_unselected-focus-icon-color);
|
||||
}
|
||||
|
||||
:host(:active) .icon {
|
||||
fill: var(--_unselected-pressed-icon-color);
|
||||
}
|
||||
|
||||
:host([disabled]) .icon {
|
||||
fill: var(--_disabled-unselected-icon-color);
|
||||
opacity: var(--_disabled-unselected-icon-opacity);
|
||||
}
|
||||
|
||||
:host([checked]:hover) .icon {
|
||||
fill: var(--_selected-hover-icon-color);
|
||||
}
|
||||
|
||||
:host([checked]:focus-within) .icon {
|
||||
fill: var(--_selected-focus-icon-color);
|
||||
}
|
||||
|
||||
:host([checked]:active) .icon {
|
||||
fill: var(--_selected-pressed-icon-color);
|
||||
}
|
||||
|
||||
:host([checked][disabled]) .icon {
|
||||
fill: var(--_disabled-selected-icon-color);
|
||||
opacity: var(--_disabled-selected-icon-opacity);
|
||||
}
|
||||
}
|
||||
|
||||
$_transition-duration: 120ms;
|
||||
|
||||
@function enter($name) {
|
||||
@return animation.deceleration($name, $_transition-duration);
|
||||
}
|
||||
|
||||
@function exit($name) {
|
||||
@return animation.sharp($name, $_transition-duration);
|
||||
}
|
||||
|
27
radio/lib/forced-colors-styles.scss
Normal file
27
radio/lib/forced-colors-styles.scss
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright 2023 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
@use './radio';
|
||||
|
||||
@media (forced-colors: active) {
|
||||
:host {
|
||||
@include radio.theme(
|
||||
(
|
||||
disabled-selected-icon-color: GrayText,
|
||||
disabled-selected-icon-opacity: 1,
|
||||
disabled-unselected-icon-color: GrayText,
|
||||
disabled-unselected-icon-opacity: 1,
|
||||
selected-icon-color: CanvasText,
|
||||
selected-hover-icon-color: CanvasText,
|
||||
selected-focus-icon-color: CanvasText,
|
||||
selected-pressed-icon-color: CanvasText,
|
||||
unselected-icon-color: CanvasText,
|
||||
unselected-hover-icon-color: CanvasText,
|
||||
unselected-focus-icon-color: CanvasText,
|
||||
unselected-pressed-icon-color: CanvasText,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -4,11 +4,5 @@
|
||||
//
|
||||
|
||||
@use './radio';
|
||||
@use './radio-theme';
|
||||
|
||||
:host {
|
||||
@include radio-theme.theme-styles(radio-theme.$light-theme);
|
||||
@include radio.static-styles();
|
||||
|
||||
display: inline-flex;
|
||||
}
|
||||
@include radio.styles;
|
||||
|
@ -4,15 +4,11 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// Style preference for leading underscores.
|
||||
// tslint:disable:strip-private-property-underscore
|
||||
|
||||
import '../../focus/focus-ring.js';
|
||||
import '../../ripple/ripple.js';
|
||||
|
||||
import {html, LitElement, nothing, PropertyValues, TemplateResult} from 'lit';
|
||||
import {html, LitElement, nothing, TemplateResult} from 'lit';
|
||||
import {property, query, queryAsync, state} from 'lit/decorators.js';
|
||||
import {classMap} from 'lit/directives/class-map.js';
|
||||
import {when} from 'lit/directives/when.js';
|
||||
|
||||
import {dispatchActivationClick, isActivationClick, redispatchEvent} from '../../controller/events.js';
|
||||
@ -28,7 +24,6 @@ const CHECKED = Symbol('checked');
|
||||
|
||||
/**
|
||||
* @fires checked
|
||||
* @soyCompatible
|
||||
*/
|
||||
export class Radio extends LitElement {
|
||||
static override shadowRootOptions:
|
||||
@ -53,7 +48,7 @@ export class Radio extends LitElement {
|
||||
|
||||
[CHECKED] = false;
|
||||
|
||||
@property({type: Boolean}) disabled = false;
|
||||
@property({type: Boolean, reflect: true}) disabled = false;
|
||||
|
||||
/**
|
||||
* The element value to use in form submission when checked.
|
||||
@ -65,13 +60,6 @@ export class Radio extends LitElement {
|
||||
*/
|
||||
@property({type: String, reflect: true}) name = '';
|
||||
|
||||
/**
|
||||
* Touch target extends beyond visual boundary of a component by default.
|
||||
* Set to `true` to remove touch target added to the component.
|
||||
* @see https://material.io/design/usability/accessibility.html
|
||||
*/
|
||||
@property({type: Boolean}) reducedTouchTarget = false;
|
||||
|
||||
@ariaProperty // tslint:disable-line:no-new-decorators
|
||||
@property({attribute: 'data-aria-label', noAccessor: true})
|
||||
override ariaLabel!: string;
|
||||
@ -83,7 +71,6 @@ export class Radio extends LitElement {
|
||||
return this.closest('form');
|
||||
}
|
||||
|
||||
@state() private focused = false;
|
||||
@query('input') private readonly input!: HTMLInputElement|null;
|
||||
@queryAsync('md-ripple') private readonly ripple!: Promise<MdRipple|null>;
|
||||
private readonly selectionController = new SingleSelectionController(this);
|
||||
@ -111,64 +98,39 @@ export class Radio extends LitElement {
|
||||
this.input?.focus();
|
||||
}
|
||||
|
||||
override updated(changedProperties: PropertyValues) {
|
||||
if (changedProperties.has('checked') && this.input) {
|
||||
this.input.checked = this.checked;
|
||||
if (!this.checked) {
|
||||
// Remove focus ring when unchecked on other radio programmatically.
|
||||
// Blur on input since this determines the focus style.
|
||||
this.input.blur();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @soyTemplate
|
||||
* @soyAttributes radioAttributes: input
|
||||
* @soyClasses radioClasses: .md3-radio
|
||||
*/
|
||||
protected override render(): TemplateResult {
|
||||
/** @classMap */
|
||||
const classes = {
|
||||
'md3-radio--touch': !this.reducedTouchTarget,
|
||||
'md3-ripple-upgraded--background-focused': this.focused,
|
||||
'md3-radio--disabled': this.disabled,
|
||||
};
|
||||
|
||||
return html`
|
||||
<div class="md3-radio ${classMap(classes)}">
|
||||
${this.renderFocusRing()}
|
||||
<input
|
||||
class="md3-radio__native-control"
|
||||
type="radio"
|
||||
name="${this.name}"
|
||||
aria-label="${this.ariaLabel || nothing}"
|
||||
.checked="${this.checked}"
|
||||
.value="${this.value}"
|
||||
?disabled="${this.disabled}"
|
||||
@change="${this.handleChange}"
|
||||
@focus="${this.handleFocus}"
|
||||
@blur="${this.handleBlur}"
|
||||
@pointerdown=${this.handlePointerDown}
|
||||
${ripple(this.getRipple)}
|
||||
>
|
||||
<div class="md3-radio__background">
|
||||
<div class="md3-radio__outer-circle"></div>
|
||||
<div class="md3-radio__inner-circle"></div>
|
||||
</div>
|
||||
<div class="md3-radio__ripple">
|
||||
${when(this.showRipple, this.renderRipple)}
|
||||
</div>
|
||||
</div>`;
|
||||
${when(this.showRipple, this.renderRipple)}
|
||||
${this.renderFocusRing()}
|
||||
<svg class="icon" viewBox="0 0 20 20">
|
||||
<mask id="cutout">
|
||||
<rect width="100%" height="100%" fill="white" />
|
||||
<circle cx="10" cy="10" r="8" fill="black" />
|
||||
</mask>
|
||||
<circle cx="10" cy="10" r="10" mask="url(#cutout)" />
|
||||
<circle cx="10" cy="10" r="5" class="inner-circle" />
|
||||
</svg>
|
||||
<input
|
||||
type="radio"
|
||||
name=${this.name}
|
||||
aria-label=${this.ariaLabel || nothing}
|
||||
.checked=${this.checked}
|
||||
.value=${this.value}
|
||||
?disabled=${this.disabled}
|
||||
@change=${this.handleChange}
|
||||
@focus=${this.handleFocus}
|
||||
@blur=${this.handleBlur}
|
||||
@pointerdown=${this.handlePointerDown}
|
||||
${ripple(this.getRipple)}
|
||||
>
|
||||
`;
|
||||
}
|
||||
|
||||
private handleBlur() {
|
||||
this.focused = false;
|
||||
this.showFocusRing = false;
|
||||
}
|
||||
|
||||
private handleFocus() {
|
||||
this.focused = true;
|
||||
this.showFocusRing = shouldShowStrongFocus();
|
||||
}
|
||||
|
||||
@ -182,7 +144,7 @@ export class Radio extends LitElement {
|
||||
redispatchEvent(this, event);
|
||||
}
|
||||
|
||||
private handlePointerDown(event: PointerEvent) {
|
||||
private handlePointerDown() {
|
||||
pointerPress();
|
||||
this.showFocusRing = shouldShowStrongFocus();
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
import {customElement} from 'lit/decorators.js';
|
||||
|
||||
import {styles as forcedColorsStyles} from './lib/forced-colors-styles.css.js';
|
||||
import {Radio} from './lib/radio.js';
|
||||
import {styles} from './lib/radio-styles.css.js';
|
||||
|
||||
@ -18,5 +19,5 @@ declare global {
|
||||
/** @soyCompatible */
|
||||
@customElement('md-radio')
|
||||
export class MdRadio extends Radio {
|
||||
static override styles = [styles];
|
||||
static override styles = [styles, forcedColorsStyles];
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import {html} from 'lit';
|
||||
|
||||
import {MdFocusRing} from '../focus/focus-ring.js';
|
||||
import {Environment} from '../testing/environment.js';
|
||||
import {createTokenTests} from '../testing/tokens.js';
|
||||
|
||||
import {RadioHarness} from './harness.js';
|
||||
import {MdRadio} from './radio.js';
|
||||
@ -32,7 +33,7 @@ const radioGroupPreSelected = html`
|
||||
<md-radio id="b1" name="b"></md-radio>
|
||||
`;
|
||||
|
||||
describe('md-radio', () => {
|
||||
describe('<md-radio>', () => {
|
||||
const env = new Environment();
|
||||
|
||||
// Note, this would be better in the harness, but waiting in the test setup
|
||||
@ -53,6 +54,10 @@ describe('md-radio', () => {
|
||||
return {harnesses, root};
|
||||
}
|
||||
|
||||
describe('.styles', () => {
|
||||
createTokenTests(MdRadio.styles);
|
||||
});
|
||||
|
||||
describe('basic', () => {
|
||||
it('initializes as an md-radio', async () => {
|
||||
const {harnesses} = await setupTest();
|
||||
|
Loading…
Reference in New Issue
Block a user