LoginSignup
22
30

More than 5 years have passed since last update.

《JavaScript》Vue.jsでコンポーネントをつくる 『ボタン編』

Last updated at Posted at 2017-11-29

Vue.jsでボタンのコンポーネントを作りました。

ゆるりと、fav-frame という vue.js のコンポーネントをまとめたフレームワークを作っているので、新しいコンポーネントができたら掲載していきます🍟

コンポーネント本体、スタイルシート、呼び出し部分について書きます。

コンポーネントの作成

.vue ファイルを作成しコンポーネントを作っていきます。

button.vue

button.vue
<template>
  <button
          class="c-button"
          :class="[
              variation ? 'c-button--' + variation : '',
              size ? 'c-button--' + size : '',
              {
                               'c-button--outline': outline,
                'c-button--round': round,
                'c-button--full': full,
                'c-button--text': text,
                'is-disabled': disabled,
                'is-loading': loading,
              }]"

          :disabled="disabled"
          :type="buttonType"
          :autofocus="autofocus"

          @click="clickHandler"
  >
    <i
            :class="[
              'fa',
              'fa-' + icon,
            ]"
            v-if="icon"
    >
    </i>
    <slot></slot>
  </button>
</template>

<script>
  export default {

    props: {
      variation: {
        type: String,
        default: ''
      },
      size: String,
      icon: {
        type: String,
        default: ''
      },
      buttonType: {
        type: String,
        default: 'button'
      },
      outline: Boolean,
      round: Boolean,
      full: Boolean,
      text: Boolean,
      loading: Boolean,
      disabled: Boolean,
      autofocus: Boolean,
    },

    methods: {

      /**
       * 親にクリックイベントを渡す
       * @param e {Object} event object
       */
      clickHandler: function(e) {

        if (this.disabled) {

          return;

        }
        this.$emit('click', e);

      }

    }
  };

</script>


他のコンポーネントからの再利用(フォームのアドオンとか)を考えると、処理が増えるかもしれませんが、ボタン単体でやりたいことをまとめると、これでいい感じかなあと思っています。

Font Awesome Icons を利用することを前提としていますが、<i> のところを少し編集すれば、他のアイコンフォントでも利用できます。

コンポーネントのセット

webpack.config.js

import のパスを通しやすいように、と、.vue ファイルの拡張子の省略を解決するのに webpack.config.js で 定義します。

webpack.config.js

import path from 'path';

module.exports = {
    // .... 他の記述は割愛
  resolve: {
    // 拡張子の省略の許可
    extensions: [
      '.vue'
    ],
    // パスを定義しておく
    alias: {
      'Vue$': 'vue/dist/vue.common.js',
      'Component': path.resolve(__dirname, 'src/assets/js/component/'),
    },
  },
}

main.js

コンポーネントを main.js でセットします。

main.js

import Vue from 'Vue';
import favButton from 'Component/button';

new Vue({
  el: '#app',
  components: {
    favButton
  }
});

コンポーネントのスタイル

.scss ファイルでスタイリングします。

variable.scss

変数を定義します。他のコンポーネントも存在することを意識して、グローバルに利用したい変数も定義しておきます。

variable.scss

// global
// - - - - - - - - - - - - - - - - - - - -
$_color-white: #fafafa;
$_color-black: #333333;

$_color-primary: #F55247;
$_color-secondary: #50C7C1;

$_color-success: #6084D0;
$_color-warning: #FFCB67;
$_color-danger: #ea1836;
$_color-info: #878d99;

$_color-text-primary: #333340;
$_color-text-secondary: mix($_color-white, $_color-text-primary, 30%);


$_border-width-base: 3px;
$_border-style-base: solid;
$_border-color-base: #d8dce6;
$_border-color-lighter: #e6ebf5;
$_border-default: $_border-width-base $_border-style-base $_border-color-base;
$_border-radius-base: 4px;
$_border-radius-small: 2px;
$_border-radius-circle: 100%;


// base
// - - - - - - - - - - - - - - - - - - - -
$_button-font-size: 16px;
$_button-font-weight: 400;
$_button-border-radius: 4px;
$_button-default-color: $_color-text-primary;
$_button-default-fill: $_color-white;
$_button-default-border: $_border-color-base;
$_button-padding-vertical: .75em;
$_button-padding-horizontal: 1.5em;


// size
// - - - - - - - - - - - - - - - - - - - -
$_button-large-font-size: 16px;
$_button-large-border-radius: $_button-border-radius;
$_button-large-padding-vertical: 1.25em;
$_button-large-padding-horizontal: 1.5em;

$_button-medium-font-size: 16px;
$_button-medium-border-radius: $_button-border-radius;
$_button-medium-padding-vertical: 1em;
$_button-medium-padding-horizontal: 1.5em;

$_button-small-font-size: 12px;
$_button-small-border-radius: $_button-border-radius;
$_button-small-padding-vertical: .5em;
$_button-small-padding-horizontal: 1em;


// modifier
// - - - - - - - - - - - - - - - - - - - -
$_button-border-primary: $_color-primary;
$_button-color-primary: $_color-white;
$_button-fill-primary: $_color-primary;

