Merge pull request #213 from shoelace-style/vertical-button-groups

add vertical orientation to button groups; fixes #185
This commit is contained in:
Cory LaViska
2024-11-14 14:02:42 -05:00
committed by GitHub
5 changed files with 131 additions and 9 deletions

View File

@@ -42,6 +42,31 @@ All button sizes are supported, but avoid mixing sizes within the same button gr
</wa-button-group>
```
### Vertical button groups
Set the `orientation` attribute to `vertical` to make a vertical button group.
```html {.example}
<wa-button-group orientation="vertical" label="Options" style="max-width: 80px;">
<wa-button>
<wa-icon slot="prefix" name="plus"></wa-icon>
New
</wa-button>
<wa-button>
<wa-icon slot="prefix" name="folder-open"></wa-icon>
Open
</wa-button>
<wa-button>
<wa-icon slot="prefix" name="save"></wa-icon>
Save
</wa-button>
<wa-button>
<wa-icon slot="prefix" name="print"></wa-icon>
Print
</wa-button>
</wa-button-group>
```
### Theme Buttons
Theme buttons are supported through the button's `variant` attribute.

View File

@@ -16,6 +16,7 @@ During the alpha period, things might break! We take breaking changes very serio
- Added support for <kbd>Enter</kbd> to `<sl-split-panel>` 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 `<wa-select>`
- Added support for vertical button groups
- Fixed a bug in `<wa-rating>` when using `precision`
- Fixed a bug in `<wa-relative-time>` where the title attribute would show with redundant info
- Fixed a bug in `<wa-tooltip>` that caused a memory leak in disconnected elements

View File

@@ -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;
}
`;

View File

@@ -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<string, unknown>) {
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);

View File

@@ -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;