Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions plugins/view-transitions/css/validator-selector.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* CSS Selector Validation Styles
*
* Styles for visual feedback of CSS selector validation.
*
* @since n.e.x.t
*/

/* Valid selector indicator */
input.plvt-selector-valid {
border-color: #46b450 !important;
background-color: #f0fdf4;
}

input.plvt-selector-valid:focus {
border-color: #46b450 !important;
}

/* Invalid selector indicator */
input.plvt-selector-invalid {
border-color: #dc2626 !important;
background-color: #fdf2f2;
}

input.plvt-selector-invalid:focus {
border-color: #dc2626 !important;
}

/* Error message styling */
.plvt-selector-error {
color: #dc2626;
margin-top: 5px;
font-style: italic;
}
1 change: 1 addition & 0 deletions plugins/view-transitions/hooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ function plvt_render_generator(): void {
add_action( 'init', 'plvt_register_setting' );
add_action( 'init', 'plvt_apply_settings_to_theme_support' );
add_action( 'load-options-reading.php', 'plvt_add_setting_ui' );
add_action( 'admin_enqueue_scripts', 'plvt_enqueue_selector_validation' );
add_filter( 'plugin_action_links_' . plugin_basename( VIEW_TRANSITIONS_MAIN_FILE ), 'plvt_add_settings_action_link' );
45 changes: 44 additions & 1 deletion plugins/view-transitions/includes/admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,56 @@ function plvt_print_view_transitions_admin_style(): void {
}

$options = plvt_get_stored_setting_value();
if ( ! isset( $options['enable_admin_transitions'] ) || true !== $options['enable_admin_transitions'] ) {
if ( ! isset( $options['enable_admin_transitions'] ) || ! (bool) $options['enable_admin_transitions'] ) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change? It can be reverted anyway since this function is now obsolete for 7.0.

return;
}

?>
<style>
@view-transition { navigation: auto; }
::view-transition-group(*) { --plvt-view-transition-animation-duration: <?php echo absint( $options['default_transition_animation_duration'] ); ?>ms; }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be reverted now that View Transitions are merged into core. We can iterate on any such setting in Trac.

#adminmenu > .menu-top { view-transition-name: attr(id type(<custom-ident>), none); }
</style>
<?php
}

/**
* Enqueues the CSS selector validation scripts and styles on the settings page.
*
* This function loads the JavaScript and CSS needed for real-time CSS selector
* validation in the View Transitions settings panel.
*
* @since n.e.x.t
* @access private
*/
function plvt_enqueue_selector_validation(): void {
$current_screen = get_current_screen();

// Only enqueue on the reading settings page.
if ( null === $current_screen || 'options-reading' !== $current_screen->id ) {
return;
}

// Enqueue validation CSS.
wp_enqueue_style(
'plvt-selector-validator',
plugin_dir_url( VIEW_TRANSITIONS_MAIN_FILE ) . 'css/validator-selector.css',
array(),
VIEW_TRANSITIONS_VERSION
);

// Enqueue validation JS.
wp_enqueue_script(
'plvt-selector-validator',
plugin_dir_url( VIEW_TRANSITIONS_MAIN_FILE ) . 'js/validator-selector.js',
array( 'wp-i18n' ),
VIEW_TRANSITIONS_VERSION,
array( 'in_footer' => false )
);

// Set up translations for the script.
wp_set_script_translations(
'plvt-selector-validator',
'view-transitions'
);
}
87 changes: 84 additions & 3 deletions plugins/view-transitions/includes/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}

/**
* Minimum allowed transition animation duration in milliseconds.
*
* @since n.e.x.t
* @var int
*/
const PLVT_MIN_ANIMATION_DURATION = 100;

/**
* Maximum allowed transition animation duration in milliseconds.
*
* @since n.e.x.t
* @var int
*/
const PLVT_MAX_ANIMATION_DURATION = 5000;
// @codeCoverageIgnoreEnd

