91 lines
2.3 KiB
Svelte
91 lines
2.3 KiB
Svelte
<script>
|
|
import { onMount } from 'svelte';
|
|
import { slide } from 'svelte/transition';
|
|
|
|
let extraClasses = "";
|
|
export {extraClasses as class};
|
|
export let slideDuration = 150;
|
|
let animationClass = "";
|
|
|
|
let error = null;
|
|
|
|
function shake() {
|
|
animationClass = 'shake';
|
|
window.setTimeout(() => animationClass = "", 400);
|
|
}
|
|
|
|
export async function run(fallible) {
|
|
try {
|
|
const ret = await Promise.resolve(fallible());
|
|
error = null;
|
|
return ret;
|
|
}
|
|
catch (e) {
|
|
if (error) shake();
|
|
error = e;
|
|
// re-throw so it can be caught by the caller if necessary
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
// this is a method rather than a prop so that we can re-shake every time
|
|
// the error occurs, even if the error message doesn't change
|
|
export function setError(e) {
|
|
if (error) shake();
|
|
error = e;
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
<style>
|
|
/* animation from https://svelte.dev/repl/e606c27c864045e5a9700691a7417f99?version=3.58.0 */
|
|
@keyframes shake {
|
|
0% {
|
|
transform: translateX(0px);
|
|
}
|
|
20% {
|
|
transform: translateX(10px);
|
|
}
|
|
40% {
|
|
transform: translateX(-10px);
|
|
}
|
|
60% {
|
|
transform: translateX(5px);
|
|
}
|
|
80% {
|
|
transform: translateX(-5px);
|
|
}
|
|
90% {
|
|
transform: translateX(2px);
|
|
}
|
|
95% {
|
|
transform: translateX(-2px);
|
|
}
|
|
100% {
|
|
transform: translateX(0px);
|
|
}
|
|
}
|
|
.shake {
|
|
animation-name: shake;
|
|
animation-play-state: running;
|
|
animation-duration: 0.4s;
|
|
}
|
|
</style>
|
|
|
|
|
|
{#if error}
|
|
<div transition:slide="{{duration: slideDuration}}" class="alert alert-error shadow-lg {animationClass} {extraClasses}">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
|
|
<span>
|
|
<slot {error}>{error.msg || error}</slot>
|
|
</span>
|
|
|
|
{#if $$slots.buttons}
|
|
<div>
|
|
<slot name="buttons"></slot>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
{/if}
|