vxe-pc-ui
Version:
A vue based PC component library
1,010 lines (938 loc) • 37.2 kB
text/typescript
import { PropType, ref, h, reactive, provide, VNode, computed, watch, nextTick, onMounted, onUnmounted, onActivated } from 'vue'
import { defineVxeComponent } from '../../ui/src/comp'
import { getConfig, getIcon, createEvent, globalEvents, globalResize, useSize, renderEmptyElement } from '../../ui'
import { getSlotVNs } from '../../ui/src/vn'
import { toCssUnit, isScale, addClass, removeClass } from '../../ui/src/dom'
import { getGlobalDefaultConfig } from '../../ui/src/utils'
import { warnLog, errLog } from '../../ui/src/log'
import XEUtils from 'xe-utils'
import type { SplitterReactData, SplitterPrivateRef, VxeSplitterPropTypes, SplitterInternalData, SplitterMethods, VxeSplitterPanelPropTypes, VxeSplitterDefines, VxeSplitterPanelProps, VxeSplitterPrivateComputed, SplitterPrivateMethods, VxeSplitterEmits, VxeSplitterConstructor, ValueOf, VxeSplitterPrivateMethods } from '../../../types'
export default defineVxeComponent({
name: 'VxeSplitter',
props: {
width: [Number, String] as PropType<VxeSplitterPropTypes.Width>,
height: [Number, String] as PropType<VxeSplitterPropTypes.Height>,
vertical: {
type: Boolean as PropType<VxeSplitterPropTypes.Vertical>,
default: () => getConfig().splitter.vertical
},
border: {
type: Boolean as PropType<VxeSplitterPropTypes.Border>,
default: () => getConfig().splitter.border
},
padding: {
type: Boolean as PropType<VxeSplitterPropTypes.Padding>,
default: () => getConfig().splitter.padding
},
resize: {
type: Boolean as PropType<VxeSplitterPropTypes.Resize>,
default: () => getConfig().splitter.resize
},
items: Array as PropType<VxeSplitterPropTypes.Items>,
itemConfig: Object as PropType<VxeSplitterPropTypes.ItemConfig>,
barConfig: Object as PropType<VxeSplitterPropTypes.BarConfig>,
resizeConfig: Object as PropType<VxeSplitterPropTypes.ResizeConfig>,
actionConfig: Object as PropType<VxeSplitterPropTypes.ActionConfig>,
size: {
type: String as PropType<VxeSplitterPropTypes.Size>,
default: () => getConfig().splitter.size || getConfig().size
}
},
emits: [
'action-dblclick',
'action-click',
'toggle-expand',
'resize-start',
'resize-drag',
'resize-end'
] as VxeSplitterEmits,
setup (props, context) {
const { emit, slots } = context
const xID = XEUtils.uniqueId()
const refElem = ref<HTMLDivElement>()
const refBarInfoElem = ref<HTMLDivElement>()
const refResizableSplitterTip = ref<HTMLDivElement>()
const { computeSize } = useSize(props)
const reactData = reactive<SplitterReactData>({
staticItems: [],
itemList: [],
barWidth: 0,
barHeight: 0
})
const internalData: SplitterInternalData = {
wrapperWidth: 0,
wrapperHeight: 0
}
const computeItemOpts = computed(() => {
return Object.assign({}, getConfig().splitter.itemConfig, props.itemConfig)
})
const computeBarOpts = computed(() => {
return Object.assign({}, getConfig().splitter.barConfig, props.barConfig)
})
const computeResizeOpts = computed(() => {
return Object.assign({}, getConfig().splitter.resizeConfig, props.resizeConfig)
})
const computeActionOpts = computed(() => {
return Object.assign({}, getConfig().splitter.actionConfig, props.actionConfig)
})
const computeVisibleItems = computed(() => {
return reactData.itemList.filter(item => item.isExpand)
})
const computeAutoItems = computed(() => {
const { vertical } = props
const autoItems: VxeSplitterDefines.PaneConfig[] = []
let heightCount = 0
let widthCount = 0
reactData.itemList.forEach(vertical
? item => {
const { renderHeight, resizeHeight, foldHeight, isExpand, height } = item
const itemHeight = isExpand ? (foldHeight || resizeHeight || renderHeight) : 0
if (!height) {
autoItems.push(item)
}
heightCount += itemHeight
}
: item => {
const { renderWidth, resizeWidth, foldWidth, isExpand, width } = item
const itemWidth = isExpand ? (foldWidth || resizeWidth || renderWidth) : 0
if (!width) {
autoItems.push(item)
}
widthCount += itemWidth
})
return {
autoItems,
heightCount,
heightRatio: heightCount / 100,
widthCount,
widthRatio: widthCount / 100
}
})
const computeBarStyle = computed(() => {
const barOpts = computeBarOpts.value
const { width, height } = barOpts
const stys: Record<string, string | number> = {}
if (height) {
stys.height = toCssUnit(height)
}
if (width) {
stys.width = toCssUnit(width)
}
return stys
})
const computeMaps: VxeSplitterPrivateComputed = {
computeItemOpts,
computeBarOpts,
computeActionOpts
}
const refMaps: SplitterPrivateRef = {
refElem
}
const $xeSplitter = {
xID,
props,
context,
reactData,
internalData,
getRefMaps: () => refMaps,
getComputeMaps: () => computeMaps
} as unknown as VxeSplitterConstructor & VxeSplitterPrivateMethods
const dispatchEvent = (type: ValueOf<VxeSplitterEmits>, params: Record<string, any>, evnt: Event | null) => {
emit(type, createEvent(evnt, { $splitter: $xeSplitter }, params))
}
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 getActionIcon = (prevItem: VxeSplitterDefines.PaneConfig, nextItem: VxeSplitterDefines.PaneConfig, isNext: boolean) => {
const { vertical } = props
const topIcon = 'SPLIT_TOP_ACTION'
const bottomIcon = 'SPLIT_BOTTOM_ACTION'
const leftIcon = 'SPLIT_LEFT_ACTION'
const rightIcon = 'SPLIT_RIGHT_ACTION'
let iconName: 'SPLIT_TOP_ACTION' | 'SPLIT_BOTTOM_ACTION' | 'SPLIT_LEFT_ACTION' | 'SPLIT_RIGHT_ACTION' | '' = ''
if (vertical) {
if (isNext) {
iconName = nextItem.isExpand ? bottomIcon : topIcon
} else {
iconName = prevItem.isExpand ? topIcon : bottomIcon
}
} else {
if (isNext) {
iconName = nextItem.isExpand ? rightIcon : leftIcon
} else {
iconName = prevItem.isExpand ? leftIcon : rightIcon
}
}
if (iconName) {
return getIcon()[iconName]
}
return ''
}
const reset = () => {
const { itemList } = reactData
itemList.forEach(item => {
item.isExpand = true
item.foldHeight = 0
item.foldWidth = 0
item.resizeHeight = 0
item.resizeWidth = 0
})
return nextTick()
}
const handleLoadItem = (list: VxeSplitterPanelProps[], isReset: boolean) => {
const { staticItems } = reactData
const actionOpts = computeActionOpts.value
const { showPrevButton, showNextButton } = actionOpts
const itemDef = {
isExpand: true,
renderWidth: 0,
resizeWidth: 0,
foldWidth: 0,
renderHeight: 0,
resizeHeight: 0,
foldHeight: 0
}
reactData.itemList = list.map(item => {
if (item.showAction) {
warnLog('vxe.error.removeProp', ['[splitter] show-action'])
}
if (item.slots) {
XEUtils.each(item.slots, (func) => {
if (!XEUtils.isFunction(func)) {
if (!slots[func]) {
errLog('vxe.error.notSlot', [`[splitter] ${func}`])
}
}
})
}
return Object.assign({}, isReset ? null : itemDef, item, isReset ? itemDef : null, {
id: XEUtils.uniqueId()
})
})
if (staticItems.length) {
errLog('vxe.error.errConflicts', ['<vxe-splitter-panel ...>', 'items'])
}
if ((showPrevButton || showNextButton) && reactData.itemList.length > 2) {
errLog('vxe.error.errConflicts', ['action-config.showPrevButton | action-config.showNextButton', 'Only supports 2 item'])
}
return recalculate()
}
const loadItem = (list: VxeSplitterPanelProps[]) => {
return handleLoadItem(list || [], false)
}
const reloadItem = (list: VxeSplitterPanelProps[]) => {
return handleLoadItem(list || [], true)
}
const handleItemByName = (name: VxeSplitterPanelPropTypes.Name) => {
const { itemList } = reactData
let index = -1
let currItem: VxeSplitterDefines.PaneConfig | null = null
let prevItem: VxeSplitterDefines.PaneConfig | null = null
let nextItem: VxeSplitterDefines.PaneConfig | null = null
for (let i = 0; i < itemList.length; i++) {
const item = itemList[i]
if (item.name === name) {
index = i
currItem = item
prevItem = itemList[i - 1] || null
nextItem = itemList[i + 1] || null
break
}
}
return {
index,
currItem,
prevItem,
nextItem
}
}
const setItemExpand = (name: VxeSplitterPanelPropTypes.Name, expanded: boolean) => {
const restItem = handleItemByName(name)
if (restItem) {
const { currItem, prevItem, nextItem } = restItem
if (currItem) {
if (expanded ? !currItem.isExpand : currItem.isExpand) {
if (nextItem) {
if (nextItem.isExpand) {
handleItemActionEvent(null, currItem, nextItem, false)
}
} else if (prevItem) {
if (prevItem.isExpand) {
handleItemActionEvent(null, prevItem, currItem, true)
}
}
}
}
}
return nextTick()
}
const toggleItemExpand = (name: VxeSplitterPanelPropTypes.Name) => {
const restItem = handleItemByName(name)
if (restItem) {
const { currItem } = restItem
if (currItem) {
return setItemExpand(name, !currItem.isExpand)
}
}
return nextTick()
}
const getItemExpand = (name: VxeSplitterPanelPropTypes.Name) => {
const restItem = handleItemByName(name)
if (restItem) {
const { currItem } = restItem
if (currItem) {
return currItem.isExpand
}
}
return false
}
const recalculate = () => {
return nextTick().then(() => {
const { vertical } = props
const { itemList } = reactData
const el = refElem.value
const barInfoElem = refBarInfoElem.value
if (!el) {
return
}
const wWidth = el.clientWidth
const wHeight = el.clientHeight
if (!wWidth || !wHeight) {
return
}
if (barInfoElem) {
reactData.barWidth = barInfoElem.offsetWidth
reactData.barHeight = barInfoElem.offsetHeight
}
const contentWidth = wWidth - (vertical ? 0 : reactData.barWidth * (itemList.length - 1))
const contentHeight = wHeight - (vertical ? reactData.barHeight * (itemList.length - 1) : 0)
const itemOpts = computeItemOpts.value
const allMinWidth = XEUtils.toNumber(itemOpts.minWidth)
const allMinHeight = XEUtils.toNumber(itemOpts.minHeight)
const residueItems: VxeSplitterDefines.PaneConfig[] = []
if (vertical) {
let countHeight = 0
itemList.forEach(item => {
const { height } = item
let itemHeight = 0
if (height) {
if (isScale(height)) {
itemHeight = contentHeight * XEUtils.toNumber(height) / 100
} else {
itemHeight = XEUtils.toNumber(height)
}
item.renderHeight = itemHeight
} else {
residueItems.push(item)
}
countHeight += itemHeight
})
if (residueItems.length) {
const reMeanHeight = (contentHeight - countHeight) / residueItems.length
residueItems.forEach(item => {
item.renderHeight = Math.max(XEUtils.toNumber(getGlobalDefaultConfig(item.minHeight, allMinHeight)), reMeanHeight)
})
}
} else {
let countWidth = 0
itemList.forEach(item => {
const { width } = item
let itemWidth = 0
if (width) {
if (isScale(width)) {
itemWidth = contentWidth * XEUtils.toNumber(width) / 100
} else {
itemWidth = XEUtils.toNumber(width)
}
item.renderWidth = itemWidth
} else {
residueItems.push(item)
}
countWidth += itemWidth
})
if (residueItems.length) {
const reMeanWidth = (contentWidth - countWidth) / residueItems.length
residueItems.forEach(item => {
item.renderWidth = Math.max(XEUtils.toNumber(getGlobalDefaultConfig(item.minWidth, allMinWidth)), reMeanWidth)
})
}
}
internalData.wrapperWidth = contentWidth
internalData.wrapperHeight = contentHeight
})
}
const dragEvent = (evnt: MouseEvent) => {
const { resize, vertical } = props
const { itemList } = reactData
if (!resize) {
return
}
evnt.preventDefault()
const barEl = evnt.currentTarget as HTMLDivElement
const handleEl = barEl.parentElement as HTMLDivElement
const el = refElem.value
if (!el) {
return
}
const prevEl = handleEl.previousElementSibling as HTMLDivElement
const nextEl = handleEl.nextElementSibling as HTMLDivElement
if (!prevEl || !nextEl) {
return
}
const prevId = prevEl.getAttribute('itemid')
const nextId = nextEl.getAttribute('itemid')
const prevItem = itemList.find(item => item.id === prevId)
const nextItem = itemList.find(item => item.id === nextId)
if (!prevItem || !nextItem) {
return
}
const containerRect = el.getBoundingClientRect()
const barRect = barEl.getBoundingClientRect()
const rsSplitterLineEl = refResizableSplitterTip.value
const rsSplitterTipEl = rsSplitterLineEl ? rsSplitterLineEl.children[0] as HTMLDivElement : null
const itemOpts = computeItemOpts.value
const resizeOpts = computeResizeOpts.value
const { immediate } = resizeOpts
const allMinWidth = XEUtils.toNumber(itemOpts.minWidth)
const allMinHeight = XEUtils.toNumber(itemOpts.minHeight)
const barOffsetX = Math.ceil(barRect.width - (evnt.clientX - barRect.left))
const barOffsetY = Math.ceil(evnt.clientY - barRect.top)
const prevWidth = prevEl.offsetWidth
const nextWidth = nextEl.offsetWidth
const prevMinWidth = XEUtils.toNumber(prevItem ? getGlobalDefaultConfig(prevItem.minWidth, allMinWidth) : allMinWidth)
const nextMinWidth = XEUtils.toNumber(nextItem ? getGlobalDefaultConfig(nextItem.minWidth, allMinWidth) : allMinWidth)
const minOffsetLeft = prevEl.offsetLeft + prevMinWidth - barOffsetX
const maxOffsetLeft = nextEl.offsetLeft + nextEl.offsetWidth - nextMinWidth - barOffsetX
const startOffsetLeft = evnt.clientX - containerRect.left
let targetOffsetWidth = -1
let prevResizeWidth = 0
let nextResizeWidth = 0
let offsetLeft = startOffsetLeft
const prevHeight = prevEl.offsetHeight
const nextHeight = nextEl.offsetHeight
const prevMinHeight = XEUtils.toNumber(prevItem ? getGlobalDefaultConfig(prevItem.minHeight, allMinHeight) : allMinHeight)
const nextMinHeight = XEUtils.toNumber(nextItem ? getGlobalDefaultConfig(nextItem.minHeight, allMinHeight) : allMinHeight)
const minOffsetTop = prevEl.offsetTop + prevMinHeight + barOffsetY
const maxOffsetTop = nextEl.offsetTop + nextEl.offsetHeight - nextMinHeight + barOffsetY
const startOffsetTop = evnt.clientY - containerRect.top
let targetOffsetHeight = -1
let prevResizeHeight = 0
let nextResizeHeight = 0
let offsetTop = startOffsetTop
const handleReStyle = (evnt: MouseEvent) => {
if (!rsSplitterLineEl) {
return
}
const rsNumPrevEl = rsSplitterTipEl ? rsSplitterTipEl.children[0] as HTMLDivElement : null
const rsNumNextEl = rsSplitterTipEl ? rsSplitterTipEl.children[1] as HTMLDivElement : null
if (vertical) {
let tipWidth = 0
if (rsNumPrevEl) {
if (targetOffsetHeight < 0) {
rsNumPrevEl.style.display = 'none'
} else {
rsNumPrevEl.textContent = `${Math.floor(prevResizeHeight)}px`
rsNumPrevEl.style.display = 'block'
tipWidth = rsNumPrevEl.offsetWidth
}
}
if (rsNumNextEl) {
if (targetOffsetHeight < 0) {
rsNumNextEl.textContent = `${Math.floor(nextResizeHeight)}px`
rsNumNextEl.style.display = 'block'
tipWidth = rsNumNextEl.offsetWidth
} else {
rsNumNextEl.style.display = 'none'
}
}
let rsLeft = Math.max(1, evnt.clientX - containerRect.left - tipWidth / 2)
if (rsLeft > containerRect.width - tipWidth - 1) {
rsLeft = containerRect.width - tipWidth - 1
}
rsSplitterLineEl.style.left = '0'
rsSplitterLineEl.style.top = `${offsetTop}px`
if (rsSplitterTipEl) {
rsSplitterTipEl.style.left = `${rsLeft}px`
}
} else {
let tipHeight = 0
if (rsNumPrevEl) {
if (targetOffsetWidth < 0) {
rsNumPrevEl.style.display = 'none'
} else {
rsNumPrevEl.textContent = `${Math.floor(prevResizeWidth)}px`
rsNumPrevEl.style.display = 'block'
tipHeight = rsNumPrevEl.offsetHeight
}
}
if (rsNumNextEl) {
if (targetOffsetWidth < 0) {
rsNumNextEl.textContent = `${Math.floor(nextResizeWidth)}px`
rsNumNextEl.style.display = 'block'
tipHeight = rsNumNextEl.offsetHeight
} else {
rsNumNextEl.style.display = 'none'
}
}
let rsTop = Math.max(1, evnt.clientY - containerRect.top - tipHeight / 2)
if (rsTop > containerRect.height - tipHeight - 1) {
rsTop = containerRect.height - tipHeight - 1
}
rsSplitterLineEl.style.top = '0'
rsSplitterLineEl.style.left = `${offsetLeft}px`
if (rsSplitterTipEl) {
rsSplitterTipEl.style.top = `${rsTop}px`
}
}
}
const handleUpdate = () => {
if (vertical) {
prevItem.resizeHeight = prevResizeHeight
nextItem.resizeHeight = nextResizeHeight
} else {
prevItem.resizeWidth = prevResizeWidth
nextItem.resizeWidth = nextResizeWidth
}
}
const handleDrag = (evnt: MouseEvent) => {
if (vertical) {
offsetTop = evnt.clientY - containerRect.top
if (offsetTop < minOffsetTop) {
offsetTop = minOffsetTop
}
if (offsetTop > maxOffsetTop) {
offsetTop = maxOffsetTop
}
targetOffsetHeight = offsetTop - startOffsetTop
prevResizeHeight = prevHeight + targetOffsetHeight
nextResizeHeight = nextHeight - targetOffsetHeight
} else {
offsetLeft = evnt.clientX - containerRect.left
if (offsetLeft < minOffsetLeft) {
offsetLeft = minOffsetLeft
}
if (offsetLeft > maxOffsetLeft) {
offsetLeft = maxOffsetLeft
}
targetOffsetWidth = offsetLeft - startOffsetLeft
prevResizeWidth = prevWidth + targetOffsetWidth
nextResizeWidth = nextWidth - targetOffsetWidth
}
if (immediate) {
if (vertical) {
prevEl.style.height = toCssUnit(prevResizeHeight)
nextEl.style.height = toCssUnit(nextResizeHeight)
} else {
prevEl.style.width = toCssUnit(prevResizeWidth)
nextEl.style.width = toCssUnit(nextResizeWidth)
}
}
if (rsSplitterLineEl) {
handleReStyle(evnt)
}
dispatchEvent('resize-drag', { prevItem, nextItem, offsetHeight: targetOffsetHeight, offsetWidth: targetOffsetWidth }, evnt)
}
document.onmousemove = (evnt) => {
evnt.preventDefault()
handleDrag(evnt)
}
document.onmouseup = (evnt) => {
document.onmousemove = null
document.onmouseup = null
if (rsSplitterLineEl) {
rsSplitterLineEl.style.display = ''
}
handleUpdate()
removeClass(el, 'is--drag')
dispatchEvent('resize-end', { prevItem, nextItem, offsetHeight: targetOffsetHeight, offsetWidth: targetOffsetWidth }, evnt)
recalculate()
}
if (rsSplitterLineEl) {
rsSplitterLineEl.style.display = 'block'
handleReStyle(evnt)
}
handleDrag(evnt)
addClass(el, 'is--drag')
dispatchEvent('resize-start', { prevItem, nextItem }, evnt)
}
const handleItemActionEvent = (evnt: MouseEvent | null, prevItem: VxeSplitterDefines.PaneConfig, nextItem: VxeSplitterDefines.PaneConfig, isNext: boolean) => {
const { vertical } = props
let expanded = false
let item = prevItem
if (isNext) {
item = nextItem
expanded = !nextItem.isExpand
nextItem.isExpand = expanded
} else {
expanded = !prevItem.isExpand
prevItem.isExpand = expanded
}
if (vertical) {
if (prevItem.isExpand && nextItem.isExpand) {
prevItem.foldHeight = 0
nextItem.foldHeight = 0
} else if (prevItem.isExpand) {
nextItem.foldHeight = 0
prevItem.foldHeight = (prevItem.resizeHeight || prevItem.renderHeight) + (nextItem.resizeHeight || nextItem.renderHeight)
} else {
prevItem.foldHeight = 0
nextItem.foldHeight = (prevItem.resizeHeight || prevItem.renderHeight) + (nextItem.resizeHeight || nextItem.renderHeight)
}
} else {
if (prevItem.isExpand && nextItem.isExpand) {
prevItem.foldWidth = 0
nextItem.foldWidth = 0
} else if (prevItem.isExpand) {
nextItem.foldWidth = 0
prevItem.foldWidth = (prevItem.resizeWidth || prevItem.renderWidth) + (nextItem.resizeWidth || nextItem.renderWidth)
} else {
prevItem.foldWidth = 0
nextItem.foldWidth = (prevItem.resizeWidth || prevItem.renderWidth) + (nextItem.resizeWidth || nextItem.renderWidth)
}
}
if (evnt) {
dispatchEvent('toggle-expand', { prevItem, nextItem, expanded, item }, evnt)
}
recalculate()
}
const handlePrevActionDblclickEvent = (evnt: MouseEvent) => {
const { itemList } = reactData
const actionOpts = computeActionOpts.value
const btnEl = evnt.currentTarget as HTMLDivElement
const btnWrapperEl = btnEl.parentElement as HTMLDivElement
const handleEl = btnWrapperEl.parentElement as HTMLDivElement
const prevEl = handleEl.previousElementSibling as HTMLDivElement
const prevId = prevEl.getAttribute('itemid')
const prevItem = itemList.find(item => item.id === prevId)
const nextEl = handleEl.nextElementSibling as HTMLDivElement
const nextId = nextEl.getAttribute('itemid')
const nextItem = itemList.find(item => item.id === nextId)
if (actionOpts.trigger === 'dblclick') {
if (prevItem && nextItem && nextItem.isExpand) {
handleItemActionEvent(evnt, prevItem, nextItem, false)
}
}
dispatchEvent('action-dblclick', { prevItem, nextItem }, evnt)
}
const handlePrevActionClickEvent = (evnt: MouseEvent) => {
const { itemList } = reactData
const actionOpts = computeActionOpts.value
const btnEl = evnt.currentTarget as HTMLDivElement
const btnWrapperEl = btnEl.parentElement as HTMLDivElement
const handleEl = btnWrapperEl.parentElement as HTMLDivElement
const prevEl = handleEl.previousElementSibling as HTMLDivElement
const prevId = prevEl.getAttribute('itemid')
const prevItem = itemList.find(item => item.id === prevId)
const nextEl = handleEl.nextElementSibling as HTMLDivElement
const nextId = nextEl.getAttribute('itemid')
const nextItem = itemList.find(item => item.id === nextId)
if (actionOpts.trigger !== 'dblclick') {
if (prevItem && nextItem && nextItem.isExpand) {
handleItemActionEvent(evnt, prevItem, nextItem, false)
}
}
dispatchEvent('action-click', { prevItem, nextItem }, evnt)
}
const handleNextActionDblclickEvent = (evnt: MouseEvent) => {
const { itemList } = reactData
const actionOpts = computeActionOpts.value
const btnEl = evnt.currentTarget as HTMLDivElement
const btnWrapperEl = btnEl.parentElement as HTMLDivElement
const handleEl = btnWrapperEl.parentElement as HTMLDivElement
const prevEl = handleEl.previousElementSibling as HTMLDivElement
const prevId = prevEl.getAttribute('itemid')
const prevItem = itemList.find(item => item.id === prevId)
const nextEl = handleEl.nextElementSibling as HTMLDivElement
const nextId = nextEl.getAttribute('itemid')
const nextItem = itemList.find(item => item.id === nextId)
if (actionOpts.trigger === 'dblclick') {
if (prevItem && nextItem && prevItem.isExpand) {
handleItemActionEvent(evnt, prevItem, nextItem, true)
}
}
dispatchEvent('action-dblclick', { prevItem, nextItem }, evnt)
}
const handleNextActionClickEvent = (evnt: MouseEvent) => {
const { itemList } = reactData
const actionOpts = computeActionOpts.value
const btnEl = evnt.currentTarget as HTMLDivElement
const btnWrapperEl = btnEl.parentElement as HTMLDivElement
const handleEl = btnWrapperEl.parentElement as HTMLDivElement
const prevEl = handleEl.previousElementSibling as HTMLDivElement
const prevId = prevEl.getAttribute('itemid')
const prevItem = itemList.find(item => item.id === prevId)
const nextEl = handleEl.nextElementSibling as HTMLDivElement
const nextId = nextEl.getAttribute('itemid')
const nextItem = itemList.find(item => item.id === nextId)
if (actionOpts.trigger !== 'dblclick') {
if (prevItem && nextItem && prevItem.isExpand) {
handleItemActionEvent(evnt, prevItem, nextItem, true)
}
}
dispatchEvent('action-click', { prevItem, nextItem }, evnt)
}
const handleGlobalResizeEvent = () => {
recalculate()
}
const splitterMethods: SplitterMethods = {
dispatchEvent,
setItemExpand,
toggleItemExpand,
getItemExpand,
recalculate,
reset,
loadItem,
reloadItem
}
const splitterPrivateMethods: SplitterPrivateMethods = {
}
Object.assign($xeSplitter, splitterMethods, splitterPrivateMethods)
const renderHandleBar = (prevItem: VxeSplitterDefines.PaneConfig, nextItem: VxeSplitterDefines.PaneConfig) => {
const { border, resize, vertical } = props
const { itemList } = reactData
const barStyle = computeBarStyle.value
const actionOpts = computeActionOpts.value
const { direction } = actionOpts
const showPrevButton = XEUtils.isBoolean(actionOpts.showPrevButton) ? actionOpts.showPrevButton : (itemList.some(item => item.showAction))
const showNextButton = XEUtils.isBoolean(actionOpts.showNextButton) ? actionOpts.showNextButton : (direction === 'next' && itemList.some(item => item.showAction))
const resizeOpts = computeResizeOpts.value
const { immediate } = resizeOpts
return h('div', {
class: ['vxe-splitter-panel-handle', vertical ? 'is--vertical' : 'is--horizontal', immediate ? 'is-resize--immediate' : 'is-resize--lazy', {
'is--resize': resize,
'is--border': border
}]
}, [
h('div', {
class: 'vxe-splitter-panel-handle-bar',
style: barStyle,
onMousedown: dragEvent
}),
itemList.length === 2
? h('div', {
class: 'vxe-splitter-panel-action-btn-wrapper'
}, [
showPrevButton && nextItem.isExpand
? h('div', {
class: 'vxe-splitter-panel-action-btn',
onDblclick: handlePrevActionDblclickEvent,
onClick: handlePrevActionClickEvent
}, [
h('i', {
class: getActionIcon(prevItem, nextItem, false)
})
])
: renderEmptyElement($xeSplitter),
showNextButton && prevItem.isExpand
? h('div', {
class: 'vxe-splitter-panel-action-btn',
onDblclick: handleNextActionDblclickEvent,
onClick: handleNextActionClickEvent
}, [
h('i', {
class: getActionIcon(prevItem, nextItem, true)
})
])
: renderEmptyElement($xeSplitter)
])
: renderEmptyElement($xeSplitter)
])
}
const renderItems = () => {
const { border, padding, resize, vertical } = props
const { itemList } = reactData
const vSize = computeSize.value
const resizeOpts = computeResizeOpts.value
const { immediate } = resizeOpts
const visibleItems = computeVisibleItems.value
const { autoItems } = computeAutoItems.value
const itemVNs: VNode[] = []
itemList.forEach((prevItem, index) => {
const { id, name, slots, renderHeight, resizeHeight, foldHeight, renderWidth, resizeWidth, foldWidth, isExpand } = prevItem
const nextItem = itemList[index + 1]
const defaultSlot = slots ? slots.default : null
const stys: Record<string, string | number> = {}
let itemWidth = isExpand ? (foldWidth || resizeWidth || renderWidth) : 0
let itemHeight = isExpand ? (foldHeight || resizeHeight || renderHeight) : 0
// 至少存在一个自适应
if (autoItems.length === 1) {
if (vertical) {
if (!prevItem.height) {
itemHeight = 0
}
} else {
if (!prevItem.width) {
itemWidth = 0
}
}
}
let isFill = true
if (vertical) {
if (itemHeight && visibleItems.length > 1) {
isFill = false
stys.height = toCssUnit(itemHeight)
}
} else {
if (itemWidth && visibleItems.length > 1) {
isFill = false
stys.width = toCssUnit(itemWidth)
}
}
itemVNs.push(
h('div', {
itemid: id,
class: ['vxe-splitter-panel', vertical ? 'is--vertical' : 'is--horizontal', immediate ? 'is-resize--immediate' : 'is-resize--lazy', {
[`size--${vSize}`]: vSize,
'is--resize': resize,
'is--padding': padding,
'is--border': border,
'is--height': itemHeight,
'is--width': itemWidth,
'is--visible': isExpand,
'is--hidden': !isExpand,
'is--fill': isExpand && isFill
}],
style: stys
}, [
h('div', {
itemid: id,
class: 'vxe-splitter-panel--wrapper'
}, [
h('div', {
class: 'vxe-splitter-panel--inner'
}, defaultSlot ? callSlot(defaultSlot, { name, isExpand }) : [])
])
])
)
if (nextItem) {
itemVNs.push(renderHandleBar(prevItem, nextItem))
}
})
return h('div', {
class: 'vxe-splitter-wrapper'
}, itemVNs)
}
const renderVN = () => {
const { vertical, width, height } = props
const vSize = computeSize.value
const resizeOpts = computeResizeOpts.value
const { immediate, showTip } = resizeOpts
const defaultSlot = slots.default
const stys: Record<string, string | number> = {}
if (height) {
stys.height = toCssUnit(height)
}
if (width) {
stys.width = toCssUnit(width)
}
return h('div', {
ref: refElem,
class: ['vxe-splitter', vertical ? 'is--vertical' : 'is--horizontal', immediate ? 'is-resize--immediate' : 'is-resize--lazy', {
[`size--${vSize}`]: vSize
}],
style: stys
}, [
h('div', {
class: 'vxe-splitter-slots'
}, defaultSlot ? defaultSlot({}) : []),
renderItems(),
h('div', {
ref: refResizableSplitterTip,
class: ['vxe-splitter--resizable-splitter-tip', vertical ? 'is--vertical' : 'is--horizontal', immediate ? 'is-resize--immediate' : 'is-resize--lazy']
}, showTip
? [
h('div', {
class: 'vxe-splitter--resizable-splitter-tip-number'
}, [
h('div', {
class: 'vxe-splitter--resizable-splitter-number-prev'
}),
h('div', {
class: 'vxe-splitter--resizable-splitter-number-next'
})
])
]
: []),
h('div', {
class: 'vxe-splitter--render-vars'
}, [
h('div', {
ref: refBarInfoElem,
class: 'vxe-splitter--handle-bar-info'
})
])
])
}
const itemFlag = ref(0)
watch(() => props.items ? props.items.length : -1, () => {
itemFlag.value++
})
watch(() => props.items, () => {
itemFlag.value++
})
watch(itemFlag, () => {
loadItem(props.items || [])
})
watch(() => reactData.staticItems, (val) => {
const actionOpts = computeActionOpts.value
const { showPrevButton, showNextButton } = actionOpts
if (props.items && props.items.length) {
errLog('vxe.error.errConflicts', ['<vxe-splitter-panel ...>', 'items'])
}
reactData.itemList = val || []
if ((showPrevButton || showNextButton) && reactData.itemList.length > 2) {
errLog('vxe.error.modelConflicts', ['[splitter] action-config.showPrevButton | action-config.showNextButton', '<vxe-splitter-panel ...> Only supports 2 panel'])
}
reactData.itemList.forEach(item => {
if (item.showAction) {
warnLog('vxe.error.removeProp', ['[splitter] showAction'])
}
})
recalculate()
})
let resizeObserver: ResizeObserver
onMounted(() => {
nextTick(() => {
recalculate()
})
const el = refElem.value
if (el) {
resizeObserver = globalResize.create(() => {
recalculate()
})
resizeObserver.observe(el)
}
const actionOpts = computeActionOpts.value
if (actionOpts.direction) {
errLog('vxe.error.delProp', ['[splitter] action-config.direction', 'action-config.showPrevButton | action-config.showNextButton'])
}
globalEvents.on($xeSplitter, 'resize', handleGlobalResizeEvent)
})
onUnmounted(() => {
if (resizeObserver) {
resizeObserver.disconnect()
}
globalEvents.off($xeSplitter, 'resize')
})
onActivated(() => {
recalculate()
})
if (props.items) {
loadItem(props.items)
}
provide('$xeSplitter', $xeSplitter)
$xeSplitter.renderVN = renderVN
return $xeSplitter
},
render () {
return this.renderVN()
}
})