Light DOM variation

pull/224/head
Cory LaViska 2020-09-16 09:34:54 -04:00
rodzic 1546b53d40
commit ad9de54752
7 zmienionych plików z 206 dodań i 8 usunięć

Wyświetl plik

@ -4,7 +4,71 @@
Alerts are used to display important messages.
Alerts are designed to be shown dynamically, so you need to include the `open` attribute to display them.
Alerts are designed to be shown dynamically, so you must include the `open` attribute to display them.
```html preview
<div class="toast-example">
<sl-button type="primary">Primary</sl-button>
<sl-button type="success">Success</sl-button>
<sl-button type="info">Info</sl-button>
<sl-button type="warning">Warning</sl-button>
<sl-button type="danger">Danger</sl-button>
<sl-alert type="primary" placement="top-end" duration="3000" closable>
<sl-icon slot="icon" name="info-circle"></sl-icon>
<strong>This is super informative</strong><br>
You can tell by how pretty the alert is.
</sl-alert>
<sl-alert type="success" placement="top-end" duration="3000" closable>
<sl-icon slot="icon" name="check2-circle"></sl-icon>
<strong>Your changes have been saved</strong><br>
You can safely exit the app now.
</sl-alert>
<sl-alert type="info" placement="top-end" duration="3000" closable>
<sl-icon slot="icon" name="gear"></sl-icon>
<strong>Your settings have been updated</strong><br>
Some settings will take affect the next time you log in.
</sl-alert>
<sl-alert type="warning" placement="top-end" duration="3000" closable>
<sl-icon slot="icon" name="exclamation-triangle"></sl-icon>
<strong>Your session has ended</strong><br>
Please login again to continue.
</sl-alert>
<sl-alert type="danger" placement="bottom" duration="3000" closable>
<sl-icon slot="icon" name="exclamation-octagon"></sl-icon>
<strong>Your account has been deleted</strong><br>
We're very sorry to see you go!
</sl-alert>
</div>
<script>
const container = document.querySelector('.toast-example');
['primary', 'success', 'info', 'warning', 'danger'].map(type => {
const button = container.querySelector(`sl-button[type="${type}"]`);
const alert = container.querySelector(`sl-alert[type="${type}"]`);
button.addEventListener('click', () => alert.show());
});
</script>
```
```html preview
<sl-alert open>
@ -46,16 +110,16 @@ Set the `type` attribute to change the alert's type.
<sl-alert type="warning" open>
<sl-icon slot="icon" name="exclamation-triangle"></sl-icon>
<strong>This will end your session</strong><br>
You will be logged out until you log in again.
<strong>Your session has ended</strong><br>
Please login again to continue.
</sl-alert>
<br>
<sl-alert type="danger" open>
<sl-icon slot="icon" name="exclamation-octagon"></sl-icon>
<strong>Delete this file?</strong><br>
This is permanent, which means forever!
<strong>Your account has been deleted</strong><br>
We're very sorry to see you go!
</sl-alert>
```

Wyświetl plik

@ -7,4 +7,5 @@ Z-indexes are used to stack components in a logical manner.
| `--sl-z-index-drawer` | 700 |
| `--sl-z-index-dialog` | 800 |
| `--sl-z-index-dropdown` | 900 |
| `--sl-z-index-alert-group` | 950 |
| `--sl-z-index-tooltip` | 1000 |

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

