| @@ -1,9 +1,9 @@ | ||
| 1 | 1 | import { |
| 2 | 2 | CANVAS_PANE_WIDTH, |
| 3 | 3 | CanvasPane, |
| 4 | 4 | DOT_GRID, |
| 5 | - useCanvasOpen, | |
| 5 | + useCanvasSeat, | |
| 6 | 6 | } from '@agent/components/Canvas'; |
| 7 | 7 | import { usePortal } from '@agent/hooks/usePortal'; |
| 8 | 8 | import { DESKTOP_MIN_WIDTH, useGlobalStore } from '@agent/state/global'; |
| 9 | 9 | import { createPortal, useEffect, useRef } from '@wordpress/element'; |
| @@ -20,9 +20,10 @@ | ||
| 20 | 20 | export const SidebarLayout = ({ children }) => { |
| 21 | 21 | const mountNode = usePortal('extendify-agent-sidebar-mount'); |
| 22 | 22 | const frameNode = usePortal('extendify-agent-border-frame-mount'); |
| 23 | 23 | const { open, setOpen } = useGlobalStore(); |
| 24 | - const canvasOpen = useCanvasOpen(); | |
| 24 | + const seat = useCanvasSeat(); | |
| 25 | + const beside = seat === 'beside'; | |
| 25 | 26 | useLayoutShift(open); |
| 26 | 27 | |
| 27 | 28 | const closeAgent = () => { |
| 28 | 29 | setOpen(false); |
| @@ -55,11 +56,12 @@ | ||
| 55 | 56 | borderRadius: 0, |
| 56 | 57 | }, |
| 57 | 58 | }; |
| 58 | 59 | |
| 60 | + // Unclipped, the 9999px shadow covers the notification bar. | |
| 59 | 61 | const frameBar = frameNode |
| 60 | 62 | ? createPortal( |
| 61 | - <div className="fixed inset-0 pointer-events-none z-high"> | |
| 63 | + <div className="fixed top-0 left-0 right-0 bottom-(--extendify-notification-bar-height,0px) overflow-hidden pointer-events-none z-high"> | |
| 62 | 64 | <motion.div |
| 63 | 65 | className="absolute overflow-hidden" |
| 64 | 66 | initial={false} |
| 65 | 67 | animate={open ? 'open' : 'closed'} |
| @@ -89,21 +91,26 @@ | ||
| 89 | 91 | // app/Agent/Skeleton.php mirrors this markup for the deferred mount; edit both. |
| 90 | 92 | const sidebar = mountNode |
| 91 | 93 | ? createPortal( |
| 92 | 94 | <motion.div |
| 93 | - className=" fixed top-0 bottom-0 left-0 z-higher border-transparent border-8" | |
| 95 | + className={`fixed top-0 bottom-[var(--extendify-notification-bar-height,0px)] left-0 border-transparent border-8 ${ | |
| 96 | + // An emptied panel belongs under the scrim with the rest of the page. | |
| 97 | + seat === 'modal' ? 'z-high' : 'z-higher' | |
| 98 | + }`} | |
| 94 | 99 | id="extendify-agent-sidebar" |
| 100 | + data-extendify-agent-panel | |
| 101 | + data-extendify-agent-emptied={seat === 'modal' ? '' : undefined} | |
| 95 | 102 | initial={false} |
| 96 | 103 | inert={open ? undefined : ''} |
| 97 | 104 | animate={{ |
| 98 | 105 | x: open ? 0 : -SIDEBAR_WIDTH, |
| 99 | - width: canvasOpen ? CANVAS_WIDTH : SIDEBAR_WIDTH, | |
| 106 | + width: beside ? CANVAS_WIDTH : SIDEBAR_WIDTH, | |
| 100 | 107 | }} |
| 101 | 108 | transition={{ duration: ANIMATE_TIME / 1000, ease: 'easeInOut' }} |
| 102 | 109 | > |
| 103 | 110 | <div |
| 104 | - className={`h-full flex shadow-lg rounded-2xl overflow-hidden ${canvasOpen ? 'bg-gray-50' : 'bg-white'}`} | |
| 105 | - style={canvasOpen ? DOT_GRID : undefined} | |
| 111 | + className={`relative h-full flex shadow-lg rounded-2xl overflow-hidden ${beside ? 'bg-gray-50' : 'bg-white'}`} | |
| 112 | + style={beside ? DOT_GRID : undefined} | |
| 106 | 113 | > |
| 107 | 114 | <div |
| 108 | 115 | className="relative z-10 h-full flex shrink-0 flex-col rounded-2xl overflow-hidden bg-white shadow-lg" |
| 109 | 116 | style={{ width: SIDEBAR_WIDTH - FRAME_WIDTH * 2 }} |
| @@ -118,9 +125,9 @@ | ||
| 118 | 125 | /> |
| 119 | 126 | </div> |
| 120 | 127 | </div> |
| 121 | 128 | <div className="flex gap-1 h-full items-center p-2"> |
| 122 | - {canvasOpen ? null : ( | |
| 129 | + {seat ? null : ( | |
| 123 | 130 | <> |
| 124 | 131 | <OptionsPopover /> |
| 125 | 132 | <button |
| 126 | 133 | type="button" |
| @@ -141,9 +148,9 @@ | ||
| 141 | 148 | </div> |
| 142 | 149 | </div> |
| 143 | 150 | {open ? children : null} |
| 144 | 151 | </div> |
| 145 | - <CanvasPane /> | |
| 152 | + <CanvasPane seat="beside" /> | |
| 146 | 153 | </div> |
| 147 | 154 | </motion.div>, |
| 148 | 155 | mountNode, |
| 149 | 156 | ) |
| @@ -230,13 +237,17 @@ | ||
| 230 | 237 | const scale = (viewportWidth - SIDEBAR_WIDTH) / viewportWidth; |
| 231 | 238 | |
| 232 | 239 | // Subtract 40 because translateY(40px) below pushes the element down. |
| 233 | 240 | const scaledHeight = (window.innerHeight - 40) / scale; |
| 241 | + // Divided by the scale because this height is in pre-scale units. | |
| 242 | + const height = | |
| 243 | + `calc(${scaledHeight}px - ` + | |
| 244 | + `var(--extendify-notification-bar-height, 0px) / ${scale})`; | |
| 234 | 245 | |
| 235 | 246 | Object.assign(siteBlocks.style, { |
| 236 | 247 | transformOrigin: 'top left', |
| 237 | 248 | transform: `translateX(${SIDEBAR_WIDTH}px) translateY(40px) scale(${scale})`, |
| 238 | - height: `${scaledHeight}px`, | |
| 249 | + height, | |
| 239 | 250 | overflowY: 'auto', |
| 240 | 251 | // `auto` so the scrollTop below is instant; `smooth` would animate from 0. |
| 241 | 252 | scrollBehavior: 'auto', |
| 242 | 253 | }); |
| @@ -301,11 +312,8 @@ | ||
| 301 | 312 | firstRun.current = false; |
| 302 | 313 | } |
| 303 | 314 | |
| 304 | 315 | const raf = requestAnimationFrame(() => { |
| 305 | - const fw = open ? `${FRAME_WIDTH}px` : '0px'; | |
| 306 | - const ml = open ? `${SIDEBAR_WIDTH}px` : '0px'; | |
| 307 | - | |
| 308 | 316 | applyScaling(); |
| 309 | 317 | |
| 310 | 318 | // External contract: no in-repo listener by design — lets host-page |
| 311 | 319 | // consumers react to the agent reflowing the viewport. |
| @@ -314,20 +322,11 @@ | ||
| 314 | 322 | detail: { open }, |
| 315 | 323 | }), |
| 316 | 324 | ); |
| 317 | 325 | |
| 318 | - if (wpadminbar) { | |
| 319 | - Object.assign(wpadminbar.style, { | |
| 320 | - marginTop: fw, | |
| 321 | - marginRight: fw, | |
| 322 | - marginBottom: '0px', | |
| 323 | - marginLeft: ml, | |
| 324 | - borderRadius: open ? '8px 8px 0 0' : '0', | |
| 325 | - maxWidth: open | |
| 326 | - ? `calc(100% - ${SIDEBAR_WIDTH + FRAME_WIDTH}px)` | |
| 327 | - : '100%', | |
| 328 | - }); | |
| 329 | - } | |
| 326 | + // agent.css carries the bar offsets, so they drop with the | |
| 327 | + // breakpoint rather than with this component. | |
| 328 | + document.documentElement.classList.toggle('extendify-agent-docked', open); | |
| 330 | 329 | }); |
| 331 | 330 | |
| 332 | 331 | window.addEventListener('resize', applyScaling); |
| 333 | 332 | |