What I am trying to design is a new Login / Register modal for using with the standard Vue Login & Register scaffolding that comes with Laravel Breeze.
Exactly what I am trying to achieve is this:
Login / Register Button -> Clicked and a modal opens The modal contains a header section where there is a "Login" tab and a "Register" tab, by default the login tab is selected. It then contains a body section that is a panel that switches between the "login.vue" file and the "register.vue" file.
It is from my understanding that I need to do the following:
Create a button that when clicked open the modal component.
Create a Vue component to house the modal code & tabs that conditionally render the login or register page.
My file structure is as follows
resources | js | Pages | Auth \-\> Login.vue (standard breeze code)
resources | js | Pages | Auth \-\> Register.vue (standard breeze code)
resources | js | Pages | Modal \-\> LoginRegister.vue
Thanks in advance, I would really appreciate guidance.
The solution for the tabbed component (LoginRegister.vue) that switches between the login & register page was to use headless UI and create the tabs & dynamically rendered login or register component, with just importing the login.vue & register.vue, this is how I did it:
<template>
<div class="mx-auto max-w-xl px-2 sm:px-0">
<TabGroup>
<TabList class="flex rounded-xl bg-blue-900/20 p-1 space-x-1">
<Tab
as="template"
v-slot="{ selected }"
>
<button
:class="[
'w-full rounded-lg py-2.5 p-4 font-medium leading-5 text-slate-800 text-blue-700',
'ring-white ring-opacity-60 ring-offset-2 ring-offset-blue-400 focus:outline-none focus:ring-2',
selected
? 'bg-white shadow'
: 'text-blue-100 hover:bg-white/[0.12] hover:text-white',
]"
>
Login
</button>
</Tab>
<Tab
as="template"
v-slot="{ selected }"
>
<button
:class="[
'w-full rounded-lg py-2.5 p-4 font-medium leading-5 text-blue-700',
'ring-white ring-opacity-60 ring-offset-2 ring-offset-blue-400 focus:outline-none focus:ring-2',
selected
? 'bg-white shadow'
: 'text-blue-100 hover:bg-white/[0.12] hover:text-white',
]"
>
Register
</button>
</Tab>
</TabList>
<TabPanels class="mt-2">
<TabPanel class="rounded-xl bg-white p-3 ring-white ring-opacity-60 ring-offset-2 ring-offset-blue-400 focus:outline-none focus:ring-2">
<Login/>
</TabPanel>
<TabPanel class="rounded-xl bg-white p-3 ring-white ring-opacity-60 ring-offset-2 ring-offset-blue-400 focus:outline-none focus:ring-2">
<Register/>
</TabPanel>
</TabPanels>
</TabGroup>
</div>
</template>
<script setup>
import Login from "@/Pages/Auth/Login.vue";
import Register from "@/Pages/Auth/Register.vue";
import {Tab, TabGroup, TabList, TabPanel, TabPanels} from '@headlessui/vue'
</script>
I Then was able to use a modal component I found, which is the following code:
<script setup>
import { computed, onMounted, onUnmounted, watch } from 'vue';
import LoginRegister from "@/Pages/Auth/LoginRegister.vue";
const props = defineProps({
show: {
type: Boolean,
default: false,
},
maxWidth: {
type: String,
default: '2xl',
},
closeable: {
type: Boolean,
default: true,
},
});
const emit = defineEmits(['close']);
watch(() => props.show, () => {
if (props.show) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = null;
}
});
const close = () => {
if (props.closeable) {
emit('close');
}
};
const closeOnEscape = (e) => {
if (e.key === 'Escape' && props.show) {
close();
}
};
onMounted(() => document.addEventListener('keydown', closeOnEscape));
onUnmounted(() => {
document.removeEventListener('keydown', closeOnEscape);
document.body.style.overflow = null;
});
const maxWidthClass = computed(() => {
return {
'sm': 'sm:max-w-sm',
'md': 'sm:max-w-md',
'lg': 'sm:max-w-lg',
'xl': 'sm:max-w-xl',
'2xl': 'sm:max-w-2xl',
}[props.maxWidth];
});
</script>
<template>
<teleport to="body">
<transition leave-active-class="duration-200">
<div v-show="show" class="fixed inset-0 overflow-y-auto px-4 py-6 sm:px-0 z-50" scroll-region>
<transition
enter-active-class="ease-out duration-300"
enter-from-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="ease-in duration-200"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<div v-show="show" class="fixed inset-0 transform transition-all" @click="close">
<div class="absolute inset-0 bg-gray-500 opacity-75" />
</div>
</transition>
<transition
enter-active-class="ease-out duration-300"
enter-from-class="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enter-to-class="opacity-100 translate-y-0 sm:scale-100"
leave-active-class="ease-in duration-200"
leave-from-class="opacity-100 translate-y-0 sm:scale-100"
leave-to-class="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<div v-show="show" class="mb-6 bg-white rounded-lg overflow-hidden shadow-xl transform transition-all sm:w-full sm:mx-auto" :class="maxWidthClass">
<div v-if="show">
<LoginRegister />
</div>
</div>
</transition>
</div>
</transition>
</teleport>
</template>