Fixed PageLeaveGuard.vue Dark Mode
This commit is contained in:
@@ -2,22 +2,19 @@
|
|||||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||||
import { onBeforeRouteLeave } from 'vue-router'
|
import { onBeforeRouteLeave } from 'vue-router'
|
||||||
|
|
||||||
// Wir erwarten eine Prop, die sagt, ob geschützt werden soll
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
when: boolean // z.B. true, wenn Formular dirty ist
|
when: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const showModal = ref(false)
|
const showModal = ref(false)
|
||||||
const pendingNext = ref<null | ((val?: boolean) => void)>(null)
|
const pendingNext = ref<null | ((val?: boolean) => void)>(null)
|
||||||
|
|
||||||
// --- 1. Interne Navigation (Nuxt) ---
|
// --- 1. Interne Navigation ---
|
||||||
onBeforeRouteLeave((to, from, next) => {
|
onBeforeRouteLeave((to, from, next) => {
|
||||||
if (!props.when) {
|
if (!props.when) {
|
||||||
next()
|
next()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation pausieren & Modal zeigen
|
|
||||||
pendingNext.value = next
|
pendingNext.value = next
|
||||||
showModal.value = true
|
showModal.value = true
|
||||||
})
|
})
|
||||||
@@ -29,10 +26,10 @@ const confirmLeave = () => {
|
|||||||
|
|
||||||
const cancelLeave = () => {
|
const cancelLeave = () => {
|
||||||
showModal.value = false
|
showModal.value = false
|
||||||
// Navigation wird implizit abgebrochen
|
// Navigation abbrechen (pendingNext verfällt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 2. Externe Navigation (Browser Tab schließen) ---
|
// --- 2. Browser Tab schließen ---
|
||||||
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
|
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
|
||||||
if (props.when) {
|
if (props.when) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
@@ -50,12 +47,12 @@ onBeforeUnmount(() => window.removeEventListener('beforeunload', handleBeforeUnl
|
|||||||
<div class="guard-modal">
|
<div class="guard-modal">
|
||||||
|
|
||||||
<div class="guard-header">
|
<div class="guard-header">
|
||||||
<slot name="title">Seite wirklich verlassen?</slot>
|
<slot name="title">Seite verlassen?</slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="guard-body">
|
<div class="guard-body">
|
||||||
<slot>
|
<slot>
|
||||||
Du hast ungespeicherte Änderungen. Diese gehen verloren, wenn du die Seite verlässt.
|
Du hast ungespeicherte Änderungen. Wenn du die Seite verlässt, gehen diese verloren.
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -64,7 +61,7 @@ onBeforeUnmount(() => window.removeEventListener('beforeunload', handleBeforeUnl
|
|||||||
Nein, bleiben
|
Nein, bleiben
|
||||||
</button>
|
</button>
|
||||||
<button @click="confirmLeave" class="btn-confirm">
|
<button @click="confirmLeave" class="btn-confirm">
|
||||||
Ja, verlassen
|
Ja, verwerfen
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -74,25 +71,98 @@ onBeforeUnmount(() => window.removeEventListener('beforeunload', handleBeforeUnl
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* Basis-Styling - passe dies an dein Design System an */
|
/* --- Layout & Animation --- */
|
||||||
.guard-overlay {
|
.guard-overlay {
|
||||||
position: fixed; inset: 0; background: rgba(0,0,0,0.6);
|
position: fixed; inset: 0;
|
||||||
|
background: rgba(0,0,0,0.5);
|
||||||
display: flex; align-items: center; justify-content: center;
|
display: flex; align-items: center; justify-content: center;
|
||||||
z-index: 9999; backdrop-filter: blur(2px);
|
z-index: 9999; backdrop-filter: blur(4px);
|
||||||
|
transition: opacity 0.2s;
|
||||||
}
|
}
|
||||||
.guard-modal {
|
|
||||||
background: white; padding: 24px; border-radius: 12px;
|
|
||||||
width: 90%; max-width: 400px; box-shadow: 0 10px 25px rgba(0,0,0,0.2);
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
.guard-header { font-size: 1.25rem; font-weight: bold; margin-bottom: 1rem; }
|
|
||||||
.guard-body { margin-bottom: 1.5rem; color: #4a5568; }
|
|
||||||
.guard-actions { display: flex; justify-content: flex-end; gap: 12px; }
|
|
||||||
|
|
||||||
/* Buttons */
|
.guard-modal {
|
||||||
button { padding: 8px 16px; border-radius: 6px; cursor: pointer; border: none; font-weight: 600;}
|
width: 90%; max-width: 420px;
|
||||||
.btn-cancel { background: #edf2f7; color: #2d3748; }
|
border-radius: 12px;
|
||||||
.btn-cancel:hover { background: #e2e8f0; }
|
padding: 24px;
|
||||||
.btn-confirm { background: #e53e3e; color: white; }
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
.btn-confirm:hover { background: #c53030; }
|
font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guard-header {
|
||||||
|
font-size: 1.125rem; font-weight: 600; margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guard-body {
|
||||||
|
margin-bottom: 1.5rem; font-size: 0.95rem; line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.guard-actions {
|
||||||
|
display: flex; justify-content: flex-end; gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Buttons Basis --- */
|
||||||
|
button {
|
||||||
|
padding: 8px 16px; border-radius: 6px; cursor: pointer; border: none; font-weight: 500; font-size: 0.9rem;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------- */
|
||||||
|
/* LIGHT MODE (Default) */
|
||||||
|
/* --------------------------------------------------------- */
|
||||||
|
.guard-modal {
|
||||||
|
background: white;
|
||||||
|
color: #1f2937; /* gray-800 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.guard-body {
|
||||||
|
color: #4b5563; /* gray-600 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-cancel {
|
||||||
|
background: #f3f4f6; /* gray-100 */
|
||||||
|
color: #374151; /* gray-700 */
|
||||||
|
}
|
||||||
|
.btn-cancel:hover { background: #e5e7eb; }
|
||||||
|
|
||||||
|
.btn-confirm {
|
||||||
|
background: #ef4444; /* red-500 */
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.btn-confirm:hover { background: #dc2626; }
|
||||||
|
|
||||||
|
|
||||||
|
/* --------------------------------------------------------- */
|
||||||
|
/* DARK MODE OVERRIDES */
|
||||||
|
/* Wir nutzen :global(.dark), um auf die Klasse im <html>/<body> zuzugreifen */
|
||||||
|
/* --------------------------------------------------------- */
|
||||||
|
:global(.dark) .guard-overlay {
|
||||||
|
background: rgba(0,0,0,0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.dark) .guard-modal {
|
||||||
|
background: #1f2937; /* gray-800 */
|
||||||
|
color: #f3f4f6; /* gray-100 */
|
||||||
|
border: 1px solid #374151; /* gray-700 Border für Kontrast */
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.dark) .guard-body {
|
||||||
|
color: #9ca3af; /* gray-400 */
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.dark) .btn-cancel {
|
||||||
|
background: #374151; /* gray-700 */
|
||||||
|
color: #e5e7eb; /* gray-200 */
|
||||||
|
}
|
||||||
|
:global(.dark) .btn-cancel:hover {
|
||||||
|
background: #4b5563; /* gray-600 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Confirm Button bleibt rot, aber evtl. leicht angepasst */
|
||||||
|
:global(.dark) .btn-confirm {
|
||||||
|
background: #b91c1c; /* red-700 (etwas dunkler für Darkmode angenehmer) */
|
||||||
|
color: #fecaca; /* red-100 Text */
|
||||||
|
}
|
||||||
|
:global(.dark) .btn-confirm:hover {
|
||||||
|
background: #991b1b;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user