Add support for link buttons; closes #165

pull/168/head
Cory LaViska 2020-08-07 15:42:55 -04:00
rodzic 883d6974e9
commit 12581ca76b
5 zmienionych plików z 94 dodań i 38 usunięć

Wyświetl plik

@ -2,6 +2,7 @@
## 2.0.0-beta.12
- Added support for `href`, `target`, and `download` to buttons
- Fixed a bug where buttons would have horizontal spacing in Safari
- Fixed a bug that caused an import resolution error when using Shoelace in a Stencil app

Wyświetl plik

@ -63,6 +63,19 @@ Use the `text` type to create text buttons that share the same size as regular b
<sl-button type="text" size="large">Text</sl-button>
```
### Link Buttons
It's often helpful to have a button that works like a link. This is possible by setting the `href` attribute, which will make the component render an `<a>` under the hood. This gives you all the default link behavior the browser provides (e.g. <kbd>CMD/CTRL/SHIFT + CLICK</kbd>) and exposes the `target` and `download` attributes.
```html preview
<sl-button href="https://example.com/">Link</sl-button>
<sl-button href="https://example.com/" target="_blank">New Window</sl-button>
<sl-button href="/assets/images/wordmark.svg" download="shoelace.svg">Download</sl-button>
<sl-button href="https://example.com/" disabled>Disabled</sl-button>
```
?> When a `target` is set, the link will receive `rel="noreferrer noopener"` for [security reasons](https://mathiasbynens.github.io/rel-noopener/).
### Setting a Custom Width
As expected, buttons can be given a custom width by setting its `width`. This is useful for making buttons span the full width of their container on smaller screens.

36
src/components.d.ts vendored
Wyświetl plik

@ -73,12 +73,20 @@ export namespace Components {
* Set to true to disable the button.
*/
"disabled": boolean;
/**
* Tells the browser to download the linked file as this filename. Only used when `href` is set.
*/
"download": string;
/**
* When set, the underlying button will be rendered as an `<a>` with this `href` instead of a `<button>`.
*/
"href": string;
/**
* Set to true to draw the button in a loading state.
*/
"loading": boolean;
/**
* An optional name for the button.
* An optional name for the button. Ignored when `href` is set.
*/
"name": string;
/**
@ -98,15 +106,19 @@ export namespace Components {
*/
"size": 'small' | 'medium' | 'large';
/**
* Indicates if activating the button should submit the form.
* Indicates if activating the button should submit the form. Ignored when `href` is set.
*/
"submit": boolean;
/**
* Tells the browser where to open the link. Only used when `href` is set.
*/
"target": '_blank' | '_parent' | '_self' | '_top';
/**
* The button's type.
*/
"type": 'default' | 'primary' | 'success' | 'info' | 'warning' | 'danger' | 'text';
/**
* An optional value for the button.
* An optional value for the button. Ignored when `href` is set.
*/
"value": string;
}
@ -1236,12 +1248,20 @@ declare namespace LocalJSX {
* Set to true to disable the button.
*/
"disabled"?: boolean;
/**
* Tells the browser to download the linked file as this filename. Only used when `href` is set.
*/
"download"?: string;
/**
* When set, the underlying button will be rendered as an `<a>` with this `href` instead of a `<button>`.
*/
"href"?: string;
/**
* Set to true to draw the button in a loading state.
*/
"loading"?: boolean;
/**
* An optional name for the button.
* An optional name for the button. Ignored when `href` is set.
*/
"name"?: string;
/**
@ -1261,15 +1281,19 @@ declare namespace LocalJSX {
*/
"size"?: 'small' | 'medium' | 'large';
/**
* Indicates if activating the button should submit the form.
* Indicates if activating the button should submit the form. Ignored when `href` is set.
*/
"submit"?: boolean;
/**
* Tells the browser where to open the link. Only used when `href` is set.
*/
"target"?: '_blank' | '_parent' | '_self' | '_top';
/**
* The button's type.
*/
"type"?: 'default' | 'primary' | 'success' | 'info' | 'warning' | 'danger' | 'text';
/**
* An optional value for the button.
* An optional value for the button. Ignored when `href` is set.
*/
"value"?: string;
}

Wyświetl plik

@ -15,6 +15,7 @@
border-width: var(--sl-input-border-width);
font-family: var(--sl-input-font-family);
font-weight: var(--sl-font-weight-semibold);
text-decoration: none;
user-select: none;
white-space: nowrap;
vertical-align: middle;
@ -74,13 +75,13 @@
border-color: var(--sl-color-gray-80);
color: var(--sl-color-gray-40);
&:hover:not(:disabled) {
&:hover:not(.button--disabled) {
background-color: var(--sl-color-primary-95);
border-color: var(--sl-color-primary-80);
color: var(--sl-color-primary-40);
}
&:focus:not(:disabled) {
&:focus:not(.button--disabled) {
background-color: var(--sl-color-primary-95);
border-color: var(--sl-color-primary-70);
color: var(--sl-color-primary-40);
@ -88,7 +89,7 @@
hsla(var(--sl-color-primary-hue), var(--sl-color-primary-saturation), 50%, var(--sl-focus-ring-alpha));
}
&:active:not(:disabled) {
&:active:not(.button--disabled) {
background-color: var(--sl-color-primary-95);
border-color: var(--sl-color-primary-50);
color: var(--sl-color-primary-30);
@ -100,13 +101,13 @@
border-color: var(--sl-color-primary-50);
color: var(--sl-color-primary-text);
&:hover:not(:disabled) {
&:hover:not(.button--disabled) {
background-color: var(--sl-color-primary-60);
border-color: var(--sl-color-primary-60);
color: var(--sl-color-primary-text);
}
&:focus:not(:disabled) {
&:focus:not(.button--disabled) {
background-color: var(--sl-color-primary-60);
border-color: var(--sl-color-primary-60);
color: var(--sl-color-primary-text);
@ -114,7 +115,7 @@
hsla(var(--sl-color-primary-hue), var(--sl-color-primary-saturation), 50%, var(--sl-focus-ring-alpha));
}
&:active:not(:disabled) {
&:active:not(.button--disabled) {
background-color: var(--sl-color-primary-50);
border-color: var(--sl-color-primary-50);
color: var(--sl-color-primary-text);
@ -126,13 +127,13 @@
border-color: var(--sl-color-success-50);
color: var(--sl-color-success-text);
&:hover:not(:disabled) {
&:hover:not(.button--disabled) {
background-color: var(--sl-color-success-60);
border-color: var(--sl-color-success-60);
color: var(--sl-color-success-text);
}
&:focus:not(:disabled) {
&:focus:not(.button--disabled) {
background-color: var(--sl-color-success-60);
border-color: var(--sl-color-success-60);
color: var(--sl-color-success-text);
@ -140,7 +141,7 @@
hsla(var(--sl-color-success-hue), var(--sl-color-success-saturation), 50%, var(--sl-focus-ring-alpha));
}
&:active:not(:disabled) {
&:active:not(.button--disabled) {
background-color: var(--sl-color-success-50);
border-color: var(--sl-color-success-50);
color: var(--sl-color-success-text);
@ -152,13 +153,13 @@
border-color: var(--sl-color-info-50);
color: var(--sl-color-info-text);
&:hover:not(:disabled) {
&:hover:not(.button--disabled) {
background-color: var(--sl-color-info-60);
border-color: var(--sl-color-info-60);
color: var(--sl-color-info-text);
}
&:focus:not(:disabled) {
&:focus:not(.button--disabled) {
background-color: var(--sl-color-info-60);
border-color: var(--sl-color-info-60);
color: var(--sl-color-info-text);
@ -166,7 +167,7 @@
hsla(var(--sl-color-info-hue), var(--sl-color-info-saturation), 50%, var(--sl-focus-ring-alpha));
}
&:active:not(:disabled) {
&:active:not(.button--disabled) {
background-color: var(--sl-color-info-50);
border-color: var(--sl-color-info-50);
color: var(--sl-color-info-text);
@ -178,13 +179,13 @@
border-color: var(--sl-color-warning-50);
color: var(--sl-color-warning-text);
&:hover:not(:disabled) {
&:hover:not(.button--disabled) {
background-color: var(--sl-color-warning-60);
border-color: var(--sl-color-warning-60);
color: var(--sl-color-warning-text);
}
&:focus:not(:disabled) {
&:focus:not(.button--disabled) {
background-color: var(--sl-color-warning-60);
border-color: var(--sl-color-warning-60);
color: var(--sl-color-warning-text);
@ -192,7 +193,7 @@
hsla(var(--sl-color-warning-hue), var(--sl-color-warning-saturation), 50%, var(--sl-focus-ring-alpha));
}
&:active:not(:disabled) {
&:active:not(.button--disabled) {
background-color: var(--sl-color-warning-50);
border-color: var(--sl-color-warning-50);
color: var(--sl-color-warning-text);
@ -204,13 +205,13 @@
border-color: var(--sl-color-danger-50);
color: var(--sl-color-danger-text);
&:hover:not(:disabled) {
&:hover:not(.button--disabled) {
background-color: var(--sl-color-danger-60);
border-color: var(--sl-color-danger-60);
color: var(--sl-color-danger-text);
}
&:focus:not(:disabled) {
&:focus:not(.button--disabled) {
background-color: var(--sl-color-danger-60);
border-color: var(--sl-color-danger-60);
color: var(--sl-color-danger-text);
@ -218,7 +219,7 @@
hsla(var(--sl-color-danger-hue), var(--sl-color-danger-saturation), 50%, var(--sl-focus-ring-alpha));
}
&:active:not(:disabled) {
&:active:not(.button--disabled) {
background-color: var(--sl-color-danger-50);
border-color: var(--sl-color-danger-50);
color: var(--sl-color-danger-text);
@ -235,13 +236,13 @@
border-color: transparent;
color: var(--sl-color-primary-50);
&:hover:not(:disabled) {
&:hover:not(.button--disabled) {
background-color: transparent;
border-color: transparent;
color: var(--sl-color-primary-60);
}
&:focus:not(:disabled) {
&:focus:not(.button--disabled) {
background-color: transparent;
border-color: transparent;
color: var(--sl-color-primary-60);
@ -249,7 +250,7 @@
hsla(var(--sl-color-primary-hue), var(--sl-color-primary-saturation), 50%, var(--sl-focus-ring-alpha));
}
&:active:not(:disabled) {
&:active:not(.button--disabled) {
background-color: transparent;
border-color: transparent;
color: var(--sl-color-primary-40);

Wyświetl plik

@ -40,20 +40,29 @@ export class Button {
/** Set to true to draw the button in a loading state. */
@Prop() loading = false;
/** An optional name for the button. */
@Prop() name: string;
/** Set to true to draw a pill-style button with rounded edges. */
@Prop() pill = false;
/** Set to true to draw a circle button. */
@Prop() circle = false;
/** An optional value for the button. */
/** Indicates if activating the button should submit the form. Ignored when `href` is set. */
@Prop() submit = false;
/** An optional name for the button. Ignored when `href` is set. */
@Prop() name: string;
/** An optional value for the button. Ignored when `href` is set. */
@Prop() value: string;
/** Indicates if activating the button should submit the form. */
@Prop() submit = false;
/** When set, the underlying button will be rendered as an `<a>` with this `href` instead of a `<button>`. */
@Prop() href: string;
/** Tells the browser where to open the link. Only used when `href` is set. */
@Prop() target: '_blank' | '_parent' | '_self' | '_top';
/** Tells the browser to download the linked file as this filename. Only used when `href` is set. */
@Prop() download: string;
/** Emitted when the button loses focus. */
@Event() slBlur: EventEmitter;
@ -97,8 +106,12 @@ export class Button {
}
render() {
const isLink = this.href ? true : false;
const isButton = !isLink;
const Button = isLink ? 'a' : 'button';
return (
<button
<Button
ref={el => (this.button = el)}
part="base"
class={{
@ -126,10 +139,14 @@ export class Button {
'button--loading': this.loading,
'button--pill': this.pill
}}
name={this.name}
value={this.value}
disabled={this.disabled}
type={this.submit ? 'submit' : 'button'}
type={isButton && this.submit ? 'submit' : 'button'}
name={isButton ? this.name : null}
value={isButton ? this.value : null}
href={isLink && this.href}
target={isLink && this.target ? this.target : null}
download={isLink && this.download ? this.download : null}
rel={isLink && this.target ? 'noreferrer noopener' : null}
onBlur={this.handleBlur}
onFocus={this.handleFocus}
onClick={this.handleClick}
@ -159,7 +176,7 @@ export class Button {
)}
{this.loading && <sl-spinner />}
</button>
</Button>
);
}
}