Add coexistence checks to all enqueue methods to prevent loading both React and Grid.js assets simultaneously. Changes: - ReactAdmin.php: Only enqueue React assets when ?react=1 - Init.php: Skip Grid.js when React active on admin pages - Form.php, Coupon.php, Access.php: Restore classic assets when ?react=0 - Customer.php, Product.php, License.php: Add coexistence checks Now the toggle between Classic and React versions works correctly. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
182 lines
4.4 KiB
JavaScript
182 lines
4.4 KiB
JavaScript
/**
|
|
* WordPress dependencies
|
|
*/
|
|
import isShallowEqual from '@wordpress/is-shallow-equal';
|
|
|
|
/** @typedef {import('./types').HistoryRecord} HistoryRecord */
|
|
/** @typedef {import('./types').HistoryChange} HistoryChange */
|
|
/** @typedef {import('./types').HistoryChanges} HistoryChanges */
|
|
/** @typedef {import('./types').UndoManager} UndoManager */
|
|
|
|
/**
|
|
* Merge changes for a single item into a record of changes.
|
|
*
|
|
* @param {Record< string, HistoryChange >} changes1 Previous changes
|
|
* @param {Record< string, HistoryChange >} changes2 NextChanges
|
|
*
|
|
* @return {Record< string, HistoryChange >} Merged changes
|
|
*/
|
|
function mergeHistoryChanges( changes1, changes2 ) {
|
|
/**
|
|
* @type {Record< string, HistoryChange >}
|
|
*/
|
|
const newChanges = { ...changes1 };
|
|
Object.entries( changes2 ).forEach( ( [ key, value ] ) => {
|
|
if ( newChanges[ key ] ) {
|
|
newChanges[ key ] = { ...newChanges[ key ], to: value.to };
|
|
} else {
|
|
newChanges[ key ] = value;
|
|
}
|
|
} );
|
|
|
|
return newChanges;
|
|
}
|
|
|
|
/**
|
|
* Adds history changes for a single item into a record of changes.
|
|
*
|
|
* @param {HistoryRecord} record The record to merge into.
|
|
* @param {HistoryChanges} changes The changes to merge.
|
|
*/
|
|
const addHistoryChangesIntoRecord = ( record, changes ) => {
|
|
const existingChangesIndex = record?.findIndex(
|
|
( { id: recordIdentifier } ) => {
|
|
return typeof recordIdentifier === 'string'
|
|
? recordIdentifier === changes.id
|
|
: isShallowEqual( recordIdentifier, changes.id );
|
|
}
|
|
);
|
|
const nextRecord = [ ...record ];
|
|
|
|
if ( existingChangesIndex !== -1 ) {
|
|
// If the edit is already in the stack leave the initial "from" value.
|
|
nextRecord[ existingChangesIndex ] = {
|
|
id: changes.id,
|
|
changes: mergeHistoryChanges(
|
|
nextRecord[ existingChangesIndex ].changes,
|
|
changes.changes
|
|
),
|
|
};
|
|
} else {
|
|
nextRecord.push( changes );
|
|
}
|
|
return nextRecord;
|
|
};
|
|
|
|
/**
|
|
* Creates an undo manager.
|
|
*
|
|
* @return {UndoManager} Undo manager.
|
|
*/
|
|
export function createUndoManager() {
|
|
/**
|
|
* @type {HistoryRecord[]}
|
|
*/
|
|
let history = [];
|
|
/**
|
|
* @type {HistoryRecord}
|
|
*/
|
|
let stagedRecord = [];
|
|
/**
|
|
* @type {number}
|
|
*/
|
|
let offset = 0;
|
|
|
|
const dropPendingRedos = () => {
|
|
history = history.slice( 0, offset || undefined );
|
|
offset = 0;
|
|
};
|
|
|
|
const appendStagedRecordToLatestHistoryRecord = () => {
|
|
const index = history.length === 0 ? 0 : history.length - 1;
|
|
let latestRecord = history[ index ] ?? [];
|
|
stagedRecord.forEach( ( changes ) => {
|
|
latestRecord = addHistoryChangesIntoRecord( latestRecord, changes );
|
|
} );
|
|
stagedRecord = [];
|
|
history[ index ] = latestRecord;
|
|
};
|
|
|
|
/**
|
|
* Checks whether a record is empty.
|
|
* A record is considered empty if it the changes keep the same values.
|
|
* Also updates to function values are ignored.
|
|
*
|
|
* @param {HistoryRecord} record
|
|
* @return {boolean} Whether the record is empty.
|
|
*/
|
|
const isRecordEmpty = ( record ) => {
|
|
const filteredRecord = record.filter( ( { changes } ) => {
|
|
return Object.values( changes ).some(
|
|
( { from, to } ) =>
|
|
typeof from !== 'function' &&
|
|
typeof to !== 'function' &&
|
|
! isShallowEqual( from, to )
|
|
);
|
|
} );
|
|
return ! filteredRecord.length;
|
|
};
|
|
|
|
return {
|
|
/**
|
|
* Record changes into the history.
|
|
*
|
|
* @param {HistoryRecord=} record A record of changes to record.
|
|
* @param {boolean} isStaged Whether to immediately create an undo point or not.
|
|
*/
|
|
addRecord( record, isStaged = false ) {
|
|
const isEmpty = ! record || isRecordEmpty( record );
|
|
if ( isStaged ) {
|
|
if ( isEmpty ) {
|
|
return;
|
|
}
|
|
record.forEach( ( changes ) => {
|
|
stagedRecord = addHistoryChangesIntoRecord(
|
|
stagedRecord,
|
|
changes
|
|
);
|
|
} );
|
|
} else {
|
|
dropPendingRedos();
|
|
if ( stagedRecord.length ) {
|
|
appendStagedRecordToLatestHistoryRecord();
|
|
}
|
|
if ( isEmpty ) {
|
|
return;
|
|
}
|
|
history.push( record );
|
|
}
|
|
},
|
|
|
|
undo() {
|
|
if ( stagedRecord.length ) {
|
|
dropPendingRedos();
|
|
appendStagedRecordToLatestHistoryRecord();
|
|
}
|
|
const undoRecord = history[ history.length - 1 + offset ];
|
|
if ( ! undoRecord ) {
|
|
return;
|
|
}
|
|
offset -= 1;
|
|
return undoRecord;
|
|
},
|
|
|
|
redo() {
|
|
const redoRecord = history[ history.length + offset ];
|
|
if ( ! redoRecord ) {
|
|
return;
|
|
}
|
|
offset += 1;
|
|
return redoRecord;
|
|
},
|
|
|
|
hasUndo() {
|
|
return !! history[ history.length - 1 + offset ];
|
|
},
|
|
|
|
hasRedo() {
|
|
return !! history[ history.length + offset ];
|
|
},
|
|
};
|
|
}
|