vxe-pc-ui
Version:
A vue based PC component library
284 lines (251 loc) • 8.98 kB
text/typescript
import { defineComponent, ref, h, reactive, provide, computed, PropType, watch } from 'vue'
import { getConfig, getIcon, createEvent, permission, renderEmptyElement, useSize } from '../../ui'
import { getSlotVNs } from '../../ui/src/vn'
import XEUtils from 'xe-utils'
import type { CollapseReactData, VxeCollapseEmits, CollapsePrivateRef, VxeCollapsePropTypes, VxeCollapsePanePropTypes, VxeCollapsePaneProps, VxeCollapsePaneDefines, ValueOf, CollapseMethods, CollapsePrivateMethods, VxeCollapsePrivateComputed, VxeCollapseConstructor, VxeCollapsePrivateMethods } from '../../../types'
export default defineComponent({
name: 'VxeCollapse',
props: {
modelValue: Array as PropType<VxeCollapsePropTypes.ModelValue>,
options: Array as PropType<VxeCollapsePropTypes.Options>,
padding: {
type: Boolean as PropType<VxeCollapsePropTypes.Padding>,
default: () => getConfig().collapse.padding
},
expandConfig: Object as PropType<VxeCollapsePropTypes.ExpandConfig>,
size: {
type: String as PropType<VxeCollapsePropTypes.Size>,
default: () => getConfig().collapse.size || getConfig().size
}
},
emits: [
'update:modelValue',
'load',
'change',
'toggle-expand'
] as VxeCollapseEmits,
setup (props, context) {
const { emit, slots } = context
const xID = XEUtils.uniqueId()
const { computeSize } = useSize(props)
const refElem = ref<HTMLDivElement>()
const reactData = reactive<CollapseReactData>({
staticPanes: [],
activeNames: [],
initNames: [],
cachePaneMaps: {}
})
const refMaps: CollapsePrivateRef = {
refElem
}
const computeItemOptions = computed(() => {
const { options } = props
return (options || []).filter((item) => handleFilterItem(item))
})
const computeItemStaticOptions = computed(() => {
const { staticPanes } = reactData
return staticPanes.filter((item) => handleFilterItem(item))
})
const computeExpandOpts = computed(() => {
return Object.assign({}, getConfig().collapse.expandConfig, props.expandConfig)
})
const computeMaps: VxeCollapsePrivateComputed = {
}
const $xeCollapse = {
xID,
props,
context,
reactData,
getRefMaps: () => refMaps,
getComputeMaps: () => computeMaps
} as unknown as VxeCollapseConstructor & VxeCollapsePrivateMethods
const handleFilterItem = (item: VxeCollapsePaneProps | VxeCollapsePaneDefines.CollapseConfig) => {
const { permissionCode } = item
if (permissionCode) {
if (!permission.checkVisible(permissionCode)) {
return false
}
}
return true
}
const addInitName = (name: VxeCollapsePanePropTypes.Name | undefined) => {
const { initNames } = reactData
if (name && !initNames.includes(name)) {
initNames.push(name)
dispatchEvent('load', { name }, null)
return true
}
return false
}
const initDefaultName = (list?: VxeCollapsePropTypes.Options | VxeCollapsePaneDefines.CollapseConfig[]) => {
const { activeNames } = reactData
const nameMaps: Record<string, {
loading: boolean
}> = {}
if (list && list.length) {
list.forEach((item) => {
const { name, preload } = item || {}
if (name) {
const isActive = activeNames.includes(name)
nameMaps[`${name}`] = {
loading: false
}
if (isActive) {
addInitName(name)
}
if (preload) {
if (!isActive) {
activeNames.push(name)
}
}
}
})
}
reactData.activeNames = activeNames ? activeNames.slice(0) : []
reactData.cachePaneMaps = nameMaps
}
const dispatchEvent = (type: ValueOf<VxeCollapseEmits>, params: Record<string, any>, evnt: Event | null) => {
emit(type, createEvent(evnt, { $collapse: $xeCollapse }, params))
}
const collapseMethods: CollapseMethods = {
dispatchEvent
}
const callSlot = (slotFunc: any, params: any) => {
if (slotFunc) {
if (XEUtils.isString(slotFunc)) {
slotFunc = slots[slotFunc] || null
}
if (XEUtils.isFunction(slotFunc)) {
return getSlotVNs(slotFunc(params))
}
}
return []
}
const handleClickEvent = (evnt: MouseEvent, item: VxeCollapsePaneDefines.CollapseConfig | VxeCollapsePaneProps) => {
const { activeNames } = reactData
const { name } = item
if (name) {
const aIndex = activeNames.indexOf(name)
let expanded = false
if (aIndex === -1) {
expanded = true
activeNames.push(name)
} else {
activeNames.splice(aIndex, 1)
}
addInitName(name)
dispatchEvent('change', { value: activeNames, name }, evnt)
dispatchEvent('toggle-expand', { value: activeNames, name, expanded }, evnt)
}
}
const collapsePrivateMethods: CollapsePrivateMethods = {
}
Object.assign($xeCollapse, collapseMethods, collapsePrivateMethods)
const renderList = (itemList: VxeCollapsePropTypes.Options | VxeCollapsePaneDefines.CollapseConfig[]) => {
const { activeNames, initNames } = reactData
const expandOpts = computeExpandOpts.value
return itemList.map(item => {
const { icon, name, title, slots } = item
const titleSlot = slots ? slots.title : null
const defaultSlot = slots ? slots.default : null
const isActive = name && activeNames.includes(name)
return h('div', {
class: 'vxe-collapse-item'
}, [
h('div', {
class: 'vxe-collapse--item-header',
onClick (evnt) {
handleClickEvent(evnt, item)
}
}, [
expandOpts.showIcon
? h('span', {
class: 'vxe-collapse--item-switch'
}, [
h('i', {
class: isActive ? getIcon().COLLAPSE_OPEN : getIcon().COLLAPSE_CLOSE
})
])
: renderEmptyElement($xeCollapse),
icon
? h('span', {
class: 'vxe-collapse--item-icon'
}, [
h('i', {
class: icon
})
])
: renderEmptyElement($xeCollapse),
h('span', {
class: 'vxe-collapse--item-name'
}, titleSlot ? callSlot(titleSlot, { name, title }) : `${title}`)
]),
h('div', {
class: ['vxe-collapse--item-content', {
'is--visible': isActive
}]
}, [
name && initNames.includes(name)
? h('div', {
class: 'vxe-collapse--item-inner'
}, [
defaultSlot ? callSlot(defaultSlot, { name, title }) : ''
])
: renderEmptyElement($xeCollapse)
])
])
})
}
const renderVN = () => {
const { padding } = props
const vSize = computeSize.value
const itemOptions = computeItemOptions.value
const itemStaticOptions = computeItemStaticOptions.value
const defaultSlot = slots.default
const itemList = defaultSlot ? itemStaticOptions : itemOptions
return h('div', {
ref: refElem,
class: ['vxe-collapse', {
[`size--${vSize}`]: vSize,
'is--padding': padding
}]
}, [
h('div', {
class: 'vxe-collapse-slots'
}, defaultSlot ? defaultSlot({}) : []),
renderList(itemList)
])
}
watch(() => props.modelValue, (val) => {
reactData.activeNames = val || []
})
const optsFlag = ref(0)
watch(() => props.options ? props.options.length : -1, () => {
optsFlag.value++
})
watch(() => props.options, () => {
optsFlag.value++
})
watch(optsFlag, () => {
initDefaultName(props.options)
})
const stFlag = ref(0)
watch(() => reactData.staticPanes ? reactData.staticPanes.length : -1, () => {
stFlag.value++
})
watch(() => reactData.staticPanes, () => {
stFlag.value++
})
watch(stFlag, () => {
initDefaultName(reactData.staticPanes)
})
reactData.activeNames = props.modelValue || []
initDefaultName(reactData.staticPanes.length ? reactData.staticPanes : props.options)
provide('$xeCollapse', $xeCollapse)
$xeCollapse.renderVN = renderVN
return $xeCollapse
},
render () {
return this.renderVN()
}
})