css
5 years ago
attributes.js
5 years ago
block.js
5 years ago
deprecated.js
5 years ago
edit.js
5 years ago
editor.scss
5 years ago
edit.js
492 lines
| 1 | /** |
| 2 | * Block: Button Container |
| 3 | */ |
| 4 | |
| 5 | import classnames from 'classnames'; |
| 6 | import DimensionsControl from '../../components/dimensions/'; |
| 7 | import ResponsiveTabs from '../../components/responsive-tabs'; |
| 8 | import getIcon from '../../utils/get-icon'; |
| 9 | import MainCSS from './css/main.js'; |
| 10 | import DesktopCSS from './css/desktop.js'; |
| 11 | import TabletCSS from './css/tablet.js'; |
| 12 | import TabletOnlyCSS from './css/tablet-only.js'; |
| 13 | import MobileCSS from './css/mobile.js'; |
| 14 | import PanelArea from '../../components/panel-area/'; |
| 15 | |
| 16 | import { |
| 17 | __, |
| 18 | } from '@wordpress/i18n'; |
| 19 | |
| 20 | import { |
| 21 | Tooltip, |
| 22 | Button, |
| 23 | ToggleControl, |
| 24 | ToolbarGroup, |
| 25 | ToolbarButton, |
| 26 | TextControl, |
| 27 | } from '@wordpress/components'; |
| 28 | |
| 29 | import { |
| 30 | Fragment, |
| 31 | Component, |
| 32 | } from '@wordpress/element'; |
| 33 | |
| 34 | import { |
| 35 | InspectorControls, |
| 36 | InnerBlocks, |
| 37 | AlignmentToolbar, |
| 38 | BlockControls, |
| 39 | InspectorAdvancedControls, |
| 40 | } from '@wordpress/block-editor'; |
| 41 | |
| 42 | import { |
| 43 | createBlock, |
| 44 | cloneBlock, |
| 45 | } from '@wordpress/blocks'; |
| 46 | |
| 47 | import { |
| 48 | applyFilters, |
| 49 | } from '@wordpress/hooks'; |
| 50 | |
| 51 | import { |
| 52 | withSelect, |
| 53 | withDispatch, |
| 54 | } from '@wordpress/data'; |
| 55 | |
| 56 | import { |
| 57 | compose, |
| 58 | } from '@wordpress/compose'; |
| 59 | |
| 60 | /** |
| 61 | * Regular expression matching invalid anchor characters for replacement. |
| 62 | * |
| 63 | * @type {RegExp} |
| 64 | */ |
| 65 | const ANCHOR_REGEX = /[\s#]/g; |
| 66 | |
| 67 | const gbButtonContainerIds = []; |
| 68 | |
| 69 | const ALIGNMENT_CONTROLS = [ |
| 70 | { |
| 71 | icon: 'editor-alignleft', |
| 72 | title: __( 'Align Buttons Left', 'generateblocks' ), |
| 73 | align: 'left', |
| 74 | }, |
| 75 | { |
| 76 | icon: 'editor-aligncenter', |
| 77 | title: __( 'Align Buttons Center', 'generateblocks' ), |
| 78 | align: 'center', |
| 79 | }, |
| 80 | { |
| 81 | icon: 'editor-alignright', |
| 82 | title: __( 'Align Buttons Right', 'generateblocks' ), |
| 83 | align: 'right', |
| 84 | }, |
| 85 | ]; |
| 86 | |
| 87 | class GenerateButtonContainer extends Component { |
| 88 | constructor() { |
| 89 | super( ...arguments ); |
| 90 | |
| 91 | this.state = { |
| 92 | selectedDevice: 'Desktop', |
| 93 | }; |
| 94 | |
| 95 | this.getDeviceType = this.getDeviceType.bind( this ); |
| 96 | this.setDeviceType = this.setDeviceType.bind( this ); |
| 97 | } |
| 98 | |
| 99 | componentDidMount() { |
| 100 | const id = this.props.clientId.substr( 2, 9 ).replace( '-', '' ); |
| 101 | |
| 102 | // We don't want to ever regenerate unique IDs if they're a global style. |
| 103 | const isGlobalStyle = 'undefined' !== typeof this.props.attributes.isGlobalStyle && this.props.attributes.isGlobalStyle; |
| 104 | |
| 105 | if ( ! this.props.attributes.uniqueId ) { |
| 106 | this.props.setAttributes( { |
| 107 | uniqueId: id, |
| 108 | } ); |
| 109 | |
| 110 | gbButtonContainerIds.push( id ); |
| 111 | } else if ( gbButtonContainerIds.includes( this.props.attributes.uniqueId ) && ! isGlobalStyle ) { |
| 112 | this.props.setAttributes( { |
| 113 | uniqueId: id, |
| 114 | } ); |
| 115 | |
| 116 | gbButtonContainerIds.push( id ); |
| 117 | } else { |
| 118 | gbButtonContainerIds.push( this.props.attributes.uniqueId ); |
| 119 | } |
| 120 | |
| 121 | const thisBlock = wp.data.select( 'core/block-editor' ).getBlocksByClientId( this.props.clientId )[ 0 ]; |
| 122 | |
| 123 | if ( thisBlock ) { |
| 124 | const childBlocks = thisBlock.innerBlocks; |
| 125 | |
| 126 | if ( 0 === childBlocks.length ) { |
| 127 | wp.data.dispatch( 'core/block-editor' ).insertBlocks( createBlock( 'generateblocks/button', generateBlocksStyling.button ), undefined, this.props.clientId ); |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | // This block used to be static. Set it to dynamic by default from now on. |
| 132 | if ( 'undefined' === typeof this.props.attributes.isDynamic || ! this.props.attributes.isDynamic ) { |
| 133 | this.props.setAttributes( { |
| 134 | isDynamic: true, |
| 135 | } ); |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | getDeviceType() { |
| 140 | let deviceType = this.props.deviceType ? this.props.deviceType : this.state.selectedDevice; |
| 141 | |
| 142 | if ( ! generateBlocksInfo.syncResponsivePreviews ) { |
| 143 | deviceType = this.state.selectedDevice; |
| 144 | } |
| 145 | |
| 146 | return deviceType; |
| 147 | } |
| 148 | |
| 149 | setDeviceType( deviceType ) { |
| 150 | if ( generateBlocksInfo.syncResponsivePreviews && this.props.deviceType ) { |
| 151 | this.props.setDeviceType( deviceType ); |
| 152 | this.setState( { selectedDevice: deviceType } ); |
| 153 | } else { |
| 154 | this.setState( { selectedDevice: deviceType } ); |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | render() { |
| 159 | const { |
| 160 | attributes, |
| 161 | setAttributes, |
| 162 | clientId, |
| 163 | } = this.props; |
| 164 | |
| 165 | const { |
| 166 | uniqueId, |
| 167 | className, |
| 168 | anchor, |
| 169 | alignment, |
| 170 | alignmentTablet, |
| 171 | alignmentMobile, |
| 172 | stack, |
| 173 | stackTablet, |
| 174 | stackMobile, |
| 175 | fillHorizontalSpace, |
| 176 | fillHorizontalSpaceTablet, |
| 177 | fillHorizontalSpaceMobile, |
| 178 | } = attributes; |
| 179 | |
| 180 | let htmlAttributes = { |
| 181 | className: classnames( { |
| 182 | 'gb-button-wrapper': true, |
| 183 | [ `gb-button-wrapper-${ uniqueId }` ]: true, |
| 184 | [ `${ className }` ]: undefined !== className, |
| 185 | } ), |
| 186 | id: anchor ? anchor : null, |
| 187 | }; |
| 188 | |
| 189 | htmlAttributes = applyFilters( 'generateblocks.frontend.htmlAttributes', htmlAttributes, 'generateblocks/button-container', attributes ); |
| 190 | |
| 191 | return ( |
| 192 | <Fragment> |
| 193 | <BlockControls> |
| 194 | <ToolbarGroup> |
| 195 | <ToolbarButton |
| 196 | className="gblocks-add-new-button" |
| 197 | icon={ 'insert' } |
| 198 | label={ __( 'Add Button', 'generateblocks' ) } |
| 199 | onClick={ () => { |
| 200 | const thisBlock = wp.data.select( 'core/block-editor' ).getBlocksByClientId( clientId )[ 0 ]; |
| 201 | |
| 202 | if ( thisBlock ) { |
| 203 | const childBlocks = thisBlock.innerBlocks; |
| 204 | const keys = Object.keys( childBlocks ); |
| 205 | const lastKey = keys[ keys.length - 1 ]; |
| 206 | |
| 207 | if ( typeof childBlocks[ lastKey ] !== 'undefined' ) { |
| 208 | const blockToCopyId = childBlocks[ lastKey ].clientId; |
| 209 | |
| 210 | if ( blockToCopyId ) { |
| 211 | const blockToCopy = wp.data.select( 'core/block-editor' ).getBlocksByClientId( blockToCopyId )[ 0 ]; |
| 212 | const clonedBlock = cloneBlock( blockToCopy ); |
| 213 | |
| 214 | wp.data.dispatch( 'core/block-editor' ).insertBlocks( clonedBlock, undefined, clientId ); |
| 215 | } |
| 216 | } else if ( 0 === childBlocks.length ) { |
| 217 | wp.data.dispatch( 'core/block-editor' ).insertBlocks( createBlock( 'generateblocks/button', generateBlocksStyling.button ), undefined, clientId ); |
| 218 | } |
| 219 | } |
| 220 | } } |
| 221 | showTooltip |
| 222 | /> |
| 223 | </ToolbarGroup> |
| 224 | |
| 225 | { 'Desktop' === this.getDeviceType() && ( |
| 226 | <AlignmentToolbar |
| 227 | value={ alignment } |
| 228 | alignmentControls={ ALIGNMENT_CONTROLS } |
| 229 | onChange={ ( nextAlign ) => { |
| 230 | setAttributes( { alignment: nextAlign } ); |
| 231 | } } |
| 232 | /> |
| 233 | ) } |
| 234 | |
| 235 | { 'Tablet' === this.getDeviceType() && ( |
| 236 | <AlignmentToolbar |
| 237 | value={ alignmentTablet } |
| 238 | alignmentControls={ ALIGNMENT_CONTROLS } |
| 239 | onChange={ ( value ) => { |
| 240 | setAttributes( { alignmentTablet: value } ); |
| 241 | } } |
| 242 | /> |
| 243 | ) } |
| 244 | |
| 245 | { 'Mobile' === this.getDeviceType() && ( |
| 246 | <AlignmentToolbar |
| 247 | value={ alignmentMobile } |
| 248 | alignmentControls={ ALIGNMENT_CONTROLS } |
| 249 | onChange={ ( value ) => { |
| 250 | setAttributes( { alignmentMobile: value } ); |
| 251 | } } |
| 252 | /> |
| 253 | ) } |
| 254 | </BlockControls> |
| 255 | |
| 256 | <InspectorControls> |
| 257 | <ResponsiveTabs { ...this.props } |
| 258 | selectedDevice={ this.getDeviceType() } |
| 259 | onClick={ ( device ) => { |
| 260 | this.setDeviceType( device ); |
| 261 | } } |
| 262 | /> |
| 263 | |
| 264 | <PanelArea { ...this.props } |
| 265 | title={ __( 'Spacing', 'generateblocks' ) } |
| 266 | initialOpen={ true } |
| 267 | icon={ getIcon( 'spacing' ) } |
| 268 | className={ 'gblocks-panel-label' } |
| 269 | id={ 'buttonContainerSpacing' } |
| 270 | state={ this.state } |
| 271 | > |
| 272 | { 'Desktop' === this.getDeviceType() && ( |
| 273 | <Fragment> |
| 274 | <DimensionsControl { ...this.props } |
| 275 | device={ this.getDeviceType() } |
| 276 | type={ 'margin' } |
| 277 | label={ __( 'Margin', 'generateblocks' ) } |
| 278 | attrTop={ 'marginTop' } |
| 279 | attrRight={ 'marginRight' } |
| 280 | attrBottom={ 'marginBottom' } |
| 281 | attrLeft={ 'marginLeft' } |
| 282 | attrUnit={ 'marginUnit' } |
| 283 | attrSyncUnits={ 'marginSyncUnits' } |
| 284 | defaults={ generateBlocksDefaults.buttonContainer } |
| 285 | units={ [ 'px', 'em', '%' ] } |
| 286 | /> |
| 287 | |
| 288 | <ToggleControl |
| 289 | label={ __( 'Stack Vertically', 'generateblocks' ) } |
| 290 | checked={ !! stack } |
| 291 | onChange={ ( value ) => { |
| 292 | setAttributes( { |
| 293 | stack: value, |
| 294 | } ); |
| 295 | } } |
| 296 | /> |
| 297 | |
| 298 | <ToggleControl |
| 299 | label={ __( 'Fill Horizontal Space', 'generateblocks' ) } |
| 300 | checked={ !! fillHorizontalSpace } |
| 301 | onChange={ ( value ) => { |
| 302 | setAttributes( { |
| 303 | fillHorizontalSpace: value, |
| 304 | } ); |
| 305 | } } |
| 306 | /> |
| 307 | </Fragment> |
| 308 | ) } |
| 309 | |
| 310 | { 'Tablet' === this.getDeviceType() && ( |
| 311 | <Fragment> |
| 312 | <DimensionsControl { ...this.props } |
| 313 | device={ this.getDeviceType() } |
| 314 | type={ 'margin' } |
| 315 | label={ __( 'Margin', 'generateblocks' ) } |
| 316 | attrTop={ 'marginTopTablet' } |
| 317 | attrRight={ 'marginRightTablet' } |
| 318 | attrBottom={ 'marginBottomTablet' } |
| 319 | attrLeft={ 'marginLeftTablet' } |
| 320 | attrUnit={ 'marginUnit' } |
| 321 | attrSyncUnits={ 'marginSyncUnits' } |
| 322 | defaults={ generateBlocksDefaults.buttonContainer } |
| 323 | units={ [ 'px', 'em', '%' ] } |
| 324 | /> |
| 325 | |
| 326 | <ToggleControl |
| 327 | label={ __( 'Stack Vertically', 'generateblocks' ) } |
| 328 | checked={ !! stackTablet } |
| 329 | onChange={ ( value ) => { |
| 330 | setAttributes( { |
| 331 | stackTablet: value, |
| 332 | } ); |
| 333 | } } |
| 334 | /> |
| 335 | |
| 336 | <ToggleControl |
| 337 | label={ __( 'Fill Horizontal Space', 'generateblocks' ) } |
| 338 | checked={ !! fillHorizontalSpaceTablet } |
| 339 | onChange={ ( value ) => { |
| 340 | setAttributes( { |
| 341 | fillHorizontalSpaceTablet: value, |
| 342 | } ); |
| 343 | } } |
| 344 | /> |
| 345 | </Fragment> |
| 346 | ) } |
| 347 | |
| 348 | { 'Mobile' === this.getDeviceType() && ( |
| 349 | <Fragment> |
| 350 | <DimensionsControl { ...this.props } |
| 351 | device={ this.getDeviceType() } |
| 352 | type={ 'margin' } |
| 353 | label={ __( 'Margin', 'generateblocks' ) } |
| 354 | attrTop={ 'marginTopMobile' } |
| 355 | attrRight={ 'marginRightMobile' } |
| 356 | attrBottom={ 'marginBottomMobile' } |
| 357 | attrLeft={ 'marginLeftMobile' } |
| 358 | attrUnit={ 'marginUnit' } |
| 359 | attrSyncUnits={ 'marginSyncUnits' } |
| 360 | defaults={ generateBlocksDefaults.buttonContainer } |
| 361 | units={ [ 'px', 'em', '%' ] } |
| 362 | /> |
| 363 | |
| 364 | <ToggleControl |
| 365 | label={ __( 'Stack Vertically', 'generateblocks' ) } |
| 366 | checked={ !! stackMobile } |
| 367 | onChange={ ( value ) => { |
| 368 | setAttributes( { |
| 369 | stackMobile: value, |
| 370 | } ); |
| 371 | } } |
| 372 | /> |
| 373 | |
| 374 | <ToggleControl |
| 375 | label={ __( 'Fill Horizontal Space', 'generateblocks' ) } |
| 376 | checked={ !! fillHorizontalSpaceMobile } |
| 377 | onChange={ ( value ) => { |
| 378 | setAttributes( { |
| 379 | fillHorizontalSpaceMobile: value, |
| 380 | } ); |
| 381 | } } |
| 382 | /> |
| 383 | </Fragment> |
| 384 | ) } |
| 385 | |
| 386 | { applyFilters( 'generateblocks.editor.controls', '', 'buttonContainerSpacing', this.props, this.state ) } |
| 387 | </PanelArea> |
| 388 | |
| 389 | <PanelArea { ...this.props } |
| 390 | title={ __( 'Documentation', 'generateblocks' ) } |
| 391 | icon={ getIcon( 'documentation' ) } |
| 392 | initialOpen={ false } |
| 393 | className={ 'gblocks-panel-label' } |
| 394 | id={ 'buttonContainerDocumentation' } |
| 395 | state={ this.state } |
| 396 | > |
| 397 | <p>{ __( 'Need help with this block?', 'generateblocks' ) }</p> |
| 398 | <a href="https://docs.generateblocks.com/collection/buttons/" target="_blank" rel="noreferrer noopener">{ __( 'Visit our documentation', 'generateblocks' ) }</a> |
| 399 | |
| 400 | { applyFilters( 'generateblocks.editor.controls', '', 'buttonContainerDocumentation', this.props, this.state ) } |
| 401 | </PanelArea> |
| 402 | </InspectorControls> |
| 403 | |
| 404 | <InspectorAdvancedControls> |
| 405 | <TextControl |
| 406 | label={ __( 'HTML Anchor', 'generateblocks' ) } |
| 407 | help={ __( 'Anchors lets you link directly to a section on a page.', 'generateblocks' ) } |
| 408 | value={ anchor || '' } |
| 409 | onChange={ ( nextValue ) => { |
| 410 | nextValue = nextValue.replace( ANCHOR_REGEX, '-' ); |
| 411 | setAttributes( { |
| 412 | anchor: nextValue, |
| 413 | } ); |
| 414 | } } /> |
| 415 | </InspectorAdvancedControls> |
| 416 | |
| 417 | <MainCSS { ...this.props } /> |
| 418 | |
| 419 | { this.props.deviceType && |
| 420 | <Fragment> |
| 421 | { 'Desktop' === this.props.deviceType && |
| 422 | <DesktopCSS { ...this.props } /> |
| 423 | } |
| 424 | |
| 425 | { ( 'Tablet' === this.props.deviceType || 'Mobile' === this.props.deviceType ) && |
| 426 | <TabletCSS { ...this.props } /> |
| 427 | } |
| 428 | |
| 429 | { 'Tablet' === this.props.deviceType && |
| 430 | <TabletOnlyCSS { ...this.props } /> |
| 431 | } |
| 432 | |
| 433 | { 'Mobile' === this.props.deviceType && |
| 434 | <MobileCSS { ...this.props } /> |
| 435 | } |
| 436 | </Fragment> |
| 437 | } |
| 438 | |
| 439 | <div |
| 440 | { ...htmlAttributes } |
| 441 | > |
| 442 | <InnerBlocks |
| 443 | allowedBlocks={ [ 'generateblocks/button' ] } |
| 444 | renderAppender={ () => ( |
| 445 | <Tooltip text={ __( 'Add Button', 'generateblocks' ) }> |
| 446 | <Button |
| 447 | className="gblocks-add-new-button gblocks-button-container-appender" |
| 448 | icon={ 'insert' } |
| 449 | onClick={ () => { |
| 450 | wp.data.dispatch( 'core/block-editor' ).insertBlocks( createBlock( 'generateblocks/button', generateBlocksStyling.button ), undefined, clientId ); |
| 451 | } } |
| 452 | /> |
| 453 | </Tooltip> |
| 454 | ) } |
| 455 | /> |
| 456 | </div> |
| 457 | </Fragment> |
| 458 | ); |
| 459 | } |
| 460 | } |
| 461 | |
| 462 | export default compose( [ |
| 463 | withDispatch( ( dispatch ) => ( { |
| 464 | setDeviceType( type ) { |
| 465 | const { |
| 466 | __experimentalSetPreviewDeviceType: setPreviewDeviceType, |
| 467 | } = dispatch( 'core/edit-post' ); |
| 468 | |
| 469 | if ( ! setPreviewDeviceType ) { |
| 470 | return; |
| 471 | } |
| 472 | |
| 473 | setPreviewDeviceType( type ); |
| 474 | }, |
| 475 | } ) ), |
| 476 | withSelect( ( select ) => { |
| 477 | const { |
| 478 | __experimentalGetPreviewDeviceType: getPreviewDeviceType, |
| 479 | } = select( 'core/edit-post' ); |
| 480 | |
| 481 | if ( ! getPreviewDeviceType ) { |
| 482 | return { |
| 483 | deviceType: null, |
| 484 | }; |
| 485 | } |
| 486 | |
| 487 | return { |
| 488 | deviceType: getPreviewDeviceType(), |
| 489 | }; |
| 490 | } ), |
| 491 | ] )( GenerateButtonContainer ); |
| 492 |