prerender-indicator.js 5.4 KB
Newer Older
J
JJ Kasper 已提交
1 2
import Router from '../router'

3
export default function initializeBuildWatcher() {
J
JJ Kasper 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
  const shadowHost = document.createElement('div')
  shadowHost.id = '__next-prerender-indicator'
  // Make sure container is fixed and on a high zIndex so it shows
  shadowHost.style.position = 'fixed'
  shadowHost.style.bottom = '20px'
  shadowHost.style.right = '10px'
  shadowHost.style.width = 0
  shadowHost.style.height = 0
  shadowHost.style.zIndex = 99998
  shadowHost.style.transition = 'all 100ms ease'

  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-prerender-indicator-'
  }

  // Container
  const container = createContainer(prefix)
  shadowRoot.appendChild(container)

  // CSS
  const css = createCss(prefix)
  shadowRoot.appendChild(css)

38 39 40
  const expandEl = container.querySelector('a')
  const closeEl = container.querySelector(`#${prefix}close`)

J
JJ Kasper 已提交
41
  // State
42 43 44 45 46
  const dismissKey = '__NEXT_DISMISS_PRERENDER_INDICATOR'
  const dismissUntil = parseInt(window.localStorage.getItem(dismissKey), 10)
  const dismissed = dismissUntil > new Date().getTime()

  let isVisible = !dismissed && window.__NEXT_DATA__.nextExport
J
JJ Kasper 已提交
47

48
  function updateContainer() {
J
JJ Kasper 已提交
49 50 51 52 53 54
    if (isVisible) {
      container.classList.add(`${prefix}visible`)
    } else {
      container.classList.remove(`${prefix}visible`)
    }
  }
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
  const expandedClass = `${prefix}expanded`
  let toggleTimeout

  const toggleExpand = (expand = true) => {
    clearTimeout(toggleTimeout)

    toggleTimeout = setTimeout(() => {
      if (expand) {
        expandEl.classList.add(expandedClass)
        closeEl.style.display = 'flex'
      } else {
        expandEl.classList.remove(expandedClass)
        closeEl.style.display = 'none'
      }
    }, 50)
  }
J
JJ Kasper 已提交
71

72 73 74 75 76
  closeEl.addEventListener('click', () => {
    const oneHourAway = new Date().getTime() + 1 * 60 * 60 * 1000
    window.localStorage.setItem(dismissKey, oneHourAway + '')
    isVisible = false
    updateContainer()
J
JJ Kasper 已提交
77
  })
78 79 80 81
  closeEl.addEventListener('mouseenter', () => toggleExpand())
  closeEl.addEventListener('mouseleave', () => toggleExpand(false))
  expandEl.addEventListener('mouseenter', () => toggleExpand())
  expandEl.addEventListener('mouseleave', () => toggleExpand(false))
J
JJ Kasper 已提交
82 83 84 85 86 87 88 89

  Router.events.on('routeChangeComplete', () => {
    isVisible = window.next.isPrerendered
    updateContainer()
  })
  updateContainer()
}

90
function createContainer(prefix) {
J
JJ Kasper 已提交
91 92 93
  const container = document.createElement('div')
  container.id = `${prefix}container`
  container.innerHTML = `
94 95 96
    <button id="${prefix}close" title="Hide indicator for session">
      <span>×</span>
    </button>
97
    <a href="https://nextjs.org/docs#automatic-static-optimization-indicator" target="_blank" rel="noreferrer">
98 99 100 101 102 103 104 105 106
      <div id="${prefix}icon-wrapper">
          <svg width="15" height="20" viewBox="0 0 60 80" fill="none" xmlns="http://www.w3.org/2000/svg">
          <path d="M36 3L30.74 41H8L36 3Z" fill="black"/>
          <path d="M25 77L30.26 39H53L25 77Z" fill="black"/>
          <path d="M13.5 33.5L53 39L47.5 46.5L7 41.25L13.5 33.5Z" fill="black"/>
          </svg>
          Prerendered Page
      </div>
    </a>
J
JJ Kasper 已提交
107 108 109 110
  `
  return container
}

111
function createCss(prefix) {
J
JJ Kasper 已提交
112 113 114 115
  const css = document.createElement('style')
  css.textContent = `
    #${prefix}container {
      position: absolute;
116
      display: none;
J
JJ Kasper 已提交
117 118
      bottom: 10px;
      right: 15px;
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
    }

    #${prefix}close {
      top: -10px;
      right: -10px;
      border: none;
      width: 18px;
      height: 18px;
      color: #333333;
      font-size: 16px;
      cursor: pointer;
      display: none;
      position: absolute;
      background: #ffffff;
      border-radius: 100%;
      align-items: center;
      flex-direction: column;
      justify-content: center;
    }

    #${prefix}container a {
      color: inherit;
      text-decoration: none;
J
JJ Kasper 已提交
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
      width: 15px;
      height: 23px;
      overflow: hidden;

      border-radius: 3px;
      background: #fff;
      color: #000;
      font: initial;
      cursor: pointer;
      letter-spacing: initial;
      text-shadow: initial;
      text-transform: initial;
      visibility: initial;
      font-size: 14px;
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;

      padding: 4px 2px;
      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);

162
      display: flex;
J
JJ Kasper 已提交
163 164 165 166 167 168 169 170 171 172
      transition: opacity 0.1s ease, bottom 0.1s ease, width 0.3s ease;
      animation: ${prefix}fade-in 0.1s ease-in-out;
    }

    #${prefix}icon-wrapper {
      width: 140px;
      height: 20px;
      display: flex;
      flex-shrink: 0;
      align-items: center;
173
      position: relative;
J
JJ Kasper 已提交
174 175 176 177 178 179 180
    }

    #${prefix}icon-wrapper svg {
      flex-shrink: 0;
      margin-right: 3px;
    }

181 182
    #${prefix}container a.${prefix}expanded {
      width: 135px;
J
JJ Kasper 已提交
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
    }

    #${prefix}container.${prefix}visible {
      display: flex;
      bottom: 10px;
      opacity: 1;
    }

    @keyframes ${prefix}fade-in {
      from {
        bottom: 0px;
        opacity: 0;
      }
      to {
        bottom: 10px;
        opacity: 1;
      }
    }
  `

  return css
}