From 84fe7db57d783f63a4d8014f4ba295dd2980fbf4 Mon Sep 17 00:00:00 2001 From: chenjiahan Date: Mon, 24 Aug 2020 21:06:40 +0800 Subject: [PATCH] refactor(Collapse): use composition api --- src/collapse-item/index.js | 184 +++++++++++++++---------------------- src/collapse/index.js | 66 +++++++++---- 2 files changed, 124 insertions(+), 126 deletions(-) diff --git a/src/collapse-item/index.js b/src/collapse-item/index.js index d40a495b5..025a27429 100644 --- a/src/collapse-item/index.js +++ b/src/collapse-item/index.js @@ -1,19 +1,20 @@ +import { ref, watch, computed, nextTick } from 'vue'; + // Utils import { createNamespace, isDef } from '../utils'; import { raf, doubleRaf } from '../utils/dom/raf'; -// Mixins -import { ChildrenMixin } from '../mixins/relation'; +// Composition +import { useParent } from '../api/use-relation'; // Components import Cell from '../cell'; +import { COLLAPSE_KEY } from '../collapse'; import { cellProps } from '../cell/shared'; const [createComponent, bem] = createNamespace('collapse-item'); export default createComponent({ - mixins: [ChildrenMixin('vanCollapse')], - props: { ...cellProps, name: [Number, String], @@ -24,152 +25,119 @@ export default createComponent({ }, }, - data() { - return { - show: null, - inited: null, + setup(props, { slots }) { + const wrapper = ref(null); + const content = ref(null); + const { parent, index } = useParent(COLLAPSE_KEY, ref()); + + const currentName = computed(() => + isDef(props.name) ? props.name : index.value + ); + + const expanded = computed(() => { + if (parent) { + return parent.isExpanded(currentName.value); + } + return null; + }); + + const show = ref(expanded.value); + const inited = ref(expanded.value); + + const onTransitionEnd = () => { + if (!expanded.value) { + show.value = false; + } else { + wrapper.value.style.height = ''; + } }; - }, - computed: { - currentName() { - return isDef(this.name) ? this.name : this.index; - }, - - expanded() { - if (!this.parent) { - return null; - } - - const { modelValue, accordion } = this.parent; - - if ( - process.env.NODE_ENV !== 'production' && - !accordion && - !Array.isArray(modelValue) - ) { - console.error( - '[Vant] Collapse: type of prop "modelValue" should be Array' - ); + watch(expanded, (value, oldValue) => { + if (oldValue === null) { return; } - return accordion - ? modelValue === this.currentName - : modelValue.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; + if (value) { + show.value = true; + inited.value = true; } // Use raf: flick when opened in safari // Use nextTick: closing animation failed when set `user-select: none` - const nextTick = expanded ? this.$nextTick : raf; + const tick = value ? nextTick : raf; - nextTick(() => { - const { content, wrapper } = this.$refs; - - if (!content || !wrapper) { + tick(() => { + if (!content.value || !wrapper.value) { return; } - const { offsetHeight } = content; + const { offsetHeight } = content.value; if (offsetHeight) { const contentHeight = `${offsetHeight}px`; - wrapper.style.height = expanded ? 0 : contentHeight; + wrapper.value.style.height = value ? 0 : contentHeight; // use double raf to ensure animation can start doubleRaf(() => { - wrapper.style.height = expanded ? contentHeight : 0; + wrapper.value.style.height = value ? contentHeight : 0; }); } else { - this.onTransitionEnd(); + onTransitionEnd(); } }); - }, - }, + }); - methods: { - onClick() { - if (this.disabled) { - return; + const onClickTitle = () => { + if (!props.disabled) { + parent.toggle(currentName.value, !expanded.value); } + }; - const { parent, currentName } = this; - const close = parent.accordion && currentName === parent.modelValue; - const name = close ? '' : currentName; - - parent.switch(name, !this.expanded); - }, - - onTransitionEnd() { - if (!this.expanded) { - this.show = false; - } else { - this.$refs.wrapper.style.height = ''; - } - }, - - genTitle() { - const { border, disabled, expanded } = this; - - const slots = { - icon: this.$slots.icon, - title: this.$slots.title, - default: this.$slots.value, - 'right-icon': this.$slots['right-icon'], - }; + const renderTitle = () => { + const { border, disabled } = props; return ( ); - }, + }; - genContent() { - if (this.inited) { + const renderContent = () => { + if (inited.value) { return (
-
- {this.$slots.default?.()} +
+ {slots.default?.()}
); } - }, - }, + }; - render() { - return ( -
- {this.genTitle()} - {this.genContent()} + return () => ( +
+ {renderTitle()} + {renderContent()}
); }, diff --git a/src/collapse/index.js b/src/collapse/index.js index e5fadb87d..b2cfe9acc 100644 --- a/src/collapse/index.js +++ b/src/collapse/index.js @@ -1,12 +1,12 @@ +import { ref, provide } from 'vue'; import { createNamespace } from '../utils'; -import { ParentMixin } from '../mixins/relation'; import { BORDER_TOP_BOTTOM } from '../utils/constant'; const [createComponent, bem] = createNamespace('collapse'); -export default createComponent({ - mixins: [ParentMixin('vanCollapse')], +export const COLLAPSE_KEY = 'vanCollapse'; +export default createComponent({ props: { accordion: Boolean, modelValue: [String, Number, Array], @@ -18,22 +18,52 @@ export default createComponent({ emits: ['change', 'update:modelValue'], - methods: { - switch(name, expanded) { - if (!this.accordion) { - name = expanded - ? this.modelValue.concat(name) - : this.modelValue.filter((activeName) => activeName !== name); - } - this.$emit('change', name); - this.$emit('update:modelValue', name); - }, - }, + setup(props, { emit, slots }) { + const children = ref([]); - render() { - return ( -
- {this.$slots.default?.()} + const toggle = (name, expanded) => { + const { accordion, modelValue } = props; + + if (accordion) { + if (name === modelValue) { + name = ''; + } + } else if (expanded) { + name = modelValue.concat(name); + } else { + name = modelValue.filter((activeName) => activeName !== name); + } + + emit('change', name); + emit('update:modelValue', name); + }; + + const isExpanded = (name) => { + const { accordion, modelValue } = props; + + if ( + !accordion && + !Array.isArray(modelValue) && + process.env.NODE_ENV !== 'production' + ) { + console.error( + '[Vant] Collapse: type of prop "modelValue" should be Array' + ); + return; + } + + return accordion ? modelValue === name : modelValue.indexOf(name) !== -1; + }; + + provide(COLLAPSE_KEY, { + toggle, + children, + isExpanded, + }); + + return () => ( +
+ {slots.default?.()}
); },