@eeacms/volto-cca-policy 0.3.104 → 0.3.106
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 +24 -0
- package/package.json +1 -1
- package/src/customizations/@eeacms/volto-listing-block/README.md +1 -0
- package/src/customizations/@eeacms/volto-listing-block/blocks/Listing/item-templates/SimpleItemTemplates.jsx +62 -0
- package/src/customizations/@eeacms/volto-listing-block/components/UniversalCard/fragments/CardTitle.jsx +28 -0
- package/src/customizations/volto/components/manage/Widgets/ObjectBrowserWidget.jsx +453 -0
- package/src/customizations/volto/components/manage/Widgets/README.md +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,30 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
6
6
|
|
|
7
|
+
### [0.3.106](https://github.com/eea/volto-cca-policy/compare/0.3.105...0.3.106) - 20 March 2026
|
|
8
|
+
|
|
9
|
+
#### :rocket: New Features
|
|
10
|
+
|
|
11
|
+
- feat(teaser): preserve query strings in CardTitle.jsx [kreafox - [`a0b70ea`](https://github.com/eea/volto-cca-policy/commit/a0b70ea9055a8b1d3a6d7c93aedad6e5b36bf3a1)]
|
|
12
|
+
|
|
13
|
+
#### :hammer_and_wrench: Others
|
|
14
|
+
|
|
15
|
+
- core: add CardTitle.jsx for customization [kreafox - [`c6b34db`](https://github.com/eea/volto-cca-policy/commit/c6b34db474de98f5a04e4d69a5b754c508c85b2d)]
|
|
16
|
+
### [0.3.105](https://github.com/eea/volto-cca-policy/compare/0.3.104...0.3.105) - 19 March 2026
|
|
17
|
+
|
|
18
|
+
#### :rocket: New Features
|
|
19
|
+
|
|
20
|
+
- feat(teaser): preserve query strings in SimpleItemTemplates.jsx [kreafox - [`dec6b82`](https://github.com/eea/volto-cca-policy/commit/dec6b82b60e50e881a352e910227ef32ef2812a2)]
|
|
21
|
+
- feat(teaser): modified ObjectBrowserWidget to show links with query strings for manual links [kreafox - [`a1141ee`](https://github.com/eea/volto-cca-policy/commit/a1141eec28f6731b715f3cece352df11e3cf3ee6)]
|
|
22
|
+
|
|
23
|
+
#### :house: Internal changes
|
|
24
|
+
|
|
25
|
+
- style: Automated code fix [eea-jenkins - [`c93a86b`](https://github.com/eea/volto-cca-policy/commit/c93a86be5bb37bc5b736d574baac0b6bc489fb2c)]
|
|
26
|
+
|
|
27
|
+
#### :hammer_and_wrench: Others
|
|
28
|
+
|
|
29
|
+
- core: added original SimpleItemTemplates.jsx for customization [kreafox - [`d707919`](https://github.com/eea/volto-cca-policy/commit/d7079194a82a4a91b9d91345c765156360706608)]
|
|
30
|
+
- core: added ObjectBrowserWidget.jsx customization [kreafox - [`50cd014`](https://github.com/eea/volto-cca-policy/commit/50cd014f4ca7bfb070b5760cbf7186388bf9ab6d)]
|
|
7
31
|
### [0.3.104](https://github.com/eea/volto-cca-policy/compare/0.3.103...0.3.104) - 5 March 2026
|
|
8
32
|
|
|
9
33
|
#### :rocket: Dependency updates
|
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Customized SimpleItemTemplates.jsx and CardTitle.jsx to preserve query strings. (refs #299560)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import cx from 'classnames';
|
|
2
|
+
import { ConditionalLink } from '@plone/volto/components';
|
|
3
|
+
import { getBaseUrl } from '@plone/volto/helpers';
|
|
4
|
+
import { getVoltoStyles } from '@eeacms/volto-listing-block/schema-utils';
|
|
5
|
+
|
|
6
|
+
const getStyles = (props) => {
|
|
7
|
+
const { itemModel = {} } = props;
|
|
8
|
+
const res = {};
|
|
9
|
+
if (itemModel.maxDescription) {
|
|
10
|
+
res[`max-${itemModel.maxDescription}-lines`] = true;
|
|
11
|
+
}
|
|
12
|
+
if (itemModel.maxTitle) {
|
|
13
|
+
res[`title-max-${itemModel.maxTitle}-lines`] = true;
|
|
14
|
+
}
|
|
15
|
+
return res;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const getItemHref = (item) => {
|
|
19
|
+
const preservedHref = item?.linkHref || item?.linkWithHash;
|
|
20
|
+
if (preservedHref) {
|
|
21
|
+
return preservedHref;
|
|
22
|
+
}
|
|
23
|
+
return item?.['@id'] ? getBaseUrl(item['@id']) : undefined;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const BasicItem = (props) => {
|
|
27
|
+
const { item, className, isEditMode = false } = props;
|
|
28
|
+
const { hasMetaType } = props.itemModel;
|
|
29
|
+
const styles = getStyles(props);
|
|
30
|
+
const href = getItemHref(item);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div
|
|
34
|
+
className={cx(
|
|
35
|
+
'u-item listing-item simple-listing-item',
|
|
36
|
+
getVoltoStyles(styles),
|
|
37
|
+
className,
|
|
38
|
+
)}
|
|
39
|
+
>
|
|
40
|
+
<div className="wrapper">
|
|
41
|
+
<div className="slot-top">
|
|
42
|
+
<ConditionalLink to={href} condition={!isEditMode && !!href}>
|
|
43
|
+
<div className="listing-body">
|
|
44
|
+
<p className="listing-header">
|
|
45
|
+
{item.title ? item.title : item.id}
|
|
46
|
+
</p>
|
|
47
|
+
</div>
|
|
48
|
+
</ConditionalLink>
|
|
49
|
+
</div>
|
|
50
|
+
<div className="simple-item-meta">
|
|
51
|
+
{hasMetaType && (
|
|
52
|
+
<span className="text-left">{item['type_title']}</span>
|
|
53
|
+
)}
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const SimpleItemLayout = (props) => {
|
|
61
|
+
return <BasicItem {...props} />;
|
|
62
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Card as UiCard } from 'semantic-ui-react';
|
|
3
|
+
import { ConditionalLink } from '@plone/volto/components';
|
|
4
|
+
|
|
5
|
+
const getItemHref = (item) =>
|
|
6
|
+
item?.linkHref || item?.linkWithHash || item?.['@id'];
|
|
7
|
+
|
|
8
|
+
const CardTitle = (props) => {
|
|
9
|
+
const { item, isEditMode, itemModel } = props;
|
|
10
|
+
const { title, Title } = item;
|
|
11
|
+
const t = title || Title;
|
|
12
|
+
|
|
13
|
+
const href = getItemHref(item);
|
|
14
|
+
|
|
15
|
+
return t && !itemModel?.titleOnImage ? (
|
|
16
|
+
<UiCard.Header>
|
|
17
|
+
<ConditionalLink
|
|
18
|
+
className="header-link"
|
|
19
|
+
to={href}
|
|
20
|
+
condition={!isEditMode && itemModel?.hasLink && !!href}
|
|
21
|
+
>
|
|
22
|
+
{t}
|
|
23
|
+
</ConditionalLink>
|
|
24
|
+
</UiCard.Header>
|
|
25
|
+
) : null;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default CardTitle;
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectBrowserWidget component.
|
|
3
|
+
* @module components/manage/Widgets/ObjectBrowserWidget
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { Component } from 'react';
|
|
7
|
+
import PropTypes from 'prop-types';
|
|
8
|
+
import { compose } from 'redux';
|
|
9
|
+
import { compact, isArray, isEmpty, remove } from 'lodash';
|
|
10
|
+
import { connect } from 'react-redux';
|
|
11
|
+
import { Label, Popup, Button } from 'semantic-ui-react';
|
|
12
|
+
import {
|
|
13
|
+
flattenToAppURL,
|
|
14
|
+
isInternalURL,
|
|
15
|
+
isUrl,
|
|
16
|
+
normalizeUrl,
|
|
17
|
+
removeProtocol,
|
|
18
|
+
} from '@plone/volto/helpers/Url/Url';
|
|
19
|
+
import { searchContent } from '@plone/volto/actions/search/search';
|
|
20
|
+
import withObjectBrowser from '@plone/volto/components/manage/Sidebar/ObjectBrowser';
|
|
21
|
+
import { defineMessages, injectIntl } from 'react-intl';
|
|
22
|
+
import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
23
|
+
import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper';
|
|
24
|
+
|
|
25
|
+
import navTreeSVG from '@plone/volto/icons/nav.svg';
|
|
26
|
+
import clearSVG from '@plone/volto/icons/clear.svg';
|
|
27
|
+
import homeSVG from '@plone/volto/icons/home.svg';
|
|
28
|
+
import aheadSVG from '@plone/volto/icons/ahead.svg';
|
|
29
|
+
import blankSVG from '@plone/volto/icons/blank.svg';
|
|
30
|
+
import { withRouter } from 'react-router';
|
|
31
|
+
|
|
32
|
+
const messages = defineMessages({
|
|
33
|
+
placeholder: {
|
|
34
|
+
id: 'No items selected',
|
|
35
|
+
defaultMessage: 'No items selected',
|
|
36
|
+
},
|
|
37
|
+
edit: {
|
|
38
|
+
id: 'Edit',
|
|
39
|
+
defaultMessage: 'Edit',
|
|
40
|
+
},
|
|
41
|
+
delete: {
|
|
42
|
+
id: 'Delete',
|
|
43
|
+
defaultMessage: 'Delete',
|
|
44
|
+
},
|
|
45
|
+
openObjectBrowser: {
|
|
46
|
+
id: 'Open object browser',
|
|
47
|
+
defaultMessage: 'Open object browser',
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* ObjectBrowserWidget component class.
|
|
53
|
+
* @class ObjectBrowserWidget
|
|
54
|
+
* @extends Component
|
|
55
|
+
*/
|
|
56
|
+
export class ObjectBrowserWidgetComponent extends Component {
|
|
57
|
+
/**
|
|
58
|
+
* Property types.
|
|
59
|
+
* @property {Object} propTypes Property types.
|
|
60
|
+
* @static
|
|
61
|
+
*/
|
|
62
|
+
static propTypes = {
|
|
63
|
+
id: PropTypes.string.isRequired,
|
|
64
|
+
title: PropTypes.string.isRequired,
|
|
65
|
+
description: PropTypes.string,
|
|
66
|
+
mode: PropTypes.string, // link, image, multiple
|
|
67
|
+
return: PropTypes.string, // single, multiple
|
|
68
|
+
initialPath: PropTypes.string,
|
|
69
|
+
required: PropTypes.bool,
|
|
70
|
+
error: PropTypes.arrayOf(PropTypes.string),
|
|
71
|
+
value: PropTypes.oneOfType([
|
|
72
|
+
PropTypes.arrayOf(PropTypes.object),
|
|
73
|
+
PropTypes.object,
|
|
74
|
+
]),
|
|
75
|
+
onChange: PropTypes.func.isRequired,
|
|
76
|
+
openObjectBrowser: PropTypes.func.isRequired,
|
|
77
|
+
allowExternals: PropTypes.bool,
|
|
78
|
+
placeholder: PropTypes.string,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Default properties
|
|
83
|
+
* @property {Object} defaultProps Default properties.
|
|
84
|
+
* @static
|
|
85
|
+
*/
|
|
86
|
+
static defaultProps = {
|
|
87
|
+
description: null,
|
|
88
|
+
required: false,
|
|
89
|
+
error: [],
|
|
90
|
+
value: [],
|
|
91
|
+
mode: 'multiple',
|
|
92
|
+
return: 'multiple',
|
|
93
|
+
initialPath: '',
|
|
94
|
+
allowExternals: false,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
state = {
|
|
98
|
+
manualLinkInput: '',
|
|
99
|
+
validURL: false,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
constructor(props) {
|
|
103
|
+
super(props);
|
|
104
|
+
this.selectedItemsRef = React.createRef();
|
|
105
|
+
this.placeholderRef = React.createRef();
|
|
106
|
+
}
|
|
107
|
+
renderLabel(item) {
|
|
108
|
+
// Prefer fully preserved href if present
|
|
109
|
+
const href = item.linkHref || item.linkWithHash || item['@id'];
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<Popup
|
|
113
|
+
key={href}
|
|
114
|
+
content={
|
|
115
|
+
<div style={{ display: 'flex' }}>
|
|
116
|
+
{isInternalURL(href) ? (
|
|
117
|
+
<Icon name={homeSVG} size="18px" />
|
|
118
|
+
) : (
|
|
119
|
+
<Icon name={blankSVG} size="18px" />
|
|
120
|
+
)}
|
|
121
|
+
|
|
122
|
+
{href}
|
|
123
|
+
</div>
|
|
124
|
+
}
|
|
125
|
+
trigger={
|
|
126
|
+
<Label>
|
|
127
|
+
<div className="item-title">{item.title}</div>
|
|
128
|
+
<div>
|
|
129
|
+
{this.props.mode === 'multiple' && (
|
|
130
|
+
<Icon
|
|
131
|
+
name={clearSVG}
|
|
132
|
+
size="12px"
|
|
133
|
+
className="right"
|
|
134
|
+
onClick={(event) => {
|
|
135
|
+
event.preventDefault();
|
|
136
|
+
this.removeItem(item);
|
|
137
|
+
}}
|
|
138
|
+
/>
|
|
139
|
+
)}
|
|
140
|
+
</div>
|
|
141
|
+
</Label>
|
|
142
|
+
}
|
|
143
|
+
/>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
removeItem = (item) => {
|
|
148
|
+
let value = [...this.props.value];
|
|
149
|
+
remove(value, function (_item) {
|
|
150
|
+
return _item['@id'] === item['@id'];
|
|
151
|
+
});
|
|
152
|
+
this.props.onChange(this.props.id, value);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
onChange = (item) => {
|
|
156
|
+
let value =
|
|
157
|
+
this.props.mode === 'multiple' && this.props.value
|
|
158
|
+
? [...this.props.value]
|
|
159
|
+
: [];
|
|
160
|
+
value = value.filter((item) => item != null);
|
|
161
|
+
const maxSize =
|
|
162
|
+
this.props.widgetOptions?.pattern_options?.maximumSelectionSize || -1;
|
|
163
|
+
if (maxSize === 1 && value.length === 1) {
|
|
164
|
+
value = []; //enable replace of selected item with another value, if maxsize is 1
|
|
165
|
+
}
|
|
166
|
+
let exists = false;
|
|
167
|
+
let index = -1;
|
|
168
|
+
|
|
169
|
+
value.forEach((_item, _index) => {
|
|
170
|
+
if (flattenToAppURL(_item['@id']) === flattenToAppURL(item['@id'])) {
|
|
171
|
+
exists = true;
|
|
172
|
+
index = _index;
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
//find(value, {
|
|
176
|
+
// '@id': flattenToAppURL(item['@id']),
|
|
177
|
+
// });
|
|
178
|
+
if (!exists) {
|
|
179
|
+
// add item
|
|
180
|
+
// Check if we want to filter the attributes of the selected item
|
|
181
|
+
let resultantItem = item;
|
|
182
|
+
if (this.props.selectedItemAttrs) {
|
|
183
|
+
const allowedItemKeys = [
|
|
184
|
+
...this.props.selectedItemAttrs,
|
|
185
|
+
// Add the required attributes for the widget to work
|
|
186
|
+
'@id',
|
|
187
|
+
'linkHref', // add linkHref to the allowed attributes
|
|
188
|
+
'linkWithHash', // add linkWithHash to the allowed attributes
|
|
189
|
+
'title',
|
|
190
|
+
];
|
|
191
|
+
resultantItem = Object.keys(item)
|
|
192
|
+
.filter((key) => allowedItemKeys.includes(key))
|
|
193
|
+
.reduce((obj, key) => {
|
|
194
|
+
obj[key] = item[key];
|
|
195
|
+
return obj;
|
|
196
|
+
}, {});
|
|
197
|
+
}
|
|
198
|
+
// Add required @id field, just in case
|
|
199
|
+
resultantItem = { ...resultantItem, '@id': item['@id'] };
|
|
200
|
+
value.push(resultantItem);
|
|
201
|
+
if (this.props.return === 'single') {
|
|
202
|
+
this.props.onChange(this.props.id, value[0]);
|
|
203
|
+
} else {
|
|
204
|
+
this.props.onChange(this.props.id, value);
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
//remove item
|
|
208
|
+
value.splice(index, 1);
|
|
209
|
+
this.props.onChange(this.props.id, value);
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
onManualLinkInput = (e) => {
|
|
214
|
+
this.setState({ manualLinkInput: e.target.value });
|
|
215
|
+
this.setState({
|
|
216
|
+
validURL: this.validateManualLink(e.target.value),
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
validateManualLink = (url) => {
|
|
221
|
+
if (this.props.allowExternals) {
|
|
222
|
+
return isUrl(url);
|
|
223
|
+
} else {
|
|
224
|
+
return isInternalURL(url);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
hasQueryOrHash = (url) => {
|
|
229
|
+
return url.includes('?') || url.includes('#');
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Returns the clean path used to resolve a Plone object,
|
|
234
|
+
* stripping query string and hash fragment.
|
|
235
|
+
*/
|
|
236
|
+
getLookupPathFromUrl = (url) => {
|
|
237
|
+
return url.split('?')[0].split('#')[0];
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
onSubmitManualLink = () => {
|
|
241
|
+
if (!this.validateManualLink(this.state.manualLinkInput)) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (isInternalURL(this.state.manualLinkInput)) {
|
|
246
|
+
const originalUrl = this.state.manualLinkInput;
|
|
247
|
+
const lookupPath = this.getLookupPathFromUrl(originalUrl);
|
|
248
|
+
const relativeLink = flattenToAppURL(lookupPath);
|
|
249
|
+
const shouldPreserveOriginal = this.hasQueryOrHash(originalUrl);
|
|
250
|
+
|
|
251
|
+
this.props
|
|
252
|
+
.searchContent(
|
|
253
|
+
'/',
|
|
254
|
+
{
|
|
255
|
+
'path.query': relativeLink,
|
|
256
|
+
'path.depth': '0',
|
|
257
|
+
sort_on: 'getObjPositionInParent',
|
|
258
|
+
metadata_fields: '_all',
|
|
259
|
+
b_size: 1000,
|
|
260
|
+
},
|
|
261
|
+
`${this.props.block}-${this.props.mode}`,
|
|
262
|
+
)
|
|
263
|
+
.then((resp) => {
|
|
264
|
+
if (resp.items?.length > 0) {
|
|
265
|
+
const resolvedItem = {
|
|
266
|
+
...resp.items[0],
|
|
267
|
+
...(shouldPreserveOriginal ? { linkHref: originalUrl } : {}),
|
|
268
|
+
};
|
|
269
|
+
this.onChange(resolvedItem);
|
|
270
|
+
} else {
|
|
271
|
+
this.props.onChange(this.props.id, [
|
|
272
|
+
{
|
|
273
|
+
'@id': relativeLink,
|
|
274
|
+
...(shouldPreserveOriginal ? { linkHref: originalUrl } : {}),
|
|
275
|
+
title: removeProtocol(originalUrl),
|
|
276
|
+
},
|
|
277
|
+
]);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
} else {
|
|
281
|
+
this.props.onChange(this.props.id, [
|
|
282
|
+
{
|
|
283
|
+
'@id': normalizeUrl(this.state.manualLinkInput),
|
|
284
|
+
title: removeProtocol(this.state.manualLinkInput),
|
|
285
|
+
},
|
|
286
|
+
]);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
this.setState({ validURL: true, manualLinkInput: '' });
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
onKeyDownManualLink = (e) => {
|
|
293
|
+
if (e.key === 'Enter') {
|
|
294
|
+
e.preventDefault();
|
|
295
|
+
e.stopPropagation();
|
|
296
|
+
this.onSubmitManualLink();
|
|
297
|
+
} else if (e.key === 'Escape') {
|
|
298
|
+
e.preventDefault();
|
|
299
|
+
e.stopPropagation();
|
|
300
|
+
// TODO: Do something on ESC key
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
showObjectBrowser = (ev) => {
|
|
305
|
+
ev.preventDefault();
|
|
306
|
+
this.props.openObjectBrowser({
|
|
307
|
+
mode: this.props.mode,
|
|
308
|
+
currentPath: this.props.initialPath || this.props.location.pathname,
|
|
309
|
+
propDataName: 'value',
|
|
310
|
+
onSelectItem: (url, item) => {
|
|
311
|
+
this.onChange(item);
|
|
312
|
+
},
|
|
313
|
+
selectableTypes:
|
|
314
|
+
this.props.widgetOptions?.pattern_options?.selectableTypes ||
|
|
315
|
+
this.props.selectableTypes,
|
|
316
|
+
maximumSelectionSize:
|
|
317
|
+
this.props.widgetOptions?.pattern_options?.maximumSelectionSize ||
|
|
318
|
+
this.props.maximumSelectionSize,
|
|
319
|
+
});
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
handleSelectedItemsRefClick = (e) => {
|
|
323
|
+
if (this.props.isDisabled) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (
|
|
328
|
+
e.target.contains(this.selectedItemsRef.current) ||
|
|
329
|
+
e.target.contains(this.placeholderRef.current)
|
|
330
|
+
) {
|
|
331
|
+
this.showObjectBrowser(e);
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Render method.
|
|
337
|
+
* @method render
|
|
338
|
+
* @returns {string} Markup for the component.
|
|
339
|
+
*/
|
|
340
|
+
render() {
|
|
341
|
+
const { id, description, fieldSet, value, mode, onChange, isDisabled } =
|
|
342
|
+
this.props;
|
|
343
|
+
|
|
344
|
+
let items = compact(!isArray(value) && value ? [value] : value || []);
|
|
345
|
+
|
|
346
|
+
let icon =
|
|
347
|
+
mode === 'multiple' || items.length === 0 ? navTreeSVG : clearSVG;
|
|
348
|
+
let iconAction =
|
|
349
|
+
mode === 'multiple' || items.length === 0
|
|
350
|
+
? this.showObjectBrowser
|
|
351
|
+
: (e) => {
|
|
352
|
+
e.preventDefault();
|
|
353
|
+
onChange(id, this.props.return === 'single' ? null : []);
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
return (
|
|
357
|
+
<FormFieldWrapper
|
|
358
|
+
{...this.props}
|
|
359
|
+
className={description ? 'help text' : 'text'}
|
|
360
|
+
>
|
|
361
|
+
<div
|
|
362
|
+
className="objectbrowser-field"
|
|
363
|
+
aria-labelledby={`fieldset-${
|
|
364
|
+
fieldSet || 'default'
|
|
365
|
+
}-field-label-${id}`}
|
|
366
|
+
>
|
|
367
|
+
<div
|
|
368
|
+
className="selected-values"
|
|
369
|
+
onClick={this.handleSelectedItemsRefClick}
|
|
370
|
+
onKeyDown={this.handleSelectedItemsRefClick}
|
|
371
|
+
role="searchbox"
|
|
372
|
+
tabIndex={0}
|
|
373
|
+
ref={this.selectedItemsRef}
|
|
374
|
+
>
|
|
375
|
+
{items.map((item) => this.renderLabel(item))}
|
|
376
|
+
|
|
377
|
+
{items.length === 0 && this.props.mode === 'multiple' && (
|
|
378
|
+
<div className="placeholder" ref={this.placeholderRef}>
|
|
379
|
+
{this.props.placeholder ??
|
|
380
|
+
this.props.intl.formatMessage(messages.placeholder)}
|
|
381
|
+
</div>
|
|
382
|
+
)}
|
|
383
|
+
{this.props.allowExternals &&
|
|
384
|
+
items.length === 0 &&
|
|
385
|
+
this.props.mode !== 'multiple' && (
|
|
386
|
+
<input
|
|
387
|
+
onKeyDown={this.onKeyDownManualLink}
|
|
388
|
+
onChange={this.onManualLinkInput}
|
|
389
|
+
value={this.state.manualLinkInput}
|
|
390
|
+
placeholder={
|
|
391
|
+
this.props.placeholder ??
|
|
392
|
+
this.props.intl.formatMessage(messages.placeholder)
|
|
393
|
+
}
|
|
394
|
+
/>
|
|
395
|
+
)}
|
|
396
|
+
</div>
|
|
397
|
+
{this.state.manualLinkInput && isEmpty(items) && (
|
|
398
|
+
<Button.Group>
|
|
399
|
+
<Button
|
|
400
|
+
basic
|
|
401
|
+
className="cancel"
|
|
402
|
+
onClick={(e) => {
|
|
403
|
+
e.stopPropagation();
|
|
404
|
+
this.setState({ manualLinkInput: '' });
|
|
405
|
+
}}
|
|
406
|
+
>
|
|
407
|
+
<Icon name={clearSVG} size="18px" color="#e40166" />
|
|
408
|
+
</Button>
|
|
409
|
+
<Button
|
|
410
|
+
basic
|
|
411
|
+
primary
|
|
412
|
+
disabled={!this.state.validURL}
|
|
413
|
+
onClick={(e) => {
|
|
414
|
+
e.stopPropagation();
|
|
415
|
+
this.onSubmitManualLink();
|
|
416
|
+
}}
|
|
417
|
+
>
|
|
418
|
+
<Icon name={aheadSVG} size="18px" />
|
|
419
|
+
</Button>
|
|
420
|
+
</Button.Group>
|
|
421
|
+
)}
|
|
422
|
+
{!this.state.manualLinkInput && (
|
|
423
|
+
<Button
|
|
424
|
+
aria-label={this.props.intl.formatMessage(
|
|
425
|
+
messages.openObjectBrowser,
|
|
426
|
+
)}
|
|
427
|
+
onClick={iconAction}
|
|
428
|
+
className="action"
|
|
429
|
+
disabled={isDisabled}
|
|
430
|
+
>
|
|
431
|
+
<Icon name={icon} size="18px" />
|
|
432
|
+
</Button>
|
|
433
|
+
)}
|
|
434
|
+
</div>
|
|
435
|
+
</FormFieldWrapper>
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const ObjectBrowserWidgetMode = (mode) =>
|
|
441
|
+
compose(
|
|
442
|
+
injectIntl,
|
|
443
|
+
withObjectBrowser,
|
|
444
|
+
withRouter,
|
|
445
|
+
connect(null, { searchContent }),
|
|
446
|
+
)((props) => <ObjectBrowserWidgetComponent {...props} mode={mode} />);
|
|
447
|
+
export { ObjectBrowserWidgetMode };
|
|
448
|
+
export default compose(
|
|
449
|
+
injectIntl,
|
|
450
|
+
withObjectBrowser,
|
|
451
|
+
withRouter,
|
|
452
|
+
connect(null, { searchContent }),
|
|
453
|
+
)(ObjectBrowserWidgetComponent);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Customized ObjectBrowserWidget to preserve query strings in the manually pasted internal URL. (refs #299560)
|