Skip to content

Instantly share code, notes, and snippets.

@manix84
Created July 24, 2024 08:19
Show Gist options
  • Select an option

  • Save manix84/db71f9ebfb0a1de0fb4198acd323a2ec to your computer and use it in GitHub Desktop.

Select an option

Save manix84/db71f9ebfb0a1de0fb4198acd323a2ec to your computer and use it in GitHub Desktop.
A React+Typescript hook for determining the correct middle of a string, and adding elipsis in the center.
/**
This code is licensed under the terms of the MIT license
*/
import { useState, useEffect, useRef, useCallback } from 'react';
const useMiddleTruncate = (text: string, separator = '...') => {
const [truncatedText, setTruncatedText] = useState(text);
const elementRef = useRef<HTMLDivElement | null>(null);
const resizeTimeoutRef = useRef<number | null>(null);
const getCharWidth = (font: string) => {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
if (!context) return 0;
context.font = font;
return context.measureText('n').width; // 'M' is generally the widest character in most fonts
};
const truncateText = useCallback(() => {
if (!elementRef.current) return;
const elementWidth = elementRef.current.offsetWidth;
const style = getComputedStyle(elementRef.current);
const font = `${style.fontWeight} ${style.fontSize} ${style.fontFamily}`;
const charWidth = getCharWidth(font);
const charsToShow = Math.floor(elementWidth / charWidth);// - separator.length
console.log({ elementWidth, charWidth, charsToShow });
if (text.length <= charsToShow) {
setTruncatedText(text);
} else {
const frontChars = Math.ceil(charsToShow / 2);
const backChars = Math.floor(charsToShow / 2);
setTruncatedText(text.substring(0, frontChars) + separator + text.substring(text.length - backChars));
}
}, [text, separator]);
useEffect(() => {
const handleResize = () => {
if (resizeTimeoutRef.current !== null) {
clearTimeout(resizeTimeoutRef.current);
}
resizeTimeoutRef.current = window.setTimeout(() => {
truncateText();
}, 100); // Adjust the debounce delay as necessary
};
const resizeObserver = new ResizeObserver(handleResize);
if (elementRef.current) {
resizeObserver.observe(elementRef.current);
}
return () => {
resizeObserver.disconnect();
if (resizeTimeoutRef.current !== null) {
clearTimeout(resizeTimeoutRef.current);
}
};
}, [truncateText]);
useEffect(() => {
truncateText();
}, [text, separator, truncateText]);
return { truncatedText, elementRef };
};
export default useMiddleTruncate;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment