auth-web/src/components/SplitPane/index.tsx

108 lines
3.3 KiB
TypeScript

import './index.css';
import resizer from './resizer';
import { computed, defineComponent, type PropType, ref, unref } from 'vue';
export interface ContextProps {
minPercent: number;
defaultPercent: number;
split: string;
}
/** 切割面板组件 */
export default defineComponent({
name: 'SplitPane',
components: { resizer },
props: {
splitSet: {
type: Object as PropType<ContextProps>,
require: true,
},
},
emits: ['resize'],
setup(props, ctx) {
const active = ref(false);
const hasMoved = ref(false);
const percent = ref(props.splitSet?.defaultPercent);
const type = props.splitSet?.split === 'vertical' ? 'width' : 'height';
const resizeType = props.splitSet?.split === 'vertical' ? 'left' : 'top';
const leftClass = ref(['splitter-pane splitter-paneL', props.splitSet?.split]);
const rightClass = ref(['splitter-pane splitter-paneR', props.splitSet?.split]);
const cursor = computed(() => {
return active.value ? (props.splitSet?.split === 'vertical' ? { cursor: 'col-resize' } : { cursor: 'row-resize' }) : { cursor: 'default' };
});
const onClick = (): void => {
if (!hasMoved.value) {
percent.value = 50;
ctx.emit('resize', percent.value);
}
};
const onMouseDown = (): void => {
active.value = true;
hasMoved.value = false;
};
const onMouseUp = (): void => {
active.value = false;
};
const onMouseMove = (e: any): void => {
if (e.buttons === 0 || e.which === 0) {
active.value = false;
}
if (active.value) {
let offset = 0;
let target = e.currentTarget;
if (props.splitSet?.split === 'vertical') {
while (target) {
offset += target.offsetLeft;
target = target.offsetParent;
}
} else {
while (target) {
offset += target.offsetTop;
target = target.offsetParent;
}
}
const currentPage = props.splitSet?.split === 'vertical' ? e.pageX : e.pageY;
const targetOffset = props.splitSet?.split === 'vertical' ? e.currentTarget.offsetWidth : e.currentTarget.offsetHeight;
const percents = Math.floor(((currentPage - offset) / targetOffset) * 10000) / 100;
if (percents > props.splitSet?.minPercent && percents < 100 - props.splitSet?.minPercent) {
percent.value = percents;
}
ctx.emit('resize', percent.value);
hasMoved.value = true;
}
};
return () => (
<>
<div class="vue-splitter-container clearfix" style={unref(cursor)} onMouseup={() => onMouseUp()} onMousemove={() => onMouseMove(event)}>
<div class={unref(leftClass)} style={{ [unref(type)]: unref(percent) + '%' }}>
{ctx.slots.paneL()}
</div>
<resizer
style={`${unref([resizeType])}:${unref(percent)}%`}
split={props.splitSet?.split}
onMousedown={() => onMouseDown()}
onClick={() => onClick()}
></resizer>
<div class={unref(rightClass)} style={{ [unref(type)]: 100 - unref(percent) + '%' }}>
{ctx.slots.paneR()}
</div>
<div v-show={unref(active)} class="vue-splitter-container-mask"></div>
</div>
</>
);
},
});