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