import SplitterPane from './components/splitter-pane';
import { reactive } from 'vue';
export interface Pane {
getPaneSize: () => number;
}
export interface PaneState {
index: number;
initialSize: number;
minSize: number;
maxSize: number;
}
export interface DragState {
prev: PaneState;
next: PaneState;
}
export type SplitterPane = typeof SplitterPane & Pane;
export interface splitterState {
panes: Array<SplitterPane>;
paneCount: number;
splitterContainerSize: number;
}
export class SplitterStore {
state: splitterState;
constructor() {
this.state = reactive({
panes: [],
splitterContainerSize: 0,
paneCount: 0,
});
}
setPanes({ panes }: { panes: SplitterPane[]}): void {
this.state.panes = panes.map((pane: SplitterPane, index: number) => {
if (pane.component) {
pane.component.exposed.order.value = index * 2;
}
pane.getPaneSize = pane?.component?.exposed.getPaneSize;
return pane;
});
this.state.paneCount = panes.length;
}
setSplitter({ containerSize }: { containerSize: number }): void {
this.state.splitterContainerSize = containerSize;
}
getPane(index: number): SplitterPane {
if (!this.state.panes || index < 0 || index >= this.state.panes.length) {
throw new Error('no pane can return.');
}
return this.state.panes[index];
}
dragState(splitbarIndex: number): DragState {
const prev = this.getPane(splitbarIndex);
const next = this.getPane(splitbarIndex + 1);
const total = prev.getPaneSize() + next.getPaneSize();
return {
prev: {
index: splitbarIndex,
initialSize: prev.getPaneSize(),
minSize:
this.toPixels(prev.component.props.minSize) ||
total - this.toPixels(next.component.props.maxSize) ||
0,
maxSize:
this.toPixels(prev.component.props.maxSize) ||
total - this.toPixels(next.component.props.minSize) ||
total,
},
next: {
index: splitbarIndex + 1,
initialSize: next.getPaneSize(),
minSize:
this.toPixels(next.component.props.minSize) ||
total - this.toPixels(prev.component.props.maxSize) ||
0,
maxSize:
this.toPixels(next.component.props.maxSize) ||
total - this.toPixels(prev.component.props.minSize) ||
total,
},
};
}
clamp(minSize: number, maxSize: number, initialSize: number): number {
return Math.min(maxSize, Math.max(minSize, initialSize));
}
resize(paneState: PaneState, moveSize: number): void {
const pane = this.getPane(paneState.index);
const splitterSize = this.state.splitterContainerSize;
const newSize = this.clamp(
paneState.minSize,
paneState.maxSize,
paneState.initialSize + moveSize
);
let size = '';
if (this.isPercent(pane.component.props.size)) {
size = (newSize / splitterSize) * 100 + '%';
} else {
size = newSize + 'px';
}
pane.component.props.size = size;
pane.component.emit('sizeChange', size);
}
isResizable(splitBarIndex: number): boolean {
const prevPane = this.getPane(splitBarIndex);
const nextPane = this.getPane(splitBarIndex + 1);
const paneCollapsed =
prevPane?.component?.props?.collapsed ||
nextPane?.component?.props?.collapsed;
return (
prevPane?.component?.props?.resizable &&
nextPane?.component?.props?.resizable &&
!paneCollapsed
);
}
isStaticBar(splitBarIndex: number): boolean {
const prevPane = this.getPane(splitBarIndex);
const nextPane = this.getPane(splitBarIndex + 1);
return !(
prevPane?.component?.props?.resizable &&
nextPane?.component?.props?.resizable
);
}
isPercent(size: string): boolean {
return /%$/.test(size);
}
toPixels(size: string): number {
let result = parseFloat(size);
if (this.isPercent(size)) {
result = (this.state.splitterContainerSize * result) / 100;
}
return result;
}
tooglePane(
paneIndex: number,
nearPaneIndex: number,
lockStatus?: boolean
): void {
const pane = this.getPane(paneIndex);
const nearPane = this.getPane(nearPaneIndex);
if (pane?.component?.props?.collapsible) {
pane.component.props.collapsed = lockStatus
? pane?.component?.props?.collapsed
: !pane?.component?.props?.collapsed;
nearPane?.component?.exposed?.toggleNearPaneFlexGrow(
pane?.component?.props?.collapsed
);
pane?.component?.emit(
'collapsedChange',
pane?.component?.props?.collapsed
);
}
}
setSize(state: DragState, distance: number): void {
const prev = this.getPane(state.prev.index);
const next = this.getPane(state.next.index);
if (prev.component.props.size && next.component.props.size) {
this.resize(state.prev, distance);
this.resize(state.next, -distance);
} else if (next.component.props.size) {
this.resize(state.next, -distance);
} else {
this.resize(state.prev, distance);
}
}
}