sendou.ink/app/form/fields/FormFieldWrapper.tsx
Kalle 1c3d6da4d1
Some checks are pending
E2E Tests / e2e (push) Waiting to run
Tests and checks on push / run-checks-and-tests (push) Waiting to run
Updates translation progress / update-translation-progress-issue (push) Waiting to run
New bracket progression form UI (#3309)
2026-08-06 10:58:47 +03:00

120 lines
2.4 KiB
TypeScript

import type * as React from "react";
import { useTranslation } from "react-i18next";
import { FormMessage } from "~/components/FormMessage";
import { Label } from "~/components/Label";
import { errorMessageId, infoMessageId } from "../utils";
import styles from "./FormFieldWrapper.module.css";
export function useTranslatedTexts({
label,
error,
bottomText,
}: {
label?: string;
error?: string;
bottomText?: string;
}) {
const { t } = useTranslation(["forms"]);
return {
translatedLabel: label?.includes(":") ? t(label as never) : label,
translatedError: error?.includes(":") ? t(error as never) : error,
translatedBottomText: bottomText?.includes(":")
? t(bottomText as never)
: bottomText,
};
}
export function FormFieldMessages({
name,
error,
bottomText,
}: {
name?: string;
error?: string;
bottomText?: string;
}) {
const { translatedError, translatedBottomText } = useTranslatedTexts({
error,
bottomText,
});
return (
<>
{translatedError ? (
<FormMessage
type="error"
spaced={false}
id={name ? errorMessageId(name) : undefined}
>
{translatedError}
</FormMessage>
) : null}
{translatedBottomText ? (
<FormMessage
type="info"
spaced={false}
id={name ? infoMessageId(name) : undefined}
>
{translatedBottomText}
</FormMessage>
) : null}
</>
);
}
interface FormFieldWrapperProps {
id: string;
name?: string;
label?: string;
/** Extra element rendered next to the label, e.g. an `<InfoPopover />` explaining the field's syntax. */
labelPopover?: React.ReactNode;
required?: boolean;
error?: string;
bottomText?: string;
valueLimits?: { current: number; max: number };
children: React.ReactNode;
}
export function FormFieldWrapper({
id,
name,
label,
labelPopover,
required,
error,
bottomText,
valueLimits,
children,
}: FormFieldWrapperProps) {
const { translatedLabel } = useTranslatedTexts({ label });
const labelElement = translatedLabel ? (
<Label
htmlFor={id}
required={required}
valueLimits={valueLimits}
spaced={false}
>
{translatedLabel}
</Label>
) : null;
return (
<div className={styles.root}>
<div className="stack xs">
{labelElement && labelPopover ? (
<div className="stack horizontal xs items-center">
{labelElement}
{labelPopover}
</div>
) : (
labelElement
)}
{children}
<FormFieldMessages name={name} error={error} bottomText={bottomText} />
</div>
</div>
);
}