PluginProbe ʕ •ᴥ•ʔ
GenerateBlocks / 1.4.0
GenerateBlocks v1.4.0
trunk 1.0 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.2.0 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.7.0 1.7.1 1.7.2 1.7.3 1.8.0 1.8.1 1.8.2 1.8.3 1.9.0 1.9.1 2.0.0 2.0.1 2.0.2 2.1.0 2.1.1 2.1.2 2.2.0 2.2.1 2.3.0
generateblocks / src / blocks / button-container / edit.js
generateblocks / src / blocks / button-container Last commit date
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