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;