feat: replace old client folder

This commit is contained in:
Valentin Kaelin 2023-09-20 22:01:43 +02:00
parent 8c3b0c5cb9
commit 1b6281404f
184 changed files with 4444 additions and 43483 deletions

View file

@ -1,13 +0,0 @@
module.exports = {
root: true,
env: {
node: true,
},
extends: ['plugin:vue/essential', 'eslint:recommended'],
parserOptions: {
parser: '@babel/eslint-parser',
},
rules: {
'vue/multi-word-component-names': 'off',
},
}

23
client-new/.gitignore vendored
View file

@ -1,23 +0,0 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.lock

View file

@ -1,37 +0,0 @@
# LeagueStats Frontend
## Project setup
```
npm install
```
### Compile and hot-reload for development
```
npm run dev
```
### Compile and minify for production
```
npm run build
```
### Test the production build locally
```
npm run preview
```
### Lint files
```
npm run lint
```
### Format files
```
npm run format
```

View file

@ -1,14 +0,0 @@
module.exports = {
presets: ['@babel/preset-env'],
plugins: [
function () {
return {
visitor: {
MetaProperty(path) {
path.replaceWithSourceString('process')
},
},
}
},
],
}

File diff suppressed because it is too large Load diff

View file

@ -1,38 +0,0 @@
{
"name": "leaguestats-frontend",
"private": true,
"version": "0.1.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview --port 8080",
"lint": "eslint --ext .js,.ts,.jsx,.tsx,.vue --ignore-path .eslintignore .",
"format": "prettier --write \"**/*.{ts,tsx,js,css,vue}\" --ignore-path .prettierignore"
},
"devDependencies": {
"@babel/eslint-parser": "^7.22.15",
"@babel/preset-env": "^7.22.20",
"@tailwindcss/custom-forms": "^0.2.1",
"@vitejs/plugin-vue2": "^2.2.0",
"autoprefixer": "^10.4.15",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-vue": "^9.17.0",
"postcss": "^8.4.30",
"postcss-import": "^12.0.1",
"tailwindcss": "^1.9.6",
"vite": "^4.4.5",
"vite-plugin-eslint": "^1.8.1"
},
"dependencies": {
"axios": "^1.5.0",
"plausible-tracker": "^0.3.8",
"portal-vue": "^2.1.7",
"vue": "^2.7.14",
"vue-content-loader": "^0.2.3",
"vue-meta": "^2.4.0",
"vue-router": "^3.6.5",
"vue-sticky-sidebar": "^1.0.5",
"vuex": "^3.6.2"
}
}

View file

@ -1 +0,0 @@
/* /index.html 200

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#38b2ac</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View file

@ -1,3 +0,0 @@
user-agent: *
Allow: /$
Disallow: /

View file

@ -1,36 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="267.000000pt" height="267.000000pt" viewBox="0 0 267.000000 267.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,267.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1110 2613 c-430 -75 -803 -374 -975 -782 -45 -107 -85 -268 -92
-377 l-6 -91 46 -11 c25 -7 55 -12 67 -12 14 0 90 66 221 192 166 158 199 195
194 212 -13 45 -17 143 -6 182 15 57 63 120 118 154 26 17 49 30 52 30 6 0 81
-116 81 -125 0 -3 -7 -5 -15 -5 -26 0 -84 -61 -95 -99 -32 -119 102 -216 208
-151 l32 20 0 -85 0 -84 -37 -7 c-53 -11 -106 -9 -158 6 l-45 13 -102 -96
c-291 -272 -311 -293 -304 -318 4 -13 10 -53 13 -89 4 -54 1 -75 -17 -118 -20
-47 -86 -119 -133 -147 -16 -9 -14 -16 19 -80 171 -332 472 -577 824 -669 249
-66 502 -59 739 20 713 237 1079 1008 809 1707 -16 42 -33 77 -36 77 -4 0
-108 -101 -232 -225 l-224 -224 12 -59 c16 -76 15 -98 -4 -163 -21 -73 -107
-158 -179 -180 -197 -57 -375 74 -375 275 0 36 -5 48 -25 64 -25 20 -26 24
-23 120 l3 100 58 -34 c32 -19 65 -34 73 -34 8 0 37 11 64 25 66 33 126 41
201 25 l61 -13 239 239 c164 164 239 246 239 260 0 56 -197 261 -342 357 -158
104 -330 173 -508 202 -118 19 -327 18 -440 -2z m328 -1085 l2 -568 375 0
c206 0 375 -3 374 -7 0 -5 -64 -89 -143 -188 l-143 -180 -551 -3 c-304 -1
-552 0 -552 3 0 3 41 67 90 143 l90 139 0 469 0 470 -90 142 c-49 79 -90 145
-90 148 0 2 143 3 318 2 l317 -3 3 -567z"/>
<path d="M903 2035 c4 -8 33 -57 67 -109 l60 -95 0 -492 0 -492 -50 -80 c-28
-43 -58 -90 -67 -103 l-15 -24 490 0 491 0 99 128 100 127 -349 3 -349 2 0
575 0 575 -241 0 c-208 0 -240 -2 -236 -15z"/>
<path d="M1706 1400 c-65 -57 -70 -132 -14 -195 110 -126 305 20 217 162 -27
43 -66 63 -123 63 -36 0 -52 -6 -80 -30z"/>
<path d="M46 1158 c14 -102 39 -193 54 -193 24 0 60 64 60 107 0 65 -53 138
-102 138 -17 0 -18 -5 -12 -52z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2 KiB

View file

@ -1,19 +0,0 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-256x256.png",
"sizes": "256x256",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

View file

@ -1,47 +0,0 @@
<template>
<div id="app" class="min-h-screen font-sans antialiased bg-blue-900">
<SVGContainer />
<NotificationsContainer />
<RunesContainer />
<portal-target name="tooltip-destination" />
<component :is="layout">
<router-view />
</component>
</div>
</template>
<script>
import { mapActions } from 'vuex'
import Default from '@/layouts/Default.vue'
import Home from '@/layouts/Home.vue'
import NotificationsContainer from '@/components/Global/NotificationsContainer.vue'
import RunesContainer from '@/components/Match/Runes/RunesContainer.vue'
import SVGContainer from '@/components/Global/SVGContainer.vue'
export default {
components: {
Default,
Home,
NotificationsContainer,
RunesContainer,
SVGContainer,
},
computed: {
layout() {
return this.$route.meta.layout || 'Default'
},
},
created() {
this.updatePercent()
this.updateSettings({ name: 'region' })
this.updateSettings({ name: 'recentSearches', isJson: true })
this.updateSettings({ name: 'favorites', isJson: true })
},
methods: {
...mapActions('settings', ['updatePercent', 'updateSettings']),
},
}
</script>

View file

@ -1,73 +0,0 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap');
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar,
::-webkit-scrollbar-track {
background: rgba(23, 49, 79, 0.6);
}
.light-scrollbar::-webkit-scrollbar,
.light-scrollbar::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background-color: rgba(194, 217, 254, 0.6);
border-radius: 8px;
}
::selection {
@apply text-white bg-blue-1000;
}
::-moz-selection {
@apply text-white bg-blue-1000;
}
.min-w-1200 {
min-width: 1200px;
}
.page-wrapper {
width: 1200px;
}
button:focus {
outline: 0;
}
.vertical-center {
top: 50%;
transform: translateY(-50%);
}
.horizontal-center {
left: 0;
right: 0;
margin: 0 auto;
}
.bg-gradient {
background: linear-gradient(180deg, #2c5282 0%, rgba(44, 82, 130, 0) 100%);
}
.bg-gradient-x {
background: linear-gradient(270deg, rgba(44, 82, 130, 1) 0%, rgba(44, 82, 130, 0) 100%);
}
.text-overflow {
text-overflow: ellipsis;
}
.heading {
background: linear-gradient(to top, rgb(34, 92, 155) 0%, rgb(34, 92, 135) 100%);
box-shadow: rgba(235, 248, 255, 0.1) 0px -1px inset;
}
.input-color {
background: rgba(23, 49, 79, 0.6);
}

View file

@ -1,4 +0,0 @@
@import 'tailwind.css';
@import 'base.css';
@import 'transition.css';
@import 'match.css';

View file

@ -1,40 +0,0 @@
/* purgecss start ignore */
.Win {
background-image: linear-gradient(90deg, rgba(1, 97, 28, 0.3) 0%, rgba(44, 82, 130, 0) 45%);
}
.Fail {
background-image: linear-gradient(90deg, rgba(140, 0, 0, 0.3) 0%, rgba(44, 82, 130, 0) 45%);
}
.Remake {
background-image: linear-gradient(90deg, rgba(233, 169, 75, 0.3) 0%, rgba(44, 82, 130, 0) 45%);
}
.ban::after {
content: '';
position: absolute;
left: 0px;
top: 50%;
width: calc(100% + 1px);
height: 2px;
transform: rotate(-45deg);
}
.ban-blue::after {
background: #38b2ac;
}
.ban-red::after {
background: #f56565;
}
.ban-img {
filter: grayscale(100%);
}
.ban-order {
left: -7px;
top: -5px;
}
/* purgecss end ignore */

View file

@ -1,3 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View file

@ -1,94 +0,0 @@
/* purgecss start ignore */
/* Fade transitions */
.fade-enter-active,
.fade-leave-active {
transition: opacity 2s;
}
.fade-fast-enter-active,
.fade-fast-leave-active {
transition: opacity 0.3s;
}
.fade-fast-enter,
.fade-fast-leave-to,
.fade-enter,
.fade-leave-to {
opacity: 0;
}
/* Slide fade transition (match details) */
.slide-fade-enter-active,
.slide-fade-leave-active {
transition: all 0.4s;
}
.slide-fade-enter,
.slide-fade-leave-to {
transform: translateX(400px);
opacity: 0;
}
/* Tab transition */
.tab-enter-active {
@apply transition duration-500 ease-in-out;
}
.tab-enter,
.tab-enter-leave-to {
@apply opacity-0;
}
.tab-enter-to,
.tab-enter-leave {
@apply opacity-100;
}
/* Scale-fade transition */
.scale-fade-enter-active {
@apply transition-all duration-75 ease-out transform;
}
.scale-fade-leave-active {
@apply transition-all duration-75 ease-in transform;
}
.scale-fade-enter,
.scale-fade-leave-to {
@apply scale-90 opacity-0;
}
.scale-fade-enter-to,
.scale-fade-leave {
@apply scale-100 opacity-100;
}
/* Grow transition (ripples) */
.grow-enter-active,
.grow-enter-to-active {
transition: all 1500ms ease-out;
}
.grow-leave-active,
.grow-leave-to-active {
transition: all 700ms ease-out;
}
.grow-enter {
transform: scale(0);
opacity: 1;
}
.grow-enter-to,
.grow-leave {
transform: scale(4);
opacity: 1;
}
.grow-leave-to {
transform: scale(4);
opacity: 0;
}
/* purgecss end ignore */

View file

@ -1,86 +0,0 @@
<template>
<div
:style="{ width: size, height: size, margin }"
class="relative"
style="transform: rotateZ(45deg)"
>
<div class="relative float-left w-1/2 cube-1 cube h-1/2"></div>
<div class="relative float-left w-1/2 cube-2 cube h-1/2"></div>
<div class="relative float-left w-1/2 cube-4 cube h-1/2"></div>
<div class="relative float-left w-1/2 cube-3 cube h-1/2"></div>
</div>
</template>
<script>
export default {
props: {
color: {
type: String,
default: '#bee3f8',
},
size: {
type: String,
default: '30px',
},
margin: {
type: String,
default: '0 auto',
},
},
}
</script>
<style scoped>
.cube {
transform: scale(1.1);
}
.cube:before {
content: '';
@apply absolute top-0 left-0 w-full h-full bg-blue-300;
animation: cubeAngle 2.4s infinite linear both;
transform-origin: 100% 100%;
}
.cube-2 {
transform: scale(1.1) rotateZ(90deg);
}
.cube-3 {
transform: scale(1.1) rotateZ(180deg);
}
.cube-4 {
transform: scale(1.1) rotateZ(270deg);
}
.cube-2:before {
animation-delay: 0.3s;
}
.cube-3:before {
animation-delay: 0.6s;
}
.cube-4:before {
animation-delay: 0.9s;
}
@keyframes cubeAngle {
0%,
10% {
@apply opacity-0;
transform: perspective(130px) rotateX(-180deg);
}
25%,
75% {
@apply opacity-100;
transform: perspective(130px) rotateX(0deg);
}
90%,
100% {
@apply opacity-0;
transform: perspective(130px) rotateY(180deg);
}
}
</style>

View file

@ -1,62 +0,0 @@
<template>
<div :style="{ width: width }" class="text-center spinner">
<div :style="dotStyle" class="inline-block rounded-full bounce1"></div>
<div :style="dotStyle" class="inline-block rounded-full bounce2"></div>
<div :style="dotStyle" class="inline-block rounded-full bounce3"></div>
</div>
</template>
<script>
export default {
props: {
color: {
type: String,
default: '#90cdf4',
},
dotWidth: {
type: String,
default: '18px',
},
width: {
type: String,
default: '70px',
},
},
computed: {
dotStyle() {
return {
backgroundColor: this.color,
height: this.dotWidth,
width: this.dotWidth,
}
},
},
}
</script>
<style scoped>
.spinner > div {
animation: bouncedelay 1.4s infinite ease-in-out both;
}
.spinner .bounce1 {
animation-delay: -0.32s;
}
.spinner .bounce2 {
animation-delay: -0.16s;
}
@keyframes bouncedelay {
0%,
60%,
100% {
transform: scale(0);
}
30% {
transform: scale(0.8);
}
}
</style>

View file

@ -1,74 +0,0 @@
<template>
<transition :name="transitionName">
<div
v-show="imageState === 'loaded'"
:class="[imageClass, imageState]"
:style="computedStyle"
:data-state="imageState"
></div>
</transition>
</template>
<script>
export default {
props: {
imageSource: {
type: String,
required: true,
},
imageClass: {
type: String,
required: false,
default: '',
},
backgroundSize: {
type: String,
required: false,
default: 'cover',
},
moreBackgrounds: {
type: String,
required: false,
default: '',
},
transitionName: {
type: String,
required: false,
default: '',
},
},
data() {
return {
imageState: 'loading',
asyncImage: new Image(),
}
},
computed: {
computedStyle() {
if (this.imageState === 'loaded') {
return `background-image: ${this.moreBackgrounds} url(${this.asyncImage.src}); background-size: ${this.backgroundSize}`
}
return ''
},
},
mounted() {
this.$nextTick(() => {
this.fetchImage()
})
},
methods: {
fetchImage() {
this.asyncImage.onload = this.imageOnLoad
this.imageState = 'loading'
this.asyncImage.src = this.imageSource
},
imageOnLoad() {
this.imageState = 'loaded'
},
},
}
</script>

View file

@ -1,77 +0,0 @@
<template>
<div ref="container" @mousedown="addRipple" class="relative overflow-hidden cursor-pointer">
<transition-group
class="absolute top-0 left-0 w-full h-full pointer-events-none"
name="grow"
tag="div"
>
<div
v-for="ripple in ripples"
:key="ripple.id"
class="absolute w-full h-full rounded-full opacity-0 pointer-events-none"
:style="{
top: ripple.top,
left: ripple.left,
width: ripple.width,
height: ripple.height,
background: color,
}"
></div>
</transition-group>
<slot></slot>
</div>
</template>
<script>
export default {
props: {
color: {
type: String,
default: 'rgba(255, 255, 255, 0.3)',
},
},
data() {
return {
ripples: [],
rippleWidth: 0,
halfRippleWidth: 0,
}
},
mounted() {
const width = this.$refs.container.offsetWidth
const height = this.$refs.container.offsetHeight
this.rippleWidth = width > height ? width : height
this.halfRippleWidth = this.rippleWidth / 2
window.addEventListener('mouseup', this.purgeRipples)
},
beforeDestroy() {
window.removeEventListener('mouseup', this.purgeRipples)
},
methods: {
addRipple(e) {
const { left, top } = this.$refs.container.getBoundingClientRect()
const rippleId = Date.now()
this.ripples.push({
width: `${this.rippleWidth}px`,
height: `${this.rippleWidth}px`,
left: `${e.clientX - left - this.halfRippleWidth}px`,
top: `${e.clientY - top - this.halfRippleWidth}px`,
id: rippleId,
})
// Remove ripple
setTimeout(() => {
this.ripples = this.ripples.filter((r) => r.id !== rippleId)
}, 400)
},
purgeRipples() {
this.ripples = []
},
},
}
</script>

View file

@ -1,108 +0,0 @@
<template>
<div>
<!-- trigger -->
<div
ref="trigger"
@mouseenter="showTooltip"
@mousemove="mousemove"
@mouseleave="hideTooltip"
:aria-expanded="isOpen"
aria-haspopup="true"
>
<slot name="trigger"></slot>
</div>
<!-- tooltip content -->
<portal v-if="isOpen" to="tooltip-destination">
<div
v-show="isOpen"
ref="content"
class="bg-blue-1000 fixed z-50 py-2 rounded-md shadow"
:style="{ ...position }"
>
<slot></slot>
</div>
</portal>
</div>
</template>
<script>
export default {
data() {
return {
isOpen: false,
left: 0,
offset: 12,
top: 0,
directionBottom: true,
directionRight: true,
directionChecked: false,
width: 0,
}
},
computed: {
position() {
const valuetoRemove = this.directionBottom ? 0 : this.height()
const leftValue = this.directionRight
? this.left + this.offset
: this.left - this.width - this.offset / 2
return {
left: `${leftValue}px`,
top: `${this.top + this.offset - valuetoRemove}px`,
}
},
},
created() {
window.addEventListener('scroll', this.handleScroll)
},
destroyed() {
window.removeEventListener('scroll', this.handleScroll)
},
methods: {
checkTooltipVisibility() {
this.directionChecked = true
const contentRect = this.$refs.content.getBoundingClientRect()
const triggerRect = this.$refs.trigger.getBoundingClientRect()
this.width = contentRect.width
const viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight)
const viewWidth = Math.max(document.documentElement.clientWidth, window.innerWidth)
this.directionBottom = contentRect.bottom + this.offset < viewHeight
this.directionRight = this.left + this.width + triggerRect.width + this.offset < viewWidth
},
handleScroll() {
this.isOpen = false
},
height() {
return this.$refs.content ? this.$refs.content.clientHeight : 0
},
hideTooltip() {
this.isOpen = false
this.directionBottom = true
this.directionRight = true
this.directionChecked = false
},
async mousemove(event) {
this.left = event.clientX
this.top = event.clientY
if (!this.directionChecked) {
// Component has been destroyed
if (!this.$refs.content || !this.$refs.trigger) {
return
}
await this.$nextTick()
this.checkTooltipVisibility()
}
},
showTooltip(event) {
this.left = event.clientX
this.top = event.clientY
this.isOpen = true
},
},
}
</script>

View file

@ -1,117 +0,0 @@
<template>
<button
@click="btnClicked"
:class="[btnClass, { loading: loading }, { 'pr-12': loading }]"
:disabled="loading"
class="relative select-none"
type="button"
>
<slot>Send</slot>
<span class="spinner absolute opacity-0 left-auto">
<span
class="inline-block absolute right-0 w-4 h-4 opacity-100 border-3 border-white rounded-full"
></span>
<span
class="inline-block absolute right-0 w-4 h-4 opacity-100 border-3 border-white rounded-full"
></span>
<span
class="inline-block absolute right-0 w-4 h-4 opacity-100 border-3 border-white rounded-full"
></span>
<span
class="inline-block absolute right-0 w-4 h-4 opacity-100 border-3 border-white rounded-full"
></span>
</span>
</button>
</template>
<script>
export default {
props: {
btnClass: {
type: String,
required: false,
default: '',
},
loading: {
type: Boolean,
required: false,
default: false,
},
},
methods: {
btnClicked() {
this.$emit('clicked')
},
},
}
</script>
<style scoped>
button {
transition: all 0.2s;
transition-timing-function: ease-in;
}
.spinner {
top: 50%;
right: 1.7rem;
margin: -0.5rem;
transition-property: padding, opacity;
transition-duration: 0.2s, 0.2s;
transition-timing-function: ease-in, ease;
transition-delay: 0s, 0.2s;
}
.spinner span {
animation: spinner 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
border-color: #fff transparent transparent transparent;
}
.spinner span:nth-child(1) {
animation-delay: 0.45s;
}
.spinner span:nth-child(2) {
animation-delay: 0.3s;
}
.spinner span:nth-child(3) {
animation-delay: 0.15s;
}
.loading .spinner {
opacity: 1;
}
button:not(:disabled) .spinner span {
box-shadow: 0 0 0 0.2rem #4fd1c5 inset;
border: 7.4px solid transparent;
transition: all 0.4s;
}
button:not(:disabled) .spinner span:nth-child(1) {
transform: rotate(0deg);
}
button:not(:disabled) .spinner span:nth-child(2) {
transform: rotate(90deg);
}
button:not(:disabled) .spinner span:nth-child(3) {
transform: rotate(180deg);
}
button:not(:disabled) .spinner span:nth-child(4) {
transform: rotate(270deg);
}
@keyframes spinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>

View file

@ -1,192 +0,0 @@
<template>
<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 bg-gray-900"
></div>
<div class="relative w-full">
<input
v-if="homepage"
ref="input"
v-model="summoner"
@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"
ref="submit"
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">
<use xlink:href="#search" />
</svg>
</button>
<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="open"
v-model="summoner"
@close="open = false"
@toggle="dropdown = !dropdown"
:dropdown="dropdown"
:homepage="homepage"
/>
</transition>
<div ref="region-dropdown">
<SearchFormRegion
v-if="homepage"
@toggle="dropdown = !dropdown"
:dropdown="dropdown"
:homepage="homepage"
/>
</div>
</div>
</form>
</template>
<script>
import { mapState } from 'vuex'
import SearchFormDropdown from '@/components/Form/SearchFormDropdown.vue'
import SearchFormRegion from '@/components/Form/SearchFormRegion.vue'
export default {
components: {
SearchFormDropdown,
SearchFormRegion,
},
props: {
homepage: {
type: Boolean,
default: false,
},
},
data() {
return {
summoner: '',
dropdown: false,
open: false,
}
},
computed: {
...mapState({
selectedRegion: (state) => state.settings.region,
}),
},
watch: {
open(newVal) {
// Search Dropdown open
if (newVal) {
this.dropDownOpening()
} else {
this.dropDownClosing()
}
},
$route(newRoute) {
this.summoner = newRoute.params.name
this.dropdown = false
this.open = false
},
},
created() {
if (!this.summoner.length && !this.homepage) {
this.summoner = this.$route.params.name
}
window.addEventListener('blur', this.windowBlur)
window.addEventListener('keydown', this.handleEscape)
},
beforeDestroy() {
window.removeEventListener('blur', this.windowBlur)
window.removeEventListener('keydown', this.handleEscape)
this.dropDownClosing()
},
methods: {
dropDownClosing() {
const header = document.querySelector('.header div')
if (!this.homepage && header) {
header.style.paddingRight = 0
}
document.body.style.marginLeft = 0
document.body.style.overflow = 'auto'
},
dropDownOpening() {
const header = document.querySelector('.header div')
if (!this.homepage) {
document.body.style.marginLeft = `-${this.getScrollbarWidth()}px`
header.style.paddingRight = `${this.getScrollbarWidth()}px`
}
document.body.style.overflow = 'hidden'
},
formSubmit() {
const search = this.summoner.split(' ').join('').replace('+', ' ')
if (search.length) {
this.$emit('formSubmit', search, this.selectedRegion)
}
},
getScrollbarWidth() {
const outer = document.createElement('div')
outer.style.visibility = 'hidden'
outer.style.overflow = 'scroll' // forcing scrollbar to appear
outer.style.msOverflowStyle = 'scrollbar' // needed for WinJS apps
document.body.appendChild(outer)
const inner = document.createElement('div')
outer.appendChild(inner)
const scrollbarWidth = outer.offsetWidth - inner.offsetWidth
outer.parentNode.removeChild(outer)
return scrollbarWidth
},
handleEscape(e) {
if (e.key === 'Esc' || e.key === 'Escape') {
this.dropdown = 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.open = false
},
},
}
</script>
<style scoped>
.summoner-input::placeholder {
@apply font-normal;
}
</style>

View file

@ -1,290 +0,0 @@
<template>
<div
:class="homepage ? 'mt-2' : 'mt-1'"
class="absolute z-30 w-full bg-blue-800 rounded-lg shadow-md"
>
<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-200 hover:text-white" type="submit">
<svg class="absolute w-4 h-4 vertical-center horizontal-center">
<use xlink:href="#search" />
</svg>
</button>
<input
ref="input"
@input="$emit('input', $event.target.value)"
:value="value"
class="w-full py-2 pl-12 pr-32 placeholder-blue-200 placeholder-opacity-75 bg-blue-700 border border-blue-500 rounded-md outline-none focus:bg-blue-760 summoner-input"
type="text"
placeholder="Search summoner"
spellcheck="false"
/>
<button
v-if="!homepage && value.length"
@click="$emit('input', '')"
class="absolute right-0 flex items-center justify-center p-1 mr-24 text-blue-200 rounded-full vertical-center hover:text-white"
type="button"
>
<svg class="w-4 h-4">
<use xlink:href="#times" />
</svg>
</button>
<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.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, index) in recentSearchesSliced"
:key="player.name + player.region"
@close="close"
@mousemove.native="onHover(index + 1)"
:selected="index === selected - 1"
:player="player"
:favorites-list="false"
/>
</template>
<template v-else-if="favorites.length === 0">
<SearchFormDropdownPlayer
@close="close"
@mousemove.native="onHover(1)"
:player="{ name: 'KC Rekkles', icon: 7, 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.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"
@close="close"
@mousemove.native="onHover(index + recentSearchesCount + 1)"
:player="player"
:selected="index === selected - 1 - recentSearchesCount"
:favorites-list="true"
/>
</div>
</div>
</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>
</template>
<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 {
bypassKeys: ['Esc', 'Escape', 'ArrowUp', 'ArrowDown', 'Enter', 'Space', '/'],
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']),
},
created() {
window.addEventListener('mousedown', this.handleClick)
window.addEventListener('keydown', this.handleKeyDown)
},
mounted() {
const input = document.querySelector('.summoner-input')
input.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)
window.removeEventListener('keydown', this.handleKeyDown)
},
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()
},
handleKeyDown(e) {
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
e.preventDefault()
this.$refs.searches.focus()
if (e.key === 'ArrowUp') {
this.onArrowUp()
} else {
this.onArrowDown()
}
}
if (this.bypassKeys.includes(e.key) || (e.key === 'k' && (e.ctrlKey || e.metaKey))) {
return
}
const input = document.querySelector('.summoner-input')
input.focus()
},
onArrow() {
const scrollIntoBlock = this.selected === 1 ? 'end' : this.selected >= 7 ? 'start' : '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()
},
onHover(id) {
this.selected = id
if (this.$refs.searches && this.$refs.searches !== document.activeElement) {
this.$refs.searches.focus()
this.onArrow()
}
},
onOptionSelect() {
console.log('OPTION SELECT')
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

@ -1,90 +0,0 @@
<template>
<router-link
@click.native="close"
:to="{ name: 'summoner', params: { region: player.region, name: player.name } }"
:title="player.name"
: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="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-6 h-6 ml-2 bg-center bg-cover rounded-full"
></div>
<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="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>
</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>
<script>
import { mapActions } from 'vuex'
export default {
props: {
favoritesList: {
type: Boolean,
default: false,
},
player: {
type: Object,
required: true,
},
selected: {
type: Boolean,
default: false,
},
},
methods: {
close() {
this.$emit('close')
},
closeClick() {
if (this.favoritesList) {
this.updateFavorite(this.player)
return
}
this.removeRecentSearch(this.player)
},
favoriteClick() {
this.updateFavorite(this.player)
},
...mapActions('settings', ['removeRecentSearch', 'updateFavorite']),
},
}
</script>

View file

@ -1,138 +0,0 @@
<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 duration-150 ease-in-out border-2 border-transparent rounded cursor-pointer 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',
'PH',
'SG',
'TH',
'TW',
'VN',
],
}
},
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

@ -1,92 +0,0 @@
<template>
<div class="relative z-10 text-sm leading-tight text-teal-400 select-none switch">
<input
v-model="selected"
id="toggle-on"
class="hidden toggle toggle-left"
:value="true"
type="radio"
/>
<label
:class="{ 'selected-label': selected }"
for="toggle-on"
class="inline-block py-1 border-t-2 border-b-2 border-l-2 border-r border-teal-500 rounded-l-full cursor-pointer"
>{{ leftLabel }}</label
>
<input
v-model="selected"
id="toggle-off"
class="hidden toggle toggle-right"
:value="false"
type="radio"
/>
<label
:class="{ 'selected-label': !selected }"
for="toggle-off"
class="inline-block py-1 border-t-2 border-b-2 border-l border-r-2 border-teal-500 rounded-r-full cursor-pointer"
>{{ rightLabel }}</label
>
<div
:class="selected ? 'left-checked' : 'right-checked'"
class="absolute inset-0 w-1/2 bg-teal-500 selector"
></div>
</div>
</template>
<script>
export default {
props: {
leftLabel: {
type: String,
required: true,
},
rightLabel: {
type: String,
required: true,
},
value: {
type: Boolean,
required: true,
},
},
computed: {
selected: {
get() {
return this.value
},
set(value) {
this.$emit('updateValue', value)
},
},
},
}
</script>
<style scoped>
.switch label {
min-width: 45px;
}
.selected-label {
cursor: default;
color: #fff;
transition: color 200ms;
}
.selector {
z-index: -1;
transition:
left 200ms cubic-bezier(0.77, 0, 0.175, 1),
border-radius 200ms cubic-bezier(0.77, 0, 0.175, 1);
}
.left-checked {
left: 0;
border-radius: 999px 0 0 999px;
}
.right-checked {
left: 50%;
border-radius: 0 999px 999px 0;
}
</style>

View file

@ -1,24 +0,0 @@
<template>
<div class="fixed bottom-0 right-0 z-50 pb-2 pr-2">
<PopupNotification
v-for="notification in notifications"
:key="notification.id"
:notification="notification"
/>
</div>
</template>
<script>
import PopupNotification from '@/components/Global/PopupNotification.vue'
import { mapState } from 'vuex'
export default {
components: {
PopupNotification,
},
computed: {
...mapState('notification', ['notifications']),
},
}
</script>

View file

@ -1,69 +0,0 @@
<template>
<transition name="slide-fade">
<div
:class="{
'bg-red-500': notification.type === 'error',
'bg-green-500': notification.type === 'success',
}"
class="relative p-6 pr-10 mt-2 text-white rounded-lg shadow-md"
style="min-width: 240px"
>
<button
@click="deleteNotification"
class="absolute top-0 right-0 block px-1 py-1 mx-1 my-1 border border-transparent rounded-full cursor-pointer focus:outline-none hover:border-white"
>
<svg class="w-3 h-3 fill-current" viewBox="0 0 20 20">
<path
d="M10 8.586L2.929 1.515 1.515 2.929 8.586 10l-7.071 7.071 1.414 1.414L10 11.414l7.071 7.071 1.414-1.414L11.414 10l7.071-7.071-1.414-1.414L10 8.586z"
/>
</svg>
</button>
<div class="flex items-center text-white">
<svg v-if="notification.type === 'success'" class="w-6 fill-current" viewBox="0 0 20 20">
<path
d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM6.7 9.29L9 11.6l4.3-4.3 1.4 1.42L9 14.4l-3.7-3.7 1.4-1.42z"
/>
</svg>
<svg v-if="notification.type === 'error'" class="w-6 fill-current" viewBox="0 0 20 20">
<path
d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm1.41-1.41A8 8 0 1 0 15.66 4.34 8 8 0 0 0 4.34 15.66zm9.9-8.49L11.41 10l2.83 2.83-1.41 1.41L10 11.41l-2.83 2.83-1.41-1.41L8.59 10 5.76 7.17l1.41-1.41L10 8.59l2.83-2.83 1.41 1.41z"
/>
</svg>
<span class="ml-3">{{ notification.message }}</span>
</div>
</div>
</transition>
</template>
<script>
import { mapActions } from 'vuex'
export default {
props: {
notification: {
type: Object,
required: true,
},
},
data() {
return {
timeout: null,
}
},
beforeDestroy() {
clearTimeout(this.timeout)
},
mounted() {
this.timeout = setTimeout(() => this.deleteNotification(), 3000)
},
methods: {
deleteNotification() {
this.remove(this.notification)
},
...mapActions('notification', ['remove']),
},
}
</script>

View file

