@esmx/router 3.0.0-rc.60 → 3.0.0-rc.61

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.
@@ -17,15 +17,14 @@ function getEventTypeList(eventType) {
17
17
  const validEvents = events.filter((type) => typeof type === "string").map((type) => type.trim()).filter(Boolean);
18
18
  return validEvents.length ? validEvents : ["click"];
19
19
  }
20
- function guardEvent(e) {
21
- var _a, _b, _c;
22
- if (!e) return;
23
- if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return;
24
- if (e.defaultPrevented) return;
25
- if (e.button !== void 0 && e.button !== 0) return;
26
- const target = (_c = (_b = (_a = e.currentTarget) == null ? void 0 : _a.getAttribute) == null ? void 0 : _b.call(_a, "target")) != null ? _c : "";
27
- if (/\b_blank\b/i.test(target)) return;
28
- if (e.preventDefault) e.preventDefault();
20
+ function shouldHandleNavigation(e) {
21
+ var _a;
22
+ if (e.defaultPrevented) return false;
23
+ if (e instanceof MouseEvent) {
24
+ if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return false;
25
+ if (e.button !== void 0 && e.button !== 0) return false;
26
+ }
27
+ (_a = e.preventDefault) == null ? void 0 : _a.call(e);
29
28
  return true;
30
29
  }
31
30
  async function executeNavigation(router, props, linkType) {
@@ -54,10 +53,9 @@ async function executeNavigation(router, props, linkType) {
54
53
  }
55
54
  function createNavigateFunction(router, props, navigationType) {
56
55
  return async (e) => {
57
- var _a;
58
- const eventHandler = (_a = props.eventHandler) != null ? _a : guardEvent;
59
- if (!eventHandler(e)) return;
60
- await executeNavigation(router, props, navigationType);
56
+ if (shouldHandleNavigation(e)) {
57
+ await executeNavigation(router, props, navigationType);
58
+ }
61
59
  };
62
60
  }
63
61
  function computeAttributes(href, navigationType, isExternal, isActive, isExactActive, activeClass) {
@@ -88,13 +86,18 @@ function computeAttributes(href, navigationType, isExternal, isActive, isExactAc
88
86
  }
89
87
  return attributes;
90
88
  }
91
- function createEventHandlersGenerator(navigate, eventTypes) {
92
- return (nameTransform) => {
89
+ function createEventHandlersGenerator(router, props, navigationType, eventTypes) {
90
+ return (format) => {
93
91
  const handlers = {};
92
+ const navigate = createNavigateFunction(router, props, navigationType);
94
93
  eventTypes.forEach((eventType) => {
95
94
  var _a;
96
- const eventName = (_a = nameTransform == null ? void 0 : nameTransform(eventType)) != null ? _a : eventType.toLowerCase();
97
- handlers[eventName] = navigate;
95
+ const eventName = (_a = format == null ? void 0 : format(eventType)) != null ? _a : eventType.toLowerCase();
96
+ handlers[eventName] = (event) => {
97
+ var _a2;
98
+ (_a2 = props.beforeNavigate) == null ? void 0 : _a2.call(props, event, eventType);
99
+ return navigate(event);
100
+ };
98
101
  });
99
102
  return handlers;
100
103
  };
@@ -106,7 +109,6 @@ export function createLinkResolver(router, props) {
106
109
  const isActive = router.isRouteMatched(route, props.exact);
107
110
  const isExactActive = router.isRouteMatched(route, "exact");
108
111
  const isExternal = route.url.origin !== router.route.url.origin;
109
- const navigate = createNavigateFunction(router, props, type);
110
112
  const attributes = computeAttributes(
111
113
  href,
112
114
  type,
@@ -116,7 +118,13 @@ export function createLinkResolver(router, props) {
116
118
  props.activeClass
117
119
  );
118
120
  const eventTypes = getEventTypeList(props.event || "click");
119
- const getEventHandlers = createEventHandlersGenerator(navigate, eventTypes);
121
+ const createEventHandlers = createEventHandlersGenerator(
122
+ router,
123
+ props,
124
+ type,
125
+ eventTypes
126
+ );
127
+ const navigate = createNavigateFunction(router, props, type);
120
128
  return {
121
129
  route,
122
130
  type,
@@ -126,6 +134,6 @@ export function createLinkResolver(router, props) {
126
134
  tag: props.tag || "a",
127
135
  attributes,
128
136
  navigate,
129
- getEventHandlers
137
+ createEventHandlers
130
138
  };
131
139
  }
package/dist/router.d.ts CHANGED
@@ -103,7 +103,7 @@ export declare class Router {
103
103
  * linkData.navigate(); // Programmatic navigation
104
104
  *
105
105
  * // Get event handlers for React
106
- * const handlers = linkData.getEventHandlers(name => `on${name.charAt(0).toUpperCase() + name.slice(1)}`);
106
+ * const handlers = linkData.createEventHandlers(name => `on${name.charAt(0).toUpperCase() + name.slice(1)}`);
107
107
  * // handlers.onClick for React
108
108
  * ```
109
109
  */
package/dist/router.mjs CHANGED
@@ -200,7 +200,7 @@ export class Router {
200
200
  * linkData.navigate(); // Programmatic navigation
201
201
  *
202
202
  * // Get event handlers for React
203
- * const handlers = linkData.getEventHandlers(name => `on${name.charAt(0).toUpperCase() + name.slice(1)}`);
203
+ * const handlers = linkData.createEventHandlers(name => `on${name.charAt(0).toUpperCase() + name.slice(1)}`);
204
204
  * // handlers.onClick for React
205
205
  * ```
206
206
  */
package/dist/types.d.ts CHANGED
@@ -219,7 +219,6 @@ export interface RouterLinkAttributes {
219
219
  target?: '_blank';
220
220
  rel?: string;
221
221
  }
222
- export type RouterLinkEventHandler = <E extends Event = MouseEvent>(event: E) => boolean | void;
223
222
  /**
224
223
  * Framework-agnostic link configuration interface
225
224
  */
@@ -235,7 +234,15 @@ export interface RouterLinkProps {
235
234
  event?: string | string[];
236
235
  tag?: string;
237
236
  layerOptions?: RouteLayerOptions;
238
- eventHandler?: RouterLinkEventHandler;
237
+ /**
238
+ * Hook function called before navigation occurs
239
+ * @param event - The DOM event that triggered the navigation
240
+ * @param eventName - The name of the event that triggered navigation
241
+ *
242
+ * Can prevent default event to block the default navigation handling logic.
243
+ * Call event.preventDefault() to stop the navigation from proceeding.
244
+ */
245
+ beforeNavigate?: (event: Event, eventName: string) => void;
239
246
  }
240
247
  /**
241
248
  * Framework-agnostic link resolution result
@@ -248,6 +255,6 @@ export interface RouterLinkResolved {
248
255
  isExternal: boolean;
249
256
  tag: string;
250
257
  attributes: RouterLinkAttributes;
251
- navigate: (e?: Event) => Promise<void>;
252
- getEventHandlers: (nameTransform?: (eventType: string) => string) => Record<string, (e: Event) => Promise<void> | undefined>;
258
+ navigate: (e: Event) => Promise<void>;
259
+ createEventHandlers: (format?: (eventType: string) => string) => Record<string, (e: Event) => Promise<void>>;
253
260
  }
package/package.json CHANGED
@@ -39,7 +39,7 @@
39
39
  "unbuild": "3.6.0",
40
40
  "vitest": "3.2.4"
41
41
  },
42
- "version": "3.0.0-rc.60",
42
+ "version": "3.0.0-rc.61",
43
43
  "type": "module",
44
44
  "private": false,
45
45
  "exports": {
@@ -58,5 +58,5 @@
58
58
  "template",
59
59
  "public"
60
60
  ],
61
- "gitHead": "615e91c617e0a58796c591643c6a2e1d2a1f0a76"
61
+ "gitHead": "cce4468980071fe7a0a1e5f4941f1750cacc5215"
62
62
  }
@@ -41,29 +41,22 @@ function getEventTypeList(eventType: unknown | unknown[]): string[] {
41
41
  }
42
42
 
43
43
  /**
44
- * Event guard check - determines if the router should handle the navigation
44
+ * Navigation event handler called before navigation - determines if the router should handle the navigation
45
45
  *
46
- * Returns !0: Let browser handle default behavior (normal link navigation)
47
- * Returns 0: Router takes over navigation, prevents default browser behavior
46
+ * Returns false: Let browser handle default behavior (normal link navigation)
47
+ * Returns true: Router takes over navigation, prevents default browser behavior
48
48
  *
49
49
  * This function intelligently decides when to let the browser handle clicks
50
50
  * (like Ctrl+click for new tabs) vs when to use SPA routing
51
51
  */
52
- function guardEvent(e?: Event & Partial<MouseEvent>): true | undefined {
53
- if (!e) return;
54
- // don't redirect with control keys
55
- if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return;
56
- // don't redirect when preventDefault called
57
- if (e.defaultPrevented) return;
58
- // don't redirect on right click
59
- if (e.button !== undefined && e.button !== 0) return;
60
- // don't redirect if `target="_blank"`
61
- // @ts-expect-error getAttribute exists
62
- const target = e.currentTarget?.getAttribute?.('target') ?? '';
63
- if (/\b_blank\b/i.test(target)) return;
64
- // Prevent default browser navigation to enable SPA routing
65
- // Note: this may be a Weex event which doesn't have this method
66
- if (e.preventDefault) e.preventDefault();
52
+ function shouldHandleNavigation(e: Event): boolean {
53
+ if (e.defaultPrevented) return false;
54
+ if (e instanceof MouseEvent) {
55
+ if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return false;
56
+ if (e.button !== undefined && e.button !== 0) return false;
57
+ }
58
+
59
+ e.preventDefault?.();
67
60
 
68
61
  return true;
69
62
  }
@@ -112,12 +105,11 @@ function createNavigateFunction(
112
105
  router: Router,
113
106
  props: RouterLinkProps,
114
107
  navigationType: RouterLinkType
115
- ): (e?: Event) => Promise<void> {
116
- return async (e?: Event): Promise<void> => {
117
- const eventHandler = props.eventHandler ?? guardEvent;
118
- if (!eventHandler(e!)) return;
119
-
120
- await executeNavigation(router, props, navigationType);
108
+ ): RouterLinkResolved['navigate'] {
109
+ return async (e: Event): Promise<void> => {
110
+ if (shouldHandleNavigation(e)) {
111
+ await executeNavigation(router, props, navigationType);
112
+ }
121
113
  };
122
114
  }
123
115
 
@@ -173,18 +165,21 @@ function computeAttributes(
173
165
  * Create event handlers generator function
174
166
  */
175
167
  function createEventHandlersGenerator(
176
- navigate: (e: Event) => Promise<void>,
168
+ router: Router,
169
+ props: RouterLinkProps,
170
+ navigationType: RouterLinkType,
177
171
  eventTypes: string[]
178
- ): (
179
- nameTransform?: (eventType: string) => string
180
- ) => Record<string, (e: Event) => Promise<void>> {
181
- return (nameTransform?: (eventType: string) => string) => {
172
+ ): RouterLinkResolved['createEventHandlers'] {
173
+ return (format?: (eventType: string) => string) => {
182
174
  const handlers: Record<string, (e: Event) => Promise<void>> = {};
175
+ const navigate = createNavigateFunction(router, props, navigationType);
183
176
 
184
177
  eventTypes.forEach((eventType) => {
185
- const eventName =
186
- nameTransform?.(eventType) ?? eventType.toLowerCase();
187
- handlers[eventName] = navigate;
178
+ const eventName = format?.(eventType) ?? eventType.toLowerCase();
179
+ handlers[eventName] = (event) => {
180
+ props.beforeNavigate?.(event, eventType);
181
+ return navigate(event);
182
+ };
188
183
  });
189
184
 
190
185
  return handlers;
@@ -210,8 +205,6 @@ export function createLinkResolver(
210
205
  const isExactActive = router.isRouteMatched(route, 'exact');
211
206
  const isExternal = route.url.origin !== router.route.url.origin;
212
207
 
213
- const navigate = createNavigateFunction(router, props, type);
214
-
215
208
  const attributes = computeAttributes(
216
209
  href,
217
210
  type,
@@ -222,7 +215,14 @@ export function createLinkResolver(
222
215
  );
223
216
 
224
217
  const eventTypes = getEventTypeList(props.event || 'click');
225
- const getEventHandlers = createEventHandlersGenerator(navigate, eventTypes);
218
+ const createEventHandlers = createEventHandlersGenerator(
219
+ router,
220
+ props,
221
+ type,
222
+ eventTypes
223
+ );
224
+
225
+ const navigate = createNavigateFunction(router, props, type);
226
226
 
227
227
  return {
228
228
  route,
@@ -233,6 +233,6 @@ export function createLinkResolver(
233
233
  tag: props.tag || 'a',
234
234
  attributes,
235
235
  navigate,
236
- getEventHandlers
236
+ createEventHandlers
237
237
  };
238
238
  }
package/src/router.ts CHANGED
@@ -225,7 +225,7 @@ export class Router {
225
225
  * linkData.navigate(); // Programmatic navigation
226
226
  *
227
227
  * // Get event handlers for React
228
- * const handlers = linkData.getEventHandlers(name => `on${name.charAt(0).toUpperCase() + name.slice(1)}`);
228
+ * const handlers = linkData.createEventHandlers(name => `on${name.charAt(0).toUpperCase() + name.slice(1)}`);
229
229
  * // handlers.onClick for React
230
230
  * ```
231
231
  */
package/src/types.ts CHANGED
@@ -308,10 +308,6 @@ export interface RouterLinkAttributes {
308
308
  rel?: string;
309
309
  }
310
310
 
311
- export type RouterLinkEventHandler = <E extends Event = MouseEvent>(
312
- event: E
313
- ) => boolean | void;
314
-
315
311
  /**
316
312
  * Framework-agnostic link configuration interface
317
313
  */
@@ -327,7 +323,15 @@ export interface RouterLinkProps {
327
323
  event?: string | string[];
328
324
  tag?: string;
329
325
  layerOptions?: RouteLayerOptions;
330
- eventHandler?: RouterLinkEventHandler;
326
+ /**
327
+ * Hook function called before navigation occurs
328
+ * @param event - The DOM event that triggered the navigation
329
+ * @param eventName - The name of the event that triggered navigation
330
+ *
331
+ * Can prevent default event to block the default navigation handling logic.
332
+ * Call event.preventDefault() to stop the navigation from proceeding.
333
+ */
334
+ beforeNavigate?: (event: Event, eventName: string) => void;
331
335
  }
332
336
 
333
337
  /**
@@ -346,10 +350,10 @@ export interface RouterLinkResolved {
346
350
  attributes: RouterLinkAttributes;
347
351
 
348
352
  // Navigation function
349
- navigate: (e?: Event) => Promise<void>;
353
+ navigate: (e: Event) => Promise<void>;
350
354
 
351
355
  // Event handling
352
- getEventHandlers: (
353
- nameTransform?: (eventType: string) => string
354
- ) => Record<string, (e: Event) => Promise<void> | undefined>;
356
+ createEventHandlers: (
357
+ format?: (eventType: string) => string
358
+ ) => Record<string, (e: Event) => Promise<void>>;
355
359
  }