vue3-context-menu-plus 2.0.7 → 2.1.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/README.md CHANGED
@@ -1,23 +1,21 @@
1
- <!--
2
- * @Author: eggYolkegg
3
- * @Date: 2025-12-11 15:41:57
4
- * @LastEditors: eggYolkegg
5
- * @LastEditTime: 2025-12-19 08:15:34
6
- * @Description:
7
- -->
8
-
9
- # Vue3 Context Menu
1
+ # Vue3 Context Menu-plus
10
2
 
11
3
  一个轻量、灵活的 Vue 3 右键菜单组件,支持根据不同组件标识配置差异化右键菜单,内置多级子菜单、分隔线、禁用状态等常用特性,指令式调用简单易用。
12
4
  特性
13
5
  🚀 适配 Vue 3 生态(支持 Options API / Composition API)
14
6
  🎨 支持按组件标识(data-component)区分菜单
15
- 📋 支持多级子菜单、分隔线、禁用菜单项
7
+ 📋 支持多级子菜单、分隔线、禁用菜单项,左右标头(icon、rightLabel) 可以直接通过h函数完全的自定义
16
8
  🔧 自定义菜单点击回调,携带完整上下文信息
17
9
  ⚡ 指令式调用,接入成本低
18
10
 
11
+ 因为蛋黄蛋君还要工作,所以会不定时更新或者发布新的小工具,希望大家会用的上,那么祝大家使用愉快, 拜拜啦!!
12
+
19
13
  git:https://github.com/eggyolkegg/vue3-context-menu-plus
20
14
 
15
+ ![展示1](./src/asstes/img1.png)
16
+ ![展示2](./src/asstes/img2.png)
17
+ ![展示3](./src/asstes/img3.png)
18
+
21
19
  ## 安装
22
20
 
