mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-06 04:55:15 -05:00
Migrate some builds files to CSS modules
This commit is contained in:
parent
b4bd9aabca
commit
fb51e3c463
34
app/components/AbilitiesSelector.module.css
Normal file
34
app/components/AbilitiesSelector.module.css
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
.container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: var(--s-3);
|
||||
}
|
||||
|
||||
.slots {
|
||||
display: grid;
|
||||
margin: 0 auto;
|
||||
gap: var(--s-2);
|
||||
grid-template-columns: repeat(4, max-content);
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.abilityButtons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: var(--s-1);
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.abilityButton {
|
||||
padding: var(--s-0-5);
|
||||
border-color: var(--abilities-button-bg);
|
||||
border-radius: 50%;
|
||||
background-color: var(--abilities-button-bg);
|
||||
}
|
||||
|
||||
.abilityButton.isDragging {
|
||||
box-shadow: 0 0 100px inset rgb(255 255 255 / 25%);
|
||||
}
|
||||
|
|
@ -7,6 +7,8 @@ import { abilityImageUrl } from "~/utils/urls";
|
|||
import { Ability } from "./Ability";
|
||||
import { Image } from "./Image";
|
||||
|
||||
import styles from "./AbilitiesSelector.module.css";
|
||||
|
||||
interface AbilitiesSelectorProps {
|
||||
selectedAbilities: BuildAbilitiesTupleWithUnknown;
|
||||
onChange: (newAbilities: BuildAbilitiesTupleWithUnknown) => void;
|
||||
|
|
@ -79,8 +81,8 @@ export function AbilitiesSelector({
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="ability-selector__container" data-testid="ability-selector">
|
||||
<div className="ability-selector__slots">
|
||||
<div className={styles.container} data-testid="ability-selector">
|
||||
<div className={styles.slots}>
|
||||
{selectedAbilities.map((row, rowI) =>
|
||||
row.map((ability, abilityI) => (
|
||||
<Ability
|
||||
|
|
@ -99,12 +101,12 @@ export function AbilitiesSelector({
|
|||
)),
|
||||
)}
|
||||
</div>
|
||||
<div className="ability-selector__ability-buttons">
|
||||
<div className={styles.abilityButtons}>
|
||||
{abilities.map((ability) => (
|
||||
<button
|
||||
key={ability.name}
|
||||
className={clsx("ability-selector__ability-button", {
|
||||
"is-dragging": ability.name === draggingAbility?.name,
|
||||
className={clsx(styles.abilityButton, {
|
||||
[styles.isDragging]: ability.name === draggingAbility?.name,
|
||||
})}
|
||||
type="button"
|
||||
onClick={() => onButtonClick(ability)}
|
||||
|
|
|
|||
32
app/components/Ability.module.css
Normal file
32
app/components/Ability.module.css
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
.ability {
|
||||
width: var(--ability-size);
|
||||
height: var(--ability-size);
|
||||
padding: 0;
|
||||
border: 2px solid var(--theme-transparent);
|
||||
border-radius: 50%;
|
||||
border-right: 0;
|
||||
border-bottom: 0;
|
||||
background: var(--bg-ability);
|
||||
background-size: 100%;
|
||||
box-shadow: 0 0 0 1px var(--bg-ability);
|
||||
transform: scale(1);
|
||||
transition: all 0.1s ease;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.isDragTarget {
|
||||
background: var(--abilities-button-bg);
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
.dragStarted:not(.dropAllowed) {
|
||||
filter: grayscale(1);
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.readonly,
|
||||
.readonly:active {
|
||||
cursor: default;
|
||||
transform: none;
|
||||
}
|
||||
|
|
@ -5,6 +5,8 @@ import type { AbilityWithUnknown } from "~/modules/in-game-lists/types";
|
|||
import { abilityImageUrl } from "~/utils/urls";
|
||||
import { Image } from "./Image";
|
||||
|
||||
import styles from "./Ability.module.css";
|
||||
|
||||
const sizeMap = {
|
||||
MAIN: 42,
|
||||
SUB: 32,
|
||||
|
|
@ -56,12 +58,12 @@ export function Ability({
|
|||
return (
|
||||
<AbilityTag
|
||||
className={clsx(
|
||||
"build__ability",
|
||||
styles.ability,
|
||||
{
|
||||
"is-drag-target": isDragTarget,
|
||||
"drag-started": dragStarted,
|
||||
"drop-allowed": dropAllowed,
|
||||
readonly,
|
||||
[styles.isDragTarget]: isDragTarget,
|
||||
[styles.dragStarted]: dragStarted,
|
||||
[styles.dropAllowed]: dropAllowed,
|
||||
[styles.readonly]: readonly,
|
||||
},
|
||||
className,
|
||||
)}
|
||||
|
|
|
|||
126
app/components/BuildCard.module.css
Normal file
126
app/components/BuildCard.module.css
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
.card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: var(--s-3);
|
||||
border-radius: var(--rounded);
|
||||
background-color: var(--bg-lighter);
|
||||
gap: var(--s-3);
|
||||
}
|
||||
|
||||
.private {
|
||||
background-color: var(--bg-lighter-transparent);
|
||||
}
|
||||
|
||||
.privateText {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
font-weight: var(--semi-bold);
|
||||
gap: var(--s-1);
|
||||
}
|
||||
|
||||
.privateIcon {
|
||||
width: 16px;
|
||||
margin-block-end: var(--s-1);
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
.title {
|
||||
overflow: hidden;
|
||||
height: 2.5rem;
|
||||
font-size: var(--fonts-sm);
|
||||
line-height: 1.25;
|
||||
word-wrap: break-all;
|
||||
}
|
||||
|
||||
.topRow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.dateAuthorRow {
|
||||
display: flex;
|
||||
font-size: var(--fonts-xxs);
|
||||
gap: var(--s-1);
|
||||
}
|
||||
|
||||
.ownerLink {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.modes {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
display: flex;
|
||||
min-width: max-content;
|
||||
margin-top: -21px;
|
||||
gap: var(--s-1);
|
||||
}
|
||||
|
||||
.weapon {
|
||||
position: relative;
|
||||
padding: var(--s-0-5);
|
||||
border-radius: 50%;
|
||||
background-color: var(--bg-darker-very-transparent);
|
||||
}
|
||||
|
||||
.top500 {
|
||||
position: absolute;
|
||||
top: -14px;
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
.weaponText {
|
||||
padding-left: var(--s-1);
|
||||
color: var(--text-lighter);
|
||||
font-size: var(--fonts-xxs);
|
||||
font-weight: var(--semi-bold);
|
||||
}
|
||||
|
||||
.weapons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--s-1);
|
||||
}
|
||||
|
||||
.gearAbilities {
|
||||
display: grid;
|
||||
gap: var(--s-2) var(--s-1);
|
||||
grid-template-columns: repeat(5, max-content);
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.noGear {
|
||||
grid-template-columns: repeat(4, max-content);
|
||||
margin: 0 auto;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 1rem;
|
||||
row-gap: 28px;
|
||||
}
|
||||
|
||||
.gear {
|
||||
border-radius: 50%;
|
||||
background-color: var(--bg-darker-very-transparent);
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.bottomRow {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
gap: var(--s-4);
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 1.2rem;
|
||||
height: 1.2rem;
|
||||
}
|
||||
|
||||
.smallText {
|
||||
font-size: var(--fonts-xxs) !important;
|
||||
}
|
||||
|
|
@ -33,6 +33,8 @@ import { LockIcon } from "./icons/Lock";
|
|||
import { SpeechBubbleIcon } from "./icons/SpeechBubble";
|
||||
import { TrashIcon } from "./icons/Trash";
|
||||
|
||||
import styles from "./BuildCard.module.css";
|
||||
|
||||
interface BuildProps {
|
||||
build: Pick<
|
||||
Tables["Build"],
|
||||
|
|
@ -92,13 +94,13 @@ export function BuildCard({
|
|||
|
||||
return (
|
||||
<div
|
||||
className={clsx("build", { build__private: build.private })}
|
||||
className={clsx(styles.card, { [styles.private]: build.private })}
|
||||
data-testid="build-card"
|
||||
>
|
||||
<div>
|
||||
<div className="build__top-row">
|
||||
<div className={styles.topRow}>
|
||||
{modes && modes.length > 0 && (
|
||||
<div className="build__modes">
|
||||
<div className={styles.modes}>
|
||||
{modes.map((mode) => (
|
||||
<Image
|
||||
key={mode}
|
||||
|
|
@ -112,17 +114,14 @@ export function BuildCard({
|
|||
))}
|
||||
</div>
|
||||
)}
|
||||
<h2 className="build__title" data-testid="build-title">
|
||||
<h2 className={styles.title} data-testid="build-title">
|
||||
{title}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="build__date-author-row">
|
||||
<div className={styles.dateAuthorRow}>
|
||||
{owner && (
|
||||
<>
|
||||
<Link
|
||||
to={userBuildsPage(owner)}
|
||||
className="build__date-author-row__owner"
|
||||
>
|
||||
<Link to={userBuildsPage(owner)} className={styles.ownerLink}>
|
||||
{owner.username}
|
||||
</Link>
|
||||
<div>•</div>
|
||||
|
|
@ -136,8 +135,8 @@ export function BuildCard({
|
|||
) : null}
|
||||
<div className="stack horizontal sm">
|
||||
{build.private ? (
|
||||
<div className="build__private-text">
|
||||
<LockIcon className="build__private-icon" />{" "}
|
||||
<div className={styles.privateText}>
|
||||
<LockIcon className={styles.privateIcon} />{" "}
|
||||
{t("common:build.private")}
|
||||
</div>
|
||||
) : null}
|
||||
|
|
@ -158,19 +157,19 @@ export function BuildCard({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="build__weapons">
|
||||
<div className={styles.weapons}>
|
||||
{weapons.map((weapon) => (
|
||||
<RoundWeaponImage key={weapon.weaponSplId} weapon={weapon} />
|
||||
))}
|
||||
{weapons.length === 1 && (
|
||||
<div className="build__weapon-text">
|
||||
<div className={styles.weaponText}>
|
||||
{t(`weapons:MAIN_${weapons[0].weaponSplId}` as any)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={clsx("build__gear-abilities", {
|
||||
"build__gear-abilities__no-gear": isNoGear,
|
||||
className={clsx(styles.gearAbilities, {
|
||||
[styles.noGear]: isNoGear,
|
||||
})}
|
||||
>
|
||||
<AbilitiesRowWithGear
|
||||
|
|
@ -189,7 +188,7 @@ export function BuildCard({
|
|||
gearId={shoesGearSplId}
|
||||
/>
|
||||
</div>
|
||||
<div className="build__bottom-row">
|
||||
<div className={styles.bottomRow}>
|
||||
<Link
|
||||
to={analyzerPage({
|
||||
weaponId: weapons[0].weaponSplId,
|
||||
|
|
@ -198,7 +197,7 @@ export function BuildCard({
|
|||
>
|
||||
<Image
|
||||
alt={t("common:pages.analyzer")}
|
||||
className="build__icon"
|
||||
className={styles.icon}
|
||||
path={navIconUrl("analyzer")}
|
||||
/>
|
||||
</Link>
|
||||
|
|
@ -208,7 +207,7 @@ export function BuildCard({
|
|||
<SendouButton
|
||||
variant="minimal"
|
||||
icon={<SpeechBubbleIcon />}
|
||||
className="build__small-text"
|
||||
className={styles.smallText}
|
||||
/>
|
||||
}
|
||||
>
|
||||
|
|
@ -218,13 +217,13 @@ export function BuildCard({
|
|||
{canEdit && (
|
||||
<>
|
||||
<LinkButton
|
||||
className="build__small-text"
|
||||
className={styles.smallText}
|
||||
variant="minimal"
|
||||
size="small"
|
||||
to={`new?buildId=${id}&userId=${user!.id}`}
|
||||
testId="edit-build"
|
||||
>
|
||||
<EditIcon className="build__icon" />
|
||||
<EditIcon className={styles.icon} />
|
||||
</LinkButton>
|
||||
<FormWithConfirm
|
||||
dialogHeading={t("builds:deleteConfirm", { title })}
|
||||
|
|
@ -234,8 +233,8 @@ export function BuildCard({
|
|||
]}
|
||||
>
|
||||
<SendouButton
|
||||
icon={<TrashIcon className="build__icon" />}
|
||||
className="build__small-text"
|
||||
icon={<TrashIcon className={styles.icon} />}
|
||||
className={styles.smallText}
|
||||
variant="minimal-destructive"
|
||||
type="submit"
|
||||
/>
|
||||
|
|
@ -259,10 +258,10 @@ function RoundWeaponImage({ weapon }: { weapon: BuildWeaponWithTop500Info }) {
|
|||
const isTop500 = typeof maxPower === "number" && typeof minRank === "number";
|
||||
|
||||
return (
|
||||
<div key={weaponSplId} className="build__weapon">
|
||||
<div key={weaponSplId} className={styles.weapon}>
|
||||
{isTop500 ? (
|
||||
<Image
|
||||
className="build__top500"
|
||||
className={styles.top500}
|
||||
path={navIconUrl("xsearch")}
|
||||
alt=""
|
||||
title={`Max X Power: ${maxPower} | Best Rank: ${minRank}`}
|
||||
|
|
@ -307,7 +306,7 @@ function AbilitiesRowWithGear({
|
|||
alt={translatedGearName}
|
||||
title={translatedGearName}
|
||||
path={gearImageUrl(gearType, gearId)}
|
||||
className="build__gear"
|
||||
className={styles.gear}
|
||||
/>
|
||||
) : null}
|
||||
{abilities.map((ability, i) => (
|
||||
|
|
|
|||
|
|
@ -109,8 +109,8 @@ function Association({
|
|||
]}
|
||||
>
|
||||
<SendouButton
|
||||
icon={<TrashIcon className="build__icon" />}
|
||||
className="build__small-text"
|
||||
icon={<TrashIcon className="small-icon" />}
|
||||
className="small-text"
|
||||
variant="minimal-destructive"
|
||||
type="submit"
|
||||
data-testid="delete-association"
|
||||
|
|
@ -249,8 +249,8 @@ function AssociationMember({
|
|||
]}
|
||||
>
|
||||
<SendouButton
|
||||
icon={<TrashIcon className="build__icon" />}
|
||||
className="build__small-text"
|
||||
icon={<TrashIcon className="small-icon" />}
|
||||
className="small-text"
|
||||
variant="minimal-destructive"
|
||||
type="submit"
|
||||
/>
|
||||
|
|
|
|||
37
app/features/builds/components/FilterSection.module.css
Normal file
37
app/features/builds/components/FilterSection.module.css
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
.filter {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: var(--s-2-5);
|
||||
border-radius: var(--rounded);
|
||||
background-color: var(--bg-lighter);
|
||||
gap: var(--s-2);
|
||||
}
|
||||
|
||||
.filterMode {
|
||||
gap: var(--s-6);
|
||||
}
|
||||
|
||||
.filterDate {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 750px) {
|
||||
.filter {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.abilityContainer {
|
||||
display: flex;
|
||||
width: 32px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.apSelect {
|
||||
width: 75px;
|
||||
}
|
||||
|
||||
.dateSelect {
|
||||
width: 275px;
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import clsx from "clsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Ability } from "~/components/Ability";
|
||||
import { ModeImage } from "~/components/Image";
|
||||
|
|
@ -17,6 +18,8 @@ import type {
|
|||
ModeBuildFilter,
|
||||
} from "../builds-types";
|
||||
|
||||
import styles from "./FilterSection.module.css";
|
||||
|
||||
export function FilterSection({
|
||||
number,
|
||||
nthOfSame,
|
||||
|
|
@ -74,8 +77,8 @@ function AbilityFilter({
|
|||
const abilityObject = abilities.find((a) => a.name === filter.ability)!;
|
||||
|
||||
return (
|
||||
<div className="build__filter">
|
||||
<div className="build__filter__ability">
|
||||
<div className={styles.filter}>
|
||||
<div className={styles.abilityContainer}>
|
||||
<Ability ability={filter.ability} size="TINY" />
|
||||
</div>
|
||||
<select
|
||||
|
|
@ -125,7 +128,7 @@ function AbilityFilter({
|
|||
{abilityObject.type === "STACKABLE" ? (
|
||||
<div className="stack horizontal sm items-center">
|
||||
<select
|
||||
className="build__filter__ap-select"
|
||||
className={styles.apSelect}
|
||||
value={typeof filter.value === "number" ? filter.value : "0"}
|
||||
onChange={(e) => onChange({ value: Number(e.target.value) })}
|
||||
>
|
||||
|
|
@ -156,7 +159,7 @@ function ModeFilter({
|
|||
const inputId = (mode: ModeShort) => `${number}-${mode}`;
|
||||
|
||||
return (
|
||||
<div className="build__filter build__filter__mode">
|
||||
<div className={clsx(styles.filter, styles.filterMode)}>
|
||||
{modesShort.map((mode) => {
|
||||
return (
|
||||
<div
|
||||
|
|
@ -213,10 +216,10 @@ function DateFilter({
|
|||
);
|
||||
|
||||
return (
|
||||
<div className="build__filter build__filter__date">
|
||||
<div className={clsx(styles.filter, styles.filterDate)}>
|
||||
<label className="mb-0">{t("builds:filters.date.since")}</label>
|
||||
<select
|
||||
className="build__filter__date-select"
|
||||
className={styles.dateSelect}
|
||||
value={selectValue()}
|
||||
data-testid="date-select"
|
||||
onChange={(e) =>
|
||||
|
|
|
|||
24
app/features/builds/routes/builds.$slug.module.css
Normal file
24
app/features/builds/routes/builds.$slug.module.css
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
.buildsContainer {
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
gap: var(--s-4) var(--s-3);
|
||||
grid-template-columns: repeat(auto-fit, 240px);
|
||||
}
|
||||
|
||||
.buildsButtons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.buildsButtonsLink {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--s-2);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 480px) {
|
||||
.buildsButtonsLink {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
|
@ -45,6 +45,8 @@ import { FilterSection } from "../components/FilterSection";
|
|||
import { loader } from "../loaders/builds.$slug.server";
|
||||
export { loader };
|
||||
|
||||
import styles from "./builds.$slug.module.css";
|
||||
|
||||
const filterOutMeaninglessFilters = (
|
||||
filter: Unpacked<BuildFiltersFromSearchParams>,
|
||||
) => {
|
||||
|
|
@ -165,7 +167,7 @@ export function BuildCards({
|
|||
const user = useUser();
|
||||
|
||||
return (
|
||||
<div className="builds-container">
|
||||
<div className={styles.buildsContainer}>
|
||||
{data.builds.map((build) => {
|
||||
return (
|
||||
<BuildCard
|
||||
|
|
@ -277,7 +279,7 @@ export default function WeaponsBuildsPage() {
|
|||
|
||||
return (
|
||||
<Main className="stack lg">
|
||||
<div className="builds-buttons">
|
||||
<div className={styles.buildsButtons}>
|
||||
<SendouMenu
|
||||
trigger={
|
||||
<SendouButton
|
||||
|
|
@ -315,7 +317,7 @@ export default function WeaponsBuildsPage() {
|
|||
{t("builds:filters.type.date")}
|
||||
</SendouMenuItem>
|
||||
</SendouMenu>
|
||||
<div className="builds-buttons__link">
|
||||
<div className={styles.buildsButtonsLink}>
|
||||
<LinkButton
|
||||
to={weaponBuildStatsPage(data.slug)}
|
||||
variant="outlined"
|
||||
|
|
|
|||
|
|
@ -5,3 +5,46 @@
|
|||
border-radius: var(--rounded);
|
||||
background-color: var(--bg-lightest);
|
||||
}
|
||||
|
||||
.topContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.category {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: var(--s-3);
|
||||
border-radius: var(--rounded);
|
||||
background-color: var(--bg-lighter);
|
||||
font-size: var(--fonts-sm);
|
||||
font-weight: 600;
|
||||
gap: var(--s-4);
|
||||
}
|
||||
|
||||
.categoryHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--s-2-5);
|
||||
}
|
||||
|
||||
.categoryWeapons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--s-3);
|
||||
}
|
||||
|
||||
.categoryWeapon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--text);
|
||||
font-size: var(--fonts-xs);
|
||||
font-weight: var(--body);
|
||||
gap: var(--s-1);
|
||||
}
|
||||
|
||||
.categoryWeaponImg {
|
||||
border-radius: var(--rounded);
|
||||
background-color: var(--bg-darker);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ import {
|
|||
} from "~/utils/urls";
|
||||
import { metaTags } from "../../../utils/remix";
|
||||
|
||||
import "~/styles/builds.css";
|
||||
|
||||
import styles from "./builds.module.css";
|
||||
|
||||
export const meta: MetaFunction = (args) => {
|
||||
|
|
@ -62,8 +60,8 @@ export default function BuildsPage() {
|
|||
</div>
|
||||
) : null}
|
||||
{weaponCategories.map((category) => (
|
||||
<div key={category.name} className="builds__category">
|
||||
<div className="builds__category__header">
|
||||
<div key={category.name} className={styles.category}>
|
||||
<div className={styles.categoryHeader}>
|
||||
<Image
|
||||
path={weaponCategoryUrl(category.name)}
|
||||
width={40}
|
||||
|
|
@ -72,7 +70,7 @@ export default function BuildsPage() {
|
|||
/>
|
||||
{t(`common:weapon.category.${category.name}`)}
|
||||
</div>
|
||||
<div className="builds__category__weapons">
|
||||
<div className={styles.categoryWeapons}>
|
||||
{(category.weaponIds as readonly MainWeaponId[])
|
||||
.filter(weaponIdIsNotAlt)
|
||||
.map((weaponId, i) => (
|
||||
|
|
@ -83,11 +81,11 @@ export default function BuildsPage() {
|
|||
<Link
|
||||
key={weaponId}
|
||||
to={weaponBuildPage(weaponIdToSlug(weaponId))}
|
||||
className="builds__category__weapon"
|
||||
className={styles.categoryWeapon}
|
||||
data-testid={`weapon-${weaponId}-link`}
|
||||
>
|
||||
<Image
|
||||
className="builds__category__weapon__img"
|
||||
className={styles.categoryWeaponImg}
|
||||
path={mainWeaponImageUrl(weaponId)}
|
||||
width={28}
|
||||
height={28}
|
||||
|
|
|
|||
|
|
@ -464,11 +464,11 @@ function PostDeleteButton({ id, type }: { id: number; type: Post["type"] }) {
|
|||
fetcher={fetcher}
|
||||
>
|
||||
<SendouButton
|
||||
className="build__small-text"
|
||||
className="small-text"
|
||||
variant="minimal-destructive"
|
||||
size="small"
|
||||
type="submit"
|
||||
icon={<TrashIcon className="build__icon" />}
|
||||
icon={<TrashIcon className="small-icon" />}
|
||||
>
|
||||
{t("common:actions.delete")}
|
||||
</SendouButton>
|
||||
|
|
|
|||
|
|
@ -795,7 +795,7 @@ function BottomSection({
|
|||
size="small"
|
||||
type="submit"
|
||||
isDisabled={ownTeamReported && !data.match.mapList[0].winnerGroupId}
|
||||
className="build__small-text mt-4"
|
||||
className="small-text mt-4"
|
||||
>
|
||||
{t("q:match.cancelMatch")}
|
||||
</SendouButton>
|
||||
|
|
|
|||
|
|
@ -641,12 +641,12 @@ function TrustedUsers() {
|
|||
submitButtonText="Remove"
|
||||
>
|
||||
<SendouButton
|
||||
className="build__small-text"
|
||||
className="small-text"
|
||||
variant="minimal-destructive"
|
||||
size="small"
|
||||
type="submit"
|
||||
>
|
||||
<TrashIcon className="build__icon" />
|
||||
<TrashIcon className="small-icon" />
|
||||
</SendouButton>
|
||||
</FormWithConfirm>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -551,7 +551,7 @@ function DeletePrivateNoteForm({
|
|||
]}
|
||||
>
|
||||
<SubmitButton variant="minimal-destructive" size="small" type="submit">
|
||||
<TrashIcon className="build__icon" />
|
||||
<TrashIcon className="small-icon" />
|
||||
</SubmitButton>
|
||||
</FormWithConfirm>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ export function SwissBracket({
|
|||
<SendouButton
|
||||
variant="minimal-destructive"
|
||||
type="submit"
|
||||
className="build__small-text mb-4"
|
||||
className="small-text mb-4"
|
||||
size="small"
|
||||
data-testid="reset-round-button"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -502,7 +502,7 @@ function RemoveStaffButton({
|
|||
size="small"
|
||||
data-testid="remove-staff-button"
|
||||
>
|
||||
<TrashIcon className="build__icon" />
|
||||
<TrashIcon className="small-icon" />
|
||||
</SendouButton>
|
||||
</FormWithConfirm>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ function TournamentRegisterInfoTabs() {
|
|||
submitButtonText="Leave"
|
||||
>
|
||||
<SendouButton
|
||||
className="build__small-text"
|
||||
className="small-text"
|
||||
variant="minimal-destructive"
|
||||
type="submit"
|
||||
>
|
||||
|
|
@ -694,7 +694,7 @@ function TeamInfo({
|
|||
<SendouButton
|
||||
size="small"
|
||||
variant="minimal-destructive"
|
||||
className="build__small-text"
|
||||
className="small-text"
|
||||
>
|
||||
{t("tournament:pre.info.unregister")}
|
||||
</SendouButton>
|
||||
|
|
@ -710,7 +710,7 @@ function TeamInfo({
|
|||
fields={[["_action", "UNREGISTER"]]}
|
||||
>
|
||||
<SendouButton
|
||||
className="build__small-text"
|
||||
className="small-text"
|
||||
variant="minimal-destructive"
|
||||
size="small"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
.buildsContainer {
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
gap: var(--s-4) var(--s-3);
|
||||
grid-template-columns: repeat(auto-fit, 240px);
|
||||
}
|
||||
|
|
@ -31,6 +31,8 @@ import {
|
|||
} from "../loaders/u.$identifier.builds.server";
|
||||
export { loader, action };
|
||||
|
||||
import styles from "./u.$identifier.builds.module.css";
|
||||
|
||||
export const handle: SendouRouteHandle = {
|
||||
i18n: ["weapons", "builds", "gear"],
|
||||
};
|
||||
|
|
@ -100,7 +102,7 @@ export default function UserBuildsPage() {
|
|||
setWeaponFilter={setWeaponFilter}
|
||||
/>
|
||||
{builds.length > 0 ? (
|
||||
<div className="builds-container">
|
||||
<div className={styles.buildsContainer}>
|
||||
{builds.map((build) => (
|
||||
<BuildCard
|
||||
key={build.id}
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
.builds__top-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.builds__category {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: var(--s-3);
|
||||
border-radius: var(--rounded);
|
||||
background-color: var(--bg-lighter);
|
||||
font-size: var(--fonts-sm);
|
||||
font-weight: 600;
|
||||
gap: var(--s-4);
|
||||
}
|
||||
|
||||
.builds__category__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--s-2-5);
|
||||
}
|
||||
|
||||
.builds__category__weapons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--s-3);
|
||||
}
|
||||
|
||||
.builds__category__weapon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--text);
|
||||
font-size: var(--fonts-xs);
|
||||
font-weight: var(--body);
|
||||
gap: var(--s-1);
|
||||
}
|
||||
|
||||
.builds__category__weapon__img {
|
||||
border-radius: var(--rounded);
|
||||
background-color: var(--bg-darker);
|
||||
}
|
||||
|
|
@ -757,264 +757,15 @@ abbr[title] {
|
|||
margin-block-start: var(--label-margin);
|
||||
}
|
||||
|
||||
.builds-container {
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
gap: var(--s-4) var(--s-3);
|
||||
grid-template-columns: repeat(auto-fit, 240px);
|
||||
}
|
||||
|
||||
.builds-buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.builds-buttons__link {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--s-2);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 480px) {
|
||||
.builds-buttons__link {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.build__filter {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: var(--s-2-5);
|
||||
border-radius: var(--rounded);
|
||||
background-color: var(--bg-lighter);
|
||||
gap: var(--s-2);
|
||||
}
|
||||
|
||||
.build__filter__mode {
|
||||
gap: var(--s-6);
|
||||
}
|
||||
|
||||
.build__filter__date {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 750px) {
|
||||
.build__filter {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.build__filter__ability {
|
||||
display: flex;
|
||||
width: 32px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.build__filter__ap-select {
|
||||
width: 75px;
|
||||
}
|
||||
|
||||
.build__filter__date-select {
|
||||
width: 275px;
|
||||
}
|
||||
|
||||
.build {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: var(--s-3);
|
||||
border-radius: var(--rounded);
|
||||
background-color: var(--bg-lighter);
|
||||
gap: var(--s-3);
|
||||
}
|
||||
|
||||
.build__private {
|
||||
background-color: var(--bg-lighter-transparent);
|
||||
}
|
||||
|
||||
.build__private-text {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
font-weight: var(--semi-bold);
|
||||
gap: var(--s-1);
|
||||
}
|
||||
|
||||
.build__private-icon {
|
||||
width: 16px;
|
||||
margin-block-end: var(--s-1);
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
.build__title {
|
||||
overflow: hidden;
|
||||
height: 2.5rem;
|
||||
font-size: var(--fonts-sm);
|
||||
line-height: 1.25;
|
||||
word-wrap: break-all;
|
||||
}
|
||||
|
||||
.build__top-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.build__date-author-row {
|
||||
display: flex;
|
||||
font-size: var(--fonts-xxs);
|
||||
gap: var(--s-1);
|
||||
}
|
||||
|
||||
.build__date-author-row__owner {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.build__modes {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
display: flex;
|
||||
min-width: max-content;
|
||||
margin-top: -21px;
|
||||
gap: var(--s-1);
|
||||
}
|
||||
|
||||
.build__weapon {
|
||||
position: relative;
|
||||
padding: var(--s-0-5);
|
||||
border-radius: 50%;
|
||||
background-color: var(--bg-darker-very-transparent);
|
||||
}
|
||||
|
||||
.build__top500 {
|
||||
position: absolute;
|
||||
top: -14px;
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
.build__weapon-text {
|
||||
padding-left: var(--s-1);
|
||||
color: var(--text-lighter);
|
||||
font-size: var(--fonts-xxs);
|
||||
font-weight: var(--semi-bold);
|
||||
}
|
||||
|
||||
.build__weapons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--s-1);
|
||||
}
|
||||
|
||||
.build__gear-abilities {
|
||||
display: grid;
|
||||
gap: var(--s-2) var(--s-1);
|
||||
grid-template-columns: repeat(5, max-content);
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.build__gear-abilities__no-gear {
|
||||
grid-template-columns: repeat(4, max-content);
|
||||
margin: 0 auto;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 1rem;
|
||||
row-gap: 28px;
|
||||
}
|
||||
|
||||
.build__gear {
|
||||
border-radius: 50%;
|
||||
background-color: var(--bg-darker-very-transparent);
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.build__ability {
|
||||
width: var(--ability-size);
|
||||
height: var(--ability-size);
|
||||
padding: 0;
|
||||
border: 2px solid var(--theme-transparent);
|
||||
border-radius: 50%;
|
||||
border-right: 0;
|
||||
border-bottom: 0;
|
||||
background: var(--bg-ability);
|
||||
background-size: 100%;
|
||||
box-shadow: 0 0 0 1px var(--bg-ability);
|
||||
transform: scale(1);
|
||||
transition: all 0.1s ease;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.build__ability.is-drag-target {
|
||||
background: var(--abilities-button-bg);
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
.build__ability.drag-started:not(.drop-allowed) {
|
||||
filter: grayscale(1);
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.build__ability.readonly,
|
||||
.build__ability.readonly:active {
|
||||
cursor: default;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.build__bottom-row {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
gap: var(--s-4);
|
||||
}
|
||||
|
||||
.build__icon {
|
||||
.small-icon {
|
||||
width: 1.2rem;
|
||||
height: 1.2rem;
|
||||
}
|
||||
|
||||
.build__small-text {
|
||||
.small-text {
|
||||
font-size: var(--fonts-xxs) !important;
|
||||
}
|
||||
|
||||
.ability-selector__container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: var(--s-3);
|
||||
}
|
||||
|
||||
.ability-selector__slots {
|
||||
display: grid;
|
||||
margin: 0 auto;
|
||||
gap: var(--s-2);
|
||||
grid-template-columns: repeat(4, max-content);
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.ability-selector__ability-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: var(--s-1);
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.ability-selector__ability-button {
|
||||
padding: var(--s-0-5);
|
||||
border-color: var(--abilities-button-bg);
|
||||
border-radius: 50%;
|
||||
background-color: var(--abilities-button-bg);
|
||||
}
|
||||
|
||||
.ability-selector__ability-button.is-dragging {
|
||||
box-shadow: 0 0 100px inset rgb(255 255 255 / 25%);
|
||||
}
|
||||
|
||||
.colors__summary {
|
||||
padding: var(--s-2) var(--s-3);
|
||||
border: 2px solid var(--border);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user