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

Create @wordpress/interactivity with the Interactivity API #50906

Merged
merged 42 commits into from
Jun 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
2efa328
Start with package.json and README
DAreRodz May 26, 2023
455396b
Add new package to docs/manifest.json
DAreRodz May 26, 2023
b48ac7c
Copy-paste runtime files from block-library
DAreRodz May 26, 2023
596e4da
Add .npmrc file
DAreRodz May 26, 2023
bbcd7fa
Add interactivity package to dependencies
DAreRodz May 26, 2023
b24a7fb
Create a custom webpack config for interactivity
DAreRodz May 26, 2023
97e0278
Expose interactivity runtime in `wp.interactivity`
DAreRodz May 26, 2023
59ba71b
Update package-lock
DAreRodz May 26, 2023
d80af2d
Add `@wordpress/interactivity` to block-library deps
DAreRodz May 26, 2023
43af7ab
Rename entry point to index
DAreRodz May 26, 2023
b19d9f5
Remove vendors chunk
DAreRodz May 26, 2023
e971cd6
Add oddly required aliases
DAreRodz May 26, 2023
02701e9
Add view prefix to interactivity.js files
DAreRodz May 26, 2023
22d7543
Use view-interactivity files when enabled
DAreRodz May 26, 2023
04bf309
Stop adding defer to Interactivity scripts
DAreRodz May 26, 2023
91e3fde
Remove webpack config for interactivity.js files
DAreRodz May 26, 2023
75159c4
Remove interactivity runtime from block-library
DAreRodz May 26, 2023
df8563b
Remove interactivity runtime from sideEffects
DAreRodz May 26, 2023
7b1953f
Undo temporary fix for Interactivity API in dependency-extraction-web…
DAreRodz May 26, 2023
8a10636
Remove script loader for Interactivity API runtime
DAreRodz May 26, 2023
ffa40e7
Remove block-librar/interactivity from build_files
DAreRodz May 26, 2023
ea033b1
Add src/index.js to Interactivity API entry file
DAreRodz May 26, 2023
d9561b8
Remove unnecessary aliases
DAreRodz May 26, 2023
bec4852
Restore data-wp-body directive
DAreRodz May 26, 2023
8cb572c
Interactivity API: add `wp_store` (#51191)
luisherranz Jun 2, 2023
1b7ed17
Rename syntax to -- and data-wp-interactive (#51241)
luisherranz Jun 6, 2023
66c4c58
Merge branch 'trunk' into interactivity
luisherranz Jun 6, 2023
e740fff
Interactivity API: initial support for SSR (#51229)
luisherranz Jun 6, 2023
2f86561
Merge branch 'trunk' into interactivity
DAreRodz Jun 15, 2023
3ac4a7b
Remove require for missing script-loader.php
DAreRodz Jun 15, 2023
db7686e
Merge branch 'trunk' into interactivity
luisherranz Jun 20, 2023
4b0b87a
Remove missing PHP file
luisherranz Jun 21, 2023
02a46bb
Rename view file and fix block.json
luisherranz Jun 21, 2023
3d1a1c4
Merge branch 'trunk' into interactivity
luisherranz Jun 21, 2023
294bf69
Add "interactivity" to supports and fix renaming
luisherranz Jun 21, 2023
1ed1e34
Merge branch 'trunk' into interactivity
luisherranz Jun 23, 2023
8758865
Code improvements for the SSR part of the Interactivity API (#51640)
DAreRodz Jun 23, 2023
c745063
Add the full Interactivity API runtime (but removing the client-side …
DAreRodz Jun 27, 2023
242a3ba
Merge branch 'trunk' into interactivity
luisherranz Jun 27, 2023
e409e41
Remove custom watchOptions in interactivity webpack config
DAreRodz Jun 28, 2023
2ad3cae
Update version and description of interactivity package
DAreRodz Jun 28, 2023
abf22e5
Merge branch 'trunk' into interactivity
luisherranz Jun 28, 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ coverage
*.log
yarn.lock
/artifacts
/test/e2e/artifacts
/perf-envs
/composer.lock

Expand Down
1 change: 0 additions & 1 deletion bin/build-plugin-zip.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ build_files=$(
build/block-library/blocks/*.php \
build/block-library/blocks/*/block.json \
build/block-library/blocks/*/*.{js,js.map,css,asset.php} \
build/block-library/interactivity/*.{js,js.map,asset.php} \
build/edit-widgets/blocks/*/block.json \
build/widgets/blocks/*.php \
build/widgets/blocks/*/block.json \
Expand Down
6 changes: 6 additions & 0 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -1673,6 +1673,12 @@
"markdown_source": "../packages/icons/README.md",
"parent": "packages"
},
{
"title": "@wordpress/interactivity",
"slug": "packages-interactivity",
"markdown_source": "../packages/interactivity/README.md",
"parent": "packages"
},
{
"title": "@wordpress/interface",
"slug": "packages-interface",
Expand Down
4 changes: 2 additions & 2 deletions docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ Add a link to a downloadable file. ([Source](https://github.com/WordPress/gutenb

- **Name:** core/file
- **Category:** media
- **Supports:** align, anchor, color (background, gradients, link, ~~text~~)
- **Supports:** align, anchor, color (background, gradients, link, ~~text~~), interactivity
- **Attributes:** displayPreview, downloadButtonText, fileId, fileName, href, id, previewHeight, showDownloadButton, textLinkHref, textLinkTarget

## Footnotes
Expand Down Expand Up @@ -421,7 +421,7 @@ A collection of blocks that allow visitors to get around your site. ([Source](ht

- **Name:** core/navigation
- **Category:** theme
- **Supports:** align (full, wide), inserter, layout (allowSizingOnChildren, default, ~~allowInheriting~~, ~~allowSwitching~~, ~~allowVerticalAlignment~~), spacing (blockGap, units), typography (fontSize, lineHeight), ~~html~~
- **Supports:** align (full, wide), inserter, interactivity, layout (allowSizingOnChildren, default, ~~allowInheriting~~, ~~allowSwitching~~, ~~allowVerticalAlignment~~), spacing (blockGap, units), typography (fontSize, lineHeight), ~~html~~
- **Attributes:** __unstableLocation, backgroundColor, customBackgroundColor, customOverlayBackgroundColor, customOverlayTextColor, customTextColor, hasIcon, icon, maxNestingLevel, openSubmenusOnClick, overlayBackgroundColor, overlayMenu, overlayTextColor, ref, rgbBackgroundColor, rgbTextColor, showSubmenuIcon, templateLock, textColor

## Custom Link
Expand Down
5 changes: 3 additions & 2 deletions lib/experimental/interactivity-api/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
/**
* Extend WordPress core blocks to use the Interactivity API.
*
* @package gutenberg
* @package Gutenberg
* @subpackage Interactivity API
*/

/**
Expand All @@ -17,7 +18,7 @@ function gutenberg_block_update_interactive_view_script( $metadata ) {
in_array( $metadata['name'], array( 'core/image' ), true ) &&
str_contains( $metadata['file'], 'build/block-library/blocks' )
) {
$metadata['viewScript'] = array( 'file:./interactivity.min.js' );
$metadata['viewScript'] = array( 'file:./view-interactivity.min.js' );
}
return $metadata;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php
/**
* Context data implementation.
*
* @package Gutenberg
* @subpackage Interactivity API
*/

/**
* This is a data structure to hold the current context.
*
* Whenever encountering a `data-wp-context` directive, we need to update
* the context with the data found in that directive. Conversely,
* when "leaving" that context (by encountering a closing tag), we
* need to reset the context to its previous state. This means that
* we actually need sort of a stack to keep track of all nested contexts.
*
* Example:
*
* <div data-wp-context='{ "foo": 123 }'>
* <!-- foo should be 123 here. -->
* <div data-wp-context='{ "foo": 456 }'>
* <!-- foo should be 456 here. -->
* </div>
* <!-- foo should be reset to 123 here. -->
* </div>
*/
class WP_Directive_Context {
/**
* The stack used to store contexts internally.
*
* @var array An array of contexts.
*/
protected $stack = array( array() );

/**
* Constructor.
*
* Accepts a context as an argument to initialize this with.
*
* @param array $context A context.
*/
function __construct( $context = array() ) {
$this->set_context( $context );
}

/**
* Return the current context.
*
* @return array The current context.
*/
public function get_context() {
return end( $this->stack );
}

/**
* Set the current context.
*
* @param array $context The context to be set.
*
* @return void
*/
public function set_context( $context ) {
if ( $context ) {
array_push( $this->stack, array_replace_recursive( $this->get_context(), $context ) );
}
}

/**
* Reset the context to its previous state.
*
* @return void
*/
public function rewind_context() {
array_pop( $this->stack );
}
}
193 changes: 193 additions & 0 deletions lib/experimental/interactivity-api/class-wp-directive-processor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
<?php
/**
* WP_Directive_Processor class
*
* @package Gutenberg
* @subpackage Interactivity API
*/

/**
* This processor is built on top of the HTML Tag Processor and augments its
* capabilities to process the Interactivity API directives.
*
* IMPORTANT DISCLAIMER: This code is highly experimental and its only purpose
* is to provide a way to test the server-side rendering of the Interactivity
* API. Most of this code will be discarded once the HTML Processor is
* available. Please restrain from investing unnecessary time and effort trying
* to improve this code.
*/
class WP_Directive_Processor extends WP_HTML_Tag_Processor {
/**
* Find the matching closing tag for an opening tag.
*
* When called while on an open tag, traverse the HTML until we find the
* matching closing tag, respecting any in-between content, including nested
* tags of the same name. Return false when called on a closing or void tag,
* or if no matching closing tag was found.
*
* @return bool Whether a matching closing tag was found.
*/
public function next_balanced_closer() {
$depth = 0;

$tag_name = $this->get_tag();

if ( self::is_html_void_element( $tag_name ) ) {
return false;
}

while ( $this->next_tag(
array(
'tag_name' => $tag_name,
'tag_closers' => 'visit',
)
) ) {
if ( ! $this->is_tag_closer() ) {
$depth++;
continue;
}

if ( 0 === $depth ) {
return true;
}

$depth--;
}

return false;
}

/**
* Return the content between two balanced tags.
*
* When called on an opening tag, return the HTML content found between that
* opening tag and its matching closing tag.
*
* @return string The content between the current opening and its matching
* closing tag.
*/
public function get_inner_html() {
$bookmarks = $this->get_balanced_tag_bookmarks();
if ( ! $bookmarks ) {
return false;
}
list( $start_name, $end_name ) = $bookmarks;

$start = $this->bookmarks[ $start_name ]->end + 1;
$end = $this->bookmarks[ $end_name ]->start;

$this->seek( $start_name ); // Return to original position.
$this->release_bookmark( $start_name );
$this->release_bookmark( $end_name );

return substr( $this->html, $start, $end - $start );
}

/**
* Set the content between two balanced tags.
*
* When called on an opening tag, set the HTML content found between that
* opening tag and its matching closing tag.
*
* @param string $new_html The string to replace the content between the
* matching tags with.
*
* @return bool Whether the content was successfully replaced.
*/
public function set_inner_html( $new_html ) {
$this->get_updated_html(); // Apply potential previous updates.

$bookmarks = $this->get_balanced_tag_bookmarks();
if ( ! $bookmarks ) {
return false;
}
list( $start_name, $end_name ) = $bookmarks;

$start = $this->bookmarks[ $start_name ]->end + 1;
$end = $this->bookmarks[ $end_name ]->start;

$this->seek( $start_name ); // Return to original position.
$this->release_bookmark( $start_name );
$this->release_bookmark( $end_name );

$this->lexical_updates[] = new WP_HTML_Text_Replacement( $start, $end, $new_html );
return true;
}

/**
* Return a pair of bookmarks for the current opening tag and the matching
* closing tag.
*
* @return array|false A pair of bookmarks, or false if there's no matching
* closing tag.
*/
public function get_balanced_tag_bookmarks() {
$i = 0;
while ( array_key_exists( 'start' . $i, $this->bookmarks ) ) {
++$i;
}
$start_name = 'start' . $i;

$this->set_bookmark( $start_name );
if ( ! $this->next_balanced_closer() ) {
$this->release_bookmark( $start_name );
return false;
}

$i = 0;
while ( array_key_exists( 'end' . $i, $this->bookmarks ) ) {
++$i;
}
$end_name = 'end' . $i;
$this->set_bookmark( $end_name );

return array( $start_name, $end_name );
}

/**
* Whether a given HTML element is void (e.g. <br>).
*
* @param string $tag_name The element in question.
* @return bool True if the element is void.
*
* @see https://html.spec.whatwg.org/#elements-2
*/
public static function is_html_void_element( $tag_name ) {
switch ( $tag_name ) {
case 'AREA':
case 'BASE':
case 'BR':
case 'COL':
case 'EMBED':
case 'HR':
case 'IMG':
case 'INPUT':
case 'LINK':
case 'META':
case 'SOURCE':
case 'TRACK':
case 'WBR':
return true;

default:
return false;
}
}

/**
* Extract and return the directive type and the the part after the double
* hyphen from an attribute name (if present), in an array format.
*
* Examples:
*
* 'wp-island' => array( 'wp-island', null )
* 'wp-bind--src' => array( 'wp-bind', 'src' )
* 'wp-thing--and--thang' => array( 'wp-thing', 'and--thang' )
*
* @param string $name The attribute name.
* @return array The resulting array
*/
public static function parse_attribute_name( $name ) {
return explode( '--', $name, 2 );
}
}
Loading