From 44d4f640f5412f8f6c2db292ae59c087162d5736 Mon Sep 17 00:00:00 2001 From: Liz Mitchell Date: Thu, 5 May 2022 18:03:08 +0000 Subject: [PATCH] fix: add js extension for lit tooling FUTURE_COPYBARA_INTEGRATE_REVIEW=https://github.com/material-components/material-web/pull/3393 from material-components:release-0.26 1fedae4189df117c28e56e5dcb93de2c24bf2476 PiperOrigin-RevId: 446764286 --- components/action-element/README.md | 10 +- components/action-element/action-element.ts | 2 +- .../test/action-element_test.ts | 6 +- components/badge/badge.ts | 6 +- components/badge/lib/badge.ts | 4 +- components/button/elevated-button.ts | 10 +- components/button/elevated-link-button.ts | 10 +- components/button/filled-button.ts | 8 +- components/button/filled-link-button.ts | 8 +- components/button/harness.ts | 4 +- components/button/lib/_button-theme.scss | 2 +- components/button/lib/_button.scss | 2 +- components/button/lib/_elevation-theme.scss | 2 +- components/button/lib/_icon-theme.scss | 2 +- .../button/lib/_outlined-button-theme.scss | 2 +- components/button/lib/button.ts | 22 +- components/button/lib/elevated-button.ts | 4 +- components/button/lib/elevated-link-button.ts | 4 +- components/button/lib/filled-button.ts | 4 +- components/button/lib/filled-link-button.ts | 4 +- components/button/lib/link-button.ts | 8 +- components/button/lib/outlined-button.ts | 4 +- components/button/lib/outlined-link-button.ts | 4 +- components/button/lib/text-button.ts | 4 +- components/button/lib/text-link-button.ts | 4 +- components/button/lib/tonal-button.ts | 4 +- components/button/lib/tonal-link-button.ts | 4 +- components/button/outlined-button.ts | 8 +- components/button/outlined-link-button.ts | 8 +- components/button/text-button.ts | 8 +- components/button/text-link-button.ts | 8 +- components/button/tonal-button.ts | 8 +- components/button/tonal-link-button.ts | 8 +- components/checkbox/checkbox.ts | 6 +- components/checkbox/harness.ts | 4 +- components/checkbox/lib/_checkbox-theme.scss | 2 +- components/checkbox/lib/checkbox.ts | 22 +- components/checkbox/lib/foundation.ts | 6 +- components/checkbox/test/md-checkbox_test.ts | 6 +- components/compat/theme/README.md | 2 + components/compat/theme/_color-palette.scss | 277 ++++++ components/compat/theme/_css.scss | 92 ++ .../compat/theme/_custom-properties.scss | 309 +++++++ components/compat/theme/_gss.scss | 45 + components/compat/theme/_keys.scss | 507 +++++++++++ components/compat/theme/_map-ext.scss | 68 ++ components/compat/theme/_replace.scss | 108 +++ components/compat/theme/_selector-ext.scss | 522 +++++++++++ components/compat/theme/_shadow-dom.scss | 474 ++++++++++ components/compat/theme/_state.scss | 752 ++++++++++++++++ components/compat/theme/_theme-color.scss | 344 ++++++++ components/compat/theme/_theme.scss | 334 +++++++ components/controller/form-controller.ts | 2 +- components/controller/observer-foundation.ts | 4 +- .../controller/test/action-controller_test.ts | 8 +- components/controller/test/events_test.ts | 2 +- .../controller/test/form-controller_test.ts | 6 +- components/controller/test/foundation_test.ts | 2 +- .../test/observer-foundation_test.ts | 4 +- components/controller/test/observer_test.ts | 2 +- components/decorators/bound.ts | 2 +- .../decorators/test/aria-property_test.ts | 8 +- components/decorators/test/bound_test.ts | 2 +- components/elevation/lib/_surface.scss | 2 +- components/fab/fab-extended.ts | 10 +- components/fab/fab.ts | 10 +- components/fab/harness.ts | 4 +- components/fab/lib/_fab-extended-theme.scss | 2 +- components/fab/lib/_fab-shared-theme.scss | 2 +- components/fab/lib/_fab.scss | 2 +- components/fab/lib/fab-extended.ts | 4 +- components/fab/lib/fab-shared.ts | 10 +- components/fab/lib/fab.ts | 4 +- components/field/filled-field.ts | 8 +- components/field/harness.ts | 4 +- components/field/lib/_content.scss | 3 +- components/field/lib/_filled-field.scss | 4 +- components/field/lib/_outlined-field.scss | 10 +- components/field/lib/field.ts | 6 +- components/field/lib/filled-field.ts | 8 +- components/field/lib/outlined-field.ts | 4 +- components/field/outlined-field.ts | 8 +- components/field/test/field_test.ts | 10 +- components/field/test/filled-field_test.ts | 10 +- components/field/test/outlined-field_test.ts | 10 +- components/focus/focus-ring.ts | 6 +- components/focus/lib/focus-ring.ts | 4 +- components/focus/test/screenshot_test.scss | 14 + components/focus/test/screenshot_test.ts | 95 ++ components/focus/test/strong-focus_test.ts | 2 +- components/formfield/formfield.ts | 6 +- components/formfield/lib/formfield-base.ts | 6 +- components/icon-button/harness.ts | 6 +- components/icon-button/icon-button-toggle.ts | 8 +- components/icon-button/icon-button.ts | 8 +- .../icon-button/lib/_icon-button-theme.scss | 6 +- .../icon-button/lib/icon-button-toggle.ts | 16 +- components/icon-button/lib/icon-button.ts | 18 +- .../icon-button/lib/link-icon-button.ts | 6 +- components/icon-button/link-icon-button.ts | 8 +- components/icon/icon.ts | 6 +- components/list/lib/_list-item-theme.scss | 2 +- components/list/lib/_list-theme.scss | 2 +- components/list/lib/constants.ts | 2 +- components/list/lib/list-item-icon.ts | 2 +- components/list/lib/list-item.ts | 6 +- components/list/lib/list.ts | 4 +- components/list/lib/option-list-item.ts | 4 +- components/list/lib/option-list.ts | 4 +- components/list/list-item-icon.ts | 6 +- components/list/list-item.ts | 6 +- components/list/list.ts | 6 +- components/list/option-list-item.ts | 4 +- components/list/option-list.ts | 6 +- components/motion/test/animation_test.ts | 2 +- components/navigation-bar/harness.ts | 6 +- .../lib/_navigation-bar-theme.scss | 6 +- .../navigation-bar/lib/navigation-bar.ts | 16 +- components/navigation-bar/navigation-bar.ts | 6 +- .../test/md-navigation-bar_test.ts | 21 +- .../lib/_navigation-drawer-modal-theme.scss | 2 +- .../lib/_navigation-drawer-theme.scss | 2 +- .../lib/navigation-drawer-modal.ts | 12 +- .../lib/navigation-drawer.ts | 12 +- .../navigation-drawer-modal.ts | 6 +- .../navigation-drawer/navigation-drawer.ts | 6 +- components/navigation-tab/harness.ts | 4 +- .../lib/_navigation-tab-theme.scss | 8 +- .../navigation-tab/lib/navigation-tab.ts | 43 +- components/navigation-tab/navigation-tab.ts | 6 +- .../test/md-navigation-tab_test.ts | 8 +- components/radio/harness.ts | 4 +- components/radio/lib/_radio-theme.scss | 8 +- components/radio/lib/foundation.ts | 6 +- components/radio/lib/radio.ts | 30 +- components/radio/md-radio_test.ts | 6 +- components/radio/radio.ts | 6 +- components/ripple/lib/ripple.ts | 8 +- components/ripple/ripple.ts | 6 +- components/ripple/test/ripple_test.ts | 2 +- components/sass/_touch-target.scss | 22 + .../segmented-button-set/lib/foundation.ts | 6 +- .../lib/multi-select-segmented-button-set.ts | 2 +- .../lib/segmented-button-set.ts | 12 +- .../lib/single-select-segmented-button-set.ts | 2 +- components/segmented-button-set/lib/state.ts | 2 +- .../multi-select-segmented-button-set.ts | 6 +- .../single-select-segmented-button-set.ts | 6 +- .../test/foundation_test.ts | 8 +- components/segmented-button/lib/foundation.ts | 4 +- .../lib/multi-select-segmented-button.ts | 2 +- .../outlined-multi-select-segmented-button.ts | 4 +- ...outlined-single-select-segmented-button.ts | 4 +- .../segmented-button/lib/segmented-button.ts | 10 +- .../lib/single-select-segmented-button.ts | 2 +- .../outlined-multi-select-segmented-button.ts | 8 +- ...outlined-single-select-segmented-button.ts | 8 +- .../segmented-button/test/foundation_test.ts | 8 +- components/switch/_switch.scss | 7 + components/switch/harness.ts | 4 +- components/switch/lib/README.md | 55 ++ .../switch/lib/_switch-handle-theme.scss | 150 ++++ components/switch/lib/_switch-icon-theme.scss | 52 ++ components/switch/lib/_switch-theme.scss | 829 ++---------------- .../switch/lib/_switch-track-theme.scss | 117 +++ components/switch/lib/_switch.scss | 241 ++--- components/switch/lib/switch-styles.scss | 13 +- components/switch/lib/switch.ts | 115 +-- components/switch/lib/switch_test.ts | 22 +- components/switch/switch.ts | 6 +- .../tabs/tab/lib/_primary-tab-theme.scss | 2 +- .../tabs/tab/lib/_secondary-tab-theme.scss | 2 +- components/tabs/tab/lib/_tab-theme.scss | 8 +- components/tabs/tab/lib/foundation.ts | 8 +- components/tabs/tab/lib/primary-tab.ts | 6 +- components/tabs/tab/lib/secondary-tab.ts | 4 +- components/tabs/tab/lib/tab.ts | 25 +- components/tabs/tab/primary-tab.ts | 14 +- components/tabs/tab/secondary-tab.ts | 14 +- .../tab_bar/lib/_primary-tab-bar-theme.scss | 4 +- .../tab_bar/lib/_secondary-tab-bar-theme.scss | 4 +- components/tabs/tab_bar/lib/adapter.ts | 2 +- components/tabs/tab_bar/lib/foundation.ts | 8 +- .../tabs/tab_bar/lib/primary-tab-bar.ts | 6 +- .../tabs/tab_bar/lib/secondary-tab-bar.ts | 6 +- components/tabs/tab_bar/lib/tab-bar.ts | 22 +- components/tabs/tab_bar/primary-tab-bar.ts | 10 +- components/tabs/tab_bar/secondary-tab-bar.ts | 10 +- .../lib/_tab-indicator-theme.scss | 2 +- .../tab_indicator/lib/fading-foundation.ts | 2 +- .../tabs/tab_indicator/lib/foundation.ts | 6 +- .../tab_indicator/lib/sliding-foundation.ts | 2 +- .../tabs/tab_indicator/lib/tab-indicator.ts | 14 +- .../tabs/tab_indicator/tab-indicator.ts | 6 +- components/tabs/tab_panel/lib/_tab-panel.scss | 18 + .../tabs/tab_panel/lib/tab-panel-styles.scss | 9 + components/tabs/tab_panel/lib/tab-panel.ts | 39 + components/tabs/tab_panel/tab-panel.ts | 26 + .../tabs/tab_scroller/lib/foundation.ts | 16 +- .../tab_scroller/lib/rtl-default-scroller.ts | 4 +- .../tab_scroller/lib/rtl-negative-scroller.ts | 4 +- .../tab_scroller/lib/rtl-reverse-scroller.ts | 4 +- .../tabs/tab_scroller/lib/rtl-scroller.ts | 4 +- .../tabs/tab_scroller/lib/tab-scroller.ts | 10 +- components/tabs/tab_scroller/tab-scroller.ts | 6 +- components/tabs/tabs/lib/tabs.ts | 55 ++ components/tabs/tabs/tabs.ts | 24 + components/testing/environment.ts | 22 +- components/testing/harness.ts | 2 +- components/testing/table/lib/test-table.ts | 4 +- components/testing/table/test-table.ts | 8 +- .../testing/table/test/md-test-table_test.ts | 6 +- components/testing/templates.ts | 10 +- components/text-field/filled-text-field.ts | 10 +- components/text-field/harness.ts | 8 +- .../text-field/lib/filled-text-field.ts | 4 +- .../text-field/lib/outlined-text-field.ts | 4 +- components/text-field/lib/text-field.ts | 16 +- components/text-field/outlined-text-field.ts | 10 +- packages/base/base-element.ts | 8 +- packages/base/form-element.ts | 6 +- packages/base/test/aria-property.test.ts | 8 +- packages/base/test/base-element.test.ts | 6 +- packages/base/test/form-element.test.ts | 6 +- packages/button/mwc-button-base.ts | 16 +- packages/button/mwc-button.ts | 6 +- packages/button/test/mwc-button.test.ts | 4 +- packages/checkbox/README.md | 6 +- packages/checkbox/mwc-checkbox-base.ts | 16 +- packages/checkbox/mwc-checkbox.ts | 6 +- packages/checkbox/test/mwc-checkbox.test.ts | 4 +- .../circular-progress-four-color/README.md | 2 +- .../mwc-circular-progress-four-color-base.ts | 2 +- .../mwc-circular-progress-four-color.ts | 6 +- .../mwc-circular-progress-four-color.test.ts | 4 +- packages/circular-progress/README.md | 2 +- .../mwc-circular-progress-base.ts | 10 +- .../mwc-circular-progress.ts | 6 +- .../test/mwc-circular-progress.test.ts | 4 +- packages/dialog/README.md | 10 +- packages/dialog/mwc-dialog-base.ts | 20 +- packages/dialog/mwc-dialog.ts | 6 +- packages/dialog/test/mwc-dialog.test.ts | 20 +- packages/drawer/mwc-drawer-base.ts | 16 +- packages/drawer/mwc-drawer.ts | 6 +- packages/drawer/test/mwc-drawer.test.ts | 4 +- packages/elevation-overlay/README.md | 2 +- packages/fab/README.md | 6 +- packages/fab/mwc-fab-base.ts | 10 +- packages/fab/mwc-fab.ts | 6 +- packages/fab/test/mwc-fab.test.ts | 4 +- .../mwc-floating-label-directive.ts | 6 +- packages/formfield/README.md | 16 +- packages/formfield/mwc-formfield-base.ts | 14 +- packages/formfield/mwc-formfield.ts | 6 +- packages/formfield/test/mwc-formfield.test.ts | 20 +- .../mwc-icon-button-toggle-base.ts | 14 +- .../mwc-icon-button-toggle.ts | 6 +- .../test/mwc-icon-button-toggle.test.ts | 2 +- packages/icon-button/mwc-icon-button-base.ts | 12 +- packages/icon-button/mwc-icon-button.ts | 6 +- .../icon-button/test/mwc-icon-button.test.ts | 2 +- packages/icon/README.md | 2 +- packages/icon/mwc-icon.ts | 4 +- packages/icon/test/mwc-icon.test.ts | 2 +- .../line-ripple/mwc-line-ripple-directive.ts | 6 +- packages/linear-progress/README.md | 2 +- .../mwc-linear-progress-base.ts | 12 +- .../linear-progress/mwc-linear-progress.ts | 6 +- .../test/mwc-linear-progress.test.ts | 8 +- packages/list/README.md | 22 +- packages/list/mwc-check-list-item-base.ts | 10 +- packages/list/mwc-check-list-item.ts | 8 +- packages/list/mwc-list-adapter.ts | 2 +- packages/list/mwc-list-base.ts | 22 +- packages/list/mwc-list-foundation.ts | 8 +- packages/list/mwc-list-item-base.ts | 12 +- packages/list/mwc-list-item.ts | 8 +- packages/list/mwc-list.ts | 8 +- packages/list/mwc-radio-list-item-base.ts | 12 +- packages/list/mwc-radio-list-item.ts | 8 +- packages/list/test/mwc-list.test.ts | 22 +- packages/menu/README.md | 8 +- packages/menu/mwc-menu-base.ts | 32 +- packages/menu/mwc-menu-surface-base.ts | 18 +- packages/menu/mwc-menu-surface.ts | 10 +- packages/menu/mwc-menu.ts | 12 +- packages/menu/test/mwc-menu.test.ts | 14 +- .../mwc-notched-outline-base.ts | 10 +- .../notched-outline/mwc-notched-outline.ts | 6 +- packages/radio/README.md | 6 +- packages/radio/mwc-radio-base.ts | 26 +- packages/radio/mwc-radio.ts | 8 +- packages/radio/test/mwc-radio.test.ts | 4 +- packages/ripple/mwc-ripple-base.ts | 16 +- packages/ripple/mwc-ripple.ts | 6 +- packages/ripple/ripple-handlers.ts | 2 +- packages/ripple/test/mwc-ripple.test.ts | 2 +- packages/select/README.md | 4 +- packages/select/mwc-select-base.ts | 44 +- packages/select/mwc-select.ts | 6 +- packages/select/test/mwc-select.test.ts | 8 +- packages/slider/README.md | 4 +- packages/slider/slider-base.ts | 26 +- packages/slider/slider-range-base.ts | 24 +- packages/slider/slider-range.ts | 8 +- packages/slider/slider-single-base.ts | 16 +- packages/slider/slider.ts | 8 +- packages/slider/test/slider.test.ts | 6 +- packages/snackbar/README.md | 2 +- .../accessible-snackbar-label-directive.ts | 6 +- packages/snackbar/mwc-snackbar-base.ts | 16 +- packages/snackbar/mwc-snackbar.ts | 6 +- packages/snackbar/test/mwc-snackbar.test.ts | 4 +- packages/switch/README.md | 4 +- packages/switch/deprecated/mwc-switch-base.ts | 22 +- packages/switch/deprecated/mwc-switch.ts | 6 +- .../switch/deprecated/test/mwc-switch.test.ts | 4 +- packages/switch/mwc-switch-base.ts | 22 +- packages/switch/mwc-switch.ts | 6 +- packages/switch/test/mwc-switch.test.ts | 8 +- packages/tab-bar/README.md | 4 +- packages/tab-bar/mwc-tab-bar-base.ts | 24 +- packages/tab-bar/mwc-tab-bar.ts | 6 +- packages/tab-bar/test/mwc-tab-bar.test.ts | 6 +- .../tab-indicator/mwc-tab-indicator-base.ts | 14 +- packages/tab-indicator/mwc-tab-indicator.ts | 6 +- .../tab-scroller/mwc-tab-scroller-base.ts | 10 +- packages/tab-scroller/mwc-tab-scroller.ts | 6 +- .../test/mwc-tab-scroller.test.ts | 2 +- packages/tab/mwc-tab-base.ts | 24 +- packages/tab/mwc-tab.ts | 8 +- packages/tab/test/mwc-tab.test.ts | 4 +- packages/textarea/README.md | 2 +- packages/textarea/mwc-textarea-base.ts | 10 +- packages/textarea/mwc-textarea.ts | 8 +- packages/textarea/test/mwc-textarea.test.ts | 4 +- packages/textfield/README.md | 6 +- packages/textfield/mwc-textfield-base.ts | 30 +- packages/textfield/mwc-textfield.ts | 6 +- packages/textfield/test/mwc-textfield.test.ts | 10 +- .../mwc-top-app-bar-fixed-base.ts | 6 +- .../mwc-top-app-bar-fixed.ts | 6 +- .../top-app-bar/mwc-top-app-bar-base-base.ts | 14 +- packages/top-app-bar/mwc-top-app-bar-base.ts | 6 +- packages/top-app-bar/mwc-top-app-bar.ts | 6 +- test/src/util/helpers.ts | 2 +- 347 files changed, 5982 insertions(+), 2215 deletions(-) create mode 100644 components/compat/theme/README.md create mode 100644 components/compat/theme/_color-palette.scss create mode 100644 components/compat/theme/_css.scss create mode 100644 components/compat/theme/_custom-properties.scss create mode 100644 components/compat/theme/_gss.scss create mode 100644 components/compat/theme/_keys.scss create mode 100644 components/compat/theme/_map-ext.scss create mode 100644 components/compat/theme/_replace.scss create mode 100644 components/compat/theme/_selector-ext.scss create mode 100644 components/compat/theme/_shadow-dom.scss create mode 100644 components/compat/theme/_state.scss create mode 100644 components/compat/theme/_theme-color.scss create mode 100644 components/compat/theme/_theme.scss create mode 100644 components/focus/test/screenshot_test.scss create mode 100644 components/focus/test/screenshot_test.ts create mode 100644 components/sass/_touch-target.scss create mode 100644 components/switch/_switch.scss create mode 100644 components/switch/lib/README.md create mode 100644 components/switch/lib/_switch-handle-theme.scss create mode 100644 components/switch/lib/_switch-icon-theme.scss create mode 100644 components/switch/lib/_switch-track-theme.scss create mode 100644 components/tabs/tab_panel/lib/_tab-panel.scss create mode 100644 components/tabs/tab_panel/lib/tab-panel-styles.scss create mode 100644 components/tabs/tab_panel/lib/tab-panel.ts create mode 100644 components/tabs/tab_panel/tab-panel.ts create mode 100644 components/tabs/tabs/lib/tabs.ts create mode 100644 components/tabs/tabs/tabs.ts diff --git a/components/action-element/README.md b/components/action-element/README.md index 76baa730848..37ba91daf7b 100644 --- a/components/action-element/README.md +++ b/components/action-element/README.md @@ -40,10 +40,10 @@ from `endPress()` have a `detail` object, it should be included in the call to ### Example ```ts -import {ActionElement, BeginPressConfig, EndPressConfig} from '../action-element/action-element'; +import {ActionElement, BeginPressConfig, EndPressConfig} from '../action-element/action-element.js'; import {html} from 'lit'; -import {property} from 'lit/decorators'; +import {property} from 'lit/decorators.js'; export class MyElement extends ActionElement { @property({type: Boolean}) disabled = false; @@ -81,11 +81,11 @@ export class MyElement extends ActionElement { ### Example with ripple ```ts -import {ActionElement, BeginPressConfig, EndPressConfig} from '../action-element/action-element'; -import {MdRipple} from '../ripple/ripple'; +import {ActionElement, BeginPressConfig, EndPressConfig} from '../action-element/action-element.js'; +import {MdRipple} from '../ripple/ripple.js'; import {html} from 'lit'; -import {property, query} from 'lit/decorators'; +import {property, query} from 'lit/decorators.js'; export class MyElementWithRipple extends ActionElement { diff --git a/components/action-element/action-element.ts b/components/action-element/action-element.ts index 4d7506b8a19..d65aa206f5e 100644 --- a/components/action-element/action-element.ts +++ b/components/action-element/action-element.ts @@ -6,7 +6,7 @@ import {LitElement} from 'lit'; -import {ActionController, ActionControllerHost, BeginPressConfig, EndPressConfig} from '../controller/action-controller'; +import {ActionController, ActionControllerHost, BeginPressConfig, EndPressConfig} from '../controller/action-controller.js'; export {BeginPressConfig, EndPressConfig}; diff --git a/components/action-element/test/action-element_test.ts b/components/action-element/test/action-element_test.ts index 50bc9326303..dc285f56711 100644 --- a/components/action-element/test/action-element_test.ts +++ b/components/action-element/test/action-element_test.ts @@ -7,10 +7,10 @@ import 'jasmine'; import {html} from 'lit'; -import {customElement, property} from 'lit/decorators'; +import {customElement, property} from 'lit/decorators.js'; -import {Environment} from '../../testing/environment'; -import {ActionElement} from '../action-element'; +import {Environment} from '../../testing/environment.js'; +import {ActionElement} from '../action-element.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/badge/badge.ts b/components/badge/badge.ts index 3e57592b51a..0f491de3a2f 100644 --- a/components/badge/badge.ts +++ b/components/badge/badge.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {Badge} from './lib/badge'; -import {styles} from './lib/badge-styles.css'; +import {Badge} from './lib/badge.js'; +import {styles} from './lib/badge-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/badge/lib/badge.ts b/components/badge/lib/badge.ts index a1764b5c3cd..ab3901a16f3 100644 --- a/components/badge/lib/badge.ts +++ b/components/badge/lib/badge.ts @@ -5,8 +5,8 @@ */ import {html, LitElement, TemplateResult} from 'lit'; -import {property} from 'lit/decorators'; -import {ClassInfo, classMap} from 'lit/directives/class-map'; +import {property} from 'lit/decorators.js'; +import {ClassInfo, classMap} from 'lit/directives/class-map.js'; /** @soyCompatible */ export class Badge extends LitElement { diff --git a/components/button/elevated-button.ts b/components/button/elevated-button.ts index c8d083fdda7..4fd3d4a1a2c 100644 --- a/components/button/elevated-button.ts +++ b/components/button/elevated-button.ts @@ -4,13 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {styles as elevationOverlayStyles} from '../elevation/lib/elevation-overlay-styles.css'; +import {styles as elevationOverlayStyles} from '../elevation/lib/elevation-overlay-styles.css.js'; -import {ElevatedButton} from './lib/elevated-button'; -import {styles as elevatedStyles} from './lib/elevated-styles.css'; -import {styles as sharedStyles} from './lib/shared-styles.css'; +import {ElevatedButton} from './lib/elevated-button.js'; +import {styles as elevatedStyles} from './lib/elevated-styles.css.js'; +import {styles as sharedStyles} from './lib/shared-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/button/elevated-link-button.ts b/components/button/elevated-link-button.ts index a0eb97871b0..348c6416307 100644 --- a/components/button/elevated-link-button.ts +++ b/components/button/elevated-link-button.ts @@ -4,13 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {styles as elevationOverlayStyles} from '../elevation/lib/elevation-overlay-styles.css'; +import {styles as elevationOverlayStyles} from '../elevation/lib/elevation-overlay-styles.css.js'; -import {ElevatedLinkButton} from './lib/elevated-link-button'; -import {styles as elevatedStyles} from './lib/elevated-styles.css'; -import {styles as sharedStyles} from './lib/shared-styles.css'; +import {ElevatedLinkButton} from './lib/elevated-link-button.js'; +import {styles as elevatedStyles} from './lib/elevated-styles.css.js'; +import {styles as sharedStyles} from './lib/shared-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/button/filled-button.ts b/components/button/filled-button.ts index bb0a2dec49d..3c1381f3ae5 100644 --- a/components/button/filled-button.ts +++ b/components/button/filled-button.ts @@ -4,11 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {FilledButton} from './lib/filled-button'; -import {styles as filledStyles} from './lib/filled-styles.css'; -import {styles as sharedStyles} from './lib/shared-styles.css'; +import {FilledButton} from './lib/filled-button.js'; +import {styles as filledStyles} from './lib/filled-styles.css.js'; +import {styles as sharedStyles} from './lib/shared-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/button/filled-link-button.ts b/components/button/filled-link-button.ts index c48c82fa44f..dc37dc767a2 100644 --- a/components/button/filled-link-button.ts +++ b/components/button/filled-link-button.ts @@ -4,11 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {FilledLinkButton} from './lib/filled-link-button'; -import {styles as filledStyles} from './lib/filled-styles.css'; -import {styles as sharedStyles} from './lib/shared-styles.css'; +import {FilledLinkButton} from './lib/filled-link-button.js'; +import {styles as filledStyles} from './lib/filled-styles.css.js'; +import {styles as sharedStyles} from './lib/shared-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/button/harness.ts b/components/button/harness.ts index 2150a981eb1..7e2dc2f6f7d 100644 --- a/components/button/harness.ts +++ b/components/button/harness.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {Harness} from '../testing/harness'; +import {Harness} from '../testing/harness.js'; -import {Button} from './lib/button'; +import {Button} from './lib/button.js'; /** * Test harness for buttons. diff --git a/components/button/lib/_button-theme.scss b/components/button/lib/_button-theme.scss index 6a572d35e32..efba6059980 100644 --- a/components/button/lib/_button-theme.scss +++ b/components/button/lib/_button-theme.scss @@ -9,7 +9,7 @@ // TODO(b/228217731): Remove MDC/MWC dependencies @use 'sass:map'; @use 'third_party/javascript/material_web_components/m3/ripple/ripple-theme'; -@use '@material/theme/state'; +@use '../../compat/theme/state'; @use '@material/typography/typography'; $light-theme: ( diff --git a/components/button/lib/_button.scss b/components/button/lib/_button.scss index 0a5fa052af5..b12f3dd9a99 100644 --- a/components/button/lib/_button.scss +++ b/components/button/lib/_button.scss @@ -8,7 +8,7 @@ // TODO(b/228217731): Remove MDC/MWC dependencies @use '@material/touch-target/touch-target'; -@use '@material/theme/selector-ext'; +@use '../../compat/theme/selector-ext'; @use '../../elevation/lib/surface' as elevation-surface; @mixin static-styles() { diff --git a/components/button/lib/_elevation-theme.scss b/components/button/lib/_elevation-theme.scss index d5d1c5575ec..8c1b0d9673c 100644 --- a/components/button/lib/_elevation-theme.scss +++ b/components/button/lib/_elevation-theme.scss @@ -7,7 +7,7 @@ @use 'sass:map'; @use 'sass:meta'; @use 'third_party/javascript/material_web_components/m3/ripple/ripple-theme'; -@use '@material/theme/state'; +@use '../../compat/theme/state'; @use '../../elevation/lib/elevation-theme'; @use './button-theme'; diff --git a/components/button/lib/_icon-theme.scss b/components/button/lib/_icon-theme.scss index 0a7d3e368cb..44eeab8ae71 100644 --- a/components/button/lib/_icon-theme.scss +++ b/components/button/lib/_icon-theme.scss @@ -9,7 +9,7 @@ // TODO(b/228217731): Remove MDC/MWC dependencies @use 'sass:map'; @use 'third_party/javascript/material_web_components/m3/ripple/ripple-theme'; -@use '@material/theme/state'; +@use '../../compat/theme/state'; @use './button-theme'; $_selectors: button-theme.$selectors; diff --git a/components/button/lib/_outlined-button-theme.scss b/components/button/lib/_outlined-button-theme.scss index 3e99f7fa1fc..6313e2ccf8d 100644 --- a/components/button/lib/_outlined-button-theme.scss +++ b/components/button/lib/_outlined-button-theme.scss @@ -9,7 +9,7 @@ // TODO(b/228217731): Remove MDC/MWC dependencies @use 'sass:map'; @use '@material/shape/shape'; -@use '@material/theme/state'; +@use '../../compat/theme/state'; @use '../../sass/resolvers'; @use '../../sass/theme'; @use './button-theme'; diff --git a/components/button/lib/button.ts b/components/button/lib/button.ts index 6300135ea73..fd7a12d9ebf 100644 --- a/components/button/lib/button.ts +++ b/components/button/lib/button.ts @@ -4,21 +4,21 @@ * SPDX-License-Identifier: Apache-2.0 */ -import '@material/mwc-icon/mwc-icon'; -import '../../focus/focus-ring'; -import '../../ripple/ripple'; +import '@material/mwc-icon/mwc-icon.js'; +import '../../focus/focus-ring.js'; +import '../../ripple/ripple.js'; import {html, LitElement, TemplateResult} from 'lit'; -import {eventOptions, property, query, queryAssignedElements, state} from 'lit/decorators'; -import {ClassInfo, classMap} from 'lit/directives/class-map'; -import {ifDefined} from 'lit/directives/if-defined'; +import {eventOptions, property, query, queryAssignedElements, state} from 'lit/decorators.js'; +import {ClassInfo, classMap} from 'lit/directives/class-map.js'; +import {ifDefined} from 'lit/directives/if-defined.js'; -import {ariaProperty} from '../../decorators/aria-property'; -import {pointerPress, shouldShowStrongFocus} from '../../focus/strong-focus'; -import {MdRipple} from '../../ripple/ripple'; -import {ARIAHasPopup} from '../../types/aria'; +import {ariaProperty} from '../../decorators/aria-property.js'; +import {pointerPress, shouldShowStrongFocus} from '../../focus/strong-focus.js'; +import {MdRipple} from '../../ripple/ripple.js'; +import {ARIAHasPopup} from '../../types/aria.js'; -import {ButtonState} from './state'; +import {ButtonState} from './state.js'; /** @soyCompatible */ export abstract class Button extends LitElement implements ButtonState { diff --git a/components/button/lib/elevated-button.ts b/components/button/lib/elevated-button.ts index d1281d6769b..959a25508b6 100644 --- a/components/button/lib/elevated-button.ts +++ b/components/button/lib/elevated-button.ts @@ -5,9 +5,9 @@ */ import {html, TemplateResult} from 'lit'; -import {ClassInfo} from 'lit/directives/class-map'; +import {ClassInfo} from 'lit/directives/class-map.js'; -import {Button} from './button'; +import {Button} from './button.js'; /** @soyCompatible */ export class ElevatedButton extends Button { diff --git a/components/button/lib/elevated-link-button.ts b/components/button/lib/elevated-link-button.ts index 9b24faa88e9..e8e83ff294f 100644 --- a/components/button/lib/elevated-link-button.ts +++ b/components/button/lib/elevated-link-button.ts @@ -5,9 +5,9 @@ */ import {html, TemplateResult} from 'lit'; -import {ClassInfo} from 'lit/directives/class-map'; +import {ClassInfo} from 'lit/directives/class-map.js'; -import {LinkButton} from './link-button'; +import {LinkButton} from './link-button.js'; /** @soyCompatible */ export class ElevatedLinkButton extends LinkButton { diff --git a/components/button/lib/filled-button.ts b/components/button/lib/filled-button.ts index 40208772728..dc0842bcc1e 100644 --- a/components/button/lib/filled-button.ts +++ b/components/button/lib/filled-button.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {ClassInfo} from 'lit/directives/class-map'; +import {ClassInfo} from 'lit/directives/class-map.js'; -import {Button} from './button'; +import {Button} from './button.js'; /** @soyCompatible */ export class FilledButton extends Button { diff --git a/components/button/lib/filled-link-button.ts b/components/button/lib/filled-link-button.ts index 5ee03517688..1f34dbc6caf 100644 --- a/components/button/lib/filled-link-button.ts +++ b/components/button/lib/filled-link-button.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {ClassInfo} from 'lit/directives/class-map'; +import {ClassInfo} from 'lit/directives/class-map.js'; -import {LinkButton} from './link-button'; +import {LinkButton} from './link-button.js'; /** @soyCompatible */ export class FilledLinkButton extends LinkButton { diff --git a/components/button/lib/link-button.ts b/components/button/lib/link-button.ts index 5e1dc8cde37..e19827edf0c 100644 --- a/components/button/lib/link-button.ts +++ b/components/button/lib/link-button.ts @@ -5,11 +5,11 @@ */ import {html, TemplateResult} from 'lit'; -import {property} from 'lit/decorators'; -import {classMap} from 'lit/directives/class-map'; -import {ifDefined} from 'lit/directives/if-defined'; +import {property} from 'lit/decorators.js'; +import {classMap} from 'lit/directives/class-map.js'; +import {ifDefined} from 'lit/directives/if-defined.js'; -import {Button} from './button'; +import {Button} from './button.js'; type LinkTarget = '_blank'|'_parent'|'_self'|'_top'; diff --git a/components/button/lib/outlined-button.ts b/components/button/lib/outlined-button.ts index f7f7c4d7b1f..c29f256ea74 100644 --- a/components/button/lib/outlined-button.ts +++ b/components/button/lib/outlined-button.ts @@ -5,9 +5,9 @@ */ import {html, TemplateResult} from 'lit'; -import {ClassInfo} from 'lit/directives/class-map'; +import {ClassInfo} from 'lit/directives/class-map.js'; -import {Button} from './button'; +import {Button} from './button.js'; /** @soyCompatible */ export class OutlinedButton extends Button { diff --git a/components/button/lib/outlined-link-button.ts b/components/button/lib/outlined-link-button.ts index 6be6af9da48..f6ed75dfdf4 100644 --- a/components/button/lib/outlined-link-button.ts +++ b/components/button/lib/outlined-link-button.ts @@ -5,9 +5,9 @@ */ import {html, TemplateResult} from 'lit'; -import {ClassInfo} from 'lit/directives/class-map'; +import {ClassInfo} from 'lit/directives/class-map.js'; -import {LinkButton} from './link-button'; +import {LinkButton} from './link-button.js'; /** @soyCompatible */ export class OutlinedLinkButton extends LinkButton { diff --git a/components/button/lib/text-button.ts b/components/button/lib/text-button.ts index 3be6fbc9b29..11b86e4f673 100644 --- a/components/button/lib/text-button.ts +++ b/components/button/lib/text-button.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {ClassInfo} from 'lit/directives/class-map'; +import {ClassInfo} from 'lit/directives/class-map.js'; -import {Button} from './button'; +import {Button} from './button.js'; /** @soyCompatible */ export class TextButton extends Button { diff --git a/components/button/lib/text-link-button.ts b/components/button/lib/text-link-button.ts index 8ac6ebf3e93..1d36df93355 100644 --- a/components/button/lib/text-link-button.ts +++ b/components/button/lib/text-link-button.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {ClassInfo} from 'lit/directives/class-map'; +import {ClassInfo} from 'lit/directives/class-map.js'; -import {LinkButton} from './link-button'; +import {LinkButton} from './link-button.js'; /** @soyCompatible */ export class TextLinkButton extends LinkButton { diff --git a/components/button/lib/tonal-button.ts b/components/button/lib/tonal-button.ts index 461319d6df0..74a22f7540c 100644 --- a/components/button/lib/tonal-button.ts +++ b/components/button/lib/tonal-button.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {ClassInfo} from 'lit/directives/class-map'; +import {ClassInfo} from 'lit/directives/class-map.js'; -import {Button} from './button'; +import {Button} from './button.js'; /** @soyCompatible */ export class TonalButton extends Button { diff --git a/components/button/lib/tonal-link-button.ts b/components/button/lib/tonal-link-button.ts index 42dc2c64ce1..22963e1957f 100644 --- a/components/button/lib/tonal-link-button.ts +++ b/components/button/lib/tonal-link-button.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {ClassInfo} from 'lit/directives/class-map'; +import {ClassInfo} from 'lit/directives/class-map.js'; -import {LinkButton} from './link-button'; +import {LinkButton} from './link-button.js'; /** @soyCompatible */ export class TonalLinkButton extends LinkButton { diff --git a/components/button/outlined-button.ts b/components/button/outlined-button.ts index 2e66b16f4a0..3674b31d5cf 100644 --- a/components/button/outlined-button.ts +++ b/components/button/outlined-button.ts @@ -4,11 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {OutlinedButton} from './lib/outlined-button'; -import {styles as outlinedStyles} from './lib/outlined-styles.css'; -import {styles as sharedStyles} from './lib/shared-styles.css'; +import {OutlinedButton} from './lib/outlined-button.js'; +import {styles as outlinedStyles} from './lib/outlined-styles.css.js'; +import {styles as sharedStyles} from './lib/shared-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/button/outlined-link-button.ts b/components/button/outlined-link-button.ts index c84eef04cd7..5561f44cce7 100644 --- a/components/button/outlined-link-button.ts +++ b/components/button/outlined-link-button.ts @@ -4,11 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {OutlinedLinkButton} from './lib/outlined-link-button'; -import {styles as outlinedStyles} from './lib/outlined-styles.css'; -import {styles as sharedStyles} from './lib/shared-styles.css'; +import {OutlinedLinkButton} from './lib/outlined-link-button.js'; +import {styles as outlinedStyles} from './lib/outlined-styles.css.js'; +import {styles as sharedStyles} from './lib/shared-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/button/text-button.ts b/components/button/text-button.ts index c98767d4161..d4a7f1c6c83 100644 --- a/components/button/text-button.ts +++ b/components/button/text-button.ts @@ -4,11 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {styles as sharedStyles} from './lib/shared-styles.css'; -import {TextButton} from './lib/text-button'; -import {styles as textStyles} from './lib/text-styles.css'; +import {styles as sharedStyles} from './lib/shared-styles.css.js'; +import {TextButton} from './lib/text-button.js'; +import {styles as textStyles} from './lib/text-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/button/text-link-button.ts b/components/button/text-link-button.ts index 937c82236ce..3a19ca3744a 100644 --- a/components/button/text-link-button.ts +++ b/components/button/text-link-button.ts @@ -4,11 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {styles as sharedStyles} from './lib/shared-styles.css'; -import {TextLinkButton} from './lib/text-link-button'; -import {styles as textStyles} from './lib/text-styles.css'; +import {styles as sharedStyles} from './lib/shared-styles.css.js'; +import {TextLinkButton} from './lib/text-link-button.js'; +import {styles as textStyles} from './lib/text-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/button/tonal-button.ts b/components/button/tonal-button.ts index e51e8a88eb6..bc7b13b5892 100644 --- a/components/button/tonal-button.ts +++ b/components/button/tonal-button.ts @@ -4,11 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {styles as sharedStyles} from './lib/shared-styles.css'; -import {TonalButton} from './lib/tonal-button'; -import {styles as tonalStyles} from './lib/tonal-styles.css'; +import {styles as sharedStyles} from './lib/shared-styles.css.js'; +import {TonalButton} from './lib/tonal-button.js'; +import {styles as tonalStyles} from './lib/tonal-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/button/tonal-link-button.ts b/components/button/tonal-link-button.ts index 1ea22f43146..b744b4537c7 100644 --- a/components/button/tonal-link-button.ts +++ b/components/button/tonal-link-button.ts @@ -4,11 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {styles as sharedStyles} from './lib/shared-styles.css'; -import {TonalLinkButton} from './lib/tonal-link-button'; -import {styles as tonalStyles} from './lib/tonal-styles.css'; +import {styles as sharedStyles} from './lib/shared-styles.css.js'; +import {TonalLinkButton} from './lib/tonal-link-button.js'; +import {styles as tonalStyles} from './lib/tonal-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/checkbox/checkbox.ts b/components/checkbox/checkbox.ts index 9df1625984b..7c8ebeac70c 100644 --- a/components/checkbox/checkbox.ts +++ b/components/checkbox/checkbox.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {Checkbox} from './lib/checkbox'; -import {styles} from './lib/checkbox-styles.css'; +import {Checkbox} from './lib/checkbox.js'; +import {styles} from './lib/checkbox-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/checkbox/harness.ts b/components/checkbox/harness.ts index 85832898bd4..c985a33e97d 100644 --- a/components/checkbox/harness.ts +++ b/components/checkbox/harness.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {Harness} from '../testing/harness'; +import {Harness} from '../testing/harness.js'; -import {Checkbox} from './lib/checkbox'; +import {Checkbox} from './lib/checkbox.js'; /** * Test harness for checkbox. diff --git a/components/checkbox/lib/_checkbox-theme.scss b/components/checkbox/lib/_checkbox-theme.scss index 7b39b8ce907..65f86411fb9 100644 --- a/components/checkbox/lib/_checkbox-theme.scss +++ b/components/checkbox/lib/_checkbox-theme.scss @@ -27,7 +27,7 @@ @use 'sass:map'; @use 'sass:math'; @use 'sass:selector'; -@use '@material/theme/shadow-dom'; +@use '../../compat/theme/shadow-dom'; @use '../../sass/theme'; @use '../../tokens'; @use '../../sass/var'; diff --git a/components/checkbox/lib/checkbox.ts b/components/checkbox/lib/checkbox.ts index 57666063dce..e7846d1c8ad 100644 --- a/components/checkbox/lib/checkbox.ts +++ b/components/checkbox/lib/checkbox.ts @@ -4,19 +4,19 @@ * SPDX-License-Identifier: Apache-2.0 */ -import '../../focus/focus-ring'; -import '../../ripple/ripple'; +import '../../focus/focus-ring.js'; +import '../../ripple/ripple.js'; -import {ariaProperty as legacyAriaProperty} from '@material/mwc-base/aria-property'; +import {ariaProperty as legacyAriaProperty} from '@material/mwc-base/aria-property.js'; import {html, PropertyValues, TemplateResult} from 'lit'; -import {property, query, state} from 'lit/decorators'; -import {classMap} from 'lit/directives/class-map'; -import {ifDefined} from 'lit/directives/if-defined'; - -import {ActionElement, BeginPressConfig, EndPressConfig} from '../../action-element/action-element'; -import {ariaProperty} from '../../decorators/aria-property'; -import {pointerPress, shouldShowStrongFocus} from '../../focus/strong-focus'; -import {MdRipple} from '../../ripple/ripple'; +import {property, query, state} from 'lit/decorators.js'; +import {classMap} from 'lit/directives/class-map.js'; +import {ifDefined} from 'lit/directives/if-defined.js'; + +import {ActionElement, BeginPressConfig, EndPressConfig} from '../../action-element/action-element.js'; +import {ariaProperty} from '../../decorators/aria-property.js'; +import {pointerPress, shouldShowStrongFocus} from '../../focus/strong-focus.js'; +import {MdRipple} from '../../ripple/ripple.js'; /** @soyCompatible */ export class Checkbox extends ActionElement { diff --git a/components/checkbox/lib/foundation.ts b/components/checkbox/lib/foundation.ts index 0fcc51d6cff..14d894c1e7a 100644 --- a/components/checkbox/lib/foundation.ts +++ b/components/checkbox/lib/foundation.ts @@ -21,9 +21,9 @@ * THE SOFTWARE. */ -import {MDCFoundation} from '@material/base/foundation'; -import {MDCCheckboxAdapter} from './adapter'; -import {cssClasses, numbers, strings} from './constants'; +import {MDCFoundation} from '@material/base/foundation.js'; +import {MDCCheckboxAdapter} from './adapter.js'; +import {cssClasses, numbers, strings} from './constants.js'; export class MDCCheckboxFoundation extends MDCFoundation { static override get cssClasses() { diff --git a/components/checkbox/test/md-checkbox_test.ts b/components/checkbox/test/md-checkbox_test.ts index 4c54aecf0c1..360f6783e54 100644 --- a/components/checkbox/test/md-checkbox_test.ts +++ b/components/checkbox/test/md-checkbox_test.ts @@ -7,9 +7,9 @@ import {MdFocusRing} from 'google3/third_party/javascript/material_web_components/m3/focus/focus-ring'; import {html} from 'lit'; -import {fixture, TestFixture} from '../../../../test/src/util/helpers'; -import {MdCheckbox} from '../checkbox'; -import {CheckboxHarness} from '../harness'; +import {fixture, TestFixture} from '../../../../test/src/util/helpers.js'; +import {MdCheckbox} from '../checkbox.js'; +import {CheckboxHarness} from '../harness.js'; interface CheckboxProps { checked: boolean; diff --git a/components/compat/theme/README.md b/components/compat/theme/README.md new file mode 100644 index 00000000000..12d793d01e3 --- /dev/null +++ b/components/compat/theme/README.md @@ -0,0 +1,2 @@ +Note: this directory is for backwards compatibility only and should not be used +by greenfield components. diff --git a/components/compat/theme/_color-palette.scss b/components/compat/theme/_color-palette.scss new file mode 100644 index 00000000000..5269c513a4a --- /dev/null +++ b/components/compat/theme/_color-palette.scss @@ -0,0 +1,277 @@ +// +// Copyright 2022 Google LLC +// SPDX-License-Identifier: Apache-2.0 +// + +$red-50: #ffebee; +$red-100: #ffcdd2; +$red-200: #ef9a9a; +$red-300: #e57373; +$red-400: #ef5350; +$red-500: #f44336; +$red-600: #e53935; +$red-700: #d32f2f; +$red-800: #c62828; +$red-900: #b71c1c; +$red-a100: #ff8a80; +$red-a200: #ff5252; +$red-a400: #ff1744; +$red-a700: #d50000; + +$pink-50: #fce4ec; +$pink-100: #f8bbd0; +$pink-200: #f48fb1; +$pink-300: #f06292; +$pink-400: #ec407a; +$pink-500: #e91e63; +$pink-600: #d81b60; +$pink-700: #c2185b; +$pink-800: #ad1457; +$pink-900: #880e4f; +$pink-a100: #ff80ab; +$pink-a200: #ff4081; +$pink-a400: #f50057; +$pink-a700: #c51162; + +$purple-50: #f3e5f5; +$purple-100: #e1bee7; +$purple-200: #ce93d8; +$purple-300: #ba68c8; +$purple-400: #ab47bc; +$purple-500: #9c27b0; +$purple-600: #8e24aa; +$purple-700: #7b1fa2; +$purple-800: #6a1b9a; +$purple-900: #4a148c; +$purple-a100: #ea80fc; +$purple-a200: #e040fb; +$purple-a400: #d500f9; +$purple-a700: #a0f; + +$deep-purple-50: #ede7f6; +$deep-purple-100: #d1c4e9; +$deep-purple-200: #b39ddb; +$deep-purple-300: #9575cd; +$deep-purple-400: #7e57c2; +$deep-purple-500: #673ab7; +$deep-purple-600: #5e35b1; +$deep-purple-700: #512da8; +$deep-purple-800: #4527a0; +$deep-purple-900: #311b92; +$deep-purple-a100: #b388ff; +$deep-purple-a200: #7c4dff; +$deep-purple-a400: #651fff; +$deep-purple-a700: #6200ea; + +$indigo-50: #e8eaf6; +$indigo-100: #c5cae9; +$indigo-200: #9fa8da; +$indigo-300: #7986cb; +$indigo-400: #5c6bc0; +$indigo-500: #3f51b5; +$indigo-600: #3949ab; +$indigo-700: #303f9f; +$indigo-800: #283593; +$indigo-900: #1a237e; +$indigo-a100: #8c9eff; +$indigo-a200: #536dfe; +$indigo-a400: #3d5afe; +$indigo-a700: #304ffe; + +$blue-50: #e3f2fd; +$blue-100: #bbdefb; +$blue-200: #90caf9; +$blue-300: #64b5f6; +$blue-400: #42a5f5; +$blue-500: #2196f3; +$blue-600: #1e88e5; +$blue-700: #1976d2; +$blue-800: #1565c0; +$blue-900: #0d47a1; +$blue-a100: #82b1ff; +$blue-a200: #448aff; +$blue-a400: #2979ff; +$blue-a700: #2962ff; + +$light-blue-50: #e1f5fe; +$light-blue-100: #b3e5fc; +$light-blue-200: #81d4fa; +$light-blue-300: #4fc3f7; +$light-blue-400: #29b6f6; +$light-blue-500: #03a9f4; +$light-blue-600: #039be5; +$light-blue-700: #0288d1; +$light-blue-800: #0277bd; +$light-blue-900: #01579b; +$light-blue-a100: #80d8ff; +$light-blue-a200: #40c4ff; +$light-blue-a400: #00b0ff; +$light-blue-a700: #0091ea; + +$cyan-50: #e0f7fa; +$cyan-100: #b2ebf2; +$cyan-200: #80deea; +$cyan-300: #4dd0e1; +$cyan-400: #26c6da; +$cyan-500: #00bcd4; +$cyan-600: #00acc1; +$cyan-700: #0097a7; +$cyan-800: #00838f; +$cyan-900: #006064; +$cyan-a100: #84ffff; +$cyan-a200: #18ffff; +$cyan-a400: #00e5ff; +$cyan-a700: #00b8d4; + +$teal-50: #e0f2f1; +$teal-100: #b2dfdb; +$teal-200: #80cbc4; +$teal-300: #4db6ac; +$teal-400: #26a69a; +$teal-500: #009688; +$teal-600: #00897b; +$teal-700: #00796b; +$teal-800: #00695c; +$teal-900: #004d40; +$teal-a100: #a7ffeb; +$teal-a200: #64ffda; +$teal-a400: #1de9b6; +$teal-a700: #00bfa5; + +$green-50: #e8f5e9; +$green-100: #c8e6c9; +$green-200: #a5d6a7; +$green-300: #81c784; +$green-400: #66bb6a; +$green-500: #4caf50; +$green-600: #43a047; +$green-700: #388e3c; +$green-800: #2e7d32; +$green-900: #1b5e20; +$green-a100: #b9f6ca; +$green-a200: #69f0ae; +$green-a400: #00e676; +$green-a700: #00c853; + +$light-green-50: #f1f8e9; +$light-green-100: #dcedc8; +$light-green-200: #c5e1a5; +$light-green-300: #aed581; +$light-green-400: #9ccc65; +$light-green-500: #8bc34a; +$light-green-600: #7cb342; +$light-green-700: #689f38; +$light-green-800: #558b2f; +$light-green-900: #33691e; +$light-green-a100: #ccff90; +$light-green-a200: #b2ff59; +$light-green-a400: #76ff03; +$light-green-a700: #64dd17; + +$lime-50: #f9fbe7; +$lime-100: #f0f4c3; +$lime-200: #e6ee9c; +$lime-300: #dce775; +$lime-400: #d4e157; +$lime-500: #cddc39; +$lime-600: #c0ca33; +$lime-700: #afb42b; +$lime-800: #9e9d24; +$lime-900: #827717; +$lime-a100: #f4ff81; +$lime-a200: #eeff41; +$lime-a400: #c6ff00; +$lime-a700: #aeea00; + +$yellow-50: #fffde7; +$yellow-100: #fff9c4; +$yellow-200: #fff59d; +$yellow-300: #fff176; +$yellow-400: #ffee58; +$yellow-500: #ffeb3b; +$yellow-600: #fdd835; +$yellow-700: #fbc02d; +$yellow-800: #f9a825; +$yellow-900: #f57f17; +$yellow-a100: #ffff8d; +$yellow-a200: #ff0; +$yellow-a400: #ffea00; +$yellow-a700: #ffd600; + +$amber-50: #fff8e1; +$amber-100: #ffecb3; +$amber-200: #ffe082; +$amber-300: #ffd54f; +$amber-400: #ffca28; +$amber-500: #ffc107; +$amber-600: #ffb300; +$amber-700: #ffa000; +$amber-800: #ff8f00; +$amber-900: #ff6f00; +$amber-a100: #ffe57f; +$amber-a200: #ffd740; +$amber-a400: #ffc400; +$amber-a700: #ffab00; + +$orange-50: #fff3e0; +$orange-100: #ffe0b2; +$orange-200: #ffcc80; +$orange-300: #ffb74d; +$orange-400: #ffa726; +$orange-500: #ff9800; +$orange-600: #fb8c00; +$orange-700: #f57c00; +$orange-800: #ef6c00; +$orange-900: #e65100; +$orange-a100: #ffd180; +$orange-a200: #ffab40; +$orange-a400: #ff9100; +$orange-a700: #ff6d00; + +$deep-orange-50: #fbe9e7; +$deep-orange-100: #ffccbc; +$deep-orange-200: #ffab91; +$deep-orange-300: #ff8a65; +$deep-orange-400: #ff7043; +$deep-orange-500: #ff5722; +$deep-orange-600: #f4511e; +$deep-orange-700: #e64a19; +$deep-orange-800: #d84315; +$deep-orange-900: #bf360c; +$deep-orange-a100: #ff9e80; +$deep-orange-a200: #ff6e40; +$deep-orange-a400: #ff3d00; +$deep-orange-a700: #dd2c00; + +$brown-50: #efebe9; +$brown-100: #d7ccc8; +$brown-200: #bcaaa4; +$brown-300: #a1887f; +$brown-400: #8d6e63; +$brown-500: #795548; +$brown-600: #6d4c41; +$brown-700: #5d4037; +$brown-800: #4e342e; +$brown-900: #3e2723; + +$grey-50: #fafafa; +$grey-100: #f5f5f5; +$grey-200: #eee; +$grey-300: #e0e0e0; +$grey-400: #bdbdbd; +$grey-500: #9e9e9e; +$grey-600: #757575; +$grey-700: #616161; +$grey-800: #424242; +$grey-900: #212121; + +$blue-grey-50: #eceff1; +$blue-grey-100: #cfd8dc; +$blue-grey-200: #b0bec5; +$blue-grey-300: #90a4ae; +$blue-grey-400: #78909c; +$blue-grey-500: #607d8b; +$blue-grey-600: #546e7a; +$blue-grey-700: #455a64; +$blue-grey-800: #37474f; +$blue-grey-900: #263238; diff --git a/components/compat/theme/_css.scss b/components/compat/theme/_css.scss new file mode 100644 index 00000000000..9e2dc6157bd --- /dev/null +++ b/components/compat/theme/_css.scss @@ -0,0 +1,92 @@ +// +// Copyright 2022 Google LLC +// SPDX-License-Identifier: Apache-2.0 +// + +@use 'sass:list'; +@use 'sass:map'; +@use 'sass:meta'; +@use './gss'; + +/// When true, add an additional property/value declaration before declarations +/// that use advanced features such as custom properties or CSS functions. This +/// adds fallback support for older browsers such as IE11 that do not support +/// these features at the cost of additional CSS. Set this variable to false to +/// disable generating fallback declarations. +$enable-fallback-declarations: true !default; + +/// Writes a CSS property/value declaration. This mixin is used throughout the +/// theme package for consistency for dynamically setting CSS property values. +/// +/// This mixin may optionally take a fallback value. For advanced features such +/// as custom properties or CSS functions like min and max, a fallback value is +/// recommended to support older browsers. +/// +/// @param {String} $property - The CSS property of the declaration. +/// @param {*} $value - The value of the CSS declaration. The value should be +/// resolved by other theme functions first (i.e. custom property Maps and +/// Material theme keys are not supported in this mixin). If the value is +/// null, no declarations will be emitted. +/// @param {*} $fallback - An optional fallback value for older browsers. If +/// provided, a second property/value declaration will be added before the +/// main property/value declaration. +/// @param {Map} $gss - An optional Map of GSS annotations to add. +/// @param {Bool} $important - If true, add `!important` to the declaration. +@mixin declaration( + $property, + $value, + $fallback-value: null, + $gss: (), + $important: false +) { + // Normally setting a null value to a property will not emit CSS, so mixins + // wouldn't need to check this. However, Sass will throw an error if the + // interpolated property is a custom property. + @if $value != null { + $important-rule: if($important, ' !important', ''); + + @if $fallback-value and $enable-fallback-declarations { + @include gss.annotate($gss); + #{$property}: #{$fallback-value} #{$important-rule}; + + // Add @alternate to annotations. + $gss: map.merge( + $gss, + ( + alternate: true, + ) + ); + } + + @include gss.annotate($gss); + #{$property}: #{$value}#{$important-rule}; + } +} + +/// Unpacks shorthand values for CSS properties (i.e. lists of 1-3 values). +/// If a list of 4 values is given, it is returned as-is. +/// +/// Examples: +/// +/// unpack-value(4px) => 4px 4px 4px 4px +/// unpack-value(4px 2px) => 4px 2px 4px 2px +/// unpack-value(4px 2px 2px) => 4px 2px 2px 2px +/// unpack-value(4px 2px 0 2px) => 4px 2px 0 2px +/// +/// @param {Number | Map | List} $value - List of 1 to 4 value numbers. +/// @return {List} a List of 4 value numbers. +@function unpack-value($value) { + @if meta.type-of($value) == 'map' or list.length($value) == 1 { + @return $value $value $value $value; + } @else if list.length($value) == 4 { + @return $value; + } @else if list.length($value) == 3 { + @return list.nth($value, 1) list.nth($value, 2) list.nth($value, 3) + list.nth($value, 2); + } @else if list.length($value) == 2 { + @return list.nth($value, 1) list.nth($value, 2) list.nth($value, 1) + list.nth($value, 2); + } + + @error "Invalid CSS property value: '#{$value}' is more than 4 values"; +} diff --git a/components/compat/theme/_custom-properties.scss b/components/compat/theme/_custom-properties.scss new file mode 100644 index 00000000000..9334abeed1a --- /dev/null +++ b/components/compat/theme/_custom-properties.scss @@ -0,0 +1,309 @@ +// +// Copyright 2022 Google LLC +// SPDX-License-Identifier: Apache-2.0 +// + +@use 'sass:list'; +@use 'sass:map'; +@use 'sass:meta'; +@use 'sass:string'; +@use './css'; +@use './gss'; + +/// Configuration for custom properties. +/// @see {mixin} configure +$_config: ( + emit-custom-properties: true, + emit-fallback-values: true, + emit-fallback-vars: true, +); + +/// Configure options for custom properties. The configuration will be applied +/// within the scope of the mixin's content and reset when the mixin scope ends. +/// +/// @example - scss +/// @include configure($emit-fallback-values: false) { +/// // No fallback values will be emitted within this mixin scope +/// @include another-mixin(); +/// } +/// +/// All parameters must be provided as argument lists. +/// +/// @link https://sass-lang.com/documentation/values/lists#argument-lists +/// +/// @param {Bool} $emit-custom-properties [true] - Enable or disable all +/// custom property emission. +/// @param {Bool} $emit-fallback-values [true] - Enable or disable emission of +/// fallback CSS static values. This does not include dynamic `var()` +/// fallback values. +/// @param {Bool} $emit-fallback-vars [true] - Enable or disable emission of +/// fallback `var()` values. +@mixin configure($config...) { + @if not meta.content-exists() { + @error 'content is required for configure()'; + } + + $config: meta.keywords($config); + @each $key, $value in $config { + @if $value == null { + $config: map.remove($config, $key); + } + } + + @if list.length($config) == 0 { + @content; + } @else { + $previous: $_config; + // Use !global to avoid shadowing + // https://sass-lang.com/documentation/variables#shadowing + $_config: map.merge($_config, $config) !global; + @content; + $_config: $previous !global; + } +} + +/// Returns true if the parameter is a custom property Map. +/// +/// @param {*} $value - the value to test. +/// @return true if the value is a custom property Map, or false if not. +@function is-custom-prop($value) { + @return meta.type-of($value) == 'map' and map.has-key($value, 'varname'); +} + +/// Indicates whether or not a value is a custom property var() string. +/// +/// @example - scss +/// $is-prop-string: is-custom-prop-string('var(--foo)'); // true +/// +/// @param {*} $value - The value to test. +/// @return {Bool} True if the value is a custom property var() string, or +/// false if not. +@function is-custom-prop-string($value) { + @return meta.type-of($value) == 'string' and string.slice($value, 1, 4) == + 'var('; +} + +/// Returns true if $prop1's varname and fallback values are deeply equal to +/// $prop2's varname and fallback values. +/// +/// @param {Map} $prop1 - the first value to test. +/// @param {Map} $prop2 - the second value to test. +/// @return true if both properties are deeply equal +@function are-equal($prop1, $prop2) { + @return create-var($prop1) == create-var($prop2); +} + +/// Creates a custom property Map. +/// +/// @param {String} $varname - the custom property name. +/// @param {String | Number | Map} - the fallback value (may be another custom +/// property Map). May be null. +/// @return a custom property Map. +@function create($varname, $fallback: null) { + @if string.slice($varname, 1, 2) != '--' { + $varname: create-varname($varname); + } + + @return (varname: $varname, fallback: $fallback); +} + +/// Global prefix for all custom properties. +$_varname-prefix: 'mdc'; + +/// Create a custom property variable name with the global prefix. +/// +/// @example - scss +/// $varname: create-varname(foo); // --mdc-foo +/// +/// @param {String} $name - The name of the custom property. +/// @return {String} The full CSS custom property variable name. +@function create-varname($name) { + @return --#{$_varname-prefix}-#{$name}; +} + +/// Returns the custom property variable name of a custom property Map. +/// +/// @param {Map} $custom-prop - a custom property Map. +/// @return the custom property variable name defined by the Map. +@function get-varname($custom-prop) { + @return map.get($custom-prop, 'varname'); +} + +/// Returns the fallback value of a custom property Map. May be null if the +/// custom property does not have a fallback. +/// +/// @param {Map} $custom-prop - a custom property Map. +/// @param {Bool} $shallow - if true, return the first fallback value, which +/// may be another custom property Map. Defaults to false, which will return +/// the deep final fallback value. +/// @return the fallback value of a custom property Map. May be null. +@function get-fallback($custom-prop, $shallow: false) { + $fallback: map.get($custom-prop, 'fallback'); + @if is-custom-prop($fallback) and not $shallow { + @return get-fallback($fallback); + } + + @return $fallback; +} + +/// Creates a new custom property Map and returns it with the specified new +/// fallback value. +/// +/// @param {Map} $custom-prop - the custom property Map to copy. +/// @param {String | Number | Map} $new-fallback - the new fallback value of the +/// custom property Map. May be null. +/// @param {Bool} $shallow - if true, set the first fallback value. Defaults to +/// false, which will set the deep final fallback value. +/// @return a new custom property Map with the new fallback value. +@function set-fallback($custom-prop, $new-fallback, $shallow: false) { + $varname: get-varname($custom-prop); + $first-fallback: get-fallback($custom-prop, $shallow: true); + + @if is-custom-prop($first-fallback) and not $shallow { + // The first fallback is a custom property and $shallow is false. Deeply + // set the fallback value of the custom property and get the new custom + // property Map returned. + $new-fallback: set-fallback($first-fallback, $new-fallback); + } + + @return create($varname, $new-fallback); +} + +/// Creates and returns a CSS `var()` function value represented by the provided +/// custom property Map. +/// +/// If custom properties are disabled, this function will return the custom +/// property's fallback value instead, which may be `null` and result in an +/// empty declaration. +/// +/// @param {Map} $custom-prop - A custom property Map. +/// @return {*} A CSS value, typically a `var()` function, representing the +/// custom property. The returned value may change depending on the current +/// configuration options for custom properties and whether or not a +/// fallback value is present in the custom property Map. +@function create-var($custom-prop) { + @if not map.get($_config, emit-custom-properties) { + // If configured not to emit custom properties and a request is made for a + // custom prop's CSS value, return its fallback value. If this is null, it + // will result in an empty declaration. + @return get-fallback($custom-prop); + } + + $varname: get-varname($custom-prop); + $fallback: get-fallback($custom-prop, $shallow: true); + + $emit-fallback-vars: map.get($_config, emit-fallback-vars); + $fallback-is-prop: is-custom-prop($fallback); + @if $fallback-is-prop and $emit-fallback-vars { + @return var($varname, create-var($fallback)); + } + + $emit-fallback-values: map.get($_config, emit-fallback-values); + @if $fallback and not $fallback-is-prop and $emit-fallback-values { + @return var($varname, $fallback); + } + + @return var($varname); +} + +/// Retrieves the CSS declaration value for a custom property Map. This is +/// typically a `var()` function. +/// +/// If custom properties are disabled, the custom property's fallback value +/// will be returned instead. If the fallback value is `null`, an error will +/// be thrown. +/// +/// @param {Map} $custom-prop - The custom property Map to retrieve a +/// declaration value for. +/// @return {*} The CSS declaration value. +@function get-declaration-value($custom-prop) { + $emit-custom-properties: map.get($_config, emit-custom-properties); + $fallback: get-fallback($custom-prop); + @if not $emit-custom-properties and not $fallback { + @error 'Custom properties are disabled and #{get-varname($custom-prop)} does not have a fallback value.'; + } + + @if not $emit-custom-properties { + @return $fallback; + } + + @return create-var($custom-prop); +} + +/// Retrieves the CSS fallback declaration value for a custom property Map. +/// This is typically a static CSS value if a custom property has a fallback, or +/// null if it does not. +/// +/// This function will always return `null` if custom properties or fallback +/// values are disabled. +/// +/// @param {Map} $custom-prop - The custom property Map to retrieve a fallback +/// declaration value for. +/// @return {String | null} The CSS fallback declaration value. +@function get-declaration-fallback($custom-prop) { + $emit-custom-properties: map.get($_config, emit-custom-properties); + $emit-fallback-values: map.get($_config, emit-fallback-values); + @if not $emit-custom-properties or not $emit-fallback-values { + @return null; + } + + @return get-fallback($custom-prop); +} + +/// Emits a CSS declaration for a custom property. A custom property may either +/// be provided as the value to a CSS property string to be emitted as a var() +/// function, or as a standalone value to be emitted as a custom property +/// declaration. +/// +/// @example - scss +/// @include declaration(color, create(--foo, teal)); +/// // color: var(--foo, teal); +/// @include declaration(create(--foo, teal)); +/// // --foo: teal; +/// +/// Standalone custom properties must have a fallback value to emit as a CSS +/// declaration. +/// +/// The declaration emitted for a custom property may change or be ignored +/// based on the current configuration for custom properties. +/// +/// @see {mixin} css.declaration +/// @see {mixin} configuration +/// +/// @param {String} $property - The CSS property of the declaration or the +/// custom property Map to emit. +/// @param {Map} $custom-prop - A custom property Map for the property's value. +/// Optional if $property is the custom property Map to emit. +/// @param {Map} $gss - An optional Map of GSS annotations to add. +/// @param {Bool} $important - If true, add `!important` to the declaration. +@mixin declaration($property, $custom-prop: null, $gss: (), $important: false) { + @if $property { + $value: null; + $fallback-value: null; + @if is-custom-prop($property) { + @if map.get($_config, emit-custom-properties) { + $custom-prop: $property; + $property: get-varname($custom-prop); + $value: get-fallback($custom-prop, $shallow: true); + @if is-custom-prop($value) { + $value: create-var($value); + } + } + } @else { + @if not is-custom-prop($custom-prop) { + @error "Invalid custom property: #{$custom-prop}. Must be a Map with 'varname' and 'fallback'."; + } + + $value: get-declaration-value($custom-prop); + $fallback-value: get-declaration-fallback($custom-prop); + } + + @include css.declaration( + $property, + $value, + $fallback-value: $fallback-value, + $gss: $gss, + $important: $important + ); + } +} diff --git a/components/compat/theme/_gss.scss b/components/compat/theme/_gss.scss new file mode 100644 index 00000000000..4fb6f3ce83e --- /dev/null +++ b/components/compat/theme/_gss.scss @@ -0,0 +1,45 @@ +// +// Copyright 2022 Google LLC +// SPDX-License-Identifier: Apache-2.0 +// + +@use 'sass:list'; +@use 'sass:map'; +@use 'sass:meta'; + +/// Adds optional GSS annotation comments. Useful for theme mixins where one or +/// more properties are set indirectly. +/// +/// Annotations may be provided as a Map of annotations or as named arguments. +/// +/// @example - scss +/// @include annotate((noflip: true)); +/// left: 0; +/// +/// @example - scss +/// @include annotate($noflip: true); +/// left: 0; +/// +/// @example - css +/// /* @noflip */ /*rtl:ignore*/ +/// left: 0; +/// +/// @param {Map} $annotations - Map of annotations. Values must be set to `true` +/// for an annotation to be added. +@mixin annotate($annotations...) { + $keywords: meta.keywords($annotations); + @if list.length($annotations) > 0 { + $annotations: list.nth($annotations, 1); + } @else { + $annotations: $keywords; + } + + @if (map.get($annotations, alternate) == true) { + /* @alternate */ + } + + // noflip must be the last tag right before the property + @if (map.get($annotations, noflip) == true) { + /* @noflip */ /*rtl:ignore*/ + } +} diff --git a/components/compat/theme/_keys.scss b/components/compat/theme/_keys.scss new file mode 100644 index 00000000000..132d0cecee5 --- /dev/null +++ b/components/compat/theme/_keys.scss @@ -0,0 +1,507 @@ +// +// Copyright 2022 Google LLC +// SPDX-License-Identifier: Apache-2.0 +// + +@use 'sass:list'; +@use 'sass:map'; +@use 'sass:meta'; +@use 'sass:string'; +@use './custom-properties'; + +/// A flat Map of keys and their values. Keys may be set to any static CSS +/// value, or another key's name to resolve to that key's value. +/// +/// @example - scss +/// $_store: ( +/// primary: purple, // keys may be set to CSS values... +/// button-color: primary, // ...or resolve to another key's value +/// ); +/// +/// @type {Map} +$_store: (); +/// A flat Map of relationship links between keys. While key values may +/// resolve to another key's value in the key store, the store does not +/// preserve or infer relationships between keys. +/// +/// Instead, this link Map records the original relationship between keys as +/// their values are updated and potentially overridden with customizations. +/// +/// @example - scss +/// // Given these keys... +/// $primary: purple; +/// $button-color: $primary; // ...button-color is linked to the primary key +/// +/// // A key store with value customizations may look like this: +/// $_store: ( +/// primary: amber, +/// button-color: teal, // the relationship is lost with a customization +/// ); +/// +/// // The links Map preserves the relationship for custom property +/// // generation, while the store Map is only focused on values. +/// $_links: ( +/// button-color: primary, +/// ); +/// +/// @type {Map} +$_links: (); +/// A map of key options. If a key has options, it will have an entry in this +/// variable with a Map value with options for the key. +/// +/// @example - scss +/// // Option structure +/// $_options: ( +/// key-name: ( +/// // An additional prefix to add when generating the varname of a +/// // key's custom property +/// // --mdc--key-name +/// custom-property-prefix: prefix +/// ) +/// ); +/// +/// @type {Map} +$_options: (); + +/// Indicates whether or not the provided value is a registered key. +/// +/// @param {String} $key - One or more key parts to check +/// @return {Bool} True if the key is registered, or false if it is not. +@function is-key($key...) { + $key: combine($key...); + @return map.has-key($_store, $key); +} + +/// Retrieves a List of all keys matching the provided key group prefix. +/// +/// @example - scss +/// $keys: get-keys(typography); +/// // (typography-headline, +/// // typography-body, +/// // typography-body-font, +/// // typography-body-size) +/// +/// @param {String} $group - Optional group prefix to search by. If ommitted, +/// all registered keys will be returned. +/// @return {List} A List of all keys matching the group prefix. +@function get-keys($group: '') { + $keys: (); + @each $key in map.keys($_store) { + @if string.index($key, $group) == 1 { + $keys: list.append($keys, $key); + } + } + + @return $keys; +} + +/// Registers a Map of keys and their values with the key store. Key values may +/// either be CSS values or other key strings. +/// +/// @example - scss +/// @include set-values(( +/// primary: teal, +/// label-color: primary +/// )); +/// +/// Options may also be added for each key by providing an `$options` parameter. +/// +/// @example - scss +/// @include set-values( +/// $key-map, +/// $options: ( +/// // An additional prefix to add when generating the varname of a +/// // key's custom property +/// // --mdc--key-name +/// custom-property-prefix: prefix +/// ) +/// ); +/// +/// Note that this mixin only sets key values. If a key points to another key, +/// it does not link those keys when custom properties are emitted. Use +/// `add-link()` or `register-theme()` to create links between keys. +/// +/// @see {mixin} set-value +/// @see {mixin} add-link +/// @see {mixin} register-theme +/// +/// @param {Map} $key-map - A Map of keys to register. +/// @param {Map} $options [null] - Optional Map of options to add for each key. +@mixin set-values($key-map, $options: null) { + $unused: set-values($key-map, $options: $options); +} + +/// Function version of `set-values()`. +/// +/// Mixins cannot be invoked within functions in Sass. Use this when +/// `set-values()` must be used within a function. The return value may be +/// discarded or re-assigned to the `$key-map` provided. +/// +/// @example - scss +/// @function foo() { +/// $unused: set-values((primary: teal)); +/// } +/// +/// @function bar() { +/// $key-map: (primary: teal); +/// $key-map: set-values($key-map); +/// } +/// +/// @see {mixin} set-values +/// +/// @return {Map} `$key-map`, unmodified, for convenience. +@function set-values($key-map, $options: null) { + @each $key, $value in $key-map { + $key: set-value($key, $value, $options: $options); + } + + @return $key-map; +} + +/// Sets the value of a key. Key values may either be CSS values or other key +/// strings. +/// +/// @example - scss +/// @include set-value(primary, teal); +/// @include set-value(label-color, primary); +/// +/// Options may also be added for each key by providing an `$options` parameter. +/// +/// @example - scss +/// @include set-value(key-name, teal, $options: ( +/// // An additional prefix to add when generating the varname of a +/// // key's custom property +/// // --mdc--key-name +/// custom-property-prefix: prefix +/// )); +/// +/// Note that this mixin only sets the key's value. If the key points to another +/// key, it does not link those keys when custom properties are emitted. Use +/// `add-link()` or `register-theme()` to create links between keys. +/// +/// @see {mixin} add-link +/// @see {mixin} register-theme +/// +/// @param {String} $key - The key to set a value for. +/// @param {*} $value - The value of the key. +/// @param {Map} $options [null] - Optional Map of options to add for each key. +@mixin set-value($key, $value, $options: null) { + $unused: set-value($key, $value, $options: $options); +} + +/// Function version of `set-value()`. +/// +/// Mixins cannot be invoked within functions in Sass. Use this when +/// `set-value()` must be used within a function. The return value may be +/// discarded or re-assigned to the `$key` provided. +/// +/// @example - scss +/// @function foo() { +/// $unused: set-value(primary, teal); +/// } +/// +/// @function bar() { +/// $key: primary; +/// $key: set-value($key, teal); +/// } +/// +/// @see {mixin} set-value +/// +/// @return {String} `$key`, unmodified, for convenience. +@function set-value($key, $value, $options: null) { + // Use !global to avoid shadowing + // https://sass-lang.com/documentation/variables#shadowing + $_store: map.set($_store, $key, $value) !global; + @if $options { + $_options: map.set($_options, $key, $options) !global; + } + + @return $key; +} + +/// Add a link between two keys. +/// +/// When keys are linked and chained custom properties are emitted, the value +/// of `$key` will always include the `var()` function of its linked key, even +/// if it overrides its linked key's value. +/// +/// @example - scss +/// @include add-link(label-color, primary); +/// @include set-values(( +/// primary: teal, +/// label-color: amber +/// )); +/// +/// .primary { +/// @include theme.property(color, primary); +/// } +/// +/// .label-color { +/// @include theme.property(color, label-color); +/// } +/// +/// @example - css +/// .primary { +/// color: var(--primary, teal); +/// } +/// +/// .label-color { +/// color: var(--label-color, var(--primary, amber)); +/// } +/// +/// +/// If a key does not already have a value set, its value will be set to the +/// linked key provided. +/// +/// @param {String} $key - The key to add a link to. +/// @param {String} $link - The name to link to `$key`. +/// @throw When attempting to change the link of a key that has already been +/// linked. +@mixin add-link($key, $link) { + $unused: add-link($key, $link); +} + +/// Function version of `add-link()`. +/// +/// Mixins cannot be invoked within functions in Sass. Use this when +/// `add-link()` must be used within a function. The return value may be +/// discarded or re-assigned to the `$key` provided. +/// +/// @example - scss +/// @function foo() { +/// $unused: add-link(label-color, primary); +/// } +/// +/// @function bar() { +/// $key: label-color; +/// $key: set-value($key, primary); +/// } +/// +/// @see {mixin} `add-link()` +/// +/// @return {String} `$key` for convenience. +@function add-link($key, $link) { + @if map.has-key($_links, $key) { + @error '#{$key} already has a link'; + } + + // Use !global to avoid shadowing + // https://sass-lang.com/documentation/variables#shadowing + $_links: map.set($_links, $key, $link) !global; + @if not map.has-key($_store, $key) { + $key: set-value($key, $link); + } + + @return $key; +} + +/// Resolve a key to its CSS value. This may be a static CSS value or a dynamic +/// `var()` value. +/// +/// The value that this function returns may change depending on configuration +/// options if a key's value points to another key. +/// +/// To always retrieve the static CSS value a key resolves to, even if it points +/// to another key, provide `$deep: true` as a parameter to the function. +/// +/// @param {String...} $key - One or more key parts to resolve to a CSS value. +/// @param {Bool} $deep [false] - Set to true as a named parameter to always +/// resolve the key to its static CSS value and not a dynamic `var()` value. +/// @return {*} The value the key resolves to. This may be `null` if the key +/// (or the key it points to) has not been registered. +@function resolve($key...) { + $deep: map.get(meta.keywords($key), deep); + $key: combine($key...); + + $value: map.get($_store, $key); + @if is-key($value) { + $value: resolve($value); + } + + @return $value; +} + +/// Register a `$theme` Map variable's keys. This should only be done once in +/// the `theme-styles()` mixin with the canonical `$theme` Map to initialize +/// default values and linked keys. +/// +/// @example - scss +/// @mixin theme-styles($theme: button-filled-theme.$light-theme) { +/// @include keys.register-theme($theme, button-filled); +/// @include button-filled-theme.theme($theme); +/// } +/// +/// A component's `$theme` Map may have shared keys (such as color, shape, and +/// typography) that need linked before user customization with the `theme()` +/// mixin. +/// +/// The `register-theme()` mixin handles adding these links with `add-link()` +/// dynamically from a canonical `$theme` configuration provided by a trusted +/// source in `theme-styles()`. Subsequent calls to `theme()` will not invoke +/// `register-theme()` or change the linked keys' registration. +/// +/// @param {Map} $theme - The theme Map to register keys for. +/// @param {String} $prefix [null] - Optional prefix to prepend before each key. +/// @param {Map} $options [null] - Optional Map of options to add for each key. +@mixin register-theme($theme, $prefix: null, $options: null) { + // The first $theme Map received in theme-styles() should be used to + // register keys. + // Subsequent calls to theme() to customize key values will not be + // wrapped within theme-styles() and will not change the registered + // key values (or more importantly, their links), since + // customizations may be simple one-offs. + @each $key, $value in $theme { + @if $value != null { + $key: combine($prefix, $key); + @include set-value($key, $value, $options: $options); + @if is-key($value) { + @include add-link($key, $link: $value); + } + } + } +} + +/// Create and resolve custom properties from a user-provided `$theme` Map +/// variable. The created custom properties are returned in a Map that matches +/// the key structure of `$theme`. +/// +/// This function should be used within a `theme()` mixin after validation and +/// before providing any values to subsequent mixins. This will ensure that all +/// values are custom properties to support runtime theming. +/// +/// @example - scss +/// $light-theme: ( +/// label-color: on-primary +/// ); +/// +/// @mixin theme($theme) { +/// $theme: keys.create-theme-properties($theme, button-filled); +/// /*( +/// label-color: ( +/// varname: --mdc-button-filled-label-color, +/// fallback: ( +/// varname: --mdc-theme-on-primary, +/// fallback: white, +/// ) +/// ) +/// )*/ +/// } +/// +/// @param {Map} $theme - The theme Map to create custom properties for. +/// @param {String} $prefix [null] - Optional prefix to prepend for each key's +/// custom property. +/// @return {Map} A similar `$theme` Map whose values are replaced with the +/// newly created and resolved custom properties. +@function create-theme-properties($theme, $prefix: null) { + $theme-with-props: (); + @each $name, $value in $theme { + @if $value != null { + @if is-key($value) { + $value: create-custom-property($value); + } + + $key: combine($prefix, $name); + + @if _is-map($value) { + @each $k, $v in $value { + $theme-with-props: map.set( + $theme-with-props, + $name, + $k, + custom-properties.create(_create-varname(combine($key, $k)), $v) + ); + } + } @else { + $theme-with-props: map.set( + $theme-with-props, + $name, + custom-properties.create(_create-varname($key), $value) + ); + } + } + } + + @return $theme-with-props; +} + +/// Create a custom property for a key that represents the key's linked +/// relationships and final resolved static value. +/// +/// This function ignores customization options and is intended to return the +/// most accurate data structure representation of a key. Customization options +/// (such as custom property configuration) will change how the returned value +/// is emitted. +/// +/// @param {$tring...} $key - One or more key parts to create a custom property +/// for. +/// @return {Map} A custom property Map for the key. +@function create-custom-property($key...) { + $key: combine($key...); + $prop: custom-properties.create(_create-varname($key)); + $link: map.get($_links, $key); + @if $link { + $prop: custom-properties.set-fallback($prop, create-custom-property($link)); + } + + @return custom-properties.set-fallback($prop, resolve($key, $deep: true)); +} + +@mixin declare-custom-properties($theme, $prefix: null) { + $theme: create-theme-properties($theme, $prefix); + + @each $key, $value in $theme { + @if _is-map($value) { + @each $k, $v in $value { + @include custom-properties.declaration($v); + } + } @else { + @include custom-properties.declaration($value); + } + } +} + +/// Creates a custom property varname for a key. This function will add a key's +/// option's `custom-property-prefix` if it exists. +/// +/// @param {String...} $key - One or more key parts to create a varname for. +/// @return {String} The key's custom property varname. +@function _create-varname($key...) { + $key: combine($key...); + $prefix: map.get($_options, $key, custom-property-prefix); + @if $prefix { + $key: combine($prefix, $key); + } + + @return custom-properties.create-varname($key); +} + +/// Combines one or more key parts into a key. +/// +/// @example - scss +/// $key: combine(body, font-size); +/// // body-font-size +/// +/// @param {String...} $parts - Arbitrary number of string key parts to combine. +/// @return {String} A combined key string. +@function combine($parts...) { + // Allow extra keywords to be passed to other functions without impacting this + // function, which does not expect any keywords. + $unused: meta.keywords($parts); + $key: ''; + @each $part in $parts { + @if $part { + @if $key == '' { + $key: $part; + } @else { + $key: #{$key}-#{$part}; + } + } + } + + @return $key; +} + +@function _is-map($map) { + @return meta.type-of($map) == 'map' and not + custom-properties.is-custom-prop($map); +} diff --git a/components/compat/theme/_map-ext.scss b/components/compat/theme/_map-ext.scss new file mode 100644 index 00000000000..c4b99323476 --- /dev/null +++ b/components/compat/theme/_map-ext.scss @@ -0,0 +1,68 @@ +// +// Copyright 2022 Google LLC +// SPDX-License-Identifier: Apache-2.0 +// + +@use 'sass:list'; +@use 'sass:map'; + +// A collection of extensions to the sass:map module +// https://sass-lang.com/documentation/modules/map + +/// Splits a Map into two separate Maps: one without the provided keys and one +/// exclusively with the provided keys. +/// +/// @example - scss +/// $map: ( +/// focus: blue, +/// focus-within: blue, +/// hover: teal, +/// active: green, +/// ); +/// +/// $pair: split($map, focus, focus-within); +/// // ( +/// // (hover: teal, active: green), +/// // (focus: blue, focus-within: blue) +/// // ); +/// +/// @param {Map} $map - The Map to split. +/// @param {String...} $keys - Keys to split the Map by. +/// @return {List} A List pair with two new Maps: the first with the keys +/// removed and the second exclusively with the keys. +@function split($map, $keys...) { + $map1: (); + $map2: (); + @each $key, $value in $map { + @if list.index($keys, $key) { + $map2: map.set($map2, $key, $value); + } @else { + $map1: map.set($map1, $key, $value); + } + } + + @return ($map1, $map2); +} + +/// Picks provided keys from a Map. +/// +/// @example - scss +/// $map: ( +/// focus: blue, +/// focus-within: blue, +/// hover: teal, +/// active: green, +/// ); +/// +/// pick($map, hover, active); +/// // (hover: teal, active: green), +/// +/// pick($map, (hover, active)...); +/// // (hover: teal, active: green), +/// +/// @param {Map} $map - The Map to pick. +/// @param {String...} $keys - Keys to pick from the Map. +/// @return {List} Map with only the keys provided. +@function pick($map, $keys...) { + @return list.nth(split($map, $keys...), 2); +} diff --git a/components/compat/theme/_replace.scss b/components/compat/theme/_replace.scss new file mode 100644 index 00000000000..7ccc9fbf160 --- /dev/null +++ b/components/compat/theme/_replace.scss @@ -0,0 +1,108 @@ +// +// Copyright 2022 Google LLC +// SPDX-License-Identifier: Apache-2.0 +// + +@use 'sass:list'; +@use 'sass:meta'; +@use 'sass:string'; + +/// Replaces all name instances in the provided string with values from the +/// provided replacement Map whose keys correspond to the name instances. +/// Returns a new string with the replacements applied. +/// +/// @example +/// replace-string('calc(x + y)', (x: 16px, y: 50%)); // 'calc(16px + 50%)' +/// +/// @example +/// $foo: custom-properties.create-var(custom-properties.create(--foo, 8px)); +/// replace-string('calc(foo)', (foo: $foo)); // 'calc(var(--foo, 8px))'; +/// +/// @access private +/// @param {String} $string - The string to make replacements on. +/// @param {Map} $replace-map - A Map of name/value replacements. The keys of +/// the Map are names that will be replaced in the string with the keys' +/// respective values. +/// @return {String} The string with replacements made, if any. +@function replace-string($string, $replace-map) { + @if meta.type-of($replace-map) != 'map' { + @error 'mdc-theme: Invalid replacement #{$replace-map}. Must be a Map.'; + } + + @each $name, $replacement in $replace-map { + // Since the replacement values may contain the name themselves (such as + // a custom property: name "foo" and value "var(--foo)"), gather the indices + // first before replacing. + $indices: (); + $substring: $string; + $prev-index: null; + $index: string.index($substring, $name); + @while $index { + $substring: string.slice($substring, $index + string.length($name)); + + @if $prev-index { + // Add previous index's value to switch from "substring index" to + // absolute string index. Also add length - 1 since slice removes + // the entire word as well as lists being 1 indexed + $index: $index + $prev-index + string.length($name) - 1; + } + + // Use list.join() to "prepend" the index to the start of the list. This + // allows us to iterate "backwards" later. + $indices: list.join($index, $indices); + $prev-index: $index; + $index: string.index($substring, $name); + } + + // Since we "prepended" the indices, the list is sorted backwards, with the + // last index first. Replacing values last index to first index removes the + // need for us to adjust the indices as we replace them. + @each $index in $indices { + $before: string.slice($string, 1, $index - 1); + $after: string.slice($string, $index + string.length($name)); + $string: $before + $replacement + $after; + } + } + + @return $string; +} + +/// Replaces all value instances in the provided list with values from the +/// provided replacement Map whose keys correspond to the name instances. +/// Returns a new list with the replacements applied. +/// +/// @example +/// replace-list(0 value, (value: 16px)); // (0 16px) +/// +/// @see {mixin} replace-string +/// +/// @access private +/// @param {List} $list - The list to make replacements on. +/// @param {Map} $replace-map - A Map of name/value replacements. The keys of +/// the Map are names that will be replaced in the list with the keys' +/// respective values. +/// property value replacements instead of the `var()` declaration. +/// @return {List} A new list with replacements made, if any. +@function replace-list($list, $replace-map) { + $separator: list.separator($list); + $replaced-list: (); + @each $value in $list { + @if meta.type-of($value) == 'string' { + $replaced-list: list.append( + $replaced-list, + replace-string($value, $replace-map), + $separator + ); + } @else if meta.type-of($value) == 'list' { + $replaced-list: list.append( + $replaced-list, + replace-list($value, $replace-map), + $separator + ); + } @else { + $replaced-list: list.append($replaced-list, $value, $separator); + } + } + + @return $replaced-list; +} diff --git a/components/compat/theme/_selector-ext.scss b/components/compat/theme/_selector-ext.scss new file mode 100644 index 00000000000..e51d03d0b81 --- /dev/null +++ b/components/compat/theme/_selector-ext.scss @@ -0,0 +1,522 @@ +// +// Copyright 2022 Google LLC +// SPDX-License-Identifier: Apache-2.0 +// + +@use 'sass:list'; +@use 'sass:math'; +@use 'sass:selector'; +@use 'sass:string'; + +// A collection of extensions to the sass:selector module +// https://sass-lang.com/documentation/modules/selector + +/// Recursively negates and flattens a compound selector. +/// +/// This is useful for IE11 support when appending two selectors when the second +/// selector may have another nested `:not()`, since IE11 does not supported +/// complex selector lists within `:not()`. +/// +/// @example - scss +/// $selector1: '.foo'; +/// $selector2: '.bar:not(.baz)'; +/// +/// #{selector.append($selector1, ':not(#{$selector2})'} { +/// /* Modern browsers */ +/// } +/// +/// #{selector.append($selector1, negate($selector2)} { +/// /* IE11 support */ +/// } +/// +/// @example - css +/// .foo:not(.bar:not(.baz)) { +/// /* Modern browsers */ +/// } +/// +/// .foo:not(.bar), .foo.baz { +/// /* IE11 support */ +/// } +/// +/// @param {String} $compound-selector - A compound selector to negate. +/// @return {List} The negated selector in selector value format. +@function negate($compound-selector) { + $result: null; + @each $simple-selector in selector.simple-selectors($compound-selector) { + $to-append: null; + @if string.index($simple-selector, ':not(') == 1 { + $inside-not: string.slice($simple-selector, 6, -2); + $inside-not-simple-selectors: selector.simple-selectors($inside-not); + $inside-not-result: null; + + @each $inside-not-simple-selector + in selector.simple-selectors($inside-not) + { + @if $inside-not-result == null { + // Skip the first simple selector, which has already been negated by + // removing :not() when parsing $inside-not. + $inside-not-result: selector.parse($inside-not-simple-selector); + } @else { + // Flatten nested :not()s ".foo:not(.bar:not(.baz))" (IE11 support) + @if string.index($inside-not-simple-selector, ':not(') == 1 { + $inside-not-result: list.join( + $inside-not-result, + _negate($inside-not-simple-selector) + ); + } @else { + $inside-not-result: selector.append( + $inside-not-result, + $inside-not-simple-selector + ); + } + } + } + + $result: if( + $result, + list.join($result, $inside-not-result), + $inside-not-result + ); + } @else { + $to-append: string.unquote(':not(#{$simple-selector})'); + $result: if($result, selector.append($result, $to-append), $to-append); + } + } + + @return $result; +} + +/// Identical to `selector.append()`, but adheres strictly to CSS compound +/// selector order. +/// +/// @example - scss +/// .foo::before { +/// &[dir=rtl] { /* Invalid */ } +/// } +/// +/// .foo::before { +/// @include append-strict(&, '[dir=rtl]') { /* Valid */ } +/// } +/// +/// @example - css +/// .foo::before[dir=rtl] { +/// /* Invalid */ +/// } +/// +/// .foo[dir=rtl]::before { +/// /* Valid */ +/// } +/// +/// This is useful for mixins where the parent selector is unknown and the +/// appended selector's position is critical to maintain valid CSS. +/// +/// @param {List} $selectors - One or more selectors to append. +@mixin append-strict($selectors...) { + @at-root { + #{append-strict($selectors...)} { + @content; + } + } +} + +/// Function version of `append-strict()`. Use this instead of the mixin along +/// with `@at-root` when combining the result of `append-strict()` with other +/// selectors. +/// +/// @example - scss +/// .foo::before { +/// // Cannot add a list of other selectors with an @include mixin. +/// // @include append-strict(&, ':hover'), & {} +/// +/// @at-root { +/// // Use @at-root and interpolation to add additional selectors +/// #{append-strict(&, ':hover')}, +/// & { +/// color: inherit; +/// } +/// } +/// } +/// +/// @example - css +/// .foo:hover::before, +/// .foo::before { +/// color: inherit; +/// } +/// +/// @see {mixin} append-strict +/// +/// @param {List} $selectors - One or more selectors to append. +/// @return {List} The appended selectors in selector value format. +@function append-strict($selectors...) { + $selector-lists: (); + @each $selector in $selectors { + $selector-lists: list.append($selector-lists, selector.parse($selector)); + } + + @return _append-strict($selector-lists); +} + +/// Iterates through multiple selector Lists and strictly appends every +/// combination of each selector lists' complex selectors. +/// +/// @see {mixin} _append-strict-complex-selectors +/// +/// @param {List} $selector-lists - A List of selector lists to append. +/// @return {List} A single selector List resulting from appending all the +/// provided selector lists. +@function _append-strict($selector-lists) { + $length: list.length($selector-lists); + // Track the current index of each complex selector (within each selector + // list) that we are creating a combination of. + // + // Selectors: ((1), (2a, 2b), (3)) + // Combinations: (1, 2a, 3), (1, 2b, 3) + // + // Initialize it to the first complex selector index for each selector list. + $current-indices: (); + @for $i from 1 through $length { + $current-indices: list.append($current-indices, 1); + } + + // The final result: a single selector list resulting from appending the + // provided selector lists. + $selector-list-result: (); + + $has-more-combinations: true; + @while $has-more-combinations { + // A combination of complex selectors to add to the selector list result. + $complex-selector-combination: (); + @for $i from 1 through $length { + $selector-list: list.nth($selector-lists, $i); + $current-index: list.nth($current-indices, $i); + $complex-selector: list.nth($selector-list, $current-index); + $complex-selector-combination: _append-strict-complex-selectors( + $complex-selector-combination, + $complex-selector + ); + } + + $selector-list-result: list.append( + $selector-list-result, + $complex-selector-combination, + $separator: comma + ); + + // Increase the index of the last selector list's complex selector to its + // next one. If it reaches the length of the array, reset to 1 and bump the + // next selector list index. + // + // Given selector lists: ((1), (2a, 2b), (3)) + // At indices: ((1), (1), (1)) + // Try bumping: ((1), (1), (2*)) *This index is >length of 1 for the list + // Bump the next: ((1), (2), (1)) + $bump-next-list-index: true; + @for $neg-i from $length * -1 through -1 { + @if $bump-next-list-index { + $i: math.abs($neg-i); + $selector-list: list.nth($selector-lists, $i); + $current-index: list.nth($current-indices, $i); + $next-index: $current-index + 1; + @if $next-index > list.length($selector-list) { + // Reset to start for the list and bump the next list (technically + // previous since we're iterating backwards). + $next-index: 1; + $bump-next-list-index: true; + } @else { + // If we bumped to the next index for this selector list, we can + // "break" the loop and continue to form the next combination. + $bump-next-list-index: false; + } + + // Save the new current index for this selector list. + $current-indices: list.set-nth($current-indices, $i, $next-index); + } + } + + // When the first selector list reaches its length, it will ask to bump the + // next selector list index. There are no more selector lists, which means + // there are no more combinations and the loop may end. + @if $bump-next-list-index { + $has-more-combinations: false; + } + } + + @return $selector-list-result; +} + +/// Appends two complex selectors together, strictly adhering to the CSS +/// `` type definition to avoid forming invalid resulting +/// compound selectors. +/// +/// @param {List} $complex-selector-a - The first List of space-separated +/// compound selectors. +/// @param {List} $complex-selector-a - The second List of space-separated +/// compound selectors. +/// @return {List} A resulting appended complex selector. +@function _append-strict-complex-selectors( + $complex-selector-a, + $complex-selector-b +) { + // If one of the lists is empty, return the other list. + @if list.length($complex-selector-a) < 1 { + @return $complex-selector-b; + } + + @if list.length($complex-selector-b) < 1 { + @return $complex-selector-a; + } + + // The "joining" of A and B happens at the last compound selector of A and the + // first compound selector of B. + // + // Example: + // ".foo .bar" and ".baz :hover" will append as + // ".foo .bar.baz :hover" + $last-compound-selector-a: list.nth( + $complex-selector-a, + list.length($complex-selector-a) + ); + $first-compound-selector-b: list.nth($complex-selector-b, 1); + + // The compound selector CSS joining (".bar" and ".baz") and their sorting + // only needs to happen on the last/first of A/B. + $simple-selectors-a: selector.simple-selectors($last-compound-selector-a); + $simple-selectors-b: selector.simple-selectors($first-compound-selector-b); + $sorted-simple-selectors: _sort-simple-selectors( + list.join($simple-selectors-a, $simple-selectors-b) + ); + + // The result can start to form by setting the last compound selector of A to + // the result of the sorted and joined ".bar.baz"... + $result: list.set-nth( + $complex-selector-a, + list.length($complex-selector-a), + _join-simple-selectors($sorted-simple-selectors) + ); + + // ...then adding the remaining compound selectors (excluding the first one, + // which was already appended) from B. + @if list.length($complex-selector-b) > 1 { + @for $i from 2 through list.length($complex-selector-b) { + $result: list.append(list.nth($complex-selector-b, $i)); + } + } + + @return $result; +} + +/// Combines a List of simple selectors together to form a compound selector. +/// If there are any pseudo class function selectors that should nest their +/// selectors within their parentheses, this function will do so. +/// +/// @param {List} $simple-selectors - A List of simple selectors to join. +/// @return {String} A compound selector. +@function _join-simple-selectors($simple-selectors) { + $compound-selector: ''; + $parens-index: _get-nestable-parens-index($simple-selectors); + @if $parens-index { + // Contains a selector, such as :host() that other selectors must be placed + // within the parentheses of. This selector should be moved to the front of + // the compound selector. + $compound-selector: list.nth($simple-selectors, $parens-index); + @if string.index($compound-selector, '(') != null { + // Already has parens. Remove the final closing parens so that additional + // selectors are placed within the parentheses. + $compound-selector: string.slice( + $compound-selector, + 1, + string.length($compound-selector) - 1 + ); + } @else { + // Otherwise, add an opening parens. + $compound-selector: #{$compound-selector}#{string.unquote('(')}; + } + } + + @for $i from 1 through list.length($simple-selectors) { + @if $i != $parens-index { + // Skip the parens selector that was moved to the front, if any + $simple-selector: list.nth($simple-selectors, $i); + $compound-selector: #{$compound-selector}#{$simple-selector}; + } + } + + @if $parens-index { + // Add the closing parens + $compound-selector: #{$compound-selector}#{string.unquote(')')}; + } + + @return $compound-selector; +} + +/// Searches a List of simple selectors for any pseudo class functions that can +/// and should be nested with other selectors. If one is found, the index is +/// returned. +/// +/// @see {mixin} _can-and-should-nest-pseudo-class +/// +/// @param {List} $simple-selectors - A List of simple selectors to search. +/// @return {Number} The index of the selector with parens to nest, or null if +/// there is none. +@function _get-nestable-parens-index($simple-selectors) { + @for $i from 1 through list.length($simple-selectors) { + $simple-selector: list.nth($simple-selectors, $i); + @if _can-and-should-nest-pseudo-class($simple-selector) { + @return $i; + } + } + + @return null; +} + +/// Checks two things: +/// +/// 1. If a simple selector is a pseudo class function that accepts selectors +/// as its arguments. +/// 2. If this selector is commonly used for nesting within. +/// +/// For example, `:host` satisfies both #1 and #2, but the `:not()` pseudo class +/// is not commonly used in abstract nesting within Sass. +/// +/// @example - scss +/// :host(:hover) { +/// :enabled { +/// // commonly expect :host(:hover:enabled), +/// // since :host(:hover):enabled is invalid CSS +/// } +/// } +/// +/// :not(:hover) { +/// :enabled { +/// // commonly expect :not(:hover):enabled +/// // do not expect :not(:hover:enabled) as the intention +/// } +/// } +/// +/// @param {String} $simple-selector - The simple selector to check. +/// @return {Bool} True if the simple selector is a pseudo class function that +/// should nest additional selectors within its parentheses. +@function _can-and-should-nest-pseudo-class($simple-selector) { + @return string.index($simple-selector, ':host') != null or + string.index($simple-selector, '::slotted') != null; +} + +/// Sorts a List of simple selectors according to the `` CSS +/// type definition. +/// +/// ``` +/// = [ ? * +/// [ * ]* ]! +/// ``` +/// +/// @example - scss +/// $unsorted: (':hover', '::before', 'h1'); +/// #{selector.append(_sort-simple-selectors($unsorted...))} {} +/// +/// @example - css +/// h1::before:hover {} +/// +/// @link https://drafts.csswg.org/selectors/#typedef-compound-selector +/// +/// @param {List} $simple-selectors - A List of simple selectors. +/// @return {List} A List of sorted simple selectors. +@function _sort-simple-selectors($simple-selectors) { + @if list.length($simple-selectors) <= 1 { + @return $simple-selectors; + } + + $type-selectors: (); + $pseudo-element-selectors: (); + $subclass-selectors: (); + + @each $simple-selector in $simple-selectors { + @if _is-type-selector($simple-selector) { + $type-selectors: list.append($type-selectors, $simple-selector); + } @else if _is-pseudo-element-selector($simple-selector) { + $pseudo-element-selectors: list.append( + $pseudo-element-selectors, + $simple-selector + ); + } @else { + $subclass-selectors: list.append($subclass-selectors, $simple-selector); + } + } + + @return list.join( + $type-selectors, + list.join($subclass-selectors, $pseudo-element-selectors) + ); +} + +/// Checks if a simple selector is a ``. +/// +/// @link https://drafts.csswg.org/selectors/#typedef-type-selector +/// +/// @param {String} $simple-selector - The simple selector to check. +/// @return {Bool} True if the selector is a type selector. +@function _is-type-selector($simple-selector) { + @return not _is-subclass-selector($simple-selector); +} + +/// Checks if a simple selector is a ``. +/// +/// @link https://drafts.csswg.org/selectors/#typedef-subclass-selector +/// +/// @param {String} $simple-selector - The simple selector to check. +/// @return {Bool} True if the selector is a subclass selector. +@function _is-subclass-selector($simple-selector) { + @return _is-id-selector($simple-selector) or + _is-class-selector($simple-selector) or + _is-attribute-selector($simple-selector) or + _is-pseudo-class-selector($simple-selector); +} + +/// Checks if a simple selector is an ``. +/// +/// @link https://drafts.csswg.org/selectors/#typedef-id-selector +/// +/// @param {String} $simple-selector - The simple selector to check. +/// @return {Bool} True if the selector is an ID selector. +@function _is-id-selector($simple-selector) { + @return string.index($simple-selector, '#') == 1; +} + +/// Checks if a simple selector is a ``. +/// +/// @link https://drafts.csswg.org/selectors/#typedef-class-selector +/// +/// @param {String} $simple-selector - The simple selector to check. +/// @return {Bool} True if the selector is a class selector. +@function _is-class-selector($simple-selector) { + @return string.index($simple-selector, '.') == 1; +} + +/// Checks if a simple selector is an ``. +/// +/// @link https://drafts.csswg.org/selectors/#typedef-attribute-selector +/// +/// @param {String} $simple-selector - The simple selector to check. +/// @return {Bool} True if the selector is an attribute selector. +@function _is-attribute-selector($simple-selector) { + @return string.index($simple-selector, '[') == 1; +} + +/// Checks if a simple selector is a ``. +/// +/// @link https://drafts.csswg.org/selectors/#typedef-pseudo-class-selector +/// +/// @param {String} $simple-selector - The simple selector to check. +/// @return {Bool} True if the selector is a pseudo class selector. +@function _is-pseudo-class-selector($simple-selector) { + @return string.index($simple-selector, ':') == 1; +} + +/// Checks if a simple selector is a ``. +/// +/// @link https://drafts.csswg.org/selectors/#typedef-pseudo-element-selector +/// +/// @param {String} $simple-selector - The simple selector to check. +/// @return {Bool} True if the selector is a pseudo element selector. +@function _is-pseudo-element-selector($simple-selector) { + @return string.index($simple-selector, '::') == 1; +} diff --git a/components/compat/theme/_shadow-dom.scss b/components/compat/theme/_shadow-dom.scss new file mode 100644 index 00000000000..bb83578cfe6 --- /dev/null +++ b/components/compat/theme/_shadow-dom.scss @@ -0,0 +1,474 @@ +// +// Copyright 2022 Google LLC +// SPDX-License-Identifier: Apache-2.0 +// + +@use 'sass:selector'; +@use 'sass:string'; +@use 'sass:list'; +@use 'sass:map'; +@use 'sass:meta'; + +/// Global variable used to conditionally emit CSS selector fallback +/// declarations in addition to CSS custom property overrides for IE11 support. +/// Use `enable-css-selector-fallback-declarations()` mixin to configure this +/// flag. +/// +/// @example +/// +/// @include shadow-dom.enable-css-selector-fallback-declarations(); +/// @include foo-bar-theme.theme($theme); +/// +/// CSS output => +/// +/// --foo-bar: red; +/// +/// // Fallback declarations for IE11 support +/// .mdc-foo-bar__baz { +/// color: red; +/// } +$css-selector-fallback-declarations: false; + +/// Enables CSS selector fallback declarations for IE11 support by setting +/// global variable `$css-selector-fallback-declarations` to true. Call this +/// mixin before theme mixin call. +/// @param {Boolean} $enable Set to `true` to emit CSS selector fallback +/// declarations. +/// @example +/// @include shadow-dom.enable-css-selector-fallback-declarations() +/// @include foo-bar-theme.theme($theme); +@mixin enable-css-selector-fallback-declarations($enable) { + $css-selector-fallback-declarations: $enable !global; +} + +$_host: ':host'; +$_host-parens: ':host('; +$_end-parens: ')'; + +/// @deprecated - Use selector-ext.append-strict() instead: +/// +/// @example - scss +/// :host([outlined]), :host, :host button { +/// @include selector-ext.append-strict(&, ':hover') { +/// --my-custom-prop: blue; +/// } +/// } +/// +/// @example - css +/// :host([outlined]:hover), :host(:hover), :host button:hover { +/// --my-custom-prop: blue; +/// } +/// +/// @example - scss +/// :host([outlined]), :host, :host button { +/// @at-root { +/// #{selector-ext.append-strict(&, ':hover')}, +/// & { +/// --my-custom-prop: blue; +/// } +/// } +/// } +/// +/// @example - css +/// :host([outlined]:hover), :host(:hover), :host button:hover, +/// :host([outlined]), :host, :host button { +/// --my-custom-prop: blue; +/// } +/// +/// Given one or more selectors, this mixin will fix any invalid `:host` parent +/// nesting by adding parentheses or inserting the nested selector into the +/// parent `:host()` selector's parentheses. The content block provided to +/// this mixin +/// will be projected under the new selectors. +/// +/// @example +/// :host([outlined]), :host, :host button { +/// @include host-aware(selector.append(&, ':hover'), &)) { +/// --my-custom-prop: blue; +/// } +/// } +/// +/// will output (but with selectors on a single line): +/// :host([outlined]:hover), // Appended :hover argument +/// :host(:hover), +/// :host button:hover, +/// :host([outlined]), // Ampersand argument +/// :host, +/// :host button, { +/// --my-custom-prop: blue; +/// }; +/// +/// @param {List} $selector-args - One or more selectors to be fixed for invalid +/// :host syntax. +@mixin host-aware($selector-args...) { + @each $selector in $selector-args { + @if not _is-sass-selector($selector) { + @error 'mdc-theme: host-aware() expected a sass:selector value type but received #{$selector}'; + } + } + + @if not _share-common-parent($selector-args...) { + @error 'mdc-theme: host-aware() requires all selectors to use the parent selector (&)'; + } + + $selectors: _flatten-selectors($selector-args...); + $processed-selectors: (); + + @each $selector in $selectors { + $first-selector: list.nth($selector, 1); + + @if _host-selector-needs-to-be-fixed($first-selector) { + $selector: list.set-nth( + $selector, + 1, + _fix-host-selector($first-selector) + ); + + $processed-selectors: list.append( + $processed-selectors, + $selector, + $separator: comma + ); + } @else { + // Either not in :host, or there are more selectors following the :host + // and nothing needs to be modified. The content can be placed within the + // original selector + $processed-selectors: list.append( + $processed-selectors, + $selector, + $separator: comma + ); + } + } + + @if list.length($processed-selectors) > 0 { + @at-root { + #{$processed-selectors} { + @content; + } + } + } +} + +/// Determines whether a selector needs to be processed. +/// Selectors that need to be processed would include anything of the format +/// `^:host(\(.*\))?.+` e.g. `:host([outlined]):hover` or `:host:hover` but not +/// `:host` or `:host([outlined])` +/// +/// @param {String} $selector - Selector string to be processed +/// @return {Boolean} Whether or not the given selector string needs to be fixed +/// for an invalid :host selector +@function _host-selector-needs-to-be-fixed($selector) { + $host-index: string.index($selector, $_host); + $begins-with-host: $host-index == 1; + + @if not $begins-with-host { + @return false; + } + + $_host-parens-index: _get-last-end-parens-index($selector); + $has-parens: $_host-parens-index != null; + + @if $has-parens { + // e.g. :host(.inside).after -> needs to be fixed + // :host(.inside) -> does not need to be fixed + $end-parens-index: string.index($selector, $_end-parens); + $content-after-parens: string.slice($selector, $end-parens-index + 1); + + $has-content-after-parens: string.length($selector) > $end-parens-index; + + @return $has-content-after-parens; + } @else { + // e.g. :host.after -> needs to be fixed + // :host -> does not need to be fixed + $has-content-after-host: $selector != $_host; + + @return $has-content-after-host; + } +} + +/// Flattens a list of selectors +/// +/// @param {List} $selector-args - A list of selectors to flatten +/// @return {List} Flattened selectors +@function _flatten-selectors($selector-args...) { + $selectors: (); + @each $selector-list in $selector-args { + $selectors: list.join($selectors, $selector-list); + } + + @return $selectors; +} + +/// Fixes an invalid `:host` selector of the format `^:host(\(.*\))?.+` to +/// `:host(.+)` +/// @example +/// @debug _fix-host-selector(':host:hover') // :host(:hover) +/// @debug _fix-host-selector(':host([outlined]):hover) // :host([outlined]:hover) +/// +/// @param {String} $selector - Selector string to be fixed that follows the +/// following format: `^:host(\(.*\))?.+` +/// @return {String} Fixed host selector. +@function _fix-host-selector($selector) { + $_host-parens-index: string.index($selector, $_host-parens); + $has-parens: $_host-parens-index != null; + $new-host-inside: ''; + + @if $has-parens { + // e.g. :host(.inside).after -> :host(.inside.after) + $end-parens-index: _get-last-end-parens-index($selector); + $inside-host-parens: string.slice( + $selector, + string.length($_host-parens) + 1, + $end-parens-index - 1 + ); + $after-host-parens: string.slice($selector, $end-parens-index + 1); + + $new-host-inside: $inside-host-parens + $after-host-parens; + } @else { + // e.g. :host.after -> :host(.after) + $new-host-inside: string.slice($selector, string.length($_host) + 1); + } + + @return ':host(#{$new-host-inside})'; +} + +/// Returns the index of the final occurrence of the end-parenthesis in the +/// given string or null if there is none. +/// +/// @param {String} $string - The string to be searched +/// @return {null|Number} +@function _get-last-end-parens-index($string) { + $index: string.length($string); + + @while $index > 0 { + $char: string.slice($string, $index, $index); + @if $char == $_end-parens { + @return $index; + } + + $index: $index - 1; + } + + @return null; +} + +/// Returns true if the provided List of Sass selectors share a common parent +/// selector. This function ensures that the parent selector (`&`) is used with +/// `host-aware()`. +/// +/// @example +/// _share-common-parent( +/// ('.foo:hover'), ('.foo' '.bar'), ('.baz' '.foo') +/// ); // true +/// +/// _share-common-parent( +/// ('.foo:hover'), ('.foo' '.bar'), ('.baz' '.bar') +/// ); // false +/// +/// The purpose of this function is to make sure that a group of selectors do +/// not violate Sass nesting rules. Due to the dynamic nature of `host-aware()`, +/// it's possible to provide invalid selector combinations. +/// +/// @example +/// // Valid native nesting +/// :host { +/// &:hover, +/// .foo, +/// .bar & { +/// color: blue; +/// } +/// } +/// // Valid host-aware() nesting +/// :host { +/// @include host-aware( +/// selector.append(&, ':hover'), +/// selector.nest(&, '.foo'), +/// selector.nest('.bar', &), +/// ) { +/// color: blue; +/// } +/// } +/// // Output +/// :host(:hover), +/// :host .foo, +/// .bar :host { +/// color: blue; +/// } +/// +/// // Invalid use of host-aware() +/// :host { +/// @include host-aware( +/// selector.append(&, ':hover'), +/// selector.parse('.foo') // Does not share a common parent via `&` +/// ) { +/// color: blue; +/// } +/// } +/// // Invalid output: no way to write this natively without using @at-root +/// :host(:hover), +/// .foo { +/// color: blue; +/// } +/// +/// @param {Arglist} $selector-lists - An argument list of Sass selectors. +/// @return true if the selectors share a common parent selector, or false +/// if not. +@function _share-common-parent($selector-lists...) { + // To validate, this function will extract the simple selectors from each + // complex selector and compare them to each other. Every complex selector + // should share at least one common simple parent selector. + // + // We do this by keeping track of each simple selector and if they're present + // within a complex selector. At the end of checking all the selectors, at + // least one of simple selectors should have been seen for each one of the + // complex selectors. + // + // Each selector list index needs to track its own selector count Map. This is + // because each comma-separated list has its own root parent selector that + // we're looking for: + // .foo, + // .bar { + // &:hover, + // .baz & { ... } + // } + // ('.foo:hover', '.bar:hover'), ('.baz' '.foo', '.baz' '.bar') + // + // In the first index of each selector list, we're looking for the parent + // ".foo". In the second index we're looking for the parent ".bar". + $selector-counts-by-index: (); + $expected-counts-by-index: (); + @each $selector-list in $selector-lists { + @each $complex-selector in $selector-list { + $selector-list-index: list.index($selector-list, $complex-selector); + $selector-count-map: map.get( + $selector-counts-by-index, + $selector-list-index + ); + @if not $selector-count-map { + $selector-count-map: (); + } + + $expected-count: map.get($expected-counts-by-index, $selector-list-index); + @if not $expected-count { + $expected-count: 0; + } + + $simple-selectors-set: (); + @each $selector in $complex-selector { + @each $simple-selector in selector.simple-selectors($selector) { + // Don't use list.join() because there may be duplicate selectors + // within the complex selector. We want to treat $simple-selectors-set + // like a Set where there are no duplicate values so that we don't + // mess up our count by counting one simple selector too many times + // for a single complex selector. + @if not list.index($simple-selectors-set, $simple-selector) { + $simple-selectors-set: list.append( + $simple-selectors-set, + $simple-selector + ); + } + } + } + + // Now that we have a "Set" of simple selectors for this complex + // selector, we can go through each one and update the selector count Map. + @each $simple-selector in $simple-selectors-set { + $count: map.get($selector-count-map, $simple-selector); + @if $count { + $count: $count + 1; + } @else { + $count: 1; + } + + $selector-count-map: map.merge( + $selector-count-map, + ( + $simple-selector: $count, + ) + ); + } + + $selector-counts-by-index: map.merge( + $selector-counts-by-index, + ( + $selector-list-index: $selector-count-map, + ) + ); + $expected-counts-by-index: map.merge( + $expected-counts-by-index, + ( + $selector-list-index: $expected-count + 1, + ) + ); + } + } + + @each $index, $selector-count-map in $selector-counts-by-index { + // If one of the selectors was seen the expected number of times, then we + // can reasonably assume that each selector shares a common parent. + // Verify for each index if there are multiple parents. + $found-parent: false; + @each $selector, $count in $selector-count-map { + $expected-count: map.get($expected-counts-by-index, $index); + @if $count == $expected-count { + $found-parent: true; + } + } + + @if not $found-parent { + @return false; + } + } + + // A common parent was found for each selector, or there were no selectors + // provided and we did not enter any for loops. + @return true; +} + +/// Returns true if the value is a Sass selector type. +/// +/// Selector types are a 2D List: a comma-separated list (the selector list) +/// that contains space-separated lists (the complex selectors) that contain +/// unquoted strings (the compound selectors). +/// @link https://sass-lang.com/documentation/modules/selector +/// +/// @example +/// .foo, .bar button:hover { } +/// $type: ((unquote('.foo')), (unquote('.bar') unquote('button:hover')),); +/// +/// @param {*} $selector-list - A value to check. +/// @return {Boolean} true if the value is a Sass selector, or false if not. +@function _is-sass-selector($selector-list) { + // For the purposes of these utility functions, we don't care if the lists + // have the correct separated or if the strings are unquoted. All that + // matters is that the type is a 2D array and the values are strings to + // ensure "close enough" that the selector was generated by Sass. + // + // This function is primarily a safe-guard against an accidental string + // slipping in and forgetting to use a selector.append() which would cause a + // hard-to-debug problem. + @if meta.type-of($selector-list) != 'list' { + @return false; + } + + // First level is the selector list: what's separated by commas + // e.g. ".foo, .bar" + @each $complex-selector in $selector-list { + // Second level is the complex selector: what's separated by spaces + // e.g. ".foo .bar" + @if meta.type-of($complex-selector) != 'list' { + @return false; + } + + // Third level is the compound selector: the actual string + // e.g. ".foo" + @each $selector in $complex-selector { + @if meta.type-of($selector) != 'string' { + @return false; + } + } + } + + @return true; +} diff --git a/components/compat/theme/_state.scss b/components/compat/theme/_state.scss new file mode 100644 index 00000000000..7e7ca5b08fc --- /dev/null +++ b/components/compat/theme/_state.scss @@ -0,0 +1,752 @@ +// +// Copyright 2022 Google LLC +// SPDX-License-Identifier: Apache-2.0 +// + +@use 'sass:list'; +@use 'sass:map'; +@use 'sass:meta'; +@use 'sass:selector'; +@use 'sass:string'; +@use './custom-properties'; +@use './selector-ext'; + +/// List of all valid states. When adding new state functions, add the name of +/// the state to this List. +$_valid-states: ( + enabled, + disabled, + dragged, + error, + focus, + hover, + opened, + pressed, + selected, + unselected +); + +/// Retrieves the default state from the provided parameter. The parameter may +/// be the state's default value or a state Map. A state Map has individual keys +/// describing each state's value. +/// +/// @example +/// get-default-state(blue); // blue +/// get-default-state((default: blue)); // blue +/// get-default-state((hover: red)); // null +/// +/// @param {*} $default-or-map - The state's default value or a state Map. +/// @return The default state if present, or null. +@function get-default-state($default-or-map) { + $state: _get-state($default-or-map, default); + @if $state == null and not _is-state-map($default-or-map) { + @return $default-or-map; + } + + @return $state; +} + +/// Retrieves the enabled state from the provided parameter. The parameter may +/// be the state's default value or a state Map. A state Map has individual keys +/// describing each state's value. +/// +/// @example +/// get-enabled-state(blue); // blue +/// get-enabled-state((enabled: blue)); // blue +/// get-enabled-state((hover: red)); // null +/// +/// @param {*} $default-or-map - The state's default value or a state Map. +/// @return The enabled state if present, or null. +@function get-enabled-state($default-or-map) { + @return _get-state($default-or-map, enabled); +} + +/// Retrieves the disabled state from the provided parameter. The parameter may +/// be the state's default value or a state Map. A state Map has individual keys +/// describing each state's value. +/// +/// @example +/// get-disabled-state(blue); // null +/// get-disabled-state((disabled: red)); // red +/// get-disabled-state((default: blue)); // null +/// +/// @param {*} $default-or-map - The state's default value or a state Map. +/// @return The disabled state if present, or null. +@function get-disabled-state($default-or-map) { + @return _get-state($default-or-map, disabled); +} + +/// Retrieves the dragged state from the provided parameter. The parameter may +/// be the state's default value or a state Map. A state Map has individual keys +/// describing each state's value. +/// +/// @example +/// get-dragged-state(blue); // null +/// get-dragged-state((dragged: red)); // red +/// get-dragged-state((default: blue)); // null +/// +/// @param {*} $default-or-map - The state's default value or a state Map. +/// @return The dragged state if present, or null. +@function get-dragged-state($default-or-map) { + @return _get-state($default-or-map, dragged); +} + +/// Retrieves the error state from the provided parameter. The parameter may +/// be the state's default value or a state Map. A state Map has individual keys +/// describing each state's value. +/// +/// @example +/// get-error-state(blue); // null +/// get-error-state((error: red)); // red +/// get-error-state((default: blue)); // null +/// +/// @param {*} $default-or-map - The state's default value or a state Map. +/// @return The error state if present, or null. +@function get-error-state($default-or-map) { + @return _get-state($default-or-map, error); +} + +/// Retrieves the focus state from the provided parameter. The parameter may +/// be the state's default value or a state Map. A state Map has individual keys +/// describing each state's value. +/// +/// @example +/// get-focus-state(blue); // null +/// get-focus-state((focus: red)); // red +/// get-focus-state((default: blue)); // null +/// +/// @param {*} $default-or-map - The state's default value or a state Map. +/// @return The focus state if present, or null. +@function get-focus-state($default-or-map) { + @return _get-state($default-or-map, focus); +} + +/// Retrieves the hover state from the provided parameter. The parameter may +/// be the state's default value or a state Map. A state Map has individual keys +/// describing each state's value. +/// +/// @example +/// get-hover-state(blue); // null +/// get-hover-state((hover: red)); // red +/// get-hover-state((default: blue)); // null +/// +/// @param {*} $default-or-map - The state's default value or a state Map. +/// @return The hover state if present, or null. +@function get-hover-state($default-or-map) { + @return _get-state($default-or-map, hover); +} + +/// Retrieves the opened state from the provided parameter. The parameter may +/// be the state's default value or a state Map. A state Map has individual keys +/// describing each state's value. +/// +/// @example +/// get-opened-state(blue); // null +/// get-opened-state((opened: red)); // red +/// get-opened-state((default: blue)); // null +/// +/// @param {*} $default-or-map - The state's default value or a state Map. +/// @return The opened state if present, or null. +@function get-opened-state($default-or-map) { + @return _get-state($default-or-map, opened); +} + +/// Retrieves the pressed state from the provided parameter. The parameter may +/// be the state's default value or a state Map. A state Map has individual keys +/// describing each state's value. +/// +/// @example +/// get-pressed-state(blue); // null +/// get-pressed-state((pressed: red)); // red +/// get-pressed-state((default: blue)); // null +/// +/// @param {*} $default-or-map - The state's default value or a state Map. +/// @return The pressed state if present, or null. +@function get-pressed-state($default-or-map) { + @return _get-state($default-or-map, pressed); +} + +/// Retrieves the selected state from the provided parameter. The parameter may +/// be the state's default value or a state Map. A state Map has individual keys +/// describing each state's value. +/// +/// @example +/// get-selected-state(blue); // null +/// get-selected-state((selected: red)); // red +/// get-selected-state((default: blue)); // null +/// +/// @param {*} $default-or-map - The state's default value or a state Map. +/// @return The selected state if present, or null. +@function get-selected-state($default-or-map) { + @return _get-state($default-or-map, selected); +} + +/// Retrieves the unselected state from the provided parameter. The parameter +/// may be the state's default value or a state Map. A state Map has individual +/// key describing each state's value. +/// +/// @example +/// get-unselected-state(blue); // null +/// get-unselected-state((unselected: red)); // red +/// get-unselected-state((default: blue)); // null +/// +/// @param {*} $default-or-map - The state's default value or a state Map. +/// @return The unselected state if present, or null. +@function get-unselected-state($default-or-map) { + @return _get-state($default-or-map, unselected); +} + +@function _get-state($default-or-map, $state) { + @if _is-state-map($default-or-map) { + @return map.get($default-or-map, $state); + } @else { + @return null; + } +} + +@function _is-state-map($default-or-map) { + @return meta.type-of($default-or-map) == 'map' and not + custom-properties.is-custom-prop($default-or-map); +} + +/// Appends the default state selector to the current parent. +/// +/// @example - scss +/// .mdc-foo { +/// @include default($selectors) { +/// color: teal; +/// } +/// } +/// +/// @example - css +/// .mdc-foo:enabled { +/// color: teal; +/// } +/// +/// @param {Map} $selectors A Map whose keys are states and values are string +/// selectors. +@mixin default($selectors) { + @include enabled($selectors) { + @content; + } +} + +/// Appends the enabled state selector to the current parent. +/// +/// @example - scss +/// .mdc-foo { +/// @include enabled($selectors) { +/// color: teal; +/// } +/// } +/// +/// @example - css +/// .mdc-foo:enabled { +/// color: teal; +/// } +/// +/// @param {Map} $selectors A Map whose keys are states and values are string +/// selectors. +@mixin enabled($selectors) { + @include _selector($selectors, enabled) { + @content; + } +} + +/// Appends the disabled state selector to the current parent. +/// +/// @example - scss +/// .mdc-foo { +/// @include disabled($selectors) { +/// color: teal; +/// } +/// } +/// +/// @example - css +/// .mdc-foo:disabled { +/// color: teal; +/// } +/// +/// @param {Map} $selectors A Map whose keys are states and values are string +/// selectors. +@mixin disabled($selectors) { + @include _selector($selectors, disabled) { + @content; + } +} + +/// Appends the dragged state selector to the current parent. +/// +/// @example - scss +/// .mdc-foo { +/// @include dragged($selectors) { +/// color: teal; +/// } +/// } +/// +/// @example - css +/// .mdc-foo:enabled.mdc-foo--dragged { +/// color: teal; +/// } +/// +/// @param {Map} $selectors A Map whose keys are states and values are string +/// selectors. +@mixin dragged($selectors) { + @include enabled($selectors) { + @include _selector($selectors, dragged) { + @content; + } + } +} + +/// Appends the error state selector to the current parent. +/// +/// @example - scss +/// .mdc-foo { +/// @include error($selectors) { +/// color: teal; +/// } +/// } +/// +/// @example - css +/// .mdc-foo:invalid { +/// color: teal; +/// } +/// +/// @param {Map} $selectors A Map whose keys are states and values are string +/// selectors. +@mixin error($selectors) { + @include _selector($selectors, error) { + @content; + } +} + +/// Appends the focus state selector to the current parent. +/// +/// @example - scss +/// .mdc-foo { +/// @include focus($selectors) { +/// color: teal; +/// } +/// } +/// +/// @example - css +/// .mdc-foo:enabled:focus:not(:active) { +/// color: teal; +/// } +/// +/// @param {Map} $selectors A Map whose keys are states and values are string +/// selectors. +@mixin focus($selectors) { + @include enabled($selectors) { + @include _selector($selectors, focus) { + @content; + } + } +} + +/// Appends the hover state selector to the current parent. +/// +/// @example - scss +/// .mdc-foo { +/// @include hover($selectors) { +/// color: teal; +/// } +/// } +/// +/// @example - css +/// .mdc-foo:enabled:hover:not(:focus):not(:active) { +/// color: teal; +/// } +/// +/// @param {Map} $selectors A Map whose keys are states and values are string +/// selectors. +@mixin hover($selectors) { + @include enabled($selectors) { + @include _selector($selectors, hover) { + @content; + } + } +} + +/// Appends the opened state selector to the current parent. +/// +/// @example - scss +/// .mdc-foo { +/// @include opened($selectors) { +/// color: teal; +/// } +/// } +/// +/// @example - css +/// .mdc-foo.mdc-foo--opened { +/// color: teal; +/// } +/// +/// @param {Map} $selectors A Map whose keys are states and values are string +/// selectors. +@mixin opened($selectors) { + @include _selector($selectors, opened) { + @content; + } +} + +/// Appends the pressed state selector to the current parent. +/// +/// @example - scss +/// .mdc-foo { +/// @include pressed($selectors) { +/// color: teal; +/// } +/// } +/// +/// @example - css +/// .mdc-foo:enabled:active { +/// color: teal; +/// } +/// +/// @param {Map} $selectors A Map whose keys are states and values are string +/// selectors. +@mixin pressed($selectors) { + @include enabled($selectors) { + @include _selector($selectors, pressed) { + @content; + } + } +} + +/// Appends the selected state selector to the current parent. +/// +/// @example - scss +/// .mdc-foo { +/// @include selected($selectors) { +/// color: teal; +/// } +/// } +/// +/// @example - css +/// .mdc-foo.mdc-foo--selected { +/// color: teal; +/// } +/// +/// @param {Map} $selectors A Map whose keys are states and values are string +/// selectors. +@mixin selected($selectors) { + @include _selector($selectors, selected) { + @content; + } +} + +/// Appends the unselected state selector to the current parent. +/// +/// @example - scss +/// .mdc-foo { +/// @include unselected($selectors) { +/// color: teal; +/// } +/// } +/// +/// @example - css +/// .mdc-foo.mdc-foo--unselected { +/// color: teal; +/// } +/// +/// @param {Map} $selectors A Map whose keys are states and values are string +/// selectors. +@mixin unselected($selectors) { + @include _selector($selectors, unselected) { + @content; + } +} + +/// Creates and returns a Map of independent selectors from a Map of simple +/// selectors. +/// +/// This function ensures that each selector is independent given all possible +/// states provided. An "independent" selector does not rely on CSS override +/// order or specificity. +/// +/// @example - scss +/// $selectors: state.create-selectors( +/// ( +/// disabled: ':disabled', +/// hover: ':hover', +/// focus: ':focus', +/// pressed: ':active', +/// ) +/// ); +/// // ( +/// // enabled: ':enabled', +/// // disabled: ':disabled', +/// // hover: ':hover:not(:focus):not(:active)', +/// // focus: ':focus:not(:active)', +/// // pressed: ':active', +/// // ) +/// +/// @see {function} _create-independent-selector +/// +/// @param {Map} $selectors A Map whose keys are states and values are string +/// selectors. +/// @return {Map} A Map of state selectors. +@function _create-selectors($selectors) { + $new-selectors: (); + @each $state, $selector in $selectors { + @if not list.index($_valid-states, $state) { + @error 'Unsupported state #{$state}, must be one of #{$_valid-states}.'; + } + + // Check if there are any dependent states for this state that we need to + // add to the selector with :not() + $dependent-states: (); + @each $group in $_dependent-state-groups { + $index: list.index($group, $state); + @if $index and $index < list.length($group) { + // State is part of this group. Add any remaining selectors as + // dependents, only if they haven't already been added (the state may be + // part of multiple groups with shared state dependents, like + // :hover:focus:active and :link:visited:hover:active) + @for $i from $index + 1 through list.length($group) { + $dependent: list.nth($group, $i); + @if not list.index($dependent-states, $dependent) { + $dependent-states: list.append($dependent-states, $dependent); + } + } + } + } + + $dependents: (); + @each $dependent-state in $dependent-states { + $dependent: map.get($selectors, $dependent-state); + @if $dependent and not list.index($_independent-states, $dependent-state) + { + $dependents: list.append($dependents, $dependent); + } + } + + // Make the selector independent (if any dependents were found) + $selector: _create-independent-selector($selector, $dependents...); + $new-selectors: map.set($new-selectors, $state, $selector); + } + + $new-selectors: _add-default-enabled-selector($new-selectors); + + @return $new-selectors; +} + +/// Adds a default selector for the "enabled" state if one does not exist and if +/// it is possible to infer one from the provided Map of selectors. +/// +/// @example - scss +/// _add-default-enabled-selector((disabled: ':disabled')); +/// // ( +/// // disabled: ':disabled', +/// // enabled: ':enabled', +/// // ) +/// +/// _add-default-enabled-selector((disabled: '.mdc-foo--disabled')); +/// // ( +/// // disabled: '.mdc-foo--disabled', +/// // enabled: ':not(.mdc-foo--disabled)', +/// // ) +/// +/// @param {Map} $selectors - A Map of state selectors. +/// @return {Map} The same Map of selectors, potentially with an additional +/// "enabled" key with the enabled selector value. +@function _add-default-enabled-selector($selectors) { + $enabled: map.get($selectors, enabled); + $disabled: map.get($selectors, disabled); + @if $disabled == ':disabled' { + @if $enabled and $enabled != ':enabled' { + // TODO: Clean up instances of :not(:disabled) + // Enabled selector was provided, but it was not :enabled. These + // can be cleaned up, but don't change them right now. + @warn 'Use :enabled instead of #{$enabled} when using :disabled.'; + @return $selectors; + } + + // For :disabled, use :enabled instead of the :not() variant + @return map.set($selectors, enabled, ':enabled'); + } + + @if $disabled and not $enabled { + @return map.set($selectors, enabled, selector-ext.negate($disabled)); + } + + @return $selectors; +} + +/// A Map of override selectors. This can be used to temporarily change and +/// configure state selectors. +/// @type {Map} +/// @see {mixin} override-selectors +$_override-selectors: (); + +/// Override the current selectors provided to a state mixin for the provided +/// content. +/// +/// @example - scss +/// // Change theme so that focus styles only show during keyboard navigation +/// @include state.override-selectors((focus: ':focus-within')) { +/// @include foo.theme($theme); +/// } +/// +/// @param {Map} $selectors A Map whose keys are states and values are string +/// selectors. +/// @content The styles to override state selectors for. +@mixin override-selectors($selectors) { + $reset: $_override-selectors; + $_override-selectors: $selectors !global; + @content; + $_override-selectors: $reset !global; +} + +$_independent-states: (); + +/// Indicates that for the given content of state mixins, the provided states +/// are on their own independent elements and that they should ignore typical +/// dependent groupings, such as `:hover`, `:focus`, and `:active`. +/// +/// This mixin is useful when multiple states within a typical dependency group +/// need to be visible at the same time (such as `:focus` and `:active`). To +/// achieve this, the states must be on their own independent elements (such as +/// separate `::before` and `::after` pseudo elements). +/// +/// @example - scss +/// .broken-ripple { +/// @include state.hover { +/// &::before { opacity: 0.1; } +/// } +/// @include state.focus { +/// &::before { opacity: 0.2; } +/// } +/// @include state.pressed { +/// &::after { opacity: 0.3; } +/// } +/// } +/// +/// .fixed-ripple { +/// @include state.independent-elements(pressed) { +/// @include state.hover { +/// &::before { opacity: 0.1; } +/// } +/// @include state.focus { +/// &::before { opacity: 0.2; } +/// } +/// @include state.pressed { +/// &::before { opacity: 0.3; } +/// } +/// } +/// } +/// +/// @example - css +/// .broken-ripple:hover:not(:focus):not(:active)::before { +/// opacity: 0.1; +/// } +/// .broken-ripple:focus:not(:active)::before { +/// /* Focus styles will not be visible due to :not(:active)!! */ +/// opacity: 0.2; +/// } +/// .broken-ripple:active::after { +/// opacity: 0.3; +/// } +/// +/// .fixed-ripple:hover:not(:focus)::before { +/// opacity: 0.1; +/// } +/// .fixed-ripple:focus::before { +/// /* Both focus and pressed styles are visible during press. Only hover +/// and focus need to be independent of each other since they share an +/// element. */ +/// opacity: 0.2; +/// } +/// .fixed-ripple:active::after { +/// opacity: 0.3; +/// } +/// +/// @param {String...} $states - One or more states that should be considered +/// independent and on its own element. +/// @content Two or more state mixins that are part of a dependency group +/// involving the provided independent states. +@mixin independent-elements($states...) { + $reset: $_independent-states; + $_independent-states: $states !global; + @content; + $_independent-states: $reset !global; +} + +/// A List of state groups that are dependent on each other for CSS override +/// order. These are used to determine which state selectors are needed for +/// `_create-independent-selector()`. +// Note: Sass syntax does not allow declaring nested Lists; an empty second List +// placeholder is added for the correct data structure. +$_dependent-state-groups: ((hover, focus, pressed), ()); + +/// Creates a selector that will be independent based on the other selectors +/// that are dependents of it. +/// +/// Selector dependencies are selector groups that must follow a certain order +/// for CSS overrides. For example: `:hover`, `:focus`, `:active` or `:link`, +/// `:visited`, `:hover`, `:active`. +/// +/// Selectors at the start of a group are dependencies of selectors at the end +/// of a group. +/// +/// @example - scss +/// #{_create-independent-selector(':hover', ':focus', ':active')} { +/// color: teal; +/// } +/// +/// #{_create-independent-selector(':focus', ':active')} { +/// color: magenta; +/// } +/// +/// @example - css +/// :hover:not(:focus):not(:active) { +/// color: teal; +/// } +/// +/// :focus:not(:active) { +/// color: magenta; +/// } +/// +/// The returned selector is considered "independent" and does not rely on CSS +/// override order or specificity within its group. In other words, "hover" +/// styles can be customized after "focus" styles without hiding default focus +/// styles. +/// +/// @example - css +/// /* Default focus styles */ +/// :focus:not(:active) { color: magenta; } +/// +/// /* New hover styles, does not prevent focus styles from being visible */ +/// :hover:not(:focus):not(:active) { color: orange; } +/// +/// @param {String} $selector - The main selector to target. +/// @param {String...} $dependents - Additional group dependents of the main +/// selector. They will be added as `:not()` selectors. +/// @return {List} A new independent selector in selector value format. +@function _create-independent-selector($selector, $dependents...) { + @each $dependent in $dependents { + @if $dependent { + $selector: selector-ext.append-strict( + $selector, + selector-ext.negate($dependent) + ); + } + } + + @return $selector; +} + +@mixin _selector($selectors, $state) { + $selectors: _create-selectors(map.merge($selectors, $_override-selectors)); + @if not map.has-key($selectors, $state) { + @error 'Missing #{$state} from #{$selectors}'; + } + + @at-root { + #{selector-ext.append-strict(&, map.get($selectors, $state))} { + @content; + } + } +} diff --git a/components/compat/theme/_theme-color.scss b/components/compat/theme/_theme-color.scss new file mode 100644 index 00000000000..774faf352ca --- /dev/null +++ b/components/compat/theme/_theme-color.scss @@ -0,0 +1,344 @@ +// +// Copyright 2022 Google LLC +// SPDX-License-Identifier: Apache-2.0 +// + +@use 'sass:color'; +@use 'sass:list'; +@use 'sass:map'; +@use 'sass:math'; +@use 'sass:meta'; +@use 'sass:string'; +@use './custom-properties'; +@use './keys'; + +@function _linear-channel-value($channel-value) { + $normalized-channel-value: math.div($channel-value, 255); + @if $normalized-channel-value < 0.03928 { + @return math.div($normalized-channel-value, 12.92); + } + + @return math.pow(math.div($normalized-channel-value + 0.055, 1.055), 2.4); +} + +// Calculate the luminance for a color. +// See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests +@function luminance($color) { + $red: _linear-channel-value(color.red($color)); + $green: _linear-channel-value(color.green($color)); + $blue: _linear-channel-value(color.blue($color)); + + @return 0.2126 * $red + 0.7152 * $green + 0.0722 * $blue; +} + +// Calculate the contrast ratio between two colors. +// See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests +@function contrast($back, $front) { + $backLum: luminance($back) + 0.05; + $foreLum: luminance($front) + 0.05; + + @return math.div(math.max($backLum, $foreLum), math.min($backLum, $foreLum)); +} + +// Determine whether the color is 'light' or 'dark'. +@function tone($color) { + @if $color == 'dark' or $color == 'light' { + @return $color; + } + + @if meta.type-of($color) != 'color' { + @warn '#{$color} is not a color. Falling back to "dark" tone.'; + @return 'dark'; + } + + $minimumContrast: 3.1; + + $lightContrast: contrast($color, white); + $darkContrast: contrast($color, rgba(black, 0.87)); + + @if ($lightContrast < $minimumContrast) and ($darkContrast > $lightContrast) { + @return 'light'; + } @else { + @return 'dark'; + } +} + +// Determine whether to use dark or light text on top of given color to meet accessibility standards for contrast. +// Returns 'dark' if the given color is light and 'light' if the given color is dark. +@function contrast-tone($color) { + @return if(tone($color) == 'dark', 'light', 'dark'); +} + +/// +/// @param $color Target color in any color format. +/// @return Returns hash in string format that uniquely represents +/// any given color format. Useful for generating unique keyframe names. +/// @example +/// `color-hash(#6200ee)` => "6200ee" +/// `color-hash(rgb(255, 112, 112))` => "ff7070" +/// `color-hash((varname: --my-fancy-color, fallback: teal))` => 'teal' +/// `color-hash((varname: --my-fancy-color, fallback: null))` => '--my-fancy-color' +/// +@function color-hash($color) { + @if custom-properties.is-custom-prop($color) { + $color-value: custom-properties.get-fallback($color); + + @if (custom-properties.is-custom-prop-string($color-value)) { + $varEndIndex: if( + string.index($color-value, ', '), + string.index($color-value, ', ') - 1, + -2 + ); + @return string.slice($color-value, 5, $varEndIndex); + } + + @if (meta.type-of($color-value) == 'color') { + @return _get-hex-string($color-value); + } + + @return custom-properties.get-varname($color); + } + + @if meta.type-of($color) == 'string' { + @return $color; + } + + @return _get-hex-string($color); +} + +@function _get-hex-string($color) { + @return string.slice(color.ie-hex-str($color), 2); // Index starts at 1 +} + +// +// Main theme colors for your brand. +// +// If you're a user customizing your color scheme in SASS, these are probably the only variables you need to change. +// + +$primary: #6200ee !default; // baseline purple, 500 tone +$on-primary: if(contrast-tone($primary) == 'dark', #000, #fff) !default; + +// The $mdc-theme-accent variable is DEPRECATED - it exists purely for backward compatibility. +// The $mdc-theme-secondary* variables should be used for all new projects. +/// @deprecated - use $secondary +$accent: #018786 !default; // baseline teal, 600 tone +$secondary: $accent !default; +$on-secondary: if(contrast-tone($secondary) == 'dark', #000, #fff) !default; +$background: #fff !default; // White + +$surface: #fff !default; +$on-surface: if(contrast-tone($surface) == 'dark', #000, #fff) !default; + +$error: #b00020 !default; +$on-error: if(contrast-tone($error) == 'dark', #000, #fff) !default; + +// +// Text colors according to light vs dark and text type. +// + +$text-colors: ( + dark: ( + primary: rgba(black, 0.87), + secondary: rgba(black, 0.54), + hint: rgba(black, 0.38), + disabled: rgba(black, 0.38), + icon: rgba(black, 0.38), + ), + light: ( + primary: white, + secondary: rgba(white, 0.7), + hint: rgba(white, 0.5), + disabled: rgba(white, 0.5), + icon: rgba(white, 0.5), + ), +) !default; + +$text-emphasis: ( + high: 0.87, + medium: 0.6, + disabled: 0.38, +) !default; + +@function ink-color-for-fill_($text-style, $fill-color) { + $contrast-tone: contrast-tone($fill-color); + + @return map.get(map.get($text-colors, $contrast-tone), $text-style); +} + +// +// Primary text colors for each of the theme colors. +// + +/// @deprecated Use individual variables (`$primary`, `$secondary`). Do not +/// override this Map of variables. +$property-values: ( + primary: $primary, + secondary: $secondary, + background: $background, + surface: $surface, + error: $error, + on-primary: $on-primary, + on-secondary: $on-secondary, + on-surface: $on-surface, + on-error: $on-error, + text-primary-on-background: ink-color-for-fill_(primary, $background), + text-secondary-on-background: ink-color-for-fill_(secondary, $background), + text-hint-on-background: ink-color-for-fill_(hint, $background), + text-disabled-on-background: ink-color-for-fill_(disabled, $background), + text-icon-on-background: ink-color-for-fill_(icon, $background), + text-primary-on-light: ink-color-for-fill_(primary, light), + text-secondary-on-light: ink-color-for-fill_(secondary, light), + text-hint-on-light: ink-color-for-fill_(hint, light), + text-disabled-on-light: ink-color-for-fill_(disabled, light), + text-icon-on-light: ink-color-for-fill_(icon, light), + text-primary-on-dark: ink-color-for-fill_(primary, dark), + text-secondary-on-dark: ink-color-for-fill_(secondary, dark), + text-hint-on-dark: ink-color-for-fill_(hint, dark), + text-disabled-on-dark: ink-color-for-fill_(disabled, dark), + text-icon-on-dark: ink-color-for-fill_(icon, dark), +) !default; + +@include keys.set-values( + $property-values, + $options: (custom-property-prefix: theme) +); + +// A copy of the property values Map that is used to detect compile-time changes +// for Angular support. +$_property-values-copy: $property-values; + +/// Checks if the global $mdc-theme-property-values was dynamically changed at +/// compile time. Typically, $property-values is configured once, but a Sass +/// hack allows the variable to be changed multiple times and effectively +/// support dynamic values. +/// +/// Angular uses this in their dynamic theming. This function checks if this +/// scenario has occurred and returns the current global value that should be +/// used instead of the key store value. +/// +/// @deprecated The function should not be used externally. It will be removed +/// when $mdc-theme-property-values is fully deprecated and removed. +@function deprecated-get-global-theme-key-value-if-changed($key) { + // Determine if we need to use a compile-time updated value to support + // Angular. + $current-global-value: map.get($property-values, $key); + $configured-global-value: map.get($_property-values-copy, $key); + @if $current-global-value != $configured-global-value { + // $mdc-theme-property-values was changed at compile time. Return the new + // compile-time value. + @return (value: $current-global-value, changed: true); + } + + @return (changed: false); +} + +// @deprecated use theme.property(). If you need to ensure the value is not a +// custom property, use custom-properties.is-custom-prop() to check if the value +// is a custom prop, then custom-properties.get-fallback() to get its value. +// If `$style` is a color (a literal color value, `currentColor`, or a CSS custom property), it is returned verbatim. +// Otherwise, `$style` is treated as a theme property name, and the corresponding value from +// `$mdc-theme-property-values` is returned. If this also fails, an error is thrown. +// +// This is mainly useful in situations where `mdc-theme-prop` cannot be used directly (e.g., `box-shadow`). +// +// Examples: +// +// 1. mdc-theme-prop-value(primary) => "#6200ee" +// 2. mdc-theme-prop-value(blue) => 'blue' +// +// NOTE: This function must be defined in _variables.scss instead of _functions.scss to avoid circular imports. +@function prop-value($style) { + @if custom-properties.is-custom-prop($style) { + @return custom-properties.get-fallback($style); + } + + @if is-valid-theme-prop-value_($style) { + @return $style; + } + + @if is-theme-key($style) { + // Determine if we need to use a compile-time updated value to support + // Angular. + $result: deprecated-get-global-theme-key-value-if-changed($style); + @if map.get($result, changed) { + @return map.get($result, value); + } + } + + @return keys.resolve($style); +} + +// NOTE: This function must be defined in _variables.scss instead of _functions.scss to avoid circular imports. +@function accessible-ink-color($fill-color, $text-style: primary) { + $fill-color-value: prop-value($fill-color); + $color-map-for-tone: map.get($text-colors, contrast-tone($fill-color-value)); + + @if not map.has-key($color-map-for-tone, $text-style) { + @error "Invalid $text-style: '#{$text-style}'. Choose one of: #{map.keys($color-map-for-tone)}"; + } + + @return map.get($color-map-for-tone, $text-style); +} + +// NOTE: This function is depended upon by mdc-theme-prop-value (above) and thus must be defined in this file. +@function is-valid-theme-prop-value_($style) { + @return meta.type-of($style) == 'color' or $style == 'currentColor' or + str_slice($style, 1, 4) == 'var(' or $style == 'inherit' or $style == + 'transparent' or + // NOTE: `GrayText` is deprecated, but is the only feasible way to convey the + // correct high-contrast mode colors in alignment with Windows system colors. + $style == 'GrayText'; +} + +@function text-emphasis($emphasis) { + @return map.get($text-emphasis, $emphasis); +} + +@function is-theme-key($style) { + @return map.has-key($property-values, $style); +} + +@function get-theme-keys() { + @return map.keys($property-values); +} + +/// +/// @param {Color|String} Color property key name (i.e., `primary`, `secondary`, +/// etc). +/// @return Returns custom property map containing CSS custom property and +/// fallback value (i.e., (varname: ..., fallback: ...). Returns color if +/// valid color value is provided. Throws error otherwise. +/// @examples +/// 1. get-custom-property(primary) +/// => (varname: --mdc-theme-primary, fallback: #6200ee) +/// +/// 2. get-custom-property(#fff) +/// => #fff +/// +@function get-custom-property($color) { + $is-tokens-custom-prop: meta.type-of($color) == 'string' and + string.index($color, '--') != null; + @if custom-properties.is-custom-prop($color) or + $is-tokens-custom-prop or + is-valid-theme-prop-value_($color) + { + @return $color; + } @else if is-theme-key($color) { + $custom-prop: keys.create-custom-property($color); + + // Determine if we need to use a compile-time updated value to support + // Angular. + $result: deprecated-get-global-theme-key-value-if-changed($color); + @if map.get($result, changed) { + $custom-prop: custom-properties.set-fallback( + $custom-prop, + map.get($result, value) + ); + } + + @return $custom-prop; + } @else { + @error "Invalid theme property: '#{$color}'. Choose one of: #{get-theme-keys()}"; + } +} diff --git a/components/compat/theme/_theme.scss b/components/compat/theme/_theme.scss new file mode 100644 index 00000000000..910e7a81cb7 --- /dev/null +++ b/components/compat/theme/_theme.scss @@ -0,0 +1,334 @@ +// +// Copyright 2022 Google LLC +// SPDX-License-Identifier: Apache-2.0 +// + +@use 'sass:list'; +@use 'sass:map'; +@use 'sass:meta'; +@use './css'; +@use './custom-properties'; +@use './gss'; +@use './keys'; +@use './replace'; +@use './theme-color'; + +@mixin core-styles($query: feature-targeting.all()) { + $feat-color: feature-targeting.create-target($query, color); + + :root { + @include feature-targeting.targets($feat-color) { + @each $style in theme-color.get-theme-keys() { + @include custom-properties.declaration( + keys.create-custom-property($style) + ); + } + } + } + + @each $style in theme-color.get-theme-keys() { + @if $style != 'background' and $style != 'surface' { + .mdc-theme--#{$style} { + @include feature-targeting.targets($feat-color) { + @include property(color, $style, $important: true); + } + } + } @else { + .mdc-theme--#{$style} { + @include feature-targeting.targets($feat-color) { + @include property(background-color, $style); + } + } + } + } + + // CSS rules for using primary and secondary (plus light/dark variants) as background colors. + @each $style in ('primary', 'secondary') { + .mdc-theme--#{$style}-bg { + @include feature-targeting.targets($feat-color) { + @include property(background-color, $style, $important: true); + } + } + } +} + +/// Applies a dynamic value to the specified property. This mixin should be used +/// in theme style mixins when setting properties. +/// +/// The value may be any of the following: +/// - a standard CSS value +/// - a custom property Map, e.g. (varname: --mdc-foo, fallback: blue) +/// - a Material theme key String, e.g. 'primary', 'on-primary' +/// +/// @example +/// @include theme.property(color, teal); +/// @include theme.property(color, custom-properties.create(foo, blue)); +/// @include theme.property(color, primary); +/// +/// A `$replace` Map parameter may be provided to replace key/value pairs for +/// string values. This can be used to substitute parameters in complex string +/// values such as `calc()` with custom properties. +/// +/// @example +/// @include theme.property( +/// width, +/// calc(foo + bar), +/// $replace: (foo: custom-properties.create(foo), bar: 8px) +/// ); +/// +/// Note: Material theme key Strings (e.g. `primary`) are not supported as +/// replacement values. +/// +/// A CSS custom property declaration may be emitted by providing a custom +/// property Map to `$property`. The fallback value (or `$value` if provided) +/// will be used as the declaration value. +/// +/// @example - scss +/// .foo { +/// @include theme.property(custom-properties.create(foo, teal)); +/// @include theme.property(custom-properties.create(bar, teal), blue); +/// } +/// +/// @example - css +/// .foo { +/// --mdc-foo: teal; +/// --mdc-bar: blue; +/// } +/// +/// @param {String | Map} $property - The name of the CSS property. May also be +/// a custom property Map to emit a custom propery declaration. +/// @param {String | Number | Color | List | Map} $value - The property's value. +/// This parameter may be omitted if `$property` is a custom property Map. +/// @param {Map} $gss - Optional Map of GSS annotations to set. +/// @param {Map} $replace - An optional Map of replacement key/value pairs if +/// the `$value` is a string. +/// @param {Bool} $important - Set to true to add an `!important` rule. Defaults +/// to false. +@mixin property( + $property, + $value: null, + $gss: (), + $replace: null, + $important: false +) { + @if custom-properties.is-custom-prop($property) { + // $property is a custom property Map + // --mdc-foo: value; + @if $value { + $property: custom-properties.set-fallback( + $property, + $value, + $shallow: true + ); + } + + @include custom-properties.declaration( + $property, + $gss: $gss, + $important: $important + ); + } @else if custom-properties.is-custom-prop($value) { + // $value is a custom property Map + // property: var(--mdc-foo, fallback); + @include custom-properties.declaration( + $property, + $value, + $gss: $gss, + $important: $important + ); + } @else if keys.is-key($value) { + // $value is a key String + // property: key; + $custom-prop: keys.create-custom-property($value); + + @if theme-color.is-theme-key($value) { + // Determine if we need to use a compile-time updated value to support + // Angular. + $key: $value; + // (changed: Bool, value: *) + $result: theme-color.deprecated-get-global-theme-key-value-if-changed( + $key + ); + + @if map.get($result, changed) { + // $mdc-theme-property-values was changed at compile time. Use the + // global value instead. Otherwise if it was not changed, continue + // using the key store normally. + $custom-prop: keys.create-custom-property($key); + $custom-prop: custom-properties.set-fallback( + $custom-prop, + map.get($result, value) + ); + } + } + + @include custom-properties.declaration( + $property, + $custom-prop, + $gss: $gss, + $important: $important + ); + } @else { + // $value is a standard CSS value + // property: value; + $fallback: null; + @if $replace { + // If any replacements are null, treat the entire value as null (do not + // emit anything). + @each $name, $replacement in $replace { + @if $replacement == null { + $value: null; + } + } + } + + @if $replace and $value { + @if meta.type-of($replace) != 'map' { + @error 'mdc-theme: Invalid replacement #{$replace}. Must be a Map.'; + } + + $replace-map-fallback: (); + $replace-map-value: (); + $needs-fallback: false; + @each $name, $replacement in $replace { + @if custom-properties.is-custom-prop($replacement) { + $replace-value: custom-properties.get-declaration-value($replacement); + $replace-fallback: custom-properties.get-declaration-fallback( + $replacement + ); + @if $replace-fallback { + $needs-fallback: true; + } + + $replace-map-value: map.set( + $replace-map-value, + $name, + $replace-value + ); + $replace-map-fallback: map.set( + $replace-map-fallback, + $name, + $replace-fallback + ); + } @else { + $replace-map-value: map.set($replace-map-value, $name, $replacement); + $replace-map-fallback: map.set( + $replace-map-fallback, + $name, + $replacement + ); + } + } + + @if meta.type-of($value) == 'string' { + @if $needs-fallback { + $fallback: replace.replace-string($value, $replace-map-fallback); + } + $value: replace.replace-string($value, $replace-map-value); + } @else if meta.type-of($value) == 'list' { + @if $needs-fallback { + $fallback: replace.replace-list($value, $replace-map-fallback); + } + $value: replace.replace-list($value, $replace-map-value); + } @else { + @error 'mdc-theme: Invalid replacement value #{$value}. $replace may only be used with string or list values.'; + } + } + + @include css.declaration( + $property, + $value, + $fallback-value: $fallback, + $gss: $gss, + $important: $important + ); + } +} + +// @deprecated use the `property()` mixin instead +@mixin prop($property, $style, $important: false) { + @include property($property, $style, $important: $important); +} + +/// Validates theme configuration keys by comparing it with original theme +/// configuration, also validates theme values to see if it has any unsupported +/// value formats. +/// @param {Map} $origin-theme - Original theme configuration in Sass map format +/// that has all supported keys. +/// @param {Map} $custom-theme - Provided theme configuration in Sass map format +/// that should be validated against `$origin-theme`. +/// @examples +/// @mixin theme($theme) { +/// @include theme.validate-theme($light-theme, $theme); +/// +/// // ... +/// } +@mixin validate-theme($origin-theme, $custom-theme, $test-only: false) { + @include validate-theme-keys( + $origin-theme, + $custom-theme, + $test-only: $test-only + ); + @include _validate-theme-values($custom-theme, $test-only: $test-only); +} + +/// Validates theme configuration keys by comparing it with original theme +/// configuration. +/// @see Use `validate-theme()` to validate both theme keys and theme values. +/// @param {Map} $origin-theme - Original theme configuration in Sass map format +/// that has all supported keys. +/// @param {Map} $custom-theme - Provided theme configuration in Sass map format +/// that should be validated against `$origin-theme`. +@mixin validate-theme-keys($origin-theme, $custom-theme, $test-only: false) { + $origin-keys: map.keys($origin-theme); + $unsupported-keys: (); + + @each $key, $value in $custom-theme { + @if (not list.index($origin-keys, $key)) { + $unsupported-keys: list.append( + $unsupported-keys, + $key, + $separator: comma + ); + } + } + + @if list.length($unsupported-keys) > 0 { + $error-message: 'Unsupported keys found: #{$unsupported-keys}. Expected one of: #{$origin-keys}.'; + + @if $test-only { + content: $error-message; + } @else { + @error $error-message; + } + } +} + +/// Validates theme configuration values to see if it has any unsupported value +/// formats. +/// @see Use `validate-theme()` to validate both theme keys and theme values. +/// @param {Map} $custom-theme - Provided theme configuration in Sass map format +/// that needs to be validated. +@mixin _validate-theme-values($custom-theme, $test-only: false) { + $unsupported-custom-prop-keys: (); + + @each $key, $value in $custom-theme { + @if custom-properties.is-custom-prop($value) { + $unsupported-custom-prop-keys: list.append( + $unsupported-custom-prop-keys, + $key, + $separator: comma + ); + } + } + + @if list.length($unsupported-custom-prop-keys) > 0 { + $error-message: 'Custom properties are not supported for theme map keys: #{$unsupported-custom-prop-keys}'; + + @if $test-only { + content: $error-message; + } @else { + @error $error-message; + } + } +} diff --git a/components/controller/form-controller.ts b/components/controller/form-controller.ts index aeeb1d8581b..4d6f955ae8a 100644 --- a/components/controller/form-controller.ts +++ b/components/controller/form-controller.ts @@ -6,7 +6,7 @@ import {ReactiveController, ReactiveControllerHost} from 'lit'; -import {bound} from '../decorators/bound'; +import {bound} from '../decorators/bound.js'; /** * An element that `FormController` may use. diff --git a/components/controller/observer-foundation.ts b/components/controller/observer-foundation.ts index e429b7a2eef..a92e25a8ecc 100644 --- a/components/controller/observer-foundation.ts +++ b/components/controller/observer-foundation.ts @@ -4,8 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {Foundation} from './foundation'; -import {observeProperty, ObserverRecord} from './observer'; +import {Foundation} from './foundation.js'; +import {observeProperty, ObserverRecord} from './observer.js'; /** * Legacy observer foundation class for components. diff --git a/components/controller/test/action-controller_test.ts b/components/controller/test/action-controller_test.ts index 61e43449dbf..c0790e1f4d5 100644 --- a/components/controller/test/action-controller_test.ts +++ b/components/controller/test/action-controller_test.ts @@ -7,11 +7,11 @@ import 'jasmine'; import {html, LitElement} from 'lit'; -import {customElement, property} from 'lit/decorators'; +import {customElement, property} from 'lit/decorators.js'; -import {Environment} from '../../testing/environment'; -import {Harness} from '../../testing/harness'; -import {ActionController, ActionControllerHost, BeginPressConfig, EndPressConfig, TOUCH_DELAY_MS, WAIT_FOR_MOUSE_CLICK_MS} from '../action-controller'; +import {Environment} from '../../testing/environment.js'; +import {Harness} from '../../testing/harness.js'; +import {ActionController, ActionControllerHost, BeginPressConfig, EndPressConfig, TOUCH_DELAY_MS, WAIT_FOR_MOUSE_CLICK_MS} from '../action-controller.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/controller/test/events_test.ts b/components/controller/test/events_test.ts index 196fc906f10..4ad6c284234 100644 --- a/components/controller/test/events_test.ts +++ b/components/controller/test/events_test.ts @@ -6,7 +6,7 @@ import 'jasmine'; -import {redispatchEvent} from '../events'; +import {redispatchEvent} from '../events.js'; describe('redispatchEvent()', () => { let instance: HTMLDivElement; diff --git a/components/controller/test/form-controller_test.ts b/components/controller/test/form-controller_test.ts index 40f541e5a8e..757b428f9ae 100644 --- a/components/controller/test/form-controller_test.ts +++ b/components/controller/test/form-controller_test.ts @@ -7,10 +7,10 @@ import 'jasmine'; import {html, LitElement, TemplateResult} from 'lit'; -import {customElement, property} from 'lit/decorators'; +import {customElement, property} from 'lit/decorators.js'; -import {Environment} from '../../testing/environment'; -import {FormController, FormElement, getFormValue} from '../form-controller'; +import {Environment} from '../../testing/environment.js'; +import {FormController, FormElement, getFormValue} from '../form-controller.js'; function submitForm(form: HTMLFormElement) { return new Promise(resolve => { diff --git a/components/controller/test/foundation_test.ts b/components/controller/test/foundation_test.ts index 76e1924961a..aa2e8199879 100644 --- a/components/controller/test/foundation_test.ts +++ b/components/controller/test/foundation_test.ts @@ -6,7 +6,7 @@ import 'jasmine'; -import {Foundation} from '../foundation'; +import {Foundation} from '../foundation.js'; describe('Foundation', () => { it('#init() should be called on construction', () => { diff --git a/components/controller/test/observer-foundation_test.ts b/components/controller/test/observer-foundation_test.ts index bdf22f83e70..282fe525005 100644 --- a/components/controller/test/observer-foundation_test.ts +++ b/components/controller/test/observer-foundation_test.ts @@ -6,8 +6,8 @@ import 'jasmine'; -import {ObserverRecord} from '../observer'; -import {ObserverFoundation} from '../observer-foundation'; +import {ObserverRecord} from '../observer.js'; +import {ObserverFoundation} from '../observer-foundation.js'; describe('ObserverFoundation', () => { class TestObserverFoundation extends ObserverFoundation<{}> { diff --git a/components/controller/test/observer_test.ts b/components/controller/test/observer_test.ts index d6999fa2414..1fad78f6a8c 100644 --- a/components/controller/test/observer_test.ts +++ b/components/controller/test/observer_test.ts @@ -6,7 +6,7 @@ import 'jasmine'; -import {observeProperty, setObserversEnabled} from '../observer'; +import {observeProperty, setObserversEnabled} from '../observer.js'; describe('observeProperty()', () => { it('should call Observer when property value changes', () => { diff --git a/components/decorators/bound.ts b/components/decorators/bound.ts index 16f95fcbc97..85136f4118a 100644 --- a/components/decorators/bound.ts +++ b/components/decorators/bound.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {FunctionKeys} from '../types/keys'; +import {FunctionKeys} from '../types/keys.js'; /** * Binds a class's method to its instance. diff --git a/components/decorators/test/aria-property_test.ts b/components/decorators/test/aria-property_test.ts index 6a25d81b5a1..e0a5e925ec4 100644 --- a/components/decorators/test/aria-property_test.ts +++ b/components/decorators/test/aria-property_test.ts @@ -7,11 +7,11 @@ import 'jasmine'; import {html, LitElement} from 'lit'; -import {customElement, property, query} from 'lit/decorators'; -import {ifDefined} from 'lit/directives/if-defined'; +import {customElement, property, query} from 'lit/decorators.js'; +import {ifDefined} from 'lit/directives/if-defined.js'; -import {Environment} from '../../testing/environment'; -import {ariaProperty} from '../aria-property'; +import {Environment} from '../../testing/environment.js'; +import {ariaProperty} from '../aria-property.js'; describe('@ariaProperty', () => { const env = new Environment(); diff --git a/components/decorators/test/bound_test.ts b/components/decorators/test/bound_test.ts index 53409d309e7..18ff83e6516 100644 --- a/components/decorators/test/bound_test.ts +++ b/components/decorators/test/bound_test.ts @@ -6,7 +6,7 @@ import 'jasmine'; -import {bound} from '../bound'; +import {bound} from '../bound.js'; describe('@bound', () => { class MyClass { diff --git a/components/elevation/lib/_surface.scss b/components/elevation/lib/_surface.scss index deacd9c929b..b184c4af1d8 100644 --- a/components/elevation/lib/_surface.scss +++ b/components/elevation/lib/_surface.scss @@ -4,7 +4,7 @@ // // TODO(b/228217731): Remove MDC/MWC dependencies -@use '@material/theme/css'; +@use '../../compat/theme/css'; $duration: 280ms; $easing: cubic-bezier(0.4, 0, 0.2, 1); diff --git a/components/fab/fab-extended.ts b/components/fab/fab-extended.ts index b038534823b..f4868efe15d 100644 --- a/components/fab/fab-extended.ts +++ b/components/fab/fab-extended.ts @@ -4,13 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {styles as overlayStyles} from '../elevation/lib/elevation-overlay-styles.css'; +import {styles as overlayStyles} from '../elevation/lib/elevation-overlay-styles.css.js'; -import {FabExtended} from './lib/fab-extended'; -import {styles as extendedStyles} from './lib/fab-extended-styles.css'; -import {styles as sharedStyles} from './lib/fab-shared-styles.css'; +import {FabExtended} from './lib/fab-extended.js'; +import {styles as extendedStyles} from './lib/fab-extended-styles.css.js'; +import {styles as sharedStyles} from './lib/fab-shared-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/fab/fab.ts b/components/fab/fab.ts index 5c5e487527a..4fce2bb1793 100644 --- a/components/fab/fab.ts +++ b/components/fab/fab.ts @@ -4,13 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {styles as overlayStyles} from '../elevation/lib/elevation-overlay-styles.css'; +import {styles as overlayStyles} from '../elevation/lib/elevation-overlay-styles.css.js'; -import {Fab} from './lib/fab'; -import {styles as sharedStyles} from './lib/fab-shared-styles.css'; -import {styles as fabStyles} from './lib/fab-styles.css'; +import {Fab} from './lib/fab.js'; +import {styles as sharedStyles} from './lib/fab-shared-styles.css.js'; +import {styles as fabStyles} from './lib/fab-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/fab/harness.ts b/components/fab/harness.ts index 21dd360205c..da0df415c4d 100644 --- a/components/fab/harness.ts +++ b/components/fab/harness.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {Harness} from '../testing/harness'; +import {Harness} from '../testing/harness.js'; -import {Fab} from './lib/fab'; +import {Fab} from './lib/fab.js'; /** * Test harness for floating action buttons. diff --git a/components/fab/lib/_fab-extended-theme.scss b/components/fab/lib/_fab-extended-theme.scss index 511df91ecf3..cc8e05c6b75 100644 --- a/components/fab/lib/_fab-extended-theme.scss +++ b/components/fab/lib/_fab-extended-theme.scss @@ -8,7 +8,7 @@ // TODO(b/228217731): Remove MDC/MWC dependencies @use 'sass:map'; -@use '@material/theme/state'; +@use '../../compat/theme/state'; @use '@material/typography/typography'; @use '../../sass/resolvers'; @use '../../sass/theme'; diff --git a/components/fab/lib/_fab-shared-theme.scss b/components/fab/lib/_fab-shared-theme.scss index f2652262b31..00518b3c969 100644 --- a/components/fab/lib/_fab-shared-theme.scss +++ b/components/fab/lib/_fab-shared-theme.scss @@ -9,7 +9,7 @@ // TODO(b/228217731): Remove MDC/MWC dependencies @use 'sass:map'; @use 'sass:meta'; -@use '@material/theme/state'; +@use '../../compat/theme/state'; @use 'third_party/javascript/material_web_components/ripple/ripple-theme'; @use '../../elevation/lib/elevation-theme'; @use '../../sass/elevation'; diff --git a/components/fab/lib/_fab.scss b/components/fab/lib/_fab.scss index 95345b35c6a..ccd5e7d7e1b 100644 --- a/components/fab/lib/_fab.scss +++ b/components/fab/lib/_fab.scss @@ -9,7 +9,7 @@ // TODO(b/228217731): Remove MDC/MWC dependencies @use 'third_party/javascript/material_web_components/icon/_mwc-icon'; @use 'third_party/javascript/material_web_components/m3/elevation/lib/surface'; -@use '@material/theme/selector-ext'; +@use '../../compat/theme/selector-ext'; @use '@material/touch-target/touch-target'; @mixin static-styles() { diff --git a/components/fab/lib/fab-extended.ts b/components/fab/lib/fab-extended.ts index 319ddde0ba8..bfc74c57f0c 100644 --- a/components/fab/lib/fab-extended.ts +++ b/components/fab/lib/fab-extended.ts @@ -5,9 +5,9 @@ */ import {html, TemplateResult} from 'lit'; -import {ClassInfo} from 'lit/directives/class-map'; +import {ClassInfo} from 'lit/directives/class-map.js'; -import {FabShared} from './fab-shared'; +import {FabShared} from './fab-shared.js'; /** * Fab Extended Base class logic and template definition diff --git a/components/fab/lib/fab-shared.ts b/components/fab/lib/fab-shared.ts index 1255214437a..bb2cda9cf18 100644 --- a/components/fab/lib/fab-shared.ts +++ b/components/fab/lib/fab-shared.ts @@ -4,13 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import '@material/mwc-ripple/mwc-ripple'; +import '@material/mwc-ripple/mwc-ripple.js'; -import {Ripple} from '@material/mwc-ripple/mwc-ripple'; -import {RippleHandlers} from '@material/mwc-ripple/ripple-handlers'; +import {Ripple} from '@material/mwc-ripple/mwc-ripple.js'; +import {RippleHandlers} from '@material/mwc-ripple/ripple-handlers.js'; import {html, LitElement, TemplateResult} from 'lit'; -import {eventOptions, property, queryAsync, state} from 'lit/decorators'; -import {ClassInfo, classMap} from 'lit/directives/class-map'; +import {eventOptions, property, queryAsync, state} from 'lit/decorators.js'; +import {ClassInfo, classMap} from 'lit/directives/class-map.js'; /** * Fab Base class logic and template definition diff --git a/components/fab/lib/fab.ts b/components/fab/lib/fab.ts index 9aa5039ddee..01bf4506b8f 100644 --- a/components/fab/lib/fab.ts +++ b/components/fab/lib/fab.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {ClassInfo} from 'lit/directives/class-map'; +import {ClassInfo} from 'lit/directives/class-map.js'; -import {FabShared} from './fab-shared'; +import {FabShared} from './fab-shared.js'; /** * Fab Extended Base class logic and template definition diff --git a/components/field/filled-field.ts b/components/field/filled-field.ts index 556ace5fcc7..ebb753cb6ef 100644 --- a/components/field/filled-field.ts +++ b/components/field/filled-field.ts @@ -4,11 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {FilledField} from './lib/filled-field'; -import {styles as filledStyles} from './lib/filled-styles.css'; -import {styles as sharedStyles} from './lib/shared-styles.css'; +import {FilledField} from './lib/filled-field.js'; +import {styles as filledStyles} from './lib/filled-styles.css.js'; +import {styles as sharedStyles} from './lib/shared-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/field/harness.ts b/components/field/harness.ts index 97c04730225..496e5aba239 100644 --- a/components/field/harness.ts +++ b/components/field/harness.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {Harness} from '../testing/harness'; +import {Harness} from '../testing/harness.js'; -import {Field} from './lib/field'; +import {Field} from './lib/field.js'; /** * Test harness for field elements. diff --git a/components/field/lib/_content.scss b/components/field/lib/_content.scss index 3f419b00853..edceacc94ab 100644 --- a/components/field/lib/_content.scss +++ b/components/field/lib/_content.scss @@ -6,9 +6,8 @@ // stylelint-disable selector-class-pattern -- // Selector '.md3-*' should only be used in this project. -// TODO(b/228217731): Remove MDC/MWC dependencies @use 'sass:math'; -@use '@material/animation/animation'; +@use '../../motion/animation'; @mixin static-styles() { .md3-field__start, diff --git a/components/field/lib/_filled-field.scss b/components/field/lib/_filled-field.scss index f8cfce1ee9a..59d2c10fcbe 100644 --- a/components/field/lib/_filled-field.scss +++ b/components/field/lib/_filled-field.scss @@ -6,10 +6,8 @@ // stylelint-disable selector-class-pattern -- // Selector '.md3-*' should only be used in this project. -// TODO(b/228217731): Remove MDC/MWC dependencies @use 'sass:map'; -@use '@material/animation/animation'; -@use '@material/theme/state'; +@use '../../motion/animation'; @use './field-theme'; $_animation-duration: 150ms; diff --git a/components/field/lib/_outlined-field.scss b/components/field/lib/_outlined-field.scss index e28d58f049b..43493ca478a 100644 --- a/components/field/lib/_outlined-field.scss +++ b/components/field/lib/_outlined-field.scss @@ -6,10 +6,8 @@ // stylelint-disable selector-class-pattern -- // Selector '.md3-*' should only be used in this project. -// TODO(b/228217731): Remove MDC/MWC dependencies @use 'sass:map'; -@use '@material/animation/animation'; -@use '@material/theme/gss'; +@use '../../motion/animation'; $_animation-duration: 150ms; @@ -184,17 +182,15 @@ $_animation-duration: 150ms; transition: animation.standard(transform, $_animation-duration); } + // Note: no need to do any RTL flipping here. If RTLCSS flips this, it's also + // ok, we just need one to be left and one to be right. &::before { - @include gss.annotate($noflip: true); right: 50%; - @include gss.annotate($noflip: true); transform-origin: top left; } &::after { - @include gss.annotate($noflip: true); left: 50%; - @include gss.annotate($noflip: true); transform-origin: top right; } } diff --git a/components/field/lib/field.ts b/components/field/lib/field.ts index 9148a8e762d..0f69cbae166 100644 --- a/components/field/lib/field.ts +++ b/components/field/lib/field.ts @@ -7,10 +7,10 @@ */ import {html, LitElement, PropertyValues, TemplateResult} from 'lit'; -import {property, queryAsync, state} from 'lit/decorators'; -import {ClassInfo, classMap} from 'lit/directives/class-map'; +import {property, queryAsync, state} from 'lit/decorators.js'; +import {ClassInfo, classMap} from 'lit/directives/class-map.js'; -import {createAnimationSignal, Easing} from '../../motion/animation'; +import {createAnimationSignal, Easing} from '../../motion/animation.js'; /** @soyCompatible */ export class Field extends LitElement { diff --git a/components/field/lib/filled-field.ts b/components/field/lib/filled-field.ts index de7fe3a084c..a73d1426e82 100644 --- a/components/field/lib/filled-field.ts +++ b/components/field/lib/filled-field.ts @@ -5,11 +5,11 @@ */ import {html, TemplateResult} from 'lit'; -import {state} from 'lit/decorators'; -import {ClassInfo} from 'lit/directives/class-map'; -import {styleMap} from 'lit/directives/style-map'; +import {state} from 'lit/decorators.js'; +import {ClassInfo} from 'lit/directives/class-map.js'; +import {styleMap} from 'lit/directives/style-map.js'; -import {Field} from './field'; +import {Field} from './field.js'; /** @soyCompatible */ export class FilledField extends Field { diff --git a/components/field/lib/outlined-field.ts b/components/field/lib/outlined-field.ts index 4f2e3051155..1179ec207ec 100644 --- a/components/field/lib/outlined-field.ts +++ b/components/field/lib/outlined-field.ts @@ -5,9 +5,9 @@ */ import {html, TemplateResult} from 'lit'; -import {ClassInfo} from 'lit/directives/class-map'; +import {ClassInfo} from 'lit/directives/class-map.js'; -import {Field} from './field'; +import {Field} from './field.js'; /** @soyCompatible */ export class OutlinedField extends Field { diff --git a/components/field/outlined-field.ts b/components/field/outlined-field.ts index 28ccdffda55..f16d4a74667 100644 --- a/components/field/outlined-field.ts +++ b/components/field/outlined-field.ts @@ -4,11 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {OutlinedField} from './lib/outlined-field'; -import {styles as outlinedStyles} from './lib/outlined-styles.css'; -import {styles as sharedStyles} from './lib/shared-styles.css'; +import {OutlinedField} from './lib/outlined-field.js'; +import {styles as outlinedStyles} from './lib/outlined-styles.css.js'; +import {styles as sharedStyles} from './lib/shared-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/field/test/field_test.ts b/components/field/test/field_test.ts index d1cd5ea5da0..3cf160d8717 100644 --- a/components/field/test/field_test.ts +++ b/components/field/test/field_test.ts @@ -7,12 +7,12 @@ import 'jasmine'; import {html} from 'lit'; -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {Environment} from '../../testing/environment'; -import {State, TemplateBuilder, TemplateProps} from '../../testing/templates'; -import {FieldHarness} from '../harness'; -import {Field} from '../lib/field'; +import {Environment} from '../../testing/environment.js'; +import {State, TemplateBuilder, TemplateProps} from '../../testing/templates.js'; +import {FieldHarness} from '../harness.js'; +import {Field} from '../lib/field.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/field/test/filled-field_test.ts b/components/field/test/filled-field_test.ts index 6d94c342f58..ec4f25e3f08 100644 --- a/components/field/test/filled-field_test.ts +++ b/components/field/test/filled-field_test.ts @@ -7,12 +7,12 @@ import 'jasmine'; import {html} from 'lit'; -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {Environment} from '../../testing/environment'; -import {State, TemplateBuilder, TemplateProps} from '../../testing/templates'; -import {FieldHarness} from '../harness'; -import {FilledField} from '../lib/filled-field'; +import {Environment} from '../../testing/environment.js'; +import {State, TemplateBuilder, TemplateProps} from '../../testing/templates.js'; +import {FieldHarness} from '../harness.js'; +import {FilledField} from '../lib/filled-field.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/field/test/outlined-field_test.ts b/components/field/test/outlined-field_test.ts index 7f69ba88f0b..18169913dbb 100644 --- a/components/field/test/outlined-field_test.ts +++ b/components/field/test/outlined-field_test.ts @@ -7,12 +7,12 @@ import 'jasmine'; import {html} from 'lit'; -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {Environment} from '../../testing/environment'; -import {State, TemplateBuilder, TemplateProps} from '../../testing/templates'; -import {FieldHarness} from '../harness'; -import {OutlinedField} from '../lib/outlined-field'; +import {Environment} from '../../testing/environment.js'; +import {State, TemplateBuilder, TemplateProps} from '../../testing/templates.js'; +import {FieldHarness} from '../harness.js'; +import {OutlinedField} from '../lib/outlined-field.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/focus/focus-ring.ts b/components/focus/focus-ring.ts index fa26fb1ef10..50791d7816a 100644 --- a/components/focus/focus-ring.ts +++ b/components/focus/focus-ring.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {FocusRing} from './lib/focus-ring'; -import {styles} from './lib/focus-ring-styles.css'; +import {FocusRing} from './lib/focus-ring.js'; +import {styles} from './lib/focus-ring-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/focus/lib/focus-ring.ts b/components/focus/lib/focus-ring.ts index a50d5b20d51..e7a37a93ca4 100644 --- a/components/focus/lib/focus-ring.ts +++ b/components/focus/lib/focus-ring.ts @@ -5,8 +5,8 @@ */ import {html, LitElement, TemplateResult} from 'lit'; -import {property} from 'lit/decorators'; -import {ClassInfo, classMap} from 'lit/directives/class-map'; +import {property} from 'lit/decorators.js'; +import {ClassInfo, classMap} from 'lit/directives/class-map.js'; /** @soyCompatible */ export class FocusRing extends LitElement { diff --git a/components/focus/test/screenshot_test.scss b/components/focus/test/screenshot_test.scss new file mode 100644 index 00000000000..2529e3d0eba --- /dev/null +++ b/components/focus/test/screenshot_test.scss @@ -0,0 +1,14 @@ +@use '../lib/focus-ring-theme'; + +// Default button has 1px border and 1px margin. This plus the 2px padding +// means the padding it set to 4px +$_focus-ring-padding: 4px; + +.test-button { + @include focus-ring-theme.theme( + ( + container-outer-padding-vertical: $_focus-ring-padding, + container-outer-padding-horizontal: $_focus-ring-padding, + ) + ); +} diff --git a/components/focus/test/screenshot_test.ts b/components/focus/test/screenshot_test.ts new file mode 100644 index 00000000000..81f188f2d6e --- /dev/null +++ b/components/focus/test/screenshot_test.ts @@ -0,0 +1,95 @@ +/** @license Googler-authored internal-only code. */ + +import 'jasmine'; +import '../../testing/table/test-table.js'; +import '../focus-ring.js'; + +import {html, LitElement, TemplateResult} from 'lit'; +import {customElement, state} from 'lit/decorators.js'; + +import {Harness} from '../../testing/harness.js'; +import {ScubaEnvironment} from '../../testing/scuba-environment.js'; +import {State, TemplateBuilder, TemplateProps} from '../../testing/templates.js'; +import {pointerPress, setForceStrongFocus, shouldShowStrongFocus} from '../strong-focus.js'; + + +declare global { + interface HTMLElementTagNameMap { + 'test-button': TestButton; + } +} + +@customElement('test-button') +class TestButton extends LitElement { + @state() protected showFocusRing = false; + + protected override render(): TemplateResult { + return html` + + `; + } + + protected handleFocus() { + this.showFocusRing = shouldShowStrongFocus(); + } + + protected handleBlur() { + this.showFocusRing = false; + } + + protected handlePointerDown() { + pointerPress(); + } +} + +class TestButtonHarness extends Harness { + override async getInteractiveElement() { + await this.element.updateComplete; + return this.element.renderRoot.querySelector('button')!; + } +} + +describe('', () => { + const env = new ScubaEnvironment({name: 'focus'}); + + const templates = + new TemplateBuilder().withHarness(TestButtonHarness).withVariants({ + default(directive) { + return html``; + }, + }); + + it('default', async () => { + renderTable('Default'); + + expect(await env.diffRootWithRtl('default')).toHaveAllPassed(); + }); + + it('strong focus', async () => { + setForceStrongFocus(true); + renderTable('Strong Focus'); + + expect(await env.diffRootWithRtl('strong_focus')).toHaveAllPassed(); + }); + + function renderTable(title: string, props: TemplateProps = {}) { + const testTemplates = templates.all(props); + env.render(html` + + `); + } +}); diff --git a/components/focus/test/strong-focus_test.ts b/components/focus/test/strong-focus_test.ts index 903a180e65d..441f18b64df 100644 --- a/components/focus/test/strong-focus_test.ts +++ b/components/focus/test/strong-focus_test.ts @@ -6,7 +6,7 @@ import 'jasmine'; -import * as strongFocus from '../strong-focus'; +import * as strongFocus from '../strong-focus.js'; class MockFocus { constructor(public visible = false) {} diff --git a/components/formfield/formfield.ts b/components/formfield/formfield.ts index 745be598e9b..2eb740c0107 100644 --- a/components/formfield/formfield.ts +++ b/components/formfield/formfield.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {Formfield} from './lib/formfield-base'; -import {styles as formfieldStyles} from './lib/formfield-styles.css'; +import {Formfield} from './lib/formfield-base.js'; +import {styles as formfieldStyles} from './lib/formfield-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/formfield/lib/formfield-base.ts b/components/formfield/lib/formfield-base.ts index 4477474b25a..b0f76c9e960 100644 --- a/components/formfield/lib/formfield-base.ts +++ b/components/formfield/lib/formfield-base.ts @@ -5,9 +5,9 @@ */ 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 {property, queryAssignedElements} from 'lit/decorators.js'; +import {ClassInfo, classMap} from 'lit/directives/class-map.js'; +import {ifDefined} from 'lit/directives/if-defined.js'; /** @soyCompatible */ export class Formfield extends LitElement { diff --git a/components/icon-button/harness.ts b/components/icon-button/harness.ts index 867fbc02463..23132962553 100644 --- a/components/icon-button/harness.ts +++ b/components/icon-button/harness.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {Harness} from '../testing/harness'; +import {Harness} from '../testing/harness.js'; -import {IconButton} from './lib/icon-button'; -import {IconButtonToggle} from './lib/icon-button-toggle'; +import {IconButton} from './lib/icon-button.js'; +import {IconButtonToggle} from './lib/icon-button-toggle.js'; /** * Test harness for icon buttons. diff --git a/components/icon-button/icon-button-toggle.ts b/components/icon-button/icon-button-toggle.ts index 11f3024239f..21f65d34b49 100644 --- a/components/icon-button/icon-button-toggle.ts +++ b/components/icon-button/icon-button-toggle.ts @@ -4,13 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import '../icon/icon'; +import '../icon/icon.js'; import {html, TemplateResult} from 'lit'; -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {styles} from './lib/icon-button-styles.css'; -import {IconButtonToggle} from './lib/icon-button-toggle'; +import {styles} from './lib/icon-button-styles.css.js'; +import {IconButtonToggle} from './lib/icon-button-toggle.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/icon-button/icon-button.ts b/components/icon-button/icon-button.ts index f71b7033d69..53c29cc13b1 100644 --- a/components/icon-button/icon-button.ts +++ b/components/icon-button/icon-button.ts @@ -4,13 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import '../icon/icon'; +import '../icon/icon.js'; import {html, TemplateResult} from 'lit'; -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {IconButton} from './lib/icon-button'; -import {styles} from './lib/icon-button-styles.css'; +import {IconButton} from './lib/icon-button.js'; +import {styles} from './lib/icon-button-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/icon-button/lib/_icon-button-theme.scss b/components/icon-button/lib/_icon-button-theme.scss index 9b785d6dac9..d2f499cc649 100644 --- a/components/icon-button/lib/_icon-button-theme.scss +++ b/components/icon-button/lib/_icon-button-theme.scss @@ -12,9 +12,9 @@ @use '../../tokens'; @use 'third_party/javascript/material_web_components/m3/ripple/ripple-theme'; @use '@material/ripple/ripple-theme' as mdc-ripple-theme; -@use '@material/theme/keys'; -@use '@material/theme/state'; -@use '@material/theme/theme-color'; +@use '../../compat/theme/keys'; +@use '../../compat/theme/state'; +@use '../../compat/theme/theme-color'; @use '@material/touch-target/touch-target'; $_custom-property-prefix: 'icon-button'; diff --git a/components/icon-button/lib/icon-button-toggle.ts b/components/icon-button/lib/icon-button-toggle.ts index 12444531f08..dde38b6e4aa 100644 --- a/components/icon-button/lib/icon-button-toggle.ts +++ b/components/icon-button/lib/icon-button-toggle.ts @@ -5,14 +5,14 @@ */ import {html, TemplateResult} from 'lit'; -import {property, query, state} from 'lit/decorators'; -import {classMap} from 'lit/directives/class-map'; -import {ifDefined} from 'lit/directives/if-defined'; - -import {ActionElement, BeginPressConfig, EndPressConfig} from '../../action-element/action-element'; -import {ariaProperty} from '../../decorators/aria-property'; -import {pointerPress, shouldShowStrongFocus} from '../../focus/strong-focus'; -import {MdRipple} from '../../ripple/ripple'; +import {property, query, state} from 'lit/decorators.js'; +import {classMap} from 'lit/directives/class-map.js'; +import {ifDefined} from 'lit/directives/if-defined.js'; + +import {ActionElement, BeginPressConfig, EndPressConfig} from '../../action-element/action-element.js'; +import {ariaProperty} from '../../decorators/aria-property.js'; +import {pointerPress, shouldShowStrongFocus} from '../../focus/strong-focus.js'; +import {MdRipple} from '../../ripple/ripple.js'; /** @soyCompatible */ export class IconButtonToggle extends ActionElement { diff --git a/components/icon-button/lib/icon-button.ts b/components/icon-button/lib/icon-button.ts index 0792afe75c7..8bc0d57a6f4 100644 --- a/components/icon-button/lib/icon-button.ts +++ b/components/icon-button/lib/icon-button.ts @@ -4,17 +4,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -import '../../focus/focus-ring'; +import '../../focus/focus-ring.js'; import {html, TemplateResult} from 'lit'; -import {property, query, state} from 'lit/decorators'; -import {ifDefined} from 'lit/directives/if-defined'; - -import {ActionElement, BeginPressConfig, EndPressConfig} from '../../action-element/action-element'; -import {ariaProperty} from '../../decorators/aria-property'; -import {pointerPress, shouldShowStrongFocus} from '../../focus/strong-focus'; -import {MdRipple} from '../../ripple/ripple'; -import {ARIAHasPopup} from '../../types/aria'; +import {property, query, state} from 'lit/decorators.js'; +import {ifDefined} from 'lit/directives/if-defined.js'; + +import {ActionElement, BeginPressConfig, EndPressConfig} from '../../action-element/action-element.js'; +import {ariaProperty} from '../../decorators/aria-property.js'; +import {pointerPress, shouldShowStrongFocus} from '../../focus/strong-focus.js'; +import {MdRipple} from '../../ripple/ripple.js'; +import {ARIAHasPopup} from '../../types/aria.js'; /** @soyCompatible */ export class IconButton extends ActionElement { diff --git a/components/icon-button/lib/link-icon-button.ts b/components/icon-button/lib/link-icon-button.ts index 6418aed40e5..2f510154c6a 100644 --- a/components/icon-button/lib/link-icon-button.ts +++ b/components/icon-button/lib/link-icon-button.ts @@ -5,10 +5,10 @@ */ import {html, TemplateResult} from 'lit'; -import {property} from 'lit/decorators'; -import {ifDefined} from 'lit/directives/if-defined'; +import {property} from 'lit/decorators.js'; +import {ifDefined} from 'lit/directives/if-defined.js'; -import {IconButton} from './icon-button'; +import {IconButton} from './icon-button.js'; // Note that we cast `linkTarget` to this type, below. The Lit compiler // enforces the `target` attribute value to be of this type, but this is not diff --git a/components/icon-button/link-icon-button.ts b/components/icon-button/link-icon-button.ts index 10cacc22a93..81dab055a80 100644 --- a/components/icon-button/link-icon-button.ts +++ b/components/icon-button/link-icon-button.ts @@ -4,13 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import '../icon/icon'; +import '../icon/icon.js'; import {html, TemplateResult} from 'lit'; -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {styles} from './lib/icon-button-styles.css'; -import {LinkIconButton} from './lib/link-icon-button'; +import {styles} from './lib/icon-button-styles.css.js'; +import {LinkIconButton} from './lib/link-icon-button.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/icon/icon.ts b/components/icon/icon.ts index 95eef5c04ea..079982e020d 100644 --- a/components/icon/icon.ts +++ b/components/icon/icon.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {Icon} from './lib/icon'; -import {styles} from './lib/icon-styles.css'; +import {Icon} from './lib/icon.js'; +import {styles} from './lib/icon-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/list/lib/_list-item-theme.scss b/components/list/lib/_list-item-theme.scss index c5b5e26fd83..c6d0f10b8ff 100644 --- a/components/list/lib/_list-item-theme.scss +++ b/components/list/lib/_list-item-theme.scss @@ -8,7 +8,7 @@ // TODO(b/228217731): Remove MDC/MWC dependencies @use 'sass:map'; -@use '@material/theme/state'; +@use '../../compat/theme/state'; @use '@material/typography/typography'; @use '../../sass/resolvers'; @use '../../sass/theme'; diff --git a/components/list/lib/_list-theme.scss b/components/list/lib/_list-theme.scss index 12049e51a37..f0fba268831 100644 --- a/components/list/lib/_list-theme.scss +++ b/components/list/lib/_list-theme.scss @@ -5,7 +5,7 @@ // TODO(b/228217731): Remove MDC/MWC dependencies @use 'sass:map'; -@use '@material/theme/state'; +@use '../../compat/theme/state'; @use '../../sass/resolvers'; @use '../../sass/theme'; diff --git a/components/list/lib/constants.ts b/components/list/lib/constants.ts index 219e8786e4c..b114dcaa068 100644 --- a/components/list/lib/constants.ts +++ b/components/list/lib/constants.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {ListItemState} from './state'; +import {ListItemState} from './state.js'; /** * ListItemInteractionEventDetail provides details for the interaction diff --git a/components/list/lib/list-item-icon.ts b/components/list/lib/list-item-icon.ts index 2fdae7b9809..fc06b2bc85e 100644 --- a/components/list/lib/list-item-icon.ts +++ b/components/list/lib/list-item-icon.ts @@ -5,7 +5,7 @@ */ import {html, LitElement, TemplateResult} from 'lit'; -import {property} from 'lit/decorators'; +import {property} from 'lit/decorators.js'; /** @soyCompatible */ export class ListItemIcon extends LitElement { diff --git a/components/list/lib/list-item.ts b/components/list/lib/list-item.ts index 6b0fe2e697f..2c0c6e96569 100644 --- a/components/list/lib/list-item.ts +++ b/components/list/lib/list-item.ts @@ -5,10 +5,10 @@ */ import {html, LitElement, PropertyValues, TemplateResult} from 'lit'; -import {property, queryAssignedElements, state} from 'lit/decorators'; -import {ClassInfo, classMap} from 'lit/directives/class-map'; +import {property, queryAssignedElements, state} from 'lit/decorators.js'; +import {ClassInfo, classMap} from 'lit/directives/class-map.js'; -import {ARIARole} from '../../types/aria'; +import {ARIARole} from '../../types/aria.js'; /** @soyCompatible */ export class ListItem extends LitElement { diff --git a/components/list/lib/list.ts b/components/list/lib/list.ts index f46142b9e55..adbc3aebbce 100644 --- a/components/list/lib/list.ts +++ b/components/list/lib/list.ts @@ -6,9 +6,9 @@ import {html, LitElement, TemplateResult} from 'lit'; -import {ARIARole} from '../../types/aria'; +import {ARIARole} from '../../types/aria.js'; -import {ListItemInteractionEvent} from './constants'; +import {ListItemInteractionEvent} from './constants.js'; /** @soyCompatible */ export class List extends LitElement { diff --git a/components/list/lib/option-list-item.ts b/components/list/lib/option-list-item.ts index 44eb06c224f..1a23b863a18 100644 --- a/components/list/lib/option-list-item.ts +++ b/components/list/lib/option-list-item.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {ARIARole} from '../../types/aria'; +import {ARIARole} from '../../types/aria.js'; -import {ListItem} from './list-item'; +import {ListItem} from './list-item.js'; /** @soyCompatible */ export class OptionListItem extends ListItem { diff --git a/components/list/lib/option-list.ts b/components/list/lib/option-list.ts index 074141776c0..41290e3b684 100644 --- a/components/list/lib/option-list.ts +++ b/components/list/lib/option-list.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {ARIARole} from '../../types/aria'; +import {ARIARole} from '../../types/aria.js'; -import {List} from './list'; +import {List} from './list.js'; /** @soyCompatible */ export class OptionList extends List { diff --git a/components/list/list-item-icon.ts b/components/list/list-item-icon.ts index 15faec7dbea..79ad4688b98 100644 --- a/components/list/list-item-icon.ts +++ b/components/list/list-item-icon.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {ListItemIcon} from './lib/list-item-icon'; -import {styles} from './lib/list-item-styles.css'; +import {ListItemIcon} from './lib/list-item-icon.js'; +import {styles} from './lib/list-item-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/list/list-item.ts b/components/list/list-item.ts index b4c9e9025fa..613e4b5042b 100644 --- a/components/list/list-item.ts +++ b/components/list/list-item.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {ListItem} from './lib/list-item'; -import {styles} from './lib/list-item-styles.css'; +import {ListItem} from './lib/list-item.js'; +import {styles} from './lib/list-item-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/list/list.ts b/components/list/list.ts index 8535a371705..4530866cde4 100644 --- a/components/list/list.ts +++ b/components/list/list.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {List} from './lib/list'; -import {styles} from './lib/list-styles.css'; +import {List} from './lib/list.js'; +import {styles} from './lib/list-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/list/option-list-item.ts b/components/list/option-list-item.ts index 9afd0c83008..fa60d1a8db6 100644 --- a/components/list/option-list-item.ts +++ b/components/list/option-list-item.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; import {styles} from './lib/list-item-styles.css' -import {OptionListItem} from './lib/option-list-item'; +import {OptionListItem} from './lib/option-list-item.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/list/option-list.ts b/components/list/option-list.ts index 99fe541eba9..80bfa471f86 100644 --- a/components/list/option-list.ts +++ b/components/list/option-list.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {styles} from './lib/list-styles.css'; -import {OptionList} from './lib/option-list'; +import {styles} from './lib/list-styles.css.js'; +import {OptionList} from './lib/option-list.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/motion/test/animation_test.ts b/components/motion/test/animation_test.ts index f623c5a26a5..b44ec9c4c3e 100644 --- a/components/motion/test/animation_test.ts +++ b/components/motion/test/animation_test.ts @@ -6,7 +6,7 @@ import 'jasmine'; -import {AnimationSignal, createAnimationSignal} from '../animation'; +import {AnimationSignal, createAnimationSignal} from '../animation.js'; describe('createAnimationSignal()', () => { let task: AnimationSignal; diff --git a/components/navigation-bar/harness.ts b/components/navigation-bar/harness.ts index f3a3161bb85..0c472833a35 100644 --- a/components/navigation-bar/harness.ts +++ b/components/navigation-bar/harness.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {NavigationTabHarness} from '../navigation_tab/harness'; -import {Harness} from '../testing/harness'; +import {NavigationTabHarness} from '../navigation_tab/harness.js'; +import {Harness} from '../testing/harness.js'; -import {NavigationBar} from './lib/navigation-bar'; +import {NavigationBar} from './lib/navigation-bar.js'; /** * Test harness for navigation bars. diff --git a/components/navigation-bar/lib/_navigation-bar-theme.scss b/components/navigation-bar/lib/_navigation-bar-theme.scss index cfceb5a81e3..e0cd7f7d08e 100644 --- a/components/navigation-bar/lib/_navigation-bar-theme.scss +++ b/components/navigation-bar/lib/_navigation-bar-theme.scss @@ -13,10 +13,10 @@ @use '../../sass/resolvers'; @use '../../sass/theme'; @use '../../tokens/latest' as tokens; -@use '@material/theme/map-ext'; -@use '@material/theme/theme-color'; +@use '../../compat/theme/map-ext'; +@use '../../compat/theme/theme-color'; @use '@material/typography/typography'; -@use '@material/theme/keys'; +@use '../../compat/theme/keys'; $light-theme: tokens.md-comp-navigation-bar-values(); $dark-theme: tokens.md-comp-navigation-bar-values( diff --git a/components/navigation-bar/lib/navigation-bar.ts b/components/navigation-bar/lib/navigation-bar.ts index e592944141e..229ba14d924 100644 --- a/components/navigation-bar/lib/navigation-bar.ts +++ b/components/navigation-bar/lib/navigation-bar.ts @@ -4,17 +4,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {KEY, normalizeKey} from '@material/dom/keyboard'; -import {ariaProperty} from '@material/mwc-base/aria-property'; -import {observer} from '@material/mwc-base/observer'; -import {deepActiveElementPath} from '@material/mwc-base/utils'; +import {KEY, normalizeKey} from '@material/dom/keyboard.js'; +import {ariaProperty} from '@material/mwc-base/aria-property.js'; +import {observer} from '@material/mwc-base/observer.js'; +import {deepActiveElementPath} from '@material/mwc-base/utils.js'; import {NavigationTab} from 'google3/third_party/javascript/material_web_components/m3/navigation_tab/lib/navigation-tab'; import {html, LitElement, PropertyValues, TemplateResult} from 'lit'; -import {property, queryAssignedElements} from 'lit/decorators'; -import {ifDefined} from 'lit/directives/if-defined'; +import {property, queryAssignedElements} from 'lit/decorators.js'; +import {ifDefined} from 'lit/directives/if-defined.js'; -import {NavigationTabInteractionEvent} from './constants'; -import {NavigationBarState} from './state'; +import {NavigationTabInteractionEvent} from './constants.js'; +import {NavigationBarState} from './state.js'; /** @soyCompatible */ export class NavigationBar extends LitElement implements NavigationBarState { diff --git a/components/navigation-bar/navigation-bar.ts b/components/navigation-bar/navigation-bar.ts index f0285f8f89b..87533d8e54e 100644 --- a/components/navigation-bar/navigation-bar.ts +++ b/components/navigation-bar/navigation-bar.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {NavigationBar} from './lib/navigation-bar'; -import {styles} from './lib/navigation-bar-styles.css'; +import {NavigationBar} from './lib/navigation-bar.js'; +import {styles} from './lib/navigation-bar-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/navigation-bar/test/md-navigation-bar_test.ts b/components/navigation-bar/test/md-navigation-bar_test.ts index 9cea92e5011..61adfe54685 100644 --- a/components/navigation-bar/test/md-navigation-bar_test.ts +++ b/components/navigation-bar/test/md-navigation-bar_test.ts @@ -4,16 +4,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {doesElementContainFocus} from '@material/mwc-base/utils'; +import {doesElementContainFocus} from '@material/mwc-base/utils.js'; import {KEY} from 'google3/third_party/javascript/material_components_web/dom/keyboard'; import {fixture, rafPromise, TestFixture} from 'google3/third_party/javascript/material_web_components/testing/helpers'; import * as hanbi from 'hanbi'; import {html} from 'lit'; -import {customElement} from 'lit/decorators'; -import {ifDefined} from 'lit/directives/if-defined'; +import {customElement} from 'lit/decorators.js'; +import {ifDefined} from 'lit/directives/if-defined.js'; -import {MdNavigationTab} from '../../navigation_tab/navigation-tab'; -import {MdNavigationBar} from '../navigation-bar'; +import {NavigationTabHarness} from '../../navigation_tab/harness.js'; +import {MdNavigationTab} from '../../navigation_tab/navigation-tab.js'; +import {MdNavigationBar} from '../navigation-bar.js'; @customElement('md-test-navigation-bar') class TestMdNavigationBar extends MdNavigationBar { @@ -115,14 +116,12 @@ describe('md-navigation-bar', () => { it('#handleNavigationTabInteraction () updates on navigation tab click', async () => { - const tab1 = element.children[0] as HTMLElement; - const tab2 = element.children[1] as HTMLElement; - const tab1Button = tab1.shadowRoot!.querySelector('button')!; - const tab2Button = tab2.shadowRoot!.querySelector('button')!; + const tab1Harness = new NavigationTabHarness(element.tabs[0]); + const tab2Harness = new NavigationTabHarness(element.tabs[1]); - tab1Button.click(); + await tab1Harness.click(); expect(element.activeIndex).toEqual(0); - tab2Button.click(); + await tab2Harness.click(); expect(element.activeIndex).toEqual(1); }); diff --git a/components/navigation-drawer/lib/_navigation-drawer-modal-theme.scss b/components/navigation-drawer/lib/_navigation-drawer-modal-theme.scss index 09a07106cc5..fbf7e6d5bdc 100644 --- a/components/navigation-drawer/lib/_navigation-drawer-modal-theme.scss +++ b/components/navigation-drawer/lib/_navigation-drawer-modal-theme.scss @@ -12,7 +12,7 @@ @use '../../sass/shape'; @use '../../elevation/lib/elevation-theme'; @use '../../sass/resolvers'; -@use '@material/theme/theme-color'; +@use '../../compat/theme/theme-color'; $light-theme: ( container-color: theme-color.$surface, diff --git a/components/navigation-drawer/lib/_navigation-drawer-theme.scss b/components/navigation-drawer/lib/_navigation-drawer-theme.scss index ea40c77475d..2749287559c 100644 --- a/components/navigation-drawer/lib/_navigation-drawer-theme.scss +++ b/components/navigation-drawer/lib/_navigation-drawer-theme.scss @@ -12,7 +12,7 @@ @use '../../sass/shape'; @use '../../elevation/lib/elevation-theme'; @use '../../sass/resolvers'; -@use '@material/theme/theme-color'; +@use '../../compat/theme/theme-color'; $light-theme: ( container-color: theme-color.$surface, diff --git a/components/navigation-drawer/lib/navigation-drawer-modal.ts b/components/navigation-drawer/lib/navigation-drawer-modal.ts index d6bab17f5e5..6b27ba3794b 100644 --- a/components/navigation-drawer/lib/navigation-drawer-modal.ts +++ b/components/navigation-drawer/lib/navigation-drawer-modal.ts @@ -4,14 +4,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {ariaProperty as legacyAriaProperty} from '@material/mwc-base/aria-property'; -import {observer} from '@material/mwc-base/observer'; +import {ariaProperty as legacyAriaProperty} from '@material/mwc-base/aria-property.js'; +import {observer} from '@material/mwc-base/observer.js'; import {html, LitElement, TemplateResult} from 'lit'; -import {property} from 'lit/decorators'; -import {classMap} from 'lit/directives/class-map'; -import {ifDefined} from 'lit/directives/if-defined'; +import {property} from 'lit/decorators.js'; +import {classMap} from 'lit/directives/class-map.js'; +import {ifDefined} from 'lit/directives/if-defined.js'; -import {ariaProperty} from '../../decorators/aria-property'; +import {ariaProperty} from '../../decorators/aria-property.js'; /** @soyCompatible */ export class NavigationDrawerModal extends LitElement { diff --git a/components/navigation-drawer/lib/navigation-drawer.ts b/components/navigation-drawer/lib/navigation-drawer.ts index 45f5a5c12f7..b7feaaccc70 100644 --- a/components/navigation-drawer/lib/navigation-drawer.ts +++ b/components/navigation-drawer/lib/navigation-drawer.ts @@ -4,14 +4,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {ariaProperty as legacyAriaProperty} from '@material/mwc-base/aria-property'; -import {observer} from '@material/mwc-base/observer'; +import {ariaProperty as legacyAriaProperty} from '@material/mwc-base/aria-property.js'; +import {observer} from '@material/mwc-base/observer.js'; import {html, LitElement, TemplateResult} from 'lit'; -import {property} from 'lit/decorators'; -import {classMap} from 'lit/directives/class-map'; -import {ifDefined} from 'lit/directives/if-defined'; +import {property} from 'lit/decorators.js'; +import {classMap} from 'lit/directives/class-map.js'; +import {ifDefined} from 'lit/directives/if-defined.js'; -import {ariaProperty} from '../../decorators/aria-property'; +import {ariaProperty} from '../../decorators/aria-property.js'; /** @soyCompatible */ export class NavigationDrawer extends LitElement { diff --git a/components/navigation-drawer/navigation-drawer-modal.ts b/components/navigation-drawer/navigation-drawer-modal.ts index d7eaef72ae3..b4ead43dad4 100644 --- a/components/navigation-drawer/navigation-drawer-modal.ts +++ b/components/navigation-drawer/navigation-drawer-modal.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {NavigationDrawerModal} from './lib/navigation-drawer-modal'; -import {styles} from './lib/navigation-drawer-modal-styles.css'; +import {NavigationDrawerModal} from './lib/navigation-drawer-modal.js'; +import {styles} from './lib/navigation-drawer-modal-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/navigation-drawer/navigation-drawer.ts b/components/navigation-drawer/navigation-drawer.ts index 9a8cadb365c..40a9549a738 100644 --- a/components/navigation-drawer/navigation-drawer.ts +++ b/components/navigation-drawer/navigation-drawer.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {customElement} from 'lit/decorators'; +import {customElement} from 'lit/decorators.js'; -import {NavigationDrawer} from './lib/navigation-drawer'; -import {styles} from './lib/navigation-drawer-styles.css'; +import {NavigationDrawer} from './lib/navigation-drawer.js'; +import {styles} from './lib/navigation-drawer-styles.css.js'; declare global { interface HTMLElementTagNameMap { diff --git a/components/navigation-tab/harness.ts b/components/navigation-tab/harness.ts index 7264a598c74..3bba5144fd3 100644 --- a/components/navigation-tab/harness.ts +++ b/components/navigation-tab/harness.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {Harness} from '../testing/harness'; +import {Harness} from '../testing/harness.js'; -import {NavigationTab} from './lib/navigation-tab'; +import {NavigationTab} from './lib/navigation-tab.js'; /** * Test harness for navigation tab elements. diff --git a/components/navigation-tab/lib/_navigation-tab-theme.scss b/components/navigation-tab/lib/_navigation-tab-theme.scss index 35f5137d7fe..d8d9f8a3fe5 100644 --- a/components/navigation-tab/lib/_navigation-tab-theme.scss +++ b/components/navigation-tab/lib/_navigation-tab-theme.scss @@ -13,10 +13,10 @@ @use '../../tokens/latest' as tokens; @use '../../badge/lib/badge-theme'; @use 'third_party/javascript/material_web_components/m3/ripple/ripple-theme'; -@use '@material/theme/state'; -@use '@material/theme/shadow-dom'; -@use '@material/theme/theme-color'; -@use '@material/theme/keys'; +@use '../../compat/theme/state'; +@use '../../compat/theme/shadow-dom'; +@use '../../compat/theme/theme-color'; +@use '../../compat/theme/keys'; @use '@material/typography/typography'; $_custom-property-prefix: 'navigation-bar'; diff --git a/components/navigation-tab/lib/navigation-tab.ts b/components/navigation-tab/lib/navigation-tab.ts index d05bea7246e..3ed156b59a5 100644 --- a/components/navigation-tab/lib/navigation-tab.ts +++ b/components/navigation-tab/lib/navigation-tab.ts @@ -4,20 +4,20 @@ * SPDX-License-Identifier: Apache-2.0 */ -import '../../badge/badge'; -import '../../focus/focus-ring'; +import '../../badge/badge.js'; +import '../../focus/focus-ring.js'; import {html, PropertyValues, TemplateResult} from 'lit'; -import {property, query, state} from 'lit/decorators'; -import {ClassInfo, classMap} from 'lit/directives/class-map'; -import {ifDefined} from 'lit/directives/if-defined'; +import {property, query, state} from 'lit/decorators.js'; +import {ClassInfo, classMap} from 'lit/directives/class-map.js'; +import {ifDefined} from 'lit/directives/if-defined.js'; -import {ActionElement, BeginPressConfig, EndPressConfig} from '../../action-element/action-element'; -import {ariaProperty} from '../../decorators/aria-property'; -import {pointerPress, shouldShowStrongFocus} from '../../focus/strong-focus'; -import {MdRipple} from '../../ripple/ripple'; +import {ActionElement, BeginPressConfig, EndPressConfig} from '../../action-element/action-element.js'; +import {ariaProperty} from '../../decorators/aria-property.js'; +import {pointerPress, shouldShowStrongFocus} from '../../focus/strong-focus.js'; +import {MdRipple} from '../../ripple/ripple.js'; -import {NavigationTabState} from './state'; +import {NavigationTabState} from './state.js'; /** @soyCompatible */ export class NavigationTab extends ActionElement implements NavigationTabState { @@ -60,7 +60,7 @@ export class NavigationTab extends ActionElement implements NavigationTabState { @clickmod="${this.handleClick}" @contextmenu="${this.handleContextMenu}" >${this.renderFocusRing()}${this.renderRipple()} -