From 84f7a22dfe38365b99c1690c211144bbcdd20362 Mon Sep 17 00:00:00 2001 From: Cory LaViska Date: Thu, 14 Nov 2024 13:59:33 -0500 Subject: [PATCH] add vertical orientation to button groups; fixes #185 --- docs/docs/components/button-group.md | 25 ++++++ docs/docs/resources/changelog.md | 1 + .../button-group/button-group.styles.ts | 15 +++- src/components/button-group/button-group.ts | 17 ++++ src/components/button/button.styles.ts | 82 +++++++++++++++++-- 5 files changed, 131 insertions(+), 9 deletions(-) diff --git a/docs/docs/components/button-group.md b/docs/docs/components/button-group.md index 2e09c930e..8588de96b 100644 --- a/docs/docs/components/button-group.md +++ b/docs/docs/components/button-group.md @@ -42,6 +42,31 @@ All button sizes are supported, but avoid mixing sizes within the same button gr ``` +### Vertical button groups + +Set the `orientation` attribute to `vertical` to make a vertical button group. + +```html {.example} + + + + New + + + + Open + + + + Save + + + + Print + + +``` + ### Theme Buttons Theme buttons are supported through the button's `variant` attribute. diff --git a/docs/docs/resources/changelog.md b/docs/docs/resources/changelog.md index cdac8f393..70144e6ec 100644 --- a/docs/docs/resources/changelog.md +++ b/docs/docs/resources/changelog.md @@ -16,6 +16,7 @@ During the alpha period, things might break! We take breaking changes very serio - Added support for Enter to `` to align with ARIA APG's [window splitter pattern](https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/) - Added more resilient support for lazy loaded options in `` +- Added support for vertical button groups - Fixed a bug in `` when using `precision` - Fixed a bug in `` where the title attribute would show with redundant info - Fixed a bug in `` that caused a memory leak in disconnected elements diff --git a/src/components/button-group/button-group.styles.ts b/src/components/button-group/button-group.styles.ts index 89e6de8d6..0e3ac3831 100644 --- a/src/components/button-group/button-group.styles.ts +++ b/src/components/button-group/button-group.styles.ts @@ -2,11 +2,22 @@ import { css } from 'lit'; export default css` :host { - display: inline-block; + display: inline-flex; } .button-group { display: flex; - flex-wrap: nowrap; + position: relative; + flex-wrap: wrap; + isolation: isolate; + } + + :host([orientation='vertical']) .button-group { + flex-direction: column; + } + + /* Show the focus indicator above other buttons */ + ::slotted(:focus) { + z-index: 1 !important; } `; diff --git a/src/components/button-group/button-group.ts b/src/components/button-group/button-group.ts index c4ebfc0ea..891391e5f 100644 --- a/src/components/button-group/button-group.ts +++ b/src/components/button-group/button-group.ts @@ -21,6 +21,7 @@ export default class WaButtonGroup extends WebAwesomeElement { @query('slot') defaultSlot: HTMLSlotElement; + /** @internal */ @state() disableRole = false; /** @@ -29,6 +30,16 @@ export default class WaButtonGroup extends WebAwesomeElement { */ @property() label = ''; + /** The button group's orientation. */ + @property({ reflect: true }) orientation: 'horizontal' | 'vertical' = 'horizontal'; + + updated(changedProps: Map) { + if (changedProps.has('orientation')) { + this.setAttribute('aria-orientation', this.orientation); + this.updateClassNames(); + } + } + private handleFocus(event: Event) { const button = findButton(event.target as HTMLElement); button?.classList.add('wa-button-group__button--focus'); @@ -50,6 +61,10 @@ export default class WaButtonGroup extends WebAwesomeElement { } private handleSlotChange() { + this.updateClassNames(); + } + + private updateClassNames() { const slottedElements = [...this.defaultSlot.assignedElements({ flatten: true })] as HTMLElement[]; slottedElements.forEach(el => { @@ -58,6 +73,8 @@ export default class WaButtonGroup extends WebAwesomeElement { if (button) { button.classList.add('wa-button-group__button'); + button.classList.toggle('wa-button-group-horizontal', this.orientation === 'horizontal'); + button.classList.toggle('wa-button-group-vertical', this.orientation === 'vertical'); button.classList.toggle('wa-button-group__button--first', index === 0); button.classList.toggle('wa-button-group__button--inner', index > 0 && index < slottedElements.length - 1); button.classList.toggle('wa-button-group__button--last', index === slottedElements.length - 1); diff --git a/src/components/button/button.styles.ts b/src/components/button/button.styles.ts index a05420235..d6f6141d6 100644 --- a/src/components/button/button.styles.ts +++ b/src/components/button/button.styles.ts @@ -440,36 +440,104 @@ export default css` * buttons and we style them here instead. */ - :host(.wa-button-group__button--first:not(.wa-button-group__button--last)) .button { + /* + :host([data-button-group-middle]) #button { + border-radius: 0; + } + + :host([data-button-group-horizontal][data-button-group-first]) #button { border-start-end-radius: 0; border-end-end-radius: 0; } + :host([data-button-group-horizontal][data-button-group-last]) #button { + border-start-start-radius: 0; + border-end-start-radius: 0; + } + + :host([data-button-group-vertical][data-button-group-first]) #button { + border-end-start-radius: 0; + border-end-end-radius: 0; + } + + :host([data-button-group-vertical][data-button-group-last]) #button { + border-start-start-radius: 0; + border-start-end-radius: 0; + } + */ + :host(.wa-button-group__button--inner) .button { border-radius: 0; } - :host(.wa-button-group__button--last:not(.wa-button-group__button--first)) .button { + :host(.wa-button-group-horizontal.wa-button-group__button--first:not(.wa-button-group__button--last)) .button { + border-start-end-radius: 0; + border-end-end-radius: 0; + } + + :host(.wa-button-group-horizontal.wa-button-group__button--last:not(.wa-button-group__button--first)) .button { border-start-start-radius: 0; border-end-start-radius: 0; } + :host(.wa-button-group-vertical.wa-button-group__button--first:not(.wa-button-group__button--last)) .button { + border-end-start-radius: 0; + border-end-end-radius: 0; + } + + :host(.wa-button-group-vertical.wa-button-group__button--last:not(.wa-button-group__button--first)) .button { + border-start-start-radius: 0; + border-start-end-radius: 0; + } + /* All except the first */ - :host(.wa-button-group__button:not(.wa-button-group__button--first)) { + :host( + .wa-button-group-horizontal.wa-button-group-horizontal.wa-button-group__button:not( + .wa-button-group__button--first + ) + ) { margin-inline-start: calc(-1 * var(--border-width)); } + :host( + .wa-button-group-vertical.wa-button-group-horizontal.wa-button-group__button:not(.wa-button-group__button--first) + ) { + margin-block-start: calc(-1 * var(--border-width)); + } + /* Add a visual separator between filled buttons */ :host(.wa-button-group__button:not(.wa-button-group__button--first, .wa-button-group__button--radio)) .button:after { content: ''; position: absolute; - top: 0; - inset-inline-start: 0; - bottom: 0; - border-left: solid max(var(--border-width), 1px) var(--border-color, rgb(0 0 0 / 0.3)); z-index: 2; /* Keep separators visible on hover */ } + :host( + .wa-button-group-horizontal.wa-button-group__button:not( + .wa-button-group__button--first, + .wa-button-group__button--radio + ) + ) + .button:after { + top: 0; + bottom: 0; + inset-inline-start: 0; + border-left: solid max(var(--border-width), 1px) var(--border-color, rgb(0 0 0 / 0.3)); + } + + :host( + .wa-button-group-vertical.wa-button-group__button:not( + .wa-button-group__button--first, + .wa-button-group__button--radio + ) + ) + .button:after { + left: 0; + right: 0; + inset-block-start: 0; + border-top: solid max(var(--border-width), 1px) var(--border-color, rgb(0 0 0 / 0.3)); + } + /* Bump hovered, focused, and checked buttons up so their focus ring isn't clipped */ :host(.wa-button-group__button--hover) { z-index: 1;