import { usePortal } from '@agent/hooks/usePortal'; import { useGlobalStore } from '@agent/state/global'; import { createPortal, useEffect, useRef } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { close, Icon } from '@wordpress/icons'; import { motion } from 'framer-motion'; import { OptionsPopover } from '../OptionsPopover'; const SIDEBAR_WIDTH = 384; // 96 * 4 (w-96) const FRAME_WIDTH = 8; // border-8 const ANIMATE_TIME = 300; export const SidebarLayout = ({ children }) => { const mountNode = usePortal('extendify-agent-sidebar-mount'); const frameNode = usePortal('extendify-agent-border-frame-mount'); const { open, setOpen } = useGlobalStore(); useLayoutShift(open); const closeAgent = () => { setOpen(false); window.dispatchEvent(new CustomEvent('extendify-agent:closed-button')); }; useEffect(() => { if (open) return; if (!mountNode?.contains(document.activeElement)) return; document.activeElement?.blur(); }, [open]); if (!mountNode) return null; // A border that sits around the entire browser to look like the sidebar is inside it const frameAnim = { open: { top: FRAME_WIDTH, right: FRAME_WIDTH, bottom: FRAME_WIDTH, left: SIDEBAR_WIDTH, boxShadow: '#e0e0e0 0px 0px 0px 9999px', borderRadius: '1rem', }, closed: { inset: 0, boxShadow: '0 0 0 0 #fff', borderRadius: 0, }, }; const frameBar = frameNode ? createPortal(
, frameNode, ) : null; const sidebar = mountNode ? createPortal(
{window.extSharedData.partnerName}
{open ? children : null}
, mountNode, ) : null; return ( <> {frameBar} {sidebar} ); }; export const useLayoutShift = (open) => { const ease = 'ease-in-out'; const t = (props) => props.map((p) => `${p} ${ANIMATE_TIME}ms ${ease}`).join(', '); const firstRun = useRef(true); useEffect(() => { const siteBlocks = document.querySelector('.wp-site-blocks'); const wpadminbar = document.querySelector('#wpadminbar'); const stickyHeader = document.querySelector( 'header.is-position-sticky, header.wp-block-template-part:has(.ext-header-sticky)', ); const applyScaling = () => { if (!siteBlocks) return; if (open) { const viewportWidth = window.innerWidth; const scale = (viewportWidth - SIDEBAR_WIDTH) / viewportWidth; const scaledHeight = window.innerHeight / scale; Object.assign(siteBlocks.style, { transformOrigin: 'top left', transform: `translateX(${SIDEBAR_WIDTH}px) translateY(40px) scale(${scale})`, height: `${scaledHeight}px`, overflowY: 'auto', scrollBehavior: 'smooth', }); if (stickyHeader) { stickyHeader.style.setProperty( '--wp-admin--admin-bar--position-offset', '0px', ); } document.body.style.overflow = 'hidden'; document.body.style.position = 'fixed'; document.body.style.top = '0'; document.body.style.left = '0'; document.body.style.width = '100vw'; window.scrollTo(0, 0); } else { Object.assign(siteBlocks.style, { transformOrigin: 'top left', transform: 'translateX(0) translateY(0) scale(1)', height: '', overflowY: '', maxWidth: '100vw', scrollBehavior: '', }); if (stickyHeader) { stickyHeader.style.removeProperty( '--wp-admin--admin-bar--position-offset', '32px', ); } document.body.style.overflow = ''; document.body.style.position = ''; document.body.style.top = ''; document.body.style.left = ''; document.body.style.width = ''; } }; if (!firstRun.current) { if (siteBlocks) { siteBlocks.style.transition = t(['transform']); } if (wpadminbar) { wpadminbar.style.transition = t([ 'margin-left', 'margin-top', 'margin-right', 'border-radius', 'max-width', ]); } } else { firstRun.current = false; } const raf = requestAnimationFrame(() => { const fw = open ? `${FRAME_WIDTH}px` : '0px'; const ml = open ? `${SIDEBAR_WIDTH}px` : '0px'; applyScaling(); if (wpadminbar) { Object.assign(wpadminbar.style, { marginTop: fw, marginRight: fw, marginBottom: '0px', marginLeft: ml, borderRadius: open ? '8px 8px 0 0' : '0', maxWidth: open ? `calc(100% - ${SIDEBAR_WIDTH + FRAME_WIDTH}px)` : '100%', }); } }); window.addEventListener('resize', applyScaling); return () => { cancelAnimationFrame(raf); window.removeEventListener('resize', applyScaling); document.body.style.overflowX = ''; if (siteBlocks) { Object.assign(siteBlocks.style, { transition: '', transform: '', transformOrigin: '', height: '', overflowY: '', maxWidth: '', }); } if (stickyHeader) { stickyHeader.style.removeProperty( '--wp-admin--admin-bar--position-offset', ); } if (wpadminbar) { Object.assign(wpadminbar.style, { transition: '', marginTop: '', marginRight: '', marginBottom: '', marginLeft: '', borderRadius: '', maxWidth: '', }); } }; }, [open]); };