@plone/volto 17.17.0 → 17.18.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/locales/ca/LC_MESSAGES/volto.po +21 -0
  3. package/locales/ca.json +1 -1
  4. package/locales/de/LC_MESSAGES/volto.po +21 -0
  5. package/locales/de.json +1 -1
  6. package/locales/en/LC_MESSAGES/volto.po +21 -0
  7. package/locales/en.json +1 -1
  8. package/locales/es/LC_MESSAGES/volto.po +21 -0
  9. package/locales/es.json +1 -1
  10. package/locales/eu/LC_MESSAGES/volto.po +21 -0
  11. package/locales/eu.json +1 -1
  12. package/locales/fi/LC_MESSAGES/volto.po +21 -0
  13. package/locales/fi.json +1 -1
  14. package/locales/fr/LC_MESSAGES/volto.po +21 -0
  15. package/locales/fr.json +1 -1
  16. package/locales/it/LC_MESSAGES/volto.po +21 -0
  17. package/locales/it.json +1 -1
  18. package/locales/ja/LC_MESSAGES/volto.po +21 -0
  19. package/locales/ja.json +1 -1
  20. package/locales/nl/LC_MESSAGES/volto.po +21 -0
  21. package/locales/nl.json +1 -1
  22. package/locales/pt/LC_MESSAGES/volto.po +21 -0
  23. package/locales/pt.json +1 -1
  24. package/locales/pt_BR/LC_MESSAGES/volto.po +21 -0
  25. package/locales/pt_BR.json +1 -1
  26. package/locales/ro/LC_MESSAGES/volto.po +21 -0
  27. package/locales/ro.json +1 -1
  28. package/locales/volto.pot +22 -1
  29. package/locales/zh_CN/LC_MESSAGES/volto.po +21 -0
  30. package/locales/zh_CN.json +1 -1
  31. package/package.json +1 -1
  32. package/packages/volto-slate/package.json +1 -1
  33. package/src/components/manage/AnchorPlugin/useLinkEditor.jsx +79 -0
  34. package/src/components/manage/Widgets/ImageWidget.jsx +311 -0
  35. package/src/config/Widgets.jsx +2 -0
  36. package/theme/themes/pastanaga/extras/blocks.less +42 -0
  37. package/theme/themes/pastanaga/extras/widgets.less +108 -0
  38. package/types/components/manage/AnchorPlugin/useLinkEditor.d.ts +7 -0
  39. package/types/components/manage/Widgets/ImageWidget.d.ts +116 -0
  40. package/types/config/Widgets.d.ts +2 -0
