Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions packages/core/src/navigationEvents.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
type MouseNavigationEvent = Pick<
MouseEvent,
'altKey' | 'ctrlKey' | 'shiftKey' | 'metaKey' | 'button' | 'currentTarget' | 'defaultPrevented' | 'target'
>

type KeyboardNavigationEvent = Pick<KeyboardEvent, 'currentTarget' | 'defaultPrevented' | 'key' | 'target'>

function isContentEditableOrPrevented(event: KeyboardNavigationEvent | MouseNavigationEvent): boolean {
return (event.target instanceof HTMLElement && event.target.isContentEditable) || event.defaultPrevented
}

/**
* Determine if this mouse event should be intercepted for navigation purposes.
* Links with modifier keys or non-left clicks should not be intercepted.
* Content editable elements and prevented events are ignored.
*/
export function shouldIntercept(
event: Pick<
MouseEvent,
'altKey' | 'ctrlKey' | 'defaultPrevented' | 'target' | 'currentTarget' | 'metaKey' | 'shiftKey' | 'button'
>,
): boolean {
export function shouldIntercept(event: MouseNavigationEvent): boolean {
const isLink = (event.currentTarget as HTMLElement).tagName.toLowerCase() === 'a'

return !(
(event.target && (event?.target as HTMLElement).isContentEditable) ||
event.defaultPrevented ||
isContentEditableOrPrevented(event) ||
(isLink && event.altKey) ||
(isLink && event.ctrlKey) ||
(isLink && event.metaKey) ||
Expand All @@ -27,8 +32,8 @@ export function shouldIntercept(
* Enter triggers navigation for both links and buttons currently.
* Space only triggers navigation for buttons specifically.
*/
export function shouldNavigate(event: Pick<KeyboardEvent, 'key' | 'currentTarget'>): boolean {
export function shouldNavigate(event: KeyboardNavigationEvent): boolean {
const isButton = (event.currentTarget as HTMLElement).tagName.toLowerCase() === 'button'

return event.key === 'Enter' || (isButton && event.key === ' ')
return !isContentEditableOrPrevented(event) && (event.key === 'Enter' || (isButton && event.key === ' '))
}
16 changes: 8 additions & 8 deletions packages/react/src/Link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const noop = () => undefined

interface BaseInertiaLinkProps extends LinkComponentBaseProps {
as?: ElementType
onClick?: (event: React.MouseEvent<Element>) => void
onClick?: (event: React.MouseEvent) => void
}

export type InertiaLinkProps = BaseInertiaLinkProps &
Expand Down Expand Up @@ -181,7 +181,7 @@ const Link = forwardRef<unknown, InertiaLinkProps>(
}, prefetchModes)

const regularEvents = {
onClick: (event) => {
onClick: (event: React.MouseEvent) => {
onClick(event)

if (shouldIntercept(event)) {
Expand All @@ -205,29 +205,29 @@ const Link = forwardRef<unknown, InertiaLinkProps>(
}

const prefetchClickEvents = {
onMouseDown: (event) => {
onMouseDown: (event: React.MouseEvent) => {
if (shouldIntercept(event)) {
event.preventDefault()
doPrefetch()
}
},
onKeyDown: (event) => {
if (shouldIntercept(event) && shouldNavigate(event)) {
onKeyDown: (event: React.KeyboardEvent) => {
if (shouldNavigate(event)) {
event.preventDefault()
doPrefetch()
}
},
onMouseUp: (event) => {
onMouseUp: (event: React.MouseEvent) => {
event.preventDefault()
router.visit(url, visitParams)
},
onKeyUp: (event) => {
onKeyUp: (event: React.KeyboardEvent) => {
if (shouldNavigate(event)) {
event.preventDefault()
router.visit(url, visitParams)
}
},
onClick: (event) => {
onClick: (event: React.MouseEvent) => {
onClick(event)

if (shouldIntercept(event)) {
Expand Down
14 changes: 7 additions & 7 deletions packages/svelte/src/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function link(
let visitParams: VisitOptions

const regularEvents: ActionEventHandlers = {
click: (event) => {
click: (event: MouseEvent) => {
if (shouldIntercept(event)) {
event.preventDefault()
router.visit(href, visitParams)
Expand All @@ -77,29 +77,29 @@ function link(
}

const prefetchClickEvents: ActionEventHandlers = {
mousedown: (event) => {
mousedown: (event: MouseEvent) => {
if (shouldIntercept(event)) {
event.preventDefault()
prefetch()
}
},
keydown: (event) => {
if (shouldIntercept(event) && shouldNavigate(event)) {
keydown: (event: KeyboardEvent) => {
if (shouldNavigate(event)) {
event.preventDefault()
prefetch()
}
},
mouseup: (event) => {
mouseup: (event: MouseEvent) => {
event.preventDefault()
router.visit(href, visitParams)
},
keyup: (event) => {
keyup: (event: KeyboardEvent) => {
if (shouldNavigate(event)) {
event.preventDefault()
router.visit(href, visitParams)
}
},
click: (event) => {
click: (event: MouseEvent) => {
if (shouldIntercept(event)) {
// Let the mouseup/keyup event handle the visit
event.preventDefault()
Expand Down
14 changes: 7 additions & 7 deletions packages/vue3/src/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ const Link: InertiaLink = defineComponent({
}

const regularEvents = {
onClick: (event) => {
onClick: (event: MouseEvent) => {
if (shouldIntercept(event)) {
event.preventDefault()
router.visit(href.value, visitParams.value)
Expand All @@ -279,29 +279,29 @@ const Link: InertiaLink = defineComponent({
}

const prefetchClickEvents = {
onMousedown: (event) => {
onMousedown: (event: MouseEvent) => {
if (shouldIntercept(event)) {
event.preventDefault()
prefetch()
}
},
onKeydown: (event) => {
if (shouldIntercept(event) && shouldNavigate(event)) {
onKeydown: (event: KeyboardEvent) => {
if (shouldNavigate(event)) {
event.preventDefault()
prefetch()
}
},
onMouseup: (event) => {
onMouseup: (event: MouseEvent) => {
event.preventDefault()
router.visit(href.value, visitParams.value)
},
onKeyup: (event) => {
onKeyup: (event: KeyboardEvent) => {
if (shouldNavigate(event)) {
event.preventDefault()
router.visit(href.value, visitParams.value)
}
},
onClick: (event) => {
onClick: (event: MouseEvent) => {
if (shouldIntercept(event)) {
// Let the mouseup/keyup event handle the visit
event.preventDefault()
Expand Down