Skip to content

Commit

Permalink
Merge pull request #12 from alleyinteractive/feature/add-blocks
Browse files Browse the repository at this point in the history
feature/add-blocks
  • Loading branch information
mogmarsh authored Mar 28, 2024
2 parents 6fcb2c6 + c52c8c9 commit ca7801e
Show file tree
Hide file tree
Showing 21 changed files with 884 additions and 1 deletion.
32 changes: 32 additions & 0 deletions blocks/condition/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "wp-conditional-blocks/condition",
"version": "0.1.0",
"title": "Condition",
"category": "theme",
"icon": "star-half",
"description": "Conditionally show is-true or is-false blocks",
"textdomain": "wp-conditional-blocks",
"editorScript": "file:index.ts",
"editorStyle": "file:index.css",
"render": "file:render.php",
"attributes": {
"condition": {
"type": "string"
},
"custom": {
"type": "string"
},
"post": {
"type": "string"
},
"query": {
"type": "string"
},
"index": {
"type": "object",
"default": { "": "" }
}
}
}
138 changes: 138 additions & 0 deletions blocks/condition/edit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { __ } from '@wordpress/i18n';
import { InnerBlocks, InspectorControls, useBlockProps } from '@wordpress/block-editor';
import {
PanelBody, PanelRow, SelectControl, TextControl,
} from '@wordpress/components';

import { useParentBlock } from '@alleyinteractive/block-editor-tools';

interface EditProps {
attributes: {
condition?: string;
custom?: string;
post?: string;
query?: string;
index?: object;
};
setAttributes: (attributes: any) => void;
clientId: string;
}

/**
* The wp-conditional-blocks/condition block edit function.
*
* @return {WPElement} Element to render.
*/
export default function Edit({
attributes: {
condition = '',
custom = '',
post = '',
query = '',
index = { '': '' },
},
setAttributes,
clientId,
}: EditProps) {
const { name: parentBlock } = useParentBlock(clientId) as { name?: string } || {};
const [operator, compared] = Object.entries(index)[0];

return (
<>
<div {...useBlockProps()}>
<InnerBlocks
allowedBlocks={['wp-conditional-blocks/is-true', 'wp-conditional-blocks/is-false']}
/>
</div>

<InspectorControls>
{/* @ts-ignore-next-line */}
<PanelBody
title={__('Condition', 'wp-conditional-blocks')}
initialOpen
>
{/* @ts-ignore-next-line */}
<PanelRow>
{/* @ts-ignore-next-line */}
<TextControl
label={__('Query', 'wp-conditional-blocks')}
help={__('Query condition, ie "is_home" or "is_category"', 'wp-conditional-blocks')}
onChange={(next) => setAttributes({ query: next })}
value={query}
/>
</PanelRow>
{/* @ts-ignore-next-line */}
<PanelRow>
{/* @ts-ignore-next-line */}
<TextControl
label={__('Post', 'wp-conditional-blocks')}
help={__('Post condition, ie "is_content"', 'wp-conditional-blocks')}
onChange={(next) => setAttributes({ post: next })}
value={post}
/>
</PanelRow>

{/* @ts-ignore-next-line */}
<PanelRow>
{/* @ts-ignore-next-line */}
<TextControl
label={__('Custom', 'wp-conditional-blocks')}
help={__('Custom condition, ie "is_column"', 'wp-conditional-blocks')}
onChange={(next) => setAttributes({ custom: next })}
value={custom}
/>
</PanelRow>

{/* @ts-ignore-next-line */}
<PanelRow>
{/* @ts-ignore-next-line */}
<TextControl
label={__('Condition', 'wp-conditional-blocks')}
help={__('Any other condition', 'wp-conditional-blocks')}
onChange={(next) => setAttributes({ condition: next })}
value={condition}
/>
</PanelRow>
</PanelBody>

{ parentBlock === 'wp-conditional-blocks/query' ? (
/* @ts-ignore-next-line */
<PanelBody
title={__('Index Condition', 'wp-conditional-blocks')}
>
<p>{__('Checks the index of how many times the parent condition block has been rendered, ie "Equal to 0", "Greater than 5"', 'wp-conditional-blocks')}</p>

{/* @ts-ignore-next-line */}
<PanelRow>
<SelectControl
label={__('Index Operator', 'wp-conditional-blocks')}
value={operator}
options={[
{ value: '', label: __('Select Operator', 'wp-conditional-blocks') },
{ value: '===', label: __('Equal', 'wp-conditional-blocks') },
{ value: '!==', label: __('Not equal', 'wp-conditional-blocks') },
{ value: '>', label: __('Greater than', 'wp-conditional-blocks') },
{ value: '<', label: __('Less than', 'wp-conditional-blocks') },
{ value: '>=', label: __('Greater than or equal to', 'wp-conditional-blocks') },
{ value: '<=', label: __('Less than or equal to', 'wp-conditional-blocks') },
]}
onChange={(next: string) => setAttributes({ index: { [next]: compared } })}
/>
</PanelRow>

{/* @ts-ignore-next-line */}
<PanelRow>
{/* @ts-ignore-next-line */}
<TextControl
label={__('Index compared', 'wp-conditional-blocks')}
onChange={(next) => setAttributes({ index: { [operator]: next } })}
type="number"
value={compared}
/>
</PanelRow>
</PanelBody>
) : null}
</InspectorControls>
</>
);
}
200 changes: 200 additions & 0 deletions blocks/condition/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
<?php
/**
* Block Name: Query Condition.
*
* @package wp-conditional-blocks
*
* phpcs:disable Squiz.Commenting.FunctionComment.MissingParamTag
* phpcs:disable Squiz.Commenting.FunctionComment.MissingParamName
*/

