Files
ppeasy_vg12/resources/js/components/ui/PasswordInput.vue
T
2026-10-09 08:21:06 +02:00

55 lines
1.8 KiB
Vue

<script setup lang="ts">
import { ref } from 'vue';
import { useTranslations } from '@/composables/useVanguard';
withDefaults(
defineProps<{
invalid?: boolean;
/** Renders the reveal toggle. */
toggleable?: boolean;
}>(),
{ toggleable: true },
);
/**
* The root is a positioning wrapper, so attributes must be forwarded to the
* input itself. Without this, id, placeholder and autocomplete would land on
* the wrapping div and the field's <label for> would associate with nothing.
*/
defineOptions({ inheritAttrs: false });
const model = defineModel<string>();
const { __ } = useTranslations();
const revealed = ref(false);
</script>
<template>
<div class="relative">
<input
v-bind="$attrs"
v-model="model"
:type="revealed ? 'text' : 'password'"
:aria-invalid="invalid || undefined"
class="bg-field block w-full rounded-md border py-2 pl-3 text-slate-700 shadow-xs transition-colors placeholder:text-slate-400 focus:outline-none"
:class="[
toggleable ? 'pr-10' : 'pr-3',
invalid
? 'border-red-500 focus:border-red-500 focus:ring-2 focus:ring-red-100'
: 'border-slate-300 focus:border-primary-500 focus:ring-2 focus:ring-primary-100',
]"
/>
<button
v-if="toggleable"
type="button"
class="absolute inset-y-0 right-0 flex w-10 cursor-pointer items-center justify-center text-slate-400 hover:text-slate-600"
:aria-label="revealed ? __('Hide password') : __('Show password')"
:aria-pressed="revealed"
@click="revealed = !revealed"
>
<i :class="revealed ? 'fas fa-eye-slash' : 'fas fa-eye'" aria-hidden="true" />
</button>
</div>
</template>