@lemonadejs/dropdown 3.0.13 → 3.1.1
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 +11 -0
- package/dist/index.d.ts +17 -4
- package/dist/index.js +73 -6
- package/dist/style.css +57 -6
- package/dist/vue.d.ts +15 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -29,6 +29,8 @@ To use Dropdown via a CDN, include the following script tags in your HTML file:
|
|
|
29
29
|
|
|
30
30
|
```html
|
|
31
31
|
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
|
|
32
|
+
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@lemonadejs/modal/dist/index.min.js"></script>
|
|
33
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lemonadejs/modal/dist/style.min.css" />
|
|
32
34
|
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@lemonadejs/dropdown/dist/index.min.js"></script>
|
|
33
35
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lemonadejs/dropdown/dist/style.min.css" />
|
|
34
36
|
```
|
|
@@ -69,6 +71,7 @@ export default function App() {
|
|
|
69
71
|
| autocomplete?: boolean | If provided, enables the autocomplete feature, displaying an input field that allows users to filter and quickly find options in the Dropdown. |
|
|
70
72
|
| value?: string | Represents the current value or selected option in the Dropdown. |
|
|
71
73
|
| type?: string | Specifies the type of display the Dropdown will use. It can be "searchbar" for a search bar interface, "picker" for a selection picker, or "default" for the default dropdown appearance. |
|
|
74
|
+
| insert?: boolean | Enables the `insert` feature, allowing users to add a new option directly by typing the title text and clicking on the plus symbol. |
|
|
72
75
|
|
|
73
76
|
### Item Details
|
|
74
77
|
|
|
@@ -79,6 +82,14 @@ export default function App() {
|
|
|
79
82
|
| text?: string | Displays the label or text associated with the option. |
|
|
80
83
|
| image?: string | Specifies the image URL to be displayed alongside the option. |
|
|
81
84
|
|
|
85
|
+
### Events
|
|
86
|
+
|
|
87
|
+
| Event | Trigger |
|
|
88
|
+
| ----- | ------- |
|
|
89
|
+
| onclose?: () => void | Invoked when the dropdown modal is closed. |
|
|
90
|
+
| onbeforeinsert?: (instance, Item) => void | Invoked before an item is added to the options through the insert feature. |
|
|
91
|
+
| oninsert?: (instance, Item) => void | Invoked after an item is added to the options through the insert feature. |
|
|
92
|
+
|
|
82
93
|
## License
|
|
83
94
|
|
|
84
95
|
The [LemonadeJS](https://lemonadejs.net) LemonadeJS Dropdown is released under the MIT.
|
package/dist/index.d.ts
CHANGED
|
@@ -35,12 +35,14 @@ declare namespace Dropdown {
|
|
|
35
35
|
autocomplete?: boolean;
|
|
36
36
|
/** Rendering style of the dropdown: 'default', 'picker', 'searchbar' or inline */
|
|
37
37
|
type?: 'default' | 'picker' | 'searchbar' | 'inline',
|
|
38
|
-
/** Defines the dropdown
|
|
38
|
+
/** Defines the dropdown width */
|
|
39
39
|
width?: number;
|
|
40
40
|
/** The initial value of the dropdown */
|
|
41
41
|
value?: string | string[] | number | number[];
|
|
42
42
|
/** Placeholder text for the dropdown */
|
|
43
43
|
placeholder?: string;
|
|
44
|
+
/** Allow insert new items */
|
|
45
|
+
insert?: boolean;
|
|
44
46
|
/** Event handler for value changes */
|
|
45
47
|
onchange?: (obj: object, newValue: string|number) => void;
|
|
46
48
|
/** Event handler for when the dropdown is ready */
|
|
@@ -49,11 +51,22 @@ declare namespace Dropdown {
|
|
|
49
51
|
onopen?: (obj: object) => void;
|
|
50
52
|
/** Event handler for when the dropdown closes */
|
|
51
53
|
onclose?: (obj: object) => void;
|
|
54
|
+
/**
|
|
55
|
+
* Event handler for just before a new option is added to the dropdown. This is an async function to handle ajax requests.
|
|
56
|
+
* Example:
|
|
57
|
+
* self.beforeInsert = async function(s, element) {
|
|
58
|
+
* let newId = await getTheNewItemIdFromDatabase(element.text);
|
|
59
|
+
* return {
|
|
60
|
+
* text: element.text,
|
|
61
|
+
* value: newId,
|
|
62
|
+
* };
|
|
63
|
+
* }
|
|
64
|
+
*
|
|
65
|
+
* */
|
|
66
|
+
onbeforeinsert?: (obj: object, item: Item) => void;
|
|
52
67
|
/** Event handler for when a new option is added to the dropdown */
|
|
53
68
|
oninsert?: (obj: object, item: Item) => void;
|
|
54
|
-
/**
|
|
55
|
-
onbeforeinsert?: (obj: object, item: Item) => void;
|
|
56
|
-
/** Event handler for before a search on autocomplete is performed */
|
|
69
|
+
/** Before the search happens */
|
|
57
70
|
onbeforesearch?: (obj: object, ajaxRequest: object) => boolean | null;
|
|
58
71
|
/** Event handler for processing search results */
|
|
59
72
|
onsearch?: (obj: object, result: object) => void;
|
package/dist/index.js
CHANGED
|
@@ -106,7 +106,14 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
106
106
|
// Get the position from top
|
|
107
107
|
let y = el.scrollTop;
|
|
108
108
|
// Get the height
|
|
109
|
-
let h =
|
|
109
|
+
let h = null;
|
|
110
|
+
if (self.type === 'searchbar' || self.type === 'picker') {
|
|
111
|
+
// Priority should be the size used on the viewport
|
|
112
|
+
h = y + (el.offsetHeight || self.height);
|
|
113
|
+
} else {
|
|
114
|
+
// Priority is the height define during initialization
|
|
115
|
+
h = y + (self.height || el.offsetHeight);
|
|
116
|
+
}
|
|
110
117
|
// Go through the items
|
|
111
118
|
let rows = [];
|
|
112
119
|
// Height
|
|
@@ -171,6 +178,8 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
171
178
|
const render = function (reset) {
|
|
172
179
|
// Move scroll to the top
|
|
173
180
|
el.scrollTop = 0;
|
|
181
|
+
// Reset scroll
|
|
182
|
+
updateScroll();
|
|
174
183
|
// Append first batch
|
|
175
184
|
getVisibleRows(reset);
|
|
176
185
|
}
|
|
@@ -213,7 +222,7 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
213
222
|
el.scrollTop = getRowPosition(item);
|
|
214
223
|
let adjust = getVisibleRows(false);
|
|
215
224
|
if (adjust) {
|
|
216
|
-
el.scrollTop
|
|
225
|
+
el.scrollTop = adjust;
|
|
217
226
|
// Last adjust on the visible rows
|
|
218
227
|
getVisibleRows(false);
|
|
219
228
|
}
|
|
@@ -470,7 +479,7 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
470
479
|
}
|
|
471
480
|
}
|
|
472
481
|
|
|
473
|
-
self.search = function() {
|
|
482
|
+
self.search = function(e) {
|
|
474
483
|
if (self.state && self.autocomplete) {
|
|
475
484
|
// Filter options
|
|
476
485
|
let data;
|
|
@@ -529,7 +538,7 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
529
538
|
} else {
|
|
530
539
|
x = e.clientX;
|
|
531
540
|
}
|
|
532
|
-
if (e.target.offsetWidth - (x - e.target.offsetLeft) <
|
|
541
|
+
if (e.target.offsetWidth - (x - e.target.offsetLeft) < 20) {
|
|
533
542
|
self.toggle();
|
|
534
543
|
} else {
|
|
535
544
|
self.open();
|
|
@@ -584,6 +593,8 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
584
593
|
width: self.width,
|
|
585
594
|
onopen: onopen,
|
|
586
595
|
onclose: onclose,
|
|
596
|
+
position: 'absolute',
|
|
597
|
+
'auto-adjust': true,
|
|
587
598
|
'auto-close': false,
|
|
588
599
|
};
|
|
589
600
|
// Generate modal
|
|
@@ -653,9 +664,65 @@ if (!Modal && typeof (require) === 'function') {
|
|
|
653
664
|
}
|
|
654
665
|
}
|
|
655
666
|
|
|
656
|
-
|
|
667
|
+
/**
|
|
668
|
+
* Sanitize any HTML from be paste on the search
|
|
669
|
+
* @param e
|
|
670
|
+
*/
|
|
671
|
+
self.onpaste = function(e) {
|
|
672
|
+
let text;
|
|
673
|
+
if (e.clipboardData || e.originalEvent.clipboardData) {
|
|
674
|
+
text = (e.originalEvent || e).clipboardData.getData('text/plain');
|
|
675
|
+
} else if (window.clipboardData) {
|
|
676
|
+
text = window.clipboardData.getData('Text');
|
|
677
|
+
}
|
|
678
|
+
text = text.replace(/(\r\n|\n|\r)/gm, "");
|
|
679
|
+
document.execCommand('insertText', false, text)
|
|
680
|
+
e.preventDefault();
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
self.add = async function(e) {
|
|
684
|
+
if (! self.input.textContent) {
|
|
685
|
+
return false;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
e.preventDefault();
|
|
689
|
+
|
|
690
|
+
// New item
|
|
691
|
+
let s = {
|
|
692
|
+
text: self.input.textContent,
|
|
693
|
+
value: self.input.textContent,
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// Event
|
|
697
|
+
if (typeof(self.onbeforeinsert) === 'function') {
|
|
698
|
+
let elClass = self.el.classList;
|
|
699
|
+
elClass.add('lm-dropdown-loading');
|
|
700
|
+
let ret = await self.onbeforeinsert(self, s);
|
|
701
|
+
elClass.remove('lm-dropdown-loading');
|
|
702
|
+
if (ret === false) {
|
|
703
|
+
return;
|
|
704
|
+
} else if (ret) {
|
|
705
|
+
s = ret;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Process the data
|
|
710
|
+
self.data.push(s);
|
|
711
|
+
// Select the new item
|
|
712
|
+
self.select(e, s);
|
|
713
|
+
// Close dropdown
|
|
714
|
+
self.close();
|
|
715
|
+
|
|
716
|
+
// Event
|
|
717
|
+
if (typeof(self.oninsert) === 'function') {
|
|
718
|
+
self.oninsert(self, s);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
return `<div class="lm-dropdown" data-insert="{{self.insert}}" data-type="{{self.type}}" data-state="{{self.state}}" :value="self.value">
|
|
657
723
|
<div class="lm-dropdown-header">
|
|
658
|
-
<div class="lm-dropdown-input" oninput="self.search" onfocus="self.open" onclick="self.click" placeholder="{{self.placeholder}}" :ref="self.input" tabindex="0"></div>
|
|
724
|
+
<div class="lm-dropdown-input" onpaste="self.onpaste" oninput="self.search" onfocus="self.open" onclick="self.click" placeholder="{{self.placeholder}}" :ref="self.input" tabindex="0"></div>
|
|
725
|
+
<div class="lm-dropdown-add" onmousedown="self.add"></div>
|
|
659
726
|
<button onclick="self.close" class="lm-dropdown-done">Done</button>
|
|
660
727
|
</div>
|
|
661
728
|
<div class="lm-dropdown-content">
|
package/dist/style.css
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
.lm-dropdown-header {
|
|
10
10
|
display: flex;
|
|
11
|
+
position: relative;
|
|
12
|
+
align-items: center;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
.lm-dropdown-header button {
|
|
@@ -35,8 +37,6 @@
|
|
|
35
37
|
|
|
36
38
|
.lm-dropdown-input {
|
|
37
39
|
padding: 5px 24px 5px 10px;
|
|
38
|
-
align-items: center;
|
|
39
|
-
position: relative;
|
|
40
40
|
white-space: nowrap;
|
|
41
41
|
overflow: hidden;
|
|
42
42
|
text-overflow: ellipsis;
|
|
@@ -52,20 +52,38 @@
|
|
|
52
52
|
content: '';
|
|
53
53
|
position: absolute;
|
|
54
54
|
width: 24px;
|
|
55
|
-
height:
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
height: 100%;
|
|
56
|
+
top: 0;
|
|
57
|
+
right: 4px;
|
|
58
58
|
background-repeat: no-repeat;
|
|
59
59
|
background-image: url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='gray'/%3E%3C/svg%3E");
|
|
60
|
+
background-position: center;
|
|
60
61
|
transition: transform .1s ease-in-out;
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
.lm-dropdown-add {
|
|
65
|
+
position: absolute;
|
|
66
|
+
padding: 10px;
|
|
67
|
+
right: 20px;
|
|
68
|
+
display: none;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.lm-dropdown-add::after {
|
|
72
|
+
content: '+';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.lm-dropdown[data-insert="true"] .lm-dropdown-input {
|
|
76
|
+
padding-right: 35px;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.lm-dropdown[data-state="true"][data-insert="true"] .lm-dropdown-add {
|
|
80
|
+
display: initial;
|
|
81
|
+
}
|
|
63
82
|
|
|
64
83
|
.lm-dropdown[data-state="true"] .lm-dropdown-input::after {
|
|
65
84
|
transform: rotate(-180deg);
|
|
66
85
|
}
|
|
67
86
|
|
|
68
|
-
|
|
69
87
|
.lm-dropdown-input:empty::before {
|
|
70
88
|
content: "\00a0";
|
|
71
89
|
}
|
|
@@ -193,6 +211,7 @@
|
|
|
193
211
|
display: initial;
|
|
194
212
|
border-left: 1px solid #ccc;
|
|
195
213
|
border-bottom: 1px solid #eee;
|
|
214
|
+
height: 100%;
|
|
196
215
|
}
|
|
197
216
|
|
|
198
217
|
.lm-dropdown[data-type="searchbar"][data-state="true"] .lm-dropdown-input {
|
|
@@ -208,6 +227,14 @@
|
|
|
208
227
|
height: 60px;
|
|
209
228
|
}
|
|
210
229
|
|
|
230
|
+
.lm-dropdown[data-type="searchbar"][data-state="true"] .lm-dropdown-input::after {
|
|
231
|
+
background: initial;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.lm-dropdown[data-type="searchbar"][data-state="true"] .lm-dropdown-add {
|
|
235
|
+
right: 100px;
|
|
236
|
+
}
|
|
237
|
+
|
|
211
238
|
.lm-dropdown[data-type="searchbar"] .lm-modal {
|
|
212
239
|
max-height: initial;
|
|
213
240
|
bottom: 0;
|
|
@@ -255,3 +282,27 @@
|
|
|
255
282
|
box-sizing: border-box;
|
|
256
283
|
width: 100%;
|
|
257
284
|
}
|
|
285
|
+
|
|
286
|
+
.lm-dropdown-loading .lm-dropdown-add::after {
|
|
287
|
+
content: '';
|
|
288
|
+
position: absolute;
|
|
289
|
+
width: 12px;
|
|
290
|
+
height: 12px;
|
|
291
|
+
margin-top: -7px;
|
|
292
|
+
margin-left: -12px;
|
|
293
|
+
border-style: solid;
|
|
294
|
+
border-color: #888;
|
|
295
|
+
border-top-color: transparent;
|
|
296
|
+
border-width: 1px;
|
|
297
|
+
border-radius: 50%;
|
|
298
|
+
animation: spin .8s linear infinite;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
@keyframes spin {
|
|
302
|
+
from {
|
|
303
|
+
transform:rotate(0deg);
|
|
304
|
+
}
|
|
305
|
+
to {
|
|
306
|
+
transform:rotate(360deg);
|
|
307
|
+
}
|
|
308
|
+
}
|
package/dist/vue.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Official Type definitions for the LemonadeJS plugins
|
|
3
|
+
* https://lemonadejs.net
|
|
4
|
+
* Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
|
5
|
+
*/
|
|
6
|
+
import Component from './index';
|
|
7
|
+
|
|
8
|
+
interface Dropdown {
|
|
9
|
+
(): any
|
|
10
|
+
[key: string]: any
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
declare function Dropdown<Dropdown>(props: Component.Options): any;
|
|
14
|
+
|
|
15
|
+
export default Dropdown;
|
package/package.json
CHANGED