Skip to content

Commit ba4f869

Browse files
committed
Add expandable
1 parent 26794e6 commit ba4f869

10 files changed

Lines changed: 132 additions & 64 deletions

File tree

pages/500.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import Head from "next/head"
2+
import FormattedLink from "../components/FormattedLink"
3+
import Main from "../components/Main"
4+
5+
export default function Custom500() {
6+
return <Main>
7+
<Head>
8+
<title>A Server Error occurred | Hu Tao</title>
9+
</Head>
10+
<h1 className="text-6xl">500 - A Server Error occurred</h1>
11+
<FormattedLink href="/" location="/500">Home</FormattedLink>
12+
</Main>
13+
}

pages/_app.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export default function MyApp({ Component, pageProps, router }: AppProps) {
2626

2727
<div className="w-full">
2828
<NavBar location={router.asPath}/>
29-
<div className="p-4 flex flex-col w-full flex-1 px-1 md:px-20 items-center justify-center">
29+
<div className="p-4 flex flex-col w-full flex-1 px-1 lg:px-20 items-center justify-center">
3030
<Component {...pageProps} location={router.asPath}/>
3131
</div>
3232
</div>

pages/characters/[name].tsx

Lines changed: 64 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,19 @@ import { useState } from "react"
66
import FormattedLink from "../../components/FormattedLink"
77
import Main from "../../components/Main"
88
import YouTube from "../../components/YouTube"
9-
import { CharacterCurves, CostTemplates, getCharacterCurves, getCharacters, getCostTemplates, urlify } from "../../utils/data-cache"
10-
import { elements, ElementType, getCharStatsAt, getCostsFromTemplate, image, isFullCharacter, stat, weapons } from "../../utils/utils"
9+
import { CharacterCurves, CostTemplates, getCharacterCurves, getCharacters, getCostTemplates } from "../../utils/data-cache"
1110
import { Character, CharacterFull, CostTemplate, CurveEnum, Meta, Skills } from "../../utils/types"
11+
import { elements, ElementType, getCharStatsAt, getCostsFromTemplate, getGuidesFor, getLinkToGuide, image, isFullCharacter, stat, urlify, weapons } from "../../utils/utils"
1212
import styles from "../style.module.css"
1313

1414
interface Props {
1515
char: Character,
1616
characterCurves: CharacterCurves | null
1717
costTemplates: CostTemplates | null
18+
guides?: string[][]
1819
}
1920

20-
export default function CharacterWebpage({ char, location, characterCurves, costTemplates }: Props & { location: string }) {
21+
export default function CharacterWebpage({ char, location, characterCurves, costTemplates, guides }: Props & { location: string }) {
2122
const charElems = char.skills?.map(skill => skill.ult?.type).filter(x => x) as ElementType[] ?? [char.meta.element]
2223
return (
2324
<Main>
@@ -39,9 +40,10 @@ export default function CharacterWebpage({ char, location, characterCurves, cost
3940

4041
<div className="float-right border-2 border-slate-500 dark:bg-slate-600 bg-slate-200 m-2 px-2">
4142
Table of Contents
42-
{isFullCharacter(char) && characterCurves && <TOC href="#stats" title="Stats / Ascensions" />}
43-
{char.meta && <TOC href="#meta" title="Meta" />}
43+
{isFullCharacter(char) && characterCurves && <TOC href="#stats" title="Stats" />}
44+
{char.ascensionCosts && costTemplates && <TOC href="#ascensions" title="Ascensions" />}
4445
{char.media.videos && <TOC href="#videos" title={Object.keys(char.media.videos).length > 1 ? "Videos" : "Video"} />}
46+
{char.meta && <TOC href="#meta" title="Meta" />}
4547
</div>
4648

4749
<div id="description">
@@ -67,10 +69,11 @@ export default function CharacterWebpage({ char, location, characterCurves, cost
6769

6870
{char.ascensionCosts && <AscensionCosts costs={char.ascensionCosts} />}
6971
{char.skills && <TalentCosts skills={char.skills} />}
72+
{guides && guides.length > 0 && <Guides guides={guides} />}
7073
{isFullCharacter(char) && characterCurves && <Stats char={char} curves={characterCurves} />}
7174
{char.ascensionCosts && costTemplates && <FullAscensionCosts template={char.ascensionCosts} costTemplates={costTemplates} />}
72-
{char.meta && <Meta meta={char.meta} />}
7375
{char.media.videos && <Videos videos={char.media.videos} />}
76+
{char.meta && <Meta meta={char.meta} />}
7477
</div>
7578
</Main>
7679
)
@@ -98,33 +101,43 @@ function AscensionCosts({ costs }: { costs: CostTemplate }) {
98101
}
99102

100103
function FullAscensionCosts({ template, costTemplates }: { template: CostTemplate, costTemplates: CostTemplates }) {
104+
const [expanded, setExpanded] = useState(false)
101105
const costs = getCostsFromTemplate(template, costTemplates)
102106

103107
return <>
104-
<table className={`table-auto w-full ${styles.table} mb-2`}>
108+
<h3 className="text-lg font-bold pt-1" id="ascensions">Ascensions:</h3>
109+
<table className={`table-auto w-full ${styles.table} mb-2 ${expanded ? "" : "cursor-pointer"}`} onClick={() => setExpanded(true)}>
105110
<thead className="font-semibold divide-x divide-gray-200 dark:divide-gray-500">
106111
<td>Asc.</td>
107112
<td>Mora</td>
108113
<td>Items</td>
109114
</thead>
110115
<tbody className="divide-y divide-gray-200 dark:divide-gray-500">
111-
{costs.slice(1).map(({ mora, items }, ind) => {
112-
let newItems = items
113-
if (ind == 0 && template.mapping.BossMat)
114-
newItems = [items[0], { name: "", count: 0 }, ...items.slice(1)]
115-
return <tr className="pr-1 divide-x divide-gray-200 dark:divide-gray-500" key={ind}>
116-
<td>A{ind + 1}</td>
117-
<td className="text-right">{mora}</td>
118-
{newItems.map(({ count, name }) => <td key={name}>
119-
{count > 0 &&
120-
<div className="flex flex-row align-middle items-center">
121-
<div>{count}&times;</div>
122-
<div className="pr-1 w-6 h-6 md:h-8 md:w-8">
123-
<img src={image("material", name)} alt={name} width={256} height={256} />
124-
</div>
125-
<div>{name}</div></div>}
126-
</td>)}
127-
</tr>})}
116+
{costs
117+
.slice(1)
118+
.map(({ mora, items }, ind) => {
119+
let newItems = items
120+
if (ind == 0 && template.mapping.BossMat)
121+
newItems = [items[0], { name: "", count: 0 }, ...items.slice(1)]
122+
return <tr className="pr-1 divide-x divide-gray-200 dark:divide-gray-500" key={ind}>
123+
<td>A{ind + 1}</td>
124+
<td className="text-right">{mora}</td>
125+
{newItems.map(({ count, name }) => <td key={name}>
126+
{count > 0 &&
127+
<div className="flex flex-row align-middle items-center">
128+
<div>{count}&times;</div>
129+
<div className="pr-1 w-8 h-8 sm:h-6 sm:w-6 md:h-8 md:w-8">
130+
<img src={image("material", name)} alt={name} width={256} height={256} />
131+
</div>
132+
<div className="md:text-sm lg:text-base sm:not-sr-only sr-only text-xs">{name}</div></div>}
133+
</td>)}
134+
</tr>
135+
})
136+
.filter((_, i, arr) => expanded ? true : (i == arr.length - 1))}
137+
{!expanded && <tr className="pr-1 cursor-pointer text-blue-700 dark:text-blue-300 hover:text-blue-400 dark:hover:text-blue-400 no-underline transition-all duration-200 font-semibold">
138+
<td colSpan={costs[costs.length - 1].items.length + 2} style={({ textAlign: "center" })}>Click to expand...</td>
139+
</tr>
140+
}
128141
</tbody>
129142
</table>
130143
</>
@@ -163,6 +176,8 @@ function TalentCosts({ skills }: { skills: Skills[] }) {
163176
}
164177

165178
function Stats({ char, curves }: { char: CharacterFull, curves: Record<CurveEnum, number[]> }) {
179+
const [expanded, setExpanded] = useState(false)
180+
166181
const maxAscension = char.ascensions[char.ascensions.length - 1]
167182

168183
const levels: { a: number, lv: number }[] = []
@@ -176,19 +191,24 @@ function Stats({ char, curves }: { char: CharacterFull, curves: Record<CurveEnum
176191
const max = getCharStatsAt(char, maxAscension.maxLevel, maxAscension.level, curves)
177192

178193
return <>
179-
<h3 className="text-lg font-bold pt-1" id="stats">Stats / Ascensions:</h3>
180-
<table className={`table-auto w-full ${styles.table} ${styles.stattable} mb-2`}>
194+
<h3 className="text-lg font-bold pt-1" id="stats">Stats:</h3>
195+
<table className={`table-auto w-full ${styles.table} ${styles.stattable} mb-2 ${expanded ? "" : "cursor-pointer"} sm:text-sm md:text-base text-xs`} onClick={() => setExpanded(true)}>
181196
<thead className="font-semibold divide-x divide-gray-200 dark:divide-gray-500">
182197
<td>Asc.</td>
183198
<td>Lv.</td>
184199
{Object.keys(max).map((name) => <td key={name}>{name}</td>)}
185200
</thead>
186201
<tbody className="divide-y divide-gray-200 dark:divide-gray-500">
187-
{levels.map(({ a, lv }) => <tr className="pr-1 divide-x divide-gray-200 dark:divide-gray-500" key={a + "," + lv}>
188-
<td>A{a}</td>
189-
<td>{lv}</td>
190-
{Object.entries(getCharStatsAt(char, lv, a, curves)).map(([name, value]) => <td key={name}>{stat(name, value)}</td>)}
191-
</tr>)}
202+
{levels
203+
.filter((r) => expanded ? true : (r.a == 0 && r.lv == 1) || (r.a == maxAscension.level && r.lv == maxAscension.maxLevel))
204+
.map(({ a, lv }) => <tr className="pr-1 divide-x divide-gray-200 dark:divide-gray-500" key={a + "," + lv}>
205+
<td>A{a}</td>
206+
<td>{lv}</td>
207+
{Object.entries(getCharStatsAt(char, lv, a, curves)).map(([name, value]) => <td key={name}>{stat(name, value)}</td>)}
208+
</tr>)}
209+
{!expanded && <tr className="pr-1 cursor-pointer text-blue-700 dark:text-blue-300 hover:text-blue-400 dark:hover:text-blue-400 no-underline transition-all duration-200 font-semibold">
210+
<td colSpan={Object.keys(getCharStatsAt(char, 1, 1, curves)).length + 2} style={({ textAlign: "center" })}>Click to expand...</td>
211+
</tr>}
192212
</tbody>
193213
</table>
194214
</>
@@ -242,6 +262,15 @@ function Video({ name, link }: { name: string, link: string }) {
242262
</div>
243263
}
244264

265+
function Guides({ guides }: { guides: string[][] }) {
266+
const multiple = guides.length > 1
267+
268+
return <>
269+
<h3 className="text-lg font-bold pt-1" id="guides">{multiple ? "Guides" : "Guide"}:</h3>
270+
{guides.map(([name, link]) => <div key={name}><FormattedLink href={link} size="base">{name}</FormattedLink></div>)}
271+
</>
272+
}
273+
245274
export async function getStaticProps(context: GetStaticPropsContext): Promise<GetStaticPropsResult<Props>> {
246275
const charName = context.params?.name
247276
const data = await getCharacters()
@@ -269,11 +298,14 @@ export async function getStaticProps(context: GetStaticPropsContext): Promise<Ge
269298

270299
}
271300

301+
const guides = (await getGuidesFor("character", char.name))?.map(({ guide, page }) => [page.name, getLinkToGuide(guide, page)])
302+
272303
return {
273304
props: {
274305
char,
275306
characterCurves,
276-
costTemplates
307+
costTemplates,
308+
guides
277309
},
278310
revalidate: 60 * 60
279311
}

pages/characters/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import Image from "next/image"
44
import { Dispatch, SetStateAction, useState } from "react"
55
import FormattedLink from "../../components/FormattedLink"
66
import Main from "../../components/Main"
7-
import { getCharacters, urlify } from "../../utils/data-cache"
7+
import { getCharacters } from "../../utils/data-cache"
88
import { WeaponType } from "../../utils/types"
9-
import { elements, ElementType, isFullCharacter, weapons } from "../../utils/utils"
9+
import { elements, ElementType, isFullCharacter, urlify, weapons } from "../../utils/utils"
1010

1111

1212
interface SmallChar {

pages/guides/[category].tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import { GetStaticPathsResult, GetStaticPropsContext, GetStaticPropsResult } fro
22
import Head from "next/head"
33
import FormattedLink from "../../components/FormattedLink"
44
import Main from "../../components/Main"
5-
import { getGuides, urlify } from "../../utils/data-cache"
5+
import { getGuides } from "../../utils/data-cache"
66
import { Guide } from "../../utils/types"
7+
import { urlify } from "../../utils/utils"
78

89
interface Props {
910
guide: Guide

pages/guides/[category]/[guide].tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import ReactMarkdown from "react-markdown"
44
import FormattedLink from "../../../components/FormattedLink"
55
import Main from "../../../components/Main"
66
import YouTube from "../../../components/YouTube"
7-
import { getGuides, urlify, yeetBrackets } from "../../../utils/data-cache"
7+
import { getGuides } from "../../../utils/data-cache"
88
import { Guide } from "../../../utils/types"
9+
import { removeBrackets, urlify } from "../../../utils/utils"
910
import styles from "../../style.module.css"
1011

1112
interface Props {
@@ -39,14 +40,14 @@ export default function GuideWebpage({ guide, pageNumber, location }: Props & {
3940
<div className="flex justify-between text-base">
4041
<div className="px-1">
4142
{prevPage && <FormattedLink href={`/guides/${urlify(guide.name, false)}/${urlify(prevPage.name, true)}`} location={location} font="bold" size="lg">
42-
&larr; {yeetBrackets(prevPage.name)}
43+
&larr; {removeBrackets(prevPage.name)}
4344
</FormattedLink>}
4445
</div>
4546

4647
<div>
4748
{nextPage &&
4849
<FormattedLink href={`/guides/${urlify(guide.name, false)}/${urlify(nextPage.name, true)}`} location={location} font="bold" size="lg">
49-
{yeetBrackets(nextPage.name)} &rarr;
50+
{removeBrackets(nextPage.name)} &rarr;
5051
</FormattedLink>}
5152
</div>
5253
</div>

pages/guides/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { GetStaticPropsContext, GetStaticPropsResult } from "next"
22
import Head from "next/head"
33
import FormattedLink from "../../components/FormattedLink"
44
import Main from "../../components/Main"
5-
import { getGuides, urlify } from "../../utils/data-cache"
5+
import { getGuides } from "../../utils/data-cache"
6+
import { urlify } from "../../utils/utils"
67

78
interface Props {
89
guides: string[]

utils/data-cache.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,6 @@ const cached: Cache = {
3838
time: 0
3939
}
4040
}
41-
export function urlify(input: string, shouldYeetBrackets: boolean): string {
42-
if (shouldYeetBrackets)
43-
input = yeetBrackets(input)
44-
return input.toLowerCase().replace(/\(|\)|:/g, "").trim().replace(/ +/g, "-")
45-
}
46-
47-
export function yeetBrackets(input: string) {
48-
return input.replace(/\(.*\)/g, "").replace(/ +:/, ":")
49-
}
5041

5142
export const getGuides: (() => Promise<Guides | undefined>) = createGetCacheable("guides")
5243
export const getCharacters: (() => Promise<Characters | undefined>) = createGetCacheable("characters", "gamedata/characters")

utils/types.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,6 @@ export interface Reminder {
2020
}
2121

2222

23-
// Guides
24-
export interface Guide {
25-
name: string
26-
pages: GuidePage[]
27-
}
28-
export interface GuidePage {
29-
name: string
30-
img?: string
31-
desc?: string
32-
url?: string
33-
credits: string
34-
}
35-
36-
3723
// Character
3824
export type Character = CharacterFull | CharacterPlaceholder
3925
export interface CharacterPlaceholder {
@@ -194,3 +180,22 @@ export enum WeaponType {
194180
Polearm = "Polearm",
195181
Sword = "Sword",
196182
}
183+
184+
185+
// Guides
186+
export interface Guide {
187+
name: string
188+
pages: GuidePage[]
189+
}
190+
export interface GuidePage {
191+
name: string
192+
img?: string
193+
desc?: string
194+
url?: string
195+
credits: string
196+
links?: {
197+
material?: string[]
198+
enemy?: string[]
199+
character?: string[]
200+
}
201+
}

utils/utils.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import Catalyst from "../public/img/weapon_types/Catalyst.png"
1010
import Claymore from "../public/img/weapon_types/Claymore.png"
1111
import Polearm from "../public/img/weapon_types/Polearm.png"
1212
import Sword from "../public/img/weapon_types/Sword.png"
13-
import { Character, CharacterFull, Cost, CostTemplate, CurveEnum, WeaponType } from "./types"
13+
import { getGuides } from "./data-cache"
14+
import { Character, CharacterFull, Cost, CostTemplate, CurveEnum, Guide, GuidePage, WeaponType } from "./types"
1415

1516
export const elements = {
1617
Pyro, Electro, Cryo, Hydro, Anemo, Geo, Dendro
@@ -97,3 +98,26 @@ export function getCostsFromTemplate(costTemplate: CostTemplate, costTemplates:
9798
}))
9899
}))
99100
}
101+
102+
export async function getGuidesFor(type: "enemy" | "character" | "material", name: string): Promise<{ guide: Guide, page: GuidePage }[]> {
103+
return (await getGuides())?.flatMap(guide => guide.pages
104+
.filter(page => page.links?.[type]?.includes(name))
105+
.map(page => ({
106+
guide, page
107+
}))
108+
) ?? []
109+
}
110+
111+
export function getLinkToGuide(guide: Guide, page: GuidePage): string {
112+
return `/guides/${urlify(guide.name, false)}/${urlify(page.name, true)}`
113+
}
114+
115+
export function urlify(input: string, shouldYeetBrackets: boolean): string {
116+
if (shouldYeetBrackets)
117+
input = removeBrackets(input)
118+
return input.toLowerCase().replace(/\(|\)|:/g, "").trim().replace(/ +/g, "-")
119+
}
120+
121+
export function removeBrackets(input: string) {
122+
return input.replace(/\(.*\)/g, "").replace(/ +:/, ":")
123+
}

0 commit comments

Comments
 (0)