@mgonza02/goal-image-gallery 1.2.5 → 1.2.6

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,14 @@ 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.2.6] - 2026-10-13
9
+
10
+ ### 🚀 Enhancements & Responsive Layout
11
+ - **Full-Width Hero / First Image (`featuredFirst`)**: Single-image galleries (`imageList.length === 1`) now automatically occupy full container width (`xs: 12, sm: 12, md: 12`) instead of being squished into a 25% narrow strip.
12
+ - **Featured First Image Prop (`featuredFirst`)**: Added `featuredFirst?: boolean` prop to allow the primary/first image (`index === 0`) to span all 12 columns as a featured hero image, while subsequent images render in balanced responsive columns underneath.
13
+ - **Balanced 2 & 3 Image Grids**: 2-image galleries split evenly (`50% / 50%`) and 3-image galleries split (`33% / 33% / 33%`) when `featuredFirst` is disabled, maximizing space usage.
14
+ - **Customizable Image Heights**: Added `itemHeight` (default: 200px) and `featuredItemHeight` (default: 240px) props to `GoalImageGallery`, `ImageGalleryGrid`, and `ImageGalleryItem`.
15
+
8
16
  ## [1.2.5] - 2026-10-13
9
17
 
10
18
  ### 🐛 Bug Fixes
@@ -262,6 +262,68 @@ const PermissionBasedGallery = ({ images, entityId, entityType }) => {
262
262
  allowDownload={hasPermission('DOWNLOAD_IMAGES')}
263
263
  />
264
264
  );
265
+ ### 3. Advanced Management, Limits & Lifecycle Callbacks (v1.2.2 - v1.2.5)
266
+
267
+ In v1.2.2 – v1.2.5, `GoalImageGallery` introduces fine-grained operational controls, a floating action button for adding images, default image highlighting/selection, and reactive lifecycle hooks:
268
+
269
+ ```javascript
270
+ import React, { useState, useCallback } from 'react';
271
+ import { GoalImageGallery, validateImageFile } from '@mgonza02/goal-image-gallery';
272
+
273
+ const ProductCatalogGallery = ({ product, onSave }) => {
274
+ const [imageCodes, setImageCodes] = useState(product.image_codes || []);
275
+ const [defaultImage, setDefaultImage] = useState(product.default_image || imageCodes[0]);
276
+
277
+ // Granular lifecycle callbacks
278
+ const handleAdd = useCallback((newCode) => {
279
+ console.log('Image added:', newCode);
280
+ }, []);
281
+
282
+ const handleUpdate = useCallback((oldCode, newCode) => {
283
+ console.log(`Replaced ${oldCode} with ${newCode}`);
284
+ if (defaultImage === oldCode) setDefaultImage(newCode);
285
+ }, [defaultImage]);
286
+
287
+ const handleDelete = useCallback((deletedCode) => {
288
+ console.log('Deleted image:', deletedCode);
289
+ if (defaultImage === deletedCode) {
290
+ setDefaultImage(imageCodes.filter(c => c !== deletedCode)[0]);
291
+ }
292
+ }, [defaultImage, imageCodes]);
293
+
294
+ const handleSetDefault = useCallback((image) => {
295
+ setDefaultImage(image.image_code);
296
+ onSave?.({ ...product, default_image: image.image_code });
297
+ }, [product, onSave]);
298
+
299
+ return (
300
+ <GoalImageGallery
301
+ imageCodes={imageCodes}
302
+ canEdit={true}
303
+ multiple={true}
304
+ ownerEntity={{ id: product.id, type: 'product' }}
305
+ imageHandlerApi={uploadApiHandler}
306
+ afterUpload={(res) => setImageCodes(res.data.image_codes)}
307
+
308
+ // Floating Add Button & Item Capping (v1.2.2 & v1.2.4)
309
+ maxItems={6} // Caps gallery at 6 images; hides floating add button
310
+ allowDelete={true} // Delete enabled in single and multi modes
311
+ minItems={1} // Must keep at least 1 image; prevents deleting last photo
312
+
313
+ // Minimum Dimension Validation (v1.2.2)
314
+ minImageWidth={400}
315
+ minImageHeight={400}
316
+
317
+ // Default Image Badge & Selection (v1.2.3)
318
+ defaultImageCode={defaultImage}
319
+ onSetAsDefault={handleSetDefault} // Star icon appears on hover when >= 2 images
320
+
321
+ // Granular Lifecycle Hooks (v1.2.3)
322
+ onAdd={handleAdd}
323
+ onUpdate={handleUpdate}
324
+ onDelete={handleDelete}
325
+ />
326
+ );
265
327
  };
266
328
  ```
