@mgonza02/goal-image-gallery 1.0.8 โ†’ 1.1.0

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/CHANGELOG.md CHANGED
@@ -5,6 +5,32 @@ All notable changes to the GoalImageGallery project will be documented in this f
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.1.0] - 2025-07-14
9
+
10
+ ### ๐Ÿ› Bug Fixes
11
+ - **Fixed handleAfterUpload Array Structure**: Resolved issue where `handleAfterUpload` was creating nested arrays instead of flat arrays for `image_code` parameter
12
+ - **Improved Array Handling**: Enhanced logic to properly handle both single image replacement and bulk image addition scenarios
13
+ - **Better Code Organization**: Refactored `handleAfterUpload` method for clearer logic flow and better maintainability
14
+
15
+ ### ๐Ÿ”ง Technical Improvements
16
+ - **Clearer Logic Separation**: Separated replacement logic from addition logic for better code readability
17
+ - **Duplicate Prevention**: Enhanced duplicate checking to prevent adding the same image code multiple times
18
+ - **Array Flattening**: Fixed array structure to ensure `image_code` is always a flat array of strings
19
+
20
+ ## [1.0.9] - 2025-07-14
21
+
22
+ ### โœจ New Features
23
+ - **Enhanced Bulk Upload Support**: The `handleAfterUpload` method now fully supports arrays of images
24
+ - **Comprehensive Bulk Upload Example**: Added detailed bulk upload example with drag-and-drop functionality
25
+ - **Improved Documentation**: Updated examples to demonstrate array-based upload patterns
26
+ - **Better TypeScript Support**: Enhanced type definitions for bulk upload scenarios
27
+
28
+ ### ๐Ÿ”ง Improvements
29
+ - Enhanced examples.js with a complete bulk upload gallery demonstration
30
+ - Added custom bulk upload selector component with drag-and-drop support
31
+ - Improved documentation for array-based `handleAfterUpload` usage
32
+ - Better error handling and progress indication for bulk uploads
33
+
8
34
  ## [1.0.8] - 2025-07-14
9
35
 
10
36
  ### ๐Ÿงน Bug Fixes
package/lib/examples.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // GoalImageGallery Usage Examples
2
2
  // This file demonstrates various ways to use the GoalImageGallery component
3
3
 
4
- import React, { useState, useCallback } from 'react';
4
+ import React, { useState, useCallback, useRef } from 'react';
5
5
  import { Box, Typography, Button, Divider } from '@mui/material';
6
6
  import GoalImageGallery from './GoalImageGallery';
7
7
 
@@ -646,6 +646,184 @@ export const CustomSelectorExample = () => {
646
646
  });
647
647
  };
648
648
 
