@mgonza02/goal-image-gallery 1.0.7 → 1.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/CHANGELOG.md CHANGED
@@ -5,6 +5,29 @@ 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.0.9] - 2025-07-14
9
+
10
+ ### ✨ New Features
11
+ - **Enhanced Bulk Upload Support**: The `handleAfterUpload` method now fully supports arrays of images
12
+ - **Comprehensive Bulk Upload Example**: Added detailed bulk upload example with drag-and-drop functionality
13
+ - **Improved Documentation**: Updated examples to demonstrate array-based upload patterns
14
+ - **Better TypeScript Support**: Enhanced type definitions for bulk upload scenarios
15
+
16
+ ### 🔧 Improvements
17
+ - Enhanced examples.js with a complete bulk upload gallery demonstration
18
+ - Added custom bulk upload selector component with drag-and-drop support
19
+ - Improved documentation for array-based `handleAfterUpload` usage
20
+ - Better error handling and progress indication for bulk uploads
21
+
22
+ ## [1.0.8] - 2025-07-14
23
+
24
+ ### 🧹 Bug Fixes
25
+ - Fixed an issue with the `use-image-gallery` hook where the `imageCodes` prop was not being correctly split into an array.
26
+ - Updated the `GoalImageGallery` component to ensure the `currentCompany` prop can accept both string and number types for better flexibility.
27
+ - Removed unused `clipboardSupport` prop from the `GoalImageGallery` component to avoid confusion.
28
+ - Ensured consistent handling of `currentCompany` across all components.
29
+
30
+
8
31
  ## [1.0.7] - 2025-07-14
9
32
  ### 🧹 Bug fix
10
33
  - Fixed an issue with clipboard support detection in the `use-image-gallery` hook.
@@ -231,7 +231,7 @@ const GoalImageGallery = _ref => {
231
231
  noImageUrl = "/images/no-image.png",
232
232
  showError = message => console.error(message),
233
233
  hasPermission = () => true,
234
- currentCompany = "default",
234
+ currentCompany = 0,
235
235
  slot = {},
236
236
  slotProps = {}
237
237
  } = _ref;
@@ -415,7 +415,7 @@ GoalImageGallery.propTypes = {
415
415
  noImageUrl: PropTypes.string,
416
416
  showError: PropTypes.func,
417
417
  hasPermission: PropTypes.func,
418
- currentCompany: PropTypes.string,
418
+ currentCompany: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
419
419
  slot: PropTypes.object,
420
420
  slotProps: PropTypes.object
421
421
  };
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;
@@ -26,7 +26,7 @@ export const useImageGallery = _ref => {
26
26
 
27
27
  // Process image codes into image list
28
28
  useEffect(() => {
29
- const splitedImages = Array.isArray(imageCodes) ? imageCodes : imageCodes?.split(/[\s,]+/);
29
+ const splitedImages = Array.isArray(imageCodes) ? imageCodes : (imageCodes || '')?.split(/[\s,]+/);
30
30
  let newList = [];
31
31
  if (splitedImages) {
32
32
  const list = splitedImages.map((image_code, index) => ({
@@ -96,22 +96,51 @@ 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 || '';
109
+
110
+ // Handle both single image and array of images
111
+ const imagesArray = Array.isArray(uploadedImages) ? uploadedImages : [uploadedImages];
101
112
  const imagesToSave = imageList.reduce((acc, img) => {
102
113
  if (img.image_code !== '+') {
103
- acc.push(img.image_code === currentImageCode ? image.code : img.image_code);
114
+ // If this is the image being replaced, replace with the first uploaded image
115
+ if (img.image_code === currentImageCode && imagesArray.length > 0) {
116
+ acc.push(imagesArray[0].code);
117
+ } else {
118
+ acc.push(img.image_code);
119
+ }
104
120
  }
105
121
  return acc;
106
122
  }, []);
123
+
124
+ // If no current image code (adding new images), add all new image codes
107
125
  if (!currentImageCode) {
108
- imagesToSave.push(image.code);
126
+ imagesArray.forEach(image => {
127
+ if (!imagesToSave.includes(image.code)) {
128
+ imagesToSave.push(image.code);
129
+ }
130
+ });
131
+ } else if (imagesArray.length > 1) {
132
+ // If replacing and multiple images uploaded, add additional images
133
+ imagesArray.slice(1).forEach(image => {
134
+ if (!imagesToSave.includes(image.code)) {
135
+ imagesToSave.push(image.code);
136
+ }
137
+ });
109
138
  }
110
139
  const result = await imageHandlerApi({
111
140
  ...ownerEntity,
112
141
  companyId: currentCompany,
113
- image_code: imagesToSave,
114
- company_id: currentCompany
142
+ company_id: currentCompany,
143
+ image_code: imagesToSave
115
144
  });
116
145
  if (result.success) {
117
146
  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.7",
3
+ "version": "1.0.9",
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",