index.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import "./index.css";
  2. import resizer from "./resizer";
  3. import { type PropType, defineComponent, ref, unref, computed } from "vue";
  4. export interface ContextProps {
  5. minPercent: number;
  6. defaultPercent: number;
  7. split: string;
  8. }
  9. /** 切割面板组件 */
  10. export default defineComponent({
  11. name: "SplitPane",
  12. components: { resizer },
  13. props: {
  14. splitSet: {
  15. type: Object as PropType<ContextProps>,
  16. require: true
  17. }
  18. },
  19. emits: ["resize"],
  20. setup(props, ctx) {
  21. const active = ref(false);
  22. const hasMoved = ref(false);
  23. const percent = ref(props.splitSet?.defaultPercent);
  24. const type = props.splitSet?.split === "vertical" ? "width" : "height";
  25. const resizeType = props.splitSet?.split === "vertical" ? "left" : "top";
  26. const leftClass = ref([
  27. "splitter-pane splitter-paneL",
  28. props.splitSet?.split
  29. ]);
  30. const rightClass = ref([
  31. "splitter-pane splitter-paneR",
  32. props.splitSet?.split
  33. ]);
  34. const cursor = computed(() => {
  35. return active.value
  36. ? props.splitSet?.split === "vertical"
  37. ? { cursor: "col-resize" }
  38. : { cursor: "row-resize" }
  39. : { cursor: "default" };
  40. });
  41. const onClick = (): void => {
  42. if (!hasMoved.value) {
  43. percent.value = 50;
  44. ctx.emit("resize", percent.value);
  45. }
  46. };
  47. const onMouseDown = (): void => {
  48. active.value = true;
  49. hasMoved.value = false;
  50. };
  51. const onMouseUp = (): void => {
  52. active.value = false;
  53. };
  54. const onMouseMove = (e: any): void => {
  55. if (e.buttons === 0 || e.which === 0) {
  56. active.value = false;
  57. }
  58. if (active.value) {
  59. let offset = 0;
  60. let target = e.currentTarget;
  61. if (props.splitSet?.split === "vertical") {
  62. while (target) {
  63. offset += target.offsetLeft;
  64. target = target.offsetParent;
  65. }
  66. } else {
  67. while (target) {
  68. offset += target.offsetTop;
  69. target = target.offsetParent;
  70. }
  71. }
  72. const currentPage =
  73. props.splitSet?.split === "vertical" ? e.pageX : e.pageY;
  74. const targetOffset =
  75. props.splitSet?.split === "vertical"
  76. ? e.currentTarget.offsetWidth
  77. : e.currentTarget.offsetHeight;
  78. const percents =
  79. Math.floor(((currentPage - offset) / targetOffset) * 10000) / 100;
  80. if (
  81. percents > props.splitSet?.minPercent &&
  82. percents < 100 - props.splitSet?.minPercent
  83. ) {
  84. percent.value = percents;
  85. }
  86. ctx.emit("resize", percent.value);
  87. hasMoved.value = true;
  88. }
  89. };
  90. return () => (
  91. <>
  92. <div
  93. class="vue-splitter-container clearfix"
  94. style={unref(cursor)}
  95. onMouseup={() => onMouseUp()}
  96. onMousemove={() => onMouseMove(event)}
  97. >
  98. <div
  99. class={unref(leftClass)}
  100. style={{ [unref(type)]: unref(percent) + "%" }}
  101. >
  102. {ctx.slots.paneL()}
  103. </div>
  104. <resizer
  105. style={`${unref([resizeType])}:${unref(percent)}%`}
  106. split={props.splitSet?.split}
  107. onMousedown={() => onMouseDown()}
  108. onClick={() => onClick()}
  109. ></resizer>
  110. <div
  111. class={unref(rightClass)}
  112. style={{ [unref(type)]: 100 - unref(percent) + "%" }}
  113. >
  114. {ctx.slots.paneR()}
  115. </div>
  116. <div v-show={unref(active)} class="vue-splitter-container-mask"></div>
  117. </div>
  118. </>
  119. );
  120. }
  121. });