19a6158631
When you edit your code, and Next.js is compiling the application, a compilation indicator appears in the bottom right corner of the page. In some cases this indicator can be misplaced on the page, for example, when conflicting with a chat launcher. To change its position, open `next.config.js` and set the `buildActivityPosition` in the `devIndicators` object to `bottom-right` (default), `bottom-left`, `top-right` or `top-left`. I also added the documentation for both `devIndicators.buildActivity` & the new `devIndicators.buildActivityPosition`. ## Feature - [x] Implements a new feature - [x] Documentation added
211 lines
5 KiB
JavaScript
211 lines
5 KiB
JavaScript
import { addMessageListener } from './error-overlay/websocket'
|
|
|
|
export default function initializeBuildWatcher(
|
|
toggleCallback,
|
|
position = 'bottom-right'
|
|
) {
|
|
const shadowHost = document.createElement('div')
|
|
shadowHost.id = '__next-build-watcher'
|
|
// Make sure container is fixed and on a high zIndex so it shows
|
|
shadowHost.style.position = 'fixed'
|
|
// Ensure container's position to be top or bottom (default)
|
|
if (['top-left', 'top-right'].indexOf(position) > -1) {
|
|
shadowHost.style.top = '10px'
|
|
} else {
|
|
shadowHost.style.bottom = '10px'
|
|
}
|
|
// Ensure container's position to be left or right (default)
|
|
if (['bottom-left', 'top-left'].indexOf(position) > -1) {
|
|
shadowHost.style.left = '20px'
|
|
} else {
|
|
shadowHost.style.right = '20px'
|
|
}
|
|
shadowHost.style.width = 0
|
|
shadowHost.style.height = 0
|
|
shadowHost.style.zIndex = 99999
|
|
document.body.appendChild(shadowHost)
|
|
|
|
let shadowRoot
|
|
let prefix = ''
|
|
|
|
if (shadowHost.attachShadow) {
|
|
shadowRoot = shadowHost.attachShadow({ mode: 'open' })
|
|
} else {
|
|
// If attachShadow is undefined then the browser does not support
|
|
// the Shadow DOM, we need to prefix all the names so there
|
|
// will be no conflicts
|
|
shadowRoot = shadowHost
|
|
prefix = '__next-build-watcher-'
|
|
}
|
|
|
|
// Container
|
|
const container = createContainer(prefix)
|
|
shadowRoot.appendChild(container)
|
|
|
|
// CSS
|
|
const css = createCss(prefix)
|
|
shadowRoot.appendChild(css)
|
|
|
|
// State
|
|
let isVisible = false
|
|
let isBuilding = false
|
|
let timeoutId = null
|
|
|
|
// Handle events
|
|
|
|
addMessageListener((event) => {
|
|
// This is the heartbeat event
|
|
if (event.data === '\uD83D\uDC93') {
|
|
return
|
|
}
|
|
|
|
try {
|
|
handleMessage(event)
|
|
} catch {}
|
|
})
|
|
|
|
function handleMessage(event) {
|
|
const obj =
|
|
typeof event === 'string' ? { action: event } : JSON.parse(event.data)
|
|
|
|
// eslint-disable-next-line default-case
|
|
switch (obj.action) {
|
|
case 'building':
|
|
timeoutId && clearTimeout(timeoutId)
|
|
isVisible = true
|
|
isBuilding = true
|
|
updateContainer()
|
|
break
|
|
case 'built':
|
|
case 'sync':
|
|
isBuilding = false
|
|
// Wait for the fade out transition to complete
|
|
timeoutId = setTimeout(() => {
|
|
isVisible = false
|
|
updateContainer()
|
|
}, 100)
|
|
updateContainer()
|
|
break
|
|
}
|
|
}
|
|
|
|
toggleCallback(handleMessage)
|
|
|
|
function updateContainer() {
|
|
if (isBuilding) {
|
|
container.classList.add(`${prefix}building`)
|
|
} else {
|
|
container.classList.remove(`${prefix}building`)
|
|
}
|
|
|
|
if (isVisible) {
|
|
container.classList.add(`${prefix}visible`)
|
|
} else {
|
|
container.classList.remove(`${prefix}visible`)
|
|
}
|
|
}
|
|
}
|
|
|
|
function createContainer(prefix) {
|
|
const container = document.createElement('div')
|
|
container.id = `${prefix}container`
|
|
container.innerHTML = `
|
|
<div id="${prefix}icon-wrapper">
|
|
<svg viewBox="0 0 226 200">
|
|
<defs>
|
|
<linearGradient
|
|
x1="114.720775%"
|
|
y1="181.283245%"
|
|
x2="39.5399306%"
|
|
y2="100%"
|
|
id="${prefix}linear-gradient"
|
|
>
|
|
<stop stop-color="#000000" offset="0%" />
|
|
<stop stop-color="#FFFFFF" offset="100%" />
|
|
</linearGradient>
|
|
</defs>
|
|
<g id="${prefix}icon-group" fill="none" stroke="url(#${prefix}linear-gradient)" stroke-width="18">
|
|
<path d="M113,5.08219117 L4.28393801,197.5 L221.716062,197.5 L113,5.08219117 Z" />
|
|
</g>
|
|
</svg>
|
|
</div>
|
|
`
|
|
|
|
return container
|
|
}
|
|
|
|
function createCss(prefix) {
|
|
const css = document.createElement('style')
|
|
css.textContent = `
|
|
#${prefix}container {
|
|
position: absolute;
|
|
bottom: 10px;
|
|
right: 30px;
|
|
|
|
border-radius: 3px;
|
|
background: #000;
|
|
color: #fff;
|
|
font: initial;
|
|
cursor: initial;
|
|
letter-spacing: initial;
|
|
text-shadow: initial;
|
|
text-transform: initial;
|
|
visibility: initial;
|
|
|
|
padding: 7px 10px 8px 10px;
|
|
align-items: center;
|
|
box-shadow: 0 11px 40px 0 rgba(0, 0, 0, 0.25), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
|
|
|
|
display: none;
|
|
opacity: 0;
|
|
transition: opacity 0.1s ease, bottom 0.1s ease;
|
|
animation: ${prefix}fade-in 0.1s ease-in-out;
|
|
}
|
|
|
|
#${prefix}container.${prefix}visible {
|
|
display: flex;
|
|
}
|
|
|
|
#${prefix}container.${prefix}building {
|
|
bottom: 20px;
|
|
opacity: 1;
|
|
}
|
|
|
|
#${prefix}icon-wrapper {
|
|
width: 16px;
|
|
height: 16px;
|
|
}
|
|
|
|
#${prefix}icon-wrapper > svg {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
#${prefix}icon-group {
|
|
animation: ${prefix}strokedash 1s ease-in-out both infinite;
|
|
}
|
|
|
|
@keyframes ${prefix}fade-in {
|
|
from {
|
|
bottom: 10px;
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
bottom: 20px;
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
@keyframes ${prefix}strokedash {
|
|
0% {
|
|
stroke-dasharray: 0 226;
|
|
}
|
|
80%,
|
|
100% {
|
|
stroke-dasharray: 659 226;
|
|
}
|
|
}
|
|
`
|
|
|
|
return css
|
|
}
|