gbs-add-block 0.0.7 → 0.0.9
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/index.js
CHANGED
package/package.json
CHANGED
|
@@ -17,13 +17,16 @@ export const Input = ({
|
|
|
17
17
|
OTPLength = 4,
|
|
18
18
|
OTPClass = "w-8 h-10 m-1 border border-gray-600 rounded-lg text-center",
|
|
19
19
|
onOTPValueChange,
|
|
20
|
+
error = undefined,
|
|
20
21
|
...props
|
|
21
22
|
}: InputProps) => {
|
|
22
23
|
const [otpValues, setOtpValues] = useState(new Array(OTPLength).fill(""));
|
|
23
24
|
const [showPassword, setShowPassword] = useState(false);
|
|
24
25
|
const inputRefs = useRef<any>([]);
|
|
25
26
|
|
|
26
|
-
const defaultClass =
|
|
27
|
+
const defaultClass = `bg-gray-100 p-2 rounded-lg ${
|
|
28
|
+
error ? "border border-red-600" : ""
|
|
29
|
+
}`;
|
|
27
30
|
|
|
28
31
|
// This will update the OTP Field Value
|
|
29
32
|
const updateOtpValue = (index: number, e: any) => {
|
|
@@ -60,7 +63,7 @@ export const Input = ({
|
|
|
60
63
|
return (
|
|
61
64
|
<div>
|
|
62
65
|
{!OTPField ? (
|
|
63
|
-
<div className="text-input-container w-
|
|
66
|
+
<div className="text-input-container w-full relative">
|
|
64
67
|
<input
|
|
65
68
|
className={twMerge(defaultClass, "w-full focus:outline-blue-400")}
|
|
66
69
|
{...props}
|
|
@@ -80,6 +83,7 @@ export const Input = ({
|
|
|
80
83
|
/>
|
|
81
84
|
</button>
|
|
82
85
|
)}
|
|
86
|
+
<div className="text-xs text-red-500">{error && error}</div>
|
|
83
87
|
</div>
|
|
84
88
|
) : (
|
|
85
89
|
<div className="otp-container">
|
|
@@ -94,7 +98,7 @@ export const Input = ({
|
|
|
94
98
|
value={otpValues[index]}
|
|
95
99
|
onChange={(e) => updateOtpValue(index, e)}
|
|
96
100
|
onKeyDown={(e) => handleKeydown(index, e)}
|
|
97
|
-
ref={(ref) => (inputRefs.current[index] = ref)}
|
|
101
|
+
// ref={(ref) => (inputRefs.current[index] = ref)}
|
|
98
102
|
className={OTPClass}
|
|
99
103
|
/>
|
|
100
104
|
))}
|
|
@@ -33,6 +33,7 @@ const Select = forwardRef<any, SelectProps>((props, ref) => {
|
|
|
33
33
|
showSearch = true,
|
|
34
34
|
onSelect,
|
|
35
35
|
selectedItem: initialSelectedItem,
|
|
36
|
+
error = undefined,
|
|
36
37
|
} = props;
|
|
37
38
|
|
|
38
39
|
const [showPopover, setShowPopover] = useState(false);
|
|
@@ -145,11 +146,14 @@ const Select = forwardRef<any, SelectProps>((props, ref) => {
|
|
|
145
146
|
}));
|
|
146
147
|
|
|
147
148
|
return (
|
|
148
|
-
<div className="relative" ref={selectRef}>
|
|
149
|
-
<div className="w-
|
|
149
|
+
<div className="relative w-full" ref={selectRef}>
|
|
150
|
+
<div className="w-full relative">
|
|
150
151
|
<button
|
|
151
|
-
className=
|
|
152
|
+
className={`flex items-center ${
|
|
153
|
+
error ? "border border-red-600" : "border"
|
|
154
|
+
} px-4 py-2 w-full justify-between rounded-lg font-medium text-sm dark:bg-black dark:border-white dark:text-white`}
|
|
152
155
|
onClick={togglePopover}
|
|
156
|
+
type="button"
|
|
153
157
|
>
|
|
154
158
|
{selectedDisplay || placeholder}
|
|
155
159
|
<Icon
|
|
@@ -168,10 +172,11 @@ const Select = forwardRef<any, SelectProps>((props, ref) => {
|
|
|
168
172
|
/>
|
|
169
173
|
</button>
|
|
170
174
|
)}
|
|
175
|
+
<p className="text-xs text-red-500">{error && error}</p>
|
|
171
176
|
</div>
|
|
172
177
|
|
|
173
178
|
{showPopover && (
|
|
174
|
-
<div className="w-
|
|
179
|
+
<div className="w-full absolute overflow-y-auto border px-2 rounded-lg mt-[1px] scrollbar bg-white z-50 scrollbar h-auto dark:bg-black dark:text-white">
|
|
175
180
|
{showSearch && (
|
|
176
181
|
<div className="flex p-2 gap-1 items-center sticky top-0 bg-white border-b dark:bg-black">
|
|
177
182
|
<Icon
|
|
@@ -17,58 +17,121 @@ export const FileUploader = ({
|
|
|
17
17
|
onChange,
|
|
18
18
|
startsUpload = false,
|
|
19
19
|
selectedFiles = [],
|
|
20
|
+
accept,
|
|
21
|
+
fileCount,
|
|
20
22
|
}: any) => {
|
|
21
23
|
const [files, setFiles] = useState<any[]>([]);
|
|
22
24
|
const [previewUrls, setPreviewUrls] = useState<{ [key: string]: string }>({});
|
|
25
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
26
|
+
const [fileCountError, setFileCountError] = useState(false);
|
|
23
27
|
const fileInputRef = useRef<any>(null);
|
|
28
|
+
const dropZoneRef = useRef<any>(null);
|
|
24
29
|
|
|
30
|
+
// For Binding values on edit
|
|
25
31
|
useEffect(() => {
|
|
26
32
|
if (selectedFiles && selectedFiles.length > 0) {
|
|
27
|
-
setFiles(selectedFiles);
|
|
33
|
+
setFiles(multiple ? selectedFiles : [selectedFiles[0]]);
|
|
28
34
|
}
|
|
29
|
-
}, [selectedFiles]);
|
|
35
|
+
}, [selectedFiles, multiple]);
|
|
30
36
|
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
// Shows Error when filecount is higher
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (files.length > fileCount) {
|
|
40
|
+
setFileCountError(true);
|
|
41
|
+
}
|
|
42
|
+
}, [files, fileCount]);
|
|
43
|
+
|
|
44
|
+
// Adds files to file array
|
|
45
|
+
const handleFileChange = (newFiles: File[]) => {
|
|
46
|
+
let updatedFiles: File[] = multiple
|
|
47
|
+
? [...files, ...newFiles]
|
|
48
|
+
: [newFiles[0]];
|
|
49
|
+
|
|
50
|
+
// Ensure the total number of files doesn't exceed the fileCount limit
|
|
51
|
+
if (updatedFiles.length > fileCount) {
|
|
52
|
+
setFileCountError(true);
|
|
53
|
+
updatedFiles = updatedFiles.slice(0, fileCount); // Limit the number of files to fileCount
|
|
54
|
+
} else {
|
|
55
|
+
setFileCountError(false);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
setFiles(updatedFiles);
|
|
59
|
+
|
|
60
|
+
// Clear previous preview URLs if not multiple
|
|
61
|
+
if (!multiple) {
|
|
62
|
+
setPreviewUrls({});
|
|
63
|
+
}
|
|
35
64
|
|
|
36
|
-
|
|
65
|
+
newFiles.forEach((file) => {
|
|
37
66
|
if (showImagePreview && file.type.startsWith("image/")) {
|
|
38
67
|
const reader = new FileReader();
|
|
39
68
|
reader.onload = () => {
|
|
40
|
-
|
|
41
|
-
|
|
69
|
+
setPreviewUrls((prev) => ({
|
|
70
|
+
...prev,
|
|
71
|
+
[file.name]: reader.result as string,
|
|
72
|
+
}));
|
|
42
73
|
};
|
|
43
74
|
reader.readAsDataURL(file);
|
|
44
75
|
}
|
|
45
76
|
});
|
|
46
77
|
|
|
47
|
-
const updatedFiles = [...files, ...selectedFiles];
|
|
48
|
-
setFiles(updatedFiles);
|
|
49
|
-
|
|
50
78
|
if (onChange) onChange(updatedFiles);
|
|
51
79
|
};
|
|
52
80
|
|
|
53
|
-
//
|
|
54
|
-
const removeFile = (index:
|
|
55
|
-
const fileToRemove = files[index];
|
|
81
|
+
// Removes the file from file array
|
|
82
|
+
const removeFile = (index: number) => {
|
|
56
83
|
const updatedFiles = files.filter((_, i) => i !== index);
|
|
57
|
-
|
|
58
84
|
setFiles(updatedFiles);
|
|
59
85
|
setPreviewUrls((prevPreviews) => {
|
|
60
|
-
const { [
|
|
86
|
+
const { [files[index].name]: _, ...rest } = prevPreviews;
|
|
61
87
|
return rest;
|
|
62
88
|
});
|
|
63
89
|
|
|
64
90
|
if (onChange) onChange(updatedFiles);
|
|
65
91
|
};
|
|
66
92
|
|
|
93
|
+
// *** Code to handle Drag and drop functionalities Starts here
|
|
94
|
+
const handleDragEnter = (e: React.DragEvent<HTMLDivElement>) => {
|
|
95
|
+
e.preventDefault();
|
|
96
|
+
e.stopPropagation();
|
|
97
|
+
setIsDragging(true);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const handleDragLeave = (e: React.DragEvent<HTMLDivElement>) => {
|
|
101
|
+
e.preventDefault();
|
|
102
|
+
e.stopPropagation();
|
|
103
|
+
setIsDragging(false);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
e.stopPropagation();
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {
|
|
112
|
+
e.preventDefault();
|
|
113
|
+
e.stopPropagation();
|
|
114
|
+
setIsDragging(false);
|
|
115
|
+
|
|
116
|
+
const droppedFiles = Array.from(e.dataTransfer.files);
|
|
117
|
+
handleFileChange(multiple ? droppedFiles : [droppedFiles[0]]);
|
|
118
|
+
};
|
|
119
|
+
// Code to handle Drag and drop functionalities Ends here ***
|
|
120
|
+
|
|
67
121
|
return (
|
|
68
122
|
<div className="w-full max-w-md mx-auto">
|
|
69
123
|
<div
|
|
70
|
-
|
|
124
|
+
ref={dropZoneRef}
|
|
125
|
+
className={`flex flex-col items-center border-2 border-dashed rounded-lg p-4 text-center cursor-pointer transition-colors ${
|
|
126
|
+
isDragging
|
|
127
|
+
? "border-blue-500 bg-blue-50"
|
|
128
|
+
: "border-gray-300 hover:border-gray-400"
|
|
129
|
+
}`}
|
|
71
130
|
onClick={() => fileInputRef.current.click()}
|
|
131
|
+
onDragEnter={handleDragEnter}
|
|
132
|
+
onDragOver={handleDragOver}
|
|
133
|
+
onDragLeave={handleDragLeave}
|
|
134
|
+
onDrop={handleDrop}
|
|
72
135
|
>
|
|
73
136
|
<Icon
|
|
74
137
|
dimensions={{ width: "36", height: "36" }}
|
|
@@ -78,20 +141,27 @@ export const FileUploader = ({
|
|
|
78
141
|
<p className="mt-2 text-sm text-gray-500">
|
|
79
142
|
Click to upload or drag and drop
|
|
80
143
|
</p>
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
Supports multiple file uploads
|
|
84
|
-
|
|
85
|
-
|
|
144
|
+
<p className="text-xs text-gray-400">
|
|
145
|
+
{multiple
|
|
146
|
+
? "Supports multiple file uploads"
|
|
147
|
+
: "Single file upload only"}
|
|
148
|
+
</p>
|
|
86
149
|
</div>
|
|
87
150
|
<input
|
|
88
151
|
type="file"
|
|
89
152
|
ref={fileInputRef}
|
|
90
153
|
className="hidden"
|
|
91
|
-
onChange={handleFileChange}
|
|
154
|
+
onChange={(e) => handleFileChange(Array.from(e.target.files || []))}
|
|
92
155
|
multiple={multiple}
|
|
156
|
+
accept={accept}
|
|
93
157
|
/>
|
|
94
158
|
|
|
159
|
+
{fileCountError && (
|
|
160
|
+
<span className="text-xs text-red-500">
|
|
161
|
+
Maximum file count is set to {fileCount}
|
|
162
|
+
</span>
|
|
163
|
+
)}
|
|
164
|
+
|
|
95
165
|
{startsUpload && <AestheticProcessingAnimationWithStyles />}
|
|
96
166
|
|
|
97
167
|
{files.length > 0 && (
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
function processFiles(files: File[], chunkSize: number) {
|
|
2
|
+
const finalFiles: { file: Blob; originalFile: File }[] = [];
|
|
3
|
+
|
|
4
|
+
files.forEach((file) => {
|
|
5
|
+
if (file.size > chunkSize) {
|
|
6
|
+
let offset = 0;
|
|
7
|
+
while (offset < file.size) {
|
|
8
|
+
const chunk = file.slice(offset, offset + chunkSize);
|
|
9
|
+
finalFiles.push({ file: chunk, originalFile: file });
|
|
10
|
+
offset += chunkSize;
|
|
11
|
+
}
|
|
12
|
+
} else {
|
|
13
|
+
finalFiles.push({ file, originalFile: file });
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
return finalFiles;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Converts large files to chunks to send over network and sends
|
|
21
|
+
export async function sendFiles(
|
|
22
|
+
files: File[],
|
|
23
|
+
chunkSize: number,
|
|
24
|
+
apiUrl = "http://localhost:8080/upload"
|
|
25
|
+
) {
|
|
26
|
+
const processedFiles = processFiles(files, chunkSize);
|
|
27
|
+
const totalChunks = processedFiles.length;
|
|
28
|
+
|
|
29
|
+
for (let i = 0; i < processedFiles.length; i++) {
|
|
30
|
+
const { file, originalFile } = processedFiles[i];
|
|
31
|
+
const formData = new FormData();
|
|
32
|
+
formData.append("file", file, originalFile.name);
|
|
33
|
+
formData.append("originalname", originalFile.name);
|
|
34
|
+
|
|
35
|
+
if (file.size > chunkSize) {
|
|
36
|
+
formData.append("chunkIndex", i.toString());
|
|
37
|
+
formData.append("totalChunks", totalChunks.toString());
|
|
38
|
+
} else {
|
|
39
|
+
formData.append("chunkIndex", "0");
|
|
40
|
+
formData.append("totalChunks", "1");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const response = await fetch(apiUrl, {
|
|
45
|
+
method: "POST",
|
|
46
|
+
body: formData,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const data = await response.json();
|
|
54
|
+
|
|
55
|
+
if (data.documentId) {
|
|
56
|
+
console.log(
|
|
57
|
+
`File uploaded successfully. Document ID: ${data.documentId}`
|
|
58
|
+
);
|
|
59
|
+
} else {
|
|
60
|
+
console.log(`Chunk ${i + 1}/${totalChunks} uploaded`);
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error("Error uploading file chunk:", error);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|