react-use#useThrottle TypeScript Examples

The following examples show how to use react-use#useThrottle. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: PanelCanvas.tsx    From ace with GNU Affero General Public License v3.0 4 votes vote down vote up
PanelCanvasElement = <T extends PossibleCanvasElements>({
    element,
    title,
    initialWidth,
    initialHeight,
    canvasZoom,
    resizingEnabled,
    onResizeCompleted,
    topBarContent,
    id,
    children,
}: PropsWithChildren<PanelCanvasElementProps<T>>) => {
    const projectDispatch = useProjectDispatch();

    const xResizeOriginalPos = useRef(0);
    const yResizeOriginalPos = useRef(0);
    const resizeMode = useRef<'x' | 'y' | 'xy' | null>(null);

    const [width, setWidth] = useState(initialWidth);
    const [height, setHeight] = useState(initialHeight);

    const [offsetX, setOffsetX] = useState(() => element.position.x);
    const [offsetY, setOffsetY] = useState(() => element.position.y);

    const throttledOffsetX = useThrottle(offsetX, 750);
    const throttledOffsetY = useThrottle(offsetY, 750);

    const TITLE_FONTSIZE = 18;

    const roundToGrid = (input: number): number => {
        const PROJECTED_GRID_CELL_SIZE = (PANEL_CANVAS_SIZE / GRID_SVG_SIZE) * GRID_LINE_SIZE;

        return Math.round(input / PROJECTED_GRID_CELL_SIZE) * PROJECTED_GRID_CELL_SIZE;
    };

    // Handle updating the saved element when the throttled position is updated
    useEffect(() => {
        projectDispatch(updateCanvasElement({ ...element, position: { x: throttledOffsetX, y: throttledOffsetY } }));
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [element.__kind, projectDispatch, throttledOffsetX, throttledOffsetY]);

    const [editPositionX, setEditPositionX] = useState(() => element.position.x);
    const [editPositionY, setEditPositionY] = useState(() => element.position.y);

    // Handle setting element position as edit movement changes
    useEffect(() => {
        setOffsetX(roundToGrid(editPositionX));
        setOffsetY(roundToGrid(editPositionY));
    }, [editPositionX, editPositionY]);

    const inEditMode = useProjectSelector((state) => state.interactionToolbar.panel === WorkspacePanelSelection.Edit);

    const canvasElementRef = useRef<HTMLDivElement>(null);

    const handlePanStart = (event: MouseEvent) => {
        document.body.addEventListener('mouseup', handlePanStop);
        document.body.addEventListener('mousemove', handlePanMouseMove);
        event.stopPropagation();
    };

    const handlePanStop = (event: globalThis.MouseEvent) => {
        document.body.removeEventListener('mouseup', handlePanStop);
        document.body.removeEventListener('mousemove', handlePanMouseMove);
        event.stopPropagation();
    };

    const handlePanMouseMove = (event: globalThis.MouseEvent) => {
        if (!canvasElementRef.current) {
            return;
        }

        setEditPositionX((old) => old + event.movementX / canvasZoom);
        setEditPositionY((old) => old + event.movementY / canvasZoom);

        event.stopPropagation();
    };

    const handleMouseDown = (e: React.MouseEvent) => {
        (e as any).canvasTarget = element;
    };

    const handleResizeMouseMove = useCallback((e: globalThis.PointerEvent) => {
        if (resizeMode.current.includes('x')) {
            const delta = (e.clientX / canvasZoom) - xResizeOriginalPos.current;

            setWidth(initialWidth + delta);
        }

        if (resizeMode.current.includes('y')) {
            const delta = (e.clientY / canvasZoom) - yResizeOriginalPos.current;

            setHeight(initialHeight + delta);
        }
    }, [canvasZoom, initialWidth, initialHeight]);

    const handleResizeStop = useCallback((event: globalThis.MouseEvent) => {
        document.body.removeEventListener('mouseup', handleResizeStop);
        document.body.removeEventListener('pointermove', handleResizeMouseMove);
        event.stopPropagation();

        // For some reason, width and height do not update fast enough
        onResizeCompleted(canvasElementRef.current.clientWidth, canvasElementRef.current.clientHeight);
    }, [handleResizeMouseMove, onResizeCompleted]);

    const handleResizeStart = useCallback((event: React.MouseEvent, newResizeMode: 'x' | 'y' | 'xy') => {
        document.body.addEventListener('mouseup', handleResizeStop);
        document.body.addEventListener('pointermove', handleResizeMouseMove);
        event.stopPropagation();

        if (newResizeMode.includes('x')) {
            xResizeOriginalPos.current = event.clientX / canvasZoom;
        }

        if (newResizeMode.includes('y')) {
            yResizeOriginalPos.current = event.clientY / canvasZoom;
        }

        resizeMode.current = newResizeMode;
    }, [canvasZoom, handleResizeMouseMove, handleResizeStop]);

    return (
        <span id={id} className="absolute" onMouseDown={handleMouseDown}>
            <span
                ref={canvasElementRef}
                className="shadow-md"
                style={{
                    width: `${width}px`,
                    height: `${height}px`,
                    position: 'absolute',
                    // transitionDuration: '0.1s',
                    transform: `translate(${(PANEL_CANVAS_SIZE / 2) + offsetX}px, ${(PANEL_CANVAS_SIZE / 2) + offsetY}px)`,
                }}
            >
                <span className="w-full absolute flex flex-row px-3 bg-gray-800 border-2 border-gray-700 rounded-t-md bg-opacity-80 h-10 -top-10 justify-between items-center">
                    <h1 style={{ fontSize: `${TITLE_FONTSIZE}px` }}>{title}</h1>

                    <div className="flex flex-row flex-grow ml-2">
                        <div className="w-full flex items-center gap-x-2 ml-2.5">
                            {topBarContent}
                        </div>

                        {inEditMode && (
                            <div className="flex flex-row ml-2">
                                <IconArrowsMaximize size={22} className="hover:text-purple-500 hover:cursor-pointer" onMouseDown={handlePanStart} />
                            </div>
                        )}
                    </div>

                </span>

                <span
                    className="block border border-[#00c2cc] hover:border-green-500 overflow-hidden"
                    style={{
                        width: `${width}px`,
                        height: `${height}px`,
                    }}
                >
                    {children}
                </span>

                {resizingEnabled && (
                    <>
                        <span className="absolute top-0 right-[-4px] flex flex-col justify-center" style={{ height: 'calc(100% + 4px)' }}>
                            <span className="absolute w-[4px] h-[40px] bg-green-500" style={{ cursor: 'ew-resize' }} onMouseDown={(e) => handleResizeStart(e, 'x')} />

                            <span className="mt-auto w-[4px] h-[40px] bg-green-500" style={{ cursor: 'nwse-resize' }} onMouseDown={(e) => handleResizeStart(e, 'xy')} />
                        </span>

                        <span className="absolute flex justify-center" style={{ width: 'calc(100% + 4px)' }}>
                            <span className="absolute w-[40px] h-[4px] bg-green-500" style={{ cursor: 'ns-resize' }} onMouseDown={(e) => handleResizeStart(e, 'y')} />

                            <span className="ml-auto w-[40px] h-[4px] bg-green-500" style={{ cursor: 'nwse-resize' }} onMouseDown={(e) => handleResizeStart(e, 'xy')} />
                        </span>
                    </>
                )}
            </span>
        </span>
    );
}