Top

Forms


Looks good!
Looks good!
@
Please choose a username.
Please provide a valid city.
Please select a valid state.
Please provide a valid zip.
You must agree before submitting.

form.vue

<template>
<form class="row g-3 needs-validation custom-input" novalidate @submit.prevent="submitForm()">
      <div class="col-md-4 position-relative">
        <InputWrapper :title="'First Name'" :required="true">
          <InputField
            :formSubmitted="formSubmitted"
            :errorMessage="'First name is required.'"
            v-model:modelValue="tooltipValidationForm.first_name"
            :inputId="'tooltip-form-first-name'"
            :placeholder="'Enter first name'"
            :tooltipValidation="true"
          />
        </InputWrapper>
      </div>
      <div class="col-md-4 position-relative">
        <InputWrapper :title="'Last Name'" :required="true">
          <InputField
            :formSubmitted="formSubmitted"
            :errorMessage="'Last name is required.'"
            v-model:modelValue="tooltipValidationForm.last_name"
            :inputId="'tooltip-form-last-name'"
            :placeholder="'Enter last name'"
            :tooltipValidation="true"
          />
        </InputWrapper>
      </div>
      <div class="col-md-4 position-relative">
        <InputWrapper :title="'Username'" :required="true">
          <div class="input-group has-validation">
            <span class="input-group-text" id="validationTooltipUsernamePrepend">&#64;</span>
            <InputField
              :formSubmitted="formSubmitted"
              :errorMessage="'User name is required.'"
              v-model:modelValue="tooltipValidationForm.user_name"
              :inputId="'tooltip-form-user-name'"
              :placeholder="'Enter user name'"
              :tooltipValidation="true"
            />
          </div>
        </InputWrapper>
      </div>
      <div class="col-md-6 position-relative">
        <InputWrapper :title="'City'" :required="true">
          <InputField
            :formSubmitted="formSubmitted"
            :errorMessage="'City is required.'"
            v-model:modelValue="tooltipValidationForm.city"
            :inputId="'tooltip-form-city'"
            :placeholder="'Enter city'"
            :tooltipValidation="true"
          />
        </InputWrapper>
      </div>
      <div class="col-md-3 position-relative">
        <InputWrapper :title="'State'" :required="true">
          <Select
            getValueKey="label"
            display-key="label"
            :placeholder="'Select state'"
            v-model="tooltipValidationForm.state"
            :errorMessage="'Please select state.'"
            :options="states"
            :formSubmitted="formSubmitted"
            :tooltipValidation="true"
          />
        </InputWrapper>
      </div>
      <div class="col-md-3 position-relative">
        <InputWrapper :title="'Zip'" :required="true">
          <InputField
            :formSubmitted="formSubmitted"
            :errorMessage="'Zip is required.'"
            v-model:modelValue="tooltipValidationForm.zip"
            :inputId="'tooltip-form-zip'"
            :placeholder="'Enter zip'"
            :tooltipValidation="true"
          />
        </InputWrapper>
      </div>
      <div class="col-12">
        <button class="btn btn-primary" type="submit">Submit form</button>
      </div>
    </form>
</template>

<script setup lang="ts">
import { initInputField, initSelectField } from '~/public/data/common';
import { states } from '~/public/data/country';
import { validateForm } from '~/utils/validators/formValidators';

const { resetForm } = baseUtils();

let formSubmitted = ref(false);
let tooltipValidationForm = reactive({
  first_name: initInputField(),
  last_name: initInputField(),
  user_name: initInputField(),
  city: initInputField(),
  state: initSelectField(),
  zip: initInputField(),
});

function submitForm() {
  formSubmitted.value = true;
  const { isValid, formData } = validateForm(tooltipValidationForm);

  if (isValid) {
    tooltipValidationForm = resetForm(tooltipValidationForm);
    formSubmitted.value = false;
  }
}
</script>

<style scoped></style>

InputWrapper.vue

<template>
  <label class="form-label" :class="props.class" v-if="!props.labelPositionBottom"
    >{{ props.title }}
    <span class="txt-danger" v-if="props.required">*</span>
  </label>
  <slot />
</template>

<script setup lang="ts">
const props = withDefaults(
  defineProps<{
    title: string;
    class?: string;
    labelPositionBottom?: boolean;
    required?: boolean;
  }>(),
  {
    class: '',
    required: false,
  },
);
</script>

