Major rewrite for 0.1.6 - Dynamic database entry generation based on json files, dynamic run page generation and more

This commit is contained in:
Firq 2024-01-02 23:19:14 +01:00
parent e75a575417
commit 7fe9e8c25f
Signed by: Firq
GPG key ID: 3ACC61C8CEC83C20
28 changed files with 2954 additions and 45 deletions

BIN
src/assets/embed.png Normal file

Binary file not shown.

After

(image error) Size: 7 KiB

58
src/assets/logo.svg Normal file
View file

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="110.08278mm"
height="101.0897mm"
viewBox="0 0 110.08278 101.0897"
version="1.1"
id="svg5"
xml:space="preserve"
inkscape:export-filename="colored_logo.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
sodipodi:docname="logo_2.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview7"
pagecolor="#000000"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.76880612"
inkscape:cx="165.84155"
inkscape:cy="226.32494"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="g429" /><defs
id="defs2" /><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(160.03878,-49.932608)"><g
id="g429"
transform="translate(-210.61878,-24.090382)"
inkscape:export-filename="g429.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
inkscape:groupmode="layer"><path
style="fill:#ffffff;stroke-width:0.107"
d="m 87.179998,136.57997 v 23.84826 l 13.810092,-13.81009 v -9.79482 h 16.73028 l 12.50209,-12.50209 h -29.23237 v -9.94692 h 35.71155 l 12.16748,-12.16747 H 87.179998 v 22.14481 H 67.955379 Z"
id="path302" /><path
style="fill:#b86cff;fill-opacity:1;stroke-width:0.107"
d="m 74.013398,131.76579 2.989789,1.97885 c 0,0 -24.434533,22.32662 -12.819526,37.81329 l 1.419613,-5.97957 c 0,0 15.830822,6.4958 44.094006,-10.36747 0,0 -0.6883,-12.26028 10.88369,-6.71089 0,0 13.20669,-8.08749 25.25188,-21.76738 -0.25811,-0.34415 -3.52752,-5.42034 -3.52752,-5.42034 l 0.086,-3.78563 5.3343,7.05504 c 0,0 23.23001,-28.048086 -8.77579,-24.09038 l -3.26936,-1.290557 c 0,0 39.2329,-8.861819 13.59385,27.703937 0,0 17.12138,25.89716 9.37805,39.6631 -7.74334,13.76593 -36.13558,-6.88297 -36.47973,-8.25956 -0.34414,-1.3766 24.95076,16.51912 33.72654,5.85052 8.77578,-10.6686 -9.80823,-37.51217 -8.60371,-35.10313 0,0 -13.07764,14.79838 -25.2949,22.02549 0,0 1.54867,10.75464 -10.06634,6.79693 0,0 -43.104576,26.15527 -59.19351,14.02405 0,0 -12.174247,-12.04519 21.272668,-40.1363 z"
id="path358" /><path
style="fill:#b86cff;fill-opacity:1;stroke-width:0.107"
d="m 84.058227,118.30098 0.08604,-9.37804 c 0,0 -5.420336,-10.410486 -1.892816,-20.56286 -0.94641,-0.344147 -6.280709,0.602261 -6.280709,0.602261 0,0 15.400637,-16.51912 45.083428,10.152375 0.43019,0.172074 5.59241,0.08604 5.59241,0.08604 0,0 -28.134123,-30.543163 -48.524911,-24.348493 0,0 -17.293454,5.850508 5.936558,43.448717 z"
id="path360" /></g></g></svg>

After

(image error) Size: 3.2 KiB

Binary file not shown.

After

(image error) Size: 31 KiB

View file

@ -1,4 +1,7 @@
---
import packagejson from '../../package.json'
const version = packagejson.version
const release = `https://forgejo.neshweb.net/Firq/fgo-ta-com-website/releases/tag/${version}`
---
<div>
@ -9,6 +12,8 @@
game in general.
<br />
<a href="https://firq.dev" target="_blank" rel="noopener noreferrer">Feel free to check out my own site.</a>
<br />
<span class="version">(Website version: <a href={release} target="_blank" rel="noopener noreferrer">{version}</a>)</span>
</span>
<slot />
</div>
@ -30,5 +35,10 @@
a {
text-align: center;
text-decoration: none;
color: var(--c-darkpurple);
}
.version {
font-size: 0.7em;
}
</style>

View file

