-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
445 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
<?php | ||
/** | ||
* Block Name: Favorite Button | ||
* Description: A button to toggle favoriting on the current item. | ||
* | ||
* @package wporg | ||
*/ | ||
|
||
namespace WordPressdotorg\MU_Plugins\Favorite_Button_Block; | ||
|
||
defined( 'WPINC' ) || die(); | ||
|
||
add_action( 'init', __NAMESPACE__ . '\init' ); | ||
add_action( 'rest_api_init', __NAMESPACE__ . '\api_init' ); | ||
|
||
/** | ||
* Register the block. | ||
*/ | ||
function init() { | ||
register_block_type( __DIR__ . '/build' ); | ||
} | ||
|
||
/** | ||
* Get the block settings, set up the filters. | ||
*/ | ||
function get_block_settings( $post_id ) { | ||
/** | ||
* Get the settings for the favorite button, used in rendering and saving favorites. | ||
* | ||
* @param array $settings { | ||
* Array of settings for the favorite button. | ||
* | ||
* The return value should use the following format. | ||
* | ||
* @type callable $add_callback Callback function to handle adding the current | ||
* item to a user's favorites. The function will | ||
* be passed the post ID and request object. It | ||
* should return a WP_Error, true, or the updated | ||
* count of favorite items. | ||
* @type callable $delete_callback Callback function to handle removing the current | ||
* item from a user's favorites. Same arguments and | ||
* return value as the `add_callback`. | ||
* @type int $count Number of times this item has been favorited. | ||
* @type bool $is_favorite Check if the current item is favorited. | ||
* } | ||
* @param int $post_id The current post ID. | ||
*/ | ||
$settings = apply_filters( 'wporg_favorite_button_settings', array(), $post_id ); | ||
if ( empty( $settings ) ) { | ||
return false; | ||
} | ||
|
||
$settings = wp_parse_args( | ||
$settings, | ||
array( | ||
'add_callback' => '__return_false', | ||
'delete_callback' => '__return_false', | ||
'count' => 0, | ||
'is_favorite' => false, | ||
) | ||
); | ||
|
||
return $settings; | ||
} | ||
|
||
/** | ||
* Initialize the API endpoints. | ||
*/ | ||
function api_init() { | ||
$namespace = 'wporg/v1'; | ||
$args = array( | ||
'id' => array( | ||
'validate_callback' => function( $param, $request, $key ) { | ||
return is_numeric( $param ); | ||
}, | ||
'required' => true, | ||
), | ||
); | ||
register_rest_route( | ||
$namespace, | ||
'/favorite', | ||
array( | ||
'methods' => \WP_REST_Server::CREATABLE, | ||
'callback' => __NAMESPACE__ . '\add_favorite', | ||
'args' => $args, | ||
'permission_callback' => 'is_user_logged_in', | ||
) | ||
); | ||
register_rest_route( | ||
$namespace, | ||
'/favorite', | ||
array( | ||
'methods' => \WP_REST_Server::DELETABLE, | ||
'callback' => __NAMESPACE__ . '\delete_favorite', | ||
'args' => $args, | ||
'permission_callback' => 'is_user_logged_in', | ||
) | ||
); | ||
} | ||
|
||
/** | ||
* Set the favorite status for a given item. | ||
*/ | ||
function add_favorite( $request ) { | ||
$id = intval( $request['id'] ); | ||
$settings = get_block_settings( $id ); | ||
$result = call_user_func( $settings['add_callback'], $id, $request ); | ||
|
||
if ( is_wp_error( $result ) ) { | ||
return $result; | ||
} else if ( false !== $result ) { | ||
if ( is_numeric( $result ) ) { | ||
return new \WP_REST_Response( $result, 200 ); | ||
} else { | ||
return new \WP_REST_Response( [ 'success' => true ] ); | ||
} | ||
} | ||
|
||
return new \WP_Error( | ||
'favorite-failed', | ||
// Users should never see this error, so we can leave it untranslated. | ||
'Unable to favorite this item.', | ||
array( 'status' => 500 ) | ||
); | ||
} | ||
|
||
/** | ||
* Remove the favorite status for a given item. | ||
*/ | ||
function delete_favorite( $request ) { | ||
$id = intval( $request['id'] ); | ||
$settings = get_block_settings( $id ); | ||
$result = call_user_func( $settings['delete_callback'], $id, $request ); | ||
|
||
if ( is_wp_error( $result ) ) { | ||
return $result; | ||
} else if ( false !== $result ) { | ||
if ( is_numeric( $result ) ) { | ||
return new \WP_REST_Response( $result, 200 ); | ||
} else { | ||
return new \WP_REST_Response( [ 'success' => true ] ); | ||
} | ||
} | ||
|
||
return new \WP_Error( | ||
'unfavorite-failed', | ||
// Users should never see this error, so we can leave it untranslated. | ||
'Unable to remove this item from favorites.', | ||
array( 'status' => 500 ) | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
<?php | ||
/** | ||
* Output the favorite button block. | ||
* | ||
* @package wporg | ||
*/ | ||
|
||
namespace WordPressdotorg\MU_Plugins\Favorite_Button_Block; | ||
|
||
$settings = get_block_settings( $block->context['postId'] ); | ||
if ( ! $settings ) { | ||
return ''; | ||
} | ||
|
||
$user_id = get_current_user_id(); | ||
$show_count = $attributes['showCount'] ?? false; | ||
$variant = $attributes['variant'] ?? 'default'; | ||
|
||
if ( ! $user_id && ! $show_count && 'small' !== $variant ) { | ||
return ''; | ||
} | ||
|
||
// Manually enqueue this script, so that it's available for the interactivity view script. | ||
wp_enqueue_script( 'wp-api-fetch' ); | ||
wp_enqueue_script( 'wp-a11y' ); | ||
|
||
$is_favorite = $settings['is_favorite']; | ||
|
||
$classes = array( | ||
$is_favorite ? 'is-favorite' : '', | ||
( 'small' === $variant ) ? 'is-variant-small' : '', | ||
); | ||
$classes = implode( ' ', array_filter( $classes ) ); | ||
|
||
$labels = array( | ||
'add' => __( 'Add to favorites', 'wporg' ), | ||
'remove' => __( 'Remove from favorites', 'wporg' ), | ||
'favorited' => __( 'Favorited', 'wporg' ), | ||
'unfavorited' => __( 'Removed from favorites', 'wporg' ), | ||
// translators: %s: number of users who favorited this item. | ||
'screenReader' => __( 'Favorited %s times', 'wporg' ), | ||
); | ||
|
||
$sr_label = sprintf( | ||
// translators: %s: number of users who favorited this item. | ||
_n( 'Favorited %s time', 'Favorited %s times', $settings['count'], 'wporg' ), | ||
$settings['count'] | ||
); | ||
|
||
// Initial state to pass to Interactivity API. | ||
$init_state = [ | ||
'id' => $block->context['postId'], | ||
'count' => $settings['count'], | ||
'isFavorite' => $is_favorite, | ||
'label' => $labels, | ||
]; | ||
$encoded_state = wp_json_encode( $init_state ); | ||
|
||
?> | ||
<div | ||
<?php echo get_block_wrapper_attributes( [ 'class' => $classes ] ); // phpcs:ignore ?> | ||
data-wp-interactive="wporg/favorite-button" | ||
data-wp-context="<?php echo esc_attr( $encoded_state ); ?>" | ||
data-wp-class--is-favorite="context.isFavorite" | ||
> | ||
<?php if ( $user_id ) : ?> | ||
<button | ||
class="wporg-favorite-button__button" | ||
disabled="disabled" | ||
data-wp-bind--disabled="!context.id" | ||
data-wp-on--click="actions.triggerAction" | ||
> | ||
<?php else : ?> | ||
<span class="wporg-favorite-button__button"> | ||
<?php endif; ?> | ||
|
||
<svg class="is-heart-filled" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" aria-hidden="true" focusable="false"> | ||
<path d="M12 19.5c-2.213-1.953-4.61-3.882-6.394-6.247-.503-.667-.855-1.28-1.056-1.84a5.06 5.06 0 0 1-.3-1.717c0-1.195.407-2.193 1.22-2.994C6.285 5.9 7.299 5.5 8.513 5.5c.672 0 1.312.14 1.919.42.607.28 1.13.674 1.569 1.182A4.588 4.588 0 0 1 15.488 5.5c1.214 0 2.228.4 3.041 1.202.814.8 1.221 1.799 1.221 2.994 0 .585-.1 1.157-.3 1.717-.2.56-.552 1.173-1.056 1.84-1.784 2.365-4.18 4.294-6.394 6.247Z"/> | ||
</svg> | ||
<svg class="is-heart-outline" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" aria-hidden="true" focusable="false"> | ||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.967 6.21c.385.243.729.54 1.033.892A4.601 4.601 0 0 1 15.488 5.5c1.214 0 2.228.4 3.041 1.202.814.8 1.221 1.799 1.221 2.994 0 .585-.1 1.157-.3 1.717-.2.56-.552 1.173-1.056 1.84-1.5 1.988-3.431 3.667-5.323 5.312-.36.312-.718.623-1.071.935-.353-.312-.711-.623-1.07-.935-1.893-1.645-3.824-3.325-5.324-5.312-.503-.667-.855-1.28-1.056-1.84a5.06 5.06 0 0 1-.3-1.717c0-1.195.407-2.193 1.22-2.994C6.285 5.9 7.299 5.5 8.513 5.5c.672 0 1.312.14 1.919.42.186.086.365.183.536.29ZM12 17.507l.116-.1c1.892-1.647 3.684-3.207 5.08-5.057.447-.592.708-1.07.841-1.443.143-.4.213-.8.213-1.21 0-.815-.26-1.422-.773-1.926-.514-.507-1.142-.771-1.99-.771a3.088 3.088 0 0 0-2.352 1.082L12 9.397l-1.135-1.315A3.088 3.088 0 0 0 8.512 7c-.847 0-1.475.264-1.99.77-.512.505-.772 1.112-.772 1.926 0 .41.07.812.213 1.211.133.372.394.851.84 1.443 1.397 1.85 3.189 3.41 5.081 5.056l.116.101Z"/> | ||
</svg> | ||
|
||
<?php if ( $show_count ) : ?> | ||
<span class="wporg-favorite-button__count"> | ||
<span class="screen-reader-text" data-wp-text="state.labelScreenReader"> | ||
<?php echo esc_html( $sr_label ); ?> | ||
</span> | ||
<span aria-hidden="true" data-wp-text="state.labelCount"><?php echo esc_html( $settings['count'] ); ?></span> | ||
</span> | ||
<?php endif; ?> | ||
|
||
<?php if ( $user_id ) : ?> | ||
<span class="wporg-favorite-button__label screen-reader-text" data-wp-text="state.labelAction"> | ||
<?php echo esc_html( $is_favorite ? $labels['remove'] : $labels['add'] ); ?> | ||
</span> | ||
<?php endif; ?> | ||
|
||
<?php if ( $user_id ) : ?> | ||
</button> | ||
<?php else : ?> | ||
</span> | ||
<?php endif; ?> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
{ | ||
"$schema": "https://schemas.wp.org/trunk/block.json", | ||
"apiVersion": 2, | ||
"name": "wporg/favorite-button", | ||
"version": "0.1.0", | ||
"title": "Favorite Button", | ||
"category": "design", | ||
"icon": "", | ||
"description": "A button to toggle favoriting on the current item.", | ||
"textdomain": "wporg", | ||
"supports": { | ||
"html": false | ||
}, | ||
"attributes": { | ||
"showCount": { | ||
"type": "boolean", | ||
"default": false | ||
}, | ||
"variant": { | ||
"type": "string", | ||
"enum": [ "default", "small" ], | ||
"default": "default" | ||
} | ||
}, | ||
"usesContext": [ "postId", "postType" ], | ||
"editorScript": "file:./index.js", | ||
"style": "file:./style-index.css", | ||
"viewScriptModule": "file:./view.js", | ||
"render": "file:../render.php" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { registerBlockType } from '@wordpress/blocks'; | ||
import ServerSideRender from '@wordpress/server-side-render'; | ||
import { useBlockProps } from '@wordpress/block-editor'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import metadata from './block.json'; | ||
import './style.scss'; | ||
|
||
function Edit( { attributes, name } ) { | ||
return ( | ||
<div { ...useBlockProps() }> | ||
<ServerSideRender block={ name } attributes={ attributes } skipBlockSupportAttributes /> | ||
</div> | ||
); | ||
} | ||
|
||
registerBlockType( metadata.name, { | ||
edit: Edit, | ||
save: () => null, | ||
} ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
:where(.wp-block-wporg-favorite-button) { | ||
.wporg-favorite-button__button { | ||
margin: 0; | ||
padding: | ||
var(--wp--custom--button--small--spacing--padding--top) | ||
calc(var(--wp--custom--button--small--spacing--padding--right) - 4px) | ||
var(--wp--custom--button--small--spacing--padding--bottom) | ||
calc(var(--wp--custom--button--small--spacing--padding--left) - 4px); | ||
background: none; | ||
border: 1px solid var(--wp--preset--color--light-grey-1); | ||
border-radius: 2px; | ||
box-shadow: none; | ||
font-size: 14px; | ||
color: var(--wp--preset--color--charcoal-1); | ||
|
||
&:where(button) { | ||
cursor: pointer; | ||
|
||
&:hover { | ||
background-color: var(--wp--preset--color--light-grey-2); | ||
} | ||
|
||
&:focus { | ||
border-color: transparent; | ||
} | ||
|
||
&:active { | ||
border-color: transparent; | ||
background-color: var(--wp--preset--color--charcoal-1); | ||
color: var(--wp--preset--color--white); | ||
|
||
path { | ||
fill: currentcolor; | ||
} | ||
} | ||
} | ||
} | ||
|
||
> * { | ||
|
||
/* Align children. */ | ||
display: flex !important; | ||
align-items: center; | ||
gap: calc(var(--wp--preset--spacing--10) / 2); | ||
} | ||
|
||
svg { | ||
height: 24px; | ||
width: 24px; | ||
overflow: visible; | ||
|
||
path { | ||
fill: var(--wp--preset--color--charcoal-4); | ||
} | ||
|
||
&.is-heart-outline { | ||
display: block; | ||
} | ||
|
||
&.is-heart-filled { | ||
display: none; | ||
} | ||
} | ||
|
||
&.is-favorite { | ||
svg.is-heart-outline { | ||
display: none; | ||
} | ||
|
||
svg.is-heart-filled { | ||
display: block; | ||
} | ||
} | ||
|
||
&.is-variant-small { | ||
.wporg-favorite-button__button { | ||
border: none; | ||
padding: 2px 4px; | ||
} | ||
} | ||
} |
Oops, something went wrong.