mattercontrol/src/components/ContextMenu.vue

111 lines
2.2 KiB
Vue

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
export interface MenuItem {
label: string
action: string
disabled?: boolean
separator?: boolean
}
defineProps<{
x: number
y: number
items: MenuItem[]
}>()
const emit = defineEmits<{
select: [action: string]
close: []
}>()
const menuRef = ref<HTMLElement | null>(null)
function handleClick(item: MenuItem) {
if (item.disabled) return
emit('select', item.action)
}
function handleClickOutside(event: MouseEvent) {
if (menuRef.value && !menuRef.value.contains(event.target as Node)) {
emit('close')
}
}
function handleKeyDown(event: KeyboardEvent) {
if (event.key === 'Escape') {
emit('close')
}
}
onMounted(() => {
document.addEventListener('mousedown', handleClickOutside)
document.addEventListener('keydown', handleKeyDown)
})
onUnmounted(() => {
document.removeEventListener('mousedown', handleClickOutside)
document.removeEventListener('keydown', handleKeyDown)
})
</script>
<template>
<div
ref="menuRef"
class="context-menu"
:style="{ left: `${x}px`, top: `${y}px` }"
>
<template v-for="(item, index) in items" :key="index">
<div v-if="item.separator" class="separator" />
<button
v-else
class="menu-item"
:class="{ disabled: item.disabled }"
:disabled="item.disabled"
@click="handleClick(item)"
>
{{ item.label }}
</button>
</template>
</div>
</template>
<style scoped>
.context-menu {
position: fixed;
z-index: 1000;
min-width: 150px;
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
padding: var(--space-xs) 0;
}
.menu-item {
display: block;
width: 100%;
padding: var(--space-sm) var(--space-md);
text-align: left;
background: none;
border: none;
color: var(--color-text-primary);
font-size: var(--font-size-sm);
cursor: pointer;
}
.menu-item:hover:not(.disabled) {
background: var(--color-bg-tertiary);
}
.menu-item.disabled {
color: var(--color-text-muted);
cursor: not-allowed;
}
.separator {
height: 1px;
background: var(--color-border);
margin: var(--space-xs) 0;
}
</style>