@ -1,35 +0,0 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" class="hidden">
<symbol id="arrow-right" 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"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></symbol>
<symbol id="caret-down" class="fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z" /></symbol>
<symbol id="caret-up" class="fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M288.662 352H31.338c-17.818 0-26.741-21.543-14.142-34.142l128.662-128.662c7.81-7.81 20.474-7.81 28.284 0l128.662 128.662c12.6 12.599 3.676 34.142-14.142 34.142z"></path></symbol>
<symbol id="chevron-down" class="fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"> <path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z" /></symbol>
<symbol id="close" 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"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></symbol>
<symbol id="creeps" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8.04 1.04912C7.97335 0.971025 7.89055 0.908316 7.79733 0.865316C7.7041 0.822316 7.60266 0.800049 7.5 0.800049C7.39734 0.800049 7.2959 0.822316 7.20267 0.865316C7.10945 0.908316 7.02665 0.971025 6.96 1.04912C5.5 2.79946 2.5 7.60041 2.5 8.42057C2.5 9.24073 5.74 12.9315 7.09 14.0717C7.20224 14.1736 7.34841 14.23 7.5 14.23C7.65159 14.23 7.79776 14.1736 7.91 14.0717C9.26 12.9315 12.5 9.26074 12.5 8.42057C12.5 7.58041 9.5 2.79946 8.04 1.04912ZM7.32 12.1813L4.58 8.53059C4.55791 8.49446 4.54621 8.45292 4.54621 8.41057C4.54621 8.36821 4.55791 8.32668 4.58 8.29054L5.38 6.69023C5.40154 6.66315 5.42891 6.64127 5.46008 6.62624C5.49124 6.6112 5.5254 6.60339 5.56 6.60339C5.5946 6.60339 5.62876 6.6112 5.65992 6.62624C5.69109 6.64127 5.71846 6.66315 5.74 6.69023L7.34 8.26054C7.36057 8.28233 7.38536 8.2997 7.41288 8.31157C7.44039 8.32343 7.47004 8.32956 7.5 8.32956C7.52996 8.32956 7.55961 8.32343 7.58712 8.31157C7.61464 8.2997 7.63943 8.28233 7.66 8.26054L9.28 6.64022C9.30154 6.61314 9.32891 6.59126 9.36008 6.57623C9.39124 6.56119 9.4254 6.55338 9.46 6.55338C9.4946 6.55338 9.52876 6.56119 9.55992 6.57623C9.59109 6.59126 9.61846 6.61314 9.64 6.64022L10.44 8.24054C10.4621 8.27667 10.4738 8.3182 10.4738 8.36056C10.4738 8.40291 10.4621 8.44445 10.44 8.48058L7.68 12.1813C7.6597 12.2102 7.63275 12.2338 7.60142 12.2501C7.57009 12.2663 7.5353 12.2748 7.5 12.2748C7.4647 12.2748 7.42991 12.2663 7.39858 12.2501C7.36725 12.2338 7.3403 12.2102 7.32 12.1813Z" fill="#81E6D9" /></symbol>
<symbol id="damage" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.0714 1.92857V1H12.1429V1.92857L11.2143 2.85714H9.35714V1.92857H8.42857V2.85714H6.57143V1.92857H5.64286V2.85714H3.78571L2.85714 1.92857V1H1.92857V1.92857H1V2.85714H1.92857L2.85714 3.78571V5.64286H1.92857V6.57143H2.85714V7.5H3.78571V6.57143H5.64286L6.57143 5.64286V3.78571H8.42857V5.64286L1 11.2143V14H3.78571L9.35714 6.57143H11.2143V7.5H12.1429V6.57143H13.0714V5.64286H12.1429V3.78571L13.0714 2.85714H14V1.92857H13.0714Z" fill="#E25656" /><path d="M8.02929 9.75643L11.2143 14H14V11.2143L9.75643 8.02929L8.02929 9.75643Z" fill="#E25656" /></symbol>
<symbol id="gold" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M14 5.63324C14 3.57057 11.7157 1.8999 8.89286 1.8999C6.07 1.8999 3.78571 3.57057 3.78571 5.63324C3.7765 5.76685 3.7765 5.90095 3.78571 6.03457C2.12357 6.65057 1 7.90124 1 9.36657C1 11.4292 3.28429 13.0999 6.10714 13.0999C8.93 13.0999 11.2143 11.4292 11.2143 9.36657C11.2235 9.23295 11.2235 9.09885 11.2143 8.96524C12.8486 8.34924 14 7.08924 14 5.63324ZM6.10714 11.3172C4.315 11.3172 2.85714 10.2999 2.85714 8.94657C2.90787 8.55215 3.06303 8.17865 3.3064 7.86507C3.54978 7.55149 3.87244 7.30934 4.24071 7.1639C4.77538 7.8881 5.47991 8.46817 6.29158 8.85247C7.10324 9.23677 7.99686 9.41338 8.89286 9.36657H9.30143C9.03214 10.4679 7.71357 11.3172 6.10714 11.3172Z" fill="#F3A05A" /></symbol>
<symbol id="kill-participation" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.5 1.5C4.18594 1.5 1.5 3.85078 1.5 6.75C1.5 8.39297 2.36484 9.85781 3.71484 10.8211C3.93984 10.9828 4.07109 11.2453 4.03125 11.5219L3.81094 13.0734C3.77813 13.2984 3.95156 13.5 4.17891 13.5H6V12.1875C6 12.0844 6.08437 12 6.1875 12H6.5625C6.66563 12 6.75 12.0844 6.75 12.1875V13.5H8.25V12.1875C8.25 12.0844 8.33437 12 8.4375 12H8.8125C8.91563 12 9 12.0844 9 12.1875V13.5H10.8211C11.0484 13.5 11.2219 13.2984 11.1891 13.0734L10.9688 11.5219C10.9289 11.2477 11.0578 10.9828 11.2852 10.8211C12.6352 9.85781 13.5 8.39297 13.5 6.75C13.5 3.85078 10.8141 1.5 7.5 1.5ZM5.25 9C4.42266 9 3.75 8.32734 3.75 7.5C3.75 6.67266 4.42266 6 5.25 6C6.07734 6 6.75 6.67266 6.75 7.5C6.75 8.32734 6.07734 9 5.25 9ZM9.75 9C8.92266 9 8.25 8.32734 8.25 7.5C8.25 6.67266 8.92266 6 9.75 6C10.5773 6 11.25 6.67266 11.25 7.5C11.25 8.32734 10.5773 9 9.75 9Z" fill="#B78787" /></symbol>
<symbol id="layers" 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"><polygon points="12 2 2 7 12 12 22 7 12 2"></polygon><polyline points="2 17 12 22 22 17"></polyline><polyline points="2 12 12 17 22 12"></polyline></symbol>
<symbol id="people" 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"> <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" /> <circle cx="9" cy="7" r="4" /> <path d="M23 21v-2a4 4 0 0 0-3-3.87" /> <path d="M16 3.13a4 4 0 0 1 0 7.75" /> </symbol>
<symbol id="graph" 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"> <line x1="18" y1="20" x2="18" y2="10" /> <line x1="12" y1="20" x2="12" y2="4" /> <line x1="6" y1="20" x2="6" y2="14" /> </symbol>
<symbol id="info" 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><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></symbol>
<symbol id="award" class="fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M97.12 362.63c-8.69-8.69-4.16-6.24-25.12-11.85-9.51-2.55-17.87-7.45-25.43-13.32L1.2 448.7c-4.39 10.77 3.81 22.47 15.43 22.03l52.69-2.01L105.56 507c8 8.44 22.04 5.81 26.43-4.96l52.05-127.62c-10.84 6.04-22.87 9.58-35.31 9.58-19.5 0-37.82-7.59-51.61-21.37zM382.8 448.7l-45.37-111.24c-7.56 5.88-15.92 10.77-25.43 13.32-21.07 5.64-16.45 3.18-25.12 11.85-13.79 13.78-32.12 21.37-51.62 21.37-12.44 0-24.47-3.55-35.31-9.58L252 502.04c4.39 10.77 18.44 13.4 26.43 4.96l36.25-38.28 52.69 2.01c11.62.44 19.82-11.27 15.43-22.03zM263 340c15.28-15.55 17.03-14.21 38.79-20.14 13.89-3.79 24.75-14.84 28.47-28.98 7.48-28.4 5.54-24.97 25.95-45.75 10.17-10.35 14.14-25.44 10.42-39.58-7.47-28.38-7.48-24.42 0-52.83 3.72-14.14-.25-29.23-10.42-39.58-20.41-20.78-18.47-17.36-25.95-45.75-3.72-14.14-14.58-25.19-28.47-28.98-27.88-7.61-24.52-5.62-44.95-26.41-10.17-10.35-25-14.4-38.89-10.61-27.87 7.6-23.98 7.61-51.9 0-13.89-3.79-28.72.25-38.89 10.61-20.41 20.78-17.05 18.8-44.94 26.41-13.89 3.79-24.75 14.84-28.47 28.98-7.47 28.39-5.54 24.97-25.95 45.75-10.17 10.35-14.15 25.44-10.42 39.58 7.47 28.36 7.48 24.4 0 52.82-3.72 14.14.25 29.23 10.42 39.59 20.41 20.78 18.47 17.35 25.95 45.75 3.72 14.14 14.58 25.19 28.47 28.98C104.6 325.96 106.27 325 121 340c13.23 13.47 33.84 15.88 49.74 5.82a39.676 39.676 0 0 1 42.53 0c15.89 10.06 36.5 7.65 49.73-5.82zM97.66 175.96c0-53.03 42.24-96.02 94.34-96.02s94.34 42.99 94.34 96.02-42.24 96.02-94.34 96.02-94.34-42.99-94.34-96.02z"></path></symbol>
<symbol id="gold" class="fill-current" viewBox="0 0 15 15" fill="fill-current"><title>gold</title><path d="M14 5.63324C14 3.57057 11.7157 1.8999 8.89286 1.8999C6.07 1.8999 3.78571 3.57057 3.78571 5.63324C3.7765 5.76685 3.7765 5.90095 3.78571 6.03457C2.12357 6.65057 1 7.90124 1 9.36657C1 11.4292 3.28429 13.0999 6.10714 13.0999C8.93 13.0999 11.2143 11.4292 11.2143 9.36657C11.2235 9.23295 11.2235 9.09885 11.2143 8.96524C12.8486 8.34924 14 7.08924 14 5.63324ZM6.10714 11.3172C4.315 11.3172 2.85714 10.2999 2.85714 8.94657C2.90787 8.55215 3.06303 8.17865 3.3064 7.86507C3.54978 7.55149 3.87244 7.30934 4.24071 7.1639C4.77538 7.8881 5.47991 8.46817 6.29158 8.85247C7.10324 9.23677 7.99686 9.41338 8.89286 9.36657H9.30143C9.03214 10.4679 7.71357 11.3172 6.10714 11.3172Z" /></symbol>
<symbol id="rank-iron" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 12" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M5.09169 7.28713C5.09174 6.7095 5.22139 6.1391 5.47128 5.61712C5.72117 5.09514 6.08505 4.63461 6.53664 4.26881C4.61235 3.59731 1.81243 2.25945 0.999109 0C0.999109 0 0.826062 2.28858 2.21044 4.84952C2.21044 4.84952 3.68308 5.80538 4.27144 7.09356L3.1276 6.39465C3.41666 7.3131 3.77114 8.21011 4.18838 9.07893C4.85288 10.3448 6.09191 11.3058 6.09191 11.3058C6.09191 11.3058 5.51566 9.59283 6.19054 10.004C6.21382 10.0176 6.23635 10.0325 6.25803 10.0485C5.88788 9.68626 5.59403 9.2549 5.39352 8.77942C5.19302 8.30395 5.08983 7.7938 5.08996 7.27856L5.09169 7.28713ZM11.5123 4.25168C11.9343 4.58914 12.2812 5.00928 12.5314 5.48574C12.7816 5.96221 12.9296 6.48472 12.9662 7.02049C13.0028 7.55626 12.9271 8.09373 12.7439 8.59918C12.5607 9.10462 12.2739 9.56714 11.9016 9.9577C12.4017 9.80182 11.9016 11.3076 11.9016 11.3076C11.9016 11.3076 13.1407 10.3431 13.8052 9.08064C14.2203 8.21155 14.5731 7.31457 14.8608 6.39636L13.7256 7.08157C14.3243 5.80195 15.7831 4.84952 15.7831 4.84952C17.1588 2.28858 16.9944 0 16.9944 0C16.1932 2.2389 13.4348 3.57333 11.5123 4.25168ZM11.7269 7.28712C11.7269 8.75826 10.5221 9.95084 9.03598 9.95084C7.54984 9.95084 6.34509 8.75826 6.34509 7.28712C6.34509 5.81599 7.54984 4.6234 9.03598 4.6234C10.5221 4.6234 11.7269 5.81599 11.7269 7.28712Z" fill="#51484A" /></symbol>
<symbol id="rank-bronze" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 12" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M5.09125 7.28713C5.09142 6.70952 5.22111 6.13916 5.47096 5.6172C5.72081 5.09523 6.08459 4.63469 6.53604 4.26881C4.61196 3.59731 1.81235 2.25945 0.999109 0C0.999109 0 0.82608 2.28858 2.21031 4.84952C2.21031 4.84952 3.68279 5.80538 4.27109 7.09356L3.12737 6.39465C3.41639 7.3131 3.77084 8.21011 4.18804 9.07893C4.85247 10.3466 6.09136 11.3058 6.09136 11.3058C6.09136 11.3058 5.51517 9.59283 6.19171 10.004L6.25746 10.0485C5.88735 9.68626 5.59353 9.2549 5.39305 8.77942C5.19256 8.30395 5.08939 7.7938 5.08952 7.27856L5.09125 7.28713ZM11.5126 4.25168C11.9705 4.6172 12.3399 5.07953 12.5938 5.60477C12.8476 6.13001 12.9796 6.70484 12.9799 7.28713C12.9791 7.63908 12.9308 7.98935 12.8363 8.32863C12.6632 8.93912 12.3418 9.4984 11.9002 9.9577C12.4003 9.80182 11.9002 11.3076 11.9002 11.3076C11.9002 11.3076 13.1374 10.3448 13.8035 9.08064C14.2211 8.21257 14.5755 7.3161 14.8642 6.39808L13.7291 7.08328C14.3226 5.80195 15.783 4.84952 15.783 4.84952C17.1672 2.28858 16.9942 0 16.9942 0C16.1931 2.2389 13.4332 3.57333 11.5126 4.25168ZM11.7254 7.28712C11.7254 8.75826 10.5208 9.95084 9.03485 9.95084C7.54887 9.95084 6.34424 8.75826 6.34424 7.28712C6.34424 5.81599 7.54887 4.6234 9.03485 4.6234C10.5208 4.6234 11.7254 5.81599 11.7254 7.28712Z" fill="#8C513A" /></symbol>
<symbol id="rank-silver" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 12" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M5.09125 7.28713C5.09142 6.70952 5.22111 6.13916 5.47096 5.6172C5.72081 5.09523 6.08459 4.63469 6.53604 4.26881C4.61196 3.59731 1.81235 2.25945 0.999109 0C0.999109 0 0.82608 2.28858 2.21031 4.84952C2.21031 4.84952 3.68279 5.80538 4.27109 7.09356L3.12737 6.39465C3.41639 7.3131 3.77084 8.21011 4.18804 9.07893C4.85247 10.3466 6.09136 11.3058 6.09136 11.3058C6.09136 11.3058 5.51517 9.59283 6.19171 10.004L6.25746 10.0485C5.88735 9.68626 5.59353 9.2549 5.39305 8.77942C5.19256 8.30395 5.08939 7.7938 5.08952 7.27856L5.09125 7.28713ZM11.5126 4.25168C11.9702 4.61729 12.3394 5.07968 12.593 5.60493C12.8466 6.13017 12.9782 6.70496 12.9782 7.28713C12.978 7.639 12.9303 7.98927 12.8363 8.32863C12.6632 8.93912 12.3418 9.4984 11.9002 9.9577C12.4003 9.80182 11.9002 11.3076 11.9002 11.3076C11.9002 11.3076 13.1374 10.3448 13.8035 9.08064C14.2211 8.21257 14.5755 7.3161 14.8642 6.39808L13.7291 7.08328C14.3226 5.80195 15.783 4.84952 15.783 4.84952C17.1672 2.28858 16.9942 0 16.9942 0C16.1931 2.2389 13.4332 3.57333 11.5126 4.25168ZM11.7255 7.28712C11.7255 8.75826 10.5208 9.95084 9.03485 9.95084C7.54887 9.95084 6.34425 8.75826 6.34425 7.28712C6.34425 5.81599 7.54887 4.6234 9.03485 4.6234C10.5208 4.6234 11.7255 5.81599 11.7255 7.28712Z" fill="#80989D" /></symbol>
<symbol id="rank-gold" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 13" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.7388 4.77729C11.2994 5.45163 11.6066 6.31003 11.6048 7.1971C11.6032 7.8058 11.457 8.40488 11.179 8.94233C10.901 9.47977 10.4996 9.93932 10.0096 10.2811C9.89852 10.3595 9.78284 10.4308 9.66325 10.4947C9.42458 10.8456 9.25671 11.2421 9.16955 11.6607C9.08239 12.0794 9.07771 12.5118 9.15578 12.9323C9.30127 11.8586 10.8271 10.4502 11.5078 10.0923C12.1885 9.73442 11.5078 11.4082 11.5078 11.4082C11.5078 11.4082 12.7773 10.5286 13.51 9.30352C13.9738 8.45975 14.3789 7.5833 14.7224 6.68073L13.4649 7.35379C13.7698 5.93645 15.2645 4.72209 15.2645 4.72209C15.8863 3.23542 16.1249 1.60908 15.9573 0C15.3511 2.46254 12.4188 4.04903 10.7388 4.77729ZM4.40497 7.26298C4.40347 6.37598 4.71064 5.51767 5.27096 4.84318C3.59441 4.11492 0.660441 2.5213 0.0559813 0.0658875C-0.111632 1.67555 0.126966 3.30246 0.748772 4.78976C0.748772 4.78976 2.22788 6.00233 2.53271 7.41967L1.27356 6.7484C1.61959 7.65073 2.02764 8.52661 2.4946 9.36941C3.22723 10.5944 4.5037 11.474 4.5037 11.474C4.5037 11.474 3.81091 9.79318 4.5037 10.16C5.19649 10.5268 6.69984 11.9299 6.84533 12.9982C6.9234 12.5777 6.91872 12.1453 6.83156 11.7266C6.7444 11.3079 6.57653 10.9115 6.33786 10.5606C6.21864 10.4954 6.103 10.4235 5.99147 10.3452C5.50334 10.0026 5.10377 9.54289 4.82733 9.00584C4.5509 8.4688 4.40593 7.87059 4.40497 7.26298ZM8.03695 9.81098C9.40576 9.81098 10.5154 8.6702 10.5154 7.26297C10.5154 5.85575 9.40576 4.71497 8.03695 4.71497C6.66813 4.71497 5.55849 5.85575 5.55849 7.26297C5.55849 8.6702 6.66813 9.81098 8.03695 9.81098Z" fill="#CD8837" /></symbol>
<symbol id="rank-platinum" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.7382 7.77729C13.2988 8.45163 13.606 9.31003 13.6042 10.1971C13.6026 10.8058 13.4564 11.4049 13.1784 11.9423C12.9005 12.4798 12.4991 12.9393 12.0091 13.2811C11.898 13.3595 11.7823 13.4308 11.6627 13.4947C11.424 13.8456 11.2561 14.2421 11.169 14.6607C11.0818 15.0794 11.0771 15.5118 11.1552 15.9323C11.3007 14.8586 12.8266 13.4502 13.5072 13.0923C14.1879 12.7344 13.5072 14.4082 13.5072 14.4082C13.5072 14.4082 14.7768 13.5286 15.5094 12.3035C15.9732 11.4598 16.3784 10.5833 16.7218 9.68073L15.4644 10.3538C15.7692 8.93645 17.2639 7.72209 17.2639 7.72209C17.8857 6.23542 18.1243 4.60908 17.9567 3C17.3505 5.46254 14.4183 7.04903 12.7382 7.77729ZM6.40441 10.263C6.40291 9.37598 6.71007 8.51767 7.2704 7.84318C5.59385 7.11492 2.65988 5.5213 2.05542 3.06589C1.8878 4.67555 2.1264 6.30246 2.74821 7.78976C2.74821 7.78976 4.22732 9.00233 4.53214 10.4197L3.273 9.7484C3.61903 10.6507 4.02708 11.5266 4.49404 12.3694C5.22667 13.5944 6.50313 14.474 6.50313 14.474C6.50313 14.474 5.81034 12.7932 6.50313 13.16C7.19592 13.5268 8.69928 14.9299 8.84477 15.9982C8.92284 15.5777 8.91816 15.1453 8.831 14.7266C8.74384 14.3079 8.57597 13.9115 8.3373 13.5606C8.21807 13.4954 8.10243 13.4235 7.9909 13.3452C7.50277 13.0026 7.1032 12.5429 6.82677 12.0058C6.55034 11.4688 6.40537 10.8706 6.40441 10.263ZM10.0364 12.811C11.4052 12.811 12.5148 11.6702 12.5148 10.263C12.5148 8.85575 11.4052 7.71497 10.0364 7.71497C8.66757 7.71497 7.55792 8.85575 7.55792 10.263C7.55792 11.6702 8.66757 12.811 10.0364 12.811Z" fill="#4E9996" /></symbol>
<symbol id="rank-emerald" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.28209 7.77729C6.7215 8.45163 6.41429 9.31003 6.4161 10.1971C6.41774 10.8058 6.5639 11.4049 6.84189 11.9423C7.11988 12.4798 7.52129 12.9393 8.01126 13.2811C8.12238 13.3595 8.23806 13.4308 8.35765 13.4947C8.59632 13.8456 8.76419 14.2421 8.85135 14.6607C8.93851 15.0794 8.94319 15.5118 8.86512 15.9323C8.71963 14.8586 7.19376 13.4502 6.51309 13.0923C5.83243 12.7344 6.51309 14.4082 6.51309 14.4082C6.51309 14.4082 5.3646 13.5605 4.33093 11.9941C4.31699 11.973 4.30313 11.9519 4.28935 11.931C3.62867 10.9293 3.1492 10.2023 2.13162 9.92188C3.11722 9.58748 4.55596 10.3538 4.55596 10.3538C4.25113 8.93645 2.75644 7.72209 2.75644 7.72209C2.13463 6.23542 1.89602 4.60908 2.06365 3C2.66984 5.46254 5.60208 7.04903 7.28209 7.77729ZM13.6042 10.1971C13.606 9.31003 13.2988 8.45163 12.7382 7.77729C14.4183 7.04903 17.3505 5.46254 17.9567 3C18.1243 4.60908 17.8857 6.23542 17.2639 7.72209C17.2639 7.72209 15.7692 8.93645 15.4644 10.3538C15.4644 10.3538 16.9031 9.58748 17.8887 9.92188C16.8711 10.2023 16.3917 10.9293 15.731 11.931L15.6894 11.9941C14.6557 13.5605 13.5072 14.4082 13.5072 14.4082C13.5072 14.4082 14.1879 12.7344 13.5072 13.0923C12.8266 13.4502 11.3007 14.8586 11.1552 15.9323C11.0771 15.5118 11.0818 15.0794 11.169 14.6607C11.2561 14.2421 11.424 13.8456 11.6627 13.4947C11.7823 13.4308 11.898 13.3595 12.0091 13.2811C12.4991 12.9393 12.9005 12.4798 13.1785 11.9423C13.4564 11.4049 13.6026 10.8058 13.6042 10.1971ZM10.0364 12.811C11.4052 12.811 12.5149 11.6702 12.5149 10.263C12.5149 8.85575 11.4052 7.71497 10.0364 7.71497C8.66758 7.71497 7.55794 8.85575 7.55794 10.263C7.55794 11.6702 8.66758 12.811 10.0364 12.811Z" fill="#149C3A" /></symbol>
<symbol id="rank-diamond" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.26501 7.77728C6.70441 8.45162 6.39721 9.31003 6.39902 10.1971C6.40066 10.8058 6.54682 11.4049 6.8248 11.9423C7.10279 12.4798 7.5042 12.9393 7.99417 13.2811C8.10529 13.3595 8.22097 13.4308 8.34056 13.4947C8.57923 13.8456 8.74711 14.2421 8.83427 14.6607C8.92142 15.0794 8.92611 15.5118 8.84803 15.9323C8.70255 14.8586 7.17668 13.4502 6.49601 13.0923C5.81534 12.7344 6.49601 14.4082 6.49601 14.4082C4.85133 13.4099 4.53293 12.4616 4.21748 11.5221C3.98661 10.8345 3.75732 10.1516 3.01078 9.45731C4.15628 9.79612 4.53888 10.3538 4.53888 10.3538C4.39221 8.54614 2.51869 7.99745 2.51869 7.99745C2.27947 7.16721 2.14678 6.49021 2.0302 5.89533C1.81547 4.79969 1.65534 3.98265 0.984772 3C4.28701 3.89704 5.63879 5.52611 7.26501 7.77728ZM13.6043 10.1971C13.6061 9.31003 13.2989 8.45163 12.7383 7.77729C14.3645 5.52612 15.7163 3.89704 19.0185 3.00001C18.3479 3.98265 18.1878 4.7997 17.9731 5.89534C17.8565 6.49021 17.7238 7.16722 17.4846 7.99745C17.4846 7.99745 15.6111 8.54615 15.4644 10.3538C15.4644 10.3538 15.847 9.79612 16.9925 9.45731C16.2459 10.1516 16.0167 10.8345 15.7858 11.5221C15.4703 12.4616 15.1519 13.4099 13.5073 14.4082C13.5073 14.4082 14.1879 12.7344 13.5073 13.0923C12.8266 13.4502 11.3007 14.8586 11.1552 15.9323C11.0772 15.5118 11.0818 15.0794 11.169 14.6607C11.2562 14.2421 11.424 13.8456 11.6627 13.4947C11.7823 13.4308 11.898 13.3595 12.0091 13.2811C12.4991 12.9393 12.9005 12.4798 13.1785 11.9423C13.4565 11.4049 13.6026 10.8058 13.6043 10.1971ZM10.0364 12.811C11.4052 12.811 12.5149 11.6702 12.5149 10.263C12.5149 8.85575 11.4052 7.71497 10.0364 7.71497C8.66759 7.71497 7.55795 8.85575 7.55795 10.263C7.55795 11.6702 8.66759 12.811 10.0364 12.811Z" fill="#576BCE" /></symbol>
<symbol id="rank-master" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 15" fill="none"><path d="M7.61488 15C7.90271 14.2471 8.14287 13.4744 8.33375 12.6872C7.75357 12.5611 7.21753 12.2657 6.78526 11.8339C6.353 11.402 6.04145 10.8506 5.88528 10.2411C5.72911 9.63153 5.73445 8.98768 5.90068 8.38116C6.06692 7.77464 6.38755 7.22924 6.82689 6.80561C5.56024 5.40575 3.3708 2.56363 3.76479 0C3.76479 0 2.61564 1.82958 2.60009 4.64589C2.60009 4.64589 3.26884 5.95906 3.29131 7.23903C2.37026 6.36297 1.39045 5.10697 1.36799 3.73478C1.36799 3.73478 1.32479 3.84913 1.26776 4.05201L1.1347 4.63667C1.05957 5.0581 1.01967 5.48582 1.01547 5.91479C0.99224 6.36754 0.99224 6.82132 1.01547 7.27407C1.07101 8.50634 1.35406 9.71476 1.84839 10.83C1.84839 10.83 2.08167 10.8816 2.45148 10.9867C3.41745 11.2486 4.76188 11.7245 5.82808 12.4825L5.92658 12.5544C6.75777 13.1501 7.41616 13.9137 7.5233 14.8672" fill="#A4584E" /><path d="M11.1591 9.33981C11.1594 8.8783 11.0315 8.42704 10.7915 8.04312C10.5515 7.6592 10.2102 7.35988 9.81083 7.18301C9.41142 7.00614 8.97184 6.95967 8.54769 7.04949C8.12354 7.1393 7.73387 7.36136 7.42798 7.68758C7.12209 8.01379 6.91373 8.4295 6.82924 8.88213C6.74475 9.33475 6.78794 9.80395 6.95334 10.2304C7.11874 10.6568 7.39893 11.0213 7.75845 11.2777C8.11797 11.5342 8.54067 11.6711 8.97309 11.6711C9.55241 11.6706 10.1079 11.4249 10.5177 10.9878C10.9275 10.5508 11.1582 9.95812 11.1591 9.33981Z" fill="#9D48E0" /><path d="M10.3849 15C10.0971 14.2471 9.8569 13.4744 9.66602 12.6872C10.2462 12.5611 10.7822 12.2657 11.2145 11.8339C11.6468 11.402 11.9583 10.8506 12.1145 10.2411C12.2707 9.63153 12.2653 8.98768 12.0991 8.38116C11.9328 7.77464 11.6122 7.22924 11.1729 6.80561C12.4395 5.40575 14.629 2.56363 14.235 0C14.235 0 15.3841 1.82958 15.398 4.64589C15.398 4.64589 14.7309 5.95906 14.7067 7.23903C15.6278 6.36297 16.6076 5.10697 16.63 3.73478C16.63 3.73478 16.6733 3.84913 16.7303 4.05201L16.8616 4.63298C16.9383 5.05421 16.9788 5.48202 16.9826 5.9111C17.0058 6.36386 17.0058 6.81763 16.9826 7.27038C16.9287 8.50227 16.6474 9.71068 16.1548 10.8263C16.1548 10.8263 15.9198 10.8779 15.5517 10.983C14.584 11.2449 13.2413 11.7208 12.1751 12.4788L12.0749 12.5507C11.2437 13.1464 10.5853 13.91 10.4782 14.8635" fill="#A4584E" /></symbol>
<symbol id="rank-grandmaster" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 15" fill="none"><path d="M7.61488 15C7.90271 14.2471 8.14287 13.4744 8.33375 12.6872C7.75357 12.5611 7.21753 12.2657 6.78526 11.8339C6.353 11.402 6.04145 10.8506 5.88528 10.2411C5.72911 9.63153 5.73445 8.98768 5.90068 8.38116C6.06692 7.77464 6.38755 7.22924 6.82689 6.80561C5.56024 5.40575 3.3708 2.56363 3.76479 0C3.76479 0 2.61564 1.82958 2.60009 4.64589C2.60009 4.64589 3.26884 5.95906 3.29131 7.23903C2.37026 6.36297 1.39045 5.10697 1.36799 3.73478C1.36799 3.73478 1.32479 3.84913 1.26776 4.05201L1.1347 4.63667C1.05957 5.0581 1.01967 5.48582 1.01547 5.91479C0.99224 6.36754 0.99224 6.82132 1.01547 7.27407C1.07101 8.50634 1.35406 9.71476 1.84839 10.83C1.84839 10.83 2.08167 10.8816 2.45148 10.9867C3.41745 11.2486 4.76188 11.7245 5.82808 12.4825L5.92658 12.5544C6.75777 13.1501 7.41616 13.9137 7.5233 14.8672" fill="#756572" /><path d="M11.1591 9.33981C11.1594 8.8783 11.0315 8.42704 10.7915 8.04312C10.5515 7.6592 10.2102 7.35988 9.81083 7.18301C9.41142 7.00614 8.97184 6.95967 8.54769 7.04949C8.12354 7.1393 7.73387 7.36136 7.42798 7.68758C7.12209 8.01379 6.91373 8.4295 6.82924 8.88213C6.74475 9.33475 6.78794 9.80395 6.95334 10.2304C7.11874 10.6568 7.39893 11.0213 7.75845 11.2777C8.11797 11.5342 8.54067 11.6711 8.97309 11.6711C9.55241 11.6706 10.1079 11.4249 10.5177 10.9878C10.9275 10.5508 11.1582 9.95812 11.1591 9.33981Z" fill="#CD4545" /><path d="M10.3849 15C10.0971 14.2471 9.8569 13.4744 9.66602 12.6872C10.2462 12.5611 10.7822 12.2657 11.2145 11.8339C11.6468 11.402 11.9583 10.8506 12.1145 10.2411C12.2707 9.63153 12.2653 8.98768 12.0991 8.38116C11.9328 7.77464 11.6122 7.22924 11.1729 6.80561C12.4395 5.40575 14.629 2.56363 14.235 0C14.235 0 15.3841 1.82958 15.398 4.64589C15.398 4.64589 14.7309 5.95906 14.7067 7.23903C15.6278 6.36297 16.6076 5.10697 16.63 3.73478C16.63 3.73478 16.6733 3.84913 16.7303 4.05201L16.8616 4.63298C16.9383 5.05421 16.9788 5.48202 16.9826 5.9111C17.0058 6.36386 17.0058 6.81763 16.9826 7.27038C16.9287 8.50227 16.6474 9.71068 16.1548 10.8263C16.1548 10.8263 15.9198 10.8779 15.5517 10.983C14.584 11.2449 13.2413 11.7208 12.1751 12.4788L12.0749 12.5507C11.2437 13.1464 10.5853 13.91 10.4782 14.8635" fill="#756572" /></symbol>
<symbol id="rank-challenger" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 15" fill="none"><path d="M7.61488 15C7.90271 14.2471 8.14287 13.4744 8.33375 12.6872C7.75357 12.5611 7.21753 12.2657 6.78526 11.8339C6.353 11.402 6.04145 10.8506 5.88528 10.2411C5.72911 9.63153 5.73445 8.98768 5.90068 8.38116C6.06692 7.77464 6.38755 7.22924 6.82689 6.80561C5.56024 5.40575 3.3708 2.56363 3.76479 0C3.76479 0 2.61564 1.82958 2.60009 4.64589C2.60009 4.64589 3.26884 5.95906 3.29131 7.23903C2.37026 6.36297 1.39045 5.10697 1.36799 3.73478C1.36799 3.73478 1.32479 3.84913 1.26776 4.05201L1.1347 4.63667C1.05957 5.0581 1.01967 5.48582 1.01547 5.91479C0.99224 6.36754 0.99224 6.82132 1.01547 7.27407C1.07101 8.50634 1.35406 9.71476 1.84839 10.83C1.84839 10.83 2.08167 10.8816 2.45148 10.9867C3.41745 11.2486 4.76188 11.7245 5.82808 12.4825L5.92658 12.5544C6.75777 13.1501 7.41616 13.9137 7.5233 14.8672" fill="#F4C874" /><path d="M11.1591 9.33981C11.1594 8.8783 11.0315 8.42704 10.7915 8.04312C10.5515 7.6592 10.2102 7.35988 9.81083 7.18301C9.41142 7.00614 8.97184 6.95967 8.54769 7.04949C8.12354 7.1393 7.73387 7.36136 7.42798 7.68758C7.12209 8.01379 6.91373 8.4295 6.82924 8.88213C6.74475 9.33475 6.78794 9.80395 6.95334 10.2304C7.11874 10.6568 7.39893 11.0213 7.75845 11.2777C8.11797 11.5342 8.54067 11.6711 8.97309 11.6711C9.55241 11.6706 10.1079 11.4249 10.5177 10.9878C10.9275 10.5508 11.1582 9.95812 11.1591 9.33981Z" fill="#3FBFDD" /><path d="M10.3849 15C10.0971 14.2471 9.8569 13.4744 9.66602 12.6872C10.2462 12.5611 10.7822 12.2657 11.2145 11.8339C11.6468 11.402 11.9583 10.8506 12.1145 10.2411C12.2707 9.63153 12.2653 8.98768 12.0991 8.38116C11.9328 7.77464 11.6122 7.22924 11.1729 6.80561C12.4395 5.40575 14.629 2.56363 14.235 0C14.235 0 15.3841 1.82958 15.398 4.64589C15.398 4.64589 14.7309 5.95906 14.7067 7.23903C15.6278 6.36297 16.6076 5.10697 16.63 3.73478C16.63 3.73478 16.6733 3.84913 16.7303 4.05201L16.8616 4.63298C16.9383 5.05421 16.9788 5.48202 16.9826 5.9111C17.0058 6.36386 17.0058 6.81763 16.9826 7.27038C16.9287 8.50227 16.6474 9.71068 16.1548 10.8263C16.1548 10.8263 15.9198 10.8779 15.5517 10.983C14.584 11.2449 13.2413 11.7208 12.1751 12.4788L12.0749 12.5507C11.2437 13.1464 10.5853 13.91 10.4782 14.8635" fill="#F4C874" /></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>
</svg>
</template>

