diff --git a/.eslintrc.js b/.eslintrc.js index c15afbcf02aa93..4385f528f44dbb 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -290,7 +290,10 @@ module.exports = { ...restrictedSyntax, ...restrictedSyntaxComponents, ...[ + 'CheckboxControl', + 'ComboboxControl', 'FocalPointPicker', + 'SearchControl', 'TextareaControl', 'TreeSelect', ].map( ( componentName ) => ( { diff --git a/.github/workflows/create-block.yml b/.github/workflows/create-block.yml index 0de1b9ee6566ae..245b136ee22c18 100644 --- a/.github/workflows/create-block.yml +++ b/.github/workflows/create-block.yml @@ -14,13 +14,17 @@ concurrency: jobs: checks: - name: Checks w/Node.js ${{ matrix.node }} on ${{ matrix.os }} + name: Checks w/Node.js ${{ matrix.node.name }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} strategy: fail-fast: false matrix: - node: ['20', '22'] + node: + - name: 20 + version: 20 + - name: 22 + version: 22.4 os: ['macos-latest', 'ubuntu-latest', 'windows-latest'] steps: @@ -31,7 +35,7 @@ jobs: - name: Setup Node.js and install dependencies uses: ./.github/setup-node with: - node-version: ${{ matrix.node }} + node-version: ${{ matrix.node.version }} - name: Create block shell: bash diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 488f41c217e7c2..75a0d34ddc8d87 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -21,13 +21,17 @@ concurrency: jobs: unit-js: - name: JavaScript (Node.js ${{ matrix.node }}) ${{ matrix.shard }} + name: JavaScript (Node.js ${{ matrix.node.name }}) ${{ matrix.shard }} runs-on: ubuntu-latest if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} strategy: fail-fast: false matrix: - node: ['20', '22'] + node: + - name: 20 + version: 20 + - name: 22 + version: 22.4 shard: ['1/4', '2/4', '3/4', '4/4'] steps: @@ -39,7 +43,7 @@ jobs: - name: Setup Node.js and install dependencies uses: ./.github/setup-node with: - node-version: ${{ matrix.node }} + node-version: ${{ matrix.node.version }} - name: Determine the number of CPU cores uses: SimenB/github-actions-cpu-cores@97ba232459a8e02ff6121db9362b09661c875ab8 # v2.0.0 @@ -60,13 +64,17 @@ jobs: --cacheDirectory="$HOME/.jest-cache" unit-js-date: - name: JavaScript Date Tests (Node.js ${{ matrix.node }}) + name: JavaScript Date Tests (Node.js ${{ matrix.node.name }}) runs-on: ubuntu-latest if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} strategy: fail-fast: false matrix: - node: ['20', '22'] + node: + - name: 20 + version: 20 + - name: 22 + version: 22.4 steps: - name: Checkout repository @@ -77,7 +85,7 @@ jobs: - name: Setup Node.js and install dependencies uses: ./.github/setup-node with: - node-version: ${{ matrix.node }} + node-version: ${{ matrix.node.version }} - name: Determine the number of CPU cores uses: SimenB/github-actions-cpu-cores@97ba232459a8e02ff6121db9362b09661c875ab8 # v2.0.0 diff --git a/backport-changelog/6.6/7061.md b/backport-changelog/6.6/7061.md new file mode 100644 index 00000000000000..307e6575cf38d8 --- /dev/null +++ b/backport-changelog/6.6/7061.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/7061 + +* https://github.com/WordPress/gutenberg/pull/63726 diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md index 4b66ad9eb6cb40..7eed5c8741288b 100644 --- a/docs/reference-guides/data/data-core-block-editor.md +++ b/docs/reference-guides/data/data-core-block-editor.md @@ -562,6 +562,18 @@ _Returns_ - `number`: Number of blocks in the post, or number of blocks with name equal to blockName. +### getHoveredBlockClientId + +Returns the currently hovered block. + +_Parameters_ + +- _state_ `Object`: Global application state. + +_Returns_ + +- `Object`: Client Id of the hovered block. + ### getInserterItems Determines the items that appear in the inserter. Includes both static items (e.g. a regular block type) and dynamic items (e.g. a reusable block). @@ -1257,6 +1269,18 @@ _Parameters_ Action that hides the insertion point. +### hoverBlock + +Returns an action object used in signalling that the block with the specified client ID has been hovered. + +_Parameters_ + +- _clientId_ `string`: Block client ID. + +_Returns_ + +- `Object`: Action object. + ### insertAfterBlock Action that inserts a default block after a given block. diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index df46c3399a2a76..65a5e5fe4b9578 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -2921,6 +2921,9 @@ static function ( $pseudo_selector ) use ( $selector ) { } /* + * Root selector (body) styles should not be wrapped in `:root where()` to keep + * specificity at (0,0,1) and maintain backwards compatibility. + * * Top-level element styles using element-only specificity selectors should * not get wrapped in `:root :where()` to maintain backwards compatibility. * @@ -2928,11 +2931,13 @@ static function ( $pseudo_selector ) use ( $selector ) { * still need to be wrapped in `:root :where` to cap specificity for nested * variations etc. Pseudo selectors won't match the ELEMENTS selector exactly. */ - $element_only_selector = $current_element && - isset( static::ELEMENTS[ $current_element ] ) && - // buttons, captions etc. still need `:root :where()` as they are class based selectors. - ! isset( static::__EXPERIMENTAL_ELEMENT_CLASS_NAMES[ $current_element ] ) && - static::ELEMENTS[ $current_element ] === $selector; + $element_only_selector = $is_root_selector || ( + $current_element && + isset( static::ELEMENTS[ $current_element ] ) && + // buttons, captions etc. still need `:root :where()` as they are class based selectors. + ! isset( static::__EXPERIMENTAL_ELEMENT_CLASS_NAMES[ $current_element ] ) && + static::ELEMENTS[ $current_element ] === $selector + ); // 2. Generate and append the rules that use the general selector. $general_selector = $element_only_selector ? $selector : ":root :where($selector)"; diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index 6b9af74d03a3ab..00951fb94c4a77 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -730,10 +730,6 @@ _Returns_ - `JSX.Element`: A React element. -### ReusableBlocksRenameHint - -Undocumented declaration. - ### RichText _Related_ diff --git a/packages/block-editor/src/components/block-list/use-block-props/index.js b/packages/block-editor/src/components/block-list/use-block-props/index.js index 64e40559bb4735..6c44aa5c5d9705 100644 --- a/packages/block-editor/src/components/block-list/use-block-props/index.js +++ b/packages/block-editor/src/components/block-list/use-block-props/index.js @@ -115,7 +115,7 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) { useFocusHandler( clientId ), useEventHandlers( { clientId, isSelected } ), useNavModeExit( clientId ), - useIsHovered(), + useIsHovered( { clientId } ), useIntersectionObserver(), useMovingAnimation( { triggerAnimationOnChange: index, clientId } ), useDisabled( { isDisabled: ! hasOverlay } ), diff --git a/packages/block-editor/src/components/block-list/use-block-props/use-is-hovered.js b/packages/block-editor/src/components/block-list/use-block-props/use-is-hovered.js index 518ed583933acd..7c4b4aae8a70a1 100644 --- a/packages/block-editor/src/components/block-list/use-block-props/use-is-hovered.js +++ b/packages/block-editor/src/components/block-list/use-block-props/use-is-hovered.js @@ -2,23 +2,37 @@ * WordPress dependencies */ import { useRefEffect } from '@wordpress/compose'; +import { useDispatch } from '@wordpress/data'; -function listener( event ) { - if ( event.defaultPrevented ) { - return; - } - - const action = event.type === 'mouseover' ? 'add' : 'remove'; - - event.preventDefault(); - event.currentTarget.classList[ action ]( 'is-hovered' ); -} +/** + * Internal dependencies + */ +import { store as blockEditorStore } from '../../../store'; /* * Adds `is-hovered` class when the block is hovered and in navigation or * outline mode. */ -export function useIsHovered() { +export function useIsHovered( { clientId } ) { + const { hoverBlock } = useDispatch( blockEditorStore ); + + function listener( event ) { + if ( event.defaultPrevented ) { + return; + } + + const action = event.type === 'mouseover' ? 'add' : 'remove'; + + event.preventDefault(); + event.currentTarget.classList[ action ]( 'is-hovered' ); + + if ( action === 'add' ) { + hoverBlock( clientId ); + } else { + hoverBlock( null ); + } + } + return useRefEffect( ( node ) => { node.addEventListener( 'mouseout', listener ); node.addEventListener( 'mouseover', listener ); @@ -29,6 +43,7 @@ export function useIsHovered() { // Remove class in case it lingers. node.classList.remove( 'is-hovered' ); + hoverBlock( null ); }; }, [] ); } diff --git a/packages/block-editor/src/components/block-tools/style.scss b/packages/block-editor/src/components/block-tools/style.scss index 20c0ab4104204b..6bf1f91cb08682 100644 --- a/packages/block-editor/src/components/block-tools/style.scss +++ b/packages/block-editor/src/components/block-tools/style.scss @@ -285,3 +285,11 @@ border: none; } } + +.block-editor-block-tools__zoom-out-mode-inserter-button { + visibility: hidden; + + &.is-visible { + visibility: visible; + } +} diff --git a/packages/block-editor/src/components/block-tools/zoom-out-mode-inserter-button.js b/packages/block-editor/src/components/block-tools/zoom-out-mode-inserter-button.js new file mode 100644 index 00000000000000..8ea80a53830135 --- /dev/null +++ b/packages/block-editor/src/components/block-tools/zoom-out-mode-inserter-button.js @@ -0,0 +1,47 @@ +/** + * External dependencies + */ +import clsx from 'clsx'; + +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; +import { Button } from '@wordpress/components'; +import { plus } from '@wordpress/icons'; +import { _x } from '@wordpress/i18n'; + +function ZoomOutModeInserterButton( { isVisible, onClick } ) { + const [ + zoomOutModeInserterButtonHovered, + setZoomOutModeInserterButtonHovered, + ] = useState( false ); + + return ( +