Newer
Older
gnexus-ui-kit / src / vue / components / GnDropdown.js
@Eugene Sukhodolskiy Eugene Sukhodolskiy 14 hours ago 1 KB Add Vue navigation overlay adapters
import { defineComponent, h, onBeforeUnmount, ref } from "vue";
import { cx, iconNode } from "../utils.js";
import GnButton from "./GnButton.js";

export default defineComponent({
	name: "GnDropdown",
	props: {
		label: { type: String, default: "Actions" },
		icon: { type: String, default: "ph-dots-three-outline" },
		variant: { type: String, default: "secondary" },
		items: { type: Array, default: () => [] }
	},
	emits: ["select"],
	setup(props, { emit, slots }) {
		const open = ref(false);
		const root = ref(null);
		const close = () => {
			open.value = false;
			document.removeEventListener("click", onOutsideClick);
			document.removeEventListener("keydown", onKeydown);
		};
		const onOutsideClick = event => {
			if(root.value && !root.value.contains(event.target)) {
				close();
			}
		};
		const onKeydown = event => {
			if(event.key === "Escape") {
				event.preventDefault();
				close();
			}
		};
		const toggle = () => {
			open.value = !open.value;

			if(open.value) {
				setTimeout(() => document.addEventListener("click", onOutsideClick), 0);
				document.addEventListener("keydown", onKeydown);
			} else {
				close();
			}
		};
		const select = item => {
			if(item.disabled) {
				return;
			}

			item.onSelect?.(item);
			emit("select", item);
			close();
		};

		onBeforeUnmount(close);

		return () => h("div", { ref: root, class: cx("dropdown", { "is-open": open.value }) }, [
			slots.trigger?.({ open: open.value, toggle }) || h(GnButton, {
				variant: props.variant,
				icon: props.icon,
				"aria-expanded": open.value ? "true" : "false",
				onClick: toggle
			}, () => props.label),
			h("div", { class: "dropdown-menu", role: "menu" }, slots.default?.({ close }) || props.items.map(item => h("button", {
				class: cx("dropdown-item", item.danger && "dropdown-item-danger"),
				type: "button",
				role: "menuitem",
				disabled: item.disabled,
				onClick: () => select(item)
			}, [
				iconNode(item.icon),
				item.label
			])))
		]);
	}
});