@lemonadejs/contextmenu 1.0.4 → 1.0.7
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 +32 -0
- package/dist/index.js +145 -37
- package/dist/style.css +3 -3
- package/package.json +3 -2
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Official Type definitions for LemonadeJS plugins
|
|
3
|
+
* https://lemonadejs.net
|
|
4
|
+
* Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
interface Contextmenu {
|
|
8
|
+
(): any
|
|
9
|
+
[key: string]: any
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface Items {
|
|
13
|
+
type: 'line' | undefined;
|
|
14
|
+
title?: string;
|
|
15
|
+
icon?: string;
|
|
16
|
+
submenu?: Items[];
|
|
17
|
+
render?: (e: MouseEvent, element: HTMLElement) => void;
|
|
18
|
+
onclick?: (e: MouseEvent, element: HTMLElement) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface Options {
|
|
22
|
+
/** Modal is closed */
|
|
23
|
+
options?: Items[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface instance {
|
|
27
|
+
options: boolean;
|
|
28
|
+
open: (options: Options, x: number, y: number, e: MouseEvent) => void;
|
|
29
|
+
close: () => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export declare function Modal(el: HTMLElement, options?: Options): instance;
|
package/dist/index.js
CHANGED
|
@@ -30,12 +30,19 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
30
30
|
|
|
31
31
|
const Item = function() {
|
|
32
32
|
let self = this;
|
|
33
|
-
|
|
33
|
+
|
|
34
|
+
self.onload = function() {
|
|
35
|
+
if (typeof(self.render) === 'function') {
|
|
36
|
+
self.render.call(self, self.el);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (self.type === 'line') {
|
|
34
41
|
return `<hr />`;
|
|
35
|
-
} else if (
|
|
36
|
-
return
|
|
42
|
+
} else if (self.type === 'inline') {
|
|
43
|
+
return `<div></div>`;
|
|
37
44
|
} else {
|
|
38
|
-
return `<div class="lm-menu-item" data-cursor="{{self.cursor}}" data-icon="{{self.icon}}" data-submenu="{{
|
|
45
|
+
return `<div class="lm-menu-item" data-cursor="{{self.cursor}}" data-icon="{{self.icon}}" data-submenu="{{self.submenu?true:''}}" onmouseup="self.parent.parent.mouseUp(self, e)" onmouseenter="self.parent.parent.mouseEnter(self)" onmouseleave="self.parent.parent.mouseLeave(self)">
|
|
39
46
|
<a>{{self.title}}</a> <div>{{self.shortcut}}</div>
|
|
40
47
|
</div>`;
|
|
41
48
|
}
|
|
@@ -44,21 +51,31 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
44
51
|
const Create = function() {
|
|
45
52
|
let self = this;
|
|
46
53
|
|
|
54
|
+
// Delay on open
|
|
55
|
+
let delayTimer;
|
|
47
56
|
// Save the position of this modal
|
|
48
57
|
let index = self.parent.modals.length;
|
|
49
58
|
|
|
50
59
|
// Close handler
|
|
51
60
|
self.onclose = function() {
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
// }
|
|
61
|
+
// Reset any cursor
|
|
62
|
+
resetCursor.call(self.modal);
|
|
63
|
+
}
|
|
56
64
|
|
|
57
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Close the modal
|
|
67
|
+
*/
|
|
68
|
+
self.close = function() {
|
|
69
|
+
// Close modals with higher level
|
|
70
|
+
self.parent.close(index);
|
|
58
71
|
}
|
|
59
72
|
|
|
60
|
-
|
|
61
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Open submenu handler
|
|
75
|
+
* @param {object} s
|
|
76
|
+
* @param {boolean} cursor - Activate the first item
|
|
77
|
+
*/
|
|
78
|
+
self.open = function(s, cursor) {
|
|
62
79
|
if (s.submenu) {
|
|
63
80
|
// Get the modal in the container of modals
|
|
64
81
|
let item = self.parent.modals[index+1];
|
|
@@ -70,18 +87,13 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
70
87
|
let parent = self.parent.modals[index].modal;
|
|
71
88
|
// Get the self of the modal
|
|
72
89
|
let modal = item.modal;
|
|
73
|
-
|
|
90
|
+
// Update modal content
|
|
74
91
|
if (modal.options !== s.submenu) {
|
|
75
92
|
// Close modals with higher level
|
|
76
93
|
modal.options = s.submenu;
|
|
77
|
-
// Remove cursor
|
|
78
|
-
if (modal.cursor) {
|
|
79
|
-
delete modal.cursor;
|
|
80
|
-
}
|
|
81
94
|
// Close other modals
|
|
82
95
|
self.parent.close(index+1);
|
|
83
96
|
}
|
|
84
|
-
|
|
85
97
|
// Open modal
|
|
86
98
|
modal.closed = false;
|
|
87
99
|
// Update selected modal
|
|
@@ -89,13 +101,46 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
89
101
|
// Define the position
|
|
90
102
|
modal.top = parent.top + s.el.offsetTop + 2;
|
|
91
103
|
modal.left = parent.left + 248;
|
|
104
|
+
|
|
105
|
+
// Activate the cursor
|
|
106
|
+
if (cursor === true) {
|
|
107
|
+
// Place cursor in the first position
|
|
108
|
+
modal.options[0].cursor = true;
|
|
109
|
+
// Position cursor
|
|
110
|
+
modal.cursor = 0;
|
|
111
|
+
}
|
|
92
112
|
} else {
|
|
93
113
|
// Close modals with higher level
|
|
94
114
|
self.parent.close(index+1);
|
|
95
115
|
}
|
|
96
116
|
}
|
|
97
117
|
|
|
98
|
-
|
|
118
|
+
// Mouse open
|
|
119
|
+
self.mouseUp = function(s, e) {
|
|
120
|
+
if (typeof(s.onclick) === 'function') {
|
|
121
|
+
s.onclick.call(s, e, s.el);
|
|
122
|
+
}
|
|
123
|
+
if (! s.submenu) {
|
|
124
|
+
self.close();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
self.mouseEnter = function(s) {
|
|
129
|
+
if (delayTimer) {
|
|
130
|
+
clearTimeout(delayTimer);
|
|
131
|
+
}
|
|
132
|
+
delayTimer = setTimeout(function() {
|
|
133
|
+
self.open(s);
|
|
134
|
+
}, 200);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
self.mouseLeave = function() {
|
|
138
|
+
if (delayTimer) {
|
|
139
|
+
clearTimeout(delayTimer);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
let template = `<Modal :closed="true" :ref="self.modal" :responsive="false" :auto-adjust="true" :onclose="self.onclose" :focus="false" :layers="false">
|
|
99
144
|
<div class="lm-menu-submenu">
|
|
100
145
|
<Item :loop="self.options" />
|
|
101
146
|
</div>
|
|
@@ -137,21 +182,59 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
137
182
|
this.options[cursor].cursor = true;
|
|
138
183
|
// Cursor
|
|
139
184
|
this.cursor = cursor;
|
|
185
|
+
// If is line move to the next one
|
|
186
|
+
if (this.options[cursor].type === 'line') {
|
|
187
|
+
setCursor.call(this, direction);
|
|
188
|
+
}
|
|
140
189
|
}
|
|
141
190
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
191
|
+
/**
|
|
192
|
+
* Reset the cursor for a contextmenu
|
|
193
|
+
*/
|
|
194
|
+
const resetCursor = function() {
|
|
195
|
+
// Contextmenu modal
|
|
196
|
+
let item = this.options[this.cursor];
|
|
197
|
+
// Cursor is found so reset it
|
|
198
|
+
if (typeof(item) !== 'undefined') {
|
|
199
|
+
// Remove the cursor style
|
|
200
|
+
item.cursor = false;
|
|
201
|
+
// Delete reference index
|
|
202
|
+
delete this.cursor;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const actionCursor = function(e) {
|
|
207
|
+
// Contextmenu modal
|
|
208
|
+
let item = this.options[this.cursor];
|
|
209
|
+
// Cursor is found so reset it
|
|
210
|
+
if (typeof(item) !== 'undefined') {
|
|
211
|
+
// Execute action
|
|
212
|
+
if (typeof(item.onclick) === 'function') {
|
|
213
|
+
item.onclick.call(item, e, item.el);
|
|
214
|
+
}
|
|
215
|
+
// Open sub menu in case exists
|
|
147
216
|
if (item.submenu) {
|
|
148
217
|
this.parent.open(item);
|
|
218
|
+
return true;
|
|
149
219
|
}
|
|
150
220
|
}
|
|
151
221
|
}
|
|
152
222
|
|
|
153
|
-
|
|
154
|
-
|
|
223
|
+
/**
|
|
224
|
+
* Open a sub option of the contextmenu by a user action
|
|
225
|
+
* @returns {boolean}
|
|
226
|
+
*/
|
|
227
|
+
const openSubmenu = function() {
|
|
228
|
+
// Get the selected cursor
|
|
229
|
+
let item = this.options[this.cursor];
|
|
230
|
+
// Open submenu
|
|
231
|
+
if (typeof(item) !== 'undefined') {
|
|
232
|
+
// Open submenu in case that exists
|
|
233
|
+
if (item.submenu) {
|
|
234
|
+
this.parent.open(item, true);
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
155
238
|
}
|
|
156
239
|
|
|
157
240
|
const Contextmenu = function() {
|
|
@@ -174,16 +257,19 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
174
257
|
return s;
|
|
175
258
|
}
|
|
176
259
|
|
|
177
|
-
self.open = function(
|
|
260
|
+
self.open = function(options, x, y, e) {
|
|
178
261
|
// Get the main modal
|
|
179
262
|
let modal = self.modals[0].modal;
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
263
|
+
// Reset cursor
|
|
264
|
+
resetCursor.call(modal);
|
|
265
|
+
|
|
266
|
+
// Current state
|
|
267
|
+
if (! e || e.type === 'contextmenu') {
|
|
184
268
|
modal.closed = false;
|
|
269
|
+
} else if (e.type === 'click') {
|
|
270
|
+
modal.closed = ! modal.closed;
|
|
185
271
|
}
|
|
186
|
-
|
|
272
|
+
|
|
187
273
|
// If the modal is open and the content is different from what is shown
|
|
188
274
|
if (modal.closed === false) {
|
|
189
275
|
// Close modals with higher level
|
|
@@ -191,6 +277,7 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
191
277
|
// Define new position
|
|
192
278
|
modal.top = y;
|
|
193
279
|
modal.left = x;
|
|
280
|
+
// Update the data
|
|
194
281
|
if (modal.options !== options) {
|
|
195
282
|
// Refresh content
|
|
196
283
|
modal.options = options;
|
|
@@ -199,50 +286,71 @@ if (! Modal && typeof (require) === 'function') {
|
|
|
199
286
|
}
|
|
200
287
|
|
|
201
288
|
self.close = function(level) {
|
|
289
|
+
// Close all modals from the level specified
|
|
202
290
|
self.modals.forEach(function(value, k) {
|
|
203
291
|
if (k >= level) {
|
|
204
292
|
// Close the modal
|
|
205
293
|
value.modal.closed = true;
|
|
206
294
|
}
|
|
207
295
|
});
|
|
208
|
-
|
|
209
|
-
self.modalIndex = level ? level-1 : 0;
|
|
296
|
+
// Keep the index of the modal that is opened
|
|
297
|
+
self.modalIndex = level ? level - 1 : 0;
|
|
210
298
|
}
|
|
211
299
|
|
|
212
300
|
self.onload = function() {
|
|
213
301
|
if (! self.root) {
|
|
214
302
|
self.root = self.el.parentNode;
|
|
215
303
|
}
|
|
304
|
+
|
|
216
305
|
// Keyboard event
|
|
217
306
|
self.root.addEventListener("keydown", function(e) {
|
|
218
307
|
// Modal object
|
|
219
308
|
let m = self.modals[self.modalIndex].modal;
|
|
309
|
+
// Something happens
|
|
310
|
+
let ret = false;
|
|
311
|
+
// Control
|
|
220
312
|
if (e.key === 'ArrowLeft') {
|
|
221
|
-
|
|
313
|
+
if (self.modalIndex > 0) {
|
|
314
|
+
// Close modal
|
|
315
|
+
m.parent.close();
|
|
316
|
+
// Action happened
|
|
317
|
+
ret = true;
|
|
318
|
+
}
|
|
222
319
|
} else if (e.key === 'ArrowRight') {
|
|
223
|
-
openSubmenu.call(m);
|
|
320
|
+
ret = openSubmenu.call(m);
|
|
224
321
|
} else if (e.key === 'ArrowUp') {
|
|
225
322
|
setCursor.call(m, 0);
|
|
226
323
|
} else if (e.key === 'ArrowDown') {
|
|
227
324
|
setCursor.call(m, 1);
|
|
325
|
+
} else if (e.key === 'Enter') {
|
|
326
|
+
ret = actionCursor.call(m, e);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Something important happen so block any progression
|
|
330
|
+
if (ret === true) {
|
|
331
|
+
e.preventDefault();
|
|
332
|
+
e.stopImmediatePropagation();
|
|
228
333
|
}
|
|
229
334
|
});
|
|
230
335
|
|
|
231
336
|
// Create event for focus out
|
|
232
337
|
self.root.addEventListener("focusout", (e) => {
|
|
233
|
-
if (! self.
|
|
338
|
+
if (! self.root.contains(e.relatedTarget)) {
|
|
234
339
|
self.close(0);
|
|
235
340
|
}
|
|
236
341
|
});
|
|
342
|
+
|
|
237
343
|
// Parent
|
|
238
344
|
self.root.addEventListener("contextmenu", function(e) {
|
|
239
345
|
let [x,y] = getCoords(e);
|
|
240
346
|
// Open the context menu
|
|
241
|
-
self.open(
|
|
347
|
+
self.open(self.options, x, y, e);
|
|
242
348
|
e.preventDefault();
|
|
243
349
|
e.stopImmediatePropagation();
|
|
244
350
|
});
|
|
351
|
+
|
|
245
352
|
self.root.setAttribute('tabindex', -1);
|
|
353
|
+
|
|
246
354
|
// Create first menu
|
|
247
355
|
self.create();
|
|
248
356
|
}
|
package/dist/style.css
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
user-select: none;
|
|
4
4
|
border: 1px solid transparent;
|
|
5
5
|
border-radius: 4px;
|
|
6
|
-
box-shadow: 0 2px 6px 2px rgba(60,64,67,.
|
|
6
|
+
box-shadow: 0 2px 6px 2px rgba(60,64,67,.2);
|
|
7
7
|
max-height: 300px;
|
|
8
8
|
overflow-y: auto;
|
|
9
9
|
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
height: initial;
|
|
12
12
|
min-width: 250px;
|
|
13
13
|
min-height: initial;
|
|
14
|
-
padding-bottom: 6px;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
.lm-menu .lm-modal::-webkit-scrollbar {
|
|
@@ -54,6 +53,8 @@
|
|
|
54
53
|
|
|
55
54
|
.lm-menu-submenu > div.lm-menu-item div {
|
|
56
55
|
margin-right: 5px;
|
|
56
|
+
font-size: 0.9em;
|
|
57
|
+
color: #888;
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
.lm-menu-submenu > div.lm-menu-item[data-submenu="true"]::after {
|
|
@@ -83,5 +84,4 @@
|
|
|
83
84
|
position: absolute;
|
|
84
85
|
left: 9px;
|
|
85
86
|
line-height: 24px;
|
|
86
|
-
transform: rotate(0.03deg);
|
|
87
87
|
}
|
package/package.json
CHANGED
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
"build": "webpack --config webpack.config.js"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@lemonadejs/modal": "^2.3.
|
|
17
|
+
"@lemonadejs/modal": "^2.3.3",
|
|
18
18
|
"lemonadejs": "^3.4.0"
|
|
19
19
|
},
|
|
20
20
|
"main": "dist/index.js",
|
|
21
|
-
"
|
|
21
|
+
"types": "dist/index.d.ts",
|
|
22
|
+
"version": "1.0.7"
|
|
22
23
|
}
|