-
Notifications
You must be signed in to change notification settings - Fork 167
/
link.ts
109 lines (100 loc) · 2.51 KB
/
link.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import { computed, defineComponent, h } from "vue";
import { useRouter } from "./router.ts";
import util from "../../shared/util.ts";
import events from "../core/events.ts";
import { redirect } from "../core/redirect.ts";
const prefetched = new Set<string>();
export const Link = defineComponent({
name: "Link",
props: {
to: {
type: String,
default: "",
},
replace: {
type: Boolean,
default: undefined,
},
},
setup(props) {
const router = useRouter();
const to = props.to;
const pathname = router.value.url.pathname;
const href = computed(() => {
if (!util.isFilledString(to)) {
throw new Error("<Link>: prop `to` is required.");
}
if (util.isLikelyHttpURL(to)) {
return to;
}
let [p, q] = util.splitBy(to, "?");
if (p.startsWith("/")) {
p = util.cleanPath(p);
} else {
p = util.cleanPath(pathname + "/" + p);
}
return [p, q].filter(Boolean).join("?");
});
const onClick = (e: PointerEvent) => {
if (e.defaultPrevented || isModifiedEvent(e)) {
return;
}
e.preventDefault();
redirect(href.value, props?.replace);
};
const prefetch = () => {
if (!util.isLikelyHttpURL(href.value) && !prefetched.has(href.value)) {
events.emit("moduleprefetch", { href });
prefetched.add(href.value);
}
};
let timer: number | undefined | null = undefined;
const onMouseenter = (e: PointerEvent) => {
if (e.defaultPrevented) {
return;
}
if (!timer && !prefetched.has(href.value)) {
timer = setTimeout(() => {
timer = null;
prefetch();
}, 150);
}
};
const onMouseleave = (e: PointerEvent) => {
if (e.defaultPrevented) {
return;
}
if (timer) {
clearTimeout(timer);
timer = null;
}
};
return {
href,
onClick,
onMouseenter,
onMouseleave,
};
},
render() {
return h(
"a",
{
href: this.href,
onClick: (e: PointerEvent) => {
this.onClick(e);
},
onMouseenter: (e: PointerEvent) => {
this.onMouseenter(e);
},
onMouseleave: (e: PointerEvent) => {
this.onMouseleave(e);
},
},
this.$slots.default ? this.$slots.default() : [],
);
},
});
function isModifiedEvent(event: MouseEvent): boolean {
return event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
}