90 lines
2.9 KiB
Vue
90 lines
2.9 KiB
Vue
<script setup lang="ts">
|
|
import InputError from '@/components/InputError.vue';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Label } from '@/components/ui/label';
|
|
|
|
withDefaults(
|
|
defineProps<{
|
|
id: string;
|
|
label: string;
|
|
kind?: 'text' | 'number' | 'textarea' | 'select';
|
|
options?: { value: string | number; label: string }[];
|
|
error?: string;
|
|
hint?: string;
|
|
disabled?: boolean;
|
|
required?: boolean;
|
|
autofocus?: boolean;
|
|
placeholder?: string;
|
|
rows?: number;
|
|
min?: number;
|
|
step?: string;
|
|
}>(),
|
|
{ kind: 'text', rows: 5 },
|
|
);
|
|
|
|
const model = defineModel<string | number | null>({ required: true });
|
|
const controlClass =
|
|
'min-h-control border-input bg-card focus-visible:border-ring focus-visible:ring-ring/35 w-full min-w-0 rounded-md border px-3 py-2 text-base shadow-xs outline-none transition-[border-color,box-shadow] hover:border-primary/45 focus-visible:ring-2 disabled:cursor-not-allowed disabled:bg-muted disabled:opacity-60 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/25 md:text-body';
|
|
</script>
|
|
|
|
<template>
|
|
<div class="grid min-w-0 content-start gap-2">
|
|
<Label :for="id">{{ label }}</Label>
|
|
<select
|
|
v-if="kind === 'select'"
|
|
:id="id"
|
|
v-model="model"
|
|
:name="id"
|
|
:disabled="disabled"
|
|
:required="required"
|
|
:aria-invalid="Boolean(error)"
|
|
:aria-describedby="`${id}-hint ${id}-error`"
|
|
:class="controlClass"
|
|
>
|
|
<option
|
|
v-for="option in options"
|
|
:key="option.value"
|
|
:value="option.value"
|
|
>
|
|
{{ option.label }}
|
|
</option>
|
|
</select>
|
|
<textarea
|
|
v-else-if="kind === 'textarea'"
|
|
:id="id"
|
|
v-model="model"
|
|
:name="id"
|
|
:rows="rows"
|
|
:disabled="disabled"
|
|
:required="required"
|
|
:placeholder="placeholder"
|
|
:aria-invalid="Boolean(error)"
|
|
:aria-describedby="`${id}-hint ${id}-error`"
|
|
:class="controlClass"
|
|
/>
|
|
<Input
|
|
v-else
|
|
:id="id"
|
|
v-model="model"
|
|
:name="id"
|
|
:type="kind"
|
|
:disabled="disabled"
|
|
:required="required"
|
|
:autofocus="autofocus"
|
|
:placeholder="placeholder"
|
|
:min="min"
|
|
:step="step"
|
|
:maxlength="kind === 'text' ? 255 : undefined"
|
|
:invalid="Boolean(error)"
|
|
:aria-describedby="`${id}-hint ${id}-error`"
|
|
/>
|
|
<p
|
|
:id="`${id}-hint`"
|
|
class="text-muted-foreground text-xs empty:hidden"
|
|
>
|
|
{{ hint }}
|
|
</p>
|
|
<InputError :id="`${id}-error`" :message="error" />
|
|
</div>
|
|
</template>
|