/**
Expand Down Expand Up @@ -135,9 +151,11 @@ function plvt_sanitize_setting( $input ): array {
$value['default_transition_animation'] = $input['default_transition_animation'];
}

// Handle default_transition_animation_duration separately.
// Handle default_transition_animation_duration with min/max bounds.
if ( isset( $input['default_transition_animation_duration'] ) ) {
$value['default_transition_animation_duration'] = absint( $input['default_transition_animation_duration'] );
$duration = absint( $input['default_transition_animation_duration'] );
// Clamp between min and max for sensible values.
$value['default_transition_animation_duration'] = max( PLVT_MIN_ANIMATION_DURATION, min( PLVT_MAX_ANIMATION_DURATION, $duration ) );
}

$selector_options = array(
Expand Down Expand Up @@ -193,6 +211,12 @@ function plvt_register_setting(): void {
'type' => 'string',
'enum' => array_keys( plvt_get_view_transition_animation_labels() ),
),
'default_transition_animation_duration' => array(
'description' => __( 'Duration of the view transition animation in milliseconds.', 'view-transitions' ),
'type' => 'integer',
'minimum' => PLVT_MIN_ANIMATION_DURATION,
'maximum' => PLVT_MAX_ANIMATION_DURATION,
),
),
'additionalProperties' => false,
),
Expand Down Expand Up @@ -331,6 +355,10 @@ static function (): void {
'section' => 'plvt_view_transitions',
'title' => __( 'Transition Animation Duration', 'view-transitions' ),
'description' => __( 'Control the duration of the view transition. Enter the value in milliseconds (e.g., 500, 1000, 2000).', 'view-transitions' ),
'min' => PLVT_MIN_ANIMATION_DURATION,
'max' => PLVT_MAX_ANIMATION_DURATION,
'step' => 50,
'unit' => 'ms',
),
'header_selector' => array(
'section' => 'plvt_view_transitions',
Expand Down Expand Up @@ -476,15 +504,68 @@ function plvt_render_settings_field( array $args ): void {
<?php echo esc_html( $args['description'] ); ?>
</label>
<?php
} elseif ( 'number' === $type ) {
?>
<input
type="number"
id="<?php echo esc_attr( $args['label_for'] ); ?>"
name="<?php echo esc_attr( "plvt_view_transitions[{$args['field']}]" ); ?>"
value="<?php echo esc_attr( (string) $value ); ?>"
class="small-text"
<?php
if ( isset( $args['min'] ) ) {
?>
min="<?php echo esc_attr( (string) $args['min'] ); ?>"
<?php
}
if ( isset( $args['max'] ) ) {
?>
max="<?php echo esc_attr( (string) $args['max'] ); ?>"
<?php
}
if ( isset( $args['step'] ) ) {
?>
step="<?php echo esc_attr( (string) $args['step'] ); ?>"
<?php
}
if ( '' !== $args['description'] ) {
?>
aria-describedby="<?php echo esc_attr( $args['label_for'] . '-description' ); ?>"
<?php
}
?>
>
<?php
if ( isset( $args['unit'] ) && '' !== $args['unit'] ) {
?>
<span class="description"><?php echo esc_html( $args['unit'] ); ?></span>
<?php
}
} else {
// Determine if this is a CSS selector field that needs validation.
$is_selector_field = in_array(
$args['field'],
array(
'header_selector',
'main_selector',
'post_title_selector',
'post_thumbnail_selector',
'post_content_selector',
),
true
);
?>
<input
<?php echo ( 'number' === $type ) ? 'type="number"' : ''; ?>
id="<?php echo esc_attr( $args['label_for'] ); ?>"
name="<?php echo esc_attr( "plvt_view_transitions[{$args['field']}]" ); ?>"
value="<?php echo esc_attr( (string) $value ); ?>"
class="regular-text code"
<?php
if ( $is_selector_field ) {
?>
data-plvt-validate-selector
<?php
}
if ( '' !== $args['description'] ) {
?>
aria-describedby="<?php echo esc_attr( $args['label_for'] . '-description' ); ?>"
Expand Down
111 changes: 111 additions & 0 deletions plugins/view-transitions/js/validator-selector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* CSS Selector Validation for View Transitions Settings
*
* This script provides real-time validation for CSS selector input fields
* in the View Transitions settings panel.
*
* @since n.e.x.t
*/

/**
* @typedef {Object} WP
* @property {Object} i18n - WordPress internationalization object
* @property {Function} i18n.__ - Translation function
*/

( () => {
/**
* Validates a CSS selector by attempting to use it with document.querySelector.
*
* @param {string} selector The CSS selector to validate.
* @return {boolean} Whether the selector is valid.
*/
function validateSelector( selector ) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something is wrong here because it is flagging valid selectors as invalid:

Image

Apparently it doesn't work for selector list 😦

CSS.supports( 'selector(foo)' )
=> true
CSS.supports( 'selector(foo,bar)' )
=> false

I guess we could switch to using a textarea where we indicate there should be one selector provided per line. But that's not very user-friendly or intuitive for someone used to writing CSS.

Maybe this validation feature isn't worthwhile, especially since this UI wouldn't be part of core eventually.

// Empty selectors are allowed (they reset to default)
if ( '' === selector.trim() ) {
return true;
}

return CSS.supports( `selector(${ selector })` );
}

/**
* Sets custom validity for a selector input field.
*
* @param {HTMLInputElement} input The input element to validate.
*/
function updateValidation( input ) {
const isValid = validateSelector( input.value );

if ( isValid ) {
input.setCustomValidity( '' );
input.classList.remove( 'plvt-selector-invalid' );
input.classList.add( 'plvt-selector-valid' );

const existingError = input.parentNode.querySelector(
'.plvt-selector-error'
);
if ( existingError ) {
existingError.remove();
}
} else {
// @ts-ignore
const errorMessage = wp.i18n.__(
'Invalid CSS selector',
'view-transitions'
);
input.setCustomValidity( errorMessage );
input.classList.remove( 'plvt-selector-valid' );
input.classList.add( 'plvt-selector-invalid' );

let errorElement = input.parentNode.querySelector(
'.plvt-selector-error'
);
if ( ! errorElement ) {
errorElement = document.createElement( 'p' );
errorElement.className = 'plvt-selector-error description';
input.parentNode.insertBefore(
errorElement,
input.nextSibling
);
}
errorElement.textContent = errorMessage;
}
}

/**
* Initializes validation for all selector input fields.
*/
function initValidation() {
// Target all text inputs for selectors
const selectorInputs = document.querySelectorAll(
'input[data-plvt-validate-selector]'
);

selectorInputs.forEach( ( element ) => {
const input = /** @type {HTMLInputElement} */ ( element );

// Validate on blur
input.addEventListener( 'blur', () => {
updateValidation( input );
} );

// Validate on input for real-time feedback
input.addEventListener( 'input', () => {
updateValidation( input );
} );

// Validate on page load if field has a value
if ( '' !== input.value.trim() ) {
updateValidation( input );
}
} );
}

// Initialize when DOM is ready
if ( 'loading' === document.readyState ) {
document.addEventListener( 'DOMContentLoaded', initValidation );
} else {
initValidation();
}
} )();
Loading
Loading