vue3-menus
Advanced tools
Comparing version 1.0.17 to 1.1.0
@@ -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" | ||
} | ||
} |
115
README.md
# 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 @@ | :-----------: | :------------------------: | :-----------: | :----------: | :----: | |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
75538
16
10
886
388
3
1