mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-05 19:41:42 +08:00
157 lines
3.4 KiB
JavaScript
157 lines
3.4 KiB
JavaScript
import { createNamespace, isDef } from '../utils';
|
|
import { raf, doubleRaf } from '../utils/dom/raf';
|
|
import Cell from '../cell';
|
|
import { cellProps } from '../cell/shared';
|
|
import { ChildrenMixin } from '../mixins/relation';
|
|
|
|
const [createComponent, bem] = createNamespace('collapse-item');
|
|
const CELL_SLOTS = ['title', 'icon', 'right-icon'];
|
|
|
|
export default createComponent({
|
|
mixins: [ChildrenMixin('vanCollapse')],
|
|
|
|
props: {
|
|
...cellProps,
|
|
name: [Number, String],
|
|
disabled: Boolean,
|
|
isLink: {
|
|
type: Boolean,
|
|
default: true
|
|
}
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
show: null,
|
|
inited: null
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
currentName() {
|
|
return isDef(this.name) ? this.name : this.index;
|
|
},
|
|
|
|
expanded() {
|
|
if (!this.parent) {
|
|
return null;
|
|
}
|
|
|
|
const { value } = this.parent;
|
|
return this.parent.accordion
|
|
? value === this.currentName
|
|
: value.some(name => name === this.currentName);
|
|
}
|
|
},
|
|
|
|
created() {
|
|
this.show = this.expanded;
|
|
this.inited = this.expanded;
|
|
},
|
|
|
|
watch: {
|
|
expanded(expanded, prev) {
|
|
if (prev === null) {
|
|
return;
|
|
}
|
|
|
|
if (expanded) {
|
|
this.show = true;
|
|
this.inited = true;
|
|
}
|
|
|
|
// Use raf: flick when opened in safari
|
|
// Use nextTick: closing animation failed when set `user-select: none`
|
|
const nextTick = expanded ? this.$nextTick : raf;
|
|
|
|
nextTick(() => {
|
|
const { content, wrapper } = this.$refs;
|
|
if (!content || !wrapper) {
|
|
return;
|
|
}
|
|
|
|
const { offsetHeight } = content;
|
|
if (offsetHeight) {
|
|
const contentHeight = `${offsetHeight}px`;
|
|
wrapper.style.height = expanded ? 0 : contentHeight;
|
|
|
|
// use double raf to ensure animation can start in mobile safari
|
|
doubleRaf(() => {
|
|
wrapper.style.height = expanded ? contentHeight : 0;
|
|
});
|
|
} else {
|
|
this.onTransitionEnd();
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
onClick() {
|
|
if (this.disabled) {
|
|
return;
|
|
}
|
|
|
|
const { parent } = this;
|
|
const name =
|
|
parent.accordion && this.currentName === parent.value ? '' : this.currentName;
|
|
this.parent.switch(name, !this.expanded);
|
|
},
|
|
|
|
onTransitionEnd() {
|
|
if (!this.expanded) {
|
|
this.show = false;
|
|
} else {
|
|
this.$refs.wrapper.style.height = '';
|
|
}
|
|
}
|
|
},
|
|
|
|
render() {
|
|
const { disabled, expanded } = this;
|
|
|
|
const titleSlots = CELL_SLOTS.reduce((slots, name) => {
|
|
if (this.slots(name)) {
|
|
slots[name] = () => this.slots(name);
|
|
}
|
|
return slots;
|
|
}, {});
|
|
|
|
if (this.slots('value')) {
|
|
titleSlots.default = () => this.slots('value');
|
|
}
|
|
|
|
const Title = (
|
|
<Cell
|
|
role="button"
|
|
class={bem('title', { disabled, expanded })}
|
|
onClick={this.onClick}
|
|
scopedSlots={titleSlots}
|
|
tabindex={disabled ? -1 : 0}
|
|
aria-expanded={String(expanded)}
|
|
{...{ props: this.$props }}
|
|
/>
|
|
);
|
|
|
|
const Content = this.inited && (
|
|
<div
|
|
vShow={this.show}
|
|
ref="wrapper"
|
|
class={bem('wrapper')}
|
|
onTransitionend={this.onTransitionEnd}
|
|
>
|
|
<div ref="content" class={bem('content')}>
|
|
{this.slots()}
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
return (
|
|
<div class={[bem(), { 'van-hairline--top': this.index }]}>
|
|
{Title}
|
|
{Content}
|
|
</div>
|
|
);
|
|
}
|
|
});
|