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
@@ -17,6 +17,7 @@ const COMPONENTS = [
17
17
  "Modal",
18
18
  "Spinner",
19
19
  "Toast",
20
+ "Uploader"
20
21
  ];
21
22
  const SOURCE_PATH = path.join(__dirname, "source", "components");
22
23
  const DEST_PATH = path.join(process.cwd(), "src", "component-lib");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gbs-add-block",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "React Component Library",
5
5
  "files": [
6
6
  "index.js",
@@ -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 = "bg-gray-100 p-2 rounded-lg";
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-60 relative">
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-[200px] relative">
149
+ <div className="relative w-full" ref={selectRef}>
150
+ <div className="w-full relative">
150
151
  <button
151
- className="flex items-center border px-4 py-2 w-[200px] justify-between rounded-lg font-medium text-sm dark:bg-black dark:border-white dark:text-white"
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-[200px] 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">
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
- // adds files to file array
32
- const handleFileChange = (e: any) => {
33
- const selectedFiles = Array.from(e.target.files);
34
- const newPreviewUrls = { ...previewUrls };
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
- selectedFiles.forEach((file: any) => {
65
+ newFiles.forEach((file) => {
37
66
  if (showImagePreview && file.type.startsWith("image/")) {
38
67
  const reader = new FileReader();
39
68
  reader.onload = () => {
40
- newPreviewUrls[file.name] = reader.result as string;
41
- setPreviewUrls(newPreviewUrls);
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
- // remove file from the file array
54
- const removeFile = (index: any) => {
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 { [fileToRemove.name]: _, ...rest } = prevPreviews;
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
- className="flex flex-col items-center border-2 border-dashed border-gray-300 rounded-lg p-4 text-center cursor-pointer hover:border-gray-400 transition-colors"
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
- {multiple && (
82
- <p className="text-xs text-gray-400">
83
- Supports multiple file uploads
84
- </p>
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
+ }