View file

@ -1,165 +0,0 @@
<template>
<footer class="pt-20 pb-4 text-blue-200">
<p
class="absolute leading-tight text-center pointer-events-none horizontal-center text-xxs"
style="color: #5d80af"
>
LeagueStats.gg isn't endorsed by Riot Games and doesn't reflect the views or opinions of Riot
Games or anyone officially involved in producing or managing Riot Games properties.
<br />Riot Games, and all associated properties are trademarks or registered trademarks of
Riot Games, Inc.
</p>
<div class="flex items-center justify-between px-6">
<p>
<a
href="https://m.do.co/c/4f4a6c382133"
class="block w-32 transition duration-200 hover:text-white"
target="_blank"
>
<svg class="fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 604 129">
<path
d="M174.3,3c4.9,0,8.7,2.9,8.7,8.6c0,5.6-3.8,8.5-8.7,8.5h-7.6v11.1h-3.5V3H174.3z M166.7,17.1h7.2
c3,0,5.6-1.8,5.6-5.5c0-3.8-2.5-5.5-5.6-5.5h-7.2V17.1z"
/>
<path
d="M208.8,21.7c0,6.1-4.3,10-9.9,10c-5.6,0-9.9-3.9-9.9-10c0-6.1,4.3-10,9.9-10
C204.5,11.7,208.8,15.6,208.8,21.7z M192.3,21.7c0,4.5,2.9,7.2,6.6,7.2c3.7,0,6.6-2.7,6.6-7.2c0-4.5-2.9-7.1-6.6-7.1
C195.2,14.5,192.3,17.2,192.3,21.7z"
/>
<path
d="M234.4,31.3l-5.2-13.8L224,31.3h-2.6L214.1,12h3.6l5.2,14l5.2-14h2.3l5.3,14l5.2-14h3.5L237,31.3H234.4z"
/>
<path
d="M253,22.9c0.2,3.7,2.6,5.9,6,5.9c2.8,0,4.8-1.3,5.4-3.4l3.2,0.2c-0.8,3.5-4.1,6.1-8.6,6.1
c-5.5,0-9.6-3.7-9.6-10c0-6.3,4-10,9.5-10c5.5,0,8.8,3.7,8.8,9.4v1.8H253z M253,20.3h11.6c-0.1-3.4-2-5.7-5.6-5.7
C255.6,14.5,253.2,16.5,253,20.3z"
/>
<path
d="M285.4,14.9c-3.4,0-5.6,2.3-5.6,5.3v11.1h-3.2V12h3.2v2.9c0.7-1.6,2.5-3.1,5.7-3.1V14.9z"
/>
<path
d="M294.7,22.9c0.2,3.7,2.6,5.9,6,5.9c2.8,0,4.8-1.3,5.4-3.4l3.2,0.2c-0.8,3.5-4.1,6.1-8.6,6.1
c-5.5,0-9.6-3.7-9.6-10c0-6.3,4-10,9.5-10c5.5,0,8.8,3.7,8.8,9.4v1.8H294.7z M294.7,20.3h11.6c-0.1-3.4-2-5.7-5.6-5.7
C297.4,14.5,294.9,16.5,294.7,20.3z"
/>
<path
d="M333.1,31.3v-3.1c-1.1,2-3.6,3.5-6.8,3.5c-5.3,0-9.3-3.8-9.3-10c0-6.2,4-10,9.3-10c3.2,0,5.6,1.4,6.6,3.2V2
h3.2v29.4H333.1z M320.3,21.7c0,4.6,2.8,7.2,6.5,7.2c3.6,0,6.2-2.2,6.2-6.6v-1.1c0-4.3-2.6-6.6-6.2-6.6
C323.1,14.5,320.3,17.1,320.3,21.7z"
/>
<path
d="M361.8,14.9c1.1-1.9,3.4-3.2,6.7-3.2c5.3,0,9.3,3.8,9.3,10c0,6.2-4,10-9.3,10c-3.3,0-5.7-1.5-6.8-3.5v3.1
h-3.1V2h3.2V14.9z M361.9,21.1v1.1c0,4.4,2.6,6.6,6.2,6.6c3.7,0,6.5-2.5,6.5-7.2c0-4.6-2.8-7.1-6.5-7.1
C364.5,14.5,361.9,16.8,361.9,21.1z"
/>
<path d="M386.3,40.9l4.6-10.7L383.2,12h3.6l5.8,14.5l5.8-14.5h3.6l-12.2,28.9H386.3z" />
<path
d="M64.4,127l0-24.2c25.6,0,45.5-25.4,35.7-52.3c-3.6-10-11.6-17.9-21.6-21.6
c-27-9.8-52.3,10-52.3,35.7c0,0,0,0,0,0L2,64.7C2,23.8,41.5-8,84.3,5.4c18.7,5.8,33.6,20.7,39.4,39.4
C137,87.6,105.2,127,64.4,127z"
/>
<polygon points="64.4,102.9 40.4,102.9 40.4,78.9 40.4,78.9 64.4,78.9 64.4,78.9" />
<polygon points="40.3,121.5 21.8,121.5 21.8,121.5 21.8,102.9 40.4,102.9 40.4,121.5" />
<path d="M21.9,102.9H6.3c0,0,0,0,0,0V87.4c0,0,0,0,0,0h15.5c0,0,0,0,0,0V102.9z" />
<path
d="M200.9,52.4c-5.5-3.8-12.4-5.8-20.5-5.8h-17.5v55.5h17.5c8,0,14.9-2.1,20.5-6.1
c3-2.1,5.4-5.1,7.1-8.9c1.7-3.7,2.5-8.2,2.5-13.1c0-4.9-0.8-9.3-2.5-13C206.3,57.4,203.9,54.4,200.9,52.4z M173.1,56h5.5
c6.1,0,11.1,1.2,15,3.6c4.2,2.6,6.4,7.4,6.4,14.4c0,7.2-2.2,12.3-6.4,15.1h0c-3.7,2.4-8.7,3.6-14.9,3.6h-5.6V56z"
/>
<path
d="M222.6,45.9c-1.7,0-3.1,0.6-4.3,1.8c-1.2,1.1-1.8,2.6-1.8,4.2c0,1.7,0.6,3.1,1.8,4.3
c1.2,1.2,2.6,1.8,4.3,1.8c1.7,0,3.1-0.6,4.3-1.8c1.2-1.2,1.8-2.6,1.8-4.3c0-1.7-0.6-3.1-1.8-4.2
C225.7,46.5,224.3,45.9,222.6,45.9z"
/>
<rect x="217.6" y="63" class="st0" width="9.8" height="39.1" />
<path
d="M263.2,66.3c-3-2.6-6.3-4.2-9.9-4.2c-5.4,0-9.9,1.9-13.4,5.6c-3.5,3.7-5.3,8.4-5.3,14.1
c0,5.5,1.8,10.2,5.2,14c3.5,3.7,8,5.5,13.5,5.5c3.8,0,7.1-1.1,9.7-3.1V99c0,3.2-0.9,5.8-2.6,7.5c-1.7,1.7-4.1,2.6-7.1,2.6
c-4.5,0-7.4-1.8-10.9-6.5l-6.7,6.4l0.2,0.3c1.4,2,3.7,4,6.6,5.9c2.9,1.9,6.6,2.8,10.9,2.8c5.8,0,10.6-1.8,14.1-5.4
c3.5-3.6,5.3-8.4,5.3-14.2V63h-9.7V66.3z M260.6,89.4c-1.7,2-3.9,2.9-6.8,2.9c-2.8,0-5-0.9-6.7-2.9c-1.7-1.9-2.5-4.5-2.5-7.7
c0-3.2,0.9-5.8,2.5-7.7c1.7-1.9,3.9-2.9,6.7-2.9c2.8,0,5,1,6.8,2.9c1.7,2,2.6,4.6,2.6,7.7C263.2,84.9,262.3,87.5,260.6,89.4z"
/>
<rect x="281.3" y="63" class="st0" width="9.8" height="39.1" />
<path
d="M286.3,45.9c-1.7,0-3.1,0.6-4.3,1.8c-1.2,1.1-1.8,2.6-1.8,4.2c0,1.7,0.6,3.1,1.8,4.3
c1.2,1.2,2.6,1.8,4.3,1.8c1.7,0,3.1-0.6,4.3-1.8c1.2-1.2,1.8-2.6,1.8-4.3c0-1.7-0.6-3.1-1.8-4.2C289.4,46.5,288,45.9,286.3,45.9
z"
/>
<path
d="M312.7,52.5H303V63h-5.6v9h5.6v16.2c0,5.1,1,8.7,3,10.8c2,2.1,5.6,3.2,10.6,3.2
c1.6,0,3.2-0.1,4.8-0.2l0.4,0v-9l-3.4,0.2c-2.3,0-3.9-0.4-4.7-1.2c-0.8-0.8-1.1-2.6-1.1-5.2V72h9.2v-9h-9.2V52.5z"
/>
<rect x="368" y="46.6" class="st0" width="9.8" height="55.5" />
<path
d="M477.3,88.2c-1.8,2-3.6,3.7-4.9,4.6v0c-1.4,0.9-3.1,1.3-5.1,1.3c-2.9,0-5.2-1.1-7.1-3.2
c-1.9-2.2-2.8-4.9-2.8-8.3s0.9-6.1,2.8-8.2c1.9-2.2,4.2-3.2,7.1-3.2c3.2,0,6.5,2,9.4,5.4l6.5-6.2l0,0c-4.2-5.5-9.7-8.1-16.1-8.1
c-5.4,0-10.1,2-13.9,5.8c-3.8,3.9-5.7,8.8-5.7,14.6s1.9,10.7,5.7,14.6c3.8,3.9,8.5,5.9,13.9,5.9c7.1,0,12.9-3.1,16.8-8.7
L477.3,88.2z"
/>
<path
d="M517.7,68.5c-1.4-1.9-3.3-3.5-5.7-4.7c-2.3-1.1-5.1-1.7-8.1-1.7c-5.5,0-10,2-13.4,6
c-3.3,4-4.9,8.9-4.9,14.7c0,5.9,1.8,10.8,5.4,14.6c3.6,3.7,8.4,5.6,14.2,5.6c6.6,0,12.1-2.7,16.2-8l0.2-0.3l-6.4-6.2l0,0
c-0.6,0.7-1.4,1.5-2.2,2.3c-1,0.9-1.9,1.6-2.9,2.1c-1.5,0.7-3.1,1.1-5,1.1c-2.7,0-5-0.8-6.7-2.4c-1.6-1.5-2.6-3.5-2.8-5.9h26.1
l0.1-3.6c0-2.5-0.3-5-1-7.3C520.1,72.6,519.1,70.4,517.7,68.5z M496.2,77.7c0.5-1.9,1.3-3.4,2.6-4.6c1.3-1.3,3.1-2,5.2-2
c2.4,0,4.2,0.7,5.5,2c1.2,1.2,1.8,2.8,2,4.6H496.2z"
/>
<path
d="M555.5,66L555.5,66c-3-2.5-7.1-3.8-12.3-3.8c-3.3,0-6.3,0.7-9.1,2.1
c-2.6,1.3-5.1,3.5-6.7,6.3l0.1,0.1l6.3,6c2.6-4.1,5.5-5.6,9.3-5.6c2.1,0,3.8,0.6,5.1,1.6c1.3,1.1,1.9,2.5,1.9,4.2v1.9
c-2.4-0.7-4.9-1.1-7.2-1.1c-4.9,0-8.9,1.2-11.8,3.4c-3,2.3-4.5,5.6-4.5,9.8c0,3.7,1.3,6.7,3.8,8.9c2.6,2.1,5.8,3.2,9.5,3.2
c3.7,0,7.3-1.5,10.4-4.1v3.2h9.7V77C560,72.2,558.5,68.5,555.5,66z M538,87.2c1.1-0.8,2.7-1.2,4.7-1.2c2.4,0,4.9,0.5,7.5,1.4
v3.8c-2.1,2-5,3-8.5,3c-1.7,0-3-0.4-3.9-1.1c-0.9-0.7-1.3-1.7-1.3-2.8C536.4,89,536.9,88,538,87.2z"
/>
<path
d="M597.9,66.7c-2.7-3.1-6.6-4.6-11.5-4.6c-3.9,0-7.1,1.1-9.4,3.3V63h-9.7v39.1h9.8V80.6
c0-3,0.7-5.3,2.1-7c1.4-1.7,3.3-2.5,5.8-2.5c2.2,0,3.9,0.7,5.2,2.2c1.3,1.5,1.9,3.6,1.9,6.2v22.7h9.8V79.5
C602,74.1,600.6,69.8,597.9,66.7z"
/>
<path
d="M355.6,66L355.6,66c-3-2.5-7.1-3.8-12.3-3.8c-3.3,0-6.3,0.7-9.1,2.1
c-2.6,1.3-5.1,3.5-6.7,6.3l0.1,0.1l6.3,6c2.6-4.1,5.5-5.6,9.3-5.6c2.1,0,3.8,0.6,5.1,1.6c1.3,1.1,1.9,2.5,1.9,4.2v1.9
c-2.4-0.7-4.9-1.1-7.2-1.1c-4.9,0-8.9,1.2-11.8,3.4c-3,2.3-4.5,5.6-4.5,9.8c0,3.7,1.3,6.7,3.8,8.9c2.6,2.1,5.8,3.2,9.5,3.2
c3.7,0,7.3-1.5,10.4-4.1v3.2h9.7V77C360.2,72.2,358.7,68.5,355.6,66z M338.2,87.2c1.1-0.8,2.7-1.2,4.7-1.2
c2.4,0,4.9,0.5,7.5,1.4v3.8c-2.1,2-5,3-8.5,3c-1.7,0-3-0.4-3.9-1.1c-0.9-0.7-1.3-1.7-1.3-2.8C336.6,89,337.1,88,338.2,87.2z"
/>
<path
d="M413.6,103c-15.8,0-28.6-12.8-28.6-28.6s12.8-28.6,28.6-28.6s28.6,12.8,28.6,28.6
S429.4,103,413.6,103z M413.6,55.8c-10.2,0-18.5,8.3-18.5,18.5s8.3,18.5,18.5,18.5s18.5-8.3,18.5-18.5S423.8,55.8,413.6,55.8z"
/>
</svg>
</a>
</p>
<a
class="relative text-sm github"
href="https://github.com/vkaelin/LeagueStats"
target="_blank"
>
<svg class="absolute fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"
/>
</svg>
<span class="ml-8">Open Source Project</span>
</a>
</div>
</footer>
</template>
<style scoped>
.github svg {
width: 22px;
height: 22px;
transform-origin: bottom left;
transition: 0.2s ease-in-out;
}
.github:hover svg {
width: 24px;
height: 24px;
transform: rotate(-5deg);
}
.github:hover span {
color: #ebf8ff;
}
</style>

View file

@ -1,108 +0,0 @@
<template>
<transition name="slide">
<div v-if="data.status === 'loaded' && detailsOpen" class="bg-blue-800 rounded-b-lg">
<DetailedMatchTeam
:data="allyTeam"
:all-players="[...allyTeam.players, ...enemyTeam.players]"
:ally-team="true"
:ranks-loaded="data.ranksLoaded"
/>
<div class="flex items-start justify-between px-3 py-2">
<DetailedMatchGlobalStats :team="allyTeam" :ally-team="true" />
<SwitchToggle
@updateValue="updatePercent"
left-label="%"
right-label="Total"
:value="percentSettings"
class="mt-2"
></SwitchToggle>
<DetailedMatchGlobalStats :team="enemyTeam" :ally-team="false" />
</div>
<DetailedMatchTeam
:data="enemyTeam"
:all-players="[...allyTeam.players, ...enemyTeam.players]"
:ally-team="false"
:ranks-loaded="data.ranksLoaded"
/>
</div>
<div v-else-if="data.status === 'loading' && detailsOpen">
<div class="py-5 bg-blue-800 rounded-b-lg">
<CubeLoader />
</div>
</div>
</transition>
</template>
<script>
import { mapActions, mapState } from 'vuex'
import CubeLoader from '@/components/Common/CubeLoader.vue'
import DetailedMatchGlobalStats from '@/components/Match/DetailedMatchGlobalStats.vue'
import DetailedMatchTeam from '@/components/Match/DetailedMatchTeam.vue'
import SwitchToggle from '@/components/Form/SwitchToggle.vue'
export default {
components: {
CubeLoader,
DetailedMatchGlobalStats,
DetailedMatchTeam,
SwitchToggle,
},
props: {
data: {
type: Object,
required: true,
},
detailsOpen: {
type: Boolean,
required: true,
},
},
computed: {
allyTeam() {
return this.data.blueTeam.players.some((p) => p.summonerId === this.account.id)
? this.data.blueTeam
: this.data.redTeam
},
enemyTeam() {
return this.data.blueTeam.players.some((p) => p.summonerId === this.account.id)
? this.data.redTeam
: this.data.blueTeam
},
...mapState({
account: (state) => state.summoner.basic.account,
percentSettings: (state) => state.settings.percent,
}),
},
methods: {
...mapActions('settings', ['updatePercent']),
},
}
</script>
<style scoped>
.slide-enter-active {
transition-duration: 0.3s;
transition-timing-function: ease-in;
}
.slide-leave-active {
transition-duration: 0.3s;
transition-timing-function: cubic-bezier(0, 1, 0.5, 1);
}
.slide-enter-to,
.slide-leave {
max-height: 737px;
overflow: hidden;
}
.slide-enter,
.slide-leave-to {
overflow: hidden;
max-height: 0;
}
</style>

View file

@ -1,105 +0,0 @@
<template>
<div :class="allyTeam ? 'text-left' : 'text-right'">
<div v-if="team.bans">
<Tooltip
v-for="ban in team.bans"
:key="'ban-' + ban.pickTurn"
:class="{ 'ml-2': ban.pickTurn !== 6 && ban.pickTurn !== 1 }"
class="inline-block"
>
<template #trigger>
<div
:class="[allyTeam ? 'ban-blue border-teal-500' : 'ban-red border-red-500']"
class="relative border-2 rounded-full cursor-pointer ban"
>
<div
:style="[ban.champion.id ? { backgroundImage: `url('${ban.champion.icon}')` } : '']"
class="w-6 h-6 bg-center bg-cover rounded-full ban-img bg-blue-1000"
></div>
<div
:class="[textLightColor, bgColor]"
class="absolute flex items-center justify-center w-4 h-4 text-xs font-bold rounded-full ban-order"
>
{{ ban.pickTurn }}
</div>
</div>
</template>
<template #default>
<div class="px-2 text-xs leading-tight text-center text-white select-none">
<div>{{ ban.champion.id ? ban.champion.name : 'No ban' }}</div>
</div>
</template>
</Tooltip>
</div>
<div
:class="allyTeam ? 'text-left' : 'text-right flex-row-reverse'"
class="flex mt-2 leading-tight"
>
<div>
<div :class="textColor" class="text-sm font-medium">
{{ `${team.teamStats.kills}/${team.teamStats.deaths}/${team.teamStats.assists}` }}
</div>
<div class="text-xs text-white">K / D / A</div>
</div>
<div :class="allyTeam ? 'ml-3' : 'mr-3'">
<div :class="textColor" class="text-sm font-medium">
{{ +(team.teamStats.gold / 1000).toFixed(1) + 'k' }}
</div>
<div class="text-xs text-white">Gold</div>
</div>
<div :class="allyTeam ? 'ml-3' : 'mr-3'">
<div :class="textColor" class="text-sm font-medium">
{{ +(team.teamStats.dmgChamp / 1000).toFixed(1) + 'k' }}
</div>
<div class="text-xs text-white">Dmg</div>
</div>
<div :class="allyTeam ? 'ml-3' : 'mr-3'" class="flex flex-col justify-end">
<div class="text-sm font-medium text-teal-400"></div>
<div class="flex text-xs text-white">
<div :class="allyTeam ? '' : 'mr-2'">
<span :class="textColor">{{ team.towers }}</span> Towers
</div>
<div :class="allyTeam ? 'ml-2' : 'mr-2'">
<span :class="textColor">{{ team.dragons }}</span> Dragons
</div>
<div :class="allyTeam ? 'ml-2' : ''">
<span :class="textColor">{{ team.barons }}</span> Barons
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Tooltip from '@/components/Common/Tooltip.vue'
export default {
components: {
Tooltip,
},
props: {
team: {
type: Object,
required: true,
},
allyTeam: {
type: Boolean,
default: false,
},
},
computed: {
textLightColor() {
return this.allyTeam ? 'text-teal-100' : 'text-red-100'
},
textColor() {
return this.allyTeam ? 'text-teal-400' : 'text-red-400'
},
bgColor() {
return this.allyTeam ? 'bg-teal-500' : 'bg-red-500'
},
},
}
</script>

View file

@ -1,459 +0,0 @@
<template>
<table
:class="[{ 'rounded-b-lg overflow-hidden': !allyTeam }, data.result]"
class="w-full table-fixed"
>
<thead class="leading-none heading-detailed">
<tr
:style="getHeadingColor(data.result)"
class="relative font-semibold text-blue-200 heading-row"
>
<th class="py-5 w-players">
<div class="flex justify-between">
<span :class="allyTeam ? 'text-teal-400' : 'text-red-400'" class="pl-2"
>{{ allyTeam ? 'Ally' : 'Enemy' }} Team</span
>
<div
v-if="data.result === 'Win'"
:class="allyTeam ? 'text-teal-400' : 'text-red-400'"
class="flex pr-2"
>
<svg class="items-center w-4 h-4">
<use xlink:href="#award" />
</svg>
<span class="ml-2px">VICTORY</span>
</div>
</div>
</th>
<th class="px-2 py-5 text-sm font-medium w-kda">K</th>
<th class="px-2 py-5 text-sm font-medium w-kda">D</th>
<th class="px-2 py-5 text-sm font-medium w-kda">A</th>
<th class="px-2 py-5 text-sm font-medium w-minions">
{{ statsFormat === 'stats' ? 'Cs' : 'Cs/m' }}
</th>
<th class="px-2 py-5 text-sm font-medium w-vision">
{{ statsFormat === 'stats' ? 'Vs' : 'Vs/m' }}
</th>
<th class="px-2 py-5 text-sm font-medium w-gold-dmg-kp">Gold</th>
<th class="px-2 py-5 text-sm font-medium w-gold-dmg-kp">
Dmg
<br />champ
</th>
<th class="px-2 py-5 text-sm font-medium w-gold-dmg-kp">
Dmg
<br />obj
</th>
<th class="px-2 py-5 text-sm font-medium w-gold-dmg-kp">
Dmg
<br />taken
</th>
<th class="px-2 py-5 text-sm font-medium w-gold-dmg-kp">KP</th>
</tr>
</thead>
<tbody :class="{ 'border-b border-blue-700': allyTeam }" class="leading-none">
<tr v-for="(player, index) in data.players" :key="player.name + index">
<td class="py-2 border-r border-blue-700">
<div class="flex justify-between px-1">
<div class="flex">
<div class="flex items-center">
<div
v-if="player.role !== 'NONE'"
:style="{
backgroundImage: `url(${'img/roles/' + player.role + '.png'})`,
}"
class="w-4 h-4 bg-center bg-cover"
></div>
</div>
<div
:style="{ backgroundImage: `url('${player.champion.icon}')` }"
class="relative w-8 h-8 ml-2 bg-center bg-cover rounded-full bg-blue-1000"
>
<div
:class="allyTeam ? 'bg-teal-500 text-teal-100' : 'bg-red-500 text-red-100'"
class="absolute bottom-0 flex items-center justify-center w-4 h-4 rounded-full level-position text-xxs"
>
<span>{{ player.level }}</span>
</div>
</div>
<div class="flex flex-col justify-around ml-1">
<Tooltip>
<template #trigger>
<div
:style="{
backgroundImage: `url(${
player.summonerSpell1 ? player.summonerSpell1.icon : null
})`,
}"
:class="{ 'cursor-pointer': player.summonerSpell1 }"
class="w-4 h-4 bg-center bg-cover rounded-md bg-blue-1000"
></div>
</template>
<template v-if="player.summonerSpell1" #default>
<div class="flex max-w-sm p-2 text-xs text-left text-white select-none">
<div
:style="{
backgroundImage: `url('${player.summonerSpell1.icon}')`,
}"
class="flex-shrink-0 w-12 h-12 ml-1 bg-center bg-cover rounded-md bg-blue-1000"
></div>
<div class="ml-2 leading-tight">
<div class="text-base leading-none">
{{ player.summonerSpell1.name }}
</div>
<div class="mt-1 font-light text-blue-200">
{{ player.summonerSpell1.description }}
</div>
</div>
</div>
</template>
</Tooltip>
<Tooltip>
<template #trigger>
<div
:style="{
backgroundImage: `url(${
player.summonerSpell2 ? player.summonerSpell2.icon : null
})`,
}"
:class="{ 'cursor-pointer': player.summonerSpell2 }"
class="w-4 h-4 bg-center bg-cover rounded-md bg-blue-1000"
></div>
</template>
<template v-if="player.summonerSpell2" #default>
<div class="flex max-w-sm p-2 text-xs text-left text-white select-none">
<div
:style="{
backgroundImage: `url('${player.summonerSpell2.icon}')`,
}"
class="flex-shrink-0 w-12 h-12 ml-1 bg-center bg-cover rounded-md bg-blue-1000"
></div>
<div class="ml-2 leading-tight">
<div class="text-base leading-none">
{{ player.summonerSpell2.name }}
</div>
<div class="mt-1 font-light text-blue-200">
{{ player.summonerSpell2.description }}
</div>
</div>
</div>
</template>
</Tooltip>
</div>
<Tooltip>
<template #trigger>
<div
@click="selectRunes(player)"
:class="{ 'cursor-pointer': player.perks }"
class="flex flex-col justify-around cursor-pointer ml-2px"
>
<div
:style="[
player.primaryRune
? {
background: `url(${player.primaryRune}) center/cover`,
}
: '',
]"
class="w-4 h-4 rounded-md bg-blue-1000"
></div>
<div
:style="[
player.secondaryRune
? {
background: `url(${player.secondaryRune}) center/cover`,
}
: '',
]"
class="w-4 h-4 rounded-md bg-blue-1000"
></div>
</div>
</template>
<template v-if="player.perks" #default>
<div class="px-2 text-sm leading-relaxed text-center text-white select-none">
<p>Click to display</p>
<p class="font-bold text-teal-400">full runes</p>
</div>
</template>
</Tooltip>
<div class="flex flex-col items-start justify-center ml-1 leading-none">
<router-link
v-if="player.summonerSpell1"
:to="{
name: 'summoner',
params: { region: $route.params.region, name: player.name },
}"
:class="{
'font-semibold text-yellow-400': account.id === player.summonerId,
}"
class="overflow-hidden text-xs text-left text-white whitespace-no-wrap w-22 text-overflow hover:text-blue-200"
>{{ player.name }}</router-link
>
<div
v-else
class="overflow-hidden text-xs text-left text-white whitespace-no-wrap w-22 text-overflow"
>
{{ player.name }}
</div>
<div class="text-teal-500 text-xxs">
{{ player.champion.name }}
</div>
</div>
</div>
<div class="flex items-center">
<div v-if="player.rank">
<svg class="w-5 h-5 ml-auto">
<use :xlink:href="`#rank-${player.rank.tier.toLowerCase()}`" />
</svg>
<div class="text-blue-300 text-xxs">
{{ player.rank.shortName }}
</div>
</div>
<div v-else-if="!ranksLoaded">
<DotsLoader width="30px" dot-width="10px" />
</div>
<div v-else class="w-5 h-5">
<div class="-mt-1 text-2xl text-blue-300">-</div>
</div>
<MatchItems :items="player.items" :one-row="true" />
</div>
</div>
</td>
<td class="relative">
<div
:style="bgColor(player, 'kills')"
class="absolute inset-0 flex items-center justify-center p-2 text-sm text-white"
>
{{ player.stats.kills }}
</div>
</td>
<td class="relative">
<div
:style="bgColor(player, 'deaths')"
class="absolute inset-0 flex items-center justify-center p-2 text-sm text-white"
>
{{ player.stats.deaths }}
</div>
</td>
<td class="relative">
<div
:style="bgColor(player, 'assists')"
class="absolute inset-0 flex items-center justify-center p-2 text-sm text-white"
>
{{ player.stats.assists }}
</div>
</td>
<td class="relative">
<div
:style="bgColor(player, 'minions')"
class="absolute inset-0 flex items-center justify-center p-2 text-sm text-white"
>
{{ player[statsFormat].minions }}
</div>
</td>
<td class="relative">
<div
:style="bgColor(player, 'vision')"
class="absolute inset-0 flex items-center justify-center p-2 text-sm text-white"
>
{{ player[statsFormat].vision }}
</div>
</td>
<td class="relative">
<div
:style="bgColor(player, 'gold')"
class="absolute inset-0 flex items-center justify-center p-2 text-sm text-white"
>
{{ roundStats(player[statsFormat].gold) }}
</div>
</td>
<td class="relative">
<div
:style="bgColor(player, 'dmgChamp')"
class="absolute inset-0 flex items-center justify-center p-2 text-sm text-white"
>
{{ roundStats(player[statsFormat].dmgChamp) }}
</div>
</td>
<td class="relative">
<div
:style="bgColor(player, 'dmgObj')"
class="absolute inset-0 flex items-center justify-center p-2 text-sm text-white"
>
{{ roundStats(player[statsFormat].dmgObj) }}
</div>
</td>
<td class="relative">
<div
:style="bgColor(player, 'dmgTaken')"
class="absolute inset-0 flex items-center justify-center p-2 text-sm text-white"
>
{{ roundStats(player[statsFormat].dmgTaken) }}
</div>
</td>
<td class="relative">
<div
:style="bgColor(player, 'kp')"
class="absolute inset-0 flex items-center justify-center p-2 text-sm text-white"
>
{{ player.stats.kp }}
</div>
</td>
</tr>
</tbody>
</table>
</template>
<script>
import { colors } from '@/data/data.js'
import { mapActions, mapState } from 'vuex'
import DotsLoader from '@/components/Common/DotsLoader.vue'
import Tooltip from '@/components/Common/Tooltip.vue'
import MatchItems from '@/components/Match/MatchItems.vue'
export default {
components: {
DotsLoader,
Tooltip,
MatchItems,
},
props: {
allPlayers: {
type: Array,
required: true,
},
allyTeam: {
type: Boolean,
required: true,
},
data: {
type: Object,
required: true,
},
ranksLoaded: {
type: Boolean,
default: false,
},
},
computed: {
statsFormat() {
return this.percentSettings ? 'percentStats' : 'stats'
},
...mapState({
account: (state) => state.summoner.basic.account,
percentSettings: (state) => state.settings.percent,
}),
},
methods: {
bgColor(player, stats) {
const value = parseFloat(player.stats[stats])
const biggestValue = Math.max(...this.allPlayers.map((p) => parseFloat(p.stats[stats])), 0)
const opacity = (value / biggestValue).toFixed(2)
const biggestValueStyle = {}
if (value === biggestValue && value !== 0) {
biggestValueStyle.boxShadow = 'rgba(181, 160, 122, 0.5) 0px 0px 10px'
biggestValueStyle.border = '2px solid'
biggestValueStyle.borderImageSlice = '1'
biggestValueStyle.borderImageSource = 'linear-gradient(to top, #edb457, #f9e9ce)'
biggestValueStyle.borderCollapse = 'separate'
}
return {
backgroundColor: `rgba(${colors[stats]}, ${opacity})`,
...biggestValueStyle,
}
},
displayBorderbottom(index) {
return this.allyTeam || index !== this.data.players.length - 1
},
getHeadingColor(result) {
switch (result) {
case 'Win':
return {
'--bg-img':
'linear-gradient(90deg, rgba(1, 97, 28, 0.3) 0%, rgba(44, 82, 130, 0) 45% )',
}
case 'Fail':
return {
'--bg-img':
'linear-gradient(90deg, rgba(140, 0, 0, 0.3) 0%, rgba(44, 82, 130, 0) 45% )',
}
default:
return {
'--bg-img':
'linear-gradient(90deg, rgba(233, 169, 75, 0.3) 0%, rgba(44, 82, 130, 0) 45% )',
}
}
},
roundStats(value) {
return this.percentSettings ? value : this.$options.filters.kilo(value)
},
selectRunes(player) {
if (!player.perks) {
return
}
this.displayOrHideRunes(player.perks)
},
...mapActions('cdragon', ['displayOrHideRunes']),
},
}
</script>
<style scoped>
.heading-row th {
position: relative;
z-index: 20;
}
.heading-row th:first-child:before {
content: '';
position: absolute;
z-index: -10;
top: 0;
left: 0;
height: 67px;
width: 884px;
background-image: var(--bg-img), linear-gradient(#2a4365 0%, #2b4c77 55%, #235a93 100%);
}
.heading-row th:first-child:after {
content: '';
position: absolute;
right: -1px;
top: 0;
width: 1px;
background-color: #2b6cb0;
height: 67px;
}
.heading-detailed {
box-shadow: inset 0 -1px #2b6cb0;
}
.level-position {
left: -5px;
}
.w-players {
width: 392px;
}
.w-kda {
width: 36px;
}
.w-minions {
width: 45px;
}
.w-vision {
width: 45px;
}
.w-gold-dmg-kp {
width: 58px;
}
</style>

View file

@ -1,113 +0,0 @@
<template>
<div class="flex ml-4 overflow-hidden text-sm rounded-lg">
<div class="relative flex justify-between w-full overflow-hidden bg-blue-800 rounded-lg">
<div class="absolute flex flex-col items-center justify-between h-full horizontal-center">
<div class="text-base leading-loose text-blue-200">{{ gamemode.name }}</div>
<div class="flex flex-col text-2xl font-bold leading-none vs">
<span>V</span>
<span class="ml-4 -mt-3">S</span>
</div>
<div :class="{ 'w-10': displayStartTime !== 'Not started yet' }" class="pb-2 text-blue-200">
{{ displayStartTime }}
</div>
</div>
<ul class="w-1/2 text-left">
<li
v-for="(ally, index) in allyTeam"
:key="ally.summonerId"
:class="index % 2 === 0 ? 'accent-ally' : 'ally'"
class="flex items-center px-5 py-1 leading-loose"
>
<div
:style="{
backgroundImage: `url('https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/champion-icons/${ally.championId}.png')`,
}"
class="w-6 h-6 bg-center bg-cover rounded-full bg-blue-1000"
></div>
<router-link
v-if="!ally.bot"
:to="{
name: 'summoner',
params: { region: $route.params.region, name: ally.summonerName },
}"
:class="[ally.summonerId === account.id ? 'text-white' : 'text-blue-200']"
class="relative ml-2 hover:text-white"
>{{ ally.summonerName }}</router-link
>
</li>
</ul>
<ul class="w-1/2 text-right">
<li
v-for="(enemy, index) in enemyTeam"
:key="enemy.summonerId"
:class="index % 2 === 0 ? 'accent-enemy' : 'enemy'"
class="flex items-center justify-end px-5 py-1 leading-loose"
>
<router-link
v-if="!enemy.bot"
:to="{
name: 'summoner',
params: { region: $route.params.region, name: enemy.summonerName },
}"
class="relative text-red-200 hover:text-white"
>{{ enemy.summonerName }}</router-link
>
<div
:style="{
backgroundImage: `url('https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/champion-icons/${enemy.championId}.png')`,
}"
class="w-6 h-6 ml-2 bg-center bg-cover rounded-full bg-blue-1000"
></div>
</li>
</ul>
</div>
<router-link
:to="{
name: 'summonerLive',
params: { region: $route.params.region, name: $route.params.name },
}"
class="flex items-center pl-6 pr-4 -ml-2 text-base text-blue-200 cursor-pointer live-game-link bg-gradient-x hover:bg-blue-800 hover:text-blue-100"
>
<div class="-mt-2px">more</div>
<svg class="w-4 h-4 ml-1 transition-transform duration-200 ease-in-out transform">
<use xlink:href="#arrow-right" />
</svg>
</router-link>
</div>
</template>
<script>
import { liveGame } from '@/mixins/liveGame'
export default {
mixins: [liveGame],
}
</script>
<style scoped>
.accent-ally {
background-image: linear-gradient(90deg, rgba(49, 130, 206, 0.7) 0%, rgba(44, 82, 130, 0) 100%);
}
.ally {
background-image: linear-gradient(90deg, rgba(49, 130, 206, 0.3) 0%, rgba(44, 82, 130, 0) 90%);
}
.accent-enemy {
background-image: linear-gradient(90deg, rgba(44, 82, 130, 0) 0%, rgba(140, 0, 0, 0.4) 100%);
}
.enemy {
background-image: linear-gradient(90deg, rgba(44, 82, 130, 0) 10%, rgba(140, 0, 0, 0.3) 100%);
}
.vs {
text-shadow:
3px 2px 0px rgba(49, 130, 206, 0.8),
-3px 2px 0px rgba(229, 62, 62, 0.8);
}
.live-game-link:hover svg {
@apply translate-x-1;
}
</style>