<style scoped></style>

InputField.vue

<template>
  <input
    :value="modelValue?.data ? modelValue?.data : ''"
    :type="inputType"
    :id="inputId"
    v-bind:placeholder="isPlaceholder ? placeholder || 'Enter Value' : undefined"
    class="form-control"
    :class="[props.class, { 'is-invalid ': modelValue?.errorMessage && !props.browserValidation }, { 'animated input-shake': animationClass }]"
    @input="onInput"
    @focusout="onInput"
    @focus="showBadge()"
    @blur="hideBadge()"
    v-if="inputType !== 'textarea' && inputType !== 'file'"
    :required="props.required && props.browserValidation"
    :disabled="props.disabled"
    :list="props.datalist?.length ? `datalistOptions-${inputId}` : undefined"
    :maxlength="props.maxlength"
    :min="props.inputType === 'date' && props.minDate ? formatDateForInput(props.minDate) : undefined"
    :max="props.inputType === 'date' && props.maxDate ? formatDateForInput(props.maxDate) : undefined"
  />

  <slot />
  <template v-if="modelValue?.errorMessage && required && !props.browserValidation">
    <div class="invalid-tooltip" v-if="props.tooltipValidation">{{ modelValue?.errorMessage }}</div>
    <div class="invalid-feedback" v-else>{{ modelValue?.errorMessage }}</div>
  </template>

  <div class="helper-text" v-if="props.helperText">
    <p class="fst-italic c-o-light" v-html="`*${props.helperText}`"></p>
  </div>
</template>

<script setup lang="ts">
import { validateNonEmptyFields, validateEmail } from '~/utils/validators/InputFieldValidators';

import type { InputField, Select } from '~/types/common';

interface ValidationStatus {
  errorMessage: string;
  valid: boolean;
}

const props = withDefaults(
  defineProps<{
    formSubmitted?: boolean;
    inputId?: string;
    modelValue?: InputField;
    errorMessage?: string;
    class?: string;
    placeholder?: string;
    inputType?: string;
    required?: boolean;
    minLength?: number;
    rows?: number;
    multiple?: boolean;
    helperText?: string;
    disabled?: boolean;
    tooltipValidation?: boolean;
    browserValidation?: boolean;
    animation?: boolean;
    datalist?: Select[];
    maxlength?: number;
    isPlaceholder?: boolean;
    showLengthBadge?: boolean;
    formatValue?: boolean;
    formatFunction?: Function | null;
    minDate?: Date;
    maxDate?: Date;
  }>(),
  {
    inputType: 'text',
    required: true,
    rows: 3,
    multiple: false,
    helperText: '',
    disabled: false,
    tooltipValidation: false,
    browserValidation: false,
    animation: false,
    datalist: () => [],
    isPlaceholder: true,
    showLengthBadge: false,
    formatValue: false,
    formatFunction: null,
  },
);

const emits = defineEmits(['update:modelValue', 'badgeVisible']);

let validStatus = ref<ValidationStatus>({
  errorMessage: '',
  valid: false,
});
let changed = ref(false);
const animationClass = ref('');

watch(
  () => props.formSubmitted,
  () => {
    props.formSubmitted && updated(props.modelValue?.data || '');
  },
  { deep: true },
);

onMounted(() => {
  if (props.formSubmitted) {
    updated(props.modelValue?.data || '');
  }
});

function onInput(event: Event) {
  const target = event.target as HTMLInputElement | null;
  if (!target) return;

  updated(target.value);
}

function updated(inputValue?: string | number | File | null) {
  changed.value = true;

  // Handle validation
  if (props.required) {
    if (props.inputType === 'email') {
      validStatus.value = validateEmail(String(inputValue));
    } else if (props.inputType === 'file') {
      const isValid = !!inputValue;
      validStatus.value = {
        valid: isValid,
        errorMessage: isValid ? '' : props.errorMessage || 'File is required.',
      };
    } else {
      validStatus.value = validateNonEmptyFields({
        value: String(inputValue),
        minLength: props.minLength,
        errorMessage: props.errorMessage,
      });
    }
  } else {
    validStatus.value = { valid: true, errorMessage: '' };
  }
}

  // Prepare emitted value
  let data = inputValue;

  if (props.inputType === 'number') {
    data = inputValue === '' || inputValue == null ? '' : Number(inputValue);
  } else if (props.inputType === 'file') {
    data = inputValue instanceof File ? inputValue : null;
  }

  // Emit value
  emits('update:modelValue', {
    data,
    errorMessage: validStatus.value.errorMessage,
  });
