mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-07-06 04:57:48 +02:00
refactor: animate tabs
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
|
||||
type AutoHeightOptions = {
|
||||
includeParentBox?: boolean;
|
||||
includeSelfBox?: boolean;
|
||||
};
|
||||
|
||||
export function useAutoHeight<T extends HTMLElement = HTMLDivElement>(
|
||||
deps: React.DependencyList = [],
|
||||
options: AutoHeightOptions = {
|
||||
includeParentBox: true,
|
||||
includeSelfBox: false,
|
||||
},
|
||||
) {
|
||||
const ref = React.useRef<T | null>(null);
|
||||
const roRef = React.useRef<ResizeObserver | null>(null);
|
||||
const [height, setHeight] = React.useState(0);
|
||||
|
||||
const measure = React.useCallback(() => {
|
||||
const el = ref.current;
|
||||
if (!el) return 0;
|
||||
|
||||
const base = el.getBoundingClientRect().height || 0;
|
||||
|
||||
let extra = 0;
|
||||
|
||||
if (options.includeParentBox && el.parentElement) {
|
||||
const cs = getComputedStyle(el.parentElement);
|
||||
const paddingY =
|
||||
(parseFloat(cs.paddingTop || "0") || 0) +
|
||||
(parseFloat(cs.paddingBottom || "0") || 0);
|
||||
const borderY =
|
||||
(parseFloat(cs.borderTopWidth || "0") || 0) +
|
||||
(parseFloat(cs.borderBottomWidth || "0") || 0);
|
||||
const isBorderBox = cs.boxSizing === "border-box";
|
||||
if (isBorderBox) {
|
||||
extra += paddingY + borderY;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.includeSelfBox) {
|
||||
const cs = getComputedStyle(el);
|
||||
const paddingY =
|
||||
(parseFloat(cs.paddingTop || "0") || 0) +
|
||||
(parseFloat(cs.paddingBottom || "0") || 0);
|
||||
const borderY =
|
||||
(parseFloat(cs.borderTopWidth || "0") || 0) +
|
||||
(parseFloat(cs.borderBottomWidth || "0") || 0);
|
||||
const isBorderBox = cs.boxSizing === "border-box";
|
||||
if (isBorderBox) {
|
||||
extra += paddingY + borderY;
|
||||
}
|
||||
}
|
||||
|
||||
const dpr =
|
||||
typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
|
||||
const total = Math.ceil((base + extra) * dpr) / dpr;
|
||||
|
||||
return total;
|
||||
}, [options.includeParentBox, options.includeSelfBox]);
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
const el = ref.current;
|
||||
if (!el) return;
|
||||
|
||||
setHeight(measure());
|
||||
|
||||
if (roRef.current) {
|
||||
roRef.current.disconnect();
|
||||
roRef.current = null;
|
||||
}
|
||||
|
||||
const ro = new ResizeObserver(() => {
|
||||
const next = measure();
|
||||
requestAnimationFrame(() => setHeight(next));
|
||||
});
|
||||
|
||||
ro.observe(el);
|
||||
if (options.includeParentBox && el.parentElement) {
|
||||
ro.observe(el.parentElement);
|
||||
}
|
||||
|
||||
roRef.current = ro;
|
||||
|
||||
return () => {
|
||||
ro.disconnect();
|
||||
roRef.current = null;
|
||||
};
|
||||
}, [...deps, measure, options.includeParentBox]);
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
if (height === 0) {
|
||||
const next = measure();
|
||||
if (next !== 0) setHeight(next);
|
||||
}
|
||||
}, [height, measure]);
|
||||
|
||||
return { ref, height } as const;
|
||||
}
|
||||
Reference in New Issue
Block a user