index.js
130 lines
| 1 | /** |
| 2 | * External dependencies |
| 3 | */ |
| 4 | import { __ } from '@wordpress/i18n'; |
| 5 | import { BaseControl, Button, Tooltip } from '@wordpress/components'; |
| 6 | import { link, linkOff } from '@wordpress/icons'; |
| 7 | import { useState, useEffect } from '@wordpress/element'; |
| 8 | |
| 9 | /** |
| 10 | * Internal dependencies |
| 11 | */ |
| 12 | import './editor.scss'; |
| 13 | import UnitControl from '../unit-control'; |
| 14 | import { labels } from './labels'; |
| 15 | import isNumeric from '../../utils/is-numeric'; |
| 16 | |
| 17 | export default function Dimensions( props ) { |
| 18 | const { |
| 19 | label = __( 'Padding', 'generateblocks' ), |
| 20 | units = [], |
| 21 | attributeNames = [], |
| 22 | values, |
| 23 | placeholders, |
| 24 | onChange, |
| 25 | } = props; |
| 26 | |
| 27 | // values can have other attributes not related to this component. |
| 28 | // This builds an object of values related to this component. |
| 29 | const attributes = attributeNames.reduce( ( o, k ) => { |
| 30 | o[ k ] = values[ k ]; |
| 31 | |
| 32 | return o; |
| 33 | }, {} ); |
| 34 | |
| 35 | const [ sync, setSync ] = useState( false ); |
| 36 | const [ lastFocused, setLastFocused ] = useState( '' ); |
| 37 | |
| 38 | useEffect( () => { |
| 39 | const areAllValuesEqual = ( arr ) => arr.length === attributeNames.length && arr.every( ( value ) => value === arr[ 0 ] ); |
| 40 | const attributeValues = Object.values( attributes ).filter( ( n ) => n ); |
| 41 | setSync( areAllValuesEqual( attributeValues ) ); |
| 42 | }, [ JSON.stringify( attributes ) ] ); |
| 43 | |
| 44 | const syncUnits = () => { |
| 45 | const sides = [ ...attributeNames ].reverse(); |
| 46 | |
| 47 | const firstValue = sides.reduce( ( result, key ) => { |
| 48 | return attributes[ key ] || isNumeric( attributes[ key ] ) ? attributes[ key ] : result; |
| 49 | }, '' ); |
| 50 | |
| 51 | if ( ! firstValue ) { |
| 52 | setSync( ! sync ); |
| 53 | |
| 54 | return; |
| 55 | } |
| 56 | |
| 57 | const syncValue = lastFocused |
| 58 | ? attributes[ lastFocused ] |
| 59 | : firstValue; |
| 60 | |
| 61 | const newAttributes = attributeNames.reduce( ( o, key ) => ( { ...o, [ key ]: syncValue } ), {} ); |
| 62 | onChange( newAttributes ); |
| 63 | setSync( ! sync ); |
| 64 | }; |
| 65 | |
| 66 | const style = attributeNames.find( ( name ) => name.includes( 'Radius' ) ) |
| 67 | ? 'corners' |
| 68 | : 'circle'; |
| 69 | |
| 70 | return ( |
| 71 | <BaseControl className="components-gblocks-dimensions-control" label={ label } id={ attributeNames[ 0 ] }> |
| 72 | <Tooltip text={ !! sync ? __( 'Unlink Sides', 'generateblocks' ) : __( 'Link Sides', 'generateblocks' ) } > |
| 73 | <Button |
| 74 | className="components-gblocks-dimensions-control_sync" |
| 75 | aria-label={ !! sync ? __( 'Unlink Sides', 'generateblocks' ) : __( 'Link Sides', 'generateblocks' ) } |
| 76 | variant={ !! sync ? 'primary' : '' } |
| 77 | aria-pressed={ !! sync } |
| 78 | onClick={ () => syncUnits() } |
| 79 | > |
| 80 | { !! sync ? link : linkOff } |
| 81 | </Button> |
| 82 | </Tooltip> |
| 83 | |
| 84 | <div |
| 85 | className={ 'components-gblocks-dimensions-control__inputs style-' + style } |
| 86 | style={ { display: !! sync ? 'block' : '' } } |
| 87 | > |
| 88 | { attributeNames.map( ( attributeName, index ) => { |
| 89 | if ( sync && index > 0 ) { |
| 90 | return null; |
| 91 | } |
| 92 | |
| 93 | return ( |
| 94 | <div key={ attributeName }> |
| 95 | <UnitControl |
| 96 | id={ attributeName } |
| 97 | value={ attributes[ attributeName ] || '' } |
| 98 | placeholder={ placeholders[ attributeName ] || '' } |
| 99 | units={ units } |
| 100 | onChange={ ( value ) => { |
| 101 | let newAttributes = {}; |
| 102 | |
| 103 | if ( sync ) { |
| 104 | newAttributes = attributeNames.reduce( ( o, key ) => ( { ...o, [ key ]: value } ), {} ); |
| 105 | } else { |
| 106 | newAttributes[ attributeName ] = value; |
| 107 | } |
| 108 | |
| 109 | onChange( newAttributes ); |
| 110 | } } |
| 111 | onFocus={ () => setLastFocused( attributeName ) } |
| 112 | /> |
| 113 | |
| 114 | { ! sync && |
| 115 | <label htmlFor={ attributeName } className="gblocks-dimensions-control__label"> |
| 116 | { |
| 117 | attributeName.includes( 'Radius' ) |
| 118 | ? labels.borderRadius[ index ] |
| 119 | : labels.default[ index ] |
| 120 | } |
| 121 | </label> |
| 122 | } |
| 123 | </div> |
| 124 | ); |
| 125 | } ) } |
| 126 | </div> |
| 127 | </BaseControl> |
| 128 | ); |
| 129 | } |
| 130 |