Huge changes: Sidebar, new UI
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
FROM node:lts-alpine AS base
|
FROM node:current-alpine AS base
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
FROM base AS build
|
FROM base AS build
|
||||||
|
|||||||
@@ -1,89 +1,28 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
type ItemTree = {
|
|
||||||
count: number
|
|
||||||
data: number
|
|
||||||
children: Array<ItemTree>
|
|
||||||
}
|
|
||||||
type Builds = {
|
|
||||||
start: Array<{count: number, data: number}>
|
|
||||||
tree: ItemTree
|
|
||||||
bootsFirst: number
|
|
||||||
boots: Array<{count: number, data: number}>
|
|
||||||
lateGame: Array<{count: number, data: number}>
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
builds: Builds
|
builds: Builds
|
||||||
}>()
|
}>()
|
||||||
const builds = props.builds
|
const builds = props.builds
|
||||||
|
|
||||||
type Item = {
|
|
||||||
id: number
|
|
||||||
}
|
|
||||||
type ItemResponse = {
|
|
||||||
data: Ref<Array<Item>>
|
|
||||||
}
|
|
||||||
|
|
||||||
const {data : items} : ItemResponse = await useFetch(CDRAGON_BASE + "plugins/rcp-be-lol-game-data/global/default/v1/items.json")
|
const {data : items} : ItemResponse = await useFetch(CDRAGON_BASE + "plugins/rcp-be-lol-game-data/global/default/v1/items.json")
|
||||||
const itemMap = reactive(new Map())
|
const itemMap = reactive(new Map())
|
||||||
for(let item of items.value) {
|
for(let item of items.value) {
|
||||||
itemMap.set(item.id, item)
|
itemMap.set(item.id, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
builds.tree.children.splice(1, builds.tree.children.length - 1)
|
||||||
|
if(builds.tree.children[0] != null && builds.tree.children[0] != undefined)
|
||||||
|
builds.tree.children[0].children.splice(1, builds.tree.children[0].children.length - 1)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div style="width: fit-content; height: fit-content;">
|
<div style="display: flex; width: fit-content; height: fit-content;">
|
||||||
|
|
||||||
|
<!-- Start items -->
|
||||||
<div class="item-box">
|
<div class="item-box">
|
||||||
<h2 class="item-box-title">start</h2>
|
<h2 class="item-box-title">start</h2>
|
||||||
<div style="display: flex; width: fit-content; height: fit-content; margin:auto;">
|
<div style="display: flex; flex-direction: column; width: fit-content; height: fit-content; margin:auto;">
|
||||||
<div style="margin-left: 5px; margin-right: 5px;" v-for="item in builds.start" >
|
<div style="margin-left: 5px; margin-right: 5px;" v-for="item in builds.start" >
|
||||||
<NuxtImg v-if="item.data != null && item.data != undefined"
|
|
||||||
style="border: 1px solid var(--color-on-surface);"
|
|
||||||
width="64" height="64" :alt="item.data.toString()"
|
|
||||||
:src="CDRAGON_BASE + mapPath(itemMap.get(item.data).iconPath)" />
|
|
||||||
<h3 style="width: fit-content; margin:auto; margin-bottom: 10px;">{{ item.count }}</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="builds.bootsFirst > 0.5" class="item-box" style="margin-top: 10px;">
|
|
||||||
<div style="display:flex; justify-content: center; align-items: baseline;">
|
|
||||||
<h2 class="item-box-title">boots rush</h2><h5 style="margin-left: 5px;">({{ (builds.bootsFirst * 100).toFixed(2) }}%)</h5>
|
|
||||||
</div>
|
|
||||||
<div style="display: flex; width: fit-content; height: fit-content; margin:auto;">
|
|
||||||
<div style="margin-left: 5px; margin-right: 5px;" v-for="item in builds.boots" >
|
|
||||||
<NuxtImg v-if="item.data != null && item.data != undefined"
|
|
||||||
style="border: 1px solid var(--color-on-surface);"
|
|
||||||
width="64" height="64" :alt="item.data.toString()"
|
|
||||||
:src="CDRAGON_BASE + mapPath(itemMap.get(item.data).iconPath)" />
|
|
||||||
<h3 style="width: fit-content; margin:auto; margin-bottom: 10px;">{{ item.count }}</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="item-box" style="margin-top: 10px;">
|
|
||||||
<h2 class="item-box-title">core</h2>
|
|
||||||
<TreeItem style="margin:auto; width: fit-content;" :tree="builds.tree" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="builds.bootsFirst <= 0.5" class="item-box" style="margin-top: 10px;">
|
|
||||||
<h2 class="item-box-title">boots</h2>
|
|
||||||
<div style="display: flex; width: fit-content; height: fit-content; margin:auto;">
|
|
||||||
<div style="margin-left: 5px; margin-right: 5px;" v-for="item in builds.boots" >
|
|
||||||
<NuxtImg v-if="item.data != null && item.data != undefined"
|
|
||||||
style="border: 1px solid var(--color-on-surface);"
|
|
||||||
width="64" height="64" :alt="item.data.toString()"
|
|
||||||
:src="CDRAGON_BASE + mapPath(itemMap.get(item.data).iconPath)" />
|
|
||||||
<h3 style="width: fit-content; margin:auto; margin-bottom: 10px;">{{ item.count }}</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="item-box" style="margin-top: 10px;">
|
|
||||||
<h2 class="item-box-title">late game</h2>
|
|
||||||
<div style="display: flex; width: fit-content; height: fit-content; margin:auto;">
|
|
||||||
<div style="margin-left: 5px; margin-right: 5px;" v-for="item in builds.lateGame.slice(0, 7)" >
|
|
||||||
<NuxtImg v-if="item.data != null && item.data != undefined"
|
<NuxtImg v-if="item.data != null && item.data != undefined"
|
||||||
class="item-img" width="64" height="64" :alt="item.data.toString()"
|
class="item-img" width="64" height="64" :alt="item.data.toString()"
|
||||||
:src="CDRAGON_BASE + mapPath(itemMap.get(item.data).iconPath)" />
|
:src="CDRAGON_BASE + mapPath(itemMap.get(item.data).iconPath)" />
|
||||||
@@ -91,25 +30,85 @@ for(let item of items.value) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="builds.bootsFirst > 0.5" class="item-box">
|
||||||
|
<div style="display:flex; flex-direction: column; justify-content: center; align-items: baseline;">
|
||||||
|
<h2 class="item-box-title">boots rush</h2>
|
||||||
|
<h5 style="margin: auto;">({{ (builds.bootsFirst * 100).toFixed(2) }}%)</h5>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; flex-direction: column; width: fit-content; height: fit-content; margin:auto;">
|
||||||
|
<div style="margin-left: 5px; margin-right: 5px;" v-for="item in builds.boots" >
|
||||||
|
<NuxtImg v-if="item.data != null && item.data != undefined"
|
||||||
|
class="item-img" width="64" height="64" :alt="item.data.toString()"
|
||||||
|
:src="CDRAGON_BASE + mapPath(itemMap.get(item.data).iconPath)" />
|
||||||
|
<h3 style="width: fit-content; margin:auto; margin-bottom: 10px;">{{ item.count }}</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item-box">
|
||||||
|
<h2 class="item-box-title">core</h2>
|
||||||
|
<TreeItem style="margin:auto; width: fit-content;" :tree="builds.tree" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="builds.bootsFirst <= 0.5" class="item-box">
|
||||||
|
<h2 class="item-box-title">boots</h2>
|
||||||
|
<div style="display: flex; flex-direction: column; width: fit-content; height: fit-content; margin:auto;">
|
||||||
|
<div style="margin-left: 5px; margin-right: 5px;" v-for="item in builds.boots.slice(0, 4)" >
|
||||||
|
<NuxtImg v-if="item.data != null && item.data != undefined"
|
||||||
|
class="item-img" width="64" height="64" :alt="item.data.toString()"
|
||||||
|
:src="CDRAGON_BASE + mapPath(itemMap.get(item.data).iconPath)" />
|
||||||
|
<h3 style="width: fit-content; margin:auto; margin-bottom: 10px;">{{ item.count }}</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item-box">
|
||||||
|
<h2 class="item-box-title">late game</h2>
|
||||||
|
|
||||||
|
<div style="display: flex;">
|
||||||
|
|
||||||
|
<div style="display: flex; flex-direction: column; width: fit-content; height: fit-content; margin:auto;">
|
||||||
|
<div style="margin-left: 5px; margin-right: 5px;" v-for="item in builds.lateGame.slice(0, 4)" >
|
||||||
|
<NuxtImg v-if="item.data != null && item.data != undefined"
|
||||||
|
class="item-img" width="64" height="64" :alt="item.data.toString()"
|
||||||
|
:src="CDRAGON_BASE + mapPath(itemMap.get(item.data).iconPath)" />
|
||||||
|
<h3 style="width: fit-content; margin:auto; margin-bottom: 10px;">{{ item.count }}</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display: flex; flex-direction: column; width: fit-content; height: fit-content; margin:auto;">
|
||||||
|
<div style="margin-left: 5px; margin-right: 5px;" v-for="item in builds.lateGame.slice(4, 8)" >
|
||||||
|
<NuxtImg v-if="item.data != null && item.data != undefined"
|
||||||
|
class="item-img" width="64" height="64" :alt="item.data.toString()"
|
||||||
|
:src="CDRAGON_BASE + mapPath(itemMap.get(item.data).iconPath)" />
|
||||||
|
<h3 style="width: fit-content; margin:auto; margin-bottom: 10px;">{{ item.count }}</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.item-img {
|
.item-img {
|
||||||
border: 1px solid var(--color-on-surface);
|
border: 1px solid var(--color-on-surface);
|
||||||
|
margin: 10px;
|
||||||
}
|
}
|
||||||
.item-box {
|
.item-box {
|
||||||
border: 1px solid var(--color-on-surface);
|
border: 1px solid var(--color-on-surface);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
||||||
width: 600px;
|
margin: 10px;
|
||||||
height: fit-content;
|
|
||||||
margin: auto;
|
width: fit-content;
|
||||||
|
height: 600px;
|
||||||
}
|
}
|
||||||
.item-box-title {
|
.item-box-title {
|
||||||
font-variant: small-caps;
|
font-variant: small-caps;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 10px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{
|
||||||
|
imgWidth?: String,
|
||||||
|
fontSize?: String
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div style="width: fit-content;">
|
<div style="width: fit-content;">
|
||||||
<NuxtLink style="display: flex; width: fit-content; text-decoration: none;" to="/">
|
<NuxtLink style="display: flex; width: fit-content; text-decoration: none;" to="/">
|
||||||
<img style="user-select: none;" width="140" src="~/assets/img/buildpath-high-resolution-logo-transparent.png" />
|
<img style="user-select: none;" :width="imgWidth == null ? '140' : Number(imgWidth)" src="~/assets/img/buildpath-high-resolution-logo-transparent.png" />
|
||||||
<h1 id="logo-text">BuildPath</h1>
|
<h1 :style="'font-size: ' + (fontSize == null ? '90px' : fontSize) + ';'" id="logo-text">BuildPath</h1>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -11,8 +18,11 @@
|
|||||||
#logo-text {
|
#logo-text {
|
||||||
font-weight: 200;
|
font-weight: 200;
|
||||||
align-content: center;
|
align-content: center;
|
||||||
|
|
||||||
font-size: 90px;
|
font-size: 90px;
|
||||||
|
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|||||||
38
frontend/components/SideBar.vue
Normal file
38
frontend/components/SideBar.vue
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{
|
||||||
|
championName: String
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="sidebar-container">
|
||||||
|
<Logo font-size="2.6rem" img-width="60" style="padding-left: 15px; padding-right: 15px; margin-top: 30px;"/>
|
||||||
|
<h1 class="sidebar-link" style="margin-top: 30px; font-size: 2.4rem; padding-left: 20px;">{{ championName }}</h1>
|
||||||
|
<h2 class="sidebar-link" @click="$emit('stateChange', 'runes')" style="margin-top: 10px; font-size: 1.9rem; padding-left: 35px;">Runes</h2>
|
||||||
|
<h2 class="sidebar-link" @click="$emit('stateChange', 'items')" style="margin-top: 10px; font-size: 1.9rem; padding-left: 35px;">Items</h2>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.sidebar-container {
|
||||||
|
background-color: #2B2826;
|
||||||
|
width: 300px;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
}
|
||||||
|
.sidebar-link {
|
||||||
|
user-select: none;
|
||||||
|
margin: 5px;
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
.sidebar-link:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
background-color: var(--color-surface-darker);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,74 +1,97 @@
|
|||||||
<script setup>
|
<script setup lang="ts">
|
||||||
const props = defineProps({
|
const props = defineProps<{
|
||||||
tree: {
|
tree: ItemTree
|
||||||
type: Object,
|
}>()
|
||||||
required: true
|
|
||||||
}
|
const emit = defineEmits<{
|
||||||
})
|
mount: [end: Element],
|
||||||
|
refresh: []
|
||||||
|
}>()
|
||||||
|
|
||||||
const item = props.tree
|
const item = props.tree
|
||||||
|
|
||||||
const {data : items} = await useFetch(CDRAGON_BASE + "plugins/rcp-be-lol-game-data/global/default/v1/items.json")
|
const {data : items} : ItemResponse = await useFetch(CDRAGON_BASE + "plugins/rcp-be-lol-game-data/global/default/v1/items.json")
|
||||||
const itemMap = reactive(new Map())
|
const itemMap = reactive(new Map())
|
||||||
for(let item of items.value) {
|
for(let item of items.value) {
|
||||||
itemMap.set(item.id, item)
|
itemMap.set(item.id, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
import pkg from 'svg-dom-arrows'; const { LinePath } = pkg;
|
import type { TreeItem } from '#build/components';
|
||||||
const treeItems = ref([])
|
import pkg from 'svg-dom-arrows';
|
||||||
const treeItem = ref(null)
|
const { LinePath } = pkg;
|
||||||
const start = ref(null)
|
|
||||||
|
|
||||||
const arrows = []
|
const start : Ref<Element | null> = useTemplateRef("start")
|
||||||
|
const arrows : Array<pkg.LinePath> = []
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
refreshArrows()
|
||||||
|
emit('mount', start.value!!)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
for(let arrow of arrows) {
|
||||||
|
arrow.release()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function drawArrow(start : Element, end : Element) {
|
||||||
|
// console.log("drawArrow(", start, ", ", end, ")")
|
||||||
|
if(start == null || end == null) return;
|
||||||
|
|
||||||
function drawTreeArrows() {
|
|
||||||
if(start.value == null) return;
|
|
||||||
for(let dest of treeItems.value) {
|
|
||||||
const arrow = new LinePath({
|
const arrow = new LinePath({
|
||||||
start: {
|
start: {
|
||||||
element: start.value,
|
element: start,
|
||||||
position: {
|
position: {
|
||||||
top: 0.5,
|
top: 0.5,
|
||||||
left: 1
|
left: 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
end: {
|
end: {
|
||||||
element: dest.childNodes[0].childNodes[0].childNodes[0],
|
element: end,
|
||||||
position: {
|
position: {
|
||||||
top: 0.5,
|
top: 0.5,
|
||||||
left: 0
|
left: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
style: 'stroke:var(--color-on-surface);stroke-width:3;fill:transparent;',
|
style: 'stroke:var(--color-on-surface);stroke-width:3;fill:transparent;',
|
||||||
appendTo: document.body
|
appendTo: document.body
|
||||||
})
|
})
|
||||||
arrows.push(arrow)
|
arrows.push(arrow)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
function refreshArrows() {
|
||||||
drawTreeArrows()
|
|
||||||
})
|
|
||||||
onUnmounted(() => {
|
|
||||||
for(let arrow of arrows) {
|
for(let arrow of arrows) {
|
||||||
document.body.removeChild(arrow.containerDiv)
|
arrow.redraw()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redraw arrows on window resize
|
||||||
|
addEventListener('resize', (_) => {
|
||||||
|
refreshArrows()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function handleSubtreeMount(end : Element) {
|
||||||
|
drawArrow(start.value!!, end)
|
||||||
|
refreshArrows()
|
||||||
|
emit('refresh')
|
||||||
|
}
|
||||||
|
function handleRefresh() {
|
||||||
|
refreshArrows()
|
||||||
|
emit('refresh')
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div ref="treeItem" style="display: flex; align-items: center;">
|
<div style="display: flex; align-items: center;">
|
||||||
|
|
||||||
<div v-if="item.data != undefined && item.data != null" style="width: fit-content; height: fit-content;">
|
<div v-if="item.data != undefined && item.data != null" style="width: fit-content; height: fit-content;">
|
||||||
<img ref="start" class="item-img" width="64" height="64" :alt="item.data" :src="CDRAGON_BASE + mapPath(itemMap.get(item.data).iconPath)" />
|
<img ref="start" class="item-img" width="64" height="64" :alt="item.data.toString()" :src="CDRAGON_BASE + mapPath(itemMap.get(item.data).iconPath)" />
|
||||||
<h3 style="width: fit-content; margin:auto; margin-bottom: 10px;">{{ item.count }}</h3>
|
<h3 style="width: fit-content; margin:auto; margin-bottom: 10px;">{{ item.count }}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="margin-left: 30px;">
|
<div style="margin-left: 30px;">
|
||||||
<div style="width: fit-content; height: fit-content;" :ref="(el) => { treeItems.push(el) }" v-for="child in item.children">
|
<div style="width: fit-content; height: fit-content;" v-for="child in item.children">
|
||||||
<TreeItem :tree="child" />
|
<TreeItem @refresh="handleRefresh" @mount="(end) => handleSubtreeMount(end)" :tree="child" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,8 +19,10 @@ export default defineNuxtConfig({
|
|||||||
{ rel: 'manifest', href: '/site.webmanifest' },
|
{ rel: 'manifest', href: '/site.webmanifest' },
|
||||||
],
|
],
|
||||||
meta: [
|
meta: [
|
||||||
{name: "apple-mobile-web-app-title", content:"BuildPath"}
|
{name: "apple-mobile-web-app-title", content:"BuildPath"},
|
||||||
]
|
],
|
||||||
|
charset: 'utf-8',
|
||||||
|
viewport: 'width=device-width, initial-scale=1'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
159
frontend/package-lock.json
generated
159
frontend/package-lock.json
generated
@@ -13,6 +13,11 @@
|
|||||||
"svg-dom-arrows": "^2.3.0",
|
"svg-dom-arrows": "^2.3.0",
|
||||||
"vue": "latest",
|
"vue": "latest",
|
||||||
"vue-router": "latest"
|
"vue-router": "latest"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vue/compiler-sfc": "^3.5.13",
|
||||||
|
"typescript": "^5.7.2",
|
||||||
|
"vue-tsc": "^2.1.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ampproject/remapping": {
|
"node_modules/@ampproject/remapping": {
|
||||||
@@ -2531,6 +2536,35 @@
|
|||||||
"vue": "^3.0.0"
|
"vue": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@volar/language-core": {
|
||||||
|
"version": "2.4.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.10.tgz",
|
||||||
|
"integrity": "sha512-hG3Z13+nJmGaT+fnQzAkS0hjJRa2FCeqZt6Bd+oGNhUkQ+mTFsDETg5rqUTxyzIh5pSOGY7FHCWUS8G82AzLCA==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@volar/source-map": "2.4.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@volar/source-map": {
|
||||||
|
"version": "2.4.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.10.tgz",
|
||||||
|
"integrity": "sha512-OCV+b5ihV0RF3A7vEvNyHPi4G4kFa6ukPmyVocmqm5QzOd8r5yAtiNvaPEjl8dNvgC/lj4JPryeeHLdXd62rWA==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@volar/typescript": {
|
||||||
|
"version": "2.4.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.10.tgz",
|
||||||
|
"integrity": "sha512-F8ZtBMhSXyYKuBfGpYwqA5rsONnOwAVvjyE7KPYJ7wgZqo2roASqNWUnianOomJX5u1cxeRooHV59N0PhvEOgw==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@volar/language-core": "2.4.10",
|
||||||
|
"path-browserify": "^1.0.1",
|
||||||
|
"vscode-uri": "^3.0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@vue-macros/common": {
|
"node_modules/@vue-macros/common": {
|
||||||
"version": "1.15.0",
|
"version": "1.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@vue-macros/common/-/common-1.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/@vue-macros/common/-/common-1.15.0.tgz",
|
||||||
@@ -2666,6 +2700,17 @@
|
|||||||
"@vue/shared": "3.5.13"
|
"@vue/shared": "3.5.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@vue/compiler-vue2": {
|
||||||
|
"version": "2.7.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz",
|
||||||
|
"integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"de-indent": "^1.0.2",
|
||||||
|
"he": "^1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@vue/devtools-api": {
|
"node_modules/@vue/devtools-api": {
|
||||||
"version": "6.6.4",
|
"version": "6.6.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
||||||
@@ -2731,6 +2776,57 @@
|
|||||||
"rfdc": "^1.4.1"
|
"rfdc": "^1.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@vue/language-core": {
|
||||||
|
"version": "2.1.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.10.tgz",
|
||||||
|
"integrity": "sha512-DAI289d0K3AB5TUG3xDp9OuQ71CnrujQwJrQnfuZDwo6eGNf0UoRlPuaVNO+Zrn65PC3j0oB2i7mNmVPggeGeQ==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@volar/language-core": "~2.4.8",
|
||||||
|
"@vue/compiler-dom": "^3.5.0",
|
||||||
|
"@vue/compiler-vue2": "^2.7.16",
|
||||||
|
"@vue/shared": "^3.5.0",
|
||||||
|
"alien-signals": "^0.2.0",
|
||||||
|
"minimatch": "^9.0.3",
|
||||||
|
"muggle-string": "^0.4.1",
|
||||||
|
"path-browserify": "^1.0.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/language-core/node_modules/brace-expansion": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/language-core/node_modules/minimatch": {
|
||||||
|
"version": "9.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||||
|
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16 || 14 >=14.17"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@vue/reactivity": {
|
"node_modules/@vue/reactivity": {
|
||||||
"version": "3.5.13",
|
"version": "3.5.13",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz",
|
||||||
@@ -2832,6 +2928,13 @@
|
|||||||
"node": ">= 6.0.0"
|
"node": ">= 6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/alien-signals": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-cZIRkbERILsBOXTQmMrxc9hgpxglstn69zm+F1ARf4aPAzdAFYd6sBq87ErO0Fj3DV94tglcyHG5kQz9nDC/8A==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/ansi-colors": {
|
"node_modules/ansi-colors": {
|
||||||
"version": "4.1.3",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
|
||||||
@@ -4148,6 +4251,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/de-indent": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.3.7",
|
"version": "4.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||||
@@ -5114,6 +5224,16 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/he": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"he": "bin/he"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/hookable": {
|
"node_modules/hookable": {
|
||||||
"version": "5.5.3",
|
"version": "5.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
|
||||||
@@ -6196,6 +6316,13 @@
|
|||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/muggle-string": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "5.0.8",
|
"version": "5.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.8.tgz",
|
||||||
@@ -6905,6 +7032,13 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/path-browserify": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/path-is-absolute": {
|
"node_modules/path-is-absolute": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
@@ -8765,11 +8899,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.6.3",
|
"version": "5.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
|
||||||
"integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
|
"integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -10044,6 +10177,24 @@
|
|||||||
"vue": "^3.2.0"
|
"vue": "^3.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-tsc": {
|
||||||
|
"version": "2.1.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.1.10.tgz",
|
||||||
|
"integrity": "sha512-RBNSfaaRHcN5uqVqJSZh++Gy/YUzryuv9u1aFWhsammDJXNtUiJMNoJ747lZcQ68wUQFx6E73y4FY3D8E7FGMA==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@volar/typescript": "~2.4.8",
|
||||||
|
"@vue/language-core": "2.1.10",
|
||||||
|
"semver": "^7.5.4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"vue-tsc": "bin/vue-tsc.js"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">=5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/webidl-conversions": {
|
"node_modules/webidl-conversions": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
|||||||
@@ -16,5 +16,10 @@
|
|||||||
"svg-dom-arrows": "^2.3.0",
|
"svg-dom-arrows": "^2.3.0",
|
||||||
"vue": "latest",
|
"vue": "latest",
|
||||||
"vue-router": "latest"
|
"vue-router": "latest"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vue/compiler-sfc": "^3.5.13",
|
||||||
|
"typescript": "^5.7.2",
|
||||||
|
"vue-tsc": "^2.1.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ const championAlias = route.params.alias
|
|||||||
|
|
||||||
const { data : championData } = await useFetch("/api/champion/" + championAlias.toLowerCase())
|
const { data : championData } = await useFetch("/api/champion/" + championAlias.toLowerCase())
|
||||||
const championId = championData.value.id
|
const championId = championData.value.id
|
||||||
|
|
||||||
|
const state = ref("runes")
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -11,14 +13,17 @@ const championId = championData.value.id
|
|||||||
<Title>{{ championData.name }} - BuildPath</Title>
|
<Title>{{ championData.name }} - BuildPath</Title>
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<div style="display: flex; width: fit-content; margin: auto;">
|
<SideBar :champion-name="championData.name" @state-change="(newState) => state = newState"/>
|
||||||
<div style="margin-top: 64px;">
|
|
||||||
|
<!-- <div style="display: flex; width: fit-content; margin: auto; margin-left: 330px;"> -->
|
||||||
|
<div style="margin-top: 64px; margin-left: 339px;">
|
||||||
<ChampionTitle :champion-id="championId" :winrate="championData.winrate" :pickrate="championData.pickrate" :game-count="championData.gameCount" />
|
<ChampionTitle :champion-id="championId" :winrate="championData.winrate" :pickrate="championData.pickrate" :game-count="championData.gameCount" />
|
||||||
<RuneSelector v-if="championData.gameCount > 0" style="margin-top: 20px;" :runes="championData.runes" />
|
<RuneSelector v-if="state == 'runes' && championData.gameCount > 0" style="margin: auto; margin-top: 40px;" :runes="championData.runes" />
|
||||||
<h2 style="margin: auto; margin-top: 20px; width: fit-content;" v-if="championData.gameCount == 0">Sorry, there is no data for this champion :(</h2>
|
<ItemViewer v-if="state == 'items' && championData.gameCount > 0" style="margin:auto; margin-top: 40px;" :builds="championData.builds" />
|
||||||
</div>
|
<h2 v-if="championData.gameCount == 0" style="margin: auto; margin-top: 20px; width: fit-content;">Sorry, there is no data for this champion :(</h2>
|
||||||
<ItemViewer v-if="championData.gameCount > 0" style="margin-top: 64px; margin-left: 64px;" :builds="championData.builds" />
|
|
||||||
</div>
|
</div>
|
||||||
|
<!-- <ItemViewer v-if="championData.gameCount > 0" style="margin-top: 64px; margin-left: 64px;" :builds="championData.builds" /> -->
|
||||||
|
<!-- </div> -->
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
22
frontend/types/items.ts
Normal file
22
frontend/types/items.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
declare global {
|
||||||
|
type ItemTree = {
|
||||||
|
count: number
|
||||||
|
data: number
|
||||||
|
children: Array<ItemTree>
|
||||||
|
}
|
||||||
|
type Builds = {
|
||||||
|
start: Array<{count: number, data: number}>
|
||||||
|
tree: ItemTree
|
||||||
|
bootsFirst: number
|
||||||
|
boots: Array<{count: number, data: number}>
|
||||||
|
lateGame: Array<{count: number, data: number}>
|
||||||
|
}
|
||||||
|
type Item = {
|
||||||
|
id: number
|
||||||
|
}
|
||||||
|
type ItemResponse = {
|
||||||
|
data: Ref<Array<Item>>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {};
|
||||||
Reference in New Issue
Block a user