nuxt-build-indicator.vue 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. <template>
  2. <transition appear>
  3. <div v-if="building" class="nuxt__build_indicator" :style="indicatorStyle">
  4. <svg viewBox="0 0 96 72" version="1" xmlns="http://www.w3.org/2000/svg">
  5. <g fill="none" fill-rule="evenodd">
  6. <path d="M6 66h23l1-3 21-37L40 6 6 66zM79 66h11L62 17l-5 9 22 37v3zM54 31L35 66h38z" />
  7. <path d="M29 69v-1-2H6L40 6l11 20 3-6L44 3s-2-3-4-3-3 1-5 3L1 63c0 1-2 3 0 6 0 1 2 2 5 2h28c-3 0-4-1-5-2z" fill="#00C58E" />
  8. <path d="M95 63L67 14c0-1-2-3-5-3-1 0-3 0-4 3l-4 6 3 6 5-9 28 49H79a5 5 0 0 1 0 3c-2 2-5 2-5 2h16c1 0 4 0 5-2 1-1 2-3 0-6z" fill="#00C58E" />
  9. <path d="M79 69v-1-2-3L57 26l-3-6-3 6-21 37-1 3a5 5 0 0 0 0 3c1 1 2 2 5 2h40s3 0 5-2zM54 31l19 35H35l19-35z" fill="#FFF" fill-rule="nonzero" />
  10. </g>
  11. </svg>
  12. {{ animatedProgress }}%
  13. </div>
  14. </transition>
  15. </template>
  16. <script>
  17. export default {
  18. name: 'NuxtBuildIndicator',
  19. data () {
  20. return {
  21. building: false,
  22. progress: 0,
  23. animatedProgress: 0,
  24. reconnectAttempts: 0
  25. }
  26. },
  27. computed: {
  28. options: () => ({"position":"bottom-right","backgroundColor":"#2E495E","color":"#00C48D"}),
  29. indicatorStyle () {
  30. const [d1, d2] = this.options.position.split('-')
  31. return {
  32. [d1]: '20px',
  33. [d2]: '20px',
  34. 'background-color': this.options.backgroundColor,
  35. color: this.options.color
  36. }
  37. }
  38. },
  39. watch: {
  40. progress (val, oldVal) {
  41. // Average progress may decrease but ignore it!
  42. if (val < oldVal) {
  43. return
  44. }
  45. // Cancel old animation
  46. clearInterval(this._progressAnimation)
  47. // Jump to edge immediately
  48. if (val < 10 || val > 90) {
  49. this.animatedProgress = val
  50. return
  51. }
  52. // Animate to value
  53. this._progressAnimation = setInterval(() => {
  54. const diff = this.progress - this.animatedProgress
  55. if (diff > 0) {
  56. this.animatedProgress++
  57. } else {
  58. clearInterval(this._progressAnimation)
  59. }
  60. }, 50)
  61. }
  62. },
  63. mounted () {
  64. if (EventSource === undefined) {
  65. return // Unsupported
  66. }
  67. this.sseConnect()
  68. },
  69. beforeDestroy () {
  70. this.sseClose()
  71. clearInterval(this._progressAnimation)
  72. },
  73. methods: {
  74. sseConnect () {
  75. if (this._connecting) {
  76. return
  77. }
  78. this._connecting = true
  79. this.sse = new EventSource('/_loading/sse')
  80. this.sse.addEventListener('message', event => this.onSseMessage(event))
  81. },
  82. onSseMessage (message) {
  83. const data = JSON.parse(message.data)
  84. if (!data.states) {
  85. return
  86. }
  87. this.progress = Math.round(data.states.reduce((p, s) => p + s.progress, 0) / data.states.length)
  88. if (!data.allDone) {
  89. this.building = true
  90. } else {
  91. this.$nextTick(() => {
  92. this.building = false
  93. this.animatedProgress = 0
  94. this.progress = 0
  95. clearInterval(this._progressAnimation)
  96. })
  97. }
  98. },
  99. sseClose () {
  100. if (this.sse) {
  101. this.sse.close()
  102. delete this.sse
  103. }
  104. }
  105. }
  106. }
  107. </script>
  108. <style scoped>
  109. .nuxt__build_indicator {
  110. box-sizing: border-box;
  111. position: fixed;
  112. font-family: monospace;
  113. padding: 5px 10px;
  114. border-radius: 5px;
  115. box-shadow: 1px 1px 2px 0px rgba(0,0,0,0.2);
  116. width: 88px;
  117. z-index: 2147483647;
  118. font-size: 16px;
  119. line-height: 1.2rem;
  120. }
  121. .v-enter-active, .v-leave-active {
  122. transition-delay: 0.2s;
  123. transition-property: all;
  124. transition-duration: 0.3s;
  125. }
  126. .v-leave-to {
  127. opacity: 0;
  128. transform: translateY(20px);
  129. }
  130. svg {
  131. display: inline-block;
  132. vertical-align: baseline;
  133. width: 1.1em;
  134. height: 0.825em;
  135. position: relative;
  136. top: 1px;
  137. }
  138. </style>