Skip to content

Commit

Permalink
Fix undo when changing padding values
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad committed Apr 21, 2022
1 parent 2f0f6de commit 1b5d80c
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 35 deletions.
40 changes: 10 additions & 30 deletions packages/components/src/input-control/input-field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import type { WordPressComponentProps } from '../ui/context';
import { useDragCursor } from './utils';
import { Input } from './styles/input-control-styles';
import { useInputControlStateReducer } from './reducer/reducer';
import { useUpdateEffect } from '../utils';
import type { InputFieldProps } from './types';

function InputField(
Expand Down Expand Up @@ -67,40 +66,21 @@ function InputField(
pressEnter,
pressUp,
reset,
} = useInputControlStateReducer( stateReducer, {
isDragEnabled,
value: valueProp,
isPressEnterToChange,
} );
} = useInputControlStateReducer(
stateReducer,
{
isDragEnabled,
value: valueProp,
isPressEnterToChange,
},
onChange
);

const { _event, value, isDragging, isDirty } = state;
const { value, isDragging, isDirty } = state;
const wasDirtyOnBlur = useRef( false );

const dragCursor = useDragCursor( isDragging, dragDirection );

/*
* Handles synchronization of external and internal value state.
* If not focused and did not hold a dirty value[1] on blur
* updates the value from the props. Otherwise if not holding
* a dirty value[1] propagates the value and event through onChange.
* [1] value is only made dirty if isPressEnterToChange is true
*/
useUpdateEffect( () => {
if ( valueProp === value ) {
return;
}
if ( ! isFocused && ! wasDirtyOnBlur.current ) {
commit( valueProp, _event as SyntheticEvent );
} else if ( ! isDirty ) {
onChange( value, {
event: _event as
| ChangeEvent< HTMLInputElement >
| PointerEvent< HTMLInputElement >,
} );
wasDirtyOnBlur.current = false;
}
}, [ value, isDirty, isFocused, valueProp ] );

const handleOnBlur = ( event: FocusEvent< HTMLInputElement > ) => {
onBlur( event );
setIsFocused?.( false );
Expand Down
43 changes: 38 additions & 5 deletions packages/components/src/input-control/reducer/reducer.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/**
* External dependencies
*/
import type { SyntheticEvent } from 'react';
import type { SyntheticEvent, ChangeEvent, PointerEvent } from 'react';

/**
* WordPress dependencies
*/
import { useReducer } from '@wordpress/element';
import { useReducer, useLayoutEffect, useRef } from '@wordpress/element';

/**
* Internal dependencies
Expand All @@ -18,6 +18,7 @@ import {
initialStateReducer,
} from './state';
import * as actions from './actions';
import type { InputChangeCallback } from '../types';

/**
* Prepares initialState for the reducer.
Expand Down Expand Up @@ -131,13 +132,15 @@ function inputControlStateReducer(
* This technique uses the "stateReducer" design pattern:
* https://kentcdodds.com/blog/the-state-reducer-pattern/
*
* @param stateReducer An external state reducer.
* @param initialState The initial state for the reducer.
* @param stateReducer An external state reducer.
* @param initialState The initial state for the reducer.
* @param onChangeHandler A handler for the onChange event.
* @return State, dispatch, and a collection of actions.
*/
export function useInputControlStateReducer(
stateReducer: StateReducer = initialStateReducer,
initialState: Partial< InputState > = initialInputControlState
initialState: Partial< InputState > = initialInputControlState,
onChangeHandler: InputChangeCallback
) {
const [ state, dispatch ] = useReducer< StateReducer >(
inputControlStateReducer( stateReducer ),
Expand Down Expand Up @@ -201,6 +204,36 @@ export function useInputControlStateReducer(
const pressDown = createKeyEvent( actions.PRESS_DOWN );
const pressEnter = createKeyEvent( actions.PRESS_ENTER );

const currentState = useRef( state );
const currentValueProp = useRef( initialState.value );
useLayoutEffect( () => {
currentState.current = state;
currentValueProp.current = initialState.value;
} );
useLayoutEffect( () => {
if (
state.value !== currentValueProp.current &&
! currentState.current.isDirty
) {
onChangeHandler( state.value ?? '', {
event: currentState.current._event as
| ChangeEvent< HTMLInputElement >
| PointerEvent< HTMLInputElement >,
} );
}
}, [ state.value ] );
useLayoutEffect( () => {
if (
initialState.value !== currentState.current.value &&
! currentState.current.isDirty
) {
reset(
initialState.value,
currentState.current._event as SyntheticEvent
);
}
}, [ initialState.value ] );

return {
change,
commit,
Expand Down

0 comments on commit 1b5d80c

Please sign in to comment.