23
21
  ```bash
@@ -72,20 +70,38 @@ app.mount('#app')
72
70
  <script setup lang="ts">
73
71
  import { reactive, ref } from "vue";
74
72
  const componentMenus = reactive({
75
- "component-a": [
76
- { id: 1, label: "编辑组件A", icon: "icon-edit" },
77
- { id: 2, label: "删除组件A", icon: "icon-delete" },
78
- { id: 3, divider: true }, // 分隔线
79
- {
80
- id: 4,
81
- label: "更多操作",
82
- children: [
83
- // 多级子菜单
84
- { id: 5, label: "操作1" },
85
- { id: 6, label: "操作2" },
86
- ],
87
- },
88
- ],
73
+ 'component-a': [
74
+ {
75
+ id: 1,
76
+ label: '编辑组件A差距撒哈市德令哈市看到啦几哈',
77
+ icon: () =>h(ElText, { type: "primary" }, 'hha'),
78
+ rightLabel: () =>h(ElText, { type: "primary" }, '这是右边嘿嘿'),
79
+ },
80
+ {
81
+ id: 2,
82
+ label: '删除组件A',
83
+ icon: () =>h(ElText, { type: "primary" }, 'hha'),
84
+ rightLabel: () => h('div', '小姐要不要喝下午茶')
85
+ },
86
+ {
87
+ id: 3, divider: true
88
+ },
89
+ {
90
+ id: 4,
91
+ label: '更多操作',
92
+ children: [
93
+ {
94
+ id: 5,
95
+ label: '操作1'
96
+ },
97
+ {
98
+ id: 6,
99
+ label: '操作2',
100
+ icon: () =>h(ElText, { type: "primary" }, 'hha')
101
+ }
102
+ ]
103
+ }
104
+ ],
89
105
  "component-b": [
90
106
  { id: 7, label: "查看组件B" },
91
107
  { id: 8, label: "导出组件B" },
@@ -113,18 +129,37 @@ export default {
113
129
  // 按组件分类的菜单配置
114
130
  componentMenus: {
115
131
  'component-a': [
116
- { id: 1, label: '编辑组件A', icon: 'icon-edit' },
117
- { id: 2, label: '删除组件A', icon: 'icon-delete' },
118
- { id: 3, divider: true }, // 分隔线
119
- {
120
- id: 4,
121
- label: '更多操作',
122
- children: [ // 多级子菜单
123
- { id: 5, label: '操作1' },
124
- { id: 6, label: '操作2' }
125
- ]
126
- }
127
- ],
132
+ {
133
+ id: 1,
134
+ label: '编辑组件A差距撒哈市德令哈市看到啦几哈',
135
+ icon: () =>h(ElText, { type: "primary" }, 'hha'),
136
+ rightLabel: () =>h(ElText, { type: "primary" }, '这是右边嘿嘿'),
137
+ },
138
+ {
139
+ id: 2,
140
+ label: '删除组件A',
141
+ icon: () =>h(ElText, { type: "primary" }, 'hha'),
142
+ rightLabel: () => h('div', '小姐要不要喝下午茶')
143
+ },
144
+ {
145
+ id: 3, divider: true
146
+ },
147
+ {
148
+ id: 4,
149
+ label: '更多操作',
150
+ children: [
151
+ {
152
+ id: 5,
153
+ label: '操作1'
154
+ },
155
+ {
156
+ id: 6,
157
+ label: '操作2',
158
+ icon: () =>h(ElText, { type: "primary" }, 'hha')
159
+ }
160
+ ]
161
+ }
162
+ ],
128
163
  'component-b': [
129
164
  { id: 7, label: '查看组件B' },
130
165
  { id: 8, label: '导出组件B' }
package/dist/index.cjs.js CHANGED
@@ -10,21 +10,32 @@ const _hoisted_2 = {
10
10
  class: "context-menu-divider"
11
11
  };
12
12
  const _hoisted_3 = ["onClick", "onMouseenter"];
13
- const _hoisted_4 = {
13
+ const _hoisted_4 = { class: "menu-content" };
14
+ const _hoisted_5 = {
14
15
  key: 0,
15
16
  class: "menu-icon"
16
17
  };
17
- const _hoisted_5 = { class: "menu-label" };
18
- const _hoisted_6 = {
18
+ const _hoisted_6 = { class: "menu-icon" };
19
+ const _hoisted_7 = { class: "menu-label" };
20
+ const _hoisted_8 = {
19
21
  key: 1,
22
+ class: "menu-label-right"
23
+ };
24
+ const _hoisted_9 = {
25
+ key: 0,
20
26
  class: "menu-arrow"
21
27
  };
22
- const _hoisted_7 = ["onClick"];
23
- const _hoisted_8 = {
28
+ const _hoisted_10 = ["onClick"];
29
+ const _hoisted_11 = {
24
30
  key: 0,
25
31
  class: "menu-icon"
26
32
  };
27
- const _hoisted_9 = { class: "menu-label" };
33
+ const _hoisted_12 = { class: "menu-icon" };
34
+ const _hoisted_13 = { class: "menu-label" };
35
+ const _hoisted_14 = {
36
+ key: 1,
37
+ class: "menu-label-right"
38
+ };
28
39
  var _sfc_main = /*@__PURE__*/ vue.defineComponent({
29
40
  __name: 'ContextMenu',
30
41
  props: {
@@ -33,25 +44,29 @@ var _sfc_main = /*@__PURE__*/ vue.defineComponent({
33
44
  y: { type: Number, required: true },
34
45
  menus: { type: [Object, Array], required: true },
35
46
  zIndex: { type: Number, required: false, default: 9999 },
36
- maxWidth: { type: Number, required: false, default: 300 },
47
+ maxWidth: { type: Number, required: false },
37
48
  minWidth: { type: Number, required: false, default: 150 }
38
49
  },
39
50
  emits: ["update:visible", "item-click"],
40
51
  setup(__props, { emit: __emit }) {
41
52
  const emit = __emit;
42
- vue.watch(() => __props.visible, (newVal) => {
43
- if (!newVal)
44
- subMenuVisible.value = {};
45
- });
46
53
  const menuRef = vue.useTemplateRef("menuRef");
47
54
  const subMenuVisible = vue.ref({});
48
55
  const subMenuStyle = vue.ref({ top: "0px", left: "0px" });
56
+ const parentShow = vue.ref(false);
57
+ const childShow = vue.ref(false);
58
+ vue.watchEffect(() => {
59
+ __props.x;
60
+ if (!__props.visible)
61
+ subMenuVisible.value = {};
62
+ parentShow.value = false;
63
+ childShow.value = false;
64
+ });
49
65
  // 菜单样式
50
66
  const menuStyle = vue.computed(() => ({
51
67
  left: `${__props.x}px`,
52
68
  top: `${__props.y}px`,
53
69
  zIndex: __props.zIndex,
54
- maxWidth: `${__props.maxWidth}px`,
55
70
  minWidth: `${__props.minWidth}px`,
56
71
  }));
57
72
  // 当前显示的菜单
@@ -97,6 +112,16 @@ var _sfc_main = /*@__PURE__*/ vue.defineComponent({
97
112
  emit("update:visible", false);
98
113
  subMenuVisible.value = {};
99
114
  };
115
+ /**
116
+ * 判断是否显示陪衬样式
117
+ * @param e icon的vnode
118
+ */
119
+ const isShow = (e, type) => {
120
+ if (type === "parent" && !parentShow.value)
121
+ parentShow.value = !!e;
122
+ if (type === "child" && !childShow.value)
123
+ childShow.value = !!e;
124
+ };
100
125
  vue.onMounted(() => {
101
126
  // 监听全局点击
102
127
  document.addEventListener("click", handleClickOutside);
@@ -124,6 +149,7 @@ var _sfc_main = /*@__PURE__*/ vue.defineComponent({
124
149
  return (vue.openBlock(), vue.createElementBlock(vue.Fragment, {
125
150
  key: item.id
126
151
  }, [
152
+ vue.createCommentVNode(" <span v-show=\"false\">{{ ds(item.icon) }}</span> "),
127
153
  vue.createCommentVNode(" 分隔线 "),
128
154
  (item.divider)
129
155
  ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2))
@@ -140,24 +166,34 @@ var _sfc_main = /*@__PURE__*/ vue.defineComponent({
140
166
  onClick: ($event) => (handleClick(item)),
141
167
  onMouseenter: ($event) => (handleMouseEnter(item))
142
168
  }, [
143
- vue.createCommentVNode(" 图标 "),
144
- (item.icon)
145
- ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_4, [
146
- vue.createElementVNode("i", {
147
- class: vue.normalizeClass(item.icon)
148
- }, null, 2 /* CLASS */)
149
- ]))
150
- : vue.createCommentVNode("v-if", true),
151
- vue.createCommentVNode(" 标签 "),
152
- vue.createElementVNode("span", _hoisted_5, vue.toDisplayString(item.label), 1 /* TEXT */),
169
+ vue.createElementVNode("div", _hoisted_4, [
170
+ vue.createCommentVNode(" 图标 "),
171
+ (item.icon)
172
+ ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_5, [
173
+ vue.createTextVNode(vue.toDisplayString(isShow(item.icon(), "parent")) + " ", 1 /* TEXT */),
174
+ (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(item.icon())))
175
+ ]))
176
+ : vue.createCommentVNode("v-if", true),
177
+ vue.withDirectives(vue.createElementVNode("span", _hoisted_6, null, 512 /* NEED_PATCH */), [
178
+ [vue.vShow, parentShow.value && item.icon === undefined]
179
+ ]),
180
+ vue.createCommentVNode(" 标签 "),
181
+ vue.createElementVNode("span", _hoisted_7, vue.toDisplayString(item.label), 1 /* TEXT */),
182
+ vue.createCommentVNode(" 右标签 "),
183
+ (item.rightLabel)
184
+ ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_8, [
185
+ (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(item.rightLabel())))
186
+ ]))
187
+ : vue.createCommentVNode("v-if", true)
188
+ ]),
153
189
  vue.createCommentVNode(" 子菜单箭头 "),
154
190
  (item.children)
155
- ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_6, "▶"))
191
+ ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_9, "▶"))
156
192
  : vue.createCommentVNode("v-if", true),
157
193
  vue.createCommentVNode(" 子菜单 "),
158
194
  (item.children && subMenuVisible.value[item.id])
159
195
  ? (vue.openBlock(), vue.createElementBlock("div", {
160
- key: 2,
196
+ key: 1,
161
197
  class: "context-submenu",
162
198
  style: vue.normalizeStyle(subMenuStyle.value)
163
199
  }, [
@@ -168,14 +204,21 @@ var _sfc_main = /*@__PURE__*/ vue.defineComponent({
168
204
  onClick: vue.withModifiers(($event) => (handleClick(child)), ["stop"])
169
205
  }, [
170
206
  (child.icon)
171
- ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_8, [
172
- vue.createElementVNode("i", {
173
- class: vue.normalizeClass(child.icon)
174
- }, null, 2 /* CLASS */)
207
+ ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_11, [
208
+ vue.createTextVNode(vue.toDisplayString(isShow(child.icon(), "child")) + " ", 1 /* TEXT */),
209
+ (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(child.icon())))
175
210
  ]))
176
211
  : vue.createCommentVNode("v-if", true),
177
- vue.createElementVNode("span", _hoisted_9, vue.toDisplayString(child.label), 1 /* TEXT */)
178
- ], 10 /* CLASS, PROPS */, _hoisted_7));
212
+ vue.withDirectives(vue.createElementVNode("span", _hoisted_12, null, 512 /* NEED_PATCH */), [
213
+ [vue.vShow, childShow.value && child.icon === undefined]
214
+ ]),
215
+ vue.createElementVNode("span", _hoisted_13, vue.toDisplayString(item.label), 1 /* TEXT */),
216
+ (item.rightLabel)
217
+ ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_14, [
218
+ (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(item.rightLabel())))
219
+ ]))
220
+ : vue.createCommentVNode("v-if", true)
221
+ ], 10 /* CLASS, PROPS */, _hoisted_10));
179
222
  }), 128 /* KEYED_FRAGMENT */))
180
223
  ], 4 /* STYLE */))
181
224
  : vue.createCommentVNode("v-if", true)
@@ -220,7 +263,7 @@ function styleInject(css, ref) {
220
263
  }
221
264
  }
222
265
 
223
- var css_248z = "\n.vue-context-menu[data-v-cbb365ae] {\r\n position: fixed;\r\n background: white;\r\n border: 1px solid #dcdfe6;\r\n border-radius: 4px;\r\n box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);\r\n padding: 5px 0;\r\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\r\n font-size: 14px;\r\n color: #606266;\n}\n.context-menu-content[data-v-cbb365ae] {\r\n width: 100%;\n}\n.context-menu-item[data-v-cbb365ae] {\r\n padding: 8px 20px;\r\n cursor: pointer;\r\n display: flex;\r\n align-items: center;\r\n position: relative;\r\n transition: background-color 0.2s;\n}\n.context-menu-item[data-v-cbb365ae]:hover:not(.disabled) {\r\n background-color: #f5f7fa;\n}\n.context-menu-item.disabled[data-v-cbb365ae] {\r\n color: #c0c4cc;\r\n cursor: not-allowed;\n}\n.context-menu-divider[data-v-cbb365ae] {\r\n height: 1px;\r\n background-color: #ebeef5;\r\n margin: 5px 0;\n}\n.menu-icon[data-v-cbb365ae] {\r\n margin-right: 8px;\r\n font-size: 14px;\r\n width: 14px;\r\n display: inline-flex;\r\n justify-content: center;\n}\n.menu-label[data-v-cbb365ae] {\r\n flex: 1;\r\n white-space: nowrap;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\n}\n.menu-arrow[data-v-cbb365ae] {\r\n margin-left: 8px;\r\n font-size: 12px;\r\n color: #909399;\n}\n.context-submenu[data-v-cbb365ae] {\r\n position: absolute;\r\n background: white;\r\n border: 1px solid #dcdfe6;\r\n border-radius: 4px;\r\n box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);\r\n min-width: 150px;\r\n z-index: 10000;\n}\r\n\r\n/* 动画效果 */\n.context-menu-fade-enter-active[data-v-cbb365ae],\r\n.context-menu-fade-leave-active[data-v-cbb365ae] {\r\n transition: opacity 0.2s, transform 0.2s;\n}\n.context-menu-fade-enter-from[data-v-cbb365ae],\r\n.context-menu-fade-leave-to[data-v-cbb365ae] {\r\n opacity: 0;\r\n transform: scale(0.95);\n}\r\n";
266
+ var css_248z = "\n.vue-context-menu[data-v-cbb365ae] {\r\n position: fixed;\r\n background: white;\r\n border: 1px solid #dcdfe6;\r\n border-radius: 4px;\r\n box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);\r\n padding: 5px;\r\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\r\n font-size: 14px;\r\n color: #606266;\n}\n.context-menu-content[data-v-cbb365ae] {\r\n width: 100%;\n}\n.context-menu-item[data-v-cbb365ae] {\r\n padding: 8px 20px;\r\n cursor: pointer;\r\n display: flex;\r\n align-items: center;\r\n position: relative;\r\n transition: background-color 0.2s;\n}\n.context-menu-item[data-v-cbb365ae]:hover:not(.disabled) {\r\n background-color: #f1f3f5;\r\n border-radius: 3px;\n}\n.context-menu-item.disabled[data-v-cbb365ae] {\r\n color: #c0c4cc;\r\n cursor: not-allowed;\n}\n.context-menu-divider[data-v-cbb365ae] {\r\n height: 1px;\r\n background-color: #ebeef5;\r\n margin: 5px 0;\n}\n.menu-icon[data-v-cbb365ae] {\r\n margin-right: 10px;\r\n font-size: 14px;\r\n width: 14px;\r\n display: inline-flex;\r\n justify-content: center;\n}\n.menu-content[data-v-cbb365ae] {\r\n display: flex;\r\n flex: 1;\r\n justify-content: space-between;\n}\n.menu-label[data-v-cbb365ae] {\r\n flex: 1;\r\n white-space: nowrap;\n}\n.menu-label-right[data-v-cbb365ae] {\r\n margin-left: 50px;\r\n flex: 1;\r\n white-space: nowrap;\n}\n.menu-arrow[data-v-cbb365ae] {\r\n margin-left: 8px;\r\n font-size: 12px;\r\n color: #909399;\n}\n.context-submenu[data-v-cbb365ae] {\r\n position: absolute;\r\n background: white;\r\n border: 1px solid #dcdfe6;\r\n border-radius: 4px;\r\n box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);\r\n min-width: 150px;\r\n z-index: 10000;\n}\r\n\r\n/* 动画效果 */\n.context-menu-fade-enter-active[data-v-cbb365ae],\r\n.context-menu-fade-leave-active[data-v-cbb365ae] {\r\n transition: opacity 0.2s, transform 0.2s;\n}\n.context-menu-fade-enter-from[data-v-cbb365ae],\r\n.context-menu-fade-leave-to[data-v-cbb365ae] {\r\n opacity: 0;\r\n transform: scale(0.95);\n}\r\n";
224
267
  styleInject(css_248z);
225
268
 
226
269
  var _export_sfc = (sfc, props) => {
@@ -238,6 +281,7 @@ let menuInstance = null;
238
281
  let container = null;
239
282
  let currentElement = null;
240
283
  let mouseEvent = null;
284
+ let menuData = null; // 响应式数据存储
241
285
  let currentOnItemClick = null;
242
286
  /**
243
287
  * 创建vue模版实例
@@ -245,23 +289,22 @@ let currentOnItemClick = null;
245
289
  const createMenuInstance = () => {
246
290
  container = document.createElement("div");
247
291
  document.body.appendChild(container);
292
+ // 创建响应式数据
293
+ menuData = vue.reactive({
294
+ visible: false,
295
+ x: 0,
296
+ y: 0,
297
+ menus: [],
298
+ });
248
299
  menuInstance = vue.createApp({
249
- data() {
250
- return {
251
- visible: false,
252
- x: 0,
253
- y: 0,
254
- menus: [],
255
- };
256
- },
257
300
  render() {
258
301
  return vue.h(ContextMenu, {
259
- visible: this.visible,
260
- x: this.x,
261
- y: this.y,
262
- menus: this.menus,
302
+ visible: menuData.visible,
303
+ x: menuData.x,
304
+ y: menuData.y,
305
+ menus: menuData.menus,
263
306
  "onUpdate:visible": (val) => {
264
- this.visible = val;
307
+ menuData.visible = val;
265
308
  },
266
309
  onItemClick: (item) => {
267
310
  if (currentOnItemClick && currentElement && mouseEvent)
@@ -285,13 +328,12 @@ function showContextMenu(event, menus, element, onItemClick) {
285
328
  currentOnItemClick = onItemClick || null;
286
329
  currentElement = element || event.currentTarget;
287
330
  mouseEvent = event;
288
- const vm = menuInstance._instance.proxy;
289
- vm.visible = true;
290
- vm.menus = menus;
291
- vm.x = event.clientX;
292
- vm.y = event.clientY;
331
+ menuData.visible = true;
332
+ menuData.menus = menus;
333
+ menuData.x = event.clientX;
334
+ menuData.y = event.clientY;
293
335
  // 防止菜单超出屏幕
294
- requestAnimationFrame(() => {
336
+ vue.nextTick(() => {
295
337
  const menuEl = container === null || container === void 0 ? void 0 : container.querySelector(".vue-context-menu");
296
338
  if (!menuEl)
297
339
  return;
@@ -299,9 +341,9 @@ function showContextMenu(event, menus, element, onItemClick) {
299
341
  const viewportWidth = window.innerWidth;
300
342
  const viewportHeight = window.innerHeight;
301
343
  if (rect.right > viewportWidth)
302
- vm.x = viewportWidth - rect.width - 5;
344
+ menuData.x = viewportWidth - rect.width - 5;
303
345
  if (rect.bottom > viewportHeight)
304
- vm.y = viewportHeight - rect.height - 5;
346
+ menuData.y = viewportHeight - rect.height - 5;
305
347
  });
306
348
  event.preventDefault();
307
349
  event.stopPropagation();
@@ -330,7 +372,7 @@ const contextmenuDirective = {
330
372
  let menus;
331
373
  let onItemClick;
332
374
  if (!value || typeof value !== "object") {
333
- console.warn("v-contextmenu: 需要绑定 object");
375
+ console.warn("v-contextmenu: 需要绑定 object 或者 Array");
334
376
  return;
335
377
  }
336
378
  for (const [key, val] of Object.entries(value)) {
@@ -341,7 +383,6 @@ const contextmenuDirective = {
341
383
  onItemClick = val;
342
384
  }
343
385
  }
344
- console.log(binding);
345
386
  const options = {
346
387
  menus: menus,
347
388
  zIndex: binding.arg ? parseInt(binding.arg) : 9999,
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../node_modules/style-inject/dist/style-inject.es.js"],"sourcesContent":["function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE;AAC/B,EAAE,KAAK,GAAG,KAAK,KAAK,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC;AACjC,EAAE,IAAI,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;AAC9B;AACA,EAAE,IAAI,CAAC,GAAG,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,EAAE,OAAO,EAAE;AAC1D;AACA,EAAE,IAAI,IAAI,GAAG,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACvE,EAAE,IAAI,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;AAC9C,EAAE,KAAK,CAAC,IAAI,GAAG,UAAU,CAAC;AAC1B;AACA,EAAE,IAAI,QAAQ,KAAK,KAAK,EAAE;AAC1B,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE;AACzB,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;AAChD,KAAK,MAAM;AACX,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AAC9B,KAAK;AACL,GAAG,MAAM;AACT,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AAC5B,GAAG;AACH;AACA,EAAE,IAAI,KAAK,CAAC,UAAU,EAAE;AACxB,IAAI,KAAK,CAAC,UAAU,CAAC,OAAO,GAAG,GAAG,CAAC;AACnC,GAAG,MAAM;AACT,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;AACpD,GAAG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"index.cjs.js","sources":["../node_modules/style-inject/dist/style-inject.es.js"],"sourcesContent":["function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE;AAC/B,EAAE,KAAK,GAAG,KAAK,KAAK,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC;AACjC,EAAE,IAAI,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;AAC9B;AACA,EAAE,IAAI,CAAC,GAAG,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,EAAE,OAAO,EAAE;AAC1D;AACA,EAAE,IAAI,IAAI,GAAG,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACvE,EAAE,IAAI,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;AAC9C,EAAE,KAAK,CAAC,IAAI,GAAG,UAAU,CAAC;AAC1B;AACA,EAAE,IAAI,QAAQ,KAAK,KAAK,EAAE;AAC1B,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE;AACzB,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;AAChD,KAAK,MAAM;AACX,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AAC9B,KAAK;AACL,GAAG,MAAM;AACT,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AAC5B,GAAG;AACH;AACA,EAAE,IAAI,KAAK,CAAC,UAAU,EAAE;AACxB,IAAI,KAAK,CAAC,UAAU,CAAC,OAAO,GAAG,GAAG,CAAC;AACnC,GAAG,MAAM;AACT,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;AACpD,GAAG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","x_google_ignoreList":[0]}
package/dist/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import { defineComponent, watch, useTemplateRef, ref, computed, onMounted, onUnmounted, openBlock, createBlock, Transition, withCtx, createElementBlock, normalizeStyle, withModifiers, createElementVNode, Fragment, renderList, createCommentVNode, normalizeClass, toDisplayString, createApp, h } from 'vue';
1
+ import { defineComponent, useTemplateRef, ref, watchEffect, computed, onMounted, onUnmounted, openBlock, createBlock, Transition, withCtx, createElementBlock, normalizeStyle, withModifiers, createElementVNode, Fragment, renderList, createCommentVNode, normalizeClass, createTextVNode, toDisplayString, resolveDynamicComponent, withDirectives, vShow, nextTick, reactive, createApp, h } from 'vue';
2
2
 
3
3
  const _hoisted_1 = { class: "context-menu-content" };
4
4
  const _hoisted_2 = {
@@ -6,21 +6,32 @@ const _hoisted_2 = {
6
6
  class: "context-menu-divider"
7
7
  };
8
8
  const _hoisted_3 = ["onClick", "onMouseenter"];
9
- const _hoisted_4 = {
9
+ const _hoisted_4 = { class: "menu-content" };
10
+ const _hoisted_5 = {
10
11
  key: 0,
11
12
  class: "menu-icon"
12
13
  };
13
- const _hoisted_5 = { class: "menu-label" };
14
- const _hoisted_6 = {
14
+ const _hoisted_6 = { class: "menu-icon" };
15
+ const _hoisted_7 = { class: "menu-label" };
16
+ const _hoisted_8 = {
15
17
  key: 1,
18
+ class: "menu-label-right"
19
+ };
20
+ const _hoisted_9 = {
21
+ key: 0,
16
22
  class: "menu-arrow"
17
23
  };
18
- const _hoisted_7 = ["onClick"];
19
- const _hoisted_8 = {
24
+ const _hoisted_10 = ["onClick"];
25
+ const _hoisted_11 = {
20
26
  key: 0,
21
27
  class: "menu-icon"
22
28
  };
23
- const _hoisted_9 = { class: "menu-label" };
29
+ const _hoisted_12 = { class: "menu-icon" };
30
+ const _hoisted_13 = { class: "menu-label" };
31
+ const _hoisted_14 = {
32
+ key: 1,
33
+ class: "menu-label-right"
34
+ };
24
35
  var _sfc_main = /*@__PURE__*/ defineComponent({
25
36
  __name: 'ContextMenu',
26
37
  props: {
@@ -29,25 +40,29 @@ var _sfc_main = /*@__PURE__*/ defineComponent({
29
40
  y: { type: Number, required: true },
30
41
  menus: { type: [Object, Array], required: true },
31
42
  zIndex: { type: Number, required: false, default: 9999 },
32
- maxWidth: { type: Number, required: false, default: 300 },
43
+ maxWidth: { type: Number, required: false },
33
44
  minWidth: { type: Number, required: false, default: 150 }
34
45
  },
35
46
  emits: ["update:visible", "item-click"],
36
47
  setup(__props, { emit: __emit }) {
37
48
  const emit = __emit;
38
- watch(() => __props.visible, (newVal) => {
39
- if (!newVal)
40
- subMenuVisible.value = {};
41
- });
42
49
  const menuRef = useTemplateRef("menuRef");
43
50
  const subMenuVisible = ref({});
44
51
  const subMenuStyle = ref({ top: "0px", left: "0px" });
52
+ const parentShow = ref(false);
53
+ const childShow = ref(false);
54
+ watchEffect(() => {
55
+ __props.x;
56
+ if (!__props.visible)
57
+ subMenuVisible.value = {};
58
+ parentShow.value = false;
59
+ childShow.value = false;
60
+ });
45
61
  // 菜单样式
46
62
  const menuStyle = computed(() => ({
47
63
  left: `${__props.x}px`,
48
64
  top: `${__props.y}px`,
49
65
  zIndex: __props.zIndex,
50
- maxWidth: `${__props.maxWidth}px`,
51
66
  minWidth: `${__props.minWidth}px`,
52
67
  }));
53
68
  // 当前显示的菜单
@@ -93,6 +108,16 @@ var _sfc_main = /*@__PURE__*/ defineComponent({
93
108
  emit("update:visible", false);
94
109
  subMenuVisible.value = {};
95
110
  };
111
+ /**
112
+ * 判断是否显示陪衬样式
113
+ * @param e icon的vnode
114
+ */
115
+ const isShow = (e, type) => {
116
+ if (type === "parent" && !parentShow.value)
117
+ parentShow.value = !!e;
118
+ if (type === "child" && !childShow.value)
119
+ childShow.value = !!e;
120
+ };
96
121
  onMounted(() => {
97
122
  // 监听全局点击
98
123
  document.addEventListener("click", handleClickOutside);
@@ -120,6 +145,7 @@ var _sfc_main = /*@__PURE__*/ defineComponent({
120
145
  return (openBlock(), createElementBlock(Fragment, {
121
146
  key: item.id
122
147
  }, [
148
+ createCommentVNode(" <span v-show=\"false\">{{ ds(item.icon) }}</span> "),
123
149
  createCommentVNode(" 分隔线 "),
124
150
  (item.divider)
125
151
  ? (openBlock(), createElementBlock("div", _hoisted_2))
@@ -136,24 +162,34 @@ var _sfc_main = /*@__PURE__*/ defineComponent({
136
162
  onClick: ($event) => (handleClick(item)),
137
163
  onMouseenter: ($event) => (handleMouseEnter(item))
138
164
  }, [
139
- createCommentVNode(" 图标 "),
140
- (item.icon)
141
- ? (openBlock(), createElementBlock("span", _hoisted_4, [
142
- createElementVNode("i", {
143
- class: normalizeClass(item.icon)
144
- }, null, 2 /* CLASS */)
145
- ]))
146
- : createCommentVNode("v-if", true),
147
- createCommentVNode(" 标签 "),
148
- createElementVNode("span", _hoisted_5, toDisplayString(item.label), 1 /* TEXT */),
165
+ createElementVNode("div", _hoisted_4, [
166
+ createCommentVNode(" 图标 "),
167
+ (item.icon)
168
+ ? (openBlock(), createElementBlock("span", _hoisted_5, [
169
+ createTextVNode(toDisplayString(isShow(item.icon(), "parent")) + " ", 1 /* TEXT */),
170
+ (openBlock(), createBlock(resolveDynamicComponent(item.icon())))
171
+ ]))
172
+ : createCommentVNode("v-if", true),
173
+ withDirectives(createElementVNode("span", _hoisted_6, null, 512 /* NEED_PATCH */), [
174
+ [vShow, parentShow.value && item.icon === undefined]
175
+ ]),
176
+ createCommentVNode(" 标签 "),
177
+ createElementVNode("span", _hoisted_7, toDisplayString(item.label), 1 /* TEXT */),
178
+ createCommentVNode(" 右标签 "),
179
+ (item.rightLabel)
180
+ ? (openBlock(), createElementBlock("span", _hoisted_8, [
181
+ (openBlock(), createBlock(resolveDynamicComponent(item.rightLabel())))
182
+ ]))
183
+ : createCommentVNode("v-if", true)
184
+ ]),
149
185
  createCommentVNode(" 子菜单箭头 "),
150
186
  (item.children)
151
- ? (openBlock(), createElementBlock("span", _hoisted_6, "▶"))
187
+ ? (openBlock(), createElementBlock("span", _hoisted_9, "▶"))
152
188
  : createCommentVNode("v-if", true),
153
189
  createCommentVNode(" 子菜单 "),
154
190
  (item.children && subMenuVisible.value[item.id])
155
191
  ? (openBlock(), createElementBlock("div", {
156
- key: 2,
192
+ key: 1,
157
193
  class: "context-submenu",
158
194
  style: normalizeStyle(subMenuStyle.value)
159
195
  }, [
@@ -164,14 +200,21 @@ var _sfc_main = /*@__PURE__*/ defineComponent({
164
200
  onClick: withModifiers(($event) => (handleClick(child)), ["stop"])
165
201
  }, [
166
202
  (child.icon)
167
- ? (openBlock(), createElementBlock("span", _hoisted_8, [
168
- createElementVNode("i", {
169
- class: normalizeClass(child.icon)
170
- }, null, 2 /* CLASS */)
203
+ ? (openBlock(), createElementBlock("span", _hoisted_11, [
204
+ createTextVNode(toDisplayString(isShow(child.icon(), "child")) + " ", 1 /* TEXT */),
205
+ (openBlock(), createBlock(resolveDynamicComponent(child.icon())))
171
206
  ]))
172
207
  : createCommentVNode("v-if", true),
173
- createElementVNode("span", _hoisted_9, toDisplayString(child.label), 1 /* TEXT */)
174
- ], 10 /* CLASS, PROPS */, _hoisted_7));
208
+ withDirectives(createElementVNode("span", _hoisted_12, null, 512 /* NEED_PATCH */), [
209
+ [vShow, childShow.value && child.icon === undefined]
210
+ ]),
211
+ createElementVNode("span", _hoisted_13, toDisplayString(item.label), 1 /* TEXT */),
212
+ (item.rightLabel)
213
+ ? (openBlock(), createElementBlock("span", _hoisted_14, [
214
+ (openBlock(), createBlock(resolveDynamicComponent(item.rightLabel())))
215
+ ]))
216
+ : createCommentVNode("v-if", true)
217
+ ], 10 /* CLASS, PROPS */, _hoisted_10));
175
218
  }), 128 /* KEYED_FRAGMENT */))
176
219
  ], 4 /* STYLE */))
177
220
  : createCommentVNode("v-if", true)
@@ -216,7 +259,7 @@ function styleInject(css, ref) {
216
259
  }
217
260
  }
218
261
 
219
- var css_248z = "\n.vue-context-menu[data-v-cbb365ae] {\r\n position: fixed;\r\n background: white;\r\n border: 1px solid #dcdfe6;\r\n border-radius: 4px;\r\n box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);\r\n padding: 5px 0;\r\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\r\n font-size: 14px;\r\n color: #606266;\n}\n.context-menu-content[data-v-cbb365ae] {\r\n width: 100%;\n}\n.context-menu-item[data-v-cbb365ae] {\r\n padding: 8px 20px;\r\n cursor: pointer;\r\n display: flex;\r\n align-items: center;\r\n position: relative;\r\n transition: background-color 0.2s;\n}\n.context-menu-item[data-v-cbb365ae]:hover:not(.disabled) {\r\n background-color: #f5f7fa;\n}\n.context-menu-item.disabled[data-v-cbb365ae] {\r\n color: #c0c4cc;\r\n cursor: not-allowed;\n}\n.context-menu-divider[data-v-cbb365ae] {\r\n height: 1px;\r\n background-color: #ebeef5;\r\n margin: 5px 0;\n}\n.menu-icon[data-v-cbb365ae] {\r\n margin-right: 8px;\r\n font-size: 14px;\r\n width: 14px;\r\n display: inline-flex;\r\n justify-content: center;\n}\n.menu-label[data-v-cbb365ae] {\r\n flex: 1;\r\n white-space: nowrap;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\n}\n.menu-arrow[data-v-cbb365ae] {\r\n margin-left: 8px;\r\n font-size: 12px;\r\n color: #909399;\n}\n.context-submenu[data-v-cbb365ae] {\r\n position: absolute;\r\n background: white;\r\n border: 1px solid #dcdfe6;\r\n border-radius: 4px;\r\n box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);\r\n min-width: 150px;\r\n z-index: 10000;\n}\r\n\r\n/* 动画效果 */\n.context-menu-fade-enter-active[data-v-cbb365ae],\r\n.context-menu-fade-leave-active[data-v-cbb365ae] {\r\n transition: opacity 0.2s, transform 0.2s;\n}\n.context-menu-fade-enter-from[data-v-cbb365ae],\r\n.context-menu-fade-leave-to[data-v-cbb365ae] {\r\n opacity: 0;\r\n transform: scale(0.95);\n}\r\n";
262
+ var css_248z = "\n.vue-context-menu[data-v-cbb365ae] {\r\n position: fixed;\r\n background: white;\r\n border: 1px solid #dcdfe6;\r\n border-radius: 4px;\r\n box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);\r\n padding: 5px;\r\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\r\n font-size: 14px;\r\n color: #606266;\n}\n.context-menu-content[data-v-cbb365ae] {\r\n width: 100%;\n}\n.context-menu-item[data-v-cbb365ae] {\r\n padding: 8px 20px;\r\n cursor: pointer;\r\n display: flex;\r\n align-items: center;\r\n position: relative;\r\n transition: background-color 0.2s;\n}\n.context-menu-item[data-v-cbb365ae]:hover:not(.disabled) {\r\n background-color: #f1f3f5;\r\n border-radius: 3px;\n}\n.context-menu-item.disabled[data-v-cbb365ae] {\r\n color: #c0c4cc;\r\n cursor: not-allowed;\n}\n.context-menu-divider[data-v-cbb365ae] {\r\n height: 1px;\r\n background-color: #ebeef5;\r\n margin: 5px 0;\n}\n.menu-icon[data-v-cbb365ae] {\r\n margin-right: 10px;\r\n font-size: 14px;\r\n width: 14px;\r\n display: inline-flex;\r\n justify-content: center;\n}\n.menu-content[data-v-cbb365ae] {\r\n display: flex;\r\n flex: 1;\r\n justify-content: space-between;\n}\n.menu-label[data-v-cbb365ae] {\r\n flex: 1;\r\n white-space: nowrap;\n}\n.menu-label-right[data-v-cbb365ae] {\r\n margin-left: 50px;\r\n flex: 1;\r\n white-space: nowrap;\n}\n.menu-arrow[data-v-cbb365ae] {\r\n margin-left: 8px;\r\n font-size: 12px;\r\n color: #909399;\n}\n.context-submenu[data-v-cbb365ae] {\r\n position: absolute;\r\n background: white;\r\n border: 1px solid #dcdfe6;\r\n border-radius: 4px;\r\n box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);\r\n min-width: 150px;\r\n z-index: 10000;\n}\r\n\r\n/* 动画效果 */\n.context-menu-fade-enter-active[data-v-cbb365ae],\r\n.context-menu-fade-leave-active[data-v-cbb365ae] {\r\n transition: opacity 0.2s, transform 0.2s;\n}\n.context-menu-fade-enter-from[data-v-cbb365ae],\r\n.context-menu-fade-leave-to[data-v-cbb365ae] {\r\n opacity: 0;\r\n transform: scale(0.95);\n}\r\n";
220
263
  styleInject(css_248z);
221
264
 
222
265
  var _export_sfc = (sfc, props) => {
@@ -234,6 +277,7 @@ let menuInstance = null;
234
277
  let container = null;
235
278
  let currentElement = null;
236
279
  let mouseEvent = null;
280
+ let menuData = null; // 响应式数据存储
237
281
  let currentOnItemClick = null;
238
282
  /**
239
283
  * 创建vue模版实例
@@ -241,23 +285,22 @@ let currentOnItemClick = null;
241
285
  const createMenuInstance = () => {
242
286
  container = document.createElement("div");
243
287
  document.body.appendChild(container);
288
+ // 创建响应式数据
289
+ menuData = reactive({
290
+ visible: false,
291
+ x: 0,
292
+ y: 0,
293
+ menus: [],
294
+ });
244
295
  menuInstance = createApp({
245
- data() {
246
- return {
247
- visible: false,
248
- x: 0,
249
- y: 0,
250
- menus: [],
251
- };
252
- },
253
296
  render() {
254
297
  return h(ContextMenu, {
255
- visible: this.visible,
256
- x: this.x,
257
- y: this.y,
258
- menus: this.menus,
298
+ visible: menuData.visible,
299
+ x: menuData.x,
300
+ y: menuData.y,
301
+ menus: menuData.menus,
259
302
  "onUpdate:visible": (val) => {
260
- this.visible = val;
303
+ menuData.visible = val;
261
304
  },
262
305
  onItemClick: (item) => {
263
306
  if (currentOnItemClick && currentElement && mouseEvent)
@@ -281,13 +324,12 @@ function showContextMenu(event, menus, element, onItemClick) {
281
324
  currentOnItemClick = onItemClick || null;
282
325
  currentElement = element || event.currentTarget;
283
326
  mouseEvent = event;
284
- const vm = menuInstance._instance.proxy;
285
- vm.visible = true;
286
- vm.menus = menus;
287
- vm.x = event.clientX;
288
- vm.y = event.clientY;
327
+ menuData.visible = true;
328
+ menuData.menus = menus;
329
+ menuData.x = event.clientX;
330
+ menuData.y = event.clientY;
289
331
  // 防止菜单超出屏幕
290
- requestAnimationFrame(() => {
332
+ nextTick(() => {
291
333
  const menuEl = container === null || container === void 0 ? void 0 : container.querySelector(".vue-context-menu");
292
334
  if (!menuEl)
293
335
  return;
@@ -295,9 +337,9 @@ function showContextMenu(event, menus, element, onItemClick) {
295
337
  const viewportWidth = window.innerWidth;
296
338
  const viewportHeight = window.innerHeight;
297
339
  if (rect.right > viewportWidth)
298
- vm.x = viewportWidth - rect.width - 5;
340
+ menuData.x = viewportWidth - rect.width - 5;
299
341
  if (rect.bottom > viewportHeight)
300
- vm.y = viewportHeight - rect.height - 5;
342
+ menuData.y = viewportHeight - rect.height - 5;
301
343
  });
302
344
  event.preventDefault();
303
345
  event.stopPropagation();
@@ -326,7 +368,7 @@ const contextmenuDirective = {
326
368
  let menus;
327
369
  let onItemClick;
328
370
  if (!value || typeof value !== "object") {
329
- console.warn("v-contextmenu: 需要绑定 object");
371
+ console.warn("v-contextmenu: 需要绑定 object 或者 Array");
330
372
  return;
331
373
  }
332
374
  for (const [key, val] of Object.entries(value)) {
@@ -337,7 +379,6 @@ const contextmenuDirective = {
337
379
  onItemClick = val;
338
380
  }
339
381
  }
340
- console.log(binding);
341
382
  const options = {
342
383
  menus: menus,
343
384
  zIndex: binding.arg ? parseInt(binding.arg) : 9999,
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../node_modules/style-inject/dist/style-inject.es.js"],"sourcesContent":["function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE;AAC/B,EAAE,KAAK,GAAG,KAAK,KAAK,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC;AACjC,EAAE,IAAI,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;AAC9B;AACA,EAAE,IAAI,CAAC,GAAG,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,EAAE,OAAO,EAAE;AAC1D;AACA,EAAE,IAAI,IAAI,GAAG,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACvE,EAAE,IAAI,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;AAC9C,EAAE,KAAK,CAAC,IAAI,GAAG,UAAU,CAAC;AAC1B;AACA,EAAE,IAAI,QAAQ,KAAK,KAAK,EAAE;AAC1B,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE;AACzB,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;AAChD,KAAK,MAAM;AACX,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AAC9B,KAAK;AACL,GAAG,MAAM;AACT,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AAC5B,GAAG;AACH;AACA,EAAE,IAAI,KAAK,CAAC,UAAU,EAAE;AACxB,IAAI,KAAK,CAAC,UAAU,CAAC,OAAO,GAAG,GAAG,CAAC;AACnC,GAAG,MAAM;AACT,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;AACpD,GAAG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"index.esm.js","sources":["../node_modules/style-inject/dist/style-inject.es.js"],"sourcesContent":["function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE;AAC/B,EAAE,KAAK,GAAG,KAAK,KAAK,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC;AACjC,EAAE,IAAI,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;AAC9B;AACA,EAAE,IAAI,CAAC,GAAG,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,EAAE,OAAO,EAAE;AAC1D;AACA,EAAE,IAAI,IAAI,GAAG,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACvE,EAAE,IAAI,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;AAC9C,EAAE,KAAK,CAAC,IAAI,GAAG,UAAU,CAAC;AAC1B;AACA,EAAE,IAAI,QAAQ,KAAK,KAAK,EAAE;AAC1B,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE;AACzB,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;AAChD,KAAK,MAAM;AACX,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AAC9B,KAAK;AACL,GAAG,MAAM;AACT,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AAC5B,GAAG;AACH;AACA,EAAE,IAAI,KAAK,CAAC,UAAU,EAAE;AACxB,IAAI,KAAK,CAAC,UAAU,CAAC,OAAO,GAAG,GAAG,CAAC;AACnC,GAAG,MAAM;AACT,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;AACpD,GAAG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","x_google_ignoreList":[0]}
package/dist/index.umd.js CHANGED
@@ -10,21 +10,32 @@
10
10
  class: "context-menu-divider"
11
11
  };
12
12
  const _hoisted_3 = ["onClick", "onMouseenter"];
13
- const _hoisted_4 = {
13
+ const _hoisted_4 = { class: "menu-content" };
14
+ const _hoisted_5 = {
14
15
  key: 0,
15
16
  class: "menu-icon"
16
17
  };
17
- const _hoisted_5 = { class: "menu-label" };
18
- const _hoisted_6 = {
18
+ const _hoisted_6 = { class: "menu-icon" };
19
+ const _hoisted_7 = { class: "menu-label" };
20
+ const _hoisted_8 = {
19
21
  key: 1,
22
+ class: "menu-label-right"
23
+ };
24
+ const _hoisted_9 = {
25
+ key: 0,
20
26
  class: "menu-arrow"
21
27
  };
22
- const _hoisted_7 = ["onClick"];
23
- const _hoisted_8 = {
28
+ const _hoisted_10 = ["onClick"];
29
+ const _hoisted_11 = {
24
30
  key: 0,
25
31
  class: "menu-icon"
26
32
  };
27
- const _hoisted_9 = { class: "menu-label" };
33
+ const _hoisted_12 = { class: "menu-icon" };
34
+ const _hoisted_13 = { class: "menu-label" };
35
+ const _hoisted_14 = {
36
+ key: 1,
37
+ class: "menu-label-right"
38
+ };
28
39
  var _sfc_main = /*@__PURE__*/ vue.defineComponent({
29
40
  __name: 'ContextMenu',
30
41
  props: {
@@ -33,25 +44,29 @@
33
44
  y: { type: Number, required: true },
34
45
  menus: { type: [Object, Array], required: true },
35
46
  zIndex: { type: Number, required: false, default: 9999 },
36
- maxWidth: { type: Number, required: false, default: 300 },
47
+ maxWidth: { type: Number, required: false },
37
48
  minWidth: { type: Number, required: false, default: 150 }
38
49
  },
39
50
  emits: ["update:visible", "item-click"],
40
51
  setup(__props, { emit: __emit }) {
41
52
  const emit = __emit;
42
- vue.watch(() => __props.visible, (newVal) => {
43
- if (!newVal)
44
- subMenuVisible.value = {};
45
- });
46
53
  const menuRef = vue.useTemplateRef("menuRef");
47
54
  const subMenuVisible = vue.ref({});
48
55
  const subMenuStyle = vue.ref({ top: "0px", left: "0px" });
56
+ const parentShow = vue.ref(false);
57
+ const childShow = vue.ref(false);
58
+ vue.watchEffect(() => {
59
+ __props.x;
60
+ if (!__props.visible)
61
+ subMenuVisible.value = {};
62
+ parentShow.value = false;
63
+ childShow.value = false;
64
+ });
49
65
  // 菜单样式
50
66
  const menuStyle = vue.computed(() => ({
51
67
  left: `${__props.x}px`,
52
68
  top: `${__props.y}px`,
53
69
  zIndex: __props.zIndex,
54
- maxWidth: `${__props.maxWidth}px`,
55
70
  minWidth: `${__props.minWidth}px`,
56
71
  }));
57
72
  // 当前显示的菜单
@@ -97,6 +112,16 @@
97
112
  emit("update:visible", false);
98
113
  subMenuVisible.value = {};
99
114
  };
115
+ /**
116
+ * 判断是否显示陪衬样式
117
+ * @param e icon的vnode
118
+ */
119
+ const isShow = (e, type) => {
120
+ if (type === "parent" && !parentShow.value)
121
+ parentShow.value = !!e;
122
+ if (type === "child" && !childShow.value)
123
+ childShow.value = !!e;
124
+ };
100
125
  vue.onMounted(() => {
101
126
  // 监听全局点击
102
127
  document.addEventListener("click", handleClickOutside);
@@ -124,6 +149,7 @@
124
149
  return (vue.openBlock(), vue.createElementBlock(vue.Fragment, {
125
150
  key: item.id
126
151
  }, [
152
+ vue.createCommentVNode(" <span v-show=\"false\">{{ ds(item.icon) }}</span> "),
127
153
  vue.createCommentVNode(" 分隔线 "),
128
154
  (item.divider)
129
155
  ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2))
@@ -140,24 +166,34 @@
140
166
  onClick: ($event) => (handleClick(item)),
141
167
  onMouseenter: ($event) => (handleMouseEnter(item))
142
168
  }, [
143
- vue.createCommentVNode(" 图标 "),
144
- (item.icon)
145
- ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_4, [
146
- vue.createElementVNode("i", {
147
- class: vue.normalizeClass(item.icon)
148
- }, null, 2 /* CLASS */)
149
- ]))
150
- : vue.createCommentVNode("v-if", true),
151
- vue.createCommentVNode(" 标签 "),
152
- vue.createElementVNode("span", _hoisted_5, vue.toDisplayString(item.label), 1 /* TEXT */),
169
+ vue.createElementVNode("div", _hoisted_4, [
170
+ vue.createCommentVNode(" 图标 "),
171
+ (item.icon)
172
+ ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_5, [
173
+ vue.createTextVNode(vue.toDisplayString(isShow(item.icon(), "parent")) + " ", 1 /* TEXT */),
174
+ (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(item.icon())))
175
+ ]))
176
+ : vue.createCommentVNode("v-if", true),
177
+ vue.withDirectives(vue.createElementVNode("span", _hoisted_6, null, 512 /* NEED_PATCH */), [
178
+ [vue.vShow, parentShow.value && item.icon === undefined]
179
+ ]),
180
+ vue.createCommentVNode(" 标签 "),
181
+ vue.createElementVNode("span", _hoisted_7, vue.toDisplayString(item.label), 1 /* TEXT */),
182
+ vue.createCommentVNode(" 右标签 "),
183
+ (item.rightLabel)
184
+ ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_8, [
185
+ (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(item.rightLabel())))
186
+ ]))
187
+ : vue.createCommentVNode("v-if", true)
188
+ ]),
153
189
  vue.createCommentVNode(" 子菜单箭头 "),
154
190
  (item.children)
155
- ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_6, "▶"))
191
+ ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_9, "▶"))
156
192
  : vue.createCommentVNode("v-if", true),
157
193
  vue.createCommentVNode(" 子菜单 "),
158
194
  (item.children && subMenuVisible.value[item.id])
159
195
  ? (vue.openBlock(), vue.createElementBlock("div", {
160
- key: 2,
196
+ key: 1,
161
197
  class: "context-submenu",
162
198
  style: vue.normalizeStyle(subMenuStyle.value)
163
199
  }, [
@@ -168,14 +204,21 @@
168
204
  onClick: vue.withModifiers(($event) => (handleClick(child)), ["stop"])
169
205
  }, [
170
206
  (child.icon)
171
- ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_8, [
172
- vue.createElementVNode("i", {
173
- class: vue.normalizeClass(child.icon)
174
- }, null, 2 /* CLASS */)
207
+ ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_11, [
208
+ vue.createTextVNode(vue.toDisplayString(isShow(child.icon(), "child")) + " ", 1 /* TEXT */),
209
+ (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(child.icon())))
175
210
  ]))
176
211
  : vue.createCommentVNode("v-if", true),
177
- vue.createElementVNode("span", _hoisted_9, vue.toDisplayString(child.label), 1 /* TEXT */)
178
- ], 10 /* CLASS, PROPS */, _hoisted_7));
212
+ vue.withDirectives(vue.createElementVNode("span", _hoisted_12, null, 512 /* NEED_PATCH */), [
213
+ [vue.vShow, childShow.value && child.icon === undefined]
214
+ ]),
215
+ vue.createElementVNode("span", _hoisted_13, vue.toDisplayString(item.label), 1 /* TEXT */),
216
+ (item.rightLabel)
217
+ ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_14, [
218
+ (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(item.rightLabel())))
219
+ ]))
220
+ : vue.createCommentVNode("v-if", true)
221
+ ], 10 /* CLASS, PROPS */, _hoisted_10));
179
222
  }), 128 /* KEYED_FRAGMENT */))
180
223
  ], 4 /* STYLE */))
181
224
  : vue.createCommentVNode("v-if", true)
@@ -220,7 +263,7 @@
220
263
  }
221
264
  }
222
265
 
223
- var css_248z = "\n.vue-context-menu[data-v-cbb365ae] {\r\n position: fixed;\r\n background: white;\r\n border: 1px solid #dcdfe6;\r\n border-radius: 4px;\r\n box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);\r\n padding: 5px 0;\r\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\r\n font-size: 14px;\r\n color: #606266;\n}\n.context-menu-content[data-v-cbb365ae] {\r\n width: 100%;\n}\n.context-menu-item[data-v-cbb365ae] {\r\n padding: 8px 20px;\r\n cursor: pointer;\r\n display: flex;\r\n align-items: center;\r\n position: relative;\r\n transition: background-color 0.2s;\n}\n.context-menu-item[data-v-cbb365ae]:hover:not(.disabled) {\r\n background-color: #f5f7fa;\n}\n.context-menu-item.disabled[data-v-cbb365ae] {\r\n color: #c0c4cc;\r\n cursor: not-allowed;\n}\n.context-menu-divider[data-v-cbb365ae] {\r\n height: 1px;\r\n background-color: #ebeef5;\r\n margin: 5px 0;\n}\n.menu-icon[data-v-cbb365ae] {\r\n margin-right: 8px;\r\n font-size: 14px;\r\n width: 14px;\r\n display: inline-flex;\r\n justify-content: center;\n}\n.menu-label[data-v-cbb365ae] {\r\n flex: 1;\r\n white-space: nowrap;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\n}\n.menu-arrow[data-v-cbb365ae] {\r\n margin-left: 8px;\r\n font-size: 12px;\r\n color: #909399;\n}\n.context-submenu[data-v-cbb365ae] {\r\n position: absolute;\r\n background: white;\r\n border: 1px solid #dcdfe6;\r\n border-radius: 4px;\r\n box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);\r\n min-width: 150px;\r\n z-index: 10000;\n}\r\n\r\n/* 动画效果 */\n.context-menu-fade-enter-active[data-v-cbb365ae],\r\n.context-menu-fade-leave-active[data-v-cbb365ae] {\r\n transition: opacity 0.2s, transform 0.2s;\n}\n.context-menu-fade-enter-from[data-v-cbb365ae],\r\n.context-menu-fade-leave-to[data-v-cbb365ae] {\r\n opacity: 0;\r\n transform: scale(0.95);\n}\r\n";
266
+ var css_248z = "\n.vue-context-menu[data-v-cbb365ae] {\r\n position: fixed;\r\n background: white;\r\n border: 1px solid #dcdfe6;\r\n border-radius: 4px;\r\n box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);\r\n padding: 5px;\r\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\r\n font-size: 14px;\r\n color: #606266;\n}\n.context-menu-content[data-v-cbb365ae] {\r\n width: 100%;\n}\n.context-menu-item[data-v-cbb365ae] {\r\n padding: 8px 20px;\r\n cursor: pointer;\r\n display: flex;\r\n align-items: center;\r\n position: relative;\r\n transition: background-color 0.2s;\n}\n.context-menu-item[data-v-cbb365ae]:hover:not(.disabled) {\r\n background-color: #f1f3f5;\r\n border-radius: 3px;\n}\n.context-menu-item.disabled[data-v-cbb365ae] {\r\n color: #c0c4cc;\r\n cursor: not-allowed;\n}\n.context-menu-divider[data-v-cbb365ae] {\r\n height: 1px;\r\n background-color: #ebeef5;\r\n margin: 5px 0;\n}\n.menu-icon[data-v-cbb365ae] {\r\n margin-right: 10px;\r\n font-size: 14px;\r\n width: 14px;\r\n display: inline-flex;\r\n justify-content: center;\n}\n.menu-content[data-v-cbb365ae] {\r\n display: flex;\r\n flex: 1;\r\n justify-content: space-between;\n}\n.menu-label[data-v-cbb365ae] {\r\n flex: 1;\r\n white-space: nowrap;\n}\n.menu-label-right[data-v-cbb365ae] {\r\n margin-left: 50px;\r\n flex: 1;\r\n white-space: nowrap;\n}\n.menu-arrow[data-v-cbb365ae] {\r\n margin-left: 8px;\r\n font-size: 12px;\r\n color: #909399;\n}\n.context-submenu[data-v-cbb365ae] {\r\n position: absolute;\r\n background: white;\r\n border: 1px solid #dcdfe6;\r\n border-radius: 4px;\r\n box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);\r\n min-width: 150px;\r\n z-index: 10000;\n}\r\n\r\n/* 动画效果 */\n.context-menu-fade-enter-active[data-v-cbb365ae],\r\n.context-menu-fade-leave-active[data-v-cbb365ae] {\r\n transition: opacity 0.2s, transform 0.2s;\n}\n.context-menu-fade-enter-from[data-v-cbb365ae],\r\n.context-menu-fade-leave-to[data-v-cbb365ae] {\r\n opacity: 0;\r\n transform: scale(0.95);\n}\r\n";
224
267
  styleInject(css_248z);
225
268
 
226
269
  var _export_sfc = (sfc, props) => {
@@ -238,6 +281,7 @@
238
281
  let container = null;
239
282
  let currentElement = null;
240
283
  let mouseEvent = null;
284
+ let menuData = null; // 响应式数据存储
241
285
  let currentOnItemClick = null;
242
286
  /**
243
287
  * 创建vue模版实例
@@ -245,23 +289,22 @@
245
289
  const createMenuInstance = () => {
246
290
  container = document.createElement("div");
247
291
  document.body.appendChild(container);
292
+ // 创建响应式数据
293
+ menuData = vue.reactive({
294
+ visible: false,
295
+ x: 0,
296
+ y: 0,
297
+ menus: [],
298
+ });
248
299
  menuInstance = vue.createApp({
249
- data() {
250
- return {
251
- visible: false,
252
- x: 0,
253
- y: 0,
254
- menus: [],
255
- };
256
- },
257
300
  render() {
258
301
  return vue.h(ContextMenu, {
259
- visible: this.visible,
260
- x: this.x,
261
- y: this.y,
262
- menus: this.menus,
302
+ visible: menuData.visible,
303
+ x: menuData.x,
304
+ y: menuData.y,
305
+ menus: menuData.menus,
263
306
  "onUpdate:visible": (val) => {
264
- this.visible = val;
307
+ menuData.visible = val;
265
308
  },
266
309
  onItemClick: (item) => {
267
310
  if (currentOnItemClick && currentElement && mouseEvent)
@@ -285,13 +328,12 @@
285
328
  currentOnItemClick = onItemClick || null;
286
329
  currentElement = element || event.currentTarget;
287
330
  mouseEvent = event;
288
- const vm = menuInstance._instance.proxy;
289
- vm.visible = true;
290
- vm.menus = menus;
291
- vm.x = event.clientX;
292
- vm.y = event.clientY;
331
+ menuData.visible = true;
332
+ menuData.menus = menus;
333
+ menuData.x = event.clientX;
334
+ menuData.y = event.clientY;
293
335
  // 防止菜单超出屏幕
294
- requestAnimationFrame(() => {
336
+ vue.nextTick(() => {
295
337
  const menuEl = container === null || container === void 0 ? void 0 : container.querySelector(".vue-context-menu");
296
338
  if (!menuEl)
297
339
  return;
@@ -299,9 +341,9 @@
299
341
  const viewportWidth = window.innerWidth;
300
342
  const viewportHeight = window.innerHeight;
301
343
  if (rect.right > viewportWidth)
302
- vm.x = viewportWidth - rect.width - 5;
344
+ menuData.x = viewportWidth - rect.width - 5;
303
345
  if (rect.bottom > viewportHeight)
304
- vm.y = viewportHeight - rect.height - 5;
346
+ menuData.y = viewportHeight - rect.height - 5;
305
347
  });
306
348
  event.preventDefault();
307
349
  event.stopPropagation();
@@ -330,7 +372,7 @@
330
372
  let menus;
331
373
  let onItemClick;
332
374
  if (!value || typeof value !== "object") {
333
- console.warn("v-contextmenu: 需要绑定 object");
375
+ console.warn("v-contextmenu: 需要绑定 object 或者 Array");
334
376
  return;
335
377
  }
336
378
  for (const [key, val] of Object.entries(value)) {
@@ -341,7 +383,6 @@
341
383
  onItemClick = val;
342
384
  }
343
385
  }
344
- console.log(binding);
345
386
  const options = {
346
387
  menus: menus,
347
388
  zIndex: binding.arg ? parseInt(binding.arg) : 9999,
@@ -1 +1 @@
1
- {"version":3,"file":"index.umd.js","sources":["../node_modules/style-inject/dist/style-inject.es.js"],"sourcesContent":["function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAA,SAAS,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE;EAC/B,EAAE,KAAK,GAAG,KAAK,KAAK,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC;EACjC,EAAE,IAAI,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;AAC9B;EACA,EAAE,IAAI,CAAC,GAAG,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,EAAE,OAAO,EAAE;AAC1D;EACA,EAAE,IAAI,IAAI,GAAG,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;EACvE,EAAE,IAAI,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;EAC9C,EAAE,KAAK,CAAC,IAAI,GAAG,UAAU,CAAC;AAC1B;EACA,EAAE,IAAI,QAAQ,KAAK,KAAK,EAAE;EAC1B,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE;EACzB,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;EAChD,KAAK,MAAM;EACX,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;EAC9B,KAAK;EACL,GAAG,MAAM;EACT,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;EAC5B,GAAG;AACH;EACA,EAAE,IAAI,KAAK,CAAC,UAAU,EAAE;EACxB,IAAI,KAAK,CAAC,UAAU,CAAC,OAAO,GAAG,GAAG,CAAC;EACnC,GAAG,MAAM;EACT,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;EACpD,GAAG;EACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"index.umd.js","sources":["../node_modules/style-inject/dist/style-inject.es.js"],"sourcesContent":["function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAA,SAAS,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE;EAC/B,EAAE,KAAK,GAAG,KAAK,KAAK,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC;EACjC,EAAE,IAAI,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;AAC9B;EACA,EAAE,IAAI,CAAC,GAAG,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,EAAE,OAAO,EAAE;AAC1D;EACA,EAAE,IAAI,IAAI,GAAG,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;EACvE,EAAE,IAAI,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;EAC9C,EAAE,KAAK,CAAC,IAAI,GAAG,UAAU,CAAC;AAC1B;EACA,EAAE,IAAI,QAAQ,KAAK,KAAK,EAAE;EAC1B,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE;EACzB,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;EAChD,KAAK,MAAM;EACX,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;EAC9B,KAAK;EACL,GAAG,MAAM;EACT,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;EAC5B,GAAG;AACH;EACA,EAAE,IAAI,KAAK,CAAC,UAAU,EAAE;EACxB,IAAI,KAAK,CAAC,UAAU,CAAC,OAAO,GAAG,GAAG,CAAC;EACnC,GAAG,MAAM;EACT,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;EACpD,GAAG;EACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","x_google_ignoreList":[0]}
@@ -1,3 +1,4 @@
1
+ import { VNode } from "vue";
1
2
  declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
2
3
  visible: {
3
4
  type: BooleanConstructor;
@@ -23,14 +24,13 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
23
24
  maxWidth: {
24
25
  type: NumberConstructor;
25
26
  required: false;
26
- default: number;
27
27
  };
28
28
  minWidth: {
29
29
  type: NumberConstructor;
30
30
  required: false;
31
31
  default: number;
32
32
  };
33
- }>, (_ctx: any, _cache: any) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
33
+ }>, (_ctx: any, _cache: any) => VNode<import("vue").RendererNode, import("vue").RendererElement, {
34
34
  [key: string]: any;
35
35
  }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("update:visible" | "item-click")[], "update:visible" | "item-click", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
36
36
  visible: {
@@ -57,7 +57,6 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
57
57
  maxWidth: {
58
58
  type: NumberConstructor;
59
59
  required: false;
60
- default: number;
61
60
  };
62
61
  minWidth: {
63
62
  type: NumberConstructor;
@@ -69,7 +68,6 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
69
68
  "onItem-click"?: ((...args: any[]) => any) | undefined;
70
69
  }>, {
71
70
  zIndex: number;
72
- maxWidth: number;
73
71
  minWidth: number;
74
72
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
75
73
  export default _default;
@@ -1,7 +1,9 @@
1
+ import { VNode } from "vue";
1
2
  export interface MenuItem {
2
3
  id: string | number;
3
4
  label: string;
4
- icon?: string;
5
+ rightLabel?: () => VNode;
6
+ icon?: () => VNode;
5
7
  disabled?: boolean;
6
8
  divider?: boolean;
7
9
  children?: MenuItem[];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "vue3-context-menu-plus",
4
- "version": "2.0.7",
4
+ "version": "2.1.7",
5
5
  "description": "A Vue 3 context menu component that shows different menus based on different components",
6
6
  "main": "dist/index.cjs.js",
7
7
  "module": "dist/index.esm.js",