remanufacturing / react-truncate

Provides `Truncate`, `MiddleTruncate` and `ShowMore` React components for truncating multi-line spans and adding an ellipsis.
https://truncate.js.org
Other
19 stars 1 forks source link

Update when @container width changes #18

Open johnstack94 opened 2 weeks ago

johnstack94 commented 2 weeks ago

Sometimes the container element is part of changes (eg: react-resizable-panels) without resizing the actual window. It'd be nice for it to update whenever this happens.

johnstack94 commented 2 weeks ago

I fixed it, I'll create a PR with the proposed fix.

johnstack94 commented 2 weeks ago

I see that I can't, hence I'll post it here.

Truncate.tsx


// Line 40: Add following ref
...
const resizeObserverRef = useRef<ResizeObserver | null>(null);

...

// Lines 84-101
  useEffect(() => {
    calcTWidth();

    // Create ResizeObserver
    resizeObserverRef.current = new ResizeObserver(() => {
      calcTWidth();
    });

    // Observe the parent element
    if (targetRef.current?.parentElement) {
      resizeObserverRef.current.observe(targetRef.current.parentElement);
    }

    return () => {
      // Cleanup ResizeObserver
      if (resizeObserverRef.current) {
        resizeObserverRef.current.disconnect();
      }

      window.cancelAnimationFrame(animationFrame);
    };
  }, [calcTWidth, animationFrame]);
johnstack94 commented 2 weeks ago

The following is if you want a debounce so it doesnt react directly.

  const resizeObserverRef = useRef<ResizeObserver | null>(null);
  const resizeTimeoutRef = useRef<number | null>(null);

  ...

    useEffect(() => {
    calcTWidth();

    // Debounce resize
    const handleResize = () => {
      if (resizeTimeoutRef.current !== null) {
        window.clearTimeout(resizeTimeoutRef.current);
      }
      resizeTimeoutRef.current = window.setTimeout(() => {
        calcTWidth();
      }, 1);
    };

    // Create ResizeObserver
    resizeObserverRef.current = new ResizeObserver(handleResize);

    // Observe the parent element
    if (targetRef.current?.parentElement) {
      resizeObserverRef.current.observe(targetRef.current.parentElement);
    }

    return () => {
      // Cleanup ResizeObserver
      if (resizeObserverRef.current) {
        resizeObserverRef.current.disconnect();
      }

      // Clear any pending timeout
      if (resizeTimeoutRef.current !== null) {
        window.clearTimeout(resizeTimeoutRef.current);
      }

      window.cancelAnimationFrame(animationFrame);
    };
  }, [calcTWidth, animationFrame]);

I found 1ms prevented flashing in my case. However this might be entirely situational.

chengpeiquan commented 1 week ago

I just finished a busy work and now I am follow up on your issue now.

Thanks for your enthusiastic research. Here I have a simpler implementation. Please refer to whether it can meet your needs.

  1. Set a variable and bind it to the Panel that needs to respond to resize changes, key={refreshKey}
const [refreshKey, setRefreshKey] = useState(Date.now());
  1. When the parent element triggers a resize change, update this value
const onResize = () => setRefreshKey(Date.now());

This makes it easy to re-truncate by React's ability to re-render nodes when a key is updated.

I added a little demo based on the official demo of react-resizable-panels, see: codesandbox