267
329
 
@@ -54,11 +54,53 @@ import { GoalImageGallery } from '@mgonza02/goal-image-gallery';
54
54
  | `imageHandlerApi` | `function` | - | Upload handler function |
55
55
  | `afterUpload` | `function` | - | Success callback |
56
56
  | `multiple` | `boolean` | `false` | Allow multiple images |
57
+ | `maxItems` | `number` | - | Maximum items allowed (hides floating add button when reached) |
58
+ | `allowDelete` | `boolean` | `true` | Enable deletion (available in both single and multi-mode) |
59
+ | `minItems` | `number` | `0` | Minimum items required (disables delete at or below this count) |
60
+ | `minImageWidth` | `number` | - | Minimum image width in pixels |
61
+ | `minImageHeight` | `number` | - | Minimum image height in pixels |
62
+ | `defaultImageCode` | `string` | - | Marks default image with a "Predeterminada" badge |
63
+ | `onSetAsDefault` | `(image) => void` | - | Callback when set as default is clicked (multiple & >=2 images) |
64
+ | `onDelete` | `(imageCode) => void` | - | Fired after successful image deletion |
65
+ | `onUpdate` | `(oldCode, newCode) => void` | - | Fired after successful image replacement |
66
+ | `onAdd` | `(imageCode) => void` | - | Fired after each newly added image |
57
67
  | `permission` | `string` | - | Required permission |
58
68
  | `emptyMessage` | `string` | `'No hay imágenes para mostrar'` | Empty state message |
59
69
  | `showImageInfo` | `boolean` | `false` | Show image info |
60
70
  | `allowDownload` | `boolean` | `true` | Enable downloads |
61
71
 
72
+ ## Floating Add Button (v1.2.4+)
73
+
74
+ In editable multi-image galleries, the add button is rendered as a floating action button (`Fab`) over the gallery card container (`bottom: 16`, `right: 16`, `zIndex: 3`) instead of occupying a grid slot tile. The button automatically hides when `maxItems` is reached.
75
+
76
+ ## Validation & Utilities (v1.2.2 - v1.2.5)
77
+
78
+ ```javascript
79
+ import {
80
+ validateImageFile,
81
+ validateImageFileSync,
82
+ countGalleryImages,
83
+ utils
84
+ } from '@mgonza02/goal-image-gallery';
85
+
86
+ // Async validation (size, MIME type, min dimensions)
87
+ const result = await validateImageFile(file, {
88
+ maxSize: 10 * 1024 * 1024,
89
+ allowedTypes: ['image/jpeg', 'image/png', 'image/webp'],
90
+ minWidth: 400,
91
+ minHeight: 400
92
+ });
93
+ if (!result.isValid) {
94
+ console.error(result.errors);
95
+ }
96
+
97
+ // Synchronous validation (size & MIME type)
98
+ const syncResult = validateImageFileSync(file, { maxSize: 5 * 1024 * 1024 });
99
+
100
+ // Count actual images (excluding any '+' placeholders)
101
+ const total = countGalleryImages(imageList);
102
+ ```
103
+
62
104
  ## Upload Methods
63
105
 
64
106
  ### 1. File Upload
package/README.md CHANGED
@@ -45,6 +45,14 @@ import { GoalImageGallery } from '@mgonza02/goal-image-gallery';
45
45
  - **TypeScript Support**: Full type definitions included
46
46
  - **Hook-Based Architecture**: Reusable custom hooks for complex logic
47
47
 
