import { defineComponent, h, ref, Ref, reactive, nextTick, PropType, computed, watchEffect } from 'vue' import { CardPrivateRef, CardReactData, VxeCardConstructor, VxeCardEmits, VxeCardMethods, VxeCardPropTypes } from '../../../types/card' import XEUtils, { isNumber, isString } from 'xe-utils' import GlobalConfig from '../../v-x-e-table/src/conf' import { getFuncText, multiDebounce } from '../../tools/utils' export default defineComponent({ name: 'VxeCard', props: { isCollapse: Boolean as PropType, loading: Boolean as PropType, round: { type: [Boolean, String, Number] as PropType, default: () => GlobalConfig.card.round }, width: [String, Number] as PropType, rotatingHeight: [String, Number] as PropType, shadow: { type: Boolean as PropType, default: () => GlobalConfig.card.shadow }, transform: [Boolean, String] as PropType, title: String as PropType, hoverEffect: String as PropType, bordered: { type: Boolean as PropType, default: () => GlobalConfig.card.bordered }, rotateMode: { type: String as PropType, default: 'horizontal' } }, emits: [ 'rotate', 'hover', 'collapse', 'expand', 'update:is-collapse' ] as VxeCardEmits, setup (props, context) { const { slots, emit } = context const xID = XEUtils.uniqueId() const reactData = reactive({ inited: false, isCollapse: !!props.isCollapse, tempExpand: false }) const refBox = ref() as Ref const refElem = ref() as Ref const refBody = ref() as Ref const refHeader = ref() as Ref const refFooter = ref() as Ref const refFront = ref() as Ref const refBack = ref() as Ref watchEffect(() => { reactData.isCollapse = !!props.isCollapse }) const refMaps: CardPrivateRef = { refElem } const $vxcard = { xID, props, context, reactData, getRefMaps: () => refMaps } as unknown as VxeCardConstructor let cardMethods = {} as VxeCardMethods const getCollapseIf = () => { return reactData.isCollapse } const toggleCollapse = () => { const isCollapse = !reactData.isCollapse reactData.isCollapse = isCollapse emit('update:is-collapse', isCollapse) emit(isCollapse ? 'collapse' : 'expand') return nextTick() } const perhapsExpand = () => { if (props.transform && reactData.isCollapse) { expand() } } const expand = () => { if (reactData.isCollapse) { return toggleCollapse() } return nextTick() } const handleHoverCover = () => { if (props.transform === 'hover' || props.transform === 'click-hover') { reactData.tempExpand = true } } const handleCardLeave = () => { if (props.transform === 'hover' || props.transform === 'click-hover') { reactData.tempExpand = false } } const mouseInOut = multiDebounce([handleHoverCover, handleCardLeave], 200) const collapse = () => { if (!reactData.isCollapse) { return toggleCollapse() } return nextTick() } const handleHeaderClick = (event: Event) => { event.stopPropagation() if (props.transform === true || props.transform === 'click') { emit('update:is-collapse', true) } else if (props.transform === 'click-hover') { emit('update:is-collapse', !reactData.isCollapse) } return nextTick() } cardMethods = { dispatchEvent (type, params, evnt) { emit(type, Object.assign({ $card: $vxcard, $event: evnt }, params)) }, getCollapseIf, toggleCollapse, expand, collapse } Object.assign($vxcard, cardMethods) const dynamicWrapperWidth = computed(() => { const styleWidthRegex = /^\d+(\.\d+)?(px|%|em|rem|pt)?$/i const pureNumberRegex = /^\d+$/ return isNumber(props.width) || (isString(props.width) && pureNumberRegex.test(props.width)) ? `${props.width}px` : isString(props.width) && styleWidthRegex.test(props.width) ? props.width : undefined }) const dynamicRotateHeight = computed(() => { const styleHeightRegex = /^\d+(\.\d+)?(px|em|rem|pt)?$/i const pureNumberRegex = /^\d+$/ return isNumber(props.rotatingHeight) || (isString(props.rotatingHeight) && pureNumberRegex.test(props.rotatingHeight)) ? `${props.rotatingHeight}px` : isString(props.rotatingHeight) && styleHeightRegex.test(props.rotatingHeight) ? props.rotatingHeight : undefined }) const renderCardHeader = () => h('div', { ref: refHeader, class: 'vxe-card-header', onClick: handleHeaderClick }, [ slots.header?.({ title: props.title }) ?? h('span', { class: 'vxe-card-header--title' }, getFuncText(props.title)) ]) const renderCardFront = () => h('div', { ref: refFront, class: [ 'vxe-card', 'vxe-card-rotating-front', (isCol.value ? 'vxe-card-cover vxe-card-cover--circle' : { 'vxe-card--shadow': props.shadow, 'vxe-card--press': props.hoverEffect === 'press', 'vxe-card--scale': props.hoverEffect === 'scale' }) ] }, [ slots.header || props.title ? renderCardHeader() : null, renderCardBody(), renderCardFooter() ]) const renderCardBack = () => h('div', { ref: refBack, class: [ 'vxe-card', 'vxe-card-rotating-back', (isCol.value ? 'vxe-card-cover vxe-card-cover--circle' : { 'vxe-card--shadow': props.shadow, 'vxe-card--press': props.hoverEffect === 'press', 'vxe-card--scale': props.hoverEffect === 'scale' }) ] }, [ h('div', { class: 'vxe-card-body' }, [ slots.back?.() ?? slots.default?.() ?? '' ]) ]) const renderCardBody = () => h('div', { ref: refBody, class: 'vxe-card-body' }, [ slots.default?.() ]) const renderCardFooter = () => slots.footer ? h('div', { ref: refFooter, class: 'vxe-card-footer' }, [ slots.footer?.() ]) : null const isCol = computed(() => (reactData.isCollapse && !reactData.tempExpand) && props.transform) const renderVN = () => { return props.hoverEffect === 'rotate' ? h('div', { ref: refBox, class: [ 'vxe-card-rotating-box', `vxe-card--rotating-${props.rotateMode}` ], style: { width: dynamicWrapperWidth.value, height: props.rotateMode !== 'diagonal' ? dynamicRotateHeight.value : undefined } }, [ renderCardFront(), renderCardBack() ]) : h('div', { ref: refElem, class: [ 'vxe-card', (isCol.value ? 'vxe-card-cover vxe-card-cover--circle' : { 'vxe-card--shadow': props.shadow, 'vxe-card--press': props.hoverEffect === 'press', 'vxe-card--scale': props.hoverEffect === 'scale' }) ], style: isCol.value ? null : { width: dynamicWrapperWidth.value, borderRadius: (props.round === false || props.round === undefined) ? 'unset' : props.round === true ? '5px' : isNumber(props.round) ? `${props.round}px` : (props.round as string) }, onClick: perhapsExpand, onMouseover: mouseInOut.handleHoverCover, onMouseout: mouseInOut.handleCardLeave }, isCol.value ? h('span', { class: 'vxe-cover--content' }, getFuncText(props.title)) : [ slots.header || props.title ? renderCardHeader() : null, renderCardBody(), renderCardFooter() ]) } $vxcard.renderVN = renderVN return $vxcard }, render () { return this.renderVN() } })