Skip to content

Commit e31b0be

Browse files
committed
client(Input.tsx): redesig the component
1 parent acef3f5 commit e31b0be

File tree

3 files changed

+82
-72
lines changed

3 files changed

+82
-72
lines changed

client/src/components/input/Input.tsx

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
1-
import { Dispatch, FC, SetStateAction, useId } from "react";
1+
import { FC, useId } from "react";
2+
import { BiError } from "react-icons/bi";
3+
import { HiOutlineInformationCircle } from "react-icons/hi";
24

35
interface InputProps {
46
label: string;
57
value: string;
6-
setValue: Dispatch<SetStateAction<any>>;
8+
setValue: (value: string) => void;
79
placeholder: string;
10+
className?: string;
11+
helperText?: string;
12+
validate: (value: string) => string;
813
}
914

1015
const Input: FC<InputProps> = (props) => {
1116
const id = `${props.label}-${useId()}`;
17+
const isError = props.validate(props.value).trim() !== "";
1218

1319
return (
14-
<div className="flex items-center gap-2 mx-4">
20+
<div className={`${props.className} w-full flex flex-col gap-1`}>
1521
<label
1622
htmlFor={id}
17-
className="text-gh-text-secondary whitespace-nowrap font-semibold"
23+
className="text-sm text-gh-text-secondary font-semibold"
1824
>
19-
{props.label}:
25+
{props.label}
2026
</label>
2127

2228
<input
@@ -27,11 +33,26 @@ const Input: FC<InputProps> = (props) => {
2733
onChange={(e) => props.setValue(e.target.value)}
2834
autoComplete="off"
2935
type="text"
30-
className="w-[72%] ml-auto text-base bg-gh-bg border border-solid border-gh-border
31-
rounded-md px-2 py-1 leading-none placeholder:text-gh-text-secondary text-gh-text
32-
outline-none focus:border-gh-blue-dark active:border-gh-blue-dark transition-all
33-
duration-150"
36+
className={`text-base bg-gh-bg border border-solid rounded-md px-2 py-1
37+
leading-none placeholder:text-gh-text-secondary text-gh-text
38+
outline-none transition-all duration-150 ${
39+
isError
40+
? "border-red-500"
41+
: "focus:border-gh-blue-dark active:border-gh-blue-dark border-gh-border"
42+
}`}
3443
/>
44+
45+
{!isError && props.helperText && (
46+
<div className="flex items-center gap-1 text-sm italic text-gh-text-secondary">
47+
<HiOutlineInformationCircle /> {props.helperText}
48+
</div>
49+
)}
50+
51+
{isError && (
52+
<div className="flex items-center gap-1 text-sm italic text-red-500">
53+
<BiError /> {props.validate(props.value)}
54+
</div>
55+
)}
3556
</div>
3657
);
3758
};

client/src/components/popups/LinePopup.tsx

Lines changed: 31 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { FC, useEffect, useState } from "react";
22
import { Badge } from "../../types/line";
33
import GreenButton from "../buttons/GreenButton";
4-
import Input from "../input/Input";
5-
import SingleCodeblock from "../text/SingleCodeblock";
64
import { FiSave } from "react-icons/fi";
75
import SecondaryButton from "../buttons/SecondaryButton";
6+
import Input from "../input/Input";
87

98
interface LinePopupProps {
109
isActive: boolean;
@@ -77,75 +76,48 @@ const LinePopup: FC<LinePopupProps> = (props) => {
7776
Line #{props.lineNumber}: Add badge
7877
</div>
7978

80-
<div className="flex flex-col gap-3 my-3">
81-
<div>
82-
<Input
83-
label="Icon"
84-
placeholder="react"
85-
setValue={setIcon}
86-
value={icon}
87-
/>
88-
<div className="italic text-gh-text-secondary text-sm mx-4">
89-
-- You can browse between the icons{" "}
90-
<a
91-
href="https://simpleicons.org/"
92-
target="_blank"
93-
rel="noreferrer"
94-
className="text-gh-blue hover:underline"
95-
tabIndex={4}
96-
>
97-
here
98-
</a>
99-
.
100-
</div>
101-
</div>
79+
<div className="flex flex-col gap-3 my-3 px-6">
80+
<Input
81+
label="Icon"
82+
placeholder="react"
83+
value={icon}
84+
setValue={(val: string) => setIcon(val)}
85+
validate={() => ""}
86+
helperText="You can browse between the icons here: https://simpleicons.org/"
87+
/>
10288

10389
<div className="w-[95%] h-[.8px] bg-gh-border mx-auto" />
10490

10591
<Input
10692
label="Badge label"
10793
placeholder="react"
108-
setValue={setLabel}
10994
value={label}
95+
setValue={(val: string) => setLabel(val)}
96+
validate={() => ""}
11097
/>
11198

11299
<div className="w-[95%] h-[.8px] bg-gh-border mx-auto" />
113100

114-
<div>
115-
<Input
116-
label="Badge color"
117-
placeholder="#3498db"
118-
setValue={setColor}
119-
value={color}
120-
/>
121-
<div className="italic text-gh-text-secondary text-sm mx-4">
122-
-- The badge color is either a hexadecimal color code or the value{" "}
123-
<SingleCodeblock code="auto" />.
124-
</div>
125-
</div>
126-
127-
<div className="w-[95%] h-[.8px] bg-gh-border mx-auto" />
128-
129-
{errorMsg !== undefined ? (
130-
<div className="text-red-400 text-sm italic mx-4">-- {errorMsg}</div>
131-
) : (
132-
<div className="text-green-500 text-sm italic mx-4">
133-
-- Looks good!
134-
</div>
135-
)}
136-
137-
<div className="w-[95%] h-[.8px] bg-gh-border mx-auto" />
138-
139-
<div className="flex items-stretch">
140-
<GreenButton
141-
icon={FiSave}
142-
onClick={handleSave}
143-
text="Save"
144-
disabled={errorMsg !== undefined}
145-
/>
101+
<Input
102+
label="Badge color"
103+
placeholder="#3498db"
104+
value={color}
105+
setValue={(val: string) => setColor(val)}
106+
validate={() => ""}
107+
helperText="The badge color is either a hexadecimal color code or the value auto."
108+
/>
109+
</div>
110+
<div className="w-[95%] h-[.8px] bg-gh-border mx-auto" />
111+
112+
<div className="flex items-stretch py-4">
113+
<GreenButton
114+
icon={FiSave}
115+
onClick={handleSave}
116+
text="Save"
117+
disabled={errorMsg !== undefined}
118+
/>
146119

147-
<SecondaryButton onClick={handleCancel} text="Cancel" />
148-
</div>
120+
<SecondaryButton onClick={handleCancel} text="Cancel" />
149121
</div>
150122
</div>
151123
);

client/src/sections/Options.tsx

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import SectionTitle from "../components/text/SectionTitle";
22
import { VscSettings } from "react-icons/vsc";
33
import { IoHammerOutline } from "react-icons/io5";
4-
import Input from "../components/input/Input";
54
import SelectInput from "../components/input/SelectInput";
65
import GreenButton from "../components/buttons/GreenButton";
76
import { useRecoilState } from "recoil";
@@ -21,6 +20,7 @@ import { generateLink } from "../utils/generate";
2120
import { useFetchThemes } from "../hooks/useFetchThemes";
2221
import NumberInput from "../components/input/NumberInput";
2322
import TrueFalseInput from "../components/input/TrueFalseInput";
23+
import Input from "../components/input/Input";
2424

2525
interface OptionsProps {
2626
setLink: (link: string) => void;
@@ -83,7 +83,8 @@ const Options: FC<OptionsProps> = (props) => {
8383
label="Title"
8484
placeholder="My Tech Stack"
8585
value={title}
86-
setValue={setTitle}
86+
setValue={(val) => setTitle(val)}
87+
validate={() => ""}
8788
/>
8889

8990
<NumberInput
@@ -115,10 +116,26 @@ const Options: FC<OptionsProps> = (props) => {
115116
/>
116117

117118
<Input
118-
label="Border radius"
119+
label="Border Radius"
119120
placeholder="4.5"
120121
value={borderRadius}
121-
setValue={setBorderRadius}
122+
setValue={(val) => setBorderRadius(val)}
123+
helperText="A number between 0 and 50."
124+
validate={(val) => {
125+
const num = parseInt(val);
126+
127+
if (val.trim() === "") {
128+
return "Please provide a border radius!";
129+
}
130+
131+
if (isNaN(num)) {
132+
return "Please provide a number!";
133+
}
134+
135+
return num > 50 || num < 0
136+
? "Please provide a value between 0 and 50"
137+
: "";
138+
}}
122139
/>
123140

124141
<div className="w-[92%] h-[.8px] bg-gh-border mx-auto" />

0 commit comments

Comments
 (0)