Skip to content

Commit bf626d1

Browse files
committed
Add characters page
1 parent bd41cd4 commit bf626d1

File tree

7 files changed

+167
-22
lines changed

7 files changed

+167
-22
lines changed

components/NavBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { useState } from "react"
22
import FormattedLink from "./FormattedLink"
33

4-
const pages = ["Guides", /* "Characters",*/ "Reminders"]
4+
const pages = ["Guides", "Characters", "Reminders"]
55

66
export default function NavBar({ location }: {location: string}) {
77
const [menuOpen, setMenuOpen] = useState(false)

pages/characters/index.tsx

Lines changed: 166 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,56 @@
11
import { GetStaticPropsContext, GetStaticPropsResult } from "next"
22
import Head from "next/head"
33
import Image from "next/image"
4+
import { Dispatch, SetStateAction, useState } from "react"
45
import FormattedLink from "../../components/FormattedLink"
56
import Main from "../../components/Main"
6-
import { getCharacters, urlify } from "../../utils/data-cache"
7-
import { Character, CharacterFull } from "../../utils/types"
8-
9-
import Cryo from "../../public/img/element/Cryo.png"
107
import Anemo from "../../public/img/element/Anemo.png"
8+
import Cryo from "../../public/img/element/Cryo.png"
119
import Dendro from "../../public/img/element/Dendro.png"
12-
import Geo from "../../public/img/element/Geo.png"
1310
import Electro from "../../public/img/element/Electro.png"
11+
import Geo from "../../public/img/element/Geo.png"
1412
import Hydro from "../../public/img/element/Hydro.png"
1513
import Pyro from "../../public/img/element/Pyro.png"
14+
import Bow from "../../public/img/weapon_types/Bow.png"
15+
import Catalyst from "../../public/img/weapon_types/Catalyst.png"
16+
import Claymore from "../../public/img/weapon_types/Claymore.png"
17+
import Polearm from "../../public/img/weapon_types/Polearm.png"
18+
import Sword from "../../public/img/weapon_types/Sword.png"
19+
import { getCharacters, urlify } from "../../utils/data-cache"
20+
import { Character, CharacterFull, WeaponType } from "../../utils/types"
21+
1622

1723
const elements = {
18-
Anemo, Cryo, Dendro, Geo, Electro, Hydro, Pyro
24+
Pyro, Electro, Cryo, Hydro, Anemo, Geo, Dendro
25+
}
26+
const weapons: Record<WeaponType, StaticImageData> = {
27+
Polearm, Sword, Claymore, Bow, Catalyst
1928
}
2029

21-
type Element = "Anemo"
30+
type ElementType = (keyof (typeof elements))
2231

2332
interface SmallChar {
2433
name: string
2534
stars?: number
26-
element?: (keyof (typeof elements))[]
27-
weapon?: string
35+
element?: ElementType[]
36+
weapon?: WeaponType
2837
icon?: string
2938
}
3039

3140
interface Props {
3241
characters: SmallChar[]
3342
}
3443

44+
const defaultElements: ElementType[] = Object.keys(elements) as ElementType[]
45+
const defaultWeapons: WeaponType[] = Object.keys(weapons) as WeaponType[]
46+
3547
export default function Characters(props: Props & { location: string }) {
48+
const [filter, setFilter] = useState(false)
49+
50+
const [starFilter, setStarFilter] = useState(0)
51+
const [elementFilter, setElementFilter] = useState(defaultElements)
52+
const [weaponFilter, setWeaponFilter] = useState(defaultWeapons)
53+
3654
return (
3755
<Main>
3856
<Head>
@@ -46,23 +64,150 @@ export default function Characters(props: Props & { location: string }) {
4664
Characters
4765
</h1>
4866

49-
<div className="flex flex-wrap justify-around text-center">
50-
{props.characters.map(char => (
51-
<FormattedLink key={char.name} font="semibold" size="xl" location={props.location} href={`/characters/${urlify(char.name, false)}`} >
52-
<div className="bg-slate-600 w-24 h-24 m-2">
53-
<div className="absolute w-6 m-1">
54-
{char.element && char.element.map(e => <Image src={elements[e]} key={e} alt={`${e} Element`}/>)}
55-
<Image alt={char.name} src={char.icon ?? "/img/unknown.png"} className="w-24" width={256} height={256} onError={(e) => (e.target as HTMLImageElement).src = "/img/unknown.png"}/>
67+
{filter ? <div className="bg-slate-100 dark:bg-slate-600 flex flex-col p-2 rounded-2xl font-semibold gap-2">
68+
<div className="pb-2">
69+
<div className="flex flex-row font-semibold float-right">
70+
<ExclusiveButton type={filter} value={false} setter={setFilter}>
71+
Hide filters
72+
</ExclusiveButton>
73+
</div>
74+
<div>
75+
Rarity filter
76+
</div>
77+
<div className="flex flex-row gap-2 pt-2">
78+
<ExclusiveButton type={starFilter} value={0} setter={setStarFilter}>
79+
All
80+
</ExclusiveButton>
81+
<ExclusiveButton type={starFilter} value={4} setter={setStarFilter}>
82+
4★ Only
83+
</ExclusiveButton>
84+
<ExclusiveButton type={starFilter} value={5} setter={setStarFilter}>
85+
5★ Only
86+
</ExclusiveButton>
87+
</div>
88+
</div>
89+
90+
<div className="py-1">
91+
<div className="flex flex-row gap-4">
92+
Element filter
93+
<ToggleAllButton type={elementFilter} value={defaultElements} setter={setElementFilter}>
94+
All
95+
</ToggleAllButton>
96+
</div>
97+
<div className="flex flex-row gap-2 pt-2">
98+
{defaultElements.map(e => (
99+
<ToggleButton key={e} type={elementFilter} value={e} setter={setElementFilter}>
100+
{e}
101+
</ToggleButton>
102+
))}
103+
</div>
104+
</div>
105+
106+
<div className="py-1">
107+
<div className="flex flex-row gap-4 pt-2">
108+
Weapon filter
109+
<ToggleAllButton type={weaponFilter} value={defaultWeapons} setter={setWeaponFilter}>
110+
All
111+
</ToggleAllButton>
112+
</div>
113+
<div className="flex flex-row gap-2 pt-2">
114+
{defaultWeapons.map(e => (
115+
<ToggleButton key={e} type={weaponFilter} value={e} setter={setWeaponFilter}>
116+
{e}
117+
</ToggleButton>
118+
))}
119+
</div>
120+
</div>
121+
</div> : <div className="flex flex-row font-semibold justify-end">
122+
<ExclusiveButton type={filter} value={true} setter={setFilter}>
123+
Show filters
124+
</ExclusiveButton>
125+
</div>
126+
}
127+
128+
<div className="flex flex-wrap justify-evenly text-center pt-2">
129+
{props.characters
130+
.filter(c => starFilter == 0 || starFilter == c.stars)
131+
.filter(c => elementFilter.some(e => c.element?.includes(e)) || c.element == undefined)
132+
.filter(c => weaponFilter.some(e => c.weapon?.includes(e)) || c.weapon == undefined)
133+
.map(char => {
134+
let color = ""
135+
if (char.stars == 5) color = "bg-amber-700"
136+
if (char.stars == 4) color = "bg-purple-800"
137+
138+
return <FormattedLink key={char.name} font="semibold" size="xl" location={props.location} href={`/characters/${urlify(char.name, false)}`} >
139+
<div className="bg-slate-300 dark:bg-slate-800 w-24 m-1 relative text-sm font-bold rounded-xl transition-all duration-100 hover:outline outline-slate-800 dark:outline-slate-300">
140+
<div className={`${color} rounded-t-xl w-24 h-24`}>
141+
<Icon char={char} className="rounded-t-xl w-24 m-0 p-0" />
142+
<span className="absolute block p-0.5 top-0 w-full">
143+
<div className="flex flex-col">
144+
{char.element && char.element.map(e => <div key={e} className="w-6 h-6">
145+
<Image src={elements[e]} alt={`${e} Element`} />
146+
</div>)}
147+
</div>
148+
</span>
149+
<span className="absolute block p-0.5 top-0 w-full">
150+
<div className="flex flex-col float-right">
151+
{char.weapon && <div className="w-6 h-6">
152+
<Image src={weapons[char.weapon]} alt={`${char.weapon}`} />
153+
</div>}
154+
</div>
155+
</span>
156+
</div>
157+
<span className="flex justify-center items-center h-10 m-0 p-0 duration-200">
158+
{char.name}
159+
</span>
56160
</div>
57-
{char.name}
58-
</div>
59-
</FormattedLink>
60-
))}
161+
</FormattedLink>
162+
})}
61163
</div>
62164
</Main>
63165
)
64166
}
65167

168+
function ExclusiveButton<T>({ type, value, setter, children }: { type: T, value: T, setter: Dispatch<SetStateAction<T>>, children: any }) {
169+
return <div
170+
onClick={() => setter(value)}
171+
className={`${type == value ? "bg-slate-400 dark:bg-slate-700 outline-slate-400 outline" : "bg-slate-300 dark:bg-slate-800"} px-2 py-0.5 rounded-lg cursor-pointer selection:bg-transparent`}
172+
>
173+
{children}
174+
</div>
175+
}
176+
177+
function ToggleAllButton<T>({ type, value, setter, children }: { type: T[], value: T[], setter: Dispatch<SetStateAction<T[]>>, children: any }) {
178+
const equal = type.length == value.length && type.every(e => value.includes(e))
179+
180+
return <div
181+
onClick={() => equal ? setter([]) : setter(value)}
182+
className={`${equal ? "bg-slate-400 dark:bg-slate-700 outline-slate-400 outline" : "bg-slate-300 dark:bg-slate-800"} px-2 py-0.5 rounded-lg cursor-pointer selection:bg-transparent`}
183+
>
184+
{children}
185+
</div>
186+
}
187+
188+
function ToggleButton<T>({ type, value, setter, children }: { type: T[], value: T, setter: Dispatch<SetStateAction<T[]>>, children: any }) {
189+
const has = type.includes(value)
190+
return <div
191+
onClick={() => {
192+
if (has) setter(type.filter(x => x != value))
193+
else setter([value, ...type])
194+
}}
195+
className={`${has ? "bg-slate-400 dark:bg-slate-700 outline-slate-400 outline" : "bg-slate-300 dark:bg-slate-800"
196+
} px-2 py-0.5 rounded-lg cursor-pointer selection:bg-transparent`}
197+
>
198+
{children}
199+
</div>
200+
}
201+
202+
203+
function Icon({ char, className }: { char: SmallChar, className?: string }) {
204+
let src = char.icon ?? "img/unknown.png"
205+
206+
if (src.startsWith("img")) src = "/" + src
207+
208+
return <Image alt={char.name} src={src} className={className} width={256} height={256} onError={(e) => (e.target as HTMLImageElement).src = "/img/unknown.png"} />
209+
}
210+
66211
function isFullCharacter(char: Character): char is CharacterFull {
67212
return typeof (char as CharacterFull).releasedOn == "string"
68213
}
@@ -93,7 +238,7 @@ export async function getStaticProps(context: GetStaticPropsContext): Promise<Ge
93238
.map(c => {
94239
const char: SmallChar = { name: c.name }
95240
if (c.star) char.stars = c.star
96-
if (c.skills) char.element = c.skills.map(skill => skill.ult?.type).filter(x => x) as Element[]
241+
if (c.skills) char.element = c.skills.map(skill => skill.ult?.type).filter(x => x) as ElementType[]
97242
if (c.weaponType) char.weapon = c.weaponType
98243
if (c.icon) char.icon = c.icon
99244
return char

public/img/weapon_types/Bow.png

12.9 KB
Loading
13.9 KB
Loading
14.2 KB
Loading
14.9 KB
Loading

public/img/weapon_types/Sword.png

14 KB
Loading

0 commit comments

Comments
 (0)