decap-cms-core 3.5.0 → 3.6.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/dist/decap-cms-core.js +9 -9
- package/dist/decap-cms-core.js.map +1 -1
- package/dist/esm/actions/auth.js +22 -37
- package/dist/esm/actions/collections.js +9 -17
- package/dist/esm/actions/config.js +58 -74
- package/dist/esm/actions/deploys.js +10 -17
- package/dist/esm/actions/editorialWorkflow.js +87 -101
- package/dist/esm/actions/entries.js +161 -211
- package/dist/esm/actions/media.js +31 -46
- package/dist/esm/actions/mediaLibrary.js +94 -126
- package/dist/esm/actions/notifications.js +5 -13
- package/dist/esm/actions/search.js +30 -47
- package/dist/esm/actions/status.js +13 -23
- package/dist/esm/actions/waitUntil.js +4 -11
- package/dist/esm/backend.js +132 -148
- package/dist/esm/bootstrap.js +37 -44
- package/dist/esm/components/App/App.js +82 -89
- package/dist/esm/components/App/Header.js +46 -52
- package/dist/esm/components/App/NotFoundPage.js +11 -18
- package/dist/esm/components/Collection/Collection.js +55 -63
- package/dist/esm/components/Collection/CollectionControls.js +15 -22
- package/dist/esm/components/Collection/CollectionSearch.js +35 -42
- package/dist/esm/components/Collection/CollectionTop.js +23 -30
- package/dist/esm/components/Collection/ControlButton.js +10 -16
- package/dist/esm/components/Collection/Entries/Entries.js +24 -31
- package/dist/esm/components/Collection/Entries/EntriesCollection.js +52 -62
- package/dist/esm/components/Collection/Entries/EntriesSearch.js +26 -33
- package/dist/esm/components/Collection/Entries/EntryCard.js +38 -45
- package/dist/esm/components/Collection/Entries/EntryListing.js +24 -32
- package/dist/esm/components/Collection/FilterControl.js +9 -16
- package/dist/esm/components/Collection/GroupControl.js +9 -16
- package/dist/esm/components/Collection/NestedCollection.js +54 -65
- package/dist/esm/components/Collection/Sidebar.js +36 -43
- package/dist/esm/components/Collection/SortControl.js +19 -26
- package/dist/esm/components/Collection/ViewStyleControl.js +17 -24
- package/dist/esm/components/Editor/Editor.js +100 -108
- package/dist/esm/components/Editor/EditorControlPane/EditorControl.js +105 -112
- package/dist/esm/components/Editor/EditorControlPane/EditorControlPane.js +68 -62
- package/dist/esm/components/Editor/EditorControlPane/Widget.js +87 -73
- package/dist/esm/components/Editor/EditorInterface.js +95 -98
- package/dist/esm/components/Editor/EditorPreviewPane/EditorPreview.js +13 -21
- package/dist/esm/components/Editor/EditorPreviewPane/EditorPreviewContent.js +64 -23
- package/dist/esm/components/Editor/EditorPreviewPane/EditorPreviewPane.js +94 -78
- package/dist/esm/components/Editor/EditorPreviewPane/PreviewHOC.js +9 -16
- package/dist/esm/components/Editor/EditorToolbar.js +133 -140
- package/dist/esm/components/Editor/withWorkflow.js +15 -22
- package/dist/esm/components/EditorWidgets/Unknown/UnknownControl.js +9 -16
- package/dist/esm/components/EditorWidgets/Unknown/UnknownPreview.js +9 -16
- package/dist/esm/components/EditorWidgets/index.js +4 -7
- package/dist/esm/components/MediaLibrary/EmptyMessage.js +12 -19
- package/dist/esm/components/MediaLibrary/MediaLibrary.js +55 -62
- package/dist/esm/components/MediaLibrary/MediaLibraryButtons.js +28 -35
- package/dist/esm/components/MediaLibrary/MediaLibraryCard.js +36 -43
- package/dist/esm/components/MediaLibrary/MediaLibraryCardGrid.js +50 -57
- package/dist/esm/components/MediaLibrary/MediaLibraryHeader.js +16 -23
- package/dist/esm/components/MediaLibrary/MediaLibraryModal.js +59 -64
- package/dist/esm/components/MediaLibrary/MediaLibrarySearch.js +18 -25
- package/dist/esm/components/MediaLibrary/MediaLibraryTop.js +39 -46
- package/dist/esm/components/UI/DragDrop.js +21 -30
- package/dist/esm/components/UI/ErrorBoundary.js +35 -43
- package/dist/esm/components/UI/FileUploadButton.js +11 -18
- package/dist/esm/components/UI/Modal.js +19 -26
- package/dist/esm/components/UI/Notifications.js +21 -28
- package/dist/esm/components/UI/SettingsDropdown.js +28 -34
- package/dist/esm/components/UI/index.js +6 -60
- package/dist/esm/components/Workflow/Workflow.js +52 -61
- package/dist/esm/components/Workflow/WorkflowCard.js +45 -51
- package/dist/esm/components/Workflow/WorkflowList.js +43 -49
- package/dist/esm/constants/collectionTypes.js +2 -8
- package/dist/esm/constants/collectionViews.js +2 -8
- package/dist/esm/constants/commitProps.js +2 -8
- package/dist/esm/constants/configSchema.js +20 -27
- package/dist/esm/constants/fieldInference.js +8 -15
- package/dist/esm/constants/publishModes.js +6 -11
- package/dist/esm/constants/validationErrorTypes.js +1 -7
- package/dist/esm/formats/formats.js +32 -41
- package/dist/esm/formats/frontmatter.js +18 -30
- package/dist/esm/formats/helpers.js +1 -7
- package/dist/esm/formats/json.js +1 -7
- package/dist/esm/formats/toml.js +11 -18
- package/dist/esm/formats/yaml.js +7 -14
- package/dist/esm/index.js +5 -12
- package/dist/esm/integrations/index.js +8 -16
- package/dist/esm/integrations/providers/algolia/implementation.js +14 -22
- package/dist/esm/integrations/providers/assetStore/implementation.js +10 -18
- package/dist/esm/lib/consoleError.js +1 -7
- package/dist/esm/lib/formatters.js +34 -47
- package/dist/esm/lib/i18n.js +37 -66
- package/dist/esm/lib/phrases.js +4 -11
- package/dist/esm/lib/registry.js +40 -75
- package/dist/esm/lib/serializeEntryValues.js +11 -18
- package/dist/esm/lib/textHelper.js +1 -7
- package/dist/esm/lib/urlHelper.js +28 -43
- package/dist/esm/mediaLibrary.js +12 -16
- package/dist/esm/reducers/auth.js +10 -16
- package/dist/esm/reducers/collections.js +70 -102
- package/dist/esm/reducers/combinedReducer.js +4 -11
- package/dist/esm/reducers/config.js +11 -19
- package/dist/esm/reducers/cursors.js +12 -18
- package/dist/esm/reducers/deploys.js +8 -15
- package/dist/esm/reducers/editorialWorkflow.js +37 -47
- package/dist/esm/reducers/entries.js +107 -132
- package/dist/esm/reducers/entryDraft.js +64 -72
- package/dist/esm/reducers/globalUI.js +5 -11
- package/dist/esm/reducers/index.js +43 -64
- package/dist/esm/reducers/integrations.js +8 -16
- package/dist/esm/reducers/mediaLibrary.js +43 -52
- package/dist/esm/reducers/medias.js +11 -18
- package/dist/esm/reducers/notifications.js +9 -15
- package/dist/esm/reducers/search.js +12 -18
- package/dist/esm/reducers/status.js +7 -13
- package/dist/esm/redux/index.js +7 -13
- package/dist/esm/redux/middleware/waitUntilAction.js +3 -10
- package/dist/esm/routing/history.js +7 -15
- package/dist/esm/types/diacritics.d.js +0 -1
- package/dist/esm/types/global.d.js +1 -5
- package/dist/esm/types/immutable.js +1 -5
- package/dist/esm/types/redux.js +7 -8
- package/dist/esm/types/tomlify-j0.4.d.js +0 -1
- package/dist/esm/valueObjects/AssetProxy.js +2 -10
- package/dist/esm/valueObjects/EditorComponent.js +5 -12
- package/dist/esm/valueObjects/Entry.js +3 -10
- package/package.json +3 -2
- package/src/components/Collection/Entries/EntriesCollection.js +7 -8
- package/src/components/Collection/Entries/__tests__/EntriesCollection.spec.js +3 -5
- package/src/components/Collection/Entries/__tests__/__snapshots__/EntriesCollection.spec.js.snap +8 -8
- package/src/components/Collection/NestedCollection.js +2 -2
- package/src/components/Collection/__tests__/__snapshots__/NestedCollection.spec.js.snap +68 -0
- package/src/components/Editor/EditorControlPane/EditorControl.js +0 -3
- package/src/components/Editor/EditorControlPane/EditorControlPane.js +21 -8
- package/src/components/Editor/EditorControlPane/Widget.js +22 -1
- package/src/components/Editor/EditorInterface.js +6 -1
- package/src/components/Editor/EditorPreviewPane/EditorPreviewContent.js +51 -11
- package/src/components/Editor/EditorPreviewPane/EditorPreviewPane.js +33 -1
- package/dist/esm/actions/editorControl.js +0 -14
- package/dist/esm/reducers/editorComponent.js +0 -1
- package/dist/esm/reducers/editorControl.js +0 -17
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.createEntry = createEntry;
|
|
7
|
-
var _isBoolean2 = _interopRequireDefault(require("lodash/isBoolean"));
|
|
8
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
9
|
-
function createEntry(collection, slug = '', path = '', options = {}) {
|
|
1
|
+
import _isBoolean from "lodash/isBoolean";
|
|
2
|
+
export function createEntry(collection, slug = '', path = '', options = {}) {
|
|
10
3
|
const returnObj = {
|
|
11
4
|
collection,
|
|
12
5
|
slug,
|
|
@@ -15,7 +8,7 @@ function createEntry(collection, slug = '', path = '', options = {}) {
|
|
|
15
8
|
raw: options.raw || '',
|
|
16
9
|
data: options.data || {},
|
|
17
10
|
label: options.label || null,
|
|
18
|
-
isModification: (
|
|
11
|
+
isModification: _isBoolean(options.isModification) ? options.isModification : null,
|
|
19
12
|
mediaFiles: options.mediaFiles || [],
|
|
20
13
|
author: options.author || '',
|
|
21
14
|
updatedOn: options.updatedOn || '',
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "decap-cms-core",
|
|
3
3
|
"description": "Decap CMS core application, see decap-cms package for the main distribution.",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.6.0",
|
|
5
5
|
"repository": "https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-core",
|
|
6
6
|
"bugs": "https://github.com/decaporg/decap-cms/issues",
|
|
7
7
|
"module": "dist/esm/index.js",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@iarna/toml": "2.2.5",
|
|
28
28
|
"@reduxjs/toolkit": "^1.9.1",
|
|
29
|
+
"@vercel/stega": "^0.1.2",
|
|
29
30
|
"ajv": "8.12.0",
|
|
30
31
|
"ajv-errors": "^3.0.0",
|
|
31
32
|
"ajv-keywords": "^5.0.0",
|
|
@@ -96,5 +97,5 @@
|
|
|
96
97
|
"@types/url-join": "^4.0.0",
|
|
97
98
|
"redux-mock-store": "^1.5.3"
|
|
98
99
|
},
|
|
99
|
-
"gitHead": "
|
|
100
|
+
"gitHead": "e2165450e5963b2a8d76d4cf331319bf91f05d45"
|
|
100
101
|
}
|
|
@@ -119,20 +119,19 @@ export class EntriesCollection extends React.Component {
|
|
|
119
119
|
|
|
120
120
|
export function filterNestedEntries(path, collectionFolder, entries) {
|
|
121
121
|
const filtered = entries.filter(e => {
|
|
122
|
-
|
|
122
|
+
let entryPath = e.get('path').slice(collectionFolder.length + 1);
|
|
123
123
|
if (!entryPath.startsWith(path)) {
|
|
124
124
|
return false;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
//
|
|
127
|
+
// for subdirectories, trim off the parent folder corresponding to
|
|
128
|
+
// this nested collection entry
|
|
128
129
|
if (path) {
|
|
129
|
-
|
|
130
|
-
const trimmed = entryPath.slice(path.length + 1);
|
|
131
|
-
return trimmed.split('/').length === 2;
|
|
132
|
-
} else {
|
|
133
|
-
// root path
|
|
134
|
-
return entryPath.split('/').length <= 2;
|
|
130
|
+
entryPath = entryPath.slice(path.length + 1);
|
|
135
131
|
}
|
|
132
|
+
|
|
133
|
+
// only show immediate children
|
|
134
|
+
return !entryPath.includes('/');
|
|
136
135
|
});
|
|
137
136
|
return filtered;
|
|
138
137
|
}
|
|
@@ -45,11 +45,11 @@ describe('filterNestedEntries', () => {
|
|
|
45
45
|
];
|
|
46
46
|
const entries = fromJS(entriesArray);
|
|
47
47
|
expect(filterNestedEntries('dir3', 'src/pages', entries).toJS()).toEqual([
|
|
48
|
-
{ slug: 'dir3/
|
|
48
|
+
{ slug: 'dir3/index', path: 'src/pages/dir3/index.md', data: { title: 'File 3' } },
|
|
49
49
|
]);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
it('should return immediate children
|
|
52
|
+
it('should return only immediate children for root path', () => {
|
|
53
53
|
const entriesArray = [
|
|
54
54
|
{ slug: 'index', path: 'src/pages/index.md', data: { title: 'Root' } },
|
|
55
55
|
{ slug: 'dir1/index', path: 'src/pages/dir1/index.md', data: { title: 'File 1' } },
|
|
@@ -60,8 +60,6 @@ describe('filterNestedEntries', () => {
|
|
|
60
60
|
const entries = fromJS(entriesArray);
|
|
61
61
|
expect(filterNestedEntries('', 'src/pages', entries).toJS()).toEqual([
|
|
62
62
|
{ slug: 'index', path: 'src/pages/index.md', data: { title: 'Root' } },
|
|
63
|
-
{ slug: 'dir1/index', path: 'src/pages/dir1/index.md', data: { title: 'File 1' } },
|
|
64
|
-
{ slug: 'dir3/index', path: 'src/pages/dir3/index.md', data: { title: 'File 3' } },
|
|
65
63
|
]);
|
|
66
64
|
});
|
|
67
65
|
});
|
|
@@ -126,7 +124,7 @@ describe('EntriesCollection', () => {
|
|
|
126
124
|
expect(asFragment()).toMatchSnapshot();
|
|
127
125
|
});
|
|
128
126
|
|
|
129
|
-
it('should render
|
|
127
|
+
it('should render with applied filter term for nested collections', () => {
|
|
130
128
|
const entriesArray = [
|
|
131
129
|
{ slug: 'index', path: 'src/pages/index.md', data: { title: 'Root' } },
|
|
132
130
|
{ slug: 'dir1/index', path: 'src/pages/dir1/index.md', data: { title: 'File 1' } },
|
package/src/components/Collection/Entries/__tests__/__snapshots__/EntriesCollection.spec.js.snap
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
|
-
exports[`EntriesCollection should render
|
|
3
|
+
exports[`EntriesCollection should render connected component 1`] = `
|
|
4
4
|
<DocumentFragment>
|
|
5
5
|
<mock-entries
|
|
6
6
|
collectionname="Pages"
|
|
7
|
-
collections="Map { \\"name\\": \\"pages\\", \\"label\\": \\"Pages\\", \\"folder\\": \\"src/pages\\"
|
|
7
|
+
collections="Map { \\"name\\": \\"pages\\", \\"label\\": \\"Pages\\", \\"folder\\": \\"src/pages\\" }"
|
|
8
8
|
cursor="[object Object]"
|
|
9
|
-
entries="List []"
|
|
9
|
+
entries="List [ Map { \\"slug\\": \\"index\\", \\"path\\": \\"src/pages/index.md\\", \\"data\\": Map { \\"title\\": \\"Root\\" } }, Map { \\"slug\\": \\"dir1/index\\", \\"path\\": \\"src/pages/dir1/index.md\\", \\"data\\": Map { \\"title\\": \\"File 1\\" } }, Map { \\"slug\\": \\"dir2/index\\", \\"path\\": \\"src/pages/dir2/index.md\\", \\"data\\": Map { \\"title\\": \\"File 2\\" } } ]"
|
|
10
10
|
isfetching="false"
|
|
11
11
|
/>
|
|
12
12
|
</DocumentFragment>
|
|
13
13
|
`;
|
|
14
14
|
|
|
15
|
-
exports[`EntriesCollection should render
|
|
15
|
+
exports[`EntriesCollection should render show only immediate children for nested collection 1`] = `
|
|
16
16
|
<DocumentFragment>
|
|
17
17
|
<mock-entries
|
|
18
18
|
collectionname="Pages"
|
|
19
|
-
collections="Map { \\"name\\": \\"pages\\", \\"label\\": \\"Pages\\", \\"folder\\": \\"src/pages\\" }"
|
|
19
|
+
collections="Map { \\"name\\": \\"pages\\", \\"label\\": \\"Pages\\", \\"folder\\": \\"src/pages\\", \\"nested\\": Map { \\"depth\\": 10 } }"
|
|
20
20
|
cursor="[object Object]"
|
|
21
|
-
entries="List [ Map { \\"slug\\": \\"index\\", \\"path\\": \\"src/pages/index.md\\", \\"data\\": Map { \\"title\\": \\"Root\\" } }
|
|
21
|
+
entries="List [ Map { \\"slug\\": \\"index\\", \\"path\\": \\"src/pages/index.md\\", \\"data\\": Map { \\"title\\": \\"Root\\" } } ]"
|
|
22
22
|
isfetching="false"
|
|
23
23
|
/>
|
|
24
24
|
</DocumentFragment>
|
|
25
25
|
`;
|
|
26
26
|
|
|
27
|
-
exports[`EntriesCollection should render
|
|
27
|
+
exports[`EntriesCollection should render with applied filter term for nested collections 1`] = `
|
|
28
28
|
<DocumentFragment>
|
|
29
29
|
<mock-entries
|
|
30
30
|
collectionname="Pages"
|
|
31
31
|
collections="Map { \\"name\\": \\"pages\\", \\"label\\": \\"Pages\\", \\"folder\\": \\"src/pages\\", \\"nested\\": Map { \\"depth\\": 10 } }"
|
|
32
32
|
cursor="[object Object]"
|
|
33
|
-
entries="List [ Map { \\"slug\\": \\"
|
|
33
|
+
entries="List [ Map { \\"slug\\": \\"dir3/dir4/index\\", \\"path\\": \\"src/pages/dir3/dir4/index.md\\", \\"data\\": Map { \\"title\\": \\"File 4\\" } } ]"
|
|
34
34
|
isfetching="false"
|
|
35
35
|
/>
|
|
36
36
|
</DocumentFragment>
|
|
@@ -80,7 +80,7 @@ function TreeNode(props) {
|
|
|
80
80
|
|
|
81
81
|
const sortedData = sortBy(treeData, getNodeTitle);
|
|
82
82
|
return sortedData.map(node => {
|
|
83
|
-
const leaf = node.children.length
|
|
83
|
+
const leaf = node.children.length === 0 && depth > 0;
|
|
84
84
|
if (leaf) {
|
|
85
85
|
return null;
|
|
86
86
|
}
|
|
@@ -90,7 +90,7 @@ function TreeNode(props) {
|
|
|
90
90
|
}
|
|
91
91
|
const title = getNodeTitle(node);
|
|
92
92
|
|
|
93
|
-
const hasChildren = depth === 0 || node.children.some(c => c.
|
|
93
|
+
const hasChildren = depth === 0 || node.children.some(c => c.isDir);
|
|
94
94
|
|
|
95
95
|
return (
|
|
96
96
|
<React.Fragment key={node.path}>
|
|
@@ -138,6 +138,20 @@ exports[`NestedCollection should render connected component 1`] = `
|
|
|
138
138
|
margin-right: 4px;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
.emotion-6 {
|
|
142
|
+
position: relative;
|
|
143
|
+
top: 2px;
|
|
144
|
+
color: #fff;
|
|
145
|
+
width: 0;
|
|
146
|
+
height: 0;
|
|
147
|
+
border: 5px solid transparent;
|
|
148
|
+
border-radius: 2px;
|
|
149
|
+
border-left: 6px solid currentColor;
|
|
150
|
+
border-right: 0;
|
|
151
|
+
color: currentColor;
|
|
152
|
+
left: 2px;
|
|
153
|
+
}
|
|
154
|
+
|
|
141
155
|
<a
|
|
142
156
|
class="emotion-0 emotion-1"
|
|
143
157
|
data-testid="/a"
|
|
@@ -155,6 +169,9 @@ exports[`NestedCollection should render connected component 1`] = `
|
|
|
155
169
|
>
|
|
156
170
|
File 1
|
|
157
171
|
</div>
|
|
172
|
+
<div
|
|
173
|
+
class="emotion-6 emotion-7"
|
|
174
|
+
/>
|
|
158
175
|
</div>
|
|
159
176
|
</a>
|
|
160
177
|
.emotion-0 {
|
|
@@ -207,6 +224,20 @@ exports[`NestedCollection should render connected component 1`] = `
|
|
|
207
224
|
margin-right: 4px;
|
|
208
225
|
}
|
|
209
226
|
|
|
227
|
+
.emotion-6 {
|
|
228
|
+
position: relative;
|
|
229
|
+
top: 2px;
|
|
230
|
+
color: #fff;
|
|
231
|
+
width: 0;
|
|
232
|
+
height: 0;
|
|
233
|
+
border: 5px solid transparent;
|
|
234
|
+
border-radius: 2px;
|
|
235
|
+
border-left: 6px solid currentColor;
|
|
236
|
+
border-right: 0;
|
|
237
|
+
color: currentColor;
|
|
238
|
+
left: 2px;
|
|
239
|
+
}
|
|
240
|
+
|
|
210
241
|
<a
|
|
211
242
|
class="emotion-0 emotion-1"
|
|
212
243
|
data-testid="/b"
|
|
@@ -224,6 +255,9 @@ exports[`NestedCollection should render connected component 1`] = `
|
|
|
224
255
|
>
|
|
225
256
|
File 2
|
|
226
257
|
</div>
|
|
258
|
+
<div
|
|
259
|
+
class="emotion-6 emotion-7"
|
|
260
|
+
/>
|
|
227
261
|
</div>
|
|
228
262
|
</a>
|
|
229
263
|
</DocumentFragment>
|
|
@@ -367,6 +401,20 @@ exports[`NestedCollection should render correctly with nested entries 1`] = `
|
|
|
367
401
|
margin-right: 4px;
|
|
368
402
|
}
|
|
369
403
|
|
|
404
|
+
.emotion-6 {
|
|
405
|
+
position: relative;
|
|
406
|
+
top: 2px;
|
|
407
|
+
color: #fff;
|
|
408
|
+
width: 0;
|
|
409
|
+
height: 0;
|
|
410
|
+
border: 5px solid transparent;
|
|
411
|
+
border-radius: 2px;
|
|
412
|
+
border-left: 6px solid currentColor;
|
|
413
|
+
border-right: 0;
|
|
414
|
+
color: currentColor;
|
|
415
|
+
left: 2px;
|
|
416
|
+
}
|
|
417
|
+
|
|
370
418
|
<a
|
|
371
419
|
class="emotion-0 emotion-1"
|
|
372
420
|
data-testid="/a"
|
|
@@ -384,6 +432,9 @@ exports[`NestedCollection should render correctly with nested entries 1`] = `
|
|
|
384
432
|
>
|
|
385
433
|
File 1
|
|
386
434
|
</div>
|
|
435
|
+
<div
|
|
436
|
+
class="emotion-6 emotion-7"
|
|
437
|
+
/>
|
|
387
438
|
</div>
|
|
388
439
|
</a>
|
|
389
440
|
.emotion-0 {
|
|
@@ -436,6 +487,20 @@ exports[`NestedCollection should render correctly with nested entries 1`] = `
|
|
|
436
487
|
margin-right: 4px;
|
|
437
488
|
}
|
|
438
489
|
|
|
490
|
+
.emotion-6 {
|
|
491
|
+
position: relative;
|
|
492
|
+
top: 2px;
|
|
493
|
+
color: #fff;
|
|
494
|
+
width: 0;
|
|
495
|
+
height: 0;
|
|
496
|
+
border: 5px solid transparent;
|
|
497
|
+
border-radius: 2px;
|
|
498
|
+
border-left: 6px solid currentColor;
|
|
499
|
+
border-right: 0;
|
|
500
|
+
color: currentColor;
|
|
501
|
+
left: 2px;
|
|
502
|
+
}
|
|
503
|
+
|
|
439
504
|
<a
|
|
440
505
|
class="emotion-0 emotion-1"
|
|
441
506
|
data-testid="/b"
|
|
@@ -453,6 +518,9 @@ exports[`NestedCollection should render correctly with nested entries 1`] = `
|
|
|
453
518
|
>
|
|
454
519
|
File 2
|
|
455
520
|
</div>
|
|
521
|
+
<div
|
|
522
|
+
class="emotion-6 emotion-7"
|
|
523
|
+
/>
|
|
456
524
|
</div>
|
|
457
525
|
</a>
|
|
458
526
|
</DocumentFragment>
|
|
@@ -139,7 +139,6 @@ class EditorControl extends React.Component {
|
|
|
139
139
|
removeInsertedMedia: PropTypes.func.isRequired,
|
|
140
140
|
persistMedia: PropTypes.func.isRequired,
|
|
141
141
|
onValidate: PropTypes.func,
|
|
142
|
-
processControlRef: PropTypes.func,
|
|
143
142
|
controlRef: PropTypes.func,
|
|
144
143
|
query: PropTypes.func.isRequired,
|
|
145
144
|
queryHits: PropTypes.object,
|
|
@@ -201,7 +200,6 @@ class EditorControl extends React.Component {
|
|
|
201
200
|
removeInsertedMedia,
|
|
202
201
|
persistMedia,
|
|
203
202
|
onValidate,
|
|
204
|
-
processControlRef,
|
|
205
203
|
controlRef,
|
|
206
204
|
query,
|
|
207
205
|
queryHits,
|
|
@@ -329,7 +327,6 @@ class EditorControl extends React.Component {
|
|
|
329
327
|
resolveWidget={resolveWidget}
|
|
330
328
|
widget={widget}
|
|
331
329
|
getEditorComponents={getEditorComponents}
|
|
332
|
-
ref={processControlRef && partial(processControlRef, field)}
|
|
333
330
|
controlRef={controlRef}
|
|
334
331
|
editorControl={ConnectedEditorControl}
|
|
335
332
|
query={query}
|
|
@@ -99,15 +99,17 @@ export default class ControlPane extends React.Component {
|
|
|
99
99
|
selectedLocale: this.props.locale,
|
|
100
100
|
};
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
childRefs = {};
|
|
103
103
|
|
|
104
|
-
controlRef(field, wrappedControl) {
|
|
104
|
+
controlRef = (field, wrappedControl) => {
|
|
105
105
|
if (!wrappedControl) return;
|
|
106
106
|
const name = field.get('name');
|
|
107
|
+
this.childRefs[name] = wrappedControl;
|
|
108
|
+
};
|
|
107
109
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
110
|
+
getControlRef = field => wrappedControl => {
|
|
111
|
+
this.controlRef(field, wrappedControl);
|
|
112
|
+
};
|
|
111
113
|
|
|
112
114
|
handleLocaleChange = val => {
|
|
113
115
|
this.setState({ selectedLocale: val });
|
|
@@ -152,7 +154,11 @@ export default class ControlPane extends React.Component {
|
|
|
152
154
|
validate = async () => {
|
|
153
155
|
this.props.fields.forEach(field => {
|
|
154
156
|
if (field.get('widget') === 'hidden') return;
|
|
155
|
-
this.
|
|
157
|
+
const control = this.childRefs[field.get('name')];
|
|
158
|
+
const validateFn = control?.innerWrappedControl?.validate ?? control?.validate;
|
|
159
|
+
if (validateFn) {
|
|
160
|
+
validateFn();
|
|
161
|
+
}
|
|
156
162
|
});
|
|
157
163
|
};
|
|
158
164
|
|
|
@@ -165,6 +171,14 @@ export default class ControlPane extends React.Component {
|
|
|
165
171
|
}
|
|
166
172
|
};
|
|
167
173
|
|
|
174
|
+
focus(path) {
|
|
175
|
+
const [fieldName, ...remainingPath] = path.split('.');
|
|
176
|
+
const control = this.childRefs[fieldName];
|
|
177
|
+
if (control?.focus) {
|
|
178
|
+
control.focus(remainingPath.join('.'));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
168
182
|
render() {
|
|
169
183
|
const { collection, entry, fields, fieldsMetaData, fieldsErrors, onChange, onValidate, t } =
|
|
170
184
|
this.props;
|
|
@@ -227,8 +241,7 @@ export default class ControlPane extends React.Component {
|
|
|
227
241
|
onChange(field, newValue, newMetadata, i18n);
|
|
228
242
|
}}
|
|
229
243
|
onValidate={onValidate}
|
|
230
|
-
|
|
231
|
-
controlRef={this.controlRef}
|
|
244
|
+
controlRef={this.getControlRef(field)}
|
|
232
245
|
entry={entry}
|
|
233
246
|
collection={collection}
|
|
234
247
|
isDisabled={isDuplicate}
|
|
@@ -44,6 +44,7 @@ export default class Widget extends Component {
|
|
|
44
44
|
fieldsErrors: ImmutablePropTypes.map,
|
|
45
45
|
onChange: PropTypes.func.isRequired,
|
|
46
46
|
onValidate: PropTypes.func,
|
|
47
|
+
controlRef: PropTypes.func,
|
|
47
48
|
onOpenMediaLibrary: PropTypes.func.isRequired,
|
|
48
49
|
onClearMediaControl: PropTypes.func.isRequired,
|
|
49
50
|
onRemoveMediaControl: PropTypes.func.isRequired,
|
|
@@ -55,7 +56,6 @@ export default class Widget extends Component {
|
|
|
55
56
|
widget: PropTypes.object.isRequired,
|
|
56
57
|
getEditorComponents: PropTypes.func.isRequired,
|
|
57
58
|
isFetching: PropTypes.bool,
|
|
58
|
-
controlRef: PropTypes.func,
|
|
59
59
|
query: PropTypes.func.isRequired,
|
|
60
60
|
clearSearch: PropTypes.func.isRequired,
|
|
61
61
|
clearFieldErrors: PropTypes.func.isRequired,
|
|
@@ -112,8 +112,29 @@ export default class Widget extends Component {
|
|
|
112
112
|
*/
|
|
113
113
|
const { shouldComponentUpdate: scu } = this.innerWrappedControl;
|
|
114
114
|
this.wrappedControlShouldComponentUpdate = scu && scu.bind(this.innerWrappedControl);
|
|
115
|
+
|
|
116
|
+
// Call the control ref if provided, passing this Widget instance
|
|
117
|
+
if (this.props.controlRef) {
|
|
118
|
+
this.props.controlRef(this);
|
|
119
|
+
}
|
|
115
120
|
};
|
|
116
121
|
|
|
122
|
+
focus(path) {
|
|
123
|
+
// Try widget's custom focus method first
|
|
124
|
+
if (this.innerWrappedControl?.focus) {
|
|
125
|
+
this.innerWrappedControl.focus(path);
|
|
126
|
+
} else {
|
|
127
|
+
// Fall back to focusing by ID for simple widgets
|
|
128
|
+
const element = document.getElementById(this.props.uniqueFieldId);
|
|
129
|
+
element?.focus();
|
|
130
|
+
}
|
|
131
|
+
// After focusing, ensure the element is visible
|
|
132
|
+
const label = document.querySelector(`label[for="${this.props.uniqueFieldId}"]`);
|
|
133
|
+
if (label) {
|
|
134
|
+
label.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
117
138
|
getValidateValue = () => {
|
|
118
139
|
let value = this.innerWrappedControl?.getValidateValue?.() || this.props.value;
|
|
119
140
|
// Convert list input widget value to string for validation test
|
|
@@ -162,6 +162,10 @@ class EditorInterface extends Component {
|
|
|
162
162
|
i18nVisible: localStorage.getItem(I18N_VISIBLE) !== 'false',
|
|
163
163
|
};
|
|
164
164
|
|
|
165
|
+
handleFieldClick = path => {
|
|
166
|
+
this.controlPaneRef?.focus(path);
|
|
167
|
+
};
|
|
168
|
+
|
|
165
169
|
handleSplitPaneDragStart = () => {
|
|
166
170
|
this.setState({ showEventBlocker: true });
|
|
167
171
|
};
|
|
@@ -298,6 +302,7 @@ class EditorInterface extends Component {
|
|
|
298
302
|
fields={fields}
|
|
299
303
|
fieldsMetaData={fieldsMetaData}
|
|
300
304
|
locale={leftPanelLocale}
|
|
305
|
+
onFieldClick={this.handleFieldClick}
|
|
301
306
|
/>
|
|
302
307
|
</PreviewPaneContainer>
|
|
303
308
|
</StyledSplitPane>
|
|
@@ -381,7 +386,7 @@ class EditorInterface extends Component {
|
|
|
381
386
|
title={t('editor.editorInterface.togglePreview')}
|
|
382
387
|
/>
|
|
383
388
|
)}
|
|
384
|
-
{scrollSyncVisible && (
|
|
389
|
+
{scrollSyncVisible && !collection.getIn(['editor', 'visualEditing']) && (
|
|
385
390
|
<EditorToggle
|
|
386
391
|
isActive={scrollSyncEnabled}
|
|
387
392
|
onClick={this.handleToggleScrollSync}
|
|
@@ -3,24 +3,63 @@ import React from 'react';
|
|
|
3
3
|
import { isElement } from 'react-is';
|
|
4
4
|
import { ScrollSyncPane } from 'react-scroll-sync';
|
|
5
5
|
import { FrameContextConsumer } from 'react-frame-component';
|
|
6
|
+
import { vercelStegaDecode } from '@vercel/stega';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* body.
|
|
9
|
+
* PreviewContent renders the preview component and optionally handles visual editing interactions.
|
|
10
|
+
* By default it uses scroll sync, but can be configured to use visual editing instead.
|
|
11
11
|
*/
|
|
12
12
|
class PreviewContent extends React.Component {
|
|
13
|
-
|
|
13
|
+
handleClick = e => {
|
|
14
|
+
const { previewProps, onFieldClick } = this.props;
|
|
15
|
+
const visualEditing = previewProps?.collection?.getIn(['editor', 'visualEditing'], false);
|
|
16
|
+
|
|
17
|
+
if (!visualEditing) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const text = e.target.textContent;
|
|
23
|
+
const decoded = vercelStegaDecode(text);
|
|
24
|
+
if (decoded?.decap) {
|
|
25
|
+
if (onFieldClick) {
|
|
26
|
+
onFieldClick(decoded.decap);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
} catch (err) {
|
|
30
|
+
console.log('Visual editing error:', err);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
renderPreview() {
|
|
14
35
|
const { previewComponent, previewProps } = this.props;
|
|
36
|
+
return (
|
|
37
|
+
<div onClick={this.handleClick}>
|
|
38
|
+
{isElement(previewComponent)
|
|
39
|
+
? React.cloneElement(previewComponent, previewProps)
|
|
40
|
+
: React.createElement(previewComponent, previewProps)}
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
render() {
|
|
46
|
+
const { previewProps } = this.props;
|
|
47
|
+
const visualEditing = previewProps?.collection?.getIn(['editor', 'visualEditing'], false);
|
|
48
|
+
const showScrollSync = !visualEditing;
|
|
49
|
+
|
|
15
50
|
return (
|
|
16
51
|
<FrameContextConsumer>
|
|
17
|
-
{context =>
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
52
|
+
{context => {
|
|
53
|
+
const preview = this.renderPreview();
|
|
54
|
+
if (showScrollSync) {
|
|
55
|
+
return (
|
|
56
|
+
<ScrollSyncPane attachTo={context.document.scrollingElement}>
|
|
57
|
+
{preview}
|
|
58
|
+
</ScrollSyncPane>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
return preview;
|
|
62
|
+
}}
|
|
24
63
|
</FrameContextConsumer>
|
|
25
64
|
);
|
|
26
65
|
}
|
|
@@ -29,6 +68,7 @@ class PreviewContent extends React.Component {
|
|
|
29
68
|
PreviewContent.propTypes = {
|
|
30
69
|
previewComponent: PropTypes.func.isRequired,
|
|
31
70
|
previewProps: PropTypes.object,
|
|
71
|
+
onFieldClick: PropTypes.func,
|
|
32
72
|
};
|
|
33
73
|
|
|
34
74
|
export default PreviewContent;
|
|
@@ -6,6 +6,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
|
6
6
|
import Frame, { FrameContextConsumer } from 'react-frame-component';
|
|
7
7
|
import { lengths } from 'decap-cms-ui-default';
|
|
8
8
|
import { connect } from 'react-redux';
|
|
9
|
+
import { encodeEntry } from 'decap-cms-lib-util/src/stega';
|
|
9
10
|
|
|
10
11
|
import {
|
|
11
12
|
resolveWidget,
|
|
@@ -92,6 +93,7 @@ export class PreviewPane extends React.Component {
|
|
|
92
93
|
if (field.get('meta')) {
|
|
93
94
|
value = this.props.entry.getIn(['meta', field.get('name')]);
|
|
94
95
|
}
|
|
96
|
+
|
|
95
97
|
const nestedFields = field.get('fields');
|
|
96
98
|
const singleField = field.get('field');
|
|
97
99
|
const metadata = fieldsMetaData && fieldsMetaData.get(field.get('name'), Map());
|
|
@@ -167,9 +169,28 @@ export class PreviewPane extends React.Component {
|
|
|
167
169
|
const { fields, entry, fieldsMetaData } = this.props;
|
|
168
170
|
const field = fields.find(f => f.get('name') === name);
|
|
169
171
|
const nestedFields = field && field.get('fields');
|
|
172
|
+
const variableTypes = field && field.get('types');
|
|
170
173
|
const value = entry.getIn(['data', field.get('name')]);
|
|
171
174
|
const metadata = fieldsMetaData.get(field.get('name'), Map());
|
|
172
175
|
|
|
176
|
+
// Variable Type lists
|
|
177
|
+
if (List.isList(value) && variableTypes) {
|
|
178
|
+
return value.map(val => {
|
|
179
|
+
const valueType = variableTypes.find(t => t.get('name') === val.get('type'));
|
|
180
|
+
const typeFields = valueType && valueType.get('fields');
|
|
181
|
+
const widgets =
|
|
182
|
+
typeFields &&
|
|
183
|
+
Map(
|
|
184
|
+
typeFields.map((f, i) => [
|
|
185
|
+
f.get('name'),
|
|
186
|
+
<div key={i}>{this.getWidget(f, val, metadata.get(f.get('name')), this.props)}</div>,
|
|
187
|
+
]),
|
|
188
|
+
);
|
|
189
|
+
return Map({ data: val, widgets });
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// List widgets
|
|
173
194
|
if (List.isList(value)) {
|
|
174
195
|
return value.map(val => {
|
|
175
196
|
const widgets =
|
|
@@ -226,9 +247,18 @@ export class PreviewPane extends React.Component {
|
|
|
226
247
|
|
|
227
248
|
this.inferFields();
|
|
228
249
|
|
|
250
|
+
const visualEditing = collection.getIn(['editor', 'visualEditing'], false);
|
|
251
|
+
|
|
252
|
+
// Only encode entry data if visual editing is enabled
|
|
253
|
+
const previewEntry = visualEditing
|
|
254
|
+
? entry.set('data', encodeEntry(entry.get('data'), this.props.fields))
|
|
255
|
+
: entry;
|
|
256
|
+
|
|
229
257
|
const previewProps = {
|
|
230
258
|
...this.props,
|
|
231
|
-
|
|
259
|
+
entry: previewEntry,
|
|
260
|
+
widgetFor: (name, fields, values = previewEntry.get('data'), fieldsMetaData) =>
|
|
261
|
+
this.widgetFor(name, fields, values, fieldsMetaData),
|
|
232
262
|
widgetsFor: this.widgetsFor,
|
|
233
263
|
getCollection: this.getCollection,
|
|
234
264
|
};
|
|
@@ -260,6 +290,7 @@ export class PreviewPane extends React.Component {
|
|
|
260
290
|
return (
|
|
261
291
|
<EditorPreviewContent
|
|
262
292
|
{...{ previewComponent, previewProps: { ...previewProps, document, window } }}
|
|
293
|
+
onFieldClick={this.props.onFieldClick}
|
|
263
294
|
/>
|
|
264
295
|
);
|
|
265
296
|
}}
|
|
@@ -276,6 +307,7 @@ PreviewPane.propTypes = {
|
|
|
276
307
|
entry: ImmutablePropTypes.map.isRequired,
|
|
277
308
|
fieldsMetaData: ImmutablePropTypes.map.isRequired,
|
|
278
309
|
getAsset: PropTypes.func.isRequired,
|
|
310
|
+
onFieldClick: PropTypes.func,
|
|
279
311
|
};
|
|
280
312
|
|
|
281
313
|
function mapStateToProps(state) {
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.SET_EDITOR_CONTROL = void 0;
|
|
7
|
-
exports.setEditorControl = setEditorControl;
|
|
8
|
-
const SET_EDITOR_CONTROL = exports.SET_EDITOR_CONTROL = 'SET_EDITOR_CONTROL';
|
|
9
|
-
function setEditorControl(control) {
|
|
10
|
-
return {
|
|
11
|
-
type: SET_EDITOR_CONTROL,
|
|
12
|
-
payload: control
|
|
13
|
-
};
|
|
14
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.default = void 0;
|
|
7
|
-
var _editorControl = require("../actions/editorControl");
|
|
8
|
-
const initialState = null;
|
|
9
|
-
function editorControl(state = initialState, action) {
|
|
10
|
-
switch (action.type) {
|
|
11
|
-
case _editorControl.SET_EDITOR_CONTROL:
|
|
12
|
-
return action.payload;
|
|
13
|
-
default:
|
|
14
|
-
return state;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
var _default = exports.default = editorControl;
|