Quellcode durchsuchen

fix: smooth sticky header

Tienson Qin vor 3 Monaten
Ursprung
Commit
77722216d6
2 geänderte Dateien mit 50 neuen und 28 gelöschten Zeilen
  1. 15 20
      deps/shui/src/logseq/shui/table/core.cljc
  2. 35 8
      src/main/frontend/utils.js

+ 15 - 20
deps/shui/src/logseq/shui/table/core.cljc

@@ -145,6 +145,12 @@
                  prop)
      children]))
 
+(defn- remove-sticky-header
+  []
+  (let [existing-headings (dom/sel ".ls-fixed")]
+    (doseq [node existing-headings]
+      (dom/remove-class! node "ls-fixed"))))
+
 (defn- use-sticky-element!
   [^js/HTMLDivElement target-ref container]
   (hooks/use-effect!
@@ -180,37 +186,26 @@
                                 ;; update scroll
                                 (set! (. target -scrollLeft) (.-scrollLeft table)))
                ;; target observer
-               target-observe! (fn []
+               target-observe! (fn [_e]
                                  (let [first-visible-table (some #(when (util/el-visible-in-viewport? % true) %)
                                                                  (dom/sel container ".ls-table-rows"))]
                                    (when (= table first-visible-table)
-                                     (let [scroll-top (js/parseInt (.-scrollTop container))
-                                           table-in-top (+ scroll-top head-height)
-                                           table-bottom (.-bottom (.getBoundingClientRect table))
-                                           table-top (.-top (.getBoundingClientRect table))
-                                           target-top (.-top (.getBoundingClientRect target))
-                                           fixed? (and (> table-bottom (+ head-height 90))
-                                                       (> table-in-top @*el-top))]
-                                       (cond
-                                         (and (pos? target-top) (pos? table-top) (>= target-top table-top))
-                                         (.remove target-cls "ls-fixed")
-                                         fixed?
-                                         (.add target-cls "ls-fixed")
-                                         :else
+                                     (let [table-bottom (.-bottom (.getBoundingClientRect table))
+                                           table-top (.-top (.getBoundingClientRect table))]
+                                       (if (and (< table-top head-height)
+                                                (> table-bottom 100))
+                                         (do
+                                           (remove-sticky-header)
+                                           (.add target-cls "ls-fixed"))
                                          (.remove target-cls "ls-fixed"))
                                        (update-target!)))))
-               target-observe-handle! (fn [^js _e]
-                                        (when (not @*ticking?)
-                                          (js/window.requestAnimationFrame
-                                           #(do (target-observe!) (vreset! *ticking? false)))
-                                          (vreset! *ticking? true)))
                resize-observer (js/ResizeObserver. update-target!)
                page-resize-observer (js/ResizeObserver. (fn [] (update-target-top!)))]
            ;; events
            (.observe resize-observer container)
            (.observe resize-observer table)
            (some->> page-el (.observe page-resize-observer))
-           (.addEventListener container "scroll" target-observe-handle!)
+           (.addEventListener container "scroll" target-observe!)
            (.addEventListener table "scroll" update-target!)
            (.addEventListener table "resize" update-target!)
            (update-footer!)

+ 35 - 8
src/main/frontend/utils.js

@@ -405,14 +405,41 @@ export const prettifyXml = (sourceXml) => {
 }
 
 export const elementIsVisibleInViewport = (el, partiallyVisible = false) => {
-  const { top, left, bottom, right } = el.getBoundingClientRect()
-  const { innerHeight, innerWidth } = window
-  return partiallyVisible
-    ? ((top > 0 && top < innerHeight) ||
-      (bottom > 0 && bottom < innerHeight)) &&
-    ((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth))
-    : top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth
-}
+  if (!el || el.getClientRects().length === 0) return false;
+
+  // Find nearest scrollable ancestor (null => window)
+  const getScrollRoot = (node) => {
+    let p = node && node.parentElement;
+    while (p) {
+      const cs = getComputedStyle(p);
+      const oy = cs.overflowY || cs.overflow, ox = cs.overflowX || cs.overflow;
+      if (/(auto|scroll|overlay)/.test(`${oy}${ox}`)) return p;
+      p = p.parentElement;
+    }
+    return null;
+  };
+
+  const r = el.getBoundingClientRect();
+  const root = getScrollRoot(el);
+
+  // Viewport rect: either the window or the scroll container’s content box
+  const vp = root
+    ? root.getBoundingClientRect()
+    : { top: 0, left: 0, right: window.innerWidth, bottom: window.innerHeight };
+
+  if (partiallyVisible) {
+    const horizontally = r.left < vp.right && r.right > vp.left;
+    const vertically   = r.top  < vp.bottom && r.bottom > vp.top;
+    return horizontally && vertically;
+  } else {
+    return (
+      r.top    >= vp.top &&
+      r.left   >= vp.left &&
+      r.bottom <= vp.bottom &&
+      r.right  <= vp.right
+    );
+  }
+};
 
 export const convertToLetters = (num) => {
   if (!+num) return false