@@ -6,14 +6,72 @@ interface DebouncedSpellcheckProps {
6
6
delay ?: number ;
7
7
}
8
8
9
+ // Global reference counter for the hook
10
+ let hookUsageCount = 0 ;
11
+ const STYLE_ID = 'debounced-spellcheck-styles' ;
12
+
13
+ const cssStyles = `
14
+ .spellcheck-hidden::-webkit-spelling-error {
15
+ text-decoration: none !important;
16
+ }
17
+
18
+ .spellcheck-hidden::-webkit-grammar-error {
19
+ text-decoration: none !important;
20
+ }
21
+
22
+ .spellcheck-hidden::-moz-spelling-error {
23
+ text-decoration: none !important;
24
+ }
25
+
26
+ .spellcheck-hidden::-moz-grammar-error {
27
+ text-decoration: none !important;
28
+ }
29
+
30
+ .spellcheck-hidden::spelling-error {
31
+ text-decoration: none !important;
32
+ }
33
+
34
+ .spellcheck-hidden::grammar-error {
35
+ text-decoration: none !important;
36
+ }
37
+ ` ;
38
+
9
39
export const useDebouncedSpellcheck = ( { delay = 300 , elementRef } : DebouncedSpellcheckProps ) => {
10
- const enableSpellcheck = useCallback ( ( ) => {
11
- elementRef . current ?. setAttribute ( 'spellcheck' , 'true' ) ;
40
+ // Inject CSS styles if they don't exist
41
+ useEffect ( ( ) => {
42
+ hookUsageCount ++ ;
43
+
44
+ // Only inject styles on first usage
45
+ if ( hookUsageCount === 1 && ! document . getElementById ( STYLE_ID ) ) {
46
+ const style = document . createElement ( 'style' ) ;
47
+ style . id = STYLE_ID ;
48
+ style . textContent = cssStyles ;
49
+ document . head . appendChild ( style ) ;
50
+ }
51
+
52
+ // Remove styles only when no components are using the hook
53
+ return ( ) => {
54
+ hookUsageCount -- ;
55
+ if ( hookUsageCount === 0 ) {
56
+ const existingStyle = document . getElementById ( STYLE_ID ) ;
57
+ if ( existingStyle ) {
58
+ existingStyle . remove ( ) ;
59
+ }
60
+ }
61
+ } ;
62
+ } , [ ] ) ;
63
+
64
+ const hideSpellcheckLines = useCallback ( ( ) => {
65
+ elementRef . current ?. classList . add ( 'spellcheck-hidden' ) ;
66
+ } , [ elementRef ] ) ;
67
+
68
+ const showSpellcheckLines = useCallback ( ( ) => {
69
+ elementRef . current ?. classList . remove ( 'spellcheck-hidden' ) ;
12
70
} , [ elementRef ] ) ;
13
71
14
72
// eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: see if we can create our own useDebounce hook
15
- const debouncedSpellcheck = useCallback ( debounce ( enableSpellcheck , delay ) , [
16
- enableSpellcheck ,
73
+ const debouncedShowSpellcheck = useCallback ( debounce ( showSpellcheckLines , delay ) , [
74
+ showSpellcheckLines ,
17
75
delay ,
18
76
] ) ;
19
77
@@ -24,15 +82,24 @@ export const useDebouncedSpellcheck = ({ delay = 300, elementRef }: DebouncedSpe
24
82
}
25
83
26
84
const handleInput = ( ) => {
27
- el . setAttribute ( 'spellcheck' , 'false' ) ;
28
- debouncedSpellcheck ( ) ;
85
+ // Hide spellcheck lines immediately while typing
86
+ hideSpellcheckLines ( ) ;
87
+ // Show them again after user stops typing
88
+ debouncedShowSpellcheck ( ) ;
29
89
} ;
30
90
31
91
el . addEventListener ( 'input' , handleInput ) ;
32
92
33
93
// eslint-disable-next-line consistent-return -- This return is the destructor
34
94
return ( ) => {
35
95
el . removeEventListener ( 'input' , handleInput ) ;
96
+ // Clean up: show spellcheck lines when component unmounts
97
+ showSpellcheckLines ( ) ;
36
98
} ;
37
- } , [ debouncedSpellcheck , elementRef ] ) ;
99
+ } , [ debouncedShowSpellcheck , hideSpellcheckLines , showSpellcheckLines , elementRef ] ) ;
100
+
101
+ return {
102
+ hideSpellcheckLines,
103
+ showSpellcheckLines,
104
+ } ;
38
105
} ;
0 commit comments