From 8cb21b1590a6db211553c6279b76ed3ea205fcb4 Mon Sep 17 00:00:00 2001 From: Timothy Armes Date: Mon, 2 Oct 2023 14:24:25 +0200 Subject: [PATCH] Improve mobile experience on the calculator --- app/[locale]/elo/ManualEloForm.tsx | 9 ++- app/[locale]/layout.tsx | 8 ++- components/form/RadioGroupField.tsx | 94 +++++++++++++++++++++++++++++ components/form/TextField.tsx | 2 +- 4 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 components/form/RadioGroupField.tsx diff --git a/app/[locale]/elo/ManualEloForm.tsx b/app/[locale]/elo/ManualEloForm.tsx index 5442371..7f5a6f3 100644 --- a/app/[locale]/elo/ManualEloForm.tsx +++ b/app/[locale]/elo/ManualEloForm.tsx @@ -8,6 +8,7 @@ import { IoAdd, IoCloseOutline } from "react-icons/io5"; import { twMerge } from "tailwind-merge"; import { z } from "zod"; +import { RadioGroupField } from "@/components/form/RadioGroupField"; import { SelectField } from "@/components/form/SelectField"; import { TextField } from "@/components/form/TextField"; import { getNewRating } from "@/utils/eloCalculator"; @@ -103,7 +104,7 @@ export const ManualEloForm = () => { return (
-
+
{ return (
-
+
{ />
- ({ value: result, label: t("gameResult", { result }), }))} - isClearable={false} required /> {gameFields.length > 1 && ( diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index d75d2f7..1ab195f 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -1,6 +1,6 @@ import { ReactNode } from "react"; -import { NextIntlClientProvider, useLocale } from "next-intl"; +import { NextIntlClientProvider } from "next-intl"; import { getTranslator } from "next-intl/server"; import { Inter, Julius_Sans_One } from "next/font/google"; import { notFound } from "next/navigation"; @@ -51,6 +51,12 @@ export default async function RootLayout({ return ( + + + diff --git a/components/form/RadioGroupField.tsx b/components/form/RadioGroupField.tsx new file mode 100644 index 0000000..678d0d8 --- /dev/null +++ b/components/form/RadioGroupField.tsx @@ -0,0 +1,94 @@ +import React from "react"; + +import { RadioGroup } from "@headlessui/react"; +import { get, isNil } from "lodash"; +import { useTranslations } from "next-intl"; +import { Controller, useFormContext } from "react-hook-form"; +import { twMerge } from "tailwind-merge"; + +import { Field, FieldProps } from "./Field"; + +export type BaseOption = { + value: T; + label: string | JSX.Element; + disabled?: boolean; + data?: D; +}; + +export type RadioGroupFieldProps = FieldProps & { + required?: boolean; + options: BaseOption[]; +}; + +export const RadioGroupField = ({ + name, + className, + labelClassName, + childrenWrapperClassName, + label, + hideErrorMessage, + required, + + options, +}: RadioGroupFieldProps) => { + const t = useTranslations("App"); + const form = useFormContext(); + + const { + formState: { errors }, + } = form; + + const hasError = !!get(errors, name)?.message; + + const valueToOption = (value: T): BaseOption => + options.find((o) => o.value === value) ?? { value, label: "" }; + + return ( + + { + const optionValue = isNil(value) ? null : valueToOption(value); + + return ( + + {options.map((option) => ( + + twMerge( + "flex flex-1 cursor-pointer items-center justify-center rounded-lg border px-5 py-3 text-center text-sm font-medium focus:outline-none", + "border-gray-300 bg-gray-50 text-gray-900", + "dark:border-gray-600 dark:bg-gray-700 dark:text-white", + checked && + "border-primary text-primary dark:border-primary dark:text-primary", + ) + } + disabled={option.disabled} + > + {option.label} + + ))} + + ); + }} + /> + + ); +}; diff --git a/components/form/TextField.tsx b/components/form/TextField.tsx index 4172fa3..49ae2d5 100644 --- a/components/form/TextField.tsx +++ b/components/form/TextField.tsx @@ -87,7 +87,7 @@ export const TextField = ( disabled={disabled} className={twMerge( "flex w-full content-center rounded-lg border p-3 text-sm", - "border-gray-300 bg-gray-50 text-gray-900 shadow-sm focus:border-primary-500 focus:ring-primary-500", + "border-gray-300 bg-gray-50 text-gray-900 shadow-sm focus:border-primary-500 focus:ring-primary-500", "dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-primary-500 dark:focus:ring-primary-500", hasError && "!border-orange-700 focus:!border-orange-700", disabled && "dark:bg-gray-50",