Files
seasoned/src/components/ui/SeasonedInput.vue

141 lines
3.3 KiB
Vue

<template>
<div class="group" :class="{ completed: modelValue, focus }">
<component :is="inputIcon" v-if="inputIcon" />
<!-- eslint-disable-next-line vuejs-accessibility/form-control-has-label -->
<input
class="input"
:type="toggledType || type || 'text'"
:placeholder="placeholder"
:value="modelValue"
@input="handleInput"
@keyup.enter="event => emit('enter', event)"
@focus="focus = true"
@blur="focus = false"
/>
<i
v-if="modelValue && type === 'password'"
class="show noselect"
tabindex="0"
@click="toggleShowPassword"
@keydown.enter="toggleShowPassword"
>{{ toggledType == "password" ? "show" : "hide" }}</i
>
</div>
</template>
<script setup lang="ts">
import { ref, computed, defineProps, defineEmits } from "vue";
import IconKey from "@/icons/IconKey.vue";
import IconEmail from "@/icons/IconEmail.vue";
import IconBinoculars from "@/icons/IconBinoculars.vue";
import type { Ref } from "vue";
interface Props {
modelValue: string;
placeholder: string;
type?: string;
}
interface Emit {
(e: "change");
(e: "enter", event?: KeyboardEvent);
(e: "update:modelValue", value: string);
}
const props = defineProps<Props>();
const emit = defineEmits<Emit>();
const toggledType: Ref<string> = ref(props.type);
const focus: Ref<boolean> = ref(false);
const inputIcon = computed(() => {
if (props.type === "password") return IconKey;
if (props.type === "email") return IconEmail;
if (props.type === "torrents") return IconBinoculars;
return false;
});
function handleInput(event: KeyboardEvent) {
const target = event?.target as HTMLInputElement;
if (!target) return;
emit("update:modelValue", target?.value);
}
// Could we move this to component that injects ??
function toggleShowPassword() {
if (toggledType.value === "text") {
toggledType.value = "password";
} else {
toggledType.value = "text";
}
}
</script>
<style lang="scss" scoped>
@import "src/scss/variables";
@import "src/scss/media-queries";
.group {
display: flex;
width: 100%;
position: relative;
max-width: 35rem;
border: 1px solid var(--text-color-50);
background-color: var(--background-color-secondary);
&.completed,
&.focus,
&:hover,
&:focus {
border-color: var(--text-color);
svg {
fill: var(--text-color);
}
}
svg {
width: 24px;
height: 24px;
fill: var(--text-color-50);
pointer-events: none;
margin-top: 10px;
margin-left: 10px;
z-index: 8;
}
input {
width: 100%;
padding: 10px;
outline: none;
background-color: var(--background-color-secondary);
color: var(--text-color);
font-weight: 100;
font-size: 1.2rem;
margin: 0;
z-index: 3;
border: none;
border-radius: 0;
-webkit-appearance: none;
}
.show {
position: absolute;
display: grid;
place-items: center;
right: 20px;
z-index: 11;
margin: auto 0;
height: 100%;
font-size: 0.9rem;
cursor: pointer;
color: var(--text-color-50);
-webkit-user-select: none;
user-select: none;
}
}
</style>