diff --git a/docs/en-US/component/splitter.md b/docs/en-US/component/splitter.md index fb1e9eb1b5..3ba2b4d7bb 100644 --- a/docs/en-US/component/splitter.md +++ b/docs/en-US/component/splitter.md @@ -57,13 +57,24 @@ splitter/size ::: +## Lazy ^(2.11.0) + +When `lazy` is enabled, the panel size will not update in real time during dragging, but only after the drag ends. + +:::demo + +splitter/lazy + +::: + ## Splitter API ### Splitter Attributes -| Name | Description | Type | Default | -| ------ | -------------------------------- | ----------------------------------- | ---------- | -| layout | Layout direction of the splitter | ^[enum]`'horizontal' \| 'vertical'` | horizontal | +| Name | Description | Type | Default | +| -------------- | -------------------------------- | ----------------------------------- | ---------- | +| layout | Layout direction of the splitter | ^[enum]`'horizontal' \| 'vertical'` | horizontal | +| lazy ^(2.11.0) | Whether to enable lazy mode | ^[boolean] | false | ### Splitter Events diff --git a/docs/examples/splitter/lazy.vue b/docs/examples/splitter/lazy.vue new file mode 100644 index 0000000000..8e54fc73d9 --- /dev/null +++ b/docs/examples/splitter/lazy.vue @@ -0,0 +1,26 @@ + + + diff --git a/packages/components/splitter/__tests__/splitter.test.tsx b/packages/components/splitter/__tests__/splitter.test.tsx index 15b58d30cd..cb690a6217 100644 --- a/packages/components/splitter/__tests__/splitter.test.tsx +++ b/packages/components/splitter/__tests__/splitter.test.tsx @@ -269,4 +269,64 @@ describe('Splitter', () => { // Panel should be collapsed (size = 400) expect(panels[0].attributes('style')).toContain('flex-basis: 400px;') }) + + it('should not update panel size until drag ends when lazy is true', async () => { + const wrapper = mount(() => ( +
+ + Left Panel + Right Panel + +
+ )) + await nextTick() + const panels = wrapper.findAll('.el-splitter-panel') + const splitBar = wrapper.find('.el-splitter-bar__dragger') + + const mousedown = new MouseEvent('mousedown', { bubbles: true }) + Object.defineProperty(mousedown, 'pageX', { value: 200 }) + splitBar.element.dispatchEvent(mousedown) + + const mousemove = new MouseEvent('mousemove', { bubbles: true }) + Object.defineProperty(mousemove, 'pageX', { value: 100 }) + window.dispatchEvent(mousemove) + await nextTick() + expect(panels[0].attributes('style')).toContain('flex-basis: 200px;') + + const mouseup = new MouseEvent('mouseup', { bubbles: true }) + Object.defineProperty(mouseup, 'pageX', { value: 100 }) + window.dispatchEvent(mouseup) + await nextTick() + expect(panels[0].attributes('style')).toContain('flex-basis: 100px;') + }) + + it('should update panel size immediately when lazy is false', async () => { + const wrapper = mount(() => ( +
+ + Left Panel + Right Panel + +
+ )) + await nextTick() + const panels = wrapper.findAll('.el-splitter-panel') + const splitBar = wrapper.find('.el-splitter-bar__dragger') + + const mousedown = new MouseEvent('mousedown', { bubbles: true }) + Object.defineProperty(mousedown, 'pageX', { value: 200 }) + splitBar.element.dispatchEvent(mousedown) + + const mousemove = new MouseEvent('mousemove', { bubbles: true }) + Object.defineProperty(mousemove, 'pageX', { value: 100 }) + window.dispatchEvent(mousemove) + await nextTick() + expect(panels[0].attributes('style')).toContain('flex-basis: 100px;') + + const mouseup = new MouseEvent('mouseup', { bubbles: true }) + Object.defineProperty(mouseup, 'pageX', { value: 100 }) + window.dispatchEvent(mouseup) + await nextTick() + expect(panels[0].attributes('style')).toContain('flex-basis: 100px;') + }) }) diff --git a/packages/components/splitter/src/hooks/useResize.ts b/packages/components/splitter/src/hooks/useResize.ts index c99a8d2886..4a9f26c59f 100644 --- a/packages/components/splitter/src/hooks/useResize.ts +++ b/packages/components/splitter/src/hooks/useResize.ts @@ -1,5 +1,6 @@ -import { computed, ref } from 'vue' +import { computed, ref, watch } from 'vue' import { getPct, getPx, isPct, isPx } from './useSize' +import { NOOP } from '@element-plus/utils' import type { ComputedRef, Ref } from 'vue' import type { PanelItemState } from '../type' @@ -7,7 +8,8 @@ import type { PanelItemState } from '../type' export function useResize( panels: Ref, containerSize: ComputedRef, - pxSizes: ComputedRef + pxSizes: ComputedRef, + lazy: Ref ) { const ptg2px = (ptg: number) => ptg * containerSize.value || 0 @@ -23,18 +25,28 @@ export function useResize( return str ?? defaultLimit } + const lazyOffset = ref(0) const movingIndex = ref<{ index: number confirmed: boolean } | null>(null) let cachePxSizes: number[] = [] + let updatePanelSizes = NOOP const limitSizes = computed(() => panels.value.map((item) => [item.min, item.max]) ) + watch(lazy, () => { + if (lazyOffset.value) { + const mouseup = new MouseEvent('mouseup', { bubbles: true }) + window.dispatchEvent(mouseup) + } + }) + const onMoveStart = (index: number) => { + lazyOffset.value = 0 movingIndex.value = { index, confirmed: false } cachePxSizes = pxSizes.value } @@ -91,13 +103,26 @@ export function useResize( numSizes[mergedIndex]! += mergedOffset numSizes[nextIndex]! -= mergedOffset + lazyOffset.value = mergedOffset - panels.value.forEach((panel, index) => { - panel.size = numSizes[index] - }) + updatePanelSizes = () => { + panels.value.forEach((panel, index) => { + panel.size = numSizes[index] + }) + updatePanelSizes = NOOP + } + + if (!lazy.value) { + updatePanelSizes() + } } const onMoveEnd = () => { + if (lazy.value) { + updatePanelSizes() + } + + lazyOffset.value = 0 movingIndex.value = null cachePxSizes = [] } @@ -135,5 +160,12 @@ export function useResize( }) } - return { onMoveStart, onMoving, onMoveEnd, movingIndex, onCollapse } + return { + lazyOffset, + onMoveStart, + onMoving, + onMoveEnd, + movingIndex, + onCollapse, + } } diff --git a/packages/components/splitter/src/split-bar.vue b/packages/components/splitter/src/split-bar.vue index e30e8b9871..5035de6f2c 100644 --- a/packages/components/splitter/src/split-bar.vue +++ b/packages/components/splitter/src/split-bar.vue @@ -28,12 +28,9 @@ const props = defineProps({ type: Boolean, default: true, }, - startCollapsible: { - type: Boolean, - }, - endCollapsible: { - type: Boolean, - }, + lazy: Boolean, + startCollapsible: Boolean, + endCollapsible: Boolean, }) const emit = defineEmits(['moveStart', 'moving', 'moveEnd', 'collapse']) @@ -143,6 +140,7 @@ const EndIcon = computed(() => (isHorizontal.value ? ArrowRight : ArrowDown)) ns.e('dragger'), draggerPseudoClass, resizable ? '' : ns.e('disable'), + ns.is('lazy', resizable && lazy), ]" :style="draggerStyles" @mousedown="onMousedown" diff --git a/packages/components/splitter/src/split-panel.vue b/packages/components/splitter/src/split-panel.vue index 9d71c879f9..b2826c5d2a 100644 --- a/packages/components/splitter/src/split-panel.vue +++ b/packages/components/splitter/src/split-panel.vue @@ -37,7 +37,7 @@ if (!splitterContext) 'usage: ' ) -const { panels, layout, containerSize, pxSizes } = toRefs(splitterContext) +const { panels, layout, lazy, containerSize, pxSizes } = toRefs(splitterContext) const { registerPanel, @@ -179,6 +179,7 @@ onBeforeUnmount(() => unregisterPanel(_panel)) v-if="isShowBar" :index="index" :layout="layout" + :lazy="lazy" :resizable="isResizable" :start-collapsible="startCollapsible" :end-collapsible="endCollapsible" diff --git a/packages/components/splitter/src/splitter.ts b/packages/components/splitter/src/splitter.ts index 72d079362b..524b374860 100644 --- a/packages/components/splitter/src/splitter.ts +++ b/packages/components/splitter/src/splitter.ts @@ -9,6 +9,7 @@ export const splitterProps = buildProps({ default: 'horizontal', values: ['horizontal', 'vertical'] as const, }, + lazy: Boolean, } as const) export type SplitterProps = ExtractPropTypes diff --git a/packages/components/splitter/src/splitter.vue b/packages/components/splitter/src/splitter.vue index d19470c728..fadadbc3fe 100644 --- a/packages/components/splitter/src/splitter.vue +++ b/packages/components/splitter/src/splitter.vue @@ -1,5 +1,12 @@