@daimo/pay
Version:
Seamless crypto payments. Onboard users from any chain, any coin into your app with one click.
134 lines (131 loc) • 3.83 kB
JavaScript
import { useCallback, useRef, useState, useEffect, useLayoutEffect } from 'react';
import ResizeObserver from 'resize-observer-polyfill';
const LOG_LEVEL = {
debug: 10,
info: 20,
warn: 30,
error: 40,
none: 100
};
const useIsoLayoutEffect = typeof window !== "undefined" && window.document && window.document.createElement !== void 0 ? useLayoutEffect : useEffect;
const useFitText = ({
logLevel: logLevelOption = "info",
maxFontSize = 100,
minFontSize = 20,
onFinish,
onStart,
resolution = 5
} = {}) => {
const logLevel = LOG_LEVEL[logLevelOption];
const initState = useCallback(() => {
return {
calcKey: 0,
fontSize: maxFontSize,
fontSizePrev: minFontSize,
fontSizeMax: maxFontSize,
fontSizeMin: minFontSize
};
}, [maxFontSize, minFontSize]);
const ref = useRef(null);
const innerHtmlPrevRef = useRef();
const isCalculatingRef = useRef(false);
const [state, setState] = useState(initState);
const { calcKey, fontSize, fontSizeMax, fontSizeMin, fontSizePrev } = state;
let animationFrameId = null;
const [ro] = useState(
() => new ResizeObserver(() => {
animationFrameId = window.requestAnimationFrame(() => {
if (isCalculatingRef.current) {
return;
}
onStart && onStart();
isCalculatingRef.current = true;
setState({
...initState(),
calcKey: calcKey + 1
});
});
})
);
useEffect(() => {
if (ref.current) {
ro.observe(ref.current);
}
return () => {
animationFrameId && window.cancelAnimationFrame(animationFrameId);
ro.disconnect();
};
}, [animationFrameId, ro]);
const innerHtml = ref.current && ref.current.innerHTML;
useEffect(() => {
if (calcKey === 0 || isCalculatingRef.current) return;
if (innerHtml !== innerHtmlPrevRef.current) {
onStart && onStart();
setState({
...initState(),
calcKey: calcKey + 1
});
}
innerHtmlPrevRef.current = innerHtml;
}, [calcKey, initState, innerHtml, onStart]);
useIsoLayoutEffect(() => {
if (calcKey === 0) {
return;
}
const isWithinResolution = Math.abs(fontSize - fontSizePrev) <= resolution;
const isOverflow = !!ref.current && (ref.current.scrollHeight > ref.current.offsetHeight || ref.current.scrollWidth > ref.current.offsetWidth);
const isFailed = isOverflow && fontSize === fontSizePrev;
const isAsc = fontSize > fontSizePrev;
if (isWithinResolution) {
if (isFailed) {
isCalculatingRef.current = false;
if (logLevel <= LOG_LEVEL.info) {
console.info(
`[use-fit-text] reached \`minFontSize = ${minFontSize}\` without fitting text`
);
}
} else if (isOverflow) {
setState({
fontSize: isAsc ? fontSizePrev : fontSizeMin,
fontSizeMax,
fontSizeMin,
fontSizePrev,
calcKey
});
} else {
isCalculatingRef.current = false;
onFinish && onFinish(fontSize);
}
return;
}
let delta;
let newMax = fontSizeMax;
let newMin = fontSizeMin;
if (isOverflow) {
delta = isAsc ? fontSizePrev - fontSize : fontSizeMin - fontSize;
newMax = Math.min(fontSizeMax, fontSize);
} else {
delta = isAsc ? fontSizeMax - fontSize : fontSizePrev - fontSize;
newMin = Math.max(fontSizeMin, fontSize);
}
setState({
calcKey,
fontSize: fontSize + delta / 2,
fontSizeMax: newMax,
fontSizeMin: newMin,
fontSizePrev: fontSize
});
}, [
calcKey,
fontSize,
fontSizeMax,
fontSizeMin,
fontSizePrev,
onFinish,
ref,
resolution
]);
return { fontSize, ref };
};
export { useFitText as default };
//# sourceMappingURL=useFitText.js.map