UNPKG

@wordpress/compose

Version:
102 lines (95 loc) 1.98 kB
/** * WordPress dependencies */ import { createUndoManager } from '@wordpress/undo-manager'; import { useCallback, useReducer } from '@wordpress/element'; import type { UndoManager } from '@wordpress/undo-manager'; type UndoRedoState< T > = { manager: UndoManager< T >; value: T; }; function undoRedoReducer< T >( state: UndoRedoState< T >, action: | { type: 'UNDO' } | { type: 'REDO' } | { type: 'RECORD'; value: T; isStaged: boolean } ): UndoRedoState< T > { switch ( action.type ) { case 'UNDO': { const undoRecord = state.manager.undo(); if ( undoRecord ) { return { ...state, value: undoRecord[ 0 ].changes.prop.from, }; } return state; } case 'REDO': { const redoRecord = state.manager.redo(); if ( redoRecord ) { return { ...state, value: redoRecord[ 0 ].changes.prop.to, }; } return state; } case 'RECORD': { state.manager.addRecord( [ { id: 'object', changes: { prop: { from: state.value, to: action.value }, }, }, ], action.isStaged ); return { ...state, value: action.value, }; } } return state; } function initReducer< T >( value: T ) { return { manager: createUndoManager(), value, }; } /** * useState with undo/redo history. * * @param initialValue Initial value. * @return Value, setValue, hasUndo, hasRedo, undo, redo. */ export default function useStateWithHistory< T >( initialValue: T ) { const [ state, dispatch ] = useReducer( undoRedoReducer, initialValue, initReducer ); return { value: state.value, setValue: useCallback( ( newValue: T, isStaged: boolean ) => { dispatch( { type: 'RECORD', value: newValue, isStaged, } ); }, [] ), hasUndo: state.manager.hasUndo(), hasRedo: state.manager.hasRedo(), undo: useCallback( () => { dispatch( { type: 'UNDO' } ); }, [] ), redo: useCallback( () => { dispatch( { type: 'REDO' } ); }, [] ), }; }