Refactor LinkButton to use the new SendouButton API

This commit is contained in:
Kalle
2025-06-09 21:35:55 +03:00
parent ed40f9fd30
commit 0c90f6b009
32 changed files with 194 additions and 317 deletions

View File

@@ -1,5 +1,5 @@
import { LinkButton } from "~/components/Button";
import { Image } from "~/components/Image";
import { LinkButton } from "~/components/elements/Button";
import { PlusIcon } from "~/components/icons/Plus";
import { navIconUrl } from "~/utils/urls";
@@ -12,7 +12,7 @@ interface AddNewButtonProps {
export function AddNewButton({ to, navIcon }: AddNewButtonProps) {
return (
<LinkButton to={to} size="tiny" className={styles.addNewButton}>
<LinkButton to={to} size="small" className={styles.addNewButton}>
<span className={styles.iconsContainer}>
<PlusIcon />
<Image path={navIconUrl(navIcon)} size={18} alt="" />

View File

@@ -24,10 +24,9 @@ import {
weaponBuildPage,
} from "~/utils/urls";
import { Ability } from "./Ability";
import { LinkButton } from "./Button";
import { FormWithConfirm } from "./FormWithConfirm";
import { Image } from "./Image";
import { SendouButton } from "./elements/Button";
import { LinkButton, SendouButton } from "./elements/Button";
import { SendouPopover } from "./elements/Popover";
import { EditIcon } from "./icons/Edit";
import { LockIcon } from "./icons/Lock";
@@ -221,7 +220,7 @@ export function BuildCard({
<LinkButton
className="build__small-text"
variant="minimal"
size="tiny"
size="small"
to={`new?buildId=${id}&userId=${user!.id}`}
testId="edit-build"
>

View File

@@ -1,93 +0,0 @@
import { Link } from "@remix-run/react";
import type { LinkProps } from "@remix-run/react";
import clsx from "clsx";
import * as React from "react";
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?:
| "primary"
| "success"
| "outlined"
| "outlined-success"
| "destructive"
| "minimal"
| "minimal-success"
| "minimal-destructive";
size?: "miniscule" | "tiny" | "big";
loading?: boolean;
loadingText?: string;
icon?: JSX.Element;
testId?: string;
_ref?: React.LegacyRef<HTMLButtonElement> | React.ForwardedRef<unknown>;
}
type LinkButtonProps = Pick<
ButtonProps,
"variant" | "children" | "className" | "size" | "testId" | "icon"
> &
Pick<LinkProps, "to" | "prefetch" | "preventScrollReset"> & {
"data-cy"?: string;
isExternal?: boolean;
onClick?: () => void;
};
export function LinkButton({
variant,
children,
size,
className,
to,
prefetch,
isExternal,
testId,
icon,
preventScrollReset,
onClick,
}: LinkButtonProps) {
if (isExternal) {
return (
<a
className={clsx(
"button",
variant,
{ tiny: size === "tiny", big: size === "big" },
className,
)}
href={to as string}
data-testid={testId}
target="_blank"
rel="noreferrer"
onClick={onClick}
>
{icon &&
React.cloneElement(icon, {
className: clsx("button-icon", {
lonely: !children,
}),
})}
{children}
</a>
);
}
return (
<Link
className={clsx(
"button",
variant,
{ tiny: size === "tiny", big: size === "big" },
className,
)}
to={to}
data-testid={testId}
prefetch={prefetch}
preventScrollReset={preventScrollReset}
>
{icon &&
React.cloneElement(icon, {
className: clsx("button-icon", { lonely: !children }),
})}
{children}
</Link>
);
}

View File

@@ -18,15 +18,18 @@
user-select: none;
}
.button[data-focus-visible] {
.button[data-focus-visible],
.button:focus-visible {
outline: 2px solid var(--theme);
}
.button[data-pressed] {
.button[data-pressed],
.button:active {
transform: translateY(1px);
}
.button[data-disabled] {
.button[data-disabled],
.button:disabled {
cursor: not-allowed;
opacity: 0.5;
transform: initial;

View File

@@ -1,3 +1,4 @@
import { Link, type LinkProps } from "@remix-run/react";
import clsx from "clsx";
import * as React from "react";
import {
@@ -18,7 +19,8 @@ type ButtonVariant =
| "minimal-destructive";
export interface SendouButtonProps
extends Omit<ReactAriaButtonProps, "onClick"> {
extends Omit<ReactAriaButtonProps, "onClick" | "className"> {
className?: string;
variant?: ButtonVariant;
size?: "miniscule" | "small" | "medium" | "big";
icon?: JSX.Element;
@@ -33,47 +35,128 @@ export function SendouButton({
icon,
...rest
}: SendouButtonProps) {
const variantClassname = variant ? variantToClassname(variant) : null;
return (
<ReactAriaButton
{...rest}
className={clsx(className, variantClassname, styles.button, {
[styles.small]: size === "small",
[styles.big]: size === "big",
[styles.miniscule]: size === "miniscule",
})}
className={buttonClassName({ className, variant, size })}
>
{icon &&
React.cloneElement(icon, {
className: clsx(icon.props.className, styles.buttonIcon, {
[styles.lonely]: !children,
}),
className: iconClassName(icon.props.className, children),
})}
{children}
</ReactAriaButton>
);
}
function variantToClassname(variant: ButtonVariant) {
switch (variant) {
case "primary":
return styles.primary;
case "success":
return styles.success;
case "outlined":
return styles.outlined;
case "outlined-success":
return styles.outlinedSuccess;
case "destructive":
return styles.destructive;
case "minimal":
return styles.minimal;
case "minimal-success":
return styles.minimalSuccess;
case "minimal-destructive":
return styles.minimalDestructive;
default:
return assertUnreachable(variant);
}
export interface LinkButtonProps {
to: LinkProps["to"];
prefetch?: LinkProps["prefetch"];
preventScrollReset?: LinkProps["preventScrollReset"];
isExternal?: boolean;
className?: string;
variant?: SendouButtonProps["variant"];
size?: SendouButtonProps["size"];
icon?: JSX.Element;
children?: React.ReactNode;
onClick?: React.MouseEventHandler<HTMLAnchorElement>;
testId?: string;
}
export function LinkButton({
to,
prefetch,
preventScrollReset,
isExternal,
className,
variant,
size,
icon,
children,
onClick,
testId,
}: LinkButtonProps) {
if (isExternal) {
return (
<a
className={buttonClassName({ className, variant, size })}
href={to as string}
target="_blank"
rel="noreferrer"
onClick={onClick}
data-testid={testId}
>
{icon &&
React.cloneElement(icon, {
className: iconClassName(icon.props.className, children),
})}
{children}
</a>
);
}
return (
<Link
className={buttonClassName({ className, variant, size })}
to={to}
data-testid={testId}
prefetch={prefetch}
preventScrollReset={preventScrollReset}
>
{icon &&
React.cloneElement(icon, {
className: iconClassName(icon.props.className, children),
})}
{children}
</Link>
);
}
function buttonClassName({
className,
variant,
size,
}: Pick<SendouButtonProps, "className" | "variant" | "size">) {
const variantToClassname = (variant: ButtonVariant) => {
switch (variant) {
case "primary":
return styles.primary;
case "success":
return styles.success;
case "outlined":
return styles.outlined;
case "outlined-success":
return styles.outlinedSuccess;
case "destructive":
return styles.destructive;
case "minimal":
return styles.minimal;
case "minimal-success":
return styles.minimalSuccess;
case "minimal-destructive":
return styles.minimalDestructive;
default:
return assertUnreachable(variant);
}
};
return clsx(
className,
variant ? variantToClassname(variant) : null,
styles.button,
{
[styles.small]: size === "small",
[styles.big]: size === "big",
[styles.miniscule]: size === "miniscule",
},
);
}
function iconClassName(
baseClassName: string | undefined,
children: React.ReactNode,
) {
return clsx(baseClassName, styles.buttonIcon, {
[styles.lonely]: !children,
});
}

View File

@@ -6,8 +6,8 @@ import { useTranslation } from "react-i18next";
import type { z } from "zod";
import { logger } from "~/utils/logger";
import type { ActionError } from "~/utils/remix.server";
import { LinkButton } from "../Button";
import { SubmitButton } from "../SubmitButton";
import { LinkButton } from "../elements/Button";
export function SendouForm<T extends z.ZodTypeAny>({
schema,
@@ -65,7 +65,7 @@ export function SendouForm<T extends z.ZodTypeAny>({
<LinkButton
variant="minimal-destructive"
to={cancelLink}
size="tiny"
size="small"
>
{t("common:actions.cancel")}
</LinkButton>

View File

@@ -12,7 +12,7 @@ import { NOTIFICATIONS } from "~/features/notifications/notifications-contants";
import type { RootLoaderData } from "~/root";
import { NOTIFICATIONS_URL } from "~/utils/urls";
import { useMarkNotificationsAsSeen } from "../../features/notifications/notifications-hooks";
import { LinkButton } from "../Button";
import { LinkButton } from "../elements/Button";
import { SendouButton } from "../elements/Button";
import { SendouPopover } from "../elements/Popover";
import { BellIcon } from "../icons/Bell";
@@ -129,7 +129,7 @@ function NotificationsFooter() {
<hr className={styles.divider} />
<LinkButton
variant="minimal"
size="tiny"
size="small"
to={NOTIFICATIONS_URL}
className="mt-1-5"
testId="notifications-see-all-button"

View File

@@ -1,6 +1,6 @@
import { useTranslation } from "react-i18next";
import { SUPPORT_PAGE } from "~/utils/urls";
import { LinkButton } from "../Button";
import { LinkButton } from "../elements/Button";
import { HamburgerIcon } from "../icons/Hamburger";
import { HeartIcon } from "../icons/Heart";
import { AnythingAdder } from "./AnythingAdder";
@@ -23,7 +23,7 @@ export function TopRightButtons({
{showSupport ? (
<LinkButton
to={SUPPORT_PAGE}
size="tiny"
size="small"
icon={<HeartIcon />}
variant="outlined"
>

View File

@@ -3,9 +3,9 @@ import clsx from "clsx";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Avatar } from "~/components/Avatar";
import { LinkButton } from "~/components/Button";
import { FormWithConfirm } from "~/components/FormWithConfirm";
import { Pagination } from "~/components/Pagination";
import { LinkButton } from "~/components/elements/Button";
import { SendouButton } from "~/components/elements/Button";
import { SendouDialog } from "~/components/elements/Dialog";
import { CrossIcon } from "~/components/icons/Cross";
@@ -191,7 +191,7 @@ function ImagePreview({
>
<LinkButton
to={newArtPage(art.id)}
size="tiny"
size="small"
variant="outlined"
icon={<EditIcon />}
>

View File

@@ -2,7 +2,7 @@ import { Outlet, useLoaderData } from "@remix-run/react";
import clsx from "clsx";
import { useTranslation } from "react-i18next";
import { Badge } from "~/components/Badge";
import { LinkButton } from "~/components/Button";
import { LinkButton } from "~/components/elements/Button";
import { useHasPermission, useHasRole } from "~/modules/permissions/hooks";
import type { SerializeFrom } from "~/utils/remix";
import { badgeExplanationText } from "../badges-utils";
@@ -60,7 +60,7 @@ export default function BadgeDetailsPage() {
</div>
</div>
{isStaff || canManageBadge ? (
<LinkButton to="edit" variant="outlined" size="tiny">
<LinkButton to="edit" variant="outlined" size="small">
Edit
</LinkButton>
) : null}

View File

@@ -8,8 +8,8 @@ import { nanoid } from "nanoid";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { BuildCard } from "~/components/BuildCard";
import { LinkButton } from "~/components/Button";
import { Main } from "~/components/Main";
import { LinkButton } from "~/components/elements/Button";
import { SendouButton } from "~/components/elements/Button";
import { SendouMenu, SendouMenuItem } from "~/components/elements/Menu";
import { BeakerFilledIcon } from "~/components/icons/BeakerFilled";
@@ -320,7 +320,7 @@ export default function WeaponsBuildsPage() {
to={weaponBuildStatsPage(data.slug)}
variant="outlined"
icon={<ChartBarIcon />}
size="tiny"
size="small"
>
{t("builds:linkButton.abilityStats")}
</LinkButton>
@@ -328,7 +328,7 @@ export default function WeaponsBuildsPage() {
to={weaponBuildPopularPage(data.slug)}
variant="outlined"
icon={<FireIcon />}
size="tiny"
size="small"
>
{t("builds:linkButton.popularBuilds")}
</LinkButton>
@@ -355,7 +355,7 @@ export default function WeaponsBuildsPage() {
data.builds.length === data.limit && (
<LinkButton
className="m-0-auto"
size="tiny"
size="small"
to={loadMoreLink()}
preventScrollReset
>

View File

@@ -5,7 +5,6 @@ import clsx from "clsx";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Avatar } from "~/components/Avatar";
import { LinkButton } from "~/components/Button";
import { FormWithConfirm } from "~/components/FormWithConfirm";
import { Image } from "~/components/Image";
import { Main } from "~/components/Main";
@@ -13,6 +12,7 @@ import { MapPoolStages } from "~/components/MapPoolSelector";
import { Placement } from "~/components/Placement";
import { Section } from "~/components/Section";
import { Table } from "~/components/Table";
import { LinkButton } from "~/components/elements/Button";
import { SendouButton } from "~/components/elements/Button";
import { useUser } from "~/features/auth/core/user";
import { MapPool } from "~/features/map-list-generator/core/map-pool";
@@ -128,7 +128,7 @@ export default function CalendarEventPage() {
<LinkButton
to={data.event.discordUrl}
variant="outlined"
size="tiny"
size="small"
isExternal
>
Discord
@@ -137,13 +137,16 @@ export default function CalendarEventPage() {
<LinkButton
to={data.event.bracketUrl}
variant="outlined"
size="tiny"
size="small"
isExternal
>
{resolveBaseUrl(data.event.bracketUrl)}
</LinkButton>
{canEditCalendarEvent({ user, event: data.event }) && (
<LinkButton size="tiny" to={calendarEditPage(data.event.eventId)}>
<LinkButton
size="small"
to={calendarEditPage(data.event.eventId)}
>
{t("common:actions.edit")}
</LinkButton>
)}
@@ -153,7 +156,7 @@ export default function CalendarEventPage() {
startTimes: data.event.startTimes,
}) && (
<LinkButton
size="tiny"
size="small"
to={calendarReportWinnersPage(data.event.eventId)}
>
{t("calendar:actions.reportWinners")}
@@ -276,7 +279,7 @@ function MapPoolInfo() {
className="event__create-map-list-link"
to={readonlyMapsPage(data.event.eventId)}
variant="outlined"
size="tiny"
size="small"
>
<Image alt="" path={navIconUrl("maps")} width={22} height={22} />
{t("calendar:createMapList")}

View File

@@ -2,8 +2,8 @@ import type { MetaFunction } from "@remix-run/node";
import * as React from "react";
import { Trans, useTranslation } from "react-i18next";
import { Badge } from "~/components/Badge";
import { LinkButton } from "~/components/Button";
import { Main } from "~/components/Main";
import { LinkButton } from "~/components/elements/Button";
import { CheckmarkIcon } from "~/components/icons/Checkmark";
import { FF_SCRIMS_ENABLED } from "~/features/scrims/scrims-constants";
import { metaTags } from "~/utils/remix";

View File

@@ -1,12 +1,12 @@
import { Link, useFetcher, useLoaderData } from "@remix-run/react";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { LinkButton } from "~/components/Button";
import { FormMessage } from "~/components/FormMessage";
import { WeaponImage } from "~/components/Image";
import { Label } from "~/components/Label";
import { Main } from "~/components/Main";
import { SubmitButton } from "~/components/SubmitButton";
import { LinkButton } from "~/components/elements/Button";
import { ArrowLeftIcon } from "~/components/icons/ArrowLeft";
import type { Tables } from "~/db/tables";
import { useUser } from "~/features/auth/core/user";

View File

@@ -4,10 +4,10 @@ import { Link, Outlet, useLoaderData, useSearchParams } from "@remix-run/react";
import clsx from "clsx";
import { Alert } from "~/components/Alert";
import { Avatar } from "~/components/Avatar";
import { LinkButton } from "~/components/Button";
import { Catcher } from "~/components/Catcher";
import { FormWithConfirm } from "~/components/FormWithConfirm";
import { RelativeTime } from "~/components/RelativeTime";
import { LinkButton } from "~/components/elements/Button";
import { TrashIcon } from "~/components/icons/Trash";
import type { Tables } from "~/db/tables";
import { useUser } from "~/features/auth/core/user";
@@ -221,7 +221,7 @@ function SuggestedUser({
}) ? (
<LinkButton
className="plus__comment-button"
size="tiny"
size="small"
variant="outlined"
to={`comment/${tier}/${suggestion.suggested.id}?tier=${tier}`}
prefetch="render"

View File

@@ -7,11 +7,11 @@ import * as R from "remeda";
import type { z } from "zod";
import { AddNewButton } from "~/components/AddNewButton";
import { Avatar } from "~/components/Avatar";
import { LinkButton } from "~/components/Button";
import { Divider } from "~/components/Divider";
import { FormWithConfirm } from "~/components/FormWithConfirm";
import { Table } from "~/components/Table";
import TimePopover from "~/components/TimePopover";
import { LinkButton } from "~/components/elements/Button";
import { SendouButton } from "~/components/elements/Button";
import { SendouDialog } from "~/components/elements/Dialog";
import { SendouPopover } from "~/components/elements/Popover";
@@ -92,7 +92,7 @@ export default function ScrimsPage() {
<Main className="stack lg">
<div className="stack horizontal justify-between items-center">
<LinkButton
size="tiny"
size="small"
to={associationsPage()}
className={clsx("mr-auto", { invisible: !user })}
variant="outlined"
@@ -583,7 +583,7 @@ function ContactButton({ postId }: { postId: number }) {
return (
<LinkButton
to={scrimPage(postId)}
size="tiny"
size="small"
className="w-max ml-auto"
icon={<SpeechBubbleFilledIcon />}
>

View File

@@ -13,7 +13,6 @@ import { Flipped, Flipper } from "react-flip-toolkit";
import { useTranslation } from "react-i18next";
import { Alert } from "~/components/Alert";
import { Avatar } from "~/components/Avatar";
import { LinkButton } from "~/components/Button";
import { WeaponCombobox } from "~/components/Combobox";
import { Divider } from "~/components/Divider";
import { FormWithConfirm } from "~/components/FormWithConfirm";
@@ -21,6 +20,7 @@ import { Image, ModeImage, StageImage, WeaponImage } from "~/components/Image";
import { Main } from "~/components/Main";
import { NewTabs } from "~/components/NewTabs";
import { SubmitButton } from "~/components/SubmitButton";
import { LinkButton } from "~/components/elements/Button";
import { SendouButton } from "~/components/elements/Button";
import { SendouPopover } from "~/components/elements/Popover";
import { SendouSwitch } from "~/components/elements/Switch";
@@ -748,7 +748,7 @@ function BottomSection({
<LinkButton
to={SENDOUQ_RULES_PAGE}
variant="outlined"
size="tiny"
size="small"
icon={<ScaleIcon />}
>
{t("q:front.nav.rules.title")}
@@ -760,7 +760,7 @@ function BottomSection({
isExternal
to={SENDOU_INK_DISCORD_URL}
variant="outlined"
size="tiny"
size="small"
icon={<DiscordIcon />}
>
{t("q:match.helpdesk")}

View File

@@ -5,10 +5,10 @@ import * as React from "react";
import { Flipped } from "react-flip-toolkit";
import { useTranslation } from "react-i18next";
import { Avatar } from "~/components/Avatar";
import { LinkButton } from "~/components/Button";
import { FormWithConfirm } from "~/components/FormWithConfirm";
import { Image, ModeImage, TierImage, WeaponImage } from "~/components/Image";
import { SubmitButton } from "~/components/SubmitButton";
import { LinkButton } from "~/components/elements/Button";
import { SendouButton } from "~/components/elements/Button";
import { SendouPopover } from "~/components/elements/Popover";
import { EditIcon } from "~/components/icons/Edit";

View File

@@ -5,11 +5,11 @@ import * as React from "react";
import { Flipper } from "react-flip-toolkit";
import { useTranslation } from "react-i18next";
import { Alert } from "~/components/Alert";
import { LinkButton } from "~/components/Button";
import { Image } from "~/components/Image";
import { Main } from "~/components/Main";
import { NewTabs } from "~/components/NewTabs";
import { SubmitButton } from "~/components/SubmitButton";
import { LinkButton } from "~/components/elements/Button";
import { useUser } from "~/features/auth/core/user";
import { Chat, useChat } from "~/features/chat/components/Chat";
import { useAutoRefresh } from "~/hooks/useAutoRefresh";
@@ -144,7 +144,7 @@ function InfoText() {
<div className="stack sm horizontal">
<LinkButton
to={SENDOUQ_SETTINGS_PAGE}
size="tiny"
size="small"
variant="outlined"
className="stack horizontal xs"
>
@@ -177,7 +177,7 @@ function StreamsLinkButton() {
return (
<LinkButton
to={SENDOUQ_STREAMS_PAGE}
size="tiny"
size="small"
variant="outlined"
className="stack horizontal xs"
>
@@ -493,7 +493,7 @@ function JoinQueuePrompt() {
const { t } = useTranslation(["q"]);
return (
<LinkButton to={SENDOUQ_PAGE} variant="minimal" size="tiny">
<LinkButton to={SENDOUQ_PAGE} variant="minimal" size="small">
{t("q:looking.joinQPrompt")}
</LinkButton>
);

View File

@@ -4,13 +4,13 @@ import clsx from "clsx";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Alert } from "~/components/Alert";
import { LinkButton } from "~/components/Button";
import { Flag } from "~/components/Flag";
import { FormMessage } from "~/components/FormMessage";
import { FriendCodeInput } from "~/components/FriendCodeInput";
import { Image } from "~/components/Image";
import { Main } from "~/components/Main";
import { SubmitButton } from "~/components/SubmitButton";
import { LinkButton } from "~/components/elements/Button";
import { SendouDialog } from "~/components/elements/Dialog";
import { UserIcon } from "~/components/icons/User";
import { UsersIcon } from "~/components/icons/Users";
@@ -465,7 +465,11 @@ function PreviewQueueButton() {
}
return (
<LinkButton to={SENDOUQ_LOOKING_PREVIEW_PAGE} variant="minimal" size="tiny">
<LinkButton
to={SENDOUQ_LOOKING_PREVIEW_PAGE}
variant="minimal"
size="small"
>
{t("q:front.preview")}
</LinkButton>
);

View File

@@ -5,12 +5,12 @@ import React from "react";
import { useTranslation } from "react-i18next";
import * as R from "remeda";
import { Avatar } from "~/components/Avatar";
import { LinkButton } from "~/components/Button";
import { Flag } from "~/components/Flag";
import { FormWithConfirm } from "~/components/FormWithConfirm";
import { WeaponImage } from "~/components/Image";
import { Main } from "~/components/Main";
import { SubmitButton } from "~/components/SubmitButton";
import { LinkButton } from "~/components/elements/Button";
import { SendouButton } from "~/components/elements/Button";
import { BskyIcon } from "~/components/icons/Bsky";
import { EditIcon } from "~/components/icons/Edit";
@@ -233,7 +233,7 @@ function ActionButtons() {
) : null}
{isTeamManager({ user, team }) || isAdmin ? (
<LinkButton
size="tiny"
size="small"
to={manageTeamRosterPage(team.customUrl)}
variant="outlined"
prefetch="intent"
@@ -245,7 +245,7 @@ function ActionButtons() {
) : null}
{isTeamManager({ user, team }) || isAdmin ? (
<LinkButton
size="tiny"
size="small"
to={editTeamPage(team.customUrl)}
variant="outlined"
prefetch="intent"

View File

@@ -2,8 +2,8 @@ import { useFetcher } from "@remix-run/react";
import clsx from "clsx";
import { sub } from "date-fns";
import * as React from "react";
import { LinkButton } from "~/components/Button";
import { SubmitButton } from "~/components/SubmitButton";
import { LinkButton } from "~/components/elements/Button";
import { SendouButton } from "~/components/elements/Button";
import { SendouPopover } from "~/components/elements/Popover";
import { CheckmarkIcon } from "~/components/icons/Checkmark";
@@ -31,7 +31,7 @@ export function TournamentTeamActions() {
matchId: status.matchId,
})}
variant="minimal"
size="tiny"
size="small"
>
Go to match
</LinkButton>

View File

@@ -2,8 +2,8 @@ import { useLoaderData, useRevalidator } from "@remix-run/react";
import clsx from "clsx";
import * as React from "react";
import { useEventSource } from "remix-utils/sse/react";
import { LinkButton } from "~/components/Button";
import { containerClassName } from "~/components/Main";
import { LinkButton } from "~/components/elements/Button";
import { ArrowLongLeftIcon } from "~/components/icons/ArrowLongLeft";
import { useUser } from "~/features/auth/core/user";
import { ConnectedChat } from "~/features/chat/components/Chat";
@@ -90,7 +90,7 @@ export default function TournamentMatchPage() {
groupId: data.match.groupId,
})}
variant="outlined"
size="tiny"
size="small"
className="w-max"
icon={<ArrowLongLeftIcon />}
testId="back-to-bracket-button"

View File

@@ -1,6 +1,6 @@
import type { SerializeFrom } from "@remix-run/node";
import clsx from "clsx";
import { LinkButton } from "~/components/Button";
import { LinkButton } from "~/components/elements/Button";
import type { MonthYear } from "~/features/plus-voting/core";
import { useIsMounted } from "~/hooks/useIsMounted";
import { databaseTimestampToDate, nullPaddedDatesOfMonth } from "~/utils/dates";

View File

@@ -2,12 +2,12 @@ import type { MetaFunction, SerializeFrom } from "@remix-run/node";
import { Link, useLoaderData, useSearchParams } from "@remix-run/react";
import { useTranslation } from "react-i18next";
import { Avatar } from "~/components/Avatar";
import { LinkButton } from "~/components/Button";
import { Divider } from "~/components/Divider";
import { Main } from "~/components/Main";
import { NewTabs } from "~/components/NewTabs";
import { Pagination } from "~/components/Pagination";
import { Placement } from "~/components/Placement";
import { LinkButton } from "~/components/elements/Button";
import { EditIcon } from "~/components/icons/Edit";
import { BadgeDisplay } from "~/features/badges/components/BadgeDisplay";
import { useIsMounted } from "~/hooks/useIsMounted";
@@ -119,7 +119,7 @@ function LogoHeader() {
<LinkButton
to={tournamentOrganizationEditPage(data.organization.slug)}
icon={<EditIcon />}
size="tiny"
size="small"
variant="outlined"
testId="edit-org-button"
>
@@ -343,7 +343,7 @@ function SeriesButton({
return (
<LinkButton
variant="minimal"
size="tiny"
size="small"
to={`?series=${seriesId ?? "all"}`}
>
{children}

View File

@@ -2,11 +2,11 @@ import { Link, useLoaderData } from "@remix-run/react";
import React from "react";
import { useTranslation } from "react-i18next";
import { Avatar } from "~/components/Avatar";
import { LinkButton } from "~/components/Button";
import { Flag } from "~/components/Flag";
import { FormWithConfirm } from "~/components/FormWithConfirm";
import { WeaponImage } from "~/components/Image";
import { Redirect } from "~/components/Redirect";
import { LinkButton } from "~/components/elements/Button";
import { SendouButton } from "~/components/elements/Button";
import { SendouPopover } from "~/components/elements/Popover";
import { MicrophoneIcon } from "~/components/icons/Microphone";
@@ -67,7 +67,7 @@ function AddOrEditSubButton() {
}
return (
<LinkButton to="new" size="tiny">
<LinkButton to="new" size="small">
{buttonText}
</LinkButton>
);

View File

@@ -3,7 +3,6 @@ import clsx from "clsx";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Avatar } from "~/components/Avatar";
import { LinkButton } from "~/components/Button";
import { Divider } from "~/components/Divider";
import { FormMessage } from "~/components/FormMessage";
import { FormWithConfirm } from "~/components/FormWithConfirm";
@@ -12,6 +11,7 @@ import { Label } from "~/components/Label";
import { containerClassName } from "~/components/Main";
import { Redirect } from "~/components/Redirect";
import { SubmitButton } from "~/components/SubmitButton";
import { LinkButton } from "~/components/elements/Button";
import { SendouButton } from "~/components/elements/Button";
import { SendouDialog } from "~/components/elements/Dialog";
import { UserSearch } from "~/components/elements/UserSearch";
@@ -56,7 +56,7 @@ export default function TournamentAdminPage() {
<div className="stack horizontal items-end">
<LinkButton
to={tournamentEditPage(tournament.ctx.eventId)}
size="tiny"
size="small"
variant="outlined"
testId="edit-event-info-button"
>

View File

@@ -2,9 +2,9 @@ import { Form, useLoaderData } from "@remix-run/react";
import React from "react";
import { useTranslation } from "react-i18next";
import { Alert } from "~/components/Alert";
import { LinkButton } from "~/components/Button";
import { FriendCodeInput } from "~/components/FriendCodeInput";
import { SubmitButton } from "~/components/SubmitButton";
import { LinkButton } from "~/components/elements/Button";
import { useUser } from "~/features/auth/core/user";
import invariant from "~/utils/invariant";
import { assertUnreachable } from "~/utils/types";
@@ -60,7 +60,7 @@ export default function JoinTeamPage() {
<Alert variation="WARNING" alertClassName="w-max">
<div className="stack horizontal sm items-center flex-wrap justify-center text-center">
This tournament requires you to have an in-game name set{" "}
<LinkButton to={userEditProfilePage(user)} size="tiny">
<LinkButton to={userEditProfilePage(user)} size="small">
Edit profile
</LinkButton>
</div>

View File

@@ -7,7 +7,6 @@ import { useTranslation } from "react-i18next";
import { useCopyToClipboard } from "react-use";
import { Alert } from "~/components/Alert";
import { Avatar } from "~/components/Avatar";
import { LinkButton } from "~/components/Button";
import { Divider } from "~/components/Divider";
import { FormWithConfirm } from "~/components/FormWithConfirm";
import { FriendCodeInput } from "~/components/FriendCodeInput";
@@ -19,6 +18,7 @@ import { MapPoolStages } from "~/components/MapPoolSelector";
import { NewTabs } from "~/components/NewTabs";
import { Section } from "~/components/Section";
import { SubmitButton } from "~/components/SubmitButton";
import { LinkButton } from "~/components/elements/Button";
import { SendouButton } from "~/components/elements/Button";
import { SendouPopover } from "~/components/elements/Popover";
import { CheckmarkIcon } from "~/components/icons/Checkmark";
@@ -223,7 +223,7 @@ function TournamentRegisterInfoTabs() {
<LinkButton
to={tournament.ctx.discordUrl}
variant="outlined"
size="tiny"
size="small"
isExternal
icon={<DiscordIcon />}
>
@@ -282,7 +282,7 @@ function TournamentRegisterInfoTabs() {
<Alert variation="WARNING">
<div className="stack horizontal sm items-center flex-wrap justify-center text-center">
This tournament requires you to have an in-game name set{" "}
<LinkButton to={userEditProfilePage(user)} size="tiny">
<LinkButton to={userEditProfilePage(user)} size="small">
Edit profile
</LinkButton>
</div>
@@ -1328,7 +1328,7 @@ function TOPickedMapPoolInfo() {
className="event__create-map-list-link"
to={readonlyMapsPage(tournament.ctx.eventId)}
variant="outlined"
size="tiny"
size="small"
>
<Image alt="" path={navIconUrl("maps")} width={22} height={22} />
{t("calendar:createMapList")}

View File

@@ -1,6 +1,6 @@
import { useLoaderData, useMatches } from "@remix-run/react";
import { useTranslation } from "react-i18next";
import { LinkButton } from "~/components/Button";
import { LinkButton } from "~/components/elements/Button";
import { useUser } from "~/features/auth/core/user";
import { UserResultsTable } from "~/features/user-page/components/UserResultsTable";
import { useSearchParamState } from "~/hooks/useSearchParamState";
@@ -44,7 +44,7 @@ export default function UserResultsPage() {
<LinkButton
to={userResultsEditHighlightsPage(user)}
className="ml-auto"
size="tiny"
size="small"
>
{t("results.highlights.choose")}
</LinkButton>

View File

@@ -3,11 +3,11 @@ import { useLoaderData } from "@remix-run/react";
import clsx from "clsx";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { LinkButton } from "~/components/Button";
import { FormWithConfirm } from "~/components/FormWithConfirm";
import { Image, WeaponImage } from "~/components/Image";
import { Main } from "~/components/Main";
import { YouTubeEmbed } from "~/components/YouTubeEmbed";
import { LinkButton } from "~/components/elements/Button";
import { EditIcon } from "~/components/icons/Edit";
import { TrashIcon } from "~/components/icons/Trash";
import { useUser } from "~/features/auth/core/user";
@@ -120,7 +120,7 @@ export default function VodPage() {
<div className="stack horizontal md">
<LinkButton
to={newVodPage(data.vod.id)}
size="tiny"
size="small"
testId="edit-vod-button"
icon={<EditIcon />}
>

View File

@@ -26,128 +26,6 @@ a {
text-decoration: none;
}
.button {
display: flex;
width: auto;
align-items: center;
justify-content: center;
border: 2px solid var(--theme);
border-radius: var(--rounded-sm);
appearance: none;
background: var(--theme);
color: var(--button-text);
cursor: pointer;
font-size: var(--fonts-sm);
font-weight: var(--bold);
line-height: 1.2;
outline-offset: 2px;
padding-block: var(--s-1-5);
padding-inline: var(--s-2-5);
user-select: none;
}
.button:focus-visible {
outline: 2px solid var(--theme);
}
.button:active {
transform: translateY(1px);
}
.button:disabled {
cursor: not-allowed;
opacity: 0.5;
transform: initial;
}
.button.outlined {
background-color: var(--theme-very-transparent);
color: var(--theme);
}
.button.outlined-success {
border-color: var(--theme-success);
background-color: transparent;
color: var(--theme-success);
}
.button.tiny {
font-size: var(--fonts-xs);
padding-block: var(--s-1);
padding-inline: var(--s-2);
}
.button.miniscule {
font-size: var(--fonts-xxs);
padding-block: var(--s-1);
padding-inline: var(--s-2);
}
.button.big {
font-size: var(--fonts-md);
padding-block: var(--s-2-5);
padding-inline: var(--s-6);
}
.button.minimal {
padding: 0;
border: none;
background-color: transparent;
color: var(--theme);
outline: initial;
}
.button.minimal:focus-visible {
outline: 2px solid var(--theme);
}
.button.minimal-success {
padding: 0;
border: none;
background-color: transparent;
color: var(--theme-success);
}
.button.success {
border-color: var(--theme-success);
background-color: var(--theme-success);
outline-color: var(--theme-success);
}
.button.destructive {
border-color: var(--theme-error);
background-color: transparent;
color: var(--theme-error);
outline-color: var(--theme-error);
}
.button.minimal-destructive {
padding: 0;
border: none;
background-color: transparent;
color: var(--theme-error);
outline-color: var(--theme-error);
}
.button.loading {
cursor: not-allowed;
opacity: 0.6;
}
.button-icon {
width: 1.25rem;
margin-inline-end: var(--s-1-5);
}
.button-icon.lonely {
margin-inline-end: 0 !important;
}
.button.tiny > .button-icon {
width: 1rem;
margin-inline-end: var(--s-1);
}
/* :not([name="text"]) workaround not to select textarea on map planner */
textarea:not(.plain, [name="text"]) {
width: 18rem;