UNPKG

vxe-pc-ui

Version:
1,010 lines (938 loc) 37.2 kB
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() } })