get('admin.navigation.prefetch', true)) { add_action('admin_print_footer_scripts', [$this, 'speculationRules']); } if ($settings->get('admin.navigation.transitions', true)) { add_action('admin_enqueue_scripts', [$this, 'viewTransitions']); } } public function speculationRules(): void { $admin = (string) wp_parse_url(admin_url(), PHP_URL_PATH); $admin = '/' . trim($admin, '/') . '/'; // Screens that do work on a plain GET: post locks (post.php), auto-drafts // (post-new.php), endpoints, updaters, downloads and the Customizer. $unsafePaths = [ 'post.php', 'post-new.php', 'admin-ajax.php', 'admin-post.php', 'async-upload.php', 'customize.php', 'update.php', 'update-core.php', 'export.php', 'export-personal-data.php', 'erase-personal-data.php', 'site-editor.php', 'plugin-install.php', 'theme-install.php', 'press-this.php', 'upgrade.php', 'setup-config.php', 'install.php', 'network.php', ]; $unsafe = array_map(static fn (string $file): string => $admin . $file . '*', $unsafePaths); // Any URL that carries an action or a nonce may change something. The // object form keeps "?" from being read as a pattern modifier. foreach (['*action=*', '*nonce*', '*delete*', '*trash*', '*activate*', '*logout*', '*doing_wp_cron*'] as $search) { $unsafe[] = ['pathname' => $admin . '*', 'search' => $search]; } $rules = [ 'prefetch' => [[ 'source' => 'document', 'where' => [ 'and' => [ ['href_matches' => $admin . '*'], ['not' => ['href_matches' => $unsafe]], ['not' => ['href_matches' => '/wp-login.php*']], ['not' => ['selector_matches' => implode(', ', [ 'a[target]', 'a[download]', 'a[rel~="nofollow"]', '.submitdelete', '.row-actions .delete a', '.row-actions .trash a', 'a.thickbox', '#wp-admin-bar-logout a', '[data-fd-no-prefetch]', '[data-fd-no-prefetch] a', ])]], ], ], 'eagerness' => 'moderate', ]], ]; /** * Filters the Speculation Rules used to prefetch admin pages on hover. * Add exclusions to `prefetch[0].where.and`, or return [] to turn * prefetching off. * * @param array $rules */ $rules = (array) apply_filters('featherdash/navigation/prefetch_rules', $rules); if ($rules === []) { return; } wp_print_inline_script_tag( (string) wp_json_encode($rules, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG), ['type' => 'speculationrules'] ); } public function viewTransitions(): void { wp_register_style('featherdash-view-transitions', false, [], FEATHERDASH_VERSION); wp_enqueue_style('featherdash-view-transitions'); wp_add_inline_style( 'featherdash-view-transitions', '@media (prefers-reduced-motion:no-preference){@view-transition{navigation:auto}}' // The toolbar and sidebar are captured separately, so they stay still // while the page content cross-fades. . '#wpadminbar{view-transition-name:fd-toolbar}' . '#adminmenuwrap{view-transition-name:fd-menu}' . '::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation-duration:.16s}' ); } }