feat(header): replace search icon with AHeaderSearch component for improved UX

This commit is contained in:
Matej Glasnák committed 2025-11-21 14:44:04 +01:00
1 parent ff1b773167
commit 3cd74cd304
2 files changed
+169 -1

No files matched your search

@@ -0,0 +1,154 @@
<template>
<div class="header-search" :class="{ 'header-search--focused': isFocused }">
<img :src="require('@/plugins/app/_img/icons/search.svg')" />
<input
id="header-search-input"
v-model="searchValue"
type="text"
placeholder="Vyhľadávať"
@focus="handleFocus"
@blur="handleBlur"
@click="navigateToSearch"
@keydown.enter="handleSubmit"
/>
<v-btn class="header-search__close" @mousedown.prevent="handleClose">
<v-icon icon="mdi-close" size="large" />
</v-btn>
</div>
</template>
<script>
export default {
name: 'AHeaderSearch',
props: {
modelValue: {
type: String,
default: '',
},
isFocused: {
type: Boolean,
default: false,
},
},
emits: ['update:modelValue', 'focus', 'blur', 'submit', 'close'],
computed: {
searchValue: {
get() {
return this.modelValue;
},
set(value) {
this.$emit('update:modelValue', value);
},
},
},
methods: {
handleFocus() {
this.$emit('focus');
},
handleBlur() {
this.$emit('blur');
},
handleClose() {
this.$emit('close');
},
navigateToSearch() {
// If already on search page, just ensure focus
if (this.$route.name === 'Search') {
this.$emit('focus');
return;
}
this.$router.push({ name: 'Search' });
},
handleSubmit() {
this.$emit('submit', this.searchValue);
},
},
};
</script>
<style lang="sass" scoped>
$breakpoint-sm: 991px
.header-search
position: relative
display: flex
align-items: center
border: 1px solid #e0e0e0
border-radius: 6px
transition: all 0.3s ease-in-out
width: auto
flex-shrink: 1
flex-grow: 1
min-width: 0
max-width: 340px
@media (max-width: $breakpoint-sm)
flex-grow: 1
width: 100%
max-width: 100%
&--focused
@media (max-width: $breakpoint-sm)
flex-grow: 999
flex-shrink: 0
width: 100%
max-width: 100%
.header-search__close
opacity: 0.8
pointer-events: auto
&:hover
opacity: 1
input
outline: none
padding: 12px 12px 12px 43px
border-right: 6px
border-left: 6px
width: 100%
min-width: 0
border: none
border-radius: 6px
img
padding: 6px
position: absolute
height: 31px
left: 8px
top: 50%
transform: translateY(-50%)
bottom: 0
user-select: none
pointer-events: none
&__close
position: absolute
opacity: 0
right: 8px
top: 50%
transform: translateY(-50%)
cursor: pointer
user-select: none
pointer-events: none
transition: opacity 0.3s ease-in-out
color: var(--color-primary)
font-size: 14px
padding: 0
--v-btn-height: 34px
--v-btn-min-height: 34px
width: 34px
min-width: 34px
border-radius: 100%
</style>
@@ -9,7 +9,7 @@
<h1>{{ $route.meta.title }}</h1> <h1>{{ $route.meta.title }}</h1>
<div class="d-flex justify-end align-center w-100 header-sm"> <div class="d-flex justify-end align-center w-100 header-sm">
<img <!-- <img
class="search-icon" class="search-icon"
:src="require('@/plugins/app/_img/icons/search.svg')" :src="require('@/plugins/app/_img/icons/search.svg')"
alt="search-icon" alt="search-icon"
@@ -21,7 +21,16 @@
placeholder="Search" placeholder="Search"
@click="$router.push({ name: 'Search' })" @click="$router.push({ name: 'Search' })"
@keydown.enter="$events.emit('onSearch', search)" @keydown.enter="$events.emit('onSearch', search)"
/> -->
<div style="margin-right: 16px">
<A-header-search
v-model="search"
@focus="$router.push({ name: 'Search' })"
@blur="() => ({})"
@submit="$events.emit('onSearch', search)"
@close="() => ({})"
/> />
</div>
<v-menu> <v-menu>
<template v-slot:activator="{ props }"> <template v-slot:activator="{ props }">
@@ -97,6 +106,7 @@
<script> <script>
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import AHeaderSearch from './a-header-search.vue';
export default { export default {
data() { data() {
@@ -109,6 +119,10 @@ export default {
...mapGetters('auth', ['user']), ...mapGetters('auth', ['user']),
}, },
components: {
AHeaderSearch,
},
methods: { methods: {
async logOut() { async logOut() {
try { try {