simp-select 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,608 +0,0 @@
1
- import { IItemLocalOptions, ISimpleSelectOptions } from './types/simpleSelect.types';
2
- import {
3
- cloneObj, createButton, getClass, ifTrueDataAttr, toCamelCase,
4
- } from './utils/simpleSelection.utils';
5
- import { ICreateLiReturn, IOptionItem, IOptionItems } from './types/item.types';
6
- import { store } from './utils/store';
7
- import { initClass } from './const/simpleSelection.const';
8
-
9
- interface IState {
10
- items: IOptionItems[];
11
- isOpen: boolean;
12
- isFloat: boolean;
13
- filterStr: string;
14
- }
15
- export class SimpleSelectItemDOM {
16
- options!: ISimpleSelectOptions;
17
-
18
- $select!:HTMLSelectElement;
19
-
20
- id!: string;
21
-
22
- titlePlaceholder!: string;
23
-
24
- isDisabled: boolean = false;
25
-
26
- isMulti!: boolean;
27
-
28
- state = store<IState>({
29
- items: [],
30
- isOpen: false,
31
- filterStr: '',
32
- isFloat: false,
33
- });
34
-
35
- // classSelectInit= 'SimpleSel__select_init'
36
- classSelectInit = getClass('select_init');
37
-
38
- isNative: boolean;
39
-
40
- elemWrap: HTMLDivElement = document.createElement('div'); // all
41
-
42
- elemTop: HTMLDivElement = document.createElement('div'); // all
43
-
44
- elemTopBody: HTMLDivElement = document.createElement('div'); // all
45
-
46
- elemDropDown: HTMLDivElement | null = null; // not native
47
-
48
- elemDropDownClose: HTMLButtonElement | null = null; // not native
49
-
50
- elemListBody: HTMLUListElement | null = null; // not native
51
-
52
- elemInputSearch: HTMLInputElement | null = null; // not native
53
-
54
- elemTitle!: HTMLDivElement; // not native
55
-
56
- confirmOk: HTMLButtonElement | null = null; // not native
57
-
58
- confirmNo: HTMLButtonElement | null = null; // not native
59
-
60
- elemControl: HTMLDivElement | null = null; // not native
61
-
62
- elemSelectAll: HTMLButtonElement | null = null; // not native
63
-
64
- elemResetAll: HTMLButtonElement | null = null; // not native
65
-
66
- cloneClasses = '';
67
-
68
- isShowCheckbox = false;
69
-
70
- bodyLiHTMLBeforeFromSelect: null | string = null;
71
-
72
- bodyLiHTMLAfterFromSelect: null | string = null;
73
-
74
- isFloatWidth = false;
75
-
76
- bodyOpenClass = `${initClass}--body_open`;
77
-
78
- constructor(select: HTMLSelectElement, options: ISimpleSelectOptions, localOptions: IItemLocalOptions) {
79
- const { id, isNative } = localOptions;
80
- this.$select = select;
81
- this.isMulti = select.multiple;
82
- this.id = id;
83
- this.isNative = isNative;
84
-
85
- this.options = cloneObj(options);
86
- if (this.options.isCloneClass) {
87
- this.cloneClasses = this.$select.className;
88
- }
89
- if (options.callbackInitialization) {
90
- this.options.callbackInitialization = options.callbackInitialization;
91
- }
92
- if (options.callbackInitialized) {
93
- this.options.callbackInitialized = options.callbackInitialized;
94
- }
95
- if (options.callbackOpen) {
96
- this.options.callbackOpen = options.callbackOpen;
97
- }
98
- if (options.callbackClose) {
99
- this.options.callbackClose = options.callbackClose;
100
- }
101
- if (options.callbackDestroyInit) {
102
- this.options.callbackDestroyInit = options.callbackDestroyInit;
103
- }
104
- if (options.callbackDestroy) {
105
- this.options.callbackDestroy = options.callbackDestroy;
106
- }
107
- if (options.callbackChangeSelect) {
108
- this.options.callbackChangeSelect = options.callbackChangeSelect;
109
- }
110
- if (options.changeBodyLi) {
111
- this.options.changeBodyLi = options.changeBodyLi;
112
- }
113
-
114
- const dataConfirm = this.$select.dataset[toCamelCase('simple-is-confirm')];
115
- if (this.isMulti && dataConfirm) {
116
- this.options.isConfirmInMulti = dataConfirm === '1' || dataConfirm === 'true';
117
- }
118
-
119
- this.optionOverride();
120
-
121
- this.isDisabled = this.$select.disabled;
122
-
123
- // this.initDom()
124
- }
125
-
126
- optionOverride() {
127
- const dataPlaceholder = toCamelCase('simple-placeholder');
128
- if (this.$select.dataset[dataPlaceholder]) {
129
- this.titlePlaceholder = this.$select.dataset[dataPlaceholder] || '';
130
- } else {
131
- this.titlePlaceholder = this.options.locale.title;
132
- }
133
-
134
- const dataResetAll = toCamelCase('simple-reset-all');
135
- if (dataResetAll in this.$select.dataset) {
136
- const resReset = this.$select.dataset[dataResetAll];
137
- this.options.resetAll = !(resReset === 'false' || resReset === '0');
138
- }
139
- // const dataSelect = toCamelCase('simple-select-all');
140
- // if (dataSelect in this.$select.dataset) {
141
- // const resSelect = this.$select.dataset[dataSelect];
142
- // this.options.selectAll = !(resSelect === 'false' || resSelect === '0');
143
- // }
144
- if (this.$select.hasAttribute('data-simple-select-all')) {
145
- const resSelect = this.$select.getAttribute('data-simple-select-all');
146
- this.options.selectAll = ifTrueDataAttr(resSelect);
147
- }
148
-
149
- const isShowCheckboxLocal = this.$select.dataset[toCamelCase('simple-show-checkbox')];
150
- if (this.isMulti) {
151
- this.isShowCheckbox = !((isShowCheckboxLocal && !ifTrueDataAttr(isShowCheckboxLocal)));
152
- } else if (isShowCheckboxLocal === 'true') {
153
- this.isShowCheckbox = true;
154
- }
155
-
156
- const itemHtmlBefore = this.$select.dataset[toCamelCase('simple-item-html-before')];
157
- if (itemHtmlBefore) {
158
- this.bodyLiHTMLBeforeFromSelect = itemHtmlBefore;
159
- }
160
- const itemHtmlAfter = this.$select.dataset[toCamelCase('simple-item-html-after')];
161
- if (itemHtmlAfter) {
162
- this.bodyLiHTMLAfterFromSelect = itemHtmlAfter;
163
- }
164
-
165
- if (this.$select.hasAttribute('data-simple-up')) {
166
- this.options.isUp = ifTrueDataAttr(this.$select.getAttribute('data-simple-up'));
167
- }
168
- }
169
-
170
- initDom() {
171
- this.createList(false);
172
- this.createHTML();
173
-
174
- this.state.subscribe('items', (_val: IOptionItems[]) => {
175
- this.createListHTML();
176
- this.createTitleHTML();
177
- });
178
- this.state.subscribe('isOpen', (val: boolean) => {
179
- this.createListHTML();
180
- this.createTitleHTML();
181
-
182
- this.toggleTabIndex(val);
183
- });
184
- this.state.subscribe('isFloat', (val: boolean) => {
185
- this.isFloatWidth = val;
186
- const cls = getClass('float', true);
187
- this.elemWrap.classList.toggle(cls, val);
188
- });
189
- }
190
-
191
- toggleTabIndex(isOpen: boolean) {
192
- const tabIndex = isOpen ? 0 : -1;
193
-
194
- if (this.state.getState('isFloat')) {
195
- document.body.classList.toggle(this.bodyOpenClass, isOpen);
196
- }
197
-
198
- if (this.elemInputSearch) {
199
- this.elemInputSearch.tabIndex = tabIndex;
200
- }
201
- if (this.elemResetAll) {
202
- this.elemResetAll.tabIndex = tabIndex;
203
- }
204
- if (this.elemSelectAll) {
205
- this.elemSelectAll.tabIndex = tabIndex;
206
- }
207
- if (this.confirmOk) {
208
- this.confirmOk.tabIndex = tabIndex;
209
- }
210
- if (this.confirmNo) {
211
- this.confirmNo.tabIndex = tabIndex;
212
- }
213
- }
214
-
215
- public updateHTML() {
216
- this.createList(true);
217
- }
218
-
219
- private createHTML() {
220
- this.$select.classList.add(this.classSelectInit);
221
- this.$select.tabIndex = -1;
222
-
223
- this.elemTopBody.className = getClass('top_body');
224
- this.elemTopBody.tabIndex = this.isDisabled ? -1 : 0;
225
-
226
- this.createIcon();
227
- this.elemTop.append(this.elemTopBody);
228
- let resClassesWrap = initClass;
229
- if (this.options.isCloneClass) {
230
- resClassesWrap += ` ${this.cloneClasses}`;
231
- }
232
- if (this.$select.hasAttribute('data-simple-add-classes')) {
233
- resClassesWrap += ` ${this.$select.getAttribute('data-simple-add-classes')}`;
234
- }
235
- if (this.isDisabled) {
236
- resClassesWrap += ` ${getClass('disabled', true)}`;
237
- }
238
- if (this.options.isUp) {
239
- resClassesWrap += ` ${getClass('up', true)}`;
240
- }
241
- resClassesWrap += ` ${this.isMulti ? getClass('multi', true) : getClass('single', true)}`;
242
- this.elemWrap.className = resClassesWrap;
243
- this.elemWrap.dataset.countAll = this.$select.options.length.toString();
244
-
245
- this.elemTop.className = getClass('top');
246
-
247
- // creating an initial structure
248
- const parentElement = this.$select.parentNode;
249
- if (parentElement) {
250
- parentElement.replaceChild(this.elemWrap, this.$select);
251
- this.elemWrap.appendChild(this.$select);
252
- }
253
- this.elemWrap.append(this.elemTop);
254
-
255
- if (this.isNative) {
256
- this.$select.classList.add(getClass('native', true, this.classSelectInit));
257
- this.elemWrap.classList.add(getClass('native', true));
258
- } else {
259
- this.createDropDown();
260
-
261
- this.createControlHTML();
262
-
263
- this.createInputHTML();
264
- }
265
-
266
- this.createTitleHTML();
267
- }
268
-
269
- private createControlHTML() {
270
- if (!this.elemDropDown || !this.isMulti) {
271
- return;
272
- }
273
- if (!this.options.selectAll && !this.options.resetAll) {
274
- return;
275
- }
276
- this.elemControl = document.createElement('div');
277
- this.elemControl.classList.add(getClass('controls'));
278
-
279
- this.elemDropDown.prepend(this.elemControl);
280
-
281
- const classControl = getClass('control');
282
- if (this.options.selectAll) {
283
- this.elemSelectAll = createButton();
284
- this.elemSelectAll.className = `${classControl} ${getClass('select_all', true, classControl)}`;
285
-
286
- this.elemSelectAll.innerHTML = `<span class="${getClass('select_all__icon')}"></span> ${this.options.locale.selectAll}`;
287
-
288
- this.elemControl.append(this.elemSelectAll);
289
- }
290
-
291
- if (this.options.resetAll) {
292
- this.elemResetAll = createButton();
293
- this.elemResetAll.className = `${classControl} ${getClass('reset_all', true, classControl)}`;
294
-
295
- this.elemResetAll.innerHTML = `<span class="${getClass('reset_all__icon')}"></span> ${this.options.locale.resetAll}`;
296
-
297
- this.elemControl.append(this.elemResetAll);
298
- }
299
- }
300
-
301
- private createIcon() {
302
- const icon = document.createElement('span');
303
- icon.className = getClass('icon');
304
- this.elemTopBody.append(icon);
305
- }
306
-
307
- private createDropDown() {
308
- if (this.isNative) {
309
- return;
310
- }
311
- this.elemDropDown = document.createElement('div');
312
- this.elemDropDown.className = getClass('body');
313
- this.elemListBody = document.createElement('ul');
314
-
315
- this.elemListBody.className = getClass('list');
316
-
317
- this.elemWrap.append(this.elemDropDown);
318
- this.elemDropDown.append(this.elemListBody);
319
-
320
- this.elemDropDownClose = createButton();
321
- this.elemDropDownClose.classList.add(getClass('close'));
322
-
323
- this.elemDropDown.append(this.elemDropDownClose);
324
-
325
- if (this.isMulti) {
326
- this.createIsConfirmInMultiHTML();
327
- }
328
- this.handlerChangeChecked();
329
- }
330
-
331
- private createIsConfirmInMultiHTML() {
332
- const confirm = document.createElement('div');
333
-
334
- const classesItem = getClass('bottom_control');
335
- this.confirmOk = createButton();
336
- this.confirmNo = createButton();
337
- confirm.append(this.confirmOk);
338
- confirm.append(this.confirmNo);
339
-
340
- this.confirmOk.innerHTML = this.options.locale.ok;
341
- this.confirmNo.innerHTML = this.options.locale.cansel;
342
-
343
- this.confirmOk.className = `${classesItem} ${getClass('ok', true, classesItem)}`;
344
- this.confirmNo.className = `${classesItem} ${getClass('no', true, classesItem)}`;
345
-
346
- let classes = getClass('bottom_controls');
347
- if (!this.options.isConfirmInMulti) {
348
- classes += ` ${getClass('hide', true, classes)}`;
349
- }
350
- confirm.className = classes;
351
-
352
- this.elemDropDown?.append(confirm);
353
- }
354
-
355
- private createTitleHTML() {
356
- if (!this.elemTitle) {
357
- this.elemTitle = document.createElement('div');
358
- this.elemTitle.className = getClass('title');
359
- this.elemTopBody.prepend(this.elemTitle);
360
- }
361
-
362
- const itemsChecked = this.getChecked();
363
-
364
- this.elemTop.title = '';
365
- const isPlaceholder = !itemsChecked.length;
366
- let title:string = this.titlePlaceholder;
367
- if (itemsChecked.length) {
368
- let attrTitle = '';
369
- itemsChecked.forEach((item, index) => {
370
- if (index !== 0) {
371
- attrTitle += `${this.options.sepChars}<span class="${getClass('sep_space', true)}">&nbsp;</span>`;
372
- }
373
- attrTitle += `${item.title}`;
374
- });
375
- this.elemTop.title = attrTitle;
376
-
377
- let maxShow = this.options.countShowSelected;
378
- const maxShowAttr = Number(this.$select.dataset.simpleCountShowsSelected);
379
- if (maxShowAttr && maxShowAttr > 0) {
380
- maxShow = maxShowAttr;
381
- }
382
- if (itemsChecked.length > maxShow) {
383
- title = `${this.options.locale.selected} ${itemsChecked.length}`;
384
-
385
- if (this.$select.querySelectorAll('option').length === itemsChecked.length) {
386
- title += ` (${this.options.locale.all})`;
387
- }
388
- } else if (attrTitle) {
389
- title = attrTitle;
390
- }
391
- }
392
-
393
- this.elemTitle.innerHTML = title;
394
- this.elemTitle.classList.toggle('SimpleSel__title--placeholder', isPlaceholder);
395
- this.elemTitle.classList.toggle('SimpleSel__title--fill', !isPlaceholder);
396
-
397
- this.elemWrap.classList.toggle(getClass('fill', true), !isPlaceholder);
398
- }
399
-
400
- protected createListHTML() {
401
- if (!this.elemListBody) {
402
- return;
403
- }
404
- let resBodyList = '';
405
- let countShowItem = 0;
406
- let countCheckedItems = 0;
407
- let countCheckedFullItems = 0;
408
-
409
- // this.items.forEach(group => {
410
- this.state.getState('items').forEach((group:IOptionItems) => {
411
- if (!group.isGroup) {
412
- const {
413
- result, countShow, countChecked, countCheckedFull,
414
- } = this.createLi(group);
415
- resBodyList += result;
416
- countShowItem += countShow;
417
- countCheckedItems += countChecked;
418
- countCheckedFullItems += countCheckedFull;
419
- } else {
420
- const {
421
- result, countShow, countChecked, countCheckedFull,
422
- } = this.createLi(group);
423
- resBodyList += `<div class="${getClass('group_items')}">`;
424
- resBodyList += result;
425
- resBodyList += '</div>';
426
-
427
- countCheckedItems += countChecked;
428
- countShowItem += countShow;
429
- countCheckedFullItems += countCheckedFull;
430
- }
431
- });
432
-
433
- const isSearch:string = this.state.getState('filterStr');
434
-
435
- if (isSearch && isSearch.length && countShowItem === 0) {
436
- resBodyList = `<div class="${getClass('no_match')}">`;
437
- resBodyList = `${this.options.locale.noSearch} "${isSearch}"`;
438
- resBodyList += '</div>';
439
- }
440
-
441
- this.elemWrap.dataset.countChecked = countCheckedItems.toString();
442
- this.elemWrap.dataset.countCheckedFull = countCheckedFullItems.toString();
443
- if (this.isMulti) {
444
- this.elemWrap.dataset.checkAllMulti = (this.$select.options.length === countCheckedItems) ? 'yes' : 'no';
445
- }
446
- this.elemListBody.innerHTML = resBodyList;
447
- }
448
-
449
- private createInputHTML(): void {
450
- let { isSearch } = this.options;
451
- let { isSearchInDropdown } = this.options;
452
- if ('simpleSelectSearch' in this.$select.dataset) {
453
- isSearch = this.$select.dataset.simpleSelectSearch !== 'false';
454
- }
455
- if ('simpleSelectSearchDropdown' in this.$select.dataset) {
456
- isSearchInDropdown = this.$select.dataset.simpleSelectSearchDropdown !== 'false';
457
- }
458
- if (!isSearch && !isSearchInDropdown) {
459
- return;
460
- }
461
- this.elemInputSearch = document.createElement('input');
462
- this.elemInputSearch.type = 'text';
463
- this.elemInputSearch.tabIndex = -1;
464
- this.elemInputSearch.autocomplete = 'off';
465
- this.elemInputSearch.ariaAutoComplete = 'none';
466
- this.elemInputSearch.inputMode = 'off';
467
- this.elemInputSearch.placeholder = this.options.locale.searchText;
468
- this.elemInputSearch.name = `${initClass}_name_${this.id}`;
469
-
470
- const className = getClass('search');
471
- if (isSearchInDropdown) {
472
- if (this.elemDropDown) {
473
- this.elemInputSearch.className = `${className} ${getClass('dropdown', true, className)}`;
474
- this.elemDropDown.prepend(this.elemInputSearch);
475
- }
476
- } else {
477
- this.elemInputSearch.className = `${className} ${getClass('top', true, className)}`;
478
- this.elemTop.append(this.elemInputSearch);
479
- }
480
-
481
- this.inputSearchHandler();
482
- }
483
-
484
- getChecked(): IOptionItem[] {
485
- const items: IOptionItems[] = this.state.getState('items');
486
- let res: IOptionItem[] = [];
487
-
488
- items.forEach((group) => {
489
- res = [
490
- ...res,
491
- ...group.items.filter((i) => i.checked),
492
- ];
493
- });
494
-
495
- return res;
496
- }
497
-
498
- private createLi(data: IOptionItems): ICreateLiReturn {
499
- let result = '';
500
- let countShow = 0;
501
- let countChecked = 0;
502
- let countCheckedFull = 0;
503
-
504
- if (!data.isShowFilter) {
505
- return {
506
- result, countShow, countChecked, countCheckedFull,
507
- };
508
- }
509
-
510
- if (data.isGroup) {
511
- result += `<label class="${getClass('group_title')}">${data.titleGroup}</label>`;
512
- result += `<ul class="${getClass('group')}">`;
513
- }
514
- data.items.forEach((option) => {
515
- if (!option.isShowFilter) {
516
- return;
517
- }
518
- countShow++;
519
- const classLiInit = getClass('list_item');
520
- let classLi = classLiInit;
521
- if (option.checked) {
522
- countChecked++;
523
- classLi += ` ${getClass('checked', true, classLiInit)}`;
524
-
525
- if (option.value) {
526
- countCheckedFull++;
527
- }
528
- }
529
- if (option.disabled) {
530
- classLi += ` ${getClass('disabled', true, classLiInit)}`;
531
- }
532
- if (!option.value) {
533
- classLi += ` ${getClass('not_value', true, classLiInit)}`;
534
- }
535
-
536
- let dataAttr = `data-sel-group-id="${data.idGroup}"`;
537
- dataAttr += ` data-sel-position="${option.position}"`;
538
- dataAttr += ` data-sel-id="${option.id}"`;
539
-
540
- if (option.value) {
541
- dataAttr += ` data-sel-value="${option.value}"`;
542
- }
543
-
544
- dataAttr += ' data-sel-opt-item';
545
- dataAttr += ` data-sel-opt-checked="${option.checked}"`;
546
- dataAttr += ` data-sel-opt-disabled="${option.disabled}"`;
547
-
548
- result += `<li class="${classLi}" ${dataAttr}>`;
549
- const createLiBodyRes = this.createLiBody(option, this.$select.options[option.position]);
550
- result += typeof createLiBodyRes === 'string' ? createLiBodyRes : createLiBodyRes.outerHTML;
551
- result += '</li>';
552
- });
553
- if (data.isGroup) {
554
- result += '</ul>';
555
- }
556
- return {
557
- result, countShow, countChecked, countCheckedFull,
558
- };
559
- }
560
-
561
- private createLiBody(option: IOptionItem, optionNative: HTMLOptionElement): HTMLElement | string {
562
- const item = document.createElement('div');
563
-
564
- item.className = getClass('list_item_body');
565
-
566
- let res:string = '';
567
- if (this.isShowCheckbox) {
568
- res = `<span class="${getClass('list_item_icon')}"></span>`;
569
- }
570
-
571
- if (this.bodyLiHTMLBeforeFromSelect) {
572
- res += this.bodyLiHTMLBeforeFromSelect;
573
- }
574
-
575
- if (optionNative.hasAttribute('data-simple-html-before')) {
576
- res += optionNative.getAttribute('data-simple-html-before');
577
- }
578
-
579
- res += `${option.title}`;
580
- if (this.bodyLiHTMLAfterFromSelect) {
581
- res += this.bodyLiHTMLAfterFromSelect;
582
- }
583
- if (optionNative.hasAttribute('data-simple-html-after')) {
584
- res += optionNative.getAttribute('data-simple-html-after');
585
- }
586
-
587
- item.innerHTML = res;
588
-
589
- if (this.options.changeBodyLi) {
590
- return this.options.changeBodyLi(item, optionNative);
591
- }
592
- return item;
593
- }
594
-
595
- protected handlerChangeChecked() {
596
- console.error('This method need redefine');
597
- }
598
-
599
- // only desktop
600
- protected createList(_isCompare: boolean) {
601
- console.error('This method need redefine');
602
- }
603
-
604
- // only desktop
605
- protected inputSearchHandler() {
606
- console.error('This method need redefine');
607
- }
608
- }