@@ -34,6 +34,7 @@ import {
3434} from '@wordpress/components'
3535import { useBlockProps } from '@wordpress/block-editor'
3636import apiFetch from '@wordpress/api-fetch'
37+
3738const 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+
8390const 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