diff --git a/src/block/design-library/edit.js b/src/block/design-library/edit.js index ba3150f95..2f4643b07 100644 --- a/src/block/design-library/edit.js +++ b/src/block/design-library/edit.js @@ -26,7 +26,9 @@ import { dispatch, select } from '@wordpress/data' import { createBlock, createBlocksFromInnerBlocksTemplate, getBlockVariations, getBlockType, } from '@wordpress/blocks' -import { useRef, useState } from '@wordpress/element' +import { + useRef, useState, useCallback, +} from '@wordpress/element' import { applyFilters } from '@wordpress/hooks' import { // eslint-disable-next-line @wordpress/no-unsafe-wp-apis @@ -326,6 +328,47 @@ const Edit = props => { await addDesigns( true ) } + const onClose = useCallback( () => setIsLibraryOpen( false ), [] ) + + const onSelect = useCallback( async ( _designs, callback, type ) => { + const designs = [] + let disabledBlocks = new Set() + + _designs.forEach( design => { + const { + designData, blocksForSubstitution, category, + } = design + + if ( blocksForSubstitution.size ) { + disabledBlocks = disabledBlocks.union( blocksForSubstitution ) + } + + designs.push( { designData, category } ) + } ) + + designsRef.current = designs + disabledBlocksRef.current = disabledBlocks + callbackRef.current = callback + + if ( type === 'pages' ) { + const allBlocks = select( 'core/block-editor' ).getBlockOrder() + const blocksToRemove = allBlocks.filter( id => id !== clientId ) + + if ( blocksToRemove.length ) { + blocksToRemoveRef.current = allBlocks + setIsDialogOpen( DIALOG_OPTIONS.REMOVE_BLOCKS ) + return + } + } + + if ( disabledBlocks.size ) { + setIsDialogOpen( DIALOG_OPTIONS.DISABLED_BLOCKS ) + return + } + + await addDesigns( false ) + }, [] ) + if ( attributes.previewMode ) { const src = previewImage.match( /https?:/i ) ? previewImage : srcUrl ? `${ srcUrl }/${ previewImage }` @@ -356,47 +399,8 @@ const Edit = props => { { isLibraryOpen && { - setIsLibraryOpen( false ) - } } - onSelect={ async ( _designs, callback, type ) => { - const designs = [] - let disabledBlocks = new Set() - - _designs.forEach( design => { - const { - designData, blocksForSubstitution, category, - } = design - - if ( blocksForSubstitution.size ) { - disabledBlocks = disabledBlocks.union( blocksForSubstitution ) - } - - designs.push( { designData, category } ) - } ) - - designsRef.current = designs - disabledBlocksRef.current = disabledBlocks - callbackRef.current = callback - - if ( type === 'pages' ) { - const allBlocks = select( 'core/block-editor' ).getBlockOrder() - const blocksToRemove = allBlocks.filter( id => id !== clientId ) - - if ( blocksToRemove.length ) { - blocksToRemoveRef.current = allBlocks - setIsDialogOpen( DIALOG_OPTIONS.REMOVE_BLOCKS ) - return - } - } - - if ( disabledBlocks.size ) { - setIsDialogOpen( DIALOG_OPTIONS.DISABLED_BLOCKS ) - return - } - - await addDesigns( false ) - } } + onClose={ onClose } + onSelect={ onSelect } /> } { isDialogOpen !== DIALOG_OPTIONS.CLOSE && diff --git a/src/components/design-library-list/design-library-list-item.js b/src/components/design-library-list/design-library-list-item.js index cd63f0f04..82db0950a 100644 --- a/src/components/design-library-list/design-library-list-item.js +++ b/src/components/design-library-list/design-library-list-item.js @@ -11,7 +11,6 @@ import { useAutoScroll } from './use-auto-scroll' /** * External dependencies. */ -import { usePresetControls } from '~stackable/hooks' import { isPro, i18n } from 'stackable' import classnames from 'classnames' import { Tooltip } from '~stackable/components' @@ -19,51 +18,56 @@ import { Tooltip } from '~stackable/components' /** * WordPress dependencies. */ -import { forwardRef, useState } from '@wordpress/element' +import { + useState, useRef, memo, +} from '@wordpress/element' import { Dashicon, Spinner } from '@wordpress/components' import { __ } from '@wordpress/i18n' -const DesignLibraryListItem = forwardRef( ( props, ref ) => { +const DesignLibraryListItem = memo( props => { const { - label, - plan, - selectedNum = false, - selectedData = null, - previewSize, - previewProps, selectedTab, + plan, label, + selectedNum, + selectedData, isMultiSelectBusy, + shouldRender, + presetMarks, } = props - const presetMarks = usePresetControls( 'spacingSizes' )?.getPresetMarks() || null - - const spacingSize = ! presetMarks || ! Array.isArray( presetMarks ) ? 120 : presetMarks[ presetMarks.length - 2 ].value + const spacingSize = Array.isArray( presetMarks ) && presetMarks.length >= 2 + ? presetMarks[ presetMarks.length - 2 ].value + : 120 const [ isLoading, setIsLoading ] = useState( true ) - const { hostRef, shadowRoot } = useShadowRoot() + const { hostRef, shadowRoot } = useShadowRoot( shouldRender ) + + const ref = useRef( null ) const { blocks, enableBackground, shadowBodySizeRef, blocksForSubstitutionRef, - onClickDesign, - } = usePreviewRenderer( - previewProps, previewSize, plan, spacingSize, - selectedTab, selectedNum, selectedData, - ref, hostRef, shadowRoot, setIsLoading, - ) + previewSize, onClickDesign, + updateShadowBodySize, + } = usePreviewRenderer( props, shouldRender, spacingSize, + ref, hostRef, shadowRoot, setIsLoading ) const { onMouseOut, onMouseOver, onMouseDown, } = useAutoScroll( hostRef, shadowBodySizeRef, selectedTab ) const getDesignPreviewSize = () => { - if ( ! shadowRoot || isLoading ) { - return 0 - } + const tempHeight = selectedTab === 'pages' ? 345 : 100 - return selectedNum && selectedData ? selectedData.selectedPreviewSize.preview + const previewHeight = selectedNum && selectedData ? selectedData.selectedPreviewSize.preview : ( enableBackground ? previewSize.heightBackground : previewSize.heightNoBackground ) + + if ( ! blocks || ! previewHeight ) { + return tempHeight + } + + return previewHeight } const mainClasses = classnames( [ @@ -100,7 +104,7 @@ const DesignLibraryListItem = forwardRef( ( props, ref ) => { showHideNote={ false } /> ) } - { isLoading &&
} +
{ } } >
- { shadowRoot && ! isLoading && }
diff --git a/src/components/design-library-list/design-preview.js b/src/components/design-library-list/design-preview.js index a4b3fa06f..a23896a71 100644 --- a/src/components/design-library-list/design-preview.js +++ b/src/components/design-library-list/design-preview.js @@ -13,9 +13,13 @@ export const DesignPreview = ( { blocks = '', shadowRoot, selectedTab, + designIndex, onMouseDown = NOOP, + updateShadowBodySize = NOOP, + setIsLoading, } ) => { const ref = useRef( null ) + const wrapperRef = useRef( null ) const isDragging = useRef( false ) const lastY = useRef( 0 ) @@ -77,13 +81,45 @@ export const DesignPreview = ( { 'preview-pages': selectedTab === 'pages', } ) + useEffect( () => { + const wrapper = wrapperRef.current + + if ( ! wrapper || ! blocks ) { + return + } + + setIsLoading( true ) + + const ric = window.requestIdleCallback || ( cb => setTimeout( cb, designIndex * 20 ) ) + const sanitizedHTML = safeHTML( blocks ) + + if ( selectedTab !== 'pages' || designIndex < 9 ) { + // insert HTML for patterns and for the first 9 pages + wrapper.innerHTML = sanitizedHTML + requestAnimationFrame( () => { + ric( () => setIsLoading( false ) ) + } ) + return + } + + requestAnimationFrame( () => { + ric( () => { + wrapper.innerHTML = sanitizedHTML + updateShadowBodySize() + requestAnimationFrame( () => { + ric( () => setIsLoading( false ) ) + } ) + } ) + } ) + }, [ blocks, shadowRoot ] ) // Only depend on blocks and shadowRoot; selectedTab and designIndex changes will cause blocks to update + return createPortal( <>
diff --git a/src/components/design-library-list/editor.scss b/src/components/design-library-list/editor.scss index e07ff2863..e68b87e50 100644 --- a/src/components/design-library-list/editor.scss +++ b/src/components/design-library-list/editor.scss @@ -58,6 +58,7 @@ } footer { line-height: 18px; + background-color: #fff; } .ugb-design-library-item__spinner { position: absolute; @@ -227,14 +228,17 @@ .ugb-design-library-items { - .stk-spinner-container .components-spinner { - display: block; - margin: 50px auto; - } - - &.stk-design-library__item-pages .stk-spinner-container { - height: 400px; + .stk-spinner-container { + height: 100%; + width: 100%; + position: absolute; + transition: opacity 0.3s cubic-bezier(0.2, 0.6, 0.4, 1); + will-change: opacity; + opacity: 1; display: flex; + &.stk-hide-spinner { + opacity: 0; + } .components-spinner { margin: auto; } diff --git a/src/components/design-library-list/index.js b/src/components/design-library-list/index.js index d04dff80c..fc1b5f89e 100644 --- a/src/components/design-library-list/index.js +++ b/src/components/design-library-list/index.js @@ -15,10 +15,11 @@ import classnames from 'classnames' import { Spinner } from '@wordpress/components' import { __ } from '@wordpress/i18n' import { - useState, useEffect, useRef, + useState, useEffect, useRef, memo, useMemo, } from '@wordpress/element' +import { usePresetControls } from '~stackable/hooks' -const DesignLibraryList = props => { +const DesignLibraryList = memo( props => { const { className = '', designs, @@ -26,6 +27,7 @@ const DesignLibraryList = props => { onSelectMulti, selectedDesigns = [], selectedDesignData = [], + selectedTab, } = props const containerRef = useRef( null ) @@ -61,14 +63,14 @@ const DesignLibraryList = props => { return ( ) } ) } @@ -79,7 +81,7 @@ const DesignLibraryList = props => {
} -} +} ) DesignLibraryList.defaultProps = { designs: [], @@ -91,24 +93,40 @@ DesignLibraryList.defaultProps = { export default DesignLibraryList const DesignLibraryItem = props => { - const { - previewProps: _previewProps, ...propsToPass - } = props - + const { selectedTab, designIndex } = props const wrapperRef = useRef( null ) - const itemRef = useRef( null ) - const [ cardHeight, setCardHeight ] = useState( {} ) - const [ previewSize, setPreviewSize ] = useState( {} ) - const [ shouldRender, setShouldRender ] = useState( props.designKey < 9 ) - - const previewProps = { - ..._previewProps, - setPreviewSize: previewSize => setPreviewSize( previewSize ), - setCardHeight: height => setCardHeight( height ), - cardHeight, - } + const [ shouldRender, setShouldRender ] = useState( designIndex < 9 ) + const { getPresetMarks } = usePresetControls( 'spacingSizes' ) + + // Intentionally no dependencies: presetMarks won't change while the design library is open + const presetMarks = useMemo( () => getPresetMarks() || null, [] ) useEffect( () => { + if ( selectedTab !== 'pages' ) { + return + } + let id + if ( typeof requestIdleCallback !== 'undefined' ) { + id = requestIdleCallback( () => ! shouldRender ? setShouldRender( true ) : {} ) + } else { + // fallback + id = setTimeout( () => setShouldRender( true ), designIndex * 20 ) + } + + return () => { + if ( typeof cancelIdleCallback !== 'undefined' ) { + cancelIdleCallback( id ) + } else { + clearTimeout( id ) + } + } + }, [ selectedTab ] ) + + useEffect( () => { + if ( selectedTab === 'pages' ) { + return + } + const rootEl = document.querySelector( '.ugb-modal-design-library__designs' ) if ( ! wrapperRef.current || ! rootEl ) { return @@ -122,30 +140,18 @@ const DesignLibraryItem = props => { }, { root: rootEl, rootMargin: '500px', + scrollMargin: '500px', threshold: 0, } ) observer.observe( wrapperRef.current ) - return () => observer.disconnect() - }, [] ) - const getCardHeight = () => { - const key = _previewProps.enableBackground ? 'background' : 'noBackground' - return props.selectedTab === 'pages' ? 472 : cardHeight?.[ key ] || 250 - } + return () => observer.disconnect() + }, [ selectedTab ] ) return (
- { ! shouldRender && ! props.selectedNum ? ( -
- ) : ( - - ) } +
) } diff --git a/src/components/design-library-list/use-auto-scroll.js b/src/components/design-library-list/use-auto-scroll.js index 5f4cbdaf0..5baf2336b 100644 --- a/src/components/design-library-list/use-auto-scroll.js +++ b/src/components/design-library-list/use-auto-scroll.js @@ -1,7 +1,9 @@ /** * WordPress dependencies */ -import { useRef, useEffect } from '@wordpress/element' +import { + useRef, useEffect, useCallback, +} from '@wordpress/element' const NOOP = () => {} @@ -101,7 +103,7 @@ export const useAutoScroll = ( hostRef, shadowBodySizeRef, selectedTab ) => { } } - const onMouseDownImpl = () => { + const onMouseDownImpl = useCallback( () => { // Stop auto-scrolling when user interacts isScrollingRef.current = false if ( animationFrameRef.current ) { @@ -115,7 +117,7 @@ export const useAutoScroll = ( hostRef, shadowBodySizeRef, selectedTab ) => { delayTimeoutRef.current = null } scrollPositionRef.current = -1 - } + }, [] ) // Cleanup any pending animation on unmount. useEffect( () => { diff --git a/src/components/design-library-list/use-preview-renderer.js b/src/components/design-library-list/use-preview-renderer.js index 5dfca6f16..b36031e59 100644 --- a/src/components/design-library-list/use-preview-renderer.js +++ b/src/components/design-library-list/use-preview-renderer.js @@ -35,41 +35,55 @@ import { cleanSerializedBlock } from '~stackable/util' const DEFAULT_CONTENT = { ...DEFAULT } export const usePreviewRenderer = ( - props, previewSize, plan, spacingSize, - selectedTab, selectedNum, selectedData, + props, shouldRender, spacingSize, ref, hostRef, shadowRoot, setIsLoading ) => { const { designId, template, category, + plan, containerScheme, backgroundScheme, enableBackground, + selectedTab, + selectedNum, + selectedData, onClick, - cardHeight, - setCardHeight, - setPreviewSize, } = props const [ blocks, setBlocks ] = useState( { parsed: null, serialized: '' } ) const [ content, setContent ] = useState( '' ) const [ contentForInsertion, setContentForInsertion ] = useState( null ) + const [ previewSize, setPreviewSize ] = useState( {} ) + const categoriesRef = useRef( [] ) const blocksForSubstitutionRef = useRef( false ) const hasBackgroundTargetRef = useRef( false ) - const initialRenderRef = useRef( null ) const shadowBodySizeRef = useRef( null ) const prevEnableBackgroundRef = useRef( null ) const prevSelectedTabRef = useRef( selectedTab ) const adjustAnimateFrameRef = useRef( null ) + const renderedTemplate = useRef( false ) const siteTitle = useSelect( select => select( 'core' ).getEntityRecord( 'root', 'site' )?.title || 'InnovateCo', [] ) const isDesignLibraryDevMode = devMode && localStorage.getItem( 'stk__design_library__dev_mode' ) === '1' const addHasBackground = selectedTab === 'patterns' + const updateShadowBodySize = _shadowBody => { + const shadowBody = _shadowBody || shadowRoot?.querySelector( 'body' ) + + if ( shadowBody ) { + shadowBodySizeRef.current = { + clientHeight: shadowBody.clientHeight, + scrollHeight: shadowBody.scrollHeight, + maxScrollTop: shadowBody.scrollHeight - shadowBody.clientHeight, + } + } + } + const adjustScale = ( force = true ) => { const parentDiv = ref?.current?.querySelector( '.stk-block-design__design-container' ) const shouldAdjust = ref.current && hostRef.current && shadowRoot && parentDiv && @@ -79,8 +93,6 @@ export const usePreviewRenderer = ( if ( ! shouldAdjust ) { return } - const newPreviewSize = { ...previewSize } - const newCardHeight = { ...cardHeight } const cardRect = ref.current.getBoundingClientRect() const hostRect = hostRef.current.getBoundingClientRect() @@ -102,9 +114,7 @@ export const usePreviewRenderer = ( const shadowBody = shadowRoot.querySelector( 'body' ) if ( shadowBody ) { - const cardWidth = cardRect.width // Get width of the card - const scaleFactor = cardWidth > 0 ? cardWidth / 1300 : 1 // Divide by 1300, which is the width of preview in the shadow DOM - newPreviewSize.scale = scaleFactor + const scaleFactor = cardWidth > 0 ? cardWidth / 1300 : 1 // Divide by 1300, which is the width of preview in the shadow DOM let _bodyHeight = 1200 if ( selectedTab === 'patterns' ) { @@ -113,33 +123,24 @@ export const usePreviewRenderer = ( const _height = parseFloat( _bodyHeight ) * scaleFactor // Also adjust the height - if ( Object.keys( newPreviewSize ).length === 1 ) { - newPreviewSize.heightBackground = _height - newPreviewSize.heightNoBackground = _height - } else { - const heightKey = enableBackground ? 'heightBackground' : 'heightNoBackground' - newPreviewSize[ heightKey ] = _height - } + // Update preview size more efficiently + setPreviewSize( prev => { + const newPreviewSize = { ...prev, scale: scaleFactor } - setPreviewSize( newPreviewSize ) + if ( Object.keys( prev ).length === 0 ) { + newPreviewSize.heightBackground = _height + newPreviewSize.heightNoBackground = _height + } else { + const heightKey = enableBackground ? 'heightBackground' : 'heightNoBackground' + newPreviewSize[ heightKey ] = _height + } - shadowBodySizeRef.current = { - clientHeight: shadowBody.clientHeight, - scrollHeight: shadowBody.scrollHeight, - maxScrollTop: shadowBody.scrollHeight - shadowBody.clientHeight, - } - } + return newPreviewSize + } ) - if ( ! Object.keys( newCardHeight ).length ) { - newCardHeight.background = cardRect.height - newCardHeight.noBackground = cardRect.height - } else { - const CardHeightKey = enableBackground ? 'background' : 'noBackground' - newCardHeight[ CardHeightKey ] = cardRect.height + updateShadowBodySize( shadowBody ) } - setTimeout( () => setCardHeight( newCardHeight ), 500 ) - if ( adjustAnimateFrameRef.current !== null ) { cancelAnimationFrame( adjustAnimateFrameRef.current ) } @@ -210,6 +211,15 @@ export const usePreviewRenderer = ( // Replace the placeholders with the default content useEffect( () => { + if ( ! shouldRender || renderedTemplate.current === template ) { + return + } + + // Reset per-template state and show spinner + setIsLoading( true ) + categoriesRef.current = [] + hasBackgroundTargetRef.current = false + let _parsedBlocks = [] let _parsedBlocksForInsertion = null const initialize = async () => { @@ -231,31 +241,42 @@ export const usePreviewRenderer = ( _parsedBlocks = cleanParse( _contentForPreview ) _parsedBlocksForInsertion = isDesignLibraryDevMode ? cleanParse( _contentForInsertion ) : null } else { - for ( let i = 0; i < _content.length; i++ ) { - const section = _content[ i ] - const design = await fetchDesign( section.designId || section.id ) - const categorySlug = getCategorySlug( section.designId || section.id ) + // Fetch all designs first, then run cleanParse once for all + const designIds = _content.map( section => section.designId || section.id ) + const designs = await Promise.all( designIds.map( id => fetchDesign( id ) ) ) + const categorySlugs = designIds.map( id => getCategorySlug( id ) ) - // For preview: always replace placeholders (ignore dev mode) - const designContentForPreview = replacePlaceholders( design.template || design.content, categorySlug, false ) - // For insertion: only create separate content if dev mode is enabled - const designContentForInsertion = isDesignLibraryDevMode ? replacePlaceholders( design.template || design.content, categorySlug, true ) : designContentForPreview + // For preview: always replace placeholders (ignore dev mode) + const designsContentForPreview = designs.map( ( design, i ) => + replacePlaceholders( design.template || design.content, categorySlugs[ i ], false ) + ).join( '\n' ) + // For insertion: only create separate content if dev mode is enabled + const designsContentForInsertion = isDesignLibraryDevMode + ? designs.map( ( design, i ) => + replacePlaceholders( design.template || design.content, categorySlugs[ i ], true ) + ).join( '\n' ) + : designsContentForPreview - categoriesRef.current.push( categorySlug ) + categoriesRef.current.push( ...categorySlugs ) - let _block = cleanParse( designContentForPreview )[ 0 ] - let _blockForInsertion = isDesignLibraryDevMode ? cleanParse( designContentForInsertion )[ 0 ] : null + // Run cleanParse once for all preview contents + const blocks = cleanParse( designsContentForPreview ) + const blocksForInsertion = isDesignLibraryDevMode ? cleanParse( designsContentForInsertion ) : null + + for ( let i = 0; i < _content.length; i++ ) { + let _block = blocks[ i ] + let _blockForInsertion = isDesignLibraryDevMode && blocksForInsertion ? blocksForInsertion[ i ] : null - if ( section.bg ) { + if ( _content[ i ].bg ) { _block = addBackgroundScheme( [ _block ], true, '' )[ 0 ] if ( _blockForInsertion ) { _blockForInsertion = addBackgroundScheme( [ _blockForInsertion ], true, '' )[ 0 ] } } - adjustPatternSpacing( _block.attributes, categorySlug, spacingSize, false ) + adjustPatternSpacing( _block.attributes, categorySlugs[ i ], spacingSize, false ) if ( _blockForInsertion ) { - adjustPatternSpacing( _blockForInsertion.attributes, categorySlug, spacingSize, true ) + adjustPatternSpacing( _blockForInsertion.attributes, categorySlugs[ i ], spacingSize, true ) } _parsedBlocks.push( _block ) if ( _blockForInsertion ) { @@ -275,39 +296,43 @@ export const usePreviewRenderer = ( setContent( parsedBlocks ) setContentForInsertion( parsedBlocksForInsertion ) setIsLoading( false ) + renderedTemplate.current = template } ) - }, [ template ] ) + }, [ template, shouldRender ] ) useEffect( () => { - if ( ! initialRenderRef.current ) { - initialRenderRef.current = true + prevSelectedTabRef.current = selectedTab + }, [ selectedTab ] ) + + useEffect( () => { + if ( ! shouldRender ) { return } - if ( ! content || - ! shadowRoot || - // don't re-render if design is selected and tab didn't change - ( selectedNum && prevSelectedTabRef.current === selectedTab ) - ) { + if ( ! content || ! shadowRoot ) { return } - renderPreview() - }, [ content, containerScheme, backgroundScheme, enableBackground ] ) + // Don't re-render if design is selected and tab didn't change + if ( selectedNum && prevSelectedTabRef.current === selectedTab ) { + return + } - // Re-render and adjust scale if design was unselected. - useEffect( () => { - if ( selectedNum === 0 && content && shadowRoot ) { + // Render preview when content or schemes change + if ( content ) { renderPreview() - if ( adjustAnimateFrameRef.current !== null ) { - cancelAnimationFrame( adjustAnimateFrameRef.current ) - } - adjustAnimateFrameRef.current = requestAnimationFrame( adjustScale ) } - }, [ selectedNum ] ) + // Schedule scale adjustment + if ( adjustAnimateFrameRef.current !== null ) { + cancelAnimationFrame( adjustAnimateFrameRef.current ) + } + adjustAnimateFrameRef.current = requestAnimationFrame( adjustScale ) + }, [ content, containerScheme, backgroundScheme, enableBackground, selectedNum, shouldRender, shadowRoot ] ) + + // Handle background changes separately to avoid unnecessary re-renders useEffect( () => { - if ( ! blocks.parsed || ! blocks.serialized ) { + if ( ! blocks.parsed || ! blocks.serialized || ! shouldRender ) { return } @@ -319,23 +344,7 @@ export const usePreviewRenderer = ( } adjustAnimateFrameRef.current = requestAnimationFrame( adjustScale ) } - }, [ blocks ] ) - - // If categories change, adjust preview sizes - useEffect( () => { - if ( ! content || ! blocks.parsed || ! blocks.serialized ) { - return - } - - if ( adjustAnimateFrameRef.current !== null ) { - cancelAnimationFrame( adjustAnimateFrameRef.current ) - } - - adjustAnimateFrameRef.current = requestAnimationFrame( () => { - adjustScale() - prevSelectedTabRef.current = selectedTab - } ) - }, [ content ] ) + }, [ blocks, enableBackground ] ) // cleanup any pending animation on unmount useEffect( () => { @@ -364,6 +373,7 @@ export const usePreviewRenderer = ( return { blocks: blocks.serialized, enableBackground, shadowBodySizeRef, blocksForSubstitutionRef, - onClickDesign, + previewSize, onClickDesign, + updateShadowBodySize, } } diff --git a/src/components/design-library-list/use-shadow-root.js b/src/components/design-library-list/use-shadow-root.js index edb55e656..2c0f96162 100644 --- a/src/components/design-library-list/use-shadow-root.js +++ b/src/components/design-library-list/use-shadow-root.js @@ -1,13 +1,15 @@ import { wpGlobalStylesInlineCss } from 'stackable' import { getAdditionalStylesForPreview } from './util' -import { useEffect, useRef } from '@wordpress/element' +import { + useEffect, useRef, useState, +} from '@wordpress/element' import { useSelect } from '@wordpress/data' import { applyFilters } from '@wordpress/hooks' -export const useShadowRoot = () => { +export const useShadowRoot = shouldRender => { const hostRef = useRef( null ) - const shadowRoot = useRef( null ) + const [ shadowRoot, setShadowRoot ] = useState( null ) const { getEditorDom } = useSelect( 'stackable/editor-dom' ) const editorDom = getEditorDom() @@ -21,7 +23,7 @@ export const useShadowRoot = () => { ] ) useEffect( () => { - if ( hostRef.current ) { + if ( shouldRender && hostRef.current ) { const shadow = hostRef.current.shadowRoot || hostRef.current.attachShadow( { mode: 'open' } ) const styleNodes = STYLE_IDS.map( id => { @@ -67,9 +69,9 @@ export const useShadowRoot = () => { shadow.appendChild( node ) } ) - shadowRoot.current = shadow + setShadowRoot( shadow ) } - }, [] ) + }, [ shouldRender ] ) - return { hostRef, shadowRoot: shadowRoot.current } + return { hostRef, shadowRoot } } diff --git a/src/components/modal-design-library/editor.scss b/src/components/modal-design-library/editor.scss index 4520bf5c0..ee5408980 100644 --- a/src/components/modal-design-library/editor.scss +++ b/src/components/modal-design-library/editor.scss @@ -44,9 +44,8 @@ height: 100%; &.ugb-modal-design-library__full-pages { - grid-template-rows: auto auto; .ugb-modal-design-library__designs { - grid-row: 1 / -1; + grid-row: 1 / 4; } } } diff --git a/src/components/modal-design-library/index.js b/src/components/modal-design-library/index.js index 4d802ac2d..e70740123 100644 --- a/src/components/modal-design-library/index.js +++ b/src/components/modal-design-library/index.js @@ -7,7 +7,7 @@ import { ModalDesignLibrary } from './modal' * WordPress dependencies */ import { applyFilters } from '@wordpress/hooks' -import { useMemo } from '@wordpress/element' +import { useMemo, useCallback } from '@wordpress/element' import { useLocalStorage } from '~stackable/util' export const Switcher = props => { @@ -25,11 +25,13 @@ export const Switcher = props => { return applyFilters( 'stackable.design-library.modal-component', ModalDesignLibrary, apiVersion ) }, [ apiVersion ] ) + const onChangeApiVersion = useCallback( v => setApiVersion( v ), [] ) + return ( 1 } apiVersion={ apiVersion } - onChangeApiVersion={ setApiVersion } + onChangeApiVersion={ onChangeApiVersion } { ...props } /> ) diff --git a/src/components/modal-design-library/modal.js b/src/components/modal-design-library/modal.js index bc9e4e364..b500afc0e 100644 --- a/src/components/modal-design-library/modal.js +++ b/src/components/modal-design-library/modal.js @@ -29,7 +29,9 @@ import { Spinner, ToggleControl, } from '@wordpress/components' -import { useEffect, useState } from '@wordpress/element' +import { + useEffect, useState, useCallback, +} from '@wordpress/element' import { sprintf, __ } from '@wordpress/i18n' import { useBlockColorSchemes } from '~stackable/hooks' import ColorSchemePreview from '../color-scheme-preview' @@ -129,6 +131,40 @@ export const ModalDesignLibrary = props => { props.onSelect( designs, cb, selectedTab ) } + const onSelectDesign = useCallback( ( designId, category, parsedBlocks, blocksForSubstitution, selectedPreviewSize ) => { + if ( selectedTab === 'pages' ) { + const selectedDesign = [ { + designId, category, designData: parsedBlocks, blocksForSubstitution, selectedPreviewSize, + } ] + addDesign( selectedDesign ) + + return + } + + const newSelectedDesigns = [ ...selectedDesignIds ] + // We also get the design data from displayDesigns + // already instead of after clicking the "Add + // Designs" button since displayDesigns can change + // when the user is switching tabs (block/ui + // kits/wireframes) and the data can be lost. + const newSelectedDesignData = [ ...selectedDesignData ] + + if ( newSelectedDesigns.includes( designId ) ) { + const i = newSelectedDesigns.indexOf( designId ) + newSelectedDesigns.splice( i, 1 ) + setSelectedDesignIds( newSelectedDesigns ) + newSelectedDesignData.splice( i, 1 ) + setSelectedDesignData( newSelectedDesignData ) + } else { + newSelectedDesigns.push( designId ) + setSelectedDesignIds( newSelectedDesigns ) + newSelectedDesignData.push( { + designId, category, designData: parsedBlocks, blocksForSubstitution, selectedPreviewSize, + } ) + setSelectedDesignData( newSelectedDesignData ) + } + }, [ selectedTab, selectedDesignIds, selectedDesignData ] ) + return ( { designs={ displayDesigns } selectedDesigns={ selectedDesignIds } selectedDesignData={ selectedDesignData } - onSelectMulti={ ( designId, category, parsedBlocks, blocksForSubstitution, selectedPreviewSize ) => { - if ( selectedTab === 'pages' ) { - const selectedDesign = [ { - designId, category, designData: parsedBlocks, blocksForSubstitution, selectedPreviewSize, - } ] - addDesign( selectedDesign ) - - return - } - - const newSelectedDesigns = [ ...selectedDesignIds ] - // We also get the design data from displayDesigns - // already instead of after clicking the "Add - // Designs" button since displayDesigns can change - // when the user is switching tabs (block/ui - // kits/wireframes) and the data can be lost. - const newSelectedDesignData = [ ...selectedDesignData ] - - if ( newSelectedDesigns.includes( designId ) ) { - const i = newSelectedDesigns.indexOf( designId ) - newSelectedDesigns.splice( i, 1 ) - setSelectedDesignIds( newSelectedDesigns ) - newSelectedDesignData.splice( i, 1 ) - setSelectedDesignData( newSelectedDesignData ) - } else { - newSelectedDesigns.push( designId ) - setSelectedDesignIds( newSelectedDesigns ) - newSelectedDesignData.push( { - designId, category, designData: parsedBlocks, blocksForSubstitution, selectedPreviewSize, - } ) - setSelectedDesignData( newSelectedDesignData ) - } - } } + onSelectMulti={ onSelectDesign } /> { selectedTab === 'patterns' &&