🌐 AI搜索 & 代理 主页
Skip to content

Commit 136a72b

Browse files
tmorehousejacobmllr95
authored andcommitted
feat(b-collapse): add new prop appear to animate an initially visible collapse (#4317)
* feat(collapse): add new prop `appear` to animate initially visible collapse * Update package.json * Update README.md * Create bv-collapse helper transition component * Update bv-collapse.js * Update bv-collapse.js * Update collapse.js * Update collapse.js * Update bv-collapse.js * Update bv-collapse.js * Update bv-collapse.js * Update bv-collapse.js * Update bv-collapse.js * Update bv-collapse.js * Update bv-collapse.js * Update collapse.js * Update collapse.js * Update bv-collapse.js * Update bv-collapse.js * Update bv-collapse.js * Update bv-collapse.js * Update bv-collapse.js * Update bv-collapse.js * Update bv-collapse.js * Update bv-collapse.js * Update README.md * Update collapse.js * Update package.json * Update bv-collapse.js * Update bv-collapse.js * Update bv-collapse.js * Update bv-collapse.js * Update bv-collapse.js * Update bv-collapse.js * Update bv-collapse.js * Update bv-collapse.js
1 parent 84ab261 commit 136a72b

File tree

4 files changed

+104
-27
lines changed

4 files changed

+104
-27
lines changed

src/components/collapse/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ To make the `<b-collapse>` show initially, set the `visible` prop:
5656
<!-- b-collapse-visible.vue -->
5757
```
5858

59+
By default, an initially visible collapse will not animate on mount. To enable the collapse
60+
expanding animation on mount (when `visible` or `v-model` is `true`), set the `appear` prop on
61+
`<b-collapse>`.
62+
5963
## `v-model` support
6064

6165
The component's collapsed (visible) state can also be set with `v-model` which binds internally to

src/components/collapse/collapse.js

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@ import idMixin from '../../mixins/id'
33
import listenOnRootMixin from '../../mixins/listen-on-root'
44
import normalizeSlotMixin from '../../mixins/normalize-slot'
55
import { isBrowser } from '../../utils/env'
6+
import { BVCollapse } from '../../utils/bv-collapse'
67
import {
78
addClass,
89
hasClass,
910
removeClass,
1011
closest,
1112
matches,
12-
reflow,
1313
getCS,
14-
getBCR,
1514
eventOn,
1615
eventOff
1716
} from '../../utils/dom'
@@ -54,6 +53,11 @@ export const BCollapse = /*#__PURE__*/ Vue.extend({
5453
tag: {
5554
type: String,
5655
default: 'div'
56+
},
57+
appear: {
58+
// If `true` (and `visible` is `true` on mount), animate initially visible
59+
type: Boolean,
60+
default: false
5761
}
5862
},
5963
data() {
@@ -141,36 +145,26 @@ export const BCollapse = /*#__PURE__*/ Vue.extend({
141145
this.show = !this.show
142146
},
143147
onEnter(el) {
144-
el.style.height = 0
145-
reflow(el)
146-
el.style.height = el.scrollHeight + 'px'
147148
this.transitioning = true
148149
// This should be moved out so we can add cancellable events
149150
this.$emit('show')
150151
},
151152
onAfterEnter(el) {
152-
el.style.height = null
153153
this.transitioning = false
154154
this.$emit('shown')
155155
},
156156
onLeave(el) {
157-
el.style.height = 'auto'
158-
el.style.display = 'block'
159-
el.style.height = getBCR(el).height + 'px'
160-
reflow(el)
161157
this.transitioning = true
162-
el.style.height = 0
163158
// This should be moved out so we can add cancellable events
164159
this.$emit('hide')
165160
},
166161
onAfterLeave(el) {
167-
el.style.height = null
168162
this.transitioning = false
169163
this.$emit('hidden')
170164
},
171165
emitState() {
172166
this.$emit('input', this.show)
173-
// Let v-b-toggle know the state of this collapse
167+
// Let `v-b-toggle` know the state of this collapse
174168
this.$root.$emit(EVENT_STATE, this.safeId(), this.show)
175169
if (this.accordion && this.show) {
176170
// Tell the other collapses in this accordion to close
@@ -184,13 +178,15 @@ export const BCollapse = /*#__PURE__*/ Vue.extend({
184178
this.$root.$emit(EVENT_STATE_SYNC, this.safeId(), this.show)
185179
},
186180
checkDisplayBlock() {
187-
// Check to see if the collapse has `display: block !important;` set.
188-
// We can't set `display: none;` directly on this.$el, as it would
189-
// trigger a new transition to start (or cancel a current one).
181+
// Check to see if the collapse has `display: block !important` set
182+
// We can't set `display: none` directly on `this.$el`, as it would
183+
// trigger a new transition to start (or cancel a current one)
190184
const restore = hasClass(this.$el, 'show')
191185
removeClass(this.$el, 'show')
192186
const isBlock = getCS(this.$el).display === 'block'
193-
restore && addClass(this.$el, 'show')
187+
if (restore) {
188+
addClass(this.$el, 'show')
189+
}
194190
return isBlock
195191
},
196192
clickHandler(evt) {
@@ -202,7 +198,7 @@ export const BCollapse = /*#__PURE__*/ Vue.extend({
202198
}
203199
if (matches(el, '.nav-link,.dropdown-item') || closest('.nav-link,.dropdown-item', el)) {
204200
if (!this.checkDisplayBlock()) {
205-
// Only close the collapse if it is not forced to be 'display: block !important;'
201+
// Only close the collapse if it is not forced to be `display: block !important`
206202
this.show = false
207203
}
208204
}
@@ -246,16 +242,9 @@ export const BCollapse = /*#__PURE__*/ Vue.extend({
246242
[this.normalizeSlot('default')]
247243
)
248244
return h(
249-
'transition',
245+
BVCollapse,
250246
{
251-
props: {
252-
enterClass: '',
253-
enterActiveClass: 'collapsing',
254-
enterToClass: '',
255-
leaveClass: '',
256-
leaveActiveClass: 'collapsing',
257-
leaveToClass: ''
258-
},
247+
props: { appear: this.appear },
259248
on: {
260249
enter: this.onEnter,
261250
afterEnter: this.onAfterEnter,

src/components/collapse/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@
3333
{
3434
"prop": "visible",
3535
"description": "When 'true', expands the collapse"
36+
},
37+
{
38+
"prop": "appear",
39+
"version": "2.2.0",
40+
"description": "When set, and prop 'visible' is true on mount, will animate on initial mount"
3641
}
3742
],
3843
"events": [

src/utils/bv-collapse.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Generic collapse transion helper component
2+
//
3+
// Note:
4+
// Applies the classes `collapse`, `show` and `collapsing`
5+
// during the enter/leave transition phases only
6+
// Although it appears that Vue may be leaving the classes
7+
// in-place after the transition completes
8+
import Vue from './vue'
9+
import { mergeData } from 'vue-functional-data-merge'
10+
import { getBCR, reflow, requestAF } from './dom'
11+
12+
// Transition event handler helpers
13+
const onEnter = el => {
14+
el.style.height = 0
15+
// Animaton frame delay neeeded for `appear` to work
16+
requestAF(() => {
17+
reflow(el)
18+
el.style.height = `${el.scrollHeight}px`
19+
})
20+
}
21+
22+
const onAfterEnter = el => {
23+
el.style.height = null
24+
}
25+
26+
const onLeave = el => {
27+
el.style.height = 'auto'
28+
el.style.display = 'block'
29+
el.style.height = `${getBCR(el).height}px`
30+
reflow(el)
31+
el.style.height = 0
32+
}
33+
34+
const onAfterLeave = el => {
35+
el.style.height = null
36+
}
37+
38+
// Default transition props
39+
// `appear` will use the enter classes
40+
const TRANSITION_PROPS = {
41+
css: true,
42+
enterClass: '',
43+
enterActiveClass: 'collapsing',
44+
enterToClass: 'collapse show',
45+
leaveClass: 'collapse show',
46+
leaveActiveClass: 'collapsing',
47+
leaveToClass: 'collapse'
48+
}
49+
50+
// Default transition handlers
51+
// `appear` will use the enter handlers
52+
const TRANSITION_HANDLERS = {
53+
enter: onEnter,
54+
afterEnter: onAfterEnter,
55+
leave: onLeave,
56+
afterLeave: onAfterLeave
57+
}
58+
59+
// @vue/component
60+
export const BVCollapse = /*#__PURE__*/ Vue.extend({
61+
name: 'BVCollapse',
62+
functional: true,
63+
props: {
64+
appear: {
65+
// If `true` (and `visible` is `true` on mount), animate initially visible
66+
type: Boolean,
67+
default: false
68+
}
69+
},
70+
render(h, { props, data, children }) {
71+
return h(
72+
'transition',
73+
// We merge in the `appear` prop last
74+
mergeData(data, { props: TRANSITION_PROPS, on: TRANSITION_HANDLERS }, { props }),
75+
// Note: `<tranition>` supports a single root element only
76+
children
77+
)
78+
}
79+
})

0 commit comments

Comments
 (0)