Newer
Older
gnexus-ui-kit / src / vue / components / GnDrawer.js
@Eugene Sukhodolskiy Eugene Sukhodolskiy 13 hours ago 2 KB Harden Vue adapter smoke path
import { defineComponent, h, nextTick, onBeforeUnmount, ref, Teleport, watch } from "vue";
import { cx, iconNode } from "../utils.js";

let drawerId = 0;

export default defineComponent({
	name: "GnDrawer",
	props: {
		open: { type: Boolean, default: false },
		title: { type: String, default: "" },
		position: { type: String, default: "right" }
	},
	emits: ["update:open", "close"],
	setup(props, { emit, slots }) {
		const titleId = `gn-drawer-title-${++drawerId}`;
		const panelRef = ref(null);
		let previousFocus = null;
		const close = () => {
			emit("update:open", false);
			emit("close");
		};
		const onKeydown = event => {
			if(event.key === "Escape") {
				event.preventDefault();
				close();
			}
		};

		watch(() => props.open, open => {
			if(open) {
				previousFocus = document.activeElement;
				document.addEventListener("keydown", onKeydown);
				nextTick(() => panelRef.value?.focus());
			} else {
				document.removeEventListener("keydown", onKeydown);
				previousFocus?.focus?.();
				previousFocus = null;
			}
		}, { flush: "post" });

		onBeforeUnmount(() => {
			document.removeEventListener("keydown", onKeydown);
		});

		return () => props.open ? h(Teleport, { to: "body" }, [
			h("div", {
				class: cx("drawer a-show", { "drawer-left": props.position === "left" }),
				"aria-hidden": "false"
			}, [
				h("div", { class: "drawer-backdrop", onClick: close }),
				h("aside", {
					ref: panelRef,
					class: "drawer-panel",
					role: "dialog",
					"aria-modal": "true",
					"aria-labelledby": titleId,
					tabindex: "-1"
				}, [
					h("header", { class: "drawer-header" }, [
						h("h4", { class: "drawer-title", id: titleId }, slots.title?.() || props.title),
						h("button", {
							class: "btn-icon drawer-close",
							type: "button",
							"aria-label": "Close",
							onClick: close
						}, [iconNode("ph-x")])
					]),
					h("div", { class: "drawer-body" }, slots.default?.()),
					slots.footer && h("footer", { class: "drawer-footer" }, slots.footer({ close }))
				])
			])
		]) : null;
	}
});