Skip to content

Commit

Permalink
Add expandable
Browse files Browse the repository at this point in the history
  • Loading branch information
Tibowl committed Jan 14, 2022
1 parent 26794e6 commit ba4f869
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 64 deletions.
13 changes: 13 additions & 0 deletions pages/500.tsx
@@ -0,0 +1,13 @@
import Head from "next/head"
import FormattedLink from "../components/FormattedLink"
import Main from "../components/Main"

export default function Custom500() {
return <Main>
<Head>
<title>A Server Error occurred | Hu Tao</title>
</Head>
<h1 className="text-6xl">500 - A Server Error occurred</h1>
<FormattedLink href="/" location="/500">Home</FormattedLink>
</Main>
}
2 changes: 1 addition & 1 deletion pages/_app.tsx
Expand Up @@ -26,7 +26,7 @@ export default function MyApp({ Component, pageProps, router }: AppProps) {

<div className="w-full">
<NavBar location={router.asPath}/>
<div className="p-4 flex flex-col w-full flex-1 px-1 md:px-20 items-center justify-center">
<div className="p-4 flex flex-col w-full flex-1 px-1 lg:px-20 items-center justify-center">
<Component {...pageProps} location={router.asPath}/>
</div>
</div>
Expand Down
96 changes: 64 additions & 32 deletions pages/characters/[name].tsx
Expand Up @@ -6,18 +6,19 @@ import { useState } from "react"
import FormattedLink from "../../components/FormattedLink"
import Main from "../../components/Main"
import YouTube from "../../components/YouTube"
import { CharacterCurves, CostTemplates, getCharacterCurves, getCharacters, getCostTemplates, urlify } from "../../utils/data-cache"
import { elements, ElementType, getCharStatsAt, getCostsFromTemplate, image, isFullCharacter, stat, weapons } from "../../utils/utils"
import { CharacterCurves, CostTemplates, getCharacterCurves, getCharacters, getCostTemplates } from "../../utils/data-cache"
import { Character, CharacterFull, CostTemplate, CurveEnum, Meta, Skills } from "../../utils/types"
import { elements, ElementType, getCharStatsAt, getCostsFromTemplate, getGuidesFor, getLinkToGuide, image, isFullCharacter, stat, urlify, weapons } from "../../utils/utils"
import styles from "../style.module.css"

interface Props {
char: Character,
characterCurves: CharacterCurves | null
costTemplates: CostTemplates | null
guides?: string[][]
}

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

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

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

{char.ascensionCosts && <AscensionCosts costs={char.ascensionCosts} />}
{char.skills && <TalentCosts skills={char.skills} />}
{guides && guides.length > 0 && <Guides guides={guides} />}
{isFullCharacter(char) && characterCurves && <Stats char={char} curves={characterCurves} />}
{char.ascensionCosts && costTemplates && <FullAscensionCosts template={char.ascensionCosts} costTemplates={costTemplates} />}
{char.meta && <Meta meta={char.meta} />}
{char.media.videos && <Videos videos={char.media.videos} />}
{char.meta && <Meta meta={char.meta} />}
</div>
</Main>
)
Expand Down Expand Up @@ -98,33 +101,43 @@ function AscensionCosts({ costs }: { costs: CostTemplate }) {
}

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

return <>
<table className={`table-auto w-full ${styles.table} mb-2`}>
<h3 className="text-lg font-bold pt-1" id="ascensions">Ascensions:</h3>
<table className={`table-auto w-full ${styles.table} mb-2 ${expanded ? "" : "cursor-pointer"}`} onClick={() => setExpanded(true)}>
<thead className="font-semibold divide-x divide-gray-200 dark:divide-gray-500">
<td>Asc.</td>
<td>Mora</td>
<td>Items</td>
</thead>
<tbody className="divide-y divide-gray-200 dark:divide-gray-500">
{costs.slice(1).map(({ mora, items }, ind) => {
let newItems = items
if (ind == 0 && template.mapping.BossMat)
newItems = [items[0], { name: "", count: 0 }, ...items.slice(1)]
return <tr className="pr-1 divide-x divide-gray-200 dark:divide-gray-500" key={ind}>
<td>A{ind + 1}</td>
<td className="text-right">{mora}</td>
{newItems.map(({ count, name }) => <td key={name}>
{count > 0 &&
<div className="flex flex-row align-middle items-center">
<div>{count}&times;</div>
<div className="pr-1 w-6 h-6 md:h-8 md:w-8">
<img src={image("material", name)} alt={name} width={256} height={256} />
</div>
<div>{name}</div></div>}
</td>)}
</tr>})}
{costs
.slice(1)
.map(({ mora, items }, ind) => {
let newItems = items
if (ind == 0 && template.mapping.BossMat)
newItems = [items[0], { name: "", count: 0 }, ...items.slice(1)]
return <tr className="pr-1 divide-x divide-gray-200 dark:divide-gray-500" key={ind}>
<td>A{ind + 1}</td>
<td className="text-right">{mora}</td>
{newItems.map(({ count, name }) => <td key={name}>
{count > 0 &&
<div className="flex flex-row align-middle items-center">
<div>{count}&times;</div>
<div className="pr-1 w-8 h-8 sm:h-6 sm:w-6 md:h-8 md:w-8">
<img src={image("material", name)} alt={name} width={256} height={256} />
</div>
<div className="md:text-sm lg:text-base sm:not-sr-only sr-only text-xs">{name}</div></div>}
</td>)}
</tr>
})
.filter((_, i, arr) => expanded ? true : (i == arr.length - 1))}
{!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">
<td colSpan={costs[costs.length - 1].items.length + 2} style={({ textAlign: "center" })}>Click to expand...</td>
</tr>
}
</tbody>
</table>
</>
Expand Down Expand Up @@ -163,6 +176,8 @@ function TalentCosts({ skills }: { skills: Skills[] }) {
}

