MatchActionPickBanTab

This commit is contained in:
Kalle 2026-04-22 17:33:41 +03:00
parent 646cc04899
commit 7ad37d6882
22 changed files with 846 additions and 29 deletions

View File

@ -10,6 +10,7 @@
& > label {
margin: 0;
text-box: trim-start cap alphabetic;
}
}

View File

@ -0,0 +1,185 @@
.root {
display: grid;
grid-template-columns: 1fr;
grid-template-areas:
"header"
"options"
"prompt"
"submit";
justify-items: center;
align-items: center;
gap: var(--s-6);
container-type: inline-size;
}
.title {
grid-area: header;
font-size: var(--font-md);
font-weight: var(--weight-semi);
text-align: center;
text-box: trim-start cap alphabetic;
}
.options {
grid-area: options;
display: flex;
flex-direction: column;
gap: var(--s-6);
width: 100%;
}
.prompt {
grid-area: prompt;
margin: 0;
font-size: var(--font-sm);
color: var(--color-text-lighter);
text-align: center;
}
.verbPick {
color: var(--color-success);
font-weight: var(--weight-semi);
}
.verbBan {
color: var(--color-error);
font-weight: var(--weight-semi);
}
.submit {
grid-area: submit;
}
.modeGroup {
display: flex;
flex-direction: column;
gap: var(--s-2);
}
.divider {
font-size: var(--font-xs);
font-weight: var(--weight-semi);
text-transform: uppercase;
display: flex;
gap: var(--s-2);
&::before,
&::after {
border-bottom: 2px dotted var(--color-bg-higher);
}
}
.stageGrid {
--tile-width: 90px;
--tile-height: 50px;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: var(--s-4) var(--s-3);
}
.modeGrid {
--tile-width: 90px;
--tile-height: 90px;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: var(--s-4) var(--s-3);
}
.tileContainer {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
width: var(--tile-width);
text-align: center;
}
.tileWrapper {
position: relative;
width: var(--tile-width);
height: var(--tile-height);
}
.tile {
height: var(--tile-height);
width: var(--tile-width);
border: none;
background-color: transparent;
transition:
filter,
opacity 0.2s;
border-radius: var(--radius-box);
cursor: pointer;
&:active {
transform: none;
}
}
.stageTile {
background-image: var(--map-image-url);
background-size: cover;
}
.modeTile {
display: flex;
align-items: center;
justify-content: center;
background-color: var(--color-bg-higher);
}
.tileSelected {
filter: grayscale(100%);
opacity: 0.4;
}
.tileIcon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 48px;
height: 48px;
pointer-events: none;
}
.tileIconPick {
color: var(--color-success);
}
.tileIconBan {
color: var(--color-error);
}
.tileNumber {
position: absolute;
background-color: var(--color-text-accent);
border-radius: 100%;
width: 18px;
height: 18px;
display: grid;
place-items: center;
color: var(--color-text-inverse);
font-size: var(--font-2xs);
font-weight: var(--weight-semi);
top: -5px;
left: 0;
pointer-events: none;
}
.tileFrom {
font-size: var(--font-2xs);
font-weight: var(--weight-bold);
text-transform: uppercase;
line-height: 1;
margin-block-start: var(--s-0-5);
}
.tileLabel {
font-size: var(--font-2xs);
color: var(--color-text-high);
font-weight: var(--weight-semi);
margin-block-start: var(--s-1);
}

View File

