@mgonza02/goal-image-gallery 1.0.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.
@@ -0,0 +1,244 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useTheme } from '@mui/material/styles';
4
+ import { useMediaQuery } from '@mui/material';
5
+ import ImageGalleryGrid from './components/ImageGalleryGrid';
6
+ import ImageGalleryModal from './components/ImageGalleryModal';
7
+ import { useImageGallery, useImageZoom, useClipboard, useKeyboardNavigation } from './hooks/use-image-gallery';
8
+ import { downloadImage } from './utils/image-utils';
9
+
10
+ /**
11
+ * Enhanced GoalImageGallery Component
12
+ *
13
+ * This component provides a modern, accessible image gallery with advanced upload capabilities.
14
+ *
15
+ * Key Features:
16
+ * - Multi-method image upload (file selection, clipboard paste, URL loading)
17
+ * - Responsive grid layout with hover effects
18
+ * - Full-screen image viewer with zoom controls
19
+ * - Keyboard navigation support
20
+ * - Drag and drop functionality
21
+ * - Mobile-friendly interface
22
+ * - Accessibility features (ARIA labels, keyboard navigation)
23
+ * - Loading states and error handling
24
+ *
25
+ * Upload Methods:
26
+ * 1. File Upload: Click to select or drag and drop image files
27
+ * 2. Clipboard Paste: Use Ctrl+V to paste images from clipboard
28
+ * 3. URL Loading: Enter image URLs to load remote images
29
+ *
30
+ * Keyboard Shortcuts (when image is open):
31
+ * - Arrow Left/Right: Navigate between images
32
+ * - +/=: Zoom in
33
+ * - -: Zoom out
34
+ * - Escape: Close image viewer
35
+ *
36
+ * Props:
37
+ * - canEdit: Enable editing capabilities
38
+ * - ownerEntity: Entity that owns the images
39
+ * - imageHandlerApi: API function for image operations
40
+ * - afterUpload: Callback after successful upload
41
+ * - imageCodes: Array or string of image codes
42
+ * - multiple: Allow multiple image selection
43
+ * - permission: Required permission for editing
44
+ * - emptyMessage: Message shown when no images
45
+ * - showImageInfo: Show image information overlay
46
+ * - allowDownload: Enable download functionality
47
+ * - getImageUrl: Function to get image URL from code
48
+ * - noImageUrl: Fallback image URL
49
+ * - showError: Function to show error messages
50
+ * - hasPermission: Function to check user permissions
51
+ */
52
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
53
+ const GoalImageGallery = _ref => {
54
+ let {
55
+ canEdit = false,
56
+ ownerEntity,
57
+ imageHandlerApi,
58
+ afterUpload: onRefresh,
59
+ imageCodes,
60
+ multiple = false,
61
+ permission,
62
+ emptyMessage = 'No hay imágenes para mostrar',
63
+ showImageInfo = false,
64
+ allowDownload = false,
65
+ getImageUrl = _ref2 => {
66
+ let {
67
+ imageCode
68
+ } = _ref2;
69
+ return `/images/${imageCode}`;
70
+ },
71
+ noImageUrl = '/images/no-image.png',
72
+ showError = message => console.error(message),
73
+ hasPermission = () => true,
74
+ currentCompany = 'default'
75
+ } = _ref;
76
+ const theme = useTheme();
77
+ const isMobile = useMediaQuery(theme.breakpoints.down('md'));
78
+ const [updatable, setUpdatable] = useState(false);
79
+
80
+ // Initialize permissions
81
+ useEffect(() => {
82
+ const isAllowed = !permission || hasPermission(permission);
83
+ setUpdatable(!!(isAllowed && canEdit && imageHandlerApi && ownerEntity));
84
+ }, [canEdit, ownerEntity, permission, hasPermission, imageHandlerApi]);
85
+
86
+ // Main gallery state and handlers
87
+ const {
88
+ openImage,
89
+ selectedImageIndex,
90
+ selectingImage,
91
+ imageList,
92
+ selectedImage,
93
+ zoomLevel,
94
+ loading,
95
+ removable,
96
+ setZoomLevel,
97
+ handleOpenImage,
98
+ handleCloseImage,
99
+ handleImageSelect,
100
+ handleCancel,
101
+ handleRemoveImage,
102
+ handleAfterUpload
103
+ } = useImageGallery({
104
+ imageCodes,
105
+ multiple,
106
+ canEdit,
107
+ updatable,
108
+ imageHandlerApi,
109
+ ownerEntity,
110
+ currentCompany,
111
+ onRefresh,
112
+ showError
113
+ });
114
+
115
+ // Zoom and navigation handlers
116
+ const {
117
+ handleZoomIn,
118
+ handleZoomOut,
119
+ handlePreviousImage,
120
+ handleNextImage
121
+ } = useImageZoom(getImageUrl, imageList);
122
+
123
+ // Enhanced zoom handlers that update the main state
124
+ const handleZoomInEnhanced = () => {
125
+ setZoomLevel(prev => Math.min(prev + 0.5, 3));
126
+ };
127
+ const handleZoomOutEnhanced = () => {
128
+ setZoomLevel(prev => Math.max(prev - 0.5, 0.5));
129
+ };
130
+ const handlePreviousImageEnhanced = () => {
131
+ const newImageUrl = handlePreviousImage();
132
+ handleOpenImage(newImageUrl, selectedImageIndex > 0 ? selectedImageIndex - 1 : imageList.length - 1);
133
+ };
134
+ const handleNextImageEnhanced = () => {
135
+ const newImageUrl = handleNextImage();
136
+ handleOpenImage(newImageUrl, selectedImageIndex < imageList.length - 1 ? selectedImageIndex + 1 : 0);
137
+ };
138
+
139
+ // Download handler
140
+ const handleDownloadImage = imageCode => {
141
+ const imageUrl = getImageUrl({
142
+ imageCode
143
+ });
144
+ downloadImage(imageUrl, imageCode);
145
+ };
146
+
147
+ // Clipboard paste handler
148
+ const handlePasteImage = file => {
149
+ handleImageSelect(null);
150
+ setTimeout(() => {
151
+ const event = new CustomEvent('pasteImage', {
152
+ detail: {
153
+ file
154
+ }
155
+ });
156
+ window.dispatchEvent(event);
157
+ }, 100);
158
+ };
159
+
160
+ // Clipboard support
161
+ const {
162
+ clipboardSupported
163
+ } = useClipboard(updatable, selectingImage, handlePasteImage);
164
+
165
+ // Keyboard navigation
166
+ useKeyboardNavigation(openImage, handlePreviousImageEnhanced, handleNextImageEnhanced, handleCloseImage, handleZoomInEnhanced, handleZoomOutEnhanced);
167
+
168
+ // Show image selector if needed
169
+ if (updatable && (selectingImage || !imageList || imageList.length === 0)) {
170
+ return /*#__PURE__*/_jsxs("div", {
171
+ style: {
172
+ padding: '20px',
173
+ border: '2px dashed #ccc',
174
+ textAlign: 'center',
175
+ borderRadius: '8px',
176
+ backgroundColor: '#f5f5f5'
177
+ },
178
+ children: [/*#__PURE__*/_jsx("p", {
179
+ children: multiple ? 'Seleccionar Imágenes' : 'Seleccionar Imagen'
180
+ }), /*#__PURE__*/_jsx("p", {
181
+ style: {
182
+ color: '#666',
183
+ fontSize: '14px'
184
+ },
185
+ children: multiple ? 'Arrastra imágenes aquí o haz clic para seleccionar' : 'Arrastra una imagen aquí o haz clic para seleccionar'
186
+ }), /*#__PURE__*/_jsx("button", {
187
+ onClick: handleCancel,
188
+ style: {
189
+ marginTop: '10px'
190
+ },
191
+ children: "Cancelar"
192
+ })]
193
+ });
194
+ }
195
+ return /*#__PURE__*/_jsxs(_Fragment, {
196
+ children: [/*#__PURE__*/_jsx(ImageGalleryGrid, {
197
+ imageList: imageList,
198
+ emptyMessage: emptyMessage,
199
+ multiple: multiple,
200
+ updatable: updatable,
201
+ removable: removable,
202
+ allowDownload: allowDownload,
203
+ showImageInfo: showImageInfo,
204
+ loading: loading,
205
+ clipboardSupported: clipboardSupported,
206
+ getImageUrl: getImageUrl,
207
+ noImageUrl: noImageUrl,
208
+ onImageClick: handleOpenImage,
209
+ onImageSelect: handleImageSelect,
210
+ onRemoveImage: handleRemoveImage,
211
+ onDownloadImage: handleDownloadImage
212
+ }), /*#__PURE__*/_jsx(ImageGalleryModal, {
213
+ open: openImage !== null,
214
+ imageUrl: openImage,
215
+ imageList: imageList,
216
+ selectedImageIndex: selectedImageIndex,
217
+ zoomLevel: zoomLevel,
218
+ noImageUrl: noImageUrl,
219
+ onClose: handleCloseImage,
220
+ onZoomIn: handleZoomInEnhanced,
221
+ onZoomOut: handleZoomOutEnhanced,
222
+ onPreviousImage: handlePreviousImageEnhanced,
223
+ onNextImage: handleNextImageEnhanced
224
+ })]
225
+ });
226
+ };
227
+ GoalImageGallery.propTypes = {
228
+ canEdit: PropTypes.bool,
229
+ ownerEntity: PropTypes.object,
230
+ imageHandlerApi: PropTypes.func,
231
+ afterUpload: PropTypes.func,
232
+ imageCodes: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
233
+ multiple: PropTypes.bool,
234
+ permission: PropTypes.string,
235
+ emptyMessage: PropTypes.string,
236
+ showImageInfo: PropTypes.bool,
237
+ allowDownload: PropTypes.bool,
238
+ getImageUrl: PropTypes.func,
239
+ noImageUrl: PropTypes.string,
240
+ showError: PropTypes.func,
241
+ hasPermission: PropTypes.func,
242
+ currentCompany: PropTypes.string
243
+ };
244
+ export default GoalImageGallery;
@@ -0,0 +1,87 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Typography, Stack, Chip, Button } from '@mui/material';
4
+ import { Image as ImageIcon, CloudUpload as UploadIcon, ContentPaste as PasteIcon, Link as LinkIcon, Add as AddIcon } from '@mui/icons-material';
5
+ import { StyledEmptyStateContainer } from '../styles/styled-components';
6
+
7
+ /**
8
+ * Empty state component for image gallery
9
+ */
10
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
11
+ const ImageGalleryEmptyState = _ref => {
12
+ let {
13
+ emptyMessage,
14
+ updatable,
15
+ clipboardSupported,
16
+ onAddImage
17
+ } = _ref;
18
+ return /*#__PURE__*/_jsx(StyledEmptyStateContainer, {
19
+ children: /*#__PURE__*/_jsxs(Stack, {
20
+ spacing: 3,
21
+ alignItems: "center",
22
+ children: [/*#__PURE__*/_jsx(ImageIcon, {
23
+ sx: {
24
+ fontSize: 64,
25
+ color: 'text.secondary'
26
+ }
27
+ }), /*#__PURE__*/_jsx(Typography, {
28
+ variant: "h6",
29
+ color: "text.secondary",
30
+ children: emptyMessage
31
+ }), updatable && /*#__PURE__*/_jsxs(_Fragment, {
32
+ children: [/*#__PURE__*/_jsx(Button, {
33
+ variant: "contained",
34
+ startIcon: /*#__PURE__*/_jsx(AddIcon, {}),
35
+ onClick: onAddImage,
36
+ sx: {
37
+ mt: 2
38
+ },
39
+ children: "Agregar imagen"
40
+ }), /*#__PURE__*/_jsxs(Stack, {
41
+ direction: "row",
42
+ spacing: 1,
43
+ alignItems: "center",
44
+ sx: {
45
+ mt: 2
46
+ },
47
+ children: [/*#__PURE__*/_jsx(Chip, {
48
+ icon: /*#__PURE__*/_jsx(UploadIcon, {}),
49
+ label: "Subir archivo",
50
+ variant: "outlined",
51
+ size: "small",
52
+ onClick: onAddImage
53
+ }), clipboardSupported && /*#__PURE__*/_jsx(Chip, {
54
+ icon: /*#__PURE__*/_jsx(PasteIcon, {}),
55
+ label: "Pegar (Ctrl+V)",
56
+ variant: "outlined",
57
+ size: "small",
58
+ color: "secondary"
59
+ }), /*#__PURE__*/_jsx(Chip, {
60
+ icon: /*#__PURE__*/_jsx(LinkIcon, {}),
61
+ label: "Desde URL",
62
+ variant: "outlined",
63
+ size: "small",
64
+ color: "info"
65
+ })]
66
+ }), /*#__PURE__*/_jsx(Typography, {
67
+ variant: "caption",
68
+ color: "text.secondary",
69
+ textAlign: "center",
70
+ children: "Soporta m\xFAltiples m\xE9todos de carga: archivos, portapapeles y URLs"
71
+ })]
72
+ })]
73
+ })
74
+ });
75
+ };
76
+ ImageGalleryEmptyState.propTypes = {
77
+ emptyMessage: PropTypes.string.isRequired,
78
+ updatable: PropTypes.bool,
79
+ clipboardSupported: PropTypes.bool,
80
+ onAddImage: PropTypes.func
81
+ };
82
+ ImageGalleryEmptyState.defaultProps = {
83
+ updatable: false,
84
+ clipboardSupported: false,
85
+ onAddImage: () => {}
86
+ };
87
+ export default ImageGalleryEmptyState;
@@ -0,0 +1,108 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Grid, Card, CardContent, useMediaQuery } from '@mui/material';
4
+ import { useTheme } from '@mui/material/styles';
5
+ import ImageGalleryItem from './ImageGalleryItem';
6
+ import ImageGalleryEmptyState from './ImageGalleryEmptyState';
7
+ import { StyledImageGridContainer } from '../styles/styled-components';
8
+ import { getGridColumns } from '../utils/image-utils';
9
+
10
+ /**
11
+ * Image grid component for displaying gallery images
12
+ */
13
+ import { jsx as _jsx } from "react/jsx-runtime";
14
+ const ImageGalleryGrid = _ref => {
15
+ let {
16
+ imageList,
17
+ emptyMessage,
18
+ multiple,
19
+ updatable,
20
+ removable,
21
+ allowDownload,
22
+ showImageInfo,
23
+ loading,
24
+ clipboardSupported,
25
+ getImageUrl,
26
+ noImageUrl,
27
+ onImageClick,
28
+ onImageSelect,
29
+ onRemoveImage,
30
+ onDownloadImage
31
+ } = _ref;
32
+ const theme = useTheme();
33
+ const isMobile = useMediaQuery(theme.breakpoints.down('md'));
34
+ const isTablet = useMediaQuery(theme.breakpoints.down('lg'));
35
+ if (imageList.length === 0) {
36
+ return /*#__PURE__*/_jsx(Card, {
37
+ elevation: 2,
38
+ children: /*#__PURE__*/_jsx(CardContent, {
39
+ children: /*#__PURE__*/_jsx(ImageGalleryEmptyState, {
40
+ emptyMessage: emptyMessage,
41
+ updatable: updatable,
42
+ clipboardSupported: clipboardSupported,
43
+ onAddImage: () => onImageSelect(null)
44
+ })
45
+ })
46
+ });
47
+ }
48
+ return /*#__PURE__*/_jsx(Card, {
49
+ elevation: 2,
50
+ children: /*#__PURE__*/_jsx(CardContent, {
51
+ children: /*#__PURE__*/_jsx(StyledImageGridContainer, {
52
+ children: /*#__PURE__*/_jsx(Grid, {
53
+ container: true,
54
+ spacing: 2,
55
+ children: imageList.map((image, index) => /*#__PURE__*/_jsx(Grid, {
56
+ item: true,
57
+ xs: getGridColumns(isMobile, isTablet, multiple),
58
+ sx: {
59
+ position: 'relative'
60
+ },
61
+ children: /*#__PURE__*/_jsx(ImageGalleryItem, {
62
+ image: image,
63
+ index: index,
64
+ getImageUrl: getImageUrl,
65
+ noImageUrl: noImageUrl,
66
+ loading: loading,
67
+ updatable: updatable,
68
+ removable: removable,
69
+ allowDownload: allowDownload,
70
+ showImageInfo: showImageInfo,
71
+ onImageClick: onImageClick,
72
+ onImageSelect: onImageSelect,
73
+ onRemoveImage: onRemoveImage,
74
+ onDownloadImage: onDownloadImage
75
+ })
76
+ }, image.id))
77
+ })
78
+ })
79
+ })
80
+ });
81
+ };
82
+ ImageGalleryGrid.propTypes = {
83
+ imageList: PropTypes.array.isRequired,
84
+ emptyMessage: PropTypes.string.isRequired,
85
+ multiple: PropTypes.bool,
86
+ updatable: PropTypes.bool,
87
+ removable: PropTypes.bool,
88
+ allowDownload: PropTypes.bool,
89
+ showImageInfo: PropTypes.bool,
90
+ loading: PropTypes.bool,
91
+ clipboardSupported: PropTypes.bool,
92
+ getImageUrl: PropTypes.func.isRequired,
93
+ noImageUrl: PropTypes.string.isRequired,
94
+ onImageClick: PropTypes.func.isRequired,
95
+ onImageSelect: PropTypes.func.isRequired,
96
+ onRemoveImage: PropTypes.func.isRequired,
97
+ onDownloadImage: PropTypes.func.isRequired
98
+ };
99
+ ImageGalleryGrid.defaultProps = {
100
+ multiple: false,
101
+ updatable: false,
102
+ removable: false,
103
+ allowDownload: false,
104
+ showImageInfo: false,
105
+ loading: false,
106
+ clipboardSupported: false
107
+ };
108
+ export default ImageGalleryGrid;
@@ -0,0 +1,175 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Box, IconButton, Skeleton, Tooltip, Typography, Button } from '@mui/material';
4
+ import { alpha, useTheme } from '@mui/material/styles';
5
+ import { ChangeCircle as ChangeIcon, Download as DownloadIcon, Visibility as ViewIcon, Add as AddIcon, Delete as DeleteIcon } from '@mui/icons-material';
6
+ import { StyledImageContainer, ImageOverlay } from '../styles/styled-components';
7
+ import { handleImageError } from '../utils/image-utils';
8
+
9
+ /**
10
+ * Individual image item component
11
+ */
12
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
+ const ImageGalleryItem = _ref => {
14
+ let {
15
+ image,
16
+ index,
17
+ getImageUrl,
18
+ noImageUrl,
19
+ loading,
20
+ updatable,
21
+ removable,
22
+ allowDownload,
23
+ showImageInfo,
24
+ onImageClick,
25
+ onImageSelect,
26
+ onRemoveImage,
27
+ onDownloadImage
28
+ } = _ref;
29
+ const theme = useTheme();
30
+
31
+ // Handle add button (+) image
32
+ if (image.image_code === '+') {
33
+ return /*#__PURE__*/_jsx(StyledImageContainer, {
34
+ sx: {
35
+ height: 200,
36
+ display: 'flex',
37
+ alignItems: 'center',
38
+ justifyContent: 'center',
39
+ border: '2px dashed',
40
+ borderColor: 'primary.main',
41
+ backgroundColor: alpha(theme.palette.primary.main, 0.04),
42
+ cursor: 'pointer',
43
+ '&:hover': {
44
+ backgroundColor: alpha(theme.palette.primary.main, 0.08)
45
+ }
46
+ },
47
+ onClick: () => onImageSelect(null),
48
+ children: /*#__PURE__*/_jsx(Button, {
49
+ variant: "outlined",
50
+ startIcon: /*#__PURE__*/_jsx(AddIcon, {}),
51
+ sx: {
52
+ width: '100%',
53
+ height: '100%',
54
+ border: 'none',
55
+ backgroundColor: 'transparent',
56
+ '&:hover': {
57
+ border: 'none',
58
+ backgroundColor: 'transparent'
59
+ }
60
+ },
61
+ children: "Agregar imagen"
62
+ })
63
+ });
64
+ }
65
+ return /*#__PURE__*/_jsxs(StyledImageContainer, {
66
+ children: [loading && /*#__PURE__*/_jsx(Skeleton, {
67
+ variant: "rectangular",
68
+ width: "100%",
69
+ height: 200,
70
+ animation: "wave"
71
+ }), /*#__PURE__*/_jsx("img", {
72
+ src: getImageUrl({
73
+ imageCode: image.image_code,
74
+ thumbMail: true
75
+ }),
76
+ alt: image.title || `Imagen ${index + 1}`,
77
+ onClick: () => onImageClick(getImageUrl({
78
+ imageCode: image.image_code
79
+ }), index),
80
+ onError: e => handleImageError(e, noImageUrl),
81
+ style: {
82
+ width: '100%',
83
+ height: 200,
84
+ objectFit: 'cover',
85
+ display: loading ? 'none' : 'block'
86
+ }
87
+ }, image.image_code), /*#__PURE__*/_jsxs(ImageOverlay, {
88
+ className: "image-overlay",
89
+ children: [/*#__PURE__*/_jsx(Tooltip, {
90
+ title: "Ver imagen",
91
+ children: /*#__PURE__*/_jsx(IconButton, {
92
+ size: "small",
93
+ onClick: e => {
94
+ e.stopPropagation();
95
+ onImageClick(getImageUrl({
96
+ imageCode: image.image_code
97
+ }), index);
98
+ },
99
+ children: /*#__PURE__*/_jsx(ViewIcon, {})
100
+ })
101
+ }), allowDownload && /*#__PURE__*/_jsx(Tooltip, {
102
+ title: "Descargar imagen",
103
+ children: /*#__PURE__*/_jsx(IconButton, {
104
+ size: "small",
105
+ onClick: e => {
106
+ e.stopPropagation();
107
+ onDownloadImage(image.image_code);
108
+ },
109
+ children: /*#__PURE__*/_jsx(DownloadIcon, {})
110
+ })
111
+ }), updatable && /*#__PURE__*/_jsx(Tooltip, {
112
+ title: "Cambiar imagen",
113
+ children: /*#__PURE__*/_jsx(IconButton, {
114
+ size: "small",
115
+ onClick: e => {
116
+ e.stopPropagation();
117
+ onImageSelect(image);
118
+ },
119
+ children: /*#__PURE__*/_jsx(ChangeIcon, {})
120
+ })
121
+ }), removable && /*#__PURE__*/_jsx(Tooltip, {
122
+ title: "Eliminar imagen",
123
+ children: /*#__PURE__*/_jsx(IconButton, {
124
+ size: "small",
125
+ onClick: e => {
126
+ e.stopPropagation();
127
+ if (window.confirm('¿Está seguro de que desea eliminar esta imagen?')) {
128
+ onRemoveImage(image);
129
+ }
130
+ },
131
+ color: "error",
132
+ children: /*#__PURE__*/_jsx(DeleteIcon, {})
133
+ })
134
+ })]
135
+ }), showImageInfo && /*#__PURE__*/_jsx(Box, {
136
+ sx: {
137
+ position: 'absolute',
138
+ bottom: 0,
139
+ left: 0,
140
+ right: 0,
141
+ backgroundColor: alpha(theme.palette.common.black, 0.7),
142
+ color: 'white',
143
+ p: 1
144
+ },
145
+ children: /*#__PURE__*/_jsx(Typography, {
146
+ variant: "caption",
147
+ noWrap: true,
148
+ children: image.title || `Imagen ${index + 1}`
149
+ })
150
+ })]
151
+ });
152
+ };
153
+ ImageGalleryItem.propTypes = {
154
+ image: PropTypes.object.isRequired,
155
+ index: PropTypes.number.isRequired,
156
+ getImageUrl: PropTypes.func.isRequired,
157
+ noImageUrl: PropTypes.string.isRequired,
158
+ loading: PropTypes.bool,
159
+ updatable: PropTypes.bool,
160
+ removable: PropTypes.bool,
161
+ allowDownload: PropTypes.bool,
162
+ showImageInfo: PropTypes.bool,
163
+ onImageClick: PropTypes.func.isRequired,
164
+ onImageSelect: PropTypes.func.isRequired,
165
+ onRemoveImage: PropTypes.func.isRequired,
166
+ onDownloadImage: PropTypes.func.isRequired
167
+ };
168
+ ImageGalleryItem.defaultProps = {
169
+ loading: false,
170
+ updatable: false,
171
+ removable: false,
172
+ allowDownload: false,
173
+ showImageInfo: false
174
+ };
175
+ export default ImageGalleryItem;