Huge changes: Sidebar, new UI
This commit is contained in:
@@ -1,95 +1,92 @@
|
||||
<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<{
|
||||
builds: 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 itemMap = reactive(new Map())
|
||||
for(let item of items.value) {
|
||||
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>
|
||||
|
||||
<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">
|
||||
<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" >
|
||||
<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()"
|
||||
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 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 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; 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.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()"
|
||||
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" style="margin-top: 10px;">
|
||||
<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" style="margin-top: 10px;">
|
||||
<div v-if="builds.bootsFirst <= 0.5" class="item-box">
|
||||
<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" >
|
||||
<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"
|
||||
style="border: 1px solid var(--color-on-surface);"
|
||||
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)" />
|
||||
<h3 style="width: fit-content; margin:auto; margin-bottom: 10px;">{{ item.count }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item-box" style="margin-top: 10px;">
|
||||
<div class="item-box">
|
||||
<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)" >
|
||||
|
||||
<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>
|
||||
</template>
|
||||
@@ -97,19 +94,21 @@ for(let item of items.value) {
|
||||
<style>
|
||||
.item-img {
|
||||
border: 1px solid var(--color-on-surface);
|
||||
margin: 10px;
|
||||
}
|
||||
.item-box {
|
||||
border: 1px solid var(--color-on-surface);
|
||||
border-radius: 8px;
|
||||
|
||||
width: 600px;
|
||||
height: fit-content;
|
||||
margin: auto;
|
||||
margin: 10px;
|
||||
|
||||
width: fit-content;
|
||||
height: 600px;
|
||||
}
|
||||
.item-box-title {
|
||||
font-variant: small-caps;
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
imgWidth?: String,
|
||||
fontSize?: String
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="width: fit-content;">
|
||||
<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" />
|
||||
<h1 id="logo-text">BuildPath</h1>
|
||||
<img style="user-select: none;" :width="imgWidth == null ? '140' : Number(imgWidth)" src="~/assets/img/buildpath-high-resolution-logo-transparent.png" />
|
||||
<h1 :style="'font-size: ' + (fontSize == null ? '90px' : fontSize) + ';'" id="logo-text">BuildPath</h1>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
@@ -11,8 +18,11 @@
|
||||
#logo-text {
|
||||
font-weight: 200;
|
||||
align-content: center;
|
||||
font-size: 90px;
|
||||
|
||||
font-size: 90px;
|
||||
|
||||
margin-left: 20px;
|
||||
|
||||
user-select: 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>
|
||||
const props = defineProps({
|
||||
tree: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
tree: ItemTree
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
mount: [end: Element],
|
||||
refresh: []
|
||||
}>()
|
||||
|
||||
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())
|
||||
for(let item of items.value) {
|
||||
itemMap.set(item.id, item)
|
||||
}
|
||||
|
||||
import pkg from 'svg-dom-arrows'; const { LinePath } = pkg;
|
||||
const treeItems = ref([])
|
||||
const treeItem = ref(null)
|
||||
const start = ref(null)
|
||||
import type { TreeItem } from '#build/components';
|
||||
import pkg from 'svg-dom-arrows';
|
||||
const { LinePath } = pkg;
|
||||
|
||||
const arrows = []
|
||||
const start : Ref<Element | null> = useTemplateRef("start")
|
||||
const arrows : Array<pkg.LinePath> = []
|
||||
|
||||
function drawTreeArrows() {
|
||||
if(start.value == null) return;
|
||||
for(let dest of treeItems.value) {
|
||||
const arrow = new LinePath({
|
||||
start: {
|
||||
element: start.value,
|
||||
position: {
|
||||
top: 0.5,
|
||||
left: 1
|
||||
}
|
||||
},
|
||||
end: {
|
||||
element: dest.childNodes[0].childNodes[0].childNodes[0],
|
||||
position: {
|
||||
top: 0.5,
|
||||
left: 0
|
||||
}
|
||||
},
|
||||
|
||||
style: 'stroke:var(--color-on-surface);stroke-width:3;fill:transparent;',
|
||||
appendTo: document.body
|
||||
})
|
||||
arrows.push(arrow)
|
||||
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;
|
||||
|
||||
const arrow = new LinePath({
|
||||
start: {
|
||||
element: start,
|
||||
position: {
|
||||
top: 0.5,
|
||||
left: 1
|
||||
}
|
||||
},
|
||||
end: {
|
||||
element: end,
|
||||
position: {
|
||||
top: 0.5,
|
||||
left: 0
|
||||
}
|
||||
},
|
||||
style: 'stroke:var(--color-on-surface);stroke-width:3;fill:transparent;',
|
||||
appendTo: document.body
|
||||
})
|
||||
arrows.push(arrow)
|
||||
}
|
||||
|
||||
function refreshArrows() {
|
||||
for(let arrow of arrows) {
|
||||
arrow.redraw()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
drawTreeArrows()
|
||||
})
|
||||
onUnmounted(() => {
|
||||
for(let arrow of arrows) {
|
||||
document.body.removeChild(arrow.containerDiv)
|
||||
}
|
||||
// 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>
|
||||
|
||||
<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;">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div style="margin-left: 30px;">
|
||||
<div style="width: fit-content; height: fit-content;" :ref="(el) => { treeItems.push(el) }" v-for="child in item.children">
|
||||
<TreeItem :tree="child" />
|
||||
<div style="width: fit-content; height: fit-content;" v-for="child in item.children">
|
||||
<TreeItem @refresh="handleRefresh" @mount="(end) => handleSubtreeMount(end)" :tree="child" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user