1- import { RefObject , useCallback , useEffect , useMemo , useRef } from "react" ;
1+ import { RefObject , useCallback , useEffect , useRef } from "react" ;
22import SignaturePad , { Options } from "signature_pad" ;
3- import { SignatureContainerProps } from "../../typings/SignatureProps" ;
4-
5- function usePrevious < T > ( value : T ) : T | null {
6- const ref = useRef < T > ( null ) ;
7- useEffect ( ( ) => {
8- ref . current = value ;
9- } , [ value ] ) ;
10- return ref . current ;
11- }
3+ import { useResizeObserver } from "@mendix/widget-plugin-hooks/useResizeObserver" ;
4+ import { PenTypeEnum , SignatureContainerProps } from "../../typings/SignatureProps" ;
125
136export function useSignaturePad (
147 props : Pick < SignatureContainerProps , "imageSource" | "hasSignatureAttribute" | "penType" | "penColor" > ,
158 onSignEnd ?: ( imageDataURL ?: string ) => void
169) : {
17- signaturePadRef : RefObject < SignaturePad | null > ;
1810 canvasRef : RefObject < HTMLCanvasElement | null > ;
19- onResize ?: ( ) => void ;
11+ containerRef : RefObject < HTMLDivElement | null > ;
2012} {
2113 const { imageSource, hasSignatureAttribute, penType, penColor } = props ;
2214 const readOnly = imageSource . readOnly ;
2315 const signaturePadRef = useRef < SignaturePad | null > ( null ) ;
2416 const canvasRef = useRef < HTMLCanvasElement | null > ( null ) ;
25- const isSignatureInitialized = useRef ( false ) ;
2617 const hasSignature = usePrevious < boolean > ( hasSignatureAttribute ?. value ?? false ) ?? false ;
2718
28- const signaturePadOptions : Options = useMemo ( ( ) => {
29- let options : Options = { } ;
30- if ( penType === "fountain" ) {
31- options = { minWidth : 0.6 , maxWidth : 2.6 , velocityFilterWeight : 0.6 } ;
32- } else if ( penType === "ballpoint" ) {
33- options = { minWidth : 1.4 , maxWidth : 1.5 , velocityFilterWeight : 1.5 } ;
34- } else if ( penType === "marker" ) {
35- options = { minWidth : 2 , maxWidth : 4 , velocityFilterWeight : 0.9 } ;
36- }
37- return options ;
38- } , [ penType ] ) ;
39-
4019 const handleSignEnd = useCallback ( ( ) => {
4120 const imageDataUrl = signaturePadRef . current ?. toDataURL ( ) ;
42-
4321 if ( hasSignatureAttribute ) {
4422 hasSignatureAttribute . setValue ( ! signaturePadRef . current ?. isEmpty ( ) ) ;
4523 }
@@ -57,26 +35,31 @@ export function useSignaturePad(
5735 }
5836 } , [ readOnly ] ) ;
5937
60- const onResize = ( ) : void => {
61- if ( canvasRef . current && signaturePadRef . current ) {
62- const data = signaturePadRef . current . toData ( ) ;
63- canvasRef . current . width =
64- canvasRef . current && canvasRef . current . parentElement ? canvasRef . current . parentElement . offsetWidth : 0 ;
65- canvasRef . current . height =
66- canvasRef . current && canvasRef . current . parentElement ? canvasRef . current . parentElement . offsetHeight : 0 ;
67- signaturePadRef . current . clear ( ) ;
68- signaturePadRef . current . fromData ( data ) ;
69- }
70- } ;
38+ const handleResize = useCallback (
39+ ( element : HTMLDivElement ) => {
40+ const pad = signaturePadRef . current ;
41+ const canvas = canvasRef . current ;
42+ if ( pad && canvas ) {
43+ // off()+on() resets _drawingStroke and clears stale pointer/move listeners,
44+ // preventing pointerdown from being silently dropped after a mid-stroke resize.
45+ pad . off ( ) ;
46+ canvas . width = element . offsetWidth ;
47+ canvas . height = element . offsetHeight ;
48+ pad . redraw ( ) ;
49+ if ( ! readOnly ) {
50+ pad . on ( ) ;
51+ }
52+ }
53+ } ,
54+ [ readOnly ]
55+ ) ;
56+
57+ const containerRef = useResizeObserver ( handleResize ) as RefObject < HTMLDivElement | null > ;
7158
7259 // Clear signature pad when hasSignature value changes from true to false
7360 useEffect ( ( ) => {
74- if ( hasSignatureAttribute ?. status === "available" ) {
75- if ( hasSignatureAttribute ?. value !== hasSignature ) {
76- if ( hasSignature === true ) {
77- signaturePadRef . current ?. clear ( ) ;
78- }
79- }
61+ if ( hasSignatureAttribute ?. status === "available" && hasSignature && hasSignatureAttribute . value === false ) {
62+ signaturePadRef . current ?. clear ( ) ;
8063 }
8164 } , [ hasSignature , hasSignatureAttribute ?. status , hasSignatureAttribute ?. value ] ) ;
8265
@@ -88,19 +71,38 @@ export function useSignaturePad(
8871 const canInstantiateSignaturePad =
8972 signaturePadRef . current === null &&
9073 ( imageSource ?. status === "available" ? imageSource . value ?. uri : imageSource . status === "unavailable" ) ;
91- if ( canInstantiateSignaturePad && ! isSignatureInitialized . current ) {
92- signaturePadRef . current = new SignaturePad ( localCanvas , {
93- penColor,
94- ...signaturePadOptions
95- } ) ;
74+ if ( canInstantiateSignaturePad ) {
75+ const container = containerRef . current ;
76+ if ( container ) {
77+ localCanvas . width = container . offsetWidth ;
78+ localCanvas . height = container . offsetHeight ;
79+ }
80+ signaturePadRef . current = new SignaturePad ( localCanvas , { penColor, ...getPenOptions ( penType ) } ) ;
9681 signaturePadRef . current . addEventListener ( "endStroke" , handleSignEnd ) ;
9782 if ( readOnly ) {
98- signaturePadRef . current ? .off ( ) ;
83+ signaturePadRef . current . off ( ) ;
9984 }
100- isSignatureInitialized . current = true ;
10185 }
10286 }
103- } , [ handleSignEnd , penColor , readOnly , signaturePadOptions , imageSource , hasSignatureAttribute ] ) ;
87+ } , [ handleSignEnd , penColor , penType , readOnly , imageSource , hasSignatureAttribute , containerRef ] ) ;
88+
89+ return { canvasRef, containerRef } ;
90+ }
91+
92+ const PEN_OPTIONS : Record < PenTypeEnum , Options > = {
93+ fountain : { minWidth : 0.6 , maxWidth : 2.6 , velocityFilterWeight : 0.6 } ,
94+ ballpoint : { minWidth : 1.4 , maxWidth : 1.5 , velocityFilterWeight : 1.5 } ,
95+ marker : { minWidth : 2 , maxWidth : 4 , velocityFilterWeight : 0.9 }
96+ } ;
97+
98+ function getPenOptions ( penType : PenTypeEnum ) : Options {
99+ return PEN_OPTIONS [ penType ] ;
100+ }
104101
105- return { signaturePadRef, canvasRef, onResize } ;
102+ function usePrevious < T > ( value : T ) : T | null {
103+ const ref = useRef < T > ( null ) ;
104+ useEffect ( ( ) => {
105+ ref . current = value ;
106+ } , [ value ] ) ;
107+ return ref . current ;
106108}
0 commit comments