diff --git a/autocomplete/_filled-autocomplete.scss b/autocomplete/_filled-autocomplete.scss new file mode 100644 index 0000000000..40edb0e98f --- /dev/null +++ b/autocomplete/_filled-autocomplete.scss @@ -0,0 +1,7 @@ +// +// Copyright 2022 Google LLC +// SPDX-License-Identifier: Apache-2.0 +// + +@forward './lib/filled-autocomplete-theme' show + theme; diff --git a/autocomplete/filled-autocomplete.ts b/autocomplete/filled-autocomplete.ts index 73c8e5ba7d..cd749f001e 100644 --- a/autocomplete/filled-autocomplete.ts +++ b/autocomplete/filled-autocomplete.ts @@ -9,6 +9,7 @@ import '@material/web/menu-surface/menu-surface.js'; import '@material/web/field/filled-field.js'; import {customElement} from 'lit/decorators.js'; +import {ClassInfo} from 'lit/directives/class-map.js'; import {literal} from 'lit/static-html.js'; import {styles as filledForcedColorsStyles} from '../textfield/lib/filled-forced-colors-styles.css.js'; @@ -16,6 +17,7 @@ import {styles as filledStyles} from '../textfield/lib/filled-styles.css.js'; import {styles as sharedStyles} from '../textfield/lib/shared-styles.css.js'; import {Autocomplete} from './lib/autocomplete.js'; +import {styles as autocompleteStyles} from './lib/filled-styles.css.js'; declare global { interface HTMLElementTagNameMap { @@ -30,10 +32,27 @@ declare global { */ @customElement('md-filled-autocomplete') export class MdFilledAutocomplete extends Autocomplete { - static override styles = - [sharedStyles, filledStyles, filledForcedColorsStyles]; + static override styles = [ + sharedStyles, filledStyles, filledForcedColorsStyles, autocompleteStyles + ]; protected override readonly listTag = literal`md-list`; protected override readonly menuSurfaceTag = literal`md-menu-surface`; protected override readonly fieldTag = literal`md-filled-field`; + + /** @soyTemplate */ + protected override getAutocompleteRenderClasses(): ClassInfo { + return { + ...super.getAutocompleteRenderClasses(), + 'md3-autocomplete--filled': true, + }; + } + + /** @soyTemplate */ + protected override getRenderClasses(): ClassInfo { + return { + ...super.getRenderClasses(), + 'md3-text-field--filled': true, + }; + } } diff --git a/autocomplete/lib/_autocomplete.scss b/autocomplete/lib/_autocomplete.scss new file mode 100644 index 0000000000..91d7673d73 --- /dev/null +++ b/autocomplete/lib/_autocomplete.scss @@ -0,0 +1,14 @@ +// +// Copyright 2022 Google LLC +// SPDX-License-Identifier: Apache-2.0 +// + +// stylelint-disable selector-class-pattern -- +// Selector '.md3-*' should only be used in this project. + +@mixin static-styles() { + .md3-autocomplete { + display: inline-flex; + flex: 1; + } +} diff --git a/autocomplete/lib/_filled-autocomplete-theme.scss b/autocomplete/lib/_filled-autocomplete-theme.scss new file mode 100644 index 0000000000..2a8c2bf231 --- /dev/null +++ b/autocomplete/lib/_filled-autocomplete-theme.scss @@ -0,0 +1,263 @@ +// +// Copyright 2022 Google LLC +// SPDX-License-Identifier: Apache-2.0 +// + +// stylelint-disable selector-class-pattern -- +// Selector '.md3-*' should only be used in this project. + +@use 'sass:map'; +@use '@material/web/elevation/lib/elevation-theme'; +@use '@material/web/field/lib/filled-field-theme'; +@use '@material/web/list/lib/listitem/list-item-theme'; +@use '@material/web/list/list'; +@use '@material/web/list/list-item'; +@use '@material/web/menusurface/menu-surface'; +@use '@material/web/sass/resolvers'; +@use '@material/web/sass/shape'; +@use '@material/web/sass/theme'; +@use '@material/web/textfield/lib/filled-text-field-theme'; +@use '@material/web/tokens'; + +$custom-property-prefix: 'filled-autocomplete'; +$light-theme: tokens.md-comp-filled-autocomplete-values(); +$dark-theme: tokens.md-comp-filled-autocomplete-values( + ( + md-sys-color: tokens.md-sys-color-values-dark-dynamic(), + ), + $exclude-hardcoded-values: true +); + +@mixin theme($theme, $resolvers: resolvers.$material) { + $theme: theme.validate-theme($light-theme, $theme); + $theme: _resolve-theme($theme, $resolvers); + $theme: theme.create-theme-vars($theme, $custom-property-prefix); + + @include theme.emit-theme-vars($theme); +} + +@mixin theme-styles($theme, $resolvers: resolvers.$material) { + $theme: theme.validate-theme-styles($light-theme, $theme); + $theme: _resolve-theme($theme, $resolvers); + $theme: theme.create-theme-vars($theme, $custom-property-prefix); + + .md3-text-field--filled { + $text-field-theme: ( + active-indicator-color: map.get($theme, text-field-active-indicator-color), + active-indicator-height: + map.get($theme, text-field-active-indicator-height), + caret-color: map.get($theme, text-field-caret-color), + container-color: map.get($theme, text-field-container-color), + container-height: map.get($theme, text-field-container-height), + disabled-active-indicator-color: + map.get($theme, text-field-disabled-active-indicator-color), + disabled-active-indicator-height: + map.get($theme, text-field-disabled-active-indicator-height), + disabled-active-indicator-opacity: + map.get($theme, text-field-disabled-active-indicator-opacity), + disabled-container-color: + map.get($theme, text-field-disabled-container-color), + disabled-container-opacity: + map.get($theme, text-field-disabled-container-opacity), + disabled-input-text-color: + map.get($theme, text-field-disabled-input-text-color), + disabled-input-text-opacity: + map.get($theme, text-field-disabled-input-text-opacity), + disabled-label-text-color: + map.get($theme, text-field-disabled-label-text-color), + disabled-label-text-opacity: + map.get($theme, text-field-disabled-label-text-opacity), + disabled-leading-icon-color: + map.get($theme, text-field-disabled-leading-icon-color), + disabled-leading-icon-opacity: + map.get($theme, text-field-disabled-leading-icon-opacity), + disabled-supporting-text-color: + map.get($theme, text-field-disabled-supporting-text-color), + disabled-supporting-text-opacity: + map.get($theme, text-field-disabled-supporting-text-opacity), + disabled-trailing-icon-color: + map.get($theme, text-field-disabled-trailing-icon-color), + disabled-trailing-icon-opacity: + map.get($theme, text-field-disabled-trailing-icon-opacity), + error-active-indicator-color: + map.get($theme, text-field-error-active-indicator-color), + error-focus-active-indicator-color: + map.get($theme, text-field-error-focus-active-indicator-color), + error-focus-caret-color: + map.get($theme, text-field-error-focus-caret-color), + error-focus-input-text-color: + map.get($theme, text-field-error-focus-input-text-color), + error-focus-label-text-color: + map.get($theme, text-field-error-focus-label-text-color), + error-focus-leading-icon-color: + map.get($theme, text-field-error-focus-leading-icon-color), + error-focus-supporting-text-color: + map.get($theme, text-field-error-focus-supporting-text-color), + error-focus-trailing-icon-color: + map.get($theme, text-field-error-focus-trailing-icon-color), + error-hover-active-indicator-color: + map.get($theme, text-field-error-hover-active-indicator-color), + error-hover-input-text-color: + map.get($theme, text-field-error-hover-input-text-color), + error-hover-label-text-color: + map.get($theme, text-field-error-hover-label-text-color), + error-hover-leading-icon-color: + map.get($theme, text-field-error-hover-leading-icon-color), + error-hover-state-layer-color: + map.get($theme, text-field-error-hover-state-layer-color), + error-hover-state-layer-opacity: + map.get($theme, text-field-error-hover-state-layer-opacity), + error-hover-supporting-text-color: + map.get($theme, text-field-error-hover-supporting-text-color), + error-hover-trailing-icon-color: + map.get($theme, text-field-error-hover-trailing-icon-color), + error-input-text-color: map.get($theme, text-field-error-input-text-color), + error-label-text-color: map.get($theme, text-field-error-label-text-color), + error-leading-icon-color: + map.get($theme, text-field-error-leading-icon-color), + error-supporting-text-color: + map.get($theme, text-field-error-supporting-text-color), + error-trailing-icon-color: + map.get($theme, text-field-error-trailing-icon-color), + focus-active-indicator-color: + map.get($theme, text-field-focus-active-indicator-color), + focus-active-indicator-height: + map.get($theme, text-field-focus-active-indicator-height), + focus-input-text-color: map.get($theme, text-field-focus-input-text-color), + focus-label-text-color: map.get($theme, text-field-focus-label-text-color), + focus-leading-icon-color: + map.get($theme, text-field-focus-leading-icon-color), + focus-supporting-text-color: + map.get($theme, text-field-focus-supporting-text-color), + focus-trailing-icon-color: + map.get($theme, text-field-focus-trailing-icon-color), + hover-active-indicator-color: + map.get($theme, text-field-hover-active-indicator-color), + hover-active-indicator-height: + map.get($theme, text-field-hover-active-indicator-height), + hover-input-text-color: map.get($theme, text-field-hover-input-text-color), + hover-label-text-color: map.get($theme, text-field-hover-label-text-color), + hover-leading-icon-color: + map.get($theme, text-field-hover-leading-icon-color), + hover-state-layer-color: + map.get($theme, text-field-hover-state-layer-color), + hover-state-layer-opacity: + map.get($theme, text-field-hover-state-layer-opacity), + hover-supporting-text-color: + map.get($theme, text-field-hover-supporting-text-color), + hover-trailing-icon-color: + map.get($theme, text-field-hover-trailing-icon-color), + input-text-color: map.get($theme, text-field-input-text-color), + input-text-font: map.get($theme, text-field-input-text-font), + input-text-line-height: map.get($theme, text-field-input-text-line-height), + input-text-size: map.get($theme, text-field-input-text-size), + input-text-tracking: map.get($theme, text-field-input-text-tracking), + input-text-type: map.get($theme, text-field-input-text-type), + input-text-weight: map.get($theme, text-field-input-text-weight), + label-text-color: map.get($theme, text-field-label-text-color), + label-text-font: map.get($theme, text-field-label-text-font), + label-text-line-height: map.get($theme, text-field-label-text-line-height), + label-text-populated-line-height: + map.get($theme, text-field-label-text-populated-line-height), + label-text-populated-size: + map.get($theme, text-field-label-text-populated-size), + label-text-size: map.get($theme, text-field-label-text-size), + label-text-tracking: map.get($theme, text-field-label-text-tracking), + label-text-type: map.get($theme, text-field-label-text-type), + label-text-weight: map.get($theme, text-field-label-text-weight), + leading-icon-color: map.get($theme, text-field-leading-icon-color), + leading-icon-size: map.get($theme, text-field-leading-icon-size), + supporting-text-color: map.get($theme, text-field-supporting-text-color), + supporting-text-font: map.get($theme, text-field-supporting-text-font), + supporting-text-line-height: + map.get($theme, text-field-supporting-text-line-height), + supporting-text-size: map.get($theme, text-field-supporting-text-size), + supporting-text-tracking: + map.get($theme, text-field-supporting-text-tracking), + supporting-text-weight: map.get($theme, text-field-supporting-text-weight), + trailing-icon-color: map.get($theme, text-field-trailing-icon-color), + trailing-icon-size: map.get($theme, text-field-trailing-icon-size), + ); + + @include filled-text-field-theme.theme($text-field-theme, $resolvers); + // Textfield cannot resolve shape when created as vars. + @include filled-field-theme.theme( + ( + container-shape-start-start: + map.get($theme, text-field-container-shape-start-start), + container-shape-start-end: + map.get($theme, text-field-container-shape-start-end), + container-shape-end-end: + map.get($theme, text-field-container-shape-end-end), + container-shape-end-start: + map.get($theme, text-field-container-shape-end-start), + ) + ); + } + + .md3-autocomplete__menu-surface { + @include menu-surface.theme( + ( + container-elevation-shadow: + map.get($theme, menu-container-elevation-shadow), + container-shape: map.get($theme, menu-container-shape), + ), + $resolvers + ); + + @include list.theme( + ( + container-color: map.get($theme, menu-container-color), + container-surface-tint-layer-color: + map.get($theme, menu-container-surface-tint-layer-color), + container-elevation-overlay-opacity: + map.get($theme, menu-container-elevation-overlay-opacity), + ) + ); + + @include list-item.theme( + ( + list-item-container-color: map.get($theme, menu-container-color), + list-item-one-line-container-height: + map.get($theme, menu-list-item-container-height), + list-item-label-text-color: + map.get($theme, menu-list-item-label-text-color), + list-item-label-text-font: + map.get($theme, menu-list-item-label-text-font), + list-item-label-text-line-height: + map.get($theme, menu-list-item-label-text-line-height), + list-item-label-text-size: + map.get($theme, menu-list-item-label-text-size), + list-item-label-text-tracking: + map.get($theme, menu-list-item-label-text-tracking), + list-item-label-text-weight: + map.get($theme, menu-list-item-label-text-weight), + ) + ); + } + + ::slotted([data-aria-selected='true']) { + @include list-item-theme.theme( + ( + list-item-container-color: + map.get($theme, menu-list-item-selected-container-color), + ), + $resolvers + ); + } +} + +@function _resolve-theme($theme, $resolvers) { + $theme: elevation-theme.resolve-theme( + $theme, + map.get($resolvers, 'elevation'), + $shadow-color-token: 'menu-container-shadow-color', + $elevation-tokens: (menu-container-elevation) + ); + $theme: shape.resolve-theme( + $theme, + map.get($resolvers, shape), + text-field-container-shape + ); + @return $theme; +} diff --git a/autocomplete/lib/autocomplete.ts b/autocomplete/lib/autocomplete.ts index 165eea7f5c..bf301d0d46 100644 --- a/autocomplete/lib/autocomplete.ts +++ b/autocomplete/lib/autocomplete.ts @@ -8,6 +8,7 @@ import {observer} from '@material/web/compat/base/observer.js'; import {html, PropertyValues, TemplateResult} from 'lit'; import {property, query, queryAssignedElements, state} from 'lit/decorators.js'; +import {ClassInfo, classMap} from 'lit/directives/class-map.js'; import {html as staticHtml, StaticValue} from 'lit/static-html.js'; import {List} from '../../list/lib/list.js'; @@ -51,7 +52,8 @@ export abstract class Autocomplete extends TextField { /** @soyTemplate */ override render(): TemplateResult { - return html`
`; } + /** @soyTemplate */ + protected getAutocompleteRenderClasses(): ClassInfo { + return {}; + } + override firstUpdated(changedProperties: PropertyValues) { super.firstUpdated(changedProperties); this.menuSurface!.anchor = this; @@ -236,6 +243,7 @@ export abstract class Autocomplete extends TextField { if (this.selectedItem && item === this.selectedItem && this.list) { item.ariaSelected = 'true'; + item.showFocusRing = true; // Scroll into view if (this.list.scrollTop + this.list.offsetHeight < @@ -247,6 +255,7 @@ export abstract class Autocomplete extends TextField { } } else { item.ariaSelected = 'false'; + item.showFocusRing = false; } }); } diff --git a/autocomplete/lib/filled-styles.scss b/autocomplete/lib/filled-styles.scss new file mode 100644 index 0000000000..5ae4799d64 --- /dev/null +++ b/autocomplete/lib/filled-styles.scss @@ -0,0 +1,18 @@ +// +// Copyright 2022 Google LLC +// SPDX-License-Identifier: Apache-2.0 +// + +// stylelint-disable selector-class-pattern -- +// Selector '.md3-*' should only be used in this project. + +@use './filled-autocomplete-theme'; +@use './autocomplete'; + +@include autocomplete.static-styles; + +.md3-autocomplete--filled { + @include filled-autocomplete-theme.theme-styles( + filled-autocomplete-theme.$light-theme + ); +}