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

Image: Add lightbox using directives. #50373

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
126 commits
Select commit Hold shift + click to select a range
bd53de2
Add behaviors to the core theme.json
michalczaplinski Apr 21, 2023
bd43bf6
Add behaviors to json schemas for theme.json
michalczaplinski Apr 21, 2023
e1dd737
Add a behaviors panel
michalczaplinski May 3, 2023
8b84a22
Remove the changes to theme.json schema
michalczaplinski May 5, 2023
d5dbc1c
Add behaviors to the VALID_SETTINGS in class-wp-theme-json-gutenberg.php
michalczaplinski May 5, 2023
037d669
Add the first (still broken) version of the lightbox settings
michalczaplinski May 5, 2023
8f62d34
WIP: Added a SelectControl for the behaviors
michalczaplinski May 9, 2023
62b6217
Format PHP
michalczaplinski May 9, 2023
edfd7d1
Format correctly again
michalczaplinski May 9, 2023
8ee4065
Use the props.name when getting the behaviors
michalczaplinski May 10, 2023
594224b
Add initial e2e tests
cbravobernal May 10, 2023
fd6fe36
Update the withBehaviors description
michalczaplinski May 10, 2023
da4a5ad
Cleaned up behaviors.js
michalczaplinski May 11, 2023
2958fd1
Update e2e tests to check visibility
cbravobernal May 12, 2023
0a68bea
Update core-blocks doc with behaviors attribute
cbravobernal May 12, 2023
fd08928
Update fixtures with new lightbox attribute
cbravobernal May 13, 2023
4e03e0d
Add a new theme for e2e tests
michalczaplinski May 16, 2023
92b6e6c
Change theme.json to include `settings.behaviors`
michalczaplinski May 16, 2023
ffa881e
Remove default behavior value from block.json
michalczaplinski May 16, 2023
99c964b
Minor fix to the implementation
michalczaplinski May 16, 2023
7f7db2c
Add more e2e tests
michalczaplinski May 16, 2023
5e5ff9a
Create a selector for behaviors
michalczaplinski May 17, 2023
ad20b64
Add a filter to load behaviors on the server from the theme.json
michalczaplinski May 17, 2023
d16266b
Add behaviors on the top-level in core theme.json
michalczaplinski May 17, 2023
0982d9f
Use the behaviors in the hooks
michalczaplinski May 17, 2023
0ef7b5e
Update the comment
michalczaplinski May 17, 2023
e75fd87
Update the comment in behaviors.js
michalczaplinski May 17, 2023
f6414f9
Update fixtures and e2e
cbravobernal May 17, 2023
31c0623
Prevent mobile test gutenberg error (temporary - not the best solution)
cbravobernal May 18, 2023
ee3701f
Add priority on filter
cbravobernal May 18, 2023
a0ac748
Fix php standards
cbravobernal May 18, 2023
1dd6df1
Found a much better way to fix php tests
cbravobernal May 18, 2023
a737200
Small refactor
cbravobernal May 18, 2023
3fd9270
Add `behaviors` as an allowed key to BLOCK_EDITOR_SETTINGS
michalczaplinski May 18, 2023
5a9748c
Move the behaviors to top level in the e2e test theme.json file
michalczaplinski May 18, 2023
9a6b1f2
Rename the `behaviors` setting to `behaviorsUIEnabled`
michalczaplinski May 18, 2023
1c7f6d5
Change "None" to "No behaviors"
michalczaplinski May 18, 2023
470f74f
Behaviors -> behavior
michalczaplinski May 18, 2023
bba7c2d
Fix redundant ternary
michalczaplinski May 18, 2023
2cb348c
Improve the JSDoc for behaviors selector
michalczaplinski May 18, 2023
c65bc57
Rename the test themes to make more sense
michalczaplinski May 19, 2023
1c655f7
Remove definition of `behaviors` attribute in core/image
michalczaplinski May 22, 2023
697c04f
Change default value for `behaviors.lightbox` to false and update e2e…
michalczaplinski May 22, 2023
d4fd9d9
Change the way we get the data from `theme.json` and adjust e2e
michalczaplinski May 23, 2023
a57d634
Capitalize behaviors' labels
michalczaplinski May 23, 2023
234a8b6
Move PHP code adding `theme.json` behaviors to `block-editor-settings`
michalczaplinski May 23, 2023
4bef633
Update comment
michalczaplinski May 23, 2023
7aa7231
Remove the behaviors require from load.php
michalczaplinski May 23, 2023
1d923ab
Revert "Update comment"
michalczaplinski May 23, 2023
aee908a
Revert "Move PHP code adding `theme.json` behaviors to `block-editor-…
michalczaplinski May 23, 2023
33cb62d
Remove the comment that was added previously
michalczaplinski May 23, 2023
55e37a3
Update comments in `behaviors.php`
michalczaplinski May 23, 2023
d4d657b
Add back the require_once in load.php
michalczaplinski May 23, 2023
8d6b0d0
Use `settings.blocks.core/image.behaviors.lightbox`
michalczaplinski May 23, 2023
76b2a75
Use `behaviors.blocks.core/image.lightbox`
michalczaplinski May 23, 2023
70e51cd
Remove experimental setting for interactivity API
cbravobernal May 24, 2023
e69833b
Add lightbox to image block
artemiomorales Apr 3, 2023
9ca2918
Add logic for hiding lightbox on esc key press and overlay click
artemiomorales Apr 3, 2023
dca1b36
Improve styles and add note to add conditional for lightbox markup
artemiomorales Apr 5, 2023
552bc3c
Add editor UI and attribute for toggling lightbox
artemiomorales Apr 13, 2023
a46c35e
Remove image translation animation and add fade instead
artemiomorales Apr 19, 2023
5bdddd5
Add accessibility; clean up styles; fix bug regarding ref in directives
artemiomorales Apr 21, 2023
3c8a458
Configure image to use new Interactivity API runtime included in Gute…
artemiomorales Apr 26, 2023
c69f6ff
Remove viewScript from image config
artemiomorales Apr 26, 2023
4802229
Add Portal directive to Interactivity API runtime
artemiomorales Apr 26, 2023
e7a7a4c
Set scrim to site background color
artemiomorales Apr 26, 2023
46b95d6
Remove extraneous image CSS declaration
artemiomorales Apr 26, 2023
f5ee39e
Improve aria labeling
artemiomorales May 2, 2023
5cc8eae
Code cleanup; simplify syntax, consolidate code
artemiomorales May 3, 2023
7009fa1
Refactor code, remove event listeners, consolidate logic
artemiomorales May 3, 2023
910706f
Fix formatting in SCSS file
artemiomorales May 3, 2023
1ebe07b
Change CheckboxControl to a ToggleControl; update API docs
artemiomorales May 3, 2023
10cdc07
Update wp_enqueue_script to correctly add interactivity runtime
artemiomorales May 3, 2023
d035dc4
Fix linter errors
artemiomorales May 4, 2023
aa267c0
Update to use core.image namespace
artemiomorales May 4, 2023
43dfa46
Pause closing of lightbox slightly when using the mousewheel
artemiomorales May 4, 2023
58b5392
Rename portal directive to 'wp-body' and remove unused reference
artemiomorales May 4, 2023
0d5ef70
Add internal dependencies flag; update comment
artemiomorales May 4, 2023
1c0e406
Remove extraneous code
artemiomorales May 4, 2023
926d503
Fix accessibility bug due to directives being defined incorrectly
artemiomorales May 5, 2023
68685f1
Move enableLightbox declaration in block.json
artemiomorales May 5, 2023
3811186
Remove extraneous package.json declarations
artemiomorales May 8, 2023
7840dfe
Fix PHP indentation linter error
artemiomorales May 8, 2023
7f2cfe5
Revise package-lock.json to match trunk
artemiomorales May 8, 2023
effd389
Fix linter errors in portals.js
artemiomorales May 8, 2023
8c7adae
Update fixtures
artemiomorales May 8, 2023
4558d29
Tests: Add e2e specs for Image block on the frontend
gziolo May 9, 2023
6629975
Disable lightbox by default
artemiomorales May 10, 2023
409941e
Add aria-role attribute to lightbox markup
artemiomorales May 10, 2023
457cbfe
Add tests
artemiomorales May 11, 2023
7807fdf
Update fixtures
artemiomorales May 11, 2023
0141d68
Fix accessibility issues
artemiomorales May 18, 2023
f8aa92f
Add focus trap
artemiomorales May 19, 2023
5558e8d
Label elements as 'inert' when lightbox is open
artemiomorales May 19, 2023
3dc3bf9
Update import of image interactivity scripts
artemiomorales May 19, 2023
99d95f9
Fix bug where interactivity only worked with admin enabled
artemiomorales May 19, 2023
53e1d47
Put lightbox rendering behind experimental flag
artemiomorales May 19, 2023
518e94f
Add check for query selector and update tests
artemiomorales May 23, 2023
fb7af8b
Update spacing in PHP file
artemiomorales May 23, 2023
ce9b0c0
Remove tick() and modify CSS
artemiomorales May 23, 2023
84be707
Update config based on behaviors UI and trunk changes
artemiomorales May 23, 2023
899b11e
Remove extraneous code
artemiomorales May 23, 2023
1ea0d58
Remove extraneous code and simplify variable name
artemiomorales May 23, 2023
3986e35
Use core WordPress API for experiments flag
artemiomorales May 23, 2023
32dc7a8
Clean up declarations
artemiomorales May 23, 2023
48a62d2
Fix PHP spacing
artemiomorales May 23, 2023
ced26e1
Remove `enableLightbox`
michalczaplinski May 23, 2023
528b60a
Remove defaultValue from image block.json
michalczaplinski May 23, 2023
566460c
Update render_callback of core/image to use Behaviors UI
michalczaplinski May 24, 2023
5efaa21
Fix e2e test:
michalczaplinski May 24, 2023
77e7fb8
Remove 'inert' declarations for now
artemiomorales May 24, 2023
d854c7e
Add period to behaviors help text
artemiomorales May 24, 2023
6b75413
Create selector for 'role' attribute
artemiomorales May 24, 2023
330f403
Update fixtures
artemiomorales May 24, 2023
5556ad7
Update comment
artemiomorales May 24, 2023
cdae7fe
Clean up data-wp-island attribute
artemiomorales May 24, 2023
9b1534d
Readd mistakenly deleted line
artemiomorales May 24, 2023
3b89cef
Improve syntax of tests
artemiomorales May 24, 2023
e532fb1
Replace getByLabel() with getByRole() in some tests
artemiomorales May 24, 2023
cd0c9dd
Remove 'experiments' flag
artemiomorales May 24, 2023
429ebbe
Remove dependency on 'experiments' flag in tests
artemiomorales May 24, 2023
95a89f7
Revert "Remove dependency on 'experiments' flag in tests"
SantosGuillamot May 24, 2023
7c9e0eb
Revert "Remove 'experiments' flag"
SantosGuillamot May 24, 2023
8aebca8
Change `aria-label` separator
SantosGuillamot May 24, 2023
6ce95ae
Add back the experimental setting
SantosGuillamot May 24, 2023
1eaffe1
Merge branch 'trunk' into experiment/image-with-interactivity-api
SantosGuillamot May 24, 2023
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
4 changes: 2 additions & 2 deletions lib/experimental/interactivity-api/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -216,15 +216,15 @@ function gutenberg_block_core_navigation_add_directives_to_submenu( $w ) {
add_filter( 'render_block_core/navigation', 'gutenberg_block_core_navigation_add_directives_to_markup', 10, 1 );

/**
* Replaces view script for the File and Navigation blocks with version using Interactivity API.
* Replaces view script for the File, Navigation, and Image blocks with version using Interactivity API.
*
* @param array $metadata Block metadata as read in via block.json.
*
* @return array Filtered block type metadata.
*/
function gutenberg_block_update_interactive_view_script( $metadata ) {
if (
in_array( $metadata['name'], array( 'core/file', 'core/navigation' ), true ) &&
in_array( $metadata['name'], array( 'core/file', 'core/navigation', 'core/image' ), true ) &&
artemiomorales marked this conversation as resolved.
Show resolved Hide resolved
str_contains( $metadata['file'], 'build/block-library/blocks' )
) {
$metadata['viewScript'] = array( 'file:./interactivity.min.js' );
Expand Down
2 changes: 1 addition & 1 deletion packages/block-editor/src/hooks/behaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export const withBehaviors = createHigherOrderComponent( ( BlockEdit ) => {
} );
} }
hideCancelButton={ true }
help={ __( 'Add behaviors' ) }
help={ __( 'Add behaviors.' ) }
size="__unstable-large"
/>
</InspectorControls>
Expand Down
77 changes: 75 additions & 2 deletions packages/block-library/src/image/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' );

Expand All @@ -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';
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason for not making this text translatable?

Copy link
Member

Choose a reason for hiding this comment

The 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
Copy link
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Member

Choose a reason for hiding this comment

The 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.

Copy link
Member

Choose a reason for hiding this comment

The 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 data-wp-interactive. Would that be easier to read, or do you have any other suggestions?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes I think data-wp-interactive would be clearer.

Copy link
Member

Choose a reason for hiding this comment

The 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(
Expand Down
113 changes: 113 additions & 0 deletions packages/block-library/src/image/interactivity.js
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.
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();
}
},
},
},
},
} );
113 changes: 113 additions & 0 deletions packages/block-library/src/image/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,116 @@
.wp-block-image figure {
margin: 0;
}