View file

@ -1,302 +0,0 @@
<template>
<li class="relative ml-4">
<Ripple
@click.native="displayDetails"
color="rgba(43, 108, 176, 0.7)"
:class="[
data.result,
showDetails ? 'rounded-t-lg' : 'rounded-lg',
{ 'mt-4': indexMatch !== 0 },
]"
class="relative text-base text-white bg-blue-800 cursor-pointer match hover:shadow-xl"
>
<div class="relative flex flex-wrap px-5 py-3">
<div
v-if="data.newMatch"
class="absolute top-0 right-0 px-2 rounded-full new-match text-xxs"
style="margin: 0.35rem 0.35rem 0 0; background-color: rgba(99, 179, 237, 0.2)"
>
New
</div>
<div class="w-4/12 text-left first">
<div>
<div class="h-6 text-lg font-extrabold leading-none text-teal-500 uppercase">
{{ data.champion.name }}
</div>
<div class="flex">
<div class="flex flex-col items-center justify-end">
<div
v-if="data.role !== 'NONE'"
:style="{ backgroundImage: `url(${'/img/roles/' + data.role + '.png'})` }"
class="w-10 h-10 bg-center bg-cover"
></div>
<div class="w-10 text-xs font-extrabold text-center text-teal-500">
LVL {{ data.level }}
</div>
</div>
<div
:style="{ backgroundImage: `url('${data.champion.icon}')` }"
class="w-16 h-16 ml-2 rounded-lg crop-champion bg-blue-1000"
></div>
<div class="flex flex-col justify-around ml-2">
<div
v-if="data.summonerSpell1"
:style="{ backgroundImage: `url(${data.summonerSpell1.icon})` }"
class="w-6 h-6 bg-center bg-cover rounded-md bg-blue-1000"
></div>
<div v-else class="w-6 h-6 rounded-md bg-blue-1000"></div>
<div
v-if="data.summonerSpell2"
:style="{ backgroundImage: `url(${data.summonerSpell2.icon})` }"
class="w-6 h-6 bg-center bg-cover rounded-md bg-blue-1000"
></div>
<div v-else class="w-6 h-6 rounded-md bg-blue-1000"></div>
</div>
<div class="flex flex-col justify-around ml-1">
<div
:style="[
data.primaryRune ? { background: `url(${data.primaryRune}) center/cover` } : '',
]"
class="w-6 h-6 rounded-md bg-blue-1000"
></div>
<div
:style="[
data.secondaryRune
? { background: `url(${data.secondaryRune}) center/cover` }
: '',
]"
class="w-6 h-6 rounded-md bg-blue-1000"
></div>
</div>
<div class="flex flex-col items-center justify-center mx-auto leading-none">
<div class="text-xl font-extrabold text-teal-500">
<span class>{{ data.stats.kills }}</span>
<span class>/</span>
<span class>{{ data.stats.deaths }}</span>
<span class>/</span>
<span class>{{ data.stats.assists }}</span>
</div>
<div class="relative z-30 mt-2 text-xs font-extrabold text-white">
{{ data.stats.kda }} KDA
</div>
</div>
</div>
<div
class="relative z-30 flex items-end h-6 text-sm font-extrabold leading-none text-white"
>
{{ data.gamemode.name }}
</div>
</div>
</div>
<div class="flex items-center w-3/12 py-6 second">
<MatchItems :items="data.items" />
<div class="relative z-30 ml-4 leading-none">
<div class="flex items-center">
<svg style="width: 15px; height: 15px">
<use xlink:href="#creeps" />
</svg>
<div class="ml-1 text-sm font-bold text-teal-300">
{{ data.stats.minions }}
<span class="font-normal">cs</span>
</div>
</div>
<div class="flex items-center">
<svg style="width: 15px; height: 15px">
<use xlink:href="#gold" />
</svg>
<div class="ml-1 text-sm font-bold gold">{{ data.stats.gold | kilo }}</div>
</div>
<div class="flex items-center">
<svg style="width: 15px; height: 15px">
<use xlink:href="#damage" />
</svg>
<div class="ml-1 text-sm font-bold damage">{{ data.stats.dmgChamp | kilo }}</div>
</div>
<div class="flex items-center">
<svg style="width: 15px; height: 15px">
<use xlink:href="#kill-participation" />
</svg>
<div class="ml-1 text-sm font-bold kp">{{ data.stats.kp | percent }}</div>
</div>
</div>
</div>
<div class="flex items-center w-5/12 py-1 third">
<div v-if="data.allyTeam.length > 1">
<div
v-for="(ally, index) in data.allyTeam"
:key="'player-' + index"
class="flex items-center ml-4 leading-none"
>
<router-link
v-if="ally.account_id !== '0' && account.accountId !== ally.account_id"
@click.native="$event.stopImmediatePropagation()"
:to="{
name: 'summoner',
params: { region: $route.params.region, name: ally.name },
}"
:class="isSummonerProfile(ally.account_id)"
class="w-16 overflow-hidden text-xs font-medium text-right whitespace-no-wrap hover:text-white text-overflow"
>{{ ally.name }}</router-link
>
<div
v-else
:class="isSummonerProfile(ally.account_id)"
class="w-16 overflow-hidden text-xs font-medium text-right whitespace-no-wrap text-overflow"
>
{{ ally.name }}
</div>
<div
:class="index !== 0 ? '-mt-1' : ''"
:style="{ backgroundImage: `url('${ally.champion.icon}')` }"
class="w-6 h-6 ml-1 overflow-hidden bg-center bg-cover rounded-full bg-blue-1000"
></div>
<div
:style="{
backgroundImage:
data.role !== 'NONE' ? `url(${'/img/roles/' + roles[index] + '.png'})` : null,
}"
class="w-4 h-4 mx-2 bg-center bg-cover"
></div>
<div
:class="index !== 0 ? '-mt-1' : ''"
:style="{ backgroundImage: `url('${data.enemyTeam[index].champion.icon}')` }"
class="w-6 h-6 bg-center bg-cover rounded-full bg-blue-1000"
></div>
<router-link
v-if="data.enemyTeam[index].account_id !== '0'"
@click.native="$event.stopImmediatePropagation()"
:to="{
name: 'summoner',
params: { region: $route.params.region, name: data.enemyTeam[index].name },
}"
class="w-16 ml-1 overflow-hidden text-xs font-medium text-left text-blue-200 whitespace-no-wrap hover:text-white text-overflow"
>{{ data.enemyTeam[index].name }}</router-link
>
<div
v-else
class="w-16 ml-1 overflow-hidden text-xs font-medium text-left text-blue-200 whitespace-no-wrap text-overflow"
>
{{ data.enemyTeam[index].name }}
</div>
</div>
</div>
<div class="flex flex-col items-center justify-center ml-auto">
<svg class="w-5 h-5 text-blue-200">
<use xlink:href="#stopwatch" />
</svg>
<div class="text-lg font-medium text-teal-400">{{ data.time | secToTime }}</div>
<Tooltip>
<template #trigger>
<div class="text-xs font-medium text-white">{{ data.date }}</div>
</template>
<template #default>
<div class="px-2 text-xs leading-tight text-center text-white select-none">
<svg class="w-4 h-4 mx-auto text-teal-400">
<use xlink:href="#time" />
</svg>
<div class="mt-1">{{ data.fullDate.date }}</div>
<div>{{ data.fullDate.time }}</div>
</div>
</template>
</Tooltip>
</div>
</div>
</div>
</Ripple>
<DetailedMatch :data="getMatchDetails(data.matchId) || {}" :details-open="showDetails" />
</li>
</template>
<script>
import { mapActions, mapState, mapGetters } from 'vuex'
import Tooltip from '@/components/Common/Tooltip.vue'
import DetailedMatch from '@/components/Match/DetailedMatch.vue'
import MatchItems from '@/components/Match/MatchItems.vue'
import Ripple from '@/components/Common/Ripple.vue'
export default {
components: {
DetailedMatch,
Tooltip,
MatchItems,
Ripple,
},
props: {
data: {
type: Object,
required: true,
},
indexMatch: {
type: Number,
default: -1,
},
},
data() {
return {
showDetails: false,
}
},
computed: {
...mapState({
account: (state) => state.summoner.basic.account,
roles: (state) => state.roles,
}),
...mapGetters('detailedMatch', ['getMatchDetails']),
},
methods: {
displayDetails() {
this.showDetails = !this.showDetails
if (!this.getMatchDetails(this.data.matchId)) {
this.matchDetails(this.data.matchId)
}
},
isSummonerProfile(account_id) {
return {
'font-bold text-white': this.account.accountId === account_id,
'text-blue-200': this.account.accountId !== account_id,
}
},
...mapActions('detailedMatch', ['matchDetails']),
},
}
</script>
<style scoped>
.match {
transition-duration: 0.3s;
transition-timing-function: cubic-bezier(0, 1, 0.5, 1);
}
.game-status {
top: 50%;
left: 6px;
transform: translateY(-50%) rotate(-90deg);
}
.crop-champion {
background-size: 74px;
background-position: center;
}
.gold {
color: #f3a05a;
}
.damage {
color: #e25656;
}
.kp {
color: #b78787;
}
</style>

View file

@ -1,163 +0,0 @@
<template>
<div :class="oneRow ? 'ml-2 items-center' : 'items-2-rows flex-wrap'" class="flex">
<Tooltip v-for="(item, index) in items" :key="index">
<template #trigger>
<div class="relative">
<div
:style="{ backgroundImage: itemLink(item) }"
:class="[
oneRow ? 'ml-2px w-6 h-6' : 'ml-1 w-8 h-8',
{ 'cursor-pointer': item !== null },
]"
class="relative z-10 bg-center bg-cover rounded-md bg-blue-1000"
>
<div v-if="isMythic(item)" class="w-full h-full rounded-md mythic-inside"></div>
</div>
<div
v-if="isMythic(item)"
class="absolute rounded-md mythic"
:class="oneRow ? 'mythic-sm' : 'mythic-xl'"
></div>
</div>
</template>
<template v-if="item !== null" #default>
<div class="flex max-w-md p-2 text-xs text-left text-white select-none">
<div
:style="{ backgroundImage: itemLink(item) }"
class="flex-shrink-0 w-12 h-12 ml-1 bg-center bg-cover rounded-md bg-blue-1000"
></div>
<div class="ml-2 leading-none">
<div class="text-base">{{ itemName(item.name) }}</div>
<div class="mt-1">
<span class="text-blue-200">Price:</span>
<span class="ml-1 text-sm font-semibold text-yellow-500">{{ item.price }}</span>
</div>
<div
v-html="item.description"
class="mt-1 font-light text-blue-200 item-description"
></div>
</div>
</div>
</template>
</Tooltip>
</div>
</template>
<script>
import Tooltip from '@/components/Common/Tooltip.vue'
export default {
components: {
Tooltip,
},
props: {
oneRow: {
type: Boolean,
default: false,
},
items: {
type: Array,
required: true,
},
},
methods: {
isMythic(item) {
return item && item.isMythic
},
itemLink(item) {
if (!item) {
return null
}
// Fix to still make work the old items links (before season 11)
const originalUrl = item.image
const newUrl = originalUrl.includes('/global/default/assets/items/')
? originalUrl
: originalUrl.replace('latest', '10.22')
return `url('${newUrl}')`
},
itemName(name) {
// Remove placeholders in item names (e.g.: for Ornn items)
return name.replace(/%[^%]*%/, '')
},
},
}
</script>
<style scoped>
.mythic {
background: linear-gradient(
195deg,
rgb(255, 197, 47) 0%,
rgb(255, 231, 146) 50%,
rgb(214, 128, 0) 100%
);
}
.mythic-sm {
top: -1px;
left: 1px;
width: calc(1.5rem + 2px);
height: calc(1.5rem + 2px);
}
.mythic-xl {
top: -2px;
left: 2px;
width: calc(2rem + 4px);
height: calc(2rem + 4px);
}
.mythic-inside {
box-shadow: rgb(26, 32, 44) 0px 0px 0px 1px inset;
}
.items-2-rows {
width: 7rem;
height: 4.5rem;
}
.item-description >>> stats {
@apply text-white leading-tight;
}
.item-description >>> br + br {
@apply hidden;
}
.item-description >>> stats br {
@apply block;
}
.item-description >>> li {
@apply block mt-2;
}
.item-description >>> passive {
@apply text-white font-normal;
}
.item-description >>> active {
@apply inline-block text-white font-bold mt-2;
}
.item-description >>> unique,
.item-description >>> li > passive:first-child,
.item-description >>> rarityMythic {
@apply text-white font-bold block mt-2;
}
.item-description >>> font {
@apply text-blue-400;
}
.item-description >>> rules {
@apply inline-block mt-2 text-blue-400 italic;
}
.item-description >>> rules active {
@apply inline text-white font-normal;
}
</style>

View file

@ -1,166 +0,0 @@
<template>
<div class="flex">
<div
:style="{
backgroundImage: `url('${createCategoryBorderUrl(runeStyle.name)}')`,
}"
class="flex items-center justify-center w-24 h-24 bg-cover"
>
<div
:style="{
backgroundImage: `url('${createCategoryUrl(runeStyle.name)}')`,
}"
style="filter: brightness(1.2)"
class="w-56 h-56 mt-4 bg-center bg-no-repeat bg-contain"
></div>
</div>
<div class="mt-24 space-y-4">
<div v-for="(category, index) in slots" :key="`secondary-category-${index}`" class="">
<div class="flex space-x-4">
<ul v-for="runeId in category" :key="`slot-${runeId}`">
<Tooltip>
<template #trigger>
<li
:style="{
backgroundImage: `url('${createCDragonAssetUrl(runes.perks[runeId].icon)}')`,
}"
:class="selectedRunes.selected.includes(runeId) ? 'used-rune' : 'not-used-rune'"
class="w-12 h-12 bg-center bg-cover border-2 border-gray-700 rounded-full cursor-pointer"
></li>
</template>
<template #default>
<div class="flex max-w-md p-2 text-sm text-left text-white select-none">
<div
:style="{
backgroundImage: `url('${createCDragonAssetUrl(runes.perks[runeId].icon)}')`,
}"
class="flex-shrink-0 w-12 h-12 ml-1 bg-center bg-cover rounded-md bg-blue-1000"
></div>
<div class="ml-2 leading-none">
<div class="text-base">{{ runes.perks[runeId].name }}</div>
<div
v-html="runes.perks[runeId].desc"
class="mt-3 font-light leading-tight text-blue-200 rune-description"
></div>
</div>
</div>
</template>
</Tooltip>
</ul>
</div>
<div v-if="primary && index == 0" class="w-full mt-4 bg-gray-500 bg-opacity-25 h-2px"></div>
</div>
<div v-if="!primary">
<div class="mt-8 space-y-4">
<div v-for="(row, index) in kStats" :key="`row-${index}`" class="flex px-3 space-x-8">
<ul v-for="(kStat, i) in row" :key="`${kStat}-${i}`">
<Tooltip>
<template #trigger>
<li
:style="{
backgroundImage: `url('${createCDragonAssetUrl(runes.perks[kStat].icon)}')`,
}"
:class="
selectedRunes.selected[index + 6] === kStat ? 'used-rune' : 'not-used-rune'
"
class="w-8 h-8 bg-gray-900 bg-center bg-cover border-2 border-gray-700 rounded-full cursor-pointer"
></li>
</template>
<template #default>
<div class="flex max-w-md p-2 text-sm text-left text-white select-none">
<div
:style="{
backgroundImage: `url('${createCDragonAssetUrl(runes.perks[kStat].icon)}')`,
}"
class="flex-shrink-0 w-8 h-8 ml-1 bg-center bg-cover rounded-md bg-blue-1000"
></div>
<div class="ml-2 leading-none">
<div class="text-base">{{ runes.perks[kStat].name }}</div>
<div
v-html="runes.perks[kStat].desc"
class="mt-3 font-light leading-tight text-blue-200 rune-description"
></div>
</div>
</div>
</template>
</Tooltip>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
import { createCDragonAssetUrl } from '@/helpers/functions'
import Tooltip from '@/components/Common/Tooltip.vue'
export default {
components: {
Tooltip,
},
props: {
primary: {
type: Boolean,
default: false,
},
runeStyle: {
type: Object,
required: true,
},
},
computed: {
slots() {
return this.primary ? this.runeStyle.slots : this.runeStyle.slots.slice(1)
},
...mapState({
kStats: (state) => state.cdragon.kStats,
runes: (state) => state.cdragon.runes,
runesOpen: (state) => state.cdragon.runesOpen,
selectedRunes: (state) => state.cdragon.selectedRunes,
}),
},
methods: {
createCategoryBorderUrl(name) {
const lower = name.toLowerCase()
return `https://raw.communitydragon.org/pbe/plugins/rcp-fe-lol-collections/global/default/perks/images/${lower}/vfx-${lower[0]}.png`
},
createCategoryUrl(name) {
const lower = name.toLowerCase()
return `https://raw.communitydragon.org/latest/plugins/rcp-fe-lol-collections/global/default/perks/images/${lower}/icon-${lower[0]}.png`
},
createCDragonAssetUrl,
},
}
</script>
<style scoped>
.not-used-rune {
@apply opacity-50 transition-all duration-150 ease-in-out;
filter: grayscale(100%);
}
.not-used-rune:hover {
@apply opacity-100;
filter: none;
}
.used-rune {
@apply transition-all duration-75 ease-in-out;
}
.used-rune:hover {
filter: brightness(1.2);
}
.rune-description >>> hr {
@apply border-blue-800;
}
</style>

View file

@ -1,97 +0,0 @@
<template>
<transition leave-active-class="duration-300">
<div v-show="runesOpen" class="fixed inset-0 z-50 flex items-center justify-center">
<transition
enter-active-class="transition duration-300 ease-out"
enter-class="transform opacity-0"
enter-to-class="transform opacity-100"
leave-active-class="transition duration-200 ease-in"
leave-class="transform opacity-100"
leave-to-class="transform opacity-0"
>
<div v-if="runesOpen" @click="close" class="fixed inset-0 bg-gray-900 bg-opacity-75"></div>
</transition>
<transition
enter-active-class="transition duration-300 ease-out"
enter-class="transform scale-95 opacity-0"
enter-to-class="transform scale-100 opacity-100"
leave-active-class="transition duration-200 ease-in"
leave-class="transform scale-100 opacity-100"
leave-to-class="transform scale-95 opacity-0"
>
<div
v-if="runesOpen"
class="relative overflow-hidden bg-gray-900 rounded-md shadow-lg"
style="width: 800px; height: 500px"
>
<LazyBackground
:image-source="`/img/runes/${primaryStyle.name.toLowerCase()}.jpg`"
image-class="absolute inset-0"
more-backgrounds="linear-gradient(rgba(26, 32, 44, 0.6), rgba(26, 32, 44, 0.8)),"
transition-name="fade-fast"
style="filter: blur(2px)"
>
</LazyBackground>
<div class="relative flex items-start h-full px-4 py-2">
<div class="w-1/2">
<RuneStyle :primary="true" :rune-style="primaryStyle" />
</div>
<div class="w-1/2">
<RuneStyle :primary="false" :rune-style="secondaryStyle" />
</div>
</div>
</div>
</transition>
</div>
</transition>
</template>
<script>
import { mapActions, mapState } from 'vuex'
import { createCDragonAssetUrl } from '@/helpers/functions'
import LazyBackground from '@/components/Common/LazyBackgroundImage.vue'
import RuneStyle from '@/components/Match/Runes/RuneStyle.vue'
export default {
components: {
LazyBackground,
RuneStyle,
},
computed: {
primaryStyle() {
return this.runes.perkstyles[this.selectedRunes.primaryStyle]
},
secondaryStyle() {
return this.runes.perkstyles[this.selectedRunes.secondaryStyle]
},
...mapState({
runes: (state) => state.cdragon.runes,
runesOpen: (state) => state.cdragon.runesOpen,
selectedRunes: (state) => state.cdragon.selectedRunes,
}),
},
created() {
document.addEventListener('keydown', this.handleEscape)
},
beforeDestroy() {
document.removeEventListener('keydown', this.handleEscape)
},
methods: {
close() {
this.displayOrHideRunes({})
},
handleEscape(e) {
if (e.key === 'Esc' || e.key === 'Escape') {
this.displayOrHideRunes({})
}
},
createCDragonAssetUrl,
...mapActions('cdragon', ['displayOrHideRunes']),
},
}
</script>

View file

@ -1,43 +0,0 @@
<template>
<Ripple color="rgba(43, 108, 176, 0.7)" class="relative inline-block rounded-lg">
<input
v-model="championName"
@input="search"
class="px-2 py-2 pl-10 rounded-lg outline-none input-color focus:bg-blue-1000"
type="text"
placeholder="Search Champions"
/>
<svg class="absolute left-0 w-4 h-4 ml-3 vertical-center">
<use xlink:href="#search" />
</svg>
</Ripple>
</template>
<script>
import Ripple from '@/components/Common/Ripple.vue'
export default {
components: {
Ripple,
},
data() {
return {
championName: '',
}
},
methods: {
search() {
this.$emit('search-champions', this.championName)
},
},
}
</script>
<style scoped>
.input-color::placeholder {
color: #fff;
font-weight: 300;
}
</style>

View file

@ -1,365 +0,0 @@
<template>
<table class="w-full leading-none text-center bg-blue-800 rounded-lg table-fixed">
<thead>
<tr class="text-sm rounded-t-lg select-none heading">
<th
@click="sortBy('index')"
:class="sortedClasses('index')"
class="relative px-2 py-4 font-normal rounded-tl-lg cursor-pointer hover:bg-blue-700"
>
rank
</th>
<th
v-for="(heading, index) in headings"
:key="`champHeading-${index}`"
@click="sortBy(heading.props)"
v-html="heading.name"
:class="[
{
'rounded-tr-lg': index === headings.length - 1,
'w-name': heading.name === 'Name',
'w-kda': heading.name === 'KDA',
},
sortedClasses(heading.props),
]"
class="relative px-2 py-4 font-normal cursor-pointer hover:bg-blue-700"
></th>
</tr>
</thead>
<tbody v-if="champions.length" class="bg-blue-760">
<tr
v-for="(champion, index) in championsToDisplay"
:key="champion._id"
:class="{ 'rounded-b-lg': index === championsToDisplay.length - 1 }"
>
<td
:class="{ 'rounded-bl-lg': index === championsToDisplay.length - 1 }"
class="relative px-2 py-3 text-sm text-white bg-blue-800 border-t-table"
>
{{ champion.index + 1 }}
</td>
<td class="relative px-2 py-3 text-sm text-white bg-blue-800 border-t-table">
<div class="flex items-center">
<div
:style="{ backgroundImage: `url('${champion.champion.icon}')` }"
class="flex-shrink-0 w-6 h-6 bg-center bg-cover rounded-full bg-blue-1000"
></div>
<div class="ml-2">{{ champion.champion.name }}</div>
</div>
</td>
<td :style="bgColor(champion, 'winrate')" class="px-2 py-3 text-sm text-white">
{{ champion.winrate | percent }}
</td>
<td :style="bgColor(champion, 'playrate')" class="px-2 py-3 text-sm text-white">
{{ champion.playrate | percent }}
</td>
<td :style="bgColor(champion, 'wins')" class="px-2 py-3 text-sm text-white">
{{ champion.wins }}
</td>
<td :style="bgColor(champion, 'count')" class="px-2 py-3 text-sm text-white">
{{ champion.count }}
</td>
<td :style="bgColor(champion, 'kda')" class="px-2 py-3 text-sm text-white">
<div>{{ champion.kda | round }}</div>
<div class="mt-1 text-blue-200 whitespace-no-wrap text-xxs">
{{ (champion.kills / champion.count) | round(1) }}
/
{{ (champion.deaths / champion.count) | round(1) }}
/
{{ (champion.assists / champion.count) | round(1) }}
</div>
</td>
<td :style="bgColor(champion, 'kp')" class="px-2 py-3 text-sm text-white">
{{ champion.kp | percent }}
</td>
<td :style="bgColor(champion, 'minions')" class="px-2 py-3 text-sm text-white">
{{ champion.minions | round(0) }}
</td>
<td :style="bgColor(champion, 'gold')" class="px-2 py-3 text-sm text-white">
{{ champion.gold | kilo }}
</td>
<td :style="bgColor(champion, 'dmgChamp')" class="px-2 py-3 text-sm text-white">
{{ champion.dmgChamp | kilo }}
</td>
<td :style="bgColor(champion, 'dmgTaken')" class="px-2 py-3 text-sm text-white">
{{ champion.dmgTaken | kilo }}
</td>
<td :style="bgColor(champion, 'gameLength')" class="px-2 py-3 text-sm text-white">
{{ champion.gameLength | secToTime }}
</td>
<td
:class="{ 'rounded-br-lg': index === championsToDisplay.length - 1 }"
class="px-2 py-3 text-xs text-white"
>
{{ champion.lastPlayed }}
</td>
</tr>
</tbody>
<tbody v-else>
<tr v-for="index in 11" :key="index">
<td colspan="14">
<content-loader
:height="50"
:width="1200"
:speed="2"
primary-color="#17314f"
secondary-color="#2b6cb0"
>
<rect x="31" y="16" rx="3" ry="3" width="20" height="20" />
<circle cx="101" cy="26" r="12" />
<rect x="119" y="16" rx="3" ry="3" width="50" height="20" />
<rect x="234.5" y="16" rx="3" ry="3" width="45" height="20" />
<rect x="316.5" y="16" rx="3" ry="3" width="45" height="20" />
<rect x="398.5" y="16" rx="3" ry="3" width="45" height="20" />
<rect x="480.5" y="16" rx="3" ry="3" width="45" height="20" />
<rect x="565" y="14" rx="3" ry="3" width="40" height="10" />
<rect x="558" y="30" rx="3" ry="3" width="55" height="10" />
<rect x="644.5" y="16" rx="3" ry="3" width="45" height="20" />
<rect x="726.5" y="16" rx="3" ry="3" width="45" height="20" />
<rect x="808.5" y="16" rx="3" ry="3" width="45" height="20" />
<rect x="890.5" y="16" rx="3" ry="3" width="45" height="20" />
<rect x="972.5" y="16" rx="3" ry="3" width="45" height="20" />
<rect x="1052" y="16" rx="3" ry="3" width="50" height="20" />
<rect x="1129" y="16" rx="3" ry="3" width="60" height="20" />
</content-loader>
</td>
</tr>
</tbody>
</table>
</template>
<script>
import { colors } from '@/data/data.js'
import { ContentLoader } from 'vue-content-loader'
import { timeDifference } from '@/helpers/functions.js'
export default {
components: {
ContentLoader,
},
props: {
champions: {
type: Array,
required: true,
},
onlyMostPlayed: {
type: Boolean,
default: false,
},
search: {
type: String,
default: '',
},
},
data() {
return {
headings: [
{
name: 'Name',
props: 'champion.name',
},
{
name: 'Win <br> rate',
props: 'winrate',
},
{
name: 'Play <br> rate',
props: 'playrate',
},
{
name: 'Wins',
props: 'wins',
},
{
name: 'Plays',
props: 'count',
},
{
name: 'KDA',
props: 'kda',
},
{
name: 'KP',
props: 'kp',
},
{
name: 'Minions',
props: 'minions',
},
{
name: 'Gold',
props: 'gold',
},
{
name: 'Dmg <br> champ',
props: 'dmgChamp',
},
{
name: 'Dmg <br> taken',
props: 'dmgTaken',
},
{
name: 'Game <br> length',
props: 'gameLength',
},
{
name: 'Last <br> played',
props: 'date',
},
],
championsFull: [],
sortProps: 'index',
order: -1,
}
},
computed: {
championsToDisplay() {
return this.championsFull.filter((c) => {
const playedEnough = this.onlyMostPlayed ? c.playrate >= 1 : true
const searched = c.champion.name.toLowerCase().includes(this.search.toLowerCase())
return playedEnough && searched
})
},
totalGames() {
return this.champions.reduce((agg, champ) => agg + champ.count, 0)
},
},
watch: {
champions() {
this.updateChampionsList()
},
championsToDisplay() {
this.reApplySorts()
},
},
created() {
this.updateChampionsList()
},
methods: {
bgColor(champion, stats) {
const biggestValue = Math.max(
...this.championsToDisplay
.filter((c) => c[stats] !== Infinity)
.map((c) => parseFloat(c[stats])),
0
)
// Take the second biggest Value if it's an Infinity KDA
const value = champion[stats] === Infinity ? biggestValue : parseFloat(champion[stats])
const opacity = (value / biggestValue).toFixed(2)
return {
backgroundColor: `rgba(${colors[stats]}, ${opacity})`,
}
},
sortBy(props) {
// Change order of the sort
if (props === this.sortProps) {
this.order *= -1
} else {
this.order = -1
}
this.championsToDisplay.sort((a, b) => {
const aProp = props.split('.').reduce((p, c) => p && p[c], a)
const bProp = props.split('.').reduce((p, c) => p && p[c], b)
let order = typeof aProp === 'string' ? aProp.localeCompare(bProp) : aProp - bProp
if (this.order == -1) order *= -1
// Revert order for rank and champion name
if (props === 'index' || props === 'champion.name') {
order *= -1
}
// Second sort by champion name
return order || a.champion.name.localeCompare(b.champion.name)
})
this.sortProps = props
},
reApplySorts() {
this.order *= -1
this.sortBy(this.sortProps)
},
sortedClasses(props) {
return {
'sorted': this.sortProps === props,
'sorted-asc': this.sortProps === props && this.order === 1,
'sorted-desc': this.sortProps === props && this.order === -1,
}
},
updateChampionsList() {
this.championsFull = this.champions.map((champ, index) => {
let kda =
champ.kills === 0 && champ.assists === 0 && champ.deaths === 0
? 0
: (champ.kills + champ.assists) / champ.deaths
return {
...champ,
winrate: (champ.wins * 100) / champ.count,
playrate: (champ.count * 100) / this.totalGames,
kda,
index,
lastPlayed: timeDifference(champ.date),
show: true,
}
})
},
},
}
</script>
<style scoped>
.heading {
box-shadow: none;
}
.border-t-table::after {
content: '';
position: absolute;
right: 0;
top: 0;
width: 100%;
height: 2px;
background-color: rgba(190, 227, 248, 0.2);
}
.sorted::after {
content: '';
position: absolute;
top: -15px;
left: 0;
height: 24px;
width: 100%;
background-color: rgb(34, 92, 135);
background-repeat: no-repeat;
background-position: center;
background-size: 16px 16px;
border-radius: 0.5rem 0.5rem 0 0;
}
.sorted-asc::after {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512'%3E%3Cpath fill='white' d='M288.662 352H31.338c-17.818 0-26.741-21.543-14.142-34.142l128.662-128.662c7.81-7.81 20.474-7.81 28.284 0l128.662 128.662c12.6 12.599 3.676 34.142-14.142 34.142z'%3E%3C/path%3E%3C/svg%3E");
}
.sorted-desc::after {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512'%3E%3Cpath fill='white' d='M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z' /%3E%3C/svg %3E");
}
.sorted:hover::after {
background-color: #2b6cb0;
}
.w-name {
width: 135px;
}
.w-kda {
width: 90px;
}
</style>

