isuke
10/20/2017 - 9:13 AM

SmartForm.vue

<template lang="pug">
.smart-form
  span.hint(v-text="hint" v-if="hint")
  input.input(:type="type" :name="attr" :placeholder="placeholder" v-model="value" v-bind:class="{ '-error': error }" v-if="isInputTag")
  textarea.input(:name="attr" :placeholder="placeholder" v-model="value" v-bind:class="{ '-error': error }" :rows="rows" v-if="isTextareaTag")
  label.label(:for="attr" v-text="i18nName + (required ? requiredText : '')")
  span.errors(v-text="errors")
</template>

<script lang="coffee">
export default
  props:
    models:
      type: Array
      required: true
      coerce: (val) ->
        if _.isArray val
          val
        else
          _.toPath(val)
    attr:
      type: String
      required: true
      coerce: (val) -> _.camelCase(val)
    type:
      type: String
      required: true
    name:
      type: String
      required: false
      default: null
    placeholder:
      type: String
      required: false
      default: ''
    required:
      type: Boolean
      required: false
      default: false
    defaultValue:
      type: [String, Number]
      required: false
      default: null
    hint:
      type: String
      required: false
      default: ''
  data: ->
    requiredText: '*'
  computed:
    ownerModal: -> _.last @models
    i18nName: ->
      if @name?
        @name
      else
        i18n.t("activerecord.attributes.#{_.snakeCase(@ownerModal)}.#{_.snakeCase(@attr)}")
    value:
      get: -> _.get @$store.state.form, @models.concat([@attr])
      set: (value) -> @$store.dispatch('SET_FORM_VALUE', @models.concat([@attr]), value)
    errors: ->
      errors = _.get(@$store.state.errors, @models.concat([@attr]))

      if errors? then  _.join errors else null
    isInputTag: -> _.includes(['text', 'number', 'checkbox'], @type)
    isTextareaTag: -> _.includes(['textarea'], @type)
    rows: ->
      num = if @value? then @value.split("\n").length else 0
      _.max([2, num])
</script>

<style lang="scss" scoped>
.smart-form {
  display: flex;
  flex-direction: column-reverse;

  > .hint {
    font-size: $small-font-size;
    color: $warn-color;
  }
  > .label {
    font-size: $small-font-size;
  }
  > .input {
    border-bottom: $base-border-height solid $base-font-color;
    &:focus {
      border-bottom: $base-border-height solid $action-color;
      + .label {
        color: $action-color;
      }
    }
    &.-error {
      border-bottom: $base-border-height solid $error-color;
      + .label {
        color: $error-color;
      }
    }
  }
  > .errors {
    color: $error-color;
    font-size: $small-font-size;
  }
}
</style>