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
55 changes: 55 additions & 0 deletions src/js/_enqueues/lib/admin-bar.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,61 @@
if ( adminBarLogout ) {
adminBarLogout.addEventListener( 'click', emptySessionStorage );
}

/*
* My Sites fly-out positioning.
*
* When the My Sites dropdown is scrollable (overflow-y: auto), the nested
* fly-out submenus (Dashboard, New Post, etc.) are clipped by the scroll
* container. The CSS switches them to position: fixed, and this JS computes
* their coordinates from getBoundingClientRect() so they appear next to
* the hovered site. The fly-out is clamped so it never extends below the
* viewport.
*
* @since 7.0.0
* @see https://core.trac.wordpress.org/ticket/15317
*/
var mySitesWrapper = document.querySelector( '#wp-admin-bar-my-sites > .ab-sub-wrapper' );

if ( mySitesWrapper ) {
mySitesWrapper.addEventListener( 'mouseover', function( e ) {
var li = getClosest( e.target, '.menupop' );

if ( ! li || li.id === 'wp-admin-bar-my-sites' ) {
return;
}

var sub = li.querySelector( ':scope > .ab-sub-wrapper' );

if ( ! sub ) {
return;
}

var rect = li.getBoundingClientRect();
var top = rect.top;
var isRTL = ( document.documentElement.dir === 'rtl' );

// Measure the fly-out to keep it inside the viewport.
sub.style.visibility = 'hidden';
sub.style.display = 'block';
var subHeight = sub.offsetHeight;
var subWidth = sub.offsetWidth;
sub.style.removeProperty( 'visibility' );
sub.style.removeProperty( 'display' );

if ( top + subHeight > window.innerHeight ) {
top = Math.max( 0, window.innerHeight - subHeight );
}

li.style.setProperty( '--msf-top', top + 'px' );

if ( isRTL ) {
li.style.setProperty( '--msf-left', Math.max( 0, rect.left - subWidth ) + 'px' );
} else {
li.style.setProperty( '--msf-left', rect.right + 'px' );
}
} );
}
} );

/**
Expand Down
121 changes: 58 additions & 63 deletions src/wp-includes/admin-bar.php
Original file line number Diff line number Diff line change
Expand Up @@ -675,91 +675,86 @@ function wp_admin_bar_my_sites_menu( $wp_admin_bar ) {
*
* @param bool $show_site_icons Whether site icons should be shown in the toolbar. Default true.
*/
$show_site_icons = apply_filters( 'wp_admin_bar_show_site_icons', true );

