@wordpress/core-data 6.10.0 → 6.12.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 +4 -0
- package/README.md +4 -0
- package/build/actions.js +252 -276
- package/build/actions.js.map +1 -1
- package/build/batch/create-batch.js +8 -16
- package/build/batch/create-batch.js.map +1 -1
- package/build/batch/default-processor.js +1 -1
- package/build/batch/default-processor.js.map +1 -1
- package/build/entities.js +16 -25
- package/build/entities.js.map +1 -1
- package/build/entity-provider.js +12 -17
- package/build/entity-provider.js.map +1 -1
- package/build/fetch/__experimental-fetch-link-suggestions.js +2 -6
- package/build/fetch/__experimental-fetch-link-suggestions.js.map +1 -1
- package/build/fetch/__experimental-fetch-url-data.js +1 -2
- package/build/fetch/__experimental-fetch-url-data.js.map +1 -1
- package/build/hooks/use-entity-record.js +7 -11
- package/build/hooks/use-entity-record.js.map +1 -1
- package/build/hooks/use-entity-records.js +3 -5
- package/build/hooks/use-entity-records.js.map +1 -1
- package/build/hooks/use-query-select.js +1 -6
- package/build/hooks/use-query-select.js.map +1 -1
- package/build/index.js +1 -7
- package/build/index.js.map +1 -1
- package/build/locks/actions.js +3 -4
- package/build/locks/actions.js.map +1 -1
- package/build/locks/reducer.js +1 -4
- package/build/locks/reducer.js.map +1 -1
- package/build/locks/selectors.js +3 -4
- package/build/locks/selectors.js.map +1 -1
- package/build/locks/utils.js +3 -5
- package/build/locks/utils.js.map +1 -1
- package/build/private-selectors.js +37 -0
- package/build/private-selectors.js.map +1 -0
- package/build/queried-data/actions.js +2 -5
- package/build/queried-data/actions.js.map +1 -1
- package/build/queried-data/reducer.js +17 -47
- package/build/queried-data/reducer.js.map +1 -1
- package/build/queried-data/selectors.js +4 -11
- package/build/queried-data/selectors.js.map +1 -1
- package/build/reducer.js +167 -194
- package/build/reducer.js.map +1 -1
- package/build/resolvers.js +175 -220
- package/build/resolvers.js.map +1 -1
- package/build/selectors.js +53 -61
- package/build/selectors.js.map +1 -1
- package/build/utils/forward-resolver.js +4 -11
- package/build/utils/forward-resolver.js.map +1 -1
- package/build/utils/on-sub-key.js +1 -3
- package/build/utils/on-sub-key.js.map +1 -1
- package/build-module/actions.js +251 -276
- package/build-module/actions.js.map +1 -1
- package/build-module/batch/create-batch.js +8 -16
- package/build-module/batch/create-batch.js.map +1 -1
- package/build-module/batch/default-processor.js +1 -1
- package/build-module/batch/default-processor.js.map +1 -1
- package/build-module/entities.js +16 -25
- package/build-module/entities.js.map +1 -1
- package/build-module/entity-provider.js +12 -17
- package/build-module/entity-provider.js.map +1 -1
- package/build-module/fetch/__experimental-fetch-link-suggestions.js +2 -6
- package/build-module/fetch/__experimental-fetch-link-suggestions.js.map +1 -1
- package/build-module/fetch/__experimental-fetch-url-data.js +1 -2
- package/build-module/fetch/__experimental-fetch-url-data.js.map +1 -1
- package/build-module/hooks/use-entity-record.js +7 -11
- package/build-module/hooks/use-entity-record.js.map +1 -1
- package/build-module/hooks/use-entity-records.js +3 -5
- package/build-module/hooks/use-entity-records.js.map +1 -1
- package/build-module/hooks/use-query-select.js +1 -6
- package/build-module/hooks/use-query-select.js.map +1 -1
- package/build-module/index.js +1 -7
- package/build-module/index.js.map +1 -1
- package/build-module/locks/actions.js +3 -4
- package/build-module/locks/actions.js.map +1 -1
- package/build-module/locks/reducer.js +1 -4
- package/build-module/locks/reducer.js.map +1 -1
- package/build-module/locks/selectors.js +3 -4
- package/build-module/locks/selectors.js.map +1 -1
- package/build-module/locks/utils.js +3 -5
- package/build-module/locks/utils.js.map +1 -1
- package/build-module/private-selectors.js +28 -0
- package/build-module/private-selectors.js.map +1 -0
- package/build-module/queried-data/actions.js +2 -5
- package/build-module/queried-data/actions.js.map +1 -1
- package/build-module/queried-data/reducer.js +17 -47
- package/build-module/queried-data/reducer.js.map +1 -1
- package/build-module/queried-data/selectors.js +4 -11
- package/build-module/queried-data/selectors.js.map +1 -1
- package/build-module/reducer.js +168 -194
- package/build-module/reducer.js.map +1 -1
- package/build-module/resolvers.js +175 -220
- package/build-module/resolvers.js.map +1 -1
- package/build-module/selectors.js +55 -61
- package/build-module/selectors.js.map +1 -1
- package/build-module/utils/forward-resolver.js +4 -11
- package/build-module/utils/forward-resolver.js.map +1 -1
- package/build-module/utils/on-sub-key.js +1 -3
- package/build-module/utils/on-sub-key.js.map +1 -1
- package/build-types/actions.d.ts.map +1 -1
- package/build-types/private-selectors.d.ts +25 -0
- package/build-types/private-selectors.d.ts.map +1 -0
- package/build-types/reducer.d.ts +6 -2
- package/build-types/reducer.d.ts.map +1 -1
- package/build-types/selectors.d.ts +16 -4
- package/build-types/selectors.d.ts.map +1 -1
- package/package.json +13 -13
- package/src/actions.js +9 -8
- package/src/index.js +0 -1
- package/src/private-selectors.ts +30 -0
- package/src/reducer.js +130 -104
- package/src/selectors.ts +33 -13
- package/src/test/reducer.js +89 -54
- package/src/test/selectors.js +8 -8
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { State, UndoEdit } from './selectors';
|
|
5
|
+
|
|
6
|
+
type Optional< T > = T | undefined;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Returns the previous edit from the current undo offset
|
|
10
|
+
* for the entity records edits history, if any.
|
|
11
|
+
*
|
|
12
|
+
* @param state State tree.
|
|
13
|
+
*
|
|
14
|
+
* @return The edit.
|
|
15
|
+
*/
|
|
16
|
+
export function getUndoEdits( state: State ): Optional< UndoEdit[] > {
|
|
17
|
+
return state.undo.list[ state.undo.list.length - 1 + state.undo.offset ];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns the next edit from the current undo offset
|
|
22
|
+
* for the entity records edits history, if any.
|
|
23
|
+
*
|
|
24
|
+
* @param state State tree.
|
|
25
|
+
*
|
|
26
|
+
* @return The edit.
|
|
27
|
+
*/
|
|
28
|
+
export function getRedoEdits( state: State ): Optional< UndoEdit[] > {
|
|
29
|
+
return state.undo.list[ state.undo.list.length + state.undo.offset ];
|
|
30
|
+
}
|
package/src/reducer.js
CHANGED
|
@@ -183,6 +183,30 @@ export function themeGlobalStyleVariations( state = {}, action ) {
|
|
|
183
183
|
return state;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
const withMultiEntityRecordEdits = ( reducer ) => ( state, action ) => {
|
|
187
|
+
if ( action.type === 'UNDO' || action.type === 'REDO' ) {
|
|
188
|
+
const { stackedEdits } = action;
|
|
189
|
+
|
|
190
|
+
let newState = state;
|
|
191
|
+
stackedEdits.forEach(
|
|
192
|
+
( { kind, name, recordId, property, from, to } ) => {
|
|
193
|
+
newState = reducer( newState, {
|
|
194
|
+
type: 'EDIT_ENTITY_RECORD',
|
|
195
|
+
kind,
|
|
196
|
+
name,
|
|
197
|
+
recordId,
|
|
198
|
+
edits: {
|
|
199
|
+
[ property ]: action.type === 'UNDO' ? from : to,
|
|
200
|
+
},
|
|
201
|
+
} );
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
return newState;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return reducer( state, action );
|
|
208
|
+
};
|
|
209
|
+
|
|
186
210
|
/**
|
|
187
211
|
* Higher Order Reducer for a given entity config. It supports:
|
|
188
212
|
*
|
|
@@ -196,6 +220,8 @@ export function themeGlobalStyleVariations( state = {}, action ) {
|
|
|
196
220
|
*/
|
|
197
221
|
function entity( entityConfig ) {
|
|
198
222
|
return compose( [
|
|
223
|
+
withMultiEntityRecordEdits,
|
|
224
|
+
|
|
199
225
|
// Limit to matching action type so we don't attempt to replace action on
|
|
200
226
|
// an unhandled action.
|
|
201
227
|
ifMatchingAction(
|
|
@@ -411,8 +437,9 @@ export const entities = ( state = {}, action ) => {
|
|
|
411
437
|
/**
|
|
412
438
|
* @typedef {Object} UndoStateMeta
|
|
413
439
|
*
|
|
414
|
-
* @property {number}
|
|
415
|
-
* @property {
|
|
440
|
+
* @property {number} list The undo stack.
|
|
441
|
+
* @property {number} offset Where in the undo stack we are.
|
|
442
|
+
* @property {Object} cache Cache of unpersisted transient edits.
|
|
416
443
|
*/
|
|
417
444
|
|
|
418
445
|
/** @typedef {Array<Object> & UndoStateMeta} UndoState */
|
|
@@ -422,10 +449,7 @@ export const entities = ( state = {}, action ) => {
|
|
|
422
449
|
*
|
|
423
450
|
* @todo Given how we use this we might want to make a custom class for it.
|
|
424
451
|
*/
|
|
425
|
-
const UNDO_INITIAL_STATE =
|
|
426
|
-
|
|
427
|
-
/** @type {Object} */
|
|
428
|
-
let lastEditAction;
|
|
452
|
+
const UNDO_INITIAL_STATE = { list: [], offset: 0 };
|
|
429
453
|
|
|
430
454
|
/**
|
|
431
455
|
* Reducer keeping track of entity edit undo history.
|
|
@@ -436,107 +460,114 @@ let lastEditAction;
|
|
|
436
460
|
* @return {UndoState} Updated state.
|
|
437
461
|
*/
|
|
438
462
|
export function undo( state = UNDO_INITIAL_STATE, action ) {
|
|
463
|
+
const omitPendingRedos = ( currentState ) => {
|
|
464
|
+
return {
|
|
465
|
+
...currentState,
|
|
466
|
+
list: currentState.list.slice(
|
|
467
|
+
0,
|
|
468
|
+
currentState.offset || undefined
|
|
469
|
+
),
|
|
470
|
+
offset: 0,
|
|
471
|
+
};
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
const appendCachedEditsToLastUndo = ( currentState ) => {
|
|
475
|
+
if ( ! currentState.cache ) {
|
|
476
|
+
return currentState;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
let nextState = {
|
|
480
|
+
...currentState,
|
|
481
|
+
list: [ ...currentState.list ],
|
|
482
|
+
};
|
|
483
|
+
nextState = omitPendingRedos( nextState );
|
|
484
|
+
const previousUndoState = nextState.list.pop();
|
|
485
|
+
const updatedUndoState = currentState.cache.reduce(
|
|
486
|
+
appendEditToStack,
|
|
487
|
+
previousUndoState
|
|
488
|
+
);
|
|
489
|
+
nextState.list.push( updatedUndoState );
|
|
490
|
+
|
|
491
|
+
return {
|
|
492
|
+
...nextState,
|
|
493
|
+
cache: undefined,
|
|
494
|
+
};
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
const appendEditToStack = (
|
|
498
|
+
stack = [],
|
|
499
|
+
{ kind, name, recordId, property, from, to }
|
|
500
|
+
) => {
|
|
501
|
+
const existingEditIndex = stack?.findIndex(
|
|
502
|
+
( { kind: k, name: n, recordId: r, property: p } ) => {
|
|
503
|
+
return (
|
|
504
|
+
k === kind && n === name && r === recordId && p === property
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
);
|
|
508
|
+
const nextStack = [ ...stack ];
|
|
509
|
+
if ( existingEditIndex !== -1 ) {
|
|
510
|
+
// If the edit is already in the stack leave the initial "from" value.
|
|
511
|
+
nextStack[ existingEditIndex ] = {
|
|
512
|
+
...nextStack[ existingEditIndex ],
|
|
513
|
+
to,
|
|
514
|
+
};
|
|
515
|
+
} else {
|
|
516
|
+
nextStack.push( {
|
|
517
|
+
kind,
|
|
518
|
+
name,
|
|
519
|
+
recordId,
|
|
520
|
+
property,
|
|
521
|
+
from,
|
|
522
|
+
to,
|
|
523
|
+
} );
|
|
524
|
+
}
|
|
525
|
+
return nextStack;
|
|
526
|
+
};
|
|
527
|
+
|
|
439
528
|
switch ( action.type ) {
|
|
440
|
-
case 'EDIT_ENTITY_RECORD':
|
|
441
529
|
case 'CREATE_UNDO_LEVEL':
|
|
442
|
-
|
|
443
|
-
const isUndoOrRedo =
|
|
444
|
-
! isCreateUndoLevel &&
|
|
445
|
-
( action.meta.isUndo || action.meta.isRedo );
|
|
446
|
-
if ( isCreateUndoLevel ) {
|
|
447
|
-
action = lastEditAction;
|
|
448
|
-
} else if ( ! isUndoOrRedo ) {
|
|
449
|
-
// Don't lose the last edit cache if the new one only has transient edits.
|
|
450
|
-
// Transient edits don't create new levels so updating the cache would make
|
|
451
|
-
// us skip an edit later when creating levels explicitly.
|
|
452
|
-
if (
|
|
453
|
-
Object.keys( action.edits ).some(
|
|
454
|
-
( key ) => ! action.transientEdits[ key ]
|
|
455
|
-
)
|
|
456
|
-
) {
|
|
457
|
-
lastEditAction = action;
|
|
458
|
-
} else {
|
|
459
|
-
lastEditAction = {
|
|
460
|
-
...action,
|
|
461
|
-
edits: {
|
|
462
|
-
...( lastEditAction && lastEditAction.edits ),
|
|
463
|
-
...action.edits,
|
|
464
|
-
},
|
|
465
|
-
};
|
|
466
|
-
}
|
|
467
|
-
}
|
|
530
|
+
return appendCachedEditsToLastUndo( state );
|
|
468
531
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
if ( state.flattenedUndo ) {
|
|
479
|
-
// The first undo in a sequence of undos might happen while we have
|
|
480
|
-
// flattened undos in state. If this is the case, we want execution
|
|
481
|
-
// to continue as if we were creating an explicit undo level. This
|
|
482
|
-
// will result in an extra undo level being appended with the flattened
|
|
483
|
-
// undo values.
|
|
484
|
-
// We also have to take into account if the `lastEditAction` had opted out
|
|
485
|
-
// of being tracked in undo history, like the action that persists the latest
|
|
486
|
-
// content right before saving. In that case we have to update the `lastEditAction`
|
|
487
|
-
// to avoid returning early before applying the existing flattened undos.
|
|
488
|
-
isCreateUndoLevel = true;
|
|
489
|
-
if ( ! lastEditAction.meta.undo ) {
|
|
490
|
-
lastEditAction.meta.undo = {
|
|
491
|
-
edits: {},
|
|
492
|
-
};
|
|
493
|
-
}
|
|
494
|
-
action = lastEditAction;
|
|
495
|
-
} else {
|
|
496
|
-
return nextState;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
532
|
+
case 'UNDO':
|
|
533
|
+
case 'REDO': {
|
|
534
|
+
const nextState = appendCachedEditsToLastUndo( state );
|
|
535
|
+
return {
|
|
536
|
+
...nextState,
|
|
537
|
+
offset: state.offset + ( action.type === 'UNDO' ? -1 : 1 ),
|
|
538
|
+
};
|
|
539
|
+
}
|
|
499
540
|
|
|
541
|
+
case 'EDIT_ENTITY_RECORD': {
|
|
500
542
|
if ( ! action.meta.undo ) {
|
|
501
543
|
return state;
|
|
502
544
|
}
|
|
503
545
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
...state.flattenedUndo,
|
|
517
|
-
...action.edits,
|
|
546
|
+
const isCachedChange = Object.keys( action.edits ).every(
|
|
547
|
+
( key ) => action.transientEdits[ key ]
|
|
548
|
+
);
|
|
549
|
+
|
|
550
|
+
const edits = Object.keys( action.edits ).map( ( key ) => {
|
|
551
|
+
return {
|
|
552
|
+
kind: action.kind,
|
|
553
|
+
name: action.name,
|
|
554
|
+
recordId: action.recordId,
|
|
555
|
+
property: key,
|
|
556
|
+
from: action.meta.undo.edits[ key ],
|
|
557
|
+
to: action.edits[ key ],
|
|
518
558
|
};
|
|
519
|
-
|
|
520
|
-
return nextState;
|
|
521
|
-
}
|
|
559
|
+
} );
|
|
522
560
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
nextState.pop();
|
|
529
|
-
if ( ! isCreateUndoLevel ) {
|
|
530
|
-
nextState.push( {
|
|
531
|
-
kind: action.meta.undo.kind,
|
|
532
|
-
name: action.meta.undo.name,
|
|
533
|
-
recordId: action.meta.undo.recordId,
|
|
534
|
-
edits: {
|
|
535
|
-
...state.flattenedUndo,
|
|
536
|
-
...action.meta.undo.edits,
|
|
537
|
-
},
|
|
538
|
-
} );
|
|
561
|
+
if ( isCachedChange ) {
|
|
562
|
+
return {
|
|
563
|
+
...state,
|
|
564
|
+
cache: edits.reduce( appendEditToStack, state.cache ),
|
|
565
|
+
};
|
|
539
566
|
}
|
|
567
|
+
|
|
568
|
+
let nextState = omitPendingRedos( state );
|
|
569
|
+
nextState = appendCachedEditsToLastUndo( nextState );
|
|
570
|
+
nextState = { ...nextState, list: [ ...nextState.list ] };
|
|
540
571
|
// When an edit is a function it's an optimization to avoid running some expensive operation.
|
|
541
572
|
// We can't rely on the function references being the same so we opt out of comparing them here.
|
|
542
573
|
const comparisonUndoEdits = Object.values(
|
|
@@ -546,16 +577,11 @@ export function undo( state = UNDO_INITIAL_STATE, action ) {
|
|
|
546
577
|
( edit ) => typeof edit !== 'function'
|
|
547
578
|
);
|
|
548
579
|
if ( ! isShallowEqual( comparisonUndoEdits, comparisonEdits ) ) {
|
|
549
|
-
nextState.push(
|
|
550
|
-
kind: action.kind,
|
|
551
|
-
name: action.name,
|
|
552
|
-
recordId: action.recordId,
|
|
553
|
-
edits: isCreateUndoLevel
|
|
554
|
-
? { ...state.flattenedUndo, ...action.edits }
|
|
555
|
-
: action.edits,
|
|
556
|
-
} );
|
|
580
|
+
nextState.list.push( edits );
|
|
557
581
|
}
|
|
582
|
+
|
|
558
583
|
return nextState;
|
|
584
|
+
}
|
|
559
585
|
}
|
|
560
586
|
|
|
561
587
|
return state;
|
package/src/selectors.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
setNestedValue,
|
|
23
23
|
} from './utils';
|
|
24
24
|
import type * as ET from './entity-types';
|
|
25
|
+
import { getUndoEdits, getRedoEdits } from './private-selectors';
|
|
25
26
|
|
|
26
27
|
// This is an incomplete, high-level approximation of the State type.
|
|
27
28
|
// It makes the selectors slightly more safe, but is intended to evolve
|
|
@@ -73,9 +74,18 @@ interface EntityConfig {
|
|
|
73
74
|
kind: string;
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
interface
|
|
77
|
-
|
|
77
|
+
export interface UndoEdit {
|
|
78
|
+
name: string;
|
|
79
|
+
kind: string;
|
|
80
|
+
recordId: string;
|
|
81
|
+
from: any;
|
|
82
|
+
to: any;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interface UndoState {
|
|
86
|
+
list: Array< UndoEdit[] >;
|
|
78
87
|
offset: number;
|
|
88
|
+
cache: UndoEdit[];
|
|
79
89
|
}
|
|
80
90
|
|
|
81
91
|
interface UserState {
|
|
@@ -884,24 +894,38 @@ function getCurrentUndoOffset( state: State ): number {
|
|
|
884
894
|
* Returns the previous edit from the current undo offset
|
|
885
895
|
* for the entity records edits history, if any.
|
|
886
896
|
*
|
|
887
|
-
* @
|
|
897
|
+
* @deprecated since 6.3
|
|
898
|
+
*
|
|
899
|
+
* @param state State tree.
|
|
888
900
|
*
|
|
889
901
|
* @return The edit.
|
|
890
902
|
*/
|
|
891
903
|
export function getUndoEdit( state: State ): Optional< any > {
|
|
892
|
-
|
|
904
|
+
deprecated( "select( 'core' ).getUndoEdit()", {
|
|
905
|
+
since: '6.3',
|
|
906
|
+
} );
|
|
907
|
+
return state.undo.list[
|
|
908
|
+
state.undo.list.length - 2 + getCurrentUndoOffset( state )
|
|
909
|
+
]?.[ 0 ];
|
|
893
910
|
}
|
|
894
911
|
|
|
895
912
|
/**
|
|
896
913
|
* Returns the next edit from the current undo offset
|
|
897
914
|
* for the entity records edits history, if any.
|
|
898
915
|
*
|
|
899
|
-
* @
|
|
916
|
+
* @deprecated since 6.3
|
|
917
|
+
*
|
|
918
|
+
* @param state State tree.
|
|
900
919
|
*
|
|
901
920
|
* @return The edit.
|
|
902
921
|
*/
|
|
903
922
|
export function getRedoEdit( state: State ): Optional< any > {
|
|
904
|
-
|
|
923
|
+
deprecated( "select( 'core' ).getRedoEdit()", {
|
|
924
|
+
since: '6.3',
|
|
925
|
+
} );
|
|
926
|
+
return state.undo.list[
|
|
927
|
+
state.undo.list.length + getCurrentUndoOffset( state )
|
|
928
|
+
]?.[ 0 ];
|
|
905
929
|
}
|
|
906
930
|
|
|
907
931
|
/**
|
|
@@ -913,7 +937,7 @@ export function getRedoEdit( state: State ): Optional< any > {
|
|
|
913
937
|
* @return Whether there is a previous edit or not.
|
|
914
938
|
*/
|
|
915
939
|
export function hasUndo( state: State ): boolean {
|
|
916
|
-
return Boolean(
|
|
940
|
+
return Boolean( getUndoEdits( state ) );
|
|
917
941
|
}
|
|
918
942
|
|
|
919
943
|
/**
|
|
@@ -925,7 +949,7 @@ export function hasUndo( state: State ): boolean {
|
|
|
925
949
|
* @return Whether there is a next edit or not.
|
|
926
950
|
*/
|
|
927
951
|
export function hasRedo( state: State ): boolean {
|
|
928
|
-
return Boolean(
|
|
952
|
+
return Boolean( getRedoEdits( state ) );
|
|
929
953
|
}
|
|
930
954
|
|
|
931
955
|
/**
|
|
@@ -1142,11 +1166,7 @@ export const hasFetchedAutosaves = createRegistrySelector(
|
|
|
1142
1166
|
export const getReferenceByDistinctEdits = createSelector(
|
|
1143
1167
|
// This unused state argument is listed here for the documentation generating tool (docgen).
|
|
1144
1168
|
( state: State ) => [],
|
|
1145
|
-
( state: State ) => [
|
|
1146
|
-
state.undo.length,
|
|
1147
|
-
state.undo.offset,
|
|
1148
|
-
state.undo.flattenedUndo,
|
|
1149
|
-
]
|
|
1169
|
+
( state: State ) => [ state.undo.list.length, state.undo.offset ]
|
|
1150
1170
|
);
|
|
1151
1171
|
|
|
1152
1172
|
/**
|
package/src/test/reducer.js
CHANGED
|
@@ -143,28 +143,34 @@ describe( 'entities', () => {
|
|
|
143
143
|
} );
|
|
144
144
|
|
|
145
145
|
describe( 'undo', () => {
|
|
146
|
-
let
|
|
146
|
+
let lastValues;
|
|
147
147
|
let undoState;
|
|
148
148
|
let expectedUndoState;
|
|
149
|
-
|
|
149
|
+
|
|
150
|
+
const createExpectedDiff = ( property, { from, to } ) => ( {
|
|
150
151
|
kind: 'someKind',
|
|
151
152
|
name: 'someName',
|
|
152
153
|
recordId: 'someRecordId',
|
|
153
|
-
|
|
154
|
+
property,
|
|
155
|
+
from,
|
|
156
|
+
to,
|
|
154
157
|
} );
|
|
155
158
|
const createNextEditAction = ( edits, transientEdits = {} ) => {
|
|
156
159
|
let action = {
|
|
157
|
-
|
|
160
|
+
kind: 'someKind',
|
|
161
|
+
name: 'someName',
|
|
162
|
+
recordId: 'someRecordId',
|
|
163
|
+
edits,
|
|
158
164
|
transientEdits,
|
|
159
165
|
};
|
|
160
166
|
action = {
|
|
161
167
|
type: 'EDIT_ENTITY_RECORD',
|
|
162
168
|
...action,
|
|
163
169
|
meta: {
|
|
164
|
-
undo: {
|
|
170
|
+
undo: { edits: lastValues },
|
|
165
171
|
},
|
|
166
172
|
};
|
|
167
|
-
|
|
173
|
+
lastValues = { ...lastValues, ...edits };
|
|
168
174
|
return action;
|
|
169
175
|
};
|
|
170
176
|
const createNextUndoState = ( ...args ) => {
|
|
@@ -172,17 +178,17 @@ describe( 'undo', () => {
|
|
|
172
178
|
if ( args[ 0 ] === 'isUndo' || args[ 0 ] === 'isRedo' ) {
|
|
173
179
|
// We need to "apply" the undo level here and build
|
|
174
180
|
// the action to move the offset.
|
|
175
|
-
lastEdits =
|
|
176
|
-
undoState[
|
|
177
|
-
undoState.length
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
]
|
|
181
|
+
const lastEdits =
|
|
182
|
+
undoState.list[
|
|
183
|
+
undoState.list.length -
|
|
184
|
+
( args[ 0 ] === 'isUndo' ? 1 : 0 ) +
|
|
185
|
+
undoState.offset
|
|
186
|
+
];
|
|
187
|
+
lastEdits.forEach( ( { property, from, to } ) => {
|
|
188
|
+
lastValues[ property ] = args[ 0 ] === 'isUndo' ? from : to;
|
|
189
|
+
} );
|
|
181
190
|
action = {
|
|
182
|
-
type: '
|
|
183
|
-
meta: {
|
|
184
|
-
[ args[ 0 ] ]: true,
|
|
185
|
-
},
|
|
191
|
+
type: args[ 0 ] === 'isUndo' ? 'UNDO' : 'REDO',
|
|
186
192
|
};
|
|
187
193
|
} else if ( args[ 0 ] === 'isCreate' ) {
|
|
188
194
|
action = { type: 'CREATE_UNDO_LEVEL' };
|
|
@@ -192,10 +198,9 @@ describe( 'undo', () => {
|
|
|
192
198
|
return deepFreeze( undo( undoState, action ) );
|
|
193
199
|
};
|
|
194
200
|
beforeEach( () => {
|
|
195
|
-
|
|
201
|
+
lastValues = {};
|
|
196
202
|
undoState = undefined;
|
|
197
|
-
expectedUndoState = [];
|
|
198
|
-
expectedUndoState.offset = 0;
|
|
203
|
+
expectedUndoState = { list: [], offset: 0 };
|
|
199
204
|
} );
|
|
200
205
|
|
|
201
206
|
it( 'initializes', () => {
|
|
@@ -208,19 +213,41 @@ describe( 'undo', () => {
|
|
|
208
213
|
// Check that the first edit creates an undo level for the current state and
|
|
209
214
|
// one for the new one.
|
|
210
215
|
undoState = createNextUndoState( { value: 1 } );
|
|
211
|
-
expectedUndoState.push(
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
);
|
|
216
|
+
expectedUndoState.list.push( [
|
|
217
|
+
createExpectedDiff( 'value', { from: undefined, to: 1 } ),
|
|
218
|
+
] );
|
|
215
219
|
expect( undoState ).toEqual( expectedUndoState );
|
|
216
220
|
|
|
217
221
|
// Check that the second and third edits just create an undo level for
|
|
218
222
|
// themselves.
|
|
219
223
|
undoState = createNextUndoState( { value: 2 } );
|
|
220
|
-
expectedUndoState.push(
|
|
224
|
+
expectedUndoState.list.push( [
|
|
225
|
+
createExpectedDiff( 'value', { from: 1, to: 2 } ),
|
|
226
|
+
] );
|
|
221
227
|
expect( undoState ).toEqual( expectedUndoState );
|
|
222
228
|
undoState = createNextUndoState( { value: 3 } );
|
|
223
|
-
expectedUndoState.push(
|
|
229
|
+
expectedUndoState.list.push( [
|
|
230
|
+
createExpectedDiff( 'value', { from: 2, to: 3 } ),
|
|
231
|
+
] );
|
|
232
|
+
expect( undoState ).toEqual( expectedUndoState );
|
|
233
|
+
} );
|
|
234
|
+
|
|
235
|
+
it( 'stacks multi-property undo levels', () => {
|
|
236
|
+
undoState = createNextUndoState();
|
|
237
|
+
|
|
238
|
+
undoState = createNextUndoState( { value: 1 } );
|
|
239
|
+
undoState = createNextUndoState( { value2: 2 } );
|
|
240
|
+
expectedUndoState.list.push(
|
|
241
|
+
[ createExpectedDiff( 'value', { from: undefined, to: 1 } ) ],
|
|
242
|
+
[ createExpectedDiff( 'value2', { from: undefined, to: 2 } ) ]
|
|
243
|
+
);
|
|
244
|
+
expect( undoState ).toEqual( expectedUndoState );
|
|
245
|
+
|
|
246
|
+
// Check that that creating another undo level merges the "edits"
|
|
247
|
+
undoState = createNextUndoState( { value: 2 } );
|
|
248
|
+
expectedUndoState.list.push( [
|
|
249
|
+
createExpectedDiff( 'value', { from: 1, to: 2 } ),
|
|
250
|
+
] );
|
|
224
251
|
expect( undoState ).toEqual( expectedUndoState );
|
|
225
252
|
} );
|
|
226
253
|
|
|
@@ -229,11 +256,10 @@ describe( 'undo', () => {
|
|
|
229
256
|
undoState = createNextUndoState( { value: 1 } );
|
|
230
257
|
undoState = createNextUndoState( { value: 2 } );
|
|
231
258
|
undoState = createNextUndoState( { value: 3 } );
|
|
232
|
-
expectedUndoState.push(
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
createEditActionPart( { value: 3 } )
|
|
259
|
+
expectedUndoState.list.push(
|
|
260
|
+
[ createExpectedDiff( 'value', { from: undefined, to: 1 } ) ],
|
|
261
|
+
[ createExpectedDiff( 'value', { from: 1, to: 2 } ) ],
|
|
262
|
+
[ createExpectedDiff( 'value', { from: 2, to: 3 } ) ]
|
|
237
263
|
);
|
|
238
264
|
expect( undoState ).toEqual( expectedUndoState );
|
|
239
265
|
|
|
@@ -255,17 +281,22 @@ describe( 'undo', () => {
|
|
|
255
281
|
// Check that another edit will go on top when there
|
|
256
282
|
// is no undo level offset.
|
|
257
283
|
undoState = createNextUndoState( { value: 4 } );
|
|
258
|
-
expectedUndoState.push(
|
|
284
|
+
expectedUndoState.list.push( [
|
|
285
|
+
createExpectedDiff( 'value', { from: 3, to: 4 } ),
|
|
286
|
+
] );
|
|
259
287
|
expect( undoState ).toEqual( expectedUndoState );
|
|
260
288
|
|
|
261
289
|
// Check that undoing and editing will slice of
|
|
262
290
|
// all the levels after the current one.
|
|
263
291
|
undoState = createNextUndoState( 'isUndo' );
|
|
264
292
|
undoState = createNextUndoState( 'isUndo' );
|
|
293
|
+
|
|
265
294
|
undoState = createNextUndoState( { value: 5 } );
|
|
266
|
-
expectedUndoState.pop();
|
|
267
|
-
expectedUndoState.pop();
|
|
268
|
-
expectedUndoState.push(
|
|
295
|
+
expectedUndoState.list.pop();
|
|
296
|
+
expectedUndoState.list.pop();
|
|
297
|
+
expectedUndoState.list.push( [
|
|
298
|
+
createExpectedDiff( 'value', { from: 2, to: 5 } ),
|
|
299
|
+
] );
|
|
269
300
|
expect( undoState ).toEqual( expectedUndoState );
|
|
270
301
|
} );
|
|
271
302
|
|
|
@@ -277,10 +308,15 @@ describe( 'undo', () => {
|
|
|
277
308
|
{ transientValue: true }
|
|
278
309
|
);
|
|
279
310
|
undoState = createNextUndoState( { value: 3 } );
|
|
280
|
-
expectedUndoState.push(
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
311
|
+
expectedUndoState.list.push(
|
|
312
|
+
[
|
|
313
|
+
createExpectedDiff( 'value', { from: undefined, to: 1 } ),
|
|
314
|
+
createExpectedDiff( 'transientValue', {
|
|
315
|
+
from: undefined,
|
|
316
|
+
to: 2,
|
|
317
|
+
} ),
|
|
318
|
+
],
|
|
319
|
+
[ createExpectedDiff( 'value', { from: 1, to: 3 } ) ]
|
|
284
320
|
);
|
|
285
321
|
expect( undoState ).toEqual( expectedUndoState );
|
|
286
322
|
} );
|
|
@@ -292,10 +328,9 @@ describe( 'undo', () => {
|
|
|
292
328
|
// transient edits.
|
|
293
329
|
undoState = createNextUndoState( { value: 1 } );
|
|
294
330
|
undoState = createNextUndoState( 'isCreate' );
|
|
295
|
-
expectedUndoState.push(
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
);
|
|
331
|
+
expectedUndoState.list.push( [
|
|
332
|
+
createExpectedDiff( 'value', { from: undefined, to: 1 } ),
|
|
333
|
+
] );
|
|
299
334
|
expect( undoState ).toEqual( expectedUndoState );
|
|
300
335
|
|
|
301
336
|
// Check that transient edits are merged into the last
|
|
@@ -305,18 +340,19 @@ describe( 'undo', () => {
|
|
|
305
340
|
{ transientValue: true }
|
|
306
341
|
);
|
|
307
342
|
undoState = createNextUndoState( 'isCreate' );
|
|
308
|
-
expectedUndoState[
|
|
309
|
-
|
|
310
|
-
|
|
343
|
+
expectedUndoState.list[ expectedUndoState.list.length - 1 ].push(
|
|
344
|
+
createExpectedDiff( 'transientValue', { from: undefined, to: 2 } )
|
|
345
|
+
);
|
|
311
346
|
expect( undoState ).toEqual( expectedUndoState );
|
|
312
347
|
|
|
313
|
-
// Check that
|
|
314
|
-
// even if undone.
|
|
348
|
+
// Check that create after undo does nothing.
|
|
315
349
|
undoState = createNextUndoState( { value: 3 } );
|
|
316
350
|
undoState = createNextUndoState( 'isUndo' );
|
|
317
351
|
undoState = createNextUndoState( 'isCreate' );
|
|
318
|
-
expectedUndoState.
|
|
319
|
-
|
|
352
|
+
expectedUndoState.list.push( [
|
|
353
|
+
createExpectedDiff( 'value', { from: 1, to: 3 } ),
|
|
354
|
+
] );
|
|
355
|
+
expectedUndoState.offset = -1;
|
|
320
356
|
expect( undoState ).toEqual( expectedUndoState );
|
|
321
357
|
} );
|
|
322
358
|
|
|
@@ -328,10 +364,10 @@ describe( 'undo', () => {
|
|
|
328
364
|
{ transientValue: true }
|
|
329
365
|
);
|
|
330
366
|
undoState = createNextUndoState( 'isUndo' );
|
|
331
|
-
expectedUndoState.push(
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
);
|
|
367
|
+
expectedUndoState.list.push( [
|
|
368
|
+
createExpectedDiff( 'value', { from: undefined, to: 1 } ),
|
|
369
|
+
createExpectedDiff( 'transientValue', { from: undefined, to: 2 } ),
|
|
370
|
+
] );
|
|
335
371
|
expectedUndoState.offset--;
|
|
336
372
|
expect( undoState ).toEqual( expectedUndoState );
|
|
337
373
|
} );
|
|
@@ -341,7 +377,6 @@ describe( 'undo', () => {
|
|
|
341
377
|
undoState = createNextUndoState();
|
|
342
378
|
undoState = createNextUndoState( { value } );
|
|
343
379
|
undoState = createNextUndoState( { value: () => {} } );
|
|
344
|
-
expectedUndoState.push( createEditActionPart( { value } ) );
|
|
345
380
|
expect( undoState ).toEqual( expectedUndoState );
|
|
346
381
|
} );
|
|
347
382
|
} );
|