In this example the splitters are thin lines but the reactive touch zone is spread to 30 pixels all around! Hover a splitter to see the enlarged fat-finger-proof reactive zone.
A Vue.js reliable, simple and touch-ready panes splitter / resizer.
<splitpanes style="height: 400px"> <pane min-size="20">1</pane> <pane> <splitpanes horizontal> <pane>2</pane> <pane>3</pane> <pane>4</pane> </splitpanes> </pane> <pane>5</pane> </splitpanes>
.splitpanes__pane { display: flex; justify-content: center; align-items: center; font-family: Helvetica, Arial, sans-serif; color: rgba(255, 255, 255, 0.6); font-size: 5em; }
You have two options: NPM or <script> tag.
npm i @tenrok/vue-splitpanes@legacy # For Vue 2.x.
npm i @tenrok/vue-splitpanes # For Vue 3.
chevron_rightView and edit a workingVue 3 example on Codepen.
// In your Vue component. import { Splitpanes, Pane } from '@tenrok/vue-splitpanes' import '@tenrok/vue-splitpanes/dist/splitpanes.css' export default { components: { Splitpanes, Pane }, ... }
Include the Splitpanes script in your document <head> as follows:
<head> ... <script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/vue-splitpanes"></script> <link href="https://unpkg.com/vue-splitpanes/dist/splitpanes.css" rel="stylesheet"> </head>
Once included in your project, use as follows.
<splitpanes class="default-theme"> <pane v-for="i in 3" :key="i"> <div>{{ i }}</div> </pane> </splitpanes>
checkNo splitter tags!
The splitters will be added automatically between the <pane> tags.
wb_incandescentBy default the layout is vertical, if you need you can set the attribute horizontal on the <splitpanes> tag to change the layout to rows.
wb_incandescentThe CSS is external so you can easily override or choose not to include it at all.
If you want to use it, you can also optionally use the CSS class default-theme
at the root of your splitpanes to apply the default theme like on this page.
If you want to go with your own style, you can check the Do Your Own Style example.
View and edit a working Vue 3 example on Codepen
You can also double click a splitter to maximize the next pane! (First pane splitter will be an option soon)
If you want to disable the 'double click splitter to maximize' behavior, you can add this attribute: :dbl-click-splitter="false".
<splitpanes class="default-theme" horizontal style="height: 400px"> <pane min-size="20" max-size="70"> <span>1</span> </pane> <pane> <span>2</span> </pane> <pane max-size="70"> <span>3</span> </pane> </splitpanes>
Provide dimension of your panes when they first load (will be used for the width or height respectively for the vertical or horizontal layout).
If you provide a default width or height, make sure you provide it for all the panes and the total equals 100%.
If a pane is missing a default width or height, then all the panes will have the same width or height.
Note that setting a default value is different than setting a min or max value.
<splitpanes class="default-theme" horizontal style="height: 400px"> <pane size="65"> <span>1</span> </pane> <pane size="10"> <span>2</span> </pane> <pane size="25"> <span>3</span> </pane> </splitpanes>
Try it yourself on Codepenopen_in_new
<splitpanes class="default-theme" horizontal :push-other-panes="false" style="height: 400px"> <pane> <span>1</span> </pane> <pane> <splitpanes :push-other-panes="false"> <pane> <span>2</span> </pane> <pane> <span>3</span> </pane> <pane> <span>4</span> </pane> </splitpanes> </pane> <pane> <span>5</span> </pane> </splitpanes>
<splitpanes class="default-theme" style="height: 400px"> <pane v-for="i in 8" :key="i" min-size="5"> <span>{{ i }}</span> </pane> </splitpanes>
This example shows the reactivity when you add a new element dynamically in splitpanes.
<button @click="panesNumber++">Add pane</button> <button @click="panesNumber--">Remove pane</button> <splitpanes class="default-theme" style="height: 400px"> <pane v-for="i in panesNumber" :key="i"> <span>{{ i }}</span> </pane> </splitpanes>
// In your Vue component. data: () => ({ panesNumber: 3 })
When changing direction, all the panes current width or height will flip to adapt to the new layout.
Showing the first splitter is an option which allows user to double click the splitter to maximize the next pane.
The first splitter does not allow to resize the next pane.
<button @click="horizontal = !horizontal">Switch to {{ horizontal ? 'Vertical' : 'Horizontal' }}</button> <button @click="firstSplitter = !firstSplitter">{{ firstSplitter ? 'Hide' : 'Show' }} First Splitter</button> <splitpanes class="default-theme" :horizontal="horizontal" :first-splitter="firstSplitter" style="height: 400px"> <pane v-for="i in 3" :key="i"> <span>{{ i }}%</span> </pane> </splitpanes>
data: () => ({ horizontal: false firstSplitter: false })
This example shows the programmatic way of resizing panes. And how it works both ways.
<w-slider v-model="paneSize" label="First pane size" :min="0" :max="100"> <splitpanes class="default-theme" @resize="paneSize = $event[0].size" style="height: 400px"> <pane :size="paneSize"> <span>{{ paneSize }}%</span> </pane> <pane :size="100 - paneSize"> <span>{{ 100 - paneSize }}%</span> </pane> </splitpanes>
// In your Vue component. data: () => ({ paneSize: 50 })
This example shows the reactivity when you modify anything in your component inside splitpanes.
<button @click="generateRandomNumber">Generate 3 random numbers</button> <button @click="incrementNumber(3)">Increment pane #3</button> <splitpanes horizontal class="default-theme" style="height: 400px"> <pane> <splitpanes> <pane v-for="i in 3" :key="i"> <span>{{ i }}</span><br> <em>Number is: {{ randomNums[i] }}</em><br> <em v-if="i === 2"> Number on the left is: {{ randomNums[1] }}<br> Number on the right is: {{ randomNums[3] }}<br> </em> <button(v-if="i !== 2" @click="randomNums[i] = randomNums[i] + 1">+1</button> </pane> </splitpanes> </pane> <pane> <span>4</span><br> <em> - Nested splitpanes -<br> [{{ randomNums[1] }}, {{ randomNums[2] }}, {{ randomNums[1] }}] </em> </pane> </splitpanes>
// In your Vue component. data: () => ({ randomNums: { 1: 0, 2: 0, 3: 0 } }), methods: { generateRandomNumber () { this.randomNums = Object.assign(this.randomNums, { 1: Math.round(Math.random() * 100), 2: Math.round(Math.random() * 100), 3: Math.round(Math.random() * 100) }) }, incrementNumber (i) { this.randomNums[i]++ } }
<button @click="hidePane2 = !hidePane2">{{ hidePane2 ? 'Show' : 'Hide' }} Pane 2</button> <splitpanes class="default-theme" style="height: 400px"> <pane> <span>1</span> </pane> <pane v-if="!hidePane2"> <span>2</span> </pane> <pane> <span>3</span> </pane> </splitpanes>
This is another reactivity example of a rather common case: Vue Router inside splitpanes.
The navigation is in the left pane, but you can also access from outside of splitpanes, through those buttons:
<button to="home-view">Home view</button> <button to="another-view">Another view</button> <splitpanes horizontal class="default-theme" style="height: 400px"> <pane min-size="20"> <p>Navigation</p> <ul> <li><router-link to="home-view">Home view</li> <li><router-link to="another-view">Another view</li> </ul> </pane> <pane> <em>router-view</em> <router-view /> </pane> <pane> <span>3</span> </pane> </splitpanes>
// Vue Router routes. routes: [ { path: '/home-view', component: () => import(/* webpackChunkName: "home-view" */ './components/home-view.vue') }, { path: '/another-view', component: () => import(/* webpackChunkName: "another-view" */ './components/another-view.vue') } ]
<template> <div class="green"> <div>This is home</div> </div> </template>
Here is the list of events that are emitted from splitpanes:
ready
has no parameter and fires when splitpanes is readyresize
returns an array of all the panes objects with their dimensions, and fires while resizing (on mousemove/touchmove)resized
returns an array of all the panes objects with their dimensions, and fires once when the resizing stops after user drag (on mouseup/touchend).pane-click
returns the clicked pane object with its dimensions.pane-maximize
returns the maximized pane object with its dimensions.pane-add
returns an object containing the index of the added pane and the new array of panes after resize.pane-remove
returns an object containing the removed pane and an array of all the remaining panes objects with their dimensions after resize.splitter-click
returns the next pane object (with its dimensions) directly after the clicked splitter.Try resizing panes and check the logs bellow.
// Event name: Event params (Last event on top)ready:
<splitpanes class="default-theme" @resize="log('resize', $event)" @resized="log('resized', $event)" @pane-maximize="log('pane-maximize', $event)" @pane-click="log('pane-click', $event)" @ready="log('ready', $event)" @splitter-click="log('splitter-click', $event)" style="height: 400px"> <pane v-for="i in 3" :key="i" min-size="10"> <span>{{ i }}</span> </pane> </splitpanes>
Try it yourself on Codepenopen_in_new
In this example the splitters are thin lines but the reactive touch zone is spread to 30 pixels all around! Hover a splitter to see the enlarged fat-finger-proof reactive zone.
<splitpanes horizontal style="height: 400px"> <pane> <splitpanes> <pane> <span>1</span> </pane> <pane> <span>2</span> </pane> <pane> <span>3</span> </pane> </splitpanes> </pane> <pane> <p>In this example the splitters are thin lines but the reactive touch zone is spread to 30 pixels all around!</p> </pane> </splitpanes>
.splitpanes {background-color: #f8f8f8;} .splitpanes__splitter {background-color: #ccc;position: relative;} .splitpanes__splitter:before { content: ''; position: absolute; left: 0; top: 0; transition: opacity 0.4s; background-color: rgba(255, 0, 0, 0.3); opacity: 0; z-index: 1; } .splitpanes__splitter:hover:before {opacity: 1;} .splitpanes--vertical > .splitpanes__splitter:before {left: -30px;right: -30px;height: 100%;} .splitpanes--horizontal > .splitpanes__splitter:before {top: -30px;bottom: -30px;width: 100%;}
If you don't want to use the default style, here is how to do your own.
Try it yourself on Codepenopen_in_new
<splitpanes horizontal style="height: 400px"> <pane> <splitpanes vertical> <pane> <span>1</span> </pane> <pane> <span>2</span> </pane> <pane> <span>3</span> </pane> </splitpanes> </pane> <pane> <span>4</span> </pane> </splitpanes>
.splitpanes { background: linear-gradient(-45deg, #EE7752, #E73C7E, #23A6D5, #23D5AB); } .splitpanes__pane { box-shadow: 0 0 5px rgba(0, 0, 0, .2) inset; justify-content: center; align-items: center; display: flex; } .splitpanes--vertical > .splitpanes__splitter { min-width: 6px; background: linear-gradient(90deg, #ccc, #111); } .splitpanes--horizontal > .splitpanes__splitter { min-height: 6px; background: linear-gradient(0deg, #ccc, #111); }
Here is the list of all the props.
horizontal
Default: falseThe orientation of the panes splitting.
Vertical by default, meaning the splitters are vertical, but you can resize horizontally
push-other-panes
Default: trueWhether it should push the next splitter when dragging a splitter until it reached another one.
dbl-click-splitter
Default: trueDouble click on splitter to maximize the next pane.
rtl
Default: falseSupports Right to left direction.
first-splitter
Default: falseDisplays the first splitter when set to true. This allows maximizing the first pane on splitter double click.
check
checkThe `resize` event - previously firing after resize end - is now firing on resize.
A new `resized` event is emitted on resize end. Check out the
Listening to emitted events example.
checkBy default and for performance, the reactivity is now limited to slot deletion and slot creation.
With the option `watchSlots` you can also track any change on the slots.