View file

@ -1,56 +0,0 @@
<template>
<div class="relative inline-block text-white">
<select
v-model="queue"
@change="filterQueue"
class="block w-full px-4 py-2 pr-8 font-semibold capitalize bg-blue-800 rounded-md appearance-none cursor-pointer hover:bg-blue-700 focus:outline-none"
style="width: 144px"
>
<option v-for="key in Object.keys(choices)" :key="key" :value="key">
{{ choices[key].name }}
</option>
</select>
<div
class="absolute inset-y-0 right-0 flex items-center px-2 text-gray-700 pointer-events-none"
>
<svg class="w-5 h-5 text-white">
<use xlink:href="#chevron-down" />
</svg>
</div>
</div>
</template>
<script>
import { mapActions } from 'vuex'
export default {
props: {
choices: {
type: Object,
required: true,
},
},
data() {
return {
queue: '',
}
},
created() {
// Show all queues when loading the page
this.queue = 0
},
destroyed() {
// Reload all champions stats for next user visit of the champions tab
if (this.queue !== 0) this.championsNotLoaded()
},
methods: {
filterQueue() {
this.$emit('filter-queue', this.queue)
},
...mapActions('summoner', ['championsNotLoaded']),
},
}
</script>

View file

@ -1,48 +0,0 @@
<template>
<div class="flex items-center space-x-2 text-base">
<input
@change="change"
id="onlyMostPlayed"
:checked="value"
class="form-checkbox"
type="checkbox"
/>
<Tooltip>
<template #trigger>
<label for="onlyMostPlayed" class="cursor-pointer select-none">Only most played</label>
</template>
<template #default>
<div class="px-2 text-xs text-center text-white">
Hide champions with less than
<br />
<span class="font-bold text-teal-400">1% playrate</span>
to be able to compare
<br />statistics more easily.
</div>
</template>
</Tooltip>
</div>
</template>
<script>
import Tooltip from '@/components/Common/Tooltip.vue'
export default {
components: {
Tooltip,
},
props: {
value: {
type: Boolean,
default: false,
},
},
methods: {
change(e) {
this.$emit('input', e.target.checked)
},
},
}
</script>

View file

@ -1,57 +0,0 @@
<template>
<div class="relative self-end inline-block leading-none text-blue-200 group">
<select
v-model="season"
@change="filterSeason"
dir="rtl"
class="block w-full px-4 pr-8 bg-transparent rounded-md appearance-none cursor-pointer focus:outline-none group-hover:text-white"
>
<option :value="null" class="bg-blue-800">All seasons</option>
<option v-for="(s, index) in sortedSeasons" :key="index" :value="s" class="bg-blue-800">
<template v-if="Number.isInteger(s)">Season {{ s }}</template>
<!-- Preseason numbers are stored in this format: 10.5 for Preseason 11 -->
<template v-else>Preseason {{ s + 0.5 }}</template>
</option>
</select>
<div
class="absolute inset-y-0 right-0 flex items-center px-2 text-gray-700 pointer-events-none"
>
<svg class="w-4 h-4 text-blue-200 group-hover:text-white">
<use xlink:href="#caret-down" />
</svg>
</div>
</div>
</template>
<script>
import { mapActions, mapState } from 'vuex'
export default {
data() {
return {
season: null,
}
},
computed: {
sortedSeasons() {
return [...this.seasons].sort((a, b) => b - a)
},
...mapState({
currentseason: (state) => state.summoner.basic.currentSeason,
seasons: (state) => state.summoner.basic.seasons,
}),
},
created() {
this.season = this.currentseason
},
methods: {
filterSeason() {
this.updateSeason(this.season)
},
...mapActions('summoner', ['updateSeason']),
},
}
</script>

View file

@ -1,163 +0,0 @@
<template>
<div class="pb-2 text-white">
<div class="flex justify-between">
<div style="width: 520px; height: 239px">
<content-loader
:height="239"
:width="520"
:speed="2"
primary-color="#17314f"
secondary-color="#2b6cb0"
>
<rect x="0" y="45" rx="3" ry="3" width="126.28" height="31" />
<circle cx="49" cy="145" r="48" />
<rect x="365" y="108" rx="3" ry="3" width="62" height="31" />
<rect x="162" y="108" rx="3" ry="3" width="195" height="31" />
<rect x="109" y="154" rx="6" ry="6" width="126" height="38" />
<rect x="243" y="154" rx="6" ry="6" width="277" height="38" />
<rect x="0" y="217" rx="3" ry="3" width="67" height="22" />
<rect x="78" y="217" rx="3" ry="3" width="80" height="22" />
<rect x="169" y="217" rx="3" ry="3" width="57" height="22" />
<rect x="237" y="217" rx="3" ry="3" width="72" height="22" />
<circle cx="133" cy="122" r="24" />
</content-loader>
</div>
<div class="rounded-lg bg-blue-850" style="width: 347px; height: 215px">
<content-loader
:height="215"
:width="347"
:speed="2"
primary-color="#17314f"
secondary-color="#2b6cb0"
>
<rect x="110" y="10" rx="3" ry="3" width="130" height="19" />
<rect x="53" y="45" rx="3" ry="3" width="30" height="11" />
<rect x="135" y="45" rx="3" ry="3" width="30" height="11" />
<rect x="220" y="45" rx="3" ry="3" width="30" height="11" />
<rect x="305" y="45" rx="3" ry="3" width="30" height="11" />
<rect x="10" y="66" rx="3" ry="3" width="22" height="11" />
<rect x="10" y="86" rx="3" ry="3" width="22" height="11" />
<rect x="10" y="106" rx="3" ry="3" width="22" height="11" />
<rect x="10" y="126" rx="3" ry="3" width="22" height="11" />
<rect x="10" y="146" rx="3" ry="3" width="22" height="11" />
<rect x="10" y="166" rx="3" ry="3" width="22" height="11" />
<rect x="10" y="186" rx="3" ry="3" width="22" height="11" />
<rect x="38" y="64" rx="0" ry="0" width="16" height="16" />
<rect x="38" y="84" rx="0" ry="0" width="16" height="16" />
<rect x="38" y="104" rx="0" ry="0" width="16" height="16" />
<rect x="38" y="124" rx="0" ry="0" width="16" height="16" />
<rect x="38" y="144" rx="0" ry="0" width="16" height="16" />
<rect x="38" y="164" rx="0" ry="0" width="16" height="16" />
<rect x="38" y="184" rx="0" ry="0" width="16" height="16" />
<rect x="58" y="64" rx="0" ry="0" width="16" height="16" />
<rect x="58" y="84" rx="0" ry="0" width="16" height="16" />
<rect x="58" y="104" rx="0" ry="0" width="16" height="16" />
<rect x="58" y="124" rx="0" ry="0" width="16" height="16" />
<rect x="58" y="144" rx="0" ry="0" width="16" height="16" />
<rect x="58" y="164" rx="0" ry="0" width="16" height="16" />
<rect x="58" y="184" rx="0" ry="0" width="16" height="16" />
<rect x="78" y="64" rx="0" ry="0" width="16" height="16" />
<rect x="78" y="84" rx="0" ry="0" width="16" height="16" />
<rect x="78" y="104" rx="0" ry="0" width="16" height="16" />
<rect x="78" y="124" rx="0" ry="0" width="16" height="16" />
<rect x="78" y="144" rx="0" ry="0" width="16" height="16" />
<rect x="78" y="164" rx="0" ry="0" width="16" height="16" />
<rect x="78" y="184" rx="0" ry="0" width="16" height="16" />
<rect x="98" y="64" rx="0" ry="0" width="16" height="16" />
<rect x="98" y="84" rx="0" ry="0" width="16" height="16" />
<rect x="98" y="104" rx="0" ry="0" width="16" height="16" />
<rect x="98" y="124" rx="0" ry="0" width="16" height="16" />
<rect x="98" y="144" rx="0" ry="0" width="16" height="16" />
<rect x="98" y="164" rx="0" ry="0" width="16" height="16" />
<rect x="98" y="184" rx="0" ry="0" width="16" height="16" />
<rect x="118" y="64" rx="0" ry="0" width="16" height="16" />
<rect x="118" y="84" rx="0" ry="0" width="16" height="16" />
<rect x="118" y="104" rx="0" ry="0" width="16" height="16" />
<rect x="118" y="124" rx="0" ry="0" width="16" height="16" />
<rect x="118" y="144" rx="0" ry="0" width="16" height="16" />
<rect x="118" y="164" rx="0" ry="0" width="16" height="16" />
<rect x="118" y="184" rx="0" ry="0" width="16" height="16" />
<rect x="138" y="64" rx="0" ry="0" width="16" height="16" />
<rect x="138" y="84" rx="0" ry="0" width="16" height="16" />
<rect x="138" y="104" rx="0" ry="0" width="16" height="16" />
<rect x="138" y="124" rx="0" ry="0" width="16" height="16" />
<rect x="138" y="144" rx="0" ry="0" width="16" height="16" />
<rect x="138" y="164" rx="0" ry="0" width="16" height="16" />
<rect x="138" y="184" rx="0" ry="0" width="16" height="16" />
<rect x="158" y="64" rx="0" ry="0" width="16" height="16" />
<rect x="158" y="84" rx="0" ry="0" width="16" height="16" />
<rect x="158" y="104" rx="0" ry="0" width="16" height="16" />
<rect x="158" y="124" rx="0" ry="0" width="16" height="16" />
<rect x="158" y="144" rx="0" ry="0" width="16" height="16" />
<rect x="158" y="164" rx="0" ry="0" width="16" height="16" />
<rect x="158" y="184" rx="0" ry="0" width="16" height="16" />
<rect x="178" y="64" rx="0" ry="0" width="16" height="16" />
<rect x="178" y="84" rx="0" ry="0" width="16" height="16" />
<rect x="178" y="104" rx="0" ry="0" width="16" height="16" />
<rect x="178" y="124" rx="0" ry="0" width="16" height="16" />
<rect x="178" y="144" rx="0" ry="0" width="16" height="16" />
<rect x="178" y="164" rx="0" ry="0" width="16" height="16" />
<rect x="178" y="184" rx="0" ry="0" width="16" height="16" />
<rect x="198" y="64" rx="0" ry="0" width="16" height="16" />
<rect x="198" y="84" rx="0" ry="0" width="16" height="16" />
<rect x="198" y="104" rx="0" ry="0" width="16" height="16" />
<rect x="198" y="124" rx="0" ry="0" width="16" height="16" />
<rect x="198" y="144" rx="0" ry="0" width="16" height="16" />
<rect x="198" y="164" rx="0" ry="0" width="16" height="16" />
<rect x="198" y="184" rx="0" ry="0" width="16" height="16" />
<rect x="218" y="64" rx="0" ry="0" width="16" height="16" />
<rect x="218" y="84" rx="0" ry="0" width="16" height="16" />
<rect x="218" y="104" rx="0" ry="0" width="16" height="16" />
<rect x="218" y="124" rx="0" ry="0" width="16" height="16" />
<rect x="218" y="144" rx="0" ry="0" width="16" height="16" />
<rect x="218" y="164" rx="0" ry="0" width="16" height="16" />
<rect x="218" y="184" rx="0" ry="0" width="16" height="16" />
<rect x="238" y="64" rx="0" ry="0" width="16" height="16" />
<rect x="238" y="84" rx="0" ry="0" width="16" height="16" />
<rect x="238" y="104" rx="0" ry="0" width="16" height="16" />
<rect x="238" y="124" rx="0" ry="0" width="16" height="16" />
<rect x="238" y="144" rx="0" ry="0" width="16" height="16" />
<rect x="238" y="164" rx="0" ry="0" width="16" height="16" />
<rect x="238" y="184" rx="0" ry="0" width="16" height="16" />
<rect x="258" y="64" rx="0" ry="0" width="16" height="16" />
<rect x="258" y="84" rx="0" ry="0" width="16" height="16" />
<rect x="258" y="104" rx="0" ry="0" width="16" height="16" />
<rect x="258" y="124" rx="0" ry="0" width="16" height="16" />
<rect x="258" y="144" rx="0" ry="0" width="16" height="16" />
<rect x="258" y="164" rx="0" ry="0" width="16" height="16" />
<rect x="258" y="184" rx="0" ry="0" width="16" height="16" />
<rect x="278" y="64" rx="0" ry="0" width="16" height="16" />
<rect x="278" y="84" rx="0" ry="0" width="16" height="16" />
<rect x="278" y="104" rx="0" ry="0" width="16" height="16" />
<rect x="278" y="124" rx="0" ry="0" width="16" height="16" />
<rect x="278" y="144" rx="0" ry="0" width="16" height="16" />
<rect x="278" y="164" rx="0" ry="0" width="16" height="16" />
<rect x="278" y="184" rx="0" ry="0" width="16" height="16" />
<rect x="298" y="64" rx="0" ry="0" width="16" height="16" />
<rect x="298" y="84" rx="0" ry="0" width="16" height="16" />
<rect x="298" y="104" rx="0" ry="0" width="16" height="16" />
<rect x="298" y="124" rx="0" ry="0" width="16" height="16" />
<rect x="298" y="144" rx="0" ry="0" width="16" height="16" />
<rect x="298" y="164" rx="0" ry="0" width="16" height="16" />
<rect x="298" y="184" rx="0" ry="0" width="16" height="16" />
<rect x="318" y="64" rx="0" ry="0" width="16" height="16" />
<rect x="318" y="84" rx="0" ry="0" width="16" height="16" />
<rect x="318" y="104" rx="0" ry="0" width="16" height="16" />
<rect x="318" y="124" rx="0" ry="0" width="16" height="16" />
<rect x="318" y="144" rx="0" ry="0" width="16" height="16" />
</content-loader>
</div>
</div>
</div>
</template>
<script>
import { ContentLoader } from 'vue-content-loader'
export default {
components: {
ContentLoader,
},
}
</script>

View file

@ -1,332 +0,0 @@
<template>
<div class="px-5 py-4 mt-2 bg-blue-800 rounded-lg">
<table
class="w-full leading-none text-center table-fixed"
style="border-collapse: separate; border-spacing: 0 0.5rem"
>
<thead>
<tr class="text-left">
<th :class="[ally ? 'text-teal-400 ' : 'text-red-400 ']" class="font-semibold w-team">
{{ ally ? 'Ally' : 'Enemy' }} Team
</th>
<th class="text-sm font-normal text-blue-200 w-ranked">SoloQ Stats</th>
<th class="text-sm font-normal text-blue-200 w-ranked">Flex Stats</th>
<th class="px-2 text-sm font-normal text-right text-blue-200 w-bans">Bans</th>
</tr>
</thead>
<tbody v-if="liveLoaded">
<tr
v-for="(player, index) in team"
:key="player.summonerId"
:style="getCSSVars(player.championId)"
class="relative live-team-row"
>
<td class="py-1 pl-2 rounded-l-lg">
<div class="flex items-center">
<div
v-if="player.perks"
@click="selectRunes(player)"
:class="{ 'cursor-pointer': player.perks }"
class="flex flex-col items-center runes"
>
<div
:style="{ backgroundImage: `url('${getPrimarRune(player.perks)}')` }"
class="w-6 h-6 bg-center bg-cover"
></div>
<div
:style="{ backgroundImage: `url('${getSecondaryRune(player.perks)}')` }"
class="w-3 h-3 mt-1 bg-center bg-cover"
></div>
</div>
<div v-else class="w-6"></div>
<div
:style="{
backgroundImage: `url('https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/champion-icons/${player.championId}.png')`,
}"
:class="borderChampion(player.summonerId)"
class="relative w-12 h-12 ml-2 bg-center bg-cover border-2 rounded-full bg-blue-1000"
>
<div
v-if="player.role && player.role !== 'NONE'"
:class="borderChampion(player.summonerId)"
class="absolute border rounded-full p-2px bg-blue-1000"
style="bottom: -5px; right: -5px"
>
<div
:style="{ backgroundImage: `url(${'/img/roles/' + player.role + '.png'})` }"
class="w-4 h-4 bg-center bg-cover"
></div>
</div>
</div>
<div class="flex flex-col ml-2">
<div
:style="{ backgroundImage: `url(${player.summonerSpell1.icon})` }"
class="w-4 h-4 bg-center bg-cover rounded-md bg-blue-1000"
></div>
<div
:style="{ backgroundImage: `url(${player.summonerSpell2.icon})` }"
class="w-4 h-4 mt-1 bg-center bg-cover rounded-md bg-blue-1000"
></div>
</div>
<div class="ml-3 text-sm leading-tight text-left">
<router-link
v-if="!player.bot"
:to="{
name: 'summoner',
params: { region: $route.params.region, name: player.summonerName },
}"
:class="[
player.summonerId === account.id ? 'text-yellow-500' : 'hover:text-blue-200',
]"
class="font-semibold"
>{{ player.summonerName }}</router-link
>
<div :class="[ally ? 'text-teal-300 ' : 'text-red-400 ']" class="text-xs">
{{ player.champion.name }}
</div>
</div>
</div>
</td>
<td class="py-1 text-left">
<div class="px-2">
<div v-if="player.rank.soloQ" class="flex items-center">
<div class="inline-block text-center">
<svg class="w-5 h-5">
<use :xlink:href="`#rank-${player.rank.soloQ.tier.toLowerCase()}`" />
</svg>
<div class="text-xs font-semibold text-blue-300 mt-2px">
{{ player.rank.soloQ.shortName }}
</div>
</div>
<div class="ml-5 text-center">
<div class="font-semibold">{{ player.rank.soloQ.winrate }}</div>
<div class="mt-1 text-xs text-blue-300">
{{ player.rank.soloQ.wins + player.rank.soloQ.losses }} games
</div>
</div>
</div>
<div v-else class="w-5 h-5">
<div class="-mt-1 text-2xl text-blue-300">-</div>
</div>
</div>
</td>
<td class="py-1 text-left">
<div class="px-2">
<div v-if="player.rank.flex5v5" class="flex items-center">
<div class="inline-block text-center">
<svg class="w-5 h-5">
<use :xlink:href="`#rank-${player.rank.flex5v5.tier.toLowerCase()}`" />
</svg>
<div class="text-xs font-semibold text-blue-300 mt-2px">
{{ player.rank.flex5v5.shortName }}
</div>
</div>
<div class="ml-5 text-center">
<div class="font-semibold">{{ player.rank.flex5v5.winrate }}</div>
<div class="mt-1 text-xs text-blue-300">
{{ player.rank.flex5v5.wins + player.rank.flex5v5.losses }} games
</div>
</div>
</div>
<div v-else class="w-5 h-5">
<div class="-mt-1 text-2xl text-blue-300">-</div>
</div>
</div>
</td>
<td class="py-1 text-right rounded-r-lg">
<div class="inline-block px-2">
<div
v-if="live.bannedChampions.length"
:class="[ally ? 'ban-blue border-teal-500' : 'ban-red border-red-500']"
class="relative border-2 rounded-full ban"
>
<div
:style="[
banChamp(index, player.teamId)
? {
backgroundImage: `url('https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/champion-icons/${
banChamp(index, player.teamId).championId
}.png')`,
}
: '',
]"
class="w-6 h-6 bg-center bg-cover rounded-full ban-img bg-blue-1000"
></div>
<div
:class="[ally ? 'text-teal-100 bg-teal-500' : 'text-red-100 bg-red-500']"
class="absolute flex items-center justify-center w-4 h-4 text-xs font-bold rounded-full ban-order"
>
{{ banChamp(index, player.teamId).pickTurn }}
</div>
</div>
<div v-else class="w-5 h-5 text-left">
<div class="text-2xl text-blue-300">-</div>
</div>
</div>
</td>
</tr>
</tbody>
<tbody v-else>
<tr v-for="index in 5" :key="index">
<td colspan="4" class="rounded-lg bg-blue-760">
<content-loader
:height="54"
:width="1160"
:speed="2"
primary-color="#17314f"
secondary-color="#2b6cb0"
>
<rect x="12" y="12" rx="3" ry="3" width="14" height="14" />
<rect x="12" y="32" rx="3" ry="3" width="14" height="14" />
<circle cx="64" cy="28" r="24" />
<rect x="96" y="10" rx="3" ry="3" width="16" height="16" />
<rect x="96" y="31" rx="3" ry="3" width="16" height="16" />
<rect x="124" y="32" rx="3" ry="3" width="50" height="12" />
<rect x="124" y="13" rx="3" ry="3" width="70" height="14" />
<rect x="640" y="35" rx="3" ry="3" width="40" height="10" />
<rect x="691" y="33" rx="3" ry="3" width="55" height="10" />
<rect x="647" y="8" rx="3" ry="3" width="25" height="20" />
<rect x="696" y="12" rx="3" ry="3" width="41" height="15" />
<rect x="860" y="35" rx="3" ry="3" width="40" height="10" />
<rect x="911" y="33" rx="3" ry="3" width="55" height="10" />
<rect x="867" y="8" rx="3" ry="3" width="25" height="20" />
<rect x="916" y="12" rx="3" ry="3" width="41" height="15" />
<circle cx="1137" cy="27" r="14" />
</content-loader>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import { mapActions, mapState } from 'vuex'
import { getPrimarRune, getSecondaryRune } from '@/helpers/summoner.js'
import { ContentLoader } from 'vue-content-loader'
export default {
components: {
ContentLoader,
},
props: {
team: {
type: Array,
required: true,
},
ally: {
type: Boolean,
default: true,
},
gamemode: {
type: String,
default: '',
},
},
data() {
return {
clashGameBanOrder: {
100: [1, 3, 5, 8, 10],
200: [2, 4, 6, 7, 9],
},
customGameBanOrder: {
100: [1, 3, 5, 2, 4],
200: [2, 4, 6, 1, 3],
},
}
},
computed: {
isClash() {
return this.gamemode === 'CLASH'
},
isCustom() {
return this.gamemode === 'Custom Game'
},
...mapState({
account: (state) => state.summoner.basic.account,
live: (state) => state.summoner.live.match,
liveLoaded: (state) => state.summoner.live.liveLoaded,
}),
},
methods: {
banChamp(index, teamId) {
if (teamId === 200 && !this.isCustom && !this.isClash) {
index += 5
}
let toFind = index + 1
if (this.isClash) {
toFind = this.clashGameBanOrder[teamId][index]
} else if (this.isCustom) {
toFind = this.customGameBanOrder[teamId][index]
}
return this.live.bannedChampions.find((b) => b.pickTurn === toFind && b.teamId === teamId)
},
borderChampion(id) {
if (id === this.account.id) {
return 'border-yellow-500'
}
return this.ally ? 'border-teal-400' : 'border-red-400'
},
getCSSVars(championId) {
return {
'--bg-img': `linear-gradient(90deg, rgba(42, 67, 101, 0.3) 0%, rgba(42, 67, 101, 0.8) 40%, rgba(42, 67, 101, 1) 100%),
url('https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/champion-splashes/${championId}/${championId}000.jpg')`,
}
},
selectRunes(player) {
if (!player.perks) return
this.displayOrHideRunes(player.perks)
},
getPrimarRune,
getSecondaryRune,
...mapActions('cdragon', ['displayOrHideRunes']),
},
}
</script>
<style scoped>
.w-team {
width: 40rem;
}
.w-ranked {
width: 13.75rem;
}
.w-bans {
width: 5rem;
}
.live-team-row td {
position: relative;
z-index: 20;
}
.live-team-row td:first-child:before {
content: '';
position: absolute;
z-index: -10;
top: 0;
left: 0;
bottom: 0;
width: 1160px;
background-image: var(--bg-img);
background-position: center;
background-size: cover;
border-radius: 0.5rem;
}
.runes {
@apply transition-all duration-150 ease-in-out;
}
.runes:hover {
filter: brightness(1.3);
}
</style>

View file

