mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-30 15:17:43 -05:00
Migrate /vods to CSS modules
This commit is contained in:
@@ -1,15 +1,13 @@
|
||||
import { useLoaderData, useMatches } from "@remix-run/react";
|
||||
import { AddNewButton } from "~/components/AddNewButton";
|
||||
import { VodListing } from "~/features/vods/components/VodListing";
|
||||
import styles from "~/features/vods/routes/vods.module.css";
|
||||
import invariant from "~/utils/invariant";
|
||||
import type { SendouRouteHandle } from "~/utils/remix.server";
|
||||
import { newVodPage } from "~/utils/urls";
|
||||
|
||||
import { loader } from "../loaders/u.$identifier.vods.server";
|
||||
export { loader };
|
||||
|
||||
import "~/features/vods/vods.css";
|
||||
|
||||
export const handle: SendouRouteHandle = {
|
||||
i18n: ["vods"],
|
||||
};
|
||||
@@ -24,7 +22,7 @@ export default function UserVodsPage() {
|
||||
<div className="stack items-end">
|
||||
<AddNewButton navIcon="vods" to={newVodPage()} />
|
||||
</div>
|
||||
<div className="vods__listing__list">
|
||||
<div className={styles.listingList}>
|
||||
{data.vods.map((vod) => (
|
||||
<VodListing key={vod.id} vod={vod} showUser={false} />
|
||||
))}
|
||||
|
||||
34
app/features/vods/components/VodListing.module.css
Normal file
34
app/features/vods/components/VodListing.module.css
Normal file
@@ -0,0 +1,34 @@
|
||||
.listing {
|
||||
max-width: 355px;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
aspect-ratio: 16 / 9;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
min-width: 355px;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.castText {
|
||||
font-size: var(--fonts-sm);
|
||||
font-weight: var(--semi-bold);
|
||||
color: var(--text-lighter);
|
||||
}
|
||||
|
||||
.weaponsNotShown {
|
||||
background-color: var(--bg-lighter);
|
||||
border-radius: 100%;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-weight: var(--semi-bold);
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
font-size: var(--fonts-sm);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { WeaponImage } from "~/components/Image";
|
||||
import { vodVideoPage } from "~/utils/urls";
|
||||
import type { ListVod } from "../vods-types";
|
||||
import styles from "./VodListing.module.css";
|
||||
import { PovUser } from "./VodPov";
|
||||
|
||||
export function VodListing({
|
||||
@@ -15,20 +16,18 @@ export function VodListing({
|
||||
const { t } = useTranslation(["vods"]);
|
||||
|
||||
return (
|
||||
<div className="vods__listing">
|
||||
<div className={styles.listing}>
|
||||
<Link to={vodVideoPage(vod.id)} className="stack sm">
|
||||
<img
|
||||
className="vods__listing__thumbnail"
|
||||
className={styles.thumbnail}
|
||||
alt=""
|
||||
src={youtubeIdToThumbnailUrl(vod.youtubeId)}
|
||||
/>
|
||||
<h2 className="text-sm text-main-forced">{vod.title}</h2>
|
||||
</Link>
|
||||
<div className="vods__listing__info">
|
||||
<div className={styles.info}>
|
||||
{vod.type === "CAST" || !showUser ? (
|
||||
<div className="vods__listing__cast-text">
|
||||
{t(`vods:type.${vod.type}`)}
|
||||
</div>
|
||||
<div className={styles.castText}>{t(`vods:type.${vod.type}`)}</div>
|
||||
) : (
|
||||
<PovUser pov={vod.pov} />
|
||||
)}
|
||||
@@ -58,9 +57,7 @@ function WeaponsPeek({ weapons }: { weapons: ListVod["weapons"] }) {
|
||||
/>
|
||||
))}
|
||||
{amountOfWeaponsNotShown > 0 ? (
|
||||
<div className="vods__listing__weapons-not-shown">
|
||||
+{amountOfWeaponsNotShown}
|
||||
</div>
|
||||
<div className={styles.weaponsNotShown}>+{amountOfWeaponsNotShown}</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
|
||||
46
app/features/vods/routes/vods.$id.module.css
Normal file
46
app/features/vods/routes/vods.$id.module.css
Normal file
@@ -0,0 +1,46 @@
|
||||
.matches {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
flex-wrap: wrap;
|
||||
column-gap: var(--s-4);
|
||||
row-gap: var(--s-8);
|
||||
justify-content: space-between;
|
||||
margin-block-start: var(--s-2);
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.match {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
flex-direction: column;
|
||||
font-size: var(--fonts-xs);
|
||||
line-height: 1.3;
|
||||
font-weight: var(--semi-bold);
|
||||
flex-grow: 1;
|
||||
width: max-content;
|
||||
gap: var(--s-1);
|
||||
}
|
||||
|
||||
.matchWeapon {
|
||||
position: absolute;
|
||||
top: -15px;
|
||||
left: -20px;
|
||||
}
|
||||
|
||||
.matchWeapons {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
margin-block-end: var(--s-2);
|
||||
}
|
||||
|
||||
.matchMode {
|
||||
position: absolute;
|
||||
top: -15px;
|
||||
right: -15px;
|
||||
}
|
||||
|
||||
.matchMode.cast {
|
||||
right: -8px;
|
||||
}
|
||||
@@ -31,10 +31,9 @@ import { PovUser } from "../components/VodPov";
|
||||
import { loader } from "../loaders/vods.$id.server";
|
||||
import type { Vod } from "../vods-types";
|
||||
import { canEditVideo, secondsToHoursMinutesSecondString } from "../vods-utils";
|
||||
import styles from "./vods.$id.module.css";
|
||||
export { loader, action };
|
||||
|
||||
import "../vods.css";
|
||||
|
||||
export const handle: SendouRouteHandle = {
|
||||
breadcrumb: ({ match }) => {
|
||||
const data = match.data as SerializeFrom<typeof loader> | undefined;
|
||||
@@ -143,7 +142,7 @@ export default function VodPage() {
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="vods__matches">
|
||||
<div className={styles.matches}>
|
||||
{data.vod.matches.map((match) => (
|
||||
<Match
|
||||
key={match.id}
|
||||
@@ -173,7 +172,7 @@ function Match({
|
||||
const weapons = match.weapons.length === 8 ? match.weapons : null;
|
||||
|
||||
return (
|
||||
<div className="vods__match">
|
||||
<div className={styles.match}>
|
||||
<Image
|
||||
alt=""
|
||||
path={stageImageUrl(match.stageId)}
|
||||
@@ -185,20 +184,20 @@ function Match({
|
||||
weaponSplId={weapon}
|
||||
variant="badge"
|
||||
width={42}
|
||||
className="vods__match__weapon"
|
||||
className={styles.matchWeapon}
|
||||
testId={`weapon-img-${weapon}`}
|
||||
/>
|
||||
) : null}
|
||||
<Image
|
||||
path={modeImageUrl(match.mode)}
|
||||
width={32}
|
||||
className={clsx("vods__match__mode", { cast: Boolean(weapons) })}
|
||||
className={clsx(styles.matchMode, { [styles.cast]: Boolean(weapons) })}
|
||||
alt={t(`game-misc:MODE_LONG_${match.mode}`)}
|
||||
title={t(`game-misc:MODE_LONG_${match.mode}`)}
|
||||
/>
|
||||
{weapons ? (
|
||||
<div className="stack horizontal md">
|
||||
<div className="vods__match__weapons">
|
||||
<div className={styles.matchWeapons}>
|
||||
{weapons.slice(0, 4).map((weapon, i) => {
|
||||
return (
|
||||
<WeaponImage
|
||||
@@ -211,7 +210,7 @@ function Match({
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="vods__match__weapons">
|
||||
<div className={styles.matchWeapons}>
|
||||
{weapons.slice(4).map((weapon, i) => {
|
||||
const adjustedI = i + 4;
|
||||
return (
|
||||
|
||||
10
app/features/vods/routes/vods.module.css
Normal file
10
app/features/vods/routes/vods.module.css
Normal file
@@ -0,0 +1,10 @@
|
||||
.listingList {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--s-6);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.typeSelect {
|
||||
width: 220px;
|
||||
}
|
||||
@@ -15,10 +15,9 @@ import { navIconUrl, newVodPage, VODS_PAGE } from "~/utils/urls";
|
||||
import { VodListing } from "../components/VodListing";
|
||||
import { loader } from "../loaders/vods.server";
|
||||
import { VODS_PAGE_BATCH_SIZE, videoMatchTypes } from "../vods-constants";
|
||||
import styles from "./vods.module.css";
|
||||
export { loader };
|
||||
|
||||
import "../vods.css";
|
||||
|
||||
export const handle: SendouRouteHandle = {
|
||||
i18n: ["vods"],
|
||||
breadcrumb: () => ({
|
||||
@@ -58,7 +57,7 @@ export default function VodsSearchPage() {
|
||||
</div>
|
||||
{data.vods.length > 0 ? (
|
||||
<>
|
||||
<div className="vods__listing__list">
|
||||
<div className={styles.listingList}>
|
||||
{data.vods.map((vod) => (
|
||||
<VodListing key={vod.id} vod={vod} />
|
||||
))}
|
||||
@@ -156,7 +155,7 @@ function Filters({
|
||||
<Label>{t("vods:forms.title.type")}</Label>
|
||||
<select
|
||||
name="type"
|
||||
className="vods__type-select"
|
||||
className={styles.typeSelect}
|
||||
value={type ?? ""}
|
||||
onChange={(e) => addToSearchParams("type", e.target.value)}
|
||||
>
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
.vods__matches {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
flex-wrap: wrap;
|
||||
column-gap: var(--s-4);
|
||||
row-gap: var(--s-8);
|
||||
justify-content: space-between;
|
||||
margin-block-start: var(--s-2);
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.vods__match {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
flex-direction: column;
|
||||
font-size: var(--fonts-xs);
|
||||
line-height: 1.3;
|
||||
font-weight: var(--semi-bold);
|
||||
flex-grow: 1;
|
||||
width: max-content;
|
||||
gap: var(--s-1);
|
||||
}
|
||||
|
||||
.vods__match__weapon {
|
||||
position: absolute;
|
||||
top: -15px;
|
||||
left: -20px;
|
||||
}
|
||||
|
||||
.vods__match__weapons {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
margin-block-end: var(--s-2);
|
||||
}
|
||||
|
||||
.vods__match__mode {
|
||||
position: absolute;
|
||||
top: -15px;
|
||||
right: -15px;
|
||||
}
|
||||
|
||||
.vods__match__mode.cast {
|
||||
right: -8px;
|
||||
}
|
||||
|
||||
.vods__listing__list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--s-6);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.vods__listing {
|
||||
max-width: 355px;
|
||||
}
|
||||
|
||||
.vods__listing__thumbnail {
|
||||
aspect-ratio: 16 / 9;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
min-width: 355px;
|
||||
}
|
||||
|
||||
.vods__listing__info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.vods__listing__cast-text {
|
||||
font-size: var(--fonts-sm);
|
||||
font-weight: var(--semi-bold);
|
||||
color: var(--text-lighter);
|
||||
}
|
||||
|
||||
.vods__listing__weapons-not-shown {
|
||||
background-color: var(--bg-lighter);
|
||||
border-radius: 100%;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-weight: var(--semi-bold);
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
font-size: var(--fonts-sm);
|
||||
}
|
||||
|
||||
.vods__type-select {
|
||||
width: 220px;
|
||||
}
|
||||
Reference in New Issue
Block a user