mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 16:23:06 +10:00
chore: use unsafe effect
This commit is contained in:
13
packages/lib/client-only/hooks/use-effect-once.ts
Normal file
13
packages/lib/client-only/hooks/use-effect-once.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import type { EffectCallback } from 'react';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dangerously runs an effect "once" by ignoring the depedencies of a given effect.
|
||||||
|
*
|
||||||
|
* DANGER: The effect will run twice in concurrent react and development environments.
|
||||||
|
*/
|
||||||
|
export const unsafe_useEffectOnce = (callback: EffectCallback) => {
|
||||||
|
// Intentionally avoiding exhaustive deps and rule of hooks here
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps, react-hooks/rules-of-hooks
|
||||||
|
return useEffect(callback, []);
|
||||||
|
};
|
||||||
@ -7,6 +7,8 @@ import { Undo2 } from 'lucide-react';
|
|||||||
import type { StrokeOptions } from 'perfect-freehand';
|
import type { StrokeOptions } from 'perfect-freehand';
|
||||||
import { getStroke } from 'perfect-freehand';
|
import { getStroke } from 'perfect-freehand';
|
||||||
|
|
||||||
|
import { unsafe_useEffectOnce } from '@documenso/lib/client-only/hooks/use-effect-once';
|
||||||
|
|
||||||
import { cn } from '../../lib/utils';
|
import { cn } from '../../lib/utils';
|
||||||
import { getSvgPathFromStroke } from './helper';
|
import { getSvgPathFromStroke } from './helper';
|
||||||
import { Point } from './point';
|
import { Point } from './point';
|
||||||
@ -28,7 +30,8 @@ export const SignaturePad = ({
|
|||||||
...props
|
...props
|
||||||
}: SignaturePadProps) => {
|
}: SignaturePadProps) => {
|
||||||
const $el = useRef<HTMLCanvasElement>(null);
|
const $el = useRef<HTMLCanvasElement>(null);
|
||||||
const defaultImageRef = useRef<ImageData | null>(null);
|
const $imageData = useRef<ImageData | null>(null);
|
||||||
|
|
||||||
const [isPressed, setIsPressed] = useState(false);
|
const [isPressed, setIsPressed] = useState(false);
|
||||||
const [lines, setLines] = useState<Point[][]>([]);
|
const [lines, setLines] = useState<Point[][]>([]);
|
||||||
const [currentLine, setCurrentLine] = useState<Point[]>([]);
|
const [currentLine, setCurrentLine] = useState<Point[]>([]);
|
||||||
@ -162,7 +165,7 @@ export const SignaturePad = ({
|
|||||||
const ctx = $el.current.getContext('2d');
|
const ctx = $el.current.getContext('2d');
|
||||||
|
|
||||||
ctx?.clearRect(0, 0, $el.current.width, $el.current.height);
|
ctx?.clearRect(0, 0, $el.current.width, $el.current.height);
|
||||||
defaultImageRef.current = null;
|
$imageData.current = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange?.(null);
|
onChange?.(null);
|
||||||
@ -176,8 +179,7 @@ export const SignaturePad = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newLines = [...lines];
|
const newLines = lines.slice(0, -1);
|
||||||
newLines.pop(); // Remove the last line
|
|
||||||
setLines(newLines);
|
setLines(newLines);
|
||||||
|
|
||||||
// Clear the canvas
|
// Clear the canvas
|
||||||
@ -185,13 +187,16 @@ export const SignaturePad = ({
|
|||||||
const ctx = $el.current.getContext('2d');
|
const ctx = $el.current.getContext('2d');
|
||||||
const { width, height } = $el.current;
|
const { width, height } = $el.current;
|
||||||
ctx?.clearRect(0, 0, width, height);
|
ctx?.clearRect(0, 0, width, height);
|
||||||
if (typeof defaultValue === 'string' && defaultImageRef.current) {
|
|
||||||
ctx?.putImageData(defaultImageRef.current, 0, 0);
|
if (typeof defaultValue === 'string' && $imageData.current) {
|
||||||
|
ctx?.putImageData($imageData.current, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
newLines.forEach((line) => {
|
newLines.forEach((line) => {
|
||||||
const pathData = new Path2D(getSvgPathFromStroke(getStroke(line, perfectFreehandOptions)));
|
const pathData = new Path2D(getSvgPathFromStroke(getStroke(line, perfectFreehandOptions)));
|
||||||
ctx?.fill(pathData);
|
ctx?.fill(pathData);
|
||||||
});
|
});
|
||||||
|
|
||||||
onChange?.($el.current.toDataURL());
|
onChange?.($el.current.toDataURL());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -203,7 +208,7 @@ export const SignaturePad = ({
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
unsafe_useEffectOnce(() => {
|
||||||
if ($el.current && typeof defaultValue === 'string') {
|
if ($el.current && typeof defaultValue === 'string') {
|
||||||
const ctx = $el.current.getContext('2d');
|
const ctx = $el.current.getContext('2d');
|
||||||
|
|
||||||
@ -213,13 +218,15 @@ export const SignaturePad = ({
|
|||||||
|
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
ctx?.drawImage(img, 0, 0, Math.min(width, img.width), Math.min(height, img.height));
|
ctx?.drawImage(img, 0, 0, Math.min(width, img.width), Math.min(height, img.height));
|
||||||
|
|
||||||
const defaultImageData = ctx?.getImageData(0, 0, width, height) || null;
|
const defaultImageData = ctx?.getImageData(0, 0, width, height) || null;
|
||||||
defaultImageRef.current = defaultImageData;
|
|
||||||
|
$imageData.current = defaultImageData;
|
||||||
};
|
};
|
||||||
|
|
||||||
img.src = defaultValue;
|
img.src = defaultValue;
|
||||||
}
|
}
|
||||||
}, []);
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in New Issue
Block a user