Skip to content

Commit 67f74fa

Browse files
committed
Add materials
1 parent 178e554 commit 67f74fa

File tree

16 files changed

+477
-102
lines changed

16 files changed

+477
-102
lines changed

components/Filters.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Dispatch, SetStateAction } from "react"
2+
3+
export
4+
function ExclusiveButton<T>({ type, value, setter, children }: { type: T, value: T, setter: Dispatch<SetStateAction<T>>, children: any }) {
5+
return <div
6+
onClick={() => setter(value)}
7+
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`}
8+
>
9+
{children}
10+
</div>
11+
}
12+
13+
export function ToggleAllButton<T>({ type, value, setter, children }: { type: T[], value: T[], setter: Dispatch<SetStateAction<T[]>>, children: any }) {
14+
const equal = type.length == value.length && type.every(e => value.includes(e))
15+
16+
return <div
17+
onClick={() => equal ? setter([]) : setter(value)}
18+
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`}
19+
>
20+
{children}
21+
</div>
22+
}
23+
24+
export function ToggleButton<T>({ type, value, setter, children }: { type: T[], value: T, setter: Dispatch<SetStateAction<T[]>>, children: any }) {
25+
const has = type.includes(value)
26+
return <div
27+
onClick={() => {
28+
if (has) setter(type.filter(x => x != value))
29+
else setter([value, ...type])
30+
}}
31+
className={`${has ? "bg-slate-400 dark:bg-slate-700 outline-slate-400 outline" : "bg-slate-300 dark:bg-slate-800"
32+
} px-2 py-0.5 rounded-lg cursor-pointer selection:bg-transparent`}
33+
>
34+
{children}
35+
</div>
36+
}

components/FormattedLink.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@ import Link from "next/link"
22
import { ForwardedRef, forwardRef } from "react"
33

44

