Fix: Usar DropdownMenu de radix-vue para menú de configuración
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 26s
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 26s
- Reemplaza dropdown manual por componente DropdownMenu con radix-vue - Agrega avoid-collisions y collision-padding para posicionamiento automático - El menú ahora se ajusta automáticamente cuando no hay espacio horizontal - Actualiza DropdownMenuItem para soportar prop 'as' y 'href' para enlaces
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, reactive, ref, computed, watch } from 'vue';
|
import { onMounted, reactive, ref, computed, watch } from 'vue';
|
||||||
import { Button, Badge, Input, Card, CardHeader, CardTitle, CardActions, CardContent, Textarea } from '@/components/ui';
|
import { Button, Badge, Input, Card, CardHeader, CardTitle, CardActions, CardContent, Textarea, DropdownMenu, DropdownMenuItem, DropdownMenuSeparator } from '@/components/ui';
|
||||||
import EventCard from '@/components/EventCard.vue';
|
import EventCard from '@/components/EventCard.vue';
|
||||||
import UserCard from '@/components/UserCard.vue';
|
import UserCard from '@/components/UserCard.vue';
|
||||||
import DispositivoCard from '@/components/DispositivoCard.vue';
|
import DispositivoCard from '@/components/DispositivoCard.vue';
|
||||||
@@ -45,7 +45,6 @@ const mainCollapsed = ref(false);
|
|||||||
const layoutMode = ref('user');
|
const layoutMode = ref('user');
|
||||||
const theme = ref(localStorage.getItem('theme') || 'dark');
|
const theme = ref(localStorage.getItem('theme') || 'dark');
|
||||||
const statusText = ref('OK');
|
const statusText = ref('OK');
|
||||||
const showSettingsMenu = ref(false);
|
|
||||||
const showInfoModal = ref(false);
|
const showInfoModal = ref(false);
|
||||||
|
|
||||||
async function fetchUsers() {
|
async function fetchUsers() {
|
||||||
@@ -305,13 +304,12 @@ function openAddGuest() {
|
|||||||
userFormModel.value = { username:'', password:'', vlan:'5', disabled:false, etiquetas: ['invitado'] };
|
userFormModel.value = { username:'', password:'', vlan:'5', disabled:false, etiquetas: ['invitado'] };
|
||||||
showUserForm.value = true;
|
showUserForm.value = true;
|
||||||
}
|
}
|
||||||
function toggleSettingsMenu() { showSettingsMenu.value = !showSettingsMenu.value; }
|
|
||||||
const showRawDb = ref(false);
|
const showRawDb = ref(false);
|
||||||
const rawDbFullscreen = ref(false);
|
const rawDbFullscreen = ref(false);
|
||||||
function openRawDb() { showSettingsMenu.value = false; showRawDb.value = true; }
|
function openRawDb() { showRawDb.value = true; }
|
||||||
function closeRawDb() { showRawDb.value = false; }
|
function closeRawDb() { showRawDb.value = false; }
|
||||||
const showVlan = ref(false);
|
const showVlan = ref(false);
|
||||||
function openVlanForm() { showSettingsMenu.value = false; showVlan.value = true; }
|
function openVlanForm() { showVlan.value = true; }
|
||||||
function closeVlan() { showVlan.value = false; }
|
function closeVlan() { showVlan.value = false; }
|
||||||
function onVlanCreated() { showVlan.value = false; }
|
function onVlanCreated() { showVlan.value = false; }
|
||||||
const showDevice = ref(false);
|
const showDevice = ref(false);
|
||||||
@@ -426,22 +424,23 @@ async function handleUserFormSubmit(data) {
|
|||||||
<img class="size-4 opacity-90" src="/icons/guest.svg" alt="invitado"> Invitado
|
<img class="size-4 opacity-90" src="/icons/guest.svg" alt="invitado"> Invitado
|
||||||
</Button>
|
</Button>
|
||||||
<UserDropdown />
|
<UserDropdown />
|
||||||
<div class="relative">
|
<DropdownMenu align="end">
|
||||||
<Button @click="toggleSettingsMenu">
|
<template #trigger>
|
||||||
<img class="size-4 opacity-90" src="/icons/settings.svg" alt="config"> Configuración
|
<Button>
|
||||||
</Button>
|
<img class="size-4 opacity-90" src="/icons/settings.svg" alt="config"> Configuración
|
||||||
<div v-if="showSettingsMenu" class="absolute right-0 top-full mt-1.5 glass-card p-1.5 min-w-[200px] shadow-lg border border-pink-200 dark:border-pink-600/50 z-50 space-y-1">
|
</Button>
|
||||||
<Button variant="ghost" class="w-full justify-start" @click="openRawDb">ver rawDB</Button>
|
</template>
|
||||||
<Button variant="ghost" class="w-full justify-start" @click="openVlanForm">crear VLAN</Button>
|
<DropdownMenuItem @click="openRawDb">ver rawDB</DropdownMenuItem>
|
||||||
<hr class="border-border my-1" />
|
<DropdownMenuItem @click="openVlanForm">crear VLAN</DropdownMenuItem>
|
||||||
<Button as="a" variant="ghost" class="w-full justify-start" href="/api/users.csv">Exportar usuarios CSV</Button>
|
<DropdownMenuSeparator />
|
||||||
<Button as="a" variant="ghost" class="w-full justify-start" href="/api/devices.csv">Exportar dispositivos CSV</Button>
|
<DropdownMenuItem as="a" href="/api/users.csv">Exportar usuarios CSV</DropdownMenuItem>
|
||||||
<Button as="a" variant="ghost" class="w-full justify-start" href="/api/vlans.csv">Exportar VLANs CSV</Button>
|
<DropdownMenuItem as="a" href="/api/devices.csv">Exportar dispositivos CSV</DropdownMenuItem>
|
||||||
<Button variant="ghost" class="w-full justify-start" @click="openImport('users')">Importar usuarios CSV</Button>
|
<DropdownMenuItem as="a" href="/api/vlans.csv">Exportar VLANs CSV</DropdownMenuItem>
|
||||||
<Button variant="ghost" class="w-full justify-start" @click="openImport('devices')">Importar dispositivos CSV</Button>
|
<DropdownMenuSeparator />
|
||||||
<Button variant="ghost" class="w-full justify-start" @click="openImport('vlans')">Importar VLANs CSV</Button>
|
<DropdownMenuItem @click="openImport('users')">Importar usuarios CSV</DropdownMenuItem>
|
||||||
</div>
|
<DropdownMenuItem @click="openImport('devices')">Importar dispositivos CSV</DropdownMenuItem>
|
||||||
</div>
|
<DropdownMenuItem @click="openImport('vlans')">Importar VLANs CSV</DropdownMenuItem>
|
||||||
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
DropdownMenuContent
|
DropdownMenuContent
|
||||||
} from 'radix-vue';
|
} from 'radix-vue';
|
||||||
|
|
||||||
const props = defineProps({
|
defineProps({
|
||||||
align: {
|
align: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'end'
|
default: 'end'
|
||||||
@@ -14,6 +14,14 @@ const props = defineProps({
|
|||||||
side: {
|
side: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'bottom'
|
default: 'bottom'
|
||||||
|
},
|
||||||
|
sideOffset: {
|
||||||
|
type: Number,
|
||||||
|
default: 6
|
||||||
|
},
|
||||||
|
collisionPadding: {
|
||||||
|
type: [Number, Object],
|
||||||
|
default: 8
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -28,7 +36,9 @@ const props = defineProps({
|
|||||||
<DropdownMenuContent
|
<DropdownMenuContent
|
||||||
:align="align"
|
:align="align"
|
||||||
:side="side"
|
:side="side"
|
||||||
:side-offset="6"
|
:side-offset="sideOffset"
|
||||||
|
:collision-padding="collisionPadding"
|
||||||
|
:avoid-collisions="true"
|
||||||
class="z-50 min-w-[180px] glass-card p-1.5 shadow-lg border border-pink-200 dark:border-pink-600/50 animate-slide-in"
|
class="z-50 min-w-[180px] glass-card p-1.5 shadow-lg border border-pink-200 dark:border-pink-600/50 animate-slide-in"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
|
|||||||
@@ -6,7 +6,12 @@ import { cn } from '@/lib/utils';
|
|||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
disabled: Boolean,
|
disabled: Boolean,
|
||||||
danger: Boolean,
|
danger: Boolean,
|
||||||
class: String
|
class: String,
|
||||||
|
as: {
|
||||||
|
type: [String, Object],
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
|
href: String
|
||||||
});
|
});
|
||||||
|
|
||||||
const classes = computed(() => cn(
|
const classes = computed(() => cn(
|
||||||
@@ -17,10 +22,12 @@ const classes = computed(() => cn(
|
|||||||
props.danger && 'hover:border-red-400/30 hover:text-red-400',
|
props.danger && 'hover:border-red-400/30 hover:text-red-400',
|
||||||
props.class
|
props.class
|
||||||
));
|
));
|
||||||
|
|
||||||
|
const elementAs = computed(() => props.as || (props.href ? 'a' : undefined));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<DropdownMenuItem :disabled="disabled" :class="classes">
|
<DropdownMenuItem :as="elementAs" :href="href" :disabled="disabled" :class="classes">
|
||||||
<slot />
|
<slot />
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user