UNPKG

18.6 kBJavaScriptView Raw
1Object.defineProperty(exports, '__esModule', { value: true });
2
3var vue = require('vue');
4
5var bind = function bind(el, binding) {
6 var _binding$instance;
7
8 var contextmenuKey = binding.arg;
9
10 if (!contextmenuKey) {
11 console.error("参数有误");
12 return;
13 }
14
15 var contextmenuOptions = binding.value;
16 var contextmenuRef = vue.isRef(contextmenuKey) ? contextmenuKey.value : (_binding$instance = binding.instance) === null || _binding$instance === void 0 ? void 0 : _binding$instance.$refs[contextmenuKey];
17
18 if (!contextmenuRef) {
19 console.error("\u6CA1\u6709\u627E\u5230 ".concat(contextmenuKey, " \u5BF9\u5E94\u7684\u5B9E\u4F8B"));
20 return;
21 }
22
23 if (typeof contextmenuRef.addReference !== "function") {
24 console.error("".concat(contextmenuKey, " \u5BF9\u5E94\u7684\u5B9E\u4F8B\u4E0D\u662F VContextmenu"));
25 return;
26 }
27
28 el.$contextmenuKey = contextmenuKey;
29 contextmenuRef.addReference(el, contextmenuOptions);
30};
31
32var unbind = function unbind(el, binding) {
33 var _binding$instance2;
34
35 var contextmenuKey = el.$contextmenuKey;
36 if (!contextmenuKey) return;
37 var contextmenuRef = (_binding$instance2 = binding.instance) === null || _binding$instance2 === void 0 ? void 0 : _binding$instance2.$refs[contextmenuKey];
38 contextmenuRef === null || contextmenuRef === void 0 ? void 0 : contextmenuRef.removeReference(el);
39};
40
41var rebind = function rebind(el, binding) {
42 unbind(el, binding);
43 bind(el, binding);
44};
45
46var contextmenuDirective = {
47 mounted: bind,
48 updated: rebind,
49 beforeUnmount: unbind
50};
51
52var CLASSES = {
53 contextmenu: "v-contextmenu",
54 // 根元素
55 contextmenuIcon: "v-contextmenu-icon",
56 // icon
57 contextmenuInner: "v-contextmenu-inner",
58 // 菜单根元素
59 contextmenuDivider: "v-contextmenu-divider",
60 // 分割线
61 contextmenuItem: "v-contextmenu-item",
62 // 单个菜单
63 contextmenuItemHover: "v-contextmenu-item--hover",
64 // 单个菜单激活状态
65 contextmenuItemDisabled: "v-contextmenu-item--disabled",
66 // 单个菜单禁用状态
67 contextmenuGroup: "v-contextmenu-group",
68 // 按钮组根元素
69 contextmenuGroupTitle: "v-contextmenu-group__title",
70 // 按钮组标题
71 contextmenuGroupMenus: "v-contextmenu-group__menus",
72 // 按钮组按钮容器
73 contextmenuSubmenu: "v-contextmenu-submenu",
74 // 子菜单容器
75 contextmenuSubmenuTitle: "v-contextmenu-submenu__title",
76 // 子菜单标题
77 contextmenuSubmenuMenus: "v-contextmenu-submenu__menus",
78 // 子菜单
79 contextmenuSubmenuMenusTop: "v-contextmenu-submenu__menus--top",
80 // 子菜单 Top
81 contextmenuSubmenuMenusRight: "v-contextmenu-submenu__menus--right",
82 // 子菜单 Right
83 contextmenuSubmenuMenusBottom: "v-contextmenu-submenu__menus--bottom",
84 // 子菜 Bottom单
85 contextmenuSubmenuMenusLeft: "v-contextmenu-submenu__menus--left",
86 // 子菜单 Left
87 contextmenuSubmenuArrow: "v-contextmenu-submenu__arrow" // 子菜单标题 icon
88
89};
90
91function _isSlot(s) {
92 return typeof s === 'function' || Object.prototype.toString.call(s) === '[object Object]' && !vue.isVNode(s);
93}
94
95var DEFAULT_REFERENCE_OPTIONS = {
96 trigger: ["contextmenu"]
97};
98var Contextmenu = vue.defineComponent({
99 name: "VContextmenu",
100 props: {
101 modelValue: {
102 type: Boolean,
103 default: false
104 },
105 autoAjustPlacement: {
106 type: Boolean,
107 default: true
108 },
109 disabled: {
110 type: Boolean,
111 default: false
112 },
113 teleport: {
114 type: [String, Object],
115 default: function _default() {
116 return "body";
117 }
118 } // destroyOnHide: {
119 // type: Boolean,
120 // default: false,
121 // },
122
123 },
124 emits: ["show", "hide", "update:modelValue"],
125 setup: function setup(props, _ref) {
126 var emit = _ref.emit;
127 var contextmenuRef = vue.ref(null);
128 var visible = vue.ref(props.modelValue || false);
129
130 var toggle = function toggle(value) {
131 visible.value = value;
132 emit("update:modelValue", value);
133 };
134
135 var position = vue.ref({
136 top: 0,
137 left: 0
138 });
139 var style = vue.computed(function () {
140 return {
141 top: "".concat(position.value.top, "px"),
142 left: "".concat(position.value.left, "px")
143 };
144 });
145 var currentOptions = vue.ref(null);
146
147 var show = function show(evt, options) {
148 var targetOptions = evt instanceof Event ? options : evt;
149 var autoAjustPlacement = (targetOptions === null || targetOptions === void 0 ? void 0 : targetOptions.autoAjustPlacement) || props.autoAjustPlacement;
150 var targetPosition = {
151 top: (targetOptions === null || targetOptions === void 0 ? void 0 : targetOptions.top) || 0,
152 left: (targetOptions === null || targetOptions === void 0 ? void 0 : targetOptions.left) || 0
153 };
154
155 if (evt instanceof Event) {
156 var _targetOptions$top, _targetOptions$left;
157
158 evt.preventDefault();
159 targetPosition.top = (_targetOptions$top = targetOptions === null || targetOptions === void 0 ? void 0 : targetOptions.top) !== null && _targetOptions$top !== void 0 ? _targetOptions$top : evt.pageY;
160 targetPosition.left = (_targetOptions$left = targetOptions === null || targetOptions === void 0 ? void 0 : targetOptions.left) !== null && _targetOptions$left !== void 0 ? _targetOptions$left : evt.pageX;
161 }
162
163 toggle(true);
164 vue.nextTick(function () {
165 if (autoAjustPlacement) {
166 var el = contextmenuRef.value;
167 if (!el) return;
168 var width = el.clientWidth;
169 var height = el.clientHeight;
170
171 if (height + targetPosition.top >= window.innerHeight + window.scrollY) {
172 var targetTop = targetPosition.top - height;
173
174 if (targetTop > window.scrollY) {
175 targetPosition.top = targetTop;
176 }
177 }
178
179 if (width + targetPosition.left >= window.innerWidth + window.scrollX) {
180 var targetWidth = targetPosition.left - width;
181
182 if (targetWidth > window.scrollX) {
183 targetPosition.left = targetWidth;
184 }
185 }
186 }
187
188 position.value = targetPosition; // TODO: 添加回掉参数
189
190 emit("show");
191 });
192 };
193
194 var hide = function hide() {
195 currentOptions.value = null;
196 toggle(false); // TODO: 添加回掉参数
197
198 emit("hide");
199 };
200
201 var references = vue.reactive(new Map());
202 var currentReference = vue.ref();
203 var currentReferenceOptions = vue.computed(function () {
204 return currentReference.value && references.get(currentReference.value);
205 });
206
207 var addReference = function addReference(el, options) {
208 var triggers = function () {
209 if (options !== null && options !== void 0 && options.trigger) {
210 return Array.isArray(options.trigger) ? options.trigger : [options.trigger];
211 }
212
213 return DEFAULT_REFERENCE_OPTIONS.trigger;
214 }();
215
216 var handler = function handler(evt) {
217 if (props.disabled) return;
218 currentReference.value = el;
219 show(evt, {});
220 };
221
222 triggers.forEach(function (eventType) {
223 el.addEventListener(eventType, handler);
224 });
225 references.set(el, {
226 triggers: triggers,
227 handler: handler
228 });
229 };
230
231 var removeReference = function removeReference(el) {
232 var options = references.get(el);
233 if (!options) return;
234 options.triggers.forEach(function (eventType) {
235 el.removeEventListener(eventType, options.handler);
236 });
237 references.delete(el);
238 };
239
240 var onBodyClick = function onBodyClick(evt) {
241 if (!evt.target || !contextmenuRef.value || !currentReference.value) return;
242 var notOutside = contextmenuRef.value.contains(evt.target) || currentReferenceOptions.value && currentReferenceOptions.value.triggers.includes("click") && currentReference.value.contains(evt.target);
243
244 if (!notOutside) {
245 toggle(false);
246 }
247 }; // watch(props.modelValue, (value) => {
248 // if (value !== visible.value) {
249 // toggle(value);
250 // }
251 // });
252
253
254 vue.watch(visible, function (value) {
255 if (value) {
256 document.addEventListener("click", onBodyClick);
257 } else {
258 document.removeEventListener("click", onBodyClick);
259 }
260 });
261 vue.onBeforeUnmount(function () {
262 document.removeEventListener("click", onBodyClick);
263 });
264 vue.provide("visible", visible);
265 vue.provide("autoAjustPlacement", props.autoAjustPlacement);
266 vue.provide("show", show);
267 vue.provide("hide", hide);
268 return {
269 visible: visible,
270 style: style,
271 currentReferenceOptions: currentReferenceOptions,
272 currentOptions: currentOptions,
273 contextmenuRef: contextmenuRef,
274 addReference: addReference,
275 removeReference: removeReference,
276 toggle: toggle,
277 show: show,
278 hide: hide
279 };
280 },
281 methods: {
282 renderContent: function renderContent() {
283 var _this$$slots$default, _this$$slots;
284
285 return vue.withDirectives(vue.createVNode("div", {
286 "class": CLASSES.contextmenu,
287 "ref": "contextmenuRef",
288 "style": this.style
289 }, [vue.createVNode("ul", {
290 "class": CLASSES.contextmenuInner
291 }, [(_this$$slots$default = (_this$$slots = this.$slots).default) === null || _this$$slots$default === void 0 ? void 0 : _this$$slots$default.call(_this$$slots, {
292 triggerOptions: "currentReferenceOptions",
293 options: "currentOptions"
294 })])]), [[vue.vShow, "visible"]]);
295 }
296 },
297 render: function render() {
298 var _slot;
299
300 if (!this.visible) return null;
301 return this.teleport ? vue.createVNode(vue.Teleport, {
302 "to": this.teleport
303 }, _isSlot(_slot = this.renderContent()) ? _slot : {
304 default: function _default() {
305 return [_slot];
306 }
307 }) : this.renderContent();
308 }
309});
310
311function _defineProperty(obj, key, value) {
312 if (key in obj) {
313 Object.defineProperty(obj, key, {
314 value: value,
315 enumerable: true,
316 configurable: true,
317 writable: true
318 });
319 } else {
320 obj[key] = value;
321 }
322
323 return obj;
324}
325
326var ContextmenuItem = vue.defineComponent({
327 name: "VContextmenuItem",
328 props: {
329 disabled: {
330 type: Boolean,
331 default: false
332 },
333 hideOnClick: {
334 type: Boolean,
335 default: true
336 }
337 },
338 emits: ["click", "mouseenter", "mouseleave"],
339 setup: function setup(props, _ref) {
340 var emit = _ref.emit;
341 var rootHide = vue.inject("hide");
342 var hover = vue.ref(false);
343 var classes = vue.computed(function () {
344 var _ref2;
345
346 return _ref2 = {}, _defineProperty(_ref2, CLASSES.contextmenuItem, true), _defineProperty(_ref2, CLASSES.contextmenuItemDisabled, props.disabled), _defineProperty(_ref2, CLASSES.contextmenuItemHover, hover.value), _ref2;
347 });
348
349 var handleClick = function handleClick(evt) {
350 if (props.disabled) return;
351 emit("click", evt);
352 props.hideOnClick && (rootHide === null || rootHide === void 0 ? void 0 : rootHide());
353 };
354
355 var handleMouseenter = function handleMouseenter(evt) {
356 if (props.disabled) return;
357 hover.value = true;
358 emit("mouseenter", evt);
359 };
360
361 var handleMouseleave = function handleMouseleave(evt) {
362 if (props.disabled) return;
363 hover.value = false;
364 emit("mouseleave", evt);
365 };
366
367 return {
368 classes: classes,
369 handleClick: handleClick,
370 handleMouseenter: handleMouseenter,
371 handleMouseleave: handleMouseleave
372 };
373 },
374 render: function render() {
375 var _this$$slots$default, _this$$slots;
376
377 return vue.createVNode("li", {
378 "class": this.classes,
379 "onClick": this.handleClick,
380 "onMouseenter": this.handleMouseenter,
381 "onMouseleave": this.handleMouseleave
382 }, [(_this$$slots$default = (_this$$slots = this.$slots).default) === null || _this$$slots$default === void 0 ? void 0 : _this$$slots$default.call(_this$$slots)]);
383 }
384});
385
386var ContextmenuDivider = vue.defineComponent({
387 name: "VContextmenuDivider",
388 render: function render() {
389 return vue.createVNode("li", {
390 "class": CLASSES.contextmenuDivider
391 }, null);
392 }
393});
394
395var ContextmenuIcon = vue.defineComponent({
396 name: "VContextmenuIcon",
397 props: {
398 name: {
399 type: String,
400 required: true
401 }
402 },
403 render: function render() {
404 return vue.createVNode("i", {
405 "class": [CLASSES.contextmenuIcon, "".concat(CLASSES.contextmenuIcon, "-").concat(this.name)]
406 }, null);
407 }
408});
409
410var ContextmenuSubmenu = vue.defineComponent({
411 name: "VContextmenuSubmenu",
412 props: {
413 title: {
414 type: String,
415 required: true
416 },
417 disabled: {
418 type: Boolean,
419 default: false
420 }
421 },
422 emits: ["mouseenter", "mouseleave"],
423 setup: function setup(props, _ref) {
424 var emit = _ref.emit;
425 var submenuRef = vue.ref(null);
426 var autoAjustPlacement = vue.inject("autoAjustPlacement");
427 var placements = vue.ref(["top", "right"]);
428 var hover = vue.ref(false);
429
430 var handleMouseenter = function handleMouseenter(evt) {
431 if (props.disabled) return;
432 hover.value = true;
433 emit("mouseenter", evt);
434 vue.nextTick(function () {
435 var targetPlacements = [];
436
437 if (autoAjustPlacement) {
438 var target = evt.target;
439 var targetDimension = target.getBoundingClientRect();
440 if (!submenuRef.value) return;
441 var submenuWidth = submenuRef.value.clientWidth;
442 var submenuHeight = submenuRef.value.clientHeight;
443
444 if (targetDimension.right + submenuWidth >= window.innerWidth) {
445 targetPlacements.push("left");
446 } else {
447 targetPlacements.push("right");
448 }
449
450 if (targetDimension.bottom + submenuHeight >= window.innerHeight) {
451 targetPlacements.push("bottom");
452 } else {
453 targetPlacements.push("top");
454 }
455 }
456
457 placements.value = targetPlacements;
458 });
459 };
460
461 var handleMouseleave = function handleMouseleave(evt) {
462 if (props.disabled) return;
463 hover.value = false;
464 emit("mouseleave", evt);
465 };
466
467 var titleClasses = vue.computed(function () {
468 var _ref2;
469
470 return _ref2 = {}, _defineProperty(_ref2, CLASSES.contextmenuItem, true), _defineProperty(_ref2, CLASSES.contextmenuSubmenuTitle, true), _defineProperty(_ref2, CLASSES.contextmenuItemHover, hover.value), _defineProperty(_ref2, CLASSES.contextmenuItemDisabled, props.disabled), _ref2;
471 });
472 var menusClasses = vue.computed(function () {
473 var _ref3;
474
475 return _ref3 = {}, _defineProperty(_ref3, CLASSES.contextmenu, true), _defineProperty(_ref3, CLASSES.contextmenuSubmenuMenus, true), _defineProperty(_ref3, CLASSES.contextmenuSubmenuMenusTop, placements.value.includes("top")), _defineProperty(_ref3, CLASSES.contextmenuSubmenuMenusRight, placements.value.includes("right")), _defineProperty(_ref3, CLASSES.contextmenuSubmenuMenusBottom, placements.value.includes("bottom")), _defineProperty(_ref3, CLASSES.contextmenuSubmenuMenusLeft, placements.value.includes("left")), _ref3;
476 });
477 return {
478 hover: hover,
479 submenuRef: submenuRef,
480 titleClasses: titleClasses,
481 menusClasses: menusClasses,
482 handleMouseenter: handleMouseenter,
483 handleMouseleave: handleMouseleave
484 };
485 },
486 render: function render() {
487 var _this$$slots$title, _this$$slots, _this$$slots$default, _this$$slots2;
488
489 return vue.createVNode("li", {
490 "class": CLASSES.contextmenuSubmenu,
491 "onMouseenter": this.handleMouseenter,
492 "onMouseleave": this.handleMouseleave
493 }, [vue.createVNode("div", {
494 "class": this.titleClasses
495 }, [((_this$$slots$title = (_this$$slots = this.$slots).title) === null || _this$$slots$title === void 0 ? void 0 : _this$$slots$title.call(_this$$slots)) || this.title, vue.createVNode("span", {
496 "class": CLASSES.contextmenuSubmenuArrow
497 }, [vue.createVNode(ContextmenuIcon, {
498 "name": "right-arrow"
499 }, null)])]), this.hover ? vue.createVNode("div", {
500 "ref": "submenuRef",
501 "class": this.menusClasses
502 }, [vue.createVNode("ul", {
503 "class": CLASSES.contextmenuInner
504 }, [(_this$$slots$default = (_this$$slots2 = this.$slots).default) === null || _this$$slots$default === void 0 ? void 0 : _this$$slots$default.call(_this$$slots2)])]) : null]);
505 }
506});
507
508var ContextmenuGroup = vue.defineComponent({
509 name: "VContextmenuGroup",
510 props: {
511 title: {
512 type: String,
513 default: undefined
514 },
515 maxWidth: {
516 type: [Number, String],
517 default: undefined
518 }
519 },
520 setup: function setup(props) {
521 var style = vue.computed(function () {
522 if (!props.maxWidth) return;
523 return {
524 "max-width": typeof props.maxWidth === "number" ? "".concat(props.maxWidth, "px") : props.maxWidth,
525 "overflow-x": "auto"
526 };
527 });
528 return {
529 style: style
530 };
531 },
532 methods: {
533 renderTitle: function renderTitle() {
534 var _this$$slots$title, _this$$slots;
535
536 var content = ((_this$$slots$title = (_this$$slots = this.$slots).title) === null || _this$$slots$title === void 0 ? void 0 : _this$$slots$title.call(_this$$slots)) || this.title;
537 return content ? vue.createVNode("div", {
538 "class": CLASSES.contextmenuGroupTitle
539 }, [content]) : null;
540 }
541 },
542 render: function render() {
543 var _this$$slots$default, _this$$slots2;
544
545 return vue.createVNode("li", {
546 "class": CLASSES.contextmenuGroup
547 }, [this.renderTitle(), vue.createVNode("ul", {
548 "style": this.style,
549 "class": CLASSES.contextmenuGroupMenus
550 }, [(_this$$slots$default = (_this$$slots2 = this.$slots).default) === null || _this$$slots$default === void 0 ? void 0 : _this$$slots$default.call(_this$$slots2)])]);
551 }
552});
553
554var version = "3.0.0";
555
556var install = function install(app) {
557 app.directive("contextmenu", contextmenuDirective);
558 app.component(Contextmenu.name, Contextmenu);
559 app.component(ContextmenuItem.name, ContextmenuItem);
560 app.component(ContextmenuDivider.name, ContextmenuDivider);
561 app.component(ContextmenuSubmenu.name, ContextmenuSubmenu);
562 app.component(ContextmenuGroup.name, ContextmenuGroup);
563};
564var VContextmenu = {
565 install: install,
566 version: version
567};
568
569exports.Contextmenu = Contextmenu;
570exports.ContextmenuDivider = ContextmenuDivider;
571exports.ContextmenuGroup = ContextmenuGroup;
572exports.ContextmenuItem = ContextmenuItem;
573exports.ContextmenuSubmenu = ContextmenuSubmenu;
574exports.default = VContextmenu;
575exports.directive = contextmenuDirective;
576exports.install = install;
577exports.version = version;