feat: add snackbar alert with refresh button (#1193)

* feat: add snackbar alert with refresh button

fixes #77

* fixup

* change refresh to reload
This commit is contained in:
Nolan Lawson 2019-05-08 07:52:12 -07:00 committed by GitHub
parent c56d561e9d
commit 0887196db4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 173 additions and 2 deletions

View file

@ -45,6 +45,9 @@
<!-- Toast.html gets rendered here --> <!-- Toast.html gets rendered here -->
<div id="theToast"></div> <div id="theToast"></div>
<!-- Snackbar.html gets rendered here -->
<div id="theSnackbar"></div>
<!-- LoadingMask.html gets rendered here --> <!-- LoadingMask.html gets rendered here -->
<div id="loading-mask" aria-hidden="true"></div> <div id="loading-mask" aria-hidden="true"></div>

View file

@ -0,0 +1,137 @@
<section class="snackbar-modal {shown ? 'shown' : ''}"
aria-live="assertive"
aria-atomic="true"
aria-hidden={!shown}
aria-label="Alert"
>
<div class="snackbar-container">
<span class="text">
{text}
</span>
<div class="button-wrapper">
<button class="button" on:click="onClick(event)">
{buttonText}
</button>
<button class="button" aria-label="Close" on:click="close(event)">
<SvgIcon className="close-snackbar-button" href="#fa-times" />
</button>
</div>
</div>
</section>
<style>
.snackbar-modal {
position: fixed;
bottom: 0;
left: 0;
right: 0;
transition: transform 333ms ease-in-out;
display: flex;
flex-direction: column;
align-items: center;
z-index: 99000;
transform: translateY(100%);
}
.snackbar-container {
width: 562px; /* same as .main, minus 20px padding */
overflow: hidden;
display: flex;
align-items: center;
background: var(--toast-bg);
padding: 10px 20px;
font-size: 1.3em;
color: var(--toast-text);
border-radius: 4px 4px 0 0;
border: 1px solid var(--toast-border);
border-bottom: none;
}
.button-wrapper {
display: flex;
align-items: center;
}
.text {
flex: 1;
}
button {
font-size: 1em;
border: none;
color: var(--toast-anchor-color);
text-transform: uppercase;
font-weight: 500;
background: var(--toast-bg);
margin-left: 5px;
}
button:active {
background: var(--toast-button-active);
}
button:hover {
background: var(--toast-button-hover);
}
.snackbar-modal.shown {
transform: translateY(0);
}
:global(.close-snackbar-button) {
margin-top: 3px;
width: 18px;
height: 18px;
fill: var(--toast-text);
}
@media (max-width: 767px) {
.snackbar-container {
width: 100%;
}
}
</style>
<script>
import { doubleRAF } from '../../_utils/doubleRAF'
import SvgIcon from '../SvgIcon.html'
export default {
data: () => ({
shown: false,
text: '',
buttonText: '',
buttonAction: null
}),
methods: {
announce (text, buttonText, buttonAction) {
this.set({ text, buttonText, buttonAction })
doubleRAF(() => {
this.set({ shown: true })
})
},
onClick (e) {
e.preventDefault()
e.stopPropagation()
let { buttonAction } = this.get()
if (buttonAction) {
buttonAction()
}
this.close()
},
close (e) {
if (e) {
e.preventDefault()
e.stopPropagation()
}
requestAnimationFrame(() => {
this.set({
buttonAction: null, // avoid memory leaks from the closure
shown: false
})
})
}
},
components: {
SvgIcon
}
}
</script>

View file

@ -0,0 +1,22 @@
import { importSnackbar } from '../../_utils/asyncModules'
let snackbar
const lazySnackbar = {
async announce (text, buttonText, buttonAction) {
if (!snackbar) {
let Snackbar = await importSnackbar()
if (!snackbar) {
snackbar = new Snackbar({
target: document.querySelector('#theSnackbar')
})
if (process.env.NODE_ENV !== 'production') {
window.snackbar = snackbar // for debugging
}
}
}
snackbar.announce(text, buttonText, buttonAction)
}
}
export { lazySnackbar as snackbar }

View file

@ -47,3 +47,7 @@ export const importEmojiMart = () => import(
export const importToast = () => import( export const importToast = () => import(
/* webpackChunkName: 'Toast.html' */ '../_components/toast/Toast.html' /* webpackChunkName: 'Toast.html' */ '../_components/toast/Toast.html'
).then(getDefault) ).then(getDefault)
export const importSnackbar = () => import(
/* webpackChunkName: 'Snackbar.html' */ '../_components/snackbar/Snackbar.html'
).then(getDefault)

View file

@ -1,11 +1,11 @@
import { toast } from '../_components/toast/toast' import { snackbar } from '../_components/snackbar/snackbar'
function onUpdateFound (registration) { function onUpdateFound (registration) {
const newWorker = registration.installing const newWorker = registration.installing
newWorker.addEventListener('statechange', async () => { newWorker.addEventListener('statechange', async () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
toast.say('App update available. Reload to update.') snackbar.announce('App update available.', 'Reload', () => document.location.reload(true))
} }
}) })
} }

View file

@ -66,6 +66,9 @@
--toast-bg: #{$toast-bg}; --toast-bg: #{$toast-bg};
--toast-border: #{$toast-border}; --toast-border: #{$toast-border};
--toast-text: #{$secondary-text-color}; --toast-text: #{$secondary-text-color};
--toast-button-hover: #{lighten($toast-bg, 5%)};
--toast-button-active: #{lighten($toast-bg, 10%)};
--toast-anchor-color: #{lighten($anchor-color, 20%)};
--mask-bg: #{$toast-bg}; --mask-bg: #{$toast-bg};
--mask-svg-fill: #{$secondary-text-color}; --mask-svg-fill: #{$secondary-text-color};

View file

@ -44,4 +44,6 @@
--tab-bg-active: #{lighten($main-bg-color, 15%)}; --tab-bg-active: #{lighten($main-bg-color, 15%)};
--tab-bg-hover: #{lighten($main-bg-color, 1%)}; --tab-bg-hover: #{lighten($main-bg-color, 1%)};
--tab-bg-hover-non-selected: #{darken($main-bg-color, 1%)}; --tab-bg-hover-non-selected: #{darken($main-bg-color, 1%)};
--toast-anchor-color: #{$anchor-color};
} }