$_button-border-secondary: $_color-secondary;
$_button-color-secondary: $_color-white;
$_button-fill-secondary: $_color-secondary;

$_button-border-success: $_color-success;
$_button-color-success: $_color-white;
$_button-fill-success: $_color-success;

$_button-border-warning: $_color-warning;
$_button-color-warning: $_color-white;
$_button-fill-warning: $_color-warning;

$_button-border-danger: $_color-danger;
$_button-color-danger: $_color-white;
$_button-fill-danger: $_color-danger;

$_button-border-info: $_color-info;
$_button-color-info: $_color-white;
$_button-fill-info: $_color-info;


// disabled
// - - - - - - - - - - - - - - - - - - - -
$_button-disabled-color: $_color-text-secondary;
$_button-disabled-fill: $_color-white;
$_button-disabled-border: $_border-color-base;


// interaction
// - - - - - - - - - - - - - - - - - - - -
$_button-hover-percent: 20%;
$_button-active-percent: 10%;
$_button-transition: all .2s cubic-bezier(0.23, 1, 0.32, 1);

mixin.scss

見通しを良くするために mixin で関数を定義します。

mixin.scss

@mixin button-outline($color) {
  color: $color;
  background: mix($_color-white, $color, 98%);
  border-color: mix($_color-white, $color, 60%);

  &:hover,
  &:focus {
    background: $color;
    border-color: $color;
    color: $_color-white;
  }

  &:active {
    background: mix($_color-black, $color, $_button-active-percent);
    border-color: mix($_color-black, $color, $_button-active-percent);
    color: $_color-white;
    outline: none;
  }

  &.is-disabled {
    &:hover,
    &:focus,
    &:active {
      color: mix($_color-white, $color, 40%);
      background-color: mix($_color-white, $color, 90%);
      border-color: mix($_color-white, $color, 80%);
    }
  }
}

@mixin button-modifier($color, $background-color, $border-color) {
  color: $color;
  background-color: $background-color;
  border-color: $border-color;

  &:hover,
  &:focus {
    background: mix($_color-white, $background-color, $_button-hover-percent);
    border-color: mix($_color-white, $border-color, $_button-hover-percent);
    color: $color;
  }

  &:active {
    background: mix($_color-black, $background-color, $_button-active-percent);
    border-color: mix($_color-black, $border-color, $_button-active-percent);
    color: $color;
    outline: none;
  }

  &.is-active {
    background: mix($_color-black, $background-color, $_button-active-percent);
    border-color: mix($_color-black, $border-color, $_button-active-percent);
    color: $color;
  }

  &.is-disabled {
    &,
    &:hover,
    &:focus,
    &:active {
      color: $_color-white;
      background-color: mix($background-color, $_color-white);
      border-color: mix($border-color, $_color-white);
    }
  }

  &.c-button--outline {
    @include button-outline($background-color);
  }

}

@mixin button-size($padding-vertical, $padding-horizontal, $font-size, $border-radius) {
  padding: $padding-vertical $padding-horizontal;
  font-size: $font-size;
  border-radius: $border-radius;
}

button.scss

そして、スタイルの本体部分です。

button.scss

