mirror of
https://github.com/material-components/material-web.git
synced 2024-09-21 10:38:19 +03:00
chore: move segmented button into the new directory
PiperOrigin-RevId: 461113111
This commit is contained in:
parent
5b2ec5e549
commit
88b164b00f
142
segmented_button/lib/_outlined-segmented-button-theme.scss
Normal file
142
segmented_button/lib/_outlined-segmented-button-theme.scss
Normal file
@ -0,0 +1,142 @@
|
||||
//
|
||||
// Copyright 2022 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
@use 'sass:map';
|
||||
@use '@material/web/sass/resolvers';
|
||||
@use '@material/web/sass/theme';
|
||||
@use '@material/web/tokens';
|
||||
|
||||
@use './segmented-button-theme';
|
||||
|
||||
$light-theme: tokens.md-comp-outlined-segmented-button-values();
|
||||
|
||||
$dark-theme: tokens.md-comp-outlined-segmented-button-values(
|
||||
(
|
||||
md-sys-color: tokens.md-sys-color-values-dark-dynamic(),
|
||||
)
|
||||
);
|
||||
|
||||
@mixin theme-styles($theme, $resolvers: resolvers.$material) {
|
||||
$theme: theme.validate-theme-styles(
|
||||
$light-theme,
|
||||
$theme,
|
||||
$require-all: false
|
||||
);
|
||||
$theme: segmented-button-theme.resolve-theme($theme, $resolvers);
|
||||
$theme: theme.create-theme-vars($theme, 'segmented-button');
|
||||
|
||||
@include segmented-button-theme.unselected-ripple(
|
||||
(
|
||||
'hover-state-layer-color':
|
||||
map.get($theme, 'unselected-hover-state-layer-color'),
|
||||
'hover-state-layer-opacity': map.get($theme, 'hover-state-layer-opacity'),
|
||||
'focus-state-layer-color':
|
||||
map.get($theme, 'unselected-focus-state-layer-color'),
|
||||
'focus-state-layer-opacity': map.get($theme, 'focus-state-layer-opacity'),
|
||||
'pressed-state-layer-color':
|
||||
map.get($theme, 'unselected-pressed-state-layer-color'),
|
||||
'pressed-state-layer-opacity':
|
||||
map.get($theme, 'pressed-state-layer-opacity'),
|
||||
)
|
||||
);
|
||||
|
||||
@include segmented-button-theme.selected-ripple(
|
||||
(
|
||||
'hover-state-layer-color':
|
||||
map.get($theme, 'selected-hover-state-layer-color'),
|
||||
'hover-state-layer-opacity': map.get($theme, 'hover-state-layer-opacity'),
|
||||
'focus-state-layer-color':
|
||||
map.get($theme, 'selected-focus-state-layer-color'),
|
||||
'focus-state-layer-opacity': map.get($theme, 'focus-state-layer-opacity'),
|
||||
'pressed-state-layer-color':
|
||||
map.get($theme, 'selected-pressed-state-layer-color'),
|
||||
'pressed-state-layer-opacity':
|
||||
map.get($theme, 'pressed-state-layer-opacity'),
|
||||
)
|
||||
);
|
||||
|
||||
@include segmented-button-theme.container-color(
|
||||
map.get($theme, 'selected-container-color')
|
||||
);
|
||||
|
||||
@include segmented-button-theme.outline-colors(
|
||||
(
|
||||
'default': map.get($theme, 'outline-color'),
|
||||
'disabled': map.get($theme, 'disabled-outline-color'),
|
||||
)
|
||||
);
|
||||
|
||||
@include segmented-button-theme.icon-size(
|
||||
map.get($theme, 'with-icon-icon-size')
|
||||
);
|
||||
|
||||
// Under the following conditions, we need to account for extra space between
|
||||
// the graphic and the text label/icon content:
|
||||
//
|
||||
// 1. A button with an icon and a label.
|
||||
// 2. A selected button with a label and checkmark.
|
||||
// 3. A selected button with an icon and checkmark but no label.
|
||||
//
|
||||
// We calculate a larger width here instead of using padding or margin for
|
||||
// two main reasons:
|
||||
//
|
||||
// 1. We may need to transition between a zero width and an explicit width.
|
||||
// 2. Both margin and padding take up space when a node has child content
|
||||
// even when a zero width is set and overflow is set to hidden.
|
||||
//
|
||||
// Because of those reasons, we calculate a new width with the given values.
|
||||
@include segmented-button-theme.visible-graphic-width(
|
||||
// TODO(b/198759625): Use padding token instead of hardcoded 8px value.
|
||||
calc(map.get($theme, 'with-icon-icon-size') + 8px)
|
||||
);
|
||||
|
||||
@include segmented-button-theme.unselected-icon-colors(
|
||||
(
|
||||
'default': map.get($theme, 'unselected-with-icon-icon-color'),
|
||||
'hover': map.get($theme, 'unselected-hover-icon-color'),
|
||||
'focus': map.get($theme, 'unselected-focus-icon-color'),
|
||||
'pressed': map.get($theme, 'unselected-pressed-icon-color'),
|
||||
'disabled': map.get($theme, 'disabled-icon-color'),
|
||||
)
|
||||
);
|
||||
|
||||
@include segmented-button-theme.selected-icon-colors(
|
||||
(
|
||||
'default': map.get($theme, 'selected-with-icon-icon-color'),
|
||||
'hover': map.get($theme, 'selected-hover-icon-color'),
|
||||
'focus': map.get($theme, 'selected-focus-icon-color'),
|
||||
'pressed': map.get($theme, 'selected-pressed-icon-color'),
|
||||
'disabled': map.get($theme, 'disabled-icon-color'),
|
||||
)
|
||||
);
|
||||
|
||||
@include segmented-button-theme.text-label-font(
|
||||
(
|
||||
'font': map.get($theme, 'label-text-font'),
|
||||
'size': map.get($theme, 'label-text-size'),
|
||||
'tracking': map.get($theme, 'label-text-tracking'),
|
||||
'weight': map.get($theme, 'label-text-weight'),
|
||||
)
|
||||
);
|
||||
|
||||
@include segmented-button-theme.selected-text-label-color(
|
||||
(
|
||||
'default': map.get($theme, 'selected-label-text-color'),
|
||||
'hover': map.get($theme, 'selected-hover-label-text-color'),
|
||||
'focus': map.get($theme, 'selected-focus-label-text-color'),
|
||||
'pressed': map.get($theme, 'selected-pressed-label-text-color'),
|
||||
)
|
||||
);
|
||||
|
||||
@include segmented-button-theme.unselected-text-label-color(
|
||||
(
|
||||
'default': map.get($theme, 'unselected-label-text-color'),
|
||||
'hover': map.get($theme, 'unselected-hover-label-text-color'),
|
||||
'focus': map.get($theme, 'unselected-focus-label-text-color'),
|
||||
'pressed': map.get($theme, 'unselected-pressed-label-text-color'),
|
||||
'disabled': map.get($theme, 'disabled-label-text-color'),
|
||||
)
|
||||
);
|
||||
}
|
19
segmented_button/lib/_outlined-segmented-button.scss
Normal file
19
segmented_button/lib/_outlined-segmented-button.scss
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// Copyright 2022 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// stylelint-disable selector-class-pattern -- allow `md3-` class selectors.
|
||||
|
||||
@mixin static-styles() {
|
||||
.md3-segmented-button__outline {
|
||||
border-radius: inherit;
|
||||
border-style: solid;
|
||||
// TODO(b/233762888): Move border-width and inset into theme after generating latest version of tokens.
|
||||
border-width: 1px;
|
||||
inset: 0px -0.5px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
298
segmented_button/lib/_segmented-button-theme.scss
Normal file
298
segmented_button/lib/_segmented-button-theme.scss
Normal file
@ -0,0 +1,298 @@
|
||||
//
|
||||
// Copyright 2022 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// stylelint-disable selector-class-pattern -- allow `md3-` class selectors.
|
||||
|
||||
@use 'sass:map';
|
||||
@use '@material/web/sass/color';
|
||||
@use '@material/web/sass/typography';
|
||||
@use '@material/web/ripple/ripple-theme';
|
||||
|
||||
@mixin unselected-ripple($ripple) {
|
||||
&.md3-segmented-button--unselected {
|
||||
@include ripple-theme.theme(
|
||||
(
|
||||
focus-state-layer-color: map.get($ripple, 'focus-state-layer-color'),
|
||||
focus-state-layer-opacity: map.get($ripple, 'focus-state-layer-opacity'),
|
||||
hover-state-layer-color: map.get($ripple, 'hover-state-layer-color'),
|
||||
hover-state-layer-opacity: map.get($ripple, 'hover-state-layer-opacity'),
|
||||
pressed-state-layer-color: map.get($ripple, 'pressed-state-layer-color'),
|
||||
pressed-state-layer-opacity:
|
||||
map.get($ripple, 'pressed-state-layer-opacity'),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin selected-ripple($ripple) {
|
||||
&.md3-segmented-button--selected {
|
||||
@include ripple-theme.theme(
|
||||
(
|
||||
focus-state-layer-color: map.get($ripple, 'focus-state-layer-color'),
|
||||
focus-state-layer-opacity: map.get($ripple, 'focus-state-layer-opacity'),
|
||||
hover-state-layer-color: map.get($ripple, 'hover-state-layer-color'),
|
||||
hover-state-layer-opacity: map.get($ripple, 'hover-state-layer-opacity'),
|
||||
pressed-state-layer-color: map.get($ripple, 'pressed-state-layer-color'),
|
||||
pressed-state-layer-opacity:
|
||||
map.get($ripple, 'pressed-state-layer-opacity'),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Container color applies the given color to the segmented button container.
|
||||
/// @param {Color} $color - the color to apply.
|
||||
///
|
||||
@mixin container-color($color) {
|
||||
&.md3-segmented-button--selected:enabled {
|
||||
background-color: $color;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Outline color applies the given color to the segmented button outline.
|
||||
/// @param {Map} $colors - a map of state keys with color values. Accepted keys
|
||||
/// include ('default', 'disabled').
|
||||
///
|
||||
@mixin outline-colors($colors) {
|
||||
.md3-segmented-button__outline {
|
||||
border-color: map.get($colors, 'default');
|
||||
}
|
||||
|
||||
&:disabled .md3-segmented-button__outline {
|
||||
border-color: map.get($colors, 'disabled');
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Unselected icon colors sets the color of the icon in the unselected state.
|
||||
/// @param {Map} $colors - a map of state keys with color values. Accepted keys
|
||||
/// include ('default', 'hover', 'focus', 'pressed', 'disabled').
|
||||
///
|
||||
@mixin unselected-icon-colors($colors) {
|
||||
@include _icon-color(map.get($colors, 'default'));
|
||||
|
||||
&:hover {
|
||||
@include _icon-color(map.get($colors, 'hover'));
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@include _icon-color(map.get($colors, 'focus'));
|
||||
}
|
||||
|
||||
&:active {
|
||||
@include _icon-color(map.get($colors, 'pressed'));
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
@include _icon-color(map.get($colors, 'disabled'));
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Selected icon colors sets the color of the icon in the selected state.
|
||||
/// @param {Map} $colors - a map of state keys with color values. Accepted keys
|
||||
/// include ('default', 'hover', 'focus', 'pressed', 'disabled').
|
||||
///
|
||||
@mixin selected-icon-colors($colors) {
|
||||
&.md3-segmented-button--selected {
|
||||
@include _icon-color(map.get($colors, 'default'));
|
||||
@include _checkmark-color(map.get($colors, 'default'));
|
||||
|
||||
&:hover {
|
||||
@include _checkmark-color(map.get($colors, 'hover'));
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@include _checkmark-color(map.get($colors, 'focus'));
|
||||
}
|
||||
|
||||
&:active {
|
||||
@include _checkmark-color(map.get($colors, 'pressed'));
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
@include _checkmark-color(map.get($colors, 'disabled'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Icon size applies the given size to the segmented button icon and checkmark
|
||||
/// when included.
|
||||
/// @param {Number} $size - the size of the icon.
|
||||
///
|
||||
@mixin icon-size($size) {
|
||||
.md3-segmented-button__graphic,
|
||||
.md3-segmented-button__checkmark,
|
||||
.md3-segmented-button__icon,
|
||||
.md3-segmented-button__icon ::slotted([slot='icon']) {
|
||||
height: $size;
|
||||
width: $size;
|
||||
font-size: $size;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Visible graphic width sets the width of the graphic area when it is visible
|
||||
/// (i.e. when it has content and should take up space on the screen). The
|
||||
/// graphic should only take up space under the following conditions:
|
||||
///
|
||||
/// 1. The segmented button has an icon and a label.
|
||||
/// 2. The segmented button is selected and has a label and a checkmark.
|
||||
/// 3. The segmented button is selected with a checkmark and an icon but has
|
||||
/// no label.
|
||||
///
|
||||
/// @param {Number} $width - the width of the visible graphic.
|
||||
///
|
||||
@mixin visible-graphic-width($width) {
|
||||
&.md3-segmented-button--with-icon.md3-segmented-button--with-label,
|
||||
&.md3-segmented-button--selected.md3-segmented-button--with-label.md3-segmented-button--with-checkmark,
|
||||
&.md3-segmented-button--selected.md3-segmented-button--without-label.md3-segmented-button--with-checkmark {
|
||||
.md3-segmented-button__graphic {
|
||||
width: $width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Checkmark color sets the color of the selected-state checkmark.
|
||||
/// @param {Map} $colors - a map of state keys with color values. Accepted keys
|
||||
/// include ('default', 'hover', 'focus', 'pressed', 'disabled').
|
||||
///
|
||||
@mixin checkmark-color($colors) {
|
||||
.md3-segmented-button__checkmark-path {
|
||||
stroke: $color;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Text label font sets the font properties of the text label.
|
||||
/// @param {Map} $font-map - a map of font keys with font values. Accepted keys
|
||||
/// include ('font', 'size', 'tracking', weight').
|
||||
///
|
||||
@mixin text-label-font($font-map) {
|
||||
.md3-segmented-button__label-text {
|
||||
font-family: map.get($font-map, 'font');
|
||||
font-size: map.get($font-map, 'size');
|
||||
letter-spacing: map.get($font-map, 'tracking');
|
||||
font-weight: map.get($font-map, 'weight');
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Unselected text label color applies the map of state keys and color values
|
||||
/// to the text label only when the button is both unselected and enabled.
|
||||
/// @param {Map} $colors - a map of state keys with color values. Accepted keys
|
||||
/// include ('default', 'hover', 'focus', 'pressed', 'disabled').
|
||||
///
|
||||
@mixin unselected-text-label-color($colors) {
|
||||
&.md3-segmented-button--unselected:enabled {
|
||||
@include _text-label-colors($colors);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
@include _text-label-color(map.get($colors, 'disabled'));
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Selected text label color applies the map of state keys and color values
|
||||
/// to the text label only when the button is both selected and enabled.
|
||||
/// @param {Map} $colors - a map of state keys with color values. Accepted keys
|
||||
/// include ('default', 'hover', 'focus', 'pressed').
|
||||
///
|
||||
@mixin selected-text-label-color($colors) {
|
||||
&.md3-segmented-button--selected:enabled {
|
||||
@include _text-label-colors($colors);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Icon label color sets the color of the icon.
|
||||
/// @param {Color} $color - the color of the icon.
|
||||
///
|
||||
@mixin _icon-color($color) {
|
||||
.md3-segmented-button__icon {
|
||||
color: $color;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Checkmark label color sets the color of the checkmark.
|
||||
/// @param {Color} $color - the color of the checkmark.
|
||||
///
|
||||
@mixin _checkmark-color($color) {
|
||||
.md3-segmented-button__checkmark-path {
|
||||
stroke: $color;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Text label colors applies the given color map to the label text for the
|
||||
/// configured states.
|
||||
/// @param {Map} $colors - a map of state keys with color values. Accepted keys
|
||||
/// include ('default', 'hover', 'focus', 'pressed', 'disabled').
|
||||
///
|
||||
@mixin _text-label-colors($colors) {
|
||||
@include _text-label-color(map.get($colors, 'default'));
|
||||
|
||||
&:hover {
|
||||
@include _text-label-color(map.get($colors, 'hover'));
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@include _text-label-color(map.get($colors, 'focus'));
|
||||
}
|
||||
|
||||
&:active {
|
||||
@include _text-label-color(map.get($colors, 'pressed'));
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Text label color sets the color of the text label.
|
||||
/// @param {Color} $color - the color of the text label.
|
||||
///
|
||||
@mixin _text-label-color($color) {
|
||||
.md3-segmented-button__label-text {
|
||||
color: $color;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Resolve theme returns the given theme with all values resolved.
|
||||
///
|
||||
@function resolve-theme($theme, $resolvers) {
|
||||
$theme: typography.resolve-theme(
|
||||
$theme,
|
||||
map.get($resolvers, 'typography'),
|
||||
'label-text'
|
||||
);
|
||||
$theme: _flatten-disabled-colors($theme);
|
||||
@return $theme;
|
||||
}
|
||||
|
||||
@function _flatten-disabled-colors($theme) {
|
||||
@return color.join-color-and-opacity-pairs(
|
||||
$theme,
|
||||
(
|
||||
(
|
||||
color-key: 'disabled-container-color',
|
||||
opacity-key: 'disabled-container-opacity'
|
||||
),
|
||||
(
|
||||
color-key: 'disabled-label-text-color',
|
||||
opacity-key: 'disabled-label-text-opacity'
|
||||
),
|
||||
(color-key: 'disabled-icon-color', opacity-key: 'disabled-icon-opacity'),
|
||||
(
|
||||
color-key: 'disabled-outline-color',
|
||||
opacity-key: 'disabled-outline-opacity'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
160
segmented_button/lib/_segmented-button.scss
Normal file
160
segmented_button/lib/_segmented-button.scss
Normal file
@ -0,0 +1,160 @@
|
||||
//
|
||||
// Copyright 2022 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// stylelint-disable selector-class-pattern -- allow `md3-` class selectors.
|
||||
|
||||
@use '@material/web/motion/animation';
|
||||
@use '@material/web/sass/touch-target';
|
||||
|
||||
@mixin static-styles() {
|
||||
@keyframes md3-segmented-button-checkmark-selection-draw-in {
|
||||
from {
|
||||
stroke-dashoffset: 29.7833385;
|
||||
}
|
||||
to {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes md3-segmented-button-simple-fade-out {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes md3-segmented-button-simple-fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
:host {
|
||||
display: inline-flex;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.md3-segmented-button {
|
||||
align-items: center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: inherit;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.md3-segmented-button:enabled {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.md3-segmented-button__focus-ring {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.md3-segmented-button__ripple {
|
||||
border-radius: inherit;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.md3-segmented-button__touch {
|
||||
@include touch-target.touch-target($width: 100%);
|
||||
}
|
||||
|
||||
.md3-segmented-button__leading,
|
||||
.md3-segmented-button__graphic {
|
||||
display: inline-flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.md3-segmented-button__graphic {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.md3-segmented-button__graphic {
|
||||
transition: animation.standard(width, 150ms);
|
||||
}
|
||||
|
||||
.md3-segmented-button--unselected.md3-segmented-button--with-label,
|
||||
.md3-segmented-button--unselected.md3-segmented-button--without-label,
|
||||
.md3-segmented-button--selected.md3-segmented-button--without-checkmark {
|
||||
.md3-segmented-button__graphic {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.md3-segmented-button--unselected .md3-segmented-button__checkmark {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.md3-segmented-button--selected.md3-segmented-button--with-label {
|
||||
.md3-segmented-button__icon {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.md3-segmented-button--with-label .md3-segmented-button__checkmark {
|
||||
display: inline-flex;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.md3-segmented-button__checkmark-path {
|
||||
stroke-width: 2px;
|
||||
stroke-dasharray: 29.7833385;
|
||||
}
|
||||
|
||||
.md3-segmented-button--selecting {
|
||||
.md3-segmented-button__checkmark-path {
|
||||
// We immediately render the checkmark in the animation start treatment
|
||||
// because we're using an animation delay. If we didn't have the delay,
|
||||
// the checkmark would render in the base fully-drawn state during the
|
||||
// brief animation-delay period which would look wrong.
|
||||
stroke-dashoffset: 29.7833385;
|
||||
animation: md3-segmented-button-checkmark-selection-draw-in;
|
||||
animation-duration: 150ms;
|
||||
animation-delay: 50ms;
|
||||
animation-fill-mode: forwards;
|
||||
animation-timing-function: animation.$standard-easing;
|
||||
}
|
||||
&.md3-segmented-button--with-label .md3-segmented-button__icon {
|
||||
animation: md3-segmented-button-simple-fade-out;
|
||||
animation-duration: 75ms;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
}
|
||||
|
||||
.md3-segmented-button--deselecting {
|
||||
.md3-segmented-button__checkmark {
|
||||
animation: md3-segmented-button-simple-fade-out;
|
||||
animation-duration: 50ms;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
&.md3-segmented-button--with-label .md3-segmented-button__icon {
|
||||
// We immediately render the icon in the animation start treatment
|
||||
// because we're using an animation delay. If we didn't have the delay,
|
||||
// the icon would render with full opacity during the brief
|
||||
// animation-delay period which would look wrong.
|
||||
opacity: 0;
|
||||
animation: md3-segmented-button-simple-fade-in;
|
||||
animation-delay: 50ms;
|
||||
animation-duration: 150ms;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
}
|
||||
}
|
26
segmented_button/lib/outlined-segmented-button.ts
Normal file
26
segmented_button/lib/outlined-segmented-button.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2022 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {html, TemplateResult} from 'lit';
|
||||
import {ClassInfo} from 'lit/directives/class-map';
|
||||
|
||||
import {SegmentedButton} from './segmented-button';
|
||||
|
||||
/** @soyCompatible */
|
||||
export class OutlinedSegmentedButton extends SegmentedButton {
|
||||
/** @soyTemplate */
|
||||
protected override getRenderClasses(): ClassInfo {
|
||||
return {
|
||||
...super.getRenderClasses(),
|
||||
'md3-segmented-button--outlined': true,
|
||||
};
|
||||
}
|
||||
|
||||
/** @soyTemplate */
|
||||
protected override renderOutline(): TemplateResult {
|
||||
return html`<span class="md3-segmented-button__outline"></span>`;
|
||||
}
|
||||
}
|
17
segmented_button/lib/outlined-styles.scss
Normal file
17
segmented_button/lib/outlined-styles.scss
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// Copyright 2022 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// stylelint-disable selector-class-pattern -- allow `md3-` class selectors.
|
||||
|
||||
@use './outlined-segmented-button';
|
||||
@use './outlined-segmented-button-theme';
|
||||
|
||||
@include outlined-segmented-button.static-styles;
|
||||
|
||||
.md3-segmented-button--outlined {
|
||||
@include outlined-segmented-button-theme.theme-styles(
|
||||
outlined-segmented-button-theme.$light-theme
|
||||
);
|
||||
}
|
8
segmented_button/lib/segmented-button-styles.scss
Normal file
8
segmented_button/lib/segmented-button-styles.scss
Normal file
@ -0,0 +1,8 @@
|
||||
//
|
||||
// Copyright 2022 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
@use './segmented-button';
|
||||
|
||||
@include segmented-button.static-styles;
|
220
segmented_button/lib/segmented-button.ts
Normal file
220
segmented_button/lib/segmented-button.ts
Normal file
@ -0,0 +1,220 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2021 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import '@material/web/focus/focus-ring';
|
||||
|
||||
import {ActionElement, BeginPressConfig, EndPressConfig} from '@material/web/actionelement/action-element';
|
||||
import {ariaProperty} from '@material/web/decorators/aria-property';
|
||||
import {pointerPress, shouldShowStrongFocus} from '@material/web/focus/strong-focus';
|
||||
import {MdRipple} from '@material/web/ripple/ripple';
|
||||
import {html, PropertyValues, TemplateResult} from 'lit';
|
||||
import {property, query, queryAssignedElements, state} from 'lit/decorators';
|
||||
import {ClassInfo, classMap} from 'lit/directives/class-map';
|
||||
import {ifDefined} from 'lit/directives/if-defined';
|
||||
|
||||
/**
|
||||
* SegmentedButton is a web component implementation of the Material Design
|
||||
* segmented button component. It is intended **only** for use as a child of a
|
||||
* `SementedButtonSet` component. It is **not** intended for use in any other
|
||||
* context.
|
||||
* @soyCompatible
|
||||
*/
|
||||
export class SegmentedButton extends ActionElement {
|
||||
@property({type: Boolean}) disabled = false;
|
||||
@property({type: Boolean}) selected = false;
|
||||
@property({type: String}) label = '';
|
||||
@property({type: Boolean}) noCheckmark = false;
|
||||
@property({type: Boolean}) hasIcon = false;
|
||||
|
||||
/** @soyPrefixAttribute */
|
||||
@ariaProperty
|
||||
@property({type: String, attribute: 'aria-label'})
|
||||
override ariaLabel!: string;
|
||||
|
||||
@state() protected animState: string = '';
|
||||
@state() protected showFocusRing = false;
|
||||
@queryAssignedElements({slot: 'icon', flatten: true})
|
||||
protected iconElement!: HTMLElement[];
|
||||
@query('md-ripple') ripple!: MdRipple;
|
||||
|
||||
protected override update(props: PropertyValues<SegmentedButton>) {
|
||||
this.animState = this.nextAnimationState(props);
|
||||
super.update(props);
|
||||
// NOTE: This needs to be set *after* calling super.update() to ensure the
|
||||
// appropriate CSS classes are applied.
|
||||
this.hasIcon = this.iconElement.length > 0;
|
||||
}
|
||||
|
||||
private nextAnimationState(changedProps: PropertyValues<SegmentedButton>):
|
||||
string {
|
||||
const prevSelected = changedProps.get('selected');
|
||||
// Early exit for first update.
|
||||
if (prevSelected === undefined) return '';
|
||||
|
||||
const nextSelected = this.selected;
|
||||
const nextHasCheckmark = !this.noCheckmark;
|
||||
if (!prevSelected && nextSelected && nextHasCheckmark) {
|
||||
return 'selecting';
|
||||
}
|
||||
if (prevSelected && !nextSelected && nextHasCheckmark) {
|
||||
return 'deselecting';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
override beginPress({positionEvent}: BeginPressConfig) {
|
||||
this.ripple.beginPress(positionEvent);
|
||||
}
|
||||
|
||||
override endPress(options: EndPressConfig) {
|
||||
this.ripple.endPress();
|
||||
super.endPress(options);
|
||||
if (!options.cancelled) {
|
||||
const event = new Event(
|
||||
'segmented-button-interaction', {bubbles: true, composed: true});
|
||||
this.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
override handlePointerDown(e: PointerEvent) {
|
||||
super.handlePointerDown(e);
|
||||
pointerPress();
|
||||
this.showFocusRing = shouldShowStrongFocus();
|
||||
}
|
||||
|
||||
override handlePointerUp(e: PointerEvent) {
|
||||
super.handlePointerUp(e);
|
||||
}
|
||||
|
||||
protected handlePointerEnter(e: PointerEvent) {
|
||||
this.ripple.beginHover(e);
|
||||
}
|
||||
|
||||
override handlePointerLeave(e: PointerEvent) {
|
||||
super.handlePointerLeave(e);
|
||||
this.ripple.endHover();
|
||||
}
|
||||
|
||||
protected handleFocus() {
|
||||
this.showFocusRing = shouldShowStrongFocus();
|
||||
}
|
||||
|
||||
protected handleBlur() {
|
||||
this.showFocusRing = false;
|
||||
}
|
||||
|
||||
/** @soyTemplate */
|
||||
override render(): TemplateResult {
|
||||
// TODO(b/219531586): Remove clickmod handler once resolved.
|
||||
return html`
|
||||
<button
|
||||
tabindex="${this.disabled ? '-1' : '0'}"
|
||||
aria-label="${ifDefined(this.ariaLabel)}"
|
||||
aria-pressed=${this.selected}
|
||||
.disabled=${this.disabled}
|
||||
@focus="${this.handleFocus}"
|
||||
@blur="${this.handleBlur}"
|
||||
@pointerdown="${this.handlePointerDown}"
|
||||
@pointerup="${this.handlePointerUp}"
|
||||
@pointercancel="${this.handlePointerCancel}"
|
||||
@pointerleave="${this.handlePointerLeave}"
|
||||
@pointerenter="${this.handlePointerEnter}"
|
||||
@click="${this.handleClick}"
|
||||
@clickmod="${this.handleClick}"
|
||||
@contextmenu="${this.handleContextMenu}"
|
||||
class="md3-segmented-button ${classMap(this.getRenderClasses())}">
|
||||
${this.renderFocusRing()}
|
||||
${this.renderRipple()}
|
||||
${this.renderOutline()}
|
||||
${this.renderLeading()}
|
||||
${this.renderLabel()}
|
||||
${this.renderTouchTarget()}
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
|
||||
/** @soyTemplate */
|
||||
protected getRenderClasses(): ClassInfo {
|
||||
return {
|
||||
'md3-segmented-button--selected': this.selected,
|
||||
'md3-segmented-button--unselected': !this.selected,
|
||||
'md3-segmented-button--with-label': this.label !== '',
|
||||
'md3-segmented-button--without-label': this.label === '',
|
||||
'md3-segmented-button--with-icon': this.hasIcon,
|
||||
'md3-segmented-button--with-checkmark': !this.noCheckmark,
|
||||
'md3-segmented-button--without-checkmark': this.noCheckmark,
|
||||
'md3-segmented-button--selecting': this.animState === 'selecting',
|
||||
'md3-segmented-button--deselecting': this.animState === 'deselecting',
|
||||
};
|
||||
}
|
||||
|
||||
/** @soyTemplate */
|
||||
protected renderFocusRing(): TemplateResult {
|
||||
return html`<md-focus-ring .visible="${
|
||||
this.showFocusRing}" class="md3-segmented-button__focus-ring"></md-focus-ring>`;
|
||||
}
|
||||
|
||||
/** @soyTemplate */
|
||||
protected renderRipple(): TemplateResult|string {
|
||||
return html`<md-ripple .disabled="${
|
||||
this.disabled}" class="md3-segmented-button__ripple"> </md-ripple>`;
|
||||
}
|
||||
|
||||
/** @soyTemplate */
|
||||
protected renderOutline(): TemplateResult {
|
||||
return html``;
|
||||
}
|
||||
|
||||
/** @soyTemplate */
|
||||
protected renderLeading(): TemplateResult {
|
||||
return this.label === '' ? this.renderLeadingWithoutLabel() :
|
||||
this.renderLeadingWithLabel();
|
||||
}
|
||||
|
||||
/** @soyTemplate */
|
||||
protected renderLeadingWithoutLabel(): TemplateResult {
|
||||
return html`
|
||||
<span class="md3-segmented-button__leading" aria-hidden="true">
|
||||
<span class="md3-segmented-button__graphic">
|
||||
<svg class="md3-segmented-button__checkmark" viewBox="0 0 24 24">
|
||||
<path class="md3-segmented-button__checkmark-path" fill="none" d="M1.73,12.91 8.1,19.28 22.79,4.59"></path>
|
||||
</svg>
|
||||
</span>
|
||||
<span class="md3-segmented-button__icon" aria-hidden="true">
|
||||
<slot name="icon"></slot>
|
||||
</span>
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
|
||||
/** @soyTemplate */
|
||||
protected renderLeadingWithLabel(): TemplateResult {
|
||||
return html`
|
||||
<span class="md3-segmented-button__leading" aria-hidden="true">
|
||||
<span class="md3-segmented-button__graphic">
|
||||
<svg class="md3-segmented-button__checkmark" viewBox="0 0 24 24">
|
||||
<path class="md3-segmented-button__checkmark-path" fill="none" d="M1.73,12.91 8.1,19.28 22.79,4.59"></path>
|
||||
</svg>
|
||||
<span class="md3-segmented-button__icon" aria-hidden="true">
|
||||
<slot name="icon"></slot>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
|
||||
/** @soyTemplate */
|
||||
protected renderLabel(): TemplateResult {
|
||||
return html`
|
||||
<span class="md3-segmented-button__label-text">${this.label}</span>
|
||||
`;
|
||||
}
|
||||
|
||||
/** @soyTemplate */
|
||||
protected renderTouchTarget(): TemplateResult {
|
||||
return html`<span class="md3-segmented-button__touch"></span>`;
|
||||
}
|
||||
}
|
29
segmented_button/outlined-segmented-button.ts
Normal file
29
segmented_button/outlined-segmented-button.ts
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2021 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {customElement} from 'lit/decorators';
|
||||
|
||||
import {OutlinedSegmentedButton} from './lib/outlined-segmented-button';
|
||||
import {styles as outlinedStyles} from './lib/outlined-styles.css';
|
||||
import {styles as sharedStyles} from './lib/segmented-button-styles.css';
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'md-outlined-segmented-button': MdOutlinedSegmentedButton;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MdOutlinedSegmentedButton is the custom element for the Material
|
||||
* Design outlined segmented button component.
|
||||
* @soyCompatible
|
||||
* @final
|
||||
* @suppress {visibility}
|
||||
*/
|
||||
@customElement('md-outlined-segmented-button')
|
||||
export class MdOutlinedSegmentedButton extends OutlinedSegmentedButton {
|
||||
static override styles = [sharedStyles, outlinedStyles];
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
//
|
||||
// Copyright 2022 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
@use 'sass:map';
|
||||
@use '@material/web/sass/resolvers';
|
||||
@use '@material/web/sass/theme';
|
||||
@use '@material/web/tokens';
|
||||
|
||||
@use './segmented-button-set-theme';
|
||||
|
||||
$light-theme: tokens.md-comp-outlined-segmented-button-values();
|
||||
|
||||
$dark-theme: tokens.md-comp-outlined-segmented-button-values(
|
||||
(
|
||||
md-sys-color: tokens.md-sys-color-values-dark-dynamic(),
|
||||
)
|
||||
);
|
||||
|
||||
@mixin theme($theme, $resolvers: resolvers.$material) {
|
||||
$theme: theme.validate-theme-styles(
|
||||
$light-theme,
|
||||
$theme,
|
||||
$require-all: false
|
||||
);
|
||||
$theme: segmented-button-set-theme.resolve-theme($theme, $resolvers);
|
||||
$theme: theme.create-theme-vars($theme, 'segmented-button');
|
||||
@include theme.emit-theme-vars($theme);
|
||||
}
|
||||
|
||||
@mixin theme-styles($theme, $resolvers: resolvers.$material) {
|
||||
$theme: theme.validate-theme-styles(
|
||||
$light-theme,
|
||||
$theme,
|
||||
$require-all: false
|
||||
);
|
||||
$theme: segmented-button-set-theme.resolve-theme($theme, $resolvers);
|
||||
$theme: theme.create-theme-vars($theme, 'segmented-button');
|
||||
|
||||
@include segmented-button-set-theme.container-height(
|
||||
map.get($theme, 'container-height')
|
||||
);
|
||||
|
||||
@include segmented-button-set-theme.container-shape(
|
||||
(
|
||||
'start-start': map.get($theme, 'shape-start-start'),
|
||||
'end-start': map.get($theme, 'shape-end-start'),
|
||||
'start-end': map.get($theme, 'shape-start-end'),
|
||||
'end-end': map.get($theme, 'shape-end-end'),
|
||||
)
|
||||
);
|
||||
}
|
12
segmented_button_set/lib/_outlined-segmented-button-set.scss
Normal file
12
segmented_button_set/lib/_outlined-segmented-button-set.scss
Normal file
@ -0,0 +1,12 @@
|
||||
//
|
||||
// Copyright 2022 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// stylelint-disable selector-class-pattern -- allow `md3-` class selectors.
|
||||
|
||||
@mixin static-styles() {
|
||||
.md3-segmented-button-set--outlined {
|
||||
// TODO(b/213634341): Write styles.
|
||||
}
|
||||
}
|
41
segmented_button_set/lib/_segmented-button-set-theme.scss
Normal file
41
segmented_button_set/lib/_segmented-button-set-theme.scss
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// Copyright 2022 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
@use 'sass:map';
|
||||
@use '@material/web/sass/shape';
|
||||
|
||||
///
|
||||
/// Container height sets the height of the container.
|
||||
/// @param {Number} $height - the height of the container.
|
||||
///
|
||||
@mixin container-height($height) {
|
||||
height: $height;
|
||||
}
|
||||
|
||||
///
|
||||
/// Container shape sets the shape of the container.
|
||||
/// @param {Map} $shape - a map of CSS logical radius keys with number values.
|
||||
/// Accepted keys include ('start-start', 'end-start', 'start-end',
|
||||
/// 'end-end').
|
||||
///
|
||||
@mixin container-shape($shape) {
|
||||
::slotted(:first-child) {
|
||||
border-start-start-radius: map.get($shape, 'start-start');
|
||||
border-end-start-radius: map.get($shape, 'end-start');
|
||||
}
|
||||
|
||||
::slotted(:last-child) {
|
||||
border-start-end-radius: map.get($shape, 'start-end');
|
||||
border-end-end-radius: map.get($shape, 'end-end');
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Resolve theme returns the given theme with all values resolved.
|
||||
///
|
||||
@function resolve-theme($theme, $resolvers) {
|
||||
$theme: shape.resolve-theme($theme, map.get($resolvers, 'shape'), 'shape');
|
||||
@return $theme;
|
||||
}
|
21
segmented_button_set/lib/_segmented-button-set.scss
Normal file
21
segmented_button_set/lib/_segmented-button-set.scss
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// Copyright 2022 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// stylelint-disable selector-class-pattern -- allow `md3-` class selectors.
|
||||
|
||||
@mixin static-styles() {
|
||||
:host {
|
||||
display: flex;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.md3-segmented-button-set {
|
||||
display: grid;
|
||||
grid-auto-columns: 1fr;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-rows: auto;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
20
segmented_button_set/lib/outlined-segmented-button-set.ts
Normal file
20
segmented_button_set/lib/outlined-segmented-button-set.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2022 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {ClassInfo} from 'lit/directives/class-map';
|
||||
|
||||
import {SegmentedButtonSet} from './segmented-button-set';
|
||||
|
||||
/** @soyCompatible */
|
||||
export class OutlinedSegmentedButtonSet extends SegmentedButtonSet {
|
||||
/** @soyTemplate */
|
||||
protected override getRenderClasses(): ClassInfo {
|
||||
return {
|
||||
...super.getRenderClasses(),
|
||||
'md3-segmented-button-set--outlined': true,
|
||||
};
|
||||
}
|
||||
}
|
17
segmented_button_set/lib/outlined-styles.scss
Normal file
17
segmented_button_set/lib/outlined-styles.scss
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// Copyright 2022 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// stylelint-disable selector-class-pattern -- allow `md3-` class selectors.
|
||||
|
||||
@use './outlined-segmented-button-set';
|
||||
@use './outlined-segmented-button-set-theme';
|
||||
|
||||
@include outlined-segmented-button-set.static-styles;
|
||||
|
||||
.md3-segmented-button-set--outlined {
|
||||
@include outlined-segmented-button-set-theme.theme-styles(
|
||||
outlined-segmented-button-set-theme.$light-theme
|
||||
);
|
||||
}
|
77
segmented_button_set/lib/segmented-button-set.ts
Normal file
77
segmented_button_set/lib/segmented-button-set.ts
Normal file
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* @requirecss {segmented_button_set.lib.shared_styles}
|
||||
*
|
||||
* @license
|
||||
* Copyright 2021 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {ariaProperty} from '@material/web/decorators/aria-property';
|
||||
import {html, LitElement, TemplateResult} from 'lit';
|
||||
import {property, queryAssignedElements} from 'lit/decorators';
|
||||
import {ClassInfo, classMap} from 'lit/directives/class-map';
|
||||
import {ifDefined} from 'lit/directives/if-defined';
|
||||
|
||||
import {SegmentedButton} from '../../segmented_button/lib/segmented-button';
|
||||
|
||||
/**
|
||||
* SegmentedButtonSet is the parent component for two or more
|
||||
* `SegmentedButton` components. **Only** `SegmentedButton` components may be
|
||||
* used as children.
|
||||
* @soyCompatible
|
||||
*/
|
||||
export class SegmentedButtonSet extends LitElement {
|
||||
@property({type: Boolean}) multiselect = false;
|
||||
|
||||
/** @soyPrefixAttribute */
|
||||
@ariaProperty
|
||||
@property({type: String, attribute: 'aria-label'})
|
||||
override ariaLabel!: string;
|
||||
|
||||
@queryAssignedElements({flatten: true}) buttons!: SegmentedButton[];
|
||||
|
||||
private handleSegmentedButtonInteraction(e: CustomEvent) {
|
||||
// TODO(b/229912696): Support interactions.
|
||||
const index = this.buttons.indexOf(e.target as SegmentedButton);
|
||||
this.toggleSelection(index);
|
||||
}
|
||||
|
||||
private toggleSelection(index: number) {
|
||||
if (this.indexOutOfBounds(index)) return;
|
||||
if (this.multiselect) {
|
||||
this.buttons[index].selected = !this.buttons[index].selected;
|
||||
return;
|
||||
}
|
||||
// Single-select segmented buttons are not unselectable.
|
||||
this.buttons[index].selected = true;
|
||||
// Deselect all other buttons for single-select.
|
||||
for (let i = 0; i < this.buttons.length; i++) {
|
||||
if (i === index) continue;
|
||||
this.buttons[i].selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
private indexOutOfBounds(index: number): boolean {
|
||||
return index < 0 || index >= this.buttons.length;
|
||||
}
|
||||
|
||||
/** @soyTemplate */
|
||||
override render(): TemplateResult {
|
||||
return html`
|
||||
<span
|
||||
role="group"
|
||||
@segmented-button-interaction="${this.handleSegmentedButtonInteraction}"
|
||||
aria-label="${ifDefined(this.ariaLabel)}"
|
||||
class="md3-segmented-button-set ${classMap(this.getRenderClasses())}">
|
||||
<slot></slot>
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
|
||||
/** @soyTemplate */
|
||||
protected getRenderClasses(): ClassInfo {
|
||||
return {
|
||||
// TODO(b/213634341): Write styles.
|
||||
};
|
||||
}
|
||||
}
|
8
segmented_button_set/lib/shared-styles.scss
Normal file
8
segmented_button_set/lib/shared-styles.scss
Normal file
@ -0,0 +1,8 @@
|
||||
//
|
||||
// Copyright 2022 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
@use './segmented-button-set';
|
||||
|
||||
@include segmented-button-set.static-styles;
|
29
segmented_button_set/outlined-segmented-button-set.ts
Normal file
29
segmented_button_set/outlined-segmented-button-set.ts
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2022 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {customElement} from 'lit/decorators';
|
||||
|
||||
import {OutlinedSegmentedButtonSet} from './lib/outlined-segmented-button-set';
|
||||
import {styles as outlinedStyles} from './lib/outlined-styles.css';
|
||||
import {styles as sharedStyles} from './lib/shared-styles.css';
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'md-outlined-segmented-button-set': MdOutlinedSegmentedButtonSet;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MdOutlinedSegmentedButtonSet is the custom element for the Material
|
||||
* Design outlined segmented button set component.
|
||||
* @soyCompatible
|
||||
* @final
|
||||
* @suppress {visibility}
|
||||
*/
|
||||
@customElement('md-outlined-segmented-button-set')
|
||||
export class MdOutlinedSegmentedButtonSet extends OutlinedSegmentedButtonSet {
|
||||
static override styles = [sharedStyles, outlinedStyles];
|
||||
}
|
Loading…
Reference in New Issue
Block a user