mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-07-19 09:02:43 -05:00
Add a time component (#2347)
* Add Time component * Use the Time component on the tournament and scrims pages * fix formatting issues * renamed the Time component to TimePopover * change the Time component trigger to a button * remove unused import * fix button styling
This commit is contained in:
parent
1c26ff31c8
commit
2fd3ff1240
83
app/components/TimePopover.tsx
Normal file
83
app/components/TimePopover.tsx
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import { useRef, useState } from "react";
|
||||
import * as React from "react";
|
||||
import { Dialog } from "react-aria-components";
|
||||
import { Popover } from "react-aria-components";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useCopyToClipboard } from "react-use";
|
||||
import { SendouButton } from "./elements/Button";
|
||||
import { CheckmarkIcon } from "./icons/Checkmark";
|
||||
import { ClipboardIcon } from "./icons/Clipboard";
|
||||
|
||||
export default function TimePopover({
|
||||
time,
|
||||
options = {
|
||||
minute: "numeric",
|
||||
hour: "numeric",
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
},
|
||||
}: {
|
||||
time: Date;
|
||||
options?: Intl.DateTimeFormatOptions;
|
||||
}) {
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const triggerRef = useRef(null);
|
||||
|
||||
const { t } = useTranslation(["common"]);
|
||||
|
||||
const [state, copyToClipboard] = useCopyToClipboard();
|
||||
const [copySuccess, setCopySuccess] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!state.value) return;
|
||||
|
||||
setCopySuccess(true);
|
||||
const timeout = setTimeout(() => setCopySuccess(false), 2000);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
}, [state]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
ref={triggerRef}
|
||||
className="dotted clickable text-only-button"
|
||||
onClick={() => {
|
||||
setOpen(true);
|
||||
}}
|
||||
>
|
||||
{time.toLocaleString(i18n.language, options)}
|
||||
</button>
|
||||
<Popover
|
||||
isOpen={open}
|
||||
className={"sendou-popover-content"}
|
||||
onOpenChange={setOpen}
|
||||
triggerRef={triggerRef}
|
||||
>
|
||||
<Dialog>
|
||||
<div className="stack sm">
|
||||
<div className="text-center">
|
||||
{time.toLocaleTimeString(i18n.language, {
|
||||
timeZoneName: "long",
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
})}
|
||||
</div>
|
||||
<SendouButton
|
||||
size="miniscule"
|
||||
variant="minimal"
|
||||
onPress={() => copyToClipboard(`<t:${time.valueOf() / 1000}:F>`)}
|
||||
icon={copySuccess ? <CheckmarkIcon /> : <ClipboardIcon />}
|
||||
>
|
||||
{t("common:actions.copyTimestampForDiscord")}
|
||||
</SendouButton>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Popover>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ import type { ScrimPost as ScrimPostType } from "../scrims-types";
|
|||
import { loader } from "../loaders/scrims.$id.server";
|
||||
export { loader };
|
||||
|
||||
import TimePopover from "~/components/TimePopover";
|
||||
import styles from "./scrims.$id.module.css";
|
||||
|
||||
export const handle: SendouRouteHandle = {
|
||||
|
|
@ -50,19 +51,21 @@ export default function ScrimPage() {
|
|||
function ScrimHeader() {
|
||||
const { t } = useTranslation(["scrims"]);
|
||||
const data = useLoaderData<typeof loader>();
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="line-height-tight" data-testid="match-header">
|
||||
<h2 className="text-lg" suppressHydrationWarning>
|
||||
{databaseTimestampToDate(data.post.at).toLocaleString(i18n.language, {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
})}
|
||||
<TimePopover
|
||||
time={databaseTimestampToDate(data.post.at)}
|
||||
options={{
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
}}
|
||||
/>
|
||||
</h2>
|
||||
<div className="text-lighter text-xs font-bold">
|
||||
{t("scrims:page.scheduledScrim")}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ import {
|
|||
} from "../tournament-utils";
|
||||
import { useTournament } from "./to.$id";
|
||||
|
||||
import TimePopover from "~/components/TimePopover";
|
||||
import { action } from "../actions/to.$id.register.server";
|
||||
import { loader } from "../loaders/to.$id.register.server";
|
||||
export { loader, action };
|
||||
|
|
@ -66,7 +67,6 @@ export { loader, action };
|
|||
export default function TournamentRegisterPage() {
|
||||
const user = useUser();
|
||||
const isMounted = useIsMounted();
|
||||
const { i18n } = useTranslation();
|
||||
const tournament = useTournament();
|
||||
|
||||
const startsAtEvenHour = tournament.ctx.startTime.getMinutes() === 0;
|
||||
|
|
@ -123,15 +123,17 @@ export default function TournamentRegisterPage() {
|
|||
<div className="tournament__by mt-2">
|
||||
<div className="stack horizontal xs items-center">
|
||||
<ClockIcon className="tournament__info__icon" />{" "}
|
||||
{isMounted
|
||||
? tournament.ctx.startTime.toLocaleString(i18n.language, {
|
||||
timeZoneName: "short",
|
||||
{isMounted ? (
|
||||
<TimePopover
|
||||
time={tournament.ctx.startTime}
|
||||
options={{
|
||||
minute: startsAtEvenHour ? undefined : "numeric",
|
||||
hour: "numeric",
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
})
|
||||
: null}
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
|
|
|||
|
|
@ -252,6 +252,21 @@ details summary {
|
|||
user-select: none;
|
||||
}
|
||||
|
||||
.text-only-button {
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.dotted {
|
||||
text-decoration-style: dotted;
|
||||
text-decoration-line: underline;
|
||||
text-decoration-thickness: 2px;
|
||||
}
|
||||
|
||||
.summary {
|
||||
border-radius: var(--rounded);
|
||||
background-color: var(--bg-darker-transparent);
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@
|
|||
"actions.showMore": "",
|
||||
"actions.showLess": "",
|
||||
"actions.copyToClipboard": "In die Zwischenablage kopieren",
|
||||
"actions.copyTimestampForDiscord": "Discord-Zeitstempel kopieren",
|
||||
"actions.create": "Erstellen",
|
||||
"actions.close": "Schließen",
|
||||
"actions.cancel": "Abbrechen",
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@
|
|||
"actions.showMore": "Show more",
|
||||
"actions.showLess": "Show less",
|
||||
"actions.copyToClipboard": "Copy to clipboard",
|
||||
"actions.copyTimestampForDiscord": "Copy Discord timestamp",
|
||||
"actions.create": "Create",
|
||||
"actions.close": "Close",
|
||||
"actions.cancel": "Cancel",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user