feat(components): [splitter] new component (#20145)

* feat(components): [splitter] new component

* docs(components): [splitter] update docs

* feat(components): [splitter] add export ElSplitter ElSplitterPanel types

* feat(components): [splitter] support touch even

* Update docs/.vitepress/crowdin/en-US/pages/component.json

Co-authored-by: sea <45450994+warmthsea@users.noreply.github.com>

* docs: update

* docs: update

* chore: update docs & code

* chore: update docs & fix code type error

* fix: use slot show error

* refactor(components): [splitter] modify props use

* refactor(components): [splitter] use native event listener to drag

* fix(components): [splitter] panel bind attrs

* refactor: use `useOrderedChildren`

* chore: fix types

* feat: add icon

* chore: translate comments and add beta tag

* docs: add default-size api

* docs: remove demo lang="scss"

* refactor: remove default-size api

---------

Co-authored-by: kooriookami <38392315+kooriookami@users.noreply.github.com>
Co-authored-by: sea <45450994+warmthsea@users.noreply.github.com>
Co-authored-by: warmthsea <2586244885@qq.com>
Co-authored-by: Dsaquel <291874700n@gmail.com>
Co-authored-by: kooriookami <bingshuanglingluo@163.com>
This commit is contained in:
Gnalvin
2025-06-03 23:01:28 +08:00
committed by GitHub
parent a48b496ba5
commit d720e5c31c
35 changed files with 1515 additions and 1 deletions

View File

@@ -52,6 +52,11 @@
"link": "/space",
"text": "Space"
},
{
"link": "/splitter",
"text": "Splitter",
"promotion": "2.10.0"
},
{
"link": "/typography",
"text": "Typography"

View File

@@ -61,6 +61,14 @@
>
@叨叨
</el-link>
<el-link
type="primary"
:underline="false"
href="https://github.com/zhiwendesign"
target="_blank"
>
@卡卡
</el-link>
</p>
</div>
</div>
@@ -189,7 +197,10 @@ onMounted(() => {
}
.designed-by {
text-align: right;
display: flex;
justify-content: flex-end;
align-items: center;
gap: 5px;
font-size: 14px;
}
}

View File

@@ -73,6 +73,7 @@ import OvDivider from './ov-divider.vue'
import OvWatermark from './ov-watermark.vue'
import OvMention from './ov-mention.vue'
import OvInputTag from './ov-input-tag.vue'
import OvSplitter from './ov-splitter.vue'
export default {
button: OvButton,
@@ -139,6 +140,7 @@ export default {
menu: OvMenu,
'page-header': OvPageHeader,
steps: OvSteps,
splitter: OvSplitter,
tabs: OvTabs,
alert: OvAlert,
dialog: OvDialog,

View File

@@ -0,0 +1,109 @@
<template>
<svg
width="280"
height="180"
viewBox="0 0 280 180"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="280" height="180" fill="var(--el-fill-color-light)" />
<g clip-path="url(#clip0_2477_1001)">
<mask id="path-3-inside-1_2477_1001" fill="white">
<path d="M80 56H120V124H80V56Z" />
</mask>
<path d="M80 56H120V124H80V56Z" fill="var(--el-fill-color-blank)" />
<path
d="M120 56H119.5V124H120H120.5V56H120Z"
fill="#CDD0D6"
mask="url(#path-3-inside-1_2477_1001)"
/>
<mask id="path-5-inside-2_2477_1001" fill="white">
<path d="M120 56H160V124H120V56Z" />
</mask>
<path d="M120 56H160V124H120V56Z" fill="var(--el-fill-color-blank)" />
<path
d="M160 56H159.5V124H160H160.5V56H160Z"
fill="#CDD0D6"
mask="url(#path-5-inside-2_2477_1001)"
/>
<rect
width="40"
height="68"
transform="translate(160 56)"
fill="var(--el-color-primary-light-8)"
/>
<rect
x="165"
y="63"
width="20"
height="4"
rx="2"
fill="var(--el-color-primary)"
/>
<rect
x="165"
y="70"
width="4"
height="4"
rx="2"
fill="var(--el-color-primary-light-5)"
/>
<rect
x="172"
y="70"
width="21"
height="4"
rx="2"
fill="var(--el-color-primary-light-5)"
/>
<rect
x="172"
y="77"
width="21"
height="4"
rx="2"
fill="var(--el-color-primary-light-5)"
/>
<path
d="M159.75 83.3335V96.6668"
stroke="var(--el-color-primary)"
stroke-width="1.25"
stroke-linecap="round"
/>
<path
d="M152.868 90.1179C152.803 90.0528 152.803 89.9472 152.868 89.8821L154.965 87.7845C155.07 87.6795 155.25 87.7539 155.25 87.9024V92.0976C155.25 92.2461 155.07 92.3205 154.965 92.2155L152.868 90.1179Z"
fill="var(--el-color-primary)"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M154.25 89.375L160.25 89.375V90.625L154.25 90.625V89.375Z"
fill="var(--el-color-primary)"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M158.583 89.375L164.583 89.375V90.625L158.583 90.625V89.375Z"
fill="var(--el-color-primary)"
/>
<path
d="M166.965 89.8821C167.03 89.9472 167.03 90.0528 166.965 90.1179L164.868 92.2155C164.763 92.3205 164.583 92.2461 164.583 92.0976V87.9024C164.583 87.7539 164.763 87.6795 164.868 87.7845L166.965 89.8821Z"
fill="var(--el-color-primary)"
/>
</g>
<rect
x="80.25"
y="56.25"
width="119.5"
height="67.5"
rx="3.75"
stroke="var(--el-border-color-dark)"
stroke-width="0.5"
/>
<defs>
<clipPath id="clip0_2477_1001">
<rect x="80" y="56" width="120" height="68" rx="4" fill="white" />
</clipPath>
</defs>
</svg>
</template>

View File

@@ -103,6 +103,7 @@ declare module 'vue' {
OvSkeleton: typeof import('./.vitepress/vitepress/components/overview-icons/ov-skeleton.vue')['default']
OvSlider: typeof import('./.vitepress/vitepress/components/overview-icons/ov-slider.vue')['default']
OvSpace: typeof import('./.vitepress/vitepress/components/overview-icons/ov-space.vue')['default']
OvSplitter: typeof import('./.vitepress/vitepress/components/overview-icons/ov-splitter.vue')['default']
OvStatistic: typeof import('./.vitepress/vitepress/components/overview-icons/ov-statistic.vue')['default']
OvSteps: typeof import('./.vitepress/vitepress/components/overview-icons/ov-steps.vue')['default']
OvSwitch: typeof import('./.vitepress/vitepress/components/overview-icons/ov-switch.vue')['default']

View File

@@ -0,0 +1,100 @@
---
title: Splitter
lang: en-US
---
# Splitter ^(beta)
Divide the area horizontally or vertically, and freely drag to adjust the size of each area.
## Basic usage
The most basic usage, if no default size is passed, it will be automatically divided equally.
:::demo
splitter/basic
:::
## Vertical
Use vertical orientation.
:::demo
splitter/vertical
:::
## Collapsible
Configuring `collapsible` provides quick shrinking capability. You can use the `min` property to prevent expanding through dragging after collapsing.
:::demo
splitter/collapsible
:::
## Disable drag
When either panel disables `resizable`, dragging will be disabled.
:::demo
splitter/disableDrag
:::
## Panel size
`v-model:size` can get the panel size.
:::demo
splitter/size
:::
## Splitter API
### Splitter Attributes
| Name | Description | Type | Default |
| ------ | -------------------------------- | ----------------------------------- | ---------- |
| layout | Layout direction of the splitter | ^[enum]`'horizontal' \| 'vertical'` | horizontal |
### Splitter Events
| Name | Description | type |
| ------------ | ------------------------------------------------------------------------ | ----------------------------------------------------- |
| resize-start | Triggered when starting to resize a panel, `index` is the drag bar index | ^[Function]`(index: number, sizes: number[]) => void` |
| resize | Triggered while resizing a panel, `index` is the drag bar index | ^[Function]`(index: number, sizes: number[]) => void` |
| resize-end | Triggered when panel resizing ends, `index` is the drag bar index | ^[Function]`(index: number, sizes: number[]) => void` |
## SplitterPanel API
### SplitterPanel Attributes
| Name | Description | Type | Default |
| ------------------- | --------------------------------------------------- | --------------------- | ------- |
| size / v-model:size | Size of the panel (in pixels or percentage) | ^[string] / ^[number] | - |
| min | Minimum size of the panel (in pixels or percentage) | ^[string] / ^[number] | - |
| max | Maximum size of the panel (in pixels or percentage) | ^[string] / ^[number] | - |
| resizable | Whether the panel can be resized | ^[boolean] | true |
| collapsible | Whether the panel can be collapsed | ^[boolean] | false |
### SplitterPanel Events
| Name | Description | type |
| ----------- | --------------------------------- | ----------------------------------- |
| update:size | Triggered when panel size changes | ^[Function]`(size: number) => void` |
### SplitterPanel Slots
| Name | Description |
| ----------------- | ----------------------------------------------- |
| default | Default content of the panel |
| start-collapsible | Custom content for the start collapsible button |
| end-collapsible | Custom content for the end collapsible button |

View File

@@ -0,0 +1,23 @@
<template>
<div
style="height: 250px; box-shadow: var(--el-border-color-light) 0px 0px 10px"
>
<el-splitter>
<el-splitter-panel size="30%">
<div class="demo-panel">1</div>
</el-splitter-panel>
<el-splitter-panel :min="200">
<div class="demo-panel">2</div>
</el-splitter-panel>
</el-splitter>
</div>
</template>
<style scoped>
.demo-panel {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
</style>

View File

@@ -0,0 +1,36 @@
<template>
<div
style="height: 250px; box-shadow: var(--el-border-color-light) 0px 0px 10px"
>
<el-splitter>
<el-splitter-panel collapsible min="50">
<div class="demo-panel">1</div>
</el-splitter-panel>
<el-splitter-panel collapsible>
<div class="demo-panel">2</div>
</el-splitter-panel>
<el-splitter-panel>
<div class="demo-panel">3</div>
</el-splitter-panel>
<el-splitter-panel collapsible>
<el-splitter layout="vertical">
<el-splitter-panel collapsible>
<div class="demo-panel">4</div>
</el-splitter-panel>
<el-splitter-panel collapsible>
<div class="demo-panel">5</div>
</el-splitter-panel>
</el-splitter>
</el-splitter-panel>
</el-splitter>
</div>
</template>
<style scoped>
.demo-panel {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
</style>

View File

@@ -0,0 +1,41 @@
<template>
<el-switch
v-model="resizable"
active-text="enable"
inactive-text="disable"
inline-prompt
class="mb-2"
/>
<div
style="height: 250px; box-shadow: var(--el-border-color-light) 0px 0px 10px"
>
<el-splitter>
<el-splitter-panel>
<div class="demo-panel">1</div>
</el-splitter-panel>
<el-splitter-panel :resizable="resizable">
<div class="demo-panel">
drag {{ resizable ? 'enable' : 'disable' }}
</div>
</el-splitter-panel>
<el-splitter-panel>
<div class="demo-panel">3</div>
</el-splitter-panel>
</el-splitter>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const resizable = ref(false)
</script>
<style scoped>
.demo-panel {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
</style>

View File

@@ -0,0 +1,48 @@
<template>
<div
style="height: 250px; box-shadow: var(--el-border-color-light) 0px 0px 10px"
>
<el-splitter
@resize-start="handleResizeStart"
@resize-end="handleResizeEnd"
@resize="handleResize"
>
<el-splitter-panel>
<div class="demo-panel">1</div>
</el-splitter-panel>
<el-splitter-panel v-model:size="size" :max="200" :min="50">
<div class="demo-panel">{{ size }}px</div>
</el-splitter-panel>
<el-splitter-panel>
<div class="demo-panel">3</div>
</el-splitter-panel>
</el-splitter>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const size = ref(100)
const handleResizeStart = (index: number, sizes: number[]) => {
console.log('resizeStart', index, sizes)
}
const handleResize = (index: number, sizes: number[]) => {
console.log('resize', index, sizes)
}
const handleResizeEnd = (index: number, sizes: number[]) => {
console.log('resizeEnd', index, sizes)
}
</script>
<style scoped>
.demo-panel {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
</style>

View File

@@ -0,0 +1,23 @@
<template>
<div
style="height: 250px; box-shadow: var(--el-border-color-light) 0px 0px 10px"
>
<el-splitter layout="vertical">
<el-splitter-panel>
<div class="demo-panel">1</div>
</el-splitter-panel>
<el-splitter-panel>
<div class="demo-panel">2</div>
</el-splitter-panel>
</el-splitter>
</div>
</template>
<style scoped>
.demo-panel {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
</style>