Skip to content

Commit 82836f5

Browse files
author
Guillaume Chau
committed
feat(ui): suggestions animation
1 parent 959ea07 commit 82836f5

File tree

4 files changed

+189
-102
lines changed

4 files changed

+189
-102
lines changed

packages/@vue/cli-ui/src/components/SuggestionBar.vue

Lines changed: 14 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -33,98 +33,31 @@
3333
/>
3434

3535
<template slot-scope="{ result: { data } }" v-if="data">
36-
<VueDropdown
37-
v-for="suggestion of withBuiltins(data.suggestions)"
38-
:key="suggestion.id"
39-
:disabled="!suggestion.message && !suggestion.link"
40-
class="suggestion"
41-
placement="bottom-end"
42-
>
43-
<VueButton
44-
slot="trigger"
45-
:label="$t(suggestion.label)"
46-
:loading="suggestion.busy"
47-
class="round"
48-
v-tooltip="$t('components.suggestion-bar.suggestion')"
49-
@click="if (!suggestion.message && !suggestion.link) activate(suggestion)"
36+
<transition-group name="suggestion" class="suggestions">
37+
<SuggestionBarItem
38+
v-for="suggestion of withBuiltins(data.suggestions)"
39+
:key="suggestion.id"
40+
:suggestion="suggestion"
5041
/>
51-
52-
<div class="suggestion-details">
53-
<div class="info label">
54-
{{ $t(suggestion.label) }}
55-
</div>
56-
57-
<div
58-
v-if="suggestion.message"
59-
class="info message"
60-
v-html="$t(suggestion.message)"
61-
/>
62-
63-
<div
64-
v-if="suggestion.image"
65-
class="info image"
66-
>
67-
<img :src="suggestion.image" alt="image">
68-
</div>
69-
70-
<div class="actions-bar">
71-
<VueButton
72-
:href="suggestion.link"
73-
:label="$t('components.list-item-info.more-info')"
74-
target="_blank"
75-
class="flat"
76-
icon-right="open_in_new"
77-
/>
78-
<div class="vue-ui-spacer"/>
79-
<VueButton
80-
:label="$t('components.suggestion-bar.modal.cancel')"
81-
icon-left="close"
82-
v-close-popover
83-
/>
84-
<VueButton
85-
class="primary"
86-
:label="$t('components.suggestion-bar.modal.continue')"
87-
icon-left="done"
88-
v-close-popover
89-
@click="activate(suggestion)"
90-
/>
91-
</div>
92-
</div>
93-
</VueDropdown>
42+
</transition-group>
9443
</template>
9544
</ApolloQuery>
9645
</template>
9746

