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