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>
datepicker

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>