<script setup lang="ts"> import {getCurrentInstance, onMounted, ref} from "vue"; import {type Constraint, forConstraint} from "@/shared/validation/validator"; interface Props { modelValue?: string; label: string; type?: string; placeholder: string; constraint: Constraint; validationError: string; } const props = defineProps<Props>(); const emit = defineEmits<{ (e: "update:modelValue", value: string): void; }>(); const input = ref<HTMLInputElement>(); const valid = ref(true); const validated = ref(false); function registerValidationComponent() { const instance = getCurrentInstance(); let parent = instance?.parent; while (parent) { if (parent.exposed?.registerValidationComponent) { parent.exposed.registerValidationComponent(instance); return; } parent = parent.parent; } throw new Error("Could not find matching ValidationForm for ValidationFormInpunt."); } function onInput() { if (validated.value) { validate(); } const element = input.value; if (!element) { console.warn("Could not get referenced input element.") return; } emit("update:modelValue", element.value); } function validate(): boolean { const element = input.value; if (!element) { console.warn("Could not get referenced input element.") return false; } valid.value = forConstraint(props.constraint, false)(element.value); validated.value = true; return valid.value; } defineExpose({ validate }); onMounted(() => { registerValidationComponent(); }); </script> <template> <div> <label> {{label}}: <input ref="input" :value="modelValue" @input="onInput" :type="type || 'text'" :placeholder="placeholder" /> </label> <div class="validation-error" v-if="!valid"> {{validationError}} </div> </div> </template> <style scoped lang="scss"> @import "../../scss/variables"; label { display: block; font-weight: $label-font-weight; cursor: pointer; } input { box-sizing: border-box; width: 100%; margin: 0.25em 0; } .validation-error { color: $variant-color-danger; margin: 0.25em 0; } </style>