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
5 changes: 5 additions & 0 deletions .changeset/four-carpets-wear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Allows the ClientRouter to open new tabs or windows when submitting forms by clicking while holding the Cmd, Ctrl, or Shift key.
25 changes: 18 additions & 7 deletions packages/astro/components/ClientRouter.astro
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const { fallback = 'animate' } = Astro.props;
import { init } from 'astro/virtual-modules/prefetch.js';

type Fallback = 'none' | 'animate' | 'swap';
let lastClickedElementLeavingWindow: EventTarget | null = null;

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

const leavesWindow = (ev: MouseEvent) =>
(ev.button && ev.button !== 0) || // left clicks only
ev.metaKey || // new tab (mac)
ev.ctrlKey || // new tab (windows)
ev.altKey || // download
ev.shiftKey; // new window

if (supportsViewTransitions || getFallback() !== 'none') {
if (import.meta.env.DEV && window.matchMedia('(prefers-reduced-motion)').matches) {
console.warn(
Expand All @@ -58,6 +66,9 @@ const { fallback = 'animate' } = Astro.props;
}
document.addEventListener('click', (ev) => {
let link = ev.target;

lastClickedElementLeavingWindow = leavesWindow(ev) ? link : null;

if (ev.composed) {
link = ev.composedPath()[0];
}
Expand All @@ -82,11 +93,7 @@ const { fallback = 'animate' } = Astro.props;
!link.href ||
(linkTarget && linkTarget !== '_self') ||
origin !== location.origin ||
ev.button !== 0 || // left clicks only
ev.metaKey || // new tab (mac)
ev.ctrlKey || // new tab (windows)
ev.altKey || // download
ev.shiftKey || // new window
lastClickedElementLeavingWindow ||
ev.defaultPrevented
) {
// No page transitions in these cases,
Expand All @@ -102,11 +109,15 @@ const { fallback = 'animate' } = Astro.props;

document.addEventListener('submit', (ev) => {
let el = ev.target as HTMLElement;
if (el.tagName !== 'FORM' || ev.defaultPrevented || isReloadEl(el)) {
const submitter = ev.submitter;

const clickedWithKeys = submitter && submitter === lastClickedElementLeavingWindow;
lastClickedElementLeavingWindow = null;

if (el.tagName !== 'FORM' || ev.defaultPrevented || isReloadEl(el) || clickedWithKeys) {
return;
}
const form = el as HTMLFormElement;
const submitter = ev.submitter;
const formData = new FormData(form, submitter);
// form.action and form.method can point to an <input name="action"> or <input name="method">
// in which case should fallback to the form attribute
Expand Down
Binary file not shown.