Newer
Older
gnexus-ui-kit / src / vue / utils.js
import { h } from "vue";

export const variants = new Set([
	"primary",
	"secondary",
	"accent",
	"success",
	"warning",
	"danger",
	"error",
	"info"
]);

export function cx(...items) {
	return items
		.flatMap(item => {
			if(!item) {
				return [];
			}

			if(Array.isArray(item)) {
				return item;
			}

			if(typeof item === "object") {
				return Object.entries(item)
					.filter(([, enabled]) => enabled)
					.map(([name]) => name);
			}

			return [item];
		})
		.filter(Boolean)
		.join(" ");
}

export function normalizeVariant(value, fallback = "primary") {
	return variants.has(value) ? value : fallback;
}

export function iconNode(icon, extraClass = "") {
	if(!icon) {
		return null;
	}

	const hasPrefix = icon.includes("ph ") || icon.startsWith("ph-");

	if(!hasPrefix) {
		if(typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production") {
			// eslint-disable-next-line no-console
			console.warn(`[gnexus-ui-kit] Icon "${icon}" is missing the required "ph-" prefix. Use "ph-${icon}" instead.`);
		}
	}

	const iconClass = hasPrefix
		? icon
		: `ph-${icon}`;

	return h("i", {
		class: cx(iconClass, extraClass),
		"aria-hidden": "true"
	});
}

export function slotOrText(slots, name, text) {
	return slots[name] ? slots[name]() : text;
}

export function eventValue(event) {
	const target = event.target;

	if(target.type === "checkbox") {
		return target.checked;
	}

	return target.value;
}

export const focusableSelector = [
	"a[href]",
	"button:not([disabled])",
	"input:not([disabled])",
	"select:not([disabled])",
	"textarea:not([disabled])",
	"[tabindex]:not([tabindex='-1'])"
].join(",");

export function trapFocus(event, root) {
	if(event.key !== "Tab" || !root) {
		return;
	}

	const focusable = [...root.querySelectorAll(focusableSelector)]
		.filter(node => !node.hasAttribute("disabled") && node.offsetParent !== null);

	if(!focusable.length) {
		event.preventDefault();
		root.focus();
		return;
	}

	const first = focusable[0];
	const last = focusable[focusable.length - 1];

	if(event.shiftKey && document.activeElement === first) {
		event.preventDefault();
		last.focus();
	} else if(!event.shiftKey && document.activeElement === last) {
		event.preventDefault();
		first.focus();
	}
}