mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-14 23:26:15 -05:00
Refactor LinkButton to use the new SendouButton API
This commit is contained in:
@@ -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="" />
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
@@ -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 />}
|
||||
>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
>
|
||||
|
||||
@@ -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")}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 />}
|
||||
>
|
||||
|
||||
@@ -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")}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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")}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 />}
|
||||
>
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user