All checks were successful
Frontend Deployment / deploy-frontend (push) Successful in 35s
182 lines
3.9 KiB
Vue
182 lines
3.9 KiB
Vue
<script setup>
|
|
import IconAction from './IconAction.vue';
|
|
import { ref, onMounted, watch, onUnmounted } from 'vue';
|
|
import { useSlots } from 'vue';
|
|
|
|
const slots = useSlots();
|
|
const boxWidth = ref(0);
|
|
const box = ref(null);
|
|
|
|
var slotsAll = slots.default ? slots.default() : [];
|
|
var allSlots = null
|
|
|
|
if(slotsAll.length > 0) {
|
|
if(typeof slotsAll[0].children !== "string") {
|
|
allSlots = slotsAll[0].children
|
|
} else {
|
|
allSlots = slotsAll
|
|
}
|
|
|
|
|
|
}
|
|
|
|
var firstSlot = allSlots[0] || null;
|
|
var otherSlots = allSlots.slice(1);
|
|
|
|
const showMenu = ref(false);
|
|
|
|
function selectOption(index) {
|
|
// Make it like a select
|
|
if (otherSlots[index]) {
|
|
const oldFirstSlot = firstSlot;
|
|
firstSlot = otherSlots[index];
|
|
otherSlots.splice(index, 1);
|
|
otherSlots.push(oldFirstSlot);
|
|
showMenu.value = false; // Hide the menu after selection
|
|
}
|
|
updateModelValue(firstSlot ? firstSlot.props ? firstSlot.props.value : '' : '');
|
|
}
|
|
|
|
const props = defineProps({
|
|
modelValue: String
|
|
});
|
|
|
|
const emit = defineEmits(['update:modelValue']);
|
|
|
|
function updateModelValue(value) {
|
|
emit('update:modelValue', value);
|
|
}
|
|
|
|
onMounted(() => {
|
|
|
|
window.addEventListener('rightclick', (e) => {
|
|
e.preventDefault(); // Prevent default context menu
|
|
});
|
|
|
|
window.addEventListener('click', (e) => {
|
|
hideMenu(e);
|
|
});
|
|
|
|
window.addEventListener('resize', () => {
|
|
showMenu.value = false;
|
|
|
|
});
|
|
updateModelValue(firstSlot ? firstSlot.props ? firstSlot.props.value : '' : '');
|
|
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
window.removeEventListener('rightclick', (e) => {
|
|
e.preventDefault(); // Prevent default context menu
|
|
});
|
|
window.removeEventListener('click', (e) => {
|
|
hideMenu(e);
|
|
});
|
|
|
|
window.removeEventListener('resize', () => {
|
|
showMenu.value = false;
|
|
});
|
|
});
|
|
|
|
function hideMenu(e) {
|
|
// Check if the click is outside the context menu
|
|
if (box.value && !box.value.contains(e.target)) {
|
|
showMenu.value = false; // Hide context menu on click outside
|
|
}
|
|
}
|
|
|
|
defineExpose({
|
|
firstSlot: () => {
|
|
return firstSlot;
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<section>
|
|
<div ref="box" :class="showMenu ? `showed firstbox` : 'firstbox'" @click="showMenu = !showMenu">
|
|
<template v-if="firstSlot">
|
|
<component style="white-space: break-spaces;" :is="firstSlot" />
|
|
</template>
|
|
<IconAction
|
|
class="icon"
|
|
:icon="showMenu ? 'fa-solid fa-angle-up' : 'fa-solid fa-angle-down'"
|
|
/>
|
|
</div>
|
|
<template v-if="otherSlots.length && showMenu">
|
|
<div class="container">
|
|
<div v-for="(slot, index) in otherSlots" :style="`width: ${box.offsetWidth - 10}px;`" class="option" @click="selectOption(index)" >
|
|
<component :is="slot" :key="index" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
</section>
|
|
|
|
</template>
|
|
|
|
<style scoped>
|
|
.firstbox {
|
|
background-color: var(--tertiary);
|
|
color: var(--text);
|
|
border: none;
|
|
border-radius: 5px;
|
|
padding: 5px;
|
|
font-size: 14px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.container {
|
|
|
|
color: var(--text);
|
|
border: none;
|
|
border-radius: 0px 0px 5px 5px;
|
|
font-size: 14px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
position: fixed;
|
|
justify-content: space-between;
|
|
user-select: none;
|
|
overflow-y: auto;
|
|
max-height: 16vh;
|
|
z-index: 4000;
|
|
}
|
|
|
|
.container .option:last-child {
|
|
border-radius: 0px 0px 5px 5px !important;
|
|
}
|
|
|
|
.container .option:only-child {
|
|
border-radius: 0px 0px 5px 5px !important;
|
|
}
|
|
|
|
.container .option:first-child {
|
|
border-top: 1px solid var(--secondary) !important;
|
|
}
|
|
|
|
.option {
|
|
width: 100%;
|
|
padding: 5px;
|
|
background-color: var(--tertiary);
|
|
cursor: pointer;
|
|
white-space: break-spaces;
|
|
}
|
|
|
|
.option:hover {
|
|
background-color: var(--primary-hover);
|
|
color: var(--text-inverse);
|
|
}
|
|
|
|
.showed {
|
|
/* margin-bottom: 0 !important;
|
|
padding-bottom: 0; */
|
|
border-radius: 5px 5px 0 0 !important;
|
|
}
|
|
|
|
.icon {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
</style> |