@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.
@@ -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
- if (this.type === 'line') {
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 (this.type === 'inline') {
36
- return '<div>' + this.component() + '</div>';
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="{{!!self.submenu}}" onmouseover="self.parent.parent.open(self)">
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
- // if (typeof(value.modal.cursor) !== 'undefined') {
53
- // value.modal.options[value.modal.cursor].cursor = false;
54
- // delete value.modal.cursor;
55
- // }
61
+ // Reset any cursor
62
+ resetCursor.call(self.modal);
63
+ }
56
64
 
57
- console.log(self.cursor,self.options);
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
- // Open handler
61
- self.open = function(s) {
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
- let template = `<Modal :closed="true" :ref="self.modal" :responsive="false" :autoadjust="true" :onclose="self.onclose">
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
- const openSubmenu = function() {
143
- if (typeof(this.options[this.cursor]) !== 'undefined') {
144
- // Get the selected cursor
145
- let item = this.options[this.cursor];
146
- // Open submenu in case that exists
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
- const closeSubmenu = function() {
154
- this.parent.parent.close(this.parent.parent.modalIndex);
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(e, options, x, y) {
260
+ self.open = function(options, x, y, e) {
178
261
  // Get the main modal
179
262
  let modal = self.modals[0].modal;
180
- // Click on the top level menu toggle the state of the menu
181
- if (e.type === 'click') {
182
- modal.closed = ! modal.closed;
183
- } else if (e.type === 'contextmenu') {
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
- closeSubmenu.call(m);
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.el.contains(e.relatedTarget)) {
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(e, self.options, x, y);
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,.15);
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.1",
17
+ "@lemonadejs/modal": "^2.3.3",
18
18
  "lemonadejs": "^3.4.0"
19
19
  },
20
20
  "main": "dist/index.js",
21
- "version": "1.0.4"
21
+ "types": "dist/index.d.ts",
22
+ "version": "1.0.7"
22
23
  }