first pass of new server page
This commit is contained in:
parent
23a1f7708b
commit
990013af2b
9 changed files with 443 additions and 57 deletions
23
app/components/alert-timer.css.ts
Normal file
23
app/components/alert-timer.css.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { keyframes, style } from "@vanilla-extract/css";
|
||||
|
||||
const alertDotBlink = keyframes({
|
||||
from: {
|
||||
backgroundColor: "#ff2d2d",
|
||||
},
|
||||
to: {
|
||||
backgroundColor: "#662929",
|
||||
},
|
||||
});
|
||||
|
||||
export const alertDot = style({
|
||||
display: "inline-block",
|
||||
height: "0.5rem",
|
||||
width: "0.5rem",
|
||||
borderRadius: "50%",
|
||||
background: "#ff2d2d",
|
||||
animation: `${alertDotBlink} var(--speed) ease-in-out infinite alternate`,
|
||||
});
|
||||
|
||||
export const timer = style({
|
||||
fontSize: "0.8rem",
|
||||
});
|
51
app/components/alert-timer.tsx
Normal file
51
app/components/alert-timer.tsx
Normal file
|
@ -0,0 +1,51 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { MetagameWorld } from "~/utils/metagame";
|
||||
import { humanTimeAgo } from "~/utils/strings";
|
||||
import * as styles from "./alert-timer.css";
|
||||
|
||||
const endTime = (alert: Required<MetagameWorld["zones"][0]>["alert"]) => {
|
||||
const alertDurationMins = alert.alert_type !== "sudden_death" ? 90 : 15;
|
||||
return new Date(alert.start_time).getTime() + alertDurationMins * 60 * 1000;
|
||||
};
|
||||
|
||||
const timeLeftString = (alert: MetagameWorld["zones"][0]["alert"]) => {
|
||||
if (alert) {
|
||||
const time = endTime(alert) - Date.now();
|
||||
if (time < 2000) {
|
||||
return <>JUST ENDED</>;
|
||||
}
|
||||
|
||||
const speed = time < 1000 * 60 * 15 ? "1s" : "4s";
|
||||
|
||||
return (
|
||||
<>
|
||||
{humanTimeAgo(time, true).toUpperCase()} LEFT{" "}
|
||||
<div
|
||||
className={styles.alertDot}
|
||||
style={{ "--speed": speed } as any}
|
||||
></div>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
|
||||
export const AlertTimer = ({
|
||||
alert,
|
||||
}: {
|
||||
alert: MetagameWorld["zones"][0]["alert"];
|
||||
}) => {
|
||||
const [timeLeft, setTimeLeft] = useState(timeLeftString(alert));
|
||||
|
||||
useEffect(() => {
|
||||
if (alert) {
|
||||
const interval = setInterval(() => {
|
||||
setTimeLeft(timeLeftString(alert));
|
||||
}, 1000);
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
}, [alert]);
|
||||
|
||||
return <div className={styles.timer}>{timeLeft}</div>;
|
||||
};
|
19
app/components/faction-pie.css.ts
Normal file
19
app/components/faction-pie.css.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { style } from "@vanilla-extract/css";
|
||||
|
||||
export const pieRoot = style({
|
||||
width: "1em",
|
||||
height: "1em",
|
||||
borderRadius: "50%",
|
||||
position: "relative",
|
||||
|
||||
"::after": {
|
||||
content: "''",
|
||||
position: "absolute",
|
||||
top: "var(--inner-margin)",
|
||||
left: "var(--inner-margin)",
|
||||
right: "var(--inner-margin)",
|
||||
bottom: "var(--inner-margin)",
|
||||
borderRadius: "50%",
|
||||
background: "var(--inner-bg)",
|
||||
},
|
||||
});
|
40
app/components/faction-pie.tsx
Normal file
40
app/components/faction-pie.tsx
Normal file
|
@ -0,0 +1,40 @@
|
|||
import type { Population } from "~/utils/saerro";
|
||||
import { pieRoot } from "./faction-pie.css";
|
||||
|
||||
export const FactionPie = ({
|
||||
population,
|
||||
innerMargin,
|
||||
innerBackground,
|
||||
size,
|
||||
}: {
|
||||
population: Population;
|
||||
innerMargin?: number;
|
||||
innerBackground?: string;
|
||||
size?: string;
|
||||
}) => {
|
||||
const { nc, tr, vs } = population;
|
||||
const total = nc + tr + vs;
|
||||
|
||||
const trPct = (tr / total) * 100;
|
||||
const vsPct = (vs / total) * 100;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={pieRoot}
|
||||
style={
|
||||
{
|
||||
fontSize: size || "1em",
|
||||
backgroundImage: `conic-gradient(
|
||||
#d30101 0% ${trPct}%,
|
||||
#991cba ${trPct}% ${trPct + vsPct}%,
|
||||
#1564cc ${trPct + vsPct}% 100%
|
||||
)`,
|
||||
"--inner-margin": innerMargin ? `${innerMargin}px` : "0",
|
||||
"--inner-bg": innerBackground || "none",
|
||||
} as any
|
||||
}
|
||||
>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -14,6 +14,7 @@ import type { MetagameWorld } from "~/utils/metagame";
|
|||
import type { PopulationWorld } from "~/utils/population";
|
||||
import { c } from "~/utils/classes";
|
||||
import { useEffect, useState } from "react";
|
||||
import { AlertTimer } from "./alert-timer";
|
||||
|
||||
export type IndexWorldProps = {
|
||||
metagame: MetagameWorld;
|
||||
|
@ -131,11 +132,6 @@ const JaegerContinent = ({ zone }: { zone: MetagameWorld["zones"][0] }) => {
|
|||
);
|
||||
};
|
||||
|
||||
const endTime = (alert: Required<MetagameWorld["zones"][0]>["alert"]) => {
|
||||
const alertDurationMins = alert.alert_type !== "sudden_death" ? 90 : 15;
|
||||
return new Date(alert.start_time).getTime() + alertDurationMins * 60 * 1000;
|
||||
};
|
||||
|
||||
const Continent = ({ zone }: { zone: MetagameWorld["zones"][0] }) => {
|
||||
const {
|
||||
name,
|
||||
|
@ -171,7 +167,7 @@ const Continent = ({ zone }: { zone: MetagameWorld["zones"][0] }) => {
|
|||
ALERT PROGRESS
|
||||
</div>{" "}
|
||||
<div>
|
||||
<TimeLeft alert={zone.alert} />{" "}
|
||||
<AlertTimer alert={zone.alert} />{" "}
|
||||
</div>
|
||||
</div>
|
||||
<FactionBar population={zone.alert.percentages} />
|
||||
|
@ -182,41 +178,3 @@ const Continent = ({ zone }: { zone: MetagameWorld["zones"][0] }) => {
|
|||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const timeLeftString = (alert: MetagameWorld["zones"][0]["alert"]) => {
|
||||
if (alert) {
|
||||
const time = endTime(alert) - Date.now();
|
||||
if (time < 2000) {
|
||||
return <>JUST ENDED</>;
|
||||
}
|
||||
|
||||
const speed = time < 1000 * 60 * 15 ? "1s" : "4s";
|
||||
|
||||
return (
|
||||
<>
|
||||
{humanTimeAgo(time, true).toUpperCase()} LEFT{" "}
|
||||
<div
|
||||
className={styles.alertDot}
|
||||
style={{ "--speed": speed } as any}
|
||||
></div>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
|
||||
const TimeLeft = ({ alert }: { alert: MetagameWorld["zones"][0]["alert"] }) => {
|
||||
const [timeLeft, setTimeLeft] = useState(timeLeftString(alert));
|
||||
|
||||
useEffect(() => {
|
||||
if (alert) {
|
||||
const interval = setInterval(() => {
|
||||
setTimeLeft(timeLeftString(alert));
|
||||
}, 1000);
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
}, [alert]);
|
||||
|
||||
return <>{timeLeft}</>;
|
||||
};
|
||||
|
|
92
app/components/world.css.ts
Normal file
92
app/components/world.css.ts
Normal file
|
@ -0,0 +1,92 @@
|
|||
import { style } from "@vanilla-extract/css";
|
||||
|
||||
export const headerFont = style({
|
||||
fontFamily: "Unbounded, Impact, monospace",
|
||||
fontWeight: "bold",
|
||||
});
|
||||
|
||||
export const header = style({
|
||||
backgroundColor: "#222",
|
||||
padding: "2em",
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
fontFamily: "Unbounded, Impact, monospace",
|
||||
});
|
||||
|
||||
export const headerName = style({
|
||||
fontSize: "4rem",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flexBasis: "100%",
|
||||
});
|
||||
|
||||
export const headerSub = style({
|
||||
fontSize: "1rem",
|
||||
color: "#ccc",
|
||||
marginLeft: "1em",
|
||||
});
|
||||
|
||||
export const outer = style({
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
minHeight: "100vh",
|
||||
flexDirection: "column",
|
||||
maxWidth: "1920px",
|
||||
});
|
||||
|
||||
export const population = style({
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
width: "100%",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-evenly",
|
||||
});
|
||||
export const populationHead = style({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-evenly",
|
||||
flexBasis: "100%",
|
||||
});
|
||||
|
||||
export const popNumbers = style({
|
||||
display: "flex",
|
||||
justifyContent: "space-evenly",
|
||||
});
|
||||
|
||||
export const popItem = style({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
});
|
||||
|
||||
export const totalPop = style({
|
||||
fontSize: "2rem",
|
||||
display: "block",
|
||||
width: "4em",
|
||||
});
|
||||
|
||||
export const headerConts = style({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-evenly",
|
||||
flexBasis: "100%",
|
||||
});
|
||||
|
||||
export const contChart = style({
|
||||
fontSize: "4rem",
|
||||
});
|
||||
|
||||
export const cont = style({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
flexDirection: "column",
|
||||
});
|
||||
|
||||
export const contSub = style({
|
||||
width: "10rem",
|
||||
fontSize: "0.8rem",
|
||||
textAlign: "center",
|
||||
color: "#ccc",
|
||||
paddingTop: "0.5rem",
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue