Skip to content

Commit

Permalink
Added Modal Manager block and started general modal interactivity.
Browse files Browse the repository at this point in the history
  • Loading branch information
mauteri committed Dec 15, 2024
1 parent 97e5a9e commit f6cd284
Show file tree
Hide file tree
Showing 16 changed files with 288 additions and 26 deletions.
19 changes: 19 additions & 0 deletions build/blocks/modal-manager/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "gatherpress/modal-manager",
"version": "1.0.0",
"title": "Modal Manager",
"category": "gatherpress",
"icon": "external",
"example": {},
"description": "Manage modals and their triggers with ease.",
"attributes": {},
"supports": {
"html": false,
"interactivity": true
},
"textdomain": "gatherpress",
"editorScript": "file:./index.js",
"viewScriptModule": "file:./view.js"
}
1 change: 1 addition & 0 deletions build/blocks/modal-manager/index.asset.php
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks'), 'version' => '1097ba5f24d0a9952920');
1 change: 1 addition & 0 deletions build/blocks/modal-manager/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions build/blocks/modal-manager/view.asset.php
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<?php return array('dependencies' => array('@wordpress/interactivity'), 'version' => '78bbf04e2bbfe6dd79f3', 'type' => 'module');
1 change: 1 addition & 0 deletions build/blocks/modal-manager/view.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions build/blocks/modal/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"name": "gatherpress/modal",
"version": "1.0.0",
"title": "Modal",
"parent": [
"gatherpress/modal-manager"
],
"category": "gatherpress",
"icon": "external",
"example": {},
Expand Down
2 changes: 1 addition & 1 deletion build/blocks/modal/index.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-data'), 'version' => 'f0f47369380caa31bad3');
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-data'), 'version' => 'd1d1ac2fd0477b184bb0');
2 changes: 1 addition & 1 deletion build/blocks/modal/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

110 changes: 110 additions & 0 deletions includes/core/classes/blocks/class-modal-manager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php
/**
* The "RSVP" class manages the RSVP block and its variations,
* primarily transforming block content and preparing it for output.
*
* @package GatherPress\Core
* @since 1.0.0
*/

namespace GatherPress\Core\Blocks;

// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore

use GatherPress\Core\Block;
use GatherPress\Core\Traits\Singleton;
use WP_HTML_Tag_Processor;

/**
* Class responsible for managing the "RSVP" block and its variations,
* including dynamic transformations and enhancements for interactive functionality.
*
* @since 1.0.0
*/
class Modal_Manager {
/**
* Enforces a single instance of this class.
*/
use Singleton;

/**
* Class constructor.
*
* This method initializes the object and sets up necessary hooks.
*
* @since 1.0.0
*/
protected function __construct() {
$this->setup_hooks();
}

/**
* Set up hooks for various purposes.
*
* This method adds hooks for different purposes as needed.
*
* @since 1.0.0
*
* @return void
*/
protected function setup_hooks(): void {
add_filter( 'render_block', array( $this, 'inject_modal_behavior' ), 10, 2 );
}

/**
* Injects modal interactivity behavior into block content.
*
* This method enhances `core/button` blocks with specific classes by injecting
* attributes necessary for modal interactivity. It supports both `<button>`
* and `<a>` elements and applies the corresponding interactivity attributes
* based on the class names `gatherpress--open-modal` and `gatherpress--close-modal`.
*
* If a block contains the `gatherpress--open-modal` class, it adds attributes
* to handle opening the modal. Similarly, for the `gatherpress--close-modal`
* class, it adds attributes for closing the modal.
*
* @since 1.0.0
*
* @param string $block_content The HTML content of the block.
* @param array $block The parsed block data.
*
* @return string The updated block content with interactivity attributes.
*/
public function inject_modal_behavior( string $block_content, array $block ): string {
$block_instance = Block::get_instance();

if (
'core/button' === $block['blockName'] &&
isset( $block['attrs']['className'] )
) {
$tag = new WP_HTML_Tag_Processor( $block_content );
$button_tag = $block_instance->locate_button_tag( $tag, 'button' );

if ( empty( $button_tag ) ) {
$tag = new WP_HTML_Tag_Processor( $block_content );
$button_tag = $block_instance->locate_button_tag( $tag, 'a' );
}

if (
$button_tag &&
false !== strpos( $block['attrs']['className'], 'gatherpress--open-modal' )
) {
$button_tag->set_attribute( 'data-wp-interactive', 'gatherpress/modal' );
$button_tag->set_attribute( 'data-wp-on--click', 'actions.openModal' );
}

if (
$button_tag &&
false !== strpos( $block['attrs']['className'], 'gatherpress--close-modal' )
) {
$button_tag->set_attribute( 'data-wp-interactive', 'gatherpress/modal' );
$button_tag->set_attribute( 'data-wp-on--click', 'actions.closeModal' );
}

$block_content = $tag->get_updated_html();
}

return $block_content;
}
}
31 changes: 7 additions & 24 deletions includes/core/classes/blocks/class-rsvp.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore

use GatherPress\Core\Block;
use GatherPress\Core\Blocks;
use GatherPress\Core\Traits\Singleton;
use WP_HTML_Tag_Processor;

Expand Down Expand Up @@ -64,6 +66,8 @@ protected function setup_hooks(): void {
* @return string The updated block content with the applied transformations.
*/
public function transform_block_content( string $block_content, array $block ): string {
$block_instance = Block::get_instance();

if ( 'gatherpress/rsvp-v2' === $block['blockName'] ) {
$inner_blocks = isset( $block['innerBlocks'] ) ? $block['innerBlocks'] : array();
$tag = new WP_HTML_Tag_Processor( $block_content );
Expand Down Expand Up @@ -117,7 +121,7 @@ public function transform_block_content( string $block_content, array $block ):
$tag = new WP_HTML_Tag_Processor( $block_content );

// Locate the <button> tag and set the attributes.
$button_tag = $this->locate_button_tag( $tag );
$button_tag = $block_instance->locate_button_tag( $tag );
if ( $button_tag ) {
$button_tag->set_attribute( 'data-wp-interactive', 'gatherpress/rsvp' );
$button_tag->set_attribute( 'data-wp-on--click', 'actions.rsvpOpenModal' );
Expand All @@ -135,7 +139,7 @@ public function transform_block_content( string $block_content, array $block ):
$tag = new WP_HTML_Tag_Processor( $block_content );

// Locate the <button> tag and set the attributes.
$button_tag = $this->locate_button_tag( $tag );
$button_tag = $block_instance->locate_button_tag( $tag );
if ( $button_tag ) {
$button_tag->set_attribute( 'data-wp-interactive', 'gatherpress/rsvp' );
$button_tag->set_attribute( 'data-wp-on--click', 'actions.rsvpCloseModal' );
Expand All @@ -153,7 +157,7 @@ public function transform_block_content( string $block_content, array $block ):
$tag = new WP_HTML_Tag_Processor( $block_content );

// Locate the <button> tag and set the attributes.
$button_tag = $this->locate_button_tag( $tag );
$button_tag = $block_instance->locate_button_tag( $tag );
if ( $button_tag ) {
$button_tag->set_attribute( 'data-wp-interactive', 'gatherpress/rsvp' );
$button_tag->set_attribute( 'data-wp-on--click', 'actions.rsvpChangeStatus' );
Expand All @@ -165,25 +169,4 @@ public function transform_block_content( string $block_content, array $block ):

return $block_content;
}

/**
* Locates the button tag within a specific block structure.
*
* This method searches for a button tag following a div tag within
* the given HTML tag processor instance. If both tags are found,
* the processor is returned for further manipulation.
*
* @since 1.0.0
*
* @param WP_HTML_Tag_Processor $tag The HTML tag processor instance for the block content.
*
* @return WP_HTML_Tag_Processor|null The tag processor instance if the button is located, or null otherwise.
*/
private function locate_button_tag( WP_HTML_Tag_Processor $tag ): ?WP_HTML_Tag_Processor {
if ( $tag->next_tag( array( 'tag_name' => 'div' ) ) && $tag->next_tag( array( 'tag_name' => 'button' ) ) ) {
return $tag;
}

return null;
}
}
24 changes: 24 additions & 0 deletions includes/core/classes/class-block.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use GatherPress\Core\Blocks\Rsvp_Template;
use GatherPress\Core\Traits\Singleton;
use WP_Block_Template;
use WP_HTML_Tag_Processor;
use WP_Post;

/**
Expand Down Expand Up @@ -108,6 +109,7 @@ public function register_blocks(): void {
*/
public function register_block_classes(): void {
Blocks\Add_To_Calendar::get_instance();
Blocks\Modal_Manager::get_instance();
Blocks\Rsvp::get_instance();
Blocks\Rsvp_Response::get_instance();
Blocks\Rsvp_Template::get_instance();
Expand Down Expand Up @@ -305,4 +307,26 @@ public function modify_hooked_blocks_in_patterns( ?array $parsed_hooked_block, s

return $parsed_hooked_block;
}

/**
* Locates a specific tag within a block structure.
*
* This method searches for a specified tag (e.g., button or anchor) following a div tag
* within the given HTML tag processor instance. If the specified tags are found, the
* processor is returned for further manipulation.
*
* @since 1.0.0
*
* @param WP_HTML_Tag_Processor $tag The HTML tag processor instance for the block content.
* @param string $tag_name The name of the tag to locate (e.g., 'button', 'a'). Defaults to 'button'.
*
* @return WP_HTML_Tag_Processor|null The tag processor instance if the specified tag is located, or null otherwise.
*/
public function locate_button_tag( WP_HTML_Tag_Processor $tag, $tag_name = 'button' ): ?WP_HTML_Tag_Processor {
if ( $tag->next_tag( array( 'tag_name' => 'div' ) ) && $tag->next_tag( array( 'tag_name' => $tag_name ) ) ) {
return $tag;
}

return null;
}
}
19 changes: 19 additions & 0 deletions src/blocks/modal-manager/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "gatherpress/modal-manager",
"version": "1.0.0",
"title": "Modal Manager",
"category": "gatherpress",
"icon": "external",
"example": {},
"description": "Manage modals and their triggers with ease.",
"attributes": {},
"supports": {
"html": false,
"interactivity": true
},
"textdomain": "gatherpress",
"editorScript": "file:./index.js",
"viewScriptModule": "file:./view.js"
}
20 changes: 20 additions & 0 deletions src/blocks/modal-manager/edit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* WordPress dependencies.
*/
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';

const Edit = () => {
const blockProps = useBlockProps();

const TEMPLATE = [
['gatherpress/modal', {}, [['gatherpress/modal-content', {}]]],
];

return (
<div {...blockProps}>
<InnerBlocks template={TEMPLATE} />
</div>
);
};

export default Edit;
34 changes: 34 additions & 0 deletions src/blocks/modal-manager/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* WordPress dependencies.
*/
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';

/**
* Internal dependencies.
*/
import edit from './edit';
import metadata from './block.json';

/**
* Edit component for the GatherPress Modal Manager block.
*
* This component renders the edit view of the GatherPress Modal Manager block.
* The block acts as a container for managing modals and their triggers.
* It includes functionality for setting up modals and dynamically handling
* their content and visibility, providing users with an interactive experience.
*
* @since 1.0.0
*
* @return {JSX.Element} The rendered React component for editing the block.
*/
registerBlockType(metadata, {
edit,
save: () => {
return (
<div {...useBlockProps.save()}>
<InnerBlocks.Content />
</div>
);
},
});
45 changes: 45 additions & 0 deletions src/blocks/modal-manager/view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* WordPress dependencies.
*/
import { store, getElement } from '@wordpress/interactivity';

store('gatherpress/modal', {
actions: {
openModal(event) {
event.preventDefault();

const element = getElement();
const modalManager = element.ref.closest(
'.wp-block-gatherpress-modal-manager'
);

if (modalManager) {
const modal = modalManager.querySelector(
'.wp-block-gatherpress-modal'
);

if (modal) {
modal.classList.add('gatherpress--is-visible');
}
}
},
closeModal(event) {
event.preventDefault();

const element = getElement();
const modalManager = element.ref.closest(
'.wp-block-gatherpress-modal-manager'
);

if (modalManager) {
const modal = modalManager.querySelector(
'.wp-block-gatherpress-modal'
);

if (modal) {
modal.classList.remove('gatherpress--is-visible');
}
}
},
},
});
Loading

0 comments on commit f6cd284

Please sign in to comment.