diff --git a/src/editor/components/StylesBorder.js b/src/editor/components/StylesBorder.js
index 12c4067..7e2b275 100644
--- a/src/editor/components/StylesBorder.js
+++ b/src/editor/components/StylesBorder.js
@@ -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
*
@@ -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 (
<>
- { __( 'Border', 'themer' ) }
+ { __( 'Border and radius', 'themer' ) }
-
+
+
+
+ { __( 'Border', 'themer' ) }
+
+
+
+
+
+ { __( 'Radius', 'themer' ) }
+
+
+
+
>
);
};
diff --git a/src/editor/components/StylesBorderRadius.js b/src/editor/components/StylesBorderRadius.js
new file mode 100644
index 0000000..746a6a9
--- /dev/null
+++ b/src/editor/components/StylesBorderRadius.js
@@ -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 (
+ <>
+
+ { hasLinkedValues ? (
+
+
+
+
+
+
+ ) : (
+
+
+ onChange(
+ handleUnlinkedValueChange(
+ topLeftValue,
+ 'topLeft'
+ )
+ )
+ }
+ value={ value?.topLeft ?? value }
+ />
+
+ onChange(
+ handleUnlinkedValueChange(
+ topRightValue,
+ 'topRight'
+ )
+ )
+ }
+ value={ value?.topRight ?? value }
+ />
+
+ onChange(
+ handleUnlinkedValueChange(
+ bottomLeftValue,
+ 'bottomLeft'
+ )
+ )
+ }
+ value={ value?.bottomLeft ?? value }
+ />
+
+ onChange(
+ handleUnlinkedValueChange(
+ bottomRightValue,
+ 'bottomRight'
+ )
+ )
+ }
+ value={ value?.bottomRight ?? value }
+ />
+
+ ) }
+
+
+ >
+ );
+};
+
+export default BorderRadius;