navigation.ts73 lines · main
1import { useRouter } from 'next/navigation'
2import type { NextRouter } from 'next/router'
3
4import { BASE_PATH } from './constants'
5
6type Router = NextRouter | ReturnType<typeof useRouter>
7
8const MIDDLE_MOUSE_BUTTON = 1
9
10/**
11 * Creates a navigation handler that supports keyboard, modifier clicks, and middle mouse button.
12 *
13 * This is a curried function that takes a URL and router, and returns an event handler that:
14 * - Handles keyboard navigation (Enter/Space keys)
15 * - Opens in new tab on Cmd/Ctrl + click
16 * - Opens in new tab on middle mouse button click
17 * - Performs normal navigation on regular click
18 *
19 * @param url - The relative URL to navigate to (e.g., "/project/123/functions/my-function")
20 * @param router - Next.js router instance (supports both Pages Router and App Router)
21 * @returns Event handler function for onClick, onAuxClick, and onKeyDown
22 *
23 * @example
24 * ```tsx
25 * const router = useRouter()
26 * const handleNavigation = createNavigationHandler(`/project/${ref}/functions/${slug}`, router)
27 *
28 * <TableRow
29 * onClick={handleNavigation}
30 * onAuxClick={handleNavigation}
31 * onKeyDown={handleNavigation}
32 * tabIndex={0}
33 * />
34 * ```
35 */
36export const createNavigationHandler = (url: string, router: Router) => {
37 return (event: React.MouseEvent | React.KeyboardEvent) => {
38 // Handle keyboard events
39 if ('key' in event) {
40 if (event.key === 'Enter' || event.key === ' ') {
41 event.preventDefault()
42
43 const isModifierKey = event.metaKey || event.ctrlKey
44 if (isModifierKey) {
45 window.open(`${BASE_PATH}${url}`, '_blank')
46 } else {
47 router.push(url)
48 }
49 }
50 return
51 }
52
53 // Handle Cmd/Ctrl + left click (modifier click)
54 const isModifierClick =
55 'button' in event && event.button === 0 && (event.metaKey || event.ctrlKey)
56 if (isModifierClick) {
57 event.preventDefault()
58 window.open(`${BASE_PATH}${url}`, '_blank')
59 return
60 }
61
62 // Handle middle mouse button click
63 const isMiddleClick = 'button' in event && event.button === MIDDLE_MOUSE_BUTTON
64 if (isMiddleClick) {
65 event.preventDefault()
66 window.open(`${BASE_PATH}${url}`, '_blank')
67 return
68 }
69
70 // Handle regular left click
71 router.push(url)
72 }
73}