Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: update toast component ♻️ #929

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/components/Pronosticos/Vote.astro
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ votes.forEach((vote) => {
</script>

<script>
import { ToastLocation, ToastType } from "@/function/toast"
import { $$ } from "@/lib/dom-selector"

document.addEventListener("astro:page-load", () => {
Expand Down Expand Up @@ -241,9 +242,9 @@ votes.forEach((vote) => {

window.toast({
title: "Error al guardar el voto",
location: "bottom-center",
location: ToastLocation.BottomCenter,
dismissible: false,
type: "error",
type: ToastType.Error,
icon: true,
})
}
Expand All @@ -259,9 +260,9 @@ votes.forEach((vote) => {
if (!res.ok) return rollbackUI()
window.toast({
title: "¡Voto registrado!",
location: "bottom-center",
location: ToastLocation.BottomCenter,
dismissible: false,
type: "success",
type: ToastType.Success,
icon: true,
})

Expand Down
6 changes: 0 additions & 6 deletions src/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,3 @@ interface ImportMetaEnv {
interface ImportMeta {
readonly env: ImportMetaEnv
}

declare global {
interface Window {
toast: function
}
}
231 changes: 118 additions & 113 deletions src/function/toast.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
// from: https://github.com/dgtlss/butterup/tree/main
import { $ } from "@/lib/dom-selector"

export enum ToastType {
Success = "success",
Error = "error",
Warning = "warning",
Info = "info",
}

export enum ToastLocation {
TopRight = "top-right",
TopCenter = "top-center",
TopLeft = "top-left",
BottomRight = "bottom-right",
BottomCenter = "bottom-center",
BottomLeft = "bottom-left",
}

interface ToastOptions {
title: string
message: string
type?: "success" | "error" | "warning" | "info"
location?:
| "top-right"
| "top-center"
| "top-left"
| "bottom-right"
| "bottom-center"
| "bottom-left"
title?: string
message?: string
type?: ToastType
location?: ToastLocation
icon?: boolean
theme?: string
dismissible?: boolean
Expand Down Expand Up @@ -41,48 +51,49 @@ export const butterup: ButterupProps = {
currentToasts: 0,
stackedToasts: true,
},
toast({ title, message, type, location, icon, theme, dismissible }) {
let toaster = $("#toaster")
if (toaster == null) {
// toaster doesn't exist, create it
toaster = document.createElement("div")
toaster.id = "toaster"
if (location == null) {
toaster.className = "toaster top-right"
} else {
toast(toastOptions) {
const {
title = "",
message = "",
type = ToastType.Success,
location = ToastLocation.TopRight,
icon = false,
theme = "",
dismissible = false,
} = toastOptions

const getToaster = () => {
let toaster = $("#toaster") as HTMLElement
if (toaster) {
toaster.classList.forEach((classItem) => {
// remove any location classes from the toaster
if (
Object.values(ToastLocation).some((toastLocation) => classItem.includes(toastLocation))
) {
toaster.classList.remove(classItem)
}
})
toaster.className = `toaster ${location}`
return toaster
}

toaster = document.createElement("div")
toaster.id = "toaster"
toaster.className = `toaster ${location}`
document.body.appendChild(toaster)

if ($("#butterupRack")) return toaster

// Create the toasting rack inside of the toaster
if ($("#butterupRack") == null) {
const rack = document.createElement("ol")
rack.id = "butterupRack"
rack.className = "rack"
toaster.appendChild(rack)
}
} else {
// check what location the toaster is in
toaster.classList.forEach((item) => {
// remove any location classes from the toaster
if (
item.includes("top-right") ||
item.includes("top-center") ||
item.includes("top-left") ||
item.includes("bottom-right") ||
item.includes("bottom-center") ||
item.includes("bottom-left")
) {
toaster?.classList.remove(item)
}
})
if (location == null) {
toaster.className = "toaster top-right"
} else {
toaster.className = `toaster ${location}`
}
const rack = document.createElement("ol")
rack.id = "butterupRack"
rack.className = "rack"
toaster.appendChild(rack)
return toaster
}

const toaster = getToaster()

// Check if there are too many toasts on the screen
if (butterup.options.currentToasts >= butterup.options.maxToasts) {
// there are too many toasts on the screen, delete the oldest one
Expand All @@ -97,66 +108,63 @@ export const butterup: ButterupProps = {
toast.className = "butteruptoast"
// if the toast class contains a top or bottom location, add the appropriate class to the toast
if (
toaster.className.includes("top-right") ||
toaster.className.includes("top-center") ||
toaster.className.includes("top-left")
[ToastLocation.TopRight, ToastLocation.TopCenter, ToastLocation.TopLeft].some(
(toastLocationTop) => toaster.className.includes(toastLocationTop)
)
) {
toast.className += " toastDown"
}

if (
toaster.className.includes("bottom-right") ||
toaster.className.includes("bottom-center") ||
toaster.className.includes("bottom-left")
[ToastLocation.BottomRight, ToastLocation.BottomCenter, ToastLocation.BottomLeft].some(
(toastLocationBottom) => toaster.className.includes(toastLocationBottom)
)
) {
toast.className += " toastUp"
}
toast.id = `butterupToast-${butterup.options.currentToasts}`
if (type != null) {
toast.className += ` ${type}`
}

if (theme != null) {
toast.className += ` ${theme}`
}
toast.id = `butterupToast-${butterup.options.currentToasts}`
toast.className += ` ${type}`
if (theme) toast.className += ` ${theme}`

// Add the toast to the rack
$("#butterupRack")?.appendChild(toast)

// check if the user wants an icon
if (icon != null && icon === true) {
;(() => {
if (!icon) return
// add a div inside the toast with a class of icon
const toastIcon = document.createElement("div")
toastIcon.className = "icon"
toast.appendChild(toastIcon)
if (type != null) {
// add the type class to the toast
toast.className += ` ${type}`
if (type === "success") {
toastIcon.innerHTML =
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>" +
"<path fill-rule='evenodd' d='M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z' clip-rule='evenodd' />" +
"</svg>"
}
if (type === "error") {
toastIcon.innerHTML =
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>" +
"<path fill-rule='evenodd' d='M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z' clip-rule='evenodd' />" +
"</svg>"
}
if (type === "warning") {
toastIcon.innerHTML =
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>" +
"<path fill-rule='evenodd' d='M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z' clip-rule='evenodd' />" +
"</svg>"
}
if (type === "info") {
toastIcon.innerHTML =
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>" +
"<path fill-rule='evenodd' d='M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z' clip-rule='evenodd' />" +
"</svg>"
}
if (!type) return
// add the type class to the toast
toast.className += ` ${type}`
if (type === ToastType.Success) {
toastIcon.innerHTML =
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>" +
"<path fill-rule='evenodd' d='M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z' clip-rule='evenodd' />" +
"</svg>"
}
}
if (type === ToastType.Error) {
toastIcon.innerHTML =
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>" +
"<path fill-rule='evenodd' d='M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z' clip-rule='evenodd' />" +
"</svg>"
}
if (type === ToastType.Warning) {
toastIcon.innerHTML =
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>" +
"<path fill-rule='evenodd' d='M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z' clip-rule='evenodd' />" +
"</svg>"
}
if (type === ToastType.Info) {
toastIcon.innerHTML =
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>" +
"<path fill-rule='evenodd' d='M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z' clip-rule='evenodd' />" +
"</svg>"
}
})()

// add a div inside the toast with a class of notif
const toastNotif = document.createElement("div")
Expand All @@ -169,28 +177,26 @@ export const butterup: ButterupProps = {
toastNotif.appendChild(toastDesc)

// check if the user added a title
if (title != null) {
if (title) {
const toastTitle = document.createElement("div")
toastTitle.className = "title"
toastTitle.innerHTML = title
toastDesc.appendChild(toastTitle)
}

// check if the user added a message
if (message != null) {
if (message) {
const toastMessage = document.createElement("div")
toastMessage.className = "message"
toastMessage.innerHTML = message
toastDesc.appendChild(toastMessage)
}

if (dismissible != null && dismissible === true) {
if (dismissible) {
// Add a class to the toast to make it dismissible
toast.className += " dismissible"
// when the item is clicked on, remove it from the DOM
toast.addEventListener("click", () => {
butterup.despawnToast(toast.id)
})
toast.addEventListener("click", () => butterup.despawnToast(toast.id))
}

// remove the entrance animation class after the animation has finished
Expand All @@ -205,26 +211,25 @@ export const butterup: ButterupProps = {
}, butterup.options.toastLife)
},
despawnToast(toastId) {
// fade out the toast and then remove it from the DOM
const toast = $(`#${toastId}`)
// does the toast exist?
if (toast != null) {
toast.className += " fadeOutToast"
setTimeout(() => {
// set the opacity to 0
try {
toast.style.opacity = "0"
toast.parentNode?.removeChild(toast)
butterup.options.currentToasts--
} catch (e) {
// do nothing
}
// if this was the last toast on the screen, remove the toaster
if (butterup.options.currentToasts === 0) {
const toaster = $("#toaster")
if (toaster) toaster.parentNode?.removeChild(toaster)
}
}, 500)
}
if (!toast) return
// fade out the toast and then remove it from the DOM
toast.className += " fadeOutToast"

setTimeout(() => {
// set the opacity to 0
try {
toast.style.opacity = "0"
toast.parentNode?.removeChild(toast)
butterup.options.currentToasts--
} catch (e) {
// do nothing
}
// if this was the last toast on the screen, remove the toaster
if (butterup.options.currentToasts !== 0) return
const toaster = $("#toaster")
if (!toaster) return
toaster.parentNode?.removeChild(toaster)
}, 500)
},
}
8 changes: 6 additions & 2 deletions src/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
interface Window {
toast: (options: ToastOptions) => void
declare global {
interface Window {
toast: (options: ToastOptions) => void
}
}

export {}
Loading