-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Image: Add lightbox using directives. #50373
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
bd53de2
bd43bf6
e1dd737
8b84a22
d5dbc1c
037d669
8f62d34
62b6217
edfd7d1
8ee4065
594224b
fd6fe36
da4a5ad
2958fd1
0a68bea
fd08928
4e03e0d
92b6e6c
ffa881e
99c964b
7f7db2c
5e5ff9a
ad20b64
d16266b
0982d9f
0ef7b5e
e75fd87
f6414f9
31c0623
ee3701f
a0ac748
1dd6df1
a737200
3fd9270
5a9748c
9a6b1f2
1c7f6d5
470f74f
bba7c2d
2cb348c
c65bc57
1c655f7
697c04f
d4fd9d9
a57d634
234a8b6
4bef633
7aa7231
1d923ab
aee908a
33cb62d
55e37a3
d4d657b
8d6b0d0
76b2a75
70e51cd
e69833b
9ca2918
dca1b36
552bc3c
a46c35e
5bdddd5
3c8a458
c69f6ff
4802229
e7a7a4c
46b95d6
f5ee39e
5cc8eae
7009fa1
910706f
1ebe07b
10cdc07
d035dc4
aa267c0
43dfa46
58b5392
0d5ef70
1c0e406
926d503
68685f1
3811186
7840dfe
7f2cfe5
effd389
8c7adae
4558d29
6629975
409941e
457cbfe
7807fdf
0141d68
f8aa92f
5558e8d
3dc3bf9
99d95f9
53e1d47
518e94f
fb7af8b
ce9b0c0
84be707
899b11e
1ea0d58
3986e35
32dc7a8
48a62d2
ced26e1
528b60a
566460c
5efaa21
77e7fb8
d854c7e
6b75413
330f403
5556ad7
cdae7fe
9b1534d
3b89cef
e532fb1
cd0c9dd
429ebbe
95a89f7
7c9e0eb
8aebca8
6ce95ae
1eaffe1
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 |
|---|---|---|
|
|
@@ -14,6 +14,7 @@ | |
| * @return string Returns the block content with the data-id attribute added. | ||
| */ | ||
| function render_block_core_image( $attributes, $content ) { | ||
|
|
||
| $processor = new WP_HTML_Tag_Processor( $content ); | ||
| $processor->next_tag( 'img' ); | ||
|
|
||
|
|
@@ -27,16 +28,88 @@ function render_block_core_image( $attributes, $content ) { | |
| // which now wraps Image Blocks within innerBlocks. | ||
| // The data-id attribute is added in a core/gallery `render_block_data` hook. | ||
| $processor->set_attribute( 'data-id', $attributes['data-id'] ); | ||
| } | ||
|
|
||
| $link_destination = isset( $attributes['linkDestination'] ) ? $attributes['linkDestination'] : 'none'; | ||
|
|
||
| // Get the lightbox setting from the block attributes. | ||
| if ( isset( $attributes['behaviors']['lightbox'] ) ) { | ||
| $lightbox = $attributes['behaviors']['lightbox']; | ||
| // If the lightbox setting is not set in the block attributes, get it from the theme.json file. | ||
| } else { | ||
| $theme_data = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data()->get_data(); | ||
| if ( isset( $theme_data['behaviors']['blocks']['core/image']['lightbox'] ) ) { | ||
| $lightbox = $theme_data['behaviors']['blocks']['core/image']['lightbox']; | ||
| } else { | ||
| $lightbox = false; | ||
| } | ||
| } | ||
|
|
||
| $experiments = get_option( 'gutenberg-experiments' ); | ||
|
|
||
| if ( ! empty( $experiments['gutenberg-interactivity-api-core-blocks'] ) && 'none' === $link_destination && $lightbox ) { | ||
|
|
||
| $aria_label = 'Open image lightbox'; | ||
|
Contributor
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. Is there a reason for not making this text translatable?
Member
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. It definitely should be translated 👍 |
||
| if ( $processor->get_attribute( 'alt' ) ) { | ||
| $aria_label .= ' : ' . $processor->get_attribute( 'alt' ); | ||
| } | ||
| $content = $processor->get_updated_html(); | ||
|
|
||
| // Wrap the image in the body content with a button. | ||
| $img = null; | ||
| preg_match( '/<img[^>]+>/', $content, $img ); | ||
| $button = '<div class="img-container"> | ||
| <button aria-haspopup="dialog" aria-label="' . $aria_label . '" data-wp-on.click="actions.core.image.showLightbox"></button>' | ||
| . $img[0] . | ||
| '</div>'; | ||
| $body_content = preg_replace( '/<img[^>]+>/', $button, $content ); | ||
|
|
||
| // For the modal, set an ID on the image to be used for an aria-labelledby attribute. | ||
| $modal_content = new WP_HTML_Tag_Processor( $content ); | ||
| $modal_content->next_tag( 'img' ); | ||
| $image_lightbox_id = $modal_content->get_attribute( 'class' ) . '-lightbox'; | ||
| $modal_content->set_attribute( 'id', $image_lightbox_id ); | ||
| $modal_content = $modal_content->get_updated_html(); | ||
|
|
||
| $background_color = wp_get_global_styles( array( 'color', 'background' ) ); | ||
| $close_button_icon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="30" height="30" aria-hidden="true" focusable="false"><path d="M13 11.8l6.1-6.3-1-1-6.1 6.2-6.1-6.2-1 1 6.1 6.3-6.5 6.7 1 1 6.5-6.6 6.5 6.6 1-1z"></path></svg>'; | ||
|
|
||
| return | ||
| <<<HTML | ||
| <div class="wp-lightbox-container" | ||
| data-wp-island | ||
|
Contributor
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. Most of these data attributes are self explanatory, but this "island" part is not, I think this could be improved to be easier to read?
Member
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. This one is special and I run into an issue with it as I had to learn it enables Interactivity API runtime for the block.
Member
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. That's a good point. Island is a term that refers to a part of the site that is hydrated and therefore becomes interactive. The idea of an "islands architecture" was first coined by Etsy's frontend architect Katie Sylor-Miller in 2019, and expanded on in this post by Jason Miller (Preact's creator). Since then, multiple JavaScript frameworks have adopted it: (1, 2, 3...). Maybe we can change it to
Contributor
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. Yes I think
Member
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. Thanks, Carolina 🙂 I opened this issue to discuss it. For context, the full version of the Interactivity API is still in that repo, but we'll move everything to the Gutenberg repo after the WP 6.3 feature freeze, including the codebase and discussions. |
||
| data-wp-context='{ "core": { "image": { "initialized": false, "lightboxEnabled": false } } }'> | ||
| $body_content | ||
| <div data-wp-body="" class="wp-lightbox-overlay" | ||
| data-wp-bind.role="selectors.core.image.roleAttribute" | ||
| aria-labelledby="$image_lightbox_id" | ||
| data-wp-class.initialized="context.core.image.initialized" | ||
| data-wp-class.active="context.core.image.lightboxEnabled" | ||
| data-wp-bind.aria-hidden="!context.core.image.lightboxEnabled" | ||
| data-wp-bind.aria-modal="context.core.image.lightboxEnabled" | ||
| data-wp-effect="effects.core.image.initLightbox" | ||
| data-wp-on.keydown="actions.core.image.handleKeydown" | ||
| data-wp-on.mousewheel="actions.core.image.hideLightbox" | ||
| data-wp-on.click="actions.core.image.hideLightbox" | ||
| > | ||
luisherranz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| <button aria-label="Close lightbox" class="close-button" data-wp-on.click="actions.core.image.hideLightbox"> | ||
artemiomorales marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| $close_button_icon | ||
| </button> | ||
| $modal_content | ||
| <div class="scrim" style="background-color: $background_color"></div> | ||
| </div> | ||
| </div> | ||
| HTML; | ||
| } | ||
| return $content; | ||
| } | ||
|
|
||
| return $processor->get_updated_html(); | ||
| } | ||
|
|
||
| /** | ||
| * Registers the `core/image` block on server. | ||
| */ | ||
| function register_block_core_image() { | ||
|
|
||
| register_block_type_from_metadata( | ||
| __DIR__ . '/image', | ||
| array( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| /** | ||
| * Internal dependencies | ||
| */ | ||
| import { store } from '../utils/interactivity'; | ||
|
|
||
| const focusableSelectors = [ | ||
| 'a[href]', | ||
| 'area[href]', | ||
| 'input:not([disabled]):not([type="hidden"]):not([aria-hidden])', | ||
| 'select:not([disabled]):not([aria-hidden])', | ||
| 'textarea:not([disabled]):not([aria-hidden])', | ||
| 'button:not([disabled]):not([aria-hidden])', | ||
| 'iframe', | ||
| 'object', | ||
| 'embed', | ||
| '[contenteditable]', | ||
| '[tabindex]:not([tabindex^="-"])', | ||
| ]; | ||
|
|
||
| store( { | ||
| actions: { | ||
| core: { | ||
| image: { | ||
| showLightbox: ( { context } ) => { | ||
| context.core.image.initialized = true; | ||
| context.core.image.lightboxEnabled = true; | ||
| context.core.image.lastFocusedElement = | ||
| window.document.activeElement; | ||
| context.core.image.scrollPosition = window.scrollY; | ||
artemiomorales marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| document.documentElement.classList.add( | ||
| 'has-lightbox-open' | ||
| ); | ||
| }, | ||
| hideLightbox: async ( { context, event } ) => { | ||
| if ( context.core.image.lightboxEnabled ) { | ||
| // If scrolling, wait a moment before closing the lightbox. | ||
| if ( | ||
| event.type === 'mousewheel' && | ||
| Math.abs( | ||
| window.scrollY - | ||
| context.core.image.scrollPosition | ||
| ) < 5 | ||
| ) { | ||
| return; | ||
| } | ||
| document.documentElement.classList.remove( | ||
| 'has-lightbox-open' | ||
| ); | ||
|
|
||
| context.core.image.lightboxEnabled = false; | ||
| context.core.image.lastFocusedElement.focus(); | ||
| } | ||
| }, | ||
| handleKeydown: ( { context, actions, event } ) => { | ||
| if ( context.core.image.lightboxEnabled ) { | ||
| if ( event.key === 'Tab' || event.keyCode === 9 ) { | ||
| // If shift + tab it change the direction | ||
| if ( | ||
| event.shiftKey && | ||
| window.document.activeElement === | ||
| context.core.image.firstFocusableElement | ||
| ) { | ||
| event.preventDefault(); | ||
| context.core.image.lastFocusableElement.focus(); | ||
| } else if ( | ||
| ! event.shiftKey && | ||
| window.document.activeElement === | ||
| context.core.image.lastFocusableElement | ||
| ) { | ||
| event.preventDefault(); | ||
| context.core.image.firstFocusableElement.focus(); | ||
| } | ||
| } | ||
|
|
||
| if ( event.key === 'Escape' || event.keyCode === 27 ) { | ||
| actions.core.image.hideLightbox( { | ||
| context, | ||
| event, | ||
| } ); | ||
| } | ||
| } | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| selectors: { | ||
| core: { | ||
| image: { | ||
| roleAttribute: ( { context } ) => { | ||
| return context.core.image.lightboxEnabled ? 'dialog' : ''; | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| effects: { | ||
| core: { | ||
| image: { | ||
| initLightbox: async ( { context, ref } ) => { | ||
| if ( context.core.image.lightboxEnabled ) { | ||
| const focusableElements = | ||
| ref.querySelectorAll( focusableSelectors ); | ||
| context.core.image.firstFocusableElement = | ||
| focusableElements[ 0 ]; | ||
| context.core.image.lastFocusableElement = | ||
| focusableElements[ focusableElements.length - 1 ]; | ||
|
|
||
| ref.querySelector( '.close-button' ).focus(); | ||
| } | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| } ); | ||
Uh oh!
There was an error while loading. Please reload this page.