@ -0,0 +1,347 @@
import clsx from "clsx";
import { Check, X } from "lucide-react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { SendouButton } from "~/components/elements/Button";
import { shortStageName } from "~/modules/in-game-lists/stage-ids";
import type { ModeShort, StageId } from "~/modules/in-game-lists/types";
import { stageImageUrl } from "~/utils/urls";
import { Divider } from "../Divider";
import { SendouTabPanel } from "../elements/Tabs";
import { ModeImage } from "../Image";
import styles from "./MatchActionPickBanTab.module.css";
import { TAB_KEYS } from "./MatchTabs";
import { WeaponReporter, type WeaponReporterProps } from "./WeaponReporter";
interface PickBanMapOption {
stageId?: StageId;
mode?: ModeShort;
picker?: "US" | "THEM" | "BOTH";
nth?: number;
}
interface PickBanSubmission {
type: "PICK" | "BAN";
map: PickBanMapOption;
}
interface MatchActionPickBanTabProps {
options: PickBanMapOption[];
type: "PICK" | "BAN";
onSubmit?: (data: PickBanSubmission) => void;
isSubmitting?: boolean;
weaponReport?: WeaponReporterProps;
}
export function MatchActionPickBanTab({
options,
type,
onSubmit,
isSubmitting,
weaponReport,
}: MatchActionPickBanTabProps) {
const { t } = useTranslation(["q", "common", "game-misc"]);
const [selected, setSelected] = useState<PickBanMapOption>();
const hasStage = options.every((option) => option.stageId !== undefined);
const hasMode = options.every((option) => option.mode !== undefined);
const layout: "STAGE_BY_MODE" | "STAGE_ONLY" | "MODE_ONLY" =
hasStage && hasMode
? "STAGE_BY_MODE"
: hasStage
? "STAGE_ONLY"
: "MODE_ONLY";
const titleKey =
layout === "MODE_ONLY"
? type === "PICK"
? "q:match.action.pickMode"
: "q:match.action.banMode"
: type === "PICK"
? "q:match.action.pickStage"
: "q:match.action.banStage";
const selectedLabel = (() => {
if (!selected) return null;
const stageName =
selected.stageId !== undefined
? t(`game-misc:STAGE_${selected.stageId}`)
: null;
const modeName =
selected.mode !== undefined
? t(
selected.stageId !== undefined
? `game-misc:MODE_SHORT_${selected.mode}`
: `game-misc:MODE_LONG_${selected.mode}`,
)
: null;
if (stageName && modeName) return `${stageName} (${modeName})`;
return stageName ?? modeName;
})();
return (
<SendouTabPanel id={TAB_KEYS.ACTION}>
<div className={styles.root}>
<div className={styles.title}>{t(titleKey)}</div>
<div className={styles.options}>
{layout === "STAGE_BY_MODE" ? (
<StageByModeGrid
options={options}
type={type}
selected={selected}
onSelect={setSelected}
/>
) : layout === "STAGE_ONLY" ? (
<StageOnlyGrid
options={options}
type={type}
selected={selected}
onSelect={setSelected}
/>
) : (
<ModeOnlyGrid
options={options}
type={type}
selected={selected}
onSelect={setSelected}
/>
)}
</div>
<p className={styles.prompt}>
{selectedLabel ? (
<>
<span
className={type === "PICK" ? styles.verbPick : styles.verbBan}
>
{type === "PICK"
? t("q:match.action.picking")
: t("q:match.action.banning")}
</span>{" "}
{selectedLabel}
</>
) : (
t("q:match.action.pickBanPrompt")
)}
</p>
<SendouButton
variant="primary"
className={styles.submit}
isDisabled={!selected || isSubmitting}
onPress={() => {
if (!selected) return;
onSubmit?.({ type, map: selected });
}}
>
{t("common:actions.submit")}
</SendouButton>
</div>
{weaponReport ? <WeaponReporter {...weaponReport} /> : null}
</SendouTabPanel>
);
}
function StageByModeGrid({
options,
type,
selected,
onSelect,
}: {
options: PickBanMapOption[];
type: "PICK" | "BAN";
selected?: PickBanMapOption;
onSelect: (option: PickBanMapOption) => void;
}) {
const modesInOrder: ModeShort[] = [];
const byMode = new Map<ModeShort, PickBanMapOption[]>();
for (const option of options) {
const mode = option.mode!;
if (!byMode.has(mode)) {
byMode.set(mode, []);
modesInOrder.push(mode);
}
byMode.get(mode)!.push(option);
}
return (
<>
{modesInOrder.map((mode) => (
<div key={mode} className={styles.modeGroup}>
<Divider className={styles.divider}>
<ModeImage mode={mode} size={32} />
</Divider>
<div className={styles.stageGrid}>
{byMode.get(mode)!.map((option) => (
<StageTile
key={`${option.stageId}-${option.mode}`}
option={option}
type={type}
isSelected={isSameOption(option, selected)}
onSelect={() => onSelect(option)}
/>
))}
</div>
</div>
))}
</>
);
}
function StageOnlyGrid({
options,
type,
selected,
onSelect,
}: {
options: PickBanMapOption[];
type: "PICK" | "BAN";
selected?: PickBanMapOption;
onSelect: (option: PickBanMapOption) => void;
}) {
return (
<div className={styles.stageGrid}>
{options.map((option) => (
<StageTile
key={option.stageId}
option={option}
type={type}
isSelected={isSameOption(option, selected)}
onSelect={() => onSelect(option)}
/>
))}
</div>
);
}
function ModeOnlyGrid({
options,
type,
selected,
onSelect,
}: {
options: PickBanMapOption[];
type: "PICK" | "BAN";
selected?: PickBanMapOption;
onSelect: (option: PickBanMapOption) => void;
}) {
return (
<div className={styles.modeGrid}>
{options.map((option) => (
<ModeTile
key={option.mode}
option={option}
type={type}
isSelected={isSameOption(option, selected)}
onSelect={() => onSelect(option)}
/>
))}
</div>
);
}
// xxx: maybe we should just have a shared custom component for stage image + label
function StageTile({
option,
type,
isSelected,
onSelect,
}: {
option: PickBanMapOption;
type: "PICK" | "BAN";
isSelected: boolean;
onSelect: () => void;
}) {
const { t } = useTranslation(["q", "game-misc"]);
return (
<div className={styles.tileContainer}>
<div className={styles.tileWrapper}>
<button
type="button"
className={clsx(styles.tile, styles.stageTile, {
[styles.tileSelected]: isSelected,
})}
style={{
"--map-image-url": `url("${stageImageUrl(option.stageId!)}.avif")`,
}}
onClick={onSelect}
/>
{isSelected ? (
type === "PICK" ? (
<Check className={clsx(styles.tileIcon, styles.tileIconPick)} />
) : (
<X className={clsx(styles.tileIcon, styles.tileIconBan)} />
)
) : null}
{option.nth ? (
<span className={styles.tileNumber}>{option.nth}</span>
) : null}
</div>
<div className={styles.tileLabel}>
{shortStageName(t(`game-misc:STAGE_${option.stageId!}`))}
</div>
{option.picker ? (
<span
className={clsx(styles.tileFrom, {
"text-theme": option.picker === "BOTH",
"text-success": option.picker === "US",
"text-error": option.picker === "THEM",
})}
>
{option.picker === "US"
? t("q:match.action.pickerUs")
: option.picker === "THEM"
? t("q:match.action.pickerThem")
: t("q:match.action.pickerBoth")}
</span>
) : null}
</div>
);
}
function ModeTile({
option,
type,
isSelected,
onSelect,
}: {
option: PickBanMapOption;
type: "PICK" | "BAN";
isSelected: boolean;
onSelect: () => void;
}) {
const { t } = useTranslation(["game-misc"]);
return (
<div className={styles.tileContainer}>
<div className={styles.tileWrapper}>
<button
type="button"
className={clsx(styles.tile, styles.modeTile, {
[styles.tileSelected]: isSelected,
})}
onClick={onSelect}
>
<ModeImage mode={option.mode!} size={48} />
</button>
{isSelected ? (
type === "PICK" ? (
<Check className={clsx(styles.tileIcon, styles.tileIconPick)} />
) : (
<X className={clsx(styles.tileIcon, styles.tileIconBan)} />
)
) : null}
</div>
<div className={styles.tileLabel}>
{t(`game-misc:MODE_LONG_${option.mode!}`)}
</div>
</div>
);
}
function isSameOption(a: PickBanMapOption, b: PickBanMapOption | undefined) {
if (!b) return false;
return a.stageId === b.stageId && a.mode === b.mode;
}