foreach ( (array) $wp_admin_bar->user->blogs as $blog ) {
switch_to_blog( $blog->userblog_id );
$blogs = (array) $wp_admin_bar->user->blogs;
$show_site_icons = apply_filters( 'wp_admin_bar_show_site_icons', count( $blogs ) <= 20 );

foreach ( $blogs as $blog ) {
$blog_id = (int) $blog->userblog_id;
$siteurl = untrailingslashit( $blog->siteurl );
$adminurl = $siteurl . '/wp-admin';
$homeurl = esc_url( set_url_scheme( 'http://' . $blog->domain . $blog->path ) );

if ( $show_site_icons ) {
switch_to_blog( $blog_id );

if ( has_site_icon() ) {
$blavatar = sprintf(
'<img class="blavatar" src="%s" srcset="%s 2x" alt="" width="16" height="16"%s />',
esc_url( get_site_icon_url( 16 ) ),
esc_url( get_site_icon_url( 32 ) ),
( wp_lazy_loading_enabled( 'img', 'site_icon_in_toolbar' ) ? ' loading="lazy"' : '' )
);
} else {
$blavatar = '<div class="blavatar"></div>';
}

if ( true === $show_site_icons && has_site_icon() ) {
$blavatar = sprintf(
'<img class="blavatar" src="%s" srcset="%s 2x" alt="" width="16" height="16"%s />',
esc_url( get_site_icon_url( 16 ) ),
esc_url( get_site_icon_url( 32 ) ),
( wp_lazy_loading_enabled( 'img', 'site_icon_in_toolbar' ) ? ' loading="lazy"' : '' )
);
restore_current_blog();
} else {
$blavatar = '<div class="blavatar"></div>';
}

$blogname = $blog->blogname;

if ( ! $blogname ) {
$blogname = preg_replace( '#^(https?://)?(www\.)?#', '', get_home_url() );
$blogname = preg_replace( '#^(https?://)?(www\.)?#', '', $siteurl );
}

$menu_id = 'blog-' . $blog->userblog_id;
$menu_id = 'blog-' . $blog_id;

if ( current_user_can( 'read' ) ) {
$wp_admin_bar->add_node(
array(
'parent' => 'my-sites-list',
'id' => $menu_id,
'title' => $blavatar . $blogname,
'href' => admin_url(),
)
);
$wp_admin_bar->add_node(
array(
'parent' => 'my-sites-list',
'id' => $menu_id,
'title' => $blavatar . esc_html( $blogname ),
'href' => $adminurl,
)
);

$wp_admin_bar->add_node(
array(
'parent' => $menu_id,
'id' => $menu_id . '-d',
'title' => __( 'Dashboard' ),
'href' => admin_url(),
)
);
} else {
$wp_admin_bar->add_node(
array(
'parent' => 'my-sites-list',
'id' => $menu_id,
'title' => $blavatar . $blogname,
'href' => home_url(),
)
);
}
$wp_admin_bar->add_node(
array(
'parent' => $menu_id,
'id' => $menu_id . '-d',
'title' => __( 'Dashboard' ),
'href' => $adminurl,
)
);

if ( current_user_can( get_post_type_object( 'post' )->cap->create_posts ) ) {
$wp_admin_bar->add_node(
array(
'parent' => $menu_id,
'id' => $menu_id . '-n',
'title' => get_post_type_object( 'post' )->labels->new_item,
'href' => admin_url( 'post-new.php' ),
)
);
}
$wp_admin_bar->add_node(
array(
'parent' => $menu_id,
'id' => $menu_id . '-n',
'title' => __( 'New Post' ),
'href' => $adminurl . '/post-new.php',
)
);

if ( current_user_can( 'edit_posts' ) ) {
$wp_admin_bar->add_node(
array(
'parent' => $menu_id,
'id' => $menu_id . '-c',
'title' => __( 'Manage Comments' ),
'href' => admin_url( 'edit-comments.php' ),
)
);
}
$wp_admin_bar->add_node(
array(
'parent' => $menu_id,
'id' => $menu_id . '-c',
'title' => __( 'Manage Comments' ),
'href' => $adminurl . '/edit-comments.php',
)
);

$wp_admin_bar->add_node(
array(
'parent' => $menu_id,
'id' => $menu_id . '-v',
'title' => __( 'Visit Site' ),
'href' => home_url( '/' ),
'href' => $homeurl,
)
);

restore_current_blog();
}
}

Expand Down
26 changes: 26 additions & 0 deletions src/wp-includes/css/admin-bar.css
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,32 @@ html:lang(he-il) .rtl #wpadminbar * {
box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.6);
}

/*
* My Sites scrollable dropdown.
*
* When the site list exceeds the viewport height, allow the menu to scroll.
* Fly-out submenus use position: fixed to escape the scroll container's
* overflow clipping. Coordinates are set via CSS custom properties from JS.
*
* @since 7.0.0
* @see https://core.trac.wordpress.org/ticket/15317
*/
@media screen and (min-width: 783px) {
#wpadminbar .ab-top-menu > li#wp-admin-bar-my-sites > .ab-sub-wrapper {
max-height: calc(100vh - var(--wp-admin--admin-bar--height, 32px));
overflow-y: auto;
}

/* Fly-out submenus: fixed position to escape the scroll container. */
#wpadminbar #wp-admin-bar-my-sites .ab-sub-wrapper .menupop > .ab-sub-wrapper {
position: fixed !important;
margin-left: 0 !important;
margin-top: 0 !important;
top: var(--msf-top, 0);
left: var(--msf-left, 0);
}
}

@media screen and (max-width: 782px) {
html {
--wp-admin--admin-bar--height: 46px;
Expand Down
Loading
Loading