This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-booster-ui.git


The following commit(s) were added to refs/heads/main by this push:
     new 8746d3c9 feat: add Support for dragging in the trace panel (#377)
8746d3c9 is described below

commit 8746d3c98530ca85a8bb152936c16a33f0961e96
Author: Starry <codeprince2...@163.com>
AuthorDate: Sat Apr 6 19:17:17 2024 +0800

    feat: add Support for dragging in the trace panel (#377)
---
 .../Table/table.scss => utils/mutation.ts}         |  84 +++++----------
 src/views/dashboard/controls/Trace.vue             | 116 ++++++++++++++++++++-
 src/views/dashboard/related/trace/Detail.vue       |   2 +-
 src/views/dashboard/related/trace/Filter.vue       |   3 +-
 src/views/dashboard/related/trace/TraceList.vue    |   8 +-
 .../related/trace/components/D3Graph/Index.vue     |  19 ++--
 .../trace/components/Table/TableContainer.vue      |   1 +
 .../related/trace/components/Table/table.scss      |  14 +--
 8 files changed, 166 insertions(+), 81 deletions(-)

diff --git a/src/views/dashboard/related/trace/components/Table/table.scss 
b/src/utils/mutation.ts
similarity index 50%
copy from src/views/dashboard/related/trace/components/Table/table.scss
copy to src/utils/mutation.ts
index e48b8e29..219d9337 100644
--- a/src/views/dashboard/related/trace/components/Table/table.scss
+++ b/src/utils/mutation.ts
@@ -14,63 +14,31 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-.argument {
-  width: 150px;
-}
-
-.start-time {
-  width: 150px;
-}
-
-.exec-ms {
-  width: 80px;
-}
-
-.exec-percent {
-  width: 100px;
-}
-
-.self {
-  width: 100px;
-}
-
-.api {
-  width: 120px;
-}
-
-.agent {
-  width: 150px;
-}
-
-.application {
-  width: 150px;
-  text-align: center;
-}
-
-.max-time {
-  width: 150px;
-}
-
-.method {
-  width: 300px;
-}
-
-.avg-time {
-  width: 150px;
-}
-
-.min-time {
-  width: 150px;
-}
-
-.count {
-  width: 120px;
-}
-
-.sum-time {
-  width: 150px;
-}
 
-.type {
-  width: 60px;
+export class mutationObserver {
+  private static mutationObserverMap: Map<string, MutationObserver> = new 
Map<string, MutationObserver>();
+
+  static create(key: string, callback: MutationCallback) {
+    const observer = new MutationObserver(callback);
+    mutationObserver.mutationObserverMap.set(key, observer);
+  }
+
+  static observe(key: string, target: Node, options?: MutationObserverInit) {
+    const observer = mutationObserver.mutationObserverMap.get(key);
+    if (observer) {
+      observer.observe(target, options);
+    }
+  }
+
+  static deleteObserve(key: string): void {
+    this.disconnect(key);
+    this.mutationObserverMap.delete(key);
+  }
+
+  static disconnect(key: string): void {
+    const observer = this.mutationObserverMap.get(key);
+    if (observer) {
+      observer.disconnect();
+    }
+  }
 }
diff --git a/src/views/dashboard/controls/Trace.vue 
b/src/views/dashboard/controls/Trace.vue
index 4daa614b..4db7e77a 100644
--- a/src/views/dashboard/controls/Trace.vue
+++ b/src/views/dashboard/controls/Trace.vue
@@ -25,22 +25,34 @@ limitations under the License. -->
       </div>
     </el-popover>
     <div class="header">
-      <Filter :needQuery="needQuery" :data="data" @get="getService" />
+      <Filter :needQuery="needQuery" :data="data" @get="getService" 
@search="popSegmentList" />
     </div>
     <div class="trace flex-h">
-      <TraceList />
+      <TraceList class="trace-list" :style="`width: ${currentWidth}px;`" />
+      <div
+        @mouseover="showIcon = true"
+        @mouseout="showIcon = false"
+        @mousedown="mousedown($event)"
+        @mouseup="mouseup($event)"
+      >
+        <div class="trace-line" />
+        <span class="trace-icon" v-show="showIcon" @mousedown="triggerArrow" 
@mouseup="stopObserve($event)">
+          <Icon class="trace-arrow" :icon-name="isLeft ? 'chevron-left' : 
'chevron-right'" size="lg" />
+        </span>
+      </div>
       <TraceDetail :serviceId="serviceId" />
     </div>
   </div>
 </template>
 <script lang="ts" setup>
-  import { provide, ref } from "vue";
+  import { provide, ref, onMounted, onUnmounted } from "vue";
   import type { PropType } from "vue";
   import Filter from "../related/trace/Filter.vue";
   import TraceList from "../related/trace/TraceList.vue";
   import TraceDetail from "../related/trace/Detail.vue";
   import { useI18n } from "vue-i18n";
   import { useDashboardStore } from "@/store/modules/dashboard";
+  import { mutationObserver } from "@/utils/mutation";
 
   /* global defineProps */
   const props = defineProps({
@@ -52,15 +64,76 @@ limitations under the License. -->
     needQuery: { type: Boolean, default: true },
   });
   provide("options", props.data);
+  const defaultWidth = 280,
+    minArrowLeftWidth = 120;
   const serviceId = ref<string>("");
   const { t } = useI18n();
   const dashboardStore = useDashboardStore();
+  const isLeft = ref<boolean>(true);
+  const showIcon = ref<boolean>(false);
+  const currentWidth = ref<number>(defaultWidth);
+  const isDrag = ref<boolean>(false);
+
   function removeWidget() {
     dashboardStore.removeControls(props.data);
   }
   function getService(id: string) {
     serviceId.value = id;
   }
+  // When click the arrow, the width of the segment list is determined by the 
direction it points to.
+  function triggerArrow() {
+    currentWidth.value = isLeft.value ? 0 : defaultWidth;
+    isLeft.value = !isLeft.value;
+    startObserve();
+  }
+  function popSegmentList() {
+    if (currentWidth.value >= defaultWidth) {
+      return;
+    }
+    currentWidth.value = defaultWidth;
+    isLeft.value = true;
+  }
+  function startObserve() {
+    mutationObserver.observe("trigger-resize", 
document.querySelector(".trace-list")!, {
+      attributes: true,
+      attributeFilter: ["style"],
+    });
+  }
+  function stopObserve(event: MouseEvent) {
+    mutationObserver.disconnect("trigger-resize");
+    event.stopPropagation();
+  }
+  const mousemove = (event: MouseEvent) => {
+    if (!isDrag.value) {
+      return;
+    }
+    const diffX = event.clientX;
+    let leftWidth = 
document.querySelector(".trace-list")!.getBoundingClientRect();
+    currentWidth.value = diffX - leftWidth.left;
+    isLeft.value = currentWidth.value >= minArrowLeftWidth;
+  };
+  const mouseup = (event: MouseEvent) => {
+    showIcon.value = false;
+    isDrag.value = false;
+    stopObserve(event);
+  };
+  const mousedown = (event: MouseEvent) => {
+    if ((event.target as HTMLDivElement)?.className === "trace-line") {
+      isDrag.value = true;
+      startObserve();
+      event.stopPropagation();
+    }
+  };
+  onMounted(() => {
+    document.addEventListener("mousedown", mousedown);
+    document.addEventListener("mousemove", mousemove);
+    document.addEventListener("mouseup", mouseup);
+  });
+  onUnmounted(() => {
+    document.removeEventListener("mousedown", mousedown);
+    document.removeEventListener("mousemove", mousemove);
+    document.removeEventListener("mouseup", mouseup);
+  });
 </script>
 <style lang="scss" scoped>
   .trace-wrapper {
@@ -68,7 +141,6 @@ limitations under the License. -->
     height: 100%;
     font-size: $font-size-smaller;
     position: relative;
-    overflow: auto;
   }
 
   .delete {
@@ -103,4 +175,40 @@ limitations under the License. -->
     overflow: auto;
     min-width: 1000px;
   }
+
+  .trace-list {
+    max-width: 480px;
+  }
+
+  .trace-line {
+    position: relative;
+    width: 2px;
+    height: 900px;
+    background-color: #e8e8e8;
+    cursor: ew-resize;
+
+    &:hover {
+      color: $active-color;
+      background-color: $active-background;
+    }
+  }
+
+  .trace-icon {
+    position: absolute;
+    cursor: pointer;
+    top: calc(50% - 15px);
+    text-align: center;
+    width: 24px;
+    height: 24px;
+    transform: translateX(-11px);
+    line-height: 24px;
+    border-radius: 50%;
+    background-color: $layout-background;
+    box-shadow: 0 3px 5px rgb(45 60 80 / 20%);
+  }
+
+  .trace-arrow {
+    padding-bottom: 1px;
+    color: $active-color;
+  }
 </style>
diff --git a/src/views/dashboard/related/trace/Detail.vue 
b/src/views/dashboard/related/trace/Detail.vue
index c5c1ea2a..f115f602 100644
--- a/src/views/dashboard/related/trace/Detail.vue
+++ b/src/views/dashboard/related/trace/Detail.vue
@@ -166,8 +166,8 @@ limitations under the License. -->
 </script>
 <style lang="scss" scoped>
   .trace-detail {
+    flex: 1;
     height: 100%;
-    width: 100%;
     overflow: hidden;
   }
 
diff --git a/src/views/dashboard/related/trace/Filter.vue 
b/src/views/dashboard/related/trace/Filter.vue
index 0ca285a1..a5092214 100644
--- a/src/views/dashboard/related/trace/Filter.vue
+++ b/src/views/dashboard/related/trace/Filter.vue
@@ -91,7 +91,7 @@ limitations under the License. -->
   import type { LayoutConfig } from "@/types/dashboard";
 
   /*global defineProps, defineEmits, Recordable */
-  const emits = defineEmits(["get"]);
+  const emits = defineEmits(["get", "search"]);
   const props = defineProps({
     needQuery: { type: Boolean, default: true },
     data: {
@@ -227,6 +227,7 @@ limitations under the License. -->
     return param;
   }
   function searchTraces() {
+    emits("search");
     traceStore.setTraceCondition(setCondition());
     queryTraces();
   }
diff --git a/src/views/dashboard/related/trace/TraceList.vue 
b/src/views/dashboard/related/trace/TraceList.vue
index ba2991a8..6013cbe4 100644
--- a/src/views/dashboard/related/trace/TraceList.vue
+++ b/src/views/dashboard/related/trace/TraceList.vue
@@ -13,7 +13,7 @@ limitations under the License. -->
 <template>
   <div class="trace-t flex-v">
     <div class="trace-t-tool flex-h">
-      <div class="title">
+      <div class="title ell">
         <span class="mr-5">Trace Segments</span>
         <el-popover
           :width="310"
@@ -144,6 +144,7 @@ limitations under the License. -->
 </script>
 <style lang="scss" scoped>
   .trace-t-tool {
+    overflow: hidden;
     background-color: rgb(196 200 225 / 20%);
     justify-content: space-between;
     border-bottom: 1px solid #c1c5ca41;
@@ -163,7 +164,7 @@ limitations under the License. -->
   }
 
   .trace-t-wrapper {
-    overflow: auto;
+    overflow: hidden;
     border-right: 1px solid var(--sw-trace-list-border);
   }
 
@@ -187,7 +188,8 @@ limitations under the License. -->
   }
 
   .list {
-    width: 280px;
+    min-width: 120px;
+    width: 100%;
   }
 
   .trace-tr {
diff --git a/src/views/dashboard/related/trace/components/D3Graph/Index.vue 
b/src/views/dashboard/related/trace/components/D3Graph/Index.vue
index ff4705e7..6a107a0b 100644
--- a/src/views/dashboard/related/trace/components/D3Graph/Index.vue
+++ b/src/views/dashboard/related/trace/components/D3Graph/Index.vue
@@ -30,8 +30,9 @@ limitations under the License. -->
   import SpanDetail from "./SpanDetail.vue";
   import { useAppStoreWithOut } from "@/store/modules/app";
   import { debounce } from "@/utils/debounce";
+  import { mutationObserver } from "@/utils/mutation";
 
-  /* global defineProps, Nullable, defineExpose,Recordable*/
+  /* global defineProps, Nullable, defineExpose, Recordable */
   const props = defineProps({
     data: { type: Array as PropType<Span[]>, default: () => [] },
     traceId: { type: String, default: "" },
@@ -60,6 +61,13 @@ limitations under the License. -->
     }
     draw();
     loading.value = false;
+
+    // monitor segment list width changes.
+    mutationObserver.create("trigger-resize", () => {
+      d3.selectAll(".d3-tip").remove();
+      debounceFunc();
+    });
+
     window.addEventListener("resize", debounceFunc);
   });
 
@@ -286,6 +294,7 @@ limitations under the License. -->
   onBeforeUnmount(() => {
     d3.selectAll(".d3-tip").remove();
     window.removeEventListener("resize", debounceFunc);
+    mutationObserver.deleteObserve("trigger-resize");
   });
   watch(
     () => props.data,
@@ -295,12 +304,8 @@ limitations under the License. -->
       }
       loading.value = true;
       changeTree();
-      tree.value.init({ label: "TRACE_ROOT", children: segmentId.value }, 
props.data, fixSpansSize.value);
-      tree.value.draw(() => {
-        setTimeout(() => {
-          loading.value = false;
-        }, 200);
-      });
+      draw();
+      loading.value = false;
     },
   );
   watch(
diff --git 
a/src/views/dashboard/related/trace/components/Table/TableContainer.vue 
b/src/views/dashboard/related/trace/components/Table/TableContainer.vue
index 125eee1f..e5ac2380 100644
--- a/src/views/dashboard/related/trace/components/Table/TableContainer.vue
+++ b/src/views/dashboard/related/trace/components/Table/TableContainer.vue
@@ -88,6 +88,7 @@ limitations under the License. -->
       return;
     }
     drag.onmousedown = (event: MouseEvent) => {
+      event.stopPropagation();
       const diffX = event.clientX;
       const copy = method.value;
       document.onmousemove = (documentEvent) => {
diff --git a/src/views/dashboard/related/trace/components/Table/table.scss 
b/src/views/dashboard/related/trace/components/Table/table.scss
index e48b8e29..7de1ae12 100644
--- a/src/views/dashboard/related/trace/components/Table/table.scss
+++ b/src/views/dashboard/related/trace/components/Table/table.scss
@@ -48,29 +48,29 @@
 }
 
 .max-time {
-  width: 150px;
+  width: 13%;
 }
 
 .method {
-  width: 300px;
+  width: 28%;
 }
 
 .avg-time {
-  width: 150px;
+  width: 13%;
 }
 
 .min-time {
-  width: 150px;
+  width: 13%;
 }
 
 .count {
-  width: 120px;
+  width: 12%;
 }
 
 .sum-time {
-  width: 150px;
+  width: 13%;
 }
 
 .type {
-  width: 60px;
+  width: 8%;
 }

Reply via email to