PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.3-beta
16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 All 507 releases
jetpack / jetpack_vendor / automattic / jetpack-wp-build-polyfills / src / class-wp-build-admin-frame.php

class-wp-build-admin-frame.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-beta, at jetpack_vendor/automattic/jetpack-wp-build-polyfills/src/class-wp-build-admin-frame.php

168 lines 6.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Frame fixes for the `@wordpress/boot` single-page layout in wp-admin.
4 *
5 * @package automattic/jetpack-wp-build-polyfills
6 */
7
8 namespace Automattic\Jetpack\WP_Build_Polyfills;
9
10 /**
11 * Reconciles the boot single-page layout with the wp-admin frame: the backdrop
12 * continues the admin menu color, and the app keeps its own scroller when the
13 * admin menu is taller than the viewport.
14 *
15 * `@wordpress/admin-ui` derives the backdrop from a fixed map of Core color
16 * schemes and falls back to a near-black `modern` seed for any other scheme,
17 * WordPress.com and third-party ones included. On WordPress 7.0+ the boot
18 * module that runs is Core's bundled copy, so the override is applied from
19 * PHP around the page: a stylesheet printed on `admin_head` and a script
20 * printed on `in_admin_header` that samples the rendered menu background into
21 * a custom property on the root element.
22 *
23 * The frame is also held steady: painted before boot mounts, and captured whole
24 * by the cross-document view transitions Core enables in wp-admin.
25 *
26 * Pages without a boot mount container are unaffected: the selectors match nothing.
27 */
28 class WP_Build_Admin_Frame {
29
30 /**
31 * Hook the stylesheet, the render hold and the frame script. Safe to call repeatedly.
32 *
33 * @return void
34 */
35 public static function register() {
36 add_action( 'admin_head', array( self::class, 'print_styles' ) );
37 add_action( 'admin_head', array( self::class, 'print_render_hold' ) );
38 add_action( 'in_admin_header', array( self::class, 'print_script' ) );
39 }
40
41 /**
42 * Print the backdrop and scroll-containment overrides.
43 *
44 * The backdrop selectors are (1,1,0), so they outrank the layout rule boot injects
45 * at runtime. The fallbacks keep boot's own colors when the sampling script
46 * did not run.
47 *
48 * @return void
49 */
50 public static function print_styles() {
51 // The layout root is `.boot-layout--single-page` up to boot 0.20. From
52 // boot 0.21 (WordPress/gutenberg#81756, Gutenberg 23.9) the styles are
53 // CSS Modules and the class is `_<hash>__layout-single-page`, so the
54 // attribute selector matches the local name whatever the hash is.
55 ?>
56 <style id="wp-build-admin-frame-css">
57 #wpcontent .boot-layout--single-page,
58 #wpcontent [class*="__layout-single-page"] {
59 background: var(--wp-build-admin-menu-background, var(--wpds-color-background-surface-neutral-weak));
60 }
61 body:has(.boot-layout--single-page),
62 body:has([class*="__layout-single-page"]) {
63 background: var(--wp-build-admin-menu-background, #fff);
64 }
65
66 /* Old-only boot surfaces zoom or slide over the next page, so let the root snapshot carry them. */
67 html.wp-build-admin-frame-leaving :is(.boot-layout--single-page, [class*="__layout-single-page"]) :is([class*="__stage"], [class*="__inspector"], [class*="__canvas"], .interface-interface-skeleton__header, .interface-interface-skeleton__sidebar) {
68 view-transition-name: none !important;
69 }
70
71 /*
72 * Boot's layout is absolutely positioned against `#wpbody`, which grows
73 * with the admin menu: a menu taller than the viewport stretches the app
74 * and scrolls its sticky header away, so cap `#wpbody` to the viewport to
75 * give the app back its own scroller. Below 783px the menu is off-canvas
76 * and the template scrolls `#wpwrap` instead. Drop this once the minimum
77 * supported WordPress bundles a boot that pins its own layout
78 * (WordPress/gutenberg#82114).
79 */
80 @media (min-width: 783px) {
81 body.js:has([id$="-wp-admin-app"]:empty) #wpbody,
82 body:has(.boot-layout--single-page) #wpbody,
83 body:has([class*="__layout-single-page"]) #wpbody {
84 position: sticky;
85 top: var(--wp-admin--admin-bar--height, 32px);
86 height: calc(100vh - var(--wp-admin--admin-bar--height, 32px));
87 }
88
89 /*
90 * Paint boot's stage on the empty app container until it mounts, where the
91 * page template's critical CSS would otherwise leave the area white. Plain
92 * #fff: boot's theme provider whitens the stage, unlike the root surface token.
93 * A container that never mounts keeps the panel and passes for an empty app.
94 */
95 body.js:has([id$="-wp-admin-app"]:empty) {
96 background: var(--wp-build-admin-menu-background, #fff);
97 }
98 body.js [id$="-wp-admin-app"]:empty {
99 position: absolute;
100 inset-block: 0 8px;
101 inset-inline: 0 8px;
102 border-radius: var(--wpds-border-radius-xl, 12px);
103 background: #fff;
104 }
105 }
106 </style>
107 <?php
108 }
109
110 /**
111 * Hold the first render of a wp-build page until the document has parsed, so Core's
112 * cross-document view transitions capture it with its admin menu.
113 *
114 * Held on every WordPress 7.0+ wp-build page, reduced motion included: PHP cannot see the media
115 * query Core gates the transition on. Never a module: Firefox then drops wp-admin's footer import map.
116 *
117 * @return void
118 */
119 public static function print_render_hold() {
120 if (
121 ! wp_style_is( 'wp-view-transitions-admin', 'enqueued' )
122 || ! preg_grep( '/-wp-admin-prerequisites$/', wp_scripts()->queue )
123 ) {
124 return;
125 }
126
127 // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- Not enqueueable: the script APIs pass `src` through esc_url(), which strips data: URLs.
128 echo '<script id="wp-build-admin-frame-render-hold" src="data:text/javascript," defer blocking="render"></script>' . "\n";
129 }
130
131 /**
132 * Print the script that samples the admin menu background and marks the root
133 * while the page leaves for a cross-document view transition.
134 *
135 * Runs right after `#adminmenuback` is printed and before the layout
136 * mounts, which is why the property goes on the root element.
137 *
138 * @return void
139 */
140 public static function print_script() {
141 $script = <<<'JS'
142 ( function () {
143 window.addEventListener( 'pageswap', function ( event ) {
144 if ( event.viewTransition ) {
145 document.documentElement.classList.add( 'wp-build-admin-frame-leaving' );
146 }
147 } );
148 // A page restored from the back/forward cache reveals again with the class still set.
149 window.addEventListener( 'pagereveal', function () {
150 document.documentElement.classList.remove( 'wp-build-admin-frame-leaving' );
151 } );
152
153 var menu = document.getElementById( 'adminmenuback' );
154 if ( ! menu ) {
155 return;
156 }
157 var color = window.getComputedStyle( menu ).backgroundColor;
158 if ( ! color || 'rgba(0, 0, 0, 0)' === color || 'transparent' === color ) {
159 return;
160 }
161 document.documentElement.style.setProperty( '--wp-build-admin-menu-background', color );
162 } )();
163 JS;
164
165 wp_print_inline_script_tag( $script, array( 'id' => 'wp-build-admin-frame-js' ) );
166 }
167 }
168