Hey is a decentralized and permissionless social media app built with Lens Protocol 馃尶
1import type { ComponentProps, ReactNode } from "react";
2import { forwardRef, memo, useId } from "react";
3import cn from "@/helpers/cn";
4import { FieldError } from "./Form";
5import HelpTooltip from "./HelpTooltip";
6
7interface InputProps extends Omit<ComponentProps<"input">, "prefix"> {
8 className?: string;
9 error?: boolean;
10 helper?: ReactNode;
11 hideError?: boolean;
12 iconLeft?: ReactNode;
13 iconRight?: ReactNode;
14 label?: ReactNode;
15 prefix?: ReactNode | string;
16}
17
18const Input = forwardRef<HTMLInputElement, InputProps>(
19 (
20 {
21 className = "",
22 error,
23 helper,
24 hideError = false,
25 iconLeft,
26 iconRight,
27 label,
28 prefix,
29 type = "text",
30 ...props
31 },
32 ref
33 ) => {
34 const id = useId();
35
36 const iconStyles = [
37 "text-zinc-500 [&>*]:peer-focus:text-gray-500 [&>*]:h-5",
38 { "!text-red-500 [&>*]:peer-focus:!text-red-500": error }
39 ];
40
41 return (
42 <label className="w-full" htmlFor={id}>
43 {label ? (
44 <div className="mb-1 flex items-center space-x-1.5">
45 <div className="font-medium text-gray-800 dark:text-gray-200">
46 {label}
47 </div>
48 <HelpTooltip>{helper}</HelpTooltip>
49 </div>
50 ) : null}
51 <div className="flex">
52 {prefix ? (
53 <span className="inline-flex items-center rounded-l-xl border border-gray-300 border-r-0 bg-gray-100 px-3 text-gray-500 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-200">
54 {prefix}
55 </span>
56 ) : null}
57 <div
58 className={cn(
59 { "!bg-gray-500/20 opacity-50": props.disabled },
60 { "!border-red-500": error },
61 prefix ? "rounded-r-xl" : "rounded-xl",
62 "flex w-full items-center border border-gray-300 bg-white focus-within:border-gray-500 dark:border-gray-700 dark:bg-gray-900"
63 )}
64 >
65 <input
66 className={cn(
67 { "placeholder:text-red-500": error },
68 prefix ? "rounded-r-xl" : "rounded-xl",
69 "peer w-full border-none bg-transparent outline-hidden focus:ring-0",
70 className
71 )}
72 id={id}
73 ref={ref}
74 type={type}
75 {...props}
76 />
77 <span
78 className={cn({ "order-first pl-3": iconLeft }, iconStyles)}
79 tabIndex={-1}
80 >
81 {iconLeft}
82 </span>
83 <span
84 className={cn({ "order-last pr-3": iconRight }, iconStyles)}
85 tabIndex={-1}
86 >
87 {iconRight}
88 </span>
89 </div>
90 </div>
91 {!hideError && props.name ? <FieldError name={props.name} /> : null}
92 </label>
93 );
94 }
95);
96
97export default memo(Input);