refactor(form): migrate form components to use doc-entry pattern
- Replace FieldGroup/Field/Label with DE.Cell/DE.Field/DE.Label components - Add ButtonAction component with preset styling for common form actions - Simplify form layouts using doc-entry grid system - Add support for disabled/readonly states in form fields
This commit is contained in:
No files matched your search
@@ -3,14 +3,16 @@ import { toTypedSchema } from '@vee-validate/zod'
|
|||||||
import { FieldArray } from 'vee-validate'
|
import { FieldArray } from 'vee-validate'
|
||||||
|
|
||||||
// components
|
// components
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
import { Form } from '~/components/pub/ui/form'
|
import { Form } from '~/components/pub/ui/form'
|
||||||
import InputBase from '~/components/pub/my-ui/form/input-base.vue'
|
|
||||||
import Label from '~/components/pub/my-ui/form/label.vue'
|
|
||||||
import { SelectRelations } from './fields'
|
import { SelectRelations } from './fields'
|
||||||
|
import InputBase from '~/components/pub/my-ui/form/input-base.vue'
|
||||||
|
import ButtonAction from '~/components/pub/my-ui/form/button-action.vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
title: string
|
title: string
|
||||||
schema: any
|
schema: any
|
||||||
|
isReadonly?: boolean
|
||||||
initialValues?: any
|
initialValues?: any
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
@@ -23,6 +25,8 @@ defineExpose({
|
|||||||
setValues: (values: any, shouldValidate = true) => formRef.value?.setValues(values, shouldValidate),
|
setValues: (values: any, shouldValidate = true) => formRef.value?.setValues(values, shouldValidate),
|
||||||
values: computed(() => formRef.value?.values),
|
values: computed(() => formRef.value?.values),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const { title = 'Kontak Pasien', isReadonly = false } = props
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -32,119 +36,77 @@ defineExpose({
|
|||||||
keep-values
|
keep-values
|
||||||
:validation-schema="formSchema"
|
:validation-schema="formSchema"
|
||||||
:validate-on-mount="false"
|
:validate-on-mount="false"
|
||||||
|
:initial-values="initialValues ? initialValues : {}"
|
||||||
validation-mode="onSubmit"
|
validation-mode="onSubmit"
|
||||||
:initial-values="initialValues || { contacts: [{ relation: '', name: '', address: '', phone: '' }] }"
|
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<p
|
<p class="text-md mb-2 mt-1 font-semibold">
|
||||||
v-if="props.title"
|
{{ title }}
|
||||||
class="text-md mb-2 mt-1 font-semibold"
|
|
||||||
>
|
|
||||||
{{ props.title || 'Kontak Pasien' }}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-5 space-y-4">
|
<div class="mb-5 space-y-4">
|
||||||
<FieldArray
|
<FieldArray
|
||||||
v-slot="{ fields, push, remove }"
|
v-slot="{ fields, push, remove }"
|
||||||
name="contacts"
|
name="medicines"
|
||||||
>
|
>
|
||||||
|
<template v-if="fields.length === 0">
|
||||||
|
{{ push({ name: '', dose: '', unit: '' }) }}
|
||||||
|
</template>
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div
|
<DE.Block
|
||||||
v-for="(field, idx) in fields"
|
v-for="(field, idx) in fields"
|
||||||
:key="field.key"
|
:key="field.key"
|
||||||
class="rounded-lg border border-gray-200 bg-gray-50/50 p-4 dark:border-gray-700 dark:bg-gray-800/50"
|
:col-count="5"
|
||||||
|
:cell-flex="false"
|
||||||
>
|
>
|
||||||
<div class="mb-3 flex items-center justify-between">
|
<SelectRelations
|
||||||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300">Penanggung Jawab {{ idx + 1 }}</h4>
|
:label="idx === 0 ? 'Hubungan dengan Pasien' : undefined"
|
||||||
<Button
|
:field-name="`contacts[${idx}].relation`"
|
||||||
v-if="idx !== 0"
|
placeholder="Pilih"
|
||||||
type="button"
|
field-group-class="mb-0"
|
||||||
variant="outline"
|
:is-disabled="isReadonly"
|
||||||
size="sm"
|
/>
|
||||||
class="text-red-600 hover:border-red-400 hover:bg-red-50 hover:text-red-700 dark:border-red-400 dark:text-red-400 dark:hover:border-red-300 dark:hover:bg-red-900/20"
|
<InputBase
|
||||||
|
:label="idx === 0 ? 'Nama' : undefined"
|
||||||
|
:field-name="`contacts[${idx}].name`"
|
||||||
|
placeholder="Masukkan Nama"
|
||||||
|
:is-disabled="isReadonly"
|
||||||
|
/>
|
||||||
|
<InputBase
|
||||||
|
:label="idx === 0 ? 'Alamat' : undefined"
|
||||||
|
:field-name="`contacts[${idx}].address`"
|
||||||
|
placeholder="Masukkan Alamat"
|
||||||
|
:is-disabled="isReadonly"
|
||||||
|
/>
|
||||||
|
<InputBase
|
||||||
|
:label="idx === 0 ? 'Nomor HP' : undefined"
|
||||||
|
:field-name="`contacts[${idx}].phone`"
|
||||||
|
placeholder="081234567890"
|
||||||
|
:max-length="15"
|
||||||
|
numeric-only
|
||||||
|
:is-disabled="isReadonly"
|
||||||
|
/>
|
||||||
|
<DE.Cell class="flex items-end justify-start">
|
||||||
|
<ButtonAction
|
||||||
|
:disabled="isReadonly"
|
||||||
|
preset="delete"
|
||||||
|
title="Hapus"
|
||||||
|
icon-only
|
||||||
@click="remove(idx)"
|
@click="remove(idx)"
|
||||||
>
|
/>
|
||||||
<Icon
|
</DE.Cell>
|
||||||
name="i-lucide-trash-2"
|
</DE.Block>
|
||||||
class="h-4 w-4"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
|
|
||||||
<div class="space-y-2">
|
|
||||||
<Label
|
|
||||||
class="text-sm font-medium text-gray-700 dark:text-gray-300"
|
|
||||||
is-required
|
|
||||||
>
|
|
||||||
Hubungan dengan Pasien
|
|
||||||
</Label>
|
|
||||||
<SelectRelations
|
|
||||||
:field-name="`contacts[${idx}].relation`"
|
|
||||||
placeholder="Pilih"
|
|
||||||
field-group-class="mb-0"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="space-y-2">
|
|
||||||
<Label
|
|
||||||
class="text-sm font-medium text-gray-700 dark:text-gray-300"
|
|
||||||
is-required
|
|
||||||
>
|
|
||||||
Nama
|
|
||||||
</Label>
|
|
||||||
<InputBase
|
|
||||||
label=""
|
|
||||||
:field-name="`contacts[${idx}].name`"
|
|
||||||
placeholder="Masukkan Nama"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="space-y-2">
|
|
||||||
<Label
|
|
||||||
class="text-sm font-medium text-gray-700 dark:text-gray-300"
|
|
||||||
is-required
|
|
||||||
>
|
|
||||||
Alamat
|
|
||||||
</Label>
|
|
||||||
<InputBase
|
|
||||||
:field-name="`contacts[${idx}].address`"
|
|
||||||
label=""
|
|
||||||
placeholder="Masukkan Alamat"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="space-y-2">
|
|
||||||
<Label
|
|
||||||
class="text-sm font-medium text-gray-700 dark:text-gray-300"
|
|
||||||
is-required
|
|
||||||
>
|
|
||||||
Nomor HP
|
|
||||||
</Label>
|
|
||||||
<InputBase
|
|
||||||
:field-name="`contacts[${idx}].phone`"
|
|
||||||
label=""
|
|
||||||
placeholder="081234567890"
|
|
||||||
:max-length="15"
|
|
||||||
numeric-only
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
<ButtonAction
|
||||||
type="button"
|
preset="add"
|
||||||
variant="outline"
|
label="Tambah Obat"
|
||||||
class="w-full rounded-md border border-primary bg-white px-4 py-2 text-primary hover:border-primary hover:bg-primary hover:text-white sm:w-auto sm:text-sm"
|
title="Tambah Kontak ke Daftar Kontak"
|
||||||
@click="push({ relation: '', name: '', address: '', phone: '' })"
|
:disabled="fields.length >= 5 || isReadonly"
|
||||||
>
|
:full-width-mobile="true"
|
||||||
<Icon
|
class="mt-4"
|
||||||
name="i-lucide-plus"
|
@click="push({ name: '', dose: '', unit: '' })"
|
||||||
class="mr-2 h-4 w-4 align-middle transition-colors"
|
/>
|
||||||
/>
|
|
||||||
Tambah Penanggung Jawab
|
|
||||||
</Button>
|
|
||||||
</FieldArray>
|
</FieldArray>
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -2,23 +2,25 @@
|
|||||||
import { cn } from '~/lib/utils'
|
import { cn } from '~/lib/utils'
|
||||||
import { relationshipCodes } from '~/lib/constants'
|
import { relationshipCodes } from '~/lib/constants'
|
||||||
|
|
||||||
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue'
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
import Field from '~/components/pub/my-ui/form/field.vue'
|
|
||||||
import Select from '~/components/pub/my-ui/form/select.vue'
|
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
fieldName: string
|
fieldName: string
|
||||||
|
label?: string
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
class?: string
|
class?: string
|
||||||
selectClass?: string
|
selectClass?: string
|
||||||
fieldGroupClass?: string
|
fieldGroupClass?: string
|
||||||
labelClass?: string
|
labelClass?: string
|
||||||
isDisabled?: boolean
|
isDisabled?: boolean
|
||||||
|
isRequired?: boolean
|
||||||
|
colSpan?: number
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { fieldName = 'phoneNumber', class: containerClass, selectClass, fieldGroupClass } = props
|
const { class: containerClass, selectClass, fieldGroupClass } = props
|
||||||
|
|
||||||
const emergencyContactOptions = Object.entries(relationshipCodes).map(([value, label]) => ({
|
const opts = Object.entries(relationshipCodes).map(([value, label]) => ({
|
||||||
label,
|
label,
|
||||||
value,
|
value,
|
||||||
...(value === 'other' && { priority: -1 }),
|
...(value === 'other' && { priority: -1 }),
|
||||||
@@ -26,8 +28,18 @@ const emergencyContactOptions = Object.entries(relationshipCodes).map(([value, l
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FieldGroup :class="cn('select-field-group', fieldGroupClass, containerClass)">
|
<DE.Cell
|
||||||
<Field
|
:col-span="colSpan"
|
||||||
|
:class="cn('select-field-group', fieldGroupClass, containerClass)"
|
||||||
|
>
|
||||||
|
<DE.Label
|
||||||
|
:label-for="fieldName"
|
||||||
|
:class="cn('select-field-label', labelClass)"
|
||||||
|
:is-required="isRequired"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</DE.Label>
|
||||||
|
<DE.Field
|
||||||
:id="fieldName"
|
:id="fieldName"
|
||||||
:class="cn('select-field-wrapper')"
|
:class="cn('select-field-wrapper')"
|
||||||
>
|
>
|
||||||
@@ -38,10 +50,10 @@ const emergencyContactOptions = Object.entries(relationshipCodes).map(([value, l
|
|||||||
<FormItem>
|
<FormItem>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Select
|
<Select
|
||||||
|
:disabled="isDisabled"
|
||||||
:id="fieldName"
|
:id="fieldName"
|
||||||
v-bind="componentField"
|
v-bind="componentField"
|
||||||
:is-disabled="isDisabled"
|
:items="opts"
|
||||||
:items="emergencyContactOptions"
|
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:preserve-order="true"
|
:preserve-order="true"
|
||||||
:class="
|
:class="
|
||||||
@@ -55,6 +67,6 @@ const emergencyContactOptions = Object.entries(relationshipCodes).map(([value, l
|
|||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
</Field>
|
</DE.Field>
|
||||||
</FieldGroup>
|
</DE.Cell>
|
||||||
</template>
|
</template>
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Button } from '~/components/pub/ui/button'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button Action Component untuk form
|
||||||
|
* Support preset: add, delete, save, cancel
|
||||||
|
*/
|
||||||
|
interface Props {
|
||||||
|
/**
|
||||||
|
* Preset button type dengan styling bawaan
|
||||||
|
* - add: Button tambah (outline primary)
|
||||||
|
* - delete: Button hapus (ghost)
|
||||||
|
* - save: Button simpan (primary)
|
||||||
|
* - cancel: Button batal (secondary)
|
||||||
|
* - custom: Custom styling
|
||||||
|
*/
|
||||||
|
preset?: 'add' | 'delete' | 'save' | 'cancel' | 'custom'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icon name (UnoCSS/Lucide)
|
||||||
|
* Default akan diset berdasarkan preset
|
||||||
|
*/
|
||||||
|
icon?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button text
|
||||||
|
* Set ke empty string ('') untuk icon-only mode
|
||||||
|
*/
|
||||||
|
label?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button title (tooltip)
|
||||||
|
* Wajib untuk icon-only buttons (accessibility)
|
||||||
|
*/
|
||||||
|
title?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icon only mode (no label)
|
||||||
|
* Otomatis true jika label kosong
|
||||||
|
*/
|
||||||
|
iconOnly?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button type
|
||||||
|
*/
|
||||||
|
type?: 'button' | 'submit' | 'reset'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disabled state
|
||||||
|
*/
|
||||||
|
disabled?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom class untuk override styling
|
||||||
|
*/
|
||||||
|
class?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responsive width (full width on mobile)
|
||||||
|
*/
|
||||||
|
fullWidthMobile?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button size
|
||||||
|
*/
|
||||||
|
size?: 'default' | 'sm' | 'lg' | 'icon'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button variant (override preset variant)
|
||||||
|
*/
|
||||||
|
variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link'
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
preset: 'custom',
|
||||||
|
type: 'button',
|
||||||
|
disabled: false,
|
||||||
|
fullWidthMobile: false,
|
||||||
|
iconOnly: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Preset configurations
|
||||||
|
const presetConfig = {
|
||||||
|
add: {
|
||||||
|
variant: 'outline' as const,
|
||||||
|
icon: 'i-lucide-plus',
|
||||||
|
label: 'Tambah',
|
||||||
|
classes:
|
||||||
|
'border-primary bg-white text-primary hover:bg-primary hover:text-white dark:bg-slate-800 dark:border-primary dark:text-primary dark:hover:bg-primary dark:hover:text-white',
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
variant: 'ghost' as const,
|
||||||
|
icon: 'i-lucide-trash-2',
|
||||||
|
label: '', // Default kosong untuk icon-only
|
||||||
|
classes:
|
||||||
|
'hover:bg-destructive hover:text-white hover:border-destructive dark:hover:bg-destructive dark:hover:text-white',
|
||||||
|
},
|
||||||
|
save: {
|
||||||
|
variant: 'default' as const,
|
||||||
|
icon: 'i-lucide-save',
|
||||||
|
label: 'Simpan',
|
||||||
|
classes: 'bg-primary text-primary-foreground hover:bg-primary/90 dark:bg-primary dark:hover:bg-primary/90',
|
||||||
|
},
|
||||||
|
cancel: {
|
||||||
|
variant: 'secondary' as const,
|
||||||
|
icon: 'i-lucide-x',
|
||||||
|
label: 'Batal',
|
||||||
|
classes: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 dark:bg-slate-700 dark:hover:bg-slate-600',
|
||||||
|
},
|
||||||
|
custom: {
|
||||||
|
variant: 'default' as const,
|
||||||
|
icon: '',
|
||||||
|
label: '',
|
||||||
|
classes: '',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentPreset = computed(() => presetConfig[props.preset])
|
||||||
|
const buttonVariant = computed(() => props.variant || currentPreset.value.variant)
|
||||||
|
const buttonIcon = computed(() => props.icon || currentPreset.value.icon)
|
||||||
|
|
||||||
|
// Label handling: gunakan prop label jika ada, fallback ke preset, atau undefined jika iconOnly
|
||||||
|
const buttonLabel = computed(() => {
|
||||||
|
if (props.label !== undefined) return props.label
|
||||||
|
return currentPreset.value.label
|
||||||
|
})
|
||||||
|
|
||||||
|
const buttonTitle = computed(() => props.title || buttonLabel.value)
|
||||||
|
|
||||||
|
// Deteksi icon-only mode
|
||||||
|
const isIconOnly = computed(() => {
|
||||||
|
return props.iconOnly || buttonLabel.value === '' || !buttonLabel.value
|
||||||
|
})
|
||||||
|
|
||||||
|
const buttonClasses = computed(() => {
|
||||||
|
// Base classes berbeda untuk icon-only vs with-label
|
||||||
|
const baseClasses = isIconOnly.value
|
||||||
|
? 'rounded-md p-2 w-9 h-9 transition-colors flex items-center justify-center' // Icon only: square button
|
||||||
|
: 'rounded-md px-4 py-2 transition-colors sm:text-sm' // With label: padding horizontal lebih besar
|
||||||
|
|
||||||
|
const widthClasses = props.fullWidthMobile && !isIconOnly.value ? 'w-full sm:w-auto' : ''
|
||||||
|
const presetClasses = currentPreset.value.classes
|
||||||
|
|
||||||
|
return cn(baseClasses, widthClasses, presetClasses, props.class)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Button
|
||||||
|
:title="buttonTitle"
|
||||||
|
:type="type"
|
||||||
|
:variant="buttonVariant"
|
||||||
|
:size="size"
|
||||||
|
:disabled="disabled"
|
||||||
|
:class="buttonClasses"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
v-if="buttonIcon"
|
||||||
|
:name="buttonIcon"
|
||||||
|
:class="cn('h-4 w-4 align-middle transition-colors', !isIconOnly ? 'mr-2' : '')"
|
||||||
|
/>
|
||||||
|
<slot v-if="!isIconOnly">{{ buttonLabel }}</slot>
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FormErrors } from '~/types/error'
|
import type { FormErrors } from '~/types/error'
|
||||||
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue'
|
|
||||||
import Field from '~/components/pub/my-ui/form/field.vue'
|
|
||||||
import Label from '~/components/pub/my-ui/form/label.vue'
|
|
||||||
import { Input } from '~/components/pub/ui/input'
|
import { Input } from '~/components/pub/ui/input'
|
||||||
import { cn } from '~/lib/utils'
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
@@ -11,7 +8,7 @@ import * as DE from '~/components/pub/my-ui/doc-entry'
|
|||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
fieldName: string
|
fieldName: string
|
||||||
placeholder: string
|
placeholder: string
|
||||||
label: string
|
label?: string
|
||||||
errors?: FormErrors
|
errors?: FormErrors
|
||||||
class?: string
|
class?: string
|
||||||
colSpan?: number
|
colSpan?: number
|
||||||
@@ -21,6 +18,8 @@ const props = defineProps<{
|
|||||||
isDisabled?: boolean
|
isDisabled?: boolean
|
||||||
rightLabel?: string
|
rightLabel?: string
|
||||||
bottomLabel?: string
|
bottomLabel?: string
|
||||||
|
suffixMsg?: string
|
||||||
|
iconName?: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
function handleInput(event: Event) {
|
function handleInput(event: Event) {
|
||||||
@@ -49,7 +48,7 @@ function handleInput(event: Event) {
|
|||||||
<template>
|
<template>
|
||||||
<DE.Cell :col-span="colSpan || 1">
|
<DE.Cell :col-span="colSpan || 1">
|
||||||
<DE.Label
|
<DE.Label
|
||||||
v-if="label !== ''"
|
v-if="label"
|
||||||
:label-for="fieldName"
|
:label-for="fieldName"
|
||||||
:is-required="isRequired && !isDisabled"
|
:is-required="isRequired && !isDisabled"
|
||||||
>
|
>
|
||||||
@@ -63,27 +62,50 @@ function handleInput(event: Event) {
|
|||||||
v-slot="{ componentField }"
|
v-slot="{ componentField }"
|
||||||
:name="fieldName"
|
:name="fieldName"
|
||||||
>
|
>
|
||||||
<FormItem :class="cn(`relative`,)">
|
<FormItem>
|
||||||
<FormControl>
|
<FormControl class="relative">
|
||||||
<Input
|
<div class="relative w-full max-w-sm items-center">
|
||||||
:disabled="isDisabled"
|
<Input
|
||||||
v-bind="componentField"
|
:disabled="isDisabled"
|
||||||
:placeholder="placeholder"
|
v-bind="componentField"
|
||||||
:maxlength="maxLength"
|
:placeholder="placeholder"
|
||||||
:class="cn('focus:border-primary focus:ring-2 focus:ring-primary focus:ring-offset-0', props.class)"
|
:maxlength="maxLength"
|
||||||
autocomplete="off"
|
:class="cn('focus:border-primary focus:ring-2 focus:ring-primary focus:ring-offset-0')"
|
||||||
aria-autocomplete="none"
|
autocomplete="off"
|
||||||
autocorrect="off"
|
aria-autocomplete="none"
|
||||||
autocapitalize="off"
|
autocorrect="off"
|
||||||
spellcheck="false"
|
autocapitalize="off"
|
||||||
@input="handleInput"
|
spellcheck="false"
|
||||||
/>
|
@input="handleInput"
|
||||||
<p v-show="rightLabel" class="text-gray-400 absolute top-0 right-3">{{ rightLabel }}</p>
|
/>
|
||||||
|
<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>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
</DE.Field>
|
</DE.Field>
|
||||||
<p v-show="bottomLabel" class="text-gray-400 mt-1">{{ bottomLabel }}</p>
|
<p
|
||||||
|
v-show="bottomLabel"
|
||||||
|
class="text-gray-400"
|
||||||
|
>
|
||||||
|
{{ bottomLabel }}
|
||||||
|
</p>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
</template>
|
</template>
|
||||||
Reference in New Issue
Block a user