@plone-collective/volto-image-editor 1.0.0-alpha.1 → 1.0.0-alpha.2

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.
Files changed (38) hide show
  1. package/.changelog.draft +3 -14
  2. package/CHANGELOG.md +7 -0
  3. package/README.md +3 -1
  4. package/package.json +14 -4
  5. package/src/components/ImageEditor/Editor/ImageEditor.mdx +217 -0
  6. package/src/components/ImageEditor/{ImageEditor.scss → Editor/ImageEditor.scss} +1 -1
  7. package/src/components/ImageEditor/Editor/ImageEditor.stories.tsx +101 -0
  8. package/src/components/ImageEditor/{ImageEditor.tsx → Editor/ImageEditor.tsx} +7 -7
  9. package/src/components/ImageEditor/Wrapper/ImageEditorWrapper.mdx +613 -0
  10. package/src/components/ImageEditor/Wrapper/ImageEditorWrapper.stories.tsx +400 -0
  11. package/src/components/ImageEditor/{ImageEditorWrapper.tsx → Wrapper/ImageEditorWrapper.tsx} +27 -5
  12. package/src/components/ImageEditor/components/AdjustableCropperBackground.tsx +1 -1
  13. package/src/components/ImageEditor/components/AdjustableImage/AdjustableImage.mdx +440 -0
  14. package/src/components/ImageEditor/components/AdjustableImage/AdjustableImage.stories.tsx +109 -0
  15. package/src/components/ImageEditor/components/AdjustablePreviewBackground.tsx +1 -1
  16. package/src/components/ImageEditor/components/Button/Button.mdx +260 -0
  17. package/src/components/ImageEditor/components/{Button.scss → Button/Button.scss} +1 -1
  18. package/src/components/ImageEditor/components/Button/Button.stories.tsx +69 -0
  19. package/src/components/ImageEditor/components/Navigation/Navigation.mdx +453 -0
  20. package/src/components/ImageEditor/components/{Navigation.scss → Navigation/Navigation.scss} +1 -1
  21. package/src/components/ImageEditor/components/Navigation/Navigation.stories.tsx +103 -0
  22. package/src/components/ImageEditor/components/{Navigation.tsx → Navigation/Navigation.tsx} +1 -1
  23. package/src/components/ImageEditor/components/SettingsModal/SettingsModal.mdx +565 -0
  24. package/src/components/ImageEditor/components/SettingsModal/SettingsModal.stories.tsx +149 -0
  25. package/src/components/ImageEditor/components/{SettingsModal.tsx → SettingsModal/SettingsModal.tsx} +2 -2
  26. package/src/components/ImageEditor/components/Slider/Slider.mdx +380 -0
  27. package/src/components/ImageEditor/components/{Slider.scss → Slider/Slider.scss} +1 -1
  28. package/src/components/ImageEditor/components/Slider/Slider.stories.tsx +121 -0
  29. package/src/components/Widgets/FileWidget.tsx +1 -1
  30. package/src/stories/Overview.mdx +467 -0
  31. package/src/stories/utils.tsx +64 -0
  32. package/src/types/storybook-react.d.ts +5 -0
  33. /package/src/components/ImageEditor/{ImageEditorWrapper.scss → Wrapper/ImageEditorWrapper.scss} +0 -0
  34. /package/src/components/ImageEditor/components/{AdjustableImage.scss → AdjustableImage/AdjustableImage.scss} +0 -0
  35. /package/src/components/ImageEditor/components/{AdjustableImage.tsx → AdjustableImage/AdjustableImage.tsx} +0 -0
  36. /package/src/components/ImageEditor/components/{Button.tsx → Button/Button.tsx} +0 -0
  37. /package/src/components/ImageEditor/components/{SettingsModal.scss → SettingsModal/SettingsModal.scss} +0 -0
  38. /package/src/components/ImageEditor/components/{Slider.tsx → Slider/Slider.tsx} +0 -0
package/.changelog.draft CHANGED
@@ -1,19 +1,8 @@
1
- ## 1.0.0-alpha.1 (2025-10-20)
1
+ ## 1.0.0-alpha.2 (2025-10-29)
2
2
 
3
3
  ### Feature
4
4
 
