@wordpress/core-data 6.18.0 → 6.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/README.md +13 -1
- package/build/actions.js +34 -30
- package/build/actions.js.map +1 -1
- package/build/entity-provider.js +2 -99
- package/build/entity-provider.js.map +1 -1
- package/build/entity-types/helpers.js.map +1 -1
- package/build/footnotes/get-footnotes-order.js +35 -0
- package/build/footnotes/get-footnotes-order.js.map +1 -0
- package/build/footnotes/get-rich-text-values-cached.js +39 -0
- package/build/footnotes/get-rich-text-values-cached.js.map +1 -0
- package/build/footnotes/index.js +96 -0
- package/build/footnotes/index.js.map +1 -0
- package/build/hooks/use-entity-record.js +6 -3
- package/build/hooks/use-entity-record.js.map +1 -1
- package/build/hooks/use-resource-permissions.js.map +1 -1
- package/build/private-selectors.js +4 -17
- package/build/private-selectors.js.map +1 -1
- package/build/reducer.js +33 -145
- package/build/reducer.js.map +1 -1
- package/build/resolvers.js +20 -1
- package/build/resolvers.js.map +1 -1
- package/build/selectors.js +21 -25
- package/build/selectors.js.map +1 -1
- package/build/utils/get-nested-value.js +30 -0
- package/build/utils/get-nested-value.js.map +1 -0
- package/build/utils/index.js +7 -0
- package/build/utils/index.js.map +1 -1
- package/build/utils/set-nested-value.js +11 -6
- package/build/utils/set-nested-value.js.map +1 -1
- package/build-module/actions.js +32 -28
- package/build-module/actions.js.map +1 -1
- package/build-module/entity-provider.js +2 -99
- package/build-module/entity-provider.js.map +1 -1
- package/build-module/entity-types/helpers.js.map +1 -1
- package/build-module/footnotes/get-footnotes-order.js +27 -0
- package/build-module/footnotes/get-footnotes-order.js.map +1 -0
- package/build-module/footnotes/get-rich-text-values-cached.js +33 -0
- package/build-module/footnotes/get-rich-text-values-cached.js.map +1 -0
- package/build-module/footnotes/index.js +88 -0
- package/build-module/footnotes/index.js.map +1 -0
- package/build-module/hooks/use-entity-record.js +6 -3
- package/build-module/hooks/use-entity-record.js.map +1 -1
- package/build-module/hooks/use-resource-permissions.js.map +1 -1
- package/build-module/private-selectors.js +3 -15
- package/build-module/private-selectors.js.map +1 -1
- package/build-module/reducer.js +30 -144
- package/build-module/reducer.js.map +1 -1
- package/build-module/resolvers.js +18 -0
- package/build-module/resolvers.js.map +1 -1
- package/build-module/selectors.js +19 -29
- package/build-module/selectors.js.map +1 -1
- package/build-module/utils/get-nested-value.js +24 -0
- package/build-module/utils/get-nested-value.js.map +1 -0
- package/build-module/utils/index.js +1 -0
- package/build-module/utils/index.js.map +1 -1
- package/build-module/utils/set-nested-value.js +11 -6
- package/build-module/utils/set-nested-value.js.map +1 -1
- package/build-types/actions.d.ts +1 -6
- package/build-types/actions.d.ts.map +1 -1
- package/build-types/entity-provider.d.ts.map +1 -1
- package/build-types/footnotes/get-footnotes-order.d.ts +2 -0
- package/build-types/footnotes/get-footnotes-order.d.ts.map +1 -0
- package/build-types/footnotes/get-rich-text-values-cached.d.ts +2 -0
- package/build-types/footnotes/get-rich-text-values-cached.d.ts.map +1 -0
- package/build-types/footnotes/index.d.ts +4 -0
- package/build-types/footnotes/index.d.ts.map +1 -0
- package/build-types/hooks/use-entity-record.d.ts +2 -0
- package/build-types/hooks/use-entity-record.d.ts.map +1 -1
- package/build-types/index.d.ts +3 -2
- package/build-types/index.d.ts.map +1 -1
- package/build-types/private-selectors.d.ts +3 -13
- package/build-types/private-selectors.d.ts.map +1 -1
- package/build-types/reducer.d.ts +7 -23
- package/build-types/reducer.d.ts.map +1 -1
- package/build-types/resolvers.d.ts +4 -0
- package/build-types/resolvers.d.ts.map +1 -1
- package/build-types/selectors.d.ts +19 -14
- package/build-types/selectors.d.ts.map +1 -1
- package/build-types/utils/get-nested-value.d.ts +14 -0
- package/build-types/utils/get-nested-value.d.ts.map +1 -0
- package/build-types/utils/index.d.ts +1 -0
- package/build-types/utils/set-nested-value.d.ts +8 -4
- package/build-types/utils/set-nested-value.d.ts.map +1 -1
- package/package.json +16 -15
- package/src/actions.js +36 -26
- package/src/entity-provider.js +2 -134
- package/src/entity-types/helpers.ts +2 -2
- package/src/footnotes/get-footnotes-order.js +30 -0
- package/src/footnotes/get-rich-text-values-cached.js +35 -0
- package/src/footnotes/index.js +119 -0
- package/src/hooks/test/use-entity-record.js +4 -0
- package/src/hooks/use-entity-record.ts +12 -3
- package/src/hooks/use-resource-permissions.ts +1 -1
- package/src/private-selectors.ts +4 -17
- package/src/reducer.js +36 -155
- package/src/resolvers.js +25 -0
- package/src/selectors.ts +39 -51
- package/src/test/reducer.js +0 -233
- package/src/test/selectors.js +0 -54
- package/src/utils/get-nested-value.js +27 -0
- package/src/utils/index.js +1 -0
- package/src/utils/set-nested-value.js +12 -6
- package/src/utils/test/get-nested-value.js +61 -0
- package/src/utils/test/set-nested-value.js +7 -0
- package/tsconfig.json +1 -0
- package/tsconfig.tsbuildinfo +1 -1
package/src/actions.js
CHANGED
|
@@ -14,6 +14,7 @@ import deprecated from '@wordpress/deprecated';
|
|
|
14
14
|
/**
|
|
15
15
|
* Internal dependencies
|
|
16
16
|
*/
|
|
17
|
+
import { getNestedValue, setNestedValue } from './utils';
|
|
17
18
|
import { receiveItems, removeItems, receiveQueriedItems } from './queried-data';
|
|
18
19
|
import { getOrLoadEntitiesConfig, DEFAULT_ENTITY_KEY } from './entities';
|
|
19
20
|
import { createBatch } from './batch';
|
|
@@ -391,20 +392,29 @@ export const editEntityRecord =
|
|
|
391
392
|
edit.edits
|
|
392
393
|
);
|
|
393
394
|
} else {
|
|
395
|
+
if ( ! options.undoIgnore ) {
|
|
396
|
+
select.getUndoManager().addRecord(
|
|
397
|
+
[
|
|
398
|
+
{
|
|
399
|
+
id: { kind, name, recordId },
|
|
400
|
+
changes: Object.keys( edits ).reduce(
|
|
401
|
+
( acc, key ) => {
|
|
402
|
+
acc[ key ] = {
|
|
403
|
+
from: editedRecord[ key ],
|
|
404
|
+
to: edits[ key ],
|
|
405
|
+
};
|
|
406
|
+
return acc;
|
|
407
|
+
},
|
|
408
|
+
{}
|
|
409
|
+
),
|
|
410
|
+
},
|
|
411
|
+
],
|
|
412
|
+
options.isCached
|
|
413
|
+
);
|
|
414
|
+
}
|
|
394
415
|
dispatch( {
|
|
395
416
|
type: 'EDIT_ENTITY_RECORD',
|
|
396
417
|
...edit,
|
|
397
|
-
meta: {
|
|
398
|
-
undo: ! options.undoIgnore && {
|
|
399
|
-
...edit,
|
|
400
|
-
// Send the current values for things like the first undo stack entry.
|
|
401
|
-
edits: Object.keys( edits ).reduce( ( acc, key ) => {
|
|
402
|
-
acc[ key ] = editedRecord[ key ];
|
|
403
|
-
return acc;
|
|
404
|
-
}, {} ),
|
|
405
|
-
isCached: options.isCached,
|
|
406
|
-
},
|
|
407
|
-
},
|
|
408
418
|
} );
|
|
409
419
|
}
|
|
410
420
|
};
|
|
@@ -416,13 +426,13 @@ export const editEntityRecord =
|
|
|
416
426
|
export const undo =
|
|
417
427
|
() =>
|
|
418
428
|
( { select, dispatch } ) => {
|
|
419
|
-
const
|
|
420
|
-
if ( !
|
|
429
|
+
const undoRecord = select.getUndoManager().undo();
|
|
430
|
+
if ( ! undoRecord ) {
|
|
421
431
|
return;
|
|
422
432
|
}
|
|
423
433
|
dispatch( {
|
|
424
434
|
type: 'UNDO',
|
|
425
|
-
|
|
435
|
+
record: undoRecord,
|
|
426
436
|
} );
|
|
427
437
|
};
|
|
428
438
|
|
|
@@ -433,13 +443,13 @@ export const undo =
|
|
|
433
443
|
export const redo =
|
|
434
444
|
() =>
|
|
435
445
|
( { select, dispatch } ) => {
|
|
436
|
-
const
|
|
437
|
-
if ( !
|
|
446
|
+
const redoRecord = select.getUndoManager().redo();
|
|
447
|
+
if ( ! redoRecord ) {
|
|
438
448
|
return;
|
|
439
449
|
}
|
|
440
450
|
dispatch( {
|
|
441
451
|
type: 'REDO',
|
|
442
|
-
|
|
452
|
+
record: redoRecord,
|
|
443
453
|
} );
|
|
444
454
|
};
|
|
445
455
|
|
|
@@ -448,9 +458,11 @@ export const redo =
|
|
|
448
458
|
*
|
|
449
459
|
* @return {Object} Action object.
|
|
450
460
|
*/
|
|
451
|
-
export
|
|
452
|
-
|
|
453
|
-
}
|
|
461
|
+
export const __unstableCreateUndoLevel =
|
|
462
|
+
() =>
|
|
463
|
+
( { select } ) => {
|
|
464
|
+
select.getUndoManager().addRecord();
|
|
465
|
+
};
|
|
454
466
|
|
|
455
467
|
/**
|
|
456
468
|
* Action triggered to save an entity record.
|
|
@@ -779,7 +791,7 @@ export const saveEditedEntityRecord =
|
|
|
779
791
|
* @param {string} kind Kind of the entity.
|
|
780
792
|
* @param {string} name Name of the entity.
|
|
781
793
|
* @param {Object} recordId ID of the record.
|
|
782
|
-
* @param {Array} itemsToSave List of entity properties to save.
|
|
794
|
+
* @param {Array} itemsToSave List of entity properties or property paths to save.
|
|
783
795
|
* @param {Object} options Saving options.
|
|
784
796
|
*/
|
|
785
797
|
export const __experimentalSaveSpecifiedEntityEdits =
|
|
@@ -794,10 +806,9 @@ export const __experimentalSaveSpecifiedEntityEdits =
|
|
|
794
806
|
recordId
|
|
795
807
|
);
|
|
796
808
|
const editsToSave = {};
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
}
|
|
809
|
+
|
|
810
|
+
for ( const item of itemsToSave ) {
|
|
811
|
+
setNestedValue( editsToSave, item, getNestedValue( edits, item ) );
|
|
801
812
|
}
|
|
802
813
|
|
|
803
814
|
const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
|
|
@@ -814,7 +825,6 @@ export const __experimentalSaveSpecifiedEntityEdits =
|
|
|
814
825
|
if ( recordId ) {
|
|
815
826
|
editsToSave[ entityIdKey ] = recordId;
|
|
816
827
|
}
|
|
817
|
-
|
|
818
828
|
return await dispatch.saveEntityRecord(
|
|
819
829
|
kind,
|
|
820
830
|
name,
|
package/src/entity-provider.js
CHANGED
|
@@ -9,20 +9,17 @@ import {
|
|
|
9
9
|
} from '@wordpress/element';
|
|
10
10
|
import { useSelect, useDispatch } from '@wordpress/data';
|
|
11
11
|
import { parse, __unstableSerializeAndClean } from '@wordpress/blocks';
|
|
12
|
-
import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor';
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* Internal dependencies
|
|
16
15
|
*/
|
|
17
16
|
import { STORE_NAME } from './name';
|
|
18
|
-
import {
|
|
17
|
+
import { updateFootnotesFromMeta } from './footnotes';
|
|
19
18
|
|
|
20
19
|
/** @typedef {import('@wordpress/blocks').WPBlock} WPBlock */
|
|
21
20
|
|
|
22
21
|
const EMPTY_ARRAY = [];
|
|
23
22
|
|
|
24
|
-
let oldFootnotes = {};
|
|
25
|
-
|
|
26
23
|
/**
|
|
27
24
|
* Internal dependencies
|
|
28
25
|
*/
|
|
@@ -182,136 +179,7 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) {
|
|
|
182
179
|
}, [ editedBlocks, content ] );
|
|
183
180
|
|
|
184
181
|
const updateFootnotes = useCallback(
|
|
185
|
-
( _blocks ) =>
|
|
186
|
-
const output = { blocks: _blocks };
|
|
187
|
-
if ( ! meta ) return output;
|
|
188
|
-
// If meta.footnotes is empty, it means the meta is not registered.
|
|
189
|
-
if ( meta.footnotes === undefined ) return output;
|
|
190
|
-
|
|
191
|
-
const { getRichTextValues } = unlock( blockEditorPrivateApis );
|
|
192
|
-
const _content = getRichTextValues( _blocks ).join( '' ) || '';
|
|
193
|
-
const newOrder = [];
|
|
194
|
-
|
|
195
|
-
// This can be avoided when
|
|
196
|
-
// https://github.com/WordPress/gutenberg/pull/43204 lands. We can then
|
|
197
|
-
// get the order directly from the rich text values.
|
|
198
|
-
if ( _content.indexOf( 'data-fn' ) !== -1 ) {
|
|
199
|
-
const regex = /data-fn="([^"]+)"/g;
|
|
200
|
-
let match;
|
|
201
|
-
while ( ( match = regex.exec( _content ) ) !== null ) {
|
|
202
|
-
newOrder.push( match[ 1 ] );
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const footnotes = meta.footnotes
|
|
207
|
-
? JSON.parse( meta.footnotes )
|
|
208
|
-
: [];
|
|
209
|
-
const currentOrder = footnotes.map( ( fn ) => fn.id );
|
|
210
|
-
|
|
211
|
-
if ( currentOrder.join( '' ) === newOrder.join( '' ) )
|
|
212
|
-
return output;
|
|
213
|
-
|
|
214
|
-
const newFootnotes = newOrder.map(
|
|
215
|
-
( fnId ) =>
|
|
216
|
-
footnotes.find( ( fn ) => fn.id === fnId ) ||
|
|
217
|
-
oldFootnotes[ fnId ] || {
|
|
218
|
-
id: fnId,
|
|
219
|
-
content: '',
|
|
220
|
-
}
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
function updateAttributes( attributes ) {
|
|
224
|
-
// Only attempt to update attributes, if attributes is an object.
|
|
225
|
-
if (
|
|
226
|
-
! attributes ||
|
|
227
|
-
Array.isArray( attributes ) ||
|
|
228
|
-
typeof attributes !== 'object'
|
|
229
|
-
) {
|
|
230
|
-
return attributes;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
attributes = { ...attributes };
|
|
234
|
-
|
|
235
|
-
for ( const key in attributes ) {
|
|
236
|
-
const value = attributes[ key ];
|
|
237
|
-
|
|
238
|
-
if ( Array.isArray( value ) ) {
|
|
239
|
-
attributes[ key ] = value.map( updateAttributes );
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if ( typeof value !== 'string' ) {
|
|
244
|
-
continue;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if ( value.indexOf( 'data-fn' ) === -1 ) {
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// When we store rich text values, this would no longer
|
|
252
|
-
// require a regex.
|
|
253
|
-
const regex =
|
|
254
|
-
/(<sup[^>]+data-fn="([^"]+)"[^>]*><a[^>]*>)[\d*]*<\/a><\/sup>/g;
|
|
255
|
-
|
|
256
|
-
attributes[ key ] = value.replace(
|
|
257
|
-
regex,
|
|
258
|
-
( match, opening, fnId ) => {
|
|
259
|
-
const index = newOrder.indexOf( fnId );
|
|
260
|
-
return `${ opening }${ index + 1 }</a></sup>`;
|
|
261
|
-
}
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
const compatRegex =
|
|
265
|
-
/<a[^>]+data-fn="([^"]+)"[^>]*>\*<\/a>/g;
|
|
266
|
-
|
|
267
|
-
attributes[ key ] = attributes[ key ].replace(
|
|
268
|
-
compatRegex,
|
|
269
|
-
( match, fnId ) => {
|
|
270
|
-
const index = newOrder.indexOf( fnId );
|
|
271
|
-
return `<sup data-fn="${ fnId }" class="fn"><a href="#${ fnId }" id="${ fnId }-link">${
|
|
272
|
-
index + 1
|
|
273
|
-
}</a></sup>`;
|
|
274
|
-
}
|
|
275
|
-
);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return attributes;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function updateBlocksAttributes( __blocks ) {
|
|
282
|
-
return __blocks.map( ( block ) => {
|
|
283
|
-
return {
|
|
284
|
-
...block,
|
|
285
|
-
attributes: updateAttributes( block.attributes ),
|
|
286
|
-
innerBlocks: updateBlocksAttributes(
|
|
287
|
-
block.innerBlocks
|
|
288
|
-
),
|
|
289
|
-
};
|
|
290
|
-
} );
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// We need to go through all block attributes deeply and update the
|
|
294
|
-
// footnote anchor numbering (textContent) to match the new order.
|
|
295
|
-
const newBlocks = updateBlocksAttributes( _blocks );
|
|
296
|
-
|
|
297
|
-
oldFootnotes = {
|
|
298
|
-
...oldFootnotes,
|
|
299
|
-
...footnotes.reduce( ( acc, fn ) => {
|
|
300
|
-
if ( ! newOrder.includes( fn.id ) ) {
|
|
301
|
-
acc[ fn.id ] = fn;
|
|
302
|
-
}
|
|
303
|
-
return acc;
|
|
304
|
-
}, {} ),
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
return {
|
|
308
|
-
meta: {
|
|
309
|
-
...meta,
|
|
310
|
-
footnotes: JSON.stringify( newFootnotes ),
|
|
311
|
-
},
|
|
312
|
-
blocks: newBlocks,
|
|
313
|
-
};
|
|
314
|
-
},
|
|
182
|
+
( _blocks ) => updateFootnotesFromMeta( _blocks, meta ),
|
|
315
183
|
[ meta ]
|
|
316
184
|
);
|
|
317
185
|
|
|
@@ -64,7 +64,7 @@ export type Context = 'view' | 'edit' | 'embed';
|
|
|
64
64
|
export type ContextualField<
|
|
65
65
|
FieldType,
|
|
66
66
|
AvailableInContexts extends Context,
|
|
67
|
-
C extends Context
|
|
67
|
+
C extends Context,
|
|
68
68
|
> = AvailableInContexts extends C ? FieldType : never;
|
|
69
69
|
|
|
70
70
|
/**
|
|
@@ -93,7 +93,7 @@ export type OmitNevers<
|
|
|
93
93
|
: T[ K ] extends Record< string, unknown >
|
|
94
94
|
? OmitNevers< T[ K ] >
|
|
95
95
|
: T[ K ];
|
|
96
|
-
}
|
|
96
|
+
},
|
|
97
97
|
> = Pick<
|
|
98
98
|
Nevers,
|
|
99
99
|
{
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import getRichTextValuesCached from './get-rich-text-values-cached';
|
|
5
|
+
|
|
6
|
+
const cache = new WeakMap();
|
|
7
|
+
|
|
8
|
+
function getBlockFootnotesOrder( block ) {
|
|
9
|
+
if ( ! cache.has( block ) ) {
|
|
10
|
+
const content = getRichTextValuesCached( block ).join( '' );
|
|
11
|
+
const newOrder = [];
|
|
12
|
+
|
|
13
|
+
// https://github.com/WordPress/gutenberg/pull/43204 lands. We can then
|
|
14
|
+
// get the order directly from the rich text values.
|
|
15
|
+
if ( content.indexOf( 'data-fn' ) !== -1 ) {
|
|
16
|
+
const regex = /data-fn="([^"]+)"/g;
|
|
17
|
+
let match;
|
|
18
|
+
while ( ( match = regex.exec( content ) ) !== null ) {
|
|
19
|
+
newOrder.push( match[ 1 ] );
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
cache.set( block, newOrder );
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return cache.get( block );
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default function getFootnotesOrder( blocks ) {
|
|
29
|
+
return blocks.flatMap( getBlockFootnotesOrder );
|
|
30
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { unlock } from '../private-apis';
|
|
10
|
+
|
|
11
|
+
// TODO: The following line should have been:
|
|
12
|
+
//
|
|
13
|
+
// const unlockedApis = unlock( blockEditorPrivateApis );
|
|
14
|
+
//
|
|
15
|
+
// But there are hidden circular dependencies in RNMobile code, specifically in
|
|
16
|
+
// certain native components in the `components` package that depend on
|
|
17
|
+
// `block-editor`. What follows is a workaround that defers the `unlock` call
|
|
18
|
+
// to prevent native code from failing.
|
|
19
|
+
//
|
|
20
|
+
// Fix once https://github.com/WordPress/gutenberg/issues/52692 is closed.
|
|
21
|
+
let unlockedApis;
|
|
22
|
+
|
|
23
|
+
const cache = new WeakMap();
|
|
24
|
+
|
|
25
|
+
export default function getRichTextValuesCached( block ) {
|
|
26
|
+
if ( ! unlockedApis ) {
|
|
27
|
+
unlockedApis = unlock( blockEditorPrivateApis );
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if ( ! cache.has( block ) ) {
|
|
31
|
+
const values = unlockedApis.getRichTextValues( [ block ] );
|
|
32
|
+
cache.set( block, values );
|
|
33
|
+
}
|
|
34
|
+
return cache.get( block );
|
|
35
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import getFootnotesOrder from './get-footnotes-order';
|
|
5
|
+
|
|
6
|
+
let oldFootnotes = {};
|
|
7
|
+
|
|
8
|
+
export function updateFootnotesFromMeta( blocks, meta ) {
|
|
9
|
+
const output = { blocks };
|
|
10
|
+
if ( ! meta ) return output;
|
|
11
|
+
|
|
12
|
+
// If meta.footnotes is empty, it means the meta is not registered.
|
|
13
|
+
if ( meta.footnotes === undefined ) return output;
|
|
14
|
+
|
|
15
|
+
const newOrder = getFootnotesOrder( blocks );
|
|
16
|
+
|
|
17
|
+
const footnotes = meta.footnotes ? JSON.parse( meta.footnotes ) : [];
|
|
18
|
+
const currentOrder = footnotes.map( ( fn ) => fn.id );
|
|
19
|
+
|
|
20
|
+
if ( currentOrder.join( '' ) === newOrder.join( '' ) ) return output;
|
|
21
|
+
|
|
22
|
+
const newFootnotes = newOrder.map(
|
|
23
|
+
( fnId ) =>
|
|
24
|
+
footnotes.find( ( fn ) => fn.id === fnId ) ||
|
|
25
|
+
oldFootnotes[ fnId ] || {
|
|
26
|
+
id: fnId,
|
|
27
|
+
content: '',
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
function updateAttributes( attributes ) {
|
|
32
|
+
// Only attempt to update attributes, if attributes is an object.
|
|
33
|
+
if (
|
|
34
|
+
! attributes ||
|
|
35
|
+
Array.isArray( attributes ) ||
|
|
36
|
+
typeof attributes !== 'object'
|
|
37
|
+
) {
|
|
38
|
+
return attributes;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
attributes = { ...attributes };
|
|
42
|
+
|
|
43
|
+
for ( const key in attributes ) {
|
|
44
|
+
const value = attributes[ key ];
|
|
45
|
+
|
|
46
|
+
if ( Array.isArray( value ) ) {
|
|
47
|
+
attributes[ key ] = value.map( updateAttributes );
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if ( typeof value !== 'string' ) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if ( value.indexOf( 'data-fn' ) === -1 ) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// When we store rich text values, this would no longer
|
|
60
|
+
// require a regex.
|
|
61
|
+
const regex =
|
|
62
|
+
/(<sup[^>]+data-fn="([^"]+)"[^>]*><a[^>]*>)[\d*]*<\/a><\/sup>/g;
|
|
63
|
+
|
|
64
|
+
attributes[ key ] = value.replace(
|
|
65
|
+
regex,
|
|
66
|
+
( match, opening, fnId ) => {
|
|
67
|
+
const index = newOrder.indexOf( fnId );
|
|
68
|
+
return `${ opening }${ index + 1 }</a></sup>`;
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const compatRegex = /<a[^>]+data-fn="([^"]+)"[^>]*>\*<\/a>/g;
|
|
73
|
+
|
|
74
|
+
attributes[ key ] = attributes[ key ].replace(
|
|
75
|
+
compatRegex,
|
|
76
|
+
( match, fnId ) => {
|
|
77
|
+
const index = newOrder.indexOf( fnId );
|
|
78
|
+
return `<sup data-fn="${ fnId }" class="fn"><a href="#${ fnId }" id="${ fnId }-link">${
|
|
79
|
+
index + 1
|
|
80
|
+
}</a></sup>`;
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return attributes;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function updateBlocksAttributes( __blocks ) {
|
|
89
|
+
return __blocks.map( ( block ) => {
|
|
90
|
+
return {
|
|
91
|
+
...block,
|
|
92
|
+
attributes: updateAttributes( block.attributes ),
|
|
93
|
+
innerBlocks: updateBlocksAttributes( block.innerBlocks ),
|
|
94
|
+
};
|
|
95
|
+
} );
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// We need to go through all block attributes deeply and update the
|
|
99
|
+
// footnote anchor numbering (textContent) to match the new order.
|
|
100
|
+
const newBlocks = updateBlocksAttributes( blocks );
|
|
101
|
+
|
|
102
|
+
oldFootnotes = {
|
|
103
|
+
...oldFootnotes,
|
|
104
|
+
...footnotes.reduce( ( acc, fn ) => {
|
|
105
|
+
if ( ! newOrder.includes( fn.id ) ) {
|
|
106
|
+
acc[ fn.id ] = fn;
|
|
107
|
+
}
|
|
108
|
+
return acc;
|
|
109
|
+
}, {} ),
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
meta: {
|
|
114
|
+
...meta,
|
|
115
|
+
footnotes: JSON.stringify( newFootnotes ),
|
|
116
|
+
},
|
|
117
|
+
blocks: newBlocks,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
@@ -46,6 +46,7 @@ describe( 'useEntityRecord', () => {
|
|
|
46
46
|
edit: expect.any( Function ),
|
|
47
47
|
editedRecord: {},
|
|
48
48
|
hasEdits: false,
|
|
49
|
+
edits: {},
|
|
49
50
|
record: undefined,
|
|
50
51
|
save: expect.any( Function ),
|
|
51
52
|
hasResolved: false,
|
|
@@ -64,6 +65,7 @@ describe( 'useEntityRecord', () => {
|
|
|
64
65
|
edit: expect.any( Function ),
|
|
65
66
|
editedRecord: { hello: 'world', id: 1 },
|
|
66
67
|
hasEdits: false,
|
|
68
|
+
edits: {},
|
|
67
69
|
record: { hello: 'world', id: 1 },
|
|
68
70
|
save: expect.any( Function ),
|
|
69
71
|
hasResolved: true,
|
|
@@ -92,6 +94,7 @@ describe( 'useEntityRecord', () => {
|
|
|
92
94
|
edit: expect.any( Function ),
|
|
93
95
|
editedRecord: { hello: 'world', id: 1 },
|
|
94
96
|
hasEdits: false,
|
|
97
|
+
edits: {},
|
|
95
98
|
record: { hello: 'world', id: 1 },
|
|
96
99
|
save: expect.any( Function ),
|
|
97
100
|
hasResolved: true,
|
|
@@ -108,5 +111,6 @@ describe( 'useEntityRecord', () => {
|
|
|
108
111
|
|
|
109
112
|
expect( widget.record ).toEqual( { hello: 'world', id: 1 } );
|
|
110
113
|
expect( widget.editedRecord ).toEqual( { hello: 'foo', id: 1 } );
|
|
114
|
+
expect( widget.edits ).toEqual( { hello: 'foo' } );
|
|
111
115
|
} );
|
|
112
116
|
} );
|
|
@@ -19,6 +19,9 @@ export interface EntityRecordResolution< RecordType > {
|
|
|
19
19
|
/** The edited entity record */
|
|
20
20
|
editedRecord: Partial< RecordType >;
|
|
21
21
|
|
|
22
|
+
/** The edits to the edited entity record */
|
|
23
|
+
edits: Partial< RecordType >;
|
|
24
|
+
|
|
22
25
|
/** Apply local (in-browser) edits to the edited entity record */
|
|
23
26
|
edit: ( diff: Partial< RecordType > ) => void;
|
|
24
27
|
|
|
@@ -152,8 +155,8 @@ export default function useEntityRecord< RecordType >(
|
|
|
152
155
|
|
|
153
156
|
const mutations = useMemo(
|
|
154
157
|
() => ( {
|
|
155
|
-
edit: ( record ) =>
|
|
156
|
-
editEntityRecord( kind, name, recordId, record ),
|
|
158
|
+
edit: ( record, editOptions: any = {} ) =>
|
|
159
|
+
editEntityRecord( kind, name, recordId, record, editOptions ),
|
|
157
160
|
save: ( saveOptions: any = {} ) =>
|
|
158
161
|
saveEditedEntityRecord( kind, name, recordId, {
|
|
159
162
|
throwOnError: true,
|
|
@@ -163,7 +166,7 @@ export default function useEntityRecord< RecordType >(
|
|
|
163
166
|
[ editEntityRecord, kind, name, recordId, saveEditedEntityRecord ]
|
|
164
167
|
);
|
|
165
168
|
|
|
166
|
-
const { editedRecord, hasEdits } = useSelect(
|
|
169
|
+
const { editedRecord, hasEdits, edits } = useSelect(
|
|
167
170
|
( select ) => ( {
|
|
168
171
|
editedRecord: select( coreStore ).getEditedEntityRecord(
|
|
169
172
|
kind,
|
|
@@ -175,6 +178,11 @@ export default function useEntityRecord< RecordType >(
|
|
|
175
178
|
name,
|
|
176
179
|
recordId
|
|
177
180
|
),
|
|
181
|
+
edits: select( coreStore ).getEntityRecordNonTransientEdits(
|
|
182
|
+
kind,
|
|
183
|
+
name,
|
|
184
|
+
recordId
|
|
185
|
+
),
|
|
178
186
|
} ),
|
|
179
187
|
[ kind, name, recordId ]
|
|
180
188
|
);
|
|
@@ -195,6 +203,7 @@ export default function useEntityRecord< RecordType >(
|
|
|
195
203
|
record,
|
|
196
204
|
editedRecord,
|
|
197
205
|
hasEdits,
|
|
206
|
+
edits,
|
|
198
207
|
...querySelectRest,
|
|
199
208
|
...mutations,
|
|
200
209
|
};
|
|
@@ -38,7 +38,7 @@ type ResourcePermissionsResolution< IdType > = [
|
|
|
38
38
|
HasResolved,
|
|
39
39
|
ResolutionDetails &
|
|
40
40
|
GlobalResourcePermissionsResolution &
|
|
41
|
-
( IdType extends void ? SpecificResourcePermissionsResolution : {} )
|
|
41
|
+
( IdType extends void ? SpecificResourcePermissionsResolution : {} ),
|
|
42
42
|
];
|
|
43
43
|
|
|
44
44
|
/**
|
package/src/private-selectors.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Internal dependencies
|
|
3
3
|
*/
|
|
4
|
-
import type { State
|
|
4
|
+
import type { State } from './selectors';
|
|
5
5
|
|
|
6
|
-
type Optional< T > = T | undefined;
|
|
7
6
|
type EntityRecordKey = string | number;
|
|
8
7
|
|
|
9
8
|
/**
|
|
@@ -12,22 +11,10 @@ type EntityRecordKey = string | number;
|
|
|
12
11
|
*
|
|
13
12
|
* @param state State tree.
|
|
14
13
|
*
|
|
15
|
-
* @return The
|
|
14
|
+
* @return The undo manager.
|
|
16
15
|
*/
|
|
17
|
-
export function
|
|
18
|
-
return state.
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Returns the next edit from the current undo offset
|
|
23
|
-
* for the entity records edits history, if any.
|
|
24
|
-
*
|
|
25
|
-
* @param state State tree.
|
|
26
|
-
*
|
|
27
|
-
* @return The edit.
|
|
28
|
-
*/
|
|
29
|
-
export function getRedoEdits( state: State ): Optional< UndoEdit[] > {
|
|
30
|
-
return state.undo.list[ state.undo.list.length + state.undo.offset ];
|
|
16
|
+
export function getUndoManager( state: State ) {
|
|
17
|
+
return state.undoManager;
|
|
31
18
|
}
|
|
32
19
|
|
|
33
20
|
/**
|