mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-14 20:19:34 +01:00
94 lines
2.6 KiB
Vue
94 lines
2.6 KiB
Vue
<script setup lang="ts">
|
|
const show = ref(false)
|
|
const password = ref('')
|
|
|
|
function checkStrength(str: string) {
|
|
const requirements = [
|
|
{ regex: /.{8,}/, text: 'At least 8 characters' },
|
|
{ regex: /\d/, text: 'At least 1 number' },
|
|
{ regex: /[a-z]/, text: 'At least 1 lowercase letter' },
|
|
{ regex: /[A-Z]/, text: 'At least 1 uppercase letter' }
|
|
]
|
|
|
|
return requirements.map(req => ({ met: req.regex.test(str), text: req.text }))
|
|
}
|
|
|
|
const strength = computed(() => checkStrength(password.value))
|
|
const score = computed(() => strength.value.filter(req => req.met).length)
|
|
|
|
const color = computed(() => {
|
|
if (score.value === 0) return 'neutral'
|
|
if (score.value <= 1) return 'error'
|
|
if (score.value <= 2) return 'warning'
|
|
if (score.value === 3) return 'warning'
|
|
return 'success'
|
|
})
|
|
|
|
const text = computed(() => {
|
|
if (score.value === 0) return 'Enter a password'
|
|
if (score.value <= 2) return 'Weak password'
|
|
if (score.value === 3) return 'Medium password'
|
|
return 'Strong password'
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="space-y-2">
|
|
<UFormField label="Password">
|
|
<UInput
|
|
v-model="password"
|
|
placeholder="Password"
|
|
:color="color"
|
|
:type="show ? 'text' : 'password'"
|
|
:ui="{ trailing: 'pr-0.5' }"
|
|
:aria-invalid="score < 4"
|
|
aria-describedby="password-strength"
|
|
class="w-full"
|
|
>
|
|
<template #trailing>
|
|
<UButton
|
|
color="neutral"
|
|
variant="link"
|
|
size="sm"
|
|
:icon="show ? 'i-lucide-eye-off' : 'i-lucide-eye'"
|
|
aria-label="show ? 'Hide password' : 'Show password'"
|
|
:aria-pressed="show"
|
|
aria-controls="password"
|
|
@click="show = !show"
|
|
/>
|
|
</template>
|
|
</UInput>
|
|
</UFormField>
|
|
|
|
<UProgress
|
|
:color="color"
|
|
:indicator="text"
|
|
:model-value="score"
|
|
:max="4"
|
|
size="sm"
|
|
/>
|
|
|
|
<p id="password-strength" class="text-sm font-medium">
|
|
{{ text }}. Must contain:
|
|
</p>
|
|
|
|
<ul class="space-y-1" aria-label="Password requirements">
|
|
<li
|
|
v-for="(req, index) in strength"
|
|
:key="index"
|
|
class="flex items-center gap-0.5"
|
|
:class="req.met ? 'text-[var(--ui-success)]' : 'text-[var(--ui-text-muted)]'"
|
|
>
|
|
<UIcon :name="req.met ? 'i-lucide-circle-check' : 'i-lucide-circle-x'" class="size-4 shrink-0" />
|
|
|
|
<span class="text-xs font-light">
|
|
{{ req.text }}
|
|
<span class="sr-only">
|
|
{{ req.met ? ' - Requirement met' : ' - Requirement not met' }}
|
|
</span>
|
|
</span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</template>
|