Skip to content

Commit 7d3127d

Browse files
authored
ClientRouter: honor ctrl/shift... keys when clicking submit (#13750)
* ClientRouter: honor ctrl/shift... keys when clicking submit * clean up after use
1 parent f4b35a4 commit 7d3127d

3 files changed

Lines changed: 23 additions & 7 deletions

File tree

.changeset/four-carpets-wear.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'astro': patch
3+
---
4+
5+
Allows the ClientRouter to open new tabs or windows when submitting forms by clicking while holding the Cmd, Ctrl, or Shift key.

packages/astro/components/ClientRouter.astro

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const { fallback = 'animate' } = Astro.props;
3737
import { init } from 'astro/virtual-modules/prefetch.js';
3838

3939
type Fallback = 'none' | 'animate' | 'swap';
40+
let lastClickedElementLeavingWindow: EventTarget | null = null;
4041

4142
function getFallback(): Fallback {
4243
const el = document.querySelector('[name="astro-view-transitions-fallback"]');
@@ -50,6 +51,13 @@ const { fallback = 'animate' } = Astro.props;
5051
return el.dataset.astroReload !== undefined;
5152
}
5253

54+
const leavesWindow = (ev: MouseEvent) =>
55+
(ev.button && ev.button !== 0) || // left clicks only
56+
ev.metaKey || // new tab (mac)
57+
ev.ctrlKey || // new tab (windows)
58+
ev.altKey || // download
59+
ev.shiftKey; // new window
60+
5361
if (supportsViewTransitions || getFallback() !== 'none') {
5462
if (import.meta.env.DEV && window.matchMedia('(prefers-reduced-motion)').matches) {
5563
console.warn(
@@ -58,6 +66,9 @@ const { fallback = 'animate' } = Astro.props;
5866
}
5967
document.addEventListener('click', (ev) => {
6068
let link = ev.target;
69+
70+
lastClickedElementLeavingWindow = leavesWindow(ev) ? link : null;
71+
6172
if (ev.composed) {
6273
link = ev.composedPath()[0];
6374
}
@@ -82,11 +93,7 @@ const { fallback = 'animate' } = Astro.props;
8293
!link.href ||
8394
(linkTarget && linkTarget !== '_self') ||
8495
origin !== location.origin ||
85-
ev.button !== 0 || // left clicks only
86-
ev.metaKey || // new tab (mac)
87-
ev.ctrlKey || // new tab (windows)
88-
ev.altKey || // download
89-
ev.shiftKey || // new window
96+
lastClickedElementLeavingWindow ||
9097
ev.defaultPrevented
9198
) {
9299
// No page transitions in these cases,
@@ -102,11 +109,15 @@ const { fallback = 'animate' } = Astro.props;
102109

103110
document.addEventListener('submit', (ev) => {
104111
let el = ev.target as HTMLElement;
105-
if (el.tagName !== 'FORM' || ev.defaultPrevented || isReloadEl(el)) {
112+
const submitter = ev.submitter;
113+
114+
const clickedWithKeys = submitter && submitter === lastClickedElementLeavingWindow;
115+
lastClickedElementLeavingWindow = null;
116+
117+
if (el.tagName !== 'FORM' || ev.defaultPrevented || isReloadEl(el) || clickedWithKeys) {
106118
return;
107119
}
108120
const form = el as HTMLFormElement;
109-
const submitter = ev.submitter;
110121
const formData = new FormData(form, submitter);
111122
// form.action and form.method can point to an <input name="action"> or <input name="method">
112123
// in which case should fallback to the form attribute
4.19 KB
Binary file not shown.

0 commit comments

Comments
 (0)