</script>

<style scoped></style>
<div class="card-wrapper border rounded-3">
    <form class="grid grid-cols-12 gap-4">
        <div class="col-span-12">
          <label class="form-label" for="inputEmail">Email Address</label>
          <input class="form-control" id="inputEmail" type="email" placeholder="name@example.com">
        </div>
    </form>
</div>
<template>
  <div class="container">
    <h4>Checkbox & Radio</h4>
    <div class="group">
      <!-- Radio Group -->
      <div class="column">
        <label>
          <input type="radio" name="radio" value="option1" />
          <span>Option 1</span>
        </label>

        <label class="disabled">
          <input type="radio" name="radio" disabled />
          <span>Disabled</span>
        </label>

        <label>
          <input type="radio" name="radio" checked />
          <span>Checked</span>
        </label>
      </div>

      <!-- Checkbox Group -->
      <div class="column">
        <label>
          <input type="checkbox" />
          <span>Default</span>
        </label>

        <label class="disabled">
          <input type="checkbox" disabled />
          <span>Disabled</span>
        </label>

        <label>
          <input type="checkbox" checked />
          <span>Checked</span>
        </label>
      </div>
    </div>
  </div>
</template>

<script setup></script>

<style scoped></style>

Installation

npm install flatpickr
      

datepicker.vue


<template>
  <Flatpickr class="form-control digits" placeholder="dd-mm-yyyy" v-model="dateState.defaultDate" :config="dateConfigs.dateConfig" />
</template>

<script setup>
import flatpickr from 'flatpickr'
import 'flatpickr/dist/flatpickr.css'

const date = new Date();
const dateState = reactive({
  defaultDate: null as string | Date | null,
});

const dateConfigs = reactive({
  dateConfig: {
    dateFormat: 'd-m-Y',
  },
})
</script>

      

Uninstalling Package

npm uninstall flatpickr
      
<label class="switch">
 <input type="checkbox" checked="" data-bs-original-title="" title="">
 <span class="switch-state"></span>
</label>
<label class="switch">
 <input type="checkbox" data-bs-original-title="" title="">
 <span class="switch-state"></span>
</label>

typeahead.vue

<template>
  <div>
    <Vue3SimpleTypeahead
      id="typeahead-basic"
      placeholder="Search for a state"
      :items="states"
      v-model="model"
      :minInputLength="2"
      @selectItem="onSelectState"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue';
import Vue3SimpleTypeahead from 'vue3-simple-typeahead';
import 'vue3-simple-typeahead/dist/vue3-simple-typeahead.css';
import { states } from '~/data/states';

const model = ref('');
const onSelectState = (selected) => {
  model.value = selected;
};
</script>

states.ts

export const states = [
  'Alabama',
  'Alaska',
  'American Samoa',
  'Arizona',
  'Arkansas',
  'California',
  'Colorado',
  'Connecticut',
  'Delaware',
  'District Of Columbia',
  'Federated States Of Micronesia',
  'Florida',
  'Georgia',
  'Guam',
  'Hawaii',
  'Idaho',
  'Illinois',
  'Indiana',
  'Iowa',
  'Kansas',
  'Kentucky',
  'Louisiana',
  'Maine',
  'Marshall Islands',
  'Maryland',
  'Massachusetts',
  'Michigan',
  'Minnesota',
  'Mississippi',
  'Missouri',
  'Montana',
  'Nebraska',
  'Nevada',
  'New Hampshire',
  'New Jersey',
  'New Mexico',
  'New York',
  'North Carolina',
  'North Dakota',
  'Northern Mariana Islands',
  'Ohio',
  'Oklahoma',
  'Oregon',
  'Palau',
  'Pennsylvania',
  'Puerto Rico',
  'Rhode Island',
  'South Carolina',
  'South Dakota',
  'Tennessee',
  'Texas',
  'Utah',
  'Vermont',
  'Virgin Islands',
  'Virginia',
  'Washington',
  'West Virginia',
  'Wisconsin',
  'Wyoming',
];