mirror of
https://github.com/material-components/material-web.git
synced 2024-09-11 13:46:10 +03:00
refactor(menu,list): clean up list-menu tokens and remove list-item variant components
In this PR: - Remove unnecessary list-item variants - `md-list-item-icon` -> md-icon[data-variant=icon] - `md-list-item-video` -> video[data-variant=video] - `md-list-item-avatar` -> :is(img,div)[data-variant=avatar] - `md-list-item-avatar` -> img[data-variant=avatar] - also upgrade menu tokens to maximize token sharing in list-item and menu-item - testing menu and list for unused & undefined tokens - fixed some small things in list to align with spec more PiperOrigin-RevId: 513932810
This commit is contained in:
parent
f7649add43
commit
5092de07e9
@ -44,8 +44,8 @@ Elevation | ✅ | ❌ | ❌
|
||||
Focus ring | ✅ | ❌ | ❌
|
||||
Field | ✅ | ✅ | 🟡
|
||||
Icon | ✅ | ✅ | ❌
|
||||
List | ✅ | ✅ | 🟡
|
||||
Menu | ✅ | ✅ | 🟡
|
||||
List | ✅ | 🟡 | 🟡
|
||||
Menu | ✅ | 🟡 | 🟡
|
||||
Progress indicator (circular) | ❌ | ❌ | ❌
|
||||
Progress indicator (linear) | ❌ | ❌ | ❌
|
||||
Radio button | ✅ | ✅ | ❌
|
||||
|
@ -7,6 +7,7 @@
|
||||
// Selector '.md3-*' should only be used in this project.
|
||||
|
||||
// go/keep-sorted start
|
||||
@use 'sass:list';
|
||||
@use 'sass:map';
|
||||
@use 'sass:string';
|
||||
// go/keep-sorted end
|
||||
@ -18,9 +19,9 @@
|
||||
// go/keep-sorted end
|
||||
|
||||
@mixin theme($tokens) {
|
||||
$reference: resolve-tokens(tokens.md-comp-list-values());
|
||||
$tokens: resolve-tokens($tokens);
|
||||
$reference: tokens.md-comp-list-values();
|
||||
$tokens: theme.validate-theme($reference, $tokens);
|
||||
$tokens: resolve-tokens($tokens);
|
||||
$tokens: theme.create-theme-vars($tokens, list);
|
||||
|
||||
@include theme.emit-theme-vars($tokens);
|
||||
@ -65,16 +66,25 @@
|
||||
container-color,
|
||||
map.get($tokens, list-item-container-color)
|
||||
);
|
||||
$list-item-tokens: list-item.resolve-tokens($tokens);
|
||||
$list-tokens: ();
|
||||
|
||||
@each $token, $value in $tokens {
|
||||
$is-unique: not map.has-key($list-item-tokens, $token);
|
||||
|
||||
@if $is-unique {
|
||||
$list-tokens: map.set($list-tokens, $token, $value);
|
||||
}
|
||||
}
|
||||
$list-tokens: remove-unused-tokens($tokens);
|
||||
|
||||
@return $list-tokens;
|
||||
}
|
||||
|
||||
// removes unused tokens
|
||||
@function remove-unused-tokens($tokens) {
|
||||
$unused-tokens: ();
|
||||
@each $token in map-keys($tokens) {
|
||||
$index: string.index($token, 'list-item');
|
||||
|
||||
@if $index {
|
||||
$unused-tokens: list.append($unused-tokens, $token);
|
||||
}
|
||||
}
|
||||
|
||||
@each $token in $unused-tokens {
|
||||
$tokens: map.remove($tokens, $token);
|
||||
}
|
||||
|
||||
@return $tokens;
|
||||
}
|
||||
|
@ -1,40 +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.
|
||||
|
||||
// go/keep-sorted start
|
||||
@use '../../../sass/map-ext';
|
||||
@use '../../../sass/theme';
|
||||
@use '../../../tokens';
|
||||
// go/keep-sorted end
|
||||
|
||||
@mixin styles() {
|
||||
$tokens: map-ext.pick(
|
||||
tokens.md-comp-list-values(),
|
||||
(
|
||||
list-item-leading-avatar-color,
|
||||
list-item-leading-avatar-shape,
|
||||
list-item-leading-avatar-size
|
||||
)
|
||||
);
|
||||
|
||||
$tokens: theme.create-theme-vars($tokens, list);
|
||||
|
||||
:host {
|
||||
@each $token, $value in $tokens {
|
||||
--_#{$token}: #{$value};
|
||||
}
|
||||
}
|
||||
|
||||
.md3-list-item__avatar {
|
||||
border-radius: var(--_list-item-leading-avatar-shape);
|
||||
display: inline-flex;
|
||||
height: var(--_list-item-leading-avatar-size);
|
||||
margin-inline-start: 16px;
|
||||
width: var(--_list-item-leading-avatar-size);
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// go/keep-sorted start
|
||||
@use './list-item-avatar';
|
||||
// go/keep-sorted end
|
||||
|
||||
@include list-item-avatar.styles;
|
@ -1,36 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2022 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {html, LitElement, nothing, TemplateResult} from 'lit';
|
||||
import {property} from 'lit/decorators.js';
|
||||
|
||||
// tslint:disable-next-line:enforce-comments-on-exported-symbols
|
||||
export class ListItemAvatar extends LitElement {
|
||||
/**
|
||||
* The image `src` for the avatar
|
||||
*/
|
||||
@property() avatar = '';
|
||||
|
||||
/**
|
||||
* The image `alt`.
|
||||
*/
|
||||
@property() altText = '';
|
||||
|
||||
/**
|
||||
* The image `loading` attribute.
|
||||
*/
|
||||
@property() loading: 'eager'|'lazy' = 'eager';
|
||||
|
||||
override render(): TemplateResult {
|
||||
return html`
|
||||
<img
|
||||
src="${this.avatar}"
|
||||
alt="${this.altText || nothing}"
|
||||
loading="${this.loading}"
|
||||
class="md3-list-item__avatar" />
|
||||
`;
|
||||
}
|
||||
}
|
@ -1,55 +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.
|
||||
|
||||
// go/keep-sorted start
|
||||
@use 'sass:map';
|
||||
// go/keep-sorted end
|
||||
// go/keep-sorted start
|
||||
@use '../../../icon/icon';
|
||||
@use '../../../sass/map-ext';
|
||||
@use '../../../sass/theme';
|
||||
// go/keep-sorted end
|
||||
|
||||
$_custom-property-prefix: 'list-item-icon';
|
||||
|
||||
$_reference: (
|
||||
list-item-icon-color: #000,
|
||||
list-item-icon-size: 0,
|
||||
list-item-icon-opacity: 1,
|
||||
);
|
||||
|
||||
@mixin theme($tokens) {
|
||||
$tokens: theme.validate-theme($_reference, $tokens);
|
||||
$tokens: theme.create-theme-vars($tokens, $_custom-property-prefix);
|
||||
|
||||
@include theme.emit-theme-vars($tokens);
|
||||
}
|
||||
|
||||
@mixin styles() {
|
||||
$tokens: $_reference;
|
||||
$tokens: theme.create-theme-vars($tokens, $_custom-property-prefix);
|
||||
|
||||
:host {
|
||||
@each $token, $value in $tokens {
|
||||
--_#{$token}: #{$value};
|
||||
}
|
||||
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.md3-list-item__icon {
|
||||
@include icon.theme(
|
||||
(
|
||||
color: var(--_list-item-icon-color),
|
||||
size: var(--_list-item-icon-size),
|
||||
)
|
||||
);
|
||||
opacity: var(--_list-item-icon-opacity);
|
||||
padding-inline-start: 16px;
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// go/keep-sorted start
|
||||
@use './list-item-icon';
|
||||
// go/keep-sorted end
|
||||
|
||||
@include list-item-icon.styles;
|
@ -1,18 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2021 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import '../../../icon/icon.js';
|
||||
|
||||
import {html, LitElement, TemplateResult} from 'lit';
|
||||
|
||||
// tslint:disable-next-line:enforce-comments-on-exported-symbols
|
||||
export class ListItemIcon extends LitElement {
|
||||
override render(): TemplateResult {
|
||||
return html`
|
||||
<md-icon class="md3-list-item__icon"><slot></slot></md-icon>
|
||||
`;
|
||||
}
|
||||
}
|
@ -1,63 +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.
|
||||
|
||||
// go/keep-sorted start
|
||||
@use 'sass:map';
|
||||
// go/keep-sorted end
|
||||
// go/keep-sorted start
|
||||
@use '../../../sass/map-ext';
|
||||
@use '../../../sass/theme';
|
||||
@use '../../../tokens';
|
||||
// go/keep-sorted end
|
||||
|
||||
$_custom-property-prefix: 'list-item-image';
|
||||
|
||||
@mixin theme($tokens) {
|
||||
$reference: map-ext.pick(
|
||||
tokens.md-comp-list-values(),
|
||||
(
|
||||
list-item-leading-image-height,
|
||||
list-item-leading-image-width,
|
||||
list-item-leading-image-shape
|
||||
)
|
||||
);
|
||||
|
||||
$tokens: theme.validate-theme($reference, $tokens);
|
||||
$tokens: theme.create-theme-vars($tokens, $_custom-property-prefix);
|
||||
|
||||
@include theme.emit-theme-vars($tokens);
|
||||
}
|
||||
|
||||
@mixin styles() {
|
||||
$tokens: map-ext.pick(
|
||||
tokens.md-comp-list-values(),
|
||||
(
|
||||
list-item-leading-image-height,
|
||||
list-item-leading-image-width,
|
||||
list-item-leading-image-shape
|
||||
)
|
||||
);
|
||||
|
||||
$tokens: theme.create-theme-vars($tokens, $_custom-property-prefix);
|
||||
|
||||
:host {
|
||||
@each $token, $value in $tokens {
|
||||
--_#{$token}: #{$value};
|
||||
}
|
||||
}
|
||||
|
||||
.md3-list-item__image {
|
||||
display: inline-flex;
|
||||
margin-block-end: 8px;
|
||||
margin-block-start: 8px;
|
||||
margin-inline-start: 16px;
|
||||
height: var(--_list-item-leading-image-height);
|
||||
width: var(--_list-item-leading-image-width);
|
||||
border-radius: var(--_list-item-leading-image-shape);
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// go/keep-sorted start
|
||||
@use './list-item-image';
|
||||
// go/keep-sorted end
|
||||
|
||||
@include list-item-image.styles;
|
@ -1,36 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2022 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {html, LitElement, nothing, TemplateResult} from 'lit';
|
||||
import {property} from 'lit/decorators.js';
|
||||
|
||||
// tslint:disable-next-line:enforce-comments-on-exported-symbols
|
||||
export class ListItemImage extends LitElement {
|
||||
/**
|
||||
* The image `src`.
|
||||
*/
|
||||
@property() image = '';
|
||||
|
||||
/**
|
||||
* The image `alt`.
|
||||
*/
|
||||
@property() altText = '';
|
||||
|
||||
/**
|
||||
* The image `loading` attribute.
|
||||
*/
|
||||
@property() loading: 'eager'|'lazy' = 'eager';
|
||||
|
||||
override render(): TemplateResult {
|
||||
return html`
|
||||
<img
|
||||
src="${this.image}"
|
||||
alt="${this.altText || nothing}"
|
||||
loading=${this.loading}
|
||||
class="md3-list-item__image" />
|
||||
`;
|
||||
}
|
||||
}
|
@ -9,14 +9,12 @@
|
||||
// go/keep-sorted end
|
||||
// go/keep-sorted start
|
||||
@use '../../../focus/focus-ring';
|
||||
@use '../../../icon/icon';
|
||||
@use '../../../ripple/ripple';
|
||||
@use '../../../sass/map-ext';
|
||||
@use '../../../sass/theme';
|
||||
@use '../../../sass/typography';
|
||||
@use '../../../tokens';
|
||||
@use '../icon/list-item-icon';
|
||||
@use '../image/list-item-image';
|
||||
@use '../video/list-item-video';
|
||||
// go/keep-sorted end
|
||||
|
||||
@mixin theme($tokens) {
|
||||
@ -30,26 +28,26 @@
|
||||
}
|
||||
|
||||
@mixin styles() {
|
||||
$tokens: resolve-tokens(tokens.md-comp-list-values());
|
||||
$tokens: theme.create-theme-vars($tokens, 'list');
|
||||
|
||||
:host {
|
||||
@each $token, $value in $tokens {
|
||||
--_#{$token}: #{$value};
|
||||
}
|
||||
}
|
||||
|
||||
@include _list-item;
|
||||
@include _image;
|
||||
@include _icon;
|
||||
@include _avatar;
|
||||
@include _video;
|
||||
}
|
||||
|
||||
@mixin _list-item() {
|
||||
:host {
|
||||
color: unset;
|
||||
|
||||
@include list-item-image.theme(
|
||||
(
|
||||
list-item-leading-image-height: var(--_list-item-leading-image-height),
|
||||
list-item-leading-image-width: var(--_list-item-leading-image-width),
|
||||
list-item-leading-image-shape: var(--_list-item-leading-image-shape),
|
||||
)
|
||||
);
|
||||
@include list-item-video.theme(
|
||||
(
|
||||
list-item-small-leading-video-height:
|
||||
var(--_list-item-small-leading-video-height),
|
||||
list-item-large-leading-video-height:
|
||||
var(--_list-item-large-leading-video-height),
|
||||
list-item-leading-video-width: var(--_list-item-leading-video-width),
|
||||
list-item-leading-video-shape: var(--_list-item-leading-video-shape),
|
||||
)
|
||||
);
|
||||
@include focus-ring.theme(
|
||||
(
|
||||
offset-vertical: -2px,
|
||||
@ -80,7 +78,7 @@
|
||||
background-color: var(--_list-item-container-color);
|
||||
border-radius: var(--_list-item-container-shape);
|
||||
|
||||
&.enabled {
|
||||
&:not(.disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@ -89,6 +87,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.with-one-line {
|
||||
min-height: var(--_list-item-one-line-container-height);
|
||||
}
|
||||
@ -102,41 +105,15 @@
|
||||
}
|
||||
|
||||
.start {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex: 0 0 auto;
|
||||
z-index: 1;
|
||||
|
||||
@include list-item-icon.theme(
|
||||
(
|
||||
list-item-icon-color: var(--_list-item-leading-icon-color),
|
||||
list-item-icon-size: var(--_list-item-leading-icon-size),
|
||||
)
|
||||
);
|
||||
|
||||
:hover & {
|
||||
@include list-item-icon.theme(
|
||||
(
|
||||
list-item-icon-color: var(--_list-item-hover-leading-icon-icon-color),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
:active & {
|
||||
@include list-item-icon.theme(
|
||||
(
|
||||
list-item-icon-color:
|
||||
var(--_list-item-pressed-leading-icon-icon-color),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
.disabled & {
|
||||
@include list-item-icon.theme(
|
||||
(
|
||||
list-item-icon-color: var(--_list-item-disabled-leading-icon-color),
|
||||
list-item-icon-opacity:
|
||||
var(--_list-item-disabled-leading-icon-opacity),
|
||||
)
|
||||
);
|
||||
.with-three-line & {
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.with-leading-thumbnail &,
|
||||
@ -150,50 +127,25 @@
|
||||
}
|
||||
|
||||
.body {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
flex: 1 0 0;
|
||||
padding-inline-start: 16px;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.end {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
flex: 0 0 auto;
|
||||
padding-inline-end: 24px;
|
||||
z-index: 1;
|
||||
|
||||
@include list-item-icon.theme(
|
||||
(
|
||||
list-item-icon-color: var(--_list-item-trailing-icon-color),
|
||||
list-item-icon-size: var(--_list-item-trailing-icon-size),
|
||||
)
|
||||
);
|
||||
|
||||
:hover & {
|
||||
@include list-item-icon.theme(
|
||||
(
|
||||
list-item-icon-color: var(--_list-item-hover-trailing-icon-icon-color),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
:active & {
|
||||
@include list-item-icon.theme(
|
||||
(
|
||||
list-item-icon-color:
|
||||
var(--_list-item-pressed-trailing-icon-icon-color),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
.disabled & {
|
||||
@include list-item-icon.theme(
|
||||
(
|
||||
list-item-icon-color: var(--_list-item-disabled-trailing-icon-color),
|
||||
list-item-icon-opacity:
|
||||
var(--_list-item-disabled-trailing-icon-opacity),
|
||||
)
|
||||
);
|
||||
.with-three-line & {
|
||||
justify-content: start;
|
||||
}
|
||||
}
|
||||
|
||||
@ -202,6 +154,18 @@
|
||||
color: var(--_list-item-label-text-color);
|
||||
font: var(--_list-item-label-text-type);
|
||||
|
||||
:hover & {
|
||||
color: var(--_list-item-hover-label-text-color);
|
||||
}
|
||||
|
||||
:focus & {
|
||||
color: var(--_list-item-focus-label-text-color);
|
||||
}
|
||||
|
||||
:active & {
|
||||
color: var(--_list-item-pressed-label-text-color);
|
||||
}
|
||||
|
||||
.disabled & {
|
||||
color: var(--_list-item-disabled-label-text-color);
|
||||
opacity: var(--_list-item-disabled-label-text-opacity);
|
||||
@ -210,7 +174,6 @@
|
||||
|
||||
.supporting-text {
|
||||
display: block;
|
||||
padding-block-start: 4px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
@ -244,13 +207,19 @@
|
||||
color: var(--_list-item-disabled-label-text-color);
|
||||
opacity: var(--_list-item-disabled-label-text-opacity);
|
||||
}
|
||||
}
|
||||
|
||||
.ripple {
|
||||
display: inline-flex;
|
||||
inset: 0;
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
.with-three-line & {
|
||||
/*
|
||||
* In three line, trailing-supporting-text must align with the mid-line of
|
||||
* the headline text.
|
||||
*/
|
||||
padding-block-start: calc(
|
||||
(
|
||||
var(--_list-item-label-text-line-height) -
|
||||
var(--_list-item-trailing-supporting-text-line-height)
|
||||
) / 2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.focus-ring {
|
||||
@ -258,6 +227,199 @@
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _image() {
|
||||
::slotted([data-variant='image']) {
|
||||
display: inline-flex;
|
||||
margin-inline-start: 16px;
|
||||
height: var(--_list-item-leading-image-height);
|
||||
width: var(--_list-item-leading-image-width);
|
||||
border-radius: var(--_list-item-leading-image-shape);
|
||||
/* Min height is two-line height */
|
||||
padding-block: calc(
|
||||
(
|
||||
var(--_list-item-two-line-container-height) -
|
||||
var(--_list-item-leading-image-height)
|
||||
) / 2
|
||||
);
|
||||
|
||||
.with-three-line & {
|
||||
padding-block: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _icon() {
|
||||
slot[name='start']::slotted([data-variant='icon']) {
|
||||
@include icon.theme(
|
||||
(
|
||||
color: var(--_list-item-leading-icon-color),
|
||||
size: var(--_list-item-leading-icon-size),
|
||||
)
|
||||
);
|
||||
|
||||
.with-three-line & {
|
||||
/* In three line, icon must align with the mid-line of headline text */
|
||||
padding-block-start: calc(
|
||||
(
|
||||
var(--_list-item-label-text-line-height) -
|
||||
var(--_list-item-leading-icon-size)
|
||||
) / 2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
slot[name='end']::slotted([data-variant='icon']) {
|
||||
@include icon.theme(
|
||||
(
|
||||
color: var(--_list-item-trailing-icon-color),
|
||||
size: var(--_list-item-trailing-icon-size),
|
||||
)
|
||||
);
|
||||
|
||||
.with-three-line & {
|
||||
/* In three line, icon must align with the mid-line of headline text */
|
||||
padding-block-start: calc(
|
||||
(
|
||||
var(--_list-item-label-text-line-height) -
|
||||
var(--_list-item-trailing-icon-size)
|
||||
) / 2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
::slotted([data-variant='icon']) {
|
||||
padding-inline-start: 16px;
|
||||
}
|
||||
|
||||
:hover {
|
||||
slot[name='start']::slotted([data-variant='icon']) {
|
||||
@include icon.theme(
|
||||
(
|
||||
color: var(--_list-item-hover-leading-icon-icon-color),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
slot[name='end']::slotted([data-variant='icon']) {
|
||||
@include icon.theme(
|
||||
(
|
||||
color: var(--_list-item-hover-trailing-icon-icon-color),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
:focus {
|
||||
slot[name='start']::slotted([data-variant='icon']) {
|
||||
@include icon.theme(
|
||||
(
|
||||
color: var(--_list-item-focus-leading-icon-icon-color),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
slot[name='end']::slotted([data-variant='icon']) {
|
||||
@include icon.theme(
|
||||
(
|
||||
color: var(--_list-item-focus-trailing-icon-icon-color),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
:active {
|
||||
slot[name='start']::slotted([data-variant='icon']) {
|
||||
@include icon.theme(
|
||||
(
|
||||
color: var(--_list-item-pressed-leading-icon-icon-color),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
slot[name='end']::slotted([data-variant='icon']) {
|
||||
@include icon.theme(
|
||||
(
|
||||
color: var(--_list-item-pressed-trailing-icon-icon-color),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.disabled {
|
||||
slot[name='start']::slotted([data-variant='icon']) {
|
||||
opacity: var(--_list-item-disabled-leading-icon-opacity);
|
||||
|
||||
@include icon.theme(
|
||||
(
|
||||
color: var(--_list-item-disabled-leading-icon-color),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
slot[name='end']::slotted([data-variant='icon']) {
|
||||
opacity: var(--_list-item-disabled-trailing-icon-opacity);
|
||||
|
||||
@include icon.theme(
|
||||
(
|
||||
color: var(--_list-item-disabled-trailing-icon-color),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _avatar() {
|
||||
::slotted([data-variant='avatar']) {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-inline-start: 16px;
|
||||
background-color: var(--_list-item-leading-avatar-color);
|
||||
height: var(--_list-item-leading-avatar-size);
|
||||
width: var(--_list-item-leading-avatar-size);
|
||||
border-radius: var(--_list-item-leading-avatar-shape);
|
||||
color: var(--_list-item-leading-avatar-label-color);
|
||||
font: var(--_list-item-leading-avatar-label-type);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin _video() {
|
||||
::slotted([data-variant='video']),
|
||||
::slotted([data-variant='video-large']) {
|
||||
display: inline-flex;
|
||||
object-fit: cover;
|
||||
height: var(--_list-item-small-leading-video-height);
|
||||
width: var(--_list-item-leading-video-width);
|
||||
border-radius: var(--_list-item-leading-video-shape);
|
||||
/* Min height is three-line height */
|
||||
padding-block: calc(
|
||||
(
|
||||
var(--_list-item-three-line-container-height) -
|
||||
var(--_list-item-small-leading-video-height)
|
||||
) / 2
|
||||
);
|
||||
|
||||
/*
|
||||
* Let it auto-layout so that we don't mess with the icons and supporting
|
||||
* text that is supposed to be top-aligned in three-line.
|
||||
*/
|
||||
.with-three-line & {
|
||||
padding-block: 0;
|
||||
}
|
||||
}
|
||||
|
||||
::slotted([data-variant='video-large']) {
|
||||
/* Min height is three-line height */
|
||||
padding-block: calc(
|
||||
(
|
||||
var(--_list-item-three-line-container-height) -
|
||||
var(--_list-item-large-leading-video-height)
|
||||
) / 2
|
||||
);
|
||||
height: var(--_list-item-large-leading-video-height);
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves the tokens that are specific to list-item.
|
||||
///
|
||||
/// The tokenset for list include list plus all of list item. We do not want to
|
||||
@ -275,22 +437,51 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Do not include list-item-trailing-supporting-text or list-item-label-text
|
||||
// because we actually need the line-height tokens separately from the
|
||||
// *-type tokens.
|
||||
$list-item-tokens: typography.resolve-tokens(
|
||||
$list-item-tokens,
|
||||
'list-item-label-text',
|
||||
'list-item-supporting-text',
|
||||
'list-item-trailing-supporting-text'
|
||||
'list-item-leading-avatar-label',
|
||||
'list-item-overline'
|
||||
);
|
||||
|
||||
$list-item-tokens: _remove-unused-tokens($list-item-tokens);
|
||||
|
||||
@return $list-item-tokens;
|
||||
}
|
||||
|
||||
@mixin private-props() {
|
||||
$tokens: resolve-tokens(tokens.md-comp-list-values());
|
||||
$tokens: theme.create-theme-vars($tokens, 'list');
|
||||
// removes unused tokens
|
||||
@function _remove-unused-tokens($tokens) {
|
||||
$unused-tokens: (
|
||||
'list-item-container-elevation',
|
||||
'list-item-disabled-state-layer-color',
|
||||
'list-item-disabled-state-layer-opacity',
|
||||
'list-item-dragged-container-elevation',
|
||||
'list-item-dragged-label-text-color',
|
||||
'list-item-dragged-leading-icon-icon-color',
|
||||
'list-item-dragged-state-layer-color',
|
||||
'list-item-dragged-state-layer-opacity',
|
||||
'list-item-dragged-trailing-icon-icon-color',
|
||||
'list-item-overline-color',
|
||||
'list-item-overline-type',
|
||||
'list-item-selected-trailing-icon',
|
||||
'list-item-selected-trailing-icon-color',
|
||||
'list-item-unselected-trailing-icon-color',
|
||||
'list-item-label-text-font',
|
||||
'list-item-label-text-size',
|
||||
'list-item-label-text-tracking',
|
||||
'list-item-label-text-weight',
|
||||
'list-item-trailing-supporting-text-font',
|
||||
'list-item-trailing-supporting-text-size',
|
||||
'list-item-trailing-supporting-text-tracking',
|
||||
'list-item-trailing-supporting-text-weight'
|
||||
);
|
||||
|
||||
:host {
|
||||
@each $token, $value in $tokens {
|
||||
--_#{$token}: #{$value};
|
||||
}
|
||||
@each $token in $unused-tokens {
|
||||
$tokens: map.remove($tokens, $token);
|
||||
}
|
||||
|
||||
@return $tokens;
|
||||
}
|
||||
|
@ -1,10 +0,0 @@
|
||||
//
|
||||
// Copyright 2023 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// go/keep-sorted start
|
||||
@use './list-item';
|
||||
// go/keep-sorted end
|
||||
|
||||
@include list-item.private-props;
|
@ -51,15 +51,16 @@ export class ListItemEl extends LitElement implements ListItem {
|
||||
@property() headline = '';
|
||||
|
||||
/**
|
||||
* The one-line supporting text below the headline.
|
||||
* The one-line supporting text below the headline. Set
|
||||
* `multiLineSupportingText` to `true` to support multiple lines in the
|
||||
* supporting text.
|
||||
*/
|
||||
@property() supportingText = '';
|
||||
|
||||
/**
|
||||
* The multi-line supporting text below the headline. __NOTE:__ if set to a
|
||||
* truthy value, overrides the visibility and behavior of `supportingText`.
|
||||
* Modifies `supportingText` to support multiple lines.
|
||||
*/
|
||||
@property() multiLineSupportingText = '';
|
||||
@property({type: Boolean}) multiLineSupportingText = false;
|
||||
|
||||
/**
|
||||
* The supporting text placed at the end of the item. Overriden by elements
|
||||
@ -85,7 +86,7 @@ export class ListItemEl extends LitElement implements ListItem {
|
||||
* tabindex is set to 0, and in some list item variants (like md-list-item),
|
||||
* focuses the underlying item.
|
||||
*/
|
||||
@property({type: Boolean, reflect: true}) active = false;
|
||||
@property({type: Boolean, reflect: true}) active = false;
|
||||
|
||||
/**
|
||||
* READONLY. Sets the `md-list-item` attribute on the element.
|
||||
@ -132,13 +133,11 @@ export class ListItemEl extends LitElement implements ListItem {
|
||||
|
||||
override render(): TemplateResult {
|
||||
return this.renderListItem(html`
|
||||
${this.renderStart()}
|
||||
${this.renderBody()}
|
||||
${this.renderEnd()}
|
||||
<div class="ripple">
|
||||
<div class="content-wrapper">
|
||||
${this.renderStart()}
|
||||
${this.renderBody()}
|
||||
${this.renderEnd()}
|
||||
${this.renderRipple()}
|
||||
</div>
|
||||
<div class="focus-ring">
|
||||
${this.renderFocusRing()}
|
||||
</div>`);
|
||||
}
|
||||
@ -179,7 +178,7 @@ export class ListItemEl extends LitElement implements ListItem {
|
||||
* Handles rendering of the focus ring.
|
||||
*/
|
||||
protected renderFocusRing(): TemplateResult {
|
||||
return html`<md-focus-ring .visible="${
|
||||
return html`<md-focus-ring class="focus-ring" .visible="${
|
||||
this.showFocusRing}"></md-focus-ring>`;
|
||||
}
|
||||
|
||||
@ -188,13 +187,12 @@ export class ListItemEl extends LitElement implements ListItem {
|
||||
*/
|
||||
protected getRenderClasses(): ClassInfo {
|
||||
return {
|
||||
'with-one-line':
|
||||
this.supportingText === '' && this.multiLineSupportingText === '',
|
||||
'with-one-line': this.supportingText === '',
|
||||
'with-two-line':
|
||||
this.supportingText !== '' && this.multiLineSupportingText === '',
|
||||
'with-three-line': this.multiLineSupportingText !== '',
|
||||
'disabled': this.disabled,
|
||||
'enabled': !this.disabled,
|
||||
this.supportingText !== '' && !this.multiLineSupportingText,
|
||||
'with-three-line':
|
||||
this.supportingText !== '' && this.multiLineSupportingText,
|
||||
'disabled': this.disabled
|
||||
};
|
||||
}
|
||||
|
||||
@ -209,10 +207,8 @@ export class ListItemEl extends LitElement implements ListItem {
|
||||
* Handles rendering the headline and supporting text.
|
||||
*/
|
||||
protected renderBody(): TemplateResult {
|
||||
const supportingText = this.multiLineSupportingText !== '' ?
|
||||
this.renderMultiLineSupportingText() :
|
||||
this.supportingText !== '' ? this.renderSupportingText() :
|
||||
'';
|
||||
const supportingText =
|
||||
this.supportingText !== '' ? this.renderSupportingText() : '';
|
||||
|
||||
return html`<div class="body"
|
||||
><span class="label-text">${this.headline}</span>${supportingText}</div>`;
|
||||
@ -222,15 +218,16 @@ export class ListItemEl extends LitElement implements ListItem {
|
||||
* Renders the one-line supporting text.
|
||||
*/
|
||||
protected renderSupportingText(): TemplateResult {
|
||||
return html`<span class="supporting-text">${this.supportingText}</span>`;
|
||||
return html`<span
|
||||
class="supporting-text ${classMap(this.getSupportingTextClasses())}"
|
||||
>${this.supportingText}</span>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the multi-line supporting text
|
||||
* Gets the classes for the supporting text node
|
||||
*/
|
||||
protected renderMultiLineSupportingText(): TemplateResult {
|
||||
return html`<span class="supporting-text supporting-text--multi-line"
|
||||
>${this.multiLineSupportingText}</span>`;
|
||||
protected getSupportingTextClasses(): ClassInfo {
|
||||
return {'supporting-text--multi-line': this.multiLineSupportingText};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,71 +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.
|
||||
|
||||
// go/keep-sorted start
|
||||
@use 'sass:map';
|
||||
// go/keep-sorted end
|
||||
// go/keep-sorted start
|
||||
@use '../../../sass/map-ext';
|
||||
@use '../../../sass/theme';
|
||||
@use '../../../tokens';
|
||||
// go/keep-sorted end
|
||||
|
||||
$_custom-property-prefix: 'list-item-video';
|
||||
|
||||
@mixin theme($tokens) {
|
||||
$reference: map-ext.pick(
|
||||
tokens.md-comp-list-values(),
|
||||
(
|
||||
list-item-small-leading-video-height,
|
||||
list-item-large-leading-video-height,
|
||||
list-item-leading-video-width,
|
||||
list-item-leading-video-shape
|
||||
)
|
||||
);
|
||||
|
||||
$tokens: theme.validate-theme($reference, $tokens);
|
||||
$tokens: theme.create-theme-vars($tokens, $_custom-property-prefix);
|
||||
|
||||
@include theme.emit-theme-vars($tokens);
|
||||
}
|
||||
|
||||
@mixin styles() {
|
||||
$tokens: map-ext.pick(
|
||||
tokens.md-comp-list-values(),
|
||||
(
|
||||
list-item-small-leading-video-height,
|
||||
list-item-large-leading-video-height,
|
||||
list-item-leading-video-width,
|
||||
list-item-leading-video-shape
|
||||
)
|
||||
);
|
||||
|
||||
$tokens: theme.create-theme-vars($tokens, $_custom-property-prefix);
|
||||
|
||||
:host {
|
||||
@each $token, $value in $tokens {
|
||||
--_#{$token}: #{$value};
|
||||
}
|
||||
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.md3-list-item__video {
|
||||
display: inline-flex;
|
||||
margin-block-end: 12px;
|
||||
margin-block-start: 12px;
|
||||
object-fit: cover;
|
||||
height: var(--_list-item-small-leading-video-height);
|
||||
width: var(--_list-item-leading-video-width);
|
||||
border-radius: var(--_list-item-leading-video-shape);
|
||||
|
||||
&.large {
|
||||
height: var(--_list-item-large-leading-video-height);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// go/keep-sorted start
|
||||
@use './list-item-video';
|
||||
// go/keep-sorted end
|
||||
|
||||
@include list-item-video.styles;
|
@ -1,82 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2022 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {html, LitElement, nothing, TemplateResult} from 'lit';
|
||||
import {property} from 'lit/decorators.js';
|
||||
|
||||
/**
|
||||
* @fires loadeddata {Event} Dispatched whenever the native HTMLVideoElement
|
||||
* fires the loadeddate event.
|
||||
*/
|
||||
export class ListItemVideo extends LitElement {
|
||||
/**
|
||||
* Displays the video in a taller format
|
||||
*/
|
||||
@property({type: Boolean}) large = false;
|
||||
|
||||
/**
|
||||
* The underlying `<video>`'s `autoplay` property.
|
||||
*/
|
||||
@property({type: Boolean}) autoplay = false;
|
||||
|
||||
/**
|
||||
* The underlying `<video>`'s `muted` property.
|
||||
*/
|
||||
@property({type: Boolean}) muted = false;
|
||||
|
||||
/**
|
||||
* The underlying `<video>`'s `loop` property.
|
||||
*/
|
||||
@property({type: Boolean}) loop = false;
|
||||
|
||||
/**
|
||||
* The underlying `<video>`'s `controls` property.
|
||||
*/
|
||||
@property({type: Boolean}) controls = false;
|
||||
|
||||
/**
|
||||
* The underlying `<video>`'s `playsinline` property.
|
||||
*/
|
||||
@property({type: Boolean}) playsinline = false;
|
||||
|
||||
/**
|
||||
* The underlying `<video>`'s `preload` property.
|
||||
*/
|
||||
@property({type: String}) preload: ''|'auto'|'metadata'|'none' = '';
|
||||
|
||||
/**
|
||||
* The underlying `<video>`'s `poster` property.
|
||||
*/
|
||||
@property({type: String}) poster = '';
|
||||
|
||||
/**
|
||||
* The `src` of the video.
|
||||
*/
|
||||
@property({type: String}) video = '';
|
||||
|
||||
/**
|
||||
* The `alt` attribute if the video.
|
||||
*/
|
||||
@property({type: String}) altText = '';
|
||||
|
||||
override render(): TemplateResult {
|
||||
return html`
|
||||
<video
|
||||
@loadeddata=${() => this.dispatchEvent(new Event('loadeddata'))}
|
||||
.src="${this.video || nothing}"
|
||||
.poster="${this.poster || nothing}"
|
||||
alt="${this.altText || nothing}"
|
||||
.autoplay=${this.autoplay}
|
||||
.muted=${this.muted}
|
||||
.loop=${this.loop}
|
||||
.playsinline=${this.playsinline}
|
||||
.controls=${this.controls}
|
||||
class="md3-list-item__video ${this.large ? 'large' : ''}">
|
||||
<slot></slot>
|
||||
</video>
|
||||
`;
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2022 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {customElement} from 'lit/decorators.js';
|
||||
|
||||
import {ListItemAvatar} from './lib/avatar/list-item-avatar.js';
|
||||
import {styles} from './lib/avatar/list-item-avatar-styles.css.js';
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'md-list-item-avatar': MdListItemAvatar;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary An image avatar that is expected to be slotted into a list item.
|
||||
*
|
||||
* @final
|
||||
* @suppress {visibility}
|
||||
*/
|
||||
@customElement('md-list-item-avatar')
|
||||
export class MdListItemAvatar extends ListItemAvatar {
|
||||
static override styles = [styles];
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2021 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {customElement} from 'lit/decorators.js';
|
||||
|
||||
import {ListItemIcon} from './lib/icon/list-item-icon.js';
|
||||
import {styles} from './lib/icon/list-item-icon-styles.css.js';
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'md-list-item-icon': MdListItemIcon;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary A material icon that is expected to be slotted into a list item.
|
||||
*
|
||||
* @final
|
||||
* @suppress {visibility}
|
||||
*/
|
||||
@customElement('md-list-item-icon')
|
||||
export class MdListItemIcon extends ListItemIcon {
|
||||
static override styles = [styles];
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2022 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {customElement} from 'lit/decorators.js';
|
||||
|
||||
import {ListItemImage} from './lib/image/list-item-image.js';
|
||||
import {styles} from './lib/image/list-item-image-styles.css.js';
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'md-list-item-image': MdListItemImage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary An image that is expected to be slotted into a list item.
|
||||
*
|
||||
* @final
|
||||
* @suppress {visibility}
|
||||
*/
|
||||
@customElement('md-list-item-image')
|
||||
export class MdListItemImage extends ListItemImage {
|
||||
static override styles = [styles];
|
||||
}
|
@ -6,7 +6,6 @@
|
||||
|
||||
import {customElement} from 'lit/decorators.js';
|
||||
|
||||
import {styles as privateProps} from './lib/listitem/list-item-private-styles.css.js';
|
||||
import {styles} from './lib/listitem/list-item-styles.css.js';
|
||||
import {ListItemLink} from './lib/listitemlink/list-item-link.js';
|
||||
|
||||
@ -34,10 +33,28 @@ declare global {
|
||||
* item in a collection and act on it.
|
||||
* - Lists should present icons, text, and actions in a consistent format.
|
||||
*
|
||||
* Example slottable child variants are:
|
||||
*
|
||||
* - `video[data-variant=video]`
|
||||
* - `img,span[data-variant=avatar]`
|
||||
* - `img[data-variant=image]`
|
||||
* - `md-icon[data-variant=icon]`
|
||||
*
|
||||
* @example
|
||||
* ```html
|
||||
* <md-list-item-link
|
||||
* headline="User Name"
|
||||
* supportingText="user@name.com"
|
||||
* href="/accounts">
|
||||
* <md-icon data-variant="icon" slot="start">account_circle</md-icon>
|
||||
* <md-icon data-variant="icon" slot="end">open_in_new</md-icon>
|
||||
* </md-list-item-link>
|
||||
* ```
|
||||
*
|
||||
* @final
|
||||
* @suppress {visibility}
|
||||
*/
|
||||
@customElement('md-list-item-link')
|
||||
export class MdListItemLink extends ListItemLink {
|
||||
static override styles = [privateProps, styles];
|
||||
static override styles = [styles];
|
||||
}
|
||||
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2022 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {customElement} from 'lit/decorators.js';
|
||||
|
||||
import {ListItemVideo} from './lib/video/list-item-video.js';
|
||||
import {styles} from './lib/video/list-item-video-styles.css.js';
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'md-list-item-video': MdListItemVideo;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary A video that is expected to be slotted into a list item.
|
||||
*
|
||||
* @final
|
||||
* @suppress {visibility}
|
||||
*/
|
||||
@customElement('md-list-item-video')
|
||||
export class MdListItemVideo extends ListItemVideo {
|
||||
static override styles = [styles];
|
||||
}
|
@ -7,7 +7,6 @@
|
||||
import {customElement} from 'lit/decorators.js';
|
||||
|
||||
import {ListItemEl as ListItem} from './lib/listitem/list-item.js';
|
||||
import {styles as privateProps} from './lib/listitem/list-item-private-styles.css.js';
|
||||
import {styles} from './lib/listitem/list-item-styles.css.js';
|
||||
|
||||
declare global {
|
||||
@ -34,10 +33,29 @@ declare global {
|
||||
* item in a collection and act on it.
|
||||
* - Lists should present icons, text, and actions in a consistent format.
|
||||
*
|
||||
* Acceptable slottable child variants are:
|
||||
*
|
||||
* - `video[data-variant=video]`
|
||||
* - `img,span[data-variant=avatar]`
|
||||
* - `img[data-variant=image]`
|
||||
* - `md-icon[data-variant=icon]`
|
||||
*
|
||||
* @example
|
||||
* ```html
|
||||
* <md-list-item
|
||||
* headline="User Name"
|
||||
* supportingText="user@name.com">
|
||||
* <md-icon data-variant="icon" slot="start">account_circle</md-icon>
|
||||
* <md-icon data-variant="icon" slot="end">check</md-icon>
|
||||
* </md-list-item>
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* @final
|
||||
* @suppress {visibility}
|
||||
*/
|
||||
@customElement('md-list-item')
|
||||
export class MdListItem extends ListItem {
|
||||
static override styles = [privateProps, styles];
|
||||
static override styles = [styles];
|
||||
}
|
||||
|
@ -4,10 +4,21 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import './list.js';
|
||||
import './list-item.js';
|
||||
// import 'jasmine'; (google3-only)
|
||||
|
||||
describe('list tests', () => {
|
||||
// TODO(b/265211574): test list
|
||||
it('TODO', () => {});
|
||||
import {createTokenTests} from '../testing/tokens.js';
|
||||
|
||||
import {MdList} from './list.js';
|
||||
import {MdListItem} from './list-item.js';
|
||||
|
||||
describe('<md-list>', () => {
|
||||
describe('.styles', () => {
|
||||
createTokenTests(MdList.styles);
|
||||
});
|
||||
});
|
||||
|
||||
describe('<md-list-item>', () => {
|
||||
describe('.styles', () => {
|
||||
createTokenTests(MdListItem.styles);
|
||||
});
|
||||
});
|
||||
|
@ -4,16 +4,18 @@
|
||||
//
|
||||
|
||||
// go/keep-sorted start
|
||||
@use 'sass:list';
|
||||
@use 'sass:map';
|
||||
@use 'sass:string';
|
||||
// go/keep-sorted end
|
||||
// go/keep-sorted start
|
||||
@use '../../elevation/lib/elevation';
|
||||
@use '../../focus/focus-ring';
|
||||
@use '../../list/list';
|
||||
@use '../../list/list' as md-list;
|
||||
@use '../../list/list-item';
|
||||
@use '../../sass/resolvers';
|
||||
@use '../../sass/theme';
|
||||
@use '../../tokens/v0_160' as tokens; // TODO(b/270568087): update to latest
|
||||
@use './menuitem/menu-item';
|
||||
@use '../../tokens';
|
||||
// go/keep-sorted end
|
||||
|
||||
$_custom-property-prefix: 'menu';
|
||||
@ -36,9 +38,9 @@ $_custom-property-prefix: 'menu';
|
||||
--_#{$token}: #{$value};
|
||||
}
|
||||
|
||||
@include list.theme(
|
||||
@include md-list.theme(
|
||||
(
|
||||
container-color: var(--_container-color),
|
||||
list-item-container-color: var(--_container-color),
|
||||
)
|
||||
);
|
||||
|
||||
@ -106,5 +108,24 @@ $_custom-property-prefix: 'menu';
|
||||
}
|
||||
|
||||
@function _resolve-tokens($tokens) {
|
||||
@return elevation.resolve-tokens($tokens, 'elevation-key');
|
||||
$menu-tokens: _remove-unused-tokens($tokens);
|
||||
@return elevation.resolve-tokens($menu-tokens, 'elevation-key');
|
||||
}
|
||||
|
||||
// removes unused tokens
|
||||
@function _remove-unused-tokens($tokens) {
|
||||
$unused-tokens: ();
|
||||
@each $token in map-keys($tokens) {
|
||||
$index: string.index($token, 'list-item');
|
||||
|
||||
@if $index {
|
||||
$unused-tokens: list.append($unused-tokens, $token);
|
||||
}
|
||||
}
|
||||
|
||||
@each $token in $unused-tokens {
|
||||
$tokens: map.remove($tokens, $token);
|
||||
}
|
||||
|
||||
@return $tokens;
|
||||
}
|
||||
|
@ -120,8 +120,7 @@ export abstract class Menu extends LitElement {
|
||||
/**
|
||||
* The tabindex of the underlying list element.
|
||||
*/
|
||||
@property({type: Number, attribute: 'list-tab-index'})
|
||||
listTabIndex = 0;
|
||||
@property({type: Number, attribute: 'list-tab-index'}) listTabIndex = 0;
|
||||
/**
|
||||
* The max time between the keystrokes of the typeahead menu behavior before
|
||||
* it clears the typeahead buffer.
|
||||
@ -242,13 +241,14 @@ export abstract class Menu extends LitElement {
|
||||
protected renderList() {
|
||||
return html`
|
||||
<md-list
|
||||
.ariaLabel=${this.ariaLabel}
|
||||
role="menu"
|
||||
listTabIndex=${this.listTabIndex}
|
||||
@focus=${this.onListFocus}
|
||||
@blur=${this.onListBlur}
|
||||
@click=${this.onListClick}
|
||||
@keydown=${this.typeaheadController.onKeydown}>
|
||||
role="menu"
|
||||
class="list"
|
||||
.ariaLabel=${this.ariaLabel}
|
||||
listTabIndex=${this.listTabIndex}
|
||||
@focus=${this.onListFocus}
|
||||
@blur=${this.onListBlur}
|
||||
@click=${this.onListClick}
|
||||
@keydown=${this.typeaheadController.onKeydown}>
|
||||
${this.renderMenuItems()}
|
||||
</md-list>`;
|
||||
}
|
||||
|
@ -4,47 +4,54 @@
|
||||
//
|
||||
|
||||
// go/keep-sorted start
|
||||
@use 'sass:list';
|
||||
@use 'sass:map';
|
||||
@use 'sass:string';
|
||||
// go/keep-sorted end
|
||||
// go/keep-sorted start
|
||||
@use '../../../elevation/lib/elevation';
|
||||
@use '../../../focus/focus-ring';
|
||||
@use '../../../list/list';
|
||||
@use '../../../list/list-item';
|
||||
@use '../../../ripple/ripple';
|
||||
@use '../../../sass/map-ext';
|
||||
@use '../../../sass/string-ext';
|
||||
@use '../../../sass/theme';
|
||||
@use '../../../sass/typography';
|
||||
@use '../../../tokens/v0_160' as tokens; // TODO(b/270568087): update to latest
|
||||
@use '../../../tokens';
|
||||
// go/keep-sorted end
|
||||
|
||||
$_custom-property-prefix: 'menu';
|
||||
|
||||
@mixin theme($theme) {
|
||||
$reference: resolve-tokens(tokens.md-comp-menu-values());
|
||||
$theme: theme.validate-theme($reference, $theme);
|
||||
$theme: resolve-tokens($theme);
|
||||
$theme: theme.validate-theme(tokens.md-comp-menu-values(), $theme);
|
||||
$theme: _resolve-tokens($theme);
|
||||
$theme: theme.create-theme-vars($theme, $_custom-property-prefix);
|
||||
|
||||
@include theme.emit-theme-vars($theme);
|
||||
}
|
||||
|
||||
@mixin styles() {
|
||||
:host([active]) .list-item {
|
||||
background-color: var(--_list-item-selected-container-color);
|
||||
$tokens: tokens.md-comp-menu-values();
|
||||
$tokens: _resolve-tokens($tokens);
|
||||
$tokens: theme.create-theme-vars($tokens, $_custom-property-prefix);
|
||||
|
||||
:host {
|
||||
@each $token, $value in $tokens {
|
||||
--_#{$token}: #{$value};
|
||||
}
|
||||
}
|
||||
|
||||
.list-item {
|
||||
:host([active]) &,
|
||||
:host(:active) &,
|
||||
&:focus {
|
||||
background-color: var(--_list-item-selected-container-color);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Set the ripple opacity to 0 if there is a submenu that is hovered.
|
||||
*/
|
||||
Set the ripple opacity to 0 if there is a submenu that is hovered.
|
||||
*/
|
||||
.list-item:has(.submenu:hover) {
|
||||
/*
|
||||
Have to use ripple theme directly because :has selector in this case does
|
||||
not work in this case with the :has selector, thus we cannot override the
|
||||
custom props set in :host
|
||||
*/
|
||||
Have to use ripple theme directly because :has selector in this case does
|
||||
not work in this case with the :has selector, thus we cannot override the
|
||||
custom props set in :host
|
||||
*/
|
||||
@include ripple.theme(
|
||||
(
|
||||
hover-opacity: 0,
|
||||
@ -53,71 +60,24 @@ $_custom-property-prefix: 'menu';
|
||||
}
|
||||
}
|
||||
|
||||
@function resolve-tokens($tokens) {
|
||||
$list-item-tokens: ();
|
||||
$tokens: map-ext.duplicate-key(
|
||||
$tokens,
|
||||
list-item-container-height,
|
||||
list-item-one-line-container-height
|
||||
);
|
||||
@function _resolve-tokens($tokens) {
|
||||
@return _remove-unused-tokens($tokens);
|
||||
}
|
||||
|
||||
@each $token, $value in $tokens {
|
||||
$index: string.index($token, list-item);
|
||||
$with-leading-index: string.index($token, list-item-with-leading-icon-);
|
||||
$with-trailing-index: string.index($token, list-item-with-trailing-icon-);
|
||||
// removes unused tokens
|
||||
@function _remove-unused-tokens($tokens) {
|
||||
$unused-tokens: ();
|
||||
@each $token in map-keys($tokens) {
|
||||
$index: string.index($token, 'list-item');
|
||||
|
||||
@if $index {
|
||||
// Replace list-item-with-leading-icon-focus-icon-color
|
||||
// with list-item-focus-leading-icon-icon-color
|
||||
// and replace list-item-with-leading-icon-leading-icon-size
|
||||
// with list-item-leading-icon-size
|
||||
@if $with-leading-index {
|
||||
$token: string-ext.replace(
|
||||
$token,
|
||||
list-item-with-leading-icon-,
|
||||
list-item-
|
||||
);
|
||||
$leading-index: string.index($token, -leading-);
|
||||
@if not $leading-index {
|
||||
$token: string-ext.replace($token, -icon-, -leading-icon-icon-);
|
||||
}
|
||||
}
|
||||
|
||||
// Replace list-item-with-trailing-icon-focus-icon-color
|
||||
// with list-item-focus-trailing-icon-icon-color
|
||||
// and replace list-item-with-trailing-icon-trailing-icon-size
|
||||
// with list-item-trailing-icon-size
|
||||
@if $with-trailing-index {
|
||||
$token: string-ext.replace(
|
||||
$token,
|
||||
list-item-with-trailing-icon-,
|
||||
list-item-
|
||||
);
|
||||
$trailing-index: string.index($token, -trailing-);
|
||||
@if not $trailing-index {
|
||||
$token: string-ext.replace($token, -icon-, -trailing-icon-icon-);
|
||||
}
|
||||
}
|
||||
|
||||
$list-item-tokens: map.set($list-item-tokens, $token, $value);
|
||||
@if not $index {
|
||||
$unused-tokens: list.append($unused-tokens, $token);
|
||||
}
|
||||
}
|
||||
|
||||
$list-item-tokens: typography.resolve-tokens(
|
||||
$list-item-tokens,
|
||||
'list-item-label-text'
|
||||
);
|
||||
|
||||
@return $list-item-tokens;
|
||||
}
|
||||
|
||||
@mixin private-props() {
|
||||
$tokens: resolve-tokens(tokens.md-comp-menu-values());
|
||||
$tokens: theme.create-theme-vars($tokens, menu);
|
||||
|
||||
:host {
|
||||
@each $token, $value in $tokens {
|
||||
--_#{$token}: #{$value};
|
||||
}
|
||||
@each $token in $unused-tokens {
|
||||
$tokens: map.remove($tokens, $token);
|
||||
}
|
||||
|
||||
@return $tokens;
|
||||
}
|
||||
|
@ -1,10 +0,0 @@
|
||||
//
|
||||
// Copyright 2023 Google LLC
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// go/keep-sorted start
|
||||
@use './menu-item';
|
||||
// go/keep-sorted end
|
||||
|
||||
@include menu-item.private-props;
|
@ -46,7 +46,6 @@ export class SubMenuItem extends MenuItemEl {
|
||||
protected override keepOpenOnClick = true;
|
||||
protected previousOpenTimeout = 0;
|
||||
protected previousCloseTimeout = 0;
|
||||
protected submenuOpen = false;
|
||||
|
||||
protected get submenuEl(): Menu|undefined {
|
||||
return this.menus[0];
|
||||
|
@ -8,7 +8,6 @@ import {customElement} from 'lit/decorators.js';
|
||||
|
||||
import {styles as listItemStyles} from '../list/lib/listitem/list-item-styles.css.js';
|
||||
|
||||
import {styles as privateProps} from './lib/menuitem/menu-item-private-styles.css.js';
|
||||
import {styles} from './lib/menuitem/menu-item-styles.css.js';
|
||||
import {MenuItemLink} from './lib/menuitemlink/menu-item-link.js';
|
||||
|
||||
@ -41,5 +40,5 @@ declare global {
|
||||
*/
|
||||
@customElement('md-menu-item-link')
|
||||
export class MdMenuItemLink extends MenuItemLink {
|
||||
static override styles = [privateProps, listItemStyles, styles];
|
||||
static override styles = [listItemStyles, styles];
|
||||
}
|
||||
|
@ -8,9 +8,8 @@ import {customElement} from 'lit/decorators.js';
|
||||
|
||||
import {styles as listItemStyles} from '../list/lib/listitem/list-item-styles.css.js';
|
||||
|
||||
import {MenuItemEl} from './lib/menuitem/menu-item.js';
|
||||
import {styles as privateProps} from './lib/menuitem/menu-item-private-styles.css.js';
|
||||
import {styles} from './lib/menuitem/menu-item-styles.css.js';
|
||||
import {MenuItemEl} from './lib/menuitem/menu-item.js';
|
||||
|
||||
export {ListItem} from '../list/lib/listitem/list-item.js';
|
||||
export {CloseMenuEvent, DeactivateItemsEvent, MenuItem} from './lib/shared.js';
|
||||
@ -38,5 +37,5 @@ declare global {
|
||||
*/
|
||||
@customElement('md-menu-item')
|
||||
export class MdMenuItem extends MenuItemEl {
|
||||
static override styles = [privateProps, listItemStyles, styles];
|
||||
static override styles = [listItemStyles, styles];
|
||||
}
|
||||
|
@ -4,7 +4,21 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
describe('menu button tests', () => {
|
||||
// TODO(b/265220649): unit test menu
|
||||
it('TODO', () => {});
|
||||
// import 'jasmine'; (google3-only)
|
||||
|
||||
import {createTokenTests} from '../testing/tokens.js';
|
||||
|
||||
import {MdMenu} from './menu.js';
|
||||
import {MdMenuItem} from './menu-item.js';
|
||||
|
||||
describe('<md-menu>', () => {
|
||||
describe('.styles', () => {
|
||||
createTokenTests(MdMenu.styles);
|
||||
});
|
||||
});
|
||||
|
||||
describe('<md-menu-item>', () => {
|
||||
describe('.styles', () => {
|
||||
createTokenTests(MdMenuItem.styles);
|
||||
});
|
||||
});
|
||||
|
@ -8,7 +8,6 @@ import {customElement} from 'lit/decorators.js';
|
||||
|
||||
import {styles as listItemStyles} from '../list/lib/listitem/list-item-styles.css.js';
|
||||
|
||||
import {styles as privateProps} from './lib/menuitem/menu-item-private-styles.css.js';
|
||||
import {styles} from './lib/menuitem/menu-item-styles.css.js';
|
||||
import {SubMenuItem} from './lib/submenuitem/sub-menu-item.js';
|
||||
|
||||
@ -67,5 +66,5 @@ declare global {
|
||||
*/
|
||||
@customElement('md-sub-menu-item')
|
||||
export class MdSubMenuItem extends SubMenuItem {
|
||||
static override styles = [privateProps, listItemStyles, styles];
|
||||
static override styles = [listItemStyles, styles];
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user