48
+ ### ⚡ **Gallery Controls & Management (v1.2.2 - v1.2.5)**
49
+ - **Floating Add Button**: Modern floating action button (`Fab`) over the gallery card rather than occupying a grid tile, automatically hidden when `maxItems` is reached.
50
+ - **Default Image Highlighting**: Highlight primary/default image with a `Predeterminada` badge via `defaultImageCode`, and quick-set default star action via `onSetAsDefault` (for multi-image galleries with 2+ images).
51
+ - **Item Limits & Delete Thresholds**: Enforce maximum images (`maxItems`), allow delete in both single and multi-image modes (`allowDelete`), and protect minimum remaining images (`minItems`).
52
+ - **Image Dimension Validation**: Enforce minimum image width and height (`minImageWidth`, `minImageHeight`).
53
+ - **Granular Lifecycle Callbacks**: Reactive hooks for individual lifecycle events: `onAdd`, `onUpdate`, and `onDelete`.
54
+ - **Validation Utilities**: Exported `validateImageFile` (async with dimension check), `validateImageFileSync` (sync size & MIME check), and `countGalleryImages`.
55
+
48
56
  ### 📱 **Mobile-First Design**
49
57
  - **Touch Gestures**: Swipe, pinch-to-zoom, and tap interactions
50
58
  - **Responsive Breakpoints**: Optimized layouts for all devices
