forked from Dogfalo/materialize
-
-
Notifications
You must be signed in to change notification settings - Fork 106
/
Copy pathcharacterCounter.ts
117 lines (97 loc) · 3.47 KB
/
characterCounter.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import { Component, BaseOptions, InitElements, MElement } from "./component";
export interface CharacterCounterOptions extends BaseOptions {};
const _defaults = Object.freeze({});
type InputElement = HTMLInputElement | HTMLTextAreaElement;
export class CharacterCounter extends Component<{}> {
declare el: InputElement;
/** Stores the reference to the counter HTML element. */
counterEl: HTMLSpanElement;
/** Specifies whether the input is valid or not. */
isInvalid: boolean;
/** Specifies whether the input text has valid length or not. */
isValidLength: boolean;
constructor(el: HTMLInputElement | HTMLTextAreaElement, options: Partial<CharacterCounterOptions>) {
super(el, {}, CharacterCounter);
(this.el as any).M_CharacterCounter = this;
this.options = {
...CharacterCounter.defaults,
...options
};
this.isInvalid = false;
this.isValidLength = false;
this._setupCounter();
this._setupEventHandlers();
}
static get defaults(): CharacterCounterOptions {
return _defaults;
}
/**
* Initializes instance of CharacterCounter.
* @param el HTML element.
* @param options Component options.
*/
static init(el: InputElement, options?: Partial<CharacterCounterOptions>): CharacterCounter;
/**
* Initializes instances of CharacterCounter.
* @param els HTML elements.
* @param options Component options.
*/
static init(els: InitElements<InputElement | MElement>, options?: Partial<CharacterCounterOptions>): CharacterCounter[];
/**
* Initializes instances of CharacterCounter.
* @param els HTML elements.
* @param options Component options.
*/
static init(els: InputElement | InitElements<InputElement | MElement>, options: Partial<CharacterCounterOptions> = {}): CharacterCounter | CharacterCounter[] {
return super.init(els, options, CharacterCounter);
}
static getInstance(el: InputElement): CharacterCounter {
return (el as any).M_CharacterCounter;
}
destroy() {
this._removeEventHandlers();
(this.el as any).CharacterCounter = undefined;
this._removeCounter();
}
_setupEventHandlers() {
this.el.addEventListener('focus', this.updateCounter, true);
this.el.addEventListener('input', this.updateCounter, true);
}
_removeEventHandlers() {
this.el.removeEventListener('focus', this.updateCounter, true);
this.el.removeEventListener('input', this.updateCounter, true);
}
_setupCounter() {
this.counterEl = document.createElement('span');
this.counterEl.classList.add('character-counter');
this.counterEl.style.float = 'right';
this.counterEl.style.fontSize = '12px';
this.counterEl.style.height = '1';
this.el.parentElement.appendChild(this.counterEl);
}
_removeCounter() {
this.counterEl.remove();
}
updateCounter = () => {
let maxLength = parseInt(this.el.getAttribute('maxlength')),
actualLength = (this.el as HTMLInputElement).value.length;
this.isValidLength = actualLength <= maxLength;
let counterString = actualLength.toString();
if (maxLength) {
counterString += '/' + maxLength;
this._validateInput();
}
this.counterEl.innerHTML = counterString;
}
_validateInput() {
if (this.isValidLength && this.isInvalid) {
this.isInvalid = false;
this.el.classList.remove('invalid');
}
else if (!this.isValidLength && !this.isInvalid) {
this.isInvalid = true;
this.el.classList.remove('valid');
this.el.classList.add('invalid');
}
}
}