Skip to content
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

Position BlockToolbar below all of the selected block's descendants #62711

Merged
merged 10 commits into from
Aug 26, 2024

Conversation

noisysocks
Copy link
Member

@noisysocks noisysocks commented Jun 21, 2024

What, why, how

Fixes #40382.

Currently BlockToolbar positions itself around a block's bounds using element.getBoundingClientRect().

The problem with this is that it doesn't take into account nested elements that overflow the block. For example, Submenu blocks within a Navigation block will overflow the Navigation block when the user hovers over a menu item. This causes the toolbar to be positioned on top of the Submenu which results in a very unusable experience.

This PR is basically a refresh of @getdave's approach in #40625, though I've tried to make things a bit more generic.

Instead of using element.getBoundingClientRect() we recurse through element's descendants and compute the block's bounds by taking a union of the root rect and all descendent rects. Hidden elements are ignored. This results in a rect that corresponds to what the user sees of the block.

This logic is applied in two places: 1) BlockPopover where the Popover components anchor is computed; and 2) useBlockToolbarPopover which decides whether or not to set flip and shift on the Popover depending on whether the block is too close to the header.

Testing Instructions

  1. Install and activate Emptytheme, or use the Create Block Theme plugin to create a new empty theme.
  2. Go to Appearance → Editor and edit the default template.
  3. Insert a Navigation block into the first position in the header template part.
  4. Edit the Navigation block and add a submenu to one of the navigation items.
  5. Try to edit the submenu. The block toolbar should not obscure your cursor.

Also need to test that the block popover appears correctly for other blocks in other instances.

Screenshots or screencast

Before:

Kapture.2024-06-21.at.11.27.44.mp4

After:

Kapture.2024-06-21.at.11.26.02.mp4

@noisysocks noisysocks added [Type] Bug An existing feature does not function as intended General Interface Parts of the UI which don't fall neatly under other labels. [Block] Navigation Affects the Navigation Block labels Jun 21, 2024
@noisysocks noisysocks self-assigned this Jun 21, 2024
@noisysocks
Copy link
Member Author

noisysocks commented Jun 21, 2024

Changing anything to do with the popover usually carries a pretty high risk of unexpected positioning regressions so I wouldn't backport this to 6.6.

Copy link

github-actions bot commented Jun 21, 2024

Size Change: +262 B (+0.01%)

Total Size: 1.77 MB