649
+ // Example 9: Bulk Upload Gallery
650
+ export const BulkUploadExample = () => {
651
+ const [galleryImages, setGalleryImages] = useState(['bulk1', 'bulk2']);
652
+ const [uploading, setUploading] = useState(false);
653
+ const currentCompany = 'bulk-company';
654
+ const handleBulkUpload = useCallback(async imageData => {
655
+ setUploading(true);
656
+ try {
657
+ // Simulate bulk upload API call
658
+ const response = await new Promise(resolve => {
659
+ setTimeout(() => {
660
+ resolve({
661
+ success: true,
662
+ data: {
663
+ code: `bulk-${Date.now()}`
664
+ },
665
+ message: 'Bulk upload completed successfully'
666
+ });
667
+ }, 1000);
668
+ });
669
+ return response;
670
+ } catch (error) {
671
+ return {
672
+ success: false,
673
+ message: error.message
674
+ };
675
+ } finally {
676
+ setUploading(false);
677
+ }
678
+ }, []);
679
+ const handleBulkRefresh = useCallback(result => {
680
+ setGalleryImages(prev => [...prev, result.data.code]);
681
+ }, []);
682
+
683
+ // Custom bulk upload component
684
+ const BulkUploadSelector = _ref2 => {
685
+ let {
686
+ afterUpload,
687
+ activate,
688
+ onCancel,
689
+ multiple
690
+ } = _ref2;
691
+ const [dragActive, setDragActive] = useState(false);
692
+ const [selectedFiles, setSelectedFiles] = useState([]);
693
+ const fileInputRef = useRef(null);
694
+ const handleDrag = e => {
695
+ e.preventDefault();
696
+ e.stopPropagation();
697
+ if (e.type === 'dragenter' || e.type === 'dragover') {
698
+ setDragActive(true);
699
+ } else if (e.type === 'dragleave') {
700
+ setDragActive(false);
701
+ }
702
+ };
703
+ const handleDrop = e => {
704
+ e.preventDefault();
705
+ e.stopPropagation();
706
+ setDragActive(false);
707
+ const files = Array.from(e.dataTransfer.files);
708
+ handleFiles(files);
709
+ };
710
+ const handleFiles = async files => {
711
+ const imageFiles = files.filter(file => file.type.startsWith('image/'));
712
+ setSelectedFiles(imageFiles);
713
+ if (imageFiles.length > 0) {
714
+ // Simulate uploading multiple files and getting their codes
715
+ const uploadPromises = imageFiles.map(async (file, index) => {
716
+ return new Promise(resolve => {
717
+ setTimeout(() => {
718
+ resolve({
719
+ code: `bulk-img-${Date.now()}-${index}`,
720
+ url: URL.createObjectURL(file),
721
+ filename: file.name,
722
+ size: file.size,
723
+ mimeType: file.type
724
+ });
725
+ }, 500 + index * 100);
726
+ });
727
+ });
728
+ const uploadedImages = await Promise.all(uploadPromises);
729
+
730
+ // Call afterUpload with array of images
731
+ await afterUpload(uploadedImages);
732
+ }
733
+ };
734
+ const handleFileSelect = () => {
735
+ fileInputRef.current?.click();
736
+ };
737
+ return /*#__PURE__*/_jsxs(Box, {
738
+ onDragEnter: handleDrag,
739
+ onDragLeave: handleDrag,
740
+ onDragOver: handleDrag,
741
+ onDrop: handleDrop,
742
+ sx: {
743
+ border: `2px dashed ${dragActive ? '#007bff' : '#ccc'}`,
744
+ borderRadius: '12px',
745
+ padding: '40px',
746
+ textAlign: 'center',
747
+ backgroundColor: dragActive ? '#f8f9fa' : '#fff',
748
+ cursor: 'pointer',
749
+ transition: 'all 0.3s ease',
750
+ '&:hover': {
751
+ borderColor: '#007bff',
752
+ backgroundColor: '#f8f9fa'
753
+ }
754
+ },
755
+ onClick: handleFileSelect,
756
+ children: [/*#__PURE__*/_jsx("input", {
757
+ ref: fileInputRef,
758
+ type: "file",
759
+ multiple: true,
760
+ accept: "image/*",
761
+ style: {
762
+ display: 'none'
763
+ },
764
+ onChange: e => handleFiles(Array.from(e.target.files))
765
+ }), /*#__PURE__*/_jsx(Typography, {
766
+ variant: "h6",
767
+ gutterBottom: true,
768
+ children: "Bulk Upload Images"
769
+ }), /*#__PURE__*/_jsx(Typography, {
770
+ variant: "body2",
771
+ color: "text.secondary",
772
+ paragraph: true,
773
+ children: "Drop multiple images here or click to select"
774
+ }), selectedFiles.length > 0 && /*#__PURE__*/_jsxs(Typography, {
775
+ variant: "body2",
776
+ color: "success.main",
777
+ children: [selectedFiles.length, " file", selectedFiles.length !== 1 ? 's' : '', " selected"]
778
+ }), /*#__PURE__*/_jsx(Button, {
779
+ variant: "contained",
780
+ sx: {
781
+ mt: 2,
782
+ mr: 1
783
+ },
784
+ disabled: uploading,
785
+ children: uploading ? 'Uploading...' : 'Select Files'
786
+ }), /*#__PURE__*/_jsx(Button, {
787
+ variant: "outlined",
788
+ onClick: onCancel,
789
+ children: "Cancel"
790
+ })]
791
+ });
792
+ };
793
+ return /*#__PURE__*/_jsxs(Box, {
794
+ sx: {
795
+ p: 2
796
+ },
797
+ children: [/*#__PURE__*/_jsx(Typography, {
798
+ variant: "h6",
799
+ gutterBottom: true,
800
+ children: "Bulk Upload Gallery"
801
+ }), /*#__PURE__*/_jsx(Typography, {
802
+ variant: "body2",
803
+ color: "text.secondary",
804
+ paragraph: true,
805
+ children: "This example demonstrates bulk upload functionality where multiple images can be uploaded at once. The handleAfterUpload method now accepts arrays of image objects."
806
+ }), /*#__PURE__*/_jsx(GoalImageGallery, {
807
+ imageCodes: galleryImages,
808
+ canEdit: true,
809
+ ownerEntity: {
810
+ id: 'bulk-gallery',
811
+ type: 'gallery'
812
+ },
813
+ imageHandlerApi: handleBulkUpload,
814
+ afterUpload: handleBulkRefresh,
815
+ multiple: true,
816
+ emptyMessage: "Bulk upload gallery - drop multiple images to upload!",
817
+ showImageInfo: true,
818
+ allowDownload: true,
819
+ currentCompany: currentCompany,
820
+ slot: {
821
+ selector: BulkUploadSelector
822
+ }
823
+ })]
824
+ });
825
+ };
826
+
649
827
  // Complete Demo Component