5
- - Adds support editing the brightness of an image. @Humanaice
6
- - Adds support editing the contrast of an image. @Humanaice
7
- - Adds support editing the hue of an image. @Humanaice
8
- - Adds support editing the saturation of an image. @Humanaice
9
- - Adds support for image cropping. @Humanaice
10
- - Adds support for image flipping. @Humanaice
11
- - Adds support for rotating the image by 90 degrees clockwise and counter-clockwise. @Humanaice
12
- - Implement a widget to edit Images in Volto. @Humanaice
13
-
14
- ### Internal
15
-
16
- - Adds basic instructions to use GitHub Copilot features. @ericof
17
- - Implement GitHub Action's workflows. @ericof
5
+ - Update README to include a small demo of @plone-collective/volto-image-editor. @humanaice [#4](https://github.com/collective/volto-image-editor/issue/4)
6
+ - Add Storybook stories for all components. @humanaice [#5](https://github.com/collective/volto-image-editor/issue/5)
18
7
 
19
8
 
package/CHANGELOG.md CHANGED
@@ -8,6 +8,13 @@
8
8
 
9
9
  <!-- towncrier release notes start -->
10
10
 
11
+ ## 1.0.0-alpha.2 (2025-10-29)
12
+
13
+ ### Feature
14
+
15
+ - Update README to include a small demo of @plone-collective/volto-image-editor. @humanaice [#4](https://github.com/collective/volto-image-editor/issue/4)
16
+ - Add Storybook stories for all components. @humanaice [#5](https://github.com/collective/volto-image-editor/issue/5)
17
+
11
18
  ## 1.0.0-alpha.1 (2025-10-20)
12
19
 
13
20
  ### Feature
package/README.md CHANGED
@@ -3,11 +3,13 @@
3
3
  An image editor, based on [React Advanced Image Cropper](https://www.npmjs.com/package/react-advanced-cropper), to be used with Plone and Volto.
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/@plone-collective/volto-image-editor)](https://www.npmjs.com/package/@plone-collective/volto-image-editor)
6
- [![](https://img.shields.io/badge/-Storybook-ff4785?logo=Storybook&logoColor=white&style=flat-square)](https://simplesconsultoria.github.io/volto-image-editor/)
6
+ [![](https://img.shields.io/badge/-Storybook-ff4785?logo=Storybook&logoColor=white&style=flat-square)](https://collective.github.io/volto-image-editor/)
7
7
  [![Code analysis checks](https://github.com/collective/volto-image-editor/actions/workflows/main.yml/badge.svg)](https://github.com/collective/volto-image-editor/actions/workflows/main.yml)
8
8
 
9
9
  ## ✨ Features
10
10
 
11
+ ![Demo](https://raw.githubusercontent.com/collective/volto-image-editor/refs/heads/main/docs/docs/_static/volto-image-editor.gif)
12
+
11
13
 
12
14
  This add-on modifies Volto's File widget, providing an image editor with the following actions:
13
15
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plone-collective/volto-image-editor",
3
- "version": "1.0.0-alpha.1",
3
+ "version": "1.0.0-alpha.2",
4
4
  "description": "An image editor to be used with Plone and Volto.",
5
5
  "main": "src/index.ts",
6
6
  "license": "MIT",
@@ -10,12 +10,22 @@
10
10
  "plone",
11
11
  "react"
12
12
  ],
13
- "author": "Simples Consultoria",
13
+ "author": {
14
+ "name": "Simples Consultoria",
15
+ "url": "https://simplesconsultoria.com.br/"
16
+ },
14
17
  "homepage": "https://github.com/collective/volto-image-editor#readme",
15
18
  "repository": {
16
19
  "type": "git",
17
20
  "url": "git@github.com:collective/volto-image-editor"
18
21
  },
22
+ "funding": {
23
+ "type": "github",
24
+ "url": "https://github.com/sponsors/plone"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/collective/volto-image-edito/issues"
28
+ },
19
29
  "publishConfig": {
20
30
  "access": "public"
21
31
  },
@@ -32,8 +42,8 @@
32
42
  "react": "18.2.0",
33
43
  "react-aria-components": "*",
34
44
  "react-dom": "18.2.0",
35
- "@plone/types": "2.0.0-alpha.5",
36
- "@plone/registry": "3.0.0-alpha.5"
45
+ "@plone/registry": "3.0.0-alpha.5",
46
+ "@plone/types": "2.0.0-alpha.5"
37
47
  },
38
48
  "devDependencies": {
39
49
  "@types/node": "^20.2.5",
@@ -0,0 +1,217 @@
1
+ import { Meta, Canvas, Story, Controls, ArgTypes } from '@storybook/blocks';
2
+ import * as ImageEditorStories from './ImageEditor.stories';
3
+
4
+ <Meta of={ImageEditorStories} />
5
+
6
+ # ImageEditor
7
+
8
+ The `ImageEditor` is the main component that provides a complete interface for image editing, including crop, rotation, color adjustments and advanced settings.
9
+
10
+ ## Overview
11
+
12
+ This component combines several features:
13
+
14
+ - **Interactive cropper** with rectangular and circular stencils
15
+ - **Transformations**: rotation (90°), horizontal/vertical flip
16
+ - **Color adjustments**: brightness, contrast, saturation and hue
17
+ - **Advanced settings**: aspect ratio, restrictions, dimensions
18
+
19
+ ---
20
+
21
+ ## Examples
22
+
23
+ ### Landscape (Horizontal)
24
+
25
+ Most common format for landscape photos, screenshots and banners.
26
+
27
+ <Canvas of={ImageEditorStories.Landscape} />
28
+
29
+ ### Portrait (Vertical)
30
+
31
+ Ideal for profile photos, stories and mobile content.
32
+
33
+ <Canvas of={ImageEditorStories.Portrait} />
34
+
35
+ ### Compact (Small)
36
+
37
+ Demonstrates the editor in reduced dimensions, useful for modals or sidebars.
38
+
39
+ <Canvas of={ImageEditorStories.Compact} />
40
+
41
+ ---
42
+
43
+ ## How to Use
44
+
45
+ ### Basic
46
+
47
+ ```tsx
48
+ import ImageEditor from '@plone-collective/volto-image-editor/components/ImageEditor/ImageEditor';
49
+
50
+ function MyEditor() {
51
+ const handleSave = (dataUrl: string) => {
52
+ // dataUrl is the edited image in base64 format
53
+ console.log('Image saved:', dataUrl);
54
+ };
55
+
56
+ const handleCancel = () => {
57
+ console.log('Edit cancelled');
58
+ };
59
+
60
+ return (
61
+ <ImageEditor
62
+ src="https://example.com/image.jpg"
63
+ onImageSave={handleSave}
64
+ onCancel={handleCancel}
65
+ />
66
+ );
67
+ }
68
+ ```
69
+
70
+ ### With Data URL
71
+
72
+ ```tsx
73
+ const imageDataUrl = 'data:image/jpeg;base64,/9j/4AAQ...';
74
+
75
+ <ImageEditor
76
+ src={imageDataUrl}
77
+ onImageSave={(newDataUrl) => {
78
+ // Save the edited image
79
+ uploadImage(newDataUrl);
80
+ }}
81
+ onCancel={() => closeEditor()}
82
+ />;
83
+ ```
84
+
85
+ ---
86
+
87
+ ## Features
88
+
89
+ ### Edit Modes
90
+
91
+ The editor has different modes accessible via the navigation bar:
92
+
93
+ - **Crop**: Allows cropping the image with the configured stencil
94
+ - **Brightness**: Adjusts image brightness (-100% to +100%)
95
+ - **Contrast**: Adjusts contrast (-100% to +100%)
96
+ - **Saturation**: Adjusts color saturation (-100% to +100%)
97
+ - **Hue**: Adjusts hue/tone (360° rotation)
98
+
99
+ ### Transformations
100
+
101
+ Actions available regardless of mode:
102
+
103
+ - **Rotate Left**: Rotates 90° counterclockwise
104
+ - **Rotate Right**: Rotates 90° clockwise
105
+ - **Flip Horizontal**: Mirrors horizontally
106
+ - **Flip Vertical**: Mirrors vertically
107
+ - **Reset Rotation**: Undoes all transformations (appears after applying rotations/flips)
108
+
109
+ ### Settings
110
+
111
+ Accessible via gear icon:
112
+
113
+ - **Aspect Ratio**: Free, 1:1, 3:4, 4:3, 16:9, 9:16, 2:1, 21:9, 1:2
114
+ - **Image Restriction**: Fit, Fill, Stencil, Free
115
+ - **Stencil Type**: Rectangle, Circle
116
+ - **Minimum dimensions**: Min Width/Height for crop
117
+ - **Maximum dimensions**: Max Width/Height of crop (optional)
118
+ - **Stencil Grid**: Shows grid in stencil to assist composition
119
+
120
+ ---
121
+
122
+ ## Keyboard Navigation
123
+
124
+ ### In Cropper
125
+
126
+ - **Arrow keys**: Move stencil
127
+ - **Shift + Arrow keys**: Move faster
128
+ - **Alt + Arrow keys**: Resize stencil
129
+
130
+ ### In Sliders (Adjustments)
131
+
132
+ - **Arrow Left/Down**: Decrease value
133
+ - **Arrow Right/Up**: Increase value
134
+ - **Page Down**: Decrease 10 steps
135
+ - **Page Up**: Increase 10 steps
136
+ - **Home**: Minimum value
137
+ - **End**: Maximum value
138
+ - **Space/Enter**: Reset to zero
139
+
140
+ ---
141
+
142
+ ## Performance
143
+
144
+ ### Implemented Optimizations
145
+
146
+ 1. **Canvas Rendering**: Filters applied via Canvas API (hardware-accelerated)
147
+ 2. **Debounced Updates**: Preview updates efficiently during adjustments
148
+ 3. **Lazy Loading**: Configuration components loaded on demand
149
+
150
+ ### Recommendations
151
+
152
+ - **Image size**: Ideal between 800x600 and 2000x1500 pixels
153
+ - **Format**: JPEG or PNG (WebP supported in modern browsers)
154
+ - **File size**: Up to 5MB for better performance
155
+
156
+ ---
157
+
158
+ ## Accessibility
159
+
160
+ - All buttons have descriptive labels
161
+ - Complete keyboard navigation
162
+ - ARIA attributes on sliders (role="slider", aria-valuemin, etc)
163
+ - Visible focus indicators
164
+ - Adequate contrast in all states
165
+
166
+ ---
167
+
168
+ ## Troubleshooting
169
+
170
+ ### Image won't load
171
+
172
+ - Check CORS if image is from external domain
173
+ - Use `crossOrigin="anonymous"` if necessary
174
+ - Confirm that URL/dataURL is valid
175
+
176
+ ### Crop too small
177
+
178
+ - Adjust `minWidth` and `minHeight` in settings
179
+ - Check `imageRestriction` (may be limiting)
180
+
181
+ ### Slow performance
182
+
183
+ - Reduce image size before loading
184
+ - Avoid too frequent adjustments (debounce)
185
+ - Use JPEG format for photos (smaller than PNG)
186
+
187
+ ---
188
+
189
+ ## Props
190
+
191
+ ### src
192
+
193
+ - **Type**: `string`
194
+ - **Required**: ✅
195
+ - **Description**: URL or data URL of the image to be edited
196
+
197
+ ### onImageSave
198
+
199
+ - **Type**: `(dataUrl: string) => void`
200
+ - **Required**: ✅
201
+ - **Description**: Callback called on save, receives the edited image as data URL
202
+
203
+ ### onCancel
204
+
205
+ - **Type**: `() => void`
206
+ - **Required**: ✅
207
+ - **Description**: Callback called when editing is cancelled
208
+
209
+ ---
210
+
211
+ ## Related Components
212
+
213
+ - **ImageEditorWrapper**: Wrapper with modal for use in widgets
214
+ - **Navigation**: Editor toolbar
215
+ - **SettingsModal**: Advanced settings panel
216
+ - **Slider**: Control for color adjustments
217
+ - **AdjustableImage**: Image renderer with filters
@@ -1,5 +1,5 @@
1
1
  @import '~react-advanced-cropper/dist/style.css';
2
- @import '../../theme/constants';
2
+ @import '../../../theme/constants';
3
3
 
4
4
  .image-editor {
5
5
  width: 100%;
@@ -0,0 +1,101 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import React from 'react';
3
+ import ImageEditor from './ImageEditor';
4
+ import { DemoContainer, createDemoImageDataURL } from '../../../stories/utils';
5
+
6
+ // Generate demo images once to avoid recreation
7
+ const demoImageSrc = createDemoImageDataURL(960, 640);
8
+ const portraitImageSrc = createDemoImageDataURL(640, 960);
9
+
10
+ const meta: Meta = {
11
+ title: 'ImageEditor/ImageEditor',
12
+ component: ImageEditor as any,
13
+ parameters: {
14
+ layout: 'fullscreen',
15
+ docs: {
16
+ description: {
17
+ component:
18
+ 'Full image editor with cropper, adjustments, and settings. Provides complete editing capabilities including crop, rotate, flip, and color adjustments.',
19
+ },
20
+ },
21
+ },
22
+ argTypes: {
23
+ src: { control: false },
24
+ onImageSave: { control: false },
25
+ onCancel: { control: false },
26
+ },
27
+ };
28
+ export default meta;
29
+
30
+ type Story = StoryObj;
31
+
32
+ export const Landscape: Story = {
33
+ render: () => {
34
+ function LandscapeStory() {
35
+ const handleSave = (_dataUrl: string) => {
36
+ /* no-op in story */
37
+ };
38
+
39
+ return (
40
+ <DemoContainer width={960} height={640}>
41
+ <ImageEditor
42
+ src={demoImageSrc}
43
+ onImageSave={handleSave}
44
+ onCancel={() => {
45
+ /* no-op */
46
+ }}
47
+ />
48
+ </DemoContainer>
49
+ );
50
+ }
51
+ return <LandscapeStory />;
52
+ },
53
+ };
54
+
55
+ export const Portrait: Story = {
56
+ render: () => {
57
+ function PortraitStory() {
58
+ const handleSave = (_dataUrl: string) => {
59
+ /* no-op in story */
60
+ };
61
+
62
+ return (
63
+ <DemoContainer width={640} height={960}>
64
+ <ImageEditor
65
+ src={portraitImageSrc}
66
+ onImageSave={handleSave}
67
+ onCancel={() => {
68
+ /* no-op */
69
+ }}
70
+ />
71
+ </DemoContainer>
72
+ );
73
+ }
74
+ return <PortraitStory />;
75
+ },
76
+ };
77
+
78
+ export const Compact: Story = {
79
+ render: () => {
80
+ function CompactStory() {
81
+ const compactImageSrc = createDemoImageDataURL(480, 320);
82
+
83
+ const handleSave = (_dataUrl: string) => {
84
+ /* no-op in story */
85
+ };
86
+
87
+ return (
88
+ <DemoContainer width={640} height={480}>
89
+ <ImageEditor
90
+ src={compactImageSrc}
91
+ onImageSave={handleSave}
92
+ onCancel={() => {
93
+ /* no-op */
94
+ }}
95
+ />
96
+ </DemoContainer>
97
+ );
98
+ }
99
+ return <CompactStory />;
100
+ },
101
+ };
@@ -8,13 +8,13 @@ import {
8
8
  ImageRestriction,
9
9
  } from 'react-advanced-cropper';
10
10
  import type { CropperRef, CropperPreviewRef } from 'react-advanced-cropper';
11
- import { AdjustablePreviewBackground } from './components/AdjustablePreviewBackground';
12
- import { Navigation } from './components/Navigation';
13
- import { Slider } from './components/Slider';
14
- import { AdjustableCropperBackground } from './components/AdjustableCropperBackground';
15
- import { Button } from './components/Button';
16
- import { SettingsModal } from './components/SettingsModal';
17
- import type { ImageSettings } from './types/ImageSettings';
11
+ import { AdjustablePreviewBackground } from '../components/AdjustablePreviewBackground';
12
+ import { Navigation } from '../components/Navigation/Navigation';
13
+ import { Slider } from '../components/Slider/Slider';
14
+ import { AdjustableCropperBackground } from '../components/AdjustableCropperBackground';
15
+ import { Button } from '../components/Button/Button';
16
+ import { SettingsModal } from '../components/SettingsModal/SettingsModal';
17
+ import type { ImageSettings } from '../types/ImageSettings';
18
18
  import { ResetIcon } from '@plone-collective/volto-image-editor/icons/ResetIcon';
19
19
  import './ImageEditor.scss';
20
20