add custom generic input and dropdown selector components for the add link modal
This commit is contained in:
parent
1597cbe837
commit
5644c7108f
@ -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>
|
||||
);
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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 };
|
||||
|
Loading…
x
Reference in New Issue
Block a user