Skip to content
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

[#113] - Radius not an option #117

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 37 additions & 7 deletions src/editor/components/StylesBorder.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import getThemeOption from '../../utils/get-theme-option';
import EditorContext from '../context/EditorContext';
import StylesContext from '../context/StylesContext';

import StylesBorderRadius from './StylesBorderRadius';

/**
* Reusable border control style component
*
Expand All @@ -23,21 +25,49 @@ const Border = ( { selector } ) => {
);

const onChange = ( newValue ) => {
// If the value has a radius, we need to merge it with the new value
const valueRadius = value?.radius;
let updatedValue = newValue;
if ( valueRadius ) {
updatedValue = {
...newValue,
radius: valueRadius,
};
}

// Set the new value in the user config
let config = structuredClone( userConfig );
config = set( config, selector, newValue );
config = set( config, selector, updatedValue );
setUserConfig( config );
};

return (
<>
<span className="themer--styles__item__title">
{ __( 'Border', 'themer' ) }
{ __( 'Border and radius', 'themer' ) }
</span>
<BorderBoxControl
colors={ themePalette }
onChange={ onChange }
value={ value }
/>
<div className="themer--styles__item__columns themer--styles__item__columns--2">
<div>
<span className="themer--styles__item__label">
{ __( 'Border', 'themer' ) }
</span>
<BorderBoxControl
colors={ themePalette }
onChange={ onChange }
value={ value }
/>
</div>
<div>
<span className="themer--styles__item__label">
{ __( 'Radius', 'themer' ) }
</span>
<StylesBorderRadius
selector={ `${ selector }.radius` }
onChange={ onChange }
value={ value }
/>
</div>
</div>
</>
);
};
Expand Down
221 changes: 221 additions & 0 deletions src/editor/components/StylesBorderRadius.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
import { set } from 'lodash';
import { __ } from '@wordpress/i18n';
import { useContext, useState } from '@wordpress/element';
import {
__experimentalUnitControl as UnitControl,
RangeControl,
__experimentalHStack as HStack,
__experimentalGrid as Grid,
Button,
} from '@wordpress/components';

import getThemeOption from '../../utils/get-theme-option';
import EditorContext from '../context/EditorContext';
import StylesContext from '../context/StylesContext';

/**
* Reusable border control style component
*
* @param {Object} props Component props
* @param {string} props.selector Property target selector
*/
const BorderRadius = ( { selector } ) => {
const { userConfig, themeConfig } = useContext( EditorContext );
const { setUserConfig } = useContext( StylesContext );
const value = getThemeOption( selector, themeConfig );

// Set initial state to determine if the values are linked or unlinked
const [ hasLinkedValues, setHasLinkedValues ] = useState(
typeof value === 'object' ? false : true
);

/**
* Handle changes to the border radius value
*
* @param {string} newValue The updated linked value
*/
const onChange = ( newValue ) => {
let config = structuredClone( userConfig );
config = set( config, selector, newValue );
setUserConfig( config );
};

/**
* Handle toggling between linked and unlinked values
*/
const handleToggleValueType = () => {
setHasLinkedValues( ! hasLinkedValues );

/**
* When switching to unlinked values, set each corner to the current value
*
* When switching to linked values, set the value to the topleft value (or the next defined value)
*/
if ( hasLinkedValues ) {
onChange( handleUnlinkedValueChange() );
} else {
onChange(
value?.topLeft ||
value?.topRight ||
value?.bottomRight ||
value?.bottomLeft
);
}
};

/**
* Handle value changes made via the range control
*
* @param {string} newValue The updated value
*/
const handleRangeValue = ( newValue ) => {
// Get the unit of measurement, or default to px
const valueUnit = value
? String( value ).replace( /[0-9.]/g, '' )
: 'px';

// Concatenate the new value with the unit of measurement (or use px if one isn't defined)
const updatedValue = newValue + ( valueUnit ?? 'px' );

// Update the value
onChange( updatedValue );
};

/**
* A function to handle changes to an unlinked value
*
* @param {string} newValue The updated value
* @param {string} position The border radius position
*
* @return {Object} The updated unlinked values
*/
const handleUnlinkedValueChange = ( newValue, position ) => {
// Define the structure of the unlinked values and set default values, if required
const unlinkedStructure = {
topLeft: value?.topLeft ?? value,
topRight: value?.topRight ?? value,
bottomLeft: value?.bottomLeft ?? value,
bottomRight: value?.bottomRight ?? value,
};

// If a value and position are not defined, return the strucure with default values
if ( ! newValue && ! position ) {
return unlinkedStructure;
}

// Insert in the updated value
const updatedValue = {
...unlinkedStructure,
[ position ]: newValue,
};

// Return the updated values
return updatedValue;
};

return (
<>
<HStack spacing="3">
{ hasLinkedValues ? (
<HStack spacing="3">
<div className="components-base-control components-input-control components-number-control components-unit-control components-unit-control-wrapper e1bagdl32 ep09it41 css-x5161z ej5x27r4">
<UnitControl
onChange={ onChange }
value={ value }
/>
</div>
<div className="components-base-control components-range-control css-mn3isp ej5x27r4">
<div className="components-base-control__field css-1sf3vf3 ej5x27r3">
<RangeControl
__nextHasNoMarginBottom
label={ __( 'Border radius' ) }
hideLabelFromVision
initialPosition={ 0 }
max={ 100 }
min={ 0 }
onChange={ handleRangeValue }
step={ 1 }
value={ parseInt( value ) || undefined }
withInputField={ false }
/>
</div>
</div>
</HStack>
) : (
<Grid alignment="bottom" columns={ 2 } gap={ 2 }>
<UnitControl
label={ __( 'Top left border radius', 'themer' ) }
hideLabelFromVision
onChange={ ( topLeftValue ) =>
onChange(
handleUnlinkedValueChange(
topLeftValue,
'topLeft'
)
)
}
value={ value?.topLeft ?? value }
/>
<UnitControl
label={ __( 'Top right border radius', 'themer' ) }
hideLabelFromVision
onChange={ ( topRightValue ) =>
onChange(
handleUnlinkedValueChange(
topRightValue,
'topRight'
)
)
}
value={ value?.topRight ?? value }
/>
<UnitControl
label={ __(
'Bottom left border radius',
'themer'
) }
hideLabelFromVision
onChange={ ( bottomLeftValue ) =>
onChange(
handleUnlinkedValueChange(
bottomLeftValue,
'bottomLeft'
)
)
}
value={ value?.bottomLeft ?? value }
/>
<UnitControl
label={ __(
'Bottom right border radius',
'themer'
) }
hideLabelFromVision
onChange={ ( bottomRightValue ) =>
onChange(
handleUnlinkedValueChange(
bottomRightValue,
'bottomRight'
)
)
}
value={ value?.bottomRight ?? value }
/>
</Grid>
) }
<Button
onClick={ handleToggleValueType }
icon={ hasLinkedValues ? 'admin-links' : 'editor-unlink' }
isSmall
label={
hasLinkedValues
? __( 'Unlink radii', 'themer' )
: __( 'Link radii', 'themer' )
}
></Button>
</HStack>
</>
);
};

export default BorderRadius;