SplitLayout usage
Basic examples
It is recommended to place SplitLayout
in the top-level component or CodeLayout
's centerArea
slot and set the width and height to fill the screen.
WARNING
This component is designed to fill the parent container. Please set position: relative
style for the parent container and set a certain height, otherwise the component will not be able to calculate the height correctly and display properly.
To use SplitLayout, there are the following steps:
- Import Components.
- Defining Slots and Content: In components, content is organized on a "panel" basis, so panel data needs to be added and then rendered in the slots.
For example:
Panel operation
Definition:
- Grid:A grid is a container that can contain multiple panels, composed in Tab mode, but can only have one displayed panel at the same time; Alternatively, it can be a parent grid that contains sub grids divided in different directions. It has two layout directions, horizontal and vertical.
- Panel:Panel, the content is organized in units of "panels", and the panel is the basic unit that ultimately allows you to render the content. Users can drag and close each panel.
You can add your panels to the components, or obtain panel instances for corresponding operations.
To operate the SplitLayout component, you need to first obtain its instance, and then call the method on the instance:
import { CodeLayoutSplitNInstance } from 'vue-code-layout';
//Bind the splitLayoutRef variable to the SplitLayout component through the ref attribute
const splitLayoutRef = ref<CodeLayoutSplitNInstance>();
Get Root Grid
The component provides a method to obtain the root grid:
const grid = splitLayoutRef.value.getRootGrid();
Root Grid Direction
The default layout direction for the root grid is horizontal (layout direction is only used for sub grids, sub panels are displayed in Tab mode), You can also change it to vertical.
const grid = splitLayoutRef.value.getRootGrid();
grid.direction = 'vertical';
Add Grid/Panel
You can add a grid to the root, for example, the following code adds a grid to the root:
const grid1 = grid.addGrid({
name: 'grid1',
visible: true,
size: 0,
});
The default direction of the grid is perpendicular to the parent, for example, if the parent grid is horizontal, the added sub grid direction is vertical.
After obtaining the grid, you can add panels to the custom grid:
grid1.addPanel({
title: `Panel title`,
tooltip: `Panel Help`,
name: `datahelp`,
iconSmall: () => h(IconMarkdown),
});
Note: The 'name' attribute of the panel must be unique, but it is not required for the grid.
Get panel
When adding a panel, the 'name' attribute must be unique, so you can use name to query the added panel instance:
//Add panels to obtain panels and modify badge
const file1 = splitLayoutRef.value.getPanelByName('file1')
file1.badge = '3';
Close panel
User can close panel.
Close button
The panel supports the close button. When creating the panel, you can specify the close button or modify the properties after obtaining the instance:
const grid1 = grid.addGrid({
name: 'grid1',
visible: true,
size: 0,
closeType: 'close', //Display close button
});
//Obtain panels and modify closeType
const file1 = splitLayoutRef.value.getPanelByName('file1')
file1.closeType = 'unSave';
- unSave Display a dot to indicate that the current file has not been saved
- close Display a close button (x)
- none No close button (default)
Close processing
After setting the close button, when the user clicks the close button, the panelClose
event will be triggered at the top level, where you can handle your own business logic, Save the file, etc. You can perform the closing operation asynchronously, and then call resolve to delete the panel, or call reject to cancel the deletion of the panel.
//Close callback
function onPanelClose(panel: CodeLayoutPanelInternal, resolve: () => void, reject: (e: any) => void) {
console.log('onPanelClose', panel.name);
resolve();
}
Manual close
In the code, the closePanel
`function on the panel instance can also be manually called to perform a close operation, which is consistent with the user's click to close.
const file1 = splitLayoutRef.value.getPanelByName('file1')
file1.closePanel();
Prohibit automatic grid shrinkage
Users are allowed to drag and drop panels between grids. By default, when all panels in a grid are removed, the panel will automatically shrink (removing itself) to make space for other panels.
If you do not want the grids you manually added to be removed, you can specify that shrinking is not allowed when creating the panels, so that when the grid is empty, it will not be removed, and the tabEmptyContentRender slot will be called during rendering, where you can render custom content.
const grid1 = grid.addGrid({
name: 'grid1',
visible: true,
size: 0,
noAutoShink: true,
});
Get user focused grid/panel
Sometimes you need to retrieve the currently activated grid/panel, for example, to save a file that the user is currently editing, you can use getActiveGird
to retrieve the currently activated grid.
const grid = splitLayoutRef.value?.getActiveGird();
After obtaining the grid, you can access the activePanel
on the instance to retrieve the currently activated panel. If you bind your own editor context on the panel, you can call it.
const panel = grid?.activePanel;
if (panel != null) {
panel.data.save(); //Call the save method in the context of my editor
}
TIP
You can listen to the gridActive
event of SplitLayout to obtain the user's latest activated grid.
Badge, Icon, Title, Actions
A panel supports the following configuration fields to control the display of some information, and its display position is shown in the figure:
You can specify these properties during creation or modify them through instance properties after creation.
grid2.addPanel({
name: 'file1',
//Tile text
title: 'File name',
//Tooltips displayed when hovering the mouse
tooltip: 'File path',
//badge, recommended within 2 digits
badge: '2',
//Icon
iconSmall: () => h(IconFile),
//Custom actions, which will be displayed as buttons on the right side of the header when the panel is activated
actions: [
{
name: 'test',
icon: () => h(IconSearch),
onClick() {},
},
{
name: 'test2',
icon: () => h(IconFile),
onClick() {},
},
]
});
Panel menu
You can define contextmenu of a Panel, use panelContextMenu
event callback to display your menu.
<template>
<SplitLayout
ref="splitLayoutRef"
@panelContextMenu="onPanelMenu"
>
<!--Other codes...-->
</SplitLayout>
</template>
<script setup lang="ts">
//...Other codes
function onPanelMenu(panel: CodeLayoutPanelInternal, e: MouseEvent) {
e.stopPropagation();
e.preventDefault();
//Here i use vue3-context-menu library to show menu, you can use other menu library
ContextMenuGlobal.showContextMenu({
x: e.x,
y: e.y,
items: [
{
label: "Menu of " + panel.name,
onClick: () => {
alert("You click a menu item");
}
},
],
});
}
</script>
Customize render Tab header
Tab header
Split Layout supports custom rendering tab buttons, allowing you to customize rendering of a certain part or all parts. You can use the tabItemRender
slot to render tab entries, which allows for the entire overlay rendering.
If you only need to customize certain parts of the rendering, you can import the default tab component SplitTabItem
, which supports the functionality of default tabs but also allows you to customize a certain part of them.
<template>
<SplitLayout
ref="splitLayoutRef"
>
<!--Other codes...-->
<template #tabItemRender="{ index, panel, active, onTabClick, onContextMenu }">
<SplitTabItem
:panel="(panel as CodeLayoutSplitNPanelInternal)"
:active="active"
@click="onTabClick"
@contextmenu="onContextMenu($event)"
>
<template #title>
<span :style="{ color: colors[panel.data] }">{{ panel.title }}</span>
</template>
<template #icon>
<MyIcon />
</template>
<template #badge>
<span class="badge">99+</span>
</template>
<template #close>
<MyCloseIcon />
</template>
</SplitTabItem>
</template>
</SplitLayout>
</template>
<script setup lang="ts">
import { SplitLayout, SplitTabItem } from 'vue-code-layout';
//...Other codes...
</script>
Tab tail
You can use the tabHeaderExtraRender
slot to render the tail area of a tab, for example, the example below adds a 'Add Panel' button at the end of the tab.
<template>
<SplitLayout
ref="splitLayoutRef"
>
<!--Other codes...-->
<template #tabHeaderExtraRender="{ grid }">
<button @click="onAddPanel(grid)">+ 添加面板</button>
</template>
</SplitLayout>
</template>
Saving and Loading Data
SplitLayout supports you to save user dragged layouts to JSON data, and then reload and restore the original layout from JSON data on the next entry.
SplitLayout supports two events, canLoadLayout
and canSaveLayout
. In the event callback, the current component instance will be returned. You can perform load and save operations in the event callback, or at other times, you can freely control the load and save operations by calling the loadLayout
and saveLayout
functions on the component instance.
<template>
<SplitLayout
ref="splitLayout"
style="height: 400px"
@canLoadLayout="loadLayout"
@canSaveLayout="saveLayout"
/>
</template>
<script lang="ts" setup>
const splitLayout = ref<CodeLayoutSplitNInstance>();
//Load and save operations can be performed in event callbacks, which are triggered by default during component initialization and uninstallation
//The event will pass the component instance ref, which can be directly called, equivalent to splitLayout.value
function loadLayout(ref: CodeLayoutSplitNInstance) {
//Load here
}
function saveLayout(ref: CodeLayoutSplitNInstance) {
//Save here
}
//You can also load/save data by calling component instance methods at other custom times
onMounted(() => {
splitLayout.value.loadLayout();
})
</script>
Save data
Save layout data by calling the saveLayout
method.
const json = splitLayout.value.saveLayout();
localStorage.setItem('SplitLayoutData', json);
Load data
Layout data only stores the basic position, size, and other information of each layout, and does not contain information that cannot be serialized (such as callback functions and icons). So you also need to fill in these data based on the panel name in the callback of loadLayout to instantiated the panel.
const data = localStorage.getItem('SplitLayoutData');
if (data) {
//If load layout from data, need fill panel data
splitLayout.value.loadLayout(JSON.parse(data), (panel) => {
switch (panel.name) {
case 'file1':
panel.title = 'File 1';
panel.tooltip = 'File 1 path';
panel.badge = '2';
panel.iconLarge = () => h(IconFile);
break;
}
return panel;
});
} else {
//No data, create new layout
//...
}
TIP: Component unmont
Tip: Vue may unmont and recreate your components in the following two situations:
- Users drag and drop a panel to another panel
- In development mode, when you modify the code, HMR overloads
At this point, Vue may unmont and recreate your component, causing the component state to be lost. Therefore, you need to handle your own component and save the relevant state.