Newer
Older
gnexus-ui-kit / src / vue / components / GnTabs.js
@Eugene Sukhodolskiy Eugene Sukhodolskiy 14 hours ago 2 KB Harden Vue adapter smoke path
import { computed, defineComponent, h } from "vue";
import { cx, iconNode } from "../utils.js";

export default defineComponent({
	name: "GnTabs",
	props: {
		modelValue: { type: String, default: "" },
		items: { type: Array, required: true },
		compact: { type: Boolean, default: false },
		vertical: { type: Boolean, default: false },
		ariaLabel: { type: String, default: "Tabs" }
	},
	emits: ["update:modelValue"],
	setup(props, { emit, slots }) {
		const activeId = computed(() => props.modelValue || props.items.find(item => !item.disabled)?.id || props.items[0]?.id);

		const activate = item => {
			if(!item.disabled) {
				emit("update:modelValue", item.id);
			}
		};
		const enabledItems = () => props.items.filter(item => !item.disabled);
		const move = (item, direction) => {
			const items = enabledItems();
			const index = items.findIndex(enabled => enabled.id === item.id);
			const next = items[(index + direction + items.length) % items.length];
			activate(next);
		};
		const handleKeydown = (event, item) => {
			if(event.key === "ArrowRight" || event.key === "ArrowDown") {
				event.preventDefault();
				move(item, 1);
			} else if(event.key === "ArrowLeft" || event.key === "ArrowUp") {
				event.preventDefault();
				move(item, -1);
			} else if(event.key === "Home") {
				event.preventDefault();
				activate(enabledItems()[0]);
			} else if(event.key === "End") {
				event.preventDefault();
				const items = enabledItems();
				activate(items[items.length - 1]);
			}
		};

		return () => h("div", {
			class: cx("tabs", {
				"tabs-compact": props.compact,
				"tabs-vertical": props.vertical
			})
		}, [
			h("div", { class: "tabs-list", role: "tablist", "aria-label": props.ariaLabel }, props.items.map(item => {
				const active = item.id === activeId.value;
				const panelId = `${item.id}-panel`;

				return h("button", {
					class: cx("tab", { "tab-active": active }),
					type: "button",
					role: "tab",
					"aria-selected": active ? "true" : "false",
					"aria-controls": panelId,
					"aria-disabled": item.disabled ? "true" : undefined,
					tabindex: active ? "0" : "-1",
					onClick: () => activate(item),
					onKeydown: event => handleKeydown(event, item)
				}, [
					iconNode(item.icon),
					item.label
				]);
			})),
			h("div", { class: "tabs-panels" }, props.items.map(item => {
				const active = item.id === activeId.value;

				return h("div", {
					id: `${item.id}-panel`,
					class: cx("tab-panel", { "tab-panel-active": active }),
					role: "tabpanel",
					hidden: !active
				}, slots[item.id]?.({ item, active }) || (active && slots.default?.({ item, active })));
			}))
		]);
	}
});