import { defineComponent, h, type PropType } from "vue"; import type { RouteLocationNormalizedLoaded, RouteLocationRaw, } from "vue-router"; import { useNativeRouter } from "@native-vue-router/core"; import "./style.css"; export type NativePlatform = "ios" | "android" | "desktop"; export function detectNativePlatform(): NativePlatform { if (typeof navigator === "undefined") return "desktop"; if ( /iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent) && "ontouchend" in document ) return "ios"; if (/Android/.test(navigator.userAgent)) return "android"; return "desktop"; } export const NativeBackButton = defineComponent({ name: "NativeBackButton", props: { label: { type: String, default: "Back" } }, setup(props) { const native = useNativeRouter(); return () => h( "button", { type: "button", class: "nvr-native-back", "aria-label": props.label, onClick: () => void native.pop(), }, [h("span", { "aria-hidden": "true" }, "‹"), h("span", props.label)], ); }, }); export interface NativeTabItem { label: string; to: RouteLocationRaw; icon?: string; activeWhen?: (current: RouteLocationNormalizedLoaded) => boolean; } export const NativeTabBar = defineComponent({ name: "NativeTabBar", props: { items: { type: Array as PropType, required: true }, }, setup(props) { const native = useNativeRouter(); return () => h( "nav", { class: "nvr-native-tabs", "aria-label": "Primary navigation" }, props.items.map((item) => { const current = native.router.currentRoute.value; const exact = current.path === native.router.resolve(item.to).path; const active = exact || Boolean(item.activeWhen?.(current)); const href = native.router.resolve(item.to).href; return h( "a", { href, class: ["nvr-native-tab", active && "nvr-native-tab--active"], "aria-current": active ? "page" : undefined, onClick: (event: MouseEvent) => { if ( event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey ) return; event.preventDefault(); if (exact) return; void native.sibling(item.to, { replace: true }); }, }, [ h( "span", { class: "nvr-native-tab__icon", "aria-hidden": "true" }, item.icon ?? "•", ), h("span", item.label), ], ); }), ); }, }); export const nativeMotionTokens = { ios: { edgeWidth: 28, commitThreshold: 0.36 }, android: { edgeWidth: 24, commitThreshold: 0.32 }, desktop: { edgeWidth: 18, commitThreshold: 0.4 }, } as const;