Components

Vue Components structure

Components allow us to split the UI into independent and reusable pieces, and think about each piece in isolation.

For more info check the official Vue documentation.

This is an example of a widely used component within Balance, BaseInput:

<template>
    <div v-if="hasLabel" class="w-full pt-1">
        <label :for="name" :class="labelClassName ?? LabelClassEnum.DEFAULT">
            {{ label }} {{ required ? '*' : '' }}
        </label>
    </div>
    <div class="flex-1">
        <textarea
            v-if="type == BaseInputTypeEnum.TEXTAREA"
            :class="
                className ??
                InputClassEnum.DEFAULT +
                    (requestErrors && requestErrors.length > 0
                        ? ' border-red-500'
                        : '')
            "
            :value="value"
            :placeholder="placeholder ?? ' '"
            :readonly="readonly"
            @input="(value) => $emit('onInput', value)"
            @keyup.enter="$emit('onPressEnter')"
            @keydown.enter.exact.prevent
        />
        <input
            v-else
            ref="inputRef"
            :class="
                className ??
                InputClassEnum.DEFAULT +
                    (requestErrors && requestErrors.length > 0
                        ? ' border-red-500'
                        : '')
            "
            :value="value"
            :placeholder="placeholder ?? ' '"
            :type="type"
            :name="name"
            :disabled="disabled"
            :alt="alt"
            :autocomplete="autocomplete ? 'on' : 'off'"
            :checked="!!value"
            :autofocus="autofocus"
            :form="form"
            :formaction="formaction"
            :formenctype="formenctype"
            :formmethod="formmethod"
            :formnovalidate="formnovalidate"
            :formtarget="formtarget"
            :height="height"
            :list="list"
            :max="max"
            :maxlength="maxlength"
            :min="min"
            :minlength="minlength"
            :multiple="multiple"
            :pattern="pattern"
            :readonly="readonly"
            :size="size"
            :src="src"
            :step="step"
            :width="width"
            :accept="accept"
            @input="(value) => $emit('onInput', value)"
            @keyup.exact.enter="$emit('onPressEnter')"
            @focusout="$emit('onFocusOut')"
            @keyup.exact.ctrl.enter="$emit('onPressCtrlEnter')"
        />
    </div>
    <div
        v-if="requestErrors && requestErrors.length > 0"
        :for="name"
        class="flex w-full flex-col gap-2"
    >
        <span
            v-for="(error, i) in requestErrors"
            :key="`request-error-${i}`"
            class="text-sm font-bold text-red-500"
        >
            {{ error }}
        </span>
    </div>
</template>

<script setup lang="ts">
import {
    InputClassEnum,
    LabelClassEnum,
} from '@/enums/styles/BaseInputClassEnum'
import { BaseInputTypeEnum } from '@enums/components/BaseInputTypeEnum'
import { onMounted, ref } from 'vue'

defineEmits(['onInput', 'onPressEnter', 'onFocusOut', 'onPressCtrlEnter'])

const props = defineProps<{
    placeholder?: string
    className?: string
    value?: any
    name?: string
    type?: BaseInputTypeEnum
    required?: boolean
    hasLabel?: boolean
    labelClassName?: LabelClassEnum
    label?: string
    disabled?: boolean
    alt?: string
    checked?: boolean
    autocomplete?: boolean
    autofocus?: boolean
    form?: string
    formaction?: string
    formenctype?: string
    formmethod?: string
    formnovalidate?: boolean
    formtarget?: string
    height?: number
    list?: string
    max?: number
    maxlength?: number
    min?: number
    minlength?: number
    multiple?: boolean
    pattern?: string
    readonly?: boolean
    size?: number
    src?: string
    step?: string
    width?: number
    accept?: string
    accesskey?: string
    contenteditable?: boolean
    contextmenu?: string
    requestErrors?: Array<string>
    dir?: string
}>()

const inputRef = ref<HTMLInputElement>()

onMounted(() => {
    if (props.autofocus) {
        inputRef.value?.focus()
    }
})
</script>

And this is its usage:

<BaseInput
    :label="
        t('cruds.user.fields.external_source')
    "
    name="external_source"
    :value="
        usersSingleStore.entry.external_source
    "
    has-label
    autocomplete
    :type="BaseInputTypeEnum.TEXT"
    :request-errors="
        usersSingleStore.errorRequest.external_source
    "
    @on-input="updateExternalSource"
/>

Last updated