From c3478b345002fe5a7421abdc9f9bc3c34a5adad6 Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Tue, 12 Mar 2024 13:20:10 -0400 Subject: [PATCH] Add new block-based theme (#624) This block-based theme is a work-in-progress, aimed at quickly updating the look and feel of the pattern directory to match the new redesign framework, and use the components (blocks) created for filtering and displaying directory content. --- .github/workflows/build-blocks.yml | 45 ++ .gitignore | 1 + package.json | 8 +- .../functions.php | 412 ++++++++++++++ .../inc/block-config.php | 509 ++++++++++++++++++ .../inc/shortcodes.php | 30 ++ .../wporg-pattern-directory-2024/package.json | 31 ++ .../parts/footer.html | 1 + .../parts/header.html | 17 + .../patterns/_footer.php | 51 ++ .../patterns/_grid-favorites.php | 103 ++++ .../patterns/_grid-front-page.php | 63 +++ .../patterns/_grid-mine.php | 77 +++ .../patterns/_grid.php | 75 +++ .../patterns/_logged-out-favorites.php | 40 ++ .../patterns/_logged-out-patterns.php | 40 ++ .../patterns/front-page-header.php | 37 ++ .../patterns/single-my-pattern.php | 76 +++ .../patterns/single-pattern.php | 107 ++++ .../src/blocks/copy-button/block.json | 25 + .../src/blocks/copy-button/index.js | 16 + .../src/blocks/copy-button/index.php | 15 + .../src/blocks/copy-button/render.php | 31 ++ .../src/blocks/copy-button/style.scss | 16 + .../src/blocks/copy-button/view.js | 39 ++ .../src/blocks/delete-button/block.json | 20 + .../src/blocks/delete-button/index.js | 16 + .../src/blocks/delete-button/index.php | 15 + .../src/blocks/delete-button/render.php | 38 ++ .../src/blocks/delete-button/style.scss | 11 + .../src/blocks/delete-button/view.js | 24 + .../src/blocks/favorite-button/block.json | 25 + .../src/blocks/favorite-button/index.js | 16 + .../src/blocks/favorite-button/index.php | 15 + .../src/blocks/favorite-button/render.php | 104 ++++ .../src/blocks/favorite-button/style.scss | 54 ++ .../src/blocks/favorite-button/view.js | 47 ++ .../pattern-preview/controls/block.json | 25 + .../blocks/pattern-preview/controls/index.js | 18 + .../pattern-preview/controls/render.php | 82 +++ .../pattern-preview/controls/style.scss | 40 ++ .../blocks/pattern-preview/controls/view.js | 73 +++ .../blocks/pattern-preview/frame/block.json | 25 + .../src/blocks/pattern-preview/frame/edit.js | 14 + .../src/blocks/pattern-preview/frame/index.js | 16 + .../blocks/pattern-preview/frame/render.php | 45 ++ .../blocks/pattern-preview/frame/style.scss | 16 + .../src/blocks/pattern-preview/index.php | 16 + .../src/blocks/pattern-thumbnail/block.json | 35 ++ .../src/blocks/pattern-thumbnail/edit.js | 14 + .../src/blocks/pattern-thumbnail/index.js | 16 + .../src/blocks/pattern-thumbnail/index.php | 22 + .../src/blocks/pattern-thumbnail/render.php | 78 +++ .../src/blocks/pattern-thumbnail/style.scss | 74 +++ .../src/blocks/pattern-thumbnail/view.js | 88 +++ .../src/blocks/post-status/block.json | 19 + .../src/blocks/post-status/index.js | 16 + .../src/blocks/post-status/index.php | 66 +++ .../src/blocks/post-status/style.scss | 36 ++ .../src/blocks/report-pattern/block.json | 20 + .../src/blocks/report-pattern/index.js | 16 + .../src/blocks/report-pattern/index.php | 15 + .../src/blocks/report-pattern/render.php | 110 ++++ .../src/blocks/report-pattern/style.scss | 109 ++++ .../src/blocks/report-pattern/view.js | 22 + .../src/blocks/status-notice/block.json | 18 + .../src/blocks/status-notice/index.js | 15 + .../src/blocks/status-notice/index.php | 94 ++++ .../src/utils/copy-to-clipboard.js | 29 + .../src/utils/dynamic-edit.js | 20 + .../wporg-pattern-directory-2024/style.css | 136 +++++ .../templates/archive.html | 21 + .../templates/front-page.html | 17 + .../templates/index.html | 21 + .../templates/page-favorites.html | 16 + .../templates/page-my-patterns.html | 16 + .../templates/page.html | 9 + .../templates/search.html | 21 + .../templates/single.html | 9 + .../wporg-pattern-directory-2024/theme.json | 34 ++ yarn.lock | 12 + 81 files changed, 3861 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/build-blocks.yml create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/functions.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/inc/block-config.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/inc/shortcodes.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/package.json create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/parts/footer.html create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/parts/header.html create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_footer.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_grid-favorites.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_grid-front-page.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_grid-mine.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_grid.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_logged-out-favorites.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_logged-out-patterns.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/front-page-header.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/single-my-pattern.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/single-pattern.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/block.json create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/index.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/index.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/render.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/style.scss create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/view.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/block.json create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/index.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/index.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/render.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/style.scss create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/view.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/block.json create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/index.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/index.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/render.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/style.scss create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/view.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/block.json create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/index.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/render.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/style.scss create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/view.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/block.json create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/edit.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/index.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/render.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/style.scss create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/index.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/block.json create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/edit.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/index.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/index.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/render.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/style.scss create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/view.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/post-status/block.json create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/post-status/index.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/post-status/index.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/post-status/style.scss create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/block.json create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/index.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/index.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/render.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/style.scss create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/view.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/status-notice/block.json create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/status-notice/index.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/status-notice/index.php create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/utils/copy-to-clipboard.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/src/utils/dynamic-edit.js create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/style.css create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/templates/archive.html create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/templates/front-page.html create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/templates/index.html create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/templates/page-favorites.html create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/templates/page-my-patterns.html create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/templates/page.html create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/templates/search.html create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/templates/single.html create mode 100644 public_html/wp-content/themes/wporg-pattern-directory-2024/theme.json diff --git a/.github/workflows/build-blocks.yml b/.github/workflows/build-blocks.yml new file mode 100644 index 000000000..9aad982e0 --- /dev/null +++ b/.github/workflows/build-blocks.yml @@ -0,0 +1,45 @@ +name: Build new theme and push to `build-blocks` branch. + +on: + push: + branches: + - trunk + paths: + - public_html/wp-content/themes/wporg-pattern-directory-2024/** + # Enable manually running action if necessary. + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + + - name: Setup + uses: WordPress/wporg-repo-tools/.github/actions/setup@trunk + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Remove build artifacts + run: | + rm -rf public_html/wp-content/themes/wporg-pattern-directory-2024/node_modules + + - name: Ignore .gitignore + run: | + git add public_html/wp-content/themes/wporg-pattern-directory-2024/* --force + + - name: Append build number to version + run: | + current_version=$(grep -oP 'Version: \K[0-9]+\.[0-9]+\.[0-9]+' public_html/wp-content/themes/wporg-pattern-directory-2024/style.css) + new_version="${current_version}-${GITHUB_SHA::7}" + sed -i "s/Version: $current_version/Version: $new_version/" public_html/wp-content/themes/wporg-pattern-directory-2024/style.css + + - name: Commit and push + # Using a specific hash here instead of a tagged version, for risk mitigation, since this action modifies our repo. + uses: actions-js/push@a52398fac807b0c1e5f1492c969b477c8560a0ba # 1.3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: build-blocks + force: true + message: 'Build: ${{ github.sha }}' diff --git a/.gitignore b/.gitignore index 3e2b8bb7d..c42925c35 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ !/public_html/wp-content/plugins/pattern-directory !/public_html/wp-content/plugins/pattern-translations !/public_html/wp-content/themes/pattern-directory +!/public_html/wp-content/themes/wporg-pattern-directory-2024 # Ignore built files inside our plugins & themes. /public_html/wp-content/themes/pattern-directory/css/*.css diff --git a/package.json b/package.json index ba3b08898..cdbc19486 100644 --- a/package.json +++ b/package.json @@ -14,11 +14,12 @@ "build:creator": "yarn workspace wporg-pattern-creator build", "build:directory": "yarn workspace wporg-pattern-directory build", "build:theme:old": "yarn workspace wporg-pattern-directory-theme build", - "build:theme": "yarn workspace wporg-pattern-directory-2022-theme build", + "build:theme": "yarn workspace wporg-pattern-directory-2024-theme build", "start:creator": "yarn workspace wporg-pattern-creator start", "start:directory": "yarn workspace wporg-pattern-directory start", "start:theme:old": "yarn workspace wporg-pattern-directory-theme start", - "start:theme": "yarn workspace wporg-pattern-directory-2022-theme start", + "start:theme": "yarn workspace wporg-pattern-directory-2024-theme start", + "setup:tools": "echo \"Not used.\"", "create": "./bin/index.sh", "wp-env": "wp-env", "lint:php": "composer run lint", @@ -28,6 +29,7 @@ "workspaces": [ "public_html/wp-content/plugins/pattern-creator", "public_html/wp-content/plugins/pattern-directory", - "public_html/wp-content/themes/pattern-directory" + "public_html/wp-content/themes/pattern-directory", + "public_html/wp-content/themes/wporg-pattern-directory-2024" ] } diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/functions.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/functions.php new file mode 100644 index 000000000..272bdda5c --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/functions.php @@ -0,0 +1,412 @@ + $post_id, + 'post_status' => 'draft', + ) + ); + if ( $success ) { + // Reload the page without the action. + wp_safe_redirect( get_the_permalink() ); + } else { + // Reload the page with an error flag. + $url = add_query_arg( + array( + 'error' => 'draft-failed' + ), + get_the_permalink() + ); + wp_safe_redirect( $url ); + } + } + } else if ( 'report' === $action ) { + if ( wp_verify_nonce( $nonce, 'report-' . $post_id ) && current_user_can( 'read' ) ) { + $success = wp_insert_post( + array( + 'post_type' => FLAG_POST_TYPE, + 'post_parent' => $post_id, + 'post_excerpt' => sanitize_text_field( $_POST['report-details'] ), + 'post_status' => PENDING_STATUS, + 'tax_input' => array( + 'wporg-pattern-flag-reason' => intval( $_POST['report-reason'] ), + ), + ) + ); + if ( $success ) { + $args = array( + 'success' => 'reported' + ); + } else { + $args = array( + 'error' => 'report-failed' + ); + } + } else { + $args = array( + 'error' => 'logged-out' + ); + } + + wp_safe_redirect( add_query_arg( $args, get_the_permalink() ) ); + } +} + +/** + * Add custom query parameters. + * + * @param array $query_vars + * + * @return array + */ +function add_patterns_query_vars( $query_vars ) { + $query_vars[] = 'curation'; + $query_vars[] = 'status'; + return $query_vars; +} + +/** + * Update the query to show patters according to the "curation" & + * sort order filters. + * + * @param WP_Query $query The WP_Query instance (passed by reference). + */ +function modify_patterns_query( $query ) { + if ( is_admin() || ! $query->is_main_query() ) { + return; + } + + // If `curation` is passed and either `core` or `community`, we should + // filter the result. If `curation=all`, no filtering is needed. + $curation = $query->get( 'curation' ); + if ( $curation ) { + $tax_query = isset( $query->tax_query->queries ) ? $query->tax_query->queries : []; + if ( 'core' === $curation ) { + // Patterns with the core keyword. + $tax_query['core_keyword'] = array( + 'taxonomy' => 'wporg-pattern-keyword', + 'field' => 'slug', + 'terms' => [ 'core' ], + 'operator' => 'IN', + ); + } else if ( 'community' === $curation ) { + // Patterns without the core keyword. + $tax_query['core_keyword'] = array( + 'taxonomy' => 'wporg-pattern-keyword', + 'field' => 'slug', + 'terms' => [ 'core' ], + 'operator' => 'NOT IN', + ); + } + $query->set( 'tax_query', $tax_query ); + } + + if ( str_ends_with( $query->get( 'orderby' ), '_desc' ) ) { + $orderby = str_replace( '_desc', '', $query->get( 'orderby' ) ); + $query->set( 'orderby', $orderby ); + $query->set( 'order', 'desc' ); + } else if ( str_ends_with( $query->get( 'orderby' ), '_asc' ) ) { + $orderby = str_replace( '_asc', '', $query->get( 'orderby' ) ); + $query->set( 'orderby', $orderby ); + $query->set( 'order', 'asc' ); + } + + if ( $query->get( 'orderby' ) === 'favorite_count' ) { + $query->set( 'orderby', 'meta_value_num' ); + $query->set( 'meta_key', 'wporg-pattern-favorites' ); + } + + if ( ! $query->is_singular() ) { + $query->set( 'post_type', array( POST_TYPE ) ); + + // The `orderby_locale` meta_query will be transformed into a query orderby by Pattern_Post_Type\filter_orderby_locale(). + $query->set( 'meta_query', array( + 'orderby_locale' => array( + 'key' => 'wpop_locale', + 'compare' => 'IN', + // Order in value determines result order + 'value' => array( get_locale(), 'en_US' ), + ), + ) ); + } +} + +/** + * Set up query customizations for the Query Loop block. + * + * @param array $query Array containing parameters for `WP_Query` as parsed by the block context. + * @param WP_Block $block Block instance. + * @param int $page Current query's page. + * + * @return array + */ +function modify_query_loop_block_query_vars( $query, $block, $page ) { + global $wp_query; + + // Return early if this is a pattern view page. + if ( isset( $wp_query->query_vars['view'] ) ) { + return $query; + } + + if ( ! isset( $query['posts_per_page'] ) ) { + $query['posts_per_page'] = 24; + } + + if ( isset( $page ) && ! isset( $query['offset'] ) ) { + $query['paged'] = $page; + } + + if ( isset( $block->context['query']['curation'] ) ) { + if ( 'core' === $block->context['query']['curation'] ) { + // Patterns with the core keyword. + $query['tax_query']['core_keyword'] = array( + 'taxonomy' => 'wporg-pattern-keyword', + 'field' => 'slug', + 'terms' => 'core', + 'operator' => 'IN', + ); + } else if ( 'community' === $block->context['query']['curation'] ) { + // Patterns without the core keyword. + $query['tax_query']['core_keyword'] = array( + 'taxonomy' => 'wporg-pattern-keyword', + 'field' => 'slug', + 'terms' => [ 'core' ], + 'operator' => 'NOT IN', + ); + } + } + + if ( isset( $block->context['query']['orderBy'] ) && 'favorite_count' === $block->context['query']['orderBy'] ) { + $query['orderby'] = 'meta_value_num'; + $query['meta_key'] = 'wporg-pattern-favorites'; + } + + // Query Loops on My Patterns & Favorites pages + if ( is_page( [ 'my-patterns', 'favorites' ] ) ) { + // Get these values from the global wp_query, they're passed via the URL. + if ( isset( $wp_query->query['pattern-categories'] ) ) { + if ( ! isset( $query['tax_query'] ) || ! is_array( $query['tax_query'] ) ) { + $query['tax_query'] = array(); + } + $query['tax_query'][] = array( + 'taxonomy' => 'wporg-pattern-category', + 'field' => 'slug', + 'terms' => $wp_query->query['pattern-categories'], + 'include_children' => false, + ); + } + + if ( isset( $wp_query->query['orderby'] ) ) { + if ( str_ends_with( $wp_query->query['orderby'], '_desc' ) ) { + $orderby = str_replace( '_desc', '', $wp_query->query['orderby'] ); + $query['orderby'] = $orderby; + $query['order'] = 'desc'; + } else if ( str_ends_with( $wp_query->query['orderby'], '_asc' ) ) { + $orderby = str_replace( '_asc', '', $wp_query->query['orderby'] ); + $query['orderby'] = $orderby; + $query['order'] = 'asc'; + } + } + + if ( is_page( 'my-patterns' ) ) { + $user_id = get_current_user_id(); + if ( $user_id ) { + $query['post_type'] = 'wporg-pattern'; + $query['post_status'] = 'any'; + $query['author'] = get_current_user_id(); + } else { + $query['post__in'] = [ -1 ]; + } + + if ( isset( $wp_query->query['status'] ) ) { + $query['post_status'] = $wp_query->query['status']; + } + } + + if ( is_page( 'favorites' ) ) { + $favorites = get_favorites(); + if ( ! empty( $favorites ) ) { + $query['post__in'] = get_favorites(); + } else { + $query['post__in'] = [ -1 ]; + } + } + } + + // The `orderby_locale` meta_query will be transformed into a query orderby by Pattern_Post_Type\filter_orderby_locale(). + $query['meta_query'] = array( + 'orderby_locale' => array( + 'key' => 'wpop_locale', + 'compare' => 'IN', + // Order in value determines result order + 'value' => array( get_locale(), 'en_US' ), + ), + ); + + return $query; +} + +/** + * Override Query Loop parameters if an `_id` property is found. + * + * This is a workaround to allow setting more complicated queries. For example, + * using the current author & excluding the current post. + * + * @param array $query Array containing parameters for `WP_Query` as parsed by the block context. + * @param WP_Block $block Block instance. + * + * @return array + */ +function custom_query_loop_by_id( $query, $block ) { + if ( ! isset( $block->context['query']['_id'] ) ) { + return $query; + } + + $current_post = get_post(); + if ( 'more-by-author' === $block->context['query']['_id'] && $current_post && $current_post->post_author ) { + $query['author'] = $current_post->post_author; + $query['post__not_in'] = [ $current_post->ID ]; + $query['post_type'] = 'wporg-pattern'; + } + + if ( 'empty-favorites' === $block->context['query']['_id'] ) { + unset( $query['post__in'] ); + $query['post_type'] = 'wporg-pattern'; + $query['orderby'] = 'meta_value_num'; + $query['meta_key'] = 'wporg-pattern-favorites'; + } + + return $query; +} + +/** + * Get the preview URL for the current pattern. + * + * @param int|WP_Post $post Post ID or post object. + * + * @return string The pattern `view` URL. + */ +function get_pattern_preview_url( $post = 0 ) { + $view_url = add_query_arg( 'view', true, get_permalink( $post ) ); + return apply_filters( 'wporg_pattern_preview_url', $view_url, $post ); +} + +/** + * Get the count of all patterns on the site (for the current locale). + * + * @return int + */ +function get_patterns_count() { + global $wpdb; + $locale = get_locale(); + + // Cache for an hour to avoid extra DB lookup. + $cache_key = 'wporg-patterns-count-' . $locale; + $ttl = HOUR_IN_SECONDS; + + $count = get_transient( $cache_key ); + if ( ! $count ) { + $sql = "SELECT COUNT(*) FROM $wpdb->posts + INNER JOIN $wpdb->postmeta + ON {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id + WHERE {$wpdb->posts}.post_status = 'publish' + AND {$wpdb->postmeta}.meta_key = 'wpop_locale' + AND {$wpdb->postmeta}.meta_value = '%s'"; + + $count = $wpdb->get_var( $wpdb->prepare( $sql, $locale ) ); + set_transient( $cache_key, $count, $ttl ); + } + return $count; +} + +/** + * Checks whether the user has a pending flag for a specific pattern. + * + * @return bool + */ +function user_has_flagged_pattern() { + $args = array( + 'author' => get_current_user_id(), + 'post_parent' => get_the_ID(), + 'post_type' => FLAG_POST_TYPE, + 'post_status' => PENDING_STATUS, + ); + + $items = new \WP_Query( $args ); + + return $items->have_posts(); +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/inc/block-config.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/inc/block-config.php new file mode 100644 index 000000000..4e8c75b45 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/inc/block-config.php @@ -0,0 +1,509 @@ + __( 'Edit label', 'wporg-patterns' ), + 'uses_context' => [ 'postId' ], + 'get_value_callback' => function( $args, $block ) { + $post_id = $block->context['postId']; + /* translators: %s: Post title. Only visible to screen readers. */ + return sprintf( + __( 'Edit "%s"', 'wporg-patterns' ), + get_the_title( $post_id ) + ); + } + ) + ); + + register_block_bindings_source( + 'wporg-pattern/edit-url', + array( + 'label' => __( 'Edit link', 'wporg-patterns' ), + 'uses_context' => [ 'postId' ], + 'get_value_callback' => function( $args, $block ) { + $post_id = $block->context['postId']; + return site_url( "pattern/$post_id/edit/" ); + } + ) + ); +} + +/** + * Get a list of the currently-applied filters. + * + * @param boolean $include_search Whether the result should include the search term. + * + * @return array + */ +function get_applied_filter_list( $include_search = true ) { + global $wp_query; + $terms = []; + $taxes = [ + 'pattern-categories' => 'wporg-pattern-category', + ]; + foreach ( $taxes as $query_var => $taxonomy ) { + if ( ! isset( $wp_query->query[ $query_var ] ) ) { + continue; + } + $values = (array) $wp_query->query[ $query_var ]; + foreach ( $values as $value ) { + $key = ( 'cat' === $query_var ) ? 'id' : 'slug'; + $term = get_term_by( $key, $value, $taxonomy ); + if ( $term ) { + $terms[] = $term; + } + } + } + if ( $include_search && isset( $wp_query->query['s'] ) ) { + $terms[] = array( 'name' => $wp_query->query['s'] ); + } + return $terms; +} + +/** + * Get the destination for query-filter submission based on the current page. + * + * @return string + */ +function get_filter_action_url() { + global $wp; + if ( is_page( 'favorites' ) || is_page( 'my-patterns' ) || is_author() ) { + return home_url( $wp->request ); + } + return home_url( '/archives/' ); +} + +/** + * Update the query total label to reflect "patterns" found. + * + * @param string $label The maybe-pluralized label to use, a result of `_n()`. + * @param int $found_posts The number of posts to use for determining pluralization. + * @return string Updated string with total placeholder. + */ +function update_query_total_label( $label, $found_posts ) { + if ( is_front_page() ) { + // Override the current query count, instead display the total number of patterns. + $count = get_patterns_count(); + + /* translators: %s: the result count. */ + return sprintf( _n( '%s pattern', '%s patterns', $count, 'wporg' ), number_format_i18n( $count ) ); + } + /* translators: %s: the result count. */ + return _n( '%s pattern', '%s patterns', $found_posts, 'wporg' ); +} + +/** + * Get the list of categories for the filters. + * + * @param array $options The options for this filter. + * @return array New list of category options. + */ +function get_category_options( $options ) { + global $wp_query; + + $args = array( + 'taxonomy' => 'wporg-pattern-category', + 'orderby' => 'name', + ); + $categories = get_terms( $args ); + + $selected = isset( $wp_query->query['pattern-categories'] ) ? (array) $wp_query->query['pattern-categories'] : array(); + + $count = count( $selected ); + $label = sprintf( + /* translators: The dropdown label for filtering, %s is the selected term count. */ + _n( 'Categories %s', 'Categories %s', $count, 'wporg' ), + $count + ); + + return array( + 'label' => $label, + 'title' => __( 'Categories', 'wporg' ), + 'key' => 'pattern-categories', + 'action' => get_filter_action_url(), + 'options' => array_combine( wp_list_pluck( $categories, 'slug' ), wp_list_pluck( $categories, 'name' ) ), + 'selected' => $selected, + ); +} + +/** + * Provide a list of curation options. + * + * @param array $options The options for this filter. + * @return array New list of curation options. + */ +function get_curation_options( $options ) { + global $wp_query; + $current = strtolower( $wp_query->get( 'curation' ) ); + + $label = __( 'Filter by', 'wporg' ); + switch ( $current ) { + case 'community': + $label = __( 'Filter by: Community', 'wporg' ); + break; + case 'core': + $label = __( 'Filter by: Curated', 'wporg' ); + break; + } + + // Show the correct filters on the front page. + if ( is_front_page() ) { + $current = 'core'; + $label = __( 'Filter by: Curated', 'wporg' ); + } + + return array( + 'label' => $label, + 'title' => __( 'Filter', 'wporg' ), + 'key' => 'curation', + 'action' => get_filter_action_url(), + 'options' => array( + 'community' => __( 'Community', 'wporg' ), + 'core' => __( 'Curated', 'wporg' ), + ), + 'selected' => [ $current ], + ); +} + +/** + * Provide a list of sort options. + * + * @param array $options The options for this filter. + * @return array New list of sort options. + */ +function get_sort_options( $options ) { + global $wp_query; + $orderby = strtolower( $wp_query->get( 'orderby' ) ); + $order = strtolower( $wp_query->get( 'order' ) ); + $sort = $orderby . '_' . $order; + + $label = __( 'Sort', 'wporg' ); + switch ( $sort ) { + case 'date_desc': + $label = __( 'Sort: Newest', 'wporg' ); + break; + case 'date_asc': + $label = __( 'Sort: Oldest', 'wporg' ); + break; + } + + // Popular is a special case since it's not a true "order" value. + if ( 'meta_value_num' === $orderby && 'wporg-pattern-favorites' === $wp_query->get( 'meta_key' ) ) { + $label = __( 'Sort: Popular', 'wporg' ); + } + + // Show the correct filters on the front page. + if ( is_front_page() ) { + $sort = 'favorite_count_desc'; + $label = __( 'Sort: Popular', 'wporg' ); + } + + $options = array( + 'date_desc' => __( 'Newest', 'wporg' ), + 'date_asc' => __( 'Oldest', 'wporg' ), + ); + + // These pages don't support sorting by favorite count. + if ( ! is_page( [ 'my-patterns', 'favorites' ] ) ) { + $options = array_merge( [ 'favorite_count_desc' => __( 'Popular', 'wporg' ) ], $options ); + } + + return array( + 'label' => $label, + 'title' => __( 'Sort', 'wporg' ), + 'key' => 'orderby', + 'action' => get_filter_action_url(), + 'options' => $options, + 'selected' => [ $sort ], + ); +} + +/** + * Provide a list of post status options. + * + * @param array $options The options for this filter. + * @return array New list of status options. + */ +function get_status_options( $options ) { + global $wp_query; + $selected = isset( $wp_query->query['status'] ) ? (array) $wp_query->query['status'] : array(); + + $count = count( $selected ); + $label = sprintf( + /* translators: The dropdown label for filtering, %s is the selected term count. */ + _n( 'Status %s', 'Status %s', $count, 'wporg' ), + $count + ); + + return array( + 'label' => $label, + 'title' => __( 'Status', 'wporg' ), + 'key' => 'status', + 'action' => get_filter_action_url(), + 'options' => array( + 'draft' => __( 'Draft', 'wporg' ), + 'pending' => __( 'Pending Review', 'wporg' ), + 'publish' => __( 'Published', 'wporg' ), + ), + 'selected' => $selected, + ); +} + +/** + * Add in the other existing filters as hidden inputs in the filter form. + * + * Enables combining filters by building up the correct URL on submit, + * for example patterns using a tag, a category, and matching a search term: + * ?tag[]=cuisine&cat[]=3&s=wordpress` + * + * @param string $key The key for the current filter. + */ +function inject_other_filters( $key ) { + global $wp_query; + + // Multiple-select query parameters. + $query_vars = [ 'pattern-categories' ]; + foreach ( $query_vars as $query_var ) { + if ( ! isset( $wp_query->query[ $query_var ] ) ) { + continue; + } + if ( $key === $query_var ) { + continue; + } + $values = (array) $wp_query->query[ $query_var ]; + foreach ( $values as $value ) { + printf( '', esc_attr( $query_var ), esc_attr( $value ) ); + } + } + + // Single-select query parameters. + $query_vars = [ 'order', 'orderby', 'curation' ]; + foreach ( $query_vars as $query_var ) { + if ( ! isset( $wp_query->query[ $query_var ] ) ) { + continue; + } + if ( $key === $query_var ) { + continue; + } + $values = (array) $wp_query->query[ $query_var ]; + foreach ( $values as $value ) { + printf( '', esc_attr( $query_var ), esc_attr( $value ) ); + } + } + + if ( is_front_page() ) { + if ( $key !== 'curation' ) { + printf( '' ); + } + if ( $key !== 'orderby' ) { + printf( '' ); + } + } + + // Pass through search query. + if ( isset( $wp_query->query['s'] ) ) { + printf( '', esc_attr( $wp_query->query['s'] ) ); + } +} + +/** + * Provide a list of local navigation menus. + */ +function add_site_navigation_menus( $menus ) { + $menu = array(); + + $menu[] = array( + 'label' => __( 'Favorites', 'wporg-patterns' ), + 'url' => '/favorites/', + ); + if ( is_user_logged_in() ) { + $menu[] = array( + 'label' => __( 'My Patterns', 'wporg-patterns' ), + 'url' => '/my-patterns/', + ); + } + $menu[] = array( + 'label' => __( 'New Pattern', 'wporg-patterns' ), + 'url' => '/new-pattern/', + ); + return array( + 'main' => $menu, + ); +} + +/** + * Update the archive title for all filter views. + * + * @param string $block_content The block content. + * @param array $block The full block, including name and attributes. + * @param WP_Block $instance The block instance. + */ +function update_archive_title( $block_content, $block, $instance ) { + global $wp_query; + $attributes = $block['attrs']; + + if ( isset( $attributes['type'] ) && 'filter' === $attributes['type'] ) { + // Skip output if there are no results. The `query-no-results` has an h1. + if ( ! $wp_query->found_posts ) { + return ''; + } + + $term_names = get_applied_filter_list(); + if ( ! empty( $term_names ) ) { + $term_names = wp_list_pluck( $term_names, 'name' ); + // translators: %s list of terms used for filtering. + $title = sprintf( __( 'Patterns: %s', 'wporg' ), implode( ', ', $term_names ) ); + } else { + $author = isset( $wp_query->query['author_name'] ) ? get_user_by( 'slug', $wp_query->query['author_name'] ) : false; + if ( $author ) { + $title = sprintf( __( 'Author: %s', 'wporg' ), $author->display_name ); + } else { + $title = __( 'All patterns', 'wporg' ); + } + } + + $tag_name = isset( $attributes['level'] ) ? 'h' . (int) $attributes['level'] : 'h1'; + $align_class_name = empty( $attributes['textAlign'] ) ? '' : "has-text-align-{$attributes['textAlign']}"; + + // Required to prevent `block_to_render` from being null in `get_block_wrapper_attributes`. + $parent = WP_Block_Supports::$block_to_render; + WP_Block_Supports::$block_to_render = $block; + $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => $align_class_name ) ); + WP_Block_Supports::$block_to_render = $parent; + + return sprintf( + '<%1$s %2$s>%3$s', + $tag_name, + $wrapper_attributes, + $title + ); + } + return $block_content; +} + +/** + * Update the breadcrumbs to the current page. + */ +function update_site_breadcrumbs( $breadcrumbs ) { + global $wp_query; + + // Build up the breadcrumbs from scratch. + $breadcrumbs = array( + array( + 'url' => home_url(), + 'title' => __( 'Home', 'wporg' ), + ), + ); + + if ( is_page() || is_single() ) { + $breadcrumbs[] = array( + 'url' => false, + 'title' => get_the_title(), + ); + return $breadcrumbs; + } + + if ( is_search() ) { + $breadcrumbs[] = array( + 'url' => home_url( '/archives/' ), + 'title' => __( 'All patterns', 'wporg' ), + ); + $breadcrumbs[] = array( + 'url' => false, + 'title' => __( 'Search results', 'wporg' ), + ); + return $breadcrumbs; + } + + // `is_home` matches the "posts page", the All Patterns page. + // `is_archive` matches any core archive (category, date, etc). + if ( is_home() || is_archive() ) { + // Get the current applied filters (except search, handled above). + $term_names = get_applied_filter_list( false ); + $author = isset( $wp_query->query['author_name'] ) ? get_user_by( 'slug', $wp_query->query['author_name'] ) : false; + + $breadcrumbs[] = array( + 'url' => home_url( '/archives/' ), + 'title' => __( 'All patterns', 'wporg' ), + ); + + if ( $author ) { + $breadcrumbs[] = array( + 'url' => get_author_posts_url( $author->ID ), + 'title' => sprintf( __( 'Author: %s', 'wporg' ), $author->display_name ), + ); + } + + if ( $term_names ) { + $term_names = wp_list_pluck( $term_names, 'name' ); + $breadcrumbs[] = array( + 'url' => false, + // translators: %s list of terms used for filtering. + 'title' => implode( ', ', $term_names ), + ); + } + } + + // Last item should be "current", no URL. + $breadcrumbs[count($breadcrumbs) - 1]['url'] = false; + + return $breadcrumbs; +} + +/** + * Update header template based on current query. + * + * @param array $parsed_block The block being rendered. + * + * @return array The updated block. + */ +function modify_pattern_include( $parsed_block ) { + if ( 'core/pattern' !== $parsed_block['blockName'] || empty( $parsed_block['attrs']['slug'] ) ) { + return $parsed_block; + } + + if ( + 'wporg-pattern-directory-2024/single-pattern' === $parsed_block['attrs']['slug'] && + get_current_user_id() === get_the_author_meta( 'ID' ) + ) { + $parsed_block['attrs']['slug'] = 'wporg-pattern-directory-2024/single-my-pattern'; + } + + if ( + 'wporg-pattern-directory-2024/grid-mine' === $parsed_block['attrs']['slug'] && + ! get_current_user_id() + ) { + $parsed_block['attrs']['slug'] = 'wporg-pattern-directory-2024/logged-out-patterns'; + } + + if ( + 'wporg-pattern-directory-2024/grid-favorites' === $parsed_block['attrs']['slug'] && + ! get_current_user_id() + ) { + $parsed_block['attrs']['slug'] = 'wporg-pattern-directory-2024/logged-out-favorites'; + } + + return $parsed_block; +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/inc/shortcodes.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/inc/shortcodes.php new file mode 100644 index 000000000..a71c53cae --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/inc/shortcodes.php @@ -0,0 +1,30 @@ + 'draft', + '_wpnonce' => wp_create_nonce( 'draft-' . $post_id ), + ), + get_the_permalink() + ); + } +); diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/package.json b/public_html/wp-content/themes/wporg-pattern-directory-2024/package.json new file mode 100644 index 000000000..69e5159d5 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/package.json @@ -0,0 +1,31 @@ +{ + "name": "wporg-pattern-directory-2024-theme", + "version": "1.0.0", + "description": "", + "author": "WordPress.org", + "license": "GPL-2.0-or-later", + "private": true, + "dependencies": { + "a11y-dialog": "^8.0.4" + }, + "devDependencies": { + "@wordpress/scripts": "27.2.0" + }, + "eslintConfig": { + "extends": "../../../../.eslintrc.js" + }, + "stylelint": { + "extends": "../../../../.stylelintrc", + "ignoreFiles": [ + "**/*.css", + "**/*.css.map" + ] + }, + "scripts": { + "build": "wp-scripts build --experimental-modules", + "start": "wp-scripts start --experimental-modules", + "lint:js": "wp-scripts lint-js src", + "lint:css": "wp-scripts lint-style src/**/*.scss", + "format": "wp-scripts format src -- --config=../../../../.prettierrc.js" + } +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/parts/footer.html b/public_html/wp-content/themes/wporg-pattern-directory-2024/parts/footer.html new file mode 100644 index 000000000..564215d69 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/parts/footer.html @@ -0,0 +1 @@ + diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/parts/header.html b/public_html/wp-content/themes/wporg-pattern-directory-2024/parts/header.html new file mode 100644 index 000000000..5029dae67 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/parts/header.html @@ -0,0 +1,17 @@ + + + + + +
+ + + + + +
+ + + + + diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_footer.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_footer.php new file mode 100644 index 000000000..2cbf01d62 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_footer.php @@ -0,0 +1,51 @@ + + + + diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_grid-favorites.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_grid-favorites.php new file mode 100644 index 000000000..84dca26e1 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_grid-favorites.php @@ -0,0 +1,103 @@ + + +
+ +
+ +
+ +
+ + + +
+ + +
+ +
+ + + + + + + + +
+ + + +
+ + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ +

+ + +

+ +
+ + + + + + + +
+ +
+ + + + +
+ + + +
+ + + +
+ +
+ + +
+ +
+ + +
+ diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_grid-front-page.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_grid-front-page.php new file mode 100644 index 000000000..637c8aa0c --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_grid-front-page.php @@ -0,0 +1,63 @@ + + +
+ +
+ +
+ + + +
+ + + +
+ + + +
+ +
+ + + + + + + + +
+ + + +
+ + + +
+ +
+ + +
+ + + +
+ +
+ +

+ +
+ +
+ diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_grid-mine.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_grid-mine.php new file mode 100644 index 000000000..f77e6bfb8 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_grid-mine.php @@ -0,0 +1,77 @@ + + +
+ +
+ +
+ +
+ + + +
+ +
+ +
+ + + + + + + + +
+ + + + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +

+ + + +
+ +
+ +
+ + +
+ diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_grid.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_grid.php new file mode 100644 index 000000000..774d1abf7 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_grid.php @@ -0,0 +1,75 @@ + + +
+ +
+ +
+ + + +
+ + + +
+ + + +
+ +
+ + + + + + + + +
+ + + +
+ + + +
+ +
+ + + + + + + + + + + + + +

+ + + +

+ all patterns or try a different search. ', 'wporg' ) ), + esc_url( home_url( '/archives/' ) ) + ); ?> +

+ + +
+ diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_logged-out-favorites.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_logged-out-favorites.php new file mode 100644 index 000000000..5bd4436c5 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_logged-out-favorites.php @@ -0,0 +1,40 @@ + + +
+ +

+ + + + + + + +
+ +
+ +
+ + + +

+ + + + + +
+ \ No newline at end of file diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_logged-out-patterns.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_logged-out-patterns.php new file mode 100644 index 000000000..09750f4cf --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/_logged-out-patterns.php @@ -0,0 +1,40 @@ + + +
+ +

+ + + + + + + +
+ +
+ +
+ + + +

+ + + + + +
+ \ No newline at end of file diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/front-page-header.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/front-page-header.php new file mode 100644 index 000000000..5d5cce926 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/front-page-header.php @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/single-my-pattern.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/single-my-pattern.php new file mode 100644 index 000000000..28a36cb49 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/single-my-pattern.php @@ -0,0 +1,76 @@ + + +
+ + +
+
+
+

+
+
+ + + + + + +
+ + + +
+ + + + + + + + + + + +
+ + + +
+ +
+ + + +
+ +
+ +
+ + + + + + + + + +
+ +
+ +
+ diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/single-pattern.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/single-pattern.php new file mode 100644 index 000000000..b07cb16d1 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/patterns/single-pattern.php @@ -0,0 +1,107 @@ + + +
+ + +
+
+
+

+
+
+ + + + +
+ + + +
+ + + + + + + + + + + +
+ + + +
+ + + +
+ +
+ +
+ + + +
+ +
+ +

+ + + +
+ + + +
+ + + +
+ + +
+ + + +
+ + + +
+ +
+ + +
+ +
+ +
+ diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/block.json b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/block.json new file mode 100644 index 000000000..1ae4a7877 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/block.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "wporg/copy-button", + "version": "0.1.0", + "title": "Copy Button", + "category": "design", + "icon": "", + "description": "A button which will copy the current pattern code to the clipboard.", + "textdomain": "wporg", + "attributes": { + "variant": { + "type": "string", + "enum": [ "default", "small" ] + } + }, + "supports": { + "html": false + }, + "usesContext": [ "postId", "postType" ], + "editorScript": "file:./index.js", + "style": "file:./style-index.css", + "viewScript": "file:./view.js", + "render": "file:./render.php" +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/index.js b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/index.js new file mode 100644 index 000000000..f56b8b425 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/index.js @@ -0,0 +1,16 @@ +/** + * WordPress dependencies + */ +import { registerBlockType } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import Edit from '../../utils/dynamic-edit'; +import metadata from './block.json'; +import './style.scss'; + +registerBlockType( metadata.name, { + edit: Edit, + save: () => null, +} ); diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/index.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/index.php new file mode 100644 index 000000000..0275cb5d3 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/index.php @@ -0,0 +1,15 @@ +context['postId']; +if ( ! $post_id ) { + return ''; +} + +$label = 'small' === $variant ? __( 'Copy', 'wporg-patterns' ) : __( 'Copy pattern', 'wporg-patterns' ); +$label_success = 'small' === $variant ? __( 'Copied', 'wporg-patterns' ) : __( 'Copied!', 'wporg-patterns' ); + +$classes = []; +if ( 'small' === $variant ) { + $classes[] = 'is-variant-small'; + $classes[] = 'is-style-outline'; +} + +$post = get_post( $post_id ); +?> +
implode( ' ', $classes ) ] ); // phpcs:ignore ?>> + + +
diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/style.scss b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/style.scss new file mode 100644 index 000000000..be3f20fdd --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/style.scss @@ -0,0 +1,16 @@ +.wp-block-wporg-copy-button { + min-width: fit-content; + + &.is-variant-small .wp-element-button { + --wp--custom--button--spacing--padding--top: calc(var(--wp--preset--spacing--10) / 2); + --wp--custom--button--spacing--padding--right: var(--wp--preset--spacing--10); + --wp--custom--button--spacing--padding--bottom: calc(var(--wp--preset--spacing--10) / 2); + --wp--custom--button--spacing--padding--left: var(--wp--preset--spacing--10); + font-size: var(--wp--preset--font-size--small); + font-weight: 400; + } + + &:not(.is-variant-small) .wp-element-button { + min-width: 170px; + } +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/view.js b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/view.js new file mode 100644 index 000000000..30589ab26 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/copy-button/view.js @@ -0,0 +1,39 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { speak } from '@wordpress/a11y'; + +/** + * Internal dependencies + */ +import copyToClipboard from '../../utils/copy-to-clipboard'; + +const init = () => { + const containers = document.querySelectorAll( '.wp-block-wporg-copy-button' ); + if ( containers ) { + containers.forEach( ( element ) => { + const button = element.querySelector( 'button' ); + const input = element.querySelector( '.wp-block-wporg-copy-button__content' ); + const content = JSON.parse( decodeURIComponent( input.value ) ); + + button.disabled = false; + button.onclick = async ( { target } ) => { + const success = copyToClipboard( content ); + + // Make sure we reset focus in case it was lost in the 'copy' command. + target.focus(); + + if ( success ) { + speak( __( 'Copied pattern to clipboard.', 'wporg-patterns' ) ); + button.innerText = button.dataset.labelSuccess; + setTimeout( () => { + button.innerText = button.dataset.label; + }, 20000 ); + } + }; + } ); + } +}; + +window.addEventListener( 'load', init ); diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/block.json b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/block.json new file mode 100644 index 000000000..863143922 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/block.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "wporg/delete-button", + "version": "0.1.0", + "title": "Delete Button", + "category": "design", + "icon": "", + "description": "A button which will delete the current pattern (after an AYS).", + "textdomain": "wporg", + "attributes": {}, + "supports": { + "html": false + }, + "usesContext": [ "postId", "postType" ], + "editorScript": "file:./index.js", + "style": "file:./style-index.css", + "viewScriptModule": "file:./view.js", + "render": "file:./render.php" +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/index.js b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/index.js new file mode 100644 index 000000000..f56b8b425 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/index.js @@ -0,0 +1,16 @@ +/** + * WordPress dependencies + */ +import { registerBlockType } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import Edit from '../../utils/dynamic-edit'; +import metadata from './block.json'; +import './style.scss'; + +registerBlockType( metadata.name, { + edit: Edit, + save: () => null, +} ); diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/index.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/index.php new file mode 100644 index 000000000..3bc319614 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/index.php @@ -0,0 +1,15 @@ +context['postId']; +if ( ! $post_id ) { + return; +} + +// Check if the user has permissions. +if ( ! current_user_can( 'edit_post', $post_id ) ) { + return; +} + +// Manually enqueue this script, so that it's available for the interactivity view script. +wp_enqueue_script( 'wp-api-fetch' ); + +// Initial state to pass to Interactivity API. +$init_state = [ + 'postId' => $post_id, + 'message' => __( 'Are you sure you want to delete this pattern?', 'wporg-patterns' ), + 'redirectUrl' => home_url( '/my-patterns/' ), +]; +$encoded_state = wp_json_encode( $init_state ); + +?> +
'is-small is-style-outline' ] ); // phpcs:ignore ?> + data-wp-interactive="wporg/patterns/delete-button" + data-wp-context="" +> + +
diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/style.scss b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/style.scss new file mode 100644 index 000000000..0f226562d --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/style.scss @@ -0,0 +1,11 @@ +.wp-block-wporg-delete-button { + --wp--custom--button--outline--color--text: #d63638; + + --wp--custom--button--outline--hover--color--text: var(--wp--preset--color--white); + --wp--custom--button--outline--hover--color--background: #d63638; + --wp--custom--button--outline--hover--border--color: #d63638; + + --wp--custom--button--outline--focus--border--color: #d63638; + --wp--custom--button--outline--focus--color--background: #d63638; + --wp--custom--button--outline--focus--color--text: var(--wp--preset--color--white); +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/view.js b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/view.js new file mode 100644 index 000000000..34a293bc2 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/delete-button/view.js @@ -0,0 +1,24 @@ +/** + * WordPress dependencies + */ +import { getContext, store } from '@wordpress/interactivity'; + +store( 'wporg/patterns/delete-button', { + actions: { + *triggerDelete() { + const { postId, message, redirectUrl } = getContext(); + const approved = yield window.confirm( message ); // eslint-disable-line no-alert + if ( approved ) { + try { + yield wp.apiFetch( { + path: `/wp/v2/wporg-pattern/${ postId }/`, + method: 'DELETE', + } ); + } catch ( error ) { + return; + } + window.location = redirectUrl; + } + }, + }, +} ); diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/block.json b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/block.json new file mode 100644 index 000000000..ba1db1065 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/block.json @@ -0,0 +1,25 @@ +{ + "$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 showing the current count of favorites, which can toggle favoriting on the current pattern.", + "textdomain": "wporg", + "attributes": { + "variant": { + "type": "string", + "enum": [ "default", "small" ] + } + }, + "supports": { + "html": false + }, + "usesContext": [ "postId", "postType" ], + "editorScript": "file:./index.js", + "style": "file:./style-index.css", + "viewScriptModule": "file:./view.js", + "render": "file:./render.php" +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/index.js b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/index.js new file mode 100644 index 000000000..f56b8b425 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/index.js @@ -0,0 +1,16 @@ +/** + * WordPress dependencies + */ +import { registerBlockType } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import Edit from '../../utils/dynamic-edit'; +import metadata from './block.json'; +import './style.scss'; + +registerBlockType( metadata.name, { + edit: Edit, + save: () => null, +} ); diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/index.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/index.php new file mode 100644 index 000000000..ece9c601e --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/index.php @@ -0,0 +1,15 @@ +context['postId']; +if ( ! $post_id ) { + return ''; +} + +$user_id = get_current_user_id(); +// Only the small variant should show if anon user. +if ( ! $user_id && 'small' !== $variant ) { + return ''; +} + +// Manually enqueue this script, so that it's available for the interactivity view script. +wp_enqueue_script( 'wp-api-fetch' ); + +$is_favorite = is_favorite( $post_id, $user_id ); +$classes = [ 'is-style-text is-small' ]; +if ( 'small' === $variant ) { + $classes[] = 'is-variant-small'; +} +$classes = implode( ' ', $classes ); + +$tag_name = ! $user_id ? 'span' : 'button'; +$favorite_count = get_favorite_count( $post_id ); + +$add_label = __( 'Add to favorites', 'wporg-patterns' ); +$remove_label = __( 'Remove from favorites', 'wporg-patterns' ); +$sr_label = sprintf( + _n( + 'Favorited %s time', + 'Favorited %s times', + $favorite_count, + 'wporg-patterns' + ), + $favorite_count +); + +// Initial state to pass to Interactivity API. +$init_state = [ + 'postId' => $post_id, + 'isFavorite' => $is_favorite, + 'count' => $favorite_count, + 'label' => [ + 'add' => $add_label, + 'remove' => $remove_label, + 'screenReader' => __( 'Favorited %s times', 'wporg-patterns' ), + ] +]; +$encoded_state = wp_json_encode( $init_state ); + +?> +
$classes ] ); // phpcs:ignore ?> + data-wp-interactive="wporg/patterns/favorite-button" + data-wp-context="" + data-wp-class--is-favorite="context.isFavorite" +> + + < + class="wporg-favorite-button__button" + disabled="disabled" + data-wp-bind--disabled="!context.postId" + data-wp-on--click="actions.triggerAction" + > + + + + + + + + + + + + > + + + +
\ No newline at end of file diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/style.scss b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/style.scss new file mode 100644 index 000000000..c48559020 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/style.scss @@ -0,0 +1,54 @@ +:where(.wp-block-wporg-favorite-button) { + .wporg-favorite-button__button { + margin: 0; + padding: 0; + background: none; + border: 0; + border-radius: 0; + box-shadow: none; + font-size: 14px; + } + + > * { + + /* Align children. */ + display: flex !important; + align-items: center; + gap: calc(var(--wp--preset--spacing--10) / 2); + } + + svg { + margin-top: 2px; + height: 24px; + width: 24px; + overflow: visible; + + path { + fill: var(--wp--preset--color--charcoal-4); + } + + &.is-star-empty { + display: block; + } + + &.is-star-filled { + display: none; + } + } + + &.is-favorite { + svg.is-star-empty { + display: none; + } + + svg.is-star-filled { + display: block; + } + } + + &:not(.is-variant-small) { + svg path { + fill: currentColor; + } + } +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/view.js b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/view.js new file mode 100644 index 000000000..bbf0e996a --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/favorite-button/view.js @@ -0,0 +1,47 @@ +/** + * WordPress dependencies + */ +import { getContext, store } from '@wordpress/interactivity'; + +store( 'wporg/patterns/favorite-button', { + state: { + get labelScreenReader() { + const { label, count } = getContext(); + return label.screenReader.replace( '%s', count ); + }, + get labelCount() { + const { count } = getContext(); + return `(${ count })`; + }, + get labelAction() { + const { label, isFavorite } = getContext(); + return isFavorite ? label.remove : label.add; + }, + }, + actions: { + *triggerAction() { + const context = getContext(); + if ( context.isFavorite ) { + try { + const newCount = yield wp.apiFetch( { + path: '/wporg/v1/pattern-favorites', + method: 'DELETE', + data: { id: context.postId }, + } ); + context.isFavorite = false; + context.count = newCount; + } catch ( error ) {} + } else { + try { + const newCount = yield wp.apiFetch( { + path: '/wporg/v1/pattern-favorites', + method: 'POST', + data: { id: context.postId }, + } ); + context.isFavorite = true; + context.count = newCount; + } catch ( error ) {} + } + }, + }, +} ); diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/block.json b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/block.json new file mode 100644 index 000000000..8c98e9ff8 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/block.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "wporg/pattern-view-control", + "version": "0.1.0", + "title": "Pattern View Control", + "category": "design", + "icon": "", + "description": "A wrapper for the pattern preview to control the viewport size.", + "textdomain": "wporg", + "supports": { + "align": [ "wide", "full" ], + "html": false, + "spacing": { + "margin": true, + "padding": false, + "blockGap": false + } + }, + "usesContext": [ "postId", "postType" ], + "editorScript": "file:./index.js", + "style": "file:./style-index.css", + "viewScriptModule": "file:./view.js", + "render": "file:./render.php" +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/index.js b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/index.js new file mode 100644 index 000000000..0ffb8f2c6 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/index.js @@ -0,0 +1,18 @@ +/** + * WordPress dependencies + */ +import { registerBlockType } from '@wordpress/blocks'; +import { useBlockProps } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import metadata from './block.json'; +import './style.scss'; + +const Edit = () =>
Thumbnail
; + +registerBlockType( metadata.name, { + edit: Edit, + save: () => null, +} ); diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/render.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/render.php new file mode 100644 index 000000000..6cd4253fc --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/render.php @@ -0,0 +1,82 @@ +context['postId'] ) ) { + return ''; +} + +$view_url = get_pattern_preview_url( $block->context['postId'] ); + +// Initial state to pass to Interactivity API. +$init_state = [ + 'url' => $view_url, + 'previewWidth' => 1200, + 'previewHeight' => 200, +]; +$encoded_state = wp_json_encode( $init_state ); + +// Remove the nested context for child blocks, so that it uses this context. +$p = new WP_HTML_Tag_Processor( $content ); +$p->next_tag( 'div' ); +$p->remove_attribute( 'data-wp-interactive' ); +$p->remove_attribute( 'data-wp-context' ); +$content = $p->get_updated_html(); + +?> +
+ data-wp-interactive="wporg/patterns/preview" + data-wp-context="" + data-wp-init="actions.handleOnResize" + data-wp-on-window--resize="actions.handleOnResize" +> + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/style.scss b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/style.scss new file mode 100644 index 000000000..6d2ab62b4 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/style.scss @@ -0,0 +1,40 @@ +.wp-block-wporg-pattern-view-control { + position: relative; + width: 100%; + --wp--custom--wporg-pattern-preview--border--width: clamp(4px, calc(1.33vw - 1.33px), 20px); + --wp--custom--wporg-pattern-preview--border--radius: clamp(4px, calc(1.33vw - 1.33px), 20px); +} + +.wporg-pattern-view-control__controls { + margin-top: calc(var(--wp--preset--spacing--20) + var(--wp--custom--wporg-pattern-preview--border--width)); + display: flex; + justify-content: center; + gap: var(--wp--preset--spacing--10); + + .wp-element-button { + display: flex !important; + flex-direction: column; + align-items: center; + + svg { + fill: currentColor !important; + } + } +} + +.wp-block-wporg-pattern-view-control .wp-block-wporg-pattern-preview { + // The height is calculated based on the child iframe, `content-box` means + // this does not need to include the padding. + box-sizing: content-box; + border: none; + padding: var(--wp--custom--wporg-pattern-preview--border--width); + + > iframe { + margin: 0 auto; + display: block; + border-radius: var(--wp--custom--wporg-pattern-preview--border--radius); + outline-color: var(--wp--custom--wporg-pattern-preview--border--color); + outline-style: solid; + outline-width: var(--wp--custom--wporg-pattern-preview--border--width); + } +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/view.js b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/view.js new file mode 100644 index 000000000..60e86ba18 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/controls/view.js @@ -0,0 +1,73 @@ +/** + * WordPress dependencies + */ +import { getContext, getElement, store } from '@wordpress/interactivity'; + +const { actions, state } = store( 'wporg/patterns/preview', { + state: { + get scale() { + const { pageWidth, previewWidth } = getContext(); + const scale = parseInt( pageWidth, 10 ) / previewWidth; + return scale > 1 ? 1 : scale; + }, + get previewHeightCSS() { + return `${ getContext().previewHeight }px`; + }, + get iframeWidthCSS() { + return `${ getContext().previewWidth }px`; + }, + get iframeHeightCSS() { + return `${ getContext().previewHeight / state.scale }px`; + }, + get transformCSS() { + return `scale(${ state.scale })`; + }, + get isWidth1200() { + return 1200 === getContext().previewWidth; + }, + get isWidth960() { + return 960 === getContext().previewWidth; + }, + get isWidth600() { + return 600 === getContext().previewWidth; + }, + get isWidth480() { + return 480 === getContext().previewWidth; + }, + }, + actions: { + onWidthChange() { + const { ref } = getElement(); + const context = getContext(); + context.previewWidth = parseInt( ref.dataset.width, 10 ); + }, + *onLoad() { + const { ref } = getElement(); + + yield new Promise( ( resolve ) => { + ref.addEventListener( 'load', () => resolve() ); + } ); + + // iframe is loaded now, so we should adjust the height. + actions.updatePreviewHeight(); + }, + updatePreviewHeight() { + const context = getContext(); + const { ref } = getElement(); + + // Need to "use" previewWidth so that `data-wp-watch` will re-run this action when it changes. + context.previewWidth; // eslint-disable-line no-unused-expressions + + const iframeDoc = ref.contentDocument; + const height = iframeDoc.querySelector( '.entry-content' )?.clientHeight; + if ( height ) { + context.previewHeight = height * state.scale; + } + }, + handleOnResize() { + const context = getContext(); + const { ref } = getElement(); + context.pageWidth = ref.clientWidth; + }, + }, +} ); diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/block.json b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/block.json new file mode 100644 index 000000000..f5438f34d --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/block.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "wporg/pattern-preview", + "version": "0.1.0", + "title": "Pattern Preview", + "category": "design", + "icon": "", + "description": "Display a preview for the current pattern.", + "textdomain": "wporg", + "supports": { + "align": ["wide", "full"], + "html": false, + "spacing": { + "margin": true, + "padding": false, + "blockGap": false + } + }, + "usesContext": [ "postId", "postType", "queryId" ], + "editorScript": "file:./index.js", + "style": "file:./style-index.css", + "viewScriptModule": "wporg-pattern-view-control-view-script-module", + "render": "file:./render.php" +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/edit.js b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/edit.js new file mode 100644 index 000000000..32c6fbfd4 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/edit.js @@ -0,0 +1,14 @@ +/** + * WordPress dependencies + */ +import { useBlockProps } from '@wordpress/block-editor'; + +/** + * The edit function describes the structure of your block in the context of the + * editor. This represents what the editor will render when the block is used. + * + * @return {Element} Element to render. + */ +export default function Edit() { + return
Thumbnail
; +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/index.js b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/index.js new file mode 100644 index 000000000..5d6da2ae2 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/index.js @@ -0,0 +1,16 @@ +/** + * WordPress dependencies + */ +import { registerBlockType } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import Edit from './edit'; +import metadata from './block.json'; +import './style.scss'; + +registerBlockType( metadata.name, { + edit: Edit, + save: () => null, +} ); diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/render.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/render.php new file mode 100644 index 000000000..5835620f8 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/render.php @@ -0,0 +1,45 @@ +context['postId'] ) ) { + return ''; +} + +$view_url = get_pattern_preview_url( $block->context['postId'] ); +$viewport_width = get_post_meta( $block->context['postId'], 'wpop_viewport_width', true ); + +if ( ! $viewport_width ) { + $viewport_width = 1200; +} + +// Initial state to pass to Interactivity API. +$init_state = [ + 'url' => $view_url, + 'previewWidth' => $viewport_width, + 'previewHeight' => 200, +]; +$encoded_state = wp_json_encode( $init_state ); + +?> +
+ data-wp-interactive="wporg/patterns/preview" + data-wp-context="" + data-wp-style--height="state.previewHeightCSS" + data-wp-init="actions.handleOnResize" + data-wp-on-window--resize="actions.handleOnResize" + tabIndex="-1" +> + +
\ No newline at end of file diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/style.scss b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/style.scss new file mode 100644 index 000000000..8e15f13c1 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/frame/style.scss @@ -0,0 +1,16 @@ +.wp-block-wporg-pattern-preview { + border-color: var(--wp--custom--wporg-pattern-thumbnail--border--color); + border-radius: var(--wp--custom--wporg-pattern-thumbnail--border--radius); + border-style: solid; + border-width: var(--wp--custom--wporg-pattern-thumbnail--border--width); + margin: 0; + position: relative; + width: 100%; + overflow: hidden; + + iframe { + border: none; + max-width: none; + pointer-events: none; + } +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/index.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/index.php new file mode 100644 index 000000000..2991d445a --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-preview/index.php @@ -0,0 +1,16 @@ +Thumbnail; +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/index.js b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/index.js new file mode 100644 index 000000000..5d6da2ae2 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/index.js @@ -0,0 +1,16 @@ +/** + * WordPress dependencies + */ +import { registerBlockType } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import Edit from './edit'; +import metadata from './block.json'; +import './style.scss'; + +registerBlockType( metadata.name, { + edit: Edit, + save: () => null, +} ); diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/index.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/index.php new file mode 100644 index 000000000..c2c0f027b --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/index.php @@ -0,0 +1,22 @@ +context['postId'] ) ) { + return ''; +} +$post_id = $block->context['postId']; + +$view_url = get_pattern_preview_url( $post_id ); +$has_link = isset( $attributes['isLink'] ) && true == $attributes['isLink']; +$is_lazyload = isset( $attributes['lazyLoad'] ) && true === $attributes['lazyLoad']; + +$viewport_width = get_post_meta( $post_id, 'wpop_viewport_width', true ); + +if ( ! $viewport_width ) { + $viewport_width = 1200; +} + +$cache_key = '20240223'; // To break out of cached image. + +$view_url = add_query_arg( 'v', $cache_key, $view_url ); +$url = add_query_arg( + array( + 'scale' => 2, + 'w' => 1100, + 'vpw' => $viewport_width, + 'vph' => 300, // Smaller than the vast majority of patterns to avoid whitespace. + 'screen_height' => 3600, // Max height of a screenshot. + ), + 'https://s0.wp.com/mshots/v1/' . urlencode( $view_url ), +); + +// Initial state to pass to Interactivity API. +$init_state = [ + 'base64Image' => '', + 'src' => esc_url( $url ), + 'alt' => the_title_attribute( array( 'echo' => false ) ), +]; +$encoded_state = wp_json_encode( $init_state ); + +$classname = ''; +if ( $has_link ) { + $classname .= ' is-linked-image'; +} + +?> +
$classname ) ); // phpcs:ignore ?> + data-wp-interactive="wporg/patterns/thumbnail" + data-wp-context="" + data-wp-init="callbacks.init" + data-wp-class--has-loaded="state.hasLoaded" + tabIndex="-1" +> + + + + +
+ + +
+ + +
+ +
\ No newline at end of file diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/style.scss b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/style.scss new file mode 100644 index 000000000..bdee4771f --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/style.scss @@ -0,0 +1,74 @@ +.wp-block-wporg-pattern-thumbnail { + border-color: var(--wp--custom--wporg-pattern-thumbnail--border--color); + border-radius: var(--wp--custom--wporg-pattern-thumbnail--border--radius); + border-style: solid; + border-width: var(--wp--custom--wporg-pattern-thumbnail--border--width); + margin: 0; + position: relative; + width: 100%; + overflow: hidden; + + &:where(.is-linked-image):hover, + &:where(.is-linked-image):focus-within { + --wp--custom--wporg-pattern-thumbnail--border--color: rgba(30, 30, 30, 0.25); + } + + &:where(.is-linked-image):focus-within { + outline: 1.5px solid var(--wp--custom--link--color--text); + outline-offset: -1.5px; + } + + img { + width: 100%; + vertical-align: middle; + } + + &:not(.has-loaded) { + aspect-ratio: 21 / 9; + } + + .wporg-pattern-thumbnail__loader { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + + img, + span { + display: none; + } + + &::after { + content: ""; + display: inline-block; + box-sizing: border-box; + height: 16px; + width: 16px; + border: 1.5px solid; + border-color: + var(--wp--custom--wporg-pattern-thumbnail--border--color) + var(--wp--custom--wporg-pattern-thumbnail--border--color) + var(--wp--custom--link--color--text); + border-radius: 50%; + animation: rotate-360 1.4s linear infinite; + } + } + + .wporg-pattern-thumbnail__error { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + aspect-ratio: 21 / 9; + } +} + +@keyframes rotate-360 { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/view.js b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/view.js new file mode 100644 index 000000000..4711b9483 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/pattern-thumbnail/view.js @@ -0,0 +1,88 @@ +/* global FileReader */ +/** + * WordPress dependencies + */ +import { getContext, store } from '@wordpress/interactivity'; + +/** + * Module constants + */ +const MAX_ATTEMPTS = 10; +const RETRY_DELAY = 2000; + +const { actions, state } = store( 'wporg/patterns/thumbnail', { + state: { + attempts: 0, + shouldRetry: true, + hasError: false, + get base64Image() { + return getContext().base64Image; + }, + get hasLoaded() { + return state.base64Image || state.hasError; + }, + }, + actions: { + setShouldRetry( value ) { + state.shouldRetry = value; + }, + + setHasError( value ) { + state.hasError = value; + }, + + setBase64Image( value ) { + const context = getContext(); + context.base64Image = value; + }, + + *fetchImage( fullUrl ) { + try { + const res = yield fetch( fullUrl ); + state.attempts++; + + if ( res.redirected ) { + actions.setShouldRetry( true ); + } else if ( res.status === 200 && ! res.redirected ) { + const blob = yield res.blob(); + + const value = yield new Promise( ( resolve ) => { + const reader = new FileReader(); + reader.onloadend = () => resolve( reader.result ); + reader.readAsDataURL( blob ); + } ); + + actions.setBase64Image( value ); + actions.setShouldRetry( false ); + } + } catch ( error ) { + actions.setHasError( true ); + actions.setShouldRetry( false ); + } + }, + }, + + callbacks: { + // Run on init, starts the image fetch process. + *init() { + const { src } = getContext(); + + if ( ! state.base64Image ) { + // Initial fetch. + yield actions.fetchImage( src ); + + while ( state.shouldRetry ) { + yield new Promise( ( resolve ) => { + setTimeout( () => resolve(), RETRY_DELAY ); + } ); + yield actions.fetchImage( src ); + + if ( state.attempts >= MAX_ATTEMPTS ) { + actions.setHasError( true ); + actions.setShouldRetry( false ); + } + } + } + }, + }, +} ); diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/post-status/block.json b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/post-status/block.json new file mode 100644 index 000000000..aa5015902 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/post-status/block.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "wporg/post-status", + "version": "0.1.0", + "title": "Post Status", + "category": "design", + "icon": "", + "description": "A label displaying the current pattern's status.", + "textdomain": "wporg", + "attributes": {}, + "supports": { + "html": false + }, + "usesContext": [ "postId", "postType" ], + "style": "file:./style-index.css", + "editorScript": "file:./index.js", + "render": "file:./render.php" +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/post-status/index.js b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/post-status/index.js new file mode 100644 index 000000000..f56b8b425 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/post-status/index.js @@ -0,0 +1,16 @@ +/** + * WordPress dependencies + */ +import { registerBlockType } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import Edit from '../../utils/dynamic-edit'; +import metadata from './block.json'; +import './style.scss'; + +registerBlockType( metadata.name, { + edit: Edit, + save: () => null, +} ); diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/post-status/index.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/post-status/index.php new file mode 100644 index 000000000..6b16b4325 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/post-status/index.php @@ -0,0 +1,66 @@ + __NAMESPACE__ . '\render', + ) + ); +} + +/** + * Render the block content. + * + * @param array $attributes Block attributes. + * @param string $content Block default content. + * @param WP_Block $block Block instance. + * + * @return string Returns the block markup. + */ +function render( $attributes, $content, $block ) { + $post_id = $block->context['postId']; + if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) ) { + return ''; + } + + $status = get_post_status( $post_id ); + + $label = ''; + switch ( $status ) { + case 'publish': + $label = __( 'Published', 'wporg-patterns' ); + $type = 'published'; + break; + case 'pending-review': // Potential spam. + case 'pending': + $label = __( 'Pending', 'wporg-patterns' ); + $type = 'pending'; + break; + case 'draft': + $label = __( 'Draft', 'wporg-patterns' ); + $type = 'draft'; + break; + case 'unlisted': + $label = __( 'Declined', 'wporg-patterns' ); + $type = 'unlisted'; + break; + } + + if ( ! $label ) { + return ''; + } + + $wrapper_attributes = get_block_wrapper_attributes( [ 'class' => 'is-' . $type ] ); + return sprintf( '
%2s
', $wrapper_attributes, $label ); +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/post-status/style.scss b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/post-status/style.scss new file mode 100644 index 000000000..d427e4200 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/post-status/style.scss @@ -0,0 +1,36 @@ +.wp-block-wporg-post-status { + display: inline-block; + padding: 0.1rem 0.75rem; + border-radius: 2px; + background: var(--wp--preset--color--blueberry-4); + color: var(--wp--preset--color--charcoal-1); + font-weight: 600; + font-size: var(--wp--preset--font-size--extra-small); + line-height: var(--wp--custom--body--extra-small--typography--line-height); + text-transform: uppercase; + + &.is-pending, + &.is-pending-review { + background: var(--wp--preset--color--lemon-2); + } + + &.is-unlisted { + background: var(--wp--preset--color--pomegrade-3); + } + + &.is-published { + background: var(--wp--preset--color--acid-green-2); + } +} + +// Adjust position of the post status chip when in my patterns grid. +.wporg-my-patterns .wp-block-post { + position: relative; + + .wp-block-wporg-post-status { + position: absolute; + top: calc(var(--wp--preset--spacing--10) * -1); + left: var(--wp--preset--spacing--10); + margin-block-start: 0 !important; + } +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/block.json b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/block.json new file mode 100644 index 000000000..ff12b8654 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/block.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "wporg/report-pattern", + "version": "0.1.0", + "title": "Report Pattern", + "category": "design", + "icon": "", + "description": "A button to trigger the report flow, including report modal.", + "textdomain": "wporg", + "attributes": {}, + "supports": { + "html": false + }, + "usesContext": [ "postId", "postType" ], + "editorScript": "file:./index.js", + "style": "file:./style-index.css", + "viewScript": "file:./view.js", + "render": "file:./render.php" +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/index.js b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/index.js new file mode 100644 index 000000000..f56b8b425 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/index.js @@ -0,0 +1,16 @@ +/** + * WordPress dependencies + */ +import { registerBlockType } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import Edit from '../../utils/dynamic-edit'; +import metadata from './block.json'; +import './style.scss'; + +registerBlockType( metadata.name, { + edit: Edit, + save: () => null, +} ); diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/index.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/index.php new file mode 100644 index 000000000..a01874b37 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/index.php @@ -0,0 +1,15 @@ +block_type->view_script ) ) { + wp_enqueue_script( $block->block_type->view_script ); + // Move to footer. + wp_script_add_data( $block->block_type->view_script, 'group', 1 ); +} + +$post_id = $block->context['postId']; +if ( ! $post_id ) { + return; +} + +if ( ! current_user_can( 'read' ) ) { + return; +} + +// If this pattern has been reported by this user, it can't be reported again. +if ( user_has_flagged_pattern() ) { + printf( + '
%s
', + get_block_wrapper_attributes(), + __( 'You’ve reported this pattern.', 'wporg-patterns' ) + ); + return; +} + +$reasons = get_terms( [ 'taxonomy' => FLAG_REASON, 'hide_empty' => false, 'orderby' => 'slug' ] ); + +?> +
> +
+ +
+ +
diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/style.scss b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/style.scss new file mode 100644 index 000000000..e202523fb --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/style.scss @@ -0,0 +1,109 @@ +.wp-block-wporg-report-pattern { + // stylelint-disable-next-line max-line-length + --wp-report-dialog-offset: calc(var(--wp-admin--admin-bar--height, 0px) + var(--wp--custom--local-navigation-bar--spacing--height, 0px)); +} + +.wporg-report-pattern__dialog-container, +.wporg-report-pattern__dialog-overlay { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.wporg-report-pattern__dialog-container { + top: var(--wp-report-dialog-offset); + z-index: 2; + display: flex; + + &[aria-hidden="true"] { + display: none; + } +} + +.wporg-report-pattern__dialog-overlay { + background-color: var(--wp--preset--color--black-opacity-15); +} + +.wporg-report-pattern__dialog-content { + z-index: 2; + position: relative; + margin: auto; + background-color: var(--wp--preset--color--white); + + h1 { + margin: 0; + font-family: var(--wp--preset--font-family--inter) !important; + font-size: var(--wp--preset--font-size--large) !important; + font-weight: 600; + align-self: center; + } +} + +.wporg-report-pattern__dialog-header { + display: flex; + flex-direction: row; + align-items: flex-start; + justify-content: space-between; + gap: var(--wp--preset--spacing--20); + padding: var(--wp--preset--spacing--20); + border-bottom: 1px solid var(--wp--preset--color--black-opacity-15); + + button { + padding: 0; + height: 24px; + width: 24px; + border: none; + background: transparent; + } +} + +.wporg-report-pattern__dialog-body { + padding: var(--wp--preset--spacing--20); + display: flex; + flex-direction: column; + gap: var(--wp--preset--spacing--20); + overflow: auto; + max-height: calc(85vh - var(--wp-report-dialog-offset) - 100px); + font-size: var(--wp--preset--font-size--small); + + fieldset, + legend { + border: none; + padding: 0; + margin: 0; + } + + .wporg-report-pattern__dialog-field > legend, + .wporg-report-pattern__dialog-field > label { + font-weight: 600; + } + + textarea { + border: 1px solid var(--wp--preset--color--black-opacity-15); + } + + textarea:focus, + input:focus { + outline: 1.5px solid var(--wp--preset--color--blueberry-1); + outline-offset: -1.5px; + box-shadow: inset 0 0 0 3px var(--wp--preset--color--white); + } +} + +.wporg-report-pattern__dialog-footer { + display: flex; + flex-direction: row; + gap: var(--wp--preset--spacing--20); + padding: var(--wp--preset--spacing--20); + border-top: 1px solid var(--wp--preset--color--black-opacity-15); + + > * { + flex: 1; + + button { + width: 100%; + } + } +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/view.js b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/view.js new file mode 100644 index 000000000..82d5e15d0 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/report-pattern/view.js @@ -0,0 +1,22 @@ +/** + * Internal dependencies + */ +import A11yDialog from 'a11y-dialog'; + +const init = () => { + const element = document.getElementById( 'report-dialog' ); + if ( ! element ) { + return; + } + // Initialize dialog. + const dialog = new A11yDialog( element ); + + // Open dialog with button. + const button = document.querySelector( '.wp-block-wporg-report-pattern [data-a11y-dialog-show]' ); + button.disabled = false; + button.addEventListener( 'click', () => { + dialog.show(); + } ); +}; + +window.addEventListener( 'load', init ); diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/status-notice/block.json b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/status-notice/block.json new file mode 100644 index 000000000..0cb8a7965 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/status-notice/block.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "wporg/status-notice", + "version": "0.1.0", + "title": "Status Notice", + "category": "design", + "icon": "", + "description": "A notice displaying the current pattern's status.", + "textdomain": "wporg", + "attributes": {}, + "supports": { + "html": false + }, + "usesContext": [ "postId", "postType" ], + "editorScript": "file:./index.js", + "render": "file:./render.php" +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/status-notice/index.js b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/status-notice/index.js new file mode 100644 index 000000000..7633e1acc --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/status-notice/index.js @@ -0,0 +1,15 @@ +/** + * WordPress dependencies + */ +import { registerBlockType } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import Edit from '../../utils/dynamic-edit'; +import metadata from './block.json'; + +registerBlockType( metadata.name, { + edit: Edit, + save: () => null, +} ); diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/status-notice/index.php b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/status-notice/index.php new file mode 100644 index 000000000..b9f611648 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/blocks/status-notice/index.php @@ -0,0 +1,94 @@ + __NAMESPACE__ . '\render', + ) + ); +} + +/** + * Render the block content. + * + * @param array $attributes Block attributes. + * @param string $content Block default content. + * @param WP_Block $block Block instance. + * + * @return string Returns the block markup. + */ +function render( $attributes, $content, $block ) { + $post_id = $block->context['postId']; + if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) ) { + return ''; + } + + $type = 'info'; + $status = get_post_status( $post_id ); + + $message = ''; + switch ( $status ) { + case 'pending-review': // Potential spam. + case 'pending': + $type = 'alert'; + $message .= '

'; + $message .= '' . __( 'Review pending.', 'wporg-patterns' ) . ' '; + $message .= __( 'This pattern is only visible to you. Once approved it will be published to everyone.', 'wporg-patterns' ); + $message .= '

'; + $message .= '

' . __( 'All patterns submitted to WordPress.org are subject to both automated and manual approval. It might take a few days for your pattern to be approved.', 'wporg-patterns' ) . '

'; + $message .= '

' . __( 'Reviewers look for content that may be problematic (copyright or trademark issues) and whether your pattern works as intended.', 'wporg-patterns' ) . '

'; + break; + case 'draft': + $message .= '

'; + $message .= '' . __( 'Saved as draft.', 'wporg-patterns' ) . ' '; + $message .= __( 'This pattern is only visible to you. When you’re ready, submit it to be published to everyone.', 'wporg-patterns' ); + $message .= '

'; + $message .= '

' . __( 'Patterns can be saved as a draft which can be submitted for approval at any time. This allows you to save your design and come back to it later to submit.', 'wporg-patterns' ) . '

'; + break; + case 'unlisted': + $type = 'warning'; + $reason = get_pattern_unlisted_reason( $post_id ); + $message .= '

'; + $message .= '' . __( 'Pattern declined.', 'wporg-patterns' ) . ' '; + $message .= __( 'WordPress.org has chosen not to include this pattern in the directory.', 'wporg-patterns' ); + $message .= '

'; + if ( $reason ) { + $message .= sprintf( + '

%s %s

', + __( 'WordPress.org has removed your pattern from the directory for the following reason:', 'wporg-patterns' ), + $reason + ); + } + $message .= '

' . __( 'You can update your pattern to resubmit it for approval at any time.', 'wporg-patterns' ) . '

'; + break; + case 'publish': + $type = 'tip'; + $message .= '

'; + $message .= '' . __( 'Pattern published!', 'wporg-patterns' ) . ' '; + $message .= __( 'Your new design is now available to everyone.', 'wporg-patterns' ); + $message .= '

'; + break; + } + + $markup = ' +
+
+
%2$s
+
+ '; + + return do_blocks( sprintf( $markup, $type, $message ) ); +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/utils/copy-to-clipboard.js b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/utils/copy-to-clipboard.js new file mode 100644 index 000000000..3af919703 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/utils/copy-to-clipboard.js @@ -0,0 +1,29 @@ +/** + * Uses a hidden textarea that is added and removed from the DOM in order to copy to clipboard via the Browser. + * + * @param {string} stringToCopy A string that will be copied to the clipboard + * @return {boolean} Whether the copy function succeeded + */ +export default function ( stringToCopy ) { + const element = document.createElement( 'textarea' ); + + // We don't want the text area to be selected since it's temporary. + element.setAttribute( 'readonly', '' ); + + // We don't want screen readers to read the content since it's pattern markup + element.setAttribute( 'aria-hidden', 'true' ); + + // We don't want the text area to be visible since it's temporary. + element.style.position = 'absolute'; + element.style.left = '-9999px'; + + element.value = stringToCopy; + + document.body.appendChild( element ); + element.select(); + + const success = document.execCommand( 'copy' ); + document.body.removeChild( element ); + + return success; +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/src/utils/dynamic-edit.js b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/utils/dynamic-edit.js new file mode 100644 index 000000000..62679697b --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/src/utils/dynamic-edit.js @@ -0,0 +1,20 @@ +/** + * WordPress dependencies + */ +import { useBlockProps } from '@wordpress/block-editor'; +import ServerSideRender from '@wordpress/server-side-render'; + +export default function Edit( { name, attributes, context } ) { + const blockProps = useBlockProps(); + const { postId } = context; + return ( +
+ +
+ ); +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/style.css b/public_html/wp-content/themes/wporg-pattern-directory-2024/style.css new file mode 100644 index 000000000..be5a94ec9 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/style.css @@ -0,0 +1,136 @@ +/* + * Theme Name: WordPress.org Pattern Directory 2024 + * Theme URI: https://github.com/WordPress/pattern-directory + * Author: WordPress.org + * Author URI: https://wordpress.org/ + * Description: + * Version: 1.0.0 + * License: GNU General Public License v2 or later + * Text Domain: wporg + * Template: wporg-parent-2021 + */ + +/* + * Note: only add styles here in cases where you can't achieve the style with + * templates or theme.json settings. + */ + +/* Fix image alignment */ +.wp-block-avatar__image { + vertical-align: middle; +} + +.wp-block-query-pagination.wp-block-query-pagination { + margin-bottom: 0; +} + +/* + * If two spacers are immediate siblings, something isn't rendering + * (like pagination, etc). So hide the subsequent spacers. + */ +.wp-block-query .wp-block-spacer + .wp-block-spacer { + display: none; +} + +.wp-block-query-no-results { + margin-top: 0; +} + +/* Both blocks are in the local header, but */ +body.blog .wp-block-wporg-local-navigation-bar .wp-block-post-title, +body.archive .wp-block-wporg-local-navigation-bar .wp-block-post-title, +body.search .wp-block-wporg-local-navigation-bar .wp-block-post-title { + display: none; +} + +body.single .wp-block-wporg-local-navigation-bar .wp-block-query-title, +body.page .wp-block-wporg-local-navigation-bar .wp-block-query-title { + display: none; +} + +/* + * A linked post title should be blueberry (needed to override parent setting, + * where post title is the same for linked or not). + */ +.wp-block-post-title a:where(:not(.wp-element-button)) { + color: var(--wp--preset--color--blueberry-1); +} + +/* Add default focus style. */ +:where(main) a:where(:not(.wp-element-button)):focus, +:where(main) button:where(:not([class*="wp-block-button"])):focus { + outline: none; + border-radius: 2px; + box-shadow: 0 0 0 1.5px var(--wp--custom--link--color--text); +} + +/* Style pattern tags. */ +.wp-block-post-terms { + line-height: var(--wp--custom--body--small--typography--line-height); +} + +.wp-block-post-terms a { + text-decoration: none; + display: inline-block; + padding: 0.25rem 1rem; + border-radius: 1.5em; + background: var(--wp--preset--color--blueberry-4); + color: var(--wp--preset--color--blueberry-1); +} + +.wp-block-post-terms a:hover, +.wp-block-post-terms a:focus { + text-decoration: underline; +} + +/* Hide the separator so it still takes up space. */ +.wp-block-post-terms .wp-block-post-terms__separator { + display: inline-block; + visibility: hidden; + width: var(--wp--preset--spacing--10); +} + +.wp-block-button.is-edit-link a { + display: flex; + align-items: center; + gap: 0.25em; + + /* Adjust the size of the button to make it more inline with the title. */ + --wp--custom--button--spacing--padding--top: 3px !important; + --wp--custom--button--spacing--padding--bottom: 3px !important; + --wp--custom--button--spacing--padding--left: 6px !important; + --wp--custom--button--spacing--padding--right: 6px !important; +} + +.wp-block-button.is-edit-link a:before { + content: ''; + display: inline-block; + height: 1.5em; + width: 1.5em; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='25' fill='none' viewBox='0 0 24 25'%3E%3Cpath fill='%233858E9' d='m19 7.478-3-3-8.5 8.5-1 4 4-1 8.5-8.5ZM12 18.978H5v1.5h7v-1.5Z'/%3E%3C/svg%3E"); + background-size: contain; +} + +.wp-block-button.is-edit-link a:active:before { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='25' fill='none' viewBox='0 0 24 25'%3E%3Cpath fill='%23ffffff' d='m19 7.478-3-3-8.5 8.5-1 4 4-1 8.5-8.5ZM12 18.978H5v1.5h7v-1.5Z'/%3E%3C/svg%3E"); +} + +/* Drop to a two column layout for grids (except the "more by"). */ +@media (max-width: 1280px) { + :where(body:not(.single-wporg-pattern)) .wp-block-post-template.is-layout-grid.columns-3 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +/* Pages using this have the 1760 wide width. */ +.wporg-patterns-nested-alignfull { + width: 100vw; + margin-inline-start: calc(1760px / 2 - 50vw); +} + +/* 1920 = 1760 + 2 * edge-space. */ +@media (max-width: 1920px) { + .wporg-patterns-nested-alignfull { + margin-inline-start: calc(var(--wp--preset--spacing--edge-space) * -1); + } +} diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/archive.html b/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/archive.html new file mode 100644 index 000000000..7a68ecd00 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/archive.html @@ -0,0 +1,21 @@ + + + +
+ +
+ + + +
+ + +
+ +
+ + +
+ + + \ No newline at end of file diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/front-page.html b/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/front-page.html new file mode 100644 index 000000000..332c4c60d --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/front-page.html @@ -0,0 +1,17 @@ + + + +
+ + +
+ +
+ + +
+ + + + + diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/index.html b/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/index.html new file mode 100644 index 000000000..7a68ecd00 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/index.html @@ -0,0 +1,21 @@ + + + +
+ +
+ + + +
+ + +
+ +
+ + +
+ + + \ No newline at end of file diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/page-favorites.html b/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/page-favorites.html new file mode 100644 index 000000000..dc1ec8b3b --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/page-favorites.html @@ -0,0 +1,16 @@ + + + +
+ + +
+ + + +
+ +
+ + + diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/page-my-patterns.html b/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/page-my-patterns.html new file mode 100644 index 000000000..5d9d4889e --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/page-my-patterns.html @@ -0,0 +1,16 @@ + + + +
+ + +
+ + + +
+ +
+ + + diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/page.html b/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/page.html new file mode 100644 index 000000000..3b0e6af4d --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/page.html @@ -0,0 +1,9 @@ + + + +
+ +
+ + + diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/search.html b/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/search.html new file mode 100644 index 000000000..1df974299 --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/search.html @@ -0,0 +1,21 @@ + + + +
+ +
+ + + +
+ + +
+ +
+ + +
+ + + diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/single.html b/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/single.html new file mode 100644 index 000000000..a567e7d7e --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/templates/single.html @@ -0,0 +1,9 @@ + + + +
+ +
+ + + \ No newline at end of file diff --git a/public_html/wp-content/themes/wporg-pattern-directory-2024/theme.json b/public_html/wp-content/themes/wporg-pattern-directory-2024/theme.json new file mode 100644 index 000000000..433155bfa --- /dev/null +++ b/public_html/wp-content/themes/wporg-pattern-directory-2024/theme.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://schemas.wp.org/trunk/theme.json", + "version": 2, + "settings": { + "custom": { + "wporg-pattern-thumbnail": { + "border": { + "color": "rgba(0, 0, 0, 0.10)", + "width": "1px", + "radius": "2px" + } + }, + "wporg-pattern-preview": { + "border": { + "color": "var(--wp--preset--color--light-grey-2)", + "width": "20px" + } + } + } + }, + "styles": { + "blocks": { + "core/post-title": { + "elements": { + "link": { + "color": { + "text": "var(--wp--preset--color--blueberry-1)" + } + } + } + } + } + } +} diff --git a/yarn.lock b/yarn.lock index f5338d23b..dc7c42f1c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4810,6 +4810,13 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== +a11y-dialog@^8.0.4: + version "8.0.4" + resolved "https://registry.yarnpkg.com/a11y-dialog/-/a11y-dialog-8.0.4.tgz#731a983d38d059665ff7e62daccedf5214131be8" + integrity sha512-MQrLxqLPx4E8+ynGwulBs6OjtxQT/XemgnlNPb+cR8K/qdwHBQv1WZQZpBuTVhlWGpAwWmSYffUZ/78anhcD3w== + dependencies: + focusable-selectors "^0.8.0" + abab@^2.0.5, abab@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" @@ -7607,6 +7614,11 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +focusable-selectors@^0.8.0: + version "0.8.4" + resolved "https://registry.yarnpkg.com/focusable-selectors/-/focusable-selectors-0.8.4.tgz#2ee34129b29371766ce201499e3b88c6f79ea4c1" + integrity sha512-0XxbkD0KhOnX10qmnfF9U8DkDD8N/e4M77wMYw2Itoi4vdcoRjSkqXLZFIzkrLIOxzmzCGy88fNG1EbeXMD/zw== + follow-redirects@^1.0.0: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"