@@ -0,0 +1,311 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { Button, Dimmer, Loader, Message } from 'semantic-ui-react';
3
+ import { useIntl, defineMessages } from 'react-intl';
4
+ import { useDispatch } from 'react-redux';
5
+ import { useLocation } from 'react-router-dom';
6
+ import loadable from '@loadable/component';
7
+ import { connect } from 'react-redux';
8
+ import { compose } from 'redux';
9
+ import useLinkEditor from '@plone/volto/components/manage/AnchorPlugin/useLinkEditor';
10
+ import withObjectBrowser from '@plone/volto/components/manage/Sidebar/ObjectBrowser';
11
+
12
+ import {
13
+ flattenToAppURL,
14
+ getBaseUrl,
15
+ isInternalURL,
16
+ validateFileUploadSize,
17
+ } from '@plone/volto/helpers';
18
+ import { createContent } from '@plone/volto/actions';
19
+ import { readAsDataURL } from 'promise-file-reader';
20
+ import { FormFieldWrapper, Icon } from '@plone/volto/components';
21
+
22
+ import imageBlockSVG from '@plone/volto/components/manage/Blocks/Image/block-image.svg';
23
+ import clearSVG from '@plone/volto/icons/clear.svg';
24
+ import navTreeSVG from '@plone/volto/icons/nav.svg';
25
+ import linkSVG from '@plone/volto/icons/link.svg';
26
+ import uploadSVG from '@plone/volto/icons/upload.svg';
27
+
28
+ const Dropzone = loadable(() => import('react-dropzone'));
29
+
30
+ export const ImageToolbar = ({ className, data, id, onChange, selected }) => (
31
+ <div className="image-upload-widget-toolbar">
32
+ <Button.Group>
33
+ <Button icon basic onClick={() => onChange(id, null)}>
34
+ <Icon className="circled" name={clearSVG} size="24px" color="#e40166" />
35
+ </Button>
36
+ </Button.Group>
37
+ </div>
38
+ );
39
+
40
+ const messages = defineMessages({
41
+ addImage: {
42
+ id: 'Browse the site, drop an image, or type a URL',
43
+ defaultMessage: 'Browse the site, drop an image, or use a URL',
44
+ },
45
+ pickAnImage: {
46
+ id: 'pickAnImage',
47
+ defaultMessage: 'Pick an existing image',
48
+ },
49
+ uploadAnImage: {
50
+ id: 'uploadAnImage',
51
+ defaultMessage: 'Upload an image from your computer',
52
+ },
53
+ linkAnImage: {
54
+ id: 'linkAnImage',
55
+ defaultMessage: 'Enter a URL to an image',
56
+ },
57
+ uploadingImage: {
58
+ id: 'Uploading image',
59
+ defaultMessage: 'Uploading image',
60
+ },
61
+ });
62
+
63
+ const UnconnectedImageInput = (props) => {
64
+ const {
65
+ id,
66
+ onChange,
67
+ onFocus,
68
+ openObjectBrowser,
69
+ value,
70
+ imageSize = 'teaser',
71
+ selected = true,
72
+ hideLinkPicker = false,
73
+ hideObjectBrowserPicker = false,
74
+ restrictFileUpload = false,
75
+ objectBrowserPickerType = 'image',
76
+ description,
77
+ placeholderLinkInput = '',
78
+ onSelectItem,
79
+ } = props;
80
+
81
+ const intl = useIntl();
82
+ const linkEditor = useLinkEditor();
83
+ const location = useLocation();
84
+ const dispatch = useDispatch();
85
+ const contextUrl = location.pathname;
86
+
87
+ const [uploading, setUploading] = React.useState(false);
88
+ const [dragging, setDragging] = React.useState(false);
89
+
90
+ const imageUploadInputRef = useRef(null);
91
+
92
+ const requestId = `image-upload-${id}`;
93
+
94
+ const { loading, loaded } = props.request;
95
+ const { content } = props;
96
+ const imageId = content?.['@id'];
97
+ const image = content?.image;
98
+
99
+ useEffect(() => {
100
+ if (uploading && !loading && loaded) {
101
+ setUploading(false);
102
+ onChange(id, imageId, {
103
+ image_field: 'image',
104
+ image_scales: { image: [image] },
105
+ });
106
+ }
107
+ }, [loading, loaded, uploading, imageId, image, id, onChange]); // Explicitly list all dependencies
108
+
109
+ const handleUpload = React.useCallback(
110
+ (eventOrFile) => {
111
+ if (restrictFileUpload === true) return;
112
+ eventOrFile.target && eventOrFile.stopPropagation();
113
+
114
+ setUploading(true);
115
+ const file = eventOrFile.target
116
+ ? eventOrFile.target.files[0]
117
+ : eventOrFile[0];
118
+ if (!validateFileUploadSize(file, intl.formatMessage)) return;
119
+ readAsDataURL(file).then((fileData) => {
120
+ const fields = fileData.match(/^data:(.*);(.*),(.*)$/);
121
+ dispatch(
122
+ createContent(
123
+ getBaseUrl(contextUrl),
124
+ {
125
+ '@type': 'Image',
126
+ title: file.name,
127
+ image: {
128
+ data: fields[3],
129
+ encoding: fields[2],
130
+ 'content-type': fields[1],
131
+ filename: file.name,
132
+ },
133
+ },
134
+ props.block || requestId,
135
+ ),
136
+ );
137
+ });
138
+ },
139
+ [
140
+ restrictFileUpload,
141
+ intl.formatMessage,
142
+ dispatch,
143
+ props,
144
+ contextUrl,
145
+ requestId,
146
+ ],
147
+ );
148
+
149
+ const onDragEnter = React.useCallback(() => {
150
+ if (restrictFileUpload === false) setDragging(true);
151
+ }, [restrictFileUpload]);
152
+ const onDragLeave = React.useCallback(() => setDragging(false), []);
153
+
154
+ return value ? (
155
+ <div
156
+ className="image-upload-widget-image"
157
+ onClick={onFocus}
158
+ onKeyDown={onFocus}
159
+ role="toolbar"
160
+ >
161
+ {selected && <ImageToolbar {...props} />}
162
+ <img
163
+ className={props.className}
164
+ src={`${flattenToAppURL(value)}/@@images/image/${imageSize}`}
165
+ alt=""
166
+ />
167
+ </div>
168
+ ) : (
169
+ <div
170
+ className="image-upload-widget"
171
+ onClick={onFocus}
172
+ onKeyDown={onFocus}
173
+ role="toolbar"
174
+ >
175
+ <Dropzone
176
+ noClick
177
+ onDrop={handleUpload}
178
+ onDragEnter={onDragEnter}
179
+ onDragLeave={onDragLeave}
180
+ className="dropzone"
181
+ >
182
+ {({ getRootProps, getInputProps }) => (
183
+ <div {...getRootProps()}>
184
+ <Message>
185
+ {dragging && <Dimmer active></Dimmer>}
186
+ {uploading && (
187
+ <Dimmer active>
188
+ <Loader indeterminate>
189
+ {intl.formatMessage(messages.uploadingImage)}
190
+ </Loader>
191
+ </Dimmer>
192
+ )}
193
+ <img src={imageBlockSVG} alt="" className="placeholder" />
194
+ <p>{description || intl.formatMessage(messages.addImage)}</p>
195
+ <div className="toolbar-wrapper">
196
+ <div className="toolbar-inner" ref={linkEditor.anchorNode}>
197
+ {hideObjectBrowserPicker === false && (
198
+ <Button.Group>
199
+ <Button
200
+ aria-label={intl.formatMessage(messages.pickAnImage)}
201
+ icon
202
+ basic
203
+ onClick={(e) => {
204
+ onFocus && onFocus();
205
+ e.preventDefault();
206
+ openObjectBrowser({
207
+ mode: objectBrowserPickerType,
208
+ onSelectItem: onSelectItem
209
+ ? onSelectItem
210
+ : (url, { title, image_field, image_scales }) => {
211
+ onChange(props.id, flattenToAppURL(url), {
212
+ title,
213
+ image_field,
214
+ image_scales,
215
+ });
216
+ },
217
+ currentPath: contextUrl,
218
+ });
219
+ }}
220
+ >
221
+ <Icon name={navTreeSVG} size="24px" />
222
+ </Button>
223
+ </Button.Group>
224
+ )}
225
+ {restrictFileUpload === false && (
226
+ <Button.Group>
227
+ <Button
228
+ aria-label={intl.formatMessage(messages.uploadAnImage)}
229
+ icon
230
+ basic
231
+ compact
232
+ onClick={() => {
233
+ imageUploadInputRef.current.click();
234
+ }}
235
+ >
236
+ <Icon name={uploadSVG} size="24px" />
237
+ </Button>
238
+ <input
239
+ {...getInputProps({
240
+ type: 'file',
241
+ ref: imageUploadInputRef,
242
+ onChange: handleUpload,
243
+ style: { display: 'none' },
244
+ })}
245
+ />
246
+ </Button.Group>
247
+ )}
248
+
249
+ {hideLinkPicker === false && (
250
+ <Button.Group>
251
+ <Button
252
+ icon
253
+ basic
254
+ aria-label={intl.formatMessage(messages.linkAnImage)}
255
+ onClick={(e) => {
256
+ !props.selected && onFocus && onFocus();
257
+ linkEditor.show();
258
+ }}
259
+ >
260
+ <Icon name={linkSVG} circled size="24px" />
261
+ </Button>
262
+ </Button.Group>
263
+ )}
264
+ </div>
265
+ {linkEditor.anchorNode && (
266
+ <linkEditor.LinkEditor
267
+ value={value}
268
+ placeholder={
269
+ placeholderLinkInput ||
270
+ intl.formatMessage(messages.linkAnImage)
271
+ }
272
+ objectBrowserPickerType={objectBrowserPickerType}
273
+ onChange={(_, e) =>
274
+ onChange(
275
+ props.id,
276
+ isInternalURL(e) ? flattenToAppURL(e) : e,
277
+ {},
278
+ )
279
+ }
280
+ id={id}
281
+ />
282
+ )}
283
+ </div>
284
+ </Message>
285
+ </div>
286
+ )}
287
+ </Dropzone>
288
+ </div>
289
+ );
290
+ };
291
+
292
+ export const ImageInput = compose(
293
+ connect(
294
+ (state, ownProps) => {
295
+ const requestId = `image-upload-${ownProps.id}`;
296
+ return {
297
+ request: state.content.subrequests[ownProps.block || requestId] || {},
298
+ content: state.content.subrequests[ownProps.block || requestId]?.data,
299
+ };
300
+ },
301
+ { createContent },
302
+ ),
303
+ )(withObjectBrowser(UnconnectedImageInput));
304
+
305
+ const ImageUploadWidget = (props) => (
306
+ <FormFieldWrapper {...props} className="image-upload-widget">
307
+ <ImageInput {...props} />
308
+ </FormFieldWrapper>
309
+ );
310
+
311
+ export default ImageUploadWidget;
@@ -54,6 +54,7 @@ import TextViewWidget from '@plone/volto/components/theme/Widgets/TextWidget';
54
54
  import TitleViewWidget from '@plone/volto/components/theme/Widgets/TitleWidget';