9847
<script>
99-
import SUGGESTION_ACTIVATE from '../graphql/suggestionActivate.gql'
100-
10148
export default {
49+
data () {
50+
return {
51+
forceDevtoolsSuggestion: false
52+
}
53+
},
10254
methods: {
103-
async activate (suggestion) {
104-
if (suggestion.actionLink) {
105-
const win = window.open(
106-
suggestion.actionLink,
107-
'_blank'
108-
)
109-
win.focus()
110-
} else {
111-
await this.$apollo.mutate({
112-
mutation: SUGGESTION_ACTIVATE,
113-
variables: {
114-
input: {
115-
id: suggestion.id
116-
}
117-
}
118-
})
119-
}
120-
},
121-
12255
// Builtin suggestions
12356
withBuiltins (suggestions) {
12457
let list = suggestions
12558
12659
// Install devtools
127-
if (!Object.prototype.hasOwnProperty.call(window, '__VUE_DEVTOOLS_GLOBAL_HOOK__')) {
60+
if (this.forceDevtoolsSuggestion || !Object.prototype.hasOwnProperty.call(window, '__VUE_DEVTOOLS_GLOBAL_HOOK__')) {
12861
let devtoolsLink = null
12962
if (/Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor)) {
13063
devtoolsLink = 'https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd'
@@ -157,27 +90,6 @@ export default {
15790
<style lang="stylus" scoped>
15891
@import "~@/style/imports"
15992
160-
.suggestion
161-
&:not(:first-child)
162-
margin-left $padding-item
163-
164-
.suggestion-details
165-
padding ($padding-item * 2 - 8px) ($padding-item * 2)
166-
box-sizing border-box
167-
width 440px
168-
169-
.label
170-
font-size 20px
171-
172-
.actions-bar
173-
padding 0
174-
margin-top ($padding-item * 3)
175-
176-
.info
177-
&:not(:last-child)
178-
margin-bottom $padding-item
179-
180-
&.image
181-
>>> img
182-
max-width 100%
93+
.suggestions
94+
h-box()
18395
</style>
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
<template>
2+
<div
3+
class="suggestion-bar-item"
4+
v-set-size="'.wrapper'"
5+
>
6+
<div class="wrapper">
7+
<VueDropdown
8+
:disabled="!suggestion.message && !suggestion.link"
9+
placement="bottom-end"
10+
class="dropdown"
11+
>
12+
<VueButton
13+
slot="trigger"
14+
:label="$t(suggestion.label)"
15+
:loading="suggestion.busy"
16+
class="round"
17+
v-tooltip="$t('components.suggestion-bar.suggestion')"
18+
@click="onTriggerClick()"
19+
/>
20+
21+
<div class="suggestion-details">
22+
<div class="info label">
23+
{{ $t(suggestion.label) }}
24+
</div>
25+
26+
<div
27+
v-if="suggestion.message"
28+
class="info message"
29+
v-html="$t(suggestion.message)"
30+
/>
31+
32+
<div
33+
v-if="suggestion.image"
34+
class="info image"
35+
>
36+
<img :src="suggestion.image" alt="image">
37+
</div>
38+
39+
<div class="actions-bar">
40+
<VueButton
41+
:href="suggestion.link"
42+
:label="$t('components.list-item-info.more-info')"
43+
target="_blank"
44+
class="flat"
45+
icon-right="open_in_new"
46+
/>
47+
<div class="vue-ui-spacer"/>
48+
<VueButton
49+
:label="$t('components.suggestion-bar.modal.cancel')"
50+
icon-left="close"
51+
v-close-popover
52+
/>
53+
<VueButton
54+
class="primary"
55+
:label="$t('components.suggestion-bar.modal.continue')"
56+
icon-left="done"
57+
v-close-popover
58+
@click="activate(suggestion)"
59+
/>
60+
</div>
61+
</div>
62+
</VueDropdown>
63+
</div>
64+
</div>
65+
</template>
66+
67+
<script>
68+
import SUGGESTION_ACTIVATE from '../graphql/suggestionActivate.gql'
69+
70+
export default {
71+
props: {
72+
suggestion: {
73+
type: Object,
74+
required: true
75+
}
76+
},
77+
78+
methods: {
79+
onTriggerClick () {
80+
if (!this.suggestion.message && !this.suggestion.link) {
81+
this.activate(this.suggestion)
82+
}
83+
},
84+
85+
async activate (suggestion) {
86+
if (suggestion.actionLink) {
87+
const win = window.open(
88+
suggestion.actionLink,
89+
'_blank'
90+
)
91+
win.focus()
92+
} else {
93+
await this.$apollo.mutate({
94+
mutation: SUGGESTION_ACTIVATE,
95+
variables: {
96+
input: {
97+
id: suggestion.id
98+
}
99+
}
100+
})
101+
}
102+
}
103+
}
104+
}
105+
</script>
106+
107+
<style lang="stylus" scoped>
108+
@import "~@/style/imports"
109+
110+
.suggestion-details
111+
padding ($padding-item * 2 - 8px) ($padding-item * 2)
112+
box-sizing border-box
113+
width 440px
114+
115+
.label
116+
font-size 20px
117+
118+
.actions-bar
119+
padding 0
120+
margin-top ($padding-item * 3)
121+
122+
.info
123+
&:not(:last-child)
124+
margin-bottom $padding-item
125+
126+
&.image
127+
>>> img
128+
max-width 100%
129+
130+
.suggestion-bar-item
131+
margin-left $padding-item
132+
h-box()
133+
box-center()
134+
.wrapper
135+
width max-content
136+
box-sizing border-box
137+
138+
.suggestion-enter-active,
139+
.suggestion-leave-active
140+
transition all 1.5s cubic-bezier(0.075, 0.820, 0.165, 1.000)
141+
>>> .vue-ui-button
142+
transition all 1.5s cubic-bezier(0.075, 0.820, 0.165, 1.000)
143+
.content
144+
transition all .2s
145+
146+
.suggestion-enter-active
147+
>>> .vue-ui-button
148+
.content
149+
transition-delay .5s
150+
151+
.suggestion-enter,
152+
.suggestion-leave-to
153+
width 0 !important
154+
opacity 0
155+
margin-left 0
156+
>>> .vue-ui-button
157+
transform scale(0)
158+
.content
159+
opacity 0
160+
</style>

packages/@vue/cli-ui/src/main.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import SharedData from './util/shared-data'
1515
import PluginAction from './util/plugin-action'
1616
import gql from 'graphql-tag'
1717
import ClientState from './mixins/ClientState'
18+
import SetSize from './util/set-size'
1819

1920
window.gql = gql
2021

@@ -47,6 +48,8 @@ for (const key in Filters) {
4748

4849
Vue.mixin(ClientState)
4950

51+
Vue.directive('set-size', SetSize)
52+
5053
Vue.config.productionTip = false
5154

5255
// For client addons
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export default {
2+
bind (el, { value }) {
3+
if (typeof value === 'string') {
4+
value = el.querySelector(value)
5+
}
6+
requestAnimationFrame(() => {
7+
const bounds = value.getBoundingClientRect()
8+
el.style.width = `${bounds.width}px`
9+
el.style.height = `${bounds.height}px`
10+
})
11+
}
12+
}

0 commit comments

Comments
 (0)