development #3
							
								
								
									
										18
									
								
								src/CloseIcon.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/CloseIcon.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
<script>
 | 
			
		||||
	export let width = "1em"
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
  width={width}
 | 
			
		||||
	style="text-align: center; display: inline-block;"
 | 
			
		||||
  aria-hidden="true"
 | 
			
		||||
  focusable="false"
 | 
			
		||||
  role="img"
 | 
			
		||||
  xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
  viewBox="0 0 352 512"
 | 
			
		||||
>
 | 
			
		||||
  <path
 | 
			
		||||
    fill="currentColor"
 | 
			
		||||
    d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"
 | 
			
		||||
  />
 | 
			
		||||
</svg>
 | 
			
		||||
							
								
								
									
										19
									
								
								src/ErrorIcon.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/ErrorIcon.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
<script>
 | 
			
		||||
	export let width = "1em"
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
  width={width}
 | 
			
		||||
	style="text-align: center; display: inline-block;"
 | 
			
		||||
  aria-hidden="true"
 | 
			
		||||
  focusable="false"
 | 
			
		||||
  role="img"
 | 
			
		||||
  xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
  viewBox="0 0 512 512"
 | 
			
		||||
>
 | 
			
		||||
  <path
 | 
			
		||||
    fill="currentColor"
 | 
			
		||||
    d="M256 40c118.621 0 216 96.075 216 216 0 119.291-96.61 216-216 216-119.244 0-216-96.562-216-216 0-119.203 96.602-216 216-216m0-32C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm-11.49 120h22.979c6.823 0 12.274 5.682 11.99 12.5l-7 168c-.268 6.428-5.556 11.5-11.99 11.5h-8.979c-6.433 0-11.722-5.073-11.99-11.5l-7-168c-.283-6.818 5.167-12.5 11.99-12.5zM256 340c-15.464 0-28 12.536-28 28s12.536 28 28 28 28-12.536 28-28-12.536-28-28-28z"
 | 
			
		||||
    class=""
 | 
			
		||||
  ></path>
 | 
			
		||||
</svg>
 | 
			
		||||
							
								
								
									
										18
									
								
								src/InfoIcon.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/InfoIcon.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
<script>
 | 
			
		||||
	export let width = "1em"
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
  width={width}
 | 
			
		||||
	style="text-align: center; display: inline-block;"
 | 
			
		||||
  aria-hidden="true"
 | 
			
		||||
  focusable="false"
 | 
			
		||||
  role="img"
 | 
			
		||||
  xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
  viewBox="0 0 512 512"
 | 
			
		||||
>
 | 
			
		||||
  <path
 | 
			
		||||
    fill="currentColor"
 | 
			
		||||
    d="M256 40c118.621 0 216 96.075 216 216 0 119.291-96.61 216-216 216-119.244 0-216-96.562-216-216 0-119.203 96.602-216 216-216m0-32C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm-36 344h12V232h-12c-6.627 0-12-5.373-12-12v-8c0-6.627 5.373-12 12-12h48c6.627 0 12 5.373 12 12v140h12c6.627 0 12 5.373 12 12v8c0 6.627-5.373 12-12 12h-72c-6.627 0-12-5.373-12-12v-8c0-6.627 5.373-12 12-12zm36-240c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32z"
 | 
			
		||||
  />
 | 
			
		||||
</svg>
 | 
			
		||||
							
								
								
									
										18
									
								
								src/SuccessIcon.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/SuccessIcon.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
<script>
 | 
			
		||||
	export let width = "1em"
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
  width={width}
 | 
			
		||||
	style="text-align: center; display: inline-block;"
 | 
			
		||||
  aria-hidden="true"
 | 
			
		||||
  focusable="false"
 | 
			
		||||
  role="img"
 | 
			
		||||
  xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
  viewBox="0 0 512 512"
 | 
			
		||||
>
 | 
			
		||||
  <path
 | 
			
		||||
    fill="currentColor"
 | 
			
		||||
    d="M256 8C119.033 8 8 119.033 8 256s111.033 248 248 248 248-111.033 248-248S392.967 8 256 8zm0 464c-118.664 0-216-96.055-216-216 0-118.663 96.055-216 216-216 118.664 0 216 96.055 216 216 0 118.663-96.055 216-216 216zm141.63-274.961L217.15 376.071c-4.705 4.667-12.303 4.637-16.97-.068l-85.878-86.572c-4.667-4.705-4.637-12.303.068-16.97l8.52-8.451c4.705-4.667 12.303-4.637 16.97.068l68.976 69.533 163.441-162.13c4.705-4.667 12.303-4.637 16.97.068l8.451 8.52c4.668 4.705 4.637 12.303-.068 16.97z"
 | 
			
		||||
  />
 | 
			
		||||
</svg>
 | 
			
		||||
							
								
								
									
										66
									
								
								src/Toast.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/Toast.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
