Skip to content

Instantly share code, notes, and snippets.

@anubra266
Created May 29, 2024 04:53
Show Gist options
  • Save anubra266/763d55c25873e2940724a7ddcc150933 to your computer and use it in GitHub Desktop.
Save anubra266/763d55c25873e2940724a7ddcc150933 to your computer and use it in GitHub Desktop.
Svelte toast with events
<!-- 1. Create the single toast -->
<script lang="ts">
  import { normalizeProps, useActor } from "@zag-js/svelte"
  import * as toast from "@zag-js/toast"

  interface Props {
    actor: toast.Service
  }

  const { actor }: Props = $props()

  const [snapshot, send] = useActor(actor)
  const api = $derived(toast.connect(snapshot, send, normalizeProps))
</script>

<div {...api.rootProps}>
  <h3 {...api.titleProps}>{api.title}</h3>
  <p {...api.descriptionProps}>{api.description}</p>
  <button onclick={api.dismiss}>Close</button>
</div>

<!-- 2. Create the toast group provider -->
<script lang="ts">
  import { onMount } from "svelte"
  import { normalizeProps, useMachine } from "@zag-js/svelte"
  import * as toast from "@zag-js/toast"
  // Single toast created above
  import Toast from "./toast.svelte"

  const [snapshot, send] = useMachine(toast.group.machine({ id: "1" }))

  const api = $derived(toast.group.connect(snapshot, send, normalizeProps))

  function handleEvent(event: any) {
    const method = event.type.replace("toast-", "")
    const args = event.detail
    const result = (api as any)[method](args[0], args[1])
    const resultEvent = new CustomEvent(`toast-${method}-result`, { detail: result })
    window.dispatchEvent(resultEvent)
  }

  onMount(() => {
    Object.keys(api).forEach((method) => {
      window.addEventListener(`toast-${method}`, handleEvent)
    })

    return () => {
      Object.keys(api).forEach((method) => {
        window.removeEventListener(`toast-${method}`, handleEvent)
      })
    }
  })
</script>

{#each api.getPlacements() as placement}
  <div {...api.getGroupProps({ placement })}>
    {#each api.getToastsByPlacement(placement) as toast (toast.id)}
      <Toast actor={toast} />
    {/each}
  </div>
{/each}

<!-- 3. Place the toast provider in root of your app -->
<script lang="ts">
  import { ToastProvier } from "./toast-provider.svelte"
</script>

<div>
  <h1>App Root</h1>
</div>
<ToastProvider />

<!-- 4. Create js/ts util file to consume toast api -->
import type { GroupApi } from "@zag-js/toast"

export const toast = new Proxy(
  {},
  {
    get(_, method: string) {
      return (...args: any[]) => {
        let eventResult = null
        const event = new CustomEvent(`toast-${method}`, {
          detail: args,
        })
        const handleEventResult = (event: any) => {
          eventResult = event.detail
        }
        window.addEventListener(`toast-${method}-result`, handleEventResult, { once: true })
        window.dispatchEvent(event)
        window.removeEventListener(`toast-${method}-result`, handleEventResult)
        return eventResult
      }
    },
  },
) as GroupApi

<!-- 5. Consume toast api in your app through toast-util.ts -->
<script lang="ts">
  import { toast } from "./toast-util"
</script>

<div>
  <button
    onclick={() => {
      toast.create({ title: "Hello", placement: "top-end" })
    }}
  >
    Add top-right toast
  </button>
  <button
    onclick={() => {
      toast.create({
        title: "Data submitted!",
        type: "success",
        placement: "bottom-end",
      })
    }}
  >
    Add bottom-right toast
  </button>
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment