forked from malte-wessel/react-textfit
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.tsx
69 lines (58 loc) · 1.45 KB
/
index.tsx
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
69
import bsearch from "@namhong2001/binary-search";
import {
HTMLAttributes,
PropsWithChildren,
useCallback,
useEffect,
useRef,
} from "react";
interface Props extends HTMLAttributes<HTMLDivElement> {
mode?: "single" | "multi";
min?: number; // inclusive
max?: number; // inclusive
}
const Textfit: React.FC<PropsWithChildren<Props>> = ({
children,
mode = "multi",
min = 0,
max = 300,
...props
}) => {
const ref = useRef<HTMLDivElement>(null);
const isOverflown = useCallback(() => {
const el = ref.current!;
return el.scrollHeight > el.clientHeight || el.scrollWidth > el.clientWidth;
}, []);
const setFontSize = useCallback(() => {
const el = ref.current!;
const originVisibility = el.style.visibility;
el.style.visibility = "hidden";
const fontSize = bsearch(min, max + 1, (mid) => {
el.style.fontSize = `${mid}px`;
return !isOverflown();
});
el.style.fontSize = `${fontSize}px`;
el.style.visibility = originVisibility;
}, [isOverflown, min, max]);
useEffect(() => {
const el = ref.current!;
setFontSize();
const observer = new ResizeObserver(setFontSize);
observer.observe(el);
return () => {
observer.disconnect();
};
}, [children, mode, setFontSize]);
return (
<div
ref={ref}
style={{
whiteSpace: mode === "single" ? "nowrap" : "normal",
}}
{...props}
>
{children}
</div>
);
};
export default Textfit;