function Stats({ char, curves }: { char: CharacterFull, curves: Record<CurveEnum, number[]> }) {
const [expanded, setExpanded] = useState(false)

const maxAscension = char.ascensions[char.ascensions.length - 1]

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

return <>
<h3 className="text-lg font-bold pt-1" id="stats">Stats / Ascensions:</h3>
<table className={`table-auto w-full ${styles.table} ${styles.stattable} mb-2`}>
<h3 className="text-lg font-bold pt-1" id="stats">Stats:</h3>
<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)}>
<thead className="font-semibold divide-x divide-gray-200 dark:divide-gray-500">
<td>Asc.</td>
<td>Lv.</td>
{Object.keys(max).map((name) => <td key={name}>{name}</td>)}
</thead>
<tbody className="divide-y divide-gray-200 dark:divide-gray-500">
{levels.map(({ a, lv }) => <tr className="pr-1 divide-x divide-gray-200 dark:divide-gray-500" key={a + "," + lv}>
<td>A{a}</td>
<td>{lv}</td>
{Object.entries(getCharStatsAt(char, lv, a, curves)).map(([name, value]) => <td key={name}>{stat(name, value)}</td>)}
</tr>)}
{levels
.filter((r) => expanded ? true : (r.a == 0 && r.lv == 1) || (r.a == maxAscension.level && r.lv == maxAscension.maxLevel))
.map(({ a, lv }) => <tr className="pr-1 divide-x divide-gray-200 dark:divide-gray-500" key={a + "," + lv}>
<td>A{a}</td>
<td>{lv}</td>
{Object.entries(getCharStatsAt(char, lv, a, curves)).map(([name, value]) => <td key={name}>{stat(name, value)}</td>)}
</tr>)}
{!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">
<td colSpan={Object.keys(getCharStatsAt(char, 1, 1, curves)).length + 2} style={({ textAlign: "center" })}>Click to expand...</td>
</tr>}
</tbody>
</table>
</>
Expand Down Expand Up @@ -242,6 +262,15 @@ function Video({ name, link }: { name: string, link: string }) {
</div>
}

function Guides({ guides }: { guides: string[][] }) {
const multiple = guides.length > 1

return <>
<h3 className="text-lg font-bold pt-1" id="guides">{multiple ? "Guides" : "Guide"}:</h3>
{guides.map(([name, link]) => <div key={name}><FormattedLink href={link} size="base">{name}</FormattedLink></div>)}
</>
}

export async function getStaticProps(context: GetStaticPropsContext): Promise<GetStaticPropsResult<Props>> {
const charName = context.params?.name
const data = await getCharacters()
Expand Down Expand Up @@ -269,11 +298,14 @@ export async function getStaticProps(context: GetStaticPropsContext): Promise<Ge

}

const guides = (await getGuidesFor("character", char.name))?.map(({ guide, page }) => [page.name, getLinkToGuide(guide, page)])

return {
props: {
char,
characterCurves,
costTemplates
costTemplates,
guides
},
revalidate: 60 * 60
}
Expand Down
4 changes: 2 additions & 2 deletions pages/characters/index.tsx
Expand Up @@ -4,9 +4,9 @@ import Image from "next/image"
import { Dispatch, SetStateAction, useState } from "react"
import FormattedLink from "../../components/FormattedLink"
import Main from "../../components/Main"
import { getCharacters, urlify } from "../../utils/data-cache"
import { getCharacters } from "../../utils/data-cache"
import { WeaponType } from "../../utils/types"
import { elements, ElementType, isFullCharacter, weapons } from "../../utils/utils"
import { elements, ElementType, isFullCharacter, urlify, weapons } from "../../utils/utils"