650
828
  export const GoalImageGalleryExamples = () => {
651
829
  return /*#__PURE__*/_jsxs(Box, {
@@ -688,7 +866,11 @@ export const GoalImageGalleryExamples = () => {
688
866
  sx: {
689
867
  my: 3
690
868
  }
691
- }), /*#__PURE__*/_jsx(CustomSelectorExample, {})]
869
+ }), /*#__PURE__*/_jsx(CustomSelectorExample, {}), /*#__PURE__*/_jsx(Divider, {
870
+ sx: {
871
+ my: 3
872
+ }
873
+ }), /*#__PURE__*/_jsx(BulkUploadExample, {})]
692
874
  });
693
875
  };
694
876
  export default GoalImageGalleryExamples;
@@ -96,22 +96,64 @@ export const useImageGallery = _ref => {
96
96
  setLoading(false);
97
97
  }
98
98
  }, [ownerEntity, currentCompany, imageList, onRefresh, showError, imageHandlerApi]);
99
- const handleAfterUpload = useCallback(async image => {
99
+
100
+ /**
101
+ * Handles the upload completion for single or multiple images
102
+ * @param {Object|Object[]} uploadedImages - Single image object or array of image objects
103
+ * @param {string} uploadedImages.code - The image code returned from the upload
104
+ * @param {string} [uploadedImages.url] - Optional image URL
105
+ * @param {string} [uploadedImages.filename] - Optional filename
106
+ */
107
+ const handleAfterUpload = useCallback(async uploadedImages => {
100
108
  const currentImageCode = selectedImage?.image_code || '';
101
- const imagesToSave = imageList.reduce((acc, img) => {
102
- if (img.image_code !== '+') {
103
- acc.push(img.image_code === currentImageCode ? image.code : img.image_code);
109
+
110
+ // Handle both single image and array of images
111
+ const imagesArray = Array.isArray(uploadedImages) ? uploadedImages : [uploadedImages];
112
+ let imagesToSave = [];
113
+ if (currentImageCode) {
114
+ // Replacing an existing image
115
+ imagesToSave = imageList.reduce((acc, img) => {
116
+ if (img.image_code !== '+') {
117
+ if (img.image_code === currentImageCode) {
118
+ // Replace the selected image with the first uploaded image
119
+ acc.push(imagesArray[0].code);
120
+ } else {
121
+ // Keep existing image codes
122
+ acc.push(img.image_code);
123
+ }
124
+ }
125
+ return acc;
126
+ }, []);
127
+
128
+ // If multiple images uploaded, add the additional ones
129
+ if (imagesArray.length > 1) {
130
+ imagesArray.slice(1).forEach(image => {
131
+ if (!imagesToSave.includes(image.code)) {
132
+ imagesToSave.push(image.code);
133
+ }
134
+ });
104
135
  }
105
- return acc;
106
- }, []);
107
- if (!currentImageCode) {
108
- imagesToSave.push(image.code);
136
+ } else {
137
+ // Adding new images (no current selection)
138
+ imagesToSave = imageList.reduce((acc, img) => {
139
+ if (img.image_code !== '+') {
140
+ acc.push(img.image_code);
141
+ }
142
+ return acc;
143
+ }, []);
144
+
145
+ // Add all new image codes
146
+ imagesArray.forEach(image => {
147
+ if (!imagesToSave.includes(image.code)) {
148
+ imagesToSave.push(image.code);
149
+ }
150
+ });
109
151
  }
110
152
  const result = await imageHandlerApi({
111
153
  ...ownerEntity,
112
154
  companyId: currentCompany,
113
- image_code: imagesToSave,
114
- company_id: currentCompany
155
+ company_id: currentCompany,
156
+ image_code: imagesToSave
115
157
  });
116
158
  if (result.success) {
117
159
  setSelectingImage(false);
package/lib/index.d.ts CHANGED
@@ -34,6 +34,15 @@ export interface ImageItem {
34
34
  image_code: string;
35
35
  }
36
36
 
37
+ // Uploaded image interface
38
+ export interface UploadedImage {
39
+ code: string;
40
+ url?: string;
41
+ filename?: string;
42
+ size?: number;
43
+ mimeType?: string;
44
+ }
45
+
37
46
  // Hook interfaces
38
47
  export interface UseImageGalleryOptions {
39
48
  imageCodes?: string | string[];
@@ -62,7 +71,7 @@ export interface UseImageGalleryReturn {
62
71
  handleImageSelect: (image: ImageItem | null) => void;
63
72
  handleCancel: () => void;
64
73
  handleRemoveImage: (image: ImageItem) => Promise<void>;
65
- handleAfterUpload: (image: any) => Promise<void>;
74
+ handleAfterUpload: (images: UploadedImage | UploadedImage[]) => Promise<void>;
66
75
  }
67
76
 
68
77
  export interface UseImageZoomReturn {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mgonza02/goal-image-gallery",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "description": "Advanced, accessible, and highly customizable React image gallery component with multi-method upload, responsive design, and extensive TypeScript support",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib/index.js",