@ -1,268 +0,0 @@
<template>
<div class="flex mt-3 text-center">
<div class="w-3/12">
<div class="rounded-lg bg-blue-850" style="width: 300px; height: 339px">
<content-loader
:height="339"
:width="300"
:speed="2"
primary-color="#17314f"
secondary-color="#2b6cb0"
>
<rect x="94" y="18" rx="3" ry="3" width="111" height="24" />
<rect x="23" y="76" rx="3" ry="3" width="74" height="12" />
<rect x="130" y="76" rx="3" ry="3" width="34" height="12" />
<rect x="185" y="76" rx="3" ry="3" width="48" height="12" />
<rect x="246" y="76" rx="3" ry="3" width="30" height="12" />
<circle cx="40" cy="123" r="16" />
<rect x="60" y="116" rx="3" ry="3" width="49" height="13" />
<rect x="130" y="116" rx="3" ry="3" width="42" height="16" />
<rect x="185" y="116" rx="3" ry="3" width="42" height="16" />
<rect x="246" y="116" rx="3" ry="3" width="42" height="16" />
<circle cx="40" cy="171" r="16" />
<rect x="60" y="163" rx="3" ry="3" width="49" height="13" />
<rect x="130" y="163" rx="3" ry="3" width="42" height="16" />
<rect x="185" y="163" rx="3" ry="3" width="42" height="16" />
<rect x="246" y="163" rx="3" ry="3" width="42" height="16" />
<circle cx="40" cy="219" r="16" />
<rect x="60" y="212" rx="3" ry="3" width="49" height="13" />
<rect x="130" y="212" rx="3" ry="3" width="42" height="16" />
<rect x="185" y="212" rx="3" ry="3" width="42" height="16" />
<rect x="246" y="212" rx="3" ry="3" width="42" height="16" />
<circle cx="40" cy="267" r="16" />
<rect x="60" y="260" rx="3" ry="3" width="49" height="13" />
<rect x="130" y="260" rx="3" ry="3" width="42" height="16" />
<rect x="185" y="260" rx="3" ry="3" width="42" height="16" />
<rect x="246" y="260" rx="3" ry="3" width="42" height="16" />
<circle cx="40" cy="315" r="16" />
<rect x="60" y="308" rx="3" ry="3" width="49" height="13" />
<rect x="130" y="308" rx="3" ry="3" width="42" height="16" />
<rect x="185" y="308" rx="3" ry="3" width="42" height="16" />
<rect x="246" y="308" rx="3" ry="3" width="42" height="16" />
</content-loader>
</div>
<div class="mt-4 rounded-lg bg-blue-850" style="width: 300px; height: 828px">
<content-loader
:height="828"
:width="300"
:speed="2"
primary-color="#17314f"
secondary-color="#2b6cb0"
>
<rect x="122" y="17" rx="3" ry="3" width="55" height="24" />
<rect x="26" y="72" rx="4" ry="4" width="8" height="51" />
<rect x="86" y="72" rx="4" ry="4" width="8" height="51" />
<rect x="146" y="72" rx="4" ry="4" width="8" height="51" />
<rect x="206" y="72" rx="4" ry="4" width="8" height="51" />
<rect x="266" y="72" rx="4" ry="4" width="8" height="51" />
<rect x="22" y="128" rx="3" ry="3" width="16" height="16" />
<rect x="82" y="128" rx="3" ry="3" width="16" height="16" />
<rect x="142" y="128" rx="3" ry="3" width="16" height="16" />
<rect x="202" y="128" rx="3" ry="3" width="16" height="16" />
<rect x="262" y="128" rx="3" ry="3" width="16" height="16" />
<rect x="14" y="182" rx="3" ry="3" width="45" height="16" />
<rect x="93" y="182" rx="3" ry="3" width="45" height="16" />
<rect x="155" y="182" rx="3" ry="3" width="58" height="16" />
<rect x="250" y="182" rx="3" ry="3" width="33" height="16" />
<rect x="14" y="212" rx="3" ry="3" width="45" height="12" />
<rect x="93" y="212" rx="3" ry="3" width="45" height="12" />
<rect x="168" y="212" rx="3" ry="3" width="45" height="12" />
<rect x="238" y="212" rx="3" ry="3" width="45" height="12" />
<rect x="14" y="237" rx="3" ry="3" width="45" height="12" />
<rect x="93" y="237" rx="3" ry="3" width="45" height="12" />
<rect x="168" y="237" rx="3" ry="3" width="45" height="12" />
<rect x="238" y="237" rx="3" ry="3" width="45" height="12" />
<rect x="14" y="262" rx="3" ry="3" width="45" height="12" />
<rect x="93" y="262" rx="3" ry="3" width="45" height="12" />
<rect x="168" y="262" rx="3" ry="3" width="45" height="12" />
<rect x="238" y="262" rx="3" ry="3" width="45" height="12" />
<rect x="14" y="287" rx="3" ry="3" width="45" height="12" />
<rect x="93" y="287" rx="3" ry="3" width="45" height="12" />
<rect x="168" y="287" rx="3" ry="3" width="45" height="12" />
<rect x="238" y="287" rx="3" ry="3" width="45" height="12" />
<rect x="14" y="312" rx="3" ry="3" width="45" height="12" />
<rect x="93" y="312" rx="3" ry="3" width="45" height="12" />
<rect x="168" y="312" rx="3" ry="3" width="45" height="12" />
<rect x="238" y="312" rx="3" ry="3" width="45" height="12" />
<rect x="14" y="337" rx="3" ry="3" width="105" height="12" />
<rect x="238" y="337" rx="3" ry="3" width="45" height="12" />
<rect x="14" y="362" rx="3" ry="3" width="85" height="12" />
<rect x="238" y="362" rx="3" ry="3" width="45" height="12" />
<rect x="14" y="397" rx="3" ry="3" width="115" height="16" />
<rect x="150" y="397" rx="3" ry="3" width="65" height="16" />
<rect x="223" y="397" rx="3" ry="3" width="60" height="16" />
<rect x="14" y="427" rx="3" ry="3" width="95" height="12" />
<rect x="165" y="427" rx="3" ry="3" width="45" height="12" />
<rect x="238" y="427" rx="3" ry="3" width="45" height="12" />
<rect x="14" y="452" rx="3" ry="3" width="95" height="12" />
<rect x="165" y="452" rx="3" ry="3" width="45" height="12" />
<rect x="238" y="452" rx="3" ry="3" width="45" height="12" />
<rect x="14" y="487" rx="3" ry="3" width="115" height="16" />
<rect x="150" y="487" rx="3" ry="3" width="65" height="16" />
<rect x="223" y="487" rx="3" ry="3" width="60" height="16" />
<rect x="14" y="517" rx="3" ry="3" width="95" height="12" />
<rect x="165" y="517" rx="3" ry="3" width="45" height="12" />
<rect x="238" y="517" rx="3" ry="3" width="45" height="12" />
<rect x="14" y="542" rx="3" ry="3" width="95" height="12" />
<rect x="165" y="542" rx="3" ry="3" width="45" height="12" />
<rect x="238" y="542" rx="3" ry="3" width="45" height="12" />
<rect x="14" y="577" rx="3" ry="3" width="115" height="16" />
<rect x="150" y="577" rx="3" ry="3" width="65" height="16" />
<rect x="223" y="577" rx="3" ry="3" width="60" height="16" />
<rect x="14" y="607" rx="3" ry="3" width="50" height="12" />
<rect x="165" y="607" rx="3" ry="3" width="45" height="12" />
<rect x="238" y="607" rx="3" ry="3" width="45" height="12" />
<rect x="14" y="632" rx="3" ry="3" width="50" height="12" />
<rect x="165" y="632" rx="3" ry="3" width="45" height="12" />
<rect x="238" y="632" rx="3" ry="3" width="45" height="12" />
<rect x="14" y="657" rx="3" ry="3" width="50" height="12" />
<rect x="165" y="657" rx="3" ry="3" width="45" height="12" />
<rect x="238" y="657" rx="3" ry="3" width="45" height="12" />
<rect x="14" y="682" rx="3" ry="3" width="50" height="12" />
<rect x="165" y="682" rx="3" ry="3" width="45" height="12" />
<rect x="238" y="682" rx="3" ry="3" width="45" height="12" />
<rect x="14" y="707" rx="3" ry="3" width="50" height="12" />
<rect x="165" y="707" rx="3" ry="3" width="45" height="12" />
<rect x="238" y="707" rx="3" ry="3" width="45" height="12" />
<rect x="14" y="732" rx="3" ry="3" width="50" height="12" />
<rect x="165" y="732" rx="3" ry="3" width="45" height="12" />
<rect x="238" y="732" rx="3" ry="3" width="45" height="12" />
<rect x="122" y="762" rx="3" ry="3" width="55" height="20" />
<rect x="115" y="789" rx="3" ry="3" width="70" height="12" />
<rect x="110" y="805" rx="3" ry="3" width="80" height="12" />
</content-loader>
</div>
<div class="mt-4 rounded-lg bg-blue-850" style="width: 300px; height: 384px">
<content-loader
:height="384"
:width="300"
:speed="2"
primary-color="#17314f"
secondary-color="#2b6cb0"
>
<rect x="112" y="17" rx="3" ry="3" width="75" height="24" />
<rect x="14" y="70" rx="3" ry="3" width="80" height="16" />
<rect x="150" y="70" rx="3" ry="3" width="40" height="16" />
<rect x="217" y="70" rx="3" ry="3" width="59" height="16" />
<rect x="14" y="100" rx="3" ry="3" width="85" height="12" />
<rect x="145" y="100" rx="3" ry="3" width="45" height="12" />
<rect x="218" y="100" rx="3" ry="3" width="65" height="12" />
<rect x="14" y="121" rx="3" ry="3" width="65" height="12" />
<rect x="145" y="121" rx="3" ry="3" width="45" height="12" />
<rect x="218" y="121" rx="3" ry="3" width="65" height="12" />
<rect x="14" y="142" rx="3" ry="3" width="85" height="12" />
<rect x="145" y="142" rx="3" ry="3" width="45" height="12" />
<rect x="218" y="142" rx="3" ry="3" width="65" height="12" />
<rect x="14" y="163" rx="3" ry="3" width="115" height="12" />
<rect x="145" y="163" rx="3" ry="3" width="45" height="12" />
<rect x="218" y="163" rx="3" ry="3" width="65" height="12" />
<rect x="14" y="184" rx="3" ry="3" width="85" height="12" />
<rect x="145" y="184" rx="3" ry="3" width="45" height="12" />
<rect x="218" y="184" rx="3" ry="3" width="65" height="12" />
<rect x="14" y="205" rx="3" ry="3" width="85" height="12" />
<rect x="145" y="205" rx="3" ry="3" width="45" height="12" />
<rect x="218" y="205" rx="3" ry="3" width="65" height="12" />
<rect x="14" y="226" rx="3" ry="3" width="65" height="12" />
<rect x="145" y="226" rx="3" ry="3" width="45" height="12" />
<rect x="218" y="226" rx="3" ry="3" width="65" height="12" />
<rect x="14" y="247" rx="3" ry="3" width="115" height="12" />
<rect x="145" y="247" rx="3" ry="3" width="45" height="12" />
<rect x="218" y="247" rx="3" ry="3" width="65" height="12" />
<rect x="14" y="268" rx="3" ry="3" width="115" height="12" />
<rect x="145" y="268" rx="3" ry="3" width="45" height="12" />
<rect x="218" y="268" rx="3" ry="3" width="65" height="12" />
<rect x="14" y="289" rx="3" ry="3" width="85" height="12" />
<rect x="145" y="289" rx="3" ry="3" width="45" height="12" />
<rect x="218" y="289" rx="3" ry="3" width="65" height="12" />
<rect x="14" y="310" rx="3" ry="3" width="65" height="12" />
<rect x="145" y="310" rx="3" ry="3" width="45" height="12" />
<rect x="218" y="310" rx="3" ry="3" width="65" height="12" />
<rect x="14" y="331" rx="3" ry="3" width="115" height="12" />
<rect x="145" y="331" rx="3" ry="3" width="45" height="12" />
<rect x="218" y="331" rx="3" ry="3" width="65" height="12" />
<rect x="14" y="352" rx="3" ry="3" width="85" height="12" />
<rect x="145" y="352" rx="3" ry="3" width="45" height="12" />
<rect x="218" y="352" rx="3" ry="3" width="65" height="12" />
</content-loader>
</div>
</div>
<div class="w-9/12">
<div
v-for="index in 10"
:key="index"
:class="{ 'mt-4': index !== 1 }"
class="ml-4 rounded-lg bg-blue-850"
style="width: 884px; height: 144px"
>
<content-loader
:height="144"
:width="884"
:speed="2"
primary-color="#17314f"
secondary-color="#2b6cb0"
>
<rect x="68" y="36" rx="8" ry="8" width="64" height="64" />
<rect x="140" y="40" rx="6" ry="6" width="24" height="24" />
<rect x="140" y="72" rx="6" ry="6" width="24" height="24" />
<rect x="206" y="46" rx="3" ry="3" width="81" height="22" />
<rect x="218" y="74" rx="3" ry="3" width="59" height="16" />
<rect x="305" y="36" rx="6" ry="6" width="32" height="32" />
<rect x="341" y="36" rx="6" ry="6" width="32" height="32" />
<rect x="377" y="36" rx="6" ry="6" width="32" height="32" />
<rect x="305" y="72" rx="6" ry="6" width="32" height="32" />
<rect x="341" y="72" rx="6" ry="6" width="32" height="32" />
<rect x="377" y="72" rx="6" ry="6" width="32" height="32" />
<rect x="430" y="45" rx="3" ry="3" width="50" height="10" />
<rect x="430" y="60" rx="3" ry="3" width="50" height="10" />
<rect x="430" y="75" rx="3" ry="3" width="70" height="10" />
<rect x="430" y="90" rx="3" ry="3" width="70" height="10" />
<circle cx="608" cy="32" r="12" />
<circle cx="608" cy="52" r="12" />
<circle cx="608" cy="72" r="12" />
<circle cx="608" cy="92" r="12" />
<circle cx="608" cy="112" r="12" />
<circle cx="672" cy="32" r="12" />
<circle cx="672" cy="52" r="12" />
<circle cx="672" cy="72" r="12" />
<circle cx="672" cy="92" r="12" />
<circle cx="672" cy="112" r="12" />
<rect x="516" y="29" rx="3" ry="3" width="72" height="9" />
<rect x="516" y="49" rx="3" ry="3" width="72" height="9" />
<rect x="516" y="69" rx="3" ry="3" width="72" height="9" />
<rect x="516" y="89" rx="3" ry="3" width="72" height="9" />
<rect x="516" y="109" rx="3" ry="3" width="72" height="9" />
<rect x="691" y="29" rx="3" ry="3" width="72" height="9" />
<rect x="691" y="49" rx="3" ry="3" width="72" height="9" />
<rect x="691" y="69" rx="3" ry="3" width="72" height="9" />
<rect x="691" y="89" rx="3" ry="3" width="72" height="9" />
<rect x="691" y="109" rx="3" ry="3" width="72" height="9" />
<circle cx="830" cy="50" r="12" />
<rect x="800" y="66" rx="3" ry="3" width="64" height="17" />
<rect x="803" y="90" rx="3" ry="3" width="59" height="14" />
</content-loader>
</div>
<div class="mx-auto mt-4" style="width: 135px; height: 40px">
<content-loader
:height="40"
:width="135"
:speed="2"
primary-color="#17314f"
secondary-color="#2b6cb0"
>
<rect x="0" y="0" rx="6" ry="6" width="135" height="40" />
</content-loader>
</div>
</div>
</div>
</template>
<script>
import { ContentLoader } from 'vue-content-loader'
export default {
components: {
ContentLoader,
},
}
</script>

View file

@ -1,156 +0,0 @@
<template>
<div class="bg-blue-800 rounded-lg">
<div class="relative flex items-center justify-center py-4 text-blue-200 rounded-t-lg heading">
<svg class="w-5 h-5" style="transform: rotate(-5deg)">
<use xlink:href="#layers" />
</svg>
<span class="mx-4 text-lg font-semibold uppercase">CHAMPIONS</span>
<svg class="w-5 h-5" style="transform: rotate(5deg)">
<use xlink:href="#layers" />
</svg>
<div class="absolute top-0 right-0 mt-3 mr-2">
<Tooltip>
<template #trigger>
<svg class="w-4 h-4 cursor-pointer">
<use xlink:href="#info" />
</svg>
</template>
<template #default>
<div class="px-2 text-sm text-center text-white select-none">
<div>Stats based on</div>
<div>
<span class="font-bold text-teal-400">{{
stats.global ? stats.global.count : 0
}}</span>
matches
</div>
<div class="mt-2 text-xs italic font-normal leading-tight text-blue-100">
Load more matches
<br />to have better results.
</div>
</div>
</template>
</Tooltip>
</div>
</div>
<div v-if="stats.champion.length">
<div
class="flex items-baseline px-4 mt-3 text-xs font-semibold text-left text-blue-300 uppercase"
>
<div class="ml-2 text-base text-blue-400 w-champion">Champion</div>
<div class="w-plays">Plays</div>
<div class="w-winrate">Winrate</div>
<div class="w-kda">KDA</div>
</div>
<ul class="mt-1 text-sm text-left text-gray-100">
<li
v-for="(champion, index) in stats.champion"
:key="index"
:class="[
{ 'rounded-b-lg': index === stats.champion.length - 1 },
{ 'bg-blue-760': index % 2 === 0 },
]"
class="relative flex items-center px-4 py-2 leading-tight"
>
<div class="absolute text-xs" style="left: 6px">{{ index + 1 }}.</div>
<div class="flex items-center ml-2 w-champion">
<div
:style="{ backgroundImage: `url('${champion.champion.icon}')` }"
class="flex-shrink-0 w-8 h-8 bg-center bg-cover rounded-full bg-blue-1000"
></div>
<div class="mx-1 truncate">{{ champion.champion.name }}</div>
</div>
<div class="w-plays">
<div class="text-xs text-purple-400">{{ champion.count }}</div>
<div
:style="{ width: widthBar(champion.count, mostPlayed) }"
class="h-1 bg-purple-400 rounded-full mt-2px"
></div>
</div>
<div class="w-winrate">
<div class="text-xs text-green-400">
{{ ((champion.wins * 100) / champion.count) | percent }}
</div>
<div
:style="{ width: widthBar(champion.wins, champion.count) }"
class="h-1 bg-green-400 rounded-full mt-2px"
></div>
</div>
<div class="w-kda">
<div class="text-xs text-blue-400">
{{ kda(champion.kills, champion.deaths, champion.assists) }}
</div>
<div
:style="{
width: widthBar(kda(champion.kills, champion.deaths, champion.assists), bestKda),
}"
class="h-1 bg-blue-400 rounded-full mt-2px"
></div>
</div>
</li>
</ul>
</div>
<div v-else class="px-4 py-2">
<div>No champions have been found.</div>
<div>😕</div>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
import Tooltip from '@/components/Common/Tooltip.vue'
export default {
components: {
Tooltip,
},
computed: {
bestKda() {
const bestChamp = this.stats.champion.reduce((a, b) => {
return this.kda(a.kills, a.deaths, a.assists) > this.kda(b.kills, b.deaths, b.assists)
? a
: b
})
return this.kda(bestChamp.kills, bestChamp.deaths, bestChamp.assists)
},
mostPlayed() {
return this.stats.champion.reduce((a, b) => (a.count > b.count ? a : b)).count
},
...mapState({
stats: (state) => state.summoner.overview.stats,
}),
},
methods: {
kda(kills, deaths, assists) {
if (kills === 0 && deaths === 0 && assists === 0) {
return 0
}
return this.$options.filters.round((kills + assists) / deaths)
},
widthBar(value, total) {
return `${(value * 36) / total}px`
},
},
}
</script>
<style scoped>
.w-champion {
width: 110px;
}
.w-plays {
width: 55px;
}
.w-winrate {
width: 65px;
}
.w-kda {
width: 36px;
}
</style>

View file

@ -1,108 +0,0 @@
<template>
<div class="mt-4 bg-blue-800 rounded-lg">
<div class="pb-2">
<div class="flex items-center justify-center py-4 text-blue-200 rounded-t-lg heading">
<svg class="w-5 h-5" style="transform: rotate(-5deg)">
<use xlink:href="#people" />
</svg>
<span class="mx-4 text-lg font-semibold uppercase">FRIENDS</span>
<svg class="w-5 h-5" style="transform: rotate(5deg)">
<use xlink:href="#people" />
</svg>
</div>
<div v-if="hasMates" class="px-4 py-2 text-sm text-left">
<div class="flex items-baseline text-xs font-semibold text-blue-300 uppercase">
<div class="w-2/4 text-base text-blue-400">Summoner</div>
<div class="w-1/4">Record</div>
<div class="w-1/4">Winrate</div>
</div>
<ul class="mt-1 text-gray-100">
<li
v-for="mate in mates.slice(0, maxMates)"
:key="mate.name"
class="flex items-center justify-between"
>
<router-link
:to="{ name: 'summoner', params: { region: $route.params.region, name: mate.name } }"
class="w-2/4 truncate hover:text-teal-200"
>{{ mate.name }}</router-link
>
<div class="w-1/4">{{ mate.wins }} / {{ mate.losses }}</div>
<div class="w-1/4">
<Tooltip>
<template #trigger>
<div class="h-2 bg-blue-900 rounded-full cursor-pointer">
<div
:class="getWinrateColor(mate.wins, mate.count)"
:style="{ width: `${winrate(mate.wins, mate.count)}%` }"
class="h-full rounded-full"
></div>
</div>
</template>
<template #default>
<div class="px-2 text-xs text-center text-white">
<div>Winrate</div>
<div>
<span
:class="getWinrateColor(mate.wins, mate.count, false)"
class="font-bold"
>{{ winrate(mate.wins, mate.count) | percent }}</span
>
</div>
</div>
</template>
</Tooltip>
</div>
</li>
</ul>
</div>
<div v-else class="px-4 py-2 text-center">
<div>No friends have been found.</div>
<div>😕</div>
</div>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
import Tooltip from '@/components/Common/Tooltip.vue'
export default {
components: {
Tooltip,
},
data() {
return {
maxMates: 15,
}
},
computed: {
hasMates() {
return this.mates.length > 0
},
...mapState({
mates: (state) => state.summoner.overview.stats.mates,
}),
},
methods: {
getWinrateColor(wins, count, background = true) {
const winrate = this.winrate(wins, count)
if (winrate >= 70) {
return background ? 'bg-yellow-400' : 'text-yellow-400'
} else if (winrate >= 60) {
return background ? 'bg-teal-500' : 'text-teal-500'
} else if (winrate >= 50) {
return background ? 'bg-teal-300' : 'text-teal-300'
}
return background ? 'bg-teal-200' : 'text-teal-200'
},
winrate(wins, count) {
return (wins * 100) / count
},
},
}
</script>

View file

@ -1,286 +0,0 @@
<template>
<div v-if="stats.global" class="mt-4 bg-blue-800 rounded-lg">
<div class="relative flex justify-center py-4 text-blue-200 rounded-t-lg heading">
<svg class="w-6 h-6">
<use xlink:href="#graph" />
</svg>
<span class="mx-4 text-lg font-semibold uppercase">STATS</span>
<svg class="w-6 h-6" style="transform: scaleX(-1)">
<use xlink:href="#graph" />
</svg>
<div class="absolute top-0 right-0 mt-3 mr-2">
<Tooltip>
<template #trigger>
<svg class="w-4 h-4 cursor-pointer">
<use xlink:href="#info" />
</svg>
</template>
<template #default>
<div class="px-2 text-sm text-center text-white select-none">
<div>Stats based on</div>
<div>
<span class="font-bold text-teal-400">{{ stats.global.count }}</span> matches
</div>
<div class="mt-2 text-xs italic font-normal leading-tight text-blue-100">
Load more matches
<br />to have better results.
</div>
</div>
</template>
</Tooltip>
</div>
</div>
<div class="flex items-center py-2 mt-2">
<div
v-for="(role, index) in stats.role"
:key="index"
class="flex flex-col items-center w-1/5"
>
<Tooltip>
<template #trigger>
<div class="flex flex-col justify-end w-2 h-12 bg-blue-900 rounded-full cursor-pointer">
<div
:style="{
height: (((role.count * 3) / mostPlayedRole) * role.wins) / role.count + 'rem',
}"
:class="roundedRoleWins(role.wins, role.count)"
class="bg-green-400"
></div>
<div
:style="{
height: (((role.count * 3) / mostPlayedRole) * role.losses) / role.count + 'rem',
}"
:class="roundedRoleLosses(role.losses, role.count)"
class="bg-red-400"
></div>
</div>
</template>
<template #default>
<div class="px-2 text-sm text-center text-white select-none">
<div>{{ role.role | capitalize }}</div>
<span :class="winLossColor(role.wins, role.losses).win" class="font-bold">{{
role.wins
}}</span>
<span class="mx-1 font-bold text-gray-400">-</span>
<span :class="winLossColor(role.wins, role.losses).loss" class="font-bold">{{
role.losses
}}</span>
<div :class="calculateWinrate(role.wins, role.count).color" class="mt-1 font-bold">
{{ calculateWinrate(role.wins, role.count).winrate | round }}%
</div>
</div>
</template>
</Tooltip>
<div
:style="{ backgroundImage: `url(${'/img/roles/' + role.role + '.png'})` }"
class="w-4 h-4 mt-1 bg-center bg-cover"
></div>
<div class="text-xs text-blue-200">{{ role.count }}</div>
</div>
</div>
<div class="py-2 text-sm text-center">
<div class="flex items-baseline px-4 text-xs font-semibold text-blue-300 uppercase">
<div class="w-1/4 text-base text-left text-blue-400">Stat</div>
<div class="w-1/4">Total</div>
<div class="w-1/4">Per min</div>
<div class="w-1/4">Avg</div>
</div>
<ul class="mt-1 text-gray-100">
<li
v-for="(stat, name, index) in globalStatsKeys"
:key="index"
:class="{ 'bg-blue-760': index % 2 !== 0 }"
class="flex items-center justify-between px-4 py-1 leading-tight"
>
<div class="w-1/4 text-left capitalize">{{ name }}</div>
<div class="w-1/4">{{ stat | kilo(false) }}</div>
<div class="w-1/4">{{ (stat / (stats.global.time / 60)) | round }}</div>
<div class="w-1/4">{{ (stat / stats.global.count) | round }}</div>
</li>
<li class="flex items-center justify-between px-4 py-1 leading-tight bg-blue-760">
<div class="w-1/4 text-left whitespace-no-wrap">Time</div>
<div class="w-1/4">{{ stats.global.time | secToHours }}</div>
<div class="w-1/4"></div>
<div class="w-1/4">{{ (stats.global.time / stats.global.count) | secToTime(true) }}</div>
</li>
<li class="flex items-center justify-between px-4 py-1 leading-tight">
<div class="w-1/4 text-left whitespace-no-wrap">KDA</div>
<div class="w-1/4">
{{ ((stats.global.kills + stats.global.assists) / stats.global.deaths) | round }}
</div>
</li>
<li class="flex items-center justify-between px-4 py-1 leading-tight bg-blue-760">
<div class="w-1/4 text-left whitespace-no-wrap">Kill participation</div>
<div class="w-1/4">{{ stats.global.kp | percent }}</div>
</li>
</ul>
<template v-if="leagueStatsByType('Ranked').length">
<div class="flex items-baseline px-4 mt-3 text-xs font-semibold text-blue-300 uppercase">
<div class="w-5/12 text-base text-left text-blue-400">Ranked</div>
<div class="w-3/12">Winrate</div>
<div class="w-4/12">Record</div>
</div>
<ul class="mt-1 text-gray-100">
<li
v-for="(league, index) in leagueStatsByType('Ranked')"
:key="index"
:class="{ 'bg-blue-760': index % 2 !== 0 }"
class="flex items-center justify-between px-4 py-1 leading-tight"
>
<div class="w-5/12 text-left capitalize">{{ league.name.toLowerCase() }}</div>
<div :class="calculateWinrate(league.wins, league.count).color" class="w-3/12">
{{ calculateWinrate(league.wins, league.count).winrate | percent }}
</div>
<div class="w-4/12">
<span :class="winLossColor(league.wins, league.losses).win" class="font-semibold">{{
league.wins
}}</span>
<span class="mx-1 font-semibold text-gray-400">-</span>
<span :class="winLossColor(league.wins, league.losses).loss" class="font-semibold">{{
league.losses
}}</span>
</div>
</li>
</ul>
</template>
<template v-if="leagueStatsByType('Normal').length">
<div class="flex items-baseline px-4 mt-3 text-xs font-semibold text-blue-300 uppercase">
<div class="w-5/12 text-base text-left text-blue-400">Normal</div>
<div class="w-3/12">Winrate</div>
<div class="w-4/12">Record</div>
</div>
<ul class="mt-1 text-gray-100">
<li
v-for="(league, index) in leagueStatsByType('Normal')"
:key="index"
:class="{ 'bg-blue-760': index % 2 !== 0 }"
class="flex items-center justify-between px-4 py-1 leading-tight"
>
<div class="w-5/12 text-left capitalize">{{ league.name.toLowerCase() }}</div>
<div :class="calculateWinrate(league.wins, league.count).color" class="w-3/12">
{{ calculateWinrate(league.wins, league.count).winrate | percent }}
</div>
<div class="w-4/12">
<span :class="winLossColor(league.wins, league.losses).win" class="font-semibold">{{
league.wins
}}</span>
<span class="mx-1 font-semibold text-gray-400">-</span>
<span :class="winLossColor(league.wins, league.losses).loss" class="font-semibold">{{
league.losses
}}</span>
</div>
</li>
</ul>
</template>
<div class="flex items-baseline px-4 mt-3 text-xs font-semibold text-blue-300 uppercase">
<div class="w-5/12 text-base text-left text-blue-400">Class</div>
<div class="w-3/12">Winrate</div>
<div class="w-4/12">Record</div>
</div>
<ul class="mt-1 text-gray-100">
<li
v-for="(championClass, index) in stats.class"
:key="index"
:class="{ 'bg-blue-760': index % 2 !== 0 }"
class="flex items-center justify-between px-4 py-1 leading-tight"
>
<div class="w-5/12 text-left capitalize">{{ championClass.id }}</div>
<div
:class="calculateWinrate(championClass.wins, championClass.count).color"
class="w-3/12"
>
{{ calculateWinrate(championClass.wins, championClass.count).winrate | percent }}
</div>
<div class="w-4/12">
<span
:class="winLossColor(championClass.wins, championClass.losses).win"
class="font-semibold"
>{{ championClass.wins }}</span
>
<span class="mx-1 font-semibold text-gray-400">-</span>
<span
:class="winLossColor(championClass.wins, championClass.losses).loss"
class="font-semibold"
>{{ championClass.losses }}</span
>
</div>
</li>
</ul>
</div>
<div class="flex flex-col items-center pb-2 leading-snug">
<div class="text-xl text-teal-400">
{{ calculateWinrate(stats.global.wins, stats.global.count).winrate | percent }}
</div>
<div class="flex text-sm">
<span :class="winLossColor(stats.global.wins, stats.global.losses).win" class>{{
stats.global.wins
}}</span>
<span class="mx-1 font-bold text-gray-400">-</span>
<span :class="winLossColor(stats.global.wins, stats.global.losses).loss" class>{{
stats.global.losses
}}</span>
</div>
<span class="text-xs">Global winrate</span>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
import Tooltip from '@/components/Common/Tooltip.vue'
import { gameModes } from '@/data/data.js'
export default {
components: {
Tooltip,
},
computed: {
mostPlayedRole() {
return Math.max(...this.stats.role.map((r) => r.count), 0)
},
globalStatsKeys() {
// eslint-disable-next-line no-unused-vars
const { id, wins, losses, count, time, kp, ...rest } = this.stats.global
return rest
},
...mapState({
stats: (state) => state.summoner.overview.stats,
}),
},
methods: {
calculateWinrate(wins, count) {
const winrate = count !== 0 ? (wins / count) * 100 : 0
const color = winrate >= 50 ? 'text-green-400' : 'text-red-400'
return {
winrate,
color,
}
},
leagueStatsByType(typeName) {
return this.stats.league
.map((l) => {
return { ...l, ...gameModes[l.id] }
})
.filter((l) => l.type === typeName)
},
roundedRoleLosses(win, count) {
return win === count ? 'rounded-full' : 'rounded-b-full'
},
roundedRoleWins(win, count) {
return win === count ? 'rounded-full' : 'rounded-t-full'
},
winLossColor(win, loss) {
const colors = {
win: 'text-gray-200',
loss: 'text-gray-200',
}
win >= loss ? (colors.win = 'text-green-400') : (colors.loss = 'text-red-400')
return colors
},
},
}
</script>

View file

@ -1,172 +0,0 @@
<template>
<div>
<div class="inline-block bg-blue-800 rounded-lg">
<div
class="relative flex items-center justify-center py-2 text-blue-200 rounded-t-lg heading"
>
<svg class="w-4 h-4">
<use xlink:href="#time" />
</svg>
<span class="mx-3 text-sm font-bold uppercase">Recent Activity</span>
<svg class="w-4 h-4">
<use xlink:href="#time" />
</svg>
</div>
<div class="p-3 pt-1">
<div class="flex">
<span class="ml-12 text-xs font-semibold text-blue-200">{{ gridDays[11].month }}</span>
<span class="ml-16 text-xs font-semibold text-blue-200">{{ gridDays[42].month }}</span>
<span class="ml-16 text-xs font-semibold text-blue-200">{{ gridDays[73].month }}</span>
<span class="ml-16 text-xs font-semibold text-blue-200">{{ gridDays[104].month }}</span>
</div>
<div class="flex mt-1">
<div class="flex flex-col">
<span class="text-xs font-semibold leading-snug text-blue-200">Mo</span>
<span class="mt-1 text-xs font-semibold leading-snug text-blue-200">Tu</span>
<span class="mt-1 text-xs font-semibold leading-snug text-blue-200">We</span>
<span class="mt-1 text-xs font-semibold leading-snug text-blue-200">Th</span>
<span class="mt-1 text-xs font-semibold leading-snug text-blue-200">Fr</span>
<span class="mt-1 text-xs font-semibold leading-snug text-blue-200">Sa</span>
<span class="mt-1 text-xs font-semibold leading-snug text-blue-200">Su</span>
</div>
<div
class="flex flex-col flex-wrap ml-1"
style="width: calc(20px * 15); height: calc(20px * 7)"
>
<Tooltip v-for="(day, index) in gridDays.slice(indexFirstMonday)" :key="day.timestamp">
<template #trigger>
<div
:class="[getCaseMargin(index), getCaseColor(day.matches)]"
class="w-4 h-4 ml-1 cursor-pointer"
/>
</template>
<template #default>
<div class="px-2 text-xs text-center text-blue-200 leading-5">
<div>
<span class="text-white font-semibold">{{ day.date }}</span>
<span>: </span>
<span class="font-bold text-teal-400">{{ day.matches }}</span>
<span> {{ day.matches > 1 ? 'games' : 'game' }}</span>
</div>
<template v-if="day.matches > 0">
<div>
<span>time played: </span>
<span class="font-semibold text-white">{{ day.time | secToHours }}</span>
</div>
<div>
<span>record: </span>
<span class="font-bold text-green-400">{{ day.wins }}</span>
<span> - </span>
<span class="font-bold text-red-400">{{ day.losses }}</span>
</div>
</template>
</div>
</template>
</Tooltip>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
import Tooltip from '@/components/Common/Tooltip.vue'
export default {
components: {
Tooltip,
},
data() {
return {
gridDays: [],
indexFirstMonday: 0,
nbColumns: 15,
options: {
year: 'numeric',
month: '2-digit',
day: 'numeric',
},
}
},
computed: {
...mapState({
recentActivity: (state) => state.summoner.basic.recentActivity,
}),
},
watch: {
recentActivity() {
this.fillGrid()
},
},
created() {
this.createGrid()
},
methods: {
createGrid() {
const nbDaysInGrid = this.nbColumns * 7
// Create array with all the days of the Grid
for (let i = 1; i <= nbDaysInGrid; i++) {
const day = new Date()
day.setDate(day.getDate() - nbDaysInGrid + i)
const formattedDay = day.toLocaleString(undefined, this.options)
this.gridDays.push({
date: formattedDay,
time: 0,
matches: 0,
wins: 0,
losses: 0,
day: day.toLocaleString('en', { weekday: 'long' }).substring(0, 2),
month: day.toLocaleString('en', { month: 'long' }).substring(0, 3),
})
}
this.fillGrid()
},
fillGrid() {
// Add all the matches made by the summoner
for (const match of this.recentActivity) {
const matchTime = new Date(match.day)
const formattedTime = matchTime.toLocaleString(undefined, this.options)
const dayOfTheMatch = this.gridDays.filter((e) => e.date === formattedTime)
if (dayOfTheMatch.length > 0) {
dayOfTheMatch[0].time = match.time
dayOfTheMatch[0].matches = match.wins + match.losses
dayOfTheMatch[0].wins = match.wins
dayOfTheMatch[0].losses = match.losses
}
}
// Get the index of the first Monday
this.indexFirstMonday = this.gridDays.findIndex((d) => d.day === 'Mo')
},
getCaseColor(nbMatches) {
/* TODO: change this */
if (nbMatches >= 6) {
return 'bg-teal-200'
} else if (nbMatches >= 4) {
return 'bg-teal-300'
} else if (nbMatches >= 2) {
return 'bg-teal-400'
} else if (nbMatches >= 1) {
return 'bg-teal-500'
}
return 'bg-teal-700'
},
getCaseMargin(index) {
if (index % 7 !== 0) {
return 'mt-1'
}
},
},
}
</script>

View file

@ -1,106 +0,0 @@
<template>
<div
@mouseenter="hover = true"
@mouseleave="hover = false"
:style="{
backgroundImage: `${hover ? gradientHover : gradient},
url('https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/champion-splashes/${
record.champion_id
}/${record.champion_id}000.jpg')`,
}"
:class="borderColor"
class="relative w-full p-4 mx-2 mt-6 leading-none bg-center bg-cover border rounded-lg record-card"
>
<div
:class="[
{ 'bg-blue-1000 bg-opacity-75': hover },
title.length > 15 ? 'text-sm' : 'text-base',
]"
:style="{ borderColor: hover ? color : 'transparent' }"
class="absolute top-0 left-0 px-3 py-2 mt-2 ml-2 font-medium leading-4 transition-colors duration-500 ease-in-out border border-transparent rounded-md"
>
<span :class="textColor" class="ml-0">{{ title }}</span>
</div>
<img
:src="`https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/champion-icons/${record.champion_id}.png`"
:class="[{ 'opacity-0 scale-125': hover }, borderColor]"
class="block w-16 h-16 mx-auto mt-10 transition duration-500 ease-in transform border-2 rounded-full"
alt="Champion Played"
/>
<div :style="{ textShadow: `-2px 1px 6px ${color}` }" class="mt-6 text-4xl">
{{ record.amount }}
</div>
<div class="text-sm">
<div class="mt-6">
<span :class="record.result ? 'text-green-400' : 'text-red-400'">{{
record.result ? 'Won' : 'Lost'
}}</span>
<span class="ml-1 font-semibold">{{ timeDifference(record.date) }}</span>
</div>
<div class="mt-2 text-gray-500">
As
<span class="font-semibold text-white">{{ record.champion.name }}</span>
</div>
</div>
<div class="mt-6 text-xs font-light text-right text-gray-200 opacity-25">
<span v-if="hover">{{ record.id }}</span>
<span v-else>{{ gameModes[record.gamemode].name }}</span>
</div>
</div>
</template>
<script>
import { gameModes } from '@/data/data.js'
import { timeDifference } from '@/helpers/functions.js'
export default {
props: {
borderColor: {
type: String,
required: true,
},
color: {
type: String,
required: true,
},
textColor: {
type: String,
required: true,
},
record: {
type: Object,
required: true,
},
title: {
type: String,
required: true,
},
},
data() {
return {
gradient:
'linear-gradient(180deg, rgba(42, 67, 101, 0.8) 0%, rgba(42, 67, 101, 0.95) 60%, rgba(42, 67, 101, 1) 100%)',
gradientHover:
'linear-gradient(rgba(42, 67, 101, 0.1) 0%, rgba(42, 67, 101, 0.3) 60%, rgba(42, 67, 101, 0.5) 100%)',
hover: false,
gameModes,
}
},
methods: {
timeDifference,
},
}
</script>
<style scoped>
.record-card {
max-width: 11rem;
}
.record-card:hover {
filter: brightness(1.2);
}
</style>

View file

