@cntwg/html-helper 0.0.27 → 0.1.0

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,1108 +0,0 @@
1
- // [v0.1.072-20250827]
2
-
3
- // === module init block ===
4
-
5
- const {
6
- readAsNumber,
7
- isPlainObject,
8
- //TItemsListEx,
9
- } = require('@ygracs/bsfoc-lib-js');
10
-
11
- const {
12
- TItemsListEx,
13
- } = require('@ygracs/lists-lib-js');
14
-
15
- const {
16
- isHTMLElement, isSelectedHTMLElement, isHiddenHTMLElement,
17
- showHTMLElement, hideHTMLElement,
18
- selectHTMLElement, unselectHTMLElement,
19
- markHTMLElementAsCurrent, unmarkCurrentHTMLElement,
20
- readAsAttrValue, valueToClassList,
21
- CSS_CLASS_STRING,
22
- } = require('../html-helper-lib.js');
23
-
24
- const { THtmlStubItemsSet } = require('./lists-stubs.js');
25
-
26
- const {
27
- pushEventHandler, triggerEventHandler,
28
- } = require('./mod-hfunc.js');
29
-
30
- const {
31
- //CSS_CLASS_SELECTED,
32
- CSS_CLASS_DISABLED,
33
- //CSS_CLASS_HIDDEN,
34
- //CSS_CLASS_CURRENT,
35
- //CSS_CLASS_ACTIVE,
36
- } = CSS_CLASS_STRING;
37
-
38
- // === module inner block ===
39
-
40
- /**
41
- * Searches an element in a list by a given attributes of that element.
42
- * @function srchListElementByAttr
43
- * @param {TItemsListEx} list
44
- * @param {string} name - target attribute name
45
- * @param {string} [value=""] - target attribute value
46
- * @returns {object}
47
- * @inner
48
- */
49
- function srchListElementByAttr(list, name, value = '') {
50
- const _value = readAsAttrValue(value);
51
- let item = null;
52
- let index = -1;
53
- let count = list.count;
54
- let isACCEPTED = false;
55
- if (count > 0 && name !== '' && _value !== null) {
56
- const acceptAnyVal = _value === '';
57
- for (let i = 0; i < count; i++) {
58
- item = list.getItem(i);
59
- if (
60
- isHTMLElement(item)
61
- && item.hasAttribute(name)
62
- && (acceptAnyVal || item.getAttribute(name) === value)
63
- ) {
64
- index = i;
65
- isACCEPTED = true;
66
- break;
67
- };
68
- };
69
- };
70
- return isACCEPTED ? { index, item } : { index, item: null };
71
- };
72
-
73
- // === module main block ===
74
-
75
- /***
76
- * (* constant definitions *)
77
- */
78
-
79
- const ILC_SMODE_DEF = 0;
80
- const ILC_SMODE_SHFT = 1;
81
- const ILC_SMODE_CTRL = 2;
82
-
83
- /***
84
- * (* function definitions *)
85
- */
86
-
87
- /***
88
- * (* class definitions *)
89
- */
90
-
91
- /**
92
- * An options set for `THtmlItemsListContainer`-class
93
- * @typedef {Object} OPT_hlconsett
94
- * @property {boolean} [autoHideNewItems=false] - indicates whether to hide a new element
95
- * @property {boolean} [markCurrentItem=false] - indicates whether to mark a current element
96
- * @property {(string|string[])} [itemBaseClassID]
97
- */
98
-
99
- /**
100
- * @classdesc This class implements an interfaco for a list container
101
- */
102
- class THtmlItemsListContainer {
103
- /** @type {?HTMLElement} */
104
- #_host;
105
- /** @type {TItemsListEx} */
106
- #_items;
107
- /** @type {OPT_hlconsett} */
108
- #_options;// = null;
109
- /**
110
- * A container status
111
- * @typedef {Object} statILCont
112
- * @property {number} curIndex
113
- * @property {?HTMLElement} curItem
114
- * @inner
115
- */
116
- /** @type {statILCont} */
117
- #_status;
118
-
119
- /**
120
- * Creates an instance of a list container
121
- * @param {HTMLElement} host - host element
122
- * @param {OPT_hlconsett} [opt] - options
123
- */
124
- constructor(host, opt) {
125
- // check host
126
- this.#_host = isHTMLElement(host) ? host : null;
127
- // init items container
128
- const _items = new TItemsListEx();
129
- this.#_items = _items;
130
- // load options
131
- /** @type {OPT_hlconsett} */
132
- const _options = isPlainObject(opt) ? opt : {};
133
- let {
134
- autoHideNewItems,
135
- markCurrentItem,
136
- itemBaseClassID,
137
- } = _options;
138
- if (typeof autoHideNewItems !== 'boolean') {
139
- _options.autoHideNewItems = autoHideNewItems = false;
140
- };
141
- if (typeof markCurrentItem !== 'boolean') {
142
- _options.markCurrentItem = markCurrentItem = false;
143
- };
144
- itemBaseClassID = valueToClassList(itemBaseClassID, true);
145
- _options.itemBaseClassID = itemBaseClassID;
146
- // init status
147
- /** @type {statILCont} */
148
- const _status = {
149
- curIndex: _items.curIndex,
150
- curItem: _items.curItem,
151
- };
152
- this.#_status = _status;
153
- // save options
154
- this.#_options = _options;
155
- }
156
-
157
- /**
158
- * Contains a Qty of an elements
159
- * @type {number}
160
- * @readonly
161
- */
162
- get count() {
163
- return this.#_items.count;
164
- }
165
-
166
- /**
167
- * Contains an of the current element
168
- * @type {number}
169
- * @readonly
170
- */
171
- get curIndex() {
172
- return this.#_items.curIndex;
173
- }
174
-
175
- /**
176
- * Returns a minimum value of an index
177
- * @type {number}
178
- * @readonly
179
- */
180
- get minIndex() {
181
- return this.#_items.minIndex;
182
- }
183
-
184
- /**
185
- * Returns a maximum value of an index
186
- * @type {number}
187
- * @readonly
188
- */
189
- get maxIndex() {
190
- return this.#_items.maxIndex;
191
- }
192
-
193
- /**
194
- * Returns a value of a previous index
195
- * @type {number}
196
- * @readonly
197
- */
198
- get prevIndex() {
199
- return this.#_items.prevIndex;
200
- }
201
-
202
- /**
203
- * Returns a value of a next index
204
- * @type {number}
205
- * @readonly
206
- */
207
- get nextIndex() {
208
- return this.#_items.nextIndex;
209
- }
210
-
211
- /**
212
- * Returns an element in the current index
213
- * @type {?HTMLElement}
214
- * @readonly
215
- */
216
- get curItem() {
217
- const item = this.#_items.curItem;
218
- return isHTMLElement(item) ? item : null;
219
- }
220
-
221
- /**
222
- * Clears an instance content.
223
- * @returns {void}
224
- */
225
- clear() {
226
- this.#_items.clear();
227
- const _status = this.#_status;
228
- _status.curIndex = this.curIndex;
229
- _status.curItem = this.curItem;
230
- const _host = this.#_host;
231
- if (_host) _host.innerHTML = '';
232
- }
233
-
234
- /**
235
- * Checks if an instance contains no items.
236
- * @returns {boolean}
237
- */
238
- isEmpty() {
239
- return this.#_items.isEmpty();
240
- }
241
-
242
- /**
243
- * Checks if an instance contains any items.
244
- * @returns {boolean}
245
- */
246
- isNotEmpty() {
247
- return this.#_items.isNotEmpty();
248
- }
249
-
250
- /**
251
- * Checks if a given value is a valid index and
252
- * it fits an index range within an instance.
253
- * @param {any} value - index value
254
- * @returns {boolean}
255
- */
256
- chkIndex(value) {
257
- return this.#_items.chkIndex(value);
258
- }
259
-
260
- /**
261
- * Checks if a given value is a valid index and
262
- * it fits an index range within an instance.
263
- * @param {any} value - value to check
264
- * @param {boolean} [opt=false] - defines a type of result
265
- * @returns {(boolean|number)}
266
- * @see TItemsListEx.chkIndexEx
267
- */
268
- chkIndexEx(...args) {
269
- return this.#_items.chkIndexEx(...args);
270
- }
271
-
272
- /**
273
- * Returns an index of a given element.
274
- * @param {HTMLElement} item - element to search
275
- * @returns {number}
276
- */
277
- srchIndex(item) {
278
- return isHTMLElement(item) ? this.#_items.srchIndex(item) : -1;
279
- }
280
-
281
- /**
282
- * Returns an index of an element wich has an attribute
283
- * with a given name and value.
284
- * @param {string} name - attribute name
285
- * @param {any} [value=""] - attribute value
286
- * @returns {number}
287
- */
288
- srchIndexByAttr(name, value = '') {
289
- const _name = typeof name === 'string' ? name.trim() : '';
290
- const { index } = srchListElementByAttr(this.#_items, _name, value);
291
- return index;
292
- };
293
-
294
- /**
295
- * Returns an index of an element wich has a given ID.
296
- * @param {string} value - element ID
297
- * @returns {number}
298
- */
299
- srchIndexByID(value) {
300
- const { index } = srchListElementByAttr(this.#_items, 'id', value);
301
- return index;
302
- };
303
-
304
- /**
305
- * Sets a current index.
306
- * @param {(number|string)} index - element index
307
- * @returns {boolean}
308
- */
309
- setCurIndex(index) {
310
- const isSUCCEED = this.#_items.setIndex(index);
311
- if (isSUCCEED) {
312
- const markCurrentItem = this.#_options.markCurrentItem;
313
- const _status = this.#_status;
314
- if (markCurrentItem && _status.curIndex !== -1) {
315
- unmarkCurrentHTMLElement(_status.curItem);
316
- };
317
- const item = this.getItem(index);
318
- _status.curIndex = Number(index);
319
- _status.curItem = item;
320
- if (markCurrentItem) markHTMLElementAsCurrent(item);
321
- };
322
- return isSUCCEED;
323
- }
324
-
325
- /**
326
- * Resets a current index.
327
- * @returns {void}
328
- */
329
- rstCurIndex() {
330
- const _status = this.#_status;
331
- if (this.#_options.markCurrentItem && _status.curIndex !== -1) {
332
- unmarkCurrentHTMLElement(_status.curItem);
333
- };
334
- this.#_items.rstIndex();
335
- _status.curIndex = this.#_items.curIndex;
336
- _status.curItem = this.curItem;
337
- }
338
-
339
- /**
340
- * Returns an item addressed by a given index.
341
- * @param {(number|string)} index - element index
342
- * @returns {?HTMLElement}
343
- */
344
- getItem(index) {
345
- const item = this.#_items.getItem(index);
346
- return isHTMLElement(item) ? item : null;
347
- }
348
-
349
- /**
350
- * Returns an item wich has an attribute with a given name and value.
351
- * @param {string} name - attribute name
352
- * @param {any} [value=""] - attribute value
353
- * @returns {?HTMLElement}
354
- */
355
- getItemByAttr(name, value = '') {
356
- const _name = typeof name === 'string' ? name.trim() : '';
357
- const { item } = srchListElementByAttr(this.#_items, _name, value);
358
- return item;
359
- }
360
-
361
- /**
362
- * Returns an item wich has a given ID.
363
- * @param {string} value - element ID
364
- * @returns {?HTMLElement}
365
- */
366
- getItemByID(value) {
367
- const { item } = srchListElementByAttr(this.#_items, 'id', value);
368
- return item;
369
- }
370
-
371
- /**
372
- * Adds an item to an instance.
373
- * @param {HTMLElement} item
374
- * @param {boolean} [opt=false]
375
- * @returns {number}
376
- */
377
- addItem(item, opt) {
378
- const forceCI = typeof opt === 'boolean' ? opt : false;
379
- const index = (
380
- isHTMLElement(item)
381
- ? this.#_items.addItemEx(item, false)
382
- : -1
383
- );
384
- const isSUCCEED = index !== -1;
385
- if (isSUCCEED) {
386
- const { autoHideNewItems, itemBaseClassID } = this.#_options;
387
- if (autoHideNewItems) hideHTMLElement(item);
388
- if (itemBaseClassID.length > 0) item.classList.add(...itemBaseClassID);
389
- if (forceCI) this.setCurIndex(index);
390
- const _host = this.#_host;
391
- if (_host) _host.append(item);
392
- };
393
- return isSUCCEED ? index : -1;
394
- }
395
-
396
- /**
397
- * Deletes an item from an instance.
398
- * @param {(number|string)} index - element index
399
- * @param {any} [opt]
400
- * @param {boolean} [optEx=true]
401
- * @returns {boolean}
402
- */
403
- delItem(index, opt, optEx) {
404
- const _items = this.#_items;
405
- const item = _items.delItemEx(index, opt);
406
- const isSUCCEED = item !== undefined;
407
- if (isSUCCEED) {
408
- if (this.#_host && isHTMLElement(item)) item.remove();
409
- const doProcEx = typeof optEx === 'boolean' ? optEx : true;
410
- if (doProcEx) {
411
- const index = _items.curIndex;
412
- if (index === -1) {
413
- this.rstCurIndex();
414
- } else {
415
- this.setCurIndex(index);
416
- };
417
- };
418
- };
419
- return isSUCCEED;
420
- }
421
-
422
- /**
423
- * Selects an item addressed by a given index.
424
- * @param {(number|string)} index - element index
425
- * @param {boolean} [opt=false]
426
- * @returns {boolean}
427
- */
428
- selectItem(index, opt) {
429
- const forceCI = typeof opt === 'boolean' ? opt : false;
430
- const isSUCCEED = selectHTMLElement(this.#_items.getItem(index));
431
- if (isSUCCEED && forceCI) this.setCurIndex(index);
432
- return isSUCCEED;
433
- }
434
-
435
- /**
436
- * Unselects an item addressed by a given index.
437
- * @param {(number|string)} index - element index
438
- * @returns {boolean}
439
- */
440
- unselectItem(index) {
441
- return unselectHTMLElement(this.#_items.getItem(index));
442
- }
443
-
444
- /**
445
- * Hides an item addressed by a given index.
446
- * @param {(number|string)} index - element index
447
- * @returns {boolean}
448
- */
449
- hideItem(index) {
450
- return hideHTMLElement(this.#_items.getItem(index));
451
- }
452
-
453
- /**
454
- * Shows an item addressed by a given index.
455
- * @param {(number|string)} index - element index
456
- * @returns {boolean}
457
- */
458
- showItem(index) {
459
- return showHTMLElement(this.#_items.getItem(index));
460
- }
461
-
462
- /**
463
- * Checks whether an item is selected.
464
- * @param {(number|string)} index - element index
465
- * @returns {boolean}
466
- */
467
- isSelectedItem(index) {
468
- return isSelectedHTMLElement(this.#_items.getItem(index));
469
- }
470
-
471
- /**
472
- * Checks whether an item is hidden.
473
- * @param {(number|string)} index - element index
474
- * @returns {boolean}
475
- */
476
- isHiddenItem(index) {
477
- return isHiddenHTMLElement(this.#_items.getItem(index));
478
- }
479
-
480
- };
481
-
482
- /**
483
- * @typedef {Object} OPT_hlctlsett
484
- * @property {boolean} [autoHideNewItems=false] - indicates whether to hide a new element
485
- * @property {boolean} [markCurrentItem=false] - indicates whether to mark a current element
486
- * @property {(string|string[])} [itemBaseClassID]
487
- * @property {boolean} [showStubsIfEmpty=false] - indicates whether to show stubs if empty
488
- * @property {boolean} [allowGroupSelection=false] - indicates whether
489
- * a selection of the elements group is allowed
490
- * @property {boolean} [allowSelectionLocks=false] - indicates whether locking
491
- * of an element selection is allowed
492
- * @property {OPT_stubconsett} [stubs]
493
- * @description An options set for `THtmlItemsListController`-class
494
- */
495
-
496
- /**
497
- * @augments THtmlItemsListContainer
498
- * @classdesc This class enhanced a capabilities implemented
499
- * in the `THtmlItemsListContainer` class
500
- */
501
- class THtmlItemsListController extends THtmlItemsListContainer {
502
- /** @type {?HTMLElement} */
503
- #_host;
504
- /** @type {THtmlStubItemsSet} */
505
- #_stubs;
506
- /** @type {Set<HTMLElement>} */
507
- #_selects;
508
- /** @property {OPT_hlctlsett} */
509
- #_options;// = null;
510
- /**
511
- * A controler status
512
- * @typedef {Object} statILCtrl
513
- * @property {boolean} isSelectionLocked
514
- * @property {boolean} isStubItemShown
515
- * @property {boolean} isHostEnabled
516
- * @property {boolean} catchEventOnHost
517
- * @property {boolean} execDelItem
518
- * @property {number} execDelItemDI
519
- * @property {number} execDelItemCI
520
- * @inner
521
- */
522
- /** @property {statILCtrl} */
523
- #_status;
524
- /** @property {Map<string, Function>} */
525
- #_events;
526
-
527
- /**
528
- * Creates a new instance of the class.
529
- * @param {?HTMLElement} host - host element
530
- * @param {OPT_hlctlsett} [opt] - options
531
- */
532
- constructor(host, opt) {
533
- // check host element
534
- const isHostEnabled = isHTMLElement(host);
535
- const _host = isHostEnabled ? host : null;
536
- // check options
537
- /** @type {OPT_hlctlsett} */
538
- const _options = isPlainObject(opt) ? opt : {};
539
- // call parent constructor
540
- super(_host, _options);
541
- // load options
542
- let {
543
- //autoHideNewItems, // [*] processed by parent
544
- //markCurrentItem, // [*] processed by parent
545
- //itemBaseClassID, // [*] processed by parent
546
- showStubsIfEmpty,
547
- allowGroupSelection,
548
- allowSelectionLocks,
549
- stubs: stubsList,
550
- } = _options;
551
- if (typeof showStubsIfEmpty !== 'boolean') {
552
- _options.showStubsIfEmpty = showStubsIfEmpty = false;
553
- };
554
- if (typeof allowGroupSelection !== 'boolean') {
555
- _options.allowGroupSelection = allowGroupSelection = false;
556
- };
557
- if (typeof allowSelectionLocks !== 'boolean') {
558
- _options.allowSelectionLocks = allowSelectionLocks = false;
559
- };
560
- // save options
561
- this.#_options = _options;
562
- // init status flags set
563
- /** @type {statILCtrl} */
564
- const _status = {
565
- isSelectionLocked: false,
566
- isStubItemShown: false,
567
- isHostEnabled: isHostEnabled,
568
- catchEventOnHost: false,
569
- execDelItem: false,
570
- execDelItemDI: -1,
571
- execDelItemCI: -1,
572
- }
573
- // bind ref to host
574
- this.#_host = _host;
575
- // init stub items set
576
- this.#_stubs = new THtmlStubItemsSet(_host, stubsList);
577
- // init index of selected items
578
- this.#_selects = new Set();
579
- // init events controller
580
- this.#_events = new Map();
581
- if (isHostEnabled) {
582
- // set on_click()-event controler
583
- /**/// use bubblig stage
584
- _host.addEventListener('click', this.#_on_will_select_item);
585
- _status.catchEventOnHost = true;
586
- };
587
- // save status flags set
588
- this.#_status = _status;
589
- }
590
-
591
- /**
592
- * @param {object} e - event
593
- * @returns {void}
594
- * @private
595
- */
596
- #_on_will_select_item = (e) => {
597
- //console.log('THtmlItemsListController._on_will_select_item() ==> was called...');
598
- //e.preventDefault(); /* need to reconsider reason for use */
599
- const {
600
- eventPhase,
601
- target,
602
- currentTarget,
603
- ctrlKey,
604
- shiftKey,
605
- } = e;
606
- //console.log('CHECK: e => ditail:['+e.detail+']');
607
- //console.log('CHECK: e => phase:['+eventPhase+']');
608
- const onClickNum = readAsNumber(e.detail, 0);
609
- const host = this.#_host;
610
- const { isSelectionLocked, catchEventOnHost } = this.#_status;
611
- let curItem = null;
612
- switch (eventPhase) {
613
- //* // NOTE: currently on eventPhase = 2 and 3
614
- //*case 1:
615
- /**/// capturing stage
616
- case 2: {
617
- /**/// target stage
618
- if (target !== host) curItem = target;
619
- break;
620
- }
621
- case 3: {
622
- /**/// bubblig stage
623
- curItem = catchEventOnHost ? target : currentTarget;
624
- break;
625
- }
626
- default: {}
627
- };
628
- if (
629
- !isSelectionLocked
630
- && curItem instanceof HTMLElement
631
- && (onClickNum === 0 || onClickNum === 1)
632
- && !curItem.classList.contains(CSS_CLASS_DISABLED)
633
- ) {
634
- //console.log(`CHECK: e => tag:[${curItem.tagName}]`);
635
- if (host) {
636
- // find an actual list element in case when some inner element was clicking
637
- let tmpItem = curItem;
638
- while (tmpItem) {
639
- //console.log(`CHECK: e => target.tag:[${tmpItem.tagName}]`);
640
- tmpItem = tmpItem.parentElement;
641
- //console.log(`CHECK: e => next.tag:[${tmpItem.tagName}]`);
642
- if (tmpItem === host) break;
643
- if (tmpItem) curItem = tmpItem;
644
- };
645
- };
646
- //console.log(`CHECK: e => tag:[${curItem.tagName}]`);
647
- this.#_selectItemEx(curItem, {
648
- ctrlKey: ctrlKey,
649
- shiftKey: shiftKey,
650
- forceCI: true,
651
- });
652
- };
653
- };
654
-
655
- /**
656
- * @param {string} name - event name
657
- * @param {...any} args
658
- * @returns {void}
659
- * @private
660
- * @see triggerEventHandler
661
- */
662
- #_triggerEvent = (name, ...args) => {
663
- triggerEventHandler(this.#_events, name, ...args);
664
- };
665
-
666
- /**
667
- * Returns a list of the selected elements
668
- * @type {HTMLElement[]}
669
- * @readonly
670
- */
671
- get SelectedItems() {
672
- const _selects = this.#_selects;
673
- // // TODO: consider to use: `[...<value>]`
674
- let result = [];
675
- for (let item of _selects) {
676
- if (item) result.push(item);
677
- };
678
- return result;
679
- }
680
-
681
- /**
682
- * Returns a `THtmlStubItemsSet` instance
683
- * @type {THtmlStubItemsSet}
684
- * @readonly
685
- */
686
- get StubItems() {
687
- return this.#_stubs;
688
- }
689
-
690
- /**
691
- * Indicates whether a selection is locked
692
- * @type {boolean}
693
- * @readonly
694
- */
695
- get isSelectionLocked() {
696
- return this.#_status.isSelectionLocked;
697
- }
698
-
699
- /**
700
- * Clears an instance content.
701
- * @returns {void}
702
- * @fires THtmlItemsListController#list-clear
703
- */
704
- clear() {
705
- // // TODO: clear event handler on elements if set
706
- super.clear();
707
- this.#_selects.clear();
708
- if (this.#_options.showStubsIfEmpty) {
709
- // show default stub-item
710
- this.#_status.isStubItemShown = this.#_stubs.showDefItem();
711
- };
712
- /**
713
- * @event THtmlItemsListController#list-clear
714
- * @type {void}
715
- */
716
- this.#_triggerEvent('list-clear');
717
- }
718
-
719
- /**
720
- * Locks an element selection.
721
- * @returns {void}
722
- */
723
- lockItemsSelection() {
724
- if (this.#_options.allowSelectionLocks) {
725
- this.#_status.isSelectionLocked = true;
726
- };
727
- }
728
-
729
- /**
730
- * Unlocks an element selection.
731
- * @returns {void}
732
- */
733
- unlockItemsSelection() {
734
- this.#_status.isSelectionLocked = false;
735
- }
736
-
737
- /**
738
- * Sets a current index.
739
- * @param {(number|string)} index - element index
740
- * @returns {boolean}
741
- * @fires THtmlItemsListController#current-item-chosen
742
- * @private
743
- */
744
- #_setCurIndex(index) {
745
- const isSUCCEED = super.setCurIndex(index);
746
- /**
747
- * @event THtmlItemsListController#current-item-chosen
748
- * @type {Object}
749
- * @property {number} index - element index
750
- * @property {?HTMLElement} item - element
751
- */
752
- if (isSUCCEED) this.#_triggerEvent('current-item-chosen', {
753
- index: Number(index),
754
- item: null,
755
- });
756
- return isSUCCEED;
757
- };
758
-
759
- /**
760
- * Sets a current index.
761
- * @param {(number|string)} index - element index
762
- * @returns {boolean}
763
- */
764
- setCurIndex(index) {
765
- return this.selectItem(index, true);
766
- }
767
-
768
- /**
769
- * Resets a current index.
770
- * @returns {void}
771
- */
772
- rstCurIndex() {
773
- const {
774
- execDelItem,
775
- execDelItemDI,
776
- execDelItemCI,
777
- } = this.#_status;
778
- if (execDelItem) {
779
- let index = execDelItemCI;
780
- if (execDelItemCI !== -1) index--;
781
- if (index !== -1 && execDelItemDI !== execDelItemCI) {
782
- this.unselectItem(index);
783
- };
784
- };
785
- super.rstCurIndex();
786
- }
787
-
788
- /**
789
- * Adds an element to an instance members.
790
- * @param {HTMLElement} item - some element
791
- * @param {boolean} [opt=false]
792
- * @returns {number}
793
- * @fires THtmlItemsListController#first-item-added
794
- * @fires THtmlItemsListController#item-added
795
- */
796
- addItem(item, opt) {
797
- const index = super.addItem(item, false);
798
- if (index !== -1) {
799
- const { autoHideNewItems, showStubsIfEmpty } = this.#_options;
800
- const _status = this.#_status;
801
- const { catchEventOnHost, isStubItemShown } = _status;
802
- if (!catchEventOnHost) {
803
- // set event handler on element if it is not set on host
804
- item.addEventListener('click', this.#_on_will_select_item);
805
- };
806
- // TODO: consider if a "boolean" <opt> is enough
807
- const forceCI = typeof opt === 'boolean' ? opt : false;
808
- if (!autoHideNewItems) {
809
- if (index === 0) {
810
- if (
811
- showStubsIfEmpty
812
- && isStubItemShown
813
- && this.#_stubs.hideDefItem()
814
- ) {
815
- // hide default stub-item
816
- _status.isStubItemShown = false;
817
- };
818
- /**
819
- * @event THtmlItemsListController#first-item-added
820
- * @type {Object}
821
- * @property {number} index - element index
822
- * @property {?HTMLElement} item - element
823
- */
824
- this.#_triggerEvent('first-item-added', {
825
- index: index,
826
- item: item,
827
- });
828
- };
829
- /**
830
- * @event THtmlItemsListController#item-added
831
- * @type {Object}
832
- * @property {number} index - element index
833
- * @property {?HTMLElement} item - element
834
- */
835
- this.#_triggerEvent('item-added', {
836
- index: index,
837
- item: item,
838
- });
839
- };
840
- if (forceCI) {
841
- this.#_selectItemEx(item, {
842
- ctrlKey: false,
843
- shiftKey: false,
844
- forceCI: forceCI,
845
- });
846
- };
847
- };
848
- return index;
849
- }
850
-
851
- /**
852
- * Deletes an element from an instance members.
853
- * @param {(number|string)} index - element index
854
- * @param {any} [opt]
855
- * @returns {boolean}
856
- * @fires THtmlItemsListController#list-clear
857
- * @fires THtmlItemsListController#item-removed
858
- */
859
- delItem(index, opt) {
860
- const item = super.getItem(index);
861
- let isSUCCEED = item !== undefined;
862
- const _status = this.#_status;
863
- if (isSUCCEED) {
864
- _status.execDelItem = true;
865
- _status.execDelItemCI = this.curIndex;
866
- _status.execDelItemDI = Number(index);
867
- isSUCCEED = super.delItem(index, opt, false);
868
- if (isSUCCEED) {
869
- const _options = this.#_options;
870
- if (!_status.catchEventOnHost && isHTMLElement(item)) {
871
- // remove event handler on element if it was set by addItem()
872
- item.removeEventListener('click', this.#_on_will_select_item);
873
- };
874
- if (this.isEmpty()) {
875
- this.#_selects.clear();
876
- if (_options.showStubsIfEmpty) {
877
- // show default stub-item
878
- _status.isStubItemShown = this.#_stubs.showDefItem();
879
- };
880
- /**
881
- * @see THtmlItemsListController#list-clear
882
- */
883
- this.#_triggerEvent('list-clear');
884
- } else {
885
- this.#_selects.delete(item);
886
- /**
887
- * @event THtmlItemsListController#item-removed
888
- * @type {Object}
889
- * @property {number} index - element index
890
- * @property {?HTMLElement} item - element
891
- */
892
- this.#_triggerEvent('item-removed', {
893
- index: Number(index),
894
- item: item,
895
- });
896
- const current = this.curIndex;
897
- if (current === -1) {
898
- this.rstCurIndex();
899
- } else {
900
- this.setCurIndex(current);
901
- };
902
- };
903
- };
904
- _status.execDelItemDI = -1;
905
- _status.execDelItemCI = -1;
906
- _status.execDelItem = false;
907
- };
908
- return isSUCCEED;
909
- }
910
-
911
- /**
912
- * Selects an element.
913
- * @param {(number|string)} index - element index
914
- * @returns {boolean}
915
- * @private
916
- * @fires THtmlItemsListController#item-selected
917
- */
918
- #_selectItem(index) {
919
- const item = super.getItem(index);
920
- const isSUCCEED = selectHTMLElement(item);
921
- if (isSUCCEED) {
922
- this.#_selects.add(item);
923
- /**
924
- * @event THtmlItemsListController#item-selected
925
- * @type {Object}
926
- * @property {number} index - element index
927
- * @property {?HTMLElement} item - element
928
- */
929
- this.#_triggerEvent('item-selected', {
930
- index: Number(index),
931
- item: item,
932
- });
933
- };
934
- return isSUCCEED;
935
- }
936
-
937
- /**
938
- * Selects an element.
939
- * @param {?HTMLElement} item - element
940
- * @param {object} [opt]
941
- * @param {boolean} [opt.ctrlKey=false]
942
- * @param {boolean} [opt.shiftKey=false]
943
- * @param {boolean} [opt.forceCI=false]
944
- * @returns {void}
945
- * @private
946
- */
947
- #_selectItemEx(item, opt){
948
- const _selects = this.#_selects;
949
- let {
950
- ctrlKey = false,
951
- shiftKey = false,
952
- forceCI = false,
953
- } = opt;
954
- let mode = ILC_SMODE_DEF;
955
- let doUnSelGrp = false;
956
- let indexCI = this.curIndex;
957
- let indexNI = this.srchIndex(item);
958
- if (indexCI !== -1) {
959
- if (this.#_options.allowGroupSelection) {
960
- if (shiftKey) {
961
- mode = ILC_SMODE_SHFT;
962
- if (_selects.size > 0) doUnSelGrp = true;
963
- } else if (ctrlKey) {
964
- mode = ILC_SMODE_CTRL;
965
- } else if (_selects.size > 0) {
966
- doUnSelGrp = true;
967
- };
968
- } else if (_selects.size > 0) {
969
- doUnSelGrp = true;
970
- };
971
- };
972
- if (doUnSelGrp) {
973
- for (let item of _selects) {
974
- this.unselectItem(this.srchIndex(item));
975
- };
976
- _selects.clear();
977
- };
978
- switch (mode) {
979
- case ILC_SMODE_SHFT: {
980
- if (indexCI > indexNI) [ indexCI, indexNI ] = [ indexNI, indexCI ];
981
- for (let i = indexCI; i < indexNI + 1; i++) {
982
- this.#_selectItem(i);
983
- };
984
- break;
985
- }
986
- case ILC_SMODE_CTRL: {
987
- this.#_selectItem(this.srchIndex(item));
988
- break;
989
- }
990
- default: {
991
- if (this.#_selectItem(this.srchIndex(item))) {
992
- if (forceCI) this.#_setCurIndex(indexNI);
993
- };
994
- break;
995
- }
996
- };
997
- }
998
-
999
- /**
1000
- * Selects an element.
1001
- * @param {(number|string)} index - element index
1002
- * @param {boolean} [opt=false]
1003
- * @returns {boolean}
1004
- */
1005
- selectItem(index, opt) {
1006
- const item = super.getItem(index);
1007
- let isSUCCEED = isHTMLElement(item);
1008
- if (isSUCCEED) {
1009
- const forceCI = typeof opt === 'boolean' ? opt : false;
1010
- this.#_selectItemEx(item, {
1011
- ctrlKey: false,
1012
- shiftKey: false,
1013
- forceCI: forceCI,
1014
- });
1015
- };
1016
- return isSUCCEED;
1017
- }
1018
-
1019
- /**
1020
- * Unselects an element.
1021
- * @param {(number|string)} index - element index
1022
- * @returns {boolean}
1023
- * @fires THtmlItemsListController#item-unselected
1024
- */
1025
- unselectItem(index) {
1026
- const item = super.getItem(index);
1027
- let isSUCCEED = unselectHTMLElement(item);
1028
- if (isSUCCEED) {
1029
- this.#_selects.delete(item);
1030
- /**
1031
- * @event THtmlItemsListController#item-unselected
1032
- * @type {Object}
1033
- * @property {number} index - element index
1034
- * @property {?HTMLElement} item - element
1035
- */
1036
- this.#_triggerEvent('item-unselected', {
1037
- index: Number(index),
1038
- item: item,
1039
- });
1040
- };
1041
- return isSUCCEED;
1042
- }
1043
-
1044
- /**
1045
- * Hides an element.
1046
- * @param {(number|string)} index - element index
1047
- * @returns {boolean}
1048
- * @fires THtmlItemsListController#item-hidden
1049
- */
1050
- hideItem(index) {
1051
- const item = super.getItem(index);
1052
- let isSUCCEED = hideHTMLElement(item);
1053
- if (isSUCCEED) {
1054
- /**
1055
- * @event THtmlItemsListController#item-hidden
1056
- * @type {Object}
1057
- * @property {number} index - element index
1058
- * @property {?HTMLElement} item - element
1059
- */
1060
- this.#_triggerEvent('item-hidden', {
1061
- index: Number(index),
1062
- item: item,
1063
- });
1064
- };
1065
- return isSUCCEED;
1066
- }
1067
-
1068
- /**
1069
- * Shows an element.
1070
- * @param {(number|string)} index - element index
1071
- * @returns {boolean}
1072
- * @fires THtmlItemsListController#item-shown
1073
- */
1074
- showItem(index) {
1075
- const item = super.getItem(index);
1076
- let isSUCCEED = showHTMLElement(item);
1077
- if (isSUCCEED) {
1078
- /**
1079
- * @event THtmlItemsListController#item-shown
1080
- * @type {Object}
1081
- * @property {number} index - element index
1082
- * @property {?HTMLElement} item - element
1083
- */
1084
- this.#_triggerEvent('item-shown', {
1085
- index: Number(index),
1086
- item: item,
1087
- });
1088
- };
1089
- return isSUCCEED;
1090
- }
1091
-
1092
- /**
1093
- * Sets a callback function to handle event.
1094
- * @param {string} name - event name
1095
- * @param {func} evnt
1096
- * @returns {void}
1097
- */
1098
- on(name, evnt) {
1099
- pushEventHandler(this.#_events, name, evnt);
1100
- }
1101
-
1102
- };
1103
-
1104
- // === module exports block ===
1105
-
1106
- exports.THtmlStubItemsSet = THtmlStubItemsSet;
1107
- exports.THtmlItemsListContainer = THtmlItemsListContainer;
1108
- exports.THtmlItemsListController = THtmlItemsListController;