@plone/volto 17.16.2 → 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.
- package/CHANGELOG.md +16 -0
- package/locales/ca/LC_MESSAGES/volto.po +33 -0
- package/locales/ca.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +33 -0
- package/locales/de.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +33 -0
- package/locales/en.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +33 -0
- package/locales/es.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +33 -0
- package/locales/eu.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +33 -0
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +33 -0
- package/locales/fr.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +33 -0
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +33 -0
- package/locales/ja.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +33 -0
- package/locales/nl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +33 -0
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +33 -0
- package/locales/pt_BR.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +33 -0
- package/locales/ro.json +1 -1
- package/locales/volto.pot +34 -1
- package/locales/zh_CN/LC_MESSAGES/volto.po +33 -0
- package/locales/zh_CN.json +1 -1
- package/package.json +1 -1
- package/packages/volto-slate/package.json +1 -1
- package/src/components/manage/AnchorPlugin/useLinkEditor.jsx +79 -0
- package/src/components/manage/Controlpanels/Groups/GroupsControlpanel.jsx +34 -4
- package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +30 -4
- package/src/components/manage/Form/Form.jsx +8 -1
- package/src/components/manage/Widgets/ImageWidget.jsx +311 -0
- package/src/config/Widgets.jsx +2 -0
- package/src/helpers/MessageLabels/MessageLabels.js +8 -0
- package/theme/themes/pastanaga/extras/blocks.less +42 -0
- package/theme/themes/pastanaga/extras/widgets.less +108 -0
- package/types/components/manage/AnchorPlugin/useLinkEditor.d.ts +7 -0
- package/types/components/manage/Widgets/ImageWidget.d.ts +116 -0
- package/types/config/Widgets.d.ts +2 -0
- package/types/helpers/MessageLabels/MessageLabels.d.ts +75 -63
|
@@ -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;
|
package/src/config/Widgets.jsx
CHANGED
|
@@ -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,
|
|
@@ -110,6 +110,10 @@ export const messages = defineMessages({
|
|
|
110
110
|
id: 'Add group',
|
|
111
111
|
defaultMessage: 'Add group',
|
|
112
112
|
},
|
|
113
|
+
groupDeleted: {
|
|
114
|
+
id: 'Group deleted',
|
|
115
|
+
defaultMessage: 'Group deleted',
|
|
116
|
+
},
|
|
113
117
|
addUserFormUsernameTitle: {
|
|
114
118
|
id: 'Username',
|
|
115
119
|
defaultMessage: 'Username',
|
|
@@ -213,6 +217,10 @@ export const messages = defineMessages({
|
|
|
213
217
|
id: 'User created',
|
|
214
218
|
defaultMessage: 'User created',
|
|
215
219
|
},
|
|
220
|
+
userDeleted: {
|
|
221
|
+
id: 'User deleted',
|
|
222
|
+
defaultMessage: 'User deleted',
|
|
223
|
+
},
|
|
216
224
|
groupCreated: {
|
|
217
225
|
id: 'Group created',
|
|
218
226
|
defaultMessage: 'Group created',
|
|
@@ -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,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';
|