@tanstack/vue-router 1.167.1 → 1.167.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/dist/esm/Asset.js +107 -151
  2. package/dist/esm/Asset.js.map +1 -1
  3. package/dist/esm/Body.js +15 -24
  4. package/dist/esm/Body.js.map +1 -1
  5. package/dist/esm/CatchBoundary.js +112 -130
  6. package/dist/esm/CatchBoundary.js.map +1 -1
  7. package/dist/esm/ClientOnly.js +59 -29
  8. package/dist/esm/ClientOnly.js.map +1 -1
  9. package/dist/esm/HeadContent.dev.js +29 -23
  10. package/dist/esm/HeadContent.dev.js.map +1 -1
  11. package/dist/esm/HeadContent.js +21 -16
  12. package/dist/esm/HeadContent.js.map +1 -1
  13. package/dist/esm/Html.js +42 -61
  14. package/dist/esm/Html.js.map +1 -1
  15. package/dist/esm/Match.js +238 -319
  16. package/dist/esm/Match.js.map +1 -1
  17. package/dist/esm/Matches.js +127 -170
  18. package/dist/esm/Matches.js.map +1 -1
  19. package/dist/esm/RouterProvider.js +50 -65
  20. package/dist/esm/RouterProvider.js.map +1 -1
  21. package/dist/esm/ScriptOnce.js +31 -36
  22. package/dist/esm/ScriptOnce.js.map +1 -1
  23. package/dist/esm/Scripts.js +79 -101
  24. package/dist/esm/Scripts.js.map +1 -1
  25. package/dist/esm/ScrollRestoration.js +25 -29
  26. package/dist/esm/ScrollRestoration.js.map +1 -1
  27. package/dist/esm/Transitioner.js +146 -164
  28. package/dist/esm/Transitioner.js.map +1 -1
  29. package/dist/esm/awaited.js +27 -34
  30. package/dist/esm/awaited.js.map +1 -1
  31. package/dist/esm/fileRoute.js +90 -92
  32. package/dist/esm/fileRoute.js.map +1 -1
  33. package/dist/esm/headContentUtils.js +92 -123
  34. package/dist/esm/headContentUtils.js.map +1 -1
  35. package/dist/esm/index.dev.js +16 -118
  36. package/dist/esm/index.js +18 -119
  37. package/dist/esm/lazyRouteComponent.js +69 -82
  38. package/dist/esm/lazyRouteComponent.js.map +1 -1
  39. package/dist/esm/link.js +364 -385
  40. package/dist/esm/link.js.map +1 -1
  41. package/dist/esm/matchContext.js +16 -11
  42. package/dist/esm/matchContext.js.map +1 -1
  43. package/dist/esm/not-found.js +30 -40
  44. package/dist/esm/not-found.js.map +1 -1
  45. package/dist/esm/renderRouteNotFound.js +20 -15
  46. package/dist/esm/renderRouteNotFound.js.map +1 -1
  47. package/dist/esm/route.js +196 -174
  48. package/dist/esm/route.js.map +1 -1
  49. package/dist/esm/router.js +11 -11
  50. package/dist/esm/router.js.map +1 -1
  51. package/dist/esm/routerContext.js +10 -7
  52. package/dist/esm/routerContext.js.map +1 -1
  53. package/dist/esm/scroll-restoration.js +39 -50
  54. package/dist/esm/scroll-restoration.js.map +1 -1
  55. package/dist/esm/ssr/RouterClient.js +29 -43
  56. package/dist/esm/ssr/RouterClient.js.map +1 -1
  57. package/dist/esm/ssr/RouterServer.js +29 -32
  58. package/dist/esm/ssr/RouterServer.js.map +1 -1
  59. package/dist/esm/ssr/client.js +1 -4
  60. package/dist/esm/ssr/defaultRenderHandler.js +11 -13
  61. package/dist/esm/ssr/defaultRenderHandler.js.map +1 -1
  62. package/dist/esm/ssr/defaultStreamHandler.js +12 -15
  63. package/dist/esm/ssr/defaultStreamHandler.js.map +1 -1
  64. package/dist/esm/ssr/renderRouterToStream.js +47 -65
  65. package/dist/esm/ssr/renderRouterToStream.js.map +1 -1
  66. package/dist/esm/ssr/renderRouterToString.js +24 -32
  67. package/dist/esm/ssr/renderRouterToString.js.map +1 -1
  68. package/dist/esm/ssr/server.js +3 -10
  69. package/dist/esm/useBlocker.js +243 -287
  70. package/dist/esm/useBlocker.js.map +1 -1
  71. package/dist/esm/useCanGoBack.js +6 -5
  72. package/dist/esm/useCanGoBack.js.map +1 -1
  73. package/dist/esm/useLoaderData.js +12 -11
  74. package/dist/esm/useLoaderData.js.map +1 -1
  75. package/dist/esm/useLoaderDeps.js +12 -14
  76. package/dist/esm/useLoaderDeps.js.map +1 -1
  77. package/dist/esm/useLocation.js +6 -7
  78. package/dist/esm/useLocation.js.map +1 -1
  79. package/dist/esm/useMatch.js +27 -35
  80. package/dist/esm/useMatch.js.map +1 -1
  81. package/dist/esm/useNavigate.js +18 -24
  82. package/dist/esm/useNavigate.js.map +1 -1
  83. package/dist/esm/useParams.js +13 -12
  84. package/dist/esm/useParams.js.map +1 -1
  85. package/dist/esm/useRouteContext.js +9 -8
  86. package/dist/esm/useRouteContext.js.map +1 -1
  87. package/dist/esm/useRouter.js +9 -8
  88. package/dist/esm/useRouter.js.map +1 -1
  89. package/dist/esm/useRouterState.js +18 -22
  90. package/dist/esm/useRouterState.js.map +1 -1
  91. package/dist/esm/useSearch.js +13 -12
  92. package/dist/esm/useSearch.js.map +1 -1
  93. package/dist/esm/utils.js +59 -38
  94. package/dist/esm/utils.js.map +1 -1
  95. package/package.json +3 -3
  96. package/dist/esm/index.dev.js.map +0 -1
  97. package/dist/esm/index.js.map +0 -1
  98. package/dist/esm/ssr/client.js.map +0 -1
  99. package/dist/esm/ssr/server.js.map +0 -1
