😎

Vue.js|Twitterのフォロー一覧のようなUIを作る

2021/12/30に公開

Twitterのフォロー一覧のようなUIが必要でしたので備忘録です。Vuetify公式はこちらの情報(単一行リストを参考)です。
https://vuetifyjs.com/ja/components/lists/#section-30b730f330d730eb306a30a230d030bf30fc30ea30b930c8

ただ、上記だけだとアイコンがクリックできなかったり、クリックした際にボタン状態の切り替え(色変えたり)が考慮されていないので、シンプルにそのあたりをカスタマイズしてみました。

ひとまず、全体コードこちらです。

List.js
<template>
  <v-app>
    <v-main>
      <v-container>
        <v-list subheader>
	  <!-- これは別になくてもいいです -->
          <v-subheader>Recent chat</v-subheader>
          
	  <v-list-item
            v-for="chat in recent"
            :key="chat.title"
          >
            <v-list-item-avatar>
              <v-img
                :alt="`${chat.title} avatar`"
                :src="chat.avatar"
              ></v-img>
            </v-list-item-avatar>

            <v-list-item-content class="text-left">
              <v-list-item-title v-text="chat.title"></v-list-item-title>
            </v-list-item-content>

            <v-list-item-action>
              <v-btn
              v-if="!chat.active"
              @click="chat.active = true"
              text
              icon>
                <v-icon
                color="grey lighten-1">
                mdi-heart-outline
                </v-icon>
              </v-btn>
              <v-btn
              v-else
              @click="chat.active = false"
              text
              icon>
                <v-icon
                color="pink">
                mdi-heart
                </v-icon>
              </v-btn>
            </v-list-item-action>
          </v-list-item>
        </v-list>
      </v-container>
    </v-main>
  </v-app>
</template>

<script>

export default {
  name: 'List',
  data: () => ({
      recent: [
        {
          active: true,
          avatar: 'https://cdn.vuetifyjs.com/images/lists/1.jpg',
          title: 'Jason Oner',
        },
        {
          active: true,
          avatar: 'https://cdn.vuetifyjs.com/images/lists/2.jpg',
          title: 'Mike Carlson',
        },
        {
          active: false,
          avatar: 'https://cdn.vuetifyjs.com/images/lists/3.jpg',
          title: 'Cindy Baker',
        },
        {
          active: false,
          avatar: 'https://cdn.vuetifyjs.com/images/lists/4.jpg',
          title: 'Ali Connors',
        },
      ],
    }),
}
</script>

こんな感じになります。

詳細をみていきます。

これは1行ごとのデータを保持しています。activeでハートアイコンを制御します。

List.js
data: () => ({
      recent: [
        {
          active: true,
          avatar: 'https://cdn.vuetifyjs.com/images/lists/1.jpg',
          title: 'Jason Oner',
        },
        {
          active: true,
          avatar: 'https://cdn.vuetifyjs.com/images/lists/2.jpg',
          title: 'Mike Carlson',
        },
        {
          active: false,
          avatar: 'https://cdn.vuetifyjs.com/images/lists/3.jpg',
          title: 'Cindy Baker',
        },
        {
          active: false,
          avatar: 'https://cdn.vuetifyjs.com/images/lists/4.jpg',
          title: 'Ali Connors',
        },
      ],
    }),
}

リストが複数あるので、forを使って回します。
以降、chat.active などでdataの値にアクセスできます。

v-list-item-contentやv-list-item-avatar、v-list-item-actionなど、中身に応じて適切なタグや働きがあるようなので公式の一覧をみながら対応したものを利用します。細かいカスタマイズをしたい場合はそれらをみながら調整していく感じと思います。

List.js
<v-list-item
 v-for="chat in recent"
 :key="chat.title"
>

こちらは行の中身です。v-ifでchat.activeの値に応じてアイコンの表示を切り替えています。
mdi-heart-outlineを使うといい感じに白抜きにしてくれたり、かなり便利ですねVuetify

List.js
<v-list-item-action>
	<v-btn
	v-if="!chat.active"
	@click="chat.active = true"
	text
	icon>
		<v-icon
		color="grey lighten-1">
		mdi-heart-outline
		</v-icon>
	</v-btn>
	<v-btn
	v-else
	@click="chat.active = false"
	text
	icon>
		<v-icon
		color="pink">
		mdi-heart
		</v-icon>
	</v-btn>
</v-list-item-action>

あとは、chat.activeの値をクリックイベントでtrue, falseと変更すれば、ハートアイコンの切り替えができます。

属性について、textでボタンぽいデザインからアイコンのみのデザインに変更できます。(通常ボタンに利用するとテキストリンクぽくなります)
また、iconはクリック時のエフェクトが丸っぽくなります。

List.js
<v-btn
v-if="!chat.active"
@click="chat.active = true"
text
icon>
	<v-icon
	color="grey lighten-1">
	mdi-heart-outline
        </v-icon>
</v-btn>

ちなみにv-iconはここから探して、ケバブケースで記載すれば利用できます。
https://materialdesignicons.com/

Discussion