-
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
Themes: Support providing border radius presets #67544
base: trunk
Are you sure you want to change the base?
Changes from 12 commits
e0b4e47
01db75a
875dce4
f3b4abe
92df5cf
21b032d
e7b603d
ee37bda
9bb6c03
7ee1523
aa60fa3
bd11946
d01da07
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,54 +3,71 @@ | |
*/ | ||
import { | ||
BaseControl, | ||
RangeControl, | ||
__experimentalParseQuantityAndUnitFromRawValue as parseQuantityAndUnitFromRawValue, | ||
__experimentalUseCustomUnits as useCustomUnits, | ||
__experimentalVStack as VStack, | ||
__experimentalHStack as HStack, | ||
} from '@wordpress/components'; | ||
import { useState } from '@wordpress/element'; | ||
import { useState, useMemo } from '@wordpress/element'; | ||
import { __ } from '@wordpress/i18n'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import AllInputControl from './all-input-control'; | ||
import InputControls from './input-controls'; | ||
import LinkedButton from './linked-button'; | ||
import { useSettings } from '../use-settings'; | ||
import { | ||
getAllValue, | ||
getAllUnit, | ||
hasDefinedValues, | ||
hasMixedValues, | ||
} from './utils'; | ||
import { hasDefinedValues, hasMixedValues } from './utils'; | ||
import SingleInputControl from './single-input-control'; | ||
|
||
const DEFAULT_VALUES = { | ||
topLeft: undefined, | ||
topRight: undefined, | ||
bottomLeft: undefined, | ||
bottomRight: undefined, | ||
}; | ||
const MIN_BORDER_RADIUS_VALUE = 0; | ||
const MAX_BORDER_RADIUS_VALUES = { | ||
px: 100, | ||
em: 20, | ||
rem: 20, | ||
}; | ||
const RANGE_CONTROL_MAX_SIZE = 8; | ||
const EMPTY_ARRAY = []; | ||
function useBorderRadiusSizes( presets ) { | ||
const defaultSizes = presets?.default ?? EMPTY_ARRAY; | ||
const customSizes = presets?.custom ?? EMPTY_ARRAY; | ||
const themeSizes = presets?.theme ?? EMPTY_ARRAY; | ||
|
||
return useMemo( () => { | ||
const sizes = [ | ||
{ name: __( 'None' ), slug: '0', size: 0 }, | ||
...customSizes, | ||
...themeSizes, | ||
...defaultSizes, | ||
]; | ||
|
||
return sizes.length > RANGE_CONTROL_MAX_SIZE | ||
? [ | ||
{ | ||
name: __( 'Default' ), | ||
slug: 'default', | ||
size: undefined, | ||
}, | ||
...sizes, | ||
] | ||
: sizes; | ||
}, [ customSizes, themeSizes, defaultSizes ] ); | ||
} | ||
|
||
/** | ||
* Control to display border radius options. | ||
* | ||
* @param {Object} props Component props. | ||
* @param {Function} props.onChange Callback to handle onChange. | ||
* @param {Object} props.values Border radius values. | ||
* @param {Object} props.presets Border radius presets. | ||
* | ||
* @return {Element} Custom border radius control. | ||
*/ | ||
export default function BorderRadiusControl( { onChange, values } ) { | ||
export default function BorderRadiusControl( { onChange, values, presets } ) { | ||
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. This component was a nightmare to refactor and update to support the presets. I'm really surprised about this and thought by this point, we'd have most of the UI components covered. I tried to replicate what we do for the "spacing sizes" a bit but I believe that component should be extracted and unified as a generic UI component for any "property" that has "sides/corders" and supports "presets". 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. From memory, this border radius component followed the original box control component although it had some different requirements like layout and an in-flux design. I have a feeling the spacing sizes component followed a similar path but evolved differently too. The proposed generic UI component sounds good but whoever picks it up might face similar friction. |
||
const [ isLinked, setIsLinked ] = useState( | ||
! hasDefinedValues( values ) || ! hasMixedValues( values ) | ||
); | ||
|
||
const options = useBorderRadiusSizes( presets ); | ||
// Tracking selected units via internal state allows filtering of CSS unit | ||
// only values from being saved while maintaining preexisting unit selection | ||
// behaviour. Filtering CSS unit only values prevents invalid style values. | ||
|
@@ -72,64 +89,49 @@ export default function BorderRadiusControl( { onChange, values } ) { | |
availableUnits: availableUnits || [ 'px', 'em', 'rem' ], | ||
} ); | ||
|
||
const unit = getAllUnit( selectedUnits ); | ||
const unitConfig = units && units.find( ( item ) => item.value === unit ); | ||
const step = unitConfig?.step || 1; | ||
|
||
const [ allValue ] = parseQuantityAndUnitFromRawValue( | ||
getAllValue( values ) | ||
); | ||
|
||
const toggleLinked = () => setIsLinked( ! isLinked ); | ||
|
||
const handleSliderChange = ( next ) => { | ||
onChange( next !== undefined ? `${ next }${ unit }` : undefined ); | ||
}; | ||
|
||
return ( | ||
<fieldset className="components-border-radius-control"> | ||
<BaseControl.VisualLabel as="legend"> | ||
{ __( 'Radius' ) } | ||
</BaseControl.VisualLabel> | ||
<div className="components-border-radius-control__wrapper"> | ||
{ isLinked ? ( | ||
<> | ||
<AllInputControl | ||
className="components-border-radius-control__unit-control" | ||
values={ values } | ||
min={ MIN_BORDER_RADIUS_VALUE } | ||
onChange={ onChange } | ||
selectedUnits={ selectedUnits } | ||
setSelectedUnits={ setSelectedUnits } | ||
units={ units } | ||
/> | ||
<RangeControl | ||
__next40pxDefaultSize | ||
label={ __( 'Border radius' ) } | ||
hideLabelFromVision | ||
className="components-border-radius-control__range-control" | ||
value={ allValue ?? '' } | ||
min={ MIN_BORDER_RADIUS_VALUE } | ||
max={ MAX_BORDER_RADIUS_VALUES[ unit ] } | ||
initialPosition={ 0 } | ||
withInputField={ false } | ||
onChange={ handleSliderChange } | ||
step={ step } | ||
__nextHasNoMarginBottom | ||
/> | ||
</> | ||
) : ( | ||
<InputControls | ||
min={ MIN_BORDER_RADIUS_VALUE } | ||
<HStack className="components-border-radius-control__header"> | ||
<BaseControl.VisualLabel as="legend"> | ||
{ __( 'Radius' ) } | ||
</BaseControl.VisualLabel> | ||
<LinkedButton onClick={ toggleLinked } isLinked={ isLinked } /> | ||
</HStack> | ||
{ isLinked ? ( | ||
<> | ||
<SingleInputControl | ||
onChange={ onChange } | ||
selectedUnits={ selectedUnits } | ||
setSelectedUnits={ setSelectedUnits } | ||
values={ values || DEFAULT_VALUES } | ||
values={ values } | ||
units={ units } | ||
corner="all" | ||
presets={ options } | ||
/> | ||
) } | ||
<LinkedButton onClick={ toggleLinked } isLinked={ isLinked } /> | ||
</div> | ||
</> | ||
) : ( | ||
<VStack> | ||
{ [ | ||
'topLeft', | ||
'topRight', | ||
'bottomLeft', | ||
'bottomRight', | ||
].map( ( corner ) => ( | ||
<SingleInputControl | ||
key={ corner } | ||
onChange={ onChange } | ||
selectedUnits={ selectedUnits } | ||
setSelectedUnits={ setSelectedUnits } | ||
values={ values || DEFAULT_VALUES } | ||
units={ units } | ||
corner={ corner } | ||
presets={ options } | ||
/> | ||
) ) } | ||
</VStack> | ||
) } | ||
</fieldset> | ||
); | ||
} |
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.
This struck me as a little odd. Is this back-to-front as it appears to spread user, theme, then default sizes?
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.
I've probably gotten lost along the way but it appears there was custom merging for spacing sizes added in #61842 while adding support for a
defaultSpacingSizes
option. This approach moved merged spacing sizes under an origin key.It doesn't appear that has been implemented for radius sizes and might be a factor in some of the bugs encountered in testing.
At this stage, it doesn't look like we'll add default border radii to the core theme.json. Without that, we probably don't need a border-radius equivalent to
defaultSpacingSizes
or custom merging of presets under origin keys.If I'm wrong about whether we'll add default border radii presets to core theme.json, implementing a
defaultRadiusSizes
option and merging might require a theme.json version bump like the spacing changes needed.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.
🤦 Nevermind me. I see now where the presets are keyed by origin.
My questions now are:
defaultRadiusSizes
option?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.
Currently, when default radius sizes are defined within
lib/theme.json
(or viaget_core_data
filter) and a theme redefines any of those sizes using the same slug both are included in thesizes
options calculated here. That issue might be exacerbated if we ever allowed user origin definitions (customSizes
).This explains part of what I was seeing while testing.
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.
I don't know, wanted to start small. I'll defer to designers
What does this mean?
I think I copied from the spacing, I guess the idea is that the user ones appears first in the list.
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.
Fair enough 👍
I meant that if the different origins contain a preset item with the same slug, the incoming value overrides the original as the spacing presets do here.
That makes sense.
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.
Why is this done in the server and not in the client for spacing sizes? This seems like it will hide presets from the global styles UI (assuming one can edit the presets...)
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.
That's context I don't have yet, I'm afraid. My hunch is that it might be due to the calculation of spacing presets from the
spacingScale
setting.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.
Even that computation shouldn't be done entirely on the server.