Completed
Push — master ( 530622...0684ff )
by D
02:37
created

SelectExtended.initEvents   B

Complexity

Conditions 5

Size

Total Lines 47
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 38
dl 0
loc 47
rs 8.5013
c 0
b 0
f 0
cc 5
1
import {define} from "../globals/globals";
2
import {SelectExtendedInterface} from "../interfaces/SelectExtendedInterface";
3
import {SelectExtendedOptions} from "../types/SelectExtendedOptions";
4
5
(function (root, factory) {
6
    if (typeof define === 'function' && define.amd) {
7
        define([], factory);
8
    } else if (typeof module === 'object' && module.exports) {
9
        module.exports = factory();
10
    } else {
11
        root!.SelectExtended = factory();
12
    }
13
}(typeof self !== 'undefined' ? self : this, function () {
14
15
    class SelectExtended implements SelectExtendedInterface {
16
17
        public $select: any;
18
        public $el: HTMLElement;
19
        public id: number;
20
        static id: number;
21
        public placeholder: string;
22
        public options: any;
23
        public blocked: boolean;
24
        public value: any;
25
        public $value: HTMLElement;
26
        valueName: string;
27
        selectedCount: number;
28
        checked: boolean;
29
30
        constructor($el: string | Element, options: SelectExtendedOptions) {
31
            let that = this;
32
33
            this.id = SelectExtended.generateId();
34
            this.$select = typeof $el === 'string' ? document.querySelector($el) : $el;
35
            this.placeholder = this.$select.dataset['placeholder'] || '';
36
37
            if(options) {
38
                this.options = options as object;
39
                this.options.containerClass = options.containerClass as string;
40
                this.options.multiSelect = options.multiSelect as boolean;
41
                this.options.multiSelectedText = options.multiSelectedText as string;
42
                this.options.onChange = options.onChange as Function;
43
            }
44
            else {
45
                this.mergeOptions();
46
            }
47
48
            this.blocked = false;
49
50
            this.initHtml();
51
            this.initEvents();
52
53
            let value = '';
54
55
            Object.defineProperty(this, 'value', {
56
                get: function (): string {
57
                    if (that.options.multiSelect) {
58
                        let result: any = [];
59
60
                        (that.$el.querySelectorAll('.select-ext-multi-option') as NodeListOf<HTMLElement>).forEach(($opt: HTMLElement) => {
61
                            if ($opt.querySelector('input')!.checked) {
62
                                result.push($opt.querySelector('input')!.value);
63
                            }
64
                        });
65
66
                        return result;
67
                    }
68
69
                    return value;
70
                },
71
                set: function (newValue: string): void {
72
                    value = newValue;
73
                    that.updateValue(value);
74
75
                    if (typeof that.options.onChange === 'function') {
76
                        that.options.onChange();
77
                    }
78
                }
79
            });
80
81
            if (!this.options.multiSelect) {
82
                this.value = null;
83
            }
84
        }
85
86
        private mergeOptions(): void {
87
            let defaults = {
88
                onChange: function () {}
89
            };
90
91
            this.options = Object.assign(defaults)
92
        }
93
94
        static generateId(): number {
95
            if (!SelectExtended.id) {
96
                SelectExtended.id = 0;
97
            }
98
99
            return SelectExtended.id++;
100
        }
101
102
        private initHtml(): void {
103
            this.$select.style.display = 'none';
104
            this.$el = document.createElement('div');
105
            this.$el.classList.add('select-ext');
106
            this.$el.classList.add('select-ext-' + this.id);
107
108
            if (this.options.containerClass) {
109
                this.$el.classList.add(this.options.containerClass);
110
            }
111
112
            if (this.options.multiSelect) {
113
                this.initMultiSelectHtml();
114
            }
115
            else {
116
                this.$el.appendChild(this.makeCurrentValue());
117
                this.$el.appendChild(this.makeOptionGroup());
118
                this.$select.parentElement.insertBefore(this.$el, this.$select);
119
                this.$el.appendChild(this.$select);
120
            }
121
        }
122
123
        private initEvents(): void {
124
            let that = this;
125
126
            that.$value.onclick = function (event: Event) {
127
                that.blocked = true;
128
                that.$el.classList.toggle('active');
129
130
                setTimeout(() => {
131
                    that.blocked = false;
132
                }, 50);
133
            };
134
135
            document.body.addEventListener('click', function (event: Event) {
136
                if (!that.blocked && (!((event.target! as Element).closest('.select-ext-' + that.id))) as boolean) {
137
                    that.$el.classList.remove('active');
138
                }
139
            });
140
141
            (that.$el.querySelectorAll('.select-ext-option') as NodeListOf<HTMLElement>).forEach((option: HTMLElement) => {
142
                option.onclick = function () {
143
                    that.value = option.dataset['value'];
144
                    that.valueName = option.innerHTML;
145
                    that.$el.classList.remove('active');
146
                }
147
            });
148
149
            if (that.options.multiSelect) {
150
                that.selectedCount = 0;
151
152
                (that.$el.querySelectorAll('.select-ext-multi-option') as NodeListOf<HTMLElement>).forEach((option: HTMLElement) => {
153
                    option.querySelector('input')!.onclick = function (event: Event) {
154
155
                        if (that.checked) {
156
                            that.selectedCount++;
157
                        }
158
                        else {
159
                            that.selectedCount--;
160
                        }
161
162
                        that.$value.innerHTML = that.options.multiSelectedText + ' ' + that.selectedCount;
163
164
                        if (that.selectedCount > 0) {
165
                            that.$value.classList.add('active');
166
                        }
167
                        else {
168
                            that.$value.classList.remove('active');
169
                        }
170
                    }
171
                });
172
            }
173
        }
174
175
        private getName(): string {
176
            return this.$select.name;
177
        }
178
179
        protected initMultiSelectHtml(): void {
180
            // Value
181
            this.$value = document.createElement('div');
182
            this.$value.classList.add('select-ext__value');
183
            this.$el.appendChild(this.$value);
184
185
            // Make custom group
186
            let $group = document.createElement('div');
187
            $group.classList.add('select-ext__options');
188
            this.$el.appendChild($group);
189
190
            // Make checkbox options
191
            for (let i = 0; i < this.$select.options.length; i++) {
192
                let $option = this.makeMultiOption(this.$select.options[i].innerHTML, this.$select.options[i].value, this.$select.options[i].dataset['selected']);
193
                $group.appendChild($option);
194
            }
195
196
            this.$select.parentElement.insertBefore(this.$el, this.$select);
197
198
            // Remove origin select
199
            this.$select.remove();
200
201
            setTimeout(() => {
202
                let that = this;
203
204
                if (that.options.multiSelect) {
205
                    that.selectedCount = 0;
206
207
                    (that.$el.querySelectorAll('.select-ext-multi-option') as NodeListOf<HTMLElement>).forEach((option: HTMLElement) => {
208
                        if (option.querySelector('input')!.checked) {
209
                            that.selectedCount++;
210
                        }
211
                    });
212
213
                    that.$value.innerHTML = that.options.multiSelectedText + ' ' + that.selectedCount;
214
215
                    if (that.selectedCount > 0) {
216
                        that.$value.classList.add('active');
217
                    }
218
                    else {
219
                        that.$value.classList.remove('active');
220
                    }
221
                }
222
            }, 100);
223
224
        }
225
226
        protected makeMultiOption(name: string, value: string, selected: boolean): HTMLElement {
227
            let $option = document.createElement('label');
228
            $option.classList.add('select-ext-multi-option');
229
230
            let $checkbox = document.createElement('input');
231
            $checkbox.type = 'checkbox';
232
            $checkbox.name = this.$select.name + '[]';
233
            $checkbox.value = value;
234
            $checkbox.checked = selected;
235
236
            let $fishSpan = document.createElement('span');
237
238
            let $name = document.createElement('span');
239
            $name.innerHTML = name;
240
241
            $option.appendChild($checkbox);
242
            $option.appendChild($fishSpan);
243
            $option.appendChild($name);
244
245
            return $option;
246
        }
247
248
        protected makeCurrentValue(): HTMLElement {
249
            this.$value = document.createElement('div');
250
            this.$value.classList.add('select-ext__value');
251
252
            return this.$value;
253
        }
254
255
        protected makeOptionGroup(): HTMLElement {
256
            let $group = document.createElement('div');
257
258
            $group.classList.add('select-ext__options');
259
260
            for (let i = 0; i < this.$select.options.length; i++) {
261
                $group.appendChild(SelectExtended.makeOption(this.$select.options[i].value, this.$select.options[i].innerHTML));
262
            }
263
264
            return $group;
265
        }
266
267
        protected updateValue(value: string): void {
268
            if (value === null) {
269
                this.setInactive();
270
271
                return;
272
            }
273
274
            let $options = this.$el.querySelectorAll('.select-ext-option');
275
276
            for (let i = 0; i < $options.length; i++) {
277
                if (($options as NodeListOf<HTMLElement>)[i].dataset['value'] === value) {
278
                    this.$select.value = value;
279
                    this.$value.innerHTML = $options[i].innerHTML;
280
281
                    if (this.$value.innerHTML) {
282
                        this.$value.classList.add('active');
283
                    }
284
285
                    return;
286
                }
287
            }
288
289
            this.value = null;
290
        }
291
292
        protected setInactive(): void {
293
            this.$select.selectIndex = -1;
294
            this.$value.innerHTML = this.placeholder;
295
            this.$value.classList.remove('active');
296
        }
297
298
        static makeOption(value: string, text: string): HTMLElement {
299
            let $option = document.createElement('div');
300
            $option.classList.add('select-ext-option');
301
            $option.dataset['value'] = value;
302
            $option.innerHTML = text;
303
304
            return $option;
305
        }
306
307
    }
308
309
    return SelectExtended;
310
}));