My goal is to create a chat app using Vuejs 3 and Vuetify 3, but I'm encountering an issue aligning the chatbox with the v-card
component. Instead of being positioned at the bottom of the v-card
, the chatbox (green) appears at the bottom of the page.
You can see a visualization of this problem in the following screenshot: https://i.stack.imgur.com/ZAlXE.png
Please refer to the GitHub link for reproduction instead: https://github.com/samzhangjy/observerx-web
I am looking for a solution to have the chatbox aligned inside the v-card
and pinned at the bottom even when scrolling, essentially keeping it within the v-card. Additionally, I would like to know if there is a built-in way to make the v-toolbar
(the orange header) sticky at the top.
Thank you so much in advance!
If you prefer not to use CodeSandbox, here is the code related to this:
MainContainer.vue
:
<script lang="ts" setup>
import { useTheme } from "vuetify";
const theme = useTheme();
const changeTheme = () =>
(theme.global.name.value = theme.global.current.value.dark
? "light"
: "dark");
</script>
<template>
<v-container class="h-screen py-md-12 py-sm-5">
<v-row justify="space-around" class="h-100">
<v-card max-width="1000" height="100%" width="100%" class="overflow-auto">
<v-toolbar color="tertiary-container" class="position-sticky">
<template v-slot:prepend>
<v-btn icon="$menu" color="on-tertiary-container"></v-btn>
</template>
<v-toolbar-title class="text-h6"> ObserverX </v-toolbar-title>
<template v-slot:append>
<v-btn
icon="mdi-theme-light-dark"
color="on-tertiary-container"
@click="changeTheme"
></v-btn>
</template>
</v-toolbar>
<v-card-text class="overflow-auto">
<slot />
</v-card-text>
</v-card>
</v-row>
</v-container>
</template>
<style scoped></style>
ChatPage.vue
:
<template>
<v-container class="h-100">
<v-responsive class="align-center text-center h-100">
<MainContainer class="container h-100">
<div class="messages">
<p class="text-body-1" v-for="i in 100">Line #{{ i }}</p>
</div>
<div class="message-input-container">
<v-textarea
variant="solo"
base-color="surface"
bg-color="surface-variant"
color="on-surface-variant"
class="rounded-t-0 message-input"
rows="3"
no-resize
rounded="0"
v-model="currentMessage"
>
<template v-slot:append-inner>
<v-btn
icon="mdi-send"
variant="tonal"
size="small"
color="primary"
@click="sendMessage"
></v-btn>
</template>
</v-textarea>
</div>
</MainContainer>
</v-responsive>
</v-container>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import MainContainer from "./MainContainer.vue";
const currentMessage = ref("");
const sendMessage = () => {
console.log(currentMessage.value);
};
</script>
<style scoped>
.container {
box-sizing: border-box;
position: relative;
overflow: hidden;
padding-bottom: 100px;
max-height: 1000px;
height: 100%;
}
.messages {
width: 100%;
height: 100%;
overflow: auto;
}
.message-input {
max-width: 1000px;
width: 100%;
height: 100px;
position: fixed;
bottom: 0;
left: 0;
resize: none;
}
.message-input-container {
position: absolute;
max-width: 1000px;
width: 100%;
}
</style>