Skip to content

Commit fb2381e

Browse files
kaeizenbfintalCopilot
authored
Feat (Design Library): Full Pages (#3573)
* add pages in design library - also add prompt if there are existing blocks in the editor * update url * fix tab design, remove unnecessary styles * update design library - use two json files - add append only option * add auto scroll * improve code readability * remove bottom area in full pages * Update src/design-library/init.php Co-authored-by: Copilot <[email protected]> * use designId and content as fallback * fix auto-scroll not working sometimes * UI tweaks * fix missing label/title * change request fix - implement dragging instead of scroll - delay start of auto-scroll, slowdown auto-scroll - display labels in patterns - select first block added * use maps graphic * add styles for maps graphic * fix scale when changing categories * tab ui tweaks * improved auto-scroll smoothness * tweaked pro notice on full pages and fixed scrolling * added grabbing cursor, and fixed bottom gap of upsell note * add missing deps * fixed cursor showing as pointer in full pages * even if devMode is true, show the actual design previews * remove scrolling in full pages * fix preview height * fix content * update maps graphic * use lowres images * keep filename and ext * slower scroll * proper escaping of title * tweaked scrolling * use slug for category * minor fixes * add delayTimeoutRef * sanitize blocks --------- Co-authored-by: Benjamin Intal <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: [email protected] <>
1 parent 60e5ed7 commit fb2381e

File tree

20 files changed

+1188
-1181
lines changed

20 files changed

+1188
-1181
lines changed

.config/externals.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ const wpExternals = [
4242
'keyboard-shortcuts',
4343
'token-list',
4444
'keycodes',
45-
'escape-html'
45+
'escape-html',
46+
'dom'
4647
].reduce( ( externals, name ) => ( {
4748
...externals,
4849
[ `@wordpress/${ name }` ]: `wp.${ camelCaseDash( name ) }`,

.config/rules.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ module.exports = [
3939
],
4040
},
4141
{
42-
test: /\.(png|jpg|gif)$/,
42+
test: /\.(png|jpg|gif|webp)$/,
4343
use: [
4444
{
4545
loader: 'file-loader',

gulpfile.js

Lines changed: 1 addition & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -411,81 +411,6 @@ ${ blockDesignSystem });
411411
cb()
412412
} )
413413

414-
gulp.task( 'generate-design-library-default-placeholders-php', function( cb ) {
415-
const fs = require( 'fs' )
416-
417-
let defaultPlaceholders = 'array()'
418-
419-
const toAssocArray = ( key, value, cb, indent ) => {
420-
if ( typeof value === 'object' ) {
421-
const parsed = cb( value, indent + 1 )
422-
return `"${ key }" => ${ parsed }`
423-
}
424-
425-
return `"${ key }" => "${ value }"`
426-
}
427-
428-
const parsePlaceholders = ( obj, indent ) => {
429-
let content = ''
430-
const tab = '\t'.repeat( indent )
431-
432-
if ( typeof obj === 'object' ) {
433-
content += 'array(\n'
434-
435-
Object.entries( obj ).forEach( ( [ key, value ], index, bds ) => {
436-
content += tab + toAssocArray( key, value, parsePlaceholders, indent )
437-
438-
if ( index !== bds.length - 1 ) {
439-
content += ',\n'
440-
} else {
441-
content += '\n'
442-
}
443-
} )
444-
content += `\t`.repeat( indent - 1 ) + ')'
445-
}
446-
return content
447-
}
448-
449-
const jsonPath = path.resolve( __dirname, `src/components/design-library-list/default.json` )
450-
if ( fs.existsSync( jsonPath ) ) {
451-
const fileContent = fs.readFileSync( jsonPath, 'utf-8' )
452-
const raw = JSON.parse( fileContent )
453-
defaultPlaceholders = parsePlaceholders( raw, 4 )
454-
}
455-
456-
// Generate PHP variable string
457-
const script = `<?php
458-
// This is a generated file by gulp generate-design-library-default-placeholders-php
459-
// Use src/components/design-library-list/default.json if you want to edit this file.
460-
461-
// Exit if accessed directly.
462-
if ( ! defined( 'ABSPATH' ) ) {
463-
exit;
464-
}
465-
466-
if ( ! class_exists( 'Stackable_Design_Library_Placeholders' ) ) {
467-
class Stackable_Design_Library_Placeholders {
468-
469-
function __construct() {
470-
}
471-
472-
public static function get_default() {
473-
$default_placeholders = ${ defaultPlaceholders };
474-
475-
return $default_placeholders;
476-
}
477-
}
478-
479-
new Stackable_Design_Library_Placeholders();
480-
}
481-
?>
482-
`
483-
// Write PHP variable to file
484-
fs.writeFileSync( path.resolve( __dirname, 'src/design-library/default-placeholders.php' ), script )
485-
486-
cb()
487-
} )
488-
489414
gulp.task( 'generate-translations-js', gulp.series(
490415
// The collect function has an issue where it will not continue if the
491416
// folder will it writes to doesn't exist, create it to prevent an error.
@@ -778,7 +703,7 @@ gulp.task( 'style-deprecated', gulp.parallel(
778703
* END deprecated build styles, we still build these
779704
********************************************************************/
780705

781-
gulp.task( 'build-process', gulp.parallel( 'style', 'style-editor', 'welcome-styles', 'style-deprecated', 'generate-translations-js', 'generate-stk-block-typesphp', 'generate-design-library-default-placeholders-php' ) )
706+
gulp.task( 'build-process', gulp.parallel( 'style', 'style-editor', 'welcome-styles', 'style-deprecated', 'generate-translations-js', 'generate-stk-block-typesphp' ) )
782707

783708
gulp.task( 'build-block-design-system', gulp.parallel( 'generate-block-design-system-php', 'generate-block-design-system-scss' ) )
784709

@@ -815,11 +740,6 @@ const watchFuncs = ( basePath = '.' ) => {
815740
[ `${ basePath }/src/block/**/block.json` ],
816741
gulp.parallel( [ 'generate-stk-block-typesphp' ] )
817742
)
818-
819-
gulp.watch(
820-
[ `${ basePath }/src/components/design-library-list/default.json` ],
821-
gulp.parallel( [ 'generate-design-library-default-placeholders-php' ] )
822-
)
823743
}
824744

825745
gulp.task( 'watch', gulp.series( 'build-block-design-system', 'build-process', function watch( done ) {

plugin.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,6 @@ function is_frontend() {
276276
require_once( plugin_dir_path( __FILE__ ) . 'src/kses.php' );
277277
require_once( plugin_dir_path( __FILE__ ) . 'src/dynamic-breakpoints.php' );
278278
require_once( plugin_dir_path( __FILE__ ) . 'src/design-library/init.php' );
279-
require_once( plugin_dir_path( __FILE__ ) . 'src/design-library/default-placeholders.php' );
280279
require_once( plugin_dir_path( __FILE__ ) . 'src/styles/block-design-system.php' );
281280
require_once( plugin_dir_path( __FILE__ ) . 'src/plugins/theme-block-style-inheritance/index.php' );
282281
require_once( plugin_dir_path( __FILE__ ) . 'src/global-settings.php' );

src/block/design-library/edit.js

Lines changed: 146 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
} from '@wordpress/components'
3535
import { useBlockProps } from '@wordpress/block-editor'
3636
import apiFetch from '@wordpress/api-fetch'
37+
3738
const convertBlocksToArray = block => {
3839
const innerBlocks = block.innerBlocks.map( innerBlock => convertBlocksToArray( innerBlock ) )
3940
return [ block.name, block.attributes, innerBlocks ]
@@ -80,18 +81,26 @@ const checkIfImageUrl = async value => {
8081
return value
8182
}
8283

84+
const DIALOG_OPTIONS = {
85+
CLOSE: 0,
86+
REMOVE_BLOCKS: 1,
87+
DISABLED_BLOCKS: 2,
88+
}
89+
8390
const Edit = props => {
8491
const {
8592
clientId,
8693
attributes,
8794
} = props
8895

8996
const [ isLibraryOpen, setIsLibraryOpen ] = useState( false )
90-
const [ isDialogOpen, setIsDialogOpen ] = useState( false )
97+
const [ isDialogOpen, setIsDialogOpen ] = useState( DIALOG_OPTIONS.CLOSE )
9198

9299
const designsRef = useRef( [] )
93100
const disabledBlocksRef = useRef( [] )
94101
const callbackRef = useRef( null )
102+
const blocksToRemoveRef = useRef( [] )
103+
const insertIndexRef = useRef( -1 )
95104

96105
const blockProps = useBlockProps( {
97106
className: 'ugb-design-library-block',
@@ -240,22 +249,40 @@ const Edit = props => {
240249

241250
for ( const blockDesign of designs ) {
242251
const { designData, category } = blockDesign
243-
const {
244-
name, attributes, innerBlocks,
245-
} = designData
246-
if ( name && attributes ) {
247-
const block = await createBlockWithAttributes( category, name, applyFilters( 'stackable.design-library.attributes', attributes ), innerBlocks || [], substituteBlocks, parentClientId )
248-
blocks.push( block )
249-
} else {
250-
console.error( 'Design library selection failed: No block data found' ) // eslint-disable-line no-console
252+
253+
for ( const patterns of designData ) {
254+
const {
255+
name, attributes, innerBlocks,
256+
} = patterns
257+
if ( name && attributes ) {
258+
const block = await createBlockWithAttributes( category, name, applyFilters( 'stackable.design-library.attributes', attributes ), innerBlocks || [], substituteBlocks, parentClientId )
259+
blocks.push( block )
260+
} else {
261+
console.error( 'Design library selection failed: No block data found' ) // eslint-disable-line no-console
262+
}
251263
}
252264
}
253265

254-
if ( blocks.length ) {
266+
if ( ! blocks.length ) {
267+
return
268+
}
269+
270+
if ( insertIndexRef.current !== -1 ) {
271+
dispatch( 'core/block-editor' ).insertBlocks( blocks, insertIndexRef.current )
272+
} else {
255273
dispatch( 'core/block-editor' ).replaceBlocks( clientId, blocks )
256-
if ( callbackRef.current ) {
257-
callbackRef.current()
258-
}
274+
}
275+
276+
// Set focus on the top-most added block
277+
dispatch( 'core/block-editor' ).selectBlock( blocks[ 0 ].clientId )
278+
279+
if ( blocksToRemoveRef.current.length ) {
280+
dispatch( 'core/block-editor' ).removeBlocks( blocksToRemoveRef.current )
281+
blocksToRemoveRef.current = []
282+
}
283+
284+
if ( callbackRef.current ) {
285+
callbackRef.current()
259286
}
260287
}
261288

@@ -324,7 +351,7 @@ const Edit = props => {
324351
onClose={ () => {
325352
setIsLibraryOpen( false )
326353
} }
327-
onSelect={ async ( _designs, callback ) => {
354+
onSelect={ async ( _designs, callback, type ) => {
328355
const designs = []
329356
let disabledBlocks = new Set()
330357

@@ -344,52 +371,124 @@ const Edit = props => {
344371
disabledBlocksRef.current = disabledBlocks
345372
callbackRef.current = callback
346373

374+
if ( type === 'pages' ) {
375+
const allBlocks = select( 'core/block-editor' ).getBlockOrder()
376+
const blocksToRemove = allBlocks.filter( id => id !== clientId )
377+
378+
if ( blocksToRemove.length ) {
379+
blocksToRemoveRef.current = allBlocks
380+
setIsDialogOpen( DIALOG_OPTIONS.REMOVE_BLOCKS )
381+
return
382+
}
383+
}
384+
347385
if ( disabledBlocks.size ) {
348-
setIsDialogOpen( true )
386+
setIsDialogOpen( DIALOG_OPTIONS.DISABLED_BLOCKS )
349387
return
350388
}
351389

352390
await addDesigns( false )
353391
} }
354392
/>
355393
}
356-
{ isDialogOpen &&
394+
{ isDialogOpen !== DIALOG_OPTIONS.CLOSE &&
357395
<Modal
358396
className="ugb-design-library__confirm-dialog"
359397
title={ __( 'Stackable Design Library', i18n ) }
360-
onRequestClose={ () => setIsDialogOpen( false ) }
398+
onRequestClose={ () => setIsDialogOpen( DIALOG_OPTIONS.CLOSE ) }
361399
>
362400
<VStack spacing={ 8 }>
363-
<div>
364-
<span>{ __( 'The designs you have selected contain the following disabled blocks:', i18n ) }</span>
365-
<ul>
366-
{ disabledBlocksRef.current && [ ...disabledBlocksRef.current ].map( ( block, i ) => <li key={ i }>{ block }</li> ) }
367-
</ul>
368-
<span> { __( 'These blocks can be enabled in the Stackable settings page. Do you want to keep the disabled blocks or substitute them with other Stackable or core blocks?', i18n ) }</span>
369-
</div>
370-
<Flex direction="column" align="flex-end">
371-
<Button
372-
__next40pxDefaultSize
373-
variant="primary"
374-
onClick={ () => onClickPrimary() }
375-
>
376-
{ __( 'Add patterns and substitute blocks', i18n ) }
377-
</Button>
378-
<Button
379-
__next40pxDefaultSize
380-
variant="secondary"
381-
onClick={ () => onClickSecondary() }
382-
>
383-
{ __( 'Add patterns only (no substitutes)', i18n ) }
384-
</Button>
385-
<Button
386-
__next40pxDefaultSize
387-
variant="tertiary"
388-
onClick={ () => onClickTertiary() }
389-
>
390-
{ __( 'Enable blocks in settings', i18n ) }
391-
</Button>
392-
</Flex>
401+
{ isDialogOpen === DIALOG_OPTIONS.REMOVE_BLOCKS && <>
402+
<div>
403+
<p>
404+
{ __( 'Adding this page design will replace all existing blocks in the editor. Are you sure you want to continue?', i18n ) }
405+
</p>
406+
</div>
407+
<Flex direction="column" align="stretch">
408+
<Button
409+
__next40pxDefaultSize
410+
variant="primary"
411+
onClick={ () => {
412+
insertIndexRef.current = 0
413+
if ( disabledBlocksRef.current.size ) {
414+
// Close this dialog and reopen after a while to show the notice for disabled blocks
415+
// The existing blocks will be removed later
416+
setIsDialogOpen( DIALOG_OPTIONS.CLOSE )
417+
setTimeout( () => setIsDialogOpen( DIALOG_OPTIONS.DISABLED_BLOCKS ), 500 )
418+
return
419+
}
420+
421+
addDesigns( false )
422+
} }
423+
>
424+
{ __( 'Replace existing content with page design', i18n ) }
425+
</Button>
426+
<Button
427+
__next40pxDefaultSize
428+
variant="secondary"
429+
onClick={ () => {
430+
insertIndexRef.current = blocksToRemoveRef.current.length
431+
// When appending the page design, only remove the design library block
432+
blocksToRemoveRef.current = [ clientId ]
433+
434+
if ( disabledBlocksRef.current.size ) {
435+
setIsDialogOpen( DIALOG_OPTIONS.CLOSE )
436+
setTimeout( () => setIsDialogOpen( DIALOG_OPTIONS.DISABLED_BLOCKS ), 500 )
437+
438+
return
439+
}
440+
addDesigns( false )
441+
} }
442+
>
443+
{ __( 'Append page design only', i18n ) }
444+
</Button>
445+
<Button
446+
__next40pxDefaultSize
447+
variant="tertiary"
448+
onClick={ () => {
449+
blocksToRemoveRef.current = []
450+
setIsDialogOpen( DIALOG_OPTIONS.CLOSE )
451+
} }
452+
>
453+
{ __( 'Cancel', i18n ) }
454+
</Button>
455+
</Flex>
456+
</> }
457+
{ isDialogOpen === DIALOG_OPTIONS.DISABLED_BLOCKS && <>
458+
<div>
459+
<p>{ __( 'The designs you have selected contain the following disabled blocks:', i18n ) }</p>
460+
<ul>
461+
{ disabledBlocksRef.current && [ ...disabledBlocksRef.current ].map( ( block, i ) => <li key={ i }>{ block }</li> ) }
462+
</ul>
463+
<p> { __( 'These blocks can be enabled in the Stackable settings page. Do you want to keep the disabled blocks or substitute them with other Stackable or core blocks?', i18n ) }</p>
464+
</div>
465+
<Flex direction="column" align="stretch">
466+
<Button
467+
__next40pxDefaultSize
468+
style={ { textAlign: 'center' } }
469+
variant="primary"
470+
onClick={ () => onClickPrimary() }
471+
>
472+
{ __( 'Add patterns and substitute blocks', i18n ) }
473+
</Button>
474+
<Button
475+
__next40pxDefaultSize
476+
style={ { textAlign: 'center', marginBottom: '16px' } }
477+
variant="secondary"
478+
onClick={ () => onClickSecondary() }
479+
>
480+
{ __( 'Add patterns only (no substitutes)', i18n ) }
481+
</Button>
482+
<Button
483+
__next40pxDefaultSize
484+
style={ { textAlign: 'center', marginBottom: '16px' } }
485+
variant="tertiary"
486+
onClick={ () => onClickTertiary() }
487+
>
488+
{ __( 'Enable blocks in settings', i18n ) }
489+
</Button>
490+
</Flex>
491+
</> }
393492
</VStack>
394493

395494
</Modal>

0 commit comments

Comments
 (0)