-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
contextmenu.js
101 lines (92 loc) · 3.04 KB
/
contextmenu.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/**
* Adds context menu functionality.
* @module contextmenu
* @license Apache-2.0
* @author Adam Bender
*/
let contextMenuExtensions = {}
/**
* Signature depends on what the user adds; in the case of our uses with
* SVGEditor, no parameters are passed nor anything expected for a return.
* @callback module:contextmenu.MenuItemAction
* @param {...args} args
* @returns {any}
*/
/**
* @typedef {PlainObject} module:contextmenu.MenuItem
* @property {string} id
* @property {string} label
* @property {module:contextmenu.MenuItemAction} action
*/
/**
* @param {module:contextmenu.MenuItem} menuItem
* @returns {boolean}
*/
const menuItemIsValid = function (menuItem) {
return menuItem && menuItem.id && menuItem.label && menuItem.action && typeof menuItem.action === 'function'
}
/**
* @function module:contextmenu.add
* @param {module:contextmenu.MenuItem} menuItem
* @throws {Error|TypeError}
* @returns {void}
*/
export const add = function (menuItem) {
// menuItem: {id, label, shortcut, action}
if (!menuItemIsValid(menuItem)) {
throw new TypeError(
'Menu items must be defined and have at least properties: ' +
'id, label, action, where action must be a function'
)
}
if (menuItem.id in contextMenuExtensions) {
throw new Error('Cannot add extension "' + menuItem.id + '", an extension by that name already exists"')
}
// Register menuItem action, see below for deferred menu dom injection
contextMenuExtensions[menuItem.id] = menuItem
// TODO: Need to consider how to handle custom enable/disable behavior
}
/**
* @function module:contextmenu.hasCustomHandler
* @param {string} handlerKey
* @returns {boolean}
*/
export const hasCustomHandler = function (handlerKey) {
return Boolean(contextMenuExtensions[handlerKey])
}
/**
* @function module:contextmenu.getCustomHandler
* @param {string} handlerKey
* @returns {module:contextmenu.MenuItemAction}
*/
export const getCustomHandler = function (handlerKey) {
return contextMenuExtensions[handlerKey].action
}
/**
* @param {module:contextmenu.MenuItem} menuItem
* @returns {void}
*/
const injectExtendedContextMenuItemIntoDom = function (menuItem) {
if (!Object.keys(contextMenuExtensions).length) {
// all menuItems appear at the bottom of the menu in their own container.
// if this is the first extension menu we need to add the separator.
document.getElementById('cmenu_canvas').appendChild('<li class=\'separator\'>')
}
const shortcut = menuItem.shortcut || ''
document.getElementById('cmenu_canvas').appendChild(`
<li class='disabled'><a href='#${menuItem.id}'>${menuItem.label}<span class='shortcut'>${shortcut}</span></a></li>`)
}
/**
* @function module:contextmenu.injectExtendedContextMenuItemsIntoDom
* @returns {void}
*/
export const injectExtendedContextMenuItemsIntoDom = function () {
Object.values(contextMenuExtensions).forEach((menuItem) => {
injectExtendedContextMenuItemIntoDom(menuItem)
})
}
/**
* @function module:contextmenu.resetCustomMenus
* @returns {void}
*/
export const resetCustomMenus = function () { contextMenuExtensions = {} }