-
Notifications
You must be signed in to change notification settings - Fork 131
Add support for dynamic view transition names for global elements, and certain post elements to View Transitions plugin #1999
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c51fc54
3d7681e
b6a7d94
8545b4f
3ca288e
ec2904e
eaf9060
fab65d7
78f24ec
b970e29
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
<?php | ||
/** | ||
* Utility functions for View Transitions. | ||
* | ||
* @package view-transitions | ||
* @since 1.0.0 | ||
*/ | ||
|
||
/** | ||
* Gets the path to a script or stylesheet. | ||
* | ||
* @since 1.0.0 | ||
* @access private | ||
* | ||
* @param string $src_path Source path, relative to plugin root. | ||
* @param string|null $min_path Minified path. If not supplied, then '.min' is injected before the file extension in the source path. | ||
* @return string Full path to script or stylesheet. | ||
* | ||
* @noinspection PhpDocMissingThrowsInspection | ||
*/ | ||
function plvt_get_asset_path( string $src_path, ?string $min_path = null ): string { | ||
if ( null === $min_path ) { | ||
// Note: wp_scripts_get_suffix() is not used here because we need access to both the source and minified paths. | ||
$min_path = (string) preg_replace( '/(?=\.\w+$)/', '.min', $src_path ); | ||
} | ||
|
||
$plugin_dir = trailingslashit( dirname( __DIR__ ) ); | ||
|
||
$force_src = false; | ||
if ( WP_DEBUG && ! file_exists( $plugin_dir . $min_path ) ) { | ||
$force_src = true; | ||
/** | ||
* No WP_Exception is thrown by wp_trigger_error() since E_USER_ERROR is not passed as the error level. | ||
* | ||
* @noinspection PhpUnhandledExceptionInspection | ||
*/ | ||
wp_trigger_error( | ||
__FUNCTION__, | ||
sprintf( | ||
/* translators: %s is the minified asset path */ | ||
__( 'Minified asset has not been built: %s', 'view-transitions' ), | ||
$min_path | ||
), | ||
E_USER_WARNING | ||
); | ||
} | ||
|
||
if ( SCRIPT_DEBUG || $force_src ) { | ||
return $plugin_dir . $src_path; | ||
} | ||
|
||
return $plugin_dir . $min_path; | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -27,6 +27,67 @@ | |||||||||
add_theme_support( 'view-transitions' ); | ||||||||||
} | ||||||||||
|
||||||||||
/** | ||||||||||
* Sanitizes theme support arguments for the 'view-transitions' feature. | ||||||||||
* | ||||||||||
* If the feature was part of WordPress Core, the logic of this function would become part of the `add_theme_support()` | ||||||||||
* function instead. There is no action or filter that could be used though, hence it is implemented here in a separate | ||||||||||
* function that runs after `after_setup_theme`, but before the 'view-transitions' feature arguments are possibly used. | ||||||||||
* | ||||||||||
* @since 1.0.0 | ||||||||||
* | ||||||||||
* @global array<string, mixed> $_wp_theme_features Theme support features added and their arguments. | ||||||||||
*/ | ||||||||||
function plvt_sanitize_view_transitions_theme_support(): void { | ||||||||||
global $_wp_theme_features; | ||||||||||
|
||||||||||
if ( ! isset( $_wp_theme_features['view-transitions'] ) ) { | ||||||||||
return; | ||||||||||
} | ||||||||||
|
||||||||||
$args = $_wp_theme_features['view-transitions']; | ||||||||||
|
||||||||||
$defaults = array( | ||||||||||
'post-selector' => '.wp-block-post.post, article.post, body.single main', | ||||||||||
'global-transition-names' => array( | ||||||||||
'header' => 'header', | ||||||||||
'main' => 'main', | ||||||||||
), | ||||||||||
'post-transition-names' => array( | ||||||||||
'.wp-block-post-title, .entry-title' => 'post-title', | ||||||||||
'.wp-post-image' => 'post-thumbnail', | ||||||||||
'.wp-block-post-content, .entry-content' => 'post-content', | ||||||||||
), | ||||||||||
); | ||||||||||
|
||||||||||
// If no specific `$args` were provided, simply use the defaults. | ||||||||||
if ( true === $args ) { | ||||||||||
$args = $defaults; | ||||||||||
} else { | ||||||||||
/* | ||||||||||
* By default, `add_theme_support()` will take all function parameters as `$args`, but for the | ||||||||||
* 'view-transitions' feature, only a single associative array of arguments is relevant, which is expected as | ||||||||||
* the sole (optional) parameter. | ||||||||||
*/ | ||||||||||
if ( count( $args ) === 1 && isset( $args[0] ) && is_array( $args[0] ) ) { | ||||||||||
$args = $args[0]; | ||||||||||
} | ||||||||||
|
||||||||||
$args = wp_parse_args( $args, $defaults ); | ||||||||||
|
||||||||||
// Enforce correct types. | ||||||||||
if ( ! is_array( $args['global-transition-names'] ) ) { | ||||||||||
$args['global-transition-names'] = array(); | ||||||||||
} | ||||||||||
if ( ! is_array( $args['post-transition-names'] ) ) { | ||||||||||
$args['post-transition-names'] = array(); | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited | ||||||||||
$_wp_theme_features['view-transitions'] = $args; | ||||||||||
} | ||||||||||
|
||||||||||
/** | ||||||||||
* Loads view transitions based on the current configuration. | ||||||||||
* | ||||||||||
|
@@ -42,4 +103,44 @@ | |||||||||
wp_register_style( 'wp-view-transitions', false, array(), null ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion | ||||||||||
wp_add_inline_style( 'wp-view-transitions', $stylesheet ); | ||||||||||
wp_enqueue_style( 'wp-view-transitions' ); | ||||||||||
|
||||||||||
$theme_support = get_theme_support( 'view-transitions' ); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Per above, this could be replaced with:
Suggested change
|
||||||||||
|
||||||||||
/* | ||||||||||
* No point in loading the script if no specific view transition names are configured. | ||||||||||
*/ | ||||||||||
if ( | ||||||||||
( ! is_array( $theme_support['global-transition-names'] ) || count( $theme_support['global-transition-names'] ) === 0 ) && | ||||||||||
( ! is_array( $theme_support['post-transition-names'] ) || count( $theme_support['post-transition-names'] ) === 0 ) | ||||||||||
Comment on lines
+113
to
+114
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need for the
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really, but since the data comes from a global variable, I think it's better to use defensive coding. |
||||||||||
) { | ||||||||||
return; | ||||||||||
} | ||||||||||
|
||||||||||
$config = array( | ||||||||||
'postSelector' => $theme_support['post-selector'], | ||||||||||
'globalTransitionNames' => $theme_support['global-transition-names'], | ||||||||||
'postTransitionNames' => $theme_support['post-transition-names'], | ||||||||||
); | ||||||||||
|
||||||||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents | ||||||||||
$src_script = file_get_contents( plvt_get_asset_path( 'js/view-transitions.js' ) ); | ||||||||||
if ( false === $src_script || '' === $src_script ) { | ||||||||||
// This clause should never be entered, but is needed to please PHPStan. Can't hurt to be safe. | ||||||||||
return; | ||||||||||
} | ||||||||||
|
||||||||||
$init_script = sprintf( | ||||||||||
'plvtInitViewTransitions( %s )', | ||||||||||
wp_json_encode( $config, JSON_FORCE_OBJECT ) | ||||||||||
); | ||||||||||
|
||||||||||
/* | ||||||||||
* This must be in the <head>, not in the footer. | ||||||||||
* This is because the pagereveal event listener must be added before the first rAF occurs since that is when the event fires. See <https://issues.chromium.org/issues/40949146#comment10>. | ||||||||||
* An inline script is used to avoid an extra request. | ||||||||||
*/ | ||||||||||
wp_register_script( 'wp-view-transitions', false, array(), null, array() ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion | ||||||||||
wp_add_inline_script( 'wp-view-transitions', $src_script ); | ||||||||||
wp_add_inline_script( 'wp-view-transitions', $init_script ); | ||||||||||
wp_enqueue_script( 'wp-view-transitions' ); | ||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
export type ViewTransitionsConfig = { | ||
postSelector?: string; | ||
globalTransitionNames?: Record< string, string >; | ||
postTransitionNames?: Record< string, string >; | ||
}; | ||
|
||
export type InitViewTransitionsFunction = ( | ||
config: ViewTransitionsConfig | ||
) => void; | ||
|
||
declare global { | ||
interface Window { | ||
plvtInitViewTransitions?: InitViewTransitionsFunction; | ||
navigation?: { | ||
activation: NavigationActivation; | ||
}; | ||
} | ||
} | ||
|
||
export type PageSwapListenerFunction = ( event: PageSwapEvent ) => void; | ||
export type PageRevealListenerFunction = ( event: PageRevealEvent ) => void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idea: Instead of having
plvt_sanitize_view_transitions_theme_support()
sanitize the global variable and then override$_wp_theme_features
with the sanitized value, what if there was instead aplvt_get_view_transitions_theme_support()
which always returned the sanitized value? Then there would be no need to setplvt_sanitize_view_transitions_theme_support()
atinit
action, and there wouldn't be a risk that a theme made changes to the theme support after theinit
action happened.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good idea, though I personally think the risk is low enough to ignore it in favor of having a more Core-like API, relying on the actual functions that should be used for this and "patching" the lack of integrated sanitization via a hook like this.
If later there are reports of problems with this, we could reassess the relevance.