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 49caca70c9..8864a7ddcb 100644 --- a/src/components/design-library-list/design-library-list-item.js +++ b/src/components/design-library-list/design-library-list-item.js @@ -46,11 +46,11 @@ const DesignLibraryListItem = forwardRef( ( props, ref ) => { const { blocks, enableBackground, shadowBodySizeRef, blocksForSubstitutionRef, - adjustScale, onClickDesign, + onClickDesign, } = usePreviewRenderer( previewProps, previewSize, plan, spacingSize, selectedTab, selectedNum, selectedData, - ref, shadowRoot, setIsLoading + ref, hostRef, shadowRoot, setIsLoading, ) const { @@ -58,6 +58,10 @@ const DesignLibraryListItem = forwardRef( ( props, ref ) => { } = useAutoScroll( hostRef, shadowBodySizeRef, selectedTab ) const getDesignPreviewSize = () => { + if ( ! shadowRoot || isLoading ) { + return 0 + } + return selectedNum && selectedData ? selectedData.selectedPreviewSize.preview : ( enableBackground ? previewSize.heightBackground : previewSize.heightNoBackground ) } @@ -88,7 +92,7 @@ const DesignLibraryListItem = forwardRef( ( props, ref ) => { onMouseOver={ onMouseOver } > { ! isPro && plan !== 'free' && } -
100 ? 'stk--design-preview-large' : 'stk--design-preview-small' }` }> +
100 ? 'stk--design-preview-large' : 'stk--design-preview-small' }` }> { ! isPro && plan !== 'free' && ( { } } >
- { shadowRoot && }
diff --git a/src/components/design-library-list/design-preview.js b/src/components/design-library-list/design-preview.js index ae8ee49381..a4b3fa06f2 100644 --- a/src/components/design-library-list/design-preview.js +++ b/src/components/design-library-list/design-preview.js @@ -13,7 +13,6 @@ export const DesignPreview = ( { blocks = '', shadowRoot, selectedTab, - adjustScale = NOOP, onMouseDown = NOOP, } ) => { const ref = useRef( null ) @@ -72,11 +71,6 @@ export const DesignPreview = ( { } }, [ selectedTab ] ) - useEffect( () => { - // The scale might not be correct on first load, so adjust it again to be sure. - setTimeout( adjustScale, 100 ) - }, [] ) - const shadowBodyClasses = classnames( applyFilters( 'stackable.global-styles.classnames', [ 'entry-content', ] ), { diff --git a/src/components/design-library-list/editor.scss b/src/components/design-library-list/editor.scss index 31b0693578..e07ff2863d 100644 --- a/src/components/design-library-list/editor.scss +++ b/src/components/design-library-list/editor.scss @@ -27,6 +27,7 @@ background: #f4f4f5; } .ugb-design-library-item { + width: 100%; display: grid; grid-template-columns: 1fr; padding: 0; diff --git a/src/components/design-library-list/index.js b/src/components/design-library-list/index.js index af15f91f55..d04dff80c5 100644 --- a/src/components/design-library-list/index.js +++ b/src/components/design-library-list/index.js @@ -29,8 +29,6 @@ const DesignLibraryList = props => { } = props const containerRef = useRef( null ) - const [ scrollTop, setScrollTop ] = useState( 0 ) - const listClasses = classnames( [ 'ugb-design-library-items', className, @@ -43,9 +41,6 @@ const DesignLibraryList = props => { return
{ - setScrollTop( e.currentTarget.scrollTop ) - } } > { isBusy && } { ! isBusy && <> @@ -73,7 +68,6 @@ const DesignLibraryList = props => { selectedNum={ selectedNum } selectedData={ selectedData } selectedTab={ props.selectedTab } - scrollTop={ scrollTop } designKey={ i } /> ) @@ -98,9 +92,10 @@ export default DesignLibraryList const DesignLibraryItem = props => { const { - scrollTop, previewProps: _previewProps, ...propsToPass + previewProps: _previewProps, ...propsToPass } = props + const wrapperRef = useRef( null ) const itemRef = useRef( null ) const [ cardHeight, setCardHeight ] = useState( {} ) const [ previewSize, setPreviewSize ] = useState( {} ) @@ -114,43 +109,43 @@ const DesignLibraryItem = props => { } useEffect( () => { - // Use a timeout to ensure designs have finished rendering before calculating visibility. - const timeoutRef = setTimeout( () => { - const itemEl = itemRef.current - const containerEl = itemEl?.closest( '.ugb-modal-design-library__designs' ) || document.querySelector( '.ugb-modal-design-library__designs' ) - - if ( ! itemEl || ! containerEl ) { - return - } - - const containerRect = containerEl.getBoundingClientRect() - const itemRect = itemEl.getBoundingClientRect() - - const BOUNDARY = 250 - - const render = itemRect.bottom >= containerRect.top - BOUNDARY && itemRect.top <= containerRect.bottom + BOUNDARY - - setShouldRender( render ) - }, 250 ) - - return () => { - clearTimeout( timeoutRef ) + const rootEl = document.querySelector( '.ugb-modal-design-library__designs' ) + if ( ! wrapperRef.current || ! rootEl ) { + return } - }, [ scrollTop, _previewProps.enableBackground, _previewProps.designId ] ) + + const observer = new IntersectionObserver( ( [ entry ] ) => { + // reduce flicker during rapid scrolls + requestAnimationFrame( () => { + requestAnimationFrame( () => setShouldRender( entry.isIntersecting ) ) + } ) + }, { + root: rootEl, + rootMargin: '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 } - if ( ! shouldRender && ! props.selectedNum ) { - return
- } - - return + return ( +
+ { ! shouldRender && ! props.selectedNum ? ( +
+ ) : ( + + ) } +
+ ) } diff --git a/src/components/design-library-list/use-preview-renderer.js b/src/components/design-library-list/use-preview-renderer.js index a2579fad32..5dfca6f16d 100644 --- a/src/components/design-library-list/use-preview-renderer.js +++ b/src/components/design-library-list/use-preview-renderer.js @@ -37,7 +37,7 @@ const DEFAULT_CONTENT = { ...DEFAULT } export const usePreviewRenderer = ( props, previewSize, plan, spacingSize, selectedTab, selectedNum, selectedData, - ref, shadowRoot, setIsLoading + ref, hostRef, shadowRoot, setIsLoading ) => { const { designId, @@ -61,64 +61,89 @@ export const usePreviewRenderer = ( const hasBackgroundTargetRef = useRef( false ) const initialRenderRef = useRef( null ) const shadowBodySizeRef = useRef( null ) - const prevEnableBackgroundRef = useRef( false ) + const prevEnableBackgroundRef = useRef( null ) const prevSelectedTabRef = useRef( selectedTab ) + const adjustAnimateFrameRef = useRef( null ) 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 adjustScale = () => { - const shouldAdjust = ref.current && shadowRoot && + const adjustScale = ( force = true ) => { + const parentDiv = ref?.current?.querySelector( '.stk-block-design__design-container' ) + const shouldAdjust = ref.current && hostRef.current && shadowRoot && parentDiv && ( ! selectedNum || // adjust if design is not selected prevSelectedTabRef.current !== selectedTab ) // adjust if selected tab changed even if design is selected - if ( shouldAdjust ) { - const newPreviewSize = { ...previewSize } - const newCardHeight = { ...cardHeight } - const cardRect = ref.current.getBoundingClientRect() + if ( ! shouldAdjust ) { + return + } + const newPreviewSize = { ...previewSize } + const newCardHeight = { ...cardHeight } - 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 cardRect = ref.current.getBoundingClientRect() + const hostRect = hostRef.current.getBoundingClientRect() + const parentDivRect = parentDiv.getBoundingClientRect() - let _bodyHeight = 1200 - if ( selectedTab === 'patterns' ) { - _bodyHeight = shadowBody.offsetHeight - } + const cardWidth = cardRect.width + const hostWidth = hostRect.width - const _height = parseFloat( _bodyHeight ) * scaleFactor // Also adjust the height + // Consider heights equal if the difference is less than 1px + const isEqualHeight = Math.abs( parentDivRect.height - hostRect.height ) < 1 - if ( Object.keys( newPreviewSize ).length === 1 ) { - newPreviewSize.heightBackground = _height - newPreviewSize.heightNoBackground = _height - } else { - const heightKey = enableBackground ? 'heightBackground' : 'heightNoBackground' - newPreviewSize[ heightKey ] = _height - } + if ( ! force && cardWidth === hostWidth && isEqualHeight ) { + if ( adjustAnimateFrameRef.current !== null ) { + cancelAnimationFrame( adjustAnimateFrameRef.current ) + } + adjustAnimateFrameRef.current = null + return + } - setPreviewSize( newPreviewSize ) + 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 - shadowBodySizeRef.current = { - clientHeight: shadowBody.clientHeight, - scrollHeight: shadowBody.scrollHeight, - maxScrollTop: shadowBody.scrollHeight - shadowBody.clientHeight, - } + let _bodyHeight = 1200 + if ( selectedTab === 'patterns' ) { + _bodyHeight = shadowBody.offsetHeight } - if ( ! Object.keys( newCardHeight ).length ) { - newCardHeight.background = cardRect.height - newCardHeight.noBackground = cardRect.height + const _height = parseFloat( _bodyHeight ) * scaleFactor // Also adjust the height + + if ( Object.keys( newPreviewSize ).length === 1 ) { + newPreviewSize.heightBackground = _height + newPreviewSize.heightNoBackground = _height } else { - const CardHeightKey = enableBackground ? 'background' : 'noBackground' - newCardHeight[ CardHeightKey ] = cardRect.height + const heightKey = enableBackground ? 'heightBackground' : 'heightNoBackground' + newPreviewSize[ heightKey ] = _height + } + + setPreviewSize( newPreviewSize ) + + shadowBodySizeRef.current = { + clientHeight: shadowBody.clientHeight, + scrollHeight: shadowBody.scrollHeight, + maxScrollTop: shadowBody.scrollHeight - shadowBody.clientHeight, } + } + + if ( ! Object.keys( newCardHeight ).length ) { + newCardHeight.background = cardRect.height + newCardHeight.noBackground = cardRect.height + } else { + const CardHeightKey = enableBackground ? 'background' : 'noBackground' + newCardHeight[ CardHeightKey ] = cardRect.height + } + + setTimeout( () => setCardHeight( newCardHeight ), 500 ) - setTimeout( () => setCardHeight( newCardHeight ), 500 ) + if ( adjustAnimateFrameRef.current !== null ) { + cancelAnimationFrame( adjustAnimateFrameRef.current ) } + adjustAnimateFrameRef.current = requestAnimationFrame( () => adjustScale( false ) ) } const renderPreview = ( blockContent = content ) => { @@ -274,7 +299,10 @@ export const usePreviewRenderer = ( useEffect( () => { if ( selectedNum === 0 && content && shadowRoot ) { renderPreview() - setTimeout( adjustScale, 100 ) + if ( adjustAnimateFrameRef.current !== null ) { + cancelAnimationFrame( adjustAnimateFrameRef.current ) + } + adjustAnimateFrameRef.current = requestAnimationFrame( adjustScale ) } }, [ selectedNum ] ) @@ -285,7 +313,11 @@ export const usePreviewRenderer = ( if ( prevEnableBackgroundRef.current !== enableBackground ) { prevEnableBackgroundRef.current = enableBackground - adjustScale() + + if ( adjustAnimateFrameRef.current !== null ) { + cancelAnimationFrame( adjustAnimateFrameRef.current ) + } + adjustAnimateFrameRef.current = requestAnimationFrame( adjustScale ) } }, [ blocks ] ) @@ -294,12 +326,25 @@ export const usePreviewRenderer = ( if ( ! content || ! blocks.parsed || ! blocks.serialized ) { return } - setTimeout( () => { + + if ( adjustAnimateFrameRef.current !== null ) { + cancelAnimationFrame( adjustAnimateFrameRef.current ) + } + + adjustAnimateFrameRef.current = requestAnimationFrame( () => { adjustScale() prevSelectedTabRef.current = selectedTab - }, 100 ) + } ) }, [ content ] ) + // cleanup any pending animation on unmount + useEffect( () => { + return () => { + cancelAnimationFrame( adjustAnimateFrameRef.current ) + adjustAnimateFrameRef.current = null + } + }, [] ) + const onClickDesign = () => { if ( ! isPro && plan !== 'free' ) { return @@ -319,6 +364,6 @@ export const usePreviewRenderer = ( return { blocks: blocks.serialized, enableBackground, shadowBodySizeRef, blocksForSubstitutionRef, - adjustScale, onClickDesign, + onClickDesign, } } diff --git a/src/components/modal-design-library/editor.scss b/src/components/modal-design-library/editor.scss index 2aa1a4f83d..4520bf5c03 100644 --- a/src/components/modal-design-library/editor.scss +++ b/src/components/modal-design-library/editor.scss @@ -45,6 +45,9 @@ &.ugb-modal-design-library__full-pages { grid-template-rows: auto auto; + .ugb-modal-design-library__designs { + grid-row: 1 / -1; + } } } .ugb-modal-design-library__sidebar {