[Onsen UI + Vue] ボタンコンポーネント

Onsen UIのボタンコンポーネントv-ons-buttonをラップしたカスタムボタンコンポーネントを作ってみました。

See the Pen Onsen UI Vue by senmyou (@senmyou) on CodePen.

ボタンのスタイリングはOnsen UIにおまかせしています。Onsen UIはモバイル端末に合わせて自動的にAndroid/iOSのスタイルを適用します。上記CodePenのサンプルではパソコンでもAndroid用のスタイルに見えるようにスタイルを固定しています。そのため、マテリアルデザインとリップル(さざ波)エフェクト付きのボタンになっています。

汎用的なボタンコンポーネントを目指して作り始めたのですが、最終的にアイコンとテキストを組み合わせたトグルボタン用コンポーネントになってしまったという気もします。以下にソースを記載します。

  • CodePenではHTMLファイル/JSファイル/CSSファイルに記述していますが、以下のソースはVueファイル(単一ファイルコンポーネント)になります。
  • アイコンはFont Awesome専用になっています。ちなみにFont Awesomeの読み込みはOnsen UIのCSSファイルで行われます。
CustomButton.vue
<template>
  <v-ons-button
    :class="buttonClass"
    :disabled="disabled"
    @click="clickHandler"
  >
    <i
      v-if="icon"
      class="fa"
      :class="iconClass"
    >
    </i>
    <slot>{{ buttonText }}</slot>
  </v-ons-button>
</template>

<script>
export default {
  data () {
    return {
      index: 0,
    }
  },

  props: {
    text: {
      type: [String, Array],
      default: ''
    },
    icon: {
      type: [String, Array],
      default: ''
    },
    disabled: {
      type: Boolean,
      default: false
    },
    buttonClass: {
      type: String,
      default: ''
    }
  },

  computed: {
    toggle () {
      console.log('toggle')
      var num = 0
      var icon = (this.icon instanceof Array) ? this.icon.length : 0
      var text = (this.text instanceof Array) ? this.text.length : 0
      if (icon) {
        if (text) {
          num = icon < text ? icon : text
        } else {
          num = icon
        }
      } else if (text) {
        num = text
      }
      console.log(icon, text, num)
      return { icon, text, num }
    },
    iconClass () {    // memo: iconが空の配列の場合、暗黙的にjoin()により""になる
      console.log('iconClass')
      return this.toggle.icon ? 'fa-' + this.icon[this.index] : 'fa-' + this.icon
    },
    buttonText () {
      console.log('buttonText')
      return this.toggle.text ? this.text[this.index] : this.text
    }
  },

  methods: {
    clickHandler (event) {
      var index = this.index;
      if (this.toggle.num) {
        this.index = (index >= this.toggle.num - 1) ? 0 : index + 1
      }
      var arg = {
        event,
        oldIdx: index,
        newIdx: this.index
      }
      this.$emit('click', arg)
    }
  }
}
</script>

<style scoped>
/*
・親コンポーネントのクラスで上書きされる
・scopedを付けないとOnsen UIのbuttonクラスで上書きされる
  (onsen-css-components.css .button)
*/
.greenButton,
.greenButton:active {
  background-color: green;
}
.redButton,
.redButton:active {
  background-color: red;
}
</style>

このコンポーネントの使い方について説明します。

ボタンのテキストはコンポーネントの開始タグと終了タグの間に記述する方法と、textプロパティに記述する方法の2通りに対応しています。アイコンはiconプロパティに記述します。テキストとアイコンは一緒に使用することができます。それぞれの位置関係は固定です。コンポーネントにプロパティを追加して上下に配置できるようにするのもいいかもしれません。

<custom-button>slot</custom-button>
<custom-button text="props"></custom-button>

<custom-button icon="cog"></custom-button>

<custom-button icon="cog">slot</custom-button>
<custom-button icon="cog" text="text"></custom-button>

disabled属性でボタンの有効/無効を設定できます。

<custom-button disabled>hello</custom-button>
<custom-button :disabled="false">hello</custom-button>
<custom-button :disabled="true">hello</custom-button>

ボタンのクリックにより “click” というイベント名でイベントを発火します。また、button-classプロパティでボタンへ適用するクラスを渡すことができます。

<custom-button
  icon="smile-o"
  @click="clickHandler"
  button-class="bigButton"
>
</custom-button>

textプロパティおよびiconプロパティに配列を渡すことでトグルボタンやグルグルボタンになります。

<custom-button
  :text="['グー', 'チョキ', 'パー']"
  @click="clickHandler"
  button-class="sample01"
>
</custom-button>

<custom-button
  :icon="['hand-rock-o', 'hand-peace-o', 'hand-paper-o']"
  @click="clickHandler"
  button-class="sample02"
>
</custom-button>

<custom-button
  :text="['グー', 'チョキ', 'パー']"
  icon="smile-o"
  @click="clickHandler"
  button-class="sample03 align-left"
>
</custom-button>

<custom-button
  text="じゃんけん"
  :icon="['hand-rock-o', 'hand-peace-o', 'hand-paper-o']"
  @click="clickHandler"
  button-class="sample04 align-right"
>
</custom-button>

<custom-button
  :text="['グー', 'チョキ', 'パー']"
  :icon="['hand-rock-o', 'hand-peace-o', 'hand-paper-o']"
  @click="clickHandler"
  button-class="sample03 align-left"
>
</custom-button>