You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2026/06/25 01:32:57 UTC

(skywalking-horizon-ui) branch fix/dashboard-editor-flow created (now 1526267)

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

wu-sheng pushed a change to branch fix/dashboard-editor-flow
in repository https://gitbox.apache.org/repos/asf/skywalking-horizon-ui.git


      at 1526267  fix(layer-dashboards): flow the widget editor into view so it opens complete

This branch includes the following new commits:

     new 1526267  fix(layer-dashboards): flow the widget editor into view so it opens complete

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(skywalking-horizon-ui) 01/01: fix(layer-dashboards): flow the widget editor into view so it opens complete

Posted by wu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

wu-sheng pushed a commit to branch fix/dashboard-editor-flow
in repository https://gitbox.apache.org/repos/asf/skywalking-horizon-ui.git

commit 1526267a656f23168d8b4f9f0d68076f5951987b
Author: Wu Sheng <wu...@foxmail.com>
AuthorDate: Thu Jun 25 09:30:41 2026 +0800

    fix(layer-dashboards): flow the widget editor into view so it opens complete
    
    The widget canvas sits far below the scope config, so clicking a widget without
    first scrolling left the sticky editor drawer at its natural position — entirely
    below the fold, clamped to its min height with the form overflowing. On select
    the editor now scrolls fully into view (and re-fits), but only when it isn't
    already usable, so clicking through widgets with the editor pinned at the top
    doesn't yank the page around.
---
 CHANGELOG.md                                       |  2 +-
 .../admin/layer-templates/LayerDashboardsAdmin.vue | 34 +++++++++++++++++++---
 2 files changed, 31 insertions(+), 5 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index cb9234e..880cd01 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,7 +37,7 @@ The version line is shared by every package in the monorepo (apps + shared packa
 
 ### Layer dashboard editor
 
-- **The widget editor's move / delete controls stay reachable.** On the Layer dashboards admin, the per-widget editor's `Up` / `Down` / `Delete` row is now a pinned footer instead of the last thing in a scrolling panel, and the panel sizes itself to the visible area — so Delete no longer slips below the fold on a long form or a short board. Adding a widget also scrolls the new widget into view, next to the editor that opens for it, instead of leaving it off-screen at the bottom of the canvas.
+- **The widget editor always opens complete and reachable.** On the Layer dashboards admin, the per-widget editor's `Up` / `Down` / `Delete` row is now a pinned footer instead of the last thing in a scrolling panel, and the panel sizes itself to the visible area — so Delete no longer slips below the fold on a long form or a short board. Selecting a widget flows the editor fully into view (the canvas sits well below the scope config, so it used to open off-screen), and adding a widget scr [...]
 
 ## 0.7.0
 
diff --git a/apps/ui/src/features/admin/layer-templates/LayerDashboardsAdmin.vue b/apps/ui/src/features/admin/layer-templates/LayerDashboardsAdmin.vue
index 52288fe..1207419 100644
--- a/apps/ui/src/features/admin/layer-templates/LayerDashboardsAdmin.vue
+++ b/apps/ui/src/features/admin/layer-templates/LayerDashboardsAdmin.vue
@@ -609,11 +609,31 @@ function onWinResizeScope(): void { updateScopeScroll(); }
 // instead, so the footer is always visible. Scroll listens in the capture
 // phase so the inner content pane's scroll (not just window) re-syncs it.
 const drawerEl = ref<HTMLElement | null>(null);
+const editorCardEl = ref<HTMLElement | null>(null);
 function syncDrawerHeight(): void {
   const el = drawerEl.value;
   if (!el) return;
   el.style.height = `${Math.max(220, window.innerHeight - el.getBoundingClientRect().top - 8)}px`;
 }
+/** Flow the editor into view so it shows complete on select. The widget canvas
+ *  sits far below the scope config, so clicking a widget without first
+ *  scrolling leaves the sticky drawer at its natural (off-screen) position,
+ *  clamped to its min height. Only scroll when it isn't already fully usable —
+ *  so clicking through widgets while the editor is pinned at the top doesn't
+ *  yank the page around. */
+function flowEditorIntoView(): void {
+  const el = drawerEl.value;
+  const card = editorCardEl.value;
+  if (!el || !card) return;
+  const body = el.querySelector('.drawer-body');
+  const lowOnScreen = el.getBoundingClientRect().top > 120;
+  const cramped = body ? body.scrollHeight > body.clientHeight + 1 : false;
+  if (lowOnScreen || cramped) {
+    card.scrollIntoView({ behavior: 'smooth', block: 'start' });
+    // The smooth scroll changes the drawer's top; re-fit once it settles.
+    window.setTimeout(syncDrawerHeight, 400);
+  }
+}
 onMounted(() => {
   window.addEventListener('resize', onWinResizeScope);
   window.addEventListener('scroll', syncDrawerHeight, { capture: true, passive: true });
@@ -708,9 +728,15 @@ function moveWidget(i: number, dir: -1 | 1): void {
  * ------------------------------------------------------------------- */
 
 const selectedIdx = ref<number | null>(null);
-/* Re-fit the drawer to the viewport when it opens / the target widget changes
- * (content height differs); the scroll + resize listeners keep it fitted after. */
-watch(selectedIdx, () => void nextTick(syncDrawerHeight));
+/* When a widget is selected: re-fit the drawer, then (if it isn't already fully
+ * usable) flow the editor into view so it shows complete. The scroll + resize
+ * listeners keep it fitted afterwards. */
+watch(selectedIdx, (idx) => {
+  void nextTick(() => {
+    syncDrawerHeight();
+    if (idx !== null) void nextTick(flowEditorIntoView);
+  });
+});
 
 /** When the user switches scope or layer we drop the selection so the
  *  drawer doesn't refer to a widget that no longer exists. */
@@ -3675,7 +3701,7 @@ const namingTest = computed<NamingTestResult>(() => {
           </div>
         </section>
 
-        <section v-else class="sw-card editor-card">
+        <section v-else ref="editorCardEl" class="sw-card editor-card">
           <div class="card-head">
             <h4>{{ scopeLabel(activeScope) }} widgets</h4>
             <span class="sub">