File "pattern.js"
Full path: /home/webcknlt/admissiontell.com/wp-content/plugins/generateblocks/src/pattern-library/components/pattern.js
File
size: 7.26 B (7.26 KB bytes)
MIME-type: text/html
Charset: utf-8
Download Open Edit Advanced Editor &nnbsp; Back
import { Spinner } from '@wordpress/components';
import { useEffect, useRef, useState, useLayoutEffect } from '@wordpress/element';
import imagesLoaded from 'imagesloaded';
import { useLibrary } from './library-provider';
export default function Pattern( { pattern, isLoading, isActive = false, globalStyleCSS } ) {
const {
id,
preview,
label,
scripts = [],
styles = [],
} = pattern;
const iframeRef = useRef();
const elementRef = useRef();
const [ height, setHeight ] = useState( 0 );
const [ injectContent, setInjectContent ] = useState( false );
const [ editorColors, setEditorColors ] = useState( {} );
const [ isLoaded, setIsLoaded ] = useState( false );
const [ patternWidth, setPatternWidth ] = useState( 0 );
const [ isResizing, setIsResizing ] = useState( false );
const { previewIframeWidth } = useLibrary();
const patternHeight = 350;
const viewport = patternWidth;
const iframe = 1280;
const editorStylesWrapper = document?.querySelector( '.editor-styles-wrapper' );
/**
* Debounce our resizing event.
* This is used to re-calculate the pattern width.
*/
useEffect( () => {
let resizeTimer;
const handleResize = () => {
setIsResizing( true );
clearTimeout( resizeTimer );
resizeTimer = setTimeout( () => {
setIsResizing( false );
}, 500 );
};
window.addEventListener( 'resize', handleResize );
return () => {
window.removeEventListener( 'resize', handleResize );
};
}, [] );
/**
* Set the width of our patterns.
*/
useEffect( () => {
if ( elementRef.current?.clientWidth && ! isResizing ) {
setPatternWidth( elementRef.current?.clientWidth );
}
}, [ elementRef.current?.clientWidth, isResizing ] );
/**
* Insert our pattern preview into the empty iframe.
*/
useEffect( () => {
if ( ! injectContent ) {
return;
}
const document = iframeRef.current.contentWindow.document;
scripts.forEach( ( script ) => {
const scriptElement = document.createElement( 'script' );
scriptElement.defer = true;
scriptElement.src = script;
document.head.appendChild( scriptElement );
} );
styles.forEach( ( style ) => {
const styleElement = document.createElement( 'link' );
styleElement.rel = 'stylesheet';
styleElement.href = style;
document.head.appendChild( styleElement );
} );
document.body.innerHTML = preview;
document.head.innerHTML += '<style id="block-active"></style>';
document.head.innerHTML += '<style id="pattern-styles"></style>';
const globalStyleElement = document.createElement( 'style' );
globalStyleElement.innerHTML = globalStyleCSS;
const firstStyleElement = document.querySelector( 'head style' );
document.head.insertBefore( globalStyleElement, firstStyleElement );
imagesLoaded( document.body, () => {
setHeight( document.body.scrollHeight );
setIsLoaded( true );
} );
}, [ injectContent ] );
/**
* Store our editor background and text color.
*/
useEffect( () => {
if ( ! editorStylesWrapper ) {
return;
}
const editorStyles = getComputedStyle( editorStylesWrapper );
if ( editorStyles ) {
setEditorColors( { background: editorStyles.backgroundColor, text: editorStyles.color } );
}
}, [ editorStylesWrapper?.style ] );
/**
* Mimic our editor styles in the pattern preview.
* This allows our patterns to have the same background/text colors as the editor.
*/
useLayoutEffect( () => {
const document = iframeRef.current?.contentWindow?.document;
if ( document && document.querySelector && document.querySelector( '#pattern-styles' ) ) {
document.querySelector( '#pattern-styles' ).innerHTML = `body{background-color:${ editorColors?.background };color:${ editorColors?.text };}`;
}
}, [ editorColors, height ] );
/**
* Set the height of the preview iframe.
*/
useEffect( () => {
if ( ! isActive ) {
return;
}
const document = iframeRef?.current?.contentWindow?.document;
imagesLoaded( document?.body, () => {
setHeight( document?.body?.scrollHeight );
} );
}, [ previewIframeWidth ] );
/**
* Add padding and center the pattern if it's not full width.
*/
useEffect( () => {
const iframeDocument = iframeRef.current.contentWindow.document;
const iframeBody = iframeDocument.body;
if ( ! iframeBody ) {
return;
}
const elements = Array.from( iframeBody.querySelectorAll( '*' ) );
const firstVisibleElement = elements?.find( ( element ) => {
const { display } = getComputedStyle( element );
return display !== 'none';
} );
if ( firstVisibleElement ) {
const parentWidth = firstVisibleElement.parentElement.clientWidth;
const isFullWidth = firstVisibleElement.offsetWidth === parentWidth;
if ( ! isFullWidth ) {
iframeBody.style.padding = '100px';
firstVisibleElement.style.marginLeft = 'auto';
firstVisibleElement.style.marginRight = 'auto';
}
}
}, [ patternWidth, injectContent ] );
const viewportHeight = Math.round( height * ( viewport / iframe ) );
const wrapperStyle = {
opacity: isLoading ? 0 : 1,
height: patternHeight + 'px',
display: 'flex',
flexDirection: 'column',
justifyContent: ( viewportHeight + 40 ) < patternHeight ? 'center' : '',
};
const sandbox = [
'allow-same-origin',
];
if ( isActive ) {
sandbox.push( 'allow-scripts' );
}
return (
<div
className="gb-pattern-frame"
style={ isActive ? {
backgroundColor: 'none',
padding: 0,
} : {} }
>
<div
ref={ elementRef }
className="gb-pattern"
style={ ! isActive ? wrapperStyle : { minHeight: '200px' } }
>
{ ! isLoaded && <Spinner /> }
<div
style={ ! isActive ? {
width: `${ viewport }px`,
height: `${ viewportHeight }px`,
} : {} }
>
<div
style={ ! isActive ? {
height: height + 'px',
width: `${ ( ( iframe / viewport ) * 100 ) }%`,
transformOrigin: '0 0',
transform: `scale( ${ viewport / iframe } )`,
} : {} }
>
<iframe
id={ id }
onLoad={ () => {
setInjectContent( true );
const iframeDoc = iframeRef.current.contentDocument || iframeRef.current.contentWindow.document;
iframeDoc.addEventListener( 'click', ( event ) => {
const clickedElement = event.target;
if ( 'A' === clickedElement.tagName ) {
const href = clickedElement.getAttribute( 'href' );
if ( href && ! href.startsWith( '#' ) ) {
event.preventDefault();
event.stopPropagation();
}
}
// Reset our height when we click anything in our preview.
// This accounts for height changes from accordions etc...
if ( isActive ) {
setTimeout( () => {
setHeight( iframeDoc.body.scrollHeight );
}, 500 );
}
} );
} }
title={ label }
src={ generateBlocksPatternLibrary.patternPreviewUrl }
ref={ iframeRef }
style={ {
height: height + 'px',
border: '0',
pointerEvents: ! isActive ? 'none' : '',
width: isActive ? previewIframeWidth : `${ iframe }px`,
opacity: ! isLoaded ? 0 : 1,
display: ( isActive && '100%' !== previewIframeWidth ) ? 'block' : '',
margin: isActive && '100%' !== previewIframeWidth ? '0 auto' : '',
} }
tabIndex="-1"
loading="lazy"
sandbox={ sandbox.join( ' ' ) }
/>
</div>
</div>
</div>
</div>
);
}