Newer
Older
gnexus-ui-kit / src / js / components / toasts.js
function appendIcon(container, icon) {
	if(icon instanceof Node) {
		container.append(icon);
		return;
	}

	const iconWrap = document.createElement("span");
	iconWrap.innerHTML = icon;
	container.append(...iconWrap.childNodes);
}

function template(type, icon, title, text) {
	const toast = document.createElement("div");
	toast.className = `toast toast-${type}`;
	toast.setAttribute("role", "alert");

	const content = document.createElement("div");
	content.className = "toast-content";

	const toastHeader = document.createElement("div");
	toastHeader.className = "toast-header";
	appendIcon(toastHeader, icon);
	toastHeader.append(document.createTextNode(` ${title ?? ""}`));

	content.append(toastHeader);

	if(text) {
		const toastText = document.createElement("p");
		toastText.className = "toast-text";
		toastText.textContent = text;
		content.append(toastText);
	}

	const progress = document.createElement("div");
	progress.className = "toast-progress";

	const progressBar = document.createElement("div");
	progressBar.className = "toast-progress-bar";
	progress.append(progressBar);

	const close = document.createElement("button");
	close.className = "btn-icon toast-close";
	close.type = "button";
	close.setAttribute("aria-label", "Close");
	close.innerHTML = '<i class="ph ph-x"></i>';

	toast.append(content, close, progress);

	return toast;
}

function init(toast, props) {
	const lifetime = props?.lifetime || 4000;

	if(props?.alone) {
		document.querySelectorAll(".toast").forEach(i => i.close?.());
	}

	const progressBar = toast.querySelector(".toast-progress-bar");
	if(progressBar && lifetime > 0) {
		progressBar.style.animationDuration = `${lifetime}ms`;
	}

	toast.close = function() {
		this.classList.add("a-hide");
		setTimeout(() => {
			this.remove();
		}, 300);
	}

	toast.querySelector(".toast-close").addEventListener("click", e => {
		toast.close();
	});

	toast.show = function() {
		document.querySelector("body").append(toast);

		setTimeout(() => {
			toast.classList.add("a-show");
		}, 10);
	}

	toast.addEventListener("mouseover", e => toast.ishovered = true);
	toast.addEventListener("mouseout", e => toast.ishovered = false);

	if(lifetime > 0) {
		const lifetimeInterval = setInterval(() => {
			if(!toast.ishovered) {
				toast.close();
				clearInterval(lifetimeInterval);
			}
		}, lifetime);
	}

	return toast;
}

function create(type, icon, title, text, props) {
	return init(template(type, icon, title, text), props);
}

function applyDefaults(props) {
	if(typeof props == "undefined") {
		props = {};
	}
	if(typeof props.lifetime == "undefined") {
		props.lifetime = 4000;
	}
	if(typeof props.alone == "undefined") {
		props.alone = true;
	}
	return props;
}

function createSuccess(title, text, props) {
	props = applyDefaults(props);
	return create(
		"success",
		`<i class="ph ph-check-circle"></i>`,
		title,
		text,
		props
	);
}

function createInfo(title, text, props) {
	props = applyDefaults(props);
	return create(
		"info",
		`<i class="ph ph-info"></i>`,
		title,
		text,
		props
	);
}

function createWarning(title, text, props) {
	props = applyDefaults(props);
	return create(
		"warning",
		`<i class="ph ph-warning"></i>`,
		title,
		text,
		props
	);
}

function createError(title, text, props) {
	props = applyDefaults(props);
	return create(
		"danger",
		`<i class="ph ph-warning-octagon"></i>`,
		title,
		text,
		props
	);
}

export default {
	create,
	createInfo,
	createSuccess,
	createWarning,
	createError,
	"createDanger": createError
};