Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

vue3-menus

Package Overview
Dependencies
Maintainers
1
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

vue3-menus - npm Package Compare versions

Comparing version 1.0.17 to 1.1.0

src/App.vue

870

dist/vue3-menus.es.js

@@ -1,173 +0,3 @@

import { h, resolveComponent, openBlock, createBlock, renderSlot, Fragment, createCommentVNode, createVNode, createTextVNode, toDisplayString, ref, onMounted, nextTick, createApp, pushScopeId, popScopeId, Teleport, Transition, withModifiers, renderList, createSlots, withScopeId, watch } from 'vue';
import { defineComponent, getCurrentInstance, ref, computed, watch, nextTick, createVNode, Teleport, Transition, render } from 'vue';
var script$3 = {
name: "menus-icon",
props: {
options: {
type: [Function, Object],
default: {}
}
},
render() {
if (typeof this.$props.options === 'function') {
return h(this.$props.options);
} else if (typeof this.$props.options.node == 'function' || typeof this.$props.options.node == 'object') {
return h(this.$props.options.node, this.$props.options.option);
} else if (typeof this.$props.options === 'object' && !this.$props.options.node) {
return h(this.$props.options);
}
return null;
}
};
script$3.__file = "src/components/MenusIcon.vue";
var script$2 = {
name: "menus-item",
components: {
MenusIcon: script$3
},
props: {
menusItemClass: {
type: String,
default: null
},
hasIcon: {
type: Boolean,
default: false
},
item: {
type: Object,
default: {}
},
index: {
type: Number,
default: 0
},
activeIndex: {
type: Number,
default: -1,
}
},
setup(props, { emit, slots }) {
function menusEnter(event, item) {
emit("menusEnter", event, item, props.index);
event.preventDefault();
}
function menusClick(event, item) {
event.preventDefault();
if (item.disabled) {
event.stopPropagation();
return;
}
if (
item &&
!item.disabled &&
!item.hidden &&
typeof item.click === "function"
) {
const val = item.click(item);
if (val === false || val === null) {
event.stopPropagation();
}
}
}
return {
slots,
menusEnter,
menusClick
};
},
};
const _hoisted_1$1 = {
key: 0,
class: "menus-item-icon"
};
const _hoisted_2 = { class: "menus-item-label" };
const _hoisted_3 = { class: "menus-item-suffix" };
const _hoisted_4 = /*#__PURE__*/createTextVNode("▶");
const _hoisted_5 = {
key: 3,
class: "menus-item-tip"
};
function render$1(_ctx, _cache, $props, $setup, $data, $options) {
const _component_MenusIcon = resolveComponent("MenusIcon");
return ($setup.slots.default)
? (openBlock(), createBlock("div", {
key: 0,
onMouseenter: _cache[1] || (_cache[1] = ($event) => $setup.menusEnter($event, $props.item)),
onClick: _cache[2] || (_cache[2] = ($event) => $setup.menusClick($event, $props.item)),
onContextmenu: _cache[3] || (_cache[3] = ($event) => $setup.menusClick($event, $props.item))
}, [
renderSlot(_ctx.$slots, "default", {
item: { activeIndex: $props.activeIndex, item: $props.item }
})
], 32 /* HYDRATE_EVENTS */))
: (openBlock(), createBlock("div", {
key: 1,
onMouseenter: _cache[4] || (_cache[4] = ($event) => $setup.menusEnter($event, $props.item)),
onClick: _cache[5] || (_cache[5] = ($event) => $setup.menusClick($event, $props.item)),
onContextmenu: _cache[6] || (_cache[6] = ($event) => $setup.menusClick($event, $props.item)),
style: $props.item.style ? $props.item.style : {},
class: ['menus-item', $props.item.disabled ? 'menus-item-disabled' : 'menus-item-available',
$props.item.divided ? 'menus-divided' : null, (!$props.item.disabled && $props.activeIndex === $props.index) ? 'menus-item-active' : null,
$props.menusItemClass]
}, [
($props.hasIcon)
? (openBlock(), createBlock("div", _hoisted_1$1, [
($setup.slots.icon)
? renderSlot(_ctx.$slots, "icon", {
key: 0,
item: { activeIndex: $props.activeIndex, item: $props.item }
})
: ($props.item.icon)
? (openBlock(), createBlock(Fragment, { key: 1 }, [
(typeof $props.item.icon === 'string')
? (openBlock(), createBlock("span", {
key: 0,
innerHTML: $props.item.icon
}, null, 8 /* PROPS */, ["innerHTML"]))
: (openBlock(), createBlock(_component_MenusIcon, {
key: 1,
options: $props.item.icon
}, null, 8 /* PROPS */, ["options"]))
], 64 /* STABLE_FRAGMENT */))
: createCommentVNode("v-if", true)
]))
: createCommentVNode("v-if", true),
createVNode("span", _hoisted_2, [
($setup.slots.label)
? renderSlot(_ctx.$slots, "label", {
key: 0,
item: { activeIndex: $props.activeIndex, item: $props.item }
})
: (openBlock(), createBlock(Fragment, { key: 1 }, [
createTextVNode(toDisplayString($props.item.label), 1 /* TEXT */)
], 64 /* STABLE_FRAGMENT */))
]),
createVNode("div", _hoisted_3, [
($props.item.children && $setup.slots.suffix)
? renderSlot(_ctx.$slots, "suffix", {
key: 0,
item: { activeIndex: $props.activeIndex, item: $props.item }
})
: ($props.item.children)
? (openBlock(), createBlock(Fragment, { key: 1 }, [
_hoisted_4
], 64 /* STABLE_FRAGMENT */))
: ($props.item.tip && $setup.slots.suffix)
? renderSlot(_ctx.$slots, "suffix", {
key: 2,
item: { activeIndex: $props.activeIndex, item: $props.item }
})
: ($props.item.tip)
? (openBlock(), createBlock("span", _hoisted_5, toDisplayString($props.item.tip), 1 /* TEXT */))
: createCommentVNode("v-if", true)
])
], 38 /* CLASS, STYLE, HYDRATE_EVENTS */))
}
function styleInject(css, ref) {

@@ -200,87 +30,110 @@ if ( ref === void 0 ) ref = {};

var css_248z$1 = "\n.menus-item {\n display: flex;\n line-height: 2rem;\n padding: 0 1rem;\n margin: 0;\n font-size: 0.8rem;\n outline: 0;\n align-items: center;\n transition: 0.2s;\n box-sizing: border-box;\n list-style: none;\n border-bottom: 1px solid #00000000;\n}\n.menus-item-divided {\n border-bottom-color: #ebeef5;\n}\n.menus-item .menus-item-icon {\n display: flex;\n margin-right: 0.6rem;\n width: 1rem;\n}\n.menus-item .menus-item-label {\n flex: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.menus-item .menus-item-suffix {\n margin-left: 1.5rem;\n font-size: 0.39rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.menus-item-available {\n color: #606266;\n cursor: pointer;\n}\n.menus-item-available:hover {\n background: #ecf5ff;\n color: #409eff;\n}\n.menus-item-disabled {\n color: #c0c4cc;\n cursor: not-allowed;\n}\n.menus-item-active {\n background: #ecf5ff;\n color: #409eff;\n}\n.menus-item-tip {\n font-size: 9px;\n color: #999;\n}\n";
styleInject(css_248z$1);
var css_248z = ".menus-fade-enter-active,\n.menus-fade-leave-active {\n transition: opacity 0.2s ease-in-out;\n}\n.menus-fade-enter-from,\n.menus-fade-leave-to {\n opacity: 0;\n}\n\n.v3-menus {\n position: fixed;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);\n background: #fff;\n border-radius: 4px;\n padding: 8px 0;\n user-select: none;\n box-sizing: border-box;\n}\n\n.v3-menus-body {\n display: block;\n}\n\n.v3-menus-item {\n display: flex;\n line-height: 2rem;\n padding: 0 1rem;\n margin: 0;\n font-size: 0.8rem;\n outline: 0;\n align-items: center;\n transition: 0.2s;\n box-sizing: border-box;\n list-style: none;\n border-bottom: 1px solid #00000000;\n}\n\n.v3-menus-divided {\n border-bottom-color: #ebeef5;\n}\n\n.v3-menus-icon {\n display: flex;\n margin-right: 0.6rem;\n width: 1rem;\n}\n\n.v3-menus-label {\n flex: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.v3-menus-suffix {\n margin-left: 1.5rem;\n font-size: 0.39rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.v3-menus-available {\n color: #606266;\n cursor: pointer;\n}\n\n.v3-menus-available:hover {\n background: #ecf5ff;\n color: #409eff;\n}\n\n.v3-menus-disabled {\n color: #c0c4cc;\n cursor: not-allowed;\n}\n\n.v3-menus-active {\n background: #ecf5ff;\n color: #409eff;\n}\n\n.v3-menus-tip {\n font-size: 9px;\n color: #999;\n}\n";
styleInject(css_248z);
script$2.render = render$1;
script$2.__file = "src/components/MenusItem.vue";
var script$1 = {
name: "menus",
components: {
MenusItem: script$2
const props = {
menus: {
type: Array,
required: true
},
props: {
menus: {
type: Array,
default: []
},
menusStyle: {
type: Object,
default: {}
},
menusItemClass: {
type: String,
default: null
},
event: {
type: Object,
default: {}
},
position: {
type: Object,
default: {}
},
minWidth: {
type: [Number, String],
default: 'none'
},
maxWidth: {
type: [Number, String],
default: 'none'
},
zIndex: {
type: [Number, String],
default: 3
},
direction: {
type: String,
default: 'right'
},
open: {
type: Boolean,
default: false
}
itemClass: {
type: String,
default: null
},
setup(props, { slots }) {
const ctx = {};
const windowWidth = globalThis.document.documentElement.clientWidth;
const windowHeight = globalThis.document.documentElement.clientHeight;
const _position = props.position.x && props.position.y ? ref(props.position) : ref({
x: props.event.clientX,
y: props.event.clientY,
width: 0,
height: 0
});
event: {
type: Object,
required: true
},
minWidth: {
type: [Number, String],
default: 'none'
},
maxWidth: {
type: [Number, String],
default: 'none'
},
zIndex: {
type: Number,
default: 3
},
direction: {
type: String,
default: 'right'
},
click: {
type: Function
},
enter: {
type: Function
},
open: {
type: Boolean,
default: false
},
args: {
type: [Object, Function, Array, Boolean, String],
default: {}
}
};
const windowWidth = globalThis.document.documentElement.clientWidth;
const windowHeight = globalThis.document.documentElement.clientHeight;
const vue3MenusComponent = defineComponent({
name: 'vue3-menus',
inheritAttrs: false,
props,
emits: ['on-click', 'on-enter'],
setup(props, {
slots,
emit,
attrs
}) {
const {
proxy
} = getCurrentInstance();
const show = ref(props.open);
const self = {};
const menusRef = ref(null);
const style = ref({
left: 0,
top: 0,
minWidth: `${props.minWidth}px`,
maxWidth: props.maxWidth == 'none' ? props.maxWidth : `${props.maxWidth}px`,
zIndex: props.zIndex
});
const _direction = ref(props.direction);
const activeIndex = ref(-1);
const open = ref(false);
const hasIcon = ref(false);
const left = ref(0);
const top = ref(0);
let direction = props.direction;
const hasIcon = computed(() => {
for (let index = 0; index < props.menus.length; index++) {
const menu = props.menus[index];
if (menu.icon !== undefined) {
return true;
}
}
});
const position = computed(() => {
return {
x: props.event.clientX,
y: props.event.clientY,
width: props.event.width || 0,
height: props.event.height || 0
};
});
const style = computed(() => {
return {
left: `${left.value}px`,
top: `${top.value}px`,
minWidth: `${props.minWidth}px`,
maxWidth: props.maxWidth == 'none' ? props.maxWidth : `${props.maxWidth}px`,
zIndex: props.zIndex
};
});
function leftOpen(menusWidth) {
style.value.left = _position.value.x - menusWidth;
_direction.value = 'left';
if (style.value.left < 0) {
_direction.value = 'right';
if (_position.value.width === 0 || _position.value.width === undefined) {
style.value.left = 0;
left.value = position.value.x - menusWidth;
direction = 'left';
if (left.value < 0) {
direction = 'right';
if (position.value.width === 0 || position.value.width === undefined) {
left.value = 0;
} else {
style.value.left = _position.valuen.x + _position.value.width;
left.value = position.value.x + position.value.width;
}

@@ -291,10 +144,12 @@ }

function rightOpen(windowWidth, menusWidth) {
style.value.left = _position.value.x + _position.value.width;
_direction.value = 'right';
if (style.value.left + menusWidth > windowWidth) {
_direction.value = 'left';
if (_position.value.width === 0 || _position.value.width === undefined) {
style.value.left = windowWidth - menusWidth;
left.value = position.value.x + position.value.width;
direction = 'right';
if (left.value + menusWidth > windowWidth) {
direction = 'left';
if (position.value.width === 0 || position.value.width === undefined) {
left.value = windowWidth - menusWidth;
} else {
style.value.left = _position.value.x - menusWidth;
left.value = position.value.x - menusWidth;
}

@@ -304,312 +159,275 @@ }

onMounted(() => {
open.value = true;
props.menus.forEach(menu => {
hasIcon.value = hasIcon.value || menu.icon !== undefined;
if (hasIcon.value) {
return;
function closeEvent() {
activeIndex.value = -1;
show.value = false;
if (self && self.instance) {
self.instance.close.bind(self.instance)();
self.instance = null;
self.index = null; // @ts-ignore
if (proxy.closeAll) {
// @ts-ignore
proxy.closeAll();
}
});
nextTick(() => {
const menusWidth = menusRef.value.offsetWidth;
const menusHeight = menusRef.value.offsetHeight;
if (_direction.value === 'left') {
leftOpen(menusWidth);
} else {
rightOpen(windowWidth, menusWidth);
}
style.value.top = _position.value.y;
if (_position.value.y + menusHeight > windowHeight) {
if (_position.value.height === 0 || _position.value.height === undefined) {
style.value.top = _position.value.y - menusHeight;
}
}
watch(() => props.open, newVal => show.value = newVal);
watch(show, newVal => {
if (newVal) {
nextTick(() => {
const menusWidth = menusRef.value.offsetWidth;
const menusHeight = menusRef.value.offsetHeight;
if (direction === 'left') {
leftOpen(menusWidth);
} else {
style.value.top = windowHeight - menusHeight;
rightOpen(windowWidth, menusWidth);
}
}
});
top.value = position.value.y;
if (position.value.y + menusHeight > windowHeight) {
if (position.value.height === 0 || position.value.height === undefined) {
top.value = position.value.y - menusHeight;
} else {
top.value = windowHeight - menusHeight;
}
}
setTimeout(() => {
globalThis.document.addEventListener('click', closeEvent);
globalThis.document.addEventListener('contextmenu', closeEvent);
globalThis.document.addEventListener('wheel', closeEvent);
}, 0);
});
} else {
activeIndex.value = -1;
globalThis.document.removeEventListener('click', closeEvent);
globalThis.document.removeEventListener('contextmenu', closeEvent);
globalThis.document.removeEventListener('wheel', closeEvent);
}
}, {
immediate: true
});
function menusEnter(event, item, index) {
function mouseEnter(event, menu, index) {
emit('on-enter', menu, props.args);
event.preventDefault();
activeIndex.value = index;
if (item.disabled) {
if (!menu || menu.disabled || menu.hidden) {
return;
}
if (ctx.instance) {
if (ctx.index === index) {
if (self.instance) {
if (self.index === index) {
return;
}
ctx.instance.close.bind(ctx.instance)();
ctx.instance = null;
ctx.index = null;
self.instance.close.bind(self.instance)();
self.instance = null;
self.index = null;
}
if (!item.children) {
if (!menu.children) {
return;
}
let enter = props.enter && typeof props.enter === 'function' ? props.enter : null;
enter = menu.enter && typeof menu.enter === 'function' ? menu.enter : enter;
if (enter) {
const val = enter(menu, props.args);
if (val === false || val === null) {
return;
}
}
const menuItemClientRect = event.target.getBoundingClientRect();
const node = h(script$1, {
...props,
menus: item.children || [],
direction: _direction.value,
position: {
x: menuItemClientRect.x + 3,
y: menuItemClientRect.y - 8,
const vm = createVNode(vue3MenusComponent, { ...props,
menus: menu.children,
direction: direction,
event: {
clientX: menuItemClientRect.x + 3,
clientY: menuItemClientRect.y - 8,
width: menuItemClientRect.width - 2 * 3,
height: menuItemClientRect.width
},
open: false
}, slots);
const app = createApp(node);
ctx.instance = app.mount(globalThis.document.createElement("div"));
ctx.instance.$unmount = app.unmount;
ctx.index = index;
const container = document.createElement('div');
render(vm, container);
vm.component.props.open = true; // @ts-ignore
vm.component.proxy.close = close;
self.instance = vm.component.proxy;
self.instance.container = container;
self.instance.props = vm.component.props;
self.index = index;
}
function mouseClick(event, menu) {
emit('on-click', menu, props.args);
event.preventDefault();
}
function close() {
open.value = false;
if (this && this.ctx && this.ctx.instance) {
this.ctx.instance.close();
if (!menu || menu.disabled) {
event.stopPropagation();
return;
}
nextTick(() => {
this.$unmount() && this.$unmount();
});
}
return {
open,
hasIcon,
menusRef,
style,
close,
menusEnter,
ctx,
activeIndex,
slots
};
},
};
const _withId = /*#__PURE__*/withScopeId("data-v-d65e2e18");
let click = props.click && typeof props.click === 'function' ? props.click : null;
click = menu.click && typeof menu.click === 'function' ? menu.click : click;
pushScopeId("data-v-d65e2e18");
const _hoisted_1 = { class: "menus_body" };
popScopeId();
if (click) {
const val = click(menu, props.args);
const render = /*#__PURE__*/_withId(function render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_MenusItem = resolveComponent("MenusItem");
if (val === false || val === null) {
event.stopPropagation();
}
}
return (openBlock(), createBlock(Teleport, { to: "body" }, [
createVNode(Transition, { name: "menus-fade" }, {
default: _withId(() => [
($setup.open)
? (openBlock(), createBlock("div", {
key: 0,
ref: "menusRef",
class: "menus",
style: { ...$props.menusStyle, top: `${$setup.style.top}px`, left: `${$setup.style.left}px`, minWidth: $setup.style.minWidth, maxWidth: $setup.style.maxWidth, zIndex: $setup.style.zIndex },
onContextmenu: _cache[1] || (_cache[1] = (e) => e.preventDefault()),
onMousewheel: _cache[2] || (_cache[2] = withModifiers(() => {}, ["stop"]))
}, [
createVNode("div", _hoisted_1, [
(openBlock(true), createBlock(Fragment, null, renderList($props.menus, (item, index) => {
return (openBlock(), createBlock(Fragment, { key: index }, [
(!item.hidden)
? (openBlock(), createBlock(_component_MenusItem, {
key: 0,
item: item,
index: index,
activeIndex: $setup.activeIndex,
onMenusEnter: $setup.menusEnter,
menusItemClass: $props.menusItemClass,
hasIcon: $setup.hasIcon
}, createSlots({ _: 2 }, [
($setup.slots.default)
? {
name: "default",
fn: _withId(({ item }) => [
renderSlot(_ctx.$slots, "default", { item: item })
])
}
: undefined,
(!$setup.slots.default && $setup.slots.icon)
? {
name: "icon",
fn: _withId(({ item }) => [
renderSlot(_ctx.$slots, "icon", { item: item })
])
}
: undefined,
(!$setup.slots.default && $setup.slots.label)
? {
name: "label",
fn: _withId(({ item }) => [
renderSlot(_ctx.$slots, "label", { item: item })
])
}
: undefined,
(!$setup.slots.default && $setup.slots.suffix)
? {
name: "suffix",
fn: _withId(({ item }) => [
renderSlot(_ctx.$slots, "suffix", { item: item })
])
}
: undefined
]), 1032 /* PROPS, DYNAMIC_SLOTS */, ["item", "index", "activeIndex", "onMenusEnter", "menusItemClass", "hasIcon"]))
: createCommentVNode("v-if", true)
], 64 /* STABLE_FRAGMENT */))
}), 128 /* KEYED_FRAGMENT */))
])
], 36 /* STYLE, HYDRATE_EVENTS */))
: createCommentVNode("v-if", true)
]),
_: 1
})
]))
});
if (menu.children) {
event.stopPropagation();
}
}
var css_248z = "\n.menus[data-v-d65e2e18] {\n position: fixed;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);\n background: #fff;\n border-radius: 4px;\n padding: 8px 0;\n user-select: none;\n box-sizing: border-box;\n}\n.menus_body[data-v-d65e2e18] {\n display: block;\n}\n.menus-fade-enter-active[data-v-d65e2e18],\n.menus-fade-leave-active[data-v-d65e2e18] {\n transition: opacity 0.1s ease-in-out;\n}\n.menus-fade-enter-from[data-v-d65e2e18],\n.menus-fade-leave-to[data-v-d65e2e18] {\n opacity: 0;\n}\n";
styleInject(css_248z);
function close() {
this.show = false;
script$1.render = render;
script$1.__scopeId = "data-v-d65e2e18";
script$1.__file = "src/components/Menus.vue";
if (this.self && this.self.instance) {
this.self.instance.close();
}
var script = {
name: "vue3-menus",
props: {
menus: {
type: Array,
default: []
},
menusStyle: {
type: Object,
default: {}
},
menusItemClass: {
type: String,
default: null
},
event: {
type: Object,
default: {}
},
position: {
type: Object,
default: {}
},
minWidth: {
type: [Number, String],
default: 'none'
},
maxWidth: {
type: [Number, String],
default: 'none'
},
zIndex: {
type: [Number, String],
default: 2
},
open: {
type: Boolean,
default: false
nextTick(() => {
render(null, this.container);
});
}
},
setup(props, { emit, slots }) {
let lastInstance = null;
function mouseEvent() {
emit("update:open", false);
if (lastInstance) {
lastInstance.close();
lastInstance = null;
}
}
watch(() => props.open, (newVal) => {
if (newVal) {
if (lastInstance) {
lastInstance.close();
lastInstance = null;
}
const node = h(script$1, {
...props
}, slots);
const app = createApp(node);
lastInstance = app.mount(globalThis.document.createElement("div"));
lastInstance.$unmount = app.unmount;
setTimeout(() => {
globalThis.document.addEventListener("click", mouseEvent);
globalThis.document.addEventListener("contextmenu", mouseEvent);
globalThis.document.addEventListener("wheel", mouseEvent);
}, 0);
} else {
globalThis.document.removeEventListener("click", mouseEvent);
globalThis.document.removeEventListener("contextmenu", mouseEvent);
globalThis.document.removeEventListener("wheel", mouseEvent);
}
const {
default: $default,
label,
icon,
suffix
} = slots;
const $class = ['v3-menus', attrs.class];
return () => createVNode(Teleport, {
"to": 'body'
}, {
default: () => [createVNode(Transition, {
"name": 'menus-fade'
}, {
default: () => [!show.value ? null : createVNode("div", {
"ref": menusRef,
"class": $class,
"style": style.value,
"onWheel": e => e.preventDefault(),
"onContextmenu": e => e.preventDefault()
}, [createVNode("div", {
"class": 'v3-menus-body'
}, [props.menus.map((menu, index) => {
if (menu.hidden) {
return null;
}
if ($default) {
return createVNode("div", {
"onContextmenu": $event => mouseClick($event, menu),
"onClick": $event => mouseClick($event, menu),
"onMouseenter": $event => mouseEnter($event, menu, index)
}, [$default({
menu,
activeIndex,
index
})]);
} else {
let $class = [props.itemClass, 'v3-menus-item', menu.disabled ? 'v3-menus-disabled' : 'v3-menus-available'];
$class = $class.concat([menu.divided ? 'v3-menus-divided' : null, !menu.disabled && activeIndex.value === index ? 'v3-menus-active' : null]);
return createVNode("div", {
"style": menu.style,
"class": $class.join(' '),
"onClick": $event => mouseClick($event, menu),
"onMouseenter": $event => mouseEnter($event, menu, index),
"onContextmenu": $event => mouseClick($event, menu)
}, [hasIcon ? createVNode("div", {
"class": 'v3-menus-icon '
}, [icon ? icon({
menu,
activeIndex,
index
}) : createVNode("span", {
"innerHTML": menu.icon
}, null)]) : null, label ? createVNode("span", {
"class": 'v3-menus-label'
}, [label({
menu,
activeIndex,
index
})]) : createVNode("span", {
"class": 'v3-menus-label'
}, [menu.label]), menu.children || menu.tip ? createVNode("div", {
"class": 'v3-menus-suffix'
}, [suffix ? suffix({
menu,
activeIndex,
index
}) : menu.children ? '▶' : menu.tip ? createVNode("span", {
"class": 'v3-menus-tip'
}, [menu.tip]) : null]) : null]);
}
})])])]
})]
});
return {}
},
render() {
return null;
}
};
script.__file = "src/components/Vue3Menus.vue";
});
let lastInstance = null;
function mouseEvent() {
if (lastInstance) {
lastInstance.close();
lastInstance = null;
function mouseEvent(menus, args, event) {
let props = {};
if (Array.isArray(menus)) {
props = {
menus,
event,
args,
open: false,
};
} else {
props = {
...menus,
args,
event,
open: false
};
}
globalThis.document.removeEventListener("click", mouseEvent);
globalThis.document.removeEventListener("contextmenu", mouseEvent);
globalThis.document.removeEventListener("wheel", mouseEvent);
}
function $menusEvent(menus, event) {
const temp = menus || {};
if (lastInstance) {
lastInstance.close();
lastInstance = null;
globalThis.document.removeEventListener("click", mouseEvent);
globalThis.document.removeEventListener("contextmenu", mouseEvent);
globalThis.document.removeEventListener("wheel", mouseEvent);
}
let instance = createApp(script$1, {
event,
...temp
});
lastInstance = instance.mount(globalThis.document.createElement("div"));
lastInstance.$unmount = instance.unmount;
if (temp.prevent == undefined || temp.prevent) {
const vNode = createVNode(vue3MenusComponent, props);
const container = document.createElement('div');
render(vNode, container);
vNode.component.props.open = true;
vNode.component.proxy.closeAll = () => {
nextTick(() => {
render(null, container);
});
};
if (props.prevent == undefined || props.prevent) {
event.preventDefault();
}
setTimeout(() => {
globalThis.document.addEventListener("click", mouseEvent);
globalThis.document.addEventListener("contextmenu", mouseEvent);
globalThis.document.addEventListener("wheel", mouseEvent);
}, 0);
return lastInstance;
}
const directive = {
mounted(el, { value, arg, instance }) {
mounted(el, { value, arg }) {
const vnode = el.__vnode || {};
if (arg === undefined || arg === 'right') {
el.addEventListener("contextmenu", $menusEvent.bind(instance, value));
el.addEventListener("contextmenu", mouseEvent.bind(el, value, vnode.props));
} else if (arg === 'left') {
el.addEventListener("click", $menusEvent.bind(instance, value));
el.addEventListener("click", mouseEvent.bind(el, value, vnode.props));
} else if (arg === 'all') {
el.addEventListener("contextmenu", $menusEvent.bind(instance, value));
el.addEventListener("click", $menusEvent.bind(instance, value));
el.addEventListener("contextmenu", mouseEvent.bind(el, value, vnode.props));
el.addEventListener("click", mouseEvent.bind(el, value, vnode.props));
}
},
unmounted(el, { arg }) {
if (arg === undefined || arg === 'right') {
el.removeEventListener("contextmenu", $menusEvent);
} else if (arg === 'left') {
el.removeEventListener("click", $menusEvent);
} else if (arg === 'all') {
el.removeEventListener("contextmenu", $menusEvent);
el.removeEventListener("click", $menusEvent);
}
unmounted(el) {
el.removeEventListener("contextmenu", mouseEvent);
el.removeEventListener("click", mouseEvent);
}

@@ -619,13 +437,13 @@ };

const install = function (app, options = {}) {
app.component(options.name || script.name, script);
app.component(options.name || vue3MenusComponent.name, vue3MenusComponent);
app.directive('menus', directive);
app.config.globalProperties.$menusEvent = (event, menus) => $menusEvent(menus, event);
app.config.globalProperties.$menusEvent = (event, menus, args) => mouseEvent(menus, args, event);
};
const menusEvent = (event, menus) => $menusEvent(menus, event);
const menusEvent = (event, menus, args) => mouseEvent(menus, args, event);
function index(app){
function index (app) {
app.use(install);
}
export { script as Vue3Menus, index as default, directive, menusEvent };
export { vue3MenusComponent as Vue3Menus, index as default, directive, menusEvent };

@@ -7,172 +7,2 @@ (function (global, factory) {

var script$3 = {
name: "menus-icon",
props: {
options: {
type: [Function, Object],
default: {}
}
},
render() {
if (typeof this.$props.options === 'function') {
return vue.h(this.$props.options);
} else if (typeof this.$props.options.node == 'function' || typeof this.$props.options.node == 'object') {
return vue.h(this.$props.options.node, this.$props.options.option);
} else if (typeof this.$props.options === 'object' && !this.$props.options.node) {
return vue.h(this.$props.options);
}
return null;
}
};
script$3.__file = "src/components/MenusIcon.vue";
var script$2 = {
name: "menus-item",
components: {
MenusIcon: script$3
},
props: {
menusItemClass: {
type: String,
default: null
},
hasIcon: {
type: Boolean,
default: false
},
item: {
type: Object,
default: {}
},
index: {
type: Number,
default: 0
},
activeIndex: {
type: Number,
default: -1,
}
},
setup(props, { emit, slots }) {
function menusEnter(event, item) {
emit("menusEnter", event, item, props.index);
event.preventDefault();
}
function menusClick(event, item) {
event.preventDefault();
if (item.disabled) {
event.stopPropagation();
return;
}
if (
item &&
!item.disabled &&
!item.hidden &&
typeof item.click === "function"
) {
const val = item.click(item);
if (val === false || val === null) {
event.stopPropagation();
}
}
}
return {
slots,
menusEnter,
menusClick
};
},
};
const _hoisted_1$1 = {
key: 0,
class: "menus-item-icon"
};
const _hoisted_2 = { class: "menus-item-label" };
const _hoisted_3 = { class: "menus-item-suffix" };
const _hoisted_4 = /*#__PURE__*/vue.createTextVNode("▶");
const _hoisted_5 = {
key: 3,
class: "menus-item-tip"
};
function render$1(_ctx, _cache, $props, $setup, $data, $options) {
const _component_MenusIcon = vue.resolveComponent("MenusIcon");
return ($setup.slots.default)
? (vue.openBlock(), vue.createBlock("div", {
key: 0,
onMouseenter: _cache[1] || (_cache[1] = ($event) => $setup.menusEnter($event, $props.item)),
onClick: _cache[2] || (_cache[2] = ($event) => $setup.menusClick($event, $props.item)),
onContextmenu: _cache[3] || (_cache[3] = ($event) => $setup.menusClick($event, $props.item))
}, [
vue.renderSlot(_ctx.$slots, "default", {
item: { activeIndex: $props.activeIndex, item: $props.item }
})
], 32 /* HYDRATE_EVENTS */))
: (vue.openBlock(), vue.createBlock("div", {
key: 1,
onMouseenter: _cache[4] || (_cache[4] = ($event) => $setup.menusEnter($event, $props.item)),
onClick: _cache[5] || (_cache[5] = ($event) => $setup.menusClick($event, $props.item)),
onContextmenu: _cache[6] || (_cache[6] = ($event) => $setup.menusClick($event, $props.item)),
style: $props.item.style ? $props.item.style : {},
class: ['menus-item', $props.item.disabled ? 'menus-item-disabled' : 'menus-item-available',
$props.item.divided ? 'menus-divided' : null, (!$props.item.disabled && $props.activeIndex === $props.index) ? 'menus-item-active' : null,
$props.menusItemClass]
}, [
($props.hasIcon)
? (vue.openBlock(), vue.createBlock("div", _hoisted_1$1, [
($setup.slots.icon)
? vue.renderSlot(_ctx.$slots, "icon", {
key: 0,
item: { activeIndex: $props.activeIndex, item: $props.item }
})
: ($props.item.icon)
? (vue.openBlock(), vue.createBlock(vue.Fragment, { key: 1 }, [
(typeof $props.item.icon === 'string')
? (vue.openBlock(), vue.createBlock("span", {
key: 0,
innerHTML: $props.item.icon
}, null, 8 /* PROPS */, ["innerHTML"]))
: (vue.openBlock(), vue.createBlock(_component_MenusIcon, {
key: 1,
options: $props.item.icon
}, null, 8 /* PROPS */, ["options"]))
], 64 /* STABLE_FRAGMENT */))
: vue.createCommentVNode("v-if", true)
]))
: vue.createCommentVNode("v-if", true),
vue.createVNode("span", _hoisted_2, [
($setup.slots.label)
? vue.renderSlot(_ctx.$slots, "label", {
key: 0,
item: { activeIndex: $props.activeIndex, item: $props.item }
})
: (vue.openBlock(), vue.createBlock(vue.Fragment, { key: 1 }, [
vue.createTextVNode(vue.toDisplayString($props.item.label), 1 /* TEXT */)
], 64 /* STABLE_FRAGMENT */))
]),
vue.createVNode("div", _hoisted_3, [
($props.item.children && $setup.slots.suffix)
? vue.renderSlot(_ctx.$slots, "suffix", {
key: 0,
item: { activeIndex: $props.activeIndex, item: $props.item }
})
: ($props.item.children)
? (vue.openBlock(), vue.createBlock(vue.Fragment, { key: 1 }, [
_hoisted_4
], 64 /* STABLE_FRAGMENT */))
: ($props.item.tip && $setup.slots.suffix)
? vue.renderSlot(_ctx.$slots, "suffix", {
key: 2,
item: { activeIndex: $props.activeIndex, item: $props.item }
})
: ($props.item.tip)
? (vue.openBlock(), vue.createBlock("span", _hoisted_5, vue.toDisplayString($props.item.tip), 1 /* TEXT */))
: vue.createCommentVNode("v-if", true)
])
], 38 /* CLASS, STYLE, HYDRATE_EVENTS */))
}
function styleInject(css, ref) {

@@ -205,87 +35,110 @@ if ( ref === void 0 ) ref = {};

var css_248z$1 = "\n.menus-item {\n display: flex;\n line-height: 2rem;\n padding: 0 1rem;\n margin: 0;\n font-size: 0.8rem;\n outline: 0;\n align-items: center;\n transition: 0.2s;\n box-sizing: border-box;\n list-style: none;\n border-bottom: 1px solid #00000000;\n}\n.menus-item-divided {\n border-bottom-color: #ebeef5;\n}\n.menus-item .menus-item-icon {\n display: flex;\n margin-right: 0.6rem;\n width: 1rem;\n}\n.menus-item .menus-item-label {\n flex: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.menus-item .menus-item-suffix {\n margin-left: 1.5rem;\n font-size: 0.39rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.menus-item-available {\n color: #606266;\n cursor: pointer;\n}\n.menus-item-available:hover {\n background: #ecf5ff;\n color: #409eff;\n}\n.menus-item-disabled {\n color: #c0c4cc;\n cursor: not-allowed;\n}\n.menus-item-active {\n background: #ecf5ff;\n color: #409eff;\n}\n.menus-item-tip {\n font-size: 9px;\n color: #999;\n}\n";
styleInject(css_248z$1);
var css_248z = ".menus-fade-enter-active,\n.menus-fade-leave-active {\n transition: opacity 0.2s ease-in-out;\n}\n.menus-fade-enter-from,\n.menus-fade-leave-to {\n opacity: 0;\n}\n\n.v3-menus {\n position: fixed;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);\n background: #fff;\n border-radius: 4px;\n padding: 8px 0;\n user-select: none;\n box-sizing: border-box;\n}\n\n.v3-menus-body {\n display: block;\n}\n\n.v3-menus-item {\n display: flex;\n line-height: 2rem;\n padding: 0 1rem;\n margin: 0;\n font-size: 0.8rem;\n outline: 0;\n align-items: center;\n transition: 0.2s;\n box-sizing: border-box;\n list-style: none;\n border-bottom: 1px solid #00000000;\n}\n\n.v3-menus-divided {\n border-bottom-color: #ebeef5;\n}\n\n.v3-menus-icon {\n display: flex;\n margin-right: 0.6rem;\n width: 1rem;\n}\n\n.v3-menus-label {\n flex: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.v3-menus-suffix {\n margin-left: 1.5rem;\n font-size: 0.39rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.v3-menus-available {\n color: #606266;\n cursor: pointer;\n}\n\n.v3-menus-available:hover {\n background: #ecf5ff;\n color: #409eff;\n}\n\n.v3-menus-disabled {\n color: #c0c4cc;\n cursor: not-allowed;\n}\n\n.v3-menus-active {\n background: #ecf5ff;\n color: #409eff;\n}\n\n.v3-menus-tip {\n font-size: 9px;\n color: #999;\n}\n";
styleInject(css_248z);
script$2.render = render$1;
script$2.__file = "src/components/MenusItem.vue";
var script$1 = {
name: "menus",
components: {
MenusItem: script$2
const props = {
menus: {
type: Array,
required: true
},
props: {
menus: {
type: Array,
default: []
},
menusStyle: {
type: Object,
default: {}
},
menusItemClass: {
type: String,
default: null
},
event: {
type: Object,
default: {}
},
position: {
type: Object,
default: {}
},
minWidth: {
type: [Number, String],
default: 'none'
},
maxWidth: {
type: [Number, String],
default: 'none'
},
zIndex: {
type: [Number, String],
default: 3
},
direction: {
type: String,
default: 'right'
},
open: {
type: Boolean,
default: false
}
itemClass: {
type: String,
default: null
},
setup(props, { slots }) {
const ctx = {};
const windowWidth = globalThis.document.documentElement.clientWidth;
const windowHeight = globalThis.document.documentElement.clientHeight;
const _position = props.position.x && props.position.y ? vue.ref(props.position) : vue.ref({
x: props.event.clientX,
y: props.event.clientY,
width: 0,
height: 0
});
event: {
type: Object,
required: true
},
minWidth: {
type: [Number, String],
default: 'none'
},
maxWidth: {
type: [Number, String],
default: 'none'
},
zIndex: {
type: Number,
default: 3
},
direction: {
type: String,
default: 'right'
},
click: {
type: Function
},
enter: {
type: Function
},
open: {
type: Boolean,
default: false
},
args: {
type: [Object, Function, Array, Boolean, String],
default: {}
}
};
const windowWidth = globalThis.document.documentElement.clientWidth;
const windowHeight = globalThis.document.documentElement.clientHeight;
const vue3MenusComponent = vue.defineComponent({
name: 'vue3-menus',
inheritAttrs: false,
props,
emits: ['on-click', 'on-enter'],
setup(props, {
slots,
emit,
attrs
}) {
const {
proxy
} = vue.getCurrentInstance();
const show = vue.ref(props.open);
const self = {};
const menusRef = vue.ref(null);
const style = vue.ref({
left: 0,
top: 0,
minWidth: `${props.minWidth}px`,
maxWidth: props.maxWidth == 'none' ? props.maxWidth : `${props.maxWidth}px`,
zIndex: props.zIndex
});
const _direction = vue.ref(props.direction);
const activeIndex = vue.ref(-1);
const open = vue.ref(false);
const hasIcon = vue.ref(false);
const left = vue.ref(0);
const top = vue.ref(0);
let direction = props.direction;
const hasIcon = vue.computed(() => {
for (let index = 0; index < props.menus.length; index++) {
const menu = props.menus[index];
if (menu.icon !== undefined) {
return true;
}
}
});
const position = vue.computed(() => {
return {
x: props.event.clientX,
y: props.event.clientY,
width: props.event.width || 0,
height: props.event.height || 0
};
});
const style = vue.computed(() => {
return {
left: `${left.value}px`,
top: `${top.value}px`,
minWidth: `${props.minWidth}px`,
maxWidth: props.maxWidth == 'none' ? props.maxWidth : `${props.maxWidth}px`,
zIndex: props.zIndex
};
});
function leftOpen(menusWidth) {
style.value.left = _position.value.x - menusWidth;
_direction.value = 'left';
if (style.value.left < 0) {
_direction.value = 'right';
if (_position.value.width === 0 || _position.value.width === undefined) {
style.value.left = 0;
left.value = position.value.x - menusWidth;
direction = 'left';
if (left.value < 0) {
direction = 'right';
if (position.value.width === 0 || position.value.width === undefined) {
left.value = 0;
} else {
style.value.left = _position.valuen.x + _position.value.width;
left.value = position.value.x + position.value.width;
}

@@ -296,10 +149,12 @@ }

function rightOpen(windowWidth, menusWidth) {
style.value.left = _position.value.x + _position.value.width;
_direction.value = 'right';
if (style.value.left + menusWidth > windowWidth) {
_direction.value = 'left';
if (_position.value.width === 0 || _position.value.width === undefined) {
style.value.left = windowWidth - menusWidth;
left.value = position.value.x + position.value.width;
direction = 'right';
if (left.value + menusWidth > windowWidth) {
direction = 'left';
if (position.value.width === 0 || position.value.width === undefined) {
left.value = windowWidth - menusWidth;
} else {
style.value.left = _position.value.x - menusWidth;
left.value = position.value.x - menusWidth;
}

@@ -309,312 +164,275 @@ }

vue.onMounted(() => {
open.value = true;
props.menus.forEach(menu => {
hasIcon.value = hasIcon.value || menu.icon !== undefined;
if (hasIcon.value) {
return;
function closeEvent() {
activeIndex.value = -1;
show.value = false;
if (self && self.instance) {
self.instance.close.bind(self.instance)();
self.instance = null;
self.index = null; // @ts-ignore
if (proxy.closeAll) {
// @ts-ignore
proxy.closeAll();
}
});
vue.nextTick(() => {
const menusWidth = menusRef.value.offsetWidth;
const menusHeight = menusRef.value.offsetHeight;
if (_direction.value === 'left') {
leftOpen(menusWidth);
} else {
rightOpen(windowWidth, menusWidth);
}
style.value.top = _position.value.y;
if (_position.value.y + menusHeight > windowHeight) {
if (_position.value.height === 0 || _position.value.height === undefined) {
style.value.top = _position.value.y - menusHeight;
}
}
vue.watch(() => props.open, newVal => show.value = newVal);
vue.watch(show, newVal => {
if (newVal) {
vue.nextTick(() => {
const menusWidth = menusRef.value.offsetWidth;
const menusHeight = menusRef.value.offsetHeight;
if (direction === 'left') {
leftOpen(menusWidth);
} else {
style.value.top = windowHeight - menusHeight;
rightOpen(windowWidth, menusWidth);
}
}
});
top.value = position.value.y;
if (position.value.y + menusHeight > windowHeight) {
if (position.value.height === 0 || position.value.height === undefined) {
top.value = position.value.y - menusHeight;
} else {
top.value = windowHeight - menusHeight;
}
}
setTimeout(() => {
globalThis.document.addEventListener('click', closeEvent);
globalThis.document.addEventListener('contextmenu', closeEvent);
globalThis.document.addEventListener('wheel', closeEvent);
}, 0);
});
} else {
activeIndex.value = -1;
globalThis.document.removeEventListener('click', closeEvent);
globalThis.document.removeEventListener('contextmenu', closeEvent);
globalThis.document.removeEventListener('wheel', closeEvent);
}
}, {
immediate: true
});
function menusEnter(event, item, index) {
function mouseEnter(event, menu, index) {
emit('on-enter', menu, props.args);
event.preventDefault();
activeIndex.value = index;
if (item.disabled) {
if (!menu || menu.disabled || menu.hidden) {
return;
}
if (ctx.instance) {
if (ctx.index === index) {
if (self.instance) {
if (self.index === index) {
return;
}
ctx.instance.close.bind(ctx.instance)();
ctx.instance = null;
ctx.index = null;
self.instance.close.bind(self.instance)();
self.instance = null;
self.index = null;
}
if (!item.children) {
if (!menu.children) {
return;
}
let enter = props.enter && typeof props.enter === 'function' ? props.enter : null;
enter = menu.enter && typeof menu.enter === 'function' ? menu.enter : enter;
if (enter) {
const val = enter(menu, props.args);
if (val === false || val === null) {
return;
}
}
const menuItemClientRect = event.target.getBoundingClientRect();
const node = vue.h(script$1, {
...props,
menus: item.children || [],
direction: _direction.value,
position: {
x: menuItemClientRect.x + 3,
y: menuItemClientRect.y - 8,
const vm = vue.createVNode(vue3MenusComponent, { ...props,
menus: menu.children,
direction: direction,
event: {
clientX: menuItemClientRect.x + 3,
clientY: menuItemClientRect.y - 8,
width: menuItemClientRect.width - 2 * 3,
height: menuItemClientRect.width
},
open: false
}, slots);
const app = vue.createApp(node);
ctx.instance = app.mount(globalThis.document.createElement("div"));
ctx.instance.$unmount = app.unmount;
ctx.index = index;
const container = document.createElement('div');
vue.render(vm, container);
vm.component.props.open = true; // @ts-ignore
vm.component.proxy.close = close;
self.instance = vm.component.proxy;
self.instance.container = container;
self.instance.props = vm.component.props;
self.index = index;
}
function mouseClick(event, menu) {
emit('on-click', menu, props.args);
event.preventDefault();
}
function close() {
open.value = false;
if (this && this.ctx && this.ctx.instance) {
this.ctx.instance.close();
if (!menu || menu.disabled) {
event.stopPropagation();
return;
}
vue.nextTick(() => {
this.$unmount() && this.$unmount();
});
}
return {
open,
hasIcon,
menusRef,
style,
close,
menusEnter,
ctx,
activeIndex,
slots
};
},
};
const _withId = /*#__PURE__*/vue.withScopeId("data-v-d65e2e18");
let click = props.click && typeof props.click === 'function' ? props.click : null;
click = menu.click && typeof menu.click === 'function' ? menu.click : click;
vue.pushScopeId("data-v-d65e2e18");
const _hoisted_1 = { class: "menus_body" };
vue.popScopeId();
if (click) {
const val = click(menu, props.args);
const render = /*#__PURE__*/_withId(function render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_MenusItem = vue.resolveComponent("MenusItem");
if (val === false || val === null) {
event.stopPropagation();
}
}
return (vue.openBlock(), vue.createBlock(vue.Teleport, { to: "body" }, [
vue.createVNode(vue.Transition, { name: "menus-fade" }, {
default: _withId(() => [
($setup.open)
? (vue.openBlock(), vue.createBlock("div", {
key: 0,
ref: "menusRef",
class: "menus",
style: { ...$props.menusStyle, top: `${$setup.style.top}px`, left: `${$setup.style.left}px`, minWidth: $setup.style.minWidth, maxWidth: $setup.style.maxWidth, zIndex: $setup.style.zIndex },
onContextmenu: _cache[1] || (_cache[1] = (e) => e.preventDefault()),
onMousewheel: _cache[2] || (_cache[2] = vue.withModifiers(() => {}, ["stop"]))
}, [
vue.createVNode("div", _hoisted_1, [
(vue.openBlock(true), vue.createBlock(vue.Fragment, null, vue.renderList($props.menus, (item, index) => {
return (vue.openBlock(), vue.createBlock(vue.Fragment, { key: index }, [
(!item.hidden)
? (vue.openBlock(), vue.createBlock(_component_MenusItem, {
key: 0,
item: item,
index: index,
activeIndex: $setup.activeIndex,
onMenusEnter: $setup.menusEnter,
menusItemClass: $props.menusItemClass,
hasIcon: $setup.hasIcon
}, vue.createSlots({ _: 2 }, [
($setup.slots.default)
? {
name: "default",
fn: _withId(({ item }) => [
vue.renderSlot(_ctx.$slots, "default", { item: item })
])
}
: undefined,
(!$setup.slots.default && $setup.slots.icon)
? {
name: "icon",
fn: _withId(({ item }) => [
vue.renderSlot(_ctx.$slots, "icon", { item: item })
])
}
: undefined,
(!$setup.slots.default && $setup.slots.label)
? {
name: "label",
fn: _withId(({ item }) => [
vue.renderSlot(_ctx.$slots, "label", { item: item })
])
}
: undefined,
(!$setup.slots.default && $setup.slots.suffix)
? {
name: "suffix",
fn: _withId(({ item }) => [
vue.renderSlot(_ctx.$slots, "suffix", { item: item })
])
}
: undefined
]), 1032 /* PROPS, DYNAMIC_SLOTS */, ["item", "index", "activeIndex", "onMenusEnter", "menusItemClass", "hasIcon"]))
: vue.createCommentVNode("v-if", true)
], 64 /* STABLE_FRAGMENT */))
}), 128 /* KEYED_FRAGMENT */))
])
], 36 /* STYLE, HYDRATE_EVENTS */))
: vue.createCommentVNode("v-if", true)
]),
_: 1
})
]))
});
if (menu.children) {
event.stopPropagation();
}
}
var css_248z = "\n.menus[data-v-d65e2e18] {\n position: fixed;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);\n background: #fff;\n border-radius: 4px;\n padding: 8px 0;\n user-select: none;\n box-sizing: border-box;\n}\n.menus_body[data-v-d65e2e18] {\n display: block;\n}\n.menus-fade-enter-active[data-v-d65e2e18],\n.menus-fade-leave-active[data-v-d65e2e18] {\n transition: opacity 0.1s ease-in-out;\n}\n.menus-fade-enter-from[data-v-d65e2e18],\n.menus-fade-leave-to[data-v-d65e2e18] {\n opacity: 0;\n}\n";
styleInject(css_248z);
function close() {
this.show = false;
script$1.render = render;
script$1.__scopeId = "data-v-d65e2e18";
script$1.__file = "src/components/Menus.vue";
if (this.self && this.self.instance) {
this.self.instance.close();
}
var script = {
name: "vue3-menus",
props: {
menus: {
type: Array,
default: []
},
menusStyle: {
type: Object,
default: {}
},
menusItemClass: {
type: String,
default: null
},
event: {
type: Object,
default: {}
},
position: {
type: Object,
default: {}
},
minWidth: {
type: [Number, String],
default: 'none'
},
maxWidth: {
type: [Number, String],
default: 'none'
},
zIndex: {
type: [Number, String],
default: 2
},
open: {
type: Boolean,
default: false
vue.nextTick(() => {
vue.render(null, this.container);
});
}
},
setup(props, { emit, slots }) {
let lastInstance = null;
function mouseEvent() {
emit("update:open", false);
if (lastInstance) {
lastInstance.close();
lastInstance = null;
}
}
vue.watch(() => props.open, (newVal) => {
if (newVal) {
if (lastInstance) {
lastInstance.close();
lastInstance = null;
}
const node = vue.h(script$1, {
...props
}, slots);
const app = vue.createApp(node);
lastInstance = app.mount(globalThis.document.createElement("div"));
lastInstance.$unmount = app.unmount;
setTimeout(() => {
globalThis.document.addEventListener("click", mouseEvent);
globalThis.document.addEventListener("contextmenu", mouseEvent);
globalThis.document.addEventListener("wheel", mouseEvent);
}, 0);
} else {
globalThis.document.removeEventListener("click", mouseEvent);
globalThis.document.removeEventListener("contextmenu", mouseEvent);
globalThis.document.removeEventListener("wheel", mouseEvent);
}
const {
default: $default,
label,
icon,
suffix
} = slots;
const $class = ['v3-menus', attrs.class];
return () => vue.createVNode(vue.Teleport, {
"to": 'body'
}, {
default: () => [vue.createVNode(vue.Transition, {
"name": 'menus-fade'
}, {
default: () => [!show.value ? null : vue.createVNode("div", {
"ref": menusRef,
"class": $class,
"style": style.value,
"onWheel": e => e.preventDefault(),
"onContextmenu": e => e.preventDefault()
}, [vue.createVNode("div", {
"class": 'v3-menus-body'
}, [props.menus.map((menu, index) => {
if (menu.hidden) {
return null;
}
if ($default) {
return vue.createVNode("div", {
"onContextmenu": $event => mouseClick($event, menu),
"onClick": $event => mouseClick($event, menu),
"onMouseenter": $event => mouseEnter($event, menu, index)
}, [$default({
menu,
activeIndex,
index
})]);
} else {
let $class = [props.itemClass, 'v3-menus-item', menu.disabled ? 'v3-menus-disabled' : 'v3-menus-available'];
$class = $class.concat([menu.divided ? 'v3-menus-divided' : null, !menu.disabled && activeIndex.value === index ? 'v3-menus-active' : null]);
return vue.createVNode("div", {
"style": menu.style,
"class": $class.join(' '),
"onClick": $event => mouseClick($event, menu),
"onMouseenter": $event => mouseEnter($event, menu, index),
"onContextmenu": $event => mouseClick($event, menu)
}, [hasIcon ? vue.createVNode("div", {
"class": 'v3-menus-icon '
}, [icon ? icon({
menu,
activeIndex,
index
}) : vue.createVNode("span", {
"innerHTML": menu.icon
}, null)]) : null, label ? vue.createVNode("span", {
"class": 'v3-menus-label'
}, [label({
menu,
activeIndex,
index
})]) : vue.createVNode("span", {
"class": 'v3-menus-label'
}, [menu.label]), menu.children || menu.tip ? vue.createVNode("div", {
"class": 'v3-menus-suffix'
}, [suffix ? suffix({
menu,
activeIndex,
index
}) : menu.children ? '▶' : menu.tip ? vue.createVNode("span", {
"class": 'v3-menus-tip'
}, [menu.tip]) : null]) : null]);
}
})])])]
})]
});
return {}
},
render() {
return null;
}
};
script.__file = "src/components/Vue3Menus.vue";
});
let lastInstance = null;
function mouseEvent() {
if (lastInstance) {
lastInstance.close();
lastInstance = null;
function mouseEvent(menus, args, event) {
let props = {};
if (Array.isArray(menus)) {
props = {
menus,
event,
args,
open: false,
};
} else {
props = {
...menus,
args,
event,
open: false
};
}
globalThis.document.removeEventListener("click", mouseEvent);
globalThis.document.removeEventListener("contextmenu", mouseEvent);
globalThis.document.removeEventListener("wheel", mouseEvent);
}
function $menusEvent(menus, event) {
const temp = menus || {};
if (lastInstance) {
lastInstance.close();
lastInstance = null;
globalThis.document.removeEventListener("click", mouseEvent);
globalThis.document.removeEventListener("contextmenu", mouseEvent);
globalThis.document.removeEventListener("wheel", mouseEvent);
}
let instance = vue.createApp(script$1, {
event,
...temp
});
lastInstance = instance.mount(globalThis.document.createElement("div"));
lastInstance.$unmount = instance.unmount;
if (temp.prevent == undefined || temp.prevent) {
const vNode = vue.createVNode(vue3MenusComponent, props);
const container = document.createElement('div');
vue.render(vNode, container);
vNode.component.props.open = true;
vNode.component.proxy.closeAll = () => {
vue.nextTick(() => {
vue.render(null, container);
});
};
if (props.prevent == undefined || props.prevent) {
event.preventDefault();
}
setTimeout(() => {
globalThis.document.addEventListener("click", mouseEvent);
globalThis.document.addEventListener("contextmenu", mouseEvent);
globalThis.document.addEventListener("wheel", mouseEvent);
}, 0);
return lastInstance;
}
const directive = {
mounted(el, { value, arg, instance }) {
mounted(el, { value, arg }) {
const vnode = el.__vnode || {};
if (arg === undefined || arg === 'right') {
el.addEventListener("contextmenu", $menusEvent.bind(instance, value));
el.addEventListener("contextmenu", mouseEvent.bind(el, value, vnode.props));
} else if (arg === 'left') {
el.addEventListener("click", $menusEvent.bind(instance, value));
el.addEventListener("click", mouseEvent.bind(el, value, vnode.props));
} else if (arg === 'all') {
el.addEventListener("contextmenu", $menusEvent.bind(instance, value));
el.addEventListener("click", $menusEvent.bind(instance, value));
el.addEventListener("contextmenu", mouseEvent.bind(el, value, vnode.props));
el.addEventListener("click", mouseEvent.bind(el, value, vnode.props));
}
},
unmounted(el, { arg }) {
if (arg === undefined || arg === 'right') {
el.removeEventListener("contextmenu", $menusEvent);
} else if (arg === 'left') {
el.removeEventListener("click", $menusEvent);
} else if (arg === 'all') {
el.removeEventListener("contextmenu", $menusEvent);
el.removeEventListener("click", $menusEvent);
}
unmounted(el) {
el.removeEventListener("contextmenu", mouseEvent);
el.removeEventListener("click", mouseEvent);
}

@@ -624,14 +442,14 @@ };

const install = function (app, options = {}) {
app.component(options.name || script.name, script);
app.component(options.name || vue3MenusComponent.name, vue3MenusComponent);
app.directive('menus', directive);
app.config.globalProperties.$menusEvent = (event, menus) => $menusEvent(menus, event);
app.config.globalProperties.$menusEvent = (event, menus, args) => mouseEvent(menus, args, event);
};
const menusEvent = (event, menus) => $menusEvent(menus, event);
const menusEvent = (event, menus, args) => mouseEvent(menus, args, event);
function index(app){
function index (app) {
app.use(install);
}
exports.Vue3Menus = script;
exports.Vue3Menus = vue3MenusComponent;
exports["default"] = index;

@@ -638,0 +456,0 @@ exports.directive = directive;

@@ -1,1 +0,1 @@

!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("vue")):"function"==typeof define&&define.amd?define(["exports","vue"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Vue3Menus={},e.Vue)}(this,(function(e,t){"use strict";var n={name:"menus-item",components:{MenusIcon:n={name:"menus-icon",props:{options:{type:[Function,Object],default:{}}},render(){return"function"==typeof this.$props.options?t.h(this.$props.options):"function"==typeof this.$props.options.node||"object"==typeof this.$props.options.node?t.h(this.$props.options.node,this.$props.options.option):"object"!=typeof this.$props.options||this.$props.options.node?null:t.h(this.$props.options)},__file:"src/components/MenusIcon.vue"}},props:{menusItemClass:{type:String,default:null},hasIcon:{type:Boolean,default:!1},item:{type:Object,default:{}},index:{type:Number,default:0},activeIndex:{type:Number,default:-1}},setup:(e,{emit:t,slots:n})=>({slots:n,menusEnter:function(n,o){t("menusEnter",n,o,e.index),n.preventDefault()},menusClick:function(e,t){e.preventDefault(),t.disabled?e.stopPropagation():!t||t.disabled||t.hidden||"function"!=typeof t.click||!1!==(t=t.click(t))&&null!==t||e.stopPropagation()}})};const o={key:0,class:"menus-item-icon"},i={class:"menus-item-label"},l={class:"menus-item-suffix"},s=t.createTextVNode("▶"),a={key:3,class:"menus-item-tip"};function u(e,t){var n,o=(t=void 0===t?{}:t).insertAt;e&&"undefined"!=typeof document&&(n=document.head||document.getElementsByTagName("head")[0],(t=document.createElement("style")).type="text/css","top"===o&&n.firstChild?n.insertBefore(t,n.firstChild):n.appendChild(t),t.styleSheet?t.styleSheet.cssText=e:t.appendChild(document.createTextNode(e)))}u("\n.menus-item {\n display: flex;\n line-height: 2rem;\n padding: 0 1rem;\n margin: 0;\n font-size: 0.8rem;\n outline: 0;\n align-items: center;\n transition: 0.2s;\n box-sizing: border-box;\n list-style: none;\n border-bottom: 1px solid #00000000;\n}\n.menus-item-divided {\n border-bottom-color: #ebeef5;\n}\n.menus-item .menus-item-icon {\n display: flex;\n margin-right: 0.6rem;\n width: 1rem;\n}\n.menus-item .menus-item-label {\n flex: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.menus-item .menus-item-suffix {\n margin-left: 1.5rem;\n font-size: 0.39rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.menus-item-available {\n color: #606266;\n cursor: pointer;\n}\n.menus-item-available:hover {\n background: #ecf5ff;\n color: #409eff;\n}\n.menus-item-disabled {\n color: #c0c4cc;\n cursor: not-allowed;\n}\n.menus-item-active {\n background: #ecf5ff;\n color: #409eff;\n}\n.menus-item-tip {\n font-size: 9px;\n color: #999;\n}\n"),n.render=function(e,n,u,d,c,m){var r=t.resolveComponent("MenusIcon");return d.slots.default?(t.openBlock(),t.createBlock("div",{key:0,onMouseenter:n[1]||(n[1]=e=>d.menusEnter(e,u.item)),onClick:n[2]||(n[2]=e=>d.menusClick(e,u.item)),onContextmenu:n[3]||(n[3]=e=>d.menusClick(e,u.item))},[t.renderSlot(e.$slots,"default",{item:{activeIndex:u.activeIndex,item:u.item}})],32)):(t.openBlock(),t.createBlock("div",{key:1,onMouseenter:n[4]||(n[4]=e=>d.menusEnter(e,u.item)),onClick:n[5]||(n[5]=e=>d.menusClick(e,u.item)),onContextmenu:n[6]||(n[6]=e=>d.menusClick(e,u.item)),style:u.item.style||{},class:["menus-item",u.item.disabled?"menus-item-disabled":"menus-item-available",u.item.divided?"menus-divided":null,u.item.disabled||u.activeIndex!==u.index?null:"menus-item-active",u.menusItemClass]},[u.hasIcon?(t.openBlock(),t.createBlock("div",o,[d.slots.icon?t.renderSlot(e.$slots,"icon",{key:0,item:{activeIndex:u.activeIndex,item:u.item}}):u.item.icon?(t.openBlock(),t.createBlock(t.Fragment,{key:1},["string"==typeof u.item.icon?(t.openBlock(),t.createBlock("span",{key:0,innerHTML:u.item.icon},null,8,["innerHTML"])):(t.openBlock(),t.createBlock(r,{key:1,options:u.item.icon},null,8,["options"]))],64)):t.createCommentVNode("v-if",!0)])):t.createCommentVNode("v-if",!0),t.createVNode("span",i,[d.slots.label?t.renderSlot(e.$slots,"label",{key:0,item:{activeIndex:u.activeIndex,item:u.item}}):(t.openBlock(),t.createBlock(t.Fragment,{key:1},[t.createTextVNode(t.toDisplayString(u.item.label),1)],64))]),t.createVNode("div",l,[u.item.children&&d.slots.suffix?t.renderSlot(e.$slots,"suffix",{key:0,item:{activeIndex:u.activeIndex,item:u.item}}):u.item.children?(t.openBlock(),t.createBlock(t.Fragment,{key:1},[s],64)):u.item.tip&&d.slots.suffix?t.renderSlot(e.$slots,"suffix",{key:2,item:{activeIndex:u.activeIndex,item:u.item}}):u.item.tip?(t.openBlock(),t.createBlock("span",a,t.toDisplayString(u.item.tip),1)):t.createCommentVNode("v-if",!0)])],38))},n.__file="src/components/MenusItem.vue";var d={name:"menus",components:{MenusItem:n},props:{menus:{type:Array,default:[]},menusStyle:{type:Object,default:{}},menusItemClass:{type:String,default:null},event:{type:Object,default:{}},position:{type:Object,default:{}},minWidth:{type:[Number,String],default:"none"},maxWidth:{type:[Number,String],default:"none"},zIndex:{type:[Number,String],default:3},direction:{type:String,default:"right"},open:{type:Boolean,default:!1}},setup(e,{slots:n}){const o={},i=globalThis.document.documentElement.clientWidth,l=globalThis.document.documentElement.clientHeight,s=e.position.x&&e.position.y?t.ref(e.position):t.ref({x:e.event.clientX,y:e.event.clientY,width:0,height:0}),a=t.ref(null),u=t.ref({left:0,top:0,minWidth:`${e.minWidth}px`,maxWidth:"none"==e.maxWidth?e.maxWidth:`${e.maxWidth}px`,zIndex:e.zIndex}),c=t.ref(e.direction),m=t.ref(-1),r=t.ref(!1),p=t.ref(!1);return t.onMounted((()=>{r.value=!0,e.menus.forEach((e=>{p.value=p.value||void 0!==e.icon,p.value})),t.nextTick((()=>{var e,t=a.value.offsetWidth,n=a.value.offsetHeight;"left"===c.value?(e=t,u.value.left=s.value.x-e,c.value="left",u.value.left<0&&(c.value="right",0===s.value.width||void 0===s.value.width?u.value.left=0:u.value.left=s.valuen.x+s.value.width)):(e=i,t=t,u.value.left=s.value.x+s.value.width,c.value="right",u.value.left+t>e&&(c.value="left",0===s.value.width||void 0===s.value.width?u.value.left=e-t:u.value.left=s.value.x-t)),u.value.top=s.value.y,s.value.y+n>l&&(0===s.value.height||void 0===s.value.height?u.value.top=s.value.y-n:u.value.top=l-n)}))})),{open:r,hasIcon:p,menusRef:a,style:u,close:function(){r.value=!1,this&&this.ctx&&this.ctx.instance&&this.ctx.instance.close(),t.nextTick((()=>{this.$unmount()&&this.$unmount()}))},menusEnter:function(i,l,s){if(m.value=s,!l.disabled){if(o.instance){if(o.index===s)return;o.instance.close.bind(o.instance)(),o.instance=null,o.index=null}if(l.children){var a=i.target.getBoundingClientRect();a=t.h(d,{...e,menus:l.children||[],direction:c.value,position:{x:a.x+3,y:a.y-8,width:a.width-6,height:a.width}},n);const u=t.createApp(a);o.instance=u.mount(globalThis.document.createElement("div")),o.instance.$unmount=u.unmount,o.index=s,i.preventDefault()}}},ctx:o,activeIndex:m,slots:n}}};const c=t.withScopeId("data-v-d65e2e18");t.pushScopeId("data-v-d65e2e18");const m={class:"menus_body"};t.popScopeId(),n=c((function(e,n,o,i,l,s){const a=t.resolveComponent("MenusItem");return t.openBlock(),t.createBlock(t.Teleport,{to:"body"},[t.createVNode(t.Transition,{name:"menus-fade"},{default:c((()=>[i.open?(t.openBlock(),t.createBlock("div",{key:0,ref:"menusRef",class:"menus",style:{...o.menusStyle,top:`${i.style.top}px`,left:`${i.style.left}px`,minWidth:i.style.minWidth,maxWidth:i.style.maxWidth,zIndex:i.style.zIndex},onContextmenu:n[1]||(n[1]=e=>e.preventDefault()),onMousewheel:n[2]||(n[2]=t.withModifiers((()=>{}),["stop"]))},[t.createVNode("div",m,[(t.openBlock(!0),t.createBlock(t.Fragment,null,t.renderList(o.menus,((n,l)=>(t.openBlock(),t.createBlock(t.Fragment,{key:l},[n.hidden?t.createCommentVNode("v-if",!0):(t.openBlock(),t.createBlock(a,{key:0,item:n,index:l,activeIndex:i.activeIndex,onMenusEnter:i.menusEnter,menusItemClass:o.menusItemClass,hasIcon:i.hasIcon},t.createSlots({_:2},[i.slots.default?{name:"default",fn:c((({item:n})=>[t.renderSlot(e.$slots,"default",{item:n})]))}:void 0,!i.slots.default&&i.slots.icon?{name:"icon",fn:c((({item:n})=>[t.renderSlot(e.$slots,"icon",{item:n})]))}:void 0,!i.slots.default&&i.slots.label?{name:"label",fn:c((({item:n})=>[t.renderSlot(e.$slots,"label",{item:n})]))}:void 0,!i.slots.default&&i.slots.suffix?{name:"suffix",fn:c((({item:n})=>[t.renderSlot(e.$slots,"suffix",{item:n})]))}:void 0]),1032,["item","index","activeIndex","onMenusEnter","menusItemClass","hasIcon"]))],64)))),128))])],36)):t.createCommentVNode("v-if",!0)])),_:1})])})),u("\n.menus[data-v-d65e2e18] {\n position: fixed;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);\n background: #fff;\n border-radius: 4px;\n padding: 8px 0;\n user-select: none;\n box-sizing: border-box;\n}\n.menus_body[data-v-d65e2e18] {\n display: block;\n}\n.menus-fade-enter-active[data-v-d65e2e18],\n.menus-fade-leave-active[data-v-d65e2e18] {\n transition: opacity 0.1s ease-in-out;\n}\n.menus-fade-enter-from[data-v-d65e2e18],\n.menus-fade-leave-to[data-v-d65e2e18] {\n opacity: 0;\n}\n"),d.render=n,d.__scopeId="data-v-d65e2e18",d.__file="src/components/Menus.vue";var r={name:"vue3-menus",props:{menus:{type:Array,default:[]},menusStyle:{type:Object,default:{}},menusItemClass:{type:String,default:null},event:{type:Object,default:{}},position:{type:Object,default:{}},minWidth:{type:[Number,String],default:"none"},maxWidth:{type:[Number,String],default:"none"},zIndex:{type:[Number,String],default:2},open:{type:Boolean,default:!1}},setup(e,{emit:n,slots:o}){let i=null;function l(){n("update:open",!1),i&&(i.close(),i=null)}return t.watch((()=>e.open),(n=>{if(n){i&&(i.close(),i=null),n=t.h(d,{...e},o);const s=t.createApp(n);i=s.mount(globalThis.document.createElement("div")),i.$unmount=s.unmount,setTimeout((()=>{globalThis.document.addEventListener("click",l),globalThis.document.addEventListener("contextmenu",l),globalThis.document.addEventListener("wheel",l)}),0)}else globalThis.document.removeEventListener("click",l),globalThis.document.removeEventListener("contextmenu",l),globalThis.document.removeEventListener("wheel",l)})),{}},render:()=>null,__file:"src/components/Vue3Menus.vue"};let p=null;function f(){p&&(p.close(),p=null),globalThis.document.removeEventListener("click",f),globalThis.document.removeEventListener("contextmenu",f),globalThis.document.removeEventListener("wheel",f)}function v(e,n){e=e||{},p&&(p.close(),p=null,globalThis.document.removeEventListener("click",f),globalThis.document.removeEventListener("contextmenu",f),globalThis.document.removeEventListener("wheel",f));let o=t.createApp(d,{event:n,...e});return p=o.mount(globalThis.document.createElement("div")),p.$unmount=o.unmount,null!=e.prevent&&!e.prevent||n.preventDefault(),setTimeout((()=>{globalThis.document.addEventListener("click",f),globalThis.document.addEventListener("contextmenu",f),globalThis.document.addEventListener("wheel",f)}),0),p}function h(e,t={}){e.component(t.name||r.name,r),e.directive("menus",x),e.config.globalProperties.$menusEvent=(e,t)=>v(t,e)}const x={mounted(e,{value:t,arg:n,instance:o}){void 0===n||"right"===n?e.addEventListener("contextmenu",v.bind(o,t)):"left"===n?e.addEventListener("click",v.bind(o,t)):"all"===n&&(e.addEventListener("contextmenu",v.bind(o,t)),e.addEventListener("click",v.bind(o,t)))},unmounted(e,{arg:t}){void 0===t||"right"===t?e.removeEventListener("contextmenu",v):"left"===t?e.removeEventListener("click",v):"all"===t&&(e.removeEventListener("contextmenu",v),e.removeEventListener("click",v))}};e.Vue3Menus=r,e.default=function(e){e.use(h)},e.directive=x,e.menusEvent=(e,t)=>v(t,e),Object.defineProperty(e,"__esModule",{value:!0})}));
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports,require("vue")):"function"==typeof define&&define.amd?define(["exports","vue"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).Vue3Menus={},e.Vue)}(this,(function(e,n){"use strict";var t,i,o;l=".menus-fade-enter-active,\n.menus-fade-leave-active {\n transition: opacity 0.2s ease-in-out;\n}\n.menus-fade-enter-from,\n.menus-fade-leave-to {\n opacity: 0;\n}\n\n.v3-menus {\n position: fixed;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);\n background: #fff;\n border-radius: 4px;\n padding: 8px 0;\n user-select: none;\n box-sizing: border-box;\n}\n\n.v3-menus-body {\n display: block;\n}\n\n.v3-menus-item {\n display: flex;\n line-height: 2rem;\n padding: 0 1rem;\n margin: 0;\n font-size: 0.8rem;\n outline: 0;\n align-items: center;\n transition: 0.2s;\n box-sizing: border-box;\n list-style: none;\n border-bottom: 1px solid #00000000;\n}\n\n.v3-menus-divided {\n border-bottom-color: #ebeef5;\n}\n\n.v3-menus-icon {\n display: flex;\n margin-right: 0.6rem;\n width: 1rem;\n}\n\n.v3-menus-label {\n flex: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.v3-menus-suffix {\n margin-left: 1.5rem;\n font-size: 0.39rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.v3-menus-available {\n color: #606266;\n cursor: pointer;\n}\n\n.v3-menus-available:hover {\n background: #ecf5ff;\n color: #409eff;\n}\n\n.v3-menus-disabled {\n color: #c0c4cc;\n cursor: not-allowed;\n}\n\n.v3-menus-active {\n background: #ecf5ff;\n color: #409eff;\n}\n\n.v3-menus-tip {\n font-size: 9px;\n color: #999;\n}\n",o=(t=void 0===t?{}:t).insertAt,l&&"undefined"!=typeof document&&(i=document.head||document.getElementsByTagName("head")[0],(t=document.createElement("style")).type="text/css","top"===o&&i.firstChild?i.insertBefore(t,i.firstChild):i.appendChild(t),t.styleSheet?t.styleSheet.cssText=l:t.appendChild(document.createTextNode(l)));var l={menus:{type:Array,required:!0},itemClass:{type:String,default:null},event:{type:Object,required:!0},minWidth:{type:[Number,String],default:"none"},maxWidth:{type:[Number,String],default:"none"},zIndex:{type:Number,default:3},direction:{type:String,default:"right"},click:{type:Function},enter:{type:Function},open:{type:Boolean,default:!1},args:{type:[Object,Function,Array,Boolean,String],default:{}}};const a=globalThis.document.documentElement.clientWidth,r=globalThis.document.documentElement.clientHeight,s=n.defineComponent({name:"vue3-menus",inheritAttrs:!1,props:l,emits:["on-click","on-enter"],setup(e,{slots:t,emit:i,attrs:o}){const l=n.getCurrentInstance().proxy,d=n.ref(e.open),u={},c=n.ref(null),v=n.ref(-1),m=n.ref(0),p=n.ref(0);let f=e.direction;const h=n.computed((()=>{for(let n=0;n<e.menus.length;n++)if(void 0!==e.menus[n].icon)return!0})),x=n.computed((()=>({x:e.event.clientX,y:e.event.clientY,width:e.event.width||0,height:e.event.height||0}))),g=n.computed((()=>({left:m.value+"px",top:p.value+"px",minWidth:e.minWidth+"px",maxWidth:"none"==e.maxWidth?e.maxWidth:e.maxWidth+"px",zIndex:e.zIndex})));function b(){v.value=-1,d.value=!1,u&&u.instance&&(u.instance.close.bind(u.instance)(),u.instance=null,u.index=null,l.closeAll&&l.closeAll())}function y(o,l,a){if(i("on-enter",l,e.args),o.preventDefault(),v.value=a,l&&!l.disabled&&!l.hidden){if(u.instance){if(u.index===a)return;u.instance.close.bind(u.instance)(),u.instance=null,u.index=null}if(l.children){let i=e.enter&&"function"==typeof e.enter?e.enter:null;if(i=l.enter&&"function"==typeof l.enter?l.enter:i,i){var r=i(l,e.args);if(!1===r||null===r)return}o=o.target.getBoundingClientRect();const d=n.createVNode(s,{...e,menus:l.children,direction:f,event:{clientX:o.x+3,clientY:o.y-8,width:o.width-6,height:o.width},open:!1},t);o=document.createElement("div"),n.render(d,o),d.component.props.open=!0,d.component.proxy.close=k,u.instance=d.component.proxy,u.instance.container=o,u.instance.props=d.component.props,u.index=a}}}function w(n,t){if(i("on-click",t,e.args),n.preventDefault(),t&&!t.disabled){let i=e.click&&"function"==typeof e.click?e.click:null;var o;i=t.click&&"function"==typeof t.click?t.click:i,i&&(!1!==(o=i(t,e.args))&&null!==o||n.stopPropagation()),t.children&&n.stopPropagation()}else n.stopPropagation()}function k(){this.show=!1,this.self&&this.self.instance&&this.self.instance.close(),n.nextTick((()=>{n.render(null,this.container)}))}n.watch((()=>e.open),(e=>d.value=e)),n.watch(d,(e=>{e?n.nextTick((()=>{var e,n=c.value.offsetWidth,t=c.value.offsetHeight;"left"===f?(e=n,m.value=x.value.x-e,f="left",m.value<0&&(f="right",0===x.value.width||void 0===x.value.width?m.value=0:m.value=x.value.x+x.value.width)):(e=a,n=n,m.value=x.value.x+x.value.width,f="right",m.value+n>e&&(f="left",0===x.value.width||void 0===x.value.width?m.value=e-n:m.value=x.value.x-n)),p.value=x.value.y,x.value.y+t>r&&(0===x.value.height||void 0===x.value.height?p.value=x.value.y-t:p.value=r-t),setTimeout((()=>{globalThis.document.addEventListener("click",b),globalThis.document.addEventListener("contextmenu",b),globalThis.document.addEventListener("wheel",b)}),0)})):(v.value=-1,globalThis.document.removeEventListener("click",b),globalThis.document.removeEventListener("contextmenu",b),globalThis.document.removeEventListener("wheel",b))}),{immediate:!0});const{default:E,label:T,icon:N,suffix:V}=t,C=["v3-menus",o.class];return()=>n.createVNode(n.Teleport,{to:"body"},{default:()=>[n.createVNode(n.Transition,{name:"menus-fade"},{default:()=>[d.value?n.createVNode("div",{ref:c,class:C,style:g.value,onWheel:e=>e.preventDefault(),onContextmenu:e=>e.preventDefault()},[n.createVNode("div",{class:"v3-menus-body"},[e.menus.map(((t,i)=>{if(t.hidden)return null;if(E)return n.createVNode("div",{onContextmenu:e=>w(e,t),onClick:e=>w(e,t),onMouseenter:e=>y(e,t,i)},[E({menu:t,activeIndex:v,index:i})]);{let o=[e.itemClass,"v3-menus-item",t.disabled?"v3-menus-disabled":"v3-menus-available"];return o=o.concat([t.divided?"v3-menus-divided":null,t.disabled||v.value!==i?null:"v3-menus-active"]),n.createVNode("div",{style:t.style,class:o.join(" "),onClick:e=>w(e,t),onMouseenter:e=>y(e,t,i),onContextmenu:e=>w(e,t)},[h?n.createVNode("div",{class:"v3-menus-icon "},[N?N({menu:t,activeIndex:v,index:i}):n.createVNode("span",{innerHTML:t.icon},null)]):null,T?n.createVNode("span",{class:"v3-menus-label"},[T({menu:t,activeIndex:v,index:i})]):n.createVNode("span",{class:"v3-menus-label"},[t.label]),t.children||t.tip?n.createVNode("div",{class:"v3-menus-suffix"},[V?V({menu:t,activeIndex:v,index:i}):t.children?"▶":t.tip?n.createVNode("span",{class:"v3-menus-tip"},[t.tip]):null]):null])}}))])]):null]})]})}});function d(e,t,i){let o={};o=Array.isArray(e)?{menus:e,event:i,args:t,open:!1}:{...e,args:t,event:i,open:!1};const l=n.createVNode(s,o),a=document.createElement("div");n.render(l,a),l.component.props.open=!0,l.component.proxy.closeAll=()=>{n.nextTick((()=>{n.render(null,a)}))},null!=o.prevent&&!o.prevent||i.preventDefault()}function u(e,n={}){e.component(n.name||s.name,s),e.directive("menus",c),e.config.globalProperties.$menusEvent=(e,n,t)=>d(n,t,e)}const c={mounted(e,{value:n,arg:t}){var i=e.__vnode||{};void 0===t||"right"===t?e.addEventListener("contextmenu",d.bind(e,n,i.props)):"left"===t?e.addEventListener("click",d.bind(e,n,i.props)):"all"===t&&(e.addEventListener("contextmenu",d.bind(e,n,i.props)),e.addEventListener("click",d.bind(e,n,i.props)))},unmounted(e){e.removeEventListener("contextmenu",d),e.removeEventListener("click",d)}};e.Vue3Menus=s,e.default=function(e){e.use(u)},e.directive=c,e.menusEvent=(e,n,t)=>d(n,t,e),Object.defineProperty(e,"__esModule",{value:!0})}));

@@ -1,58 +0,38 @@

type elementIconType = import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin,
import('vue').ComponentOptionsMixin, import('vue').EmitsOptions, string,
import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, Readonly<{} & {} & {}>, {}>;
type antdvIconType = import('vue').FunctionalComponent;
type menusItemType<I = string | elementIconType | antdvIconType> = {
type menusItemType = {
label: string;
icon?: I | {
node: I,
option?: {
[key: string]: unknown
}
};
style?: {
[key: string]: string | number
}
icon?: string | unknown;
disabled?: boolean;
divided?: boolean;
click?: (...arg: unknown[]) => unknown;
enter?: (menu: menusItemType, args: unknown) => unknown;
click?: (menu: menusItemType, args: unknown) => unknown;
children?: Array<menusItemType>;
tip?: string;
hidden?: boolean;
style?: {
[key: string]: string | number
}
}
type baseType<I = string | elementIconType | antdvIconType> = {
menus: Array<menusItemType<I>>;
menusStyle?: {
[key: string]: unknown
};
menusItemClass?: string;
type menusType = {
menus: Array<menusItemType>;
itemClass?: string;
minWidth?: number | string;
maxWidth?: number | string;
zIndex?: number | string;
direction?: "left" | "right";
enter?: (menu: menusItemType, args: unknown) => unknown;
click?: (menu: menusItemType, args: unknown) => unknown;
}
type menusType<I = string | elementIconType | antdvIconType> = {
event: MouseEvent;
} & baseType<I> | {
position: {
x: number,
y: number
};
} & baseType<I> | {
type componentMenusType = menusType & {
event?: MouseEvent;
position?: {
x: number,
y: number
};
} & baseType<I>
open: boolean;
args?: unknown
}
declare module 'vue3-menus' {
export const Vue3Menus: import('vue').DefineComponent<menusType & {
open: boolean
}>;
export const Vue3Menus: import('vue').DefineComponent<componentMenusType, componentMenusType, componentMenusType, componentMenusType, componentMenusType,
componentMenusType, componentMenusType, componentMenusType, componentMenusType, componentMenusType, componentMenusType, componentMenusType>;
export const menusEvent: (event: MouseEvent, menus: menusType) => import('vue').ComponentPublicInstance;
export const menusEvent: (event: MouseEvent, menus: menusType, args: unknown) => void;

@@ -66,1 +46,6 @@ export const directive: import('vue').Directive;

}
export {
menusType,
menusItemType
}
{
"name": "vue3-menus",
"version": "1.0.17",
"version": "1.1.0",
"author": "xufangyi",

@@ -36,3 +36,4 @@ "private": false,

"scripts": {
"build": "rollup -c"
"build": "rollup -c",
"dev": "vite"
},

@@ -42,2 +43,4 @@ "dependencies": {},

"@babel/core": "^7.15.5",
"@vitejs/plugin-vue": "^1.10.1",
"@vitejs/plugin-vue-jsx": "^1.3.0",
"@vue/compiler-sfc": "3.0.0",

@@ -54,4 +57,5 @@ "rollup": "^2.57.0",

"rollup-plugin-vue": "^6.0.0",
"vite": "^2.7.0-beta.11",
"vue": "3.0.0"
}
}
# vue3-menus
Vue3.0 自定义右键菜单,支持 Vite2.0
Vue3.0 自定义右键菜单,支持 Vite2.0,[官网](https://doc.wssio.com/opensource/vue3-menus/)
Vue3.0 原生实现完全自定义右键菜单组件, 零依赖,可根据可视区域自动调节显示位置,可支持插槽完全重写每一项菜单
![演示](./public/info.png)
![演示](./example/vue3-menus.png)

@@ -64,5 +64,5 @@ ## 在线演示

<div class="div" @click.stop @contextmenu="rightClick">组件方式打开菜单</div>
<vue3-menus v-model:open="isOpen" :event="eventVal" :menus="menus.menus" hasIcon>
<template #icon="{item: {activeIndex}}">{{activeIndex}}</template>
<template #label="{ item: { item } }">插槽:{{ item.label }}</template>
<vue3-menus :open="isOpen" :event="eventVal" :menus="menus.menus">
<template #icon="{menu, activeIndex, index}">{{activeIndex}}</template>
<template #label="{ menu, activeIndex, index }">插槽:{{ menu.label }}</template>
</vue3-menus>

@@ -113,8 +113,2 @@ </div>

tip: 'Ctrl+R',
icon: {
node: SyncOutlined,
option: {
spin: true
}
},
click: () => location.reload(),

@@ -130,8 +124,2 @@ divided: true

tip: 'Ctrl+P',
icon: {
node: Printer,
option: {
color: 'red'
}
},
click: () => window.print(),

@@ -145,3 +133,2 @@ },

label: '发送到你的设备',
icon: WindowsOutlined,
children: [

@@ -162,10 +149,2 @@ {

divided: true,
icon: {
node: QrcodeOutlined,
option: {
style: {
color: 'aqua'
}
}
}
},

@@ -308,4 +287,4 @@ {

<vue3-menus v-model:open="isOpen" :event="eventVal" :menus="menus" hasIcon>
<template #icon="{item: {activeIndex}}">{{activeIndex}}</template>
<template #label="{ item: { item } }">插槽:{{ item.label }}</template>
<template #icon="{menu, activeIndex, index}">{{activeIndex}}</template>
<template #label="{ menu, activeIndex, index}">插槽:{{ menu.label }}</template>
</vue3-menus>

@@ -359,46 +338,52 @@ </template>

| 属性 | 描述 | 类型 | 是否必填 | 默认值 |
| :------: | :------------------------------------------------------------------------------------: | :------------------: | :------: | :---------: |
| label | 菜单项名称 | `string` | `true` | — |
| style | 每一项菜单的自定义样式 | `object` | `false` | `{}` |
| icon | `string`: 传入图标html代码、`object`: 传入组件或者`{node: 组件, option: 组件配置参数}` | `string` \| `object` | `false` | `undefined` |
| disabled | 是否禁用菜单项 | `boolean` | `false` | `undefined` |
| divided | 是否显示分割线 | `boolean` | `false` | `undefined` |
| tip | 没项菜单后面的小提示 | `string` | `false` | `''` |
| click | 菜单项点击事件,返回`null`或`false`不关闭菜单 | `Function()` | `false` | `undefined` |
| children | 子菜单列表信息 | `MenusItemOptions[]` | `false` | `undefined` |
| 属性 | 描述 | 类型 | 是否必填 | 默认值 |
| :------: | :----------------------------------------------------------: | :--------------------: | :------: | :---------: |
| label | 菜单项名称 | `string` | `true` | — |
| style | 每一项菜单的自定义样式 | `object` | `false` | `{}` |
| icon | 图标参数,内部支持html字符串图标,传入组件时需要实现icon插槽 | `string` \| `其他类型` | `false` | `undefined` |
| disabled | 是否禁用菜单项 | `boolean` | `false` | `undefined` |
| divided | 是否显示分割线 | `boolean` | `false` | `undefined` |
| tip | 没项菜单后面的小提示 | `string` | `false` | `''` |
| hidden | 是否隐藏该项 | `boolean` | `false` | `undefined` |
| children | 子菜单列表信息 | `MenusItemOptions[]` | `false` | `undefined` |
| enter | 菜单项移入事件,返回`null`或`false`不展开子菜单 | `Function()` | `false` | `undefined` |
| click | 菜单项点击事件,返回`null`或`false`不关闭菜单 | `Function()` | `false` | `undefined` |
### 公共参数`MenuOptions`
### 方法使用参数
| 属性 | 描述 | 类型 | 是否必填 | 默认值 |
| :------------: | :--------------------------------------: | :----------------------: | :------------------: | :----: |
| menus | 菜单列表信息 | `MenusItemOptions[]` | `true` | [] |
| menusStyle | 菜单容器的样式 | `object` | `false` | {} |
| menusItemClass | 菜单每一项的`class`名 | `string` | `false` | `null` |
| event | 鼠标事件信息(指令使用时可以不传) | `Event` | 与`position`必填一项 | {} |
| position | 手动传入菜单显示位置(指令使用时可以不传) | `{x: number, y: number}` | 与`event`必填一项 | {} |
| minWidth | 菜单容器最小宽度 | `number` \| `string` | `false` | `none` |
| maxWidth | 菜单容器最打宽度 | `number` \| `string` | `false` | `none` |
| zIndex | 菜单层级 | `number` \| `string` | `false` | `3` |
| 属性 | 描述 | 类型 | 是否必填 | 默认值 |
| :-------: | :---------------------------------------------: | :-------------------: | :------: | :---------: |
| menus | 菜单列表信息 | `MenusItemOptions[]` | `true` | [] |
| itemClass | 菜单每一项的`class`名 | `string` | `false` | `null` |
| minWidth | 菜单容器最小宽度 | `number` \| `string` | `false` | `none` |
| maxWidth | 菜单容器最打宽度 | `number` \| `string` | `false` | `none` |
| zIndex | 菜单层级 | `number` \| `string` | `false` | `3` |
| direction | 菜单打开方向 | `left` \| `right` | `false` | `right` |
| enter | 菜单项移入事件,返回`null`或`false`不展开子菜单 | `Function()` | `false` | `undefined` |
| click | 菜单项点击事件,返回`null`或`false`不关闭菜单 | `Function()` | `false` | `undefined` |
### 组件`Vue3Menus`参数
### 组件使用参数
| 属性 | 描述 | 类型 | 是否必填 | 默认值 | 插槽传入值 |
| :------------: | :--------------------------------------: | :----------------------: | :------------------: | :-----: | :-----------------------------------------------: |
| menus | 菜单列表信息 | `MenusItemOptions[]` | `true` | [] |
| menusStyle | 菜单容器的样式 | `object` | `false` | {} |
| menusItemClass | 菜单每一项的`class`名 | `string` | `false` | `null` |
| event | 鼠标事件信息(指令使用时可以不传) | `Event` | 与`position`必填一项 | {} |
| position | 手动传入菜单显示位置(指令使用时可以不传) | `{x: number, y: number}` | 与`event`必填一项 | {} |
| minWidth | 菜单容器最小宽度 | `number` \| `string` | `false` | `none` |
| maxWidth | 菜单容器最打宽度 | `number` \| `string` | `false` | `none` |
| zIndex | 菜单层级 | `number` \| `string` | `false` | `3` |
| open | 控制菜单组件显示: `v-model:open` | `boolean` | `true` | `false` | `false` |
| default | 默认插槽 | `Slot` | `false` | - | `activeIndex`: 当前选中项, `item`: 当前菜单属性值 |
| icon | 图标插槽 | `Slot` | `false` | - | `activeIndex`: 当前选中项, `item`: 当前菜单属性值 |
| label | 菜单标题插槽 | `Slot` | `false` | - | `activeIndex`: 当前选中项, `item`: 当前菜单属性值 |
| suffix | 菜单后缀插槽 | `Slot` | `false` | - | `activeIndex`: 当前选中项, `item`: 当前菜单属性值 |
| 属性 | 描述 | 类型 | 是否必填 | 默认值 | 插槽传入值 |
| :-------: | :---------------------------------------------: | :-------------------: | :------------------: | :---------: | :-----------------------------------------------: |
| menus | 菜单列表信息 | `MenusItemOptions[]` | `true` | [] | |
| event | 鼠标事件信息(指令使用时可以不传) | `Event` | 与`position`必填一项 | {} | |
| itemClass | 菜单每一项的`class`名 | `string` | `false` | `null` | |
| minWidth | 菜单容器最小宽度 | `number` \| `string` | `false` | `none` | |
| maxWidth | 菜单容器最打宽度 | `number` \| `string` | `false` | `none` | |
| zIndex | 菜单层级 | `number` \| `string` | `false` | `3` | |
| direction | 菜单打开方向 | `left` \| `right` | `false` | `right` | |
| open | 控制菜单组件显示 | `boolean` | `true` | `false` | |
| args | 附加参数 | `unknown` | `false` | `undefined` | |
| enter | 菜单项移入事件,返回`null`或`false`不展开子菜单 | `Function()` | `false` | `undefined` | |
| click | 菜单项点击事件,返回`null`或`false`不关闭菜单 | `Function()` | `false` | `undefined` | |
| default | 默认插槽 | `Slot` | `false` | - | `activeIndex`: 当前选中项, `item`: 当前菜单属性值 |
| icon | 图标插槽 | `Slot` | `false` | - | `activeIndex`: 当前选中项, `item`: 当前菜单属性值 |
| label | 菜单标题插槽 | `Slot` | `false` | - | `activeIndex`: 当前选中项, `item`: 当前菜单属性值 |
| suffix | 菜单后缀插槽 | `Slot` | `false` | - | `activeIndex`: 当前选中项, `item`: 当前菜单属性值 |
### 指令使用配置
> 配置参数与方法使用相同
| 指令使用方式 | 描述 | 参数类型 | 参数是否必填 | 默认值 |

@@ -405,0 +390,0 @@ | :-----------: | :------------------------: | :-----------: | :----------: | :----: |

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc