From af0ade31fd878803fec8fbe8edc812a8befbb159 Mon Sep 17 00:00:00 2001 From: Cory LaViska Date: Thu, 17 Sep 2020 16:27:11 -0400 Subject: [PATCH] Use toast() method instead of prop --- docs/components/alert.md | 217 +++++++++++----------- src/components.d.ts | 8 +- src/components/alert/alert.light-dom.scss | 6 + src/components/alert/alert.scss | 11 -- src/components/alert/alert.tsx | 68 ++++--- 5 files changed, 152 insertions(+), 158 deletions(-) diff --git a/docs/components/alert.md b/docs/components/alert.md index 15d213b4c..d9660c3ce 100644 --- a/docs/components/alert.md +++ b/docs/components/alert.md @@ -85,118 +85,15 @@ Icons are optional. Simply omit the `icon` slot if you don't want them. ``` -### Toast Notifications - -When the `toast` prop is used, the alert will be displayed as a toast notification. To facilitate this, the alert is appended to the toast stack the first time it is shown and removed from the DOM when dismissed. By storing a reference to the alert, you can use it again later as shown in this example. - -```html preview -
- Primary - Success - Info - Warning - Danger - - - - This is super informative
- You can tell by how pretty the alert is. -
- - - - Your changes have been saved
- You can safely exit the app now. -
- - - - Your settings have been updated
- Settings will take affect on next login. -
- - - - Your session has ended
- Please login again to continue. -
- - - - Your account has been deleted
- We're very sorry to see you go! -
-
- - -``` - -The toast stack is a fixed position singleton element created and managed internally by the alert component. It will be added and removed from the DOM as needed when toast alerts are shown. By default, the toast stack is positioned at the top-right of the viewport. When more than one alert is visible, they will stack vertically. - -You can change the toast stack's position and behavior in your stylesheet. To make toasts appear at the top-left of the viewport, for example, add the following to your stylesheet. - -```css -.sl-toast-stack { - left: 0; - right: auto; -} -``` - -?> By design, toasts cannot be shown in more than one stack. That would be distracting and confusing to users, which makes for a poor experience. - -### Creating Toasts Dynamically - -Toast alerts can be created declaratively, but you can create them imperatively as well. Just make sure to append them to the DOM before calling `show()`. - -```html preview -
- Create New Toast -
- - -``` - ### Duration -Set the `duration` prop to automatically hide an alert after a period of time. This is useful for alerts that don't require user acknowledgement and works especially well with the `toast` prop. +Set the `duration` prop to automatically hide an alert after a period of time. This is useful for alerts that don't require acknowledgement. ```html preview
Show Alert - + This alert will automatically hide itself after three seconds, unless you interact with it. @@ -211,4 +108,114 @@ Set the `duration` prop to automatically hide an alert after a period of time. T ``` +### Toast Notifications + +To display an alert as a toast notification, call its `toast()` method. This will move the alert out of its position in the DOM and into the [toast stack](#toast-stack) where it will be shown. Once dismissed, it will be removed from the DOM completely. To reuse an alert, store a reference to it and call `toast()` again the next time you want it to appear. + +You should always use the `closable` prop so users can dismiss the notification. It's also common to set a reasonable `duration` when the notification doesn't require acknowledgement. + +```html preview +
+ Primary + Success + Info + Warning + Danger + + + + This is super informative
+ You can tell by how pretty the alert is. +
+ + + + Your changes have been saved
+ You can safely exit the app now. +
+ + + + Your settings have been updated
+ Settings will take affect on next login. +
+ + + + Your session has ended
+ Please login again to continue. +
+ + + + Your account has been deleted
+ We're very sorry to see you go! +
+
+ + +``` + +For convenience, you can create a utility that emits toast notifications with a single function call. To do this, generate the alert dynamically, append it to the body, and call its `toast()` method as shown in the example below. + +```html preview +
+ Create Toast +
+ + +``` + +### Toast Stack + +The toast stack is a fixed position singleton element created and managed by the alert component. It will be added and removed from the DOM as needed when toast alerts are shown. By default, the toast stack is positioned at the top-right of the viewport. When more than one alert is visible, they will stack vertically. + +You can change the toast stack's position and behavior in your stylesheet. To make toasts appear at the top-left of the viewport, for example, add the following CSS. + +```css +.sl-toast-stack { + left: 0; + right: auto; +} +``` + +?> By design, toasts cannot be shown in more than one stack. This makes for a poor user experience, as it distracts and confuses users. + [component-metadata:sl-alert] diff --git a/src/components.d.ts b/src/components.d.ts index 449b32bdd..de18ceba1 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -28,9 +28,9 @@ export namespace Components { */ "show": () => Promise; /** - * When true, the alert will be shown as a toast notification. To facilitate this, the alert is appended to the toast stack the first time it is shown and removed from the DOM when dismissed. + * Displays the alert as a toast notification. This will move the alert out of its position in the DOM and, when dismissed, it will be removed from the DOM completely. By storing a reference to the alert, you can reuse it by calling this method again. The returned promise resolves when the alert is hidden. */ - "toast": boolean; + "toast": () => Promise; /** * The type of alert. */ @@ -1450,10 +1450,6 @@ declare namespace LocalJSX { * Indicates whether or not the alert is open. You can use this in lieu of the show/hide methods. */ "open"?: boolean; - /** - * When true, the alert will be shown as a toast notification. To facilitate this, the alert is appended to the toast stack the first time it is shown and removed from the DOM when dismissed. - */ - "toast"?: boolean; /** * The type of alert. */ diff --git a/src/components/alert/alert.light-dom.scss b/src/components/alert/alert.light-dom.scss index 8f4a88c98..93921af44 100644 --- a/src/components/alert/alert.light-dom.scss +++ b/src/components/alert/alert.light-dom.scss @@ -3,7 +3,13 @@ top: 0; right: 0; z-index: var(--sl-z-index-toast); + width: 28rem; max-width: 100%; max-height: 100%; overflow: auto; + + sl-alert { + --box-shadow: var(--sl-shadow-large); + margin: var(--sl-spacing-medium); + } } diff --git a/src/components/alert/alert.scss b/src/components/alert/alert.scss index fa86bad00..d060ef768 100644 --- a/src/components/alert/alert.scss +++ b/src/components/alert/alert.scss @@ -2,13 +2,9 @@ /** * @prop --box-shadow: The alert's box shadow. - * @prop --toast-spacing: The spacing to use when the alert is shown as a toast notification. - * @prop --toast-width: The width of the alert when shown as a toast notification. */ :host { --box-shadow: none; - --toast-spacing: var(--sl-spacing-medium); - --toast-width: 28rem; display: block; @@ -36,13 +32,6 @@ transition: var(--sl-transition-medium) opacity ease, var(--sl-transition-medium) transform ease; } -.alert--toast { - width: var(--toast-width); - max-width: calc(100% - var(--toast-spacing) * 2); - box-shadow: var(--sl-shadow-large); - margin: var(--toast-spacing); -} - .alert--open { opacity: 1; transform: scale(1); diff --git a/src/components/alert/alert.tsx b/src/components/alert/alert.tsx index 95984dbf4..1e41483d4 100644 --- a/src/components/alert/alert.tsx +++ b/src/components/alert/alert.tsx @@ -36,12 +36,6 @@ export class Alert { /** The type of alert. */ @Prop({ reflect: true }) type: 'primary' | 'success' | 'info' | 'warning' | 'danger' = 'primary'; - /** - * When true, the alert will be shown as a toast notification. To facilitate this, the alert is appended to the toast - * stack the first time it is shown and removed from the DOM when dismissed. - */ - @Prop({ reflect: true }) toast = false; - /** * The length of time, in milliseconds, the alert will show before closing itself. If the user interacts with the * alert before it closes (e.g. moves the mouse over it), the duration will restart. @@ -91,16 +85,9 @@ export class Alert { return; } - if (this.toast) { - this.appendToStack(); - } - const slShow = this.slShow.emit(); if (slShow.defaultPrevented) { this.open = false; - if (this.toast) { - this.removeFromStack(); - } return; } @@ -133,6 +120,38 @@ export class Alert { this.open = false; } + /** + * Displays the alert as a toast notification. This will move the alert out of its position in the DOM and, when + * dismissed, it will be removed from the DOM completely. By storing a reference to the alert, you can reuse it by + * calling this method again. The returned promise resolves when the alert is hidden. + */ + @Method() + async toast() { + return new Promise(resolve => { + if (!stack.parentElement) { + document.body.append(stack); + } + + stack.clientWidth; // force a reflow + stack.append(this.host); + this.show(); + + this.host.addEventListener( + 'slAfterHide', + () => { + this.host.remove(); + resolve(); + + // Remove the stack from the DOM when there are no more alerts + if (stack.querySelector('sl-alert') === null) { + stack.remove(); + } + }, + { once: true } + ); + }); + } + handleCloseClick() { this.hide(); } @@ -148,28 +167,6 @@ export class Alert { if (event.propertyName === 'opacity' && target.classList.contains('alert')) { this.host.hidden = !this.open; this.open ? this.slAfterShow.emit() : this.slAfterHide.emit(); - - if (this.toast && !this.open) { - this.removeFromStack(); - } - } - } - - appendToStack() { - if (!stack.parentElement) { - document.body.append(stack); - } - - stack.clientWidth; // force a reflow - stack.append(this.host); - } - - removeFromStack() { - this.host.remove(); - - // Remove the stack from the DOM when there are no more alerts - if (stack.querySelector('sl-alert') === null) { - stack.remove(); } } @@ -190,7 +187,6 @@ export class Alert { alert: true, 'alert--open': this.open, 'alert--closable': this.closable, - 'alert--toast': this.toast, 'alert--primary': this.type === 'primary', 'alert--success': this.type === 'success', 'alert--info': this.type === 'info',