@ -1,164 +0,0 @@
<template>
<div class="flex items-center ml-2 leading-none">
<div class="flex flex-col justify-center ml-1">
<div class="flex items-center">
<div
ref="leagueBorder"
:style="{ backgroundColor: colorBorder }"
class="relative flex items-center justify-center w-12 h-12 rounded-full percentage-circle"
>
<div class="relative p-1 bg-blue-900 rounded-full w-11 h-11">
<div
class="h-full bg-center bg-cover mt-2px"
:style="{ backgroundImage: `url(${selectedLeague.rankImgLink})` }"
></div>
</div>
</div>
<div class="ml-2 text-3xl font-bold text-teal-500 uppercase">
{{ selectedLeague.fullRank }}
</div>
<div class="ml-4 text-2xl font-bold">{{ selectedLeague.leaguePoints }} LP</div>
<div
v-if="selectedLeague.miniSeries"
class="flex items-center p-2 ml-2 bg-blue-800 rounded"
>
<div
v-for="(result, index) in bo"
:key="index + result"
:class="[{ 'ml-1': index !== 0 }, boGame(result)]"
class="w-3 h-3 rounded-full"
></div>
</div>
</div>
<div class="flex items-center mt-2">
<div class="relative inline-block text-white">
<select
v-model="selectedKey"
class="block w-full px-4 py-2 pr-8 text-lg font-bold leading-tight bg-blue-800 rounded-md appearance-none cursor-pointer hover:bg-blue-700 focus:outline-none"
>
<option v-for="(data, leagueName) in ranked" :key="leagueName" :value="leagueName">
{{ data.name }}
</option>
</select>
<div
class="absolute inset-y-0 right-0 flex items-center px-2 text-gray-700 pointer-events-none"
>
<svg class="w-5 h-5 text-white">
<use xlink:href="#chevron-down" />
</svg>
</div>
</div>
<div class="flex items-center p-2 ml-2 bg-blue-800 rounded">
<div class="text-base font-semibold uppercase">Record</div>
<div class="ml-2 font-semibold text-green-400">{{ selectedLeague.wins }}</div>
<span class="mx-1">-</span>
<div class="font-semibold text-red-400">{{ selectedLeague.losses }}</div>
<div class="ml-3 text-base font-semibold uppercase">Winrate</div>
<div
:class="[
'ml-2 text-base leading-tight font-semibold',
parseFloat(selectedLeague.winrate) >= 50 ? 'text-green-400' : 'text-red-400',
]"
>
{{ selectedLeague.winrate }}
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
ranked: {
type: Object,
required: true,
},
},
data() {
return {
currentDegree: 0,
rankColors: {
iron: '#574D4F',
bronze: '#8C523A',
silver: '#80989D',
gold: '#CD8837',
platinum: '#4E9996',
diamond: '#576BCE',
master: '#9D48E0',
grandmaster: '#CD4545',
challenger: '#F4C874',
},
selectedKey: Object.keys(this.ranked)[0],
}
},
computed: {
bo() {
return this.selectedLeague.miniSeries.progress.split('')
},
colorBorder() {
if (!this.selectedLeague.tier || this.selectedLeague.leaguePoints === 0) {
return '#2c5282'
}
return this.rankColors[this.selectedLeague.tier.toLowerCase()]
},
leagueDegrees() {
return (
((this.selectedLeague.leaguePoints <= 100 ? this.selectedLeague.leaguePoints : 100) * 360) /
100
)
},
selectedLeague() {
return this.ranked[this.selectedKey]
},
},
watch: {
selectedKey() {
this.currentDegree = 0
this.$refs.leagueBorder.style.backgroundImage = null
this.triggerAnimation()
},
},
mounted() {
this.triggerAnimation()
},
methods: {
animateLeagueDegrees(stop = false) {
if (stop || !this.$refs.leagueBorder) return
this.selectedLeague.leaguePoints > 50 ? (this.currentDegree += 2) : this.currentDegree++
const linearGradient =
this.currentDegree <= 180
? `linear-gradient(${90 + this.currentDegree}deg, transparent 50%, #2c5282 50%)`
: `linear-gradient(${this.currentDegree - 90}deg, transparent 50%, ${
this.colorBorder
} 50%)`
this.$refs.leagueBorder.style.backgroundImage = `${linearGradient}, linear-gradient(90deg, #2c5282 50%, transparent 50%)`
this.triggerAnimation()
},
boGame(result) {
switch (result) {
case 'W':
return 'bg-green-400'
case 'L':
return 'bg-red-400'
default:
return 'bg-blue-200'
}
},
triggerAnimation() {
setTimeout(() => {
if (this.currentDegree < 360 && this.currentDegree < this.leagueDegrees)
this.animateLeagueDegrees()
else this.animateLeagueDegrees(true)
}, 1)
},
},
}
</script>

View file

@ -1,132 +0,0 @@
export const maps = { 10: 'The Twisted Treeline', 11: "Summoner's Rift", 12: 'Howling Abyss' }
export const gameModes = {
0: {
type: 'Custom',
name: 'Custom Game',
},
900: {
type: 'Normal',
name: 'ARURF',
},
450: {
type: 'Normal',
name: 'ARAM',
},
400: {
type: 'Normal',
name: 'DRAFT 5vs5',
},
420: {
type: 'Ranked',
name: 'Solo/Duo',
},
430: {
type: 'Normal',
name: 'BLIND 5vs5',
},
440: {
type: 'Ranked',
name: 'FLEX 5vs5',
},
460: {
type: 'Normal',
name: 'BLIND 3vs3',
},
470: {
type: 'Ranked',
name: 'FLEX 3vs3',
},
700: {
type: 'Ranked',
name: 'CLASH',
},
720: {
type: 'Ranked',
name: 'CLASH ARAM',
},
800: {
type: 'Bot',
name: '3vs3 Co-op vs. AI (Intermediate)',
},
810: {
type: 'Bot',
name: '3vs3 Co-op vs. AI (Intro)',
},
820: {
type: 'Bot',
name: '3vs3 Co-op vs. AI (Beginner)',
},
830: {
type: 'Bot',
name: 'Co-op vs. AI (Intro)',
},
840: {
type: 'Bot',
name: 'Co-op vs. AI (Beginner)',
},
850: {
type: 'Bot',
name: 'Co-op vs. AI (Intermediate)',
},
920: {
type: 'Normal',
name: 'PORO KING',
},
1020: {
type: 'Normal',
name: 'One for All',
},
1300: {
type: 'Normal',
name: 'Nexus Blitz',
},
1400: {
type: 'Normal',
name: 'Ultimate Spellbook',
},
1900: {
type: 'Normal',
name: 'URF',
},
}
/* ========= OLD 5 COLORS ========= */
// KILLS, KP : green -> 71, 132, 116
// DEATHS, DMGCHAMP, DMGOBJ: red -> 156, 71, 109
// ASSISTS, GOLD, DMGTAKEN: golden -> 146, 100, 79
// MINIONS: purple -> 140, 101, 182
// VISION: blue -> 55, 118, 179
const colorValues = {
green: '54,148,109',
red: '197,85,93',
purple: '141,116,217',
teal: '104,186,191',
yellow: '166,176,134',
orange: '184,137,101',
brown: '161,127,134',
blue: '55, 118, 179',
}
export const colors = {
// match-details
kills: colorValues['green'],
deaths: colorValues['red'],
assists: colorValues['purple'],
minions: colorValues['teal'],
vision: colorValues['yellow'],
gold: colorValues['orange'],
dmgChamp: colorValues['red'],
dmgObj: colorValues['yellow'],
dmgTaken: colorValues['red'],
kp: colorValues['brown'],
// champions
winrate: colorValues['green'],
playrate: colorValues['purple'],
wins: colorValues['green'],
count: colorValues['purple'],
kda: colorValues['blue'],
gameLength: colorValues['green'],
}

View file

@ -1,54 +0,0 @@
/**
* Return the relative time betweeen a chosen moment and the current time
* @param previous : time we want to get difference
*/
export function timeDifference(previous) {
const current = new Date()
const msPerMinute = 60 * 1000
const msPerHour = msPerMinute * 60
const msPerDay = msPerHour * 24
const msPerWeek = msPerDay * 7
const elapsed = current - previous
if (elapsed < msPerMinute) {
return Math.round(elapsed / 1000) + ' seconds ago'
} else if (elapsed < msPerHour) {
return Math.round(elapsed / msPerMinute) + ' minutes ago'
} else if (elapsed < msPerDay) {
return Math.round(elapsed / msPerHour) + ' hours ago'
} else if (elapsed < msPerWeek) {
return Math.round(elapsed / msPerDay) + ' days ago'
} else {
const dateOptions = { day: '2-digit', month: '2-digit', year: 'numeric' }
return new Date(previous).toLocaleString(undefined, dateOptions).replace(/\//g, '.')
}
}
/**
* Convert seconds to a readable string
* @param {Number} seconds
*/
export function secToTime(seconds) {
const min = Math.floor(seconds / 60)
let newSec = Math.floor(seconds - min * 60)
newSec = newSec < 10 ? '0' + newSec : newSec
return `${min}:${newSec}`
}
/**
* Sort an array of players by role
*/
export function sortTeamByRole(a, b) {
const sortingArr = ['TOP', 'JUNGLE', 'MIDDLE', 'BOTTOM', 'UTILITY']
return sortingArr.indexOf(a.role) - sortingArr.indexOf(b.role)
}
/**
* Give the full CDragon image path from the iconPath field
* @param {String} iconPath
*/
export function createCDragonAssetUrl(iconPath) {
const name = iconPath.split('/assets/')[1].toLowerCase()
return `https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/${name}`
}

View file

@ -1,118 +0,0 @@
import { createCDragonAssetUrl, secToTime, timeDifference } from '@/helpers/functions.js'
import { maps, gameModes } from '@/data/data.js'
import store from '@/store'
/**
* Get the url of the of the player primary rune
* @param {Object} perks : from the API
*/
export function getPrimarRune(perks) {
const primaryRune = perks.selected.length
? store.state.cdragon.runes.perks[perks.selected[0]]
: null
return primaryRune ? createCDragonAssetUrl(primaryRune.icon) : null
}
/**
* Get the url of the of the player secondary rune
* @param {Object} perks : from the API
*/
export function getSecondaryRune(perks) {
const secondaryRune = store.state.cdragon.runes.perkstyles[perks.secondaryStyle]
return secondaryRune ? createCDragonAssetUrl(secondaryRune.icon) : null
}
/**
* Return all the infos about a list of matches built with the api data
* @param {Object} matches : all data from the api matches endpoint
*/
export function createMatchData(matches) {
for (const match of matches) {
// Runes
match.primaryRune = getPrimarRune(match.perks)
match.secondaryRune = getSecondaryRune(match.perks)
const date = new Date(match.date)
const dateOptions = { day: '2-digit', month: '2-digit', year: 'numeric' }
const timeOptions = { hour12: false, hour: '2-digit', minute: '2-digit' }
match.fullDate = {
date: date.toLocaleString(undefined, dateOptions),
time: date.toLocaleString(undefined, timeOptions),
}
match.date = timeDifference(match.date)
match.map = maps[match.map]
match.gamemode = gameModes[match.gamemode]
if (!match.gamemode) {
match.gamemode = { name: 'Unknown gamemode' }
}
}
return matches
}
/**
* Return the formatted basic info for a summoner
* @param {Object} summonerBasic : all data from the api basic endpoint
*/
export function createBasicSummonerData(summonerBasic) {
// Ranked Stats
summonerBasic.ranked.soloQ = getLeagueData(summonerBasic.ranked.soloQ, 'Solo/Duo')
if (!summonerBasic.ranked.soloQ) delete summonerBasic.ranked.soloQ
summonerBasic.ranked.flex5v5 = getLeagueData(summonerBasic.ranked.flex5v5, 'Flex 5vs5')
if (!summonerBasic.ranked.flex5v5) delete summonerBasic.ranked.flex5v5
summonerBasic.ranked.flex3v3 = getLeagueData(summonerBasic.ranked.flex3v3, 'Flex 3vs3')
if (!summonerBasic.ranked.flex3v3) delete summonerBasic.ranked.flex3v3
// If Summoner is Unranked
if (Object.entries(summonerBasic.ranked).length === 0) {
summonerBasic.ranked.soloQ = {
fullRank: 'Unranked',
rankImgLink: 'https://res.cloudinary.com/kln/image/upload/v1693310423/unranked.png',
leaguePoints: 0,
wins: 0,
losses: 0,
winrate: '0%',
name: 'Solo/Duo',
}
}
return summonerBasic
}
/**
* Return the formatted records of a summoner
* @param {Object} recordsDto : raw records from the database stats
*/
export function createRecordsData(recordsDto) {
const records = recordsDto.reduce((acc, record) => {
acc[record.what] = record
return acc
}, {})
records.game_duration.amount = secToTime(records.game_duration.amount)
records.gold.amount = records.gold.amount.toLocaleString()
records.damage_taken.amount = records.damage_taken.amount.toLocaleString()
records.damage_dealt_champions.amount = records.damage_dealt_champions.amount.toLocaleString()
records.damage_dealt_objectives.amount = records.damage_dealt_objectives.amount.toLocaleString()
records.kp.amount = `${records.kp.amount}%`
records.time_spent_living.amount = secToTime(records.time_spent_living.amount)
records.heal.amount = records.heal.amount.toLocaleString()
return records
}
/**
* Add rank img and ranked data
* @param {Object} leagueData
* @param {String} leagueName
*/
function getLeagueData(leagueData, leagueName) {
if (!leagueData) return null
leagueData.rankImgLink = `https://res.cloudinary.com/kln/image/upload/v1693310423/${leagueData.tier}.png`
leagueData.name = leagueName
return leagueData
}

View file

@ -1,367 +0,0 @@
<template>
<div class="flex flex-col min-h-screen overflow-hidden bg-blue-900">
<LazyBackground
image-source="/img/bg-homepage-1.jpg"
image-class="absolute z-0 w-full h-200"
more-backgrounds="linear-gradient(180deg, rgba(42, 67, 101, 0) 0%, #2A4365 50%),"
transition-name="fade"
></LazyBackground>
<header
:class="bgHeader ? 'header-scrolled' : 'bg-transparent'"
class="fixed left-0 right-0 z-20 px-4 text-teal-100 transition-colors duration-100 ease-in-out border-b-2 header"
style="border-color: rgba(144, 205, 244, 0.4)"
>
<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="/img/Logo.svg" alt="LeagueStats logo" />
</router-link>
</div>
<SearchForm @formSubmit="redirect" :homepage="false" />
<div class="flex-1">
<div class="flex items-center justify-end">
<a class="relative text-sm discord" href="https://discord.gg/RjBzjfk" target="_blank">
<svg
class="absolute fill-current"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 448 512"
>
<path
fill="currentColor"
d="M297.216 243.2c0 15.616-11.52 28.416-26.112 28.416-14.336 0-26.112-12.8-26.112-28.416s11.52-28.416 26.112-28.416c14.592 0 26.112 12.8 26.112 28.416zm-119.552-28.416c-14.592 0-26.112 12.8-26.112 28.416s11.776 28.416 26.112 28.416c14.592 0 26.112-12.8 26.112-28.416.256-15.616-11.52-28.416-26.112-28.416zM448 52.736V512c-64.494-56.994-43.868-38.128-118.784-107.776l13.568 47.36H52.48C23.552 451.584 0 428.032 0 398.848V52.736C0 23.552 23.552 0 52.48 0h343.04C424.448 0 448 23.552 448 52.736zm-72.96 242.688c0-82.432-36.864-149.248-36.864-149.248-36.864-27.648-71.936-26.88-71.936-26.88l-3.584 4.096c43.52 13.312 63.744 32.512 63.744 32.512-60.811-33.329-132.244-33.335-191.232-7.424-9.472 4.352-15.104 7.424-15.104 7.424s21.248-20.224 67.328-33.536l-2.56-3.072s-35.072-.768-71.936 26.88c0 0-36.864 66.816-36.864 149.248 0 0 21.504 37.12 78.08 38.912 0 0 9.472-11.52 17.152-21.248-32.512-9.728-44.8-30.208-44.8-30.208 3.766 2.636 9.976 6.053 10.496 6.4 43.21 24.198 104.588 32.126 159.744 8.96 8.96-3.328 18.944-8.192 29.44-15.104 0 0-12.8 20.992-46.336 30.464 7.68 9.728 16.896 20.736 16.896 20.736 56.576-1.792 78.336-38.912 78.336-38.912z"
/>
</svg>
<span class="ml-8">
Join
<span class="font-bold">our Discord</span>
</span>
</a>
</div>
</div>
</div>
</header>
<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 />
</template>
<template v-else-if="summonerFound">
<div class="flex items-center justify-between">
<div>
<div class="flex items-center mt-2">
<Tooltip>
<template #trigger>
<h1 class="text-4xl font-extrabold">
{{ basic.account.name }}
</h1>
</template>
<template #default>
<div
v-if="basic.account.names.length > 1"
class="px-2 text-sm text-center text-white select-none"
>
<div>Old summoner names</div>
<ul class="pl-2 text-left list-disc list-inside">
<li
v-for="name in basic.account.names.slice(0, -1)"
:key="name.date"
class="text-teal-400"
>
{{ name.name }}
</li>
</ul>
</div>
</template>
</Tooltip>
<div
v-if="playing"
class="flex items-center px-3 py-1 mt-2 ml-4 bg-teal-800 border border-teal-400 rounded-full"
>
<div class="w-2 h-2 rounded-full playing-dot bg-teal-flashy"></div>
<span class="ml-2 text-sm font-semibold text-teal-flashy">In Game</span>
</div>
<div
v-if="false"
class="inline-flex items-center px-2 py-1 mt-2 ml-4 leading-tight border border-teal-500 rounded"
style="background: rgba(40, 94, 97, 0.35)"
>
<svg class="w-4 h-4 text-teal-600">
<use xlink:href="#star" />
</svg>
<div class="ml-1 text-xs font-bold text-teal-200">Favorite</div>
</div>
</div>
<div class="flex mt-2">
<div :class="{ playing: playing }" class="relative w-24 h-24">
<div
:class="{ 'border-2': !playing }"
class="relative z-10 w-24 h-24 bg-center bg-cover border-teal-400 rounded-full bg-blue-1000"
:style="{
backgroundImage: `url('https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/profile-icons/${basic.account.profileIconId}.jpg')`,
}"
>
<div
class="absolute bottom-0 left-0 flex items-center justify-center w-8 h-8 text-xs font-extrabold text-teal-500 bg-blue-900 border-2 border-teal-400 rounded-full"
>
{{ basic.account.summonerLevel }}
</div>
</div>
</div>
<SummonerRanked
v-if="Object.entries(basic.ranked).length !== 0"
:ranked="basic.ranked"
/>
</div>
</div>
<div>
<RecentActivity />
</div>
</div>
<div class="flex items-center justify-between">
<!-- NAVIGATION -->
<div class="pb-2">
<router-link
:to="{
name: 'summoner',
params: { region: $route.params.region, name: $route.params.name },
}"
:class="isRouteActive('summoner')"
class="pb-2 text-blue-300 border-b-2 border-transparent cursor-pointer hover:text-blue-100"
exact
>Overview</router-link
>
<router-link
:to="{
name: 'summonerChampions',
params: { region: $route.params.region, name: $route.params.name },
}"
:class="isRouteActive('summonerChampions')"
class="pb-2 ml-4 text-blue-300 border-b-2 border-transparent cursor-pointer hover:text-blue-100"
exact
>Champions</router-link
>
<router-link
:to="{
name: 'summonerRecords',
params: { region: $route.params.region, name: $route.params.name },
}"
:class="isRouteActive('summonerRecords')"
class="pb-2 ml-4 text-blue-300 border-b-2 border-transparent cursor-pointer hover:text-blue-100"
exact
>Records</router-link
>
<router-link
:to="{
name: 'summonerLive',
params: { region: $route.params.region, name: $route.params.name },
}"
:class="isRouteActive('summonerLive')"
class="pb-2 ml-4 text-blue-300 border-b-2 border-transparent cursor-pointer hover:text-blue-100"
exact
>Live game</router-link
>
</div>
<!-- Select Season -->
<template v-if="$route.meta.season">
<FilterSeason />
</template>
</div>
</template>
<!-- View -->
<transition :name="tabTransition">
<slot></slot>
</transition>
</template>
<template v-else-if="summonerNotFound">
<div class="flex justify-center mt-16">
<div class="px-4 py-3 text-lg font-bold text-center text-blue-100 rounded-lg bg-gradient">
<div>Player can't be found.</div>
<div>😕</div>
</div>
</div>
</template>
</div>
<MainFooter />
</div>
</template>
<script>
import { mapState, mapActions, mapGetters } from 'vuex'
import FilterSeason from '@/components/Summoner/FilterSeason.vue'
import LazyBackground from '@/components/Common/LazyBackgroundImage.vue'
import MainFooter from '@/components/Layout/MainFooter.vue'
import RecentActivity from '@/components/Summoner/RecentActivity.vue'
import SearchForm from '@/components/Form/SearchForm.vue'
import HeaderLoader from '@/components/Summoner/HeaderLoader.vue'
import SummonerRanked from '@/components/Summoner/SummonerRanked.vue'
import Tooltip from '@/components/Common/Tooltip.vue'
export default {
components: {
FilterSeason,
LazyBackground,
MainFooter,
RecentActivity,
SearchForm,
HeaderLoader,
SummonerRanked,
Tooltip,
},
data() {
return {
bgHeader: false,
}
},
computed: {
tabTransition() {
return this.summonerFound && this.overviewLoaded ? 'tab' : 'none'
},
...mapState({
basic: (state) => state.summoner.basic,
}),
...mapGetters('summoner', [
'playing',
'overviewLoaded',
'summonerFound',
'summonerNotFound',
'summonerLoading',
]),
},
watch: {
$route(to, from) {
if (from.params.region === to.params.region && from.params.name === to.params.name) return
this.apiCall()
},
},
created() {
this.apiCall()
window.addEventListener('scroll', this.handleScroll)
},
destroyed() {
window.removeEventListener('scroll', this.handleScroll)
},
methods: {
apiCall() {
this.updateSettings({ name: 'region', value: this.$route.params.region.toLowerCase() })
this.basicRequest({ summoner: this.$route.params.name, region: this.$route.params.region })
},
handleScroll() {
this.bgHeader = window.scrollY > 25
},
isRouteActive(currentRoute) {
return {
'router-link-active': this.$route.name === currentRoute,
}
},
redirect(summoner, region) {
this.$router.push(`/summoner/${region}/${summoner}`).catch(() => {})
},
...mapActions('settings', ['updateSettings']),
...mapActions('summoner', ['basicRequest']),
},
metaInfo() {
return {
titleTemplate: this.summonerFound
? `${this.basic.account.name} | LeagueStats.gg %s`
: 'LeagueStats.gg %s',
}
},
}
</script>
<style scoped>
.header-scrolled {
background-color: rgba(42, 67, 101, 0.95);
}
.discord svg {
width: 22px;
height: 22px;
transform-origin: bottom left;
transition: 0.2s ease-in-out;
}
.discord:hover svg {
width: 24px;
height: 24px;
transform: rotate(-5deg);
}
.discord:hover span {
color: #ebf8ff;
}
.router-link-active {
color: #fff;
border-color: #fff;
}
.playing::before {
z-index: 0;
background: rgba(137, 160, 181, 0.2);
}
.playing::before,
.playing::after {
content: '';
position: absolute;
height: 100px;
width: 100px;
top: -2px;
left: -2px;
right: 0;
bottom: 0;
border-radius: 50%;
}
.playing::after {
z-index: 0;
background: linear-gradient(to top, rgba(0, 0, 0, 0) 30%, rgb(36, 232, 204) 100%);
animation: 0.75s linear 0s infinite normal none running rotate;
}
@keyframes rotate {
100% {
transform: rotate(360deg);
}
}
.playing-dot {
box-shadow: 0 0 0 0 rgba(0, 0, 0, 1);
transform: scale(1);
animation: 2.5s ease-in-out 0s infinite normal none running pulse;
}
@keyframes pulse {
0% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(36, 232, 204, 0.7);
}
70% {
transform: scale(1);
box-shadow: 0 0 0 8px rgba(0, 0, 0, 0);
}
100% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
}
}
</style>

View file

@ -1,5 +0,0 @@
<template>
<div>
<slot />
</div>
</template>

View file

@ -1,74 +0,0 @@
import Vue from 'vue'
import VueAxios from './plugins/axios'
import VueMeta from 'vue-meta'
import VuePlausible from './plugins/plausible'
import PortalVue from 'portal-vue'
import './assets/css/main.css'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
Vue.use(VueAxios)
Vue.use(VuePlausible)
Vue.use(VueMeta)
Vue.use(PortalVue)
Vue.filter('capitalize', (value) => {
return value.charAt(0).toUpperCase() + value.slice(1).toLowerCase()
})
Vue.filter('kilo', (value, shortFormat = true) => {
return value > 1000 || shortFormat ? `${+(value / 1000).toFixed(1)}k` : value
})
Vue.filter('secToTime', (sec, dotNotation = false) => {
if (isNaN(sec)) return 0
const min = Math.floor(sec / 60)
let newSec = Math.floor(sec - min * 60)
newSec = newSec < 10 ? '0' + newSec : newSec
return dotNotation ? `${min}:${newSec}` : `${min}m${newSec}s`
})
Vue.filter('secToHours', (sec) => {
if (isNaN(sec)) return 0
const result = []
const d = Math.floor(sec / (3600 * 24))
const h = Math.floor((sec % (3600 * 24)) / 3600)
const m = Math.floor((sec % 3600) / 60)
if (d > 0) {
result.push(d + ' days')
} else {
if (h > 0) {
result.push(h + 'h')
}
if (m > 0) {
result.push(m + 'm')
}
}
return result.join(' ')
})
Vue.filter('percent', (value) => {
return `${+value.toFixed(1)}%`
})
Vue.filter('round', (value, decimals = 2) => {
if (isNaN(value)) return 0
return parseFloat(value.toFixed(decimals))
})
new Vue({
router,
store,
render: (h) => h(App),
}).$mount('#app')

View file

@ -1,80 +0,0 @@
import { gameModes } from '@/data/data.js'
import { sortTeamByRole } from '@/helpers/functions.js'
import { mapState } from 'vuex'
export const liveGame = {
data() {
return {
gameLength: 0,
}
},
computed: {
allyTeam() {
if (!this.current || !this.current.participants) {
return []
}
return this.current.participants
.filter((p) => p.teamId === this.teamColor)
.sort(this.sortTeamByRole)
},
displayStartTime() {
if (this.current.gameStartTime === 0) {
return 'Not started yet'
}
return this.$options.filters.secToTime(this.gameLength, true)
},
enemyTeam() {
if (!this.current || !this.current.participants) {
return []
}
return this.current.participants
.filter((p) => p.teamId !== this.teamColor)
.sort(this.sortTeamByRole)
},
gamemode() {
if (this.current.participants) {
return this.current.gameType === 'CUSTOM_GAME'
? { type: '', name: 'Custom Game' }
: gameModes[this.current.gameQueueConfigId]
} else {
return { type: '', name: '' }
}
},
gameStartTime() {
return this.current ? this.current.gameStartTime : 0
},
teamColor() {
return this.current.participants.find((p) => p.summonerId === this.account.id).teamId
},
...mapState({
account: (state) => state.summoner.basic.account,
current: (state) => state.summoner.live.match,
}),
},
created() {
this.updateGameLength()
setInterval(() => {
this.gameLength++
}, 1000)
},
watch: {
gameStartTime() {
this.updateGameLength()
},
},
methods: {
updateGameLength() {
if (this.gameStartTime === 0) {
return (this.gameLength = 0)
}
this.gameLength = (new Date() - new Date(this.gameStartTime)) / 1000
},
sortTeamByRole,
},
}

View file

@ -1,34 +0,0 @@
import axiosHttp from 'axios'
import router from '../router'
import store from '../store'
export const axios = axiosHttp
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
axios.defaults.headers.common['Content-Type'] = 'application/json'
axios.defaults.baseURL = import.meta.env.DEV
? 'http://localhost:3333/'
: 'https://api.leaguestats.gg/'
const CancelToken = axios.CancelToken
const axiosSource = CancelToken.source()
axios.defaults.axiosSource = axiosSource
axios.defaults.cancelToken = axiosSource.token
// Add season number to data if the route need it
axios.interceptors.request.use(function (config) {
if (
config.method === 'post' &&
config.url !== 'summoner/basic' &&
router.currentRoute.meta.season
) {
config.data.season = store.state.summoner.basic.currentSeason
}
return config
})
export default {
install(Vue) {
Vue.prototype.$axios = axiosHttp
},
}

View file

@ -1,72 +0,0 @@
import Vue from 'vue'
import Router from 'vue-router'
import { axios } from './plugins/axios'
import Home from '@/views/Home.vue'
import Summoner from '@/views/Summoner.vue'
import SummonerChampions from '@/views/SummonerChampions.vue'
import SummonerLive from '@/views/SummonerLive.vue'
import SummonerRecords from '@/views/SummonerRecords.vue'
Vue.use(Router)
const router = new Router({
mode: 'history',
base: import.meta.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home,
meta: {
layout: 'Home',
},
},
{
path: '/summoner/:region/:name',
name: 'summoner',
component: Summoner,
meta: {
season: true,
},
},
{
path: '/summoner/:region/:name/champions',
name: 'summonerChampions',
component: SummonerChampions,
meta: {
season: true,
},
},
{
path: '/summoner/:region/:name/records',
name: 'summonerRecords',
component: SummonerRecords,
meta: {
season: true,
},
},
{
path: '/summoner/:region/:name/live',
name: 'summonerLive',
component: SummonerLive,
},
],
})
router.beforeEach((to, from, next) => {
if (to.params.name !== from.params.name && from.name !== null) {
// Cancel old requests
const axiosCancel = axios.defaults.axiosSource.cancel
axiosCancel('Summoner changed')
// Update cancel token
const CancelToken = axios.CancelToken
const axiosSource = CancelToken.source()
axios.defaults.axiosSource = axiosSource
axios.defaults.cancelToken = axiosSource.token
}
next()
})
export default router

View file

@ -1,43 +0,0 @@
import Vue from 'vue'
import Vuex from 'vuex'
import * as cdragon from '@/store/modules/cdragon'
import * as detailedMatch from '@/store/modules/detailedMatch'
import * as notification from '@/store/modules/notification'
import * as settings from '@/store/modules/settings'
import * as summoner from '@/store/modules/summoner'
Vue.use(Vuex)
const debug = import.meta.env.DEV
export default new Vuex.Store({
modules: {
cdragon,
detailedMatch,
notification,
settings,
summoner,
},
state: {
regionsList: {
br: 'br1',
eune: 'eun1',
euw: 'euw1',
jp: 'jp1',
kr: 'kr',
lan: 'la1',
las: 'la2',
na: 'na1',
oce: 'oc1',
tr: 'tr1',
ru: 'ru',
ph: 'ph2',
sg: 'sg2',
th: 'th2',
tw: 'tw2',
vn: 'vn2',
},
roles: ['TOP', 'JUNGLE', 'MIDDLE', 'BOTTOM', 'UTILITY'],
},
strict: debug,
})

View file

@ -1,45 +0,0 @@
import { axios } from '@/plugins/axios'
export const namespaced = true
export const state = {
kStats: [
[5008, 5005, 5007],
[5008, 5002, 5003],
[5001, 5002, 5003],
],
runes: null,
runesOpen: false,
selectedRunes: {},
}
export const mutations = {
DISPLAY_HIDE_RUNES(state, selectedRunes) {
state.runesOpen = !state.runesOpen
state.selectedRunes = selectedRunes
},
SET_RUNES(state, runes) {
state.runes = runes
},
}
export const actions = {
displayOrHideRunes({ commit }, selectedRunes) {
commit('DISPLAY_HIDE_RUNES', selectedRunes)
},
async getRunes({ commit, getters }) {
if (getters.runesLoaded) {
return
}
const { data } = await axios.get('cdragon/runes').catch((e) => {
console.log(e)
})
console.log(data)
commit('SET_RUNES', data)
},
}
export const getters = {
runesLoaded: (state) => state.runes,
}

View file

@ -1,83 +0,0 @@
import Vue from 'vue'
import { axios } from '@/plugins/axios'
export const namespaced = true
export const state = {
matches: [],
}
export const mutations = {
MATCH_LOADING(state, matchId) {
const alreadyIn = state.matches.find((m) => m.matchId === matchId)
if (!alreadyIn) {
state.matches.push({ matchId, status: 'loading' })
}
},
MATCH_FOUND(state, { matchDetails, ranksLoaded }) {
matchDetails.status = 'loaded'
matchDetails.ranksLoaded = ranksLoaded
// Set SoloQ as rank for now
if (ranksLoaded) {
for (const player of matchDetails.blueTeam.players) {
player.rank = player.rank && player.rank[420]
}
for (const player of matchDetails.redTeam.players) {
player.rank = player.rank && player.rank[420]
}
}
const index = state.matches.findIndex((m) => m.matchId === matchDetails.matchId)
Vue.set(state.matches, index, matchDetails)
},
MATCH_RANKS_FOUND(state, { matchId, ranksByPlayer }) {
const match = state.matches.find((m) => m.matchId === matchId)
for (const player of match.blueTeam.players) {
const ranks = ranksByPlayer[player.id]
if (!ranks) continue
Vue.set(player, 'rank', ranks[420])
}
for (const player of match.redTeam.players) {
const ranks = ranksByPlayer[player.id]
if (!ranks) continue
Vue.set(player, 'rank', ranks[420])
}
match.ranksLoaded = true
},
}
export const actions = {
async matchDetails({ commit }, matchId) {
commit('MATCH_LOADING', matchId)
console.log('MATCH DETAILS STORE', matchId)
const resp = await axios({ url: 'match/details', data: { matchId }, method: 'POST' }).catch(
() => {}
)
console.log('--- DETAILS INFOS ---')
console.log(resp.data)
const { matchDetails, ranksLoaded } = resp.data
commit('MATCH_FOUND', { matchDetails, ranksLoaded })
// If the ranks of the players are not yet known
if (!ranksLoaded) {
const ranks = await axios({
url: 'match/details/ranks',
data: { matchId },
method: 'POST',
}).catch(() => {})
if (!ranks) return
console.log('--- RANK OF MATCH DETAILS ---')
console.log(ranks.data)
commit('MATCH_RANKS_FOUND', { matchId, ranksByPlayer: ranks.data })
}
},
}
export const getters = {
getMatchDetails: (state) => (matchId) => state.matches.find((m) => m.matchId === matchId),
}

