1
|
|
|
import React, { useState, useEffect } from "react"; |
2
|
|
|
import { countNumberOfWords } from "./helpers"; |
3
|
|
|
|
4
|
|
|
export interface WordCounterProps { |
5
|
|
|
/** Id of textarea element */ |
6
|
|
|
elementId: string; |
7
|
|
|
/** Maximum amount of words before passing the optimal range. The Progress Ring color correlates with this number. */ |
8
|
|
|
maxWords: number; |
9
|
|
|
/** Minimum amount of words to reach the optimal range. The Progress Ring color correlates with this number. */ |
10
|
|
|
minWords: number; |
11
|
|
|
/** Keep absolute value of max words - number of words */ |
12
|
|
|
absoluteValue?: boolean; |
13
|
|
|
/** String before current number of words */ |
14
|
|
|
beforeText?: string; |
15
|
|
|
/** String after counter when user is under the max */ |
16
|
|
|
underMaxMessage?: string; |
17
|
|
|
/** String after counter when user is over the max */ |
18
|
|
|
overMaxMessage?: string; |
19
|
|
|
/** List of clone data style attributes */ |
20
|
|
|
dataAttributes?: { [key: string]: string }; |
21
|
|
|
} |
22
|
|
|
|
23
|
|
|
const WordCounter: React.FunctionComponent<WordCounterProps> = ({ |
24
|
|
|
elementId, |
25
|
|
|
minWords, |
26
|
|
|
maxWords, |
27
|
|
|
absoluteValue, |
28
|
|
|
beforeText, |
29
|
|
|
underMaxMessage, |
30
|
|
|
overMaxMessage, |
31
|
|
|
dataAttributes, |
32
|
|
|
}): React.ReactElement => { |
33
|
|
|
const [numOfWords, setNumOfWords] = useState(0); |
34
|
|
|
|
35
|
|
|
useEffect((): (() => void) => { |
36
|
|
|
const element: HTMLTextAreaElement = document.getElementById( |
37
|
|
|
elementId, |
38
|
|
|
) as HTMLTextAreaElement; |
39
|
|
|
|
40
|
|
|
setNumOfWords(countNumberOfWords(element.value)); |
41
|
|
|
|
42
|
|
|
const handleInputChange = (e: Event): void => { |
43
|
|
|
const target = e.target as HTMLTextAreaElement; |
44
|
|
|
setNumOfWords(countNumberOfWords(target.value)); |
45
|
|
|
}; |
46
|
|
|
|
47
|
|
|
element.addEventListener("input", handleInputChange); |
48
|
|
|
|
49
|
|
|
return function cleanup(): void { |
50
|
|
|
element.removeEventListener("input", handleInputChange, false); |
51
|
|
|
}; |
52
|
|
|
}, [elementId]); |
53
|
|
|
|
54
|
|
|
return ( |
55
|
|
|
<span |
56
|
|
|
role="progressbar" |
57
|
|
|
aria-valuenow={numOfWords} |
58
|
|
|
aria-valuemin={minWords} |
59
|
|
|
aria-valuemax={maxWords} |
60
|
|
|
> |
61
|
|
|
{beforeText && <span>{beforeText} </span>} |
62
|
|
|
<span data-c-color={`${numOfWords <= maxWords ? "go" : "stop"}`}> |
63
|
|
|
{absoluteValue |
64
|
|
|
? Math.abs(maxWords - numOfWords) |
65
|
|
|
: maxWords - numOfWords} |
66
|
|
|
</span> |
67
|
|
|
<span> {numOfWords <= maxWords ? underMaxMessage : overMaxMessage}</span> |
68
|
|
|
</span> |
69
|
|
|
); |
70
|
|
|
}; |
71
|
|
|
|
72
|
|
|
export default WordCounter; |
73
|
|
|
|