package/dist/esm/link.js CHANGED
@@ -1,396 +1,375 @@
1
- import * as Vue from "vue";
2
- import { isDangerousProtocol, preloadWarning, exactPathTest, removeTrailingSlash, deepEqual } from "@tanstack/router-core";
3
- import { useRouterState } from "./useRouterState.js";
4
1
  import { useRouter } from "./useRouter.js";
2
+ import { useRouterState } from "./useRouterState.js";
5
3
  import { useIntersectionObserver } from "./utils.js";
6
4
  import { useMatches } from "./Matches.js";
7
- const timeoutMap = /* @__PURE__ */ new WeakMap();
5
+ import { deepEqual, exactPathTest, isDangerousProtocol, preloadWarning, removeTrailingSlash } from "@tanstack/router-core";
6
+ import * as Vue from "vue";
7
+ //#region src/link.tsx
8
+ var timeoutMap = /* @__PURE__ */ new WeakMap();
8
9
  function useLinkProps(options) {
9
- const router = useRouter();
10
- const isTransitioning = Vue.ref(false);
11
- let hasRenderFetched = false;
12
- if (!router) {
13
- console.warn("useRouter must be used inside a <RouterProvider> component!");
14
- return Vue.computed(() => ({}));
15
- }
16
- const type = Vue.computed(() => {
17
- try {
18
- new URL(`${options.to}`);
19
- return "external";
20
- } catch {
21
- return "internal";
22
- }
23
- });
24
- const buildLocationKey = useRouterState({
25
- select: (s) => {
26
- const leaf = s.matches[s.matches.length - 1];
27
- return {
28
- search: leaf?.search,
29
- hash: s.location.hash,
30
- path: leaf?.pathname
31
- // path + params
32
- };
33
- }
34
- });
35
- const from = useMatches({
36
- select: (matches) => options.from ?? matches[matches.length - 1]?.fullPath
37
- });
38
- const _options = Vue.computed(() => ({
39
- ...options,
40
- from: from.value
41
- }));
42
- const next = Vue.computed(() => {
43
- buildLocationKey.value;
44
- return router.buildLocation(_options.value);
45
- });
46
- const preload = Vue.computed(() => {
47
- if (_options.value.reloadDocument) {
48
- return false;
49
- }
50
- return options.preload ?? router.options.defaultPreload;
51
- });
52
- const preloadDelay = Vue.computed(() => options.preloadDelay ?? router.options.defaultPreloadDelay ?? 0);
53
- const isActive = useRouterState({
54
- select: (s) => {
55
- const activeOptions = options.activeOptions;
56
- if (activeOptions?.exact) {
57
- const testExact = exactPathTest(s.location.pathname, next.value.pathname, router.basepath);
58
- if (!testExact) {
59
- return false;
60
- }
61
- } else {
62
- const currentPathSplit = removeTrailingSlash(s.location.pathname, router.basepath).split("/");
63
- const nextPathSplit = removeTrailingSlash(next.value?.pathname, router.basepath)?.split("/");
64
- const pathIsFuzzyEqual = nextPathSplit?.every((d, i) => d === currentPathSplit[i]);
65
- if (!pathIsFuzzyEqual) {
66
- return false;
67
- }
68
- }
69
- if (activeOptions?.includeSearch ?? true) {
70
- const searchTest = deepEqual(s.location.search, next.value.search, {
71
- partial: !activeOptions?.exact,
72
- ignoreUndefined: !activeOptions?.explicitUndefined
73
- });
74
- if (!searchTest) {
75
- return false;
76
- }
77
- }
78
- if (activeOptions?.includeHash) {
79
- return s.location.hash === next.value.hash;
80
- }
81
- return true;
82
- }
83
- });
84
- const doPreload = () => router.preloadRoute(_options.value).catch((err) => {
85
- console.warn(err);
86
- console.warn(preloadWarning);
87
- });
88
- const preloadViewportIoCallback = (entry) => {
89
- if (entry?.isIntersecting) {
90
- doPreload();
91
- }
92
- };
93
- const ref = Vue.ref(null);
94
- useIntersectionObserver(ref, preloadViewportIoCallback, {
95
- rootMargin: "100px"
96
- }, {
97
- disabled: () => !!options.disabled || !(preload.value === "viewport")
98
- });
99
- Vue.effect(() => {
100
- if (hasRenderFetched) {
101
- return;
102
- }
103
- if (!options.disabled && preload.value === "render") {
104
- doPreload();
105
- hasRenderFetched = true;
106
- }
107
- });
108
- const getPropsSafeToSpread = () => {
109
- const result = {};
110
- const optionRecord = options;
111
- for (const key in options) {
112
- if (!["activeProps", "inactiveProps", "activeOptions", "to", "preload", "preloadDelay", "hashScrollIntoView", "replace", "startTransition", "resetScroll", "viewTransition", "children", "target", "disabled", "style", "class", "onClick", "onBlur", "onFocus", "onMouseEnter", "onMouseLeave", "onMouseOver", "onMouseOut", "onTouchStart", "ignoreBlocker", "params", "search", "hash", "state", "mask", "reloadDocument", "_asChild", "from", "additionalProps"].includes(key)) {
113
- result[key] = optionRecord[key];
114
- }
115
- }
116
- return result;
117
- };
118
- if (type.value === "external") {
119
- if (isDangerousProtocol(options.to, router.protocolAllowlist)) {
120
- if (process.env.NODE_ENV !== "production") {
121
- console.warn(`Blocked Link with dangerous protocol: ${options.to}`);
122
- }
123
- const safeProps = {
124
- ...getPropsSafeToSpread(),
125
- ref,
126
- // No href attribute - blocks the dangerous protocol
127
- target: options.target,
128
- disabled: options.disabled,
129
- style: options.style,
130
- class: options.class,
131
- onClick: options.onClick,
132
- onBlur: options.onBlur,
133
- onFocus: options.onFocus,
134
- onMouseEnter: options.onMouseEnter,
135
- onMouseLeave: options.onMouseLeave,
136
- onMouseOver: options.onMouseOver,
137
- onMouseOut: options.onMouseOut,
138
- onTouchStart: options.onTouchStart
139
- };
140
- Object.keys(safeProps).forEach((key) => {
141
- if (safeProps[key] === void 0) {
142
- delete safeProps[key];
143
- }
144
- });
145
- return Vue.computed(() => safeProps);
146
- }
147
- const externalProps = {
148
- ...getPropsSafeToSpread(),
149
- ref,
150
- href: options.to,
151
- target: options.target,
152
- disabled: options.disabled,
153
- style: options.style,
154
- class: options.class,
155
- onClick: options.onClick,
156
- onBlur: options.onBlur,
157
- onFocus: options.onFocus,
158
- onMouseEnter: options.onMouseEnter,
159
- onMouseLeave: options.onMouseLeave,
160
- onMouseOver: options.onMouseOver,
161
- onMouseOut: options.onMouseOut,
162
- onTouchStart: options.onTouchStart
163
- };
164
- Object.keys(externalProps).forEach((key) => {
165
- if (externalProps[key] === void 0) {
166
- delete externalProps[key];
167
- }
168
- });
169
- return Vue.computed(() => externalProps);
170
- }
171
- const handleClick = (e) => {
172
- const elementTarget = e.currentTarget?.getAttribute("target");
173
- const effectiveTarget = options.target !== void 0 ? options.target : elementTarget;
174
- if (!options.disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!effectiveTarget || effectiveTarget === "_self") && e.button === 0) {
175
- if (_options.value.reloadDocument) {
176
- return;
177
- }
178
- e.preventDefault();
179
- isTransitioning.value = true;
180
- const unsub = router.subscribe("onResolved", () => {
181
- unsub();
182
- isTransitioning.value = false;
183
- });
184
- router.navigate({
185
- ..._options.value,
186
- replace: options.replace,
187
- resetScroll: options.resetScroll,
188
- hashScrollIntoView: options.hashScrollIntoView,
189
- startTransition: options.startTransition,
190
- viewTransition: options.viewTransition,
191
- ignoreBlocker: options.ignoreBlocker
192
- });
193
- }
194
- };
195
- const enqueueIntentPreload = (e) => {
196
- if (options.disabled || preload.value !== "intent") return;
197
- if (!preloadDelay.value) {
198
- doPreload();
199
- return;
200
- }
201
- const eventTarget = e.currentTarget || e.target;
202
- if (!eventTarget || timeoutMap.has(eventTarget)) return;
203
- timeoutMap.set(eventTarget, setTimeout(() => {
204
- timeoutMap.delete(eventTarget);
205
- doPreload();
206
- }, preloadDelay.value));
207
- };
208
- const handleTouchStart = (_) => {
209
- if (options.disabled || preload.value !== "intent") return;
210
- doPreload();
211
- };
212
- const handleLeave = (e) => {
213
- if (options.disabled) return;
214
- const eventTarget = e.currentTarget || e.target;
215
- if (eventTarget) {
216
- const id = timeoutMap.get(eventTarget);
217
- clearTimeout(id);
218
- timeoutMap.delete(eventTarget);
219
- }
220
- };
221
- function composeEventHandlers(handlers) {
222
- return (event) => {
223
- for (const handler of handlers) {
224
- if (handler) {
225
- handler(event);
226
- }
227
- }
228
- };
229
- }
230
- const resolvedActiveProps = Vue.computed(() => {
231
- const activeProps = options.activeProps || (() => ({
232
- class: "active"
233
- }));
234
- const props = isActive.value ? typeof activeProps === "function" ? activeProps() : activeProps : {};
235
- return props || {
236
- class: void 0,
237
- style: void 0
238
- };
239
- });
240
- const resolvedInactiveProps = Vue.computed(() => {
241
- const inactiveProps = options.inactiveProps || (() => ({}));
242
- const props = isActive.value ? {} : typeof inactiveProps === "function" ? inactiveProps() : inactiveProps;
243
- return props || {
244
- class: void 0,
245
- style: void 0
246
- };
247
- });
248
- const resolvedClassName = Vue.computed(() => {
249
- const classes = [options.class, resolvedActiveProps.value?.class, resolvedInactiveProps.value?.class].filter(Boolean);
250
- return classes.length ? classes.join(" ") : void 0;
251
- });
252
- const resolvedStyle = Vue.computed(() => {
253
- const result = {};
254
- if (options.style) {
255
- Object.assign(result, options.style);
256
- }
257
- if (resolvedActiveProps.value?.style) {
258
- Object.assign(result, resolvedActiveProps.value.style);
259
- }
260
- if (resolvedInactiveProps.value?.style) {
261
- Object.assign(result, resolvedInactiveProps.value.style);
262
- }
263
- return Object.keys(result).length > 0 ? result : void 0;
264
- });
265
- const href = Vue.computed(() => {
266
- if (options.disabled) {
267
- return void 0;
268
- }
269
- const nextLocation = next.value;
270
- const location = nextLocation?.maskedLocation ?? nextLocation;
271
- const publicHref = location?.publicHref;
272
- if (!publicHref) return void 0;
273
- const external = location?.external;
274
- if (external) return publicHref;
275
- return router.history.createHref(publicHref) || "/";
276
- });
277
- const staticEventHandlers = {
278
- onClick: composeEventHandlers([options.onClick, handleClick]),
279
- onBlur: composeEventHandlers([options.onBlur, handleLeave]),
280
- onFocus: composeEventHandlers([options.onFocus, enqueueIntentPreload]),
281
- onMouseenter: composeEventHandlers([options.onMouseEnter, enqueueIntentPreload]),
282
- onMouseover: composeEventHandlers([options.onMouseOver, enqueueIntentPreload]),
283
- onMouseleave: composeEventHandlers([options.onMouseLeave, handleLeave]),
284
- onMouseout: composeEventHandlers([options.onMouseOut, handleLeave]),
285
- onTouchstart: composeEventHandlers([options.onTouchStart, handleTouchStart])
286
- };
287
- const computedProps = Vue.computed(() => {
288
- const result = {
289
- ...getPropsSafeToSpread(),
290
- href: href.value,
291
- ref,
292
- ...staticEventHandlers,
293
- disabled: !!options.disabled,
294
- target: options.target
295
- };
296
- if (resolvedStyle.value) {
297
- result.style = resolvedStyle.value;
298
- }
299
- if (resolvedClassName.value) {
300
- result.class = resolvedClassName.value;
301
- }
302
- if (options.disabled) {
303
- result.role = "link";
304
- result["aria-disabled"] = true;
305
- }
306
- if (isActive.value) {
307
- result["data-status"] = "active";
308
- result["aria-current"] = "page";
309
- }
310
- if (isTransitioning.value) {
311
- result["data-transitioning"] = "transitioning";
312
- }
313
- const activeP = resolvedActiveProps.value;
314
- const inactiveP = resolvedInactiveProps.value;
315
- for (const key of Object.keys(activeP)) {
316
- if (key !== "class" && key !== "style") {
317
- result[key] = activeP[key];
318
- }
319
- }
320
- for (const key of Object.keys(inactiveP)) {
321
- if (key !== "class" && key !== "style") {
322
- result[key] = inactiveP[key];
323
- }
324
- }
325
- return result;
326
- });
327
- return computedProps;
10
+ const router = useRouter();
11
+ const isTransitioning = Vue.ref(false);
12
+ let hasRenderFetched = false;
13
+ if (!router) {
14
+ console.warn("useRouter must be used inside a <RouterProvider> component!");
15
+ return Vue.computed(() => ({}));
16
+ }
17
+ const type = Vue.computed(() => {
18
+ try {
19
+ new URL(`${options.to}`);
20
+ return "external";
21
+ } catch {
22
+ return "internal";
23
+ }
24
+ });
25
+ const buildLocationKey = useRouterState({ select: (s) => {
26
+ const leaf = s.matches[s.matches.length - 1];
27
+ return {
28
+ search: leaf?.search,
29
+ hash: s.location.hash,
30
+ path: leaf?.pathname
31
+ };
32
+ } });
33
+ const from = useMatches({ select: (matches) => options.from ?? matches[matches.length - 1]?.fullPath });
34
+ const _options = Vue.computed(() => ({
35
+ ...options,
36
+ from: from.value
37
+ }));
38
+ const next = Vue.computed(() => {
39
+ buildLocationKey.value;
40
+ return router.buildLocation(_options.value);
41
+ });
42
+ const preload = Vue.computed(() => {
43
+ if (_options.value.reloadDocument) return false;
44
+ return options.preload ?? router.options.defaultPreload;
45
+ });
46
+ const preloadDelay = Vue.computed(() => options.preloadDelay ?? router.options.defaultPreloadDelay ?? 0);
47
+ const isActive = useRouterState({ select: (s) => {
48
+ const activeOptions = options.activeOptions;
49
+ if (activeOptions?.exact) {
50
+ if (!exactPathTest(s.location.pathname, next.value.pathname, router.basepath)) return false;
51
+ } else {
52
+ const currentPathSplit = removeTrailingSlash(s.location.pathname, router.basepath).split("/");
53
+ if (!(removeTrailingSlash(next.value?.pathname, router.basepath)?.split("/"))?.every((d, i) => d === currentPathSplit[i])) return false;
54
+ }
55
+ if (activeOptions?.includeSearch ?? true) {
56
+ if (!deepEqual(s.location.search, next.value.search, {
57
+ partial: !activeOptions?.exact,
58
+ ignoreUndefined: !activeOptions?.explicitUndefined
59
+ })) return false;
60
+ }
61
+ if (activeOptions?.includeHash) return s.location.hash === next.value.hash;
62
+ return true;
63
+ } });
64
+ const doPreload = () => router.preloadRoute(_options.value).catch((err) => {
65
+ console.warn(err);
66
+ console.warn(preloadWarning);
67
+ });
68
+ const preloadViewportIoCallback = (entry) => {
69
+ if (entry?.isIntersecting) doPreload();
70
+ };
71
+ const ref = Vue.ref(null);
72
+ useIntersectionObserver(ref, preloadViewportIoCallback, { rootMargin: "100px" }, { disabled: () => !!options.disabled || !(preload.value === "viewport") });
73
+ Vue.effect(() => {
74
+ if (hasRenderFetched) return;
75
+ if (!options.disabled && preload.value === "render") {
76
+ doPreload();
77
+ hasRenderFetched = true;
78
+ }
79
+ });
80
+ const getPropsSafeToSpread = () => {
81
+ const result = {};
82
+ const optionRecord = options;
83
+ for (const key in options) if (![
84
+ "activeProps",
85
+ "inactiveProps",
86
+ "activeOptions",
87
+ "to",
88
+ "preload",
89
+ "preloadDelay",
90
+ "hashScrollIntoView",
91
+ "replace",
92
+ "startTransition",
93
+ "resetScroll",
94
+ "viewTransition",
95
+ "children",
96
+ "target",
97
+ "disabled",
98
+ "style",
99
+ "class",
100
+ "onClick",
101
+ "onBlur",
102
+ "onFocus",
103
+ "onMouseEnter",
104
+ "onMouseLeave",
105
+ "onMouseOver",
106
+ "onMouseOut",
107
+ "onTouchStart",
108
+ "ignoreBlocker",
109
+ "params",
110
+ "search",
111
+ "hash",
112
+ "state",
113
+ "mask",
114
+ "reloadDocument",
115
+ "_asChild",
116
+ "from",
117
+ "additionalProps"
118
+ ].includes(key)) result[key] = optionRecord[key];
119
+ return result;
120
+ };
121
+ if (type.value === "external") {
122
+ if (isDangerousProtocol(options.to, router.protocolAllowlist)) {
123
+ if (process.env.NODE_ENV !== "production") console.warn(`Blocked Link with dangerous protocol: ${options.to}`);
124
+ const safeProps = {
125
+ ...getPropsSafeToSpread(),
126
+ ref,
127
+ target: options.target,
128
+ disabled: options.disabled,
129
+ style: options.style,
130
+ class: options.class,
131
+ onClick: options.onClick,
132
+ onBlur: options.onBlur,
133
+ onFocus: options.onFocus,
134
+ onMouseEnter: options.onMouseEnter,
135
+ onMouseLeave: options.onMouseLeave,
136
+ onMouseOver: options.onMouseOver,
137
+ onMouseOut: options.onMouseOut,
138
+ onTouchStart: options.onTouchStart
139
+ };
140
+ Object.keys(safeProps).forEach((key) => {
141
+ if (safeProps[key] === void 0) delete safeProps[key];
142
+ });
143
+ return Vue.computed(() => safeProps);
144
+ }
145
+ const externalProps = {
146
+ ...getPropsSafeToSpread(),
147
+ ref,
148
+ href: options.to,
149
+ target: options.target,
150
+ disabled: options.disabled,
151
+ style: options.style,
152
+ class: options.class,
153
+ onClick: options.onClick,
154
+ onBlur: options.onBlur,
155
+ onFocus: options.onFocus,
156
+ onMouseEnter: options.onMouseEnter,
157
+ onMouseLeave: options.onMouseLeave,
158
+ onMouseOver: options.onMouseOver,
159
+ onMouseOut: options.onMouseOut,
160
+ onTouchStart: options.onTouchStart
161
+ };
162
+ Object.keys(externalProps).forEach((key) => {
163
+ if (externalProps[key] === void 0) delete externalProps[key];
164
+ });
165
+ return Vue.computed(() => externalProps);
166
+ }
167
+ const handleClick = (e) => {
168
+ const elementTarget = e.currentTarget?.getAttribute("target");
169
+ const effectiveTarget = options.target !== void 0 ? options.target : elementTarget;
170
+ if (!options.disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!effectiveTarget || effectiveTarget === "_self") && e.button === 0) {
171
+ if (_options.value.reloadDocument) return;
172
+ e.preventDefault();
173
+ isTransitioning.value = true;
174
+ const unsub = router.subscribe("onResolved", () => {
175
+ unsub();
176
+ isTransitioning.value = false;
177
+ });
178
+ router.navigate({
179
+ ..._options.value,
180
+ replace: options.replace,
181
+ resetScroll: options.resetScroll,
182
+ hashScrollIntoView: options.hashScrollIntoView,
183
+ startTransition: options.startTransition,
184
+ viewTransition: options.viewTransition,
185
+ ignoreBlocker: options.ignoreBlocker
186
+ });
187
+ }
188
+ };
189
+ const enqueueIntentPreload = (e) => {
190
+ if (options.disabled || preload.value !== "intent") return;
191
+ if (!preloadDelay.value) {
192
+ doPreload();
193
+ return;
194
+ }
195
+ const eventTarget = e.currentTarget || e.target;
196
+ if (!eventTarget || timeoutMap.has(eventTarget)) return;
197
+ timeoutMap.set(eventTarget, setTimeout(() => {
198
+ timeoutMap.delete(eventTarget);
199
+ doPreload();
200
+ }, preloadDelay.value));
201
+ };
202
+ const handleTouchStart = (_) => {
203
+ if (options.disabled || preload.value !== "intent") return;
204
+ doPreload();
205
+ };
206
+ const handleLeave = (e) => {
207
+ if (options.disabled) return;
208
+ const eventTarget = e.currentTarget || e.target;
209
+ if (eventTarget) {
210
+ const id = timeoutMap.get(eventTarget);
211
+ clearTimeout(id);
212
+ timeoutMap.delete(eventTarget);
213
+ }
214
+ };
215
+ function composeEventHandlers(handlers) {
216
+ return (event) => {
217
+ for (const handler of handlers) if (handler) handler(event);
218
+ };
219
+ }
220
+ const resolvedActiveProps = Vue.computed(() => {
221
+ const activeProps = options.activeProps || (() => ({ class: "active" }));
222
+ return (isActive.value ? typeof activeProps === "function" ? activeProps() : activeProps : {}) || {
223
+ class: void 0,
224
+ style: void 0
225
+ };
226
+ });
227
+ const resolvedInactiveProps = Vue.computed(() => {
228
+ const inactiveProps = options.inactiveProps || (() => ({}));
229
+ return (isActive.value ? {} : typeof inactiveProps === "function" ? inactiveProps() : inactiveProps) || {
230
+ class: void 0,
231
+ style: void 0
232
+ };
233
+ });
234
+ const resolvedClassName = Vue.computed(() => {
235
+ const classes = [
236
+ options.class,
237
+ resolvedActiveProps.value?.class,
238
+ resolvedInactiveProps.value?.class
239
+ ].filter(Boolean);
240
+ return classes.length ? classes.join(" ") : void 0;
241
+ });
242
+ const resolvedStyle = Vue.computed(() => {
243
+ const result = {};
244
+ if (options.style) Object.assign(result, options.style);
245
+ if (resolvedActiveProps.value?.style) Object.assign(result, resolvedActiveProps.value.style);
246
+ if (resolvedInactiveProps.value?.style) Object.assign(result, resolvedInactiveProps.value.style);
247
+ return Object.keys(result).length > 0 ? result : void 0;
248
+ });
249
+ const href = Vue.computed(() => {
250
+ if (options.disabled) return;
251
+ const nextLocation = next.value;
252
+ const location = nextLocation?.maskedLocation ?? nextLocation;
253
+ const publicHref = location?.publicHref;
254
+ if (!publicHref) return void 0;
255
+ if (location?.external) return publicHref;
256
+ return router.history.createHref(publicHref) || "/";
257
+ });
258
+ const staticEventHandlers = {
259
+ onClick: composeEventHandlers([options.onClick, handleClick]),
260
+ onBlur: composeEventHandlers([options.onBlur, handleLeave]),
261
+ onFocus: composeEventHandlers([options.onFocus, enqueueIntentPreload]),
262
+ onMouseenter: composeEventHandlers([options.onMouseEnter, enqueueIntentPreload]),
263
+ onMouseover: composeEventHandlers([options.onMouseOver, enqueueIntentPreload]),
264
+ onMouseleave: composeEventHandlers([options.onMouseLeave, handleLeave]),
265
+ onMouseout: composeEventHandlers([options.onMouseOut, handleLeave]),
266
+ onTouchstart: composeEventHandlers([options.onTouchStart, handleTouchStart])
267
+ };
268
+ return Vue.computed(() => {
269
+ const result = {
270
+ ...getPropsSafeToSpread(),
271
+ href: href.value,
272
+ ref,
273
+ ...staticEventHandlers,
274
+ disabled: !!options.disabled,
275
+ target: options.target
276
+ };
277
+ if (resolvedStyle.value) result.style = resolvedStyle.value;
278
+ if (resolvedClassName.value) result.class = resolvedClassName.value;
279
+ if (options.disabled) {
280
+ result.role = "link";
281
+ result["aria-disabled"] = true;
282
+ }
283
+ if (isActive.value) {
284
+ result["data-status"] = "active";
285
+ result["aria-current"] = "page";
286
+ }
287
+ if (isTransitioning.value) result["data-transitioning"] = "transitioning";
288
+ const activeP = resolvedActiveProps.value;
289
+ const inactiveP = resolvedInactiveProps.value;
290
+ for (const key of Object.keys(activeP)) if (key !== "class" && key !== "style") result[key] = activeP[key];
291
+ for (const key of Object.keys(inactiveP)) if (key !== "class" && key !== "style") result[key] = inactiveP[key];
292
+ return result;
293
+ });
328
294
  }
