refactor(search): entire new system to find and save summoner profiles

This commit is contained in:
Valentin Kaelin 2020-08-23 23:43:42 +02:00
parent d1681a94f2
commit 389b10d7b5
9 changed files with 500 additions and 216 deletions

View file

@ -10,6 +10,11 @@
background: rgba(23, 49, 79, .6);
}
.light-scrollbar::-webkit-scrollbar,
.light-scrollbar::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background-color: rgba(194, 217, 254, .6);
border-radius: 8px

View file

@ -1,10 +1,20 @@
<template>
<form @submit.prevent="formSubmit" :class="formClasses" class="flex w-full text-lg text-teal-100">
<form
@submit.prevent="formSubmit"
:class="{'max-w-lg': !homepage}"
class="flex self-start w-full h-full text-lg text-teal-100"
>
<div
v-if="open"
@click="open = false"
:style="{opacity: homepage ? 0 : 0.9}"
class="fixed inset-0 z-20 w-screen h-screen bg-gray-900"
></div>
<div class="relative w-full">
<button
v-if="homepage"
ref="submit"
:class="[btnClasses]"
class="absolute z-30 h-full hover:text-teal-200"
class="absolute right-0 z-40 w-12 h-full hover:text-teal-200"
type="submit"
>
<svg class="absolute w-4 h-4 vertical-center horizontal-center">
@ -12,80 +22,67 @@
</svg>
</button>
<input
v-if="homepage"
ref="input"
v-model="summoner"
@focus="selected = true"
:class="[inputClasses]"
class="w-full font-bold placeholder-teal-100 placeholder-opacity-75 rounded-none outline-none summoner-input"
@focus="open = true"
:class="dropdown ? 'bg-blue-1000' : 'input-color'"
class="relative z-30 w-full py-4 pl-6 pr-32 font-bold placeholder-teal-100 placeholder-opacity-75 rounded-lg outline-none focus:bg-blue-1000 summoner-input bypass-click"
spellcheck="false"
type="text"
placeholder="Search summoner"
/>
<button
v-if="!homepage"
@click="open = true"
class="w-full h-10 px-4 -mt-px text-base font-light text-left text-blue-200 rounded-md bg-blue-1000"
type="button"
>
<div class="flex items-center space-x-3">
<svg class="w-4 h-4">
<use xlink:href="#search" />
</svg>
<span>Search summoner (Press "/" to focus)</span>
</div>
</button>
<transition name="scale-fade">
<SearchFormDropdown v-if="selected" @click-dropdown="clickDropdown = true" />
<SearchFormDropdown
v-if="open"
v-model="summoner"
@close="open = false"
@toggle="dropdown = !dropdown"
:dropdown="dropdown"
:homepage="homepage"
/>
</transition>
<div ref="region-dropdown">
<div
:class="{'mr-12': size === 'xl'}"
class="absolute right-0 z-30 flex items-center h-full vertical-center"
>
<div
@click="dropdown = !dropdown"
:class="[selectRegionClasses]"
class="flex items-center transition-all border-2 border-transparent rounded cursor-pointer transition-fast ease-in-quad ease-out-quad hover:text-white"
>
<span class="font-bold uppercase select-none selected">{{ selectedRegion }}</span>
<svg class="w-4 h-4 ml-1 -mr-1">
<use xlink:href="#caret-down" />
</svg>
</div>
</div>
<transition name="scale-fade">
<div
v-show="dropdown"
:class="[dropdownClasses]"
class="absolute right-0 z-30 text-white shadow cursor-pointer"
>
<div
v-for="(region, index) in regions"
:key="region"
@click="updateSettings({name: 'region', value: region.toLowerCase()})"
:class="classRegions(index)"
class="relative py-1 pl-5 pr-2 text-xs text-right select-none bg-blue-1000 hover:bg-blue-800"
>
<svg
v-if="region.toLowerCase() === selectedRegion"
class="absolute w-3 h-3 fill-current vertical-center offsetIcon"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"
<SearchFormRegion
v-if="homepage"
@toggle="dropdown = !dropdown"
:dropdown="dropdown"
:homepage="homepage"
/>
</svg>
{{ region }}
</div>
</div>
</transition>
</div>
</div>
</form>
</template>
<script>
import { mapActions, mapState } from 'vuex'
import { mapState } from 'vuex'
import SearchFormDropdown from '@/components/Form/SearchFormDropdown.vue'
import SearchFormRegion from '@/components/Form/SearchFormRegion.vue'
export default {
components: {
SearchFormDropdown,
SearchFormRegion
},
props: {
size: {
type: String,
default: 'xl'
homepage: {
type: Boolean,
default: false
}
},
@ -93,117 +90,68 @@ export default {
return {
summoner: '',
dropdown: false,
regions: [
'BR',
'EUNE',
'EUW',
'JP',
'KR',
'LAN',
'LAS',
'NA',
'OCE',
'TR',
'RU'
],
clickDropdown: false,
selected: false,
open: false,
}
},
computed: {
btnClasses() {
return {
'left-0 w-8': this.size === 'small',
'right-0 w-12': this.size === 'xl'
}
},
formClasses() {
return {
'max-w-lg': this.size === 'small',
}
},
inputClasses() {
return {
'py-2 pl-12 pr-20 bg-transparent text-base border-b-2 border-blue-300 focus:border-white': this.size === 'small',
'py-4 pl-6 pr-32 focus:bg-blue-1000 rounded-lg': this.size === 'xl',
'input-color': !this.dropdown && this.size === 'xl',
'bg-blue-1000': this.dropdown && this.size === 'xl',
}
},
dropdownClasses() {
return {
'-mt-1 rounded': this.size === 'small',
'offsetDropDownXl rounded-b': this.size === 'xl'
}
},
selectRegionClasses() {
return {
'px-2 text-base rounded-md': this.size === 'small',
'px-2 py-1': this.size === 'xl',
'bg-blue-1000': this.dropdown && this.size === 'small',
'border-teal-200': this.dropdown && this.size === 'xl',
}
},
...mapState({
selectedRegion: state => state.settings.region
}),
},
watch: {
open(newVal) {
// Search Dropdown open
if (newVal) {
if (!this.homepage) {
document.body.style.marginLeft = '-8px'
}
document.body.style.overflow = 'hidden'
} else {
document.body.style.marginLeft = 0
document.body.style.overflow = 'auto'
}
},
$route() {
this.dropdown = false
this.open = false
}
},
created() {
if (!this.summoner.length) {
if (!this.summoner.length && !this.homepage) {
this.summoner = this.$route.params.name
}
window.addEventListener('mousedown', this.globalClick)
window.addEventListener('blur', this.windowBlur)
document.addEventListener('click', this.clickOutsideRegionDropdown)
document.addEventListener('keydown', this.handleEscape)
},
beforeDestroy() {
window.removeEventListener('mousedown', this.globalClick)
window.removeEventListener('blur', this.windowBlur)
document.removeEventListener('click', this.clickOutsideRegionDropdown)
document.removeEventListener('keydown', this.handleEscape)
},
methods: {
classRegions(index) {
return {
'rounded-t': index === 0,
'rounded-b': index === this.regions.length - 1
}
},
clickOutsideRegionDropdown(e) {
e.stopPropagation()
if (e.target === this.$refs['region-dropdown'] || this.$refs['region-dropdown'].contains(e.target)) {
return
}
this.dropdown = false
},
formSubmit() {
const search = this.summoner.split(' ').join('')
if (search.length) {
this.$emit('formSubmit', search, this.selectedRegion)
}
},
globalClick(e) {
if (e.target === this.$refs.input || e.target === this.$refs.submit) return
if (!this.clickDropdown) {
this.selected = false
}
this.clickDropdown = false
},
handleEscape(e) {
if (e.key === 'Esc' || e.key === 'Escape') {
this.dropdown = false
this.selected = false
this.open = false
} else if ((e.key === 'k' && (e.ctrlKey || e.metaKey)) || e.key === '/') {
e.preventDefault()
this.dropdown = false
this.open = !this.open
}
},
windowBlur() {
this.selected = false
this.open = false
},
...mapActions('settings', ['updateSettings']),
}
}
</script>
@ -212,13 +160,4 @@ export default {
.summoner-input::placeholder {
@apply font-normal;
}
.offsetDropDownXl {
top: 58px;
right: 50px;
}
.offsetIcon {
left: 4px;
}
</style>

View file

@ -1,42 +1,116 @@
<template>
<div
@mousedown="clickDropdown"
class="absolute z-30 w-full px-3 py-2 mt-2 bg-blue-900 border border-blue-800 rounded-lg shadow"
:class="homepage ? 'mt-2' : 'mt-1'"
class="absolute z-30 w-full bg-blue-800 rounded-lg shadow-md"
>
<div v-if="favorites.length">
<div class="text-base text-blue-100">Favorites:</div>
<div class="flex flex-wrap items-center -mx-1 text-xs leading-none">
<div class="shadow">
<div class="pt-3">
<div v-if="!homepage" class="relative px-3 bypass-click">
<button class="absolute w-12 h-full text-blue-300 hover:text-white" type="submit">
<svg class="absolute w-4 h-4 vertical-center horizontal-center">
<use xlink:href="#search" />
</svg>
</button>
<input
@input="$emit('input', $event.target.value)"
:value="value"
class="w-full px-12 py-2 pr-4 placeholder-blue-200 placeholder-opacity-75 bg-blue-700 border border-blue-500 rounded-md outline-none focus:bg-blue-760"
type="text"
placeholder="Search summoner"
spellcheck="false"
/>
<div v-if="!homepage" ref="region-dropdown">
<SearchFormRegion @toggle="toggle" :dropdown="dropdown" :homepage="homepage" />
</div>
</div>
<div
:style="{maxHeight: homepage ? '300px' : '480px'}"
class="px-3 pb-6 overflow-y-auto light-scrollbar"
>
<div :class="{'mt-4': !homepage}">
<div v-if="recentSearches.length" class="text-base text-blue-100">Recent</div>
<div v-else-if="favorites.length === 0" class="flex items-center space-x-2">
<svg class="w-4 h-4 text-blue-100">
<use xlink:href="#info" />
</svg>
<div class="text-base text-blue-100">Summoner example</div>
</div>
<div
ref="searches"
@keydown.prevent.up="onArrowUp()"
@keydown.prevent.down="onArrowDown()"
@keydown.prevent.stop.enter="onOptionSelect()"
@keydown.prevent.stop.space="onOptionSelect()"
role="listbox"
tabindex="-1"
class="flex flex-wrap items-center text-xs leading-none focus:outline-none"
>
<template v-if="recentSearches.length">
<SearchFormDropdownPlayer
v-for="player in favorites"
:key="player.name"
v-for="(player, index) in recentSearchesSliced"
:key="player.name + player.region"
@mouseenter.native="selected = index + 1"
@mouseleave.native="selected = null"
:selected="index === selected - 1"
:player="player"
:favorite="true"
:favorites-list="false"
/>
</template>
<template v-else-if="favorites.length === 0">
<SearchFormDropdownPlayer
@mouseenter.native="selected = 1"
@mouseleave.native="selected = null"
:player="{name: 'Alderiate', icon: 1150, region: 'euw'}"
:selected="selected === 1"
:favorites-list="false"
/>
</template>
</div>
</div>
<div v-if="favorites.length" :class="{'mt-4': recentSearches.length}">
<div class="text-base text-blue-100">Favorites</div>
<div
ref="favorites"
@keydown.prevent.up="onArrowUp()"
@keydown.prevent.down="onArrowDown()"
@keydown.prevent.stop.enter="onOptionSelect()"
@keydown.prevent.stop.space="onOptionSelect()"
role="listbox"
tabindex="-1"
class="flex flex-wrap items-center text-xs leading-none"
>
<SearchFormDropdownPlayer
v-for="(player, index) in favorites"
:key="player.name + player.region"
@mouseenter.native="selected = index + recentSearchesCount + 1"
@mouseleave.native="selected = null"
:player="player"
:selected="index === selected - 1 - recentSearchesCount"
:favorites-list="true"
/>
</div>
</div>
<div :class="{'mt-2': favorites.length}">
<div class="text-base text-blue-100">Recent searches:</div>
<div class="flex flex-wrap items-center -mx-1 text-xs leading-none">
<template v-if="recentSearches.length">
<SearchFormDropdownPlayer
v-for="player in recentSearches"
:key="player.name"
:player="player"
:favorite="isFavorite(player.name)"
/>
</template>
<template v-else>
<svg class="w-4 h-4 mt-1 ml-4 text-blue-200">
<use xlink:href="#info" />
</svg>
<div class="mt-1 ml-1 text-sm text-blue-200">Example :</div>
<SearchFormDropdownPlayer
:player="{name: 'Alderiate', icon: 1150, region: 'euw'}"
:favorite="false"
class="ml-2"
/>
</template>
</div>
</div>
<div class="px-4 py-4 bg-blue-1000 rounded-b-md">
<div class="flex items-center justify-between select-none text-xxs">
<div class="space-x-2">
<span class="text-xs font-medium text-blue-700 bg-blue-100 rounded-md key">Enter</span>
<span>to select</span>
</div>
<div class="space-x-2">
<span class="text-xs font-medium text-blue-700 bg-blue-100 rounded-md key">&darr; &uarr;</span>
<span>to navigate</span>
</div>
<div class="space-x-2">
<span class="text-xs font-medium text-blue-700 bg-blue-100 rounded-md key">Escape</span>
<span>to close</span>
</div>
<div class="space-x-2">
<span class="text-xs font-medium text-blue-700 bg-blue-100 rounded-md key">CTRL K</span>
<span>to open</span>
</div>
</div>
</div>
</div>
</div>
@ -44,24 +118,127 @@
<script>
import { mapState } from 'vuex'
import SearchFormRegion from '@/components/Form/SearchFormRegion.vue'
import SearchFormDropdownPlayer from '@/components/Form/SearchFormDropdownPlayer.vue'
export default {
components: {
SearchFormRegion,
SearchFormDropdownPlayer,
},
props: {
dropdown: {
type: Boolean,
default: false
},
homepage: {
type: Boolean,
default: false
},
value: {
type: String,
required: true
}
},
data() {
return {
favoritesCount: null,
totalCount: null,
recentSearchesCount: null,
selected: null,
}
},
computed: {
allPlayers() {
return [...this.recentSearchesSliced, ...this.favorites]
},
recentSearchesSliced() {
return this.recentSearches.slice(0, 4)
},
...mapState('settings', ['favorites', 'recentSearches'])
},
methods: {
clickDropdown() {
this.$emit('click-dropdown')
created() {
window.addEventListener('mousedown', this.handleClick)
},
isFavorite(name) {
return this.favorites.some(s => s.name === name)
mounted() {
this.$refs.searches.focus()
this.recentSearchesCount = this.$refs.searches ? this.$refs.searches.children.length : 0
this.favoritesCount = this.$refs.favorites ? this.$refs.favorites.children.length : 0
this.totalCount = this.recentSearchesCount + this.favoritesCount
if (this.totalCount > 0) {
this.selected = 1
}
},
beforeDestroy() {
window.removeEventListener('mousedown', this.handleClick)
},
methods: {
close() {
this.$emit('close')
// Close region dropdown if open while closing global dropdown
if (this.dropdown) {
this.toggle()
}
},
handleClick(e) {
const bypassElements = document.querySelectorAll('.bypass-click')
for (const element of bypassElements) {
if (e.target === element || element.contains(e.target)) return
}
// Click outside to close region dropdown
if (this.$refs['region-dropdown'] &&
e.target !== this.$refs['region-dropdown'] &&
!this.$refs['region-dropdown'].contains(e.target) && this.dropdown) {
this.toggle()
}
e.preventDefault()
this.$refs.searches.focus()
},
onArrow() {
const scrollIntoBlock = this.selected === 1 ? 'end' : 'nearest'
if (this.selected > this.recentSearchesCount) {
this.$refs.favorites.children[this.selected - this.recentSearchesCount - 1].scrollIntoView({ block: scrollIntoBlock })
} else {
this.$refs.searches.children[this.selected - 1].scrollIntoView({ block: scrollIntoBlock })
}
},
onArrowUp() {
this.selected = this.selected - 1 < 1 ? this.totalCount : this.selected - 1
this.onArrow()
},
onArrowDown() {
this.selected = this.selected + 1 > this.totalCount ? 1 : this.selected + 1
this.onArrow()
},
onOptionSelect() {
if (this.selected === null) {
return
}
const player = this.allPlayers[this.selected - 1]
this.$router.push(`/summoner/${player.region}/${player.name}`).catch(() => { })
this.close()
},
toggle() {
this.$emit('toggle')
},
}
}
</script>
<style scoped>
.key {
padding: 0.2rem 0.45rem;
box-shadow: 0 2px 0 0 #3182ce, 0 5px 3px 0 rgba(0, 0, 0, 0.1),
0 5px 2px 0 rgba(0, 0, 0, 0.06);
}
</style>

View file

@ -2,24 +2,47 @@
<router-link
:to="{ name: 'summoner', params: { region: player.region, name: player.name }}"
:title="player.name"
class="flex items-center p-1 mt-1 ml-1 text-blue-200 cursor-pointer select-none bg-blue-1000 hover:bg-blue-800"
:class="selected ? 'bg-blue-760' : 'bg-blue-900'"
class="flex items-center justify-between w-full px-4 py-3 mt-1 text-blue-200 rounded-md shadow-md cursor-pointer select-none bypass-click"
role="option"
>
<div class="p-1 font-bold text-white uppercase bg-blue-800 rounded text-xxs">{{ player.region }}</div>
<div class="flex items-center">
<svg v-if="favoritesList" class="w-5 h-5 text-yellow-400">
<use xlink:href="#star-outline" />
</svg>
<svg v-else class="w-5 h-5">
<use xlink:href="#time" />
</svg>
<div class="w-20">
<div
class="inline-flex px-2 py-1 ml-6 text-xs font-semibold text-white uppercase bg-blue-800 rounded"
>{{ player.region }}</div>
</div>
<div
:style="{backgroundImage: `url('https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/profile-icons/${player.icon}.jpg')`}"
class="w-5 h-5 ml-1 bg-center bg-cover rounded"
class="w-6 h-6 ml-2 bg-center bg-cover rounded-full"
></div>
<div class="ml-1 truncate max-w-12">{{ player.name }}</div>
<svg
<div class="ml-2 text-base">{{ player.name }}</div>
</div>
<div class="flex items-center space-x-1">
<button
v-if="!favoritesList"
@click.prevent="favoriteClick"
:class="favorite ? 'text-yellow-400' : 'hover:text-yellow-400 cursor-pointer'"
class="ml-2 w-3p5 h-3p5"
class="flex items-center justify-center p-2 rounded-full hover:text-yellow-400 hover:bg-blue-700"
>
<svg class="w-4 h-4">
<use xlink:href="#star" />
</svg>
<svg @click.prevent="closeClick" class="ml-2 cursor-pointer w-3p5 h-3p5 hover:text-white">
</button>
<button
@click.prevent="closeClick"
class="p-2 rounded-full cursor-pointerhover:text-white hover:bg-blue-700"
>
<svg class="w-4 h-4">
<use xlink:href="#times" />
</svg>
</button>
</div>
</router-link>
</template>
@ -28,10 +51,6 @@ import { mapActions } from 'vuex'
export default {
props: {
favorite: {
type: Boolean,
default: false,
},
favoritesList: {
type: Boolean,
default: false,
@ -40,6 +59,10 @@ export default {
type: Object,
required: true,
},
selected: {
type: Boolean,
default: false
}
},
methods: {
@ -51,9 +74,6 @@ export default {
this.removeRecentSearch(this.player)
},
favoriteClick() {
if (this.favorite) {
return
}
this.updateFavorite(this.player)
},
...mapActions('settings', ['removeRecentSearch', 'updateFavorite'])

View file

@ -0,0 +1,133 @@
<template>
<div>
<div
:class="[homepage ? 'mr-12' : 'mr-4']"
class="absolute right-0 z-30 flex items-center h-full vertical-center"
>
<div
@click="toggle"
:class="[selectRegionClasses]"
class="flex items-center transition-all border-2 border-transparent rounded cursor-pointer transition-fast ease-in-quad ease-out-quad hover:text-white"
>
<span class="font-bold uppercase select-none selected">{{ selectedRegion }}</span>
<svg class="w-4 h-4 ml-1 -mr-1">
<use xlink:href="#caret-down" />
</svg>
</div>
</div>
<transition name="scale-fade">
<div
v-show="dropdown"
:class="[dropdownClasses]"
class="absolute right-0 z-30 text-white shadow cursor-pointer"
>
<div
v-for="(region, index) in regions"
:key="region"
@click="selectRegion(region)"
:class="classRegions(index)"
class="relative py-1 pl-5 pr-2 text-xs text-right select-none bg-blue-1000 hover:bg-blue-800"
>
<svg
v-if="region.toLowerCase() === selectedRegion"
class="absolute w-3 h-3 fill-current vertical-center offsetIcon"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"
/>
</svg>
{{ region }}
</div>
</div>
</transition>
</div>
</template>
<script>
import { mapActions, mapState } from 'vuex'
export default {
props: {
dropdown: {
type: Boolean,
default: false
},
homepage: {
type: Boolean,
default: false
}
},
data() {
return {
regions: [
'BR',
'EUNE',
'EUW',
'JP',
'KR',
'LAN',
'LAS',
'NA',
'OCE',
'TR',
'RU'
],
}
},
computed: {
dropdownClasses() {
return {
'offsetDropDown mr-4 rounded': !this.homepage,
'offsetDropDownXl rounded-b': this.homepage
}
},
selectRegionClasses() {
return {
'px-2 text-base rounded-md': !this.homepage,
'px-2 py-1': this.homepage,
'bg-blue-1000': this.dropdown && !this.homepage,
'border-teal-200': this.dropdown && this.homepage,
}
},
...mapState({
selectedRegion: state => state.settings.region
}),
},
methods: {
classRegions(index) {
return {
'rounded-t': index === 0,
'rounded-b': index === this.regions.length - 1
}
},
selectRegion(region) {
this.toggle()
this.updateSettings({ name: 'region', value: region.toLowerCase() })
},
toggle() {
this.$emit('toggle')
},
...mapActions('settings', ['updateSettings']),
}
}
</script>
<style scoped>
.offsetDropDown {
top: 40px;
}
.offsetDropDownXl {
top: 58px;
right: 50px;
}
.offsetIcon {
left: 4px;
}
</style>

View file

@ -26,6 +26,7 @@
<symbol id="rank-challenger" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 88 88"><path d="M56 24C49.1233 15.4277 43.5 9 43.5 9S37.8767 15.4277 31 24c3.4821-2.6881 7.8113-4.283 12.5-4.283S52.5179 21.3119 56 24z" fill="#F4C874" /><path d="M56.6025 23.8464L86 9c0 14.8495-2.1009 29.6959-2.1009 29.6959L65 46.3323l16.7981 2.9686s-7.3486 11.6677-22.0489 22.2727l-7.3486-10.605C52.4006 68.0036 46.1009 79 44 79c-2.1009 0-8.4006-11.0186-8.4006-18.0314-.9765 1.2028-7.3486 10.605-7.3486 10.605C13.5505 60.9686 6.20189 49.3009 6.20189 49.3009L23 46.3323 4.10095 38.6959S2 23.8464 2 9l29.3975 14.8464C26.2979 27.7155 23 33.8786 23 40.8182c0 8.5018 4.9516 15.8359 12.1017 19.2182 2.7026 1.2791 5.717 1.9918 8.8983 1.9918 11.5977 0 21-9.4977 21-21.2132 0-6.9395-3.2979-13.0995-8.3975-16.9718v.0032z" fill="#F4C874" /><path d="M44.5 54C51.4031 54 57 48.4031 57 41.5S51.4031 29 44.5 29 32 34.5969 32 41.5 37.5969 54 44.5 54z" fill="#3FBFDD" /></symbol>
<symbol id="search" class="fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z" /></symbol>
<symbol id="star" class="fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M259.3 17.8L194 150.2 47.9 171.5c-26.2 3.8-36.7 36.1-17.7 54.6l105.7 103-25 145.5c-4.5 26.3 23.2 46 46.4 33.7L288 439.6l130.7 68.7c23.2 12.2 50.9-7.4 46.4-33.7l-25-145.5 105.7-103c19-18.5 8.5-50.8-17.7-54.6L382 150.2 316.7 17.8c-11.7-23.6-45.6-23.9-57.4 0z"></path></symbol>
<symbol id="star-outline" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z"></path></symbol>
<symbol id="stopwatch" class="fill-current" viewBox="0 0 17 20" xmlns="http://www.w3.org/2000/svg"><path d="M16.999 11.875C16.999 16.3633 13.1946 20 8.49949 20C3.80434 20 0 16.3633 0 11.875C0 7.8125 3.11784 4.44531 7.19188 3.84766V2.5H6.04771C5.77802 2.5 5.55736 2.28906 5.55736 2.03125V0.46875C5.55736 0.210938 5.77802 0 6.04771 0H10.9513C11.221 0 11.4416 0.210938 11.4416 0.46875V2.03125C11.4416 2.28906 11.221 2.5 10.9513 2.5H9.8071V3.84766C11.3395 4.07422 12.737 4.69141 13.8811 5.58984L15.0049 4.51562C15.1969 4.33203 15.5075 4.33203 15.6995 4.51562L16.856 5.62109C17.048 5.80469 17.048 6.10156 16.856 6.28516L15.6546 7.43359L15.6301 7.45703C16.4964 8.72266 16.999 10.2422 16.999 11.875ZM9.8071 13.2812V7.36328C9.8071 7.10547 9.58644 6.89453 9.31675 6.89453H7.68223C7.41254 6.89453 7.19188 7.10547 7.19188 7.36328V13.2812C7.19188 13.5391 7.41254 13.75 7.68223 13.75H9.31675C9.58644 13.75 9.8071 13.5391 9.8071 13.2812Z" /></symbol>
<symbol id="time" class="stroke-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></symbol>
<symbol id="times" class="fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 352 512"><path fill="currentColor" d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"></path></symbol>

View file

@ -12,14 +12,14 @@
class="fixed left-0 right-0 z-20 px-4 text-teal-100 transition-colors duration-100 ease-in-out border-b-2"
style="border-color: rgba(144, 205, 244, 0.4);"
>
<div class="flex items-center justify-between -mb-2px">
<div class="flex items-center justify-between py-2 -mb-2px">
<div class="flex flex-1">
<router-link to="/">
<img class="block h-10" src="@/assets/img/Logo.svg" alt="LeagueStats logo" />
</router-link>
</div>
<SearchForm @formSubmit="redirect" size="small" />
<SearchForm @formSubmit="redirect" :homepage="false" />
<div class="flex-1">
<div class="flex items-center justify-end">
@ -44,7 +44,7 @@
</div>
</header>
<div class="relative z-10 flex-grow mx-auto mt-16 text-white page-wrapper">
<div class="relative z-10 flex-grow mx-auto mt-20 text-white page-wrapper">
<template v-if="summonerLoading || summonerFound">
<template v-if="summonerLoading">
<HeaderLoader />

View file

@ -12,27 +12,32 @@ export const mutations = {
state.favorites.push(summoner)
},
ADD_SEARCH(state, summoner) {
const alreadyFav = state.favorites.find(s => s.name === summoner.name && s.region === summoner.region)
if (alreadyFav) {
return
}
let searches = state.recentSearches
const alreadySearch = searches.find(s => s.name === summoner.name)
const alreadySearch = searches.find(s => s.name === summoner.name && s.region === summoner.region)
if (alreadySearch) {
alreadySearch.date = Date.now()
searches.sort((a, b) => b.date - a.date)
return
}
if (searches.length >= 6) {
if (searches.length > 10) {
searches.pop()
}
summoner.date = Date.now()
searches.unshift(summoner)
},
REMOVE_FAVORITE(state, summonerName) {
state.favorites = state.favorites.filter(s => s.name !== summonerName)
REMOVE_FAVORITE(state, summoner) {
state.favorites = state.favorites.filter(s => s.name !== summoner.name || s.region !== summoner.region)
},
REMOVE_SEARCH(state, summonerName) {
state.recentSearches = state.recentSearches.filter(s => s.name !== summonerName)
REMOVE_SEARCH(state, summoner) {
state.recentSearches = state.recentSearches.filter(s => s.name !== summoner.name || s.region !== summoner.region)
},
UPDATE_SETTING(state, { name, value }) {
state[name] = value
@ -45,13 +50,13 @@ export const actions = {
dispatch('updateSettings', { name: 'recentSearches', value: state.recentSearches, isJson: true })
},
removeRecentSearch({ commit, dispatch }, summoner) {
commit('REMOVE_SEARCH', summoner.name)
commit('REMOVE_SEARCH', summoner)
dispatch('updateSettings', { name: 'recentSearches', value: state.recentSearches, isJson: true })
},
updateFavorite({ commit, dispatch, state }, summoner) {
const alreadyFav = state.favorites.find(s => s.name === summoner.name)
const alreadyFav = state.favorites.find(s => s.name === summoner.name && s.region === summoner.region)
if (alreadyFav) {
commit('REMOVE_FAVORITE', summoner.name)
commit('REMOVE_FAVORITE', summoner)
} else {
if (state.favorites.length >= 6) {
// Display error message
@ -61,6 +66,10 @@ export const actions = {
}, { root: true })
}
commit('ADD_FAVORITE', summoner)
const searched = state.recentSearches.find(s => s.name === summoner.name && s.region === summoner.region)
if (searched) {
dispatch('removeRecentSearch', summoner)
}
}
dispatch('updateSettings', { name: 'favorites', value: state.favorites, isJson: true })

View file

@ -17,7 +17,7 @@
<div class="relative flex flex-col items-center w-full max-w-lg">
<img class="absolute logo" src="@/assets/img/Logo.svg" alt="logo" />
<SearchForm @formSubmit="redirect" />
<SearchForm @formSubmit="redirect" :homepage="true" />
</div>
<p