Ctrl K
ComunidadGuías…

components/AppToast.vue

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
<script setup lang="ts">
import { useToast } from '~/composables/useToast';

const { toasts, dismiss } = useToast();

const variantIconClass: Record<string, string> = {
  info: 'text-info',
  success: 'text-success',
  warning: 'text-warning',
  error: 'text-error',
};

const toastPopoverEl = ref<HTMLElement | null>(null);
watch(toasts, (list) => {
  const el = toastPopoverEl.value as any;
  if (!el || typeof el.showPopover !== 'function') return;
  if (el.matches(':popover-open')) el.hidePopover();
  if (list.length > 0) el.showPopover();
}, { deep: true });
</script>

<template>
  <Teleport to="body">
    <div ref="toastPopoverEl" popover="manual"
      class="fixed bottom-20 left-1/2 -translate-x-1/2 top-auto right-auto flex flex-col-reverse gap-1.5 z-[1000] m-0 p-0 border-0 bg-transparent overflow-visible pointer-events-none">
      <TransitionGroup name="toast">
        <div v-for="toast in toasts" :key="toast.id"
          class="app-toast flex items-center gap-2 bg-neutral border border-neutral-content/10 shadow-md min-w-56 max-w-72 pointer-events-auto py-2 pl-3 pr-1.5 rounded-xl"
          role="alert">
          <!-- success icon -->
          <svg v-if="toast.variant === 'success'" xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 shrink-0"
            :class="variantIconClass.success" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
            <path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
          </svg>
          <!-- error icon -->
          <svg v-else-if="toast.variant === 'error'" xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 shrink-0"
            :class="variantIconClass.error" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
            <path stroke-linecap="round" stroke-linejoin="round"
              d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
          </svg>
          <!-- warning icon -->
          <svg v-else-if="toast.variant === 'warning'" xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 shrink-0"
            :class="variantIconClass.warning" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
            <path stroke-linecap="round" stroke-linejoin="round"
              d="M12 9v2m0 4h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z" />
          </svg>
          <!-- info icon -->
          <svg v-else xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 shrink-0" :class="variantIconClass.info"
            fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
            <path stroke-linecap="round" stroke-linejoin="round"
              d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
          </svg>

          <span class="text-sm text-neutral-content leading-snug flex-1">{{ toast.message }}</span>

          <button class="btn btn-ghost btn-xs btn-circle text-neutral-content opacity-40 hover:opacity-100"
            @click="dismiss(toast.id)" aria-label="Dismiss">
            <svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24"
              stroke="currentColor" stroke-width="2.5">
              <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
            </svg>
          </button>
        </div>
      </TransitionGroup>
    </div>
  </Teleport>
</template>

<style scoped>
.toast-enter-active {
  transition: opacity 0.25s ease-out, transform 0.25s ease-out;
}

.toast-leave-active {
  transition: opacity 0.2s ease-in, transform 0.2s ease-in;
}

.toast-enter-from {
  opacity: 0;
  transform: translateY(8px);
}

.toast-leave-to {
  opacity: 0;
  transform: translateX(8px);
}

.toast-move {
  transition: transform 0.25s ease;
}
</style>