2022-08-02 17:03:47 +00:00
|
|
|
import { customElement, property, query, state } from 'lit/decorators.js';
|
2023-01-13 20:43:55 +00:00
|
|
|
import { html } from 'lit';
|
2022-08-17 15:37:37 +00:00
|
|
|
import ShoelaceElement from '../../internal/shoelace-element';
|
2021-07-10 00:45:44 +00:00
|
|
|
import styles from './button-group.styles';
|
2022-07-19 12:27:39 +00:00
|
|
|
import type { CSSResultGroup } from 'lit';
|
2020-08-06 13:07:24 +00:00
|
|
|
|
|
|
|
/**
|
2022-10-21 13:56:35 +00:00
|
|
|
* @summary Button groups can be used to group related buttons into sections.
|
2023-01-12 15:26:25 +00:00
|
|
|
* @documentation https://shoelace.style/components/button-group
|
2020-08-06 13:07:24 +00:00
|
|
|
* @status stable
|
2023-01-12 15:26:25 +00:00
|
|
|
* @since 2.0
|
2020-08-06 13:07:24 +00:00
|
|
|
*
|
2021-06-25 20:25:46 +00:00
|
|
|
* @slot - One or more `<sl-button>` elements to display in the button group.
|
2020-08-06 13:07:24 +00:00
|
|
|
*
|
2022-12-06 16:18:14 +00:00
|
|
|
* @csspart base - The component's base wrapper.
|
2020-08-06 13:07:24 +00:00
|
|
|
*/
|
2021-03-18 13:04:23 +00:00
|
|
|
@customElement('sl-button-group')
|
2022-08-17 15:37:37 +00:00
|
|
|
export default class SlButtonGroup extends ShoelaceElement {
|
2022-07-19 12:27:39 +00:00
|
|
|
static styles: CSSResultGroup = styles;
|
2020-08-06 13:07:24 +00:00
|
|
|
|
2021-04-02 11:24:59 +00:00
|
|
|
@query('slot') defaultSlot: HTMLSlotElement;
|
|
|
|
|
2022-08-02 17:03:47 +00:00
|
|
|
@state() disableRole = false;
|
|
|
|
|
2022-12-06 16:18:14 +00:00
|
|
|
/**
|
|
|
|
* A label to use for the button group. This won't be displayed on the screen, but it will be announced by assistive
|
|
|
|
* devices when interacting with the control and is strongly recommended.
|
|
|
|
*/
|
2021-07-01 00:04:46 +00:00
|
|
|
@property() label = '';
|
2020-08-06 13:07:24 +00:00
|
|
|
|
2023-01-03 20:04:07 +00:00
|
|
|
private handleFocus(event: CustomEvent) {
|
2021-04-02 11:24:59 +00:00
|
|
|
const button = findButton(event.target as HTMLElement);
|
|
|
|
button?.classList.add('sl-button-group__button--focus');
|
2020-08-06 13:07:24 +00:00
|
|
|
}
|
|
|
|
|
2023-01-03 20:04:07 +00:00
|
|
|
private handleBlur(event: CustomEvent) {
|
2021-04-02 11:24:59 +00:00
|
|
|
const button = findButton(event.target as HTMLElement);
|
|
|
|
button?.classList.remove('sl-button-group__button--focus');
|
|
|
|
}
|
|
|
|
|
2023-01-03 20:04:07 +00:00
|
|
|
private handleMouseOver(event: CustomEvent) {
|
2021-04-02 11:24:59 +00:00
|
|
|
const button = findButton(event.target as HTMLElement);
|
|
|
|
button?.classList.add('sl-button-group__button--hover');
|
|
|
|
}
|
|
|
|
|
2023-01-03 20:04:07 +00:00
|
|
|
private handleMouseOut(event: CustomEvent) {
|
2021-04-02 11:24:59 +00:00
|
|
|
const button = findButton(event.target as HTMLElement);
|
|
|
|
button?.classList.remove('sl-button-group__button--hover');
|
|
|
|
}
|
|
|
|
|
2023-01-03 20:04:07 +00:00
|
|
|
private handleSlotChange() {
|
2021-04-02 11:24:59 +00:00
|
|
|
const slottedElements = [...this.defaultSlot.assignedElements({ flatten: true })] as HTMLElement[];
|
|
|
|
|
2022-01-16 05:47:14 +00:00
|
|
|
slottedElements.forEach(el => {
|
2021-04-02 11:24:59 +00:00
|
|
|
const index = slottedElements.indexOf(el);
|
|
|
|
const button = findButton(el);
|
|
|
|
|
2022-01-16 05:47:14 +00:00
|
|
|
if (button !== null) {
|
2021-04-02 11:24:59 +00:00
|
|
|
button.classList.add('sl-button-group__button');
|
|
|
|
button.classList.toggle('sl-button-group__button--first', index === 0);
|
|
|
|
button.classList.toggle('sl-button-group__button--inner', index > 0 && index < slottedElements.length - 1);
|
|
|
|
button.classList.toggle('sl-button-group__button--last', index === slottedElements.length - 1);
|
2022-07-06 13:48:30 +00:00
|
|
|
button.classList.toggle('sl-button-group__button--radio', button.tagName.toLowerCase() === 'sl-radio-button');
|
2021-04-02 11:24:59 +00:00
|
|
|
}
|
|
|
|
});
|
2020-08-06 13:07:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
2022-12-02 22:03:59 +00:00
|
|
|
// eslint-disable-next-line lit-a11y/mouse-events-have-key-events
|
2021-02-26 14:09:13 +00:00
|
|
|
return html`
|
2022-12-02 22:03:59 +00:00
|
|
|
<slot
|
2021-02-26 14:09:13 +00:00
|
|
|
part="base"
|
|
|
|
class="button-group"
|
2022-08-02 17:03:47 +00:00
|
|
|
role="${this.disableRole ? 'presentation' : 'group'}"
|
2021-02-26 14:09:13 +00:00
|
|
|
aria-label=${this.label}
|
2021-03-06 17:01:39 +00:00
|
|
|
@focusout=${this.handleBlur}
|
|
|
|
@focusin=${this.handleFocus}
|
2021-04-02 11:24:59 +00:00
|
|
|
@mouseover=${this.handleMouseOver}
|
|
|
|
@mouseout=${this.handleMouseOut}
|
2022-12-02 22:03:59 +00:00
|
|
|
@slotchange=${this.handleSlotChange}
|
|
|
|
></slot>
|
2021-02-26 14:09:13 +00:00
|
|
|
`;
|
2020-08-06 13:07:24 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-12 14:07:38 +00:00
|
|
|
|
2021-04-02 11:24:59 +00:00
|
|
|
function findButton(el: HTMLElement) {
|
2022-11-29 19:22:50 +00:00
|
|
|
const selector = 'sl-button, sl-radio-button';
|
|
|
|
|
|
|
|
// The button could be the target element or a child of it (e.g. a dropdown or tooltip anchor)
|
|
|
|
return el.closest(selector) ?? el.querySelector(selector);
|
2021-04-02 11:24:59 +00:00
|
|
|
}
|
|
|
|
|
2021-03-12 14:09:08 +00:00
|
|
|
declare global {
|
|
|
|
interface HTMLElementTagNameMap {
|
|
|
|
'sl-button-group': SlButtonGroup;
|
|
|
|
}
|
|
|
|
}
|