-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
BoxControl: Convert to TypeScript #47622
Changes from 18 commits
51c052f
42e639c
527cab8
3a4e091
69df1ab
89768fd
2da9696
100ea1d
245d110
b38a4b4
e1960bd
325629e
11a6208
c049ba4
c1fe951
57390da
c6391b0
1aeed0c
f5c8246
8703310
56f225e
20f8fa6
c64c2a6
1b1d1e5
fbcf0e8
07f6cdf
23e6620
d4b7461
229db2c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import type { UnitControlProps } from '../unit-control/types'; | ||
import type { BoxControlInputControlProps } from './types'; | ||
import UnitControl from './unit-control'; | ||
import { | ||
LABELS, | ||
|
@@ -22,18 +24,20 @@ export default function AllInputControl( { | |
selectedUnits, | ||
setSelectedUnits, | ||
...props | ||
} ) { | ||
}: BoxControlInputControlProps ) { | ||
const allValue = getAllValue( values, selectedUnits, sides ); | ||
const hasValues = isValuesDefined( values ); | ||
const isMixed = hasValues && isValuesMixed( values, selectedUnits, sides ); | ||
const allPlaceholder = isMixed ? LABELS.mixed : null; | ||
const allPlaceholder = isMixed ? LABELS.mixed : undefined; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
|
||
const handleOnFocus = ( event ) => { | ||
const handleOnFocus: React.FocusEventHandler< HTMLInputElement > = ( | ||
event | ||
) => { | ||
onFocus( event, { side: 'all' } ); | ||
}; | ||
|
||
const handleOnChange = ( next ) => { | ||
const isNumeric = ! isNaN( parseFloat( next ) ); | ||
const handleOnChange: UnitControlProps[ 'onChange' ] = ( next ) => { | ||
const isNumeric = next !== undefined && ! isNaN( parseFloat( next ) ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just making the undefined case explicit. Should not change runtime behavior. |
||
const nextValue = isNumeric ? next : undefined; | ||
const nextValues = applyValueToSides( values, nextValue, sides ); | ||
|
||
|
@@ -42,7 +46,7 @@ export default function AllInputControl( { | |
|
||
// Set selected unit so it can be used as fallback by unlinked controls | ||
// when individual sides do not have a value containing a unit. | ||
const handleOnUnitChange = ( unit ) => { | ||
const handleOnUnitChange: UnitControlProps[ 'onUnitChange' ] = ( unit ) => { | ||
const newUnits = applyValueToSides( selectedUnits, unit, sides ); | ||
setSelectedUnits( newUnits ); | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,19 +29,50 @@ import { | |
isValuesDefined, | ||
} from './utils'; | ||
import { useControlledState } from '../utils/hooks'; | ||
import type { | ||
BoxControlIconProps, | ||
BoxControlProps, | ||
BoxControlValue, | ||
} from './types'; | ||
|
||
const defaultInputProps = { | ||
min: 0, | ||
}; | ||
|
||
const noop = () => {}; | ||
|
||
function useUniqueId( idProp ) { | ||
function useUniqueId( idProp?: string ) { | ||
const instanceId = useInstanceId( BoxControl, 'inspector-box-control' ); | ||
|
||
return idProp || instanceId; | ||
} | ||
export default function BoxControl( { | ||
|
||
/** | ||
* BoxControl components let users set values for Top, Right, Bottom, and Left. | ||
* This can be used as an input control for values like `padding` or `margin`. | ||
* | ||
* ```jsx | ||
* import { __experimentalBoxControl as BoxControl } from '@wordpress/components'; | ||
* import { useState } from '@wordpress/element'; | ||
* | ||
* const Example = () => { | ||
* const [ values, setValues ] = useState( { | ||
* top: '50px', | ||
* left: '10%', | ||
* right: '10%', | ||
* bottom: '50px', | ||
* } ); | ||
* | ||
* return ( | ||
* <BoxControl | ||
* values={ values } | ||
* onChange={ ( nextValues ) => setValues( nextValues ) } | ||
* /> | ||
* ); | ||
* }; | ||
* ``` | ||
*/ | ||
function BoxControl( { | ||
id: idProp, | ||
inputProps = defaultInputProps, | ||
onChange = noop, | ||
|
@@ -54,7 +85,7 @@ export default function BoxControl( { | |
resetValues = DEFAULT_VALUES, | ||
onMouseOver, | ||
onMouseOut, | ||
} ) { | ||
}: BoxControlProps ) { | ||
const [ values, setValues ] = useControlledState( valuesProp, { | ||
fallback: DEFAULT_VALUES, | ||
} ); | ||
|
@@ -67,14 +98,14 @@ export default function BoxControl( { | |
! hasInitialValue || ! isValuesMixed( inputValues ) || hasOneSide | ||
); | ||
|
||
const [ side, setSide ] = useState( | ||
const [ side, setSide ] = useState< BoxControlIconProps[ 'side' ] >( | ||
getInitialSide( isLinked, splitOnAxis ) | ||
); | ||
|
||
// Tracking selected units via internal state allows filtering of CSS unit | ||
// only values from being saved while maintaining preexisting unit selection | ||
// behaviour. Filtering CSS only values prevents invalid style values. | ||
const [ selectedUnits, setSelectedUnits ] = useState( { | ||
const [ selectedUnits, setSelectedUnits ] = useState< BoxControlValue >( { | ||
top: parseQuantityAndUnitFromRawValue( valuesProp?.top )[ 1 ], | ||
right: parseQuantityAndUnitFromRawValue( valuesProp?.right )[ 1 ], | ||
bottom: parseQuantityAndUnitFromRawValue( valuesProp?.bottom )[ 1 ], | ||
|
@@ -89,11 +120,14 @@ export default function BoxControl( { | |
setSide( getInitialSide( ! isLinked, splitOnAxis ) ); | ||
}; | ||
|
||
const handleOnFocus = ( event, { side: nextSide } ) => { | ||
const handleOnFocus = ( | ||
_event: React.FocusEvent< HTMLInputElement >, | ||
{ side: nextSide }: { side: typeof side } | ||
) => { | ||
setSide( nextSide ); | ||
}; | ||
|
||
const handleOnChange = ( nextValues ) => { | ||
const handleOnChange = ( nextValues: BoxControlValue ) => { | ||
onChange( nextValues ); | ||
setValues( nextValues ); | ||
setIsDirty( true ); | ||
|
@@ -132,7 +166,7 @@ export default function BoxControl( { | |
<FlexItem> | ||
<Button | ||
className="component-box-control__reset-button" | ||
isSecondary | ||
variant="secondary" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated a deprecated prop. |
||
isSmall | ||
onClick={ handleOnReset } | ||
disabled={ ! isDirty } | ||
|
@@ -176,3 +210,4 @@ export default function BoxControl( { | |
} | ||
|
||
export { applyValueToSides } from './utils'; | ||
export default BoxControl; |
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -5,6 +5,8 @@ import UnitControl from './unit-control'; | |||||||||||||||
import { parseQuantityAndUnitFromRawValue } from '../unit-control/utils'; | ||||||||||||||||
import { ALL_SIDES, LABELS } from './utils'; | ||||||||||||||||
import { LayoutContainer, Layout } from './styles/box-control-styles'; | ||||||||||||||||
import type { BoxControlInputControlProps, BoxControlValue } from './types'; | ||||||||||||||||
import type { UnitControlProps } from '../unit-control/types'; | ||||||||||||||||
|
||||||||||||||||
const noop = () => {}; | ||||||||||||||||
|
||||||||||||||||
|
@@ -18,29 +20,33 @@ export default function BoxInputControls( { | |||||||||||||||
setSelectedUnits, | ||||||||||||||||
sides, | ||||||||||||||||
...props | ||||||||||||||||
} ) { | ||||||||||||||||
const createHandleOnFocus = ( side ) => ( event ) => { | ||||||||||||||||
onFocus( event, { side } ); | ||||||||||||||||
}; | ||||||||||||||||
}: BoxControlInputControlProps ) { | ||||||||||||||||
const createHandleOnFocus = | ||||||||||||||||
( side: keyof BoxControlValue ) => | ||||||||||||||||
( event: React.FocusEvent< HTMLInputElement > ) => { | ||||||||||||||||
onFocus( event, { side } ); | ||||||||||||||||
}; | ||||||||||||||||
|
||||||||||||||||
const createHandleOnHoverOn = ( side ) => () => { | ||||||||||||||||
const createHandleOnHoverOn = ( side: keyof BoxControlValue ) => () => { | ||||||||||||||||
onHoverOn( { [ side ]: true } ); | ||||||||||||||||
}; | ||||||||||||||||
|
||||||||||||||||
const createHandleOnHoverOff = ( side ) => () => { | ||||||||||||||||
const createHandleOnHoverOff = ( side: keyof BoxControlValue ) => () => { | ||||||||||||||||
onHoverOff( { [ side ]: false } ); | ||||||||||||||||
}; | ||||||||||||||||
|
||||||||||||||||
const handleOnChange = ( nextValues ) => { | ||||||||||||||||
const handleOnChange = ( nextValues: BoxControlValue ) => { | ||||||||||||||||
onChange( nextValues ); | ||||||||||||||||
}; | ||||||||||||||||
|
||||||||||||||||
const createHandleOnChange = | ||||||||||||||||
const createHandleOnChange: ( | ||||||||||||||||
side: keyof BoxControlValue | ||||||||||||||||
) => UnitControlProps[ 'onChange' ] = | ||||||||||||||||
( side ) => | ||||||||||||||||
( next, { event } ) => { | ||||||||||||||||
const { altKey } = event; | ||||||||||||||||
const nextValues = { ...values }; | ||||||||||||||||
const isNumeric = ! isNaN( parseFloat( next ) ); | ||||||||||||||||
const isNumeric = | ||||||||||||||||
next !== undefined && ! isNaN( parseFloat( next ) ); | ||||||||||||||||
const nextValue = isNumeric ? next : undefined; | ||||||||||||||||
|
||||||||||||||||
nextValues[ side ] = nextValue; | ||||||||||||||||
|
@@ -49,7 +55,9 @@ export default function BoxInputControls( { | |||||||||||||||
* Supports changing pair sides. For example, holding the ALT key | ||||||||||||||||
* when changing the TOP will also update BOTTOM. | ||||||||||||||||
*/ | ||||||||||||||||
if ( altKey ) { | ||||||||||||||||
// @ts-expect-error - event.altKey is only present when the change event was | ||||||||||||||||
// triggered by a keyboard event. | ||||||||||||||||
if ( event.altKey ) { | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure it's a better solution, but we could avoid the
Suggested change
haven't tested it at runtime, though There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll add a TODO comment here because I think it should be addressed in a different layer, for example in how the upstream change handler passes events. c64c2a6 |
||||||||||||||||
switch ( side ) { | ||||||||||||||||
case 'top': | ||||||||||||||||
nextValues.bottom = nextValue; | ||||||||||||||||
|
@@ -69,11 +77,12 @@ export default function BoxInputControls( { | |||||||||||||||
handleOnChange( nextValues ); | ||||||||||||||||
}; | ||||||||||||||||
|
||||||||||||||||
const createHandleOnUnitChange = ( side ) => ( next ) => { | ||||||||||||||||
const newUnits = { ...selectedUnits }; | ||||||||||||||||
newUnits[ side ] = next; | ||||||||||||||||
setSelectedUnits( newUnits ); | ||||||||||||||||
}; | ||||||||||||||||
const createHandleOnUnitChange = | ||||||||||||||||
( side: keyof BoxControlValue ) => ( next?: string ) => { | ||||||||||||||||
const newUnits = { ...selectedUnits }; | ||||||||||||||||
newUnits[ side ] = next; | ||||||||||||||||
setSelectedUnits( newUnits ); | ||||||||||||||||
}; | ||||||||||||||||
|
||||||||||||||||
// Filter sides if custom configuration provided, maintaining default order. | ||||||||||||||||
const filteredSides = sides?.length | ||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These props are actually passed through to UnitControl, not InputControl. Why does it matter?
InputControlProps[ 'max' ]
isstring
butUnitControlProps[ 'max' ]
isnumber
☠️There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😵💫 Hopefully this will be progressively addressed as we work our way up from
NumberControl