@@ -240,6 +240,9 @@ const GoalImageGallery = _ref => {
240
240
  minImageWidth,
241
241
  minImageHeight,
242
242
  defaultImageCode,
243
+ featuredFirst = false,
244
+ itemHeight = 200,
245
+ featuredItemHeight = 240,
243
246
  onSetAsDefault,
244
247
  onDelete,
245
248
  onUpdate,
@@ -417,7 +420,10 @@ const GoalImageGallery = _ref => {
417
420
  onDownloadImage: handleDownloadImage,
418
421
  canSetDefault: canSetDefault,
419
422
  canAdd: canAdd,
420
- defaultImageCode: defaultImageCode
423
+ defaultImageCode: defaultImageCode,
424
+ featuredFirst: featuredFirst,
425
+ itemHeight: itemHeight,
426
+ featuredItemHeight: featuredItemHeight
421
427
  }), /*#__PURE__*/_jsx(ImageGalleryModal, {
422
428
  open: openImage !== null,
423
429
  imageUrl: openImage,
@@ -457,6 +463,9 @@ GoalImageGallery.propTypes = {
457
463
  minImageWidth: PropTypes.number,
458
464
  minImageHeight: PropTypes.number,
459
465
  defaultImageCode: PropTypes.string,
466
+ featuredFirst: PropTypes.bool,
467
+ itemHeight: PropTypes.number,
468
+ featuredItemHeight: PropTypes.number,
460
469
  onSetAsDefault: PropTypes.func,
461
470
  onDelete: PropTypes.func,
462
471
  onUpdate: PropTypes.func,
@@ -7,9 +7,41 @@ import ImageGalleryEmptyState from './ImageGalleryEmptyState';
7
7
  import { StyledImageGridContainer } from '../styles/styled-components';
8
8
 
9
9
  /**
10
- * Image grid component for displaying gallery images
10
+ * Helper to compute responsive grid column size for gallery items
11
11
  */
12
12
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
+ const getItemGridSize = (index, totalCount, multiple, featuredFirst) => {
14
+ if (totalCount === 1 || featuredFirst && index === 0) {
15
+ return {
16
+ xs: 12,
17
+ sm: 12,
18
+ md: 12
19
+ };
20
+ }
21
+ if (totalCount === 2 && !featuredFirst) {
22
+ return {
23
+ xs: 12,
24
+ sm: 6,
25
+ md: 6
26
+ };
27
+ }
28
+ if (totalCount === 3 && !featuredFirst) {
29
+ return {
30
+ xs: 12,
31
+ sm: 4,
32
+ md: 4
33
+ };
34
+ }
35
+ return {
36
+ xs: 6,
37
+ sm: multiple ? 4 : 6,
38
+ md: multiple ? 3 : 4
39
+ };
40
+ };
41
+
42
+ /**
43
+ * Image grid component for displaying gallery images
44
+ */
13
45
  const ImageGalleryGrid = _ref => {
14
46
  let {
15
47
  imageList,
@@ -30,7 +62,10 @@ const ImageGalleryGrid = _ref => {
30
62
  onDownloadImage,
31
63
  canSetDefault = false,
32
64
  canAdd = false,
33
- defaultImageCode
65
+ defaultImageCode,
66
+ featuredFirst = false,
67
+ itemHeight = 200,
68
+ featuredItemHeight = 240
34
69
  } = _ref;
35
70
  const theme = useTheme();
36
71
  if (imageList.length === 0) {
@@ -56,34 +91,35 @@ const ImageGalleryGrid = _ref => {
56
91
  children: /*#__PURE__*/_jsx(Grid, {
57
92
  container: true,
58
93
  spacing: 2,
59
- children: imageList.map((image, index) => /*#__PURE__*/_jsx(Grid, {
60
- size: {
61
- xs: 6,
62
- sm: multiple ? 4 : 6,
63
- md: multiple ? 3 : 4
64
- },
65
- sx: {
66
- position: 'relative'
67
- },
68
- children: /*#__PURE__*/_jsx(ImageGalleryItem, {
69
- image: image,
70
- index: index,
71
- getImageUrl: getImageUrl,
72
- noImageUrl: noImageUrl,
73
- loading: loading,
74
- updatable: updatable,
75
- removable: removable,
76
- allowDownload: allowDownload,
77
- showImageInfo: showImageInfo,
78
- onImageClick: onImageClick,
79
- onImageSelect: onImageSelect,
80
- onRemoveImage: onRemoveImage,
81
- onSetAsDefault: onSetAsDefault,
82
- onDownloadImage: onDownloadImage,
83
- canSetDefault: canSetDefault,
84
- isDefault: !!defaultImageCode && image.image_code === defaultImageCode
85
- })
86
- }, image.id))
94
+ children: imageList.map((image, index) => {
95
+ const isFeatured = imageList.length === 1 || featuredFirst && index === 0;
96
+ const height = isFeatured && featuredItemHeight ? featuredItemHeight : itemHeight;
97
+ return /*#__PURE__*/_jsx(Grid, {
98
+ size: getItemGridSize(index, imageList.length, multiple, featuredFirst),
99
+ sx: {
100
+ position: 'relative'
101
+ },
102
+ children: /*#__PURE__*/_jsx(ImageGalleryItem, {
103
+ image: image,
104
+ index: index,
105
+ getImageUrl: getImageUrl,
106
+ noImageUrl: noImageUrl,
107
+ loading: loading,
108
+ updatable: updatable,
109
+ removable: removable,
110
+ allowDownload: allowDownload,
111
+ showImageInfo: showImageInfo,
112
+ itemHeight: height,
113
+ onImageClick: onImageClick,
114
+ onImageSelect: onImageSelect,
115
+ onRemoveImage: onRemoveImage,
116
+ onSetAsDefault: onSetAsDefault,
117
+ onDownloadImage: onDownloadImage,
118
+ canSetDefault: canSetDefault,
119
+ isDefault: !!defaultImageCode && image.image_code === defaultImageCode
120
+ })
121
+ }, image.id);
122
+ })
87
123
  })
88
124
  })
89
125
  }), canAdd && /*#__PURE__*/_jsx(Tooltip, {
@@ -123,6 +159,9 @@ ImageGalleryGrid.propTypes = {
123
159
  onDownloadImage: PropTypes.func.isRequired,
124
160
  canSetDefault: PropTypes.bool,
125
161
  canAdd: PropTypes.bool,
126
- defaultImageCode: PropTypes.string
162
+ defaultImageCode: PropTypes.string,
163
+ featuredFirst: PropTypes.bool,
164
+ itemHeight: PropTypes.number,
165
+ featuredItemHeight: PropTypes.number
127
166
  };
128
167
  export default ImageGalleryGrid;
@@ -23,6 +23,7 @@ const ImageGalleryItem = _ref => {
23
23
  showImageInfo = false,
24
24
  canSetDefault = false,
25
25
  isDefault = false,
26
+ itemHeight = 200,
26
27
  onImageClick,
27
28
  onImageSelect,
28
29
  onRemoveImage,
@@ -36,7 +37,7 @@ const ImageGalleryItem = _ref => {
36
37
  return /*#__PURE__*/_jsx(Box, {
37
38
  sx: {
38
39
  ...getImageContainerStyles(theme),
39
- height: 200,
40
+ height: itemHeight,
40
41
  display: 'flex',
41
42
  alignItems: 'center',
42
43
  justifyContent: 'center',
@@ -95,7 +96,7 @@ const ImageGalleryItem = _ref => {
95
96
  }), loading && /*#__PURE__*/_jsx(Skeleton, {
96
97
  variant: "rectangular",
97
98
  width: "100%",
98
- height: 200,
99
+ height: itemHeight,
99
100
  animation: "wave"
100
101
  }), /*#__PURE__*/_jsx("img", {
101
102
  src: getImageUrl({
@@ -109,7 +110,7 @@ const ImageGalleryItem = _ref => {
109
110
  onError: e => handleImageError(e, noImageUrl),
110
111
  style: {
111
112
  width: '100%',
112
- height: 200,
113
+ height: itemHeight,
113
114
  objectFit: 'cover',
114
115
  display: loading ? 'none' : 'block'
115
116
  }
@@ -203,6 +204,7 @@ ImageGalleryItem.propTypes = {
203
204
  showImageInfo: PropTypes.bool,
204
205
  canSetDefault: PropTypes.bool,
205
206
  isDefault: PropTypes.bool,
207
+ itemHeight: PropTypes.number,
206
208
  onImageClick: PropTypes.func.isRequired,
207
209
  onImageSelect: PropTypes.func.isRequired,
208
210
  onRemoveImage: PropTypes.func.isRequired,
package/lib/index.d.ts CHANGED
@@ -35,6 +35,12 @@ export interface GoalImageGalleryProps {
35
35
  minImageHeight?: number;
36
36
  /** Image code marked as default (badge + star state) */
37
37
  defaultImageCode?: string;
38
+ /** When true, the first image uses all space (12 columns) as a featured hero image */
39
+ featuredFirst?: boolean;
40
+ /** Height in pixels of gallery image items (default: 200) */
41
+ itemHeight?: number;
42
+ /** Height in pixels of featured / single gallery image items (default: 240) */
43
+ featuredItemHeight?: number;
38
44
  /** Called when user sets an image as default (multi-image galleries with 2+ images) */
39
45
  onSetAsDefault?: (imageCode: string) => void;
40
46
  /** Called after a successful delete */
package/package.json CHANGED
@@ -1,89 +1,92 @@
1
1
  {
2
- "name": "@mgonza02/goal-image-gallery",
3
- "version": "1.2.5",
4
- "description": "Advanced, accessible, and highly customizable React image gallery component with multi-method upload, responsive design, and extensive TypeScript support",
5
- "main": "lib/index.js",
6
- "module": "lib/index.js",
7
- "types": "lib/index.d.ts",
8
- "keywords": [
9
- "react",
10
- "image-gallery",
11
- "material-ui",
12
- "component",
13
- "upload",
14
- "drag-drop",
15
- "clipboard",
16
- "modal",
17
- "zoom",
18
- "responsive"
19
- ],
20
- "author": "Goal Team",
21
- "license": "MIT",
22
- "repository": {
23
- "type": "git",
24
- "url": "git+https://github.com/mgonza02/goal-image-gallery.git"
25
- },
26
- "homepage": "https://github.com/mgonza02/goal-image-gallery#readme",
27
- "bugs": {
28
- "url": "https://github.com/mgonza02/goal-image-gallery/issues"
29
- },
30
- "publishConfig": {
31
- "access": "public"
32
- },
33
- "files": [
34
- "lib/",
35
- "README.md",
36
- "CHANGELOG.md",
37
- "QUICK_REFERENCE.md",
38
- "MIGRATION.md",
39
- "IMPLEMENTATION_GUIDE.md",
40
- "IMPLEMENTATION_SUMMARY.md",
41
- "DOCUMENTATION_UPDATE_SUMMARY.md",
42
- "PACKAGE_CLEANUP_SUMMARY.md",
43
- "LICENSE"
44
- ],
45
- "peerDependencies": {
46
- "@emotion/react": "^11.14.0",
47
- "@emotion/styled": "^11.14.0",
48
- "@mui/icons-material": "^9.0.0",
49
- "@mui/material": "^9.0.0",
50
- "react": "^18.0.0",
51
- "react-dom": "^18.0.0"
52
- },
53
- "dependencies": {
54
- "prop-types": "^15.8.0"
55
- },
56
- "devDependencies": {
57
- "@babel/cli": "^7.20.0",
58
- "@babel/core": "^7.20.0",
59
- "@babel/preset-env": "^7.20.0",
60
- "@babel/preset-react": "^7.18.0",
61
- "prettier": "^3.4.2"
62
- },
63
- "browserslist": {
64
- "production": [
65
- ">0.2%",
66
- "not dead",
67
- "not op_mini all"
2
+ "name": "@mgonza02/goal-image-gallery",
3
+ "version": "1.2.6",
4
+ "description": "Advanced, accessible, and highly customizable React image gallery component with multi-method upload, responsive design, and extensive TypeScript support",
5
+ "main": "lib/index.js",
6
+ "module": "lib/index.js",
7
+ "types": "lib/index.d.ts",
8
+ "keywords": [
9
+ "react",
10
+ "image-gallery",
11
+ "material-ui",
12
+ "component",
13
+ "upload",
14
+ "drag-drop",
15
+ "clipboard",
16
+ "modal",
17
+ "zoom",
18
+ "responsive"
68
19
  ],
69
- "development": [
70
- "last 1 chrome version",
71
- "last 1 firefox version",
72
- "last 1 safari version"
73
- ]
74
- },
75
- "engines": {
76
- "node": ">=18.0.0",
77
- "pnpm": ">=9.0.0"
78
- },
79
- "scripts": {
80
- "build": "pnpm clean && pnpm build:js && pnpm build:types",
81
- "build:js": "babel src --out-dir lib --copy-files --ignore '**/*.test.js'",
82
- "build:types": "cp src/index.d.ts lib/index.d.ts",
83
- "format": "prettier --write src/**/*.js",
84
- "format:check": "prettier --check src/**/*.js",
85
- "clean": "rm -rf lib",
86
- "validate": "pnpm format:check",
87
- "dev": "pnpm build -- --watch"
88
- }
20
+ "author": "Goal Team",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/mgonza02/goal-image-gallery.git"
25
+ },
26
+ "homepage": "https://github.com/mgonza02/goal-image-gallery#readme",
27
+ "bugs": {
28
+ "url": "https://github.com/mgonza02/goal-image-gallery/issues"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "files": [
34
+ "lib/",
35
+ "README.md",
36
+ "CHANGELOG.md",
37
+ "QUICK_REFERENCE.md",
38
+ "MIGRATION.md",
39
+ "IMPLEMENTATION_GUIDE.md",
40
+ "IMPLEMENTATION_SUMMARY.md",
41
+ "DOCUMENTATION_UPDATE_SUMMARY.md",
42
+ "PACKAGE_CLEANUP_SUMMARY.md",
43
+ "LICENSE"
44
+ ],
45
+ "peerDependencies": {
46
+ "@emotion/react": "^11.14.0",
47
+ "@emotion/styled": "^11.14.0",
48
+ "@mui/icons-material": "^9.0.0",
49
+ "@mui/material": "^9.0.0",
50
+ "react": "^18.0.0",
51
+ "react-dom": "^18.0.0"
52
+ },
53
+ "dependencies": {
54
+ "prop-types": "^15.8.0"
55
+ },
56
+ "packageManager": "pnpm@10.33.2",
57
+ "scripts": {
58
+ "build": "pnpm clean && pnpm build:js && pnpm build:types",
59
+ "build:js": "babel src --out-dir lib --copy-files --ignore '**/*.test.js'",
60
+ "build:types": "cp src/index.d.ts lib/index.d.ts",
61
+ "prepublishOnly": "pnpm build",
62
+ "format": "prettier --write src/**/*.js",
63
+ "format:check": "prettier --check src/**/*.js",
64
+ "clean": "rm -rf lib",
65
+ "validate": "pnpm format:check",
66
+ "dev": "pnpm build -- --watch",
67
+ "prepack": "pnpm validate && pnpm build"
68
+ },
69
+ "devDependencies": {
70
+ "@babel/cli": "^7.20.0",
71
+ "@babel/core": "^7.20.0",
72
+ "@babel/preset-env": "^7.20.0",
73
+ "@babel/preset-react": "^7.18.0",
74
+ "prettier": "^3.4.2"
75
+ },
76
+ "browserslist": {
77
+ "production": [
78
+ ">0.2%",
79
+ "not dead",
80
+ "not op_mini all"
81
+ ],
82
+ "development": [
83
+ "last 1 chrome version",
84
+ "last 1 firefox version",
85
+ "last 1 safari version"
86
+ ]
87
+ },
88
+ "engines": {
89
+ "node": ">=18.0.0",
90
+ "pnpm": ">=9.0.0"
91
+ }
89
92
  }