use Alley\WP\WP_Conditional_Blocks\Global_Post_Query;
use Alley\WP\WP_Conditional_Blocks\Validator\Slug_Is_In_Category;

/**
* Registers the block using the metadata loaded from the `block.json` file.
* Behind the scenes, it registers also all assets so they can be enqueued
* through the block editor in the corresponding context.
*
* @see https://developer.wordpress.org/reference/functions/register_block_type/
*/
function wp_conditional_blocks_condition_block_init(): void {
// Register the block by passing the location of block.json.
register_block_type(
__DIR__,
[
'render_callback' => fn ( $attributes, $content ) => $content,
],
);
}
add_action( 'init', 'wp_conditional_blocks_condition_block_init' );

/**
* Evaluate the result of condition block attributes.
*
* @param array{
* attrs?: array{
* query?: string|array<string|callable>,
* post?: mixed[],
* custom?: mixed[],
* condition?: string[],
* index?: array<string, int>
* }
* } $parsed_block Parsed condition block.
* @param array{'postId'?: int} $context Available context.
* @return bool
*/
function wp_conditional_blocks_condition_block_result( array $parsed_block, array $context ): bool {
global $wp_query;

$num_conditions = 0;
$num_true = 0;

$conditions = [];

if ( isset( $parsed_block['attrs'] ) ) {
$conditions = $parsed_block['attrs'];
}

if ( isset( $conditions['query'] ) && $wp_query instanceof WP_Query ) {
// Map `{"query": "is_home"} to {"query": {"is_home": true}}`.
if ( is_string( $conditions['query'] ) ) {
$conditions['query'] = array_fill_keys( (array) $conditions['query'], true );
}

foreach ( $conditions['query'] as $condition => $expect ) {
$num_conditions++;

switch ( true ) {
case 'is_singular' === $condition && ( is_string( $expect ) || is_array( $expect ) ):
$result = $wp_query->is_singular( $expect );
break;

case 'is_page' === $condition && ( is_string( $expect ) || is_array( $expect ) ):
$result = $wp_query->is_page( $expect );
break;

case 'is_tax' === $condition && ( is_string( $expect ) || is_array( $expect ) ):
$result = $wp_query->is_tax( $expect );
break;

case method_exists( $wp_query, $condition ) && is_callable( [ $wp_query, $condition ] ):
$result = call_user_func( [ $wp_query, $condition ] ) === $expect; // @phpstan-ignore-line
break;

default:
$result = false;
break;
}

if ( false === $result ) {
break;
}

$num_true++;
}
}

/*
* Checks the index of how many times the parent condition block has been rendered, like:
*
* {"index": {"===": 0}}
* {"index": {">": 2}}
* {"index": {">": 2, "<": 4}}
*
* @see \Alley\Validator\Comparison for the available operators.
*
* Note that this approach means that two identical conditions with two identical set of
* child blocks will use the same counter.
*/
if ( isset( $conditions['index'] ) ) {
$num_conditions++;

$validator = new \Laminas\Validator\ValidatorChain();

foreach ( $conditions['index'] as $operator => $compared ) {
try {
$validator->attach(
validator: new \Alley\Validator\Comparison(
[
'operator' => $operator,
'compared' => $compared,
],
),
breakChainOnFailure: true,
);
} catch ( Exception $exception ) {
// Nothing yet.
unset( $exception );
}
}

if ( count( $validator ) > 0 ) {
if ( $validator->isValid( wp_conditional_blocks_current_counter_block() ) ) {
$num_true++;
}
}
}

if (
isset( $conditions['post'] )
&& isset( $context['postId'] )
&& $context['postId'] > 0
) {
$conditions['post'] = (array) $conditions['post'];

foreach ( $conditions['post'] as $condition ) {
$num_conditions++;

if ( 'has_content' === $condition ) {
if ( '' !== get_the_content( null, false, $context['postId'] ) ) {
$num_true++;
}

continue;
}

/**
* Filters the condition block's result for the given post condition.
*
* @param bool $result Condition result.
* @param mixed $condition Condition name.
* @param int $post_id Post ID.
*/
if ( true === apply_filters( 'wp_conditional_blocks_condition_block_post_condition', false, $condition, $context['postId'] ) ) {
$num_true++;
}
}
}

if ( isset( $conditions['custom'] ) ) {
$conditions['custom'] = (array) $conditions['custom'];

foreach ( $conditions['custom'] as $condition ) {
$num_conditions++;
}
}

if ( isset( $conditions['condition'] ) ) {
$conditions['condition'] = (array) $conditions['condition'];

foreach ( $conditions['condition'] as $name ) {
$num_conditions++;

/**
* Filters the condition block's result for the given condition.
*
* @param bool $result Condition result.
* @param array $context Available context.
* @param WP_Query $wp_query Global query object.
*/
$result = apply_filters( "wp_conditional_blocks_condition_block_{$name}_condition", false, $context, $wp_query );

if ( true === $result ) {
$num_true++;
}
}
}

return $num_conditions > 0 && $num_conditions === $num_true;
}
16 changes: 16 additions & 0 deletions blocks/condition/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { registerBlockType } from '@wordpress/blocks';
import { InnerBlocks, useBlockProps } from '@wordpress/block-editor';

import edit from './edit';
import metadata from './block.json';

/* @ts-expect-error Provided types are inaccurate to the actual plugin API. */
registerBlockType(metadata, {
edit,
save: () => (
<div {...useBlockProps.save()}>
{/* @ts-ignore */}
<InnerBlocks.Content />
</div>
),
});
Loading

0 comments on commit ca7801e

Please # to comment.