Newer
Older
vmk-ui-kit / src / vue / components / GnNavigationShell.js
/**
 * GnNavigationShell - Application layout with topbar and sidebar.
 */

import { defineComponent, h, ref, provide, computed } from "vue";
import { cx, iconNode } from "../utils.js";

export const navigationKey = Symbol("vmk-ui-kit-navigation");

export default defineComponent({
	name: "GnNavigationShell",
	props: {
		brand: { type: String, default: "" },
		collapsed: { type: Boolean, default: false }
	},
	emits: ["update:collapsed"],
	setup(props, { slots, emit }) {
		const isOpen = ref(false);

		const toggle = () => {
			isOpen.value = !isOpen.value;
			emit("update:collapsed", isOpen.value);
		};

		const close = () => {
			isOpen.value = false;
			emit("update:collapsed", false);
		};

		provide(navigationKey, { isOpen, close });

		return () => h("div", { class: "navigation-shell" }, [
			h("header", { class: "navigation-topbar" }, [
				h("button", {
					type: "button",
					class: "navigation-toggle btn btn-sm btn-tertiary",
					"data-navigation-toggle": "",
					onClick: toggle
				}, [iconNode("ph-list")]),
				h("div", { class: "navigation-brand" }, props.brand || slots.brand?.()),
				h("div", { class: "navigation-topbar-actions" }, slots.topbar?.())
			]),
			h("aside", {
				class: cx("navigation-sidebar", { "is-open": isOpen.value })
			}, [
				props.brand && h("div", { class: "navigation-brand navigation-brand-sidebar" }, props.brand || slots.brand?.()),
				h("nav", { class: "navigation-menu" }, slots.menu?.())
			]),
			h("div", {
				class: cx("navigation-backdrop", { "is-visible": isOpen.value }),
				onClick: close
			}),
			h("main", { class: "navigation-content" }, slots.default?.())
		]);
	}
});