Vuetify flexbox chat layout

Viewed 4782

I am creating a chat layout in Vuetify (Vue.js Material Design Framework). As a developer I prefer to keep my custom css as less a possible and utilize as much as the framework has to offer. But not having solid understanding of Flexbox I am have difficulty finalizing the chat layout which I want.
Things I am struggling with are:

  1. the title of v-card (right side) should stick to the top of box.
  2. the type_a_message field should stick to bottom of the box.
  3. the middle (message bubbles) area is scrollable in the remaining space.
  4. the sidebar (users list) to not shrink whereas the right side should be expand/shrink.

My Template looks like this (apologies for verbosity)

    <v-container
      class="fill-height pa-0 elevation-4"
    >
      <v-row class="no-gutters">
        <v-col
          cols="3"
          class="flex-grow-1 flex-shrink-0"
          style="border-right: 1px solid #0000001f;"
        >
          <v-responsive
            class="overflow-y-auto fill-height"
            height="500"
          >
            <v-list subheader>
              <v-list-item-group v-model="activeChat">
                <template v-for="(item, index) in parents">
                  <v-list-item
                    :key="`parent${index}`"
                    :value="item.id"
                  >
                    <v-list-item-avatar color="grey lighten-1 white--text">
                      <v-icon>
                        chat_bubble
                      </v-icon>
                    </v-list-item-avatar>
                    <v-list-item-content>
                      <v-list-item-title v-text="item.title" />
                      <v-list-item-subtitle v-text="'hi'" />
                    </v-list-item-content>
                    <v-list-item-icon>
                      <v-icon :color="item.active ? 'deep-purple accent-4' : 'grey'">
                        chat_bubble
                      </v-icon>
                    </v-list-item-icon>
                  </v-list-item>
                  <v-divider
                    :key="`chatDivider${index}`"
                    class="my-0"
                  />
                </template>
              </v-list-item-group>
            </v-list>
          </v-responsive>
        </v-col>
        <v-col
          cols="3"
          style="max-width: 100%;"
          class="flex-grow-1 flex-shrink-0"
        >
          <v-responsive
            v-if="activeChat"
            class="overflow-y-auto fill-height"
            height="500"
          >
            <v-card
              flat
              class="fill-height"
            >
              <v-card-title>
                john doe
              </v-card-title>
              <v-card-subtitle>
                hi
              </v-card-subtitle>
              <v-divider class="my-0" />
              <v-card-text class="flex-grow-1 fill-height">
                <template v-for="(msg, i) in messages">
                  <div
                    :class="{ 'd-flex flex-row-reverse': msg.me }"
                  >
                    <v-menu offset-y>
                      <template v-slot:activator="{ on }">
                        <v-hover
                          v-slot:default="{ hover }"
                        >
                          <v-chip
                            :color="msg.me ? 'primary' : ''"
                            dark
                            style="height:auto;white-space: normal;"
                            class="pa-4 mb-2"
                            v-on="on"
                          >
                            {{ msg.content }}
                            <sub
                              class="ml-2"
                              style="font-size: 0.5rem;"
                            >{{ msg.created_at }}</sub>
                            <v-icon
                              v-if="hover"
                              small
                            >
                              expand_more
                            </v-icon>
                          </v-chip>
                        </v-hover>
                      </template>
                      <v-list>
                        <v-list-item>
                          <v-list-item-title>delete</v-list-item-title>
                        </v-list-item>
                      </v-list>
                    </v-menu>
                  </div>
                </template>
                <v-text-field
                  v-model="messageForm.content"
                  label="type_a_message"
                  type="text"
                  no-details
                  outlined
                  append-outer-icon="send"
                  @keyup.enter="messages.push(messageForm)"
                  @click:append-outer="messages.push(messageForm)"
                />
              </v-card-text>
            </v-card>
          </v-responsive>
        </v-col>
      </v-row>
    </v-container>
1 Answers

Use flex-direction:column on the v-card, and then flex-grow-1 overflow-y-auto on the messages section...

             <v-card
                  flat
                  class="d-flex flex-column fill-height"
                >
                 <v-card-title>
                    john doe
                 </v-card-title>
                 <v-card-text class="flex-grow-1 overflow-y-auto">
                    <template v-for="(msg, i) in messages">
                      <div
                        :class="{ 'd-flex flex-row-reverse': msg.me }"
                      >
                        ...
                      </div>
                    </template>
                 </v-card-text>
                 <v-card-text class="flex-shrink-1">
                     <v-text-field/>
                 </v-card-text>
             </v-card>

Demo: https://codeply.com/p/2n5OiAvWd9

Related