-
Notifications
You must be signed in to change notification settings - Fork 42
Expand file tree
/
Copy pathuseScrollableHeight.ts
More file actions
68 lines (59 loc) · 1.87 KB
/
useScrollableHeight.ts
File metadata and controls
68 lines (59 loc) · 1.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import { useEffect, type RefObject } from "react";
/** Max share of the panel a section can claim via --section-flex-grow. */
const MAX_FLEX_RATIO = 0.5;
/**
* Sets an explicit pixel height on a wrapper element inside a VscodeCollapsible
* so it can scroll, and writes a --section-flex-grow CSS custom property on the
* host for content-adaptive sizing.
*/
export function useScrollableHeight(
hostRef: RefObject<HTMLElement | null>,
scrollRef: RefObject<HTMLElement | null>,
) {
useEffect(() => {
const host = hostRef.current;
const scroll = scrollRef.current;
if (!host || !scroll) {
return;
}
const observer = new ResizeObserver(() => {
if (!scroll.offsetParent) {
scroll.style.height = "";
return;
}
const hostRect = host.getBoundingClientRect();
const scrollTop = scroll.getBoundingClientRect().top;
const available = hostRect.bottom - scrollTop;
scroll.style.height = available > 0 ? `${available}px` : "";
const contentEl = scroll.firstElementChild as HTMLElement | null;
const panel = host.parentElement;
if (contentEl && panel && panel.clientHeight > 0) {
const ratio = Math.min(
(scrollTop - hostRect.top + contentEl.offsetHeight) /
panel.clientHeight,
MAX_FLEX_RATIO,
);
host.style.setProperty(
"--section-flex-grow",
(ratio / (1 - ratio)).toFixed(3),
);
}
});
// Observe content child so the layout recalculates when
// the content is replaced (e.g. loading state -> form).
function observeContent() {
if (scroll?.firstElementChild) {
observer.observe(scroll.firstElementChild);
}
}
observer.observe(host);
observer.observe(scroll);
observeContent();
const mutations = new MutationObserver(observeContent);
mutations.observe(scroll, { childList: true });
return () => {
observer.disconnect();
mutations.disconnect();
};
}, [hostRef, scrollRef]);
}