From 66122fbfcadfd2349abb2e0e66bbfe5e455a033b Mon Sep 17 00:00:00 2001 From: nickonometry Date: Wed, 31 Jan 2024 11:34:29 -0500 Subject: [PATCH 01/19] chore(stack): refactored the stack component to use tokens --- src/dev/pages/stack/stack.ejs | 127 +++++++++++++----- src/dev/pages/stack/stack.scss | 26 +++- src/lib/core/styles/tokens/stack/_tokens.scss | 13 ++ src/lib/stack/_core.scss | 51 +++++++ src/lib/stack/_mixins.scss | 44 ------ src/lib/stack/_token-utils.scss | 25 ++++ src/lib/stack/_variables.scss | 1 - src/lib/stack/stack.scss | 60 ++++++--- 8 files changed, 256 insertions(+), 91 deletions(-) create mode 100644 src/lib/core/styles/tokens/stack/_tokens.scss create mode 100644 src/lib/stack/_core.scss delete mode 100644 src/lib/stack/_mixins.scss create mode 100644 src/lib/stack/_token-utils.scss delete mode 100644 src/lib/stack/_variables.scss diff --git a/src/dev/pages/stack/stack.ejs b/src/dev/pages/stack/stack.ejs index e52bb6073..24b55d2b3 100644 --- a/src/dev/pages/stack/stack.ejs +++ b/src/dev/pages/stack/stack.ejs @@ -1,28 +1,69 @@

Resize browser window to see wrapping (set inline and wrap to true)

- -
-
-
-
+ +
+
+
+
-

More examples

- +

More examples

+ + +
+ + +

Back arrow and title text in a secondary toolbar

+
+ + + + + +

Go back

+
+
+
+
+ + +
+ + +

Icon buttons in a toolbar

+
+ + + + + + + + + + + + + +
+
+ - - -

Forge toolbar with perfectly spaced end slot buttons

-
- - - Cancel - Save - - -
+
+ + +

Forge toolbar with perfectly spaced end slot buttons

+
+ + + Cancel + Save + + +
+
@@ -43,6 +84,14 @@ + + + + + + + +
@@ -53,22 +102,40 @@

Complex form

- - - - - - - - - - - + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + +
diff --git a/src/dev/pages/stack/stack.scss b/src/dev/pages/stack/stack.scss index 0da3cf6f7..447dae732 100644 --- a/src/dev/pages/stack/stack.scss +++ b/src/dev/pages/stack/stack.scss @@ -3,12 +3,36 @@ } .box { - padding: 36px; border: 4px dashed var(--mdc-theme-text-secondary-on-background); background-color: var(--mdc-theme-text-disabled-on-background); border-radius: 8px; } +.small { + height: 40px; + width: 40px; +} + +.medium { + height: 80px; + width: 80px; +} + +.large { + height: 120px; + width: 120px; +} + +.xlarge { + height: 160px; + width: 160px; +} + .more-examples { margin-block-start: 64px; +} + +p { + margin: 0; + padding: 0; } \ No newline at end of file diff --git a/src/lib/core/styles/tokens/stack/_tokens.scss b/src/lib/core/styles/tokens/stack/_tokens.scss new file mode 100644 index 000000000..3d954d80b --- /dev/null +++ b/src/lib/core/styles/tokens/stack/_tokens.scss @@ -0,0 +1,13 @@ +@use 'sass:map'; +@use '../../utils'; + +$tokens: ( + gap: utils.module-val(stack, gap, 16px), + height: utils.module-val(stack, height, initial), + stretch: utils.module-val(stack, stretch, initial), + wrap: utils.module-val(stack, wrap, wrap), +) !default; + +@function get($key) { + @return map.get($tokens, $key); +} diff --git a/src/lib/stack/_core.scss b/src/lib/stack/_core.scss new file mode 100644 index 000000000..a110569f4 --- /dev/null +++ b/src/lib/stack/_core.scss @@ -0,0 +1,51 @@ +@use './token-utils' as *; +@forward './token-utils'; + +@mixin host() { + display: grid; + height: #{token(height)}; +} + +@mixin base() { + display: flex; + flex-direction: column; + gap: #{token(gap)}; + height: #{token(height)}; + width: 100%; + + ::slotted(*) { + flex: #{token(stretch)}; + } +} + +@mixin inline() { + flex-direction: row; +} + +@mixin wrap() { + flex-wrap: wrap; +} + +@mixin justify-start() { + justify-content: start; +} + +@mixin justify-center() { + justify-content: center; +} + +@mixin justify-end() { + justify-content: end; +} + +@mixin align-start() { + align-items: start; +} + +@mixin align-center() { + align-items: center; +} + +@mixin align-end() { + align-items: end; +} \ No newline at end of file diff --git a/src/lib/stack/_mixins.scss b/src/lib/stack/_mixins.scss deleted file mode 100644 index 8c4e51855..000000000 --- a/src/lib/stack/_mixins.scss +++ /dev/null @@ -1,44 +0,0 @@ -@use '../theme'; -@use './variables'; - -@mixin core-styles() { - .forge-stack { - @include base(); - @include stretch(); - @include theme.css-custom-property(gap, --forge-stack-gap, variables.$gap); - @include theme.css-custom-property(height, --forge-stack-height, 100%); - } -} - -@mixin base() { - display: flex; - flex-direction: column; - width: 100%; -} - -@mixin inline() { - flex-direction: row; - --forge-stack-height: auto; -} - -@mixin wrap() { - flex-wrap: wrap; -} - -@mixin stretch() { - ::slotted(*) { - flex: var(--forge-stack-stretch, initial); - } -} - -@mixin justify-start() { - justify-content: start; -} - -@mixin justify-center() { - justify-content: center; -} - -@mixin justify-end() { - justify-content: end; -} \ No newline at end of file diff --git a/src/lib/stack/_token-utils.scss b/src/lib/stack/_token-utils.scss new file mode 100644 index 000000000..ded2bd077 --- /dev/null +++ b/src/lib/stack/_token-utils.scss @@ -0,0 +1,25 @@ +@use '../core/styles/tokens/stack/tokens'; +@use '../core/styles/tokens/token-utils'; + +$_module: stack; +$_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/stack/_variables.scss b/src/lib/stack/_variables.scss deleted file mode 100644 index 39b4b658b..000000000 --- a/src/lib/stack/_variables.scss +++ /dev/null @@ -1 +0,0 @@ -$gap: 16px !default; \ No newline at end of file diff --git a/src/lib/stack/stack.scss b/src/lib/stack/stack.scss index 88f4bb9c9..9ab4f66e5 100644 --- a/src/lib/stack/stack.scss +++ b/src/lib/stack/stack.scss @@ -1,44 +1,74 @@ -@use './mixins'; -@include mixins.core-styles; +@use './core' as *; :host { - display: block; + @include host; + @include tokens; +} + +.forge-stack { + @include base; + @include tokens; } :host([inline]) { .forge-stack { - @include mixins.inline; + @include inline; + @include override(height, fit-content, value); } } :host([inline][wrap]) { .forge-stack { - @include mixins.wrap; + @include wrap; + } +} + +:host(:not([inline])) { + @include override(height, 100%, value); + + .forge-stack { + @include override(height, 100%, value); + } +} + +:host([stretch]) { + ::slotted(*) { + @include override(stretch, 1, value); + } +} + +:host([alignment=start]:not([inline])) { + .forge-stack { + @include justify-start; } } -:host([stretch]:not([inline])) { - height: 100%; +:host([alignment=center]:not([inline])) { + .forge-stack { + @include justify-center; + } } -:host([stretch]) { - --forge-stack-stretch: 1; +:host([alignment=end]:not([inline])) { + .forge-stack { + @include justify-end; + } } -:host([alignment=start]) { +:host([inline][alignment=start]) { .forge-stack { - @include mixins.justify-start; + @include align-start; } } -:host([alignment=center]) { +:host([inline][alignment=center]) { .forge-stack { - @include mixins.justify-center; + @include align-center; } } -:host([alignment=end]) { +:host([inline][alignment=end]) { .forge-stack { - @include mixins.justify-end; + @include align-end; } } From d9cfb17e1b37e8459033c254881777e213f852bc Mon Sep 17 00:00:00 2001 From: nickonometry Date: Fri, 16 Feb 2024 09:54:01 -0500 Subject: [PATCH 02/19] chore(stack): added another variation/example to the EJS page --- src/dev/pages/stack/stack.ejs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/dev/pages/stack/stack.ejs b/src/dev/pages/stack/stack.ejs index 24b55d2b3..53a0dad52 100644 --- a/src/dev/pages/stack/stack.ejs +++ b/src/dev/pages/stack/stack.ejs @@ -15,7 +15,7 @@
-

Back arrow and title text in a secondary toolbar

+

Back arrow and title text in a secondary toolbar (Heading has padding:0, margin:0)

@@ -28,6 +28,23 @@
+ +
+ + +

Back arrow and title text in a secondary toolbar (Heading has padding:0, margin:0)

+
+ + + + + +

This has default header margin and padding

+
+
+
+
+
From cd49ce4ffee3fc832e975525b504b5bdad10e3a1 Mon Sep 17 00:00:00 2001 From: nickonometry Date: Fri, 16 Feb 2024 13:41:14 -0500 Subject: [PATCH 03/19] feat(stack): added a justify property to the stack component and created more examples --- src/dev/pages/stack/stack.ejs | 86 +++++++++++++------ src/dev/pages/stack/stack.html | 11 +++ src/dev/pages/stack/stack.ts | 5 ++ src/lib/core/styles/tokens/stack/_tokens.scss | 2 +- src/lib/stack/_core.scss | 2 - src/lib/stack/stack-constants.ts | 3 +- src/lib/stack/stack-foundation.ts | 13 +++ src/lib/stack/stack.scss | 28 +++--- src/lib/stack/stack.ts | 13 ++- 9 files changed, 117 insertions(+), 46 deletions(-) diff --git a/src/dev/pages/stack/stack.ejs b/src/dev/pages/stack/stack.ejs index 53a0dad52..bf1adc8a8 100644 --- a/src/dev/pages/stack/stack.ejs +++ b/src/dev/pages/stack/stack.ejs @@ -11,6 +11,42 @@

More examples

+ +
+ + +

Info icon + label

+
+
+ + Featured + + + + Show me some useful information here! + +
+
+
+ + +
+ + +

Breadcrumbs

+
+
+ + Home + + Components + + Stack component + +
+
+
+
@@ -18,7 +54,7 @@

Back arrow and title text in a secondary toolbar (Heading has padding:0, margin:0)

- + @@ -28,22 +64,22 @@
- -
- - -

Back arrow and title text in a secondary toolbar (Heading has padding:0, margin:0)

-
- - - - - -

This has default header margin and padding

-
-
-
-
+ +
+ + +

Back arrow and title text in a secondary toolbar (heading has default margin/padding)

+
+ + + + + +

This has default header margin and padding

+
+
+
+
@@ -71,7 +107,7 @@
-

Forge toolbar with perfectly spaced end slot buttons

+

Forge toolbar with end slot buttons

@@ -136,23 +172,25 @@ - - - + + + - + - + + + - +
diff --git a/src/dev/pages/stack/stack.html b/src/dev/pages/stack/stack.html index b76258f25..602271f64 100644 --- a/src/dev/pages/stack/stack.html +++ b/src/dev/pages/stack/stack.html @@ -18,6 +18,17 @@ { label: 'Center', value: 'center' }, { label: 'End', value: 'end' }, ] + }, + { + type: 'select', + label: 'Justify', + id: 'justify-select', + defaultValue: 'start', + options: [ + { label: 'Start', value: 'start' }, + { label: 'Center', value: 'center' }, + { label: 'End', value: 'end' }, + ] } ] } diff --git a/src/dev/pages/stack/stack.ts b/src/dev/pages/stack/stack.ts index 1a1f53dff..dc4aa6596 100644 --- a/src/dev/pages/stack/stack.ts +++ b/src/dev/pages/stack/stack.ts @@ -34,6 +34,11 @@ alignSelect.addEventListener('change', () => { stackContainer.alignment = alignSelect.value; }); +const justifySelect = document.getElementById('justify-select') as ISelectComponent; +justifySelect.addEventListener('change', () => { + stackContainer.justify = justifySelect.value; +}); + gapInput.addEventListener('input', () => { stackContainer.gap = gapInput.value; }); diff --git a/src/lib/core/styles/tokens/stack/_tokens.scss b/src/lib/core/styles/tokens/stack/_tokens.scss index 3d954d80b..33eb8f25e 100644 --- a/src/lib/core/styles/tokens/stack/_tokens.scss +++ b/src/lib/core/styles/tokens/stack/_tokens.scss @@ -3,7 +3,7 @@ $tokens: ( gap: utils.module-val(stack, gap, 16px), - height: utils.module-val(stack, height, initial), + height: utils.module-val(stack, height, null), stretch: utils.module-val(stack, stretch, initial), wrap: utils.module-val(stack, wrap, wrap), ) !default; diff --git a/src/lib/stack/_core.scss b/src/lib/stack/_core.scss index a110569f4..3b69753ee 100644 --- a/src/lib/stack/_core.scss +++ b/src/lib/stack/_core.scss @@ -2,8 +2,6 @@ @forward './token-utils'; @mixin host() { - display: grid; - height: #{token(height)}; } @mixin base() { diff --git a/src/lib/stack/stack-constants.ts b/src/lib/stack/stack-constants.ts index 62853c1ae..aee49e8a1 100644 --- a/src/lib/stack/stack-constants.ts +++ b/src/lib/stack/stack-constants.ts @@ -15,7 +15,8 @@ const attributes = { WRAP: 'wrap', STRETCH: 'stretch', GAP: 'gap', - ALIGNMENT: 'alignment' + ALIGNMENT: 'alignment', + JUSTIFY: 'justify' }; const strings = { diff --git a/src/lib/stack/stack-foundation.ts b/src/lib/stack/stack-foundation.ts index 03a23e744..6d74c6a30 100644 --- a/src/lib/stack/stack-foundation.ts +++ b/src/lib/stack/stack-foundation.ts @@ -8,6 +8,7 @@ export interface IStackFoundation extends ICustomElementFoundation { stretch: boolean; gap: string; alignment: StackAlignment; + justify: StackAlignment; } export class StackFoundation implements IStackFoundation { @@ -16,6 +17,7 @@ export class StackFoundation implements IStackFoundation { private _stretch = false; private _gap = STACK_CONSTANTS.strings.DEFAULT_GAP; private _alignment: StackAlignment = 'start'; + private _justify: StackAlignment = 'start'; constructor(private _adapter: IStackAdapter) {} @@ -77,4 +79,15 @@ export class StackFoundation implements IStackFoundation { this._adapter.setHostAttribute(STACK_CONSTANTS.attributes.ALIGNMENT, this._alignment); } } + + /** Controls the justify-content of children */ + public get justify(): StackAlignment { + return this._justify; + } + public set justify(value: StackAlignment) { + if (this._justify !== value) { + this._justify = value; + this._adapter.setHostAttribute(STACK_CONSTANTS.attributes.JUSTIFY, this._justify); + } + } } diff --git a/src/lib/stack/stack.scss b/src/lib/stack/stack.scss index 9ab4f66e5..e342d9118 100644 --- a/src/lib/stack/stack.scss +++ b/src/lib/stack/stack.scss @@ -2,7 +2,6 @@ :host { @include host; - @include tokens; } .forge-stack { @@ -13,7 +12,6 @@ :host([inline]) { .forge-stack { @include inline; - @include override(height, fit-content, value); } } @@ -24,8 +22,6 @@ } :host(:not([inline])) { - @include override(height, 100%, value); - .forge-stack { @include override(height, 100%, value); } @@ -37,38 +33,38 @@ } } -:host([alignment=start]:not([inline])) { +:host([alignment="start"]) { .forge-stack { - @include justify-start; + @include align-start; } } -:host([alignment=center]:not([inline])) { +:host([alignment="center"]) { .forge-stack { - @include justify-center; + @include align-center; } } -:host([alignment=end]:not([inline])) { +:host([alignment="end"]) { .forge-stack { - @include justify-end; + @include align-end; } } -:host([inline][alignment=start]) { +:host([justify="start"]) { .forge-stack { - @include align-start; + @include justify-start; } } -:host([inline][alignment=center]) { +:host([justify="center"]) { .forge-stack { - @include align-center; + @include justify-center; } } -:host([inline][alignment=end]) { +:host([justify="end"]) { .forge-stack { - @include align-end; + @include justify-end; } } diff --git a/src/lib/stack/stack.ts b/src/lib/stack/stack.ts index 03b78435a..a75adbeb5 100644 --- a/src/lib/stack/stack.ts +++ b/src/lib/stack/stack.ts @@ -13,6 +13,7 @@ export interface IStackComponent extends ICustomElement { stretch: boolean; gap: string; alignment: StackAlignMode | StackAlignment; + justify: StackAlignment; } declare global { @@ -36,7 +37,8 @@ export class StackComponent extends BaseComponent implements IStackComponent { STACK_CONSTANTS.attributes.WRAP, STACK_CONSTANTS.attributes.STRETCH, STACK_CONSTANTS.attributes.GAP, - STACK_CONSTANTS.attributes.ALIGNMENT + STACK_CONSTANTS.attributes.ALIGNMENT, + STACK_CONSTANTS.attributes.JUSTIFY ]; } @@ -65,6 +67,9 @@ export class StackComponent extends BaseComponent implements IStackComponent { case STACK_CONSTANTS.attributes.ALIGNMENT: this.alignment = newValue as StackAlignment; break; + case STACK_CONSTANTS.attributes.JUSTIFY: + this.justify = newValue as StackAlignment; + break; } } @@ -84,7 +89,11 @@ export class StackComponent extends BaseComponent implements IStackComponent { @FoundationProperty() public declare gap: string; - /** Controls if stack items are at the end of the row or column */ + /** Controls the align-items property of a row or column */ @FoundationProperty() public declare alignment: StackAlignMode | StackAlignment; + + /** Controls the justify-content property of a row or column */ + @FoundationProperty() + public declare justify: StackAlignment; } From 07be52571518b4a411fae0c5f953bda6015d57df Mon Sep 17 00:00:00 2001 From: nickonometry Date: Fri, 16 Feb 2024 13:42:52 -0500 Subject: [PATCH 04/19] chore: removed unused host selector from stack SCSS --- src/lib/stack/_core.scss | 3 --- src/lib/stack/stack.scss | 4 ---- 2 files changed, 7 deletions(-) diff --git a/src/lib/stack/_core.scss b/src/lib/stack/_core.scss index 3b69753ee..3137a440f 100644 --- a/src/lib/stack/_core.scss +++ b/src/lib/stack/_core.scss @@ -1,9 +1,6 @@ @use './token-utils' as *; @forward './token-utils'; -@mixin host() { -} - @mixin base() { display: flex; flex-direction: column; diff --git a/src/lib/stack/stack.scss b/src/lib/stack/stack.scss index e342d9118..b9e9519b4 100644 --- a/src/lib/stack/stack.scss +++ b/src/lib/stack/stack.scss @@ -1,9 +1,5 @@ @use './core' as *; -:host { - @include host; -} - .forge-stack { @include base; @include tokens; From f496f14f5e22c210732a153e163725566f23c611 Mon Sep 17 00:00:00 2001 From: nickonometry Date: Wed, 21 Feb 2024 14:00:37 -0500 Subject: [PATCH 05/19] chore: visual bug fixes to the close icon button --- src/lib/stack/stack.scss | 15 ++++++++------- src/lib/stack/stack.ts | 2 -- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/lib/stack/stack.scss b/src/lib/stack/stack.scss index b9e9519b4..654cd33de 100644 --- a/src/lib/stack/stack.scss +++ b/src/lib/stack/stack.scss @@ -23,43 +23,44 @@ } } -:host([stretch]) { +:host([stretch]) { ::slotted(*) { @include override(stretch, 1, value); } } -:host([alignment="start"]) { +:host([alignment=start]) { .forge-stack { + //override stretch css prop @include align-start; } } -:host([alignment="center"]) { +:host([alignment=center]) { .forge-stack { @include align-center; } } -:host([alignment="end"]) { +:host([alignment=end]) { .forge-stack { @include align-end; } } -:host([justify="start"]) { +:host([justify=start]) { .forge-stack { @include justify-start; } } -:host([justify="center"]) { +:host([justify=center]) { .forge-stack { @include justify-center; } } -:host([justify="end"]) { +:host([justify=end]) { .forge-stack { @include justify-end; } diff --git a/src/lib/stack/stack.ts b/src/lib/stack/stack.ts index a75adbeb5..a3486edc4 100644 --- a/src/lib/stack/stack.ts +++ b/src/lib/stack/stack.ts @@ -23,8 +23,6 @@ declare global { } /** - * The custom element class behind the `` component. - * * @tag forge-stack */ @CustomElement({ From d08d50949049d0518858d9a1170312d6038e6881 Mon Sep 17 00:00:00 2001 From: derekmoss Date: Wed, 28 Feb 2024 11:11:17 -0800 Subject: [PATCH 06/19] feat(button-area): add tokens, state-layer, focus-indicator --- src/lib/button-area/_core.scss | 25 ++++ src/lib/button-area/_mixins.scss | 42 ------ src/lib/button-area/_token-utils.scss | 25 ++++ src/lib/button-area/button-area-adapter.ts | 134 ++++++++---------- src/lib/button-area/button-area-constants.ts | 7 +- src/lib/button-area/button-area-foundation.ts | 85 ++++++++++- src/lib/button-area/button-area.html | 4 +- src/lib/button-area/button-area.scss | 57 +++++++- src/lib/button-area/button-area.test.ts | 125 ++++++++++++++++ src/lib/button-area/button-area.ts | 27 +++- src/lib/button-area/index.scss | 1 + .../styles/tokens/button-area/_tokens.scss | 25 ++++ src/test/spec/button-area/button-area.spec.ts | 86 ----------- 13 files changed, 425 insertions(+), 218 deletions(-) create mode 100644 src/lib/button-area/_core.scss delete mode 100644 src/lib/button-area/_mixins.scss create mode 100644 src/lib/button-area/_token-utils.scss create mode 100644 src/lib/button-area/button-area.test.ts create mode 100644 src/lib/button-area/index.scss create mode 100644 src/lib/core/styles/tokens/button-area/_tokens.scss delete mode 100644 src/test/spec/button-area/button-area.spec.ts diff --git a/src/lib/button-area/_core.scss b/src/lib/button-area/_core.scss new file mode 100644 index 000000000..8e60594ac --- /dev/null +++ b/src/lib/button-area/_core.scss @@ -0,0 +1,25 @@ +@use './token-utils' as *; + +@forward './token-utils'; + +@mixin host { + display: block; + position: relative; +} + +@mixin base { + position: relative; + + outline: none; + user-select: none; + -webkit-appearance: none; + + overflow: hidden; +} + +@mixin enabled { + cursor: #{token(cursor)}; +} +@mixin disabled { + cursor: #{token(disabled-cursor)}; +} diff --git a/src/lib/button-area/_mixins.scss b/src/lib/button-area/_mixins.scss deleted file mode 100644 index 4d628e828..000000000 --- a/src/lib/button-area/_mixins.scss +++ /dev/null @@ -1,42 +0,0 @@ -@use '@material/ripple/ripple' as mdc-ripple; -@use '@material/ripple/ripple-theme' as mdc-ripple-theme; -@use '../utils/mixins' as utils; -@use '../ripple/forge-ripple'; - -@mixin core-styles() { - .forge-button-area { - @include base; - - &:not(.forge-button-area--disabled) { - &.forge-button-area { - @include enabled; - } - } - - &__button { - @include button; - } - } -} - -@mixin host() { - display: block; -} - -@mixin base() { - overflow: hidden; -} - -@mixin enabled() { - @include mdc-ripple.surface; - @include mdc-ripple.radius-bounded; - @include mdc-ripple-theme.states(primary); - @include mdc-ripple-theme.states-activated(primary); - @include mdc-ripple-theme.states-selected(primary); - - cursor: pointer; -} - -@mixin button() { - @include utils.visually-hidden; -} diff --git a/src/lib/button-area/_token-utils.scss b/src/lib/button-area/_token-utils.scss new file mode 100644 index 000000000..b02e4571f --- /dev/null +++ b/src/lib/button-area/_token-utils.scss @@ -0,0 +1,25 @@ +@use '../core/styles/tokens/button-area/tokens'; +@use '../core/styles/tokens/token-utils'; + +$_module: button-area; +$_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-area/button-area-adapter.ts b/src/lib/button-area/button-area-adapter.ts index eb587a3cd..dbce21ba6 100644 --- a/src/lib/button-area/button-area-adapter.ts +++ b/src/lib/button-area/button-area-adapter.ts @@ -1,43 +1,68 @@ -import { addClass, getShadowElement, removeClass, toggleClass } from '@tylertech/forge-core'; +import { getShadowElement, toggleClass } from '@tylertech/forge-core'; import { BaseAdapter, IBaseAdapter, createUserInteractionListener } from '../core'; -import { ForgeRipple, ForgeRippleAdapter, ForgeRippleCapableSurface, ForgeRippleFoundation } from '../ripple'; +import { FOCUS_INDICATOR_CONSTANTS, IFocusIndicatorComponent } from '../focus-indicator'; +import { IStateLayerComponent, STATE_LAYER_CONSTANTS } from '../state-layer'; import { IButtonAreaComponent } from './button-area'; import { BUTTON_AREA_CONSTANTS } from './button-area-constants'; export interface IButtonAreaAdapter extends IBaseAdapter { destroy(): void; + deferInitialization(listener: (evt?: PointerEvent) => void): void; setDisabled(value: boolean): void; - addListener(type: string, listener: (event: Event) => void): void; - removeListener(type: string, listener: (event: Event) => void): void; - addSlotChangeListener(listener: () => void): void; - removeSlotChangeListener(listener: () => void): void; + addListener(type: string, listener: (event: Event) => void, capture?: boolean): void; + removeListener(type: string, listener: (event: Event) => void, capture?: boolean): void; + addButtonSlotListener(type: string, listener: (event: Event) => void): void; + removeButtonSlotListener(type: string, listener: (event: Event) => void): void; + addContentListener(type: string, listener: (event: Event) => void): void; + removeContentListener(type: string, listener: (event: Event) => void): void; + animateStateLayer(): void; startButtonObserver(callback: MutationCallback): void; stopButtonObserver(): void; detectSlottedButton(): void; buttonIsDisabled(): boolean; requestDisabledButtonFrame(): void; - createRipple(): Promise; } -export class ButtonAreaAdapter extends BaseAdapter implements IButtonAreaAdapter, ForgeRippleCapableSurface { +export class ButtonAreaAdapter extends BaseAdapter implements IButtonAreaAdapter { private _rootElement: HTMLElement; private _buttonSlotElement: HTMLSlotElement; + private _contentSlotElement: HTMLSlotElement; private _buttonElement?: HTMLButtonElement; - private _rippleInstance?: ForgeRipple; private _buttonObserver?: MutationObserver; + private _focusIndicatorElement: IFocusIndicatorComponent; + private _stateLayerElement: IStateLayerComponent; private _destroyUserInteractionListener: (() => void) | undefined; + private _destroyDeferListener: (() => void) | undefined; + constructor(component: IButtonAreaComponent) { super(component); this._rootElement = getShadowElement(component, BUTTON_AREA_CONSTANTS.selectors.ROOT); this._buttonSlotElement = getShadowElement(component, BUTTON_AREA_CONSTANTS.selectors.BUTTON_SLOT) as HTMLSlotElement; + this._contentSlotElement = getShadowElement(component, BUTTON_AREA_CONSTANTS.selectors.CONTENT_SLOT) as HTMLSlotElement; + this._focusIndicatorElement = getShadowElement(component, FOCUS_INDICATOR_CONSTANTS.elementName) as IFocusIndicatorComponent; + this._stateLayerElement = getShadowElement(component, STATE_LAYER_CONSTANTS.elementName) as IStateLayerComponent; } public destroy(): void { if (typeof this._destroyUserInteractionListener === 'function') { this._destroyUserInteractionListener(); } + if (typeof this._destroyDeferListener === 'function') { + this._destroyDeferListener(); + this._destroyDeferListener = undefined; + } + } + + public async deferInitialization(listener: (evt?: PointerEvent) => void): Promise { + if (!this._rootElement) { + return; + } + const { userInteraction, destroy } = createUserInteractionListener(this._rootElement); + this._destroyDeferListener = destroy; + const evt = await userInteraction; + listener(evt.type === 'pointerenter' ? evt as PointerEvent : undefined); } public get root(): HTMLElement { @@ -55,22 +80,40 @@ export class ButtonAreaAdapter extends BaseAdapter impleme public setDisabled(value: boolean): void { toggleClass(this._rootElement, value, BUTTON_AREA_CONSTANTS.classes.DISABLED); this._buttonElement?.toggleAttribute(BUTTON_AREA_CONSTANTS.attributes.DISABLED, value); + if (value) { + this._focusIndicatorElement.remove(); + this._stateLayerElement.remove(); + } else { + this._rootElement.append(this._focusIndicatorElement, this._stateLayerElement); + } + } + + public addListener(type: string, listener: (event: Event) => void, capture?: boolean): void { + this._rootElement.addEventListener(type, listener, { capture }); + } + + public removeListener(type: string, listener: (event: Event) => void, capture?: boolean): void { + this._rootElement.removeEventListener(type, listener, { capture }); + } + + public addButtonSlotListener(type: string, listener: (event: Event) => void): void { + this._buttonSlotElement.addEventListener(type, listener); } - public addListener(type: string, listener: (event: Event) => void): void { - this._rootElement.addEventListener(type, listener); + public removeButtonSlotListener(type: string, listener: (event: Event) => void): void { + this._buttonSlotElement.removeEventListener(type, listener); } - public removeListener(type: string, listener: (event: Event) => void): void { - this._rootElement.removeEventListener(type, listener); + public addContentListener(type: string, listener: (event: Event) => void): void { + this._contentSlotElement.addEventListener(type, listener); } - public addSlotChangeListener(listener: () => void): void { - this._buttonSlotElement.addEventListener('slotchange', listener); + public removeContentListener(type: string, listener: (event: Event) => void): void { + this._contentSlotElement.removeEventListener(type, listener); } - public removeSlotChangeListener(listener: () => void): void { - this._buttonSlotElement.removeEventListener('slotchange', listener); + public animateStateLayer(): void { + this._stateLayerElement.playAnimation(); } public startButtonObserver(callback: MutationCallback): void { @@ -102,61 +145,4 @@ export class ButtonAreaAdapter extends BaseAdapter impleme requestAnimationFrame(() => this._buttonElement!.disabled = false); } } - - public async createRipple(): Promise { - if (!this._rippleInstance) { - const type = await this._userInteractionListener(); - if (!this._rippleInstance) { - const adapter: ForgeRippleAdapter = { - ...ForgeRipple.createAdapter(this), - isSurfaceActive: () => this._rootElement.matches(':active') || (this._buttonElement?.matches(':active') ?? false), - isSurfaceDisabled: () => this.disabled ?? true, - isUnbounded: () => !!this.unbounded, - registerInteractionHandler: (evtType, handler) => { - if (this._isRootEvent(evtType)) { - this._rootElement.addEventListener(evtType, handler, { passive: true }); - } else { - this._buttonElement?.addEventListener(evtType, handler, { passive: true}); - } - }, - deregisterInteractionHandler: (evtType, handler) => { - if (this._isRootEvent(evtType)) { - this._rootElement.removeEventListener(evtType, handler, { passive: true } as AddEventListenerOptions); - } else { - this._buttonElement?.removeEventListener(evtType, handler, { passive: true } as AddEventListenerOptions); - } - }, - addClass: (className) => addClass(className, this._rootElement), - removeClass: (className) => removeClass(className, this._rootElement), - updateCssVariable: (varName, value) => this._rootElement.style.setProperty(varName, value) - }; - this._rippleInstance = new ForgeRipple(this._rootElement, new ForgeRippleFoundation(adapter)); - if (type === 'focusin') { - this._rippleInstance.handleFocus(); - } - } - } else { - if (typeof this._destroyUserInteractionListener === 'function') { - this._destroyUserInteractionListener(); - } - - this._rippleInstance.destroy(); - this._rippleInstance = undefined; - } - } - - private async _userInteractionListener(): Promise { - if (typeof this._destroyUserInteractionListener === 'function') { - this._destroyUserInteractionListener(); - } - const { userInteraction, destroy } = createUserInteractionListener(this._rootElement); - this._destroyUserInteractionListener = destroy; - const { type } = await userInteraction; - this._destroyUserInteractionListener = undefined; - return type; - } - - private _isRootEvent(evtType: string): boolean { - return ['touchstart', 'pointerdown', 'mousedown'].includes(evtType); - } } diff --git a/src/lib/button-area/button-area-constants.ts b/src/lib/button-area/button-area-constants.ts index afe2adaa5..97ab23dcd 100644 --- a/src/lib/button-area/button-area-constants.ts +++ b/src/lib/button-area/button-area-constants.ts @@ -10,16 +10,19 @@ const attributes = { const ids = { ROOT: 'root', - BUTTON_SLOT: 'button' + BUTTON_SLOT: 'button', + CONTENT_SLOT: 'content' }; const classes = { + ROOT: `forge-button-area`, DISABLED: `forge-button-area--disabled` }; const selectors = { ROOT: `#${ids.ROOT}`, - BUTTON_SLOT: `slot[name=button]` + BUTTON_SLOT: `slot[name=button]`, + CONTENT_SLOT: `#${ids.CONTENT_SLOT}` }; export const BUTTON_AREA_CONSTANTS = { diff --git a/src/lib/button-area/button-area-foundation.ts b/src/lib/button-area/button-area-foundation.ts index 3e0da66af..20e74e49c 100644 --- a/src/lib/button-area/button-area-foundation.ts +++ b/src/lib/button-area/button-area-foundation.ts @@ -9,33 +9,78 @@ export interface IButtonAreaFoundation extends ICustomElementFoundation { export class ButtonAreaFoundation implements IButtonAreaFoundation { private _disabled = false; + private _attached = false; + private _deferred = true; private _clickListener: (event: Event) => void; private _keydownListener: (event: KeyboardEvent) => void; private _pointerdownListener: (event: Event) => void; + private _ignoreStateLayerListener: (event: Event) => void; private _slotListener: () => void; constructor(private _adapter: IButtonAreaAdapter) { this._clickListener = event => this._handleClick(event); this._keydownListener = event => this._handleKeydown(event); this._pointerdownListener = event => this._handlePointerdown(event); + this._ignoreStateLayerListener = event => this._handleIgnoreStateLayer(event); this._slotListener = () => this._handleSlotChange(); } public initialize(): void { + this._adapter.addButtonSlotListener('slotchange', this._slotListener); + + // Clicks could be triggered programmatically so we need to listen for them regardless this._adapter.addListener('click', this._clickListener); - this._adapter.addListener('keydown', this._keydownListener); - this._adapter.addListener('pointerdown', this._pointerdownListener); - this._adapter.addSlotChangeListener(this._slotListener); - this._adapter.createRipple(); + + // We defer initialization until the first pointerenter event is received. + // + // This is a performance optimization to avoid attaching many listeners to the target element + // until the user is first interacting with it. + this._deferInitialization(); } public disconnect(): void { + this._removeListeners(); this._adapter.destroy(); - this._adapter.removeListener('click', this._clickListener); - this._adapter.removeSlotChangeListener(this._slotListener); this._adapter.stopButtonObserver(); } + private _deferInitialization(): void { + this._adapter.deferInitialization(this._onDeferredInitialize.bind(this)); + } + + private _applyListeners(): void { + if (this._disabled) { + return; + } + + this._adapter.addListener('keydown', this._keydownListener); + this._adapter.addListener('pointerdown', this._pointerdownListener); + this._adapter.addContentListener('click', this._ignoreStateLayerListener.bind(this)); + this._adapter.addContentListener('pointerdown', this._ignoreStateLayerListener.bind(this)); + this._adapter.addContentListener('pointerup', this._ignoreStateLayerListener.bind(this)); + this._attached = true; + } + + private _removeListeners(): void { + this._adapter.removeListener('click', this._clickListener); + this._adapter.removeListener('keydown', this._keydownListener); + this._adapter.removeListener('pointerdown', this._pointerdownListener); + this._adapter.removeContentListener('click', this._ignoreStateLayerListener.bind(this)); + this._adapter.removeContentListener('pointerdown', this._ignoreStateLayerListener.bind(this)); + this._adapter.removeContentListener('pointerup', this._ignoreStateLayerListener.bind(this)); + this._adapter.removeButtonSlotListener('slotchange', this._slotListener); + this._attached = false; + } + + private _onDeferredInitialize(event?: PointerEvent): void { + if (!this._adapter.isConnected) { + return; + } + + this._applyListeners(); + this._deferred = false; + } + private _handleClick(event: Event): void { // Prevent the click if disabled if (this._disabled) { @@ -62,6 +107,8 @@ export class ButtonAreaFoundation implements IButtonAreaFoundation { // Prevent the keydown if it originates from an ignored element if (this._shouldIgnoreEvent(event)) { event.stopPropagation(); + } else { + this._adapter.animateStateLayer(); } } @@ -70,12 +117,23 @@ export class ButtonAreaFoundation implements IButtonAreaFoundation { return; } - // Prevent the ripple animation when ignored children are clicked + // Prevent the pointerdown if it originates from an ignored element if (this._shouldIgnoreEvent(event)) { this._adapter.requestDisabledButtonFrame(); } } + private _handleIgnoreStateLayer(event: Event): void { + if (this._disabled) { + return; + } + + // Prevent the state layer animation if the event originates from an ignored element + if (this._shouldIgnoreEvent(event)) { + event.stopPropagation(); + } + } + private _handleSlotChange(): void { // Clear old button-connected listeners this._adapter.stopButtonObserver(); @@ -100,12 +158,25 @@ export class ButtonAreaFoundation implements IButtonAreaFoundation { return eventPath.some(el => el.nodeType === 1 && (el.hasAttribute(BUTTON_AREA_CONSTANTS.attributes.IGNORE) || el.hasAttribute(BUTTON_AREA_CONSTANTS.attributes.IGNORE_ALT))); } + public get isAttached(): boolean { + return this._attached; + } + public get disabled(): boolean { return this._disabled; } public set disabled(value: boolean) { if (this._disabled !== value) { this._disabled = value; + + if (this._adapter.isConnected) { + if (this._disabled) { + this._removeListeners(); + } else if (!this._deferred) { + this._deferInitialization(); + } + } + this._adapter.setDisabled(this._disabled); this._adapter.toggleHostAttribute(BUTTON_AREA_CONSTANTS.attributes.DISABLED, this._disabled); } diff --git a/src/lib/button-area/button-area.html b/src/lib/button-area/button-area.html index c84007b5d..6cd57c2fd 100644 --- a/src/lib/button-area/button-area.html +++ b/src/lib/button-area/button-area.html @@ -3,6 +3,8 @@
- + + +
\ No newline at end of file diff --git a/src/lib/button-area/button-area.scss b/src/lib/button-area/button-area.scss index e956eeb05..604d48ccb 100644 --- a/src/lib/button-area/button-area.scss +++ b/src/lib/button-area/button-area.scss @@ -1,11 +1,62 @@ -@use './mixins'; +@use './core' as *; +@use '../state-layer'; +@use '../focus-indicator'; +@use '../utils/mixins' as utils; -@include mixins.core-styles; +// +// Host +// + +$host-tokens: [disabled-cursor focus-indicator-color]; :host { - @include mixins.host; + @include tokens($includes: $host-tokens); +} + +:host { + @include host; } :host([hidden]) { display: none; } + +// +// Base +// + +.forge-button-area { + @include tokens($excludes: $host-tokens); +} + +.forge-button-area { + @include base; + + &:not(.forge-button-area--disabled) { + &.forge-button-area { + @include enabled; + } + } + + &.forge-button-area--disabled { + @include disabled; + } + + &__button { + @include utils.visually-hidden; + } +} + +forge-focus-indicator { + z-index: 1; // Fixes an animation artifact in Safari + + @include focus-indicator.provide-theme(( + color: #{token(primary-color)}, + outward-offset: #{token(focus-indicator-offset)} + )); +} + +forge-state-layer { + @include state-layer.provide-theme(( color: #{token(color)} )); + border-radius: inherit; +} diff --git a/src/lib/button-area/button-area.test.ts b/src/lib/button-area/button-area.test.ts new file mode 100644 index 000000000..07e276207 --- /dev/null +++ b/src/lib/button-area/button-area.test.ts @@ -0,0 +1,125 @@ +import { expect } from '@esm-bundle/chai'; +import { spy } from 'sinon'; +import { elementUpdated, fixture, html } from '@open-wc/testing'; +import { BUTTON_AREA_CONSTANTS } from './button-area-constants'; +import { IButtonAreaComponent } from './button-area'; +import type { IStateLayerComponent } from '../state-layer'; +import type { IFocusIndicatorComponent } from '../focus-indicator'; + +import './button-area'; +import { ButtonAreaFoundation } from './button-area-foundation'; +import { simulateHover } from '../core/testing/pointer'; + +const FULL_TEMPLATE = html` + +
+
+ Heading + Content +
+ + + Favorite + + +
+
`; + +const DATA_FORGE_IGNORE_TEMPLATE = html``; + +describe('Button Area', () => { + it('should initialize', async () => { + const el = await fixture(html``); + + const rootEl = getRootEl(el); + const stateLayer = getStateLayer(el); + const focusIndicator = getFocusIndicator(el); + + expect(el.shadowRoot).not.to.be.null; + expect(rootEl.getAttribute('part')).to.equal('root'); + expect(rootEl.classList.contains(BUTTON_AREA_CONSTANTS.classes.ROOT)).to.be.true; + expect(stateLayer.disabled).to.be.false; + expect(focusIndicator).to.be.ok; + }); + + it('should defer attaching event listeners', async () => { + const el = await fixture(FULL_TEMPLATE); + const rootEl = getRootEl(el); + const foundation = el['_foundation'] as ButtonAreaFoundation; + await elementUpdated(rootEl); + expect(foundation.isAttached).to.be.false; + + simulateHover(rootEl); + await elementUpdated(rootEl); + + expect(foundation.isAttached).to.be.true; + }); + + it('should be accessible', async () => { + const el = await fixture(FULL_TEMPLATE); + + await expect(el).to.be.accessible(); + }); + + it('should handle click', async () => { + const el = await fixture(FULL_TEMPLATE); + const clickSpy = spy(); + el.addEventListener('click', clickSpy); + + el.click(); + + expect(clickSpy.calledOnce).to.be.true; + }); + + it('should dispatch click event when button is clicked', async () => { + const el = await fixture(FULL_TEMPLATE); + const button = getButtonEl(el); + const clickSpy = spy(); + el.addEventListener('click', clickSpy); + + button.click(); + + expect(clickSpy.calledOnce).to.be.true; + }); + + it('should not dispatch click event when disabled', async () => { + const el = await fixture(FULL_TEMPLATE); + const button = getButtonEl(el); + const clickSpy = spy(); + el.addEventListener('click', clickSpy); + + el.disabled = true; + button.click(); + + expect(clickSpy.called).to.be.false; + }); + + it('should not dispatch click event when ignored children are clicked', async () => { + const el = await fixture(DATA_FORGE_IGNORE_TEMPLATE); + const button = getButtonEl(el); + const clickSpy = spy(); + el.addEventListener('click', clickSpy); + + button.click(); + + expect(clickSpy.called).to.be.false; + }); + + function getRootEl(el: IButtonAreaComponent): HTMLElement { + return el.shadowRoot?.firstElementChild as HTMLElement; + } + + function getStateLayer(el: IButtonAreaComponent): IStateLayerComponent { + return el.shadowRoot?.querySelector('forge-state-layer') as IStateLayerComponent + } + + function getFocusIndicator(el: IButtonAreaComponent): IFocusIndicatorComponent { + return el.shadowRoot?.querySelector('forge-focus-indicator') as IFocusIndicatorComponent; + } + + function getButtonEl(el: IButtonAreaComponent): HTMLButtonElement { + return el.querySelector('[slot=button]') as HTMLButtonElement; + } +}); diff --git a/src/lib/button-area/button-area.ts b/src/lib/button-area/button-area.ts index dbef38258..f48f169c0 100644 --- a/src/lib/button-area/button-area.ts +++ b/src/lib/button-area/button-area.ts @@ -2,7 +2,8 @@ import { CustomElement, attachShadowTemplate, ICustomElement, FoundationProperty import { ButtonAreaAdapter } from './button-area-adapter'; import { ButtonAreaFoundation } from './button-area-foundation'; import { BUTTON_AREA_CONSTANTS } from './button-area-constants'; -import { RippleComponent } from '../ripple'; +import { FocusIndicatorComponent } from '../focus-indicator'; +import { StateLayerComponent } from '../state-layer'; import template from './button-area.html'; import styles from './button-area.scss'; @@ -19,13 +20,33 @@ declare global { /** * The custom element class behind the `` element. - * + * * @tag forge-button-area + * + * @summary Button areas are used to create a clickable area that group related information and actions about a single subject. + * + * @description + * The button area component wraps any arbitrary content with a @@ -44,19 +42,6 @@ describe('Button Area', () => { expect(focusIndicator).to.be.ok; }); - it('should defer attaching event listeners', async () => { - const el = await fixture(FULL_TEMPLATE); - const rootEl = getRootEl(el); - const foundation = el['_foundation'] as ButtonAreaFoundation; - await elementUpdated(rootEl); - expect(foundation.isAttached).to.be.false; - - simulateHover(rootEl); - await elementUpdated(rootEl); - - expect(foundation.isAttached).to.be.true; - }); - it('should be accessible', async () => { const el = await fixture(FULL_TEMPLATE); From 09dfc3a59c8c3348077a65df29f19aa18c09e34d Mon Sep 17 00:00:00 2001 From: derekmoss Date: Wed, 28 Feb 2024 14:10:24 -0800 Subject: [PATCH 11/19] chore: remove whitespace --- src/lib/button-area/button-area-foundation.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/button-area/button-area-foundation.ts b/src/lib/button-area/button-area-foundation.ts index 447edaab6..1ce747677 100644 --- a/src/lib/button-area/button-area-foundation.ts +++ b/src/lib/button-area/button-area-foundation.ts @@ -128,7 +128,6 @@ export class ButtonAreaFoundation implements IButtonAreaFoundation { public set disabled(value: boolean) { if (this._disabled !== value) { this._disabled = value; - this._adapter.setDisabled(this._disabled); this._adapter.toggleHostAttribute(BUTTON_AREA_CONSTANTS.attributes.DISABLED, this._disabled); } From ef7a2b354753a7d5a8e57396b7f795d047e07849 Mon Sep 17 00:00:00 2001 From: derekmoss Date: Mon, 4 Mar 2024 11:48:35 -0800 Subject: [PATCH 12/19] fix: remove appearance styles --- src/lib/button-area/_core.scss | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/lib/button-area/_core.scss b/src/lib/button-area/_core.scss index 8e60594ac..b2815a05b 100644 --- a/src/lib/button-area/_core.scss +++ b/src/lib/button-area/_core.scss @@ -9,11 +9,6 @@ @mixin base { position: relative; - - outline: none; - user-select: none; - -webkit-appearance: none; - overflow: hidden; } From cb73c498a2575b75c0cc63f837fb2c36d43ab42e Mon Sep 17 00:00:00 2001 From: derekmoss Date: Mon, 4 Mar 2024 11:57:29 -0800 Subject: [PATCH 13/19] chore: switch to disabled attr style --- src/lib/button-area/button-area-adapter.ts | 3 +-- src/lib/button-area/button-area-constants.ts | 3 +-- src/lib/button-area/button-area.scss | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/lib/button-area/button-area-adapter.ts b/src/lib/button-area/button-area-adapter.ts index 1bb6c234a..ff8d85a4e 100644 --- a/src/lib/button-area/button-area-adapter.ts +++ b/src/lib/button-area/button-area-adapter.ts @@ -1,4 +1,4 @@ -import { getShadowElement, toggleClass } from '@tylertech/forge-core'; +import { getShadowElement } from '@tylertech/forge-core'; import { BaseAdapter, IBaseAdapter } from '../core'; import { FOCUS_INDICATOR_CONSTANTS, IFocusIndicatorComponent } from '../focus-indicator'; @@ -63,7 +63,6 @@ export class ButtonAreaAdapter extends BaseAdapter impleme } public setDisabled(value: boolean): void { - toggleClass(this._rootElement, value, BUTTON_AREA_CONSTANTS.classes.DISABLED); this._buttonElement?.toggleAttribute(BUTTON_AREA_CONSTANTS.attributes.DISABLED, value); if (value) { this._focusIndicatorElement.remove(); diff --git a/src/lib/button-area/button-area-constants.ts b/src/lib/button-area/button-area-constants.ts index 97ab23dcd..841e62a96 100644 --- a/src/lib/button-area/button-area-constants.ts +++ b/src/lib/button-area/button-area-constants.ts @@ -15,8 +15,7 @@ const ids = { }; const classes = { - ROOT: `forge-button-area`, - DISABLED: `forge-button-area--disabled` + ROOT: `forge-button-area` }; const selectors = { diff --git a/src/lib/button-area/button-area.scss b/src/lib/button-area/button-area.scss index 7bbecd7cc..e3e62068a 100644 --- a/src/lib/button-area/button-area.scss +++ b/src/lib/button-area/button-area.scss @@ -15,12 +15,22 @@ $host-tokens: [disabled-cursor focus-indicator-color]; :host { @include host; + + .forge-button-area { + @include enabled; + } } :host([hidden]) { display: none; } +:host([disabled]) { + .forge-button-area { + @include disabled; + } +} + // // Base // @@ -32,16 +42,6 @@ $host-tokens: [disabled-cursor focus-indicator-color]; .forge-button-area { @include base; - &:not(.forge-button-area--disabled) { - &.forge-button-area { - @include enabled; - } - } - - &.forge-button-area--disabled { - @include disabled; - } - &__button { @include utils.visually-hidden; } From 08bedd081d9cd9aa8ca430fd2f662977e25532cb Mon Sep 17 00:00:00 2001 From: derekmoss Date: Mon, 4 Mar 2024 11:57:44 -0800 Subject: [PATCH 14/19] chore: remove demo delta --- src/dev/pages/stack/stack.ejs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/dev/pages/stack/stack.ejs b/src/dev/pages/stack/stack.ejs index d88e62034..b6b484b0c 100644 --- a/src/dev/pages/stack/stack.ejs +++ b/src/dev/pages/stack/stack.ejs @@ -153,14 +153,6 @@ - - - - - - - - From 6cd7f077dc210a27f8fc166238d817be5af1d698 Mon Sep 17 00:00:00 2001 From: derekmoss Date: Mon, 4 Mar 2024 13:32:43 -0800 Subject: [PATCH 15/19] chore: remove BEM --- src/lib/button-area/button-area.html | 2 +- src/lib/button-area/button-area.scss | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/lib/button-area/button-area.html b/src/lib/button-area/button-area.html index 6cd57c2fd..5ac5c4fe7 100644 --- a/src/lib/button-area/button-area.html +++ b/src/lib/button-area/button-area.html @@ -1,6 +1,6 @@