Files
Native-Router-Vue/packages/preset-native/src/index.ts
2026-07-22 02:12:05 +00:00

104 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<NativeTabItem[]>, 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;