Revert "CYS: Revert Zoom Feature (#50535)"
This reverts commit 16ef869587
.
This commit is contained in:
parent
0f7773dd47
commit
4ff24a0188
|
@ -0,0 +1,145 @@
|
||||||
|
/* eslint-disable @woocommerce/dependency-group */
|
||||||
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import React from 'react';
|
||||||
|
import { useDispatch, useSelect } from '@wordpress/data';
|
||||||
|
import { useContext } from '@wordpress/element';
|
||||||
|
// @ts-ignore No types for this exist yet.
|
||||||
|
import { store as editorStore } from '@wordpress/editor';
|
||||||
|
import { Icon, desktop, tablet, mobile } from '@wordpress/icons';
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import {
|
||||||
|
// @ts-ignore No types for this exist yet.
|
||||||
|
NavigableMenu,
|
||||||
|
Circle,
|
||||||
|
SVG,
|
||||||
|
Path,
|
||||||
|
} from '@wordpress/components';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { ZoomOutContext } from '../context/zoom-out-context';
|
||||||
|
|
||||||
|
const zoomIn = (
|
||||||
|
<SVG width="24" height="24" viewBox="0 0 24 24">
|
||||||
|
<Circle cx="11" cy="11" r="7.25" fill="transparent" strokeWidth="1.5" />
|
||||||
|
<Path d="M8 11H14M11 8V14" strokeWidth="1.5" />
|
||||||
|
<Path d="M16 16L20 20" strokeWidth="1.5" />
|
||||||
|
</SVG>
|
||||||
|
);
|
||||||
|
|
||||||
|
const zoomOut = (
|
||||||
|
<SVG width="24" height="24" viewBox="0 0 24 24">
|
||||||
|
<Circle cx="11" cy="11" r="7.25" fill="transparent" strokeWidth="1.5" />
|
||||||
|
<Path d="M16 16L20 20" strokeWidth="1.5" />
|
||||||
|
<Path d="M8 11H14" strokeWidth="1.5" />
|
||||||
|
</SVG>
|
||||||
|
);
|
||||||
|
|
||||||
|
const BUTTON_CLASS_NAMES =
|
||||||
|
'components-button has-icon woocommerce-customize-store__device-button';
|
||||||
|
const ICON_CLASS_NAMES = 'woocommerce-customize-store__device-icon';
|
||||||
|
|
||||||
|
export function DeviceToolbar( { isEditorLoading = false } ) {
|
||||||
|
// @ts-expect-error expect error
|
||||||
|
const { setDeviceType } = useDispatch( editorStore );
|
||||||
|
const { toggleZoomOut, isZoomedOut } = useContext( ZoomOutContext );
|
||||||
|
|
||||||
|
const { deviceType } = useSelect( ( select ) => {
|
||||||
|
// @ts-ignore expect error
|
||||||
|
const { getDeviceType } = select( editorStore );
|
||||||
|
|
||||||
|
return {
|
||||||
|
deviceType: getDeviceType(),
|
||||||
|
};
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Zoom Out isn't available on mobile or tablet.
|
||||||
|
// In this case, we want to switch to desktop mode when zooming out.
|
||||||
|
const switchDeviceType = ( newDeviceType: string ) => {
|
||||||
|
if ( isZoomedOut ) {
|
||||||
|
toggleZoomOut();
|
||||||
|
}
|
||||||
|
setDeviceType( newDeviceType );
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NavigableMenu
|
||||||
|
className="woocommerce-customize-store__device-toolbar"
|
||||||
|
orientation="horizontal"
|
||||||
|
role="toolbar"
|
||||||
|
aria-label={ __( 'Resize Options', 'woocommerce' ) }
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
disabled={ isEditorLoading }
|
||||||
|
className={ clsx( BUTTON_CLASS_NAMES, {
|
||||||
|
'is-selected': deviceType === 'Desktop',
|
||||||
|
} ) }
|
||||||
|
aria-label="Desktop View"
|
||||||
|
onClick={ () => {
|
||||||
|
switchDeviceType( 'Desktop' );
|
||||||
|
} }
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon={ desktop }
|
||||||
|
size={ 30 }
|
||||||
|
className={ clsx( ICON_CLASS_NAMES ) }
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
disabled={ isEditorLoading }
|
||||||
|
className={ clsx( BUTTON_CLASS_NAMES, {
|
||||||
|
'is-selected': deviceType === 'Tablet',
|
||||||
|
} ) }
|
||||||
|
aria-label="Tablet View"
|
||||||
|
onClick={ () => {
|
||||||
|
switchDeviceType( 'Tablet' );
|
||||||
|
} }
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon={ tablet }
|
||||||
|
size={ 30 }
|
||||||
|
className={ clsx( ICON_CLASS_NAMES ) }
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
disabled={ isEditorLoading }
|
||||||
|
className={ clsx( BUTTON_CLASS_NAMES, {
|
||||||
|
'is-selected': deviceType === 'Mobile',
|
||||||
|
} ) }
|
||||||
|
aria-label="Mobile View"
|
||||||
|
onClick={ () => {
|
||||||
|
switchDeviceType( 'Mobile' );
|
||||||
|
} }
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon={ mobile }
|
||||||
|
size={ 30 }
|
||||||
|
className={ clsx( ICON_CLASS_NAMES ) }
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
disabled={ isEditorLoading }
|
||||||
|
className={ clsx( BUTTON_CLASS_NAMES ) }
|
||||||
|
aria-label={ isZoomedOut ? 'Zoom In View' : 'Zoom Out View' }
|
||||||
|
onClick={ () => {
|
||||||
|
setDeviceType( 'Desktop' );
|
||||||
|
toggleZoomOut();
|
||||||
|
} }
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon={ isZoomedOut ? zoomIn : zoomOut }
|
||||||
|
size={ 30 }
|
||||||
|
className={ clsx(
|
||||||
|
ICON_CLASS_NAMES,
|
||||||
|
`${ ICON_CLASS_NAMES }--zoom`
|
||||||
|
) }
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</NavigableMenu>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import React, { createContext, useState } from '@wordpress/element';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
export const ZoomOutContext = createContext< {
|
||||||
|
isZoomedOut: boolean;
|
||||||
|
toggleZoomOut: () => void;
|
||||||
|
} >( {
|
||||||
|
isZoomedOut: false,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
toggleZoomOut: () => {
|
||||||
|
// No op by default.
|
||||||
|
},
|
||||||
|
} );
|
||||||
|
|
||||||
|
export const ZoomOutContextProvider = ( {
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: ReactNode;
|
||||||
|
} ) => {
|
||||||
|
const [ isZoomedOut, setIsZoomedOut ] = useState< boolean >( false );
|
||||||
|
|
||||||
|
const toggleZoomOut = () => {
|
||||||
|
setIsZoomedOut( ! isZoomedOut );
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ZoomOutContext.Provider
|
||||||
|
value={ {
|
||||||
|
isZoomedOut,
|
||||||
|
toggleZoomOut,
|
||||||
|
} }
|
||||||
|
>
|
||||||
|
{ children }
|
||||||
|
</ZoomOutContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
|
@ -58,7 +58,7 @@ export const usePopoverHandler = () => {
|
||||||
hoveredBlockClientId: string | null;
|
hoveredBlockClientId: string | null;
|
||||||
} ) => {
|
} ) => {
|
||||||
const iframe = window.document.querySelector(
|
const iframe = window.document.querySelector(
|
||||||
'.woocommerce-customize-store-assembler > iframe[name="editor-canvas"]'
|
'.woocommerce-customize-store-assembler > .block-editor-iframe__container iframe[name="editor-canvas"]'
|
||||||
) as HTMLElement;
|
) as HTMLElement;
|
||||||
|
|
||||||
const target = event.target as HTMLElement;
|
const target = event.target as HTMLElement;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// Reference: https://github.com/WordPress/gutenberg/blob/f91b4fb4a12e41dd39c9594f24ea1a1a4e23dade/packages/block-editor/src/components/iframe/index.js#L1
|
// Reference: https://github.com/WordPress/gutenberg/blob/f91b4fb4a12e41dd39c9594f24ea1a1a4e23dade/packages/block-editor/src/components/iframe/index.js#L1
|
||||||
// We fork the code from the above link to reduce the unnecessary network requests and improve the performance.
|
// We fork the code from the above link to reduce the unnecessary network requests and improve the performance.
|
||||||
|
// Some of the code is not used in the project and is removed.
|
||||||
|
// We've also made some changes to the code to make it work with the Zoom Out feature.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
|
@ -11,6 +13,8 @@ import {
|
||||||
forwardRef,
|
forwardRef,
|
||||||
useMemo,
|
useMemo,
|
||||||
useEffect,
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useContext,
|
||||||
} from '@wordpress/element';
|
} from '@wordpress/element';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import {
|
import {
|
||||||
|
@ -21,37 +25,42 @@ import {
|
||||||
} from '@wordpress/compose';
|
} from '@wordpress/compose';
|
||||||
import { __experimentalStyleProvider as StyleProvider } from '@wordpress/components';
|
import { __experimentalStyleProvider as StyleProvider } from '@wordpress/components';
|
||||||
import { useSelect } from '@wordpress/data';
|
import { useSelect } from '@wordpress/data';
|
||||||
|
|
||||||
import { store as blockEditorStore } from '@wordpress/block-editor';
|
import { store as blockEditorStore } from '@wordpress/block-editor';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { ZoomOutContext } from './context/zoom-out-context';
|
||||||
|
|
||||||
function Iframe( {
|
function Iframe( {
|
||||||
contentRef,
|
contentRef,
|
||||||
children,
|
children,
|
||||||
tabIndex = 0,
|
tabIndex = 0,
|
||||||
scale = 1,
|
scale = 1,
|
||||||
frameSize = 0,
|
frameSize = 0,
|
||||||
expand = false,
|
|
||||||
readonly,
|
readonly,
|
||||||
forwardedRef: ref,
|
forwardedRef: ref,
|
||||||
loadStyles = true,
|
title = __( 'Editor canvas', 'woocommerce' ),
|
||||||
loadScripts = false,
|
canEnableZoomOutView = false,
|
||||||
...props
|
...props
|
||||||
} ) {
|
} ) {
|
||||||
const [ iframeDocument, setIframeDocument ] = useState();
|
|
||||||
|
|
||||||
const [ bodyClasses, setBodyClasses ] = useState( [] );
|
|
||||||
|
|
||||||
const { resolvedAssets } = useSelect( ( select ) => {
|
const { resolvedAssets } = useSelect( ( select ) => {
|
||||||
const settings = select( blockEditorStore ).getSettings();
|
const { getSettings } = select( blockEditorStore );
|
||||||
|
const settings = getSettings();
|
||||||
return {
|
return {
|
||||||
resolvedAssets: settings.__unstableResolvedAssets,
|
resolvedAssets: settings.__unstableResolvedAssets,
|
||||||
};
|
};
|
||||||
}, [] );
|
}, [] );
|
||||||
const { styles = '', scripts = '' } = resolvedAssets;
|
|
||||||
|
|
||||||
|
const { styles = '', scripts = '' } = resolvedAssets;
|
||||||
|
const [ iframeDocument, setIframeDocument ] = useState();
|
||||||
|
const prevContainerWidth = useRef( 0 );
|
||||||
|
const [ bodyClasses, setBodyClasses ] = useState( [] );
|
||||||
const [ contentResizeListener, { height: contentHeight } ] =
|
const [ contentResizeListener, { height: contentHeight } ] =
|
||||||
useResizeObserver();
|
useResizeObserver();
|
||||||
|
const [ containerResizeListener, { width: containerWidth } ] =
|
||||||
|
useResizeObserver();
|
||||||
|
|
||||||
const setRef = useRefEffect( ( node ) => {
|
const setRef = useRefEffect( ( node ) => {
|
||||||
node._load = () => {
|
node._load = () => {
|
||||||
setIframeDocument( node.contentDocument );
|
setIframeDocument( node.contentDocument );
|
||||||
|
@ -62,6 +71,9 @@ function Iframe( {
|
||||||
|
|
||||||
documentElement.classList.add( 'block-editor-iframe__html' );
|
documentElement.classList.add( 'block-editor-iframe__html' );
|
||||||
|
|
||||||
|
// Ideally ALL classes that are added through get_body_class should
|
||||||
|
// be added in the editor too, which we'll somehow have to get from
|
||||||
|
// the server in the future (which will run the PHP filters).
|
||||||
setBodyClasses(
|
setBodyClasses(
|
||||||
Array.from( ownerDocument?.body.classList ).filter(
|
Array.from( ownerDocument?.body.classList ).filter(
|
||||||
( name ) =>
|
( name ) =>
|
||||||
|
@ -70,6 +82,8 @@ function Iframe( {
|
||||||
name === 'wp-embed-responsive'
|
name === 'wp-embed-responsive'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
contentDocument.dir = ownerDocument.dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
node.addEventListener( 'load', onLoad );
|
node.addEventListener( 'load', onLoad );
|
||||||
|
@ -80,8 +94,53 @@ function Iframe( {
|
||||||
};
|
};
|
||||||
}, [] );
|
}, [] );
|
||||||
|
|
||||||
|
const [ iframeWindowInnerHeight, setIframeWindowInnerHeight ] = useState();
|
||||||
|
|
||||||
|
const iframeResizeRef = useRefEffect( ( node ) => {
|
||||||
|
const nodeWindow = node.ownerDocument.defaultView;
|
||||||
|
|
||||||
|
setIframeWindowInnerHeight( nodeWindow.innerHeight );
|
||||||
|
const onResize = () => {
|
||||||
|
setIframeWindowInnerHeight( nodeWindow.innerHeight );
|
||||||
|
};
|
||||||
|
nodeWindow.addEventListener( 'resize', onResize );
|
||||||
|
return () => {
|
||||||
|
nodeWindow.removeEventListener( 'resize', onResize );
|
||||||
|
};
|
||||||
|
}, [] );
|
||||||
|
|
||||||
|
const [ windowInnerWidth, setWindowInnerWidth ] = useState();
|
||||||
|
|
||||||
|
const windowResizeRef = useRefEffect( ( node ) => {
|
||||||
|
const nodeWindow = node.ownerDocument.defaultView;
|
||||||
|
|
||||||
|
setWindowInnerWidth( nodeWindow.innerWidth );
|
||||||
|
const onResize = () => {
|
||||||
|
setWindowInnerWidth( nodeWindow.innerWidth );
|
||||||
|
};
|
||||||
|
nodeWindow.addEventListener( 'resize', onResize );
|
||||||
|
return () => {
|
||||||
|
nodeWindow.removeEventListener( 'resize', onResize );
|
||||||
|
};
|
||||||
|
}, [] );
|
||||||
|
|
||||||
|
const isZoomedOut = scale !== 1 && canEnableZoomOutView;
|
||||||
|
|
||||||
|
useEffect( () => {
|
||||||
|
if ( ! isZoomedOut && ! prevContainerWidth.current ) {
|
||||||
|
prevContainerWidth.current = containerWidth;
|
||||||
|
}
|
||||||
|
}, [ containerWidth, isZoomedOut ] );
|
||||||
|
|
||||||
const disabledRef = useDisabled( { isDisabled: ! readonly } );
|
const disabledRef = useDisabled( { isDisabled: ! readonly } );
|
||||||
const bodyRef = useMergeRefs( [ contentRef, disabledRef ] );
|
const bodyRef = useMergeRefs( [
|
||||||
|
contentRef,
|
||||||
|
disabledRef,
|
||||||
|
// Avoid resize listeners when not needed, these will trigger
|
||||||
|
// unnecessary re-renders when animating the iframe width, or when
|
||||||
|
// expanding preview iframes.
|
||||||
|
isZoomedOut ? iframeResizeRef : null,
|
||||||
|
] );
|
||||||
|
|
||||||
// Correct doctype is required to enable rendering in standards
|
// Correct doctype is required to enable rendering in standards
|
||||||
// mode. Also preload the styles to avoid a flash of unstyled
|
// mode. Also preload the styles to avoid a flash of unstyled
|
||||||
|
@ -89,10 +148,23 @@ function Iframe( {
|
||||||
const html = `<!doctype html>
|
const html = `<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
<script>window.frameElement._load()</script>
|
<script>window.frameElement._load()</script>
|
||||||
<style>html{height:auto!important;min-height:100%;}body{margin:0}</style>
|
<style>
|
||||||
${ loadStyles ? styles : '' }
|
html{
|
||||||
${ loadScripts ? scripts : '' }
|
height: auto !important;
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
/* Lowest specificity to not override global styles */
|
||||||
|
:where(body) {
|
||||||
|
margin: 0;
|
||||||
|
/* Default background color in case zoom out mode background
|
||||||
|
colors the html element */
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
${ styles }
|
||||||
|
${ scripts }
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script>document.currentScript.parentElement.remove()</script>
|
<script>document.currentScript.parentElement.remove()</script>
|
||||||
|
@ -108,30 +180,86 @@ function Iframe( {
|
||||||
|
|
||||||
useEffect( () => cleanup, [ cleanup ] );
|
useEffect( () => cleanup, [ cleanup ] );
|
||||||
|
|
||||||
// We need to counter the margin created by scaling the iframe. If the scale
|
useEffect( () => {
|
||||||
// is e.g. 0.45, then the top + bottom margin is 0.55 (1 - scale). Just the
|
if ( ! canEnableZoomOutView || ! iframeDocument || ! isZoomedOut ) {
|
||||||
// top or bottom margin is 0.55 / 2 ((1 - scale) / 2).
|
return;
|
||||||
const marginFromScaling = ( contentHeight * ( 1 - scale ) ) / 2;
|
}
|
||||||
|
|
||||||
return (
|
const maxWidth = 800;
|
||||||
|
|
||||||
|
iframeDocument.documentElement.classList.add( 'is-zoomed-out' );
|
||||||
|
|
||||||
|
iframeDocument.documentElement.style.setProperty(
|
||||||
|
'--wp-block-editor-iframe-zoom-out-scale',
|
||||||
|
scale === 'default'
|
||||||
|
? Math.min( containerWidth, maxWidth ) /
|
||||||
|
prevContainerWidth.current
|
||||||
|
: scale
|
||||||
|
);
|
||||||
|
iframeDocument.documentElement.style.setProperty(
|
||||||
|
'--wp-block-editor-iframe-zoom-out-frame-size',
|
||||||
|
typeof frameSize === 'number' ? `${ frameSize }px` : frameSize
|
||||||
|
);
|
||||||
|
iframeDocument.documentElement.style.setProperty(
|
||||||
|
'--wp-block-editor-iframe-zoom-out-content-height',
|
||||||
|
`${ contentHeight }px`
|
||||||
|
);
|
||||||
|
iframeDocument.documentElement.style.setProperty(
|
||||||
|
'--wp-block-editor-iframe-zoom-out-inner-height',
|
||||||
|
`${ iframeWindowInnerHeight }px`
|
||||||
|
);
|
||||||
|
iframeDocument.documentElement.style.setProperty(
|
||||||
|
'--wp-block-editor-iframe-zoom-out-container-width',
|
||||||
|
`${ containerWidth }px`
|
||||||
|
);
|
||||||
|
iframeDocument.documentElement.style.setProperty(
|
||||||
|
'--wp-block-editor-iframe-zoom-out-prev-container-width',
|
||||||
|
`${ prevContainerWidth.current }px`
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
iframeDocument.documentElement.classList.remove( 'is-zoomed-out' );
|
||||||
|
|
||||||
|
iframeDocument.documentElement.style.removeProperty(
|
||||||
|
'--wp-block-editor-iframe-zoom-out-scale'
|
||||||
|
);
|
||||||
|
iframeDocument.documentElement.style.removeProperty(
|
||||||
|
'--wp-block-editor-iframe-zoom-out-frame-size'
|
||||||
|
);
|
||||||
|
iframeDocument.documentElement.style.removeProperty(
|
||||||
|
'--wp-block-editor-iframe-zoom-out-content-height'
|
||||||
|
);
|
||||||
|
iframeDocument.documentElement.style.removeProperty(
|
||||||
|
'--wp-block-editor-iframe-zoom-out-inner-height'
|
||||||
|
);
|
||||||
|
iframeDocument.documentElement.style.removeProperty(
|
||||||
|
'--wp-block-editor-iframe-zoom-out-container-width'
|
||||||
|
);
|
||||||
|
iframeDocument.documentElement.style.removeProperty(
|
||||||
|
'--wp-block-editor-iframe-zoom-out-prev-container-width'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}, [
|
||||||
|
scale,
|
||||||
|
frameSize,
|
||||||
|
iframeDocument,
|
||||||
|
iframeWindowInnerHeight,
|
||||||
|
contentHeight,
|
||||||
|
containerWidth,
|
||||||
|
windowInnerWidth,
|
||||||
|
isZoomedOut,
|
||||||
|
canEnableZoomOutView,
|
||||||
|
] );
|
||||||
|
|
||||||
|
const iframe = (
|
||||||
<>
|
<>
|
||||||
|
{ /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */ }
|
||||||
<iframe
|
<iframe
|
||||||
{ ...props }
|
{ ...props }
|
||||||
style={ {
|
style={ {
|
||||||
|
border: 0,
|
||||||
...props.style,
|
...props.style,
|
||||||
height: expand ? contentHeight : props.style?.height,
|
height: props.style?.height,
|
||||||
marginTop:
|
|
||||||
scale !== 1
|
|
||||||
? -marginFromScaling + frameSize
|
|
||||||
: props.style?.marginTop,
|
|
||||||
marginBottom:
|
|
||||||
scale !== 1
|
|
||||||
? -marginFromScaling + frameSize
|
|
||||||
: props.style?.marginBottom,
|
|
||||||
transform:
|
|
||||||
scale !== 1
|
|
||||||
? `scale( ${ scale } )`
|
|
||||||
: props.style?.transform,
|
|
||||||
transition: 'all .3s',
|
transition: 'all .3s',
|
||||||
} }
|
} }
|
||||||
ref={ useMergeRefs( [ ref, setRef ] ) }
|
ref={ useMergeRefs( [ ref, setRef ] ) }
|
||||||
|
@ -140,7 +268,7 @@ function Iframe( {
|
||||||
// mode. Also preload the styles to avoid a flash of unstyled
|
// mode. Also preload the styles to avoid a flash of unstyled
|
||||||
// content.
|
// content.
|
||||||
src={ src }
|
src={ src }
|
||||||
title={ __( 'Editor canvas', 'woocommerce' ) }
|
title={ title }
|
||||||
name="editor-canvas"
|
name="editor-canvas"
|
||||||
>
|
>
|
||||||
{ iframeDocument &&
|
{ iframeDocument &&
|
||||||
|
@ -163,6 +291,26 @@ function Iframe( {
|
||||||
</iframe>
|
</iframe>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="block-editor-iframe__container" ref={ windowResizeRef }>
|
||||||
|
{ containerResizeListener }
|
||||||
|
<div
|
||||||
|
className={ clsx(
|
||||||
|
'block-editor-iframe__scale-container',
|
||||||
|
isZoomedOut && 'is-zoomed-out'
|
||||||
|
) }
|
||||||
|
style={ {
|
||||||
|
'--wp-block-editor-iframe-zoom-out-container-width':
|
||||||
|
isZoomedOut && `${ containerWidth }px`,
|
||||||
|
'--wp-block-editor-iframe-zoom-out-prev-container-width':
|
||||||
|
isZoomedOut && `${ prevContainerWidth.current }px`,
|
||||||
|
} }
|
||||||
|
>
|
||||||
|
{ iframe }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function IframeIfReady( props, ref ) {
|
function IframeIfReady( props, ref ) {
|
||||||
|
@ -172,6 +320,15 @@ function IframeIfReady( props, ref ) {
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { isZoomedOut } = useContext( ZoomOutContext );
|
||||||
|
|
||||||
|
const zoomOutProps = isZoomedOut
|
||||||
|
? {
|
||||||
|
scale: 'default',
|
||||||
|
frameSize: '48px',
|
||||||
|
}
|
||||||
|
: {};
|
||||||
|
|
||||||
// We shouldn't render the iframe until the editor settings are initialised.
|
// We shouldn't render the iframe until the editor settings are initialised.
|
||||||
// The initial settings are needed to get the styles for the srcDoc, which
|
// The initial settings are needed to get the styles for the srcDoc, which
|
||||||
// cannot be changed after the iframe is mounted. srcDoc is used to to set
|
// cannot be changed after the iframe is mounted. srcDoc is used to to set
|
||||||
|
@ -181,7 +338,12 @@ function IframeIfReady( props, ref ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Iframe { ...props } forwardedRef={ ref } />;
|
const iframeProps = {
|
||||||
|
...props,
|
||||||
|
...zoomOutProps,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Iframe { ...iframeProps } forwardedRef={ ref } />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default forwardRef( IframeIfReady );
|
export default forwardRef( IframeIfReady );
|
||||||
|
|
|
@ -51,6 +51,8 @@ import { getNewPath } from '@woocommerce/navigation';
|
||||||
import useBodyClass from '../hooks/use-body-class';
|
import useBodyClass from '../hooks/use-body-class';
|
||||||
import { OptInSubscribe } from './opt-in/opt-in';
|
import { OptInSubscribe } from './opt-in/opt-in';
|
||||||
import { OptInContextProvider } from './opt-in/context';
|
import { OptInContextProvider } from './opt-in/context';
|
||||||
|
import { ZoomOutContextProvider } from './context/zoom-out-context';
|
||||||
|
|
||||||
import './tracking';
|
import './tracking';
|
||||||
|
|
||||||
const { RouterProvider } = unlock( routerPrivateApis );
|
const { RouterProvider } = unlock( routerPrivateApis );
|
||||||
|
@ -150,7 +152,15 @@ const initializeAssembleHub = () => {
|
||||||
export const AssemblerHub: CustomizeStoreComponent = ( props ) => {
|
export const AssemblerHub: CustomizeStoreComponent = ( props ) => {
|
||||||
const isInitializedRef = useRef( false );
|
const isInitializedRef = useRef( false );
|
||||||
|
|
||||||
|
// @ts-expect-error temp fix
|
||||||
|
const isAiFlow = window.parent?.window.cys_aiFlow ? true : false;
|
||||||
|
|
||||||
useBodyClass( 'woocommerce-assembler' );
|
useBodyClass( 'woocommerce-assembler' );
|
||||||
|
useBodyClass(
|
||||||
|
isAiFlow
|
||||||
|
? 'woocommerce-assembler--with-ai'
|
||||||
|
: 'woocommerce-assembler--without-ai'
|
||||||
|
);
|
||||||
|
|
||||||
if ( ! isInitializedRef.current ) {
|
if ( ! isInitializedRef.current ) {
|
||||||
initializeAssembleHub();
|
initializeAssembleHub();
|
||||||
|
@ -184,7 +194,9 @@ export const AssemblerHub: CustomizeStoreComponent = ( props ) => {
|
||||||
<OptInContextProvider>
|
<OptInContextProvider>
|
||||||
<GlobalStylesProvider>
|
<GlobalStylesProvider>
|
||||||
<RouterProvider>
|
<RouterProvider>
|
||||||
<Layout />
|
<ZoomOutContextProvider>
|
||||||
|
<Layout />
|
||||||
|
</ZoomOutContextProvider>
|
||||||
</RouterProvider>
|
</RouterProvider>
|
||||||
<OptInSubscribe />
|
<OptInSubscribe />
|
||||||
</GlobalStylesProvider>
|
</GlobalStylesProvider>
|
||||||
|
|
|
@ -34,6 +34,9 @@ import { NavigableRegion } from '@wordpress/interface';
|
||||||
import { EntityProvider } from '@wordpress/core-data';
|
import { EntityProvider } from '@wordpress/core-data';
|
||||||
// @ts-ignore No types for this exist yet.
|
// @ts-ignore No types for this exist yet.
|
||||||
import useEditedEntityRecord from '@wordpress/edit-site/build-module/components/use-edited-entity-record';
|
import useEditedEntityRecord from '@wordpress/edit-site/build-module/components/use-edited-entity-record';
|
||||||
|
// @ts-ignore No types for this exist yet.
|
||||||
|
import { store as editorStore } from '@wordpress/editor';
|
||||||
|
import { useSelect } from '@wordpress/data';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -52,12 +55,14 @@ import { useQuery } from '@woocommerce/navigation';
|
||||||
import { FlowType } from '../types';
|
import { FlowType } from '../types';
|
||||||
import { isOfflineAIFlow } from '../guards';
|
import { isOfflineAIFlow } from '../guards';
|
||||||
import { isWooExpress } from '~/utils/is-woo-express';
|
import { isWooExpress } from '~/utils/is-woo-express';
|
||||||
|
import { isFullComposabilityFeatureAndAPIAvailable } from './utils/is-full-composability-enabled';
|
||||||
import { trackEvent } from '../tracking';
|
import { trackEvent } from '../tracking';
|
||||||
import { SidebarNavigationExtraScreen } from './sidebar/navigation-extra-screen/sidebar-navigation-extra-screen';
|
import { SidebarNavigationExtraScreen } from './sidebar/navigation-extra-screen/sidebar-navigation-extra-screen';
|
||||||
|
import { DeviceToolbar } from './components/device-toolbar';
|
||||||
|
|
||||||
const { useGlobalStyle } = unlock( blockEditorPrivateApis );
|
const { useGlobalStyle } = unlock( blockEditorPrivateApis );
|
||||||
|
|
||||||
const ANIMATION_DURATION = 0.5;
|
const ANIMATION_DURATION = 0.3;
|
||||||
|
|
||||||
export const Layout = () => {
|
export const Layout = () => {
|
||||||
const [ logoBlockIds, setLogoBlockIds ] = useState< Array< string > >( [] );
|
const [ logoBlockIds, setLogoBlockIds ] = useState< Array< string > >( [] );
|
||||||
|
@ -72,6 +77,15 @@ export const Layout = () => {
|
||||||
isOfflineAIFlow( context.flowType ) && customizing !== 'true'
|
isOfflineAIFlow( context.flowType ) && customizing !== 'true'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { deviceType } = useSelect( ( select ) => {
|
||||||
|
// @ts-ignore No types for this exist yet.
|
||||||
|
const { getDeviceType } = select( editorStore );
|
||||||
|
|
||||||
|
return {
|
||||||
|
deviceType: getDeviceType(),
|
||||||
|
};
|
||||||
|
} );
|
||||||
|
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
setShowAiOfflineModal(
|
setShowAiOfflineModal(
|
||||||
isOfflineAIFlow( context.flowType ) && customizing !== 'true'
|
isOfflineAIFlow( context.flowType ) && customizing !== 'true'
|
||||||
|
@ -203,6 +217,18 @@ export const Layout = () => {
|
||||||
|
|
||||||
{ ! isMobileViewport && (
|
{ ! isMobileViewport && (
|
||||||
<div className="edit-site-layout__canvas-container">
|
<div className="edit-site-layout__canvas-container">
|
||||||
|
{ isFullComposabilityFeatureAndAPIAvailable() && (
|
||||||
|
<motion.div
|
||||||
|
initial={ false }
|
||||||
|
layout="position"
|
||||||
|
>
|
||||||
|
<DeviceToolbar
|
||||||
|
isEditorLoading={
|
||||||
|
isEditorLoading
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
) }
|
||||||
{ canvasResizer }
|
{ canvasResizer }
|
||||||
{ !! canvasSize.width && (
|
{ !! canvasSize.width && (
|
||||||
<motion.div
|
<motion.div
|
||||||
|
@ -234,11 +260,17 @@ export const Layout = () => {
|
||||||
setIsOversized={
|
setIsOversized={
|
||||||
setIsResizableFrameOversized
|
setIsResizableFrameOversized
|
||||||
}
|
}
|
||||||
|
isResizingHandleEnabled={
|
||||||
|
! isFullComposabilityFeatureAndAPIAvailable()
|
||||||
|
}
|
||||||
innerContentStyle={ {
|
innerContentStyle={ {
|
||||||
background:
|
background:
|
||||||
gradientValue ??
|
gradientValue ??
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
} }
|
} }
|
||||||
|
deviceType={
|
||||||
|
deviceType
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{ editor }
|
{ editor }
|
||||||
</ResizableFrame>
|
</ResizableFrame>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useState, useRef, createContext } from '@wordpress/element';
|
import { useState, useRef, createContext, useEffect } from '@wordpress/element';
|
||||||
import {
|
import {
|
||||||
ResizableBox,
|
ResizableBox,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
|
@ -14,6 +14,7 @@ import {
|
||||||
} from '@wordpress/components';
|
} from '@wordpress/components';
|
||||||
import { useInstanceId } from '@wordpress/compose';
|
import { useInstanceId } from '@wordpress/compose';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { __experimentalUseResizeCanvas as useResizeCanvas } from '@wordpress/block-editor';
|
||||||
|
|
||||||
// Removes the inline styles in the drag handles.
|
// Removes the inline styles in the drag handles.
|
||||||
const HANDLE_STYLES_OVERRIDE = {
|
const HANDLE_STYLES_OVERRIDE = {
|
||||||
|
@ -78,6 +79,9 @@ function ResizableFrame( {
|
||||||
defaultSize,
|
defaultSize,
|
||||||
innerContentStyle,
|
innerContentStyle,
|
||||||
isHandleVisibleByDefault = false,
|
isHandleVisibleByDefault = false,
|
||||||
|
isResizingHandleEnabled = true,
|
||||||
|
/** Passing as a prop because the LYS feature does not have access to the editor data store, but CYS feature does. */
|
||||||
|
deviceType = null,
|
||||||
} ) {
|
} ) {
|
||||||
const [ frameSize, setFrameSize ] = useState( INITIAL_FRAME_SIZE );
|
const [ frameSize, setFrameSize ] = useState( INITIAL_FRAME_SIZE );
|
||||||
// The width of the resizable frame when a new resize gesture starts.
|
// The width of the resizable frame when a new resize gesture starts.
|
||||||
|
@ -94,6 +98,27 @@ function ResizableFrame( {
|
||||||
);
|
);
|
||||||
const defaultAspectRatio = defaultSize.width / defaultSize.height;
|
const defaultAspectRatio = defaultSize.width / defaultSize.height;
|
||||||
|
|
||||||
|
const deviceStyles = useResizeCanvas( deviceType );
|
||||||
|
|
||||||
|
useEffect( () => {
|
||||||
|
if ( ! deviceType ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( deviceType === 'Desktop' ) {
|
||||||
|
setFrameSize( INITIAL_FRAME_SIZE );
|
||||||
|
} else {
|
||||||
|
const { width, height, marginLeft, marginRight } = deviceStyles;
|
||||||
|
setIsOversized( width > defaultSize.width );
|
||||||
|
setFrameSize( {
|
||||||
|
width: isOversized ? '100%' : width,
|
||||||
|
height: isOversized ? '100%' : height,
|
||||||
|
marginLeft,
|
||||||
|
marginRight,
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}, [ deviceType ] );
|
||||||
|
|
||||||
const handleResizeStart = ( _event, _direction, ref ) => {
|
const handleResizeStart = ( _event, _direction, ref ) => {
|
||||||
// Remember the starting width so we don't have to get `ref.offsetWidth` on
|
// Remember the starting width so we don't have to get `ref.offsetWidth` on
|
||||||
// every resize event thereafter, which will cause layout thrashing.
|
// every resize event thereafter, which will cause layout thrashing.
|
||||||
|
@ -253,7 +278,7 @@ function ResizableFrame( {
|
||||||
right: false,
|
right: false,
|
||||||
bottom: false,
|
bottom: false,
|
||||||
// Resizing will be disabled until the editor content is loaded.
|
// Resizing will be disabled until the editor content is loaded.
|
||||||
left: isReady,
|
left: isReady && isResizingHandleEnabled,
|
||||||
topRight: false,
|
topRight: false,
|
||||||
bottomRight: false,
|
bottomRight: false,
|
||||||
bottomLeft: false,
|
bottomLeft: false,
|
||||||
|
|
|
@ -162,7 +162,7 @@ export const SidebarPatternScreen = ( { category }: { category: string } ) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const iframe = window.document.querySelector(
|
const iframe = window.document.querySelector(
|
||||||
'.woocommerce-customize-store-assembler > iframe[name="editor-canvas"]'
|
'.woocommerce-customize-store-assembler > .block-editor-iframe__container iframe[name="editor-canvas"]'
|
||||||
) as HTMLIFrameElement;
|
) as HTMLIFrameElement;
|
||||||
|
|
||||||
const blockList = iframe?.contentWindow?.document.body.querySelector(
|
const blockList = iframe?.contentWindow?.document.body.querySelector(
|
||||||
|
|
|
@ -523,9 +523,19 @@ body.woocommerce-assembler {
|
||||||
/* Preview Canvas */
|
/* Preview Canvas */
|
||||||
.edit-site-layout__canvas {
|
.edit-site-layout__canvas {
|
||||||
bottom: 16px;
|
bottom: 16px;
|
||||||
top: 16px;
|
top: 75px;
|
||||||
left: 6px; // the default styles for this undersizes the width by 24px so we want to center this
|
left: 6px; // the default styles for this undersizes the width by 24px so we want to center this
|
||||||
padding: 0 4px 0 16px;
|
padding: 0;
|
||||||
|
background: #ddd;
|
||||||
|
border-radius: 0 0 12px 12px;
|
||||||
|
|
||||||
|
// Design with AI: This is necessary because the design with AI flow does not have the device toolbar.
|
||||||
|
.woocommerce-assembler--with-ai & {
|
||||||
|
top: 16px;
|
||||||
|
padding: 0 4px 0 16px;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-site-resizable-frame__handle {
|
.edit-site-resizable-frame__handle {
|
||||||
|
@ -537,13 +547,22 @@ body.woocommerce-assembler {
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
/* new frame */
|
/* new frame */
|
||||||
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.05);
|
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.05);
|
||||||
|
transition: all 0.3s ease 0s;
|
||||||
|
|
||||||
|
// Design with AI: This is necessary because the design with AI flow does not have the device toolbar.
|
||||||
|
.woocommerce-assembler--with-ai & {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-customize-store__block-editor,
|
.woocommerce-customize-store__block-editor,
|
||||||
.edit-site-layout:not(.is-full-canvas)
|
.edit-site-layout:not(.is-full-canvas)
|
||||||
.edit-site-layout__canvas > div
|
.edit-site-layout__canvas > div
|
||||||
.interface-interface-skeleton__content {
|
.interface-interface-skeleton__content {
|
||||||
border-radius: 12px;
|
// Design with AI: This is necessary because the design with AI flow does not have the device toolbar.
|
||||||
|
.woocommerce-customize-store__step-designWithAi & {
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.woocommerce-customize-store__block-editor,
|
.woocommerce-customize-store__block-editor,
|
||||||
.woocommerce-block-preview-container,
|
.woocommerce-block-preview-container,
|
||||||
|
@ -560,7 +579,59 @@ body.woocommerce-assembler {
|
||||||
|
|
||||||
.customize-your-store-edit-site-resizable-frame__inner-content {
|
.customize-your-store-edit-site-resizable-frame__inner-content {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 12px;
|
border-radius: 0 0 12px 12px;
|
||||||
|
|
||||||
|
// Design with AI: This is necessary because the design with AI flow does not have the device toolbar.
|
||||||
|
.woocommerce-customize-store__step-designWithAi & {
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-customize-store__device-toolbar {
|
||||||
|
padding: 10px 8px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.05);
|
||||||
|
position: relative;
|
||||||
|
top: 16px;
|
||||||
|
z-index: 10;
|
||||||
|
border-radius: 12px 12px 0 0;
|
||||||
|
width: calc(100% - 16px);
|
||||||
|
left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-customize-store__device-button:not([disabled]):focus {
|
||||||
|
box-shadow: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-customize-store__device-button:not([disabled]) {
|
||||||
|
&:hover,
|
||||||
|
&.is-selected {
|
||||||
|
box-shadow: none;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
.woocommerce-customize-store__device-icon {
|
||||||
|
fill: initial;
|
||||||
|
|
||||||
|
&--zoom {
|
||||||
|
stroke: rgb(0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-customize-store__device-icon {
|
||||||
|
margin-right: 10px;
|
||||||
|
fill: #757575;
|
||||||
|
|
||||||
|
&--zoom {
|
||||||
|
stroke: #757575;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ import Delete from './delete';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
import { useIsNoBlocksPlaceholderPresent } from '../hooks/block-placeholder/use-is-no-blocks-placeholder-present';
|
import { useIsNoBlocksPlaceholderPresent } from '../hooks/block-placeholder/use-is-no-blocks-placeholder-present';
|
||||||
import { SelectedBlockContext } from '../context/selected-block-ref-context';
|
import { SelectedBlockContext } from '../context/selected-block-ref-context';
|
||||||
|
import { isFullComposabilityFeatureAndAPIAvailable } from '../utils/is-full-composability-enabled';
|
||||||
|
|
||||||
const isHomepageUrl = ( path: string ) => {
|
const isHomepageUrl = ( path: string ) => {
|
||||||
return path.includes( '/customize-store/assembler-hub/homepage' );
|
return path.includes( '/customize-store/assembler-hub/homepage' );
|
||||||
|
@ -143,6 +144,10 @@ export const Toolbar = () => {
|
||||||
|
|
||||||
const blockPopoverRef = useRef< HTMLDivElement | null >( null );
|
const blockPopoverRef = useRef< HTMLDivElement | null >( null );
|
||||||
|
|
||||||
|
// Note: This feature is only available when the full composability feature flag is enabled.
|
||||||
|
const isEligibleForZoomOutFeature =
|
||||||
|
isFullComposabilityFeatureAndAPIAvailable();
|
||||||
|
|
||||||
const popoverAnchor = useMemo( () => {
|
const popoverAnchor = useMemo( () => {
|
||||||
if ( ! selectedBlockRef || ! selectedBlockClientId ) {
|
if ( ! selectedBlockRef || ! selectedBlockClientId ) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -153,22 +158,39 @@ export const Toolbar = () => {
|
||||||
const { top, width, height } =
|
const { top, width, height } =
|
||||||
selectedBlockRef.getBoundingClientRect();
|
selectedBlockRef.getBoundingClientRect();
|
||||||
|
|
||||||
const rect = window.document
|
const iframe = window.document.querySelector(
|
||||||
.querySelector(
|
'.woocommerce-customize-store-assembler > .block-editor-iframe__container iframe[name="editor-canvas"]'
|
||||||
'.woocommerce-customize-store-assembler > iframe[name="editor-canvas"]'
|
);
|
||||||
)
|
const iframeHtmlElement =
|
||||||
?.getBoundingClientRect();
|
// @ts-expect-error missing type
|
||||||
|
iframe?.contentDocument?.documentElement;
|
||||||
|
const iframeRect = iframe?.getBoundingClientRect();
|
||||||
|
const iframeHtmlElementRect =
|
||||||
|
iframeHtmlElement?.getBoundingClientRect();
|
||||||
|
|
||||||
if ( ! rect ) {
|
const isZoomedOut =
|
||||||
|
isEligibleForZoomOutFeature &&
|
||||||
|
iframeHtmlElement?.classList.contains( 'is-zoomed-out' );
|
||||||
|
|
||||||
|
if ( ! iframeRect ) {
|
||||||
return new window.DOMRect( 0, 0, 0, 0 );
|
return new window.DOMRect( 0, 0, 0, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
return new window.DOMRect(
|
// Here we need to account for when the iframe is zoomed out as the width changes.
|
||||||
rect?.left + 10,
|
const rectLeft =
|
||||||
Math.max( top + 70 + rect.top, 100 ),
|
isZoomedOut && iframeHtmlElementRect
|
||||||
width,
|
? iframeRect?.left + iframeHtmlElementRect.left + 60
|
||||||
height
|
: iframeRect?.left + 10;
|
||||||
);
|
|
||||||
|
// Here we need to account for when the zoom out feature is eligible because a toolbar is added to the top of the iframe.
|
||||||
|
const rectTop = isEligibleForZoomOutFeature
|
||||||
|
? Math.max( top + 70 + iframeRect.top, iframeRect.top + 60 )
|
||||||
|
: Math.max(
|
||||||
|
top + 70 + iframeRect.top,
|
||||||
|
iframeRect.top + 60
|
||||||
|
);
|
||||||
|
|
||||||
|
return new window.DOMRect( rectLeft, rectTop, width, height );
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}, [ selectedBlockRef, selectedBlockClientId ] );
|
}, [ selectedBlockRef, selectedBlockClientId ] );
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: add
|
||||||
|
|
||||||
|
Adds a Zoom Out feature to the Customize Your Store experience
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: patch
|
||||||
|
Type: add
|
||||||
|
|
||||||
|
E2E test coverage for CYS device toolbar
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: patch
|
||||||
|
Type: fix
|
||||||
|
|
||||||
|
Fix animation of devicetoolbar
|
|
@ -374,4 +374,38 @@ test.describe( 'Assembler -> Full composability', { tag: '@gutenberg' }, () => {
|
||||||
).toHaveCount( 10 );
|
).toHaveCount( 10 );
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test( 'Clicking buttons in resize and zoom toolbar changes the frame size', async ( {
|
||||||
|
pageObject,
|
||||||
|
baseURL,
|
||||||
|
} ) => {
|
||||||
|
await prepareAssembler( pageObject, baseURL );
|
||||||
|
const assembler = await pageObject.getAssembler();
|
||||||
|
const editor = await pageObject.getEditor();
|
||||||
|
|
||||||
|
const toolbar = assembler.locator( '[aria-label="Resize Options"]' );
|
||||||
|
const resizeContainer = assembler.locator(
|
||||||
|
'.components-resizable-box__container'
|
||||||
|
);
|
||||||
|
const tabletBtn = assembler.locator( '[aria-label="Tablet View"]' );
|
||||||
|
const mobileBtn = assembler.locator( '[aria-label="Mobile View"]' );
|
||||||
|
|
||||||
|
await mobileBtn.click();
|
||||||
|
const mobileWidth = await resizeContainer.evaluate( ( element ) =>
|
||||||
|
window.getComputedStyle( element ).getPropertyValue( 'width' )
|
||||||
|
);
|
||||||
|
|
||||||
|
await tabletBtn.click();
|
||||||
|
const tabletWidth = await resizeContainer.evaluate( ( element ) =>
|
||||||
|
window.getComputedStyle( element ).getPropertyValue( 'width' )
|
||||||
|
);
|
||||||
|
|
||||||
|
await assembler.locator( '[aria-label="Zoom Out View"]' ).click();
|
||||||
|
|
||||||
|
await expect( editor.locator( '.is-zoomed-out' ) ).toBeVisible();
|
||||||
|
await expect( parseFloat( tabletWidth ) ).toBeGreaterThan(
|
||||||
|
parseFloat( mobileWidth )
|
||||||
|
);
|
||||||
|
await expect( toolbar ).toBeVisible();
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
Loading…
Reference in New Issue