@lemonadejs/dropdown 3.2.2 → 3.6.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/dist/index.d.ts CHANGED
@@ -45,6 +45,8 @@ declare namespace Dropdown {
45
45
  insert?: boolean;
46
46
  /** Specifies the URL for fetching the data. */
47
47
  url?: string;
48
+ /** Allow empty. Default: true */
49
+ allowempty?: boolean;
48
50
  /** Event handler for value changes */
49
51
  onchange?: (obj: object, newValue: string|number) => void;
50
52
  /** Event handler for when the dropdown is ready */
package/dist/index.js CHANGED
@@ -242,20 +242,96 @@ if (!Modal && typeof (require) === 'function') {
242
242
  }
243
243
  }
244
244
 
245
- const Dropdown = function () {
245
+ const getAttributeName = function(prop) {
246
+ if (prop.substring(0,1) === ':') {
247
+ prop = prop.substring(1);
248
+ } else if (prop.substring(0,3) === 'lm-') {
249
+ prop = prop.substring(3);
250
+ }
251
+ return prop.toLowerCase();
252
+ }
253
+
254
+ const extractFromHtml = function(element) {
255
+ let data = [];
256
+ // Content
257
+ for (let i = 0; i < element.children.length; i++) {
258
+ let e = element.children[i];
259
+ let item = {
260
+ text: e.textContent || e.getAttribute('title'),
261
+ value: e.getAttribute('value'),
262
+ }
263
+ if (item.value == null) {
264
+ item.value = item.text;
265
+ }
266
+ data.push(item);
267
+ }
268
+
269
+ return data;
270
+ }
271
+
272
+ const extract = function(children) {
273
+ let data = [];
274
+
275
+ if (this.tagName) {
276
+ data = extractFromHtml(this);
277
+ // Remove all elements
278
+ this.textContent = '';
279
+ } else {
280
+ // Get data
281
+ if (typeof(children) === 'string') {
282
+ // Version 4
283
+ let d = document.createElement('div');
284
+ d.innerHTML = children;
285
+ data = extractFromHtml(d);
286
+ } else if (children && children.length) {
287
+ // Version 5
288
+ children.forEach((v) => {
289
+ let item = {}
290
+ v.props.forEach((prop) => {
291
+ item[getAttributeName(prop.name)] = prop.value;
292
+ });
293
+ if (! item.text) {
294
+ item.text = v.children[0]?.props[0]?.value || '';
295
+ }
296
+ data.push(item);
297
+ });
298
+ // Block children
299
+ children.length = 0;
300
+ }
301
+ }
302
+
303
+ return data;
304
+ }
305
+
306
+ const Dropdown = function (children) {
246
307
  let self = this;
308
+ // Data
309
+ let data = [];
247
310
  // Internal value controllers
248
311
  let value = [];
249
312
  // Cursor
250
313
  let cursor = null;
251
314
  // Control events
252
315
  let ignoreEvents = false;
253
- // Default widht
254
- if (!self.width) {
316
+ // Default width
317
+ if (! self.width) {
255
318
  self.width = 260;
256
319
  }
257
320
  // Lazy loading global instance
258
321
  let lazyloading = null;
322
+
323
+ // Data
324
+ if (! Array.isArray(self.data)) {
325
+ self.data = [];
326
+ }
327
+
328
+ let d = extract.call(this, children);
329
+ if (d) {
330
+ d.forEach((v) => {
331
+ self.data.push(v)
332
+ })
333
+ }
334
+
259
335
  // Custom events defined by the user
260
336
  let onload = self.onload;
261
337
  let onchange = self.onchange;
@@ -332,9 +408,11 @@ if (!Modal && typeof (require) === 'function') {
332
408
  const setData = function () {
333
409
  // Estimate width
334
410
  let width = self.width;
411
+ // Data
412
+ data = JSON.parse(JSON.stringify(self.data));
335
413
  // Re-order to make sure groups are in sequence
336
- if (self.data && self.data.length) {
337
- self.data.sort((a, b) => {
414
+ if (data && data.length) {
415
+ data.sort((a, b) => {
338
416
  // Compare groups
339
417
  if (a.group && b.group) {
340
418
  return a.group.localeCompare(b.group);
@@ -343,15 +421,15 @@ if (!Modal && typeof (require) === 'function') {
343
421
  });
344
422
  let group = '';
345
423
  // Define group headers
346
- self.data.map((v) => {
424
+ data.map((v) => {
347
425
  // Compare groups
348
426
  if (v && v.group && v.group !== group) {
349
- v.header = true;
427
+ v.header = v.group;
350
428
  group = v.group;
351
429
  }
352
430
  });
353
431
  // Width && values
354
- self.data.map(function (s) {
432
+ data.map(function (s) {
355
433
  // Estimated width of the element
356
434
  if (s.text) {
357
435
  width = Math.max(width, s.text.length * 8);
@@ -375,7 +453,7 @@ if (!Modal && typeof (require) === 'function') {
375
453
  self.animation = true;
376
454
  }
377
455
  // Data to be listed
378
- self.rows = self.data;
456
+ self.rows = data;
379
457
  }
380
458
 
381
459
  const updateLabel = function () {
@@ -386,7 +464,7 @@ if (!Modal && typeof (require) === 'function') {
386
464
  }
387
465
  }
388
466
 
389
- const setValue = function (v) {
467
+ const setValue = function (v, ignoreEvent) {
390
468
  // Values
391
469
  let newValue;
392
470
  if (!Array.isArray(v)) {
@@ -402,8 +480,8 @@ if (!Modal && typeof (require) === 'function') {
402
480
  // Width && values
403
481
  value = [];
404
482
 
405
- if (Array.isArray(self.data)) {
406
- self.data.map(function (s) {
483
+ if (Array.isArray(data)) {
484
+ data.map(function (s) {
407
485
  // Select values
408
486
  if (newValue.indexOf(s.value) !== -1) {
409
487
  s.selected = true;
@@ -418,7 +496,7 @@ if (!Modal && typeof (require) === 'function') {
418
496
  updateLabel();
419
497
 
420
498
  // Component onchange
421
- if (typeof (onchange) === 'function') {
499
+ if (! ignoreEvent && typeof(onchange) === 'function') {
422
500
  onchange.call(self, self, getValue());
423
501
  }
424
502
  }
@@ -444,7 +522,7 @@ if (!Modal && typeof (require) === 'function') {
444
522
  // Reset search
445
523
  if (self.autocomplete) {
446
524
  // Go to begin of the data
447
- self.rows = self.data;
525
+ self.rows = data;
448
526
  // Remove editable attribute
449
527
  self.input.removeAttribute('contenteditable');
450
528
  // Clear input
@@ -498,6 +576,26 @@ if (!Modal && typeof (require) === 'function') {
498
576
  }
499
577
  }
500
578
 
579
+ const loadData = function(result) {
580
+ // Loading controls
581
+ lazyloading = lazyLoading(self);
582
+ // Loading new data from a remote source
583
+ if (result) {
584
+ result.forEach((v) => {
585
+ self.data.push(v);
586
+ });
587
+ }
588
+ // Process the data
589
+ setData();
590
+ // Set value
591
+ if (typeof(self.value) !== 'undefined') {
592
+ setValue(self.value, true);
593
+ }
594
+ // Custom event by the developer
595
+ if (typeof(onload) === 'function') {
596
+ onload(self);
597
+ }
598
+ }
501
599
 
502
600
  self.add = async function (e) {
503
601
  if (!self.input.textContent) {
@@ -526,11 +624,11 @@ if (!Modal && typeof (require) === 'function') {
526
624
  }
527
625
 
528
626
  // Process the data
529
- self.data.push(s);
627
+ data.unshift(s);
530
628
  // Select the new item
531
629
  self.select(e, s);
532
630
  // Close dropdown
533
- self.close();
631
+ self.search();
534
632
 
535
633
  // Event
536
634
  if (typeof (self.oninsert) === 'function') {
@@ -538,23 +636,22 @@ if (!Modal && typeof (require) === 'function') {
538
636
  }
539
637
  }
540
638
 
541
- self.search = function (e) {
639
+ self.search = function () {
542
640
  if (self.state && self.autocomplete) {
543
641
  // Filter options
544
- let data;
545
- if (!self.input.textContent) {
546
- data = self.data;
642
+ let temp;
643
+ let value = self.input.textContent.toLowerCase()
644
+ if (! value) {
645
+ temp = data;
547
646
  } else {
548
- data = self.data.filter(item => {
549
- return item.selected === true ||
550
- (item.text.toLowerCase().includes(self.input.textContent.toLowerCase())) ||
551
- (item.group && item.group.toLowerCase().includes(self.input.textContent.toLowerCase()));
647
+ temp = data.filter(item => {
648
+ return item.selected === true || (item.text.toLowerCase().includes(value)) || (item.group && item.group.toLowerCase().includes(value));
552
649
  });
553
650
  }
554
651
  // Cursor
555
652
  removeCursor(true);
556
653
  // Update the data from the dropdown
557
- self.rows = data;
654
+ self.rows = temp;
558
655
  }
559
656
  }
560
657
 
@@ -613,7 +710,11 @@ if (!Modal && typeof (require) === 'function') {
613
710
  }
614
711
  } else {
615
712
  if (value[0] === s) {
616
- s.selected = !s.selected;
713
+ if (self.allowempty === false) {
714
+ s.selected = true;
715
+ } else {
716
+ s.selected = !s.selected;
717
+ }
617
718
  } else {
618
719
  if (value[0]) {
619
720
  value[0].selected = false;
@@ -631,14 +732,6 @@ if (!Modal && typeof (require) === 'function') {
631
732
  }
632
733
  }
633
734
 
634
- self.getGroup = function () {
635
- if (this.group && this.header) {
636
- return this.group;
637
- } else {
638
- return '';
639
- }
640
- }
641
-
642
735
  self.onload = function () {
643
736
  if (self.type !== "inline") {
644
737
  // Create modal instance
@@ -661,36 +754,9 @@ if (!Modal && typeof (require) === 'function') {
661
754
  self.input.remove();
662
755
  }
663
756
 
664
- if (!Array.isArray(self.data)) {
665
- self.data = [];
666
- }
667
-
668
- if (self.url && self.data.length === 0) {
669
- const xhr = new XMLHttpRequest();
670
-
671
- xhr.onreadystatechange = function () {
672
- if (xhr.readyState === 4) {
673
- if (xhr.status === 200) {
674
- self.data = JSON.parse(xhr.responseText);
675
- } else {
676
- console.error('Failed to fetch data. Status code: ' + xhr.status);
677
- }
678
- }
679
- };
680
-
681
- xhr.open('GET', self.url, true);
682
- xhr.setRequestHeader('Content-Type', 'text/json')
683
- xhr.send();
684
- }
757
+ // Default width
758
+ self.el.style.width = self.width + 'px';
685
759
 
686
- // Loading controls
687
- lazyloading = lazyLoading(self);
688
- // Process the data
689
- setData();
690
- // Set value
691
- if (typeof (self.value) !== 'undefined') {
692
- setValue(self.value);
693
- }
694
760
  // Focus out of the component
695
761
  self.el.addEventListener('focusout', function (e) {
696
762
  if (self.modal) {
@@ -701,9 +767,10 @@ if (!Modal && typeof (require) === 'function') {
701
767
  }
702
768
  }
703
769
  });
770
+
704
771
  // Key events
705
772
  self.el.addEventListener('keydown', function (e) {
706
- if (!self.modal.closed) {
773
+ if (! self.modal.closed) {
707
774
  let prevent = false;
708
775
  if (e.key === 'ArrowUp') {
709
776
  moveCursor(-1);
@@ -724,6 +791,9 @@ if (!Modal && typeof (require) === 'function') {
724
791
  } else if (e.key === 'Enter') {
725
792
  self.select(e, self.rows[cursor]);
726
793
  prevent = true;
794
+ } else if (e.key === 'Escape') {
795
+ self.modal.closed = true;
796
+ prevent = true;
727
797
  } else {
728
798
  if (e.keyCode === 32 && !self.autocomplete) {
729
799
  self.select(e, self.rows[cursor]);
@@ -735,14 +805,21 @@ if (!Modal && typeof (require) === 'function') {
735
805
  e.stopImmediatePropagation();
736
806
  }
737
807
  } else {
738
- if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
808
+ if (e.key === 'ArrowUp' || e.key === 'ArrowDown' || e.key === 'Enter') {
739
809
  self.modal.closed = false;
740
810
  }
741
811
  }
742
812
  });
743
- // Custom event by the developer
744
- if (typeof (onload) === 'function') {
745
- onload(self);
813
+
814
+ // Load remote data
815
+ if (self.url) {
816
+ fetch(self.url, {
817
+ headers: {
818
+ 'Content-Type': 'text/json',
819
+ }
820
+ }).then(r => r.json()).then(loadData);
821
+ } else {
822
+ loadData();
746
823
  }
747
824
  }
748
825
 
@@ -777,7 +854,7 @@ if (!Modal && typeof (require) === 'function') {
777
854
 
778
855
  return `<div class="lm-dropdown" data-insert="{{self.insert}}" data-type="{{self.type}}" data-state="{{self.state}}" :value="self.value" :data="self.data">
779
856
  <div class="lm-dropdown-header">
780
- <div class="lm-dropdown-input" onpaste="self.onpaste" oninput="self.search" onfocus="self.open" onmousedown="self.click" placeholder="{{self.placeholder}}" :ref="self.input" tabindex="0"></div>
857
+ <div class="lm-dropdown-input" onpaste="self.onpaste" oninput="self.search" onmousedown="self.click" placeholder="{{self.placeholder}}" :ref="self.input" tabindex="0"></div>
781
858
  <div class="lm-dropdown-add" onmousedown="self.add"></div>
782
859
  <div class="lm-dropdown-header-controls">
783
860
  <button onclick="self.close" class="lm-dropdown-done">Done</button>
@@ -786,7 +863,7 @@ if (!Modal && typeof (require) === 'function') {
786
863
  <div class="lm-dropdown-content">
787
864
  <div>
788
865
  <div :loop="self.result" :ref="self.container" :rows="self.rows">
789
- <div class="lm-dropdown-item" onclick="self.parent.select" data-cursor="{{self.cursor}}" data-selected="{{self.selected}}" data-group="{{self.parent.getGroup}}">
866
+ <div class="lm-dropdown-item" onclick="self.parent.select" data-cursor="{{self.cursor}}" data-selected="{{self.selected}}" data-group="{{self.header}}">
790
867
  <div><img :src="self.image" /><span>{{self.text}}</span></div>
791
868
  </div>
792
869
  </div>
@@ -797,6 +874,8 @@ if (!Modal && typeof (require) === 'function') {
797
874
 
798
875
  lemonade.setComponents({ Dropdown: Dropdown });
799
876
 
877
+ lemonade.createWebComponent('dropdown', Dropdown);
878
+
800
879
  return function (root, options) {
801
880
  if (typeof (root) === 'object') {
802
881
  lemonade.render(Dropdown, root, options)
package/dist/react.d.ts CHANGED
@@ -6,10 +6,13 @@
6
6
  import Component from './index';
7
7
 
8
8
  interface Dropdown {
9
+ ref?: MutableRefObject<undefined>;
9
10
  (): any
10
11
  [key: string]: any
11
12
  }
12
13
 
13
- declare function Dropdown<Dropdown>(props: Component.Options): any;
14
+ type Props = IntrinsicAttributes & Component.Options & Dropdown;
15
+
16
+ declare function Dropdown<Dropdown>(props: Props): any;
14
17
 
15
18
  export default Dropdown;
package/dist/style.css CHANGED
@@ -40,12 +40,8 @@
40
40
  border-radius: 0;
41
41
  }
42
42
 
43
- .lm-dropdown .lm-lazy {
44
- max-height: 300px;
45
- }
46
-
47
43
  .lm-dropdown-input {
48
- padding: 5px 24px 5px 10px;
44
+ padding: var(--lm-input-padding, 6px) 24px var(--lm-input-padding, 6px) 10px;
49
45
  white-space: nowrap;
50
46
  overflow: hidden;
51
47
  text-overflow: ellipsis;
@@ -59,6 +55,10 @@
59
55
  border-radius: 2px;
60
56
  }
61
57
 
58
+ .lm-dropdown-input > br {
59
+ display: none;
60
+ }
61
+
62
62
  .lm-dropdown-input:focus {
63
63
  outline: 2px solid var(--lm-border-outline, #000);
64
64
  outline-offset: -1px;
@@ -152,7 +152,11 @@
152
152
  margin-right: 6px;
153
153
  }
154
154
 
155
- .lm-dropdown-item > div > img[src=''] {
155
+ .lm-dropdown-item > div > img:not([src]) {
156
+ display: none;
157
+ }
158
+
159
+ .lm-dropdown-item > div > img[src=""] {
156
160
  display: none;
157
161
  }
158
162
 
@@ -311,7 +315,9 @@
311
315
  }
312
316
 
313
317
  .lm-dropdown .lm-lazy {
318
+ max-height: 300px;
314
319
  scrollbar-width: thin;
320
+ padding-bottom: 5px;
315
321
  }
316
322
 
317
323
  .lm-dropdown .lm-lazy::-webkit-scrollbar {
package/package.json CHANGED
@@ -14,10 +14,10 @@
14
14
  "javascript plugins"
15
15
  ],
16
16
  "dependencies": {
17
- "lemonadejs": "^4.2.2",
18
- "@lemonadejs/modal": "^2.8.1"
17
+ "lemonadejs": "^4.3.3",
18
+ "@lemonadejs/modal": "^3.3.0"
19
19
  },
20
20
  "main": "dist/index.js",
21
21
  "types": "dist/index.d.ts",
22
- "version": "3.2.2"
22
+ "version": "3.6.0"
23
23
  }