@ -11,6 +11,10 @@ export namespace Components {
* Set to true to make the alert closable.
*/
"closable": boolean;
/**
* The length of time, in milliseconds, the alert will show before closing itself.
*/
"duration": number;
/**
* Hides the alert
*/
@ -19,6 +23,10 @@ export namespace Components {
* Indicates whether or not the alert is open. You can use this in lieu of the show/hide methods.
*/
"open": boolean;
/**
* Determines how the alert will be shown. If this is anything other than `inline`, the alert will be shown in a stack as a "toast" notification. When the alert is shown as a notification, it will be hoisted to a stack and removed from the DOM when hidden. (You can reuse alerts that have been removed by storing a reference to the element.)
*/
"placement": 'inline' | 'top-start' | 'top' | 'top-end' | 'bottom-start' | 'bottom' | 'bottom-end';
/**
* Shows the alert.
*/
@ -1418,6 +1426,10 @@ declare namespace LocalJSX {
* Set to true to make the alert closable.
*/
"closable"?: boolean;
/**
* The length of time, in milliseconds, the alert will show before closing itself.
*/
"duration"?: number;
/**
* Emitted after the alert closes and all transitions are complete.
*/
@ -1438,6 +1450,10 @@ declare namespace LocalJSX {
* Indicates whether or not the alert is open. You can use this in lieu of the show/hide methods.
*/
"open"?: boolean;
/**
* Determines how the alert will be shown. If this is anything other than `inline`, the alert will be shown in a stack as a "toast" notification. When the alert is shown as a notification, it will be hoisted to a stack and removed from the DOM when hidden. (You can reuse alerts that have been removed by storing a reference to the element.)
*/
"placement"?: 'inline' | 'top-start' | 'top' | 'top-end' | 'bottom-start' | 'bottom' | 'bottom-end';
/**
* The type of alert.
*/

Wyświetl plik

@ -0,0 +1,49 @@
:root {
--width: 28rem;
--spacing: var(--sl-spacing-medium);
}
.sl-alert-stack {
position: fixed;
z-index: var(--sl-z-index-toast);
width: var(--width);
max-width: 100%;
max-height: 100%;
overflow: auto;
padding: 0 var(--spacing);
sl-alert {
--box-shadow: var(--sl-shadow-large);
margin: var(--spacing) 0;
}
}
.sl-alert-stack[data-placement='top-start'] {
top: 0;
left: 0;
}
.sl-alert-stack[data-placement='top'] {
top: 0;
left: calc(50% - var(--width) / 2);
}
.sl-alert-stack[data-placement='top-end'] {
top: 0;
right: 0;
}
.sl-alert-stack[data-placement='bottom-start'] {
bottom: 0;
left: 0;
}
.sl-alert-stack[data-placement='bottom'] {
bottom: 0;
left: calc(50% - var(--width) / 2);
}
.sl-alert-stack[data-placement='bottom-end'] {
bottom: 0;
right: 0;
}

Wyświetl plik

@ -1,6 +1,11 @@
@import 'component';
/**
* @prop --box-shadow: The alert's box shadow.
*/
:host {
--box-shadow: none;
display: block;
&[hidden] {
@ -16,17 +21,20 @@
border: solid 1px var(--sl-color-gray-90);
border-top-width: 3px;
border-radius: var(--sl-border-radius-medium);
box-shadow: var(--box-shadow);
font-family: var(--sl-font-sans);
font-size: var(--sl-font-size-small);
font-weight: var(--sl-font-weight-normal);
line-height: 1.6;
color: var(--sl-color-gray-30);
opacity: 0;
transition: var(--sl-transition-medium) opacity ease;
transform: scale(0.9);
transition: var(--sl-transition-medium) opacity ease, var(--sl-transition-medium) transform ease;
}
.alert--open {
opacity: 1;
transform: scale(1);
}
.alert__icon {
@ -91,5 +99,5 @@
display: flex;
align-items: center;
font-size: var(--sl-font-size-large);
padding: 0 var(--sl-spacing-medium);
padding-right: var(--sl-spacing-medium);
}

Wyświetl plik

@ -13,6 +13,8 @@ import { Component, Element, Event, EventEmitter, Host, Method, Prop, Watch, h }
* @part close-button - The close button.
*/
const stack = Object.assign(document.createElement('div'), { className: 'sl-alert-stack' });
@Component({
tag: 'sl-alert',
styleUrl: 'alert.scss',
@ -20,6 +22,7 @@ import { Component, Element, Event, EventEmitter, Host, Method, Prop, Watch, h }
})
export class Alert {
alert: HTMLElement;
autoHideTimeout: any;
isShowing = false;
@Element() host: HTMLSlAlertElement;
@ -33,11 +36,31 @@ export class Alert {
/** The type of alert. */
@Prop() type: 'primary' | 'success' | 'info' | 'warning' | 'danger' = 'primary';
/**
* Determines how the alert will be shown. If this is anything other than `inline`, the alert will be shown in a stack
* as a "toast" notification. When the alert is shown as a notification, it will be hoisted to a stack and removed
* from the DOM when hidden. (You can reuse alerts that have been removed by storing a reference to the element.)
*/
@Prop() placement: 'inline' | 'top-start' | 'top' | 'top-end' | 'bottom-start' | 'bottom' | 'bottom-end' = 'inline';
/** The length of time, in milliseconds, the alert will show before closing itself. */
@Prop() duration = Infinity;
@Watch('open')
handleOpenChange() {
this.open ? this.show() : this.hide();
}
@Watch('duration')
handleDurationChange() {
clearTimeout(this.autoHideTimeout);
// Restart the timeout if the duration changes and the alert is open
if (this.open && this.duration < Infinity) {
this.autoHideTimeout = setTimeout(() => this.hide(), this.duration);
}
}
/** Emitted when the alert opens. Calling `event.preventDefault()` will prevent it from being opened. */
@Event() slShow: EventEmitter;
@ -80,6 +103,14 @@ export class Alert {
this.host.clientWidth; // force a reflow
this.isShowing = true;
this.open = true;
if (this.placement !== 'inline') {
this.appendToStack();
}
if (this.duration < Infinity) {
this.autoHideTimeout = setTimeout(() => this.hide(), this.duration);
}
}
/** Hides the alert */
@ -96,6 +127,7 @@ export class Alert {
return;
}
clearTimeout(this.autoHideTimeout);
this.isShowing = false;
this.open = false;
}
@ -110,10 +142,34 @@ export class Alert {
// Ensure we only emit one event when the target element is no longer visible
if (event.propertyName === 'opacity' && target.classList.contains('alert')) {
this.host.hidden = !this.open;
if (this.placement !== 'inline' && !this.open) {
this.removeFromStack();
}
this.open ? this.slAfterShow.emit() : this.slAfterHide.emit();
}
}
appendToStack() {
if (!stack.parentElement) {
document.body.append(stack);
}
stack.dataset.placement = this.placement;
stack.append(this.host);
}
removeFromStack() {
this.host.remove();
// Remove the stack from the DOM when there are no more alerts
const openAlerts = [...stack.querySelectorAll('sl-alert')].filter((el: HTMLSlAlertElement) => el.open === true);
if (openAlerts.length === 0) {
stack.remove();
}
}
render() {
return (
<Host hidden>
@ -145,7 +201,9 @@ export class Alert {
</span>
{this.closable && (
<sl-icon-button part="close-button" class="alert__close" name="x" onClick={this.handleCloseClick} />
<span class="alert__close">
<sl-icon-button part="close-button" name="x" onClick={this.handleCloseClick} />
</span>
)}
</div>
</Host>

Wyświetl plik

@ -249,6 +249,7 @@
--sl-z-index-drawer: 700;
--sl-z-index-dialog: 800;
--sl-z-index-dropdown: 900;
--sl-z-index-toast: 950;
--sl-z-index-tooltip: 1000;
}
@ -268,4 +269,5 @@
// Component light DOM styles - only follow this pattern when absolutely necessary!
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@import '../components/alert/alert.light-dom';
@import '../components/button-group/button-group.light-dom';