PluginProbe
Extendify / trunk
Extendify vtrunk
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
← All changes | src/Agent/components/layouts/SidebarLayout.jsx +74 -50 3.1.4 → trunk View file →
@@ -1,6 +1,12 @@
1 +import {
2 + CANVAS_PANE_WIDTH,
3 + CanvasPane,
4 + DOT_GRID,
5 + useCanvasSeat,
6 +} from '@agent/components/Canvas';
1 7 import { usePortal } from '@agent/hooks/usePortal';
2 -import { useGlobalStore } from '@agent/state/global';
8 +import { DESKTOP_MIN_WIDTH, useGlobalStore } from '@agent/state/global';
3 9 import { createPortal, useEffect, useRef } from '@wordpress/element';
4 10 import { __ } from '@wordpress/i18n';
5 11 import { close, Icon } from '@wordpress/icons';
6 12 import { motion } from 'framer-motion';
@@ -5,9 +11,10 @@
5 11 import { close, Icon } from '@wordpress/icons';
6 12 import { motion } from 'framer-motion';
7 13 import { OptionsPopover } from '../OptionsPopover';
8 14
9 -const SIDEBAR_WIDTH = 384; // 96 * 4 (w-96)
15 +const SIDEBAR_WIDTH = 384;
16 +const CANVAS_WIDTH = SIDEBAR_WIDTH + CANVAS_PANE_WIDTH;
10 17 const FRAME_WIDTH = 8; // border-8
11 18 const ANIMATE_TIME = 300;
12 19
13 20 export const SidebarLayout = ({ children }) => {
@@ -13,8 +20,10 @@
13 20 export const SidebarLayout = ({ children }) => {
14 21 const mountNode = usePortal('extendify-agent-sidebar-mount');
15 22 const frameNode = usePortal('extendify-agent-border-frame-mount');
16 23 const { open, setOpen } = useGlobalStore();
24 + const seat = useCanvasSeat();
25 + const beside = seat === 'beside';
17 26 useLayoutShift(open);
18 27
19 28 const closeAgent = () => {
20 29 setOpen(false);
@@ -47,11 +56,12 @@
47 56 borderRadius: 0,
48 57 },
49 58 };
50 59
60 + // Unclipped, the 9999px shadow covers the notification bar.
51 61 const frameBar = frameNode
52 62 ? createPortal(
53 - <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">
54 64 <motion.div
55 65 className="absolute overflow-hidden"
56 66 initial={false}
57 67 animate={open ? 'open' : 'closed'}
@@ -77,49 +87,70 @@
77 87 frameNode,
78 88 )
79 89 : null;
80 90
91 + // app/Agent/Skeleton.php mirrors this markup for the deferred mount; edit both.
81 92 const sidebar = mountNode
82 93 ? createPortal(
83 94 <motion.div
84 - style={{ width: SIDEBAR_WIDTH }}
85 - className=" fixed top-0 bottom-0 left-0 w-96 flex-col 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 + }`}
86 99 id="extendify-agent-sidebar"
100 + data-extendify-agent-panel
101 + data-extendify-agent-emptied={seat === 'modal' ? '' : undefined}
87 102 initial={false}
88 103 inert={open ? undefined : ''}
89 - animate={{ x: open ? 0 : -SIDEBAR_WIDTH }}
104 + animate={{
105 + x: open ? 0 : -SIDEBAR_WIDTH,
106 + width: beside ? CANVAS_WIDTH : SIDEBAR_WIDTH,
107 + }}
90 108 transition={{ duration: ANIMATE_TIME / 1000, ease: 'easeInOut' }}
91 109 >
92 - <div className="h-full flex flex-col shadow-lg rounded-2xl overflow-hidden bg-white">
93 - <div className="group flex shrink-0 items-center justify-between overflow-hidden bg-banner-main text-banner-text">
94 - <div className="flex h-full grow items-center justify-between gap-1 p-0 py-2.5">
95 - <div className="flex h-5 px-4 max-w-36 overflow-hidden">
96 - <img
97 - className="max-h-full max-w-full object-contain"
98 - src={window.extSharedData.partnerLogo}
99 - alt={window.extSharedData.partnerName}
100 - />
110 + <div
111 + className={`relative h-full flex shadow-lg rounded-2xl overflow-hidden ${beside ? 'bg-gray-50' : 'bg-white'}`}
112 + style={beside ? DOT_GRID : undefined}
113 + >
114 + <div
115 + className="relative z-10 h-full flex shrink-0 flex-col rounded-2xl overflow-hidden bg-white shadow-lg"
116 + style={{ width: SIDEBAR_WIDTH - FRAME_WIDTH * 2 }}
117 + >
118 + <div className="group flex shrink-0 items-center justify-between overflow-hidden bg-banner-main text-banner-text">
119 + <div className="flex h-full grow items-center justify-between gap-1 p-0 py-2.5">
120 + <div className="flex h-5 px-4 max-w-36 overflow-hidden">
121 + <img
122 + className="max-h-full max-w-full object-contain"
123 + src={window.extSharedData.partnerLogo}
124 + alt={window.extSharedData.partnerName}
125 + />
126 + </div>
101 127 </div>
128 + <div className="flex gap-1 h-full items-center p-2">
129 + {seat ? null : (
130 + <>
131 + <OptionsPopover />
132 + <button
133 + type="button"
134 + className="relative z-10 flex justify-center h-6 w-6 items-center border-0 bg-banner-main text-banner-text outline-hidden ring-design-main focus:shadow-none focus:outline-hidden focus-visible:outline-design-main focus:ring-2 hover:opacity-80 rounded-sm"
135 + onClick={closeAgent}
136 + >
137 + <Icon
138 + className="pointer-events-none fill-current leading-none"
139 + icon={close}
140 + size={18}
141 + />
142 + <span className="sr-only">
143 + {__('Close window', 'extendify-local')}
144 + </span>
145 + </button>
146 + </>
147 + )}
148 + </div>
102 149 </div>
103 - <div className="flex gap-1 h-full items-center p-2">
104 - <OptionsPopover />
105 - <button
106 - type="button"
107 - className="relative z-10 flex justify-center h-6 w-6 items-center border-0 bg-banner-main text-banner-text outline-hidden ring-design-main focus:shadow-none focus:outline-hidden focus-visible:outline-design-main focus:ring-2 hover:opacity-80 rounded-sm"
108 - onClick={closeAgent}
109 - >
110 - <Icon
111 - className="pointer-events-none fill-current leading-none"
112 - icon={close}
113 - size={18}
114 - />
115 - <span className="sr-only">
116 - {__('Close window', 'extendify-local')}
117 - </span>
118 - </button>
119 - </div>
150 + {open ? children : null}
120 151 </div>
121 - {open ? children : null}
152 + <CanvasPane seat="beside" />
122 153 </div>
123 154 </motion.div>,
124 155 mountNode,
125 156 )
@@ -193,9 +224,10 @@
193 224
194 225 const applyScaling = () => {
195 226 if (!siteBlocks) return;
196 227
197 - if (open) {
228 + // Unmount leaves these styles, so scaling below the breakpoint is permanent.
229 + if (open && window.innerWidth >= DESKTOP_MIN_WIDTH) {
198 230 // Capture before `position: fixed` zeroes window.scrollY.
199 231 // Fall through to savedScroll.current so resize / strict-mode
200 232 // re-runs don't clobber it with the now-pinned scrollY (0).
201 233 const stashed = isFirstApply ? consumeStashedScroll() : 0;
@@ -205,13 +237,17 @@
205 237 const scale = (viewportWidth - SIDEBAR_WIDTH) / viewportWidth;
206 238
207 239 // Subtract 40 because translateY(40px) below pushes the element down.
208 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})`;
209 245
210 246 Object.assign(siteBlocks.style, {
211 247 transformOrigin: 'top left',
212 248 transform: `translateX(${SIDEBAR_WIDTH}px) translateY(40px) scale(${scale})`,
213 - height: `${scaledHeight}px`,
249 + height,
214 250 overflowY: 'auto',
215 251 // `auto` so the scrollTop below is instant; `smooth` would animate from 0.
216 252 scrollBehavior: 'auto',
217 253 });
@@ -276,11 +312,8 @@
276 312 firstRun.current = false;
277 313 }
278 314
279 315 const raf = requestAnimationFrame(() => {
280 - const fw = open ? `${FRAME_WIDTH}px` : '0px';
281 - const ml = open ? `${SIDEBAR_WIDTH}px` : '0px';
282 -
283 316 applyScaling();
284 317
285 318 // External contract: no in-repo listener by design — lets host-page
286 319 // consumers react to the agent reflowing the viewport.
@@ -289,20 +322,11 @@
289 322 detail: { open },
290 323 }),
291 324 );
292 325
293 - if (wpadminbar) {
294 - Object.assign(wpadminbar.style, {
295 - marginTop: fw,
296 - marginRight: fw,
297 - marginBottom: '0px',
298 - marginLeft: ml,
299 - borderRadius: open ? '8px 8px 0 0' : '0',
300 - maxWidth: open
301 - ? `calc(100% - ${SIDEBAR_WIDTH + FRAME_WIDTH}px)`
302 - : '100%',
303 - });
304 - }
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);
305 329 });
306 330
307 331 window.addEventListener('resize', applyScaling);
308 332