<template>
  <FormulateForm
    id="basic-signup-form"
    @submit="submitHandler"
  >
    <h2>Sign up for FormulateConf 2020 in Charlottesville, VA</h2>
    <FormulateInput
      name="username"
      label="Username" 
      validation="^required|^min:4,length|^max:8,length|^alpha:latin|unique"
      :validation-rules="{ unique: checkUsername }"
      :validation-messages="{ unique: 'Sorry, that username is already taken' }"
      help="Usernames must be lowercase and between 4-8 characters in length. (psst, try 'bossman' for a failing username)"
    />
    <FormulateInput
      name="email"
      label="Email"
      type="email"
      validation="^required|email"
    />
    <FormulateInput
      name="bio"
      label="Bio"
      type="textarea"
      validation="required"
      help="Help others get to know you a bit."
    />
    <FormulateInput
      name="password"
      label="Password"
      validation="^required|min:6,length"
      help="Your password must be at least 6 characters long."
      autocomplete="off"
    />
    <FormulateInput
      name="password_confirm" 
      label="Confirm Password"
      validation="^required|^min:6,length|confirm"
      validation-name="Password"
      autocomplete="off"
    />
    <FormulateInput 
      type="submit" 
      name="Register!"
    />
  </FormulateForm>
</template>

<script>
export default {
  data () {
    return {
      takenNamePartials: [
        'dawg',
        'js',
        'boss',
        'oy'
      ]
    }
  },
  methods: {
    submitHandler () {
      alert('Thank you for signing up!')
    },
    async checkUsername ({ value }) {
      // check to see if the username contains any of 
      // our partial matches to simulate taken usernames.
      // wrap it in a promise and timeout to simulate an xhr request.
      let match = false
      await new Promise((resolve) => {
        setTimeout(() => {
          // use .find() to check for matches and bail on first success
          match = this.takenNamePartials.find(partial => value.includes(partial))
          resolve()
        }, 2000)
      })
      // return the cooreced boolean value of !match
      return !!!match
    }
  }
}
</script>

<style lang="scss">
body {
  min-height: 100vh;
  padding: 2em;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(to bottom right, cyan, magenta);
}
  
form {
  display: flex;
  flex-direction: column;
  background-color: #fff;
  border: 1px solid #999;
  width: 90%;
  max-width: 320px;
  padding: 3em 2em;
  border-radius: 0.3em;
  box-shadow: 0.25em 0.15em 12em 0 rgba(#000, 0.25);
  
  * {
    box-sizing: border-box;
  }
  
  h2 {
    margin-top: 0;
    color: #555;
  }
}
  
.formulate-input[data-classification=button] {
  display: flex;
  
  .formulate-input-wrapper {
    margin: auto;
  }

  button {
    appearance: none;
    border: 1px solid deeppink;
    background-color: magenta;
    font-weight: bold;
    color: #fff;
    text-transform: uppercase;
    padding: 1em 1.5em;
    border-radius: 0.5em;
    margin: 1em auto 0 auto;
    
    &:active,
    &:focus {
      border: 1px solid deeppink;
      background-color: magenta;
      color: #fff;
    }
    
  }
}
</style>

External CSS

  1. https://s3-us-west-2.amazonaws.com/s.cdpn.io/15253/codepen-2.3.0-snow.min.css

External JavaScript

  1. https://s3-us-west-2.amazonaws.com/s.cdpn.io/15253/codepen-2.3.0-bootstrap.js