PluginProbe
Extendify / 3.2.1
Extendify v3.2.1
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 -52 3.1.23.2.1 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,17 +237,20 @@
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 - colorScheme: 'light',
218 253 });
219 254 // Force layout so scrollTop respects the new height/overflow.
220 255 void siteBlocks.scrollHeight;
221 256 siteBlocks.scrollTop = savedScroll.current;
@@ -240,9 +275,8 @@
240 275 height: '',
241 276 overflowY: '',
242 277 maxWidth: '100vw',
243 278 scrollBehavior: '',
244 - colorScheme: '',
245 279 });
246 280 if (stickyHeader) {
247 281 stickyHeader.style.removeProperty(
248 282 '--wp-admin--admin-bar--position-offset',
@@ -278,11 +312,8 @@
278 312 firstRun.current = false;
279 313 }
280 314
281 315 const raf = requestAnimationFrame(() => {
282 - const fw = open ? `${FRAME_WIDTH}px` : '0px';
283 - const ml = open ? `${SIDEBAR_WIDTH}px` : '0px';
284 -
285 316 applyScaling();
286 317
287 318 // External contract: no in-repo listener by design — lets host-page
288 319 // consumers react to the agent reflowing the viewport.
@@ -291,20 +322,11 @@
291 322 detail: { open },
292 323 }),
293 324 );
294 325
295 - if (wpadminbar) {
296 - Object.assign(wpadminbar.style, {
297 - marginTop: fw,
298 - marginRight: fw,
299 - marginBottom: '0px',
300 - marginLeft: ml,
301 - borderRadius: open ? '8px 8px 0 0' : '0',
302 - maxWidth: open
303 - ? `calc(100% - ${SIDEBAR_WIDTH + FRAME_WIDTH}px)`
304 - : '100%',
305 - });
306 - }
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);
307 329 });
308 330
309 331 window.addEventListener('resize', applyScaling);
310 332