gbs-add-block 0.0.37 → 0.0.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -5
- package/package.json +1 -1
- package/source/components/datepicker/DatePickerHelper.ts +2 -2
- package/source/components/datepicker/index.tsx +51 -17
- package/source/components/datepicker/types.ts +3 -1
- package/source/components/formrenderer/CheckBoxHandles.tsx +72 -0
- package/source/components/formrenderer/DatePickerHandles.tsx +65 -0
- package/source/components/formrenderer/index.tsx +26 -2
- package/source/components/formrenderer/types.ts +11 -0
- package/source/components/materialInput/index.tsx +59 -13
- package/source/components/materialInput/types.ts +9 -4
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# GBS Building Blocks 2.0 (v0.0.
|
|
1
|
+
# GBS Building Blocks 2.0 (v0.0.39)
|
|
2
2
|
|
|
3
3
|
Latest and upgraded version of GBS building blocks with headless UI and removed dependencies.
|
|
4
4
|
|
|
@@ -6,11 +6,9 @@ Latest and upgraded version of GBS building blocks with headless UI and removed
|
|
|
6
6
|
|
|
7
7
|
For detailed documentation on usage and props, Please visit: [Building Block Documentation v2.0](https://blackmax-designs.gitbook.io/building-block-v2.0)
|
|
8
8
|
|
|
9
|
-
## What's New 🎉 (Ver 0.0.
|
|
9
|
+
## What's New 🎉 (Ver 0.0.39)
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
- Updated Grid
|
|
13
|
-
- Updated DatePicker
|
|
11
|
+
- Form Renderer Now Supports Date Picker and CheckBox
|
|
14
12
|
|
|
15
13
|
## Authors
|
|
16
14
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const getDaysInMonth = (year:
|
|
1
|
+
const getDaysInMonth = (year: number, month: number) => {
|
|
2
2
|
return new Date(year, month + 1, 0).getDate();
|
|
3
3
|
};
|
|
4
4
|
|
|
5
|
-
export const generateCalendarHelper = (year:
|
|
5
|
+
export const generateCalendarHelper = (year: number, month: number) => {
|
|
6
6
|
const daysInMonth = getDaysInMonth(year, month);
|
|
7
7
|
const firstDay = new Date(year, month, 1).getDay();
|
|
8
8
|
const weeks = [];
|
|
@@ -10,6 +10,7 @@ import { generateCalendarHelper, months } from "./DatePickerHelper";
|
|
|
10
10
|
import Icon from "../icon/Icon";
|
|
11
11
|
import { calender, down, leftArrows, rightArrows } from "../icon/iconPaths";
|
|
12
12
|
import type { DatePickerProps } from "./types";
|
|
13
|
+
import { primary } from "../globalStyle";
|
|
13
14
|
|
|
14
15
|
export const DatePicker = ({
|
|
15
16
|
selectedDateValue,
|
|
@@ -18,14 +19,20 @@ export const DatePicker = ({
|
|
|
18
19
|
yearLimitStart = 50,
|
|
19
20
|
yearLimitEnd = 0,
|
|
20
21
|
onDateChange,
|
|
22
|
+
name,
|
|
23
|
+
error,
|
|
24
|
+
placeholder = "Select a date",
|
|
21
25
|
}: DatePickerProps) => {
|
|
22
26
|
const [showDatepicker, setShowDatepicker] = useState(false);
|
|
23
27
|
const [showYearMonthPicker, setShowYearMonthPicker] = useState(false);
|
|
24
28
|
const [currentMonth, setCurrentMonth] = useState(new Date().getMonth());
|
|
25
29
|
const [currentYear, setCurrentYear] = useState(new Date().getFullYear());
|
|
26
|
-
const [days, setDays] = useState<
|
|
27
|
-
const [selectedDate, setSelectedDate] = useState<
|
|
28
|
-
selectedDateValue
|
|
30
|
+
const [days, setDays] = useState<(Date | null)[][]>([]);
|
|
31
|
+
const [selectedDate, setSelectedDate] = useState<Date | null>(
|
|
32
|
+
selectedDateValue || null
|
|
33
|
+
);
|
|
34
|
+
const [tempSelectedDate, setTempSelectedDate] = useState<Date | null>(
|
|
35
|
+
selectedDateValue || new Date()
|
|
29
36
|
);
|
|
30
37
|
const [hasMounted, setHasMounted] = useState(false);
|
|
31
38
|
|
|
@@ -41,6 +48,7 @@ export const DatePicker = ({
|
|
|
41
48
|
useEffect(() => {
|
|
42
49
|
if (selectedDateValue) {
|
|
43
50
|
setSelectedDate(selectedDateValue);
|
|
51
|
+
setTempSelectedDate(selectedDateValue);
|
|
44
52
|
setCurrentMonth(selectedDateValue.getMonth());
|
|
45
53
|
setCurrentYear(selectedDateValue.getFullYear());
|
|
46
54
|
}
|
|
@@ -54,6 +62,7 @@ export const DatePicker = ({
|
|
|
54
62
|
function handleClickOutside(event: MouseEvent) {
|
|
55
63
|
if (dateRef.current && !dateRef.current.contains(event.target as Node)) {
|
|
56
64
|
setShowDatepicker(false);
|
|
65
|
+
setTempSelectedDate(selectedDate || new Date());
|
|
57
66
|
}
|
|
58
67
|
}
|
|
59
68
|
|
|
@@ -61,7 +70,7 @@ export const DatePicker = ({
|
|
|
61
70
|
return () => {
|
|
62
71
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
63
72
|
};
|
|
64
|
-
}, []);
|
|
73
|
+
}, [selectedDate]);
|
|
65
74
|
|
|
66
75
|
useEffect(() => {
|
|
67
76
|
setDays(generateCalendarHelper(currentYear, currentMonth));
|
|
@@ -70,19 +79,26 @@ export const DatePicker = ({
|
|
|
70
79
|
const toggleDatepicker = () => {
|
|
71
80
|
setShowDatepicker(!showDatepicker);
|
|
72
81
|
setShowYearMonthPicker(false);
|
|
82
|
+
if (!showDatepicker) {
|
|
83
|
+
setTempSelectedDate(selectedDate || new Date());
|
|
84
|
+
const date = selectedDate || new Date();
|
|
85
|
+
setCurrentMonth(date.getMonth());
|
|
86
|
+
setCurrentYear(date.getFullYear());
|
|
87
|
+
}
|
|
73
88
|
};
|
|
74
89
|
|
|
75
90
|
const toggleYearMonthPicker = () => {
|
|
76
91
|
setShowYearMonthPicker(!showYearMonthPicker);
|
|
77
92
|
};
|
|
78
93
|
|
|
79
|
-
const selectDate = (date:
|
|
94
|
+
const selectDate = (date: Date) => {
|
|
80
95
|
setSelectedDate(date);
|
|
96
|
+
setTempSelectedDate(date);
|
|
81
97
|
setShowDatepicker(false);
|
|
82
98
|
if (onDateChange) onDateChange(date);
|
|
83
99
|
};
|
|
84
100
|
|
|
85
|
-
const selectYearMonth = (year:
|
|
101
|
+
const selectYearMonth = (year: number, month: number) => {
|
|
86
102
|
setCurrentYear(year);
|
|
87
103
|
setCurrentMonth(month);
|
|
88
104
|
setShowYearMonthPicker(false);
|
|
@@ -108,10 +124,13 @@ export const DatePicker = ({
|
|
|
108
124
|
};
|
|
109
125
|
|
|
110
126
|
const goToToday = () => {
|
|
127
|
+
const today = new Date();
|
|
111
128
|
setCurrentMonth(today.getMonth());
|
|
112
129
|
setCurrentYear(today.getFullYear());
|
|
113
|
-
setSelectedDate(
|
|
114
|
-
|
|
130
|
+
setSelectedDate(today);
|
|
131
|
+
setTempSelectedDate(today);
|
|
132
|
+
if (onDateChange) onDateChange(today);
|
|
133
|
+
setShowDatepicker(false);
|
|
115
134
|
};
|
|
116
135
|
|
|
117
136
|
if (!hasMounted) {
|
|
@@ -119,19 +138,32 @@ export const DatePicker = ({
|
|
|
119
138
|
}
|
|
120
139
|
|
|
121
140
|
return (
|
|
122
|
-
<div className="relative inline-block w-
|
|
141
|
+
<div className="relative inline-block w-full dark:bg-black" ref={dateRef}>
|
|
123
142
|
<button
|
|
124
143
|
type="button"
|
|
125
144
|
onClick={toggleDatepicker}
|
|
126
|
-
className=
|
|
145
|
+
className={`${
|
|
146
|
+
error ? primary["error-border"] : "border"
|
|
147
|
+
} p-2 rounded-lg w-full flex items-center gap-2 dark:text-white`}
|
|
127
148
|
>
|
|
128
149
|
<Icon
|
|
129
150
|
dimensions={{ width: "20", height: "20" }}
|
|
130
151
|
elements={calender}
|
|
131
152
|
svgClass={"stroke-gray-500 fill-none dark:stroke-white"}
|
|
132
153
|
/>
|
|
133
|
-
{selectedDate
|
|
154
|
+
<span className={!selectedDate ? "text-gray-400" : ""}>
|
|
155
|
+
{selectedDate ? selectedDate.toLocaleDateString() : placeholder}
|
|
156
|
+
</span>
|
|
134
157
|
</button>
|
|
158
|
+
<p className={primary["error-primary"]}>{error && error}</p>
|
|
159
|
+
|
|
160
|
+
{/* Hidden input to integrate with the form */}
|
|
161
|
+
<input
|
|
162
|
+
type="hidden"
|
|
163
|
+
name={name}
|
|
164
|
+
value={selectedDate?.toDateString() || ""}
|
|
165
|
+
readOnly
|
|
166
|
+
/>
|
|
135
167
|
|
|
136
168
|
{showDatepicker && (
|
|
137
169
|
<div className="absolute z-10 bg-white border border-gray-300 shadow-lg mt-1 w-full rounded dark:bg-black dark:text-white px-2">
|
|
@@ -223,18 +255,20 @@ export const DatePicker = ({
|
|
|
223
255
|
{day}
|
|
224
256
|
</div>
|
|
225
257
|
))}
|
|
226
|
-
{days.flat().map((day:
|
|
258
|
+
{days.flat().map((day: Date | null, index: number) => {
|
|
227
259
|
if (!day) {
|
|
228
260
|
return (
|
|
229
261
|
<div key={index} className="text-center p-1 w-8 h-8"></div>
|
|
230
262
|
);
|
|
231
263
|
}
|
|
232
|
-
const isDisabled:
|
|
233
|
-
(minDate && day < minDate) ||
|
|
264
|
+
const isDisabled: boolean | undefined =
|
|
265
|
+
(minDate && day < minDate) ||
|
|
266
|
+
(maxDate && day > maxDate) ||
|
|
267
|
+
undefined;
|
|
234
268
|
const isSelected =
|
|
235
|
-
day.getDate() ===
|
|
236
|
-
day.getMonth() ===
|
|
237
|
-
day.getFullYear() ===
|
|
269
|
+
day.getDate() === tempSelectedDate?.getDate() &&
|
|
270
|
+
day.getMonth() === tempSelectedDate?.getMonth() &&
|
|
271
|
+
day.getFullYear() === tempSelectedDate?.getFullYear();
|
|
238
272
|
|
|
239
273
|
return (
|
|
240
274
|
<button
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { CheckboxHandlesProps, FormItem } from "./types";
|
|
3
|
+
|
|
4
|
+
const CheckboxHandles = ({
|
|
5
|
+
item,
|
|
6
|
+
formRef,
|
|
7
|
+
requirementError,
|
|
8
|
+
setRequirementError,
|
|
9
|
+
}: CheckboxHandlesProps) => {
|
|
10
|
+
const [checked, setChecked] = useState<boolean>(!!item?.value);
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (item?.name) {
|
|
14
|
+
handleSelect(checked, item.name);
|
|
15
|
+
}
|
|
16
|
+
}, []);
|
|
17
|
+
|
|
18
|
+
const handleSelect = (value: boolean, key: string) => {
|
|
19
|
+
if (formRef && formRef.current) {
|
|
20
|
+
const hiddenInput = formRef.current.querySelector(
|
|
21
|
+
`input[name="${key}"]`
|
|
22
|
+
) as HTMLInputElement;
|
|
23
|
+
|
|
24
|
+
if (hiddenInput) {
|
|
25
|
+
hiddenInput.value = value.toString();
|
|
26
|
+
} else {
|
|
27
|
+
const input = document.createElement("input");
|
|
28
|
+
input.type = "hidden";
|
|
29
|
+
input.name = key;
|
|
30
|
+
input.value = value.toString();
|
|
31
|
+
formRef.current.appendChild(input);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div className="w-full flex items-center gap-2">
|
|
38
|
+
<input
|
|
39
|
+
type="checkbox"
|
|
40
|
+
checked={checked}
|
|
41
|
+
onChange={(e) => {
|
|
42
|
+
const newValue = e.target.checked;
|
|
43
|
+
setChecked(newValue);
|
|
44
|
+
if (item?.name) {
|
|
45
|
+
handleSelect(newValue, item.name);
|
|
46
|
+
setRequirementError?.((prevErrors) =>
|
|
47
|
+
prevErrors.filter((error) => error !== item.name)
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}}
|
|
51
|
+
className={`w-5 h-5 cursor-pointer rounded-md border-2 transition-colors duration-200 ${
|
|
52
|
+
item?.name && requirementError.includes(item?.name)
|
|
53
|
+
? "border-red-500"
|
|
54
|
+
: "border-gray-300"
|
|
55
|
+
}`}
|
|
56
|
+
/>
|
|
57
|
+
{item?.label && (
|
|
58
|
+
<label htmlFor={item.name} className="font-medium text-sm">
|
|
59
|
+
{item.label}
|
|
60
|
+
{item.required && <span className="text-red-500">*</span>}
|
|
61
|
+
</label>
|
|
62
|
+
)}
|
|
63
|
+
{item?.name && requirementError.includes(item.name) && (
|
|
64
|
+
<p className="text-red-500 text-xs">{`${
|
|
65
|
+
item?.label || item?.name
|
|
66
|
+
} is required`}</p>
|
|
67
|
+
)}
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export default CheckboxHandles;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { DatePicker } from "../datepicker";
|
|
3
|
+
import { FormItem } from "./types";
|
|
4
|
+
|
|
5
|
+
interface DatePickerHandlesProps {
|
|
6
|
+
item?: FormItem;
|
|
7
|
+
requirementError: string[];
|
|
8
|
+
setRequirementError?: React.Dispatch<React.SetStateAction<string[]>>;
|
|
9
|
+
formRef?: React.RefObject<HTMLFormElement | null>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function DatePickerHandles({
|
|
13
|
+
item,
|
|
14
|
+
requirementError,
|
|
15
|
+
setRequirementError,
|
|
16
|
+
formRef,
|
|
17
|
+
}: DatePickerHandlesProps) {
|
|
18
|
+
const handleSelectDate = (value: string[], key: string) => {
|
|
19
|
+
if (formRef && formRef.current) {
|
|
20
|
+
const hiddenInput = formRef.current.querySelector(
|
|
21
|
+
`input[name="${key}"]`
|
|
22
|
+
) as HTMLInputElement;
|
|
23
|
+
|
|
24
|
+
if (hiddenInput) {
|
|
25
|
+
hiddenInput.value = Array.isArray(value)
|
|
26
|
+
? JSON.stringify(value)
|
|
27
|
+
: value;
|
|
28
|
+
} else {
|
|
29
|
+
const input = document.createElement("input");
|
|
30
|
+
input.type = "hidden";
|
|
31
|
+
input.name = key;
|
|
32
|
+
input.value = Array.isArray(value) ? JSON.stringify(value) : value;
|
|
33
|
+
formRef.current.appendChild(input);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div className="w-full">
|
|
40
|
+
{item?.label && (
|
|
41
|
+
<label htmlFor={item.name} className="font-medium text-sm">
|
|
42
|
+
{item.label}
|
|
43
|
+
{item.required && <span className="text-red-500">*</span>}
|
|
44
|
+
</label>
|
|
45
|
+
)}
|
|
46
|
+
<DatePicker
|
|
47
|
+
name={item?.name}
|
|
48
|
+
onDateChange={(value: any) => {
|
|
49
|
+
if (item?.name) {
|
|
50
|
+
handleSelectDate(value, item.name);
|
|
51
|
+
setRequirementError?.((prevErrors) =>
|
|
52
|
+
prevErrors.filter((errorName) => errorName !== item.name)
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
}}
|
|
56
|
+
error={
|
|
57
|
+
item?.name && requirementError.includes(item.name)
|
|
58
|
+
? `${item.name} is required`
|
|
59
|
+
: undefined
|
|
60
|
+
}
|
|
61
|
+
selectedDateValue={item?.value ? new Date(item.value) : undefined}
|
|
62
|
+
/>
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -8,8 +8,10 @@
|
|
|
8
8
|
import React, { useRef, useState } from "react";
|
|
9
9
|
import InputHandles from "./InputHandles";
|
|
10
10
|
import SelectHandles from "./SelectHandles";
|
|
11
|
-
import { FormItem, FormRendererProps } from "./types";
|
|
11
|
+
import { FormElement, FormItem, FormRendererProps } from "./types";
|
|
12
12
|
import MultiHandles from "./MultiHandles";
|
|
13
|
+
import DatePickerHandles from "./DatePickerHandles";
|
|
14
|
+
import CheckboxHandles from "./CheckBoxHandles";
|
|
13
15
|
|
|
14
16
|
const FormRenderer = ({
|
|
15
17
|
onSubmit,
|
|
@@ -18,7 +20,7 @@ const FormRenderer = ({
|
|
|
18
20
|
formParentClass = "w-96",
|
|
19
21
|
dependencyConfig,
|
|
20
22
|
}: FormRendererProps) => {
|
|
21
|
-
const formRef = useRef<
|
|
23
|
+
const formRef = useRef<FormElement>(null);
|
|
22
24
|
const [requirementError, setRequirementError] = useState<string[]>([]);
|
|
23
25
|
|
|
24
26
|
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
|
@@ -84,6 +86,28 @@ const FormRenderer = ({
|
|
|
84
86
|
/>
|
|
85
87
|
);
|
|
86
88
|
|
|
89
|
+
case "datepicker":
|
|
90
|
+
return (
|
|
91
|
+
<DatePickerHandles
|
|
92
|
+
key={index}
|
|
93
|
+
item={item}
|
|
94
|
+
requirementError={requirementError}
|
|
95
|
+
setRequirementError={setRequirementError}
|
|
96
|
+
formRef={formRef}
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
case "checkbox":
|
|
101
|
+
return (
|
|
102
|
+
<CheckboxHandles
|
|
103
|
+
key={index}
|
|
104
|
+
item={item}
|
|
105
|
+
requirementError={requirementError}
|
|
106
|
+
setRequirementError={setRequirementError}
|
|
107
|
+
formRef={formRef}
|
|
108
|
+
/>
|
|
109
|
+
);
|
|
110
|
+
|
|
87
111
|
case "button":
|
|
88
112
|
return (
|
|
89
113
|
<div key={index} className="w-full mt-1">
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
export interface FormElement extends HTMLFormElement {
|
|
2
|
+
[key: string]: any;
|
|
3
|
+
}
|
|
4
|
+
|
|
1
5
|
export type FormItem = {
|
|
2
6
|
id?: string;
|
|
3
7
|
name?: string;
|
|
@@ -48,3 +52,10 @@ export type Option = {
|
|
|
48
52
|
value: string;
|
|
49
53
|
label: string;
|
|
50
54
|
};
|
|
55
|
+
|
|
56
|
+
export type CheckboxHandlesProps = {
|
|
57
|
+
item?: FormItem;
|
|
58
|
+
requirementError: string[];
|
|
59
|
+
setRequirementError?: React.Dispatch<React.SetStateAction<string[]>>;
|
|
60
|
+
formRef?: React.RefObject<HTMLFormElement | null>;
|
|
61
|
+
};
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Grampro Business Services and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
1
8
|
import React, { useState, useRef } from "react";
|
|
2
9
|
import { MaterialInputProps } from "./types";
|
|
3
10
|
|
|
@@ -11,9 +18,13 @@ const MaterialInput = ({
|
|
|
11
18
|
required = false,
|
|
12
19
|
disabled = false,
|
|
13
20
|
className = "",
|
|
21
|
+
textarea = false,
|
|
22
|
+
rows = 3,
|
|
23
|
+
cols = 30,
|
|
24
|
+
...props
|
|
14
25
|
}: MaterialInputProps) => {
|
|
15
26
|
const [isFocused, setIsFocused] = useState(false);
|
|
16
|
-
const inputRef = useRef<HTMLInputElement>(null);
|
|
27
|
+
const inputRef = useRef<HTMLTextAreaElement | HTMLInputElement>(null);
|
|
17
28
|
|
|
18
29
|
const handleFocus = () => setIsFocused(true);
|
|
19
30
|
const handleBlur = () => {
|
|
@@ -27,16 +38,50 @@ const MaterialInput = ({
|
|
|
27
38
|
return (
|
|
28
39
|
<div className={`relative w-full ${className}`}>
|
|
29
40
|
<div className="relative">
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
{textarea ? (
|
|
42
|
+
<textarea
|
|
43
|
+
{...props}
|
|
44
|
+
ref={inputRef as React.RefObject<HTMLTextAreaElement>}
|
|
45
|
+
value={value}
|
|
46
|
+
onChange={(e) => {
|
|
47
|
+
if (onChange) onChange(e.target.value);
|
|
48
|
+
}}
|
|
49
|
+
onFocus={handleFocus}
|
|
50
|
+
onBlur={handleBlur}
|
|
51
|
+
rows={rows}
|
|
52
|
+
cols={cols}
|
|
53
|
+
disabled={disabled}
|
|
54
|
+
required={required}
|
|
55
|
+
className={`
|
|
56
|
+
peer w-full px-3 py-3
|
|
57
|
+
bg-transparent
|
|
58
|
+
border rounded-md
|
|
59
|
+
outline-none
|
|
60
|
+
transition-all duration-200 ease-in-out
|
|
61
|
+
placeholder-transparent
|
|
62
|
+
${
|
|
63
|
+
error
|
|
64
|
+
? "border-red-500 focus:border-red-500"
|
|
65
|
+
: "border-gray-300 focus:border-blue-500"
|
|
66
|
+
}
|
|
67
|
+
${disabled ? "text-gray-400 cursor-not-allowed" : "text-gray-900"}
|
|
68
|
+
`}
|
|
69
|
+
/>
|
|
70
|
+
) : (
|
|
71
|
+
<input
|
|
72
|
+
{...props}
|
|
73
|
+
autoComplete="off"
|
|
74
|
+
ref={inputRef as React.RefObject<HTMLInputElement>}
|
|
75
|
+
type={type}
|
|
76
|
+
value={value}
|
|
77
|
+
onChange={(e) => {
|
|
78
|
+
if (onChange) onChange(e.target.value);
|
|
79
|
+
}}
|
|
80
|
+
onFocus={handleFocus}
|
|
81
|
+
onBlur={handleBlur}
|
|
82
|
+
disabled={disabled}
|
|
83
|
+
required={required}
|
|
84
|
+
className={`
|
|
40
85
|
peer w-full px-3 py-3
|
|
41
86
|
bg-transparent
|
|
42
87
|
border rounded-md
|
|
@@ -50,7 +95,8 @@ const MaterialInput = ({
|
|
|
50
95
|
}
|
|
51
96
|
${disabled ? "text-gray-400 cursor-not-allowed" : "text-gray-900"}
|
|
52
97
|
`}
|
|
53
|
-
|
|
98
|
+
/>
|
|
99
|
+
)}
|
|
54
100
|
<label
|
|
55
101
|
onClick={() => inputRef.current?.focus()}
|
|
56
102
|
className={`
|
|
@@ -58,7 +104,7 @@ const MaterialInput = ({
|
|
|
58
104
|
pointer-events-none
|
|
59
105
|
transition-all duration-200 ease-in-out
|
|
60
106
|
cursor-text
|
|
61
|
-
${isFloating ? "-top-2 text-xs bg-white px-1" : "top-
|
|
107
|
+
${isFloating ? "-top-2 text-xs bg-white px-1" : "top-2 text-base"}
|
|
62
108
|
${
|
|
63
109
|
error
|
|
64
110
|
? "text-red-500"
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
import { InputHTMLAttributes } from "react";
|
|
2
|
+
|
|
1
3
|
export type MaterialInputProps = {
|
|
2
|
-
label
|
|
4
|
+
label?: string;
|
|
3
5
|
type?: string;
|
|
4
|
-
value
|
|
5
|
-
onChange
|
|
6
|
+
value?: string;
|
|
7
|
+
onChange?: (value: string) => void;
|
|
6
8
|
error?: string;
|
|
7
9
|
helperText?: string;
|
|
8
10
|
required?: boolean;
|
|
9
11
|
disabled?: boolean;
|
|
10
12
|
className?: string;
|
|
11
|
-
|
|
13
|
+
textarea?: boolean;
|
|
14
|
+
rows?: number;
|
|
15
|
+
cols?: number;
|
|
16
|
+
} & InputHTMLAttributes<HTMLInputElement | HTMLTextAreaElement>;
|