@lemonadejs/contextmenu 1.0.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.js +173 -0
- package/dist/style.css +72 -0
- package/package.json +22 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
if (!lemonade && typeof (require) === 'function') {
|
|
2
|
+
var lemonade = require('lemonadejs');
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
if (!Modal && typeof (require) === 'function') {
|
|
6
|
+
var Modal = require('@lemonadejs/modal');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
; (function (global, factory) {
|
|
10
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
11
|
+
typeof define === 'function' && define.amd ? define(factory) :
|
|
12
|
+
global.Contextmenu = factory();
|
|
13
|
+
}(this, (function () {
|
|
14
|
+
|
|
15
|
+
// Level
|
|
16
|
+
let index = 0;
|
|
17
|
+
|
|
18
|
+
// Get the coordinates of the action
|
|
19
|
+
const getCoords = function(e) {
|
|
20
|
+
let x;
|
|
21
|
+
let y;
|
|
22
|
+
|
|
23
|
+
if (e.changedTouches && e.changedTouches[0]) {
|
|
24
|
+
x = e.changedTouches[0].clientX;
|
|
25
|
+
y = e.changedTouches[0].clientY;
|
|
26
|
+
} else {
|
|
27
|
+
x = e.clientX;
|
|
28
|
+
y = e.clientY;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return [x,y];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const Item = function() {
|
|
35
|
+
if (this.type === 'line') {
|
|
36
|
+
return `<hr />`;
|
|
37
|
+
} else {
|
|
38
|
+
return `<div data-icon="{{self.icon}}" data-submenu="{{!!self.submenu}}" onmouseover="self.parent.parent.open(e, self)">
|
|
39
|
+
<a>{{self.title}}</a> <span>{{self.shortcut}}</span>
|
|
40
|
+
</div>`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const Create = function() {
|
|
45
|
+
let self = this;
|
|
46
|
+
|
|
47
|
+
self.open = function(e, s) {
|
|
48
|
+
if (s.submenu) {
|
|
49
|
+
// Get the modal in the container of modals
|
|
50
|
+
let item = self.parent.modals[self.index+1];
|
|
51
|
+
if (! item) {
|
|
52
|
+
// Modal need to be created
|
|
53
|
+
item = self.parent.create();
|
|
54
|
+
}
|
|
55
|
+
// Get the parent from this one
|
|
56
|
+
let parent = self.parent.modals[self.index].modal;
|
|
57
|
+
// Get the self of the modal
|
|
58
|
+
let modal = item.modal;
|
|
59
|
+
|
|
60
|
+
if (modal.options !== s.submenu) {
|
|
61
|
+
// Close modals with higher level
|
|
62
|
+
modal.options = s.submenu;
|
|
63
|
+
// Close other modals
|
|
64
|
+
self.parent.close(self.index+1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Open modal
|
|
68
|
+
modal.closed = false;
|
|
69
|
+
// Define the position
|
|
70
|
+
modal.top = parent.top + e.target.offsetTop + 2;
|
|
71
|
+
modal.left = parent.left + 248;
|
|
72
|
+
} else {
|
|
73
|
+
// Close modals with higher level
|
|
74
|
+
self.parent.close(self.index+1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let template = `<Modal :closed="true" :ref="self.modal">
|
|
79
|
+
<div class="lm-menu-submenu">
|
|
80
|
+
<Item :loop="self.options" />
|
|
81
|
+
</div>
|
|
82
|
+
</Modal>`;
|
|
83
|
+
|
|
84
|
+
return lemonade.element(template, self, { Item: Item });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const Contextmenu = function() {
|
|
88
|
+
let self = this;
|
|
89
|
+
|
|
90
|
+
// Container for all modals
|
|
91
|
+
self.modals = [];
|
|
92
|
+
|
|
93
|
+
self.create = function() {
|
|
94
|
+
// Create a new self for each modal
|
|
95
|
+
let s = {
|
|
96
|
+
index: index++,
|
|
97
|
+
parent: self,
|
|
98
|
+
};
|
|
99
|
+
// Render the modal inside the main container
|
|
100
|
+
lemonade.render(Create, self.el, s);
|
|
101
|
+
// Add the reference of the modal in a container
|
|
102
|
+
self.modals.push(s);
|
|
103
|
+
// Return self
|
|
104
|
+
return s;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
self.open = function(e, options, x, y) {
|
|
108
|
+
// Get the main modal
|
|
109
|
+
let modal = self.modals[0].modal;
|
|
110
|
+
// Click on the top level menu toggle the state of the menu
|
|
111
|
+
if (e.type == 'click') {
|
|
112
|
+
modal.closed = ! modal.closed;
|
|
113
|
+
} else if (e.type == 'contextmenu') {
|
|
114
|
+
modal.closed = false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// If the modal is open and the content is different from what is shown
|
|
118
|
+
if (modal.closed === false) {
|
|
119
|
+
// Close modals with higher level
|
|
120
|
+
self.close(1);
|
|
121
|
+
// Define new position
|
|
122
|
+
modal.top = y;
|
|
123
|
+
modal.left = x;
|
|
124
|
+
if (modal.options !== options) {
|
|
125
|
+
// Refresh content
|
|
126
|
+
modal.options = options;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
self.close = function(level) {
|
|
132
|
+
self.modals.forEach(function(value, k) {
|
|
133
|
+
if (k >= level) {
|
|
134
|
+
value.modal.closed = true;
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
self.onload = function() {
|
|
140
|
+
if (! self.root) {
|
|
141
|
+
self.root = self.el.parentNode;
|
|
142
|
+
}
|
|
143
|
+
// Create event for focus out
|
|
144
|
+
self.root.addEventListener("focusout", (e) => {
|
|
145
|
+
self.close(0);
|
|
146
|
+
});
|
|
147
|
+
// Parent
|
|
148
|
+
self.root.addEventListener("contextmenu", function(e) {
|
|
149
|
+
let [x,y] = getCoords(e);
|
|
150
|
+
// Open the context menu
|
|
151
|
+
self.open(e, self.options, x, y);
|
|
152
|
+
e.preventDefault();
|
|
153
|
+
e.stopImmediatePropagation();
|
|
154
|
+
});
|
|
155
|
+
self.root.setAttribute('tabindex', -1);
|
|
156
|
+
// Create first menu
|
|
157
|
+
self.create();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return `<div class="lm-menu"></div>`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
lemonade.setComponents({ Contextmenu: Contextmenu });
|
|
164
|
+
|
|
165
|
+
return function (root, options) {
|
|
166
|
+
if (typeof (root) === 'object') {
|
|
167
|
+
lemonade.render(Contextmenu, root, options)
|
|
168
|
+
return options;
|
|
169
|
+
} else {
|
|
170
|
+
return Contextmenu.call(this, root)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
})));
|
package/dist/style.css
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
.lm-menu .lm-modal {
|
|
2
|
+
color: #555;
|
|
3
|
+
user-select: none;
|
|
4
|
+
border: 1px solid transparent;
|
|
5
|
+
border-radius: 4px;
|
|
6
|
+
box-shadow: 0 2px 6px 2px rgba(60,64,67,.15);
|
|
7
|
+
max-height: calc(100vh - 94px);
|
|
8
|
+
overflow-y: auto;
|
|
9
|
+
|
|
10
|
+
width: initial;
|
|
11
|
+
height: initial;
|
|
12
|
+
min-height: initial;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.lm-menu-submenu {
|
|
16
|
+
padding-top: 1px;
|
|
17
|
+
padding-bottom: 1px;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.lm-menu-submenu > div {
|
|
21
|
+
box-sizing: border-box;
|
|
22
|
+
display: flex;
|
|
23
|
+
padding: 0 12px 0 32px;
|
|
24
|
+
width: 250px;
|
|
25
|
+
font-size: 11px;
|
|
26
|
+
font-family:sans-serif;
|
|
27
|
+
text-align: left;
|
|
28
|
+
align-items: center;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.lm-menu-submenu > div a {
|
|
32
|
+
text-decoration: none;
|
|
33
|
+
flex: 1;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
line-height: 28px;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.lm-menu-submenu > div span {
|
|
39
|
+
margin-right: 10px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.lm-menu-submenu > div[data-submenu="true"]::after {
|
|
43
|
+
content: '\25B6'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.lm-menu-submenu > div.disabled {
|
|
47
|
+
color: #ccc;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.lm-menu-submenu > div:hover {
|
|
51
|
+
background: #ebebeb;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.lm-menu-submenu hr {
|
|
55
|
+
border: 1px solid #e9e9e9;
|
|
56
|
+
border-bottom: 0;
|
|
57
|
+
margin-top:5px;
|
|
58
|
+
margin-bottom:5px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.lm-menu-submenu > div::before {
|
|
62
|
+
content: attr(data-icon);
|
|
63
|
+
font-family: 'Material Icons';
|
|
64
|
+
font-size: 15px;
|
|
65
|
+
position: absolute;
|
|
66
|
+
left: 9px;
|
|
67
|
+
line-height: 24px;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.lm-menu-submenu.symbols > div::before {
|
|
71
|
+
font-family: 'Material Symbols Outlined';
|
|
72
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lemonadejs/contextmenu",
|
|
3
|
+
"title": "LemonadeJS contextmenu",
|
|
4
|
+
"description": "LemonadeJS Contextmenu JavaScript Plugin",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Contact <contact@lemonadejs.net>",
|
|
7
|
+
"url": "https://lemonadejs.net"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"javascript contextmenu"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"start": "webpack serve --history-api-fallback",
|
|
14
|
+
"build": "webpack --config webpack.config.js"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@lemonadejs/modal": "^2.0.4",
|
|
18
|
+
"lemonadejs": "^3.3.2"
|
|
19
|
+
},
|
|
20
|
+
"main": "dist/index.js",
|
|
21
|
+
"version": "1.0.0"
|
|
22
|
+
}
|