kopia lustrzana https://github.com/shoelace-style/shoelace
Add button group component
rodzic
767decf6c6
commit
2b2a37bc3f
|
@ -2,6 +2,7 @@
|
|||
|
||||
## 2.0.0-beta.10
|
||||
|
||||
- Added button group component
|
||||
- Added community page to the docs
|
||||
- Fixed a bug where many components would erroneously receive an `id` when using the custom elements bundle
|
||||
- Fixed a bug where tab groups weren't scrollable with the mouse
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
- [Avatar](/components/avatar.md)
|
||||
- [Badge](/components/badge.md)
|
||||
- [Button](/components/button.md)
|
||||
- [Button Group](/components/button-group.md)
|
||||
- [Card](/components/card.md)
|
||||
- [Checkbox](/components/checkbox.md)
|
||||
- [Color Picker](/components/color-picker.md)
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
# Button Group
|
||||
|
||||
[component-header:sl-button-group]
|
||||
|
||||
Button groups can be used to group related buttons into sections.
|
||||
|
||||
```html preview
|
||||
<sl-button-group>
|
||||
<sl-button>Left</sl-button>
|
||||
<sl-button>Center</sl-button>
|
||||
<sl-button>Right</sl-button>
|
||||
</sl-button-group>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Button Sizes
|
||||
|
||||
All button sizes are supported, but avoid mixing sizes within the same button group.
|
||||
|
||||
```html preview
|
||||
<sl-button-group>
|
||||
<sl-button size="small">Left</sl-button>
|
||||
<sl-button size="small">Center</sl-button>
|
||||
<sl-button size="small">Right</sl-button>
|
||||
</sl-button-group>
|
||||
|
||||
<br><br>
|
||||
|
||||
<sl-button-group>
|
||||
<sl-button size="medium">Left</sl-button>
|
||||
<sl-button size="medium">Center</sl-button>
|
||||
<sl-button size="medium">Right</sl-button>
|
||||
</sl-button-group>
|
||||
|
||||
<br><br>
|
||||
|
||||
<sl-button-group>
|
||||
<sl-button size="large">Left</sl-button>
|
||||
<sl-button size="large">Center</sl-button>
|
||||
<sl-button size="large">Right</sl-button>
|
||||
</sl-button-group>
|
||||
```
|
||||
|
||||
### Theme Buttons
|
||||
|
||||
Theme buttons are supported through the button's `type` attribute. Types can be mixed as needed.
|
||||
|
||||
```html preview
|
||||
<sl-button-group>
|
||||
<sl-button type="primary">Left</sl-button>
|
||||
<sl-button type="primary">Center</sl-button>
|
||||
<sl-button type="primary">Right</sl-button>
|
||||
</sl-button-group>
|
||||
|
||||
<br><br>
|
||||
|
||||
<sl-button-group>
|
||||
<sl-button type="info">Left</sl-button>
|
||||
<sl-button type="info">Center</sl-button>
|
||||
<sl-button type="info">Right</sl-button>
|
||||
</sl-button-group>
|
||||
|
||||
<br><br>
|
||||
|
||||
<sl-button-group>
|
||||
<sl-button>Create</sl-button>
|
||||
<sl-button>Edit</sl-button>
|
||||
<sl-button type="danger">Destroy</sl-button>
|
||||
</sl-button-group>
|
||||
```
|
||||
|
||||
### Pill Buttons
|
||||
|
||||
Pill buttons are supported through the button's `pill` attribute.
|
||||
|
||||
```html preview
|
||||
<sl-button-group>
|
||||
<sl-button size="small" pill>Left</sl-button>
|
||||
<sl-button size="small" pill>Center</sl-button>
|
||||
<sl-button size="small" pill>Right</sl-button>
|
||||
</sl-button-group>
|
||||
|
||||
<br><br>
|
||||
|
||||
<sl-button-group>
|
||||
<sl-button size="medium" pill>Left</sl-button>
|
||||
<sl-button size="medium" pill>Center</sl-button>
|
||||
<sl-button size="medium" pill>Right</sl-button>
|
||||
</sl-button-group>
|
||||
|
||||
<br><br>
|
||||
|
||||
<sl-button-group>
|
||||
<sl-button size="large" pill>Left</sl-button>
|
||||
<sl-button size="large" pill>Center</sl-button>
|
||||
<sl-button size="large" pill>Right</sl-button>
|
||||
</sl-button-group>
|
||||
```
|
||||
|
||||
### Dropdowns in Button Groups
|
||||
|
||||
Dropdowns can be placed inside button groups as long as the trigger is a `<sl-dropdown>` element.
|
||||
|
||||
```html preview
|
||||
<sl-button-group>
|
||||
<sl-button>Button</sl-button>
|
||||
<sl-button>Button</sl-button>
|
||||
|
||||
<sl-dropdown>
|
||||
<sl-button slot="trigger" caret>Dropdown</sl-button>
|
||||
<sl-menu>
|
||||
<sl-menu-item>Item 1</sl-menu-item>
|
||||
<sl-menu-item>Item 2</sl-menu-item>
|
||||
<sl-menu-item>Item 3</sl-menu-item>
|
||||
</sl-menu>
|
||||
</sl-dropdown>
|
||||
|
||||
</sl-button-group>
|
||||
```
|
||||
|
||||
### Tooltips in Button Groups
|
||||
|
||||
Buttons can be wrapped in tooltips to provide more detail when the user interacts with them.
|
||||
|
||||
```html preview
|
||||
<sl-button-group>
|
||||
<sl-tooltip content="I'm on the left">
|
||||
<sl-button>Left</sl-button>
|
||||
</sl-tooltip>
|
||||
|
||||
<sl-tooltip content="I'm in the middle">
|
||||
<sl-button>Center</sl-button>
|
||||
</sl-tooltip>
|
||||
|
||||
<sl-tooltip content="I'm on the right">
|
||||
<sl-button>Right</sl-button>
|
||||
</sl-tooltip>
|
||||
</sl-button-group>
|
||||
```
|
||||
|
||||
### Toolbar Example
|
||||
|
||||
Create interactive toolbars with button groups.
|
||||
|
||||
```html preview
|
||||
<div class="button-group-toolbar">
|
||||
<sl-button-group label="History">
|
||||
<sl-tooltip content="Undo">
|
||||
<sl-button><sl-icon name="arrow-counterclockwise"></sl-icon></sl-button>
|
||||
</sl-tooltip>
|
||||
<sl-tooltip content="Redo">
|
||||
<sl-button><sl-icon name="arrow-clockwise"></sl-icon></sl-button>
|
||||
</sl-tooltip>
|
||||
</sl-button-group>
|
||||
|
||||
<sl-button-group label="Formatting">
|
||||
<sl-tooltip content="Bold">
|
||||
<sl-button><sl-icon name="type-bold"></sl-icon></sl-button>
|
||||
</sl-tooltip>
|
||||
<sl-tooltip content="Italic">
|
||||
<sl-button><sl-icon name="type-italic"></sl-icon></sl-button>
|
||||
</sl-tooltip>
|
||||
<sl-tooltip content="Underline">
|
||||
<sl-button><sl-icon name="type-underline"></sl-icon></sl-button>
|
||||
</sl-tooltip>
|
||||
</sl-button-group>
|
||||
|
||||
<sl-button-group label="Alignment">
|
||||
<sl-tooltip content="Align Left">
|
||||
<sl-button><sl-icon name="justify-left"></sl-icon></sl-button>
|
||||
</sl-tooltip>
|
||||
<sl-tooltip content="Align Center">
|
||||
<sl-button><sl-icon name="justify"></sl-icon></sl-button>
|
||||
</sl-tooltip>
|
||||
<sl-tooltip content="Align Right">
|
||||
<sl-button><sl-icon name="justify-right"></sl-icon></sl-button>
|
||||
</sl-tooltip>
|
||||
</sl-button-group>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.button-group-toolbar sl-button-group:not(:last-of-type) {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
[component-metadata:sl-button-group]
|
|
@ -110,6 +110,12 @@ export namespace Components {
|
|||
*/
|
||||
"value": string;
|
||||
}
|
||||
interface SlButtonGroup {
|
||||
/**
|
||||
* A label to use for the button groups `aria-label` attribute.
|
||||
*/
|
||||
"label": string;
|
||||
}
|
||||
interface SlCard {
|
||||
}
|
||||
interface SlCheckbox {
|
||||
|
@ -930,6 +936,12 @@ declare global {
|
|||
prototype: HTMLSlButtonElement;
|
||||
new (): HTMLSlButtonElement;
|
||||
};
|
||||
interface HTMLSlButtonGroupElement extends Components.SlButtonGroup, HTMLStencilElement {
|
||||
}
|
||||
var HTMLSlButtonGroupElement: {
|
||||
prototype: HTMLSlButtonGroupElement;
|
||||
new (): HTMLSlButtonGroupElement;
|
||||
};
|
||||
interface HTMLSlCardElement extends Components.SlCard, HTMLStencilElement {
|
||||
}
|
||||
var HTMLSlCardElement: {
|
||||
|
@ -1115,6 +1127,7 @@ declare global {
|
|||
"sl-avatar": HTMLSlAvatarElement;
|
||||
"sl-badge": HTMLSlBadgeElement;
|
||||
"sl-button": HTMLSlButtonElement;
|
||||
"sl-button-group": HTMLSlButtonGroupElement;
|
||||
"sl-card": HTMLSlCardElement;
|
||||
"sl-checkbox": HTMLSlCheckboxElement;
|
||||
"sl-color-picker": HTMLSlColorPickerElement;
|
||||
|
@ -1260,6 +1273,12 @@ declare namespace LocalJSX {
|
|||
*/
|
||||
"value"?: string;
|
||||
}
|
||||
interface SlButtonGroup {
|
||||
/**
|
||||
* A label to use for the button groups `aria-label` attribute.
|
||||
*/
|
||||
"label"?: string;
|
||||
}
|
||||
interface SlCard {
|
||||
}
|
||||
interface SlCheckbox {
|
||||
|
@ -2143,6 +2162,7 @@ declare namespace LocalJSX {
|
|||
"sl-avatar": SlAvatar;
|
||||
"sl-badge": SlBadge;
|
||||
"sl-button": SlButton;
|
||||
"sl-button-group": SlButtonGroup;
|
||||
"sl-card": SlCard;
|
||||
"sl-checkbox": SlCheckbox;
|
||||
"sl-color-picker": SlColorPicker;
|
||||
|
@ -2183,6 +2203,7 @@ declare module "@stencil/core" {
|
|||
"sl-avatar": LocalJSX.SlAvatar & JSXBase.HTMLAttributes<HTMLSlAvatarElement>;
|
||||
"sl-badge": LocalJSX.SlBadge & JSXBase.HTMLAttributes<HTMLSlBadgeElement>;
|
||||
"sl-button": LocalJSX.SlButton & JSXBase.HTMLAttributes<HTMLSlButtonElement>;
|
||||
"sl-button-group": LocalJSX.SlButtonGroup & JSXBase.HTMLAttributes<HTMLSlButtonGroupElement>;
|
||||
"sl-card": LocalJSX.SlCard & JSXBase.HTMLAttributes<HTMLSlCardElement>;
|
||||
"sl-checkbox": LocalJSX.SlCheckbox & JSXBase.HTMLAttributes<HTMLSlCheckboxElement>;
|
||||
"sl-color-picker": LocalJSX.SlColorPicker & JSXBase.HTMLAttributes<HTMLSlColorPickerElement>;
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// 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));
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
@import 'component';
|
||||
|
||||
:host {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
::slotted(.sl-hover) {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
::slotted(.sl-focus) {
|
||||
z-index: 2;
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
import { Component, Prop, h } from '@stencil/core';
|
||||
|
||||
/**
|
||||
* @since 2.0
|
||||
* @status stable
|
||||
*
|
||||
* @slot - One or more `<sl-button>` elements to display in the button group.
|
||||
*
|
||||
* @part base - The component's base wrapper.
|
||||
*/
|
||||
|
||||
@Component({
|
||||
tag: 'sl-button-group',
|
||||
styleUrl: 'button-group.scss',
|
||||
shadow: true
|
||||
})
|
||||
export class ButtonGroup {
|
||||
buttonGroup: HTMLElement;
|
||||
|
||||
/** A label to use for the button groups `aria-label` attribute. */
|
||||
@Prop() label = '';
|
||||
|
||||
connectedCallback() {
|
||||
this.handleFocus = this.handleFocus.bind(this);
|
||||
this.handleBlur = this.handleBlur.bind(this);
|
||||
}
|
||||
|
||||
componentDidLoad() {
|
||||
this.buttonGroup.addEventListener('slFocus', this.handleFocus);
|
||||
this.buttonGroup.addEventListener('slBlur', this.handleBlur);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this.buttonGroup.removeEventListener('slFocus', this.handleFocus);
|
||||
this.buttonGroup.removeEventListener('slBlur', this.handleBlur);
|
||||
}
|
||||
|
||||
handleFocus(event: CustomEvent) {
|
||||
const button = event.target as HTMLElement;
|
||||
button.classList.add('sl-focus');
|
||||
}
|
||||
|
||||
handleBlur(event: CustomEvent) {
|
||||
const button = event.target as HTMLElement;
|
||||
button.classList.remove('sl-focus');
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div ref={el => (this.buttonGroup = el)} part="base" class="button-group" aria-label={this.label}>
|
||||
<slot />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -265,3 +265,9 @@
|
|||
.sl-scroll-lock {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Component light DOM styles - only follow this pattern when absolutely necessary!
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@import '../components/button-group/button-group.light-dom';
|
||||
|
|
Ładowanie…
Reference in New Issue