decap-cms-core 3.6.3 → 3.7.1
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/dist/decap-cms-core.js +25 -25
- package/dist/decap-cms-core.js.LICENSE.txt +14 -8
- package/dist/decap-cms-core.js.map +1 -1
- package/dist/esm/actions/config.js +57 -49
- package/dist/esm/actions/editorialWorkflow.js +4 -4
- package/dist/esm/actions/entries.js +8 -14
- package/dist/esm/actions/mediaLibrary.js +6 -11
- package/dist/esm/actions/search.js +2 -2
- package/dist/esm/actions/status.js +2 -8
- package/dist/esm/backend.js +70 -79
- package/dist/esm/bootstrap.js +3 -2
- package/dist/esm/components/App/App.js +28 -34
- package/dist/esm/components/App/Header.js +32 -39
- package/dist/esm/components/Collection/Collection.js +45 -48
- package/dist/esm/components/Collection/CollectionSearch.js +76 -81
- package/dist/esm/components/Collection/CollectionTop.js +1 -2
- package/dist/esm/components/Collection/Entries/Entries.js +2 -4
- package/dist/esm/components/Collection/Entries/EntriesCollection.js +25 -29
- package/dist/esm/components/Collection/Entries/EntriesSearch.js +34 -38
- package/dist/esm/components/Collection/Entries/EntryCard.js +8 -13
- package/dist/esm/components/Collection/Entries/EntryListing.js +72 -76
- package/dist/esm/components/Collection/FilterControl.js +1 -1
- package/dist/esm/components/Collection/GroupControl.js +1 -1
- package/dist/esm/components/Collection/NestedCollection.js +50 -53
- package/dist/esm/components/Collection/Sidebar.js +35 -38
- package/dist/esm/components/Collection/SortControl.js +3 -3
- package/dist/esm/components/Collection/ViewStyleControl.js +1 -2
- package/dist/esm/components/Editor/Editor.js +197 -201
- package/dist/esm/components/Editor/EditorControlPane/EditorControl.js +79 -87
- package/dist/esm/components/Editor/EditorControlPane/EditorControlPane.js +75 -86
- package/dist/esm/components/Editor/EditorControlPane/Widget.js +226 -228
- package/dist/esm/components/Editor/EditorInterface.js +69 -80
- package/dist/esm/components/Editor/EditorPreviewPane/EditorPreview.js +1 -2
- package/dist/esm/components/Editor/EditorPreviewPane/EditorPreviewContent.js +20 -28
- package/dist/esm/components/Editor/EditorPreviewPane/EditorPreviewPane.js +163 -161
- package/dist/esm/components/Editor/EditorPreviewPane/PreviewHOC.js +4 -8
- package/dist/esm/components/Editor/EditorToolbar.js +335 -347
- package/dist/esm/components/Editor/withWorkflow.js +5 -6
- package/dist/esm/components/MediaLibrary/MediaLibrary.js +304 -294
- package/dist/esm/components/MediaLibrary/MediaLibraryButtons.js +40 -46
- package/dist/esm/components/MediaLibrary/MediaLibraryCard.js +1 -2
- package/dist/esm/components/MediaLibrary/MediaLibraryCardGrid.js +8 -13
- package/dist/esm/components/MediaLibrary/MediaLibraryModal.js +3 -3
- package/dist/esm/components/MediaLibrary/MediaLibrarySearch.js +1 -2
- package/dist/esm/components/MediaLibrary/MediaLibraryTop.js +3 -6
- package/dist/esm/components/UI/DragDrop.js +15 -23
- package/dist/esm/components/UI/ErrorBoundary.js +23 -25
- package/dist/esm/components/UI/Modal.js +10 -12
- package/dist/esm/components/UI/Notifications.js +4 -8
- package/dist/esm/components/UI/SettingsDropdown.js +4 -8
- package/dist/esm/components/Workflow/Workflow.js +19 -20
- package/dist/esm/components/Workflow/WorkflowCard.js +2 -4
- package/dist/esm/components/Workflow/WorkflowList.js +105 -113
- package/dist/esm/constants/configSchema.js +18 -16
- package/dist/esm/formats/formats.js +11 -12
- package/dist/esm/formats/frontmatter.js +17 -21
- package/dist/esm/formats/toml.js +2 -2
- package/dist/esm/formats/yaml.js +2 -6
- package/dist/esm/index.js +3 -7
- package/dist/esm/integrations/providers/algolia/implementation.js +12 -14
- package/dist/esm/integrations/providers/assetStore/implementation.js +10 -12
- package/dist/esm/lib/formatters.js +13 -17
- package/dist/esm/lib/i18n.js +35 -33
- package/dist/esm/lib/phrases.js +2 -2
- package/dist/esm/lib/polyfill.js +8 -0
- package/dist/esm/lib/registry.js +35 -35
- package/dist/esm/lib/serializeEntryValues.js +3 -3
- package/dist/esm/lib/stega.js +142 -0
- package/dist/esm/lib/urlHelper.js +16 -18
- package/dist/esm/mediaLibrary.js +3 -4
- package/dist/esm/reducers/collections.js +26 -42
- package/dist/esm/reducers/combinedReducer.js +3 -6
- package/dist/esm/reducers/config.js +3 -7
- package/dist/esm/reducers/editorialWorkflow.js +5 -9
- package/dist/esm/reducers/entries.js +33 -35
- package/dist/esm/reducers/entryDraft.js +2 -2
- package/dist/esm/reducers/integrations.js +8 -14
- package/dist/esm/reducers/mediaLibrary.js +18 -20
- package/dist/esm/reducers/notifications.js +4 -8
- package/dist/esm/types/immutable.js +7 -1
- package/dist/esm/valueObjects/AssetProxy.js +1 -9
- package/dist/esm/valueObjects/EditorComponent.js +18 -25
- package/dist/esm/valueObjects/Entry.js +2 -2
- package/index.d.ts +2 -0
- package/package.json +14 -11
- package/src/actions/__tests__/config.spec.js +3 -3
- package/src/actions/config.ts +3 -1
- package/src/actions/editorialWorkflow.ts +1 -1
- package/src/actions/entries.ts +1 -1
- package/src/actions/search.ts +1 -1
- package/src/backend.ts +8 -1
- package/src/bootstrap.js +1 -0
- package/src/components/App/App.js +5 -0
- package/src/components/App/Header.js +3 -0
- package/src/components/Collection/Collection.js +5 -0
- package/src/components/Collection/CollectionSearch.js +5 -0
- package/src/components/Collection/Entries/EntriesCollection.js +4 -1
- package/src/components/Collection/Entries/EntriesSearch.js +4 -1
- package/src/components/Collection/Entries/EntryListing.js +5 -0
- package/src/components/Collection/Entries/__tests__/__snapshots__/EntriesCollection.spec.js.snap +0 -4
- package/src/components/Collection/NestedCollection.js +6 -1
- package/src/components/Collection/Sidebar.js +5 -0
- package/src/components/Editor/Editor.js +4 -1
- package/src/components/Editor/EditorControlPane/EditorControl.js +7 -1
- package/src/components/Editor/EditorControlPane/Widget.js +5 -0
- package/src/components/Editor/EditorPreviewPane/EditorPreviewPane.js +1 -1
- package/src/components/Editor/EditorToolbar.js +3 -0
- package/src/components/Editor/__tests__/Editor.spec.js +3 -4
- package/src/components/Editor/__tests__/__snapshots__/Editor.spec.js.snap +5 -5
- package/src/components/Editor/__tests__/__snapshots__/EditorToolbar.spec.js.snap +708 -393
- package/src/components/MediaLibrary/MediaLibrary.js +5 -1
- package/src/components/MediaLibrary/MediaLibraryModal.js +1 -1
- package/src/components/UI/ErrorBoundary.js +6 -1
- package/src/components/UI/Modal.js +3 -0
- package/src/components/Workflow/Workflow.js +3 -0
- package/src/components/Workflow/WorkflowList.js +5 -0
- package/src/constants/__tests__/configSchema.spec.js +1 -1
- package/src/formats/formats.ts +1 -1
- package/src/formats/toml.ts +2 -2
- package/src/integrations/providers/algolia/implementation.js +2 -2
- package/src/integrations/providers/assetStore/implementation.js +2 -1
- package/src/lib/formatters.ts +4 -1
- package/src/lib/i18n.ts +3 -1
- package/src/lib/phrases.js +1 -1
- package/src/lib/polyfill.js +9 -0
- package/src/lib/serializeEntryValues.js +1 -1
- package/src/lib/stega.ts +145 -0
- package/src/lib/urlHelper.ts +4 -1
- package/src/mediaLibrary.ts +1 -1
- package/src/reducers/collections.ts +2 -1
- package/src/reducers/editorialWorkflow.ts +1 -1
- package/src/reducers/entries.ts +6 -1
- package/src/reducers/entryDraft.js +1 -1
- package/src/types/immutable.ts +10 -0
- package/src/types/redux.ts +2 -0
- package/src/valueObjects/EditorComponent.js +1 -1
- package/src/valueObjects/Entry.ts +1 -1
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
2
|
-
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
3
|
-
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
4
|
-
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
|
|
5
|
-
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
6
1
|
import React from 'react';
|
|
7
2
|
import { connect } from 'react-redux';
|
|
8
3
|
import { EDITORIAL_WORKFLOW } from '../../constants/publishModes';
|
|
@@ -46,7 +41,11 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
|
|
|
46
41
|
// Overwrite persistEntry to persistUnpublishedEntry
|
|
47
42
|
returnObj.persistEntry = collection => dispatch(persistUnpublishedEntry(collection, unpublishedEntry));
|
|
48
43
|
}
|
|
49
|
-
return
|
|
44
|
+
return {
|
|
45
|
+
...ownProps,
|
|
46
|
+
...stateProps,
|
|
47
|
+
...returnObj
|
|
48
|
+
};
|
|
50
49
|
}
|
|
51
50
|
export default function withWorkflow(Editor) {
|
|
52
51
|
return connect(mapStateToProps, null, mergeProps)(class WorkflowEditor extends React.Component {
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import _map from "lodash/map";
|
|
2
|
-
import _orderBy from "lodash/orderBy";
|
|
3
|
-
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
4
|
-
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
5
|
-
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
6
|
-
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
|
|
7
|
-
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
8
1
|
import React from 'react';
|
|
9
2
|
import PropTypes from 'prop-types';
|
|
10
3
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
11
4
|
import { connect } from 'react-redux';
|
|
5
|
+
import orderBy from 'lodash/orderBy';
|
|
6
|
+
import map from 'lodash/map';
|
|
12
7
|
import { translate } from 'react-polyglot';
|
|
13
8
|
import fuzzy from 'fuzzy';
|
|
14
9
|
import { fileExtension } from 'decap-cms-lib-util';
|
|
@@ -24,266 +19,47 @@ import { jsx as ___EmotionJSX } from "@emotion/react";
|
|
|
24
19
|
const IMAGE_EXTENSIONS_VIEWABLE = ['jpg', 'jpeg', 'webp', 'gif', 'png', 'bmp', 'tiff', 'svg', 'avif'];
|
|
25
20
|
const IMAGE_EXTENSIONS = [...IMAGE_EXTENSIONS_VIEWABLE];
|
|
26
21
|
class MediaLibrary extends React.Component {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
*/
|
|
56
|
-
_defineProperty(this, "toTableData", files => {
|
|
57
|
-
const tableData = files && files.map(({
|
|
58
|
-
key,
|
|
59
|
-
name,
|
|
60
|
-
id,
|
|
61
|
-
size,
|
|
62
|
-
path,
|
|
63
|
-
queryOrder,
|
|
64
|
-
displayURL,
|
|
65
|
-
draft
|
|
66
|
-
}) => {
|
|
67
|
-
const ext = fileExtension(name).toLowerCase();
|
|
68
|
-
return {
|
|
69
|
-
key,
|
|
70
|
-
id,
|
|
71
|
-
name,
|
|
72
|
-
path,
|
|
73
|
-
type: ext.toUpperCase(),
|
|
74
|
-
size,
|
|
75
|
-
queryOrder,
|
|
76
|
-
displayURL,
|
|
77
|
-
draft,
|
|
78
|
-
isImage: IMAGE_EXTENSIONS.includes(ext),
|
|
79
|
-
isViewableImage: IMAGE_EXTENSIONS_VIEWABLE.includes(ext)
|
|
80
|
-
};
|
|
81
|
-
});
|
|
22
|
+
static propTypes = {
|
|
23
|
+
isVisible: PropTypes.bool,
|
|
24
|
+
loadMediaDisplayURL: PropTypes.func,
|
|
25
|
+
displayURLs: ImmutablePropTypes.map,
|
|
26
|
+
canInsert: PropTypes.bool,
|
|
27
|
+
files: PropTypes.arrayOf(PropTypes.shape(fileShape)).isRequired,
|
|
28
|
+
dynamicSearch: PropTypes.bool,
|
|
29
|
+
dynamicSearchActive: PropTypes.bool,
|
|
30
|
+
forImage: PropTypes.bool,
|
|
31
|
+
isLoading: PropTypes.bool,
|
|
32
|
+
isPersisting: PropTypes.bool,
|
|
33
|
+
isDeleting: PropTypes.bool,
|
|
34
|
+
hasNextPage: PropTypes.bool,
|
|
35
|
+
isPaginating: PropTypes.bool,
|
|
36
|
+
privateUpload: PropTypes.bool,
|
|
37
|
+
config: ImmutablePropTypes.map,
|
|
38
|
+
loadMedia: PropTypes.func.isRequired,
|
|
39
|
+
dynamicSearchQuery: PropTypes.string,
|
|
40
|
+
page: PropTypes.number,
|
|
41
|
+
persistMedia: PropTypes.func.isRequired,
|
|
42
|
+
deleteMedia: PropTypes.func.isRequired,
|
|
43
|
+
insertMedia: PropTypes.func.isRequired,
|
|
44
|
+
closeMediaLibrary: PropTypes.func.isRequired,
|
|
45
|
+
t: PropTypes.func.isRequired
|
|
46
|
+
};
|
|
47
|
+
static defaultProps = {
|
|
48
|
+
files: []
|
|
49
|
+
};
|
|
82
50
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return _orderBy(tableData, fieldNames, directions);
|
|
93
|
-
});
|
|
94
|
-
_defineProperty(this, "handleClose", () => {
|
|
95
|
-
this.props.closeMediaLibrary();
|
|
96
|
-
});
|
|
97
|
-
/**
|
|
98
|
-
* Toggle asset selection on click.
|
|
99
|
-
*/
|
|
100
|
-
_defineProperty(this, "handleAssetClick", asset => {
|
|
101
|
-
const selectedFile = this.state.selectedFile.key === asset.key ? {} : asset;
|
|
102
|
-
this.setState({
|
|
103
|
-
selectedFile
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
/**
|
|
107
|
-
* Upload a file.
|
|
108
|
-
*/
|
|
109
|
-
_defineProperty(this, "handlePersist", async event => {
|
|
110
|
-
/**
|
|
111
|
-
* Stop the browser from automatically handling the file input click, and
|
|
112
|
-
* get the file for upload, and retain the synthetic event for access after
|
|
113
|
-
* the asynchronous persist operation.
|
|
114
|
-
*/
|
|
115
|
-
event.persist();
|
|
116
|
-
event.stopPropagation();
|
|
117
|
-
event.preventDefault();
|
|
118
|
-
const {
|
|
119
|
-
persistMedia,
|
|
120
|
-
privateUpload,
|
|
121
|
-
config,
|
|
122
|
-
t,
|
|
123
|
-
field
|
|
124
|
-
} = this.props;
|
|
125
|
-
const {
|
|
126
|
-
files: fileList
|
|
127
|
-
} = event.dataTransfer || event.target;
|
|
128
|
-
const files = [...fileList];
|
|
129
|
-
const file = files[0];
|
|
130
|
-
const maxFileSize = config.get('max_file_size');
|
|
131
|
-
if (maxFileSize && file.size > maxFileSize) {
|
|
132
|
-
window.alert(t('mediaLibrary.mediaLibrary.fileTooLarge', {
|
|
133
|
-
size: Math.floor(maxFileSize / 1000)
|
|
134
|
-
}));
|
|
135
|
-
} else {
|
|
136
|
-
await persistMedia(file, {
|
|
137
|
-
privateUpload,
|
|
138
|
-
field
|
|
139
|
-
});
|
|
140
|
-
this.setState({
|
|
141
|
-
isPersisted: true
|
|
142
|
-
});
|
|
143
|
-
this.scrollToTop();
|
|
144
|
-
}
|
|
145
|
-
event.target.value = null;
|
|
146
|
-
});
|
|
147
|
-
/**
|
|
148
|
-
* Stores the public path of the file in the application store, where the
|
|
149
|
-
* editor field that launched the media library can retrieve it.
|
|
150
|
-
*/
|
|
151
|
-
_defineProperty(this, "handleInsert", () => {
|
|
152
|
-
const {
|
|
153
|
-
selectedFile
|
|
154
|
-
} = this.state;
|
|
155
|
-
const {
|
|
156
|
-
path
|
|
157
|
-
} = selectedFile;
|
|
158
|
-
const {
|
|
159
|
-
insertMedia,
|
|
160
|
-
field
|
|
161
|
-
} = this.props;
|
|
162
|
-
insertMedia(path, field);
|
|
163
|
-
this.handleClose();
|
|
164
|
-
});
|
|
165
|
-
/**
|
|
166
|
-
* Removes the selected file from the backend.
|
|
167
|
-
*/
|
|
168
|
-
_defineProperty(this, "handleDelete", () => {
|
|
169
|
-
const {
|
|
170
|
-
selectedFile
|
|
171
|
-
} = this.state;
|
|
172
|
-
const {
|
|
173
|
-
files,
|
|
174
|
-
deleteMedia,
|
|
175
|
-
privateUpload,
|
|
176
|
-
t
|
|
177
|
-
} = this.props;
|
|
178
|
-
if (!window.confirm(t('mediaLibrary.mediaLibrary.onDelete'))) {
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
const file = files.find(file => selectedFile.key === file.key);
|
|
182
|
-
deleteMedia(file, {
|
|
183
|
-
privateUpload
|
|
184
|
-
}).then(() => {
|
|
185
|
-
this.setState({
|
|
186
|
-
selectedFile: {}
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
/**
|
|
191
|
-
* Downloads the selected file.
|
|
192
|
-
*/
|
|
193
|
-
_defineProperty(this, "handleDownload", () => {
|
|
194
|
-
const {
|
|
195
|
-
selectedFile
|
|
196
|
-
} = this.state;
|
|
197
|
-
const {
|
|
198
|
-
displayURLs
|
|
199
|
-
} = this.props;
|
|
200
|
-
const url = displayURLs.getIn([selectedFile.id, 'url']) || selectedFile.url;
|
|
201
|
-
if (!url) {
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
const filename = selectedFile.name;
|
|
205
|
-
const element = document.createElement('a');
|
|
206
|
-
element.setAttribute('href', url);
|
|
207
|
-
element.setAttribute('download', filename);
|
|
208
|
-
element.style.display = 'none';
|
|
209
|
-
document.body.appendChild(element);
|
|
210
|
-
element.click();
|
|
211
|
-
document.body.removeChild(element);
|
|
212
|
-
this.setState({
|
|
213
|
-
selectedFile: {}
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
/**
|
|
217
|
-
*
|
|
218
|
-
*/
|
|
219
|
-
_defineProperty(this, "handleLoadMore", () => {
|
|
220
|
-
const {
|
|
221
|
-
loadMedia,
|
|
222
|
-
dynamicSearchQuery,
|
|
223
|
-
page,
|
|
224
|
-
privateUpload
|
|
225
|
-
} = this.props;
|
|
226
|
-
loadMedia({
|
|
227
|
-
query: dynamicSearchQuery,
|
|
228
|
-
page: page + 1,
|
|
229
|
-
privateUpload
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
/**
|
|
233
|
-
* Executes media library search for implementations that support dynamic
|
|
234
|
-
* search via request. For these implementations, the Enter key must be
|
|
235
|
-
* pressed to execute search. If assets are being stored directly through
|
|
236
|
-
* the GitHub backend, search is in-memory and occurs as the query is typed,
|
|
237
|
-
* so this handler has no impact.
|
|
238
|
-
*/
|
|
239
|
-
_defineProperty(this, "handleSearchKeyDown", async event => {
|
|
240
|
-
const {
|
|
241
|
-
dynamicSearch,
|
|
242
|
-
loadMedia,
|
|
243
|
-
privateUpload
|
|
244
|
-
} = this.props;
|
|
245
|
-
if (event.key === 'Enter' && dynamicSearch) {
|
|
246
|
-
await loadMedia({
|
|
247
|
-
query: this.state.query,
|
|
248
|
-
privateUpload
|
|
249
|
-
});
|
|
250
|
-
this.scrollToTop();
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
_defineProperty(this, "scrollToTop", () => {
|
|
254
|
-
this.scrollContainerRef.scrollTop = 0;
|
|
255
|
-
});
|
|
256
|
-
/**
|
|
257
|
-
* Updates query state as the user types in the search field.
|
|
258
|
-
*/
|
|
259
|
-
_defineProperty(this, "handleSearchChange", event => {
|
|
260
|
-
this.setState({
|
|
261
|
-
query: event.target.value
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
/**
|
|
265
|
-
* Filters files that do not match the query. Not used for dynamic search.
|
|
266
|
-
*/
|
|
267
|
-
_defineProperty(this, "queryFilter", (query, files) => {
|
|
268
|
-
/**
|
|
269
|
-
* Because file names don't have spaces, typing a space eliminates all
|
|
270
|
-
* potential matches, so we strip them all out internally before running the
|
|
271
|
-
* query.
|
|
272
|
-
*/
|
|
273
|
-
const strippedQuery = query.replace(/ /g, '');
|
|
274
|
-
const matches = fuzzy.filter(strippedQuery, files, {
|
|
275
|
-
extract: file => file.name
|
|
276
|
-
});
|
|
277
|
-
const matchFiles = matches.map((match, queryIndex) => {
|
|
278
|
-
const file = files[match.index];
|
|
279
|
-
return _objectSpread(_objectSpread({}, file), {}, {
|
|
280
|
-
queryIndex
|
|
281
|
-
});
|
|
282
|
-
});
|
|
283
|
-
return matchFiles;
|
|
284
|
-
});
|
|
285
|
-
}
|
|
51
|
+
/**
|
|
52
|
+
* The currently selected file and query are tracked in component state as
|
|
53
|
+
* they do not impact the rest of the application.
|
|
54
|
+
*/
|
|
55
|
+
state = {
|
|
56
|
+
selectedFile: {},
|
|
57
|
+
query: '',
|
|
58
|
+
isPersisted: false
|
|
59
|
+
};
|
|
286
60
|
componentDidMount() {
|
|
61
|
+
// Manually validate PropTypes - React 19 breaking change
|
|
62
|
+
PropTypes.checkPropTypes(MediaLibrary.propTypes, this.props, 'prop', 'MediaLibrary');
|
|
287
63
|
this.props.loadMedia();
|
|
288
64
|
}
|
|
289
65
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
@@ -320,6 +96,266 @@ class MediaLibrary extends React.Component {
|
|
|
320
96
|
});
|
|
321
97
|
}
|
|
322
98
|
}
|
|
99
|
+
loadDisplayURL = file => {
|
|
100
|
+
const {
|
|
101
|
+
loadMediaDisplayURL
|
|
102
|
+
} = this.props;
|
|
103
|
+
loadMediaDisplayURL(file);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Filter an array of file data to include only images.
|
|
108
|
+
*/
|
|
109
|
+
filterImages = files => {
|
|
110
|
+
return files.filter(file => {
|
|
111
|
+
const ext = fileExtension(file.name).toLowerCase();
|
|
112
|
+
return IMAGE_EXTENSIONS.includes(ext);
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Transform file data for table display.
|
|
118
|
+
*/
|
|
119
|
+
toTableData = files => {
|
|
120
|
+
const tableData = files && files.map(({
|
|
121
|
+
key,
|
|
122
|
+
name,
|
|
123
|
+
id,
|
|
124
|
+
size,
|
|
125
|
+
path,
|
|
126
|
+
queryOrder,
|
|
127
|
+
displayURL,
|
|
128
|
+
draft
|
|
129
|
+
}) => {
|
|
130
|
+
const ext = fileExtension(name).toLowerCase();
|
|
131
|
+
return {
|
|
132
|
+
key,
|
|
133
|
+
id,
|
|
134
|
+
name,
|
|
135
|
+
path,
|
|
136
|
+
type: ext.toUpperCase(),
|
|
137
|
+
size,
|
|
138
|
+
queryOrder,
|
|
139
|
+
displayURL,
|
|
140
|
+
draft,
|
|
141
|
+
isImage: IMAGE_EXTENSIONS.includes(ext),
|
|
142
|
+
isViewableImage: IMAGE_EXTENSIONS_VIEWABLE.includes(ext)
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get the sort order for use with `lodash.orderBy`, and always add the
|
|
148
|
+
* `queryOrder` sort as the lowest priority sort order.
|
|
149
|
+
*/
|
|
150
|
+
const {
|
|
151
|
+
sortFields
|
|
152
|
+
} = this.state;
|
|
153
|
+
const fieldNames = map(sortFields, 'fieldName').concat('queryOrder');
|
|
154
|
+
const directions = map(sortFields, 'direction').concat('asc');
|
|
155
|
+
return orderBy(tableData, fieldNames, directions);
|
|
156
|
+
};
|
|
157
|
+
handleClose = () => {
|
|
158
|
+
this.props.closeMediaLibrary();
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Toggle asset selection on click.
|
|
163
|
+
*/
|
|
164
|
+
handleAssetClick = asset => {
|
|
165
|
+
const selectedFile = this.state.selectedFile.key === asset.key ? {} : asset;
|
|
166
|
+
this.setState({
|
|
167
|
+
selectedFile
|
|
168
|
+
});
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Upload a file.
|
|
173
|
+
*/
|
|
174
|
+
handlePersist = async event => {
|
|
175
|
+
/**
|
|
176
|
+
* Stop the browser from automatically handling the file input click, and
|
|
177
|
+
* get the file for upload, and retain the synthetic event for access after
|
|
178
|
+
* the asynchronous persist operation.
|
|
179
|
+
*/
|
|
180
|
+
event.persist();
|
|
181
|
+
event.stopPropagation();
|
|
182
|
+
event.preventDefault();
|
|
183
|
+
const {
|
|
184
|
+
persistMedia,
|
|
185
|
+
privateUpload,
|
|
186
|
+
config,
|
|
187
|
+
t,
|
|
188
|
+
field
|
|
189
|
+
} = this.props;
|
|
190
|
+
const {
|
|
191
|
+
files: fileList
|
|
192
|
+
} = event.dataTransfer || event.target;
|
|
193
|
+
const files = [...fileList];
|
|
194
|
+
const file = files[0];
|
|
195
|
+
const maxFileSize = config.get('max_file_size');
|
|
196
|
+
if (maxFileSize && file.size > maxFileSize) {
|
|
197
|
+
window.alert(t('mediaLibrary.mediaLibrary.fileTooLarge', {
|
|
198
|
+
size: Math.floor(maxFileSize / 1000)
|
|
199
|
+
}));
|
|
200
|
+
} else {
|
|
201
|
+
await persistMedia(file, {
|
|
202
|
+
privateUpload,
|
|
203
|
+
field
|
|
204
|
+
});
|
|
205
|
+
this.setState({
|
|
206
|
+
isPersisted: true
|
|
207
|
+
});
|
|
208
|
+
this.scrollToTop();
|
|
209
|
+
}
|
|
210
|
+
event.target.value = null;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Stores the public path of the file in the application store, where the
|
|
215
|
+
* editor field that launched the media library can retrieve it.
|
|
216
|
+
*/
|
|
217
|
+
handleInsert = () => {
|
|
218
|
+
const {
|
|
219
|
+
selectedFile
|
|
220
|
+
} = this.state;
|
|
221
|
+
const {
|
|
222
|
+
path
|
|
223
|
+
} = selectedFile;
|
|
224
|
+
const {
|
|
225
|
+
insertMedia,
|
|
226
|
+
field
|
|
227
|
+
} = this.props;
|
|
228
|
+
insertMedia(path, field);
|
|
229
|
+
this.handleClose();
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Removes the selected file from the backend.
|
|
234
|
+
*/
|
|
235
|
+
handleDelete = () => {
|
|
236
|
+
const {
|
|
237
|
+
selectedFile
|
|
238
|
+
} = this.state;
|
|
239
|
+
const {
|
|
240
|
+
files,
|
|
241
|
+
deleteMedia,
|
|
242
|
+
privateUpload,
|
|
243
|
+
t
|
|
244
|
+
} = this.props;
|
|
245
|
+
if (!window.confirm(t('mediaLibrary.mediaLibrary.onDelete'))) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const file = files.find(file => selectedFile.key === file.key);
|
|
249
|
+
deleteMedia(file, {
|
|
250
|
+
privateUpload
|
|
251
|
+
}).then(() => {
|
|
252
|
+
this.setState({
|
|
253
|
+
selectedFile: {}
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Downloads the selected file.
|
|
260
|
+
*/
|
|
261
|
+
handleDownload = () => {
|
|
262
|
+
const {
|
|
263
|
+
selectedFile
|
|
264
|
+
} = this.state;
|
|
265
|
+
const {
|
|
266
|
+
displayURLs
|
|
267
|
+
} = this.props;
|
|
268
|
+
const url = displayURLs.getIn([selectedFile.id, 'url']) || selectedFile.url;
|
|
269
|
+
if (!url) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const filename = selectedFile.name;
|
|
273
|
+
const element = document.createElement('a');
|
|
274
|
+
element.setAttribute('href', url);
|
|
275
|
+
element.setAttribute('download', filename);
|
|
276
|
+
element.style.display = 'none';
|
|
277
|
+
document.body.appendChild(element);
|
|
278
|
+
element.click();
|
|
279
|
+
document.body.removeChild(element);
|
|
280
|
+
this.setState({
|
|
281
|
+
selectedFile: {}
|
|
282
|
+
});
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
*
|
|
287
|
+
*/
|
|
288
|
+
|
|
289
|
+
handleLoadMore = () => {
|
|
290
|
+
const {
|
|
291
|
+
loadMedia,
|
|
292
|
+
dynamicSearchQuery,
|
|
293
|
+
page,
|
|
294
|
+
privateUpload
|
|
295
|
+
} = this.props;
|
|
296
|
+
loadMedia({
|
|
297
|
+
query: dynamicSearchQuery,
|
|
298
|
+
page: page + 1,
|
|
299
|
+
privateUpload
|
|
300
|
+
});
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Executes media library search for implementations that support dynamic
|
|
305
|
+
* search via request. For these implementations, the Enter key must be
|
|
306
|
+
* pressed to execute search. If assets are being stored directly through
|
|
307
|
+
* the GitHub backend, search is in-memory and occurs as the query is typed,
|
|
308
|
+
* so this handler has no impact.
|
|
309
|
+
*/
|
|
310
|
+
handleSearchKeyDown = async event => {
|
|
311
|
+
const {
|
|
312
|
+
dynamicSearch,
|
|
313
|
+
loadMedia,
|
|
314
|
+
privateUpload
|
|
315
|
+
} = this.props;
|
|
316
|
+
if (event.key === 'Enter' && dynamicSearch) {
|
|
317
|
+
await loadMedia({
|
|
318
|
+
query: this.state.query,
|
|
319
|
+
privateUpload
|
|
320
|
+
});
|
|
321
|
+
this.scrollToTop();
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
scrollToTop = () => {
|
|
325
|
+
this.scrollContainerRef.scrollTop = 0;
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Updates query state as the user types in the search field.
|
|
330
|
+
*/
|
|
331
|
+
handleSearchChange = event => {
|
|
332
|
+
this.setState({
|
|
333
|
+
query: event.target.value
|
|
334
|
+
});
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Filters files that do not match the query. Not used for dynamic search.
|
|
339
|
+
*/
|
|
340
|
+
queryFilter = (query, files) => {
|
|
341
|
+
/**
|
|
342
|
+
* Because file names don't have spaces, typing a space eliminates all
|
|
343
|
+
* potential matches, so we strip them all out internally before running the
|
|
344
|
+
* query.
|
|
345
|
+
*/
|
|
346
|
+
const strippedQuery = query.replace(/ /g, '');
|
|
347
|
+
const matches = fuzzy.filter(strippedQuery, files, {
|
|
348
|
+
extract: file => file.name
|
|
349
|
+
});
|
|
350
|
+
const matchFiles = matches.map((match, queryIndex) => {
|
|
351
|
+
const file = files[match.index];
|
|
352
|
+
return {
|
|
353
|
+
...file,
|
|
354
|
+
queryIndex
|
|
355
|
+
};
|
|
356
|
+
});
|
|
357
|
+
return matchFiles;
|
|
358
|
+
};
|
|
323
359
|
render() {
|
|
324
360
|
const {
|
|
325
361
|
isVisible,
|
|
@@ -371,34 +407,6 @@ class MediaLibrary extends React.Component {
|
|
|
371
407
|
});
|
|
372
408
|
}
|
|
373
409
|
}
|
|
374
|
-
_defineProperty(MediaLibrary, "propTypes", {
|
|
375
|
-
isVisible: PropTypes.bool,
|
|
376
|
-
loadMediaDisplayURL: PropTypes.func,
|
|
377
|
-
displayURLs: ImmutablePropTypes.map,
|
|
378
|
-
canInsert: PropTypes.bool,
|
|
379
|
-
files: PropTypes.arrayOf(PropTypes.shape(fileShape)).isRequired,
|
|
380
|
-
dynamicSearch: PropTypes.bool,
|
|
381
|
-
dynamicSearchActive: PropTypes.bool,
|
|
382
|
-
forImage: PropTypes.bool,
|
|
383
|
-
isLoading: PropTypes.bool,
|
|
384
|
-
isPersisting: PropTypes.bool,
|
|
385
|
-
isDeleting: PropTypes.bool,
|
|
386
|
-
hasNextPage: PropTypes.bool,
|
|
387
|
-
isPaginating: PropTypes.bool,
|
|
388
|
-
privateUpload: PropTypes.bool,
|
|
389
|
-
config: ImmutablePropTypes.map,
|
|
390
|
-
loadMedia: PropTypes.func.isRequired,
|
|
391
|
-
dynamicSearchQuery: PropTypes.string,
|
|
392
|
-
page: PropTypes.number,
|
|
393
|
-
persistMedia: PropTypes.func.isRequired,
|
|
394
|
-
deleteMedia: PropTypes.func.isRequired,
|
|
395
|
-
insertMedia: PropTypes.func.isRequired,
|
|
396
|
-
closeMediaLibrary: PropTypes.func.isRequired,
|
|
397
|
-
t: PropTypes.func.isRequired
|
|
398
|
-
});
|
|
399
|
-
_defineProperty(MediaLibrary, "defaultProps", {
|
|
400
|
-
files: []
|
|
401
|
-
});
|
|
402
410
|
function mapStateToProps(state) {
|
|
403
411
|
const {
|
|
404
412
|
mediaLibrary
|
|
@@ -423,7 +431,9 @@ function mapStateToProps(state) {
|
|
|
423
431
|
isPaginating: mediaLibrary.get('isPaginating'),
|
|
424
432
|
field
|
|
425
433
|
};
|
|
426
|
-
return
|
|
434
|
+
return {
|
|
435
|
+
...mediaLibraryProps
|
|
436
|
+
};
|
|
427
437
|
}
|
|
428
438
|
const mapDispatchToProps = {
|
|
429
439
|
loadMedia: loadMediaAction,
|