feat(ui): better build progress

This commit is contained in:
Guillaume Chau
2018-06-30 19:18:54 +02:00
parent 120be23da2
commit 55c2819e31
6 changed files with 212 additions and 71 deletions
@@ -100,6 +100,10 @@ class DashboardPlugin {
{
type: 'status',
value: 'Compiling'
},
{
type: 'progress',
value: 0
}
])
})
@@ -167,7 +171,7 @@ class DashboardPlugin {
},
{
type: 'progress',
value: 0
value: 1
},
{
type: 'operations',
@@ -1,32 +1,70 @@
<template>
<div class="build-progress">
<div
class="build-progress"
:class="{
[`mode-${mode}`]: true
}"
>
<div class="content">
<loading-progress
:progress="progress"
size="128"
counter-clockwise
/>
<div class="progress-wrapper">
<transition-group
name="vue-ui-fade"
class="progress-bars"
>
<div
v-for="(key, index) of Object.keys(progress)"
:key="key"
class="progress-bar-wrapper"
>
<loading-progress
:key="key"
:progress="progress[key]"
:size="128 - 16 * index"
class="progress-bar"
counter-clockwise
:class="{
'disable-animation': progress[key] === 0,
[`mode-${key}`]: true
}"
/>
</div>
</transition-group>
<div class="progress">
{{ typeof progress === 'number' ? Math.round(progress * 100) : 0 }}
<div class="progress">
<div
class="progress-animation"
:class="{
active: status && status !== 'Idle'
}"
>
<div
v-for="n in 4"
:key="n"
class="animation"
:style="{
'animation-delay': `${n * 0.25}s`
}"
/>
</div>
</div>
<transition duration="500">
<div v-if="status === 'Success'" class="status-icon done">
<div class="wrapper">
<VueIcon icon="check_circle"/>
</div>
</div>
</transition>
<transition duration="500">
<div v-if="status === 'Failed'" class="status-icon error">
<div class="wrapper">
<VueIcon icon="error"/>
</div>
</div>
</transition>
</div>
<transition duration="500">
<div v-if="status === 'Success'" class="status-icon done">
<div class="wrapper">
<VueIcon icon="check_circle"/>
</div>
</div>
</transition>
<transition duration="500">
<div v-if="status === 'Failed'" class="status-icon error">
<div class="wrapper">
<VueIcon icon="error"/>
</div>
</div>
</transition>
<div class="operations">
<span v-if="operations">{{ operations }}</span>
<VueIcon
@@ -43,18 +81,23 @@
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters([
'mode'
])
},
sharedData () {
return mapSharedData('org.vue.webpack.', {
status: `${this.mode}-status`,
progress: `${this.mode}-progress`,
rawProgress: `${this.mode}-progress`,
operations: `${this.mode}-operations`
})
},
computed: {
...mapGetters([
'mode'
]),
progress () {
const raw = this.rawProgress
return raw && Object.keys(raw).length ? raw : { unknown: 0 }
}
}
}
</script>
@@ -87,6 +130,12 @@ export default {
&:first-letter
text-transform uppercase
.progress-wrapper
width 178px
height @width
position relative
.progress-bar-wrapper,
.progress,
.status-icon
h-box()
@@ -96,12 +145,36 @@ export default {
top 0
left 0
width 100%
height 178px
height @width
.progress
color lighten($vue-ui-color-dark, 60%)
font-weight lighter
font-size 42px
.progress-bar
&.disable-animation
>>> path
transition none
&.mode-build-modern
>>> .progress
stroke $vue-ui-color-info
.progress-animation
display grid
$size = 12px
grid-template-columns repeat(2, $size)
grid-template-rows repeat(2, $size)
grid-template-areas "z1 z4" "z2 z3"
grid-gap 12px
.animation
width 100%
height @width
border-radius 50%
background rgba(black, .1)
transition background .15s
for n in (1..4)
&:nth-child({n})
grid-area 'z%s' % n
&.active
.animation
background $vue-ui-color-primary
animation progress 1s infinite
.status-icon
.wrapper
@@ -161,4 +234,25 @@ export default {
opacity 0
&.v-leave-to
opacity 0
&.mode-build-modern
.progress-animation.active
.animation
background $vue-ui-color-info
.status-icon
&.done
.wrapper::before
background $vue-ui-color-info
>>> svg
fill $vue-ui-color-info
@keyframes progress
0%,
30%
transform none
50%
transform scale(1.5)
80%
transform none
</style>
@@ -5,7 +5,7 @@
<div class="title">{{ $t('org.vue.vue-webpack.analyzer.title') }}</div>
<VueSwitch
v-if="modernMode"
v-if="mode !== 'serve' && modernMode"
v-model="showModernBuild"
>
{{ $t('org.vue.vue-webpack.modern-mode') }}
@@ -21,7 +21,7 @@
</template>
<VueSwitch
v-if="modernMode"
v-if="mode !== 'serve' && modernMode"
v-model="showModernBuild"
>
{{ $t('org.vue.vue-webpack.modern-mode') }}
+75 -26
View File
@@ -6,38 +6,57 @@ module.exports = api => {
let firstRun = true
let hadFailed = false
let modernMode = false
// Specific to each modes (serve, build, ...)
const fields = {
status: null,
progress: 0,
progress: {},
operations: null,
stats: null,
sizes: null,
problems: null,
url: null
}
// Common fields for all mode
const commonFields = {
'modern-mode': false
}
// Called when opening a project
function setupSharedData (mode) {
resetSharedData(mode)
for (const field in fields) {
const id = `${mode}-${field}`
watchSharedData(id, (value) => {
const project = api.getProject()
if (project) {
setSharedData(`${project.id}-${id}`, value)
}
})
watchData(id)
}
}
// Called when opening a project
function setupCommonData () {
for (const field in commonFields) {
setSharedData(field, getSharedDataInitialValue(field, commonFields[field]))
watchData(field)
}
}
function resetSharedData (mode, clear = false) {
for (const field in fields) {
const id = `${mode}-${field}`
setSharedData(id, getSharedDataInitialValue(id, field, clear))
setSharedData(id, getSharedDataInitialValue(id, fields[field], clear))
}
}
function getSharedDataInitialValue (id, field, clear) {
function watchData (id) {
watchSharedData(id, (value) => {
const project = api.getProject()
if (project) {
setSharedData(`${project.id}-${id}`, value)
}
})
}
function getSharedDataInitialValue (id, defaultValue, clear) {
if (!clear) {
const project = api.getProject()
if (project) {
@@ -45,29 +64,50 @@ module.exports = api => {
if (data != null) return data.value
}
}
return fields[field]
return defaultValue
}
async function onWebpackMessage ({ data: message }) {
if (message.webpackDashboardData) {
const modernMode = getSharedData('modern-mode').value
const type = message.webpackDashboardData.type
for (const data of message.webpackDashboardData.value) {
const id = `${type}-${data.type}`
if (data.type === 'stats') {
// Stats are read from a file
const statsFile = path.resolve(process.cwd(), `./node_modules/.stats-${type}.json`)
const value = await fs.readJson(statsFile)
setSharedData(`${type}-${data.type}`, value)
setSharedData(id, value)
await fs.remove(statsFile)
} else if (type.indexOf('build') !== -1 && modernMode && data.type === 'progress') {
// Progress is shared between 'build' and 'build-modern'
// 'build' first and then 'build-modern'
const value = type === 'build' ? data.value / 2 : (data.value + 1) / 2
// We display the same progress bar for both
for (const t of ['build', 'build-modern']) {
setSharedData(`${t}-${data.type}`, value)
} else if (data.type === 'progress') {
if (type === 'serve' || !modernMode) {
setSharedData(id, {
[type]: data.value
})
} else {
// Display two progress bars
const progress = getSharedData(id).value
progress[type] = data.value
for (const t of ['build', 'build-modern']) {
setSharedData(`${t}-${data.type}`, {
build: progress.build || 0,
'build-modern': progress['build-modern'] || 0
})
}
}
} else {
setSharedData(`${type}-${data.type}`, data.value)
// Don't display success until both build and build-modern are done
if (type !== 'serve' && modernMode && data.type === 'status' && data.value === 'Success') {
if (type === 'build-modern') {
for (const t of ['build', 'build-modern']) {
setSharedData(`${t}-status`, data.value)
}
}
} else {
setSharedData(id, data.value)
}
// Notifications
if (type === 'serve' && data.type === 'status') {
@@ -106,6 +146,7 @@ module.exports = api => {
for (const key of ['serve', 'build', 'build-modern']) {
setupSharedData(key)
}
setupCommonData()
})
// Tasks
@@ -285,7 +326,7 @@ module.exports = api => {
if (answers.name) args.push('--port', answers.name)
if (answers.watch) args.push('--watch')
if (answers.modern) args.push('--modern')
setSharedData('modern-mode', modernMode = !!answers.modern)
setSharedData('modern-mode', !!answers.modern)
args.push('--dashboard')
// Data
@@ -341,11 +382,19 @@ module.exports = api => {
}
})
// Webpack dashboard
api.addClientAddon({
id: 'org.vue.webpack.client-addon',
path: '@vue/cli-ui-addon-webpack/dist'
})
if (process.env.VUE_APP_CLI_UI_DEV) {
// Add dynamic components in dev mode (webpack dashboard & analyzer)
api.addClientAddon({
id: 'org.vue.webpack.client-addon.dev',
url: 'http://localhost:8096/index.js'
})
} else {
// Webpack dashboard
api.addClientAddon({
id: 'org.vue.webpack.client-addon',
path: '@vue/cli-ui-addon-webpack/dist'
})
}
// Open app button
api.ipcOn(({ data }) => {
-6
View File
@@ -5,12 +5,6 @@ module.exports = api => {
console.log('has(eslint)', api.hasPlugin('eslint'))
console.log('has(typescript)', api.hasPlugin('typescript'))
// Add dynamic components in dev mode (webpack dashboard & analyzer)
api.addClientAddon({
id: 'org.vue.webpack.client-addon.dev',
url: 'http://localhost:8096/index.js'
})
// Add a test page below 'plugins', 'configurations' and 'tasks' on the left sidebar
api.addView({
id: 'org.vue.webpack.views.test',