diff options
Diffstat (limited to 'apps/kit/src/lib/components/alert.svelte')
| -rw-r--r-- | apps/kit/src/lib/components/alert.svelte | 188 |
1 files changed, 131 insertions, 57 deletions
diff --git a/apps/kit/src/lib/components/alert.svelte b/apps/kit/src/lib/components/alert.svelte index 4a5c7ea..87962cf 100644 --- a/apps/kit/src/lib/components/alert.svelte +++ b/apps/kit/src/lib/components/alert.svelte @@ -1,7 +1,15 @@ <script lang="ts"> - import { random_string } from "$shared/lib/helpers"; - import { afterUpdate, onMount } from "svelte"; + import { random_string } from "$lib/helpers"; + import { createEventDispatcher } from "svelte"; + import { onMount } from "svelte"; import { Temporal } from "temporal-polyfill"; + import { + ExclamationTriangle, + CheckCircle, + InformationCircle, + XCircle, + XMark, + } from "./icons"; const noCooldownSetting = "no-cooldown"; // if no unique id is supplied, cooldown will not work between page loads. @@ -9,12 +17,48 @@ export let id = "alert--" + noCooldownSetting + "--" + random_string(4); export let title = ""; export let message = ""; - export let type = "info"; + export let type: "info" | "success" | "warning" | "error" = "info"; export let closeable = false; export let closeableCooldown = "-1"; + export let rightLinkText = ""; + export let listItems: Array<string> = []; + export let actions: Array<{ id: string; text: string; color?: string }> = + []; + // This value is set on a plain anchor tag without any svelte routing, + // listen to the right-link-click if you want to intercept the click without navigating + export let rightLinkHref = "javascript:void(0)"; export let visible = true; + const dispatch = createEventDispatcher(); + const cooldownStorageKey = "lastseen--" + id; + + let iconComponent: any; + let colorClassPart = ""; + + $: switch (type) { + case "info": { + colorClassPart = "blue"; + iconComponent = InformationCircle; + break; + } + case "warning": { + colorClassPart = "yellow"; + iconComponent = ExclamationTriangle; + break; + } + case "error": { + colorClassPart = "red"; + iconComponent = XCircle; + break; + } + case "success": { + colorClassPart = "green"; + iconComponent = CheckCircle; + break; + } + } + $: cooldownEnabled = id.indexOf(noCooldownSetting) === -1 && closeable && @@ -59,7 +103,7 @@ } const lastSeen = Temporal.Instant.fromEpochSeconds( - localStorage.getItem(cooldownStorageKey) as number + parseInt(localStorage.getItem(cooldownStorageKey) ?? "-1") ); if ( Temporal.Instant.compare( @@ -87,63 +131,93 @@ run_cooldown(); } }); - - afterUpdate(() => { - if (type === "default") { - type = "primary"; - } - }); </script> -<div - class="alert alert--{type} padding-sm radius-md" - {id} - class:alert--is-visible={visible} - role="alert" -> - <div class="flex justify-between"> - <div class="flex flex-row items-center"> - <svg - class="icon icon--sm alert__icon margin-right-xxs" - viewBox="0 0 24 24" - aria-hidden="true" - > - <path - d="M12,0C5.383,0,0,5.383,0,12s5.383,12,12,12s12-5.383,12-12S18.617,0,12,0z M14.658,18.284 c-0.661,0.26-2.952,1.354-4.272,0.191c-0.394-0.346-0.59-0.785-0.59-1.318c0-0.998,0.328-1.868,0.919-3.957 c0.104-0.395,0.231-0.907,0.231-1.313c0-0.701-0.266-0.887-0.987-0.887c-0.352,0-0.742,0.125-1.095,0.257l0.195-0.799 c0.787-0.32,1.775-0.71,2.621-0.71c1.269,0,2.203,0.633,2.203,1.837c0,0.347-0.06,0.955-0.186,1.375l-0.73,2.582 c-0.151,0.522-0.424,1.673-0.001,2.014c0.416,0.337,1.401,0.158,1.887-0.071L14.658,18.284z M13.452,8c-0.828,0-1.5-0.672-1.5-1.5 s0.672-1.5,1.5-1.5s1.5,0.672,1.5,1.5S14.28,8,13.452,8z" +{#if visible} + <div class="rounded-md bg-{colorClassPart}-50 p-4 "> + <div class="flex"> + <div class="flex-shrink-0"> + <svelte:component + this={iconComponent} + class="text-{colorClassPart}-400" /> - </svg> - {#if title} - <p class="text-sm"> - <strong class="error-title">{title}</strong> - </p> - {:else if message} - <div class="text-component text-sm break-word"> - {@html message} + </div> + <div class="ml-3"> + {#if title} + <h3 class="text-sm font-medium text-{colorClassPart}-800"> + {title} + </h3> + {/if} + {#if message} + <div + class="{title + ? 'mt-2 text-sm' + : ''} text-{colorClassPart}-700" + > + <p> + {@html message} + {#if rightLinkText} + <p class="mt-3 text-sm md:mt-0 md:ml-6"> + <a + href={rightLinkHref} + on:click={() => + dispatch("right-link-click")} + class="whitespace-nowrap font-medium text-{colorClassPart}-700 hover:text-{colorClassPart}-600" + > + {rightLinkText} + <span aria-hidden="true"> →</span> + </a> + </p> + {/if} + </p> + {#if listItems?.length ?? 0} + <ul class="list-disc space-y-1 pl-5"> + {#each listItems as listItem} + <li>{listItem}</li> + {/each} + </ul> + {/if} + </div> + {/if} + </div> + {#if actions?.length ?? 0} + <div class="mt-4"> + <div class="-mx-2 -my-1.5 flex"> + {#each actions as action} + {@const color = action?.color ?? colorClassPart} + <button + type="button" + on:click={() => dispatch("act-" + action.id)} + class="rounded-md + bg-{color}-50 + px-2 py-1.5 text-sm font-medium + text-{color}-800 + hover:bg-{color}-100 + focus:outline-none focus:ring-2 + focus:ring-{color}-600 + focus:ring-offset-2 + focus:ring-offset-{color}-50" + > + {action.text} + </button> + {/each} + </div> + </div> + {/if} + {#if closeable} + <div class="ml-auto pl-3"> + <div class="-mx-1.5 -my-1.5"> + <button + type="button" + on:click={() => close()} + class="inline-flex rounded-md bg-{colorClassPart}-50 p-1.5 text-{colorClassPart}-500 hover:bg-{colorClassPart}-100 focus:outline-none focus:ring-2 focus:ring-{colorClassPart}-600 focus:ring-offset-2 focus:ring-offset-{colorClassPart}-50" + > + <span class="sr-only">Dismiss</span> + <XMark /> + </button> + </div> </div> {/if} </div> - {#if closeable} - <button class="reset alert__close-btn" on:click={close}> - <svg - class="icon" - viewBox="0 0 20 20" - fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - > - <title>Close alert</title> - <line x1="3" y1="3" x2="17" y2="17" /> - <line x1="17" y1="3" x2="3" y2="17" /> - </svg> - </button> - {/if} </div> - - {#if message && title} - <div class="text-component text-sm break-word padding-top-xs"> - {@html message} - </div> - {/if} -</div> +{/if} |
