Skip to content

Commit 22f4cb1

Browse files
authored
Merge pull request #5616 from FlowFuse/sign-up-as-pop-up
Open sign-up in a popup window instead of navigating away (draft)
2 parents 13913b7 + 24a6c0d commit 22f4cb1

4 files changed

Lines changed: 121 additions & 0 deletions

File tree

.eleventy.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ module.exports = function(eleventyConfig) {
141141
eleventyConfig.addPassthroughCopy("src/js/ai-expert-modal.js");
142142
eleventyConfig.addPassthroughCopy("src/js/hm-promo-banner.js");
143143
eleventyConfig.addPassthroughCopy("src/js/nav-tracking.js");
144+
eleventyConfig.addPassthroughCopy("src/js/signup-popup.js");
144145

145146
// Watch content images for the image pipeline
146147
eleventyConfig.addWatchTarget("src/**/*.{svg,webp,png,jpeg,gif}");

nuxt/nuxt.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,9 @@ export default defineNuxtConfig({
316316
// Explicit nav-click tracking. Source is src/js/nav-tracking.js;
317317
// prod:eleventy-nuxt copies the 11ty output into nuxt/public/.
318318
{ src: '/js/nav-tracking.js', defer: true },
319+
// Opens sign-up in a small popup window on desktop/tablet.
320+
// Source is src/js/signup-popup.js; copied the same way as nav-tracking.js.
321+
{ src: '/js/signup-popup.js', defer: true },
319322
]
320323
}
321324
},

src/_includes/layouts/base.njk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ eleventyComputed:
162162
<script type="module" src="/js/flowrenderer.min.js"></script>
163163
<!-- Explicit nav-click tracking; keep in sync with the Nuxt copy -->
164164
<script defer src="/js/nav-tracking.js"></script>
165+
<!-- Opens sign-up in a small popup window on desktop/tablet -->
166+
<script defer src="/js/signup-popup.js"></script>
165167

166168
{%- if not DEV_MODE -%}
167169
{% include "analytics/head.html" %}

src/js/signup-popup.js

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Opens the sign-up page in a small popup window on desktop/tablet, centered
2+
// over the visible page content, with a dark overlay behind it.
3+
// On mobile, leaves the link's default navigation untouched.
4+
document.addEventListener('DOMContentLoaded', () => {
5+
// Tracks the one popup/overlay pair that can be open at a time, so
6+
// there's a single thing to clean up instead of separate mutable
7+
// variables that a second click could overwrite and orphan.
8+
let session = null
9+
10+
function closeSession () {
11+
if (!session) {
12+
return
13+
}
14+
if (!session.popup.closed) {
15+
session.popup.close()
16+
}
17+
clearInterval(session.pollClosed)
18+
session.overlay.remove()
19+
session = null
20+
}
21+
22+
function openPopup (href) {
23+
if (session) {
24+
if (!session.popup.closed) {
25+
// Already have one open - bring it forward instead of
26+
// opening a second popup and orphaning this one's overlay.
27+
session.popup.focus()
28+
return
29+
}
30+
closeSession()
31+
}
32+
33+
const width = 420
34+
const height = Math.min(900, window.innerHeight * 0.75)
35+
36+
// outerWidth/outerHeight include browser chrome (toolbars, a
37+
// vertical tab strip, etc). Subtracting innerWidth/innerHeight
38+
// estimates that chrome so we can center over the visible page
39+
// content instead of the full browser window.
40+
const chromeWidth = window.outerWidth - window.innerWidth
41+
const chromeHeight = window.outerHeight - window.innerHeight
42+
const viewportLeft = window.screenX + chromeWidth
43+
const viewportTop = window.screenY + chromeHeight
44+
45+
const left = viewportLeft + (window.innerWidth - width) / 2
46+
const top = viewportTop + (window.innerHeight - height) / 2
47+
48+
const popup = window.open(
49+
href,
50+
'flowfuse-signup',
51+
`width=${width},height=${height},left=${left},top=${top},menubar=no,toolbar=no,location=no,status=no`
52+
)
53+
54+
if (!popup) {
55+
return
56+
}
57+
58+
const overlay = document.createElement('div')
59+
overlay.setAttribute('id', 'signup-popup-overlay')
60+
overlay.style.position = 'fixed'
61+
overlay.style.inset = '0'
62+
overlay.style.background = 'rgba(0, 0, 0, 0.45)'
63+
overlay.style.zIndex = '9999'
64+
overlay.addEventListener('click', closeSession)
65+
document.body.appendChild(overlay)
66+
67+
const pollClosed = setInterval(() => {
68+
if (popup.closed) {
69+
closeSession()
70+
}
71+
}, 100)
72+
73+
session = { popup, overlay, pollClosed }
74+
popup.focus()
75+
}
76+
77+
// Delegated on `document` (rather than binding each matching link once)
78+
// so this also catches anchors rendered after this script ran - e.g.
79+
// Nuxt's client-side route navigation swapping in a new CtaSignUp link
80+
// without a full page load.
81+
document.addEventListener('click', (event) => {
82+
const link = event.target.closest('a[href*="/account/create"]')
83+
if (!link) {
84+
return
85+
}
86+
87+
const isDesktopOrTablet = window.matchMedia('(min-width: 768px)').matches
88+
if (!isDesktopOrTablet) {
89+
return
90+
}
91+
92+
event.preventDefault()
93+
94+
// Explicit signal for the product to key its popup-specific
95+
// layout off, instead of `window.opener` — that's also set for
96+
// an ordinary ctrl/cmd-click "open in new tab", which isn't
97+
// this popup at all.
98+
const popupUrl = new URL(link.href)
99+
popupUrl.searchParams.set('context', 'popup')
100+
101+
openPopup(popupUrl.href)
102+
})
103+
104+
// Deliberate trade-off, not an oversight: closing on any window focus
105+
// (e.g. switching tabs/apps and coming back) can cancel an in-progress,
106+
// not-yet-submitted sign-up. That's judged preferable to the
107+
// alternative - the popup falling behind this window with no auto-close,
108+
// which leaves the overlay covering the page with no visible popup and
109+
// no clear reason why, reading as the site being stuck rather than a
110+
// sign-up that's still open one window over. Nothing of substance is
111+
// lost by closing early here: this only ever covers the initial,
112+
// unsubmitted form step. Added once (not per click) so listeners don't
113+
// accumulate across retries.
114+
window.addEventListener('focus', closeSession)
115+
})

0 commit comments

Comments
 (0)