Skip to content

Commit

Permalink
Revert "Finish making InputControl et al. more controllable (#40568)"
Browse files Browse the repository at this point in the history
This reverts commit 769e69d.
  • Loading branch information
stokesman committed May 29, 2022
1 parent f1f4daa commit 0de33ab
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 178 deletions.
2 changes: 1 addition & 1 deletion packages/components/src/input-control/reducer/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const PRESS_UP = 'PRESS_UP';
export const RESET = 'RESET';

interface EventPayload {
event: SyntheticEvent;
event?: SyntheticEvent;
}

interface Action< Type, ExtraPayload = {} > {
Expand Down
49 changes: 29 additions & 20 deletions packages/components/src/input-control/reducer/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,6 @@ function inputControlStateReducer(
composedStateReducers: StateReducer
): StateReducer {
return ( state, action ) => {
// Updates state and returns early when there's no action type. These
// are controlled updates and need no exposure to additional reducers.
if ( ! ( 'type' in action ) ) {
return {
...state,
value: `${ action.value ?? '' }`,
};
}
const nextState = { ...state };

switch ( action.type ) {
Expand Down Expand Up @@ -106,7 +98,7 @@ function inputControlStateReducer(
case actions.RESET:
nextState.error = null;
nextState.isDirty = false;
nextState.value = action.payload.value ?? state.initialValue;
nextState.value = action.payload.value || state.initialValue;
break;

/**
Expand All @@ -117,6 +109,10 @@ function inputControlStateReducer(
break;
}

if ( action.payload.event ) {
nextState._event = action.payload.event;
}

/**
* Send the nextState + action to the composedReducers via
* this "bridge" mechanism. This allows external stateReducers
Expand Down Expand Up @@ -155,7 +151,15 @@ export function useInputControlStateReducer(
nextValue: actions.ChangeEventAction[ 'payload' ][ 'value' ],
event: actions.ChangeEventAction[ 'payload' ][ 'event' ]
) => {
refEvent.current = event;
/**
* Persist allows for the (Synthetic) event to be used outside of
* this function call.
* https://reactjs.org/docs/events.html#event-pooling
*/
if ( event && event.persist ) {
event.persist();
}

dispatch( {
type,
payload: { value: nextValue, event },
Expand All @@ -165,25 +169,30 @@ export function useInputControlStateReducer(
const createKeyEvent = ( type: actions.KeyEventAction[ 'type' ] ) => (
event: actions.KeyEventAction[ 'payload' ][ 'event' ]
) => {
refEvent.current = event;
/**
* Persist allows for the (Synthetic) event to be used outside of
* this function call.
* https://reactjs.org/docs/events.html#event-pooling
*/
if ( event && event.persist ) {
event.persist();
}

dispatch( { type, payload: { event } } );
};

const createDragEvent = ( type: actions.DragEventAction[ 'type' ] ) => (
payload: actions.DragEventAction[ 'payload' ]
) => {
refEvent.current = payload.event;
dispatch( { type, payload } );
};

/**
* Actions for the reducer
*/
const change = createChangeEvent( actions.CHANGE );
const invalidate = ( error: unknown, event: SyntheticEvent ) => {
refEvent.current = event;
const invalidate = ( error: unknown, event: SyntheticEvent ) =>
dispatch( { type: actions.INVALIDATE, payload: { error, event } } );
};
const reset = createChangeEvent( actions.RESET );
const commit = createChangeEvent( actions.COMMIT );

Expand All @@ -197,19 +206,17 @@ export function useInputControlStateReducer(

const currentState = useRef( state );
const currentValueProp = useRef( initialState.value );
const refEvent = useRef< SyntheticEvent | null >( null );
useLayoutEffect( () => {
currentState.current = state;
currentValueProp.current = initialState.value;
} );
useLayoutEffect( () => {
if (
refEvent.current &&
state.value !== currentValueProp.current &&
! currentState.current.isDirty
) {
onChangeHandler( state.value ?? '', {
event: refEvent.current as
event: currentState.current._event as
| ChangeEvent< HTMLInputElement >
| PointerEvent< HTMLInputElement >,
} );
Expand All @@ -220,9 +227,11 @@ export function useInputControlStateReducer(
initialState.value !== currentState.current.value &&
! currentState.current.isDirty
) {
dispatch( { value: initialState.value } );
reset(
initialState.value,
currentState.current._event as SyntheticEvent
);
}
if ( refEvent.current ) refEvent.current = null;
}, [ initialState.value ] );

return {
Expand Down
12 changes: 5 additions & 7 deletions packages/components/src/input-control/reducer/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,22 @@ import type { Reducer } from 'react';
import type { InputAction } from './actions';

export interface InputState {
_event: Event | {};
error: unknown;
initialValue: string;
initialValue?: string;
isDirty: boolean;
isDragEnabled: boolean;
isDragging: boolean;
isPressEnterToChange: boolean;
value: string;
value?: string;
}

export type StateReducer = Reducer<
InputState,
InputAction | Partial< InputState >
>;
export type SecondaryReducer = Reducer< InputState, InputAction >;
export type StateReducer = Reducer< InputState, InputAction >;

export const initialStateReducer: StateReducer = ( state: InputState ) => state;

export const initialInputControlState: InputState = {
_event: {},
error: null,
initialValue: '',
isDirty: false,
Expand Down
103 changes: 61 additions & 42 deletions packages/components/src/range-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import { useInstanceId } from '@wordpress/compose';
import BaseControl from '../base-control';
import Button from '../button';
import Icon from '../icon';
import { COLORS, useControlledState } from '../utils';
import { useUnimpededRangedNumberEntry } from './utils';
import { COLORS } from '../utils';
import { floatClamp, useControlledRangeValue } from './utils';
import InputRange from './input-range';
import RangeRail from './rail';
import SimpleTooltip from './tooltip';
Expand Down Expand Up @@ -70,10 +70,13 @@ function RangeControl(
},
ref
) {
const isResetPendent = useRef( false );
const [ value, setValue ] = useControlledState( valueProp, {
fallback: null,
const [ value, setValue ] = useControlledRangeValue( {
min,
max,
value: valueProp,
initial: initialPosition,
} );
const isResetPendent = useRef( false );

if ( step === 'any' ) {
// The tooltip and number input field are hidden when the step is "any"
Expand All @@ -99,15 +102,15 @@ function RangeControl(
const isThumbFocused = ! disabled && isFocused;

const isValueReset = value === null;
const usedValue = isValueReset
? resetFallbackValue ?? initialPosition
: value ?? currentInput;
const currentValue = value !== undefined ? value : currentInput;

const fillPercent = `${
usedValue === null || usedValue === undefined
? 50
: ( ( clamp( usedValue, min, max ) - min ) / ( max - min ) ) * 100
}%`;
const inputSliderValue = isValueReset ? '' : currentValue;

const rangeFillValue = isValueReset ? ( max - min ) / 2 + min : value;

const calculatedFillValue = ( ( value - min ) / ( max - min ) ) * 100;
const fillValue = isValueReset ? 50 : calculatedFillValue;
const fillValueOffset = `${ clamp( fillValue, 0, 100 ) }%`;

const classes = classnames( 'components-range-control', className );

Expand All @@ -126,20 +129,23 @@ function RangeControl(
onChange( nextValue );
};

const someNumberInputProps = useUnimpededRangedNumberEntry( {
max,
min,
value: usedValue ?? '',
onChange: ( nextValue ) => {
if ( ! isNaN( nextValue ) ) {
setValue( nextValue );
onChange( nextValue );
isResetPendent.current = false;
} else if ( allowReset ) {
isResetPendent.current = true;
const handleOnChange = ( nextValue ) => {
nextValue = parseFloat( nextValue );
setValue( nextValue );
/*
* Calls onChange only when nextValue is numeric
* otherwise may queue a reset for the blur event.
*/
if ( ! isNaN( nextValue ) ) {
if ( nextValue < min || nextValue > max ) {
nextValue = floatClamp( nextValue, min, max );
}
},
} );
onChange( nextValue );
isResetPendent.current = false;
} else if ( allowReset ) {
isResetPendent.current = true;
}
};

const handleOnInputNumberBlur = () => {
if ( isResetPendent.current ) {
Expand All @@ -149,20 +155,30 @@ function RangeControl(
};

const handleOnReset = () => {
const resetValue = parseFloat( resetFallbackValue );
let resetValue = parseFloat( resetFallbackValue );
let onChangeResetValue = resetValue;

if ( isNaN( resetValue ) ) {
setValue( null );
/*
* If the value is reset without a resetFallbackValue, the onChange
* callback receives undefined as that was the behavior when the
* component was stablized.
*/
onChange( undefined );
} else {
setValue( resetValue );
onChange( resetValue );
resetValue = null;
onChangeResetValue = undefined;
}

setValue( resetValue );

/**
* Previously, this callback would always receive undefined as
* an argument. This behavior is unexpected, specifically
* when resetFallbackValue is defined.
*
* The value of undefined is not ideal. Passing it through
* to internal <input /> elements would change it from a
* controlled component to an uncontrolled component.
*
* For now, to minimize unexpected regressions, we're going to
* preserve the undefined callback argument, except when a
* resetFallbackValue is defined.
*/
onChange( onChangeResetValue );
};

const handleShowTooltip = () => setShowTooltip( true );
Expand All @@ -181,7 +197,7 @@ function RangeControl(
};

const offsetStyle = {
[ isRTL() ? 'right' : 'left' ]: fillPercent,
[ isRTL() ? 'right' : 'left' ]: fillValueOffset,
};

return (
Expand Down Expand Up @@ -219,7 +235,7 @@ function RangeControl(
onMouseLeave={ onMouseLeave }
ref={ setRef }
step={ step }
value={ usedValue ?? '' }
value={ inputSliderValue }
/>
<RangeRail
aria-hidden={ true }
Expand All @@ -229,13 +245,13 @@ function RangeControl(
min={ min }
railColor={ railColor }
step={ step }
value={ usedValue }
value={ rangeFillValue }
/>
<Track
aria-hidden={ true }
className="components-range-control__track"
disabled={ disabled }
style={ { width: fillPercent } }
style={ { width: fillValueOffset } }
trackColor={ trackColor }
/>
<ThumbWrapper style={ offsetStyle } disabled={ disabled }>
Expand Down Expand Up @@ -269,10 +285,13 @@ function RangeControl(
disabled={ disabled }
inputMode="decimal"
isShiftStepEnabled={ isShiftStepEnabled }
max={ max }
min={ min }
onBlur={ handleOnInputNumberBlur }
onChange={ handleOnChange }
shiftStep={ shiftStep }
step={ step }
{ ...someNumberInputProps }
value={ inputSliderValue }
/>
) }
{ allowReset && (
Expand Down
4 changes: 2 additions & 2 deletions packages/components/src/range-control/rail.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default function RangeRail( {
min = 0,
max = 100,
step = 1,
value,
value = 0,
...restProps
} ) {
return (
Expand All @@ -29,7 +29,7 @@ export default function RangeRail( {
min={ min }
max={ max }
step={ step }
value={ value ?? ( max - min ) / 2 + min }
value={ value }
/>
) }
</>
Expand Down
Loading

0 comments on commit 0de33ab

Please sign in to comment.