decap-cms-core 3.5.0 → 3.6.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 +10 -10
- 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 +60 -63
- 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 +56 -64
- 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 +23 -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/index.d.ts +1 -0
- package/package.json +3 -2
- package/src/components/Collection/Entries/EntriesCollection.js +21 -10
- package/src/components/Collection/Entries/__tests__/EntriesCollection.spec.js +7 -7
- package/src/components/Collection/Entries/__tests__/__snapshots__/EntriesCollection.spec.js.snap +9 -9
- package/src/components/Collection/NestedCollection.js +11 -2
- package/src/components/Collection/__tests__/NestedCollection.spec.js +3 -0
- 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/src/constants/configSchema.js +1 -0
- package/src/types/redux.ts +1 -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/index.d.ts
CHANGED
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.1",
|
|
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": "d10b9848a31ae3a7b59c3f403d839d062b1f876f"
|
|
100
101
|
}
|
|
@@ -117,22 +117,28 @@ export class EntriesCollection extends React.Component {
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
export function filterNestedEntries(path, collectionFolder, entries) {
|
|
120
|
+
export function filterNestedEntries(path, collectionFolder, entries, subfolders) {
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
130
|
+
entryPath = entryPath.slice(path.length + 1);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// if subfolders legacy mode is enabled, show only immediate subfolders
|
|
134
|
+
// also show index file in root folder
|
|
135
|
+
if (subfolders) {
|
|
136
|
+
const depth = entryPath.split('/').length;
|
|
137
|
+
return path ? depth === 2 : depth <= 2;
|
|
135
138
|
}
|
|
139
|
+
|
|
140
|
+
// only show immediate children
|
|
141
|
+
return !entryPath.includes('/');
|
|
136
142
|
});
|
|
137
143
|
return filtered;
|
|
138
144
|
}
|
|
@@ -146,7 +152,12 @@ function mapStateToProps(state, ownProps) {
|
|
|
146
152
|
|
|
147
153
|
if (collection.has('nested')) {
|
|
148
154
|
const collectionFolder = collection.get('folder');
|
|
149
|
-
entries = filterNestedEntries(
|
|
155
|
+
entries = filterNestedEntries(
|
|
156
|
+
filterTerm || '',
|
|
157
|
+
collectionFolder,
|
|
158
|
+
entries,
|
|
159
|
+
collection.get('nested').get('subfolders') !== false,
|
|
160
|
+
);
|
|
150
161
|
}
|
|
151
162
|
const entriesLoaded = selectEntriesLoaded(state.entries, collection.get('name'));
|
|
152
163
|
const isFetching = selectIsFetching(state.entries, collection.get('name'));
|
|
@@ -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
|
});
|
|
@@ -117,7 +115,9 @@ describe('EntriesCollection', () => {
|
|
|
117
115
|
});
|
|
118
116
|
|
|
119
117
|
const { asFragment } = renderWithRedux(
|
|
120
|
-
<ConnectedEntriesCollection
|
|
118
|
+
<ConnectedEntriesCollection
|
|
119
|
+
collection={collection.set('nested', fromJS({ depth: 10, subfolders: false }))}
|
|
120
|
+
/>,
|
|
121
121
|
{
|
|
122
122
|
store,
|
|
123
123
|
},
|
|
@@ -126,7 +126,7 @@ describe('EntriesCollection', () => {
|
|
|
126
126
|
expect(asFragment()).toMatchSnapshot();
|
|
127
127
|
});
|
|
128
128
|
|
|
129
|
-
it('should render
|
|
129
|
+
it('should render with applied filter term for nested collections', () => {
|
|
130
130
|
const entriesArray = [
|
|
131
131
|
{ slug: 'index', path: 'src/pages/index.md', data: { title: 'Root' } },
|
|
132
132
|
{ slug: 'dir1/index', path: 'src/pages/dir1/index.md', data: { title: 'File 1' } },
|
|
@@ -142,7 +142,7 @@ describe('EntriesCollection', () => {
|
|
|
142
142
|
|
|
143
143
|
const { asFragment } = renderWithRedux(
|
|
144
144
|
<ConnectedEntriesCollection
|
|
145
|
-
collection={collection.set('nested', fromJS({ depth: 10 }))}
|
|
145
|
+
collection={collection.set('nested', fromJS({ depth: 10, subfolders: false }))}
|
|
146
146
|
filterTerm="dir3/dir4"
|
|
147
147
|
/>,
|
|
148
148
|
{
|
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, \\"subfolders\\": false } }"
|
|
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
|
-
collections="Map { \\"name\\": \\"pages\\", \\"label\\": \\"Pages\\", \\"folder\\": \\"src/pages\\", \\"nested\\": Map { \\"depth\\": 10 } }"
|
|
31
|
+
collections="Map { \\"name\\": \\"pages\\", \\"label\\": \\"Pages\\", \\"folder\\": \\"src/pages\\", \\"nested\\": Map { \\"depth\\": 10, \\"subfolders\\": false } }"
|
|
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>
|
|
@@ -79,8 +79,13 @@ function TreeNode(props) {
|
|
|
79
79
|
const collectionName = collection.get('name');
|
|
80
80
|
|
|
81
81
|
const sortedData = sortBy(treeData, getNodeTitle);
|
|
82
|
+
const subfolders = collection.get('nested')?.get('subfolders') !== false;
|
|
82
83
|
return sortedData.map(node => {
|
|
83
|
-
const leaf =
|
|
84
|
+
const leaf =
|
|
85
|
+
depth > 0 &&
|
|
86
|
+
(subfolders
|
|
87
|
+
? node.children.length <= 1 && !node.children[0]?.isDir
|
|
88
|
+
: node.children.length === 0);
|
|
84
89
|
if (leaf) {
|
|
85
90
|
return null;
|
|
86
91
|
}
|
|
@@ -90,7 +95,11 @@ function TreeNode(props) {
|
|
|
90
95
|
}
|
|
91
96
|
const title = getNodeTitle(node);
|
|
92
97
|
|
|
93
|
-
const hasChildren =
|
|
98
|
+
const hasChildren =
|
|
99
|
+
depth === 0 ||
|
|
100
|
+
(subfolders
|
|
101
|
+
? node.children.some(c => c.children.some(c => c.isDir))
|
|
102
|
+
: node.children.some(c => c.isDir));
|
|
94
103
|
|
|
95
104
|
return (
|
|
96
105
|
<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;
|