mirror of
https://github.com/BlossomiShymae/clean-cuts.git
synced 2025-12-06 10:10:47 +01:00
Init commit
This commit is contained in:
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Nuxt dev/build outputs
|
||||
.output
|
||||
.data
|
||||
.nuxt
|
||||
.nitro
|
||||
.cache
|
||||
dist
|
||||
|
||||
# Node dependencies
|
||||
node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.fleet
|
||||
.idea
|
||||
|
||||
# Local env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "public/lib/MaterialDesign"]
|
||||
path = public/lib/MaterialDesign
|
||||
url = git@github.com:Templarian/MaterialDesign.git
|
||||
73
README.md
Normal file
73
README.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# Clean Cuts
|
||||
|
||||

|
||||
|
||||
## Setup
|
||||
|
||||
Make sure to install the dependencies:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm install
|
||||
|
||||
# pnpm
|
||||
pnpm install
|
||||
|
||||
# yarn
|
||||
yarn install
|
||||
|
||||
# bun
|
||||
bun install
|
||||
```
|
||||
|
||||
## Development Server
|
||||
|
||||
Start the development server on `http://localhost:3000`:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm run dev
|
||||
|
||||
# pnpm
|
||||
pnpm run dev
|
||||
|
||||
# yarn
|
||||
yarn dev
|
||||
|
||||
# bun
|
||||
bun run dev
|
||||
```
|
||||
|
||||
## Production
|
||||
|
||||
Build the application for production:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm run build
|
||||
|
||||
# pnpm
|
||||
pnpm run build
|
||||
|
||||
# yarn
|
||||
yarn build
|
||||
|
||||
# bun
|
||||
bun run build
|
||||
```
|
||||
|
||||
Locally preview production build:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm run preview
|
||||
|
||||
# pnpm
|
||||
pnpm run preview
|
||||
|
||||
# yarn
|
||||
yarn preview
|
||||
|
||||
# bun
|
||||
bun run preview
|
||||
```
|
||||
27
app.vue
Normal file
27
app.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<NuxtLayout>
|
||||
<NuxtPage keepalive />
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.page-enter-active,
|
||||
.page-leave-active {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.page-enter-from,
|
||||
.page-leave-to {
|
||||
opacity: 0;
|
||||
filter: blur(0.125rem);
|
||||
}
|
||||
|
||||
.layout-enter-active,
|
||||
.layout-leave-active {
|
||||
transform: none;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
.layout-enter-from,
|
||||
.layout-leave-to {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
</style>
|
||||
18
components/Badge.vue
Normal file
18
components/Badge.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<span class="badge text-bg-dark border text-dark-emphasis fw-normal d-flex align-items-center gap-2 bg-transparent bg-blur-4 border-light border-opacity-25">
|
||||
<MaterialIcon :size="24" :name="name" />
|
||||
<slot></slot>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import MaterialIcon from './MaterialIcon.vue';
|
||||
|
||||
defineProps<{
|
||||
name: string
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
19
components/MaterialIcon.vue
Normal file
19
components/MaterialIcon.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<svg :width="size" :height="size">
|
||||
<image :href="`/lib/MaterialDesign/svg/${name}.svg`"
|
||||
:width="size"
|
||||
:height="size"/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
name: string,
|
||||
size: number
|
||||
}>();
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
41
components/Pagination.vue
Normal file
41
components/Pagination.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div class="btn-group">
|
||||
<NuxtLink :class="`btn btn-outline-dark ${hasFurtherPreviousCss}`">
|
||||
<MaterialIcon name="chevron-double-left" :size="32" />
|
||||
</NuxtLink>
|
||||
<NuxtLink :class="`btn btn-outline-dark ${hasPreviousCss}`">
|
||||
<MaterialIcon name="chevron-left" :size="32" />
|
||||
</NuxtLink>
|
||||
<NuxtLink :class="`btn btn-outline-dark text-light`">
|
||||
{{ pageIndex / totalPages }}
|
||||
</NuxtLink>
|
||||
<NuxtLink :class="`btn btn-outline-dark ${hasNextCss}`">
|
||||
<MaterialIcon name="chevron-right" :size="32" />
|
||||
</NuxtLink>
|
||||
<NuxtLink :class="`btn btn-outline-dark ${hasFurtherNextCss}`">
|
||||
<MaterialIcon name="chevron-double-right" :size="32" />
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import MaterialIcon from './MaterialIcon.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
hasPrevious: boolean;
|
||||
hasFurtherPrevious: boolean;
|
||||
hasNext: boolean;
|
||||
hasFurtherNext: boolean;
|
||||
pageIndex: number;
|
||||
totalPages: number;
|
||||
}>();
|
||||
|
||||
const hasPreviousCss = !props.hasPrevious ? "disabled" : "";
|
||||
const hasFurtherPreviousCss = !props.hasFurtherPrevious ? "disabled" : "";
|
||||
const hasNextCss = !props.hasNext ? "disabled" : "";
|
||||
const hasFurtherNextCss = !props.hasFurtherNext ? "disabled" : "";
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
3
components/TheTitle.vue
Normal file
3
components/TheTitle.vue
Normal file
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<span>Clean Cuts</span>
|
||||
</template>
|
||||
9
composables/useClient.ts
Normal file
9
composables/useClient.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Client } from "~/core/client";
|
||||
|
||||
const client = new Client();
|
||||
|
||||
export default function useClient(): { client: Client } {
|
||||
return {
|
||||
client
|
||||
}
|
||||
};
|
||||
70
core/client.ts
Normal file
70
core/client.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { ChampionSummary, Item, LocaleVersionArgs, Perk, SummonerEmote, SummonerIcon, WardSkin } from "./models";
|
||||
import axios from "axios";
|
||||
|
||||
export abstract class ApiObject {
|
||||
static url = "https://raw.communitydragon.org";
|
||||
|
||||
getClientPath(args: LocaleVersionArgs): string {
|
||||
return `${ApiObject.url}/${args.version}/plugins/rcp-be-lol-game-data/global/${args.locale}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class Client {
|
||||
public items: ItemApi;
|
||||
public perks: PerkApi;
|
||||
public championSummaries: ChampionSummaryApi;
|
||||
public summonerEmotes: SummonerEmoteApi;
|
||||
public summonerIcons: SummonerIconApi;
|
||||
public wardSkins: WardSkinApi;
|
||||
|
||||
constructor() {
|
||||
this.items = new ItemApi();
|
||||
this.perks = new PerkApi();
|
||||
this.championSummaries = new ChampionSummaryApi();
|
||||
this.summonerEmotes = new SummonerEmoteApi();
|
||||
this.summonerIcons = new SummonerIconApi();
|
||||
this.wardSkins = new WardSkinApi();
|
||||
}
|
||||
}
|
||||
|
||||
export class ItemApi extends ApiObject {
|
||||
async listAsync(args: LocaleVersionArgs): Promise<Array<Item>> {
|
||||
let res = await axios.get(`${this.getClientPath(args)}/v1/items.json`);
|
||||
return res.data.map((x: any) => new Item(x));
|
||||
}
|
||||
}
|
||||
|
||||
export class PerkApi extends ApiObject {
|
||||
async listAsync(args: LocaleVersionArgs): Promise<Array<Perk>> {
|
||||
let res = await axios.get(`${this.getClientPath(args)}/v1/perks.json`);
|
||||
return res.data.map((x: any) => new Perk(x));
|
||||
}
|
||||
}
|
||||
|
||||
export class ChampionSummaryApi extends ApiObject {
|
||||
async listAsync(args: LocaleVersionArgs): Promise<Array<ChampionSummary>> {
|
||||
let res = await axios.get(`${this.getClientPath(args)}/v1/champion-summary.json`);
|
||||
return res.data.map((x: any) => new ChampionSummary(x));
|
||||
}
|
||||
}
|
||||
|
||||
export class SummonerEmoteApi extends ApiObject {
|
||||
async listAsync(args: LocaleVersionArgs): Promise<Array<SummonerEmote>> {
|
||||
let res = await axios.get(`${this.getClientPath(args)}/v1/summoner-emotes.json`);
|
||||
return res.data.map((x: any) => new SummonerEmote(x));
|
||||
}
|
||||
}
|
||||
|
||||
export class SummonerIconApi extends ApiObject {
|
||||
async listAsync(args: LocaleVersionArgs): Promise<Array<SummonerIcon>> {
|
||||
let res = await axios.get(`${this.getClientPath(args)}/v1/summoner-icons.json`);
|
||||
return res.data.map((x: any) => new SummonerEmote(x));
|
||||
}
|
||||
}
|
||||
|
||||
export class WardSkinApi extends ApiObject {
|
||||
async listAsync(args: LocaleVersionArgs): Promise<Array<WardSkin>> {
|
||||
let res = await axios.get(`${this.getClientPath(args)}/v1/ward-skins.json`);
|
||||
return res.data.map((x: any) => new WardSkin(x));
|
||||
}
|
||||
}
|
||||
282
core/models.ts
Normal file
282
core/models.ts
Normal file
@@ -0,0 +1,282 @@
|
||||
|
||||
export class LocaleVersionArgs {
|
||||
locale: string;
|
||||
version: string
|
||||
|
||||
constructor({locale, version}: {locale: string, version: string}) {
|
||||
this.locale = locale;
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class CommunityDragonObject {
|
||||
static url = "https://raw.communitydragon.org";
|
||||
|
||||
resolveClientPath({path, args}: {path: string, args: LocaleVersionArgs}): string {
|
||||
const uri = path.replace("/lol-game-data/assets", "").toLowerCase();
|
||||
return `${CommunityDragonObject.url}/${args.version}/plugins/rcp-be-lol-game-data/global/${args.locale}/${uri}`;
|
||||
}
|
||||
|
||||
resolveGamePath({path, version}: {path: string, version: string}): string {
|
||||
const uri = path.replace("/lol-game-data/assets/ASSETS/", "").replace(".jpg", ".png").toLowerCase();
|
||||
return `${CommunityDragonObject.url}/${version}/game/assets/${uri}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class Champion extends CommunityDragonObject {
|
||||
id: number;
|
||||
name: string;
|
||||
alias: string;
|
||||
title: string;
|
||||
shortBio: string;
|
||||
playstyleInfo: PlaystyleInfo;
|
||||
skins: Array<Skin>;
|
||||
passive: Passive;
|
||||
spells: Array<Spell>;
|
||||
|
||||
constructor(json: any) {
|
||||
super();
|
||||
|
||||
this.id = json.id;
|
||||
this.name = json.name;
|
||||
this.alias = json.alias;
|
||||
this.title = json.title;
|
||||
this.shortBio = json.shortBio;
|
||||
this.playstyleInfo = new PlaystyleInfo(json.playstyleInfo);
|
||||
this.skins = json.skins.map((x: any) => new Skin(x));
|
||||
this.passive = new Passive(json.passive);
|
||||
this.spells = json.spells.map((x: any) => new Spell(x));
|
||||
}
|
||||
}
|
||||
|
||||
export class PlaystyleInfo extends CommunityDragonObject {
|
||||
damage: number;
|
||||
durability: number;
|
||||
crowdControl: number;
|
||||
mobility: number;
|
||||
utility: number;
|
||||
|
||||
constructor(json: any) {
|
||||
super();
|
||||
|
||||
this.damage = json.damage;
|
||||
this.durability = json.durability;
|
||||
this.crowdControl = json.crowdControl;
|
||||
this.mobility = json.mobility;
|
||||
this.utility = json.utility;
|
||||
}
|
||||
}
|
||||
|
||||
export class Skin extends CommunityDragonObject {
|
||||
id: number;
|
||||
isBase: boolean;
|
||||
name: string;
|
||||
rarity: string;
|
||||
isLegacy: boolean;
|
||||
loadScreenPath: string;
|
||||
|
||||
constructor(json: any) {
|
||||
super();
|
||||
|
||||
this.id = json.id;
|
||||
this.isBase = json.isBase;
|
||||
this.name = json.name;
|
||||
this.rarity = json.rarity;
|
||||
this.isLegacy = json.isLegacy;
|
||||
this.loadScreenPath = json.loadScreenPath;
|
||||
}
|
||||
|
||||
getLoadScreen(version: string): string {
|
||||
return this.resolveGamePath({path: this.loadScreenPath, version: version});
|
||||
}
|
||||
}
|
||||
|
||||
export class Passive extends CommunityDragonObject {
|
||||
name: string;
|
||||
description: string;
|
||||
|
||||
constructor(json: any) {
|
||||
super();
|
||||
|
||||
this.name = json.name;
|
||||
this.description = json.description;
|
||||
}
|
||||
}
|
||||
|
||||
export class Spell extends CommunityDragonObject {
|
||||
spellKey: string;
|
||||
name: string;
|
||||
description: string;
|
||||
|
||||
constructor(json: any) {
|
||||
super();
|
||||
|
||||
this.spellKey = json.spellKey;
|
||||
this.name = json.name;
|
||||
this.description = json.description;
|
||||
}
|
||||
}
|
||||
|
||||
export class ChampionSummary extends CommunityDragonObject {
|
||||
id: number;
|
||||
name: string;
|
||||
alias: string;
|
||||
squarePortraitPath: string;
|
||||
|
||||
constructor(json: any) {
|
||||
super();
|
||||
|
||||
this.id = json.id;
|
||||
this.name = json.name;
|
||||
this.alias = json.alias;
|
||||
this.squarePortraitPath = json.squarePortraitPath;
|
||||
}
|
||||
|
||||
getIcon(args: LocaleVersionArgs): string {
|
||||
return this.resolveClientPath({path: this.squarePortraitPath, args: args});
|
||||
}
|
||||
}
|
||||
|
||||
export class Item extends CommunityDragonObject {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
active: boolean;
|
||||
inStore: boolean;
|
||||
from: Array<number>;
|
||||
to: Array<number>;
|
||||
categories: Array<number>;
|
||||
price: number;
|
||||
priceTotal: number;
|
||||
iconPath: string;
|
||||
|
||||
constructor(json: any) {
|
||||
super();
|
||||
|
||||
this.id = json.id;
|
||||
this.name = json.name;
|
||||
this.description = json.description;
|
||||
this.active = json.active;
|
||||
this.inStore = json.inStore;
|
||||
this.from = json.from;
|
||||
this.to = json.to;
|
||||
this.categories = json.categories;
|
||||
this.price = json.price;
|
||||
this.priceTotal = json.priceTotal;
|
||||
this.iconPath = json.iconPath;
|
||||
}
|
||||
|
||||
getIcon(version: string): string {
|
||||
return this.resolveGamePath({path: this.iconPath, version: version});
|
||||
}
|
||||
}
|
||||
|
||||
export class Perk extends CommunityDragonObject {
|
||||
id: number;
|
||||
name: string;
|
||||
shortDesc: string;
|
||||
longDesc: string;
|
||||
iconPath: string;
|
||||
|
||||
constructor(json: any) {
|
||||
super();
|
||||
|
||||
this.id = json.id;
|
||||
this.name = json.name;
|
||||
this.shortDesc = json.shortDesc;
|
||||
this.longDesc = json.longDesc;
|
||||
this.iconPath = json.iconPath;
|
||||
}
|
||||
|
||||
getIcon(version: string): string {
|
||||
return this.resolveClientPath({path: this.iconPath, args: {version: version, locale: "default"}});
|
||||
}
|
||||
}
|
||||
|
||||
export class SummonerEmote extends CommunityDragonObject {
|
||||
id: number;
|
||||
name: string;
|
||||
inventoryIcon: string;
|
||||
|
||||
constructor(json: any) {
|
||||
super();
|
||||
|
||||
this.id = json.id;
|
||||
this.name = json.name;
|
||||
this.inventoryIcon = json.inventoryIcon;
|
||||
}
|
||||
|
||||
getInventoryIcon(version: string): string {
|
||||
return this.resolveGamePath({path: this.inventoryIcon, version: version}).replace("inventory", "vfx");
|
||||
}
|
||||
}
|
||||
|
||||
export class SummonerIcon extends CommunityDragonObject {
|
||||
id: number;
|
||||
title: string;
|
||||
yearReleased: number;
|
||||
isLegacy: boolean;
|
||||
imagePath?: string;
|
||||
descriptions: Array<Description>;
|
||||
rarities: Array<Rarity>;
|
||||
|
||||
constructor(json: any) {
|
||||
super();
|
||||
|
||||
this.id = json.id;
|
||||
this.title = json.title;
|
||||
this.yearReleased = json.yearReleased;
|
||||
this.isLegacy = json.isLegacy;
|
||||
this.imagePath = json.imagePath;
|
||||
this.descriptions = json.descriptions.map((x: any) => new Description(x));
|
||||
this.rarities = json.rarities.map((x: any) => new Rarity(x));
|
||||
}
|
||||
}
|
||||
|
||||
export class WardSkin extends CommunityDragonObject {
|
||||
id: number;
|
||||
name: string;
|
||||
description : string;
|
||||
wardImagePath: string;
|
||||
wardShadowImagePath: string;
|
||||
isLegacy: boolean;
|
||||
regionDescriptions: Array<Description>;
|
||||
rarities: Array<Rarity>;
|
||||
|
||||
constructor(json: any) {
|
||||
super();
|
||||
|
||||
this.id = json.id;
|
||||
this.name = json.name;
|
||||
this.description = json.description;
|
||||
this.wardImagePath = json.wardImagePath;
|
||||
this.wardShadowImagePath = json.wardShadowImagePath;
|
||||
this.isLegacy = json.isLegacy;
|
||||
this.regionDescriptions = json.regionalDescriptions.map((x: any) => new Description(x));
|
||||
this.rarities = json.rarities.map((x: any) => new Rarity(x));
|
||||
}
|
||||
}
|
||||
|
||||
export class Description extends CommunityDragonObject {
|
||||
region: string;
|
||||
description: string;
|
||||
|
||||
constructor(json: any) {
|
||||
super();
|
||||
|
||||
this.region = json.region;
|
||||
this.description = json.description;
|
||||
}
|
||||
}
|
||||
|
||||
export class Rarity extends CommunityDragonObject {
|
||||
region: string;
|
||||
rarity: string;
|
||||
|
||||
constructor(json: any) {
|
||||
super();
|
||||
|
||||
this.region = json.region;
|
||||
this.rarity = json.rarity;
|
||||
}
|
||||
}
|
||||
128
layouts/default.vue
Normal file
128
layouts/default.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
|
||||
<template>
|
||||
<div class="h-100">
|
||||
<header>
|
||||
<div class="background background-transparent background-blur-2"></div>
|
||||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light border-bottom border-light border-opacity-25 border-2 box-shadow">
|
||||
<div class="container">
|
||||
<NuxtLink class="navbar-brand fw-light" to="/" ><Title /></NuxtLink>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
|
||||
aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
|
||||
<ul class="navbar-nav flex-grow-1">
|
||||
<li class="nav-item">
|
||||
<NuxtLink class="nav-link" to="/">
|
||||
<MaterialIcon name="home" :size="24" /> Home
|
||||
</NuxtLink>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<NuxtLink class="nav-link" to="/">
|
||||
<MaterialIcon name="account-group" :size="24" /> Champions
|
||||
</NuxtLink>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<NuxtLink class="nav-link" to="/items">
|
||||
<MaterialIcon name="magic-staff" :size="24" /> Items
|
||||
</NuxtLink>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<NuxtLink class="nav-link" to="/">
|
||||
<MaterialIcon name="shield" :size="24" /> Runes
|
||||
</NuxtLink>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<NuxtLink class="nav-link" to="/">
|
||||
<MaterialIcon name="image" :size="24" /> Summoner Icons
|
||||
</NuxtLink>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<NuxtLink class="nav-link" to="/">
|
||||
<MaterialIcon name="floor-lamp" :size="24" /> Ward Skins
|
||||
</NuxtLink>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<NuxtLink class="nav-link" to="/">
|
||||
<MaterialIcon name="face-woman-shimmer" :size="24" /> Emotes
|
||||
</NuxtLink>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="container">
|
||||
<main role="main" class="pt-3 pb-3">
|
||||
<slot></slot>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<footer class="container border border-light border-opacity-25 rounded p-4 pb-2 mt-2 mb-4 bg-blur-4">
|
||||
<div class="d-flex justify-content-around align-items-center gap-2 mb-3 flex-wrap">
|
||||
<NuxtLink class="text-decoration-none text-light" to="about">About</NuxtLink>
|
||||
<a class="text-decoration-none text-light" href="https://challenges.darkintaqt.com" referrerpolicy="no-referrer">Challenge Tracker</a>
|
||||
<a class="text-decoration-none text-light" href="https://blossomishymae.github.io/" referrerpolicy="no-referrer">blossomishymae.github.io</a>
|
||||
<a class="text-decoration-none text-light" href="https://communitydragon.org" referrerpolicy="no-referrer">CommunityDragon</a>
|
||||
<!-- <a class="text-decoration-none text-light" href="https://discord.com/invite/riotgamesdevrel" referrerpolicy="no-referrer">DevRel Discord</a> -->
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-center align-items-center pt-2 border-top border-light border-opacity-25">
|
||||
<p class="text-muted fw-light" style="font-size: 0.8rem;">Peaches was created under Riot Games' "Legal Jibber Jabber" policy using assets owned by Riot Games. Riot Games does not endorse or sponsor this project. </p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MaterialIcon from '~/components/MaterialIcon.vue';
|
||||
import Title from '~/components/Title.vue';
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
a.navbar-brand {
|
||||
white-space: normal;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0077cc;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background-color: #1b6ec2;
|
||||
border-color: #1861ac;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
|
||||
color: #fff;
|
||||
background-color: #1b6ec2;
|
||||
border-color: #1861ac;
|
||||
}
|
||||
|
||||
.border-top {
|
||||
border-top: 1px solid #e5e5e5;
|
||||
}
|
||||
.border-bottom {
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.box-shadow {
|
||||
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
|
||||
}
|
||||
|
||||
button.accept-policy {
|
||||
font-size: 1rem;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
line-height: 60px;
|
||||
}
|
||||
</style>
|
||||
62
nuxt.config.ts
Normal file
62
nuxt.config.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import path from "path";
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
|
||||
export default defineNuxtConfig({
|
||||
devtools: { enabled: true },
|
||||
nitro: {
|
||||
output: {
|
||||
publicDir: path.join(__dirname, "docs"),
|
||||
},
|
||||
},
|
||||
app: {
|
||||
pageTransition: {
|
||||
name: "page",
|
||||
mode: "out-in",
|
||||
},
|
||||
layoutTransition: {
|
||||
name: "layout",
|
||||
mode: "out-in",
|
||||
},
|
||||
head: {
|
||||
htmlAttrs: {
|
||||
"data-bs-theme": "dark",
|
||||
"style": "background-image: url('https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/champion-splashes/498/498004.jpg');"
|
||||
},
|
||||
bodyAttrs: {
|
||||
class: "h-100"
|
||||
},
|
||||
meta: [
|
||||
{ name: "viewport", content: "width=device-width, initial-scale=1" },
|
||||
],
|
||||
link: [
|
||||
{
|
||||
rel: "stylesheet",
|
||||
href: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css",
|
||||
integrity:
|
||||
"sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM",
|
||||
crossorigin: "anonymous",
|
||||
},
|
||||
{
|
||||
rel: "stylesheet",
|
||||
href: "/css/app.css",
|
||||
},
|
||||
{
|
||||
rel: "icon",
|
||||
type: "image/png",
|
||||
href: "/favicon.png",
|
||||
},
|
||||
],
|
||||
script: [
|
||||
{
|
||||
src: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js",
|
||||
integrity:
|
||||
"sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz",
|
||||
crossorigin: "anonymous",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
experimental: {
|
||||
payloadExtraction: false,
|
||||
},
|
||||
})
|
||||
10647
package-lock.json
generated
Normal file
10647
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
package.json
Normal file
21
package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "nuxt-app",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.6.8",
|
||||
"nuxt": "^3.11.2",
|
||||
"vue": "^3.4.21",
|
||||
"vue-router": "^4.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"sass": "^1.76.0"
|
||||
}
|
||||
}
|
||||
53
pages/about.vue
Normal file
53
pages/about.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="row mb-4">
|
||||
<div class="d-flex align-items-center flex-column">
|
||||
<div class="col-md-6">
|
||||
<h1 class="display-4">About <TheTitle /> </h1>
|
||||
<p class="lead">The <TheTitle /> project was started after experiencing stumbling blocks
|
||||
with a shelved project.
|
||||
</p>
|
||||
<p>Our purpose is to provide League of Legends game data that is easy
|
||||
to view for the common person. This game data is provided by CommunityDragon, another community resource.
|
||||
</p>
|
||||
<p>Therefore, no computer programming or scripting is needed.
|
||||
Not everyone can browse a JSON or navigate game data folders. :3</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 d-flex justify-content-center">
|
||||
<div style="width: 250px !important;" class="d-flex flex-column justify-content-center align-items-center gap-1 border border-light border-opacity-25 bg-blur-3 rounded">
|
||||
<img class="img-fluid rounded mb-2" src="/img/avatar.png"/>
|
||||
<div class="px-3">
|
||||
<div class="d-flex gap-2">
|
||||
<NuxtLink class="text-decoration-none d-inline-block text-light m-0" to="https://blossomishymae.github.io"><h5 class="text-light mb-0">Blossomi Shymae</h5></NuxtLink>
|
||||
<NuxtLink class="text-decoration-none d-inline-block text-light m-0" to="https://github.com/BlossomiShymae"><span><MaterialIcon name="github" :size="20" /> </span></NuxtLink>
|
||||
</div>
|
||||
<h5 class="fw-bold m-0 mb-2">Lonely elf girl</h5>
|
||||
<p>She likes to chat with her friends and do random programming projects, such as this one.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 d-flex flex-column justify-content-around">
|
||||
<div>
|
||||
<h3 class="fw-light">Technology stack</h3>
|
||||
<p>This website uses Nuxt.js, the meta-framework of universal application.</p>
|
||||
<p >Other libraries used include Bootstrap, the CSS framework, and Material Design for icons.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="fw-light">Contact</h3>
|
||||
<p>Blossomi Shymae can be reached via her Discord server.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MaterialIcon from '~/components/MaterialIcon.vue';
|
||||
import TheTitle from '~/components/TheTitle.vue';
|
||||
</script>
|
||||
57
pages/index.vue
Normal file
57
pages/index.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="d-flex flex-column justify-content-center align-items-center gap-2" style="margin-top: 25%;">
|
||||
<div class="d-flex flex-column justify-content-center align-items-center">
|
||||
<h1 class="display-4"><TheTitle /></h1>
|
||||
<p>Your local League of Legends companion index.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<h2>The <TheTitle /> Project</h2>
|
||||
<p>Our primary purpose is to make game data viewable in a human-friendly
|
||||
format without needing computer programming or scripting knowledge.
|
||||
</p>
|
||||
<p>CommunityDragon, the unofficial League of Legends data resource, is used for our project.</p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<h2>Resources</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<div class="d-flex flex-column justify-content-center align-items-stretch gap-2">
|
||||
<a class="btn btn-dark bg-transparent bg-blur-3 border-light border-opacity-25" asp-area="" asp-page="/Champion/Index">
|
||||
<MaterialIcon name="account-group" :size="24"/> Champions
|
||||
</a>
|
||||
<a class="btn btn-dark bg-transparent bg-blur-3 border-light border-opacity-25">
|
||||
<MaterialIcon name="magic-staff" :size="24"/> Items
|
||||
</a>
|
||||
<a class="btn btn-dark bg-transparent bg-blur-3 border-light border-opacity-25">
|
||||
<MaterialIcon name="shield" :size="24"/> Runes
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<div class="d-flex flex-column justify-content-center align-items-stretch gap-2">
|
||||
<a class="btn btn-dark bg-transparent bg-blur-3 border-light border-opacity-25">
|
||||
<MaterialIcon name="image" :size="24"/> Summoner Icons
|
||||
</a>
|
||||
<a class="btn btn-dark bg-transparent bg-blur-3 border-light border-opacity-25">
|
||||
<MaterialIcon name="floor-lamp" :size="24"/> Ward Skins
|
||||
</a>
|
||||
<a class="btn btn-dark bg-transparent bg-blur-3 border-light border-opacity-25">
|
||||
<MaterialIcon name="face-woman-shimmer" :size="24"/> Emotes
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import MaterialIcon from '~/components/MaterialIcon.vue';
|
||||
import TheTitle from '~/components/TheTitle.vue';
|
||||
</script>
|
||||
49
pages/items/index.vue
Normal file
49
pages/items/index.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="d-flex flex-column gap-2">
|
||||
<h1>Items</h1>
|
||||
|
||||
<div class="overflow-hidden rounded border border-light border-opacity-25 p-4">
|
||||
<table class="sortable table table-borderless">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Id</th>
|
||||
<th scope="col">Icon</th>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Price</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in items" :key="item.id">
|
||||
<th scope="row">
|
||||
<NuxtLink class="text-decoration-none text-light" :to="`/items/overview/${item.id}`">
|
||||
{{ item.id }}
|
||||
</NuxtLink>
|
||||
</th>
|
||||
<td>
|
||||
<NuxtLink class="text-decoration-none text-light" :to="`/items/overview/${item.id}`">
|
||||
<img class="rounded" :src="item.getIcon('latest')" width="32" height="32" loading="lazy" onerror="this.onerror = null; this.src = '/img/error.png'"/>
|
||||
</NuxtLink>
|
||||
</td>
|
||||
<td>
|
||||
<NuxtLink class="text-decoration-none text-light" :to="`/items/overview/${item.id}`">
|
||||
<span v-html="item.name"></span>
|
||||
</NuxtLink>
|
||||
</td>
|
||||
<td>
|
||||
<NuxtLink class="text-decoration-none text-light" :to="`/items/overview/${item.id}`">
|
||||
{{ item.priceTotal }}
|
||||
</NuxtLink>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import useClient from '../../composables/useClient';
|
||||
|
||||
const { client } = useClient();
|
||||
const items = await client.items.listAsync({ locale: "default", version: "latest"});
|
||||
</script>
|
||||
68
pages/items/overview/[id].vue
Normal file
68
pages/items/overview/[id].vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<h1 class="display-4 mb-0"> {{ item.name }}</h1>
|
||||
<p class="text-muted" v-html="item.description"></p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-sm-12 d-flex justify-content-center align-items-center">
|
||||
<img class="border rounded border-dark" :src="item.getIcon('latest')"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="d-flex flex-wrap gap-3">
|
||||
<Badge name="identifier">{{ item.id }}</Badge>
|
||||
<Badge name="hand-coin">{{ item.priceTotal }}</Badge>
|
||||
<Badge name="hand-coin-outline">{{ item.price }}</Badge>
|
||||
<Badge name="keyboard-variant" v-if="item.active">Active</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-6 col-sm-12 border-start border-light border-opacity-25 border-4"
|
||||
v-if="components.length > 0">
|
||||
<h4 class="fw-light">Component</h4>
|
||||
<div class="d-flex justify-content-around align-items-center gap-2 flex-wrap">
|
||||
<NuxtLink v-for="component in components" :to="`/items/overview/${component.id}`" :key="component.id">
|
||||
<img class="border rounded border-dark" :src="component.getIcon('latest')"/>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-sm-12 border-start border-light border-opacity-25 border-4"
|
||||
v-if="composites.length > 0">
|
||||
<h4 class="fw-light">Composite</h4>
|
||||
<div class="d-flex justify-content-around align-items-center gap-2 flex-wrap">
|
||||
<NuxtLink v-for="composite in composites" :to="`/items/overview/${composite.id}`" :key="composite.id">
|
||||
<img class="border rounded border-dark" :src="composite.getIcon('latest')"/>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from 'vue-router';
|
||||
import useClient from '../../../composables/useClient';
|
||||
import { Item } from '~/core/models';
|
||||
import Badge from '~/components/Badge.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const id = route.params.id as unknown;
|
||||
|
||||
const { client } = useClient();
|
||||
const items = await client.items.listAsync({locale: "default", version: "latest"});
|
||||
const _default = new Item({});
|
||||
|
||||
const item = items.find((x) => x.id == id) || _default;
|
||||
const components = item.from.map((id) => items.find((x) => x.id == id) || _default);
|
||||
const composites = item.to.map((id) => items.find((x) => x.id == id) || _default);
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
BIN
preview.png
Normal file
BIN
preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 744 KiB |
0
public/.nojekyll
Normal file
0
public/.nojekyll
Normal file
133
public/css/app.css
Normal file
133
public/css/app.css
Normal file
@@ -0,0 +1,133 @@
|
||||
* {
|
||||
color: white;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 14px;
|
||||
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-attachment: fixed;
|
||||
background-position: center;
|
||||
min-height: 100%;
|
||||
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
html::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #0008;
|
||||
z-index: 1;
|
||||
|
||||
backdrop-filter: blur(0.2rem);
|
||||
}
|
||||
|
||||
body {
|
||||
position: relative;
|
||||
background-color: transparent;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn:focus,
|
||||
.btn:active:focus,
|
||||
.btn-link.nav-link:focus,
|
||||
.form-control:focus,
|
||||
.form-check-input:focus,
|
||||
a:focus-visible {
|
||||
box-shadow: 0.05rem 0.05rem 0.375rem 0.1rem rgba(255, 255, 255, 0.744) !important;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
header {
|
||||
top: 0;
|
||||
position: sticky;
|
||||
z-index: 1000;
|
||||
background-color: #0004;
|
||||
|
||||
}
|
||||
|
||||
svg {
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
||||
table {
|
||||
background-color: #0004 !important;
|
||||
}
|
||||
|
||||
thead, tbody, th, td {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
table > tbody > tr:nth-of-type(2n+1) > * {
|
||||
background-color: #0004 !important;
|
||||
}
|
||||
|
||||
table > tbody > tr:hover > * {
|
||||
background-color: #0008 !important;
|
||||
}
|
||||
|
||||
.background {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.background-screen {
|
||||
background-color: #0008;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.bg-screen {
|
||||
background-color: #0004 !important;
|
||||
}
|
||||
|
||||
.background-transparent {
|
||||
background-color: transparent;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.background-blur {
|
||||
backdrop-filter: blur(0.2rem);
|
||||
}
|
||||
|
||||
.bg-blur {
|
||||
backdrop-filter: blur(0.2rem);
|
||||
}
|
||||
|
||||
.background-blur-2 {
|
||||
backdrop-filter: blur(0.4rem);
|
||||
}
|
||||
|
||||
.bg-blur-2 {
|
||||
backdrop-filter: blur(0.4rem);
|
||||
}
|
||||
|
||||
.bg-blur-3 {
|
||||
backdrop-filter: blur(0.6rem);
|
||||
}
|
||||
|
||||
.bg-blur-4 {
|
||||
backdrop-filter: blur(0.8rem);
|
||||
}
|
||||
|
||||
.z-index--10 {
|
||||
z-index: -10;
|
||||
}
|
||||
|
||||
.z-index-10 {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
BIN
public/img/avatar.png
Normal file
BIN
public/img/avatar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 344 KiB |
BIN
public/img/error.png
Normal file
BIN
public/img/error.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
1
public/lib/MaterialDesign
Submodule
1
public/lib/MaterialDesign
Submodule
Submodule public/lib/MaterialDesign added at 49bf943a7a
3
server/tsconfig.json
Normal file
3
server/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../.nuxt/tsconfig.server.json"
|
||||
}
|
||||
4
tsconfig.json
Normal file
4
tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
// https://nuxt.com/docs/guide/concepts/typescript
|
||||
"extends": "./.nuxt/tsconfig.json"
|
||||
}
|
||||
50
utils/paginated-array.ts
Normal file
50
utils/paginated-array.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
export class PaginatedArray<T> extends Array<T> {
|
||||
pageSize: number;
|
||||
items: Array<T>;
|
||||
pageIndex: number;
|
||||
totalPages: number;
|
||||
|
||||
constructor(items: Array<T>, pageIndex: number, pageSize: number) {
|
||||
super();
|
||||
|
||||
this.pageSize = pageSize;
|
||||
this.items = items;
|
||||
|
||||
this.pageIndex = pageIndex;
|
||||
this.totalPages = Math.ceil(items.length / pageSize);
|
||||
|
||||
this.push(...items.slice((pageIndex - 1) * pageSize).slice(0, pageSize));
|
||||
}
|
||||
|
||||
public get hasFurtherPreviousPage() {
|
||||
return this.pageIndex > 2;
|
||||
}
|
||||
|
||||
public get hasPreviousPage() {
|
||||
return this.pageIndex > 1;
|
||||
}
|
||||
|
||||
public get hasNextPage() {
|
||||
return this.pageIndex < this.totalPages;
|
||||
}
|
||||
|
||||
public get hasFurtherNextPage() {
|
||||
return this.pageIndex < this.totalPages - 1;
|
||||
}
|
||||
|
||||
public get furtherPreviousPage() {
|
||||
return this.pageIndex - 2;
|
||||
}
|
||||
|
||||
public get previousPage() {
|
||||
return this.pageIndex - 1;
|
||||
}
|
||||
|
||||
public get nextPage() {
|
||||
return this.pageIndex + 1;
|
||||
}
|
||||
|
||||
public get furtherNextPage() {
|
||||
return this.pageIndex + 2;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user