/**
* GnTopNavbar — application-level top header bar.
*
* Figma: Top Navbar.
*
* Variants:
* ghost - transparent over dark surfaces (logo + center nav + pill action).
* solid - white bar with title/actions (back buttons, outline actions).
* centered - centered logo/brand only.
* compact - mobile-style bar with back arrow, title/meta, and optional action.
*
* @typedef {Object} GnTopNavbarBrand
* @property {string} title - Brand text.
* @property {boolean} [logo=true] - Show the default logo orb.
*
* @typedef {Object} GnTopNavbarAction
* @property {string} label - Action text.
* @property {string} [icon] - VMK icon key or legacy Phosphor name (ph-*).
* @property {string} [href] - Link URL.
* @property {string} [variant] - "pill" (white pill) or "outline" (mint outline). Ghost defaults to pill.
* @property {Function} [onClick] - Click handler.
*
* @slots brand - Left brand region.
* @slots center - Center region (overrides title/navItems).
* @slots actions - Right actions region (overrides actions prop).
*/
import { defineComponent, h, ref, watch } from "vue";
import { cx, iconNode } from "../utils.js";
import GnBottomBar from "./GnBottomBar.js";
export default defineComponent({
name: "GnTopNavbar",
inheritAttrs: false,
props: {
variant: { type: String, default: "ghost" },
brand: { type: [String, Object], default: "" },
title: { type: String, default: "" },
subtitle: { type: String, default: "" },
navItems: { type: Array, default: () => [] },
actions: { type: Array, default: () => [] },
back: { type: Boolean, default: false },
activeNav: { type: [String, Number], default: undefined }
},
emits: ["select", "back"],
setup(props, { attrs, slots, emit }) {
const centerActive = ref(props.activeNav ?? (props.navItems.length ? 0 : undefined));
watch(() => props.activeNav, value => {
if (value !== undefined) {
centerActive.value = value;
}
});
const brandTitle = typeof props.brand === "string" ? props.brand : props.brand?.title;
const showLogo = typeof props.brand === "object" ? props.brand?.logo !== false : true;
const renderBrand = () => {
if (slots.brand) {
return slots.brand();
}
if (!brandTitle) {
return null;
}
return h("a", { class: "top-navbar__brand", href: "#" }, [
showLogo && h("span", { class: "top-navbar__logo" }),
h("span", { class: "top-navbar__title" }, brandTitle)
]);
};
const renderCenterContent = () => {
if (slots.center) {
return slots.center();
}
if (props.title) {
return h("h1", { class: "top-navbar__title" }, props.title);
}
if (props.navItems.length) {
return h(GnBottomBar, {
class: "top-navbar__nav",
layout: "inline",
items: props.navItems,
modelValue: centerActive.value,
"onUpdate:modelValue": value => {
centerActive.value = value;
emit("select", value);
}
});
}
return null;
};
const renderCenter = () => {
const content = [renderCenterContent()];
if (props.subtitle) {
content.push(h("span", { class: "top-navbar__meta" }, props.subtitle));
}
const stack = props.subtitle && !slots.center;
return h("div", { class: cx("top-navbar__center", { "top-navbar__center--stack": stack }) }, content);
};
const renderActions = () => {
if (slots.actions) {
return slots.actions();
}
return props.actions.map((action, index) => {
const tag = action.href ? "a" : "button";
const isPill = action.variant === "pill" || (props.variant === "ghost" && action.variant !== "outline");
return h(tag, {
key: index,
class: isPill ? "top-navbar__pill" : cx("btn", "btn-secondary", "btn-sm"),
href: action.href,
type: action.href ? undefined : "button",
onClick: () => action.onClick?.(action)
}, [
iconNode(action.icon),
action.label
]);
});
};
return () => {
const children = [];
if (props.variant === "centered") {
children.push(h("div", { class: "top-navbar__center" }, slots.center?.() ?? renderBrand()));
} else {
children.push(renderBrand());
if (props.back) {
children.push(h("button", {
class: cx("btn", "btn-icon-only", "btn-sm", "btn-secondary", "top-navbar__back"),
type: "button",
onClick: () => emit("back")
}, [iconNode("ph-arrow-left")]));
}
children.push(renderCenter());
children.push(h("div", { class: "top-navbar__actions" }, [renderActions()]));
}
return h("header", {
...attrs,
class: cx("top-navbar", `top-navbar--${props.variant}`, attrs.class)
}, children);
};
}
});