Files
simrsx-fe/app/components/pub/my-ui/form/input-base.vue

124 lines
3.4 KiB
Vue

<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import { Input } from '~/components/pub/ui/input'
import { cn } from '~/lib/utils'
import { computed } from 'vue'
import { useFieldError } from 'vee-validate'
import * as DE from '~/components/pub/my-ui/doc-entry'
type InputType = 'text' | 'number' | 'password' | 'email' | 'date' | 'time' | 'datetime-local' | 'search' | 'tel'
const props = defineProps<{
fieldName: string
placeholder: string
label?: string
errors?: FormErrors
class?: string
colSpan?: number
numericOnly?: boolean
maxLength?: number
isRequired?: boolean
isDisabled?: boolean
rightLabel?: string
bottomLabel?: string
suffixMsg?: string
iconName?: string
inputType?: InputType
}>()
const { class: containerClass } = props
function handleInput(event: Event) {
const target = event.target as HTMLInputElement
let value = target.value
// Filter numeric only jika diperlukan
if (props.numericOnly) {
value = value.replace(/\D/g, '')
}
// Batasi panjang maksimal jika diperlukan
if (props.maxLength && value.length > props.maxLength) {
value = value.slice(0, props.maxLength)
}
// Update value jika ada perubahan
if (target.value !== value) {
target.value = value
// Trigger input event untuk update form
target.dispatchEvent(new Event('input', { bubbles: true }))
}
}
// Get error state from vee-validate
const fieldError = useFieldError(() => props.fieldName)
const hasError = computed(() => !!fieldError.value)
</script>
<template>
<DE.Cell :col-span="colSpan || 1">
<DE.Label
v-if="label"
:label-for="fieldName"
:is-required="isRequired && !isDisabled"
>
{{ label }}
</DE.Label>
<DE.Field
:id="fieldName"
:errors="errors"
>
<FormField
v-slot="{ componentField }"
:name="fieldName"
>
<FormItem>
<FormControl :class="cn('relative', containerClass)">
<div class="relative w-full max-w-sm items-center">
<Input
:disabled="isDisabled"
v-bind="componentField"
:placeholder="placeholder"
:maxlength="maxLength"
:class="cn(hasError && 'border-red-500 focus-visible:border-red-500 focus-visible:ring-red-500')"
autocomplete="off"
aria-autocomplete="none"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
@input="handleInput"
:type="inputType"
/>
<span
v-if="suffixMsg"
class="absolute inset-y-0 end-0 flex items-center justify-center px-2 text-muted-foreground"
>
{{ suffixMsg }}
</span>
<Icon
v-if="iconName"
:name="iconName"
class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-500 dark:text-gray-400"
/>
<p
v-show="rightLabel"
class="absolute right-3 top-0 text-gray-400"
>
{{ rightLabel }}
</p>
</div>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</DE.Field>
<p
v-show="bottomLabel"
class="text-gray-400"
>
{{ bottomLabel }}
</p>
</DE.Cell>
</template>