import { __ } from '@wordpress/i18n';
import { link, linkOff } from '@wordpress/icons';
import { useContext, useState, useEffect, useRef } from '@wordpress/element';
import { BaseControl, Tooltip, Button } from '@wordpress/components';
import { applyFilters } from '@wordpress/hooks';
import { isEqual, isEmpty } from 'lodash';
import { ColorPicker } from '@edge22/components';
import PanelArea from '../../../../components/panel-area';
import getIcon from '../../../../utils/get-icon';
import ControlsContext from '../../../../block-context';
import getDeviceType from '../../../../utils/get-device-type';
import getResponsivePlaceholder from '../../../../utils/get-responsive-placeholder';
import DimensionsControl from '../../../../components/dimensions';
import FlexControl from '../../../../components/flex-control';
import UnitControl from '../../../../components/unit-control';
import StyleDropdown from './components/style-dropdown';
import isNumeric from '../../../../utils/is-numeric';
import { useStyleIndicator, useDeviceAttributes } from '../../../../hooks';
import { getContentAttribute } from '../../../../utils/get-content-attribute';
import './editor.scss';
export default function Borders( { attributes, setAttributes, computedStyles } ) {
const device = getDeviceType();
const { id, blockName, supports: { borders: bordersPanel } } = useContext( ControlsContext );
const [ deviceAttributes, setDeviceAttributes ] = useDeviceAttributes( attributes, setAttributes );
const borderRadiusAttributes = [ 'borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomLeftRadius', 'borderBottomRightRadius' ];
const borderAreas = [ 'borderTop', 'borderRight', 'borderBottom', 'borderLeft' ];
const borderLabels = {
borderTop: __( 'Top', 'generateblocks' ),
borderRight: __( 'Right', 'generateblocks' ),
borderBottom: __( 'Bottom', 'generateblocks' ),
borderLeft: __( 'Left', 'generateblocks' ),
};
const [ sync, setSync ] = useState( false );
const contentValue = getContentAttribute( attributes, blockName );
const panelControls = {
borderTopColor: false,
borderTopStyle: false,
borderTopWidth: false,
borderRightColor: false,
borderRightStyle: false,
borderRightWidth: false,
borderBottomColor: false,
borderBottomStyle: false,
borderBottomWidth: false,
borderLeftColor: false,
borderLeftStyle: false,
borderLeftWidth: false,
borderTopLeftRadius: false,
borderTopRightRadius: false,
borderBottomRightRadius: false,
borderBottomLeftRadius: false,
};
const {
dispatchControlGlobalStyle,
styleSources,
hasGlobalStyle,
contentWasUpdated,
} = useStyleIndicator( computedStyles, panelControls, contentValue, deviceAttributes );
const panelRef = useRef( null );
function getLabel( defaultLabel, rules ) {
return applyFilters(
'generateblocks.editor.control.label',
defaultLabel,
rules,
styleSources,
dispatchControlGlobalStyle,
contentWasUpdated,
);
}
const {
borderTopColor = '',
borderTopStyle = '',
borderTopWidth = '',
borderRightColor = '',
borderRightStyle = '',
borderRightWidth = '',
borderBottomColor = '',
borderBottomStyle = '',
borderBottomWidth = '',
borderLeftColor = '',
borderLeftStyle = '',
borderLeftWidth = '',
borderTopLeftRadius = '',
borderTopRightRadius = '',
borderBottomRightRadius = '',
borderBottomLeftRadius = '',
} = deviceAttributes.borders;
const labels = {
borders: getLabel(
__( 'Border', 'generateblocks' ),
{
borderTopColor,
borderTopStyle,
borderTopWidth,
borderRightColor,
borderRightStyle,
borderRightWidth,
borderBottomColor,
borderBottomStyle,
borderBottomWidth,
borderLeftColor,
borderLeftStyle,
borderLeftWidth,
}
),
borderRadius: getLabel(
__( 'Border Radius', 'generateblocks' ),
{
borderTopLeftRadius,
borderTopRightRadius,
borderBottomRightRadius,
borderBottomLeftRadius,
}
),
};
useEffect( () => {
const allValues = borderAreas.map( ( area ) => {
return Object.entries( deviceAttributes.borders ).reduce( ( newObject, [ key, value ] ) => {
if ( key.startsWith( area ) && value ) {
const newKey = key.replace( area, '' );
newObject = {
...newObject,
[ newKey ]: value,
};
}
return newObject;
}, {} );
} );
if (
4 === allValues.length &&
allValues.every( ( obj ) => ! isEmpty( obj ) && isEqual( obj, allValues[ 0 ] ) )
) {
setSync( true );
}
}, [] );
function manualSync() {
const areasWithWidth = borderAreas.filter( ( area ) => deviceAttributes.borders[ area + 'Width' ] || isNumeric( deviceAttributes.borders[ area + 'Width' ] ) );
if ( ! areasWithWidth.length ) {
return;
}
const firstArea = areasWithWidth[ 0 ];
const valuesToSync = Object.entries( deviceAttributes.borders ).reduce( ( newObject, [ key, value ] ) => {
if ( key.startsWith( firstArea ) ) {
const newKey = key.replace( firstArea, '' );
newObject[ newKey ] = value;
}
return newObject;
}, {} );
const newDeviceAttributes = Object.entries( valuesToSync ).reduce( ( newObject, [ key, value ] ) => {
borderAreas.forEach( ( area ) => {
newObject[ area + key ] = value;
} );
return newObject;
}, {} );
setDeviceAttributes( newDeviceAttributes, 'borders' );
}
return (
<PanelArea
title={ __( 'Borders', 'generateblocks' ) }
initialOpen={ false }
icon={ getIcon( 'borders' ) }
className="gblocks-panel-label"
ref={ panelRef }
hasGlobalStyle={ hasGlobalStyle }
id={ `${ id }Borders` }
>
{ ( bordersPanel.borderTop || bordersPanel.borderRight || bordersPanel.borderBottom || bordersPanel.borderLeft ) &&
<BaseControl
label={ labels.borders }
id={ 'gblocks-borderTop-width' }
className="gblocks-border-row"
>
<Tooltip text={ !! sync ? __( 'Unlink Sides', 'generateblocks' ) : __( 'Link Sides', 'generateblocks' ) } >
<Button
className="components-gblocks-dimensions-control_sync"
aria-label={ !! sync ? __( 'Unlink Sides', 'generateblocks' ) : __( 'Link Sides', 'generateblocks' ) }
variant={ !! sync ? 'primary' : '' }
aria-pressed={ !! sync }
onClick={ () => {
setSync( ! sync );
if ( ! sync ) {
manualSync();
}
} }
>
{ !! sync ? link : linkOff }
</Button>
</Tooltip>
{ borderAreas.map( ( borderArea, areaIndex ) => {
if ( ! bordersPanel[ borderArea ] ) {
return null;
}
if ( sync && areaIndex > 0 ) {
return null;
}
const iconBorderStyle = !! sync
? 'borderAll'
: borderArea;
return (
<FlexControl key={ borderArea }>
<Tooltip text={ !! sync ? __( 'All sides', 'generateblocks' ) : borderLabels[ borderArea ] }>
<div className={ 'gblocks-border-icon ' + iconBorderStyle } style={ { borderStyle: attributes.borders[ borderArea + 'Style' ] } }></div>
</Tooltip>
<UnitControl
id={ 'gblocks-' + borderArea + '-width' }
value={ deviceAttributes.borders[ borderArea + 'Width' ] || '' }
placeholder={ getResponsivePlaceholder( borderArea + 'Width', attributes.borders, device ) }
onChange={ ( value ) => {
const newAttributes = {
[ borderArea + 'Width' ]: value,
};
if ( sync ) {
newAttributes.borderRightWidth = value;
newAttributes.borderBottomWidth = value;
newAttributes.borderLeftWidth = value;
}
if ( ! value ) {
newAttributes[ borderArea + 'Style' ] = '';
if ( sync ) {
newAttributes.borderRightStyle = '';
newAttributes.borderBottomStyle = '';
newAttributes.borderLeftStyle = '';
}
} else if ( ! attributes.borders[ borderArea + 'Style' ] ) {
newAttributes[ borderArea + 'Style' ] = 'solid';
if ( sync ) {
newAttributes.borderRightStyle = 'solid';
newAttributes.borderBottomStyle = 'solid';
newAttributes.borderLeftStyle = 'solid';
}
}
setDeviceAttributes( newAttributes, 'borders' );
} }
/>
<StyleDropdown
value={ deviceAttributes.borders[ borderArea + 'Style' ] || getResponsivePlaceholder( borderArea + 'Style', attributes.borders, device ) }
onChange={ ( value ) => {
const newAttributes = {
[ borderArea + 'Style' ]: value,
};
if ( sync ) {
newAttributes.borderRightStyle = value;
newAttributes.borderBottomStyle = value;
newAttributes.borderLeftStyle = value;
}
setDeviceAttributes( newAttributes, 'borders' );
} }
/>
{ !! bordersPanel.borderColors.length && 'Desktop' === device &&
<div className="gblocks-border-colors">
{ bordersPanel.borderColors.map( ( borderColor, index ) => {
return (
<ColorPicker
key={ 'border' + index }
tooltip={ borderColor?.tooltip }
value={ attributes.borders[ borderArea + 'Color' + borderColor.state ] || '' }
alpha={ borderColor.alpha || false }
onChange={ ( nextBackgroundColor ) => {
const newAttributes = {
[ borderArea + 'Color' + borderColor.state ]: nextBackgroundColor,
};
if ( sync ) {
newAttributes[ 'borderRightColor' + borderColor.state ] = nextBackgroundColor;
newAttributes[ 'borderBottomColor' + borderColor.state ] = nextBackgroundColor;
newAttributes[ 'borderLeftColor' + borderColor.state ] = nextBackgroundColor;
}
setAttributes( {
borders: {
...newAttributes,
},
} );
} }
/>
);
} ) }
</div>
}
</FlexControl>
);
} ) }
</BaseControl>
}
{ bordersPanel.borderRadius &&
<DimensionsControl
label={ labels.borderRadius }
attributeNames={ borderRadiusAttributes }
values={ deviceAttributes.borders }
placeholders={ borderRadiusAttributes.reduce( ( o, key ) => (
{ ...o, [ key ]: getResponsivePlaceholder( key, attributes.borders, device, '' ) }
), {} ) }
onChange={ ( values ) => setDeviceAttributes( values, 'borders' ) }
/>
}
</PanelArea>
);
}