index.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import { use, isDef } from '../utils';
  2. import { raf } from '../utils/raf';
  3. import Cell from '../cell';
  4. import { cellProps } from '../cell/shared';
  5. import { FindParentMixin } from '../mixins/find-parent';
  6. const [sfc, bem] = use('collapse-item');
  7. const CELL_SLOTS = ['title', 'icon', 'right-icon'];
  8. export default sfc({
  9. mixins: [FindParentMixin],
  10. props: {
  11. ...cellProps,
  12. name: [String, Number],
  13. disabled: Boolean,
  14. isLink: {
  15. type: Boolean,
  16. default: true
  17. }
  18. },
  19. data() {
  20. return {
  21. show: null,
  22. inited: null
  23. };
  24. },
  25. computed: {
  26. items() {
  27. return this.parent.items;
  28. },
  29. index() {
  30. return this.items.indexOf(this);
  31. },
  32. currentName() {
  33. return isDef(this.name) ? this.name : this.index;
  34. },
  35. expanded() {
  36. if (!this.parent) {
  37. return null;
  38. }
  39. const { value } = this.parent;
  40. return this.parent.accordion
  41. ? value === this.currentName
  42. : value.some(name => name === this.currentName);
  43. }
  44. },
  45. created() {
  46. this.findParent('van-collapse');
  47. this.items.push(this);
  48. this.show = this.expanded;
  49. this.inited = this.expanded;
  50. },
  51. destroyed() {
  52. this.items.splice(this.index, 1);
  53. },
  54. watch: {
  55. expanded(expanded, prev) {
  56. if (prev === null) {
  57. return;
  58. }
  59. if (expanded) {
  60. this.show = true;
  61. this.inited = true;
  62. }
  63. raf(() => {
  64. const { content, wrapper } = this.$refs;
  65. if (!content || !wrapper) {
  66. return;
  67. }
  68. const { clientHeight } = content;
  69. if (clientHeight) {
  70. const contentHeight = `${clientHeight}px`;
  71. wrapper.style.height = expanded ? 0 : contentHeight;
  72. raf(() => {
  73. wrapper.style.height = expanded ? contentHeight : 0;
  74. });
  75. } else {
  76. this.onTransitionEnd();
  77. }
  78. });
  79. }
  80. },
  81. methods: {
  82. onClick() {
  83. if (this.disabled) {
  84. return;
  85. }
  86. const { parent } = this;
  87. const name =
  88. parent.accordion && this.currentName === parent.value
  89. ? ''
  90. : this.currentName;
  91. const expanded = !this.expanded;
  92. this.parent.switch(name, expanded);
  93. },
  94. onTransitionEnd() {
  95. if (!this.expanded) {
  96. this.show = false;
  97. } else {
  98. this.$refs.wrapper.style.height = null;
  99. }
  100. }
  101. },
  102. render(h) {
  103. const titleSlots = CELL_SLOTS.reduce((slots, name) => {
  104. if (this.slots(name)) {
  105. slots[name] = () => this.slots(name);
  106. }
  107. return slots;
  108. }, {});
  109. if (this.slots('value')) {
  110. titleSlots.default = () => this.slots('value');
  111. }
  112. const Title = (
  113. <Cell
  114. class={bem('title', {
  115. disabled: this.disabled,
  116. expanded: this.expanded
  117. })}
  118. onClick={this.onClick}
  119. scopedSlots={titleSlots}
  120. {...{ props: this.$props }}
  121. />
  122. );
  123. const Content = this.inited && (
  124. <div
  125. vShow={this.show}
  126. ref="wrapper"
  127. class={bem('wrapper')}
  128. onTransitionend={this.onTransitionEnd}
  129. >
  130. <div ref="content" class={bem('content')}>
  131. {this.slots()}
  132. </div>
  133. </div>
  134. );
  135. return (
  136. <div class={[bem(), { 'van-hairline--top': this.index }]}>
  137. {Title}
  138. {Content}
  139. </div>
  140. );
  141. }
  142. });