View File

@ -4,25 +4,22 @@
grid-template-areas:
"header header header"
"actions actions actions"
"alpha stage bravo"
". label ."
"selection selection selection"
"points-alpha ko points-bravo"
". submit .";
"submit submit submit";
justify-items: center;
align-items: center;
gap: var(--s-4);
gap: var(--s-5);
container-type: inline-size;
@container (max-width: 599px) {
grid-template-columns: auto 1fr;
grid-template-columns: 1fr;
grid-template-areas:
"header header"
"actions actions"
"stage alpha"
"stage bravo"
"label ."
"points points"
"submit submit";
"header"
"actions"
"selection"
"points"
"submit";
}
}
@ -31,6 +28,7 @@
font-size: var(--font-md);
font-weight: var(--weight-semi);
text-align: center;
text-box: trim-start cap alphabetic;
}
.actionButtons {
@ -41,7 +39,26 @@
}
.selectionRow {
display: contents;
grid-area: selection;
display: grid;
grid-template-columns: 1fr auto 1fr;
grid-template-areas:
"alpha stage bravo"
". text .";
column-gap: var(--s-4);
row-gap: var(--s-1);
justify-items: center;
align-items: center;
width: 100%;
@container (max-width: 599px) {
column-gap: var(--s-2);
grid-template-columns: auto 1fr;
grid-template-areas:
"stage alpha"
"stage bravo"
"text .";
}
}
.teamRadioContainer {
@ -96,14 +113,13 @@
}
.stageLabel {
grid-area: label;
grid-area: text;
display: flex;
align-items: center;
gap: var(--s-1);
font-size: var(--font-3xs);
font-weight: var(--weight-semi);
color: var(--color-text-high);
margin-block-start: -11px;
}
.pointsRow {
@ -129,7 +145,6 @@
flex-direction: column;
align-items: center;
gap: var(--s-1);
align-self: flex-end;
}
.pointsBravo {
@ -139,7 +154,6 @@
.submit {
grid-area: submit;
margin-block-start: calc(-1 * var(--s-2));
}
.checkCircle {

View File

@ -2,6 +2,6 @@
& [class*="tabPanel"] {
background-color: var(--color-bg-high);
border-radius: 0 0 var(--radius-box) var(--radius-box);
padding: var(--s-4) var(--s-4);
padding: var(--s-6) var(--s-4);
}
}

View File

@ -1,7 +1,14 @@
import { ArrowLeft, Ban } from "lucide-react";
import { ArrowLeft, Ban, Undo2 } from "lucide-react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { SendouButton } from "~/components/elements/Button";
import {
SendouTab,
SendouTabList,
SendouTabs,
} from "~/components/elements/Tabs";
import { Main } from "~/components/Main";
import { MatchActionPickBanTab } from "~/components/match-page/MatchActionPickBanTab";
import { MatchActionTab } from "~/components/match-page/MatchActionTab";
import {
IconBanner,
@ -18,12 +25,21 @@ import { MatchTabs } from "~/components/match-page/MatchTabs";
import { logger } from "~/utils/logger";
import type { SendouRouteHandle } from "~/utils/remix.server";
type ActionVariant =
| "winner"
| "counterpick-stage"
| "ban-stage"
| "ban-stage-only"
| "pick-mode"
| "ban-mode";
export const handle: SendouRouteHandle = {
i18n: ["q"],
};
export default function MatchPageTestRoute() {
const { t } = useTranslation(["q"]);
const [actionVariant, setActionVariant] = useState<ActionVariant>("winner");
return (
<Main>
@ -39,6 +55,22 @@ export default function MatchPageTestRoute() {
Round 2.1
</MatchPageHeader>
<SendouTabs
selectedKey={actionVariant}
onSelectionChange={(key) => setActionVariant(key as ActionVariant)}
disappearing={false}
padded={false}
>
<SendouTabList>
<SendouTab id="winner">Winner</SendouTab>
<SendouTab id="counterpick-stage">Counterpick</SendouTab>
<SendouTab id="ban-stage">Ban stage</SendouTab>
<SendouTab id="ban-stage-only">Ban stage (any mode)</SendouTab>
<SendouTab id="pick-mode">Pick mode</SendouTab>
<SendouTab id="ban-mode">Ban mode</SendouTab>
</SendouTabList>
</SendouTabs>
<MatchBannerContainer>
<MatchBannerTopRow
score={{
@ -239,16 +271,94 @@ export default function MatchPageTestRoute() {
},
]}
/>
<MatchActionTab
teams={[
{ id: 1, name: "Chimera" },
{ id: 2, name: "Koopa Clan" },
]}
ownTeamId={1}
stageId={4}
mode="SZ"
withPoints={true}
/>
{actionVariant === "winner" ? (
<MatchActionTab
teams={[
{ id: 1, name: "Chimera" },
{ id: 2, name: "Koopa Clan" },
]}
ownTeamId={1}
stageId={4}
mode="SZ"
withPoints={true}
actionButtons={
<SendouButton
variant="minimal-destructive"
size="miniscule"
icon={<Undo2 size={16} />}
>
{t("q:match.undoReport")}
</SendouButton>
}
/>
) : actionVariant === "counterpick-stage" ? (
<MatchActionPickBanTab
type="PICK"
options={[
{ stageId: 1, mode: "SZ", picker: "US" },
{ stageId: 2, mode: "SZ", picker: "BOTH" },
{ stageId: 3, mode: "SZ", picker: "THEM" },
{ stageId: 4, mode: "TC", picker: "US" },
{ stageId: 5, mode: "TC", picker: "THEM" },
{ stageId: 6, mode: "RM", picker: "BOTH" },
{ stageId: 7, mode: "RM", picker: "US" },
]}
onSubmit={(data) => logger.info("pick submit", data)}
/>
) : actionVariant === "ban-stage" ? (
<MatchActionPickBanTab
type="BAN"
options={[
{ stageId: 1, mode: "SZ", nth: 1 },
{ stageId: 2, mode: "SZ", nth: 2 },
{ stageId: 4, mode: "TC", nth: 3 },
{ stageId: 5, mode: "TC", nth: 4 },
{ stageId: 6, mode: "RM", nth: 5 },
{ stageId: 7, mode: "RM", nth: 6 },
{ stageId: 8, mode: "CB", nth: 7 },
{ stageId: 9, mode: "CB", nth: 8 },
]}
onSubmit={(data) => logger.info("ban submit", data)}
/>
) : actionVariant === "ban-stage-only" ? (
<MatchActionPickBanTab
type="BAN"
options={[
{ stageId: 1 },
{ stageId: 2 },
{ stageId: 3 },
{ stageId: 4 },
{ stageId: 5 },
{ stageId: 6 },
{ stageId: 7 },
{ stageId: 8 },
{ stageId: 9 },
]}
onSubmit={(data) => logger.info("ban stage-only submit", data)}
/>
) : actionVariant === "pick-mode" ? (
<MatchActionPickBanTab
type="PICK"
options={[
{ mode: "SZ" },
{ mode: "TC" },
{ mode: "RM" },
{ mode: "CB" },
]}
onSubmit={(data) => logger.info("pick mode submit", data)}
/>
) : (
<MatchActionPickBanTab
type="BAN"
options={[
{ mode: "SZ" },
{ mode: "TC" },
{ mode: "RM" },
{ mode: "CB" },
]}
onSubmit={(data) => logger.info("ban mode submit", data)}
/>
)}
<MatchResultTab
teams={{
alpha: { name: "me in japan" },

View File

@ -184,6 +184,16 @@
"match.action.confirmSetEnding": "",
"match.action.requestCancel": "",
"match.action.acceptCancelingSet": "",
"match.action.pickStage": "",
"match.action.banStage": "",
"match.action.pickMode": "",
"match.action.banMode": "",
"match.action.pickerUs": "",
"match.action.pickerThem": "",
"match.action.pickerBoth": "",
"match.action.pickBanPrompt": "",
"match.action.picking": "",
"match.action.banning": "",
"match.cancelRequested.subtitle": "",
"match.timeline.win": "",
"match.timeline.loss": "",

View File

@ -184,6 +184,16 @@
"match.action.confirmSetEnding": "",
"match.action.requestCancel": "",
"match.action.acceptCancelingSet": "",
"match.action.pickStage": "",
"match.action.banStage": "",
"match.action.pickMode": "",
"match.action.banMode": "",
"match.action.pickerUs": "",
"match.action.pickerThem": "",
"match.action.pickerBoth": "",
"match.action.pickBanPrompt": "",
"match.action.picking": "",
"match.action.banning": "",
"match.cancelRequested.subtitle": "",
"match.timeline.win": "",
"match.timeline.loss": "",

View File

@ -184,6 +184,16 @@
"match.action.confirmSetEnding": "This score will end the set. Please confirm below.",
"match.action.requestCancel": "Request cancel",
"match.action.acceptCancelingSet": "Accept canceling the set?",
"match.action.pickStage": "Pick a stage",
"match.action.banStage": "Ban a stage",
"match.action.pickMode": "Pick a mode",
"match.action.banMode": "Ban a mode",
"match.action.pickerUs": "Us",
"match.action.pickerThem": "Them",
"match.action.pickerBoth": "Both",
"match.action.pickBanPrompt": "Make your selection above",
"match.action.picking": "Picking",
"match.action.banning": "Banning",
"match.cancelRequested.subtitle": "{{teamName}} has requested to cancel the set",
"match.timeline.win": "Win",
"match.timeline.loss": "Loss",

View File

@ -184,6 +184,16 @@
"match.action.confirmSetEnding": "",
"match.action.requestCancel": "",
"match.action.acceptCancelingSet": "",
"match.action.pickStage": "",
"match.action.banStage": "",
"match.action.pickMode": "",
"match.action.banMode": "",
"match.action.pickerUs": "",
"match.action.pickerThem": "",
"match.action.pickerBoth": "",
"match.action.pickBanPrompt": "",
"match.action.picking": "",
"match.action.banning": "",
"match.cancelRequested.subtitle": "",
"match.timeline.win": "",
"match.timeline.loss": "",

View File

@ -184,6 +184,16 @@
"match.action.confirmSetEnding": "",
"match.action.requestCancel": "",
"match.action.acceptCancelingSet": "",
"match.action.pickStage": "",
"match.action.banStage": "",
"match.action.pickMode": "",
"match.action.banMode": "",
"match.action.pickerUs": "",
"match.action.pickerThem": "",
"match.action.pickerBoth": "",
"match.action.pickBanPrompt": "",
"match.action.picking": "",
"match.action.banning": "",
"match.cancelRequested.subtitle": "",
"match.timeline.win": "",
"match.timeline.loss": "",

View File

@ -184,6 +184,16 @@
"match.action.confirmSetEnding": "",
"match.action.requestCancel": "",
"match.action.acceptCancelingSet": "",
"match.action.pickStage": "",
"match.action.banStage": "",
"match.action.pickMode": "",
"match.action.banMode": "",
"match.action.pickerUs": "",
"match.action.pickerThem": "",
"match.action.pickerBoth": "",
"match.action.pickBanPrompt": "",
"match.action.picking": "",
"match.action.banning": "",
"match.cancelRequested.subtitle": "",
"match.timeline.win": "",
"match.timeline.loss": "",

View File

@ -184,6 +184,16 @@
"match.action.confirmSetEnding": "",
"match.action.requestCancel": "",
"match.action.acceptCancelingSet": "",
"match.action.pickStage": "",
"match.action.banStage": "",
"match.action.pickMode": "",
"match.action.banMode": "",
"match.action.pickerUs": "",
"match.action.pickerThem": "",
"match.action.pickerBoth": "",
"match.action.pickBanPrompt": "",
"match.action.picking": "",
"match.action.banning": "",
"match.cancelRequested.subtitle": "",
"match.timeline.win": "",
"match.timeline.loss": "",

View File

@ -184,6 +184,16 @@
"match.action.confirmSetEnding": "",
"match.action.requestCancel": "",
"match.action.acceptCancelingSet": "",
"match.action.pickStage": "",
"match.action.banStage": "",
"match.action.pickMode": "",
"match.action.banMode": "",
"match.action.pickerUs": "",
"match.action.pickerThem": "",
"match.action.pickerBoth": "",
"match.action.pickBanPrompt": "",
"match.action.picking": "",
"match.action.banning": "",
"match.cancelRequested.subtitle": "",
"match.timeline.win": "",
"match.timeline.loss": "",

View File

@ -184,6 +184,16 @@
"match.action.confirmSetEnding": "",
"match.action.requestCancel": "",
"match.action.acceptCancelingSet": "",
"match.action.pickStage": "",
"match.action.banStage": "",
"match.action.pickMode": "",
"match.action.banMode": "",
"match.action.pickerUs": "",
"match.action.pickerThem": "",
"match.action.pickerBoth": "",
"match.action.pickBanPrompt": "",
"match.action.picking": "",
"match.action.banning": "",
"match.cancelRequested.subtitle": "",
"match.timeline.win": "",
"match.timeline.loss": "",

View File

@ -184,6 +184,16 @@
"match.action.confirmSetEnding": "",
"match.action.requestCancel": "",
"match.action.acceptCancelingSet": "",
"match.action.pickStage": "",
"match.action.banStage": "",
"match.action.pickMode": "",
"match.action.banMode": "",
"match.action.pickerUs": "",
"match.action.pickerThem": "",
"match.action.pickerBoth": "",
"match.action.pickBanPrompt": "",
"match.action.picking": "",
"match.action.banning": "",
"match.cancelRequested.subtitle": "",
"match.timeline.win": "",
"match.timeline.loss": "",

View File

@ -184,6 +184,16 @@
"match.action.confirmSetEnding": "",
"match.action.requestCancel": "",
"match.action.acceptCancelingSet": "",
"match.action.pickStage": "",
"match.action.banStage": "",
"match.action.pickMode": "",
"match.action.banMode": "",
"match.action.pickerUs": "",
"match.action.pickerThem": "",
"match.action.pickerBoth": "",
"match.action.pickBanPrompt": "",
"match.action.picking": "",
"match.action.banning": "",
"match.cancelRequested.subtitle": "",
"match.timeline.win": "",
"match.timeline.loss": "",

View File

@ -184,6 +184,16 @@
"match.action.confirmSetEnding": "",
"match.action.requestCancel": "",
"match.action.acceptCancelingSet": "",
"match.action.pickStage": "",
"match.action.banStage": "",
"match.action.pickMode": "",
"match.action.banMode": "",
"match.action.pickerUs": "",
"match.action.pickerThem": "",
"match.action.pickerBoth": "",
"match.action.pickBanPrompt": "",
"match.action.picking": "",
"match.action.banning": "",
"match.cancelRequested.subtitle": "",
"match.timeline.win": "",
"match.timeline.loss": "",

View File

@ -184,6 +184,16 @@
"match.action.confirmSetEnding": "",
"match.action.requestCancel": "",
"match.action.acceptCancelingSet": "",
"match.action.pickStage": "",
"match.action.banStage": "",
"match.action.pickMode": "",
"match.action.banMode": "",
"match.action.pickerUs": "",
"match.action.pickerThem": "",
"match.action.pickerBoth": "",
"match.action.pickBanPrompt": "",
"match.action.picking": "",
"match.action.banning": "",
"match.cancelRequested.subtitle": "",
"match.timeline.win": "",
"match.timeline.loss": "",

View File

@ -184,6 +184,16 @@
"match.action.confirmSetEnding": "",
"match.action.requestCancel": "",
"match.action.acceptCancelingSet": "",
"match.action.pickStage": "",
"match.action.banStage": "",
"match.action.pickMode": "",
"match.action.banMode": "",
"match.action.pickerUs": "",
"match.action.pickerThem": "",
"match.action.pickerBoth": "",
"match.action.pickBanPrompt": "",
"match.action.picking": "",
"match.action.banning": "",
"match.cancelRequested.subtitle": "",
"match.timeline.win": "",
"match.timeline.loss": "",

View File

@ -184,6 +184,16 @@
"match.action.confirmSetEnding": "",
"match.action.requestCancel": "",
"match.action.acceptCancelingSet": "",
"match.action.pickStage": "",
"match.action.banStage": "",
"match.action.pickMode": "",
"match.action.banMode": "",
"match.action.pickerUs": "",
"match.action.pickerThem": "",
"match.action.pickerBoth": "",
"match.action.pickBanPrompt": "",
"match.action.picking": "",
"match.action.banning": "",
"match.cancelRequested.subtitle": "",
"match.timeline.win": "",
"match.timeline.loss": "",

View File

@ -184,6 +184,16 @@
"match.action.confirmSetEnding": "",
"match.action.requestCancel": "",
"match.action.acceptCancelingSet": "",
"match.action.pickStage": "",
"match.action.banStage": "",
"match.action.pickMode": "",
"match.action.banMode": "",
"match.action.pickerUs": "",
"match.action.pickerThem": "",
"match.action.pickerBoth": "",
"match.action.pickBanPrompt": "",
"match.action.picking": "",
"match.action.banning": "",
"match.cancelRequested.subtitle": "",
"match.timeline.win": "",
"match.timeline.loss": "",