<script>
 | 
			
		||||
  import { createEventDispatcher } from "svelte";
 | 
			
		||||
  import { fade } from "svelte/transition";
 | 
			
		||||
  import SuccessIcon from "./SuccessIcon.svelte";
 | 
			
		||||
  import ErrorIcon from "./ErrorIcon.svelte";
 | 
			
		||||
  import InfoIcon from "./InfoIcon.svelte";
 | 
			
		||||
  import CloseIcon from "./CloseIcon.svelte";
 | 
			
		||||
 | 
			
		||||
  const dispatch = createEventDispatcher();
 | 
			
		||||
 | 
			
		||||
  export let type = "error";
 | 
			
		||||
  export let dismissible = true;
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<article class={type} role="alert" transition:fade>
 | 
			
		||||
  {#if type === "success"}
 | 
			
		||||
    <SuccessIcon width="1.1em" />
 | 
			
		||||
  {:else if type === "error"}
 | 
			
		||||
    <ErrorIcon width="1.1em" />
 | 
			
		||||
  {:else}
 | 
			
		||||
    <InfoIcon width="1.1em" />
 | 
			
		||||
  {/if}
 | 
			
		||||
 | 
			
		||||
  <div class="text">
 | 
			
		||||
    <slot />
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  {#if dismissible}
 | 
			
		||||
    <button class="close" on:click={() => dispatch("dismiss")}>
 | 
			
		||||
      <CloseIcon width="0.8em" />
 | 
			
		||||
    </button>
 | 
			
		||||
  {/if}
 | 
			
		||||
</article>
 | 
			
		||||
 | 
			
		||||
<style lang="postcss">
 | 
			
		||||
  article {
 | 
			
		||||
    color: white;
 | 
			
		||||
    padding: 0.75rem 1.5rem;
 | 
			
		||||
    border-radius: 0.2rem;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    margin: 0 auto 0.5rem auto;
 | 
			
		||||
    width: 20rem;
 | 
			
		||||
  }
 | 
			
		||||
  .error {
 | 
			
		||||
    background: IndianRed;
 | 
			
		||||
  }
 | 
			
		||||
  .success {
 | 
			
		||||
    background: MediumSeaGreen;
 | 
			
		||||
  }
 | 
			
		||||
  .info {
 | 
			
		||||
    background: SkyBlue;
 | 
			
		||||
  }
 | 
			
		||||
  .text {
 | 
			
		||||
    margin-left: 1rem;
 | 
			
		||||
  }
 | 
			
		||||
  button {
 | 
			
		||||
    color: white;
 | 
			
		||||
    background: transparent;
 | 
			
		||||
    border: 0 none;
 | 
			
		||||
    padding: 0;
 | 
			
		||||
    margin: 0 0 0 auto;
 | 
			
		||||
    line-height: 1;
 | 
			
		||||
    font-size: 1rem;
 | 
			
		||||
  }
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										32
									
								
								src/Toasts.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/Toasts.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
  import Toast from "./Toast.svelte";
 | 
			
		||||
 | 
			
		||||
  import { dismissToast, toasts } from "./store";
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
{#if $toasts}
 | 
			
		||||
  <section>
 | 
			
		||||
    {#each $toasts as toast (toast.id)}
 | 
			
		||||
      <Toast
 | 
			
		||||
        type={toast.type}
 | 
			
		||||
        dismissible={toast.dismissible}
 | 
			
		||||
        on:dismiss={() => dismissToast(toast.id)}>{toast.message}</Toast
 | 
			
		||||
      >
 | 
			
		||||
    {/each}
 | 
			
		||||
  </section>
 | 
			
		||||
{/if}
 | 
			
		||||
 | 
			
		||||
<style lang="postcss">
 | 
			
		||||
  section {
 | 
			
		||||
    position: fixed;
 | 
			
		||||
    top: 0;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    right: 0;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    margin-top: 1rem;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    z-index: 1000;
 | 
			
		||||
  }
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										10
									
								
								src/lib/toast.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/lib/toast.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
class Toast {
 | 
			
		||||
    constructor(text: String, type: String, dismissable: boolean, timeout: number ) {
 | 
			
		||||
        id = text
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    id: String;
 | 
			
		||||
    type: String;
 | 
			
		||||
    dismissible: boolean;
 | 
			
		||||
    timeout: number;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
<script>
 | 
			
		||||
    import { getJson } from "$lib/data";
 | 
			
		||||
    import { removeMessage } from "$lib/snackbar"
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
<script>
 | 
			
		||||
	import Snackbar from '../snackbar.svelte';
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
@@ -34,13 +35,13 @@
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @-webkit-keyframes fadeinout {
 | 
			
		||||
    0% { opacity: 0; }
 | 
			
		||||
    100% { opacity: 1; }
 | 
			
		||||
        0% { opacity: 0; }
 | 
			
		||||
        100% { opacity: 1; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @keyframes fadeinout {
 | 
			
		||||
    0% { opacity: 0; }
 | 
			
		||||
    100% { opacity: 1; }
 | 
			
		||||
        0% { opacity: 0; }
 | 
			
		||||
        100% { opacity: 1; }
 | 
			
		||||
    }
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
@@ -51,7 +52,5 @@
 | 
			
		||||
</nav>
 | 
			
		||||
 | 
			
		||||
<div class="main-container fade">
 | 
			
		||||
 | 
			
		||||
    <slot />
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
<script>
 | 
			
		||||
    import Main from '../main.svelte';
 | 
			
		||||
    import Snackbar from '../snackbar.svelte';
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<Main></Main>
 | 
			
		||||
<Main></Main>
 | 
			
		||||
<Snackbar></Snackbar>
 | 
			
		||||
							
								
								
									
										73
									
								
								src/snackbar.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/snackbar.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
    let messages: string[] = [];
 | 
			
		||||
 | 
			
		||||
	export function addMessage(text: string) {
 | 
			
		||||
		messages = [...messages, text];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    export function removeMessage() {
 | 
			
		||||
		if(messages.length == 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        messages = messages.splice(1, messages.length-1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
    /* Flex container for all of the elements waiting to be shown*/
 | 
			
		||||
    .snackbar-container {
 | 
			
		||||
        position: fixed;
 | 
			
		||||
        top: 0;
 | 
			
		||||
        left: 0;
 | 
			
		||||
        right: 0;
 | 
			
		||||
        width: 100%;
 | 
			
		||||
        display: flex;
 | 
			
		||||
        margin: 1rem;
 | 
			
		||||
        justify-content: center;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        gap: 1em;
 | 
			
		||||
        z-index: 1000;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /* The snackbar - position it at the bottom and in the middle of the screen */
 | 
			
		||||
    .snackbar-item {
 | 
			
		||||
        background-color: var(--bg-2);
 | 
			
		||||
        color: var(--green); 
 | 
			
		||||
        width: 10em;
 | 
			
		||||
        text-align: center; 
 | 
			
		||||
        border-radius: 1em; 
 | 
			
		||||
        padding: 16px; 
 | 
			
		||||
        left: 50%;
 | 
			
		||||
        -webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
 | 
			
		||||
        animation: fadein 0.5s, fadeout 0.5s 2.5s;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Animations to fade the snackbar in and out */
 | 
			
		||||
    @-webkit-keyframes fadein {
 | 
			
		||||
        from {bottom: 0; opacity: 0;}
 | 
			
		||||
        to {bottom: 30px; opacity: 1;}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @keyframes fadein {
 | 
			
		||||
        from {bottom: 0; opacity: 0;}
 | 
			
		||||
        to {bottom: 30px; opacity: 1;}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @-webkit-keyframes fadeout {
 | 
			
		||||
        from {bottom: 30px; opacity: 1;}
 | 
			
		||||
        to {bottom: 0; opacity: 0;}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @keyframes fadeout {
 | 
			
		||||
        from {bottom: 30px; opacity: 1;}
 | 
			
		||||
        to {bottom: 0; opacity: 0;}
 | 
			
		||||
    }
 | 
			
		||||
</style>
 | 
			
		||||
<button on:click={() => addMessage("Hello")}>Hello</button>
 | 
			
		||||
 | 
			
		||||
<div class="snackbar-container">
 | 
			
		||||
    {#each messages as message}
 | 
			
		||||
        <div class="snackbar-item" on:click={removeMessage}>{message}</div>
 | 
			
		||||
    {/each}
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										27
									
								
								src/store.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/store.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
import { writable } from "svelte/store";
 | 
			
		||||
 | 
			
		||||
export const toasts = writable([]);
 | 
			
		||||
 | 
			
		||||
export const addToast = (toast: ) => {
 | 
			
		||||
  // Create a unique ID so we can easily find/remove it
 | 
			
		||||
  // if it is dismissible/has a timeout.
 | 
			
		||||
  const id = Math.floor(Math.random() * 10000);
 | 
			
		||||
 | 
			
		||||
  // Setup some sensible defaults for a toast.
 | 
			
		||||
  const defaults = {
 | 
			
		||||
    id,
 | 
			
		||||
    type: "info",
 | 
			
		||||
    dismissible: true,
 | 
			
		||||
    timeout: 3000,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Push the toast to the top of the list of toasts
 | 
			
		||||
  toasts.update((all) => [{ ...defaults, ...toast }, ...all]);
 | 
			
		||||
 | 
			
		||||
  // If toast is dismissible, dismiss it after "timeout" amount of time.
 | 
			
		||||
  if (toast.timeout) setTimeout(() => dismissToast(id), toast.timeout);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const dismissToast = (id) => {
 | 
			
		||||
  toasts.update((all) => all.filter((t) => t.id !== id));
 | 
			
		||||
};
 | 
			
		||||
		Reference in New Issue
	
	Block a user