329
295
  function createLink(Comp) {
330
- return Vue.defineComponent({
331
- name: "CreatedLink",
332
- inheritAttrs: false,
333
- setup(_, {
334
- attrs,
335
- slots
336
- }) {
337
- return () => Vue.h(LinkImpl, {
338
- ...attrs,
339
- _asChild: Comp
340
- }, slots);
341
- }
342
- });
296
+ return Vue.defineComponent({
297
+ name: "CreatedLink",
298
+ inheritAttrs: false,
299
+ setup(_, { attrs, slots }) {
300
+ return () => Vue.h(LinkImpl, {
301
+ ...attrs,
302
+ _asChild: Comp
303
+ }, slots);
304
+ }
305
+ });
343
306
  }
344
- const LinkImpl = Vue.defineComponent({
345
- name: "Link",
346
- inheritAttrs: false,
347
- props: ["_asChild", "to", "preload", "preloadDelay", "activeProps", "inactiveProps", "activeOptions", "from", "search", "params", "hash", "state", "mask", "reloadDocument", "disabled", "additionalProps", "viewTransition", "resetScroll", "startTransition", "hashScrollIntoView", "replace", "ignoreBlocker", "target"],
348
- setup(props, {
349
- attrs,
350
- slots
351
- }) {
352
- const allProps = {
353
- ...props,
354
- ...attrs
355
- };
356
- const linkPropsComputed = useLinkProps(allProps);
357
- return () => {
358
- const Component = props._asChild || "a";
359
- const linkProps = linkPropsComputed.value;
360
- const isActive = linkProps["data-status"] === "active";
361
- const isTransitioning = linkProps["data-transitioning"] === "transitioning";
362
- const slotContent = slots.default ? slots.default({
363
- isActive,
364
- isTransitioning
365
- }) : [];
366
- if (Component === "svg") {
367
- const svgLinkProps = {
368
- ...linkProps
369
- };
370
- delete svgLinkProps.class;
371
- return Vue.h("svg", {}, [Vue.h("a", svgLinkProps, slotContent)]);
372
- }
373
- if (typeof Component !== "string") {
374
- return Vue.h(Component, {
375
- ...linkProps,
376
- children: slotContent
377
- }, slotContent);
378
- }
379
- return Vue.h(Component, linkProps, slotContent);
380
- };
381
- }
307
+ var LinkImpl = Vue.defineComponent({
308
+ name: "Link",
309
+ inheritAttrs: false,
310
+ props: [
311
+ "_asChild",
312
+ "to",
313
+ "preload",
314
+ "preloadDelay",
315
+ "activeProps",
316
+ "inactiveProps",
317
+ "activeOptions",
318
+ "from",
319
+ "search",
320
+ "params",
321
+ "hash",
322
+ "state",
323
+ "mask",
324
+ "reloadDocument",
325
+ "disabled",
326
+ "additionalProps",
327
+ "viewTransition",
328
+ "resetScroll",
329
+ "startTransition",
330
+ "hashScrollIntoView",
331
+ "replace",
332
+ "ignoreBlocker",
333
+ "target"
334
+ ],
335
+ setup(props, { attrs, slots }) {
336
+ const linkPropsComputed = useLinkProps({
337
+ ...props,
338
+ ...attrs
339
+ });
340
+ return () => {
341
+ const Component = props._asChild || "a";
342
+ const linkProps = linkPropsComputed.value;
343
+ const isActive = linkProps["data-status"] === "active";
344
+ const isTransitioning = linkProps["data-transitioning"] === "transitioning";
345
+ const slotContent = slots.default ? slots.default({
346
+ isActive,
347
+ isTransitioning
348
+ }) : [];
349
+ if (Component === "svg") {
350
+ const svgLinkProps = { ...linkProps };
351
+ delete svgLinkProps.class;
352
+ return Vue.h("svg", {}, [Vue.h("a", svgLinkProps, slotContent)]);
353
+ }
354
+ if (typeof Component !== "string") return Vue.h(Component, {
355
+ ...linkProps,
356
+ children: slotContent
357
+ }, slotContent);
358
+ return Vue.h(Component, linkProps, slotContent);
359
+ };
360
+ }
382
361
  });
383
- const Link = LinkImpl;
362
+ /**
363
+ * Link component with proper TypeScript generics support
364
+ */
365
+ var Link = LinkImpl;
384
366
  function isCtrlEvent(e) {
385
- return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
367
+ return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
386
368
  }
387
- const linkOptions = (options) => {
388
- return options;
389
- };
390
- export {
391
- Link,
392
- createLink,
393
- linkOptions,
394
- useLinkProps
369
+ var linkOptions = (options) => {
370
+ return options;
395
371
  };
396
- //# sourceMappingURL=link.js.map
372
+ //#endregion
373
+ export { Link, createLink, linkOptions, useLinkProps };
374
+
375
+ //# sourceMappingURL=link.js.map