interface SmallChar {
Expand Down
3 changes: 2 additions & 1 deletion pages/guides/[category].tsx
Expand Up @@ -2,8 +2,9 @@ import { GetStaticPathsResult, GetStaticPropsContext, GetStaticPropsResult } fro
import Head from "next/head"
import FormattedLink from "../../components/FormattedLink"
import Main from "../../components/Main"
import { getGuides, urlify } from "../../utils/data-cache"
import { getGuides } from "../../utils/data-cache"
import { Guide } from "../../utils/types"
import { urlify } from "../../utils/utils"

interface Props {
guide: Guide
Expand Down
7 changes: 4 additions & 3 deletions pages/guides/[category]/[guide].tsx
Expand Up @@ -4,8 +4,9 @@ import ReactMarkdown from "react-markdown"
import FormattedLink from "../../../components/FormattedLink"
import Main from "../../../components/Main"
import YouTube from "../../../components/YouTube"
import { getGuides, urlify, yeetBrackets } from "../../../utils/data-cache"
import { getGuides } from "../../../utils/data-cache"
import { Guide } from "../../../utils/types"
import { removeBrackets, urlify } from "../../../utils/utils"
import styles from "../../style.module.css"

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

<div>
{nextPage &&
<FormattedLink href={`/guides/${urlify(guide.name, false)}/${urlify(nextPage.name, true)}`} location={location} font="bold" size="lg">
{yeetBrackets(nextPage.name)} &rarr;
{removeBrackets(nextPage.name)} &rarr;
</FormattedLink>}
</div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion pages/guides/index.tsx
Expand Up @@ -2,7 +2,8 @@ import { GetStaticPropsContext, GetStaticPropsResult } from "next"
import Head from "next/head"
import FormattedLink from "../../components/FormattedLink"
import Main from "../../components/Main"
import { getGuides, urlify } from "../../utils/data-cache"
import { getGuides } from "../../utils/data-cache"
import { urlify } from "../../utils/utils"

interface Props {
guides: string[]
Expand Down
9 changes: 0 additions & 9 deletions utils/data-cache.ts
Expand Up @@ -38,15 +38,6 @@ const cached: Cache = {
time: 0
}
}
export function urlify(input: string, shouldYeetBrackets: boolean): string {
if (shouldYeetBrackets)
input = yeetBrackets(input)
return input.toLowerCase().replace(/\(|\)|:/g, "").trim().replace(/ +/g, "-")
}

export function yeetBrackets(input: string) {
return input.replace(/\(.*\)/g, "").replace(/ +:/, ":")
}

export const getGuides: (() => Promise<Guides | undefined>) = createGetCacheable("guides")
export const getCharacters: (() => Promise<Characters | undefined>) = createGetCacheable("characters", "gamedata/characters")
Expand Down
33 changes: 19 additions & 14 deletions utils/types.ts
Expand Up @@ -20,20 +20,6 @@ export interface Reminder {
}


// Guides
export interface Guide {
name: string
pages: GuidePage[]
}
export interface GuidePage {
name: string
img?: string
desc?: string
url?: string
credits: string
}


// Character
export type Character = CharacterFull | CharacterPlaceholder
export interface CharacterPlaceholder {
Expand Down Expand Up @@ -194,3 +180,22 @@ export enum WeaponType {
Polearm = "Polearm",
Sword = "Sword",
}


// Guides
export interface Guide {
name: string
pages: GuidePage[]
}
export interface GuidePage {
name: string
img?: string
desc?: string
url?: string
credits: string
links?: {
material?: string[]
enemy?: string[]
character?: string[]
}
}
26 changes: 25 additions & 1 deletion utils/utils.ts
Expand Up @@ -10,7 +10,8 @@ import Catalyst from "../public/img/weapon_types/Catalyst.png"
import Claymore from "../public/img/weapon_types/Claymore.png"
import Polearm from "../public/img/weapon_types/Polearm.png"
import Sword from "../public/img/weapon_types/Sword.png"
import { Character, CharacterFull, Cost, CostTemplate, CurveEnum, WeaponType } from "./types"
import { getGuides } from "./data-cache"
import { Character, CharacterFull, Cost, CostTemplate, CurveEnum, Guide, GuidePage, WeaponType } from "./types"

export const elements = {
Pyro, Electro, Cryo, Hydro, Anemo, Geo, Dendro
Expand Down Expand Up @@ -97,3 +98,26 @@ export function getCostsFromTemplate(costTemplate: CostTemplate, costTemplates:
}))
}))
}

export async function getGuidesFor(type: "enemy" | "character" | "material", name: string): Promise<{ guide: Guide, page: GuidePage }[]> {
return (await getGuides())?.flatMap(guide => guide.pages
.filter(page => page.links?.[type]?.includes(name))
.map(page => ({
guide, page
}))
) ?? []
}

export function getLinkToGuide(guide: Guide, page: GuidePage): string {
return `/guides/${urlify(guide.name, false)}/${urlify(page.name, true)}`
}

export function urlify(input: string, shouldYeetBrackets: boolean): string {
if (shouldYeetBrackets)
input = removeBrackets(input)
return input.toLowerCase().replace(/\(|\)|:/g, "").trim().replace(/ +/g, "-")
}

export function removeBrackets(input: string) {
return input.replace(/\(.*\)/g, "").replace(/ +:/, ":")
}

0 comments on commit ba4f869

Please sign in to comment.