LoginStep2Panel.vue 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. <template>
  2. <q-card-section class="code-step full-width no-padding q-mb-xl">
  3. <div class="text-text q-mb-xs text-left">
  4. <span class="font14 fontbold">{{ $t("auth.validation_code") }}</span>
  5. </div>
  6. <div class="code-boxes-wrapper" @click="focusCodeInput">
  7. <div
  8. v-for="i in 6"
  9. :key="i"
  10. :class="{ 'code-box--active': code.length === i - 1 }"
  11. class="code-box"
  12. >
  13. <span v-if="code[i - 1]" class="text-text">{{ code[i - 1] }}</span>
  14. <span v-else class="text-text-light text-h6">{{ placeholder }}</span>
  15. </div>
  16. </div>
  17. <q-input
  18. ref="codeInputRef"
  19. v-model="code"
  20. class="code-hidden-input"
  21. hide-bottom-space
  22. inputmode="numeric"
  23. lazy-rules
  24. mask="######"
  25. no-error-icon
  26. :error="!!serverErrors.code"
  27. :error-message="serverErrors.code"
  28. :rules="[inputRules.required]"
  29. @update:model-value="clearServerError('code')"
  30. />
  31. </q-card-section>
  32. </template>
  33. <script setup>
  34. import { inject, ref } from "vue";
  35. import { useInputRules } from "src/composables/useInputRules";
  36. const code = defineModel("code", {
  37. required: true,
  38. type: String,
  39. });
  40. defineProps({
  41. serverErrors: {
  42. type: Object,
  43. default: () => ({}),
  44. },
  45. });
  46. const clearServerError = inject("clearServerError", () => {});
  47. const { inputRules } = useInputRules();
  48. const codeInputRef = ref(null);
  49. const placeholder = "x";
  50. const focusCodeInput = () => {
  51. codeInputRef.value?.$el.querySelector("input")?.focus();
  52. };
  53. </script>
  54. <style lang="scss" scoped>
  55. .code-boxes-wrapper {
  56. cursor: text;
  57. display: grid;
  58. gap: 6px;
  59. grid-template-columns: repeat(6, minmax(0, 1fr));
  60. margin: 0 auto;
  61. max-width: 292px;
  62. width: 100%;
  63. .code-box {
  64. align-items: center;
  65. background-color: #f9fafb;
  66. border: 2px solid #e4e4e4;
  67. border-radius: 8px;
  68. color: var(--q-text);
  69. display: flex;
  70. aspect-ratio: 1;
  71. height: auto;
  72. justify-content: center;
  73. min-width: 0;
  74. transition: border-color 0.2s ease;
  75. width: 100%;
  76. &--active {
  77. border-color: var(--q-primary);
  78. }
  79. }
  80. }
  81. .code-step {
  82. max-width: 292px;
  83. }
  84. .code-hidden-input {
  85. height: 1px;
  86. left: -9999px;
  87. opacity: 0;
  88. overflow: hidden;
  89. pointer-events: none;
  90. position: fixed;
  91. top: 0;
  92. width: 1px;
  93. :deep(.q-field__control),
  94. :deep(.q-field__bottom) {
  95. height: 1px;
  96. min-height: 1px;
  97. }
  98. }
  99. </style>