View file

@ -1,30 +0,0 @@
export const namespaced = true
export const state = {
notifications: [],
}
let nextId = 1
export const mutations = {
PUSH(state, notification) {
state.notifications.push({
...notification,
id: nextId++,
})
},
DELETE(state, notificationToRemove) {
state.notifications = state.notifications.filter(
(notification) => notification.id !== notificationToRemove.id
)
},
}
export const actions = {
add({ commit }, notification) {
commit('PUSH', notification)
},
remove({ commit }, notificationToRemove) {
commit('DELETE', notificationToRemove)
},
}

View file

@ -1,119 +0,0 @@
export const namespaced = true
export const state = {
favorites: [],
percent: false,
recentSearches: [],
region: 'euw',
}
export const mutations = {
ADD_FAVORITE(state, summoner) {
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 && s.region === summoner.region
)
if (alreadySearch) {
alreadySearch.date = Date.now()
searches.sort((a, b) => b.date - a.date)
return
}
if (searches.length > 10) {
searches.pop()
}
summoner.date = Date.now()
searches.unshift(summoner)
},
REMOVE_FAVORITE(state, summoner) {
state.favorites = state.favorites.filter(
(s) => s.name !== summoner.name || s.region !== summoner.region
)
},
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
},
}
export const actions = {
addRecentSearch({ commit, dispatch, state }, summoner) {
commit('ADD_SEARCH', summoner)
dispatch('updateSettings', {
name: 'recentSearches',
value: state.recentSearches,
isJson: true,
})
},
removeRecentSearch({ commit, dispatch }, summoner) {
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 && s.region === summoner.region
)
if (alreadyFav) {
commit('REMOVE_FAVORITE', summoner)
} else {
if (state.favorites.length >= 6) {
// Display error message
return dispatch(
'notification/add',
{
type: 'error',
message: 'Too many favorite summoners.',
},
{ 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 })
},
updatePercent({ commit }, percent) {
if (typeof percent !== 'boolean') {
percent = localStorage.getItem('settings-percent') === 'true'
} else {
localStorage.setItem('settings-percent', percent)
}
commit('UPDATE_SETTING', { name: 'percent', value: percent })
},
updateSettings({ commit }, { name, value, isJson = false }) {
if (!value) {
value = localStorage.getItem(name)
value = isJson ? JSON.parse(value) : value
if (!value) return
} else {
localStorage.setItem(name, isJson ? JSON.stringify(value) : value)
}
commit('UPDATE_SETTING', { name, value })
},
}

View file

@ -1,271 +0,0 @@
import { axios } from '@/plugins/axios'
import { createMatchData, createBasicSummonerData, createRecordsData } from '@/helpers/summoner'
export const namespaced = true
export const state = {
basic: {
account: {},
currentSeason: null,
ranked: {},
recentActivity: [],
seasons: [],
gamemodes: [],
status: '',
},
overview: {
NB_LOAD_GAMES: 10,
matches: [],
stats: {},
loaded: false,
matchesLoading: false,
moreMatchesToFetch: true,
},
champions: {
list: [],
championsLoaded: false,
},
records: {
list: {},
recordsLoaded: false,
},
live: {
match: {},
liveLoaded: false,
playing: false,
},
}
export const mutations = {
BASIC_REQUEST(state) {
state.basic.status = 'loading'
state.basic.currentSeason = null
state.champions.championsLoaded = false
state.records.recordsLoaded = false
state.overview.loaded = false
state.overview.moreMatchesToFetch = true
state.live.liveLoaded = false
},
CHAMPIONS_NOT_FOUND(state) {
state.champions.championsLoaded = false
},
CHAMPIONS_FOUND(state, { champions }) {
state.champions.list = champions
state.champions.championsLoaded = true
},
KEEP_LAST_X_MATCHES(state, number) {
state.overview.matches = state.overview.matches.slice(0, number)
},
LIVE_FOUND(state, { live }) {
state.live.match = live
state.live.liveLoaded = true
},
LIVE_LOADING(state) {
state.live.playing = true
state.live.liveLoaded = false
},
MATCHES_LOADING(state) {
state.overview.matchesLoading = true
},
MATCHES_FOUND(state, { newMatches, stats }) {
state.overview.matchesLoading = false
if (newMatches.length > 0) {
state.basic.recentActivity = stats.recentActivity
state.overview.matches = [...state.overview.matches, ...newMatches]
state.overview.stats = stats
state.champions.championsLoaded = false
state.records.recordsLoaded = false
}
state.overview.moreMatchesToFetch = newMatches.length > 0
},
OVERVIEW_FOUND(state, infos) {
state.basic.recentActivity = infos.stats.recentActivity
state.overview.matches = infos.matches
state.overview.stats = infos.stats
state.overview.loaded = true
state.records.recordsLoaded = false
state.overview.moreMatchesToFetch = infos.matches.length > 0
},
RECORDS_FOUND(state, { records }) {
state.records.list = records
state.records.recordsLoaded = true
},
SUMMONER_FOUND(state, infos) {
state.basic.account = infos.account
state.basic.ranked = infos.ranked
state.basic.recentActivity = infos.recentActivity
state.basic.seasons = infos.seasons.sort((a, b) => b - a)
state.basic.gamemodes = infos.gamemodes
state.basic.status = 'found'
state.live.match = infos.current
state.live.playing = infos.playing
},
SUMMONER_NOT_FOUND(state) {
state.basic.status = 'error'
},
SUMMONER_NOT_PLAYING(state) {
state.live.match = {}
state.live.playing = false
state.live.liveLoaded = false
},
UPDATE_SEASON(state, { season }) {
state.basic.currentSeason = season
state.overview.loaded = false
state.champions.championsLoaded = false
state.records.recordsLoaded = false
},
}
export const actions = {
async basicRequest({ commit, dispatch, rootState }, { summoner, region }) {
const regionId = rootState.regionsList[region]
commit('BASIC_REQUEST')
try {
const resp = await axios({
url: 'summoner/basic',
data: { summoner, region: regionId },
method: 'POST',
})
if (!resp.data) {
dispatch(
'notification/add',
{
type: 'error',
message: 'Summoner not found.',
},
{ root: true }
)
return commit('SUMMONER_NOT_FOUND')
}
console.log(`---SUMMONER INFOS ${resp.data.account.name}---`)
console.log(resp.data)
const infos = createBasicSummonerData(resp.data)
commit('SUMMONER_FOUND', infos)
// Add summoner to recent searches
dispatch(
'settings/addRecentSearch',
{
name: infos.account.name,
icon: infos.account.profileIconId,
region,
},
{ root: true }
)
} catch (error) {
if (error.response && error.response.status === 422) {
dispatch(
'notification/add',
{
type: 'error',
message: 'Summoner not found.',
},
{ root: true }
)
}
if (error.message !== 'Summoner changed') {
commit('SUMMONER_NOT_FOUND')
}
}
},
championsNotLoaded({ commit }) {
commit('CHAMPIONS_NOT_FOUND')
},
async championsRequest({ commit }, queue = null) {
const resp = await axios({
url: 'summoner/champions',
data: { puuid: state.basic.account.puuid, queue: queue },
method: 'POST',
}).catch(() => {})
console.log('---CHAMPIONS---')
console.log(resp.data)
commit('CHAMPIONS_FOUND', { champions: resp.data })
},
async liveMatchRequest({ commit, rootState }) {
commit('LIVE_LOADING')
const resp = await axios({
url: 'summoner/live',
data: {
id: state.basic.account.id,
region: rootState.regionsList[rootState.settings.region],
},
method: 'POST',
}).catch(() => {})
console.log('---LIVE---')
console.log(resp.data)
if (resp.data) {
commit('LIVE_FOUND', { live: resp.data })
} else {
commit('SUMMONER_NOT_PLAYING')
}
},
async moreMatches({ commit, rootState }) {
commit('MATCHES_LOADING')
if (!state.overview.matches.length) return
const lastMatchId = state.overview.matches[state.overview.matches.length - 1].matchId
const resp = await axios({
url: 'match',
data: {
puuid: state.basic.account.puuid,
region: rootState.regionsList[rootState.settings.region],
lastMatchId,
},
method: 'POST',
}).catch(() => {})
console.log('---MATCHES INFOS---')
console.log(resp.data)
const newMatches = createMatchData(resp.data.matches)
commit('MATCHES_FOUND', { newMatches, stats: resp.data.stats })
},
async overviewRequest({ commit, rootState }) {
const resp = await axios({
url: 'summoner/overview',
data: {
puuid: state.basic.account.puuid,
accountId: state.basic.account.accountId,
region: rootState.regionsList[rootState.settings.region],
},
method: 'POST',
}).catch(() => {})
console.log('---OVERVIEW---')
console.log(resp.data)
resp.data.matches = createMatchData(resp.data.matchesDetails)
commit('OVERVIEW_FOUND', resp.data)
},
async recordsRequest({ commit }) {
const resp = await axios({
url: 'summoner/records',
data: { puuid: state.basic.account.puuid },
method: 'POST',
}).catch(() => {})
console.log('---RECORDS---')
console.log(resp.data)
const records = resp.data.length ? createRecordsData(resp.data) : {}
commit('RECORDS_FOUND', { records })
},
sliceOverviewMatches({ commit }, number) {
commit('KEEP_LAST_X_MATCHES', number)
},
updateSeason({ commit }, season) {
commit('UPDATE_SEASON', { season })
},
}
export const getters = {
matchesLoading: (state) => state.overview.matchesLoading,
overviewLoaded: (state) => state.overview.loaded,
playing: (state) => state.live.playing,
regionFilterApplied: (state) => !!state.basic.currentSeason,
summonerFound: (state) => state.basic.status === 'found',
summonerNotFound: (state) => state.basic.status === 'error',
summonerLoading: (state) => state.basic.status === 'loading',
}

View file

@ -1,86 +0,0 @@
<template>
<div class="relative overflow-hidden bg-blue-900">
<LazyBackground
image-source="/img/bg-homepage-1.jpg"
image-class="absolute inset-0 bg-center"
transition-name="fade"
></LazyBackground>
<div class="flex flex-col items-center justify-center h-screen">
<div class="absolute top-0 right-0">
<div class="relative w-20 h-2 mt-4 mr-4 line line-top"></div>
</div>
<div class="absolute bottom-0 left-0">
<div class="relative w-20 h-2 mb-4 ml-4 line line-bottom"></div>
</div>
<div class="relative flex flex-col items-center w-full max-w-lg">
<img class="absolute logo" src="/img/Logo.svg" alt="logo" />
<SearchForm @formSubmit="redirect" :homepage="true" />
</div>
<p
class="absolute bottom-0 pb-4 leading-tight text-center text-blue-300 horizontal-center text-xxs"
>
LeagueStats.gg isn't endorsed by Riot Games and doesn't reflect the views or opinions of
Riot Games or anyone officially involved in producing or managing Riot Games properties.
<br />Riot Games, and all associated properties are trademarks or registered trademarks of
Riot Games, Inc.
</p>
</div>
</div>
</template>
<script>
import LazyBackground from '@/components/Common/LazyBackgroundImage.vue'
import SearchForm from '@/components/Form/SearchForm.vue'
export default {
components: {
LazyBackground,
SearchForm,
},
methods: {
redirect(summoner, region) {
this.$router.push(`/summoner/${region}/${summoner}`)
},
},
metaInfo() {
return {
title: 'LeagueStats.gg',
}
},
}
</script>
<style scoped>
.line {
background: rgba(178, 245, 234, 0.55);
}
.line::after {
content: '';
position: absolute;
width: 4.5rem;
height: 0.5rem;
background: rgba(178, 245, 234, 0.55);
}
.line-top::after {
transform-origin: bottom right;
transform: rotate(90deg) translateX(100%);
}
.line-bottom::after {
transform-origin: top left;
transform: rotate(90deg) translateX(-100%);
margin-left: 0.5rem;
}
.logo {
top: -160px;
}
</style>

View file

@ -1,125 +0,0 @@
<template>
<div
v-if="overviewLoaded"
key="overview"
class="relative flex items-start justify-between mt-3 text-center vue-sticky-container"
>
<VueStickySidebar
:top-spacing="48"
:bottom-spacing="123"
class="z-40 sidebar"
container-selector=".vue-sticky-container"
>
<SummonerChampions />
<SummonerStats />
<SummonerMates />
</VueStickySidebar>
<div class="w-9/12">
<div v-if="current && current.participants" class="mb-4">
<LiveMatch />
</div>
<div v-if="overview.matches.length">
<ul>
<Match
v-for="(match, index) in overview.matches"
:key="index"
:data="overview.matches[index]"
:index-match="index"
/>
</ul>
<LoadingButton
v-if="overview.moreMatchesToFetch"
@clicked="moreMatches"
:loading="matchesLoading"
btn-class="block px-4 py-2 mx-auto mt-4 font-semibold bg-blue-800 rounded-md shadow-lg hover:bg-blue-1000"
>More matches</LoadingButton
>
</div>
<div v-else>
<div class="flex justify-center">
<div class="px-4 py-3 text-lg font-bold text-center text-blue-100 rounded-lg bg-gradient">
<div>No matches found.</div>
<div>😕</div>
</div>
</div>
</div>
</div>
</div>
<div v-else>
<OverviewLoader />
</div>
</template>
<script>
import { mapState, mapActions, mapGetters } from 'vuex'
import LiveMatch from '@/components/Match/LiveMatch.vue'
import LoadingButton from '@/components/Form/LoadingButton.vue'
import Match from '@/components/Match/Match.vue'
import OverviewLoader from '@/components/Summoner/Overview/OverviewLoader.vue'
import SummonerChampions from '@/components/Summoner/Overview/SummonerChampions.vue'
import SummonerMates from '@/components/Summoner/Overview/SummonerMates.vue'
import SummonerStats from '@/components/Summoner/Overview/SummonerStats.vue'
import VueStickySidebar from 'vue-sticky-sidebar'
export default {
components: {
LiveMatch,
LoadingButton,
Match,
OverviewLoader,
SummonerChampions,
SummonerMates,
SummonerStats,
VueStickySidebar,
},
computed: {
...mapState({
current: (state) => state.summoner.live.match,
overview: (state) => state.summoner.overview,
}),
...mapGetters('summoner', ['matchesLoading', 'overviewLoaded', 'summonerFound']),
},
watch: {
overviewLoaded() {
this.fetchData()
},
summonerFound() {
this.fetchData()
},
},
created() {
this.fetchData()
this.getRunes()
},
methods: {
fetchData() {
if (!this.overviewLoaded && this.summonerFound) {
this.overviewRequest()
}
// Keep only the 10 last matches when summoner enters overview page
else if (this.overviewLoaded && this.summonerFound && this.overview.matches.length > 10) {
this.sliceOverviewMatches(10)
}
},
...mapActions('cdragon', ['getRunes']),
...mapActions('summoner', ['moreMatches', 'overviewRequest', 'sliceOverviewMatches']),
},
metaInfo() {
return {
title: 'Summoner Overview',
}
},
}
</script>
<style scoped>
.sidebar {
width: 300px;
}
</style>

View file

@ -1,101 +0,0 @@
<template>
<div key="champions" class="mt-3">
<div class="flex items-center space-x-4">
<ChampionsSearch @search-champions="updateSearch" />
<FilterQueue @filter-queue="filterByQueue" :choices="queues" />
<OnlyMostPlayed v-model="onlyMostPlayed" />
</div>
<ChampionsTable
:champions="champions"
:search="searchChampions"
:only-most-played="onlyMostPlayed"
class="mt-6"
/>
</div>
</template>
<script>
import { mapActions, mapGetters, mapState } from 'vuex'
import { gameModes } from '@/data/data.js'
import ChampionsSearch from '@/components/Summoner/Champions/ChampionsSearch.vue'
import ChampionsTable from '@/components/Summoner/Champions/ChampionsTable.vue'
import FilterQueue from '@/components/Summoner/Champions/FilterQueue.vue'
import OnlyMostPlayed from '@/components/Summoner/Champions/OnlyMostPlayed.vue'
export default {
components: {
ChampionsSearch,
ChampionsTable,
FilterQueue,
OnlyMostPlayed,
},
data() {
return {
onlyMostPlayed: false,
queue: null,
searchChampions: '',
}
},
computed: {
queues() {
// Only keep the gameModes the summoner has played
const queues = Object.keys(gameModes)
.filter(
(gameMode) =>
gameModes[gameMode].type !== 'Bot' && this.gamemodes.includes(Number(gameMode))
)
.reduce((obj, key) => {
return {
...obj,
[key]: gameModes[key],
}
}, {})
return { 0: { type: 'Normal', name: 'All queues' }, ...queues }
},
...mapGetters('summoner', ['summonerFound']),
...mapState({
champions: (state) => state.summoner.champions.list,
championsLoaded: (state) => state.summoner.champions.championsLoaded,
gamemodes: (state) => state.summoner.basic.gamemodes,
}),
},
watch: {
championsLoaded() {
this.fetchData()
},
summonerFound() {
this.fetchData()
},
},
created() {
this.fetchData()
},
methods: {
fetchData() {
if (!this.championsLoaded && this.summonerFound) {
this.championsRequest(this.queue)
}
},
filterByQueue(queue) {
queue = Number(queue)
this.queue = queue === 0 ? null : queue
this.championsRequest(this.queue)
},
updateSearch(search) {
this.searchChampions = search
},
...mapActions('summoner', ['championsRequest']),
},
metaInfo() {
return {
title: 'Summoner Champions',
}
},
}
</script>

View file

@ -1,88 +0,0 @@
<template>
<div key="live-game">
<div v-if="playing || summonerLoading">
<div v-if="liveLoaded" class="flex items-center justify-end -mt-4 text-base text-blue-200">
<div>{{ gamemode.type }} {{ gamemode.name }}</div>
<div class="mx-2">-</div>
<div :class="{ 'w-12': displayStartTime !== 'Not started yet' }">
{{ displayStartTime }}
</div>
<button
@click="liveMatchRequest"
class="px-3 py-1 ml-4 text-blue-100 bg-blue-800 rounded-md shadow-md hover:bg-blue-760"
>
Reload
</button>
</div>
<div v-else class="h-4"></div>
<LiveTeam :team="allyTeam" :ally="true" :gamemode="gamemode.name" />
<LiveTeam :team="enemyTeam" :ally="false" :gamemode="gamemode.name" class="mt-4" />
</div>
<div v-else>
<div class="flex justify-center mt-16">
<div class="px-4 py-3 text-lg font-bold text-center text-blue-100 rounded-lg bg-gradient">
<div>This summoner is not in game.</div>
<div class="mt-2">🕊</div>
<button
@click="liveMatchRequest"
class="px-3 py-1 my-4 text-sm text-blue-100 bg-blue-800 rounded-md shadow-md hover:bg-blue-760"
>
Reload
</button>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapActions, mapGetters, mapState } from 'vuex'
import { liveGame } from '@/mixins/liveGame'
import LiveTeam from '@/components/Summoner/Live/LiveTeam.vue'
export default {
components: {
LiveTeam,
},
mixins: [liveGame],
computed: {
...mapGetters('summoner', ['summonerLoading', 'summonerFound']),
...mapState({
live: (state) => state.summoner.live.match,
liveLoaded: (state) => state.summoner.live.liveLoaded,
playing: (state) => state.summoner.live.playing,
}),
},
watch: {
summonerFound() {
this.fetchData()
},
},
created() {
this.fetchData()
this.getRunes()
},
methods: {
fetchData() {
if (this.playing && !this.liveLoaded && this.summonerFound) {
this.liveMatchRequest()
}
},
...mapActions('cdragon', ['getRunes']),
...mapActions('summoner', ['liveMatchRequest']),
},
metaInfo() {
return {
title: 'Summoner Live Game',
}
},
}
</script>

View file

@ -1,322 +0,0 @@
<template>
<div key="records">
<template v-if="!recordsLoaded || (recordsLoaded && records.assists)">
<div
class="relative pl-6 text-2xl text-blue-200 border-b-2 border-blue-800 category blue-900"
>
Basics
</div>
<div class="flex flex-wrap -mx-2">
<template v-if="recordsLoaded">
<RecordCard
color="#63b3ed"
text-color="text-blue-400"
border-color="border-blue-400"
:record="records.kda"
title="KDA"
/>
<RecordCard
color="#68D391"
text-color="text-green-400"
border-color="border-green-400"
:record="records.kills"
title="Kills"
/>
<RecordCard
color="#9F7AEA"
text-color="text-purple-500"
border-color="border-purple-500"
:record="records.assists"
title="Assists"
/>
<RecordCard
color="#F56565"
text-color="text-red-500"
border-color="border-red-500"
:record="records.deaths"
title="Deaths"
/>
<RecordCard
color="#D69E2E"
text-color="text-yellow-600"
border-color="border-yellow-600"
:record="records.gold"
title="Gold earned"
/>
<RecordCard
color="#81E6D9"
text-color="text-teal-300"
border-color="border-teal-300"
:record="records.minions"
title="Minions killed"
/>
</template>
<template v-else>
<div
v-for="index in 6"
:key="index"
style="width: 176px; height: 294px"
class="mx-2 mt-6"
>
<content-loader
:height="294"
:width="176"
:speed="2"
primary-color="#17314f"
secondary-color="#2b6cb0"
>
<rect x="0" y="0" rx="8" ry="8" width="176" height="294" />
</content-loader>
</div>
</template>
</div>
<div
class="relative pl-6 mt-3 text-2xl text-blue-200 border-b-2 border-blue-800 blue-900 category"
>
Game impact
</div>
<div class="flex flex-wrap -mx-2">
<template v-if="recordsLoaded">
<RecordCard
color="#FC8181"
text-color="text-red-400"
border-color="border-red-400"
:record="records.damage_dealt_champions"
title="Damage champions"
/>
<RecordCard
color="#D69E2E"
text-color="text-yellow-400"
border-color="border-yellow-400"
:record="records.damage_dealt_objectives"
title="Damage objectives"
/>
<RecordCard
color="#FC8181"
text-color="text-red-400"
border-color="border-red-400"
:record="records.damage_taken"
title="Damage taken"
/>
<RecordCard
v-if="records.maxTowers"
color="#D69E2E"
text-color="text-yellow-400"
border-color="border-yellow-400"
:record="records.turret_kills"
title="Towers"
/>
<RecordCard
color="#68D391"
text-color="text-green-400"
border-color="border-green-400"
:record="records.kp"
title="Kill participation"
/>
<RecordCard
color="#D69E2E"
text-color="text-yellow-400"
border-color="border-yellow-400"
:record="records.vision_score"
title="Vision score"
/>
</template>
<template v-else>
<div
v-for="index in 6"
:key="index"
style="width: 176px; height: 294px"
class="mx-2 mt-6"
>
<content-loader
:height="294"
:width="176"
:speed="2"
primary-color="#17314f"
secondary-color="#2b6cb0"
>
<rect x="0" y="0" rx="8" ry="8" width="176" height="294" />
</content-loader>
</div>
</template>
</div>
<div class="relative pl-6 mt-3 text-2xl text-blue-200 border-b-2 border-blue-800 category">
Miscellaneous
</div>
<div class="flex flex-wrap -mx-2">
<template v-if="recordsLoaded">
<RecordCard
color="#4299E1"
text-color="text-blue-500"
border-color="border-blue-500"
:record="records.game_duration"
title="Longest game"
/>
<RecordCard
color="#4299E1"
text-color="text-blue-500"
border-color="border-blue-500"
:record="records.time_spent_living"
title="Longest living"
/>
<RecordCard
color="#D69E2E"
text-color="text-yellow-400"
border-color="border-yellow-400"
:record="records.critical_strike"
title="Critical Strike"
/>
<RecordCard
color="#68D391"
text-color="text-green-400"
border-color="border-green-400"
:record="records.heal"
title="Heal"
/>
</template>
<template v-else>
<div
v-for="index in 4"
:key="index"
style="width: 176px; height: 294px"
class="mx-2 mt-6"
>
<content-loader
:height="294"
:width="176"
:speed="2"
primary-color="#17314f"
secondary-color="#2b6cb0"
>
<rect x="0" y="0" rx="8" ry="8" width="176" height="294" />
</content-loader>
</div>
</template>
</div>
<div class="relative pl-6 mt-3 text-2xl text-blue-200 border-b-2 border-blue-800 category">
Multi kills
</div>
<div class="flex flex-wrap -mx-2">
<template v-if="recordsLoaded">
<RecordCard
color="#FEFCBF"
text-color="text-yellow-200"
border-color="border-yellow-200"
:record="records.double_kills"
title="Double kills"
/>
<RecordCard
color="#F6E05E"
text-color="text-yellow-400"
border-color="border-yellow-400"
:record="records.triple_kills"
title="Triple kills"
/>
<RecordCard
color="#D69E2E"
text-color="text-yellow-600"
border-color="border-yellow-600"
:record="records.quadra_kills"
title="Quadra kills"
/>
<RecordCard
color="#F56565"
text-color="text-red-500"
border-color="border-red-500"
:record="records.penta_kills"
title="Penta kills"
/>
<RecordCard
color="#63b3ed"
text-color="text-blue-400"
border-color="border-blue-400"
:record="records.killing_spree"
title="Killing Spree"
/>
</template>
<template v-else>
<div
v-for="index in 5"
:key="index"
style="width: 176px; height: 294px"
class="mx-2 mt-6"
>
<content-loader
:height="294"
:width="176"
:speed="2"
primary-color="#17314f"
secondary-color="#2b6cb0"
>
<rect x="0" y="0" rx="8" ry="8" width="176" height="294" />
</content-loader>
</div>
</template>
</div>
</template>
<template v-if="recordsLoaded && !records.assists">
<div class="flex flex-col items-center mt-4">
<div>No records have been found.</div>
<div>😕</div>
</div>
</template>
</div>
</template>
<script>
import { mapActions, mapGetters, mapState } from 'vuex'
import { ContentLoader } from 'vue-content-loader'
import RecordCard from '@/components/Summoner/Records/RecordCard.vue'
export default {
components: {
ContentLoader,
RecordCard,
},
computed: {
...mapGetters('summoner', ['summonerFound']),
...mapState({
records: (state) => state.summoner.records.list,
recordsLoaded: (state) => state.summoner.records.recordsLoaded,
}),
},
watch: {
recordsLoaded() {
this.fetchData()
},
summonerFound() {
this.fetchData()
},
},
created() {
this.fetchData()
},
methods: {
fetchData() {
if (!this.recordsLoaded && this.summonerFound) {
this.recordsRequest()
}
},
...mapActions('summoner', ['recordsRequest']),
},
metaInfo() {
return {
title: 'Summoner Records',
}
},
}
</script>
<style scoped>
.category:before {
@apply w-2 h-2 bg-blue-200 absolute block left-0 ml-1;
content: '';
top: 35%;
transform: rotate(45deg);
}
</style>

View file

@ -1,82 +0,0 @@
/*
** TailwindCSS Configuration File
**
** Docs: https://tailwindcss.com/docs/configuration
** Default: https://github.com/tailwindcss/tailwindcss/blob/master/stubs/defaultConfig.stub.js
*/
const defaultTheme = require('tailwindcss/defaultTheme')
module.exports = {
theme: {
customForms: (theme) => ({
default: {
checkbox: {
'width': theme('spacing.6'),
'height': theme('spacing.6'),
'backgroundColor': 'rgba(23, 49, 79, 0.6)',
'borderColor': theme('colors.blue.800'),
'textColor': theme('colors.blue.1000'),
'&:focus': {
backgroundColor: theme('colors.blue.1000'),
borderColor: theme('colors.blue.700'),
boxShadow: undefined,
},
'&:checked': {
backgroundColor: theme('colors.blue.1000'),
borderColor: 'transparent',
},
},
},
}),
extend: {
colors: {
teal: {
...defaultTheme.colors.teal,
flashy: '#24e8cc',
},
blue: {
...defaultTheme.colors.blue,
760: '#2C5C94',
850: '#2B4B74',
1000: '#17314f',
},
},
spacing: {
'2px': '2px',
'3p5': '0.875rem',
'4b': '1.15rem',
'11': '2.75rem',
},
borderWidth: {
3: '3px',
},
fontFamily: {
sans: ['Inter', ...defaultTheme.fontFamily.sans],
},
fontSize: {
xxs: '0.625rem',
},
height: {
'200': '50rem',
'1/2': '50%',
},
maxWidth: {
12: '3rem',
},
width: {
22: '5.5rem',
},
},
},
variants: {
textColor: ['responsive', 'hover', 'focus', 'group-hover'],
},
plugins: [require('@tailwindcss/custom-forms')],
purge: {
enabled: process.env.NODE_ENV === 'production',
content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
},
future: {
removeDeprecatedGapUtilities: true,
},
}

View file

@ -1,3 +0,0 @@
> 1%
last 2 versions
not ie <= 8

View file

@ -1,41 +1,13 @@
module.exports = {
root: true,
env: {
node: true
},
'extends': [
'plugin:vue/essential',
'plugin:vue/recommended',
'eslint:recommended'
],
rules: {
//'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-console': 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/multiline-html-element-content-newline': 'off',
"vue/html-self-closing": 'off',
'vue/max-attributes-per-line': 'off',
'vue/no-v-html': 'off',
"vue/attributes-order": ["error", {
"order": [
"DEFINITION",
"LIST_RENDERING",
"CONDITIONALS",
"RENDER_MODIFIERS",
"UNIQUE",
"TWO_WAY_BINDING",
"OTHER_DIRECTIVES",
"EVENTS",
"CONTENT",
"GLOBAL",
"OTHER_ATTR"
]
}],
"quotes": [2, "single", { "avoidEscape": true }],
"semi": [2, "never"]
node: true,
},
extends: ['plugin:vue/essential', 'eslint:recommended'],
parserOptions: {
parser: 'babel-eslint'
}
parser: '@babel/eslint-parser',
},
rules: {
'vue/multi-word-component-names': 'off',
},
}

4
client/.gitignore vendored
View file

@ -10,6 +10,7 @@ node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
@ -18,4 +19,5 @@ yarn-error.log*
*.ntvs*
*.njsproj
*.sln
*.sw*
*.sw?
*.lock

View file

@ -1,29 +1,37 @@
# LeagueStats Frontend
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
### Compile and hot-reload for development
```
npm run serve
npm run dev
```
### Compiles and minifies for production
### Compile and minify for production
```
npm run build
```
### Run your tests
### Test the production build locally
```
npm run test
npm run preview
```
### Lints and fixes files
### Lint files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
### Format files
```
npm run format
```

View file

@ -1,10 +1,14 @@
module.exports = {
presets: [
'@vue/app'
presets: ['@babel/preset-env'],
plugins: [
function () {
return {
visitor: {
MetaProperty(path) {
path.replaceWithSourceString('process')
},
},
}
},
],
env: {
production: {
plugins: ['transform-remove-console']
}
}
}

View file

@ -1,10 +0,0 @@
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": [
"./src/*"
]
}
}
}

31630
client/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,34 +1,38 @@
{
"name": "leaguestats-frontend",
"version": "0.1.0",
"private": true,
"version": "0.1.0",
"scripts": {
"dev": "npm run serve",
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.21.2",
"portal-vue": "^2.1.7",
"vue": "^2.6.12",
"vue-content-loader": "^0.2.3",
"vue-meta": "^2.4.0",
"vue-plausible": "^1.1.4",
"vue-router": "^3.5.1",
"vue-sticky-sidebar": "^1.0.5",
"vuex": "^3.6.2"
"dev": "vite",
"build": "vite build",
"preview": "vite preview --port 8080",
"lint": "eslint --ext .js,.ts,.jsx,.tsx,.vue --ignore-path .eslintignore .",
"format": "prettier --write \"**/*.{ts,tsx,js,css,vue}\" --ignore-path .prettierignore"
},
"devDependencies": {
"@babel/eslint-parser": "^7.22.15",
"@babel/preset-env": "^7.22.20",
"@tailwindcss/custom-forms": "^0.2.1",
"@vue/cli-plugin-babel": "^4.5.13",
"@vue/cli-plugin-eslint": "^4.5.13",
"@vue/cli-service": "^4.5.13",
"babel-eslint": "^10.1.0",
"babel-plugin-transform-remove-console": "^6.9.4",
"eslint": "^7.27.0",
"eslint-plugin-vue": "^7.9.0",
"@vitejs/plugin-vue2": "^2.2.0",
"autoprefixer": "^10.4.15",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-vue": "^9.17.0",
"postcss": "^8.4.30",
"postcss-import": "^12.0.1",
"tailwindcss": "^1.9.6",
"vue-template-compiler": "^2.6.12"
"vite": "^4.4.5",
"vite-plugin-eslint": "^1.8.1"
},
"dependencies": {
"axios": "^1.5.0",
"plausible-tracker": "^0.3.8",
"portal-vue": "^2.1.7",
"vue": "^2.7.14",
"vue-content-loader": "^0.2.3",
"vue-meta": "^2.4.0",
"vue-router": "^3.6.5",
"vue-sticky-sidebar": "^1.0.5",
"vuex": "^3.6.2"
}
}

View file

@ -1,6 +0,0 @@
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer')
]
}

View file

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View file

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

View file

Before

Width:  |  Height:  |  Size: 520 B

After

Width:  |  Height:  |  Size: 520 B

View file

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Some files were not shown because too many files have changed in this diff Show more