From 328bf26834daa0e062c6eb7a0f527643eb19baf5 Mon Sep 17 00:00:00 2001 From: Kieran Nichols Date: Wed, 8 Nov 2023 09:30:12 -0500 Subject: [PATCH] [@next] refactor button component (#420) --- README.md | 7 +- package-lock.json | 12 +- src/dev/index.ejs | 2 +- src/dev/pages/banner/banner.ejs | 4 +- src/dev/pages/banner/banner.ts | 1 - src/dev/pages/bottom-sheet/bottom-sheet.ejs | 20 +- src/dev/pages/bottom-sheet/bottom-sheet.scss | 4 - src/dev/pages/bottom-sheet/bottom-sheet.ts | 1 - .../pages/busy-indicator/busy-indicator.ejs | 4 +- .../pages/busy-indicator/busy-indicator.ts | 1 - src/dev/pages/button/button.ejs | 80 +- src/dev/pages/button/button.html | 24 +- src/dev/pages/button/button.scss | 81 +- src/dev/pages/button/button.ts | 104 +- src/dev/pages/card/card.ejs | 8 +- src/dev/pages/card/card.ts | 1 - .../pages/dialog/dialog-template-basic.ejs | 8 +- src/dev/pages/dialog/dialog.ejs | 8 +- src/dev/pages/dialog/dialog.scss | 4 +- src/dev/pages/dialog/dialog.ts | 1 - src/dev/pages/file-picker/file-picker.ejs | 12 +- src/dev/pages/file-picker/file-picker.ts | 1 - .../keyboard-shortcut/keyboard-shortcut.ejs | 4 +- .../keyboard-shortcut/keyboard-shortcut.ts | 1 - src/dev/pages/menu/menu.ejs | 5 +- src/dev/pages/menu/menu.ts | 1 - src/dev/pages/page-state/page-state.ejs | 8 +- src/dev/pages/page-state/page-state.ts | 1 - src/dev/pages/popup/popup.ejs | 8 +- src/dev/pages/popup/popup.ts | 1 - src/dev/pages/radio/radio.ejs | 12 +- src/dev/pages/radio/radio.scss | 2 +- src/dev/pages/radio/radio.ts | 1 - src/dev/pages/select/select.ejs | 8 +- src/dev/pages/select/select.ts | 1 - src/dev/pages/stack/stack.ejs | 8 +- src/dev/pages/table/table.ts | 4 +- src/dev/pages/toast/toast.ejs | 4 +- src/dev/pages/toast/toast.ts | 1 - src/dev/pages/tooltip/tooltip.ejs | 6 +- src/dev/pages/tooltip/tooltip.ts | 1 - src/dev/pages/typography/typography.ejs | 2 +- src/dev/src/component-list.ts | 8 +- src/dev/src/partials/controls/button.ejs | 4 +- src/dev/src/shared.scss | 1 - src/dev/src/styles/_header.scss | 6 + src/dev/src/styles/_options.scss | 4 +- src/lib/app-bar/_mixins.scss | 6 +- src/lib/busy-indicator/_mixins.scss | 2 +- src/lib/busy-indicator/busy-indicator.html | 4 +- src/lib/busy-indicator/busy-indicator.scss | 1 - .../button-toggle/button-toggle/_mixins.scss | 33 +- src/lib/button/_button-base.scss | 178 --- src/lib/button/_button-filled-theme.scss | 55 - src/lib/button/_button-filled.scss | 66 -- src/lib/button/_button-outlined-theme.scss | 170 --- src/lib/button/_button-outlined.scss | 71 -- src/lib/button/_button-protected-theme.scss | 55 - src/lib/button/_button-protected.scss | 83 -- src/lib/button/_button-ripple.scss | 64 - src/lib/button/_button-shared-theme.scss | 442 ------- src/lib/button/_button-text-theme.scss | 55 - src/lib/button/_button-text.scss | 52 - src/lib/button/_button.mixins.scss | 143 --- src/lib/button/_configuration.scss | 11 + src/lib/button/_core.scss | 171 +++ src/lib/button/_mixins.scss | 178 --- src/lib/button/_token-utils.scss | 25 + src/lib/button/base/base-button-adapter.ts | 298 +++++ src/lib/button/base/base-button-constants.ts | 35 + src/lib/button/base/base-button-foundation.ts | 261 +++++ src/lib/button/base/base-button.ts | 119 ++ src/lib/button/build.json | 7 - src/lib/button/button-adapter.ts | 16 + src/lib/button/button-component-delegate.ts | 37 +- src/lib/button/button-constants.ts | 31 +- src/lib/button/button-foundation.ts | 57 + src/lib/button/button.html | 9 + src/lib/button/button.scss | 184 +++ src/lib/button/button.test.ts | 1042 +++++++++++++++++ src/lib/button/button.ts | 344 +++--- src/lib/button/forge-button.scss | 4 - src/lib/button/index.scss | 3 + src/lib/button/index.ts | 4 +- src/lib/calendar/calendar-dom-utils.ts | 28 +- src/lib/calendar/calendar.scss | 1 - src/lib/checkbox/_configuration.scss | 53 +- src/lib/checkbox/_core.scss | 63 +- src/lib/checkbox/_token-utils.scss | 25 + src/lib/checkbox/checkbox.scss | 12 +- src/lib/checkbox/index.scss | 1 + src/lib/chips/chip/_mixins.scss | 28 +- src/lib/circular-progress/_configuration.scss | 23 +- src/lib/circular-progress/_core.scss | 33 +- src/lib/circular-progress/_token-utils.scss | 25 + .../circular-progress/circular-progress.scss | 27 +- src/lib/circular-progress/index.scss | 1 + src/lib/constants.ts | 1 + src/lib/core/base/base-adapter.ts | 5 + src/lib/core/styles/_utils.scss | 51 +- src/lib/core/styles/spacing/index.scss | 7 + src/lib/core/styles/tokens/_token-utils.scss | 113 ++ .../core/styles/tokens/button/_tokens.scss | 111 ++ .../core/styles/tokens/checkbox/_tokens.scss | 5 +- .../tokens/focus-indicator/_tokens.scss | 2 +- src/lib/core/styles/tokens/index.scss | 1 - src/lib/core/styles/tokens/label/_tokens.scss | 7 - .../core/styles/tokens/spacing/_tokens.scss | 16 +- .../core/styles/tokens/switch/_tokens.scss | 1 - .../styles/tokens/tabs/tab-bar/_tokens.scss | 4 +- .../core/styles/tokens/tabs/tab/_tokens.scss | 2 +- src/lib/core/styles/typography/index.scss | 27 +- src/lib/core/utils/feature-detection.ts | 7 + src/lib/core/utils/index.ts | 1 + src/lib/core/utils/reflect-utils.ts | 12 + .../file-picker-component-delegate.ts | 2 +- src/lib/focus-indicator/_animations.scss | 10 +- src/lib/focus-indicator/_configuration.scss | 17 +- src/lib/focus-indicator/_core.scss | 45 +- src/lib/focus-indicator/_token-utils.scss | 26 + src/lib/focus-indicator/focus-indicator.scss | 34 +- .../focus-indicator/focus-indicator.test.ts | 2 +- src/lib/focus-indicator/index.scss | 1 + src/lib/forge.scss | 1 - src/lib/linear-progress/_animations.scss | 4 +- src/lib/linear-progress/_configuration.scss | 31 +- src/lib/linear-progress/_core.scss | 33 +- src/lib/linear-progress/_token-utils.scss | 26 + src/lib/linear-progress/index.scss | 1 + src/lib/linear-progress/linear-progress.scss | 27 +- src/lib/list/list-item/_configuration.scss | 66 +- src/lib/list/list-item/_core.scss | 107 +- src/lib/list/list-item/_token-utils.scss | 25 + src/lib/list/list-item/index.scss | 1 + src/lib/list/list-item/list-item.scss | 13 +- src/lib/list/list/_configuration.scss | 6 +- src/lib/list/list/_core.scss | 11 +- src/lib/list/list/_token-utils.scss | 25 + src/lib/list/list/index.scss | 1 + src/lib/list/list/list.scss | 11 + src/lib/menu/menu-constants.ts | 2 +- src/lib/profile-card/profile-card-adapter.ts | 9 +- src/lib/profile-card/profile-card.html | 8 +- src/lib/profile-card/profile-card.scss | 1 - src/lib/slider/_configuration.scss | 55 +- src/lib/slider/_core.scss | 121 +- src/lib/slider/_token-utils.scss | 26 + src/lib/slider/index.scss | 1 + src/lib/slider/slider-constants.ts | 6 +- src/lib/slider/slider.scss | 26 +- src/lib/slider/slider.test.ts | 42 +- src/lib/state-layer/_configuration.scss | 11 +- src/lib/state-layer/_core.scss | 27 +- src/lib/state-layer/_token-utils.scss | 26 + src/lib/state-layer/index.scss | 1 + src/lib/state-layer/state-layer-adapter.ts | 1 + src/lib/state-layer/state-layer-foundation.ts | 2 +- src/lib/state-layer/state-layer.scss | 7 + src/lib/switch/_configuration.scss | 90 +- src/lib/switch/_core.scss | 147 ++- src/lib/switch/_token-utils.scss | 25 + src/lib/switch/index.scss | 1 + src/lib/switch/switch.scss | 35 +- src/lib/switch/switch.ts | 1 - src/lib/tabs/tab-bar/_configuration.scss | 7 +- src/lib/tabs/tab-bar/_core.scss | 35 +- src/lib/tabs/tab-bar/_token-utils.scss | 25 + src/lib/tabs/tab-bar/index.scss | 3 +- src/lib/tabs/tab-bar/tab-bar.scss | 94 +- src/lib/tabs/tab/_configuration.scss | 56 +- src/lib/tabs/tab/_core.scss | 59 +- src/lib/tabs/tab/_token-utils.scss | 25 + src/lib/tabs/tab/index.scss | 1 + src/lib/tabs/tab/tab.html | 4 +- src/lib/tabs/tab/tab.scss | 131 ++- src/lib/toast/_mixins.scss | 2 +- src/lib/toast/toast-adapter.ts | 7 +- src/lib/toast/toast.html | 4 +- src/lib/toast/toast.scss | 1 - src/lib/typography/forge-typography.scss | 7 +- .../components/backdrop/backdrop.stories.tsx | 4 +- .../src/components/banner/banner.stories.tsx | 5 +- .../banner/code/banner-combined-full.ts | 4 +- .../bottom-sheet/bottom-sheet.stories.tsx | 8 +- .../bottom-sheet/code/bottom-sheet-default.ts | 4 +- .../busy-indicator/busy-indicator.stories.tsx | 4 +- .../src/components/button/button-args.ts | 32 +- .../src/components/button/button.stories.tsx | 16 +- .../components/button/code/button-default.ts | 12 +- .../src/components/card/card.stories.tsx | 16 +- .../src/components/card/code/card-scaffold.ts | 8 +- .../src/components/card/code/card-styled.ts | 8 +- .../components/dialog/code/dialog-complex.ts | 8 +- .../components/dialog/code/dialog-simple.ts | 8 +- .../src/components/dialog/dialog.stories.tsx | 20 +- .../file-picker/code/file-picker-compact.ts | 4 +- .../file-picker/code/file-picker-default.ts | 4 +- .../file-picker/file-picker.stories.tsx | 4 +- .../code/keyboard-shortcut-basic.ts | 4 +- .../keyboard-shortcut/keyboard-shortcut.mdx | 4 +- .../keyboard-shortcut.stories.tsx | 8 +- .../src/components/menu/code/menu-default.ts | 4 +- .../src/components/menu/menu.stories.tsx | 4 +- .../page-state/code/page-state-default.ts | 8 +- .../page-state/page-state.stories.tsx | 8 +- src/stories/src/components/popup/popup.mdx | 8 +- .../src/components/popup/popup.stories.tsx | 4 +- .../components/shared/sidesheet-filter.tsx | 6 +- .../src/components/split-view/split-view.mdx | 4 +- .../components/toast/code/toast-default.ts | 4 +- .../src/components/toast/toast.stories.tsx | 4 +- .../tooltip/code/tooltip-default.ts | 6 +- .../components/tooltip/tooltip.stories.tsx | 6 +- src/stories/src/mock/document-filters.tsx | 4 +- src/test/spec/banner/banner.fixture.html | 4 +- src/test/spec/banner/banner.spec.ts | 8 +- src/test/spec/button/button.spec.ts | 248 ---- src/test/spec/date-picker/date-picker.spec.ts | 11 +- .../date-range-picker.spec.ts | 11 +- 219 files changed, 4243 insertions(+), 3451 deletions(-) delete mode 100644 src/lib/button/_button-base.scss delete mode 100644 src/lib/button/_button-filled-theme.scss delete mode 100644 src/lib/button/_button-filled.scss delete mode 100644 src/lib/button/_button-outlined-theme.scss delete mode 100644 src/lib/button/_button-outlined.scss delete mode 100644 src/lib/button/_button-protected-theme.scss delete mode 100644 src/lib/button/_button-protected.scss delete mode 100644 src/lib/button/_button-ripple.scss delete mode 100644 src/lib/button/_button-shared-theme.scss delete mode 100644 src/lib/button/_button-text-theme.scss delete mode 100644 src/lib/button/_button-text.scss delete mode 100644 src/lib/button/_button.mixins.scss create mode 100644 src/lib/button/_configuration.scss create mode 100644 src/lib/button/_core.scss delete mode 100644 src/lib/button/_mixins.scss create mode 100644 src/lib/button/_token-utils.scss create mode 100644 src/lib/button/base/base-button-adapter.ts create mode 100644 src/lib/button/base/base-button-constants.ts create mode 100644 src/lib/button/base/base-button-foundation.ts create mode 100644 src/lib/button/base/base-button.ts delete mode 100644 src/lib/button/build.json create mode 100644 src/lib/button/button-adapter.ts create mode 100644 src/lib/button/button-foundation.ts create mode 100644 src/lib/button/button.html create mode 100644 src/lib/button/button.scss create mode 100644 src/lib/button/button.test.ts delete mode 100644 src/lib/button/forge-button.scss create mode 100644 src/lib/button/index.scss create mode 100644 src/lib/checkbox/_token-utils.scss create mode 100644 src/lib/circular-progress/_token-utils.scss create mode 100644 src/lib/core/styles/tokens/_token-utils.scss create mode 100644 src/lib/core/styles/tokens/button/_tokens.scss delete mode 100644 src/lib/core/styles/tokens/label/_tokens.scss create mode 100644 src/lib/core/utils/feature-detection.ts create mode 100644 src/lib/focus-indicator/_token-utils.scss create mode 100644 src/lib/linear-progress/_token-utils.scss create mode 100644 src/lib/list/list-item/_token-utils.scss create mode 100644 src/lib/list/list/_token-utils.scss create mode 100644 src/lib/slider/_token-utils.scss create mode 100644 src/lib/state-layer/_token-utils.scss create mode 100644 src/lib/switch/_token-utils.scss create mode 100644 src/lib/tabs/tab-bar/_token-utils.scss create mode 100644 src/lib/tabs/tab/_token-utils.scss delete mode 100644 src/test/spec/button/button.spec.ts diff --git a/README.md b/README.md index dc3da8291..74c304da8 100644 --- a/README.md +++ b/README.md @@ -100,9 +100,6 @@ The `forge.css` file contains other stylesheets that you may or may not need. We /// Include the theme and typography styles @use '@tylertech/forge/dist/theme/forge-theme.css'; @use '@tylertech/forge/dist/typography/forge-typography.css'; - -/// Include styles for elements -@use '@tylertech/forge/dist/button/forge-button.css'; ``` Additionally, apply the `forge-typography` class to a root element (typically the ``): @@ -143,9 +140,7 @@ Now the text-field and button components can be used anywhere in the DOM: - - - +Button ``` Don't forget to also add the `forge-typography` class to the `` element to ensure your application inherits the default typography styles: diff --git a/package-lock.json b/package-lock.json index 74fc61215..54d05fc65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7121,9 +7121,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001527", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001527.tgz", - "integrity": "sha512-YkJi7RwPgWtXVSgK4lG9AHH57nSzvvOp9MesgXmw4Q7n0C3H04L0foHqfxcmSAm5AcWb8dW9AYj2tR7/5GnddQ==", + "version": "1.0.30001561", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz", + "integrity": "sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==", "dev": true, "funding": [ { @@ -26652,9 +26652,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001527", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001527.tgz", - "integrity": "sha512-YkJi7RwPgWtXVSgK4lG9AHH57nSzvvOp9MesgXmw4Q7n0C3H04L0foHqfxcmSAm5AcWb8dW9AYj2tR7/5GnddQ==", + "version": "1.0.30001561", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz", + "integrity": "sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==", "dev": true }, "canonical-path": { diff --git a/src/dev/index.ejs b/src/dev/index.ejs index 231c31127..582a2df64 100644 --- a/src/dev/index.ejs +++ b/src/dev/index.ejs @@ -3,7 +3,7 @@ - diff --git a/src/dev/pages/banner/banner.ejs b/src/dev/pages/banner/banner.ejs index 6645469e9..82f21a110 100644 --- a/src/dev/pages/banner/banner.ejs +++ b/src/dev/pages/banner/banner.ejs @@ -1,9 +1,7 @@ diff --git a/src/dev/pages/banner/banner.ts b/src/dev/pages/banner/banner.ts index a9c9f7440..fc91b1bb8 100644 --- a/src/dev/pages/banner/banner.ts +++ b/src/dev/pages/banner/banner.ts @@ -2,7 +2,6 @@ import '$src/shared'; import { IconRegistry } from '@tylertech/forge/icon'; import '@tylertech/forge/banner'; import '@tylertech/forge/button'; -import '@tylertech/forge/button/forge-button.scss'; import { tylIconAddAlert } from '@tylertech/tyler-icons/standard'; import type { IBannerComponent, ISelectComponent, ISwitchComponent } from '@tylertech/forge'; diff --git a/src/dev/pages/bottom-sheet/bottom-sheet.ejs b/src/dev/pages/bottom-sheet/bottom-sheet.ejs index b21b5b18c..89f28f7a5 100644 --- a/src/dev/pages/bottom-sheet/bottom-sheet.ejs +++ b/src/dev/pages/bottom-sheet/bottom-sheet.ejs @@ -1,11 +1,7 @@
- - - + Show standard bottom sheet - - - + Show scrollable bottom sheet
@@ -14,9 +10,7 @@ @@ -38,12 +32,8 @@

Lorem ipsum dolor sit amet consectetur adipisicing elit. Officia deleniti id sint recusandae minus ullam aliquam voluptatibus sunt consequuntur, ab laborum nesciunt enim dolorem! Quas corrupti itaque est accusantium rerum.

- - - - - - + Disagree + Agree
diff --git a/src/dev/pages/bottom-sheet/bottom-sheet.scss b/src/dev/pages/bottom-sheet/bottom-sheet.scss index 174036a43..ae72b9714 100644 --- a/src/dev/pages/bottom-sheet/bottom-sheet.scss +++ b/src/dev/pages/bottom-sheet/bottom-sheet.scss @@ -1,9 +1,5 @@ .demo { forge-button { width: 256px; - - button { - width: 256px; - } } } diff --git a/src/dev/pages/bottom-sheet/bottom-sheet.ts b/src/dev/pages/bottom-sheet/bottom-sheet.ts index 53afccf73..26000ff85 100644 --- a/src/dev/pages/bottom-sheet/bottom-sheet.ts +++ b/src/dev/pages/bottom-sheet/bottom-sheet.ts @@ -2,7 +2,6 @@ import '$src/shared'; import '@tylertech/forge/bottom-sheet'; import { IBottomSheetComponent } from '@tylertech/forge/bottom-sheet'; import '@tylertech/forge/button'; -import '@tylertech/forge/button/forge-button.scss'; import '@tylertech/forge/dialog/forge-dialog-utils.scss'; import './bottom-sheet.scss'; diff --git a/src/dev/pages/busy-indicator/busy-indicator.ejs b/src/dev/pages/busy-indicator/busy-indicator.ejs index b38d90acd..4e5bd8ca9 100644 --- a/src/dev/pages/busy-indicator/busy-indicator.ejs +++ b/src/dev/pages/busy-indicator/busy-indicator.ejs @@ -1,7 +1,5 @@
- - - + Show busy indicator
diff --git a/src/dev/pages/busy-indicator/busy-indicator.ts b/src/dev/pages/busy-indicator/busy-indicator.ts index 34bb445b8..4f86688fa 100644 --- a/src/dev/pages/busy-indicator/busy-indicator.ts +++ b/src/dev/pages/busy-indicator/busy-indicator.ts @@ -3,7 +3,6 @@ import type { IBusyIndicatorComponent, ISelectComponent, ISwitchComponent } from import '@tylertech/forge/busy-indicator'; import '@tylertech/forge/busy-indicator/forge-busy-indicator.scss'; import '@tylertech/forge/button'; -import '@tylertech/forge/button/forge-button.scss'; import './busy-indicator.scss'; const titleInput = document.querySelector('#title-input') as HTMLInputElement; diff --git a/src/dev/pages/button/button.ejs b/src/dev/pages/button/button.ejs index 2cdf77aff..c77bb49d6 100644 --- a/src/dev/pages/button/button.ejs +++ b/src/dev/pages/button/button.ejs @@ -1,34 +1,60 @@ -
- - +
+ Default button + Raised button + Flat button + Outlined button + + w/<span> container - - + + + + w/Leading icon - - - - - - - - - - - + + + w/Trailing icon + - - - w/anchor tag - - + + + w/Anchor link + + + Link button + + Custom button + +
+

Form example

+
+ + + +
+ Submit + Reset +
+
+
+ +
+

Popover target

+ +
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Ipsam blanditiis cum quod, velit, architecto consequatur commodi sint odit non deleniti qui? Accusamus expedita, exercitationem laborum architecto facere quasi rem! Voluptate!
+
+ +
+

Dialog

+ Show dialog + +

Lorem ipsum dolor sit amet consectetur adipisicing elit. Accusamus architecto sunt esse voluptatum possimus magnam omnis ipsum commodi neque maxime ab facere reiciendis, ex distinctio soluta qui maiores et aliquam.

+
+ Close +
+
+
diff --git a/src/dev/pages/button/button.html b/src/dev/pages/button/button.html index 2a4eb4a6b..3f928cfc8 100644 --- a/src/dev/pages/button/button.html +++ b/src/dev/pages/button/button.html @@ -4,8 +4,28 @@ title: 'Button', includePath: './pages/button/button.ejs', options: [ - { type: 'switch', label: 'Disabled', id: 'disabled-switch' }, - { type: 'switch', label: 'Dense', id: 'dense-switch' } + { type: 'switch', label: 'Disabled', id: 'opt-disabled' }, + { type: 'switch', label: 'Dense', id: 'opt-dense' }, + { type: 'switch', label: 'Pill', id: 'opt-pill' }, + { type: 'switch', label: 'Anchor', id: 'opt-anchor' }, + { type: 'switch', label: 'Popover icon', id: 'opt-popover-icon' }, + { type: 'switch', label: 'Form prevent default', id: 'opt-form-prevent' }, + { type: 'switch', label: 'Anchor prevent default', id: 'opt-href-prevent' }, + { + type: 'select', + label: 'Theme', + id: 'opt-theme', + defaultValue: 'primary', + options: [ + { label: 'Primary', value: 'primary' }, + { label: 'Secondary', value: 'secondary' }, + { label: 'Tertiary', value: 'tertiary' }, + { label: 'Success', value: 'success' }, + { label: 'Error', value: 'error' }, + { label: 'Warning', value: 'warning' }, + { label: 'Info', value: 'info' } + ] + } ] } }) diff --git a/src/dev/pages/button/button.scss b/src/dev/pages/button/button.scss index 7102b123e..aa6aa1c5e 100644 --- a/src/dev/pages/button/button.scss +++ b/src/dev/pages/button/button.scss @@ -1,5 +1,84 @@ @use '../../src/shared'; +@use '../../../lib/button'; +@use '../../../lib/focus-indicator'; +@use '../../../lib/core/styles/elevation'; -button, a { +.button-demo { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 16px; +} + +forge-button { width: 256px; } + +form:has(forge-button) forge-button { + width: auto; +} + +#my-popover { + max-inline-size: 500px; +} + +.test-form { + display: flex; + flex-direction: column; + gap: 8px; +} + +h3 { + margin-block: 16px; +} + +.custom-button { + --_custom-button-background: var(--forge-theme-primary); + + @include button.provide-theme(( + height: 56px, + dense-height: 40px, + focus-indicator-offset: 4px, + shadow: elevation.value(2), + hover-shadow: elevation.value(4), + active-shadow: elevation.value(8), + color: var(--forge-theme-on-primary), + background: var(--_custom-button-background), + disabled-background: linear-gradient(to right, #aaa 0%, #ccc 51%, #aaa 100%), + disabled-shadow: none + )); + + &::after { + content: ''; + position: absolute; + top: 2px; + left: 2px; + width: calc(100% - 4px); + height: 50%; + pointer-events: none; + background: linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.2)); + } + + &[popover-icon] { + @include button.provide-theme(( justify: space-between )); + } + + &[theme=secondary] { + --_custom-button-background: var(--forge-theme-secondary); + } + &[theme=tertiary] { + --_custom-button-background: var(--forge-theme-primary); + } + &[theme=success] { + --_custom-button-background: var(--forge-theme-success); + } + &[theme=error] { + --_custom-button-background: var(--forge-theme-error); + } + &[theme=warning] { + --_custom-button-background: var(--forge-theme-warning); + } + &[theme=info] { + --_custom-button-background: var(--forge-theme-info); + } +} diff --git a/src/dev/pages/button/button.ts b/src/dev/pages/button/button.ts index fdba833e8..4e2f67887 100644 --- a/src/dev/pages/button/button.ts +++ b/src/dev/pages/button/button.ts @@ -1,40 +1,88 @@ -// Styles -import './button.scss'; -import '@tylertech/forge/button/forge-button.scss'; - -// Components -import '@tylertech/forge/button'; - -// Icons +import '$src/shared'; +import type { ISwitchComponent } from '@tylertech/forge/switch'; +import type { IButtonComponent } from '@tylertech/forge/button'; +import type { ISelectComponent } from '@tylertech/forge/select'; import { IconRegistry } from '@tylertech/forge/icon'; import { tylIconFavorite, tylIconOpenInNew } from '@tylertech/tyler-icons/standard'; import { tylIconForgeLogo } from '@tylertech/tyler-icons/custom'; - -import '$src/shared'; -import type { ButtonComponent } from '@tylertech/forge/button'; -import { isDefined } from '@tylertech/forge-core'; -import type { ISwitchComponent } from '@tylertech/forge/switch'; +import '@tylertech/forge/button'; +import './button.scss'; IconRegistry.define([tylIconForgeLogo, tylIconFavorite, tylIconOpenInNew]); -function getButtonElements(): NodeListOf { - return document.querySelectorAll('.content forge-button'); -} +const formPreventToggle = document.querySelector('#opt-form-prevent') as ISwitchComponent; +const hrefPreventToggle = document.querySelector('#opt-href-prevent') as ISwitchComponent; + +const submitBtn = document.querySelector('#submit-btn') as HTMLButtonElement; +submitBtn.addEventListener('click', evt => { + if (formPreventToggle.on) { + evt.preventDefault(); + } + console.log('submit button click', evt); +}); + +const resetBtn = document.querySelector('#reset-btn') as HTMLButtonElement; +resetBtn.addEventListener('click', evt => { + if (formPreventToggle.on) { + evt.preventDefault(); + } + console.log('reset button click', evt); +}); + +const testForm = document.querySelector('#test-btn-form') as HTMLFormElement; +testForm.addEventListener('submit', evt => { + console.log('Form submitted!', evt.submitter); +}); + +const hrefBtn = document.querySelector('forge-button[href]') as HTMLAnchorElement; +hrefBtn.addEventListener('click', evt => { + if (hrefPreventToggle.on) { + evt.preventDefault(); + } + console.log('href button click', evt); +}); + +const popoverBtn = document.querySelector('#popover-button') as HTMLButtonElement; +const popoverEl = document.querySelector('#my-popover') as HTMLElement; +popoverEl.addEventListener('toggle', ({ newState }: any) => { // TODO: remove `any` when TypeScript version is upgraded for latest DOM typings + popoverBtn.setAttribute('aria-expanded', `${newState === 'open'}`); +}); + +const showDialogBtn = document.querySelector('#show-dialog-btn') as HTMLButtonElement; +showDialogBtn.addEventListener('click', () => { + const dialog = document.querySelector('#test-dialog') as HTMLDialogElement; + dialog.showModal(); +}); + +const allButtons = Array.from(document.querySelectorAll('.content forge-button')); +allButtons.forEach(btn => btn.addEventListener('click', evt => console.log('click', evt))); -const disabledToggle = document.querySelector('#disabled-switch') as ISwitchComponent; +const disabledToggle = document.querySelector('#opt-disabled') as ISwitchComponent; disabledToggle.addEventListener('forge-switch-change', ({ detail: selected }) => { - const buttons = getButtonElements(); - buttons.forEach(forgeButton => { - const buttonEl = forgeButton.querySelector('button') as HTMLButtonElement; - buttonEl.disabled = selected; - }); + allButtons.forEach(btn => btn.toggleAttribute('disabled', selected)); }); -const denseToggle = document.querySelector('#dense-switch') as ISwitchComponent; +const denseToggle = document.querySelector('#opt-dense') as ISwitchComponent; denseToggle.addEventListener('forge-switch-change', ({ detail: selected }) => { - const buttons = getButtonElements(); - buttons.forEach(forgeButton => { - const isChecked = selected; - forgeButton.type = isDefined(forgeButton.type) ? forgeButton.type.replace(/(?:-?dense)?$/, isChecked ? '-dense' : '') : isChecked ? 'dense' : ''; - }); + allButtons.forEach(btn => btn.dense = selected); +}); + +const pillToggle = document.querySelector('#opt-pill') as ISwitchComponent; +pillToggle.addEventListener('forge-switch-change', ({ detail: selected }) => { + allButtons.forEach(btn => btn.pill = selected); +}); + +const anchorToggle = document.querySelector('#opt-anchor') as ISwitchComponent; +anchorToggle.addEventListener('forge-switch-change', ({ detail: selected }) => { + allButtons.filter(b => !b.href).forEach(btn => btn.anchor = selected); +}); + +const popoverIconToggle = document.querySelector('#opt-popover-icon') as ISwitchComponent; +popoverIconToggle.addEventListener('forge-switch-change', ({ detail: selected }) => { + allButtons.forEach(btn => btn.popoverIcon = selected); +}); + +const themeSelect = document.querySelector('#opt-theme') as ISelectComponent; +themeSelect.addEventListener('change', ({ detail }) => { + allButtons.forEach(btn => btn.setAttribute('theme', detail)); }); diff --git a/src/dev/pages/card/card.ejs b/src/dev/pages/card/card.ejs index 450b50892..622722559 100644 --- a/src/dev/pages/card/card.ejs +++ b/src/dev/pages/card/card.ejs @@ -19,12 +19,8 @@ diff --git a/src/dev/pages/card/card.ts b/src/dev/pages/card/card.ts index afbe797d5..55d78d827 100644 --- a/src/dev/pages/card/card.ts +++ b/src/dev/pages/card/card.ts @@ -2,7 +2,6 @@ import '$src/shared'; import '@tylertech/forge/card'; import '@tylertech/forge/scaffold'; import '@tylertech/forge/button'; -import '@tylertech/forge/button/forge-button.scss'; import '@tylertech/forge/icon-button'; import '@tylertech/forge/icon-button/forge-icon-button.scss'; import './card.scss'; diff --git a/src/dev/pages/dialog/dialog-template-basic.ejs b/src/dev/pages/dialog/dialog-template-basic.ejs index 6ee7de002..d596de848 100644 --- a/src/dev/pages/dialog/dialog-template-basic.ejs +++ b/src/dev/pages/dialog/dialog-template-basic.ejs @@ -29,12 +29,8 @@
- - - - - - + Close + Apply
diff --git a/src/dev/pages/dialog/dialog.ejs b/src/dev/pages/dialog/dialog.ejs index 19d86d04f..a1d68e68e 100644 --- a/src/dev/pages/dialog/dialog.ejs +++ b/src/dev/pages/dialog/dialog.ejs @@ -1,11 +1,7 @@
- - - + Show inline dialog - - - + Show dynamic dialog
diff --git a/src/dev/pages/dialog/dialog.scss b/src/dev/pages/dialog/dialog.scss index 8e7530967..03b750bfb 100644 --- a/src/dev/pages/dialog/dialog.scss +++ b/src/dev/pages/dialog/dialog.scss @@ -24,6 +24,6 @@ overflow-y: auto; } -.footer-toolbar forge-button:first-child { - margin-right: 8px +.footer-toolbar forge-button { + margin-right: 8px; } diff --git a/src/dev/pages/dialog/dialog.ts b/src/dev/pages/dialog/dialog.ts index e1c705daf..84ec17d0c 100644 --- a/src/dev/pages/dialog/dialog.ts +++ b/src/dev/pages/dialog/dialog.ts @@ -3,7 +3,6 @@ import '@tylertech/forge/dialog'; import '@tylertech/forge/toolbar'; import '@tylertech/forge/scaffold'; import '@tylertech/forge/button'; -import '@tylertech/forge/button/forge-button.scss'; import { IconRegistry, IDialogComponent, IDialogMoveEventData, ISwitchComponent } from '@tylertech/forge'; import './dialog.scss'; import { tylIconClose } from '@tylertech/tyler-icons/standard'; diff --git a/src/dev/pages/file-picker/file-picker.ejs b/src/dev/pages/file-picker/file-picker.ejs index 83df23425..231835b13 100644 --- a/src/dev/pages/file-picker/file-picker.ejs +++ b/src/dev/pages/file-picker/file-picker.ejs @@ -3,9 +3,7 @@ Drag files here or Secondary text here - - - + Select files
    @@ -13,9 +11,7 @@

    Compact

    - - - + Select files
      @@ -28,9 +24,7 @@ borderless> Drag files here or click the button above to manually select files - - - + Select files diff --git a/src/dev/pages/file-picker/file-picker.ts b/src/dev/pages/file-picker/file-picker.ts index 9c8744369..d40728b62 100644 --- a/src/dev/pages/file-picker/file-picker.ts +++ b/src/dev/pages/file-picker/file-picker.ts @@ -1,7 +1,6 @@ import '$src/shared'; import '@tylertech/forge/file-picker'; import '@tylertech/forge/button'; -import '@tylertech/forge/button/forge-button.scss'; import type { IFilePickerComponent, ISwitchComponent } from '@tylertech/forge'; const filePicker = document.querySelector('#file-picker-default') as IFilePickerComponent; diff --git a/src/dev/pages/keyboard-shortcut/keyboard-shortcut.ejs b/src/dev/pages/keyboard-shortcut/keyboard-shortcut.ejs index 0ac07d46b..0532547f3 100644 --- a/src/dev/pages/keyboard-shortcut/keyboard-shortcut.ejs +++ b/src/dev/pages/keyboard-shortcut/keyboard-shortcut.ejs @@ -1,8 +1,6 @@
      - - - + Shortcut target
      diff --git a/src/dev/pages/keyboard-shortcut/keyboard-shortcut.ts b/src/dev/pages/keyboard-shortcut/keyboard-shortcut.ts index 5e01f4989..f194f7a8a 100644 --- a/src/dev/pages/keyboard-shortcut/keyboard-shortcut.ts +++ b/src/dev/pages/keyboard-shortcut/keyboard-shortcut.ts @@ -1,7 +1,6 @@ import '$src/shared'; import '@tylertech/forge/keyboard-shortcut'; import '@tylertech/forge/button'; -import '@tylertech/forge/button/forge-button.scss'; import '@tylertech/forge/text-field'; import { IKeyboardShortcutComponent } from '@tylertech/forge/keyboard-shortcut'; import { ISwitchComponent } from '@tylertech/forge'; diff --git a/src/dev/pages/menu/menu.ejs b/src/dev/pages/menu/menu.ejs index 80eb3a82d..16022522f 100644 --- a/src/dev/pages/menu/menu.ejs +++ b/src/dev/pages/menu/menu.ejs @@ -1,7 +1,6 @@ - - - + + Show menu diff --git a/src/dev/pages/menu/menu.ts b/src/dev/pages/menu/menu.ts index 584cc3644..2b1ed9133 100644 --- a/src/dev/pages/menu/menu.ts +++ b/src/dev/pages/menu/menu.ts @@ -5,7 +5,6 @@ import type { IListItemComponent, IMenuComponent, IMenuOption, ISwitchComponent import '@tylertech/forge/menu'; import '@tylertech/forge/divider'; import '@tylertech/forge/button'; -import '@tylertech/forge/button/forge-button.scss'; import { tylIconArrowBack, tylIconArrowForward, tylIconHelp, tylIconLoop, tylIconPerson, tylIconSettings } from '@tylertech/tyler-icons/standard'; IconRegistry.define([ diff --git a/src/dev/pages/page-state/page-state.ejs b/src/dev/pages/page-state/page-state.ejs index 83100eb8e..b10a194ea 100644 --- a/src/dev/pages/page-state/page-state.ejs +++ b/src/dev/pages/page-state/page-state.ejs @@ -2,12 +2,8 @@
      Nothing but tumbleweeds here...
      Even our best explorer couldn't find the page you're looking for. It might have been removed or you may have mistyped the URL.
      - - - - - - + Go back + Refresh diff --git a/src/dev/pages/page-state/page-state.ts b/src/dev/pages/page-state/page-state.ts index 487794285..5f61a0ade 100644 --- a/src/dev/pages/page-state/page-state.ts +++ b/src/dev/pages/page-state/page-state.ts @@ -1,4 +1,3 @@ import '$src/shared'; import '@tylertech/forge/page-state'; import '@tylertech/forge/button'; -import '@tylertech/forge/button/forge-button.scss'; diff --git a/src/dev/pages/popup/popup.ejs b/src/dev/pages/popup/popup.ejs index f331cfc96..458452532 100644 --- a/src/dev/pages/popup/popup.ejs +++ b/src/dev/pages/popup/popup.ejs @@ -1,6 +1,4 @@ - - - +Show popup diff --git a/src/dev/pages/popup/popup.ts b/src/dev/pages/popup/popup.ts index 908d0f17b..77853b6e6 100644 --- a/src/dev/pages/popup/popup.ts +++ b/src/dev/pages/popup/popup.ts @@ -4,7 +4,6 @@ import '@tylertech/forge/select'; import '@tylertech/forge/text-field'; import '@tylertech/forge/divider'; import '@tylertech/forge/button'; -import '@tylertech/forge/button/forge-button.scss'; import type { IPopupComponent, IPopupPositionEventData, ISelectComponent } from '@tylertech/forge'; let popupElement: IPopupComponent | undefined; diff --git a/src/dev/pages/radio/radio.ejs b/src/dev/pages/radio/radio.ejs index d2f25b4d1..5eec17672 100644 --- a/src/dev/pages/radio/radio.ejs +++ b/src/dev/pages/radio/radio.ejs @@ -72,15 +72,9 @@
      - - - - - - - - - + Next + Add radio + Remove radio
      diff --git a/src/dev/pages/radio/radio.scss b/src/dev/pages/radio/radio.scss index 6865c438d..74a4160ca 100644 --- a/src/dev/pages/radio/radio.scss +++ b/src/dev/pages/radio/radio.scss @@ -2,7 +2,7 @@ display: flex; gap: 8px; - forge-button > button { + forge-button { width: 150px; } } \ No newline at end of file diff --git a/src/dev/pages/radio/radio.ts b/src/dev/pages/radio/radio.ts index 8099e14b2..b0074efd4 100644 --- a/src/dev/pages/radio/radio.ts +++ b/src/dev/pages/radio/radio.ts @@ -2,7 +2,6 @@ import '$src/shared'; import '@tylertech/forge/radio'; import '@tylertech/forge/divider'; import '@tylertech/forge/button'; -import '@tylertech/forge/button/forge-button.scss'; import './radio.scss'; const radioGroup = document.getElementById('example-radio-group'); diff --git a/src/dev/pages/select/select.ejs b/src/dev/pages/select/select.ejs index 3e2af8714..be50a185f 100644 --- a/src/dev/pages/select/select.ejs +++ b/src/dev/pages/select/select.ejs @@ -20,11 +20,9 @@

      Select dropdown

      - - + + Choose... + - - - - - - + Cancel + Save diff --git a/src/dev/pages/table/table.ts b/src/dev/pages/table/table.ts index 5403cd296..93d94be3f 100644 --- a/src/dev/pages/table/table.ts +++ b/src/dev/pages/table/table.ts @@ -376,9 +376,7 @@ function getSelectAllTemplate(): string { - - - + Custom `; } diff --git a/src/dev/pages/toast/toast.ejs b/src/dev/pages/toast/toast.ejs index 041514962..4710fb397 100644 --- a/src/dev/pages/toast/toast.ejs +++ b/src/dev/pages/toast/toast.ejs @@ -1,5 +1,3 @@ - - - +Show toast diff --git a/src/dev/pages/toast/toast.ts b/src/dev/pages/toast/toast.ts index 8b8eda95b..24b7c56aa 100644 --- a/src/dev/pages/toast/toast.ts +++ b/src/dev/pages/toast/toast.ts @@ -1,7 +1,6 @@ import '$src/shared'; import type { ISelectComponent, ISwitchComponent, IToastComponent } from '@tylertech/forge'; import '@tylertech/forge/button'; -import '@tylertech/forge/button/forge-button.scss'; import '@tylertech/forge/toast'; import './toast.scss'; diff --git a/src/dev/pages/tooltip/tooltip.ejs b/src/dev/pages/tooltip/tooltip.ejs index ac887c62f..9fe1c0cbd 100644 --- a/src/dev/pages/tooltip/tooltip.ejs +++ b/src/dev/pages/tooltip/tooltip.ejs @@ -1,6 +1,4 @@ - - - ✨ Tooltips are cool ✨ - +Hover me +✨ Tooltips are cool ✨ diff --git a/src/dev/pages/tooltip/tooltip.ts b/src/dev/pages/tooltip/tooltip.ts index e963dbb89..2bbd1d15f 100644 --- a/src/dev/pages/tooltip/tooltip.ts +++ b/src/dev/pages/tooltip/tooltip.ts @@ -1,6 +1,5 @@ import '$src/shared'; import '@tylertech/forge/button'; -import '@tylertech/forge/button/forge-button.scss'; import '@tylertech/forge/tooltip'; import type { ISelectComponent, ISwitchComponent, ITooltipComponent } from '@tylertech/forge'; diff --git a/src/dev/pages/typography/typography.ejs b/src/dev/pages/typography/typography.ejs index 17ecb4477..565312bcc 100644 --- a/src/dev/pages/typography/typography.ejs +++ b/src/dev/pages/typography/typography.ejs @@ -2,7 +2,7 @@
      Heading
      Subheading

      - Lorem ipsum dolor sit amet consectetur adipisicing elit. Odio vero dolore aspernatur veritatis cupiditate accusantium totam odit nihil officiis perspiciatis molestiae, cum sed ad quae dolorum. Veritatis omnis nobis repellat? + Lorem ipsum dolor sit amet consectetur adipisicing elit. Odio vero dolore aspernatur veritatis cupiditate accusantium totam odit nihil officiis perspiciatis molestiae, cum sed ad quae dolorum. Veritatis omnis nobis repellat?

      diff --git a/src/dev/src/component-list.ts b/src/dev/src/component-list.ts index 39bbf3fb0..4c941d24c 100644 --- a/src/dev/src/component-list.ts +++ b/src/dev/src/component-list.ts @@ -132,20 +132,16 @@ function buildComponentsList(groups: IComponentGroup[]): HTMLElement[] { elements.push(groupHeader); for (const { label: componentLabel, path } of components) { - const anchor = document.createElement('a'); - anchor.classList.add('list-item-link'); - anchor.href = path; - const listItem = document.createElement('forge-list-item'); listItem.textContent = componentLabel; - anchor.appendChild(listItem); + listItem.href = path; const trailingIcon = document.createElement('forge-icon'); trailingIcon.slot = 'trailing'; trailingIcon.name = 'chevron_right'; listItem.appendChild(trailingIcon); - elements.push(anchor); + elements.push(listItem); } } diff --git a/src/dev/src/partials/controls/button.ejs b/src/dev/src/partials/controls/button.ejs index 30372c498..650d9d012 100644 --- a/src/dev/src/partials/controls/button.ejs +++ b/src/dev/src/partials/controls/button.ejs @@ -1,3 +1 @@ - - - +<%= control.label %> diff --git a/src/dev/src/shared.scss b/src/dev/src/shared.scss index 37e2ef877..5a19ee61a 100644 --- a/src/dev/src/shared.scss +++ b/src/dev/src/shared.scss @@ -3,7 +3,6 @@ @use './styles/options'; @use './styles/flex'; @use './styles/grid'; -@use './styles/list'; body.ready { opacity: 1; diff --git a/src/dev/src/styles/_header.scss b/src/dev/src/styles/_header.scss index c0a25bdb6..dd41b085e 100644 --- a/src/dev/src/styles/_header.scss +++ b/src/dev/src/styles/_header.scss @@ -1,4 +1,10 @@ #page-app-bar { --forge-app-bar-theme-background: var(--forge-theme-secondary); --forge-app-bar-theme-on-background: var(--forge-theme-on-secondary); + + a[slot=title] { + text-decoration: none; + color: inherit; + } } + diff --git a/src/dev/src/styles/_options.scss b/src/dev/src/styles/_options.scss index f51213299..604dff2dd 100644 --- a/src/dev/src/styles/_options.scss +++ b/src/dev/src/styles/_options.scss @@ -41,9 +41,7 @@ } .option-button { - button { - width: 100%; - } + width: 100%; } } diff --git a/src/lib/app-bar/_mixins.scss b/src/lib/app-bar/_mixins.scss index e5b8bfa92..2425b9840 100644 --- a/src/lib/app-bar/_mixins.scss +++ b/src/lib/app-bar/_mixins.scss @@ -237,8 +237,8 @@ )); } - ::slotted(forge-button[type=outlined]), - ::slotted(forge-button:not([type])) { - --mdc-theme-primary: var(--forge-app-bar-theme-on-background, rgba(255, 255, 255, 0.87)); + ::slotted(forge-button[variant=outlined]), + ::slotted(forge-button:not([variant])) { + --forge-theme-primary: var(--forge-app-bar-theme-on-background, rgba(255, 255, 255, 0.87)); } } diff --git a/src/lib/busy-indicator/_mixins.scss b/src/lib/busy-indicator/_mixins.scss index ff140d981..2bce13e6a 100644 --- a/src/lib/busy-indicator/_mixins.scss +++ b/src/lib/busy-indicator/_mixins.scss @@ -70,7 +70,7 @@ @include row-layout-message; } - &__forge-cancel-button { + &__cancel-button { @include row-layout-cancel-button; } diff --git a/src/lib/busy-indicator/busy-indicator.html b/src/lib/busy-indicator/busy-indicator.html index 7b073aab8..ebf8be3d2 100644 --- a/src/lib/busy-indicator/busy-indicator.html +++ b/src/lib/busy-indicator/busy-indicator.html @@ -5,9 +5,7 @@

      Loading

      - - - + Cancel
      diff --git a/src/lib/busy-indicator/busy-indicator.scss b/src/lib/busy-indicator/busy-indicator.scss index 912548d2f..4f6ac2375 100644 --- a/src/lib/busy-indicator/busy-indicator.scss +++ b/src/lib/busy-indicator/busy-indicator.scss @@ -1,5 +1,4 @@ @use './mixins'; -@use '../button/forge-button'; :host { @include mixins.host; diff --git a/src/lib/button-toggle/button-toggle/_mixins.scss b/src/lib/button-toggle/button-toggle/_mixins.scss index 4a281d285..6e11a70d9 100644 --- a/src/lib/button-toggle/button-toggle/_mixins.scss +++ b/src/lib/button-toggle/button-toggle/_mixins.scss @@ -1,7 +1,13 @@ -@use '@material/ripple/ripple-theme' as mdc-ripple-theme; @use '@material/theme/theme' as mdc-theme; @use '@material/animation/functions' as mdc-animation; -@use '../../button/mixins' as button-mixins; +@use '@material/ripple/ripple' as mdc-ripple; +@use '@material/ripple/ripple-theme' as mdc-ripple-theme; +@use '@material/feature-targeting/feature-targeting' as mdc-feature-targeting; +@use '@material/button/button-base' as mdc-button-base; +@use '@material/button/button-shared-theme' as mdc-button-shared-theme; +@use '@material/button/button-outlined' as mdc-button-outlined; +@use '@material/button/button-outlined-theme' as mdc-button-outlined-theme; +@use '../../theme'; @mixin host() { display: inline-block; @@ -12,7 +18,8 @@ @include base; &:disabled { - @include button-mixins.outlined-disabled; + @include theme.property(border-color, border-color); + @include mdc-theme.property(color, text-disabled-on-light); } &__selected:not(:disabled) { @@ -51,9 +58,20 @@ } @mixin base() { - @include button-mixins.base; - @include button-mixins.outlined; - + @include mdc-ripple.surface(); + @include mdc-ripple.radius-bounded(); + @include mdc-ripple-theme.states(primary, false); + @include mdc-button-base.base(mdc-feature-targeting.all()); + @include mdc-button-shared-theme.shape-radius(small); + @include mdc-button-shared-theme.container-fill-color(transparent); + @include mdc-button-shared-theme.ink-color(primary); + @include mdc-button-shared-theme.density(0); + @include mdc-button-outlined.outlined(mdc-feature-targeting.all()); + @include mdc-button-outlined-theme.outline-width(mdc-button-outlined-theme.$outlined-border-width); + @include mdc-button-outlined-theme.outline-color(primary); + + text-transform: none; + overflow: hidden; width: 100%; box-sizing: border-box; transition: mdc-animation.enter(background-color, 150ms); @@ -73,8 +91,9 @@ } @mixin dense() { - @include button-mixins.dense; + @include mdc-button-shared-theme.density(-3); + height: 1.5rem; font-size: 0.75rem; } diff --git a/src/lib/button/_button-base.scss b/src/lib/button/_button-base.scss deleted file mode 100644 index 6437139cf..000000000 --- a/src/lib/button/_button-base.scss +++ /dev/null @@ -1,178 +0,0 @@ -// -// Copyright 2020 Google Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -// stylelint-disable selector-class-pattern -- -// Selector '.forge-*' should only be used in this project. - -@use '@material/elevation/elevation' as mdc-elevation; -@use '@material/elevation/elevation-theme' as mdc-elevation-theme; -@use '@material/feature-targeting/feature-targeting' as mdc-feature-targeting; -@use '@material/rtl/rtl' as mdc-rtl; -@use '@material/typography/typography' as mdc-typography; -@use './button-ripple'; -@use './button-shared-theme'; - -@mixin static-styles($query: mdc-feature-targeting.all()) { - @include static-styles-without-ripple($query: $query); - @include button-ripple.static-styles($query: $query); -} - -@mixin static-styles-without-ripple($query: mdc-feature-targeting.all()) { - $feat-structure: mdc-feature-targeting.create-target($query, structure); - - // prettier-ignore - @include mdc-elevation.overlay-common($query); // COPYBARA_COMMENT_THIS_LINE - - // postcss-bem-linter: define button - .forge-button { - @include base($query); - // The icon CSS class overrides styles defined in the .material-icons CSS - // class, which is loaded separately so the order of CSS definitions is not - // guaranteed. Therefore, increase specifity by nesting this class to ensure - // overrides apply. - .forge-button__icon { - @include mdc-feature-targeting.targets($feat-structure) { - @include icon; - } - } - } - - .forge-button__label ~ .forge-button__icon { - @include mdc-feature-targeting.targets($feat-structure) { - @include icon-trailing; - } - } - - svg.forge-button__icon { - @include mdc-feature-targeting.targets($feat-structure) { - @include icon-svg; - } - } - - // TODO: figure out the correct place for this rule. - .forge-button--raised, - .forge-button--unelevated, - .forge-button--outlined { - .forge-button__icon { - @include mdc-feature-targeting.targets($feat-structure) { - // Icons inside contained buttons have different styles due to increased button padding - @include icon-contained; - } - } - - .forge-button__label ~ .forge-button__icon { - @include mdc-feature-targeting.targets($feat-structure) { - @include icon-contained-trailing; - } - } - } - // postcss-bem-linter: end -} - -@mixin base($query) { - $feat-structure: mdc-feature-targeting.create-target($query, structure); - - @include mdc-typography.typography(button, $query); - @include mdc-elevation-theme.overlay-surface-position($query: $query); - @include mdc-elevation-theme.overlay-dimensions(100%, $query: $query); - - @include mdc-feature-targeting.targets($feat-structure) { - display: inline-flex; - // position: relative; already set in mdc-elevation-overlay-surface-position - align-items: center; - justify-content: center; - box-sizing: border-box; - min-width: 64px; - border: none; - outline: none; - /* @alternate */ - line-height: inherit; - user-select: none; - -webkit-appearance: none; - // Even though `visible` is the default, IE 11 computes the property as - // `hidden` in some cases, unless it's explicitly defined here. - overflow: visible; - vertical-align: middle; - } - - &::-moz-focus-inner { - @include mdc-feature-targeting.targets($feat-structure) { - padding: 0; - border: 0; - } - } - - // postcss-bem-linter: ignore - &:active { - @include mdc-feature-targeting.targets($feat-structure) { - outline: none; - } - } - - &:hover { - @include mdc-feature-targeting.targets($feat-structure) { - cursor: pointer; - } - } - - &:disabled { - @include mdc-feature-targeting.targets($feat-structure) { - cursor: default; - pointer-events: none; - } - } -} - -@mixin icon { - @include mdc-rtl.reflexive-box(margin, right, 8px); - - $icon-size: mdc-typography.px-to-rem(18px); - - display: inline-block; - font-size: $icon-size; - height: $icon-size; - vertical-align: top; - width: $icon-size; -} - -@mixin icon-trailing { - @include mdc-rtl.reflexive-box(margin, left, 8px); -} - -@mixin icon-svg { - fill: currentColor; -} - -@mixin icon-contained { - @include mdc-rtl.reflexive-property(margin, -4px, 8px); -} - -@mixin icon-contained-trailing { - @include mdc-rtl.reflexive-property(margin, 8px, -4px); -} - -// FORGE(new): added new dense mixin -@mixin dense() { - @include button-shared-theme.density(-3); - - height: 1.5rem; -} diff --git a/src/lib/button/_button-filled-theme.scss b/src/lib/button/_button-filled-theme.scss deleted file mode 100644 index cf8744920..000000000 --- a/src/lib/button/_button-filled-theme.scss +++ /dev/null @@ -1,55 +0,0 @@ -// -// Copyright 2021 Google Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -@use '@material/feature-targeting/feature-targeting' as mdc-feature-targeting; -@use '@material/theme/theme' as mdc-theme; -@use './button-shared-theme'; - -$light-theme: ( - container-color: primary, - container-hover-color: null, - container-focus-color: null, - container-pressed-color: null, - container-disabled-color: button-shared-theme.$disabled-container-color, - density: button-shared-theme.$density-scale, - icon-color: null, - icon-hover-color: null, - icon-focus-color: null, - icon-pressed-color: null, - icon-disabled-color: null, - label-color: on-primary, - label-hover-color: null, - label-focus-color: null, - label-pressed-color: null, - label-disabled-color: button-shared-theme.$disabled-ink-color, - ripple-color: on-primary, - ripple-opacity: null, - shape: button-shared-theme.$shape-radius, -); - -/// Sets theme based on provided theme configuration. -/// Only emits theme related styles. -/// @param {Map} $theme - Theme configuration to use. -@mixin theme($theme, $query: mdc-feature-targeting.all()) { - @include mdc-theme.validate-theme($light-theme, $theme); - @include button-shared-theme.theme($theme, $query: $query); -} diff --git a/src/lib/button/_button-filled.scss b/src/lib/button/_button-filled.scss deleted file mode 100644 index 3213ff662..000000000 --- a/src/lib/button/_button-filled.scss +++ /dev/null @@ -1,66 +0,0 @@ -// -// Copyright 2020 Google Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -// stylelint-disable selector-class-pattern -- -// Selector '.forge-*' should only be used in this project. - -@use 'sass:map'; -@use '@material/feature-targeting/feature-targeting' as mdc-feature-targeting; -@use './button-base'; -@use './button-filled-theme'; -@use './button-shared-theme'; - -@mixin styles( - $theme: button-filled-theme.$light-theme, - $query: mdc-feature-targeting.all() -) { - @include button-base.static-styles($query: $query); - @include static-styles($query: $query); - @include theme-styles($theme, $query: $query); -} - -@mixin theme-styles( - $theme: button-filled-theme.$light-theme, - $query: mdc-feature-targeting.all() -) { - .forge-button--unelevated { - @include _theme-styles($theme, $query: $query); - } -} - -@mixin static-styles($query: mdc-feature-targeting.all()) { - // Intentionally left blank for future-proofing. -} - -@mixin filled($query: mdc-feature-targeting.all()) { - @include _theme-styles(button-filled-theme.$light-theme, $query: $query); -} - -@mixin _theme-styles($theme, $query: mdc-feature-targeting.all()) { - // TODO(b/179402677): move into theme config. - @include button-shared-theme.horizontal-padding( - button-shared-theme.$contained-horizontal-padding, - $query: $query - ); - - @include button-filled-theme.theme($theme, $query: $query); -} diff --git a/src/lib/button/_button-outlined-theme.scss b/src/lib/button/_button-outlined-theme.scss deleted file mode 100644 index 0c9a84aac..000000000 --- a/src/lib/button/_button-outlined-theme.scss +++ /dev/null @@ -1,170 +0,0 @@ -// -// Copyright 2021 Google Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -// stylelint-disable selector-class-pattern -- -// Selector '.forge-*' should only be used in this project. - -@use 'sass:map'; -@use 'sass:math'; -@use '@material/feature-targeting/feature-targeting' as mdc-feature-targeting; -@use '@material/theme/state' as mdc-theme-state; -@use '@material/theme/theme' as mdc-theme; -@use '@material/ripple/ripple-theme'as mdc-ripple-theme; -@use './button-base'; -@use './button-shared-theme'; -@use './button-ripple'; - -$outlined-border-width: 1px !default; -$outline-color: primary !default; // FORGE(modify): use primary theme instead of static color - -$light-theme: ( - container-color: transparent, - container-hover-color: null, - container-focus-color: null, - container-pressed-color: null, - container-disabled-color: transparent, - density: button-shared-theme.$density-scale, - icon-color: null, - icon-hover-color: null, - icon-focus-color: null, - icon-pressed-color: null, - icon-disabled-color: null, - label-color: primary, - label-hover-color: null, - label-focus-color: null, - label-pressed-color: null, - label-disabled-color: button-shared-theme.$disabled-ink-color, - outline-width: $outlined-border-width, - outline-color: $outline-color, - outline-hover-color: null, - outline-focus-color: null, - outline-pressed-color: null, - outline-disabled-color: button-shared-theme.$disabled-container-color, - ripple-color: primary, - ripple-opacity: null, - shape: button-shared-theme.$shape-radius, -); - -/// Sets theme based on provided theme configuration. -/// Only emits theme related styles. -/// @param {Map} $theme - Theme configuration to use. -@mixin theme($theme, $query: mdc-feature-targeting.all()) { - @include mdc-theme.validate-theme($light-theme, $theme); - @include button-shared-theme.theme($theme, $query: $query); - @include outline-color( - ( - default: map.get($theme, outline-color), - hover: map.get($theme, outline-hover-color), - focus: map.get($theme, outline-focus-color), - pressed: map.get($theme, outline-pressed-color), - disabled: map.get($theme, outline-disabled-color), - ), - $query: $query - ); - @include outline-width(map.get($theme, outline-width), $query: $query); -} - -/// -/// Sets the outline color to the given color for an enabled button. -/// @param {Color} $color-or-map - The desired outline color, specified either -/// as a flat value or a map of colors with states -/// {default, hover, focused, pressed, disabled} as keys. -/// -@mixin outline-color($color-or-map, $query: mdc-feature-targeting.all()) { - &:not(:disabled) { - @include _outline-color( - mdc-theme-state.get-default-state($color-or-map), - $query: $query - ); - - &:hover { - @include _outline-color( - mdc-theme-state.get-hover-state($color-or-map), - $query: $query - ); - } - - @include mdc-ripple-theme.focus() { - @include _outline-color( - mdc-theme-state.get-focus-state($color-or-map), - $query: $query - ); - } - - // Increase active state specificity due to mdc-ripple-theme.focus(). - &:active, - &:focus:active { - @include _outline-color( - mdc-theme-state.get-pressed-state($color-or-map), - $query: $query - ); - } - } - - &:disabled { - @include _outline-color( - mdc-theme-state.get-disabled-state($color-or-map), - $query: $query - ); - } -} - -@mixin outline-width( - $outline-width, - $padding: button-shared-theme.$contained-horizontal-padding, - $query: mdc-feature-targeting.all() -) { - @if $outline-width != null { - $feat-structure: mdc-feature-targeting.create-target($query, structure); - // Note: Adjust padding to maintain consistent width with non-outlined buttons - $padding-value: math.max($padding - $outline-width, 0); - - @include button-shared-theme.horizontal-padding($padding-value, $query); - - @include mdc-feature-targeting.targets($feat-structure) { - border-width: $outline-width; - } - - #{button-ripple.$ripple-target} { - @include mdc-feature-targeting.targets($feat-structure) { - top: -$outline-width; - left: -$outline-width; - border: $outline-width solid transparent; - } - } - } -} - -/// -/// Sets the outline color to the given color. This mixin should be -/// wrapped in a selector that qualifies button state. -/// @access private -/// -@mixin _outline-color($color, $query: mdc-feature-targeting.all()) { - $feat-color: mdc-feature-targeting.create-target($query, color); - - @if $color { - @include mdc-feature-targeting.targets($feat-color) { - @include mdc-theme.property(border-color, $color); - } - } -} diff --git a/src/lib/button/_button-outlined.scss b/src/lib/button/_button-outlined.scss deleted file mode 100644 index b4cd2fda0..000000000 --- a/src/lib/button/_button-outlined.scss +++ /dev/null @@ -1,71 +0,0 @@ -// -// Copyright 2020 Google Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -// stylelint-disable selector-class-pattern -- -// Selector '.forge-*' should only be used in this project. - -@use 'sass:map'; -@use '@material/feature-targeting/feature-targeting' as mdc-feature-targeting; -@use './button-base'; -@use './button-outlined-theme'; -@use './button-shared-theme'; - -@mixin styles( - $theme: button-outlined-theme.$light-theme, - $query: mdc-feature-targeting.all() -) { - @include button-base.static-styles($query: $query); - @include static-styles($query: $query); - @include theme-styles($theme, $query: $query); -} - -@mixin theme-styles( - $theme: button-outlined-theme.$light-theme, - $query: mdc-feature-targeting.all() -) { - .forge-button--outlined { - @include _theme-styles($theme, $query: $query); - } -} - -@mixin static-styles($query: mdc-feature-targeting.all()) { - .forge-button--outlined { - @include _static-styles($query: $query); - } -} - -@mixin _theme-styles($theme, $query: mdc-feature-targeting.all()) { - @include button-outlined-theme.theme($theme, $query: $query); -} - -@mixin _static-styles($query: mdc-feature-targeting.all()) { - $feat-structure: mdc-feature-targeting.create-target($query, structure); - - @include mdc-feature-targeting.targets($feat-structure) { - border-style: solid; - } -} - -@mixin outlined($query: mdc-feature-targeting.all()) { - @include _static-styles($query: $query); - @include _theme-styles(button-outlined-theme.$light-theme, $query: $query); -} diff --git a/src/lib/button/_button-protected-theme.scss b/src/lib/button/_button-protected-theme.scss deleted file mode 100644 index cf8744920..000000000 --- a/src/lib/button/_button-protected-theme.scss +++ /dev/null @@ -1,55 +0,0 @@ -// -// Copyright 2021 Google Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -@use '@material/feature-targeting/feature-targeting' as mdc-feature-targeting; -@use '@material/theme/theme' as mdc-theme; -@use './button-shared-theme'; - -$light-theme: ( - container-color: primary, - container-hover-color: null, - container-focus-color: null, - container-pressed-color: null, - container-disabled-color: button-shared-theme.$disabled-container-color, - density: button-shared-theme.$density-scale, - icon-color: null, - icon-hover-color: null, - icon-focus-color: null, - icon-pressed-color: null, - icon-disabled-color: null, - label-color: on-primary, - label-hover-color: null, - label-focus-color: null, - label-pressed-color: null, - label-disabled-color: button-shared-theme.$disabled-ink-color, - ripple-color: on-primary, - ripple-opacity: null, - shape: button-shared-theme.$shape-radius, -); - -/// Sets theme based on provided theme configuration. -/// Only emits theme related styles. -/// @param {Map} $theme - Theme configuration to use. -@mixin theme($theme, $query: mdc-feature-targeting.all()) { - @include mdc-theme.validate-theme($light-theme, $theme); - @include button-shared-theme.theme($theme, $query: $query); -} diff --git a/src/lib/button/_button-protected.scss b/src/lib/button/_button-protected.scss deleted file mode 100644 index cde3ef3f6..000000000 --- a/src/lib/button/_button-protected.scss +++ /dev/null @@ -1,83 +0,0 @@ -// -// Copyright 2020 Google Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -// stylelint-disable selector-class-pattern -- -// Selector '.forge-*' should only be used in this project. - -@use '@material/elevation/functions' as mdc-elevation-functions; -@use '@material/elevation/mixins' as mdc-elevation-mixins; -@use '@material/feature-targeting/feature-targeting' as mdc-feature-targeting; -@use './button-base'; -@use './button-protected-theme'; -@use './button-shared-theme'; - -@mixin styles( - $theme: button-protected-theme.$light-theme, - $query: mdc-feature-targeting.all() -) { - @include button-base.static-styles($query: $query); - @include static-styles($query: $query); - @include theme-styles($theme, $query: $query); -} - -@mixin theme-styles( - $theme: button-protected-theme.$light-theme, - $query: mdc-feature-targeting.all() -) { - .forge-button--raised { - // TODO(b/179402677): move into theme config. - @include button-shared-theme.horizontal-padding( - button-shared-theme.$contained-horizontal-padding, - $query - ); - @include button-protected-theme.theme($theme, $query: $query); - } -} - -@mixin static-styles($query: mdc-feature-targeting.all()) { - .forge-button--raised { - @include raised($query); - } -} - -@mixin raised($query) { - $feat-animation: mdc-feature-targeting.create-target($query, animation); - - @include mdc-elevation-mixins.elevation(2, $query: $query); - - &:hover, - &:focus { - @include mdc-elevation-mixins.elevation(4, $query: $query); - } - - &:active { - @include mdc-elevation-mixins.elevation(8, $query: $query); - } - - &:disabled { - @include mdc-elevation-mixins.elevation(0, $query: $query); - } - - @include mdc-feature-targeting.targets($feat-animation) { - transition: mdc-elevation-functions.transition-value(); - } -} diff --git a/src/lib/button/_button-ripple.scss b/src/lib/button/_button-ripple.scss deleted file mode 100644 index a3c83d7d9..000000000 --- a/src/lib/button/_button-ripple.scss +++ /dev/null @@ -1,64 +0,0 @@ -// -// Copyright 2016 Google Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -// stylelint-disable selector-class-pattern -- -// Selector '.forge-*' should only be used in this project. - -@use '@material/feature-targeting/feature-targeting' as mdc-feature-targeting; -@use '@material/ripple/ripple' as mdc-ripple; - -$ripple-target: '.forge-button__ripple'; - -@mixin static-styles($query: mdc-feature-targetingall()) { - $feat-structure: mdc-feature-targeting.create-target($query, structure); - - @include mdc-ripple.common($query); // COPYBARA_COMMENT_THIS_LINE - - .forge-button { - @include mdc-ripple.surface($query: $query, $ripple-target: $ripple-target); - @include mdc-ripple.radius-bounded( - $query: $query, - $ripple-target: $ripple-target - ); - - #{$ripple-target} { - @include mdc-feature-targeting.targets($feat-structure) { - position: absolute; - // Ripple needs content-box as the box sizing and box-sizing: border-box - // is often set as a default, so we override that here. - box-sizing: content-box; - width: 100%; - height: 100%; - overflow: hidden; - } - } - - // Ripple targets inside outlined buttons set their own `top`/`left`, - // depending on the border width. - &:not(.forge-button--outlined) #{$ripple-target} { - @include mdc-feature-targeting.targets($feat-structure) { - top: 0; - left: 0; - } - } - } -} diff --git a/src/lib/button/_button-shared-theme.scss b/src/lib/button/_button-shared-theme.scss deleted file mode 100644 index 5f215a2ae..000000000 --- a/src/lib/button/_button-shared-theme.scss +++ /dev/null @@ -1,442 +0,0 @@ -// -// Copyright 2021 Google Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -// stylelint-disable selector-class-pattern -- -// Selector '.forge-*' should only be used in this project. - -@use 'sass:map'; -@use '@material/density/functions' as mdc-density-functions; -@use '@material/density/variables' as mdc-density-variables; -@use '@material/dom/mixins' as mdc-dom-mixins; -@use '@material/feature-targeting/feature-targeting' as mdc-feature-targeting; -@use '@material/ripple/ripple-theme' as mdc-ripple-theme; -@use '@material/shape/mixins' as mdc-shape-mixins; -@use '@material/theme/state' as mdc-theme-state; -@use '@material/theme/theme' as mdc-theme; -@use '@material/theme/theme-color' as mdc-theme-color; -@use '@material/typography/typography' as mdc-typography; -@use './button-ripple'; - -$height: 36px !default; -$horizontal-padding: 8px !default; -$contained-horizontal-padding: 16px !default; - -$minimum-height: 24px !default; -$maximum-height: $height !default; -$density-scale: mdc-density-variables.$default-scale !default; -$density-config: ( - height: ( - default: $height, - maximum: $maximum-height, - minimum: $minimum-height, - ), -) !default; - -$shape-radius: small !default; - -$disabled-ink-color: text-disabled-on-light !default; // FORGE(modify): use theme styles instead of static color values -$disabled-container-color: text-disabled-on-background !default; // FORGE(modify): use theme styles instead of static color values - -@mixin theme($theme, $query: mdc-feature-targeting.all()) { - @include container-fill-color( - ( - default: map.get($theme, container-color), - hover: map.get($theme, container-hover-color), - focus: map.get($theme, container-focus-color), - pressed: map.get($theme, container-pressed-color), - disabled: map.get($theme, container-disabled-color), - ), - $query: $query - ); - - @include ink-color( - ( - default: map.get($theme, label-color), - hover: map.get($theme, label-hover-color), - focus: map.get($theme, label-focus-color), - pressed: map.get($theme, label-pressed-color), - disabled: map.get($theme, label-disabled-color), - ), - $query: $query - ); - - @include icon-color( - ( - default: map.get($theme, icon-color), - hover: map.get($theme, icon-hover-color), - focus: map.get($theme, icon-focus-color), - pressed: map.get($theme, icon-pressed-color), - disabled: map.get($theme, icon-disabled-color), - ), - $query: $query - ); - - $ripple-color: map.get($theme, ripple-color); - $ripple-opacity: map.get($theme, ripple-opacity); - @if $ripple-color { - @include ripple-states( - $color: $ripple-color, - $opacity-map: $ripple-opacity, - $query: $query - ); - } - - $density: map.get($theme, density); - @if $density != null { - @include density($density-scale: $density, $query: $query); - } - - $shape: map.get($theme, shape); - @if $density == null { - $density: $density-scale; - } - @if $shape { - @include shape-radius($shape, $density-scale: $density, $query: $query); - } -} - -/// -/// Sets ripple color for button. -/// -@mixin ripple-states( - $color, - $opacity-map: null, - $query: mdc-feature-targeting.all() -) { - @include mdc-ripple-theme.states( - $color: $color, - $opacity-map: $opacity-map, - $query: $query, - $ripple-target: button-ripple.$ripple-target - ); -} - -@mixin filled-accessible( - $container-fill-color, - $query: mdc-feature-targeting.all() -) { - $fill-tone: mdc-theme-color.tone($container-fill-color); - - @include container-fill-color($container-fill-color, $query); - - @if ($fill-tone == 'dark') { - @include ink-color(text-primary-on-dark, $query); - @include ripple-states($color: text-primary-on-dark, $query: $query); - } @else { - @include ink-color(text-primary-on-light, $query); - @include ripple-states($color: text-primary-on-light, $query: $query); - } -} - -/// -/// Sets the container fill color to the given color for an enabled button. -/// @param {Color|map} $color-or-map - The desired container fill color, -/// specified either as a flat value or a map of colors with states -/// {default, hover, focused, pressed, disabled} as keys. -/// -@mixin container-fill-color($color-or-map, $query: mdc-feature-targeting.all()) { - // :not(:disabled) is used to support link styled as button - // as link does not support :enabled style - &:not(:disabled) { - @include _container-fill-color( - mdc-theme-state.get-default-state($color-or-map), - $query: $query - ); - - &:hover { - @include _container-fill-color( - mdc-theme-state.get-hover-state($color-or-map), - $query: $query - ); - } - - @include mdc-ripple-theme.focus() { - @include _container-fill-color( - mdc-theme-state.get-focus-state($color-or-map), - $query: $query - ); - } - - // Increase active state specificity due to mdc-ripple-theme.focus(). - &:active, - &:focus:active { - @include _container-fill-color( - mdc-theme-state.get-pressed-state($color-or-map), - $query: $query - ); - } - } - - &:disabled { - @include _container-fill-color( - mdc-theme-state.get-disabled-state($color-or-map), - $query: $query - ); - } -} - -/// -/// Sets the icon color to the given color for an enabled button. -/// @param {Color} $color-or-map - The desired icon color, specified either -/// as a flat value or a map of colors with states -/// {default, hover, focused, pressed, disabled} as keys. -/// -@mixin icon-color($color-or-map, $query: mdc-feature-targeting.all()) { - &:not(:disabled) { - @include _icon-color( - mdc-theme-state.get-default-state($color-or-map), - $query: $query - ); - - &:hover { - @include _icon-color( - mdc-theme-state.get-hover-state($color-or-map), - $query: $query - ); - } - - @include mdc-ripple-theme.focus() { - @include _icon-color( - mdc-theme-state.get-focus-state($color-or-map), - $query: $query - ); - } - - // Increase active state specificity due to mdc-ripple-theme.focus(). - &:active, - &:focus:active { - @include _icon-color( - mdc-theme-state.get-pressed-state($color-or-map), - $query: $query - ); - } - } - - &:disabled { - @include _icon-color( - mdc-theme-state.get-disabled-state($color-or-map), - $query: $query - ); - } -} - -/// -/// Sets the ink color to the given color for an enabled button, -/// and sets the icon color to the given color unless `mdc-button-icon-color` -/// is also used. -/// @param {Color} $color-or-map - The desired ink color, specified either -/// as a flat value or a map of colors with states -/// {default, hover, focused, pressed, disabled} as keys. -/// -@mixin ink-color($color-or-map, $query: mdc-feature-targeting.all()) { - &:not(:disabled) { - @include _ink-color(mdc-theme-state.get-default-state($color-or-map), $query: $query); - - &:hover { - @include _ink-color(mdc-theme-state.get-hover-state($color-or-map), $query: $query); - } - - @include mdc-ripple-theme.focus() { - @include _ink-color(mdc-theme-state.get-focus-state($color-or-map), $query: $query); - } - - // Increase active state specificity due to mdc-ripple-theme.focus(). - &:active, - &:focus:active { - @include _ink-color( - mdc-theme-state.get-pressed-state($color-or-map), - $query: $query - ); - } - } - - &:disabled { - @include _ink-color( - mdc-theme-state.get-disabled-state($color-or-map), - $query: $query - ); - } -} - -/// -/// Sets density scale for button. -/// -/// @param {Number | String} $density-scale - Density scale value for component. Supported density scale values `-3`, -/// `-2`, `-1`, `0`. -/// -@mixin density($density-scale, $query: mdc-feature-targeting.all()) { - $height: mdc-density-functions.prop-value( - $density-config: $density-config, - $density-scale: $density-scale, - $property-name: height, - ); - - @include height($height, $query: $query); - - @if $density-scale != 0 { - @include _touch-target-reset($query: $query); - } -} - -/// -/// Resets touch target-related styles. This is called from the density mixin to -/// automatically remove the increased touch target, since dense components -/// don't have the same default a11y requirements. -/// @access private -/// -@mixin _touch-target-reset($query: mdc-feature-targeting.all()) { - $feat-structure: mdc-feature-targeting.create-target($query, structure); - - @include mdc-feature-targeting.targets($feat-structure) { - margin-top: 0; - margin-bottom: 0; - } -} - -/// -/// Sets custom height for button. -/// @param {Number} $height - Height of button in `px`. -/// -@mixin height($height, $query: mdc-feature-targeting.all()) { - $feat-structure: mdc-feature-targeting.create-target($query, structure); - - @include mdc-feature-targeting.targets($feat-structure) { - height: $height; - } -} - -@mixin shape-radius( - $radius, - $rtl-reflexive: false, - $density-scale: $density-scale, - $query: mdc-feature-targeting.all() -) { - $height: mdc-density-functions.prop-value( - $density-config: $density-config, - $density-scale: $density-scale, - $property-name: height, - ); - - @include mdc-shape-mixins.radius( - $radius, - $rtl-reflexive, - $component-height: $height, - $query: $query - ); - - #{button-ripple.$ripple-target} { - @include mdc-shape-mixins.radius( - $radius, - $rtl-reflexive, - $component-height: $height, - $query: $query - ); - } -} - -/// -/// Sets horizontal padding to the given number. -/// @param {Number} $padding -/// -@mixin horizontal-padding($padding, $query: mdc-feature-targeting.all()) { - $feat-structure: mdc-feature-targeting.create-target($query, structure); - - @include mdc-feature-targeting.targets($feat-structure) { - // $padding should be a single value; enforce it by specifying all 4 sides in the output - padding: 0 $padding 0 $padding; - } -} - -/// -/// Sets the button label to overflow as ellipsis -/// -@mixin label-overflow-ellipsis($query: mdc-feature-targeting.all()) { - .forge-button__label { - @include mdc-typography.overflow-ellipsis($query: $query); - } -} - -/// -/// Includes ad-hoc high contrast mode support. -/// -@mixin high-contrast-mode-shim($query: mdc-feature-targeting.all()) { - &::before { - @include mdc-dom-mixins.transparent-border($query: $query); - } - - &:focus { - &::before { - @include mdc-dom-mixins.transparent-border( - $border-width: 5px, - $border-style: double, - $query: $query - ); - } - } -} - -/// -/// Sets the container fill color to the given color. This mixin should be -/// wrapped in a selector that qualifies button state. -/// @access private -/// -@mixin _container-fill-color($color, $query: mdc-feature-targeting.all()) { - $feat-color: mdc-feature-targeting.create-target($query, color); - - @if $color { - @include mdc-feature-targeting.targets($feat-color) { - @include mdc-theme.property(background-color, $color); - } - } -} - -/// -/// Sets the icon color to the given color. This mixin should be -/// wrapped in a selector that qualifies button state. -/// @access private -/// -@mixin _icon-color($color, $query: mdc-feature-targeting.all()) { - $feat-color: mdc-feature-targeting.create-target($query, color); - - @if $color { - .forge-button__icon { - @include mdc-feature-targeting.targets($feat-color) { - @include mdc-theme.property(color, $color); - } - } - } -} - -/// -/// Sets the ink color to the given color. This mixin should be -/// wrapped in a selector that qualifies button state. -/// @access private -/// -@mixin _ink-color($color, $query: mdc-feature-targeting.all()) { - $feat-color: mdc-feature-targeting.create-target($query, color); - - @if $color { - @include mdc-feature-targeting.targets($feat-color) { - @include mdc-theme.property(color, $color); - } - } -} diff --git a/src/lib/button/_button-text-theme.scss b/src/lib/button/_button-text-theme.scss deleted file mode 100644 index 339030d83..000000000 --- a/src/lib/button/_button-text-theme.scss +++ /dev/null @@ -1,55 +0,0 @@ -// -// Copyright 2021 Google Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -@use '@material/feature-targeting/feature-targeting' as mdc-feature-targeting; -@use '@material/theme/theme' as mdc-theme; -@use './button-shared-theme'; - -$light-theme: ( - container-color: transparent, - container-hover-color: null, - container-focus-color: null, - container-pressed-color: null, - container-disabled-color: transparent, - density: button-shared-theme.$density-scale, - icon-color: null, - icon-hover-color: null, - icon-focus-color: null, - icon-pressed-color: null, - icon-disabled-color: null, - label-color: primary, - label-hover-color: null, - label-focus-color: null, - label-pressed-color: null, - label-disabled-color: button-shared-theme.$disabled-ink-color, - ripple-color: primary, - ripple-opacity: null, - shape: button-shared-theme.$shape-radius, -); - -/// Sets theme based on provided theme configuration. -/// Only emits theme related styles. -/// @param {Map} $theme - Theme configuration to use. -@mixin theme($theme, $query: mdc-feature-targeting.all()) { - @include mdc-theme.validate-theme($light-theme, $theme); - @include button-shared-theme.theme($theme, $query: $query); -} diff --git a/src/lib/button/_button-text.scss b/src/lib/button/_button-text.scss deleted file mode 100644 index bf30f22c3..000000000 --- a/src/lib/button/_button-text.scss +++ /dev/null @@ -1,52 +0,0 @@ -// -// Copyright 2021 Google Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -// stylelint-disable selector-class-pattern -- -// Selector '.forge-*' should only be used in this project. - -@use 'sass:map'; -@use './button-shared-theme'; -@use './button-text-theme'; - -@mixin styles($theme: button-text-theme.$light-theme, $query: $query) { - @include button-base.static-styles($query: $query); - @include static-styles($query: $query); - @include theme-styles($theme, $query: $query); -} - -@mixin theme-styles( - $theme: button-text-theme.$light-theme, - $query: feature-targeting.all() -) { - .forge-button { - @include button-text-theme.theme($theme, $query: $query); - // TODO(b/179402677): move this into theme config - @include button-shared-theme.horizontal-padding( - button-shared-theme.$horizontal-padding, - $query: $query - ); - } -} - -@mixin static-styles($query: feature-targeting.all()) { - // Intentionally left blank for future-proofing. -} diff --git a/src/lib/button/_button.mixins.scss b/src/lib/button/_button.mixins.scss deleted file mode 100644 index ea202501f..000000000 --- a/src/lib/button/_button.mixins.scss +++ /dev/null @@ -1,143 +0,0 @@ -// -// Copyright 2020 Google Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -@use '@material/feature-targeting/feature-targeting' as mdc-feature-targeting; -@use '@material/theme/color-palette' as mdc-color-palette; -@use './button-base'; -@use './button-filled'; -@use './button-outlined'; -@use './button-outlined-theme'; -@use './button-protected'; -@use './button-ripple'; -@use './button-text'; -@use './button-text-theme'; -@use './button-shared-theme'; -@use '../theme'; - -@mixin styles($query: mdc-feature-targeting.all()) { - @include static-styles($query: $query); - @include theme-styles($query: $query); - - // FORGE(new): add custom forge-specific styles - @include forge-styles; -} - -@mixin forge-styles() { - .forge-button--dense { - @include button-base.dense; - } - - forge-button { - @include host; - } - - forge-button[hidden] { - display: none; - } - - forge-button[disabled] { - cursor: not-allowed; - } - - a.forge-hyperlink { - @include anchor-link; - } - - button.forge-hyperlink { - @include link; - } -} - -@mixin theme-styles($query: mdc-feature-targeting.all()) { - @include button-text.theme-styles( - button-text-theme.$light-theme, - $query: $query - ); - @include button-filled.theme-styles($query: $query); - @include button-protected.theme-styles($query: $query); - @include button-outlined.theme-styles($query: $query); -} - -@mixin static-styles($query: mdc-feature-targeting.all()) { - @include button-base.static-styles($query: $query); - @include button-text.static-styles($query: $query); - @include button-filled.static-styles($query: $query); - @include button-protected.static-styles($query: $query); - @include button-outlined.static-styles($query: $query); -} - -@mixin without-ripple($query: mdc-feature-targeting.all()) { - @include button-base.static-styles-without-ripple($query: $query); - @include button-text.static-styles($query: $query); - @include button-filled.static-styles($query: $query); - @include button-protected.static-styles($query: $query); - @include button-outlined.static-styles($query: $query); -} - -/// -/// FORGE(new): additional Forge mixins -/// - -@mixin host() { - display: inline-block; -} - -@mixin link() { - @include theme.property(color, primary); - - cursor: pointer; - border: none; - background-color: transparent; - font-size: inherit; - padding: 0; - outline: none; - text-align: left; - - &[disabled] { - pointer-events: none; - cursor: default; - } - - &:hover:not([disabled]) { - text-decoration: underline; - } -} - -@mixin anchor-link() { - @include theme.property(color, primary); - - text-decoration: none; - - &:visited { - @include theme.property(color, primary); - } - - &[disabled] { - pointer-events: none; - cursor: default; - } - - &:focus, - &:hover:not([disabled]) { - text-decoration: underline; - } -} diff --git a/src/lib/button/_configuration.scss b/src/lib/button/_configuration.scss new file mode 100644 index 000000000..f51b8640c --- /dev/null +++ b/src/lib/button/_configuration.scss @@ -0,0 +1,11 @@ +@use './token-utils' as *; + +$host-tokens: [display disabled-cursor]; + +@mixin host-configuration { + @include tokens($includes: $host-tokens); +} + +@mixin configuration { + @include tokens($excludes: $host-tokens); +} diff --git a/src/lib/button/_core.scss b/src/lib/button/_core.scss new file mode 100644 index 000000000..8dc49412e --- /dev/null +++ b/src/lib/button/_core.scss @@ -0,0 +1,171 @@ +@use '../core/styles/typography'; +@use './token-utils' as *; + +@mixin host { + display: #{token(display)}; + position: relative; + outline: none; + -webkit-tap-highlight-color: transparent; +} + +@mixin base { + @include typography.style(button); + + position: relative; + display: #{token(display)}; + align-items: center; + justify-content: #{token(justify)}; + gap: #{token(spacing)}; + z-index: 0; + + box-sizing: border-box; + min-inline-size: #{token(min-width)}; + height: #{token(height)}; + inline-size: 100%; + border-width: #{token(border-width)}; + border-style: #{token(border-style)}; + border-color: #{token(border-color)}; + border-top-left-radius: #{token(border-top-left-radius)}; + border-top-right-radius: #{token(border-top-right-radius)}; + border-bottom-left-radius: #{token(border-bottom-left-radius)}; + border-bottom-right-radius: #{token(border-bottom-right-radius)}; + padding-block: #{token(padding-block)}; + padding-inline: #{token(padding-inline)}; + box-shadow: #{token(shadow)}; + + outline: none; + user-select: none; + -webkit-appearance: none; + vertical-align: middle; + text-decoration: none; + + background: #{token(background)}; + color: #{token(color)}; + cursor: #{token(cursor)}; + + transition-property: box-shadow, background; + transition-duration: #{token(transition-duration)}; + transition-timing-function: #{token(transition-timing)}; + + &::-moz-focus-inner { + padding: 0; + border: 0; + } + + &:hover { + @include override(shadow, hover-shadow); + + background: #{token(hover-background)}; + } + + &:active { + @include override(shadow, active-shadow); + + background: #{token(active-background)}; + } +} + +@mixin slotted-start-end { + font-size: #{token(icon-size)}; +} + +@mixin anchor-base { + position: absolute; + inset: 0; + text-decoration: none; +} + +@mixin text { + @include override(focus-indicator-offset, text-focus-indicator-offset); +} + +@mixin raised { + @include override(background, raised-background); + @include override(color, raised-color); + @include override(shadow, raised-shadow); + + &:hover { + @include override(shadow, raised-hover-shadow); + } + + &:active { + @include override(shadow, raised-active-shadow); + } +} + +@mixin flat { + @include override(background, flat-background); + @include override(color, flat-color); +} + +@mixin outlined { + @include override(background, outlined-background); + @include override(color, outlined-color); + @include override(border-width, outlined-border-width); + @include override(border-style, outlined-border-style); + @include override(border-color, outlined-border-color); +} + +@mixin link { + @include override(color, link-color); + @include override(height, link-height); + @include override(padding, link-padding); + @include override(focus-indicator-offset, link-focus-indicator-offset); + + text-decoration: #{token(link-text-decoration)}; + line-height: #{token(link-line-height)}; + transition: opacity #{token(link-transition-duration)} #{token(link-transition-timing)}; + inline-size: #{token(link-width)}; + + &:hover { + text-decoration: #{token(link-hover-text-decoration)}; + } + + &:active { + opacity: #{token(link-active-opacity)}; + } +} + +@mixin host-disabled { + cursor: #{token(disabled-cursor)}; +} + +@mixin disabled { + @include override(background, disabled-background); + @include override(color, disabled-text-color); + @include override(shadow, disabled-shadow); + + pointer-events: none; +} + +@mixin raised-disabled { + @include override(shadow, raised-disabled-shadow); + @include override(background, raised-disabled-background); + @include override(color, raised-disabled-color); +} + +@mixin flat-disabled { + @include override(background, flat-disabled-background); + @include override(color, flat-disabled-color); +} + +@mixin outlined-disabled { + @include override(border-color, disabled-border-color); +} + +@mixin dense { + @include override(height, dense-height); +} + +@mixin pill { + @include override(shape, pill-shape); + @include override(padding-inline, pill-padding-inline); +} + +@mixin popover-icon { + transition: rotate #{token(popover-icon-transition-duration)} #{token(popover-icon-transition-timing)}; +} + +@mixin popover-icon-open { + rotate: #{token(popover-icon-open-rotation)}; +} diff --git a/src/lib/button/_mixins.scss b/src/lib/button/_mixins.scss deleted file mode 100644 index 729e58808..000000000 --- a/src/lib/button/_mixins.scss +++ /dev/null @@ -1,178 +0,0 @@ -@use '@material/typography/typography'; -@use '@material/feature-targeting/feature-targeting'; -@use '@material/button/button-base'; -@use '@material/button/button-filled'; -@use '@material/button/button-protected'; -@use '@material/button/button-outlined'; -@use '@material/button/button-outlined-theme'; -@use '@material/button/button-shared-theme'; -@use '@material/ripple/ripple'; -@use '@material/ripple/ripple-theme'; -@use '@material/theme/theme'; -@use '@material/theme/color-palette'; -@use '../theme' as forge-theme; - -@mixin core-styles() { - .forge-button { - @include base; - - &__icon { - @include icon; - } - - &__label { - @include label; - } - - &__label + &__icon { - @include icon-trailing; - } - - svg.forge-button__icon { - @include icon-svg; - } - - &--raised, - &--unelevated, - &--outlined { - .forge-button__icon { - @include icon-contained; - } - - .forge-button__label + .forge-button__icon { - @include icon-contained-trailing; - } - } - - &--raised, - &--unelevated { - @include raised-elevated; - } - - &--raised { - @include raised; - } - - &--outlined { - @include outlined; - - &:disabled { - @include outlined-disabled; - } - } - - &--dense { - @include dense; - } - } -} - -@mixin host() { - display: inline-block; -} - -@mixin base() { - @include ripple.surface(); - @include ripple.radius-bounded(); - @include ripple-theme.states(primary, false); - @include button-base.base(feature-targeting.all()); - @include button-shared-theme.shape-radius(small); - @include button-shared-theme.container-fill-color(transparent); - @include button-shared-theme.ink-color(primary); - @include button-shared-theme.density(0); - - text-transform: none; - box-sizing: border-box; - overflow: hidden; -} - -@mixin label() { - @include typography.overflow-ellipsis(); -} - -@mixin icon() { - @include button-base.icon; -} - -@mixin icon-svg() { - @include button-base.icon-svg; -} - -@mixin icon-trailing() { - @include button-base.icon-trailing; -} - -@mixin icon-contained() { - @include button-base.icon-contained; -} - -@mixin icon-contained-trailing() { - @include button-base.icon-contained-trailing; -} - -@mixin outlined() { - @include button-outlined.outlined(feature-targeting.all()); - @include button-outlined-theme.outline-width(button-outlined-theme.$outlined-border-width); - @include button-outlined-theme.outline-color(primary); -} - -@mixin outlined-disabled() { - @include forge-theme.property(border-color, border-color); - @include theme.property(color, text-disabled-on-light); -} - -@mixin raised() { - @include button-protected.raised(feature-targeting.all()); -} - -@mixin dense() { - @include button-shared-theme.density(-3); - - height: 1.5rem; -} - -@mixin raised-elevated() { - @include ripple-theme.states(on-primary, false); - @include button-filled.filled(feature-targeting.all()); - @include button-shared-theme.container-fill-color(primary); - @include button-shared-theme.ink-color(on-primary); -} - -@mixin link() { - cursor: pointer; - border: none; - color: color-palette.$indigo-500; - background-color: transparent; - font-size: inherit; - padding: 0; - outline: none; - text-align: left; - - &[disabled] { - pointer-events: none; - cursor: default; - } - - &:hover:not([disabled]) { - text-decoration: underline; - } -} - -@mixin anchor-link() { - text-decoration: none; - color: color-palette.$indigo-500; - - &:visited { - color: color-palette.$indigo-500; - } - - &[disabled] { - pointer-events: none; - cursor: default; - } - - &:focus, - &:hover:not([disabled]) { - text-decoration: underline; - } -} diff --git a/src/lib/button/_token-utils.scss b/src/lib/button/_token-utils.scss new file mode 100644 index 000000000..646c2a0b4 --- /dev/null +++ b/src/lib/button/_token-utils.scss @@ -0,0 +1,25 @@ +@use '../core/styles/tokens/button/tokens'; +@use '../core/styles/tokens/token-utils'; + +$_module: button; +$_tokens: tokens.$tokens; + +@mixin provide-theme($theme) { + @include token-utils.provide-theme($_module, $_tokens, $theme); +} + +@function token($name, $type: token) { + @return token-utils.token($_module, $_tokens, $name, $type); +} + +@function declare($token) { + @return token-utils.declare($_module, $token); +} + +@mixin override($token, $token-or-value, $type: token) { + @include token-utils.override($_module, $_tokens, $token, $token-or-value, $type); +} + +@mixin tokens($includes: null, $excludes: null) { + @include token-utils.tokens($_module, $_tokens, $includes, $excludes); +} diff --git a/src/lib/button/base/base-button-adapter.ts b/src/lib/button/base/base-button-adapter.ts new file mode 100644 index 000000000..4b28e806c --- /dev/null +++ b/src/lib/button/base/base-button-adapter.ts @@ -0,0 +1,298 @@ +import { getShadowElement, toggleAttribute } from '@tylertech/forge-core'; +import { tylIconArrowDropDown } from '@tylertech/tyler-icons/standard'; +import { BaseAdapter, IBaseAdapter } from '../../core/base/base-adapter'; +import { FOCUS_INDICATOR_CONSTANTS, IFocusIndicatorComponent } from '../../focus-indicator'; +import { IStateLayerComponent, STATE_LAYER_CONSTANTS } from '../../state-layer'; +import { IBaseButton } from './base-button'; +import { BASE_BUTTON_CONSTANTS } from './base-button-constants'; +import { BUTTON_FORM_ATTRIBUTES, cloneAttributes } from '../../core/utils/reflect-utils'; +import { internals } from '../../constants'; +import { supportsPopover } from '../../core/utils/feature-detection'; + +// TODO: remove this augmentation when the TypeScript version is upgraded for latest DOM typings +type TempHTMLElementWithPopover = IBaseButton & { + popoverTargetElement: TempHTMLElementWithPopover | null; + popover: 'manual' | 'auto'; + showPopover(): void; + hidePopover(): void; + togglePopover(): boolean; +}; + +export interface IBaseButtonAdapter extends IBaseAdapter { + initialize(): void; + initializeAnchor(): void; + removeAnchor(): void; + setAnchorHref(href: string): void; + setAnchorTarget(target: string): void; + setAnchorDownload(value: string): void; + setAnchorRel(value: string): void; + setDisabled(value: boolean): void; + ensureAnchorEnabled(value: boolean): void; + clickAnchor(): void; + clickHost(): void; + clickFormButton(type: string): void; + addAnchorEventListener(type: string, listener: EventListener): void; + removeAnchorEventListener(type: string, listener: EventListener): void; + hasPopoverTarget(): boolean; + managePopover(): boolean; + toggleDefaultPopoverIcon(value: boolean): void; +} + +export abstract class BaseButtonAdapter extends BaseAdapter implements IBaseButtonAdapter { + protected _rootElement: HTMLElement; + protected _anchorElement: HTMLAnchorElement | undefined; + protected _focusIndicatorElement: IFocusIndicatorComponent; + protected _stateLayerElement: IStateLayerComponent; + protected _endSlotElement: HTMLSlotElement; + + constructor(component: IBaseButton) { + super(component); + this._rootElement = getShadowElement(this._component, BASE_BUTTON_CONSTANTS.selectors.ROOT) as HTMLButtonElement; + this._focusIndicatorElement = getShadowElement(this._component, FOCUS_INDICATOR_CONSTANTS.elementName) as IFocusIndicatorComponent; + this._stateLayerElement = getShadowElement(this._component, STATE_LAYER_CONSTANTS.elementName) as IStateLayerComponent; + this._endSlotElement = getShadowElement(this._component, BASE_BUTTON_CONSTANTS.selectors.END_SLOT) as HTMLSlotElement; + } + + public initialize(): void { + this._applyHostSemantics(); + } + + public initializeAnchor(): void { + this._anchorElement = this._createAnchorRootElement(); + this._rootElement.insertAdjacentElement('afterend', this._anchorElement); + this._applyHostSemantics(); + } + + public removeAnchor(): void { + this._anchorElement?.remove(); + this._anchorElement = undefined; + this._applyHostSemantics(); + } + + public setAnchorHref(href: string): void { + if (this._anchorElement) { + this._anchorElement.href = href; + } + } + + public setAnchorTarget(target: string): void { + if (this._anchorElement) { + this._anchorElement.target = target; + } + } + + public setAnchorDownload(value: string): void { + if (this._anchorElement) { + this._anchorElement.download = value; + } + } + + public setAnchorRel(value: string): void { + if (this._anchorElement) { + this._anchorElement.rel = value; + } + } + + public setDisabled(value: boolean): void { + if (this._anchorElement) { + return; // Cannot disable an anchor element + } + this.ensureAnchorEnabled(value); + } + + public ensureAnchorEnabled(value: boolean): void { + if (value) { + this._focusIndicatorElement.remove(); + this._stateLayerElement.remove(); + } else { + this._rootElement.append(this._focusIndicatorElement, this._stateLayerElement); + } + + this._component.tabIndex = value ? -1 : 0; + toggleAttribute(this._component, value, 'aria-disabled', 'true'); + } + + public clickAnchor(): void { + this._anchorElement?.click(); + } + + public clickHost(): void { + // Calling click() on the prototype ensures we don't end up in an infinite + // recursion since the host overrides the HTMLElement.click() method + HTMLElement.prototype.click.call(this._component); + } + + public clickFormButton(type: string): void { + if (!this._component.form) { + return; // Nothing for us to do if there is no form element associated to us + } + + if (type === 'submit') { + // We need to set the form value to the button value before submitting the form + this._component[internals].setFormValue(this._component.value); + + // We don't use a real
      - - - - - - + Profile + Sign out \ No newline at end of file diff --git a/src/lib/profile-card/profile-card.scss b/src/lib/profile-card/profile-card.scss index ef7702cfc..aac513f90 100644 --- a/src/lib/profile-card/profile-card.scss +++ b/src/lib/profile-card/profile-card.scss @@ -1,6 +1,5 @@ @use './mixins'; @use '../ripple/forge-ripple'; -@use '../button/forge-button'; @include mixins.core-styles; diff --git a/src/lib/slider/_configuration.scss b/src/lib/slider/_configuration.scss index 44992b310..f4b73993c 100644 --- a/src/lib/slider/_configuration.scss +++ b/src/lib/slider/_configuration.scss @@ -1,56 +1,11 @@ -@use '../core/styles/tokens/slider/tokens'; - -// The max clip is reduced by 1 full tick display which is 2x the container -// size to account for always showing the active track on the outside -// edge of the last tick. -$_active-track-max-clip: calc(100% - var(--_with-tick-marks-container-size) * 2); - -// When the start fraction is !0, add clipping by the tick container size -$_start-fraction-not-zero: min(var(--_start-fraction) * 1e9, 1); -$_active-track-start-offset: calc(var(--_with-tick-marks-container-size) * $_start-fraction-not-zero); -$_active-track-start-clip: calc($_active-track-start-offset + $_active-track-max-clip * var(--_start-fraction)); - -// When the end fraction is !1, add clipping by the tick container size -$_end-fraction-not-one: min((1 - var(--_end-fraction)) * 1e9, 1); -$_active-track-end-offset: calc(var(--_with-tick-marks-container-size) * $_end-fraction-not-one); -$_active-track-end-clip: calc($_active-track-end-offset + $_active-track-max-clip * (1 - var(--_end-fraction))); +@use './token-utils' as *; @mixin configuration { // Internal state variables - --_start-fraction: 0; - --_end-fraction: 0; - --_tick-count: 100; + #{declare(start-fraction)}: 0; + #{declare(end-fraction)}: 0; + #{declare(tick-count)}: 100; // External state variables - --_track-height: #{tokens.get(track-height)}; - --_active-track-color: #{tokens.get(active-track-color)}; - --_active-track-height: #{tokens.get(active-track-height)}; - --_active-track-shape: #{tokens.get(active-track-shape)}; - --_disabled-active-track-color: #{tokens.get(disabled-active-track-color)}; - --_disabled-active-track-opacity: #{tokens.get(disabled-active-track-opacity)}; - --_disabled-handle-color: #{tokens.get(disabled-handle-color)}; - --_disabled-inactive-track-color: #{tokens.get(disabled-inactive-track-color)}; - --_disabled-inactive-track-opacity: #{tokens.get(disabled-inactive-track-opacity)}; - --_focus-handle-color: #{tokens.get(focus-handle-color)}; - --_handle-color: #{tokens.get(handle-color)}; - --_handle-height: #{tokens.get(handle-height)}; - --_handle-shape: #{tokens.get(handle-shape)}; - --_handle-width: #{tokens.get(handle-width)}; - --_hover-handle-color: #{tokens.get(hover-handle-color)}; - --_inactive-track-color: #{tokens.get(inactive-track-color)}; - --_inactive-track-height: #{tokens.get(inactive-track-height)}; - --_inactive-track-shape: #{tokens.get(inactive-track-shape)}; - --_label-container-shape: #{tokens.get(label-container-shape)}; - --_label-container-color: #{tokens.get(label-container-color)}; - --_label-container-height: #{tokens.get(label-container-height)}; - --_label-text-color: #{tokens.get(label-text-color)}; - --_pressed-handle-color: #{tokens.get(pressed-handle-color)}; - --_state-layer-size: #{tokens.get(state-layer-size)}; - --_with-overlap-handle-outline-color: #{tokens.get(with-overlap-handle-outline-color)}; - --_with-overlap-handle-outline-width: #{tokens.get(with-overlap-handle-outline-width)}; - --_with-tick-marks-active-container-color: #{tokens.get(with-tick-marks-active-container-color)}; - --_with-tick-marks-container-size: #{tokens.get(with-tick-marks-container-size)}; - --_with-tick-marks-disabled-active-container-color: #{tokens.get(with-tick-marks-disabled-active-container-color)}; - --_with-tick-marks-disabled-inactive-container-color: #{tokens.get(with-tick-marks-disabled-inactive-container-color)}; - --_with-tick-marks-inactive-container-color: #{tokens.get(with-tick-marks-inactive-container-color)}; + @include tokens; } diff --git a/src/lib/slider/_core.scss b/src/lib/slider/_core.scss index ed0df27c6..ce9f6ba5e 100644 --- a/src/lib/slider/_core.scss +++ b/src/lib/slider/_core.scss @@ -1,27 +1,22 @@ -@use '../core/styles/utils'; -@use '../core/styles/tokens/slider/tokens'; @use '../core/styles/elevation'; @use '../core/styles/typography'; @use '../core/styles/animation'; - -@mixin provide-theme($theme) { - @include utils.provide(tokens.$tokens, $theme, slider); -} +@use './token-utils' as *; // The max clip is reduced by 1 full tick display which is 2x the container // size to account for always showing the active track on the outside // edge of the last tick. -$_active-track-max-clip: calc(100% - var(--_with-tick-marks-container-size) * 2); +$_active-track-max-clip: calc(100% - #{token(with-tick-marks-container-size)} * 2); // When the start fraction is !0, add clipping by the tick container size -$_start-fraction-not-zero: min(var(--_start-fraction) * 1e9, 1); -$_active-track-start-offset: calc(var(--_with-tick-marks-container-size) * $_start-fraction-not-zero); -$_active-track-start-clip: calc($_active-track-start-offset + $_active-track-max-clip * var(--_start-fraction)); +$_start-fraction-not-zero: min(#{token(start-fraction, custom)} * 1e9, 1); +$_active-track-start-offset: calc(#{token(with-tick-marks-container-size)} * #{$_start-fraction-not-zero}); +$_active-track-start-clip: calc(#{$_active-track-start-offset} + #{$_active-track-max-clip} * #{token(start-fraction, custom)}); // When the end fraction is !1, add clipping by the tick container size -$_end-fraction-not-one: min((1 - var(--_end-fraction)) * 1e9, 1); -$_active-track-end-offset: calc(var(--_with-tick-marks-container-size) * $_end-fraction-not-one); -$_active-track-end-clip: calc($_active-track-end-offset + $_active-track-max-clip * (1 - var(--_end-fraction))); +$_end-fraction-not-one: min((1 - #{token(end-fraction, custom)}) * 1e9, 1); +$_active-track-end-offset: calc(#{token(with-tick-marks-container-size)} * #{$_end-fraction-not-one}); +$_active-track-end-clip: calc(#{$_active-track-end-offset} + #{$_active-track-max-clip} * (1 - #{token(end-fraction, custom)})); @mixin container { display: inline-flex; @@ -34,7 +29,7 @@ $_active-track-end-clip: calc($_active-track-end-offset + $_active-track-max-cli display: flex; align-items: center; position: relative; - block-size: var(--_state-layer-size); + block-size: #{token(state-layer-size)}; pointer-events: none; touch-action: none; } @@ -43,8 +38,8 @@ $_active-track-end-clip: calc($_active-track-end-offset + $_active-track-max-cli // Clip the inputs to the space left/right of the center point between the // values so the right input gets pointer events. $_clip-to-start: calc( - var(--_state-layer-size) / 2 + (100% - var(--_state-layer-size)) * - (var(--_start-fraction) + ((var(--_end-fraction) - var(--_start-fraction)) / 2)) + #{token(state-layer-size)} / 2 + (100% - #{token(state-layer-size)}) * + (#{token(start-fraction, custom)} + ((#{token(end-fraction, custom)} - #{token(start-fraction, custom)}) / 2)) ); $_clip-to-end: calc(100% - $_clip-to-start); @@ -80,34 +75,34 @@ $_active-track-end-clip: calc($_active-track-end-offset + $_active-track-max-cli } @mixin track-dimensions { - $_track-padding: calc((var(--_state-layer-size) / 2) - var(--_with-tick-marks-container-size)); + $_track-padding: calc((#{token(state-layer-size)} / 2) - #{token(with-tick-marks-container-size)}); position: absolute; inset-inline-start: $_track-padding; inset-inline-end: $_track-padding; - background-size: calc((100% - var(--_with-tick-marks-container-size) * 2) / var(--_tick-count)) 100%; + background-size: calc((100% - #{token(with-tick-marks-container-size)} * 2) / #{token(tick-count, custom)}) 100%; } @mixin track-active { - block-size: var(--_active-track-height); - border-radius: var(--_active-track-shape); + block-size: #{token(active-track-height)}; + border-radius: #{token(active-track-shape)}; clip-path: inset(0 $_active-track-end-clip 0 $_active-track-start-clip); - background-color: var(--_active-track-color); + background-color: #{token(active-track-color)}; } @mixin track-inactive { - block-size: var(--_inactive-track-height); - border-radius: var(--_inactive-track-shape); - background-color: var(--_inactive-track-color); + block-size: #{token(inactive-track-height)}; + border-radius: #{token(inactive-track-shape)}; + background-color: #{token(inactive-track-color)}; } @mixin track-active-disabled { - background-color: var(--_disabled-active-track-color); + background-color: #{token(disabled-active-track-color)}; } @mixin track-inactive-disabled { - opacity: calc((1 / var(--_disabled-active-track-opacity)) * var(--_disabled-inactive-track-opacity)); - background-color: var(--_disabled-inactive-track-color); + opacity: calc((1 / #{token(disabled-active-track-opacity)}) * #{token(disabled-inactive-track-opacity)}); + background-color: #{token(disabled-inactive-track-color)}; } @mixin rtl-track-clipping { @@ -115,19 +110,19 @@ $_active-track-end-clip: calc($_active-track-end-offset + $_active-track-max-cli } @mixin tickmarks-inactive { - background-image: get-tick-image('var(--_with-tick-marks-inactive-container-color)'); + background-image: get-tick-image(#{token(with-tick-marks-inactive-container-color)}); } @mixin tickmarks-active { - background-image: get-tick-image('var(--_with-tick-marks-active-container-color)'); + background-image: get-tick-image(#{token(with-tick-marks-active-container-color)}); } @mixin tickmarks-active-disabled { - background-image: get-tick-image('var(--_with-tick-marks-disabled-active-container-color)'); + background-image: get-tick-image(#{token(with-tick-marks-disabled-active-container-color)}); } @mixin tickmarks-inactive-disabled { - background-image: get-tick-image('var(--_with-tick-marks-disabled-inactive-container-color)'); + background-image: get-tick-image(#{token(with-tick-marks-disabled-inactive-container-color)}); } @mixin handle-container-dimensions { @@ -137,60 +132,60 @@ $_active-track-end-clip: calc($_active-track-end-offset + $_active-track-max-cli } @mixin handle-container-padded { - padding-inline: calc(var(--_state-layer-size) / 2); + padding-inline: calc(#{token(state-layer-size)} / 2); } @mixin handle-container { position: absolute; inset-block-start: 0; inset-block-end: 0; - inset-inline-start: calc(100% * var(--_start-fraction)); - inline-size: calc(100% * (var(--_end-fraction) - var(--_start-fraction))); + inset-inline-start: calc(100% * #{token(start-fraction, custom)}); + inline-size: calc(100% * (#{token(end-fraction, custom)} - #{token(start-fraction, custom)})); } @mixin handle { position: absolute; - block-size: var(--_state-layer-size); - inline-size: var(--_state-layer-size); - border-radius: var(--_handle-shape); + block-size: #{token(state-layer-size)}; + inline-size: #{token(state-layer-size)}; + border-radius: #{token(handle-shape)}; display: grid; place-items: center; } @mixin handle-start { - inset-inline-start: calc(0px - var(--_state-layer-size) / 2); + inset-inline-start: calc(0px - #{token(state-layer-size)} / 2); } @mixin handle-end { - inset-inline-end: calc(0px - var(--_state-layer-size) / 2); + inset-inline-end: calc(0px - #{token(state-layer-size)} / 2); } @mixin handle-thumb { @include elevation.box-shadow(1); position: absolute; - height: var(--_handle-height); - width: var(--_handle-width); - border-radius: var(--_handle-shape); - background: var(--_handle-color); + height: #{token(handle-height)}; + width: #{token(handle-width)}; + border-radius: #{token(handle-shape)}; + background: #{token(handle-color)}; } @mixin handle-thumb-hover { - background: var(--_hover-handle-color); + background: #{token(hover-handle-color)}; } @mixin handle-thumb-pressed { - background: var(--_pressed-handle-color); + background: #{token(pressed-handle-color)}; } @mixin handle-thumb-focused { - background: var(--_focus-handle-color); + background: #{token(focus-handle-color)}; } @mixin handle-thumb-disabled { @include elevation.box-shadow(0); - background: var(--_disabled-handle-color); + background: #{token(disabled-handle-color)}; } @mixin handle-label { @@ -201,21 +196,21 @@ $_active-track-end-clip: calc($_active-track-end-offset + $_active-track-max-cli display: grid; padding: 4px; place-items: center; - border-radius: var(--_label-container-shape); - color: var(--_label-text-color); + border-radius: #{token(label-container-shape)}; + color: #{token(label-text-color)}; font-weight: 500; white-space: nowrap; inset-block-end: 100%; - min-inline-size: var(--_label-container-height); - min-block-size: var(--_label-container-height); - background: var(--_label-container-color); + min-inline-size: #{token(label-container-height)}; + min-block-size: #{token(label-container-height)}; + background: #{token(label-container-color)}; transition: transform animation.variable(duration-short2) animation.variable(easing-standard); transform-origin: center bottom; transform: scale(0); } @mixin handle-label-disabled { - background: var(--_disabled-handle-color); + background: #{token(disabled-handle-color)}; } @mixin handle-label-content { @@ -234,10 +229,10 @@ $_active-track-end-clip: calc($_active-track-end-offset + $_active-track-max-cli } @mixin handle-label-bottom { - $_triangle-size: calc(var(--_label-container-height) / 2); + $_triangle-size: calc(#{token(label-container-height)} / 2); inline-size: $_triangle-size; block-size: $_triangle-size; - bottom: calc(var(--_label-container-height) / -10); + bottom: calc(#{token(label-container-height)} / -10); transform: rotate(45deg); } @@ -250,7 +245,7 @@ $_active-track-end-clip: calc($_active-track-end-offset + $_active-track-max-cli } @mixin handles-overlapping { - border: var(--_with-overlap-handle-outline-color) solid var(--_with-overlap-handle-outline-width); + border: #{token(with-overlap-handle-outline-color)} solid #{token(with-overlap-handle-outline-width)}; } @mixin on-top { @@ -285,8 +280,8 @@ $_active-track-end-clip: calc($_active-track-end-offset + $_active-track-max-cli &::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; - block-size: var(--_state-layer-size); - inline-size: var(--_state-layer-size); + block-size: #{token(state-layer-size)}; + inline-size: #{token(state-layer-size)}; transform: scaleX(0); opacity: 0; z-index: 2; @@ -294,8 +289,8 @@ $_active-track-end-clip: calc($_active-track-end-offset + $_active-track-max-cli &::-moz-range-thumb { appearance: none; - block-size: var(--_state-layer-size); - inline-size: var(--_state-layer-size); + block-size: #{token(state-layer-size)}; + inline-size: #{token(state-layer-size)}; transform: scaleX(0); opacity: 0; z-index: 2; @@ -310,10 +305,10 @@ $_active-track-end-clip: calc($_active-track-end-offset + $_active-track-max-cli // Returns a background-image with sized circular ticks of the given color. @function get-tick-image($color) { @return radial-gradient( - circle at var(--_with-tick-marks-container-size) center, + circle at #{token(with-tick-marks-container-size)} center, #{$color} 0, - #{$color} calc(var(--_with-tick-marks-container-size) / 2), - transparent calc(var(--_with-tick-marks-container-size) / 2) + #{$color} calc(#{token(with-tick-marks-container-size)} / 2), + transparent calc(#{token(with-tick-marks-container-size)} / 2) ); } diff --git a/src/lib/slider/_token-utils.scss b/src/lib/slider/_token-utils.scss new file mode 100644 index 000000000..30c02e3d8 --- /dev/null +++ b/src/lib/slider/_token-utils.scss @@ -0,0 +1,26 @@ +@use '../core/styles/utils'; +@use '../core/styles/tokens/slider/tokens'; +@use '../core/styles/tokens/token-utils'; + +$_module: slider; +$_tokens: tokens.$tokens; + +@mixin provide-theme($theme) { + @include token-utils.provide-theme($_module, $_tokens, $theme); +} + +@function token($name, $type: token) { + @return token-utils.token($_module, $_tokens, $name, $type); +} + +@function declare($token) { + @return token-utils.declare($_module, $token); +} + +@mixin override($token, $token-or-value, $type: token) { + @include token-utils.override($_module, $_tokens, $token, $token-or-value, $type); +} + +@mixin tokens($includes: null, $excludes: null) { + @include token-utils.tokens($_module, $_tokens, $includes, $excludes); +} diff --git a/src/lib/slider/index.scss b/src/lib/slider/index.scss index c78abe8eb..1ca832e7e 100644 --- a/src/lib/slider/index.scss +++ b/src/lib/slider/index.scss @@ -1,2 +1,3 @@ @forward './configuration'; @forward './core'; +@forward './token-utils' show provide-theme; diff --git a/src/lib/slider/slider-constants.ts b/src/lib/slider/slider-constants.ts index f7368eb6b..3c2cc44db 100644 --- a/src/lib/slider/slider-constants.ts +++ b/src/lib/slider/slider-constants.ts @@ -60,9 +60,9 @@ const events = { }; const cssCustomProperties = { - START_FRACTION: '--_start-fraction', - END_FRACTION: '--_end-fraction', - TICK_COUNT: '--_tick-count' + START_FRACTION: '--_slider-start-fraction', + END_FRACTION: '--_slider-end-fraction', + TICK_COUNT: '--_slider-tick-count' }; const numbers = { diff --git a/src/lib/slider/slider.scss b/src/lib/slider/slider.scss index 2d43fc59a..9638bda49 100644 --- a/src/lib/slider/slider.scss +++ b/src/lib/slider/slider.scss @@ -2,6 +2,11 @@ @use './configuration'; @use '../focus-indicator'; @use '../state-layer'; +@use './token-utils' as *; + +// +// Host +// :host { @include core.container; @@ -11,8 +16,15 @@ display: none; } +// +// Base +// + .forge-slider { @include configuration.configuration; +} + +.forge-slider { @include core.slider; &.range { @@ -20,6 +32,10 @@ } } +// +// Track +// + .track { @include core.track; @@ -49,6 +65,10 @@ } } +// +// Handle +// + .handle-container-padded, .handle-container-block { @include core.handle-container-dimensions; @@ -111,6 +131,10 @@ } } +// +// Disabled +// + :host([disabled]) { .handle-thumb { @include core.handle-thumb-disabled; @@ -195,6 +219,6 @@ forge-focus-indicator { forge-state-layer { @include state-layer.provide-theme(( - color: var(--_handle-color) + color: #{token(handle-color)} )); } \ No newline at end of file diff --git a/src/lib/slider/slider.test.ts b/src/lib/slider/slider.test.ts index 90a42f65f..1a10f45d3 100644 --- a/src/lib/slider/slider.test.ts +++ b/src/lib/slider/slider.test.ts @@ -139,9 +139,9 @@ describe('Slider', () => { expect(el.readonly).to.be.false; expect(el.labeled).to.be.true; expect(el.range).to.be.false; - expect(ctx.rootElement.style.getPropertyValue('--_end-fraction')).to.equal('0.5'); - expect(ctx.rootElement.style.getPropertyValue('--_start-fraction')).to.equal('0'); - expect(ctx.rootElement.style.getPropertyValue('--_tick-count')).to.equal('100'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-end-fraction')).to.equal('0.5'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-start-fraction')).to.equal('0'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-tick-count')).to.equal('100'); expect(ctx.endInputElement.valueAsNumber).to.equal(50); expect(ctx.endInputElement.getAttribute('aria-valuetext')).to.equal('50'); expect(ctx.endInputElement.min).to.equal('0'); @@ -158,7 +158,7 @@ describe('Slider', () => { const ctx = new SliderHarness(el); expect(el.value).to.equal(75); - expect(ctx.rootElement.style.getPropertyValue('--_end-fraction')).to.equal('0.75'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-end-fraction')).to.equal('0.75'); expect(ctx.endInputElement.valueAsNumber).to.equal(75); expect(ctx.endInputElement.getAttribute('aria-valuetext')).to.equal('75'); expect(ctx.endLabelContentElement.textContent).to.equal('75'); @@ -185,7 +185,7 @@ describe('Slider', () => { const ctx = new SliderHarness(el); expect(el.value).to.equal(-100); - expect(ctx.rootElement.style.getPropertyValue('--_end-fraction')).to.equal('0'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-end-fraction')).to.equal('0'); expect(ctx.endInputElement.valueAsNumber).to.equal(0); }); @@ -194,7 +194,7 @@ describe('Slider', () => { const ctx = new SliderHarness(el); expect(el.value).to.equal(500); - expect(ctx.rootElement.style.getPropertyValue('--_end-fraction')).to.equal('1'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-end-fraction')).to.equal('1'); expect(ctx.endInputElement.valueAsNumber).to.equal(100); }); @@ -204,8 +204,8 @@ describe('Slider', () => { expect(el.valueStart).to.equal(-100); expect(el.valueEnd).to.equal(-100); - expect(ctx.rootElement.style.getPropertyValue('--_start-fraction')).to.equal('0'); - expect(ctx.rootElement.style.getPropertyValue('--_end-fraction')).to.equal('0'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-start-fraction')).to.equal('0'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-end-fraction')).to.equal('0'); expect(ctx.startInputElement.valueAsNumber).to.equal(0); expect(ctx.endInputElement.valueAsNumber).to.equal(0); }); @@ -216,8 +216,8 @@ describe('Slider', () => { expect(el.valueStart).to.equal(500); expect(el.valueEnd).to.equal(500); - expect(ctx.rootElement.style.getPropertyValue('--_start-fraction')).to.equal('1'); - expect(ctx.rootElement.style.getPropertyValue('--_end-fraction')).to.equal('1'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-start-fraction')).to.equal('1'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-end-fraction')).to.equal('1'); expect(ctx.startInputElement.valueAsNumber).to.equal(100); expect(ctx.endInputElement.valueAsNumber).to.equal(100); }); @@ -229,7 +229,7 @@ describe('Slider', () => { expect(el.step).to.equal(10); expect(ctx.endInputElement.step).to.equal('10'); expect(ctx.endInputElement.step).to.equal('10'); - expect(ctx.rootElement.style.getPropertyValue('--_tick-count')).to.equal('10'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-tick-count')).to.equal('10'); }); it('should show tickmarks', async () => { @@ -288,8 +288,8 @@ describe('Slider', () => { expect(ctx.startInputElement.max).to.equal('100'); expect(ctx.startInputElement.step).to.equal('1'); expect(ctx.startLabelContentElement.textContent).to.equal('33'); - expect(ctx.rootElement.style.getPropertyValue('--_start-fraction')).to.equal('0.33'); - expect(ctx.rootElement.style.getPropertyValue('--_end-fraction')).to.equal('0.67'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-start-fraction')).to.equal('0.33'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-end-fraction')).to.equal('0.67'); expect(ctx.endInputElement.valueAsNumber).to.equal(67); }); @@ -309,8 +309,8 @@ describe('Slider', () => { expect(ctx.startInputElement.max).to.equal('100'); expect(ctx.startInputElement.step).to.equal('1'); expect(ctx.startLabelContentElement.textContent).to.equal('33'); - expect(ctx.rootElement.style.getPropertyValue('--_start-fraction')).to.equal('0.33'); - expect(ctx.rootElement.style.getPropertyValue('--_end-fraction')).to.equal('0.67'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-start-fraction')).to.equal('0.33'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-end-fraction')).to.equal('0.67'); expect(ctx.endInputElement.valueAsNumber).to.equal(67); }); @@ -669,8 +669,8 @@ describe('Slider', () => { expect(ctx.startInputElement.min).to.equal(String(ctx.element.min)); expect(ctx.startInputElement.max).to.equal(String(ctx.element.max)); expect(ctx.startInputElement.step).to.equal(String(ctx.element.step)); - expect(ctx.rootElement.style.getPropertyValue('--_start-fraction')).to.equal('0.33'); - expect(ctx.rootElement.style.getPropertyValue('--_end-fraction')).to.equal('0.67'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-start-fraction')).to.equal('0.33'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-end-fraction')).to.equal('0.67'); expect(ctx.endInputElement.valueAsNumber).to.equal(67); expect(ctx.endInputElement.getAttribute('aria-valuetext')).to.equal('67'); expect(ctx.endLabelContentElement.textContent).to.equal('67'); @@ -690,8 +690,8 @@ describe('Slider', () => { expect(ctx.startHandleElement).not.to.be.ok; expect(ctx.startStateLayer).not.to.be.ok; expect(ctx.startLabelContentElement).not.to.ok; - expect(ctx.rootElement.style.getPropertyValue('--_start-fraction')).to.equal('0'); - expect(ctx.rootElement.style.getPropertyValue('--_end-fraction')).to.equal('0.5'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-start-fraction')).to.equal('0'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-end-fraction')).to.equal('0.5'); expect(ctx.endInputElement.valueAsNumber).to.equal(50); expect(ctx.endInputElement.getAttribute('aria-valuetext')).to.equal('50'); expect(ctx.endLabelContentElement.textContent).to.equal('50'); @@ -712,8 +712,8 @@ describe('Slider', () => { expect(ctx.startInputElement.min).to.equal('40'); expect(ctx.startInputElement.max).to.equal('60'); expect(ctx.startInputElement.step).to.equal('2'); - expect(ctx.rootElement.style.getPropertyValue('--_start-fraction')).to.equal('0'); - expect(ctx.rootElement.style.getPropertyValue('--_end-fraction')).to.equal('1'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-start-fraction')).to.equal('0'); + expect(ctx.rootElement.style.getPropertyValue('--_slider-end-fraction')).to.equal('1'); expect(ctx.endInputElement.valueAsNumber).to.equal(60); expect(ctx.endInputElement.getAttribute('aria-valuetext')).to.equal('60'); expect(ctx.endLabelContentElement.textContent).to.equal('60'); diff --git a/src/lib/state-layer/_configuration.scss b/src/lib/state-layer/_configuration.scss index 78eb611e7..e219ada23 100644 --- a/src/lib/state-layer/_configuration.scss +++ b/src/lib/state-layer/_configuration.scss @@ -1,12 +1,5 @@ -@use '../core/styles/tokens/state-layer/tokens'; +@use './token-utils' as *; @mixin configuration { - --_color: #{tokens.get(color)}; - --_hover-color: #{tokens.get(hover-color)}; - --_hover-opacity: #{tokens.get(hover-opacity)}; - --_pressed-color: #{tokens.get(pressed-color)}; - --_pressed-opacity: #{tokens.get(pressed-opacity)}; - --_hover-duration: #{tokens.get(hover-duration)}; - --_pressed-duration: #{tokens.get(pressed-duration)}; - --_animation-duration: #{tokens.get(animation-duration)}; + @include tokens; } diff --git a/src/lib/state-layer/_core.scss b/src/lib/state-layer/_core.scss index 6d0134851..bea858175 100644 --- a/src/lib/state-layer/_core.scss +++ b/src/lib/state-layer/_core.scss @@ -1,10 +1,5 @@ -@use '../core/styles/utils'; -@use '../core/styles/tokens/state-layer/tokens'; @use './configuration'; - -@mixin provide-theme($theme) { - @include utils.provide(tokens.$tokens, $theme, state-layer); -} +@use './token-utils' as *; @mixin host { @include container; @@ -44,13 +39,13 @@ } @mixin hovered { - background-color: var(--_hover-color); - opacity: var(--_hover-opacity); + background-color: #{token(hover-color)}; + opacity: #{token(hover-opacity)}; } @mixin pressed { - opacity: var(--_pressed-opacity); - transition-duration: var(--_pressed-duration); + opacity: #{token(pressed-opacity)}; + transition-duration: #{token(pressed-duration)}; } @mixin layer-base { @@ -59,20 +54,20 @@ } @mixin hover-base { - background-color: var(--_hover-color); + background-color: #{token(hover-color)}; inset: 0; - transition: opacity var(--_hover-duration) linear, - background-color var(--_hover-duration) linear; + transition: opacity #{token(hover-duration)} linear, + background-color #{token(hover-duration)} linear; } @mixin ripple-base { background: radial-gradient( closest-side, - var(--_pressed-color) max(calc(100% - 70px), 65%), + #{token(pressed-color)} max(calc(100% - 70px), 65%), transparent 100% ); transform-origin: center center; - transition: opacity var(--_animation-duration) linear; + transition: opacity #{token(animation-duration)} linear; } /// @@ -108,7 +103,7 @@ &:active { .forge-state-layer::before { - opacity: var(--_pressed-opacity); + opacity: #{token(pressed-opacity)}; } } } diff --git a/src/lib/state-layer/_token-utils.scss b/src/lib/state-layer/_token-utils.scss new file mode 100644 index 000000000..6b51eb0a9 --- /dev/null +++ b/src/lib/state-layer/_token-utils.scss @@ -0,0 +1,26 @@ +@use '../core/styles/utils'; +@use '../core/styles/tokens/state-layer/tokens'; +@use '../core/styles/tokens/token-utils'; + +$_module: state-layer; +$_tokens: tokens.$tokens; + +@mixin provide-theme($theme) { + @include token-utils.provide-theme($_module, $_tokens, $theme); +} + +@function token($name, $type: token) { + @return token-utils.token($_module, $_tokens, $name, $type); +} + +@function declare($token) { + @return token-utils.declare($_module, $token); +} + +@mixin override($token, $token-or-value, $type: token) { + @include token-utils.override($_module, $_tokens, $token, $token-or-value, $type); +} + +@mixin tokens($includes: null, $excludes: null) { + @include token-utils.tokens($_module, $_tokens, $includes, $excludes); +} diff --git a/src/lib/state-layer/index.scss b/src/lib/state-layer/index.scss index c78abe8eb..1ca832e7e 100644 --- a/src/lib/state-layer/index.scss +++ b/src/lib/state-layer/index.scss @@ -1,2 +1,3 @@ @forward './configuration'; @forward './core'; +@forward './token-utils' show provide-theme; diff --git a/src/lib/state-layer/state-layer-adapter.ts b/src/lib/state-layer/state-layer-adapter.ts index 8c66faac5..3cc7c5aaf 100644 --- a/src/lib/state-layer/state-layer-adapter.ts +++ b/src/lib/state-layer/state-layer-adapter.ts @@ -36,6 +36,7 @@ export class StateLayerAdapter extends BaseAdapter impleme this._destroyDeferListener(); this._destroyDeferListener = undefined; } + this._targetElement = null; } public async deferInitialization(listener: (evt?: PointerEvent) => void): Promise { diff --git a/src/lib/state-layer/state-layer-foundation.ts b/src/lib/state-layer/state-layer-foundation.ts index 62639ac29..d3f7a9097 100644 --- a/src/lib/state-layer/state-layer-foundation.ts +++ b/src/lib/state-layer/state-layer-foundation.ts @@ -54,8 +54,8 @@ export class StateLayerFoundation implements IStateLayerFoundation { this._pointerState = PointerState.INACTIVE; this._adapter.setHovered(false); this._adapter.setPressed(false); + this._removeListeners(); // Must be called before destroying adapter this._adapter.destroy(); - this._removeListeners(); } public playAnimation(coords?: StateLayerCoords): void { diff --git a/src/lib/state-layer/state-layer.scss b/src/lib/state-layer/state-layer.scss index 751c3c767..a7204f15d 100644 --- a/src/lib/state-layer/state-layer.scss +++ b/src/lib/state-layer/state-layer.scss @@ -1,6 +1,10 @@ @use './core'; @use './configuration'; +// +// Host +// + :host { @include core.host; } @@ -15,6 +19,9 @@ .forge-state-layer { @include configuration.configuration; +} + +.forge-state-layer { @include core.surface; &--hovered::before { diff --git a/src/lib/switch/_configuration.scss b/src/lib/switch/_configuration.scss index df7523404..04a6062f2 100644 --- a/src/lib/switch/_configuration.scss +++ b/src/lib/switch/_configuration.scss @@ -1,91 +1,5 @@ -@use '../core/styles/tokens/switch/tokens' as switch-tokens; +@use './token-utils' as *; @mixin configuration { - // Shared - --_handle-size: #{switch-tokens.get(handle-size)}; - --_handle-scale: #{switch-tokens.get(handle-scale)}; - --_handle-elevation: #{switch-tokens.get(handle-elevation)}; - --_track-border-width: #{switch-tokens.get(track-border-width)}; - --_track-border-color: #{switch-tokens.get(track-border-color)}; - --_icon-color: #{switch-tokens.get(icon-color)}; - --_icon-size: #{switch-tokens.get(icon-size)}; - --_icon-scale: #{switch-tokens.get(icon-scale)}; - --_state-layer-size: #{switch-tokens.get(state-layer-size)}; - --_state-layer-dense-size: #{switch-tokens.get(state-layer-dense-size)}; - - // Handle - --_handle-on-color: #{switch-tokens.get(handle-on-color)}; - --_handle-off-color: #{switch-tokens.get(handle-off-color)}; - --_handle-width: #{switch-tokens.get(handle-width)}; - --_handle-height: #{switch-tokens.get(handle-height)}; - --_handle-on-scale: #{switch-tokens.get(handle-on-scale)}; - --_handle-off-scale: #{switch-tokens.get(handle-off-scale)}; - --_handle-shape: #{switch-tokens.get(handle-shape)}; - --_handle-on-elevation: #{switch-tokens.get(handle-on-elevation)}; - --_handle-off-elevation: #{switch-tokens.get(handle-off-elevation)}; - - // Track - --_track-on-color: #{switch-tokens.get(track-on-color)}; - --_track-off-color: #{switch-tokens.get(track-off-color)}; - --_track-width: #{switch-tokens.get(track-width)}; - --_track-height: #{switch-tokens.get(track-height)}; - --_track-shape: #{switch-tokens.get(track-shape)}; - --_track-on-border-width: #{switch-tokens.get(track-on-border-width)}; - --_track-off-border-width: #{switch-tokens.get(track-off-border-width)}; - --_track-on-border-color: #{switch-tokens.get(track-on-border-color)}; - --_track-off-border-color: #{switch-tokens.get(track-off-border-color)}; - - // Icon - --_icon-on-color: #{switch-tokens.get(icon-on-color)}; - --_icon-off-color: #{switch-tokens.get(icon-off-color)}; - --_icon-on-size: #{switch-tokens.get(icon-on-size)}; - --_icon-off-size: #{switch-tokens.get(icon-off-size)}; - --_icon-on-scale: #{switch-tokens.get(icon-on-scale)}; - --_icon-off-scale: #{switch-tokens.get(icon-off-scale)}; - - // Label - --_gap: #{switch-tokens.get(gap)}; - --_justify: #{switch-tokens.get(justify)}; - --_direction: #{switch-tokens.get(direction)}; - - // State layer - --_state-layer-width: #{switch-tokens.get(state-layer-width)}; - --_state-layer-height: #{switch-tokens.get(state-layer-height)}; - --_state-layer-on-color: #{switch-tokens.get(state-layer-on-color)}; - --_state-layer-off-color: #{switch-tokens.get(state-layer-off-color)}; - - // Dense - --_state-layer-dense-width: #{switch-tokens.get(state-layer-dense-width)}; - --_state-layer-dense-height: #{switch-tokens.get(state-layer-dense-height)}; - - // Disabled - --_disabled-opacity: #{switch-tokens.get(disabled-opacity)}; - - // Active - --_handle-active-on-color: #{switch-tokens.get(handle-active-on-color)}; - --_handle-active-off-color: #{switch-tokens.get(handle-active-off-color)}; - --_handle-active-scale: #{switch-tokens.get(handle-active-scale)}; - --_handle-active-on-scale: #{switch-tokens.get(handle-active-on-scale)}; - --_handle-active-off-scale: #{switch-tokens.get(handle-active-off-scale)}; - --_handle-active-elevation: #{switch-tokens.get(handle-active-elevation)}; - --_handle-active-on-elevation: #{switch-tokens.get(handle-active-on-elevation)}; - --_handle-active-off-elevation: #{switch-tokens.get(handle-active-off-elevation)}; - - --_track-active-on-color: #{switch-tokens.get(track-active-on-color)}; - --_track-active-off-color: #{switch-tokens.get(track-active-off-color)}; - --_track-active-on-border-width: #{switch-tokens.get(track-active-on-border-width)}; - --_track-active-off-border-width: #{switch-tokens.get(track-active-off-border-width)}; - --_track-active-on-border-color: #{switch-tokens.get(track-active-on-border-color)}; - --_track-active-off-border-color: #{switch-tokens.get(track-active-off-border-color)}; - - --_icon-active-on-color: #{switch-tokens.get(icon-active-on-color)}; - --_icon-active-off-color: #{switch-tokens.get(icon-active-off-color)}; - --_icon-active-scale: #{switch-tokens.get(icon-active-scale)}; - --_icon-active-on-scale: #{switch-tokens.get(icon-active-on-scale)}; - --_icon-active-off-scale: #{switch-tokens.get(icon-active-off-scale)}; - - // Animation - --_animation-duration: #{switch-tokens.get(animation-duration)}; - --_animation-timing: #{switch-tokens.get(animation-timing)}; - --_active-animation-timing: #{switch-tokens.get(active-animation-timing)}; + @include tokens; } diff --git a/src/lib/switch/_core.scss b/src/lib/switch/_core.scss index ffb2ae01a..88622dd79 100644 --- a/src/lib/switch/_core.scss +++ b/src/lib/switch/_core.scss @@ -1,27 +1,22 @@ @use '../core/styles/typography'; -@use '../core/styles/utils'; -@use '../core/styles/tokens/switch/tokens'; +@use './token-utils' as *; // The tallest element may be absolutely positioned so the container needs to // be manually sized to match. -$_container-block-size: max(var(--_handle-height), var(--_track-height), var(--_current-state-layer-height)); +$_container-block-size: max(#{token(handle-height)}, #{token(track-height)}, #{token(current-state-layer-height, custom)}); // The effective track border radius, for use in other calculations. -$_track-border-radius: calc(var(--_track-height) / 2); +$_track-border-radius: calc(#{token(track-height)} / 2); // Returns the wider of the handle width and state layer width. -$_max-handle-inline-size: max(var(--_handle-width), var(--_current-state-layer-width)); +$_max-handle-inline-size: max(#{token(handle-width)}, #{token(current-state-layer-width, custom)}); // Offsets the track to place the center of its border caps at the center of // the handle's off and on positions. $_track-margin-inline: calc(#{$_max-handle-inline-size} / 2 - #{$_track-border-radius}); // The x translation of the handle in its on state. -$_handle-on-translate: calc(var(--_track-width) - #{$_track-border-radius} * 2); - -@mixin provide-theme($theme) { - @include utils.provide(tokens.$tokens, $theme, switch); -} +$_handle-on-translate: calc(#{token(track-width)} - #{$_track-border-radius} * 2); @mixin host { display: inline-block; @@ -29,17 +24,17 @@ $_handle-on-translate: calc(var(--_track-width) - #{$_track-border-radius} * 2); @mixin switch { position: relative; - flex-direction: var(--_direction); + flex-direction: #{token(direction)}; flex-shrink: 0; align-items: center; - justify-content: var(--_justify); - gap: var(--_gap); + justify-content: #{token(justify)}; + gap: #{token(gap)}; display: flex; } @mixin switch-disabled { - opacity: var(--_disabled-opacity); + opacity: #{token(disabled-opacity)}; } @mixin input { @@ -73,40 +68,40 @@ $_handle-on-translate: calc(var(--_track-width) - #{$_track-border-radius} * 2); @mixin track { transition-property: background-color, border-color, border-width; - transition-duration: var(--_animation-duration); - transition-timing-function: var(--_animation-timing); + transition-duration: #{token(animation-duration)}; + transition-timing-function: #{token(animation-timing)}; box-sizing: border-box; margin-inline: #{$_track-margin-inline}; - border-width: var(--_track-off-border-width); - border-color: var(--_track-off-border-color); + border-width: #{token(track-off-border-width)}; + border-color: #{token(track-off-border-color)}; border-style: solid; - border-radius: var(--_track-shape); - inline-size: var(--_track-width); - block-size: var(--_track-height); + border-radius: #{token(track-shape)}; + inline-size: #{token(track-width)}; + block-size: #{token(track-height)}; - background-color: var(--_track-off-color); + background-color: #{token(track-off-color)}; } @mixin track-on { - border-width: var(--_track-on-border-width); - border-color: var(--_track-on-border-color); + border-width: #{token(track-on-border-width)}; + border-color: #{token(track-on-border-color)}; - background-color: var(--_track-on-color); + background-color: #{token(track-on-color)}; } @mixin track-active { - border-width: var(--_track-active-off-border-width); - border-color: var(--_track-active-off-border-color); + border-width: #{token(track-active-off-border-width)}; + border-color: #{token(track-active-off-border-color)}; - background-color: var(--_track-active-off-color); + background-color: #{token(track-active-off-color)}; } @mixin track-active-on { - border-width: var(--_track-active-on-border-width); - border-color: var(--_track-active-on-border-color); + border-width: #{token(track-active-on-border-width)}; + border-color: #{token(track-active-on-border-color)}; - background-color: var(--_track-active-on-color); + background-color: #{token(track-active-on-color)}; } @mixin handle { @@ -117,12 +112,12 @@ $_handle-on-translate: calc(var(--_track-width) - #{$_track-border-radius} * 2); display: flex; transition-property: translate; - transition-duration: var(--_animation-duration); - transition-timing-function: var(--_animation-timing); + transition-duration: #{token(animation-duration)}; + transition-timing-function: #{token(animation-timing)}; - border-radius: var(--_handle-shape); - inline-size: var(--_current-state-layer-width); - block-size: var(--_current-state-layer-height); + border-radius: #{token(handle-shape)}; + inline-size: #{token(current-state-layer-width, custom)}; + block-size: #{token(current-state-layer-height, custom)}; &::before { content: ''; @@ -130,19 +125,19 @@ $_handle-on-translate: calc(var(--_track-width) - #{$_track-border-radius} * 2); position: relative; display: block; - scale: var(--_handle-off-scale); + scale: #{token(handle-off-scale)}; transition: - background-color var(--_animation-duration) var(--_animation-timing), - box-shadow var(--_animation-duration) var(--_animation-timing), - scale var(--_animation-duration) var(--_active-animation-timing); + background-color #{token(animation-duration)} #{token(animation-timing)}, + box-shadow #{token(animation-duration)} #{token(animation-timing)}, + scale #{token(animation-duration)} #{token(active-animation-timing)}; - box-shadow: var(--_handle-off-elevation); - border-radius: var(--_handle-shape); - inline-size: var(--_handle-width); - block-size: var(--_handle-height); + box-shadow: #{token(handle-off-elevation)}; + border-radius: #{token(handle-shape)}; + inline-size: #{token(handle-width)}; + block-size: #{token(handle-height)}; - background-color: var(--_handle-off-color); + background-color: #{token(handle-off-color)}; } } @@ -150,31 +145,31 @@ $_handle-on-translate: calc(var(--_track-width) - #{$_track-border-radius} * 2); translate: #{$_handle-on-translate}; &::before { - scale: var(--_handle-on-scale); + scale: #{token(handle-on-scale)}; - box-shadow: var(--_handle-on-elevation); + box-shadow: #{token(handle-on-elevation)}; - background-color: var(--_handle-on-color); + background-color: #{token(handle-on-color)}; } } @mixin handle-active { &::before { - scale: var(--_handle-active-off-scale); + scale: #{token(handle-active-off-scale)}; - box-shadow: var(--_handle-active-off-elevation); + box-shadow: #{token(handle-active-off-elevation)}; - background-color: var(--_handle-active-off-color); + background-color: #{token(handle-active-off-color)}; } } @mixin handle-active-on { &::before { - scale: var(--_handle-active-on-scale); + scale: #{token(handle-active-on-scale)}; - box-shadow: var(--_handle-active-on-elevation); + box-shadow: #{token(handle-active-on-elevation)}; - background-color: var(--_handle-active-on-color); + background-color: #{token(handle-active-on-color)}; } } @@ -185,8 +180,6 @@ $_handle-on-translate: calc(var(--_track-width) - #{$_track-border-radius} * 2); } @mixin icon { - --forge-icon-font-size: var(--_icon-off-size); - position: absolute; align-items: center; justify-content: center; @@ -194,52 +187,50 @@ $_handle-on-translate: calc(var(--_track-width) - #{$_track-border-radius} * 2); display: flex; transition-property: opacity, scale; - transition-duration: var(--_animation-duration); - transition-timing-function: var(--_animation-timing); + transition-duration: #{token(animation-duration)}; + transition-timing-function: #{token(animation-timing)}; - inline-size: var(--_icon-off-size); - block-size: var(--_icon-off-size); + inline-size: #{token(icon-off-size)}; + block-size: #{token(icon-off-size)}; - color: var(--_icon-off-color); - fill: var(--_icon-off-color); + color: #{token(icon-off-color)}; + fill: #{token(icon-off-color)}; - font-size: var(--_icon-off-size); + font-size: #{token(icon-off-size)}; } @mixin icon-on { - --forge-icon-font-size: var(--_icon-on-size); - - inline-size: var(--_icon-on-size); - block-size: var(--_icon-on-size); + inline-size: #{token(icon-on-size)}; + block-size: #{token(icon-on-size)}; - color: var(--_icon-on-color); - fill: var(--_icon-on-color); + color: #{token(icon-on-color)}; + fill: #{token(icon-on-color)}; - font-size: var(--_icon-on-size); + font-size: #{token(icon-on-size)}; } @mixin icon-active { - scale: var(--_icon-active-off-scale); + scale: #{token(icon-active-off-scale)}; - color: var(--_icon-active-off-color); - fill: var(--_icon-active-off-color); + color: #{token(icon-active-off-color)}; + fill: #{token(icon-active-off-color)}; } @mixin icon-active-on { - scale: var(--_icon-active-on-scale); + scale: #{token(icon-active-on-scale)}; - color: var(--_icon-active-on-color); - fill: var(--_icon-active-on-color); + color: #{token(icon-active-on-color)}; + fill: #{token(icon-active-on-color)}; } @mixin icon-on-shown { opacity: 1; - scale: var(--_icon-on-scale); + scale: #{token(icon-on-scale)}; } @mixin icon-off-shown { opacity: 1; - scale: var(--_icon-off-scale); + scale: #{token(icon-off-scale)}; } @mixin icon-hidden { diff --git a/src/lib/switch/_token-utils.scss b/src/lib/switch/_token-utils.scss new file mode 100644 index 000000000..0fca3ae3a --- /dev/null +++ b/src/lib/switch/_token-utils.scss @@ -0,0 +1,25 @@ +@use '../core/styles/tokens/switch/tokens'; +@use '../core/styles/tokens/token-utils'; + +$_module: switch; +$_tokens: tokens.$tokens; + +@mixin provide-theme($theme) { + @include token-utils.provide-theme($_module, $_tokens, $theme); +} + +@function token($name, $type: token) { + @return token-utils.token($_module, $_tokens, $name, $type); +} + +@function declare($token) { + @return token-utils.declare($_module, $token); +} + +@mixin override($token, $token-or-value, $type: token) { + @include token-utils.override($_module, $_tokens, $token, $token-or-value, $type); +} + +@mixin tokens($includes: null, $excludes: null) { + @include token-utils.tokens($_module, $_tokens, $includes, $excludes); +} diff --git a/src/lib/switch/index.scss b/src/lib/switch/index.scss index c78abe8eb..1ca832e7e 100644 --- a/src/lib/switch/index.scss +++ b/src/lib/switch/index.scss @@ -1,2 +1,3 @@ @forward './configuration'; @forward './core'; +@forward './token-utils' show provide-theme; diff --git a/src/lib/switch/switch.scss b/src/lib/switch/switch.scss index e477d0e98..0f34d750d 100644 --- a/src/lib/switch/switch.scss +++ b/src/lib/switch/switch.scss @@ -1,8 +1,8 @@ @use './core'; @use './configuration'; -@use '../core/styles/tokens/switch/tokens'; @use '../focus-indicator'; @use '../state-layer'; +@use './token-utils' as *; :host { @include core.host; @@ -12,13 +12,16 @@ display: none; } -// Off .forge-switch { @include configuration.configuration; +} + +// Off +.forge-switch { @include core.switch; - --_current-state-layer-width: var(--_state-layer-width); - --_current-state-layer-height: var(--_state-layer-height); + #{declare(current-state-layer-width)}: #{token(state-layer-width)}; + #{declare(current-state-layer-height)}: #{token(state-layer-height)}; .container { @include core.container; @@ -39,7 +42,19 @@ .icon { @include core.icon; + // TODO: Use when icon supports tokens + // @include icon.provide-theme(( + // font-size: #{token(icon-size)} + // )); + --forge-icon-font-size: #{token(icon-off-size)}; + &__on { + // TODO: Use when icon supports tokens + // @include icon.provide-theme(( + // font-size: #{token(icon-on-size)} + // )); + --forge-icon-font-size: #{token(icon-on-size)}; + @include core.icon-on; @include core.icon-hidden; } @@ -85,7 +100,7 @@ forge-state-layer { @include state-layer.provide-theme(( - color: var(--_state-layer-on-color) + color: #{token(state-layer-on-color)} )); } } @@ -130,8 +145,8 @@ :host([dense]) { .forge-switch { // Set the state layer width and height for use in calculations of other properties - --_current-state-layer-width: var(--_state-layer-dense-width); - --_current-state-layer-height: var(--_state-layer-dense-height); + #{declare(current-state-layer-width)}: #{token(state-layer-dense-width)}; + #{declare(current-state-layer-height)}: #{token(state-layer-dense-height)}; } } @@ -162,20 +177,20 @@ // Reduced motion @media (prefers-reduced-motion) { .switch { - --_animation-duration: 0s; + @include override(animation-duration, 0s); } } forge-state-layer { @include state-layer.provide-theme(( - color: var(--_state-layer-off-color) + color: #{token(state-layer-off-color)} )); } /* stylelint-disable length-zero-no-unit */ forge-focus-indicator { @include focus-indicator.provide-theme(( - shape: var(--_track-shape), + shape: #{token(track-shape)}, outward-offset: 0px )); } diff --git a/src/lib/switch/switch.ts b/src/lib/switch/switch.ts index 9951c2151..4f36ce586 100644 --- a/src/lib/switch/switch.ts +++ b/src/lib/switch/switch.ts @@ -117,7 +117,6 @@ declare global { * @cssproperty --forge-switch-icon-on-size - The size of the handle icon in the switch's on state. * @cssproperty --forge-switch-icon-off-size - The size of the handle icon in the switch's off state. * @cssproperty --forge-switch-icon-scale - The scale transformation applied to the handle icons. - * @cssproperty --forge-switch-icon-active-scale - The scale transformation applied to the handle icons when the switch is active (pressed). * @cssproperty --forge-switch-icon-on-scale - The scale transformation applied to the handle icons in the switch's on state. * @cssproperty --forge-switch-icon-off-scale - The scale transformation applied to the handle icons in the switch's off state. * @cssproperty --forge-switch-icon-active-on-scale - The scale transformation applied to the handle icons when the switch is active (pressed) in its on state. diff --git a/src/lib/tabs/tab-bar/_configuration.scss b/src/lib/tabs/tab-bar/_configuration.scss index 1e043e959..04a6062f2 100644 --- a/src/lib/tabs/tab-bar/_configuration.scss +++ b/src/lib/tabs/tab-bar/_configuration.scss @@ -1,8 +1,5 @@ -@use '../../core/styles/tokens/tabs/tab-bar/tokens'; +@use './token-utils' as *; @mixin configuration { - --_container-justify: #{tokens.get(container-justify)}; - --_tab-flex: #{tokens.get(tab-flex)}; - --_divider-color: #{tokens.get(divider-color)}; - --_divider-thickness: #{tokens.get(divider-thickness)}; + @include tokens; } diff --git a/src/lib/tabs/tab-bar/_core.scss b/src/lib/tabs/tab-bar/_core.scss index 5375289ec..83f25f0aa 100644 --- a/src/lib/tabs/tab-bar/_core.scss +++ b/src/lib/tabs/tab-bar/_core.scss @@ -1,9 +1,4 @@ -@use '../../core/styles/utils'; -@use '../../core/styles/tokens/tabs/tab-bar/tokens'; - -@mixin provide-theme($theme) { - @include utils.provide(tokens.$tokens, $theme, tab-bar); -} +@use './token-utils' as *; @mixin host { position: relative; @@ -13,7 +8,7 @@ @mixin base { position: relative; display: flex; - justify-content: var(--_container-justify); + justify-content: #{token(justify)}; align-items: flex-end; box-sizing: border-box; overflow: auto; @@ -26,6 +21,14 @@ } } +@mixin slotted-base { + flex: #{token(stretch)}; +} + +@mixin slotted-selected { + z-index: 1; +} + @mixin container { position: relative; display: grid; @@ -33,7 +36,7 @@ max-width: 100%; max-height: 100%; align-items: center; - border-bottom: var(--_divider-thickness) solid var(--_divider-color); + border-bottom: #{token(divider-thickness)} solid #{token(divider-color)}; } @mixin container-vertical { @@ -41,7 +44,7 @@ grid-template-rows: auto 1fr auto; align-items: initial; border-bottom: none; - border-right: var(--_divider-thickness) solid var(--_divider-color); + border-right: #{token(divider-thickness)} solid #{token(divider-color)}; height: 100%; } @@ -53,3 +56,17 @@ grid-column: auto; grid-row: 2; } + +@mixin vertical-scroll-button { + justify-self: center; +} + +@mixin inverted { + border-bottom: none; + border-top: #{variale(divider-thickness)} solid #{token(divider-color)}; +} + +@mixin vertical-inverted { + border-bottom: none; + border-top: #{token(divider-thickness)} solid #{token(divider-color)}; +} diff --git a/src/lib/tabs/tab-bar/_token-utils.scss b/src/lib/tabs/tab-bar/_token-utils.scss new file mode 100644 index 000000000..d4225d6ab --- /dev/null +++ b/src/lib/tabs/tab-bar/_token-utils.scss @@ -0,0 +1,25 @@ +@use '../../core/styles/tokens/tabs/tab-bar/tokens'; +@use '../../core/styles/tokens/token-utils'; + +$_module: tab-bar; +$_tokens: tokens.$tokens; + +@mixin provide-theme($theme) { + @include token-utils.provide-theme($_module, $_tokens, $theme); +} + +@function token($name, $type: token) { + @return token-utils.token($_module, $_tokens, $name, $type); +} + +@function declare($token) { + @return token-utils.declare($_module, $token); +} + +@mixin override($token, $token-or-value, $type: token) { + @include token-utils.override($_module, $_tokens, $token, $token-or-value, $type); +} + +@mixin tokens($includes: null, $excludes: null) { + @include token-utils.tokens($_module, $_tokens, $includes, $excludes); +} diff --git a/src/lib/tabs/tab-bar/index.scss b/src/lib/tabs/tab-bar/index.scss index e001955ad..1ca832e7e 100644 --- a/src/lib/tabs/tab-bar/index.scss +++ b/src/lib/tabs/tab-bar/index.scss @@ -1,2 +1,3 @@ @forward './configuration'; -@forward './core'; \ No newline at end of file +@forward './core'; +@forward './token-utils' show provide-theme; diff --git a/src/lib/tabs/tab-bar/tab-bar.scss b/src/lib/tabs/tab-bar/tab-bar.scss index 5c9f3f06a..bda4b53b3 100644 --- a/src/lib/tabs/tab-bar/tab-bar.scss +++ b/src/lib/tabs/tab-bar/tab-bar.scss @@ -1,12 +1,48 @@ @use './core'; @use './configuration'; @use '../../icon-button/forge-icon-button'; +@use './token-utils' as *; + +// +// Host +// -/// Host styles :host { @include core.host; } +:host([hidden]) { + display: none; +} + +// +// Base styles +// + +.container { + @include configuration.configuration; +} + +.container { + @include core.container; +} + +.scroll-container { + @include core.base; +} + +::slotted(*) { + @include core.slotted-base; +} + +::slotted([selected]) { + @include core.slotted-selected; +} + +// +// Vertical +// + :host([vertical]) { .container { @include core.container-vertical; @@ -17,59 +53,51 @@ } .scroll-button { - justify-self: center; + @include core.vertical-scroll-button; } } +// +// Inverted +// + :host([inverted]:not([vertical])) { .container { - border-bottom: none; - border-top: var(--_divider-thickness) solid var(--_divider-color); + @include core.inverted; } } :host([inverted][vertical]) { .container { - border-right: none; - border-left: var(--_divider-thickness) solid var(--_divider-color); + @include core.vertical-inverted; } } +// +// Clustered +// + :host([clustered]) { - --forge-tab-bar-justify: flex-start; - --forge-tab-bar-stretch: 0; + .container { + @include override(justify, flex-start); + @include override(stretch, 0); + } } :host([clustered=start]) { - --forge-tab-bar-justify: flex-start; + .container { + @include override(justify, flex-start); + } } :host([clustered=center]) { - --forge-tab-bar-justify: center; + .container { + @include override(justify, center); + } } :host([clustered=end]) { - --forge-tab-bar-justify: flex-end; -} - -:host([hidden]) { - display: none; -} - -/// Base styles -.container { - @include configuration.configuration; - @include core.container; -} - -.scroll-container { - @include core.base; -} - -::slotted(*) { - flex: var(--_tab-flex); -} - -::slotted([selected]) { - z-index: 1; + .container { + @include override(justify, flex-end); + } } diff --git a/src/lib/tabs/tab/_configuration.scss b/src/lib/tabs/tab/_configuration.scss index b87b1e9ba..eed650fc4 100644 --- a/src/lib/tabs/tab/_configuration.scss +++ b/src/lib/tabs/tab/_configuration.scss @@ -1,64 +1,22 @@ -@use '../../core/styles/tokens/tabs/tab/tokens' as tab-tokens; +@use './token-utils' as *; @mixin configuration { - // Shared - --_active-color: #{tab-tokens.get(active-color)}; - --_inactive-color: #{tab-tokens.get(inactive-color)}; - --_height: #{tab-tokens.get(height)}; - --_stacked-height: #{tab-tokens.get(stacked-height)}; - - // Disabled - --_disabled-opacity: #{tab-tokens.get(disabled-opacity)}; - - // Indicator - --_active-indicator-color: #{tab-tokens.get(indicator-color)}; - --_active-indicator-height: #{tab-tokens.get(indicator-height)}; - --_active-indicator-shape: #{tab-tokens.get(indicator-shape)}; - - // Container - --_container-color: #{tab-tokens.get(container-color)}; - --_container-height: #{tab-tokens.get(container-height)}; - --_container-shape: #{tab-tokens.get(container-shape)}; - - // Content - --_content-height: #{tab-tokens.get(content-height)}; - --_content-padding: #{tab-tokens.get(content-padding)}; - - // Icon - --_active-focus-icon-color: #{tab-tokens.get(active-focus-icon-color)}; - --_active-hover-icon-color: #{tab-tokens.get(active-hover-icon-color)}; - --_active-icon-color: #{tab-tokens.get(active-icon-color)}; - --_active-pressed-icon-color: #{tab-tokens.get(active-pressed-icon-color)}; - --_icon-size: #{tab-tokens.get(icon-size)}; - --_focus-icon-color: #{tab-tokens.get(focus-icon-color)}; - --_hover-icon-color: #{tab-tokens.get(hover-icon-color)}; - --_icon-color: #{tab-tokens.get(icon-color)}; - --_pressed-icon-color: #{tab-tokens.get(pressed-icon-color)}; - - // Label - --_active-focus-label-text-color: #{tab-tokens.get(active-focus-label-text-color)}; - --_active-hover-label-text-color: #{tab-tokens.get(active-hover-label-text-color)}; - --_active-label-text-color: #{tab-tokens.get(active-label-text-color)}; - --_active-pressed-label-text-color: #{tab-tokens.get(active-pressed-label-text-color)}; - --_focus-label-text-color: #{tab-tokens.get(focus-label-text-color)}; - --_hover-label-text-color: #{tab-tokens.get(hover-label-text-color)}; - --_label-text-color: #{tab-tokens.get(label-text-color)}; - --_pressed-label-text-color: #{tab-tokens.get(pressed-label-text-color)}; + @include tokens; } @mixin vertical-primary-configuration { - --_active-indicator-shape: #{tab-tokens.get(vertical-indicator-shape)}; + @include override(indicator-shape, vertical-indicator-shape); } @mixin configuration-secondary { - --_active-indicator-height: #{tab-tokens.get(secondary-indicator-height)}; - --_active-indicator-shape: #{tab-tokens.get(secondary-indicator-shape)}; + @include override(indicator-height, secondary-indicator-height); + @include override(indicator-shape, secondary-indicator-shape); } @mixin configuration-inverted { - --_active-indicator-shape: #{tab-tokens.get(inverted-indicator-shape)}; + @include override(indicator-shape, inverted-indicator-shape); } @mixin configuration-vertical-inverted { - --_active-indicator-shape: #{tab-tokens.get(vertical-inverted-indicator-shape)}; + @include override(indicator-shape, vertical-inverted-indicator-shape); } diff --git a/src/lib/tabs/tab/_core.scss b/src/lib/tabs/tab/_core.scss index f6ffc49b1..382f79a2c 100644 --- a/src/lib/tabs/tab/_core.scss +++ b/src/lib/tabs/tab/_core.scss @@ -1,10 +1,5 @@ -@use '../../core/styles/utils'; -@use '../../core/styles/tokens/tabs/tab/tokens'; @use '../../core/styles/typography'; - -@mixin provide-theme($theme) { - @include utils.provide(tokens.$tokens, $theme, tab); -} +@use './token-utils' as *; @mixin host { display: inline-flex; @@ -18,7 +13,7 @@ } @mixin disabled { - opacity: var(--_disabled-opacity); + opacity: #{token(disabled-opacity)}; } @mixin tab { @@ -40,9 +35,9 @@ padding: 0; margin: 0; z-index: 0; // Ensure this is a stacking context so the indicator displays - background-color: var(--_container-color); - color: var(--_label-text-color); - min-height: var(--_container-height); + background-color: #{token(container-color)}; + color: #{token(label-text-color)}; + min-height: #{token(container-height)}; &::-moz-focus-inner { padding: 0; @@ -55,59 +50,59 @@ position: relative; writing-mode: horizontal-tb; fill: currentColor; - color: var(--_icon-color); - font-size: var(--_icon-size); - width: var(--_icon-size); - height: var(--_icon-size); + color: #{token(icon-color)}; + font-size: #{token(icon-size)}; + width: #{token(icon-size)}; + height: #{token(icon-size)}; } &:hover { - color: var(--_hover-label-text-color); + color: #{token(hover-label-text-color)}; cursor: pointer; } &:hover { ::slotted([slot=leading]), ::slotted([slot=trailing]) { - color: var(--_hover-icon-color); + color: #{token(hover-icon-color)}; } } } @mixin focus { - color: var(--_focus-label-text-color); + color: #{token(focus-label-text-color)}; ::slotted([slot=leading]), ::slotted([slot=trailing]) { - color: var(--_focus-icon-color); + color:#{token(focus-icon-color)}; } } @mixin selected-focus { - color: var(--_active-focus-label-text-color); + color: #{token(active-focus-label-text-color)}; ::slotted([slot=leading]), ::slotted([slot=trailing]) { - color: var(--_active-focus-icon-color); + color: #{token(active-focus-icon-color)}; } } @mixin pressed { - color: var(--_pressed-label-text-color); + color: #{token(pressed-label-text-color)}; outline: none; ::slotted([slot=leading]), ::slotted([slot=trailing]) { - color: var(--_pressed-icon-color); + color: #{token(pressed-icon-color)}; } } @mixin selected-pressed { - color: var(--_active-pressed-label-text-color); + color: #{token(active-pressed-label-text-color)}; ::slotted([slot=leading]), ::slotted([slot=trailing]) { - color: var(--_active-pressed-icon-color); + color: #{token(active-pressed-icon-color)}; } } @@ -120,10 +115,10 @@ justify-content: center; white-space: nowrap; transition: 150ms color linear; - max-height: calc(var(--_content-height) + 2 * var(--_content-padding)); - min-height: var(--_content-height); - padding: var(--_content-padding) calc(2 * var(--_content-padding)); - gap: var(--_content-padding); + max-height: calc(#{token(content-height)} + 2 * #{token(content-padding)}); + min-height: #{token(content-height)}; + padding: #{token(content-padding)} calc(2 * #{token(content-padding)}); + gap: #{token(content-padding)}; } @mixin label { @@ -136,9 +131,9 @@ box-sizing: border-box; z-index: -1; transform-origin: bottom left; - background: var(--_active-indicator-color); - border-radius: var(--_active-indicator-shape); - height: var(--_active-indicator-height); + background: #{token(indicator-color)}; + border-radius: #{token(indicator-shape)}; + height: #{token(indicator-height)}; inset: auto 0 0; opacity: 0; } @@ -167,7 +162,7 @@ @mixin vertical-indicator { height: 100%; - min-width: var(--_active-indicator-height); + min-width: #{token(indicator-height)}; inset: 0 0 0 auto; } diff --git a/src/lib/tabs/tab/_token-utils.scss b/src/lib/tabs/tab/_token-utils.scss new file mode 100644 index 000000000..001c9539d --- /dev/null +++ b/src/lib/tabs/tab/_token-utils.scss @@ -0,0 +1,25 @@ +@use '../../core/styles/tokens/tabs/tab/tokens'; +@use '../../core/styles/tokens/token-utils'; + +$_module: tab; +$_tokens: tokens.$tokens; + +@mixin provide-theme($theme) { + @include token-utils.provide-theme($_module, $_tokens, $theme); +} + +@function token($name, $type: token) { + @return token-utils.token($_module, $_tokens, $name, $type); +} + +@function declare($token) { + @return token-utils.declare($_module, $token); +} + +@mixin override($token, $token-or-value, $type: token) { + @include token-utils.override($_module, $_tokens, $token, $token-or-value, $type); +} + +@mixin tokens($includes: null, $excludes: null) { + @include token-utils.tokens($_module, $_tokens, $includes, $excludes); +} diff --git a/src/lib/tabs/tab/index.scss b/src/lib/tabs/tab/index.scss index c78abe8eb..1ca832e7e 100644 --- a/src/lib/tabs/tab/index.scss +++ b/src/lib/tabs/tab/index.scss @@ -1,2 +1,3 @@ @forward './configuration'; @forward './core'; +@forward './token-utils' show provide-theme; diff --git a/src/lib/tabs/tab/tab.html b/src/lib/tabs/tab/tab.html index dde078b7e..d10a6ba5c 100644 --- a/src/lib/tabs/tab/tab.html +++ b/src/lib/tabs/tab/tab.html @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/src/lib/tabs/tab/tab.scss b/src/lib/tabs/tab/tab.scss index 25771efb9..310160b28 100644 --- a/src/lib/tabs/tab/tab.scss +++ b/src/lib/tabs/tab/tab.scss @@ -2,6 +2,11 @@ @use './configuration'; @use '../../focus-indicator'; @use '../../state-layer'; +@use './token-utils' as *; + +// +// Host +// :host { @include core.host; @@ -11,6 +16,51 @@ display: none; } +// +// Base +// + +.forge-tab { + @include configuration.configuration; +} + +.forge-tab { + @include core.tab; + + .content { + @include core.content; + + .label { + @include core.label; + } + } + + .indicator { + @include core.indicator; + } +} + +:host, ::slotted(*) { + white-space: nowrap; +} + +forge-focus-indicator { + @include focus-indicator.provide-theme(( + color: #{token(active-color)}, + shape: 8px + )); +} + +forge-state-layer { + @include state-layer.provide-theme(( + color: #{token(inactive-color)} + )); +} + +// +// States +// + :host(:focus) { .forge-tab { @include core.focus; @@ -23,6 +73,10 @@ } } +// +// Disabled +// + :host([disabled]) { @include core.host-disabled; @@ -33,23 +87,27 @@ } } +// +// Selected +// + :host([selected]) { .forge-tab { - color: var(--_active-label-text-color); + color: #{token(active-label-text-color)}; ::slotted([slot=leading]), ::slotted([slot=trailing]) { - color: var(--_active-icon-color); + color: #{token(active-icon-color)}; } &:hover { - color: var(--_active-hover-label-text-color); + color: #{token(active-hover-label-text-color)}; } &:hover { ::slotted([slot=leading]), ::slotted([slot=trailing]) { - color: var(--_active-hover-icon-color); + color: #{token(active-hover-icon-color)}; } } @@ -57,12 +115,18 @@ opacity: 1; } } + + forge-state-layer { + @include state-layer.provide-theme(( + color: #{token(active-color)} + )); + } } :host([selected]:not([vertical])) { forge-focus-indicator { @include focus-indicator.provide-theme(( - offset-block: 0 calc(var(--_active-indicator-height) + 1px) + offset-block: 0 calc(#{token(indicator-height)} + 1px) )); } } @@ -79,9 +143,13 @@ } } +// +// Stacked +// + :host([stacked]) { .forge-tab { - --_height: var(--_stacked-height); + @include override(height, stacked-height); .content { @include core.content-stacked; @@ -89,6 +157,10 @@ } } +// +// Secondary +// + :host([secondary]) { .forge-tab { @include configuration.configuration-secondary; @@ -103,6 +175,10 @@ } } +// +// Vertical +// + :host([vertical]) { .forge-tab { @include core.vertical; @@ -118,7 +194,7 @@ forge-focus-indicator { @include focus-indicator.provide-theme(( - offset-inline: 0 calc(var(--_active-indicator-height) + 1px) + offset-inline: 0 calc(#{token(indicator-height)} + 1px) )); } } @@ -135,6 +211,10 @@ } } +// +// Inverted +// + :host([inverted]) { .forge-tab { @include configuration.configuration-inverted; @@ -154,40 +234,3 @@ @include core.vertical-inverted-indicator; } } - -/// Base styles -.forge-tab { - @include configuration.configuration; - @include core.tab; - - .content { - @include core.content; - - .label { - @include core.label; - } - } - - .indicator { - @include core.indicator; - } -} - -host, -::slotted(*) { - white-space: nowrap; -} - - -forge-focus-indicator { - @include focus-indicator.provide-theme(( - color: var(--_active-color), - shape: 8px - )); -} - -forge-state-layer { - @include state-layer.provide-theme(( - color: var(--_inactive-color) - )); -} diff --git a/src/lib/toast/_mixins.scss b/src/lib/toast/_mixins.scss index 1ed511dda..e6d8bcbe7 100644 --- a/src/lib/toast/_mixins.scss +++ b/src/lib/toast/_mixins.scss @@ -222,7 +222,7 @@ } @mixin action-button() { - @include theme.css-custom-property(--mdc-theme-primary, --forge-toast-theme-action, variables.$action); + @include theme.css-custom-property(--forge-theme-primary, --forge-toast-theme-action, variables.$action); } @mixin close-button() { diff --git a/src/lib/toast/toast-adapter.ts b/src/lib/toast/toast-adapter.ts index 202939466..ed2fba8ff 100644 --- a/src/lib/toast/toast-adapter.ts +++ b/src/lib/toast/toast-adapter.ts @@ -1,4 +1,5 @@ import { getShadowElement, isString, removeAllChildren, removeClass, removeElement, toggleElementPlaceholder } from '@tylertech/forge-core'; +import { IButtonComponent } from '../button'; import { BaseAdapter, IBaseAdapter } from '../core/base/base-adapter'; import { PopupPlacement } from '../popup'; import { IToastComponent } from './toast'; @@ -24,7 +25,7 @@ export interface IToastAdapter extends IBaseAdapter { export class ToastAdapter extends BaseAdapter implements IToastAdapter { private _containerElement: HTMLElement; private _messageElement: HTMLElement; - private _actionButtonElement: HTMLButtonElement; + private _actionButtonElement: IButtonComponent; private _actionButtonPlaceholder: Comment; private _closeButtonElement: HTMLButtonElement; @@ -32,12 +33,12 @@ export class ToastAdapter extends BaseAdapter implements IToast super(component); this._containerElement = getShadowElement(component, TOAST_CONSTANTS.selectors.CONTAINER); this._messageElement = getShadowElement(component, TOAST_CONSTANTS.selectors.MESSAGE); - this._actionButtonElement = getShadowElement(component, TOAST_CONSTANTS.selectors.ACTION_BUTTON) as HTMLButtonElement; + this._actionButtonElement = getShadowElement(component, TOAST_CONSTANTS.selectors.ACTION_BUTTON) as IButtonComponent; this._closeButtonElement = getShadowElement(component, TOAST_CONSTANTS.selectors.CLOSE_BUTTON) as HTMLButtonElement; } /** - * Sets an attrinbute on the host element. + * Sets an attribute on the host element. * @param name The attribute name. * @param value The attribute value. */ diff --git a/src/lib/toast/toast.html b/src/lib/toast/toast.html index cd4e63d78..2d865d47a 100644 --- a/src/lib/toast/toast.html +++ b/src/lib/toast/toast.html @@ -3,9 +3,7 @@
      - - - + - + Show backdrop

      (When open, click backdrop to close)

      = ({ {hasIcon && }
      Minim sunt eu laborum labore minim.
      - {hasButton && - - - } + {hasButton && Learn more...}
      ); }; diff --git a/src/stories/src/components/banner/code/banner-combined-full.ts b/src/stories/src/components/banner/code/banner-combined-full.ts index 512e34d52..7947fcb18 100644 --- a/src/stories/src/components/banner/code/banner-combined-full.ts +++ b/src/stories/src/components/banner/code/banner-combined-full.ts @@ -2,8 +2,6 @@ export const BannerCombinedFullCodeHtml = () => `
      Minim sunt eu laborum labore minim.
      - - - + Learn more...
      `; diff --git a/src/stories/src/components/bottom-sheet/bottom-sheet.stories.tsx b/src/stories/src/components/bottom-sheet/bottom-sheet.stories.tsx index b8fed78e2..7391037d2 100644 --- a/src/stories/src/components/bottom-sheet/bottom-sheet.stories.tsx +++ b/src/stories/src/components/bottom-sheet/bottom-sheet.stories.tsx @@ -28,9 +28,7 @@ export const Default: Story = ({ const show = () => setIsOpen(true); return ( <> - - - + Show bottom sheet
      @@ -40,9 +38,7 @@ export const Default: Story = ({ {LOREM_IPSUM.p1.slice(0, 162)}
      - - - + Close
      diff --git a/src/stories/src/components/bottom-sheet/code/bottom-sheet-default.ts b/src/stories/src/components/bottom-sheet/code/bottom-sheet-default.ts index e879ece40..174e40e17 100644 --- a/src/stories/src/components/bottom-sheet/code/bottom-sheet-default.ts +++ b/src/stories/src/components/bottom-sheet/code/bottom-sheet-default.ts @@ -9,9 +9,7 @@ export const BottomSheetDefaultHtml = () => ` numquam odio.
      - - - + Close
      `; diff --git a/src/stories/src/components/busy-indicator/busy-indicator.stories.tsx b/src/stories/src/components/busy-indicator/busy-indicator.stories.tsx index ebd8fb535..a91303e56 100644 --- a/src/stories/src/components/busy-indicator/busy-indicator.stories.tsx +++ b/src/stories/src/components/busy-indicator/busy-indicator.stories.tsx @@ -79,9 +79,7 @@ export const Default: Story = ({ }; return ( - - - + Show busy ); }; Default.args = { diff --git a/src/stories/src/components/button/button-args.ts b/src/stories/src/components/button/button-args.ts index c6fb5b9e2..c10526cc1 100644 --- a/src/stories/src/components/button/button-args.ts +++ b/src/stories/src/components/button/button-args.ts @@ -1,26 +1,27 @@ export interface IButtonProps { - type: string; + variant: string; text: string; disabled: boolean; + dense: boolean; hasLeadingIcon: boolean; hasTrailingIcon: boolean; } export interface IButtonMobileProps { - type: string; + variant: string; hasLeadingIcon: boolean; hasTrailingIcon: boolean; } export interface IButtonMenuProps { - type: string; + variant: string; hasLeadingIcon: boolean; hasTrailingIcon: boolean; persistSelection: boolean; } export interface IButtonLoadingOnSubmitProps { - type: string; + variant: string; determinate: boolean; progress: number; } @@ -29,25 +30,17 @@ const buttonType = { control: { type: 'select', labels: { - 'default': 'Flat', + '': 'Default', 'outlined': 'Outlined', 'raised': 'Raised', - 'unelevated': 'Unelevated', - 'dense': 'Flat dense', - 'outlined-dense': 'Outlined dense', - 'raised-dense': 'Raised dense', - 'unelevated-dense': 'Unelevated dense', + 'flat': 'Flat' }, }, options: [ 'default', 'outlined', - 'raised', - 'unelevated', - 'dense', - 'outlined-dense', - 'raised-dense', - 'unelevated-dense', + 'raised', + 'flat' ], description: '', table: { @@ -71,6 +64,13 @@ export const buttonArgTypes = { category: 'Properties', }, }, + dense: { + control: 'boolean', + description: '', + table: { + category: 'Properties', + }, + }, hasLeadingIcon: { control: 'boolean', description: '', diff --git a/src/stories/src/components/button/button.stories.tsx b/src/stories/src/components/button/button.stories.tsx index 33f871f15..46bd2d8e5 100644 --- a/src/stories/src/components/button/button.stories.tsx +++ b/src/stories/src/components/button/button.stories.tsx @@ -17,9 +17,10 @@ export default { } as Meta; export const Default: Story = ({ - type = 'flat', + variant = '', text = 'Button', disabled = false, + dense = false, hasLeadingIcon = false, hasTrailingIcon = false }) => { @@ -28,20 +29,19 @@ export const Default: Story = ({ }, []); return ( - - + + {hasLeadingIcon && } + {text} + {hasTrailingIcon && } ) }; Default.argTypes = buttonArgTypes; Default.args = { - type: 'flat', + variant: '', text: 'Button', disabled: false, + dense: false, hasLeadingIcon: false, hasTrailingIcon: false } as IButtonProps; diff --git a/src/stories/src/components/button/code/button-default.ts b/src/stories/src/components/button/code/button-default.ts index b982f0cf2..fcb72e8dc 100644 --- a/src/stories/src/components/button/code/button-default.ts +++ b/src/stories/src/components/button/code/button-default.ts @@ -1,18 +1,14 @@ export const ButtonDefaultCodeHtml = () => { return ` - - - +Button `; }; export const ButtonWithIconCodeHtml = () => { return ` - - + + Button + `; }; diff --git a/src/stories/src/components/card/card.stories.tsx b/src/stories/src/components/card/card.stories.tsx index 91cf02c1f..54f86f388 100644 --- a/src/stories/src/components/card/card.stories.tsx +++ b/src/stories/src/components/card/card.stories.tsx @@ -75,12 +75,8 @@ export const Styled: Story = ({
      - - - - - - + Ok + Cancel
      @@ -116,12 +112,8 @@ export const WithScaffold: Story = ({

      - - - - - - + Cancel + Ok diff --git a/src/stories/src/components/card/code/card-scaffold.ts b/src/stories/src/components/card/code/card-scaffold.ts index 4eccf747a..2cc8996a3 100644 --- a/src/stories/src/components/card/code/card-scaffold.ts +++ b/src/stories/src/components/card/code/card-scaffold.ts @@ -21,12 +21,8 @@ export const CardScaffoldCodeHtml = () => {

      - - - - - - + Cancel + Ok diff --git a/src/stories/src/components/card/code/card-styled.ts b/src/stories/src/components/card/code/card-styled.ts index cab4cc069..1f5b1b27c 100644 --- a/src/stories/src/components/card/code/card-styled.ts +++ b/src/stories/src/components/card/code/card-styled.ts @@ -19,12 +19,8 @@ export const CardStyledCodeHtml = () => {
      - - - - - - + Ok + Cancel
      diff --git a/src/stories/src/components/dialog/code/dialog-complex.ts b/src/stories/src/components/dialog/code/dialog-complex.ts index c0294ba66..11f12dff2 100644 --- a/src/stories/src/components/dialog/code/dialog-complex.ts +++ b/src/stories/src/components/dialog/code/dialog-complex.ts @@ -14,12 +14,8 @@ export const DialogComplexCodeHtml = () => `

      - - - - - - + Cancel + Discard `; diff --git a/src/stories/src/components/dialog/code/dialog-simple.ts b/src/stories/src/components/dialog/code/dialog-simple.ts index 4e3bc83f5..d2aedf96a 100644 --- a/src/stories/src/components/dialog/code/dialog-simple.ts +++ b/src/stories/src/components/dialog/code/dialog-simple.ts @@ -1,7 +1,5 @@ export const DialogSimpleCodeHtml = () => ` - - - +Show dialog
      @@ -15,9 +13,7 @@ export const DialogSimpleCodeHtml = () => `

      - - - + Close
      `; diff --git a/src/stories/src/components/dialog/dialog.stories.tsx b/src/stories/src/components/dialog/dialog.stories.tsx index 4aee50b83..c6cc58a24 100644 --- a/src/stories/src/components/dialog/dialog.stories.tsx +++ b/src/stories/src/components/dialog/dialog.stories.tsx @@ -34,9 +34,7 @@ export const Simple: Story = ({ return ( <> - - - + Show dialog = ({ {LOREM_IPSUM.p1.slice(0, 162)}

      - - - + Close
      @@ -96,9 +92,7 @@ export const Complex: Story = ({ return ( <> - - - + Show complex dialog = ({

      - - - - - - + Cancel + Discard
      diff --git a/src/stories/src/components/file-picker/code/file-picker-compact.ts b/src/stories/src/components/file-picker/code/file-picker-compact.ts index 0767cba5a..2d7cc1de0 100644 --- a/src/stories/src/components/file-picker/code/file-picker-compact.ts +++ b/src/stories/src/components/file-picker/code/file-picker-compact.ts @@ -1,9 +1,7 @@ export const FilePickerCompactCodeHtml = () => { return ` - - - + Select files `; }; diff --git a/src/stories/src/components/file-picker/code/file-picker-default.ts b/src/stories/src/components/file-picker/code/file-picker-default.ts index 701b7c7c2..e258c9292 100644 --- a/src/stories/src/components/file-picker/code/file-picker-default.ts +++ b/src/stories/src/components/file-picker/code/file-picker-default.ts @@ -3,9 +3,7 @@ export const FilePickerDefaultCodeHtml = () => { Drag files here or Secondary text here - - - + Select files Helper text goes here `; diff --git a/src/stories/src/components/file-picker/file-picker.stories.tsx b/src/stories/src/components/file-picker/file-picker.stories.tsx index 06fca2332..f0d4a1bd1 100644 --- a/src/stories/src/components/file-picker/file-picker.stories.tsx +++ b/src/stories/src/components/file-picker/file-picker.stories.tsx @@ -39,9 +39,7 @@ export const Default: Story = ({ style={styles}> Drag files here or Secondary text here - - - + Select files {hasHelperText && Helper text goes here} ); diff --git a/src/stories/src/components/keyboard-shortcut/code/keyboard-shortcut-basic.ts b/src/stories/src/components/keyboard-shortcut/code/keyboard-shortcut-basic.ts index b23e30d5b..c44f46667 100644 --- a/src/stories/src/components/keyboard-shortcut/code/keyboard-shortcut-basic.ts +++ b/src/stories/src/components/keyboard-shortcut/code/keyboard-shortcut-basic.ts @@ -1,7 +1,5 @@ export const KeyboardShortcutBasicHtml = () => ` - - - +Button `; diff --git a/src/stories/src/components/keyboard-shortcut/keyboard-shortcut.mdx b/src/stories/src/components/keyboard-shortcut/keyboard-shortcut.mdx index b19e90f62..89f6c6119 100644 --- a/src/stories/src/components/keyboard-shortcut/keyboard-shortcut.mdx +++ b/src/stories/src/components/keyboard-shortcut/keyboard-shortcut.mdx @@ -31,9 +31,7 @@ elements with the `target` attribute which accepts a CSS selector. Keyboard shortcut can also target a specific element using the `target` attribute: ```html - - - +Button

      Some other element

      ``` diff --git a/src/stories/src/components/keyboard-shortcut/keyboard-shortcut.stories.tsx b/src/stories/src/components/keyboard-shortcut/keyboard-shortcut.stories.tsx index 0e269a5a4..6a3926024 100644 --- a/src/stories/src/components/keyboard-shortcut/keyboard-shortcut.stories.tsx +++ b/src/stories/src/components/keyboard-shortcut/keyboard-shortcut.stories.tsx @@ -63,9 +63,7 @@ export const Default: Story = ({ - - - + Button = ({ return (
      - - - + Button { return ` - - - + Show menu `; }; diff --git a/src/stories/src/components/menu/menu.stories.tsx b/src/stories/src/components/menu/menu.stories.tsx index bb0288a13..3d1f189ea 100644 --- a/src/stories/src/components/menu/menu.stories.tsx +++ b/src/stories/src/components/menu/menu.stories.tsx @@ -37,9 +37,7 @@ export const Default: Story = ({ persistSelection={persistSelection} options={mode === 'click' ? SIMPLE_OPTIONS : CASCADING_OPTIONS} on-forge-menu-select={onSelect}> - - - + Show menu ); }; diff --git a/src/stories/src/components/page-state/code/page-state-default.ts b/src/stories/src/components/page-state/code/page-state-default.ts index c13201262..b5a3cb7c2 100644 --- a/src/stories/src/components/page-state/code/page-state-default.ts +++ b/src/stories/src/components/page-state/code/page-state-default.ts @@ -3,11 +3,7 @@ export const PageStateDefaultHtml = () => `
      Nothing but tumbleweeds here...

      Even our best explorer couldn't find the page you're looking for. It might have been removed or you may have mistyped the URL.

      - - - - - - + Go back + Refresh `; diff --git a/src/stories/src/components/page-state/page-state.stories.tsx b/src/stories/src/components/page-state/page-state.stories.tsx index a40ee537e..f8ef01bd0 100644 --- a/src/stories/src/components/page-state/page-state.stories.tsx +++ b/src/stories/src/components/page-state/page-state.stories.tsx @@ -31,12 +31,8 @@ export const Default: Story = ({

      } {hasActions && <> - - - - - - + Go back + Refresh } diff --git a/src/stories/src/components/popup/popup.mdx b/src/stories/src/components/popup/popup.mdx index c203c22ae..e81d3d25e 100644 --- a/src/stories/src/components/popup/popup.mdx +++ b/src/stories/src/components/popup/popup.mdx @@ -50,18 +50,14 @@ your own custom popups and it's highly recommended that you use to to make your The following example shows how to use a popup all through Angular's template syntax using ``: ```html - - - +Show popup
      Popup Content
      - - - + Close
      ``` diff --git a/src/stories/src/components/popup/popup.stories.tsx b/src/stories/src/components/popup/popup.stories.tsx index 06b482074..e1676affe 100644 --- a/src/stories/src/components/popup/popup.stories.tsx +++ b/src/stories/src/components/popup/popup.stories.tsx @@ -30,9 +30,7 @@ export const Default: Story = ({ }; return (
      - - - + setIsOpen(!isOpen)}>Open popup = ({filters, label, open, groupId} console.log(evt)}>
      setIsOpen(!isOpen)}>{label}
      - - - + }}>Clear
      { filters.map( (f, i) => ())} diff --git a/src/stories/src/components/split-view/split-view.mdx b/src/stories/src/components/split-view/split-view.mdx index b9375cb55..836c431fb 100644 --- a/src/stories/src/components/split-view/split-view.mdx +++ b/src/stories/src/components/split-view/split-view.mdx @@ -343,9 +343,7 @@ Resizes panels within the split view to avoid overflow. #### HTML ```html - - - +Close aside diff --git a/src/stories/src/components/toast/code/toast-default.ts b/src/stories/src/components/toast/code/toast-default.ts index 9c741109a..98be38097 100644 --- a/src/stories/src/components/toast/code/toast-default.ts +++ b/src/stories/src/components/toast/code/toast-default.ts @@ -1,7 +1,5 @@ export const ToastDefaultHtml = () => ` - - - +Show toast `; export const ToastDefaultTs = () => ` diff --git a/src/stories/src/components/toast/toast.stories.tsx b/src/stories/src/components/toast/toast.stories.tsx index 3e7b8ae54..6a1f57c1d 100644 --- a/src/stories/src/components/toast/toast.stories.tsx +++ b/src/stories/src/components/toast/toast.stories.tsx @@ -35,9 +35,7 @@ export const Default: Story = ({ } return ( <> - - - + setIsOpen(true)}>Show toast setIsOpen(false)} /> ); diff --git a/src/stories/src/components/tooltip/code/tooltip-default.ts b/src/stories/src/components/tooltip/code/tooltip-default.ts index f879f19a0..b58e5c391 100644 --- a/src/stories/src/components/tooltip/code/tooltip-default.ts +++ b/src/stories/src/components/tooltip/code/tooltip-default.ts @@ -1,6 +1,4 @@ export const TooltipDefaultHtml = () => ` - - + <> + Hover me - + ); Default.args = { text: 'Forge components are awesome!', diff --git a/src/stories/src/mock/document-filters.tsx b/src/stories/src/mock/document-filters.tsx index 1c2c6f69d..276cb7b59 100644 --- a/src/stories/src/mock/document-filters.tsx +++ b/src/stories/src/mock/document-filters.tsx @@ -101,9 +101,7 @@ const DocumentPrice: FC = () => { Document price - - - + Clear
      add_alert
      Laboris cillum dolore id incididunt consequat nisi nisi. Consequat cillum officia quis quis.
      - - - + Learn more...
      \ No newline at end of file diff --git a/src/test/spec/banner/banner.spec.ts b/src/test/spec/banner/banner.spec.ts index 98c2e4164..bc9d3271c 100644 --- a/src/test/spec/banner/banner.spec.ts +++ b/src/test/spec/banner/banner.spec.ts @@ -162,11 +162,9 @@ describe('BannerComponent', function(this: ITestContext) { text.textContent = DEFAULT_TEXT_CONTENT; component.appendChild(text); const buttonComponent = document.createElement(BUTTON_CONSTANTS.elementName) as IButtonComponent; - buttonComponent.setAttribute('type', 'outlined'); - const buttonElement = document.createElement('button') as HTMLButtonElement; - buttonElement.type = 'button'; - buttonElement.textContent = 'Learn more...'; - buttonComponent.appendChild(buttonElement); + buttonComponent.setAttribute('variant', 'outlined'); + buttonComponent.type = 'button'; + buttonComponent.textContent = 'Learn more...'; component.appendChild(buttonComponent); fixture.appendChild(component); document.body.appendChild(fixture); diff --git a/src/test/spec/button/button.spec.ts b/src/test/spec/button/button.spec.ts deleted file mode 100644 index 9113d7a74..000000000 --- a/src/test/spec/button/button.spec.ts +++ /dev/null @@ -1,248 +0,0 @@ -import { removeElement } from '@tylertech/forge-core'; -import { tick } from '@tylertech/forge-testing'; -import { ButtonComponent, BUTTON_CONSTANTS, defineButtonComponent, IButtonComponent } from '@tylertech/forge/button'; - -interface ITestContext { - context: ITestButtonContext | ITestPartialButtonContext; -} - -interface ITestButtonContext { - component: IButtonComponent; - buttonElement: HTMLButtonElement; - destroy(): void; -} - -interface ITestPartialButtonContext { - component: IButtonComponent; - destroy(): void; -} - -describe('ButtonComponent', function(this: ITestContext) { - beforeAll(function(this: ITestContext) { - defineButtonComponent(); - }); - - afterEach(function(this: ITestContext) { - this.context.destroy(); - }); - - describe('with default values', function(this: ITestContext) { - it('should be instantiated', function(this: ITestContext) { - this.context = setupTestContext(); - expect(this.context.component instanceof ButtonComponent).toBe(true); - }); - - it('should receive correct default classes', async function(this: ITestContext) { - this.context = setupTestContext(); - const buttonElement = (this.context as ITestButtonContext).buttonElement; - expect(buttonElement.classList.contains(BUTTON_CONSTANTS.classes.BUTTON)).withContext('Expected the default button class').toBeTrue(); - }); - - it('should not receive other type classes', function(this: ITestContext) { - this.context = setupTestContext(); - const buttonElement = (this.context as ITestButtonContext).buttonElement; - expect(buttonElement.classList.contains(BUTTON_CONSTANTS.classes.BUTTON_RAISED)).not.toBe(true); - expect(buttonElement.classList.contains(BUTTON_CONSTANTS.classes.BUTTON_DENSE)).not.toBe(true); - expect(buttonElement.classList.contains(BUTTON_CONSTANTS.classes.BUTTON_OUTLINED)).not.toBe(true); - expect(buttonElement.classList.contains(BUTTON_CONSTANTS.classes.BUTTON_UNELEVATED)).not.toBe(true); - }); - - it('should emit click event from button', function(this: ITestContext) { - this.context = setupTestContext(); - const callback = jasmine.createSpy('callback'); - const buttonElement = (this.context as ITestButtonContext).buttonElement; - buttonElement.addEventListener('click', callback); - buttonElement.click(); - expect(callback).toHaveBeenCalledTimes(1); - }); - - it('should bubble click event through to component', function(this: ITestContext) { - this.context = setupTestContext(); - const callback = jasmine.createSpy('callback'); - this.context.component.addEventListener('click', callback); - const buttonElement = (this.context as ITestButtonContext).buttonElement; - buttonElement.click(); - expect(callback).toHaveBeenCalledTimes(1); - }); - - it('should apply the correct default class to a dynamically added button', async function(this: ITestContext) { - this.context = setupTestContext(); - const oldButton = this.context.component.querySelector('button'); - const newButton = document.createElement('button'); - if (oldButton) { - oldButton.remove(); - } else { - fail(`Button doesn't exist`); - } - this.context.component.append(newButton); - await tick(); - expect(newButton.classList.contains(BUTTON_CONSTANTS.classes.BUTTON)).toBeTrue(); - }); - - it('should apply the correct class and attribute to a dynamically added icon', async function(this: ITestContext) { - this.context = setupTestContext(); - const icon = document.createElement('forge-icon'); - const buttonElement = (this.context as ITestButtonContext).buttonElement; - buttonElement.prepend(icon); - await tick(); - expect(icon.classList.contains(BUTTON_CONSTANTS.classes.ICON)).toBeTrue(); - expect(icon.getAttribute('aria-hidden') === 'true').toBeTrue(); - }); - - it('should not set ripple when initialized', async function(this: ITestContext) { - this.context = setupTestContext(); - await tick(); - - const ripple = this.context.component['_rippleInstance']; - expect(ripple).toBeFalsy(); - }); - - it('should set ripple after user interaction', async function(this: ITestContext) { - this.context = setupTestContext(); - await tick(); - - const button = this.context.component.querySelector('button') as HTMLButtonElement; - button.dispatchEvent(new Event('pointerenter')); - await tick(); - - const ripple = this.context.component['_rippleInstance']; - expect(ripple).toBeTruthy(); - }); - }); - - describe('partial rendering', function(this: ITestContext) { - it('should wait for child elements before initialization', async function(this: ITestContext) { - this.context = setupPartialTestContext(); - const buttonElement = document.createElement('button'); - buttonElement.textContent = 'BUTTON'; - this.context.component.appendChild(buttonElement); - expect(buttonElement.classList.contains(BUTTON_CONSTANTS.classes.BUTTON)).toBe(false); - await tick(); - expect(this.context.component.children.length).toBe(1); - expect(buttonElement.classList.contains(BUTTON_CONSTANTS.classes.BUTTON)).toBe(true); - }); - - it('should not add classes if child is not a button', async function(this: ITestContext) { - this.context = setupPartialTestContext(); - const span = document.createElement('span'); - this.context.component.appendChild(span); - await tick(); - expect(span.classList.contains(BUTTON_CONSTANTS.classes.BUTTON)).toBe(false); - }); - - it('should allow for label element within button', async function(this: ITestContext) { - this.context = setupPartialTestContext(); - const buttonElement = document.createElement('button'); - - const labelElement = document.createElement('span'); - labelElement.textContent = 'BUTTON'; - buttonElement.appendChild(labelElement); - - this.context.component.appendChild(buttonElement); - - await tick(); - expect(labelElement.classList.contains(BUTTON_CONSTANTS.classes.LABEL)).toBe(true); - }); - - it('should allow for icon element within button', async function(this: ITestContext) { - this.context = setupPartialTestContext(); - const buttonElement = document.createElement('button'); - - const iconElement = document.createElement('i'); - iconElement.classList.add('tyler-icons'); - iconElement.textContent = 'face'; - buttonElement.appendChild(iconElement); - - this.context.component.appendChild(buttonElement); - - await tick(); - expect(iconElement.classList.contains(BUTTON_CONSTANTS.classes.ICON)).toBe(true); - expect(iconElement.getAttribute('aria-hidden')).toBe('true'); - }); - }); - - describe('with raised type', function(this: ITestContext) { - describe('set via property', function(this: ITestContext) { - it('should mirror property to attribute', function(this: ITestContext) { - this.context = setupTestContext(); - this.context.component.type = 'raised'; - expect(this.context.component.getAttribute(BUTTON_CONSTANTS.attributes.TYPE)).toBe('raised'); - }); - - it('should set type via property', function(this: ITestContext) { - this.context = setupTestContext(); - this.context.component.type = 'raised'; - const buttonElement = (this.context as ITestButtonContext).buttonElement; - expect(buttonElement.classList.contains(BUTTON_CONSTANTS.classes.BUTTON_RAISED)).toBe(true); - }); - }); - - describe('set via property', function(this: ITestContext) { - it('should set type via attribute', function(this: ITestContext) { - this.context = setupTestContext('raised'); - this.context.component.setAttribute(BUTTON_CONSTANTS.attributes.TYPE, 'raised'); - expect(this.context.component.type).toBe('raised', 'Expected the type property to match the attribute value'); - const buttonElement = (this.context as ITestButtonContext).buttonElement; - expect(buttonElement.classList.contains(BUTTON_CONSTANTS.classes.BUTTON_RAISED)).toBe(true); - }); - }); - }); - - describe('with unelevated type', function(this: ITestContext) { - it('should set correct classes', function(this: ITestContext) { - this.context = setupTestContext(); - this.context.component.setAttribute(BUTTON_CONSTANTS.attributes.TYPE, 'unelevated'); - const buttonElement = (this.context as ITestButtonContext).buttonElement; - expect(buttonElement.classList.contains(BUTTON_CONSTANTS.classes.BUTTON_UNELEVATED)).toBe(true); - }); - }); - - describe('with outlined type', function(this: ITestContext) { - it('should set correct classes', function(this: ITestContext) { - this.context = setupTestContext(); - this.context.component.setAttribute(BUTTON_CONSTANTS.attributes.TYPE, 'outlined'); - const buttonElement = (this.context as ITestButtonContext).buttonElement; - expect(buttonElement.classList.contains(BUTTON_CONSTANTS.classes.BUTTON_OUTLINED)).toBe(true); - }); - }); - - describe('with dense type', function(this: ITestContext) { - it('should set correct classes', function(this: ITestContext) { - this.context = setupTestContext(); - const buttonElement = (this.context as ITestButtonContext).buttonElement; - this.context.component.setAttribute(BUTTON_CONSTANTS.attributes.TYPE, 'dense'); - expect(buttonElement.classList.contains(BUTTON_CONSTANTS.classes.BUTTON_DENSE)).toBe(true); - }); - }); - - function setupTestContext(typeAttribute?: string): ITestButtonContext { - const fixture = document.createElement('div'); - fixture.id = 'button-test-fixture'; - const component = document.createElement(BUTTON_CONSTANTS.elementName) as IButtonComponent; - if (typeAttribute) { - component.setAttribute(BUTTON_CONSTANTS.attributes.TYPE, typeAttribute); - } - const buttonElement = document.createElement('button'); - buttonElement.textContent = 'BUTTON'; - component.appendChild(buttonElement); - fixture.appendChild(component); - document.body.appendChild(fixture); - return { - component, - buttonElement, - destroy: () => removeElement(fixture) - }; - } - - function setupPartialTestContext(): ITestPartialButtonContext { - const fixture = document.createElement('div'); - fixture.id = 'button-test-fixture'; - const component = document.createElement(BUTTON_CONSTANTS.elementName) as IButtonComponent; - fixture.appendChild(component); - document.body.appendChild(fixture); - return { - component, - destroy: () => removeElement(fixture) - }; - } -}); diff --git a/src/test/spec/date-picker/date-picker.spec.ts b/src/test/spec/date-picker/date-picker.spec.ts index b27a01cba..f92eb9fa7 100644 --- a/src/test/spec/date-picker/date-picker.spec.ts +++ b/src/test/spec/date-picker/date-picker.spec.ts @@ -14,6 +14,7 @@ import { timer, tick, dispatchNativeEvent } from '@tylertech/forge-testing'; import { tryCleanupPopups } from '../../utils'; import { FIELD_CONSTANTS } from '@tylertech/forge/field/field-constants'; import { BASE_DATE_PICKER_CONSTANTS } from '@tylertech/forge/date-picker/base/base-date-picker-constants'; +import type { IButtonComponent } from '@tylertech/forge/button'; interface ITestContext { @@ -1553,28 +1554,26 @@ describe('DatePickerComponent', function(this: ITestContext) { activeCell.click(); } - function getTodayButton(component: IDatePickerComponent): HTMLButtonElement { + function getTodayButton(component: IDatePickerComponent): IButtonComponent { const popup = getPopup(component); const calendar = popup.querySelector('forge-calendar') as ICalendarComponent; - return getShadowElement(calendar, '#today-button')?.firstElementChild as HTMLButtonElement ?? null; + return getShadowElement(calendar, '#today-button') as IButtonComponent ?? null; } - function getClearButton(component: IDatePickerComponent): HTMLButtonElement { + function getClearButton(component: IDatePickerComponent): IButtonComponent { const popup = getPopup(component); const calendar = popup.querySelector('forge-calendar') as ICalendarComponent; - return getShadowElement(calendar, '#clear-button')?.firstElementChild as HTMLButtonElement ?? null; + return getShadowElement(calendar, '#clear-button') as IButtonComponent ?? null; } function clickTodayButton(component: IDatePickerComponent): void { const todayButton = getTodayButton(component); todayButton.click(); - todayButton.dispatchEvent(new MouseEvent('click')); } function clickClearButton(component: IDatePickerComponent): void { const clearButton = getClearButton(component); clearButton.click(); - clearButton.dispatchEvent(new MouseEvent('click')); } function getAnnouncerElement(component: IDatePickerComponent): HTMLElement { diff --git a/src/test/spec/date-range-picker/date-range-picker.spec.ts b/src/test/spec/date-range-picker/date-range-picker.spec.ts index a289ec240..8bd48e0ed 100644 --- a/src/test/spec/date-range-picker/date-range-picker.spec.ts +++ b/src/test/spec/date-range-picker/date-range-picker.spec.ts @@ -9,6 +9,7 @@ import { defineDateRangePickerComponent, defineTextFieldComponent, formatDate, + IButtonComponent, ICalendarComponent, ICON_BUTTON_CONSTANTS, IDateRange, @@ -1627,28 +1628,26 @@ describe('DateRangePickerComponent', function(this: ITestContext) { return popup.querySelector('[data-forge-live-announcer]') as HTMLElement; } - function getTodayButton(component: IDateRangePickerComponent): HTMLButtonElement { + function getTodayButton(component: IDateRangePickerComponent): IButtonComponent { const popup = getPopup(component); const calendar = popup.querySelector('forge-calendar') as ICalendarComponent; - return getShadowElement(calendar, '#today-button')?.firstElementChild as HTMLButtonElement ?? null; + return getShadowElement(calendar, '#today-button') as IButtonComponent ?? null; } - function getClearButton(component: IDateRangePickerComponent): HTMLButtonElement { + function getClearButton(component: IDateRangePickerComponent): IButtonComponent { const popup = getPopup(component); const calendar = popup.querySelector('forge-calendar') as ICalendarComponent; - return getShadowElement(calendar, '#clear-button')?.firstElementChild as HTMLButtonElement ?? null; + return getShadowElement(calendar, '#clear-button') as IButtonComponent ?? null; } function clickTodayButton(component: IDateRangePickerComponent): void { const todayButton = getTodayButton(component); todayButton.click(); - todayButton.dispatchEvent(new MouseEvent('click')); } function clickClearButton(component: IDateRangePickerComponent): void { const clearButton = getClearButton(component); clearButton.click(); - clearButton.dispatchEvent(new MouseEvent('click')); } async function popupCloseAnimation(): Promise {