Skip to content

Commit 68621fa

Browse files
authored
feat: support <a> download attribute in preview (#5238)
Ref #5197 We prevented all link clicks. Though seems like download is safe to proceed when url is relative (asset).
1 parent 04949cb commit 68621fa

File tree

1 file changed

+43
-32
lines changed

1 file changed

+43
-32
lines changed

apps/builder/app/canvas/interceptor.ts

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -76,45 +76,58 @@ const switchPageAndUpdateSystem = (href: string, formData?: FormData) => {
7676

7777
export const subscribeInterceptedEvents = () => {
7878
const handleClick = (event: MouseEvent) => {
79-
if (event.target instanceof Element && !$isPreviewMode.get()) {
80-
// Prevent forwarding the click event on an input element when the associated label has a "for" attribute
81-
// and prevent checkbox or radio inputs changing when clicked
82-
if (event.target.closest("label[for]") || event.target.closest("input")) {
83-
event.preventDefault();
79+
if (!(event.target instanceof Element)) {
80+
return;
81+
}
82+
const isPreviewMode = $isPreviewMode.get();
83+
84+
// Prevent forwarding the click event on an input element when the associated label has a "for" attribute
85+
// and prevent checkbox or radio inputs changing when clicked
86+
if (event.target.closest("label[for]") || event.target.closest("input")) {
87+
if (isPreviewMode) {
88+
return;
8489
}
90+
event.preventDefault();
8591
}
8692

87-
if (
88-
event.target instanceof HTMLElement ||
89-
event.target instanceof SVGElement
90-
) {
91-
const a = event.target.closest("a");
92-
if (a) {
93-
event.preventDefault();
94-
if ($isPreviewMode.get()) {
95-
// use attribute instead of a.href to get raw unresolved value
96-
const href = a.getAttribute("href") ?? "";
97-
if (isAbsoluteUrl(href)) {
98-
window.open(href, "_blank");
99-
} else {
100-
switchPageAndUpdateSystem(href);
101-
}
93+
const a = event.target.closest("a");
94+
if (a) {
95+
if (isPreviewMode) {
96+
// use attribute instead of a.href to get raw unresolved value
97+
const href = a.getAttribute("href") ?? "";
98+
if (isAbsoluteUrl(href)) {
99+
window.open(href, "_blank");
100+
// relative paths can be safely downloaded
101+
} else if (a.hasAttribute("download")) {
102+
return;
103+
} else {
104+
switchPageAndUpdateSystem(href);
102105
}
103-
}
104-
// prevent invoking submit with buttons in canvas mode
105-
// because form with prevented submit still invokes validation
106-
if (event.target.closest("button") && $isPreviewMode.get() === false) {
107106
event.preventDefault();
107+
return;
108108
}
109+
event.preventDefault();
110+
}
111+
// prevent invoking submit with buttons in canvas mode
112+
// because form with prevented submit still invokes validation
113+
if (event.target.closest("button")) {
114+
if (isPreviewMode) {
115+
return;
116+
}
117+
event.preventDefault();
109118
}
110119
};
111120

112121
const handlePointerDown = (event: PointerEvent) => {
113-
if (false === event.target instanceof HTMLElement) {
122+
if (!(event.target instanceof Element)) {
114123
return;
115124
}
125+
const isPreviewMode = $isPreviewMode.get();
116126

117-
if (event.target.closest("select") && $isPreviewMode.get() === false) {
127+
if (event.target.closest("select")) {
128+
if (isPreviewMode) {
129+
return;
130+
}
118131
event.preventDefault();
119132
}
120133
};
@@ -140,16 +153,14 @@ export const subscribeInterceptedEvents = () => {
140153
};
141154

142155
const handleKeydown = (event: KeyboardEvent) => {
156+
if (!(event.target instanceof Element)) {
157+
return;
158+
}
143159
if ($isPreviewMode.get()) {
144160
return;
145161
}
146162
// prevent typing in inputs only in canvas mode
147-
if (
148-
event.target instanceof HTMLInputElement ||
149-
event.target instanceof HTMLTextAreaElement
150-
) {
151-
event.preventDefault();
152-
}
163+
event.preventDefault();
153164
};
154165

155166
// Note: Event handlers behave unexpectedly when used inside a dialog component.

0 commit comments

Comments
 (0)