import { ref, watch, computed, nextTick } from 'vue'; // Utils import { createNamespace } from '../utils'; // Composables import { raf, doubleRaf, useParent } from '@vant/use'; import { useExpose } from '../composables/use-expose'; import { useLazyRender } from '../composables/use-lazy-render'; // Components import Cell, { cellProps } from '../cell'; import { COLLAPSE_KEY, CollapseProvide } from '../collapse'; const [createComponent, bem] = createNamespace('collapse-item'); export default createComponent({ props: { ...cellProps, name: [Number, String], disabled: Boolean, isLink: { type: Boolean, default: true, }, }, setup(props, { slots }) { const wrapperRef = ref(); const contentRef = ref(); const { parent, index } = useParent(COLLAPSE_KEY); if (!parent) { if (process.env.NODE_ENV !== 'production') { console.error( '[Vant] must be a child component of .' ); } return; } const name = computed(() => props.name ?? index.value); const expanded = computed(() => parent.isExpanded(name.value)); const show = ref(expanded.value); const lazyRender = useLazyRender(show); const onTransitionEnd = () => { if (!expanded.value) { show.value = false; } else { wrapperRef.value!.style.height = ''; } }; watch(expanded, (value, oldValue) => { if (oldValue === null) { return; } if (value) { show.value = true; } // Use raf: flick when opened in safari // Use nextTick: closing animation failed when set `user-select: none` const tick = value ? nextTick : raf; tick(() => { if (!contentRef.value || !wrapperRef.value) { return; } const { offsetHeight } = contentRef.value; if (offsetHeight) { const contentHeight = `${offsetHeight}px`; wrapperRef.value.style.height = value ? '0' : contentHeight; // use double raf to ensure animation can start doubleRaf(() => { wrapperRef.value!.style.height = value ? contentHeight : '0'; }); } else { onTransitionEnd(); } }); }); const toggle = (newValue = !expanded.value) => { parent.toggle(name.value, newValue); }; const onClickTitle = () => { if (!props.disabled) { toggle(); } }; const renderTitle = () => { const { border, disabled } = props; return ( ); }; const renderContent = lazyRender(() => (
{slots.default?.()}
)); useExpose({ toggle }); return () => (
{renderTitle()} {renderContent()}
); }, });