@lemonadejs/dropdown 5.0.0 → 5.8.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.
- package/README.md +173 -172
- package/dist/index.d.ts +43 -18
- package/dist/index.js +567 -280
- package/dist/style.css +79 -54
- package/package.json +23 -23
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* traducoes
|
|
2
|
+
* Implement page up and down navigation
|
|
3
|
+
* Implement color attribute for items
|
|
5
4
|
*/
|
|
5
|
+
|
|
6
6
|
if (!lemonade && typeof (require) === 'function') {
|
|
7
7
|
var lemonade = require('lemonadejs');
|
|
8
8
|
}
|
|
@@ -17,36 +17,80 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
17
17
|
global.Dropdown = factory();
|
|
18
18
|
}(this, (function () {
|
|
19
19
|
|
|
20
|
+
class CustomEvents extends Event {
|
|
21
|
+
constructor(type, props, options) {
|
|
22
|
+
super(type, {
|
|
23
|
+
bubbles: true,
|
|
24
|
+
composed: true,
|
|
25
|
+
...options,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (props) {
|
|
29
|
+
for (const key in props) {
|
|
30
|
+
// Avoid assigning if property already exists anywhere on `this`
|
|
31
|
+
if (! (key in this)) {
|
|
32
|
+
this[key] = props[key];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Dispatcher
|
|
40
|
+
const Dispatch = function(method, type, options) {
|
|
41
|
+
// Try calling the method directly if provided
|
|
42
|
+
if (typeof method === 'function') {
|
|
43
|
+
let a = Object.values(options);
|
|
44
|
+
return method(...a);
|
|
45
|
+
} else if (this.tagName) {
|
|
46
|
+
return this.dispatchEvent(new CustomEvents(type, options));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
20
50
|
// Default row height
|
|
21
51
|
let defaultRowHeight = 24;
|
|
22
52
|
|
|
53
|
+
// Translations
|
|
54
|
+
const T = function(t) {
|
|
55
|
+
if (typeof(document) !== "undefined" && document.dictionary) {
|
|
56
|
+
return document.dictionary[t] || t;
|
|
57
|
+
} else {
|
|
58
|
+
return t;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const isEmpty = function(v) {
|
|
63
|
+
return v === '' || v === null || v === undefined || (Array.isArray(v) && v.length === 0);
|
|
64
|
+
}
|
|
65
|
+
|
|
23
66
|
/**
|
|
24
|
-
* Compare two arrays
|
|
25
|
-
*
|
|
26
|
-
* @param {
|
|
67
|
+
* Compare two values (arrays, strings, numbers, etc.)
|
|
68
|
+
* Returns true if both are equal or empty
|
|
69
|
+
* @param {*} a1
|
|
70
|
+
* @param {*} a2
|
|
27
71
|
*/
|
|
28
|
-
const compareValues = function
|
|
72
|
+
const compareValues = function(a1, a2) {
|
|
73
|
+
if (a1 === a2 || (isEmpty(a1) && isEmpty(a2))) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
|
|
29
77
|
if (!a1 || !a2) {
|
|
30
78
|
return false;
|
|
31
79
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
} else {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
} else {
|
|
39
|
-
let i = a1.length;
|
|
40
|
-
if (i !== a2.length) {
|
|
80
|
+
|
|
81
|
+
if (Array.isArray(a1) && Array.isArray(a2)) {
|
|
82
|
+
if (a1.length !== a2.length) {
|
|
41
83
|
return false;
|
|
42
84
|
}
|
|
43
|
-
|
|
85
|
+
for (let i = 0; i < a1.length; i++) {
|
|
44
86
|
if (a1[i] !== a2[i]) {
|
|
45
87
|
return false;
|
|
46
88
|
}
|
|
47
89
|
}
|
|
90
|
+
return true;
|
|
48
91
|
}
|
|
49
|
-
|
|
92
|
+
|
|
93
|
+
return a1 === a2;
|
|
50
94
|
}
|
|
51
95
|
|
|
52
96
|
const lazyLoading = function (self) {
|
|
@@ -303,7 +347,11 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
303
347
|
return data;
|
|
304
348
|
}
|
|
305
349
|
|
|
306
|
-
const
|
|
350
|
+
const isDOM = function(o) {
|
|
351
|
+
return (o instanceof Element || o instanceof HTMLDocument || o instanceof DocumentFragment);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const Dropdown = function (children, { onchange, onload }) {
|
|
307
355
|
let self = this;
|
|
308
356
|
// Data
|
|
309
357
|
let data = [];
|
|
@@ -313,12 +361,12 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
313
361
|
let cursor = null;
|
|
314
362
|
// Control events
|
|
315
363
|
let ignoreEvents = false;
|
|
316
|
-
// Default width
|
|
317
|
-
if (! self.width) {
|
|
318
|
-
self.width = 260;
|
|
319
|
-
}
|
|
320
364
|
// Lazy loading global instance
|
|
321
365
|
let lazyloading = null;
|
|
366
|
+
// Tracking changes
|
|
367
|
+
let changesDetected = false;
|
|
368
|
+
// Debounce timer for search
|
|
369
|
+
let searchTimeout = null;
|
|
322
370
|
|
|
323
371
|
// Data
|
|
324
372
|
if (! Array.isArray(self.data)) {
|
|
@@ -332,16 +380,25 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
332
380
|
})
|
|
333
381
|
}
|
|
334
382
|
|
|
383
|
+
// Decide the type based on the size of the screen
|
|
384
|
+
let autoType = self.type === 'auto';
|
|
385
|
+
|
|
335
386
|
// Custom events defined by the user
|
|
336
|
-
let
|
|
337
|
-
|
|
387
|
+
let load = self.onload;
|
|
388
|
+
self.onload = null;
|
|
389
|
+
let change = self.onchange;
|
|
390
|
+
self.onchange = null;
|
|
391
|
+
|
|
392
|
+
// Compatibility
|
|
393
|
+
if (typeof self.newOptions !== 'undefined') {
|
|
394
|
+
self.insert = self.newOptions;
|
|
395
|
+
}
|
|
338
396
|
|
|
339
397
|
// Cursor controllers
|
|
340
398
|
const setCursor = function (index, force) {
|
|
341
399
|
let item = self.rows[index];
|
|
342
|
-
|
|
343
400
|
if (typeof (item) !== 'undefined') {
|
|
344
|
-
// Set cursor number
|
|
401
|
+
// Set the cursor number
|
|
345
402
|
cursor = index;
|
|
346
403
|
// Set visual indication
|
|
347
404
|
item.cursor = true;
|
|
@@ -407,9 +464,9 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
407
464
|
|
|
408
465
|
const adjustDimensions = function(data) {
|
|
409
466
|
// Estimate width
|
|
410
|
-
let width = self.width;
|
|
467
|
+
let width = self.width ?? 0;
|
|
411
468
|
// Adjust the width
|
|
412
|
-
let w =
|
|
469
|
+
let w = getInput().offsetWidth;
|
|
413
470
|
if (width < w) {
|
|
414
471
|
width = w;
|
|
415
472
|
}
|
|
@@ -425,13 +482,6 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
425
482
|
});
|
|
426
483
|
// Min width for the container
|
|
427
484
|
self.container.parentNode.style.width = (width - 2) + 'px';
|
|
428
|
-
// Height
|
|
429
|
-
self.height = 400;
|
|
430
|
-
|
|
431
|
-
// Animation for mobile
|
|
432
|
-
if (document.documentElement.clientWidth < 800) {
|
|
433
|
-
self.animation = true;
|
|
434
|
-
}
|
|
435
485
|
}
|
|
436
486
|
|
|
437
487
|
const setData = function () {
|
|
@@ -465,18 +515,18 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
465
515
|
|
|
466
516
|
const updateLabel = function () {
|
|
467
517
|
if (value && value.length) {
|
|
468
|
-
|
|
518
|
+
getInput().textContent = value.filter(v => v.selected).map(i => i.text).join('; ');
|
|
469
519
|
} else {
|
|
470
|
-
|
|
520
|
+
getInput().textContent = '';
|
|
471
521
|
}
|
|
472
522
|
}
|
|
473
523
|
|
|
474
524
|
const setValue = function (v, ignoreEvent) {
|
|
475
525
|
// Values
|
|
476
526
|
let newValue;
|
|
477
|
-
if (!Array.isArray(v)) {
|
|
478
|
-
if (typeof
|
|
479
|
-
newValue = v.split(';');
|
|
527
|
+
if (! Array.isArray(v)) {
|
|
528
|
+
if (typeof(v) === 'string') {
|
|
529
|
+
newValue = v.split(self.divisor ?? ';');
|
|
480
530
|
} else {
|
|
481
531
|
newValue = [v];
|
|
482
532
|
}
|
|
@@ -489,22 +539,24 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
489
539
|
|
|
490
540
|
if (Array.isArray(data)) {
|
|
491
541
|
data.map(function (s) {
|
|
492
|
-
|
|
493
|
-
if (
|
|
494
|
-
s.selected = true;
|
|
542
|
+
s.selected = newValue.some(v => v == s.value);
|
|
543
|
+
if (s.selected) {
|
|
495
544
|
value.push(s);
|
|
496
|
-
} else {
|
|
497
|
-
s.selected = false;
|
|
498
545
|
}
|
|
499
546
|
});
|
|
500
547
|
}
|
|
501
548
|
|
|
502
549
|
// Update label
|
|
503
|
-
|
|
550
|
+
if (self.isClosed()) {
|
|
551
|
+
updateLabel();
|
|
552
|
+
}
|
|
504
553
|
|
|
505
554
|
// Component onchange
|
|
506
|
-
if (! ignoreEvent
|
|
507
|
-
|
|
555
|
+
if (! ignoreEvent) {
|
|
556
|
+
Dispatch.call(self, change, 'change', {
|
|
557
|
+
instance: self,
|
|
558
|
+
value: getValue(),
|
|
559
|
+
});
|
|
508
560
|
}
|
|
509
561
|
}
|
|
510
562
|
|
|
@@ -513,46 +565,13 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
513
565
|
if (value && value.length) {
|
|
514
566
|
return value.filter(v => v.selected).map(i => i.value);
|
|
515
567
|
}
|
|
516
|
-
return [];
|
|
517
568
|
} else {
|
|
518
569
|
if (value && value.length) {
|
|
519
570
|
return value[0].value;
|
|
520
|
-
} else {
|
|
521
|
-
return '';
|
|
522
571
|
}
|
|
523
572
|
}
|
|
524
|
-
}
|
|
525
573
|
|
|
526
|
-
|
|
527
|
-
// Cursor
|
|
528
|
-
removeCursor(true);
|
|
529
|
-
// Reset search
|
|
530
|
-
if (self.autocomplete) {
|
|
531
|
-
// Go to begin of the data
|
|
532
|
-
self.rows = data;
|
|
533
|
-
// Remove editable attribute
|
|
534
|
-
self.input.removeAttribute('contenteditable');
|
|
535
|
-
// Clear input
|
|
536
|
-
self.input.textContent = '';
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
// Current value
|
|
540
|
-
let newValue = getValue();
|
|
541
|
-
|
|
542
|
-
// If that is different from the component value
|
|
543
|
-
if (!compareValues(newValue, self.value)) {
|
|
544
|
-
self.value = newValue;
|
|
545
|
-
} else {
|
|
546
|
-
// Update label
|
|
547
|
-
updateLabel();
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
// Identify the new state of the dropdown
|
|
551
|
-
self.state = false;
|
|
552
|
-
|
|
553
|
-
if (typeof (self.onclose) === 'function') {
|
|
554
|
-
self.onclose(self);
|
|
555
|
-
}
|
|
574
|
+
return null;
|
|
556
575
|
}
|
|
557
576
|
|
|
558
577
|
const onopen = function () {
|
|
@@ -570,20 +589,82 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
570
589
|
}
|
|
571
590
|
// Prepare search field
|
|
572
591
|
if (self.autocomplete) {
|
|
573
|
-
//
|
|
574
|
-
|
|
592
|
+
// Get the input
|
|
593
|
+
let input = getInput();
|
|
575
594
|
// Editable
|
|
576
|
-
|
|
595
|
+
input.setAttribute('contenteditable', true);
|
|
596
|
+
// Clear input
|
|
597
|
+
input.textContent = '';
|
|
577
598
|
// Focus on the item
|
|
578
|
-
|
|
599
|
+
input.focus();
|
|
579
600
|
}
|
|
601
|
+
// Adjust width and height
|
|
602
|
+
adjustDimensions(self.data);
|
|
603
|
+
// Open event
|
|
604
|
+
Dispatch.call(self, self.onopen, 'open', {
|
|
605
|
+
instance: self
|
|
606
|
+
});
|
|
607
|
+
}
|
|
580
608
|
|
|
581
|
-
|
|
582
|
-
|
|
609
|
+
const onclose = function (options, origin) {
|
|
610
|
+
// Cursor
|
|
611
|
+
removeCursor(true);
|
|
612
|
+
// Reset search
|
|
613
|
+
if (self.autocomplete) {
|
|
614
|
+
// Go to begin of the data
|
|
615
|
+
self.rows = data;
|
|
616
|
+
// Get the input
|
|
617
|
+
let input = getInput();
|
|
618
|
+
if (input) {
|
|
619
|
+
// Remove editable attribute
|
|
620
|
+
input.removeAttribute('contenteditable');
|
|
621
|
+
// Clear input
|
|
622
|
+
input.textContent = '';
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
if (origin === 'escape') {
|
|
627
|
+
// Cancel operation and keep the same previous value
|
|
628
|
+
setValue(self.value, true);
|
|
629
|
+
} else {
|
|
630
|
+
// Current value
|
|
631
|
+
let newValue = getValue();
|
|
632
|
+
|
|
633
|
+
// If that is different from the component value
|
|
634
|
+
if (changesDetected === true && ! compareValues(newValue, self.value)) {
|
|
635
|
+
self.value = newValue;
|
|
636
|
+
} else {
|
|
637
|
+
// Update label
|
|
638
|
+
updateLabel();
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Identify the new state of the dropdown
|
|
643
|
+
self.state = false;
|
|
644
|
+
|
|
645
|
+
// Close event
|
|
646
|
+
Dispatch.call(self, self.onclose, 'close', {
|
|
647
|
+
instance: self,
|
|
648
|
+
...options
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
const normalizeData = function(result) {
|
|
653
|
+
if (result && result.length) {
|
|
654
|
+
return result.map((v) => {
|
|
655
|
+
if (typeof v === 'string' || typeof v === 'number') {
|
|
656
|
+
return { value: v, text: v };
|
|
657
|
+
} else if (typeof v === 'object' && v.hasOwnProperty('name')) {
|
|
658
|
+
return { value: v.id, text: v.name };
|
|
659
|
+
} else {
|
|
660
|
+
return v;
|
|
661
|
+
}
|
|
662
|
+
});
|
|
583
663
|
}
|
|
584
664
|
}
|
|
585
665
|
|
|
586
666
|
const loadData = function(result) {
|
|
667
|
+
result = normalizeData(result);
|
|
587
668
|
// Loading controls
|
|
588
669
|
lazyloading = lazyLoading(self);
|
|
589
670
|
// Loading new data from a remote source
|
|
@@ -598,152 +679,376 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
598
679
|
if (typeof(self.value) !== 'undefined') {
|
|
599
680
|
setValue(self.value, true);
|
|
600
681
|
}
|
|
601
|
-
//
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
}
|
|
682
|
+
// Onload method
|
|
683
|
+
Dispatch.call(self, load, 'load', {
|
|
684
|
+
instance: self
|
|
685
|
+
});
|
|
686
|
+
// Remove loading spin
|
|
687
|
+
self.input.classList.remove('lm-dropdown-loading');
|
|
605
688
|
}
|
|
606
689
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
690
|
+
const resetData = function(result) {
|
|
691
|
+
result = normalizeData(result);
|
|
692
|
+
// Reset cursor
|
|
693
|
+
removeCursor(true);
|
|
694
|
+
let r = data.filter(item => {
|
|
695
|
+
return item.selected === true;
|
|
696
|
+
});
|
|
697
|
+
// Loading new data from a remote source
|
|
698
|
+
if (result) {
|
|
699
|
+
result.forEach((v) => {
|
|
700
|
+
r.push(v);
|
|
701
|
+
});
|
|
610
702
|
}
|
|
703
|
+
self.rows = r;
|
|
704
|
+
// Remove loading spin
|
|
705
|
+
self.input.classList.remove('lm-dropdown-loading');
|
|
611
706
|
|
|
612
|
-
|
|
707
|
+
// Event
|
|
708
|
+
Dispatch.call(self, self.onsearch, 'search', {
|
|
709
|
+
instance: self,
|
|
710
|
+
result: result,
|
|
711
|
+
});
|
|
712
|
+
}
|
|
613
713
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
value: self.input.textContent,
|
|
618
|
-
}
|
|
714
|
+
const getInput = function() {
|
|
715
|
+
return self.input;
|
|
716
|
+
}
|
|
619
717
|
|
|
620
|
-
|
|
621
|
-
if (
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
718
|
+
const search = function(query) {
|
|
719
|
+
if (! self.isClosed() && self.autocomplete) {
|
|
720
|
+
|
|
721
|
+
// Remote or normal search
|
|
722
|
+
if (self.remote === true) {
|
|
723
|
+
// Clear existing timeout
|
|
724
|
+
if (searchTimeout) {
|
|
725
|
+
clearTimeout(searchTimeout);
|
|
726
|
+
}
|
|
727
|
+
// Loading spin
|
|
728
|
+
self.input.classList.add('lm-dropdown-loading');
|
|
729
|
+
// Headers
|
|
730
|
+
let http = {
|
|
731
|
+
headers: {
|
|
732
|
+
'Content-Type': 'text/json',
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
let ret = Dispatch.call(self, self.onbeforesearch, 'beforesearch', {
|
|
736
|
+
instance: self,
|
|
737
|
+
http: http,
|
|
738
|
+
query: query,
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
if (ret === false) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// Debounce the search with 300ms delay
|
|
746
|
+
searchTimeout = setTimeout(() => {
|
|
747
|
+
fetch(`${self.url}?q=${query}`, http).then(r => r.json()).then(resetData).catch((error) => {
|
|
748
|
+
resetData([]);
|
|
749
|
+
});
|
|
750
|
+
}, 300);
|
|
751
|
+
} else {
|
|
752
|
+
// Filter options
|
|
753
|
+
let temp;
|
|
754
|
+
|
|
755
|
+
const find = (prop) => {
|
|
756
|
+
if (prop) {
|
|
757
|
+
if (Array.isArray(prop)) {
|
|
758
|
+
// match if ANY element contains the query (case-insensitive)
|
|
759
|
+
return prop.some(v => v != null && v.toString().toLowerCase().includes(query));
|
|
760
|
+
}
|
|
761
|
+
// handle strings/numbers/others
|
|
762
|
+
return prop.toString().toLowerCase().includes(query);
|
|
763
|
+
}
|
|
764
|
+
return false;
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
if (! query) {
|
|
768
|
+
temp = data;
|
|
769
|
+
} else {
|
|
770
|
+
temp = data.filter(item => {
|
|
771
|
+
return item.selected === true || find(item.text) || find(item.group) || find(item.keywords) || find(item.synonym);
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// Cursor
|
|
776
|
+
removeCursor(true);
|
|
777
|
+
// Update the data from the dropdown
|
|
778
|
+
self.rows = temp;
|
|
630
779
|
}
|
|
631
780
|
}
|
|
781
|
+
}
|
|
632
782
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
783
|
+
const events = {
|
|
784
|
+
focusout: (e) => {
|
|
785
|
+
if (self.modal) {
|
|
786
|
+
if (! (e.relatedTarget && self.el.contains(e.relatedTarget))) {
|
|
787
|
+
if (! self.isClosed()) {
|
|
788
|
+
self.close({ origin: 'focusout '});
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
},
|
|
793
|
+
keydown: (e) => {
|
|
794
|
+
if (! self.isClosed()) {
|
|
795
|
+
let prevent = false;
|
|
796
|
+
if (e.code === 'ArrowUp') {
|
|
797
|
+
moveCursor(-1);
|
|
798
|
+
prevent = true;
|
|
799
|
+
} else if (e.code === 'ArrowDown') {
|
|
800
|
+
moveCursor(1);
|
|
801
|
+
prevent = true;
|
|
802
|
+
} else if (e.code === 'Home') {
|
|
803
|
+
moveCursor(-1, true);
|
|
804
|
+
if (!self.autocomplete) {
|
|
805
|
+
prevent = true;
|
|
806
|
+
}
|
|
807
|
+
} else if (e.code === 'End') {
|
|
808
|
+
moveCursor(1, true);
|
|
809
|
+
if (!self.autocomplete) {
|
|
810
|
+
prevent = true;
|
|
811
|
+
}
|
|
812
|
+
} else if (e.code === 'Enter') {
|
|
813
|
+
if (e.target.tagName === 'BUTTON') {
|
|
814
|
+
e.target.click();
|
|
815
|
+
let input = getInput();
|
|
816
|
+
input.focus();
|
|
817
|
+
} else {
|
|
818
|
+
select(e, self.rows[cursor]);
|
|
819
|
+
}
|
|
820
|
+
prevent = true;
|
|
821
|
+
} else if (e.code === 'Escape') {
|
|
822
|
+
self.close({ origin: 'escape'});
|
|
823
|
+
prevent = true;
|
|
824
|
+
} else {
|
|
825
|
+
if (e.keyCode === 32 && !self.autocomplete) {
|
|
826
|
+
select(e, self.rows[cursor]);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
639
829
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
830
|
+
if (prevent) {
|
|
831
|
+
e.preventDefault();
|
|
832
|
+
e.stopImmediatePropagation();
|
|
833
|
+
}
|
|
834
|
+
} else {
|
|
835
|
+
if (e.code === 'ArrowUp' || e.code === 'ArrowDown' || e.code === 'Enter') {
|
|
836
|
+
self.open();
|
|
837
|
+
e.preventDefault();
|
|
838
|
+
e.stopImmediatePropagation();
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
},
|
|
842
|
+
mousedown: (e) => {
|
|
843
|
+
if (e.target.classList.contains('lm-dropdown-input')) {
|
|
844
|
+
if (self.autocomplete) {
|
|
845
|
+
let x;
|
|
846
|
+
if (e.changedTouches && e.changedTouches[0]) {
|
|
847
|
+
x = e.changedTouches[0].clientX;
|
|
848
|
+
} else {
|
|
849
|
+
x = e.clientX;
|
|
850
|
+
}
|
|
851
|
+
if (e.target.offsetWidth - (x - e.target.offsetLeft) < 20) {
|
|
852
|
+
toggle();
|
|
853
|
+
} else {
|
|
854
|
+
self.open();
|
|
855
|
+
}
|
|
856
|
+
} else {
|
|
857
|
+
toggle();
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
},
|
|
861
|
+
paste: (e) => {
|
|
862
|
+
if (e.target.classList.contains('lm-dropdown-input')) {
|
|
863
|
+
let text;
|
|
864
|
+
if (e.clipboardData || e.originalEvent.clipboardData) {
|
|
865
|
+
text = (e.originalEvent || e).clipboardData.getData('text/plain');
|
|
866
|
+
} else if (window.clipboardData) {
|
|
867
|
+
text = window.clipboardData.getData('Text');
|
|
868
|
+
}
|
|
869
|
+
text = text.replace(/(\r\n|\n|\r)/gm, "");
|
|
870
|
+
document.execCommand('insertText', false, text)
|
|
871
|
+
e.preventDefault();
|
|
872
|
+
}
|
|
873
|
+
},
|
|
874
|
+
input: (e) => {
|
|
875
|
+
if (e.target.classList.contains('lm-dropdown-input')) {
|
|
876
|
+
search(e.target.textContent.toLowerCase());
|
|
877
|
+
}
|
|
878
|
+
},
|
|
644
879
|
}
|
|
645
880
|
|
|
646
|
-
|
|
647
|
-
if (self.
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
881
|
+
const selectItem = function(s) {
|
|
882
|
+
if (self.remote === true) {
|
|
883
|
+
if (data.indexOf(s) === -1) {
|
|
884
|
+
self.data.push(s);
|
|
885
|
+
data.push(s);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
if (self.multiple === true) {
|
|
890
|
+
let position = value.indexOf(s);
|
|
891
|
+
if (position === -1) {
|
|
892
|
+
value.push(s);
|
|
893
|
+
s.selected = true;
|
|
653
894
|
} else {
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
895
|
+
value.splice(position, 1);
|
|
896
|
+
s.selected = false;
|
|
897
|
+
}
|
|
898
|
+
} else {
|
|
899
|
+
if (value[0] === s) {
|
|
900
|
+
if (self.allowEmpty === false) {
|
|
901
|
+
s.selected = true;
|
|
902
|
+
} else {
|
|
903
|
+
s.selected = !s.selected;
|
|
904
|
+
}
|
|
905
|
+
} else {
|
|
906
|
+
if (value[0]) {
|
|
907
|
+
value[0].selected = false;
|
|
908
|
+
}
|
|
909
|
+
s.selected = true;
|
|
910
|
+
}
|
|
911
|
+
if (s.selected) {
|
|
912
|
+
value = [s];
|
|
913
|
+
} else {
|
|
914
|
+
value = [];
|
|
660
915
|
}
|
|
661
|
-
// Cursor
|
|
662
|
-
removeCursor(true);
|
|
663
|
-
// Update the data from the dropdown
|
|
664
|
-
self.rows = temp;
|
|
665
916
|
}
|
|
917
|
+
|
|
918
|
+
changesDetected = true;
|
|
666
919
|
}
|
|
667
920
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
921
|
+
const add = async function (e) {
|
|
922
|
+
let input = getInput();
|
|
923
|
+
let text = input.textContent;
|
|
924
|
+
if (! text) {
|
|
925
|
+
return false;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// New item
|
|
929
|
+
let s = {
|
|
930
|
+
text: text,
|
|
931
|
+
value: text,
|
|
672
932
|
}
|
|
933
|
+
|
|
934
|
+
self.add(s);
|
|
935
|
+
|
|
936
|
+
e.preventDefault();
|
|
673
937
|
}
|
|
674
938
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
939
|
+
const select = function (e, s) {
|
|
940
|
+
if (s && s.disabled !== true) {
|
|
941
|
+
selectItem(s);
|
|
942
|
+
// Close the modal
|
|
943
|
+
if (self.multiple !== true) {
|
|
944
|
+
self.close({ origin: 'button' });
|
|
945
|
+
}
|
|
679
946
|
}
|
|
680
947
|
}
|
|
681
948
|
|
|
682
|
-
|
|
949
|
+
const toggle = function () {
|
|
683
950
|
if (self.modal) {
|
|
684
|
-
if (self.
|
|
951
|
+
if (self.isClosed()) {
|
|
685
952
|
self.open();
|
|
686
953
|
} else {
|
|
687
|
-
self.close();
|
|
954
|
+
self.close({ origin: 'button' });
|
|
688
955
|
}
|
|
689
956
|
}
|
|
690
957
|
}
|
|
691
958
|
|
|
692
|
-
self.
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
} else {
|
|
703
|
-
self.open();
|
|
959
|
+
self.add = async function (newItem) {
|
|
960
|
+
// Event
|
|
961
|
+
if (typeof(self.onbeforeinsert) === 'function') {
|
|
962
|
+
self.input.classList.add('lm-dropdown-loading');
|
|
963
|
+
let ret = await self.onbeforeinsert(self, newItem);
|
|
964
|
+
self.input.classList.remove('lm-dropdown-loading');
|
|
965
|
+
if (ret === false) {
|
|
966
|
+
return;
|
|
967
|
+
} else if (ret) {
|
|
968
|
+
newItem = ret;
|
|
704
969
|
}
|
|
705
|
-
} else {
|
|
706
|
-
self.toggle();
|
|
707
970
|
}
|
|
971
|
+
// Process the data
|
|
972
|
+
data.push(newItem);
|
|
973
|
+
self.data.push(newItem);
|
|
974
|
+
// Refresh screen
|
|
975
|
+
self.result.unshift(newItem);
|
|
976
|
+
self.rows.unshift(newItem);
|
|
977
|
+
self.refresh('result');
|
|
978
|
+
|
|
979
|
+
Dispatch.call(self, self.oninsert, 'insert', {
|
|
980
|
+
instance: self,
|
|
981
|
+
item: newItem,
|
|
982
|
+
});
|
|
708
983
|
}
|
|
709
984
|
|
|
710
|
-
self.
|
|
711
|
-
if (
|
|
712
|
-
if (self.
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
value.push(s);
|
|
716
|
-
s.selected = true;
|
|
717
|
-
} else {
|
|
718
|
-
value.splice(position, 1);
|
|
719
|
-
s.selected = false;
|
|
985
|
+
self.open = function () {
|
|
986
|
+
if (self.modal && ! self.disabled) {
|
|
987
|
+
if (self.isClosed()) {
|
|
988
|
+
if (autoType) {
|
|
989
|
+
self.type = window.innerWidth > 640 ? self.type = 'default' : (self.autocomplete ? 'searchbar' : 'picker');
|
|
720
990
|
}
|
|
991
|
+
// Track
|
|
992
|
+
changesDetected = false;
|
|
993
|
+
// Open the modal
|
|
994
|
+
self.modal.open();
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
self.close = function (options) {
|
|
1000
|
+
if (self.modal) {
|
|
1001
|
+
if (options?.origin) {
|
|
1002
|
+
self.modal.close(options)
|
|
721
1003
|
} else {
|
|
722
|
-
|
|
723
|
-
if (self.allowempty === false) {
|
|
724
|
-
s.selected = true;
|
|
725
|
-
} else {
|
|
726
|
-
s.selected = !s.selected;
|
|
727
|
-
}
|
|
728
|
-
} else {
|
|
729
|
-
if (value[0]) {
|
|
730
|
-
value[0].selected = false;
|
|
731
|
-
}
|
|
732
|
-
s.selected = true;
|
|
733
|
-
}
|
|
734
|
-
if (s.selected) {
|
|
735
|
-
value = [s];
|
|
736
|
-
} else {
|
|
737
|
-
value = [];
|
|
738
|
-
}
|
|
739
|
-
// Close the modal
|
|
740
|
-
self.close();
|
|
1004
|
+
self.modal.close({ origin: 'button' })
|
|
741
1005
|
}
|
|
742
1006
|
}
|
|
743
1007
|
}
|
|
744
1008
|
|
|
745
|
-
self.
|
|
746
|
-
if (self.
|
|
1009
|
+
self.isClosed = function() {
|
|
1010
|
+
if (self.modal) {
|
|
1011
|
+
return self.modal.isClosed();
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
self.setData = function(data) {
|
|
1016
|
+
self.data = data;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
self.getData = function() {
|
|
1020
|
+
return self.data;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
self.getValue = function() {
|
|
1024
|
+
return self.value;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
self.setValue = function(v) {
|
|
1028
|
+
self.value = v;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
self.reset = function() {
|
|
1032
|
+
self.value = null;
|
|
1033
|
+
self.close({ origin: 'button' });
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
self.onevent = function(e) {
|
|
1037
|
+
if (events[e.type]) {
|
|
1038
|
+
events[e.type](e);
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
// Init with a
|
|
1043
|
+
let input = self.input;
|
|
1044
|
+
|
|
1045
|
+
onload(() => {
|
|
1046
|
+
if (self.type === "inline") {
|
|
1047
|
+
// For inline dropdown
|
|
1048
|
+
self.el.setAttribute('tabindex', 0);
|
|
1049
|
+
// Remove search
|
|
1050
|
+
self.input.remove();
|
|
1051
|
+
} else {
|
|
747
1052
|
// Create modal instance
|
|
748
1053
|
self.modal = {
|
|
749
1054
|
closed: true,
|
|
@@ -756,11 +1061,40 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
756
1061
|
};
|
|
757
1062
|
// Generate modal
|
|
758
1063
|
Modal(self.el.children[1], self.modal);
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
if (self.remote === 'true') {
|
|
1067
|
+
self.remote = true;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
if (self.autocomplete === 'true') {
|
|
1071
|
+
self.autocomplete = true;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
if (self.multiple === 'true') {
|
|
1075
|
+
self.multiple = true;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
if (self.insert === 'true') {
|
|
1079
|
+
self.insert = true;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
// Autocomplete will be forced to be true when insert action is active
|
|
1083
|
+
if ((self.insert === true || self.type === 'searchbar' || self.remote === true) && ! self.autocomplete) {
|
|
1084
|
+
self.autocomplete = true;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
if (typeof(input) !== 'undefined') {
|
|
1088
|
+
// Remove the native element
|
|
1089
|
+
if (isDOM(input)) {
|
|
1090
|
+
input.classList.add('lm-dropdown-input');
|
|
1091
|
+
}
|
|
762
1092
|
// Remove search
|
|
763
1093
|
self.input.remove();
|
|
1094
|
+
// New input
|
|
1095
|
+
self.input = input;
|
|
1096
|
+
} else {
|
|
1097
|
+
self.el.children[0].style.position = 'relative';
|
|
764
1098
|
}
|
|
765
1099
|
|
|
766
1100
|
// Default width
|
|
@@ -768,76 +1102,44 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
768
1102
|
// Dropdown
|
|
769
1103
|
self.el.style.width = self.width + 'px';
|
|
770
1104
|
}
|
|
771
|
-
// Adjust width and height
|
|
772
|
-
adjustDimensions(self.data);
|
|
773
1105
|
|
|
774
|
-
//
|
|
775
|
-
self.
|
|
776
|
-
if (self.modal) {
|
|
777
|
-
if (!(e.relatedTarget && self.el.contains(e.relatedTarget)) && !self.el.contains(e.relatedTarget)) {
|
|
778
|
-
if (!self.modal.closed) {
|
|
779
|
-
self.modal.closed = true;
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
});
|
|
1106
|
+
// Height
|
|
1107
|
+
self.height = 400;
|
|
784
1108
|
|
|
785
|
-
//
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
if (e.key === 'ArrowUp') {
|
|
790
|
-
moveCursor(-1);
|
|
791
|
-
prevent = true;
|
|
792
|
-
} else if (e.key === 'ArrowDown') {
|
|
793
|
-
moveCursor(1);
|
|
794
|
-
prevent = true;
|
|
795
|
-
} else if (e.key === 'Home') {
|
|
796
|
-
moveCursor(-1, true);
|
|
797
|
-
if (!self.autocomplete) {
|
|
798
|
-
prevent = true;
|
|
799
|
-
}
|
|
800
|
-
} else if (e.key === 'End') {
|
|
801
|
-
moveCursor(1, true);
|
|
802
|
-
if (!self.autocomplete) {
|
|
803
|
-
prevent = true;
|
|
804
|
-
}
|
|
805
|
-
} else if (e.key === 'Enter') {
|
|
806
|
-
self.select(e, self.rows[cursor]);
|
|
807
|
-
prevent = true;
|
|
808
|
-
} else if (e.key === 'Escape') {
|
|
809
|
-
self.modal.closed = true;
|
|
810
|
-
prevent = true;
|
|
811
|
-
} else {
|
|
812
|
-
if (e.keyCode === 32 && !self.autocomplete) {
|
|
813
|
-
self.select(e, self.rows[cursor]);
|
|
814
|
-
}
|
|
815
|
-
}
|
|
1109
|
+
// Animation for mobile
|
|
1110
|
+
if (document.documentElement.clientWidth < 800) {
|
|
1111
|
+
self.animation = true;
|
|
1112
|
+
}
|
|
816
1113
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
self.modal.closed = false;
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
});
|
|
1114
|
+
// Events
|
|
1115
|
+
self.el.addEventListener('focusout', events.focusout);
|
|
1116
|
+
self.el.addEventListener('keydown', events.keydown);
|
|
1117
|
+
self.el.addEventListener('mousedown', events.mousedown);
|
|
1118
|
+
self.el.addEventListener('paste', events.paste);
|
|
1119
|
+
self.el.addEventListener('input', events.input);
|
|
827
1120
|
|
|
828
1121
|
// Load remote data
|
|
829
1122
|
if (self.url) {
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
1123
|
+
if (self.remote === true) {
|
|
1124
|
+
loadData();
|
|
1125
|
+
} else {
|
|
1126
|
+
// Loading spin
|
|
1127
|
+
self.input.classList.add('lm-dropdown-loading');
|
|
1128
|
+
// Load remote data
|
|
1129
|
+
fetch(self.url, {
|
|
1130
|
+
headers: {
|
|
1131
|
+
'Content-Type': 'text/json',
|
|
1132
|
+
}
|
|
1133
|
+
}).then(r => r.json()).then(loadData).catch(() => {
|
|
1134
|
+
loadData();
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
835
1137
|
} else {
|
|
836
1138
|
loadData();
|
|
837
1139
|
}
|
|
838
|
-
}
|
|
1140
|
+
});
|
|
839
1141
|
|
|
840
|
-
|
|
1142
|
+
onchange(prop => {
|
|
841
1143
|
if (prop === 'value') {
|
|
842
1144
|
setValue(self.value);
|
|
843
1145
|
} else if (prop === 'data') {
|
|
@@ -848,36 +1150,21 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
848
1150
|
if (typeof (lazyloading) === 'function') {
|
|
849
1151
|
lazyloading(prop);
|
|
850
1152
|
}
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
/**
|
|
854
|
-
* Sanitize any HTML from be paste on the search
|
|
855
|
-
* @param e
|
|
856
|
-
*/
|
|
857
|
-
self.onpaste = function (e) {
|
|
858
|
-
let text;
|
|
859
|
-
if (e.clipboardData || e.originalEvent.clipboardData) {
|
|
860
|
-
text = (e.originalEvent || e).clipboardData.getData('text/plain');
|
|
861
|
-
} else if (window.clipboardData) {
|
|
862
|
-
text = window.clipboardData.getData('Text');
|
|
863
|
-
}
|
|
864
|
-
text = text.replace(/(\r\n|\n|\r)/gm, "");
|
|
865
|
-
document.execCommand('insertText', false, text)
|
|
866
|
-
e.preventDefault();
|
|
867
|
-
}
|
|
1153
|
+
});
|
|
868
1154
|
|
|
869
|
-
return `<div class="lm-dropdown" data-insert="{{self.insert}}" data-type="{{self.type}}" data-
|
|
1155
|
+
return render => render`<div class="lm-dropdown" data-state="{{self.state}}" data-insert="{{self.insert}}" data-type="{{self.type}}" data-disabled="{{self.disabled}}" :value="self.value" :data="self.data">
|
|
870
1156
|
<div class="lm-dropdown-header">
|
|
871
|
-
<div class="lm-dropdown-input"
|
|
872
|
-
<
|
|
1157
|
+
<div class="lm-dropdown-input" placeholder="{{self.placeholder}}" :ref="self.input" tabindex="0"></div>
|
|
1158
|
+
<button class="lm-dropdown-add" onclick="${add}" tabindex="0"></button>
|
|
873
1159
|
<div class="lm-dropdown-header-controls">
|
|
874
|
-
<button onclick="self.
|
|
1160
|
+
<button onclick="self.reset" class="lm-dropdown-done">${T('Reset')}</button>
|
|
1161
|
+
<button onclick="self.close" class="lm-dropdown-done">${T('Done')}</button>
|
|
875
1162
|
</div>
|
|
876
1163
|
</div>
|
|
877
1164
|
<div class="lm-dropdown-content">
|
|
878
1165
|
<div>
|
|
879
1166
|
<div :loop="self.result" :ref="self.container" :rows="self.rows">
|
|
880
|
-
<div class="lm-dropdown-item" onclick="
|
|
1167
|
+
<div class="lm-dropdown-item" onclick="${select}" data-cursor="{{self.cursor}}" data-disabled="{{self.disabled}}" data-selected="{{self.selected}}" data-group="{{self.header}}">
|
|
881
1168
|
<div><img :src="self.image" /> <div>{{self.text}}</div></div>
|
|
882
1169
|
</div>
|
|
883
1170
|
</div>
|