Просмотр исходного кода

feat: 优化侧边栏拖拽逻辑

zouawen 3 месяцев назад
Родитель
Сommit
99710ef9dc

+ 54 - 17
packages/@core/composables/src/use-resizable.ts

@@ -1,4 +1,4 @@
-import { onUnmounted, ref } from 'vue';
+import { onUnmounted } from 'vue';
 
 interface ResizableOptions {
   max?: number;
@@ -9,40 +9,70 @@ interface ResizableOptions {
 export function useResizable(options: ResizableOptions = {}) {
   const { min = 0, max = 999, onChange } = options;
 
-  const isDragging = ref(false);
-
-  let cleanup: (() => void) | null = null;
-
+  let startX = 0;
+  let startWidth = 0;
+  let targetTransition = '';
+  let dragBarTransition = '';
+  let dragBarOffsetLeft = 0;
+  let dragBarLeft = '';
+  let dragBarRight = '';
   let userSelect = '';
   let cursor = '';
+  let cleanup: (() => void) | null = null;
+
+  const startDrag = (
+    e: MouseEvent,
+    currentWidth: number,
+    targetElement: HTMLElement | null,
+    dragBarElement: HTMLElement | null,
+  ) => {
+    cleanup?.();
 
-  const startDrag = (e: MouseEvent, width: number) => {
     e.preventDefault();
     e.stopPropagation();
 
-    isDragging.value = true;
-    const startX = e.clientX;
-    const startWidth = width;
+    if (!dragBarElement || !targetElement) return;
+
+    startX = e.clientX;
+    startWidth = currentWidth;
+
+    targetTransition = targetElement.style.transition;
+    dragBarTransition = dragBarElement.style.transition;
+
+    dragBarOffsetLeft = dragBarElement.offsetLeft;
+    dragBarLeft = dragBarElement.style.left;
+    dragBarRight = dragBarElement.style.right;
 
     userSelect = document.body.style.userSelect;
     cursor = document.body.style.cursor;
 
+    targetElement.style.transition = 'none';
+    dragBarElement.style.transition = 'none';
+
+    dragBarElement.style.left = `${dragBarOffsetLeft}px`;
+    dragBarElement.style.right = 'auto';
+
     document.body.style.userSelect = 'none';
     document.body.style.cursor = 'col-resize';
 
     const onMouseMove = (moveEvent: MouseEvent) => {
-      if (!isDragging.value) return;
-
       const deltaX = moveEvent.clientX - startX;
-      let newWidth = startWidth + deltaX;
+      const newLeft = dragBarOffsetLeft + deltaX;
+      dragBarElement.style.left = `${newLeft}px`;
+    };
 
+    const onMouseUp = (upEvent: MouseEvent) => {
+      const deltaX = upEvent.clientX - startX;
+      let newWidth = startWidth + deltaX;
       newWidth = Math.min(max, Math.max(min, newWidth));
 
+      if (dragBarElement) {
+        dragBarElement.style.left = dragBarLeft;
+        dragBarElement.style.right = dragBarRight;
+      }
+
       onChange?.(newWidth);
-    };
 
-    const onMouseUp = () => {
-      if (!isDragging.value) return;
       cleanup?.();
     };
 
@@ -53,10 +83,18 @@ export function useResizable(options: ResizableOptions = {}) {
       document.removeEventListener('mousemove', onMouseMove);
       document.removeEventListener('mouseup', onMouseUp);
 
+      if (targetElement) {
+        targetElement.style.transition = targetTransition;
+      }
+      if (dragBarElement) {
+        dragBarElement.style.transition = dragBarTransition;
+        dragBarElement.style.left = dragBarLeft;
+        dragBarElement.style.right = dragBarRight;
+      }
+
       document.body.style.userSelect = userSelect;
       document.body.style.cursor = cursor;
 
-      isDragging.value = false;
       cleanup = null;
     };
   };
@@ -66,7 +104,6 @@ export function useResizable(options: ResizableOptions = {}) {
   });
 
   return {
-    isDragging,
     startDrag,
   };
 }

+ 12 - 12
packages/@core/ui-kit/layout-ui/src/components/layout-sidebar.vue

@@ -118,8 +118,8 @@ const extraVisible = defineModel<boolean>('extraVisible');
 const isLocked = useScrollLock(document.body);
 const slots = useSlots();
 
-// @ts-expect-error unused
-const asideRef = shallowRef<HTMLDivElement | null>();
+const asideRef = shallowRef<HTMLElement | null>(null);
+const dragBarRef = shallowRef<HTMLElement | null>(null);
 
 const hiddenSideStyle = computed((): CSSProperties => calcMenuWidthStyle(true));
 
@@ -256,7 +256,7 @@ function handleMouseleave() {
   extraVisible.value = false;
 }
 
-const { isDragging, startDrag } = useResizable({
+const { startDrag } = useResizable({
   min: 160,
   max: 320,
   onChange: (newWidth) => {
@@ -265,8 +265,9 @@ const { isDragging, startDrag } = useResizable({
 });
 
 const handleDragSidebar = (e: MouseEvent) => {
-  const { width } = props;
-  startDrag(e, width);
+  const { isSidebarMixed, extraWidth, width } = props;
+  const currentWidth = isSidebarMixed ? extraWidth : width;
+  startDrag(e, currentWidth, asideRef.value, dragBarRef.value);
 };
 </script>
 
@@ -278,9 +279,9 @@ const handleDragSidebar = (e: MouseEvent) => {
     class="h-full transition-all duration-150"
   ></div>
   <aside
+    ref="asideRef"
     :style="style"
     class="fixed left-0 top-0 h-full transition-all duration-150"
-    :class="{ 'transition-none': isDragging }"
     @mouseenter="handleMouseenter"
     @mouseleave="handleMouseleave"
   >
@@ -311,19 +312,13 @@ const handleDragSidebar = (e: MouseEvent) => {
         v-if="showCollapseButton && !isSidebarMixed"
         v-model:collapsed="collapse"
       />
-      <div
-        class="absolute inset-y-0 -right-0.5 z-1000 w-1 cursor-col-resize"
-        @mousedown="handleDragSidebar"
-      ></div>
     </div>
     <div
       v-if="isSidebarMixed"
-      ref="asideRef"
       :class="[
         themeSub,
         {
           'border-l': extraVisible,
-          'transition-none': isDragging,
         },
       ]"
       :style="extraStyle"
@@ -350,5 +345,10 @@ const handleDragSidebar = (e: MouseEvent) => {
         <slot name="extra"></slot>
       </VbenScrollbar>
     </div>
+    <div
+      ref="dragBarRef"
+      class="absolute inset-y-0 -right-[1px] z-1000 w-[2px] cursor-col-resize hover:bg-primary"
+      @mousedown="handleDragSidebar"
+    ></div>
   </aside>
 </template>

+ 3 - 9
packages/effects/layouts/src/basic/layout.vue

@@ -211,14 +211,6 @@ const slots: SetupContext['slots'] = useSlots();
 const headerSlots = computed(() => {
   return Object.keys(slots).filter((key) => key.startsWith('header-'));
 });
-
-function handleUpdateSidebarWidth(newWidth: number) {
-  updatePreferences({
-    sidebar: {
-      width: newWidth,
-    },
-  });
-}
 </script>
 
 <template>
@@ -275,7 +267,9 @@ function handleUpdateSidebarWidth(newWidth: number) {
       (value: boolean) =>
         updatePreferences({ sidebar: { extraCollapse: value } })
     "
-    @update:sidebar-width="handleUpdateSidebarWidth"
+    @update:sidebar-width="
+      (value: number) => updatePreferences({ sidebar: { width: value } })
+    "
   >
     <!-- logo -->
     <template #logo>