.c-button {
  display: inline-block;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  line-height: 1;
  white-space: nowrap;
  text-align: center;
  cursor: pointer;
  color: $_button-default-color;
  font-family: inherit;
  font-weight: $_button-font-weight;
  @include button-size($_button-padding-vertical, $_button-padding-horizontal, $_button-font-size, $_button-border-radius);
  border: $_border-default;
  border-color: $_button-default-border;
  background: $_button-default-fill;
  appearance: none;
  outline: none;
  transition: $_button-transition;

  + .c-button {
    margin-left: 1rem;
  }

  &:hover,
  &:focus {
    color: $_color-primary;
    border-color: mix($_color-white, $_color-primary, 65%);
    background: mix($_color-white, $_color-primary, 95%);
  }

  &:active {
    color: mix($_color-black, $_color-primary, $_button-active-percent);
    border-color: mix($_color-black, $_color-primary, $_button-active-percent);
    outline: none;
  }

  // has icon
  // - - - - - - - - - - - - - - - - - - - -
  > .fa {
    margin-right: 5px;
  }

  // modifier
  // - - - - - - - - - - - - - - - - - - - -
  &.c-button--primary {
    @include button-modifier($_button-color-primary, $_button-fill-primary, $_button-border-primary);
  }
  &.c-button--secondary {
    @include button-modifier($_button-color-secondary, $_button-fill-secondary, $_button-border-secondary);
  }
  &.c-button--success {
    @include button-modifier($_button-color-success, $_button-fill-success, $_button-border-success);
  }
  &.c-button--warning {
    @include button-modifier($_button-color-warning, $_button-fill-warning, $_button-border-warning);
  }
  &.c-button--danger {
    @include button-modifier($_button-color-danger, $_button-fill-danger, $_button-border-danger);
  }
  &.c-button--info {
    @include button-modifier($_button-color-info, $_button-fill-info, $_button-border-info);
  }

  &.c-button--large {
    @include button-size($_button-large-padding-vertical, $_button-large-padding-horizontal, $_button-large-font-size, $_button-large-border-radius);
  }
  &.c-button--medium {
    @include button-size($_button-medium-padding-vertical, $_button-medium-padding-horizontal, $_button-medium-font-size, $_button-medium-border-radius);
  }
  &.c-button--small {
    @include button-size($_button-small-padding-vertical, $_button-small-padding-horizontal, $_button-small-font-size, $_button-small-border-radius);
  }

  &.c-button--full {
    width: 100%;
  }

  //
  &.c-button--text {
    padding-left: 0;
    padding-right: 0;
    color: $_color-primary;
    border: none;
    background: transparent;

    &:hover,
    &:focus {
      color: mix($_color-white, $_color-primary, $_button-hover-percent);
      border-color: transparent;
      background-color: transparent;
    }
    &:active {
      color: mix($_color-black, $_color-primary, $_button-active-percent);
      border-color: transparent;
      background-color: transparent;
    }
  }

  &.c-button--round {
    border-radius: 1.5em / 50%;
  }

  &.c-button--outline {
    &:hover,
    &:focus {
      background: $_color-white;
      border-color: $_color-primary;
      color: $_color-primary;
    }

    &:active {
      background: $_color-white;
      border-color: mix($_color-black, $_color-primary, $_button-active-percent);
      color: mix($_color-black, $_color-primary, $_button-active-percent);
      outline: none;
    }
  }

  // state
  // - - - - - - - - - - - - - - - - - - - -
  &.is-active {
    color: mix($_color-black, $_color-primary, $_button-active-percent);
    border-color: mix($_color-black, $_color-primary, $_button-active-percent);
  }

  &.is-disabled {
    &,
    &:hover,
    &:focus {
      color: $_button-disabled-color;
      cursor: not-allowed;
      background-image: none;
      background-color: $_button-disabled-fill;
      border-color: $_button-disabled-border;
    }

    &.c-button--text {
      background-color: transparent;
    }

    &.c-button--outline {
      &:hover,
      &:focus {
        background-color: $_color-white;
        border-color: $_button-disabled-border;
        color: $_button-disabled-color;
      }
    }
  }

  &.is-loading {
    position: relative;
    z-index: 1;
    pointer-events: none;

    &::before {
      content: '';
      position: absolute;
      z-index: 2;
      left: -2px;
      top: -2px;
      right: -2px;
      bottom: -2px;
      border-radius: inherit;
      background-color: rgba($_color-black,.35);
      pointer-events: none;
    }

    &::after {
      content: "";
      position: absolute;
      z-index: 3;
      display: block;
      left: calc(50% - (1em / 2));
      top: calc(50% - (1em / 2));
      height: 1em;
      width: 1em;
      border: $_border-default;
      border-color: $_border-color-lighter;
      border-radius: 100%;
      border-right-color: transparent;
      border-top-color: transparent;
      animation: spin .48s infinite ease-in;
      pointer-events: none;
    }

  }

}

コンポーネントを呼び出す

ようやくコンポーネントを呼び出すところにやってきました。😇

index.html

index.html

  <div id="app">
      <p>
          <fav-button variation="" icon="">button</fav-button>
      </p>
      <p>
          <fav-button variation="primary" icon="twitter">button</fav-button>
          <fav-button variation="primary" size="small" icon="twitter">button</fav-button>
          <fav-button variation="primary" size="medium" icon="twitter">button</fav-button>
          <fav-button variation="primary" size="large" icon="twitter">button</fav-button>
      </p>
      <p>
          <fav-button variation="primary" icon="facebook">primary</fav-button>
          <fav-button variation="secondary" icon="twitter">secondary</fav-button>
          <fav-button variation="success" icon="coffee">success</fav-button>
          <fav-button variation="warning" icon="check">warning</fav-button>
          <fav-button variation="danger" icon="bolt">danger</fav-button>
          <fav-button variation="info" icon="info">info</fav-button>
      </p>
      <p>
          <fav-button variation="primary" outline icon="facebook">primary</fav-button>
          <fav-button variation="secondary" outline icon="twitter">secondary</fav-button>
          <fav-button variation="success" outline icon="coffee">success</fav-button>
          <fav-button variation="warning" outline icon="check">warning</fav-button>
          <fav-button variation="danger" outline icon="bolt">danger</fav-button>
          <fav-button variation="info" outline icon="info">info</fav-button>
      </p>
      <p>
          <fav-button variation="primary" loading icon="twitter">loading</fav-button>
          <fav-button variation="primary" disabled icon="twitter">disabled</fav-button>
          <fav-button round>round</fav-button>
      </p>
      <p>
          <fav-button variation="primary" full icon="twitter">fulll width</fav-button>
      </p>
    <p>
          <fav-button text>plane text</fav-button>
       </p>
  </div>

すると、こんな感じに描画しました。

s_.jpg

できた!

コンポーネントのスタイルガイド

これについてはまだ実現していません。
普段は、GitHub - frontainer/gulp-frontnote: スタイルガイドジェネレーターFrontNoteのGulpプラグイン を使っているのですが、今回、何を使おうか悩んでいます🤔

参考

おわります。

22
30
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
30