.wp-lightbox-container {

.img-container {
position: relative;
}

button {
border: none;
background: none;
cursor: zoom-in;
width: 100%;
height: 100%;
position: absolute;
z-index: 100;

&:focus-visible {
outline: 5px auto #212121;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: 5px;
}
}
}

.wp-lightbox-overlay {
position: fixed;
top: 0;
left: 0;
z-index: 100000;
overflow: hidden;
width: 100vw;
height: 100vh;
visibility: hidden;

.close-button {
font-size: 40px;
position: absolute;
top: 20px;
right: 20px;
cursor: pointer;
z-index: 5000000;
}

.wp-block-image {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
z-index: 3000000;
position: absolute;
flex-direction: column;
}

button {
border: none;
background: none;
}

.scrim {
width: 100%;
height: 100%;
position: absolute;
z-index: 2000000;
background-color: rgb(255, 255, 255);
opacity: 0.9;
}

&.initialized {
animation: both turn-off-visibility 300ms;

img {
animation: both turn-off-visibility 250ms;
}

&.active {
visibility: visible;
animation: both turn-on-visibility 250ms;

img {
animation: both turn-on-visibility 300ms;
}
}
}
}

@keyframes turn-on-visibility {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}

@keyframes turn-off-visibility {
0% {
opacity: 1;
visibility: visible;
}
99% {
opacity: 0;
visibility: visible;
}
100% {
opacity: 0;
visibility: hidden;
}
}

html.has-lightbox-open {
overflow: hidden;
}
14 changes: 14 additions & 0 deletions packages/block-library/src/utils/interactivity/directives.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
*/
import { useContext, useMemo, useEffect } from 'preact/hooks';
import { deepSignal, peek } from 'deepsignal';
/**
* Internal dependencies
*/
import { createPortal } from './portals.js';

/**
* Internal dependencies
Expand Down Expand Up @@ -53,6 +57,16 @@ export default () => {
{ priority: 5 }
);

// data-wp-body
directive( 'body', ( { props: { children }, context: inherited } ) => {
const { Provider } = inherited;
const inheritedValue = useContext( inherited );
return createPortal(
<Provider value={ inheritedValue }>{ children }</Provider>,
document.body
);
} );

// data-wp-effect.[name]
directive( 'effect', ( { directives: { effect }, context, evaluate } ) => {
const contextValue = useContext( context );
Expand Down
Loading