@ -1,9 +1,13 @@
---
import { Image } from 'astro:assets';
import logo from '../assets/logo.svg'
import hamburger from 'iconoir/icons/menu.svg'
const hamburger_src_url = `url("${hamburger.src}")`;
---
<header>
<a href="/" rel="noopener noreferrer" aria-label="Home">
<img src="/assets/logo.svg" alt="" />
<Image src={logo} alt="Website Logo"/>
</a>
<ul class="desktop">
<slot />
@ -13,11 +17,11 @@
<slot />
</ul>
<div class="placeholder"></div>
<i class="iconoir-menu"></i>
<div class="hamburger-menu"></div>
</button>
</header>
<style>
<style define:vars={{ hamburger_src_url }}>
header {
z-index: 1000;
position: sticky;
@ -79,16 +83,6 @@
height: 64px;
}
.mobile > i {
position: static;
color: white;
font-weight: bold;
font-size: 2em;
align-self: flex-start;
padding-right: 1em;
padding-top: 1.15rem;
}
.mobile > ul {
display: none;
padding: 0px;
@ -111,6 +105,17 @@
justify-self: top;
}
.hamburger-menu {
mask: var(--hamburger_src_url) no-repeat center;
background-color: white;
width: 2em;
height: 2em;
position: static;
align-self: flex-start;
padding-right: 1em;
padding-top: 2.5em;
}
@media (min-width: 1140px) {
.mobile {
display: none;

View file

@ -3,7 +3,7 @@ export interface Props {
currentPage?: string
link: string
text: string
icon: string
icon: ImageMetadata
}
const { icon, text, link, currentPage } = Astro.props
@ -17,6 +17,7 @@ if (currentPage === slug) {
currPage = 'current'
}
const icon_src_url = `url("${icon.src}")`;
const fulllink = `/${slug}`
---
@ -28,12 +29,12 @@ const fulllink = `/${slug}`
class={currPage}
tabindex="0"
>
<i class={icon}></i>
<div class="icon"></div>
{text}
</a>
</li>
<style>
<style define:vars={{ icon_src_url }}>
li {
align-items: center;
justify-content: center;
@ -41,7 +42,9 @@ const fulllink = `/${slug}`
display: flex;
width: 200px;
}
li > a {
display: inline-flex;
color: white;
text-decoration: none;
justify-content: center;
@ -49,11 +52,29 @@ const fulllink = `/${slug}`
font-size: 1.4em;
height: 100%;
font-weight: bold;
gap: 0.2em;
}
li > a:hover {
color: var(--c-purplepink);
}
li > a:hover > .icon {
background-color: var(--c-purplepink);
}
.current {
color: var(--c-darkpurple);
color: var(--c-darkpurple) !important;
}
.current > .icon {
background-color: var(--c-darkpurple) !important;
}
.icon {
mask: var(--icon_src_url) no-repeat center;
background-color: white;
width: 1.4em;
height: 1.4em;
}
</style>

View file

@ -0,0 +1,103 @@
---
export interface Props {
url: string | undefined
title: string
questReleaseDate: string
shortdescription: string
}
const options_date: Intl.DateTimeFormatOptions = {
year: 'numeric',
month: 'long',
day: '2-digit',
}
const { shortdescription, questReleaseDate, url, title } = Astro.props
const render_date = new Date(questReleaseDate).toLocaleDateString('en-GB', options_date)
---
<a href={url} rel="noopener noreferrer">
<div class="circle"></div>
<article>
<h2>{title}</h2>
<h3>{render_date}</h3>
<p>{shortdescription}</p>
</article>
</a>
<style>
.circle {
display: none;
}
@media (min-width: 900px) {
.circle {
margin: 1rem 1rem 1rem 0rem;
position: relative;
display: flex;
visibility: visible;
height: 1.5rem;
width: 1.5rem;
border-radius: 40%;
background-color: var(--c-darkpurple);
transition: transform var(--speed) var(--ease);
}
a:hover > .circle {
height: 1.75rem;
width: 1.75rem;
translate: -0.125rem;
margin-right: 0.825rem;
}
article {
margin-left: 0.5rem;
}
}
a {
align-items: center;
justify-content: center;
display: flex;
text-decoration: none;
height: auto;
margin: 0.5rem;
width: 100%;
}
p {
color: white;
text-align: left;
font-size: 1.1em;
margin: 0.5em;
}
article > h2 {
margin: 0.3rem 0.5rem;
color: var(--c-darkpurple);
font-size: 1.5rem;
line-height: normal;
text-decoration: none;
}
article > h3 {
margin: 0.2em 0.5rem;
color: white;
font-size: 1rem;
line-height: normal;
text-decoration: none;
}
article {
display: flex;
flex: 1;
flex-wrap: wrap;
flex-direction: column;
align-items: flex-start;
align-content: flex-start;
justify-content: center;
background-color: var(--c-darkergray);
padding: 10px;
text-align: center;
transition: transform var(--speed) var(--ease);
min-height: 100%;
border-radius: 1.25rem;
}
a:hover > article {
transform: scaleY(102.5%) scaleX(101%);
}
</style>

184
src/components/taCard.astro Normal file
View file

@ -0,0 +1,184 @@
---
import type { ImageMetadata } from 'astro'
import { Image } from 'astro:assets'
export interface Props {
title: string,
link: string,
date: string,
servant: string,
turns: string,
runner: string
}
const { turns, runner, date, servant, link, title } =
Astro.props
const options_date: Intl.DateTimeFormatOptions = {
year: 'numeric',
month: '2-digit',
day: '2-digit',
}
const servantImagePath = `/src/assets/ta_servants/${servant}.png`
const formatted_date = new Date(date).toLocaleDateString('de-DE', options_date)
const servant_images = import.meta.glob<{ default: ImageMetadata }>(
'/src/assets/ta_servants/*.png'
)
---
<a href={link} target="_blank" rel="noopener noreferrer" aria-label={title}>
<article>
<Image src={servant_images[servantImagePath]()} alt="" class="icon"/>
<div class="title">
<h2>{title}</h2>
</div>
<p>
<span>
By {runner}<br /> •
</span>
{formatted_date}
</p>
<div class="expand-on-hover">
<h2>{turns}</h2>
</div>
</article>
</a>
<style>
div {
display: none;
}
span {
display: none;
}
span {
display: flex;
}
a {
text-decoration: none;
}
article {
background-color: var(--c-darkergray);
border-color: var(--c-darkgray);
padding: 10px;
text-align: center;
transition: transform var(--speed) var(--ease);
height: auto;
width: auto;
max-width: 8rem;
border-radius: 1.25rem;
padding-bottom: 1.5rem;
--size-value: 7rem;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
article:hover {
transform: scale(var(--hover-scale));
}
article > .icon {
border-radius: 1.25rem;
width: var(--size-value);
height: var(--size-value);
margin: 0.5rem;
}
article:hover .title {
display: flex;
position: absolute;
align-items: center;
justify-content: center;
text-align: center;
background-color: var(--c-darkgray);
height: calc(var(--size-value) + 0.1rem);
width: calc(var(--size-value) + 0.1rem);
opacity: 90%;
border-radius: 1.25rem;
top: 1.1em;
}
article:hover .title h2 {
margin: 0;
display: inline-flex;
font-weight: bold;
color: white;
font-size: 18px;
line-height: 150%;
padding: 0.5rem;
}
article .title h2 {
display: none;
}
article .title {
display: none;
}
.icon {
display: flex;
justify-content: center;
align-items: center;
}
p {
display: flex;
text-align: center;
justify-content: center;
align-items: center;
line-height: 100%;
text-decoration: none;
color: white;
font-size: 1rem;
font-weight: bold;
padding-top: 0.5rem;
margin: 0.5rem 0px;
flex-wrap: wrap;
flex-direction: column;
}
.expand-on-hover {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
justify-content: space-evenly;
background-color: var(--c-duskgray);
z-index: 99;
transform: scaleY(0);
transform-origin: top;
position: absolute;
top: 90%;
left: 0px;
right: 0px;
color: white;
border-radius: 0px 0px 1.25rem 1.25rem;
}
.expand-on-hover img {
width: 3rem;
height: 3rem;
margin-top: 0.5rem;
margin-bottom: 0.5rem;
border-radius: 0.5rem;
}
.expand-on-hover h2 {
margin: 0.5rem;
}
article:hover .expand-on-hover {
transform: scaleY(1);
transition: transform 200ms ease-in-out;
background-color: var(--c-duskgray);
}
</style>

View file

@ -0,0 +1,26 @@
{
"info": {
"title": "Cernunnos",
"questReleaseDate": "2023-07-10",
"shortdescription": "One of FGOs most notorious boss fights due to up to 100% special defense, strong DoT damage and powerful field effects",
"description": "One of FGOs most notorious boss fights due to up to 100% special defense, strong DoT damage and powerful field effects - and still, the TA community prevailed and created some of the most amazing runs of all time"
},
"data": [
{
"title": "Cernunnos 4T (No Castoria)",
"link": "https://www.youtube.com/watch?v=WrHudtdfivA",
"date": "2023-07-19",
"servant": "shishou",
"turns": "4T",
"runner": "Firq"
},
{
"title": "Cernunnos 4T (FLO)",
"link": "https://www.youtube.com/watch?O1f-go7uJQM",
"date": "2023-07-19",
"servant": "shishou",
"turns": "4T",
"runner": "Requiem"
}
]
}

View file

@ -1,7 +1,11 @@
---
import Navbar from '../components/navbar.astro'
import NavbarEntry from '../components/navbarEntry.astro'
import navdata from '../../static/assets/data/_navdata.json'
import navdata from '../../static/data/_navdata.json'
import embed from '../assets/embed.png'
import home from 'iconoir/icons/home.svg'
import database from 'iconoir/icons/database.svg'
import type { IconsLookup } from '../types/icons'
export interface Props {
title: string
@ -9,6 +13,11 @@ export interface Props {
descriptionOverride?: string
}
const icons: IconsLookup = {
home: home,
database: database
}
const { descriptionOverride, currentpage, title } = Astro.props
let description
@ -22,6 +31,11 @@ let currPage = 'https://fgo-ta.com/'
if (currentpage !== 'home') {
currPage += currentpage
}
const mapped_navdata = navdata.map((item) => ({
...item,
...{ icon: icons[item.icon] },
}))
---
<!DOCTYPE html>
@ -36,23 +50,19 @@ if (currentpage !== 'home') {
<meta property="og:title" content={title} />
<meta property="og:url" content={currPage} />
<meta property="og:description" content={description} />
<meta property="og:image" content="/assets/embed.png" />
<meta property="og:image" content={embed.src} />
<meta property="og:type" content="website" />
<meta property="og:locale" content="en_US" />
<meta name="theme-color" content="#b86cff" />
<!-- Links -->
<link rel="icon" type="image/ico" href="/assets/favicon.ico" />
<link rel="icon" type="image/ico" href="/favicon.ico" />
<link rel="sitemap" href="/sitemap-index.xml" />
<link href="https://mastodon.neshweb.net/@Firq" rel="me" />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/iconoir-icons/iconoir@main/css/iconoir.css"
/>
</head>
<body>
<Navbar>
{
navdata.map((item) => (
mapped_navdata.map((item) => (
<NavbarEntry currentPage={currentpage} {...item} />
))
}

View file

@ -1,13 +1,15 @@
---
export interface Props {
title: string
description: string
}
const { title } = Astro.props
const { title, description } = Astro.props
---
<div class="base">
<h1>{title}</h1>
<h2>{description}</h2>
<div>
<slot />
</div>
@ -45,6 +47,16 @@ const { title } = Astro.props
background-color: var(--c-darkgray);
padding-bottom: 0.5rem;
}
div h2 {
color: white;
font-size: 16px;
font-weight: 600;
max-width: 75;
margin: 1rem;
line-height: 20px;
text-align: center;
}
@media (min-width: 512px) {
div {
row-gap: 1.5em;
@ -64,10 +76,18 @@ const { title } = Astro.props
}
}
@media (min-width: 1500px) {
@media (min-width: 1140px) {
.base {
margin-left: 10%;
margin-right: 10%;
}
div h1 {
margin-left: unset;
margin-right: unset;
}
div h2 {
text-align: left;
}
}
</style>

View file

@ -0,0 +1,100 @@
---
export interface Props {
title: string
}
const { title } = Astro.props
---
<section>
<h1>{title}</h1>
<div class="wrapper">
<div class="line"></div>
<slot />
<div class="drop"></div>
</div>
</section>
<style>
.drop {
display: flex;
position: absolute;
visibility: visible;
left: 0;
right: 0;
bottom: -5rem;
margin-left: auto;
margin-right: auto;
height: 1.5rem;
width: 1.5rem;
border-radius: 0% 50% 50% 50%;
transform: rotate(45deg);
background-color: var(--c-darkpurple);
}
.line {
display: flex;
position: absolute;
visibility: visible;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
background-color: var(--c-darkpurple);
height: calc(100% + 5rem);
translate: 0% 2rem;
width: 0.25rem;
z-index: -1;
}
h1 {
font-size: 40px;
line-height: 48px;
letter-spacing: -1px;
color: white;
font-size: 2.25rem;
margin-top: 1rem;
margin-bottom: 0;
margin-left: auto;
margin-right: auto;
padding: 0.25rem 0.75rem;
max-width: max-content;
background-color: var(--c-darkgray);
padding: 0.25rem 1.5rem;
border-radius: 0.5rem;
padding-bottom: 0.5rem;
}
.wrapper {
margin: 2rem 3rem 0.5rem 3rem;
display: flex;
flex-flow: column wrap;
row-gap: 1em;
column-gap: 1em;
align-self: center;
align-items: stretch;
justify-content: space-around;
padding: 1em;
color: white;
font-size: 1em;
position: relative;
}
@media (min-width: 900px) {
.drop {
margin-left: 1.5rem;
}
.line {
margin-left: 2.1rem;
}
h1 {
margin-left: 3rem;
}
}
@media (min-width: 1500px) {
.wrapper {
margin-left: 20rem;
margin-right: 20rem;
}
h1 {
margin-left: 20rem;
margin-right: 20rem;
}
}
</style>

View file

@ -0,0 +1,52 @@
---
import Layout from '../layouts/Layout.astro'
import BaseSection from '../layouts/baseSection.astro'
import TACard from '../components/taCard.astro'
import type { filedata } from '../types/ta'
export interface Props {
datafile: string
}
const { datafile } = Astro.props
const fulldata = import.meta.glob<{ default: any }>(
`../content/data/*.json`
)
const filecontent: filedata = (
await fulldata[`../content/data/${datafile}.json`]()
)['default']
const title = filecontent.info.title
---
<Layout
title={title}
currentpage="database-entry"
descriptionOverride={filecontent.info.shortdescription}
>
<a href="/database">&lt&lt Back to database</a>
<BaseSection title={title} description={filecontent.info.description}>
{filecontent.data.map((item) => <TACard {...item} />)}
</BaseSection>
<div class="placeholder"></div>
</Layout>
<style>
.placeholder {
visibility: hidden;
width: 100%;
height: 2.5rem;
}
a {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
text-align: center;
color: white;
background-color: var(--c-gray);
padding: 0.5rem 0px;
text-decoration: none;
}
</style>

37
src/pages/database.astro Normal file
View file

@ -0,0 +1,37 @@
---
import Layout from '../layouts/Layout.astro'
import QuestListing from '../components/questListing.astro'
import DatabaseSection from '../layouts/databaseSection.astro'
import { findSlug } from '../utils/slugTools'
import type { filedata } from '../types/ta'
const description =
'My own small blog. Topics include FGO, TA, Programming, web technologies and more!'
const questInfo = []
const fulldata = import.meta.glob<{ default: filedata }>(`../content/data/*.json`)
for (const [key, value] of Object.entries(fulldata)) {
const url = `${Astro.url}/${findSlug(key)}`
questInfo.push({
...(await value())['default'].info,
url: url,
})
}
questInfo.sort(
(a, b) => Date.parse(b.questReleaseDate) - Date.parse(a.questReleaseDate)
)
---
<Layout
title="TA Database"
currentpage="database"
descriptionOverride={description}
>
<DatabaseSection title="FGO NA TA Database">
{questInfo.map((quest) => <QuestListing {...quest} />)}
</DatabaseSection>
</Layout>
<style></style>

View file

@ -0,0 +1,23 @@
---
import TaShowcaseLayout from '../../layouts/taShowcaseLayout.astro'
import {findSlug} from '../../utils/slugTools'
export function getStaticPaths() {
const fulldata = import.meta.glob<{ default: any }>(
`../../content/data/*.json`
)
const keylist = Object.keys(fulldata).map(
(item) => findSlug(item)
)
const paths: { params: { slug: string } }[] = []
for (const key of keylist) {
paths.push({ params: { slug: key! } })
}
return paths
}
const { slug } = Astro.params
---
<TaShowcaseLayout datafile={slug} />

3
src/types/icons.ts Normal file
View file

@ -0,0 +1,3 @@
export interface IconsLookup {
[key: string]: ImageMetadata
}

20
src/types/ta.ts Normal file
View file

@ -0,0 +1,20 @@
interface tadata {
title: string
link: string
servant: string
turns: string
runner: string
date: string
}
interface info {
title: string
questReleaseDate: string
description: string
shortdescription: string
}
export interface filedata {
info: info
data: tadata[]
}

3
src/utils/slugTools.ts Normal file
View file

@ -0,0 +1,3 @@
export function findSlug(filepath: string) {
return filepath.match(/(?:.*[\\/])(.+)(?:\.json)/)?.[1]
}