Filename Size Change
build/block-editor/index.min.js 256 kB +262 B (+0.1%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 951 B
build/annotations/index.min.js 2.26 kB
build/api-fetch/index.min.js 2.31 kB
build/autop/index.min.js 2.12 kB
build/blob/index.min.js 579 B
build/block-directory/index.min.js 7.29 kB
build/block-directory/style-rtl.css 1.01 kB
build/block-directory/style.css 1.01 kB
build/block-editor/content-rtl.css 4.57 kB
build/block-editor/content.css 4.56 kB
build/block-editor/default-editor-styles-rtl.css 394 B
build/block-editor/default-editor-styles.css 394 B
build/block-editor/style-rtl.css 16.3 kB
build/block-editor/style.css 16.3 kB
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 90 B
build/block-library/blocks/archives/style.css 90 B
build/block-library/blocks/audio/editor-rtl.css 149 B
build/block-library/blocks/audio/editor.css 151 B
build/block-library/blocks/audio/style-rtl.css 132 B
build/block-library/blocks/audio/style.css 132 B
build/block-library/blocks/audio/theme-rtl.css 134 B
build/block-library/blocks/audio/theme.css 134 B
build/block-library/blocks/avatar/editor-rtl.css 115 B
build/block-library/blocks/avatar/editor.css 115 B
build/block-library/blocks/avatar/style-rtl.css 104 B
build/block-library/blocks/avatar/style.css 104 B
build/block-library/blocks/button/editor-rtl.css 310 B
build/block-library/blocks/button/editor.css 310 B
build/block-library/blocks/button/style-rtl.css 538 B
build/block-library/blocks/button/style.css 538 B
build/block-library/blocks/buttons/editor-rtl.css 336 B
build/block-library/blocks/buttons/editor.css 336 B
build/block-library/blocks/buttons/style-rtl.css 328 B
build/block-library/blocks/buttons/style.css 328 B
build/block-library/blocks/calendar/style-rtl.css 240 B
build/block-library/blocks/calendar/style.css 240 B
build/block-library/blocks/categories/editor-rtl.css 132 B
build/block-library/blocks/categories/editor.css 131 B
build/block-library/blocks/categories/style-rtl.css 152 B
build/block-library/blocks/categories/style.css 152 B
build/block-library/blocks/code/editor-rtl.css 53 B
build/block-library/blocks/code/editor.css 53 B
build/block-library/blocks/code/style-rtl.css 121 B
build/block-library/blocks/code/style.css 121 B
build/block-library/blocks/code/theme-rtl.css 122 B
build/block-library/blocks/code/theme.css 122 B
build/block-library/blocks/columns/editor-rtl.css 108 B
build/block-library/blocks/columns/editor.css 108 B
build/block-library/blocks/columns/style-rtl.css 420 B
build/block-library/blocks/columns/style.css 420 B
build/block-library/blocks/comment-author-avatar/editor-rtl.css 124 B
build/block-library/blocks/comment-author-avatar/editor.css 124 B
build/block-library/blocks/comment-author-name/style-rtl.css 72 B
build/block-library/blocks/comment-author-name/style.css 72 B
build/block-library/blocks/comment-content/style-rtl.css 120 B
build/block-library/blocks/comment-content/style.css 120 B
build/block-library/blocks/comment-date/style-rtl.css 65 B
build/block-library/blocks/comment-date/style.css 65 B
build/block-library/blocks/comment-template/style-rtl.css 200 B
build/block-library/blocks/comment-template/style.css 199 B
build/block-library/blocks/comments-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/comments-pagination-numbers/editor.css 121 B
build/block-library/blocks/comments-pagination/editor-rtl.css 221 B
build/block-library/blocks/comments-pagination/editor.css 211 B
build/block-library/blocks/comments-pagination/style-rtl.css 234 B
build/block-library/blocks/comments-pagination/style.css 231 B
build/block-library/blocks/comments-title/editor-rtl.css 75 B
build/block-library/blocks/comments-title/editor.css 75 B
build/block-library/blocks/comments/editor-rtl.css 832 B
build/block-library/blocks/comments/editor.css 832 B
build/block-library/blocks/comments/style-rtl.css 632 B
build/block-library/blocks/comments/style.css 631 B
build/block-library/blocks/cover/editor-rtl.css 668 B
build/block-library/blocks/cover/editor.css 669 B
build/block-library/blocks/cover/style-rtl.css 1.62 kB
build/block-library/blocks/cover/style.css 1.6 kB
build/block-library/blocks/details/editor-rtl.css 65 B
build/block-library/blocks/details/editor.css 65 B
build/block-library/blocks/details/style-rtl.css 86 B
build/block-library/blocks/details/style.css 86 B
build/block-library/blocks/embed/editor-rtl.css 331 B
build/block-library/blocks/embed/editor.css 331 B
build/block-library/blocks/embed/style-rtl.css 419 B
build/block-library/blocks/embed/style.css 419 B
build/block-library/blocks/embed/theme-rtl.css 133 B
build/block-library/blocks/embed/theme.css 133 B
build/block-library/blocks/file/editor-rtl.css 326 B
build/block-library/blocks/file/editor.css 326 B
build/block-library/blocks/file/style-rtl.css 278 B
build/block-library/blocks/file/style.css 279 B
build/block-library/blocks/file/view.min.js 324 B
build/block-library/blocks/footnotes/style-rtl.css 198 B
build/block-library/blocks/footnotes/style.css 197 B
build/block-library/blocks/form-input/editor-rtl.css 229 B
build/block-library/blocks/form-input/editor.css 229 B
build/block-library/blocks/form-input/style-rtl.css 342 B
build/block-library/blocks/form-input/style.css 342 B
build/block-library/blocks/form-submission-notification/editor-rtl.css 344 B
build/block-library/blocks/form-submission-notification/editor.css 341 B
build/block-library/blocks/form-submit-button/style-rtl.css 69 B
build/block-library/blocks/form-submit-button/style.css 69 B
build/block-library/blocks/form/view.min.js 470 B
build/block-library/blocks/freeform/editor-rtl.css 2.6 kB
build/block-library/blocks/freeform/editor.css 2.6 kB
build/block-library/blocks/gallery/editor-rtl.css 955 B
build/block-library/blocks/gallery/editor.css 958 B
build/block-library/blocks/gallery/style-rtl.css 1.83 kB
build/block-library/blocks/gallery/style.css 1.82 kB
build/block-library/blocks/gallery/theme-rtl.css 108 B
build/block-library/blocks/gallery/theme.css 108 B
build/block-library/blocks/group/editor-rtl.css 344 B
build/block-library/blocks/group/editor.css 344 B
build/block-library/blocks/group/style-rtl.css 103 B
build/block-library/blocks/group/style.css 103 B
build/block-library/blocks/group/theme-rtl.css 79 B
build/block-library/blocks/group/theme.css 79 B
build/block-library/blocks/heading/style-rtl.css 188 B
build/block-library/blocks/heading/style.css 188 B
build/block-library/blocks/html/editor-rtl.css 346 B
build/block-library/blocks/html/editor.css 347 B
build/block-library/blocks/image/editor-rtl.css 894 B
build/block-library/blocks/image/editor.css 892 B
build/block-library/blocks/image/style-rtl.css 1.59 kB
build/block-library/blocks/image/style.css 1.59 kB
build/block-library/blocks/image/theme-rtl.css 137 B
build/block-library/blocks/image/theme.css 137 B
build/block-library/blocks/image/view.min.js 1.65 kB
build/block-library/blocks/latest-comments/style-rtl.css 355 B
build/block-library/blocks/latest-comments/style.css 354 B
build/block-library/blocks/latest-posts/editor-rtl.css 179 B
build/block-library/blocks/latest-posts/editor.css 179 B
build/block-library/blocks/latest-posts/style-rtl.css 509 B
build/block-library/blocks/latest-posts/style.css 510 B
build/block-library/blocks/list/style-rtl.css 107 B
build/block-library/blocks/list/style.css 107 B
build/block-library/blocks/loginout/style-rtl.css 61 B
build/block-library/blocks/loginout/style.css 61 B
build/block-library/blocks/media-text/editor-rtl.css 304 B
build/block-library/blocks/media-text/editor.css 303 B
build/block-library/blocks/media-text/style-rtl.css 516 B
build/block-library/blocks/media-text/style.css 515 B
build/block-library/blocks/more/editor-rtl.css 427 B
build/block-library/blocks/more/editor.css 427 B
build/block-library/blocks/navigation-link/editor-rtl.css 644 B
build/block-library/blocks/navigation-link/editor.css 645 B
build/block-library/blocks/navigation-link/style-rtl.css 192 B
build/block-library/blocks/navigation-link/style.css 191 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 295 B
build/block-library/blocks/navigation-submenu/editor.css 294 B
build/block-library/blocks/navigation/editor-rtl.css 2.2 kB
build/block-library/blocks/navigation/editor.css 2.2 kB
build/block-library/blocks/navigation/style-rtl.css 2.25 kB
build/block-library/blocks/navigation/style.css 2.23 kB
build/block-library/blocks/navigation/view.min.js 1.03 kB
build/block-library/blocks/nextpage/editor-rtl.css 392 B
build/block-library/blocks/nextpage/editor.css 392 B
build/block-library/blocks/page-list/editor-rtl.css 378 B
build/block-library/blocks/page-list/editor.css 378 B
build/block-library/blocks/page-list/style-rtl.css 175 B
build/block-library/blocks/page-list/style.css 175 B
build/block-library/blocks/paragraph/editor-rtl.css 236 B
build/block-library/blocks/paragraph/editor.css 236 B
build/block-library/blocks/paragraph/style-rtl.css 341 B
build/block-library/blocks/paragraph/style.css 340 B
build/block-library/blocks/post-author-biography/style-rtl.css 74 B
build/block-library/blocks/post-author-biography/style.css 74 B
build/block-library/blocks/post-author-name/style-rtl.css 69 B
build/block-library/blocks/post-author-name/style.css 69 B
build/block-library/blocks/post-author/editor-rtl.css 107 B
build/block-library/blocks/post-author/editor.css 107 B
build/block-library/blocks/post-author/style-rtl.css 188 B
build/block-library/blocks/post-author/style.css 189 B
build/block-library/blocks/post-comments-form/editor-rtl.css 96 B
build/block-library/blocks/post-comments-form/editor.css 96 B
build/block-library/blocks/post-comments-form/style-rtl.css 527 B
build/block-library/blocks/post-comments-form/style.css 528 B
build/block-library/blocks/post-content/editor-rtl.css 74 B
build/block-library/blocks/post-content/editor.css 74 B
build/block-library/blocks/post-content/style-rtl.css 79 B
build/block-library/blocks/post-content/style.css 79 B
build/block-library/blocks/post-date/style-rtl.css 62 B
build/block-library/blocks/post-date/style.css 62 B
build/block-library/blocks/post-excerpt/editor-rtl.css 71 B
build/block-library/blocks/post-excerpt/editor.css 71 B
build/block-library/blocks/post-excerpt/style-rtl.css 155 B
build/block-library/blocks/post-excerpt/style.css 155 B
build/block-library/blocks/post-featured-image/editor-rtl.css 729 B
build/block-library/blocks/post-featured-image/editor.css 726 B
build/block-library/blocks/post-featured-image/style-rtl.css 347 B
build/block-library/blocks/post-featured-image/style.css 347 B
build/block-library/blocks/post-navigation-link/style-rtl.css 215 B
build/block-library/blocks/post-navigation-link/style.css 214 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 399 B
build/block-library/blocks/post-template/style.css 398 B
build/block-library/blocks/post-terms/style-rtl.css 96 B
build/block-library/blocks/post-terms/style.css 96 B
build/block-library/blocks/post-time-to-read/style-rtl.css 70 B
build/block-library/blocks/post-time-to-read/style.css 70 B
build/block-library/blocks/post-title/style-rtl.css 100 B
build/block-library/blocks/post-title/style.css 100 B
build/block-library/blocks/preformatted/style-rtl.css 125 B
build/block-library/blocks/preformatted/style.css 125 B
build/block-library/blocks/pullquote/editor-rtl.css 134 B
build/block-library/blocks/pullquote/editor.css 134 B
build/block-library/blocks/pullquote/style-rtl.css 342 B
build/block-library/blocks/pullquote/style.css 342 B
build/block-library/blocks/pullquote/theme-rtl.css 167 B
build/block-library/blocks/pullquote/theme.css 167 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 121 B
build/block-library/blocks/query-pagination-numbers/editor.css 118 B
build/block-library/blocks/query-pagination/editor-rtl.css 220 B
build/block-library/blocks/query-pagination/editor.css 208 B
build/block-library/blocks/query-pagination/style-rtl.css 287 B
build/block-library/blocks/query-pagination/style.css 283 B
build/block-library/blocks/query-title/style-rtl.css 64 B
build/block-library/blocks/query-title/style.css 64 B
build/block-library/blocks/query/editor-rtl.css 452 B
build/block-library/blocks/query/editor.css 451 B
build/block-library/blocks/query/view.min.js 958 B
build/block-library/blocks/quote/style-rtl.css 238 B
build/block-library/blocks/quote/style.css 238 B
build/block-library/blocks/quote/theme-rtl.css 233 B
build/block-library/blocks/quote/theme.css 236 B
build/block-library/blocks/read-more/style-rtl.css 138 B
build/block-library/blocks/read-more/style.css 138 B
build/block-library/blocks/rss/editor-rtl.css 101 B
build/block-library/blocks/rss/editor.css 101 B
build/block-library/blocks/rss/style-rtl.css 288 B
build/block-library/blocks/rss/style.css 287 B
build/block-library/blocks/search/editor-rtl.css 199 B
build/block-library/blocks/search/editor.css 199 B
build/block-library/blocks/search/style-rtl.css 672 B
build/block-library/blocks/search/style.css 671 B
build/block-library/blocks/search/theme-rtl.css 113 B
build/block-library/blocks/search/theme.css 113 B
build/block-library/blocks/search/view.min.js 475 B
build/block-library/blocks/separator/editor-rtl.css 100 B
build/block-library/blocks/separator/editor.css 100 B
build/block-library/blocks/separator/style-rtl.css 248 B
build/block-library/blocks/separator/style.css 248 B
build/block-library/blocks/separator/theme-rtl.css 195 B
build/block-library/blocks/separator/theme.css 195 B
build/block-library/blocks/shortcode/editor-rtl.css 286 B
build/block-library/blocks/shortcode/editor.css 286 B
build/block-library/blocks/site-logo/editor-rtl.css 806 B
build/block-library/blocks/site-logo/editor.css 803 B
build/block-library/blocks/site-logo/style-rtl.css 218 B
build/block-library/blocks/site-logo/style.css 218 B
build/block-library/blocks/site-tagline/editor-rtl.css 87 B
build/block-library/blocks/site-tagline/editor.css 87 B
build/block-library/blocks/site-tagline/style-rtl.css 65 B
build/block-library/blocks/site-tagline/style.css 65 B
build/block-library/blocks/site-title/editor-rtl.css 123 B
build/block-library/blocks/site-title/editor.css 123 B
build/block-library/blocks/site-title/style-rtl.css 90 B
build/block-library/blocks/site-title/style.css 90 B
build/block-library/blocks/social-link/editor-rtl.css 338 B
build/block-library/blocks/social-link/editor.css 338 B
build/block-library/blocks/social-links/editor-rtl.css 676 B
build/block-library/blocks/social-links/editor.css 675 B
build/block-library/blocks/social-links/style-rtl.css 1.51 kB
build/block-library/blocks/social-links/style.css 1.5 kB
build/block-library/blocks/spacer/editor-rtl.css 346 B
build/block-library/blocks/spacer/editor.css 346 B
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table-of-contents/style-rtl.css 83 B
build/block-library/blocks/table-of-contents/style.css 83 B
build/block-library/blocks/table/editor-rtl.css 394 B
build/block-library/blocks/table/editor.css 394 B
build/block-library/blocks/table/style-rtl.css 640 B
build/block-library/blocks/table/style.css 639 B
build/block-library/blocks/table/theme-rtl.css 152 B
build/block-library/blocks/table/theme.css 152 B
build/block-library/blocks/tag-cloud/editor-rtl.css 144 B
build/block-library/blocks/tag-cloud/editor.css 144 B
build/block-library/blocks/tag-cloud/style-rtl.css 266 B
build/block-library/blocks/tag-cloud/style.css 265 B
build/block-library/blocks/template-part/editor-rtl.css 368 B
build/block-library/blocks/template-part/editor.css 368 B
build/block-library/blocks/template-part/theme-rtl.css 113 B
build/block-library/blocks/template-part/theme.css 113 B
build/block-library/blocks/term-description/style-rtl.css 126 B
build/block-library/blocks/term-description/style.css 126 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/style-rtl.css 165 B
build/block-library/blocks/text-columns/style.css 165 B
build/block-library/blocks/verse/style-rtl.css 98 B
build/block-library/blocks/verse/style.css 98 B
build/block-library/blocks/video/editor-rtl.css 541 B
build/block-library/blocks/video/editor.css 542 B
build/block-library/blocks/video/style-rtl.css 192 B
build/block-library/blocks/video/style.css 192 B
build/block-library/blocks/video/theme-rtl.css 134 B
build/block-library/blocks/video/theme.css 134 B
build/block-library/classic-rtl.css 179 B
build/block-library/classic.css 179 B
build/block-library/common-rtl.css 1.1 kB
build/block-library/common.css 1.1 kB
build/block-library/editor-elements-rtl.css 75 B
build/block-library/editor-elements.css 75 B
build/block-library/editor-rtl.css 11.9 kB
build/block-library/editor.css 11.9 kB
build/block-library/elements-rtl.css 54 B
build/block-library/elements.css 54 B
build/block-library/index.min.js 217 kB
build/block-library/reset-rtl.css 472 B
build/block-library/reset.css 472 B
build/block-library/style-rtl.css 14.8 kB
build/block-library/style.css 14.8 kB
build/block-library/theme-rtl.css 708 B
build/block-library/theme.css 712 B
build/block-serialization-default-parser/index.min.js 1.12 kB
build/block-serialization-spec-parser/index.min.js 2.87 kB
build/blocks/index.min.js 52.4 kB
build/commands/index.min.js 16.1 kB
build/commands/style-rtl.css 955 B
build/commands/style.css 952 B
build/components/index.min.js 224 kB
build/components/style-rtl.css 12.1 kB
build/components/style.css 12.1 kB
build/compose/index.min.js 12.9 kB
build/core-commands/index.min.js 2.82 kB
build/core-data/index.min.js 73.1 kB
build/customize-widgets/index.min.js 11 kB
build/customize-widgets/style-rtl.css 1.35 kB
build/customize-widgets/style.css 1.35 kB
build/data-controls/index.min.js 641 B
build/data/index.min.js 8.98 kB
build/date/index.min.js 18 kB
build/deprecated/index.min.js 458 B
build/dom-ready/index.min.js 325 B
build/dom/index.min.js 4.65 kB
build/edit-post/classic-rtl.css 578 B
build/edit-post/classic.css 580 B
build/edit-post/index.min.js 12.7 kB
build/edit-post/style-rtl.css 2.31 kB
build/edit-post/style.css 2.31 kB
build/edit-site/index.min.js 217 kB
build/edit-site/posts-rtl.css 7.32 kB
build/edit-site/posts.css 7.32 kB
build/edit-site/style-rtl.css 12.6 kB
build/edit-site/style.css 12.6 kB
build/edit-widgets/index.min.js 17.7 kB
build/edit-widgets/style-rtl.css 4.2 kB
build/edit-widgets/style.css 4.2 kB
build/editor/index.min.js 101 kB
build/editor/style-rtl.css 9.28 kB
build/editor/style.css 9.28 kB
build/element/index.min.js 4.83 kB
build/escape-html/index.min.js 537 B
build/format-library/index.min.js 8.09 kB
build/format-library/style-rtl.css 476 B
build/format-library/style.css 476 B
build/hooks/index.min.js 1.54 kB
build/html-entities/index.min.js 445 B
build/i18n/index.min.js 3.58 kB
build/interactivity/debug.min.js 16.3 kB
build/interactivity/file.min.js 447 B
build/interactivity/image.min.js 1.78 kB
build/interactivity/index.min.js 13.2 kB
build/interactivity/navigation.min.js 1.16 kB
build/interactivity/query.min.js 742 B
build/interactivity/router.min.js 2.8 kB
build/interactivity/search.min.js 615 B
build/is-shallow-equal/index.min.js 526 B
build/keyboard-shortcuts/index.min.js 1.31 kB
build/keycodes/index.min.js 1.46 kB
build/list-reusable-blocks/index.min.js 2.16 kB
build/list-reusable-blocks/style-rtl.css 846 B
build/list-reusable-blocks/style.css 846 B
build/media-utils/index.min.js 2.92 kB
build/modules/importmap-polyfill.min.js 12.3 kB
build/notices/index.min.js 946 B
build/nux/index.min.js 1.59 kB
build/nux/style-rtl.css 749 B
build/nux/style.css 745 B
build/patterns/index.min.js 7.34 kB
build/patterns/style-rtl.css 687 B
build/patterns/style.css 685 B
build/plugins/index.min.js 1.81 kB
build/preferences-persistence/index.min.js 2.06 kB
build/preferences/index.min.js 2.9 kB
build/preferences/style-rtl.css 554 B
build/preferences/style.css 554 B
build/primitives/index.min.js 829 B
build/priority-queue/index.min.js 1.54 kB
build/private-apis/index.min.js 1.01 kB
build/react-i18n/index.min.js 630 B
build/react-refresh-entry/index.min.js 9.47 kB
build/react-refresh-runtime/index.min.js 6.76 kB
build/redux-routine/index.min.js 2.69 kB
build/reusable-blocks/index.min.js 2.55 kB
build/reusable-blocks/style-rtl.css 256 B
build/reusable-blocks/style.css 256 B
build/rich-text/index.min.js 10.1 kB
build/router/index.min.js 1.96 kB
build/server-side-render/index.min.js 1.94 kB
build/shortcode/index.min.js 1.4 kB
build/style-engine/index.min.js 2.04 kB
build/token-list/index.min.js 581 B
build/url/index.min.js 3.85 kB
build/vendors/react-dom.min.js 41.7 kB
build/vendors/react-jsx-runtime.min.js 560 B
build/vendors/react.min.js 4.02 kB
build/viewport/index.min.js 965 B
build/warning/index.min.js 250 B
build/widgets/index.min.js 7.2 kB
build/widgets/style-rtl.css 1.16 kB
build/widgets/style.css 1.16 kB
build/wordcount/index.min.js 1.03 kB

compressed-size-action

@noisysocks noisysocks requested a review from getdave June 21, 2024 02:09
@noisysocks noisysocks force-pushed the fix/block-toolbar-positioning branch from a009ab1 to 69e3b3c Compare June 21, 2024 04:38
Copy link
Contributor

@tellthemachines tellthemachines left a comment

Choose a reason for hiding this comment

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

This is looking pretty good!

Before:
Screenshot 2024-06-21 at 3 03 41 PM

After:
Screenshot 2024-06-21 at 3 18 09 PM

One thing that feels a bit unexpected is the toolbar doesn't disappear when scrolling down the page:
Screenshot 2024-06-21 at 3 19 01 PM

I can reproduce it with several blocks in this PR, so something might need tweaking here.

@noisysocks
Copy link
Member Author

One thing that feels a bit unexpected is the toolbar doesn't disappear when scrolling down the page:

Good catch! My isElementVisible function was too aggressive and filtered out children that weren't on the viewport because of scrolling. Should be fixed now.

@tellthemachines
Copy link
Contributor

Hmm, I'm still seeing the toolbar moving along the page when scrolling:

moving-toolbar.mov

@noisysocks
Copy link
Member Author

noisysocks commented Jun 24, 2024

Forgot to ping @jasmussen 🙂 this all came from his suggestion in #40382 (comment).

Oops, thanks @tellthemachines. I think I misread your initial comment and fixed something different. Will look into this later.

@jasmussen
Copy link
Contributor

Thanks for working on this!

At a glance this seems a solid improvement, with no side-effects I can see. Can you think of any potential side-effects/downsides?

@noisysocks noisysocks force-pushed the fix/block-toolbar-positioning branch from bceca61 to e05a055 Compare August 19, 2024 03:55
Copy link

Flaky tests detected in e05a055.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/10446855219
📝 Reported issues:

@noisysocks
Copy link
Member Author

noisysocks commented Aug 19, 2024

I've rebased this and (I think) fixed the bug @tellthemachines noticed. @ramonjd reckon you could test? ❤️

@ramonjd
Copy link
Member

ramonjd commented Aug 19, 2024

Scroll issue looks to be resolved. Good one!

Kapture.2024-08-19.at.18.43.25.mp4

On thing I noticed is that the toolbar align itself to the right - from what I can tell it's the inline translateX value of the block-editor-block-list__block-popover element.

Kapture.2024-08-19.at.18.41.34.mp4

It appears to affect dir="rtl" locales differently in that the toolbar for nav items are positioned to the absolute left (in trunk they're underneath the block)

Trunk This PR
Screenshot 2024-08-19 at 6 51 14 PM Screenshot 2024-08-19 at 6 51 29 PM

Copy link
Member

@ramonjd ramonjd left a comment

Choose a reason for hiding this comment

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

This PR is definitely an improvement on the current status. Good stuff.

The positioning would be a good follow up if it can't be done neatly here. I think it's expected, and easier to access the controls, when the toolbar positions itself near the selected block.

Comment on lines 69 to 70
const left = Math.min( rect1.left, rect2.left );
const top = Math.min( rect1.top, rect2.top );
Copy link
Member

Choose a reason for hiding this comment

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

On thing I noticed is that the toolbar align itself to the right - from what I can tell it's the inline translateX value of the block-editor-block-list__block-popover element.

Could be due to negative values being misinterpreted by the Popover component.

If I pass 0 in the case of negative values — left = left < 0 ? 0 : left — things look okay 🤔

Kapture.2024-08-19.at.19.04.36.mp4

I'm not 100% on this. Smells like a Popover thing.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm. Can you share the markup you have in the template?

Copy link
Member

Choose a reason for hiding this comment

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

Nothing out of the ordinary I think. Here's the template:

<!-- wp:template-part {"slug":"header","theme":"emptytheme","tagName":"header"} /-->

<!-- wp:query {"queryId":1,"query":{"offset":0,"postType":"post","order":"desc","orderBy":"date","author":"","search":"","sticky":"","perPage":10}} -->
<div class="wp-block-query"><!-- wp:post-template -->
<!-- wp:post-title {"isLink":true} /-->

<!-- wp:post-excerpt /-->
<!-- /wp:post-template --></div>
<!-- /wp:query -->

And the header template part:

<!-- wp:navigation {"ref":6} /-->

<!-- wp:site-title /-->

<!-- wp:site-tagline /-->

Does that help? 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm there is probably some hidden element that makes getVisibleElementBounds return a bounds that's larger than it should be, but I'm having a hard time reproducing. Could you export your theme? Or come up with steps to reproduce that begin with installing Emptytheme?

Copy link
Member Author

@noisysocks noisysocks Aug 20, 2024

Choose a reason for hiding this comment

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

Try the latest commit I just pushed @ramonjd. I updated our check for hidden elements to explicitly check for <VisuallyHidden> which I think is causing problems because it hides its content using clip which is hard to detect.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks @aaronrobertshaw

If I get time, I might play around with disallowing negative values. I'm not super confident about that approach but hacking never hurt anyone, except maybe some major corporations, millions of consumers and certain small democracies.

Copy link
Member

@ramonjd ramonjd Aug 22, 2024

Choose a reason for hiding this comment

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

I'm not sure where the calculations are happening, but a console.trace confirms that that the -1 value is running through these new functions

{left: -1, top: 0, width: 1781, height: 63}
rectUnion	@	dom.js:73
getVisibleElementBounds	@	dom.js:142
getBoundingClientRect	@	index.js:94

But after looking in the wrong place for ages, from what I can tell, it's because the selected submenu has a left value of -1px in its editor styles

left: -1px; // Border width.

Remove these, and it's kosher.

2024-08-22.12.27.22.mp4

So, I suppose there are two choices as far as I can tell:

  1. Remove the -1px rules on the submenu (the quickest fix). I tested the bug it was trying to fix and I can't reproduce with the values removed, but it doesn't address the primary cause.

  2. "Normalize" negative values in rectUnion (e.g.,round up to zero in the case of x) to calculate the visible element bounds. On the surface this makes the most sense since we're only interested in what's visible in the viewport.

Copy link
Member

Choose a reason for hiding this comment

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

"Normalize" negative values in rectUnion (e.g.,round up to zero in the case of x) to calculate the visible element bounds. On the surface this makes the most sense since we're only interested in what's visible in the viewport.

This is what I was thinking
diff --git a/packages/block-editor/src/utils/dom.js b/packages/block-editor/src/utils/dom.js
index eef322959a..2504c7824b 100644
--- a/packages/block-editor/src/utils/dom.js
+++ b/packages/block-editor/src/utils/dom.js
@@ -65,12 +65,25 @@ export function getBlockClientId( node ) {
  * @param {DOMRect} rect2 Second rectangle.
  * @return {DOMRect} Union of the two rectangles.
  */
-export function rectUnion( rect1, rect2 ) {
-	const left = Math.min( rect1.left, rect2.left );
-	const top = Math.min( rect1.top, rect2.top );
-	const right = Math.max( rect1.right, rect2.right );
-	const bottom = Math.max( rect1.bottom, rect2.bottom );
-	return new window.DOMRect( left, top, right - left, bottom - top );
+export function rectUnion( rect1, rect2, viewportRect ) {
+	let left = Math.min( rect1.left, rect2.left );
+	let top = Math.min( rect1.top, rect2.top );
+	let right = Math.max( rect1.right, rect2.right );
+	let bottom = Math.max( rect1.bottom, rect2.bottom );
+
+	if ( viewportRect ) {
+		left = Math.max( left, viewportRect.left );
+		top = Math.max( top, viewportRect.top );
+		right = Math.min( right, viewportRect.right );
+		bottom = Math.min( bottom, viewportRect.bottom );
+	}
+
+	return new window.DOMRect(
+		left,
+		top,
+		right - left,
+		bottom - top
+	);
 }
 
 /**
@@ -130,7 +143,12 @@ export function getVisibleElementBounds( element ) {
 	}
 
 	let bounds = element.getBoundingClientRect();
-
+	const viewportRect = {
+		top: 0,
+		right: viewport.innerWidth,
+		bottom: viewport.innerHeight,
+		left: 0,
+	};
 	const stack = [ element ];
 	let currentElement;
 
@@ -138,7 +156,7 @@ export function getVisibleElementBounds( element ) {
 		for ( const child of currentElement.children ) {
 			if ( isElementVisible( child ) ) {
 				const childBounds = child.getBoundingClientRect();
-				bounds = rectUnion( bounds, childBounds );
+				bounds = rectUnion( bounds, childBounds, viewportRect );
 				stack.push( child );
 			}
 		}

Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

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

Remove the -1px rules on the submenu

Without looking, I assume this is just to prevent the appearance of a double border along the window edge. That sounds like something we'd visually want to keep.

"Normalize" negative values in rectUnion

In the absence of the first option. This seems like the winner by default 🤷

Thoughts?

Looks viable to me. There's a lot of Math.min/max going on, would a quick inline comment help our future selves grok why the viewport rect is used etc?

Copy link
Member

Choose a reason for hiding this comment

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

would a quick inline comment help our future selves grok why the viewport rect is used etc?

Definitely. Thanks for the sanity check. I'll commit this change. Appears to work as intended.

packages/block-editor/src/utils/dom.js Outdated Show resolved Hide resolved
Copy link
Contributor

@aaronrobertshaw aaronrobertshaw left a comment

Choose a reason for hiding this comment

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

I've taken this for a quick spin and in general it's testing pretty well for me, with the exception of the misaligned toolbar already under discussion.

I also did some smoke testing of other components based off the BlockPopover such as spacing visualizers and resizable cover popover. Didn't spot any issues using this PR, other than gremlins in the visualizers that already exist on trunk.

@ramonjd ramonjd force-pushed the fix/block-toolbar-positioning branch from ae2d23d to d712323 Compare August 22, 2024 04:50
* this calculation function is called
* multiple times with the same values.
*/
const rectUnionCache = new Map();
Copy link
Member

@ramonjd ramonjd Aug 22, 2024

Choose a reason for hiding this comment

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

This is necessary (for now) because, if not cached, rectUnion() is called a few times every second.

On a page left untouched with a popover open, the call count can potentially reach n.

This cache reduces the times the internal logic of rectUnion runs dramatically.

Most of the functions up the chain that use this function are written with useCallback and useMemo, but it indicates that the Popover component is updating all these values using requestAnimationFrame.

Presumably indirectly, via useFloating and/or autoUpdate from floating-ui/@floating-ui/react-dom

autoUpdate( referenceParam, floatingParam, updateParam, {

Copy link
Member

Choose a reason for hiding this comment

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

One possible problem would be that this cache is never cleared. So a long-running page with multiple popovers might cause memory leaks.

What if we use memoization rather than a global cache? memize might be helpful here. We can also create and clear the memoization for each instance. This should also help us avoid the need to generate a cache key string using JSON.stringify.

Copy link
Member

Choose a reason for hiding this comment

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

Great idea, thanks for the feedback

Copy link
Member

Choose a reason for hiding this comment

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

I tried a few rounds with memoizing the function, and it made things a little janky when scrolling the page, even with a maxSize to limit the cache size.

So I've taken out the optimization for now. It wasn't in response to any perceived performance issue, only a defensive tactic.

Copy link
Member

Choose a reason for hiding this comment

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

Is it possible that you're using the DOMRect as the input parameter which cannot be memoized? I think the simplest way is to just flatten the input parameters and use primitives like so:

rectUnion(leftX, leftY, leftWidth, leftHeight, rightX, rightY, rightWidth, rightHeight)

It gets a little verbose but then they're all primitives which can be memoized accordingly.

That said, we don't need to do this at all if this is not the bottleneck until proven otherwise! 👍

Copy link
Member

@ramonjd ramonjd Aug 23, 2024

Choose a reason for hiding this comment

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

we don't need to do this at all if this is not the bottleneck until proven otherwise

yeah, there's no demonstrated bottleneck, I was just surprised that this function was running 100,000s of times in a short period of time

Copy link
Member Author

Choose a reason for hiding this comment

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

Caching comes with its own performance concerns 😀 So long as isElementVisible is fast it should be okay. I ordered the checks in order of most performant to least performant.

@ramonjd
Copy link
Member

ramonjd commented Aug 22, 2024

I've added a commit that trims the return left/right values of rectUnion if they fall beyond the left and right bounds of a given container, in our case the window.

This logic applies only in getVisibleElementBounds because presumably, we only interested in the "visible" bounds.

Anyway, it works it seems.

Without it, the popover might not position itself correctly if the selected element's right or left values are offscreen. See #62711 (comment)

LTR

Kapture.2024-08-22.at.14.44.50.mp4
Kapture.2024-08-22.at.14.45.40.mp4

RTL

Kapture.2024-08-22.at.15.05.00.mp4
Kapture.2024-08-22.at.14.46.44.mp4

Copy link
Member

@kevin940726 kevin940726 left a comment

Choose a reason for hiding this comment

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

This certainly is an interesting problem 😅. I left some suggestions but they are not blocking, just throwing out some potential ideas.

packages/block-editor/src/utils/dom.js Show resolved Hide resolved
packages/block-editor/src/utils/dom.js Show resolved Hide resolved
* this calculation function is called
* multiple times with the same values.
*/
const rectUnionCache = new Map();
Copy link
Member

Choose a reason for hiding this comment

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

One possible problem would be that this cache is never cleared. So a long-running page with multiple popovers might cause memory leaks.

What if we use memoization rather than a global cache? memize might be helpful here. We can also create and clear the memoization for each instance. This should also help us avoid the need to generate a cache key string using JSON.stringify.

@ramonjd
Copy link
Member

ramonjd commented Aug 22, 2024

One possible problem would be that this cache is never cleared. So a long-running page with multiple popovers might cause memory leaks.
What if we use memoization rather than a global cache?

Very good point. I'll look into memoization. 👍🏻

Copy link
Member

@kevin940726 kevin940726 left a comment

Choose a reason for hiding this comment

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

LGTM 👍. Thanks! 💯

return false;
}

const bounds = element.getBoundingClientRect();
Copy link
Member

Choose a reason for hiding this comment

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

Do we still need this if we're using checkVisibility?

Copy link
Member

Choose a reason for hiding this comment

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

Good question.

I believe so because this tests for elements that would otherwise be visible if not for the 0 dimensions.

E.g., for <div id="test" style="width:0;height:0;">Test</div>, checkVisibility( ...options ) returns true.

https://drafts.csswg.org/cssom-view-1/#dom-element-checkvisibility

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure this is supported in all the browsers that we support. I keep getting errors when using Safari.

Copy link
Contributor

@getdave getdave Sep 4, 2024

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Based on the browserslist config we support, we support the last 2 versions of Safari (17.5, 17.4). Both versions support checkVisibility according to caniuse.

Maybe the config we use is too loose if this is unexpected?

Copy link
Contributor

Choose a reason for hiding this comment

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

Safari only updates with MacOS updates for me. (stable MacOS) and I'm still on 17.3 for this reason. I think we might want to update our Safari browser list config to 3 or 4 versions because of this.

Copy link
Contributor

Choose a reason for hiding this comment

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

Good catch — yeah, I think we should handle calling this optionally. Even if we officially support the latest two Safari versions, we shouldn't be in a situation where we're breaking the editor in older Safari versions. Here's what I'm seeing when I select a block in the post editor in Safari 16.4.1:

image

I've put up a quick fix PR here: #65069

noisysocks and others added 10 commits August 26, 2024 09:27
…ter limits of the container in which an element is supposed to be "visible"

For example, if an element is positioned -10px to the left of the window x value (0), we should discount the negative overhang because it's not visible and therefore to be counted in the visible calculations.

Updated comments
@ramonjd ramonjd force-pushed the fix/block-toolbar-positioning branch from 591c2e7 to 675b3a6 Compare August 25, 2024 23:28
@ramonjd ramonjd enabled auto-merge (squash) August 25, 2024 23:41
@ramonjd ramonjd merged commit dec82e2 into trunk Aug 26, 2024
60 of 61 checks passed
@ramonjd ramonjd deleted the fix/block-toolbar-positioning branch August 26, 2024 00:03
@github-actions github-actions bot added this to the Gutenberg 19.2 milestone Aug 26, 2024
Comment on lines +77 to +91
/*
* To calculate visible bounds using rectUnion, take into account the outer
* horizontal limits of the container in which an element is supposed to be "visible".
* For example, if an element is positioned -10px to the left of the window x value (0),
* this function discounts the negative overhang because it's not visible and
* therefore not to be counted in the visibility calculations.
* Top and bottom values are not accounted for to accommodate vertical scroll.
*/
if ( containerRect ) {
left = Math.max( left, containerRect.left );
right = Math.min( right, containerRect.right );
}

return new window.DOMRect( left, top, right - left, bottom - top );
}
Copy link
Member Author

Choose a reason for hiding this comment

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

The approach makes sense but I don't really think this belongs in rectUnion. In a previous commit we had rectIntersect which we could bring back.

Copy link
Member Author

Choose a reason for hiding this comment

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

@noisysocks
Copy link
Member Author

Thanks for landing this @ramonjd! ❤️

@annezazu
Copy link
Contributor

Absolutely love to see this. Huge usability improvement :)

bph pushed a commit to bph/gutenberg that referenced this pull request Aug 31, 2024
…ordPress#62711)

* Position BlockToolbar below all of the selected block's descendants

* Fix scrolling

* Don't use window global

* Explain what capturingClientId is

* No need to clip bounds to viewport

* Use explicit check for VisuallyHidden

* To calculate visible bounds using rectUnion, take into account the outer limits of the container in which an element is supposed to be "visible"
For example, if an element is positioned -10px to the left of the window x value (0), we should discount the negative overhang because it's not visible and therefore to be counted in the visible calculations.

* switch to checkVisibility DOM method

---------

Co-authored-by: noisysocks <noisysocks@git.wordpress.org>
Co-authored-by: ramonopoly <ramonopoly@git.wordpress.org>
Co-authored-by: kevin940726 <kevin940726@git.wordpress.org>
Co-authored-by: aaronrobertshaw <aaronrobertshaw@git.wordpress.org>
@t-hamano
Copy link
Contributor

Due to this PR, it seems that when a block is scrollable, the block toolbar position moves with it. Could you help me find out the cause of this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Block] Navigation Affects the Navigation Block General Interface Parts of the UI which don't fall neatly under other labels. [Type] Bug An existing feature does not function as intended
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Navigation Block: Toolbar overlaps with submenu links when opening sidebar or using smaller viewport