diff --git a/.eslintrc.js b/.eslintrc.js index caf01b7cbd71d4..2e92b495691873 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -216,6 +216,12 @@ module.exports = { }, ], 'no-restricted-syntax': [ 'error', ...restrictedSyntax ], + 'jsdoc/check-tag-names': [ + 'error', + { + definedTags: [ 'jest-environment' ], + }, + ], }, overrides: [ { @@ -315,6 +321,7 @@ module.exports = { ...[ 'BorderBoxControl', 'BorderControl', + 'BoxControl', 'ComboboxControl', 'CustomSelectControl', 'DimensionControl', diff --git a/.github/workflows/bundle-size.yml b/.github/workflows/bundle-size.yml index 6106eee492c32a..499a2c020255cb 100644 --- a/.github/workflows/bundle-size.yml +++ b/.github/workflows/bundle-size.yml @@ -52,5 +52,5 @@ jobs: - uses: preactjs/compressed-size-action@f780fd104362cfce9e118f9198df2ee37d12946c # v2.6.0 with: repo-token: '${{ secrets.GITHUB_TOKEN }}' - pattern: '{build/**/*.min.js,build/**/*.css}' + pattern: '{build/**/*.min.js,build/**/*.css,build-module/**/*.min.js}' clean-script: 'distclean' diff --git a/.github/workflows/rnmobile-android-runner.yml b/.github/workflows/rnmobile-android-runner.yml index 3efd7d79ee2276..2c3998c2952808 100644 --- a/.github/workflows/rnmobile-android-runner.yml +++ b/.github/workflows/rnmobile-android-runner.yml @@ -28,7 +28,7 @@ jobs: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - name: Use desired version of Java - uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2 + uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # v4.3.0 with: distribution: 'corretto' java-version: '17' @@ -47,7 +47,7 @@ jobs: run: npm run native test:e2e:setup - name: Gradle cache - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0 - name: AVD cache uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 diff --git a/.stylelintrc.json b/.stylelintrc.json index 663befa2e4ce06..557376e02c4062 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -1,5 +1,5 @@ { - "extends": "@wordpress/stylelint-config/scss", + "extends": "@wordpress/stylelint-config/scss-stylistic", "rules": { "at-rule-empty-line-before": null, "at-rule-no-unknown": null, @@ -21,7 +21,7 @@ } ], "font-weight-notation": null, - "max-line-length": null, + "@stylistic/max-line-length": null, "no-descending-specificity": null, "property-disallowed-list": [ [ "order" ], @@ -34,7 +34,7 @@ "value-keyword-case": null, "scss/operator-no-unspaced": null, "scss/selector-no-redundant-nesting-selector": null, - "scss/at-import-partial-extension": null, + "scss/load-partial-extension": null, "scss/no-global-function-names": null, "scss/comment-no-empty": null, "scss/at-extend-no-missing-placeholder": null, diff --git a/backport-changelog/6.7/7314.md b/backport-changelog/6.7/7314.md new file mode 100644 index 00000000000000..7d75cdff0f9075 --- /dev/null +++ b/backport-changelog/6.7/7314.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/7314 + +* https://github.com/WordPress/gutenberg/pull/64167 diff --git a/bin/build-plugin-zip.sh b/bin/build-plugin-zip.sh index 4ba931c4a4aeb6..c823ca6a8017f1 100755 --- a/bin/build-plugin-zip.sh +++ b/bin/build-plugin-zip.sh @@ -78,26 +78,16 @@ npm run build php bin/generate-gutenberg-php.php > gutenberg.tmp.php mv gutenberg.tmp.php gutenberg.php -build_files=$( - ls build/*/*.{js,js.map,css,asset.php} \ - build/block-library/blocks/*.php \ - build/block-library/blocks/*/block.json \ - build/block-library/blocks/*/*.{js,js.map,css,asset.php} \ - build/edit-widgets/blocks/*/block.json \ - build/widgets/blocks/*.php \ - build/widgets/blocks/*/block.json \ - build/style-engine/*.php \ -) - - # Generate the plugin zip file. status "Creating archive... 🎁" -zip -r gutenberg.zip \ +zip --recurse-paths --no-dir-entries \ + gutenberg.zip \ gutenberg.php \ lib \ packages/block-serialization-default-parser/*.php \ post-content.php \ - $build_files \ + build \ + build-module \ readme.txt \ changelog.txt \ README.md diff --git a/changelog.txt b/changelog.txt index dc0a2487f725ff..b0e6c8e907582e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,7 +1,6 @@ == Changelog == -= 19.2.0-rc.1 = - += 19.2.0 = ## Changelog @@ -40,6 +39,12 @@ - Rely on `Text` component instead of `Truncate` in bindings panel. ([65007](https://github.com/WordPress/gutenberg/pull/65007)) - Remove `getPlaceholder` API and rely on `key` argument or source label. ([64910](https://github.com/WordPress/gutenberg/pull/64910)) +#### Data Views +- Add: Reorder control at the field level on the new view configuration UI. ([64381](https://github.com/WordPress/gutenberg/pull/64381)) +- Dataviews Filter search widget: Do not use Composite store. ([64985](https://github.com/WordPress/gutenberg/pull/64985)) +- Dataviews list view: Do not use Composite store. ([64987](https://github.com/WordPress/gutenberg/pull/64987)) +- Move bulk actions menu to the Footer, consolidate with floating toolbar and total items display. ([64268](https://github.com/WordPress/gutenberg/pull/64268)) + #### Block Editor - Add 'Reset' option to MediaReplaceFlow component. ([64826](https://github.com/WordPress/gutenberg/pull/64826)) - Block Patterns List: Do not use Composite store. ([64983](https://github.com/WordPress/gutenberg/pull/64983)) @@ -106,12 +111,14 @@ - Add safeguard to `mediaUploadMiddleware`. ([64843](https://github.com/WordPress/gutenberg/pull/64843)) - Allow multi-select on iOS Safari/touch devices. ([63671](https://github.com/WordPress/gutenberg/pull/63671)) +- Core Data: Fix the 'query._fields' property check inside 'getEntityRecord' resolver. ([65079](https://github.com/WordPress/gutenberg/pull/65079)) - Fix Modify content-locked menu item not showing if the block is not selected. ([61605](https://github.com/WordPress/gutenberg/pull/61605)) - Fix editor error in Safari due to availability of checkVisibility method. ([65069](https://github.com/WordPress/gutenberg/pull/65069)) - Fix: Pagination arrows are pointing in the wrong direction in RTL languages. ([64962](https://github.com/WordPress/gutenberg/pull/64962)) - Footnotes: Only replace attribute if footnotes were detected. ([63935](https://github.com/WordPress/gutenberg/pull/63935)) - Paste: Fix image paste from Google Forms. ([64502](https://github.com/WordPress/gutenberg/pull/64502)) - Revert Focus pattern inserter search when activating zoom out inserter. ([64748](https://github.com/WordPress/gutenberg/pull/64748)) +- Try: Update block warnings. ([64997](https://github.com/WordPress/gutenberg/pull/64997)) #### Block Library - De-duplicate block toolbar icons for patterns. ([65054](https://github.com/WordPress/gutenberg/pull/65054)) @@ -175,6 +182,9 @@ #### Site Editor - DataViews: Fix pattern title direction in RTL languages. ([64967](https://github.com/WordPress/gutenberg/pull/64967)) +#### Typography +- Site Title, Post Title: Fix typography for blocks with `a` children. ([64911](https://github.com/WordPress/gutenberg/pull/64911)) + #### NUX - Fix visibility of the template Welcome Guide in the Site Editor. ([64789](https://github.com/WordPress/gutenberg/pull/64789)) @@ -223,6 +233,9 @@ - Add 'OPTIONS /page' to preloaded paths. ([64890](https://github.com/WordPress/gutenberg/pull/64890)) - Editor: Don't use selector shortcuts for the Site data. ([64884](https://github.com/WordPress/gutenberg/pull/64884)) +#### Interactivity API +- Prevent calling `proxifyContext` with context proxies inside `wp-context`. ([65090](https://github.com/WordPress/gutenberg/pull/65090)) + #### Block Library - Media & Text: Don't use background-image. ([64981](https://github.com/WordPress/gutenberg/pull/64981)) @@ -353,7 +366,7 @@ The following PRs were merged by first-time contributors: The following contributors merged PRs in this release: -@aaronrobertshaw @afercia @akasunil @Aljullu @andrewserong @atachibana @benoitchantre @carolinan @cbravobernal @ciampo @DAreRodz @dcalhoun @desrosj @dsas @ellatrix @fullofcaffeine @getdave @gziolo @Imran92 @imrraaj @jacobcassidy @jameskoster @jasmussen @jawadmalikdev @jeryj @jorgefilipecosta @jsnajdr @juanmaguitar @kevin940726 @lezama @Mamaduka @matiasbenedetto @mirka @noisysocks @ntsekouras @oandregal @ockham @ramonjd @richtabor @rithik56 @rohitmathur-7 @ryanwelcher @SantosGuillamot @scruffian @sgomes @shail-mehta @spacedmonkey @stokesman @swissspidy @t-hamano @talldan @tjcafferkey @tyxla +@aaronrobertshaw @afercia @akasunil @Aljullu @andrewserong @atachibana @benoitchantre @carolinan @cbravobernal @ciampo @DAreRodz @dcalhoun @desrosj @dsas @ellatrix @fullofcaffeine @getdave @gziolo @Imran92 @imrraaj @jacobcassidy @jameskoster @jasmussen @jawadmalikdev @jeryj @jorgefilipecosta @jsnajdr @juanmaguitar @kevin940726 @lezama @Mamaduka @matiasbenedetto @mirka @noisysocks @ntsekouras @oandregal @ockham @rafaelgallani @ramonjd @richtabor @rithik56 @rohitmathur-7 @ryanwelcher @SantosGuillamot @scruffian @sgomes @shail-mehta @spacedmonkey @stokesman @swissspidy @t-hamano @talldan @tjcafferkey @tyxla = 19.1.0 = diff --git a/docs/reference-guides/block-api/block-api-versions.md b/docs/reference-guides/block-api/block-api-versions.md index b4b11e7c23c7cc..d89362777ca58c 100644 --- a/docs/reference-guides/block-api/block-api-versions.md +++ b/docs/reference-guides/block-api/block-api-versions.md @@ -3,7 +3,7 @@ This document lists the changes made between the different API versions. ## Version 3 (>= WordPress 6.3) -- The post editor will be iframed if all registered blocks have a Block API version 3 or higher and the editor has no classic meta boxes below the blocks. Adding version 3 support means that the block should work inside an iframe, though the block may still be rendered outside the iframe if not all blocks support version 3. +- The post editor will be iframed if all registered blocks have a Block API version 3 or higher. Adding version 3 support means that the block should work inside an iframe, though the block may still be rendered outside the iframe if not all blocks support version 3. ## Version 2 (>= WordPress 5.6) diff --git a/docs/reference-guides/block-api/block-context.md b/docs/reference-guides/block-api/block-context.md index 5fdc670fe60403..09c33dfb71b7c3 100644 --- a/docs/reference-guides/block-api/block-context.md +++ b/docs/reference-guides/block-api/block-context.md @@ -141,7 +141,7 @@ export default function Edit( props ) { return (
setAttributes( { recordId: Number( val ) } ) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index b9cae44550181c..0e0de9d4f8a507 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -493,7 +493,7 @@ Add a submenu to your navigation. ([Source](https://github.com/WordPress/gutenbe - **Name:** core/navigation-submenu - **Category:** design - **Parent:** core/navigation -- **Supports:** interactivity (clientNavigation), ~~html~~, ~~reusable~~ +- **Supports:** interactivity (clientNavigation), typography (fontSize, lineHeight), ~~html~~, ~~reusable~~ - **Attributes:** description, id, isTopLevelItem, kind, label, opensInNewTab, rel, title, type, url ## Page Break diff --git a/docs/reference-guides/slotfills/README.md b/docs/reference-guides/slotfills/README.md index bab08dc34c40c1..874a3a69096b81 100644 --- a/docs/reference-guides/slotfills/README.md +++ b/docs/reference-guides/slotfills/README.md @@ -166,7 +166,7 @@ import { __ } from '@wordpress/i18n'; */ const SiteEditorDocumentSettingPanel = () => { // Retrieve information about the current post type. - const { isViewable } = useSelect( ( select ) => { + const isViewable = useSelect( ( select ) => { const postTypeName = select( editorStore ).getCurrentPostType(); const postTypeObject = select( coreStore ).getPostType( postTypeName ); diff --git a/gutenberg.php b/gutenberg.php index 117f4168524d8a..342c61fb56cd13 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -5,7 +5,7 @@ * Description: Printing since 1440. This is the development plugin for the block editor, site editor, and other future WordPress core functionality. * Requires at least: 6.5 * Requires PHP: 7.2 - * Version: 19.2.0-rc.1 + * Version: 19.2.0 * Author: Gutenberg Team * Text Domain: gutenberg * @@ -15,6 +15,8 @@ ### BEGIN AUTO-GENERATED DEFINES defined( 'GUTENBERG_DEVELOPMENT_MODE' ) or define( 'GUTENBERG_DEVELOPMENT_MODE', true ); ### END AUTO-GENERATED DEFINES +defined( 'GUTENBERG_MINIMUM_WP_VERSION' ) or define( 'GUTENBERG_MINIMUM_WP_VERSION', '6.5' ); + gutenberg_pre_init(); @@ -26,7 +28,7 @@ function gutenberg_wordpress_version_notice() { echo '

'; /* translators: %s: Minimum required version */ - printf( __( 'Gutenberg requires WordPress %s or later to function properly. Please upgrade WordPress before activating Gutenberg.', 'gutenberg' ), '5.9' ); + printf( __( 'Gutenberg requires WordPress %s or later to function properly. Please upgrade WordPress before activating Gutenberg.', 'gutenberg' ), GUTENBERG_MINIMUM_WP_VERSION ); echo '

'; deactivate_plugins( array( 'gutenberg/gutenberg.php' ) ); @@ -67,7 +69,7 @@ function gutenberg_pre_init() { // Compare against major release versions (X.Y) rather than minor (X.Y.Z) // unless a minor release is the actual minimum requirement. WordPress reports // X.Y for its major releases. - if ( version_compare( $version, '5.9', '<' ) ) { + if ( version_compare( $version, GUTENBERG_MINIMUM_WP_VERSION, '<' ) ) { add_action( 'admin_notices', 'gutenberg_wordpress_version_notice' ); return; } diff --git a/lib/class-wp-rest-global-styles-controller-gutenberg.php b/lib/class-wp-rest-global-styles-controller-gutenberg.php index 421408e6f20b4a..e33304e596e129 100644 --- a/lib/class-wp-rest-global-styles-controller-gutenberg.php +++ b/lib/class-wp-rest-global-styles-controller-gutenberg.php @@ -13,25 +13,30 @@ /** * Base Global Styles REST API Controller. */ -class WP_REST_Global_Styles_Controller_Gutenberg extends WP_REST_Controller { +class WP_REST_Global_Styles_Controller_Gutenberg extends WP_REST_Posts_Controller { /** - * Post type. + * Whether the controller supports batching. * - * @since 5.9.0 - * @var string + * @since 6.6.0 + * @var array */ - protected $post_type; + protected $allow_batch = array( 'v1' => false ); /** * Constructor. * * @since 5.9.0 */ - public function __construct() { - $this->namespace = 'wp/v2'; - $this->rest_base = 'global-styles'; - $this->post_type = 'wp_global_styles'; + /** + * Constructor. + * + * @since 6.6.0 + * + * @param string $post_type Post type. + */ + public function __construct( $post_type = 'wp_global_styles' ) { + parent::__construct( $post_type ); } /** @@ -54,8 +59,14 @@ public function register_routes() { 'type' => 'string', ), ), + 'allow_batch' => $this->allow_batch, ), - ) + ), + /* + * $override is set to true to avoid conflicts with the core endpoint. + * Do not sync to WordPress core. + */ + true ); // List themes global styles. @@ -65,8 +76,10 @@ public function register_routes() { sprintf( '/%s/themes/(?P%s)', $this->rest_base, - // Matches theme's directory: `/themes///` or `/themes//`. - // Excludes invalid directory name characters: `/:<>*?"|`. + /* + * Matches theme's directory: `/themes///` or `/themes//`. + * Excludes invalid directory name characters: `/:<>*?"|`. + */ '[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?' ), array( @@ -81,8 +94,14 @@ public function register_routes() { 'sanitize_callback' => array( $this, '_sanitize_global_styles_callback' ), ), ), + 'allow_batch' => $this->allow_batch, ), - ) + ), + /* + * $override is set to true to avoid conflicts with the core endpoint. + * Do not sync to WordPress core. + */ + true ); // Lists/updates a single global style variation based on the given id. @@ -108,8 +127,14 @@ public function register_routes() { 'permission_callback' => array( $this, 'update_item_permissions_check' ), 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) + 'schema' => array( $this, 'get_public_item_schema' ), + 'allow_batch' => $this->allow_batch, + ), + /* + * $override is set to true to avoid conflicts with the core endpoint. + * Do not sync to WordPress core. + */ + true ); } @@ -196,28 +221,10 @@ public function get_item_permissions_check( $request ) { * @param WP_Post $post Post object. * @return bool Whether the post can be read. */ - protected function check_read_permission( $post ) { + public function check_read_permission( $post ) { return current_user_can( 'read_post', $post->ID ); } - /** - * Returns the given global styles config. - * - * @since 5.9.0 - * - * @param WP_REST_Request $request The request instance. - * - * @return WP_REST_Response|WP_Error - */ - public function get_item( $request ) { - $post = $this->get_post( $request['id'] ); - if ( is_wp_error( $post ) ) { - return $post; - } - - return $this->prepare_item_for_response( $post, $request ); - } - /** * Checks if a given request has access to write a single global styles config. * @@ -243,61 +250,12 @@ public function update_item_permissions_check( $request ) { return true; } - /** - * Checks if a global style can be edited. - * - * @since 5.9.0 - * - * @param WP_Post $post Post object. - * @return bool Whether the post can be edited. - */ - protected function check_update_permission( $post ) { - return current_user_can( 'edit_post', $post->ID ); - } - - /** - * Updates a single global style config. - * - * @since 5.9.0 - * @since 6.2.0 Added validation of styles.css property. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. - */ - public function update_item( $request ) { - $post_before = $this->get_post( $request['id'] ); - if ( is_wp_error( $post_before ) ) { - return $post_before; - } - - $changes = $this->prepare_item_for_database( $request ); - if ( is_wp_error( $changes ) ) { - return $changes; - } - - $result = wp_update_post( wp_slash( (array) $changes ), true, false ); - if ( is_wp_error( $result ) ) { - return $result; - } - - $post = get_post( $request['id'] ); - $fields_update = $this->update_additional_fields_for_object( $post, $request ); - if ( is_wp_error( $fields_update ) ) { - return $fields_update; - } - - wp_after_insert_post( $post, true, $post_before ); - - $response = $this->prepare_item_for_response( $post, $request ); - - return rest_ensure_response( $response ); - } - /** * Prepares a single global styles config for update. * * @since 5.9.0 * @since 6.2.0 Added validation of styles.css property. + * @since 6.6.0 Added registration of block style variations from theme.json sources (theme.json, user theme.json, partials). * * @param WP_REST_Request $request Request object. * @return stdClass|WP_Error Prepared item on success. WP_Error on when the custom CSS is not valid. @@ -394,10 +352,12 @@ public function prepare_item_for_response( $post, $request ) { // phpcs:ignore V } if ( rest_is_field_included( 'title.rendered', $fields ) ) { add_filter( 'protected_title_format', array( $this, 'protected_title_format' ) ); + add_filter( 'private_title_format', array( $this, 'protected_title_format' ) ); $data['title']['rendered'] = get_the_title( $post->ID ); remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) ); + remove_filter( 'private_title_format', array( $this, 'protected_title_format' ) ); } if ( rest_is_field_included( 'settings', $fields ) ) { @@ -426,7 +386,7 @@ public function prepare_item_for_response( $post, $request ) { // phpcs:ignore V } $response->add_links( $links ); if ( ! empty( $links['self']['href'] ) ) { - $actions = $this->get_available_actions(); + $actions = $this->get_available_actions( $post, $request ); $self = $links['self']['href']; foreach ( $actions as $rel ) { $response->add_link( $rel, $self ); @@ -450,9 +410,12 @@ protected function prepare_links( $id ) { $base = sprintf( '%s/%s', $this->namespace, $this->rest_base ); $links = array( - 'self' => array( + 'self' => array( 'href' => rest_url( trailingslashit( $base ) . $id ), ), + 'about' => array( + 'href' => rest_url( 'wp/v2/types/' . $this->post_type ), + ), ); if ( post_type_supports( $this->post_type, 'revisions' ) ) { @@ -473,13 +436,16 @@ protected function prepare_links( $id ) { * * @since 5.9.0 * @since 6.2.0 Added 'edit-css' action. + * @since 6.6.0 Added $post and $request parameters. * + * @param WP_Post $post Post object. + * @param WP_REST_Request $request Request object. * @return array List of link relations. */ - protected function get_available_actions() { + protected function get_available_actions( $post, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable $rels = array(); - $post_type = get_post_type_object( $this->post_type ); + $post_type = get_post_type_object( $post->post_type ); if ( current_user_can( $post_type->cap->publish_posts ) ) { $rels[] = 'https://api.w.org/action-publish'; } @@ -491,21 +457,6 @@ protected function get_available_actions() { return $rels; } - /** - * Overwrites the default protected title format. - * - * By default, WordPress will show password protected posts with a title of - * "Protected: %s", as the REST API communicates the protected status of a post - * in a machine readable format, we remove the "Protected: " prefix. - * - * @since 5.9.0 - * - * @return string Protected title format. - */ - public function protected_title_format() { - return '%s'; - } - /** * Retrieves the query params for the global styles collection. * @@ -589,7 +540,7 @@ public function get_theme_item_permissions_check( $request ) { // phpcs:ignore V /* * Verify if the current user has edit_theme_options capability. - * This capability is required to edit/view/delete templates. + * This capability is required to edit/view/delete global styles. */ if ( ! current_user_can( 'edit_theme_options' ) ) { return new WP_Error( @@ -623,8 +574,8 @@ public function get_theme_item( $request ) { } $theme = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( 'theme' ); - $data = array(); $fields = $this->get_fields_for_response( $request ); + $data = array(); if ( rest_is_field_included( 'settings', $fields ) ) { $data['settings'] = $theme->get_settings(); @@ -669,7 +620,7 @@ public function get_theme_items_permissions_check( $request ) { // phpcs:ignore /* * Verify if the current user has edit_theme_options capability. - * This capability is required to edit/view/delete templates. + * This capability is required to edit/view/delete global styles. */ if ( ! current_user_can( 'edit_theme_options' ) ) { return new WP_Error( diff --git a/lib/compat/wordpress-6.6/rest-api.php b/lib/compat/wordpress-6.6/rest-api.php index fee9c71b86c070..eadd3b1d376a72 100644 --- a/lib/compat/wordpress-6.6/rest-api.php +++ b/lib/compat/wordpress-6.6/rest-api.php @@ -88,76 +88,116 @@ function gutenberg_register_global_styles_revisions_endpoints() { add_action( 'rest_api_init', 'gutenberg_register_global_styles_revisions_endpoints' ); -if ( ! function_exists( 'gutenberg_register_wp_rest_themes_stylesheet_directory_uri_field' ) ) { - /** - * Adds `stylesheet_uri` fields to WP_REST_Themes_Controller class. - */ - function gutenberg_register_wp_rest_themes_stylesheet_directory_uri_field() { - register_rest_field( - 'theme', - 'stylesheet_uri', - array( - 'get_callback' => function ( $item ) { - if ( ! empty( $item['stylesheet'] ) ) { - $theme = wp_get_theme( $item['stylesheet'] ); - $current_theme = wp_get_theme(); - if ( $theme->get_stylesheet() === $current_theme->get_stylesheet() ) { - return get_stylesheet_directory_uri(); - } else { - return $theme->get_stylesheet_directory_uri(); - } +/** + * Adds `stylesheet_uri` fields to WP_REST_Themes_Controller class. + */ +function gutenberg_register_wp_rest_themes_stylesheet_directory_uri_field() { + register_rest_field( + 'theme', + 'stylesheet_uri', + array( + 'get_callback' => function ( $item ) { + if ( ! empty( $item['stylesheet'] ) ) { + $theme = wp_get_theme( $item['stylesheet'] ); + $current_theme = wp_get_theme(); + if ( $theme->get_stylesheet() === $current_theme->get_stylesheet() ) { + return get_stylesheet_directory_uri(); + } else { + return $theme->get_stylesheet_directory_uri(); } + } - return null; - }, - 'schema' => array( - 'type' => 'string', - 'description' => __( 'The uri for the theme\'s stylesheet directory.', 'gutenberg' ), - 'format' => 'uri', - 'readonly' => true, - 'context' => array( 'view', 'edit', 'embed' ), - ), - ) - ); - } + return null; + }, + 'schema' => array( + 'type' => 'string', + 'description' => __( 'The uri for the theme\'s stylesheet directory.', 'gutenberg' ), + 'format' => 'uri', + 'readonly' => true, + 'context' => array( 'view', 'edit', 'embed' ), + ), + ) + ); } add_action( 'rest_api_init', 'gutenberg_register_wp_rest_themes_stylesheet_directory_uri_field' ); -if ( ! function_exists( 'gutenberg_register_wp_rest_themes_template_directory_uri_field' ) ) { - /** - * Adds `template_uri` fields to WP_REST_Themes_Controller class. - */ - function gutenberg_register_wp_rest_themes_template_directory_uri_field() { - register_rest_field( - 'theme', - 'template_uri', - array( - 'get_callback' => function ( $item ) { - if ( ! empty( $item['stylesheet'] ) ) { - $theme = wp_get_theme( $item['stylesheet'] ); - $current_theme = wp_get_theme(); - if ( $theme->get_stylesheet() === $current_theme->get_stylesheet() ) { - return get_template_directory_uri(); - } else { - return $theme->get_template_directory_uri(); - } +/** + * Adds `template_uri` fields to WP_REST_Themes_Controller class. + */ +function gutenberg_register_wp_rest_themes_template_directory_uri_field() { + register_rest_field( + 'theme', + 'template_uri', + array( + 'get_callback' => function ( $item ) { + if ( ! empty( $item['stylesheet'] ) ) { + $theme = wp_get_theme( $item['stylesheet'] ); + $current_theme = wp_get_theme(); + if ( $theme->get_stylesheet() === $current_theme->get_stylesheet() ) { + return get_template_directory_uri(); + } else { + return $theme->get_template_directory_uri(); } + } - return null; - }, - 'schema' => array( - 'type' => 'string', - 'description' => __( 'The uri for the theme\'s template directory. If this is a child theme, this refers to the parent theme, otherwise this is the same as the theme\'s stylesheet directory.', 'gutenberg' ), - 'format' => 'uri', - 'readonly' => true, - 'context' => array( 'view', 'edit', 'embed' ), - ), - ) - ); - } + return null; + }, + 'schema' => array( + 'type' => 'string', + 'description' => __( 'The uri for the theme\'s template directory. If this is a child theme, this refers to the parent theme, otherwise this is the same as the theme\'s stylesheet directory.', 'gutenberg' ), + 'format' => 'uri', + 'readonly' => true, + 'context' => array( 'view', 'edit', 'embed' ), + ), + ) + ); } add_action( 'rest_api_init', 'gutenberg_register_wp_rest_themes_template_directory_uri_field' ); +/** + * Adds `template` and `template_lock` fields to WP_REST_Post_Types_Controller class. + */ +function gutenberg_register_wp_rest_post_types_controller_fields() { + register_rest_field( + 'type', + 'template', + array( + 'get_callback' => function ( $item ) { + $post_type = get_post_type_object( $item['slug'] ); + if ( ! empty( $post_type ) ) { + return $post_type->template ?? array(); + } + }, + 'schema' => array( + 'type' => 'array', + 'description' => __( 'The block template associated with the post type.', 'gutenberg' ), + 'readonly' => true, + 'context' => array( 'view', 'edit', 'embed' ), + ), + ) + ); + register_rest_field( + 'type', + 'template_lock', + array( + 'get_callback' => function ( $item ) { + $post_type = get_post_type_object( $item['slug'] ); + if ( ! empty( $post_type ) ) { + return ! empty( $post_type->template_lock ) ? $post_type->template_lock : false; + } + }, + 'schema' => array( + 'type' => array( 'string', 'boolean' ), + 'enum' => array( 'all', 'insert', 'contentOnly', false ), + 'description' => __( 'The template_lock associated with the post type, or false if none.', 'gutenberg' ), + 'readonly' => true, + 'context' => array( 'view', 'edit', 'embed' ), + ), + ) + ); +} +add_action( 'rest_api_init', 'gutenberg_register_wp_rest_post_types_controller_fields' ); + /** * Preload theme and global styles paths to avoid flash of variation styles in post editor. * diff --git a/lib/compat/wordpress-6.7/blocks.php b/lib/compat/wordpress-6.7/blocks.php index 18d21621be7197..6b9526f8056fd3 100644 --- a/lib/compat/wordpress-6.7/blocks.php +++ b/lib/compat/wordpress-6.7/blocks.php @@ -43,3 +43,63 @@ function gutenberg_filter_block_type_metadata_settings_allow_variations_php_file return $settings; } add_filter( 'block_type_metadata_settings', 'gutenberg_filter_block_type_metadata_settings_allow_variations_php_file', 10, 2 ); + +/** + * Adds post format query vars to the query loop block's WP_Query when the block's attributes call for them. + * + * @see 'query_loop_block_query_vars' + * + * @param array $query The query vars. + * @param WP_Block $block Block instance. + * @return array The filtered query vars. + */ +function gutenberg_add_format_query_vars_to_query_loop_block( $query, $block ) { + // Return early if there is no format or if the format is not an array. + if ( empty( $block->context['query']['format'] ) || ! is_array( $block->context['query']['format'] ) ) { + return $query; + } + + $formats = $block->context['query']['format']; + $tax_query = array( 'relation' => 'OR' ); + + // The default post format, 'standard', is not stored in the database. + // If 'standard' is part of the request, the query needs to exclude all post items that + // have a format assigned. + if ( in_array( 'standard', $formats, true ) ) { + $tax_query[] = array( + 'taxonomy' => 'post_format', + 'field' => 'slug', + 'terms' => array(), + 'operator' => 'NOT EXISTS', + ); + // Remove the standard format, since it cannot be queried. + unset( $formats[ array_search( 'standard', $formats, true ) ] ); + } + + // Add any remaining formats to the tax query. + if ( ! empty( $formats ) ) { + // Add the post-format- prefix. + $terms = array_map( + static function ( $format ) { + return 'post-format-' . $format; + }, + $formats + ); + + $tax_query[] = array( + 'taxonomy' => 'post_format', + 'field' => 'slug', + 'terms' => $terms, + 'operator' => 'IN', + ); + } + + // This condition is intended to prevent $tax_query from being added to $query + // if it only contains the relation. + if ( count( $tax_query ) > 1 ) { + $query['tax_query'][] = $tax_query; + } + + return $query; +} +add_filter( 'query_loop_block_query_vars', 'gutenberg_add_format_query_vars_to_query_loop_block', 10, 2 ); diff --git a/lib/compat/wordpress-6.7/class-gutenberg-rest-posts-controller-6-7.php b/lib/compat/wordpress-6.7/class-gutenberg-rest-posts-controller-6-7.php new file mode 100644 index 00000000000000..c7de4371c94f56 --- /dev/null +++ b/lib/compat/wordpress-6.7/class-gutenberg-rest-posts-controller-6-7.php @@ -0,0 +1,698 @@ + 400 ) + ); + } + + // Ensure an include parameter is set in case the orderby is set to 'include'. + if ( ! empty( $request['orderby'] ) && 'include' === $request['orderby'] && empty( $request['include'] ) ) { + return new WP_Error( + 'rest_orderby_include_missing_include', + __( 'You need to define an include parameter to order by include.' ), + array( 'status' => 400 ) + ); + } + + // Retrieve the list of registered collection query parameters. + $registered = $this->get_collection_params(); + $args = array(); + + /* + * This array defines mappings between public API query parameters whose + * values are accepted as-passed, and their internal WP_Query parameter + * name equivalents (some are the same). Only values which are also + * present in $registered will be set. + */ + $parameter_mappings = array( + 'author' => 'author__in', + 'author_exclude' => 'author__not_in', + 'exclude' => 'post__not_in', + 'include' => 'post__in', + 'menu_order' => 'menu_order', + 'offset' => 'offset', + 'order' => 'order', + 'orderby' => 'orderby', + 'page' => 'paged', + 'parent' => 'post_parent__in', + 'parent_exclude' => 'post_parent__not_in', + 'search' => 's', + 'search_columns' => 'search_columns', + 'slug' => 'post_name__in', + 'status' => 'post_status', + ); + + /* + * For each known parameter which is both registered and present in the request, + * set the parameter's value on the query $args. + */ + foreach ( $parameter_mappings as $api_param => $wp_param ) { + if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) { + $args[ $wp_param ] = $request[ $api_param ]; + } + } + + // Check for & assign any parameters which require special handling or setting. + $args['date_query'] = array(); + + if ( isset( $registered['before'], $request['before'] ) ) { + $args['date_query'][] = array( + 'before' => $request['before'], + 'column' => 'post_date', + ); + } + + if ( isset( $registered['modified_before'], $request['modified_before'] ) ) { + $args['date_query'][] = array( + 'before' => $request['modified_before'], + 'column' => 'post_modified', + ); + } + + if ( isset( $registered['after'], $request['after'] ) ) { + $args['date_query'][] = array( + 'after' => $request['after'], + 'column' => 'post_date', + ); + } + + if ( isset( $registered['modified_after'], $request['modified_after'] ) ) { + $args['date_query'][] = array( + 'after' => $request['modified_after'], + 'column' => 'post_modified', + ); + } + + // Ensure our per_page parameter overrides any provided posts_per_page filter. + if ( isset( $registered['per_page'] ) ) { + $args['posts_per_page'] = $request['per_page']; + } + + if ( isset( $registered['sticky'], $request['sticky'] ) ) { + $sticky_posts = get_option( 'sticky_posts', array() ); + if ( ! is_array( $sticky_posts ) ) { + $sticky_posts = array(); + } + if ( $request['sticky'] ) { + /* + * As post__in will be used to only get sticky posts, + * we have to support the case where post__in was already + * specified. + */ + $args['post__in'] = $args['post__in'] ? array_intersect( $sticky_posts, $args['post__in'] ) : $sticky_posts; + + /* + * If we intersected, but there are no post IDs in common, + * WP_Query won't return "no posts" for post__in = array() + * so we have to fake it a bit. + */ + if ( ! $args['post__in'] ) { + $args['post__in'] = array( 0 ); + } + } elseif ( $sticky_posts ) { + /* + * As post___not_in will be used to only get posts that + * are not sticky, we have to support the case where post__not_in + * was already specified. + */ + $args['post__not_in'] = array_merge( $args['post__not_in'], $sticky_posts ); + } + } + + $args = $this->prepare_tax_query( $args, $request ); + + if ( ! empty( $request['format'] ) ) { + $formats = $request['format']; + $tax_query = array( 'relation' => 'OR' ); + + // The default post format, 'standard', is not stored in the database. + // If 'standard' is part of the request, the query needs to exclude all post items that + // have a format assigned. + if ( in_array( 'standard', $formats, true ) ) { + $tax_query[] = array( + 'taxonomy' => 'post_format', + 'field' => 'slug', + 'terms' => array(), + 'operator' => 'NOT EXISTS', + ); + // Remove the standard format, since it cannot be queried. + unset( $formats[ array_search( 'standard', $formats, true ) ] ); + } + + // Add any remaining formats to the tax query. + if ( ! empty( $formats ) ) { + // Add the post-format- prefix. + $terms = array_map( + static function ( $format ) { + return 'post-format-' . $format; + }, + $formats + ); + + $tax_query[] = array( + 'taxonomy' => 'post_format', + 'field' => 'slug', + 'terms' => $terms, + 'operator' => 'IN', + ); + } + + // Enable filtering by both post formats and other taxonomies by combining them with AND. + if ( isset( $args['tax_query'] ) ) { + $args['tax_query'][] = array( + 'relation' => 'AND', + $tax_query, + ); + } else { + $args['tax_query'] = $tax_query; + } + } + + // Force the post_type argument, since it's not a user input variable. + $args['post_type'] = $this->post_type; + + /** + * Filters WP_Query arguments when querying posts via the REST API. + * + * The dynamic portion of the hook name, `$this->post_type`, refers to the post type slug. + * + * Possible hook names include: + * + * - `rest_post_query` + * - `rest_page_query` + * - `rest_attachment_query` + * + * Enables adding extra arguments or setting defaults for a post collection request. + * + * @since 4.7.0 + * @since 5.7.0 Moved after the `tax_query` query arg is generated. + * + * @link https://developer.wordpress.org/reference/classes/wp_query/ + * + * @param array $args Array of arguments for WP_Query. + * @param WP_REST_Request $request The REST API request. + */ + $args = apply_filters( "rest_{$this->post_type}_query", $args, $request ); + $query_args = $this->prepare_items_query( $args, $request ); + + $posts_query = new WP_Query(); + $query_result = $posts_query->query( $query_args ); + + // Allow access to all password protected posts if the context is edit. + if ( 'edit' === $request['context'] ) { + add_filter( 'post_password_required', array( $this, 'check_password_required' ), 10, 2 ); + } + + $posts = array(); + + update_post_author_caches( $query_result ); + update_post_parent_caches( $query_result ); + + if ( post_type_supports( $this->post_type, 'thumbnail' ) ) { + update_post_thumbnail_cache( $posts_query ); + } + + foreach ( $query_result as $post ) { + if ( ! $this->check_read_permission( $post ) ) { + continue; + } + + $data = $this->prepare_item_for_response( $post, $request ); + $posts[] = $this->prepare_response_for_collection( $data ); + } + + // Reset filter. + if ( 'edit' === $request['context'] ) { + remove_filter( 'post_password_required', array( $this, 'check_password_required' ) ); + } + + $page = (int) $query_args['paged']; + $total_posts = $posts_query->found_posts; + + if ( $total_posts < 1 && $page > 1 ) { + // Out-of-bounds, run the query again without LIMIT for total count. + unset( $query_args['paged'] ); + + $count_query = new WP_Query(); + $count_query->query( $query_args ); + $total_posts = $count_query->found_posts; + } + + $max_pages = (int) ceil( $total_posts / (int) $posts_query->query_vars['posts_per_page'] ); + + if ( $page > $max_pages && $total_posts > 0 ) { + return new WP_Error( + 'rest_post_invalid_page_number', + __( 'The page number requested is larger than the number of pages available.' ), + array( 'status' => 400 ) + ); + } + + $response = rest_ensure_response( $posts ); + + $response->header( 'X-WP-Total', (int) $total_posts ); + $response->header( 'X-WP-TotalPages', (int) $max_pages ); + + $request_params = $request->get_query_params(); + $collection_url = rest_url( rest_get_route_for_post_type_items( $this->post_type ) ); + $base = add_query_arg( urlencode_deep( $request_params ), $collection_url ); + + if ( $page > 1 ) { + $prev_page = $page - 1; + + if ( $prev_page > $max_pages ) { + $prev_page = $max_pages; + } + + $prev_link = add_query_arg( 'page', $prev_page, $base ); + $response->link_header( 'prev', $prev_link ); + } + if ( $max_pages > $page ) { + $next_page = $page + 1; + $next_link = add_query_arg( 'page', $next_page, $base ); + + $response->link_header( 'next', $next_link ); + } + + return $response; + } + + /** + * Retrieves the query params for the posts collection. + * + * @since 4.7.0 + * @since 5.4.0 The `tax_relation` query parameter was added. + * @since 5.7.0 The `modified_after` and `modified_before` query parameters were added. + * @since 6.7.0 The `format` query parameter was added. + * + * @return array Collection parameters. + */ + public function get_collection_params() { + $query_params = parent::get_collection_params(); + + $query_params['context']['default'] = 'view'; + + $query_params['after'] = array( + 'description' => __( 'Limit response to posts published after a given ISO8601 compliant date.' ), + 'type' => 'string', + 'format' => 'date-time', + ); + + $query_params['modified_after'] = array( + 'description' => __( 'Limit response to posts modified after a given ISO8601 compliant date.' ), + 'type' => 'string', + 'format' => 'date-time', + ); + + if ( post_type_supports( $this->post_type, 'author' ) ) { + $query_params['author'] = array( + 'description' => __( 'Limit result set to posts assigned to specific authors.' ), + 'type' => 'array', + 'items' => array( + 'type' => 'integer', + ), + 'default' => array(), + ); + $query_params['author_exclude'] = array( + 'description' => __( 'Ensure result set excludes posts assigned to specific authors.' ), + 'type' => 'array', + 'items' => array( + 'type' => 'integer', + ), + 'default' => array(), + ); + } + + $query_params['before'] = array( + 'description' => __( 'Limit response to posts published before a given ISO8601 compliant date.' ), + 'type' => 'string', + 'format' => 'date-time', + ); + + $query_params['modified_before'] = array( + 'description' => __( 'Limit response to posts modified before a given ISO8601 compliant date.' ), + 'type' => 'string', + 'format' => 'date-time', + ); + + $query_params['exclude'] = array( + 'description' => __( 'Ensure result set excludes specific IDs.' ), + 'type' => 'array', + 'items' => array( + 'type' => 'integer', + ), + 'default' => array(), + ); + + $query_params['include'] = array( + 'description' => __( 'Limit result set to specific IDs.' ), + 'type' => 'array', + 'items' => array( + 'type' => 'integer', + ), + 'default' => array(), + ); + + if ( 'page' === $this->post_type || post_type_supports( $this->post_type, 'page-attributes' ) ) { + $query_params['menu_order'] = array( + 'description' => __( 'Limit result set to posts with a specific menu_order value.' ), + 'type' => 'integer', + ); + } + + $query_params['offset'] = array( + 'description' => __( 'Offset the result set by a specific number of items.' ), + 'type' => 'integer', + ); + + $query_params['order'] = array( + 'description' => __( 'Order sort attribute ascending or descending.' ), + 'type' => 'string', + 'default' => 'desc', + 'enum' => array( 'asc', 'desc' ), + ); + + $query_params['orderby'] = array( + 'description' => __( 'Sort collection by post attribute.' ), + 'type' => 'string', + 'default' => 'date', + 'enum' => array( + 'author', + 'date', + 'id', + 'include', + 'modified', + 'parent', + 'relevance', + 'slug', + 'include_slugs', + 'title', + ), + ); + + if ( 'page' === $this->post_type || post_type_supports( $this->post_type, 'page-attributes' ) ) { + $query_params['orderby']['enum'][] = 'menu_order'; + } + + $post_type = get_post_type_object( $this->post_type ); + + if ( $post_type->hierarchical || 'attachment' === $this->post_type ) { + $query_params['parent'] = array( + 'description' => __( 'Limit result set to items with particular parent IDs.' ), + 'type' => 'array', + 'items' => array( + 'type' => 'integer', + ), + 'default' => array(), + ); + $query_params['parent_exclude'] = array( + 'description' => __( 'Limit result set to all items except those of a particular parent ID.' ), + 'type' => 'array', + 'items' => array( + 'type' => 'integer', + ), + 'default' => array(), + ); + } + + $query_params['search_columns'] = array( + 'default' => array(), + 'description' => __( 'Array of column names to be searched.' ), + 'type' => 'array', + 'items' => array( + 'enum' => array( 'post_title', 'post_content', 'post_excerpt' ), + 'type' => 'string', + ), + ); + + $query_params['slug'] = array( + 'description' => __( 'Limit result set to posts with one or more specific slugs.' ), + 'type' => 'array', + 'items' => array( + 'type' => 'string', + ), + ); + + $query_params['status'] = array( + 'default' => 'publish', + 'description' => __( 'Limit result set to posts assigned one or more statuses.' ), + 'type' => 'array', + 'items' => array( + 'enum' => array_merge( array_keys( get_post_stati() ), array( 'any' ) ), + 'type' => 'string', + ), + 'sanitize_callback' => array( $this, 'sanitize_post_statuses' ), + ); + + $query_params = $this->prepare_taxonomy_limit_schema( $query_params ); + + if ( 'post' === $this->post_type ) { + $query_params['sticky'] = array( + 'description' => __( 'Limit result set to items that are sticky.' ), + 'type' => 'boolean', + ); + } + + if ( post_type_supports( $this->post_type, 'post-formats' ) ) { + $query_params['format'] = array( + 'description' => __( 'Limit result set to items assigned one or more given formats.' ), + 'type' => 'array', + 'uniqueItems' => true, + 'items' => array( + 'enum' => array_values( get_post_format_slugs() ), + 'type' => 'string', + ), + ); + } + + /** + * Filters collection parameters for the posts controller. + * + * The dynamic part of the filter `$this->post_type` refers to the post + * type slug for the controller. + * + * This filter registers the collection parameter, but does not map the + * collection parameter to an internal WP_Query parameter. Use the + * `rest_{$this->post_type}_query` filter to set WP_Query parameters. + * + * @since 4.7.0 + * + * @param array $query_params JSON Schema-formatted collection parameters. + * @param WP_Post_Type $post_type Post type object. + */ + return apply_filters( "rest_{$this->post_type}_collection_params", $query_params, $post_type ); + } + + /** + * Prepares the 'tax_query' for a collection of posts. + * + * @since 5.7.0 + * + * @param array $args WP_Query arguments. + * @param WP_REST_Request $request Full details about the request. + * @return array Updated query arguments. + */ + private function prepare_tax_query( array $args, WP_REST_Request $request ) { + $relation = $request['tax_relation']; + + if ( $relation ) { + $args['tax_query'] = array( 'relation' => $relation ); + } + + $taxonomies = wp_list_filter( + get_object_taxonomies( $this->post_type, 'objects' ), + array( 'show_in_rest' => true ) + ); + + foreach ( $taxonomies as $taxonomy ) { + $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name; + + $tax_include = $request[ $base ]; + $tax_exclude = $request[ $base . '_exclude' ]; + + if ( $tax_include ) { + $terms = array(); + $include_children = false; + $operator = 'IN'; + + if ( rest_is_array( $tax_include ) ) { + $terms = $tax_include; + } elseif ( rest_is_object( $tax_include ) ) { + $terms = empty( $tax_include['terms'] ) ? array() : $tax_include['terms']; + $include_children = ! empty( $tax_include['include_children'] ); + + if ( isset( $tax_include['operator'] ) && 'AND' === $tax_include['operator'] ) { + $operator = 'AND'; + } + } + + if ( $terms ) { + $args['tax_query'][] = array( + 'taxonomy' => $taxonomy->name, + 'field' => 'term_id', + 'terms' => $terms, + 'include_children' => $include_children, + 'operator' => $operator, + ); + } + } + + if ( $tax_exclude ) { + $terms = array(); + $include_children = false; + + if ( rest_is_array( $tax_exclude ) ) { + $terms = $tax_exclude; + } elseif ( rest_is_object( $tax_exclude ) ) { + $terms = empty( $tax_exclude['terms'] ) ? array() : $tax_exclude['terms']; + $include_children = ! empty( $tax_exclude['include_children'] ); + } + + if ( $terms ) { + $args['tax_query'][] = array( + 'taxonomy' => $taxonomy->name, + 'field' => 'term_id', + 'terms' => $terms, + 'include_children' => $include_children, + 'operator' => 'NOT IN', + ); + } + } + } + + return $args; + } + + /** + * Prepares the collection schema for including and excluding items by terms. + * + * @since 5.7.0 + * + * @param array $query_params Collection schema. + * @return array Updated schema. + */ + private function prepare_taxonomy_limit_schema( array $query_params ) { + $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) ); + + if ( ! $taxonomies ) { + return $query_params; + } + + $query_params['tax_relation'] = array( + 'description' => __( 'Limit result set based on relationship between multiple taxonomies.' ), + 'type' => 'string', + 'enum' => array( 'AND', 'OR' ), + ); + + $limit_schema = array( + 'type' => array( 'object', 'array' ), + 'oneOf' => array( + array( + 'title' => __( 'Term ID List' ), + 'description' => __( 'Match terms with the listed IDs.' ), + 'type' => 'array', + 'items' => array( + 'type' => 'integer', + ), + ), + array( + 'title' => __( 'Term ID Taxonomy Query' ), + 'description' => __( 'Perform an advanced term query.' ), + 'type' => 'object', + 'properties' => array( + 'terms' => array( + 'description' => __( 'Term IDs.' ), + 'type' => 'array', + 'items' => array( + 'type' => 'integer', + ), + 'default' => array(), + ), + 'include_children' => array( + 'description' => __( 'Whether to include child terms in the terms limiting the result set.' ), + 'type' => 'boolean', + 'default' => false, + ), + ), + 'additionalProperties' => false, + ), + ), + ); + + $include_schema = array_merge( + array( + /* translators: %s: Taxonomy name. */ + 'description' => __( 'Limit result set to items with specific terms assigned in the %s taxonomy.' ), + ), + $limit_schema + ); + // 'operator' is supported only for 'include' queries. + $include_schema['oneOf'][1]['properties']['operator'] = array( + 'description' => __( 'Whether items must be assigned all or any of the specified terms.' ), + 'type' => 'string', + 'enum' => array( 'AND', 'OR' ), + 'default' => 'OR', + ); + + $exclude_schema = array_merge( + array( + /* translators: %s: Taxonomy name. */ + 'description' => __( 'Limit result set to items except those with specific terms assigned in the %s taxonomy.' ), + ), + $limit_schema + ); + + foreach ( $taxonomies as $taxonomy ) { + $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name; + $base_exclude = $base . '_exclude'; + + $query_params[ $base ] = $include_schema; + $query_params[ $base ]['description'] = sprintf( $query_params[ $base ]['description'], $base ); + + $query_params[ $base_exclude ] = $exclude_schema; + $query_params[ $base_exclude ]['description'] = sprintf( $query_params[ $base_exclude ]['description'], $base ); + + if ( ! $taxonomy->hierarchical ) { + unset( $query_params[ $base ]['oneOf'][1]['properties']['include_children'] ); + unset( $query_params[ $base_exclude ]['oneOf'][1]['properties']['include_children'] ); + } + } + + return $query_params; + } +} diff --git a/lib/compat/wordpress-6.7/post-formats.php b/lib/compat/wordpress-6.7/post-formats.php new file mode 100644 index 00000000000000..d3de5b83957e29 --- /dev/null +++ b/lib/compat/wordpress-6.7/post-formats.php @@ -0,0 +1,24 @@ +register( $name, $style_properties ) ) { - $result = false; - } + foreach ( $block_names as $name ) { + if ( ! WP_Block_Styles_Registry::get_instance()->register( $name, $style_properties ) ) { + $result = false; } - - return $result; } + + return $result; } diff --git a/lib/experimental/script-modules.php b/lib/experimental/script-modules.php index 5a14e1418ed6de..a113df02b9d758 100644 --- a/lib/experimental/script-modules.php +++ b/lib/experimental/script-modules.php @@ -200,3 +200,30 @@ function gutenberg_dequeue_module( $module_identifier ) { _deprecated_function( __FUNCTION__, 'Gutenberg 17.6.0', 'wp_dequeue_script_module' ); wp_script_modules()->dequeue( $module_identifier ); } + +/** + * Registers Gutenberg Script Modules. + * + * @since 19.3 + */ +function gutenberg_register_script_modules() { + // When in production, use the plugin's version as the default asset version; + // else (for development or test) default to use the current time. + $default_version = defined( 'GUTENBERG_VERSION' ) && ! SCRIPT_DEBUG ? GUTENBERG_VERSION : time(); + + wp_deregister_script_module( '@wordpress/a11y' ); + wp_register_script_module( + '@wordpress/a11y', + gutenberg_url( 'build-module/a11y/index.min.js' ), + array(), + $default_version + ); + add_filter( + 'script_module_data_@wordpress/a11y', + function ( $data ) { + $data['i18n'] = array( 'Notifications' => __( 'Notifications', 'default' ) ); + return $data; + } + ); +} +add_action( 'init', 'gutenberg_register_script_modules' ); diff --git a/lib/interactivity-api.php b/lib/interactivity-api.php index 6f04a3ba8fc927..90535f1ebaa42f 100644 --- a/lib/interactivity-api.php +++ b/lib/interactivity-api.php @@ -16,14 +16,14 @@ function gutenberg_reregister_interactivity_script_modules() { wp_register_script_module( '@wordpress/interactivity', - gutenberg_url( '/build/interactivity/' . ( SCRIPT_DEBUG ? 'debug.min.js' : 'index.min.js' ) ), + gutenberg_url( '/build-module/' . ( SCRIPT_DEBUG ? 'interactivity/debug.min.js' : 'interactivity/index.min.js' ) ), array(), $default_version ); wp_register_script_module( '@wordpress/interactivity-router', - gutenberg_url( '/build/interactivity/router.min.js' ), + gutenberg_url( '/build-module/interactivity-router/index.min.js' ), array( '@wordpress/interactivity' ), $default_version ); diff --git a/lib/load.php b/lib/load.php index 6ac2bd61f1de49..c26160eba2b67d 100644 --- a/lib/load.php +++ b/lib/load.php @@ -41,6 +41,7 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/compat/wordpress-6.6/rest-api.php'; // WordPress 6.7 compat. + require __DIR__ . '/compat/wordpress-6.7/class-gutenberg-rest-posts-controller-6-7.php'; require __DIR__ . '/compat/wordpress-6.7/class-gutenberg-rest-templates-controller-6-7.php'; require __DIR__ . '/compat/wordpress-6.7/class-gutenberg-rest-server.php'; require __DIR__ . '/compat/wordpress-6.7/rest-api.php'; @@ -109,6 +110,7 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/compat/wordpress-6.7/script-modules.php'; require __DIR__ . '/compat/wordpress-6.7/class-wp-block-templates-registry.php'; require __DIR__ . '/compat/wordpress-6.7/compat.php'; +require __DIR__ . '/compat/wordpress-6.7/post-formats.php'; // Experimental features. require __DIR__ . '/experimental/block-editor-settings-mobile.php'; diff --git a/lib/rest-api.php b/lib/rest-api.php index ea87f424637048..ac020e243ec056 100644 --- a/lib/rest-api.php +++ b/lib/rest-api.php @@ -11,68 +11,30 @@ } /** - * Registers the Global Styles REST API routes. + * Overrides the REST controller for the `wp_global_styles` post type. + * + * @param array $args Array of arguments for registering a post type. + * See the register_post_type() function for accepted arguments. + * @param string $post_type Post type key. + * + * @return array Array of arguments for registering a post type. */ -function gutenberg_register_global_styles_endpoints() { - $global_styles_controller = new WP_REST_Global_Styles_Controller_Gutenberg(); - $global_styles_controller->register_routes(); -} -add_action( 'rest_api_init', 'gutenberg_register_global_styles_endpoints' ); +function gutenberg_override_global_styles_endpoint( array $args ): array { + $args['rest_controller_class'] = 'WP_REST_Global_Styles_Controller_Gutenberg'; + $args['revisions_rest_controller_class'] = 'Gutenberg_REST_Global_Styles_Revisions_Controller_6_6'; + $args['late_route_registration'] = true; + $args['show_in_rest'] = true; + $args['rest_base'] = 'global-styles'; -if ( ! function_exists( 'gutenberg_register_edit_site_export_controller_endpoints' ) ) { - /** - * Registers the Edit Site Export REST API routes. - */ - function gutenberg_register_edit_site_export_controller_endpoints() { - $edit_site_export_controller = new WP_REST_Edit_Site_Export_Controller_Gutenberg(); - $edit_site_export_controller->register_routes(); - } + return $args; } +add_filter( 'register_wp_global_styles_post_type_args', 'gutenberg_override_global_styles_endpoint', 10, 2 ); -add_action( 'rest_api_init', 'gutenberg_register_edit_site_export_controller_endpoints' ); - -if ( ! function_exists( 'gutenberg_register_wp_rest_post_types_controller_fields' ) ) { - /** - * Adds `template` and `template_lock` fields to WP_REST_Post_Types_Controller class. - */ - function gutenberg_register_wp_rest_post_types_controller_fields() { - register_rest_field( - 'type', - 'template', - array( - 'get_callback' => function ( $item ) { - $post_type = get_post_type_object( $item['slug'] ); - if ( ! empty( $post_type ) ) { - return $post_type->template ?? array(); - } - }, - 'schema' => array( - 'type' => 'array', - 'description' => __( 'The block template associated with the post type.', 'gutenberg' ), - 'readonly' => true, - 'context' => array( 'view', 'edit', 'embed' ), - ), - ) - ); - register_rest_field( - 'type', - 'template_lock', - array( - 'get_callback' => function ( $item ) { - $post_type = get_post_type_object( $item['slug'] ); - if ( ! empty( $post_type ) ) { - return ! empty( $post_type->template_lock ) ? $post_type->template_lock : false; - } - }, - 'schema' => array( - 'type' => array( 'string', 'boolean' ), - 'enum' => array( 'all', 'insert', 'contentOnly', false ), - 'description' => __( 'The template_lock associated with the post type, or false if none.', 'gutenberg' ), - 'readonly' => true, - 'context' => array( 'view', 'edit', 'embed' ), - ), - ) - ); - } +/** + * Registers the Edit Site Export REST API routes. + */ +function gutenberg_register_edit_site_export_controller_endpoints() { + $edit_site_export_controller = new WP_REST_Edit_Site_Export_Controller_Gutenberg(); + $edit_site_export_controller->register_routes(); } -add_action( 'rest_api_init', 'gutenberg_register_wp_rest_post_types_controller_fields' ); +add_action( 'rest_api_init', 'gutenberg_register_edit_site_export_controller_endpoints' ); diff --git a/package-lock.json b/package-lock.json index 4ca6d143acd4be..0bf382361797d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gutenberg", - "version": "19.2.0-rc.1", + "version": "19.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "gutenberg", - "version": "19.2.0-rc.1", + "version": "19.2.0", "hasInstallScript": true, "license": "GPL-2.0-or-later", "dependencies": { @@ -47,6 +47,7 @@ "@wordpress/i18n": "file:packages/i18n", "@wordpress/icons": "file:packages/icons", "@wordpress/interactivity": "file:packages/interactivity", + "@wordpress/interactivity-router": "file:packages/interactivity-router", "@wordpress/interface": "file:packages/interface", "@wordpress/is-shallow-equal": "file:packages/is-shallow-equal", "@wordpress/keyboard-shortcuts": "file:packages/keyboard-shortcuts", @@ -244,9 +245,9 @@ "storybook-source-link": "2.0.9", "strip-json-comments": "5.0.0", "style-loader": "3.2.1", + "terser": "5.32.0", "terser-webpack-plugin": "5.3.9", "typescript": "5.5.3", - "uglify-js": "3.13.7", "uuid": "9.0.1", "webdriverio": "8.16.20", "webpack": "5.88.2", @@ -4098,6 +4099,92 @@ "node": ">=0.1.90" } }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.1.tgz", + "integrity": "sha512-lSquqZCHxDfuTg/Sk2hiS0mcSFCEBuj49JfzPHJogDBT0mGCyY5A1AQzBWngitrp7i1/HAZpIgzF/VjhOEIJIg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.1" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.1.tgz", + "integrity": "sha512-UBqaiu7kU0lfvaP982/o3khfXccVlHPWp0/vwwiIgDF0GmqqqxoiXC/6FCjlS9u92f7CoEz6nXKQnrn1kIAkOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/media-query-list-parser": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.1.tgz", + "integrity": "sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.1", + "@csstools/css-tokenizer": "^3.0.1" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-4.0.0.tgz", + "integrity": "sha512-189nelqtPd8++phaHNwYovKZI0FOzH1vQEE3QhHHkNIGrg5fSs9CbYP3RvfEH5geztnIA9Jwq91wyOIwAW5JIQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^6.1.0" + } + }, "node_modules/@dabh/diagnostics": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", @@ -4118,6 +4205,16 @@ "node": ">=10.0.0" } }, + "node_modules/@dual-bundle/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/@egjs/hammerjs": { "version": "2.0.17", "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", @@ -13363,6 +13460,34 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@stylistic/stylelint-plugin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.0.1.tgz", + "integrity": "sha512-j3mH8HSw2Rob/KJFWZ627w3CQ8gQqVHtzCdPeEffUg5vOgpz4rgrR+Xw2kU0OQCDcdW8Y1nKfdXKKjM5Rn8X0g==", + "dev": true, + "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.0", + "@csstools/css-tokenizer": "^3.0.0", + "@csstools/media-query-list-parser": "^3.0.0", + "is-plain-object": "^5.0.0", + "postcss-selector-parser": "^6.1.2", + "postcss-value-parser": "^4.2.0", + "style-search": "^0.1.0", + "stylelint": "^16.8.2" + }, + "engines": { + "node": "^18.12 || >=20.9" + }, + "peerDependencies": { + "stylelint": "^16.8.0" + } + }, + "node_modules/@stylistic/stylelint-plugin/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", @@ -21020,27 +21145,6 @@ "node": ">=0.10.0" } }, - "node_modules/clone-regexp": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz", - "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==", - "dev": true, - "dependencies": { - "is-regexp": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clone-regexp/node_modules/is-regexp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz", - "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/clone-response": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", @@ -22653,6 +22757,15 @@ "postcss": "^8.0.9" } }, + "node_modules/css-functions-list": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.2.tgz", + "integrity": "sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==", + "dev": true, + "engines": { + "node": ">=12 || >=16" + } + }, "node_modules/css-loader": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.2.0.tgz", @@ -26048,18 +26161,6 @@ "node": ">= 8" } }, - "node_modules/execall": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz", - "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==", - "dev": true, - "dependencies": { - "clone-regexp": "^2.1.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -26564,10 +26665,13 @@ } }, "node_modules/fastest-levenshtein": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", - "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", - "dev": true + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } }, "node_modules/fastq": { "version": "1.6.0", @@ -27058,9 +27162,9 @@ } }, "node_modules/flatted": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.0.tgz", - "integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, "node_modules/flow-enums-runtime": { @@ -28992,12 +29096,15 @@ } }, "node_modules/html-tags": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", - "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/html-webpack-plugin": { @@ -29457,15 +29564,6 @@ "node": ">=4" } }, - "node_modules/import-lazy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", - "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/import-local": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", @@ -32389,9 +32487,9 @@ } }, "node_modules/keyv": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", - "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "dependencies": { "json-buffer": "3.0.1" @@ -32441,9 +32539,9 @@ } }, "node_modules/known-css-properties": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.24.0.tgz", - "integrity": "sha512-RTSoaUAfLvpR357vWzAz/50Q/BmHfmE6ETSWfutT0AJiw10e6CmcdYRQJlLRd95B53D0Y2aD1jSxD3V3ySF+PA==", + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.34.0.tgz", + "integrity": "sha512-tBECoUqNFbyAY4RrbqsBQqDFpGXAEbdD5QKr8kACx3+rnArmuuR22nKQWKazvp07N9yjTyDZaw/20UIH8tL9DQ==", "dev": true }, "node_modules/kuler": { @@ -37967,12 +38065,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-selector": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/normalize-selector/-/normalize-selector-0.2.0.tgz", - "integrity": "sha1-0LFF62kRicY6eNIB3E/bEpPvDAM=", - "dev": true - }, "node_modules/normalize-url": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", @@ -40996,9 +41088,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -41821,31 +41913,67 @@ "dev": true }, "node_modules/postcss-resolve-nested-selector": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", - "integrity": "sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", + "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==", "dev": true }, "node_modules/postcss-safe-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", - "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.0.tgz", + "integrity": "sha512-ovehqRNVCpuFzbXoTb4qLtyzK3xn3t/CUBxOs8LsnQjQrShaB4lKiHoVqY8ANaC0hBMHq5QVWk77rwGklFUDrg==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "engines": { - "node": ">=12.0" + "node": ">=18.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-scss": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", + "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-scss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "engines": { + "node": ">=12.0" }, "peerDependencies": { - "postcss": "^8.3.3" + "postcss": "^8.4.29" } }, "node_modules/postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -47313,128 +47441,135 @@ } }, "node_modules/stylelint": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.2.0.tgz", - "integrity": "sha512-i0DrmDXFNpDsWiwx6SPRs4/pyw4kvZgqpDGvsTslQMY7hpUl6r33aQvNSn6cnTg2wtZ9rreFElI7XAKpOWi1vQ==", + "version": "16.8.2", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.8.2.tgz", + "integrity": "sha512-fInKATippQhcSm7AB+T32GpI+626yohrg33GkFT/5jzliUw5qhlwZq2UQQwgl3HsHrf09oeARi0ZwgY/UWEv9A==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.0", + "@csstools/css-tokenizer": "^3.0.0", + "@csstools/media-query-list-parser": "^3.0.0", + "@csstools/selector-specificity": "^4.0.0", + "@dual-bundle/import-meta-resolve": "^4.1.0", "balanced-match": "^2.0.0", - "colord": "^2.9.2", - "cosmiconfig": "^7.0.1", - "debug": "^4.3.3", - "execall": "^2.0.0", - "fast-glob": "^3.2.7", - "fastest-levenshtein": "^1.0.12", - "file-entry-cache": "^6.0.1", - "get-stdin": "^8.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^9.0.0", + "css-functions-list": "^3.2.2", + "css-tree": "^2.3.1", + "debug": "^4.3.6", + "fast-glob": "^3.3.2", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^9.0.0", "global-modules": "^2.0.0", - "globby": "^11.0.4", + "globby": "^11.1.0", "globjoin": "^0.1.4", - "html-tags": "^3.1.0", - "ignore": "^5.2.0", - "import-lazy": "^4.0.0", + "html-tags": "^3.3.1", + "ignore": "^5.3.2", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", - "known-css-properties": "^0.24.0", + "known-css-properties": "^0.34.0", "mathml-tag-names": "^2.1.3", - "meow": "^9.0.0", - "micromatch": "^4.0.4", + "meow": "^13.2.0", + "micromatch": "^4.0.7", "normalize-path": "^3.0.0", - "normalize-selector": "^0.2.0", - "picocolors": "^1.0.0", - "postcss": "^8.3.11", - "postcss-media-query-parser": "^0.2.3", - "postcss-resolve-nested-selector": "^0.1.1", - "postcss-safe-parser": "^6.0.0", - "postcss-selector-parser": "^6.0.7", - "postcss-value-parser": "^4.1.0", + "picocolors": "^1.0.1", + "postcss": "^8.4.41", + "postcss-resolve-nested-selector": "^0.1.6", + "postcss-safe-parser": "^7.0.0", + "postcss-selector-parser": "^6.1.2", + "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", - "specificity": "^0.4.1", "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "style-search": "^0.1.0", + "strip-ansi": "^7.1.0", + "supports-hyperlinks": "^3.0.0", "svg-tags": "^1.0.0", - "table": "^6.7.5", - "v8-compile-cache": "^2.3.0", - "write-file-atomic": "^3.0.3" + "table": "^6.8.2", + "write-file-atomic": "^5.0.1" }, "bin": { - "stylelint": "bin/stylelint.js" + "stylelint": "bin/stylelint.mjs" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/stylelint" + "node": ">=18.12.0" } }, - "node_modules/stylelint-config-recommended": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", - "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", - "dev": true, - "peerDependencies": { - "stylelint": "^14.0.0" - } + "node_modules/stylelint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, - "node_modules/stylelint-config-recommended-scss": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-5.0.2.tgz", - "integrity": "sha512-b14BSZjcwW0hqbzm9b0S/ScN2+3CO3O4vcMNOw2KGf8lfVSwJ4p5TbNEXKwKl1+0FMtgRXZj6DqVUe/7nGnuBg==", + "node_modules/stylelint/node_modules/balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true + }, + "node_modules/stylelint/node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, "dependencies": { - "postcss-scss": "^4.0.2", - "stylelint-config-recommended": "^6.0.0", - "stylelint-scss": "^4.0.0" + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" }, - "peerDependencies": { - "stylelint": "^14.0.0" - } - }, - "node_modules/stylelint-config-recommended-scss/node_modules/postcss-scss": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.3.tgz", - "integrity": "sha512-j4KxzWovfdHsyxwl1BxkUal/O4uirvHgdzMKS1aWJBAV0qh2qj5qAZqpeBfVUYGWv+4iK9Az7SPyZ4fyNju1uA==", - "dev": true, "engines": { - "node": ">=12.0" + "node": ">=14" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "url": "https://github.com/sponsors/d-fischer" }, "peerDependencies": { - "postcss": "^8.3.3" + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/stylelint-scss": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-4.1.0.tgz", - "integrity": "sha512-BNYTo7MMamhFOlcaAWp2dMpjg6hPyM/FFqfDIYzmYVLMmQJqc8lWRIiTqP4UX5bresj9Vo0dKC6odSh43VP2NA==", + "node_modules/stylelint/node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "dev": true, "dependencies": { - "lodash": "^4.17.21", - "postcss-media-query-parser": "^0.2.3", - "postcss-resolve-nested-selector": "^0.1.1", - "postcss-selector-parser": "^6.0.6", - "postcss-value-parser": "^4.1.0" + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" }, - "peerDependencies": { - "stylelint": "^14.0.0" + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, - "node_modules/stylelint-scss/node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true - }, - "node_modules/stylelint/node_modules/balanced-match": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", - "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", - "dev": true + "node_modules/stylelint/node_modules/debug": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } }, "node_modules/stylelint/node_modules/emoji-regex": { "version": "8.0.0", @@ -47442,16 +47577,75 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/stylelint/node_modules/get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "node_modules/stylelint/node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, "engines": { - "node": ">=10" + "node": ">=8.6.0" + } + }, + "node_modules/stylelint/node_modules/file-entry-cache": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.0.0.tgz", + "integrity": "sha512-6MgEugi8p2tiUhqO7GnPsmbCCzj0YRCwwaTbpGRyKZesjRSzkqkAE9fPp7V2yMs5hwfgbQLgdvSSkGNg1s5Uvw==", + "dev": true, + "dependencies": { + "flat-cache": "^5.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=18" + } + }, + "node_modules/stylelint/node_modules/flat-cache": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", + "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", + "dev": true, + "dependencies": { + "flatted": "^3.3.1", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/stylelint/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/stylelint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylelint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" } }, "node_modules/stylelint/node_modules/is-fullwidth-code-point": { @@ -47463,45 +47657,68 @@ "node": ">=8" } }, - "node_modules/stylelint/node_modules/meow": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", - "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "node_modules/stylelint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize": "^1.2.0", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" + "argparse": "^2.0.1" }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/stylelint/node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true + }, + "node_modules/stylelint/node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stylelint/node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "node_modules/stylelint/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/stylelint/node_modules/postcss": { + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" }, "engines": { - "node": ">=10" + "node": "^10 || ^12 || >=14" } }, "node_modules/stylelint/node_modules/postcss-value-parser": { @@ -47519,6 +47736,27 @@ "node": ">=8" } }, + "node_modules/stylelint/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/stylelint/node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/stylelint/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -47533,28 +47771,84 @@ "node": ">=8" } }, - "node_modules/stylelint/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "node_modules/stylelint/node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { - "node": ">=10" + "node": ">=8" + } + }, + "node_modules/stylelint/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/stylelint/node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/stylelint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylelint/node_modules/supports-hyperlinks": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.1.0.tgz", + "integrity": "sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/stylelint/node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "dependencies": { "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/stylis": { @@ -47747,9 +48041,9 @@ } }, "node_modules/table": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", - "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", + "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", "dev": true, "dependencies": { "ajv": "^8.0.1", @@ -48137,10 +48431,9 @@ } }, "node_modules/terser": { - "version": "5.31.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.2.tgz", - "integrity": "sha512-LGyRZVFm/QElZHy/CPr/O4eNZOZIzsrQ92y4v9UJe/pFJjypje2yI3C2FmPtvUEnhadlSbmG2nXtdcjHOjCfxw==", - "license": "BSD-2-Clause", + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.32.0.tgz", + "integrity": "sha512-v3Gtw3IzpBJ0ugkxEX8U0W6+TnPKRRCWGh1jC/iM/e3Ki5+qvO1L1EAZ56bZasc64aXHwRHNIQEzm6//i5cemQ==", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -48973,6 +49266,7 @@ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.7.tgz", "integrity": "sha512-1Psi2MmnZJbnEsgJJIlfnd7tFlJfitusmR7zDI8lXlFI0ACD4/Rm/xdrU8bh6zF0i74aiVoBtkRiFulkrmh3AA==", "dev": true, + "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" }, @@ -53539,6 +53833,7 @@ "@wordpress/editor": "file:../editor", "@wordpress/element": "file:../element", "@wordpress/escape-html": "file:../escape-html", + "@wordpress/fields": "file:../fields", "@wordpress/hooks": "file:../hooks", "@wordpress/html-entities": "file:../html-entities", "@wordpress/i18n": "file:../i18n", @@ -53639,6 +53934,7 @@ "@wordpress/deprecated": "file:../deprecated", "@wordpress/dom": "file:../dom", "@wordpress/element": "file:../element", + "@wordpress/fields": "file:../fields", "@wordpress/hooks": "file:../hooks", "@wordpress/html-entities": "file:../html-entities", "@wordpress/i18n": "file:../i18n", @@ -53900,7 +54196,27 @@ "version": "0.0.1", "license": "GPL-2.0-or-later", "dependencies": { - "@babel/runtime": "^7.16.0" + "@babel/runtime": "^7.16.0", + "@wordpress/blob": "file:../blob", + "@wordpress/blocks": "file:../blocks", + "@wordpress/components": "file:../components", + "@wordpress/compose": "file:../compose", + "@wordpress/core-data": "file:../core-data", + "@wordpress/data": "file:../data", + "@wordpress/dataviews": "file:../dataviews", + "@wordpress/element": "file:../element", + "@wordpress/hooks": "file:../hooks", + "@wordpress/html-entities": "file:../html-entities", + "@wordpress/i18n": "file:../i18n", + "@wordpress/icons": "file:../icons", + "@wordpress/notices": "file:../notices", + "@wordpress/patterns": "file:../patterns", + "@wordpress/primitives": "file:../primitives", + "@wordpress/private-apis": "file:../private-apis", + "@wordpress/url": "file:../url", + "@wordpress/warning": "file:../warning", + "change-case": "4.1.2", + "client-zip": "^2.4.5" }, "engines": { "node": ">=18.12.0", @@ -54832,7 +55148,7 @@ "sass-loader": "^12.1.0", "schema-utils": "^4.2.0", "source-map-loader": "^3.0.0", - "stylelint": "^14.2.0", + "stylelint": "^16.8.2", "terser-webpack-plugin": "^5.3.9", "url-loader": "^4.1.1", "webpack": "^5.88.2", @@ -55286,15 +55602,116 @@ "dev": true, "license": "MIT", "dependencies": { - "stylelint-config-recommended": "^6.0.0", - "stylelint-config-recommended-scss": "^5.0.2" + "@stylistic/stylelint-plugin": "^3.0.1", + "stylelint-config-recommended": "^14.0.1", + "stylelint-config-recommended-scss": "^14.1.0" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" }, "peerDependencies": { - "stylelint": "^14.2" + "stylelint": "^16.8.2" + } + }, + "packages/stylelint-config/node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "packages/stylelint-config/node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true + }, + "packages/stylelint-config/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "packages/stylelint-config/node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "packages/stylelint-config/node_modules/stylelint-config-recommended": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.1.tgz", + "integrity": "sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": "^16.1.0" + } + }, + "packages/stylelint-config/node_modules/stylelint-config-recommended-scss": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-14.1.0.tgz", + "integrity": "sha512-bhaMhh1u5dQqSsf6ri2GVWWQW5iUjBYgcHkh7SgDDn92ijoItC/cfO/W+fpXshgTQWhwFkP1rVcewcv4jaftRg==", + "dev": true, + "dependencies": { + "postcss-scss": "^4.0.9", + "stylelint-config-recommended": "^14.0.1", + "stylelint-scss": "^6.4.0" + }, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "postcss": "^8.3.3", + "stylelint": "^16.6.1" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + } + } + }, + "packages/stylelint-config/node_modules/stylelint-scss": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-6.5.1.tgz", + "integrity": "sha512-ZLqdqihm6uDYkrsOeD6YWb+stZI8Wn92kUNDhE4M+g9g1aCnRv0JlOrttFiAJJwaNzpdQgX3YJb5vDQXVuO9Ww==", + "dev": true, + "dependencies": { + "css-tree": "2.3.1", + "is-plain-object": "5.0.0", + "known-css-properties": "^0.34.0", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.4", + "postcss-selector-parser": "^6.1.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": "^16.0.2" } }, "packages/sync": { @@ -58170,6 +58587,30 @@ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true }, + "@csstools/css-parser-algorithms": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.1.tgz", + "integrity": "sha512-lSquqZCHxDfuTg/Sk2hiS0mcSFCEBuj49JfzPHJogDBT0mGCyY5A1AQzBWngitrp7i1/HAZpIgzF/VjhOEIJIg==", + "dev": true + }, + "@csstools/css-tokenizer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.1.tgz", + "integrity": "sha512-UBqaiu7kU0lfvaP982/o3khfXccVlHPWp0/vwwiIgDF0GmqqqxoiXC/6FCjlS9u92f7CoEz6nXKQnrn1kIAkOw==", + "dev": true + }, + "@csstools/media-query-list-parser": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.1.tgz", + "integrity": "sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==", + "dev": true + }, + "@csstools/selector-specificity": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-4.0.0.tgz", + "integrity": "sha512-189nelqtPd8++phaHNwYovKZI0FOzH1vQEE3QhHHkNIGrg5fSs9CbYP3RvfEH5geztnIA9Jwq91wyOIwAW5JIQ==", + "dev": true + }, "@dabh/diagnostics": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", @@ -58187,6 +58628,12 @@ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true }, + "@dual-bundle/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==", + "dev": true + }, "@egjs/hammerjs": { "version": "2.0.17", "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", @@ -64754,6 +65201,30 @@ "file-system-cache": "2.3.0" } }, + "@stylistic/stylelint-plugin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.0.1.tgz", + "integrity": "sha512-j3mH8HSw2Rob/KJFWZ627w3CQ8gQqVHtzCdPeEffUg5vOgpz4rgrR+Xw2kU0OQCDcdW8Y1nKfdXKKjM5Rn8X0g==", + "dev": true, + "requires": { + "@csstools/css-parser-algorithms": "^3.0.0", + "@csstools/css-tokenizer": "^3.0.0", + "@csstools/media-query-list-parser": "^3.0.0", + "is-plain-object": "^5.0.0", + "postcss-selector-parser": "^6.1.2", + "postcss-value-parser": "^4.2.0", + "style-search": "^0.1.0", + "stylelint": "^16.8.2" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + } + } + }, "@svgr/babel-plugin-add-jsx-attribute": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", @@ -68209,6 +68680,7 @@ "@wordpress/editor": "file:../editor", "@wordpress/element": "file:../element", "@wordpress/escape-html": "file:../escape-html", + "@wordpress/fields": "file:../fields", "@wordpress/hooks": "file:../hooks", "@wordpress/html-entities": "file:../html-entities", "@wordpress/i18n": "file:../i18n", @@ -68289,6 +68761,7 @@ "@wordpress/deprecated": "file:../deprecated", "@wordpress/dom": "file:../dom", "@wordpress/element": "file:../element", + "@wordpress/fields": "file:../fields", "@wordpress/hooks": "file:../hooks", "@wordpress/html-entities": "file:../html-entities", "@wordpress/i18n": "file:../i18n", @@ -68470,7 +68943,27 @@ "@wordpress/fields": { "version": "file:packages/fields", "requires": { - "@babel/runtime": "^7.16.0" + "@babel/runtime": "^7.16.0", + "@wordpress/blob": "file:../blob", + "@wordpress/blocks": "file:../blocks", + "@wordpress/components": "file:../components", + "@wordpress/compose": "file:../compose", + "@wordpress/core-data": "file:../core-data", + "@wordpress/data": "file:../data", + "@wordpress/dataviews": "file:../dataviews", + "@wordpress/element": "file:../element", + "@wordpress/hooks": "file:../hooks", + "@wordpress/html-entities": "file:../html-entities", + "@wordpress/i18n": "file:../i18n", + "@wordpress/icons": "file:../icons", + "@wordpress/notices": "file:../notices", + "@wordpress/patterns": "file:../patterns", + "@wordpress/primitives": "file:../primitives", + "@wordpress/private-apis": "file:../private-apis", + "@wordpress/url": "file:../url", + "@wordpress/warning": "file:../warning", + "change-case": "4.1.2", + "client-zip": "^2.4.5" } }, "@wordpress/format-library": { @@ -69016,7 +69509,7 @@ "sass-loader": "^12.1.0", "schema-utils": "^4.2.0", "source-map-loader": "^3.0.0", - "stylelint": "^14.2.0", + "stylelint": "^16.8.2", "terser-webpack-plugin": "^5.3.9", "url-loader": "^4.1.1", "webpack": "^5.88.2", @@ -69330,8 +69823,71 @@ "@wordpress/stylelint-config": { "version": "file:packages/stylelint-config", "requires": { - "stylelint-config-recommended": "^6.0.0", - "stylelint-config-recommended-scss": "^5.0.2" + "@stylistic/stylelint-plugin": "^3.0.1", + "stylelint-config-recommended": "^14.0.1", + "stylelint-config-recommended-scss": "^14.1.0" + }, + "dependencies": { + "css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "requires": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + } + }, + "mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true + }, + "stylelint-config-recommended": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.1.tgz", + "integrity": "sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg==", + "dev": true + }, + "stylelint-config-recommended-scss": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-14.1.0.tgz", + "integrity": "sha512-bhaMhh1u5dQqSsf6ri2GVWWQW5iUjBYgcHkh7SgDDn92ijoItC/cfO/W+fpXshgTQWhwFkP1rVcewcv4jaftRg==", + "dev": true, + "requires": { + "postcss-scss": "^4.0.9", + "stylelint-config-recommended": "^14.0.1", + "stylelint-scss": "^6.4.0" + } + }, + "stylelint-scss": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-6.5.1.tgz", + "integrity": "sha512-ZLqdqihm6uDYkrsOeD6YWb+stZI8Wn92kUNDhE4M+g9g1aCnRv0JlOrttFiAJJwaNzpdQgX3YJb5vDQXVuO9Ww==", + "dev": true, + "requires": { + "css-tree": "2.3.1", + "is-plain-object": "5.0.0", + "known-css-properties": "^0.34.0", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.4", + "postcss-selector-parser": "^6.1.1", + "postcss-value-parser": "^4.2.0" + } + } } }, "@wordpress/sync": { @@ -72327,23 +72883,6 @@ } } }, - "clone-regexp": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz", - "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==", - "dev": true, - "requires": { - "is-regexp": "^2.0.0" - }, - "dependencies": { - "is-regexp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz", - "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", - "dev": true - } - } - }, "clone-response": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", @@ -73620,6 +74159,12 @@ "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", "dev": true }, + "css-functions-list": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.2.tgz", + "integrity": "sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==", + "dev": true + }, "css-loader": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.2.0.tgz", @@ -76155,15 +76700,6 @@ } } }, - "execall": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz", - "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==", - "dev": true, - "requires": { - "clone-regexp": "^2.1.0" - } - }, "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -76576,9 +77112,9 @@ } }, "fastest-levenshtein": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", - "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "dev": true }, "fastq": { @@ -76967,9 +77503,9 @@ } }, "flatted": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.0.tgz", - "integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, "flow-enums-runtime": { @@ -78433,9 +78969,9 @@ } }, "html-tags": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", - "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", "dev": true }, "html-webpack-plugin": { @@ -78757,12 +79293,6 @@ } } }, - "import-lazy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", - "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", - "dev": true - }, "import-local": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", @@ -80916,9 +81446,9 @@ } }, "keyv": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", - "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "requires": { "json-buffer": "3.0.1" @@ -80956,9 +81486,9 @@ "dev": true }, "known-css-properties": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.24.0.tgz", - "integrity": "sha512-RTSoaUAfLvpR357vWzAz/50Q/BmHfmE6ETSWfutT0AJiw10e6CmcdYRQJlLRd95B53D0Y2aD1jSxD3V3ySF+PA==", + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.34.0.tgz", + "integrity": "sha512-tBECoUqNFbyAY4RrbqsBQqDFpGXAEbdD5QKr8kACx3+rnArmuuR22nKQWKazvp07N9yjTyDZaw/20UIH8tL9DQ==", "dev": true }, "kuler": { @@ -85266,12 +85796,6 @@ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true }, - "normalize-selector": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/normalize-selector/-/normalize-selector-0.2.0.tgz", - "integrity": "sha1-0LFF62kRicY6eNIB3E/bEpPvDAM=", - "dev": true - }, "normalize-url": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", @@ -87529,9 +88053,9 @@ "integrity": "sha512-SO+NP5argMoJVCWcYiOofPUeEWDIM47FNCBJtp6uJ8PpjtBcudYJTzCbCMit5dzmfSLCoijzEwIXOqPqD45xQg==" }, "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "picomatch": { "version": "2.3.1", @@ -88121,21 +88645,27 @@ } }, "postcss-resolve-nested-selector": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", - "integrity": "sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", + "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==", "dev": true }, "postcss-safe-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", - "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.0.tgz", + "integrity": "sha512-ovehqRNVCpuFzbXoTb4qLtyzK3xn3t/CUBxOs8LsnQjQrShaB4lKiHoVqY8ANaC0hBMHq5QVWk77rwGklFUDrg==", + "dev": true + }, + "postcss-scss": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", + "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", "dev": true }, "postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "requires": { "cssesc": "^3.0.0", @@ -92311,68 +92841,152 @@ } }, "stylelint": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.2.0.tgz", - "integrity": "sha512-i0DrmDXFNpDsWiwx6SPRs4/pyw4kvZgqpDGvsTslQMY7hpUl6r33aQvNSn6cnTg2wtZ9rreFElI7XAKpOWi1vQ==", + "version": "16.8.2", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.8.2.tgz", + "integrity": "sha512-fInKATippQhcSm7AB+T32GpI+626yohrg33GkFT/5jzliUw5qhlwZq2UQQwgl3HsHrf09oeARi0ZwgY/UWEv9A==", "dev": true, "requires": { + "@csstools/css-parser-algorithms": "^3.0.0", + "@csstools/css-tokenizer": "^3.0.0", + "@csstools/media-query-list-parser": "^3.0.0", + "@csstools/selector-specificity": "^4.0.0", + "@dual-bundle/import-meta-resolve": "^4.1.0", "balanced-match": "^2.0.0", - "colord": "^2.9.2", - "cosmiconfig": "^7.0.1", - "debug": "^4.3.3", - "execall": "^2.0.0", - "fast-glob": "^3.2.7", - "fastest-levenshtein": "^1.0.12", - "file-entry-cache": "^6.0.1", - "get-stdin": "^8.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^9.0.0", + "css-functions-list": "^3.2.2", + "css-tree": "^2.3.1", + "debug": "^4.3.6", + "fast-glob": "^3.3.2", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^9.0.0", "global-modules": "^2.0.0", - "globby": "^11.0.4", + "globby": "^11.1.0", "globjoin": "^0.1.4", - "html-tags": "^3.1.0", - "ignore": "^5.2.0", - "import-lazy": "^4.0.0", + "html-tags": "^3.3.1", + "ignore": "^5.3.2", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", - "known-css-properties": "^0.24.0", + "known-css-properties": "^0.34.0", "mathml-tag-names": "^2.1.3", - "meow": "^9.0.0", - "micromatch": "^4.0.4", + "meow": "^13.2.0", + "micromatch": "^4.0.7", "normalize-path": "^3.0.0", - "normalize-selector": "^0.2.0", - "picocolors": "^1.0.0", - "postcss": "^8.3.11", - "postcss-media-query-parser": "^0.2.3", - "postcss-resolve-nested-selector": "^0.1.1", - "postcss-safe-parser": "^6.0.0", - "postcss-selector-parser": "^6.0.7", - "postcss-value-parser": "^4.1.0", + "picocolors": "^1.0.1", + "postcss": "^8.4.41", + "postcss-resolve-nested-selector": "^0.1.6", + "postcss-safe-parser": "^7.0.0", + "postcss-selector-parser": "^6.1.2", + "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", - "specificity": "^0.4.1", "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "style-search": "^0.1.0", + "strip-ansi": "^7.1.0", + "supports-hyperlinks": "^3.0.0", "svg-tags": "^1.0.0", - "table": "^6.7.5", - "v8-compile-cache": "^2.3.0", - "write-file-atomic": "^3.0.3" + "table": "^6.8.2", + "write-file-atomic": "^5.0.1" }, "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "balanced-match": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", "dev": true }, + "cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "requires": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + } + }, + "css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "requires": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + } + }, + "debug": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "file-entry-cache": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.0.0.tgz", + "integrity": "sha512-6MgEugi8p2tiUhqO7GnPsmbCCzj0YRCwwaTbpGRyKZesjRSzkqkAE9fPp7V2yMs5hwfgbQLgdvSSkGNg1s5Uvw==", + "dev": true, + "requires": { + "flat-cache": "^5.0.0" + } + }, + "flat-cache": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", + "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", + "dev": true, + "requires": { + "flatted": "^3.3.1", + "keyv": "^4.5.4" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true }, "is-fullwidth-code-point": { @@ -92381,36 +92995,42 @@ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, - "meow": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", - "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize": "^1.2.0", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" + "argparse": "^2.0.1" } }, - "normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true + }, + "meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "postcss": { + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", "dev": true, "requires": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" } }, "postcss-value-parser": { @@ -92425,6 +93045,18 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + }, + "source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -92434,74 +93066,67 @@ "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } } }, - "type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + } + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-hyperlinks": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.1.0.tgz", + "integrity": "sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + } }, "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "requires": { "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" + "signal-exit": "^4.0.1" } } } }, - "stylelint-config-recommended": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", - "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", - "dev": true - }, - "stylelint-config-recommended-scss": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-5.0.2.tgz", - "integrity": "sha512-b14BSZjcwW0hqbzm9b0S/ScN2+3CO3O4vcMNOw2KGf8lfVSwJ4p5TbNEXKwKl1+0FMtgRXZj6DqVUe/7nGnuBg==", - "dev": true, - "requires": { - "postcss-scss": "^4.0.2", - "stylelint-config-recommended": "^6.0.0", - "stylelint-scss": "^4.0.0" - }, - "dependencies": { - "postcss-scss": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.3.tgz", - "integrity": "sha512-j4KxzWovfdHsyxwl1BxkUal/O4uirvHgdzMKS1aWJBAV0qh2qj5qAZqpeBfVUYGWv+4iK9Az7SPyZ4fyNju1uA==", - "dev": true - } - } - }, - "stylelint-scss": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-4.1.0.tgz", - "integrity": "sha512-BNYTo7MMamhFOlcaAWp2dMpjg6hPyM/FFqfDIYzmYVLMmQJqc8lWRIiTqP4UX5bresj9Vo0dKC6odSh43VP2NA==", - "dev": true, - "requires": { - "lodash": "^4.17.21", - "postcss-media-query-parser": "^0.2.3", - "postcss-resolve-nested-selector": "^0.1.1", - "postcss-selector-parser": "^6.0.6", - "postcss-value-parser": "^4.1.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true - } - } - }, "stylis": { "version": "4.0.13", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz", @@ -92646,9 +93271,9 @@ } }, "table": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", - "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", + "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", "dev": true, "requires": { "ajv": "^8.0.1", @@ -92950,9 +93575,9 @@ } }, "terser": { - "version": "5.31.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.2.tgz", - "integrity": "sha512-LGyRZVFm/QElZHy/CPr/O4eNZOZIzsrQ92y4v9UJe/pFJjypje2yI3C2FmPtvUEnhadlSbmG2nXtdcjHOjCfxw==", + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.32.0.tgz", + "integrity": "sha512-v3Gtw3IzpBJ0ugkxEX8U0W6+TnPKRRCWGh1jC/iM/e3Ki5+qvO1L1EAZ56bZasc64aXHwRHNIQEzm6//i5cemQ==", "requires": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -93574,7 +94199,8 @@ "version": "3.13.7", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.7.tgz", "integrity": "sha512-1Psi2MmnZJbnEsgJJIlfnd7tFlJfitusmR7zDI8lXlFI0ACD4/Rm/xdrU8bh6zF0i74aiVoBtkRiFulkrmh3AA==", - "dev": true + "dev": true, + "optional": true }, "unbox-primitive": { "version": "1.0.2", diff --git a/package.json b/package.json index 63d42613c31978..46d2b42f267abb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "19.2.0-rc.1", + "version": "19.2.0", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", @@ -59,6 +59,7 @@ "@wordpress/i18n": "file:packages/i18n", "@wordpress/icons": "file:packages/icons", "@wordpress/interactivity": "file:packages/interactivity", + "@wordpress/interactivity-router": "file:packages/interactivity-router", "@wordpress/interface": "file:packages/interface", "@wordpress/is-shallow-equal": "file:packages/is-shallow-equal", "@wordpress/keyboard-shortcuts": "file:packages/keyboard-shortcuts", @@ -256,9 +257,9 @@ "storybook-source-link": "2.0.9", "strip-json-comments": "5.0.0", "style-loader": "3.2.1", + "terser": "5.32.0", "terser-webpack-plugin": "5.3.9", "typescript": "5.5.3", - "uglify-js": "3.13.7", "uuid": "9.0.1", "webdriverio": "8.16.20", "webpack": "5.88.2", diff --git a/packages/a11y/README.md b/packages/a11y/README.md index 0f40d9edd010ed..755396d2bb8f09 100644 --- a/packages/a11y/README.md +++ b/packages/a11y/README.md @@ -39,7 +39,7 @@ speak( 'The message you want to send to the ARIA live region', 'assertive' ); _Parameters_ - _message_ `string`: The message to be announced by assistive technologies. -- _ariaLive_ `[string]`: The politeness level for aria-live; default: 'polite'. +- _ariaLive_ `['polite'|'assertive']`: The politeness level for aria-live; default: 'polite'. diff --git a/packages/a11y/package.json b/packages/a11y/package.json index 88123b3c6c7126..327d6b9ff07167 100644 --- a/packages/a11y/package.json +++ b/packages/a11y/package.json @@ -28,6 +28,7 @@ "module": "build-module/index.js", "react-native": "src/index", "types": "build-types", + "wpScriptModuleExports": "./build-module/module/index.js", "dependencies": { "@babel/runtime": "^7.16.0", "@wordpress/dom-ready": "file:../dom-ready", diff --git a/packages/a11y/src/index.js b/packages/a11y/src/index.js index 957c76500c4344..59e93da780bd81 100644 --- a/packages/a11y/src/index.js +++ b/packages/a11y/src/index.js @@ -2,87 +2,20 @@ * WordPress dependencies */ import domReady from '@wordpress/dom-ready'; +import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import addIntroText from './add-intro-text'; -import addContainer from './add-container'; -import clear from './clear'; -import filterMessage from './filter-message'; +import { makeSetupFunction } from './shared/index'; +export { speak } from './shared/index'; /** * Create the live regions. */ -export function setup() { - const introText = document.getElementById( 'a11y-speak-intro-text' ); - const containerAssertive = document.getElementById( - 'a11y-speak-assertive' - ); - const containerPolite = document.getElementById( 'a11y-speak-polite' ); - - if ( introText === null ) { - addIntroText(); - } - - if ( containerAssertive === null ) { - addContainer( 'assertive' ); - } - - if ( containerPolite === null ) { - addContainer( 'polite' ); - } -} +export const setup = makeSetupFunction( __( 'Notifications' ) ); /** * Run setup on domReady. */ domReady( setup ); - -/** - * Allows you to easily announce dynamic interface updates to screen readers using ARIA live regions. - * This module is inspired by the `speak` function in `wp-a11y.js`. - * - * @param {string} message The message to be announced by assistive technologies. - * @param {string} [ariaLive] The politeness level for aria-live; default: 'polite'. - * - * @example - * ```js - * import { speak } from '@wordpress/a11y'; - * - * // For polite messages that shouldn't interrupt what screen readers are currently announcing. - * speak( 'The message you want to send to the ARIA live region' ); - * - * // For assertive messages that should interrupt what screen readers are currently announcing. - * speak( 'The message you want to send to the ARIA live region', 'assertive' ); - * ``` - */ -export function speak( message, ariaLive ) { - /* - * Clear previous messages to allow repeated strings being read out and hide - * the explanatory text from assistive technologies. - */ - clear(); - - message = filterMessage( message ); - - const introText = document.getElementById( 'a11y-speak-intro-text' ); - const containerAssertive = document.getElementById( - 'a11y-speak-assertive' - ); - const containerPolite = document.getElementById( 'a11y-speak-polite' ); - - if ( containerAssertive && ariaLive === 'assertive' ) { - containerAssertive.textContent = message; - } else if ( containerPolite ) { - containerPolite.textContent = message; - } - - /* - * Make the explanatory text available to assistive technologies by removing - * the 'hidden' HTML attribute. - */ - if ( introText ) { - introText.removeAttribute( 'hidden' ); - } -} diff --git a/packages/a11y/src/index.native.js b/packages/a11y/src/index.native.js index f6f53b6343adb0..e17597a8b2747d 100644 --- a/packages/a11y/src/index.native.js +++ b/packages/a11y/src/index.native.js @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import filterMessage from './filter-message'; +import filterMessage from './shared/filter-message'; /** * Update the ARIA live notification area text node. diff --git a/packages/a11y/src/module/index.ts b/packages/a11y/src/module/index.ts new file mode 100644 index 00000000000000..a2c87f397f4875 --- /dev/null +++ b/packages/a11y/src/module/index.ts @@ -0,0 +1,25 @@ +/** + * Internal dependencies + */ +import { makeSetupFunction } from '../shared/index'; +export { speak } from '../shared/index'; + +// Without an i18n Script Module, "Notifications" (the only localized text used in this module) +// will be translated on the server and provided as script-module data. +let notificationsText = 'Notifications'; +try { + const textContent = document.getElementById( + 'wp-script-module-data-@wordpress/a11y' + )?.textContent; + if ( textContent ) { + const parsed = JSON.parse( textContent ); + notificationsText = parsed?.i18n?.Notifications ?? notificationsText; + } +} catch {} + +/** + * Create the live regions. + */ +export const setup = makeSetupFunction( notificationsText ); + +setup(); diff --git a/packages/a11y/src/add-container.js b/packages/a11y/src/shared/add-container.js similarity index 100% rename from packages/a11y/src/add-container.js rename to packages/a11y/src/shared/add-container.js diff --git a/packages/a11y/src/add-intro-text.js b/packages/a11y/src/shared/add-intro-text.ts similarity index 83% rename from packages/a11y/src/add-intro-text.js rename to packages/a11y/src/shared/add-intro-text.ts index 2bcf453ec44c8e..6bd97c887664d3 100644 --- a/packages/a11y/src/add-intro-text.js +++ b/packages/a11y/src/shared/add-intro-text.ts @@ -1,22 +1,18 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; - /** * Build the explanatory text to be placed before the aria live regions. * * This text is initially hidden from assistive technologies by using a `hidden` * HTML attribute which is then removed once a message fills the aria-live regions. * + * @param {string} introTextContent The translated intro text content. * @return {HTMLParagraphElement} The explanatory text HTML element. */ -export default function addIntroText() { +export default function addIntroText( introTextContent: string ) { const introText = document.createElement( 'p' ); introText.id = 'a11y-speak-intro-text'; introText.className = 'a11y-speak-intro-text'; - introText.textContent = __( 'Notifications' ); + introText.textContent = introTextContent; introText.setAttribute( 'style', diff --git a/packages/a11y/src/clear.js b/packages/a11y/src/shared/clear.js similarity index 100% rename from packages/a11y/src/clear.js rename to packages/a11y/src/shared/clear.js diff --git a/packages/a11y/src/filter-message.js b/packages/a11y/src/shared/filter-message.js similarity index 100% rename from packages/a11y/src/filter-message.js rename to packages/a11y/src/shared/filter-message.js diff --git a/packages/a11y/src/shared/index.js b/packages/a11y/src/shared/index.js new file mode 100644 index 00000000000000..a05f891f428561 --- /dev/null +++ b/packages/a11y/src/shared/index.js @@ -0,0 +1,81 @@ +/** + * Internal dependencies + */ +import addContainer from './add-container'; +import addIntroText from './add-intro-text'; +import clear from './clear'; +import filterMessage from './filter-message'; + +/** + * Create the live regions. + * @param {string} introTextContent The intro text content. + */ +export function makeSetupFunction( introTextContent ) { + return function setup() { + const introText = document.getElementById( 'a11y-speak-intro-text' ); + const containerAssertive = document.getElementById( + 'a11y-speak-assertive' + ); + const containerPolite = document.getElementById( 'a11y-speak-polite' ); + + if ( introText === null ) { + addIntroText( introTextContent ); + } + + if ( containerAssertive === null ) { + addContainer( 'assertive' ); + } + + if ( containerPolite === null ) { + addContainer( 'polite' ); + } + }; +} + +/** + * Allows you to easily announce dynamic interface updates to screen readers using ARIA live regions. + * This module is inspired by the `speak` function in `wp-a11y.js`. + * + * @param {string} message The message to be announced by assistive technologies. + * @param {'polite'|'assertive'} [ariaLive] The politeness level for aria-live; default: 'polite'. + * + * @example + * ```js + * import { speak } from '@wordpress/a11y'; + * + * // For polite messages that shouldn't interrupt what screen readers are currently announcing. + * speak( 'The message you want to send to the ARIA live region' ); + * + * // For assertive messages that should interrupt what screen readers are currently announcing. + * speak( 'The message you want to send to the ARIA live region', 'assertive' ); + * ``` + */ +export function speak( message, ariaLive ) { + /* + * Clear previous messages to allow repeated strings being read out and hide + * the explanatory text from assistive technologies. + */ + clear(); + + message = filterMessage( message ); + + const introText = document.getElementById( 'a11y-speak-intro-text' ); + const containerAssertive = document.getElementById( + 'a11y-speak-assertive' + ); + const containerPolite = document.getElementById( 'a11y-speak-polite' ); + + if ( containerAssertive && ariaLive === 'assertive' ) { + containerAssertive.textContent = message; + } else if ( containerPolite ) { + containerPolite.textContent = message; + } + + /* + * Make the explanatory text available to assistive technologies by removing + * the 'hidden' HTML attribute. + */ + if ( introText ) { + introText.removeAttribute( 'hidden' ); + } +} diff --git a/packages/a11y/src/test/add-container.test.js b/packages/a11y/src/shared/test/add-container.test.js similarity index 100% rename from packages/a11y/src/test/add-container.test.js rename to packages/a11y/src/shared/test/add-container.test.js diff --git a/packages/a11y/src/test/clear.test.js b/packages/a11y/src/shared/test/clear.test.js similarity index 100% rename from packages/a11y/src/test/clear.test.js rename to packages/a11y/src/shared/test/clear.test.js diff --git a/packages/a11y/src/test/filter-message.test.js b/packages/a11y/src/shared/test/filter-message.test.js similarity index 100% rename from packages/a11y/src/test/filter-message.test.js rename to packages/a11y/src/shared/test/filter-message.test.js diff --git a/packages/a11y/src/test/index.test.js b/packages/a11y/src/test/index.test.js index 4815baa2205047..0f6b9d0bd572ed 100644 --- a/packages/a11y/src/test/index.test.js +++ b/packages/a11y/src/test/index.test.js @@ -7,10 +7,10 @@ import domReady from '@wordpress/dom-ready'; * Internal dependencies */ import { setup, speak } from '../'; -import clear from '../clear'; -import filterMessage from '../filter-message'; +import clear from '../shared/clear'; +import filterMessage from '../shared/filter-message'; -jest.mock( '../clear', () => { +jest.mock( '../shared/clear', () => { return jest.fn(); } ); jest.mock( '@wordpress/dom-ready', () => { @@ -18,7 +18,7 @@ jest.mock( '@wordpress/dom-ready', () => { callback(); } ); } ); -jest.mock( '../filter-message', () => { +jest.mock( '../shared/filter-message', () => { return jest.fn( ( message ) => { return message; } ); diff --git a/packages/babel-preset-default/bin/index.js b/packages/babel-preset-default/bin/index.js index 54c35564d43d74..0e0e66b450c1d6 100755 --- a/packages/babel-preset-default/bin/index.js +++ b/packages/babel-preset-default/bin/index.js @@ -4,7 +4,7 @@ * External dependencies */ const builder = require( 'core-js-builder' ); -const { minify } = require( 'uglify-js' ); +const { minify } = require( 'terser' ); const { writeFile } = require( 'fs' ).promises; builder( { @@ -18,15 +18,15 @@ builder( { targets: require( '@wordpress/browserslist-config' ), filename: './build/polyfill.js', } ) - .then( async ( code ) => { - const output = minify( code, { + .then( ( code ) => + minify( code, { output: { comments: ( node, comment ) => - comment.value.indexOf( 'License' ) >= 0, + comment.value.toLowerCase().includes( 'license' ), }, - } ); - await writeFile( './build/polyfill.min.js', output.code ); - } ) + } ) + ) + .then( ( output ) => writeFile( './build/polyfill.min.js', output.code ) ) .catch( ( error ) => { // eslint-disable-next-line no-console console.log( error ); diff --git a/packages/base-styles/_animations.scss b/packages/base-styles/_animations.scss index ce1f935b7d4d5b..8706d185aa221b 100644 --- a/packages/base-styles/_animations.scss +++ b/packages/base-styles/_animations.scss @@ -1,5 +1,37 @@ -@mixin edit-post__fade-in-animation($speed: 0.08s, $delay: 0s) { - animation: edit-post__fade-in-animation $speed linear $delay; +@mixin keyframes($name) { + @keyframes #{$name} { + @content; + } +} + +@mixin animation__fade-in($speed: 0.08s, $delay: 0s) { + @include keyframes(__wp-base-styles-fade-in) { + from { + opacity: 0; + } + to { + opacity: 1; + } + } + + + animation: __wp-base-styles-fade-in $speed linear $delay; + animation-fill-mode: forwards; + @include reduce-motion("animation"); +} + +@mixin animation__fade-out($speed: 0.08s, $delay: 0s) { + @include keyframes(__wp-base-styles-fade-out) { + from { + opacity: 1; + } + to { + opacity: 0; + } + } + + + animation: __wp-base-styles-fade-out $speed linear $delay; animation-fill-mode: forwards; @include reduce-motion("animation"); } @@ -8,3 +40,9 @@ transition: all 0.5s cubic-bezier(0.65, 0, 0.45, 1); @include reduce-motion("transition"); } + +// Deprecated +@mixin edit-post__fade-in-animation($speed: 0.08s, $delay: 0s) { + @warn "The `edit-post__fade-in-animation` mixin is deprecated. Use `animation__fade-in` instead."; + @include animation__fade-in($speed, $delay); +} diff --git a/packages/base-styles/_mixins.scss b/packages/base-styles/_mixins.scss index ebccbe0e5e8aee..91017c8bb99320 100644 --- a/packages/base-styles/_mixins.scss +++ b/packages/base-styles/_mixins.scss @@ -512,7 +512,7 @@ @mixin gradient-colors-deprecated() { // Our classes uses the same values we set for gradient value attributes. - /* stylelint-disable function-comma-space-after -- We can not use spacing because of WP multi site kses rule. */ + /* stylelint-disable @stylistic/function-comma-space-after -- We can not use spacing because of WP multi site kses rule. */ .has-vivid-green-cyan-to-vivid-cyan-blue-gradient-background { background: linear-gradient(135deg,rgba(0,208,132,1) 0%,rgba(6,147,227,1) 100%); } @@ -540,7 +540,7 @@ .has-midnight-gradient-background { background: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%); } - /* stylelint-enable function-comma-space-after */ + /* stylelint-enable @stylistic/function-comma-space-after */ } @mixin custom-scrollbars-on-hover($handle-color, $handle-color-hover) { diff --git a/packages/block-editor/src/components/background-image-control/index.js b/packages/block-editor/src/components/background-image-control/index.js new file mode 100644 index 00000000000000..2703aa3988d64e --- /dev/null +++ b/packages/block-editor/src/components/background-image-control/index.js @@ -0,0 +1,741 @@ +/** + * External dependencies + */ +import clsx from 'clsx'; + +/** + * WordPress dependencies + */ +import { + ToggleControl, + __experimentalToggleGroupControl as ToggleGroupControl, + __experimentalToggleGroupControlOption as ToggleGroupControlOption, + __experimentalUnitControl as UnitControl, + __experimentalVStack as VStack, + DropZone, + FlexItem, + FocalPointPicker, + MenuItem, + VisuallyHidden, + __experimentalItemGroup as ItemGroup, + __experimentalHStack as HStack, + __experimentalTruncate as Truncate, + Dropdown, + Placeholder, + Spinner, + __experimentalDropdownContentWrapper as DropdownContentWrapper, +} from '@wordpress/components'; +import { __, _x, sprintf } from '@wordpress/i18n'; +import { store as noticesStore } from '@wordpress/notices'; +import { getFilename } from '@wordpress/url'; +import { useRef, useState, useEffect, useMemo } from '@wordpress/element'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { focus } from '@wordpress/dom'; +import { isBlobURL } from '@wordpress/blob'; + +/** + * Internal dependencies + */ +import { getResolvedValue } from '../global-styles/utils'; +import { hasBackgroundImageValue } from '../global-styles/background-panel'; +import { setImmutably } from '../../utils/object'; +import MediaReplaceFlow from '../media-replace-flow'; +import { store as blockEditorStore } from '../../store'; + +import { + globalStylesDataKey, + globalStylesLinksDataKey, +} from '../../store/private-keys'; + +const IMAGE_BACKGROUND_TYPE = 'image'; + +const BACKGROUND_POPOVER_PROPS = { + placement: 'left-start', + offset: 36, + shift: true, + className: 'block-editor-global-styles-background-panel__popover', +}; +const noop = () => {}; + +/** + * Get the help text for the background size control. + * + * @param {string} value backgroundSize value. + * @return {string} Translated help text. + */ +function backgroundSizeHelpText( value ) { + if ( value === 'cover' || value === undefined ) { + return __( 'Image covers the space evenly.' ); + } + if ( value === 'contain' ) { + return __( 'Image is contained without distortion.' ); + } + return __( 'Image has a fixed width.' ); +} + +/** + * Converts decimal x and y coords from FocalPointPicker to percentage-based values + * to use as backgroundPosition value. + * + * @param {{x?:number, y?:number}} value FocalPointPicker coords. + * @return {string} backgroundPosition value. + */ +export const coordsToBackgroundPosition = ( value ) => { + if ( ! value || ( isNaN( value.x ) && isNaN( value.y ) ) ) { + return undefined; + } + + const x = isNaN( value.x ) ? 0.5 : value.x; + const y = isNaN( value.y ) ? 0.5 : value.y; + + return `${ x * 100 }% ${ y * 100 }%`; +}; + +/** + * Converts backgroundPosition value to x and y coords for FocalPointPicker. + * + * @param {string} value backgroundPosition value. + * @return {{x?:number, y?:number}} FocalPointPicker coords. + */ +export const backgroundPositionToCoords = ( value ) => { + if ( ! value ) { + return { x: undefined, y: undefined }; + } + + let [ x, y ] = value.split( ' ' ).map( ( v ) => parseFloat( v ) / 100 ); + x = isNaN( x ) ? undefined : x; + y = isNaN( y ) ? x : y; + + return { x, y }; +}; + +function InspectorImagePreviewItem( { + as = 'span', + imgUrl, + toggleProps = {}, + filename, + label, + className, + onToggleCallback = noop, +} ) { + useEffect( () => { + if ( typeof toggleProps?.isOpen !== 'undefined' ) { + onToggleCallback( toggleProps?.isOpen ); + } + }, [ toggleProps?.isOpen, onToggleCallback ] ); + return ( + + + { imgUrl && ( + + + + ) } + + + { label } + + + { imgUrl + ? sprintf( + /* translators: %s: file name */ + __( 'Background image: %s' ), + filename || label + ) + : __( 'No background image selected' ) } + + + + + ); +} + +function BackgroundControlsPanel( { + label, + filename, + url: imgUrl, + children, + onToggle: onToggleCallback = noop, + hasImageValue, +} ) { + if ( ! hasImageValue ) { + return; + } + + const imgLabel = + label || getFilename( imgUrl ) || __( 'Add background image' ); + + return ( + { + const toggleProps = { + onClick: onToggle, + className: + 'block-editor-global-styles-background-panel__dropdown-toggle', + 'aria-expanded': isOpen, + 'aria-label': __( + 'Background size, position and repeat options.' + ), + isOpen, + }; + return ( + + ); + } } + renderContent={ () => ( + + { children } + + ) } + /> + ); +} + +function LoadingSpinner() { + return ( + + + + ); +} + +function BackgroundImageControls( { + onChange, + style, + inheritedValue, + onRemoveImage = noop, + onResetImage = noop, + displayInPanel, + defaultValues, +} ) { + const [ isUploading, setIsUploading ] = useState( false ); + const { getSettings } = useSelect( blockEditorStore ); + + const { id, title, url } = style?.background?.backgroundImage || { + ...inheritedValue?.background?.backgroundImage, + }; + const replaceContainerRef = useRef(); + const { createErrorNotice } = useDispatch( noticesStore ); + const onUploadError = ( message ) => { + createErrorNotice( message, { type: 'snackbar' } ); + setIsUploading( false ); + }; + + const resetBackgroundImage = () => + onChange( + setImmutably( + style, + [ 'background', 'backgroundImage' ], + undefined + ) + ); + + const onSelectMedia = ( media ) => { + if ( ! media || ! media.url ) { + resetBackgroundImage(); + setIsUploading( false ); + return; + } + + if ( isBlobURL( media.url ) ) { + setIsUploading( true ); + return; + } + + // For media selections originated from a file upload. + if ( + ( media.media_type && + media.media_type !== IMAGE_BACKGROUND_TYPE ) || + ( ! media.media_type && + media.type && + media.type !== IMAGE_BACKGROUND_TYPE ) + ) { + onUploadError( + __( 'Only images can be used as a background image.' ) + ); + return; + } + + const sizeValue = + style?.background?.backgroundSize || defaultValues?.backgroundSize; + const positionValue = style?.background?.backgroundPosition; + onChange( + setImmutably( style, [ 'background' ], { + ...style?.background, + backgroundImage: { + url: media.url, + id: media.id, + source: 'file', + title: media.title || undefined, + }, + backgroundPosition: + /* + * A background image uploaded and set in the editor receives a default background position of '50% 0', + * when the background image size is the equivalent of "Tile". + * This is to increase the chance that the image's focus point is visible. + * This is in-editor only to assist with the user experience. + */ + ! positionValue && ( 'auto' === sizeValue || ! sizeValue ) + ? '50% 0' + : positionValue, + backgroundSize: sizeValue, + } ) + ); + setIsUploading( false ); + }; + + // Drag and drop callback, restricting image to one. + const onFilesDrop = ( filesList ) => { + if ( filesList?.length > 1 ) { + onUploadError( + __( 'Only one image can be used as a background image.' ) + ); + return; + } + getSettings().mediaUpload( { + allowedTypes: [ IMAGE_BACKGROUND_TYPE ], + filesList, + onFileChange( [ image ] ) { + onSelectMedia( image ); + }, + onError: onUploadError, + } ); + }; + + const hasValue = hasBackgroundImageValue( style ); + + const closeAndFocus = () => { + const [ toggleButton ] = focus.tabbable.find( + replaceContainerRef.current + ); + // Focus the toggle button and close the dropdown menu. + // This ensures similar behaviour as to selecting an image, where the dropdown is + // closed and focus is redirected to the dropdown toggle button. + toggleButton?.focus(); + toggleButton?.click(); + }; + + const onRemove = () => + onChange( + setImmutably( style, [ 'background' ], { + backgroundImage: 'none', + } ) + ); + const canRemove = ! hasValue && hasBackgroundImageValue( inheritedValue ); + const imgLabel = + title || getFilename( url ) || __( 'Add background image' ); + + return ( +
+ { isUploading && } + + } + variant="secondary" + onError={ onUploadError } + onReset={ () => { + closeAndFocus(); + onResetImage(); + } } + > + { canRemove && ( + { + closeAndFocus(); + onRemove(); + onRemoveImage(); + } } + > + { __( 'Remove' ) } + + ) } + + +
+ ); +} + +function BackgroundSizeControls( { + onChange, + style, + inheritedValue, + defaultValues, +} ) { + const sizeValue = + style?.background?.backgroundSize || + inheritedValue?.background?.backgroundSize; + const repeatValue = + style?.background?.backgroundRepeat || + inheritedValue?.background?.backgroundRepeat; + const imageValue = + style?.background?.backgroundImage?.url || + inheritedValue?.background?.backgroundImage?.url; + const isUploadedImage = style?.background?.backgroundImage?.id; + const positionValue = + style?.background?.backgroundPosition || + inheritedValue?.background?.backgroundPosition; + const attachmentValue = + style?.background?.backgroundAttachment || + inheritedValue?.background?.backgroundAttachment; + + /* + * Set default values for uploaded images. + * The default values are passed by the consumer. + * Block-level controls may have different defaults to root-level controls. + * A falsy value is treated by default as `auto` (Tile). + */ + let currentValueForToggle = + ! sizeValue && isUploadedImage + ? defaultValues?.backgroundSize + : sizeValue || 'auto'; + /* + * The incoming value could be a value + unit, e.g. '20px'. + * In this case set the value to 'tile'. + */ + currentValueForToggle = ! [ 'cover', 'contain', 'auto' ].includes( + currentValueForToggle + ) + ? 'auto' + : currentValueForToggle; + /* + * If the current value is `cover` and the repeat value is `undefined`, then + * the toggle should be unchecked as the default state. Otherwise, the toggle + * should reflect the current repeat value. + */ + const repeatCheckedValue = ! ( + repeatValue === 'no-repeat' || + ( currentValueForToggle === 'cover' && repeatValue === undefined ) + ); + + const updateBackgroundSize = ( next ) => { + // When switching to 'contain' toggle the repeat off. + let nextRepeat = repeatValue; + let nextPosition = positionValue; + + if ( next === 'contain' ) { + nextRepeat = 'no-repeat'; + nextPosition = undefined; + } + + if ( next === 'cover' ) { + nextRepeat = undefined; + nextPosition = undefined; + } + + if ( + ( currentValueForToggle === 'cover' || + currentValueForToggle === 'contain' ) && + next === 'auto' + ) { + nextRepeat = undefined; + /* + * A background image uploaded and set in the editor (an image with a record id), + * receives a default background position of '50% 0', + * when the toggle switches to "Tile". This is to increase the chance that + * the image's focus point is visible. + * This is in-editor only to assist with the user experience. + */ + if ( !! style?.background?.backgroundImage?.id ) { + nextPosition = '50% 0'; + } + } + + /* + * Next will be null when the input is cleared, + * in which case the value should be 'auto'. + */ + if ( ! next && currentValueForToggle === 'auto' ) { + next = 'auto'; + } + + onChange( + setImmutably( style, [ 'background' ], { + ...style?.background, + backgroundPosition: nextPosition, + backgroundRepeat: nextRepeat, + backgroundSize: next, + } ) + ); + }; + + const updateBackgroundPosition = ( next ) => { + onChange( + setImmutably( + style, + [ 'background', 'backgroundPosition' ], + coordsToBackgroundPosition( next ) + ) + ); + }; + + const toggleIsRepeated = () => + onChange( + setImmutably( + style, + [ 'background', 'backgroundRepeat' ], + repeatCheckedValue === true ? 'no-repeat' : 'repeat' + ) + ); + + const toggleScrollWithPage = () => + onChange( + setImmutably( + style, + [ 'background', 'backgroundAttachment' ], + attachmentValue === 'fixed' ? 'scroll' : 'fixed' + ) + ); + + // Set a default background position for non-site-wide, uploaded images with a size of 'contain'. + const backgroundPositionValue = + ! positionValue && isUploadedImage && 'contain' === sizeValue + ? defaultValues?.backgroundPosition + : positionValue; + + return ( + + + + + + + + + + + + + + ); +} + +export default function BackgroundImagePanel( { + value, + onChange, + inheritedValue = value, + settings, + defaultValues = {}, +} ) { + /* + * Resolve any inherited "ref" pointers. + * Should the block editor need resolved, inherited values + * across all controls, this could be abstracted into a hook, + * e.g., useResolveGlobalStyle + */ + const { globalStyles, _links } = useSelect( ( select ) => { + const { getSettings } = select( blockEditorStore ); + const _settings = getSettings(); + return { + globalStyles: _settings[ globalStylesDataKey ], + _links: _settings[ globalStylesLinksDataKey ], + }; + }, [] ); + const resolvedInheritedValue = useMemo( () => { + const resolvedValues = { + background: {}, + }; + + if ( ! inheritedValue?.background ) { + return inheritedValue; + } + + Object.entries( inheritedValue?.background ).forEach( + ( [ key, backgroundValue ] ) => { + resolvedValues.background[ key ] = getResolvedValue( + backgroundValue, + { + styles: globalStyles, + _links, + } + ); + } + ); + return resolvedValues; + }, [ globalStyles, _links, inheritedValue ] ); + + const resetBackground = () => + onChange( setImmutably( value, [ 'background' ], {} ) ); + + const { title, url } = value?.background?.backgroundImage || { + ...resolvedInheritedValue?.background?.backgroundImage, + }; + const hasImageValue = + hasBackgroundImageValue( value ) || + hasBackgroundImageValue( resolvedInheritedValue ); + + const imageValue = + value?.background?.backgroundImage || + inheritedValue?.background?.backgroundImage; + + const shouldShowBackgroundImageControls = + hasImageValue && + 'none' !== imageValue && + ( settings?.background?.backgroundSize || + settings?.background?.backgroundPosition || + settings?.background?.backgroundRepeat ); + + const [ isDropDownOpen, setIsDropDownOpen ] = useState( false ); + + return ( +
+ { shouldShowBackgroundImageControls ? ( + + + { + setIsDropDownOpen( false ); + resetBackground(); + } } + onRemoveImage={ () => setIsDropDownOpen( false ) } + defaultValues={ defaultValues } + /> + + + + ) : ( + { + setIsDropDownOpen( false ); + resetBackground(); + } } + onRemoveImage={ () => setIsDropDownOpen( false ) } + /> + ) } +
+ ); +} diff --git a/packages/block-editor/src/components/background-image-control/style.scss b/packages/block-editor/src/components/background-image-control/style.scss new file mode 100644 index 00000000000000..cde8044c24c121 --- /dev/null +++ b/packages/block-editor/src/components/background-image-control/style.scss @@ -0,0 +1,170 @@ +.block-editor-global-styles-background-panel__inspector-media-replace-container { + border: $border-width solid $gray-300; + border-radius: $radius-small; + // Full width. ToolsPanel lays out children in a grid. + grid-column: 1 / -1; + + &.is-open { + background-color: $gray-100; + } + + .block-editor-global-styles-background-panel__image-tools-panel-item { + flex-grow: 1; + border: 0; + + .components-dropdown { + display: block; + } + } + + .block-editor-global-styles-background-panel__inspector-preview-inner { + height: 100%; + } + + .components-dropdown { + display: block; + height: 36px; + } +} + +.block-editor-global-styles-background-panel__image-tools-panel-item { + border: $border-width solid $gray-300; + + // Full width. ToolsPanel lays out children in a grid. + grid-column: 1 / -1; + + // Ensure the dropzone is positioned to the size of the item. + position: relative; + + // Since there is no option to skip rendering the drag'n'drop icon in drop + // zone, we hide it for now. + .components-drop-zone__content-icon { + display: none; + } + + .components-dropdown { + display: block; + height: 36px; + } + + button.components-button { + color: $gray-900; + width: 100%; + display: block; + + &:hover { + color: var(--wp-admin-theme-color); + } + + &:focus { + box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); + } + } + + .block-editor-global-styles-background-panel__loading { + height: 100%; + position: absolute; + z-index: 1; + width: 100%; + padding: 10px 0 0 0; + + svg { + margin: 0; + } + } +} + +.block-editor-global-styles-background-panel__image-preview-content, +.block-editor-global-styles-background-panel__dropdown-toggle { + height: 100%; + width: 100%; + padding-left: $grid-unit-15; +} + +.block-editor-global-styles-background-panel__dropdown-toggle { + cursor: pointer; + background: transparent; + border: none; +} + +.block-editor-global-styles-background-panel__inspector-media-replace-title { + word-break: break-all; + // The Button component is white-space: nowrap, and that won't work with line-clamp. + white-space: normal; + + // Without this, the ellipsis can sometimes be partially hidden by the Button padding. + text-align: start; + text-align-last: center; +} + +.block-editor-global-styles-background-panel__inspector-preview-inner { + .block-editor-global-styles-background-panel__inspector-image-indicator-wrapper { + width: 20px; + height: 20px; + min-width: auto; + } +} + +.block-editor-global-styles-background-panel__inspector-image-indicator { + background-size: cover; + border-radius: $radius-round; + width: 20px; + height: 20px; + display: block; + position: relative; +} + +.block-editor-global-styles-background-panel__inspector-image-indicator::after { + content: ""; + position: absolute; + top: -1px; + left: -1px; + bottom: -1px; + right: -1px; + border-radius: $radius-round; + box-shadow: inset 0 0 0 $border-width rgba(0, 0, 0, 0.2); + // Show a thin outline in Windows high contrast mode, otherwise the button is invisible. + border: 1px solid transparent; + box-sizing: inherit; +} + +.block-editor-global-styles-background-panel__dropdown-content-wrapper { + min-width: 260px; + overflow-x: hidden; + + .components-focal-point-picker-wrapper { + background-color: $gray-100; + width: 100%; + border-radius: $radius-small; + border: $border-width solid $gray-300; + } + + .components-focal-point-picker__media--image { + max-height: 180px; + } + + // Override focal picker to avoid a double border. + .components-focal-point-picker::after { + content: none; + } +} + +// Push control panel into the background when the media modal is open. +.modal-open .block-editor-global-styles-background-panel__popover { + z-index: z-index(".block-editor-global-styles-background-panel__popover"); +} + +.block-editor-global-styles-background-panel__media-replace-popover { + .components-popover__content { + // width of block-editor-global-styles-background-panel__dropdown-content-wrapper minus padding. + width: 226px; + } + + .components-button { + padding: 0 $grid-unit-10; + } + + .components-button .components-menu-items__item-icon.has-icon-right { + margin-left: $grid-unit-30 - $grid-unit-10; + } +} diff --git a/packages/block-editor/src/components/background-image-control/test/index.js b/packages/block-editor/src/components/background-image-control/test/index.js new file mode 100644 index 00000000000000..ebadad97eda022 --- /dev/null +++ b/packages/block-editor/src/components/background-image-control/test/index.js @@ -0,0 +1,47 @@ +/** + * Internal dependencies + */ + +import { backgroundPositionToCoords, coordsToBackgroundPosition } from '../'; + +describe( 'backgroundPositionToCoords', () => { + it( 'should return the correct coordinates for a percentage value using 2-value syntax', () => { + expect( backgroundPositionToCoords( '25% 75%' ) ).toEqual( { + x: 0.25, + y: 0.75, + } ); + } ); + + it( 'should return the correct coordinates for a percentage using 1-value syntax', () => { + expect( backgroundPositionToCoords( '50%' ) ).toEqual( { + x: 0.5, + y: 0.5, + } ); + } ); + + it( 'should return undefined coords in given an empty value', () => { + expect( backgroundPositionToCoords( '' ) ).toEqual( { + x: undefined, + y: undefined, + } ); + } ); + + it( 'should return undefined coords in given a string that cannot be converted', () => { + expect( backgroundPositionToCoords( 'apples' ) ).toEqual( { + x: undefined, + y: undefined, + } ); + } ); +} ); + +describe( 'coordsToBackgroundPosition', () => { + it( 'should return the correct background position for a set of coordinates', () => { + expect( coordsToBackgroundPosition( { x: 0.25, y: 0.75 } ) ).toBe( + '25% 75%' + ); + } ); + + it( 'should return undefined if no coordinates are provided', () => { + expect( coordsToBackgroundPosition( {} ) ).toBeUndefined(); + } ); +} ); diff --git a/packages/block-editor/src/components/block-inspector/style.scss b/packages/block-editor/src/components/block-inspector/style.scss index cf7131722722c1..bdbf3660d9619e 100644 --- a/packages/block-editor/src/components/block-inspector/style.scss +++ b/packages/block-editor/src/components/block-inspector/style.scss @@ -11,10 +11,8 @@ } .components-base-control { - margin-bottom: #{ $grid-unit-30 }; - - &:last-child { - margin-bottom: $grid-unit-10; + &:where(:not(:last-child)) { + margin-bottom: $grid-unit-20; } } diff --git a/packages/block-editor/src/components/block-list/use-block-props/use-zoom-out-mode-exit.js b/packages/block-editor/src/components/block-list/use-block-props/use-zoom-out-mode-exit.js index bb6edd066f06f1..d0001bd3b33c68 100644 --- a/packages/block-editor/src/components/block-list/use-block-props/use-zoom-out-mode-exit.js +++ b/packages/block-editor/src/components/block-list/use-block-props/use-zoom-out-mode-exit.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useDispatch } from '@wordpress/data'; +import { useSelect, useDispatch } from '@wordpress/data'; import { useRefEffect } from '@wordpress/compose'; /** @@ -16,6 +16,7 @@ import { unlock } from '../../../lock-unlock'; * @param {string} clientId Block client ID. */ export function useZoomOutModeExit( { editorMode } ) { + const { getSettings } = useSelect( blockEditorStore ); const { __unstableSetEditorMode } = unlock( useDispatch( blockEditorStore ) ); @@ -29,6 +30,14 @@ export function useZoomOutModeExit( { editorMode } ) { function onDoubleClick( event ) { if ( ! event.defaultPrevented ) { event.preventDefault(); + + const { __experimentalSetIsInserterOpened } = getSettings(); + + if ( + typeof __experimentalSetIsInserterOpened === 'function' + ) { + __experimentalSetIsInserterOpened( false ); + } __unstableSetEditorMode( 'edit' ); } } @@ -39,6 +48,6 @@ export function useZoomOutModeExit( { editorMode } ) { node.removeEventListener( 'dblclick', onDoubleClick ); }; }, - [ editorMode, __unstableSetEditorMode ] + [ editorMode, getSettings, __unstableSetEditorMode ] ); } diff --git a/packages/block-editor/src/components/block-switcher/style.scss b/packages/block-editor/src/components/block-switcher/style.scss index 823a656668a621..3dc2a7d591c926 100644 --- a/packages/block-editor/src/components/block-switcher/style.scss +++ b/packages/block-editor/src/components/block-switcher/style.scss @@ -55,30 +55,6 @@ } } -// Style this the same as the block buttons in the library. -// Needs specificity to override the icon button. -.block-editor-block-toolbar .components-toolbar-group .components-button.block-editor-block-switcher__no-switcher-icon.has-icon.has-icon, -.block-editor-block-toolbar .components-toolbar .components-button.block-editor-block-switcher__no-switcher-icon.has-icon.has-icon, -.block-editor-block-toolbar .components-toolbar-group .components-button.block-editor-block-switcher__toggle.has-icon.has-icon, -.block-editor-block-toolbar .components-toolbar .components-button.block-editor-block-switcher__toggle.has-icon.has-icon { - .block-editor-block-icon { - height: 100%; - position: relative; - margin: 0 auto; - display: flex; - align-items: center; - min-width: 100%; - } - - // Position the focus style correctly. - &::before { - top: $grid-unit-10; - right: $grid-unit-10; - bottom: $grid-unit-10; - left: $grid-unit-10; - } -} - .components-popover.block-editor-block-switcher__popover .components-popover__content { min-width: 300px; } diff --git a/packages/block-editor/src/components/block-tools/zoom-out-toolbar.js b/packages/block-editor/src/components/block-tools/zoom-out-toolbar.js index 0d3df9e20dfc54..a3c46c4b4c970a 100644 --- a/packages/block-editor/src/components/block-tools/zoom-out-toolbar.js +++ b/packages/block-editor/src/components/block-tools/zoom-out-toolbar.js @@ -31,7 +31,12 @@ export default function ZoomOutToolbar( { clientId, __unstableContentRef } ) { getPreviousBlockClientId, canRemoveBlock, canMoveBlock, + getSettings, } = select( blockEditorStore ); + + const { __experimentalSetIsInserterOpened: setIsInserterOpened } = + getSettings(); + const { getBlockType } = select( blocksStore ); const { name } = getBlock( clientId ); const blockType = getBlockType( name ); @@ -63,6 +68,7 @@ export default function ZoomOutToolbar( { clientId, __unstableContentRef } ) { isPrevBlockTemplatePart, canRemove: canRemoveBlock( clientId ), canMove: canMoveBlock( clientId ), + setIsInserterOpened, }; }, [ clientId ] @@ -75,6 +81,7 @@ export default function ZoomOutToolbar( { clientId, __unstableContentRef } ) { isPrevBlockTemplatePart, canRemove, canMove, + setIsInserterOpened, } = selected; const { removeBlock, __unstableSetEditorMode } = @@ -132,6 +139,10 @@ export default function ZoomOutToolbar( { clientId, __unstableContentRef } ) { icon={ edit } label={ __( 'Edit' ) } onClick={ () => { + // Setting may be undefined. + if ( typeof setIsInserterOpened === 'function' ) { + setIsInserterOpened( false ); + } __unstableSetEditorMode( 'edit' ); __unstableContentRef.current?.focus(); } } diff --git a/packages/block-editor/src/components/global-styles/background-panel.js b/packages/block-editor/src/components/global-styles/background-panel.js index 93fed5780e454d..c66ea01bce5498 100644 --- a/packages/block-editor/src/components/global-styles/background-panel.js +++ b/packages/block-editor/src/components/global-styles/background-panel.js @@ -1,71 +1,22 @@ -/** - * External dependencies - */ -import clsx from 'clsx'; - /** * WordPress dependencies */ import { __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, - ToggleControl, - __experimentalToggleGroupControl as ToggleGroupControl, - __experimentalToggleGroupControlOption as ToggleGroupControlOption, - __experimentalUnitControl as UnitControl, - __experimentalVStack as VStack, - DropZone, - FlexItem, - FocalPointPicker, - MenuItem, - VisuallyHidden, - __experimentalItemGroup as ItemGroup, - __experimentalHStack as HStack, - __experimentalTruncate as Truncate, - Dropdown, - Placeholder, - Spinner, - __experimentalDropdownContentWrapper as DropdownContentWrapper, } from '@wordpress/components'; -import { __, _x, sprintf } from '@wordpress/i18n'; -import { store as noticesStore } from '@wordpress/notices'; -import { getFilename } from '@wordpress/url'; -import { - useCallback, - Platform, - useRef, - useState, - useEffect, - useMemo, -} from '@wordpress/element'; -import { useDispatch, useSelect } from '@wordpress/data'; -import { focus } from '@wordpress/dom'; -import { isBlobURL } from '@wordpress/blob'; - +import { useCallback, Platform } from '@wordpress/element'; /** * Internal dependencies */ -import { useToolsPanelDropdownMenuProps, getResolvedValue } from './utils'; +import BackgroundImageControl from '../background-image-control'; +import { useToolsPanelDropdownMenuProps } from './utils'; import { setImmutably } from '../../utils/object'; -import MediaReplaceFlow from '../media-replace-flow'; -import { store as blockEditorStore } from '../../store'; - -import { - globalStylesDataKey, - globalStylesLinksDataKey, -} from '../../store/private-keys'; +import { __ } from '@wordpress/i18n'; -const IMAGE_BACKGROUND_TYPE = 'image'; const DEFAULT_CONTROLS = { backgroundImage: true, }; -const BACKGROUND_POPOVER_PROPS = { - placement: 'left-start', - offset: 36, - shift: true, - className: 'block-editor-global-styles-background-panel__popover', -}; -const noop = () => {}; /** * Checks site settings to see if the background panel may be used. @@ -110,567 +61,6 @@ export function hasBackgroundImageValue( style ) { ); } -/** - * Get the help text for the background size control. - * - * @param {string} value backgroundSize value. - * @return {string} Translated help text. - */ -function backgroundSizeHelpText( value ) { - if ( value === 'cover' || value === undefined ) { - return __( 'Image covers the space evenly.' ); - } - if ( value === 'contain' ) { - return __( 'Image is contained without distortion.' ); - } - return __( 'Image has a fixed width.' ); -} - -/** - * Converts decimal x and y coords from FocalPointPicker to percentage-based values - * to use as backgroundPosition value. - * - * @param {{x?:number, y?:number}} value FocalPointPicker coords. - * @return {string} backgroundPosition value. - */ -export const coordsToBackgroundPosition = ( value ) => { - if ( ! value || ( isNaN( value.x ) && isNaN( value.y ) ) ) { - return undefined; - } - - const x = isNaN( value.x ) ? 0.5 : value.x; - const y = isNaN( value.y ) ? 0.5 : value.y; - - return `${ x * 100 }% ${ y * 100 }%`; -}; - -/** - * Converts backgroundPosition value to x and y coords for FocalPointPicker. - * - * @param {string} value backgroundPosition value. - * @return {{x?:number, y?:number}} FocalPointPicker coords. - */ -export const backgroundPositionToCoords = ( value ) => { - if ( ! value ) { - return { x: undefined, y: undefined }; - } - - let [ x, y ] = value.split( ' ' ).map( ( v ) => parseFloat( v ) / 100 ); - x = isNaN( x ) ? undefined : x; - y = isNaN( y ) ? x : y; - - return { x, y }; -}; - -function InspectorImagePreviewItem( { - as = 'span', - imgUrl, - toggleProps = {}, - filename, - label, - className, - onToggleCallback = noop, -} ) { - useEffect( () => { - if ( typeof toggleProps?.isOpen !== 'undefined' ) { - onToggleCallback( toggleProps?.isOpen ); - } - }, [ toggleProps?.isOpen, onToggleCallback ] ); - return ( - - - { imgUrl && ( - - - - ) } - - - { label } - - - { imgUrl - ? sprintf( - /* translators: %s: file name */ - __( 'Background image: %s' ), - filename || label - ) - : __( 'No background image selected' ) } - - - - - ); -} - -function BackgroundControlsPanel( { - label, - filename, - url: imgUrl, - children, - onToggle: onToggleCallback = noop, - hasImageValue, -} ) { - if ( ! hasImageValue ) { - return; - } - - const imgLabel = - label || getFilename( imgUrl ) || __( 'Add background image' ); - - return ( - { - const toggleProps = { - onClick: onToggle, - className: - 'block-editor-global-styles-background-panel__dropdown-toggle', - 'aria-expanded': isOpen, - 'aria-label': __( - 'Background size, position and repeat options.' - ), - isOpen, - }; - return ( - - ); - } } - renderContent={ () => ( - - { children } - - ) } - /> - ); -} - -function LoadingSpinner() { - return ( - - - - ); -} - -function BackgroundImageControls( { - onChange, - style, - inheritedValue, - onRemoveImage = noop, - onResetImage = noop, - displayInPanel, - defaultValues, -} ) { - const [ isUploading, setIsUploading ] = useState( false ); - const { getSettings } = useSelect( blockEditorStore ); - - const { id, title, url } = style?.background?.backgroundImage || { - ...inheritedValue?.background?.backgroundImage, - }; - const replaceContainerRef = useRef(); - const { createErrorNotice } = useDispatch( noticesStore ); - const onUploadError = ( message ) => { - createErrorNotice( message, { type: 'snackbar' } ); - setIsUploading( false ); - }; - - const resetBackgroundImage = () => - onChange( - setImmutably( - style, - [ 'background', 'backgroundImage' ], - undefined - ) - ); - - const onSelectMedia = ( media ) => { - if ( ! media || ! media.url ) { - resetBackgroundImage(); - setIsUploading( false ); - return; - } - - if ( isBlobURL( media.url ) ) { - setIsUploading( true ); - return; - } - - // For media selections originated from a file upload. - if ( - ( media.media_type && - media.media_type !== IMAGE_BACKGROUND_TYPE ) || - ( ! media.media_type && - media.type && - media.type !== IMAGE_BACKGROUND_TYPE ) - ) { - onUploadError( - __( 'Only images can be used as a background image.' ) - ); - return; - } - - const sizeValue = - style?.background?.backgroundSize || defaultValues?.backgroundSize; - const positionValue = style?.background?.backgroundPosition; - onChange( - setImmutably( style, [ 'background' ], { - ...style?.background, - backgroundImage: { - url: media.url, - id: media.id, - source: 'file', - title: media.title || undefined, - }, - backgroundPosition: - /* - * A background image uploaded and set in the editor receives a default background position of '50% 0', - * when the background image size is the equivalent of "Tile". - * This is to increase the chance that the image's focus point is visible. - * This is in-editor only to assist with the user experience. - */ - ! positionValue && ( 'auto' === sizeValue || ! sizeValue ) - ? '50% 0' - : positionValue, - backgroundSize: sizeValue, - } ) - ); - setIsUploading( false ); - }; - - // Drag and drop callback, restricting image to one. - const onFilesDrop = ( filesList ) => { - if ( filesList?.length > 1 ) { - onUploadError( - __( 'Only one image can be used as a background image.' ) - ); - return; - } - getSettings().mediaUpload( { - allowedTypes: [ IMAGE_BACKGROUND_TYPE ], - filesList, - onFileChange( [ image ] ) { - onSelectMedia( image ); - }, - onError: onUploadError, - } ); - }; - - const hasValue = hasBackgroundImageValue( style ); - - const closeAndFocus = () => { - const [ toggleButton ] = focus.tabbable.find( - replaceContainerRef.current - ); - // Focus the toggle button and close the dropdown menu. - // This ensures similar behaviour as to selecting an image, where the dropdown is - // closed and focus is redirected to the dropdown toggle button. - toggleButton?.focus(); - toggleButton?.click(); - }; - - const onRemove = () => - onChange( - setImmutably( style, [ 'background' ], { - backgroundImage: 'none', - } ) - ); - const canRemove = ! hasValue && hasBackgroundImageValue( inheritedValue ); - const imgLabel = - title || getFilename( url ) || __( 'Add background image' ); - - return ( -
- { isUploading && } - - } - variant="secondary" - onError={ onUploadError } - onReset={ () => { - closeAndFocus(); - onResetImage(); - } } - > - { canRemove && ( - { - closeAndFocus(); - onRemove(); - onRemoveImage(); - } } - > - { __( 'Remove' ) } - - ) } - - -
- ); -} - -function BackgroundSizeControls( { - onChange, - style, - inheritedValue, - defaultValues, -} ) { - const sizeValue = - style?.background?.backgroundSize || - inheritedValue?.background?.backgroundSize; - const repeatValue = - style?.background?.backgroundRepeat || - inheritedValue?.background?.backgroundRepeat; - const imageValue = - style?.background?.backgroundImage?.url || - inheritedValue?.background?.backgroundImage?.url; - const isUploadedImage = style?.background?.backgroundImage?.id; - const positionValue = - style?.background?.backgroundPosition || - inheritedValue?.background?.backgroundPosition; - const attachmentValue = - style?.background?.backgroundAttachment || - inheritedValue?.background?.backgroundAttachment; - - /* - * Set default values for uploaded images. - * The default values are passed by the consumer. - * Block-level controls may have different defaults to root-level controls. - * A falsy value is treated by default as `auto` (Tile). - */ - let currentValueForToggle = - ! sizeValue && isUploadedImage - ? defaultValues?.backgroundSize - : sizeValue || 'auto'; - /* - * The incoming value could be a value + unit, e.g. '20px'. - * In this case set the value to 'tile'. - */ - currentValueForToggle = ! [ 'cover', 'contain', 'auto' ].includes( - currentValueForToggle - ) - ? 'auto' - : currentValueForToggle; - /* - * If the current value is `cover` and the repeat value is `undefined`, then - * the toggle should be unchecked as the default state. Otherwise, the toggle - * should reflect the current repeat value. - */ - const repeatCheckedValue = ! ( - repeatValue === 'no-repeat' || - ( currentValueForToggle === 'cover' && repeatValue === undefined ) - ); - - const updateBackgroundSize = ( next ) => { - // When switching to 'contain' toggle the repeat off. - let nextRepeat = repeatValue; - let nextPosition = positionValue; - - if ( next === 'contain' ) { - nextRepeat = 'no-repeat'; - nextPosition = undefined; - } - - if ( next === 'cover' ) { - nextRepeat = undefined; - nextPosition = undefined; - } - - if ( - ( currentValueForToggle === 'cover' || - currentValueForToggle === 'contain' ) && - next === 'auto' - ) { - nextRepeat = undefined; - /* - * A background image uploaded and set in the editor (an image with a record id), - * receives a default background position of '50% 0', - * when the toggle switches to "Tile". This is to increase the chance that - * the image's focus point is visible. - * This is in-editor only to assist with the user experience. - */ - if ( !! style?.background?.backgroundImage?.id ) { - nextPosition = '50% 0'; - } - } - - /* - * Next will be null when the input is cleared, - * in which case the value should be 'auto'. - */ - if ( ! next && currentValueForToggle === 'auto' ) { - next = 'auto'; - } - - onChange( - setImmutably( style, [ 'background' ], { - ...style?.background, - backgroundPosition: nextPosition, - backgroundRepeat: nextRepeat, - backgroundSize: next, - } ) - ); - }; - - const updateBackgroundPosition = ( next ) => { - onChange( - setImmutably( - style, - [ 'background', 'backgroundPosition' ], - coordsToBackgroundPosition( next ) - ) - ); - }; - - const toggleIsRepeated = () => - onChange( - setImmutably( - style, - [ 'background', 'backgroundRepeat' ], - repeatCheckedValue === true ? 'no-repeat' : 'repeat' - ) - ); - - const toggleScrollWithPage = () => - onChange( - setImmutably( - style, - [ 'background', 'backgroundAttachment' ], - attachmentValue === 'fixed' ? 'scroll' : 'fixed' - ) - ); - - // Set a default background position for non-site-wide, uploaded images with a size of 'contain'. - const backgroundPositionValue = - ! positionValue && isUploadedImage && 'contain' === sizeValue - ? defaultValues?.backgroundPosition - : positionValue; - - return ( - - - - - - - - - - - - - - ); -} - function BackgroundToolsPanel( { resetAllFilter, onChange, @@ -697,54 +87,20 @@ function BackgroundToolsPanel( { ); } -export default function BackgroundPanel( { +export default function BackgroundImagePanel( { as: Wrapper = BackgroundToolsPanel, value, onChange, - inheritedValue = value, + inheritedValue, settings, panelId, defaultControls = DEFAULT_CONTROLS, defaultValues = {}, headerLabel = __( 'Background image' ), } ) { - /* - * Resolve any inherited "ref" pointers. - * Should the block editor need resolved, inherited values - * across all controls, this could be abstracted into a hook, - * e.g., useResolveGlobalStyle - */ - const { globalStyles, _links } = useSelect( ( select ) => { - const { getSettings } = select( blockEditorStore ); - const _settings = getSettings(); - return { - globalStyles: _settings[ globalStylesDataKey ], - _links: _settings[ globalStylesLinksDataKey ], - }; - }, [] ); - const resolvedInheritedValue = useMemo( () => { - const resolvedValues = { - background: {}, - }; - - if ( ! inheritedValue?.background ) { - return inheritedValue; - } - - Object.entries( inheritedValue?.background ).forEach( - ( [ key, backgroundValue ] ) => { - resolvedValues.background[ key ] = getResolvedValue( - backgroundValue, - { - styles: globalStyles, - _links, - } - ); - } - ); - return resolvedValues; - }, [ globalStyles, _links, inheritedValue ] ); - + const showBackgroundImageControl = useHasBackgroundPanel( settings ); + const resetBackground = () => + onChange( setImmutably( value, [ 'background' ], {} ) ); const resetAllFilter = useCallback( ( previousValue ) => { return { ...previousValue, @@ -752,29 +108,6 @@ export default function BackgroundPanel( { }; }, [] ); - const resetBackground = () => - onChange( setImmutably( value, [ 'background' ], {} ) ); - - const { title, url } = value?.background?.backgroundImage || { - ...resolvedInheritedValue?.background?.backgroundImage, - }; - const hasImageValue = - hasBackgroundImageValue( value ) || - hasBackgroundImageValue( resolvedInheritedValue ); - - const imageValue = - value?.background?.backgroundImage || - inheritedValue?.background?.backgroundImage; - - const shouldShowBackgroundImageControls = - hasImageValue && - 'none' !== imageValue && - ( settings?.background?.backgroundSize || - settings?.background?.backgroundPosition || - settings?.background?.backgroundRepeat ); - - const [ isDropDownOpen, setIsDropDownOpen ] = useState( false ); - return ( -
+ { showBackgroundImageControl && ( !! value?.background } label={ __( 'Image' ) } @@ -798,53 +124,16 @@ export default function BackgroundPanel( { isShownByDefault={ defaultControls.backgroundImage } panelId={ panelId } > - { shouldShowBackgroundImageControls ? ( - - - { - setIsDropDownOpen( false ); - resetBackground(); - } } - onRemoveImage={ () => - setIsDropDownOpen( false ) - } - defaultValues={ defaultValues } - /> - - - - ) : ( - { - setIsDropDownOpen( false ); - resetBackground(); - } } - onRemoveImage={ () => setIsDropDownOpen( false ) } - /> - ) } + -
+ ) }
); } diff --git a/packages/block-editor/src/components/global-styles/dimensions-panel.js b/packages/block-editor/src/components/global-styles/dimensions-panel.js index 8430703aec966d..ce508a5ebc89e5 100644 --- a/packages/block-editor/src/components/global-styles/dimensions-panel.js +++ b/packages/block-editor/src/components/global-styles/dimensions-panel.js @@ -531,6 +531,7 @@ export default function DimensionsPanel( { > { ! showSpacingPresetsControl && ( { ! showSpacingPresetsControl && (
+ \ No newline at end of file diff --git a/test/e2e/specs/editor/various/__snapshots__/Inserting-blocks-firefox-webkit-inserts-p-59603-ragging-and-dropping-from-the-global-inserter-1-webkit.txt b/test/e2e/specs/editor/various/__snapshots__/Inserting-blocks-firefox-webkit-inserts-p-59603-ragging-and-dropping-from-the-global-inserter-1-webkit.txt index 45d99c7c27e6fe..753c30a0926707 100644 --- a/test/e2e/specs/editor/various/__snapshots__/Inserting-blocks-firefox-webkit-inserts-p-59603-ragging-and-dropping-from-the-global-inserter-1-webkit.txt +++ b/test/e2e/specs/editor/various/__snapshots__/Inserting-blocks-firefox-webkit-inserts-p-59603-ragging-and-dropping-from-the-global-inserter-1-webkit.txt @@ -2,10 +2,18 @@

Dummy text

- - - \ No newline at end of file + + + +
+ + + + + \ No newline at end of file diff --git a/test/e2e/specs/editor/various/adding-patterns.spec.js b/test/e2e/specs/editor/various/adding-patterns.spec.js index 2846c7adac271f..35192dd364dc66 100644 --- a/test/e2e/specs/editor/various/adding-patterns.spec.js +++ b/test/e2e/specs/editor/various/adding-patterns.spec.js @@ -14,15 +14,13 @@ test.describe( 'adding patterns', () => { await page.getByRole( 'tab', { name: 'Patterns' } ).click(); await page.fill( 'role=region[name="Block Library"i] >> role=searchbox[name="Search for blocks and patterns"i]', - 'Social links with a shared background color' + 'Standard' ); - await page.click( - 'role=option[name="Social links with a shared background color"i]' - ); + await page.getByRole( 'option', { name: 'Standard' } ).click(); await expect.poll( editor.getBlocks ).toMatchObject( [ { - name: 'core/social-links', + name: 'core/query', }, ] ); } ); diff --git a/test/e2e/specs/editor/various/inserting-blocks.spec.js b/test/e2e/specs/editor/various/inserting-blocks.spec.js index 1a443152800dec..b4f23c8b8e2bbf 100644 --- a/test/e2e/specs/editor/various/inserting-blocks.spec.js +++ b/test/e2e/specs/editor/various/inserting-blocks.spec.js @@ -194,7 +194,7 @@ test.describe( 'Inserting blocks (@firefox, @webkit)', () => { 'role=region[name="Editor top bar"i] >> role=button[name="Toggle block inserter"i]' ); - const PATTERN_NAME = 'Social links with a shared background color'; + const PATTERN_NAME = 'Standard'; await page.fill( 'role=region[name="Block Library"i] >> role=searchbox[name="Search for blocks and patterns"i]', @@ -350,7 +350,7 @@ test.describe( 'Inserting blocks (@firefox, @webkit)', () => { 'role=region[name="Editor top bar"i] >> role=button[name="Toggle block inserter"i]' ); - const PATTERN_NAME = 'Social links with a shared background color'; + const PATTERN_NAME = 'Standard'; await page.fill( 'role=region[name="Block Library"i] >> role=searchbox[name="Search for blocks and patterns"i]', diff --git a/test/e2e/specs/editor/various/post-editor-template-mode.spec.js b/test/e2e/specs/editor/various/post-editor-template-mode.spec.js index d46062eded07be..981f8ccbbd5d38 100644 --- a/test/e2e/specs/editor/various/post-editor-template-mode.spec.js +++ b/test/e2e/specs/editor/various/post-editor-template-mode.spec.js @@ -196,7 +196,7 @@ class PostEditorTemplateMode { ); const title = this.editorTopBar.getByRole( 'heading', { - name: 'Editing template: Single Entries', + name: 'Single Entries', } ); await expect( title ).toBeVisible(); diff --git a/test/e2e/specs/editor/various/publish-button.spec.js b/test/e2e/specs/editor/various/publish-button.spec.js index 631ddcd0fe61ba..cdc9c1a9936361 100644 --- a/test/e2e/specs/editor/various/publish-button.spec.js +++ b/test/e2e/specs/editor/various/publish-button.spec.js @@ -70,13 +70,12 @@ test.describe( 'Post publish button', () => { admin, page, requestUtils, + editor, } ) => { await requestUtils.activatePlugin( 'gutenberg-test-plugin-meta-box' ); await admin.createNewPost(); - await page - .getByRole( 'textbox', { - name: 'Add title', - } ) + await editor.canvas + .getByRole( 'textbox', { name: 'Add title' } ) .fill( 'Test post' ); const topBar = page.getByRole( 'region', { name: 'Editor top bar' } ); diff --git a/test/e2e/specs/site-editor/command-center.spec.js b/test/e2e/specs/site-editor/command-center.spec.js index fce951ca767bed..5b049cda252a8b 100644 --- a/test/e2e/specs/site-editor/command-center.spec.js +++ b/test/e2e/specs/site-editor/command-center.spec.js @@ -47,7 +47,7 @@ test.describe( 'Site editor command palette', () => { page .getByRole( 'region', { name: 'Editor top bar' } ) .getByRole( 'heading', { level: 1 } ) - ).toHaveText( 'Index' ); + ).toContainText( 'Index' ); } ); test( 'Open the command palette and navigate to Customize CSS', async ( { diff --git a/test/e2e/specs/site-editor/template-registration.spec.js b/test/e2e/specs/site-editor/template-registration.spec.js index ba9667358b3142..90e56645813c30 100644 --- a/test/e2e/specs/site-editor/template-registration.spec.js +++ b/test/e2e/specs/site-editor/template-registration.spec.js @@ -108,7 +108,7 @@ test.describe( 'Block template registration', () => { } ); // Swap template. - await page.getByRole( 'button', { name: 'Post' } ).click(); + await page.getByRole( 'button', { name: 'Post', exact: true } ).click(); await page.getByRole( 'button', { name: 'Template options' } ).click(); await page.getByRole( 'menuitem', { name: 'Swap template' } ).click(); await page.getByText( 'Plugin Template' ).click(); @@ -135,7 +135,7 @@ test.describe( 'Block template registration', () => { } ); // Swap template. - await page.getByRole( 'button', { name: 'Post' } ).click(); + await page.getByRole( 'button', { name: 'Post', exact: true } ).click(); await page.getByRole( 'button', { name: 'Template options' } ).click(); await page.getByRole( 'menuitem', { name: 'Swap template' } ).click(); await page.getByText( 'Custom', { exact: true } ).click(); diff --git a/test/e2e/specs/site-editor/title.spec.js b/test/e2e/specs/site-editor/title.spec.js index 3224c519f4f9e8..8f6c5252c9f41b 100644 --- a/test/e2e/specs/site-editor/title.spec.js +++ b/test/e2e/specs/site-editor/title.spec.js @@ -25,7 +25,7 @@ test.describe( 'Site editor title', () => { const title = page .getByRole( 'region', { name: 'Editor top bar' } ) .getByRole( 'heading', { - name: 'Editing template: Index', + name: 'Index', } ); await expect( title ).toBeVisible(); @@ -44,7 +44,7 @@ test.describe( 'Site editor title', () => { const title = page .getByRole( 'region', { name: 'Editor top bar' } ) .getByRole( 'heading', { - name: 'Editing template part: header', + name: 'header', } ); await expect( title ).toBeVisible(); diff --git a/test/e2e/specs/widgets/customizing-widgets.spec.js b/test/e2e/specs/widgets/customizing-widgets.spec.js index 34400ea00976e2..53b4da7be2d61f 100644 --- a/test/e2e/specs/widgets/customizing-widgets.spec.js +++ b/test/e2e/specs/widgets/customizing-widgets.spec.js @@ -358,9 +358,7 @@ test.describe( 'Widgets Customizer', () => { const legacyWidgetBlock = await widgetsCustomizerPage.addBlock( 'Legacy Widget' ); await page - .locator( - 'role=combobox[name="Select a legacy widget to display:"i]' - ) + .locator( 'role=combobox[name="Legacy widget"i]' ) .selectOption( 'test_widget' ); await expect( diff --git a/test/integration/fixtures/blocks/core__form-input.html b/test/integration/fixtures/blocks/core__form-input.html index 33f1fe88c2c6a1..81ff6cf53534e8 100644 --- a/test/integration/fixtures/blocks/core__form-input.html +++ b/test/integration/fixtures/blocks/core__form-input.html @@ -1,3 +1,20 @@
+ + +
+ + + +
+ + + +
+ + + +
+ + diff --git a/test/integration/fixtures/blocks/core__form-input.json b/test/integration/fixtures/blocks/core__form-input.json index fee4df284f1156..2d9865ef735562 100644 --- a/test/integration/fixtures/blocks/core__form-input.json +++ b/test/integration/fixtures/blocks/core__form-input.json @@ -11,5 +11,57 @@ "visibilityPermissions": "all" }, "innerBlocks": [] + }, + { + "name": "core/form-input", + "isValid": true, + "attributes": { + "type": "email", + "label": "Label", + "inlineLabel": false, + "required": false, + "value": "", + "visibilityPermissions": "all" + }, + "innerBlocks": [] + }, + { + "name": "core/form-input", + "isValid": true, + "attributes": { + "type": "number", + "label": "Label", + "inlineLabel": false, + "required": false, + "value": "", + "visibilityPermissions": "all" + }, + "innerBlocks": [] + }, + { + "name": "core/form-input", + "isValid": true, + "attributes": { + "type": "tel", + "label": "Label", + "inlineLabel": false, + "required": false, + "value": "", + "visibilityPermissions": "all" + }, + "innerBlocks": [] + }, + { + "name": "core/form-input", + "isValid": true, + "attributes": { + "type": "url", + "label": "Label", + "inlineLabel": false, + "required": false, + "value": "", + "visibilityPermissions": "all" + }, + "innerBlocks": [] } ] diff --git a/test/integration/fixtures/blocks/core__form-input.parsed.json b/test/integration/fixtures/blocks/core__form-input.parsed.json index 5470c653c403b5..439943df2841c8 100644 --- a/test/integration/fixtures/blocks/core__form-input.parsed.json +++ b/test/integration/fixtures/blocks/core__form-input.parsed.json @@ -7,5 +7,77 @@ "innerContent": [ "\n
\n" ] + }, + { + "blockName": null, + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n\n", + "innerContent": [ "\n\n" ] + }, + { + "blockName": "core/form-input", + "attrs": { + "type": "email" + }, + "innerBlocks": [], + "innerHTML": "\n
\n", + "innerContent": [ + "\n
\n" + ] + }, + { + "blockName": null, + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n\n", + "innerContent": [ "\n\n" ] + }, + { + "blockName": "core/form-input", + "attrs": { + "type": "number" + }, + "innerBlocks": [], + "innerHTML": "\n
\n", + "innerContent": [ + "\n
\n" + ] + }, + { + "blockName": null, + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n\n", + "innerContent": [ "\n\n" ] + }, + { + "blockName": "core/form-input", + "attrs": { + "type": "tel" + }, + "innerBlocks": [], + "innerHTML": "\n
\n", + "innerContent": [ + "\n
\n" + ] + }, + { + "blockName": null, + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n\n", + "innerContent": [ "\n\n" ] + }, + { + "blockName": "core/form-input", + "attrs": { + "type": "url" + }, + "innerBlocks": [], + "innerHTML": "\n
\n", + "innerContent": [ + "\n
\n" + ] } ] diff --git a/test/integration/fixtures/blocks/core__form-input.serialized.html b/test/integration/fixtures/blocks/core__form-input.serialized.html index 33f1fe88c2c6a1..d66cedcf9215b8 100644 --- a/test/integration/fixtures/blocks/core__form-input.serialized.html +++ b/test/integration/fixtures/blocks/core__form-input.serialized.html @@ -1,3 +1,19 @@
+ + +
+ + + +
+ + + +
+ + + +
+ diff --git a/test/integration/fixtures/blocks/core__form-input__checkbox.html b/test/integration/fixtures/blocks/core__form-input__checkbox.html new file mode 100644 index 00000000000000..fd08525a17eb33 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__checkbox.html @@ -0,0 +1,3 @@ + +
+ diff --git a/test/integration/fixtures/blocks/core__form-input__checkbox.json b/test/integration/fixtures/blocks/core__form-input__checkbox.json new file mode 100644 index 00000000000000..cc86ecd2a9080b --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__checkbox.json @@ -0,0 +1,15 @@ +[ + { + "name": "core/form-input", + "isValid": true, + "attributes": { + "type": "checkbox", + "label": "Label", + "inlineLabel": true, + "required": true, + "value": "", + "visibilityPermissions": "all" + }, + "innerBlocks": [] + } +] diff --git a/test/integration/fixtures/blocks/core__form-input__checkbox.parsed.json b/test/integration/fixtures/blocks/core__form-input__checkbox.parsed.json new file mode 100644 index 00000000000000..60b7b7dd37b091 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__checkbox.parsed.json @@ -0,0 +1,14 @@ +[ + { + "blockName": "core/form-input", + "attrs": { + "type": "checkbox", + "inlineLabel": true + }, + "innerBlocks": [], + "innerHTML": "\n
\n", + "innerContent": [ + "\n
\n" + ] + } +] diff --git a/test/integration/fixtures/blocks/core__form-input__checkbox.serialized.html b/test/integration/fixtures/blocks/core__form-input__checkbox.serialized.html new file mode 100644 index 00000000000000..fd08525a17eb33 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__checkbox.serialized.html @@ -0,0 +1,3 @@ + +
+ diff --git a/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v1.html b/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v1.html new file mode 100644 index 00000000000000..bd64e201e19185 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v1.html @@ -0,0 +1,7 @@ + + + + diff --git a/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v1.json b/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v1.json new file mode 100644 index 00000000000000..162304dc84b0c2 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v1.json @@ -0,0 +1,15 @@ +[ + { + "name": "core/form-input", + "isValid": true, + "attributes": { + "type": "checkbox", + "label": "Label", + "inlineLabel": false, + "required": false, + "value": "", + "visibilityPermissions": "all" + }, + "innerBlocks": [] + } +] diff --git a/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v1.parsed.json b/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v1.parsed.json new file mode 100644 index 00000000000000..2a2490e5c12607 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v1.parsed.json @@ -0,0 +1,13 @@ +[ + { + "blockName": "core/form-input", + "attrs": { + "type": "checkbox" + }, + "innerBlocks": [], + "innerHTML": "\n\n", + "innerContent": [ + "\n\n" + ] + } +] diff --git a/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v1.serialized.html b/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v1.serialized.html new file mode 100644 index 00000000000000..66705934019701 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v1.serialized.html @@ -0,0 +1,3 @@ + +
+ diff --git a/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v2.html b/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v2.html new file mode 100644 index 00000000000000..3f1de1c6028470 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v2.html @@ -0,0 +1,8 @@ + +
+ +
+ diff --git a/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v2.json b/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v2.json new file mode 100644 index 00000000000000..162304dc84b0c2 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v2.json @@ -0,0 +1,15 @@ +[ + { + "name": "core/form-input", + "isValid": true, + "attributes": { + "type": "checkbox", + "label": "Label", + "inlineLabel": false, + "required": false, + "value": "", + "visibilityPermissions": "all" + }, + "innerBlocks": [] + } +] diff --git a/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v2.parsed.json b/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v2.parsed.json new file mode 100644 index 00000000000000..dfa57c0755f685 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v2.parsed.json @@ -0,0 +1,13 @@ +[ + { + "blockName": "core/form-input", + "attrs": { + "type": "checkbox" + }, + "innerBlocks": [], + "innerHTML": "\n
\n\t\n
\n", + "innerContent": [ + "\n
\n\t\n
\n" + ] + } +] diff --git a/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v2.serialized.html b/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v2.serialized.html new file mode 100644 index 00000000000000..66705934019701 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__checkbox__deprecated-v2.serialized.html @@ -0,0 +1,3 @@ + +
+ diff --git a/test/integration/fixtures/blocks/core__form-input__deprecated-v1.html b/test/integration/fixtures/blocks/core__form-input__deprecated-v1.html index 08ea6618386200..5f043b0b2ef644 100644 --- a/test/integration/fixtures/blocks/core__form-input__deprecated-v1.html +++ b/test/integration/fixtures/blocks/core__form-input__deprecated-v1.html @@ -4,3 +4,33 @@ + + + + + + + + + + + + + + + + + + diff --git a/test/integration/fixtures/blocks/core__form-input__deprecated-v1.json b/test/integration/fixtures/blocks/core__form-input__deprecated-v1.json index fee4df284f1156..2d9865ef735562 100644 --- a/test/integration/fixtures/blocks/core__form-input__deprecated-v1.json +++ b/test/integration/fixtures/blocks/core__form-input__deprecated-v1.json @@ -11,5 +11,57 @@ "visibilityPermissions": "all" }, "innerBlocks": [] + }, + { + "name": "core/form-input", + "isValid": true, + "attributes": { + "type": "email", + "label": "Label", + "inlineLabel": false, + "required": false, + "value": "", + "visibilityPermissions": "all" + }, + "innerBlocks": [] + }, + { + "name": "core/form-input", + "isValid": true, + "attributes": { + "type": "number", + "label": "Label", + "inlineLabel": false, + "required": false, + "value": "", + "visibilityPermissions": "all" + }, + "innerBlocks": [] + }, + { + "name": "core/form-input", + "isValid": true, + "attributes": { + "type": "tel", + "label": "Label", + "inlineLabel": false, + "required": false, + "value": "", + "visibilityPermissions": "all" + }, + "innerBlocks": [] + }, + { + "name": "core/form-input", + "isValid": true, + "attributes": { + "type": "url", + "label": "Label", + "inlineLabel": false, + "required": false, + "value": "", + "visibilityPermissions": "all" + }, + "innerBlocks": [] } ] diff --git a/test/integration/fixtures/blocks/core__form-input__deprecated-v1.parsed.json b/test/integration/fixtures/blocks/core__form-input__deprecated-v1.parsed.json index 645337cbfdb4a1..963614974675d5 100644 --- a/test/integration/fixtures/blocks/core__form-input__deprecated-v1.parsed.json +++ b/test/integration/fixtures/blocks/core__form-input__deprecated-v1.parsed.json @@ -7,5 +7,77 @@ "innerContent": [ "\n\n" ] + }, + { + "blockName": null, + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n\n\n", + "innerContent": [ "\n\n\n" ] + }, + { + "blockName": "core/form-input", + "attrs": { + "type": "email" + }, + "innerBlocks": [], + "innerHTML": "\n\n", + "innerContent": [ + "\n\n" + ] + }, + { + "blockName": null, + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n\n", + "innerContent": [ "\n\n" ] + }, + { + "blockName": "core/form-input", + "attrs": { + "type": "number" + }, + "innerBlocks": [], + "innerHTML": "\n\n", + "innerContent": [ + "\n\n" + ] + }, + { + "blockName": null, + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n\n", + "innerContent": [ "\n\n" ] + }, + { + "blockName": "core/form-input", + "attrs": { + "type": "tel" + }, + "innerBlocks": [], + "innerHTML": "\n\n", + "innerContent": [ + "\n\n" + ] + }, + { + "blockName": null, + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n\n", + "innerContent": [ "\n\n" ] + }, + { + "blockName": "core/form-input", + "attrs": { + "type": "url" + }, + "innerBlocks": [], + "innerHTML": "\n\n", + "innerContent": [ + "\n\n" + ] } ] diff --git a/test/integration/fixtures/blocks/core__form-input__deprecated-v1.serialized.html b/test/integration/fixtures/blocks/core__form-input__deprecated-v1.serialized.html index 33f1fe88c2c6a1..d66cedcf9215b8 100644 --- a/test/integration/fixtures/blocks/core__form-input__deprecated-v1.serialized.html +++ b/test/integration/fixtures/blocks/core__form-input__deprecated-v1.serialized.html @@ -1,3 +1,19 @@
+ + +
+ + + +
+ + + +
+ + + +
+ diff --git a/test/integration/fixtures/blocks/core__form-input__radio.html b/test/integration/fixtures/blocks/core__form-input__radio.html new file mode 100644 index 00000000000000..1e6c76424334fb --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__radio.html @@ -0,0 +1,3 @@ + +
+ diff --git a/test/integration/fixtures/blocks/core__form-input__radio.json b/test/integration/fixtures/blocks/core__form-input__radio.json new file mode 100644 index 00000000000000..18cec11e3305b1 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__radio.json @@ -0,0 +1,15 @@ +[ + { + "name": "core/form-input", + "isValid": true, + "attributes": { + "type": "radio", + "label": "Label", + "inlineLabel": true, + "required": true, + "value": "", + "visibilityPermissions": "all" + }, + "innerBlocks": [] + } +] diff --git a/test/integration/fixtures/blocks/core__form-input__radio.parsed.json b/test/integration/fixtures/blocks/core__form-input__radio.parsed.json new file mode 100644 index 00000000000000..8dbb5b5a09942a --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__radio.parsed.json @@ -0,0 +1,14 @@ +[ + { + "blockName": "core/form-input", + "attrs": { + "type": "radio", + "inlineLabel": true + }, + "innerBlocks": [], + "innerHTML": "\n
\n", + "innerContent": [ + "\n
\n" + ] + } +] diff --git a/test/integration/fixtures/blocks/core__form-input__radio.serialized.html b/test/integration/fixtures/blocks/core__form-input__radio.serialized.html new file mode 100644 index 00000000000000..1e6c76424334fb --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__radio.serialized.html @@ -0,0 +1,3 @@ + +
+ diff --git a/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v1.html b/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v1.html new file mode 100644 index 00000000000000..8c41e02f10f04a --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v1.html @@ -0,0 +1,7 @@ + + + + diff --git a/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v1.json b/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v1.json new file mode 100644 index 00000000000000..113ce01f7ab7ef --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v1.json @@ -0,0 +1,15 @@ +[ + { + "name": "core/form-input", + "isValid": true, + "attributes": { + "type": "radio", + "label": "Label", + "inlineLabel": false, + "required": false, + "value": "", + "visibilityPermissions": "all" + }, + "innerBlocks": [] + } +] diff --git a/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v1.parsed.json b/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v1.parsed.json new file mode 100644 index 00000000000000..7433a6c7e9c508 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v1.parsed.json @@ -0,0 +1,13 @@ +[ + { + "blockName": "core/form-input", + "attrs": { + "type": "radio" + }, + "innerBlocks": [], + "innerHTML": "\n\n", + "innerContent": [ + "\n\n" + ] + } +] diff --git a/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v1.serialized.html b/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v1.serialized.html new file mode 100644 index 00000000000000..be1ae98e023107 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v1.serialized.html @@ -0,0 +1,3 @@ + +
+ diff --git a/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v2.html b/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v2.html new file mode 100644 index 00000000000000..a1c1b58f9b7fc5 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v2.html @@ -0,0 +1,8 @@ + +
+ +
+ diff --git a/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v2.json b/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v2.json new file mode 100644 index 00000000000000..113ce01f7ab7ef --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v2.json @@ -0,0 +1,15 @@ +[ + { + "name": "core/form-input", + "isValid": true, + "attributes": { + "type": "radio", + "label": "Label", + "inlineLabel": false, + "required": false, + "value": "", + "visibilityPermissions": "all" + }, + "innerBlocks": [] + } +] diff --git a/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v2.parsed.json b/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v2.parsed.json new file mode 100644 index 00000000000000..14c7828b1c890d --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v2.parsed.json @@ -0,0 +1,13 @@ +[ + { + "blockName": "core/form-input", + "attrs": { + "type": "radio" + }, + "innerBlocks": [], + "innerHTML": "\n
\n\t\n
\n", + "innerContent": [ + "\n
\n\t\n
\n" + ] + } +] diff --git a/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v2.serialized.html b/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v2.serialized.html new file mode 100644 index 00000000000000..be1ae98e023107 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__radio__deprecated-v2.serialized.html @@ -0,0 +1,3 @@ + +
+ diff --git a/test/integration/fixtures/blocks/core__form-input__textarea.html b/test/integration/fixtures/blocks/core__form-input__textarea.html new file mode 100644 index 00000000000000..b39e1c8b51166f --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__textarea.html @@ -0,0 +1,3 @@ + +
+ diff --git a/test/integration/fixtures/blocks/core__form-input__textarea.json b/test/integration/fixtures/blocks/core__form-input__textarea.json new file mode 100644 index 00000000000000..62ae6f76a0e9d2 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__textarea.json @@ -0,0 +1,15 @@ +[ + { + "name": "core/form-input", + "isValid": true, + "attributes": { + "type": "textarea", + "label": "Label", + "inlineLabel": false, + "required": true, + "value": "", + "visibilityPermissions": "all" + }, + "innerBlocks": [] + } +] diff --git a/test/integration/fixtures/blocks/core__form-input__textarea.parsed.json b/test/integration/fixtures/blocks/core__form-input__textarea.parsed.json new file mode 100644 index 00000000000000..3f76a2194f27e4 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__textarea.parsed.json @@ -0,0 +1,13 @@ +[ + { + "blockName": "core/form-input", + "attrs": { + "type": "textarea" + }, + "innerBlocks": [], + "innerHTML": "\n
\n", + "innerContent": [ + "\n
\n" + ] + } +] diff --git a/test/integration/fixtures/blocks/core__form-input__textarea.serialized.html b/test/integration/fixtures/blocks/core__form-input__textarea.serialized.html new file mode 100644 index 00000000000000..b39e1c8b51166f --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__textarea.serialized.html @@ -0,0 +1,3 @@ + +
+ diff --git a/test/integration/fixtures/blocks/core__form-input__textarea__deprecated-v1.html b/test/integration/fixtures/blocks/core__form-input__textarea__deprecated-v1.html new file mode 100644 index 00000000000000..8f7722f81ccc57 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__textarea__deprecated-v1.html @@ -0,0 +1,6 @@ + + + diff --git a/test/integration/fixtures/blocks/core__form-input__textarea__deprecated-v1.json b/test/integration/fixtures/blocks/core__form-input__textarea__deprecated-v1.json new file mode 100644 index 00000000000000..398106d8de1caf --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__textarea__deprecated-v1.json @@ -0,0 +1,15 @@ +[ + { + "name": "core/form-input", + "isValid": true, + "attributes": { + "type": "textarea", + "label": "Label", + "inlineLabel": false, + "required": false, + "value": "", + "visibilityPermissions": "all" + }, + "innerBlocks": [] + } +] diff --git a/test/integration/fixtures/blocks/core__form-input__textarea__deprecated-v1.parsed.json b/test/integration/fixtures/blocks/core__form-input__textarea__deprecated-v1.parsed.json new file mode 100644 index 00000000000000..e5e11f26d99606 --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__textarea__deprecated-v1.parsed.json @@ -0,0 +1,13 @@ +[ + { + "blockName": "core/form-input", + "attrs": { + "type": "textarea" + }, + "innerBlocks": [], + "innerHTML": "\n\n", + "innerContent": [ + "\n\n" + ] + } +] diff --git a/test/integration/fixtures/blocks/core__form-input__textarea__deprecated-v1.serialized.html b/test/integration/fixtures/blocks/core__form-input__textarea__deprecated-v1.serialized.html new file mode 100644 index 00000000000000..530403c4964fae --- /dev/null +++ b/test/integration/fixtures/blocks/core__form-input__textarea__deprecated-v1.serialized.html @@ -0,0 +1,3 @@ + +
+ diff --git a/test/integration/fixtures/blocks/core__query.json b/test/integration/fixtures/blocks/core__query.json index b050aaa2b5b1fd..a5ee4523128df8 100644 --- a/test/integration/fixtures/blocks/core__query.json +++ b/test/integration/fixtures/blocks/core__query.json @@ -16,7 +16,8 @@ "sticky": "", "inherit": true, "taxQuery": null, - "parents": [] + "parents": [], + "format": [] }, "tagName": "div", "enhancedPagination": false diff --git a/tools/webpack/blocks.js b/tools/webpack/blocks.js index fa0c6123dbcdba..a7866b61a6a1f4 100644 --- a/tools/webpack/blocks.js +++ b/tools/webpack/blocks.js @@ -2,7 +2,7 @@ * External dependencies */ const CopyWebpackPlugin = require( 'copy-webpack-plugin' ); -const { join, sep } = require( 'path' ); +const { join, sep, basename } = require( 'path' ); const fastGlob = require( 'fast-glob' ); const { realpathSync } = require( 'fs' ); @@ -135,7 +135,7 @@ module.exports = [ { from: `${ from }/**/*.php`, to( { absoluteFilename } ) { - const [ , dirname, basename ] = + const [ , dirname, fileBasename ] = absoluteFilename.match( new RegExp( `([\\w-]+)${ escapeRegExp( @@ -143,15 +143,18 @@ module.exports = [ ) }([\\w-]+)\\.php$` ) ); - - if ( basename === 'index' ) { + if ( fileBasename === 'index' ) { return join( to, `${ dirname }.php` ); } - return join( to, dirname, `${ basename }.php` ); + return join( + to, + dirname, + `${ fileBasename }.php` + ); }, filter: ( filepath ) => { return ( - filepath.endsWith( sep + 'index.php' ) || + basename( filepath ) === 'index.php' || PhpFilePathsPlugin.paths.includes( realpathSync( filepath ).replace( /\\/g, diff --git a/tools/webpack/interactivity.js b/tools/webpack/interactivity.js deleted file mode 100644 index 7f5c7f64a09d7f..00000000000000 --- a/tools/webpack/interactivity.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * External dependencies - */ -const { join } = require( 'path' ); - -/** - * WordPress dependencies - */ -const DependencyExtractionWebpackPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' ); - -/** - * Internal dependencies - */ -const { baseConfig, plugins } = require( './shared' ); - -module.exports = { - ...baseConfig, - name: 'interactivity', - entry: { - index: './packages/interactivity', - debug: './packages/interactivity/src/debug', - router: './packages/interactivity-router', - navigation: './packages/block-library/src/navigation/view.js', - query: './packages/block-library/src/query/view.js', - image: './packages/block-library/src/image/view.js', - file: './packages/block-library/src/file/view.js', - search: './packages/block-library/src/search/view.js', - }, - experiments: { - outputModule: true, - }, - output: { - devtoolNamespace: 'wp', - filename: './build/interactivity/[name].min.js', - library: { - type: 'module', - }, - path: join( __dirname, '..', '..' ), - environment: { module: true }, - module: true, - chunkFormat: 'module', - }, - resolve: { - extensions: [ '.js', '.ts', '.tsx' ], - }, - module: { - rules: [ - { - test: /\.(j|t)sx?$/, - exclude: /node_modules/, - use: [ - { - loader: require.resolve( 'babel-loader' ), - options: { - cacheDirectory: - process.env.BABEL_CACHE_DIRECTORY || true, - babelrc: false, - configFile: false, - presets: [ - '@babel/preset-typescript', - '@babel/preset-react', - ], - }, - }, - ], - }, - ], - }, - plugins: [ ...plugins, new DependencyExtractionWebpackPlugin() ], - watchOptions: { - ignored: [ '**/node_modules' ], - aggregateTimeout: 500, - }, -}; diff --git a/tools/webpack/script-modules.js b/tools/webpack/script-modules.js new file mode 100644 index 00000000000000..18287c96d83c8a --- /dev/null +++ b/tools/webpack/script-modules.js @@ -0,0 +1,110 @@ +/** + * External dependencies + */ +const { join } = require( 'path' ); + +/** + * WordPress dependencies + */ +const DependencyExtractionWebpackPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' ); + +/** + * Internal dependencies + */ +const { baseConfig, plugins } = require( './shared' ); + +const WORDPRESS_NAMESPACE = '@wordpress/'; +const { createRequire } = require( 'node:module' ); + +const rootURL = new URL( '..', `file://${ __dirname }` ); +const fromRootRequire = createRequire( rootURL ); + +/** @type {Iterable<[string, string]>} */ +const iterableDeps = Object.entries( + fromRootRequire( './package.json' ).dependencies +); + +/** @type {Map} */ +const gutenbergScriptModules = new Map(); +for ( const [ packageName, versionSpecifier ] of iterableDeps ) { + if ( + ! packageName.startsWith( WORDPRESS_NAMESPACE ) || + ! versionSpecifier.startsWith( 'file:' ) || + packageName.startsWith( WORDPRESS_NAMESPACE + 'react-native' ) + ) { + continue; + } + + const packageRequire = createRequire( + // Remove the leading "file:" specifier to build a package URL. + new URL( `${ versionSpecifier.substring( 5 ) }/`, rootURL ) + ); + + const depPackageJson = packageRequire( './package.json' ); + if ( ! Object.hasOwn( depPackageJson, 'wpScriptModuleExports' ) ) { + continue; + } + + const moduleName = packageName.substring( WORDPRESS_NAMESPACE.length ); + let { wpScriptModuleExports } = depPackageJson; + + // Special handling for { "wpScriptModuleExports": "./build-module/index.js" }. + if ( typeof wpScriptModuleExports === 'string' ) { + wpScriptModuleExports = { '.': wpScriptModuleExports }; + } + + if ( Object.getPrototypeOf( wpScriptModuleExports ) !== Object.prototype ) { + throw new Error( 'wpScriptModuleExports must be an object' ); + } + + for ( const [ exportName, exportPath ] of Object.entries( + wpScriptModuleExports + ) ) { + if ( typeof exportPath !== 'string' ) { + throw new Error( 'wpScriptModuleExports paths must be strings' ); + } + + if ( ! exportPath.startsWith( './' ) ) { + throw new Error( + 'wpScriptModuleExports paths must start with "./"' + ); + } + + const name = + exportName === '.' ? 'index' : exportName.replace( /^\.\/?/, '' ); + + gutenbergScriptModules.set( + `${ moduleName }/${ name }`, + packageRequire.resolve( exportPath ) + ); + } +} + +module.exports = { + ...baseConfig, + name: 'script-modules', + entry: Object.fromEntries( gutenbergScriptModules.entries() ), + experiments: { + outputModule: true, + }, + output: { + devtoolNamespace: 'wp', + filename: './build-module/[name].min.js', + library: { + type: 'module', + }, + path: join( __dirname, '..', '..' ), + environment: { module: true }, + module: true, + chunkFormat: 'module', + asyncChunks: false, + }, + resolve: { + extensions: [ '.js', '.ts', '.tsx' ], + }, + plugins: [ ...plugins, new DependencyExtractionWebpackPlugin() ], + watchOptions: { + ignored: [ '**/node_modules' ], + aggregateTimeout: 500, + }, +}; diff --git a/webpack.config.js b/webpack.config.js index 45b22cc5354dcf..51889b06d1eb44 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -3,13 +3,13 @@ */ const blocksConfig = require( './tools/webpack/blocks' ); const developmentConfigs = require( './tools/webpack/development' ); -const interactivity = require( './tools/webpack/interactivity' ); +const scriptModules = require( './tools/webpack/script-modules' ); const packagesConfig = require( './tools/webpack/packages' ); const vendorsConfig = require( './tools/webpack/vendors' ); module.exports = [ ...blocksConfig, - interactivity, + scriptModules, packagesConfig, ...developmentConfigs, ...vendorsConfig,