55
55
  import TokenViewWidget from '@plone/volto/components/theme/Widgets/TokenWidget';
56
56
  import UrlViewWidget from '@plone/volto/components/theme/Widgets/UrlWidget';
57
+ import ImageWidget from '@plone/volto/components/manage/Widgets/ImageWidget';
57
58
 
58
59
  export const DatetimeWidget = loadable(() =>
59
60
  import('@plone/volto/components/manage/Widgets/DatetimeWidget'),
@@ -101,6 +102,7 @@ export const widgetMapping = {
101
102
  autocomplete: SelectAutoComplete,
102
103
  color_picker: ColorPickerWidget,
103
104
  select: SelectWidget,
105
+ image: ImageWidget,
104
106
  },
105
107
  vocabulary: {
106
108
  'plone.app.vocabularies.Catalog': ObjectBrowserWidget,
@@ -415,6 +415,48 @@ body.has-toolbar.has-sidebar-collapsed .ui.wrapper > .ui.inner.block.full {
415
415
  }
416
416
  }
417
417
 
418
+ .toolbar-buttons {
419
+ // used by the ImageUploadWidget
420
+ display: flex;
421
+ justify-content: center;
422
+ gap: 4px;
423
+ }
424
+
425
+ .image-upload-widget-toolbar {
426
+ position: absolute;
427
+ top: 1em;
428
+ right: 1em;
429
+
430
+ .icon.circled {
431
+ border: 1px solid #e4e8ec;
432
+ background: #f3f5f7;
433
+ box-shadow: inset 0 0 0 0.14285714rem transparent !important;
434
+ }
435
+ }
436
+
437
+ div.image-upload-widget-image {
438
+ position: relative;
439
+
440
+ img {
441
+ max-width: 100% !important;
442
+ }
443
+ }
444
+
445
+ .image-upload-widget {
446
+ text-align: center;
447
+
448
+ .toolbar-wrapper {
449
+ display: flex;
450
+ flex-direction: column;
451
+ justify-content: flex-end;
452
+ }
453
+ }
454
+
455
+ .ui.active.basic.icon.button {
456
+ border: 1px solid #2996da;
457
+ background: transparent !important;
458
+ }
459
+
418
460
  // Hero block
419
461
  .block.hero {
420
462
  clear: both;
@@ -153,3 +153,111 @@ body.babel-view .field.language-independent-field {
153
153
  .react-select__option {
154
154
  .word-break();
155
155
  }
156
+
157
+ .image-widget {
158
+ container-type: inline-size;
159
+
160
+ .no-image-wrapper {
161
+ display: flex;
162
+ width: ~'min(100%, 450px)';
163
+ flex-direction: column;
164
+ align-items: center;
165
+ }
166
+
167
+ .toolbar-inner {
168
+ display: flex;
169
+ width: ~'min(100%, 450px)';
170
+ height: 40px;
171
+ padding: 4px;
172
+ border-radius: 2px;
173
+ background-color: rgba(255, 255, 255, 0.975);
174
+ box-shadow:
175
+ 0 0 8px rgba(0, 0, 0, 0.1),
176
+ 0 2px 4px rgba(0, 0, 0, 0.05);
177
+
178
+ .ui.form & .ui.input input[type='text'] {
179
+ height: initial;
180
+ }
181
+
182
+ & > svg {
183
+ display: inline-block !important;
184
+ box-sizing: content-box;
185
+ padding: 4px;
186
+ margin-right: 4px;
187
+ color: @brown !important;
188
+ }
189
+
190
+ .ui.input {
191
+ width: 100%;
192
+ margin-left: 8px;
193
+ }
194
+
195
+ .ui.input > input {
196
+ padding: 0;
197
+ border: none;
198
+ }
199
+
200
+ .ui.icon.button {
201
+ padding: 4px;
202
+ border-radius: 1px;
203
+ margin-left: -3px;
204
+ color: @brown !important;
205
+
206
+ &:hover {
207
+ background-color: #edf1f2 !important;
208
+ }
209
+ }
210
+
211
+ .ui.buttons:first-child {
212
+ & .ui.icon.button {
213
+ margin-left: 0;
214
+ }
215
+ }
216
+ }
217
+
218
+ @blueArctic: #e2f1fd;
219
+ @greySnow: #f3f5f7;
220
+ @greySmoke: #e4e8ec;
221
+
222
+ .image-widget-preview-wrapper {
223
+ position: relative;
224
+ }
225
+
226
+ .ui.basic.button.remove-block-button {
227
+ position: absolute;
228
+ z-index: 2;
229
+ top: 6px;
230
+ right: 2px;
231
+ background-color: transparent;
232
+ box-shadow: none;
233
+
234
+ .icon {
235
+ height: 18px !important;
236
+ }
237
+
238
+ .circled {
239
+ padding: 8px;
240
+ border: 1px solid @greySmoke;
241
+ background: @greySnow;
242
+
243
+ &:hover {
244
+ background-color: @greySmoke;
245
+ }
246
+ }
247
+
248
+ &:hover {
249
+ background-color: transparent !important;
250
+ }
251
+ }
252
+
253
+ .image-widget-filepath-preview {
254
+ display: flex;
255
+ align-items: center;
256
+ padding: 5px;
257
+ word-break: break-all;
258
+
259
+ a {
260
+ margin-left: 5px;
261
+ }
262
+ }
263
+ }
@@ -0,0 +1,7 @@
1
+ export default useLinkEditor;
2
+ declare function useLinkEditor(): {
3
+ anchorNode: React.MutableRefObject<undefined>;
4
+ show: () => void;
5
+ LinkEditor: (props: any) => JSX.Element;
6
+ };
7
+ import React from 'react';
@@ -0,0 +1,116 @@
1
+ export function ImageToolbar({ className, data, id, onChange, selected }: {
2
+ className: any;
3
+ data: any;
4
+ id: any;
5
+ onChange: any;
6
+ selected: any;
7
+ }): JSX.Element;
8
+ export const ImageInput: import("react-redux").ConnectedComponent<{
9
+ new (): {
10
+ state: {
11
+ isObjectBrowserOpen: boolean;
12
+ };
13
+ openObjectBrowser: ({ mode, onSelectItem, dataName, overlay, propDataName, searchableTypes, selectableTypes, maximumSelectionSize, currentPath, }?: {
14
+ mode: string;
15
+ dataName: string;
16
+ onSelectItem: string;
17
+ overlay: string;
18
+ }) => void;
19
+ closeObjectBrowser: () => void;
20
+ render(): JSX.Element;
21
+ context: unknown;
22
+ setState<K extends string | number | symbol>(state: any, callback?: () => void): void;
23
+ forceUpdate(callback?: () => void): void;
24
+ readonly props: Readonly<any>;
25
+ refs: {
26
+ [key: string]: React.ReactInstance;
27
+ };
28
+ componentDidMount?(): void;
29
+ shouldComponentUpdate?(nextProps: Readonly<any>, nextState: Readonly<any>, nextContext: any): boolean;
30
+ componentWillUnmount?(): void;
31
+ componentDidCatch?(error: Error, errorInfo: React.ErrorInfo): void;
32
+ getSnapshotBeforeUpdate?(prevProps: Readonly<any>, prevState: Readonly<any>): any;
33
+ componentDidUpdate?(prevProps: Readonly<any>, prevState: Readonly<any>, snapshot?: any): void;
34
+ componentWillMount?(): void;
35
+ UNSAFE_componentWillMount?(): void;
36
+ componentWillReceiveProps?(nextProps: Readonly<any>, nextContext: any): void;
37
+ UNSAFE_componentWillReceiveProps?(nextProps: Readonly<any>, nextContext: any): void;
38
+ componentWillUpdate?(nextProps: Readonly<any>, nextState: Readonly<any>, nextContext: any): void;
39
+ UNSAFE_componentWillUpdate?(nextProps: Readonly<any>, nextState: Readonly<any>, nextContext: any): void;
40
+ };
41
+ defaultProps: {
42
+ onChangeBlock: () => void;
43
+ data: {};
44
+ block: string;
45
+ };
46
+ contextType?: React.Context<any>;
47
+ }, import("react-redux").Omit<Pick<React.ClassAttributes<{
48
+ state: {
49
+ isObjectBrowserOpen: boolean;
50
+ };
51
+ openObjectBrowser: ({ mode, onSelectItem, dataName, overlay, propDataName, searchableTypes, selectableTypes, maximumSelectionSize, currentPath, }?: {
52
+ mode: string;
53
+ dataName: string;
54
+ onSelectItem: string;
55
+ overlay: string;
56
+ }) => void;
57
+ closeObjectBrowser: () => void;
58
+ render(): JSX.Element;
59
+ context: unknown;
60
+ setState<K extends string | number | symbol>(state: any, callback?: () => void): void;
61
+ forceUpdate(callback?: () => void): void;
62
+ readonly props: Readonly<any>;
63
+ refs: {
64
+ [key: string]: React.ReactInstance;
65
+ };
66
+ componentDidMount?(): void;
67
+ shouldComponentUpdate?(nextProps: Readonly<any>, nextState: Readonly<any>, nextContext: any): boolean;
68
+ componentWillUnmount?(): void;
69
+ componentDidCatch?(error: Error, errorInfo: React.ErrorInfo): void;
70
+ getSnapshotBeforeUpdate?(prevProps: Readonly<any>, prevState: Readonly<any>): any;
71
+ componentDidUpdate?(prevProps: Readonly<any>, prevState: Readonly<any>, snapshot?: any): void;
72
+ componentWillMount?(): void;
73
+ UNSAFE_componentWillMount?(): void;
74
+ componentWillReceiveProps?(nextProps: Readonly<any>, nextContext: any): void;
75
+ UNSAFE_componentWillReceiveProps?(nextProps: Readonly<any>, nextContext: any): void;
76
+ componentWillUpdate?(nextProps: Readonly<any>, nextState: Readonly<any>, nextContext: any): void;
77
+ UNSAFE_componentWillUpdate?(nextProps: Readonly<any>, nextState: Readonly<any>, nextContext: any): void;
78
+ }>, keyof React.ClassAttributes<{
79
+ state: {
80
+ isObjectBrowserOpen: boolean;
81
+ };
82
+ openObjectBrowser: ({ mode, onSelectItem, dataName, overlay, propDataName, searchableTypes, selectableTypes, maximumSelectionSize, currentPath, }?: {
83
+ mode: string;
84
+ dataName: string;
85
+ onSelectItem: string;
86
+ overlay: string;
87
+ }) => void;
88
+ closeObjectBrowser: () => void;
89
+ render(): JSX.Element;
90
+ context: unknown;
91
+ setState<K extends string | number | symbol>(state: any, callback?: () => void): void;
92
+ forceUpdate(callback?: () => void): void;
93
+ readonly props: Readonly<any>;
94
+ refs: {
95
+ [key: string]: React.ReactInstance;
96
+ };
97
+ componentDidMount?(): void;
98
+ shouldComponentUpdate?(nextProps: Readonly<any>, nextState: Readonly<any>, nextContext: any): boolean;
99
+ componentWillUnmount?(): void;
100
+ componentDidCatch?(error: Error, errorInfo: React.ErrorInfo): void;
101
+ getSnapshotBeforeUpdate?(prevProps: Readonly<any>, prevState: Readonly<any>): any;
102
+ componentDidUpdate?(prevProps: Readonly<any>, prevState: Readonly<any>, snapshot?: any): void;
103
+ componentWillMount?(): void;
104
+ UNSAFE_componentWillMount?(): void;
105
+ componentWillReceiveProps?(nextProps: Readonly<any>, nextContext: any): void;
106
+ UNSAFE_componentWillReceiveProps?(nextProps: Readonly<any>, nextContext: any): void;
107
+ componentWillUpdate?(nextProps: Readonly<any>, nextState: Readonly<any>, nextContext: any): void;
108
+ UNSAFE_componentWillUpdate?(nextProps: Readonly<any>, nextState: Readonly<any>, nextContext: any): void;
109
+ }>> & {} & {
110
+ data?: {};
111
+ block?: string;
112
+ onChangeBlock?: () => void;
113
+ }, never>>;
114
+ export default ImageUploadWidget;
115
+ import React from 'react';
116
+ declare function ImageUploadWidget(props: any): JSX.Element;
@@ -36,6 +36,7 @@ export namespace widgetMapping {
36
36
  export { SelectAutoComplete as autocomplete };
37
37
  export { ColorPickerWidget as color_picker };
38
38
  export { SelectWidget as select };
39
+ export { ImageWidget as image };
39
40
  }
40
41
  export let vocabulary: {
41
42
  'plone.app.vocabularies.Catalog': import("react").ForwardRefExoticComponent<Pick<import("react-intl").WithIntlProps<import("react-intl").WrappedComponentProps<string>>, string> & import("react").RefAttributes<import("react").ComponentType<import("react-intl").WrappedComponentProps<string>>>> & {
@@ -132,6 +133,7 @@ import SelectMetadataWidget from '@plone/volto/components/manage/Blocks/Search/w
132
133
  import SelectAutoComplete from '@plone/volto/components/manage/Widgets/SelectAutoComplete';
133
134
  import ColorPickerWidget from '@plone/volto/components/manage/Widgets/ColorPickerWidget';
134
135
  import SelectWidget from '@plone/volto/components/manage/Widgets/SelectWidget';
136
+ import ImageWidget from '@plone/volto/components/manage/Widgets/ImageWidget';
135
137
  import CheckboxWidget from '@plone/volto/components/manage/Widgets/CheckboxWidget';
136
138
  import NumberWidget from '@plone/volto/components/manage/Widgets/NumberWidget';
137
139
  import { getWidgetView } from '@plone/volto/helpers/Widget/widget';