add custom generic input and dropdown selector components for the add link modal

This commit is contained in:
Brandon Egger 2023-06-10 17:55:12 -05:00
parent 1597cbe837
commit 5644c7108f
3 changed files with 140 additions and 13 deletions

View File

@ -3,24 +3,34 @@ import {
SkillLevel,
Skill,
type PlatformLink,
Platform,
} from "@prisma/client";
import Image from "next/image";
import { PencilSquareIcon } from "@heroicons/react/24/solid";
import { ChevronDownIcon, TrashIcon } from "@heroicons/react/24/outline";
import {
PencilSquareIcon,
XCircleIcon as XCircleSolid,
} from "@heroicons/react/24/solid";
import {
ChevronDownIcon,
TrashIcon,
XCircleIcon as XCircleOutline,
} from "@heroicons/react/24/outline";
import { PlusIcon } from "@heroicons/react/20/solid";
import {
DropdownSelector,
MultiSelectorMany,
MultiSelectorOption,
SelectedManyContext,
SimpleSelectorManyOption,
} from "../../forms/selectors";
import { InfoInputLine } from "~/components/forms/textInput";
import { GenericInput, InfoInputLine } from "~/components/forms/textInput";
import { PriceIcon } from "~/prices/Icons";
import { type Dispatch, type SetStateAction, useState } from "react";
import {
type UseFormReturn,
FormProvider,
useFormContext,
useForm,
} from "react-hook-form";
import Modal from "react-modal";
import { type RouterInputs } from "~/utils/api";
@ -72,6 +82,26 @@ const LinkModal = ({
isOpen: boolean;
setOpen: Dispatch<SetStateAction<boolean>>;
}) => {
const { register } = useForm<PlatformLink>();
const platformTypeOptions = [
{
label: "Website",
value: Platform.WEBSITE,
},
{
label: "iOS App",
value: Platform.APP_IOS,
},
{
label: "Android App",
value: Platform.APP_ANDROID,
},
{
label: "PDF Document",
value: Platform.PDF,
},
];
return (
<Modal
style={{
@ -80,10 +110,10 @@ const LinkModal = ({
height: "300px",
margin: "auto",
padding: 0,
overflow: "visible",
overflow: "hidden",
boxShadow: "0 25px 50px -12px rgb(0 0 0 / 0.25)",
borderRadius: ".8rem",
border: "1px solid #d4d4d4",
border: ".1rem solid #d4d4d4",
},
overlay: {
zIndex: 60,
@ -95,8 +125,37 @@ const LinkModal = ({
setOpen(false);
}}
>
<div className="p-2">
<h1 className="text-lg font-bold">Link Details</h1>
<div className="h-full bg-neutral-200">
<section className="relative bg-gradient-to-t from-neutral-800 to-neutral-600 p-2 drop-shadow-xl">
<h1 className="text-center text-lg font-bold text-neutral-200">
Platform Details
</h1>
<button
onClick={() => {
setOpen(false);
}}
type="button"
className="group absolute bottom-0 right-0 top-0 h-full px-4"
>
<XCircleSolid className="hidden h-6 w-6 text-white group-hover:block" />
<XCircleOutline className="block h-6 w-6 text-white group-hover:hidden" />
</button>
</section>
<div className="space-y-4 p-4">
<DropdownSelector
options={platformTypeOptions}
label="Type"
details={register("platform", { required: true })}
/>
<GenericInput
placeholder="platform URL"
label="Link"
type="text"
details={register("link", { required: true })}
/>
</div>
</div>
</Modal>
);

View File

@ -1,5 +1,9 @@
import { createContext, useContext, useState } from "react";
import { useFormContext, type UseFormRegisterReturn } from "react-hook-form";
import {
type InternalFieldName,
useFormContext,
type UseFormRegisterReturn,
} from "react-hook-form";
// generics
interface ToStringable {
@ -165,6 +169,45 @@ function SimpleSelectorManyOption<T extends ToStringable>({
);
}
interface SelectorOption<
T extends string | number | readonly string[] | undefined
> {
label: string;
value: T;
}
function DropdownSelector<
TFieldName extends InternalFieldName,
T extends string | number | readonly string[] | undefined
>({
options,
label,
placeholder,
details,
}: {
options: SelectorOption<T>[];
label: string;
placeholder?: string;
details: UseFormRegisterReturn<TFieldName>;
}) {
return (
<section className="space-y-1">
<label className="text-md block px-1 font-semibold text-neutral-600">
{label}
</label>
<select
className="block h-8 w-full rounded-lg border border-neutral-600 px-2 py-1"
{...details}
placeholder={placeholder}
>
{options.map((option, key) => {
return <option key={key} {...option} />;
})}
</select>
</section>
);
}
export {
SelectedUniqueContext,
SelectorContext,
@ -173,4 +216,5 @@ export {
MultiSelector,
MultiSelectorOption,
SimpleSelectorManyOption,
DropdownSelector,
};

View File

@ -1,4 +1,4 @@
import { useState } from "react";
import { type HTMLInputTypeAttribute, useState } from "react";
import {
type UseFormRegisterReturn,
type InternalFieldName,
@ -8,9 +8,7 @@ import {
* Single line input for the fields found to the right of the
* resource logo.
*/
function InfoInputLine<
TFieldName extends InternalFieldName = InternalFieldName
>({
function InfoInputLine<TFieldName extends InternalFieldName>({
value,
placeholder,
hint,
@ -46,4 +44,30 @@ function InfoInputLine<
);
}
export { InfoInputLine };
function GenericInput<TFieldName extends InternalFieldName>({
label,
placeholder,
type = "text",
details,
}: {
label: string;
placeholder?: string;
type: HTMLInputTypeAttribute;
details: UseFormRegisterReturn<TFieldName>;
}) {
return (
<section className="w-full space-y-1">
<label className="text-md block px-1 font-semibold text-neutral-600">
{label}
</label>
<input
className="block h-8 w-full rounded-lg border border-neutral-600 px-2 py-1"
{...details}
placeholder={placeholder}
type={type}
></input>
</section>
);
}
export { InfoInputLine, GenericInput };