5-
const Wrapped = forwardRef(function FLink({ children, onClick, href, font = "semibold", size = "xl", className = "", style={}, location, target = undefined }: any, innerRef: ForwardedRef<HTMLAnchorElement>) {
5+
const Wrapped = forwardRef(function FLink({ children, onClick, href, className = "", style={}, location, target = undefined }: any, innerRef: ForwardedRef<HTMLAnchorElement>) {
66
let colors = "text-blue-800 dark:text-blue-100 hover:text-blue-500 dark:hover:text-blue-400"
77
if (href == location)
88
colors = "text-blue-600 dark:text-blue-400 hover:text-blue-400 dark:hover:text-blue-500"
99
else if (location?.startsWith(href) && href != "/")
1010
colors = "text-blue-700 dark:text-blue-300 hover:text-blue-400 dark:hover:text-blue-400"
1111

1212
return (
13-
<a ref={innerRef} className={`${className} text-${size} font-${font} no-underline transition-all duration-200 ${colors}`} style={style} onClick={onClick} href={href} target={target} >
13+
<a ref={innerRef} className={`${className} no-underline transition-all duration-200 ${colors}`} style={style} onClick={onClick} href={href} target={target} >
1414
{children}
1515
</a>
1616
)
1717
})
1818

19-
function FormattedLink({ children, href, font = "semibold", size = "xl", className = "", style = {}, location, target = undefined }: any) {
19+
function FormattedLink({ children, href, className = "", style = {}, location, target = undefined }: any) {
2020
return (
21-
<Link href={href} passHref><Wrapped font={font} size={size} location={location} className={className} target={target} style={style}>{children}</Wrapped></Link>
21+
<Link href={href} passHref><Wrapped location={location} className={className} target={target} style={style}>{children}</Wrapped></Link>
2222
)
2323
}
2424

components/Guides.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import FormattedLink from "./FormattedLink"
2+
3+
export default function Guides({ guides }: { guides: string[][] }) {
4+
const multiple = guides.length > 1
5+
6+
return <>
7+
<h3 className="text-lg font-bold pt-1" id="guides">{multiple ? "Guides" : "Guide"}:</h3>
8+
{guides.map(([name, link]) => <div key={name}>
9+
<FormattedLink href={link}>{name}</FormattedLink>
10+
</div>)}
11+
</>
12+
}

components/Material.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { image, urlify } from "../utils/utils"
2+
import FormattedLink from "./FormattedLink"
3+
4+
/* eslint-disable @next/next/no-img-element */
5+
export function MaterialImage({ name }: { name: string }) {
6+
return <FormattedLink className="inline-block pr-1 w-6 h-6 md:h-8 md:w-8" href={`/materials/${urlify(name, false)}`}>
7+
<img src={image("material", name)} alt={name} width={256} height={256} />
8+
</FormattedLink>
9+
}
10+
11+
12+
export function MaterialCost({ name, count }: { name: string, count: number }) {
13+
return <div className="flex flex-row align-middle items-center">
14+
<div>{count}&times;</div>
15+
<FormattedLink href={`/materials/${urlify(name, false)}`} className="flex flex-row align-middle items-center">
16+
<div className="pr-1 w-8 h-8 sm:h-6 sm:w-6 md:h-8 md:w-8">
17+
<img src={image("material", name)} alt={name} width={256} height={256} />
18+
</div>
19+
<div className="md:text-sm lg:text-base sm:not-sr-only sr-only text-xs font-normal">{name}</div>
20+
</FormattedLink>
21+
</div>
22+
}

components/NavBar.tsx

Lines changed: 2 additions & 2 deletions
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", "Materials", "Reminders"]
55

66
export default function NavBar({ location }: {location: string}) {
77
const [menuOpen, setMenuOpen] = useState(false)
@@ -11,7 +11,7 @@ export default function NavBar({ location }: {location: string}) {
1111
<div className="text-xl bg-gradient-to-r from-blue-200 to-blue-100 dark:from-slate-900 dark:to-slate-800 flex items-center justify-between md:justify-start p-4 w-full top-0">
1212
<div className={`flex items-center pr-20 ${menuOpen ? "hidden": ""} sm:block`}>
1313
<Logo />
14-
<FormattedLink font="bold" location={location} href="/">Hu Tao</FormattedLink>
14+
<FormattedLink location={location} href="/" className="font-bold">Hu Tao</FormattedLink>
1515
</div>
1616
<nav className="hidden md:block space-x-6">
1717
{navLinks}
Lines changed: 18 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import Image from "next/image"
55
import { ReactElement, useState } from "react"
66
import ReactMarkdown from "react-markdown"
77
import FormattedLink from "../../components/FormattedLink"
8+
import Guides from "../../components/Guides"
89
import Icon from "../../components/Icon"
910
import Main from "../../components/Main"
11+
import { MaterialCost, MaterialImage } from "../../components/Material"
1012
import YouTube from "../../components/YouTube"
1113
import { CharacterCurves, CostTemplates, getCharacterCurves, getCharacters, getCostTemplates } from "../../utils/data-cache"
1214
import { Character, CharacterFull, Constellation, CostTemplate, CurveEnum, Meta, Passive, Skill, Skills, TalentTable, TalentValue } from "../../utils/types"
13-
import { elements, ElementType, getCharStatsAt, getCostsFromTemplate, getGuidesFor, getLinkToGuide, image, isFullCharacter, isValueTable, stat, urlify, weapons } from "../../utils/utils"
15+
import { elements, ElementType, getCharStatsAt, getCostsFromTemplate, getGuidesFor, getLinkToGuide, getStarColor, image, isFullCharacter, isValueTable, stat, urlify, weapons } from "../../utils/utils"
1416
import styles from "../style.module.css"
1517

1618
interface Props {
@@ -23,9 +25,7 @@ interface Props {
2325
export default function CharacterWebpage({ char, location, characterCurves, costTemplates, guides }: Props & { location: string }) {
2426
const charElems = char.skills?.map(skill => skill.ult?.type).filter(x => x) as ElementType[] ?? [char.meta.element]
2527
const multiskill = (char.skills?.length ?? 0) > 1
26-
let color = ""
27-
if (char.star == 5) color = "bg-amber-600 dark:bg-amber-700"
28-
if (char.star == 4) color = "bg-purple-700 dark:bg-purple-800"
28+
const color = getStarColor(char.star ?? 0)
2929

3030
return (
3131
<Main>
@@ -36,7 +36,7 @@ export default function CharacterWebpage({ char, location, characterCurves, cost
3636
<meta property="og:description" content={`View ${char.name} information`} />
3737
</Head>
3838
<h2 className="font-semibold">
39-
<FormattedLink href="/characters/" location={location} font="semibold" size="lg">
39+
<FormattedLink href="/characters/" location={location} className="font-semibold text-lg">
4040
Characters
4141
</FormattedLink>
4242
</h2>
@@ -60,7 +60,7 @@ export default function CharacterWebpage({ char, location, characterCurves, cost
6060
</>))}
6161
</div>
6262

63-
<div className="grid grid-flow-col">
63+
<div className="grid grid-flow-col justify-start">
6464
<div className="sm:w-36 mr-2 w-0 ">
6565
<Icon icon={char} className={`${color} rounded-xl`} />
6666
</div>
@@ -110,7 +110,7 @@ export default function CharacterWebpage({ char, location, characterCurves, cost
110110
function TOC({ href, title, depth = 0 }: { href: string, title: string, depth?: number }) {
111111
const size = depth > 0 ? "sm" : "base"
112112
return <div>
113-
<FormattedLink href={href} size={size} style={({ marginLeft: (0.25 * depth) + "rem" })}>{title}</FormattedLink>
113+
<FormattedLink href={href} className={`text-${size}`} style={({ marginLeft: (0.25 * depth) + "rem" })}>{title}</FormattedLink>
114114
</div>
115115
}
116116

@@ -123,9 +123,7 @@ function AscensionCosts({ costs }: { costs: CostTemplate }) {
123123
].filter(x => x)
124124
return <div className="flex flex-wrap items-center">
125125
<div className="text-base font-semibold pt-1 inline-block pr-1 h-9">Ascension materials:</div>
126-
{ascensionCosts.map(e => <div className="inline-block pr-1 w-6 h-6 md:h-8 md:w-8" key={e}>
127-
<img src={image("material", e)} alt={e} width={256} height={256} />
128-
</div>)}
126+
{ascensionCosts.map(e => <MaterialImage key={e} name={e} />)}
129127
</div>
130128
}
131129

@@ -135,7 +133,7 @@ function FullAscensionCosts({ template, costTemplates }: { template: CostTemplat
135133

136134
return <>
137135
<h3 className="text-lg font-bold pt-1" id="ascensions">Ascensions:</h3>
138-
<table className={`table-auto w-full ${styles.table} mb-2 ${expanded ? "" : "cursor-pointer"} sm:text-sm md:text-base text-xs`} onClick={() => setExpanded(true)}>
136+
<table className={`table-auto w-full ${styles.table} mb-2 ${expanded ? "" : "cursor-pointer"} sm:text-sm md:text-base text-xs`} onClick={(e) => setExpanded(true)}>
139137
<thead className="font-semibold divide-x divide-gray-200 dark:divide-gray-500">
140138
<td>Asc.</td>
141139
<td>Mora</td>
@@ -152,13 +150,7 @@ function FullAscensionCosts({ template, costTemplates }: { template: CostTemplat
152150
<td>A{ind + 1}</td>
153151
<td className="text-right">{mora}</td>
154152
{newItems.map(({ count, name }) => <td key={name}>
155-
{count > 0 &&
156-
<div className="flex flex-row align-middle items-center">
157-
<div>{count}&times;</div>
158-
<div className="pr-1 w-8 h-8 sm:h-6 sm:w-6 md:h-8 md:w-8">
159-
<img src={image("material", name)} alt={name} width={256} height={256} />
160-
</div>
161-
<div className="md:text-sm lg:text-base sm:not-sr-only sr-only text-xs">{name}</div></div>}
153+
{count > 0 && <MaterialCost name={name} count={count}/> }
162154
</td>)}
163155
</tr>
164156
})
@@ -197,9 +189,7 @@ function TalentCosts({ skills }: { skills: Skills[] }) {
197189
const all = [...books, ...mats, ...drops] as string[]
198190
return <div className="flex flex-wrap items-center">
199191
<div className="text-base font-semibold pt-1 inline-block pr-1 h-9">Talent materials:</div>
200-
{all.map(e => <div className="inline-block pr-1 w-6 h-6 md:h-8 md:w-8" key={e}>
201-
<img src={image("material", e)} alt={e} width={256} height={256} />
202-
</div>)}
192+
{all.map(e => <MaterialImage key={e} name={e} />)}
203193
</div>
204194
}
205195

@@ -248,7 +238,7 @@ function Meta({ meta }: { meta: Meta }) {
248238
<table className={`table-auto ${styles.table} mb-2 w-full`}>
249239
<tbody className="divide-y divide-gray-200 dark:divide-gray-500">
250240
{meta.title && <tr><td>Title</td><td>{meta.title}</td></tr>}
251-
{meta.birthDay && meta.birthMonth && <tr><td>Title</td><td>{
241+
{meta.birthDay && meta.birthMonth && <tr><td>Birthday</td><td>{
252242
new Date(Date.UTC(2020, meta.birthMonth - 1, meta.birthDay))
253243
.toLocaleString("en-UK", {
254244
timeZone: "UTC",
@@ -290,15 +280,6 @@ function Video({ name, link }: { name: string, link: string }) {
290280
</div>
291281
}
292282

293-
function Guides({ guides }: { guides: string[][] }) {
294-
const multiple = guides.length > 1
295-
296-
return <>
297-
<h3 className="text-lg font-bold pt-1" id="guides">{multiple ? "Guides" : "Guide"}:</h3>
298-
{guides.map(([name, link]) => <div key={name}><FormattedLink href={link} size="base">{name}</FormattedLink></div>)}
299-
</>
300-
}
301-
302283
function CharacterSkills({ skills, costTemplates }: { skills: Skills[], costTemplates: CostTemplates }) {
303284
return <>
304285
{skills.map((skill, i) => {
@@ -358,14 +339,7 @@ function TalentCost({ template, costTemplates }: { template: CostTemplate, costT
358339
<td>{ind + 1}&rarr;{ind + 2}</td>
359340
<td className="text-right">{mora}</td>
360341
{items.map(({ count, name }, i, arr) => <td key={name} colSpan={i == arr.length - 1 ? maxCostWidth - i : 1}>
361-
{count > 0 &&
362-
<div className="flex flex-row align-middle items-center">
363-
<div>{count}&times;</div>
364-
<div className="pr-1 w-8 h-8 sm:h-6 sm:w-6 md:h-8 md:w-8">
365-
<img src={image("material", name)} alt={name} width={256} height={256} />
366-
</div>
367-
<div className="md:text-sm lg:text-base sm:not-sr-only sr-only text-xs">{name}</div>
368-
</div>}
342+
{count > 0 && <MaterialCost name={name} count={count}/> }
369343
</td>)}
370344
</tr>
371345
)
@@ -386,7 +360,7 @@ function TalentTable({ table }: { table: (TalentTable | TalentValue)[] }) {
386360

387361
function hint(input: string): ReactElement {
388362
return <>
389-
{input.split("").map(x => <>{x}{x.match(/[+/%]/) && <wbr />}</>)}
363+
{input.split("").map(x => <span key={x}>{x}{x.match(/[+/%]/) && <wbr />}</span>)}
390364
</>
391365
}
392366
function countUp<T>(arr: T[], v: T, i: number): number {
@@ -405,7 +379,7 @@ function TalentTable({ table }: { table: (TalentTable | TalentValue)[] }) {
405379
<table className={`${maxLevel > 3 ? "table-auto" : "table-fixed"} w-full ${styles.table} mb-2 sm:text-sm md:text-base text-xs`}>
406380
<thead className="font-semibold divide-x divide-gray-200 dark:divide-gray-500">
407381
<td>Name</td>
408-
{levels.map((i) => <td key={i}>Lv. {i + 1}</td>)}
382+
{levels.map((i) => <td key={i + 1}>Lv. {i + 1}</td>)}
409383
</thead>
410384
<tbody className="divide-y divide-gray-200 dark:divide-gray-500">
411385
{table
@@ -446,7 +420,7 @@ function Constellation({ c }: { c: Constellation }) {
446420
}
447421

448422
export async function getStaticProps(context: GetStaticPropsContext): Promise<GetStaticPropsResult<Props>> {
449-
const charName = context.params?.name
423+
const charName = context.params?.char
450424
const data = await getCharacters()
451425

452426
const char = Object.values(data ?? {}).find(g => urlify(g.name, false) == charName)
@@ -499,8 +473,8 @@ export async function getStaticProps(context: GetStaticPropsContext): Promise<Ge
499473
export async function getStaticPaths(): Promise<GetStaticPathsResult> {
500474
const data = await getCharacters()
501475
return {
502-
paths: Object.values(data ?? {}).map(g => ({
503-
params: { name: urlify(g.name, false) }
476+
paths: Object.values(data ?? {}).map(c => ({
477+
params: { char: urlify(c.name, false) }
504478
})) ?? [],
505479
fallback: "blocking"
506480
}

pages/characters/index.tsx

Lines changed: 5 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
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"
4+
import { useState } from "react"
5+
import { ExclusiveButton, ToggleAllButton, ToggleButton } from "../../components/Filters"
56
import FormattedLink from "../../components/FormattedLink"
67
import Icon from "../../components/Icon"
78
import Main from "../../components/Main"
89
import { getCharacters } from "../../utils/data-cache"
910
import { WeaponType } from "../../utils/types"
10-
import { elements, ElementType, isFullCharacter, urlify, weapons } from "../../utils/utils"
11+
import { elements, ElementType, getStarColor, isFullCharacter, urlify, weapons } from "../../utils/utils"
1112

1213

1314
interface SmallChar {
@@ -118,11 +119,9 @@ export default function Characters(props: Props & { location: string }) {
118119
.filter(c => elementFilter.some(e => c.element?.includes(e)) || c.element == undefined)
119120
.filter(c => weaponFilter.some(e => c.weapon?.includes(e)) || c.weapon == undefined)
120121
.map(char => {
121-
let color = ""
122-
if (char.stars == 5) color = "bg-amber-600 dark:bg-amber-700"
123-
if (char.stars == 4) color = "bg-purple-700 dark:bg-purple-800"
122+
const color = getStarColor(char.stars ?? 0)
124123

125-
return <FormattedLink key={char.name} font="bold" size="sm" location={props.location} href={`/characters/${urlify(char.name, false)}`} className="bg-slate-300 dark:bg-slate-800 w-24 sm:w-28 lg:w-32 m-1 relative rounded-xl transition-all duration-100 hover:outline outline-slate-800 dark:outline-slate-300" >
124+
return <FormattedLink key={char.name} location={props.location} href={`/characters/${urlify(char.name, false)}`} className="bg-slate-300 dark:bg-slate-800 w-24 sm:w-28 lg:w-32 m-1 relative rounded-xl transition-all duration-100 hover:outline outline-slate-800 dark:outline-slate-300 font-bold text-sm" >
126125
<div className={`${color} rounded-t-xl h-24 sm:h-28 lg:h-32`}>
127126
<Icon icon={char} className="rounded-t-xl m-0 p-0" />
128127
<span className="absolute block p-0.5 top-0 w-full">
@@ -150,39 +149,6 @@ export default function Characters(props: Props & { location: string }) {
150149
)
151150
}
152151

153-
function ExclusiveButton<T>({ type, value, setter, children }: { type: T, value: T, setter: Dispatch<SetStateAction<T>>, children: any }) {
154-
return <div
155-
onClick={() => setter(value)}
156-
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`}
157-
>
158-
{children}
159-
</div>
160-
}
161-
162-
function ToggleAllButton<T>({ type, value, setter, children }: { type: T[], value: T[], setter: Dispatch<SetStateAction<T[]>>, children: any }) {
163-
const equal = type.length == value.length && type.every(e => value.includes(e))
164-
165-
return <div
166-
onClick={() => equal ? setter([]) : setter(value)}
167-
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`}
168-
>
169-
{children}
170-
</div>
171-
}
172-
173-
function ToggleButton<T>({ type, value, setter, children }: { type: T[], value: T, setter: Dispatch<SetStateAction<T[]>>, children: any }) {
174-
const has = type.includes(value)
175-
return <div
176-
onClick={() => {
177-
if (has) setter(type.filter(x => x != value))
178-
else setter([value, ...type])
179-
}}
180-
className={`${has ? "bg-slate-400 dark:bg-slate-700 outline-slate-400 outline" : "bg-slate-300 dark:bg-slate-800"
181-
} px-2 py-0.5 rounded-lg cursor-pointer selection:bg-transparent`}
182-
>
183-
{children}
184-
</div>
185-
}
186152

187153
export async function getStaticProps(context: GetStaticPropsContext): Promise<GetStaticPropsResult<Props>> {
188154
const data = await getCharacters()

0 commit comments

Comments
 (0)