kopia lustrzana https://github.com/shoelace-style/shoelace
rework button group to not use light DOM styles
rodzic
48216a4fdc
commit
7a64963fef
|
@ -16,6 +16,7 @@ _During the beta period, these restrictions may be relaxed in the event of a mis
|
|||
- Improved a11y for disabled buttons that are rendered as links
|
||||
- Improved a11y for `sl-button-group`
|
||||
- Removed `sl-show`, `sl-hide`, `sl-after-show`, `sl-after-hide` events from `sl-color-picker` (the color picker's visibility cannot be controlled programmatically so these shouldn't have been exposed; the dropdown events now bubble up so you can listen for those instead)
|
||||
- Reworked `sl-button-group` so it doesn't require light DOM styles
|
||||
|
||||
## 2.0.0-beta.36
|
||||
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
//
|
||||
// In general, we should avoid placing styles in the light DOM. However, we don't have a way to target slotted element
|
||||
// parts, e.g. `::slotted(sl-button)::part(base)`, so we need these styles to make buttons groups work.
|
||||
//
|
||||
// The alternative approach is to set the styles with JavaScript, but this is more expensive because it requires
|
||||
// multiple listeners + DOM traversals.
|
||||
//
|
||||
sl-button-group {
|
||||
// First
|
||||
> sl-button:first-child::part(base),
|
||||
> sl-dropdown:first-child > sl-button[slot='trigger']::part(base),
|
||||
> sl-tooltip:first-child > sl-button::part(base) {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
// Last
|
||||
> sl-button:last-child::part(base),
|
||||
> sl-dropdown:last-child > sl-button[slot='trigger']::part(base),
|
||||
> sl-tooltip:last-child > sl-button::part(base) {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
// Interior
|
||||
> sl-button:not(:first-child):not(:last-child)::part(base),
|
||||
> sl-dropdown:not(:first-child):not(:last-child) > sl-button[slot='trigger']::part(base),
|
||||
> sl-tooltip:not(:first-child):not(:last-child) > sl-button::part(base) {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
// All except the first
|
||||
> sl-button:not(:first-child),
|
||||
> sl-dropdown:not(:first-child) > sl-button[slot='trigger'],
|
||||
> sl-tooltip:not(:first-child) > sl-button {
|
||||
margin-left: calc(-1 * var(--sl-input-border-width));
|
||||
|
||||
// Add a visual separator between solid buttons
|
||||
&:not([type='default'])::part(base):not(:hover):not(:active):not(:focus):after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
border-left: solid 1px #ffffff40;
|
||||
}
|
||||
}
|
||||
|
||||
// Hover
|
||||
> sl-button:hover,
|
||||
> sl-dropdown:hover > sl-button[slot='trigger'],
|
||||
> sl-tooltip:hover > sl-button {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
// Focus
|
||||
> sl-button.sl-focus,
|
||||
> sl-dropdown > sl-button[slot='trigger'].sl-focus,
|
||||
> sl-tooltip > sl-button.sl-focus {
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
|
@ -7,9 +7,4 @@
|
|||
.button-group {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
::slotted(.sl-focus) {
|
||||
z-index: 1;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { LitElement, html, unsafeCSS } from 'lit';
|
||||
import { customElement, property } from 'lit/decorators';
|
||||
import { customElement, property, query } from 'lit/decorators';
|
||||
import styles from 'sass:./button-group.scss';
|
||||
|
||||
/**
|
||||
|
@ -14,17 +14,45 @@ import styles from 'sass:./button-group.scss';
|
|||
export default class SlButtonGroup extends LitElement {
|
||||
static styles = unsafeCSS(styles);
|
||||
|
||||
@query('slot') defaultSlot: HTMLSlotElement;
|
||||
|
||||
/** A label to use for the button group's `aria-label` attribute. */
|
||||
@property() label = '';
|
||||
|
||||
handleFocus(event: CustomEvent) {
|
||||
const button = event.target as HTMLElement;
|
||||
button.classList.add('sl-focus');
|
||||
const button = findButton(event.target as HTMLElement);
|
||||
button?.classList.add('sl-button-group__button--focus');
|
||||
}
|
||||
|
||||
handleBlur(event: CustomEvent) {
|
||||
const button = event.target as HTMLElement;
|
||||
button.classList.remove('sl-focus');
|
||||
const button = findButton(event.target as HTMLElement);
|
||||
button?.classList.remove('sl-button-group__button--focus');
|
||||
}
|
||||
|
||||
handleMouseOver(event: CustomEvent) {
|
||||
const button = findButton(event.target as HTMLElement);
|
||||
button?.classList.add('sl-button-group__button--hover');
|
||||
}
|
||||
|
||||
handleMouseOut(event: CustomEvent) {
|
||||
const button = findButton(event.target as HTMLElement);
|
||||
button?.classList.remove('sl-button-group__button--hover');
|
||||
}
|
||||
|
||||
handleSlotChange() {
|
||||
const slottedElements = [...this.defaultSlot.assignedElements({ flatten: true })] as HTMLElement[];
|
||||
|
||||
slottedElements.map(el => {
|
||||
const index = slottedElements.indexOf(el);
|
||||
const button = findButton(el);
|
||||
|
||||
if (button) {
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -36,13 +64,19 @@ export default class SlButtonGroup extends LitElement {
|
|||
aria-label=${this.label}
|
||||
@focusout=${this.handleBlur}
|
||||
@focusin=${this.handleFocus}
|
||||
@mouseover=${this.handleMouseOver}
|
||||
@mouseout=${this.handleMouseOut}
|
||||
>
|
||||
<slot></slot>
|
||||
<slot @slotchange=${this.handleSlotChange}></slot>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
function findButton(el: HTMLElement) {
|
||||
return el.tagName.toLowerCase() === 'sl-button' ? el : el.querySelector('sl-button');
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'sl-button-group': SlButtonGroup;
|
||||
|
|
|
@ -446,3 +446,49 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Button groups support a variety of button types (e.g. buttons with tooltips, buttons as dropdown triggers, etc.).
|
||||
// This means buttons aren't always direct descendants of the button group, thus we can't target them with the ::slotted
|
||||
// selector. To work around this, the button group component does some magic to add these special classes to buttons and
|
||||
// we style them here instead.
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
:host(.sl-button-group__button--first) .button {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
:host(.sl-button-group__button--inner) .button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
:host(.sl-button-group__button--last) .button {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
// All except the first
|
||||
:host(.sl-button-group__button:not(.sl-button-group__button--first)) {
|
||||
margin-left: calc(-1 * var(--sl-input-border-width));
|
||||
}
|
||||
|
||||
// Add a visual separator between solid buttons
|
||||
:host(.sl-button-group__button:not(.sl-button-group__button--focus, .sl-button-group__button--first, [type='default']):not(:hover, :active, :focus))
|
||||
.button:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
border-left: solid 1px #ffffff40;
|
||||
}
|
||||
|
||||
// Bump focused buttons up so their focus ring isn't clipped
|
||||
:host(.sl-button-group__button--hover) {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
:host(.sl-button-group__button--focus) {
|
||||
z-index: 2;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@use '../components/alert/alert.light-dom';
|
||||
@use '../components/button-group/button-group.light-dom';
|
||||
|
||||
:root {
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
Ładowanie…
Reference in New Issue