changed component folders
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
// types
|
||||
|
||||
type Props = {
|
||||
status?: "success" | "error" | "idle";
|
||||
modelValue: never[];
|
||||
autofocus?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
// props
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
status: "idle"
|
||||
});
|
||||
const { modelValue, disabled, status } = toRefs(props);
|
||||
|
||||
// state
|
||||
|
||||
const { $gsap: gsap } = useNuxtApp();
|
||||
|
||||
// emit
|
||||
|
||||
const emit = defineEmits(["complete", "update:modelValue"]);
|
||||
|
||||
// state
|
||||
|
||||
const currentOtpCode = ref([]);
|
||||
|
||||
// methods
|
||||
|
||||
const handleChange = () => {
|
||||
emit("update:modelValue", currentOtpCode.value);
|
||||
};
|
||||
|
||||
const handleComplete = () => {
|
||||
emit("update:modelValue", currentOtpCode.value);
|
||||
emit("complete", currentOtpCode.value);
|
||||
};
|
||||
|
||||
const playStatusAnimation = () => {
|
||||
|
||||
const inputCount = 6;
|
||||
const duration = 0.100;
|
||||
|
||||
let statusColor = {
|
||||
border : "",
|
||||
bg : ""
|
||||
};
|
||||
|
||||
if (status.value === "success") {
|
||||
statusColor.border = "var(--color-success-500)";
|
||||
statusColor.bg = "var(--color-success-50)";
|
||||
} else if (status.value === "error") {
|
||||
statusColor.border = "var(--color-danger-500)";
|
||||
statusColor.bg = "var(--color-danger-50)";
|
||||
}
|
||||
|
||||
let index = 0;
|
||||
const animate = (index: number) => {
|
||||
setTimeout(() => {
|
||||
gsap.to(`#otp-input-${index}`, {
|
||||
borderColor: statusColor.border,
|
||||
backgroundColor: statusColor.bg,
|
||||
scale: 1.2,
|
||||
duration: duration / 2
|
||||
});
|
||||
|
||||
gsap.to(`#otp-input-${index}`, {
|
||||
scale: 1,
|
||||
duration: duration / 2,
|
||||
delay: duration
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
gsap.to(`#otp-input-${index}`, {
|
||||
borderColor: "black",
|
||||
backgroundColor: "var(--color-slate-50)"
|
||||
});
|
||||
}, (inputCount + 1) * duration * 3000);
|
||||
|
||||
}, index * duration * 500);
|
||||
};
|
||||
|
||||
while (index < 6) {
|
||||
animate(index);
|
||||
index++;
|
||||
}
|
||||
};
|
||||
|
||||
// watch
|
||||
|
||||
watch(() => modelValue.value, (value) => {
|
||||
currentOtpCode.value = value;
|
||||
});
|
||||
|
||||
watch(() => disabled.value, (value) => {
|
||||
if (!value) {
|
||||
const otpInputFirst = document.querySelector("#otp-input-0") as HTMLInputElement;
|
||||
setTimeout(() => {
|
||||
otpInputFirst.focus();
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
watch(() => status.value, (value) => {
|
||||
if (value !== "idle") {
|
||||
playStatusAnimation();
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<PinInputRoot
|
||||
:disabled="disabled"
|
||||
v-bind="$attrs"
|
||||
type="number"
|
||||
v-model="currentOtpCode"
|
||||
placeholder="_"
|
||||
class="flex gap-4 items-center justify-center mt-1"
|
||||
@change="handleChange"
|
||||
@complete="handleComplete"
|
||||
otp
|
||||
>
|
||||
<PinInputInput
|
||||
v-for="(id, index) in 6"
|
||||
:id="`otp-input-${index}`"
|
||||
:key="id"
|
||||
:index="index"
|
||||
:autofocus="autofocus ? index === 0 ? true : 'off' : 'off'"
|
||||
class="disabled:text-slate-400 focus-within:border-black transition-all size-16 bg-slate-50 typo-label-lg rounded-lg text-center border-[1.5px] border-slate-200 outline-none"
|
||||
/>
|
||||
</PinInputRoot>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user